@zhushanwen/pi-plan 0.3.15 → 0.4.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/package.json +3 -3
- package/src/command.ts +6 -27
- package/src/index.ts +2 -9
- package/src/tool.ts +6 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-plan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Lightweight plan mode for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@earendil-works/pi-coding-agent": ">=0.73.0",
|
|
22
22
|
"typebox": "*",
|
|
23
23
|
"@earendil-works/pi-ai": "^0.84.4",
|
|
24
|
-
"@zhushanwen/pi-goal": "0.
|
|
24
|
+
"@zhushanwen/pi-goal": "0.12.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependenciesMeta": {
|
|
27
27
|
"@earendil-works/pi-ai": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"templates/"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@zhushanwen/pi-extension-logger": "0.
|
|
41
|
+
"@zhushanwen/pi-extension-logger": "0.4.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"typecheck": "npx tsc --noEmit",
|
package/src/command.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
|
|
6
|
-
import type { PlanSessionMap } from "./state.js";
|
|
6
|
+
import type { PlanSessionMap, PlanState } from "./state.js";
|
|
7
7
|
import { getPlanState, persistPlanState, resetPlanState } from "./state.js";
|
|
8
8
|
import { updatePlanWidget } from "./widget.js";
|
|
9
9
|
|
|
@@ -88,7 +88,7 @@ async function handleAbort(
|
|
|
88
88
|
sessions: PlanSessionMap,
|
|
89
89
|
sessionId: string,
|
|
90
90
|
ctx: ExtensionContext,
|
|
91
|
-
state:
|
|
91
|
+
state: PlanState,
|
|
92
92
|
): Promise<void> {
|
|
93
93
|
if (!state.isActive) {
|
|
94
94
|
ctx.ui.notify("No active plan mode.", "info");
|
|
@@ -104,7 +104,7 @@ async function handleAbort(
|
|
|
104
104
|
/** Handle /plan status subcommand */
|
|
105
105
|
function handleStatus(
|
|
106
106
|
ctx: ExtensionContext,
|
|
107
|
-
state:
|
|
107
|
+
state: PlanState,
|
|
108
108
|
): void {
|
|
109
109
|
if (!state.isActive) {
|
|
110
110
|
ctx.ui.notify("No active plan mode.", "info");
|
|
@@ -136,7 +136,7 @@ function handleEnterPlanMode(
|
|
|
136
136
|
sessions: PlanSessionMap,
|
|
137
137
|
sessionId: string,
|
|
138
138
|
ctx: ExtensionContext,
|
|
139
|
-
state:
|
|
139
|
+
state: PlanState,
|
|
140
140
|
requirement: string,
|
|
141
141
|
): void {
|
|
142
142
|
const slug = requirement
|
|
@@ -157,7 +157,7 @@ function handleEnterPlanMode(
|
|
|
157
157
|
persistPlanState(pi, state);
|
|
158
158
|
updatePlanWidget(ctx, state);
|
|
159
159
|
|
|
160
|
-
// Restrict tools to
|
|
160
|
+
// Restrict tools to the plan-mode set (includes bash — file-write constraints come from the injected plan mode prompt below)
|
|
161
161
|
pi.setActiveTools(["read", "bash", "grep", "find", "ls", "plan"]);
|
|
162
162
|
|
|
163
163
|
// Inject plan mode system prompt inline
|
|
@@ -183,27 +183,6 @@ function handleEnterPlanMode(
|
|
|
183
183
|
`## Phase D: Completion\n` +
|
|
184
184
|
`1. Ask user to review the complete plan.\n` +
|
|
185
185
|
`2. Call plan tool (complete) with isolation method (compact/tree/direct).\n` +
|
|
186
|
-
`3. After plan complete:
|
|
186
|
+
`3. After plan complete: the user picks an execution path (subagent-driven / goal-driven / single-agent) via the completion dialog.`,
|
|
187
187
|
);
|
|
188
188
|
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Programmatic entry to start plan mode (for `pi.__planStart`).
|
|
192
|
-
*
|
|
193
|
-
* Mirrors the `/plan` command's enter logic. Returns false if plan mode is
|
|
194
|
-
* already active (caller can then surface a message or wait).
|
|
195
|
-
*
|
|
196
|
-
* @returns true if plan mode started; false if already active
|
|
197
|
-
*/
|
|
198
|
-
export function startPlanMode(
|
|
199
|
-
pi: ExtensionAPI,
|
|
200
|
-
sessions: PlanSessionMap,
|
|
201
|
-
ctx: ExtensionContext,
|
|
202
|
-
requirement: string,
|
|
203
|
-
): boolean {
|
|
204
|
-
const sessionId = ctx.sessionManager.getSessionId();
|
|
205
|
-
const state = getPlanState(sessions, sessionId, ctx);
|
|
206
|
-
if (state.isActive) return false;
|
|
207
|
-
handleEnterPlanMode(pi, sessions, sessionId, ctx, state, requirement);
|
|
208
|
-
return true;
|
|
209
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { getLogger } from "@zhushanwen/pi-extension-logger";
|
|
3
3
|
|
|
4
|
-
import { registerPlanCommand
|
|
4
|
+
import { registerPlanCommand } from "./command.js";
|
|
5
5
|
import { type PlanSessionMap, reconstructPlanState } from "./state.js";
|
|
6
6
|
import { registerPlanTool } from "./tool.js";
|
|
7
7
|
import { updatePlanWidget } from "./widget.js";
|
|
@@ -16,13 +16,6 @@ export default function planExtension(pi: ExtensionAPI) {
|
|
|
16
16
|
registerPlanTool(pi, sessions);
|
|
17
17
|
registerPlanCommand(pi, sessions);
|
|
18
18
|
|
|
19
|
-
// External API: __planStart(allow other extensions to start plan mode programmatically, #9)
|
|
20
|
-
// 交叉类型单步断言(ExtensionAPI 可赋给 ExtensionAPI & { __planStart? },无需 unknown 中转)
|
|
21
|
-
const api = pi as ExtensionAPI & { __planStart?: (requirement: string, ctx: ExtensionContext) => boolean };
|
|
22
|
-
api.__planStart = (requirement: string, ctx: ExtensionContext): boolean => {
|
|
23
|
-
return startPlanMode(pi, sessions, ctx, requirement);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
19
|
// Dynamic import compact handlers — avoids cross-group static import
|
|
27
20
|
import("./compact.js").then(({ registerPlanEventHandlers }) => {
|
|
28
21
|
registerPlanEventHandlers(pi, sessions);
|
|
@@ -36,7 +29,7 @@ export default function planExtension(pi: ExtensionAPI) {
|
|
|
36
29
|
const state = reconstructPlanState(ctx);
|
|
37
30
|
sessions.set(sessionId, state);
|
|
38
31
|
updatePlanWidget(ctx, state);
|
|
39
|
-
// If plan mode was active, re-restrict tools to
|
|
32
|
+
// If plan mode was active, re-restrict tools to the plan-mode set (includes bash — file-write constraints come from the injected plan mode prompt)
|
|
40
33
|
if (state.isActive) {
|
|
41
34
|
pi.setActiveTools(["read", "bash", "grep", "find", "ls", "plan"]);
|
|
42
35
|
}
|
package/src/tool.ts
CHANGED
|
@@ -182,7 +182,7 @@ export function registerPlanTool(
|
|
|
182
182
|
label: "Plan Mode",
|
|
183
183
|
description:
|
|
184
184
|
"Manages plan mode lifecycle (template selection, state transitions, completion). " +
|
|
185
|
-
"NOT for writing plan content —
|
|
185
|
+
"NOT for writing plan content — write plan.md via the bash tool (e.g. cat heredoc). " +
|
|
186
186
|
"Actions: list-template, select-template, create-template, complete, abort.",
|
|
187
187
|
parameters: Type.Object({
|
|
188
188
|
action: StringEnum(PLAN_ACTIONS, { description: "Action to perform" }),
|
|
@@ -195,25 +195,25 @@ export function registerPlanTool(
|
|
|
195
195
|
),
|
|
196
196
|
}),
|
|
197
197
|
promptSnippet:
|
|
198
|
-
"## When to use this tool vs
|
|
198
|
+
"## When to use this tool vs the bash tool\n" +
|
|
199
199
|
"Use 'plan' tool ONLY for plan mode state management:\n" +
|
|
200
200
|
"- list-template / select-template / create-template — template operations\n" +
|
|
201
201
|
"- complete — user approved plan, exit plan mode\n" +
|
|
202
202
|
"- abort — cancel plan mode\n" +
|
|
203
203
|
"\n" +
|
|
204
|
-
"Use
|
|
204
|
+
"Use the bash tool for ALL plan content: writing plan.md, updating plan chapters (e.g. cat heredoc).\n" +
|
|
205
205
|
"\n" +
|
|
206
206
|
"## End-to-end workflow example\n" +
|
|
207
207
|
"1. /plan 'add dark mode' — user enters plan mode\n" +
|
|
208
208
|
"2. AI explores codebase (read, grep, bash) — brainstorming\n" +
|
|
209
209
|
"3. plan(action='list-template') — show available templates\n" +
|
|
210
210
|
"4. User picks template → plan(action='select-template', templateName='feature-plan')\n" +
|
|
211
|
-
"5.
|
|
211
|
+
"5. bash: cat > \"$PLAN_FILE\" <<'EOF' ... EOF — write plan content\n" +
|
|
212
212
|
"6. User reviews → plan(action='complete', isolation='compact') — exit plan mode\n" +
|
|
213
213
|
"\n" +
|
|
214
214
|
"## Common mistakes\n" +
|
|
215
|
-
"❌ plan(action='complete') to 'write the plan' — WRONG,
|
|
216
|
-
"❌ Calling plan tool when user says 'write plan to file' — use
|
|
215
|
+
"❌ plan(action='complete') to 'write the plan' — WRONG, write plan.md via the bash tool\n" +
|
|
216
|
+
"❌ Calling plan tool when user says 'write plan to file' — use the bash tool\n" +
|
|
217
217
|
"✅ plan(action='list-template') to discover templates\n" +
|
|
218
218
|
"✅ plan(action='complete') AFTER plan.md is written AND user approves",
|
|
219
219
|
renderResult(
|