@ghyper9023/pi-dev-workflow 0.5.1 → 0.6.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.
@@ -399,7 +399,7 @@ const FEAT_WORKFLOW_STEPS: WorkflowStepDef[] = [
399
399
  label: "📝 曎新文档",
400
400
  type: "confirm",
401
401
  agentName: "docWriter",
402
- timeoutMs: 300_000,
402
+ timeoutMs: 600_000,
403
403
  },
404
404
  ];
405
405
 
@@ -426,7 +426,7 @@ const FIX_WORKFLOW_STEPS: WorkflowStepDef[] = [
426
426
  label: "📝 曎新文档",
427
427
  type: "confirm",
428
428
  agentName: "docWriter",
429
- timeoutMs: 300_000,
429
+ timeoutMs: 600_000,
430
430
  },
431
431
  ];
432
432
 
@@ -513,7 +513,7 @@ const DOC_WORKFLOW_STEPS: WorkflowStepDef[] = [
513
513
  label: "📝 撰写文档",
514
514
  type: "auto",
515
515
  agentName: "docWriter",
516
- timeoutMs: 300_000,
516
+ timeoutMs: 600_000,
517
517
  },
518
518
  ];
519
519
 
@@ -536,7 +536,7 @@ const SECURITY_WORKFLOW_STEPS: WorkflowStepDef[] = [
536
536
  label: "🔒 安党审查",
537
537
  type: "auto",
538
538
  agentName: "reviewer",
539
- timeoutMs: 300_000,
539
+ timeoutMs: 900_000,
540
540
  },
541
541
  ];
542
542
 
@@ -857,7 +857,7 @@ const COMPARE_QUESTIONS = [
857
857
  export default function (pi: ExtensionAPI) {
858
858
  // ── /dev-feat ──────────────────────────────────────────────
859
859
  pi.registerCommand("dev-feat", {
860
- description: "(prompt wizard) 新功胜/创意生成 — 支持讟计评审 (Grill) + 自劚化工䜜流",
860
+ description: "(prompt wizard) 新功胜/创意生成 — 支持讟计方案远问完善 (Grill) + 自劚化工䜜流",
861
861
  handler: async (_args, ctx) => {
862
862
  const answers: Record<string, string> = {};
863
863
  let featIdx = 0;
@@ -911,17 +911,17 @@ export default function (pi: ExtensionAPI) {
911
911
 
912
912
  // ── /dev-fix ───────────────────────────────────────────────
913
913
  pi.registerCommand("dev-fix", {
914
- description: "(prompt wizard) 问题排查/错误修正 — 支持根因分析评审 (Grill)",
914
+ description: "(prompt wizard) 问题排查/错误修正 — 支持 Bug 根因远问 (Grill)",
915
915
  handler: async (_args, ctx) => {
916
916
  await runWizardWithGrill(
917
917
  ctx, pi, "fix", "问题排查/错误修正",
918
918
  FIX_QUESTIONS, assembleFixPrompt,
919
919
  {
920
920
  agentDef: _fixGrillAgent,
921
- title: "🐛 Bug 根因分析评审",
922
- description: "AI 䌚从倍现条件、根因掚理、修倍方案、回園风险等绎床挑战䜠的理解。",
921
+ title: "🐛 Bug 根因远问",
922
+ description: "AI 䌚通过䞀步步远问垮䜠粟准定䜍根因从倍现条件到根本原因掚理再到修倍方案验证和回園风险评䌰。",
923
923
  questionTitle: "Bug 根因分析",
924
- loaderLabel: "🧠 AI 正圚分析代码并生成根因评审问题...",
924
+ loaderLabel: "🧠 AI 正圚分析代码并生成根因远问问题...",
925
925
  },
926
926
  { steps: FIX_WORKFLOW_STEPS },
927
927
  );
@@ -930,17 +930,17 @@ export default function (pi: ExtensionAPI) {
930
930
 
931
931
  // ── /dev-doc ───────────────────────────────────────────────
932
932
  pi.registerCommand("dev-doc", {
933
- description: "(prompt wizard) 文档生成/总结 — 支持倧纲评审 (Grill)",
933
+ description: "(prompt wizard) 文档生成/总结 — 支持文档倧纲远问完善 (Grill)",
934
934
  handler: async (_args, ctx) => {
935
935
  await runWizardWithGrill(
936
936
  ctx, pi, "doc", "文档生成/总结",
937
937
  DOC_QUESTIONS, assembleDocPrompt,
938
938
  {
939
939
  agentDef: _docGrillAgent,
940
- title: "📄 文档倧纲评审",
941
- description: "AI 䌚从受䌗定䜍、结构安排、瀺䟋选择等绎床审视䜠的文档计划。",
942
- questionTitle: "文档倧纲评审",
943
- loaderLabel: "🧠 AI 正圚分析并生成文档倧纲评审问题...",
940
+ title: "📄 文档倧纲远问完善",
941
+ description: "AI 䌚通过远问垮䜠完善文档倧纲从受䌗定䜍到结构安排确讀术语䞀臎性和瀺䟋芆盖范囎。",
942
+ questionTitle: "文档倧纲远问完善",
943
+ loaderLabel: "🧠 AI 正圚分析并生成文档倧纲远问问题...",
944
944
  },
945
945
  { steps: DOC_WORKFLOW_STEPS },
946
946
  );
@@ -949,17 +949,17 @@ export default function (pi: ExtensionAPI) {
949
949
 
950
950
  // ── /dev-refactor ──────────────────────────────────────────
951
951
  pi.registerCommand("dev-refactor", {
952
- description: "(prompt wizard) 重构/䌘化现有结构 — 支持重构计划评审 (Grill)",
952
+ description: "(prompt wizard) 重构/䌘化现有结构 — 支持重构方案远问 (Grill)",
953
953
  handler: async (_args, ctx) => {
954
954
  await runWizardWithGrill(
955
955
  ctx, pi, "refactor", "重构/䌘化",
956
956
  REFACTOR_QUESTIONS, assembleRefactorPrompt,
957
957
  {
958
958
  agentDef: _refactorGrillAgent,
959
- title: "🔧 重构方案评审",
960
- description: "AI 䌚从暡块蟹界、API 兌容性、测试策略、迁移风险等绎床审视䜠的重构计划。",
961
- questionTitle: "重构方案评审",
962
- loaderLabel: "🧠 AI 正圚分析代码并生成重构评审问题...",
959
+ title: "🔧 重构方案远问",
960
+ description: "AI 䌚通过远问垮䜠识别隐藏耊合风险从暡块蟹界到 API 兌容性验证行䞺保持和迁移路埄安党性。",
961
+ questionTitle: "重构方案远问",
962
+ loaderLabel: "🧠 AI 正圚分析代码并生成重构远问问题...",
963
963
  },
964
964
  { steps: REFACTOR_WORKFLOW_STEPS },
965
965
  );
@@ -968,17 +968,17 @@ export default function (pi: ExtensionAPI) {
968
968
 
969
969
  // ── /dev-test ──────────────────────────────────────────────
970
970
  pi.registerCommand("dev-test", {
971
- description: "(prompt wizard) 测试甚䟋生成 — 支持测试计划评审 (Grill)",
971
+ description: "(prompt wizard) 测试甚䟋生成 — 支持测试策略远问 (Grill)",
972
972
  handler: async (_args, ctx) => {
973
973
  await runWizardWithGrill(
974
974
  ctx, pi, "test", "测试甚䟋/评䌰",
975
975
  TEST_QUESTIONS, assembleTestPrompt,
976
976
  {
977
977
  agentDef: _testGrillAgent,
978
- title: "🧪 测试计划评审",
979
- description: "AI 䌚从芆盖绎床、蟹界条件、暡拟策略等角床审视䜠的测试方案。",
980
- questionTitle: "测试计划评审",
981
- loaderLabel: "🧠 AI 正圚分析并生成测试评审问题...",
978
+ title: "🧪 测试策略远问",
979
+ description: "AI 䌚通过远问垮䜠发现测试猺口从芆盖绎床到蟹界条件验证暡拟策略和测试隔犻是吊到䜍。",
980
+ questionTitle: "测试策略远问",
981
+ loaderLabel: "🧠 AI 正圚分析并生成测试远问问题...",
982
982
  },
983
983
  { steps: TEST_WORKFLOW_STEPS },
984
984
  );
@@ -995,17 +995,17 @@ export default function (pi: ExtensionAPI) {
995
995
 
996
996
  // ── /dev-perf ──────────────────────────────────────────────
997
997
  pi.registerCommand("dev-perf", {
998
- description: "(prompt wizard) 性胜䌘化 — 支持䌘化方案评审 (Grill)",
998
+ description: "(prompt wizard) 性胜䌘化 — 支持性胜䌘化方案远问 (Grill)",
999
999
  handler: async (_args, ctx) => {
1000
1000
  await runWizardWithGrill(
1001
1001
  ctx, pi, "perf", "性胜䌘化",
1002
1002
  PERF_QUESTIONS, assemblePerfPrompt,
1003
1003
  {
1004
1004
  agentDef: _perfGrillAgent,
1005
- title: "⚡ 性胜䌘化方案评审",
1006
- description: "AI 䌚从基准测试方法、䌘化方向、回園风险等绎床审视䜠的方案。",
1007
- questionTitle: "性胜䌘化方案评审",
1008
- loaderLabel: "🧠 AI 正圚分析并生成性胜䌘化评审问题...",
1005
+ title: "⚡ 性胜䌘化方案远问",
1006
+ description: "AI 䌚通过远问垮䜠验证瓶颈刀断和䌘化方向从基准测试方法到朜圚回園风险确保方案合理性。",
1007
+ questionTitle: "性胜䌘化方案远问",
1008
+ loaderLabel: "🧠 AI 正圚分析并生成性胜䌘化远问问题...",
1009
1009
  },
1010
1010
  { steps: PERF_WORKFLOW_STEPS },
1011
1011
  );
@@ -1,5 +1,5 @@
1
1
  /**
2
- * grill-me-agent.ts — 讟计评审 (Grill) 和 PRD 生成的独立管理噚
2
+ * grill-me-agent.ts — 讟计 (Grill) 和 PRD 生成的独立管理噚
3
3
  *
4
4
  * 职莣
5
5
  * 1. runGrillPhase() — 启劚 sub-agent 生成评审问题TUI 逐题呈现选项 + 自定义蟓入
@@ -372,10 +372,10 @@ export async function runGrillPhase(
372
372
  };
373
373
 
374
374
  const agentDef = options?.agentDef ?? _defaultGrillAgent;
375
- const confirmTitle = options?.title ?? "🔍 讟计方案评审";
376
- const confirmDesc = options?.description ?? "AI 䌚从架构、数据流、蟹界条件、安党等倚䞪绎床挑战䜠的讟计。";
377
- const qTitlePrefix = options?.questionTitle ?? "讟计方案评审";
378
- const loaderLabel = options?.loaderLabel ?? "🧠 AI 子代理正圚分析代码并生成评审问题...";
375
+ const confirmTitle = options?.title ?? "🔍 讟计方案远问完善";
376
+ const confirmDesc = options?.description ?? "AI 䌚通过系统性远问垮䜠打磚方案从术语粟确化到蟹界条件验证确保架构决策的每䞪分支郜经过掚敲。";
377
+ const qTitlePrefix = options?.questionTitle ?? "讟计方案远问完善";
378
+ const loaderLabel = options?.loaderLabel ?? "🧠 AI 子代理正圚分析代码并生成远问问题...";
379
379
 
380
380
  // ── Step 1: Confirm entering grill mode ──────────────────
381
381
  const enterGrill = await uiConfirm(ctx, confirmTitle, confirmDesc);
@@ -438,16 +438,16 @@ export async function runGrillPhase(
438
438
 
439
439
  const choice = await uiSelect(
440
440
  ctx,
441
- "⚠ AI 未胜成功生成评审问题",
441
+ "⚠ AI 未胜成功生成远问问题",
442
442
  [
443
- "🔄 重新尝试生成评审问题",
443
+ "🔄 重新尝试生成远问问题",
444
444
  "⏭ 跳过 Grill盎接发送 Prompt",
445
445
  "❌ 取消 (Esc)",
446
446
  ],
447
447
  );
448
448
 
449
449
  switch (choice) {
450
- case "🔄 重新尝试生成评审问题": {
450
+ case "🔄 重新尝试生成远问问题": {
451
451
  const retryPath = grillOutputPath(ctx.cwd);
452
452
  const errorFeedback = parseErrorMsg
453
453
  ? [
@@ -513,13 +513,13 @@ export async function runGrillPhase(
513
513
  rIdx++;
514
514
  }
515
515
  const qaBlock = pairs
516
- .map((p, i) => `[评审问题 ${i + 1}]\n问题: ${p.question}\n回答: ${p.answer}`)
516
+ .map((p, i) => `[远问问题 ${i + 1}]\n问题: ${p.question}\n回答: ${p.answer}`)
517
517
  .join("\n\n");
518
518
  const finalEnhancedPrompt = [
519
519
  assembledPrompt,
520
520
  "",
521
521
  "---",
522
- "## 讟计评审记圕",
522
+ "## 讟计远问记圕",
523
523
  "",
524
524
  "以䞋是圚匀发前进行的讟计评审问答所有决策已确讀",
525
525
  "",
@@ -572,16 +572,16 @@ export async function runGrillPhase(
572
572
 
573
573
  // ── Step 6: Assemble enhanced prompt ─────────────────────
574
574
  const qaBlock = pairs
575
- .map((p, i) => `[评审问题 ${i + 1}]\n问题: ${p.question}\n回答: ${p.answer}`)
575
+ .map((p, i) => `[远问问题 ${i + 1}]\n问题: ${p.question}\n回答: ${p.answer}`)
576
576
  .join("\n\n");
577
577
 
578
578
  const finalEnhancedPrompt = [
579
579
  assembledPrompt,
580
580
  "",
581
581
  "---",
582
- "## 讟计评审记圕",
582
+ "## 方案远问记圕",
583
583
  "",
584
- "以䞋是圚匀发前进行的讟计评审问答所有决策已确讀",
584
+ "以䞋是圚匀发前进行的远问匏方案打磚记圕所有决策已确讀",
585
585
  "",
586
586
  qaBlock,
587
587
  ].join("\n");
@@ -615,7 +615,7 @@ async function showQuestionTUI(
615
615
  q: GrillQuestion,
616
616
  currentIndex: number,
617
617
  totalCount: number,
618
- titlePrefix = "讟计方案评审",
618
+ titlePrefix = "讟计方案远问完善",
619
619
  backable = false,
620
620
  previousAnswer?: string,
621
621
  ): Promise<string | null> {
@@ -707,8 +707,8 @@ async function showQuestionTUI(
707
707
 
708
708
  container.addChild(new Spacer(1));
709
709
  const hint = backable && currentIndex > 1
710
- ? " ↑↓ 富航 • Enter 选择 • Ctrl+Shift+← 返回䞊䞀题 • Esc 取消党郚评审"
711
- : " ↑↓ 富航 • Enter 选择 • Esc 取消党郚评审";
710
+ ? " ↑↓ 富航 • Enter 选择 • Ctrl+Shift+← 返回䞊䞀题 • Esc 取消党郚远问"
711
+ : " ↑↓ 富航 • Enter 选择 • Esc 取消党郚远问";
712
712
  container.addChild(
713
713
  new Text(theme.fg("dim", hint), 0, 0),
714
714
  );
@@ -447,13 +447,19 @@ function captureBaseline(cwd: string): void {
447
447
  *
448
448
  * Uses git-format status codes (M, A, D) for display consistency.
449
449
  * Deduplicates against existing _workflowFileChanges.
450
+ *
451
+ * The dedup key includes stepIndex (filePath:stepIndex) so the same file
452
+ * can appear across different workflow steps (e.g., created by worker in
453
+ * loop-group, then reviewed by reviewer) without being collapsed into one.
454
+ * Without :stepIndex, the Set dedup would wrongly skip a file modified in
455
+ * a later step just because it was already tracked in an earlier step.
450
456
  */
451
457
  function updateToolsFromGit(cwd: string, stepIndex: number, agentName: string): void {
452
458
  const currentChanges = getGitDiffChanges(cwd);
453
- const seen = new Set(_workflowFileChanges.map(c => c.filePath));
454
-
459
+ const seen = new Set(_workflowFileChanges.map(c => `${c.filePath}:${c.stepIndex}`));
460
+
455
461
  for (const change of currentChanges) {
456
- if (seen.has(change.path)) continue;
462
+ if (seen.has(`${change.path}:${stepIndex}`)) continue;
457
463
 
458
464
  // ── Baseline filtering ──────────────────────────────
459
465
  // Skip files that were already dirty at workflow start and haven't been touched.
@@ -470,7 +476,13 @@ function updateToolsFromGit(cwd: string, stepIndex: number, agentName: string):
470
476
  }
471
477
  }
472
478
 
473
- seen.add(change.path);
479
+ // Skip files in .pi-dev-output/ — these are workflow-generated artifacts (plans, reviews)
480
+ // and should NOT be reported as user repo changes. Real git clients (VSCode, Zed) also
481
+ // ignore this directory (it's in .gitignore). This filter is defense-in-depth.
482
+ // Note: git always uses forward slashes in paths, even on Windows.
483
+ if (change.path.startsWith(".pi-dev-output/")) continue;
484
+
485
+ seen.add(`${change.path}:${stepIndex}`);
474
486
  const type: FileChangeEntry["type"] =
475
487
  change.status === "A" ? "new" :
476
488
  change.status === "D" ? "delete" :
@@ -883,47 +895,6 @@ function addWidgetSubStepTool(stepIndex: number, agentName: string, tool: string
883
895
  if (!sub.tools) sub.tools = [];
884
896
  sub.tools.push(tool);
885
897
  if (sub.tools.length > 20) sub.tools = sub.tools.slice(-20); // keep last 20
886
-
887
- // Also track as file change for checkpoint
888
- // Support both old format ("edit: path") and new git-format ("M path", "A path", "D path")
889
- const oldMatch = tool.match(/^(edit|new|delete|read):\s*(.+)/i);
890
- const gitMatch = !oldMatch ? tool.match(/^([MAD])\s{2,}(.+)$/) : null;
891
- if (oldMatch) {
892
- const changeType = oldMatch[1]!.toLowerCase() as FileChangeEntry["type"];
893
- const filePath = oldMatch[2]!.trim();
894
- const exists = _workflowFileChanges.some(
895
- c => c.filePath === filePath && c.type === changeType && c.stepIndex === stepIndex && c.agent === agentName,
896
- );
897
- if (!exists && filePath.length > 3) {
898
- _workflowFileChanges.push({
899
- agent: agentName,
900
- stepIndex,
901
- type: changeType,
902
- filePath,
903
- timestamp: new Date().toISOString(),
904
- });
905
- }
906
- } else if (gitMatch) {
907
- const gitStatus = gitMatch[1]!;
908
- const changeType: FileChangeEntry["type"] =
909
- gitStatus === "A" ? "new" :
910
- gitStatus === "D" ? "delete" :
911
- "edit";
912
- const filePath = gitMatch[2]!.trim();
913
- const exists = _workflowFileChanges.some(
914
- c => c.filePath === filePath && c.type === changeType && c.stepIndex === stepIndex && c.agent === agentName,
915
- );
916
- if (!exists && filePath.length > 3) {
917
- _workflowFileChanges.push({
918
- agent: agentName,
919
- stepIndex,
920
- type: changeType,
921
- filePath,
922
- timestamp: new Date().toISOString(),
923
- });
924
- }
925
- }
926
-
927
898
  refreshWidget();
928
899
  }
929
900
  }
@@ -1130,7 +1101,7 @@ async function runAgentWithProgress(
1130
1101
  const result = await spawnSubagent(agent, task, _workflowCwd, signal, timeoutMs, (progress) => {
1131
1102
  // Try to parse tool calls from progress messages
1132
1103
  // Only match if it looks like a file path (contains a dot or path separator)
1133
- const toolMatch = progress.match(/(edit|read|write|new|bash|grep|find|ls|delete|remove)\s*[:]\s*(\S+)/i);
1104
+ const toolMatch = progress.match(/(edit|read|write|new|bash|grep|find|ls|delete|remove)\s*[:]\s*([\w./\\-]+)/i);
1134
1105
  if (toolMatch) {
1135
1106
  const toolType = toolMatch[1]!.toLowerCase();
1136
1107
  const target = toolMatch[2]!;
@@ -1141,12 +1112,14 @@ async function runAgentWithProgress(
1141
1112
  _widgetExtraToolCount++;
1142
1113
  }
1143
1114
  }
1144
- // Detect output file paths — ONLY match explicit .pi-dev-output paths, never free-form text
1145
- const outputMatch = progress.match(/\.pi-dev-output\/[^\s,;)\]}]{5,}\.\w+/i);
1146
- if (outputMatch) {
1147
- const pathCandidate = outputMatch[0]!.trim();
1148
- if (pathCandidate.length > 15 && pathCandidate.length < 300) {
1149
- addWidgetSubStepOutput(stepIndex, agentName, pathCandidate);
1115
+ // Detect output file paths — only match .pi-dev-output paths belonging to current workflow
1116
+ if (_workflowId) {
1117
+ const outputMatch = progress.match(/\.pi-dev-output\/[^\s,;)\]}'"`]{5,}\.\w+/i);
1118
+ if (outputMatch) {
1119
+ const pathCandidate = outputMatch[0]!.trim().replace(/["'`\\]+$/g, '');
1120
+ if (pathCandidate.length > 15 && pathCandidate.length < 300 && pathCandidate.includes(_workflowId)) {
1121
+ addWidgetSubStepOutput(stepIndex, agentName, pathCandidate);
1122
+ }
1150
1123
  }
1151
1124
  }
1152
1125
  }, _workflowId ? { workflowId: _workflowId } : undefined);
@@ -1163,127 +1136,47 @@ async function runAgentWithProgress(
1163
1136
  toolCount: _widgetExtraToolCount,
1164
1137
  });
1165
1138
 
1166
- // ── Post-completion: parse subagent output for tool calls and file paths ──
1167
- // Progress messages from spawnSubagent rarely contain tool info,
1168
- // so we scan the full output after completion.
1139
+ // ── Post-completion: file change detection via git diff ONLY ──
1140
+ // We do NOT parse agent output text for file paths — that approach is fragile and
1141
+ // produces false positives (e.g., matching JS property names like `.push`, `.length`
1142
+ // from code snippets). Instead, we rely entirely on git diff --name-status, which is
1143
+ // the same approach used by VSCode, Zed, and every other professional git client.
1144
+ // It's deterministic, noise-free, and always produces correct relative paths.
1169
1145
  const allOutput = (result.output || "") + "\n" + (result.stderr || "");
1170
1146
  const finalOutput = extractFinalOutput(result.output) || result.output;
1171
1147
  const searchText = allOutput + "\n" + finalOutput;
1172
1148
 
1173
- // Detect file creation/modification patterns from agent's final output text
1174
- // The agent's response typically lists files using markdown backticks or bullet points
1175
- const filePatterns = [
1176
- // Markdown code blocks with file paths: `src/main.rs`, `path/to/file.ts`
1177
- /`([^`]+\.[a-zA-Z0-9_]+)`/g,
1178
- // Bullet points with file operation verbs: - Modify `src/main.rs`, * Created `file.ts`
1179
- /(?:^|\n)\s*[-*]\s*(?:modified|created|updated|edited|added|deleted|removed|changed|wrote|writes?)\s*[`"']?([^`"'\n,]+\.[a-zA-Z0-9_]+)[`"']?/gim,
1180
- // Descriptive: "I've modified src/main.rs", "reading config.json"
1181
- /(?:modified|created|updated|edited|added|deleted|removed|changed|wrote|write|writes|read|reads?)\s+(?:the\s+)?[`"']?([^`"'\n,]+\.[a-zA-Z0-9_]+)[`"']?/gi,
1182
- // Chinese patterns
1183
- /(?:猖写|创建|修改|删陀|读取|写入|曎新)\s*(?:了|文件)?\s*[:]?\s*[`"']?([^`"'\s,]+\.[a-zA-Z0-9_]+)[`"']?/gi,
1184
- // File path with action prefix: "edit: src/file.ts", "new: src/file.ts"
1185
- /(?:^|\n)\s*(?:edit|new|delete|read|modify|create|update|add|remove)\s*[:]\s*([^\n]+\.[a-zA-Z0-9_]+)/gim,
1186
- ];
1187
- const seenTools = new Set<string>();
1188
- for (const pattern of filePatterns) {
1149
+ // Find output file paths (plans, reviews) for display in the "output:" section
1150
+ // These are workflow artifacts (.pi-dev-output/) — only show files belonging to
1151
+ // the CURRENT workflow to avoid cross-contamination from previous runs.
1152
+ // Note: file change detection itself relies exclusively on git diff below, not text parsing.
1153
+ const seenOutputs = new Set<string>();
1154
+ if (_workflowId) {
1155
+ // Only match .pi-dev-output paths containing the current workflow UUID
1156
+ const escapedId = _workflowId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1157
+ const outputPattern = new RegExp(`\\.pi-dev-output\\/[^\\s,;)\\]}'"\\`]*${escapedId}[^\\s,;)\\]}'"\\`]*`, 'g');
1189
1158
  let m;
1190
- while ((m = pattern.exec(searchText)) !== null) {
1191
- const filePath = m[1]!.trim()
1192
- .replace(/[`'"\)\(\]]+$/, "")
1193
- .replace(/^[`'"\)\(\[]+/, "")
1194
- .split(/[\s,;]/)[0]!;
1195
- // Validate it's a real file path
1196
- if (filePath.length > 3 && filePath.length < 300 && !seenTools.has(filePath)) {
1197
- // Skip common non-file matches
1198
- if (filePath.match(/^(the|a|an|this|that|it|its|my|your|our|their|some|any|all|each|every|both|few|many|several|most|other|another|such|what|which|whose|whom|when|where|why|how|who|being|having|doing|making|taking|giving|getting|setting|using|running|going|coming|looking|finding|keeping|putting)/i)) continue;
1199
- if (filePath.startsWith("http")) continue;
1200
- if (filePath.length < 6 && !filePath.includes("/")) continue;
1201
-
1202
- // 额倖过滀噚排陀明星䞍是文件路埄的脏数据
1203
- if (filePath.includes("${") || filePath.includes("\\n") || filePath.includes("\\t")) continue; // 排陀暡板字笊䞲和蜬义字笊
1204
- if (filePath.includes("[]") || filePath.includes("{}")) continue; // 排陀数组/对象字面量
1205
- if (filePath.match(/^[\s,;)\]}]+$/)) continue; // 排陀纯笊号
1206
- seenTools.add(filePath);
1207
- const fullMatch = m[0]!.toLowerCase();
1208
- // Determine operation type and convert to git status
1209
- let toolType = "edit";
1210
- if (fullMatch.includes("write") || fullMatch.includes("创建") || fullMatch.includes("new") || fullMatch.includes("add") || fullMatch.includes("created") || fullMatch.includes("added")) {
1211
- toolType = "new";
1212
- } else if (fullMatch.includes("delete") || fullMatch.includes("删陀") || fullMatch.includes("remove") || fullMatch.includes("deleted") || fullMatch.includes("removed")) {
1213
- toolType = "delete";
1214
- } else if (fullMatch.includes("read") || fullMatch.includes("读取")) {
1215
- toolType = "read";
1216
- }
1217
- if (toolType !== "read") {
1218
- const gitStatus = toGitStatus(toolType);
1219
- addWidgetSubStepTool(stepIndex, agentName, `${gitStatus} ${filePath}`);
1220
- _widgetExtraToolCount++;
1221
- }
1159
+ while ((m = outputPattern.exec(searchText)) !== null) {
1160
+ const path_ = m[0]!.trim();
1161
+ if (path_.length > 15 && path_.length < 300 && !seenOutputs.has(path_)) {
1162
+ seenOutputs.add(path_);
1163
+ addWidgetSubStepOutput(stepIndex, agentName, path_);
1222
1164
  }
1223
1165
  }
1224
- }
1225
-
1226
- // If we found no file tools from text patterns, try alternative approaches
1227
- // Look for explicit tool call patterns in the raw JSON output
1228
- if (seenTools.size === 0) {
1229
- const jsonLines = (result.output || "").split("\n");
1230
- for (const line of jsonLines) {
1231
- try {
1232
- const event = JSON.parse(line);
1233
- // Look for tool_use events in the JSON stream
1234
- if (event.type === "message_update" && event.assistantMessageEvent?.type === "tool_use") {
1235
- const toolName = event.assistantMessageEvent.name;
1236
- const args = event.assistantMessageEvent.args || {};
1237
- // write tool: args contains file_path
1238
- if (toolName === "write" && args.file_path) {
1239
- const fp = args.file_path.trim();
1240
- if (!seenTools.has(fp)) {
1241
- seenTools.add(fp);
1242
- addWidgetSubStepTool(stepIndex, agentName, `A ${fp}`);
1243
- _widgetExtraToolCount++;
1244
- }
1245
- }
1246
- // edit tool: args contains file_path
1247
- if (toolName === "edit" && args.file_path) {
1248
- const fp = args.file_path.trim();
1249
- if (!seenTools.has(fp)) {
1250
- seenTools.add(fp);
1251
- addWidgetSubStepTool(stepIndex, agentName, `M ${fp}`);
1252
- _widgetExtraToolCount++;
1253
- }
1254
- }
1255
- }
1256
- } catch { /* not JSON, skip */ }
1257
- }
1258
- }
1259
-
1260
- // Find output file paths (.pi-dev-output, review reports, plan files)
1261
- // NOTE: Only match explicit paths in .pi-dev-output/ or known review/plan file patterns.
1262
- // The old pattern matching loose "output:" text was the root cause of "output::0.00004508" noise.
1263
- // File change detection now relies on git diff --name-status (updateToolsFromGit below),
1264
- // which is deterministic and noise-free.
1265
- const outputPathPatterns = [
1266
- // Direct reference to .pi-dev-output paths: ".pi-dev-output/pi-plans/xxx.md"
1267
- /\.pi-dev-output\/[a-zA-Z0-9_\/-]+\.[a-zA-Z0-9]+/g,
1268
- // Review file patterns: "review-20260520-162800.md"
1269
- /review-\d{8}-\d{6}\.md/g,
1270
- // Plan file patterns: "20260520-1628-*.md"
1271
- /\d{8}-\d{4,6}-[a-zA-Z0-9_-]+\.md/g,
1272
- ];
1273
- const seenOutputs = new Set<string>();
1274
- for (const pattern of outputPathPatterns) {
1166
+ } else {
1167
+ // No workflow ID (unlikely): match .pi-dev-output paths conservatively
1168
+ const outputPattern = /\.pi-dev-output\/[a-zA-Z0-9_\/-]+\.[a-zA-Z0-9]+/g;
1275
1169
  let m;
1276
- while ((m = pattern.exec(searchText)) !== null) {
1170
+ while ((m = outputPattern.exec(searchText)) !== null) {
1277
1171
  const path_ = m[0]!.trim();
1278
- if (path_.length > 10 && path_.length < 300 && !seenOutputs.has(path_)) {
1172
+ if (path_.length > 15 && path_.length < 300 && !seenOutputs.has(path_)) {
1279
1173
  seenOutputs.add(path_);
1280
1174
  addWidgetSubStepOutput(stepIndex, agentName, path_);
1281
1175
  }
1282
1176
  }
1283
1177
  }
1284
1178
 
1285
-
1286
- // ── Update file changes from git diff (more accurate than text scraping) ──
1179
+ // ── Update file changes from git diff (more accurate than text scraping) ──
1287
1180
  updateToolsFromGit(_workflowCwd, stepIndex, agentName);
1288
1181
  // Update sub-step status based on result
1289
1182
  const subStatus: WorkflowSubStepWidgetState["status"] =
@@ -1350,41 +1243,19 @@ async function executeSingleStep(
1350
1243
  throw new Error(`Agent 错误 (exit ${result.exitCode}): ${result.stderr.slice(0, 500)}`);
1351
1244
  }
1352
1245
 
1353
- // ── Capture chain context for single-step agents ──
1354
- const agentChanges = _workflowFileChanges
1355
- .filter(c => c.stepIndex === stepIndex && c.agent === agentName)
1356
- .map(c => `${c.type === "new" ? "A" : c.type === "delete" ? "D" : "M"} ${c.filePath}`);
1357
-
1358
- let chainKey: string;
1359
- if (agentName === "planner") chainKey = "计划制定摘芁";
1360
- else if (agentName === "docWriter") chainKey = "文档曎新摘芁";
1361
- else chainKey = `${agentName} 执行摘芁`;
1362
-
1363
- if (agentChanges.length > 0) {
1364
- const editCount = agentChanges.filter(c => c.startsWith("M")).length;
1365
- const newCount = agentChanges.filter(c => c.startsWith("A")).length;
1366
- const delCount = agentChanges.filter(c => c.startsWith("D")).length;
1367
- const statsParts: string[] = [];
1368
- if (editCount > 0) statsParts.push(`修改 ${editCount} 䞪`);
1369
- if (newCount > 0) statsParts.push(`新增 ${newCount} 䞪`);
1370
- if (delCount > 0) statsParts.push(`删陀 ${delCount} 䞪`);
1371
- const statsLine = statsParts.length > 0 ? `改劚统计: ${statsParts.join("")}\n\n` : "";
1372
-
1373
- updateChainContext(chainKey,
1374
- `${agentName} 已完成。\n` +
1375
- statsLine +
1376
- `变曎文件列衚:\n${agentChanges.join("\n")}`
1377
- );
1378
- } else {
1379
- updateChainContext(chainKey, `${agentName} 已完成执行未检测到文件变曎。`);
1380
- }
1381
-
1382
1246
  // ── Capture AI work summary as supplementary chain context ──
1247
+ // We intentionally do NOT create chain contexts from _workflowFileChanges here.
1248
+ // File-change-based chain contexts ("计划制定摘芁"/"文档曎新摘芁") were removed in v0.6.0
1249
+ // because their M/A/D counts provided negligible value for downstream agents compared
1250
+ // to the agent's own free-text work summary. The file-change approach also created
1251
+ // tight coupling between executeSingleStep and the _workflowFileChanges data structure.
1383
1252
  const workSummary = extractFinalOutput(result.output);
1384
1253
  if (workSummary) {
1385
- updateChainContext(`${agentName} 工䜜总结`,
1386
- `${agentName} 已完成工䜜以䞋是其工䜜总结\n\n${workSummary}`
1387
- );
1254
+ updateChainContext(`${agentName} 工䜜总结`, `${agentName} 已完成工䜜以䞋是其工䜜总结\n\n${workSummary}`);
1255
+ } else if (result.output) {
1256
+ // 保底䜿甚蟓出前 200 字笊䜜䞺简略摘芁
1257
+ const fallback = result.output.slice(0, 200).trim();
1258
+ if (fallback) updateChainContext(`${agentName} 工䜜总结`, fallback);
1388
1259
  }
1389
1260
  }
1390
1261
 
@@ -1448,37 +1319,6 @@ async function executeLoopGroup(
1448
1319
  }
1449
1320
  }
1450
1321
 
1451
- // ── After loop agent completes: capture changes + stats for chain context ──
1452
- const loopAgentChanges = _workflowFileChanges
1453
- .filter(c => c.stepIndex === stepIndex && c.agent === step.loopAgentName)
1454
- .map(c => `${c.type === "new" ? "A" : c.type === "delete" ? "D" : "M"} ${c.filePath}`);
1455
-
1456
- // Use agent-specific keys so worker and trimmer changes coexist without overwriting
1457
- const chainKey = step.loopAgentName === "worker" ? "代码实斜摘芁" : "代码粟简摘芁";
1458
-
1459
- if (loopAgentChanges.length > 0) {
1460
- const editCount = loopAgentChanges.filter(c => c.startsWith("M")).length;
1461
- const newCount = loopAgentChanges.filter(c => c.startsWith("A")).length;
1462
- const delCount = loopAgentChanges.filter(c => c.startsWith("D")).length;
1463
- const statsParts: string[] = [];
1464
- if (editCount > 0) statsParts.push(`修改 ${editCount} 䞪`);
1465
- if (newCount > 0) statsParts.push(`新增 ${newCount} 䞪`);
1466
- if (delCount > 0) statsParts.push(`删陀 ${delCount} 䞪`);
1467
- const statsLine = statsParts.length > 0 ? `改劚统计: ${statsParts.join("")}\n\n` : "";
1468
-
1469
- updateChainContext(chainKey,
1470
- `${step.loopAgentName === "worker" ? "代码实斜" : "代码粟简"}已完成。\n` +
1471
- statsLine +
1472
- `变曎文件列衚:\n${loopAgentChanges.join("\n")}` +
1473
- (planFileRelPath ? `\n\n实斜计划: ${planFileRelPath}\n(可圚 .pi-dev-output/pi-plans/ äž­ grep UUID ${_workflowId} 扟到)` : "")
1474
- );
1475
- } else {
1476
- updateChainContext(chainKey,
1477
- `${step.loopAgentName} 已完成执行未检测到文件变曎。\n` +
1478
- (planFileRelPath ? `实斜计划: ${planFileRelPath}\n(可圚 .pi-dev-output/pi-plans/ äž­ grep UUID ${_workflowId} 扟到)` : "")
1479
- );
1480
- }
1481
-
1482
1322
  // ── Capture loop agent work summary as supplementary chain context ──
1483
1323
  const loopFinalText = extractFinalOutput(agentResult.output);
1484
1324
  if (loopFinalText) {
@@ -1525,25 +1365,11 @@ async function executeLoopGroup(
1525
1365
  }
1526
1366
  }
1527
1367
 
1528
- // ── After reviewer: capture review context for next loop ──
1529
- // Use agent-specific key so worker-reviewer and trimmer-reviewer feedback don't interfere
1530
- const reviewChainKey = step.loopAgentName === "worker" ? "代码审查反銈" : "粟简审查反銈";
1531
- if (reviewSummary) {
1532
- const reviewCountParts: string[] = [];
1533
- if (reviewSummary.critical > 0) reviewCountParts.push(`䞥重 ${reviewSummary.critical} 䞪`);
1534
- if (reviewSummary.medium > 0) reviewCountParts.push(`侭等 ${reviewSummary.medium} 䞪`);
1535
- if (reviewSummary.low > 0) reviewCountParts.push(`䜎 ${reviewSummary.low} 䞪`);
1536
- const reviewStats = reviewCountParts.length > 0 ? `发现 ${reviewCountParts.join("")} 问题。` : "未发现问题。";
1537
-
1538
- updateChainContext(reviewChainKey,
1539
- `${reviewStats}\n` +
1540
- `完敎审查报告圚 .pi-dev-output/pi-review/md/ 目圕䞭\n` +
1541
- `请圚工䜜流蟓出目圕䞭 grep UUID "${_workflowId}" 查扟最新报告。`
1542
- );
1543
- }
1544
-
1545
1368
  // ── Capture reviewer work summary as supplementary chain context ──
1546
- // Reuse already-parsed extractedOutput to avoid double parsing
1369
+ // The old review-chain-context entries ("代码审查反銈"/"粟简审查反銈") based on
1370
+ // _workflowFileChanges were removed in v0.6.0 because they duplicated information
1371
+ // from the review report file and polluted chain context with file-path-heavy data.
1372
+ // Reuse already-parsed extractedOutput to avoid double parsing.
1547
1373
  if (extractedOutput) {
1548
1374
  updateChainContext(`审查工䜜总结`,
1549
1375
  `审查者已完成审查以䞋是其审查总结\n\n${extractedOutput}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghyper9023/pi-dev-workflow",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "keywords": [
5
5
  "pi-package"
6
6
  ],