@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
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `
|
|
2
|
+
* The `steps` action of the `plan` tool.
|
|
3
3
|
*
|
|
4
4
|
* It rewrites the plan file's checkboxes in one call, then mirrors the same
|
|
5
5
|
* batch into the todo items approval seeded. A rejected batch leaves both
|
|
6
6
|
* artifacts untouched.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
9
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
10
|
+
import type { AgentToolResult, ExtensionToolContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { Type, type Static } from "typebox";
|
|
12
|
-
import {
|
|
12
|
+
import { textResult } from "../../../lib/tool-result.ts";
|
|
13
13
|
import { GLYPHS } from "../../../lib/tui/glyphs.ts";
|
|
14
14
|
import { paint } from "../../../lib/tui/tokens.ts";
|
|
15
|
-
import { callLine,
|
|
16
|
-
import { failedResult, textResult } from "../../../lib/tool-result.ts";
|
|
15
|
+
import { callLine, type ToolCallText } from "../../../lib/tui/tool.ts";
|
|
17
16
|
import { describeUpdates, stepLabel } from "../format.ts";
|
|
18
|
-
import {
|
|
17
|
+
import { PLAN_TOOL, type PlanRuntime } from "../runtime.ts";
|
|
19
18
|
import { extractPlanSteps } from "../steps.ts";
|
|
20
|
-
import type { SetPlanStepsDetails } from "../types.ts";
|
|
19
|
+
import type { PlanDetails, SetPlanStepsDetails } from "../types.ts";
|
|
20
|
+
import { planRefusal } from "./refusal.ts";
|
|
21
21
|
import { mirrorTodos } from "./todo-seed.ts";
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/** One step of an `updates` batch, as the tool schema validates it. */
|
|
24
|
+
export const StepUpdateParams = Type.Object({
|
|
24
25
|
step: Type.Integer({
|
|
25
26
|
minimum: 1,
|
|
26
27
|
description: "1-based step number, in the order the plan was approved.",
|
|
27
28
|
}),
|
|
28
29
|
status: StringEnum(["pending", "completed"] as const, {
|
|
29
|
-
description:
|
|
30
|
+
description:
|
|
31
|
+
"The step's new state: `completed` ticks it, `pending` reopens it. A plan step has no in-progress state.",
|
|
30
32
|
}),
|
|
31
33
|
expected: Type.String({
|
|
32
34
|
description:
|
|
@@ -34,90 +36,71 @@ const StepUpdateParams = Type.Object({
|
|
|
34
36
|
}),
|
|
35
37
|
});
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
minItems: 1,
|
|
43
|
-
description:
|
|
44
|
-
"The steps to set; each entry sets one step's checkbox, and the last entry wins for a duplicate step. Pass expected as well to refuse a stale step number.",
|
|
45
|
-
}),
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
type SetPlanStepsArgs = Static<typeof SetPlanStepsParams>;
|
|
49
|
-
|
|
50
|
-
export function registerSetPlanStepsTool(pi: ExtensionAPI, runtime: PlanRuntime): void {
|
|
51
|
-
pi.registerTool({
|
|
52
|
-
name: SET_PLAN_STEPS_TOOL,
|
|
53
|
-
label: "Set plan steps",
|
|
54
|
-
description:
|
|
55
|
-
"Keep an approved plan file current by setting the checkboxes of the given top-level steps in place. Each step " +
|
|
56
|
-
"number matches the order the plan was approved in; a step can be marked completed or reopened. Only the " +
|
|
57
|
-
"checkboxes change; the step text is left as written. The file is rewritten once, and nothing changes when any " +
|
|
58
|
-
"step is out of range or an entry's expected text no longer matches the file.",
|
|
59
|
-
promptSnippet: "Tick steps off in the approved plan file.",
|
|
60
|
-
promptGuidelines: [
|
|
61
|
-
"After a plan is approved, call set_plan_steps as you complete steps, passing each step's text as expected so a reordered plan is refused instead of mis-ticked.",
|
|
62
|
-
],
|
|
63
|
-
parameters: SetPlanStepsParams,
|
|
64
|
-
executionMode: "sequential",
|
|
65
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
39
|
+
/** The fields `action: "steps"` reads. */
|
|
40
|
+
export interface SetPlanStepsArgs {
|
|
41
|
+
plan_path?: string;
|
|
42
|
+
updates?: Array<Static<typeof StepUpdateParams>>;
|
|
43
|
+
}
|
|
66
44
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
45
|
+
export async function executeSteps(
|
|
46
|
+
runtime: PlanRuntime,
|
|
47
|
+
args: SetPlanStepsArgs,
|
|
48
|
+
ctx: ExtensionToolContext,
|
|
49
|
+
): Promise<AgentToolResult<PlanDetails>> {
|
|
50
|
+
if (runtime.isEnabled()) {
|
|
51
|
+
return planRefusal("steps", "Not while planning; refine the plan with `plan` action `write` instead.");
|
|
52
|
+
}
|
|
53
|
+
const planPath = args.plan_path?.trim() ?? "";
|
|
54
|
+
if (planPath === "") {
|
|
55
|
+
return planRefusal(
|
|
56
|
+
"steps",
|
|
57
|
+
"`steps` needs plan_path: the approved plan file, as `plan` action `exit` returned it.",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const updates = args.updates ?? [];
|
|
61
|
+
if (updates.length === 0) {
|
|
62
|
+
return planRefusal("steps", "`steps` needs updates: the checkbox changes to apply, one entry per step.");
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const plan = await runtime.plans.setSteps(ctx.cwd, planPath, updates);
|
|
66
|
+
const steps = extractPlanSteps(plan.content);
|
|
67
|
+
const done = steps.filter((step) => step.status === "completed").length;
|
|
68
|
+
const todoNote = await mirrorTodos(ctx, updates);
|
|
69
|
+
return textResult(
|
|
70
|
+
`${describeUpdates(updates)} (${done}/${steps.length} done) in ${plan.relativePath}${todoNote}.`,
|
|
71
|
+
{
|
|
72
|
+
action: "steps",
|
|
73
|
+
path: plan.path,
|
|
74
|
+
relativePath: plan.relativePath,
|
|
75
|
+
updates,
|
|
76
|
+
done,
|
|
77
|
+
total: steps.length,
|
|
78
|
+
} satisfies SetPlanStepsDetails,
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
return planRefusal("steps", `Could not update the plan: ${message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
86
|
+
export function callSteps(args: SetPlanStepsArgs, theme: Theme, last: unknown): ToolCallText {
|
|
87
|
+
const updates = args.updates;
|
|
88
|
+
const label =
|
|
89
|
+
updates && updates.length === 1 && updates[0]?.step !== undefined
|
|
90
|
+
? `steps ${updates[0].step}`
|
|
91
|
+
: updates && updates.length > 1
|
|
92
|
+
? `steps ${updates.length} steps`
|
|
93
|
+
: "steps";
|
|
94
|
+
return callLine(theme, last, PLAN_TOOL, label);
|
|
95
|
+
}
|
|
103
96
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const pending = details.updates.filter((update) => update.status === "pending").map((u) => u.step);
|
|
114
|
-
const heading =
|
|
115
|
-
completed.length > 0
|
|
116
|
-
? paint(theme, "ok", `${GLYPHS.check} ${stepLabel(completed)} done`) +
|
|
117
|
-
(pending.length > 0 ? paint(theme, "meta", ` · ${stepLabel(pending)} reopened`) : "")
|
|
118
|
-
: paint(theme, "meta", `${stepLabel(pending)} reopened`);
|
|
119
|
-
text.setText(heading + paint(theme, "faint", ` · ${details.done}/${details.total}`));
|
|
120
|
-
return text;
|
|
121
|
-
},
|
|
122
|
-
});
|
|
97
|
+
export function textSteps(details: SetPlanStepsDetails, theme: Theme): string {
|
|
98
|
+
const completed = details.updates.filter((update) => update.status === "completed").map((u) => u.step);
|
|
99
|
+
const pending = details.updates.filter((update) => update.status === "pending").map((u) => u.step);
|
|
100
|
+
const heading =
|
|
101
|
+
completed.length > 0
|
|
102
|
+
? paint(theme, "ok", `${GLYPHS.check} ${stepLabel(completed)} done`) +
|
|
103
|
+
(pending.length > 0 ? paint(theme, "meta", ` · ${stepLabel(pending)} reopened`) : "")
|
|
104
|
+
: paint(theme, "meta", `${stepLabel(pending)} reopened`);
|
|
105
|
+
return heading + paint(theme, "faint", ` · ${details.done}/${details.total}`);
|
|
123
106
|
}
|
|
@@ -1,135 +1,92 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `
|
|
2
|
+
* The `write` action of the `plan` tool.
|
|
3
3
|
*
|
|
4
4
|
* The cap refusal happens before the file is written, so approval never lists a
|
|
5
5
|
* step it cannot track; the store owns naming, containment, and atomicity.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
9
|
-
import { Type, type Static } from "typebox";
|
|
8
|
+
import type { AgentToolResult, ExtensionToolContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
10
9
|
import { sanitizeLine } from "../../../lib/format.ts";
|
|
11
|
-
import {
|
|
10
|
+
import { textResult } from "../../../lib/tool-result.ts";
|
|
12
11
|
import { GLYPHS } from "../../../lib/tui/glyphs.ts";
|
|
13
12
|
import { paint } from "../../../lib/tui/tokens.ts";
|
|
14
|
-
import { callLine,
|
|
15
|
-
import { failedResult, textResult } from "../../../lib/tool-result.ts";
|
|
13
|
+
import { callLine, type ToolCallText } from "../../../lib/tui/tool.ts";
|
|
16
14
|
import { overCap, stepElisionNote } from "../format.ts";
|
|
17
|
-
import {
|
|
18
|
-
import { MAX_STEPS,
|
|
19
|
-
import type { WritePlanDetails } from "../types.ts";
|
|
15
|
+
import { PLAN_TOOL, type PlanRuntime } from "../runtime.ts";
|
|
16
|
+
import { MAX_STEPS, planStepSummary } from "../steps.ts";
|
|
17
|
+
import type { PlanDetails, WritePlanDetails } from "../types.ts";
|
|
18
|
+
import { planRefusal } from "./refusal.ts";
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}),
|
|
28
|
-
plan_path: Type.Optional(
|
|
29
|
-
Type.String({
|
|
30
|
-
description:
|
|
31
|
-
"Overwrite an existing plan file (one returned by an earlier write_plan) to refine it instead of creating a new file.",
|
|
32
|
-
}),
|
|
33
|
-
),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
type WritePlanArgs = Static<typeof WritePlanParams>;
|
|
37
|
-
|
|
38
|
-
export function registerWriteTool(pi: ExtensionAPI, runtime: PlanRuntime): void {
|
|
39
|
-
pi.registerTool({
|
|
40
|
-
name: WRITE_PLAN_TOOL,
|
|
41
|
-
label: "Write plan",
|
|
42
|
-
description:
|
|
43
|
-
"Save the plan you designed as a markdown file in the plans directory (the agent directory, keyed by " +
|
|
44
|
-
"repository). Write the complete plan, not a summary, in this outline so it is consistent and scannable:\n\n" +
|
|
45
|
-
`${PLAN_OUTLINE}\n\n` +
|
|
46
|
-
"Each section has one job: lead it with its outcome, keep a prose section to a few bullets or two or three " +
|
|
47
|
-
"sentences, give each step one verifiable outcome with the detail in a nested note, and put the sketch, files, " +
|
|
48
|
-
"and commands in inline code or a fence rather than pasted prose. " +
|
|
49
|
-
"When the change alters a surface that already exists, Visual shows it before and after in one fence each; " +
|
|
50
|
-
"tag a fence only when every line in it is code. " +
|
|
51
|
-
"The outline is a guide, not a form: drop Context, Approach, Visual, or Risks when there is nothing to say. " +
|
|
52
|
-
"Keep Steps and Verification — the steps are read from that section (without it, the shallowest list " +
|
|
53
|
-
"anywhere becomes the steps), and Verification becomes the last todo item. " +
|
|
54
|
-
"In Steps, each top-level bullet is one outcome in the order you will do them; " +
|
|
55
|
-
"an indented line continues that outcome, and a deeper bullet is a note. " +
|
|
56
|
-
"Returns the path to pass to exit_plan_mode; plan_path overwrites an existing file when refining. " +
|
|
57
|
-
`A plan with more than ${MAX_STEPS} top-level steps in its Steps section is refused, so approval never drops steps it cannot track.`,
|
|
58
|
-
promptSnippet: "Save the designed plan as a markdown file in the plans directory.",
|
|
59
|
-
promptGuidelines: [
|
|
60
|
-
"While plan mode is active, write the full plan with write_plan, then call exit_plan_mode with the returned path.",
|
|
61
|
-
"When refining with plan_path, keep every section heading as it was: the review marks changed sections by heading, " +
|
|
62
|
-
"so a rename reads as an edit.",
|
|
63
|
-
],
|
|
64
|
-
parameters: WritePlanParams,
|
|
65
|
-
defaultActive: false,
|
|
66
|
-
executionMode: "sequential",
|
|
67
|
-
// destructiveHint is true because plan_path overwrites an existing plan
|
|
68
|
-
// file; a plain write creates a new one.
|
|
69
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
20
|
+
/** The fields `action: "write"` reads. */
|
|
21
|
+
export interface WritePlanArgs {
|
|
22
|
+
title?: string;
|
|
23
|
+
content?: string;
|
|
24
|
+
plan_path?: string;
|
|
25
|
+
}
|
|
70
26
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
27
|
+
export async function executeWrite(
|
|
28
|
+
runtime: PlanRuntime,
|
|
29
|
+
args: WritePlanArgs,
|
|
30
|
+
ctx: ExtensionToolContext,
|
|
31
|
+
): Promise<AgentToolResult<PlanDetails>> {
|
|
32
|
+
if (!runtime.isEnabled()) {
|
|
33
|
+
return planRefusal("write", "Not in plan mode; call `plan` action `enter` first.");
|
|
34
|
+
}
|
|
35
|
+
const title = args.title?.trim() ?? "";
|
|
36
|
+
if (title === "") {
|
|
37
|
+
return planRefusal("write", "`write` needs a title: a short name for the plan, used as the file-name slug.");
|
|
38
|
+
}
|
|
39
|
+
const content = args.content ?? "";
|
|
40
|
+
if (content.trim() === "") {
|
|
41
|
+
return planRefusal("write", "`write` needs content: the complete plan as markdown in the documented outline.");
|
|
42
|
+
}
|
|
43
|
+
const summary = planStepSummary(content);
|
|
44
|
+
if (summary.total > MAX_STEPS) {
|
|
45
|
+
// Refuse before writing: a note after the fact arrives once the file exists and
|
|
46
|
+
// the review has listed every step, so the overflow is already lost.
|
|
47
|
+
return planRefusal(
|
|
48
|
+
"write",
|
|
49
|
+
`${overCap(summary.total)} Nothing was written; group the steps (one outcome each, sub-bullets for detail) and call \`plan\` action \`write\` again.`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const file = await runtime.plans.write(ctx.cwd, { title, content, planPath: args.plan_path });
|
|
54
|
+
runtime.setLastPlan(ctx, file.path);
|
|
55
|
+
// A store an older version kept in the checkout was moved in by this
|
|
56
|
+
// write; say so once, where the user is looking.
|
|
57
|
+
const moved = runtime.plans.takeNotice();
|
|
58
|
+
if (moved) ctx.ui.notify(moved, "info");
|
|
59
|
+
// A refinement says where the text it replaced went: a reviewer's comment that was
|
|
60
|
+
// carried to a block that moved can be checked against what she actually read.
|
|
61
|
+
const kept =
|
|
62
|
+
file.replaced === undefined ? "" : `, keeping the revision it replaced at ${file.replaced.relativePath}`;
|
|
63
|
+
// A plan that was approved before is now out of step with the todo list, and
|
|
64
|
+
// the list is only re-seeded by approving it again. Say so here, where the
|
|
65
|
+
// refinement is made, instead of letting the two disagree in silence.
|
|
66
|
+
const reseed =
|
|
67
|
+
runtime.seeded(file.path) === undefined
|
|
68
|
+
? ""
|
|
69
|
+
: " This plan was approved before: its todo items are the ones that approval seeded, and approving it again replaces the steps that changed (an item you started or finished is left alone and named).";
|
|
70
|
+
return textResult(
|
|
71
|
+
`Wrote the plan to ${file.relativePath}${kept}. Call \`plan\` action \`exit\` with plan_path: ${file.path}${stepElisionNote(summary.elided)}${reseed}`,
|
|
72
|
+
{
|
|
73
|
+
action: "write",
|
|
74
|
+
path: file.path,
|
|
75
|
+
relativePath: file.relativePath,
|
|
76
|
+
bytes: file.bytes,
|
|
77
|
+
} satisfies WritePlanDetails,
|
|
78
|
+
);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
return planRefusal("write", `Could not write the plan: ${message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
116
84
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
85
|
+
export function callWrite(args: WritePlanArgs, theme: Theme, last: unknown): ToolCallText {
|
|
86
|
+
const title = args.title?.trim() ?? "";
|
|
87
|
+
return callLine(theme, last, PLAN_TOOL, title === "" ? "write" : `write ${sanitizeLine(title)}`);
|
|
88
|
+
}
|
|
121
89
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const outcome = detailsOrLine(theme, result, options.expanded, toolDetails<WritePlanDetails>(result.details));
|
|
125
|
-
if ("line" in outcome) {
|
|
126
|
-
text.setText(outcome.line);
|
|
127
|
-
return text;
|
|
128
|
-
}
|
|
129
|
-
text.setText(
|
|
130
|
-
paint(theme, "ok", `${GLYPHS.check} Saved plan `) + paint(theme, "meta", outcome.details.relativePath),
|
|
131
|
-
);
|
|
132
|
-
return text;
|
|
133
|
-
},
|
|
134
|
-
});
|
|
90
|
+
export function textWrite(details: WritePlanDetails, theme: Theme): string {
|
|
91
|
+
return paint(theme, "ok", `${GLYPHS.check} Saved plan `) + paint(theme, "meta", details.relativePath);
|
|
135
92
|
}
|