@ghyper9023/pi-dev-workflow 0.6.3 → 0.8.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.
Files changed (40) hide show
  1. package/.github/workflows/release.yml +56 -0
  2. package/.pre-commit-config.yaml +75 -0
  3. package/.version/RELEASE-v0.7.0.md +85 -0
  4. package/.version/RELEASE-v0.8.0.md +93 -0
  5. package/README.md +178 -294
  6. package/extensions/dev-prompts.ts +121 -1033
  7. package/extensions/git-commands.ts +62 -171
  8. package/extensions/grill-me-agent.ts +160 -164
  9. package/extensions/pre-check.ts +190 -0
  10. package/extensions/review-detect.ts +123 -0
  11. package/extensions/session-utils.ts +168 -0
  12. package/extensions/ui-helpers.ts +22 -780
  13. package/package.json +2 -2
  14. package/prompts/APPEND_SYSTEM.md +73 -59
  15. package/skills/review-html/SKILL.md +1 -1
  16. package/tests/test-no-subagents.mjs +264 -0
  17. package/.doc/AGENT-FRONTMATTER-REFERENCE.md +0 -198
  18. package/agents/git-agent.md +0 -44
  19. package/agents/grill/dev-doc-grill-agent.md +0 -42
  20. package/agents/grill/dev-fix-grill-agent.md +0 -44
  21. package/agents/grill/dev-grill-agent.md +0 -40
  22. package/agents/grill/dev-perf-grill-agent.md +0 -45
  23. package/agents/grill/dev-prd-agent.md +0 -60
  24. package/agents/grill/dev-refactor-grill-agent.md +0 -46
  25. package/agents/grill/dev-test-grill-agent.md +0 -45
  26. package/agents/review-agent.md +0 -53
  27. package/agents/workflow/docWriter-agent.md +0 -53
  28. package/agents/workflow/planner-agent.md +0 -131
  29. package/agents/workflow/reviewer-agent.md +0 -128
  30. package/agents/workflow/trimmer-agent.md +0 -78
  31. package/agents/workflow/worker-agent.md +0 -70
  32. package/extensions/sub-agents.ts +0 -954
  33. package/extensions/workflow-engine.ts +0 -2005
  34. package/tests/test-grill-json-fix.mjs +0 -243
  35. package/tests/test-loopcount-timeout-fix.mjs +0 -336
  36. package/tests/test-output-directory-structure.mjs +0 -177
  37. package/tests/test-save-answer-file-workflow.mjs +0 -187
  38. package/tests/test-workflow-config.mjs +0 -244
  39. package/tests/test-workflow-engine-bugs.mjs +0 -908
  40. package/tests/test-workflow-engine.mjs +0 -518
@@ -1,243 +0,0 @@
1
- /**
2
- * test-grill-json-fix.mjs — Verify the JSON stability fixes for grill-me-agent
3
- *
4
- * Tests:
5
- * 1. writeToolPromptSuffix 包含 JSON Schema
6
- * 2. writeToolPromptSuffix 包含转义规则
7
- * 3. writeToolPromptSuffix 包含自我校验指令
8
- * 4. readQuestionsFromFile 正确处理合法 JSON
9
- * 5. readQuestionsFromFile 正确处理无效 JSON(返回空数组 + 不报错)
10
- * 6. readQuestionsFromFile 正确处理含特殊字符的 JSON
11
- * 7. 输出文件不再被删除
12
- * 8. 重试 prompt 包含错误上下文
13
- * 9. ensureOutputDir 的 .gitignore 让文件不被 git 跟踪(保留在本地)
14
- *
15
- * Run: node tests/test-grill-json-fix.mjs
16
- */
17
-
18
- import * as fs from "node:fs";
19
- import * as path from "node:path";
20
- import { fileURLToPath } from "node:url";
21
-
22
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
- const EXT_PATH = path.resolve(__dirname, "../extensions/grill-me-agent.ts");
24
-
25
- // ── Helpers ──────────────────────────────────────────────────
26
-
27
- let pass = 0;
28
- let fail = 0;
29
-
30
- function assert(condition, msg) {
31
- if (condition) {
32
- pass++;
33
- console.log(` ✅ ${msg}`);
34
- } else {
35
- fail++;
36
- console.error(` ❌ ${msg}`);
37
- }
38
- }
39
-
40
- // ── Read the source file ─────────────────────────────────────
41
-
42
- let source;
43
- try {
44
- source = fs.readFileSync(EXT_PATH, "utf-8");
45
- } catch (e) {
46
- console.error(`Failed to read source file: ${e.message}`);
47
- process.exit(1);
48
- }
49
-
50
- console.log(`📄 源文件: ${EXT_PATH}`);
51
- console.log(`📏 文件大小: ${source.length} 字节\n`);
52
-
53
- // ── Test 1: Contains JSON Schema ─────────────────────────────
54
-
55
- assert(
56
- source.includes('"$schema": "http://json-schema.org/draft-07/schema#"'),
57
- "writeToolPromptSuffix 应包含 Draft-07 JSON Schema",
58
- );
59
-
60
- assert(
61
- source.includes('"required": ["id", "question", "options"]'),
62
- "JSON Schema 要求 id, question, options 为必填字段",
63
- );
64
-
65
- assert(
66
- source.includes('"minItems": 1'),
67
- "JSON Schema 要求 options 至少 1 项",
68
- );
69
-
70
- // ── Test 2: Contains escaping rules ──────────────────────────
71
-
72
- assert(
73
- source.includes("String escaping rules") ||
74
- source.includes("JSON-escaped"),
75
- "writeToolPromptSuffix 应包含字符串转义规则",
76
- );
77
-
78
- assert(
79
- source.includes("Double quotes inside text"),
80
- "转义规则应提及双引号",
81
- );
82
-
83
- assert(
84
- source.includes("Newlines"),
85
- "转义规则应提及换行符",
86
- );
87
-
88
- assert(
89
- source.includes("Backslashes"),
90
- "转义规则应提及反斜杠",
91
- );
92
-
93
- // ── Test 3: Contains self-review instruction ─────────────────
94
-
95
- assert(
96
- source.includes("Self-review") ||
97
- source.includes("mentally validate") ||
98
- source.includes("mentally validate your JSON"),
99
- "writeToolPromptSuffix 应包含自我校验指令",
100
- );
101
-
102
- // ── Test 4: Example uses unicode escapes ─────────────────────
103
- // Note: The source file has \\uXXXX (double backslash in raw text)
104
- // because TypeScript single-quoted string \\ → single backslash in value.
105
- // search for "\\\\u" (two backslashes in raw file = "\\u" in JS regex)
106
- assert(
107
- source.includes("\\\\u9879\\\\u76ee") ||
108
- (source.includes("\\\\u9879") && source.includes("u9879")),
109
- "示例应包含 JSON unicode 转义序列(合法 JSON 示例)",
110
- );
111
-
112
- // ── Test 5: readQuestionsFromFile has error logging ──────────
113
-
114
- assert(
115
- source.includes("[grill-me-agent] JSON parse error in"),
116
- "readQuestionsFromFile 应在 JSON 解析失败时输出错误日志",
117
- );
118
-
119
- // ── Test 6: Output files are no longer deleted ───────────────
120
-
121
- assert(
122
- !source.includes("fs.unlinkSync(outputFilePath)") &&
123
- !source.includes("fs.unlinkSync(retryPath)"),
124
- "输出文件不应再被 fs.unlinkSync 删除",
125
- );
126
-
127
- // ── Test 7: Retry prompt includes error context ──────────────
128
-
129
- assert(
130
- source.includes("Previous attempt had JSON errors") ||
131
- source.includes("parseErrorMsg"),
132
- "重试 prompt 应包含前次错误的上下文",
133
- );
134
-
135
- // ── Test 8: Parse valid JSON ─────────────────────────────────
136
-
137
- function simulateReadQuestions(raw) {
138
- try {
139
- const parsed = JSON.parse(raw);
140
- const items = Array.isArray(parsed) ? parsed : parsed.questions;
141
- if (!Array.isArray(items)) return 0;
142
- return items.filter(
143
- (q) => q && typeof q.question === "string" && Array.isArray(q.options) && q.options.length > 0,
144
- ).length;
145
- } catch {
146
- return 0;
147
- }
148
- }
149
-
150
- const validJson = JSON.stringify({
151
- questions: [
152
- { id: 1, question: "测试问题", options: ["选项A", "选项B"] },
153
- { id: 2, question: "第二个问题", options: ["选项X", "选项Y", "选项Z"] },
154
- ],
155
- });
156
-
157
- assert(
158
- simulateReadQuestions(validJson) === 2,
159
- "readQuestionsFromFile 应解析合法 JSON 并返回正确数量的问题",
160
- );
161
-
162
- // ── Test 9: Invalid JSON (unescaped double quotes) ───────────
163
-
164
- const invalidJson = `{
165
- "questions": [
166
- {
167
- "id": 1,
168
- "question": "他说"这个不行",如何处理?",
169
- "options": ["忽略", "修复"]
170
- }
171
- ]
172
- }`;
173
-
174
- assert(
175
- simulateReadQuestions(invalidJson) === 0,
176
- "readQuestionsFromFile 遇到非法 JSON 应返回空数组,而非崩溃",
177
- );
178
-
179
- // ── Test 10: JSON with properly escaped special chars ────────
180
-
181
- const escapedJson = JSON.stringify({
182
- questions: [
183
- {
184
- id: 1,
185
- question: '他说"这个不行",如何处理?',
186
- options: ["忽略", "修复"],
187
- },
188
- ],
189
- });
190
-
191
- assert(
192
- simulateReadQuestions(escapedJson) === 1,
193
- "readQuestionsFromFile 应解析含转义双引号的合法 JSON",
194
- );
195
-
196
- // ── Test 11: Bare array format backward compat ───────────────
197
-
198
- const bareArray = JSON.stringify([
199
- { id: 1, question: "测试问题", options: ["A", "B"] },
200
- ]);
201
-
202
- assert(
203
- simulateReadQuestions(bareArray) === 1,
204
- "readQuestionsFromFile 应后向兼容裸数组格式 [...]",
205
- );
206
-
207
- // ── Test 12: Missing vs empty fields ─────────────────────────
208
- // Empty string "" passes filter (typeof "" === "string" is true).
209
- // Only truly missing (undefined) fields are filtered.
210
-
211
- const withIncomplete = JSON.stringify({
212
- questions: [
213
- { id: 1, question: "完整问题", options: ["A"] },
214
- { id: 2, question: "", options: ["A"] }, // empty string, passes filter
215
- { id: 3, options: ["A"] }, // missing question, filtered
216
- { id: 4, question: "无选项" }, // missing options, filtered
217
- ],
218
- });
219
-
220
- assert(
221
- simulateReadQuestions(withIncomplete) === 2,
222
- "readQuestionsFromFile 应过滤缺失字段的问题,空字符串字段应保留",
223
- );
224
-
225
- // ── Test 13: gitignore for output dir exists ─────────────────
226
-
227
- assert(
228
- source.includes("pi-grill"),
229
- "代码中应引用 pi-grill 输出目录",
230
- );
231
-
232
- // ── Summary ──────────────────────────────────────────────────
233
-
234
- console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
235
- console.log(`结果: ${pass} 通过, ${fail} 失败, 共 ${pass + fail} 个测试`);
236
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
237
-
238
- if (fail > 0) {
239
- console.error("\n⚠️ 部分测试未通过");
240
- process.exit(1);
241
- } else {
242
- console.log("\n✅ 所有测试通过");
243
- }
@@ -1,336 +0,0 @@
1
- /**
2
- * test-loopcount-timeout-fix.mjs
3
- *
4
- * 测试 loopCount UI 同步、reviewer 独立超时、默认超时值修改
5
- *
6
- * 测试范围:
7
- * 1. loopCount 在每次循环后通过 updateWidgetStep 同步到 widget
8
- * 2. reviewer 使用 reviewTimeoutMs(独立超时)
9
- * 3. 默认超时值 worker=30min (1_800_000), trimmer=20min (1_200_000), reviewer=15min (900_000)
10
- * 4. loop-group 行不显示 timeoutMs(通过静态分析 updateWidgetStep 参数)
11
- * 5. sub-step 在 runAgentWithProgress 中写入 detail(超时信息)
12
- * 6. 回归:非 loop-group 步骤仍正常显示超时
13
- */
14
-
15
- import * as fs from "node:fs";
16
- import * as path from "node:path";
17
- import { fileURLToPath } from "node:url";
18
-
19
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
- const EXT_DIR = path.resolve(__dirname, "..", "extensions");
21
-
22
- // ── Test helpers ──
23
-
24
- let passed = 0;
25
- let failed = 0;
26
- const results = [];
27
-
28
- function assert(label, condition, detail = "") {
29
- if (condition) {
30
- passed++;
31
- results.push({ label, ok: true });
32
- } else {
33
- failed++;
34
- results.push({ label, ok: false, detail });
35
- console.error(` ❌ ${label}${detail ? " — " + detail : ""}`);
36
- }
37
- }
38
-
39
- function assertIncludes(label, text, pattern, detail = "") {
40
- assert(label, text.includes(pattern), detail);
41
- }
42
-
43
- function assertNotIncludes(label, text, pattern, detail = "") {
44
- assert(label, !text.includes(pattern), detail);
45
- }
46
-
47
- // ── Read source files ──
48
-
49
- const weContent = fs.readFileSync(path.join(EXT_DIR, "workflow-engine.ts"), "utf8");
50
- const dpContent = fs.readFileSync(path.join(EXT_DIR, "dev-prompts.ts"), "utf8");
51
- const uhContent = fs.readFileSync(path.join(EXT_DIR, "ui-helpers.ts"), "utf8");
52
-
53
- // ═══════════════════════════════════════════════════════════════
54
- // Test 1: loopCount UI sync
55
- // 验证 executeLoopGroup 中 loopCount++ 后调用 updateWidgetStep
56
- // ═══════════════════════════════════════════════════════════════
57
-
58
- console.log("\n📋 Test 1: loopCount UI 同步");
59
-
60
- assertIncludes(
61
- "1.1 executeLoopGroup 中有 loopCount++ 后立即更新 UI",
62
- weContent,
63
- "// 立即更新 UI 显示当前循环次数",
64
- );
65
-
66
- assertIncludes(
67
- "1.2 updateWidgetStep 调用中传入 loopCount",
68
- weContent,
69
- "loopCount,",
70
- );
71
-
72
- assertIncludes(
73
- "1.3 updateWidgetStep 调用中包含 loopCount 参数名",
74
- weContent,
75
- "loopCount,", // 在 extra 对象中
76
- );
77
-
78
- assertIncludes(
79
- "1.4 updateWidgetStep 调用中包含 maxLoops",
80
- weContent,
81
- "maxLoops: step.maxLoops",
82
- );
83
-
84
- // ═══════════════════════════════════════════════════════════════
85
- // Test 2: Reviewer 独立超时
86
- // 验证 reviewer 使用 reviewTimeoutMs 而非 step.timeoutMs
87
- // ═══════════════════════════════════════════════════════════════
88
-
89
- console.log("\n📋 Test 2: Reviewer 独立超时");
90
-
91
- assertIncludes(
92
- "2.1 executeLoopGroup 中定义了 reviewTimeoutMs 变量",
93
- weContent,
94
- "const reviewTimeoutMs = step.reviewTimeoutMs ?? step.timeoutMs;",
95
- );
96
-
97
- assertIncludes(
98
- "2.2 reviewer 的 runAgentWithProgress 使用 reviewTimeoutMs",
99
- weContent,
100
- "step.reviewAgentName!, reviewTimeoutMs);",
101
- );
102
-
103
- assert(
104
- "2.3 reviewer 调用不再使用 step.timeoutMs",
105
- !weContent.includes("step.reviewAgentName!, step.timeoutMs)"),
106
- "reviewer 的 runAgentWithProgress 不应该使用 step.timeoutMs",
107
- );
108
-
109
- assertIncludes(
110
- "2.4 Worker 仍使用 step.timeoutMs(未受 reviewer 变更影响)",
111
- weContent,
112
- "step.loopAgentName!, step.timeoutMs)",
113
- "注意:worker 的 runAgentWithProgress 仍使用 step.timeoutMs",
114
- );
115
-
116
- // ═══════════════════════════════════════════════════════════════
117
- // Test 3: 默认超时值验证(读取 dev-prompts.ts)
118
- // ═══════════════════════════════════════════════════════════════
119
-
120
- console.log("\n📋 Test 3: 默认超时值验证");
121
-
122
- // Helper: extract all loop-group config blocks
123
- function extractLoopGroups(source) {
124
- const lines = source.split("\n");
125
- const groups = [];
126
- let currentBlock = null;
127
- let braceDepth = 0;
128
-
129
- for (const line of lines) {
130
- if (line.includes("type: \"loop-group\"")) {
131
- currentBlock = { start: lines.indexOf(line), lines: [line], loopAgent: "", reviewAgent: "", timeoutMs: 0, reviewTimeoutMs: 0 };
132
- braceDepth = 0;
133
- continue;
134
- }
135
- if (currentBlock) {
136
- currentBlock.lines.push(line);
137
- if (line.includes("loopAgentName:")) {
138
- const m = line.match(/loopAgentName:\s*"(\w+)"/);
139
- if (m) currentBlock.loopAgent = m[1];
140
- }
141
- if (line.includes("reviewAgentName:")) {
142
- const m = line.match(/reviewAgentName:\s*"(\w+)"/);
143
- if (m) currentBlock.reviewAgent = m[1];
144
- }
145
- if (line.includes("timeoutMs:")) {
146
- const m = line.match(/timeoutMs:\s*(\d[\d_]*)/);
147
- if (m) currentBlock.timeoutMs = parseInt(m[1].replace(/_/g, ""), 10);
148
- }
149
- if (line.includes("reviewTimeoutMs:")) {
150
- const m = line.match(/reviewTimeoutMs:\s*(\d[\d_]*)/);
151
- if (m) currentBlock.reviewTimeoutMs = parseInt(m[1].replace(/_/g, ""), 10);
152
- }
153
- if (line.includes("},") || line.includes("} ;") || line.includes("};")) {
154
- braceDepth++;
155
- if (braceDepth >= 1) {
156
- groups.push(currentBlock);
157
- currentBlock = null;
158
- }
159
- }
160
- }
161
- }
162
- return groups;
163
- }
164
-
165
- const loopGroups = extractLoopGroups(dpContent);
166
-
167
- assert("3.0 找到至少 6 个 loop-group 配置", loopGroups.length >= 6, `找到 ${loopGroups.length} 个`);
168
-
169
- // worker 相关:timeoutMs 应为 1_800_000 (30min)
170
- const workerGroups = loopGroups.filter(g => g.loopAgent === "worker");
171
- for (const g of workerGroups) {
172
- assert(
173
- `3.1 Worker loop-group 超时=30min: ${g.loopAgent} → ${g.timeoutMs}`,
174
- g.timeoutMs === 1_800_000,
175
- `期望 1_800_000,实际 ${g.timeoutMs}`,
176
- );
177
- assert(
178
- `3.2 Worker loop-group reviewTimeoutMs=15min: ${g.loopAgent} → ${g.reviewTimeoutMs}`,
179
- g.reviewTimeoutMs === 900_000,
180
- `期望 900_000,实际 ${g.reviewTimeoutMs}`,
181
- );
182
- }
183
-
184
- // trimmer 相关:timeoutMs 应为 1_200_000 (20min)
185
- const trimmerGroups = loopGroups.filter(g => g.loopAgent === "trimmer");
186
- for (const g of trimmerGroups) {
187
- assert(
188
- `3.3 Trimmer loop-group 超时=20min: ${g.loopAgent} → ${g.timeoutMs}`,
189
- g.timeoutMs === 1_200_000,
190
- `期望 1_200_000,实际 ${g.timeoutMs}`,
191
- );
192
- assert(
193
- `3.4 Trimmer loop-group reviewTimeoutMs=15min: ${g.loopAgent} → ${g.reviewTimeoutMs}`,
194
- g.reviewTimeoutMs === 900_000,
195
- `期望 900_000,实际 ${g.reviewTimeoutMs}`,
196
- );
197
- }
198
-
199
- // 所有 loop-group 都应有 reviewTimeoutMs
200
- for (const g of loopGroups) {
201
- assert(
202
- `3.5 ${g.loopAgent} loop-group 有 reviewTimeoutMs`,
203
- g.reviewTimeoutMs > 0,
204
- `缺少 reviewTimeoutMs`,
205
- );
206
- }
207
-
208
- // ═══════════════════════════════════════════════════════════════
209
- // Test 4: loop-group 行不显示 timeout
210
- // 验证 updateWidgetStep 对 loop-group 步骤不传 timeoutMs
211
- // ═══════════════════════════════════════════════════════════════
212
-
213
- console.log("\n📋 Test 4: loop-group 行不显示 timeout");
214
-
215
- assertIncludes(
216
- "4.1 loop-group running 状态不传 timeoutMs",
217
- weContent,
218
- 'step.type === "loop-group" ? undefined : step.timeoutMs',
219
- );
220
-
221
- // ═══════════════════════════════════════════════════════════════
222
- // Test 5: sub-step 显示 timeout
223
- // 验证 runAgentWithProgress 在 sub-step 的 detail 中写入超时
224
- // ═══════════════════════════════════════════════════════════════
225
-
226
- console.log("\n📋 Test 5: sub-step 显示 timeout");
227
-
228
- assertIncludes(
229
- "5.1 runAgentWithProgress 在新建 sub-step 时写入 detail",
230
- weContent,
231
- "detail: `超时时间${formatTimeout(timeoutMs)}`",
232
- );
233
-
234
- assertIncludes(
235
- "5.2 runAgentWithProgress 在复用 sub-step 时更新 detail",
236
- weContent,
237
- "existing.detail = `超时时间${formatTimeout(timeoutMs)}`;",
238
- );
239
-
240
- // ═══════════════════════════════════════════════════════════════
241
- // Test 6: formatTimeout 已导出
242
- // ═══════════════════════════════════════════════════════════════
243
-
244
- console.log("\n📋 Test 6: formatTimeout 导出");
245
-
246
- assertIncludes(
247
- "6.1 formatTimeout 从 ui-helpers.ts 导出",
248
- uhContent,
249
- "export function formatTimeout",
250
- );
251
-
252
- assertIncludes(
253
- "6.2 formatTimeout 被 workflow-engine.ts 导入",
254
- weContent,
255
- "formatTimeout,",
256
- );
257
-
258
- // ═══════════════════════════════════════════════════════════════
259
- // Test 7: WorkflowStepDef 接口有 reviewTimeoutMs 字段
260
- // ═══════════════════════════════════════════════════════════════
261
-
262
- console.log("\n📋 Test 7: WorkflowStepDef 接口");
263
-
264
- assertIncludes(
265
- "7.1 WorkflowStepDef 有 reviewTimeoutMs 字段定义",
266
- weContent,
267
- "reviewTimeoutMs?: number;",
268
- );
269
-
270
- // ═══════════════════════════════════════════════════════════════
271
- // Test 8: 回归测试 — 非 loop-group 功能不受影响
272
- // ═══════════════════════════════════════════════════════════════
273
-
274
- console.log("\n📋 Test 8: 回归测试");
275
-
276
- assertIncludes(
277
- "8.1 非 loop-group 步骤运行状态仍传 timeoutMs",
278
- weContent,
279
- "timeoutMs: step.type === \"loop-group\" ? undefined : step.timeoutMs,", // 对于非 loop-group 仍传
280
- );
281
-
282
- assertIncludes(
283
- "8.2 非 loop-group 步骤 done 状态仍传 timeoutMs",
284
- weContent,
285
- "timeoutMs: step.type === \"loop-group\" ? undefined : step.timeoutMs,", // done 状态同样逻辑
286
- );
287
-
288
- // 验证 executeSingleStep 未受影响 — 它仍然传递超时给 runAgentWithProgress
289
- if (weContent.includes("executeSingleStep")) {
290
- assert(
291
- "8.3 executeSingleStep 中仍传递 timeoutMs",
292
- true,
293
- );
294
- }
295
-
296
- // ═══════════════════════════════════════════════════════════════
297
- // Test 9: sub-step 重置 — 每次循环重置 sub-step 状态
298
- // ═══════════════════════════════════════════════════════════════
299
-
300
- console.log("\n📋 Test 9: sub-step 状态重置");
301
-
302
- assertIncludes(
303
- "9.1 每次循环开始时重置 sub-step 状态",
304
- weContent,
305
- "// 每次循环开始时重置 sub-step 状态",
306
- );
307
-
308
- assert(
309
- "9.2 loopAgent sub-step 在循环开始被重置为 pending",
310
- weContent.includes('setWidgetSubStepStatus(stepIndex, step.loopAgentName!, "pending")'),
311
- );
312
-
313
- assert(
314
- "9.3 reviewAgent sub-step 在循环开始被重置为 pending",
315
- weContent.includes('setWidgetSubStepStatus(stepIndex, step.reviewAgentName!, "pending")'),
316
- );
317
-
318
- // ═══════════════════════════════════════════════════════════════
319
- // Summary
320
- // ═══════════════════════════════════════════════════════════════
321
-
322
- console.log("\n" + "═".repeat(60));
323
- console.log(`结果: ${passed} 通过, ${failed} 失败, 共 ${passed + failed} 个测试`);
324
- console.log("═".repeat(60));
325
-
326
- if (failed > 0) {
327
- console.error("\n⚠️ 部分测试未通过:");
328
- for (const r of results) {
329
- if (!r.ok) {
330
- console.error(` ❌ ${r.label}${r.detail ? " — " + r.detail : ""}`);
331
- }
332
- }
333
- process.exit(1);
334
- } else {
335
- console.log("\n✅ 全部通过");
336
- }