@gavin-hu/my-pi-agent 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -31
- package/README.zh-CN.md +40 -26
- package/assets/screens/board.png +0 -0
- package/assets/screens/chrome.png +0 -0
- package/assets/screens/file-browse.png +0 -0
- package/assets/screens/plan-review.png +0 -0
- package/assets/screens/plan.png +0 -0
- package/docs/extensions/README.md +22 -24
- package/docs/extensions/board.md +361 -147
- package/docs/extensions/bytefly.md +152 -111
- package/docs/extensions/check.md +15 -17
- package/docs/extensions/context.md +35 -15
- package/docs/extensions/file-browse.md +30 -31
- package/docs/extensions/file-open.md +9 -11
- package/docs/extensions/file-read.md +12 -14
- package/docs/extensions/goal.md +15 -17
- package/docs/extensions/job.md +54 -42
- package/docs/extensions/memory.md +17 -19
- package/docs/extensions/plan.md +166 -126
- package/docs/extensions/question.md +16 -18
- package/docs/extensions/rewind.md +17 -19
- package/docs/extensions/serve.md +34 -18
- package/docs/extensions/session-bus.md +38 -16
- package/docs/extensions/subagent.md +23 -25
- package/docs/extensions/team.md +72 -33
- package/docs/extensions/todo.md +18 -18
- package/docs/extensions/watch.md +31 -31
- package/docs/extensions/web-access.md +2 -4
- package/docs/extensions/wechat.md +34 -35
- package/docs/extensions/worktree.md +56 -50
- package/extensions/board/commands.ts +56 -15
- package/extensions/board/format.ts +43 -24
- package/extensions/board/index.ts +5 -4
- package/extensions/board/input.ts +4 -9
- package/extensions/board/join.ts +4 -40
- package/extensions/board/lazy-page.ts +40 -0
- package/extensions/board/page.ts +13 -11
- package/extensions/board/result.ts +30 -16
- package/extensions/board/rows.ts +62 -25
- package/extensions/board/schema.ts +42 -10
- package/extensions/board/snapshot.ts +2 -1
- package/extensions/board/tone.ts +22 -0
- package/extensions/board/tools.ts +131 -44
- package/extensions/board/types.ts +36 -32
- package/extensions/board/view/card.ts +229 -0
- package/extensions/board/view/client.ts +33 -15
- package/extensions/board/view/dialog.ts +169 -6
- package/extensions/board/view/empty.ts +47 -0
- package/extensions/board/view/keys.ts +13 -2
- package/extensions/board/view/live.ts +89 -21
- package/extensions/board/view/names.ts +13 -4
- package/extensions/board/view/page.ts +23 -9
- package/extensions/board/view/render.ts +208 -271
- package/extensions/board/view/styles.ts +123 -31
- package/extensions/board/view/team.ts +297 -76
- package/extensions/bytefly/block/fallback.ts +1 -1
- package/extensions/bytefly/doctor/runtime.ts +5 -2
- package/extensions/bytefly/footer/collect/label.ts +60 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
- package/extensions/bytefly/footer/config.ts +4 -2
- package/extensions/bytefly/footer/register.ts +33 -4
- package/extensions/bytefly/footer/render/lines.ts +7 -4
- package/extensions/bytefly/footer/render/segments.ts +37 -10
- package/extensions/bytefly/footer/render/status.ts +0 -11
- package/extensions/bytefly/footer/types.ts +7 -0
- package/extensions/bytefly/header/config.ts +1 -1
- package/extensions/bytefly/index.ts +32 -6
- package/extensions/bytefly/mode/editor.ts +115 -0
- package/extensions/bytefly/panel/commands.ts +210 -120
- package/extensions/bytefly/panel/format.ts +4 -4
- package/extensions/bytefly/panel/settings.ts +73 -6
- package/extensions/bytefly/panel/tui/keys.ts +2 -6
- package/extensions/bytefly/update/cache.ts +1 -1
- package/extensions/bytefly/update/register.ts +4 -4
- package/extensions/bytefly/working/config.ts +5 -7
- package/extensions/bytefly/working/format.ts +10 -13
- package/extensions/bytefly/working/register.ts +22 -27
- package/extensions/check/commands.ts +3 -2
- package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
- package/extensions/context/commands.ts +2 -2
- package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
- package/extensions/file-browse/index.ts +67 -36
- package/extensions/file-read/render.ts +4 -4
- package/extensions/goal/plan-route.ts +2 -2
- package/extensions/job/child.ts +527 -0
- package/extensions/job/clock.ts +142 -0
- package/extensions/job/commands.ts +4 -2
- package/extensions/job/config.ts +8 -1
- package/extensions/job/format.ts +22 -1
- package/extensions/job/log-writer.ts +13 -2
- package/extensions/job/registry.ts +63 -7
- package/extensions/job/render.ts +34 -0
- package/extensions/job/runtime.ts +296 -655
- package/extensions/job/session.ts +132 -19
- package/extensions/job/store.ts +7 -7
- package/extensions/job/tools.ts +3 -3
- package/extensions/job/types.ts +47 -0
- package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -73
- package/extensions/job/waiters.ts +44 -1
- package/extensions/memory/commands.ts +4 -2
- package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
- package/extensions/plan/commands.ts +12 -9
- package/extensions/plan/format.ts +8 -2
- package/extensions/plan/git-bash.ts +115 -16
- package/extensions/plan/index.ts +53 -52
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plans.ts +5 -54
- package/extensions/plan/policy.ts +8 -10
- package/extensions/plan/review-lazy.ts +85 -0
- package/extensions/plan/review.ts +2 -2
- package/extensions/plan/runtime.ts +49 -39
- package/extensions/plan/sections.ts +1 -1
- package/extensions/plan/steps.ts +3 -3
- package/extensions/plan/todos.ts +1 -1
- package/extensions/plan/tools/cancel.ts +27 -62
- package/extensions/plan/tools/enter.ts +55 -81
- package/extensions/plan/tools/exit.ts +221 -238
- package/extensions/plan/tools/refusal.ts +18 -0
- package/extensions/plan/tools/steps.ts +75 -92
- package/extensions/plan/tools/write.ts +77 -120
- package/extensions/plan/tools.ts +188 -30
- package/extensions/plan/tui.ts +5 -5
- package/extensions/plan/types.ts +48 -5
- package/extensions/plan/web-client.ts +3 -0
- package/extensions/plan/web-lazy.ts +53 -0
- package/extensions/plan/web-markdown.ts +9 -67
- package/extensions/plan/web-payload.ts +3 -3
- package/extensions/plan/web-router.ts +18 -9
- package/extensions/plan/web.ts +1 -12
- package/extensions/question/tools.ts +6 -2
- package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
- package/extensions/rewind/commands/commands.ts +6 -3
- package/extensions/rewind/state/runtime.ts +1 -1
- package/extensions/rewind/{view/status.ts → status.ts} +4 -4
- package/extensions/rewind/view/review-tui.ts +4 -4
- package/extensions/serve/config.ts +10 -8
- package/extensions/serve/index.ts +17 -3
- package/extensions/serve/registry.ts +26 -17
- package/extensions/session-bus/commands.ts +11 -6
- package/extensions/session-bus/join-request.ts +2 -1
- package/extensions/session-bus/mail-watch.ts +26 -17
- package/extensions/session-bus/protocol.ts +15 -3
- package/extensions/session-bus/resolve.ts +5 -7
- package/extensions/session-bus/runtime.ts +40 -12
- package/extensions/session-bus/store.ts +101 -19
- package/extensions/session-bus/types.ts +19 -0
- package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
- package/extensions/subagent/agents.ts +1 -1
- package/extensions/subagent/commands.ts +4 -2
- package/extensions/subagent/format.ts +15 -15
- package/extensions/subagent/index.ts +1 -1
- package/extensions/subagent/render.ts +3 -3
- package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
- package/extensions/team/commands.ts +24 -7
- package/extensions/team/dispatch.ts +18 -4
- package/extensions/team/format.ts +17 -2
- package/extensions/team/index.ts +9 -8
- package/extensions/team/schema.ts +6 -5
- package/extensions/team/tools.ts +107 -45
- package/extensions/team/types.ts +3 -2
- package/extensions/todo/commands.ts +4 -2
- package/extensions/todo/format.ts +11 -0
- package/extensions/todo/render.ts +57 -0
- package/extensions/todo/runtime.ts +1 -1
- package/extensions/todo/tools.ts +1 -1
- package/extensions/todo/view/todo-screen.ts +122 -0
- package/extensions/todo/widget.ts +47 -0
- package/extensions/watch/commands.ts +4 -2
- package/extensions/watch/format.ts +36 -7
- package/extensions/watch/render.ts +57 -0
- package/extensions/watch/runtime.ts +27 -22
- package/extensions/watch/tools.ts +4 -4
- package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
- package/extensions/web-access/fetch/tool.ts +12 -4
- package/extensions/wechat/commands/commands.ts +18 -14
- package/extensions/wechat/index.ts +72 -37
- package/extensions/wechat/state/credentials.ts +1 -1
- package/extensions/wechat/state/state.ts +1 -1
- package/extensions/wechat/text/status.ts +112 -0
- package/extensions/wechat/types.ts +2 -2
- package/extensions/wechat/view/status-screen.ts +4 -103
- package/extensions/worktree/commands.ts +11 -9
- package/extensions/worktree/enter.ts +2 -2
- package/extensions/worktree/exit.ts +2 -2
- package/extensions/worktree/index.ts +5 -5
- package/extensions/worktree/screen.ts +3 -3
- package/extensions/worktree/status.ts +2 -2
- package/extensions/worktree/tools.ts +113 -116
- package/lib/README.md +27 -13
- package/lib/board/actions.ts +111 -0
- package/lib/board/artifact.ts +149 -0
- package/lib/board/card.ts +163 -52
- package/lib/board/config.ts +58 -0
- package/lib/board/contract.ts +113 -0
- package/lib/board/join.ts +38 -0
- package/lib/board/lanes.ts +182 -0
- package/lib/board/lead.ts +130 -0
- package/lib/board/limits.ts +78 -0
- package/lib/board/links.ts +31 -10
- package/lib/board/page-pointer.ts +1 -1
- package/lib/board/read.ts +83 -0
- package/lib/board/reason.ts +102 -0
- package/lib/board/round-text.ts +68 -6
- package/lib/board/round.ts +31 -15
- package/lib/board/rules.ts +151 -307
- package/lib/board/seat.ts +35 -0
- package/lib/board/session.ts +136 -54
- package/lib/board/skipped.ts +22 -0
- package/lib/board/steps.ts +487 -0
- package/lib/board/store.ts +87 -30
- package/lib/board/tone.ts +92 -0
- package/lib/board/view.ts +70 -58
- package/{extensions/watch → lib}/child.ts +40 -21
- package/lib/env.ts +65 -7
- package/lib/format.ts +13 -0
- package/lib/http/index.ts +14 -12
- package/lib/http/port.ts +15 -10
- package/lib/lazy.ts +45 -0
- package/lib/plan-entry.ts +39 -10
- package/lib/plans/paths.ts +82 -0
- package/lib/policy.ts +9 -6
- package/lib/report.ts +3 -2
- package/lib/requirements.ts +8 -8
- package/lib/session-label.ts +111 -5
- package/lib/session-roster.ts +43 -1
- package/lib/session-tool.ts +5 -4
- package/lib/stray-fields.ts +7 -2
- package/lib/tool-names.ts +5 -10
- package/lib/tui/README.md +23 -8
- package/lib/tui/chrome.ts +16 -15
- package/lib/tui/cursor.ts +18 -3
- package/lib/tui/glyphs.ts +9 -7
- package/lib/tui/tokens.ts +33 -2
- package/lib/tui/tool.ts +3 -3
- package/lib/web/README.md +21 -0
- package/lib/web/components.ts +5 -1
- package/lib/web/index.ts +1 -0
- package/lib/web/markdown.ts +83 -0
- package/lib/web/markup.ts +2 -1
- package/lib/web/runtime.ts +9 -10
- package/package.json +6 -1
- package/skills/init/SKILL.md +5 -5
- package/skills/next/SKILL.md +3 -5
- package/extensions/board/seat.ts +0 -28
- package/extensions/todo/tui.ts +0 -212
- package/lib/tui/index.ts +0 -34
- package/skills/project-docs/SKILL.md +0 -133
- package/skills/project-docs/references/audit.md +0 -131
- package/skills/project-docs/references/changelog.md +0 -105
- package/skills/project-docs/references/discovery.md +0 -127
- package/skills/project-docs/references/readme.md +0 -80
- package/skills/project-docs/references/release.md +0 -84
- package/skills/project-docs/references/roadmap.md +0 -93
- package/skills/project-docs/scripts/state.ts +0 -316
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* Session-scoped state and tool gating for plan mode.
|
|
3
3
|
*
|
|
4
4
|
* Enabling plan mode removes every tool the policy does not consider read-only
|
|
5
|
-
* from the active set and
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* persisted as a custom session entry so `/resume` and `/tree`
|
|
9
|
-
* branch, and it is also mirrored into the footer status. The review
|
|
10
|
-
* travels the same way, as one digest per heading section, so the
|
|
11
|
-
* markers survive a resume too.
|
|
5
|
+
* from the active set, and keeps the one `plan` tool active in both modes: its
|
|
6
|
+
* actions are what ask to enter, save the artifact under the plans directory,
|
|
7
|
+
* present it, tick it, and leave. Disabling restores exactly what it hid. The
|
|
8
|
+
* enabled flag is persisted as a custom session entry so `/resume` and `/tree`
|
|
9
|
+
* follow the branch, and it is also mirrored into the footer status. The review
|
|
10
|
+
* baseline travels the same way, as one digest per heading section, so the
|
|
11
|
+
* changed-section markers survive a resume too.
|
|
12
12
|
*
|
|
13
|
-
* `
|
|
14
|
-
* in `index.ts` permits
|
|
15
|
-
* extension's (matched by source path), so a same-named tool from another
|
|
16
|
-
* extension cannot borrow the exemption.
|
|
13
|
+
* The `plan` tool's `write` action is the one sanctioned write while planning. The
|
|
14
|
+
* read-only guard in `index.ts` permits that tool only when the registered one is
|
|
15
|
+
* this extension's (matched by source path), so a same-named tool from another
|
|
16
|
+
* extension cannot borrow the exemption, and each action refuses outside its mode.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { basename, resolve } from "node:path";
|
|
@@ -23,23 +23,25 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
|
|
|
23
23
|
import { sanitizeLine } from "../../lib/format.ts";
|
|
24
24
|
import { PLAN_MODE_ENTRY_TYPE } from "../../lib/plan-state.ts";
|
|
25
25
|
import type { ReadOnlyPolicy } from "../../lib/policy.ts";
|
|
26
|
-
import {
|
|
26
|
+
import { PLAN_TOOL as PLAN_TOOL_NAME } from "../../lib/tool-names.ts";
|
|
27
27
|
import { GLYPHS, STATUS_KEYS } from "../../lib/tui/glyphs.ts";
|
|
28
|
-
import { paint } from "../../lib/tui/tokens.ts";
|
|
29
28
|
import { createPlanStore, type PlanStore } from "./plans.ts";
|
|
30
29
|
import { sameSections, sectionDigests, type SectionDigest } from "./plan-text.ts";
|
|
31
30
|
import type { PlanModeEntry, ReviewBaselineEntry, SeededStepsEntry } from "./types.ts";
|
|
32
31
|
|
|
33
|
-
/**
|
|
34
|
-
export const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export const
|
|
32
|
+
/** The one model-facing tool: every planning verb is one of its actions. */
|
|
33
|
+
export const PLAN_TOOL = PLAN_TOOL_NAME;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The verbs of the one `plan` tool, in the order the schema offers them.
|
|
37
|
+
*
|
|
38
|
+
* One action tool rather than five tools, so every verb shares a name and a
|
|
39
|
+
* registration; the order is the one the description walks them in.
|
|
40
|
+
*/
|
|
41
|
+
export const PLAN_ACTIONS = ["enter", "write", "exit", "steps", "cancel"] as const;
|
|
42
|
+
|
|
43
|
+
/** One verb of the `plan` tool. */
|
|
44
|
+
export type PlanAction = (typeof PLAN_ACTIONS)[number];
|
|
43
45
|
/** Custom-entry type used for plan-mode state. */
|
|
44
46
|
export const STATE_TYPE = PLAN_MODE_ENTRY_TYPE;
|
|
45
47
|
/** Custom-entry type used for a plan's review baseline. */
|
|
@@ -50,16 +52,13 @@ export const SEEDED_TYPE = "plan-seeded";
|
|
|
50
52
|
/** Longest plan-file basename shown in the footer chip. */
|
|
51
53
|
const STATUS_NAME_LENGTH = 28;
|
|
52
54
|
|
|
53
|
-
/** Tools plan mode activates itself. */
|
|
54
|
-
const CONTROL_TOOLS = [EXIT_TOOL, CANCEL_TOOL, WRITE_PLAN_TOOL];
|
|
55
|
-
|
|
56
55
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
56
|
+
* The one tool plan mode keeps in the active set in both modes. Every other tool
|
|
57
|
+
* follows the policy, which is about writing files and cannot answer for a tool
|
|
58
|
+
* whose actions are the mode's own verbs: hiding it while planning would hide the
|
|
59
|
+
* way to write the plan and the way out.
|
|
61
60
|
*/
|
|
62
|
-
const
|
|
61
|
+
const MODE_TOOLS = [PLAN_TOOL];
|
|
63
62
|
|
|
64
63
|
export interface PlanRuntime {
|
|
65
64
|
/** Whether plan mode is currently on. */
|
|
@@ -69,9 +68,9 @@ export interface PlanRuntime {
|
|
|
69
68
|
toggle(ctx: ExtensionContext): void;
|
|
70
69
|
/** Restore state from the branch and the `--plan` flag. */
|
|
71
70
|
restore(ctx: ExtensionContext): void;
|
|
72
|
-
/** Plan-file storage for `
|
|
71
|
+
/** Plan-file storage for the `write` and `exit` actions. */
|
|
73
72
|
plans: PlanStore;
|
|
74
|
-
/** Whether `toolName` is
|
|
73
|
+
/** Whether `toolName` is the `plan` tool (not a same-named impostor) the read-only guard exempts. */
|
|
75
74
|
isOwnTool(toolName: string): boolean;
|
|
76
75
|
/** Path of the most recently written plan, for the footer chip. */
|
|
77
76
|
lastPlanPath(): string | undefined;
|
|
@@ -195,6 +194,9 @@ export function createPlanRuntime(
|
|
|
195
194
|
// Tools plan mode deactivated, so disabling restores exactly those and does
|
|
196
195
|
// not re-activate a tool another extension intentionally hid.
|
|
197
196
|
let removedForPlan: string[] = [];
|
|
197
|
+
// Mode tools this enable had to activate, so disabling puts the user's own
|
|
198
|
+
// active set back instead of widening it.
|
|
199
|
+
let addedForPlan: string[] = [];
|
|
198
200
|
|
|
199
201
|
const plans = createPlanStore(pi);
|
|
200
202
|
|
|
@@ -204,7 +206,7 @@ export function createPlanRuntime(
|
|
|
204
206
|
* is rejected.
|
|
205
207
|
*/
|
|
206
208
|
const isOwnTool = (toolName: string): boolean => {
|
|
207
|
-
if (
|
|
209
|
+
if (toolName !== PLAN_TOOL) return false;
|
|
208
210
|
const info = pi.getAllTools().find((tool) => tool.name === toolName);
|
|
209
211
|
const source = info?.sourceInfo?.path;
|
|
210
212
|
return source === undefined || options.entryPath === undefined || samePath(source, options.entryPath);
|
|
@@ -212,7 +214,7 @@ export function createPlanRuntime(
|
|
|
212
214
|
|
|
213
215
|
/** Tools plan mode would hide from the current active set. */
|
|
214
216
|
const captureHidden = (): string[] => [
|
|
215
|
-
...new Set(pi.getActiveTools().filter((name) => !policy.isAllowed(name) && !
|
|
217
|
+
...new Set(pi.getActiveTools().filter((name) => !policy.isAllowed(name) && !MODE_TOOLS.includes(name))),
|
|
216
218
|
];
|
|
217
219
|
|
|
218
220
|
/**
|
|
@@ -224,12 +226,18 @@ export function createPlanRuntime(
|
|
|
224
226
|
const active = pi.getActiveTools();
|
|
225
227
|
if (enabled) {
|
|
226
228
|
// Keep only tools the policy considers safe to expose while read-only
|
|
227
|
-
// (structured readers and the plan/goal trackers) plus the
|
|
228
|
-
|
|
229
|
+
// (structured readers and the plan/goal trackers), plus the `plan` tool
|
|
230
|
+
// itself, whose actions are the mode's own vocabulary.
|
|
231
|
+
addedForPlan = MODE_TOOLS.filter((name) => !active.includes(name));
|
|
232
|
+
pi.setActiveTools([...new Set([...active.filter((name) => policy.isAllowed(name)), ...MODE_TOOLS])]);
|
|
229
233
|
return;
|
|
230
234
|
}
|
|
235
|
+
// A mode tool this enable had to activate goes back to how the user left it,
|
|
236
|
+
// rather than staying active because the mode needed it for a while.
|
|
237
|
+
const kept = active.filter((name) => !addedForPlan.includes(name));
|
|
238
|
+
addedForPlan = [];
|
|
231
239
|
const restored = addHidden ? removedForPlan : [];
|
|
232
|
-
pi.setActiveTools([...new Set([...
|
|
240
|
+
pi.setActiveTools([...new Set([...kept, ...restored])]);
|
|
233
241
|
removedForPlan = [];
|
|
234
242
|
};
|
|
235
243
|
|
|
@@ -237,7 +245,9 @@ export function createPlanRuntime(
|
|
|
237
245
|
lastPlanPath ? `${GLYPHS.plan} plan · ${statusName(lastPlanPath)}` : `${GLYPHS.plan} plan`;
|
|
238
246
|
|
|
239
247
|
const setStatus = (ctx: ExtensionContext): void => {
|
|
240
|
-
|
|
248
|
+
// Published unstyled: the chip's text is what a footer without bytefly shows, and
|
|
249
|
+
// bytefly's mode segment paints the mode by role instead of reading this pill.
|
|
250
|
+
ctx.ui.setStatus(STATUS_KEYS.planMode, enabled ? statusText() : undefined);
|
|
241
251
|
};
|
|
242
252
|
|
|
243
253
|
const setEnabled = (next: boolean, ctx: ExtensionContext | undefined, persist: boolean): void => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Plan-document sections (pure).
|
|
3
3
|
*
|
|
4
4
|
* One fence-aware scan of a plan's markdown: the review screen navigates by
|
|
5
|
-
* section, `
|
|
5
|
+
* section, the `plan` tool's `steps` action looks up `## Steps`, a carried comment finds the
|
|
6
6
|
* block it belongs to, and the browser outline lists the same headings. A second
|
|
7
7
|
* scanner would disagree about a heading inside a fence, so every reader goes
|
|
8
8
|
* through this module.
|
package/extensions/plan/steps.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const MAX_STEP_LENGTH = 200;
|
|
|
31
31
|
* section that carries the end state — the same surface before and after when the
|
|
32
32
|
* change alters one that already exists, one frame when it is new — in a fence
|
|
33
33
|
* that stays untagged unless every line is code, so the reviewer sees the result
|
|
34
|
-
* before the steps that build it. Guidance only: `
|
|
34
|
+
* before the steps that build it. Guidance only: the `write` action never rejects or
|
|
35
35
|
* rewrites a plan that omits a section, and a change with no visible surface drops
|
|
36
36
|
* `Visual`.
|
|
37
37
|
*/
|
|
@@ -268,7 +268,7 @@ const LIST_PREFIX = /^\s*(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s*)?/;
|
|
|
268
268
|
* list markers and inline emphasis, and the lines are joined with `; `, prefixed
|
|
269
269
|
* with `Verify:` and elided like a long step. The item is deliberately *not* a
|
|
270
270
|
* plan step: it is not in the `Steps` section, so it must not count against the
|
|
271
|
-
* step cap or appear in `
|
|
271
|
+
* step cap or appear in the `steps` action's numbering.
|
|
272
272
|
*
|
|
273
273
|
* A plan with no `Steps` heading feeds its shallowest list as steps (the older
|
|
274
274
|
* rule), so that list already covers the verification bullets; appending a
|
|
@@ -297,7 +297,7 @@ export function extractPlanSteps(plan: string): PlanStep[] {
|
|
|
297
297
|
return topLevelItems(plan).map((item) => ({ content: elide(item.step.content), status: item.step.status }));
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
/** The full cost of a plan's step list, for the warning `
|
|
300
|
+
/** The full cost of a plan's step list, for the warning the `write` action reports. */
|
|
301
301
|
export interface PlanStepSummary {
|
|
302
302
|
/** Top-level items in the step list, before the {@link MAX_STEPS} cap. */
|
|
303
303
|
total: number;
|
package/extensions/plan/todos.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface TodoMerge {
|
|
|
44
44
|
/** Fallback list cap when the registered todo tool does not advertise one. */
|
|
45
45
|
export const DEFAULT_TODO_MAX = 50;
|
|
46
46
|
|
|
47
|
-
/** What the tick-time mirror did for one `
|
|
47
|
+
/** What the tick-time mirror did for one `steps` batch. */
|
|
48
48
|
export interface TodoMirror {
|
|
49
49
|
/** The list after the matching items were brought in line with the plan. */
|
|
50
50
|
todos: ExistingTodo[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `
|
|
2
|
+
* The `cancel` action of the `plan` tool.
|
|
3
3
|
*
|
|
4
4
|
* This is the escape hatch that does not need a plan file or a review dialog:
|
|
5
5
|
* when the user asks to leave planning, the model can restore write access
|
|
@@ -7,73 +7,38 @@
|
|
|
7
7
|
* still review the saved artifact.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
11
|
-
import { Type } from "typebox";
|
|
12
|
-
import { reuseText, toolDetails } from "../../../lib/tui/chrome.ts";
|
|
10
|
+
import type { AgentToolResult, ExtensionToolContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
13
11
|
import { paint } from "../../../lib/tui/tokens.ts";
|
|
14
|
-
import { callLine,
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import type { CancelPlanModeDetails } from "../types.ts";
|
|
12
|
+
import { callLine, type ToolCallText } from "../../../lib/tui/tool.ts";
|
|
13
|
+
import { planRefusal } from "./refusal.ts";
|
|
14
|
+
import { PLAN_TOOL, type PlanRuntime } from "../runtime.ts";
|
|
15
|
+
import type { CancelPlanModeDetails, PlanDetails } from "../types.ts";
|
|
18
16
|
|
|
19
17
|
const CANCELLED_TEXT =
|
|
20
18
|
"Plan mode is off and write access is restored. The plan was not approved; no todo items or goal were recorded. " +
|
|
21
19
|
"Any saved plan file remains for a later review.";
|
|
22
20
|
|
|
23
|
-
export function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
],
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// The mode change touches no files; like enter_plan_mode, it needs no
|
|
38
|
-
// destructive annotation.
|
|
39
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
40
|
-
|
|
41
|
-
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
42
|
-
if (!runtime.isEnabled()) {
|
|
43
|
-
return failedResult("Not in plan mode; there is nothing to cancel.", {
|
|
44
|
-
cancelled: false,
|
|
45
|
-
} satisfies CancelPlanModeDetails);
|
|
46
|
-
}
|
|
47
|
-
runtime.disable(ctx);
|
|
48
|
-
ctx.ui.notify("Plan mode off — full access restored. The plan was not approved.", "info");
|
|
49
|
-
return {
|
|
50
|
-
content: [{ type: "text", text: CANCELLED_TEXT }],
|
|
51
|
-
details: { cancelled: true } satisfies CancelPlanModeDetails,
|
|
52
|
-
};
|
|
53
|
-
},
|
|
21
|
+
export async function executeCancel(
|
|
22
|
+
runtime: PlanRuntime,
|
|
23
|
+
ctx: ExtensionToolContext,
|
|
24
|
+
): Promise<AgentToolResult<PlanDetails>> {
|
|
25
|
+
if (!runtime.isEnabled()) {
|
|
26
|
+
return planRefusal("cancel", "Not in plan mode; there is nothing to cancel.");
|
|
27
|
+
}
|
|
28
|
+
runtime.disable(ctx);
|
|
29
|
+
ctx.ui.notify("Plan mode off — full access restored. The plan was not approved.", "info");
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: "text", text: CANCELLED_TEXT }],
|
|
32
|
+
details: { action: "cancel", cancelled: true } satisfies CancelPlanModeDetails,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
54
35
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
36
|
+
export function callCancel(theme: Theme, last: unknown): ToolCallText {
|
|
37
|
+
return callLine(theme, last, PLAN_TOOL, "cancel");
|
|
38
|
+
}
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
result,
|
|
64
|
-
options.expanded,
|
|
65
|
-
toolDetails<CancelPlanModeDetails>(result.details),
|
|
66
|
-
);
|
|
67
|
-
if ("line" in outcome) {
|
|
68
|
-
text.setText(outcome.line);
|
|
69
|
-
return text;
|
|
70
|
-
}
|
|
71
|
-
text.setText(
|
|
72
|
-
outcome.details.cancelled
|
|
73
|
-
? paint(theme, "warn", "Plan mode off — plan not approved")
|
|
74
|
-
: paint(theme, "meta", "Not in plan mode"),
|
|
75
|
-
);
|
|
76
|
-
return text;
|
|
77
|
-
},
|
|
78
|
-
});
|
|
40
|
+
export function textCancel(details: CancelPlanModeDetails, theme: Theme): string {
|
|
41
|
+
return details.cancelled
|
|
42
|
+
? paint(theme, "warn", "Plan mode off — plan not approved")
|
|
43
|
+
: paint(theme, "meta", "Not in plan mode");
|
|
79
44
|
}
|
|
@@ -1,98 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `
|
|
2
|
+
* The `enter` action of the `plan` tool.
|
|
3
3
|
*
|
|
4
4
|
* It asks for confirmation, then turns the runtime's enabled flag on; the tool
|
|
5
5
|
* gating and persistence belong to `runtime.ts`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
9
|
-
import { Type } from "typebox";
|
|
10
|
-
import { reuseText, toolDetails } from "../../../lib/tui/chrome.ts";
|
|
11
|
-
import { GLYPHS } from "../../../lib/tui/glyphs.ts";
|
|
12
|
-
import { paint } from "../../../lib/tui/tokens.ts";
|
|
13
|
-
import { callLine, detailsOrLine } from "../../../lib/tui/tool.ts";
|
|
14
|
-
import { failedResult } from "../../../lib/tool-result.ts";
|
|
8
|
+
import type { AgentToolResult, ExtensionToolContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
15
9
|
import { isRemoteTurn, remoteLanguage } from "../../../lib/interaction.ts";
|
|
16
10
|
import { PLAN_CONSENT, PLAN_CONSENT_SHORT, PLAN_CONSENT_SHORT_ZH, READ_ONLY_SUMMARY } from "../../../lib/plan-entry.ts";
|
|
17
|
-
import {
|
|
11
|
+
import { failedResult } from "../../../lib/tool-result.ts";
|
|
12
|
+
import { GLYPHS } from "../../../lib/tui/glyphs.ts";
|
|
13
|
+
import { paint } from "../../../lib/tui/tokens.ts";
|
|
14
|
+
import { callLine, type ToolCallText } from "../../../lib/tui/tool.ts";
|
|
15
|
+
import { PLAN_TOOL, type PlanRuntime } from "../runtime.ts";
|
|
18
16
|
import type { EnterPlanModeDetails } from "../types.ts";
|
|
19
17
|
|
|
20
18
|
const ENTERED_TEXT =
|
|
21
|
-
"You are now in plan mode. Explore read-only, then save the full plan with
|
|
22
|
-
"and call
|
|
19
|
+
"You are now in plan mode. Explore read-only, then save the full plan with `plan` action `write` (a short title and the " +
|
|
20
|
+
"markdown) and call `plan` action `exit` with the returned plan_path so the user can read the file and choose. " +
|
|
23
21
|
`While planning, ${READ_ONLY_SUMMARY}.`;
|
|
24
22
|
|
|
25
|
-
export function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
43
|
-
if (runtime.isEnabled()) {
|
|
44
|
-
return {
|
|
45
|
-
content: [{ type: "text", text: "Already in plan mode." }],
|
|
46
|
-
details: { entered: true, already: true } satisfies EnterPlanModeDetails,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
if (!ctx.hasUI) {
|
|
50
|
-
return failedResult(
|
|
51
|
-
"No interactive UI is available to confirm entering plan mode. Ask the user whether they want a plan.",
|
|
52
|
-
{ entered: false, unavailable: true } satisfies EnterPlanModeDetails,
|
|
53
|
-
);
|
|
54
|
-
}
|
|
23
|
+
export async function executeEnter(
|
|
24
|
+
runtime: PlanRuntime,
|
|
25
|
+
ctx: ExtensionToolContext,
|
|
26
|
+
): Promise<AgentToolResult<EnterPlanModeDetails>> {
|
|
27
|
+
if (runtime.isEnabled()) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: "Already in plan mode." }],
|
|
30
|
+
details: { action: "enter", entered: true, already: true } satisfies EnterPlanModeDetails,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (!ctx.hasUI) {
|
|
34
|
+
return failedResult(
|
|
35
|
+
"No interactive UI is available to confirm entering plan mode. Ask the user whether they want a plan.",
|
|
36
|
+
{ action: "enter", entered: false, unavailable: true } satisfies EnterPlanModeDetails,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
40
|
+
const consent = isRemoteTurn(ctx)
|
|
41
|
+
? remoteLanguage(ctx) === "zh"
|
|
42
|
+
? PLAN_CONSENT_SHORT_ZH
|
|
43
|
+
: PLAN_CONSENT_SHORT
|
|
44
|
+
: PLAN_CONSENT;
|
|
45
|
+
const approved = await ctx.ui.confirm("Enter plan mode?", consent);
|
|
46
|
+
if (!approved) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: "text", text: "Stayed in normal mode. Proceed, or ask the user how to continue." }],
|
|
49
|
+
details: { action: "enter", entered: false } satisfies EnterPlanModeDetails,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
runtime.enable(ctx);
|
|
53
|
+
if (isRemoteTurn(ctx)) {
|
|
54
|
+
// The peer UI has no footer chip; say the mode and its exit in the chat.
|
|
55
|
+
ctx.ui.notify("Plan mode on — write access is off. Say “exit plan mode” to leave without approving.", "info");
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: ENTERED_TEXT }],
|
|
59
|
+
details: { action: "enter", entered: true } satisfies EnterPlanModeDetails,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
78
62
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
63
|
+
export function callEnter(theme: Theme, last: unknown): ToolCallText {
|
|
64
|
+
return callLine(theme, last, PLAN_TOOL, "enter");
|
|
65
|
+
}
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return text;
|
|
89
|
-
}
|
|
90
|
-
const details = outcome.details;
|
|
91
|
-
if (details.unavailable) text.setText(paint(theme, "warn", "No UI to confirm plan mode"));
|
|
92
|
-
else if (details.already) text.setText(paint(theme, "meta", "Already in plan mode"));
|
|
93
|
-
else if (details.entered) text.setText(paint(theme, "ok", `${GLYPHS.check} Plan mode enabled`));
|
|
94
|
-
else text.setText(paint(theme, "meta", "Stayed in normal mode"));
|
|
95
|
-
return text;
|
|
96
|
-
},
|
|
97
|
-
});
|
|
67
|
+
export function textEnter(details: EnterPlanModeDetails, theme: Theme): string {
|
|
68
|
+
if (details.unavailable) return paint(theme, "warn", "No UI to confirm plan mode");
|
|
69
|
+
if (details.already) return paint(theme, "meta", "Already in plan mode");
|
|
70
|
+
if (details.entered) return paint(theme, "ok", `${GLYPHS.check} Plan mode enabled`);
|
|
71
|
+
return paint(theme, "meta", "Stayed in normal mode");
|
|
98
72
|
}
|