@zhushanwen/pi-plan 0.4.6 → 0.4.8
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 +1 -3
- package/package.json +4 -4
- package/src/__tests__/command.test.ts +4 -4
- package/src/__tests__/state.test.ts +7 -7
- package/src/command.ts +4 -4
- package/src/tool.ts +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
轻量级规划模式 pi extension:`/plan [描述]` 触发,产出结构化 plan 文件。与 coding-workflow 的区别:无 gate / review / retrospect,只负责「想清楚再动手」的计划产出(ADR pi-ext-021 prompt-only readonly / pi-ext-022 session-manager state)。
|
|
4
4
|
|
|
5
|
-
> 本 README 的核心概念词条(Plan Mode / Plan File / Brainstorming)迁自 `docs/extensions/glossary.md`「Extension 专属概念」(2026-09-13)。
|
|
6
|
-
|
|
7
5
|
## 用法
|
|
8
6
|
|
|
9
7
|
```
|
|
@@ -26,7 +24,7 @@
|
|
|
26
24
|
|
|
27
25
|
## Plan File
|
|
28
26
|
|
|
29
|
-
Plan Mode 的产出物,存储在 `.
|
|
27
|
+
Plan Mode 的产出物,存储在 `.taiji-harness/{slug}/plan.md`(slug 截断 30 字符)。含 YAML frontmatter 与模板章节。进入 plan mode 时自动扫描既有 plan 文件供续写。
|
|
30
28
|
|
|
31
29
|
## 模板
|
|
32
30
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-plan",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "Lightweight plan mode for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
7
|
-
"
|
|
7
|
+
"taiji": {
|
|
8
8
|
"role": "universal"
|
|
9
9
|
},
|
|
10
10
|
"pi": {
|
|
@@ -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.14.
|
|
24
|
+
"@zhushanwen/pi-goal": "0.14.4"
|
|
25
25
|
},
|
|
26
26
|
"peerDependenciesMeta": {
|
|
27
27
|
"@earendil-works/pi-ai": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"templates/"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@
|
|
44
|
+
"@zhushanwen/extension-protocol": "0.11.0",
|
|
45
45
|
"@zhushanwen/pi-ext-guards": "0.4.0",
|
|
46
46
|
"@zhushanwen/pi-extension-logger": "0.6.0"
|
|
47
47
|
},
|
|
@@ -122,7 +122,7 @@ describe("registerPlanCommand", () => {
|
|
|
122
122
|
await handler("Implement User Auth", ctx);
|
|
123
123
|
|
|
124
124
|
expect(fs.mkdirSync).toHaveBeenCalledWith(
|
|
125
|
-
"/tmp/test-project/.
|
|
125
|
+
"/tmp/test-project/.taiji-harness/implement-user-auth",
|
|
126
126
|
{ recursive: true },
|
|
127
127
|
);
|
|
128
128
|
expect(pi.setActiveTools).toHaveBeenCalledWith(["read", "bash", "grep", "find", "ls", "plan"]);
|
|
@@ -130,7 +130,7 @@ describe("registerPlanCommand", () => {
|
|
|
130
130
|
expect(pi.sendUserMessage).toHaveBeenCalledWith(expect.stringContaining("Implement User Auth"));
|
|
131
131
|
expect(pi.appendEntry).toHaveBeenCalledWith("plan-state", {
|
|
132
132
|
isActive: true,
|
|
133
|
-
planFilePath: "/tmp/test-project/.
|
|
133
|
+
planFilePath: "/tmp/test-project/.taiji-harness/implement-user-auth/plan.md",
|
|
134
134
|
requirement: "Implement User Auth",
|
|
135
135
|
templateName: "",
|
|
136
136
|
});
|
|
@@ -140,7 +140,7 @@ describe("registerPlanCommand", () => {
|
|
|
140
140
|
await handler("Fix bug #123: 中文标题!", ctx);
|
|
141
141
|
|
|
142
142
|
expect(fs.mkdirSync).toHaveBeenCalledWith(
|
|
143
|
-
expect.stringContaining("/.
|
|
143
|
+
expect.stringContaining("/.taiji-harness/fix-bug-123"),
|
|
144
144
|
{ recursive: true },
|
|
145
145
|
);
|
|
146
146
|
});
|
|
@@ -150,7 +150,7 @@ describe("registerPlanCommand", () => {
|
|
|
150
150
|
await handler("", ctx);
|
|
151
151
|
|
|
152
152
|
expect(fs.mkdirSync).toHaveBeenCalledWith(
|
|
153
|
-
"/tmp/test-project/.
|
|
153
|
+
"/tmp/test-project/.taiji-harness/untitled",
|
|
154
154
|
{ recursive: true },
|
|
155
155
|
);
|
|
156
156
|
});
|
|
@@ -39,7 +39,7 @@ describe("PlanState", () => {
|
|
|
39
39
|
{
|
|
40
40
|
type: "custom",
|
|
41
41
|
customType: "plan-state",
|
|
42
|
-
data: { isActive: true, phase: "writing", planFilePath: ".
|
|
42
|
+
data: { isActive: true, phase: "writing", planFilePath: ".taiji-harness/test/plan.md", requirement: "test", templateName: "feature-plan" },
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
45
|
},
|
|
@@ -56,7 +56,7 @@ describe("State persistence", () => {
|
|
|
56
56
|
const mockPi = { appendEntry: vi.fn() } as unknown as ExtensionAPI;
|
|
57
57
|
const state: PlanState = {
|
|
58
58
|
isActive: true,
|
|
59
|
-
planFilePath: ".
|
|
59
|
+
planFilePath: ".taiji-harness/test/plan.md",
|
|
60
60
|
requirement: "test requirement",
|
|
61
61
|
templateName: "feature-plan",
|
|
62
62
|
};
|
|
@@ -66,7 +66,7 @@ describe("State persistence", () => {
|
|
|
66
66
|
// 精确匹配:新写的 plan-state entry 无 phase 字段(V5② 守卫)
|
|
67
67
|
expect(mockPi.appendEntry).toHaveBeenCalledWith("plan-state", {
|
|
68
68
|
isActive: true,
|
|
69
|
-
planFilePath: ".
|
|
69
|
+
planFilePath: ".taiji-harness/test/plan.md",
|
|
70
70
|
requirement: "test requirement",
|
|
71
71
|
templateName: "feature-plan",
|
|
72
72
|
});
|
|
@@ -91,7 +91,7 @@ describe("State persistence", () => {
|
|
|
91
91
|
data: {
|
|
92
92
|
isActive: true,
|
|
93
93
|
phase: "writing",
|
|
94
|
-
planFilePath: ".
|
|
94
|
+
planFilePath: ".taiji-harness/test/plan.md",
|
|
95
95
|
requirement: "test",
|
|
96
96
|
templateName: "feature-plan",
|
|
97
97
|
},
|
|
@@ -102,7 +102,7 @@ describe("State persistence", () => {
|
|
|
102
102
|
|
|
103
103
|
const state = reconstructPlanState(mockCtx);
|
|
104
104
|
expect(state.isActive).toBe(true);
|
|
105
|
-
expect(state.planFilePath).toBe(".
|
|
105
|
+
expect(state.planFilePath).toBe(".taiji-harness/test/plan.md");
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
it("reconstructPlanState ignores the legacy phase field in old entries (D6 兼容读 — V5②)", () => {
|
|
@@ -116,7 +116,7 @@ describe("State persistence", () => {
|
|
|
116
116
|
data: {
|
|
117
117
|
isActive: true,
|
|
118
118
|
phase: "brainstorming",
|
|
119
|
-
planFilePath: ".
|
|
119
|
+
planFilePath: ".taiji-harness/legacy/plan.md",
|
|
120
120
|
requirement: "legacy",
|
|
121
121
|
templateName: "feature-plan",
|
|
122
122
|
},
|
|
@@ -128,7 +128,7 @@ describe("State persistence", () => {
|
|
|
128
128
|
const state = reconstructPlanState(mockCtx);
|
|
129
129
|
expect(Object.keys(state).sort()).toEqual(["isActive", "planFilePath", "requirement", "templateName"]);
|
|
130
130
|
expect(state.isActive).toBe(true);
|
|
131
|
-
expect(state.planFilePath).toBe(".
|
|
131
|
+
expect(state.planFilePath).toBe(".taiji-harness/legacy/plan.md");
|
|
132
132
|
expect(state.requirement).toBe("legacy");
|
|
133
133
|
expect(state.templateName).toBe("feature-plan");
|
|
134
134
|
});
|
package/src/command.ts
CHANGED
|
@@ -58,10 +58,10 @@ export function registerPlanCommand(
|
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// Reentry: check for existing plan files in .
|
|
61
|
+
// Reentry: check for existing plan files in .taiji-harness/
|
|
62
62
|
if (!state.isActive && !trimmed) {
|
|
63
63
|
const projectDir = ctx.cwd;
|
|
64
|
-
const harnessDir = path.join(projectDir, ".
|
|
64
|
+
const harnessDir = path.join(projectDir, ".taiji-harness");
|
|
65
65
|
const existingPlans = findExistingPlans(harnessDir);
|
|
66
66
|
if (existingPlans.length > 0) {
|
|
67
67
|
pi.sendUserMessage(
|
|
@@ -116,7 +116,7 @@ function handleStatus(
|
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
/** Find existing plan.md files in .
|
|
119
|
+
/** Find existing plan.md files in .taiji-harness/ subdirectories */
|
|
120
120
|
function findExistingPlans(harnessDir: string): string[] {
|
|
121
121
|
try {
|
|
122
122
|
return fs.readdirSync(harnessDir)
|
|
@@ -144,7 +144,7 @@ function handleEnterPlanMode(
|
|
|
144
144
|
: "untitled";
|
|
145
145
|
|
|
146
146
|
const projectDir = ctx.cwd;
|
|
147
|
-
const planDir = path.join(projectDir, ".
|
|
147
|
+
const planDir = path.join(projectDir, ".taiji-harness", slug);
|
|
148
148
|
fs.mkdirSync(planDir, { recursive: true });
|
|
149
149
|
const planFilePath = path.join(planDir, "plan.md");
|
|
150
150
|
|
package/src/tool.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { ExtensionAPI, ExtensionContext, Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { Text } from "@earendil-works/pi-tui";
|
|
4
|
-
import { firstContentText } from "@
|
|
4
|
+
import { firstContentText } from "@zhushanwen/extension-protocol";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
|
|
7
7
|
import { detectGoalCapability, GOAL_FAILURE_RECOVERY, handlePlanComplete } from "./compact.js";
|