@zhushanwen/pi-plan 0.3.14 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-plan",
3
- "version": "0.3.14",
3
+ "version": "0.4.0",
4
4
  "description": "Lightweight plan mode for Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -20,8 +20,8 @@
20
20
  "peerDependencies": {
21
21
  "@earendil-works/pi-coding-agent": ">=0.73.0",
22
22
  "typebox": "*",
23
- "@earendil-works/pi-ai": "^0.84.1",
24
- "@zhushanwen/pi-goal": "0.11.0"
23
+ "@earendil-works/pi-ai": "^0.84.4",
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.3.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: PlanSessionMap extends Map<string, infer V> ? V : never,
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: PlanSessionMap extends Map<string, infer V> ? V : never,
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: PlanSessionMap extends Map<string, infer V> ? V : never,
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 read-only set during plan mode
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: check subagent capability suggest goal + wave or single-agent execution.`,
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, startPlanMode } from "./command.js";
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 read-only set
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 — use the 'write' tool to write plan.md. " +
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 'write'\n" +
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 'write' tool for ALL plan content: writing plan.md, updating plan chapters.\n" +
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. write({path: planFilePath, content: '...filled template...'}) — write plan content\n" +
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, use write tool\n" +
216
- "❌ Calling plan tool when user says 'write plan to file' — use write tool\n" +
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(