shell-prompter 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shell_prompter-0.1.0/LICENSE +21 -0
- shell_prompter-0.1.0/PKG-INFO +289 -0
- shell_prompter-0.1.0/README.md +262 -0
- shell_prompter-0.1.0/prompter/__init__.py +21 -0
- shell_prompter-0.1.0/prompter/__main__.py +10 -0
- shell_prompter-0.1.0/prompter/agent.py +205 -0
- shell_prompter-0.1.0/prompter/cli.py +458 -0
- shell_prompter-0.1.0/prompter/colors.py +47 -0
- shell_prompter-0.1.0/prompter/config.py +138 -0
- shell_prompter-0.1.0/prompter/constants.py +17 -0
- shell_prompter-0.1.0/prompter/keys.py +59 -0
- shell_prompter-0.1.0/prompter/prompts.py +83 -0
- shell_prompter-0.1.0/prompter/providers/__init__.py +47 -0
- shell_prompter-0.1.0/prompter/providers/anthropic_provider.py +172 -0
- shell_prompter-0.1.0/prompter/providers/base.py +266 -0
- shell_prompter-0.1.0/prompter/providers/gemini_provider.py +205 -0
- shell_prompter-0.1.0/prompter/providers/openai_provider.py +183 -0
- shell_prompter-0.1.0/prompter/risk.py +110 -0
- shell_prompter-0.1.0/prompter/shell.py +125 -0
- shell_prompter-0.1.0/prompter/ui.py +256 -0
- shell_prompter-0.1.0/pyproject.toml +44 -0
- shell_prompter-0.1.0/setup.cfg +4 -0
- shell_prompter-0.1.0/shell_prompter.egg-info/PKG-INFO +289 -0
- shell_prompter-0.1.0/shell_prompter.egg-info/SOURCES.txt +34 -0
- shell_prompter-0.1.0/shell_prompter.egg-info/dependency_links.txt +1 -0
- shell_prompter-0.1.0/shell_prompter.egg-info/entry_points.txt +2 -0
- shell_prompter-0.1.0/shell_prompter.egg-info/requires.txt +8 -0
- shell_prompter-0.1.0/shell_prompter.egg-info/top_level.txt +1 -0
- shell_prompter-0.1.0/tests/test_agent.py +156 -0
- shell_prompter-0.1.0/tests/test_cli.py +287 -0
- shell_prompter-0.1.0/tests/test_config.py +102 -0
- shell_prompter-0.1.0/tests/test_keys.py +60 -0
- shell_prompter-0.1.0/tests/test_providers.py +342 -0
- shell_prompter-0.1.0/tests/test_risk.py +49 -0
- shell_prompter-0.1.0/tests/test_shell.py +96 -0
- shell_prompter-0.1.0/tests/test_ui.py +93 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 radinkv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shell-prompter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A natural-language shell agent powered by Claude, OpenAI, or Gemini.
|
|
5
|
+
Author-email: radinkv <26397342+Radinkv@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Radinkv/shell-prompter
|
|
8
|
+
Project-URL: Repository, https://github.com/Radinkv/shell-prompter
|
|
9
|
+
Keywords: cli,shell,ai,agent,llm,anthropic,openai,gemini
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: POSIX
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: anthropic>=0.40
|
|
20
|
+
Requires-Dist: openai>=1.0
|
|
21
|
+
Requires-Dist: google-genai>=0.3
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
25
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# prompter
|
|
29
|
+
|
|
30
|
+
prompter turns plain English into real shell commands. You describe what you
|
|
31
|
+
want. It figures out the commands, shows each one with a risk rating, asks
|
|
32
|
+
before anything risky, and runs them. It tracks the working directory as it
|
|
33
|
+
goes, like a real shell session.
|
|
34
|
+
|
|
35
|
+
It runs on the model you choose: Claude, OpenAI (or any OpenAI-compatible
|
|
36
|
+
endpoint such as Groq or OpenRouter), or Gemini.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
$ prompter "make a folder called scratch, cd into it, then run claude"
|
|
40
|
+
$ prompter "download uv if it isn't installed, then print its version"
|
|
41
|
+
$ prompter
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Run `prompter` with no goal to start an interactive REPL.
|
|
45
|
+
|
|
46
|
+
A coding agent lives inside one repository. prompter works on your whole shell.
|
|
47
|
+
Launching a coding agent, installing tools, cloning repos, converting files: it
|
|
48
|
+
is all just commands. A coding agent like Claude Code becomes one of the tools
|
|
49
|
+
prompter can launch, not the thing you live inside.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
The distribution is `shell-prompter` and it installs one command, `prompter`. A
|
|
54
|
+
single install includes all three providers (Anthropic, OpenAI, and Gemini).
|
|
55
|
+
|
|
56
|
+
Install it globally with [pipx](https://pipx.pypa.io):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pipx install "git+https://github.com/Radinkv/shell-prompter.git"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To work on the code instead, use an editable install in a virtual environment:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cd shell-prompter
|
|
66
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Then add an API key for your provider:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
prompter keys add anthropic sk-ant-...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Or export the matching environment variable (`ANTHROPIC_API_KEY`,
|
|
77
|
+
`OPENAI_API_KEY`, or `GEMINI_API_KEY`). See [API keys](#api-keys).
|
|
78
|
+
|
|
79
|
+
## Config
|
|
80
|
+
|
|
81
|
+
On first run prompter writes `~/.prompter/config.json`:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"default_workspace": "~/Code",
|
|
86
|
+
"provider": "anthropic",
|
|
87
|
+
"model": "",
|
|
88
|
+
"base_url": null,
|
|
89
|
+
"api_key_env": null,
|
|
90
|
+
"max_fix_attempts": 3,
|
|
91
|
+
"auto_approve_safe": true,
|
|
92
|
+
"preferences": [
|
|
93
|
+
"When compiling C++, prefer clang++ with -std=c++17, and fall back to g++ if clang++ isn't available."
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| Key | What it does |
|
|
99
|
+
|-----|--------------|
|
|
100
|
+
| `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. |
|
|
101
|
+
| `provider` | `anthropic`, `openai`, or `gemini`. See [Providers](#providers). |
|
|
102
|
+
| `model` | Empty means use the provider's default (listed in the Providers table). Set it to pin a model. |
|
|
103
|
+
| `base_url` | Points the OpenAI adapter at a compatible endpoint such as Groq or OpenRouter. Other providers ignore it. |
|
|
104
|
+
| `api_key_env` | The environment variable that holds the API key. Defaults to `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GEMINI_API_KEY`. |
|
|
105
|
+
| `max_fix_attempts` | How many commands may fail in a row before prompter stops. See [Self-repair](#self-repair). |
|
|
106
|
+
| `auto_approve_safe` | Set to `false` to confirm every command, including safe ones. |
|
|
107
|
+
| `preferences` | Free-form lines passed straight to the model, such as `"Use pnpm, not npm."` |
|
|
108
|
+
|
|
109
|
+
Run `prompter config` to print the path, or `prompter status` to see the
|
|
110
|
+
current provider, model, workspace, and which providers have a key. Any value
|
|
111
|
+
can be overridden for a single run with a flag (see [Flags](#flags)).
|
|
112
|
+
|
|
113
|
+
## Providers
|
|
114
|
+
|
|
115
|
+
The model backend sits behind one small interface, so prompter behaves the same
|
|
116
|
+
whichever you pick. Set your default with `prompter use <provider> [model]`, or
|
|
117
|
+
override it for one run with `--provider`. Provider names are case-insensitive,
|
|
118
|
+
and `claude`, `gpt`, and `google` work as aliases.
|
|
119
|
+
|
|
120
|
+
| Provider | Default model | API key | Notes |
|
|
121
|
+
|----------|---------------|---------|-------|
|
|
122
|
+
| `anthropic` | `claude-sonnet-4-6` | `ANTHROPIC_API_KEY` | Default. Also reads an `ant auth login` profile. |
|
|
123
|
+
| `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. |
|
|
124
|
+
| `gemini` | `gemini-3.5-flash` | `GEMINI_API_KEY` | Generous free tier, good for everyday use. |
|
|
125
|
+
|
|
126
|
+
A note on billing. An Anthropic Pro or Max subscription and an API key are
|
|
127
|
+
separate accounts. A program cannot bill against your web subscription. For a
|
|
128
|
+
free tier, use Gemini, or Groq and OpenRouter through the OpenAI adapter. Switch
|
|
129
|
+
to Anthropic when you want it.
|
|
130
|
+
|
|
131
|
+
## API keys
|
|
132
|
+
|
|
133
|
+
prompter needs an API key for the active provider. It looks in two places, in
|
|
134
|
+
order:
|
|
135
|
+
|
|
136
|
+
1. The environment variable for the provider (`ANTHROPIC_API_KEY`,
|
|
137
|
+
`OPENAI_API_KEY`, or `GEMINI_API_KEY`).
|
|
138
|
+
2. A key you stored with `prompter keys add`, in `~/.prompter/keys.json`.
|
|
139
|
+
|
|
140
|
+
The environment variable always wins, so CI and one-off overrides keep working.
|
|
141
|
+
|
|
142
|
+
No command ever prompts you. If a key is missing when you run, prompter prints
|
|
143
|
+
the exact command to fix it and exits, rather than dropping into a hidden prompt:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
✗ No API key for gemini
|
|
147
|
+
add it: prompter keys add gemini <key>
|
|
148
|
+
or export: GEMINI_API_KEY=<key>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Manage stored keys by command:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
prompter keys add anthropic sk-ant-...
|
|
155
|
+
prompter keys list
|
|
156
|
+
prompter keys remove anthropic
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Because the key is an argument, it lands in your shell history. To avoid that,
|
|
160
|
+
export the environment variable instead. Stored keys live in
|
|
161
|
+
`~/.prompter/keys.json` with file mode 0600, readable only by you. It is plain
|
|
162
|
+
text, the same approach as `~/.aws/credentials`.
|
|
163
|
+
|
|
164
|
+
## Risk tiers and confirmation
|
|
165
|
+
|
|
166
|
+
Every command prompter proposes is graded into one of three tiers. The tier
|
|
167
|
+
decides whether it runs on its own or asks first.
|
|
168
|
+
|
|
169
|
+
| Tier | Examples | Default |
|
|
170
|
+
|------|----------|---------|
|
|
171
|
+
| 🟢 SAFE | `ls`, `cd`, `mkdir`, `git status`, `cat` | runs automatically |
|
|
172
|
+
| 🟡 CONFIRM | `brew install`, `pip install`, `git clone`, `mv`, `rm`, `curl` | asks first |
|
|
173
|
+
| 🔴 DANGER | `rm -rf`, `sudo`, `curl ... \| sh`, force-push, `dd` | asks first, shown in red |
|
|
174
|
+
|
|
175
|
+
When prompter asks, you answer:
|
|
176
|
+
|
|
177
|
+
- `y` or Enter: run it
|
|
178
|
+
- `n`: skip it (the model is told and adapts)
|
|
179
|
+
- `a`: run it and auto-approve the rest of this run
|
|
180
|
+
- `q`: quit
|
|
181
|
+
|
|
182
|
+
`curl ... | sh` is always DANGER, because it runs code you have not seen.
|
|
183
|
+
`--yolo` removes the gate entirely. Use it only when you trust the task.
|
|
184
|
+
|
|
185
|
+
## Self-repair
|
|
186
|
+
|
|
187
|
+
prompter runs a command, reads the actual error, and decides the next step. If
|
|
188
|
+
`clang++` is missing it retries with `g++` on its own. There are no scripted
|
|
189
|
+
repair rules.
|
|
190
|
+
|
|
191
|
+
`max_fix_attempts` (default 3) stops it from looping on a stuck step. It counts
|
|
192
|
+
commands that fail in a row. At the limit, prompter tells the model to stop and
|
|
193
|
+
explain what went wrong. A command you decline does not count. Only commands
|
|
194
|
+
that ran and failed do.
|
|
195
|
+
|
|
196
|
+
## How it works
|
|
197
|
+
|
|
198
|
+
1. Your request, plus your OS, shell, current directory, default workspace, and
|
|
199
|
+
preferences, go to the model.
|
|
200
|
+
2. The model works toward the goal by calling one tool, `run_command`, a single
|
|
201
|
+
command at a time, reacting to each result.
|
|
202
|
+
3. prompter grades each command (see [Risk tiers](#risk-tiers-and-confirmation))
|
|
203
|
+
and runs or gates it.
|
|
204
|
+
4. The working directory persists across commands, so "make a folder, cd in,
|
|
205
|
+
then run claude" lands in the right place.
|
|
206
|
+
|
|
207
|
+
prompter cannot change the directory of the shell you launched it from. No
|
|
208
|
+
program can. It runs every command, and launches your coding agent, in the right
|
|
209
|
+
place, which covers these workflows.
|
|
210
|
+
|
|
211
|
+
Programs that take over the terminal (`claude`, `vim`, `ssh`, a REPL, `top`) get
|
|
212
|
+
the real terminal so you can interact with them. The model marks these
|
|
213
|
+
automatically.
|
|
214
|
+
|
|
215
|
+
## Commands
|
|
216
|
+
|
|
217
|
+
Management is done with subcommands. Flags only modify a single run. No command
|
|
218
|
+
opens a prompt.
|
|
219
|
+
|
|
220
|
+
| Command | Effect |
|
|
221
|
+
|---------|--------|
|
|
222
|
+
| `prompter "<goal>"` | Run a one-off goal. |
|
|
223
|
+
| `prompter` | Interactive chat (REPL). |
|
|
224
|
+
| `prompter keys add <provider> <key>` | Store an API key. |
|
|
225
|
+
| `prompter keys list` | Show which providers have a key (masked). |
|
|
226
|
+
| `prompter keys remove <provider>` | Delete a stored key. |
|
|
227
|
+
| `prompter use <provider> [model]` | Set your default provider and model. |
|
|
228
|
+
| `prompter status` | Show the current provider, model, workspace, and keys. |
|
|
229
|
+
| `prompter config` | Print the config file path. |
|
|
230
|
+
| `prompter help` | Show usage. |
|
|
231
|
+
|
|
232
|
+
## Flags
|
|
233
|
+
|
|
234
|
+
Flags modify a single run and sit alongside the goal (`prompter --yolo "..."`).
|
|
235
|
+
|
|
236
|
+
| Flag | Effect |
|
|
237
|
+
|------|--------|
|
|
238
|
+
| `--provider NAME` | Use anthropic, openai, or gemini for this run. |
|
|
239
|
+
| `--model ID` | Override the model. |
|
|
240
|
+
| `--base-url URL` | OpenAI-compatible endpoint (Groq, OpenRouter). |
|
|
241
|
+
| `--workspace PATH` | Override the default workspace. |
|
|
242
|
+
| `--max-fix N` | Override `max_fix_attempts`. |
|
|
243
|
+
| `--ask-all` | Confirm every command, including safe ones. |
|
|
244
|
+
| `--yolo` | Run everything with no confirmation. Dangerous. |
|
|
245
|
+
|
|
246
|
+
## Project layout
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
prompter/
|
|
250
|
+
constants.py shared primitive constants (empty string, separators)
|
|
251
|
+
colors.py Palette (ANSI, off when output is not a TTY)
|
|
252
|
+
config.py Config dataclass, ApprovalMode, load and save
|
|
253
|
+
risk.py RiskTier and classify(): the safe/confirm/danger rules
|
|
254
|
+
shell.py Shell and CommandResult: execution and cwd tracking
|
|
255
|
+
prompts.py system prompt and per-turn environment context
|
|
256
|
+
providers/ pluggable model backends
|
|
257
|
+
base.py neutral types, ModelProvider ABC, registry
|
|
258
|
+
anthropic_provider.py, openai_provider.py, gemini_provider.py
|
|
259
|
+
ui.py Console and Decision: all printing and input
|
|
260
|
+
agent.py Agent and Conversation: the orchestration loop
|
|
261
|
+
cli.py command dispatch, run setup, actionable errors, main()
|
|
262
|
+
tests/
|
|
263
|
+
conftest.py, _helpers.py fixtures and fakes (FakeProvider, FakeShell)
|
|
264
|
+
test_*.py one module per package module
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The design follows a state boundary. Stateful pieces (`Shell`, `Agent`, the
|
|
268
|
+
provider, `Console`) are objects injected into each other. Pure transforms
|
|
269
|
+
(`classify`, `truncate`, config loading, context building) are plain functions.
|
|
270
|
+
`Agent` orchestrates its collaborators and does no I/O or API calls itself, so it
|
|
271
|
+
can be tested with a fake provider and console.
|
|
272
|
+
|
|
273
|
+
Providers share a template-method base. `ModelProvider.complete()` owns the
|
|
274
|
+
fixed algorithm: build a request, stream it into a `TurnCollector`, wrap provider
|
|
275
|
+
errors, return a turn. Each adapter supplies only `build_request` and
|
|
276
|
+
`run_stream`. Adding a backend is one file in `providers/` plus one `@register`
|
|
277
|
+
line, with no change to the agent.
|
|
278
|
+
|
|
279
|
+
## Tests
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
pip install -e ".[dev]"
|
|
283
|
+
pytest
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
About 165 unit tests, no network or API key needed. The agent loop is tested
|
|
287
|
+
with fakes: a scripted provider, a recording shell, a mock console. Each
|
|
288
|
+
adapter's translation to and from its wire format is tested with a fake SDK
|
|
289
|
+
client.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# prompter
|
|
2
|
+
|
|
3
|
+
prompter turns plain English into real shell commands. You describe what you
|
|
4
|
+
want. It figures out the commands, shows each one with a risk rating, asks
|
|
5
|
+
before anything risky, and runs them. It tracks the working directory as it
|
|
6
|
+
goes, like a real shell session.
|
|
7
|
+
|
|
8
|
+
It runs on the model you choose: Claude, OpenAI (or any OpenAI-compatible
|
|
9
|
+
endpoint such as Groq or OpenRouter), or Gemini.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
$ prompter "make a folder called scratch, cd into it, then run claude"
|
|
13
|
+
$ prompter "download uv if it isn't installed, then print its version"
|
|
14
|
+
$ prompter
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run `prompter` with no goal to start an interactive REPL.
|
|
18
|
+
|
|
19
|
+
A coding agent lives inside one repository. prompter works on your whole shell.
|
|
20
|
+
Launching a coding agent, installing tools, cloning repos, converting files: it
|
|
21
|
+
is all just commands. A coding agent like Claude Code becomes one of the tools
|
|
22
|
+
prompter can launch, not the thing you live inside.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
The distribution is `shell-prompter` and it installs one command, `prompter`. A
|
|
27
|
+
single install includes all three providers (Anthropic, OpenAI, and Gemini).
|
|
28
|
+
|
|
29
|
+
Install it globally with [pipx](https://pipx.pypa.io):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pipx install "git+https://github.com/Radinkv/shell-prompter.git"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To work on the code instead, use an editable install in a virtual environment:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd shell-prompter
|
|
39
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then add an API key for your provider:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
prompter keys add anthropic sk-ant-...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or export the matching environment variable (`ANTHROPIC_API_KEY`,
|
|
50
|
+
`OPENAI_API_KEY`, or `GEMINI_API_KEY`). See [API keys](#api-keys).
|
|
51
|
+
|
|
52
|
+
## Config
|
|
53
|
+
|
|
54
|
+
On first run prompter writes `~/.prompter/config.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"default_workspace": "~/Code",
|
|
59
|
+
"provider": "anthropic",
|
|
60
|
+
"model": "",
|
|
61
|
+
"base_url": null,
|
|
62
|
+
"api_key_env": null,
|
|
63
|
+
"max_fix_attempts": 3,
|
|
64
|
+
"auto_approve_safe": true,
|
|
65
|
+
"preferences": [
|
|
66
|
+
"When compiling C++, prefer clang++ with -std=c++17, and fall back to g++ if clang++ isn't available."
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Key | What it does |
|
|
72
|
+
|-----|--------------|
|
|
73
|
+
| `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. |
|
|
74
|
+
| `provider` | `anthropic`, `openai`, or `gemini`. See [Providers](#providers). |
|
|
75
|
+
| `model` | Empty means use the provider's default (listed in the Providers table). Set it to pin a model. |
|
|
76
|
+
| `base_url` | Points the OpenAI adapter at a compatible endpoint such as Groq or OpenRouter. Other providers ignore it. |
|
|
77
|
+
| `api_key_env` | The environment variable that holds the API key. Defaults to `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GEMINI_API_KEY`. |
|
|
78
|
+
| `max_fix_attempts` | How many commands may fail in a row before prompter stops. See [Self-repair](#self-repair). |
|
|
79
|
+
| `auto_approve_safe` | Set to `false` to confirm every command, including safe ones. |
|
|
80
|
+
| `preferences` | Free-form lines passed straight to the model, such as `"Use pnpm, not npm."` |
|
|
81
|
+
|
|
82
|
+
Run `prompter config` to print the path, or `prompter status` to see the
|
|
83
|
+
current provider, model, workspace, and which providers have a key. Any value
|
|
84
|
+
can be overridden for a single run with a flag (see [Flags](#flags)).
|
|
85
|
+
|
|
86
|
+
## Providers
|
|
87
|
+
|
|
88
|
+
The model backend sits behind one small interface, so prompter behaves the same
|
|
89
|
+
whichever you pick. Set your default with `prompter use <provider> [model]`, or
|
|
90
|
+
override it for one run with `--provider`. Provider names are case-insensitive,
|
|
91
|
+
and `claude`, `gpt`, and `google` work as aliases.
|
|
92
|
+
|
|
93
|
+
| Provider | Default model | API key | Notes |
|
|
94
|
+
|----------|---------------|---------|-------|
|
|
95
|
+
| `anthropic` | `claude-sonnet-4-6` | `ANTHROPIC_API_KEY` | Default. Also reads an `ant auth login` profile. |
|
|
96
|
+
| `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. |
|
|
97
|
+
| `gemini` | `gemini-3.5-flash` | `GEMINI_API_KEY` | Generous free tier, good for everyday use. |
|
|
98
|
+
|
|
99
|
+
A note on billing. An Anthropic Pro or Max subscription and an API key are
|
|
100
|
+
separate accounts. A program cannot bill against your web subscription. For a
|
|
101
|
+
free tier, use Gemini, or Groq and OpenRouter through the OpenAI adapter. Switch
|
|
102
|
+
to Anthropic when you want it.
|
|
103
|
+
|
|
104
|
+
## API keys
|
|
105
|
+
|
|
106
|
+
prompter needs an API key for the active provider. It looks in two places, in
|
|
107
|
+
order:
|
|
108
|
+
|
|
109
|
+
1. The environment variable for the provider (`ANTHROPIC_API_KEY`,
|
|
110
|
+
`OPENAI_API_KEY`, or `GEMINI_API_KEY`).
|
|
111
|
+
2. A key you stored with `prompter keys add`, in `~/.prompter/keys.json`.
|
|
112
|
+
|
|
113
|
+
The environment variable always wins, so CI and one-off overrides keep working.
|
|
114
|
+
|
|
115
|
+
No command ever prompts you. If a key is missing when you run, prompter prints
|
|
116
|
+
the exact command to fix it and exits, rather than dropping into a hidden prompt:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
✗ No API key for gemini
|
|
120
|
+
add it: prompter keys add gemini <key>
|
|
121
|
+
or export: GEMINI_API_KEY=<key>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Manage stored keys by command:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
prompter keys add anthropic sk-ant-...
|
|
128
|
+
prompter keys list
|
|
129
|
+
prompter keys remove anthropic
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Because the key is an argument, it lands in your shell history. To avoid that,
|
|
133
|
+
export the environment variable instead. Stored keys live in
|
|
134
|
+
`~/.prompter/keys.json` with file mode 0600, readable only by you. It is plain
|
|
135
|
+
text, the same approach as `~/.aws/credentials`.
|
|
136
|
+
|
|
137
|
+
## Risk tiers and confirmation
|
|
138
|
+
|
|
139
|
+
Every command prompter proposes is graded into one of three tiers. The tier
|
|
140
|
+
decides whether it runs on its own or asks first.
|
|
141
|
+
|
|
142
|
+
| Tier | Examples | Default |
|
|
143
|
+
|------|----------|---------|
|
|
144
|
+
| 🟢 SAFE | `ls`, `cd`, `mkdir`, `git status`, `cat` | runs automatically |
|
|
145
|
+
| 🟡 CONFIRM | `brew install`, `pip install`, `git clone`, `mv`, `rm`, `curl` | asks first |
|
|
146
|
+
| 🔴 DANGER | `rm -rf`, `sudo`, `curl ... \| sh`, force-push, `dd` | asks first, shown in red |
|
|
147
|
+
|
|
148
|
+
When prompter asks, you answer:
|
|
149
|
+
|
|
150
|
+
- `y` or Enter: run it
|
|
151
|
+
- `n`: skip it (the model is told and adapts)
|
|
152
|
+
- `a`: run it and auto-approve the rest of this run
|
|
153
|
+
- `q`: quit
|
|
154
|
+
|
|
155
|
+
`curl ... | sh` is always DANGER, because it runs code you have not seen.
|
|
156
|
+
`--yolo` removes the gate entirely. Use it only when you trust the task.
|
|
157
|
+
|
|
158
|
+
## Self-repair
|
|
159
|
+
|
|
160
|
+
prompter runs a command, reads the actual error, and decides the next step. If
|
|
161
|
+
`clang++` is missing it retries with `g++` on its own. There are no scripted
|
|
162
|
+
repair rules.
|
|
163
|
+
|
|
164
|
+
`max_fix_attempts` (default 3) stops it from looping on a stuck step. It counts
|
|
165
|
+
commands that fail in a row. At the limit, prompter tells the model to stop and
|
|
166
|
+
explain what went wrong. A command you decline does not count. Only commands
|
|
167
|
+
that ran and failed do.
|
|
168
|
+
|
|
169
|
+
## How it works
|
|
170
|
+
|
|
171
|
+
1. Your request, plus your OS, shell, current directory, default workspace, and
|
|
172
|
+
preferences, go to the model.
|
|
173
|
+
2. The model works toward the goal by calling one tool, `run_command`, a single
|
|
174
|
+
command at a time, reacting to each result.
|
|
175
|
+
3. prompter grades each command (see [Risk tiers](#risk-tiers-and-confirmation))
|
|
176
|
+
and runs or gates it.
|
|
177
|
+
4. The working directory persists across commands, so "make a folder, cd in,
|
|
178
|
+
then run claude" lands in the right place.
|
|
179
|
+
|
|
180
|
+
prompter cannot change the directory of the shell you launched it from. No
|
|
181
|
+
program can. It runs every command, and launches your coding agent, in the right
|
|
182
|
+
place, which covers these workflows.
|
|
183
|
+
|
|
184
|
+
Programs that take over the terminal (`claude`, `vim`, `ssh`, a REPL, `top`) get
|
|
185
|
+
the real terminal so you can interact with them. The model marks these
|
|
186
|
+
automatically.
|
|
187
|
+
|
|
188
|
+
## Commands
|
|
189
|
+
|
|
190
|
+
Management is done with subcommands. Flags only modify a single run. No command
|
|
191
|
+
opens a prompt.
|
|
192
|
+
|
|
193
|
+
| Command | Effect |
|
|
194
|
+
|---------|--------|
|
|
195
|
+
| `prompter "<goal>"` | Run a one-off goal. |
|
|
196
|
+
| `prompter` | Interactive chat (REPL). |
|
|
197
|
+
| `prompter keys add <provider> <key>` | Store an API key. |
|
|
198
|
+
| `prompter keys list` | Show which providers have a key (masked). |
|
|
199
|
+
| `prompter keys remove <provider>` | Delete a stored key. |
|
|
200
|
+
| `prompter use <provider> [model]` | Set your default provider and model. |
|
|
201
|
+
| `prompter status` | Show the current provider, model, workspace, and keys. |
|
|
202
|
+
| `prompter config` | Print the config file path. |
|
|
203
|
+
| `prompter help` | Show usage. |
|
|
204
|
+
|
|
205
|
+
## Flags
|
|
206
|
+
|
|
207
|
+
Flags modify a single run and sit alongside the goal (`prompter --yolo "..."`).
|
|
208
|
+
|
|
209
|
+
| Flag | Effect |
|
|
210
|
+
|------|--------|
|
|
211
|
+
| `--provider NAME` | Use anthropic, openai, or gemini for this run. |
|
|
212
|
+
| `--model ID` | Override the model. |
|
|
213
|
+
| `--base-url URL` | OpenAI-compatible endpoint (Groq, OpenRouter). |
|
|
214
|
+
| `--workspace PATH` | Override the default workspace. |
|
|
215
|
+
| `--max-fix N` | Override `max_fix_attempts`. |
|
|
216
|
+
| `--ask-all` | Confirm every command, including safe ones. |
|
|
217
|
+
| `--yolo` | Run everything with no confirmation. Dangerous. |
|
|
218
|
+
|
|
219
|
+
## Project layout
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
prompter/
|
|
223
|
+
constants.py shared primitive constants (empty string, separators)
|
|
224
|
+
colors.py Palette (ANSI, off when output is not a TTY)
|
|
225
|
+
config.py Config dataclass, ApprovalMode, load and save
|
|
226
|
+
risk.py RiskTier and classify(): the safe/confirm/danger rules
|
|
227
|
+
shell.py Shell and CommandResult: execution and cwd tracking
|
|
228
|
+
prompts.py system prompt and per-turn environment context
|
|
229
|
+
providers/ pluggable model backends
|
|
230
|
+
base.py neutral types, ModelProvider ABC, registry
|
|
231
|
+
anthropic_provider.py, openai_provider.py, gemini_provider.py
|
|
232
|
+
ui.py Console and Decision: all printing and input
|
|
233
|
+
agent.py Agent and Conversation: the orchestration loop
|
|
234
|
+
cli.py command dispatch, run setup, actionable errors, main()
|
|
235
|
+
tests/
|
|
236
|
+
conftest.py, _helpers.py fixtures and fakes (FakeProvider, FakeShell)
|
|
237
|
+
test_*.py one module per package module
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The design follows a state boundary. Stateful pieces (`Shell`, `Agent`, the
|
|
241
|
+
provider, `Console`) are objects injected into each other. Pure transforms
|
|
242
|
+
(`classify`, `truncate`, config loading, context building) are plain functions.
|
|
243
|
+
`Agent` orchestrates its collaborators and does no I/O or API calls itself, so it
|
|
244
|
+
can be tested with a fake provider and console.
|
|
245
|
+
|
|
246
|
+
Providers share a template-method base. `ModelProvider.complete()` owns the
|
|
247
|
+
fixed algorithm: build a request, stream it into a `TurnCollector`, wrap provider
|
|
248
|
+
errors, return a turn. Each adapter supplies only `build_request` and
|
|
249
|
+
`run_stream`. Adding a backend is one file in `providers/` plus one `@register`
|
|
250
|
+
line, with no change to the agent.
|
|
251
|
+
|
|
252
|
+
## Tests
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
pip install -e ".[dev]"
|
|
256
|
+
pytest
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
About 165 unit tests, no network or API key needed. The agent loop is tested
|
|
260
|
+
with fakes: a scripted provider, a recording shell, a mock console. Each
|
|
261
|
+
adapter's translation to and from its wire format is tested with a fake SDK
|
|
262
|
+
client.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""prompter is a natural-language shell agent powered by Claude.
|
|
2
|
+
|
|
3
|
+
You describe what you want in plain English. prompter figures out the shell
|
|
4
|
+
commands, shows them to you with a risk rating, and runs them after your
|
|
5
|
+
approval for anything non-trivial. It keeps track of the working directory as
|
|
6
|
+
it goes, just like a real shell session.
|
|
7
|
+
|
|
8
|
+
prompter "make a folder called scratch, cd into it, then run claude"
|
|
9
|
+
prompter "download uv if it isn't installed, then check the version"
|
|
10
|
+
prompter (run with no goal for the interactive REPL)
|
|
11
|
+
|
|
12
|
+
The agent's workspace is your shell, not a single repo. Claude is just one of
|
|
13
|
+
the tools it knows how to launch.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from .cli import main
|
|
19
|
+
|
|
20
|
+
__all__ = ["main"]
|
|
21
|
+
__version__ = "0.1.0"
|