@gavin-hu/my-pi-agent 0.21.0 → 0.22.0
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 +46 -31
- package/README.zh-CN.md +40 -26
- package/assets/screens/board.png +0 -0
- package/assets/screens/chrome.png +0 -0
- package/assets/screens/file-browse.png +0 -0
- package/assets/screens/plan-review.png +0 -0
- package/assets/screens/plan.png +0 -0
- package/docs/extensions/README.md +22 -24
- package/docs/extensions/board.md +361 -147
- package/docs/extensions/bytefly.md +152 -111
- package/docs/extensions/check.md +15 -17
- package/docs/extensions/context.md +35 -15
- package/docs/extensions/file-browse.md +30 -31
- package/docs/extensions/file-open.md +9 -11
- package/docs/extensions/file-read.md +12 -14
- package/docs/extensions/goal.md +15 -17
- package/docs/extensions/job.md +54 -42
- package/docs/extensions/memory.md +17 -19
- package/docs/extensions/plan.md +166 -126
- package/docs/extensions/question.md +16 -18
- package/docs/extensions/rewind.md +17 -19
- package/docs/extensions/serve.md +34 -18
- package/docs/extensions/session-bus.md +38 -16
- package/docs/extensions/subagent.md +23 -25
- package/docs/extensions/team.md +72 -33
- package/docs/extensions/todo.md +18 -18
- package/docs/extensions/watch.md +31 -31
- package/docs/extensions/web-access.md +2 -4
- package/docs/extensions/wechat.md +34 -35
- package/docs/extensions/worktree.md +56 -50
- package/extensions/board/commands.ts +56 -15
- package/extensions/board/format.ts +43 -24
- package/extensions/board/index.ts +5 -4
- package/extensions/board/input.ts +4 -9
- package/extensions/board/join.ts +4 -40
- package/extensions/board/lazy-page.ts +40 -0
- package/extensions/board/page.ts +13 -11
- package/extensions/board/result.ts +30 -16
- package/extensions/board/rows.ts +62 -25
- package/extensions/board/schema.ts +42 -10
- package/extensions/board/snapshot.ts +2 -1
- package/extensions/board/tone.ts +22 -0
- package/extensions/board/tools.ts +131 -44
- package/extensions/board/types.ts +36 -32
- package/extensions/board/view/card.ts +229 -0
- package/extensions/board/view/client.ts +33 -15
- package/extensions/board/view/dialog.ts +169 -6
- package/extensions/board/view/empty.ts +47 -0
- package/extensions/board/view/keys.ts +13 -2
- package/extensions/board/view/live.ts +89 -21
- package/extensions/board/view/names.ts +13 -4
- package/extensions/board/view/page.ts +23 -9
- package/extensions/board/view/render.ts +208 -271
- package/extensions/board/view/styles.ts +123 -31
- package/extensions/board/view/team.ts +297 -76
- package/extensions/bytefly/block/fallback.ts +1 -1
- package/extensions/bytefly/doctor/runtime.ts +5 -2
- package/extensions/bytefly/footer/collect/label.ts +60 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
- package/extensions/bytefly/footer/config.ts +4 -2
- package/extensions/bytefly/footer/register.ts +33 -4
- package/extensions/bytefly/footer/render/lines.ts +7 -4
- package/extensions/bytefly/footer/render/segments.ts +37 -10
- package/extensions/bytefly/footer/render/status.ts +0 -11
- package/extensions/bytefly/footer/types.ts +7 -0
- package/extensions/bytefly/header/config.ts +1 -1
- package/extensions/bytefly/index.ts +32 -6
- package/extensions/bytefly/mode/editor.ts +115 -0
- package/extensions/bytefly/panel/commands.ts +210 -120
- package/extensions/bytefly/panel/format.ts +4 -4
- package/extensions/bytefly/panel/settings.ts +73 -6
- package/extensions/bytefly/panel/tui/keys.ts +2 -6
- package/extensions/bytefly/update/cache.ts +1 -1
- package/extensions/bytefly/update/register.ts +4 -4
- package/extensions/bytefly/working/config.ts +5 -7
- package/extensions/bytefly/working/format.ts +10 -13
- package/extensions/bytefly/working/register.ts +22 -27
- package/extensions/check/commands.ts +3 -2
- package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
- package/extensions/context/commands.ts +2 -2
- package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
- package/extensions/file-browse/index.ts +67 -36
- package/extensions/file-read/render.ts +4 -4
- package/extensions/goal/plan-route.ts +2 -2
- package/extensions/job/child.ts +527 -0
- package/extensions/job/clock.ts +142 -0
- package/extensions/job/commands.ts +4 -2
- package/extensions/job/config.ts +8 -1
- package/extensions/job/format.ts +22 -1
- package/extensions/job/log-writer.ts +13 -2
- package/extensions/job/registry.ts +63 -7
- package/extensions/job/render.ts +34 -0
- package/extensions/job/runtime.ts +296 -655
- package/extensions/job/session.ts +132 -19
- package/extensions/job/store.ts +7 -7
- package/extensions/job/tools.ts +3 -3
- package/extensions/job/types.ts +47 -0
- package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -73
- package/extensions/job/waiters.ts +44 -1
- package/extensions/memory/commands.ts +4 -2
- package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
- package/extensions/plan/commands.ts +12 -9
- package/extensions/plan/format.ts +8 -2
- package/extensions/plan/git-bash.ts +115 -16
- package/extensions/plan/index.ts +53 -52
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plans.ts +5 -54
- package/extensions/plan/policy.ts +8 -10
- package/extensions/plan/review-lazy.ts +85 -0
- package/extensions/plan/review.ts +2 -2
- package/extensions/plan/runtime.ts +49 -39
- package/extensions/plan/sections.ts +1 -1
- package/extensions/plan/steps.ts +3 -3
- package/extensions/plan/todos.ts +1 -1
- package/extensions/plan/tools/cancel.ts +27 -62
- package/extensions/plan/tools/enter.ts +55 -81
- package/extensions/plan/tools/exit.ts +221 -238
- package/extensions/plan/tools/refusal.ts +18 -0
- package/extensions/plan/tools/steps.ts +75 -92
- package/extensions/plan/tools/write.ts +77 -120
- package/extensions/plan/tools.ts +188 -30
- package/extensions/plan/tui.ts +5 -5
- package/extensions/plan/types.ts +48 -5
- package/extensions/plan/web-client.ts +3 -0
- package/extensions/plan/web-lazy.ts +53 -0
- package/extensions/plan/web-markdown.ts +9 -67
- package/extensions/plan/web-payload.ts +3 -3
- package/extensions/plan/web-router.ts +18 -9
- package/extensions/plan/web.ts +1 -12
- package/extensions/question/tools.ts +6 -2
- package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
- package/extensions/rewind/commands/commands.ts +6 -3
- package/extensions/rewind/state/runtime.ts +1 -1
- package/extensions/rewind/{view/status.ts → status.ts} +4 -4
- package/extensions/rewind/view/review-tui.ts +4 -4
- package/extensions/serve/config.ts +10 -8
- package/extensions/serve/index.ts +17 -3
- package/extensions/serve/registry.ts +26 -17
- package/extensions/session-bus/commands.ts +11 -6
- package/extensions/session-bus/join-request.ts +2 -1
- package/extensions/session-bus/mail-watch.ts +26 -17
- package/extensions/session-bus/protocol.ts +15 -3
- package/extensions/session-bus/resolve.ts +5 -7
- package/extensions/session-bus/runtime.ts +40 -12
- package/extensions/session-bus/store.ts +101 -19
- package/extensions/session-bus/types.ts +19 -0
- package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
- package/extensions/subagent/agents.ts +1 -1
- package/extensions/subagent/commands.ts +4 -2
- package/extensions/subagent/format.ts +15 -15
- package/extensions/subagent/index.ts +1 -1
- package/extensions/subagent/render.ts +3 -3
- package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
- package/extensions/team/commands.ts +24 -7
- package/extensions/team/dispatch.ts +18 -4
- package/extensions/team/format.ts +17 -2
- package/extensions/team/index.ts +9 -8
- package/extensions/team/schema.ts +6 -5
- package/extensions/team/tools.ts +107 -45
- package/extensions/team/types.ts +3 -2
- package/extensions/todo/commands.ts +4 -2
- package/extensions/todo/format.ts +11 -0
- package/extensions/todo/render.ts +57 -0
- package/extensions/todo/runtime.ts +1 -1
- package/extensions/todo/tools.ts +1 -1
- package/extensions/todo/view/todo-screen.ts +122 -0
- package/extensions/todo/widget.ts +47 -0
- package/extensions/watch/commands.ts +4 -2
- package/extensions/watch/format.ts +36 -7
- package/extensions/watch/render.ts +57 -0
- package/extensions/watch/runtime.ts +27 -22
- package/extensions/watch/tools.ts +4 -4
- package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
- package/extensions/web-access/fetch/tool.ts +12 -4
- package/extensions/wechat/commands/commands.ts +18 -14
- package/extensions/wechat/index.ts +72 -37
- package/extensions/wechat/state/credentials.ts +1 -1
- package/extensions/wechat/state/state.ts +1 -1
- package/extensions/wechat/text/status.ts +112 -0
- package/extensions/wechat/types.ts +2 -2
- package/extensions/wechat/view/status-screen.ts +4 -103
- package/extensions/worktree/commands.ts +11 -9
- package/extensions/worktree/enter.ts +2 -2
- package/extensions/worktree/exit.ts +2 -2
- package/extensions/worktree/index.ts +5 -5
- package/extensions/worktree/screen.ts +3 -3
- package/extensions/worktree/status.ts +2 -2
- package/extensions/worktree/tools.ts +113 -116
- package/lib/README.md +27 -13
- package/lib/board/actions.ts +111 -0
- package/lib/board/artifact.ts +149 -0
- package/lib/board/card.ts +163 -52
- package/lib/board/config.ts +58 -0
- package/lib/board/contract.ts +113 -0
- package/lib/board/join.ts +38 -0
- package/lib/board/lanes.ts +182 -0
- package/lib/board/lead.ts +130 -0
- package/lib/board/limits.ts +78 -0
- package/lib/board/links.ts +31 -10
- package/lib/board/page-pointer.ts +1 -1
- package/lib/board/read.ts +83 -0
- package/lib/board/reason.ts +102 -0
- package/lib/board/round-text.ts +68 -6
- package/lib/board/round.ts +31 -15
- package/lib/board/rules.ts +151 -307
- package/lib/board/seat.ts +35 -0
- package/lib/board/session.ts +136 -54
- package/lib/board/skipped.ts +22 -0
- package/lib/board/steps.ts +487 -0
- package/lib/board/store.ts +87 -30
- package/lib/board/tone.ts +92 -0
- package/lib/board/view.ts +70 -58
- package/{extensions/watch → lib}/child.ts +40 -21
- package/lib/env.ts +65 -7
- package/lib/format.ts +13 -0
- package/lib/http/index.ts +14 -12
- package/lib/http/port.ts +15 -10
- package/lib/lazy.ts +45 -0
- package/lib/plan-entry.ts +39 -10
- package/lib/plans/paths.ts +82 -0
- package/lib/policy.ts +9 -6
- package/lib/report.ts +3 -2
- package/lib/requirements.ts +8 -8
- package/lib/session-label.ts +111 -5
- package/lib/session-roster.ts +43 -1
- package/lib/session-tool.ts +5 -4
- package/lib/stray-fields.ts +7 -2
- package/lib/tool-names.ts +5 -10
- package/lib/tui/README.md +23 -8
- package/lib/tui/chrome.ts +16 -15
- package/lib/tui/cursor.ts +18 -3
- package/lib/tui/glyphs.ts +9 -7
- package/lib/tui/tokens.ts +33 -2
- package/lib/tui/tool.ts +3 -3
- package/lib/web/README.md +21 -0
- package/lib/web/components.ts +5 -1
- package/lib/web/index.ts +1 -0
- package/lib/web/markdown.ts +83 -0
- package/lib/web/markup.ts +2 -1
- package/lib/web/runtime.ts +9 -10
- package/package.json +6 -1
- package/skills/init/SKILL.md +5 -5
- package/skills/next/SKILL.md +3 -5
- package/extensions/board/seat.ts +0 -28
- package/extensions/todo/tui.ts +0 -212
- package/lib/tui/index.ts +0 -34
- package/skills/project-docs/SKILL.md +0 -133
- package/skills/project-docs/references/audit.md +0 -131
- package/skills/project-docs/references/changelog.md +0 -105
- package/skills/project-docs/references/discovery.md +0 -127
- package/skills/project-docs/references/readme.md +0 -80
- package/skills/project-docs/references/release.md +0 -84
- package/skills/project-docs/references/roadmap.md +0 -93
- package/skills/project-docs/scripts/state.ts +0 -316
package/docs/extensions/plan.md
CHANGED
|
@@ -8,52 +8,50 @@ executes.
|
|
|
8
8
|
|
|
9
9
|
## Capabilities
|
|
10
10
|
|
|
11
|
-
Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
|
|
12
|
-
|
|
13
11
|
### Planning lifecycle
|
|
14
12
|
|
|
15
|
-
| Capability | How | What you get |
|
|
16
|
-
|
|
17
|
-
| Read-only planning | `
|
|
18
|
-
| Confirmed model entry | `
|
|
19
|
-
| Read-only git shell | `bash` with the read-only git allowlist | Plan mode can inspect `git status`, `diff`, `log`, and `show`; pipes, redirects, and non-git commands are refused. |
|
|
20
|
-
| Read-only delegation | `subagent` with forced `readOnly: true` | A delegated agent can read and search but cannot write, edit, or run a shell while the plan is being researched. |
|
|
21
|
-
| Absolute-path guard | `path-guard.ts` | A path outside the project is refused unless it is a known plan-safe reader, so planning cannot write through an absolute path. |
|
|
22
|
-
| One writable artifact | `
|
|
23
|
-
| Approval gate | `
|
|
24
|
-
| Session goal at approval | `goal` at approval | A `planning` goal is promoted to the plan's objective, and a session with none gets it, without a second dialog; an active goal is left alone. |
|
|
25
|
-
| Steps as todos | `
|
|
26
|
-
| Plan kept current | `
|
|
27
|
-
| Stale-step protection | `expected` in `
|
|
28
|
-
| Cancel without a plan | `
|
|
29
|
-
| Plans index | `/plan list` | Every saved plan with its title, modified time, and step progress, newest first, under a header naming the store's size and its oldest plan. |
|
|
30
|
-
| Plan-store migration | `<repo>/.pi/plans` to the agent-dir store | A store left by an older version is moved in once, with a notice, so worktrees and main see the same index. |
|
|
31
|
-
| Instruction-file bootstrap | `/skill:init` | The packaged `init` skill plans and gates a project's `AGENTS.md`, writing only after approval. |
|
|
32
|
-
| Branch-following mode | the `plan-mode` session entry | `/resume` and `/tree` restore the mode and its tool set for the branch they land on. |
|
|
13
|
+
| Capability | How | What you get |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Read-only planning | `plan` action `enter`, `/plan`, `Shift+Tab` (bytefly), `--plan` | The session keeps only its readers, so the model investigates and proposes without touching the tree. |
|
|
16
|
+
| Confirmed model entry | `plan` action `enter` | The model asks before it takes write access away, and the user answers first. |
|
|
17
|
+
| Read-only git shell | `bash` with the read-only git allowlist | Plan mode can inspect `git status`, `diff`, `log`, and `show`; pipes, redirects, and non-git commands are refused. |
|
|
18
|
+
| Read-only delegation | `subagent` with forced `readOnly: true` | A delegated agent can read and search but cannot write, edit, or run a shell while the plan is being researched. |
|
|
19
|
+
| Absolute-path guard | `path-guard.ts` | A path outside the project is refused unless it is a known plan-safe reader, so planning cannot write through an absolute path. |
|
|
20
|
+
| One writable artifact | `plan` action `write` | The plan lands as markdown under the agent directory, one store per repository, shared by its worktrees. |
|
|
21
|
+
| Approval gate | `plan` action `exit` | The plan is read back and presented; nothing is written until the user approves it. |
|
|
22
|
+
| Session goal at approval | `goal` at approval | A `planning` goal is promoted to the plan's objective, and a session with none gets it, without a second dialog; an active goal is left alone. |
|
|
23
|
+
| Steps as todos | `plan` action `exit` | Approval seeds the plan's steps into the todo list, with the verification section as the last item; approving the same plan again replaces the items its earlier approval seeded. |
|
|
24
|
+
| Plan kept current | `plan` action `steps` | Ticking a step rewrites its checkbox in the file and its todo item in one call. |
|
|
25
|
+
| Stale-step protection | `expected` in `plan` action `steps` | A step whose number moved, is out of range, or no longer matches its text refuses the whole batch instead of ticking the wrong line. |
|
|
26
|
+
| Cancel without a plan | `plan` action `cancel`, `/plan cancel` | Write access returns, and no todo items or goal are recorded. |
|
|
27
|
+
| Plans index | `/plan list` | Every saved plan with its title, modified time, and step progress, newest first, under a header naming the store's size and its oldest plan. |
|
|
28
|
+
| Plan-store migration | `<repo>/.pi/plans` to the agent-dir store | A store left by an older version is moved in once, with a notice, so worktrees and main see the same index. |
|
|
29
|
+
| Instruction-file bootstrap | `/skill:init` | The packaged `init` skill plans and gates a project's `AGENTS.md`, writing only after approval. |
|
|
30
|
+
| Branch-following mode | the `plan-mode` session entry | `/resume` and `/tree` restore the mode and its tool set for the branch they land on. |
|
|
33
31
|
|
|
34
32
|
### Review
|
|
35
33
|
|
|
36
34
|
| Capability | How | What you get | Status |
|
|
37
35
|
|---|---|---|---|
|
|
38
|
-
| Review screen | `
|
|
36
|
+
| Review screen | `plan` action `exit` | A scrollable, themed plan with approve, refine, keep, browser, and exit in the footer. | Shipped |
|
|
39
37
|
| Browser page | `/plan review`, `w` on the review screen | The plan opens in a local browser as a reading-first page with live section and step progress. | Partial |
|
|
40
38
|
| Approval in the browser | the `/plan review` page's **Approve & run** | The plan is approved without returning to the terminal, and the steps are seeded. | Shipped |
|
|
41
39
|
| Inline comments | the page's `+ Comment`, **Save feedback** | Comments are saved into the plan file as `> [!REVIEW]` lines and returned to the model. | Shipped |
|
|
42
40
|
| Live page | the page's `events` stream, the plan-file poll | An edit made elsewhere repaints every open page without a reload. | Shipped |
|
|
43
|
-
| Changed sections | the `
|
|
41
|
+
| Changed sections | the `exit` review screen, the `/plan review` outline | After a refinement, only the sections that changed are marked. | Shipped |
|
|
44
42
|
| Block-level change marks | the `/plan review` page's **Changed** view | After a refinement, each block that is new or edited is marked — an inserted step marks itself and leaves the rest of its section alone — and a section that lost blocks says how much shorter it became. **All** / **Changed** / **Comments** switch what the page shows, `v` cycles them, `n`/`N` walk the blocks that carry a comment, and a printed review is always the whole plan. | Unreleased |
|
|
45
43
|
| Settled comments | the page's `Resolve` / `Reopen` on a note you wrote | The note keeps its place, its quote and its history and is written `> [!REVIEW resolved]`, where a later review and the model read it as closed; the panel counts `2 open · 1 resolved`, and every card names who wrote it. | Unreleased |
|
|
46
44
|
| Section navigation | the review screen, `n`/`p` | The screen names the section you are in and jumps between headings. | Shipped |
|
|
47
|
-
| Remote review | `
|
|
45
|
+
| Remote review | `plan` action `exit` on a WeChat turn | The plan file is sent, then one compact prompt to approve, keep planning, or exit. | Shipped |
|
|
48
46
|
|
|
49
47
|
### Collaboration
|
|
50
48
|
|
|
51
|
-
| Capability | How | What you get |
|
|
52
|
-
|
|
53
|
-
| Shared review | `/plan share`, **Share** | A reviewer link, with a QR a phone can scan, opens the same plan from another machine on this Wi-Fi. |
|
|
54
|
-
| One link per teammate | **New link**, **Revoke**, `/plan share stop` | Each teammate holds a link of their own, and one is revoked without disturbing the rest. |
|
|
55
|
-
| Two roles | the owner's `token`, a reviewer's link | Only the owner saves, approves, cancels, and manages links; a reviewer reads and comments. |
|
|
56
|
-
| Signed comments | the page's `name` control | A reviewer's name binds to her link on its first post, so her notes are hers and the file records who wrote them. |
|
|
49
|
+
| Capability | How | What you get |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| Shared review | `/plan share`, **Share** | A reviewer link, with a QR a phone can scan, opens the same plan from another machine on this Wi-Fi. |
|
|
52
|
+
| One link per teammate | **New link**, **Revoke**, `/plan share stop` | Each teammate holds a link of their own, and one is revoked without disturbing the rest. |
|
|
53
|
+
| Two roles | the owner's `token`, a reviewer's link | Only the owner saves, approves, cancels, and manages links; a reviewer reads and comments. |
|
|
54
|
+
| Signed comments | the page's `name` control | A reviewer's name binds to her link on its first post, so her notes are hers and the file records who wrote them. |
|
|
57
55
|
|
|
58
56
|
## Quickstart
|
|
59
57
|
|
|
@@ -66,34 +64,43 @@ pi install ./ # install the package for a user
|
|
|
66
64
|
```text
|
|
67
65
|
/plan # toggle plan mode
|
|
68
66
|
/plan add rate limiting to the public API # enter plan mode and send the task
|
|
67
|
+
Shift+Tab # toggle too, from bytefly's editor
|
|
69
68
|
```
|
|
70
69
|
|
|
71
|
-
The model calls `
|
|
72
|
-
`
|
|
70
|
+
The model calls `plan` with action `enter` before a non-trivial task; `/plan` and
|
|
71
|
+
`Shift+Tab` are the user's own switch. Shift+Tab is bytefly's: the editor binds
|
|
72
|
+
it and asks plan through `PLAN_TOGGLE_REQUEST`, so a session that loads `plan`
|
|
73
|
+
without bytefly uses `/plan` (or `--plan`) instead.
|
|
73
74
|
|
|
74
75
|
## Tools
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
One tool with five actions — `enter`, `write`, `exit`, `steps`, `cancel` — because
|
|
78
|
+
the verbs share one domain, the way `board` and `todo` keep theirs in one tool. How
|
|
79
|
+
the `write` and `exit` calls and their results look in the transcript — the call
|
|
80
|
+
line as a headline and the body indented under it:
|
|
77
81
|
|
|
78
|
-
<img src="../../assets/tools/
|
|
82
|
+
<img src="../../assets/tools/plan-write.png" alt="The plan tool's write action: the call line and an indented body with the saved plan path" width="100%">
|
|
79
83
|
|
|
80
|
-
<img src="../../assets/tools/
|
|
84
|
+
<img src="../../assets/tools/plan-exit.png" alt="The plan tool's exit action: the call line and an indented body with the approval line and the approved plan preview" width="100%">
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
The tool is `executionMode: "sequential"`, closed-world, and active in both modes;
|
|
87
|
+
each action refuses outside its own, so plan mode never has to hide it. Its one
|
|
88
|
+
annotation set is the strictest of the five, because `write` overwrites a plan file
|
|
89
|
+
with `plan_path`; the actions that only change the mode are exempted by the guard
|
|
90
|
+
in `index.ts`, the one place that can still tell them apart, and the policy's
|
|
91
|
+
`isAllowed` stays a statement about files rather than about this tool.
|
|
85
92
|
|
|
86
|
-
|
|
|
93
|
+
| Action | Active | Purpose |
|
|
87
94
|
|---|---|---|
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
95
|
+
| `enter` | either mode | Ask to enter plan mode; no fields, confirmed with `ctx.ui.confirm`. |
|
|
96
|
+
| `write` | plan mode | Save the plan: `title` (file-name slug), `content` (full markdown in the documented outline), optional `plan_path` to overwrite a refinement. Returns the absolute path. |
|
|
97
|
+
| `exit` | plan mode | Read the plan file (`plan_path`) and present it for approval. |
|
|
98
|
+
| `steps` | normal mode | Set the checkboxes of the given top-level steps of an approved plan in one write, and their todo items with them. |
|
|
99
|
+
| `cancel` | plan mode | Leave plan mode without a plan file or a review. No fields. |
|
|
93
100
|
|
|
94
|
-
### `
|
|
101
|
+
### `enter`
|
|
95
102
|
|
|
96
|
-
No
|
|
103
|
+
No fields. It confirms with `ctx.ui.confirm` before switching the tool set,
|
|
97
104
|
because the model is changing the mode on the user's behalf; a declined confirm
|
|
98
105
|
answers "stayed in normal mode" and the session keeps write access. Already
|
|
99
106
|
planning, it says so. With no UI it fails with an actionable message instead of
|
|
@@ -102,12 +109,12 @@ deciding for the user.
|
|
|
102
109
|
On a remote turn the confirmation is a short consent sentence in the peer's
|
|
103
110
|
language, and the notice names how to leave.
|
|
104
111
|
|
|
105
|
-
### `
|
|
112
|
+
### `write`
|
|
106
113
|
|
|
107
114
|
`title` is the file-name slug, `content` the full markdown, and `plan_path`
|
|
108
115
|
overwrites an existing plan when refining. The result carries the absolute path,
|
|
109
|
-
the relative path, and the byte count — the absolute path is what `
|
|
110
|
-
|
|
116
|
+
the relative path, and the byte count — the absolute path is what `exit` takes. A
|
|
117
|
+
call with neither `title` nor `content` is refused before anything is written.
|
|
111
118
|
|
|
112
119
|
The file lands in
|
|
113
120
|
`<agent-dir>/plans/<repo-key>/<YYYY-MM-DD-HHmm>-<slug>.md`, keyed by the
|
|
@@ -117,8 +124,8 @@ repository the store is `<agent-dir>/plans`. Plans are outside the project, so
|
|
|
117
124
|
they are never untracked files and never appear in a `rewind` snapshot; a store
|
|
118
125
|
an older version left in `<repo>/.pi/plans` is moved in once, with a notice.
|
|
119
126
|
|
|
120
|
-
The description documents a canonical outline — a `# <Title>`, then
|
|
121
|
-
`## Context`, `## Approach`, `## Visual`, `## Steps`, `## Risks`, and
|
|
127
|
+
The tool's description documents a canonical outline — a `# <Title>`, then
|
|
128
|
+
`## Goal`, `## Context`, `## Approach`, `## Visual`, `## Steps`, `## Risks`, and
|
|
122
129
|
`## Verification` — and the model is guided to follow it. Every example in the
|
|
123
130
|
template is the shape wanted rather than a description of it, and each section
|
|
124
131
|
has one job: the goal is one sentence, the context is the facts that force the
|
|
@@ -141,7 +148,7 @@ A plan with more than 50 top-level steps in its `Steps` section is refused with
|
|
|
141
148
|
the cap and the fix named, and nothing is written.
|
|
142
149
|
|
|
143
150
|
The file is the source of truth. On approval the model follows the plan file's
|
|
144
|
-
steps, and refinements rewrite the same file (`
|
|
151
|
+
steps, and refinements rewrite the same file (`write` with `plan_path`), so
|
|
145
152
|
the reviewed artifact is the executed artifact. The text a refinement replaces is
|
|
146
153
|
kept beside it, as a dotfile named for the plan and the revision
|
|
147
154
|
(`.2026-10-26-1008-add-rate-limiting-r001.md`), up to the newest five: that is
|
|
@@ -152,9 +159,9 @@ the review never offers it for approval — and a refinement that changed nothin
|
|
|
152
159
|
keeps nothing, because the copy would be the plan itself. The write's result names
|
|
153
160
|
the revision it kept.
|
|
154
161
|
|
|
155
|
-
### `
|
|
162
|
+
### `exit`
|
|
156
163
|
|
|
157
|
-
`plan_path` names the file `
|
|
164
|
+
`plan_path` names the file `write` returned; the action reads it back and
|
|
158
165
|
presents it for approval. A hand-edited or older file whose step list is over the
|
|
159
166
|
cap is refused before the review screen. The user approves, keeps planning, asks
|
|
160
167
|
for a refinement, or exits plan mode without approving; the last keeps the plan
|
|
@@ -174,19 +181,19 @@ the todo list:
|
|
|
174
181
|
- The plan's `## Verification` section follows as the **last** todo item,
|
|
175
182
|
summarized on one line (`Verify: …`) — a fenced block contributes its
|
|
176
183
|
commands, not its fence lines. It is not a plan step, so it is ticked in the
|
|
177
|
-
todo list rather than with `
|
|
184
|
+
todo list rather than with the `steps` action, and it never counts against the
|
|
178
185
|
50-step cap.
|
|
179
186
|
- A plan without a `Steps` heading keeps the older rule — the shallowest list in
|
|
180
187
|
the document — so prose bullets in a plan that follows the outline never become
|
|
181
188
|
todos, and its verification bullets are already covered.
|
|
182
189
|
- Each seeded item is matched back to its step by text, so ticking a step with
|
|
183
|
-
`
|
|
190
|
+
`steps` completes the item too.
|
|
184
191
|
- An approval records the step texts it seeded, per plan path, as a `plan-seeded`
|
|
185
|
-
entry. A plan refined after approval is approved again — `
|
|
192
|
+
entry. A plan refined after approval is approved again — `write` needs plan
|
|
186
193
|
mode, so a second round is the way back — and that second approval **replaces the
|
|
187
194
|
leftovers its own earlier approval made**: an untouched item whose step was
|
|
188
195
|
renamed or dropped goes, while one somebody started or finished is kept and
|
|
189
|
-
named, because deleting it would erase work a reader did. `
|
|
196
|
+
named, because deleting it would erase work a reader did. `write` says the
|
|
190
197
|
list is stale when it refines a plan that was approved before.
|
|
191
198
|
|
|
192
199
|
**Goal-aware.** While planning, the injected context restates a pursued session
|
|
@@ -198,15 +205,10 @@ the decision. A session with no goal at all is given the same objective, also
|
|
|
198
205
|
without asking. An `active` or `achieved` goal is never rewritten: a final
|
|
199
206
|
objective is the user's. No path spends a model turn.
|
|
200
207
|
|
|
201
|
-
### `
|
|
202
|
-
|
|
203
|
-
Leaves plan mode without a plan file or a review. Restores write access, records
|
|
204
|
-
no todo items or goal, and keeps any saved plan. Empty schema.
|
|
205
|
-
|
|
206
|
-
### `set_plan_steps`
|
|
208
|
+
### `steps`
|
|
207
209
|
|
|
208
210
|
Keep an approved plan current: `plan_path`, plus `updates`, each a 1-based `step`
|
|
209
|
-
in the order `
|
|
211
|
+
in the order `exit` listed, a `status` of `pending` or `completed`, and
|
|
210
212
|
`expected` — the step's text, so an update whose step number moved is refused
|
|
211
213
|
instead of ticking the wrong line. It is a single all-or-nothing write: a step out
|
|
212
214
|
of range, or an `expected` text that no longer matches the file, refuses every
|
|
@@ -215,27 +217,32 @@ marker.
|
|
|
215
217
|
|
|
216
218
|
The todo item approval seeded from a step is ticked with it: the file is written
|
|
217
219
|
first and is authoritative, a renamed item is left alone and reported as
|
|
218
|
-
unmatched, and a failed todo write is reported in the result. The
|
|
219
|
-
while plan mode is on — refine the plan with `
|
|
220
|
+
unmatched, and a failed todo write is reported in the result. The action refuses
|
|
221
|
+
while plan mode is on — refine the plan with `write` instead.
|
|
222
|
+
|
|
223
|
+
### `cancel`
|
|
224
|
+
|
|
225
|
+
Leaves plan mode without a plan file or a review. Restores write access, records
|
|
226
|
+
no todo items or goal, and keeps any saved plan. No fields.
|
|
220
227
|
|
|
221
228
|
### Transcript
|
|
222
229
|
|
|
223
|
-
The
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
The blocks reuse the transcript component, and `exit` separates the plan body from
|
|
231
|
+
its header with a blank line; the collapse hint binds to `app.tools.expand`
|
|
232
|
+
(`ctrl+o` by default).
|
|
226
233
|
|
|
227
234
|
```
|
|
228
|
-
|
|
235
|
+
plan enter
|
|
229
236
|
✓ Plan mode enabled
|
|
230
237
|
```
|
|
231
238
|
|
|
232
239
|
```
|
|
233
|
-
|
|
240
|
+
plan write Add rate limiting
|
|
234
241
|
✓ Saved plan ~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
235
242
|
```
|
|
236
243
|
|
|
237
244
|
```
|
|
238
|
-
|
|
245
|
+
plan exit 2026-10-09-1809-add-rate-limiting
|
|
239
246
|
✓ Plan approved
|
|
240
247
|
~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
241
248
|
|
|
@@ -249,7 +256,7 @@ exit_plan_mode submitted a plan
|
|
|
249
256
|
|
|
250
257
|
| Path | Who starts it | Confirmation |
|
|
251
258
|
|---|---|---|
|
|
252
|
-
| `
|
|
259
|
+
| `plan` (action `enter`) | the model, before a non-trivial task | `ctx.ui.confirm` |
|
|
253
260
|
| `/plan` | the user | immediate |
|
|
254
261
|
| `/plan <prompt>` | the user, with the task | immediate |
|
|
255
262
|
| `/plan list` | the user, browsing the plans directory | immediate |
|
|
@@ -257,7 +264,7 @@ exit_plan_mode submitted a plan
|
|
|
257
264
|
| `/plan share [stop]` | the user, handing the review to a teammate | immediate |
|
|
258
265
|
| `/plan cancel` | the user, leaving plan mode without approving | immediate |
|
|
259
266
|
| `/skill:init` | the user, creating or updating `AGENTS.md` | enters plan mode; write waits for plan approval |
|
|
260
|
-
| `
|
|
267
|
+
| `Shift+Tab` (bytefly) | the user | immediate |
|
|
261
268
|
| `--plan` | the user, at launch | immediate |
|
|
262
269
|
|
|
263
270
|
`/plan` with no argument toggles plan mode; `/plan <prompt>` enters it (if it is
|
|
@@ -269,10 +276,13 @@ already runs read-only:
|
|
|
269
276
|
```
|
|
270
277
|
|
|
271
278
|
`/plan <prompt>` never disables plan mode; only the argument-less `/plan` (or
|
|
272
|
-
`
|
|
273
|
-
the toggle that works
|
|
274
|
-
|
|
275
|
-
is on
|
|
279
|
+
`Shift+Tab`, through bytefly) toggles. Because slash commands go through Pi
|
|
280
|
+
directly, `/plan` is the toggle that works with or without bytefly. Bytefly's
|
|
281
|
+
footer shows `▶ auto` in accent in its mode segment, and `≡ plan` in warning there
|
|
282
|
+
while plan mode is on; the bar draws that word itself, so the plan file name the
|
|
283
|
+
chip carries never reaches it. `auto` is Pi's default mode — every tool as it ships,
|
|
284
|
+
writes included — so the chip says which mode the session is in, not that a decision
|
|
285
|
+
was taken for you.
|
|
276
286
|
|
|
277
287
|
`/plan list` walks the plans directory and shows every plan with its title,
|
|
278
288
|
modified time, and step progress, newest first — in the TUI as a docked list
|
|
@@ -293,7 +303,7 @@ never swallows one.
|
|
|
293
303
|
`/plan share` prints the reviewer link and its QR, and `/plan share stop` revokes
|
|
294
304
|
every link; the page's **Share** button does the same. With no browser review open
|
|
295
305
|
it says so and points at `/plan review`. `/plan cancel` leaves plan mode without
|
|
296
|
-
approving, exactly like `
|
|
306
|
+
approving, exactly like `cancel`: write access returns and the plan file
|
|
297
307
|
stays for a later review. Both are exact words — `share the tokens` and `cancel
|
|
298
308
|
that idea` stay tasks — as is `list`.
|
|
299
309
|
|
|
@@ -312,7 +322,7 @@ approval. Its workflow is:
|
|
|
312
322
|
└─ unavailable/cancelled ──> evidence + assumptions
|
|
313
323
|
└─> draft: new file, exact edits, or an honest no-op
|
|
314
324
|
└─> quality gate
|
|
315
|
-
└─>
|
|
325
|
+
└─> plan · write ──> plan · exit
|
|
316
326
|
├─ not approved ──> refine the same plan
|
|
317
327
|
└─ approved ──────> write/edit the target
|
|
318
328
|
└─ read back + quality checklist
|
|
@@ -368,9 +378,13 @@ session token, and the owner's token is the one that opened the page.
|
|
|
368
378
|
| `POST /api/share/revoke?token=…` | owner only: revoke one link by `{ id }`, leaving the others answering |
|
|
369
379
|
| `POST /api/share/stop?token=…` | owner only: revoke every reviewer link |
|
|
370
380
|
|
|
381
|
+
Every route that reads a body takes JSON and only JSON: a form-encoded, `text/plain`,
|
|
382
|
+
or headerless body is refused `415` before the role check, so a form cannot reach the
|
|
383
|
+
parser even from the page's own origin.
|
|
384
|
+
|
|
371
385
|
## Behaviour by mode
|
|
372
386
|
|
|
373
|
-
The review screen is TUI-only: `
|
|
387
|
+
The review screen is TUI-only: `exit` opens `PlanViewComponent`, and `w`
|
|
374
388
|
on it opens the browser review. RPC and other non-interactive runs get no screen.
|
|
375
389
|
|
|
376
390
|
Dialog-capable non-TUI modes (RPC/WeChat) use one editor prompt instead: on a
|
|
@@ -393,10 +407,10 @@ its closing rule on a short terminal.
|
|
|
393
407
|
Browser review is a local-turn feature: the server binds `127.0.0.1`, so a remote
|
|
394
408
|
(WeChat) turn cannot reach it and `/plan review` there refuses with a notice
|
|
395
409
|
instead of opening a server nobody can use. The browser's **Approve & run** is
|
|
396
|
-
offered only for the review `
|
|
410
|
+
offered only for the review `exit` opens, since only that flow owns
|
|
397
411
|
leaving plan mode; `/plan review` stays comment-only.
|
|
398
412
|
|
|
399
|
-
Aborting the turn closes a browser review. `
|
|
413
|
+
Aborting the turn closes a browser review. `exit` forwards the call's
|
|
400
414
|
abort signal to the review server, so an aborted turn returns "not approved"
|
|
401
415
|
and stops listening instead of waiting on a page nobody is watching.
|
|
402
416
|
|
|
@@ -411,7 +425,7 @@ command. The read-only guarantee is layered:
|
|
|
411
425
|
git commands through `bash` (chained with `&&`/`;`), `powershell` is blocked
|
|
412
426
|
outright, and other mutating tools (worktree mutations, MCP tools) are blocked
|
|
413
427
|
until you exit plan mode. `subagent` stays active but is forced read-only. The
|
|
414
|
-
only permitted write is the plan
|
|
428
|
+
only permitted write is the `plan` tool's `write` action, which can only
|
|
415
429
|
create or overwrite markdown plan files directly inside the plans directory —
|
|
416
430
|
never dotfiles and never through a symlink.
|
|
417
431
|
- **Capability policy.** A tool is allowed only when it is a known structured
|
|
@@ -423,18 +437,18 @@ command. The read-only guarantee is layered:
|
|
|
423
437
|
file path is refused unless it is a known plan-safe reader. `path-guard.ts`
|
|
424
438
|
scans nested arguments and path-shaped values, not just fixed top-level keys.
|
|
425
439
|
- **Control-tool identity.** The `tool_call` guard exempts plan's own tools —
|
|
426
|
-
`
|
|
440
|
+
the `plan` tool's `write`, `exit`, and `cancel` actions — only
|
|
427
441
|
when the registered tool is this extension's, matched by source path, so a
|
|
428
442
|
same-named tool from another extension cannot borrow the exemption.
|
|
429
|
-
|
|
443
|
+
the tool is exempted as a whole, and its `enter` action answers "already"
|
|
430
444
|
is answered with "Already in plan mode" instead of being refused as not
|
|
431
445
|
read-only; the policy still denies it, so it stays hidden while planning.
|
|
432
|
-
- **Contained writes.** `
|
|
446
|
+
- **Contained writes.** `write` only writes markdown non-dotfiles whose
|
|
433
447
|
real parent resolves inside the plans directory, and every plan write —
|
|
434
|
-
create, refine, `
|
|
448
|
+
create, refine, `steps`, browser feedback — goes through a hidden
|
|
435
449
|
temp file followed by a rename, so an interrupted write cannot leave a
|
|
436
450
|
truncated plan.
|
|
437
|
-
- **Sanitized model text.** The plan file is model-authored, so `
|
|
451
|
+
- **Sanitized model text.** The plan file is model-authored, so `exit`
|
|
438
452
|
strips control characters from the plan body before rendering it in the
|
|
439
453
|
transcript and in non-TUI notices; a plan file cannot drive the terminal.
|
|
440
454
|
- **Loopback browser review.** The review server binds `127.0.0.1` and requires a
|
|
@@ -470,8 +484,19 @@ git diff -- extensions\plan # allowed: a Windows path separator
|
|
|
470
484
|
git status && rm -rf x # blocked: the second segment is not git
|
|
471
485
|
git status | sh # blocked: pipes are never allowed
|
|
472
486
|
git diff > out.patch # blocked: redirection is a write
|
|
487
|
+
git config --get user.email # allowed: a read
|
|
488
|
+
git config --add core.hooksPath x # blocked: it writes .git/config
|
|
473
489
|
```
|
|
474
490
|
|
|
491
|
+
`git config` reads only. Its flags are an allowlist — `--get`, `--get-all`,
|
|
492
|
+
`--get-regexp`, `--list`, and the modifiers that shape them (`--show-origin`,
|
|
493
|
+
`--show-scope`, `--local`, `--global`, `--null`) — one bare `git config <name>` reads a
|
|
494
|
+
key, and every action that writes is refused by name: `--add`, `--replace-all`, `--unset`,
|
|
495
|
+
`--unset-all`, `--rename-section`, `--remove-section`, `--edit`, or a `<name> <value>`
|
|
496
|
+
pair. A write action used to pass as long as a read flag appeared beside it, so
|
|
497
|
+
`git config --show-scope --replace-all core.fsmonitor /tmp/hook.sh` configured a hook from
|
|
498
|
+
inside a plan and the next `git status` ran it.
|
|
499
|
+
|
|
475
500
|
A refused shell command explains the fix: the working directory is already the
|
|
476
501
|
project root (so `cd` is unnecessary), and files belong to the `read`, `grep`,
|
|
477
502
|
`find`, and `ls` tools.
|
|
@@ -509,7 +534,7 @@ agent file that overrides it.
|
|
|
509
534
|
|
|
510
535
|
<img src="../../assets/screens/plan.png" alt="The terminal plan review: a Plan Review title rule, a notice that one section changed since the last review, the plan body with its headings and ◼/◻ task checkboxes, a scroll gauge naming the current section and the visible line range, and the Esc/a/r/w, scroll, and section-jump hints" width="100%">
|
|
511
536
|
|
|
512
|
-
`
|
|
537
|
+
`exit` opens this screen on the plan file it just wrote. The body is
|
|
513
538
|
rendered with Pi's own `Markdown` component and `getMarkdownTheme()`, so
|
|
514
539
|
headings, lists, inline code, code blocks, links, and quotes carry the active
|
|
515
540
|
theme's colours; task items get the shared `◼` (done) or `◻` (pending) marker,
|
|
@@ -637,7 +662,7 @@ file and returns them to the model.
|
|
|
637
662
|
Stripping is fence-aware: a `> [!REVIEW]` line inside a code block is
|
|
638
663
|
documentation, not feedback. The file is the only copy of a saved comment; the
|
|
639
664
|
ones this tab has not saved yet live in its own `sessionStorage` and nowhere else.
|
|
640
|
-
- **A revision keeps the review.** `
|
|
665
|
+
- **A revision keeps the review.** `write` passes the file it is replacing
|
|
641
666
|
through `carryFeedback`, which re-attaches each comment by the strongest evidence
|
|
642
667
|
that survived: the anchored block's text, per occurrence, so a plan that repeats
|
|
643
668
|
a step keeps the comments apart; then the block whose wording still overlaps it —
|
|
@@ -750,13 +775,13 @@ both.
|
|
|
750
775
|
keeps no registry, so it sees an external edit or a hand-copied plan
|
|
751
776
|
immediately; it lists a file over the 256 KiB plan limit without reading it,
|
|
752
777
|
and does not index the agent directory's plans while a worktree is active.
|
|
753
|
-
- Approval tracks at most 50 steps: `
|
|
754
|
-
`
|
|
778
|
+
- Approval tracks at most 50 steps: `write` refuses a larger plan, and
|
|
779
|
+
`exit` refuses to present one, so no reviewed step is silently
|
|
755
780
|
dropped. A step longer than 200 characters is shortened in the todo list and
|
|
756
781
|
the approval listing — a display rule only, because the plan file always keeps
|
|
757
782
|
the full text.
|
|
758
783
|
- The todo item for a step is matched by its text. An item renamed after
|
|
759
|
-
approval no longer matches, so `
|
|
784
|
+
approval no longer matches, so `steps` leaves it alone and says so in
|
|
760
785
|
its result; the plan file is still updated.
|
|
761
786
|
- Browser review needs a local browser: the server is loopback-only, so a remote
|
|
762
787
|
turn cannot drive it.
|
|
@@ -781,12 +806,18 @@ both.
|
|
|
781
806
|
upgrade reads differently there.
|
|
782
807
|
- A shared review holds at most eight links. Each is a live bearer credential, so
|
|
783
808
|
a ninth is refused with the cap named rather than minted.
|
|
809
|
+
- **One tool means one filter.** Pi's per-tool filter is all-or-nothing, so a
|
|
810
|
+
session that disables `plan` loses every action with it, and plan mode
|
|
811
|
+
force-activates the tool while it is on (and puts the user's set back when it
|
|
812
|
+
ends). `rewind` also snapshots a run at the tool's first call rather than at
|
|
813
|
+
the first real write, because one annotation set cannot say that `enter` writes
|
|
814
|
+
nothing; it is still one snapshot per run, of the same tree.
|
|
784
815
|
|
|
785
816
|
## Non-goals
|
|
786
817
|
|
|
787
818
|
- **Not a sandbox.** Extensions share Pi's OS permissions.
|
|
788
819
|
- **No progress tracker of record.** `todo` owns completion state; the plan
|
|
789
|
-
file only mirrors it (`
|
|
820
|
+
file only mirrors it (`steps`), and plan mode does not read completion
|
|
790
821
|
back out.
|
|
791
822
|
- **No auto-execution.** Approval hands control back to the model.
|
|
792
823
|
|
|
@@ -794,10 +825,11 @@ both.
|
|
|
794
825
|
|
|
795
826
|
| Integration point | How |
|
|
796
827
|
|---|---|
|
|
797
|
-
| Tools | `
|
|
798
|
-
| Commands and flags | `/plan` (toggle, or enter and send a task), `/plan list` (plans index), `/plan review [path]` (browser review), `/plan share [stop]` (reviewer link), `/plan cancel` (leave without approving), `/skill:init` (skill-backed `AGENTS.md` bootstrap), `--plan` launch flag
|
|
828
|
+
| Tools | one `plan` tool, active in both modes, with five actions: `enter`, `write`, `exit`, `steps`, `cancel`. It is `executionMode: "sequential"`, closed-world, and carries one annotation set — `readOnlyHint: false`, `destructiveHint: true`, `idempotentHint: false` — because `write` overwrites a plan file: the actions that only change the mode are exempted by the guard in `index.ts`, the only place that can tell them apart. Each action returns typed `details` discriminated by `action` (`EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `CancelPlanModeDetails`, `SetPlanStepsDetails`); a refusal carries `RefusedPlanDetails` (`refused`, `error`) and no action's state; no `outputSchema`/`structuredContent`. |
|
|
829
|
+
| Commands and flags | `/plan` (toggle, or enter and send a task), `/plan list` (plans index), `/plan review [path]` (browser review), `/plan share [stop]` (reviewer link), `/plan cancel` (leave without approving), `/skill:init` (skill-backed `AGENTS.md` bootstrap), `--plan` launch flag. `Shift+Tab` is bytefly's editor chord, not a registered shortcut: `app.thinking.cycle` is reserved, so the editor requests a toggle over the event bus instead. |
|
|
799
830
|
| State | Enabled flag and the tools it hid persisted with `pi.appendEntry("plan-mode", { enabled, removed })`, and each review's baseline persisted with `pi.appendEntry("plan-baseline", { path, sections })` as one digest per heading section; both rebuilt from `ctx.sessionManager.getBranch()`, so `/resume` and `/tree` restore the mode and the changed markers of the branch they land on. Plan writes a goal only at approval, through `ctx.executeTool("goal", { objective })` when the `goal` tool is registered: a `planning` goal is promoted to the plan's `## Goal`, a session with none is given the same objective, and an `active`/`achieved` goal is left alone — no path asks first. A review that shows the same content appends nothing. The last plan path lives in runtime memory for the footer chip, and a bare `/plan review` falls back to the newest plan file in the directory. |
|
|
800
|
-
| Entry requests | `pi.events`
|
|
831
|
+
| Entry requests | `pi.events` channels from `lib/plan-entry.ts`: `PLAN_ENTER_REQUEST` (the `goal` workflow asks, with the shared consent sentence, and plan enables the mode on the caller's context without a second dialog) and `PLAN_TOGGLE_REQUEST` (bytefly's Shift+Tab asks to flip it and gets the state back). Both share one `PlanModeRequest` payload and `isPlanExtensionLoaded`. Subscribed on `session_start`, released on `session_shutdown`. |
|
|
832
|
+
| `board` while planning | The board is one action-shaped tool, so its annotation cannot say it reads: plan mode reads the action instead, through `lib/policy.ts`'s `isBoardRead` and `lib/board/actions.ts`'s table. The refusal is built by `boardWriteRefusal` in that same module — the board's words, because the sentence names the board's order: claim a card before you plan, and record its plan once it is approved. |
|
|
801
833
|
| Hooks | `tool_call` blocks non-read-only calls and forces `subagent` read-only; `before_agent_start` injects the hidden `[PLAN MODE ACTIVE]` context, with a pursued session goal read from the branch through `lib/goal-state.ts`; `context` keeps only the newest plan context while enabled and drops stale ones otherwise. |
|
|
802
834
|
| Lifecycle | `session_start`/`session_tree` restore state; `session_shutdown` clears the footer status and closes any browser review server. |
|
|
803
835
|
|
|
@@ -805,16 +837,16 @@ both.
|
|
|
805
837
|
|
|
806
838
|
- **Consent is one dialog, whichever extension asks.** The goal workflow offers
|
|
807
839
|
plan mode as a phase of the goal, but a command context cannot call
|
|
808
|
-
`
|
|
840
|
+
the `plan` tool (`ExtensionCommandContext` has no `executeTool`). So the
|
|
809
841
|
consent sentence left `policy.ts` for `lib/plan-entry.ts`, and the entry itself
|
|
810
842
|
is a `pi.events` request: the asker shows the question, plan enables silently
|
|
811
|
-
with the asker's context. `
|
|
843
|
+
with the asker's context. The tool's `enter` action keeps its own confirm, using the
|
|
812
844
|
same sentence, so the model-initiated path is unchanged and the two texts
|
|
813
845
|
cannot drift. A request that nobody answers resolves `false` rather than
|
|
814
846
|
hanging, so `goal` loaded alone still works.
|
|
815
847
|
- **One review loop owns the human gate.** `review.ts` asks however the session
|
|
816
848
|
is attached — TUI screen, dialog plus editor, remote file plus digest, or
|
|
817
|
-
browser — and returns one `ReviewDecision`, so `
|
|
849
|
+
browser — and returns one `ReviewDecision`, so `exit` and
|
|
818
850
|
`/plan review` no longer carry two copies of the browser lifecycle and the
|
|
819
851
|
tool's own `for(;;)` with a `continue` per outcome. The loop also owns the
|
|
820
852
|
baseline rule (mark what the screen showed, after it showed it) and forwards
|
|
@@ -822,9 +854,11 @@ both.
|
|
|
822
854
|
- **Two tools, not one.** `EnterPlanMode`/`ExitPlanMode` are a pair: entering is
|
|
823
855
|
consent to explore read-only, exiting is consent to act. A single toggle tool
|
|
824
856
|
cannot express "the model wants to plan but the user declines".
|
|
825
|
-
- **User entry is instant, model entry confirmed.** `/plan`, `
|
|
826
|
-
`--plan` are the user's own action and take effect at once; `
|
|
827
|
-
changes the mode on the user's behalf, so it confirms first.
|
|
857
|
+
- **User entry is instant, model entry confirmed.** `/plan`, `Shift+Tab`, and
|
|
858
|
+
`--plan` are the user's own action and take effect at once; the `enter` action
|
|
859
|
+
changes the mode on the user's behalf, so it confirms first. Shift+Tab travels
|
|
860
|
+
through bytefly's editor and `PLAN_TOGGLE_REQUEST`, so the mode stays owned by
|
|
861
|
+
`plan` even when the key is pressed in another extension's surface.
|
|
828
862
|
- **Review the file, decide in a screen.** The plan is a file, so it is the
|
|
829
863
|
artifact already on screen. `ctx.ui.confirm()` is a non-scrollable selector
|
|
830
864
|
that pushes buttons off-screen for a long plan, so the TUI gets a scrollable
|
|
@@ -832,7 +866,7 @@ both.
|
|
|
832
866
|
renders the body with Pi's `Markdown` component so the plan reads like any
|
|
833
867
|
other markdown in the app; the plan text is sanitized before it reaches the
|
|
834
868
|
renderer, so only the theme's own ANSI survives.
|
|
835
|
-
- **`
|
|
869
|
+
- **`write` is the only write.** It is not the builtin `write`, so the
|
|
836
870
|
capability policy and path backstop stay intact; a `plan_path` must name a
|
|
837
871
|
markdown non-dotfile whose real parent resolves inside the plans directory,
|
|
838
872
|
so symlinks and the directory's own `.gitignore` are never writable targets.
|
|
@@ -855,7 +889,7 @@ both.
|
|
|
855
889
|
authoritative, so a failed nested write is reported in the result text — once
|
|
856
890
|
one artifact has changed there is no previous state to hand back.
|
|
857
891
|
- **A cap is enforced where it can still be fixed.** Both refusal points —
|
|
858
|
-
`
|
|
892
|
+
`write` and `exit` — sit before the write and before the review,
|
|
859
893
|
because those are the moments the model can still regroup the steps. A note on
|
|
860
894
|
a successful write would arrive after the file existed and the review had
|
|
861
895
|
already listed every step, which is exactly when the overflow becomes lost
|
|
@@ -869,7 +903,7 @@ both.
|
|
|
869
903
|
a session entry rather than the previous text, so the markers survive a resume
|
|
870
904
|
without copying the plan file into the branch, and the reader and the writer
|
|
871
905
|
share one normalization so a tab cannot read as a change.
|
|
872
|
-
- **The outline guides, it does not enforce.** `
|
|
906
|
+
- **The outline guides, it does not enforce.** `write` documents the
|
|
873
907
|
canonical shape in its description and stores whatever markdown it is given;
|
|
874
908
|
no section is required and nothing is rewritten. The description names the two
|
|
875
909
|
headings that do carry behavior — `Steps` is what approval reads as steps, and
|
|
@@ -881,7 +915,7 @@ both.
|
|
|
881
915
|
source-line map, so `tui.ts` splits the body at its headings and renders each
|
|
882
916
|
section separately. That gives exact offsets for the current-section label and
|
|
883
917
|
`n`/`p`; the single-section case is byte-for-byte the old render.
|
|
884
|
-
- **`
|
|
918
|
+
- **`steps` is a second contained write.** It changes only the named
|
|
885
919
|
steps' checkboxes, in an existing markdown plan inside the plans directory
|
|
886
920
|
(`isContainedPlan`), in a single all-or-nothing write — refusing every entry
|
|
887
921
|
if any step is out of range or an entry's `expected` text no longer matches —
|
|
@@ -938,20 +972,21 @@ both.
|
|
|
938
972
|
|
|
939
973
|
| File | Responsibility |
|
|
940
974
|
|---|---|
|
|
941
|
-
| `index.ts` | Wiring: tools, command, flag,
|
|
942
|
-
| `types.ts` | `
|
|
975
|
+
| `index.ts` | Wiring: tools, command, flag, events, context injection. |
|
|
976
|
+
| `types.ts` | `PLAN_ACTIONS`, `PlanModeEntry`, the five action `details` types and their `PlanDetails` union, and `RefusedPlanDetails`. |
|
|
943
977
|
| `policy.ts` | Plan mode's read-only policy, the shared prompt summary, and the bash-refusal guidance. |
|
|
944
978
|
| `git-bash.ts` | `checkReadOnlyGit`: the pure allowlist that restricts `bash` to read-only git commands. |
|
|
945
|
-
| `plans.ts` | Plan-file store: slug/stamp naming, directory
|
|
979
|
+
| `plans.ts` | Plan-file store: slug/stamp naming, the store's directory (`<agent-dir>/plans/`, keyed by the repository, unkeyed outside git, through `lib/plans/paths.ts`), `write`/`setSteps`/`saveFeedback`, and `read`/`latest`. |
|
|
980
|
+
| `lib/plans/paths.ts` | Where a plan lives, and which paths are one: `plansDir` for `<agent-dir>/plans/` (keyed by the repository, unkeyed outside it), the markdown/non-dotfile name rule, symlink-safe containment, and `resolvePlanArg`. Shared with `board`, which records the file a card is executed against. |
|
|
946
981
|
| `plan-files.ts` | How a plan file reaches the disk: `writeAtomic` (a dotfile temp, then a rename, so a crash cannot truncate the plan a review is reading) and `keepRevision`, which keeps the text a refinement replaces beside it, numbered, up to the newest five. |
|
|
947
982
|
| `todos.ts` | Approval-time todo merge (pure): the last list replayed from the branch, and the merge of the plan's steps into it with the cap respected. |
|
|
948
983
|
| `steps.ts` | Plan-document structure: the outline, step extraction and the `Steps`-section scope, the plan's own goal (`goalSection`), and the checkbox edit. |
|
|
949
984
|
| `sections.ts` | The one fence-aware heading scan: `extractSections`, `sectionKeys` (the key that tells two identically titled sections apart), and the `HEADING` / `FENCE` / `normalizeHeadingText` vocabulary `blocks.ts` reads a heading through. |
|
|
950
985
|
| `format.ts` | Pure result/approval text: the plan preview, the step-cap diagnosis, step labels, the merge summary, and the verification note. |
|
|
951
986
|
| `plan-text.ts` | The plan text a review shows and compares: `sanitizePlanText`, the one normalization both sides use, the per-section `sectionDigests`, and `changedSections`, which hands each section back with its flag. |
|
|
952
|
-
| `runtime.ts` | Enabled state, tool gating, persistence (mode and review baseline),
|
|
953
|
-
| `tools.ts` |
|
|
954
|
-
| `tools/` | One module per
|
|
987
|
+
| `runtime.ts` | Enabled state, tool gating, persistence (mode and review baseline), the `plan` tool's identity and its place in the active set, footer status. |
|
|
988
|
+
| `tools.ts` | The one registration: the flat action schema, the dispatch over the five actions, and the call and result blocks they draw. |
|
|
989
|
+
| `tools/` | One module per action (`enter.ts`, `write.ts`, `exit.ts`, `cancel.ts`, `steps.ts`), each exporting its `execute`/`call`/`text` seam; plus `refusal.ts` (the one rejected-call shape), `todo-seed.ts` (the two nested `todo` writes) and `nested.ts` (reading a nested result). |
|
|
955
990
|
| `review.ts` | The review gate: one decision from the TUI screen, a dialog, or the browser, the shared browser lifecycle, and the "mark what was shown" baseline rule. |
|
|
956
991
|
| `review-prompt.ts` | Pure rendering and parsing for the compact remote review: one action table behind both the prompt and the reply parser. |
|
|
957
992
|
| `commands.ts` | `/plan` (enter, optional task), `/plan review [path]`, `/plan list`, `/plan share [stop]`, and `/plan cancel`. |
|
|
@@ -959,6 +994,8 @@ both.
|
|
|
959
994
|
| `plan-index.ts` | `/plan list`: the notice text and the docked index screen (store size and oldest plan, title, step progress, age, focused path; Enter reviews). |
|
|
960
995
|
| `blocks.ts` | Source-line block model (fence-aware headings, paragraphs, list items, code, quotes, tables, rules, blanks), and `contentBlockAt`: the block with content a comment beside blank space belongs to. |
|
|
961
996
|
| `feedback.ts` | Comment codec: authored markers, `stripFeedback`, `readFeedback`/`parseFeedback` (body, blocks, and comments out of one parse), `formatFeedback` (writer-scoped), `displayFeedback`, `carryFeedback`, `feedbackSummary`, and payload validation. |
|
|
997
|
+
| `review-lazy.ts` | The review gate behind a dynamic import: `reviewPlan` and `runBrowserReview` load `review.ts` (and its TUI screen) on the first review, so registration, `/plan cancel`, and the tool schemas never do. The decision and baseline types live here, where a type import costs nothing. |
|
|
998
|
+
| `web-lazy.ts` | The review behind `lib/lazy`: `NO_SERVER_DEPS`, and the proxy `/plan` and the tools share, so `web.ts` loads on the first review and shutdown closes only an opened one |
|
|
962
999
|
| `web.ts` | `createPlanWebReview`: the review session (per-token roles, the change stream, the plan-file poll, the body cap, settle-after-flush, abort) over `lib/http`'s one loopback server, and the router wiring — the island is `web-payload.ts` and the writes are `web-write.ts`. |
|
|
963
1000
|
| `web-tokens.ts` | The token table: the role each token names, the collision rule that keeps two roles off one token, and the names the other live links have bound. |
|
|
964
1001
|
| `web-streams.ts` | The event streams: who is listening, what a frame is addressed to, and the closed-tab grace that ends the review when the owner's page goes away. |
|
|
@@ -977,7 +1014,7 @@ both.
|
|
|
977
1014
|
| `web-client.ts` | The `/client.js` source: the composition root, and nothing else by decision — the element handles, the state each rule is handed, one zero-argument adapter per rule, the listeners that attach them, the `EventSource` that keeps a tab's presence known, and the boot order. Every rule it used to hold lives in the `web-*.ts` modules below, where it can be typed and driven. |
|
|
978
1015
|
| `web-comments.ts` | The client's comment half: selection quotes, the composer, authored cards and who may edit them, delete-with-undo. |
|
|
979
1016
|
| `web-collab.ts` | The client's collaboration half: the link list with copy and per-link revoke, the reviewer's name, and the island refetch a stream frame triggers. |
|
|
980
|
-
| `web-markdown.ts` | The client's
|
|
1017
|
+
| `web-markdown.ts` | The client's block half: tables, fenced code, headings, quotes, and lists, with each block's text handed to the shared `renderInline` from `lib/web/markdown.ts` (interpolated into `web-client.ts`). |
|
|
981
1018
|
| `web-chrome.ts` | The client's chrome half: the state chip, the status line, the header, and reading progress (interpolated into `web-client.ts`). |
|
|
982
1019
|
| `web-outline.ts` | The client's outline as typed source (`WEB_OUTLINE_JS`): `sectionFor` — the last heading at or before a line — `renderOutline`, which builds the list and hands back the counters it made, `observeSections`, which watches the blocks and returns the one watcher the client keeps, and `renderFacts`, which paints the step progress and counts each comment under its heading. |
|
|
983
1020
|
| `path-guard.ts` | Recursive path-argument detection for the read-only backstop. |
|
|
@@ -988,7 +1025,7 @@ both.
|
|
|
988
1025
|
| [`../../lib/qr.ts`](../../lib/qr.ts) | QR matrix, terminal half-blocks, and inline SVG for the share link (shared with `wechat`). |
|
|
989
1026
|
| [`../../lib/http/`](../../lib/http/) | The one loopback server entry, the bind, `Host` allowlist, header sets, and response kit both browser surfaces serve through. |
|
|
990
1027
|
| [`../../lib/web/index.ts`](../../lib/web/index.ts) | Shared Bytefly web brand system: tokens, base rules, `tok-*` colors, layout, the bootstrap, the toggle wiring, the shortcut sheet (`keysSheet`, `keysDialogScript`), and shared markup. |
|
|
991
|
-
| [`../../lib/http/index.ts`](../../lib/http/index.ts) |
|
|
1028
|
+
| [`../../lib/http/index.ts`](../../lib/http/index.ts) | The served-page kit shared with `file-browse`: the page policy and header sets, `ServeResponse`, the request-envelope helpers, bind/respond/close. Reached from the handler that serves a page; the mount contract is the leaf [`mount.ts`](../../lib/http/mount.ts). |
|
|
992
1029
|
| `../../lib/policy.ts` | Shared read-only capability policy (default-deny + `readOnlyHint`). |
|
|
993
1030
|
| `../../lib/tui/chrome.ts` | Shared screen header, viewport-row helper, and styled key-hint footer. |
|
|
994
1031
|
|
|
@@ -998,16 +1035,20 @@ both.
|
|
|
998
1035
|
bun test extensions/plan
|
|
999
1036
|
```
|
|
1000
1037
|
|
|
1001
|
-
|
|
1038
|
+
Thirteen co-located suites, declared in `test/suite-manifest.ts`:
|
|
1002
1039
|
|
|
1003
|
-
- `plans.test.ts` — the plan-file store: naming, directory
|
|
1004
|
-
writes, `read`/`latest`/`index`,
|
|
1005
|
-
|
|
1040
|
+
- `plans.test.ts` — the plan-file store: naming, the store's directory, atomic
|
|
1041
|
+
writes, `read`/`latest`/`index`, `setSteps`, `saveFeedback`, and step extraction;
|
|
1042
|
+
the location and path rules it runs through (`plansDir`, `plansDirFor`, the name
|
|
1043
|
+
rule, symlink-safe containment) are `lib/plans/paths.ts` and its own suite.
|
|
1006
1044
|
- `feedback.test.ts` — the comment codec: authored markers, stripping and
|
|
1007
1045
|
parsing, who a reviewer's write is signed by, carrying feedback onto a
|
|
1008
1046
|
revision, and displaying stored comments.
|
|
1009
|
-
- `tools.test.ts` —
|
|
1047
|
+
- `tools.test.ts` — all five actions against a real plan store, the approval merge into
|
|
1010
1048
|
`todo`, the session goal, and the call/result renderings.
|
|
1049
|
+
- `tool-registration.test.ts` — the registration itself: the flat action schema,
|
|
1050
|
+
the strictest-of-five annotations, and the refusal each action gives when it
|
|
1051
|
+
needs a field or runs outside its mode.
|
|
1011
1052
|
- `review-prompt.test.ts` — the compact remote prompt and its reply parser
|
|
1012
1053
|
against one action table.
|
|
1013
1054
|
- `web-*.test.ts` — the browser review, one capability per suite: the comment
|
|
@@ -1023,9 +1064,9 @@ Twelve co-located suites, declared in `test/suite-manifest.ts`:
|
|
|
1023
1064
|
- `web-share.test.ts` — the LAN address selector, `shareUrl`, the link table with
|
|
1024
1065
|
its cap, and the reasons a share is unavailable.
|
|
1025
1066
|
- `commands.test.ts` — `/plan review`, `/plan list`, `/plan share`, and the one
|
|
1026
|
-
review gate shared by `
|
|
1067
|
+
review gate shared by `exit` and the browser, including the abort and
|
|
1027
1068
|
baseline rules.
|
|
1028
|
-
- `runtime.test.ts` — tool gating, persistence and status,
|
|
1069
|
+
- `runtime.test.ts` — tool gating, persistence and status, the `plan` tool's identity,
|
|
1029
1070
|
the review baseline, branch restore, the read-only git allowlist, and the
|
|
1030
1071
|
path-argument scan.
|
|
1031
1072
|
- `tui.test.ts` — the review screen at fixed widths: Markdown body, scrolling,
|
|
@@ -1033,6 +1074,5 @@ Twelve co-located suites, declared in `test/suite-manifest.ts`:
|
|
|
1033
1074
|
refine editor, and the changed-section markers.
|
|
1034
1075
|
- `sections.test.ts` — the fence-aware scan: heading shape, fences, the preamble,
|
|
1035
1076
|
source lines and levels, and the occurrence key.
|
|
1036
|
-
- `extension.test.ts` — the Pi wiring: registration, the
|
|
1037
|
-
session lifecycle, the tool-call guard, injected context, the command
|
|
1038
|
-
shortcut.
|
|
1077
|
+
- `extension.test.ts` — the Pi wiring: registration, the enter and toggle requests,
|
|
1078
|
+
the session lifecycle, the tool-call guard, injected context, and the command.
|