@gavin-hu/my-pi-agent 0.16.0 → 0.17.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 +14 -8
- package/README.zh-CN.md +14 -9
- package/extensions/bytefly/README.md +174 -101
- package/extensions/bytefly/footer/collect/entries.ts +1 -6
- package/extensions/bytefly/footer/collect/snapshot.ts +23 -9
- package/extensions/bytefly/footer/price/price.ts +12 -8
- package/extensions/bytefly/footer/register.ts +13 -1
- package/extensions/bytefly/footer/types.ts +11 -0
- package/extensions/bytefly/footer/unpriced.ts +36 -0
- package/extensions/bytefly/header/component.ts +15 -29
- package/extensions/bytefly/header/identity.ts +71 -0
- package/extensions/bytefly/header/register.ts +9 -3
- package/extensions/bytefly/index.ts +13 -5
- package/extensions/bytefly/message/format.ts +20 -13
- package/extensions/bytefly/message/register.ts +8 -7
- package/extensions/bytefly/panel/commands.ts +46 -25
- package/extensions/bytefly/panel/format.ts +49 -14
- package/extensions/bytefly/panel/join.ts +35 -9
- package/extensions/bytefly/panel/settings.ts +10 -27
- package/extensions/bytefly/panel/skills.ts +51 -0
- package/extensions/bytefly/panel/tui/extensions-view.ts +7 -61
- package/extensions/bytefly/panel/tui/frame.ts +9 -1
- package/extensions/bytefly/panel/tui/list-view.ts +72 -0
- package/extensions/bytefly/panel/tui/nav.ts +71 -3
- package/extensions/bytefly/panel/tui/settings-section.ts +250 -0
- package/extensions/bytefly/panel/tui/skills-view.ts +50 -0
- package/extensions/bytefly/panel/tui.ts +223 -331
- package/extensions/bytefly/panel/types.ts +26 -8
- package/extensions/bytefly/picker/discovery.ts +32 -27
- package/extensions/bytefly/picker/resources.ts +16 -8
- package/extensions/bytefly/picker/runtime.ts +62 -34
- package/extensions/bytefly/picker/toggle.ts +16 -10
- package/extensions/bytefly/picker/types.ts +31 -13
- package/extensions/bytefly/update/format.ts +66 -8
- package/extensions/bytefly/update/patch.ts +66 -0
- package/extensions/bytefly/update/register.ts +4 -11
- package/extensions/bytefly/update/release.ts +65 -0
- package/extensions/bytefly/update/state.ts +14 -0
- package/extensions/bytefly/update/suppress.ts +47 -29
- package/extensions/bytefly/update/types.ts +15 -5
- package/extensions/check/tui.ts +2 -0
- package/extensions/context/README.md +21 -11
- package/extensions/context/format.ts +10 -8
- package/extensions/context/report.ts +21 -8
- package/extensions/context/tui.ts +26 -10
- package/extensions/context/types.ts +5 -3
- package/extensions/file-browse/README.md +29 -1
- package/extensions/file-browse/client.ts +16 -463
- package/extensions/file-browse/html.ts +7 -5
- package/extensions/file-browse/palette.ts +224 -0
- package/extensions/file-browse/router.ts +2 -2
- package/extensions/file-browse/styles.ts +8 -3
- package/extensions/file-browse/tree.ts +381 -0
- package/extensions/file-read/README.md +4 -1
- package/extensions/file-read/extract/odt.ts +7 -5
- package/extensions/goal/tools.ts +6 -11
- package/extensions/job/index.ts +20 -45
- package/extensions/job/tools.ts +51 -77
- package/extensions/memory/tools.ts +4 -6
- package/extensions/memory/tui.ts +5 -1
- package/extensions/plan/README.md +88 -33
- package/extensions/plan/commands.ts +19 -4
- package/extensions/plan/config.ts +27 -0
- package/extensions/plan/plan-index.ts +3 -1
- package/extensions/plan/plans.ts +31 -13
- package/extensions/plan/review.ts +34 -14
- package/extensions/plan/runtime.ts +3 -1
- package/extensions/plan/steps.ts +43 -14
- package/extensions/plan/tools/cancel.ts +79 -0
- package/extensions/plan/tools/enter.ts +10 -10
- package/extensions/plan/tools/exit.ts +54 -43
- package/extensions/plan/tools/steps.ts +7 -19
- package/extensions/plan/tools/write.ts +22 -29
- package/extensions/plan/tools.ts +5 -2
- package/extensions/plan/tui.ts +9 -7
- package/extensions/plan/types.ts +8 -0
- package/extensions/plan/web-chrome.ts +101 -0
- package/extensions/plan/web-client.ts +98 -311
- package/extensions/plan/web-markdown.ts +222 -0
- package/extensions/plan/web-page.ts +28 -2
- package/extensions/plan/web-router.ts +2 -2
- package/extensions/rewind/checklist.ts +4 -0
- package/extensions/rewind/review-tui.ts +3 -1
- package/extensions/rewind/rewind-tui.ts +4 -0
- package/extensions/subagent/README.md +25 -12
- package/extensions/subagent/format.ts +72 -25
- package/extensions/subagent/index.ts +21 -47
- package/extensions/subagent/orchestrate.ts +18 -41
- package/extensions/subagent/render.ts +29 -42
- package/extensions/subagent/stream.ts +24 -18
- package/extensions/subagent/types.ts +7 -0
- package/extensions/todo/tools.ts +5 -6
- package/extensions/todo/tui.ts +5 -1
- package/extensions/watch/index.ts +10 -35
- package/extensions/watch/tools.ts +31 -59
- package/extensions/wechat/README.md +40 -18
- package/extensions/wechat/bridge.ts +38 -40
- package/extensions/wechat/client.ts +1 -4
- package/extensions/wechat/commands.ts +1 -1
- package/extensions/wechat/interaction.ts +25 -4
- package/extensions/wechat/login-screen.ts +2 -3
- package/extensions/wechat/notices.ts +7 -5
- package/extensions/wechat/prompt.ts +19 -4
- package/extensions/wechat/qrcode.ts +9 -9
- package/extensions/wechat/slash.ts +14 -0
- package/extensions/wechat/status-screen.ts +2 -2
- package/extensions/wechat/tools.ts +3 -11
- package/extensions/worktree/README.md +48 -12
- package/extensions/worktree/commands.ts +109 -65
- package/extensions/worktree/enter.ts +1 -1
- package/extensions/worktree/exit.ts +11 -1
- package/extensions/worktree/lifecycle.ts +9 -1
- package/extensions/worktree/screen.ts +263 -0
- package/extensions/worktree/status.ts +84 -38
- package/lib/README.md +7 -6
- package/lib/config.ts +12 -0
- package/lib/format.ts +6 -2
- package/lib/interaction.ts +18 -1
- package/lib/report.ts +75 -2
- package/lib/tool-result.ts +54 -4
- package/lib/tui/README.md +16 -8
- package/lib/tui/chrome.ts +27 -6
- package/lib/tui/dock-screen.ts +6 -2
- package/lib/tui/gauge.ts +9 -5
- package/lib/usage.ts +26 -0
- package/lib/web/README.md +131 -0
- package/lib/web/base.ts +19 -2
- package/lib/web/components.ts +10 -6
- package/lib/web/layout.ts +2 -0
- package/lib/web/markup.ts +2 -1
- package/lib/web/runtime.ts +103 -25
- package/lib/web/tokens.ts +23 -7
- package/package.json +3 -1
- package/skills/changelog/SKILL.md +57 -45
- package/skills/next/SKILL.md +184 -0
package/extensions/goal/tools.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { JsonValue } from "@earendil-works/pi-ai";
|
|
11
11
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { GOAL_TOOL } from "../../lib/tool-names.ts";
|
|
13
|
+
import { failedResult, textResult } from "../../lib/tool-result.ts";
|
|
13
14
|
import { callLine, detailsOrLine } from "../../lib/tui/tool.ts";
|
|
14
15
|
import { reuseText, toolDetails } from "../../lib/tui/chrome.ts";
|
|
15
16
|
import { paint } from "../../lib/tui/tokens.ts";
|
|
@@ -52,17 +53,11 @@ function toStructuredContent(details: GoalDetails): Record<string, JsonValue> {
|
|
|
52
53
|
*/
|
|
53
54
|
function goalResult(details: GoalDetails, options: { error?: string; openTodos?: number } = {}) {
|
|
54
55
|
const { error, openTodos = 0 } = options;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
details,
|
|
63
|
-
structuredContent: toStructuredContent(details),
|
|
64
|
-
...(error === undefined ? {} : { isError: true as const }),
|
|
65
|
-
};
|
|
56
|
+
const text = error === undefined ? formatGoalText(details.goal, openTodos) : `Error: ${error}`;
|
|
57
|
+
const structuredContent = toStructuredContent(details);
|
|
58
|
+
return error === undefined
|
|
59
|
+
? textResult(text, details, structuredContent)
|
|
60
|
+
: failedResult(text, details, structuredContent);
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
export function registerTools(pi: ExtensionAPI, runtime: GoalRuntime): void {
|
package/extensions/job/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
14
14
|
import { isExtensionEnabled } from "../../lib/env.ts";
|
|
15
15
|
import { JOB_FINISHED_CHANNEL, type JobFinishedEvent } from "../../lib/events.ts";
|
|
16
16
|
import { noticeMessage, type NoticeMessage } from "../../lib/notice.ts";
|
|
17
|
-
import { createPendingReporter } from "../../lib/report.ts";
|
|
17
|
+
import { createPendingReporter, registerPendingChannel } from "../../lib/report.ts";
|
|
18
18
|
import { registerCommands } from "./commands.ts";
|
|
19
19
|
import { formatCompletion, jobOutcome, shortLabel } from "./format.ts";
|
|
20
20
|
import { createJobsRuntime, type JobsRuntime } from "./runtime.ts";
|
|
@@ -80,51 +80,26 @@ export default function jobs(pi: ExtensionAPI, deps: JobsDeps = {}): void {
|
|
|
80
80
|
registerTools(pi, runtime);
|
|
81
81
|
registerCommands(pi, runtime);
|
|
82
82
|
|
|
83
|
-
pi
|
|
84
|
-
reporter
|
|
85
|
-
runtime
|
|
83
|
+
registerPendingChannel(pi, {
|
|
84
|
+
reporter,
|
|
85
|
+
runtime,
|
|
86
86
|
// `shutdown` clears these; a reload or session replacement reuses the
|
|
87
87
|
// runtime, so re-arm them on every session start.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
pi.on("agent_start", () => {
|
|
106
|
-
reporter.agentStart();
|
|
107
|
-
});
|
|
108
|
-
pi.on("agent_settled", () => {
|
|
109
|
-
reporter.agentSettled();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Report completions to the model at the start of the next turn.
|
|
113
|
-
pi.on("before_agent_start", (_event, ctx) => {
|
|
114
|
-
const reported = reporter.beforeAgentStart();
|
|
115
|
-
if (!reported) return undefined;
|
|
116
|
-
runtime.setStatus(ctx);
|
|
117
|
-
return reported;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Keep stale completion notices out of later turns. Only this extension's
|
|
121
|
-
// notices are pruned; another source's are left for its own handler.
|
|
122
|
-
pi.on("context", (event) => reporter.filterContext(event.messages));
|
|
123
|
-
|
|
124
|
-
// `shutdown` detaches the UI controller, which clears both chips.
|
|
125
|
-
pi.on("session_shutdown", async () => {
|
|
126
|
-
runtime.onFinish = undefined;
|
|
127
|
-
runtime.onComplete = undefined;
|
|
128
|
-
await runtime.shutdown();
|
|
88
|
+
onSessionStart: () => {
|
|
89
|
+
runtime.onFinish = () => reporter.report();
|
|
90
|
+
// Notify-listening extensions follow completions on the shared bus; the
|
|
91
|
+
// runtime only knows the callback, never the channel.
|
|
92
|
+
runtime.onComplete = (job) => {
|
|
93
|
+
try {
|
|
94
|
+
pi.events.emit(JOB_FINISHED_CHANNEL, toFinishedEvent(job));
|
|
95
|
+
} catch {
|
|
96
|
+
// The event bus is optional.
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
onSessionShutdown: () => {
|
|
101
|
+
runtime.onFinish = undefined;
|
|
102
|
+
runtime.onComplete = undefined;
|
|
103
|
+
},
|
|
129
104
|
});
|
|
130
105
|
}
|
package/extensions/job/tools.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import { errorResult } from "../../lib/tool-result.ts";
|
|
12
|
+
import { errorResult, textResult } from "../../lib/tool-result.ts";
|
|
13
13
|
import { reuseText, toolDetails } from "../../lib/tui/chrome.ts";
|
|
14
14
|
import { callLine, detailsOrLine, elapsedSuffix, rejectionLine, syncElapsedTimer } from "../../lib/tui/tool.ts";
|
|
15
15
|
import { SEPARATORS } from "../../lib/tui/glyphs.ts";
|
|
@@ -77,46 +77,40 @@ export function registerTools(pi: ExtensionAPI, runtime: JobsRuntime): void {
|
|
|
77
77
|
},
|
|
78
78
|
ctx,
|
|
79
79
|
);
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
text: `Started ${job.id} (pid ${job.pid ?? "?"}): ${job.command}\nPoll with \`job status ${job.id}\`, or \`job wait ${job.id}\`.`,
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
details: { action: "start", job: toRecord(job) } satisfies JobDetails,
|
|
88
|
-
};
|
|
80
|
+
return textResult(
|
|
81
|
+
`Started ${job.id} (pid ${job.pid ?? "?"}): ${job.command}\nPoll with \`job status ${job.id}\`, or \`job wait ${job.id}\`.`,
|
|
82
|
+
{ action: "start", job: toRecord(job) } satisfies JobDetails,
|
|
83
|
+
);
|
|
89
84
|
}
|
|
90
85
|
|
|
91
86
|
case "list": {
|
|
92
87
|
const jobs = runtime.list();
|
|
93
|
-
return {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
return textResult(formatJobList(jobs, Date.now(), runtime.sessionId), {
|
|
89
|
+
action: "list",
|
|
90
|
+
jobs,
|
|
91
|
+
sessionId: runtime.sessionId,
|
|
92
|
+
} satisfies JobDetails);
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
case "status": {
|
|
100
96
|
const job = runtime.get(call.id);
|
|
101
97
|
if (!job) return errorResult("status", `no job "${call.id}".`);
|
|
102
|
-
return {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
return textResult(formatJobStatus(job, Date.now(), runtime.sessionId), {
|
|
99
|
+
action: "status",
|
|
100
|
+
job: toRecord(job),
|
|
101
|
+
sessionId: runtime.sessionId,
|
|
102
|
+
} satisfies JobDetails);
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
case "logs": {
|
|
109
106
|
const result = runtime.logs(call.id, call.lines);
|
|
110
107
|
if (!result) return errorResult("logs", `no job "${call.id}".`);
|
|
111
|
-
return {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
truncated: result.truncated,
|
|
118
|
-
} satisfies JobDetails,
|
|
119
|
-
};
|
|
108
|
+
return textResult(result.text, {
|
|
109
|
+
action: "logs",
|
|
110
|
+
job: toRecord(result.job),
|
|
111
|
+
logs: result.lines.join("\n"),
|
|
112
|
+
truncated: result.truncated,
|
|
113
|
+
} satisfies JobDetails);
|
|
120
114
|
}
|
|
121
115
|
|
|
122
116
|
case "send": {
|
|
@@ -132,15 +126,12 @@ export function registerTools(pi: ExtensionAPI, runtime: JobsRuntime): void {
|
|
|
132
126
|
result.closed ? " and closed its stdin" : ""
|
|
133
127
|
}.`
|
|
134
128
|
: `Closed ${job.id}'s stdin.`;
|
|
135
|
-
return {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
stdinClosed: result.closed,
|
|
142
|
-
} satisfies JobDetails,
|
|
143
|
-
};
|
|
129
|
+
return textResult(text, {
|
|
130
|
+
action: "send",
|
|
131
|
+
job: toRecord(job),
|
|
132
|
+
sent: result.sent,
|
|
133
|
+
stdinClosed: result.closed,
|
|
134
|
+
} satisfies JobDetails);
|
|
144
135
|
}
|
|
145
136
|
|
|
146
137
|
case "kill": {
|
|
@@ -149,30 +140,27 @@ export function registerTools(pi: ExtensionAPI, runtime: JobsRuntime): void {
|
|
|
149
140
|
const job = runtime.kill(call.id, call.signal);
|
|
150
141
|
if (!job) return errorResult("kill", `no job "${call.id}".`);
|
|
151
142
|
const wasRunning = before.status === "running";
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
? `Sent ${call.signal ?? "SIGTERM"} to ${job.id} (${job.label}).`
|
|
158
|
-
: `${job.id} already ${job.status}; nothing to kill.`,
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
details: {
|
|
143
|
+
return textResult(
|
|
144
|
+
wasRunning
|
|
145
|
+
? `Sent ${call.signal ?? "SIGTERM"} to ${job.id} (${job.label}).`
|
|
146
|
+
: `${job.id} already ${job.status}; nothing to kill.`,
|
|
147
|
+
{
|
|
162
148
|
action: "kill",
|
|
163
149
|
job: toRecord(job),
|
|
164
150
|
signalled: wasRunning,
|
|
165
151
|
signal: call.signal ?? "SIGTERM",
|
|
166
152
|
} satisfies JobDetails,
|
|
167
|
-
|
|
153
|
+
);
|
|
168
154
|
}
|
|
169
155
|
|
|
170
156
|
case "wait": {
|
|
171
157
|
const result = await runtime.wait(call.id, call.timeoutMs, signal, () =>
|
|
172
|
-
onUpdate?.(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
158
|
+
onUpdate?.(
|
|
159
|
+
textResult(`Waiting on ${call.id}…`, {
|
|
160
|
+
action: "wait",
|
|
161
|
+
job: runtime.get(call.id),
|
|
162
|
+
} satisfies JobDetails),
|
|
163
|
+
),
|
|
176
164
|
);
|
|
177
165
|
if (!result) return errorResult("wait", `no job "${call.id}".`);
|
|
178
166
|
const { job, timedOut, cancelled } = result;
|
|
@@ -181,37 +169,23 @@ export function registerTools(pi: ExtensionAPI, runtime: JobsRuntime): void {
|
|
|
181
169
|
: timedOut
|
|
182
170
|
? " (still running after the timeout)"
|
|
183
171
|
: "";
|
|
184
|
-
return {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
],
|
|
191
|
-
details: {
|
|
192
|
-
action: "wait",
|
|
193
|
-
job: toRecord(job),
|
|
194
|
-
timedOut,
|
|
195
|
-
cancelled,
|
|
196
|
-
} satisfies JobDetails,
|
|
197
|
-
};
|
|
172
|
+
return textResult(`${formatCompletion(job)}${note}${job.lastLine ? `\nlast: ${job.lastLine}` : ""}`, {
|
|
173
|
+
action: "wait",
|
|
174
|
+
job: toRecord(job),
|
|
175
|
+
timedOut,
|
|
176
|
+
cancelled,
|
|
177
|
+
} satisfies JobDetails);
|
|
198
178
|
}
|
|
199
179
|
|
|
200
180
|
case "clear": {
|
|
201
181
|
const result = runtime.clear(call.id, call.all ?? false);
|
|
202
182
|
if (result.refused) return errorResult("clear", result.refused);
|
|
203
|
-
return
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
? "Nothing to clear."
|
|
210
|
-
: `Cleared ${result.cleared} job${result.cleared === 1 ? "" : "s"}.`,
|
|
211
|
-
},
|
|
212
|
-
],
|
|
213
|
-
details: { action: "clear", cleared: result.cleared, jobs: runtime.list() } satisfies JobDetails,
|
|
214
|
-
};
|
|
183
|
+
return textResult(
|
|
184
|
+
result.cleared === 0
|
|
185
|
+
? "Nothing to clear."
|
|
186
|
+
: `Cleared ${result.cleared} job${result.cleared === 1 ? "" : "s"}.`,
|
|
187
|
+
{ action: "clear", cleared: result.cleared, jobs: runtime.list() } satisfies JobDetails,
|
|
188
|
+
);
|
|
215
189
|
}
|
|
216
190
|
}
|
|
217
191
|
} catch (error) {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { JsonValue } from "@earendil-works/pi-ai";
|
|
12
12
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import { callLine, detailsOrLine } from "../../lib/tui/tool.ts";
|
|
14
|
+
import { failedResult, textResult } from "../../lib/tool-result.ts";
|
|
14
15
|
import { reuseText, toolDetails } from "../../lib/tui/chrome.ts";
|
|
15
16
|
import { messageOf } from "./errors.ts";
|
|
16
17
|
import { formatCallText, formatResultText, scopeLabel } from "./format.ts";
|
|
@@ -49,12 +50,9 @@ function toStructuredContent(details: MemoryDetails): Record<string, JsonValue>
|
|
|
49
50
|
*/
|
|
50
51
|
function memoryResult(details: MemoryDetails, error?: string) {
|
|
51
52
|
const full: MemoryDetails = error === undefined ? details : { ...details, error };
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
structuredContent: toStructuredContent(full),
|
|
56
|
-
...(error === undefined ? {} : { isError: true as const }),
|
|
57
|
-
};
|
|
53
|
+
const text = formatResultText(full);
|
|
54
|
+
const structuredContent = toStructuredContent(full);
|
|
55
|
+
return error === undefined ? textResult(text, full, structuredContent) : failedResult(text, full, structuredContent);
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
export function registerTools(pi: ExtensionAPI, runtime: MemoryRuntime): void {
|
package/extensions/memory/tui.ts
CHANGED
|
@@ -43,7 +43,7 @@ const MAX_NOTE_CHARS = 500;
|
|
|
43
43
|
/** Rows the screen shows when the terminal height is unknown. */
|
|
44
44
|
const SCREEN_DEFAULT_ROWS = 14;
|
|
45
45
|
|
|
46
|
-
/** Fixed rows around the list: header, summary, blank, blank, hint,
|
|
46
|
+
/** Fixed rows around the list: header, summary, blank, blank, hint, closing rule. */
|
|
47
47
|
const CHROME = 6;
|
|
48
48
|
|
|
49
49
|
/** Which scopes the list shows. */
|
|
@@ -482,6 +482,8 @@ export class MemoryPickerComponent implements Component {
|
|
|
482
482
|
const body: string[] = [];
|
|
483
483
|
for (const line of this.editor.render(Math.max(1, width - 2))) body.push(`${pad}${line}`);
|
|
484
484
|
return dockListFrame({
|
|
485
|
+
theme: this.theme,
|
|
486
|
+
width,
|
|
485
487
|
header: titleRule(this.theme, width, `Memory · ${verb} ${scope}`),
|
|
486
488
|
summary: clip(` ${paint(this.theme, "meta", summary)}`, width),
|
|
487
489
|
body,
|
|
@@ -528,6 +530,8 @@ export class MemoryPickerComponent implements Component {
|
|
|
528
530
|
]);
|
|
529
531
|
|
|
530
532
|
return dockListFrame({
|
|
533
|
+
theme: this.theme,
|
|
534
|
+
width: w,
|
|
531
535
|
header: titleRule(this.theme, w, "Memory"),
|
|
532
536
|
summary: this.summary(w),
|
|
533
537
|
body,
|
|
@@ -45,18 +45,35 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
45
45
|
empty repositories, and falls back to evidence with recorded assumptions when
|
|
46
46
|
the interview is unavailable or cancelled; it never invents commands.
|
|
47
47
|
- **Plan files.** The model saves the plan with `write_plan`; the file lands in
|
|
48
|
-
`<repo-root>/.pi/plans/<YYYY-MM-DD-HHmm>-<slug>.md`
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
`<repo-root>/.pi/plans/<YYYY-MM-DD-HHmm>-<slug>.md` — the entered worktree's
|
|
49
|
+
root when one is active — or `<agent-dir>/plans` outside a repository. The
|
|
50
|
+
plans directory is self-ignoring, so plan files never show up as untracked
|
|
51
|
+
files or in `rewind` snapshots.
|
|
51
52
|
- **A discoverable index.** `/plan list` walks the plans directory and shows
|
|
52
53
|
every plan with its title, modified time, and step progress, newest first — in
|
|
53
54
|
the TUI as a docked list where `Enter` opens the plan's review, elsewhere as a
|
|
54
55
|
notice with each plan's path. The directory stays the index: no registry file,
|
|
55
56
|
one bounded read per plan.
|
|
56
57
|
- **A shape that reads.** `write_plan` documents a canonical outline — a
|
|
57
|
-
`# <Title>`, then `## Goal`, `## Context`, `## Approach`, `##
|
|
58
|
-
`## Risks`, and `## Verification` — and the model is guided to
|
|
59
|
-
|
|
58
|
+
`# <Title>`, then `## Goal`, `## Context`, `## Approach`, `## Visual`,
|
|
59
|
+
`## Steps`, `## Risks`, and `## Verification` — and the model is guided to
|
|
60
|
+
follow it. Every example in the template is the shape wanted rather than a
|
|
61
|
+
description of it, and each section has one job: the goal is one sentence, the
|
|
62
|
+
context is the facts that force the approach, the approach names the
|
|
63
|
+
alternative it rejected, and verification is a fence of the commands to run.
|
|
64
|
+
`Visual` carries the end state in an untagged fence, with one line naming what to
|
|
65
|
+
look at: the same surface before and after when the change alters one that
|
|
66
|
+
already exists, one frame when the surface is new, because a reviewer judges the
|
|
67
|
+
result before the steps that build it; a change with no visible surface drops it.
|
|
68
|
+
The description calls the shape a guide rather than a form: `Context`,
|
|
69
|
+
`Approach`, `Visual`, and `Risks` are dropped when they have nothing to say,
|
|
70
|
+
while `Steps` and `Verification` are kept, because the steps are read from that
|
|
71
|
+
section (without it, the shallowest list anywhere becomes the steps) and
|
|
72
|
+
Verification becomes the last todo item. Within `Steps` only the shallowest
|
|
73
|
+
list is tracked, so nested bullets stay notes instead of becoming todos. This
|
|
74
|
+
is guidance only: any markdown is accepted, stored, and rendered as written,
|
|
75
|
+
and a refinement is asked to keep its section headings so the review can still
|
|
76
|
+
mark only what changed.
|
|
60
77
|
- **Trackable steps.** Steps are written as `- [ ]` checkboxes in the `## Steps`
|
|
61
78
|
section; approval seeds them into `todo`, with the `## Verification` section as
|
|
62
79
|
the last item so the definition of done travels with the work. A plan with more
|
|
@@ -76,7 +93,9 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
76
93
|
widget and the plan do not drift apart. Pass the step's text as `expected` and
|
|
77
94
|
an update whose step number moved is refused instead of ticking the wrong line.
|
|
78
95
|
- **Reviewable plan.** `exit_plan_mode` reads the plan file back and presents
|
|
79
|
-
it for approval. The user approves, keeps planning,
|
|
96
|
+
it for approval. The user approves, keeps planning, asks for a refinement, or
|
|
97
|
+
exits plan mode without approving; the last keeps the plan file for a later
|
|
98
|
+
review and records no todo items or goal.
|
|
80
99
|
On a remote turn (WeChat) the plan file is sent first, then a digest of its
|
|
81
100
|
title, step count, and first steps, then the choices, so the review is still
|
|
82
101
|
answerable when the attachment is not opened. The choices include **Send the
|
|
@@ -100,9 +119,10 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
100
119
|
replacement: existing items keep their place and status, a step whose text is
|
|
101
120
|
already there is skipped, and the tool's item cap defers the rest instead of
|
|
102
121
|
dropping anything. The plan's `## Verification` section follows as the **last**
|
|
103
|
-
todo item, summarized on one line (`Verify: …`) —
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
todo item, summarized on one line (`Verify: …`) — a fenced block contributes its
|
|
123
|
+
commands, not its fence lines. It is not a plan step, so it is ticked in the todo
|
|
124
|
+
list rather than with `set_plan_steps`, and it never counts against the 50-step
|
|
125
|
+
cap. A plan without a `Steps` heading keeps the older rule —
|
|
106
126
|
the shallowest list in the document — so prose bullets in a plan that follows the
|
|
107
127
|
outline never become todos, and its verification bullets are already covered.
|
|
108
128
|
Each seeded item is matched back to its step by text, so ticking a step with
|
|
@@ -113,8 +133,9 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
113
133
|
(`planning`) is finalized from the plan's own `## Goal` section — falling back
|
|
114
134
|
to the title — through the `goal` tool, with no extra dialog, because approving
|
|
115
135
|
the plan *is* the decision. A session with no goal at all gets one confirm to
|
|
116
|
-
record the same objective,
|
|
117
|
-
|
|
136
|
+
record the same objective, unless `plan.goalPrompt` is off, which records it
|
|
137
|
+
without asking. An `active` or `achieved` goal is never rewritten: a final
|
|
138
|
+
objective is the user's. No path spends a model turn.
|
|
118
139
|
- **Branch-aware.** The enabled flag and the review baseline are custom session
|
|
119
140
|
entries, so `/resume` and `/tree` restore the right mode and keep the
|
|
120
141
|
changed-section markers.
|
|
@@ -125,7 +146,8 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
125
146
|
|---|---|---|
|
|
126
147
|
| `enter_plan_mode` | normal mode | Ask to enter plan mode; empty schema, confirmed with `ctx.ui.confirm`. |
|
|
127
148
|
| `write_plan` | plan mode | Save the plan: `title` (file-name slug), `content` (full markdown in the documented outline), optional `plan_path` to overwrite a refinement. Returns the absolute path. A plan with more than 50 top-level steps in its `Steps` section is refused, with the cap and the fix named, and nothing is written. |
|
|
128
|
-
| `exit_plan_mode` | plan mode | Read the plan file (`plan_path`) and present it for approval. Refuses a file whose step list is over the cap, before the review screen. |
|
|
149
|
+
| `exit_plan_mode` | plan mode | Read the plan file (`plan_path`) and present it for approval. The review also offers **Exit plan mode (discard)**, which leaves plan mode without approving. Refuses a file whose step list is over the cap, before the review screen. |
|
|
150
|
+
| `cancel_plan_mode` | plan mode | Leave plan mode without a plan file or a review. Restores write access; records no todo items or goal; keeps any saved plan. Empty schema. |
|
|
129
151
|
| `set_plan_steps` | normal mode | Keep an approved plan current: set the checkboxes of the given top-level steps (`updates`, each a 1-based `step` in the order `exit_plan_mode` listed, plus `status`, and `expected` — the step's text, so a reordered plan is refused) to `pending` or `completed` in one write. Only the checkboxes change, and a reopened step drops its `[DONE:n]` marker. The todo item approval seeded from a step is ticked with it: the file is written first and is authoritative, a renamed item is left alone, and a failed todo write is reported in the result. |
|
|
130
152
|
|
|
131
153
|
### Commands and flags
|
|
@@ -137,6 +159,7 @@ How the `write_plan` and `exit_plan_mode` calls and their results look in the tr
|
|
|
137
159
|
| `/plan <prompt>` | the user, with the task | immediate |
|
|
138
160
|
| `/plan list` | the user, browsing the plans directory | immediate |
|
|
139
161
|
| `/plan review [path]` | the user, reviewing a plan in the browser | immediate |
|
|
162
|
+
| `/plan cancel` | the user, leaving plan mode without approving | immediate |
|
|
140
163
|
| `/skill:init` | the user, creating or updating `AGENTS.md` | enters plan mode; write waits for plan approval |
|
|
141
164
|
| `Ctrl+Alt+P` | the user | immediate |
|
|
142
165
|
| `--plan` | the user, at launch | immediate |
|
|
@@ -204,7 +227,7 @@ package, so load the whole package (`pi -e .`) or install it; loading only
|
|
|
204
227
|
## Behaviour by mode
|
|
205
228
|
|
|
206
229
|
The review screen is TUI-only. `exit_plan_mode` opens `PlanViewComponent`: a
|
|
207
|
-
scrollable, width-safe view of the plan with approve/refine/keep in the footer.
|
|
230
|
+
scrollable, width-safe view of the plan with approve/refine/keep/exit in the footer.
|
|
208
231
|
The body is rendered with Pi's own `Markdown` component and `getMarkdownTheme()`,
|
|
209
232
|
so headings, lists, inline code, code blocks, links, and quotes carry the active
|
|
210
233
|
theme's colors; task items get the shared `◼` (done) or `◻` (pending) marker,
|
|
@@ -213,7 +236,11 @@ actions (`a approve & run` in `success`, `r refine` in `accent`).
|
|
|
213
236
|
`a` ends plan mode, restores write access, and seeds `todo`; `r` keeps the plan
|
|
214
237
|
mounted and opens an inline editor beneath it (`Enter` submits, an empty buffer
|
|
215
238
|
does nothing, `Esc` returns to the plan); `Esc` keeps planning and tells the
|
|
216
|
-
model the plan was not approved
|
|
239
|
+
model the plan was not approved; `x` leaves plan mode without approving and
|
|
240
|
+
keeps the plan file. The dialog path offers the same five outcomes, including
|
|
241
|
+
**Exit plan mode (discard)**. Dismissing the dialog (or a timeout) is also an
|
|
242
|
+
exit, not a silent `keep`; only the explicit **Keep planning** option stays in
|
|
243
|
+
plan mode. When the plan changed since the previous review
|
|
217
244
|
on this branch, a summary line under the title names how many sections changed
|
|
218
245
|
and the scroll row appends `· changed` to the current section. Scroll with `↑`/`↓` or `j`/`k`, `space`/`b` or
|
|
219
246
|
`PgUp`/`PgDn`, `d`/`u`, `g`/`G`, and the mouse wheel. Press `n`/`p` to jump to
|
|
@@ -225,12 +252,12 @@ from the design system's `gaugeBar` and stays
|
|
|
225
252
|
visible while the refine editor is open, since the body above is still clipped.
|
|
226
253
|
Hints are
|
|
227
254
|
dropped whole as the terminal narrows, and each footer leads with its safe exit
|
|
228
|
-
(`Esc keep
|
|
255
|
+
(`Esc keep plan` in the review, `Esc back to plan` in the inline editor) so
|
|
229
256
|
the exit survives even when only one hint fits.
|
|
230
257
|
|
|
231
|
-
Dialog-capable non-TUI modes (RPC) show the same
|
|
258
|
+
Dialog-capable non-TUI modes (RPC) show the same five choices as a select menu
|
|
232
259
|
plus the refine editor; cancelling the editor returns to the select instead of
|
|
233
|
-
being reported as "not approved". A WeChat turn takes the same dialog path and
|
|
260
|
+
being reported as "not approved". Dismissing the plan select is an exit. A WeChat turn takes the same dialog path and
|
|
234
261
|
is answered over WeChat, even though the session is in TUI mode: the choice
|
|
235
262
|
comes from `askHuman(ctx, { custom, dialogs, file, body })` in
|
|
236
263
|
`lib/interaction.ts`, not from `ctx.mode`. A remote turn also receives the plan
|
|
@@ -276,6 +303,16 @@ exit_plan_mode submitted a plan
|
|
|
276
303
|
… 12 more lines (ctrl+o to expand)
|
|
277
304
|
```
|
|
278
305
|
|
|
306
|
+
## Configuration
|
|
307
|
+
|
|
308
|
+
The `plan` section of the shared `bytefly.json` (global then project), or the
|
|
309
|
+
**Offer the session goal** row in `/bytefly settings`. It is read when a plan is
|
|
310
|
+
approved, so a change applies to the next approval without a reload.
|
|
311
|
+
|
|
312
|
+
| Key | Default | Meaning |
|
|
313
|
+
|---|---|---|
|
|
314
|
+
| `goalPrompt` | `true` | Ask before recording an approved plan's objective as the session goal; `false` records it without asking. |
|
|
315
|
+
|
|
279
316
|
## Security
|
|
280
317
|
|
|
281
318
|
Plan mode is a **guard rail, not a sandbox**: extensions run with Pi's OS
|
|
@@ -409,18 +446,28 @@ file and returns them to the model.
|
|
|
409
446
|
- **Inline threads.** A comment renders directly under the block it anchors to,
|
|
410
447
|
labelled and indented as a thread, so it reads in place and the plan keeps one
|
|
411
448
|
measure at every width. There is no separate column to reflow.
|
|
449
|
+
- **Nothing typed is lost.** **Save feedback**, **Approve & run**, and **Back to
|
|
450
|
+
Pi** refuse while a composer holds text and put the cursor back in it, and a
|
|
451
|
+
deleted comment is offered back in the header for a few seconds. The page keeps
|
|
452
|
+
no draft of its own — these are the three ways an open comment could disappear.
|
|
453
|
+
- **Prints as a document.** Printing uses the light palette whatever the screen
|
|
454
|
+
shows, drops the header, panel, rail, and progress bar, and lets the comments
|
|
455
|
+
stay with the blocks they belong to.
|
|
412
456
|
- **Keyboard first.** `j`/`k` move to the next or previous block, `c` comments
|
|
413
457
|
on the current block, `a` approves (the approval flow only), `s` saves, `Esc`
|
|
414
458
|
closes the composer or the sheet and then the review, and `?` opens a shortcut
|
|
415
|
-
sheet; the header's `?` button opens the same sheet.
|
|
459
|
+
sheet; the header's `?` button opens the same sheet. Either dialog traps
|
|
460
|
+
`Tab` and hands focus back to whatever opened it.
|
|
416
461
|
- **Decision in place.** A review opened for approval (`w` on the review screen)
|
|
417
462
|
shows **Approve & run** beside Save feedback: it persists the current comments,
|
|
418
463
|
ends plan mode, restores write access, seeds `todo`, and hands control back to
|
|
419
|
-
the model.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
464
|
+
the model. Because that cannot be undone from the page, the button and `a`
|
|
465
|
+
open a confirmation that says what happens and starts on **Cancel**; only its
|
|
466
|
+
own **Approve & run** posts. **Save feedback** keeps plan mode and returns the
|
|
467
|
+
comments as a refinement; **Back to Pi** closes the review with no decision.
|
|
468
|
+
Each action settles the page on a terminal state — `Approved`, `Saved`, or
|
|
469
|
+
`Closed` — so the tab never falls to Offline. `/plan review` is comment-only:
|
|
470
|
+
it renders no approve action and the server refuses one.
|
|
424
471
|
- **Markdown that reads.** Headings, lists, task items, blockquotes, fenced
|
|
425
472
|
code, GFM tables, `---` rules, and bare-URL autolinks render; a list item's
|
|
426
473
|
continuation lines hang under the item's text, inline code is a soft fill
|
|
@@ -441,9 +488,10 @@ file and returns them to the model.
|
|
|
441
488
|
page rendered and answers `409`; the page then offers a **Reload** action,
|
|
442
489
|
because only a reload can re-anchor the comments to the new text.
|
|
443
490
|
- **Back to the model.** A save from the review screen returns `refined: true`
|
|
444
|
-
with the comments, so the model stays in plan mode and revises the same file.
|
|
445
|
-
|
|
446
|
-
|
|
491
|
+
with the comments, so the model stays in plan mode and revises the same file. A
|
|
492
|
+
save that carries no comments asks for nothing, so the review screen comes back
|
|
493
|
+
instead. `/plan review` sends them as a user message (a plan-mode revision or a
|
|
494
|
+
plain update, depending on the mode).
|
|
447
495
|
- **Loopback and tokenized.** The server binds `127.0.0.1`, carries a random
|
|
448
496
|
per-session token in the URL, rejects a missing or foreign `Host` header,
|
|
449
497
|
serves only `GET`, `HEAD`, and `POST`, and caps the request body. A save or cancel
|
|
@@ -503,7 +551,7 @@ file and returns them to the model.
|
|
|
503
551
|
|---|---|
|
|
504
552
|
| Tools | `enter_plan_mode`, `write_plan`, `exit_plan_mode`, and `set_plan_steps` registered with `executionMode: "sequential"`. `enter_plan_mode` is active normally with `readOnlyHint: true`; `write_plan` (`readOnlyHint: false`) and `exit_plan_mode` (`readOnlyHint: true`) are `defaultActive: false`. `set_plan_steps` is active normally (`readOnlyHint: false`, `idempotentHint: true`); the read-only policy default-denies it in plan mode. Each returns typed `details` (`EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `SetPlanStepsDetails`), and `ExitPlanModeDetails` carries the review's `annotations` when a browser approval or save supplied comments; no `outputSchema`/`structuredContent`. |
|
|
505
553
|
| Commands and flags | `/plan` (toggle, or enter and send a task), `/plan list` (plans index), `/plan review [path]` (browser review), `/skill:init` (skill-backed `AGENTS.md` bootstrap), `--plan` launch flag, `Ctrl+Alt+P` shortcut. |
|
|
506
|
-
| State | Enabled flag and the tools it hid persisted with `pi.appendEntry("plan-mode", { enabled, removed })`, and each review's baseline persisted with `pi.appendEntry("plan-baseline", { path, sections })` as one digest per heading section; both rebuilt from `ctx.sessionManager.getBranch()`, so `/resume` and `/tree` restore the mode and the changed markers of the branch they land on. Plan writes a goal only at approval, through `ctx.executeTool("goal", { objective })`: a `planning` goal is promoted to the plan's `## Goal`, a session with none is offered one, and an `active`/`achieved` goal is left alone. A review that shows the same content appends nothing. The last plan path lives in runtime memory for the footer chip, and a bare `/plan review` falls back to the newest plan file in the directory. |
|
|
554
|
+
| State | Enabled flag and the tools it hid persisted with `pi.appendEntry("plan-mode", { enabled, removed })`, and each review's baseline persisted with `pi.appendEntry("plan-baseline", { path, sections })` as one digest per heading section; both rebuilt from `ctx.sessionManager.getBranch()`, so `/resume` and `/tree` restore the mode and the changed markers of the branch they land on. Plan writes a goal only at approval, through `ctx.executeTool("goal", { objective })`: a `planning` goal is promoted to the plan's `## Goal`, a session with none is offered one unless `plan.goalPrompt` is off, and an `active`/`achieved` goal is left alone. A review that shows the same content appends nothing. The last plan path lives in runtime memory for the footer chip, and a bare `/plan review` falls back to the newest plan file in the directory. |
|
|
507
555
|
| Entry requests | `pi.events` channel `PLAN_ENTER_REQUEST` from `lib/plan-entry.ts`: the `goal` workflow asks, with the shared consent sentence, and plan enables the mode on the caller's context without a second dialog. Subscribed on `session_start`, released on `session_shutdown`. |
|
|
508
556
|
| Hooks | `tool_call` blocks non-read-only calls and forces `subagent` read-only; `before_agent_start` injects the hidden `[PLAN MODE ACTIVE]` context, with a pursued session goal read from the branch through `lib/goal-state.ts`; `context` keeps only the newest plan context while enabled and drops stale ones otherwise. |
|
|
509
557
|
| Lifecycle | `session_start`/`session_tree` restore state; `session_shutdown` clears the footer status and closes any browser review server. |
|
|
@@ -577,8 +625,12 @@ file and returns them to the model.
|
|
|
577
625
|
share one normalization so a tab cannot read as a change.
|
|
578
626
|
- **The outline guides, it does not enforce.** `write_plan` documents the
|
|
579
627
|
canonical shape in its description and stores whatever markdown it is given;
|
|
580
|
-
no section is required and nothing is rewritten.
|
|
581
|
-
|
|
628
|
+
no section is required and nothing is rewritten. The description names the two
|
|
629
|
+
headings that do carry behavior — `Steps` is what approval reads as steps, and
|
|
630
|
+
`Verification` the trailing check — so the model is told the cost of dropping
|
|
631
|
+
one instead of having the plan refused. The review's baseline is keyed by
|
|
632
|
+
heading text, so a refinement is asked to keep its headings; a rename reads as
|
|
633
|
+
an edit, which is the one change the markers cannot tell from a rewrite.
|
|
582
634
|
- **Sections are rendered one at a time.** Pi's `Markdown` exposes no
|
|
583
635
|
source-line map, so `tui.ts` splits the body at its headings and renders each
|
|
584
636
|
section separately. That gives exact offsets for the current-section label and
|
|
@@ -623,9 +675,10 @@ file and returns them to the model.
|
|
|
623
675
|
|---|---|
|
|
624
676
|
| `index.ts` | Wiring: tools, command, flag, shortcut, events, context injection. |
|
|
625
677
|
| `types.ts` | `PlanModeEntry`, `EnterPlanModeDetails`, `WritePlanDetails`, `ExitPlanModeDetails`, `SetPlanStepsDetails`. |
|
|
678
|
+
| `config.ts` | The `plan` section loader: `goalPrompt`, default on. |
|
|
626
679
|
| `policy.ts` | Plan mode's read-only policy, the shared prompt summary, and the bash-refusal guidance. |
|
|
627
680
|
| `git-bash.ts` | `checkReadOnlyGit`: the pure allowlist that restricts `bash` to read-only git commands. |
|
|
628
|
-
| `plans.ts` | Plan-file store: slug/stamp naming, directory resolution, containment, atomic `write`/`setSteps`/`saveFeedback`, and `read`/`latest`. |
|
|
681
|
+
| `plans.ts` | Plan-file store: slug/stamp naming, directory resolution (repository, active worktree, or agent directory), containment, atomic `write`/`setSteps`/`saveFeedback`, and `read`/`latest`. |
|
|
629
682
|
| `todos.ts` | Approval-time todo merge (pure): the last list replayed from the branch, and the merge of the plan's steps into it with the cap respected. |
|
|
630
683
|
| `steps.ts` | Plan-document structure (pure): the outline, step extraction and the `Steps`-section scope, heading sections, the plan's own goal (`goalSection`), and the checkbox edit. |
|
|
631
684
|
| `format.ts` | Pure result/approval text: the plan preview, the step-cap diagnosis, step labels, the merge summary, and the verification note. |
|
|
@@ -641,8 +694,10 @@ file and returns them to the model.
|
|
|
641
694
|
| `feedback.ts` | Browser-comment codec: `stripFeedback`, `parseFeedback`, `formatFeedback`, `feedbackSummary`, and payload validation. |
|
|
642
695
|
| `web.ts` | `createPlanWebReview`: the review session (SSE close detection, the body cap, settle-after-flush, abort) over the shared loopback bind, writer, and close. |
|
|
643
696
|
| `web-router.ts` | Pure request handler: token/`Host`/method checks and the review routes, over the shared page and JSON header sets. |
|
|
644
|
-
| `web-page.ts` | The review page shell (header, reading progress, review facts, section outline, shortcut sheet), its data island, and inline CSS. |
|
|
645
|
-
| `web-client.ts` | The `/client.js` source
|
|
697
|
+
| `web-page.ts` | The review page shell (header, reading progress, review facts, section outline, shortcut sheet, approve confirmation), its data island, and inline CSS. |
|
|
698
|
+
| `web-client.ts` | The `/client.js` source: state and wiring (comment rail, comments, selection quotes, save/approve/back, keyboard nav) over the two fragments below. |
|
|
699
|
+
| `web-markdown.ts` | The client's document half: `safeHref` and the inline/table/code/list rendering (interpolated into `web-client.ts`). |
|
|
700
|
+
| `web-chrome.ts` | The client's chrome half: the state chip, the status line, the header, reading progress, and the stream's lifetime (interpolated into `web-client.ts`). |
|
|
646
701
|
| `path-guard.ts` | Recursive path-argument detection for the read-only backstop. |
|
|
647
702
|
| [`../../lib/browser.ts`](../../lib/browser.ts) | Cross-platform default-browser opener (shared with `file-browse`). |
|
|
648
703
|
| [`../../lib/plan-entry.ts`](../../lib/plan-entry.ts) | Plan-mode entry shared with `goal`: the read-only summary, the consent sentence, and the `pi.events` request `plan` answers by enabling the mode. |
|