@aragon-agent/cli 0.5.8 → 0.5.10

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 (156) hide show
  1. package/CHANGELOG.md +1157 -747
  2. package/README.md +1552 -1163
  3. package/dist/agent/coalesce.d.ts +13 -0
  4. package/dist/agent/coalesce.js +50 -11
  5. package/dist/agent/controller.d.ts +141 -2
  6. package/dist/agent/controller.js +396 -4
  7. package/dist/agent/headless.d.ts +8 -0
  8. package/dist/agent/headless.js +40 -0
  9. package/dist/agent/reducer.d.ts +245 -3
  10. package/dist/agent/reducer.js +490 -17
  11. package/dist/agent/retry-view.d.ts +68 -0
  12. package/dist/agent/retry-view.js +95 -0
  13. package/dist/agent/system-prompt.d.ts +11 -0
  14. package/dist/agent/system-prompt.js +2 -0
  15. package/dist/boot/guard.d.ts +135 -0
  16. package/dist/boot/guard.js +218 -0
  17. package/dist/boot/rollback.d.ts +86 -0
  18. package/dist/boot/rollback.js +185 -0
  19. package/dist/cli.js +368 -3
  20. package/dist/commands/builtins.d.ts +1 -1
  21. package/dist/commands/builtins.js +426 -2
  22. package/dist/commands/registry.d.ts +17 -0
  23. package/dist/config/app-paths.d.ts +31 -0
  24. package/dist/config/app-paths.js +37 -0
  25. package/dist/config/cli-commands.d.ts +79 -2
  26. package/dist/config/cli-commands.js +185 -3
  27. package/dist/config/env.d.ts +15 -3
  28. package/dist/config/env.js +143 -3
  29. package/dist/config/load.d.ts +66 -0
  30. package/dist/config/load.js +107 -1
  31. package/dist/config/schema.d.ts +268 -2
  32. package/dist/config/schema.js +288 -1
  33. package/dist/config/store.js +32 -1
  34. package/dist/fast/critique.d.ts +43 -0
  35. package/dist/fast/critique.js +62 -0
  36. package/dist/fast/digest.d.ts +42 -0
  37. package/dist/fast/digest.js +85 -0
  38. package/dist/fast/frames.d.ts +99 -0
  39. package/dist/fast/frames.js +180 -0
  40. package/dist/fast/limits.d.ts +98 -0
  41. package/dist/fast/limits.js +98 -0
  42. package/dist/fast/prompt.d.ts +72 -0
  43. package/dist/fast/prompt.js +94 -0
  44. package/dist/fast/resolve.d.ts +43 -0
  45. package/dist/fast/resolve.js +105 -0
  46. package/dist/fast/review-call.d.ts +99 -0
  47. package/dist/fast/review-call.js +130 -0
  48. package/dist/fast/reviewer.d.ts +248 -0
  49. package/dist/fast/reviewer.js +664 -0
  50. package/dist/fast/types.d.ts +128 -0
  51. package/dist/fast/types.js +14 -0
  52. package/dist/fast/wiring.d.ts +170 -0
  53. package/dist/fast/wiring.js +316 -0
  54. package/dist/launcher.d.ts +14 -0
  55. package/dist/launcher.js +43 -0
  56. package/dist/logging/install.d.ts +15 -1
  57. package/dist/logging/install.js +37 -4
  58. package/dist/logging/logger.d.ts +1 -1
  59. package/dist/session/persist.js +57 -1
  60. package/dist/team/normalize.d.ts +26 -1
  61. package/dist/team/normalize.js +23 -3
  62. package/dist/team/prompt.d.ts +11 -0
  63. package/dist/team/prompt.js +10 -0
  64. package/dist/team/report.d.ts +18 -0
  65. package/dist/team/report.js +27 -1
  66. package/dist/team/runtime.d.ts +13 -2
  67. package/dist/team/runtime.js +33 -6
  68. package/dist/team/subagent.d.ts +19 -1
  69. package/dist/team/subagent.js +72 -10
  70. package/dist/team/task-tool.d.ts +26 -0
  71. package/dist/team/task-tool.js +48 -4
  72. package/dist/team/types.d.ts +80 -1
  73. package/dist/tools/bash-tool.js +19 -3
  74. package/dist/tools/diff.d.ts +25 -5
  75. package/dist/tools/diff.js +72 -41
  76. package/dist/tools/file-change-store.d.ts +34 -0
  77. package/dist/tools/file-change-store.js +57 -0
  78. package/dist/tools/fs-tools.d.ts +26 -0
  79. package/dist/tools/fs-tools.js +62 -5
  80. package/dist/tools/index.d.ts +31 -0
  81. package/dist/tools/index.js +7 -1
  82. package/dist/tools/patch.d.ts +115 -0
  83. package/dist/tools/patch.js +453 -0
  84. package/dist/tools/terminal-output.d.ts +107 -0
  85. package/dist/tools/terminal-output.js +198 -0
  86. package/dist/tools/tool-output-store.d.ts +68 -0
  87. package/dist/tools/tool-output-store.js +102 -0
  88. package/dist/ui/ActivityLine.d.ts +48 -0
  89. package/dist/ui/ActivityLine.js +22 -0
  90. package/dist/ui/App.d.ts +11 -4
  91. package/dist/ui/App.js +359 -37
  92. package/dist/ui/BottomStatusRow.d.ts +65 -0
  93. package/dist/ui/BottomStatusRow.js +17 -0
  94. package/dist/ui/Composer.d.ts +1 -6
  95. package/dist/ui/Composer.js +2 -2
  96. package/dist/ui/PromptInput.d.ts +1 -15
  97. package/dist/ui/PromptInput.js +1 -38
  98. package/dist/ui/StatusBar.d.ts +37 -0
  99. package/dist/ui/StatusBar.js +5 -3
  100. package/dist/ui/TeamPanel.d.ts +5 -4
  101. package/dist/ui/TeamPanel.js +1 -1
  102. package/dist/ui/TodoPanel.d.ts +5 -4
  103. package/dist/ui/Transcript.d.ts +26 -3
  104. package/dist/ui/Transcript.js +76 -10
  105. package/dist/ui/UpdateLine.d.ts +44 -0
  106. package/dist/ui/UpdateLine.js +129 -0
  107. package/dist/ui/activity-phrases.d.ts +33 -0
  108. package/dist/ui/activity-phrases.js +50 -0
  109. package/dist/ui/entries/AssistantEntry.d.ts +20 -0
  110. package/dist/ui/entries/AssistantEntry.js +3 -2
  111. package/dist/ui/entries/DiffView.d.ts +67 -0
  112. package/dist/ui/entries/DiffView.js +103 -0
  113. package/dist/ui/entries/FastCard.d.ts +53 -0
  114. package/dist/ui/entries/FastCard.js +87 -0
  115. package/dist/ui/entries/RetryCard.d.ts +56 -0
  116. package/dist/ui/entries/RetryCard.js +60 -0
  117. package/dist/ui/entries/TeamCard.js +6 -1
  118. package/dist/ui/entries/ToolCard.d.ts +38 -1
  119. package/dist/ui/entries/ToolCard.js +68 -5
  120. package/dist/ui/glyphs.d.ts +9 -0
  121. package/dist/ui/glyphs.js +2 -0
  122. package/dist/ui/layout/AppShell.d.ts +13 -22
  123. package/dist/ui/layout/AppShell.js +3 -38
  124. package/dist/ui/layout/virtual-window.d.ts +1 -1
  125. package/dist/ui/layout/virtual-window.js +109 -5
  126. package/dist/ui/overlays/HelpOverlay.js +16 -2
  127. package/dist/ui/overlays/SettingsScreen.d.ts +82 -1
  128. package/dist/ui/overlays/SettingsScreen.js +147 -1
  129. package/dist/ui/transcript-text.js +27 -0
  130. package/dist/ui/use-wheel-routing.d.ts +16 -32
  131. package/dist/ui/use-wheel-routing.js +21 -38
  132. package/dist/update/classify-failure.d.ts +32 -0
  133. package/dist/update/classify-failure.js +60 -0
  134. package/dist/update/install-lock.d.ts +55 -0
  135. package/dist/update/install-lock.js +184 -0
  136. package/dist/update/install-source.d.ts +97 -0
  137. package/dist/update/install-source.js +218 -0
  138. package/dist/update/installer.d.ts +108 -0
  139. package/dist/update/installer.js +242 -0
  140. package/dist/update/limits.d.ts +111 -0
  141. package/dist/update/limits.js +111 -0
  142. package/dist/update/npm-view.d.ts +62 -0
  143. package/dist/update/npm-view.js +133 -0
  144. package/dist/update/registry.d.ts +52 -0
  145. package/dist/update/registry.js +161 -0
  146. package/dist/update/semver.d.ts +89 -0
  147. package/dist/update/semver.js +295 -0
  148. package/dist/update/service.d.ts +193 -0
  149. package/dist/update/service.js +636 -0
  150. package/dist/update/state.d.ts +103 -0
  151. package/dist/update/state.js +142 -0
  152. package/dist/update/types.d.ts +169 -0
  153. package/dist/update/types.js +68 -0
  154. package/package.json +3 -3
  155. package/dist/ui/layout/regions.d.ts +0 -55
  156. package/dist/ui/layout/regions.js +0 -52
package/README.md CHANGED
@@ -1,1163 +1,1552 @@
1
- # @aragon-agent/cli
2
-
3
- A Claude-Code / Codex-style **interactive terminal UI** for the
4
- [`@aragon-agent/core`](../core) engine. Type a task, watch the agent think and
5
- stream its answer, see each tool call render as a live card, and interrupt /
6
- steer / configure the session — all without leaving the terminal.
7
-
8
- > ⚠️ **Full permission, no sandbox.** This first version runs at **maximum
9
- > permission**: `bash` executes directly, file writes hit the real filesystem,
10
- > and there is **no per-action approval gate by default**. Only run it in
11
- > workspaces you trust. An opt-in confirmation mode is available via `--confirm`.
12
-
13
- ## Install
14
-
15
- ```bash
16
- # Global
17
- npm i -g @aragon-agent/cli
18
- aragon
19
-
20
- # Zero-install
21
- npx @aragon-agent/cli
22
- ```
23
-
24
- > **Release note (R3):** `npx @aragon-agent/cli` resolves `@aragon-agent/core`
25
- > from the npm registry, so `@aragon-agent/core@^0.1.0` must be published first
26
- > (or bundled into the CLI `dist`). During local development inside this
27
- > monorepo, npm workspaces link the local `packages/core` automatically.
28
-
29
- ## Quick start
30
-
31
- ```bash
32
- # Set a key (any one of these)
33
- export ANTHROPIC_API_KEY=sk-ant-...
34
- # ...or paste it live in the TUI via /settings, or pass --api-key
35
-
36
- aragon # interactive TUI
37
- aragon "summarize README.md" # interactive, auto-submitting the prompt
38
- echo "list files" | aragon -p # one-shot, prints to stdout then exits
39
- ```
40
-
41
- ## Usage
42
-
43
- ```
44
- aragon [prompt] Start the interactive TUI (or one-shot with -p / piped stdin)
45
- aragon -p, --print [prompt] Headless: stream the answer to stdout, then exit
46
- aragon config Open the settings screen
47
- aragon config set <key> <value> Non-interactive config write (e.g. model, provider)
48
- aragon config path Print the config file path
49
- aragon models [--provider p] List builtin + discovered models
50
- aragon --version | -v Print the version
51
- aragon --help | -h Print help
52
- ```
53
-
54
- ### Global flags
55
-
56
- | Flag | Meaning |
57
- | --- | --- |
58
- | `--provider <id>` | `anthropic` \| `openai` \| `google` |
59
- | `--model <id>` | Model id |
60
- | `--base-url <url>` | Override the provider base URL (OpenAI-compatible endpoints) |
61
- | `--api-key <key>` | One-shot key override (not persisted) |
62
- | `--thinking <level>` | `off\|minimal\|low\|medium\|high\|xhigh` |
63
- | `--max-tokens <n\|auto>` | Output token cap: a number, or `auto` for the model's own ceiling (default `64000`) |
64
- | `--cwd <dir>` | Working directory for tools |
65
- | `--confirm` | Confirm each mutating tool call |
66
- | `--tool-timeout <ms>` | Per-tool executor ceiling (default 180000) |
67
- | `--idle-timeout <ms>` | Watchdog idle timeout (auto-raised to ≥ tool-timeout + 30s) |
68
- | `--theme <name>` | `auto\|warm\|cool\|light` (`dark` is a compatibility alias for `cool`) |
69
- | `--compact` / `--no-compact` | Transcript density: no blank rows between turns, or the default |
70
- | `--hints` / `--no-hints` | Show or hide the composer hint row |
71
- | `--fullscreen` | Force the full-screen TUI, overriding the automatic downgrades |
72
- | `--no-fullscreen` | Force the inline renderer (the 0.2.0 behavior) |
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 |
75
- | `--team` / `--no-team` | Enable or disable team subagents. `--no-team` does not register the `task` tool at all |
76
- | `--team-max <n>` | Max subagents per dispatch for this run, clamped to `[1, 10]` |
77
- | `--todo` / `--no-todo` | Enable or disable todo planning. `--no-todo` does not register the `todo_write` tool at all |
78
- | `--todo-panel` / `--no-todo-panel` | Show or hide the right-hand todo rail. The tool stays registered either way |
79
- | `--todo-follow <mode>` | What happens when a run ends with steps left: `notify` (default), `auto`, `off` |
80
- | `--no-skills` | Disable the skill system entirely (no catalog, no skill tools) |
81
- | `--skill <name>` | Force-load a skill's full body for this run (repeatable) |
82
- | `--skills-yes` | Approve skill installs for this run (CI / headless) |
83
- | `--transcript-retain <n>` | Entries kept in memory, clamped to `[200, 20000]`. Older ones are dropped and counted |
84
- | `--render-governor` / `--no-render-governor` | Adapt the frame interval under load, or pin it at 33 ms |
85
- | `--max-render-interval <ms>` | Governor ceiling, clamped to `[33, 1000]`. `33` flattens the ladder |
86
- | `--no-color` | Disable ANSI color |
87
- | `--quiet` | (print mode) suppress tool/usage lines on stderr |
88
-
89
- Exit codes: `0` success · `1` agent/runtime error · `2` config/usage error ·
90
- `130` interrupted.
91
-
92
- ## Slash commands (interactive)
93
-
94
- | Command | Action |
95
- | --- | --- |
96
- | `/help` | Keybindings + command help |
97
- | `/model` | Open the model picker |
98
- | `/settings` | Open the settings screen |
99
- | `/thinking <level>` | Set the thinking level |
100
- | `/max-tokens [<n>\|auto\|default]` | Output token cap; with no argument, reports the setting **and** the effective cap for the live model |
101
- | `/tools` | List active tools |
102
- | `/clear` | Clear the visible transcript |
103
- | `/reset` | New conversation |
104
- | `/cwd [dir]` | Show / change the tool working directory |
105
- | `/save [file]` | Save the session to JSON |
106
- | `/resume [file]` | Load a saved session |
107
- | `/plan [on\|off\|status]` | Toggle plan mode - the keyboard-free equivalent of `Shift+Tab` |
108
- | `/team [on\|off\|max <n>]` | Team subagents: status and the live roster, the on/off switch, the fan-out width |
109
- | `/todo [on\|off\|panel on\|off\|follow <mode>\|clear\|continue]` | Todo planning: status, the on/off switch, the rail, follow-through, and picking an unfinished plan back up |
110
- | `/theme <auto\|warm\|cool\|light>` | Switch the color theme live (persisted) |
111
- | `/expand [n]` | Expand / collapse the n-th-from-last (default last) tool card |
112
- | `/copy` | Copy the last answer to the clipboard |
113
- | `/perf` | What the renderer is doing: frame interval, mounted entries, dropped entries, cache occupancy. `/perf reset` clears the caches and the governor |
114
- | `/logs` | Show where this session is being logged, and at what level |
115
- | `/reload` | Re-read `config.json` after editing it in another window (refused mid-run) |
116
- | `/skills [sub]` | List and manage skills (see below) |
117
- | `/<skill-name> [args]` | Run an installed skill directly |
118
- | `/exit` (`/quit`) | Exit |
119
-
120
- A leading `/` meant literally can be escaped as `//`. Typing `/` opens a
121
- command palette and `@` opens a file-path completion popup — `Tab` / `→`
122
- completes, `Up` / `Down` moves the selection, `Esc` closes it.
123
-
124
- ## Keybindings
125
-
126
- | Key | Action |
127
- | --- | --- |
128
- | `Enter` | Submit (idle) / queue a steering message (running) |
129
- | `Alt+Enter` / `Shift+Enter` | Insert a newline |
130
- | `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan` |
131
- | `Esc` | Abort the run / close an overlay / close a popup |
132
- | `Ctrl+C` ×2 | Exit (first press warns) |
133
- | `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
134
- | `Ctrl+T` | Toggle thinking blocks |
135
- | `Ctrl+O` | Expand / collapse the most recent tool card |
136
- | `PgUp` / `PgDn` | Scroll the transcript a page (full-screen mode) |
137
- | `Shift+↑` / `Shift+↓` | Scroll the transcript a line (full-screen mode) |
138
- | Mouse wheel | Scroll the transcript (over the input box: prompt history) |
139
- | `Shift`+wheel | Scroll the transcript a page |
140
- | `Home` / `End`, `Ctrl+A` / `Ctrl+E` | Cursor to line start / end |
141
- | `Alt/Ctrl+←` / `→` | Word-wise cursor jump |
142
- | `Ctrl+W`, `Alt+Backspace` | Delete the previous word |
143
- | `Ctrl+U` / `Ctrl+K` | Kill to line start / end |
144
- | `Up` / `Down` | Move between draft lines; recall prompt history at the edges |
145
- | `?` | Open help (empty input) |
146
-
147
- In **full-screen mode** the transcript is scrolled by the app itself: `PgUp` /
148
- `PgDn` and `Shift+↑` / `Shift+↓`. While pinned to the bottom the viewport follows
149
- new output automatically; once you scroll away the status bar shows `↑N`, a slim
150
- rail on the right edge shows where you are, and a hint counts the lines below
151
- you. Submitting a message always re-pins to the newest output. In **inline mode**
152
- (`--no-fullscreen`) history lives in your terminal's native scrollback, printed
153
- once via Ink `<Static>`.
154
-
155
- **The mouse wheel scrolls whatever is under the pointer.** Over the transcript
156
- it scrolls the transcript three rows per notch (a page with `Shift`); over an
157
- open overlay it scrolls that overlay; over the input box at the bottom it steps
158
- prompt history one entry per notch, exactly as `↑` / `↓` do — including the rule
159
- that an in-progress draft is never overwritten.
160
-
161
- Two things follow from the app owning the mouse, and they are worth knowing
162
- before you meet them:
163
-
164
- - **Selecting text now needs `Shift`+drag** in most terminals. That is the
165
- terminal's convention for "an application is tracking the mouse", not
166
- something this CLI can opt out of while still reading wheel events.
167
- - **`tmux` with `mouse on` keeps the wheel for itself**, so the app never sees
168
- it. `set -g mouse off` hands it back.
169
-
170
- Turn the whole thing off with `--no-mouse`, `ARAGON_MOUSE=0`, or
171
- `aragon config set mouse false`. With mouse support off the wheel simply does
172
- nothing in full-screen mode — it does not fall back to editing your draft, which
173
- is what it used to do. Inline mode is unaffected either way.
174
-
175
- ## Themes
176
-
177
- Three palettes plus `auto`:
178
-
179
- | Name | Look |
180
- | --- | --- |
181
- | `warm` | Terracotta + amber over warm neutrals. The default (`auto` resolves here). |
182
- | `cool` | The blue/violet palette that shipped as `dark` through 0.3.x. |
183
- | `light` | For light terminal backgrounds. |
184
-
185
- `auto` never guesses `light`: terminals cannot report their background reliably,
186
- and guessing wrong makes the app unreadable rather than merely wrong-looking.
187
-
188
- **Migrating from 0.3.x** — `dark` was renamed `cool` when `warm` became the
189
- default, and the old name still works everywhere: `--theme dark`,
190
- `aragon config set theme dark`, `/theme dark`, and a config file containing
191
- `"theme": "dark"` all resolve to `cool`, so a screen you explicitly chose does
192
- not change under you. The value is rewritten as `cool` the next time the config
193
- is saved. Only users who never picked a theme see the new default.
194
-
195
- Density and hints are settable the same three ways: `--compact` / `--no-hints`
196
- for one run, `aragon config set density compact` / `aragon config set hints false`
197
- to persist. The composer hint row also shortens to `? help` on its own after a
198
- few sessions — except while a run is in progress, when it always spells out
199
- `esc abort` in full.
200
-
201
- ## Full-screen mode
202
-
203
- `aragon` takes over the terminal's **alternate screen buffer** — the mechanism
204
- `vim`, `htop`, and `lazygit` use. Two consequences worth knowing:
205
-
206
- - Your previous shell output is **covered, not erased**, and returns untouched
207
- when you exit. Nothing in your scrollback is destroyed.
208
- - The frame is fixed at `rows - 1` tall, which is what keeps the composer and the
209
- status bar at the bottom of the screen even on an empty session.
210
-
211
- On exit the session is replayed into the normal buffer as plain text so the
212
- conversation survives leaving the screen (`--no-exit-transcript` opts out;
213
- `/save` still exports the full JSON).
214
-
215
- It downgrades to the inline renderer automatically when stdout is not a TTY,
216
- `TERM=dumb`, a CI environment variable is set, or the terminal is under 12 rows
217
- or 40 columns. `--fullscreen` overrides all of those except the non-TTY check —
218
- writing screen-control sequences into a pipe or a redirected file is never safe.
219
- `--no-fullscreen`, `ARAGON_FULLSCREEN=0`, or `aragon config set fullscreen false`
220
- opt out permanently.
221
-
222
- The terminal's native scrollback and mouse wheel do not scroll the transcript in
223
- this mode, and mouse tracking is deliberately left off because enabling it costs
224
- text selection and copy in most terminals. If a crash ever strands your terminal
225
- on the alternate screen, `reset` restores it.
226
-
227
- ## Render performance
228
-
229
- A long session used to get slower and slower, and past a point it stopped
230
- responding at all. The cause was structural rather than incidental: Ink has no
231
- output caching, so every frame re-walked the whole mounted tree, re-measured
232
- every text node and re-serialised the frame. The cost was therefore proportional
233
- to **every character the session had ever produced**, not to the characters
234
- actually on screen — and `transcriptWindow` bounded the entry *count*, which is
235
- not the same thing when one entry can be fifty thousand lines.
236
-
237
- Four things changed, and you should not have to think about any of them:
238
-
239
- - **Off-screen entries are not mounted.** The viewport renders the band you can
240
- see plus a couple of entries either side, and replaces the rest with two
241
- spacers of the right height. Scrolling and the `↑N` readout are unchanged.
242
- - **The expensive pure work is cached.** Syntax highlighting and markdown
243
- parsing now run once per distinct block instead of once per frame. Themes are
244
- still applied at render time, so `/theme` takes effect immediately.
245
- - **The transcript is bounded.** A single answer cannot exceed 256 KiB in the
246
- view, and `transcriptRetain` caps how many entries are kept. Both bounds
247
- report what they removed rather than dropping it silently.
248
- - **The frame interval adapts.** If a frame is genuinely expensive, updates
249
- coalesce into a wider window — 33 ms up to a 320 ms ceiling — so the terminal
250
- stays responsive to typing and to `Ctrl+C`. A muted **`eco`** chip appears in
251
- the status bar whenever this is in effect: the stream getting chunkier is
252
- something you should be able to see a reason for.
253
-
254
- `/perf` reports exactly what the renderer is doing:
255
-
256
- ```
257
- render rung 2 - interval 80ms - last commit 61ms - eco
258
- transcript 4213 entries - 1000 retained - 3213 dropped - 38 mounted
259
- heights 412 cached - 6 estimated - cols 132
260
- caches md 178 (1.9MB) - hl 96 (1.4MB) - lines 340
261
- mode fullscreen - viewport 44 rows - offset 0
262
- ```
263
-
264
- `/perf reset` clears the render caches and returns the governor to its fastest
265
- rung. `--no-render-governor` (or `renderGovernor: false`, or
266
- `ARAGON_RENDER_GOVERNOR=0`) turns the adaptation off entirely; the output is
267
- identical, just heavier under load.
268
-
269
- **Inline mode** (`--no-fullscreen`, `TERM=dumb`, CI, a very small terminal) gets
270
- one extra guarantee. Ink repaints the *entire session history* on every frame
271
- once a live entry grows taller than the terminal, which is a genuine freeze
272
- rather than a slowdown. The live region is now clamped to stay below that
273
- threshold, with a `... N earlier lines` marker naming what is deferred; the
274
- entry prints **in full** into your scrollback the moment it finishes.
275
-
276
- ## Plan mode
277
-
278
- `aragon` has two postures, and `Shift+Tab` switches between them. `/plan` does
279
- the same thing for terminals that swallow the key (see below).
280
-
281
- | Mode | What it means |
282
- | --- | --- |
283
- | `BUILD` | The default, and unchanged from earlier versions: you ask, the agent does it. |
284
- | `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. |
285
-
286
- `BUILD` is right for "rename this symbol". `PLAN` is right for "add multi-tenant
287
- support" — anything where you want to see and steer the *approach* before a
288
- single byte changes.
289
-
290
- ### What plan mode actually stops
291
-
292
- In `PLAN`, five tools are refused at the tool boundary: `write_file`,
293
- `edit_file`, `bash`, `skill_install` and `skill_create`. That is a property of
294
- the wiring, not a promise in a prompt — the refusal happens before the tool runs,
295
- so the badge is telling you something that is structurally true.
296
-
297
- **`bash` is refused in full, `git status` included.** This is the most likely
298
- question this feature generates, so: there is no reliable way to classify a shell
299
- string as read-only, and a gate that is right 95 % of the time is worse than one
300
- that is always right, because you stop trusting the badge. Use `read_file`,
301
- `list_dir`, `glob` and `grep`, which stay available; the refusal message names
302
- them so the agent has somewhere to go. There is no allowlist and there will not
303
- be one built out of substring matching.
304
-
305
- Everything else still works: reading, searching, loading skills, and asking you
306
- questions.
307
-
308
- ### The two plan-mode tools
309
-
310
- - **`ask_user`** renders a keyboard-driven wizard: 1–5 questions, 2–4 options
311
- each, exactly one marked `RECOMMENDED`, plus an `Other…` free-text option.
312
- `↑`/`↓` choose, `Enter` confirms and advances, `←` goes back, `Esc` cancels.
313
- Pressing `Enter` through the whole wizard accepts every recommendation, which
314
- is a deliberate "use your judgement" path rather than an accident. A last
315
- screen lets you review before submitting.
316
- - **`submit_plan`** renders the plan as a scrollable card: `a` approves, `r`
317
- opens a one-line feedback field (`Enter` sends it, `←` goes back to the card),
318
- `Esc` dismisses. **Approving flips the session to `BUILD` immediately and the
319
- same run continues straight into implementation** — you do not have to re-ask.
320
-
321
- Cancelling either one is not an error. The agent is told to proceed on stated
322
- assumptions rather than to retry.
323
-
324
- **Dismissing a plan is not how you stop.** `Esc` on a plan card asks for a
325
- *better* plan — the agent is told to refine it and submit again — so a model that
326
- keeps submitting keeps getting cards. To stop the run itself, **press `Esc`
327
- twice**: the first closes the card, the second aborts the run. `Ctrl+C` twice
328
- still exits the session outright.
329
-
330
- ### Where the mode is shown
331
-
332
- The status bar always names a non-default mode (`PLAN`, or `PLAN → BUILD` while
333
- a switch is pending), including on short terminals, with `--no-hints`, and in
334
- inline mode. The composer additionally shows a `PLAN` chip on its hint row and
335
- tints its border, where there is room for it.
336
-
337
- ### Switching mid-run
338
-
339
- Tightening applies at once; loosening waits. Pressing `Shift+Tab` toward `PLAN`
340
- during a run takes effect immediately — you pressed it *because* the agent is
341
- about to do something. Pressing it toward `BUILD` during a run is **deferred to
342
- the end of the run**, so a session you launched under a read-only guarantee
343
- cannot start writing files because of one stray keypress. The status bar shows
344
- `PLAN → BUILD` while that is pending. Approving a plan is the one exception, and
345
- it is an informed act rather than a stray keypress.
346
-
347
- ### If `Shift+Tab` does nothing
348
-
349
- Some multiplexers and remote-desktop stacks never send `CSI Z`. `/plan`,
350
- `/plan on`, `/plan off` and `/plan status` are first-class equivalents, and
351
- `--plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the startup case.
352
-
353
- ### Headless
354
-
355
- `aragon -p --plan "add SSO"` is a useful combination: the read-only gate still
356
- applies, so it means *"tell me how you would do this, and do not touch my
357
- repository"*. No question or plan tool is registered there — there is nobody to
358
- ask — and the agent writes the finished plan as its final message in markdown.
359
-
360
- ## TODO planning
361
-
362
- For work with three or more distinct steps the agent keeps a **visible plan**:
363
- it writes a checklist with the `todo_write` tool, marks exactly one item as in
364
- progress, and ticks it off before starting the next. In full-screen mode that
365
- list is rendered as a right-hand rail, about a fifth of the terminal wide.
366
-
367
- ```
368
- transcript ... | TODO 2/7
369
- | ####------
370
- |
371
- | v 1 Read the reducer
372
- | v 2 Design the store
373
- | > 3 Wiring the rail into
374
- | AppShell
375
- | o 4 Add the panel-rows test
376
- | o 5 Update the README
377
- | +2 more
378
- ```
379
-
380
- Ask for something small and nothing appears. The agent is told, in its system
381
- prompt, **not** to use the tool for anything it can finish in one or two steps,
382
- for a question, or for a search with no work attached — and a one-item list
383
- against an empty plan is refused outright. A session that never plans has
384
- byte-identical layout to a build without this feature.
385
-
386
- **The list is a projection of what the model believes**, so nothing that does
387
- not also tell the model may change it:
388
-
389
- - `/clear` wipes the transcript but not the conversation, so the plan stays.
390
- - `/reset` clears the conversation, so the plan goes with it.
391
- - `/save` writes the list and `/resume` restores it. Resuming a session that has
392
- no list clears the current one, rather than leaving a plan on screen whose
393
- conversation has just been replaced.
394
- - A finished plan is dropped at the START of your next message, so you still get
395
- to see `7/7 done`. An unfinished one survives "continue" / "now do the rest",
396
- and is dropped after three unrelated turns rather than holding a fifth of the
397
- screen for the rest of the session.
398
-
399
- ### When a run ends with steps left
400
-
401
- By default you get a notice and `/todo continue` — nothing auto-continues on
402
- your behalf. `/todo continue` no longer says "continue the remaining items"; it
403
- sends the model the numbered list of steps that are still open, so a plan that
404
- scrolled out of the context window some turns ago is still followed exactly.
405
-
406
- `/todo follow auto` (or `--todo-follow auto`, or `todo.followThrough` in
407
- `config.json`) turns that into an actual loop: the CLI announces
408
- `Continuing with 4 remaining steps in 3s - Esc to stop.`, waits, and then sends
409
- the continuation itself. Two structural limits bound it, and neither is a
410
- setting:
411
-
412
- - A continuation that completes no step buys exactly **one** more attempt.
413
- After that you get a sentence and the keyboard back.
414
- - A single plan is continued at most **25** times however productive it looks.
415
- A re-plan forgives the first counter but never the second, so a model that
416
- reshapes its list every turn cannot lift the ceiling.
417
-
418
- `Esc` during the grace window cancels; so does typing anything. A run that ended
419
- with an **error** never auto-continues in any mode — you get a warning instead.
420
- A run **you** aborted with `Esc` now says nothing at all, where it used to
421
- report what you had just interrupted.
422
-
423
- `/todo follow off` is the third mode: no notice, no continuation, silence.
424
-
425
- Under `-p` there is no grace window (there is nobody there to use it), so the
426
- two counters are the whole of the protection. `[todo] continuing (3 steps left)`
427
- goes to stderr before each continuation and `[todo] 3 steps unfinished` when it
428
- stops; both are suppressed by `--quiet`, and an unfinished plan never changes
429
- the exit code.
430
-
431
- ### Inline mode
432
-
433
- Inline mode has no fixed frame and therefore no rail, but it does get a one-row
434
- plan strip under the transcript:
435
-
436
- ```
437
- todo 3/7 > Adding the rail to AppShell +2 done
438
- ```
439
-
440
- It never wraps and never grows past one row, so the composer does not move. The
441
- `+N done` suffix drops below 80 columns and the counter compacts to `[3/7]`
442
- below 100, matching the status bar. `--no-todo-panel` hides the strip as well as
443
- the rail.
444
-
445
- `/todo` reports status and switches things off: `/todo off` unregisters nothing
446
- (the tool list is fixed when the session starts) but stops the tool working and
447
- drops the guidance from the system prompt, while `/todo panel off` keeps the
448
- planning discipline and hides the column — which is what you want with a screen
449
- reader, and what `--no-todo-panel` does for a whole session. Under `-p` the plan
450
- is written to stderr as `[todo] 3/7 <step>` lines, suppressed by `--quiet`.
451
-
452
- The rail is not mounted below 80 columns, while an overlay is open, in inline
453
- mode, or when the viewport is too short to say anything useful; the transcript
454
- keeps at least 62 columns at every width. In those cases the counter appears in
455
- the status bar instead, and the checklist is still written into the transcript
456
- as one card per turn.
457
-
458
- ## Built-in tools
459
-
460
- `read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `grep`, `bash` — all
461
- at full permission. Paths resolve against the session working directory. The
462
- per-tool timeout and 100 KB output truncation are enforced by the core executor;
463
- `bash`'s `timeout` param only shrinks within that ceiling.
464
-
465
- When skills are enabled, four more are added — the two read tools `skill` and
466
- `skill_find`, then the two mutating ones `skill_install` and `skill_create` (see
467
- below). `--no-skills` removes them again. Updating a skill is deliberately not
468
- among them; that is a `aragon skills update` decision for you, not the model.
469
-
470
- With team mode on — which is the default — one more is added: `task`, the
471
- delegation tool (see below). `--no-team` removes it again.
472
-
473
- With todo planning on — also the default — one more is added: `todo_write`, the
474
- planning tool (see above). `--no-todo` removes it again.
475
-
476
- ## Team subagents
477
-
478
- The agent can hand parts of a job to short-lived **subagents** that run in
479
- parallel, then read one combined report of what they each found. It decides for
480
- itself whether to delegate; a session that never calls `task` is the session
481
- that existed before this feature.
482
-
483
- ```
484
- > read the auth middleware, map the route table, and check the migration
485
-
486
- ◆ team 2 running · 1 done · 84s ✉ 1
487
- ⠋ a1 read the auth middleware tool: grep 22.1s
488
- ⠙ a2 map the route table thinking 31.6s
489
- ✔ a3 check the migration done 4 turns 18.2s
490
- ```
491
-
492
- **What a subagent is.** A full agent with its own message history, the same
493
- tools and the same working directory — but no memory of your conversation, so
494
- its brief has to be self-contained. It cannot delegate further (depth is capped
495
- at one by construction) and it cannot ask you anything.
496
-
497
- **Delegate wide, not deep.** Two to four subagents is the usual size. The
498
- default cap is 5 per dispatch and 3 in flight at once; the hard maximum is 10
499
- and no config file can raise it. `maxConcurrent` is separate from `maxSubagents`
500
- on purpose: five simultaneous streams against one API key is a reliable way to
501
- collect rate-limit errors, and each one would land in the report as a failed
502
- subagent.
503
-
504
- **The report.** One text result: a header with the ok/failed counts, aggregate
505
- tokens and cost, then one section per subagent with its status, timing, the
506
- files it wrote and its own final summary. A failed subagent never fails the
507
- dispatch — partial results are the normal outcome of a fan-out. When two
508
- subagents wrote the same file the header says so, phrased as a warning rather
509
- than a fact: the file list is derived from `write_file` / `edit_file`, so a
510
- write made through `bash` is invisible to it.
511
-
512
- **Talking to each other.** Subagents get two extra tools, `team_send` and
513
- `team_wait`. Messages are delivered by attaching them to the recipient's next
514
- tool result, so nobody has to poll and nobody's work is interrupted. The limits
515
- are the point rather than an afterthought: **6 messages per subagent, one every
516
- 15 seconds, 24 per dispatch**, a broadcast costing two. Every refusal is a plain
517
- result naming the limit, never an error — an error reads as a malfunction and
518
- invites a retry, which is the behaviour the limits exist to prevent.
519
-
520
- **What you see.** While a dispatch runs, the roster above sits between the
521
- transcript and the composer and the status bar carries `agents 3/5` (`[3]` under
522
- 100 columns). Both disappear when it finishes and the transcript keeps a card
523
- with the per-agent results; `Ctrl+O` expands it to their summaries. Under `-p`
524
- the same transitions go to stderr as `[team] a1 ok 22.1s 7 tools` lines, and the
525
- children's tokens are folded into the `[usage]` footer.
526
-
527
- **Interaction with the other subsystems.**
528
-
529
- - **Plan mode reaches one level down.** In PLAN, every subagent is built with
530
- the same read-only gate, so `write_file` / `edit_file` / `bash` are refused
531
- inside them too. Parallel research is plan mode's best use, so `task` itself
532
- is allowed. A single subagent can be pinned read-only with `readOnly: true`
533
- even in a BUILD session — the field can only ever tighten.
534
- - **A skill's `allowed-tools` ceiling reaches one level down too**, so
535
- delegating is not a way around it. Subagents get `skill_find` but not `skill`:
536
- loading a skill mutates a turn-scoped registry the lead owns, and three
537
- subagents doing it at once would narrow what the lead may do next in a way
538
- nothing would report. Always-on skills still reach them.
539
- - **`--confirm`** queues subagent confirmations one at a time, labelled with the
540
- subagent that is asking (`[a2] Write: src/routes.ts`).
541
- - **Logs.** `dispatch_start` and `dispatch_end` are recorded at `info` with the
542
- ok / failed / aborted counts and aggregate usage; phase transitions at
543
- `debug`. A `team_send` subject is recorded, never its body; a subagent's brief
544
- and summary only at `trace`.
545
-
546
- **`Esc`** aborts the whole dispatch — every subagent within a couple of seconds
547
- — and the report comes back marked `ABORTED` with whatever was finished.
548
-
549
- ## Skills
550
-
551
- A skill is a reusable expert procedure stored on disk: a directory containing a
552
- `SKILL.md` (YAML frontmatter + Markdown body) and, optionally, `reference/`,
553
- `scripts/` and `assets/`. The format matches Claude Code's, so an existing
554
- community skill directory works as-is.
555
-
556
- ### Progressive disclosure
557
-
558
- This is the part that makes skills cheap enough to install a lot of:
559
-
560
- | Level | What the model gets | When |
561
- | --- | --- | --- |
562
- | 1 | Name, scope, and a one-line "what + when" | Always, in the system prompt. Whole catalog capped at 6 000 bytes. |
563
- | 2 | The full `SKILL.md` body + a list of bundled files | Only when the model calls `skill(name=…)` |
564
- | 3 | The bundled files themselves | Only when it reads them with `read_file` / `bash` |
565
-
566
- A skill costs a line in the prompt rather than its whole body, so installing
567
- many of them stays cheap. The model decides on its own whether a task matches a
568
- skill — you do not have to name one.
569
-
570
- At Level 2 the file list is split into what to read and what to run, and carries
571
- the skill's absolute root so the model can build real paths. The accompanying
572
- guidance is **platform-specific**: on Windows the `bash` tool runs `cmd.exe`, so
573
- the model is told to run `.py` with `python <abs path>` and that a `.sh` file
574
- will not work — rather than the previous, flatly untrue "run scripts with bash".
575
- `aragon skills doctor` warns when a skill bundles only scripts for the other
576
- shell.
577
-
578
- **How many fit in Level 1 depends on your descriptions, and on the language they
579
- are written in.** The 6 000-byte ceiling is measured in UTF-8 bytes: a terse
580
- English description costs roughly 180 bytes a line (~30 skills), while a Chinese
581
- one at the 220-character cap can cost 660 (~10 skills). Past that point the
582
- catalog truncates.
583
-
584
- Truncation is not a dead end. Entries are ranked before it happens, and what
585
- falls off is still reachable:
586
-
587
- - **Ranking**: scope first (`env` > `project` > `user` > `bundled`), then how
588
- recently and how often you have actually used each skill. A skill you ran an
589
- hour ago outranks one you have never used, so what survives truncation is what
590
- you actually work with — not whatever sorts first alphabetically.
591
- - **`skill_find`**: when the catalog *does* truncate, it says so and names this
592
- tool, which searches everything installed by name, description and keyword.
593
- It only ever searches **what is already on this machine** — it never reaches
594
- the network and never suggests something to install.
595
-
596
- Both are off the critical path: with no usage data recorded the order is exactly
597
- the plain scope-then-name order, and `skill_find` is only advertised in the
598
- prompt when something was actually omitted.
599
-
600
- ### Using them
601
-
602
- ```bash
603
- aragon skills list [--sort=recent] # what is installed
604
- aragon skills info <name> # version, path, provenance, files
605
- aragon skills install <source> [--yes] # dir | git repo | https .md / .zip
606
- aragon skills install github:owner/repo#v1.2.0
607
- aragon skills update <name> [--yes] [--force] [--dry-run]
608
- aragon skills update --all [--yes] # every skill that has an upstream
609
- aragon skills update --check [<name>|--all] # is there a newer version?
610
- aragon skills remove <name> [--yes]
611
- aragon skills create <name> --description "what + when" [--yes]
612
- aragon skills path # user skills directory
613
- aragon skills doctor # validate + detect tampering
614
- aragon skills usage [--json] # local use counters
615
- aragon skills usage --reset --yes # delete them
616
- aragon skills trust <dir> # allow a project skills directory
617
- ```
618
-
619
- #### Updating
620
-
621
- `update` re-fetches a skill from the source recorded in its install manifest,
622
- keeping `installedAt` and recording `updatedAt` + `previousVersion`.
623
-
624
- - **Local edits are protected.** If you have changed an installed skill, `update`
625
- refuses and names the files, *before downloading anything*. `--force`
626
- overwrites them — and **`--force` discards your edits**; copy them out first.
627
- The replaced directory is parked under the staging trash, so a mistake is
628
- recoverable by hand.
629
- - **The host allowlist is re-checked** against your *current*
630
- `skills.allowedHosts`, not the one in force when you installed. Narrowing that
631
- list therefore also constrains updates.
632
- - **`--all` never stops on one failure.** Skills that cannot be updated
633
- (hand-authored, bundled, or locally modified) are reported as `skipped`, which
634
- is not an error; only a genuine I/O or validation failure exits non-zero.
635
- - **`--check` reports `unknown` freely.** It compares the commit recorded at
636
- install time against `git ls-remote`, so it can only answer for git sources
637
- installed by a version that records one. Anything else is honestly reported as
638
- unknown rather than guessed.
639
- - **The model cannot call `update`.** Installing is something you pointed it at;
640
- swapping working instructions for different ones is a maintenance decision and
641
- stays with you.
642
-
643
- Interactively the same lives under `/skills …` (`/skills update <name> [--force]`,
644
- `/skills update --all`, `/skills list --sort=recent`, `/skills policy [mode]`,
645
- `/skills unload`, `/skills usage [--reset]`). Every usable skill also becomes a
646
- slash command:
647
-
648
- ```
649
- /pdf-forms invoice.pdf --flatten
650
- ```
651
-
652
- Both delivery paths share `skills.bodyMaxBytes`, so an oversized `SKILL.md` is
653
- truncated with a visible marker rather than submitted whole. Loading the same
654
- skill twice in one conversation returns a short digest (description, file list,
655
- guidance) instead of a second copy of the body; the model can ask for the full
656
- text back with `skill(name="…", force=true)`.
657
-
658
- Bodies may use `$ARGUMENTS` and `$1..$9`; `$$` is a literal `$`. If a skill
659
- declares no placeholder, the arguments are appended under an `## Arguments`
660
- heading instead. Where a skill's name collides with a built-in command, the
661
- **built-in wins** and the skill is reachable as `/skill:<name>`.
662
-
663
- ### Where skills come from
664
-
665
- Four scopes, later ones overriding earlier ones by name:
666
-
667
- | Scope | Location | Writable |
668
- | --- | --- | --- |
669
- | `bundled` | `<package>/skills/` | no |
670
- | `user` | `aragon skills path` | yes — the default install target |
671
- | `project` | `<cwd>/.aragon/skills/` | yes |
672
- | `project` | `<cwd>/.claude/skills/` | no — read-only Claude Code interop |
673
- | `env` | `ARAGON_SKILLS_PATH` (`:`/`;`-separated) | no |
674
-
675
- A shadowed skill is not lost: `/skills` shows it as `~shadowed by <scope>`.
676
-
677
- ### Frontmatter
678
-
679
- ```yaml
680
- ---
681
- name: pdf-forms # required, kebab-case, ≤ 64 chars
682
- description: Fill, flatten and validate AcroForm PDFs. Use when the task
683
- mentions PDF forms, field filling, or flattening a fillable PDF.
684
- version: 1.2.0
685
- license: MIT
686
- author: Jane Doe
687
- homepage: https://github.com/jane/pdf-forms
688
- keywords: [pdf, forms]
689
- allowed-tools: [read_file, bash] # enforced as a per-turn tool ceiling (union)
690
- activation: auto # auto | always | manual
691
- ---
692
- ```
693
-
694
- `description` is the only text a model sees before deciding to load the skill,
695
- so it has to say both what the skill does and when to use it. Unknown keys are
696
- preserved, so a newer Claude Code field will not break parsing.
697
-
698
- `activation: always` injects the body into every session's system prompt (12 000
699
- bytes total across all such skills); `manual` keeps it out of the catalog so it
700
- runs only when explicitly invoked.
701
-
702
- ### Security model — and its limits
703
-
704
- Be clear about what this does and does not buy you:
705
-
706
- - **Installing requires a human.** The approval gate checks whether a person is
707
- actually reachable *before* asking, and refuses when nobody is. Under `-p`
708
- there is no TUI and therefore no approver, so `skill_install` fails with an
709
- actionable message instead of writing to disk unattended. Opt out
710
- deliberately with `--skills-yes` or `skills.requireApproval=false`.
711
- - **Project directories need trusting once.** Cloning an unfamiliar repository
712
- does not grant it the right to inject instructions into your session.
713
- - **Nothing is executed at install time.** No postinstall, no dependency
714
- install; the executable bit is not preserved. Scripts run only when the model
715
- invokes `bash`.
716
- - **Archives are gated on real bytes** — total size, per-entry size, compression
717
- ratio and entry count, all abortable mid-extraction. Path traversal,
718
- absolute paths, symlinks and Windows device names are refused.
719
- - **Git is never run through a shell**, and ref / owner / repo fragments are
720
- whitelisted and refused if they start with `-`.
721
- - **Third-party text cannot break out of its prompt block**: angle brackets in
722
- descriptions, bodies and file names are neutralized before rendering.
723
-
724
- - **A changed `SKILL.md` is noticed at load time.** Every install records a
725
- sha256; discovery re-checks the one file that reaches the model's context and
726
- flags it as `!modified` if it no longer matches what you approved. See
727
- `skills.integrity` below.
728
- - **Concurrent installs cannot corrupt a skill.** Writes take a per-directory
729
- advisory lock, so two `aragon` processes installing at once queue instead of
730
- interleaving their atomic-replace steps. *Limit:* the lock relies on exclusive
731
- file creation, which is not reliably atomic on every NFS implementation. It
732
- self-expires after 60 seconds and waits at most 5, so the worst case there is
733
- no mutual exclusion — never a hang.
734
-
735
- What it does **not** do: there is **no sandbox**. Approval blocks *silent*
736
- installation; it does not make a skill you approved safe. A skill you accept can
737
- contain adversarial instructions, and its scripts run with your full user
738
- permissions — the same trust model as pasting text into the prompt and the same
739
- as the `bash` tool. `/skills info` records provenance and a sha256 per file so
740
- you can audit what arrived and notice if it changed. The integrity check answers
741
- "is this still the file I approved?", **not** "is this file from someone I
742
- trust" — there is no signing and no key distribution.
743
-
744
- `allowed-tools` **is** enforced now (see below), but read the next section before
745
- treating it as a permission boundary: it is blast-radius control, not a security
746
- boundary, and a skill can widen its own ceiling by loading another skill.
747
-
748
- ### The tool ceiling (`allowed-tools`)
749
-
750
- A skill that declares `allowed-tools` constrains which tools the model may use
751
- **for the rest of the current turn**:
752
-
753
- ```yaml
754
- allowed-tools: [read_file, write_file]
755
- ```
756
-
757
- - **Scope is one turn.** The ceiling appears when a skill is loaded — by the
758
- model calling `skill(...)`, or by you running `/<skill-name>` — and it is gone
759
- as soon as you send the next message. A skill you used this morning does not
760
- restrict unrelated work this afternoon.
761
- - **Read-only tools are always allowed**, declared or not: `read_file`,
762
- `list_dir`, `glob`, `grep`, plus `skill` and `skill_find`. What the declaration
763
- actually controls is the five tools that change something — `write_file`,
764
- `edit_file`, `bash`, `skill_install`, `skill_create`.
765
- - **Claude Code names work.** `[Read, Bash, MultiEdit]` maps onto this host's
766
- `read_file` / `bash` / `edit_file`. Tools that exist there but not here
767
- (`WebFetch`, `Task`, `TodoWrite`, …) are ignored rather than treated as errors.
768
- - **A name we cannot resolve waives the whole declaration**, and says so. One
769
- typo means that skill imposes no ceiling at all — never a narrower one you did
770
- not write. `aragon skills doctor` reports these up front.
771
- - **`activation: always` skills never impose a ceiling.** They are ambient
772
- reference material, not a procedure you chose to run.
773
-
774
- Three modes, via `skills.toolPolicy` (default `enforce`),
775
- `aragon --skill-tool-policy <mode>` for one run, or `/skills policy <mode>` for
776
- the current session (which takes effect immediately, even mid-turn):
777
-
778
- | Mode | Behaviour |
779
- | --- | --- |
780
- | `enforce` | The call is refused; the model is told what it may use instead. |
781
- | `warn` | The call runs, and you get one notice per tool per turn. |
782
- | `off` | No ceiling, and nothing about `allowed-tools` reaches the model. |
783
-
784
- `/skills unload` drops the ceiling for the current turn. It does **not** remove
785
- skill text already in the conversation — nothing can.
786
-
787
- **What this is and is not.** It is **blast-radius control, not a security
788
- boundary**. The declaration is written by the skill's own author, so a malicious
789
- skill can simply declare `[bash]`. Its value is that a declaration is now
790
- binding: you can read one in `/skills info` and decide whether to install, and an
791
- honest author's `[read_file]` means a "while you're there, run `rm -rf`" line
792
- smuggled into that skill's text — or into a file it tells the model to read —
793
- cannot execute.
794
-
795
- **And a known limit:** the ceiling is a **union** across every skill loaded this
796
- turn, and `skill` is always available. So a skill can widen its own ceiling by
797
- instructing the model to load a second, more permissive skill. This is not
798
- blocked — blocking it would either strand the model inside a skill it cannot
799
- escape, or break every multi-skill workflow — but every widening is printed to
800
- the transcript:
801
-
802
- ```
803
- Tool ceiling widened by skill "skill-creator": +write_file, +skill_create
804
- ```
805
-
806
- So it stops the smuggled instruction; it does not stop a skill that deliberately
807
- teaches the model to reach for another one.
808
-
809
- ### Usage counters
810
-
811
- To rank the catalog, the CLI keeps a small file next to your skills directory:
812
-
813
- ```jsonc
814
- // <aragon skills path>/../skill-usage.json
815
- { "schema": 1, "skills": { "deploy-preview": { "useCount": 12, "lastUsedAt": 1785050000000 } } }
816
- ```
817
-
818
- **It contains the skill name, a count, and a timestamp. Nothing else** — no
819
- arguments, no conversation content, no working directory, no machine identifier.
820
- It is never transmitted anywhere, by anything. Delete it whenever you like; the
821
- only effect is that the catalog falls back to plain scope-then-name order. Set
822
- `skills.usageTracking=false` to switch it off entirely, after which the file is
823
- neither read nor written.
824
-
825
- Automatically injected `activation: always` skills are deliberately **not**
826
- counted — they load on every turn, so counting them would say nothing about what
827
- you actually use.
828
-
829
- ## Configuration
830
-
831
- Config is layered (highest priority last): **defaults → user config file →
832
- env / `.env` → CLI flags**.
833
-
834
- - **Env / `.env`**: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
835
- `GOOGLE_API_KEY` / `GEMINI_API_KEY`, plus `ARAGON_PROVIDER`, `ARAGON_MODEL`,
836
- `ARAGON_BASE_URL`, `ARAGON_THINKING`,
837
- `ARAGON_MAX_TOKENS` (a number, or `auto` / `0` for the per-model ceiling),
838
- `ARAGON_THEME`,
839
- `ARAGON_FULLSCREEN`, `ARAGON_PLAN` (`1` starts in plan mode),
840
- `ARAGON_TEAM` (`0` disables team subagents), `ARAGON_TEAM_MAX` (fan-out width),
841
- `ARAGON_TODO` (`0` disables todo planning entirely),
842
- `ARAGON_TODO_FOLLOW` (`notify` / `auto` / `off`),
843
- `ARAGON_SKILLS` (`0` disables), `ARAGON_SKILLS_PATH`,
844
- `ARAGON_SKILLS_DISABLED` (comma-separated names), `ARAGON_LOG_LEVEL`,
845
- `ARAGON_LOG_FILE` (`0` disables), `ARAGON_LOG_DIR`,
846
- `ARAGON_TRANSCRIPT_RETAIN`, `ARAGON_RENDER_GOVERNOR` (`0` disables),
847
- `ARAGON_MAX_RENDER_INTERVAL_MS`.
848
- `ARAGON_HOME` is the exception that **cannot** come from `.env`: it is read
849
- before any `.env` is loaded. See [Files & logs](#files--logs).
850
- - **User config file** (`aragon config path`): JSON, written `0600` on POSIX.
851
- Holds provider / model / keys and your preferences, and nothing else — the
852
- prompts you type and the counters behind one-time notices live in their own
853
- files (see [Files & logs](#files--logs)). Keys are masked in the UI and never
854
- logged. It lives in your home directory — see [Files & logs](#files--logs) —
855
- and editing it by hand is supported: `aragon config edit` opens it, and an
856
- invalid edit is reported rather than silently resetting every setting.
857
-
858
- | Config key | Default | Meaning |
859
- | --- | --- | --- |
860
- | `maxTokens` | `64000` | Output token cap, clamped to `[256, 200000]`. `null` means **auto** — use each model's own ceiling, never above 64000. Absent means the default. See [Output token limits](#output-token-limits). |
861
- | `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. |
862
- | `exitTranscript` | `true` | Replay a plain-text session summary after exiting (full-screen only). |
863
- | `transcriptWindow` | `1000` | How far back you can scroll, clamped to `[50, 20000]`. Entries beyond it collapse into one line. It is no longer a rendering budget: the viewport is virtualised, so off-screen entries are not laid out at all. |
864
- | `transcriptRetain` | `1000` | Entries kept in memory, clamped to `[200, 20000]` and raised to `transcriptWindow` if the two conflict — with a startup notice saying so, because retaining less than you can scroll to would otherwise make part of the horizon quietly unreachable. Older entries are dropped, counted, and reported by `/perf` and by the exit replay. |
865
- | `renderGovernor` | `true` | Widen the streaming frame interval when a frame is genuinely expensive. See [Render performance](#render-performance). |
866
- | `maxRenderIntervalMs` | `320` | Ceiling of the governor ladder, clamped to `[33, 1000]`. `33` flattens it to the pre-0.6 cadence without disabling the mechanism. |
867
- | `planModeDefault` | `false` | Start every session in plan mode. `--no-plan` still overrides it for a single run. |
868
- | `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. |
869
- | `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. |
870
- | `historyEnabled` | `true` | Record submitted prompts to `prompt-history.jsonl` for `↑` recall. Setting it to `false` stops new writes; it does not delete what is already stored (that is `aragon history clear --yes`) and does not stop reading. See [Prompt history](#prompt-history). |
871
- | `team.enabled` | `true` | Master switch for team subagents. `false` means the `task` tool is never registered — `/team on` in such a session saves the setting for next launch and says so, rather than advertising a tool that is not there. |
872
- | `team.maxSubagents` | `5` | Subagents per dispatch, clamped to `[1, 10]`. The 10 is the hard ceiling and is enforced twice — on the config value and again on what the model asks for — so hand-editing this file cannot raise it. |
873
- | `team.maxConcurrent` | `3` | Subagents in flight at once, clamped to `[1, 10]` and capped at `maxSubagents`. A separate knob because provider rate limits are a different constraint from context economics. |
874
- | `team.subagentTimeoutMs` | `300000` | One subagent's wall clock before it is aborted, clamped to `[30000, 1800000]`. A wedged subagent dies on its own ceiling without taking the dispatch with it. |
875
- | `team.dispatchTimeoutMs` | `900000` | The whole dispatch's wall clock, clamped to `[60000, 3600000]`. |
876
- | `team.maxTurnsPerSubagent` | `24` | Runaway-loop cap, clamped to `[4, 100]`. A subagent stopped here is reported as `stopped: turn cap reached`, with whatever it had produced. |
877
- | `todo.enabled` | `true` | Master switch for todo planning. `false` means the `todo_write` tool is never registered — `/todo on` in such a session saves the setting for next launch and says so, rather than advertising a tool that is not there. |
878
- | `todo.panel` | `true` | Render the right-hand rail (and, in inline mode, the one-row plan strip). Independent of `todo.enabled` on purpose: a screen-reader user wants the planning discipline without the column, and the system prompt varies one sentence accordingly. |
879
- | `todo.followThrough` | `"notify"` | What happens when a run ends with steps left. `notify` says so and stops; `auto` continues the plan after a 3-second grace window, bounded by two structural limits (one fruitless attempt, 25 continuations per plan) that are deliberately **not** settings; `off` is silent. An unrecognized value falls back to `notify`. |
880
- | `skills.enabled` | `true` | Master switch for the skill system. |
881
- | `skills.requireApproval` | `true` | Require a human to approve `skill_install` / `skill_create`. |
882
- | `skills.catalogMaxBytes` | `6000` | Level 1 catalog ceiling, in UTF-8 **bytes**, clamped to `[500, 40000]`. |
883
- | `skills.bodyMaxBytes` | `30000` | Level 2 body ceiling, in UTF-8 **bytes**, clamped to `[1000, 50000]`. |
884
- | `skills.allowedHosts` | GitHub / GitLab hosts | Hosts an install **or update** may fetch from. Re-checked on every update, not just at install time. |
885
- | `skills.disabled` | `[]` | Skill names switched off. |
886
- | `skills.trustedProjectDirs` | `[]` | Project skill directories you have trusted. |
887
- | `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. |
888
- | `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`. |
889
- | `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. |
890
- | `log.level` | `info` | `silent` · `error` · `warn` · `info` · `debug` · `trace`. See [Files & logs](#files--logs) for what each level records. |
891
- | `log.toFile` | `true` | Write records to a file at all. `false` here is respected; `--log-file` overrides it for one run. |
892
- | `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. |
893
- | `log.maxFileBytes` | `5242880` | Rotate once a file would exceed this, clamped to `[65536, 268435456]`. |
894
- | `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. |
895
- | `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. |
896
- | `log.previewChars` | `512` | How much user content `debug` keeps per field, clamped to `[0, 8192]`. `trace` is never truncated. |
897
-
898
- Byte, not character: `ToolExecutor` truncates at 100 000 **bytes**, so a
899
- character-based budget would be roughly triple its nominal size for CJK text
900
- and would get cut mid-tag.
901
-
902
- ### Output token limits
903
-
904
- Every model and every provider defaults to **64000** output tokens. You never
905
- have to configure this, and if you never touch it the number stays 64000.
906
-
907
- The number you set is an **ambition, not a promise**: it is clamped down to
908
- whatever the target model actually accepts. `gpt-4o` caps at 16384 and
909
- `gemini-1.5-pro` at 8192, and sending 64000 to either is an HTTP 400 that ends
910
- the turn — so `aragon` sends what the model will take, silently. When the model
911
- is one it has never seen (a proxy, an endpoint behind `--base-url`, a model that
912
- shipped last week), it sends 64000, and if the provider objects it reads the real
913
- ceiling out of the provider's own error, repairs the request, sends it again, and
914
- remembers the answer for the rest of the session. You see one warning line and
915
- the run continues.
916
-
917
- Four ways to change it, highest priority first:
918
-
919
- ```bash
920
- aragon --max-tokens 32000 # this run only
921
- aragon --max-tokens auto # this run only, per-model ceiling
922
- ARAGON_MAX_TOKENS=32000 aragon # env layer, between the flag and the file
923
- aragon config set maxTokens 32000 # persisted; prints "Set maxTokens = 32000"
924
- aragon config set maxTokens auto # persisted as null
925
- aragon config set maxTokens default # back to 64000
926
- aragon config get maxTokens # "64000" | "auto"
927
- ```
928
-
929
- Inside the TUI, `/max-tokens` reports the current setting **and** the effective
930
- cap for the live model, and `/max-tokens <n|auto|default>` changes it. The
931
- settings screen (`/settings`) has the same field with an `Effective:` line under
932
- it, so a value the model will not honour is visible before you save it.
933
-
934
- And by hand, in `~/.aragon-agent/config.json` (`aragon config path` prints the
935
- exact location; `aragon config edit` opens it, and `/reload` picks up an edit
936
- made in another window):
937
-
938
- ```jsonc
939
- {
940
- "version": 1,
941
- "provider": "anthropic",
942
- "model": "claude-sonnet-4-5-20250929",
943
-
944
- // Output token cap.
945
- // 64000 (or any 256..200000 integer) — an explicit cap, clamped down to
946
- // the target model's real ceiling when that ceiling is lower.
947
- // null — auto: use each model's own ceiling, never above 64000.
948
- // absent — the product default, 64000.
949
- "maxTokens": 64000
950
- }
951
- ```
952
-
953
- Out-of-range numbers are clamped on the way in **and** on the way out, so a
954
- hand-edited `900000` becomes `200000` on disk rather than reverting to the
955
- default on every launch.
956
-
957
- Extended thinking is reconciled with the cap automatically: `--thinking xhigh`
958
- asks for a 65536-token budget, Anthropic requires `max_tokens` to be strictly
959
- greater than it, and the two are resolved together rather than left to collide.
960
-
961
- These are additive: a config file written by an earlier version loads
962
- unchanged, with no migration and no version bump.
963
-
964
- ## Files & logs
965
-
966
- Everything AragonAgent keeps for you lives under one directory in your home:
967
-
968
- ```
969
- ~/.aragon-agent/ (Windows: C:\Users\<you>\.aragon-agent\)
970
- ├── config.json settings + API keys (0600 on POSIX)
971
- ├── config.json.bak written by `aragon config edit` before it opens
972
- ├── prompt-history.jsonl prompts you submitted, for ↑ recall (0600 on POSIX)
973
- ├── state.json UI bookkeeping: submit count, one-shot notices
974
- ├── logs/
975
- │ ├── aragon-2026-07-27.log today, JSON Lines
976
- │ └── aragon-2026-07-27.1.log rotated at log.maxFileBytes
977
- ├── sessions/ /save and /resume
978
- ├── skills/ aragon skills install
979
- └── skill-usage.json
980
- ```
981
-
982
- `config.json` holds **settings only**. What you typed and how often you have
983
- typed it live in the two files next to it, so that editing your config means
984
- scrolling past your own settings and nothing else, and so that submitting a
985
- prompt no longer rewrites the file that holds your API keys.
986
-
987
- `ARAGON_HOME` moves the whole tree. **It cannot come from a `.env` file** — the
988
- path is resolved when the process starts, long before a `.env` is read — so set
989
- it as a real environment variable. `aragon config home` always prints the
990
- directory actually in use.
991
-
992
- Upgrading from 0.5.0 or earlier moves your config, sessions and skills here
993
- automatically, once, and prints a line on stderr when it does. The old
994
- `%APPDATA%\aragon-agent-nodejs` / `%LOCALAPPDATA%\aragon-agent-nodejs`
995
- directories are **left in place** so that reinstalling an older build stays
996
- lossless; `aragon config home` names them, and you can delete them once you are
997
- satisfied.
998
-
999
- ### Prompt history
1000
-
1001
- Every prompt you submit is appended to `~/.aragon-agent/prompt-history.jsonl`,
1002
- one JSON object per line, so that `↑` recalls it — in this session and in every
1003
- later one. It is written `0600` on POSIX, it never leaves the machine, and it is
1004
- **stored verbatim**: a redacted history would recall the wrong text, which is
1005
- worse than not recalling at all.
1006
-
1007
- The last 100 distinct prompts are what `↑` walks. The file is rewritten down to
1008
- that many entries once it passes 400 lines, and a single submission longer than
1009
- 16 KiB is not recorded at all rather than recorded truncated.
1010
-
1011
- ```bash
1012
- aragon history path # where it is, plus a reminder of what is in it
1013
- aragon history list -n 50 # the 50 most recent, newest first
1014
- aragon history list --json # raw entries with timestamps
1015
- aragon history clear --yes # delete the file
1016
- ```
1017
-
1018
- Set `historyEnabled` to `false` (`aragon config set historyEnabled false`) to
1019
- stop recording. Switching it off does **not** delete what is already stored —
1020
- `aragon history clear --yes` does that — and reading keeps working either way,
1021
- so you can look before you erase.
1022
-
1023
- Upgrading from 0.5.x moves an existing `promptHistory` out of `config.json` into
1024
- this file once, automatically and silently.
1025
-
1026
- ### What gets logged
1027
-
1028
- Logs are local files. Nothing is uploaded, ever — there is no network path out
1029
- of this subsystem at all.
1030
-
1031
- | Level | Records | Contains your content? |
1032
- | --- | --- | --- |
1033
- | `error` | crashes, failed requests, config write/parse failures | no |
1034
- | `warn` | degraded migrations, clamped values, dropped records | no |
1035
- | `info` *(default)* | start/exit, config writes, turn boundaries with token counts, tool names and durations | **no** |
1036
- | `debug` | the above, plus prompts, replies and tool arguments truncated to `log.previewChars` | yes, truncated |
1037
- | `trace` | full message bodies and full tool output | yes, in full |
1038
-
1039
- The default is `info` specifically so that attaching a log to a bug report is
1040
- safe. **`debug` and `trace` write what you typed and what the model replied** —
1041
- check before sharing one.
1042
-
1043
- API keys are removed at every level, by two independent passes: any field whose
1044
- name looks like a credential is blanked whatever it holds, and every string is
1045
- scanned for known key formats *and* for the literal keys this process is
1046
- holding. The second half is what covers a custom endpoint whose key looks like
1047
- nothing in particular. Setting `log.redactSecrets false` disables both, and says
1048
- so on stdout when you do.
1049
-
1050
- ### Commands
1051
-
1052
- ```bash
1053
- aragon logs path # where records are going right now, and at what level
1054
- aragon logs list # files, sizes, timestamps
1055
- aragon logs tail -n 50 # render the last 50 records
1056
- aragon logs tail --follow # keep printing new ones
1057
- aragon logs tail --json # raw JSON Lines, for jq
1058
- aragon logs tail --level warn # only warn and above
1059
- aragon logs clear --yes # delete every log file
1060
- aragon logs open # open the directory in your file manager
1061
-
1062
- aragon history path # where prompt history is kept
1063
- aragon history list -n 50 # the 50 most recent prompts, newest first
1064
- aragon history clear --yes # delete the prompt history
1065
-
1066
- aragon config home # the user-state root (and whether ARAGON_HOME set it)
1067
- aragon config get log.level # one value; secrets always masked
1068
- aragon config list --json # everything; secrets always masked here too
1069
- aragon config edit # open config.json in $VISUAL / $EDITOR
1070
- ```
1071
-
1072
- Per-run overrides, none of which are persisted:
1073
-
1074
- ```bash
1075
- aragon --log-level debug "…" # or --verbose, which means the same thing
1076
- aragon --no-log-file "…" # nothing reaches a file this run
1077
- aragon --log-dir D:\logs "…"
1078
- ```
1079
-
1080
- `ARAGON_LOG_LEVEL`, `ARAGON_LOG_FILE=0` and `ARAGON_LOG_DIR` do the same from the
1081
- environment, and unlike `ARAGON_HOME` these three *can* come from a `.env`.
1082
-
1083
- ### Insecure TLS opt-in
1084
-
1085
- When `NODE_TLS_REJECT_UNAUTHORIZED` is the exact string `0`, the installed or
1086
- locally built `aragon` process honors that setting and suppresses only Node's
1087
- standard insecure-TLS warning. Every unrelated process warning remains visible,
1088
- and any other value leaves warning behavior unchanged. The check happens when a
1089
- warning is emitted, so a `0` loaded from the project `.env` is covered too; the
1090
- CLI never creates, normalizes, or persists this variable.
1091
-
1092
- > **Security warning:** `0` genuinely disables certificate verification. It does
1093
- > not make an insecure connection safe and can expose credentials or request
1094
- > contents to interception. Use it only in a controlled environment where you
1095
- > accept that risk.
1096
-
1097
- The automatic filter starts with the `aragon` process. An earlier `npm install`
1098
- or `npx` bootstrap belongs to npm and is outside the installed package's process
1099
- boundary, so that parent may still print Node's warning. The official repository
1100
- release flow covers its npm children through `publish-latest.ps1`; callers that
1101
- own another npm/npx parent must preload the filter in that parent themselves if
1102
- quiet output is required.
1103
-
1104
- ### Theming & color
1105
-
1106
- `--theme` / `config.theme` / `/theme` pick a palette: `warm`, `cool`, `light`, or
1107
- `auto` (which resolves to `warm` — terminals can't reliably report their
1108
- background). `dark` remains accepted everywhere as an alias for `cool`.
1109
- Colors degrade automatically to your terminal's depth (truecolor → 256 → 16 →
1110
- monochrome) and glyphs fall back to ASCII on terminals without Unicode. Color is
1111
- disabled — and the UI renders plain monochrome with ASCII glyphs — when any of
1112
- `NO_COLOR`, `--no-color`, `TERM=dumb`, or `config.color=false` is set; `FORCE_COLOR`
1113
- is respected. `reducedMotion` (also `ARAGON_REDUCED_MOTION`, and implied by
1114
- `--no-color`) replaces spinners with a static glyph.
1115
-
1116
- ### Timeout invariant
1117
-
1118
- The core idle watchdog (60 s by default) is **shorter** than the tool timeout
1119
- (120 s), so a long single tool run (a build, `npm install`) would be killed
1120
- mid-run. The CLI fixes this by always constructing the agent with
1121
- `idleTimeout ≥ toolTimeout` (defaults 210 s ≥ 180 s), re-derived after config
1122
- merge. Raise both `--tool-timeout` / `--idle-timeout` for genuinely long jobs.
1123
-
1124
- ## Development
1125
-
1126
- ```bash
1127
- npm run build -w packages/cli # tsc + shebang → dist/cli.js
1128
- npm test -w packages/cli # vitest
1129
- node packages/cli/dist/cli.js --version
1130
- ```
1131
-
1132
- ### Manual smoke checklist
1133
-
1134
- - `npm run build -w packages/cli` produces an executable `dist/cli.js` with a
1135
- shebang; `node packages/cli/dist/cli.js --version` prints the version.
1136
- - `npm pack -w packages/cli --dry-run --json` lists `dist/cli.js` under `files`.
1137
- - With `ANTHROPIC_API_KEY` set: `aragon` streams a reply, a tool card renders,
1138
- `Esc` aborts mid-run, `/model` switches models, `/settings` saves a key,
1139
- `Ctrl+C` twice exits.
1140
- - `echo "list files" | node dist/cli.js -p` prints an answer and exits `0`.
1141
- - A missing/invalid key produces a visible, actionable message (never a blank
1142
- screen), both in the TUI and headless.
1143
- - `Shift+Tab` at 40, 80 and 120 columns - the chip, the hint and the status bar
1144
- are all legible and nothing wraps onto a second row.
1145
- - The question wizard at 24 rows with 5 questions x 4 options - no clipping, and
1146
- the `n/N` counter matches what is on screen.
1147
- - Approve a plan and watch the SAME run start writing files, with no second
1148
- prompt.
1149
- - `--no-color` on a legacy `cmd.exe` - the chip renders `[PLAN]`, radios render
1150
- `*`/`o`, checkboxes render `[x]`/`[ ]`, no mojibake anywhere.
1151
- - A plan with a 600-character summary and a 400-character step detail - the text
1152
- wraps and scrolls, the footer count matches the real number of rows, and
1153
- nothing is truncated to a single line.
1154
- - `--no-hints` on a 20-row terminal in plan mode - the status bar still says
1155
- `PLAN` with no chip on screen.
1156
- - Dismiss a plan card with `Esc`, let the agent resubmit, then press `Esc` twice:
1157
- the run aborts and the session returns to an idle prompt. Do this one BY HAND
1158
- as well as in a test - the point of the check is whether the exit is findable,
1159
- which no assertion can tell you.
1160
-
1161
- ## License
1162
-
1163
- MIT
1
+ # @aragon-agent/cli
2
+
3
+ A Claude-Code / Codex-style **interactive terminal UI** for the
4
+ [`@aragon-agent/core`](../core) engine. Type a task, watch the agent think and
5
+ stream its answer, see each tool call render as a live card, and interrupt /
6
+ steer / configure the session — all without leaving the terminal.
7
+
8
+ > ⚠️ **Full permission, no sandbox.** This first version runs at **maximum
9
+ > permission**: `bash` executes directly, file writes hit the real filesystem,
10
+ > and there is **no per-action approval gate by default**. Only run it in
11
+ > workspaces you trust. An opt-in confirmation mode is available via `--confirm`.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ # Global
17
+ npm i -g @aragon-agent/cli
18
+ aragon
19
+
20
+ # Zero-install
21
+ npx @aragon-agent/cli
22
+ ```
23
+
24
+ > **Release note (R3):** `npx @aragon-agent/cli` resolves `@aragon-agent/core`
25
+ > from the npm registry, so `@aragon-agent/core@^0.1.0` must be published first
26
+ > (or bundled into the CLI `dist`). During local development inside this
27
+ > monorepo, npm workspaces link the local `packages/core` automatically.
28
+
29
+ ## Quick start
30
+
31
+ ```bash
32
+ # Set a key (any one of these)
33
+ export ANTHROPIC_API_KEY=sk-ant-...
34
+ # ...or paste it live in the TUI via /settings, or pass --api-key
35
+
36
+ aragon # interactive TUI
37
+ aragon "summarize README.md" # interactive, auto-submitting the prompt
38
+ echo "list files" | aragon -p # one-shot, prints to stdout then exits
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ```
44
+ aragon [prompt] Start the interactive TUI (or one-shot with -p / piped stdin)
45
+ aragon -p, --print [prompt] Headless: stream the answer to stdout, then exit
46
+ aragon config Open the settings screen
47
+ aragon config set <key> <value> Non-interactive config write (e.g. model, provider)
48
+ aragon config path Print the config file path
49
+ aragon models [--provider p] List builtin + discovered models
50
+ aragon --version | -v Print the version
51
+ aragon --help | -h Print help
52
+ ```
53
+
54
+ ### Global flags
55
+
56
+ | Flag | Meaning |
57
+ | --- | --- |
58
+ | `--provider <id>` | `anthropic` \| `openai` \| `google` |
59
+ | `--model <id>` | Model id |
60
+ | `--base-url <url>` | Override the provider base URL (OpenAI-compatible endpoints) |
61
+ | `--api-key <key>` | One-shot key override (not persisted) |
62
+ | `--thinking <level>` | `off\|minimal\|low\|medium\|high\|xhigh` |
63
+ | `--max-tokens <n\|auto>` | Output token cap: a number, or `auto` for the model's own ceiling (default `64000`) |
64
+ | `--cwd <dir>` | Working directory for tools |
65
+ | `--confirm` | Confirm each mutating tool call |
66
+ | `--tool-timeout <ms>` | Per-tool executor ceiling (default 180000) |
67
+ | `--idle-timeout <ms>` | Watchdog idle timeout (auto-raised to ≥ tool-timeout + 30s) |
68
+ | `--theme <name>` | `auto\|warm\|cool\|light` (`dark` is a compatibility alias for `cool`) |
69
+ | `--compact` / `--no-compact` | Transcript density: no blank rows between turns, or the default |
70
+ | `--hints` / `--no-hints` | Show or hide the composer hint row |
71
+ | `--show-thinking` / `--no-show-thinking` | Draw the model's reasoning blocks in the transcript. Off by default — see [What you see while it runs](#what-you-see-while-it-runs) |
72
+ | `--live-tool-output` / `--no-live-tool-output` | Show a bounded tail of a running command's output on its card. **On** by default — see [What you see while it runs](#what-you-see-while-it-runs) |
73
+ | `--fullscreen` | Force the full-screen TUI, overriding the automatic downgrades |
74
+ | `--no-fullscreen` | Force the inline renderer (the 0.2.0 behavior) |
75
+ | `--no-exit-transcript` | Do not replay the session summary after exiting |
76
+ | `--plan` / `--no-plan` | Start the session in PLAN mode (read-only research + review) or force BUILD |
77
+ | `--team` / `--no-team` | Enable or disable team subagents. `--no-team` does not register the `task` tool at all |
78
+ | `--team-max <n>` | Max subagents per dispatch for this run, clamped to `[1, 10]` |
79
+ | `--todo` / `--no-todo` | Enable or disable todo planning. `--no-todo` does not register the `todo_write` tool at all |
80
+ | `--todo-panel` / `--no-todo-panel` | Show or hide the right-hand todo rail. The tool stays registered either way |
81
+ | `--todo-follow <mode>` | What happens when a run ends with steps left: `notify` (default), `auto`, `off` |
82
+ | `--retry` / `--no-retry` | Retry a failed provider call with backoff (the default), or fail on the first error |
83
+ | `--retry-max <n>` | Retries after the first attempt for this run, clamped to `[0, 20]`. `0` turns retry off |
84
+ | `--fast` / `--no-fast` | Enable or disable the fast model tier. Off by default; see **Fast model tier** |
85
+ | `--fast-model <id>` | The fast model id. Implies nothing about `--fast` on its own |
86
+ | `--fast-provider <id>` | Provider for the fast model. Absent inherits the main provider |
87
+ | `--fast-review <n\|off>` | Turns between automatic fast reviews, or `off` |
88
+ | `--no-skills` | Disable the skill system entirely (no catalog, no skill tools) |
89
+ | `--skill <name>` | Force-load a skill's full body for this run (repeatable) |
90
+ | `--skills-yes` | Approve skill installs for this run (CI / headless) |
91
+ | `--transcript-retain <n>` | Entries kept in memory, clamped to `[200, 20000]`. Older ones are dropped and counted |
92
+ | `--render-governor` / `--no-render-governor` | Adapt the frame interval under load, or pin it at 33 ms |
93
+ | `--max-render-interval <ms>` | Governor ceiling, clamped to `[33, 1000]`. `33` flattens the ladder |
94
+ | `--no-color` | Disable ANSI color |
95
+ | `--quiet` | (print mode) suppress tool/usage lines on stderr |
96
+
97
+ Exit codes: `0` success · `1` agent/runtime error · `2` config/usage error ·
98
+ `130` interrupted.
99
+
100
+ ## Slash commands (interactive)
101
+
102
+ | Command | Action |
103
+ | --- | --- |
104
+ | `/help` | Keybindings + command help |
105
+ | `/model` | Open the model picker |
106
+ | `/settings` | Open the settings screen |
107
+ | `/thinking <level>` | Set the thinking level |
108
+ | `/max-tokens [<n>\|auto\|default]` | Output token cap; with no argument, reports the setting **and** the effective cap for the live model |
109
+ | `/tools` | List active tools |
110
+ | `/clear` | Clear the visible transcript |
111
+ | `/reset` | New conversation |
112
+ | `/cwd [dir]` | Show / change the tool working directory |
113
+ | `/save [file]` | Save the session to JSON |
114
+ | `/resume [file]` | Load a saved session |
115
+ | `/plan [on\|off\|status]` | Toggle plan mode - the keyboard-free equivalent of `Shift+Tab` |
116
+ | `/team [on\|off\|max <n>]` | Team subagents: status and the live roster, the on/off switch, the fan-out width |
117
+ | `/todo [on\|off\|panel on\|off\|follow <mode>\|clear\|continue]` | Todo planning: status, the on/off switch, the rail, follow-through, and picking an unfinished plan back up |
118
+ | `/retry [show\|on\|off\|max <n>]` | API retry: the effective ladder, the on/off switch, the retry count. Takes effect **in the running session**, children included |
119
+ | `/fast [on\|off\|model <id>\|provider <id>\|same\|review <n\|off>\|delegate on\|off]` | Fast model tier: status and this session's totals, the on/off switch, which model, the review cadence |
120
+ | `/update [status\|now\|skip\|off]` | Auto-update: what is running, what is available, where it came from, when it looks again. `now` forces a check past the throttle; `skip` declines the offered version until a newer one appears |
121
+ | `/theme <auto\|warm\|cool\|light>` | Switch the color theme live (persisted) |
122
+ | `/expand [n]` | Expand / collapse the n-th-from-last (default last) tool card |
123
+ | `/copy` | Copy the last answer to the clipboard |
124
+ | `/perf` | What the renderer is doing: frame interval, mounted entries, dropped entries, cache occupancy. `/perf reset` clears the caches and the governor |
125
+ | `/logs` | Show where this session is being logged, and at what level |
126
+ | `/reload` | Re-read `config.json` after editing it in another window (refused mid-run) |
127
+ | `/skills [sub]` | List and manage skills (see below) |
128
+ | `/<skill-name> [args]` | Run an installed skill directly |
129
+ | `/exit` (`/quit`) | Exit |
130
+
131
+ A leading `/` meant literally can be escaped as `//`. Typing `/` opens a
132
+ command palette and `@` opens a file-path completion popup — `Tab` / `→`
133
+ completes, `Up` / `Down` moves the selection, `Esc` closes it.
134
+
135
+ ## Keybindings
136
+
137
+ | Key | Action |
138
+ | --- | --- |
139
+ | `Enter` | Submit (idle) / queue a steering message (running) |
140
+ | `Alt+Enter` / `Shift+Enter` | Insert a newline |
141
+ | `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan` |
142
+ | `Esc` | Abort the run / close an overlay / close a popup |
143
+ | `Ctrl+C` ×2 | Exit (first press warns) |
144
+ | `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
145
+ | `Ctrl+T` | Show / hide thinking (**off by default**). In inline mode it applies to the live entry and everything drawn after it — settled entries are already in your terminal's scrollback and cannot be repainted |
146
+ | `Ctrl+O` | Expand / collapse the most recent tool card — including its diff |
147
+ | `PgUp` / `PgDn` | Scroll the transcript a page (full-screen mode) |
148
+ | `Shift+↑` / `Shift+↓` | Scroll the transcript a line (full-screen mode) |
149
+ | Mouse wheel | Scroll the transcript (or the open overlay) |
150
+ | `Shift`+wheel | Scroll the transcript a page |
151
+ | `Home` / `End`, `Ctrl+A` / `Ctrl+E` | Cursor to line start / end |
152
+ | `Alt/Ctrl+←` / `→` | Word-wise cursor jump |
153
+ | `Ctrl+W`, `Alt+Backspace` | Delete the previous word |
154
+ | `Ctrl+U` / `Ctrl+K` | Kill to line start / end |
155
+ | `Up` / `Down` | Move between draft lines; recall prompt history at the edges |
156
+ | `?` | Open help (empty input) |
157
+
158
+ In **full-screen mode** the transcript is scrolled by the app itself: `PgUp` /
159
+ `PgDn` and `Shift+↑` / `Shift+↓`. While pinned to the bottom the viewport follows
160
+ new output automatically; once you scroll away the status bar shows `↑N`, a slim
161
+ rail on the right edge shows where you are, and a hint counts the lines below
162
+ you. Submitting a message always re-pins to the newest output. In **inline mode**
163
+ (`--no-fullscreen`) history lives in your terminal's native scrollback, printed
164
+ once via Ink `<Static>`.
165
+
166
+ **The mouse wheel is a viewport gesture and nothing else.** Wherever the pointer
167
+ happens to be — including right on top of the composer — a notch scrolls the
168
+ transcript three rows (a page with `Shift`), or scrolls the overlay when one is
169
+ open. It never touches your draft, and it never recalls prompt history: that is
170
+ `↑` / `↓`, and only `↑` / `↓`.
171
+
172
+ Two things follow from the app owning the mouse, and they are worth knowing
173
+ before you meet them:
174
+
175
+ - **Selecting text now needs `Shift`+drag** in most terminals. That is the
176
+ terminal's convention for "an application is tracking the mouse", not
177
+ something this CLI can opt out of while still reading wheel events.
178
+ - **`tmux` with `mouse on` keeps the wheel for itself**, so the app never sees
179
+ it. `set -g mouse off` hands it back.
180
+
181
+ Turn the whole thing off with `--no-mouse`, `ARAGON_MOUSE=0`, or
182
+ `aragon config set mouse false`. With mouse support off the wheel simply does
183
+ nothing in full-screen mode — it does not fall back to editing your draft, which
184
+ is what it used to do. Inline mode is unaffected either way.
185
+
186
+ ## Themes
187
+
188
+ Three palettes plus `auto`:
189
+
190
+ | Name | Look |
191
+ | --- | --- |
192
+ | `warm` | Terracotta + amber over warm neutrals. The default (`auto` resolves here). |
193
+ | `cool` | The blue/violet palette that shipped as `dark` through 0.3.x. |
194
+ | `light` | For light terminal backgrounds. |
195
+
196
+ `auto` never guesses `light`: terminals cannot report their background reliably,
197
+ and guessing wrong makes the app unreadable rather than merely wrong-looking.
198
+
199
+ **Migrating from 0.3.x** — `dark` was renamed `cool` when `warm` became the
200
+ default, and the old name still works everywhere: `--theme dark`,
201
+ `aragon config set theme dark`, `/theme dark`, and a config file containing
202
+ `"theme": "dark"` all resolve to `cool`, so a screen you explicitly chose does
203
+ not change under you. The value is rewritten as `cool` the next time the config
204
+ is saved. Only users who never picked a theme see the new default.
205
+
206
+ Density and hints are settable the same three ways: `--compact` / `--no-hints`
207
+ for one run, `aragon config set density compact` / `aragon config set hints false`
208
+ to persist. The composer hint row also shortens to `? help` on its own after a
209
+ few sessions — except while a run is in progress, when it always spells out
210
+ `esc abort` in full.
211
+
212
+ ## What you see while it runs
213
+
214
+ Four rules, and each of them exists because the opposite was worse.
215
+
216
+ **Reasoning is not shown.** A model at `thinkingLevel: high` emits more
217
+ chain-of-thought than answer, and it used to scroll the answer and the tool calls
218
+ off the top of the screen. It is written for the model, not for you. So it is off
219
+ by default, and a settled turn that thought leaves one muted row in its place:
220
+
221
+ ```
222
+ ✱ thought for 12s · ctrl+t to show
223
+ ```
224
+
225
+ That row is the whole point — hiding information without saying that it exists
226
+ is how a "clean" UI becomes a dishonest one. `Ctrl+T` shows the bodies for the
227
+ session, `showThinking: true` (or `--show-thinking`, or `ARAGON_SHOW_THINKING=1`)
228
+ shows them permanently, and `/settings` has a **Show thinking** row next to
229
+ **Thinking**.
230
+
231
+ In **inline** mode (`--no-fullscreen`) the marker reads `✱ thought for 12s` with
232
+ no key hint, and the toast on `Ctrl+T` says `Thinking shown for new output.`
233
+ Settled entries there have already been printed into your terminal's own
234
+ scrollback, which cannot be repainted — so the key applies to the live entry and
235
+ to everything drawn after it, and the row does not offer what it cannot deliver.
236
+
237
+ **There is something to look at while it thinks.** One row above the composer,
238
+ for as long as a run is in flight:
239
+
240
+ ```
241
+ ⠋ Percolating…
242
+ ```
243
+
244
+ The word rotates every four seconds. It carries no clock and no token count on
245
+ purpose: the status bar one row below already has both, under the same
246
+ condition, driven by the same ticker. Under `reducedMotion` the spinner is a
247
+ static glyph and the word is chosen once and holds — rotating text is motion too.
248
+ An idle frame is exactly what it was before this existed, down to the row count.
249
+
250
+ While a **tool** is in flight the row stops guessing and names it instead:
251
+
252
+ ```
253
+ ⠙ Running bash
254
+ ```
255
+
256
+ `running` covers the whole turn, including the minutes the model is idle and a
257
+ child process is doing the work. "Pondering" is right for the gap before the
258
+ first token and simply untrue during a build.
259
+
260
+ **A long command is not a blank card.** `bash` is the only tool here that can run
261
+ for minutes — `npm test`, a `git clone`, a training job — and while it ran, its
262
+ card used to be one line of text that never changed. Now it shows the last eight
263
+ lines of what the command is printing, plus one honest footer row:
264
+
265
+ ```
266
+ bash npm test ⠹ running
267
+ │ ✓ src/__tests__/patch.test.ts (49 tests)
268
+ │ ✓ src/__tests__/diff-view.test.tsx (9 tests)
269
+ │ (running)
270
+ ```
271
+
272
+ and, when the child goes quiet, `no output for 45s` instead of a spinner that
273
+ means nothing. A `\r`-driven progress bar — npm, pip, curl, docker — is *one*
274
+ line rewritten a thousand times, and it renders as one row counting up in place,
275
+ not a thousand rows of nearly the same string.
276
+
277
+ **What the tail deliberately does not do.** It is a display, not a transcript.
278
+ ANSI colour, cursor motion and every other control byte are **stripped**, not
279
+ interpreted (keeping colour would mean validating it, and an unterminated escape
280
+ leaks into the rest of the frame; interpreting cursor motion would mean owning a
281
+ screen buffer). It keeps eight rows, so `Ctrl+O` does nothing while a command is
282
+ running — expansion is a promise about stored content, and there is no stored
283
+ rest of a 400 MB stream to show. It is not saved: `/save` during a run writes no
284
+ tail, because a resumed one would describe a process that died with the session.
285
+ And it never reaches the model — at settle the card is replaced by the
286
+ authoritative result, exactly as before. Set `liveToolOutput: false` (or
287
+ `--no-live-tool-output`, or `ARAGON_LIVE_TOOL_OUTPUT=0`, or the **Live output**
288
+ row in `/settings`) and the card is a single `running` row again, with no store
289
+ allocated and no recorder attached.
290
+
291
+ **A file modification looks like a file modification.** `write_file` and
292
+ `edit_file` now produce a real diff — hunks, line numbers, `+N -M`:
293
+
294
+ ```
295
+ +6 -5
296
+ 1163 top level of the file where the section never takes effect. It
297
+ 1164 - `applyFastConfigSet` (`:259`). A key missing from the list is
298
+ 1164 + `applyFastConfigSet` (`:252`). A key missing from the list is
299
+ 1165 `Unknown config key`; a key in the list but missing from the switch
300
+ +13 lines (Ctrl+O)
301
+ ```
302
+
303
+ Creating a 300-line file is the single most consequential thing an agent does,
304
+ and it used to be reported as a byte count. Diffs collapse at 12 rows behind the
305
+ existing `Ctrl+O`; there is no new keybinding.
306
+
307
+ **This costs the model nothing — it saves.** The structured patch reaches the
308
+ screen on a CLI-local channel, not through the tool result, and the text the
309
+ model reads gained a 4 000-character ceiling it did not have before. For a
310
+ single-region edit that text is what it always was plus one `@@` header; for a
311
+ multi-region edit it is strictly smaller, because a change at line 40 and one at
312
+ line 900 are now two small hunks instead of 860 removed rows followed by 860
313
+ near-identical added ones.
314
+
315
+ ## Full-screen mode
316
+
317
+ `aragon` takes over the terminal's **alternate screen buffer** — the mechanism
318
+ `vim`, `htop`, and `lazygit` use. Two consequences worth knowing:
319
+
320
+ - Your previous shell output is **covered, not erased**, and returns untouched
321
+ when you exit. Nothing in your scrollback is destroyed.
322
+ - The frame is fixed at `rows - 1` tall, which is what keeps the composer and the
323
+ status bar at the bottom of the screen even on an empty session.
324
+
325
+ On exit the session is replayed into the normal buffer as plain text so the
326
+ conversation survives leaving the screen (`--no-exit-transcript` opts out;
327
+ `/save` still exports the full JSON).
328
+
329
+ It downgrades to the inline renderer automatically when stdout is not a TTY,
330
+ `TERM=dumb`, a CI environment variable is set, or the terminal is under 12 rows
331
+ or 40 columns. `--fullscreen` overrides all of those except the non-TTY check —
332
+ writing screen-control sequences into a pipe or a redirected file is never safe.
333
+ `--no-fullscreen`, `ARAGON_FULLSCREEN=0`, or `aragon config set fullscreen false`
334
+ opt out permanently.
335
+
336
+ The terminal's native scrollback and mouse wheel do not scroll the transcript in
337
+ this mode, and mouse tracking is deliberately left off because enabling it costs
338
+ text selection and copy in most terminals. If a crash ever strands your terminal
339
+ on the alternate screen, `reset` restores it.
340
+
341
+ ## Render performance
342
+
343
+ A long session used to get slower and slower, and past a point it stopped
344
+ responding at all. The cause was structural rather than incidental: Ink has no
345
+ output caching, so every frame re-walked the whole mounted tree, re-measured
346
+ every text node and re-serialised the frame. The cost was therefore proportional
347
+ to **every character the session had ever produced**, not to the characters
348
+ actually on screen — and `transcriptWindow` bounded the entry *count*, which is
349
+ not the same thing when one entry can be fifty thousand lines.
350
+
351
+ Four things changed, and you should not have to think about any of them:
352
+
353
+ - **Off-screen entries are not mounted.** The viewport renders the band you can
354
+ see plus a couple of entries either side, and replaces the rest with two
355
+ spacers of the right height. Scrolling and the `↑N` readout are unchanged.
356
+ - **The expensive pure work is cached.** Syntax highlighting and markdown
357
+ parsing now run once per distinct block instead of once per frame. Themes are
358
+ still applied at render time, so `/theme` takes effect immediately.
359
+ - **The transcript is bounded.** A single answer cannot exceed 256 KiB in the
360
+ view, and `transcriptRetain` caps how many entries are kept. Both bounds
361
+ report what they removed rather than dropping it silently.
362
+ - **The frame interval adapts.** If a frame is genuinely expensive, updates
363
+ coalesce into a wider window — 33 ms up to a 320 ms ceiling — so the terminal
364
+ stays responsive to typing and to `Ctrl+C`. A muted **`eco`** chip appears in
365
+ the status bar whenever this is in effect: the stream getting chunkier is
366
+ something you should be able to see a reason for.
367
+
368
+ `/perf` reports exactly what the renderer is doing:
369
+
370
+ ```
371
+ render rung 2 - interval 80ms - last commit 61ms - eco
372
+ transcript 4213 entries - 1000 retained - 3213 dropped - 38 mounted
373
+ heights 412 cached - 6 estimated - cols 132
374
+ caches md 178 (1.9MB) - hl 96 (1.4MB) - lines 340
375
+ mode fullscreen - viewport 44 rows - offset 0
376
+ ```
377
+
378
+ `/perf reset` clears the render caches and returns the governor to its fastest
379
+ rung. `--no-render-governor` (or `renderGovernor: false`, or
380
+ `ARAGON_RENDER_GOVERNOR=0`) turns the adaptation off entirely; the output is
381
+ identical, just heavier under load.
382
+
383
+ **Inline mode** (`--no-fullscreen`, `TERM=dumb`, CI, a very small terminal) gets
384
+ one extra guarantee. Ink repaints the *entire session history* on every frame
385
+ once a live entry grows taller than the terminal, which is a genuine freeze
386
+ rather than a slowdown. The live region is now clamped to stay below that
387
+ threshold, with a `... N earlier lines` marker naming what is deferred; the
388
+ entry prints **in full** into your scrollback the moment it finishes.
389
+
390
+ ## Plan mode
391
+
392
+ `aragon` has two postures, and `Shift+Tab` switches between them. `/plan` does
393
+ the same thing for terminals that swallow the key (see below).
394
+
395
+ | Mode | What it means |
396
+ | --- | --- |
397
+ | `BUILD` | The default, and unchanged from earlier versions: you ask, the agent does it. |
398
+ | `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. |
399
+
400
+ `BUILD` is right for "rename this symbol". `PLAN` is right for "add multi-tenant
401
+ support" — anything where you want to see and steer the *approach* before a
402
+ single byte changes.
403
+
404
+ ### What plan mode actually stops
405
+
406
+ In `PLAN`, five tools are refused at the tool boundary: `write_file`,
407
+ `edit_file`, `bash`, `skill_install` and `skill_create`. That is a property of
408
+ the wiring, not a promise in a prompt — the refusal happens before the tool runs,
409
+ so the badge is telling you something that is structurally true.
410
+
411
+ **`bash` is refused in full, `git status` included.** This is the most likely
412
+ question this feature generates, so: there is no reliable way to classify a shell
413
+ string as read-only, and a gate that is right 95 % of the time is worse than one
414
+ that is always right, because you stop trusting the badge. Use `read_file`,
415
+ `list_dir`, `glob` and `grep`, which stay available; the refusal message names
416
+ them so the agent has somewhere to go. There is no allowlist and there will not
417
+ be one built out of substring matching.
418
+
419
+ Everything else still works: reading, searching, loading skills, and asking you
420
+ questions.
421
+
422
+ ### The two plan-mode tools
423
+
424
+ - **`ask_user`** renders a keyboard-driven wizard: 1–5 questions, 2–4 options
425
+ each, exactly one marked `RECOMMENDED`, plus an `Other…` free-text option.
426
+ `↑`/`↓` choose, `Enter` confirms and advances, `←` goes back, `Esc` cancels.
427
+ Pressing `Enter` through the whole wizard accepts every recommendation, which
428
+ is a deliberate "use your judgement" path rather than an accident. A last
429
+ screen lets you review before submitting.
430
+ - **`submit_plan`** renders the plan as a scrollable card: `a` approves, `r`
431
+ opens a one-line feedback field (`Enter` sends it, `←` goes back to the card),
432
+ `Esc` dismisses. **Approving flips the session to `BUILD` immediately and the
433
+ same run continues straight into implementation** — you do not have to re-ask.
434
+
435
+ Cancelling either one is not an error. The agent is told to proceed on stated
436
+ assumptions rather than to retry.
437
+
438
+ **Dismissing a plan is not how you stop.** `Esc` on a plan card asks for a
439
+ *better* plan — the agent is told to refine it and submit again — so a model that
440
+ keeps submitting keeps getting cards. To stop the run itself, **press `Esc`
441
+ twice**: the first closes the card, the second aborts the run. `Ctrl+C` twice
442
+ still exits the session outright.
443
+
444
+ ### Where the mode is shown
445
+
446
+ The status bar always names a non-default mode (`PLAN`, or `PLAN → BUILD` while
447
+ a switch is pending), including on short terminals, with `--no-hints`, and in
448
+ inline mode. The composer additionally shows a `PLAN` chip on its hint row and
449
+ tints its border, where there is room for it.
450
+
451
+ ### Switching mid-run
452
+
453
+ Tightening applies at once; loosening waits. Pressing `Shift+Tab` toward `PLAN`
454
+ during a run takes effect immediately — you pressed it *because* the agent is
455
+ about to do something. Pressing it toward `BUILD` during a run is **deferred to
456
+ the end of the run**, so a session you launched under a read-only guarantee
457
+ cannot start writing files because of one stray keypress. The status bar shows
458
+ `PLAN → BUILD` while that is pending. Approving a plan is the one exception, and
459
+ it is an informed act rather than a stray keypress.
460
+
461
+ ### If `Shift+Tab` does nothing
462
+
463
+ Some multiplexers and remote-desktop stacks never send `CSI Z`. `/plan`,
464
+ `/plan on`, `/plan off` and `/plan status` are first-class equivalents, and
465
+ `--plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the startup case.
466
+
467
+ ### Headless
468
+
469
+ `aragon -p --plan "add SSO"` is a useful combination: the read-only gate still
470
+ applies, so it means *"tell me how you would do this, and do not touch my
471
+ repository"*. No question or plan tool is registered there — there is nobody to
472
+ ask — and the agent writes the finished plan as its final message in markdown.
473
+
474
+ ## TODO planning
475
+
476
+ For work with three or more distinct steps the agent keeps a **visible plan**:
477
+ it writes a checklist with the `todo_write` tool, marks exactly one item as in
478
+ progress, and ticks it off before starting the next. In full-screen mode that
479
+ list is rendered as a right-hand rail, about a fifth of the terminal wide.
480
+
481
+ ```
482
+ transcript ... | TODO 2/7
483
+ | ####------
484
+ |
485
+ | v 1 Read the reducer
486
+ | v 2 Design the store
487
+ | > 3 Wiring the rail into
488
+ | AppShell
489
+ | o 4 Add the panel-rows test
490
+ | o 5 Update the README
491
+ | +2 more
492
+ ```
493
+
494
+ Ask for something small and nothing appears. The agent is told, in its system
495
+ prompt, **not** to use the tool for anything it can finish in one or two steps,
496
+ for a question, or for a search with no work attached — and a one-item list
497
+ against an empty plan is refused outright. A session that never plans has
498
+ byte-identical layout to a build without this feature.
499
+
500
+ **The list is a projection of what the model believes**, so nothing that does
501
+ not also tell the model may change it:
502
+
503
+ - `/clear` wipes the transcript but not the conversation, so the plan stays.
504
+ - `/reset` clears the conversation, so the plan goes with it.
505
+ - `/save` writes the list and `/resume` restores it. Resuming a session that has
506
+ no list clears the current one, rather than leaving a plan on screen whose
507
+ conversation has just been replaced.
508
+ - A finished plan is dropped at the START of your next message, so you still get
509
+ to see `7/7 done`. An unfinished one survives "continue" / "now do the rest",
510
+ and is dropped after three unrelated turns rather than holding a fifth of the
511
+ screen for the rest of the session.
512
+
513
+ ### When a run ends with steps left
514
+
515
+ By default you get a notice and `/todo continue` — nothing auto-continues on
516
+ your behalf. `/todo continue` no longer says "continue the remaining items"; it
517
+ sends the model the numbered list of steps that are still open, so a plan that
518
+ scrolled out of the context window some turns ago is still followed exactly.
519
+
520
+ `/todo follow auto` (or `--todo-follow auto`, or `todo.followThrough` in
521
+ `config.json`) turns that into an actual loop: the CLI announces
522
+ `Continuing with 4 remaining steps in 3s - Esc to stop.`, waits, and then sends
523
+ the continuation itself. Two structural limits bound it, and neither is a
524
+ setting:
525
+
526
+ - A continuation that completes no step buys exactly **one** more attempt.
527
+ After that you get a sentence and the keyboard back.
528
+ - A single plan is continued at most **25** times however productive it looks.
529
+ A re-plan forgives the first counter but never the second, so a model that
530
+ reshapes its list every turn cannot lift the ceiling.
531
+
532
+ `Esc` during the grace window cancels; so does typing anything. A run that ended
533
+ with an **error** never auto-continues in any mode — you get a warning instead.
534
+ A run **you** aborted with `Esc` now says nothing at all, where it used to
535
+ report what you had just interrupted.
536
+
537
+ `/todo follow off` is the third mode: no notice, no continuation, silence.
538
+
539
+ Under `-p` there is no grace window (there is nobody there to use it), so the
540
+ two counters are the whole of the protection. `[todo] continuing (3 steps left)`
541
+ goes to stderr before each continuation and `[todo] 3 steps unfinished` when it
542
+ stops; both are suppressed by `--quiet`, and an unfinished plan never changes
543
+ the exit code.
544
+
545
+ ### Inline mode
546
+
547
+ Inline mode has no fixed frame and therefore no rail, but it does get a one-row
548
+ plan strip under the transcript:
549
+
550
+ ```
551
+ todo 3/7 > Adding the rail to AppShell +2 done
552
+ ```
553
+
554
+ It never wraps and never grows past one row, so the composer does not move. The
555
+ `+N done` suffix drops below 80 columns and the counter compacts to `[3/7]`
556
+ below 100, matching the status bar. `--no-todo-panel` hides the strip as well as
557
+ the rail.
558
+
559
+ `/todo` reports status and switches things off: `/todo off` unregisters nothing
560
+ (the tool list is fixed when the session starts) but stops the tool working and
561
+ drops the guidance from the system prompt, while `/todo panel off` keeps the
562
+ planning discipline and hides the column — which is what you want with a screen
563
+ reader, and what `--no-todo-panel` does for a whole session. Under `-p` the plan
564
+ is written to stderr as `[todo] 3/7 <step>` lines, suppressed by `--quiet`.
565
+
566
+ The rail is not mounted below 80 columns, while an overlay is open, in inline
567
+ mode, or when the viewport is too short to say anything useful; the transcript
568
+ keeps at least 62 columns at every width. In those cases the counter appears in
569
+ the status bar instead, and the checklist is still written into the transcript
570
+ as one card per turn.
571
+
572
+ ## Built-in tools
573
+
574
+ `read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `grep`, `bash` — all
575
+ at full permission. Paths resolve against the session working directory. The
576
+ per-tool timeout and 100 KB output truncation are enforced by the core executor;
577
+ `bash`'s `timeout` param only shrinks within that ceiling.
578
+
579
+ When skills are enabled, four more are added — the two read tools `skill` and
580
+ `skill_find`, then the two mutating ones `skill_install` and `skill_create` (see
581
+ below). `--no-skills` removes them again. Updating a skill is deliberately not
582
+ among them; that is a `aragon skills update` decision for you, not the model.
583
+
584
+ With team mode on — which is the default — one more is added: `task`, the
585
+ delegation tool (see below). `--no-team` removes it again.
586
+
587
+ With todo planning on — also the default — one more is added: `todo_write`, the
588
+ planning tool (see above). `--no-todo` removes it again.
589
+
590
+ ## Team subagents
591
+
592
+ The agent can hand parts of a job to short-lived **subagents** that run in
593
+ parallel, then read one combined report of what they each found. It decides for
594
+ itself whether to delegate; a session that never calls `task` is the session
595
+ that existed before this feature.
596
+
597
+ ```
598
+ > read the auth middleware, map the route table, and check the migration
599
+
600
+ ◆ team 2 running · 1 done · 84s ✉ 1
601
+ ⠋ a1 read the auth middleware tool: grep 22.1s
602
+ ⠙ a2 map the route table thinking 31.6s
603
+ ✔ a3 check the migration done 4 turns 18.2s
604
+ ```
605
+
606
+ **What a subagent is.** A full agent with its own message history, the same
607
+ tools and the same working directory — but no memory of your conversation, so
608
+ its brief has to be self-contained. It cannot delegate further (depth is capped
609
+ at one by construction) and it cannot ask you anything.
610
+
611
+ **Delegate wide, not deep.** Two to four subagents is the usual size. The
612
+ default cap is 5 per dispatch and 3 in flight at once; the hard maximum is 10
613
+ and no config file can raise it. `maxConcurrent` is separate from `maxSubagents`
614
+ on purpose: five simultaneous streams against one API key is a reliable way to
615
+ collect rate-limit errors, and each one would land in the report as a failed
616
+ subagent.
617
+
618
+ **The report.** One text result: a header with the ok/failed counts, aggregate
619
+ tokens and cost, then one section per subagent with its status, timing, the
620
+ files it wrote and its own final summary. A failed subagent never fails the
621
+ dispatch — partial results are the normal outcome of a fan-out. When two
622
+ subagents wrote the same file the header says so, phrased as a warning rather
623
+ than a fact: the file list is derived from `write_file` / `edit_file`, so a
624
+ write made through `bash` is invisible to it.
625
+
626
+ **Talking to each other.** Subagents get two extra tools, `team_send` and
627
+ `team_wait`. Messages are delivered by attaching them to the recipient's next
628
+ tool result, so nobody has to poll and nobody's work is interrupted. The limits
629
+ are the point rather than an afterthought: **6 messages per subagent, one every
630
+ 15 seconds, 24 per dispatch**, a broadcast costing two. Every refusal is a plain
631
+ result naming the limit, never an error — an error reads as a malfunction and
632
+ invites a retry, which is the behaviour the limits exist to prevent.
633
+
634
+ **What you see.** While a dispatch runs, the roster above sits between the
635
+ transcript and the composer and the status bar carries `agents 3/5` (`[3]` under
636
+ 100 columns). Both disappear when it finishes and the transcript keeps a card
637
+ with the per-agent results; `Ctrl+O` expands it to their summaries. Under `-p`
638
+ the same transitions go to stderr as `[team] a1 ok 22.1s 7 tools` lines, and the
639
+ children's tokens are folded into the `[usage]` footer.
640
+
641
+ **Interaction with the other subsystems.**
642
+
643
+ - **Plan mode reaches one level down.** In PLAN, every subagent is built with
644
+ the same read-only gate, so `write_file` / `edit_file` / `bash` are refused
645
+ inside them too. Parallel research is plan mode's best use, so `task` itself
646
+ is allowed. A single subagent can be pinned read-only with `readOnly: true`
647
+ even in a BUILD session — the field can only ever tighten.
648
+ - **A skill's `allowed-tools` ceiling reaches one level down too**, so
649
+ delegating is not a way around it. Subagents get `skill_find` but not `skill`:
650
+ loading a skill mutates a turn-scoped registry the lead owns, and three
651
+ subagents doing it at once would narrow what the lead may do next in a way
652
+ nothing would report. Always-on skills still reach them.
653
+ - **`--confirm`** queues subagent confirmations one at a time, labelled with the
654
+ subagent that is asking (`[a2] Write: src/routes.ts`).
655
+ - **Logs.** `dispatch_start` and `dispatch_end` are recorded at `info` with the
656
+ ok / failed / aborted counts and aggregate usage; phase transitions at
657
+ `debug`. A `team_send` subject is recorded, never its body; a subagent's brief
658
+ and summary only at `trace`.
659
+
660
+ **`Esc`** aborts the whole dispatch — every subagent within a couple of seconds
661
+ — and the report comes back marked `ABORTED` with whatever was finished.
662
+
663
+ ## Fast model tier
664
+
665
+ A second, cheaper model can be made a first-class citizen of a session. It is
666
+ **off by default** — with `fast.enabled: false` the system prompt, the `task`
667
+ tool schema, the transcript and the request payloads are exactly what they were
668
+ before this feature existed — and it buys you two things once you turn it on.
669
+
670
+ **Delegation (the agent decides).** `task` gains one optional field per
671
+ subagent, `model: "fast"`. A child marked that way runs on the fast model and is
672
+ otherwise identical: same tools, same working directory, same `--confirm` gate,
673
+ same plan-mode gate, same skills ceiling. Delegating to a cheaper model is a
674
+ *model* choice, never a permission boundary. This is where "not complicated, but
675
+ very expensive in context" is actually paid for: a child that reads nine files
676
+ and reports four sentences moves ~200 KB of file bodies out of the lead's window
677
+ and onto a model that costs a fraction as much per token.
678
+
679
+ **Periodic review (the harness decides).** Every *N* completed turns, a small
680
+ digest of what the lead has been doing goes to the fast model with one question
681
+ — is this still on track? — and if the answer is not "on track" it is injected
682
+ back into the running loop as a `<fast_review>` block. The lead sees a short
683
+ second opinion mid-run, when it can still act on it, for a few hundred tokens
684
+ instead of a second full-size turn.
685
+
686
+ ```
687
+ > refactor the config loader and keep the tests green
688
+
689
+ fast review #2 · claude-haiku-4-5 · turn 12 · 1.4s
690
+ You have edited config/schema.ts three times without running the tests.
691
+ The clamp for reviewEveryTurns is missing its upper bound.
692
+ ```
693
+
694
+ **The digest is bounded, and that is the whole economic argument.** The reviewer
695
+ keeps its own ring of recent turns — the tools you called, whether they failed,
696
+ the tail of what the model said — and never reads the message history. Sending
697
+ that history to the "cheap" model would make a review cost more than the turn it
698
+ is reviewing.
699
+
700
+ **Cost is attributed per tier.** A fast-tier subagent's tokens are priced at the
701
+ fast model's table, not the lead's, and the dispatch report states the two
702
+ separately. If the fast model is not in the built-in price table the number is
703
+ shown as `unknown` rather than as `$0.00` — a feature that looks free while it
704
+ is spending money is worse than one that admits it does not know.
705
+
706
+ **The review has a session budget, and delegation deliberately does not.** A
707
+ session is not one run — it is one run per message you send — so a cadence alone
708
+ bounds nothing over a working afternoon. `fast.reviewMaxPerSession` (default
709
+ **40**) is the total number of reviews a session may *start*; reaching it prints
710
+ one `info` notice naming the remedy, and `/fast status` reads `12/40` from the
711
+ first review rather than a bare count you can only size afterwards. Raise it live
712
+ with `/fast budget 80`. The budget is denominated in **reviews, not currency**,
713
+ because the fast tier is precisely where a model the price table has never seen
714
+ lives — a dollar ceiling would silently never fire for the configuration that
715
+ most needs one. Delegation is never budgeted: it is spend the lead chose in
716
+ service of a message you sent, it is already bounded by `team.*`, and it exists
717
+ to *reduce* total cost.
718
+
719
+ ### Turning it on
720
+
721
+ ```bash
722
+ aragon config set fast.model claude-haiku-4-5
723
+ aragon config set fast.enabled true
724
+ ```
725
+
726
+ or, in a session: `/fast model claude-haiku-4-5` then `/fast on`. Either way the
727
+ tier is on for the next launch; `/fast on` in a session that started without it
728
+ saves the setting and says so, because the `task` schema is fixed at startup and
729
+ cannot grow a field mid-session.
730
+
731
+ | key | default | meaning |
732
+ |---|---|---|
733
+ | `fast.enabled` | `false` | Resolve the tier at all. |
734
+ | `fast.provider` | `''` | Empty inherits the main provider. |
735
+ | `fast.model` | `''` | The fast model id. Empty means *not configured* — it never falls back to the main model, so "same as main" stays distinguishable from a typo. |
736
+ | `fast.baseUrl` | `''` | Empty inherits the session's base URL **only** when the provider matches. |
737
+ | `fast.thinkingLevel` | `'off'` | Applied to fast children and to reviews. |
738
+ | `fast.delegate` | `true` | Allow `model:"fast"` on `task`. |
739
+ | `fast.review` | `true` | Run the periodic review. |
740
+ | `fast.reviewEveryTurns` | `5` | Turns between reviews (1–50). |
741
+ | `fast.reviewContextTurns` | `3` | Turns included in one digest (1–10). |
742
+ | `fast.reviewMaxChars` | `280` | Ceiling on an injected critique (80–600). |
743
+ | `fast.reviewMaxPerSession` | `40` | Reviews started per session, across all runs (1–500). There is no "unlimited" sentinel. |
744
+
745
+ **Flags:** `--fast` / `--no-fast`, `--fast-model <id>`, `--fast-provider <id>`,
746
+ `--fast-review <n|off>`. **Environment:** `ARAGON_FAST`,
747
+ `ARAGON_FAST_PROVIDER`, `ARAGON_FAST_MODEL`, `ARAGON_FAST_BASE_URL` — the four
748
+ that a container or a wrapper owning argv cannot otherwise reach. There is
749
+ deliberately no env var for the review cadence: tuning it is what the config
750
+ file is for.
751
+
752
+ **`/fast`** is the discoverable surface and `/fast status` is the guaranteed
753
+ reporting one:
754
+
755
+ ```
756
+ /fast status, the resolved model, the cadence, session totals
757
+ /fast on | off live switch
758
+ /fast model <id> accepts provider:model
759
+ /fast provider <id>
760
+ /fast same run the fast tier on the main model
761
+ /fast review <n> | off
762
+ /fast budget [n] the session review budget; bare form reads it
763
+ /fast delegate on | off
764
+ ```
765
+
766
+ `/fast budget` with no argument is a **read**, so it is answerable while a `task`
767
+ dispatch is running; `/fast budget <n>` is a write and is refused mid-dispatch
768
+ like every other `/fast` setting.
769
+
770
+ **What you see.** A `fast` chip in the status bar's right cluster while the tier
771
+ is live (`fast*` while a review is in flight, dropped below 100 columns), a
772
+ one-line card per review in the transcript, and `n fast` plus a trailing `~` on
773
+ fast children in the team roster. `/fast status` reports the session totals the
774
+ status bar cannot: reviews run, children delegated, tokens and cost. Under `-p`
775
+ nothing extra is written to stdout or stderr — the review spend is folded into
776
+ the `[usage]` footer and the reviews are in the log.
777
+
778
+ **When the tier stops resolving.** `fast.provider: ''` inherits from the main
779
+ provider, so switching model or clearing a key can leave the tier unusable
780
+ mid-session. When that happens `model:"fast"` is downgraded to the main model,
781
+ the downgrade is **counted and stated in the dispatch report**, and one notice
782
+ names the reason. A silent downgrade would leave the agent believing its cost
783
+ model while paying full price.
784
+
785
+ **When the review fails.** Three consecutive *misconfiguration* failures (a
786
+ wrong model id, a dead gateway, a key with no access) disable the reviewer for
787
+ the session with exactly one warning. A busy provider — rate-limited or
788
+ overloaded — is treated as transient and never counts, because that condition
789
+ fixes itself and a session-long silent shutdown over it would not. A review that
790
+ you *cancelled* — Esc, a run that ended first, quitting — is neither: it leaves a
791
+ `cancelled` card and no strike.
792
+
793
+ **The review fails fast, and your own work does not.** The reviewer calls through
794
+ its own provider registry with **one** retry — not the ten `retry.maxRetries`
795
+ defaults to for the lead — and it does not observe `retry.*` or `/retry` at all.
796
+ That policy is a statement about how hard to fight for *your* answer; inheriting
797
+ it for a background advisory is how an unrequested call ends up competing with
798
+ you for a rate-limited provider's quota. The review's own wall-clock bound is
799
+ 20 s.
800
+
801
+ ## Skills
802
+
803
+ A skill is a reusable expert procedure stored on disk: a directory containing a
804
+ `SKILL.md` (YAML frontmatter + Markdown body) and, optionally, `reference/`,
805
+ `scripts/` and `assets/`. The format matches Claude Code's, so an existing
806
+ community skill directory works as-is.
807
+
808
+ ### Progressive disclosure
809
+
810
+ This is the part that makes skills cheap enough to install a lot of:
811
+
812
+ | Level | What the model gets | When |
813
+ | --- | --- | --- |
814
+ | 1 | Name, scope, and a one-line "what + when" | Always, in the system prompt. Whole catalog capped at 6 000 bytes. |
815
+ | 2 | The full `SKILL.md` body + a list of bundled files | Only when the model calls `skill(name=…)` |
816
+ | 3 | The bundled files themselves | Only when it reads them with `read_file` / `bash` |
817
+
818
+ A skill costs a line in the prompt rather than its whole body, so installing
819
+ many of them stays cheap. The model decides on its own whether a task matches a
820
+ skill — you do not have to name one.
821
+
822
+ At Level 2 the file list is split into what to read and what to run, and carries
823
+ the skill's absolute root so the model can build real paths. The accompanying
824
+ guidance is **platform-specific**: on Windows the `bash` tool runs `cmd.exe`, so
825
+ the model is told to run `.py` with `python <abs path>` and that a `.sh` file
826
+ will not work — rather than the previous, flatly untrue "run scripts with bash".
827
+ `aragon skills doctor` warns when a skill bundles only scripts for the other
828
+ shell.
829
+
830
+ **How many fit in Level 1 depends on your descriptions, and on the language they
831
+ are written in.** The 6 000-byte ceiling is measured in UTF-8 bytes: a terse
832
+ English description costs roughly 180 bytes a line (~30 skills), while a Chinese
833
+ one at the 220-character cap can cost 660 (~10 skills). Past that point the
834
+ catalog truncates.
835
+
836
+ Truncation is not a dead end. Entries are ranked before it happens, and what
837
+ falls off is still reachable:
838
+
839
+ - **Ranking**: scope first (`env` > `project` > `user` > `bundled`), then how
840
+ recently and how often you have actually used each skill. A skill you ran an
841
+ hour ago outranks one you have never used, so what survives truncation is what
842
+ you actually work with — not whatever sorts first alphabetically.
843
+ - **`skill_find`**: when the catalog *does* truncate, it says so and names this
844
+ tool, which searches everything installed by name, description and keyword.
845
+ It only ever searches **what is already on this machine** — it never reaches
846
+ the network and never suggests something to install.
847
+
848
+ Both are off the critical path: with no usage data recorded the order is exactly
849
+ the plain scope-then-name order, and `skill_find` is only advertised in the
850
+ prompt when something was actually omitted.
851
+
852
+ ### Using them
853
+
854
+ ```bash
855
+ aragon skills list [--sort=recent] # what is installed
856
+ aragon skills info <name> # version, path, provenance, files
857
+ aragon skills install <source> [--yes] # dir | git repo | https .md / .zip
858
+ aragon skills install github:owner/repo#v1.2.0
859
+ aragon skills update <name> [--yes] [--force] [--dry-run]
860
+ aragon skills update --all [--yes] # every skill that has an upstream
861
+ aragon skills update --check [<name>|--all] # is there a newer version?
862
+ aragon skills remove <name> [--yes]
863
+ aragon skills create <name> --description "what + when" [--yes]
864
+ aragon skills path # user skills directory
865
+ aragon skills doctor # validate + detect tampering
866
+ aragon skills usage [--json] # local use counters
867
+ aragon skills usage --reset --yes # delete them
868
+ aragon skills trust <dir> # allow a project skills directory
869
+ ```
870
+
871
+ #### Updating
872
+
873
+ `update` re-fetches a skill from the source recorded in its install manifest,
874
+ keeping `installedAt` and recording `updatedAt` + `previousVersion`.
875
+
876
+ - **Local edits are protected.** If you have changed an installed skill, `update`
877
+ refuses and names the files, *before downloading anything*. `--force`
878
+ overwrites them — and **`--force` discards your edits**; copy them out first.
879
+ The replaced directory is parked under the staging trash, so a mistake is
880
+ recoverable by hand.
881
+ - **The host allowlist is re-checked** against your *current*
882
+ `skills.allowedHosts`, not the one in force when you installed. Narrowing that
883
+ list therefore also constrains updates.
884
+ - **`--all` never stops on one failure.** Skills that cannot be updated
885
+ (hand-authored, bundled, or locally modified) are reported as `skipped`, which
886
+ is not an error; only a genuine I/O or validation failure exits non-zero.
887
+ - **`--check` reports `unknown` freely.** It compares the commit recorded at
888
+ install time against `git ls-remote`, so it can only answer for git sources
889
+ installed by a version that records one. Anything else is honestly reported as
890
+ unknown rather than guessed.
891
+ - **The model cannot call `update`.** Installing is something you pointed it at;
892
+ swapping working instructions for different ones is a maintenance decision and
893
+ stays with you.
894
+
895
+ Interactively the same lives under `/skills …` (`/skills update <name> [--force]`,
896
+ `/skills update --all`, `/skills list --sort=recent`, `/skills policy [mode]`,
897
+ `/skills unload`, `/skills usage [--reset]`). Every usable skill also becomes a
898
+ slash command:
899
+
900
+ ```
901
+ /pdf-forms invoice.pdf --flatten
902
+ ```
903
+
904
+ Both delivery paths share `skills.bodyMaxBytes`, so an oversized `SKILL.md` is
905
+ truncated with a visible marker rather than submitted whole. Loading the same
906
+ skill twice in one conversation returns a short digest (description, file list,
907
+ guidance) instead of a second copy of the body; the model can ask for the full
908
+ text back with `skill(name="…", force=true)`.
909
+
910
+ Bodies may use `$ARGUMENTS` and `$1..$9`; `$$` is a literal `$`. If a skill
911
+ declares no placeholder, the arguments are appended under an `## Arguments`
912
+ heading instead. Where a skill's name collides with a built-in command, the
913
+ **built-in wins** and the skill is reachable as `/skill:<name>`.
914
+
915
+ ### Where skills come from
916
+
917
+ Four scopes, later ones overriding earlier ones by name:
918
+
919
+ | Scope | Location | Writable |
920
+ | --- | --- | --- |
921
+ | `bundled` | `<package>/skills/` | no |
922
+ | `user` | `aragon skills path` | yes — the default install target |
923
+ | `project` | `<cwd>/.aragon/skills/` | yes |
924
+ | `project` | `<cwd>/.claude/skills/` | no — read-only Claude Code interop |
925
+ | `env` | `ARAGON_SKILLS_PATH` (`:`/`;`-separated) | no |
926
+
927
+ A shadowed skill is not lost: `/skills` shows it as `~shadowed by <scope>`.
928
+
929
+ ### Frontmatter
930
+
931
+ ```yaml
932
+ ---
933
+ name: pdf-forms # required, kebab-case, ≤ 64 chars
934
+ description: Fill, flatten and validate AcroForm PDFs. Use when the task
935
+ mentions PDF forms, field filling, or flattening a fillable PDF.
936
+ version: 1.2.0
937
+ license: MIT
938
+ author: Jane Doe
939
+ homepage: https://github.com/jane/pdf-forms
940
+ keywords: [pdf, forms]
941
+ allowed-tools: [read_file, bash] # enforced as a per-turn tool ceiling (union)
942
+ activation: auto # auto | always | manual
943
+ ---
944
+ ```
945
+
946
+ `description` is the only text a model sees before deciding to load the skill,
947
+ so it has to say both what the skill does and when to use it. Unknown keys are
948
+ preserved, so a newer Claude Code field will not break parsing.
949
+
950
+ `activation: always` injects the body into every session's system prompt (12 000
951
+ bytes total across all such skills); `manual` keeps it out of the catalog so it
952
+ runs only when explicitly invoked.
953
+
954
+ ### Security model — and its limits
955
+
956
+ Be clear about what this does and does not buy you:
957
+
958
+ - **Installing requires a human.** The approval gate checks whether a person is
959
+ actually reachable *before* asking, and refuses when nobody is. Under `-p`
960
+ there is no TUI and therefore no approver, so `skill_install` fails with an
961
+ actionable message instead of writing to disk unattended. Opt out
962
+ deliberately with `--skills-yes` or `skills.requireApproval=false`.
963
+ - **Project directories need trusting once.** Cloning an unfamiliar repository
964
+ does not grant it the right to inject instructions into your session.
965
+ - **Nothing is executed at install time.** No postinstall, no dependency
966
+ install; the executable bit is not preserved. Scripts run only when the model
967
+ invokes `bash`.
968
+ - **Archives are gated on real bytes** — total size, per-entry size, compression
969
+ ratio and entry count, all abortable mid-extraction. Path traversal,
970
+ absolute paths, symlinks and Windows device names are refused.
971
+ - **Git is never run through a shell**, and ref / owner / repo fragments are
972
+ whitelisted and refused if they start with `-`.
973
+ - **Third-party text cannot break out of its prompt block**: angle brackets in
974
+ descriptions, bodies and file names are neutralized before rendering.
975
+
976
+ - **A changed `SKILL.md` is noticed at load time.** Every install records a
977
+ sha256; discovery re-checks the one file that reaches the model's context and
978
+ flags it as `!modified` if it no longer matches what you approved. See
979
+ `skills.integrity` below.
980
+ - **Concurrent installs cannot corrupt a skill.** Writes take a per-directory
981
+ advisory lock, so two `aragon` processes installing at once queue instead of
982
+ interleaving their atomic-replace steps. *Limit:* the lock relies on exclusive
983
+ file creation, which is not reliably atomic on every NFS implementation. It
984
+ self-expires after 60 seconds and waits at most 5, so the worst case there is
985
+ no mutual exclusion — never a hang.
986
+
987
+ What it does **not** do: there is **no sandbox**. Approval blocks *silent*
988
+ installation; it does not make a skill you approved safe. A skill you accept can
989
+ contain adversarial instructions, and its scripts run with your full user
990
+ permissions — the same trust model as pasting text into the prompt and the same
991
+ as the `bash` tool. `/skills info` records provenance and a sha256 per file so
992
+ you can audit what arrived and notice if it changed. The integrity check answers
993
+ "is this still the file I approved?", **not** "is this file from someone I
994
+ trust" — there is no signing and no key distribution.
995
+
996
+ `allowed-tools` **is** enforced now (see below), but read the next section before
997
+ treating it as a permission boundary: it is blast-radius control, not a security
998
+ boundary, and a skill can widen its own ceiling by loading another skill.
999
+
1000
+ ### The tool ceiling (`allowed-tools`)
1001
+
1002
+ A skill that declares `allowed-tools` constrains which tools the model may use
1003
+ **for the rest of the current turn**:
1004
+
1005
+ ```yaml
1006
+ allowed-tools: [read_file, write_file]
1007
+ ```
1008
+
1009
+ - **Scope is one turn.** The ceiling appears when a skill is loaded — by the
1010
+ model calling `skill(...)`, or by you running `/<skill-name>` — and it is gone
1011
+ as soon as you send the next message. A skill you used this morning does not
1012
+ restrict unrelated work this afternoon.
1013
+ - **Read-only tools are always allowed**, declared or not: `read_file`,
1014
+ `list_dir`, `glob`, `grep`, plus `skill` and `skill_find`. What the declaration
1015
+ actually controls is the five tools that change something — `write_file`,
1016
+ `edit_file`, `bash`, `skill_install`, `skill_create`.
1017
+ - **Claude Code names work.** `[Read, Bash, MultiEdit]` maps onto this host's
1018
+ `read_file` / `bash` / `edit_file`. Tools that exist there but not here
1019
+ (`WebFetch`, `Task`, `TodoWrite`, …) are ignored rather than treated as errors.
1020
+ - **A name we cannot resolve waives the whole declaration**, and says so. One
1021
+ typo means that skill imposes no ceiling at all — never a narrower one you did
1022
+ not write. `aragon skills doctor` reports these up front.
1023
+ - **`activation: always` skills never impose a ceiling.** They are ambient
1024
+ reference material, not a procedure you chose to run.
1025
+
1026
+ Three modes, via `skills.toolPolicy` (default `enforce`),
1027
+ `aragon --skill-tool-policy <mode>` for one run, or `/skills policy <mode>` for
1028
+ the current session (which takes effect immediately, even mid-turn):
1029
+
1030
+ | Mode | Behaviour |
1031
+ | --- | --- |
1032
+ | `enforce` | The call is refused; the model is told what it may use instead. |
1033
+ | `warn` | The call runs, and you get one notice per tool per turn. |
1034
+ | `off` | No ceiling, and nothing about `allowed-tools` reaches the model. |
1035
+
1036
+ `/skills unload` drops the ceiling for the current turn. It does **not** remove
1037
+ skill text already in the conversation — nothing can.
1038
+
1039
+ **What this is and is not.** It is **blast-radius control, not a security
1040
+ boundary**. The declaration is written by the skill's own author, so a malicious
1041
+ skill can simply declare `[bash]`. Its value is that a declaration is now
1042
+ binding: you can read one in `/skills info` and decide whether to install, and an
1043
+ honest author's `[read_file]` means a "while you're there, run `rm -rf`" line
1044
+ smuggled into that skill's text — or into a file it tells the model to read —
1045
+ cannot execute.
1046
+
1047
+ **And a known limit:** the ceiling is a **union** across every skill loaded this
1048
+ turn, and `skill` is always available. So a skill can widen its own ceiling by
1049
+ instructing the model to load a second, more permissive skill. This is not
1050
+ blocked — blocking it would either strand the model inside a skill it cannot
1051
+ escape, or break every multi-skill workflow — but every widening is printed to
1052
+ the transcript:
1053
+
1054
+ ```
1055
+ Tool ceiling widened by skill "skill-creator": +write_file, +skill_create
1056
+ ```
1057
+
1058
+ So it stops the smuggled instruction; it does not stop a skill that deliberately
1059
+ teaches the model to reach for another one.
1060
+
1061
+ ### Usage counters
1062
+
1063
+ To rank the catalog, the CLI keeps a small file next to your skills directory:
1064
+
1065
+ ```jsonc
1066
+ // <aragon skills path>/../skill-usage.json
1067
+ { "schema": 1, "skills": { "deploy-preview": { "useCount": 12, "lastUsedAt": 1785050000000 } } }
1068
+ ```
1069
+
1070
+ **It contains the skill name, a count, and a timestamp. Nothing else** — no
1071
+ arguments, no conversation content, no working directory, no machine identifier.
1072
+ It is never transmitted anywhere, by anything. Delete it whenever you like; the
1073
+ only effect is that the catalog falls back to plain scope-then-name order. Set
1074
+ `skills.usageTracking=false` to switch it off entirely, after which the file is
1075
+ neither read nor written.
1076
+
1077
+ Automatically injected `activation: always` skills are deliberately **not**
1078
+ counted — they load on every turn, so counting them would say nothing about what
1079
+ you actually use.
1080
+
1081
+ ## Configuration
1082
+
1083
+ Config is layered (highest priority last): **defaults → user config file →
1084
+ env / `.env` → CLI flags**.
1085
+
1086
+ - **Env / `.env`**: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
1087
+ `GOOGLE_API_KEY` / `GEMINI_API_KEY`, plus `ARAGON_PROVIDER`, `ARAGON_MODEL`,
1088
+ `ARAGON_BASE_URL`, `ARAGON_THINKING`, `ARAGON_SHOW_THINKING` (`1` shows
1089
+ reasoning blocks), `ARAGON_LIVE_TOOL_OUTPUT` (`0` keeps a running tool card to
1090
+ one line),
1091
+ `ARAGON_MAX_TOKENS` (a number, or `auto` / `0` for the per-model ceiling),
1092
+ `ARAGON_THEME`,
1093
+ `ARAGON_FULLSCREEN`, `ARAGON_PLAN` (`1` starts in plan mode),
1094
+ `ARAGON_TEAM` (`0` disables team subagents), `ARAGON_TEAM_MAX` (fan-out width),
1095
+ `ARAGON_TODO` (`0` disables todo planning entirely),
1096
+ `ARAGON_TODO_FOLLOW` (`notify` / `auto` / `off`),
1097
+ `ARAGON_RETRY` (`0` disables API retry), `ARAGON_RETRY_MAX` (retry count; `0`
1098
+ also disables),
1099
+ `ARAGON_UPDATE` (`0` / `off` disables auto-update, `notify` reports without
1100
+ installing), `ARAGON_UPDATE_REGISTRY` (registry base URL for the update check),
1101
+ `ARAGON_SKILLS` (`0` disables), `ARAGON_SKILLS_PATH`,
1102
+ `ARAGON_SKILLS_DISABLED` (comma-separated names), `ARAGON_LOG_LEVEL`,
1103
+ `ARAGON_LOG_FILE` (`0` disables), `ARAGON_LOG_DIR`,
1104
+ `ARAGON_TRANSCRIPT_RETAIN`, `ARAGON_RENDER_GOVERNOR` (`0` disables),
1105
+ `ARAGON_MAX_RENDER_INTERVAL_MS`.
1106
+ `ARAGON_HOME` is the exception that **cannot** come from `.env`: it is read
1107
+ before any `.env` is loaded. See [Files & logs](#files--logs).
1108
+ - **User config file** (`aragon config path`): JSON, written `0600` on POSIX.
1109
+ Holds provider / model / keys and your preferences, and nothing else — the
1110
+ prompts you type and the counters behind one-time notices live in their own
1111
+ files (see [Files & logs](#files--logs)). Keys are masked in the UI and never
1112
+ logged. It lives in your home directory — see [Files & logs](#files--logs) —
1113
+ and editing it by hand is supported: `aragon config edit` opens it, and an
1114
+ invalid edit is reported rather than silently resetting every setting.
1115
+
1116
+ | Config key | Default | Meaning |
1117
+ | --- | --- | --- |
1118
+ | `maxTokens` | `64000` | Output token cap, clamped to `[256, 200000]`. `null` means **auto** — use each model's own ceiling, never above 64000. Absent means the default. See [Output token limits](#output-token-limits). |
1119
+ | `showThinking` | `false` | Draw the reasoning the model returns. `thinkingLevel` is the effort the provider is asked to **spend**; this is whether the terminal **shows** it. Off by default: a settled turn that thought leaves one muted `thought for 12s` row in its place, so nothing is hidden silently. |
1120
+ | `liveToolOutput` | `true` | Draw up to eight sanitised rows of a **running** tool's output on its card, plus a `no output for Ns` row when the child goes quiet. On by default, unlike `showThinking`: this ADDS the information a long `bash` call otherwise hides, and its cost is bounded by construction — eight rows per call, sixteen calls, whatever the command emits. Turn it off and the card is a single `running` row again, with no store allocated and no recorder attached. |
1121
+ | `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. |
1122
+ | `exitTranscript` | `true` | Replay a plain-text session summary after exiting (full-screen only). |
1123
+ | `transcriptWindow` | `1000` | How far back you can scroll, clamped to `[50, 20000]`. Entries beyond it collapse into one line. It is no longer a rendering budget: the viewport is virtualised, so off-screen entries are not laid out at all. |
1124
+ | `transcriptRetain` | `1000` | Entries kept in memory, clamped to `[200, 20000]` and raised to `transcriptWindow` if the two conflict — with a startup notice saying so, because retaining less than you can scroll to would otherwise make part of the horizon quietly unreachable. Older entries are dropped, counted, and reported by `/perf` and by the exit replay. |
1125
+ | `renderGovernor` | `true` | Widen the streaming frame interval when a frame is genuinely expensive. See [Render performance](#render-performance). |
1126
+ | `maxRenderIntervalMs` | `320` | Ceiling of the governor ladder, clamped to `[33, 1000]`. `33` flattens it to the pre-0.6 cadence without disabling the mechanism. |
1127
+ | `planModeDefault` | `false` | Start every session in plan mode. `--no-plan` still overrides it for a single run. |
1128
+ | `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. |
1129
+ | `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. |
1130
+ | `historyEnabled` | `true` | Record submitted prompts to `prompt-history.jsonl` for `↑` recall. Setting it to `false` stops new writes; it does not delete what is already stored (that is `aragon history clear --yes`) and does not stop reading. See [Prompt history](#prompt-history). |
1131
+ | `team.enabled` | `true` | Master switch for team subagents. `false` means the `task` tool is never registered — `/team on` in such a session saves the setting for next launch and says so, rather than advertising a tool that is not there. |
1132
+ | `team.maxSubagents` | `5` | Subagents per dispatch, clamped to `[1, 10]`. The 10 is the hard ceiling and is enforced twice — on the config value and again on what the model asks for — so hand-editing this file cannot raise it. |
1133
+ | `team.maxConcurrent` | `3` | Subagents in flight at once, clamped to `[1, 10]` and capped at `maxSubagents`. A separate knob because provider rate limits are a different constraint from context economics. |
1134
+ | `team.subagentTimeoutMs` | `300000` | One subagent's wall clock before it is aborted, clamped to `[30000, 1800000]`. A wedged subagent dies on its own ceiling without taking the dispatch with it. |
1135
+ | `team.dispatchTimeoutMs` | `900000` | The whole dispatch's wall clock, clamped to `[60000, 3600000]`. |
1136
+ | `team.maxTurnsPerSubagent` | `24` | Runaway-loop cap, clamped to `[4, 100]`. A subagent stopped here is reported as `stopped: turn cap reached`, with whatever it had produced. |
1137
+ | `todo.enabled` | `true` | Master switch for todo planning. `false` means the `todo_write` tool is never registered — `/todo on` in such a session saves the setting for next launch and says so, rather than advertising a tool that is not there. |
1138
+ | `todo.panel` | `true` | Render the right-hand rail (and, in inline mode, the one-row plan strip). Independent of `todo.enabled` on purpose: a screen-reader user wants the planning discipline without the column, and the system prompt varies one sentence accordingly. |
1139
+ | `todo.followThrough` | `"notify"` | What happens when a run ends with steps left. `notify` says so and stops; `auto` continues the plan after a 3-second grace window, bounded by two structural limits (one fruitless attempt, 25 continuations per plan) that are deliberately **not** settings; `off` is silent. An unrecognized value falls back to `notify`. |
1140
+ | `retry.enabled` | `true` | Retry a failed provider call at all. Unlike `team.enabled` / `todo.enabled` this decides nothing at construction — the policy lives on the provider registry, so `/retry on` / `/retry off` takes effect **in the running session**, subagents included. |
1141
+ | `retry.maxRetries` | `10` | Retries **after** the first attempt, clamped to `[0, 20]`. `0` is the kill switch and is a real value here, not a typo for the default. The 20 is a hard ceiling enforced twice — on the config value and again inside the engine — so hand-editing this file cannot raise it. |
1142
+ | `retry.initialDelayMs` | `1000` | The first wait, clamped to `[100, 30000]`. |
1143
+ | `retry.maxDelayMs` | `30000` | Ceiling on one wait, clamped to `[1000, 120000]` and raised to `initialDelayMs` if the two conflict. Kept below the engine's 60 s idle window on purpose, so the mechanism does not depend on the watchdog pause being correct — it merely benefits from it. |
1144
+ | `retry.multiplier` | `2` | Growth factor, clamped to `[1, 5]`. The one **float** key in this section: `2.5` is a legitimate factor and is stored as typed. |
1145
+ | `retry.jitter` | `true` | Equal jitter (`d/2 + rand·d/2`), which de-correlates concurrent subagents hitting the same 429 while keeping the announced countdown within a factor of two of the truth. Full jitter can fire 40 ms after announcing 30 s, which reads as a bug. |
1146
+ | `retry.respectRetryAfter` | `true` | Honour `Retry-After` (and the provider rate-limit reset headers) as a **floor**, never a replacement — a `Retry-After: 1` on the ninth retry does not undo eight retries of backoff. A stated wait above 60 s is surfaced immediately instead of waited out. |
1147
+ | `retry.maxElapsedMs` | `240000` | Wall-clock budget from the first failure, clamped to `[10000, 1800000]`. Deliberately **below** `team.subagentTimeoutMs`: an equal budget means a subagent is killed at the same instant its ladder ends, and the user is then told "subagent timed out" for what was a provider outage. |
1148
+ | `retry.onPartialStream` | `true` | Allow a restart after content has already streamed to the transcript. The view is rewound and the discarded tool cards removed; engine history is provably clean at that point, because it is only written on a completed turn. `false` refuses the whole path. |
1149
+ | `skills.enabled` | `true` | Master switch for the skill system. |
1150
+ | `skills.requireApproval` | `true` | Require a human to approve `skill_install` / `skill_create`. |
1151
+ | `skills.catalogMaxBytes` | `6000` | Level 1 catalog ceiling, in UTF-8 **bytes**, clamped to `[500, 40000]`. |
1152
+ | `skills.bodyMaxBytes` | `30000` | Level 2 body ceiling, in UTF-8 **bytes**, clamped to `[1000, 50000]`. |
1153
+ | `skills.allowedHosts` | GitHub / GitLab hosts | Hosts an install **or update** may fetch from. Re-checked on every update, not just at install time. |
1154
+ | `skills.disabled` | `[]` | Skill names switched off. |
1155
+ | `skills.trustedProjectDirs` | `[]` | Project skill directories you have trusted. |
1156
+ | `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. |
1157
+ | `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`. |
1158
+ | `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. |
1159
+ | `log.level` | `info` | `silent` · `error` · `warn` · `info` · `debug` · `trace`. See [Files & logs](#files--logs) for what each level records. |
1160
+ | `log.toFile` | `true` | Write records to a file at all. `false` here is respected; `--log-file` overrides it for one run. |
1161
+ | `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. |
1162
+ | `log.maxFileBytes` | `5242880` | Rotate once a file would exceed this, clamped to `[65536, 268435456]`. |
1163
+ | `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. |
1164
+ | `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. |
1165
+ | `log.previewChars` | `512` | How much user content `debug` keeps per field, clamped to `[0, 8192]`. `trace` is never truncated. |
1166
+
1167
+ Byte, not character: `ToolExecutor` truncates at 100 000 **bytes**, so a
1168
+ character-based budget would be roughly triple its nominal size for CJK text
1169
+ and would get cut mid-tag.
1170
+
1171
+ ### Output token limits
1172
+
1173
+ Every model and every provider defaults to **64000** output tokens. You never
1174
+ have to configure this, and if you never touch it the number stays 64000.
1175
+
1176
+ The number you set is an **ambition, not a promise**: it is clamped down to
1177
+ whatever the target model actually accepts. `gpt-4o` caps at 16384 and
1178
+ `gemini-1.5-pro` at 8192, and sending 64000 to either is an HTTP 400 that ends
1179
+ the turn — so `aragon` sends what the model will take, silently. When the model
1180
+ is one it has never seen (a proxy, an endpoint behind `--base-url`, a model that
1181
+ shipped last week), it sends 64000, and if the provider objects it reads the real
1182
+ ceiling out of the provider's own error, repairs the request, sends it again, and
1183
+ remembers the answer for the rest of the session. You see one warning line and
1184
+ the run continues.
1185
+
1186
+ Four ways to change it, highest priority first:
1187
+
1188
+ ```bash
1189
+ aragon --max-tokens 32000 # this run only
1190
+ aragon --max-tokens auto # this run only, per-model ceiling
1191
+ ARAGON_MAX_TOKENS=32000 aragon # env layer, between the flag and the file
1192
+ aragon config set maxTokens 32000 # persisted; prints "Set maxTokens = 32000"
1193
+ aragon config set maxTokens auto # persisted as null
1194
+ aragon config set maxTokens default # back to 64000
1195
+ aragon config get maxTokens # "64000" | "auto"
1196
+ ```
1197
+
1198
+ Inside the TUI, `/max-tokens` reports the current setting **and** the effective
1199
+ cap for the live model, and `/max-tokens <n|auto|default>` changes it. The
1200
+ settings screen (`/settings`) has the same field with an `Effective:` line under
1201
+ it, so a value the model will not honour is visible before you save it.
1202
+
1203
+ And by hand, in `~/.aragon-agent/config.json` (`aragon config path` prints the
1204
+ exact location; `aragon config edit` opens it, and `/reload` picks up an edit
1205
+ made in another window):
1206
+
1207
+ ```jsonc
1208
+ {
1209
+ "version": 1,
1210
+ "provider": "anthropic",
1211
+ "model": "claude-sonnet-4-5-20250929",
1212
+
1213
+ // Output token cap.
1214
+ // 64000 (or any 256..200000 integer) — an explicit cap, clamped down to
1215
+ // the target model's real ceiling when that ceiling is lower.
1216
+ // null — auto: use each model's own ceiling, never above 64000.
1217
+ // absent — the product default, 64000.
1218
+ "maxTokens": 64000
1219
+ }
1220
+ ```
1221
+
1222
+ Out-of-range numbers are clamped on the way in **and** on the way out, so a
1223
+ hand-edited `900000` becomes `200000` on disk rather than reverting to the
1224
+ default on every launch.
1225
+
1226
+ Extended thinking is reconciled with the cap automatically: `--thinking xhigh`
1227
+ asks for a 65536-token budget, Anthropic requires `max_tokens` to be strictly
1228
+ greater than it, and the two are resolved together rather than left to collide.
1229
+
1230
+ These are additive: a config file written by an earlier version loads
1231
+ unchanged, with no migration and no version bump.
1232
+
1233
+ ## Auto-update
1234
+
1235
+ `aragon` checks npm for a newer release in the background, installs it silently
1236
+ into the same global installation you already have, and tells you on one line
1237
+ above the composer:
1238
+
1239
+ ```
1240
+ * 0.6.0 installed - restart aragon to apply
1241
+ ```
1242
+
1243
+ **The running session is never touched.** The new version lands on disk and
1244
+ takes effect the next time you start `aragon`. A restart in the middle of a turn
1245
+ would destroy a live run, a subagent dispatch and an open plan review, so the
1246
+ line waits and you decide when. Nothing else about the check is visible: it
1247
+ happens at most once every four hours, at most once per machine per interval,
1248
+ and every failure is silent. Only after three consecutive failures does the line
1249
+ say anything, and then it says something you can act on.
1250
+
1251
+ **It only ever installs for one kind of installation**: a global `npm i -g`,
1252
+ where nothing else claims to own the version. If `aragon` came from pnpm, yarn,
1253
+ bun or volta it will not run a command on your behalf — it prints the right one
1254
+ for your manager and stops. If it came from `npx`, from a project's
1255
+ `node_modules`, or from a clone of this repo it says **nothing at all**: an
1256
+ `npx` run resolved `latest` seconds ago, a project's manifest deliberately pins
1257
+ the version, and a checkout is not out of date. A global directory it cannot
1258
+ write to (the usual Linux case) is detected before anything is spawned.
1259
+
1260
+ ### The four keys
1261
+
1262
+ | Key | Default | Meaning |
1263
+ | --- | --- | --- |
1264
+ | `update.mode` | `auto` | `auto` installs · `notify` only reports · `off` disables the subsystem entirely |
1265
+ | `update.checkIntervalMs` | `14400000` (4 h) | Minimum time between registry checks, across every `aragon` on this machine |
1266
+ | `update.registry` | `''` | Registry base URL. Empty derives it: `ARAGON_UPDATE_REGISTRY`, then `npm_config_registry`, then npmjs |
1267
+ | `update.distTag` | `latest` | The dist-tag to track |
1268
+
1269
+ ```bash
1270
+ aragon config set update.mode notify # tell me, do not install
1271
+ aragon config set update.mode off # never check at all
1272
+ ARAGON_UPDATE=0 aragon # off for this run
1273
+ aragon --no-update # the same, as a flag
1274
+ aragon update --check # ask right now, install nothing
1275
+ aragon update # ask right now, install if eligible
1276
+ aragon update --to 0.5.8 # go back to a specific version
1277
+ aragon update --rollback # undo the last auto-update
1278
+ ```
1279
+
1280
+ `aragon update` is also the answer for headless users: the background updater is
1281
+ only ever constructed for an interactive session, so `aragon -p "…"`,
1282
+ `aragon config set`, CI (any truthy `CI`) and any non-TTY execute **zero** lines
1283
+ of update code.
1284
+
1285
+ ### If an update breaks the CLI
1286
+
1287
+ The failure an auto-updater has to answer for is the one where the version it
1288
+ installed will not start — an undeclared dependency, a Node API that moved, a
1289
+ syntax error past the transpile target. None of those reach any line inside
1290
+ `main()`, so no amount of care there helps.
1291
+
1292
+ So the `aragon` command is a small launcher that runs **before** the CLI itself.
1293
+ It reads one file, and on almost every launch that is all it does. But if the
1294
+ version it is about to start is one **this updater installed**, and that version
1295
+ has already exited non-zero twice, it reinstalls the version you were running
1296
+ before the update and marks the bad one so nothing puts it back:
1297
+
1298
+ ```
1299
+ aragon 0.6.0 failed to start twice; rolling back to 0.5.9.
1300
+ ```
1301
+
1302
+ It boots anyway — if the crash was transient, refusing to start would lock you
1303
+ out on a guess. The next session tells you once what happened, and then stops
1304
+ mentioning it. A **newer** release clears the mark and installs normally: you
1305
+ were never opted out of updates, only out of the loop.
1306
+
1307
+ Three things this deliberately does not do. It never touches a version **you**
1308
+ installed yourself — `npm i -g @aragon-agent/cli@something` is your decision, and
1309
+ an updater that silently reverts it is a worse actor than one that does nothing.
1310
+ It does not roll back a release that starts and is merely *wrong*; that is not
1311
+ detectable from inside, and a heuristic that guessed would eventually revert a
1312
+ working version on a bad day. Use `aragon update --rollback` for that — it is the
1313
+ same mechanism, run deliberately. And **`update.mode: off` disables this too**:
1314
+ the guard is armed only by an auto-install, so a machine that never auto-installs
1315
+ never arms it. That is the honest reading of the kill switch — it turns off the
1316
+ whole subsystem, recovery included.
1317
+
1318
+ ### Behind a proxy
1319
+
1320
+ Node's own `fetch` ignores `HTTP_PROXY`, so on a proxied machine the direct check
1321
+ simply fails. When it does, `aragon` asks **the npm you already have** instead,
1322
+ which honours `proxy`, `https-proxy` and your `.npmrc`. It runs only after the
1323
+ direct request has already failed, so on an ordinary machine it never runs at
1324
+ all. `/update status` shows `probe: npm` when this is what answered.
1325
+
1326
+ No credentials are read or forwarded by either path — we run the command you
1327
+ could have typed and read one version string out of its JSON.
1328
+
1329
+ ### The trade this makes
1330
+
1331
+ Auto-update widens the window in which a compromised release could reach you
1332
+ without a human deciding to fetch it. That is a real cost, and it is worth
1333
+ stating plainly rather than burying:
1334
+
1335
+ - npm verifies each package against the registry's `dist.integrity` SRI hash, so
1336
+ what is installed is what was published. That protects the transport, not the
1337
+ publisher.
1338
+ - Only the `latest` dist-tag is tracked, prereleases are never installed onto a
1339
+ stable version, and a release its own author has `npm deprecate`d is skipped.
1340
+ - `update.mode: off` and `ARAGON_UPDATE=0` are kill switches, and they are
1341
+ documented rather than incidental.
1342
+ - Organisations that want a review gate point `update.registry` at a mirror they
1343
+ control.
1344
+
1345
+ No credentials are ever sent with the check. The updater does not read
1346
+ `_authToken` out of your `.npmrc` — an updater that attached a token to a
1347
+ user-configurable URL would be a credential-exfiltration primitive the moment
1348
+ that URL was mis-set. Private-registry users should point `update.registry` at a
1349
+ mirror they can read anonymously, or turn the feature off.
1350
+
1351
+ ## Files & logs
1352
+
1353
+ Everything AragonAgent keeps for you lives under one directory in your home:
1354
+
1355
+ ```
1356
+ ~/.aragon-agent/ (Windows: C:\Users\<you>\.aragon-agent\)
1357
+ ├── config.json settings + API keys (0600 on POSIX)
1358
+ ├── config.json.bak written by `aragon config edit` before it opens
1359
+ ├── prompt-history.jsonl prompts you submitted, for ↑ recall (0600 on POSIX)
1360
+ ├── state.json UI bookkeeping: submit count, one-shot notices
1361
+ ├── update-state.json auto-update: last check, pending restart, skipped version,
1362
+ │ and the boot guard's rollback bookkeeping
1363
+ ├── logs/
1364
+ │ ├── aragon-2026-07-27.log today, JSON Lines
1365
+ │ └── aragon-2026-07-27.1.log rotated at log.maxFileBytes
1366
+ ├── sessions/ /save and /resume
1367
+ ├── skills/ aragon skills install
1368
+ └── skill-usage.json
1369
+ ```
1370
+
1371
+ `config.json` holds **settings only**. What you typed and how often you have
1372
+ typed it live in the two files next to it, so that editing your config means
1373
+ scrolling past your own settings and nothing else, and so that submitting a
1374
+ prompt no longer rewrites the file that holds your API keys.
1375
+
1376
+ `ARAGON_HOME` moves the whole tree. **It cannot come from a `.env` file** — the
1377
+ path is resolved when the process starts, long before a `.env` is read — so set
1378
+ it as a real environment variable. `aragon config home` always prints the
1379
+ directory actually in use.
1380
+
1381
+ Upgrading from 0.5.0 or earlier moves your config, sessions and skills here
1382
+ automatically, once, and prints a line on stderr when it does. The old
1383
+ `%APPDATA%\aragon-agent-nodejs` / `%LOCALAPPDATA%\aragon-agent-nodejs`
1384
+ directories are **left in place** so that reinstalling an older build stays
1385
+ lossless; `aragon config home` names them, and you can delete them once you are
1386
+ satisfied.
1387
+
1388
+ ### Prompt history
1389
+
1390
+ Every prompt you submit is appended to `~/.aragon-agent/prompt-history.jsonl`,
1391
+ one JSON object per line, so that `↑` recalls it — in this session and in every
1392
+ later one. It is written `0600` on POSIX, it never leaves the machine, and it is
1393
+ **stored verbatim**: a redacted history would recall the wrong text, which is
1394
+ worse than not recalling at all.
1395
+
1396
+ The last 100 distinct prompts are what `↑` walks. The file is rewritten down to
1397
+ that many entries once it passes 400 lines, and a single submission longer than
1398
+ 16 KiB is not recorded at all rather than recorded truncated.
1399
+
1400
+ ```bash
1401
+ aragon history path # where it is, plus a reminder of what is in it
1402
+ aragon history list -n 50 # the 50 most recent, newest first
1403
+ aragon history list --json # raw entries with timestamps
1404
+ aragon history clear --yes # delete the file
1405
+ ```
1406
+
1407
+ Set `historyEnabled` to `false` (`aragon config set historyEnabled false`) to
1408
+ stop recording. Switching it off does **not** delete what is already stored —
1409
+ `aragon history clear --yes` does that — and reading keeps working either way,
1410
+ so you can look before you erase.
1411
+
1412
+ Upgrading from 0.5.x moves an existing `promptHistory` out of `config.json` into
1413
+ this file once, automatically and silently.
1414
+
1415
+ ### What gets logged
1416
+
1417
+ Logs are local files. Nothing is uploaded, ever — there is no network path out
1418
+ of this subsystem at all.
1419
+
1420
+ | Level | Records | Contains your content? |
1421
+ | --- | --- | --- |
1422
+ | `error` | crashes, failed requests, config write/parse failures | no |
1423
+ | `warn` | degraded migrations, clamped values, dropped records | no |
1424
+ | `info` *(default)* | start/exit, config writes, turn boundaries with token counts, tool names and durations | **no** |
1425
+ | `debug` | the above, plus prompts, replies and tool arguments truncated to `log.previewChars` | yes, truncated |
1426
+ | `trace` | full message bodies and full tool output | yes, in full |
1427
+
1428
+ The default is `info` specifically so that attaching a log to a bug report is
1429
+ safe. **`debug` and `trace` write what you typed and what the model replied** —
1430
+ check before sharing one.
1431
+
1432
+ API keys are removed at every level, by two independent passes: any field whose
1433
+ name looks like a credential is blanked whatever it holds, and every string is
1434
+ scanned for known key formats *and* for the literal keys this process is
1435
+ holding. The second half is what covers a custom endpoint whose key looks like
1436
+ nothing in particular. Setting `log.redactSecrets false` disables both, and says
1437
+ so on stdout when you do.
1438
+
1439
+ ### Commands
1440
+
1441
+ ```bash
1442
+ aragon logs path # where records are going right now, and at what level
1443
+ aragon logs list # files, sizes, timestamps
1444
+ aragon logs tail -n 50 # render the last 50 records
1445
+ aragon logs tail --follow # keep printing new ones
1446
+ aragon logs tail --json # raw JSON Lines, for jq
1447
+ aragon logs tail --level warn # only warn and above
1448
+ aragon logs clear --yes # delete every log file
1449
+ aragon logs open # open the directory in your file manager
1450
+
1451
+ aragon history path # where prompt history is kept
1452
+ aragon history list -n 50 # the 50 most recent prompts, newest first
1453
+ aragon history clear --yes # delete the prompt history
1454
+
1455
+ aragon config home # the user-state root (and whether ARAGON_HOME set it)
1456
+ aragon config get log.level # one value; secrets always masked
1457
+ aragon config list --json # everything; secrets always masked here too
1458
+ aragon config edit # open config.json in $VISUAL / $EDITOR
1459
+ ```
1460
+
1461
+ Per-run overrides, none of which are persisted:
1462
+
1463
+ ```bash
1464
+ aragon --log-level debug "…" # or --verbose, which means the same thing
1465
+ aragon --no-log-file "…" # nothing reaches a file this run
1466
+ aragon --log-dir D:\logs "…"
1467
+ ```
1468
+
1469
+ `ARAGON_LOG_LEVEL`, `ARAGON_LOG_FILE=0` and `ARAGON_LOG_DIR` do the same from the
1470
+ environment, and unlike `ARAGON_HOME` these three *can* come from a `.env`.
1471
+
1472
+ ### Insecure TLS opt-in
1473
+
1474
+ When `NODE_TLS_REJECT_UNAUTHORIZED` is the exact string `0`, the installed or
1475
+ locally built `aragon` process honors that setting and suppresses only Node's
1476
+ standard insecure-TLS warning. Every unrelated process warning remains visible,
1477
+ and any other value leaves warning behavior unchanged. The check happens when a
1478
+ warning is emitted, so a `0` loaded from the project `.env` is covered too; the
1479
+ CLI never creates, normalizes, or persists this variable.
1480
+
1481
+ > **Security warning:** `0` genuinely disables certificate verification. It does
1482
+ > not make an insecure connection safe and can expose credentials or request
1483
+ > contents to interception. Use it only in a controlled environment where you
1484
+ > accept that risk.
1485
+
1486
+ The automatic filter starts with the `aragon` process. An earlier `npm install`
1487
+ or `npx` bootstrap belongs to npm and is outside the installed package's process
1488
+ boundary, so that parent may still print Node's warning. The official repository
1489
+ release flow covers its npm children through `publish-latest.ps1`; callers that
1490
+ own another npm/npx parent must preload the filter in that parent themselves if
1491
+ quiet output is required.
1492
+
1493
+ ### Theming & color
1494
+
1495
+ `--theme` / `config.theme` / `/theme` pick a palette: `warm`, `cool`, `light`, or
1496
+ `auto` (which resolves to `warm` — terminals can't reliably report their
1497
+ background). `dark` remains accepted everywhere as an alias for `cool`.
1498
+ Colors degrade automatically to your terminal's depth (truecolor → 256 → 16 →
1499
+ monochrome) and glyphs fall back to ASCII on terminals without Unicode. Color is
1500
+ disabled — and the UI renders plain monochrome with ASCII glyphs — when any of
1501
+ `NO_COLOR`, `--no-color`, `TERM=dumb`, or `config.color=false` is set; `FORCE_COLOR`
1502
+ is respected. `reducedMotion` (also `ARAGON_REDUCED_MOTION`, and implied by
1503
+ `--no-color`) replaces spinners with a static glyph.
1504
+
1505
+ ### Timeout invariant
1506
+
1507
+ The core idle watchdog (60 s by default) is **shorter** than the tool timeout
1508
+ (120 s), so a long single tool run (a build, `npm install`) would be killed
1509
+ mid-run. The CLI fixes this by always constructing the agent with
1510
+ `idleTimeout ≥ toolTimeout` (defaults 210 s ≥ 180 s), re-derived after config
1511
+ merge. Raise both `--tool-timeout` / `--idle-timeout` for genuinely long jobs.
1512
+
1513
+ ## Development
1514
+
1515
+ ```bash
1516
+ npm run build -w packages/cli # tsc + shebang → dist/cli.js
1517
+ npm test -w packages/cli # vitest
1518
+ node packages/cli/dist/cli.js --version
1519
+ ```
1520
+
1521
+ ### Manual smoke checklist
1522
+
1523
+ - `npm run build -w packages/cli` produces an executable `dist/cli.js` with a
1524
+ shebang; `node packages/cli/dist/cli.js --version` prints the version.
1525
+ - `npm pack -w packages/cli --dry-run --json` lists `dist/cli.js` under `files`.
1526
+ - With `ANTHROPIC_API_KEY` set: `aragon` streams a reply, a tool card renders,
1527
+ `Esc` aborts mid-run, `/model` switches models, `/settings` saves a key,
1528
+ `Ctrl+C` twice exits.
1529
+ - `echo "list files" | node dist/cli.js -p` prints an answer and exits `0`.
1530
+ - A missing/invalid key produces a visible, actionable message (never a blank
1531
+ screen), both in the TUI and headless.
1532
+ - `Shift+Tab` at 40, 80 and 120 columns - the chip, the hint and the status bar
1533
+ are all legible and nothing wraps onto a second row.
1534
+ - The question wizard at 24 rows with 5 questions x 4 options - no clipping, and
1535
+ the `n/N` counter matches what is on screen.
1536
+ - Approve a plan and watch the SAME run start writing files, with no second
1537
+ prompt.
1538
+ - `--no-color` on a legacy `cmd.exe` - the chip renders `[PLAN]`, radios render
1539
+ `*`/`o`, checkboxes render `[x]`/`[ ]`, no mojibake anywhere.
1540
+ - A plan with a 600-character summary and a 400-character step detail - the text
1541
+ wraps and scrolls, the footer count matches the real number of rows, and
1542
+ nothing is truncated to a single line.
1543
+ - `--no-hints` on a 20-row terminal in plan mode - the status bar still says
1544
+ `PLAN` with no chip on screen.
1545
+ - Dismiss a plan card with `Esc`, let the agent resubmit, then press `Esc` twice:
1546
+ the run aborts and the session returns to an idle prompt. Do this one BY HAND
1547
+ as well as in a test - the point of the check is whether the exit is findable,
1548
+ which no assertion can tell you.
1549
+
1550
+ ## License
1551
+
1552
+ MIT