kcode-pi 0.1.24 → 0.1.30
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 +3 -0
- package/docs/CHANGELOG.md +82 -0
- package/docs/COMMAND_REFERENCE.md +55 -0
- package/docs/HARNESS_WORKFLOW.md +33 -0
- package/extensions/kingdee-harness.ts +127 -5
- package/extensions/kingdee-header.ts +14 -1
- package/extensions/kingdee-subagents.ts +430 -0
- package/extensions/kingdee-tools.ts +19 -5
- package/package.json +2 -1
- package/prompts/kd-verify.md +1 -1
- package/skills/kd-verify/SKILL.md +2 -2
- package/src/harness/artifacts.ts +11 -3
- package/src/harness/delegation.ts +297 -0
- package/src/harness/evidence.ts +16 -5
- package/src/harness/gates.ts +10 -4
- package/src/harness/prompt.ts +13 -1
- package/src/harness/repair.ts +224 -0
- package/src/harness/state.ts +97 -11
- package/src/harness/types.ts +10 -0
package/README.md
CHANGED
|
@@ -108,6 +108,8 @@ enterprise 金蝶企业版 / C#
|
|
|
108
108
|
/kd-advance [阶段]
|
|
109
109
|
/kd-artifact [阶段] [内容] [--replace]
|
|
110
110
|
/kd-answer Q-001 <答案>
|
|
111
|
+
/kd-review [审查重点]
|
|
112
|
+
/kd-delegate <research|doc|code|review|verify> <任务> [--dry-run]
|
|
111
113
|
```
|
|
112
114
|
|
|
113
115
|
完整说明见 [命令参考](docs/COMMAND_REFERENCE.md)。
|
|
@@ -129,6 +131,7 @@ kd_sdk_signature 从当前项目实际 SDK jar/dll 中读取类和方法签
|
|
|
129
131
|
kd_ksql_lint 运行 KSQL/SQL lint
|
|
130
132
|
kd_build 按产品画像执行或 dry-run 构建
|
|
131
133
|
kd_debug 分析金蝶日志和堆栈
|
|
134
|
+
kd_subagent 将调研、文档、代码、验证或交叉审查委派给隔离子 agent
|
|
132
135
|
```
|
|
133
136
|
|
|
134
137
|
工具细节和使用顺序见 [Harness 工作流](docs/HARNESS_WORKFLOW.md)。
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,88 @@
|
|
|
6
6
|
|
|
7
7
|
- 暂无。
|
|
8
8
|
|
|
9
|
+
## 0.1.30 - 2026-06-07
|
|
10
|
+
|
|
11
|
+
### 新增
|
|
12
|
+
|
|
13
|
+
- 新增 `kd_subagent` 工具,用隔离 Pi 子进程执行调研、文档、代码、验证和交叉审查任务。
|
|
14
|
+
- 新增 `/kd-review` 命令,用只读子 agent 执行交叉自查。
|
|
15
|
+
- 新增 `/kd-delegate` 命令,支持按 `research/doc/code/review/verify` 角色委派任务,并可用 `--dry-run` 预览上下文包。
|
|
16
|
+
- `kd_subagent` 支持单任务、只读角色并行 `tasks` 和链式 `chain` 三种模式。
|
|
17
|
+
|
|
18
|
+
### 改进
|
|
19
|
+
|
|
20
|
+
- 子 agent 上下文由 `src/harness/delegation.ts` 集中生成,明确写入边界、主状态机边界和禁止递归委派。
|
|
21
|
+
- 工作流 prompt 增加自动委派策略:复杂调研、交叉审查和可并行拆分任务可主动调用 `kd_subagent`,但不自动推进阶段。
|
|
22
|
+
- 子 agent 进程使用角色环境标记和工具白名单;child 环境不注册 `kd_subagent`,避免递归委派。
|
|
23
|
+
- `research/review/verify` 为只读角色;`doc` 只能写 README、docs/ 和当前 run 阶段文档;`code` 只能在 `execute` 阶段写产品源码并继续受 PLAN/TDD/SDK 门禁约束。
|
|
24
|
+
|
|
25
|
+
### 验证
|
|
26
|
+
|
|
27
|
+
- `npm run smoke:harness` 覆盖委派参数解析、上下文包关键约束和写入边界预览。
|
|
28
|
+
|
|
29
|
+
## 0.1.29 - 2026-06-07
|
|
30
|
+
|
|
31
|
+
### 修复
|
|
32
|
+
|
|
33
|
+
- 修复自动修复循环达到上限后,即使后续验证通过也会被遗留阻断问题继续卡住的问题。
|
|
34
|
+
- 强化 `kd_verify_result` 输入容错,避免坏 payload 触发类似 `undefined.replace/trim` 的崩溃。
|
|
35
|
+
- 限制验证结果只能在 `verify` 阶段或自动修复中的 `execute` 阶段记录,避免非验证阶段污染 `VERIFY.md` 和 evidence。
|
|
36
|
+
- 收敛 `/kd-verify` 与 `kd-verify` skill 的验证结果入口,要求通过 `kd_verify_result` 进入同一修复闭环。
|
|
37
|
+
|
|
38
|
+
### 验证
|
|
39
|
+
|
|
40
|
+
- `npm run smoke:harness` 覆盖 repair 阻断问题关闭、坏验证 payload 容错和非法阶段拒绝。
|
|
41
|
+
|
|
42
|
+
## 0.1.28 - 2026-06-07
|
|
43
|
+
|
|
44
|
+
### 新增
|
|
45
|
+
|
|
46
|
+
- 新增 `kd_verify_result` 工具和 `/kd-verify-result` 命令,用于记录验证命令结果。
|
|
47
|
+
- 验证失败时自动写入 `evidence/verify-failure-###.md`,切回 `execute` 阶段并注入修复上下文。
|
|
48
|
+
- 验证通过时写入 `evidence/verify-pass.md`,重置修复状态并继续尝试推进。
|
|
49
|
+
- 自动修复循环默认最多 3 轮;达到上限后创建阻断问题,避免无限修复。
|
|
50
|
+
|
|
51
|
+
### 验证
|
|
52
|
+
|
|
53
|
+
- `npm run smoke:harness` 通过,覆盖验证失败回到 execute、三轮失败阻塞、验证通过重置修复状态。
|
|
54
|
+
|
|
55
|
+
## 0.1.27 - 2026-06-07
|
|
56
|
+
|
|
57
|
+
### 修复
|
|
58
|
+
|
|
59
|
+
- 强化 active run / RUN.json 读取容错,过滤异常 `questions`、`artifacts`、`riskAssessment`、`gate` 字段,避免旧状态或坏状态触发门禁崩溃。
|
|
60
|
+
- Header 渲染门禁时增加兜底,门禁异常会显示为阻塞状态,不再导致 TUI 进程退出。
|
|
61
|
+
- 阶段产物存在但不可读或误建为目录时,现在按缺失处理,不再在读取 `CONTEXT.md`、`PLAN.md`、`EXECUTION.md` 等文件时抛出异常。
|
|
62
|
+
|
|
63
|
+
### 验证
|
|
64
|
+
|
|
65
|
+
- `npm run smoke:harness` 通过,覆盖损坏 run state 和损坏 evidence index 的容错。
|
|
66
|
+
|
|
67
|
+
## 0.1.26 - 2026-06-07
|
|
68
|
+
|
|
69
|
+
### 修复
|
|
70
|
+
|
|
71
|
+
- 修复已有项目的 `evidence/index.json` 中存在异常 entry 时,Header 门禁渲染可能因读取 `path` 崩溃的问题。
|
|
72
|
+
- `readEvidenceIndex` 现在会过滤无效 evidence entry,`hasEvidenceEntry` 和 evidence 记录逻辑对坏数据保持容错。
|
|
73
|
+
|
|
74
|
+
### 验证
|
|
75
|
+
|
|
76
|
+
- `npm run smoke:harness` 通过,覆盖损坏 evidence index 不再触发崩溃。
|
|
77
|
+
|
|
78
|
+
## 0.1.25 - 2026-06-07
|
|
79
|
+
|
|
80
|
+
### 修复
|
|
81
|
+
|
|
82
|
+
- 修复 `/kd-risk`、`/kd-product`、`/kd-answer`、`/kd-artifact` 等命令在解除门禁后不会自动尝试推进下一阶段的问题。
|
|
83
|
+
- 修复 `kd_question` 回答阻断问题后只刷新门禁、不尝试推进的问题。
|
|
84
|
+
- 修复 `kd_cosmic_config`、`kd_cosmic_metadata`、`kd_cosmic_api`、`kd_sdk_signature`、`kd_ksql_lint` 写入证据后不反馈自动推进结果的问题。
|
|
85
|
+
- 修复官方 evidence 只要文件存在就可能满足门禁的问题;现在要求 evidence index 中记录的退出码为 `0`。
|
|
86
|
+
|
|
87
|
+
### 验证
|
|
88
|
+
|
|
89
|
+
- `npm run smoke:harness` 通过,覆盖阻断问题回答后的自动推进、风险更新后的自动推进尝试,以及失败 evidence 不能通过门禁。
|
|
90
|
+
|
|
9
91
|
## 0.1.24 - 2026-06-07
|
|
10
92
|
|
|
11
93
|
### 新增
|
|
@@ -255,6 +255,37 @@ discuss -> spec -> plan -> execute -> verify -> ship
|
|
|
255
255
|
/kd-finish
|
|
256
256
|
```
|
|
257
257
|
|
|
258
|
+
### /kd-review
|
|
259
|
+
|
|
260
|
+
启动只读交叉自查子 agent:
|
|
261
|
+
|
|
262
|
+
```text
|
|
263
|
+
/kd-review [审查重点]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
用于检查状态机漏洞、门禁绕过、证据缺口、提示词分散和测试缺口。子 agent 不修改文件,主 agent 负责采纳结论和后续修复。
|
|
267
|
+
|
|
268
|
+
### /kd-delegate
|
|
269
|
+
|
|
270
|
+
把局部任务委派给隔离子 agent:
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
/kd-delegate <research|doc|code|review|verify> <任务> [--dry-run]
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
角色:
|
|
277
|
+
|
|
278
|
+
```text
|
|
279
|
+
research 只读调研,输出压缩结论和证据位置
|
|
280
|
+
doc 写指定文档或阶段产物
|
|
281
|
+
code 只在 execute 阶段修改 PLAN.md 批准文件
|
|
282
|
+
review 只读交叉自查,输出 findings 和是否阻止发布
|
|
283
|
+
verify 只读分析验证命令和失败证据,实际验证由主 agent 执行并用 kd_verify_result 记录
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`--dry-run` 只预览上下文包,不启动子进程。
|
|
287
|
+
复杂任务也可能由主 agent 自动调用 `kd_subagent` 委派;自动委派不会改变 Harness 阶段。
|
|
288
|
+
|
|
258
289
|
## 内置工具
|
|
259
290
|
|
|
260
291
|
这些工具多数情况下会由 KCode 自动使用;当需要明确证据或排障时,也可以按下面参数手动调用。
|
|
@@ -279,6 +310,30 @@ kd_question action=list
|
|
|
279
310
|
|
|
280
311
|
一次只能登记一个当前最阻塞的问题,最多 3 个简短选项。
|
|
281
312
|
|
|
313
|
+
### kd_subagent
|
|
314
|
+
|
|
315
|
+
将局部任务委派给隔离 Pi 子进程:
|
|
316
|
+
|
|
317
|
+
```text
|
|
318
|
+
kd_subagent role=review task="审查当前 run 的门禁和证据缺口"
|
|
319
|
+
kd_subagent role=research task="查找采购订单保存插件相关代码" dryRun=true
|
|
320
|
+
kd_subagent tasks=[{"role":"research","task":"查找模型层"},{"role":"review","task":"审查状态机"}]
|
|
321
|
+
kd_subagent chain=[{"role":"research","task":"找相关代码"},{"role":"review","task":"基于上一输出审查风险"}]
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
参数:
|
|
325
|
+
|
|
326
|
+
```text
|
|
327
|
+
role 必填,research/doc/code/review/verify
|
|
328
|
+
task 必填,具体委派任务
|
|
329
|
+
tasks 可选,并行任务数组,只允许 research/review/verify,和 role/task/chain 三选一
|
|
330
|
+
chain 可选,链式任务数组,和 role/task/tasks 三选一
|
|
331
|
+
dryRun 可选,只预览上下文包
|
|
332
|
+
maxOutputChars 可选,限制返回给主 agent 的输出长度
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
主 Harness 仍负责阶段推进、证据和门禁;子 agent 返回结果后由主 agent 决策下一步。
|
|
336
|
+
|
|
282
337
|
### kd_search
|
|
283
338
|
|
|
284
339
|
搜索随包金蝶知识库:
|
package/docs/HARNESS_WORKFLOW.md
CHANGED
|
@@ -145,3 +145,36 @@ KCode 会阻止过早写入 Java/XML/SQL/C# 等产品代码:
|
|
|
145
145
|
- 必须先理解当前业务项目已有目录、模块、包名、基类和本地封装。
|
|
146
146
|
|
|
147
147
|
证据和门禁细节见 [证据和门禁](EVIDENCE_AND_GATES.md)。
|
|
148
|
+
|
|
149
|
+
## 子 agent 委派
|
|
150
|
+
|
|
151
|
+
KCode 支持把局部任务委派给隔离子 agent,用来降低长上下文带来的注意力漂移。主 Harness 仍是唯一状态机,负责阶段推进、门禁、证据和风险记录。
|
|
152
|
+
|
|
153
|
+
触发方式有两种:
|
|
154
|
+
|
|
155
|
+
- 自动:主 agent 在大量调研、独立交叉审查、长上下文复盘或可并行拆分时,可以主动调用 `kd_subagent`。
|
|
156
|
+
- 显式:用户用 `/kd-review` 或 `/kd-delegate` 指定委派任务。
|
|
157
|
+
|
|
158
|
+
常用入口:
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
/kd-review 审查当前实现是否有门禁绕过和测试缺口
|
|
162
|
+
/kd-delegate research 调研采购订单保存插件相关代码
|
|
163
|
+
/kd-delegate doc 更新当前阶段文档 --dry-run
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
角色边界:
|
|
167
|
+
|
|
168
|
+
- `research`、`review` 默认只读。
|
|
169
|
+
- `doc` 只写明确指定的文档或阶段产物。
|
|
170
|
+
- `code` 只能在 `execute` 阶段运行,并且只能修改 `PLAN.md` 批准文件。
|
|
171
|
+
- `verify` 只读分析验证命令、失败证据和风险;实际命令和结果记录仍由主 agent 执行。
|
|
172
|
+
|
|
173
|
+
`--dry-run` 会预览发送给子 agent 的上下文包,用来检查上下文是否过长、是否包含不该交给子 agent 的信息。
|
|
174
|
+
|
|
175
|
+
工具层支持并行和链式委派。并行只允许 `research`、`review`、`verify` 这类只读角色;`doc` 和 `code` 必须串行执行:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
kd_subagent tasks=[{"role":"research","task":"查找模型层"},{"role":"review","task":"审查门禁"}]
|
|
179
|
+
kd_subagent chain=[{"role":"research","task":"找相关代码"},{"role":"review","task":"基于上一输出审查风险"}]
|
|
180
|
+
```
|
|
@@ -4,6 +4,7 @@ import { formatStatus } from "../src/harness/format.ts";
|
|
|
4
4
|
import { PHASE_ORDER, isKdPhase, type KdPhase } from "../src/harness/types.ts";
|
|
5
5
|
import {
|
|
6
6
|
advanceRun,
|
|
7
|
+
advanceRunIfReady,
|
|
7
8
|
addQuestion,
|
|
8
9
|
answerQuestion,
|
|
9
10
|
createActiveRun,
|
|
@@ -23,7 +24,9 @@ import { flagshipWriteBlockReason, isSourceLikePath, planWriteBlockReason } from
|
|
|
23
24
|
import { sdkSignatureProductionWriteBlockReason } from "../src/harness/sdk-policy.ts";
|
|
24
25
|
import { tddProductionWriteBlockReason } from "../src/harness/tdd-policy.ts";
|
|
25
26
|
import { windowsPathHint } from "../src/platform/path.ts";
|
|
26
|
-
import { workflowPromptForRun } from "../src/harness/prompt.ts";
|
|
27
|
+
import { repairPromptForRun, workflowPromptForRun } from "../src/harness/prompt.ts";
|
|
28
|
+
import { recordVerifyResult, type VerifyResultOutcome } from "../src/harness/repair.ts";
|
|
29
|
+
import { isSubagentChild, subagentRoleFromEnv, subagentToolCallBlockReason } from "../src/harness/delegation.ts";
|
|
27
30
|
|
|
28
31
|
function requireRun(cwd: string): ReturnType<typeof readActiveRun> {
|
|
29
32
|
return readActiveRun(cwd);
|
|
@@ -109,6 +112,26 @@ function sendWorkflowPrompt(pi: ExtensionAPI, ctx: ExtensionContext, run: NonNul
|
|
|
109
112
|
if (ctx.hasUI) ctx.ui.notify("KCode 工作流消息已排队。", "info");
|
|
110
113
|
}
|
|
111
114
|
|
|
115
|
+
function autoAdvanceCommand(
|
|
116
|
+
pi: ExtensionAPI,
|
|
117
|
+
ctx: ExtensionContext,
|
|
118
|
+
run: NonNullable<ReturnType<typeof readActiveRun>>,
|
|
119
|
+
reason: string,
|
|
120
|
+
): ReturnType<typeof advanceRunIfReady> {
|
|
121
|
+
const result = advanceRunIfReady(ctx.cwd, run);
|
|
122
|
+
if (result.advanced) {
|
|
123
|
+
if (ctx.hasUI) ctx.ui.notify(result.message, "info");
|
|
124
|
+
sendWorkflowPrompt(pi, ctx, result.run, `${reason}\n继续 KCode Harness run ${result.run.id}:${result.run.goal ?? "未知需求"}`);
|
|
125
|
+
} else if (!result.message.includes("最终阶段")) {
|
|
126
|
+
if (ctx.hasUI) ctx.ui.notify(`门禁仍阻塞:${result.message}`, "warning");
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function autoAdvanceTool(cwd: string, run: NonNullable<ReturnType<typeof readActiveRun>>): ReturnType<typeof advanceRunIfReady> {
|
|
132
|
+
return advanceRunIfReady(cwd, run);
|
|
133
|
+
}
|
|
134
|
+
|
|
112
135
|
function codeWriteBlockReason(cwd: string, path: string | undefined): string | undefined {
|
|
113
136
|
if (!path || !isSourceLikePath(path)) return undefined;
|
|
114
137
|
|
|
@@ -188,9 +211,10 @@ const kdQuestionTool = defineTool({
|
|
|
188
211
|
};
|
|
189
212
|
}
|
|
190
213
|
appendQuestionEventToArtifact(ctx.cwd, run, [`- 已回答 ${answered.id}:${answered.answer}`]);
|
|
214
|
+
const auto = autoAdvanceTool(ctx.cwd, run);
|
|
191
215
|
return {
|
|
192
|
-
content: [{ type: "text", text: `已记录 ${answered.id}
|
|
193
|
-
details: { question: answered, gate: readActiveRun(ctx.cwd)?.gate },
|
|
216
|
+
content: [{ type: "text", text: `已记录 ${answered.id} 的答案。${auto.advanced ? auto.message : `门禁仍阻塞:${auto.message}`}` }],
|
|
217
|
+
details: { question: answered, gate: readActiveRun(ctx.cwd)?.gate, autoAdvance: auto },
|
|
194
218
|
};
|
|
195
219
|
}
|
|
196
220
|
|
|
@@ -240,9 +264,10 @@ const kdQuestionTool = defineTool({
|
|
|
240
264
|
const answered = answerQuestion(ctx.cwd, run, question.id, interactiveAnswer);
|
|
241
265
|
if (answered) {
|
|
242
266
|
appendQuestionEventToArtifact(ctx.cwd, run, [`- 已回答 ${answered.id}:${answered.answer}`]);
|
|
267
|
+
const auto = autoAdvanceTool(ctx.cwd, run);
|
|
243
268
|
return {
|
|
244
|
-
content: [{ type: "text", text: `用户已回答 ${answered.id}:${answered.answer}` }],
|
|
245
|
-
details: { question: answered, gate: readActiveRun(ctx.cwd)?.gate, answered: true },
|
|
269
|
+
content: [{ type: "text", text: `用户已回答 ${answered.id}:${answered.answer}。${auto.advanced ? auto.message : `门禁仍阻塞:${auto.message}`}` }],
|
|
270
|
+
details: { question: answered, gate: readActiveRun(ctx.cwd)?.gate, answered: true, autoAdvance: auto },
|
|
246
271
|
};
|
|
247
272
|
}
|
|
248
273
|
}
|
|
@@ -252,9 +277,47 @@ const kdQuestionTool = defineTool({
|
|
|
252
277
|
},
|
|
253
278
|
});
|
|
254
279
|
|
|
280
|
+
function createKdVerifyResultTool(pi: ExtensionAPI) {
|
|
281
|
+
return defineTool({
|
|
282
|
+
name: "kd_verify_result",
|
|
283
|
+
label: "KD 验证结果",
|
|
284
|
+
description: "记录当前 verify 命令结果。失败时自动写失败证据并回到 execute 修复;成功时记录通过证据并尝试推进。",
|
|
285
|
+
parameters: Type.Object({
|
|
286
|
+
command: Type.String({ description: "实际执行的验证命令。" }),
|
|
287
|
+
exitCode: Type.Number({ description: "验证命令退出码。" }),
|
|
288
|
+
stdout: Type.Optional(Type.String({ description: "验证命令 STDOUT 摘要或完整输出。" })),
|
|
289
|
+
stderr: Type.Optional(Type.String({ description: "验证命令 STDERR 摘要或完整输出。" })),
|
|
290
|
+
summary: Type.Optional(Type.String({ description: "失败原因或通过结论摘要。" })),
|
|
291
|
+
}),
|
|
292
|
+
|
|
293
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
294
|
+
const run = readActiveRun(ctx.cwd);
|
|
295
|
+
if (!run) {
|
|
296
|
+
return {
|
|
297
|
+
content: [{ type: "text", text: "当前没有 active Kingdee Harness run。请先使用 /kd-start <需求> 创建。" }],
|
|
298
|
+
details: { error: "no-active-run" },
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const outcome = recordVerifyResult(ctx.cwd, run, {
|
|
302
|
+
command: params.command,
|
|
303
|
+
exitCode: params.exitCode,
|
|
304
|
+
stdout: params.stdout,
|
|
305
|
+
stderr: params.stderr,
|
|
306
|
+
summary: params.summary,
|
|
307
|
+
});
|
|
308
|
+
handleVerifyOutcome(pi, ctx, outcome);
|
|
309
|
+
return {
|
|
310
|
+
content: [{ type: "text", text: outcome.message }],
|
|
311
|
+
details: { outcome },
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
255
317
|
export default function (pi: ExtensionAPI) {
|
|
256
318
|
pi.registerTool(kdPlanStatusTool);
|
|
257
319
|
pi.registerTool(kdQuestionTool);
|
|
320
|
+
pi.registerTool(createKdVerifyResultTool(pi));
|
|
258
321
|
|
|
259
322
|
pi.on("session_start", async (_event, ctx) => {
|
|
260
323
|
const run = readActiveRun(ctx.cwd);
|
|
@@ -271,6 +334,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
271
334
|
|
|
272
335
|
pi.on("input", async (event, ctx) => {
|
|
273
336
|
if (event.source === "extension") return { action: "continue" };
|
|
337
|
+
if (isSubagentChild()) return { action: "continue" };
|
|
274
338
|
|
|
275
339
|
let run = readActiveRun(ctx.cwd);
|
|
276
340
|
if (!run && shouldStartHarnessFromInput(event.text)) {
|
|
@@ -301,6 +365,29 @@ export default function (pi: ExtensionAPI) {
|
|
|
301
365
|
return { block: true, reason };
|
|
302
366
|
}
|
|
303
367
|
|
|
368
|
+
const subagentRole = isSubagentChild() ? subagentRoleFromEnv() : undefined;
|
|
369
|
+
if (subagentRole) {
|
|
370
|
+
const run = readActiveRun(ctx.cwd);
|
|
371
|
+
const sourceWriteBlock =
|
|
372
|
+
sdkSignatureProductionWriteBlockReason(ctx.cwd, run, path) ??
|
|
373
|
+
tddProductionWriteBlockReason(ctx.cwd, run, path) ??
|
|
374
|
+
planWriteBlockReason(ctx.cwd, run, path, run ? (readArtifact(ctx.cwd, run, "plan") ?? "") : "") ??
|
|
375
|
+
flagshipWriteBlockReason(run, path, ctx.cwd);
|
|
376
|
+
const reason = subagentToolCallBlockReason({
|
|
377
|
+
role: subagentRole,
|
|
378
|
+
toolName: event.toolName,
|
|
379
|
+
path,
|
|
380
|
+
cwd: ctx.cwd,
|
|
381
|
+
run,
|
|
382
|
+
sourceLike: path ? isSourceLikePath(path) : false,
|
|
383
|
+
sourceWriteBlockReason: sourceWriteBlock,
|
|
384
|
+
});
|
|
385
|
+
if (reason) {
|
|
386
|
+
if (ctx.hasUI) ctx.ui.notify(reason, "warning");
|
|
387
|
+
return { block: true, reason };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
304
391
|
if (event.toolName !== "write" && event.toolName !== "edit") return undefined;
|
|
305
392
|
|
|
306
393
|
const reason = codeWriteBlockReason(ctx.cwd, path) ?? flagshipWriteBlockReason(readActiveRun(ctx.cwd), path, ctx.cwd);
|
|
@@ -427,6 +514,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
427
514
|
|
|
428
515
|
const updated = updateProductProfile(ctx.cwd, run, parsed.product, parsed.version);
|
|
429
516
|
ctx.ui.notify(`产品画像:${updated.profile?.product}/${updated.profile?.techStack}/${updated.profile?.language}`, "info");
|
|
517
|
+
autoAdvanceCommand(pi, ctx, updated, "产品画像已更新。");
|
|
430
518
|
},
|
|
431
519
|
});
|
|
432
520
|
|
|
@@ -447,6 +535,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
447
535
|
|
|
448
536
|
const updated = updateRisk(ctx.cwd, run, risk.risk, risk.reason);
|
|
449
537
|
ctx.ui.notify(`风险等级:${updated.riskAssessment?.level}(${updated.riskAssessment?.reason})`, "info");
|
|
538
|
+
autoAdvanceCommand(pi, ctx, updated, "风险评估已更新。");
|
|
450
539
|
},
|
|
451
540
|
});
|
|
452
541
|
|
|
@@ -498,6 +587,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
498
587
|
? updatePhaseArtifact(ctx.cwd, run, parsed.phase, parsed.content)
|
|
499
588
|
: ensurePhaseArtifact(ctx.cwd, run, parsed.phase);
|
|
500
589
|
ctx.ui.notify(`阶段文档已就绪:${path}`, "info");
|
|
590
|
+
autoAdvanceCommand(pi, ctx, readActiveRun(ctx.cwd) ?? run, `${parsed.phase} 阶段文档已更新。`);
|
|
501
591
|
},
|
|
502
592
|
});
|
|
503
593
|
|
|
@@ -522,8 +612,40 @@ export default function (pi: ExtensionAPI) {
|
|
|
522
612
|
}
|
|
523
613
|
appendQuestionEventToArtifact(ctx.cwd, run, [`- 已回答 ${answered.id}:${answered.answer}`]);
|
|
524
614
|
ctx.ui.notify(`已记录 ${answered.id} 的答案`, "info");
|
|
615
|
+
autoAdvanceCommand(pi, ctx, readActiveRun(ctx.cwd) ?? run, `${answered.id} 已回答。`);
|
|
525
616
|
},
|
|
526
617
|
});
|
|
618
|
+
|
|
619
|
+
pi.registerCommand("kd-verify-result", {
|
|
620
|
+
description: "记录验证命令结果:/kd-verify-result <exitCode> <command>",
|
|
621
|
+
handler: async (args, ctx) => {
|
|
622
|
+
const run = requireRun(ctx.cwd);
|
|
623
|
+
if (!run) {
|
|
624
|
+
ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const [exitCodeText, ...commandParts] = args.trim().split(/\s+/);
|
|
628
|
+
const exitCode = Number(exitCodeText);
|
|
629
|
+
const command = commandParts.join(" ").trim();
|
|
630
|
+
if (!Number.isFinite(exitCode) || !command) {
|
|
631
|
+
ctx.ui.notify("用法:/kd-verify-result <exitCode> <command>", "error");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const outcome = recordVerifyResult(ctx.cwd, run, { command, exitCode });
|
|
635
|
+
ctx.ui.notify(outcome.message, outcome.status === "passed" ? "info" : "warning");
|
|
636
|
+
handleVerifyOutcome(pi, ctx, outcome);
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function handleVerifyOutcome(pi: ExtensionAPI, ctx: ExtensionContext, outcome: VerifyResultOutcome): void {
|
|
642
|
+
if (outcome.status === "passed") {
|
|
643
|
+
autoAdvanceCommand(pi, ctx, outcome.run, "验证结果已通过。");
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (outcome.status === "repairing") {
|
|
647
|
+
sendWorkflowPrompt(pi, ctx, outcome.run, repairPromptForRun(outcome.run));
|
|
648
|
+
}
|
|
527
649
|
}
|
|
528
650
|
|
|
529
651
|
function formatQuestions(run: NonNullable<ReturnType<typeof readActiveRun>>): string {
|
|
@@ -19,6 +19,19 @@ function formatGate(gate: GateResult | undefined): string {
|
|
|
19
19
|
return gate.passed ? "门禁:通过" : "门禁:阻塞";
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function safeInspectGate(cwd: string, run: ActiveRun | undefined): GateResult | undefined {
|
|
23
|
+
if (!run) return undefined;
|
|
24
|
+
try {
|
|
25
|
+
return inspectGate(cwd, run);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return {
|
|
28
|
+
passed: false,
|
|
29
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
30
|
+
checkedAt: new Date().toISOString(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
function riskLevel(run: ActiveRun | undefined): string {
|
|
23
36
|
return run?.riskAssessment?.level ?? "未知";
|
|
24
37
|
}
|
|
@@ -54,7 +67,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
54
67
|
return {
|
|
55
68
|
render(width: number): string[] {
|
|
56
69
|
const run = readActiveRun(ctx.cwd);
|
|
57
|
-
const gateState =
|
|
70
|
+
const gateState = safeInspectGate(ctx.cwd, run);
|
|
58
71
|
const phase = formatPhase(run?.phase);
|
|
59
72
|
const product = formatProduct(run);
|
|
60
73
|
const gate = formatGate(gateState);
|