@gavin-hu/my-pi-agent 0.18.0 → 0.19.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 +52 -44
- package/README.zh-CN.md +46 -41
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +344 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +446 -404
- package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
- package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
- package/docs/extensions/file-browse.md +554 -0
- package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
- package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
- package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
- package/{extensions/job/README.md → docs/extensions/job.md} +61 -50
- package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
- package/{extensions/plan/README.md → docs/extensions/plan.md} +390 -317
- package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
- package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
- package/docs/extensions/session-bus.md +285 -0
- package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
- package/docs/extensions/team.md +226 -0
- package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
- package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
- package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
- package/{extensions/wechat/README.md → docs/extensions/wechat.md} +258 -162
- package/{extensions/worktree/README.md → docs/extensions/worktree.md} +115 -72
- package/extensions/board/commands.ts +87 -0
- package/extensions/board/config.ts +36 -0
- package/extensions/board/format.ts +72 -0
- package/extensions/board/index.ts +40 -0
- package/extensions/board/input.ts +54 -0
- package/extensions/board/join.ts +42 -0
- package/extensions/board/page.ts +312 -0
- package/extensions/board/result.ts +99 -0
- package/extensions/board/rows.ts +48 -0
- package/extensions/board/schema.ts +75 -0
- package/extensions/board/seat.ts +29 -0
- package/extensions/board/snapshot.ts +41 -0
- package/extensions/board/tools.ts +279 -0
- package/extensions/board/transport/router.ts +172 -0
- package/extensions/board/transport/server.ts +149 -0
- package/extensions/board/types.ts +54 -0
- package/extensions/board/view/actions.ts +306 -0
- package/extensions/board/view/client.ts +41 -0
- package/extensions/board/view/keys.ts +121 -0
- package/extensions/board/view/live.ts +397 -0
- package/extensions/board/view/page.ts +158 -0
- package/extensions/board/view/render.ts +496 -0
- package/extensions/board/view/styles.ts +211 -0
- package/extensions/board/view/team.ts +305 -0
- package/extensions/board/writes.ts +99 -0
- package/extensions/bytefly/panel/commands.ts +40 -7
- package/extensions/bytefly/panel/settings-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +3 -20
- package/extensions/bytefly/panel/tui/settings-view.ts +1 -1
- package/extensions/bytefly/picker/discovery.ts +0 -2
- package/extensions/bytefly/picker/types.ts +0 -2
- package/extensions/bytefly/update/check.ts +39 -0
- package/extensions/bytefly/update/format.ts +15 -0
- package/extensions/bytefly/update/register.ts +73 -34
- package/extensions/bytefly/update/state.ts +6 -3
- package/extensions/context/tui.ts +1 -1
- package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
- package/extensions/file-browse/{files.ts → files/files.ts} +1 -14
- package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
- package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
- package/extensions/file-browse/index.ts +4 -4
- package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
- package/extensions/file-browse/{router.ts → transport/router.ts} +21 -15
- package/extensions/file-browse/transport/server.ts +110 -0
- package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
- package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
- package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
- package/extensions/file-browse/{html.ts → view/html.ts} +14 -12
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{markdown.ts → view/markdown.ts} +7 -7
- package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +1 -1
- package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
- package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
- package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
- package/extensions/file-read/schema.ts +2 -1
- package/extensions/memory/store.ts +12 -5
- package/extensions/plan/blocks.ts +23 -2
- package/extensions/plan/commands.ts +21 -16
- package/extensions/plan/feedback.ts +147 -38
- package/extensions/plan/index.ts +6 -4
- package/extensions/plan/plan-index.ts +43 -9
- package/extensions/plan/plan-text.ts +33 -23
- package/extensions/plan/plans.ts +8 -11
- package/extensions/plan/policy.ts +20 -12
- package/extensions/plan/review.ts +3 -27
- package/extensions/plan/runtime.ts +21 -9
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +3 -22
- package/extensions/plan/tools/exit.ts +11 -95
- package/extensions/plan/tui.ts +5 -34
- package/extensions/plan/types.ts +0 -8
- package/extensions/plan/web-client.ts +1 -1
- package/extensions/plan/web-page.ts +0 -7
- package/extensions/plan/web-router.ts +3 -57
- package/extensions/plan/web-share-session.ts +24 -30
- package/extensions/plan/web-streams.ts +14 -14
- package/extensions/plan/web-tokens.ts +2 -2
- package/extensions/plan/web.ts +66 -120
- package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
- package/extensions/rewind/{review.ts → commands/review.ts} +13 -13
- package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
- package/extensions/rewind/{config.ts → config/config.ts} +1 -1
- package/extensions/rewind/index.ts +3 -3
- package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
- package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
- package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
- package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
- package/extensions/rewind/{format.ts → text/format.ts} +3 -3
- package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
- package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
- package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
- package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
- package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
- package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
- package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
- package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
- package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
- package/extensions/rewind/{status.ts → view/status.ts} +4 -4
- package/extensions/session-bus/commands.ts +17 -69
- package/extensions/session-bus/format.ts +22 -45
- package/extensions/session-bus/index.ts +13 -7
- package/extensions/session-bus/join-request.ts +40 -0
- package/extensions/session-bus/mail-watch.ts +134 -0
- package/extensions/session-bus/notice.ts +32 -4
- package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
- package/extensions/session-bus/protocol.ts +19 -49
- package/extensions/session-bus/resolve.ts +67 -0
- package/extensions/session-bus/result.ts +16 -51
- package/extensions/session-bus/rows.ts +4 -2
- package/extensions/session-bus/runtime.ts +129 -238
- package/extensions/session-bus/store.ts +42 -6
- package/extensions/session-bus/tools.ts +31 -66
- package/extensions/session-bus/tui.ts +4 -4
- package/extensions/session-bus/types.ts +13 -13
- package/extensions/subagent/agents.ts +9 -8
- package/extensions/subagent/invocation.ts +4 -2
- package/extensions/team/commands.ts +112 -0
- package/extensions/team/dispatch.ts +133 -0
- package/extensions/team/format.ts +105 -0
- package/extensions/team/index.ts +33 -0
- package/extensions/team/schema.ts +29 -0
- package/extensions/team/tools.ts +246 -0
- package/extensions/team/types.ts +48 -0
- package/extensions/web-access/fetch/page.ts +4 -1
- package/extensions/web-access/fetch/structured.ts +447 -0
- package/extensions/web-access/fetch/tool.ts +2 -1
- package/extensions/web-access/search/tool.ts +2 -1
- package/extensions/wechat/bridge.ts +76 -92
- package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
- package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
- package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
- package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
- package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
- package/extensions/wechat/dialog/channels.ts +136 -0
- package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
- package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
- package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
- package/extensions/wechat/index.ts +10 -10
- package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
- package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
- package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
- package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
- package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
- package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
- package/extensions/wechat/{state.ts → state/state.ts} +14 -2
- package/extensions/wechat/{format.ts → text/format.ts} +3 -3
- package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
- package/extensions/wechat/tools.ts +29 -12
- package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
- package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
- package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
- package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
- package/extensions/wechat/transport/outbound.ts +142 -0
- package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
- package/extensions/wechat/types.ts +10 -1
- package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
- package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
- package/extensions/worktree/guard.ts +3 -5
- package/lib/README.md +8 -6
- package/lib/board/card.ts +290 -0
- package/lib/board/links.ts +117 -0
- package/lib/board/page-pointer.ts +256 -0
- package/lib/board/round-text.ts +72 -0
- package/lib/board/round.ts +89 -0
- package/lib/board/rules.ts +304 -0
- package/lib/board/session.ts +105 -0
- package/lib/board/store.ts +310 -0
- package/lib/board/view.ts +150 -0
- package/lib/errors.ts +12 -4
- package/lib/format.ts +13 -0
- package/lib/http/guard.ts +112 -0
- package/lib/http/index.ts +16 -5
- package/lib/http/serve.ts +234 -0
- package/lib/http/server.ts +12 -10
- package/lib/http/sink.ts +110 -0
- package/lib/http/token.ts +38 -0
- package/lib/policy.ts +12 -2
- package/lib/requirements.ts +6 -7
- package/lib/session-label.ts +64 -0
- package/lib/session-roster.ts +130 -0
- package/lib/session-tool.ts +184 -0
- package/lib/tool-names.ts +25 -2
- package/lib/tui/glyphs.ts +2 -0
- package/lib/web/README.md +5 -2
- package/lib/web/components.ts +5 -0
- package/lib/web/runtime.ts +7 -1
- package/lib/web/tokens.ts +25 -2
- package/package.json +26 -14
- package/skills/next/SKILL.md +4 -3
- package/skills/project-docs/SKILL.md +133 -0
- package/skills/project-docs/references/audit.md +131 -0
- package/skills/project-docs/references/changelog.md +105 -0
- package/skills/project-docs/references/discovery.md +127 -0
- package/skills/project-docs/references/readme.md +80 -0
- package/skills/project-docs/references/release.md +84 -0
- package/skills/project-docs/references/roadmap.md +93 -0
- package/skills/project-docs/scripts/state.ts +316 -0
- package/skills/refactor/SKILL.md +100 -125
- package/skills/refactor/references/handoff.md +35 -0
- package/skills/refactor/references/signals.md +54 -0
- package/skills/refactor/scripts/inventory.ts +587 -0
- package/extensions/file-browse/README.md +0 -506
- package/extensions/file-browse/server.ts +0 -141
- package/extensions/plan/split.ts +0 -87
- package/extensions/session-bus/README.md +0 -247
- package/extensions/session-bus/team.ts +0 -102
- package/extensions/wechat/outbound.ts +0 -100
- package/lib/plans/sections.ts +0 -172
- package/lib/plans/setting.ts +0 -52
- package/lib/plans/split.ts +0 -215
- package/lib/plans/store.ts +0 -19
- package/lib/plans/team.ts +0 -151
- package/skills/changelog/SKILL.md +0 -148
- package/skills/parallel-dev/SKILL.md +0 -150
- /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
- /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
- /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
- /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
- /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
- /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
- /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
- /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
- /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
- /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
- /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
|
@@ -6,13 +6,70 @@ but the only thing it may write is the plan file itself. The plan is saved under
|
|
|
6
6
|
the agent directory; the user reviews that file, and it is the artifact the model
|
|
7
7
|
executes.
|
|
8
8
|
|
|
9
|
+
## Capabilities
|
|
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
|
+
### Planning lifecycle
|
|
14
|
+
|
|
15
|
+
| Capability | How | What you get | Status |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| Read-only planning | `enter_plan_mode`, `/plan`, `Ctrl+Alt+P`, `--plan` | The session keeps only its readers, so the model investigates and proposes without touching the tree. | Shipped |
|
|
18
|
+
| Confirmed model entry | `enter_plan_mode` | The model asks before it takes write access away, and the user answers first. | Shipped |
|
|
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. | Shipped |
|
|
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. | Shipped |
|
|
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. | Shipped |
|
|
22
|
+
| One writable artifact | `write_plan` | The plan lands as markdown under the agent directory, one store per repository, shared by its worktrees. | Shipped |
|
|
23
|
+
| Approval gate | `exit_plan_mode` | The plan is read back and presented; nothing is written until the user approves it. | Shipped |
|
|
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. | Shipped |
|
|
25
|
+
| Steps as todos | `exit_plan_mode` | Approval seeds the plan's steps into the todo list, with the verification section as the last item. | Shipped |
|
|
26
|
+
| Plan kept current | `set_plan_steps` | Ticking a step rewrites its checkbox in the file and its todo item in one call. | Shipped |
|
|
27
|
+
| Stale-step protection | `expected` in `set_plan_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. | Shipped |
|
|
28
|
+
| Cancel without a plan | `cancel_plan_mode`, `/plan cancel` | Write access returns, and no todo items or goal are recorded. | Shipped |
|
|
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. | Shipped |
|
|
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. | Shipped |
|
|
31
|
+
| Instruction-file bootstrap | `/skill:init` | The packaged `init` skill plans and gates a project's `AGENTS.md`, writing only after approval. | Shipped |
|
|
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. | Shipped |
|
|
33
|
+
|
|
34
|
+
### Review
|
|
35
|
+
|
|
36
|
+
| Capability | How | What you get | Status |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| Review screen | `exit_plan_mode` | A scrollable, themed plan with approve, refine, keep, browser, and exit in the footer. | Shipped |
|
|
39
|
+
| 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. | Shipped |
|
|
40
|
+
| 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
|
+
| 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
|
+
| 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 `exit_plan_mode` review screen, the `/plan review` outline | After a refinement, only the sections that changed are marked. | Shipped |
|
|
44
|
+
| Section navigation | the review screen, `n`/`p` | The screen names the section you are in and jumps between headings. | Shipped |
|
|
45
|
+
| Remote review | `exit_plan_mode` on a WeChat turn | The plan file is sent, then one compact prompt to approve, keep planning, or exit. | Shipped |
|
|
46
|
+
|
|
47
|
+
### Collaboration
|
|
48
|
+
|
|
49
|
+
| Capability | How | What you get | Status |
|
|
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. | Shipped |
|
|
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. | Shipped |
|
|
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. | Shipped |
|
|
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. | Shipped |
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
9
58
|
```bash
|
|
10
59
|
pi --extension ./extensions/plan # load just this extension
|
|
11
60
|
pi -e . # load the whole @gavin-hu/my-pi-agent package
|
|
12
|
-
pi
|
|
61
|
+
pi install ./ # install the package for a user
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
/plan # toggle plan mode
|
|
66
|
+
/plan add rate limiting to the public API # enter plan mode and send the task
|
|
13
67
|
```
|
|
14
68
|
|
|
15
|
-
|
|
69
|
+
The model calls `enter_plan_mode` before a non-trivial task; `/plan` and
|
|
70
|
+
`Ctrl+Alt+P` are the user's own switch.
|
|
71
|
+
|
|
72
|
+
## Tools
|
|
16
73
|
|
|
17
74
|
How the `write_plan` and `exit_plan_mode` calls and their results look in the transcript — the call line as a headline and the body indented under it:
|
|
18
75
|
|
|
@@ -20,207 +77,158 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
20
77
|
|
|
21
78
|
<img src="../../assets/tools/exit_plan_mode.png" alt="The exit_plan_mode tool's call and result: the call line and an indented body with the approval line and the approved plan preview" width="100%">
|
|
22
79
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
file's steps; refinements rewrite the same file (`write_plan` with
|
|
117
|
-
`plan_path`), so the reviewed artifact is the executed artifact.
|
|
118
|
-
- **Steps become todos.** On approval, the top-level numbered/bulleted items of
|
|
119
|
-
the plan's `Steps` section are recorded with the `todo` tool (via
|
|
120
|
-
`ctx.executeTool`); steps marked `- [x]` (or `[DONE:n]`) seed as completed, and
|
|
121
|
-
nested sub-bullets are treated as notes while an indented line continues its
|
|
122
|
-
step. The list is a merge, not a replacement: existing items keep their place
|
|
123
|
-
and status, a step whose text is already there is skipped, and the tool's item
|
|
124
|
-
cap defers the rest instead of dropping anything. The plan's `## Verification`
|
|
125
|
-
section follows as the **last**
|
|
126
|
-
todo item, summarized on one line (`Verify: …`) — a fenced block contributes its
|
|
127
|
-
commands, not its fence lines. It is not a plan step, so it is ticked in the todo
|
|
128
|
-
list rather than with `set_plan_steps`, and it never counts against the 50-step
|
|
129
|
-
cap. A plan without a `Steps` heading keeps the older rule —
|
|
130
|
-
the shallowest list in the document — so prose bullets in a plan that follows the
|
|
131
|
-
outline never become todos, and its verification bullets are already covered.
|
|
132
|
-
Each seeded item is matched back to its step by text, so ticking a step with
|
|
80
|
+
All five tools are `executionMode: "sequential"` and closed-world. Two are active
|
|
81
|
+
in normal mode and three are `defaultActive: false`, so the plan-mode tool set
|
|
82
|
+
stays small.
|
|
83
|
+
|
|
84
|
+
| Tool | Active | Purpose |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `enter_plan_mode` | normal mode | Ask to enter plan mode; empty schema, confirmed with `ctx.ui.confirm`. |
|
|
87
|
+
| `write_plan` | 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. |
|
|
88
|
+
| `exit_plan_mode` | plan mode | Read the plan file (`plan_path`) and present it for approval. |
|
|
89
|
+
| `cancel_plan_mode` | plan mode | Leave plan mode without a plan file or a review. Empty schema. |
|
|
90
|
+
| `set_plan_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. |
|
|
91
|
+
|
|
92
|
+
### `enter_plan_mode`
|
|
93
|
+
|
|
94
|
+
No parameters. It confirms with `ctx.ui.confirm` before switching the tool set,
|
|
95
|
+
because the model is changing the mode on the user's behalf; a declined confirm
|
|
96
|
+
answers "stayed in normal mode" and the session keeps write access. Already
|
|
97
|
+
planning, it says so. With no UI it fails with an actionable message instead of
|
|
98
|
+
deciding for the user.
|
|
99
|
+
|
|
100
|
+
On a remote turn the confirmation is a short consent sentence in the peer's
|
|
101
|
+
language, and the notice names how to leave.
|
|
102
|
+
|
|
103
|
+
### `write_plan`
|
|
104
|
+
|
|
105
|
+
`title` is the file-name slug, `content` the full markdown, and `plan_path`
|
|
106
|
+
overwrites an existing plan when refining. The result carries the absolute path,
|
|
107
|
+
the relative path, and the byte count — the absolute path is what `exit_plan_mode`
|
|
108
|
+
takes.
|
|
109
|
+
|
|
110
|
+
The file lands in
|
|
111
|
+
`<agent-dir>/plans/<repo-key>/<YYYY-MM-DD-HHmm>-<slug>.md`, keyed by the
|
|
112
|
+
repository's common git directory, so a plan approved in one worktree is the
|
|
113
|
+
same plan from the main checkout and from every other session. Outside a
|
|
114
|
+
repository the store is `<agent-dir>/plans`. Plans are outside the project, so
|
|
115
|
+
they are never untracked files and never appear in a `rewind` snapshot; a store
|
|
116
|
+
an older version left in `<repo>/.pi/plans` is moved in once, with a notice.
|
|
117
|
+
|
|
118
|
+
The description documents a canonical outline — a `# <Title>`, then `## Goal`,
|
|
119
|
+
`## Context`, `## Approach`, `## Visual`, `## Steps`, `## Risks`, and
|
|
120
|
+
`## Verification` — and the model is guided to follow it. Every example in the
|
|
121
|
+
template is the shape wanted rather than a description of it, and each section
|
|
122
|
+
has one job: the goal is one sentence, the context is the facts that force the
|
|
123
|
+
approach, the approach names the alternative it rejected, and verification is a
|
|
124
|
+
fence of the commands to run. `Visual` carries the end state in an untagged
|
|
125
|
+
fence, with one line naming what to look at: the same surface before and after
|
|
126
|
+
when the change alters one that already exists, one frame when the surface is
|
|
127
|
+
new, because a reviewer judges the result before the steps that build it; a
|
|
128
|
+
change with no visible surface drops it. The description calls the shape a guide
|
|
129
|
+
rather than a form: `Context`, `Approach`, `Visual`, and `Risks` are dropped when
|
|
130
|
+
they have nothing to say, while `Steps` and `Verification` are kept, because the
|
|
131
|
+
steps are read from that section (without it, the shallowest list anywhere
|
|
132
|
+
becomes the steps) and Verification becomes the last todo item. Within `Steps`
|
|
133
|
+
only the shallowest list is tracked, so nested bullets stay notes instead of
|
|
134
|
+
becoming todos. This is guidance only: any markdown is accepted, stored, and
|
|
135
|
+
rendered as written, and a refinement is asked to keep its section headings so
|
|
136
|
+
the review can still mark only what changed.
|
|
137
|
+
|
|
138
|
+
A plan with more than 50 top-level steps in its `Steps` section is refused with
|
|
139
|
+
the cap and the fix named, and nothing is written.
|
|
140
|
+
|
|
141
|
+
The file is the source of truth. On approval the model follows the plan file's
|
|
142
|
+
steps, and refinements rewrite the same file (`write_plan` with `plan_path`), so
|
|
143
|
+
the reviewed artifact is the executed artifact.
|
|
144
|
+
|
|
145
|
+
### `exit_plan_mode`
|
|
146
|
+
|
|
147
|
+
`plan_path` names the file `write_plan` returned; the tool reads it back and
|
|
148
|
+
presents it for approval. A hand-edited or older file whose step list is over the
|
|
149
|
+
cap is refused before the review screen. The user approves, keeps planning, asks
|
|
150
|
+
for a refinement, or exits plan mode without approving; the last keeps the plan
|
|
151
|
+
file for a later review and records no todo items or goal. The review also offers
|
|
152
|
+
**Exit plan mode (discard)**, which leaves plan mode without approving.
|
|
153
|
+
|
|
154
|
+
On approval, plan mode ends, write access returns, and the plan's steps go into
|
|
155
|
+
the todo list:
|
|
156
|
+
|
|
157
|
+
- Steps are the top-level numbered/bulleted items of the plan's `Steps` section,
|
|
158
|
+
and approval records them with the `todo` tool (via `ctx.executeTool`). Steps
|
|
159
|
+
marked `- [x]` (or `[DONE:n]`) seed as completed, nested sub-bullets are notes,
|
|
160
|
+
and an indented line continues its step.
|
|
161
|
+
- The list is a merge, not a replacement: existing items keep their place and
|
|
162
|
+
status, a step whose text is already there is skipped, and the tool's item cap
|
|
163
|
+
defers the rest instead of dropping anything.
|
|
164
|
+
- The plan's `## Verification` section follows as the **last** todo item,
|
|
165
|
+
summarized on one line (`Verify: …`) — a fenced block contributes its
|
|
166
|
+
commands, not its fence lines. It is not a plan step, so it is ticked in the
|
|
167
|
+
todo list rather than with `set_plan_steps`, and it never counts against the
|
|
168
|
+
50-step cap.
|
|
169
|
+
- A plan without a `Steps` heading keeps the older rule — the shallowest list in
|
|
170
|
+
the document — so prose bullets in a plan that follows the outline never become
|
|
171
|
+
todos, and its verification bullets are already covered.
|
|
172
|
+
- Each seeded item is matched back to its step by text, so ticking a step with
|
|
133
173
|
`set_plan_steps` completes the item too.
|
|
134
|
-
- **Goal-aware.** While planning, the injected context restates a pursued
|
|
135
|
-
session goal as one bullet, so the plan is designed against the objective
|
|
136
|
-
instead of beside it. On approval, a goal the goal workflow left as a draft
|
|
137
|
-
(`planning`) is finalized from the plan's own `## Goal` section — falling back
|
|
138
|
-
to the title — through the `goal` tool, with no extra dialog, because approving
|
|
139
|
-
the plan *is* the decision. A session with no goal at all is given the same
|
|
140
|
-
objective, also without asking. An `active` or `achieved` goal is never
|
|
141
|
-
rewritten: a final objective is the user's. No path spends a model turn.
|
|
142
|
-
- **A split, when the switch is on.** With `plan.split` enabled (off by default;
|
|
143
|
-
a `/bytefly settings` row), `exit_plan_mode` also accepts `units`: the plan's
|
|
144
|
-
slices, each with a name, path prefixes, a test, and the units it depends on.
|
|
145
|
-
The split is validated *before* the review opens, so a refusal costs no review,
|
|
146
|
-
and the review screen shows it as a read-only `Split` section. On approval the
|
|
147
|
-
team is written into the plan as a `## Team` table — a sentence naming the lead's
|
|
148
|
-
unit, then one row per unit — and the result carries a roster card: the exact
|
|
149
|
-
commands for the sessions this package cannot start, the merge order, which unit
|
|
150
|
-
is the lead's own, and the lead's own label or the rename that sets it. The team
|
|
151
|
-
section is what a joining session reads, so it appears only after approval; a
|
|
152
|
-
refinement rewrites the plan, and with it the team ceases to exist until the plan
|
|
153
|
-
is approved again. Refusals name the pair: fewer than two units (with the
|
|
154
|
-
skeleton advice), overlapping prefixes, a `dependsOn` cycle, more than two owners
|
|
155
|
-
plus the lead, a unit with no test, an owner unit that claims a shared file, and a
|
|
156
|
-
value that would break the table. See [Splitting a plan](#splitting-a-plan).
|
|
157
|
-
- **Branch-aware.** The enabled flag and the review baseline are custom session
|
|
158
|
-
entries, so `/resume` and `/tree` restore the right mode and keep the
|
|
159
|
-
changed-section markers.
|
|
160
|
-
|
|
161
|
-
## Splitting a plan
|
|
162
|
-
|
|
163
|
-
Off by default: enable **Split plans into slices** in `/bytefly settings`, or set
|
|
164
|
-
`"plan": { "split": true }` in the project's `.pi/bytefly.json` so a repository
|
|
165
|
-
switches its own team on.
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
```
|
|
175
|
+
**Goal-aware.** While planning, the injected context restates a pursued session
|
|
176
|
+
goal as one bullet, so the plan is designed against the objective instead of
|
|
177
|
+
beside it. On approval, a goal the goal workflow left as a draft (`planning`) is
|
|
178
|
+
finalized from the plan's own `## Goal` section — falling back to the title —
|
|
179
|
+
through the `goal` tool, with no extra dialog, because approving the plan *is*
|
|
180
|
+
the decision. A session with no goal at all is given the same objective, also
|
|
181
|
+
without asking. An `active` or `achieved` goal is never rewritten: a final
|
|
182
|
+
objective is the user's. No path spends a model turn.
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
it cannot start — and the one line it can act on for the session it is in:
|
|
184
|
+
### `cancel_plan_mode`
|
|
179
185
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
You start and switch every session; nothing here can.
|
|
183
|
-
core: enter_worktree core · /sessions name core · /sessions watch wake
|
|
184
|
-
session({action:"join", team:"2026-11-04-1802-billing", unit:"core"})
|
|
185
|
-
merge order: contract → core · your unit: contract
|
|
186
|
-
your label: none — name this session: session({action:"name", label:"contract"})
|
|
187
|
-
```
|
|
186
|
+
Leaves plan mode without a plan file or a review. Restores write access, records
|
|
187
|
+
no todo items or goal, and keeps any saved plan. Empty schema.
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
the user starts; this one is the lead's own, so it names the label when the session
|
|
191
|
-
already has one, and otherwise prints the rename to run. Once set, it reads
|
|
192
|
-
`your label: billing-lead — owners send their report there`, which is what an
|
|
193
|
-
owner addresses its completion report to.
|
|
189
|
+
### `set_plan_steps`
|
|
194
190
|
|
|
195
|
-
|
|
191
|
+
Keep an approved plan current: `plan_path`, plus `updates`, each a 1-based `step`
|
|
192
|
+
in the order `exit_plan_mode` listed, a `status` of `pending` or `completed`, and
|
|
193
|
+
`expected` — the step's text, so an update whose step number moved is refused
|
|
194
|
+
instead of ticking the wrong line. It is a single all-or-nothing write: a step out
|
|
195
|
+
of range, or an `expected` text that no longer matches the file, refuses every
|
|
196
|
+
entry. Only the checkboxes change, and a reopened step drops its `[DONE:n]`
|
|
197
|
+
marker.
|
|
196
198
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
| a `dependsOn` cycle | No unit can start. |
|
|
202
|
-
| more than two owners | The user switches to every owner each round; three means the split is wrong. |
|
|
203
|
-
| a unit with no test | The unit's definition of done would be undocumented. |
|
|
204
|
-
| an owner unit claiming a shared file | `package.json`, the changelog, the test manifests, and the shared name modules belong to the lead. |
|
|
205
|
-
| no unit marked lead | The approving session needs a unit of its own, or it is a queue. |
|
|
206
|
-
|
|
207
|
-
The table itself is a wire format: [`session-bus`](../session-bus/) parses it with
|
|
208
|
-
the same module that writes it, so a hand-edited or stale section is refused
|
|
209
|
-
rather than half-read. The browser review cannot approve a split plan — a section
|
|
210
|
-
written at approval has no source lines for its comments to anchor to — so the
|
|
211
|
-
terminal review is where a split is approved.
|
|
199
|
+
The todo item approval seeded from a step is ticked with it: the file is written
|
|
200
|
+
first and is authoritative, a renamed item is left alone and reported as
|
|
201
|
+
unmatched, and a failed todo write is reported in the result. The tool refuses
|
|
202
|
+
while plan mode is on — refine the plan with `write_plan` instead.
|
|
212
203
|
|
|
213
|
-
|
|
204
|
+
### Transcript
|
|
214
205
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
206
|
+
The three tool rows reuse the transcript component, and `exit_plan_mode`
|
|
207
|
+
separates the plan body from its header with a blank line; the collapse hint
|
|
208
|
+
binds to `app.tools.expand` (`ctrl+o` by default).
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
enter_plan_mode requested plan mode
|
|
212
|
+
✓ Plan mode enabled
|
|
213
|
+
```
|
|
222
214
|
|
|
223
|
-
|
|
215
|
+
```
|
|
216
|
+
write_plan Add rate limiting
|
|
217
|
+
✓ Saved plan ~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
exit_plan_mode submitted a plan
|
|
222
|
+
✓ Plan approved
|
|
223
|
+
~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
224
|
+
|
|
225
|
+
# Add rate limiting
|
|
226
|
+
1. Add a token bucket …
|
|
227
|
+
|
|
228
|
+
… 12 more lines (ctrl+o to expand)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Commands and flags
|
|
224
232
|
|
|
225
233
|
| Path | Who starts it | Confirmation |
|
|
226
234
|
|---|---|---|
|
|
@@ -229,6 +237,7 @@ terminal review is where a split is approved.
|
|
|
229
237
|
| `/plan <prompt>` | the user, with the task | immediate |
|
|
230
238
|
| `/plan list` | the user, browsing the plans directory | immediate |
|
|
231
239
|
| `/plan review [path]` | the user, reviewing a plan in the browser | immediate |
|
|
240
|
+
| `/plan share [stop]` | the user, handing the review to a teammate | immediate |
|
|
232
241
|
| `/plan cancel` | the user, leaving plan mode without approving | immediate |
|
|
233
242
|
| `/skill:init` | the user, creating or updating `AGENTS.md` | enters plan mode; write waits for plan approval |
|
|
234
243
|
| `Ctrl+Alt+P` | the user | immediate |
|
|
@@ -248,13 +257,28 @@ the toggle that works in every terminal (for example Zed's integrated terminal,
|
|
|
248
257
|
which does not forward `Ctrl+Alt+P`). The footer shows `≡ plan` while plan mode
|
|
249
258
|
is on, and `≡ plan · <plan-file>` once the model has written a plan.
|
|
250
259
|
|
|
251
|
-
`/plan
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
260
|
+
`/plan list` walks the plans directory and shows every plan with its title,
|
|
261
|
+
modified time, and step progress, newest first — in the TUI as a docked list
|
|
262
|
+
where `Enter` opens the plan's review, elsewhere as a notice with each plan's
|
|
263
|
+
path. The header carries the store's own footprint for this repository: the bytes
|
|
264
|
+
its plans hold and how old the oldest one is, both derived from the walk that just
|
|
265
|
+
happened. The directory stays the index: no registry file, nothing deletes a plan,
|
|
266
|
+
and one bounded read per plan.
|
|
267
|
+
|
|
268
|
+
`/plan review` is the only subcommand that takes an argument. Bare `/plan review`
|
|
269
|
+
reviews the plan this session wrote, or, in a later session, the newest plan file
|
|
270
|
+
in the plans directory (newest modification time); `/plan review <path>.md` (or a
|
|
271
|
+
path with a separator) reviews that plan. A line that merely starts with the word
|
|
272
|
+
— `review the auth flow` — is still treated as a planning task, so the subcommand
|
|
256
273
|
never swallows one.
|
|
257
274
|
|
|
275
|
+
`/plan share` prints the reviewer link and its QR, and `/plan share stop` revokes
|
|
276
|
+
every link; the page's **Share** button does the same. With no browser review open
|
|
277
|
+
it says so and points at `/plan review`. `/plan cancel` leaves plan mode without
|
|
278
|
+
approving, exactly like `cancel_plan_mode`: write access returns and the plan file
|
|
279
|
+
stays for a later review. Both are exact words — `share the tokens` and `cancel
|
|
280
|
+
that idea` stay tasks — as is `list`.
|
|
281
|
+
|
|
258
282
|
### `/skill:init`
|
|
259
283
|
|
|
260
284
|
`/skill:init` loads the packaged `init` skill. The skill enters plan mode itself
|
|
@@ -300,51 +324,53 @@ The quality reference is
|
|
|
300
324
|
[`skills/init/references/agents-md-quality.md`](../../skills/init/references/agents-md-quality.md).
|
|
301
325
|
An existing instruction file is reviewed and updated only after approval, a root
|
|
302
326
|
override is edited in place or removed explicitly when consolidating, an accurate
|
|
303
|
-
file can produce a no-op plan, and an empty repository gets only known facts.
|
|
327
|
+
file can produce a no-op plan, and an empty repository gets only known facts. It
|
|
328
|
+
never invents commands.
|
|
304
329
|
The skill needs the plan extension for its review gate. It also ships with the
|
|
305
330
|
package, so load the whole package (`pi -e .`) or install it; loading only
|
|
306
331
|
`./extensions/plan` leaves `/skill:init` unavailable.
|
|
307
332
|
|
|
333
|
+
## Routes
|
|
334
|
+
|
|
335
|
+
The browser review serves these routes on its loopback origin; `…` is the
|
|
336
|
+
session token, and the owner's token is the one that opened the page.
|
|
337
|
+
|
|
338
|
+
| Route | Result |
|
|
339
|
+
|---|---|
|
|
340
|
+
| `GET /?token=…` | the review page (a JSON data island plus `/theme.js` and `/client.js`) |
|
|
341
|
+
| `GET /theme.js` | the shared pre-paint theme bootstrap |
|
|
342
|
+
| `GET /client.js` | the first-party client script |
|
|
343
|
+
| `GET /events?token=…` | Server-Sent Events stream: a `comment`, `plan`, or `share` frame when something changed, `closed` when the review ends, `revoked` when sharing stops, and a dropped stream to detect a closed tab |
|
|
344
|
+
| `POST /api/comment?token=…` | reviewer only: write their own comments into the plan, replacing only their own; never ends the review |
|
|
345
|
+
| `POST /api/feedback?token=…` | owner only: write the owner's comments and return to the model |
|
|
346
|
+
| `POST /api/approve?token=…` | owner only: save, then approve |
|
|
347
|
+
| `POST /api/cancel?token=…` | owner only: end the session without saving |
|
|
348
|
+
| `POST /api/share?token=…` | owner only: mint the first reviewer link and its QR |
|
|
349
|
+
| `POST /api/share/link?token=…` | owner only: mint one more reviewer link, so each teammate holds their own |
|
|
350
|
+
| `POST /api/share/revoke?token=…` | owner only: revoke one link by `{ id }`, leaving the others answering |
|
|
351
|
+
| `POST /api/share/stop?token=…` | owner only: revoke every reviewer link |
|
|
352
|
+
|
|
308
353
|
## Behaviour by mode
|
|
309
354
|
|
|
310
|
-
The review screen is TUI-only
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
plan
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
the next or previous heading; the scroll row names the current section when the
|
|
330
|
-
plan has more than one. When the plan overflows,
|
|
331
|
-
a gauge row above the hints shows the scroll position, the percent, and the
|
|
332
|
-
visible line range (the range drops first on a narrow terminal); the gauge comes
|
|
333
|
-
from the design system's `gaugeBar` and stays
|
|
334
|
-
visible while the refine editor is open, since the body above is still clipped.
|
|
335
|
-
Hints are
|
|
336
|
-
dropped whole as the terminal narrows, and each footer leads with its safe exit
|
|
337
|
-
(`Esc keep plan` in the review, `Esc back to plan` in the inline editor) so
|
|
338
|
-
the exit survives even when only one hint fits.
|
|
339
|
-
|
|
340
|
-
Dialog-capable non-TUI modes (RPC/WeChat) use one editor prompt; a non-action
|
|
341
|
-
reply is the refinement, so “select Refine, then type” is gone. A WeChat turn
|
|
342
|
-
takes the same dialog path and is answered over WeChat, even though the session
|
|
343
|
-
is in TUI mode: the choice comes from `askHuman(ctx, { custom, dialogs, body })`
|
|
344
|
-
in `lib/interaction.ts`, not from `ctx.mode`. No plan file is attached; **full
|
|
345
|
-
plan** posts the text and asks again.
|
|
346
|
-
Without any UI the tools fail with an actionable message instead of deciding for
|
|
347
|
-
the user.
|
|
355
|
+
The review screen is TUI-only: `exit_plan_mode` opens `PlanViewComponent`, and `w`
|
|
356
|
+
on it opens the browser review. RPC and other non-interactive runs get no screen.
|
|
357
|
+
|
|
358
|
+
Dialog-capable non-TUI modes (RPC/WeChat) use one editor prompt instead: on a
|
|
359
|
+
remote turn the plan file is sent first, then a compact prompt carrying the
|
|
360
|
+
title, step count, first steps, mode status, and three actions — `1` approve,
|
|
361
|
+
`2` keep planning, `3` exit. If the upload fails, the plan's text is sent
|
|
362
|
+
instead. Any other reply is a refinement, so "select Refine, then type" is gone.
|
|
363
|
+
A WeChat turn takes the same dialog path and is answered over
|
|
364
|
+
WeChat, even though the session is in TUI mode: the choice comes from
|
|
365
|
+
`askHuman(ctx, { custom, dialogs, body })` in `lib/interaction.ts`, not from
|
|
366
|
+
`ctx.mode`. Without any UI the tools fail with an actionable message instead of
|
|
367
|
+
deciding for the user.
|
|
368
|
+
|
|
369
|
+
`/plan list` is a docked list screen in the TUI and a notice with each plan's
|
|
370
|
+
path everywhere else; both name the store's size and its oldest plan. The dock is
|
|
371
|
+
the list's alone while it is open — the goal and todo rails hide, as they do for
|
|
372
|
+
the other list screens — and the screen counts its own frame, so a full list keeps
|
|
373
|
+
its closing rule on a short terminal.
|
|
348
374
|
|
|
349
375
|
Browser review is a local-turn feature: the server binds `127.0.0.1`, so a remote
|
|
350
376
|
(WeChat) turn cannot reach it and `/plan review` there refuses with a notice
|
|
@@ -356,49 +382,35 @@ Aborting the turn closes a browser review. `exit_plan_mode` forwards the call's
|
|
|
356
382
|
abort signal to the review server, so an aborted turn returns "not approved"
|
|
357
383
|
and stops listening instead of waiting on a page nobody is watching.
|
|
358
384
|
|
|
359
|
-
### Transcript
|
|
360
|
-
|
|
361
|
-
The three tool rows reuse the transcript component, and `exit_plan_mode`
|
|
362
|
-
separates the plan body from its header with a blank line; the collapse hint
|
|
363
|
-
binds to `app.tools.expand` (`ctrl+o` by default).
|
|
364
|
-
|
|
365
|
-
```
|
|
366
|
-
enter_plan_mode requested plan mode
|
|
367
|
-
✓ Plan mode enabled
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
```
|
|
371
|
-
write_plan Add rate limiting
|
|
372
|
-
✓ Saved plan ~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
```
|
|
376
|
-
exit_plan_mode submitted a plan
|
|
377
|
-
✓ Plan approved
|
|
378
|
-
~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
379
|
-
|
|
380
|
-
# Add rate limiting
|
|
381
|
-
1. Add a token bucket …
|
|
382
|
-
|
|
383
|
-
… 12 more lines (ctrl+o to expand)
|
|
384
|
-
```
|
|
385
|
-
|
|
386
385
|
## Security
|
|
387
386
|
|
|
388
387
|
Plan mode is a **guard rail, not a sandbox**: extensions run with Pi's OS
|
|
389
388
|
permissions, so it prevents accidental writes while planning, not a hostile
|
|
390
389
|
command. The read-only guarantee is layered:
|
|
391
390
|
|
|
391
|
+
- **Tool set at entry.** Tools that are not read-only are removed from the active
|
|
392
|
+
set or blocked. `write` and `edit` are hidden, raw shell is limited to read-only
|
|
393
|
+
git commands through `bash` (chained with `&&`/`;`), `powershell` is blocked
|
|
394
|
+
outright, and other mutating tools (worktree mutations, MCP tools) are blocked
|
|
395
|
+
until you exit plan mode. `subagent` stays active but is forced read-only. The
|
|
396
|
+
only permitted write is the plan control tool `write_plan`, which can only
|
|
397
|
+
create or overwrite markdown plan files directly inside the plans directory —
|
|
398
|
+
never dotfiles and never through a symlink.
|
|
392
399
|
- **Capability policy.** A tool is allowed only when it is a known structured
|
|
393
400
|
reader or declares the MCP `readOnlyHint`; `write`, `edit`, and unknown
|
|
394
|
-
mutators are denied.
|
|
401
|
+
mutators are denied. Classification defaults to *deny* and is driven by the
|
|
402
|
+
shared [`lib/policy.ts`](../../lib/policy.ts), so unknown tools are safe by
|
|
403
|
+
default and the MCP `readOnlyHint` is the only thing that opens one up.
|
|
395
404
|
- **Path backstop.** A `readOnlyHint` is only a claim, so a tool that takes a
|
|
396
405
|
file path is refused unless it is a known plan-safe reader. `path-guard.ts`
|
|
397
406
|
scans nested arguments and path-shaped values, not just fixed top-level keys.
|
|
398
|
-
- **Control-tool identity.** The `tool_call` guard exempts
|
|
399
|
-
`
|
|
400
|
-
matched by source path, so a
|
|
401
|
-
borrow the exemption.
|
|
407
|
+
- **Control-tool identity.** The `tool_call` guard exempts plan's own tools —
|
|
408
|
+
`enter_plan_mode`, `cancel_plan_mode`, `write_plan`, and `exit_plan_mode` — only
|
|
409
|
+
when the registered tool is this extension's, matched by source path, so a
|
|
410
|
+
same-named tool from another extension cannot borrow the exemption.
|
|
411
|
+
`enter_plan_mode` is exempted so a call already in flight when plan mode came on
|
|
412
|
+
is answered with "Already in plan mode" instead of being refused as not
|
|
413
|
+
read-only; the policy still denies it, so it stays hidden while planning.
|
|
402
414
|
- **Contained writes.** `write_plan` only writes markdown non-dotfiles whose
|
|
403
415
|
real parent resolves inside the plans directory, and every plan write —
|
|
404
416
|
create, refine, `set_plan_steps`, browser feedback — goes through a hidden
|
|
@@ -428,9 +440,10 @@ conservative allowlist in `git-bash.ts`. `powershell` is not allowed at all, so
|
|
|
428
440
|
plan mode uses Git Bash on native Windows: the same read-only git commands work
|
|
429
441
|
there, and a backslash before an ordinary character is accepted as a Windows
|
|
430
442
|
path separator while a backslash that escapes a metacharacter is still refused.
|
|
431
|
-
Everything else uses `read`, `grep`, `find`,
|
|
432
|
-
`
|
|
433
|
-
|
|
443
|
+
Everything else uses the structured readers — `read`, `grep`, `find`, `ls`, and
|
|
444
|
+
`read_doc` for a PDF, Word, OpenDocument, RTF, or Excel file — and the other
|
|
445
|
+
read-only tools already available (`web_search`, `web_fetch`,
|
|
446
|
+
`ask_user_question`, `todo`, `goal`).
|
|
434
447
|
|
|
435
448
|
```bash
|
|
436
449
|
git status && git diff --stat # allowed: two read-only git commands
|
|
@@ -441,6 +454,10 @@ git status | sh # blocked: pipes are never allowed
|
|
|
441
454
|
git diff > out.patch # blocked: redirection is a write
|
|
442
455
|
```
|
|
443
456
|
|
|
457
|
+
A refused shell command explains the fix: the working directory is already the
|
|
458
|
+
project root (so `cd` is unnecessary), and files belong to the `read`, `grep`,
|
|
459
|
+
`find`, and `ls` tools.
|
|
460
|
+
|
|
444
461
|
A string parser can never be the boundary, so capability gating replaced the
|
|
445
462
|
earlier segment-based command allowlist. The git shell guard is a narrow,
|
|
446
463
|
deliberate exception — an allowlist over read-only git command shapes, not a
|
|
@@ -452,7 +469,7 @@ first or wait for approval.
|
|
|
452
469
|
While planning, `subagent` stays available but every call is **forced
|
|
453
470
|
read-only**. Plan mode sets `readOnly: true` on the call in the `tool_call`
|
|
454
471
|
guard, and `subagent` in turn gives each spawned agent a reader-only `--tools`
|
|
455
|
-
list (`read`, `grep`, `find`, `ls`, `web_search`, `web_fetch`). The child
|
|
472
|
+
list (`read`, `grep`, `find`, `ls`, `read_doc`, `web_search`, `web_fetch`). The child
|
|
456
473
|
process enforces it, so a delegated `explorer`, `researcher`, or even `worker`
|
|
457
474
|
cannot write, edit, or run a shell — regardless of its prompt or an external
|
|
458
475
|
agent file that overrides it.
|
|
@@ -472,20 +489,46 @@ agent file that overrides it.
|
|
|
472
489
|
|
|
473
490
|
## Review screen
|
|
474
491
|
|
|
475
|
-
<img src="../../assets/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%">
|
|
492
|
+
<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%">
|
|
476
493
|
|
|
477
494
|
`exit_plan_mode` opens this screen on the plan file it just wrote. The body is
|
|
478
|
-
Pi's Markdown
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
495
|
+
rendered with Pi's own `Markdown` component and `getMarkdownTheme()`, so
|
|
496
|
+
headings, lists, inline code, code blocks, links, and quotes carry the active
|
|
497
|
+
theme's colours; task items get the shared `◼` (done) or `◻` (pending) marker,
|
|
498
|
+
like the bytefly screen and the question dialog. A plan taller than the terminal
|
|
499
|
+
scrolls under a gauge that names the section at the top of the viewport and the
|
|
500
|
+
line range it shows, and the footer lists only the keys that apply.
|
|
501
|
+
|
|
502
|
+
- **Actions.** The footer colours the primary actions (`a approve & run` in
|
|
503
|
+
`success`, `r refine` in `accent`). `a` asks `y/N` in the footer, then ends
|
|
504
|
+
plan mode, restores write access, and seeds `todo`; `y` or `Enter` approves and
|
|
505
|
+
`n`, `q`, or `Esc` goes back to reading. `r` keeps the plan mounted and opens
|
|
506
|
+
an inline editor beneath it (`Enter` submits, an empty buffer does nothing,
|
|
507
|
+
`Esc` returns to the plan); `Esc` keeps planning and tells the model the plan
|
|
508
|
+
was not approved; `x` leaves plan mode without approving and keeps the plan
|
|
509
|
+
file; `w` opens the browser review.
|
|
510
|
+
- **Sections.** The screen splits the body at its headings, names the section you
|
|
511
|
+
are in, and jumps between sections with `n`/`p`; the scroll row names the
|
|
512
|
+
current section when the plan has more than one.
|
|
513
|
+
- **What changed since the last review.** After a refinement, the screen names
|
|
514
|
+
how many sections changed, marks the current one in the scroll row, and the
|
|
515
|
+
browser outline dots each changed section, so a re-review does not mean
|
|
516
|
+
re-reading the whole plan. The baseline is one digest per heading section of
|
|
517
|
+
what the previous review showed, kept as a session entry, so a resumed session
|
|
518
|
+
or a `/tree` restore marks only what changed on its own branch.
|
|
519
|
+
- **Scrolling.** `↑`/`↓` or `j`/`k`, `space`/`b` or `PgUp`/`PgDn`, `d`/`u`,
|
|
520
|
+
`g`/`G`, and the mouse wheel. When the plan overflows, a gauge row above the
|
|
521
|
+
hints shows the scroll position, the percent, and the visible line range (the
|
|
522
|
+
range drops first on a narrow terminal); the gauge comes from the design
|
|
523
|
+
system's `gaugeBar` and stays visible while the refine editor is open, since the
|
|
524
|
+
body above is still clipped.
|
|
525
|
+
- **Narrow widths.** Hints are dropped whole as the terminal narrows, and each
|
|
526
|
+
footer leads with its safe exit (`Esc keep plan` in the review, `Esc back to
|
|
527
|
+
plan` in the inline editor) so the exit survives even when only one hint fits.
|
|
485
528
|
|
|
486
529
|
## Browser review
|
|
487
530
|
|
|
488
|
-
<img src="../../assets/plan-review.png" alt="The browser-based plan review: a sticky header with the plan title, a status chip, and the Approve & run, Save feedback, and Back to Pi actions, a Share action, a side panel with step progress and a section outline, and the plan as one measured column with a comment rail holding the owner's own comment and a teammate's @priya comment" width="100%">
|
|
531
|
+
<img src="../../assets/screens/plan-review.png" alt="The browser-based plan review: a sticky header with the plan title, a status chip, and the Approve & run, Save feedback, and Back to Pi actions, a Share action, a side panel with step progress and a section outline, and the plan as one measured column with a comment rail holding the owner's own comment and a teammate's @priya comment" width="100%">
|
|
489
532
|
|
|
490
533
|
Press `w` on the review screen — or run `/plan review [path]` — to open the plan
|
|
491
534
|
in the default browser on `127.0.0.1`. The page is a reading-first review
|
|
@@ -500,7 +543,7 @@ text first to quote it. **Save feedback** writes the comments into the plan
|
|
|
500
543
|
file and returns them to the model.
|
|
501
544
|
|
|
502
545
|
- **Shared theme.** The page uses the same Bytefly tokens, base rules, and
|
|
503
|
-
syntax-token
|
|
546
|
+
syntax-token colours as `file-browse` (`lib/web/`), follows the system light/dark
|
|
504
547
|
preference until the header toggle sets an explicit choice, and remembers that
|
|
505
548
|
choice under the shared `pi-web-theme` key.
|
|
506
549
|
- **One content column, one rail.** The document is GitHub's Markdown content
|
|
@@ -556,7 +599,6 @@ file and returns them to the model.
|
|
|
556
599
|
- **Highlighted code.** Fenced code is tokenized by the shared
|
|
557
600
|
`lib/highlight.ts`; the client copies only text and `tok-*` spans through
|
|
558
601
|
`DOMParser`, so a code line is never parsed as markup.
|
|
559
|
-
|
|
560
602
|
- **The file is the store.** Each comment is written as
|
|
561
603
|
`> [!REVIEW] <comment>` on the line after the block it anchors to, tagged with
|
|
562
604
|
its author when a teammate wrote it (`> [!REVIEW] @alice: <comment>`). Saving is
|
|
@@ -568,10 +610,14 @@ file and returns them to the model.
|
|
|
568
610
|
- **A revision keeps the review.** `write_plan` passes the file it is replacing
|
|
569
611
|
through `carryFeedback`, which re-attaches each comment by the strongest evidence
|
|
570
612
|
that survived: the anchored block's text, per occurrence, so a plan that repeats
|
|
571
|
-
a step keeps the comments apart; then the
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
613
|
+
a step keeps the comments apart; then the block whose wording still overlaps it —
|
|
614
|
+
half the longer block's words — so a reworded step keeps its note, and a step
|
|
615
|
+
that shifted down under an inserted one keeps it too; then the gap it left
|
|
616
|
+
between the steps that survived; then the last block that section still has. A
|
|
617
|
+
comment whose section is gone too moves to the plan's last block rather than
|
|
618
|
+
disappearing, and every one of those placements lands beside a block, never after
|
|
619
|
+
a blank line, so the page keeps the thread under its block. Only a plan with no
|
|
620
|
+
block at all leaves a comment in the unattached list.
|
|
575
621
|
- **Anchored to source.** `blocks.ts` splits the plan (fence-aware) into
|
|
576
622
|
source-line ranges; a comment is placed after the whole anchored block, so a
|
|
577
623
|
fenced code block is never split. A save re-checks the file against what the
|
|
@@ -594,20 +640,7 @@ file and returns them to the model.
|
|
|
594
640
|
so refreshing or reloading a conflicted page does not end the review. A
|
|
595
641
|
reviewer's tab opening or closing is not the review's lifetime.
|
|
596
642
|
|
|
597
|
-
|
|
598
|
-
|---|---|
|
|
599
|
-
| `GET /?token=…` | the review page (a JSON data island plus `/theme.js` and `/client.js`) |
|
|
600
|
-
| `GET /theme.js` | the shared pre-paint theme bootstrap |
|
|
601
|
-
| `GET /client.js` | the first-party client script |
|
|
602
|
-
| `GET /events?token=…` | Server-Sent Events stream: a `comment`, `plan`, or `share` frame when something changed, `closed` when the review ends, `revoked` when sharing stops, and a dropped stream to detect a closed tab |
|
|
603
|
-
| `POST /api/comment?token=…` | reviewer only: write their own comments into the plan, replacing only their own; never ends the review |
|
|
604
|
-
| `POST /api/feedback?token=…` | owner only: write the owner's comments and return to the model |
|
|
605
|
-
| `POST /api/approve?token=…` | owner only: save, then approve |
|
|
606
|
-
| `POST /api/cancel?token=…` | owner only: end the session without saving |
|
|
607
|
-
| `POST /api/share?token=…` | owner only: mint the first reviewer link and its QR |
|
|
608
|
-
| `POST /api/share/link?token=…` | owner only: mint one more reviewer link, so each teammate holds their own |
|
|
609
|
-
| `POST /api/share/revoke?token=…` | owner only: revoke one link by `{ id }`, leaving the others answering |
|
|
610
|
-
| `POST /api/share/stop?token=…` | owner only: revoke every reviewer link |
|
|
643
|
+
The routes it serves are in [Routes](#routes).
|
|
611
644
|
|
|
612
645
|
## Shared review
|
|
613
646
|
|
|
@@ -716,9 +749,9 @@ both.
|
|
|
716
749
|
|
|
717
750
|
| Integration point | How |
|
|
718
751
|
|---|---|
|
|
719
|
-
| Tools | `enter_plan_mode
|
|
720
|
-
| Commands and flags | `/plan` (toggle, or enter and send a task), `/plan list` (plans index), `/plan review [path]` (browser review), `/skill:init` (skill-backed `AGENTS.md` bootstrap), `--plan` launch flag, `Ctrl+Alt+P` shortcut. |
|
|
721
|
-
| 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 })
|
|
752
|
+
| Tools | `enter_plan_mode` (active; `readOnlyHint: true`), `write_plan` (`defaultActive: false`; `readOnlyHint: false`, `destructiveHint: true`), `exit_plan_mode` (`defaultActive: false`; `readOnlyHint: true`), `cancel_plan_mode` (`defaultActive: false`; `readOnlyHint: true`), `set_plan_steps` (active; `readOnlyHint: false`, `idempotentHint: true`). All five are `executionMode: "sequential"` and closed-world, and the read-only policy default-denies `set_plan_steps` while planning. Each returns typed `details` (`EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `CancelPlanModeDetails`, `SetPlanStepsDetails`), and `ExitPlanModeDetails` carries the review's `annotations` when a browser approval or save supplied comments; no `outputSchema`/`structuredContent`. |
|
|
753
|
+
| 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, `Ctrl+Alt+P` shortcut. |
|
|
754
|
+
| 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. |
|
|
722
755
|
| Entry requests | `pi.events` channel `PLAN_ENTER_REQUEST` from `lib/plan-entry.ts`: the `goal` workflow asks, with the shared consent sentence, and plan enables the mode on the caller's context without a second dialog. Subscribed on `session_start`, released on `session_shutdown`. |
|
|
723
756
|
| 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. |
|
|
724
757
|
| Lifecycle | `session_start`/`session_tree` restore state; `session_shutdown` clears the footer status and closes any browser review server. |
|
|
@@ -861,30 +894,32 @@ both.
|
|
|
861
894
|
| File | Responsibility |
|
|
862
895
|
|---|---|
|
|
863
896
|
| `index.ts` | Wiring: tools, command, flag, shortcut, events, context injection. |
|
|
864
|
-
| `types.ts` | `PlanModeEntry`, `EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `SetPlanStepsDetails`. |
|
|
897
|
+
| `types.ts` | `PlanModeEntry`, `EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `CancelPlanModeDetails`, `SetPlanStepsDetails`. |
|
|
865
898
|
| `policy.ts` | Plan mode's read-only policy, the shared prompt summary, and the bash-refusal guidance. |
|
|
866
899
|
| `git-bash.ts` | `checkReadOnlyGit`: the pure allowlist that restricts `bash` to read-only git commands. |
|
|
867
900
|
| `plans.ts` | Plan-file store: slug/stamp naming, directory resolution (repository, active worktree, or agent directory), containment, atomic `write`/`setSteps`/`saveFeedback`, and `read`/`latest`. |
|
|
868
901
|
| `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. |
|
|
869
|
-
| `steps.ts` | Plan-document structure
|
|
902
|
+
| `steps.ts` | Plan-document structure: the outline, step extraction and the `Steps`-section scope, the plan's own goal (`goalSection`), and the checkbox edit. |
|
|
903
|
+
| `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. |
|
|
870
904
|
| `format.ts` | Pure result/approval text: the plan preview, the step-cap diagnosis, step labels, the merge summary, and the verification note. |
|
|
871
|
-
| `plan-text.ts` | The plan text a review shows and compares: `sanitizePlanText`, the one normalization both sides use, the per-section `sectionDigests`, and `changedSections
|
|
905
|
+
| `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. |
|
|
872
906
|
| `runtime.ts` | Enabled state, tool gating, persistence (mode and review baseline), control-tool identity, footer status. |
|
|
873
|
-
| `tools.ts` | Registration entry: composes the
|
|
874
|
-
| `tools/` | One module per tool (`enter.ts`, `write.ts`, `exit.ts`, `steps.ts`), plus `todo-seed.ts` (the two nested `todo` writes) and `nested.ts` (reading a nested result). |
|
|
907
|
+
| `tools.ts` | Registration entry: composes the five tool registrations in the order the session sees them. |
|
|
908
|
+
| `tools/` | One module per tool (`enter.ts`, `write.ts`, `exit.ts`, `cancel.ts`, `steps.ts`), plus `todo-seed.ts` (the two nested `todo` writes) and `nested.ts` (reading a nested result). |
|
|
875
909
|
| `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. |
|
|
876
|
-
| `
|
|
910
|
+
| `review-prompt.ts` | Pure rendering and parsing for the compact remote review: one action table behind both the prompt and the reply parser. |
|
|
911
|
+
| `commands.ts` | `/plan` (enter, optional task), `/plan review [path]`, `/plan list`, `/plan share [stop]`, and `/plan cancel`. |
|
|
877
912
|
| `tui.ts` | `PlanViewComponent` (sectioned Markdown body, changed-section summary and marker, section jump, scroll gauge, the approve `y/N` question, approve/refine/browser/keep). |
|
|
878
|
-
| `plan-index.ts` | `/plan list`: the notice text and the docked index screen (title, step progress, age, focused path; Enter reviews). |
|
|
879
|
-
| `blocks.ts` | Source-line block model (fence-aware headings, paragraphs, list items, code, quotes, tables, rules, blanks). |
|
|
880
|
-
| `feedback.ts` | Comment codec: authored markers, `stripFeedback`, `parseFeedback
|
|
881
|
-
| `web.ts` | `createPlanWebReview`: the review session (per-token roles, the change stream, the plan-file poll, the body cap, settle-after-flush, abort) over
|
|
913
|
+
| `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). |
|
|
914
|
+
| `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. |
|
|
915
|
+
| `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. |
|
|
916
|
+
| `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. |
|
|
882
917
|
| `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. |
|
|
883
918
|
| `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. |
|
|
884
919
|
| `web-router.ts` | Pure request handler: token role, `Host` allowlist, origin, and method checks through one POST-and-role guard, then the page, comment, owner-save, approve, cancel, and the four share routes. |
|
|
885
920
|
| `web-page.ts` | The review page shell (header, reading progress, review facts, section outline, shortcut sheet, approve confirmation, share and name dialogs), its data island, and inline CSS. |
|
|
886
921
|
| `web-share.ts` | Pure sharing rules: the reachable LAN addresses from an injected snapshot, one tokenized URL, the link table (mint, find, revoke, list, state) with its cap, and the reasons a share is unavailable. |
|
|
887
|
-
| `web-share-session.ts` | The shared listener
|
|
922
|
+
| `web-share-session.ts` | The shared listener: its bound address, the link table over the session's tokens, `ShareState`, and invite/revoke/stop/close, injected with the request handler, role map, and frame hooks. |
|
|
888
923
|
| `web-client.ts` | The `/client.js` source: state and wiring (comment rail, save/approve/back, keyboard nav, the stream's lifetime) over the three fragments below. |
|
|
889
924
|
| `web-comments.ts` | The client's comment half: selection quotes, the composer, authored cards and who may edit them, delete-with-undo. |
|
|
890
925
|
| `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. |
|
|
@@ -896,8 +931,46 @@ both.
|
|
|
896
931
|
| [`../../lib/shell.ts`](../../lib/shell.ts) | The shell-escape rule the read-only git guard shares with the worktree isolation guard. |
|
|
897
932
|
| [`../../lib/highlight.ts`](../../lib/highlight.ts) | First-party escape-first syntax tokenizer (shared with `file-browse`). |
|
|
898
933
|
| [`../../lib/qr.ts`](../../lib/qr.ts) | QR matrix, terminal half-blocks, and inline SVG for the share link (shared with `wechat`). |
|
|
899
|
-
| [`../../lib/http/`](../../lib/http/) | The bind, `Host` allowlist, header sets, and response kit both browser surfaces serve through. |
|
|
934
|
+
| [`../../lib/http/`](../../lib/http/) | The one loopback server entry, the bind, `Host` allowlist, header sets, and response kit both browser surfaces serve through. |
|
|
900
935
|
| [`../../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. |
|
|
901
936
|
| [`../../lib/http/index.ts`](../../lib/http/index.ts) | Loopback HTTP layer shared with `file-browse`: the page policy and header sets, `ServeResponse`, the request-envelope helpers, bind/respond/close. |
|
|
902
937
|
| `../../lib/policy.ts` | Shared read-only capability policy (default-deny + `readOnlyHint`). |
|
|
903
938
|
| `../../lib/tui/chrome.ts` | Shared screen header, viewport-row helper, and styled key-hint footer. |
|
|
939
|
+
|
|
940
|
+
## Testing
|
|
941
|
+
|
|
942
|
+
```bash
|
|
943
|
+
bun test extensions/plan
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
Eleven co-located suites, declared in `test/suite-manifest.ts`:
|
|
947
|
+
|
|
948
|
+
- `plans.test.ts` — the plan-file store: naming, directory resolution, atomic
|
|
949
|
+
writes, `read`/`latest`/`index`, containment, `setSteps`, `saveFeedback`, and
|
|
950
|
+
step extraction.
|
|
951
|
+
- `feedback.test.ts` — the comment codec: authored markers, stripping and
|
|
952
|
+
parsing, who a reviewer's write is signed by, carrying feedback onto a
|
|
953
|
+
revision, and displaying stored comments.
|
|
954
|
+
- `tools.test.ts` — registration and all five tools, the approval merge into
|
|
955
|
+
`todo`, the session goal, and the call/result renderings.
|
|
956
|
+
- `review-prompt.test.ts` — the compact remote prompt and its reply parser
|
|
957
|
+
against one action table.
|
|
958
|
+
- `web.test.ts` — the browser review: the comment codec, the router, the page
|
|
959
|
+
roles and page shell, the client script, the stream, the plan-file poll, the
|
|
960
|
+
share rules, and a real loopback session.
|
|
961
|
+
- `web-share.test.ts` — the LAN address selector, `shareUrl`, the link table with
|
|
962
|
+
its cap, and the reasons a share is unavailable.
|
|
963
|
+
- `commands.test.ts` — `/plan review`, `/plan list`, `/plan share`, and the one
|
|
964
|
+
review gate shared by `exit_plan_mode` and the browser, including the abort and
|
|
965
|
+
baseline rules.
|
|
966
|
+
- `runtime.test.ts` — tool gating, persistence and status, control-tool identity,
|
|
967
|
+
the review baseline, branch restore, the read-only git allowlist, and the
|
|
968
|
+
path-argument scan.
|
|
969
|
+
- `tui.test.ts` — the review screen at fixed widths: Markdown body, scrolling,
|
|
970
|
+
section navigation, the gauge, footer hints, the approve question, the inline
|
|
971
|
+
refine editor, and the changed-section markers.
|
|
972
|
+
- `sections.test.ts` — the fence-aware scan: heading shape, fences, the preamble,
|
|
973
|
+
source lines and levels, and the occurrence key.
|
|
974
|
+
- `extension.test.ts` — the Pi wiring: registration, the entry request, the
|
|
975
|
+
session lifecycle, the tool-call guard, injected context, the command, and the
|
|
976
|
+
shortcut.
|