@aragon-agent/cli 0.5.0 → 0.5.2

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