@gavin-hu/my-pi-agent 0.17.0 → 0.18.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 +17 -9
- package/README.zh-CN.md +16 -9
- package/extensions/bytefly/README.md +25 -18
- package/extensions/bytefly/notify/types.ts +1 -1
- package/extensions/bytefly/panel/settings.ts +25 -18
- package/extensions/file-browse/README.md +60 -21
- package/extensions/file-browse/chrome.ts +83 -0
- package/extensions/file-browse/client.ts +62 -0
- package/extensions/file-browse/config.ts +12 -3
- package/extensions/file-browse/diff-html.ts +94 -0
- package/extensions/file-browse/html.ts +19 -147
- package/extensions/file-browse/live-client.ts +47 -1
- package/extensions/file-browse/live.ts +35 -14
- package/extensions/file-browse/rev.ts +42 -0
- package/extensions/file-browse/router.ts +25 -4
- package/extensions/file-browse/server.ts +7 -0
- package/extensions/file-browse/styles.ts +2 -0
- package/extensions/goal/plan-route.ts +8 -2
- package/extensions/job/README.md +7 -5
- package/extensions/job/index.ts +15 -7
- package/extensions/memory/README.md +3 -3
- package/extensions/plan/README.md +280 -86
- package/extensions/plan/commands.ts +56 -5
- package/extensions/plan/feedback.ts +300 -63
- package/extensions/plan/index.ts +1 -1
- package/extensions/plan/plan-index.ts +1 -1
- package/extensions/plan/plan-text.ts +18 -1
- package/extensions/plan/plans.ts +165 -57
- package/extensions/plan/review-prompt.ts +93 -0
- package/extensions/plan/review.ts +78 -98
- package/extensions/plan/runtime.ts +1 -1
- package/extensions/plan/split.ts +87 -0
- package/extensions/plan/steps.ts +80 -93
- package/extensions/plan/tools/enter.ts +8 -3
- package/extensions/plan/tools/exit.ts +101 -23
- package/extensions/plan/tools/write.ts +8 -3
- package/extensions/plan/tools.ts +1 -1
- package/extensions/plan/tui.ts +51 -8
- package/extensions/plan/types.ts +37 -3
- package/extensions/plan/web-chrome.ts +2 -1
- package/extensions/plan/web-client.ts +121 -177
- package/extensions/plan/web-collab.ts +207 -0
- package/extensions/plan/web-comments.ts +269 -0
- package/extensions/plan/web-page.ts +103 -17
- package/extensions/plan/web-router.ts +146 -30
- package/extensions/plan/web-share-session.ts +197 -0
- package/extensions/plan/web-share.ts +169 -0
- package/extensions/plan/web-streams.ts +124 -0
- package/extensions/plan/web-tokens.ts +63 -0
- package/extensions/plan/web.ts +193 -45
- package/extensions/question/README.md +3 -0
- package/extensions/question/dialogs.ts +91 -1
- package/extensions/question/tools.ts +2 -1
- package/extensions/session-bus/README.md +247 -0
- package/extensions/session-bus/commands.ts +165 -0
- package/extensions/session-bus/format.ts +124 -0
- package/extensions/session-bus/index.ts +69 -0
- package/extensions/session-bus/notice.ts +58 -0
- package/extensions/session-bus/protocol.ts +134 -0
- package/extensions/session-bus/result.ts +243 -0
- package/extensions/session-bus/rows.ts +169 -0
- package/extensions/session-bus/runtime.ts +575 -0
- package/extensions/session-bus/store.ts +329 -0
- package/extensions/session-bus/team.ts +102 -0
- package/extensions/session-bus/tools.ts +227 -0
- package/extensions/session-bus/tui.ts +394 -0
- package/extensions/session-bus/types.ts +98 -0
- package/extensions/subagent/README.md +1 -1
- package/extensions/watch/README.md +9 -5
- package/extensions/watch/index.ts +15 -9
- package/extensions/wechat/README.md +20 -33
- package/extensions/wechat/bridge.ts +8 -8
- package/extensions/wechat/commands.ts +2 -2
- package/extensions/wechat/deliver.ts +57 -37
- package/extensions/wechat/interaction.ts +31 -16
- package/extensions/wechat/notices.ts +7 -7
- package/extensions/wechat/peer-worker.ts +19 -34
- package/extensions/wechat/presence.ts +3 -4
- package/extensions/wechat/prompt.ts +39 -24
- package/extensions/wechat/reply-pipeline.ts +12 -4
- package/extensions/worktree/git.ts +5 -0
- package/extensions/worktree/registry.ts +11 -5
- package/extensions/worktree/status.ts +1 -3
- package/lib/README.md +6 -2
- package/lib/claims/overlap.ts +43 -0
- package/lib/http/router.ts +40 -13
- package/lib/http/server.ts +27 -13
- package/lib/interaction.ts +10 -0
- package/lib/notice.ts +29 -2
- package/lib/plan-entry.ts +7 -2
- package/lib/plans/sections.ts +172 -0
- package/lib/plans/setting.ts +52 -0
- package/lib/plans/split.ts +215 -0
- package/lib/plans/store.ts +19 -0
- package/lib/plans/team.ts +151 -0
- package/lib/qr.ts +64 -0
- package/lib/repo-key.ts +74 -0
- package/lib/report.ts +52 -33
- package/lib/requirements.ts +1 -2
- package/lib/tui/README.md +16 -2
- package/lib/tui/dock-screen.ts +85 -17
- package/lib/tui/line-pane.ts +15 -4
- package/lib/tui/tool.ts +23 -4
- package/lib/web/components.ts +1 -1
- package/package.json +3 -2
- package/skills/init/SKILL.md +80 -45
- package/skills/init/references/agents-md-quality.md +26 -2
- package/skills/init/references/interview-questions.md +36 -14
- package/skills/next/SKILL.md +78 -53
- package/skills/parallel-dev/SKILL.md +150 -0
- package/extensions/plan/config.ts +0 -27
- package/extensions/wechat/qrcode.ts +0 -46
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A read-only exploration mode modelled on Claude Code's `EnterPlanMode` /
|
|
4
4
|
`ExitPlanMode` pair. While plan mode is on, the model investigates and proposes,
|
|
5
5
|
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
9
|
```bash
|
|
@@ -44,11 +44,14 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
44
44
|
quality checklist, and writes only after plan approval. It supports existing and
|
|
45
45
|
empty repositories, and falls back to evidence with recorded assumptions when
|
|
46
46
|
the interview is unavailable or cancelled; it never invents commands.
|
|
47
|
-
- **Plan files.** The model saves the plan with
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
- **Plan files, shared by the whole repository.** The model saves the plan with
|
|
48
|
+
`write_plan`; the file lands in
|
|
49
|
+
`<agent-dir>/plans/<repo-key>/<YYYY-MM-DD-HHmm>-<slug>.md`, keyed by the
|
|
50
|
+
repository's common git directory, so a plan approved in one worktree is the
|
|
51
|
+
same plan from the main checkout and from every other session. Outside a
|
|
52
|
+
repository the store is `<agent-dir>/plans`. Plans are outside the project, so
|
|
53
|
+
they are never untracked files and never appear in a `rewind` snapshot; a store
|
|
54
|
+
an older version left in `<repo>/.pi/plans` is moved in once, with a notice.
|
|
52
55
|
- **A discoverable index.** `/plan list` walks the plans directory and shows
|
|
53
56
|
every plan with its title, modified time, and step progress, newest first — in
|
|
54
57
|
the TUI as a docked list where `Enter` opens the plan's review, elsewhere as a
|
|
@@ -96,10 +99,10 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
96
99
|
it for approval. The user approves, keeps planning, asks for a refinement, or
|
|
97
100
|
exits plan mode without approving; the last keeps the plan file for a later
|
|
98
101
|
review and records no todo items or goal.
|
|
99
|
-
On a remote turn (WeChat) the plan file is sent first, then
|
|
100
|
-
title, step count,
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
On a remote turn (WeChat) the plan file is sent first, then one compact editor
|
|
103
|
+
prompt: the title, step count, first steps, mode status, and three actions
|
|
104
|
+
(approve, keep, exit). Any other reply is a refinement request; if the file
|
|
105
|
+
upload fails, its text is sent instead.
|
|
103
106
|
- **Browser review on demand.** Press `w` on the review screen (or run
|
|
104
107
|
`/plan review [path]`) to open the plan in a local browser: a reading-first
|
|
105
108
|
page with the plan's H1 as its title, GitHub's Markdown content column, a
|
|
@@ -115,10 +118,11 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
115
118
|
- **Steps become todos.** On approval, the top-level numbered/bulleted items of
|
|
116
119
|
the plan's `Steps` section are recorded with the `todo` tool (via
|
|
117
120
|
`ctx.executeTool`); steps marked `- [x]` (or `[DONE:n]`) seed as completed, and
|
|
118
|
-
nested sub-bullets are treated as notes
|
|
119
|
-
replacement: existing items keep their place
|
|
120
|
-
already there is skipped, and the tool's item
|
|
121
|
-
dropping anything. The plan's `## Verification`
|
|
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**
|
|
122
126
|
todo item, summarized on one line (`Verify: …`) — a fenced block contributes its
|
|
123
127
|
commands, not its fence lines. It is not a plan step, so it is ticked in the todo
|
|
124
128
|
list rather than with `set_plan_steps`, and it never counts against the 50-step
|
|
@@ -132,21 +136,87 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
132
136
|
instead of beside it. On approval, a goal the goal workflow left as a draft
|
|
133
137
|
(`planning`) is finalized from the plan's own `## Goal` section — falling back
|
|
134
138
|
to the title — through the `goal` tool, with no extra dialog, because approving
|
|
135
|
-
the plan *is* the decision. A session with no goal at all
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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).
|
|
139
157
|
- **Branch-aware.** The enabled flag and the review baseline are custom session
|
|
140
158
|
entries, so `/resume` and `/tree` restore the right mode and keep the
|
|
141
159
|
changed-section markers.
|
|
142
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
|
+
|
|
167
|
+
```text
|
|
168
|
+
exit_plan_mode({
|
|
169
|
+
plan_path: "~/.pi/agent/plans/a1f9c02/2026-11-04-1802-billing.md",
|
|
170
|
+
units: [
|
|
171
|
+
{ name: "contract", paths: ["src/billing/contract"], test: "bun test src/billing/contract", lead: true },
|
|
172
|
+
{ name: "core", paths: ["src/billing/core"], test: "bun test src/billing/core", dependsOn: ["contract"] }
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The result prints the roster card, which is all the package can do for the sessions
|
|
178
|
+
it cannot start — and the one line it can act on for the session it is in:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
Split: 2 units · ~/.pi/agent/plans/a1f9c02/2026-11-04-1802-billing.md
|
|
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
|
+
```
|
|
188
|
+
|
|
189
|
+
The last line is the lead's own label. The card's other lines are for the sessions
|
|
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.
|
|
194
|
+
|
|
195
|
+
What the gate enforces, and why each rule is a refusal rather than a warning:
|
|
196
|
+
|
|
197
|
+
| Refusal | Why |
|
|
198
|
+
|---|---|
|
|
199
|
+
| fewer than two units | One path is not a split: approve as one flow, or build the thinnest end-to-end path first. |
|
|
200
|
+
| overlapping prefixes | Two owners on one path is a merge conflict by construction. |
|
|
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.
|
|
212
|
+
|
|
143
213
|
## Tools
|
|
144
214
|
|
|
145
215
|
| Tool | Active | Purpose |
|
|
146
216
|
|---|---|---|
|
|
147
217
|
| `enter_plan_mode` | normal mode | Ask to enter plan mode; empty schema, confirmed with `ctx.ui.confirm`. |
|
|
148
218
|
| `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. A plan with more than 50 top-level steps in its `Steps` section is refused, with the cap and the fix named, and nothing is written. |
|
|
149
|
-
| `exit_plan_mode` | plan mode | Read the plan file (`plan_path`) and present it for approval. The review also offers **Exit plan mode (discard)**, which leaves plan mode without approving. Refuses a file whose step list is over the cap, before the review screen. |
|
|
219
|
+
| `exit_plan_mode` | plan mode | Read the plan file (`plan_path`) and present it for approval. Optional `units` proposes a split when `plan.split` is on; it is validated before the review, written into the plan on approval, and ignored with one note while the switch is off. The review also offers **Exit plan mode (discard)**, which leaves plan mode without approving. Refuses a file whose step list is over the cap, before the review screen. |
|
|
150
220
|
| `cancel_plan_mode` | plan mode | Leave plan mode without a plan file or a review. Restores write access; records no todo items or goal; keeps any saved plan. Empty schema. |
|
|
151
221
|
| `set_plan_steps` | normal mode | Keep an approved plan current: set the checkboxes of the given top-level steps (`updates`, each a 1-based `step` in the order `exit_plan_mode` listed, plus `status`, and `expected` — the step's text, so a reordered plan is refused) to `pending` or `completed` in one write. Only the checkboxes change, and a reopened step drops its `[DONE:n]` marker. The todo item approval seeded from a step is ticked with it: the file is written first and is authoritative, a renamed item is left alone, and a failed todo write is reported in the result. |
|
|
152
222
|
|
|
@@ -194,15 +264,15 @@ approval. Its workflow is:
|
|
|
194
264
|
```text
|
|
195
265
|
/skill:init
|
|
196
266
|
└─> plan mode (read-only)
|
|
197
|
-
└─> recon:
|
|
198
|
-
└─> interview: built-in bank, one call of at most 4 questions
|
|
267
|
+
└─> recon: effective instruction file, sourced evidence, existing instructions
|
|
268
|
+
└─> interview: built-in bank, one call of at most 4 questions, minus resolved facts
|
|
199
269
|
├─ answered ───────────────> reconcile with evidence
|
|
200
270
|
└─ unavailable/cancelled ──> evidence + assumptions
|
|
201
271
|
└─> draft: new file, exact edits, or an honest no-op
|
|
202
272
|
└─> quality gate
|
|
203
273
|
└─> write_plan ──> exit_plan_mode
|
|
204
274
|
├─ not approved ──> refine the same plan
|
|
205
|
-
└─ approved ──────> write/edit
|
|
275
|
+
└─ approved ──────> write/edit the target
|
|
206
276
|
└─ read back + quality checklist
|
|
207
277
|
```
|
|
208
278
|
|
|
@@ -210,15 +280,26 @@ The interview comes from a built-in bank in
|
|
|
210
280
|
[`skills/init/references/interview-questions.md`](../../skills/init/references/interview-questions.md):
|
|
211
281
|
core questions cover purpose, verification, non-obvious rules, isolation,
|
|
212
282
|
completion, sensitive paths, and dependency or network limits, with state-specific
|
|
213
|
-
entries for an existing file, an empty repository,
|
|
214
|
-
The first call uses
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
and continues from
|
|
283
|
+
entries for an existing file, an empty repository, related instruction files, and
|
|
284
|
+
a shadowing override. The first call uses the bank's deterministic set for the
|
|
285
|
+
state, minus every field the evidence sheet resolved; a second call is allowed
|
|
286
|
+
only for a material ambiguity. If the tool is inactive, there is no UI, or the
|
|
287
|
+
user cancels, the skill records the gaps as assumptions and continues from
|
|
288
|
+
repository evidence.
|
|
289
|
+
|
|
290
|
+
Recon resolves the effective instruction file first: a root `AGENTS.override.md`
|
|
291
|
+
replaces `AGENTS.md` and becomes the target, while an override above the project
|
|
292
|
+
root is reported rather than edited. Recon also builds a sourced evidence sheet —
|
|
293
|
+
commands with the file that defines them, generated and vendored paths from
|
|
294
|
+
`.gitignore`, workspace packages and boundaries, and purpose from the README or
|
|
295
|
+
manifest — and the interview asks only what it could not resolve. The plan uses
|
|
296
|
+
the canonical outline, including `## Steps` and `## Verification`, so approval
|
|
297
|
+
seeds the apply actions rather than the draft's bullets.
|
|
218
298
|
|
|
219
299
|
The quality reference is
|
|
220
300
|
[`skills/init/references/agents-md-quality.md`](../../skills/init/references/agents-md-quality.md).
|
|
221
|
-
An existing
|
|
301
|
+
An existing instruction file is reviewed and updated only after approval, a root
|
|
302
|
+
override is edited in place or removed explicitly when consolidating, an accurate
|
|
222
303
|
file can produce a no-op plan, and an empty repository gets only known facts.
|
|
223
304
|
The skill needs the plan extension for its review gate. It also ships with the
|
|
224
305
|
package, so load the whole package (`pi -e .`) or install it; loading only
|
|
@@ -233,14 +314,15 @@ so headings, lists, inline code, code blocks, links, and quotes carry the active
|
|
|
233
314
|
theme's colors; task items get the shared `◼` (done) or `◻` (pending) marker,
|
|
234
315
|
like the bytefly screen and the question dialog. The footer colors the primary
|
|
235
316
|
actions (`a approve & run` in `success`, `r refine` in `accent`).
|
|
236
|
-
`a` ends plan mode, restores write access, and
|
|
317
|
+
`a` asks `y/N` in the footer, then ends plan mode, restores write access, and
|
|
318
|
+
seeds `todo`; `y` or `Enter` approves and `n`, `q`, or `Esc` goes back to
|
|
319
|
+
reading. `r` keeps the plan
|
|
237
320
|
mounted and opens an inline editor beneath it (`Enter` submits, an empty buffer
|
|
238
321
|
does nothing, `Esc` returns to the plan); `Esc` keeps planning and tells the
|
|
239
322
|
model the plan was not approved; `x` leaves plan mode without approving and
|
|
240
|
-
keeps the plan file. The dialog path
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
plan mode. When the plan changed since the previous review
|
|
323
|
+
keeps the plan file. The dialog path is one editor prompt: `1` approve, `2` full
|
|
324
|
+
plan, `3` keep, `4` exit; any other text refines the plan. Dismissing or timing
|
|
325
|
+
out the prompt exits without approving. When the plan changed since the previous review
|
|
244
326
|
on this branch, a summary line under the title names how many sections changed
|
|
245
327
|
and the scroll row appends `· changed` to the current section. Scroll with `↑`/`↓` or `j`/`k`, `space`/`b` or
|
|
246
328
|
`PgUp`/`PgDn`, `d`/`u`, `g`/`G`, and the mouse wheel. Press `n`/`p` to jump to
|
|
@@ -255,14 +337,12 @@ dropped whole as the terminal narrows, and each footer leads with its safe exit
|
|
|
255
337
|
(`Esc keep plan` in the review, `Esc back to plan` in the inline editor) so
|
|
256
338
|
the exit survives even when only one hint fits.
|
|
257
339
|
|
|
258
|
-
Dialog-capable non-TUI modes (RPC)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
is
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
**file** first (falling back to the plan text if the upload fails) so the whole
|
|
265
|
-
plan is reviewable there; RPC has no channel and still sees only the choices.
|
|
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.
|
|
266
346
|
Without any UI the tools fail with an actionable message instead of deciding for
|
|
267
347
|
the user.
|
|
268
348
|
|
|
@@ -289,13 +369,13 @@ enter_plan_mode requested plan mode
|
|
|
289
369
|
|
|
290
370
|
```
|
|
291
371
|
write_plan Add rate limiting
|
|
292
|
-
✓ Saved plan
|
|
372
|
+
✓ Saved plan ~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
293
373
|
```
|
|
294
374
|
|
|
295
375
|
```
|
|
296
376
|
exit_plan_mode submitted a plan
|
|
297
377
|
✓ Plan approved
|
|
298
|
-
|
|
378
|
+
~/.pi/agent/plans/a1f9c02/2026-10-09-1809-add-rate-limiting.md
|
|
299
379
|
|
|
300
380
|
# Add rate limiting
|
|
301
381
|
1. Add a token bucket …
|
|
@@ -303,16 +383,6 @@ exit_plan_mode submitted a plan
|
|
|
303
383
|
… 12 more lines (ctrl+o to expand)
|
|
304
384
|
```
|
|
305
385
|
|
|
306
|
-
## Configuration
|
|
307
|
-
|
|
308
|
-
The `plan` section of the shared `bytefly.json` (global then project), or the
|
|
309
|
-
**Offer the session goal** row in `/bytefly settings`. It is read when a plan is
|
|
310
|
-
approved, so a change applies to the next approval without a reload.
|
|
311
|
-
|
|
312
|
-
| Key | Default | Meaning |
|
|
313
|
-
|---|---|---|
|
|
314
|
-
| `goalPrompt` | `true` | Ask before recording an approved plan's objective as the session goal; `false` records it without asking. |
|
|
315
|
-
|
|
316
386
|
## Security
|
|
317
387
|
|
|
318
388
|
Plan mode is a **guard rail, not a sandbox**: extensions run with Pi's OS
|
|
@@ -409,12 +479,13 @@ Pi's Markdown renderer with the active theme, so headings, lists, code, and
|
|
|
409
479
|
links read as they do in the transcript; the checkboxes in `Steps` use the
|
|
410
480
|
shared `◼`/`◻` vocabulary. A plan taller than the terminal scrolls under a gauge
|
|
411
481
|
that names the section at the top of the viewport and the line range it shows,
|
|
412
|
-
and the footer lists only the keys that apply. `Esc` keeps planning, `a`
|
|
413
|
-
and runs, `r` refines inline, and `w` opens the browser
|
|
482
|
+
and the footer lists only the keys that apply. `Esc` keeps planning, `a` asks
|
|
483
|
+
`y/N` and then approves and runs, `r` refines inline, and `w` opens the browser
|
|
484
|
+
review below.
|
|
414
485
|
|
|
415
486
|
## Browser review
|
|
416
487
|
|
|
417
|
-
<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 side panel with step progress and a section outline, and the plan as one measured column with a comment rail and
|
|
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%">
|
|
418
489
|
|
|
419
490
|
Press `w` on the review screen — or run `/plan review [path]` — to open the plan
|
|
420
491
|
in the default browser on `127.0.0.1`. The page is a reading-first review
|
|
@@ -447,17 +518,26 @@ file and returns them to the model.
|
|
|
447
518
|
labelled and indented as a thread, so it reads in place and the plan keeps one
|
|
448
519
|
measure at every width. There is no separate column to reflow.
|
|
449
520
|
- **Nothing typed is lost.** **Save feedback**, **Approve & run**, and **Back to
|
|
450
|
-
Pi** refuse while a composer holds text and put the cursor back in it,
|
|
451
|
-
deleted comment is offered back in the header for a few seconds
|
|
452
|
-
|
|
521
|
+
Pi** refuse while a composer holds text and put the cursor back in it, a
|
|
522
|
+
deleted comment is offered back in the header for a few seconds, and this tab's
|
|
523
|
+
unsaved work survives a reload: the comments it added and the text still in its
|
|
524
|
+
composer are kept in `sessionStorage` under a digest of the plan text the page
|
|
525
|
+
rendered, then restored on the next load of that same text with the composer
|
|
526
|
+
reopened at the caret and the selection it quoted, and dropped when the plan
|
|
527
|
+
changed underneath them. A composer closed with `Esc` or **Cancel** discards its
|
|
528
|
+
text, so only an abandoned tab gets it back. Nothing is stored on the server, and
|
|
529
|
+
another writer's comment is never part of the draft.
|
|
453
530
|
- **Prints as a document.** Printing uses the light palette whatever the screen
|
|
454
531
|
shows, drops the header, panel, rail, and progress bar, and lets the comments
|
|
455
532
|
stay with the blocks they belong to.
|
|
456
533
|
- **Keyboard first.** `j`/`k` move to the next or previous block, `c` comments
|
|
457
534
|
on the current block, `a` approves (the approval flow only), `s` saves, `Esc`
|
|
458
535
|
closes the composer or the sheet and then the review, and `?` opens a shortcut
|
|
459
|
-
sheet; the header's `?` button opens the same sheet.
|
|
460
|
-
|
|
536
|
+
sheet; the header's `?` button opens the same sheet. Adding, editing, or
|
|
537
|
+
deleting a comment rebuilds the document, so the tab stop returns to that
|
|
538
|
+
block's comment control instead of being dropped, and the status line says
|
|
539
|
+
which change landed; a repaint the stream caused never moves focus. Either
|
|
540
|
+
dialog traps `Tab` and hands focus back to whatever opened it.
|
|
461
541
|
- **Decision in place.** A review opened for approval (`w` on the review screen)
|
|
462
542
|
shows **Approve & run** beside Save feedback: it persists the current comments,
|
|
463
543
|
ends plan mode, restores write access, seeds `todo`, and hands control back to
|
|
@@ -478,10 +558,20 @@ file and returns them to the model.
|
|
|
478
558
|
`DOMParser`, so a code line is never parsed as markup.
|
|
479
559
|
|
|
480
560
|
- **The file is the store.** Each comment is written as
|
|
481
|
-
`> [!REVIEW] <comment>` on the line after the block it anchors to
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
561
|
+
`> [!REVIEW] <comment>` on the line after the block it anchors to, tagged with
|
|
562
|
+
its author when a teammate wrote it (`> [!REVIEW] @alice: <comment>`). Saving is
|
|
563
|
+
idempotent per writer: that writer's previous markers are replaced and everyone
|
|
564
|
+
else's are kept, so two people reviewing the same plan cannot erase each other.
|
|
565
|
+
Stripping is fence-aware: a `> [!REVIEW]` line inside a code block is
|
|
566
|
+
documentation, not feedback. The file is the only copy of a saved comment; the
|
|
567
|
+
ones this tab has not saved yet live in its own `sessionStorage` and nowhere else.
|
|
568
|
+
- **A revision keeps the review.** `write_plan` passes the file it is replacing
|
|
569
|
+
through `carryFeedback`, which re-attaches each comment by the strongest evidence
|
|
570
|
+
that survived: the anchored block's text, per occurrence, so a plan that repeats
|
|
571
|
+
a step keeps the comments apart; then the same section at the same position, so a
|
|
572
|
+
reworded step keeps its note; then the end of that section. A comment whose
|
|
573
|
+
section is gone too moves to the end of the file, where the page shows it as
|
|
574
|
+
unattached, rather than disappearing.
|
|
485
575
|
- **Anchored to source.** `blocks.ts` splits the plan (fence-aware) into
|
|
486
576
|
source-line ranges; a comment is placed after the whole anchored block, so a
|
|
487
577
|
fenced code block is never split. A save re-checks the file against what the
|
|
@@ -492,24 +582,89 @@ file and returns them to the model.
|
|
|
492
582
|
save that carries no comments asks for nothing, so the review screen comes back
|
|
493
583
|
instead. `/plan review` sends them as a user message (a plan-mode revision or a
|
|
494
584
|
plain update, depending on the mode).
|
|
495
|
-
- **Loopback
|
|
496
|
-
per-session token in the URL, rejects a
|
|
497
|
-
serves only `GET`, `HEAD`, and `POST`, and caps the
|
|
585
|
+
- **Loopback first, tokenized.** The server binds `127.0.0.1`, carries a random
|
|
586
|
+
per-session token in the URL, rejects a `Host` header it was not bound on,
|
|
587
|
+
refuses a cross-origin POST, serves only `GET`, `HEAD`, and `POST`, and caps the
|
|
588
|
+
request body. Sharing adds a second listener (below); it never widens the first. A save or cancel
|
|
498
589
|
resolves the session only after its HTTP response is delivered, so the browser
|
|
499
590
|
never sees a false "Failed to fetch". The last bound port is remembered per
|
|
500
591
|
plan, so reviewing the same plan again reuses the origin (and the browser's
|
|
501
|
-
saved theme with it). It is closed on save
|
|
502
|
-
`session_shutdown`; a reload reconnects inside that grace,
|
|
503
|
-
reloading a conflicted page does not end the review.
|
|
592
|
+
saved theme with it). It is closed on the owner's save or cancel, on the owner's
|
|
593
|
+
page going away, or on `session_shutdown`; a reload reconnects inside that grace,
|
|
594
|
+
so refreshing or reloading a conflicted page does not end the review. A
|
|
595
|
+
reviewer's tab opening or closing is not the review's lifetime.
|
|
504
596
|
|
|
505
597
|
| Route | Result |
|
|
506
598
|
|---|---|
|
|
507
599
|
| `GET /?token=…` | the review page (a JSON data island plus `/theme.js` and `/client.js`) |
|
|
508
600
|
| `GET /theme.js` | the shared pre-paint theme bootstrap |
|
|
509
601
|
| `GET /client.js` | the first-party client script |
|
|
510
|
-
| `GET /events?token=…` | Server-Sent Events stream,
|
|
511
|
-
| `POST /api/
|
|
512
|
-
| `POST /api/
|
|
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 |
|
|
611
|
+
|
|
612
|
+
## Shared review
|
|
613
|
+
|
|
614
|
+
A review is not only the owner's. **Share** in the review header (or `/plan share`)
|
|
615
|
+
mints a reviewer link, binds a second listener on the machine's first reachable
|
|
616
|
+
IPv4 address, and shows the link with a QR a phone can scan. **New link** in the
|
|
617
|
+
same dialog mints one more, so each teammate holds a link of their own. **Stop
|
|
618
|
+
sharing** revokes every link and closes that listener; closing the review does
|
|
619
|
+
both.
|
|
620
|
+
|
|
621
|
+
- **Two roles, one token each.** The owner's token — the one the browser opened
|
|
622
|
+
with — decides: it may comment, save (which returns to the model), approve,
|
|
623
|
+
cancel, mint links, and revoke them. A reviewer token may read and comment, and
|
|
624
|
+
its one write route is `POST /api/comment`: `/api/feedback`, `/api/approve`,
|
|
625
|
+
`/api/cancel`, and the `/api/share` family answer `403`, and the page renders
|
|
626
|
+
only the name control and **Post comments** for it — and once that link has
|
|
627
|
+
posted, the control shows the bound name instead of offering the dialog.
|
|
628
|
+
Anything the reviewer holds is reachable only while its link is shared.
|
|
629
|
+
- **One link is one person.** The dialog lists every link with the name it has
|
|
630
|
+
bound, and the two things an owner does with one: **Copy**, and **Revoke**, which
|
|
631
|
+
drops that link alone — its page is told `revoked` and its token stops
|
|
632
|
+
answering, while the others keep working. Revoking the last link stops sharing.
|
|
633
|
+
A review holds at most eight links, because each is a live credential.
|
|
634
|
+
- **The owner's page is the review's lifetime.** A reviewer's tab opening or
|
|
635
|
+
closing changes nothing; the review ends when the owner saves, approves, or
|
|
636
|
+
closes it, or when the owner's own page goes away. A reviewer's post writes the
|
|
637
|
+
plan and updates every open page, and leaves the review waiting. A page that is
|
|
638
|
+
still open when the review ends is told why — `closed`, or `revoked` when its
|
|
639
|
+
link was revoked or sharing was stopped — and keeps the plan readable.
|
|
640
|
+
- **Signed comments.** A reviewer names herself once, on the page, and that name
|
|
641
|
+
is bound to her link on its first post: the link stores under it from then on,
|
|
642
|
+
a second tab of the same link that picked another name is refused (`409`) rather
|
|
643
|
+
than silently re-signing, and a name another live link already holds is refused
|
|
644
|
+
when it is first claimed. A bound name is shown, not offered, so a reviewer can
|
|
645
|
+
edit or withdraw her own notes and nobody else's, and her second post replaces
|
|
646
|
+
rather than duplicates the first. The name is a label on a bearer link, not an
|
|
647
|
+
account, which is why the owner still makes the only decision.
|
|
648
|
+
- **One listener, one machine.** Sharing opens exactly the address the dialog
|
|
649
|
+
printed — never `0.0.0.0` — and the `Host` allowlist becomes loopback plus that
|
|
650
|
+
address, however many links answer on it. A machine with no reachable IPv4
|
|
651
|
+
address reports why instead of printing a link that cannot work. There is no
|
|
652
|
+
tunnel, no relay, and no cross-machine sync: this is the same Wi-Fi.
|
|
653
|
+
- **Live.** Every open page follows the stream, so a reviewer's post appears on the
|
|
654
|
+
owner's page without a reload. The review also polls its own plan file once a
|
|
655
|
+
second, so an edit this review did not make — the model rewriting the plan while
|
|
656
|
+
`/plan review` is open in normal mode, or a teammate's editor — repaints every
|
|
657
|
+
reader instead of waiting for the next request, and a save that changed nothing
|
|
658
|
+
but comments stays quiet. A page with a draft open refuses to repaint and says
|
|
659
|
+
what arrived instead. The owner's terminal is told too — `Plan review: @alice
|
|
660
|
+
posted 2 comments.` — because whoever is waiting in the TUI is not looking at
|
|
661
|
+
the page.
|
|
662
|
+
- **What a shared link is.** A bearer credential: anyone who has it reads the plan
|
|
663
|
+
and can comment, under the one name it binds on its first post. The dialog says
|
|
664
|
+
so, `Referrer-Policy: no-referrer` keeps a click from leaking it, and **Revoke**
|
|
665
|
+
(or **Stop sharing**) is the revocation. The plan file is the record — a comment
|
|
666
|
+
arrives as an author-tagged marker, so `/resume` and the terminal show the same
|
|
667
|
+
review the page did.
|
|
513
668
|
|
|
514
669
|
## Limitations
|
|
515
670
|
|
|
@@ -536,6 +691,18 @@ file and returns them to the model.
|
|
|
536
691
|
- Feedback stripping follows fenced code blocks; an unterminated fence makes the
|
|
537
692
|
rest of the file code, so a marker-looking line after it is treated as content
|
|
538
693
|
rather than stored feedback.
|
|
694
|
+
- A comment is one line. Stored feedback is a single blockquote line, so newlines
|
|
695
|
+
and runs of spaces collapse before it is written; a pasted multi-line
|
|
696
|
+
instruction reaches the model as one sentence, which is what keeps the marker
|
|
697
|
+
grammar valid markdown.
|
|
698
|
+
- The plan-file poll runs once a second, so an edit made elsewhere reaches an open
|
|
699
|
+
page within about that long, and a write that leaves the plan's text identical —
|
|
700
|
+
another process appending only a comment — is not announced; the next save still
|
|
701
|
+
refuses a file it did not render (`409`). It is a poll rather than a file
|
|
702
|
+
watcher, so a change on a filesystem that reports nothing at all is still seen
|
|
703
|
+
at the next request, because every page read re-compares the file.
|
|
704
|
+
- A shared review holds at most eight links. Each is a live bearer credential, so
|
|
705
|
+
a ninth is refused with the cap named rather than minted.
|
|
539
706
|
|
|
540
707
|
## Non-goals
|
|
541
708
|
|
|
@@ -551,7 +718,7 @@ file and returns them to the model.
|
|
|
551
718
|
|---|---|
|
|
552
719
|
| Tools | `enter_plan_mode`, `write_plan`, `exit_plan_mode`, and `set_plan_steps` registered with `executionMode: "sequential"`. `enter_plan_mode` is active normally with `readOnlyHint: true`; `write_plan` (`readOnlyHint: false`) and `exit_plan_mode` (`readOnlyHint: true`) are `defaultActive: false`. `set_plan_steps` is active normally (`readOnlyHint: false`, `idempotentHint: true`); the read-only policy default-denies it in plan mode. Each returns typed `details` (`EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `SetPlanStepsDetails`), and `ExitPlanModeDetails` carries the review's `annotations` when a browser approval or save supplied comments; no `outputSchema`/`structuredContent`. |
|
|
553
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. |
|
|
554
|
-
| 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 })`: a `planning` goal is promoted to the plan's `## Goal`, a session with none is
|
|
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 })`: 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. |
|
|
555
722
|
| 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`. |
|
|
556
723
|
| 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. |
|
|
557
724
|
| Lifecycle | `session_start`/`session_tree` restore state; `session_shutdown` clears the footer status and closes any browser review server. |
|
|
@@ -595,8 +762,9 @@ file and returns them to the model.
|
|
|
595
762
|
`ctx`, so a listener could not refresh the todo widget; calling the real tool
|
|
596
763
|
runs its validation and widget sync and needs no change to `todo`. Only the
|
|
597
764
|
top-level list items of the `Steps` section become steps (the whole document
|
|
598
|
-
is the fallback when that heading is absent),
|
|
599
|
-
never seed the list. The list is
|
|
765
|
+
is the fallback when that heading is absent), and an indented line continues
|
|
766
|
+
its step, so the outline's prose lists never seed the list. The list is
|
|
767
|
+
whole-list replacement, so the current items
|
|
600
768
|
are read back from the branch's last `todo` result (the published state
|
|
601
769
|
channel; no import from that extension) and the steps are merged into them.
|
|
602
770
|
A rejected seed reports the nested tool's own reason in the approval message
|
|
@@ -668,6 +836,25 @@ file and returns them to the model.
|
|
|
668
836
|
so annotating never moves the text. That removes the two-column grid, the
|
|
669
837
|
`bf-review--no-rail` collapse, and the breakpoint ladder, and matches the TUI,
|
|
670
838
|
where neither review header carries a progress counter.
|
|
839
|
+
- **A poll, not a file watcher.** An open review re-reads its plan once a second
|
|
840
|
+
and announces when the text stopped matching what the page rendered, because the
|
|
841
|
+
review owns the page and not the file: `/plan review` runs in normal mode, so the
|
|
842
|
+
model can rewrite the plan underneath it. The house rule is a poll (see
|
|
843
|
+
`file-browse`'s note): `fs.watch` is the one part of `node:fs` whose availability
|
|
844
|
+
and event semantics differ per platform, and it would add a long-lived resource
|
|
845
|
+
to start and release. The check only announces — it never writes `pageText`,
|
|
846
|
+
which stays the anchor a save is validated against — so a save through the routes
|
|
847
|
+
says nothing, and an announced edit can be announced once more by the read that
|
|
848
|
+
repaints: that read is what updates `pageText`, so it converges instead of
|
|
849
|
+
looping, at the cost of one extra refetch.
|
|
850
|
+
- **One link is one person, and the token table is the roster.** A shared review's
|
|
851
|
+
unit of access is a link, not a room: a token names a role and binds the name its
|
|
852
|
+
owner posts under, so two people on one link are one writer and the second name
|
|
853
|
+
is refused. The owner hands out one link per teammate and takes one back without
|
|
854
|
+
disturbing the rest, and the exit of a link is the exit of one token, one stream,
|
|
855
|
+
and one marker author — which is why the streams are addressed by token rather
|
|
856
|
+
than by role, and why the link table is pure and separate from the listener that
|
|
857
|
+
answers on it.
|
|
671
858
|
|
|
672
859
|
## Files
|
|
673
860
|
|
|
@@ -675,7 +862,6 @@ file and returns them to the model.
|
|
|
675
862
|
|---|---|
|
|
676
863
|
| `index.ts` | Wiring: tools, command, flag, shortcut, events, context injection. |
|
|
677
864
|
| `types.ts` | `PlanModeEntry`, `EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `SetPlanStepsDetails`. |
|
|
678
|
-
| `config.ts` | The `plan` section loader: `goalPrompt`, default on. |
|
|
679
865
|
| `policy.ts` | Plan mode's read-only policy, the shared prompt summary, and the bash-refusal guidance. |
|
|
680
866
|
| `git-bash.ts` | `checkReadOnlyGit`: the pure allowlist that restricts `bash` to read-only git commands. |
|
|
681
867
|
| `plans.ts` | Plan-file store: slug/stamp naming, directory resolution (repository, active worktree, or agent directory), containment, atomic `write`/`setSteps`/`saveFeedback`, and `read`/`latest`. |
|
|
@@ -687,22 +873,30 @@ file and returns them to the model.
|
|
|
687
873
|
| `tools.ts` | Registration entry: composes the four tool registrations in the order the session sees them. |
|
|
688
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). |
|
|
689
875
|
| `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. |
|
|
690
|
-
| `commands.ts` | `/plan` (enter, optional task)
|
|
691
|
-
| `tui.ts` | `PlanViewComponent` (sectioned Markdown body, changed-section summary and marker, section jump, scroll gauge, approve/refine/browser/keep). |
|
|
876
|
+
| `commands.ts` | `/plan` (enter, optional task), `/plan review [path]`, and `/plan share [stop]`. |
|
|
877
|
+
| `tui.ts` | `PlanViewComponent` (sectioned Markdown body, changed-section summary and marker, section jump, scroll gauge, the approve `y/N` question, approve/refine/browser/keep). |
|
|
692
878
|
| `plan-index.ts` | `/plan list`: the notice text and the docked index screen (title, step progress, age, focused path; Enter reviews). |
|
|
693
879
|
| `blocks.ts` | Source-line block model (fence-aware headings, paragraphs, list items, code, quotes, tables, rules, blanks). |
|
|
694
|
-
| `feedback.ts` |
|
|
695
|
-
| `web.ts` | `createPlanWebReview`: the review session (
|
|
696
|
-
| `web-
|
|
697
|
-
| `web-
|
|
698
|
-
| `web-
|
|
880
|
+
| `feedback.ts` | Comment codec: authored markers, `stripFeedback`, `parseFeedback`, `formatFeedback` (writer-scoped), `displayFeedback`, `carryFeedback`, `resolveAnchor`, `feedbackSummary`, and payload validation. |
|
|
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 the shared bind, writer, and close. |
|
|
882
|
+
| `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
|
+
| `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
|
+
| `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
|
+
| `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
|
+
| `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's lifecycle: 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
|
+
| `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
|
+
| `web-comments.ts` | The client's comment half: selection quotes, the composer, authored cards and who may edit them, delete-with-undo. |
|
|
890
|
+
| `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. |
|
|
699
891
|
| `web-markdown.ts` | The client's document half: `safeHref` and the inline/table/code/list rendering (interpolated into `web-client.ts`). |
|
|
700
|
-
| `web-chrome.ts` | The client's chrome half: the state chip, the status line, the header, reading progress
|
|
892
|
+
| `web-chrome.ts` | The client's chrome half: the state chip, the status line, the header, and reading progress (interpolated into `web-client.ts`). |
|
|
701
893
|
| `path-guard.ts` | Recursive path-argument detection for the read-only backstop. |
|
|
702
894
|
| [`../../lib/browser.ts`](../../lib/browser.ts) | Cross-platform default-browser opener (shared with `file-browse`). |
|
|
703
895
|
| [`../../lib/plan-entry.ts`](../../lib/plan-entry.ts) | Plan-mode entry shared with `goal`: the read-only summary, the consent sentence, and the `pi.events` request `plan` answers by enabling the mode. |
|
|
704
896
|
| [`../../lib/shell.ts`](../../lib/shell.ts) | The shell-escape rule the read-only git guard shares with the worktree isolation guard. |
|
|
705
897
|
| [`../../lib/highlight.ts`](../../lib/highlight.ts) | First-party escape-first syntax tokenizer (shared with `file-browse`). |
|
|
898
|
+
| [`../../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. |
|
|
706
900
|
| [`../../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. |
|
|
707
901
|
| [`../../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. |
|
|
708
902
|
| `../../lib/policy.ts` | Shared read-only capability policy (default-deny + `readOnlyHint`). |
|