@gavin-hu/my-pi-agent 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -30
- package/README.zh-CN.md +40 -25
- package/assets/screens/board.png +0 -0
- package/assets/screens/chrome.png +0 -0
- package/assets/screens/file-browse.png +0 -0
- package/assets/screens/plan-review.png +0 -0
- package/assets/screens/plan.png +0 -0
- package/docs/extensions/README.md +22 -23
- package/docs/extensions/board.md +397 -168
- package/docs/extensions/bytefly.md +234 -197
- package/docs/extensions/check.md +15 -17
- package/docs/extensions/context.md +35 -15
- package/docs/extensions/file-browse.md +57 -63
- package/docs/extensions/file-open.md +9 -11
- package/docs/extensions/file-read.md +12 -14
- package/docs/extensions/goal.md +15 -17
- package/docs/extensions/job.md +54 -42
- package/docs/extensions/memory.md +17 -19
- package/docs/extensions/plan.md +197 -140
- package/docs/extensions/question.md +16 -18
- package/docs/extensions/rewind.md +17 -19
- package/docs/extensions/serve.md +145 -0
- package/docs/extensions/session-bus.md +38 -16
- package/docs/extensions/subagent.md +23 -25
- package/docs/extensions/team.md +83 -44
- package/docs/extensions/todo.md +18 -18
- package/docs/extensions/watch.md +31 -31
- package/docs/extensions/web-access.md +2 -4
- package/docs/extensions/wechat.md +34 -35
- package/docs/extensions/worktree.md +56 -50
- package/extensions/board/commands.ts +79 -25
- package/extensions/board/format.ts +43 -24
- package/extensions/board/index.ts +17 -4
- package/extensions/board/input.ts +4 -9
- package/extensions/board/join.ts +5 -20
- package/extensions/board/lazy-page.ts +40 -0
- package/extensions/board/page.ts +65 -93
- package/extensions/board/result.ts +30 -39
- package/extensions/board/rows.ts +62 -25
- package/extensions/board/schema.ts +49 -11
- package/extensions/board/snapshot.ts +8 -6
- package/extensions/board/tone.ts +22 -0
- package/extensions/board/tools.ts +154 -55
- package/extensions/board/transport/router.ts +3 -5
- package/extensions/board/transport/server.ts +59 -28
- package/extensions/board/types.ts +36 -42
- package/extensions/board/view/card.ts +229 -0
- package/extensions/board/view/client.ts +35 -16
- package/extensions/board/view/dialog.ts +169 -6
- package/extensions/board/view/empty.ts +47 -0
- package/extensions/board/view/keys.ts +13 -2
- package/extensions/board/view/live.ts +90 -28
- package/extensions/board/view/names.ts +13 -4
- package/extensions/board/view/page.ts +38 -13
- package/extensions/board/view/render.ts +219 -277
- package/extensions/board/view/styles.ts +130 -32
- package/extensions/board/view/team.ts +297 -76
- package/extensions/bytefly/block/fallback.ts +1 -1
- package/extensions/bytefly/doctor/runtime.ts +5 -2
- package/extensions/bytefly/footer/collect/label.ts +60 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
- package/extensions/bytefly/footer/config.ts +7 -8
- package/extensions/bytefly/footer/register.ts +33 -4
- package/extensions/bytefly/footer/render/footer.ts +18 -4
- package/extensions/bytefly/footer/render/key.ts +46 -0
- package/extensions/bytefly/footer/render/layout.ts +13 -3
- package/extensions/bytefly/footer/render/lines.ts +10 -7
- package/extensions/bytefly/footer/render/segments.ts +41 -38
- package/extensions/bytefly/footer/render/status.ts +0 -11
- package/extensions/bytefly/footer/types.ts +7 -0
- package/extensions/bytefly/header/component.ts +4 -20
- package/extensions/bytefly/header/config.ts +1 -1
- package/extensions/bytefly/index.ts +32 -6
- package/extensions/bytefly/mode/editor.ts +115 -0
- package/extensions/bytefly/panel/commands.ts +210 -120
- package/extensions/bytefly/panel/format.ts +4 -4
- package/extensions/bytefly/panel/settings.ts +73 -6
- package/extensions/bytefly/panel/tui/keys.ts +2 -6
- package/extensions/bytefly/update/cache.ts +1 -1
- package/extensions/bytefly/update/register.ts +4 -4
- package/extensions/bytefly/working/config.ts +5 -7
- package/extensions/bytefly/working/format.ts +10 -13
- package/extensions/bytefly/working/register.ts +22 -27
- package/extensions/check/commands.ts +3 -2
- package/extensions/check/render.ts +10 -1
- package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
- package/extensions/context/commands.ts +2 -2
- package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
- package/extensions/file-browse/config/config.ts +4 -19
- package/extensions/file-browse/index.ts +89 -79
- package/extensions/file-browse/live/live.ts +1 -1
- package/extensions/file-browse/transport/router.ts +12 -4
- package/extensions/file-browse/transport/server.ts +61 -28
- package/extensions/file-browse/view/chrome.ts +4 -4
- package/extensions/file-browse/view/client.ts +3 -1
- package/extensions/file-browse/view/html.ts +73 -40
- package/extensions/file-browse/view/live-client.ts +2 -2
- package/extensions/file-browse/view/markdown.ts +13 -14
- package/extensions/file-browse/view/palette.ts +2 -2
- package/extensions/file-browse/view/raw-html.ts +9 -9
- package/extensions/file-browse/view/tree.ts +1 -1
- package/extensions/file-browse/view/urls.ts +20 -6
- package/extensions/file-read/render.ts +4 -4
- package/extensions/goal/plan-route.ts +2 -2
- package/extensions/goal/tui.ts +12 -2
- package/extensions/job/child.ts +527 -0
- package/extensions/job/clock.ts +142 -0
- package/extensions/job/commands.ts +4 -2
- package/extensions/job/config.ts +8 -1
- package/extensions/job/format.ts +22 -1
- package/extensions/job/log-writer.ts +13 -2
- package/extensions/job/registry.ts +63 -7
- package/extensions/job/render.ts +34 -0
- package/extensions/job/runtime.ts +296 -655
- package/extensions/job/session.ts +132 -19
- package/extensions/job/store.ts +7 -7
- package/extensions/job/tools.ts +21 -9
- package/extensions/job/types.ts +47 -0
- package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -99
- package/extensions/job/waiters.ts +44 -1
- package/extensions/memory/commands.ts +4 -2
- package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
- package/extensions/plan/commands.ts +12 -9
- package/extensions/plan/feedback.ts +62 -16
- package/extensions/plan/format.ts +8 -2
- package/extensions/plan/git-bash.ts +115 -16
- package/extensions/plan/index.ts +58 -51
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plan-text.ts +114 -0
- package/extensions/plan/plans.ts +5 -54
- package/extensions/plan/policy.ts +8 -10
- package/extensions/plan/review-lazy.ts +85 -0
- package/extensions/plan/review.ts +2 -2
- package/extensions/plan/runtime.ts +61 -41
- package/extensions/plan/sections.ts +1 -1
- package/extensions/plan/steps.ts +3 -3
- package/extensions/plan/todos.ts +1 -1
- package/extensions/plan/tools/cancel.ts +27 -62
- package/extensions/plan/tools/enter.ts +55 -81
- package/extensions/plan/tools/exit.ts +221 -238
- package/extensions/plan/tools/refusal.ts +18 -0
- package/extensions/plan/tools/steps.ts +75 -92
- package/extensions/plan/tools/write.ts +77 -120
- package/extensions/plan/tools.ts +188 -30
- package/extensions/plan/tui.ts +5 -5
- package/extensions/plan/types.ts +48 -5
- package/extensions/plan/web-blocks.ts +76 -5
- package/extensions/plan/web-chrome.ts +4 -0
- package/extensions/plan/web-client.ts +102 -2
- package/extensions/plan/web-collab.ts +4 -4
- package/extensions/plan/web-comments.ts +39 -8
- package/extensions/plan/web-composer.ts +14 -3
- package/extensions/plan/web-keys.ts +149 -25
- package/extensions/plan/web-lazy.ts +53 -0
- package/extensions/plan/web-markdown.ts +10 -68
- package/extensions/plan/web-outline.ts +22 -2
- package/extensions/plan/web-page.ts +56 -86
- package/extensions/plan/web-payload.ts +188 -0
- package/extensions/plan/web-plan.ts +23 -13
- package/extensions/plan/web-router.ts +18 -9
- package/extensions/plan/web-settle.ts +14 -7
- package/extensions/plan/web-share-session.ts +24 -55
- package/extensions/plan/web-share.ts +19 -37
- package/extensions/plan/web-write.ts +112 -0
- package/extensions/plan/web.ts +67 -131
- package/extensions/question/tools.ts +6 -2
- package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
- package/extensions/rewind/commands/commands.ts +6 -3
- package/extensions/rewind/state/runtime.ts +1 -1
- package/extensions/rewind/{view/status.ts → status.ts} +4 -4
- package/extensions/rewind/view/review-tui.ts +4 -4
- package/extensions/serve/config.ts +39 -0
- package/extensions/serve/index.ts +85 -0
- package/extensions/serve/registry.ts +260 -0
- package/extensions/session-bus/commands.ts +11 -6
- package/extensions/session-bus/join-request.ts +17 -9
- package/extensions/session-bus/mail-watch.ts +26 -17
- package/extensions/session-bus/protocol.ts +15 -3
- package/extensions/session-bus/resolve.ts +5 -7
- package/extensions/session-bus/result.ts +1 -27
- package/extensions/session-bus/runtime.ts +40 -12
- package/extensions/session-bus/store.ts +101 -19
- package/extensions/session-bus/tools.ts +5 -4
- package/extensions/session-bus/types.ts +19 -0
- package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
- package/extensions/subagent/agents.ts +1 -1
- package/extensions/subagent/commands.ts +4 -2
- package/extensions/subagent/format.ts +15 -15
- package/extensions/subagent/index.ts +1 -1
- package/extensions/subagent/render.ts +3 -3
- package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
- package/extensions/team/commands.ts +24 -5
- package/extensions/team/dispatch.ts +18 -4
- package/extensions/team/format.ts +17 -2
- package/extensions/team/index.ts +9 -8
- package/extensions/team/schema.ts +6 -5
- package/extensions/team/tools.ts +107 -45
- package/extensions/team/types.ts +3 -2
- package/extensions/todo/commands.ts +4 -2
- package/extensions/todo/format.ts +11 -0
- package/extensions/todo/render.ts +57 -0
- package/extensions/todo/runtime.ts +1 -1
- package/extensions/todo/tools.ts +1 -1
- package/extensions/todo/{tui.ts → view/todo-screen.ts} +18 -90
- package/extensions/todo/widget.ts +47 -0
- package/extensions/watch/commands.ts +4 -2
- package/extensions/watch/format.ts +36 -7
- package/extensions/watch/render.ts +57 -0
- package/extensions/watch/runtime.ts +27 -22
- package/extensions/watch/tools.ts +4 -4
- package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
- package/extensions/web-access/fetch/tool.ts +12 -4
- package/extensions/wechat/commands/commands.ts +18 -14
- package/extensions/wechat/index.ts +72 -37
- package/extensions/wechat/state/credentials.ts +1 -1
- package/extensions/wechat/state/state.ts +1 -1
- package/extensions/wechat/text/status.ts +112 -0
- package/extensions/wechat/types.ts +2 -2
- package/extensions/wechat/view/status-screen.ts +4 -103
- package/extensions/worktree/commands.ts +11 -9
- package/extensions/worktree/enter.ts +2 -2
- package/extensions/worktree/exit.ts +2 -2
- package/extensions/worktree/index.ts +5 -5
- package/extensions/worktree/screen.ts +3 -3
- package/extensions/worktree/status.ts +2 -2
- package/extensions/worktree/tools.ts +113 -116
- package/lib/README.md +28 -13
- package/lib/board/actions.ts +111 -0
- package/lib/board/artifact.ts +149 -0
- package/lib/board/card.ts +163 -52
- package/lib/board/config.ts +58 -0
- package/lib/board/contract.ts +113 -0
- package/lib/board/join.ts +38 -0
- package/lib/board/lanes.ts +182 -0
- package/lib/board/lead.ts +130 -0
- package/lib/board/limits.ts +78 -0
- package/lib/board/links.ts +31 -10
- package/lib/board/page-pointer.ts +30 -5
- package/lib/board/read.ts +83 -0
- package/lib/board/reason.ts +102 -0
- package/lib/board/round-text.ts +68 -6
- package/lib/board/round.ts +31 -15
- package/lib/board/rules.ts +151 -307
- package/lib/board/seat.ts +35 -0
- package/lib/board/session.ts +199 -51
- package/lib/board/skipped.ts +22 -0
- package/lib/board/steps.ts +487 -0
- package/lib/board/store.ts +119 -36
- package/lib/board/tone.ts +92 -0
- package/lib/board/view.ts +81 -63
- package/lib/bus-request.ts +83 -0
- package/{extensions/watch → lib}/child.ts +40 -21
- package/lib/env.ts +65 -7
- package/lib/format.ts +13 -0
- package/lib/http/index.ts +17 -11
- package/lib/http/mount.ts +142 -0
- package/lib/http/port.ts +30 -0
- package/lib/http/serve.ts +170 -46
- package/lib/http/server.ts +3 -3
- package/lib/lazy.ts +45 -0
- package/lib/plan-entry.ts +46 -21
- package/lib/plans/paths.ts +82 -0
- package/lib/policy.ts +9 -6
- package/lib/report.ts +3 -2
- package/lib/requirements.ts +10 -10
- package/lib/session-label.ts +129 -7
- package/lib/session-roster.ts +43 -1
- package/lib/session-tool.ts +39 -22
- package/lib/stray-fields.ts +7 -2
- package/lib/tool-names.ts +5 -10
- package/lib/tui/README.md +29 -11
- package/lib/tui/chrome.ts +16 -15
- package/lib/tui/cursor.ts +18 -3
- package/lib/tui/glyphs.ts +11 -11
- package/lib/tui/render-cache.ts +29 -0
- package/lib/tui/tokens.ts +33 -2
- package/lib/tui/tool.ts +33 -4
- package/lib/web/README.md +21 -0
- package/lib/web/client-runtime.ts +27 -1
- package/lib/web/components.ts +5 -1
- package/lib/web/index.ts +1 -0
- package/lib/web/markdown.ts +83 -0
- package/lib/web/markup.ts +20 -2
- package/lib/web/runtime.ts +9 -10
- package/package.json +8 -2
- package/skills/init/SKILL.md +5 -5
- package/skills/next/SKILL.md +3 -5
- package/extensions/board/config.ts +0 -36
- package/extensions/board/seat.ts +0 -28
- package/lib/tui/index.ts +0 -34
- package/skills/project-docs/SKILL.md +0 -133
- package/skills/project-docs/references/audit.md +0 -131
- package/skills/project-docs/references/changelog.md +0 -105
- package/skills/project-docs/references/discovery.md +0 -127
- package/skills/project-docs/references/readme.md +0 -80
- package/skills/project-docs/references/release.md +0 -84
- package/skills/project-docs/references/roadmap.md +0 -93
- package/skills/project-docs/scripts/state.ts +0 -316
package/extensions/plan/tools.ts
CHANGED
|
@@ -1,43 +1,201 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Model-facing registration for the plan
|
|
2
|
+
* Model-facing registration for the `plan` tool.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* returns its path
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* and on approval seeds the `todo` tool with the plan's steps (through
|
|
10
|
-
* `ctx.executeTool`, so the todo widget and validation run normally).
|
|
11
|
-
* `set_plan_steps` then keeps the plan current: after approval the model ticks
|
|
12
|
-
* each step off in place as it finishes it, and the todo item approval seeded
|
|
13
|
-
* from that step is ticked with it. The file is the source of truth: the model
|
|
14
|
-
* executes from it.
|
|
4
|
+
* One tool with five actions, like `board` and `memory`: `enter` asks the user to
|
|
5
|
+
* plan before touching code, `write` saves the plan artifact in the plans
|
|
6
|
+
* directory and returns its path, `exit` reads that file back and presents it for
|
|
7
|
+
* approval, `steps` keeps an approved plan current, and `cancel` leaves planning
|
|
8
|
+
* without approving, so a phone user is never trapped in read-only mode.
|
|
15
9
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
10
|
+
* `exit` presents the plan through `review.ts` (a scrollable review screen in the
|
|
11
|
+
* TUI, a select dialog elsewhere) and, on approval, seeds the `todo` tool with the
|
|
12
|
+
* plan's steps through `ctx.executeTool`, so the todo widget and validation run
|
|
13
|
+
* normally. After approval the model ticks each step off in place with `steps`, and
|
|
14
|
+
* the todo item approval seeded from that step is ticked with it. The file is the
|
|
15
|
+
* source of truth: the model executes from it.
|
|
19
16
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Each action lives in `tools/`: its execute handler and the block it draws stay
|
|
18
|
+
* together, and the approval-time helpers they share live next to them. This
|
|
19
|
+
* module only composes them, in the order the description names them.
|
|
20
|
+
*
|
|
21
|
+
* Without an interactive UI the two actions that need one (`enter`, `exit`) fail
|
|
22
|
+
* with an actionable message instead of deciding for the user.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
25
|
-
import
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
26
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
27
|
+
import { Type } from "typebox";
|
|
28
|
+
import { reuseText, toolDetails } from "../../lib/tui/chrome.ts";
|
|
29
|
+
import { paint } from "../../lib/tui/tokens.ts";
|
|
30
|
+
import { detailsOrLine, messageText, callLine } from "../../lib/tui/tool.ts";
|
|
31
|
+
import { MAX_STEPS, PLAN_OUTLINE } from "./steps.ts";
|
|
32
|
+
import { PLAN_ACTIONS, PLAN_TOOL, type PlanAction, type PlanRuntime } from "./runtime.ts";
|
|
33
|
+
import { callCancel, executeCancel, textCancel } from "./tools/cancel.ts";
|
|
34
|
+
import { callEnter, executeEnter, textEnter } from "./tools/enter.ts";
|
|
35
|
+
import { callExit, executeExit, textExit } from "./tools/exit.ts";
|
|
36
|
+
import { callSteps, executeSteps, StepUpdateParams, textSteps } from "./tools/steps.ts";
|
|
37
|
+
import { callWrite, executeWrite, textWrite } from "./tools/write.ts";
|
|
38
|
+
import type { PlanDetails } from "./types.ts";
|
|
39
|
+
import { createLazyPlanWebReview, NO_SERVER_DEPS } from "./web-lazy.ts";
|
|
40
|
+
import type { PlanWebReview } from "./web.ts";
|
|
41
|
+
|
|
42
|
+
const PlanParams = Type.Object({
|
|
43
|
+
action: StringEnum(PLAN_ACTIONS, {
|
|
44
|
+
description:
|
|
45
|
+
"`enter` asks to plan, `write` saves the plan file, `exit` presents it for approval, `steps` ticks its steps, `cancel` leaves without approving.",
|
|
46
|
+
}),
|
|
47
|
+
title: Type.Optional(
|
|
48
|
+
Type.String({
|
|
49
|
+
description: '`write`: short title for the plan, used as the file-name slug, e.g. "Add rate limiting".',
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
content: Type.Optional(
|
|
53
|
+
Type.String({
|
|
54
|
+
description: "`write`: the complete plan as markdown in the documented outline, including every step.",
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
plan_path: Type.Optional(
|
|
58
|
+
Type.String({
|
|
59
|
+
description:
|
|
60
|
+
"`write`: overwrite an existing plan file (one an earlier `write` returned) to refine it instead of creating a new one. " +
|
|
61
|
+
"`exit`: the file `write` returned, which the user reviews. " +
|
|
62
|
+
"`steps`: the approved plan file, as `exit` returned it.",
|
|
63
|
+
}),
|
|
64
|
+
),
|
|
65
|
+
updates: Type.Optional(
|
|
66
|
+
Type.Array(StepUpdateParams, {
|
|
67
|
+
minItems: 1,
|
|
68
|
+
description:
|
|
69
|
+
"`steps`: the steps to set; each entry sets one step's checkbox, and the last entry wins for a duplicate step.",
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const DESCRIPTION =
|
|
75
|
+
"Plan mode and the plan file it is built around, in one tool. `enter` asks the user to enter read-only plan mode; " +
|
|
76
|
+
"`write` saves the plan you designed as markdown in the plans directory; `exit` presents the saved plan for approval; " +
|
|
77
|
+
"`steps` ticks its steps off in place; `cancel` leaves plan mode without approving. Each action refuses outside its " +
|
|
78
|
+
"mode, and every field but `action` says which action reads it.\n\n" +
|
|
79
|
+
"`write` takes the complete plan, not a summary, in this outline so it is consistent and scannable:\n\n" +
|
|
80
|
+
`${PLAN_OUTLINE}\n\n` +
|
|
81
|
+
"Each section has one job: lead it with its outcome, keep a prose section to a few bullets or two or three " +
|
|
82
|
+
"sentences, give each step one verifiable outcome with the detail in a nested note, and put the sketch, files, " +
|
|
83
|
+
"and commands in inline code or a fence rather than pasted prose. " +
|
|
84
|
+
"When the change alters a surface that already exists, Visual shows it before and after in one fence each; " +
|
|
85
|
+
"tag a fence only when every line in it is code. " +
|
|
86
|
+
"The outline is a guide, not a form: drop Context, Approach, Visual, or Risks when there is nothing to say. " +
|
|
87
|
+
"Keep Steps and Verification — the steps are read from that section (without it, the shallowest list " +
|
|
88
|
+
"anywhere becomes the steps), and Verification becomes the last todo item. " +
|
|
89
|
+
"In Steps, each top-level bullet is one outcome in the order you will do them; " +
|
|
90
|
+
"an indented line continues that outcome, and a deeper bullet is a note. " +
|
|
91
|
+
`A plan with more than ${MAX_STEPS} top-level steps in its Steps section is refused, so approval never drops steps it ` +
|
|
92
|
+
"cannot track. `write` returns the path to pass to `exit`; plan_path overwrites an existing file when refining.\n\n" +
|
|
93
|
+
"`steps` sets the checkboxes of the given top-level steps of an approved plan in one write, and their todo items " +
|
|
94
|
+
"with them. Each step number matches the order the plan was approved in; a step can be marked completed or reopened. " +
|
|
95
|
+
"Only the checkboxes change; the step text is left as written. The file is rewritten once, and nothing changes when " +
|
|
96
|
+
"any step is out of range or an entry's expected text no longer matches the file.\n\n" +
|
|
97
|
+
"`exit` reads the plan file back and presents it with approve, keep, refine, and exit choices; on approval plan mode " +
|
|
98
|
+
"ends, write access returns, and the plan's steps go into the todo list. The plan file stays the source of truth. " +
|
|
99
|
+
"`cancel` is the escape hatch that needs no plan file: write access returns and nothing is recorded.";
|
|
100
|
+
|
|
101
|
+
/** The verb a call names, or undefined for a transcript recorded before the fold. */
|
|
102
|
+
function planActionOf(raw: unknown): PlanAction | undefined {
|
|
103
|
+
return typeof raw === "string" ? PLAN_ACTIONS.find((action) => action === raw) : undefined;
|
|
104
|
+
}
|
|
32
105
|
|
|
33
106
|
export function registerTools(
|
|
34
107
|
pi: ExtensionAPI,
|
|
35
108
|
runtime: PlanRuntime,
|
|
36
|
-
web: PlanWebReview =
|
|
109
|
+
web: PlanWebReview = createLazyPlanWebReview(runtime.plans, NO_SERVER_DEPS),
|
|
37
110
|
): void {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
111
|
+
pi.registerTool<typeof PlanParams, PlanDetails>({
|
|
112
|
+
name: PLAN_TOOL,
|
|
113
|
+
label: "Plan",
|
|
114
|
+
description: DESCRIPTION,
|
|
115
|
+
promptSnippet: "Enter read-only plan mode, save the plan, present it for approval, tick its steps, or leave it.",
|
|
116
|
+
promptGuidelines: [
|
|
117
|
+
"Call plan with action enter before a non-trivial implementation task; skip it for small, obvious changes.",
|
|
118
|
+
"While plan mode is active, write the full plan with action write, then call action exit with the returned plan_path.",
|
|
119
|
+
"When refining with plan_path, keep every section heading as it was: the review marks changed sections by heading, " +
|
|
120
|
+
"so a rename reads as an edit.",
|
|
121
|
+
"After a plan is approved, call action steps as you complete steps, passing each step's text as expected so a " +
|
|
122
|
+
"reordered plan is refused instead of mis-ticked.",
|
|
123
|
+
"Call action cancel when the user asks to leave or abandon planning without approving a plan.",
|
|
124
|
+
],
|
|
125
|
+
parameters: PlanParams,
|
|
126
|
+
executionMode: "sequential",
|
|
127
|
+
// One annotation set for five verbs, so it takes the strictest of them: `write`
|
|
128
|
+
// overwrites a plan file with plan_path. The actions that only change the mode
|
|
129
|
+
// are exempted by the guard in `index.ts`, which is the only place that can
|
|
130
|
+
// still tell them apart.
|
|
131
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
132
|
+
|
|
133
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
134
|
+
switch (params.action) {
|
|
135
|
+
case "enter":
|
|
136
|
+
return await executeEnter(runtime, ctx);
|
|
137
|
+
case "write":
|
|
138
|
+
return await executeWrite(runtime, params, ctx);
|
|
139
|
+
case "exit":
|
|
140
|
+
return await executeExit(pi, runtime, web, params, ctx, signal);
|
|
141
|
+
case "steps":
|
|
142
|
+
return await executeSteps(runtime, params, ctx);
|
|
143
|
+
case "cancel":
|
|
144
|
+
return await executeCancel(runtime, ctx);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
renderCall(args, theme, context) {
|
|
149
|
+
const last = context?.lastComponent;
|
|
150
|
+
switch (planActionOf(args.action)) {
|
|
151
|
+
case "enter":
|
|
152
|
+
return callEnter(theme, last);
|
|
153
|
+
case "write":
|
|
154
|
+
return callWrite(args, theme, last);
|
|
155
|
+
case "exit":
|
|
156
|
+
return callExit(args, theme, last);
|
|
157
|
+
case "steps":
|
|
158
|
+
return callSteps(args, theme, last);
|
|
159
|
+
case "cancel":
|
|
160
|
+
return callCancel(theme, last);
|
|
161
|
+
case undefined:
|
|
162
|
+
return callLine(theme, last, PLAN_TOOL, "");
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
renderResult(result, options, theme, context) {
|
|
167
|
+
const text = reuseText(context?.lastComponent);
|
|
168
|
+
const details = toolDetails<PlanDetails>(result.details);
|
|
169
|
+
const outcome = detailsOrLine(theme, result, options.expanded, details);
|
|
170
|
+
if ("line" in outcome) {
|
|
171
|
+
text.setText(outcome.line);
|
|
172
|
+
return text;
|
|
173
|
+
}
|
|
174
|
+
const found = outcome.details;
|
|
175
|
+
// No `action` means a refusal (which reports only its verb and reason), or a
|
|
176
|
+
// result recorded before the tool was folded; its own text is what is left.
|
|
177
|
+
if (!("action" in found)) {
|
|
178
|
+
text.setText(paint(theme, "toolOutput", messageText(result)));
|
|
179
|
+
return text;
|
|
180
|
+
}
|
|
181
|
+
switch (found.action) {
|
|
182
|
+
case "enter":
|
|
183
|
+
text.setText(textEnter(found, theme));
|
|
184
|
+
break;
|
|
185
|
+
case "write":
|
|
186
|
+
text.setText(textWrite(found, theme));
|
|
187
|
+
break;
|
|
188
|
+
case "exit":
|
|
189
|
+
text.setText(textExit(found, options.expanded, theme));
|
|
190
|
+
break;
|
|
191
|
+
case "steps":
|
|
192
|
+
text.setText(textSteps(found, theme));
|
|
193
|
+
break;
|
|
194
|
+
case "cancel":
|
|
195
|
+
text.setText(textCancel(found, theme));
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
return text;
|
|
199
|
+
},
|
|
200
|
+
});
|
|
43
201
|
}
|
package/extensions/plan/tui.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Terminal rendering for the plan review screen.
|
|
3
3
|
*
|
|
4
|
-
* `PlanViewComponent` is the scrollable review screen `
|
|
4
|
+
* `PlanViewComponent` is the scrollable review screen the `exit` action opens
|
|
5
5
|
* for approval. The plan body is rendered by Pi's own `Markdown` component with
|
|
6
6
|
* the active Markdown theme, so headings, lists, code, links, and quotes match
|
|
7
7
|
* the rest of the application; plan-authored control characters are stripped
|
|
@@ -100,10 +100,10 @@ const GAUGE_SEGMENTS = 10;
|
|
|
100
100
|
/** Footer hints shared by the review and the inline editor's primary actions. */
|
|
101
101
|
const PRIMARY_HINTS: ScreenHint[] = [
|
|
102
102
|
"Esc keep plan",
|
|
103
|
-
{ text: "a approve", color: "
|
|
103
|
+
{ text: "a approve", color: "ok" },
|
|
104
104
|
{ text: "r refine", color: "accent" },
|
|
105
105
|
{ text: "w browser", color: "accent" },
|
|
106
|
-
{ text: "x exit plan", color: "
|
|
106
|
+
{ text: "x exit plan", color: "warn" },
|
|
107
107
|
];
|
|
108
108
|
/** Scroll keys appended to the review footer while the plan overflows. */
|
|
109
109
|
const SCROLL_HINTS: ScreenHint[] = ["j/k scroll", "space page", "g/G ends"];
|
|
@@ -168,7 +168,7 @@ function scrollRow(
|
|
|
168
168
|
return truncateToWidth(` ${head}`, width);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
/** Scrollable plan review screen opened by `
|
|
171
|
+
/** Scrollable plan review screen opened by the `exit` action. */
|
|
172
172
|
export class PlanViewComponent implements Component {
|
|
173
173
|
private offset = 0;
|
|
174
174
|
private visible = DEFAULT_ROWS;
|
|
@@ -407,7 +407,7 @@ export class PlanViewComponent implements Component {
|
|
|
407
407
|
lines.push(truncateToWidth(` ${paint(theme, "meta", "Refine the plan:")}`, w));
|
|
408
408
|
for (const line of editorLines) lines.push(truncateToWidth(` ${line}`, w));
|
|
409
409
|
lines.push("");
|
|
410
|
-
lines.push(screenHint(theme, w, ["Esc back to plan", { text: "Enter submit", color: "
|
|
410
|
+
lines.push(screenHint(theme, w, ["Esc back to plan", { text: "Enter submit", color: "ok" }]));
|
|
411
411
|
} else {
|
|
412
412
|
if (this.maxOffset > 0) lines.push(scrollRow(theme, w, this.offset, end, body.length, sectionLabel));
|
|
413
413
|
else lines.push("");
|
package/extensions/plan/types.ts
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import type { PlanAnnotation } from "./feedback.ts";
|
|
6
6
|
import type { SectionDigest } from "./plan-text.ts";
|
|
7
|
+
import type { PlanAction } from "./runtime.ts";
|
|
8
|
+
|
|
9
|
+
export type { PlanAction };
|
|
7
10
|
import type { StepUpdate } from "./steps.ts";
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -65,8 +68,10 @@ export interface SeededStepsEntry {
|
|
|
65
68
|
steps: string[];
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
/** Structured result carried in `
|
|
71
|
+
/** Structured result carried in the `plan` tool's `details` for `action: "enter"`. */
|
|
69
72
|
export interface EnterPlanModeDetails {
|
|
73
|
+
/** The verb this result answers, so a renderer reads the result instead of the call. */
|
|
74
|
+
action: "enter";
|
|
70
75
|
/** Whether the user agreed to enter plan mode. */
|
|
71
76
|
entered: boolean;
|
|
72
77
|
/** True when plan mode was already enabled before this call. */
|
|
@@ -75,8 +80,10 @@ export interface EnterPlanModeDetails {
|
|
|
75
80
|
unavailable?: boolean;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
/** Structured result carried in `
|
|
83
|
+
/** Structured result carried in the `plan` tool's `details` for `action: "write"`. */
|
|
79
84
|
export interface WritePlanDetails {
|
|
85
|
+
/** The verb this result answers. */
|
|
86
|
+
action: "write";
|
|
80
87
|
/** Absolute path of the written plan file. */
|
|
81
88
|
path: string;
|
|
82
89
|
/** How the path is shown: `~`-shortened, since plans live outside the project. */
|
|
@@ -85,8 +92,10 @@ export interface WritePlanDetails {
|
|
|
85
92
|
bytes: number;
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
/** Structured result carried in `
|
|
95
|
+
/** Structured result carried in the `plan` tool's `details` for `action: "steps"`. */
|
|
89
96
|
export interface SetPlanStepsDetails {
|
|
97
|
+
/** The verb this result answers. */
|
|
98
|
+
action: "steps";
|
|
90
99
|
/** Absolute path of the plan file that was updated. */
|
|
91
100
|
path: string;
|
|
92
101
|
/** How the path is shown: `~`-shortened. */
|
|
@@ -99,8 +108,10 @@ export interface SetPlanStepsDetails {
|
|
|
99
108
|
total: number;
|
|
100
109
|
}
|
|
101
110
|
|
|
102
|
-
/** Structured result carried in `
|
|
111
|
+
/** Structured result carried in the `plan` tool's `details` for `action: "exit"`. */
|
|
103
112
|
export interface ExitPlanModeDetails {
|
|
113
|
+
/** The verb this result answers. */
|
|
114
|
+
action: "exit";
|
|
104
115
|
/** Whether the user approved the plan. */
|
|
105
116
|
approved: boolean;
|
|
106
117
|
/** The plan the model submitted, read from its file. */
|
|
@@ -121,8 +132,40 @@ export interface ExitPlanModeDetails {
|
|
|
121
132
|
unavailable?: boolean;
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
/** Structured result carried in `
|
|
135
|
+
/** Structured result carried in the `plan` tool's `details` for `action: "cancel"`. */
|
|
125
136
|
export interface CancelPlanModeDetails {
|
|
137
|
+
/** The verb this result answers. */
|
|
138
|
+
action: "cancel";
|
|
126
139
|
/** True when plan mode was on and has now been turned off. */
|
|
127
140
|
cancelled: boolean;
|
|
128
141
|
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* A refused call: the reason, and the verb it named. It carries no `action`
|
|
145
|
+
* field on purpose — a refusal has none of that verb's state to report, and the
|
|
146
|
+
* absent discriminant is what tells a renderer it is looking at a rejection.
|
|
147
|
+
*/
|
|
148
|
+
export interface RefusedPlanDetails {
|
|
149
|
+
/** The verb the call named. */
|
|
150
|
+
refused: PlanAction;
|
|
151
|
+
/** Why the call was refused, as the model reads it. */
|
|
152
|
+
error: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* What the `plan` tool's result carries: one of the five verbs, told apart by
|
|
157
|
+
* `action`, or a refusal that carried no state at all.
|
|
158
|
+
*
|
|
159
|
+
* The details name their own verb so a renderer draws the result it was given
|
|
160
|
+
* rather than re-reading the call's arguments, which a resumed transcript may
|
|
161
|
+
* no longer spell the same way. A details block recorded before the fold has no
|
|
162
|
+
* `action` at all, and a refusal never reaches the per-verb renderers; both take
|
|
163
|
+
* the same fallback.
|
|
164
|
+
*/
|
|
165
|
+
export type PlanDetails =
|
|
166
|
+
| EnterPlanModeDetails
|
|
167
|
+
| WritePlanDetails
|
|
168
|
+
| ExitPlanModeDetails
|
|
169
|
+
| SetPlanStepsDetails
|
|
170
|
+
| CancelPlanModeDetails
|
|
171
|
+
| RefusedPlanDetails;
|
|
@@ -59,13 +59,20 @@ export function setActive(planEl: ClientNode | null, index: number): number | nu
|
|
|
59
59
|
* The index one step from `activeIndex`, or `null` when there is nothing to walk.
|
|
60
60
|
*
|
|
61
61
|
* With nothing active yet, `j` starts at the top and `k` at the bottom, which is what a
|
|
62
|
-
* reader pressing either key on a fresh page expects.
|
|
62
|
+
* reader pressing either key on a fresh page expects. A view can hide blocks, and a
|
|
63
|
+
* hidden block is not a place the reader can be sent: the step lands on the next block
|
|
64
|
+
* they can actually see, or nowhere when the filter left none.
|
|
63
65
|
*/
|
|
64
66
|
export function moveActive(planEl: ClientNode | null, activeIndex: number, delta: number): number | null {
|
|
65
67
|
const blocks = blockElements(planEl);
|
|
66
68
|
if (blocks.length === 0) return null;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
+
const step = delta < 0 ? -1 : 1;
|
|
70
|
+
const first =
|
|
71
|
+
activeIndex < 0 ? (step < 0 ? blocks.length - 1 : 0) : Math.min(Math.max(activeIndex + step, 0), blocks.length - 1);
|
|
72
|
+
for (let index = first; index >= 0 && index < blocks.length; index += step) {
|
|
73
|
+
if (blocks[index]?.hidden !== true) return setActive(planEl, index);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
/**
|
|
@@ -85,5 +92,69 @@ export function blockAtLine(
|
|
|
85
92
|
return wrap && block ? { wrap, block } : null;
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
/**
|
|
89
|
-
export
|
|
95
|
+
/** What the reader asked the plan to show. */
|
|
96
|
+
export type ReviewView = "all" | "changed" | "comments";
|
|
97
|
+
|
|
98
|
+
/** How much of the plan a view kept, so the page can say what it is hiding. */
|
|
99
|
+
export interface ViewCounts {
|
|
100
|
+
shown: number;
|
|
101
|
+
hidden: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Keep the blocks a view asks for, and hide the rest.
|
|
106
|
+
*
|
|
107
|
+
* A view is a filter over what is drawn, not a second document: `All` restores every
|
|
108
|
+
* block, so the reader is one click from the whole plan, and the page's status line says
|
|
109
|
+
* how many were hidden so a filtered document never reads as a short one. Headings stay
|
|
110
|
+
* in every view — a paragraph without its section is a sentence without a subject — and a
|
|
111
|
+
* view with nothing to show keeps the document whole rather than blanking it.
|
|
112
|
+
*/
|
|
113
|
+
/**
|
|
114
|
+
* Keep the blocks a view asks for, and hide the rest.
|
|
115
|
+
*
|
|
116
|
+
* A view is a filter over what is drawn, not a second document: `All` restores every
|
|
117
|
+
* block, so the reader is one click from the whole plan, and the page's status line says
|
|
118
|
+
* how many were hidden so a filtered document never reads as a short one. Headings stay
|
|
119
|
+
* in every view — a paragraph without its section is a sentence without a subject — and a
|
|
120
|
+
* view with nothing to show keeps the document whole rather than blanking it. A comment
|
|
121
|
+
* thread is hidden with the block it hangs under: a note with no text it refers to reads
|
|
122
|
+
* as a comment on whatever happens to be above it.
|
|
123
|
+
*/
|
|
124
|
+
export function applyView(planEl: ClientNode | null, view: ReviewView): ViewCounts {
|
|
125
|
+
if (!planEl) return { shown: 0, hidden: 0 };
|
|
126
|
+
const blocks = blockElements(planEl);
|
|
127
|
+
const keep = (block: ClientNode): boolean =>
|
|
128
|
+
view === "all" ||
|
|
129
|
+
block.getAttribute("data-kind") === "heading" ||
|
|
130
|
+
(view === "changed"
|
|
131
|
+
? block.getAttribute("data-changed") === "1"
|
|
132
|
+
: (block.className || "").split(" ").indexOf("bf-block--commented") >= 0);
|
|
133
|
+
// A filter that matches nothing but the headings is not a view of the plan: showing an
|
|
134
|
+
// empty page is worse than showing the whole document beside the count.
|
|
135
|
+
const matched = blocks.filter((block) => block.getAttribute("data-kind") !== "heading" && keep(block)).length;
|
|
136
|
+
const whole = view !== "all" && blocks.length > 0 && matched === 0;
|
|
137
|
+
const visible: Record<string, boolean> = {};
|
|
138
|
+
for (const block of blocks) {
|
|
139
|
+
block.hidden = !whole && !keep(block);
|
|
140
|
+
if (!block.hidden) visible[block.getAttribute("data-line") ?? ""] = true;
|
|
141
|
+
}
|
|
142
|
+
const threads = planEl.querySelectorAll(".bf-comments");
|
|
143
|
+
for (let i = 0; i < threads.length; i++) {
|
|
144
|
+
const thread = threads[i];
|
|
145
|
+
if (!thread) continue;
|
|
146
|
+
const line = thread.getAttribute("data-line");
|
|
147
|
+
if (line !== null) thread.hidden = visible[line] !== true;
|
|
148
|
+
}
|
|
149
|
+
const shown = whole ? blocks.length : blocks.filter(keep).length;
|
|
150
|
+
return { shown, hidden: blocks.length - shown };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The block rules as source, for the client bundle.
|
|
155
|
+
*
|
|
156
|
+
* `applyView` travels with them because the page's view control calls it on every render:
|
|
157
|
+
* a name the bundle does not carry is a `ReferenceError` at the first repaint, which
|
|
158
|
+
* aborts the boot before the composer and the progress bar are wired.
|
|
159
|
+
*/
|
|
160
|
+
export const WEB_BLOCKS_JS = [blockElements, setActive, moveActive, blockAtLine, applyView].map(String).join("\n\n");
|
|
@@ -76,6 +76,10 @@ export const WEB_CHROME_JS = String.raw` function setStatus(text, kind) {
|
|
|
76
76
|
if (saveButton) saveButton.disabled = true;
|
|
77
77
|
if (postButton) postButton.disabled = true;
|
|
78
78
|
if (cancelButton) cancelButton.disabled = true;
|
|
79
|
+
// The rail's comment controls go with them: a settled review takes no new comment, and
|
|
80
|
+
// the frame renderer is the one rule that decides that, so this asks for a repaint
|
|
81
|
+
// rather than removing the buttons itself.
|
|
82
|
+
renderAll();
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
// A conflict means the file moved under the page; only a reload can re-anchor.
|