@xingjisoft/neutrino 0.1.7 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,10 +17,12 @@ A CLI coding agent that lives in your terminal. Neutrino gives you an interactiv
17
17
  - **Session history** — messages are saved before a turn starts, streamed replies are checkpointed every second, and tool calls/results are saved immediately. The history screen lists conversations started in the current working directory. Failed saves are shown in the terminal and browser UI.
18
18
  - **Auto-folding turns** — once a turn finishes, the work behind the reply (the thinking and the tool calls) folds into one summary line in both the terminal and the browser, while the answer itself always stays on screen. Click the line to read the whole round again; a turn with little to hide, or a long answer on its own, stays open. A turn stopped with `ESC ESC` is paused, not finished, so it stays open. Nothing is dropped: the transcript, the saved session and the model's context keep every message.
19
19
  - **Complete conversation history** — context compression keeps every original message and tool result on disk. The earlier portion starts collapsed in the terminal and browser; click **Earlier conversation** to expand it, including in reopened history. Model requests use a separate summary plus recent messages.
20
- - **Token usage stats** — click the `ⓘ` in the chat header to see prompt/completion/total token counts for the current session.
20
+ - **Plan-first turns** — before a request is answered, a planner agent splits it into ordered, independent steps ("model the case from box.png", "animate the camera: push in, orbit, pull back"), and the worker then executes those steps one at a time, top to bottom. Every step is a concrete deliverable, so later work is explicit instead of one vague instruction. See [Plan-first turns](#plan-first-turns).
21
+ - **The step card** — click `ⓘ` in the chat header (or press `Ctrl+P`) to open the info card in the top-right corner. It lists the plan as a vertical timeline of nodes: the step being worked on is highlighted, steps still to come are lit, and finished steps are drawn dark with a tick. The same card shows prompt/completion/total token counts when no plan is running.
22
+ - **Sub-agents** — independent work can be handed to a sub-agent: a second agent loop with its own context, its own tool calls and the same approval gate. Only its report comes back, so a survey that reads thirty files does not fill the conversation with thirty files. See [Sub-agents](#sub-agents).
21
23
  - **Agent history browser** — view past sessions from other CLI agents (Claude Code, Codex, OpenCode) directly from the history screen.
22
24
  - **Approval gate** — risky commands and paths outside the working directory need your sign-off. The header turns orange and reads **Request Approval**; press `Y` or `N`. Delete-class commands always ask, unless you explicitly granted full trust, and there is an opt-in reviewer agent for automatic decisions.
23
- - **Browser UI on any port** — mount a local website that lists every saved conversation grouped by working directory, streams the running one in real time, and lets you pause/resume it, send messages, switch provider/model/intensity, and edit settings from the browser.
25
+ - **Browser UI on by default** — every `neutrino` launch also mounts a local website at <http://127.0.0.1:8765> that lists every saved conversation grouped by working directory, streams the running one in real time, and lets you pause/resume it, send messages, switch provider/model/intensity, and edit settings from the browser. Use `--port`/`--host` to move it, `--lan` to share it, or `--no-web` to keep the terminal only.
24
26
 
25
27
  ## Requirements
26
28
 
@@ -50,6 +52,8 @@ neutrino
50
52
  ```
51
53
 
52
54
  Type a message on the start screen and press `Enter` to begin a new chat.
55
+ The browser UI is mounted at <http://127.0.0.1:8765> at the same time; see
56
+ [Web UI](#web-ui) below for `--port`, `--lan` and `--no-web`.
53
57
 
54
58
  ### Keys
55
59
 
@@ -67,6 +71,7 @@ Type a message on the start screen and press `Enter` to begin a new chat.
67
71
  | `Ctrl+C` × 3 | Quit after three consecutive presses, no more than 1.5 seconds apart (another key, paste, click or scroll resets the count) |
68
72
  | `Ctrl+Shift+C` | Quit immediately (requires the terminal to forward the shortcut) |
69
73
  | `Ctrl+A` | Toggle Request Approval / Auto Approval in a conversation |
74
+ | `Ctrl+P` | Toggle the plan / token info card in the top-right corner |
70
75
  | `Y` | Approve the pending request (while the approval card is up) |
71
76
  | `N` / `Esc` | Deny the pending request |
72
77
  | `Ctrl+Q` | Quit |
@@ -83,7 +88,7 @@ Type a message on the start screen and press `Enter` to begin a new chat.
83
88
  | Click and drag | Select text under the rectangle; releasing copies it to the clipboard |
84
89
  | Drag onto the first/last row of the log | Scroll the log a line at a time (faster the longer you hold it) and keep growing the selection past the screen |
85
90
  | Click the header (left of `ⓘ`) | Open the model selector |
86
- | Click `ⓘ` (chat header) | Toggle token usage stats |
91
+ | Click `ⓘ` (chat header) | Toggle the plan / token info card |
87
92
  | Drag the scrollbar | Grab the thumb on the right edge of the chat/log and drag it to scroll |
88
93
  | Scroll wheel | Scroll the chat/log, or move through lists |
89
94
  | Right-click | Go back (like `Esc`) |
@@ -96,13 +101,96 @@ highlight stops at the log's edge instead of spilling into the header or the
96
101
  input box, and the copy includes the lines that scrolled out of view. Scroll back
97
102
  and the selection shrinks again; press `Esc` to drop it.
98
103
 
104
+ ## Plan-first turns
105
+
106
+ A request is answered in two passes. A **planner agent** first turns the message
107
+ into an ordered list of independent steps; a **worker agent** then executes
108
+ exactly one step per pass, from the top down. Each step names one deliverable, so
109
+ a long request stops being a single vague instruction and becomes a sequence of
110
+ concrete, verifiable ones. The plan is written into the conversation as a
111
+ message and saved with the session, so the steps of a reopened conversation are
112
+ still there.
113
+
114
+ The steps are also listed in the `ⓘ` card in the top-right corner of the chat —
115
+ click it, or press `Ctrl+P`:
116
+
117
+ - nodes run top to bottom in execution order, joined by a connector,
118
+ - the step being worked on is the highlighted row,
119
+ - steps still to come are lit, and
120
+ - finished steps are drawn dark with a tick; a failed one is marked in red.
121
+
122
+ The card opens by itself as soon as a plan is written, and closing it loses
123
+ nothing: `Ctrl+P` brings it back. On a short terminal the card shows a window
124
+ around the running step and counts the steps above and below it.
125
+
126
+ A step that fails stops the plan: the remaining steps stay pending and the next
127
+ message can re-plan them. A turn paused with `Esc Esc` stops the plan and marks
128
+ it **stopped**, so no step keeps looking live. When no usable plan comes back —
129
+ a single-action request, a provider error, an answer that is not a plan — the
130
+ turn runs in one pass exactly as before, so planning is never a new way to fail.
131
+
132
+ **Simple requests are not planned at all.** Planning costs a model round-trip
133
+ before any work starts, and for "把这个变量改名" that round-trip is pure friction.
134
+ A request is planned only when it shows a reason to be split:
135
+
136
+ | Signal | Example |
137
+ | --- | --- |
138
+ | More than one thing to do | `先改配置,再更新文档`, `运行测试,并且把报错修掉` |
139
+ | Its own structure | a numbered or bulleted list |
140
+ | It reads as a brief | longer than about 180 columns |
141
+ | It asks for a plan | `列个计划`, `plan`, `steps` |
142
+
143
+ Everything else runs in one pass: no planner call, no plan card, and the agent
144
+ still names the steps it is taking inside its own answer. The check lives in
145
+ `needsPlan()` in `src/llm/plan.ts`.
146
+
147
+ The prompts are files, not code: [`assets/planner.txt`](./assets/planner.txt)
148
+ says how steps are written, [`assets/step.txt`](./assets/step.txt) is the
149
+ instruction for each step, and [`assets/prompt.txt`](./assets/prompt.txt) is the
150
+ worker's base system prompt. Turn planning off for one run with
151
+ `NEUTRINO_PLAN=off`, or permanently with `"plan_first": false` in `config.json`.
152
+
153
+ ## Sub-agents
154
+
155
+ The agent can hand one self-contained sub-task to a **sub-agent**: a second agent
156
+ loop with its own transcript, its own tool calls and the same working directory.
157
+ Only its final report comes back — the files it read and the commands it ran
158
+ never enter the main conversation. That is the point: surveying a large codebase
159
+ or probing how a command behaves costs many messages, and the parent needs the
160
+ conclusion, not the transcript. The child's tokens still count in the
161
+ conversation's usage.
162
+
163
+ - The parent calls the `delegate` tool with a complete task and, optionally, a
164
+ little context. The sub-agent cannot see the conversation and cannot ask the
165
+ user, so a task that refers to "the file above" fails; the system prompt tells
166
+ the model to write a brief that stands alone.
167
+ - Its tool calls go through the **same approval gate**: a risky call parks the
168
+ turn and shows the usual card (the header turns orange, the browser shows its
169
+ banner). Denying it tells the sub-agent not to retry unchanged, and the parent
170
+ receives the refusal in the report.
171
+ - Delegation is one level deep — `delegate` is not in a sub-agent's toolset — so
172
+ the work always has one owner and one pause button.
173
+ - While it runs, the terminal status line and the browser badge name the current
174
+ phase (`Sub-agent — read_file`), and the `delegate` tool block shows the task
175
+ it was given. The report replaces that marker when it arrives, as the result of
176
+ the tool call.
177
+ - The round limit (12) bounds a child that never finishes; the parent then gets
178
+ the partial report, clearly marked as partial. A paused turn aborts the
179
+ sub-agent and says so in the tool result.
180
+
181
+ The sub-agent's prompt is a file too: [`assets/subagent.txt`](./assets/subagent.txt).
182
+
99
183
  ## Configuration
100
184
 
101
185
  Settings are stored under `~/.neutrino/`:
102
186
 
103
187
  - `model.json` — provider and model definitions (edit to add your own endpoints).
188
+ DeepSeek-style thinking endpoints get `reasoning_content` sent back on their
189
+ assistant messages automatically; a differently named endpoint can opt in
190
+ with `"reasoning": true` (or opt out with `false`).
104
191
  - `config.json` — app configuration (selected provider/model/intensity, theme,
105
- language, approval mode, `auto_approve_extreme_risk`, `sound_alerts`).
192
+ language, approval mode, `auto_approve_extreme_risk`, `sound_alerts`,
193
+ `web_console`).
106
194
  - `session/` — saved conversation sessions, one JSON file per session.
107
195
 
108
196
  Set your API key in the model select screen or directly in `model.json`.
@@ -111,8 +199,11 @@ A few environment variables override flags:
111
199
 
112
200
  | Variable | Effect |
113
201
  | --- | --- |
114
- | `NEUTRINO_WEB_PORT` | Web UI port: like `--web-port` in TUI mode, and the port in `neutrino web`. |
115
- | `NEUTRINO_WEB_HOST` | Interface to bind, like `--host`. |
202
+ | `NEUTRINO_WEB_PORT` | Web UI port: like `--port` in either mode, and it mounts the UI even when `web_console` is off. |
203
+ | `NEUTRINO_WEB_HOST` | Interface to bind, like `--host`; also mounts the UI when `web_console` is off. |
204
+ | `NEUTRINO_NETWORK_ATTEMPTS` | Total tries for a model request that keeps dropping (default 7). |
205
+ | `NEUTRINO_RETRY_DELAY_MS` | Wait before the first retry in ms; each retry doubles it (default 5000). |
206
+ | `NEUTRINO_PLAN` | `off` answers a request in one pass without planning it (overrides `plan_first`); `on` forces planning. |
116
207
 
117
208
  ### Approvals
118
209
 
@@ -210,11 +301,11 @@ Only sessions that were started in the current working directory are listed, so
210
301
 
211
302
  ## Web UI
212
303
 
213
- Neutrino can mount a small website on a port of your choice. It groups every
214
- saved conversation by working directory (showing the five newest per path with
215
- the rest collapsed), folds the work of every finished turn into one summary line
216
- that opens on a click while the reply stays on screen, streams a running
217
- conversation live, and lets you pause or
304
+ Neutrino mounts a small website at <http://127.0.0.1:8765> whenever the
305
+ terminal UI starts. It groups every saved conversation by working directory
306
+ (showing the five newest per path with the rest collapsed), folds the work of
307
+ every finished turn into one summary line that opens on a click while the reply
308
+ stays on screen, streams a running conversation live, and lets you pause or
218
309
  resume it and send new messages from the browser.
219
310
 
220
311
  A saved transcript opens read-only, because nothing is running for it. Press
@@ -225,7 +316,7 @@ the same session file.
225
316
 
226
317
  Use the selectors above a conversation to change its provider, model or
227
318
  intensity, then press **Apply to conversation** (pause an active turn first).
228
- The **⚙ Settings** button in the sidebar edits defaults for new conversations,
319
+ The **Settings** (gear) button in the sidebar edits defaults for new conversations,
229
320
  context compression and approval settings. Conversation choices are saved with
230
321
  the transcript and do not overwrite the defaults. Configured models and keys
231
322
  are shared with the terminal; keys stay on the server.
@@ -238,8 +329,12 @@ to future turns in active conversations. The legacy fixed
238
329
  `context_compress_threshold` setting is no longer used.
239
330
 
240
331
  ```sh
241
- # Mount the web UI while the terminal UI runs
242
- neutrino --web-port 8765
332
+ # The terminal UI now brings the web UI up with it
333
+ neutrino
334
+
335
+ # Pick a different port or interface
336
+ neutrino --port 9000
337
+ neutrino --host 100.64.0.7
243
338
 
244
339
  # Or serve only the saved history, with no terminal
245
340
  neutrino web --port 8765
@@ -248,6 +343,12 @@ neutrino web --port 8765
248
343
  Then open <http://127.0.0.1:8765>. The site itself lives in [`web/`](./web) and
249
344
  needs no build step; see [`web/README.md`](./web/README.md) for the API.
250
345
 
346
+ The console is on by default; turn it off for one run with `--no-web`, or
347
+ permanently in the terminal's **Settings → Web console**. The `--web-port` flag
348
+ is still accepted as an alias for `--port`. If the default port is already taken
349
+ (usually by another Neutrino instance), the terminal UI starts anyway and prints
350
+ how to choose another port.
351
+
251
352
  ### Reaching it from another device
252
353
 
253
354
  The server binds to loopback, so only this machine can open it. To use it from a
@@ -255,11 +356,13 @@ phone, a tablet or another box on your LAN or VPN, bind an interface others can
255
356
  see:
256
357
 
257
358
  ```sh
258
- neutrino web --port 8765 --lan # every interface (0.0.0.0)
259
- neutrino --web-port 8765 --lan # same, while the TUI runs
260
- neutrino web --port 8765 --host 100.64.0.7 # one specific address
359
+ neutrino --lan # TUI + web UI on every interface (0.0.0.0)
360
+ neutrino --host 100.64.0.7 # TUI + web UI on one specific address
361
+ neutrino web --port 8765 --lan # headless web UI on every interface
261
362
  ```
262
363
 
364
+ `--lan` is shorthand for `--host 0.0.0.0`; the two cannot be combined.
365
+
263
366
  Startup prints every address that works, so you can copy the one matching the
264
367
  device you are on:
265
368
 
@@ -274,7 +377,9 @@ Open the address that matches your device:
274
377
  Open the address and sign in with your permanent Web password. The initial
275
378
  password is **`114514`**. Change it in the browser's **Settings → Web access**
276
379
  or in the terminal's **Settings → Web password**. It is saved as a salted hash
277
- in `~/.neutrino/config.json` and survives restarts.
380
+ in `~/.neutrino/config.json` and survives restarts. While the site is running,
381
+ its address is also shown in the terminal header and in the terminal's
382
+ **Settings → Web console** row, which can start or stop it without a restart.
278
383
 
279
384
  For automatic login, open `http://<host>:8765/?pwd=114514`. The page checks the
280
385
  password, remembers the login, and removes `pwd` from the address bar. If it is
@@ -292,8 +397,8 @@ and startup output never contain your password or session credentials.
292
397
  - **Firewall:** the first bind to `0.0.0.0` triggers an allow prompt on Windows
293
398
  and macOS. Allow it for private networks, and check that your WLAN is not
294
399
  marked "public", where incoming connections are blocked anyway.
295
- - **`127.0.0.1` remains the default** because the zero-config case should not be
296
- a network service.
400
+ - **`127.0.0.1` is still the default interface.** The console starts with the
401
+ terminal UI, but it is not a network service until you pass `--host`/`--lan`.
297
402
 
298
403
  ## Built-in tools
299
404
 
@@ -303,6 +408,7 @@ The agent can call the following tools during a conversation:
303
408
  - `read_file` — read a text file (capped at 200KB).
304
409
  - `write_file` — write content to a file.
305
410
  - `list_files` — list directory contents.
411
+ - `delegate` — hand one self-contained sub-task to a sub-agent and return its report.
306
412
 
307
413
  Every tool takes an additional `reason` argument: one sentence saying what the
308
414
  call is for and why it is needed now. It is what the approval card shows and what
@@ -332,11 +438,14 @@ node scripts/test-render.mjs # fuzz the diff renderer + render the chat scre
332
438
  # asserting no stale/residual cells are left behind
333
439
  node scripts/test-sanitize.mjs # verify ANSI/control codes never reach the screen
334
440
  node scripts/test-drift.mjs # verify the input area self-heals after terminal drift
335
- node scripts/test-fetch.mjs # verify streamed requests release aborts and retry drops
441
+ node scripts/test-fetch.mjs # verify streamed requests release aborts, retry drops and the 7-attempt backoff
336
442
  node scripts/test-compact.mjs # verify context-compression planning
337
443
  node scripts/test-history-fold.mjs # verify full-history retention, folding and resume
338
444
  node scripts/test-web-history.mjs # verify browser history disclosures and streaming state
445
+ node scripts/test-web-default.mjs # verify the web console starts and toggles by default
339
446
  node scripts/test-turn-fold.mjs # verify folded turns keep their reply and every message
447
+ node scripts/test-plan.mjs # verify plan-first turns, step order, the step card and the fallback
448
+ node scripts/test-subagent.mjs # verify delegation: isolation, approvals, abort, round limits
340
449
  node scripts/test-tools.mjs # verify tool-output folding
341
450
  node scripts/test-web.mjs # verify the web server, API, SSE stream and path-traversal guards
342
451
  node scripts/test-web-settings.mjs # verify model switching, persistence, browser controls and defaults
@@ -359,11 +468,28 @@ A cell's glyph is copied straight into the output stream, so a cell holding a *c
359
468
 
360
469
  ### Network resilience
361
470
 
362
- Model responses arrive over a streamed connection that can drop at any point. A single blip should not end the session, so three cases are handled:
471
+ Model responses arrive over a streamed connection that can drop at any point. A single blip must not end the session — a turn is minutes of work, and a proxy that resets an idle socket says nothing about the user's task — so a failed request is retried before anything is given up:
472
+
473
+ | Attempt | Wait before it |
474
+ | --- | --- |
475
+ | 1 | — |
476
+ | 2 | 5s |
477
+ | 3 | 10s |
478
+ | 4 | 20s |
479
+ | 5 | 40s |
480
+ | 6 | 80s |
481
+ | 7 | 2m 40s |
482
+
483
+ That is seven tries and roughly five minutes in total, doubling every time: a link that is merely flaky recovers long before the budget is spent, while a link that is genuinely down is not hammered. `NEUTRINO_NETWORK_ATTEMPTS` and `NEUTRINO_RETRY_DELAY_MS` tune both numbers. The policy lives in `src/llm/retry.ts`.
484
+
485
+ What counts as worth retrying:
486
+
487
+ - **Connection failures** — a reset (`ECONNRESET`), a refused or unreachable socket, `EPIPE`, DNS (`EAI_AGAIN`), a connect timeout, and undici's `UND_ERR_*` codes.
488
+ - **A stream that dies before the first byte** — the server or a proxy closed the connection mid-flight (`TypeError: terminated`, `other side closed`, `socket hang up`, …). Nothing has reached the user, so the request is simply re-issued.
489
+ - **A transient HTTP status** — 408/425, 429 and 5xx (502/503/504 from a load balancer, 529 "overloaded"). A bad request is different: 400/401/403/404/422 surface on the first answer, because re-sending them only delays the real message.
490
+ - **A stream that dies after output has started** is *not* retried: replaying it would duplicate the reply, so the error is surfaced with the real reason expanded out of the `error.cause` chain (`fetch failed` alone says nothing useful) and the partial text is kept.
363
491
 
364
- - **The request never goes out** (`fetch` rejects) — retried a couple of times with a short backoff, bounded by a connect timeout.
365
- - **The stream dies before the first byte** — the server or a proxy closed the connection mid-flight (`TypeError: terminated`, `other side closed`, …). Nothing has reached the user, so the request is simply re-issued.
366
- - **The stream dies after output has started** — retrying would duplicate text, so the error is surfaced instead, with the real reason expanded out of the `error.cause` chain (`fetch failed` alone says nothing useful).
492
+ A retry can leave the stream silent for up to 160s, so it is never silent *about* being silent: the terminal status line and the web badge name the attempt and the delay ("Network hiccup — retrying in 40s (attempt 4/7)"). The wait is also polled, so pausing the turn, sending a newer message or quitting drops it immediately instead of sitting out the backoff.
367
493
 
368
494
  Any stream the consumer walks away from (pause, or a newer turn taking over) always has its body cancelled, so sockets are not leaked.
369
495
 
@@ -0,0 +1,35 @@
1
+ You are Neutrino's planning agent.
2
+
3
+ You execute nothing. You are given one user request, the project directory and
4
+ the conversation so far, and you return the plan that a second agent will carry
5
+ out. That agent has file, shell and network tools, and it is handed exactly one
6
+ step at a time, so a step that is vague here becomes vague work there.
7
+
8
+ Split the request into the fewest steps that can each be executed and verified on
9
+ their own:
10
+
11
+ - One step, one deliverable. "Model the case from box.png" is one step;
12
+ "model the case and light the scene" is two.
13
+ - Name the concrete material: which reference image, which file, which command,
14
+ which output. A worker who has to guess guesses differently every run.
15
+ - Order the steps the way the work has to happen: look at what already exists,
16
+ then create, then wire together, then verify (build, run, test, render).
17
+ - End with the step that proves the whole result works, when the request allows
18
+ it.
19
+ - Include only what the user asked for. Do not add features, styling or
20
+ refactors nobody requested, and do not invent files that are neither mentioned
21
+ nor obviously implied.
22
+ - Between 2 and 8 steps. A single-action request returns one step and runs as a
23
+ normal turn.
24
+ - When the user supplied exact wording — camera moves, copy, timings, a script —
25
+ keep it verbatim in that step's detail.
26
+ - Write every title in the same language as the user's request, as an imperative
27
+ action of at most about 80 characters.
28
+ - Add "detail" only when the step needs it: paths, parameters, acceptance
29
+ criteria or the user's own wording. Keep it to one line of about 200
30
+ characters or less.
31
+
32
+ Answer with JSON only. No prose, no explanation, no code fence:
33
+
34
+ {"goal": "one line restating what the user wants",
35
+ "steps": [{"title": "the action", "detail": "optional material for the step"}]}
package/assets/prompt.txt CHANGED
@@ -1,24 +1,60 @@
1
- You are XINGJI Neutrino, a powerful CLI-based AI coding assistant. Your capabilities include:
1
+ You are XINGJI Neutrino, an AI engineering agent working inside the user's
2
+ project on the user's machine. You read and edit files and run commands: the
3
+ work is done when the files are changed and the result is verified, not when it
4
+ is described.
2
5
 
3
- 1. **Code Generation**: Write clean, efficient, and well-structured code in any programming language.
4
- 2. **File Operations**: Read, create, edit, and manage files in the user's project.
5
- 3. **Command Execution**: Run shell commands to build, test, lint, and deploy code.
6
- 4. **Code Review**: Analyze code for bugs, security issues, and performance problems.
7
- 5. **Refactoring**: Suggest and implement code improvements and restructuring.
8
- 6. **Debugging**: Help identify and fix issues in codebases.
9
- 7. **Documentation**: Generate clear and comprehensive documentation.
10
-
11
- Guidelines:
12
- - Always understand the full context before making changes.
13
- - Follow existing code style and conventions in the project.
14
- - Write robust, production-quality code with proper error handling.
15
- - Prefer simple, readable solutions over complex ones.
16
- - When suggesting changes, explain the reasoning.
17
- - Use the tools available to you to explore the codebase before making changes.
18
- - Verify your changes by running tests and linting when possible.
6
+ ## How you work
7
+ - Your work is planned before it starts. When a turn carries a plan you are given
8
+ exactly one step of it: do that step, verify it, and stop. Never start a later
9
+ step, and never redo an earlier one.
10
+ - Without a plan, just do the work. A single action goes straight to the answer —
11
+ do not decorate it with a plan. When you are taking several steps, name them in
12
+ one short line each and then take them in that order.
13
+ - Look before you change: read the files you are about to edit, list the
14
+ directory, run the command that shows the current state. Anything about the
15
+ code you have not read is a guess, and guesses are the main source of bad
16
+ patches.
19
17
  - Every tool call must carry a `reason` argument: one honest sentence saying what
20
- the call is for and why it is needed for the current request. It is what the
21
- user sees when the call needs approval, and a call that needs approval but gives
22
- no reason is refused instead of run.
18
+ the call is for and why the current request needs it. It is what the user sees
19
+ when the call needs approval, and a call that needs approval but gives no
20
+ reason is refused instead of run. Never claim in it that the user already
21
+ approved something.
22
+ - Keep the change minimal and in the project's own style: reuse the existing
23
+ helpers, match the surrounding naming and formatting, and do not reformat or
24
+ restructure code you were not asked to touch.
25
+ - Verify what you did: run the build, the tests, or the command whose output
26
+ proves the result. Do not report success you have not seen, and never invent
27
+ command output.
28
+ - When something cannot be done, say so plainly with the error or the missing
29
+ piece, instead of quietly improvising a different task.
30
+
31
+ ## Sub-agents
32
+ - Delegate work that is independent and whose exploration would crowd this
33
+ conversation: surveying a large codebase, checking how a library or a command
34
+ really behaves, or one self-contained piece of implementation. The `delegate`
35
+ tool runs such work in a fresh context and returns only its report, so the
36
+ files it reads never fill this conversation.
37
+ - Write the task so it stands alone: the sub-agent cannot see this conversation
38
+ and cannot ask a question. Say what to do, where (paths, references), and what
39
+ "done" looks like; do not bury it in a list of steps unless the order matters.
40
+ - Its risky calls still go to the user's approval gate, and it cannot delegate
41
+ further. Treat its report as evidence: quote what it found, and check anything
42
+ the result depends on before you rely on it.
43
+
44
+ ## Quality bar
45
+ - Production quality: real error handling, no placeholders, no TODOs, no "left as
46
+ an exercise", no stubbed-out functions, no fake data standing in for the real
47
+ thing.
48
+ - Prefer the simplest solution that fully works; do not add configuration,
49
+ options or abstraction that nothing asked for.
50
+ - Never print secrets or API keys, and never write them into a file that is not
51
+ meant to hold them.
52
+ - Do not delete or overwrite the user's data without a reason the user can see.
23
53
 
24
- Language: Always respond in the same language the user writes in.
54
+ ## Answering
55
+ - Answer in the language the user writes in.
56
+ - Be specific and short: what changed (paths), what proves it (command, test,
57
+ render), what is still open. Skip the description of the process unless it is
58
+ needed to continue the work.
59
+ - If something is ambiguous in a way that changes the result, ask one precise
60
+ question instead of guessing silently.
@@ -0,0 +1,14 @@
1
+ You are executing step {{index}} of {{total}} of the plan below. Do this step
2
+ and nothing else: no later step, and no rewriting of earlier ones. Stop as soon
3
+ as this step is finished and verified.
4
+
5
+ Plan agreed with the user:
6
+ {{plan}}
7
+
8
+ Current step {{index}}: {{step}}
9
+ {{detail}}
10
+
11
+ End with one short paragraph: what you did, which files changed, and the evidence
12
+ (command output, test result, render) that shows it works. If the step cannot be
13
+ done as written, say precisely what blocks it instead of doing something else —
14
+ the user sees this and can re-plan.
@@ -0,0 +1,20 @@
1
+ You are a sub-agent. Another agent working with the user delegated one
2
+ self-contained task to you; it cannot see your messages, and the only thing it
3
+ receives from you is your final message.
4
+
5
+ Rules:
6
+ - Do the task with the tools you have, in the working directory you are given.
7
+ You cannot ask the user a question: when something is missing, do what can be
8
+ done and say precisely what is missing.
9
+ - You have no sub-agents of your own, and you must not try to delegate.
10
+ - Look before you change: read the files you are about to edit and run the
11
+ command that shows the current state.
12
+ - Risky calls still need the user's approval, exactly as for the parent agent.
13
+ If one is denied, do not retry it unchanged — work around it or report it as
14
+ blocked.
15
+ - Verify what you did with a command whose output you can quote. Do not report
16
+ success you have not seen, and never invent command output.
17
+ - Your final message is your report: what you did, the files you touched, the
18
+ commands you ran and what their output proved, and anything left unfinished or
19
+ blocked. Be factual and short — no preamble, no restating of the task.
20
+ - Answer in the same language as the task.
package/dist/app.js CHANGED
@@ -90,6 +90,8 @@ export class App {
90
90
  selOffset = null;
91
91
  resizeHandler = () => this.onResize();
92
92
  webHandle = null;
93
+ /** Where the web UI lives; kept even while it is switched off. */
94
+ webOptions = { port: 8765, auth: true };
93
95
  constructor() {
94
96
  this.width = process.stdout.columns || 80;
95
97
  this.height = process.stdout.rows || 24;
@@ -119,6 +121,13 @@ export class App {
119
121
  this.state.selectedModelApiKey = String(config.selected_model_api_key ?? this.state.selectedModelApiKey);
120
122
  this.state.selectedModelStreaming = Boolean(config.selected_model_streaming ?? true);
121
123
  }
124
+ /**
125
+ * Remember where the browser UI should listen. Called at startup even when
126
+ * the console is disabled, so Settings can mount it later without guessing.
127
+ */
128
+ configureWeb(port, host, auth) {
129
+ this.webOptions = { port, host, auth };
130
+ }
122
131
  /**
123
132
  * Mount the browser UI on the given port. Safe to call once; subsequent calls
124
133
  * return the already-running server.
@@ -126,8 +135,40 @@ export class App {
126
135
  async enableWeb(port, host, auth = true) {
127
136
  if (this.webHandle)
128
137
  return this.webHandle;
129
- this.webHandle = await startWebServer({ port, host, auth });
130
- return this.webHandle;
138
+ this.webOptions = { port, host, auth };
139
+ const handle = await startWebServer({ port, host, auth });
140
+ this.webHandle = handle;
141
+ this.state.webUrl = handle.url;
142
+ return handle;
143
+ }
144
+ /**
145
+ * Start or stop the browser UI while the terminal keeps running. Returns a
146
+ * result instead of throwing so a failed bind (usually a port already in use)
147
+ * can be reported inline without an unhandled rejection.
148
+ */
149
+ async setWebConsole(enabled) {
150
+ if (enabled) {
151
+ if (this.webHandle)
152
+ return { ok: true };
153
+ try {
154
+ await this.enableWeb(this.webOptions.port, this.webOptions.host, this.webOptions.auth);
155
+ this.showToast(`Web console: ${this.state.webUrl}`);
156
+ return { ok: true };
157
+ }
158
+ catch (err) {
159
+ const error = err instanceof Error ? err.message : String(err);
160
+ this.state.webUrl = null;
161
+ this.showToast(`Web console failed: ${error}`, "top");
162
+ return { ok: false, error };
163
+ }
164
+ }
165
+ const handle = this.webHandle;
166
+ this.webHandle = null;
167
+ this.state.webUrl = null;
168
+ this.render();
169
+ await handle?.close();
170
+ this.showToast("Web console stopped");
171
+ return { ok: true };
131
172
  }
132
173
  async start() {
133
174
  // Installed before anything touches the terminal: from here on, a crash
@@ -752,6 +793,7 @@ export class App {
752
793
  screen.loadedTokens = data.tokens ?? null;
753
794
  screen.loadedContext = data.context ?? null;
754
795
  screen.loadedApprovalMode = data.approval_mode ?? null;
796
+ screen.loadedPlan = data.plan ?? null;
755
797
  if (data.provider)
756
798
  this.state.selectedProvider = data.provider;
757
799
  if (data.model)