@zhushanwen/pi-cw-tool 0.2.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/src/index.ts ADDED
@@ -0,0 +1,231 @@
1
+ /**
2
+ * cw-tool extension — 把 `cw` CLI 包成 4 个 role-restricted pi 工具。
3
+ *
4
+ * 背景(v4 递归编排方案 §1.4/§3):cw 命令包成 pi 自定义工具,按 role 限制可调 action。
5
+ * 层主(planning/wave)的 cw-tool 不含 design-review/exec-review → 物理上调不了审查 →
6
+ * 必须派独立 review-agent(独立 review 从 prompt 软约束变成工具白名单硬约束)。
7
+ * dev 含 execute/test;review 只含审查命令。
8
+ *
9
+ * 本文件只做工具注册(白名单 + schema + execute 适配);核心逻辑在 cw-runner.ts,
10
+ * spawn 抽象在 cw-spawn.ts。cw 引擎改动不在此处理。
11
+ */
12
+ import type {
13
+ AgentToolResult,
14
+ AgentToolUpdateCallback,
15
+ ExtensionAPI,
16
+ ExtensionContext,
17
+ ToolDefinition,
18
+ } from "@earendil-works/pi-coding-agent";
19
+ import { StringEnum } from "@earendil-works/pi-ai";
20
+ import { type Static, Type } from "typebox";
21
+
22
+ import { type CwSpawner, defaultCwSpawner } from "./cw-spawn.ts";
23
+ import { type CwDetails, type CwToolOptions, executeCwAction } from "./cw-runner.ts";
24
+
25
+ // ── action 白名单(与方案表格逐字一致)──────────────────────────
26
+
27
+ /** cw_planning:epic/feature/slice 层主。可 execute 下沉,但不含审查命令。 */
28
+ const PLANNING_ALLOWED = [
29
+ "design",
30
+ "execute",
31
+ "replan",
32
+ "retrospect",
33
+ "closeout",
34
+ "status",
35
+ "handoff",
36
+ "list",
37
+ "tree",
38
+ "frontier",
39
+ ] as const;
40
+
41
+ /** cw_wave:wave 层主。无 execute/test/design-review/exec-review(不亲自写码/测试/审查)。 */
42
+ const WAVE_ALLOWED = [
43
+ "design",
44
+ "replan",
45
+ "retrospect",
46
+ "closeout",
47
+ "status",
48
+ "handoff",
49
+ "list",
50
+ "tree",
51
+ "frontier",
52
+ ] as const;
53
+
54
+ /** cw_dev:wave 内 dev。写码 + 测试。 */
55
+ const DEV_ALLOWED = ["execute", "test", "status", "handoff"] as const;
56
+
57
+ /** cw_review:审 design/exec 结果。只含审查命令 + status(不改被审物)。 */
58
+ const REVIEW_ALLOWED = ["design-review", "exec-review", "status"] as const;
59
+
60
+ // ── 工具元信息 ──────────────────────────────────────────────────
61
+
62
+ interface ToolMeta {
63
+ readonly name: string;
64
+ readonly label: string;
65
+ readonly description: string;
66
+ readonly promptSnippet: string;
67
+ }
68
+
69
+ const PLANNING_META: ToolMeta = {
70
+ name: "cw_planning",
71
+ label: "CW Planning",
72
+ description: `运行 cw 编码流程 action(planning 层主:epic/feature/slice)。
73
+
74
+ 允许的 action:design / execute / replan / retrospect / closeout + 只读 status / handoff / list / tree / frontier。
75
+ 不含 design-review / exec-review —— 审查必须派独立 review-agent(cw_review),不可自审。
76
+
77
+ 参数:
78
+ - action:要执行的 cw action(受限于此工具白名单)。
79
+ - unitId:目标 cw unit id(大多数 action 必传)。
80
+ - input:cw action 的 JSON 输入内容(字符串),经 stdin 传给 cw。
81
+ - inputFile:输入文件路径,直接传 cw(与 input 互斥)。
82
+ - commitHash:execute 关联的 commit sha(wave 层)。
83
+
84
+ 返回 details.ok 区分成功/失败;成功时 details.data 为 cw stdout 解析出的 JSON(若可解析),details.stdout 为原始输出。`,
85
+ promptSnippet:
86
+ "用 cw_planning 推进编码流程(design/execute/replan/retrospect/closeout + 只读查询)。审查须派独立 review-agent,本工具不含审查命令。",
87
+ };
88
+
89
+ const WAVE_META: ToolMeta = {
90
+ name: "cw_wave",
91
+ label: "CW Wave",
92
+ description: `运行 cw 编码流程 action(wave 层主)。
93
+
94
+ 允许的 action:design / replan / retrospect / closeout + 只读 status / handoff / list / tree / frontier。
95
+ **不含** execute / test / design-review / exec-review —— wave 层主不亲自写码/测试/审查:写码派 dev-agent(cw_dev),审查派 review-agent(cw_review)。
96
+
97
+ 参数同 cw_planning(action / unitId / input / inputFile / commitHash)。`,
98
+ promptSnippet:
99
+ "用 cw_wave 推进 wave 层流程(design/replan/retrospect/closeout + 只读查询)。写码派 dev、审查派 review,本工具不含 execute/test/审查。",
100
+ };
101
+
102
+ const DEV_META: ToolMeta = {
103
+ name: "cw_dev",
104
+ label: "CW Dev",
105
+ description: `运行 cw 编码流程 action(wave 内 dev:写码 + 测试)。
106
+
107
+ 允许的 action:execute / test + 只读 status / handoff。
108
+ 不含审查命令 —— exec-review 必须派独立 review-agent(cw_review)。
109
+
110
+ 参数:action / unitId / input / inputFile / commitHash(execute 关联 commit)。`,
111
+ promptSnippet: "用 cw_dev 写码(execute)和测试(test)+ 只读 status/handoff。exec-review 须派独立 review-agent。",
112
+ };
113
+
114
+ const REVIEW_META: ToolMeta = {
115
+ name: "cw_review",
116
+ label: "CW Review",
117
+ description: `运行 cw 审查 action(design-review / exec-review)。
118
+
119
+ 允许的 action:design-review / exec-review + 只读 status。
120
+ 供独立 review-agent 提交审查 judgment(designReviewJudgment / execReviewJudgment 经 input 传入)。不含 execute/test/design 等 —— review 只审查,不改被审物。
121
+
122
+ 参数:action / unitId / input(审查 judgment JSON)/ inputFile。`,
123
+ promptSnippet: "用 cw_review 提交审查(design-review/exec-review)+ 只读 status。仅审查,不执行/不写码。",
124
+ };
125
+
126
+ // ── 工具构造(泛型保留 action 枚举的窄类型)────────────────────
127
+
128
+ /**
129
+ * 构造单个 cw-tool。execute 内联闭包适配 SDK 全签名(toolCallId/params/signal/onUpdate/ctx),
130
+ * 转调纯逻辑 executeCwAction(白名单 + spawn + 解析)。
131
+ *
132
+ * 错误经 execute 返回(details.ok=false + content 文本),不抛异常。
133
+ */
134
+ function buildTool<A extends readonly string[]>(
135
+ allowed: A,
136
+ meta: ToolMeta,
137
+ spawner: CwSpawner,
138
+ ): ToolDefinition<typeof parameters, CwDetails> {
139
+ const parameters = Type.Object({
140
+ action: StringEnum(allowed, { description: "要执行的 cw action(受限于此工具的白名单)。" }),
141
+ unitId: Type.Optional(
142
+ Type.String({
143
+ description:
144
+ "目标 cw unit id。写 action(design/execute/replan/retrospect/closeout/test/design-review/exec-review)必传,缺失返回错误;只读 action(list/tree/frontier/status/handoff)可省略。",
145
+ }),
146
+ ),
147
+ input: Type.Optional(
148
+ Type.String({
149
+ description: "cw action 的 JSON 输入内容(字符串),经 stdin 传给 cw(cw --input -)。与 inputFile 互斥。",
150
+ }),
151
+ ),
152
+ inputFile: Type.Optional(
153
+ Type.String({ description: "输入文件路径,直接传 cw --input <path>。与 input 互斥。" }),
154
+ ),
155
+ commitHash: Type.Optional(
156
+ Type.String({ description: "execute 关联的 commit sha(wave 层),传 cw --commitHash。" }),
157
+ ),
158
+ });
159
+ type Params = Static<typeof parameters>;
160
+
161
+ return {
162
+ name: meta.name,
163
+ label: meta.label,
164
+ description: meta.description,
165
+ promptSnippet: meta.promptSnippet,
166
+ parameters,
167
+ executionMode: "sequential",
168
+ async execute(
169
+ _toolCallId: string,
170
+ params: Params,
171
+ signal: AbortSignal | undefined,
172
+ _onUpdate: AgentToolUpdateCallback<CwDetails> | undefined,
173
+ ctx: ExtensionContext,
174
+ ): Promise<AgentToolResult<CwDetails>> {
175
+ if (signal?.aborted) {
176
+ const aborted: CwDetails = {
177
+ ok: false,
178
+ action: params.action,
179
+ unitId: params.unitId,
180
+ error: "aborted by signal",
181
+ };
182
+ return { content: [{ type: "text", text: `[${meta.name}] aborted` }], details: aborted };
183
+ }
184
+
185
+ const opts: CwToolOptions = {
186
+ input: params.input,
187
+ inputFile: params.inputFile,
188
+ commitHash: params.commitHash,
189
+ };
190
+
191
+ const details = await executeCwAction(
192
+ params.action,
193
+ allowed,
194
+ meta.name,
195
+ params.unitId,
196
+ opts,
197
+ spawner,
198
+ ctx.cwd,
199
+ signal,
200
+ );
201
+
202
+ const text = details.ok
203
+ ? details.stdout
204
+ : `[${meta.name}] ${params.action} 失败: ${details.error}`;
205
+ return { content: [{ type: "text", text }], details };
206
+ },
207
+ };
208
+ }
209
+
210
+ // ── 扩展入口 ────────────────────────────────────────────────────
211
+
212
+ /**
213
+ * cw-tool extension 工厂。注册 4 个 role-restricted cw 工具。
214
+ *
215
+ * 无状态(cw 状态在 cw 引擎内,本扩展不持有 session 级状态),无需 session_start 重建。
216
+ */
217
+ export default function cwToolExtension(pi: ExtensionAPI): void {
218
+ pi.registerTool(buildTool(PLANNING_ALLOWED, PLANNING_META, defaultCwSpawner));
219
+ pi.registerTool(buildTool(WAVE_ALLOWED, WAVE_META, defaultCwSpawner));
220
+ pi.registerTool(buildTool(DEV_ALLOWED, DEV_META, defaultCwSpawner));
221
+ pi.registerTool(buildTool(REVIEW_ALLOWED, REVIEW_META, defaultCwSpawner));
222
+ }
223
+
224
+ // ── 导出(供测试 + 跨扩展引用)──────────────────────────────────
225
+
226
+ export { executeCwAction, buildCwArgs, rejectDisallowedAction } from "./cw-runner.ts";
227
+ export type { CwAction, CwDetails, CwToolOptions } from "./cw-runner.ts";
228
+ export type { CwSpawner, CwSpawnResult } from "./cw-spawn.ts";
229
+ export { defaultCwSpawner } from "./cw-spawn.ts";
230
+ export { buildTool };
231
+ export { PLANNING_ALLOWED, WAVE_ALLOWED, DEV_ALLOWED, REVIEW_ALLOWED };