@ghyper9023/pi-dev-workflow 0.4.2 → 0.5.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 (60) hide show
  1. package/.doc/AGENT-FRONTMATTER-REFERENCE.md +198 -0
  2. package/.version/RELEASE-v0.4.2.md +31 -0
  3. package/.version/RELEASE-v0.4.3.md +42 -0
  4. package/.version/RELEASE-v0.5.0.md +82 -0
  5. package/README.md +23 -3
  6. package/agents/git-agent.md +7 -0
  7. package/agents/grill/dev-doc-grill-agent.md +7 -0
  8. package/agents/grill/dev-fix-grill-agent.md +7 -0
  9. package/agents/grill/dev-grill-agent.md +7 -0
  10. package/agents/grill/dev-perf-grill-agent.md +7 -0
  11. package/agents/grill/dev-prd-agent.md +7 -0
  12. package/agents/grill/dev-refactor-grill-agent.md +7 -0
  13. package/agents/grill/dev-test-grill-agent.md +7 -0
  14. package/agents/review-agent.md +7 -0
  15. package/agents/workflow/docWriter-agent.md +13 -2
  16. package/agents/workflow/planner-agent.md +16 -3
  17. package/agents/workflow/reviewer-agent.md +21 -4
  18. package/agents/workflow/trimmer-agent.md +12 -1
  19. package/agents/workflow/worker-agent.md +17 -2
  20. package/extensions/grill-me-agent.ts +58 -8
  21. package/extensions/sub-agents.ts +133 -16
  22. package/extensions/ui-helpers.ts +27 -27
  23. package/extensions/workflow-engine.ts +323 -16
  24. package/package.json +1 -1
  25. package/tests/test-workflow-engine-bugs.mjs +317 -0
  26. package/.pi-dev-output/pi-grill/answers/answer-mpds3by7-20260520-1606.md +0 -14
  27. package/.pi-dev-output/pi-grill/answers/answer-mpfe77f1-20260521-1913.md +0 -58
  28. package/.pi-dev-output/pi-grill/answers/answer-mpfh37wu-20260521-2034.md +0 -13
  29. package/.pi-dev-output/pi-grill/answers/answer-mpfi5q4c-20260521-2104.md +0 -13
  30. package/.pi-dev-output/pi-grill/answers/answer-mpfizccb-20260521-2127.md +0 -13
  31. package/.pi-dev-output/pi-grill/answers/answer-mpfjk78k-20260521-2143.md +0 -13
  32. package/.pi-dev-output/pi-grill/questions/questions-mpfdz1tz-20260521-1907.json +0 -94
  33. package/.pi-dev-output/pi-plans/20260520-153000-fix-workflow-engine-bugs.md +0 -150
  34. package/.pi-dev-output/pi-plans/20260521-113000-fix-loopcount-timeout.md +0 -215
  35. package/.pi-dev-output/pi-plans/20260521-1730-grill-input-wrap-back-fix.md +0 -240
  36. package/.pi-dev-output/pi-plans/20260521-230000-fix-timeout-display-loopcount-gitdiff.md +0 -253
  37. package/.pi-dev-output/pi-plans/20260521-230500-esc-double-press-confirm-workflow.md +0 -137
  38. package/.pi-dev-output/pi-plans/20260521-235000-fix-gitdiff-loopcount.md +0 -258
  39. package/.pi-dev-output/pi-review/html/20260521-2305-review-workflow-index.html +0 -196
  40. package/.pi-dev-output/pi-review/md/review-20260520-100000.md +0 -91
  41. package/.pi-dev-output/pi-review/md/review-20260521-140000.md +0 -191
  42. package/.pi-dev-output/pi-review/md/review-20260521-190000.md +0 -189
  43. package/.pi-dev-output/pi-review/md/review-20260521-204500.md +0 -241
  44. package/.pi-dev-output/pi-review/md/review-20260521-214500.md +0 -270
  45. package/.pi-dev-output/pi-review/md/review-20260521-215158.md +0 -214
  46. package/.pi-dev-output/pi-review/md/review-20260521-234500.md +0 -201
  47. package/.pi-dev-output/pi-review/md/review-20260521-235500.md +0 -422
  48. package/.pi-dev-output/pi-review/md/review-20260522-000000.md +0 -212
  49. package/.pi-dev-output/pi-review/md/review-20260522-003000.md +0 -377
  50. package/.pi-dev-output/pi-review/md/review-20260522-003500.md +0 -296
  51. package/.pi-dev-output/pi-workflow/checkpoint-20260520-153000-fix-workflow-engine-bugs.json +0 -108
  52. package/.pi-dev-output/pi-workflow/checkpoint-20260521-113000-fix-loopcount-timeout.json +0 -402
  53. package/.pi-dev-output/pi-workflow/checkpoint-20260521-1730-grill-input-wrap-back-fix.json +0 -447
  54. package/.pi-dev-output/pi-workflow/checkpoint-20260521-230000-fix-timeout-display-loopcount-gitdiff.json +0 -708
  55. package/.pi-dev-output/pi-workflow/checkpoint-20260521-230500-esc-double-press-confirm-workflow.json +0 -365
  56. package/.pi-dev-output/pi-workflow/checkpoint-20260521-235000-fix-gitdiff-loopcount.json +0 -395
  57. package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfhyxc5.json +0 -30
  58. package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi2unc.json +0 -49
  59. package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi382e.json +0 -59
  60. package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi5r22.json +0 -76
@@ -338,6 +338,323 @@ simulateInitWidget();
338
338
  assertEq(workflowRunning, true, "空定时器时启动工作流正常");
339
339
 
340
340
 
341
+ console.log("\n═══ Bug C 测试 — buildTaskForStep worker 注入 prompt(review 反馈循环) ═══\n");
342
+
343
+ // ── Test C1: worker 有 planContent 时 prompt 不被忽略 ──
344
+ console.log("📋 测试 C1: worker 有 planContent 时 prompt 不被忽略\n");
345
+
346
+ // 模拟 buildTaskForStep 行为
347
+ function simulateBuildTaskForStep(agentName, prompt, planFileRelPath, cwd, planContentExists) {
348
+ if (agentName === "worker") {
349
+ const planContent = planContentExists ? "# 实施计划\n1. 修改 login.ts\n2. 添加 auth 中间件" : undefined;
350
+ if (planContent) {
351
+ const result = [
352
+ "请根据以下实施计划逐步实现代码改动。",
353
+ "",
354
+ "## 实施计划",
355
+ planContent,
356
+ "",
357
+ "## 原始需求与修改反馈",
358
+ prompt,
359
+ "",
360
+ "请严格按照计划中的步骤实施,不要做计划外的修改。",
361
+ ].join("\n");
362
+ // 验证 prompt 包含在结果中
363
+ return result.includes("\n" + prompt) || result.includes(prompt + "\n");
364
+ }
365
+ }
366
+ return false;
367
+ }
368
+
369
+ // 场景 A: prompt 包含原始需求 + 审查反馈
370
+ const promptWithFeedback = [
371
+ "[fix] 修复 login.ts 中的 401 错误",
372
+ "",
373
+ "## 上次审查发现的问题",
374
+ '审查摘要: {"maxSeverity":"critical","critical":2,"medium":1,"low":0}',
375
+ "请修复 2 个严重问题后重新运行。",
376
+ ].join("\n");
377
+
378
+ const hasPromptWithPlan = simulateBuildTaskForStep("worker", promptWithFeedback, "plan.md", "/cwd", true);
379
+ assertTrue(hasPromptWithPlan, "worker 有 planContent 时 prompt(含审查反馈)被注入到任务中");
380
+
381
+ // 场景 B: prompt 是原始需求(第一轮循环)
382
+ const originalPrompt = "[fix] 修复 login.ts 中的 401 错误";
383
+ const hasOriginalPrompt = simulateBuildTaskForStep("worker", originalPrompt, "plan.md", "/cwd", true);
384
+ assertTrue(hasOriginalPrompt, "worker 有 planContent 时原始 prompt 也被注入");
385
+
386
+ // ── Test C2: 源代码静态分析 ──
387
+ console.log("\n📋 测试 C2: 源代码中 buildTaskForStep worker 分支包含 prompt\n");
388
+
389
+ const workerBranchRegex = /if \(agentName === \"worker\"\)[\s\S]{0,1000}\]\.join\(\"\\n\"\);/;
390
+ const workerMatch = source.match(workerBranchRegex);
391
+ assertNotNull(workerMatch, "找到 worker 分支");
392
+
393
+ const hasOriginalRequirementFeedback = workerMatch?.[0]?.includes("## 原始需求与修改反馈") ?? false;
394
+ assertTrue(hasOriginalRequirementFeedback, "worker prompt 模板包含 '## 原始需求与修改反馈' 节");
395
+
396
+ // ── Test C3: 源代码中 no-plan 分支保持不变 ──
397
+ console.log("\n📋 测试 C3: worker 无 plan 分支保持不变\n");
398
+
399
+ const noPlanBranchMatch = source.match(/请根据以下功能需求实施代码改动[\s\S]{0,200}请先分析代码库,制定简要计划,再逐步实施/);
400
+ assertNotNull(noPlanBranchMatch, "无 plan 分支仍然存在");
401
+ const noPlanHasFeedback = noPlanBranchMatch?.[0]?.includes("## 原始需求与修改反馈") ?? false;
402
+ assertFalse(noPlanHasFeedback, "无 plan 分支不含 '## 原始需求与修改反馈'");
403
+
404
+
405
+ console.log("\n═══ Bug D 测试 — hasContentChanged 使用 execSync 而非 require ═══\n");
406
+
407
+ // ── Test D1: 源代码不包含 require('child_process') ──
408
+ console.log("📋 测试 D1: hasContentChanged 不使用 require('child_process')\n");
409
+
410
+ const hasRequireChildProc = source.includes("require('child_process')") || source.includes('require("child_process")');
411
+ assertFalse(hasRequireChildProc, "源代码中不包含 require('child_process')");
412
+
413
+ // ── Test D2: hasContentChanged 使用 execSync ──
414
+ console.log("\n📋 测试 D2: hasContentChanged 使用 execSync\n");
415
+
416
+ const funcStart = source.indexOf("function hasContentChanged");
417
+ assert(funcStart !== -1, "找到 hasContentChanged 函数");
418
+ const funcBody = source.slice(funcStart, funcStart + 400);
419
+ const hasExecSync = funcBody.includes("execSync(");
420
+ assertTrue(hasExecSync, "hasContentChanged 使用 execSync");
421
+ const noOldSpawnSync = !funcBody.includes("spawnSync");
422
+ assertTrue(noOldSpawnSync, "hasContentChanged 不使用 spawnSync");
423
+
424
+ // ── Test D3: 模拟 hasContentChanged 行为逻辑 ──
425
+ console.log("\n📋 测试 D3: hasContentChanged 逻辑验证\n");
426
+
427
+ function simulateHasContentChanged(currentHash, baselineHash) {
428
+ try {
429
+ // 模拟 execSync 返回 hash
430
+ const data = currentHash;
431
+ return data.trim() !== baselineHash;
432
+ } catch {
433
+ return true;
434
+ }
435
+ }
436
+
437
+ assertTrue(simulateHasContentChanged("abc123", "def456"), "不同 hash → changed");
438
+ assertFalse(simulateHasContentChanged("abc123", "abc123"), "相同 hash → unchanged");
439
+
440
+
441
+ console.log("\n═══ Bug E 测试 — trimmer prompt 包含 plan 上下文 ═══\n");
442
+
443
+ // ── Test E1: 源代码静态分析 ──
444
+ console.log("📋 测试 E1: buildTaskForStep trimmer 分支包含 plan 上下文\n");
445
+
446
+ const trimmerStart = source.indexOf('if (agentName === "trimmer")');
447
+ assert(trimmerStart !== -1, "找到 trimmer 分支");
448
+ const trimmerSection = source.slice(trimmerStart, trimmerStart + 600);
449
+
450
+ const trimmerHasWarning = trimmerSection.includes("注意:以下实施计划");
451
+ assertTrue(trimmerHasWarning, "trimmer prompt 包含 plan 保护提示");
452
+
453
+ const trimmerHasPlanContent = trimmerSection.includes("## 实施计划(改动范围)");
454
+ assertTrue(trimmerHasPlanContent, "trimmer prompt 包含 '## 实施计划(改动范围)’ 节");
455
+
456
+ const trimmerHasSpread = trimmerSection.includes("...(planContent ?");
457
+ assertTrue(trimmerHasSpread, "trimmer prompt 条件性包含 planContent");
458
+
459
+
460
+ console.log("\n═══ Bug F 测试 — 注释与代码一致性(5s vs 3s) ═══\n");
461
+
462
+ // ── Test F1: Esc 双击超时注释匹配代码 ──
463
+ console.log("📋 测试 F1: Esc 双击注释与代码一致\n");
464
+
465
+ const escCommentMatch = source.match(/Second Esc press within (\d+)s/);
466
+ if (escCommentMatch) {
467
+ const commentVal = parseInt(escCommentMatch[1], 10);
468
+ assertEq(commentVal, 3, `注释说 ${commentVal}s,代码中阈值应为 ${commentVal}s (3000ms)`);
469
+
470
+ // 验证注释值匹配 actual timeout in ms
471
+ const thresholdMs = 3000;
472
+ const thresholdSec = thresholdMs / 1000;
473
+ assertEq(commentVal, thresholdSec, `注释值 ${commentVal}s 匹配代码阈值 ${thresholdSec}s`);
474
+ } else {
475
+ // Try the previous comment text
476
+ const prevCommentMatch = source.match(/Second Esc press within [\w\s]+ → confirm cancel/);
477
+ if (prevCommentMatch) {
478
+ const commentText = prevCommentMatch[0];
479
+ // Should contain "3s" now
480
+ assertTrue(commentText.includes("3s"), `注释现在说 "3s",得到: "${commentText}"`);
481
+ } else {
482
+ fail++;
483
+ console.error(" ❌ 找不到 Esc 注释");
484
+ }
485
+ }
486
+
487
+
488
+ console.log("\n═══ Bug G 测试 — 链上下文传递 (executeSingleStep) ═══\n");
489
+
490
+ // ── Test G1: executeSingleStep 中包含链上下文捕获逻辑 ──
491
+ console.log("📋 测试 G1: executeSingleStep 中有 chain context 捕获\n");
492
+
493
+ const singleStepFuncStart = source.indexOf("async function executeSingleStep");
494
+ assert(singleStepFuncStart !== -1, "找到 executeSingleStep 函数");
495
+ const singleStepEndSearch = source.indexOf("async function executeLoopGroup", singleStepFuncStart);
496
+ const fullSingleStep = source.slice(singleStepFuncStart, singleStepEndSearch);
497
+
498
+ const hasChainContextKey = fullSingleStep.includes('chainKey');
499
+ assertTrue(hasChainContextKey, "executeSingleStep 中有 chainKey 变量");
500
+
501
+ const hasUpdateChainContext = fullSingleStep.includes('updateChainContext(chainKey');
502
+ assertTrue(hasUpdateChainContext, "executeSingleStep 中调用 updateChainContext");
503
+
504
+ const hasPlannerKey = fullSingleStep.includes('"计划制定摘要"');
505
+ assertTrue(hasPlannerKey, "agentName === planner 时使用 '计划制定摘要' key");
506
+
507
+ const hasDocWriterKey = fullSingleStep.includes('"文档更新摘要"');
508
+ assertTrue(hasDocWriterKey, "agentName === docWriter 时使用 '文档更新摘要' key");
509
+
510
+
511
+ console.log("\n═══ Bug H 测试 — Agent 前置元数据解析 ═══\n");
512
+
513
+ // ── Test H1: 所有 workflow agent 的 session 已启用(可追溯) ──
514
+ console.log("📋 测试 H1: 所有 workflow agent 的 session 已启用(session: true)\n");
515
+
516
+ const agentFiles = [
517
+ "agents/workflow/planner-agent.md",
518
+ "agents/workflow/worker-agent.md",
519
+ "agents/workflow/reviewer-agent.md",
520
+ "agents/workflow/trimmer-agent.md",
521
+ "agents/workflow/docWriter-agent.md",
522
+ "agents/review-agent.md",
523
+ ];
524
+ for (const af of agentFiles) {
525
+ const agentPath = path.resolve(__dirname, "..", af);
526
+ if (fs.existsSync(agentPath)) {
527
+ const content = fs.readFileSync(agentPath, "utf-8");
528
+ const hasSessionTrue = content.includes("session: true");
529
+ assertTrue(hasSessionTrue, `${af} 包含 session: true`);
530
+ const hasSessionFalse = content.includes("session: false");
531
+ assertFalse(hasSessionFalse, `${af} 不包含 session: false`);
532
+ } else {
533
+ console.log(` ℹ️ 跳过不存在的文件: ${af}`);
534
+ }
535
+ }
536
+
537
+ // ── Test H2: 所有 workflow agent 有 MCP/SKILL 可用声明(工具白名单已移除 → MCP 实际可用) ──
538
+ console.log("\n📋 测试 H2: workflow agent 包含 MCP/SKILL 可用声明(白名单已移除,MCP 实际可用)\n");
539
+
540
+ const workflowAgentFiles = [
541
+ "agents/workflow/planner-agent.md",
542
+ "agents/workflow/worker-agent.md",
543
+ "agents/workflow/reviewer-agent.md",
544
+ "agents/workflow/trimmer-agent.md",
545
+ "agents/workflow/docWriter-agent.md",
546
+ ];
547
+ for (const af of workflowAgentFiles) {
548
+ const agentPath = path.resolve(__dirname, "..", af);
549
+ if (fs.existsSync(agentPath)) {
550
+ const content = fs.readFileSync(agentPath, "utf-8");
551
+ const hasExtraToolsSection = content.includes("## 额外可用工具");
552
+ assertTrue(hasExtraToolsSection, `${af} 包含 '## 额外可用工具' 节`);
553
+ const hasMcpClaim = content.includes("MCP");
554
+ assertTrue(hasMcpClaim, `${af} 包含 MCP 声明`);
555
+ const hasSkillClaim = content.includes("SKILL");
556
+ assertTrue(hasSkillClaim, `${af} 包含 SKILL 声明`);
557
+ } else {
558
+ console.log(` ℹ️ 跳过不存在的文件: ${af}`);
559
+ }
560
+ }
561
+
562
+ // ── Test H3: workflow agent 没有 tools 白名单(以允许 MCP 工具) ──
563
+ console.log("\n📋 测试 H3: workflow agent 没有 tools 白名单限制\n");
564
+
565
+ for (const af of workflowAgentFiles) {
566
+ const agentPath = path.resolve(__dirname, "..", af);
567
+ if (fs.existsSync(agentPath)) {
568
+ const content = fs.readFileSync(agentPath, "utf-8");
569
+ // Should NOT have a tools: line in frontmatter
570
+ const hasToolsLine = /^tools:/.test(content.split("---")?.[1] ?? "");
571
+ assertFalse(hasToolsLine, `${af} 无 tools: 行(白名单已移除)`);
572
+ } else {
573
+ console.log(` ℹ️ 跳过不存在的文件: ${af}`);
574
+ }
575
+ }
576
+
577
+ // ── Test H4: sub-agents.ts 中 session 使用完整路径 + .jsonl ──
578
+ console.log("\n📋 测试 H4: sub-agents.ts session 路径构造\n");
579
+
580
+ const subAgentSource = fs.readFileSync(path.resolve(__dirname, "../extensions/sub-agents.ts"), "utf-8");
581
+ const hasMkdirSync = subAgentSource.includes("fs.mkdirSync(sessionDir");
582
+ assertTrue(hasMkdirSync, "spawnSubagent 创建 session 目录");
583
+ const hasJsonlPath = subAgentSource.includes(".jsonl");
584
+ assertTrue(hasJsonlPath, "session 文件路径包含 .jsonl 扩展名");
585
+ const hasSessionPath = subAgentSource.includes("path.join(sessionDir");
586
+ assertTrue(hasSessionPath, "使用 path.join 构建完整 session 路径");
587
+ const noSessionDirArg = subAgentSource.includes("--session-dir");
588
+ assertFalse(noSessionDirArg, "不再使用 --session-dir(改用完整路径 --session)");
589
+
590
+ // ── Test H5: Agent frontmatter 字段解析正确 ──
591
+ console.log("\n📋 测试 H5: Agent frontmatter 字段解析\n");
592
+
593
+ // subAgentSource 已在 H4 中声明,此处直接复用
594
+
595
+ const hasThinkingField = subAgentSource.includes('fields.thinking');
596
+ assertTrue(hasThinkingField, "loadAgent 解析 thinking 字段");
597
+
598
+ const hasSessionField = subAgentSource.includes('fields.session');
599
+ assertTrue(hasSessionField, "loadAgent 解析 session 字段");
600
+
601
+ const hasSessionDirField = subAgentSource.includes('fields["session-dir"]');
602
+ assertTrue(hasSessionDirField, "loadAgent 解析 session-dir 字段");
603
+
604
+ const hasNoContextField = subAgentSource.includes('fields["no-context"]');
605
+ assertTrue(hasNoContextField, "loadAgent 解析 no-context 字段");
606
+
607
+ const hasNoExtensionsField = subAgentSource.includes('fields["no-extensions"]');
608
+ assertTrue(hasNoExtensionsField, "loadAgent 解析 no-extensions 字段");
609
+
610
+ const hasExtraArgsField = subAgentSource.includes('fields["extra-args"]');
611
+ assertTrue(hasExtraArgsField, "loadAgent 解析 extra-args 字段");
612
+
613
+
614
+ console.log("\n═══ Bug I 测试 — 工作流 UUID 溯源机制 ═══\n");
615
+
616
+ // ── Test I1: workflowId 注入到 buildTaskForStep ──
617
+ console.log("📋 测试 I1: buildTaskForStep 接收 workflowId 参数\n");
618
+
619
+ const bldFuncStart = source.indexOf("function buildTaskForStep");
620
+ assert(bldFuncStart !== -1, "找到 buildTaskForStep 函数");
621
+ const bldFuncParams = source.slice(bldFuncStart, bldFuncStart + 300);
622
+
623
+ const hasWorkflowIdParam = bldFuncParams.includes("workflowId");
624
+ assertTrue(hasWorkflowIdParam, "buildTaskForStep 接收 workflowId 参数");
625
+
626
+ const hasChainContextParam = bldFuncParams.includes("chainContext");
627
+ assertTrue(hasChainContextParam, "buildTaskForStep 接收 chainContext 参数");
628
+
629
+ // ── Test I2: CheckpointData 包含 workflowId ──
630
+ console.log("\n📋 测试 I2: CheckpointData 包含 workflowId\n");
631
+
632
+ const checkpointDataMatch = source.match(/interface CheckpointData [\s\S]{0,500}workflowId/);
633
+ assertNotNull(checkpointDataMatch, "CheckpointData 接口包含 workflowId");
634
+
635
+ // ── Test I3: buildWorkflowInfoBlock 函数存在 ──
636
+ console.log("\n📋 测试 I3: buildWorkflowInfoBlock 函数存在\n");
637
+
638
+ const hasBuildWorkflowInfoBlock = source.includes("function buildWorkflowInfoBlock");
639
+ assertTrue(hasBuildWorkflowInfoBlock, "buildWorkflowInfoBlock 函数存在");
640
+
641
+ // ── Test I4: buildReviewTask 也接收 workflowId ──
642
+ console.log("\n📋 测试 I4: buildReviewTask 接收 workflowId\n");
643
+
644
+ const reviewTaskStart = source.indexOf("function buildReviewTask");
645
+ assert(reviewTaskStart !== -1, "找到 buildReviewTask 函数");
646
+ const reviewTaskParams = source.slice(reviewTaskStart, reviewTaskStart + 200);
647
+ const reviewHasWorkflowId = reviewTaskParams.includes("workflowId");
648
+ assertTrue(reviewHasWorkflowId, "buildReviewTask 接收 workflowId 参数");
649
+
650
+
651
+ console.log("\n=== 增强功能测试汇总 ===\n");
652
+ console.log("📋 附加测试覆盖:");
653
+ console.log(" - G: 链上下文传递 (executeSingleStep)");
654
+ console.log(" - H: Agent 前置元数据解析 + MCP/SKILL 移除");
655
+ console.log(" - I: 工作流 UUID 溯源机制");
656
+
657
+
341
658
  console.log("\n═══════════════════════════════════════════════════════\n");
342
659
  console.log(`📊 结果: ${pass} 通过, ${fail} 失败\n`);
343
660
 
@@ -1,14 +0,0 @@
1
- [fix] 修复 extensions/workflow-engine.ts 1179行的let agentResult = await runAgentWithProgress(loopAgent, loopTask, stepIndex, step.loopAgentName!, step.timeoutMs);和1432行-1435行的 sendWorkflowResult(pi, finalState, prompt, _workflowType); // Cleanup widget after delay setTimeout(() => cleanupWidget(), 5000); 中的 1179行:executeLoopGroup 函数在调用 runAgentWithProgress 后没有检查 agentResult.exitCode。如果 sub-agent 进程非正常退出(例如崩溃或报错),工作流会忽略该错误并继续尝试运行 Reviewer。建议检查退出码,并在失败时抛出异常或中断循环。1432行:使用 setTimeout 延迟 5 秒执行 cleanupWidget 存在竞态风险。如果用户在工作流完成后 5 秒内立即启动了一个新的工作流,这个定时器触发时会调用 cleanupWidget 并将全局变量 _workflowRunning 重置为 false,从而干扰甚至中断正在运行的新工作流。建议通过对比工作流启动时间戳或在启动新工作流时显式取消之前的定时器来解决。
2
-
3
- **背景**:
4
- - 输入:见代码上下文
5
- - 预期行为:修复问题,但不能破坏原因功能结构和其他代码
6
- - 当前错误:请描述当前错误
7
- **任务**:
8
- 1. 不要仅仅消除报错(Suppress),要解决根本原因。
9
- 2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。
10
- 3. 提供至少一种修复方案,并说明为什么这样做。
11
- 4. 编写测试用例复现该 Bug 并确认修复有效。
12
- **输出**:提供 diff 和两句话的根因分析。
13
- **约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。
14
- **验证**:运行 tests通过 确认修复。
@@ -1,58 +0,0 @@
1
- [fix] 修复 extensions/workflow-engine.ts 中的 Workflow的loop循环,进入第2,3,4循环时候,ui仍然是第1次循环,计数没有+=1. 超时时间问题:worker review是一个loop组,超时时间应该是独自的,现在ui显示的超时时间是显示在loop组长的,且worker和reviewer用的一个超时时间,需要检查这部分逻辑,超时时间应该交给子代理而不是loop组,如果只是ui显示错误就修复ui问题。trimmer的默认超时时间较短,给20分钟,worker给30分钟 ,reviewer给15分钟。
2
-
3
- **背景**:
4
- - 输入:见代码上下文
5
- - 预期行为:修复问题,确保原有功能,其他功能不被破坏。
6
- - 当前错误:请描述当前错误
7
- **任务**:
8
- 1. 不要仅仅消除报错(Suppress),要解决根本原因。
9
- 2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。
10
- 3. 提供至少一种修复方案,并说明为什么这样做。
11
- 4. 编写测试用例复现该 Bug 并确认修复有效。
12
- **输出**:提供 diff 和两句话的根因分析。
13
- **约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。
14
-
15
- ---
16
- ## 设计评审记录
17
-
18
- 以下是在开发前进行的设计评审问答,所有决策已确认:
19
-
20
- [评审问题 1]
21
- 问题: 关于 loopCount UI 不同步问题:executeLoopGroup() 内部的 while 循环中,每次迭代完成后(loopCount++ 之后),是否应该立即调用 updateWidgetStep() 更新 UI 中的 loopCount?还是你认为只在 executeLoopGroup 返回后由外层 executeWorkflowBackground 统一更新即可?
22
- 回答: a[推荐] 在 executeLoopGroup 的 while 循环内,每次 loopCount++ 之后立即调用 updateWidgetStep() 更新 UI 当前循环次数
23
-
24
- [评审问题 2]
25
- 问题: 关于超时时间分离问题:executeLoopGroup() 中 reviewer 和 loopAgent 使用相同的 step.timeoutMs。你希望 reviewer 使用独立的超时时间,还是认为只需在 UI 上把显示的超时时间区分开即可?
26
- 回答: a[推荐] 给 WorkflowStepDef 增加 reviewTimeoutMs 字段,reviewer 使用独立的超时时间
27
-
28
- [评审问题 3]
29
- 问题: 关于默认超时时间调整:当前配置中 worker 是 900000ms (15min),trimmer 是 300000ms (5min),reviewer 是 900000ms(loop-group中) 或 300000ms(独立步骤)。根据需求,worker 应改为 30min (1800000ms),trimmer 应改为 20min (1200000ms),reviewer 保持 15min (900000ms)。这些超时是硬编码在 dev-prompts.ts 的 WorkflowStepDef 中的,还是有其他来源?
30
- 回答: a[推荐] 直接在 dev-prompts.ts 中修改各个 WorkflowStepDef 的 timeoutMs 值
31
-
32
- [评审问题 4]
33
- 问题: 关于超时时间的粒度:如果给 reviewer 独立超时,是每个 loop-group 内的 reviewer 都用固定 15min?还是不同 loop-group(worker-reviewer vs trimmer-reviewer)中的 reviewer 应该有不同的超时值?
34
- 回答: a[推荐] 所有 reviewer 统一使用固定的超时时间 15min(900000ms)
35
-
36
- [评审问题 5]
37
- 问题: 确认一下:当前 executeLoopGroup() 中,loopAgent(worker/trimmer)和 reviewer 都用同一个 step.timeoutMs 传给 runAgentWithProgress。如果要分离超时,你建议 reviewer 的超时时间从哪里读取?
38
- 回答: a[推荐] 从 WorkflowStepDef 的新字段 reviewTimeoutMs 读取
39
-
40
- [评审问题 6]
41
- 问题: 关于 loopCount 显示细节:当前 buildWidgetLines 中,当 loop-group 处于 running 状态时,loopStr 固定显示 "第 1 次循环"。进入第 2, 3, 4 次循环时 UI 仍然是 "第 1 次循环"。除了上面第 1 问提到的在循环中更新 UI,widget 端是否也需要调整 loopStr 的判断逻辑?
42
- 回答: a[推荐] 同时修改两端:executeLoopGroup 每次循环后更新 widget,同时 buildWidgetLines 中直接从 s.loopCount 读取显示
43
-
44
- [评审问题 7]
45
- 问题: 关于 runAgentWithProgress 的超时传递:注意 reviewer 在 executeLoopGroup 中调用时传的是 step.timeoutMs。如果要给 reviewer 用独立超时,runAgentWithProgress 调用的第二个参数需要改。你能确认一下 reviewer 和 loopAgent 的超时分别应该是什么值吗?
46
- 回答: a[推荐] worker loopAgent=30min(1800000ms), reviewer=15min(900000ms); trimmer loopAgent=20min(1200000ms), reviewer=15min(900000ms)
47
-
48
- [评审问题 8]
49
- 问题: 关于超时显示在 UI 上的位置:当前 UI 在 loop-group 的 row 上显示超时(如 "/超时时间15m"),但 bug 描述说 "超时时间应该交给子代理而不是 loop 组"。你认为 loop-group 的 UI row 上还应该显示超时时间吗?还是只在子代理(worker/reviewer)的 sub-step 级别显示各自的超时?
50
- 回答: a[推荐] loop-group 行不显示超时时间,在 worker 和 reviewer 的 sub-step 中分别显示各自的超时时间
51
-
52
- [评审问题 9]
53
- 问题: 关于 trimmer 默认超时 20 分钟:当前 trimmer 的 timeoutMs 是 300000 (5min)。改为 1200000 (20min) 是否合理?请确认这个改动是否会影响其他步骤的规划。
54
- 回答: a[推荐] 合理,仅改 dev-prompts.ts 中所有 trimmer 相关步骤的 timeoutMs 为 1200000
55
-
56
- [评审问题 10]
57
- 问题: 关于测试验证:如果要验证修复是否有效,是否有现成的测试文件或测试框架可用?还是需要新建测试?
58
- 回答: a[推荐] 在 test/ 目录下新建测试文件,模拟 workflow engine 执行并检查 UI state 的 loopCount 和 timeoutMs
@@ -1,13 +0,0 @@
1
- [fix] 修复 extensions文件夹 中的 问题: 1.dev-*命令输入,grill提问环节自定义输入等地方的输入不会自动换行,全部内容都在一行。 2.grill的提问回答环节,提问问题现在正确换行了,但是回答选项a/b/c...在字数较多时候没有换行,无法看到超出ui部分的内容,所有文件都在一行。 3.grill的提问回答环节,<-左方向键位功能是要求回到上一个问题,但是现在左方向键功能没有实现,按下后没反应。
2
-
3
- **背景**:
4
- - 输入:见代码上下文
5
- - 预期行为:预期要求: 1.dev-*命令输入,grill提问环节自定义输入等地方需要自动换行。 2.grill的提问回答环节,选项也需要自动换行,a/b/c...以及自定义内容。 3.grill的提问回答环节,<-左方向键位功能修复,能够回到上一个问题,且保留了上一个问题的选择(<上次选择)。 4.严格完成以上任务要求,不得破坏原有其他功能,不得偷懒省略,以最小改动实现。
6
- - 当前错误:请描述当前错误
7
- **任务**:
8
- 1. 不要仅仅消除报错(Suppress),要解决根本原因。
9
- 2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。
10
- 3. 提供至少一种修复方案,并说明为什么这样做。
11
- 4. 编写测试用例复现该 Bug 并确认修复有效。
12
- **输出**:提供 diff 和两句话的根因分析。
13
- **约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。
@@ -1,13 +0,0 @@
1
- [fix] 修复 当前目录 中的 问题: 1.commit提交哈希:01413c9edb1dee3af525bfabdb4f55de7f0a3b4b里面的改动, 现在的超时时间显示位置: ``` ▶ ⠏ 🔧 修复代码 → 审查 (52.6s) |__ ⠏ worker · |__ 超时时间60m ``` 2.问题2,`第 0 次循环`只在排队时候显示了,等loop组开始工作连`第 1 次循环`的提示都不见了 2.根据git diff --name-status获取的文件变动信息,是通过正则筛选的,但很明显git diff --name-status给的结果如下: ```git diff --name-statusM .gitignoreM Cargo.lock ``` 非常的规整,正则现在会识别出来一些无关东西,判断不出来是哪里来的,git diff --name-status直接拿到的信息`X 空格 filepath 换行`只需要简单的string处理即可,无需复杂正则,而且正则效果不好。
2
-
3
- **背景**:
4
- - 输入:见代码上下文
5
- - 预期行为:预期: 1.超时时间显示位置: ``` ▶ ⠏ 🔧 修复代码 → 审查 |__ ⠏ worker · (52.6s/超时时间60m ) |__ reviewer · (0s/超时时间60m) ``` 超时时间应该跟在计时的后面(当前计时/超时时间xm),且位于子代理名称后面 2.恢复`第 x 次循环`的显示,上次fix的模板是x计数不更新,但是修改后直接导致`第 x 次循环`不见了,恢复显示并修复x计数和更新ui逻辑。 3.将正则匹配改成普通的string处理 3.严格完成以上任务要求,不得破坏原有其他功能,不得偷懒省略,以最小改动实现。
6
- - 当前错误:请描述当前错误
7
- **任务**:
8
- 1. 不要仅仅消除报错(Suppress),要解决根本原因。
9
- 2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。
10
- 3. 提供至少一种修复方案,并说明为什么这样做。
11
- 4. 编写测试用例复现该 Bug 并确认修复有效。
12
- **输出**:提供 diff 和两句话的根因分析。
13
- **约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。
@@ -1,13 +0,0 @@
1
- [fix] 修复 当前目录 中的 问题: 1.Workflow工作期间,ecs是中断当前工作流,容易误触
2
-
3
- **背景**:
4
- - 输入:见代码上下文
5
- - 预期行为:预期要求: 1.Workflow工作期间,ecs第一次,显示提示:“再次按下ecs键,停止Workflow”,俩次ecs间隔<5s才退出,5s之后,提示内容“再次按下ecs键,停止Workflow”去掉,需要重新监听ecs按下和重新计时。 2.严格完成以上任务要求,不得破坏原有其他功能,不得偷懒省略,以最小改动实现。
6
- - 当前错误:请描述当前错误
7
- **任务**:
8
- 1. 不要仅仅消除报错(Suppress),要解决根本原因。
9
- 2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。
10
- 3. 提供至少一种修复方案,并说明为什么这样做。
11
- 4. 编写测试用例复现该 Bug 并确认修复有效。
12
- **输出**:提供 diff 和两句话的根因分析。
13
- **约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。
@@ -1,13 +0,0 @@
1
- [fix] 修复 当前目录 中的 问题: 1.commit提交哈希f98799d17f561127ff31156653f717526fc28fbb中的修改,把git diff改成简单的string操作,这是修复后的运行ui情况:``` fix - 修复 当前目录 中的 问题:1.Workflow工作期间,ecs是中断当前工作流,容易误触 ⠧ 工作流 · 完全值守模式 · 4m33s ✓ 📋 分析根因并制定修复计划 (1m33s/超时时间15m) |__ ✓ planner · (4m33s/超时时间15m) | M checkpoint-${planId}.json | M src/main.rs | M path/to/file.ts | M file.ts | D file\n | A file\n\t\t\t\t\ttry | A .pi-dev-output/pi-plans/20260521-230500-esc-double-press-confirm-workflow.md | A .pi-dev-output/pi-workflow/checkpoint.json | output:.pi-dev-output/pi-plans/xxx.md |__ output:review-20260520-162800.md ▶ ⠧ 🔧 修复代码 → 审查 · 第 1 次循环 (1m59s) |__ ⠧ worker · (1m59s/超时时间60m) | M [],\n\t\t\t\toutputs: | M path\\\") | M [],\n\t\t\t\toutputs: | M [],\n\t\t\toutputs: | M [],\n\t\t\t\toutputs: | M path\\\") | M path\\\") | M [],\\n\\t\\t\\toutputs: | M [],\\n\\t\\t\\toutputs: | M [],\\n\\t\\t\\toutputs: | M [],\\n\\t\\t\\toutputs: | M [],\\n\\t\\t\\toutputs: | M [],\\n\\t\\t\\ | M [],\\n\\t\\t\\t\\toutputs: | M [],\\n\\t\\t\\t\\toutputs: | M [],\\n\\t\\t\\t\\toutputs: | M [],\\n\\t\\t\\t\\toutputs: | M [],\\n\\t\\t\\t\\toutputs: | M [],\\n\\t\\t\\t\\toutputs: |__ M [],\\n\\ |__ ◦ reviewer · |__ 正在排队 27 tools Ctrl+O 展开详情 | Escape 取消```出现了一些M checkpoint-${planId}.json,A file\n\t\t\t\t\ttry ,M [],\\n\\t\\t\\t\\toutputs:完全不知道是哪里来的东西。2.问题2,`第 0 次循环`出现在pedding时候(正确),当第一次loop时候(即从planner进入loop)显示`第 · 次循环`(正确),但是遇到reviewer触发严重bug,需要循环一次时候,仍然显示`第 1 次循环`,如果再进一次循环才会显示`第 2 次循环`,也就是本该`第 0 次循环`->`第 1 次循环`->`第 2 次循环`->`第 3 次循环`,现在却是:`第 0 次循环`->`第 1 次循环`->`第 1 次循环`->`第 2 次循环`
2
-
3
- **背景**:
4
- - 输入:见代码上下文
5
- - 预期行为:预期:1.找到根本问题所在,彻底修复。2.严格完成任务要求,不得破坏原有其他功能,不得偷懒省略,以最小改动实现。
6
- - 当前错误:请描述当前错误
7
- **任务**:
8
- 1. 不要仅仅消除报错(Suppress),要解决根本原因。
9
- 2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。
10
- 3. 提供至少一种修复方案,并说明为什么这样做。
11
- 4. 编写测试用例复现该 Bug 并确认修复有效。
12
- **输出**:提供 diff 和两句话的根因分析。
13
- **约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。
@@ -1,94 +0,0 @@
1
- {
2
- "questions": [
3
- {
4
- "id": 1,
5
- "question": "关于 loopCount UI 不同步问题:executeLoopGroup() 内部的 while 循环中,每次迭代完成后(loopCount++ 之后),是否应该立即调用 updateWidgetStep() 更新 UI 中的 loopCount?还是你认为只在 executeLoopGroup 返回后由外层 executeWorkflowBackground 统一更新即可?",
6
- "options": [
7
- "a[推荐] 在 executeLoopGroup 的 while 循环内,每次 loopCount++ 之后立即调用 updateWidgetStep() 更新 UI 当前循环次数",
8
- "b 不需要改 UI 逻辑,是 UI 构建函数 buildWidgetLines 中的 loopStr 渲染条件太严格导致",
9
- "c 在外层 executeWorkflowBackground 的 for 循环内增加额外逻辑来刷新 loopGroup 的 UI"
10
- ]
11
- },
12
- {
13
- "id": 2,
14
- "question": "关于超时时间分离问题:executeLoopGroup() 中 reviewer 和 loopAgent 使用相同的 step.timeoutMs。你希望 reviewer 使用独立的超时时间,还是认为只需在 UI 上把显示的超时时间区分开即可?",
15
- "options": [
16
- "a[推荐] 给 WorkflowStepDef 增加 reviewTimeoutMs 字段,reviewer 使用独立的超时时间",
17
- "b 不需要增加字段,只是在 UI 上正确显示各自的超时时间(当前问题只是 UI 显示问题)",
18
- "c 在 loop-group 的配置项中新增 reviewer 专用的超时配置"
19
- ]
20
- },
21
- {
22
- "id": 3,
23
- "question": "关于默认超时时间调整:当前配置中 worker 是 900000ms (15min),trimmer 是 300000ms (5min),reviewer 是 900000ms(loop-group中) 或 300000ms(独立步骤)。根据需求,worker 应改为 30min (1800000ms),trimmer 应改为 20min (1200000ms),reviewer 保持 15min (900000ms)。这些超时是硬编码在 dev-prompts.ts 的 WorkflowStepDef 中的,还是有其他来源?",
24
- "options": [
25
- "a[推荐] 直接在 dev-prompts.ts 中修改各个 WorkflowStepDef 的 timeoutMs 值",
26
- "b 需要通过配置文件或环境变量来设置",
27
- "c 从 agent 定义(sub-agents.ts 中的 AgentDef)中动态读取超时时间"
28
- ]
29
- },
30
- {
31
- "id": 4,
32
- "question": "关于超时时间的粒度:如果给 reviewer 独立超时,是每个 loop-group 内的 reviewer 都用固定 15min?还是不同 loop-group(worker-reviewer vs trimmer-reviewer)中的 reviewer 应该有不同的超时值?",
33
- "options": [
34
- "a[推荐] 所有 reviewer 统一使用固定的超时时间 15min(900000ms)",
35
- "b worker-reviewer 中的 reviewer 用 15min,trimmer-reviewer 中的 reviewer 用 10min",
36
- "c 在 WorkflowStepDef 中为 reviewer 单独配置不同的超时值"
37
- ]
38
- },
39
- {
40
- "id": 5,
41
- "question": "确认一下:当前 executeLoopGroup() 中,loopAgent(worker/trimmer)和 reviewer 都用同一个 step.timeoutMs 传给 runAgentWithProgress。如果要分离超时,你建议 reviewer 的超时时间从哪里读取?",
42
- "options": [
43
- "a[推荐] 从 WorkflowStepDef 的新字段 reviewTimeoutMs 读取",
44
- "b 从 agentMap.get(reviewAgentName!).timeoutMs 读取(AgentDef 上的超时)",
45
- "c 固定写 900000ms 硬编码在 executeLoopGroup 中"
46
- ]
47
- },
48
- {
49
- "id": 6,
50
- "question": "关于 loopCount 显示细节:当前 buildWidgetLines 中,当 loop-group 处于 running 状态时,loopStr 固定显示 \"第 1 次循环\"。进入第 2, 3, 4 次循环时 UI 仍然是 \"第 1 次循环\"。除了上面第 1 问提到的在循环中更新 UI,widget 端是否也需要调整 loopStr 的判断逻辑?",
51
- "options": [
52
- "a[推荐] 同时修改两端:executeLoopGroup 每次循环后更新 widget,同时 buildWidgetLines 中直接从 s.loopCount 读取显示",
53
- "b 只需修 executeLoopGroup 端,UI widget 渲染逻辑已经能正确处理 loopCount>0 的情况",
54
- "c 只需修 UI widget 端,让它在 running 状态下也能正确渲染变化的 loopCount"
55
- ]
56
- },
57
- {
58
- "id": 7,
59
- "question": "关于 runAgentWithProgress 的超时传递:注意 reviewer 在 executeLoopGroup 中调用时传的是 step.timeoutMs。如果要给 reviewer 用独立超时,runAgentWithProgress 调用的第二个参数需要改。你能确认一下 reviewer 和 loopAgent 的超时分别应该是什么值吗?",
60
- "options": [
61
- "a[推荐] worker loopAgent=30min(1800000ms), reviewer=15min(900000ms); trimmer loopAgent=20min(1200000ms), reviewer=15min(900000ms)",
62
- "b worker loopAgent=15min, reviewer=15min; trimmer loopAgent=5min, reviewer=5min — 只需要在 UI 上修正显示",
63
- "c worker loopAgent=30min, reviewer=10min; trimmer loopAgent=20min, reviewer=5min"
64
- ]
65
- },
66
- {
67
- "id": 8,
68
- "question": "关于超时显示在 UI 上的位置:当前 UI 在 loop-group 的 row 上显示超时(如 \"/超时时间15m\"),但 bug 描述说 \"超时时间应该交给子代理而不是 loop 组\"。你认为 loop-group 的 UI row 上还应该显示超时时间吗?还是只在子代理(worker/reviewer)的 sub-step 级别显示各自的超时?",
69
- "options": [
70
- "a[推荐] loop-group 行不显示超时时间,在 worker 和 reviewer 的 sub-step 中分别显示各自的超时时间",
71
- "b loop-group 行仍然显示整体超时,同时子代理也显示各自的超时",
72
- "c 只需修复 ui 显示错误,不在子代理级别显示超时"
73
- ]
74
- },
75
- {
76
- "id": 9,
77
- "question": "关于 trimmer 默认超时 20 分钟:当前 trimmer 的 timeoutMs 是 300000 (5min)。改为 1200000 (20min) 是否合理?请确认这个改动是否会影响其他步骤的规划。",
78
- "options": [
79
- "a[推荐] 合理,仅改 dev-prompts.ts 中所有 trimmer 相关步骤的 timeoutMs 为 1200000",
80
- "b 改为 600000 (10min) 更合理",
81
- "c trimmer 不应该改超时,问题在其他地方"
82
- ]
83
- },
84
- {
85
- "id": 10,
86
- "question": "关于测试验证:如果要验证修复是否有效,是否有现成的测试文件或测试框架可用?还是需要新建测试?",
87
- "options": [
88
- "a[推荐] 在 test/ 目录下新建测试文件,模拟 workflow engine 执行并检查 UI state 的 loopCount 和 timeoutMs",
89
- "b 已有测试文件,可以扩展",
90
- "c 不需要测试"
91
- ]
92
- }
93
- ]
94
- }