@astralyn/pi 0.84.1 → 0.84.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +2 -3
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +3 -5
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -25
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +29 -408
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction-controller.d.ts +89 -0
- package/dist/core/compaction-controller.d.ts.map +1 -0
- package/dist/core/compaction-controller.js +428 -0
- package/dist/core/compaction-controller.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.js +3 -3
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -15
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/provider-attribution.js +2 -2
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/settings-manager.js +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/telemetry.d.ts +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js +1 -1
- package/dist/core/telemetry.js.map +1 -1
- package/dist/extensions/deepwiki/index.d.ts.map +1 -1
- package/dist/extensions/deepwiki/index.js +23 -1
- package/dist/extensions/deepwiki/index.js.map +1 -1
- package/dist/extensions/deepwiki/render.d.ts +1 -1
- package/dist/extensions/deepwiki/render.d.ts.map +1 -1
- package/dist/extensions/deepwiki/render.js +4 -2
- package/dist/extensions/deepwiki/render.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +0 -4
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js +0 -1
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +20 -2
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +1 -1
- package/dist/extensions/todo/state.js +5 -5
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +0 -2
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +1 -1
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-chrome.d.ts +28 -0
- package/dist/modes/interactive/components/tool-chrome.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-chrome.js +81 -0
- package/dist/modes/interactive/components/tool-chrome.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +0 -3
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +11 -108
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +35 -52
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/bundled/README.md +0 -2
- package/docs/bundled/extensions/subagent.md +1 -1
- package/docs/bundled/tool-presentation.md +2 -2
- package/docs/docs.json +0 -8
- package/docs/extensions.md +6 -23
- package/docs/settings.md +1 -1
- package/docs/usage.md +1 -2
- package/examples/extensions/README.md +2 -0
- package/examples/extensions/plan-mode/README.md +66 -0
- package/examples/extensions/plan-mode/index.ts +390 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/subagent/README.md +175 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +126 -0
- package/examples/extensions/subagent/index.ts +1009 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/extensions/biu/index.d.ts +0 -19
- package/dist/extensions/biu/index.d.ts.map +0 -1
- package/dist/extensions/biu/index.js +0 -350
- package/dist/extensions/biu/index.js.map +0 -1
- package/dist/extensions/biu/prompts.d.ts +0 -26
- package/dist/extensions/biu/prompts.d.ts.map +0 -1
- package/dist/extensions/biu/prompts.js +0 -260
- package/dist/extensions/biu/prompts.js.map +0 -1
- package/dist/extensions/biu/workspace.d.ts +0 -122
- package/dist/extensions/biu/workspace.d.ts.map +0 -1
- package/dist/extensions/biu/workspace.js +0 -404
- package/dist/extensions/biu/workspace.js.map +0 -1
- package/dist/extensions/rewind/anchor.d.ts +0 -29
- package/dist/extensions/rewind/anchor.d.ts.map +0 -1
- package/dist/extensions/rewind/anchor.js +0 -35
- package/dist/extensions/rewind/anchor.js.map +0 -1
- package/dist/extensions/rewind/config.d.ts +0 -16
- package/dist/extensions/rewind/config.d.ts.map +0 -1
- package/dist/extensions/rewind/config.js +0 -69
- package/dist/extensions/rewind/config.js.map +0 -1
- package/dist/extensions/rewind/engine.d.ts +0 -93
- package/dist/extensions/rewind/engine.d.ts.map +0 -1
- package/dist/extensions/rewind/engine.js +0 -778
- package/dist/extensions/rewind/engine.js.map +0 -1
- package/dist/extensions/rewind/gc.d.ts +0 -35
- package/dist/extensions/rewind/gc.d.ts.map +0 -1
- package/dist/extensions/rewind/gc.js +0 -169
- package/dist/extensions/rewind/gc.js.map +0 -1
- package/dist/extensions/rewind/index.d.ts +0 -3
- package/dist/extensions/rewind/index.d.ts.map +0 -1
- package/dist/extensions/rewind/index.js +0 -287
- package/dist/extensions/rewind/index.js.map +0 -1
- package/dist/extensions/rewind/menu.d.ts +0 -13
- package/dist/extensions/rewind/menu.d.ts.map +0 -1
- package/dist/extensions/rewind/menu.js +0 -123
- package/dist/extensions/rewind/menu.js.map +0 -1
- package/dist/extensions/rewind/paths.d.ts +0 -4
- package/dist/extensions/rewind/paths.d.ts.map +0 -1
- package/dist/extensions/rewind/paths.js +0 -15
- package/dist/extensions/rewind/paths.js.map +0 -1
- package/dist/extensions/rewind/restore.d.ts +0 -54
- package/dist/extensions/rewind/restore.d.ts.map +0 -1
- package/dist/extensions/rewind/restore.js +0 -94
- package/dist/extensions/rewind/restore.js.map +0 -1
- package/dist/extensions/rewind/snapshot.d.ts +0 -45
- package/dist/extensions/rewind/snapshot.d.ts.map +0 -1
- package/dist/extensions/rewind/snapshot.js +0 -21
- package/dist/extensions/rewind/snapshot.js.map +0 -1
- package/dist/extensions/rewind/storage.d.ts +0 -12
- package/dist/extensions/rewind/storage.d.ts.map +0 -1
- package/dist/extensions/rewind/storage.js +0 -33
- package/dist/extensions/rewind/storage.js.map +0 -1
- package/dist/extensions/rewind/text.d.ts +0 -7
- package/dist/extensions/rewind/text.d.ts.map +0 -1
- package/dist/extensions/rewind/text.js +0 -19
- package/dist/extensions/rewind/text.js.map +0 -1
- package/dist/extensions/rewind/tool-path.d.ts +0 -3
- package/dist/extensions/rewind/tool-path.d.ts.map +0 -1
- package/dist/extensions/rewind/tool-path.js +0 -9
- package/dist/extensions/rewind/tool-path.js.map +0 -1
- package/dist/utils/coalescing-terminal.d.ts +0 -132
- package/dist/utils/coalescing-terminal.d.ts.map +0 -1
- package/dist/utils/coalescing-terminal.js +0 -292
- package/dist/utils/coalescing-terminal.js.map +0 -1
- package/docs/bundled/extensions/biu.md +0 -63
- package/docs/bundled/extensions/rewind.md +0 -86
package/docs/bundled/README.md
CHANGED
|
@@ -9,10 +9,8 @@ These hidden built-ins use the same public Extension API available to external e
|
|
|
9
9
|
| Feature | Tool or command | Purpose |
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| [`llama.cpp`](../llama-cpp.md) | `/llama` | Manage models served by the local llama.cpp router |
|
|
12
|
-
| [Biu](extensions/biu.md) | `/biu`, `biu://` paths | Carry a project cycle through plan, optional decomposition, execution, and archive |
|
|
13
12
|
| [DeepWiki](extensions/deepwiki.md) | `deepwiki` | Query indexed public GitHub repository documentation |
|
|
14
13
|
| [Question](extensions/question.md) | `question` | Ask structured questions through native interactive UI |
|
|
15
|
-
| [Rewind](extensions/rewind.md) | `/rewind`, `/tree` lifecycle | Create and restore project snapshots around session operations |
|
|
16
14
|
| [Router](extensions/router.md) | `/router` | Configure and probe Codex-style routing endpoints |
|
|
17
15
|
| [Statusline](extensions/statusline.md) | Footer status | Show concise extension-managed activity state |
|
|
18
16
|
| [Subagent](extensions/subagent.md) | `subagent`, `/agents` | Delegate bounded work to isolated child Pi sessions |
|
|
@@ -85,7 +85,7 @@ They apply only to future subagent runs and never change the parent session's `/
|
|
|
85
85
|
- Parent abort, `/reload`, `/new`, `/resume`, `/fork`, and session shutdown abort active child sessions. Queued workers do not survive the call.
|
|
86
86
|
- Parallel worker failures do not cancel other independent workers.
|
|
87
87
|
- Transient provider errors auto-retry: the worker session retries retryable stream errors with backoff (visible as a live `Retrying (n/m) in Xs…` countdown), and a failure that produced no work (for example a preflight auth throw) is rerun at the task level up to two more times. Provider and task retries share one absolute-deadline state; at zero it reads `Retrying now…`, and parent abort/session shutdown interrupts backoff immediately. Runs with partial work behind them are never rerun.
|
|
88
|
-
- Progress is rendered in Pi's native tool transcript. While a call is running, the
|
|
88
|
+
- Progress is rendered in Pi's native tool transcript. While a call is running, the Subagent renderer re-schedules its own repaint once per second, independently of worker output and the main Working spinner. Silent single and parallel runs therefore keep advancing their own elapsed clocks in collapsed and expanded views, and queued retries keep counting down. Once settled, the final duration is fixed and refresh stops.
|
|
89
89
|
- The collapsed view stays lean in both phases: while running, one bounded intent line (for example `Verifying changes`) carries tool uses, the `ctx:` context watermark, cost, and elapsed time above the current tool marked with `›` and the live output tail; once settled, the sentence-aware result excerpt is followed by a single metrics line (`tok · $ · duration`, plus tool uses and `ctx:` for single runs) and the configured expand hint. Live lines never quote token totals — mid-run totals are cache-inflated. The call-level status dot belongs to the tool shell; run and activity rows only use `›`, `○`, `✓`, `×`, and `■`, and parallel run rows carry stable dim ordinals that match the expanded section numbers. `Ctrl+O` expands each run into a report cover sheet: a status line, a metrics line, a fixed two-line `Prompt` preview (`… continues, N more lines`, honest about the 1KB bound), an `Activity · last n of N` digest whose successful rows are quiet aligned one-liners, a `Working` tail while streaming, and the full `Report` (or `Error`, with `Report · partial` for salvaged output) rendered as Markdown. Expanded batches start with a numbered contents list whose numbers match the `── N · ✓ Agent` section headers.
|
|
90
90
|
- Failed and aborted batch runs place their bounded reason on a separate error-colored line below the run headline instead of cutting it into the status row. Reasons are capped at 200 characters and keep their hanging indent when wrapped; successful settled excerpts prefer complete leading sentences within their 96-character budget. Rounded durations switch to minute form at 60 seconds without producing `1m 60s`.
|
|
91
91
|
- Full child transcripts are not stored separately. Bounded run details remain in the parent tool result, so completed calls restore naturally in the parent session tree.
|
|
@@ -12,9 +12,9 @@ The `@astralyn/pi` package uses Pi's native tool transcript presentation rather
|
|
|
12
12
|
│ final result line
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
The dim result rail continues through every visual line; an empty output line renders as a bare `│`. Calls keep their status in the leading dot: warning while pending or running, green after success, and red after failure. For
|
|
15
|
+
The dim result rail continues through every visual line; an empty output line renders as a bare `│`. Calls keep their status in the leading dot: warning while pending or running, green after success, and red after failure. For bash commands that are still running after two seconds, the shell adds a live result row such as `Running… (2.1s)` and removes it when the call settles. Other tools with live progress render it themselves; tools using `renderShell: "self"` continue to own their entire presentation.
|
|
16
16
|
|
|
17
|
-
Time-driven renderers
|
|
17
|
+
Time-driven renderers schedule their own repaints: while a result is partial they arm a timer in renderer state and call the render context's `invalidate()`, clearing the timer on the first settled render. Renderers compute elapsed/countdown values from absolute timestamps or deadlines, so a delayed repaint never changes the underlying wall-clock meaning.
|
|
18
18
|
|
|
19
19
|
## Implementation boundary
|
|
20
20
|
|
package/docs/docs.json
CHANGED
|
@@ -56,10 +56,6 @@
|
|
|
56
56
|
"title": "Overview",
|
|
57
57
|
"path": "bundled/README.md"
|
|
58
58
|
},
|
|
59
|
-
{
|
|
60
|
-
"title": "Biu",
|
|
61
|
-
"path": "bundled/extensions/biu.md"
|
|
62
|
-
},
|
|
63
59
|
{
|
|
64
60
|
"title": "DeepWiki",
|
|
65
61
|
"path": "bundled/extensions/deepwiki.md"
|
|
@@ -68,10 +64,6 @@
|
|
|
68
64
|
"title": "Question",
|
|
69
65
|
"path": "bundled/extensions/question.md"
|
|
70
66
|
},
|
|
71
|
-
{
|
|
72
|
-
"title": "Rewind",
|
|
73
|
-
"path": "bundled/extensions/rewind.md"
|
|
74
|
-
},
|
|
75
67
|
{
|
|
76
68
|
"title": "Router",
|
|
77
69
|
"path": "bundled/extensions/router.md"
|
package/docs/extensions.md
CHANGED
|
@@ -1996,10 +1996,6 @@ pi.registerTool({
|
|
|
1996
1996
|
},
|
|
1997
1997
|
|
|
1998
1998
|
// Optional: Custom rendering
|
|
1999
|
-
// rendersOwnProgress suppresses only the shell's generic Running… row.
|
|
2000
|
-
rendersOwnProgress: true,
|
|
2001
|
-
// While the result is partial, rebuild renderCall/renderResult every second.
|
|
2002
|
-
renderRefreshIntervalMs: 1000,
|
|
2003
1999
|
renderCall(args, theme, context) { ... },
|
|
2004
2000
|
renderResult(result, options, theme, context) { ... },
|
|
2005
2001
|
});
|
|
@@ -2253,12 +2249,7 @@ By default, tool output is wrapped in Pi's native call/result shell, which owns
|
|
|
2253
2249
|
|
|
2254
2250
|
Set `renderShell: "self"` when the tool should render its own shell instead of using the native shell. This is useful for tools that need complete control over framing or background behavior, for example large previews that must stay visually stable after the tool settles.
|
|
2255
2251
|
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
- `rendersOwnProgress: true` suppresses only the native shell's generic `Running… (Ns)` row. It does not schedule renderer updates.
|
|
2259
|
-
- `renderRefreshIntervalMs` periodically rebuilds `renderCall` and `renderResult` while the tool result is partial. It does not suppress generic progress.
|
|
2260
|
-
|
|
2261
|
-
Use both when a tool renders its own elapsed time, retry countdown, or similar progress UI. Refresh intervals are rounded and constrained to 250–60,000ms. The shell may rebuild sooner when another native concern needs it, so the configured interval is the maximum refresh gap rather than an exclusive cadence. Refresh stops when the result settles or the row is disposed.
|
|
2252
|
+
For live progress UI (elapsed time, retry countdowns), schedule repaints from the renderer itself: store a timer handle in `context.state`, arm a `setTimeout` that calls `context.invalidate()` while `options.isPartial` is true, and clear it on the first settled render. `context.invalidate()` is a no-op after the row is disposed, so a trailing timeout is harmless.
|
|
2262
2253
|
|
|
2263
2254
|
Derive displayed time from an absolute timestamp or deadline (`Date.now()`), not by decrementing a counter on every refresh. A refresh is only a repaint opportunity; event-loop stalls and machine sleep must not extend a wall-clock timeout.
|
|
2264
2255
|
|
|
@@ -2368,8 +2359,7 @@ Custom editors and `ctx.ui.custom()` components receive `keybindings: Keybinding
|
|
|
2368
2359
|
- Use `Text` with padding `(0, 0)`. The native shell handles padding.
|
|
2369
2360
|
- Use `\n` for multi-line content.
|
|
2370
2361
|
- Handle `isPartial` for streaming progress.
|
|
2371
|
-
-
|
|
2372
|
-
- Set `rendersOwnProgress` separately when the renderer replaces the native `Running…` row.
|
|
2362
|
+
- For time-driven renderers, self-schedule repaints with `context.state` timers and `context.invalidate()`, and compute displayed values from absolute time.
|
|
2373
2363
|
- Support `expanded` for detail on demand.
|
|
2374
2364
|
- Keep default view compact.
|
|
2375
2365
|
- Read `context.args` in `renderResult` instead of copying args into `context.state`.
|
|
@@ -2542,17 +2532,10 @@ const choice = await ctx.ui.select("Pick one:", ["A", "B", "C"]);
|
|
|
2542
2532
|
|
|
2543
2533
|
// Options can carry a muted description line, so the action and its
|
|
2544
2534
|
// trade-off can be scanned separately. select() still resolves to the label.
|
|
2545
|
-
const action = await ctx.ui.select(
|
|
2546
|
-
"
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
{ label: "Compact, then execute", description: "best for long tasks" },
|
|
2550
|
-
],
|
|
2551
|
-
{
|
|
2552
|
-
subtitle: "Context now 29% full", // muted line under the title
|
|
2553
|
-
cancelHint: "go back", // Describe what Esc actually does
|
|
2554
|
-
},
|
|
2555
|
-
);
|
|
2535
|
+
const action = await ctx.ui.select("Approve?", [
|
|
2536
|
+
{ label: "Start executing", description: "keep full context" },
|
|
2537
|
+
{ label: "Compact, then execute", description: "best for long tasks" },
|
|
2538
|
+
]);
|
|
2556
2539
|
|
|
2557
2540
|
// Confirm dialog
|
|
2558
2541
|
const ok = await ctx.ui.confirm("Delete?", "This cannot be undone");
|
package/docs/settings.md
CHANGED
|
@@ -65,7 +65,7 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
|
|
|
65
65
|
| `outputPad` | number | `1` | Horizontal padding for user messages, assistant messages, and thinking (0 or 1) |
|
|
66
66
|
| `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
|
|
67
67
|
| `showHardwareCursor` | boolean | `false` | Show the terminal cursor while TUI positions it for IME support |
|
|
68
|
-
| `tuiMode` | string | `"
|
|
68
|
+
| `tuiMode` | string | `"fullscreen"` | Interactive TUI mode: `"fullscreen"` or `"regular"`. Changes from `/settings` apply immediately; `--tui-mode` overrides this setting at startup |
|
|
69
69
|
| `fullscreenScrollbar` | string | `"auto"` | Fullscreen transcript scrollbar: `"auto"` shows it temporarily while scrolling, `"always"` reserves the rightmost column and keeps it visible, and `"hidden"` hides it. Has no effect in regular TUI mode |
|
|
70
70
|
|
|
71
71
|
For VS Code, include `--wait` so pi resumes after the editor exits:
|
package/docs/usage.md
CHANGED
|
@@ -37,7 +37,6 @@ Type `/` in the editor to open command completion. Extensions can register custo
|
|
|
37
37
|
| Command | Description |
|
|
38
38
|
|---------|-------------|
|
|
39
39
|
| `/login`, `/logout` | Manage OAuth or API-key credentials |
|
|
40
|
-
| [`/biu`](bundled/extensions/biu.md) | Toggle the TUI-only Biu workflow; inside the mode, open its menu |
|
|
41
40
|
| [`/llama`](llama-cpp.md) | Download, load, and unload llama.cpp router models |
|
|
42
41
|
| `/model` | Switch models |
|
|
43
42
|
| `/scoped-models` | Enable/disable models for Ctrl+P cycling |
|
|
@@ -234,7 +233,7 @@ pi --no-extensions -e ./my-extension.ts
|
|
|
234
233
|
|--------|-------------|
|
|
235
234
|
| `--system-prompt <text>` | Replace default prompt; context files and skills are still appended |
|
|
236
235
|
| `--append-system-prompt <text>` | Append to system prompt |
|
|
237
|
-
| `--tui-mode <mode>` | TUI mode: `
|
|
236
|
+
| `--tui-mode <mode>` | TUI mode: `fullscreen` (default) or `regular` |
|
|
238
237
|
| `--verbose` | Force verbose startup |
|
|
239
238
|
| `-a`, `--approve` | Trust project-local files for this run |
|
|
240
239
|
| `-na`, `--no-approve` | Ignore project-local files for this run |
|
|
@@ -43,12 +43,14 @@ cp permission-gate.ts ~/.pi/agent/extensions/
|
|
|
43
43
|
| `minimal-mode.ts` | Override built-in tool rendering for minimal display (only tool calls, no output in collapsed mode) |
|
|
44
44
|
| `truncated-tool.ts` | Wraps ripgrep with proper output truncation (50KB/2000 lines) |
|
|
45
45
|
| `ssh.ts` | Delegate all tools to a remote machine via SSH using pluggable operations |
|
|
46
|
+
| `subagent/` | Delegate tasks to specialized subagents with isolated context windows |
|
|
46
47
|
|
|
47
48
|
### Commands & UI
|
|
48
49
|
|
|
49
50
|
| Extension | Description |
|
|
50
51
|
|-----------|-------------|
|
|
51
52
|
| `preset.ts` | Named presets for model, thinking level, tools, and instructions via `--preset` flag and `/preset` command |
|
|
53
|
+
| `plan-mode/` | Claude Code-style plan mode for read-only exploration with `/plan` command and step tracking |
|
|
52
54
|
| `tools.ts` | Interactive `/tools` command to enable/disable tools with session persistence |
|
|
53
55
|
| `handoff.ts` | Transfer context to a new focused session via `/handoff <goal>` |
|
|
54
56
|
| `commands.ts` | Lists available slash commands, optionally filtered by source |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Plan Mode Extension
|
|
2
|
+
|
|
3
|
+
Read-only exploration mode for safe code analysis.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Built-in write tools disabled**: Disables edit/write while preserving other active tools
|
|
8
|
+
- **Bash allowlist**: Only read-only bash commands are allowed
|
|
9
|
+
- **Plan extraction**: Extracts numbered steps from `Plan:` sections
|
|
10
|
+
- **Progress tracking**: Widget shows completion status during execution
|
|
11
|
+
- **[DONE:n] markers**: Explicit step completion tracking
|
|
12
|
+
- **Session persistence**: State survives session resume
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
- `/plan` - Toggle plan mode
|
|
17
|
+
- `/todos` - Show current plan progress
|
|
18
|
+
- `Ctrl+Alt+P` - Toggle plan mode (shortcut)
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
1. Enable plan mode with `/plan` or `--plan` flag
|
|
23
|
+
2. Ask the agent to analyze code and create a plan
|
|
24
|
+
3. The agent should output a numbered plan under a `Plan:` header:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Plan:
|
|
28
|
+
1. First step description
|
|
29
|
+
2. Second step description
|
|
30
|
+
3. Third step description
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
4. Choose "Execute the plan" when prompted
|
|
34
|
+
5. During execution, the agent marks steps complete with `[DONE:n]` tags
|
|
35
|
+
6. Progress widget shows completion status
|
|
36
|
+
|
|
37
|
+
## How It Works
|
|
38
|
+
|
|
39
|
+
### Plan Mode (Read-Only)
|
|
40
|
+
- Built-in edit/write tools disabled
|
|
41
|
+
- Other active tools remain available
|
|
42
|
+
- Bash commands filtered through allowlist
|
|
43
|
+
- Agent creates a plan without making changes
|
|
44
|
+
|
|
45
|
+
### Execution Mode
|
|
46
|
+
- Full tool access restored
|
|
47
|
+
- Agent executes steps in order
|
|
48
|
+
- `[DONE:n]` markers track completion
|
|
49
|
+
- Widget shows progress
|
|
50
|
+
|
|
51
|
+
### Command Allowlist
|
|
52
|
+
|
|
53
|
+
Safe commands (allowed):
|
|
54
|
+
- File inspection: `cat`, `head`, `tail`, `less`, `more`
|
|
55
|
+
- Search: `grep`, `find`, `rg`, `fd`
|
|
56
|
+
- Directory: `ls`, `pwd`, `tree`
|
|
57
|
+
- Git read: `git status`, `git log`, `git diff`, `git branch`
|
|
58
|
+
- Package info: `npm list`, `npm outdated`, `yarn info`
|
|
59
|
+
- System info: `uname`, `whoami`, `date`, `uptime`
|
|
60
|
+
|
|
61
|
+
Blocked commands:
|
|
62
|
+
- File modification: `rm`, `mv`, `cp`, `mkdir`, `touch`
|
|
63
|
+
- Git write: `git add`, `git commit`, `git push`
|
|
64
|
+
- Package install: `npm install`, `yarn add`, `pip install`
|
|
65
|
+
- System: `sudo`, `kill`, `reboot`
|
|
66
|
+
- Editors: `vim`, `nano`, `code`
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Mode Extension
|
|
3
|
+
*
|
|
4
|
+
* Read-only exploration mode for safe code analysis.
|
|
5
|
+
* When enabled, built-in write tools are disabled.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - /plan command or Ctrl+Alt+P to toggle
|
|
9
|
+
* - Bash restricted to allowlisted read-only commands
|
|
10
|
+
* - Extracts numbered plan steps from "Plan:" sections
|
|
11
|
+
* - [DONE:n] markers to complete steps during execution
|
|
12
|
+
* - Progress tracking widget during execution
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { ExtensionAPI, ExtensionContext } from "@astralyn/pi";
|
|
16
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
17
|
+
import type { AssistantMessage, TextContent } from "@earendil-works/pi-ai";
|
|
18
|
+
import { Key } from "@earendil-works/pi-tui";
|
|
19
|
+
import { extractTodoItems, isSafeCommand, markCompletedSteps, type TodoItem } from "./utils.ts";
|
|
20
|
+
|
|
21
|
+
// Tools
|
|
22
|
+
const PLAN_MODE_TOOLS = ["read", "bash", "grep", "find", "ls", "questionnaire"];
|
|
23
|
+
const NORMAL_MODE_TOOLS = ["read", "bash", "edit", "write"];
|
|
24
|
+
const PLAN_MODE_DISABLED_TOOLS = new Set<string>(["edit", "write"]);
|
|
25
|
+
const PLAN_MANAGED_TOOLS = new Set<string>([...PLAN_MODE_TOOLS, ...NORMAL_MODE_TOOLS]);
|
|
26
|
+
|
|
27
|
+
interface PlanModeState {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
todos?: TodoItem[];
|
|
30
|
+
executing?: boolean;
|
|
31
|
+
toolsBeforePlanMode?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Type guard for assistant messages
|
|
35
|
+
function isAssistantMessage(m: AgentMessage): m is AssistantMessage {
|
|
36
|
+
return m.role === "assistant" && Array.isArray(m.content);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Extract text content from an assistant message
|
|
40
|
+
function getTextContent(message: AssistantMessage): string {
|
|
41
|
+
return message.content
|
|
42
|
+
.filter((block): block is TextContent => block.type === "text")
|
|
43
|
+
.map((block) => block.text)
|
|
44
|
+
.join("\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function planModeExtension(pi: ExtensionAPI): void {
|
|
48
|
+
let planModeEnabled = false;
|
|
49
|
+
let executionMode = false;
|
|
50
|
+
let todoItems: TodoItem[] = [];
|
|
51
|
+
let toolsBeforePlanMode: string[] | undefined;
|
|
52
|
+
|
|
53
|
+
pi.registerFlag("plan", {
|
|
54
|
+
description: "Start in plan mode (read-only exploration)",
|
|
55
|
+
type: "boolean",
|
|
56
|
+
default: false,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function updateStatus(ctx: ExtensionContext): void {
|
|
60
|
+
// Footer status
|
|
61
|
+
if (executionMode && todoItems.length > 0) {
|
|
62
|
+
const completed = todoItems.filter((t) => t.completed).length;
|
|
63
|
+
ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("accent", `📋 ${completed}/${todoItems.length}`));
|
|
64
|
+
} else if (planModeEnabled) {
|
|
65
|
+
ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("warning", "⏸ plan"));
|
|
66
|
+
} else {
|
|
67
|
+
ctx.ui.setStatus("plan-mode", undefined);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Widget showing todo list
|
|
71
|
+
if (executionMode && todoItems.length > 0) {
|
|
72
|
+
const lines = todoItems.map((item) => {
|
|
73
|
+
if (item.completed) {
|
|
74
|
+
return (
|
|
75
|
+
ctx.ui.theme.fg("success", "☑ ") + ctx.ui.theme.fg("muted", ctx.ui.theme.strikethrough(item.text))
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return `${ctx.ui.theme.fg("muted", "☐ ")}${item.text}`;
|
|
79
|
+
});
|
|
80
|
+
ctx.ui.setWidget("plan-todos", lines);
|
|
81
|
+
} else {
|
|
82
|
+
ctx.ui.setWidget("plan-todos", undefined);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function uniqueToolNames(toolNames: string[]): string[] {
|
|
87
|
+
return [...new Set(toolNames)];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getPlanModeTools(activeToolNames: string[]): string[] {
|
|
91
|
+
return uniqueToolNames([
|
|
92
|
+
...activeToolNames.filter((name) => !PLAN_MODE_DISABLED_TOOLS.has(name)),
|
|
93
|
+
...PLAN_MODE_TOOLS,
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getNormalModeTools(activeToolNames: string[]): string[] {
|
|
98
|
+
return uniqueToolNames([
|
|
99
|
+
...NORMAL_MODE_TOOLS,
|
|
100
|
+
...activeToolNames.filter((name) => !PLAN_MANAGED_TOOLS.has(name)),
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function enablePlanModeTools(): void {
|
|
105
|
+
if (toolsBeforePlanMode === undefined) {
|
|
106
|
+
toolsBeforePlanMode = pi.getActiveTools();
|
|
107
|
+
}
|
|
108
|
+
pi.setActiveTools(getPlanModeTools(toolsBeforePlanMode));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function restoreNormalModeTools(): void {
|
|
112
|
+
pi.setActiveTools(toolsBeforePlanMode ?? getNormalModeTools(pi.getActiveTools()));
|
|
113
|
+
toolsBeforePlanMode = undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function persistState(): void {
|
|
117
|
+
pi.appendEntry("plan-mode", {
|
|
118
|
+
enabled: planModeEnabled,
|
|
119
|
+
todos: todoItems,
|
|
120
|
+
executing: executionMode,
|
|
121
|
+
toolsBeforePlanMode,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function togglePlanMode(ctx: ExtensionContext): void {
|
|
126
|
+
planModeEnabled = !planModeEnabled;
|
|
127
|
+
executionMode = false;
|
|
128
|
+
todoItems = [];
|
|
129
|
+
|
|
130
|
+
if (planModeEnabled) {
|
|
131
|
+
enablePlanModeTools();
|
|
132
|
+
ctx.ui.notify("Plan mode enabled. Built-in write tools disabled.");
|
|
133
|
+
} else {
|
|
134
|
+
restoreNormalModeTools();
|
|
135
|
+
ctx.ui.notify("Plan mode disabled. Full access restored.");
|
|
136
|
+
}
|
|
137
|
+
updateStatus(ctx);
|
|
138
|
+
persistState();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
pi.registerCommand("plan", {
|
|
142
|
+
description: "Toggle plan mode (read-only exploration)",
|
|
143
|
+
handler: async (_args, ctx) => togglePlanMode(ctx),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
pi.registerCommand("todos", {
|
|
147
|
+
description: "Show current plan todo list",
|
|
148
|
+
handler: async (_args, ctx) => {
|
|
149
|
+
if (todoItems.length === 0) {
|
|
150
|
+
ctx.ui.notify("No todos. Create a plan first with /plan", "info");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const list = todoItems.map((item, i) => `${i + 1}. ${item.completed ? "✓" : "○"} ${item.text}`).join("\n");
|
|
154
|
+
ctx.ui.notify(`Plan Progress:\n${list}`, "info");
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
pi.registerShortcut(Key.ctrlAlt("p"), {
|
|
159
|
+
description: "Toggle plan mode",
|
|
160
|
+
handler: async (ctx) => togglePlanMode(ctx),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Block destructive bash commands in plan mode
|
|
164
|
+
pi.on("tool_call", async (event) => {
|
|
165
|
+
if (!planModeEnabled || event.toolName !== "bash") return;
|
|
166
|
+
|
|
167
|
+
const command = event.input.command as string;
|
|
168
|
+
if (!isSafeCommand(command)) {
|
|
169
|
+
return {
|
|
170
|
+
block: true,
|
|
171
|
+
reason: `Plan mode: command blocked (not allowlisted). Use /plan to disable plan mode first.\nCommand: ${command}`,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Filter out stale plan mode context when not in plan mode
|
|
177
|
+
pi.on("context", async (event) => {
|
|
178
|
+
if (planModeEnabled) return;
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
messages: event.messages.filter((m) => {
|
|
182
|
+
const msg = m as AgentMessage & { customType?: string };
|
|
183
|
+
if (msg.customType === "plan-mode-context") return false;
|
|
184
|
+
if (msg.role !== "user") return true;
|
|
185
|
+
|
|
186
|
+
const content = msg.content;
|
|
187
|
+
if (typeof content === "string") {
|
|
188
|
+
return !content.includes("[PLAN MODE ACTIVE]");
|
|
189
|
+
}
|
|
190
|
+
if (Array.isArray(content)) {
|
|
191
|
+
return !content.some(
|
|
192
|
+
(c) => c.type === "text" && (c as TextContent).text?.includes("[PLAN MODE ACTIVE]"),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}),
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Inject plan/execution context before agent starts
|
|
201
|
+
pi.on("before_agent_start", async () => {
|
|
202
|
+
if (planModeEnabled) {
|
|
203
|
+
return {
|
|
204
|
+
message: {
|
|
205
|
+
customType: "plan-mode-context",
|
|
206
|
+
content: `[PLAN MODE ACTIVE]
|
|
207
|
+
You are in plan mode - a read-only exploration mode for safe code analysis.
|
|
208
|
+
|
|
209
|
+
Restrictions:
|
|
210
|
+
- Built-in edit and write tools are disabled
|
|
211
|
+
- Other currently active tools remain available
|
|
212
|
+
- Bash is restricted to an allowlist of read-only commands
|
|
213
|
+
|
|
214
|
+
Ask clarifying questions using the questionnaire tool.
|
|
215
|
+
Use brave-search skill via bash for web research.
|
|
216
|
+
|
|
217
|
+
Create a detailed numbered plan under a "Plan:" header:
|
|
218
|
+
|
|
219
|
+
Plan:
|
|
220
|
+
1. First step description
|
|
221
|
+
2. Second step description
|
|
222
|
+
...
|
|
223
|
+
|
|
224
|
+
Do NOT attempt to make changes - just describe what you would do.`,
|
|
225
|
+
display: false,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (executionMode && todoItems.length > 0) {
|
|
231
|
+
const remaining = todoItems.filter((t) => !t.completed);
|
|
232
|
+
const todoList = remaining.map((t) => `${t.step}. ${t.text}`).join("\n");
|
|
233
|
+
return {
|
|
234
|
+
message: {
|
|
235
|
+
customType: "plan-execution-context",
|
|
236
|
+
content: `[EXECUTING PLAN - Full tool access enabled]
|
|
237
|
+
|
|
238
|
+
Remaining steps:
|
|
239
|
+
${todoList}
|
|
240
|
+
|
|
241
|
+
Execute each step in order.
|
|
242
|
+
After completing a step, include a [DONE:n] tag in your response.`,
|
|
243
|
+
display: false,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Track progress after each turn
|
|
250
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
251
|
+
if (!executionMode || todoItems.length === 0) return;
|
|
252
|
+
if (!isAssistantMessage(event.message)) return;
|
|
253
|
+
|
|
254
|
+
const text = getTextContent(event.message);
|
|
255
|
+
if (markCompletedSteps(text, todoItems) > 0) {
|
|
256
|
+
updateStatus(ctx);
|
|
257
|
+
}
|
|
258
|
+
persistState();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Handle plan completion and plan mode UI
|
|
262
|
+
pi.on("agent_end", async (event, ctx) => {
|
|
263
|
+
// Check if execution is complete
|
|
264
|
+
if (executionMode && todoItems.length > 0) {
|
|
265
|
+
if (todoItems.every((t) => t.completed)) {
|
|
266
|
+
const completedList = todoItems.map((t) => `~~${t.text}~~`).join("\n");
|
|
267
|
+
pi.sendMessage(
|
|
268
|
+
{ customType: "plan-complete", content: `**Plan Complete!** ✓\n\n${completedList}`, display: true },
|
|
269
|
+
{ triggerTurn: false },
|
|
270
|
+
);
|
|
271
|
+
executionMode = false;
|
|
272
|
+
todoItems = [];
|
|
273
|
+
updateStatus(ctx);
|
|
274
|
+
persistState(); // Save cleared state so resume doesn't restore old execution mode
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (!planModeEnabled || !ctx.hasUI) return;
|
|
280
|
+
|
|
281
|
+
// Extract todos from last assistant message
|
|
282
|
+
const lastAssistant = [...event.messages].reverse().find(isAssistantMessage);
|
|
283
|
+
if (lastAssistant) {
|
|
284
|
+
const extracted = extractTodoItems(getTextContent(lastAssistant));
|
|
285
|
+
if (extracted.length > 0) {
|
|
286
|
+
todoItems = extracted;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (todoItems.length === 0) return;
|
|
291
|
+
persistState();
|
|
292
|
+
|
|
293
|
+
// Show plan steps and prompt for next action
|
|
294
|
+
const todoListText = todoItems.map((t, i) => `${i + 1}. ☐ ${t.text}`).join("\n");
|
|
295
|
+
const planTodoListMessage = {
|
|
296
|
+
customType: "plan-todo-list",
|
|
297
|
+
content: `**Plan Steps (${todoItems.length}):**\n\n${todoListText}`,
|
|
298
|
+
display: true,
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const choice = await ctx.ui.select("Plan mode - what next?", [
|
|
302
|
+
"Execute the plan (track progress)",
|
|
303
|
+
"Stay in plan mode",
|
|
304
|
+
"Refine the plan",
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
if (choice?.startsWith("Execute")) {
|
|
308
|
+
const firstTodoItem = todoItems[0];
|
|
309
|
+
if (!firstTodoItem) return;
|
|
310
|
+
|
|
311
|
+
planModeEnabled = false;
|
|
312
|
+
executionMode = true;
|
|
313
|
+
restoreNormalModeTools();
|
|
314
|
+
updateStatus(ctx);
|
|
315
|
+
persistState();
|
|
316
|
+
|
|
317
|
+
const remainingList = todoItems.map((t) => `${t.step}. ${t.text}`).join("\n");
|
|
318
|
+
const execMessage = `Execute the plan.
|
|
319
|
+
|
|
320
|
+
Remaining steps:
|
|
321
|
+
${remainingList}
|
|
322
|
+
|
|
323
|
+
Start with: ${firstTodoItem.text}
|
|
324
|
+
After completing a step, include a [DONE:n] tag in your response.`;
|
|
325
|
+
pi.sendMessage(planTodoListMessage, { deliverAs: "followUp" });
|
|
326
|
+
pi.sendMessage(
|
|
327
|
+
{ customType: "plan-mode-execute", content: execMessage, display: true },
|
|
328
|
+
{ triggerTurn: true, deliverAs: "followUp" },
|
|
329
|
+
);
|
|
330
|
+
} else if (choice === "Refine the plan") {
|
|
331
|
+
const refinement = await ctx.ui.editor("Refine the plan:", "");
|
|
332
|
+
if (refinement?.trim()) {
|
|
333
|
+
pi.sendMessage(planTodoListMessage, { deliverAs: "followUp" });
|
|
334
|
+
pi.sendUserMessage(refinement.trim(), { deliverAs: "followUp" });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Restore state on session start/resume
|
|
340
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
341
|
+
if (pi.getFlag("plan") === true) {
|
|
342
|
+
planModeEnabled = true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const entries = ctx.sessionManager.getEntries();
|
|
346
|
+
|
|
347
|
+
// Restore persisted state
|
|
348
|
+
const planModeEntry = entries
|
|
349
|
+
.filter((e: { type: string; customType?: string }) => e.type === "custom" && e.customType === "plan-mode")
|
|
350
|
+
.pop() as { data?: PlanModeState } | undefined;
|
|
351
|
+
|
|
352
|
+
if (planModeEntry?.data) {
|
|
353
|
+
planModeEnabled = planModeEntry.data.enabled ?? planModeEnabled;
|
|
354
|
+
todoItems = planModeEntry.data.todos ?? todoItems;
|
|
355
|
+
executionMode = planModeEntry.data.executing ?? executionMode;
|
|
356
|
+
toolsBeforePlanMode = planModeEntry.data.toolsBeforePlanMode ?? toolsBeforePlanMode;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// On resume: re-scan messages to rebuild completion state
|
|
360
|
+
// Only scan messages AFTER the last "plan-mode-execute" to avoid picking up [DONE:n] from previous plans
|
|
361
|
+
const isResume = planModeEntry !== undefined;
|
|
362
|
+
if (isResume && executionMode && todoItems.length > 0) {
|
|
363
|
+
// Find the index of the last plan-mode-execute entry (marks when current execution started)
|
|
364
|
+
let executeIndex = -1;
|
|
365
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
366
|
+
const entry = entries[i] as { type: string; customType?: string };
|
|
367
|
+
if (entry.customType === "plan-mode-execute") {
|
|
368
|
+
executeIndex = i;
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Only scan messages after the execute marker
|
|
374
|
+
const messages: AssistantMessage[] = [];
|
|
375
|
+
for (let i = executeIndex + 1; i < entries.length; i++) {
|
|
376
|
+
const entry = entries[i];
|
|
377
|
+
if (entry.type === "message" && "message" in entry && isAssistantMessage(entry.message as AgentMessage)) {
|
|
378
|
+
messages.push(entry.message as AssistantMessage);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const allText = messages.map(getTextContent).join("\n");
|
|
382
|
+
markCompletedSteps(allText, todoItems);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (planModeEnabled) {
|
|
386
|
+
enablePlanModeTools();
|
|
387
|
+
}
|
|
388
|
+
updateStatus(ctx);
|
|
389
|
+
});
|
|
390
|
+
}
|