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.
Files changed (36) hide show
  1. shell_prompter-0.1.0/LICENSE +21 -0
  2. shell_prompter-0.1.0/PKG-INFO +289 -0
  3. shell_prompter-0.1.0/README.md +262 -0
  4. shell_prompter-0.1.0/prompter/__init__.py +21 -0
  5. shell_prompter-0.1.0/prompter/__main__.py +10 -0
  6. shell_prompter-0.1.0/prompter/agent.py +205 -0
  7. shell_prompter-0.1.0/prompter/cli.py +458 -0
  8. shell_prompter-0.1.0/prompter/colors.py +47 -0
  9. shell_prompter-0.1.0/prompter/config.py +138 -0
  10. shell_prompter-0.1.0/prompter/constants.py +17 -0
  11. shell_prompter-0.1.0/prompter/keys.py +59 -0
  12. shell_prompter-0.1.0/prompter/prompts.py +83 -0
  13. shell_prompter-0.1.0/prompter/providers/__init__.py +47 -0
  14. shell_prompter-0.1.0/prompter/providers/anthropic_provider.py +172 -0
  15. shell_prompter-0.1.0/prompter/providers/base.py +266 -0
  16. shell_prompter-0.1.0/prompter/providers/gemini_provider.py +205 -0
  17. shell_prompter-0.1.0/prompter/providers/openai_provider.py +183 -0
  18. shell_prompter-0.1.0/prompter/risk.py +110 -0
  19. shell_prompter-0.1.0/prompter/shell.py +125 -0
  20. shell_prompter-0.1.0/prompter/ui.py +256 -0
  21. shell_prompter-0.1.0/pyproject.toml +44 -0
  22. shell_prompter-0.1.0/setup.cfg +4 -0
  23. shell_prompter-0.1.0/shell_prompter.egg-info/PKG-INFO +289 -0
  24. shell_prompter-0.1.0/shell_prompter.egg-info/SOURCES.txt +34 -0
  25. shell_prompter-0.1.0/shell_prompter.egg-info/dependency_links.txt +1 -0
  26. shell_prompter-0.1.0/shell_prompter.egg-info/entry_points.txt +2 -0
  27. shell_prompter-0.1.0/shell_prompter.egg-info/requires.txt +8 -0
  28. shell_prompter-0.1.0/shell_prompter.egg-info/top_level.txt +1 -0
  29. shell_prompter-0.1.0/tests/test_agent.py +156 -0
  30. shell_prompter-0.1.0/tests/test_cli.py +287 -0
  31. shell_prompter-0.1.0/tests/test_config.py +102 -0
  32. shell_prompter-0.1.0/tests/test_keys.py +60 -0
  33. shell_prompter-0.1.0/tests/test_providers.py +342 -0
  34. shell_prompter-0.1.0/tests/test_risk.py +49 -0
  35. shell_prompter-0.1.0/tests/test_shell.py +96 -0
  36. 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"
@@ -0,0 +1,10 @@
1
+ """Enable ``python -m prompter``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from .cli import main
8
+
9
+ if __name__ == "__main__":
10
+ sys.exit(main())