@aragon-agent/cli
v0.5.12
Published
AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.
Maintainers
Readme
@aragon-agent/cli
A Claude-Code / Codex-style interactive terminal UI for the
@aragon-agent/core engine. Type a task, watch the agent think and
stream its answer, see each tool call render as a live card, and interrupt /
steer / configure the session — all without leaving the terminal.
⚠️ Full permission, no sandbox. This first version runs at maximum permission:
bashexecutes directly, file writes hit the real filesystem, and there is no per-action approval gate by default. Only run it in workspaces you trust. An opt-in confirmation mode is available via--confirm.
Install
# Global
npm i -g @aragon-agent/cli
aragon
# Zero-install
npx @aragon-agent/cliRelease note (R3):
npx @aragon-agent/cliresolves@aragon-agent/corefrom the npm registry, so@aragon-agent/core@^0.1.0must be published first (or bundled into the CLIdist). During local development inside this monorepo, npm workspaces link the localpackages/coreautomatically.
Quick start
# Set a key (any one of these)
export ANTHROPIC_API_KEY=sk-ant-...
# ...or paste it live in the TUI via /settings, or pass --api-key
aragon # interactive TUI
aragon "summarize README.md" # interactive, auto-submitting the prompt
echo "list files" | aragon -p # one-shot, prints to stdout then exitsUsage
aragon [prompt] Start the interactive TUI (or one-shot with -p / piped stdin)
aragon -p, --print [prompt] Headless: stream the answer to stdout, then exit
aragon config Open the settings screen
aragon config set <key> <value> Non-interactive config write (e.g. model, provider)
aragon config path Print the config file path
aragon models [--provider p] List builtin + discovered models
aragon --version | -v Print the version
aragon --help | -h Print helpGlobal flags
| Flag | Meaning |
| --- | --- |
| --provider <id> | anthropic | openai | google |
| --model <id> | Model id |
| --base-url <url> | Override the provider base URL (OpenAI-compatible endpoints) |
| --api-key <key> | One-shot key override (not persisted) |
| --thinking <level> | off\|minimal\|low\|medium\|high\|xhigh |
| --max-tokens <n\|auto> | Output token cap: a number, or auto for the model's own ceiling (default 64000) |
| --cwd <dir> | Working directory for tools |
| --confirm | Confirm each mutating tool call |
| --tool-timeout <ms> | Per-tool executor ceiling (default 180000) |
| --idle-timeout <ms> | Watchdog idle timeout (auto-raised to ≥ tool-timeout + 30s) |
| --theme <name> | auto\|warm\|cool\|light (dark is a compatibility alias for cool) |
| --compact / --no-compact | Transcript density: no blank rows between turns, or the default |
| --hints / --no-hints | Show or hide the composer hint row |
| --show-thinking / --no-show-thinking | Draw the model's reasoning blocks in the transcript. Off by default — see What you see while it runs |
| --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 |
| --fullscreen | Force the full-screen TUI, overriding the automatic downgrades |
| --no-fullscreen | Force the inline renderer (the 0.2.0 behavior) |
| --no-exit-transcript | Do not replay the session summary after exiting |
| --plan / --no-plan | Start the session in PLAN mode (read-only research + review) or force BUILD |
| --team / --no-team | Enable or disable team subagents. --no-team does not register the task tool at all |
| --team-max <n> | Max subagents per dispatch for this run, clamped to [1, 10] |
| --todo / --no-todo | Enable or disable todo planning. --no-todo does not register the todo_write tool at all |
| --todo-panel / --no-todo-panel | Show or hide the right-hand todo rail. The tool stays registered either way |
| --todo-follow <mode> | What happens when a run ends with steps left: notify (default), auto, off |
| --retry / --no-retry | Retry a failed provider call with backoff (the default), or fail on the first error |
| --retry-max <n> | Retries after the first attempt for this run, clamped to [0, 20]. 0 turns retry off |
| --fast / --no-fast | Enable or disable the fast model tier. Off by default; see Fast model tier |
| --fast-model <id> | The fast model id. Implies nothing about --fast on its own |
| --fast-provider <id> | Provider for the fast model. Absent inherits the main provider |
| --fast-review <n\|off> | Turns between automatic fast reviews, or off |
| --no-skills | Disable the skill system entirely (no catalog, no skill tools) |
| --skill <name> | Force-load a skill's full body for this run (repeatable) |
| --skills-yes | Approve skill installs for this run (CI / headless) |
| --transcript-retain <n> | Entries kept in memory, clamped to [200, 20000]. Older ones are dropped and counted |
| --render-governor / --no-render-governor | Adapt the frame interval under load, or pin it at 33 ms |
| --max-render-interval <ms> | Governor ceiling, clamped to [33, 1000]. 33 flattens the ladder |
| --no-color | Disable ANSI color |
| --quiet | (print mode) suppress tool/usage lines on stderr |
Exit codes: 0 success · 1 agent/runtime error · 2 config/usage error ·
130 interrupted.
Slash commands (interactive)
| Command | Action |
| --- | --- |
| /help | Keybindings + command help |
| /model | Open the model picker |
| /settings | Open the settings screen |
| /thinking <level> | Set the thinking level |
| /max-tokens [<n>\|auto\|default] | Output token cap; with no argument, reports the setting and the effective cap for the live model |
| /tools | List active tools |
| /clear | Clear the visible transcript and the todo panel |
| /reset | New conversation |
| /cwd [dir] | Show / change the tool working directory |
| /save [file] | Save the session to JSON |
| /resume [file] | Load a saved session |
| /plan [on\|off\|status] | Toggle plan mode - the keyboard-free equivalent of Shift+Tab |
| /team [on\|off\|max <n>] | Team subagents: status and the live roster, the on/off switch, the fan-out width |
| /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 |
| /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 |
| /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 |
| /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 |
| /theme <auto\|warm\|cool\|light> | Switch the color theme live (persisted) |
| /expand [n] | Expand / collapse the n-th-from-last (default last) tool card |
| /copy | Copy the last answer to the clipboard |
| /perf | What the renderer is doing: frame interval, mounted entries, dropped entries, cache occupancy. /perf reset clears the caches and the governor |
| /logs | Show where this session is being logged, and at what level |
| /reload | Re-read config.json after editing it in another window (refused mid-run) |
| /skills [sub] | List and manage skills (see below) |
| /<skill-name> [args] | Run an installed skill directly |
| /exit (/quit) | Exit |
A leading / meant literally can be escaped as //. Typing / opens a
command palette and @ opens a file-path completion popup — Tab / →
completes, Up / Down moves the selection, Esc closes it.
Keybindings
| Key | Action |
| --- | --- |
| Enter | Submit (idle) / queue a steering message (running) |
| Alt+Enter / Shift+Enter | Insert a newline |
| Shift+Tab | Toggle plan mode (BUILD <-> PLAN). Equivalent: /plan |
| Esc | Abort the run / close an overlay / close a popup |
| Ctrl+C ×2 | Exit (first press warns) |
| Ctrl+L | Redraw the frame (clear the screen in inline mode) |
| 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 |
| Ctrl+O | Expand / collapse the most recent tool card — including its diff |
| PgUp / PgDn | Scroll the transcript a page (full-screen mode) |
| Shift+↑ / Shift+↓ | Scroll the transcript a line (full-screen mode) |
| Mouse wheel | Scroll the transcript (or the open overlay) |
| Shift+wheel | Scroll the transcript a page |
| Home / End, Ctrl+A / Ctrl+E | Cursor to line start / end |
| Alt/Ctrl+← / → | Word-wise cursor jump |
| Ctrl+W, Alt+Backspace | Delete the previous word |
| Ctrl+U / Ctrl+K | Kill to line start / end |
| Up / Down | Move between draft lines; recall prompt history at the edges |
| ? | Open help (empty input) |
In full-screen mode the transcript is scrolled by the app itself: PgUp /
PgDn and Shift+↑ / Shift+↓. While pinned to the bottom the viewport follows
new output automatically; once you scroll away the status bar shows ↑N, a slim
rail on the right edge shows where you are, and a hint counts the lines below
you. Submitting a message always re-pins to the newest output. In inline mode
(--no-fullscreen) history lives in your terminal's native scrollback, printed
once via Ink <Static>.
The mouse wheel is a viewport gesture and nothing else. Wherever the pointer
happens to be — including right on top of the composer — a notch scrolls the
transcript three rows (a page with Shift), or scrolls the overlay when one is
open. It never touches your draft, and it never recalls prompt history: that is
↑ / ↓, and only ↑ / ↓.
Two things follow from the app owning the mouse, and they are worth knowing before you meet them:
- Selecting text now needs
Shift+drag in most terminals. That is the terminal's convention for "an application is tracking the mouse", not something this CLI can opt out of while still reading wheel events. tmuxwithmouse onkeeps the wheel for itself, so the app never sees it.set -g mouse offhands it back.
Turn the whole thing off with --no-mouse, ARAGON_MOUSE=0, or
aragon config set mouse false. With mouse support off the wheel simply does
nothing in full-screen mode — it does not fall back to editing your draft, which
is what it used to do. Inline mode is unaffected either way.
Themes
Three palettes plus auto:
| Name | Look |
| --- | --- |
| warm | Terracotta + amber over warm neutrals. The default (auto resolves here). |
| cool | The blue/violet palette that shipped as dark through 0.3.x. |
| light | For light terminal backgrounds. |
auto never guesses light: terminals cannot report their background reliably,
and guessing wrong makes the app unreadable rather than merely wrong-looking.
Migrating from 0.3.x — dark was renamed cool when warm became the
default, and the old name still works everywhere: --theme dark,
aragon config set theme dark, /theme dark, and a config file containing
"theme": "dark" all resolve to cool, so a screen you explicitly chose does
not change under you. The value is rewritten as cool the next time the config
is saved. Only users who never picked a theme see the new default.
Density and hints are settable the same three ways: --compact / --no-hints
for one run, aragon config set density compact / aragon config set hints false
to persist. The composer hint row also shortens to ? help on its own after a
few sessions — except while a run is in progress, when it always spells out
esc abort in full.
What you see while it runs
Four rules, and each of them exists because the opposite was worse.
Reasoning is not shown. A model at thinkingLevel: high emits more
chain-of-thought than answer, and it used to scroll the answer and the tool calls
off the top of the screen. It is written for the model, not for you. So it is off
by default, and a settled turn that thought leaves one muted row in its place:
✱ thought for 12s · ctrl+t to showThat row is the whole point — hiding information without saying that it exists
is how a "clean" UI becomes a dishonest one. Ctrl+T shows the bodies for the
session, showThinking: true (or --show-thinking, or ARAGON_SHOW_THINKING=1)
shows them permanently, and /settings has a Show thinking row next to
Thinking.
In inline mode (--no-fullscreen) the marker reads ✱ thought for 12s with
no key hint, and the toast on Ctrl+T says Thinking shown for new output.
Settled entries there have already been printed into your terminal's own
scrollback, which cannot be repainted — so the key applies to the live entry and
to everything drawn after it, and the row does not offer what it cannot deliver.
There is something to look at while it thinks. One row above the composer, for as long as a run is in flight:
⠋ Percolating…The word rotates every four seconds. It carries no clock and no token count on
purpose: the status bar one row below already has both, under the same
condition, driven by the same ticker. Under reducedMotion the spinner is a
static glyph and the word is chosen once and holds — rotating text is motion too.
An idle frame is exactly what it was before this existed, down to the row count.
While a tool is in flight the row stops guessing and names it instead:
⠙ Running bashrunning covers the whole turn, including the minutes the model is idle and a
child process is doing the work. "Pondering" is right for the gap before the
first token and simply untrue during a build.
A long command is not a blank card. bash is the only tool here that can run
for minutes — npm test, a git clone, a training job — and while it ran, its
card used to be one line of text that never changed. Now it shows the last eight
lines of what the command is printing, plus one honest footer row:
bash npm test ⠹ running
│ ✓ src/__tests__/patch.test.ts (49 tests)
│ ✓ src/__tests__/diff-view.test.tsx (9 tests)
│ (running)and, when the child goes quiet, no output for 45s instead of a spinner that
means nothing. A \r-driven progress bar — npm, pip, curl, docker — is one
line rewritten a thousand times, and it renders as one row counting up in place,
not a thousand rows of nearly the same string.
What the tail deliberately does not do. It is a display, not a transcript.
ANSI colour, cursor motion and every other control byte are stripped, not
interpreted (keeping colour would mean validating it, and an unterminated escape
leaks into the rest of the frame; interpreting cursor motion would mean owning a
screen buffer). It keeps eight rows, so Ctrl+O does nothing while a command is
running — expansion is a promise about stored content, and there is no stored
rest of a 400 MB stream to show. It is not saved: /save during a run writes no
tail, because a resumed one would describe a process that died with the session.
And it never reaches the model — at settle the card is replaced by the
authoritative result, exactly as before. Set liveToolOutput: false (or
--no-live-tool-output, or ARAGON_LIVE_TOOL_OUTPUT=0, or the Live output
row in /settings) and the card is a single running row again, with no store
allocated and no recorder attached.
A file modification looks like a file modification. write_file and
edit_file now produce a real diff — hunks, line numbers, +N -M:
+6 -5
1163 top level of the file where the section never takes effect. It
1164 - `applyFastConfigSet` (`:259`). A key missing from the list is
1164 + `applyFastConfigSet` (`:252`). A key missing from the list is
1165 `Unknown config key`; a key in the list but missing from the switch
+13 lines (Ctrl+O)Creating a 300-line file is the single most consequential thing an agent does,
and it used to be reported as a byte count. Diffs collapse at 12 rows behind the
existing Ctrl+O; there is no new keybinding.
This costs the model nothing — it saves. The structured patch reaches the
screen on a CLI-local channel, not through the tool result, and the text the
model reads gained a 4 000-character ceiling it did not have before. For a
single-region edit that text is what it always was plus one @@ header; for a
multi-region edit it is strictly smaller, because a change at line 40 and one at
line 900 are now two small hunks instead of 860 removed rows followed by 860
near-identical added ones.
Full-screen mode
aragon takes over the terminal's alternate screen buffer — the mechanism
vim, htop, and lazygit use. Two consequences worth knowing:
- Your previous shell output is covered, not erased, and returns untouched when you exit. Nothing in your scrollback is destroyed.
- The frame is fixed at
rows - 1tall, which is what keeps the composer and the status bar at the bottom of the screen even on an empty session.
On exit the session is replayed into the normal buffer as plain text so the
conversation survives leaving the screen (--no-exit-transcript opts out;
/save still exports the full JSON).
It downgrades to the inline renderer automatically when stdout is not a TTY,
TERM=dumb, a CI environment variable is set, or the terminal is under 12 rows
or 40 columns. --fullscreen overrides all of those except the non-TTY check —
writing screen-control sequences into a pipe or a redirected file is never safe.
--no-fullscreen, ARAGON_FULLSCREEN=0, or aragon config set fullscreen false
opt out permanently.
The terminal's native scrollback and mouse wheel do not scroll the transcript in
this mode, and mouse tracking is deliberately left off because enabling it costs
text selection and copy in most terminals. If a crash ever strands your terminal
on the alternate screen, reset restores it.
Render performance
A long session used to get slower and slower, and past a point it stopped
responding at all. The cause was structural rather than incidental: Ink has no
output caching, so every frame re-walked the whole mounted tree, re-measured
every text node and re-serialised the frame. The cost was therefore proportional
to every character the session had ever produced, not to the characters
actually on screen — and transcriptWindow bounded the entry count, which is
not the same thing when one entry can be fifty thousand lines.
Four things changed, and you should not have to think about any of them:
- Off-screen entries are not mounted. The viewport renders the band you can
see plus a couple of entries either side, and replaces the rest with two
spacers of the right height. Scrolling and the
↑Nreadout are unchanged. - The expensive pure work is cached. Syntax highlighting and markdown
parsing now run once per distinct block instead of once per frame. Themes are
still applied at render time, so
/themetakes effect immediately. - The transcript is bounded. A single answer cannot exceed 256 KiB in the
view, and
transcriptRetaincaps how many entries are kept. Both bounds report what they removed rather than dropping it silently. - The frame interval adapts. If a frame is genuinely expensive, updates
coalesce into a wider window — 33 ms up to a 320 ms ceiling — so the terminal
stays responsive to typing and to
Ctrl+C. A mutedecochip appears in the status bar whenever this is in effect: the stream getting chunkier is something you should be able to see a reason for.
/perf reports exactly what the renderer is doing:
render rung 2 - interval 80ms - last commit 61ms - eco
transcript 4213 entries - 1000 retained - 3213 dropped - 38 mounted
heights 412 cached - 6 estimated - cols 132
caches md 178 (1.9MB) - hl 96 (1.4MB) - lines 340
mode fullscreen - viewport 44 rows - offset 0/perf reset clears the render caches and returns the governor to its fastest
rung. --no-render-governor (or renderGovernor: false, or
ARAGON_RENDER_GOVERNOR=0) turns the adaptation off entirely; the output is
identical, just heavier under load.
Inline mode (--no-fullscreen, TERM=dumb, CI, a very small terminal) gets
one extra guarantee. Ink repaints the entire session history on every frame
once a live entry grows taller than the terminal, which is a genuine freeze
rather than a slowdown. The live region is now clamped to stay below that
threshold, with a ... N earlier lines marker naming what is deferred; the
entry prints in full into your scrollback the moment it finishes.
Plan mode
aragon has two postures, and Shift+Tab switches between them. /plan does
the same thing for terminals that swallow the key (see below).
| Mode | What it means |
| --- | --- |
| BUILD | The default, and unchanged from earlier versions: you ask, the agent does it. |
| 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. |
BUILD is right for "rename this symbol". PLAN is right for "add multi-tenant
support" — anything where you want to see and steer the approach before a
single byte changes.
What plan mode actually stops
In PLAN, five tools are refused at the tool boundary: write_file,
edit_file, bash, skill_install and skill_create. That is a property of
the wiring, not a promise in a prompt — the refusal happens before the tool runs,
so the badge is telling you something that is structurally true.
bash is refused in full, git status included. This is the most likely
question this feature generates, so: there is no reliable way to classify a shell
string as read-only, and a gate that is right 95 % of the time is worse than one
that is always right, because you stop trusting the badge. Use read_file,
list_dir, glob and grep, which stay available; the refusal message names
them so the agent has somewhere to go. There is no allowlist and there will not
be one built out of substring matching.
Everything else still works: reading, searching, loading skills, and asking you questions.
The two plan-mode tools
ask_userrenders a keyboard-driven wizard: 1–5 questions, 2–4 options each, exactly one markedRECOMMENDED, plus anOther…free-text option.↑/↓choose,Enterconfirms and advances,←goes back,Esccancels. PressingEnterthrough the whole wizard accepts every recommendation, which is a deliberate "use your judgement" path rather than an accident. A last screen lets you review before submitting.submit_planrenders the plan as a scrollable card:aapproves,ropens a one-line feedback field (Entersends it,←goes back to the card),Escdismisses. Approving flips the session toBUILDimmediately and the same run continues straight into implementation — you do not have to re-ask.
Cancelling either one is not an error. The agent is told to proceed on stated assumptions rather than to retry.
Dismissing a plan is not how you stop. Esc on a plan card asks for a
better plan — the agent is told to refine it and submit again — so a model that
keeps submitting keeps getting cards. To stop the run itself, press Esc
twice: the first closes the card, the second aborts the run. Ctrl+C twice
still exits the session outright.
Where the mode is shown
The status bar always names a non-default mode (PLAN, or PLAN → BUILD while
a switch is pending), including on short terminals, with --no-hints, and in
inline mode. The composer additionally shows a PLAN chip on its hint row and
tints its border, where there is room for it.
Switching mid-run
Tightening applies at once; loosening waits. Pressing Shift+Tab toward PLAN
during a run takes effect immediately — you pressed it because the agent is
about to do something. Pressing it toward BUILD during a run is deferred to
the end of the run, so a session you launched under a read-only guarantee
cannot start writing files because of one stray keypress. The status bar shows
PLAN → BUILD while that is pending. Approving a plan is the one exception, and
it is an informed act rather than a stray keypress.
If Shift+Tab does nothing
Some multiplexers and remote-desktop stacks never send CSI Z. /plan,
/plan on, /plan off and /plan status are first-class equivalents, and
--plan / ARAGON_PLAN=1 / planModeDefault cover the startup case.
Headless
aragon -p --plan "add SSO" is a useful combination: the read-only gate still
applies, so it means "tell me how you would do this, and do not touch my
repository". No question or plan tool is registered there — there is nobody to
ask — and the agent writes the finished plan as its final message in markdown.
TODO planning
For work with three or more distinct steps the agent keeps a visible plan:
it writes a checklist with the todo_write tool, marks exactly one item as in
progress, and ticks it off before starting the next. In full-screen mode that
list is rendered as a right-hand rail, about a fifth of the terminal wide.
transcript ... | TODO 2/7
| ####------
|
| v 1 Read the reducer
| v 2 Design the store
| > 3 Wiring the rail into
| AppShell
| o 4 Add the panel-rows test
| o 5 Update the README
| +2 moreAsk for something small and nothing appears. The agent is told, in its system prompt, not to use the tool for anything it can finish in one or two steps, for a question, or for a search with no work attached — and a one-item list against an empty plan is refused outright. A session that never plans has byte-identical layout to a build without this feature.
The list is a projection of what the model believes, so nothing that does not also tell the model may change it — except you, saying so:
/clearand/todo cleartake the plan off the screen. Neither touches the conversation, so the model still knows what it was doing and its next update brings the panel straight back./resetclears the conversation, so the plan goes with it./savewrites the list and/resumerestores it. Resuming a session that has no list clears the current one, rather than leaving a plan on screen whose conversation has just been replaced.- A finished plan is dropped at the START of your next message, so you still get
to see
7/7 done. An unfinished one survives "continue" / "now do the rest", and is dropped after three unrelated turns rather than holding a fifth of the screen for the rest of the session.
When a run ends with steps left
By default you get a notice and /todo continue — nothing auto-continues on
your behalf. /todo continue no longer says "continue the remaining items"; it
sends the model the numbered list of steps that are still open, so a plan that
scrolled out of the context window some turns ago is still followed exactly.
/todo follow auto (or --todo-follow auto, or todo.followThrough in
config.json) turns that into an actual loop: the CLI announces
Continuing with 4 remaining steps in 3s - Esc to stop., waits, and then sends
the continuation itself. Two structural limits bound it, and neither is a
setting:
- A continuation that completes no step buys exactly one more attempt. After that you get a sentence and the keyboard back.
- A single plan is continued at most 25 times however productive it looks. A re-plan forgives the first counter but never the second, so a model that reshapes its list every turn cannot lift the ceiling.
Esc during the grace window cancels; so does typing anything. A run that ended
with an error never auto-continues in any mode — you get a warning instead.
A run you aborted with Esc now says nothing at all, where it used to
report what you had just interrupted.
/todo follow off is the third mode: no notice, no continuation, silence.
Under -p there is no grace window (there is nobody there to use it), so the
two counters are the whole of the protection. [todo] continuing (3 steps left)
goes to stderr before each continuation and [todo] 3 steps unfinished when it
stops; both are suppressed by --quiet, and an unfinished plan never changes
the exit code.
Inline mode
Inline mode has no fixed frame and therefore no rail, but it does get a one-row plan strip under the transcript:
todo 3/7 > Adding the rail to AppShell +2 doneIt never wraps and never grows past one row, so the composer does not move. The
+N done suffix drops below 80 columns and the counter compacts to [3/7]
below 100, matching the status bar. --no-todo-panel hides the strip as well as
the rail.
/todo reports status and switches things off: /todo off unregisters nothing
(the tool list is fixed when the session starts) but stops the tool working and
drops the guidance from the system prompt, while /todo panel off keeps the
planning discipline and hides the column — which is what you want with a screen
reader, and what --no-todo-panel does for a whole session. Under -p the plan
is written to stderr as [todo] 3/7 <step> lines, suppressed by --quiet.
The rail is not mounted below 80 columns, while an overlay is open, in inline mode, or when the viewport is too short to say anything useful; the transcript keeps at least 62 columns at every width. In those cases the counter appears in the status bar instead, and the checklist is still written into the transcript as one card per turn.
Built-in tools
read_file, write_file, edit_file, list_dir, glob, grep, bash — all
at full permission. Paths resolve against the session working directory. The
per-tool timeout and 100 KB output truncation are enforced by the core executor;
bash's timeout param only shrinks within that ceiling.
When skills are enabled, four more are added — the two read tools skill and
skill_find, then the two mutating ones skill_install and skill_create (see
below). --no-skills removes them again. Updating a skill is deliberately not
among them; that is a aragon skills update decision for you, not the model.
With team mode on — which is the default — one more is added: task, the
delegation tool (see below). --no-team removes it again.
With todo planning on — also the default — one more is added: todo_write, the
planning tool (see above). --no-todo removes it again.
Team subagents
The agent can hand parts of a job to short-lived subagents that run in
parallel, then read one combined report of what they each found. It decides for
itself whether to delegate; a session that never calls task is the session
that existed before this feature.
> read the auth middleware, map the route table, and check the migration
◆ team 2 running · 1 done · 84s ✉ 1
⠋ a1 read the auth middleware tool: grep 22.1s
⠙ a2 map the route table thinking 31.6s
✔ a3 check the migration done 4 turns 18.2sWhat a subagent is. A full agent with its own message history, the same tools and the same working directory — but no memory of your conversation, so its brief has to be self-contained. It cannot delegate further (depth is capped at one by construction) and it cannot ask you anything.
Delegate wide, not deep. Two to four subagents is the usual size. The
default cap is 5 per dispatch and 3 in flight at once; the hard maximum is 10
and no config file can raise it. maxConcurrent is separate from maxSubagents
on purpose: five simultaneous streams against one API key is a reliable way to
collect rate-limit errors, and each one would land in the report as a failed
subagent.
The report. One text result: a header with the ok/failed counts, aggregate
tokens and cost, then one section per subagent with its status, timing, the
files it wrote and its own final summary. A failed subagent never fails the
dispatch — partial results are the normal outcome of a fan-out. When two
subagents wrote the same file the header says so, phrased as a warning rather
than a fact: the file list is derived from write_file / edit_file, so a
write made through bash is invisible to it.
Talking to each other. Subagents get two extra tools, team_send and
team_wait. Messages are delivered by attaching them to the recipient's next
tool result, so nobody has to poll and nobody's work is interrupted. The limits
are the point rather than an afterthought: 6 messages per subagent, one every
15 seconds, 24 per dispatch, a broadcast costing two. Every refusal is a plain
result naming the limit, never an error — an error reads as a malfunction and
invites a retry, which is the behaviour the limits exist to prevent.
What you see. While a dispatch runs, the roster above sits between the
transcript and the composer and the status bar carries agents 3/5 ([3] under
100 columns). Both disappear when it finishes and the transcript keeps a card
with the per-agent results; Ctrl+O expands it to their summaries. Under -p
the same transitions go to stderr as [team] a1 ok 22.1s 7 tools lines, and the
children's tokens are folded into the [usage] footer.
Interaction with the other subsystems.
- Plan mode reaches one level down. In PLAN, every subagent is built with
the same read-only gate, so
write_file/edit_file/bashare refused inside them too. Parallel research is plan mode's best use, sotaskitself is allowed. A single subagent can be pinned read-only withreadOnly: trueeven in a BUILD session — the field can only ever tighten. - A skill's
allowed-toolsceiling reaches one level down too, so delegating is not a way around it. Subagents getskill_findbut notskill: loading a skill mutates a turn-scoped registry the lead owns, and three subagents doing it at once would narrow what the lead may do next in a way nothing would report. Always-on skills still reach them. --confirmqueues subagent confirmations one at a time, labelled with the subagent that is asking ([a2] Write: src/routes.ts).- Logs.
dispatch_startanddispatch_endare recorded atinfowith the ok / failed / aborted counts and aggregate usage; phase transitions atdebug. Ateam_sendsubject is recorded, never its body; a subagent's brief and summary only attrace.
Esc aborts the whole dispatch — every subagent within a couple of seconds
— and the report comes back marked ABORTED with whatever was finished.
Fast model tier
A second, cheaper model can be made a first-class citizen of a session. It is
off by default — with fast.enabled: false the system prompt, the task
tool schema, the transcript and the request payloads are exactly what they were
before this feature existed — and it buys you two things once you turn it on.
Delegation (the agent decides). task gains one optional field per
subagent, model: "fast". A child marked that way runs on the fast model and is
otherwise identical: same tools, same working directory, same --confirm gate,
same plan-mode gate, same skills ceiling. Delegating to a cheaper model is a
model choice, never a permission boundary. This is where "not complicated, but
very expensive in context" is actually paid for: a child that reads nine files
and reports four sentences moves ~200 KB of file bodies out of the lead's window
and onto a model that costs a fraction as much per token.
Periodic review (the harness decides). Every N completed turns, a small
digest of what the lead has been doing goes to the fast model with one question
— is this still on track? — and if the answer is not "on track" it is injected
back into the running loop as a <fast_review> block. The lead sees a short
second opinion mid-run, when it can still act on it, for a few hundred tokens
instead of a second full-size turn.
> refactor the config loader and keep the tests green
fast review #2 · claude-haiku-4-5 · turn 12 · 1.4s
You have edited config/schema.ts three times without running the tests.
The clamp for reviewEveryTurns is missing its upper bound.The digest is bounded, and that is the whole economic argument. The reviewer keeps its own ring of recent turns — the tools you called, whether they failed, the tail of what the model said — and never reads the message history. Sending that history to the "cheap" model would make a review cost more than the turn it is reviewing.
Cost is attributed per tier. A fast-tier subagent's tokens are priced at the
fast model's table, not the lead's, and the dispatch report states the two
separately. If the fast model is not in the built-in price table the number is
shown as unknown rather than as $0.00 — a feature that looks free while it
is spending money is worse than one that admits it does not know.
The review has a session budget, and delegation deliberately does not. A
session is not one run — it is one run per message you send — so a cadence alone
bounds nothing over a working afternoon. fast.reviewMaxPerSession (default
40) is the total number of reviews a session may start; reaching it prints
one info notice naming the remedy, and /fast status reads 12/40 from the
first review rather than a bare count you can only size afterwards. Raise it live
with /fast budget 80. The budget is denominated in reviews, not currency,
because the fast tier is precisely where a model the price table has never seen
lives — a dollar ceiling would silently never fire for the configuration that
most needs one. Delegation is never budgeted: it is spend the lead chose in
service of a message you sent, it is already bounded by team.*, and it exists
to reduce total cost.
Turning it on
aragon config set fast.model claude-haiku-4-5
aragon config set fast.enabled trueor, in a session: /fast model claude-haiku-4-5 then /fast on. Either way the
tier is on for the next launch; /fast on in a session that started without it
saves the setting and says so, because the task schema is fixed at startup and
cannot grow a field mid-session.
| key | default | meaning |
|---|---|---|
| fast.enabled | false | Resolve the tier at all. |
| fast.provider | '' | Empty inherits the main provider. |
| 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. |
| fast.baseUrl | '' | Empty inherits the session's base URL only when the provider matches. |
| fast.thinkingLevel | 'off' | Applied to fast children and to reviews. |
| fast.delegate | true | Allow model:"fast" on task. |
| fast.review | true | Run the periodic review. |
| fast.reviewEveryTurns | 5 | Turns between reviews (1–50). |
| fast.reviewContextTurns | 3 | Turns included in one digest (1–10). |
| fast.reviewMaxChars | 280 | Ceiling on an injected critique (80–600). |
| fast.reviewMaxPerSession | 40 | Reviews started per session, across all runs (1–500). There is no "unlimited" sentinel. |
Flags: --fast / --no-fast, --fast-model <id>, --fast-provider <id>,
--fast-review <n|off>. Environment: ARAGON_FAST,
ARAGON_FAST_PROVIDER, ARAGON_FAST_MODEL, ARAGON_FAST_BASE_URL — the four
that a container or a wrapper owning argv cannot otherwise reach. There is
deliberately no env var for the review cadence: tuning it is what the config
file is for.
/fast is the discoverable surface and /fast status is the guaranteed
reporting one:
/fast status, the resolved model, the cadence, session totals
/fast on | off live switch
/fast model <id> accepts provider:model
/fast provider <id>
/fast same run the fast tier on the main model
/fast review <n> | off
/fast budget [n] the session review budget; bare form reads it
/fast delegate on | off/fast budget with no argument is a read, so it is answerable while a task
dispatch is running; /fast budget <n> is a write and is refused mid-dispatch
like every other /fast setting.
What you see. A fast chip in the status bar's right cluster while the tier
is live (fast* while a review is in flight, dropped below 100 columns), a
one-line card per review in the transcript, and n fast plus a trailing ~ on
fast children in the team roster. /fast status reports the session totals the
status bar cannot: reviews run, children delegated, tokens and cost. Under -p
nothing extra is written to stdout or stderr — the review spend is folded into
the [usage] footer and the reviews are in the log.
When the tier stops resolving. fast.provider: '' inherits from the main
provider, so switching model or clearing a key can leave the tier unusable
mid-session. When that happens model:"fast" is downgraded to the main model,
the downgrade is counted and stated in the dispatch report, and one notice
names the reason. A silent downgrade would leave the agent believing its cost
model while paying full price.
When the review fails. Three consecutive misconfiguration failures (a
wrong model id, a dead gateway, a key with no access) disable the reviewer for
the session with exactly one warning. A busy provider — rate-limited or
overloaded — is treated as transient and never counts, because that condition
fixes itself and a session-long silent shutdown over it would not. A review that
you cancelled — Esc, a run that ended first, quitting — is neither: it leaves a
cancelled card and no strike.
The review fails fast, and your own work does not. The reviewer calls through
its own provider registry with one retry — not the ten retry.maxRetries
defaults to for the lead — and it does not observe retry.* or /retry at all.
That policy is a statement about how hard to fight for your answer; inheriting
it for a background advisory is how an unrequested call ends up competing with
you for a rate-limited provider's quota. The review's own wall-clock bound is
20 s.
Skills
A skill is a reusable expert procedure stored on disk: a directory containing a
SKILL.md (YAML frontmatter + Markdown body) and, optionally, reference/,
scripts/ and assets/. The format matches Claude Code's, so an existing
community skill directory works as-is.
Progressive disclosure
This is the part that makes skills cheap enough to install a lot of:
| Level | What the model gets | When |
| --- | --- | --- |
| 1 | Name, scope, and a one-line "what + when" | Always, in the system prompt. Whole catalog capped at 6 000 bytes. |
| 2 | The full SKILL.md body + a list of bundled files | Only when the model calls skill(name=…) |
| 3 | The bundled files themselves | Only when it reads them with read_file / bash |
A skill costs a line in the prompt rather than its whole body, so installing many of them stays cheap. The model decides on its own whether a task matches a skill — you do not have to name one.
At Level 2 the file list is split into what to read and what to run, and carries
the skill's absolute root so the model can build real paths. The accompanying
guidance is platform-specific: on Windows the bash tool runs cmd.exe, so
the model is told to run .py with python <abs path> and that a .sh file
will not work — rather than the previous, flatly untrue "run scripts with bash".
aragon skills doctor warns when a skill bundles only scripts for the other
shell.
How many fit in Level 1 depends on your descriptions, and on the language they are written in. The 6 000-byte ceiling is measured in UTF-8 bytes: a terse English description costs roughly 180 bytes a line (~30 skills), while a Chinese one at the 220-character cap can cost 660 (~10 skills). Past that point the catalog truncates.
Truncation is not a dead end. Entries are ranked before it happens, and what falls off is still reachable:
- Ranking: scope first (
env>project>user>bundled), then how recently and how often you have actually used each skill. A skill you ran an hour ago outranks one you have never used, so what survives truncation is what you actually work with — not whatever sorts first alphabetically. skill_find: when the catalog does truncate, it says so and names this tool, which searches everything installed by name, description and keyword. It only ever searches what is already on this machine — it never reaches the network and never suggests something to install.
Both are off the critical path: with no usage data recorded the order is exactly
the plain scope-then-name order, and skill_find is only advertised in the
prompt when something was actually omitted.
Using them
aragon skills list [--sort=recent] # what is installed
aragon skills info <name> # version, path, provenance, files
aragon skills install <source> [--yes] # dir | git repo | https .md / .zip
aragon skills install github:owner/repo#v1.2.0
aragon skills update <name> [--yes] [--force] [--dry-run]
aragon skills update --all [--yes] # every skill that has an upstream
aragon skills update --check [<name>|--all] # is there a newer version?
aragon skills remove <name> [--yes]
aragon skills create <name> --description "what + when" [--yes]
aragon skills path # user skills directory
aragon skills doctor # validate + detect tampering
aragon skills usage [--json] # local use counters
aragon skills usage --reset --yes # delete them
aragon skills trust <dir> # allow a project skills directoryUpdating
update re-fetches a skill from the source recorded in its install manifest,
keeping installedAt and recording updatedAt + previousVersion.
- Local edits are protected. If you have changed an installed skill,
updaterefuses and names the files, before downloading anything.--forceoverwrites them — and--forcediscards your edits; copy them out first. The replaced directory is parked under the staging trash, so a mistake is recoverable by hand. - The host allowlist is re-checked against your current
skills.allowedHosts, not the one in force when you installed. Narrowing that list therefore also constrains updates. --allnever stops on one failure. Skills that cannot be updated (hand-authored, bundled, or locally modified) are reported asskipped, which is not an error; only a genuine I/O or validation failure exits non-zero.--checkreportsunknownfreely. It compares the commit recorded at install time againstgit ls-remote, so it can only answer for git sources installed by a version that records one. Anything else is honestly reported as unknown rather than guessed.- The model cannot call
update. Installing is something you pointed it at; swapping working instructions for different ones is a maintenance decision and stays with you.
Interactively the same lives under /skills … (/skills update <name> [--force],
/skills update --all, /skills list --sort=recent, /skills policy [mode],
/skills unload, /skills usage [--reset]). Every usable skill also becomes a
slash command:
/pdf-forms invoice.pdf --flattenBoth delivery paths share skills.bodyMaxBytes, so an oversized SKILL.md is
truncated with a visible marker rather than submitted whole. Loading the same
skill twice in one conversation returns a short digest (description, file list,
guidance) instead of a second copy of the body; the model can ask for the full
text back with skill(name="…", force=true).
Bodies may use $ARGUMENTS and $1..$9; $$ is a literal $. If a skill
declares no placeholder, the arguments are appended under an ## Arguments
heading instead. Where a skill's name collides with a built-in command, the
built-in wins and the skill is reachable as /skill:<name>.
Where skills come from
Four scopes, later ones overriding earlier ones by name:
| Scope | Location | Writable |
| --- | --- | --- |
| bundled | <package>/skills/ | no |
| user | aragon skills path | yes — the default install target |
| project | <cwd>/.aragon/skills/ | yes |
| project | <cwd>/.claude/skills/ | no — read-only Claude Code interop |
| env | ARAGON_SKILLS_PATH (:/;-separated) | no |
A shadowed skill is not lost: /skills shows it as ~shadowed by <scope>.
Frontmatter
---
name: pdf-forms # required, kebab-case, ≤ 64 chars
description: Fill, flatten and validate AcroForm PDFs. Use when the task
mentions PDF forms, field filling, or flattening a fillable PDF.
version: 1.2.0
license: MIT
author: Jane Doe
homepage: https://github.com/jane/pdf-forms
keywords: [pdf, forms]
allowed-tools: [read_file, bash] # enforced as a per-turn tool ceiling (union)
activation: auto # auto | always | manual
---description is the only text a model sees before deciding to load the skill,
so it has to say both what the skill does and when to use it. Unknown keys are
preserved, so a newer Claude Code field will not break parsing.
activation: always injects the body into every session's system prompt (12 000
bytes total across all such skills); manual keeps it out of the catalog so it
runs only when explicitly invoked.
Security model — and its limits
Be clear about what this does and does not buy you:
Installing requires a human. The approval gate checks whether a person is actually reachable before asking, and refuses when nobody is. Under
-pthere is no TUI and therefore no approver, soskill_installfails with an actionable message instead of writing to disk unattended. Opt out deliberately with--skills-yesorskills.requireApproval=false.Project directories need trusting once. Cloning an unfamiliar repository does not grant it the right to inject instructions into your session.
Nothing is executed at install time. No postinstall, no dependency install; the executable bit is not preserved. Scripts run only when the model invokes
bash.Archives are gated on real bytes — total size, per-entry size, compression ratio and entry count, all abortable mid-extraction. Path traversal, absolute paths, symlinks and Windows device names are refused.
Git is never run through a shell, and ref / owner / repo fragments are whitelisted and refused if they start with
-.Third-party text cannot break out of its prompt block: angle brackets in descriptions, bodies and file names are neutralized before rendering.
A changed
SKILL.mdis noticed at load time. Every install records a sha256; discovery re-checks the one file that reaches the model's context and flags it as!modifiedif it no longer matches what you approved. Seeskills.integritybelow.Concurrent installs cannot corrupt a skill. Writes take a per-directory advisory lock, so two
aragonprocesses installing at once queue instead of interleaving their atomic-replace steps. Limit: the lock relies on exclusive file creation, which is not reliably atomic on every NFS implementation. It self-expires after 60 seconds and waits at most 5, so the worst case there is no mutual exclusion — never a hang.
What it does not do: there is no sandbox. Approval blocks silent
installation; it does not make a skill you approved safe. A skill you accept can
contain adversarial instructions, and its scripts run with your full user
permissions — the same trust model as pasting text into the prompt and the same
as the bash tool. /skills info records provenance and a sha256 per file so
you can audit what arrived and notice if it changed. The integrity check answers
"is this still the file I approved?", not "is this file from someone I
trust" — there is no signing and no key distribution.
allowed-tools is enforced now (see below), but read the next section before
treating it as a permission boundary: it is blast-radius control, not a security
boundary, and a skill can widen its own ceiling by loading another skill.
The tool ceiling (allowed-tools)
A skill that declares allowed-tools constrains which tools the model may use
for the rest of the current turn:
allowed-tools: [read_file, write_file]- Scope is one turn. The ceiling appears when a skill is loaded — by the
model calling
skill(...), or by you running/<skill-name>— and it is gone as soon as you send the next message. A skill you used this morning does not restrict unrelated work this afternoon. - Read-only tools are always allowed, declared or not:
read_file,list_dir,glob,grep, plusskillandskill_find. What the declaration actually controls is the five tools that change something —write_file,edit_file,bash,skill_install,skill_create. - Claude Code names work.
[Read, Bash, MultiEdit]maps onto this host'sread_file/bash/edit_file. Tools that exist there but not here (WebFetch,Task,TodoWrite, …) are ignored rather than treated as errors. - A name we cannot resolve waives the whole declaration, and says so. One
typo means that skill imposes no ceiling at all — never a narrower one you did
not write.
aragon skills doctorreports these up front. activation: alwaysskills never impose a ceiling. They are ambient reference material, not a procedure you chose to run.
Three modes, via skills.toolPolicy (default enforce),
aragon --skill-tool-policy <mode> for one run, or /skills policy <mode> for
the current session (which takes effect immediately, even mid-turn):
| Mode | Behaviour |
| --- | --- |
| enforce | The call is refused; the model is told what it may use instead. |
| warn | The call runs, and you get one notice per tool per turn. |
| off | No ceiling, and nothing about allowed-tools reaches the model. |
/skills unload drops the ceiling for the current turn. It does not remove
skill text already in the conversation — nothing can.
What this is and is not. It is blast-radius control, not a security
boundary. The declaration is written by the skill's own author, so a malicious
skill can simply declare [bash]. Its value is that a declaration is now
binding: you can read one in /skills info and decide whether to install, and an
honest author's [read_file] means a "while you're there, run rm -rf" line
smuggled into that skill's text — or into a file it tells the model to read —
cannot execute.
And a known limit: the ceiling is a union across every skill loaded this
turn, and skill is always available. So a skill can widen its own ceiling by
instructing the model to load a second, more permissive skill. This is not
blocked — blocking it would either strand the model inside a skill it cannot
escape, or break every multi-skill workflow — but every widening is printed to
the transcript:
Tool ceiling widened by skill "skill-creator": +write_file, +skill_createSo it stops the smuggled instruction; it does not stop a skill that deliberately teaches the model to reach for another one.
Usage counters
To rank the catalog, the CLI keeps a small file next to your skills directory:
// <aragon skills path>/../skill-usage.json
{ "schema": 1, "skills": { "deploy-preview": { "useCount": 12, "lastUsedAt": 1785050000000 } } }It contains the skill name, a count, and a timestamp. Nothing else — no
arguments, no conversation content, no working directory, no machine identifier.
It is never transmitted anywhere, by anything. Delete it whenever you like; the
only effect is that the catalog falls back to plain scope-then-name order. Set
skills.usageTracking=false to switch it off entirely, after which the file is
neither read nor written.
Automatically injected activation: always skills are deliberately not
counted — they load on every turn, so counting them would say nothing about what
you actually use.
Configuration
Config is layered (highest priority last): defaults → user config file →
env / .env → CLI flags.
- Env /
.env:ANTHROPIC_API_KEY,OPENAI_API_KEY,GOOGLE_API_KEY/GEMINI_API_KEY, plusARAGON_PROVIDER,ARAGON_MODEL,ARAGON_BASE_URL,ARAGON_THINKING,ARAGON_SHOW_THINKING(1shows reasoning blocks),ARAGON_LIVE_TOOL_OUTPUT(0keeps a running tool card to one line),ARAGON_MAX_TOKENS(a number, orauto/0for the per-model ceiling),ARAGON_THEME,ARAGON_FULLSCREEN,ARAGON_PLAN(1starts in plan mode),ARAGON_TEAM(0disables team subagents),ARAGON_TEAM_MAX(fan-out width),ARAGON_TODO(0disables todo planning entirely),ARAGON_TODO_FOLLOW(notify/auto/off),ARAGON_RETRY(0disables API retry),ARAGON_RETRY_MAX(retry count;0also disables),ARAGON_UPDATE(0/offdisables auto-update,notifyreports without installing),ARAGON_UPDATE_REGISTRY(registry base URL for the update check),ARAGON_SKILLS(0disables),ARAGON_SKILLS_PATH,ARAGON_SKILLS_DISABLED(comma-separated names),ARAGON_LOG_LEVEL,ARAGON_LOG_FILE(0disables),ARAGON_LOG_DIR,ARAGON_TRANSCRIPT_RETAIN,ARAGON_RENDER_GOVERNOR(0disables),ARAGON_MAX_RENDER_INTERVAL_MS.ARAGON_HOMEis the exception that cannot come from.env: it is read before any.envis loaded. See Files & logs. - User config file (
aragon config path): JSON, written0600on POSIX. Holds provider / model / keys and your preferences, and nothing else — the prompts you type and the counters behind one-time notices live in their own files (see Files & logs). Keys are masked in the UI and never logged. It lives in your home directory — see Files & logs — and editing it by hand is supported:aragon config editopens it, and an invalid edit is reported rather than silently resetting every setting.
| Config key | Default | Meaning |
| --- | --- | --- |
| 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. |
| 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. |
| 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. |
| 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. |
| exitTranscript | true | Replay a plain-text session summary after exiting (full-screen only). |
| 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. |
| 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. |
| renderGovernor | true | Widen the streaming frame interval when a frame is genuinely expensive. See Render performance. |
| maxRenderIntervalMs | 320 | Ceiling of the governor ladder, clamped to [33, 1000]. 33 flattens it to the pre-0.6 cadence without disabling the mechanism. |
| planModeDefault | false | Start every session in plan mode. --no-plan still overrides it for a single run. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| team.dispatchTimeoutMs | 900000 | The whole dispatch's wall clock, clamped to [60000, 3600000]. |
| 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. |
| 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. |
| 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. |
| 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 g
