@aragon-agent/cli 0.5.0 → 0.5.2
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.
- package/CHANGELOG.md +158 -0
- package/README.md +254 -7
- package/dist/agent/agent-mode.d.ts +53 -0
- package/dist/agent/agent-mode.js +69 -0
- package/dist/agent/controller.d.ts +69 -0
- package/dist/agent/controller.js +154 -1
- package/dist/agent/plan-prompt.d.ts +18 -0
- package/dist/agent/plan-prompt.js +93 -0
- package/dist/agent/reducer.d.ts +14 -1
- package/dist/agent/reducer.js +9 -0
- package/dist/agent/system-prompt.d.ts +15 -0
- package/dist/agent/system-prompt.js +8 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +247 -23
- package/dist/commands/builtins.js +60 -0
- package/dist/commands/registry.d.ts +17 -0
- package/dist/config/app-paths.d.ts +71 -4
- package/dist/config/app-paths.js +139 -8
- package/dist/config/cli-commands.d.ts +43 -0
- package/dist/config/cli-commands.js +196 -0
- package/dist/config/env.d.ts +7 -1
- package/dist/config/env.js +53 -1
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.js +82 -2
- package/dist/config/migrate-home.d.ts +63 -0
- package/dist/config/migrate-home.js +165 -0
- package/dist/config/migrate-legacy-state.d.ts +10 -0
- package/dist/config/migrate-legacy-state.js +15 -6
- package/dist/config/schema.d.ts +81 -0
- package/dist/config/schema.js +85 -0
- package/dist/config/store.d.ts +30 -12
- package/dist/config/store.js +47 -35
- package/dist/input/mouse-events.d.ts +61 -0
- package/dist/input/mouse-events.js +153 -0
- package/dist/input/stdin-mouse-filter.d.ts +42 -0
- package/dist/input/stdin-mouse-filter.js +153 -0
- package/dist/logging/cli-commands.d.ts +25 -0
- package/dist/logging/cli-commands.js +221 -0
- package/dist/logging/file-sink.d.ts +114 -0
- package/dist/logging/file-sink.js +326 -0
- package/dist/logging/install.d.ts +163 -0
- package/dist/logging/install.js +302 -0
- package/dist/logging/levels.d.ts +40 -0
- package/dist/logging/levels.js +59 -0
- package/dist/logging/logger.d.ts +110 -0
- package/dist/logging/logger.js +237 -0
- package/dist/logging/redact.d.ts +28 -0
- package/dist/logging/redact.js +90 -0
- package/dist/logging/secret-registry.d.ts +30 -0
- package/dist/logging/secret-registry.js +55 -0
- package/dist/skills/paths.d.ts +2 -4
- package/dist/skills/paths.js +7 -9
- package/dist/tools/human-input.d.ts +179 -0
- package/dist/tools/human-input.js +227 -0
- package/dist/tools/index.d.ts +35 -3
- package/dist/tools/index.js +96 -9
- package/dist/tools/plan-tools.d.ts +64 -0
- package/dist/tools/plan-tools.js +198 -0
- package/dist/ui/App.d.ts +12 -1
- package/dist/ui/App.js +223 -17
- package/dist/ui/Composer.d.ts +9 -1
- package/dist/ui/Composer.js +26 -7
- package/dist/ui/ModeChip.d.ts +29 -0
- package/dist/ui/ModeChip.js +15 -0
- package/dist/ui/PromptInput.d.ts +22 -1
- package/dist/ui/PromptInput.js +49 -2
- package/dist/ui/StatusBar.d.ts +15 -0
- package/dist/ui/StatusBar.js +10 -2
- package/dist/ui/entries/ToolPreview.d.ts +2 -0
- package/dist/ui/entries/ToolPreview.js +64 -0
- package/dist/ui/glyphs.d.ts +21 -0
- package/dist/ui/glyphs.js +12 -0
- package/dist/ui/layout/AppShell.d.ts +10 -1
- package/dist/ui/layout/AppShell.js +38 -3
- package/dist/ui/layout/ScrollIndicator.d.ts +31 -0
- package/dist/ui/layout/ScrollIndicator.js +15 -0
- package/dist/ui/layout/ScrollViewport.d.ts +8 -2
- package/dist/ui/layout/ScrollViewport.js +24 -4
- package/dist/ui/layout/regions.d.ts +55 -0
- package/dist/ui/layout/regions.js +52 -0
- package/dist/ui/layout/scroll-indicator.d.ts +27 -0
- package/dist/ui/layout/scroll-indicator.js +35 -0
- package/dist/ui/layout/scroll.d.ts +9 -0
- package/dist/ui/layout/scroll.js +23 -0
- package/dist/ui/layout/wrap-rows.d.ts +24 -0
- package/dist/ui/layout/wrap-rows.js +74 -0
- package/dist/ui/overlays/HelpOverlay.js +15 -0
- package/dist/ui/overlays/PlanReviewOverlay.d.ts +50 -0
- package/dist/ui/overlays/PlanReviewOverlay.js +117 -0
- package/dist/ui/overlays/QuestionOverlay.d.ts +30 -0
- package/dist/ui/overlays/QuestionOverlay.js +193 -0
- package/dist/ui/overlays/SettingsScreen.d.ts +7 -0
- package/dist/ui/overlays/SettingsScreen.js +8 -1
- package/dist/ui/screen.d.ts +14 -1
- package/dist/ui/screen.js +37 -3
- package/dist/ui/use-startup-notices.d.ts +25 -0
- package/dist/ui/use-startup-notices.js +64 -0
- package/dist/ui/use-wheel-routing.d.ts +68 -0
- package/dist/ui/use-wheel-routing.js +139 -0
- package/package.json +3 -2
- package/runtime/insecure-tls-warning.cjs +62 -0
- package/runtime/insecure-tls-warning.d.cts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,164 @@ All notable changes to `@aragon-agent/cli` are documented here. Entries at
|
|
|
5
5
|
— those releases really were published under that name, so they are left as they
|
|
6
6
|
were written.
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **The mouse wheel scrolls what is under the pointer.** In full-screen mode the
|
|
13
|
+
wheel now scrolls the transcript three rows per notch (a page with `Shift`),
|
|
14
|
+
scrolls an open overlay when the pointer is over one, and steps prompt history
|
|
15
|
+
one entry per notch when the pointer is over the input box — the same code
|
|
16
|
+
path `↑` / `↓` use, so it inherits the rule that an in-progress draft is never
|
|
17
|
+
overwritten. A slim rail on the viewport's right edge shows where you are in
|
|
18
|
+
the conversation.
|
|
19
|
+
|
|
20
|
+
**This fixes a destructive bug.** Previously the wheel recalled prompt history
|
|
21
|
+
no matter where the pointer was, because the alternate screen makes terminals
|
|
22
|
+
translate each notch into arrow keys. Wheeling while composing a message
|
|
23
|
+
silently replaced the draft with an old prompt, and nothing scrolled.
|
|
24
|
+
|
|
25
|
+
**Selecting text with the mouse now requires `Shift`+drag** in most terminals.
|
|
26
|
+
That is the standard convention for "an application is tracking the mouse" and
|
|
27
|
+
it applies to everyone, including people who never scroll with the wheel — so
|
|
28
|
+
the first session says so once, in the transcript, and never again. Turn the
|
|
29
|
+
whole feature off with `--no-mouse`, `ARAGON_MOUSE=0`, or
|
|
30
|
+
`aragon config set mouse false`; the wheel then does nothing rather than
|
|
31
|
+
reverting to the old behaviour. Inline mode (`--no-fullscreen`) is untouched:
|
|
32
|
+
your terminal's own scrollback already does the right thing there.
|
|
33
|
+
|
|
34
|
+
Under `tmux` with `mouse on`, tmux keeps the wheel for itself and the app
|
|
35
|
+
never sees it. `set -g mouse off` hands it back.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- **Your files moved to `~/.aragon-agent/`.** Config, sessions, installed skills
|
|
40
|
+
and the usage counters now live in one directory in your home — on Windows
|
|
41
|
+
`C:\Users\<you>\.aragon-agent\`, and the same path on macOS and Linux. They
|
|
42
|
+
used to be split across `%APPDATA%\aragon-agent-nodejs\Config` and
|
|
43
|
+
`%LOCALAPPDATA%\aragon-agent-nodejs\Data` (or the XDG equivalents), which are
|
|
44
|
+
correct locations and terrible answers to "where do I change the model?".
|
|
45
|
+
|
|
46
|
+
**The move happens once, automatically, on first launch, and prints a line on
|
|
47
|
+
stderr when it does.** Nothing is deleted: `config.json` is COPIED rather than
|
|
48
|
+
moved, and the old directories are left where they are, so reinstalling 0.5.0
|
|
49
|
+
remains lossless. `aragon config home` prints the new location and names the
|
|
50
|
+
old ones. Once you are satisfied, you can delete them yourself.
|
|
51
|
+
|
|
52
|
+
`ARAGON_HOME` relocates the whole tree. It has to be a real environment
|
|
53
|
+
variable — the path is resolved before any `.env` is read, so putting it in a
|
|
54
|
+
`.env` will not work. `aragon config home` always prints what is actually in
|
|
55
|
+
use.
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- **Logging.** Records go to `~/.aragon-agent/logs/aragon-<date>.log` as JSON
|
|
60
|
+
Lines: startup and exit, config writes, turn boundaries with token counts,
|
|
61
|
+
tool names with durations, skill loads, and every uncaught error. Files rotate
|
|
62
|
+
by day and by size (`log.maxFileBytes`, default 5 MiB) and the oldest beyond
|
|
63
|
+
`log.maxFiles` (default 10) are deleted.
|
|
64
|
+
|
|
65
|
+
**`info`, the default, records no conversation content**, so attaching a log
|
|
66
|
+
to a bug report is safe. `--log-level debug` (or `--verbose`) adds prompts,
|
|
67
|
+
replies and tool arguments truncated to `log.previewChars`; `trace` adds them
|
|
68
|
+
in full. Those two write what you typed — check before sharing one.
|
|
69
|
+
|
|
70
|
+
API keys are removed at every level by two independent passes: fields whose
|
|
71
|
+
name looks like a credential are blanked whatever they hold, and every string
|
|
72
|
+
is scanned both for known key formats and for the literal keys this process is
|
|
73
|
+
holding — which is what covers a custom endpoint whose key looks like nothing
|
|
74
|
+
in particular. `log.redactSecrets false` turns both off and warns you on
|
|
75
|
+
stdout when you set it.
|
|
76
|
+
|
|
77
|
+
Nothing is ever uploaded. There is no network path out of this subsystem.
|
|
78
|
+
|
|
79
|
+
New commands: `aragon logs path | list | tail [-n N] [--follow] [--level lv]
|
|
80
|
+
[--json] | clear --yes | open`, and `/logs` inside the TUI. New flags:
|
|
81
|
+
`--log-level`, `--verbose`, `--log-file` / `--no-log-file`, `--log-dir`. New
|
|
82
|
+
environment variables: `ARAGON_LOG_LEVEL`, `ARAGON_LOG_FILE`, `ARAGON_LOG_DIR`
|
|
83
|
+
— these three, unlike `ARAGON_HOME`, can come from a `.env`.
|
|
84
|
+
|
|
85
|
+
- **`aragon config get <key>` / `list [--json]` / `edit` / `home`.** `get` and
|
|
86
|
+
`list` mask every secret, `--json` included. `edit` opens `config.json` in
|
|
87
|
+
`$VISUAL` / `$EDITOR` (`notepad` on Windows), takes a `config.json.bak` first,
|
|
88
|
+
and re-parses on exit — an invalid edit is reported immediately, and the
|
|
89
|
+
backup is named in the message.
|
|
90
|
+
|
|
91
|
+
- **`/reload`** re-reads `config.json` into the running session, for when you
|
|
92
|
+
have edited it in another window. Refused while a run is in progress: swapping
|
|
93
|
+
the model mid-turn would let the second half of an answer come from different
|
|
94
|
+
settings than the first, with nothing afterwards able to explain the result.
|
|
95
|
+
|
|
96
|
+
- **Log level in the settings screen**, alongside provider / model / thinking.
|
|
97
|
+
The rest of the log settings — and `redactSecrets` above all — stay
|
|
98
|
+
command-line-only, so the one switch that can put credentials on disk cannot
|
|
99
|
+
be flipped with an arrow key.
|
|
100
|
+
|
|
101
|
+
### Fixed
|
|
102
|
+
|
|
103
|
+
- **A malformed `config.json` no longer resets everything in silence.** A stray
|
|
104
|
+
comma used to send the model, theme, timeouts, skills and (as far as you could
|
|
105
|
+
tell) your API key back to defaults, with nothing anywhere explaining it. The
|
|
106
|
+
fallback is unchanged — a bad config still never stops the CLI from starting —
|
|
107
|
+
but it now says so, and records why.
|
|
108
|
+
|
|
109
|
+
- **Ctrl+C now flushes pending log records in every mode.** The signal handlers
|
|
110
|
+
only existed in the full-screen branch, so `-p`, the inline renderer and every
|
|
111
|
+
subcommand took Node's default termination, which does not run exit handlers.
|
|
112
|
+
|
|
113
|
+
### Added
|
|
114
|
+
|
|
115
|
+
- **Plan mode.** `Shift+Tab` toggles between `BUILD` (everything as before) and
|
|
116
|
+
`PLAN`, a read-only research-and-design posture. `/plan [on|off|status]` is a
|
|
117
|
+
first-class equivalent for terminals that swallow `CSI Z`, and `--plan` /
|
|
118
|
+
`--no-plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the startup case.
|
|
119
|
+
|
|
120
|
+
In `PLAN`, `write_file`, `edit_file`, `bash`, `skill_install` and
|
|
121
|
+
`skill_create` are refused **at the tool boundary**, so read-only is a
|
|
122
|
+
property of the wiring rather than a promise in a prompt. `bash` is refused in
|
|
123
|
+
full, `git status` included: a shell string cannot be classified reliably, and
|
|
124
|
+
a gate that is right most of the time is worse than one that is always right,
|
|
125
|
+
because you stop trusting the badge. The refusal names `read_file` / `glob` /
|
|
126
|
+
`grep` as the alternatives.
|
|
127
|
+
|
|
128
|
+
Two tools exist only while a human is attached. `ask_user` renders a
|
|
129
|
+
keyboard-driven wizard - 1-5 questions, 2-4 options each, exactly one marked
|
|
130
|
+
`RECOMMENDED`, plus an `Other...` free-text option - where pressing `Enter`
|
|
131
|
+
through the wizard accepts every recommendation. `submit_plan` renders the
|
|
132
|
+
plan as a scrollable card: `a` approves, `r` sends written feedback (`<-`
|
|
133
|
+
returns to the card), `Esc` dismisses. **Approving flips the session to
|
|
134
|
+
`BUILD` immediately and the same run continues into implementation.**
|
|
135
|
+
|
|
136
|
+
Dismissing a plan asks for a better one rather than stopping - the agent is
|
|
137
|
+
told to refine and resubmit - so the way out of a run is `Esc` twice: the
|
|
138
|
+
first closes the card, the second aborts the run.
|
|
139
|
+
|
|
140
|
+
Toggling mid-run is asymmetric on purpose: `build -> plan` applies at once,
|
|
141
|
+
`plan -> build` is deferred to the end of the run (the status bar shows
|
|
142
|
+
`PLAN -> BUILD` meanwhile) so a run launched under a read-only guarantee
|
|
143
|
+
cannot start writing files because of one stray keypress.
|
|
144
|
+
|
|
145
|
+
`aragon -p --plan "..."` registers no question or plan tool - there is nobody
|
|
146
|
+
to ask - but the read-only gate still applies, so it means "tell me how you
|
|
147
|
+
would do this, and do not touch my repository".
|
|
148
|
+
|
|
149
|
+
- Three flat config keys: `planModeDefault` (default `false`),
|
|
150
|
+
`planModeMaxAskRounds` (default `4`, clamped `[1, 10]`) and
|
|
151
|
+
`planModeHumanTimeoutMs` (default `1800000`, clamped `[60000, 7200000]`). All
|
|
152
|
+
three are settable with `aragon config set`. Additive, as always: a config
|
|
153
|
+
file written by an earlier version loads unchanged.
|
|
154
|
+
|
|
155
|
+
### Fixed
|
|
156
|
+
|
|
157
|
+
- A `--confirm` prompt no longer races the idle watchdog. Taking longer than
|
|
158
|
+
`idleTimeoutMs` over `Proceed? (y/N)` used to abort the run with no
|
|
159
|
+
explanation; every human wait now suspends the watchdog and restarts it in a
|
|
160
|
+
`finally`.
|
|
161
|
+
|
|
162
|
+
- Honor an explicit `NODE_TLS_REJECT_UNAUTHORIZED=0` setting in the CLI and the
|
|
163
|
+
official npm release workflow while hiding only Node's standard insecure-TLS
|
|
164
|
+
warning. Other process warnings remain visible.
|
|
165
|
+
|
|
8
166
|
## 0.5.0
|
|
9
167
|
|
|
10
168
|
### Breaking
|
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ aragon --help | -h Print help
|
|
|
71
71
|
| `--fullscreen` | Force the full-screen TUI, overriding the automatic downgrades |
|
|
72
72
|
| `--no-fullscreen` | Force the inline renderer (the 0.2.0 behavior) |
|
|
73
73
|
| `--no-exit-transcript` | Do not replay the session summary after exiting |
|
|
74
|
+
| `--plan` / `--no-plan` | Start the session in PLAN mode (read-only research + review) or force BUILD |
|
|
74
75
|
| `--no-skills` | Disable the skill system entirely (no catalog, no skill tools) |
|
|
75
76
|
| `--skill <name>` | Force-load a skill's full body for this run (repeatable) |
|
|
76
77
|
| `--skills-yes` | Approve skill installs for this run (CI / headless) |
|
|
@@ -94,9 +95,12 @@ Exit codes: `0` success · `1` agent/runtime error · `2` config/usage error ·
|
|
|
94
95
|
| `/cwd [dir]` | Show / change the tool working directory |
|
|
95
96
|
| `/save [file]` | Save the session to JSON |
|
|
96
97
|
| `/resume [file]` | Load a saved session |
|
|
98
|
+
| `/plan [on\|off\|status]` | Toggle plan mode - the keyboard-free equivalent of `Shift+Tab` |
|
|
97
99
|
| `/theme <auto\|warm\|cool\|light>` | Switch the color theme live (persisted) |
|
|
98
100
|
| `/expand [n]` | Expand / collapse the n-th-from-last (default last) tool card |
|
|
99
101
|
| `/copy` | Copy the last answer to the clipboard |
|
|
102
|
+
| `/logs` | Show where this session is being logged, and at what level |
|
|
103
|
+
| `/reload` | Re-read `config.json` after editing it in another window (refused mid-run) |
|
|
100
104
|
| `/skills [sub]` | List and manage skills (see below) |
|
|
101
105
|
| `/<skill-name> [args]` | Run an installed skill directly |
|
|
102
106
|
| `/exit` (`/quit`) | Exit |
|
|
@@ -111,6 +115,7 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
111
115
|
| --- | --- |
|
|
112
116
|
| `Enter` | Submit (idle) / queue a steering message (running) |
|
|
113
117
|
| `Alt+Enter` / `Shift+Enter` | Insert a newline |
|
|
118
|
+
| `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan` |
|
|
114
119
|
| `Esc` | Abort the run / close an overlay / close a popup |
|
|
115
120
|
| `Ctrl+C` ×2 | Exit (first press warns) |
|
|
116
121
|
| `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
|
|
@@ -118,6 +123,8 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
118
123
|
| `Ctrl+O` | Expand / collapse the most recent tool card |
|
|
119
124
|
| `PgUp` / `PgDn` | Scroll the transcript a page (full-screen mode) |
|
|
120
125
|
| `Shift+↑` / `Shift+↓` | Scroll the transcript a line (full-screen mode) |
|
|
126
|
+
| Mouse wheel | Scroll the transcript (over the input box: prompt history) |
|
|
127
|
+
| `Shift`+wheel | Scroll the transcript a page |
|
|
121
128
|
| `Home` / `End`, `Ctrl+A` / `Ctrl+E` | Cursor to line start / end |
|
|
122
129
|
| `Alt/Ctrl+←` / `→` | Word-wise cursor jump |
|
|
123
130
|
| `Ctrl+W`, `Alt+Backspace` | Delete the previous word |
|
|
@@ -127,10 +134,31 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
127
134
|
|
|
128
135
|
In **full-screen mode** the transcript is scrolled by the app itself: `PgUp` /
|
|
129
136
|
`PgDn` and `Shift+↑` / `Shift+↓`. While pinned to the bottom the viewport follows
|
|
130
|
-
new output automatically; once you scroll away the status bar shows `↑N
|
|
131
|
-
|
|
132
|
-
newest output. In **inline mode**
|
|
133
|
-
terminal's native scrollback, printed
|
|
137
|
+
new output automatically; once you scroll away the status bar shows `↑N`, a slim
|
|
138
|
+
rail on the right edge shows where you are, and a hint counts the lines below
|
|
139
|
+
you. Submitting a message always re-pins to the newest output. In **inline mode**
|
|
140
|
+
(`--no-fullscreen`) history lives in your terminal's native scrollback, printed
|
|
141
|
+
once via Ink `<Static>`.
|
|
142
|
+
|
|
143
|
+
**The mouse wheel scrolls whatever is under the pointer.** Over the transcript
|
|
144
|
+
it scrolls the transcript three rows per notch (a page with `Shift`); over an
|
|
145
|
+
open overlay it scrolls that overlay; over the input box at the bottom it steps
|
|
146
|
+
prompt history one entry per notch, exactly as `↑` / `↓` do — including the rule
|
|
147
|
+
that an in-progress draft is never overwritten.
|
|
148
|
+
|
|
149
|
+
Two things follow from the app owning the mouse, and they are worth knowing
|
|
150
|
+
before you meet them:
|
|
151
|
+
|
|
152
|
+
- **Selecting text now needs `Shift`+drag** in most terminals. That is the
|
|
153
|
+
terminal's convention for "an application is tracking the mouse", not
|
|
154
|
+
something this CLI can opt out of while still reading wheel events.
|
|
155
|
+
- **`tmux` with `mouse on` keeps the wheel for itself**, so the app never sees
|
|
156
|
+
it. `set -g mouse off` hands it back.
|
|
157
|
+
|
|
158
|
+
Turn the whole thing off with `--no-mouse`, `ARAGON_MOUSE=0`, or
|
|
159
|
+
`aragon config set mouse false`. With mouse support off the wheel simply does
|
|
160
|
+
nothing in full-screen mode — it does not fall back to editing your draft, which
|
|
161
|
+
is what it used to do. Inline mode is unaffected either way.
|
|
134
162
|
|
|
135
163
|
## Themes
|
|
136
164
|
|
|
@@ -184,6 +212,90 @@ this mode, and mouse tracking is deliberately left off because enabling it costs
|
|
|
184
212
|
text selection and copy in most terminals. If a crash ever strands your terminal
|
|
185
213
|
on the alternate screen, `reset` restores it.
|
|
186
214
|
|
|
215
|
+
## Plan mode
|
|
216
|
+
|
|
217
|
+
`aragon` has two postures, and `Shift+Tab` switches between them. `/plan` does
|
|
218
|
+
the same thing for terminals that swallow the key (see below).
|
|
219
|
+
|
|
220
|
+
| Mode | What it means |
|
|
221
|
+
| --- | --- |
|
|
222
|
+
| `BUILD` | The default, and unchanged from earlier versions: you ask, the agent does it. |
|
|
223
|
+
| `PLAN` | Research and design only. The agent reads, asks you the decisions it cannot settle by reading, and submits a plan for approval before anything is written. |
|
|
224
|
+
|
|
225
|
+
`BUILD` is right for "rename this symbol". `PLAN` is right for "add multi-tenant
|
|
226
|
+
support" — anything where you want to see and steer the *approach* before a
|
|
227
|
+
single byte changes.
|
|
228
|
+
|
|
229
|
+
### What plan mode actually stops
|
|
230
|
+
|
|
231
|
+
In `PLAN`, five tools are refused at the tool boundary: `write_file`,
|
|
232
|
+
`edit_file`, `bash`, `skill_install` and `skill_create`. That is a property of
|
|
233
|
+
the wiring, not a promise in a prompt — the refusal happens before the tool runs,
|
|
234
|
+
so the badge is telling you something that is structurally true.
|
|
235
|
+
|
|
236
|
+
**`bash` is refused in full, `git status` included.** This is the most likely
|
|
237
|
+
question this feature generates, so: there is no reliable way to classify a shell
|
|
238
|
+
string as read-only, and a gate that is right 95 % of the time is worse than one
|
|
239
|
+
that is always right, because you stop trusting the badge. Use `read_file`,
|
|
240
|
+
`list_dir`, `glob` and `grep`, which stay available; the refusal message names
|
|
241
|
+
them so the agent has somewhere to go. There is no allowlist and there will not
|
|
242
|
+
be one built out of substring matching.
|
|
243
|
+
|
|
244
|
+
Everything else still works: reading, searching, loading skills, and asking you
|
|
245
|
+
questions.
|
|
246
|
+
|
|
247
|
+
### The two plan-mode tools
|
|
248
|
+
|
|
249
|
+
- **`ask_user`** renders a keyboard-driven wizard: 1–5 questions, 2–4 options
|
|
250
|
+
each, exactly one marked `RECOMMENDED`, plus an `Other…` free-text option.
|
|
251
|
+
`↑`/`↓` choose, `Enter` confirms and advances, `←` goes back, `Esc` cancels.
|
|
252
|
+
Pressing `Enter` through the whole wizard accepts every recommendation, which
|
|
253
|
+
is a deliberate "use your judgement" path rather than an accident. A last
|
|
254
|
+
screen lets you review before submitting.
|
|
255
|
+
- **`submit_plan`** renders the plan as a scrollable card: `a` approves, `r`
|
|
256
|
+
opens a one-line feedback field (`Enter` sends it, `←` goes back to the card),
|
|
257
|
+
`Esc` dismisses. **Approving flips the session to `BUILD` immediately and the
|
|
258
|
+
same run continues straight into implementation** — you do not have to re-ask.
|
|
259
|
+
|
|
260
|
+
Cancelling either one is not an error. The agent is told to proceed on stated
|
|
261
|
+
assumptions rather than to retry.
|
|
262
|
+
|
|
263
|
+
**Dismissing a plan is not how you stop.** `Esc` on a plan card asks for a
|
|
264
|
+
*better* plan — the agent is told to refine it and submit again — so a model that
|
|
265
|
+
keeps submitting keeps getting cards. To stop the run itself, **press `Esc`
|
|
266
|
+
twice**: the first closes the card, the second aborts the run. `Ctrl+C` twice
|
|
267
|
+
still exits the session outright.
|
|
268
|
+
|
|
269
|
+
### Where the mode is shown
|
|
270
|
+
|
|
271
|
+
The status bar always names a non-default mode (`PLAN`, or `PLAN → BUILD` while
|
|
272
|
+
a switch is pending), including on short terminals, with `--no-hints`, and in
|
|
273
|
+
inline mode. The composer additionally shows a `PLAN` chip on its hint row and
|
|
274
|
+
tints its border, where there is room for it.
|
|
275
|
+
|
|
276
|
+
### Switching mid-run
|
|
277
|
+
|
|
278
|
+
Tightening applies at once; loosening waits. Pressing `Shift+Tab` toward `PLAN`
|
|
279
|
+
during a run takes effect immediately — you pressed it *because* the agent is
|
|
280
|
+
about to do something. Pressing it toward `BUILD` during a run is **deferred to
|
|
281
|
+
the end of the run**, so a session you launched under a read-only guarantee
|
|
282
|
+
cannot start writing files because of one stray keypress. The status bar shows
|
|
283
|
+
`PLAN → BUILD` while that is pending. Approving a plan is the one exception, and
|
|
284
|
+
it is an informed act rather than a stray keypress.
|
|
285
|
+
|
|
286
|
+
### If `Shift+Tab` does nothing
|
|
287
|
+
|
|
288
|
+
Some multiplexers and remote-desktop stacks never send `CSI Z`. `/plan`,
|
|
289
|
+
`/plan on`, `/plan off` and `/plan status` are first-class equivalents, and
|
|
290
|
+
`--plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the startup case.
|
|
291
|
+
|
|
292
|
+
### Headless
|
|
293
|
+
|
|
294
|
+
`aragon -p --plan "add SSO"` is a useful combination: the read-only gate still
|
|
295
|
+
applies, so it means *"tell me how you would do this, and do not touch my
|
|
296
|
+
repository"*. No question or plan tool is registered there — there is nobody to
|
|
297
|
+
ask — and the agent writes the finished plan as its final message in markdown.
|
|
298
|
+
|
|
187
299
|
## Built-in tools
|
|
188
300
|
|
|
189
301
|
`read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `grep`, `bash` — all
|
|
@@ -484,17 +596,26 @@ env / `.env` → CLI flags**.
|
|
|
484
596
|
- **Env / `.env`**: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
|
|
485
597
|
`GOOGLE_API_KEY` / `GEMINI_API_KEY`, plus `ARAGON_PROVIDER`, `ARAGON_MODEL`,
|
|
486
598
|
`ARAGON_BASE_URL`, `ARAGON_THINKING`, `ARAGON_MAX_TOKENS`, `ARAGON_THEME`,
|
|
487
|
-
`ARAGON_FULLSCREEN`, `
|
|
488
|
-
`
|
|
599
|
+
`ARAGON_FULLSCREEN`, `ARAGON_PLAN` (`1` starts in plan mode),
|
|
600
|
+
`ARAGON_SKILLS` (`0` disables), `ARAGON_SKILLS_PATH`,
|
|
601
|
+
`ARAGON_SKILLS_DISABLED` (comma-separated names), `ARAGON_LOG_LEVEL`,
|
|
602
|
+
`ARAGON_LOG_FILE` (`0` disables), `ARAGON_LOG_DIR`.
|
|
603
|
+
`ARAGON_HOME` is the exception that **cannot** come from `.env`: it is read
|
|
604
|
+
before any `.env` is loaded. See [Files & logs](#files--logs).
|
|
489
605
|
- **User config file** (`aragon config path`): JSON, written `0600` on POSIX.
|
|
490
606
|
Holds provider/model/keys and MRU lists. Keys are masked in the UI and never
|
|
491
|
-
logged.
|
|
607
|
+
logged. It lives in your home directory — see [Files & logs](#files--logs) —
|
|
608
|
+
and editing it by hand is supported: `aragon config edit` opens it, and an
|
|
609
|
+
invalid edit is reported rather than silently resetting every setting.
|
|
492
610
|
|
|
493
611
|
| Config key | Default | Meaning |
|
|
494
612
|
| --- | --- | --- |
|
|
495
613
|
| `fullscreen` | `true` | Use the full-screen TUI (still subject to the automatic downgrades). Setting it to `false` opts out permanently; leaving it `true` is *not* a force — only `--fullscreen` / `ARAGON_FULLSCREEN=1` override the heuristics. |
|
|
496
614
|
| `exitTranscript` | `true` | Replay a plain-text session summary after exiting (full-screen only). |
|
|
497
615
|
| `transcriptWindow` | `300` | How many trailing entries the full-screen viewport renders, clamped to `[50, 2000]`. The full history stays in memory and in `/save`. |
|
|
616
|
+
| `planModeDefault` | `false` | Start every session in plan mode. `--no-plan` still overrides it for a single run. |
|
|
617
|
+
| `planModeMaxAskRounds` | `4` | `ask_user` rounds allowed per user message, clamped to `[1, 10]`. The counter resets on your next message; steering does **not** reset it. |
|
|
618
|
+
| `planModeHumanTimeoutMs` | `1800000` | Ceiling on a single question / plan wait, clamped to `[60000, 7200000]`. Thirty minutes is far outside "went to get coffee", but a ceiling has to exist so an unattended terminal cannot wedge a CI job forever. |
|
|
498
619
|
| `skills.enabled` | `true` | Master switch for the skill system. |
|
|
499
620
|
| `skills.requireApproval` | `true` | Require a human to approve `skill_install` / `skill_create`. |
|
|
500
621
|
| `skills.catalogMaxBytes` | `6000` | Level 1 catalog ceiling, in UTF-8 **bytes**, clamped to `[500, 40000]`. |
|
|
@@ -505,6 +626,13 @@ env / `.env` → CLI flags**.
|
|
|
505
626
|
| `skills.integrity` | `warn` | What to do when `SKILL.md` no longer matches the copy you approved. `off` skips the check; `warn` flags it in `/skills list` and once per session; `strict` removes it from the catalog and makes `skill()` refuse it. Defaults to `warn` because editing an installed skill is a legitimate thing to do — `strict` suits CI and managed environments. |
|
|
506
627
|
| `skills.usageTracking` | `true` | Keep local per-skill use counters to rank the catalog. Name, count and timestamp only; never transmitted. Inspect with `aragon skills usage`, erase with `--reset --yes`. |
|
|
507
628
|
| `skills.toolPolicy` | `enforce` | How hard a skill's `allowed-tools` bites, for the turn it is loaded in. `off` disables it and says nothing about it to the model; `warn` runs the tool and reports it; `enforce` refuses. Unlike `skills.integrity`, a false positive here means a skill AUTHOR under-declared their own skill — which `aragon skills doctor` reports before it can bite. |
|
|
629
|
+
| `log.level` | `info` | `silent` · `error` · `warn` · `info` · `debug` · `trace`. See [Files & logs](#files--logs) for what each level records. |
|
|
630
|
+
| `log.toFile` | `true` | Write records to a file at all. `false` here is respected; `--log-file` overrides it for one run. |
|
|
631
|
+
| `log.dir` | `""` | Log directory. Empty means `<home>/logs`. Only **absolute** paths are accepted — a relative one would mean a different directory for every working directory you launch from. |
|
|
632
|
+
| `log.maxFileBytes` | `5242880` | Rotate once a file would exceed this, clamped to `[65536, 268435456]`. |
|
|
633
|
+
| `log.maxFiles` | `10` | Files kept in the log directory, clamped to `[1, 200]`. The oldest beyond this are deleted — on rotation **and** on the first write of each day. |
|
|
634
|
+
| `log.redactSecrets` | `true` | Run every record through redaction. Setting it to `false` is the one way to get credentials onto disk, so it prints a warning and has no settings-screen entry. |
|
|
635
|
+
| `log.previewChars` | `512` | How much user content `debug` keeps per field, clamped to `[0, 8192]`. `trace` is never truncated. |
|
|
508
636
|
|
|
509
637
|
Byte, not character: `ToolExecutor` truncates at 100 000 **bytes**, so a
|
|
510
638
|
character-based budget would be roughly triple its nominal size for CJK text
|
|
@@ -513,6 +641,108 @@ and would get cut mid-tag.
|
|
|
513
641
|
These are additive: a config file written by an earlier version loads
|
|
514
642
|
unchanged, with no migration and no version bump.
|
|
515
643
|
|
|
644
|
+
## Files & logs
|
|
645
|
+
|
|
646
|
+
Everything AragonAgent keeps for you lives under one directory in your home:
|
|
647
|
+
|
|
648
|
+
```
|
|
649
|
+
~/.aragon-agent/ (Windows: C:\Users\<you>\.aragon-agent\)
|
|
650
|
+
├── config.json settings + API keys (0600 on POSIX)
|
|
651
|
+
├── config.json.bak written by `aragon config edit` before it opens
|
|
652
|
+
├── logs/
|
|
653
|
+
│ ├── aragon-2026-07-27.log today, JSON Lines
|
|
654
|
+
│ └── aragon-2026-07-27.1.log rotated at log.maxFileBytes
|
|
655
|
+
├── sessions/ /save and /resume
|
|
656
|
+
├── skills/ aragon skills install
|
|
657
|
+
└── skill-usage.json
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
`ARAGON_HOME` moves the whole tree. **It cannot come from a `.env` file** — the
|
|
661
|
+
path is resolved when the process starts, long before a `.env` is read — so set
|
|
662
|
+
it as a real environment variable. `aragon config home` always prints the
|
|
663
|
+
directory actually in use.
|
|
664
|
+
|
|
665
|
+
Upgrading from 0.5.0 or earlier moves your config, sessions and skills here
|
|
666
|
+
automatically, once, and prints a line on stderr when it does. The old
|
|
667
|
+
`%APPDATA%\aragon-agent-nodejs` / `%LOCALAPPDATA%\aragon-agent-nodejs`
|
|
668
|
+
directories are **left in place** so that reinstalling an older build stays
|
|
669
|
+
lossless; `aragon config home` names them, and you can delete them once you are
|
|
670
|
+
satisfied.
|
|
671
|
+
|
|
672
|
+
### What gets logged
|
|
673
|
+
|
|
674
|
+
Logs are local files. Nothing is uploaded, ever — there is no network path out
|
|
675
|
+
of this subsystem at all.
|
|
676
|
+
|
|
677
|
+
| Level | Records | Contains your content? |
|
|
678
|
+
| --- | --- | --- |
|
|
679
|
+
| `error` | crashes, failed requests, config write/parse failures | no |
|
|
680
|
+
| `warn` | degraded migrations, clamped values, dropped records | no |
|
|
681
|
+
| `info` *(default)* | start/exit, config writes, turn boundaries with token counts, tool names and durations | **no** |
|
|
682
|
+
| `debug` | the above, plus prompts, replies and tool arguments truncated to `log.previewChars` | yes, truncated |
|
|
683
|
+
| `trace` | full message bodies and full tool output | yes, in full |
|
|
684
|
+
|
|
685
|
+
The default is `info` specifically so that attaching a log to a bug report is
|
|
686
|
+
safe. **`debug` and `trace` write what you typed and what the model replied** —
|
|
687
|
+
check before sharing one.
|
|
688
|
+
|
|
689
|
+
API keys are removed at every level, by two independent passes: any field whose
|
|
690
|
+
name looks like a credential is blanked whatever it holds, and every string is
|
|
691
|
+
scanned for known key formats *and* for the literal keys this process is
|
|
692
|
+
holding. The second half is what covers a custom endpoint whose key looks like
|
|
693
|
+
nothing in particular. Setting `log.redactSecrets false` disables both, and says
|
|
694
|
+
so on stdout when you do.
|
|
695
|
+
|
|
696
|
+
### Commands
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
aragon logs path # where records are going right now, and at what level
|
|
700
|
+
aragon logs list # files, sizes, timestamps
|
|
701
|
+
aragon logs tail -n 50 # render the last 50 records
|
|
702
|
+
aragon logs tail --follow # keep printing new ones
|
|
703
|
+
aragon logs tail --json # raw JSON Lines, for jq
|
|
704
|
+
aragon logs tail --level warn # only warn and above
|
|
705
|
+
aragon logs clear --yes # delete every log file
|
|
706
|
+
aragon logs open # open the directory in your file manager
|
|
707
|
+
|
|
708
|
+
aragon config home # the user-state root (and whether ARAGON_HOME set it)
|
|
709
|
+
aragon config get log.level # one value; secrets always masked
|
|
710
|
+
aragon config list --json # everything; secrets always masked here too
|
|
711
|
+
aragon config edit # open config.json in $VISUAL / $EDITOR
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
Per-run overrides, none of which are persisted:
|
|
715
|
+
|
|
716
|
+
```bash
|
|
717
|
+
aragon --log-level debug "…" # or --verbose, which means the same thing
|
|
718
|
+
aragon --no-log-file "…" # nothing reaches a file this run
|
|
719
|
+
aragon --log-dir D:\logs "…"
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
`ARAGON_LOG_LEVEL`, `ARAGON_LOG_FILE=0` and `ARAGON_LOG_DIR` do the same from the
|
|
723
|
+
environment, and unlike `ARAGON_HOME` these three *can* come from a `.env`.
|
|
724
|
+
|
|
725
|
+
### Insecure TLS opt-in
|
|
726
|
+
|
|
727
|
+
When `NODE_TLS_REJECT_UNAUTHORIZED` is the exact string `0`, the installed or
|
|
728
|
+
locally built `aragon` process honors that setting and suppresses only Node's
|
|
729
|
+
standard insecure-TLS warning. Every unrelated process warning remains visible,
|
|
730
|
+
and any other value leaves warning behavior unchanged. The check happens when a
|
|
731
|
+
warning is emitted, so a `0` loaded from the project `.env` is covered too; the
|
|
732
|
+
CLI never creates, normalizes, or persists this variable.
|
|
733
|
+
|
|
734
|
+
> **Security warning:** `0` genuinely disables certificate verification. It does
|
|
735
|
+
> not make an insecure connection safe and can expose credentials or request
|
|
736
|
+
> contents to interception. Use it only in a controlled environment where you
|
|
737
|
+
> accept that risk.
|
|
738
|
+
|
|
739
|
+
The automatic filter starts with the `aragon` process. An earlier `npm install`
|
|
740
|
+
or `npx` bootstrap belongs to npm and is outside the installed package's process
|
|
741
|
+
boundary, so that parent may still print Node's warning. The official repository
|
|
742
|
+
release flow covers its npm children through `publish-latest.ps1`; callers that
|
|
743
|
+
own another npm/npx parent must preload the filter in that parent themselves if
|
|
744
|
+
quiet output is required.
|
|
745
|
+
|
|
516
746
|
### Theming & color
|
|
517
747
|
|
|
518
748
|
`--theme` / `config.theme` / `/theme` pick a palette: `warm`, `cool`, `light`, or
|
|
@@ -552,6 +782,23 @@ node packages/cli/dist/cli.js --version
|
|
|
552
782
|
- `echo "list files" | node dist/cli.js -p` prints an answer and exits `0`.
|
|
553
783
|
- A missing/invalid key produces a visible, actionable message (never a blank
|
|
554
784
|
screen), both in the TUI and headless.
|
|
785
|
+
- `Shift+Tab` at 40, 80 and 120 columns - the chip, the hint and the status bar
|
|
786
|
+
are all legible and nothing wraps onto a second row.
|
|
787
|
+
- The question wizard at 24 rows with 5 questions x 4 options - no clipping, and
|
|
788
|
+
the `n/N` counter matches what is on screen.
|
|
789
|
+
- Approve a plan and watch the SAME run start writing files, with no second
|
|
790
|
+
prompt.
|
|
791
|
+
- `--no-color` on a legacy `cmd.exe` - the chip renders `[PLAN]`, radios render
|
|
792
|
+
`*`/`o`, checkboxes render `[x]`/`[ ]`, no mojibake anywhere.
|
|
793
|
+
- A plan with a 600-character summary and a 400-character step detail - the text
|
|
794
|
+
wraps and scrolls, the footer count matches the real number of rows, and
|
|
795
|
+
nothing is truncated to a single line.
|
|
796
|
+
- `--no-hints` on a 20-row terminal in plan mode - the status bar still says
|
|
797
|
+
`PLAN` with no chip on screen.
|
|
798
|
+
- Dismiss a plan card with `Esc`, let the agent resubmit, then press `Esc` twice:
|
|
799
|
+
the run aborts and the session returns to an idle prompt. Do this one BY HAND
|
|
800
|
+
as well as in a test - the point of the check is whether the exit is findable,
|
|
801
|
+
which no assertion can tell you.
|
|
555
802
|
|
|
556
803
|
## License
|
|
557
804
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent mode vocabulary (plan-mode §5.1).
|
|
3
|
+
*
|
|
4
|
+
* Pure: no React, no I/O, no `node:*`. Imported by the controller, the tool
|
|
5
|
+
* gate, the prompt builder, the slash command and four UI components, which is
|
|
6
|
+
* exactly why it must stay free of every one of their dependencies.
|
|
7
|
+
*
|
|
8
|
+
* `PLAN_MODE_BLOCKED_TOOLS` is deliberately NOT here. It lives in
|
|
9
|
+
* `tools/index.ts` beside `SKILL_TOOL_FLOOR`, because the invariant worth having
|
|
10
|
+
* is that the two sets PARTITION `HOST_TOOL_NAMES` — and a partition test is
|
|
11
|
+
* only as trustworthy as the distance between the two definitions. This file
|
|
12
|
+
* owns the mode vocabulary; that file owns which tools the modes act on.
|
|
13
|
+
*
|
|
14
|
+
* ASCII ONLY in every string literal below. `agent/` is inside the glyph
|
|
15
|
+
* scanner's scope (`glyphs.test.ts`), so a stray ellipsis here fails the build —
|
|
16
|
+
* which is why `OTHER_OPTION_LABEL` is a bare word and the ellipsis is appended
|
|
17
|
+
* at render time from `glyphs.ellipsis`.
|
|
18
|
+
*/
|
|
19
|
+
export type AgentMode = 'build' | 'plan';
|
|
20
|
+
export declare const AGENT_MODES: readonly AgentMode[];
|
|
21
|
+
/** The user-visible word for each mode. English, per the repo convention. */
|
|
22
|
+
export declare const MODE_LABEL: Record<AgentMode, string>;
|
|
23
|
+
/**
|
|
24
|
+
* The next mode in the cycle.
|
|
25
|
+
*
|
|
26
|
+
* Written as a cycle rather than a boolean flip so that adding Claude Code's
|
|
27
|
+
* "auto-accept edits" as a third posture is a one-line change to `AGENT_MODES`
|
|
28
|
+
* and nothing else. Two ship today (§1 non-goals).
|
|
29
|
+
*/
|
|
30
|
+
export declare function nextMode(current: AgentMode): AgentMode;
|
|
31
|
+
/** The two tools that only exist because a human is attached. */
|
|
32
|
+
export declare const PLAN_TOOL_NAMES: readonly ["ask_user", "submit_plan"];
|
|
33
|
+
/**
|
|
34
|
+
* The synthetic free-text option appended to every question.
|
|
35
|
+
*
|
|
36
|
+
* Plain `Other`, with no ellipsis: this module is inside the glyph scanner's
|
|
37
|
+
* scope, and the overlay appends `glyphs.ellipsis` itself so the label degrades
|
|
38
|
+
* to `Other...` on a terminal without Unicode.
|
|
39
|
+
*/
|
|
40
|
+
export declare const OTHER_OPTION_LABEL = "Other";
|
|
41
|
+
/**
|
|
42
|
+
* Why a mutating tool was refused in plan mode.
|
|
43
|
+
*
|
|
44
|
+
* The wording carries weight beyond politeness (§3.9). A run that flips
|
|
45
|
+
* `build -> plan` mid-flight keeps its original system prompt to the end — the
|
|
46
|
+
* loop reads `ctx.systemPrompt`, which was copied by value when the run
|
|
47
|
+
* started — so for the rest of that run this refusal is the ONLY place the
|
|
48
|
+
* model learns that plan mode is on. A bare "not permitted" reads as a tool
|
|
49
|
+
* malfunction and gets retried.
|
|
50
|
+
*/
|
|
51
|
+
export declare function planRefusal(tool: string): string;
|
|
52
|
+
/** Why `submit_plan` was refused outside plan mode. */
|
|
53
|
+
export declare function submitPlanRefusal(): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent mode vocabulary (plan-mode §5.1).
|
|
3
|
+
*
|
|
4
|
+
* Pure: no React, no I/O, no `node:*`. Imported by the controller, the tool
|
|
5
|
+
* gate, the prompt builder, the slash command and four UI components, which is
|
|
6
|
+
* exactly why it must stay free of every one of their dependencies.
|
|
7
|
+
*
|
|
8
|
+
* `PLAN_MODE_BLOCKED_TOOLS` is deliberately NOT here. It lives in
|
|
9
|
+
* `tools/index.ts` beside `SKILL_TOOL_FLOOR`, because the invariant worth having
|
|
10
|
+
* is that the two sets PARTITION `HOST_TOOL_NAMES` — and a partition test is
|
|
11
|
+
* only as trustworthy as the distance between the two definitions. This file
|
|
12
|
+
* owns the mode vocabulary; that file owns which tools the modes act on.
|
|
13
|
+
*
|
|
14
|
+
* ASCII ONLY in every string literal below. `agent/` is inside the glyph
|
|
15
|
+
* scanner's scope (`glyphs.test.ts`), so a stray ellipsis here fails the build —
|
|
16
|
+
* which is why `OTHER_OPTION_LABEL` is a bare word and the ellipsis is appended
|
|
17
|
+
* at render time from `glyphs.ellipsis`.
|
|
18
|
+
*/
|
|
19
|
+
export const AGENT_MODES = ['build', 'plan'];
|
|
20
|
+
/** The user-visible word for each mode. English, per the repo convention. */
|
|
21
|
+
export const MODE_LABEL = { build: 'BUILD', plan: 'PLAN' };
|
|
22
|
+
/**
|
|
23
|
+
* The next mode in the cycle.
|
|
24
|
+
*
|
|
25
|
+
* Written as a cycle rather than a boolean flip so that adding Claude Code's
|
|
26
|
+
* "auto-accept edits" as a third posture is a one-line change to `AGENT_MODES`
|
|
27
|
+
* and nothing else. Two ship today (§1 non-goals).
|
|
28
|
+
*/
|
|
29
|
+
export function nextMode(current) {
|
|
30
|
+
const index = AGENT_MODES.indexOf(current);
|
|
31
|
+
return AGENT_MODES[(index + 1) % AGENT_MODES.length];
|
|
32
|
+
}
|
|
33
|
+
/** The two tools that only exist because a human is attached. */
|
|
34
|
+
export const PLAN_TOOL_NAMES = ['ask_user', 'submit_plan'];
|
|
35
|
+
/**
|
|
36
|
+
* The synthetic free-text option appended to every question.
|
|
37
|
+
*
|
|
38
|
+
* Plain `Other`, with no ellipsis: this module is inside the glyph scanner's
|
|
39
|
+
* scope, and the overlay appends `glyphs.ellipsis` itself so the label degrades
|
|
40
|
+
* to `Other...` on a terminal without Unicode.
|
|
41
|
+
*/
|
|
42
|
+
export const OTHER_OPTION_LABEL = 'Other';
|
|
43
|
+
/** Read-only alternatives named in every refusal, so the model has somewhere to go. */
|
|
44
|
+
const READ_ONLY_ALTERNATIVES = 'read_file / list_dir / glob / grep';
|
|
45
|
+
/**
|
|
46
|
+
* Why a mutating tool was refused in plan mode.
|
|
47
|
+
*
|
|
48
|
+
* The wording carries weight beyond politeness (§3.9). A run that flips
|
|
49
|
+
* `build -> plan` mid-flight keeps its original system prompt to the end — the
|
|
50
|
+
* loop reads `ctx.systemPrompt`, which was copied by value when the run
|
|
51
|
+
* started — so for the rest of that run this refusal is the ONLY place the
|
|
52
|
+
* model learns that plan mode is on. A bare "not permitted" reads as a tool
|
|
53
|
+
* malfunction and gets retried.
|
|
54
|
+
*/
|
|
55
|
+
export function planRefusal(tool) {
|
|
56
|
+
const extra = tool === 'bash'
|
|
57
|
+
? ' bash is refused in full, including read-only commands like "git status":' +
|
|
58
|
+
' a shell string cannot be classified reliably, and a gate that is right' +
|
|
59
|
+
' most of the time is worse than one that is always right.'
|
|
60
|
+
: '';
|
|
61
|
+
return (`Plan mode is active, so ${tool} is refused.${extra}` +
|
|
62
|
+
` Use ${READ_ONLY_ALTERNATIVES} to research, then call submit_plan.` +
|
|
63
|
+
' Do not try to work around this; finish the plan instead.');
|
|
64
|
+
}
|
|
65
|
+
/** Why `submit_plan` was refused outside plan mode. */
|
|
66
|
+
export function submitPlanRefusal() {
|
|
67
|
+
return ('submit_plan is only available in Plan mode. You are in Build mode - ' +
|
|
68
|
+
'just do the work and summarize when you are done.');
|
|
69
|
+
}
|