A natural-language shell agent powered by Claude, OpenAI, or Gemini.
Project description
prompter
prompter turns plain English into real shell commands. You describe what you want. It figures out the commands, shows each one with a risk rating, asks before anything risky, and runs them. It tracks the working directory as it goes, like a real shell session.
It runs on the model you choose: Claude, OpenAI (or any OpenAI-compatible endpoint such as Groq or OpenRouter), or Gemini.
$ prompter "make a folder called scratch, cd into it, then run claude"
$ prompter "download uv if it isn't installed, then print its version"
$ prompter "write a C++ linked list, compile it with clang++, and run it"
$ prompter "make a venv in ~/Documents/ml, install scikit-learn, open it in VS Code"
$ prompter
Run prompter with no goal to start an interactive REPL.
A coding agent lives inside one repository. prompter works on your whole shell. Launching a coding agent, installing tools, cloning repos, converting files: it is all just commands. A coding agent like Claude Code becomes one of the tools prompter can launch, not the thing you live inside.
Install
The distribution is shell-prompter and it installs one command, prompter. A
single install includes all four providers (Anthropic, OpenAI, Gemini, and
Cohere).
Install it globally with pipx:
pipx install shell-prompter
To install the latest unreleased code instead, point pipx at the repository:
pipx install "git+https://github.com/Radinkv/shell-prompter.git"
To work on the code instead, use an editable install in a virtual environment:
cd shell-prompter
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
Then add an API key for your provider:
prompter keys add anthropic sk-ant-...
Or export the matching environment variable (ANTHROPIC_API_KEY,
OPENAI_API_KEY, or GEMINI_API_KEY). See API keys.
Config
On first run prompter writes ~/.prompter/config.json:
{
"default_workspace": "~/Code",
"provider": "anthropic",
"model": "",
"base_url": null,
"api_key_env": null,
"max_fix_attempts": 3,
"auto_approve_safe": true,
"preferences": [
"When compiling C++, prefer clang++ with -std=c++17, and fall back to g++ if clang++ isn't available."
]
}
| Key | What it does |
|---|---|
default_workspace |
Where new projects go when you don't say where. prompter "make a project called hunchday" creates ~/Code/hunchday, not a folder in the current directory. |
provider |
anthropic, openai, gemini, or cohere. See Providers. |
model |
Empty means use the provider's default (listed in the Providers table). Set it to pin a model. |
base_url |
Points the OpenAI adapter at a compatible endpoint such as Groq or OpenRouter. Other providers ignore it. |
api_key_env |
The environment variable that holds the API key. Defaults to ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, or COHERE_API_KEY. |
max_fix_attempts |
How many commands may fail in a row before prompter stops. See Self-repair. |
auto_approve_safe |
Set to false to confirm every command, including safe ones. |
concise |
Set to true to make the model write code and scripts with no comments and skip the closing explanation. Saves tokens. Off by default; override per run with --concise / --verbose. |
show_usage |
Set to true to print token usage after each run. Off by default; enable per run with --usage. |
render_markdown |
Render the model's markdown (bold, headings, lists, code) as styled terminal text. On by default; disable per run with --raw. Always off when output isn't a TTY, so pipes stay plain. |
pricing |
Optional per-model price map for a cost estimate, e.g. {"claude-sonnet-4-6": {"input": 3.0, "output": 15.0}} (USD per million tokens). Without it the usage line shows tokens only. |
preferences |
Free-form lines passed straight to the model, such as "Use pnpm, not npm." |
Run prompter config to print the path, or prompter status to see the
current provider, model, workspace, and which providers have a key. Any value
can be overridden for a single run with a flag (see Flags).
Providers
The model backend sits behind one small interface, so prompter behaves the same
whichever you pick. Set your default with prompter use <provider> [model], or
override it for one run with --provider. Provider names are case-insensitive,
and claude, gpt, google, and command work as aliases.
| Provider | Default model | API key | Notes |
|---|---|---|---|
anthropic |
claude-sonnet-4-6 |
ANTHROPIC_API_KEY |
Default. Also reads an ant auth login profile. |
openai |
gpt-5.4 |
OPENAI_API_KEY |
Set base_url for Groq or OpenRouter (both speak the OpenAI API). Drop to gpt-5.4-mini to go cheaper. |
gemini |
gemini-3.5-flash |
GEMINI_API_KEY |
Generous free tier, good for everyday use. |
cohere |
command-a-03-2025 |
COHERE_API_KEY |
Cohere's flagship Command model. Also reachable via the command alias; drop to command-r-plus to go cheaper. |
A note on billing. An Anthropic Pro or Max subscription and an API key are separate accounts. A program cannot bill against your web subscription. For a free tier, use Gemini, or Groq and OpenRouter through the OpenAI adapter. Switch to Anthropic when you want it.
API keys
prompter needs an API key for the active provider. It looks in two places, in order:
- The environment variable for the provider (
ANTHROPIC_API_KEY,OPENAI_API_KEY, orGEMINI_API_KEY). - A key you stored with
prompter keys add, in~/.prompter/keys.json.
The environment variable always wins, so CI and one-off overrides keep working.
No command ever prompts you. If a key is missing when you run, prompter prints the exact command to fix it and exits, rather than dropping into a hidden prompt:
✗ No API key for gemini
add it: prompter keys add gemini <key>
or export: GEMINI_API_KEY=<key>
Manage stored keys by command:
prompter keys add anthropic sk-ant-...
prompter keys list
prompter keys remove anthropic
Because the key is an argument, it lands in your shell history. To avoid that,
export the environment variable instead. Stored keys live in
~/.prompter/keys.json with file mode 0600, readable only by you. It is plain
text, the same approach as ~/.aws/credentials.
Risk tiers and confirmation
Every command prompter proposes is graded into one of three tiers. The tier decides whether it runs on its own or asks first.
| Tier | Examples | Default |
|---|---|---|
| 🟢 SAFE | ls, cd, mkdir, git status, cat, ... 2>/dev/null |
runs automatically |
| 🟡 CONFIRM | brew install, pip install, git clone, mv, rm, curl, git clean -f |
asks first |
| 🔴 DANGER | rm -rf, sudo/su/pkexec, curl ... | sh, force-push, dd, shutdown, shred |
asks first, shown in red |
When prompter asks, you answer:
yor Enter: run itn: skip it (the model is told and adapts)a: run it and auto-approve the rest of this runq: quit
Piping a download into an interpreter (curl ... | sh, and likewise bash,
python, perl, ruby, node) is always DANGER, because it runs code you
have not seen. --yolo removes the gate entirely. Use it only when you trust
the task.
Self-repair
prompter runs a command, reads the actual error, and decides the next step. If
clang++ is missing it retries with g++ on its own. There are no scripted
repair rules.
max_fix_attempts (default 3) stops it from looping on a stuck step. It counts
commands that fail in a row. At the limit, prompter tells the model to stop and
explain what went wrong. A command you decline does not count. Only commands
that ran and failed do.
This is separate from network resilience. When a model request fails transiently (a rate limit, timeout, or 5xx), prompter retries it automatically with exponential backoff before giving up; a bad request or auth failure is not retried. A retry only happens before any of the turn's response has streamed, so output is never printed twice.
How it works
- Your request, plus your OS, shell, current directory, default workspace, and preferences, go to the model.
- The model works toward the goal by calling one tool,
run_command, a single command at a time, reacting to each result. - prompter grades each command (see Risk tiers) and runs or gates it.
- The working directory persists across commands, so "make a folder, cd in, then run claude" lands in the right place.
prompter cannot change the directory of the shell you launched it from. No program can. It runs every command, and launches your coding agent, in the right place, which covers these workflows.
Programs that take over the terminal (claude, vim, ssh, a REPL, top) get
the real terminal so you can interact with them. The model marks these
automatically.
Commands
Management is done with subcommands. Flags only modify a single run. No command opens a prompt.
| Command | Effect |
|---|---|
prompter "<goal>" |
Run a one-off goal. |
prompter |
Interactive chat (REPL). |
prompter keys add <provider> <key> |
Store an API key. |
prompter keys list |
Show which providers have a key (masked). |
prompter keys remove <provider> |
Delete a stored key. |
prompter use <provider> [model] |
Set your default provider and model. |
prompter status |
Show the current provider, model, workspace, and keys. |
prompter config |
Print the config file path. |
prompter completions <shell> |
Print a tab-completion script (zsh, bash). See Tab completion. |
prompter version |
Print the installed version (also --version, -V). |
prompter help |
Show usage. |
Tab completion
prompter completions <shell> prints a completion script for zsh or bash.
It completes subcommands, provider names, keys actions, and run flags. Install
it once, then press TAB:
$ prompter <TAB>
keys use status config help
$ prompter use <TAB>
anthropic gemini openai
zsh:
mkdir -p ~/.zsh/completions
prompter completions zsh > ~/.zsh/completions/_prompter
# then, in ~/.zshrc before `compinit`:
# fpath=(~/.zsh/completions $fpath)
# autoload -Uz compinit && compinit
bash:
echo 'source <(prompter completions bash)' >> ~/.bashrc
Open a new shell for it to take effect. The provider list in the script is generated from the installed providers, so it stays current.
Flags
Flags modify a single run and sit alongside the goal (prompter --yolo "...").
| Flag | Effect |
|---|---|
--provider NAME |
Use anthropic, openai, gemini, or cohere for this run. |
--model ID |
Override the model. |
--base-url URL |
OpenAI-compatible endpoint (Groq, OpenRouter). |
--workspace PATH |
Override the default workspace. |
--max-fix N |
Override max_fix_attempts. |
--ask-all |
Confirm every command, including safe ones. |
--yolo |
Run everything with no confirmation. Dangerous. |
--concise |
No code comments, minimal explanation, for this run. |
--verbose |
Full comments and explanation, even if concise is set in config. |
--usage |
Print token usage (and cost, if pricing is set) after the run. |
--raw |
Print the model's text as-is, without rendering markdown. |
Project layout
prompter/
constants.py shared primitive constants (empty string, separators)
colors.py Palette (ANSI, off when output is not a TTY)
config.py Config dataclass, ApprovalMode, load and save
risk.py RiskTier and classify(): the safe/confirm/danger rules
shell.py Shell and CommandResult: execution and cwd tracking
prompts.py system prompt and per-turn environment context
providers/ pluggable model backends
base.py neutral types, ModelProvider ABC, registry
anthropic_provider.py, openai_provider.py, gemini_provider.py
ui.py Console and Decision: all printing and input
agent.py Agent and Conversation: the orchestration loop
completion.py shell completion script generation (zsh, bash)
cli.py command dispatch, run setup, actionable errors, main()
tests/
conftest.py, _helpers.py fixtures and fakes (FakeProvider, FakeShell)
test_*.py one module per package module
The design follows a state boundary. Stateful pieces (Shell, Agent, the
provider, Console) are objects injected into each other. Pure transforms
(classify, truncate, config loading, context building) are plain functions.
Agent orchestrates its collaborators and does no I/O or API calls itself, so it
can be tested with a fake provider and console.
Providers share a template-method base. ModelProvider.complete() owns the
fixed algorithm: build a request, stream it into a TurnCollector, wrap provider
errors, return a turn. Each adapter supplies only build_request and
run_stream. Adding a backend is one file in providers/ plus one @register
line, with no change to the agent.
Tests
pip install -e ".[dev]"
pytest
About 165 unit tests, no network or API key needed. The agent loop is tested with fakes: a scripted provider, a recording shell, a mock console. Each adapter's translation to and from its wire format is tested with a fake SDK client.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shell_prompter-0.7.0.tar.gz.
File metadata
- Download URL: shell_prompter-0.7.0.tar.gz
- Upload date:
- Size: 71.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60df3a6e52be3fcb4ea7ad9e1a904240d2e7a9d2e1fa00608c2ebe8dfcbdde77
|
|
| MD5 |
0cdbbd99188566a7b32c83c91412256a
|
|
| BLAKE2b-256 |
00813332458fbcc563a8a21e8cdc9a2078c023224ca5719b08ff8a795e35d5cb
|
Provenance
The following attestation bundles were made for shell_prompter-0.7.0.tar.gz:
Publisher:
publish.yml on Radinkv/shell-prompter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shell_prompter-0.7.0.tar.gz -
Subject digest:
60df3a6e52be3fcb4ea7ad9e1a904240d2e7a9d2e1fa00608c2ebe8dfcbdde77 - Sigstore transparency entry: 2028467720
- Sigstore integration time:
-
Permalink:
Radinkv/shell-prompter@9954f0744776d5be74ba63819a6598dec3505c3f -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/Radinkv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9954f0744776d5be74ba63819a6598dec3505c3f -
Trigger Event:
push
-
Statement type:
File details
Details for the file shell_prompter-0.7.0-py3-none-any.whl.
File metadata
- Download URL: shell_prompter-0.7.0-py3-none-any.whl
- Upload date:
- Size: 58.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d38bd2fa14a7d0bdcbe2864c53a3afb84d5a953f073e8b9b5459eddc0170d80f
|
|
| MD5 |
51946c4d3e09287b54e6c351c17744bf
|
|
| BLAKE2b-256 |
b0f4c6b882a1bae16086b723f1f678a3ec3f7a1dbd0c03f99871c235a032f7c3
|
Provenance
The following attestation bundles were made for shell_prompter-0.7.0-py3-none-any.whl:
Publisher:
publish.yml on Radinkv/shell-prompter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shell_prompter-0.7.0-py3-none-any.whl -
Subject digest:
d38bd2fa14a7d0bdcbe2864c53a3afb84d5a953f073e8b9b5459eddc0170d80f - Sigstore transparency entry: 2028467828
- Sigstore integration time:
-
Permalink:
Radinkv/shell-prompter@9954f0744776d5be74ba63819a6598dec3505c3f -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/Radinkv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9954f0744776d5be74ba63819a6598dec3505c3f -
Trigger Event:
push
-
Statement type: