@zhushanwen/pi-plan 0.4.3 → 0.4.5

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 ADDED
@@ -0,0 +1,37 @@
1
+ # @zhushanwen/pi-plan
2
+
3
+ 轻量级规划模式 pi extension:`/plan [描述]` 触发,产出结构化 plan 文件。与 coding-workflow 的区别:无 gate / review / retrospect,只负责「想清楚再动手」的计划产出(ADR pi-ext-021 prompt-only readonly / pi-ext-022 session-manager state)。
4
+
5
+ > 本 README 的核心概念词条(Plan Mode / Plan File / Brainstorming)迁自 `docs/extensions/glossary.md`「Extension 专属概念」(2026-09-13)。
6
+
7
+ ## 用法
8
+
9
+ ```
10
+ /plan 修复项目中所有失败的测试 # 进入 plan mode(brainstorming 阶段开始)
11
+ /plan status # 查看当前阶段与 plan 文件路径
12
+ /plan abort # 取消活跃的 plan mode
13
+ ```
14
+
15
+ ## 阶段状态机
16
+
17
+ `idle → brainstorming → writing → complete`(`state.ts` PlanPhase):
18
+
19
+ - **brainstorming**:需求探索。固定四步——1. Quick Overview(ls 项目根 / 读 README / package.json,30 秒内建立上下文)→ 2. 渐进式提问澄清需求 → 3. 方案探索 → 4. 假设审计。
20
+ - **writing**:选定模板后撰写 plan 文件。
21
+ - **complete**:完成时提示选择执行方式,恢复工具,状态重置。
22
+
23
+ ## plan 工具
24
+
25
+ 模型经 `plan` tool 驱动阶段流转,actions:`list-template` / `select-template` / `create-template` / `complete` / `complete-cancelled` / `abort`。
26
+
27
+ ## Plan File
28
+
29
+ Plan Mode 的产出物,存储在 `.xyz-harness/{slug}/plan.md`(slug 截断 30 字符)。含 YAML frontmatter 与模板章节。进入 plan mode 时自动扫描既有 plan 文件供续写。
30
+
31
+ ## 模板
32
+
33
+ 内置 5 个(`templates/`):`feature-plan` / `bugfix-plan` / `refactor-plan` / `implementation-plan` / `research-plan`。支持项目自定义模板(`listTemplates` / `loadTemplate` 按 projectDir 发现)。
34
+
35
+ ## 依赖
36
+
37
+ peer 依赖 `@zhushanwen/pi-goal`(plan 完成后衔接 goal 驱动执行)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-plan",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
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.13.0"
24
+ "@zhushanwen/pi-goal": "0.14.1"
25
25
  },
26
26
  "peerDependenciesMeta": {
27
27
  "@earendil-works/pi-ai": {
@@ -38,7 +38,8 @@
38
38
  "templates/"
39
39
  ],
40
40
  "dependencies": {
41
- "@zhushanwen/pi-extension-logger": "0.4.1"
41
+ "@zhushanwen/pi-ext-guards": "0.3.0",
42
+ "@zhushanwen/pi-extension-logger": "0.6.0"
42
43
  },
43
44
  "scripts": {
44
45
  "typecheck": "npx tsc --noEmit",
package/src/compact.ts CHANGED
@@ -2,11 +2,15 @@ import * as fs from "node:fs";
2
2
  import { basename } from "node:path";
3
3
 
4
4
  import type { ExtensionAPI, ExtensionContext, SessionBeforeCompactEvent, SessionBeforeTreeEvent } from "@earendil-works/pi-coding-agent";
5
+ import { guardStaleCtx, toErrorMessage } from "@zhushanwen/pi-ext-guards";
5
6
  import type { GoalInitFn } from "@zhushanwen/pi-goal";
7
+ import { getLogger } from "@zhushanwen/pi-extension-logger";
6
8
 
7
9
  import type { PlanSessionMap, PlanState } from "./state.js";
8
10
  import { getPlanState } from "./state.js";
9
11
 
12
+ const logger = getLogger("pi-plan");
13
+
10
14
  export function registerPlanEventHandlers(
11
15
  pi: ExtensionAPI,
12
16
  sessions: PlanSessionMap,
@@ -217,14 +221,30 @@ export function handlePlanComplete(
217
221
  case "compact": {
218
222
  ctx.compact({
219
223
  customInstructions: `Plan file: ${planFilePath}. Read plan and execute implementation.`,
224
+ // E1 同构崩溃点(crash-resilience D1):onComplete/onError 由 compact 内部
225
+ // Promise 链异步调用、不在 pi runner emit() 的 try/catch 内——压缩进行中用户
226
+ // 切换/重载 session 后,回调触碰捕获的 pi/ctx 命中 stale 同步抛错即杀 pi 进程。
227
+ // 守卫 stale 静默降级(执行消息不投递,用户可手动 Read plan 文件执行),非
228
+ // stale 错误原样上抛。plan 未注入代际计数器(低频路径),分诊依赖 PS-30 门禁
229
+ // 守卫的 stale 文案兜底(D1 降级语义声明的合法形态)。
220
230
  onComplete: () => {
221
- pi.sendUserMessage(executeMessage, { deliverAs: "steer" });
222
- tryGoalInit(pi, planFilePath, ctx);
231
+ guardStaleCtx(() => {
232
+ pi.sendUserMessage(executeMessage, { deliverAs: "steer" });
233
+ tryGoalInit(pi, planFilePath, ctx);
234
+ }, {
235
+ label: "plan:compact-onComplete",
236
+ onStale: (error) => logger.warn("plan execution notice delivery skipped (stale ctx)", { error: toErrorMessage(error) }),
237
+ });
223
238
  },
224
239
  onError: (_error: Error) => {
225
- ctx.ui.notify("Compact failed, continuing without isolation.", "warning");
226
- pi.sendUserMessage(executeMessage, { deliverAs: "steer" });
227
- tryGoalInit(pi, planFilePath, ctx);
240
+ guardStaleCtx(() => {
241
+ ctx.ui.notify("Compact failed, continuing without isolation.", "warning");
242
+ pi.sendUserMessage(executeMessage, { deliverAs: "steer" });
243
+ tryGoalInit(pi, planFilePath, ctx);
244
+ }, {
245
+ label: "plan:compact-onError",
246
+ onStale: (error) => logger.warn("plan execution notice delivery skipped (stale ctx)", { error: toErrorMessage(error) }),
247
+ });
228
248
  },
229
249
  });
230
250
  break;