@xdevops/issue-auto-finish 1.0.62 → 1.0.64

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 (118) hide show
  1. package/dist/{LockNote-53KFIXKS.js → LockNote-6EWVEN4I.js} +3 -3
  2. package/dist/{analyze-F5DFTM2A.js → analyze-4EIZPNAO.js} +2 -2
  3. package/dist/{braindump-JZ2CJC2I.js → braindump-FOMGOB3E.js} +5 -5
  4. package/dist/{chunk-SH5RIZIS.js → chunk-DSXXSWKK.js} +188 -7
  5. package/dist/chunk-DSXXSWKK.js.map +1 -0
  6. package/dist/{chunk-MEDXZ5JM.js → chunk-EB6TGE3H.js} +7 -1
  7. package/dist/chunk-EB6TGE3H.js.map +1 -0
  8. package/dist/{chunk-BXCTN44G.js → chunk-G3VAYLSN.js} +1161 -274
  9. package/dist/chunk-G3VAYLSN.js.map +1 -0
  10. package/dist/{chunk-NPCR4ZNN.js → chunk-GZ4WQCVM.js} +458 -106
  11. package/dist/chunk-GZ4WQCVM.js.map +1 -0
  12. package/dist/{chunk-4D5GYH4P.js → chunk-O5JTJWDH.js} +78 -4
  13. package/dist/chunk-O5JTJWDH.js.map +1 -0
  14. package/dist/{chunk-WBHUOIEV.js → chunk-PDPVW66P.js} +13 -5
  15. package/dist/chunk-PDPVW66P.js.map +1 -0
  16. package/dist/{chunk-NKJGLRZS.js → chunk-SF2CFEWJ.js} +2 -2
  17. package/dist/{chunk-6Q7ITCA4.js → chunk-XAJ7ZZGA.js} +2 -2
  18. package/dist/{chunk-UBRYQRTJ.js → chunk-YRPHDIBG.js} +2 -2
  19. package/dist/{chunk-MSZQ3T4G.js → chunk-YSADBTGK.js} +2 -2
  20. package/dist/cli/setup/ConfigGenerator.d.ts +4 -0
  21. package/dist/cli/setup/ConfigGenerator.d.ts.map +1 -1
  22. package/dist/cli.js +7 -7
  23. package/dist/{config-U4BU2FHH.js → config-ZDBHBVDT.js} +2 -2
  24. package/dist/config-schema.d.ts +22 -0
  25. package/dist/config-schema.d.ts.map +1 -1
  26. package/dist/config.d.ts +19 -1
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/{doctor-TRERBG3Q.js → doctor-5ITA6CG7.js} +3 -3
  29. package/dist/e2e/E2EBrowserRunner.d.ts +113 -0
  30. package/dist/e2e/E2EBrowserRunner.d.ts.map +1 -0
  31. package/dist/e2e/E2EScriptRunner.d.ts +35 -0
  32. package/dist/e2e/E2EScriptRunner.d.ts.map +1 -0
  33. package/dist/e2e/E2ETestHelper.d.ts +35 -0
  34. package/dist/e2e/E2ETestHelper.d.ts.map +1 -0
  35. package/dist/e2e/index.d.ts +12 -0
  36. package/dist/e2e/index.d.ts.map +1 -0
  37. package/dist/git/WorktreeContext.d.ts +27 -0
  38. package/dist/git/WorktreeContext.d.ts.map +1 -1
  39. package/dist/i18n/locales/en.d.ts.map +1 -1
  40. package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +9 -9
  43. package/dist/{init-USNTJBIC.js → init-5SYKAFZO.js} +5 -5
  44. package/dist/lib.d.ts +2 -0
  45. package/dist/lib.d.ts.map +1 -1
  46. package/dist/lib.js +487 -4
  47. package/dist/lib.js.map +1 -1
  48. package/dist/orchestrator/IssueProcessingContext.d.ts +24 -1
  49. package/dist/orchestrator/IssueProcessingContext.d.ts.map +1 -1
  50. package/dist/orchestrator/PipelineOrchestrator.d.ts +13 -1
  51. package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
  52. package/dist/orchestrator/steps/CompletionStep.d.ts +4 -3
  53. package/dist/orchestrator/steps/CompletionStep.d.ts.map +1 -1
  54. package/dist/orchestrator/steps/FailureHandler.d.ts +4 -2
  55. package/dist/orchestrator/steps/FailureHandler.d.ts.map +1 -1
  56. package/dist/orchestrator/steps/PhaseLoopStep.d.ts +1 -1
  57. package/dist/orchestrator/steps/PhaseLoopStep.d.ts.map +1 -1
  58. package/dist/orchestrator/steps/SetupStep.d.ts +2 -0
  59. package/dist/orchestrator/steps/SetupStep.d.ts.map +1 -1
  60. package/dist/persistence/PlanPersistence.d.ts +5 -1
  61. package/dist/persistence/PlanPersistence.d.ts.map +1 -1
  62. package/dist/phases/BasePhase.d.ts +23 -0
  63. package/dist/phases/BasePhase.d.ts.map +1 -1
  64. package/dist/phases/BuildPhase.d.ts +1 -1
  65. package/dist/phases/BuildPhase.d.ts.map +1 -1
  66. package/dist/phases/PlanPhase.d.ts.map +1 -1
  67. package/dist/phases/VerifyPhase.d.ts.map +1 -1
  68. package/dist/poller/IssuePoller.d.ts +2 -1
  69. package/dist/poller/IssuePoller.d.ts.map +1 -1
  70. package/dist/prompts/templates.d.ts +22 -1
  71. package/dist/prompts/templates.d.ts.map +1 -1
  72. package/dist/repo/RepoSpec.d.ts +49 -0
  73. package/dist/repo/RepoSpec.d.ts.map +1 -0
  74. package/dist/{restart-RUY67743.js → restart-GXXPL4IJ.js} +3 -3
  75. package/dist/run.js +9 -9
  76. package/dist/{start-DPGDJHIM.js → start-MK5Q4ZIC.js} +3 -3
  77. package/dist/tenant/TenantConfig.d.ts +73 -0
  78. package/dist/tenant/TenantConfig.d.ts.map +1 -0
  79. package/dist/tenant/TenantRegistry.d.ts +41 -0
  80. package/dist/tenant/TenantRegistry.d.ts.map +1 -0
  81. package/dist/tenant/index.d.ts +5 -0
  82. package/dist/tenant/index.d.ts.map +1 -0
  83. package/dist/tracker/IssueState.d.ts +4 -0
  84. package/dist/tracker/IssueState.d.ts.map +1 -1
  85. package/dist/tracker/IssueTracker.d.ts +2 -1
  86. package/dist/tracker/IssueTracker.d.ts.map +1 -1
  87. package/dist/web/routes/api.d.ts.map +1 -1
  88. package/dist/web/routes/setup.d.ts.map +1 -1
  89. package/dist/workspace/WorkspaceConfig.d.ts +57 -0
  90. package/dist/workspace/WorkspaceConfig.d.ts.map +1 -0
  91. package/dist/workspace/WorkspaceManager.d.ts +74 -0
  92. package/dist/workspace/WorkspaceManager.d.ts.map +1 -0
  93. package/dist/workspace/index.d.ts +5 -0
  94. package/dist/workspace/index.d.ts.map +1 -0
  95. package/package.json +5 -2
  96. package/src/web/frontend/dist/assets/index-BagNsMLv.js +125 -0
  97. package/src/web/frontend/dist/assets/index-DxXyMMb9.css +1 -0
  98. package/src/web/frontend/dist/index.html +2 -2
  99. package/dist/chunk-4D5GYH4P.js.map +0 -1
  100. package/dist/chunk-BXCTN44G.js.map +0 -1
  101. package/dist/chunk-MEDXZ5JM.js.map +0 -1
  102. package/dist/chunk-NPCR4ZNN.js.map +0 -1
  103. package/dist/chunk-SH5RIZIS.js.map +0 -1
  104. package/dist/chunk-WBHUOIEV.js.map +0 -1
  105. package/src/web/frontend/dist/assets/index-BqzWCv1L.css +0 -1
  106. package/src/web/frontend/dist/assets/index-RawqoOth.js +0 -125
  107. /package/dist/{LockNote-53KFIXKS.js.map → LockNote-6EWVEN4I.js.map} +0 -0
  108. /package/dist/{analyze-F5DFTM2A.js.map → analyze-4EIZPNAO.js.map} +0 -0
  109. /package/dist/{braindump-JZ2CJC2I.js.map → braindump-FOMGOB3E.js.map} +0 -0
  110. /package/dist/{chunk-NKJGLRZS.js.map → chunk-SF2CFEWJ.js.map} +0 -0
  111. /package/dist/{chunk-6Q7ITCA4.js.map → chunk-XAJ7ZZGA.js.map} +0 -0
  112. /package/dist/{chunk-UBRYQRTJ.js.map → chunk-YRPHDIBG.js.map} +0 -0
  113. /package/dist/{chunk-MSZQ3T4G.js.map → chunk-YSADBTGK.js.map} +0 -0
  114. /package/dist/{config-U4BU2FHH.js.map → config-ZDBHBVDT.js.map} +0 -0
  115. /package/dist/{doctor-TRERBG3Q.js.map → doctor-5ITA6CG7.js.map} +0 -0
  116. /package/dist/{init-USNTJBIC.js.map → init-5SYKAFZO.js.map} +0 -0
  117. /package/dist/{restart-RUY67743.js.map → restart-GXXPL4IJ.js.map} +0 -0
  118. /package/dist/{start-DPGDJHIM.js.map → start-MK5Q4ZIC.js.map} +0 -0
@@ -5,8 +5,8 @@ import {
5
5
  findLockNote,
6
6
  isLockStale,
7
7
  parseLockNote
8
- } from "./chunk-UBRYQRTJ.js";
9
- import "./chunk-MEDXZ5JM.js";
8
+ } from "./chunk-YRPHDIBG.js";
9
+ import "./chunk-EB6TGE3H.js";
10
10
  export {
11
11
  buildLockNoteBody,
12
12
  buildReleaseNoteBody,
@@ -15,4 +15,4 @@ export {
15
15
  isLockStale,
16
16
  parseLockNote
17
17
  };
18
- //# sourceMappingURL=LockNote-53KFIXKS.js.map
18
+ //# sourceMappingURL=LockNote-6EWVEN4I.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadConfig
3
- } from "./chunk-4D5GYH4P.js";
3
+ } from "./chunk-O5JTJWDH.js";
4
4
  import {
5
5
  analyze
6
6
  } from "./chunk-B7TVVODN.js";
@@ -72,4 +72,4 @@ async function analyzeCommand(opts) {
72
72
  export {
73
73
  analyzeCommand
74
74
  };
75
- //# sourceMappingURL=analyze-F5DFTM2A.js.map
75
+ //# sourceMappingURL=analyze-4EIZPNAO.js.map
@@ -9,16 +9,16 @@ import {
9
9
  import {
10
10
  BraindumpOrchestrator,
11
11
  BraindumpTracker
12
- } from "./chunk-NKJGLRZS.js";
12
+ } from "./chunk-SF2CFEWJ.js";
13
13
  import {
14
14
  AsyncMutex,
15
15
  GitOperations
16
- } from "./chunk-SH5RIZIS.js";
16
+ } from "./chunk-DSXXSWKK.js";
17
17
  import "./chunk-ACVOOHAR.js";
18
- import "./chunk-MEDXZ5JM.js";
18
+ import "./chunk-EB6TGE3H.js";
19
19
  import {
20
20
  loadConfig
21
- } from "./chunk-4D5GYH4P.js";
21
+ } from "./chunk-O5JTJWDH.js";
22
22
  import "./chunk-B7TVVODN.js";
23
23
  import "./chunk-TN2SYADO.js";
24
24
  import "./chunk-DAX3FD2O.js";
@@ -204,4 +204,4 @@ ${bold("\u786E\u8BA4\u6267\u884C\uFF1F")} (${green("y")}=\u786E\u8BA4 / ${red("q
204
204
  export {
205
205
  braindumpCommand
206
206
  };
207
- //# sourceMappingURL=braindump-JZ2CJC2I.js.map
207
+ //# sourceMappingURL=braindump-FOMGOB3E.js.map
@@ -3,7 +3,10 @@ import {
3
3
  } from "./chunk-ACVOOHAR.js";
4
4
  import {
5
5
  t
6
- } from "./chunk-MEDXZ5JM.js";
6
+ } from "./chunk-EB6TGE3H.js";
7
+ import {
8
+ isMultiRepo
9
+ } from "./chunk-O5JTJWDH.js";
7
10
  import {
8
11
  KNOWLEDGE_DEFAULTS
9
12
  } from "./chunk-B7TVVODN.js";
@@ -434,6 +437,8 @@ function braindumpTaskToExecutableTask(task, batch) {
434
437
  }
435
438
 
436
439
  // src/prompts/templates.ts
440
+ import fs2 from "fs";
441
+ import path2 from "path";
437
442
  function planDir(iid) {
438
443
  return `.claude-plan/issue-${iid}`;
439
444
  }
@@ -491,6 +496,25 @@ ${s.freeText}`);
491
496
  ${parts.join("\n\n")}` : ""
492
497
  };
493
498
  }
499
+ function buildWorkspaceSection(workspace) {
500
+ if (!workspace || workspace.repos.length <= 1) return "";
501
+ const lines = [
502
+ "## \u591A\u4ED3\u5E93\u5DE5\u4F5C\u533A",
503
+ "",
504
+ "\u5F53\u524D\u5DE5\u4F5C\u533A\u5305\u542B\u591A\u4E2A\u5173\u8054\u4ED3\u5E93\uFF0C\u4F60\u53EF\u4EE5\u8DE8\u4ED3\u5E93\u8BFB\u5199\u6587\u4EF6\u3002\u5404\u4ED3\u5E93\u76F8\u5BF9\u4E8E\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u7684\u5E03\u5C40\u5982\u4E0B\uFF1A",
505
+ "",
506
+ "| \u4ED3\u5E93 | \u76F8\u5BF9\u8DEF\u5F84 | \u89D2\u8272 |",
507
+ "|------|---------|------|"
508
+ ];
509
+ for (const repo of workspace.repos) {
510
+ const relPath = repo.name + (repo.workDir !== repo.gitRootDir ? ` (\u9879\u76EE\u76EE\u5F55: ${repo.name}/${repo.workDir.slice(repo.gitRootDir.length + 1)})` : "");
511
+ const roleText = repo.role || (repo.isPrimary ? "\u4E3B\u4ED3\u5E93" : "\u5173\u8054\u4ED3\u5E93");
512
+ lines.push(`| ${repo.name} | \`${relPath}\` | ${roleText} |`);
513
+ }
514
+ lines.push("");
515
+ lines.push("\u4FEE\u6539\u4EFB\u4E00\u4ED3\u5E93\u7684\u4EE3\u7801\u65F6\uFF0C\u4F7F\u7528\u5BF9\u5E94\u4ED3\u5E93\u7684\u76F8\u5BF9\u8DEF\u5F84\u5373\u53EF\u3002\u8BF7\u5148\u9605\u8BFB\u5404\u4ED3\u5E93\u6839\u76EE\u5F55\u4E0B\u7684 CLAUDE.md\uFF08\u5982\u6709\uFF09\u4E86\u89E3\u9879\u76EE\u7EA6\u5B9A\u3002");
516
+ return lines.join("\n");
517
+ }
494
518
  function verifyPrompt(ctx) {
495
519
  const pd = planDir(ctx.issueIid);
496
520
  const kv = getKnowledgeForPrompt();
@@ -504,45 +528,58 @@ function verifyPrompt(ctx) {
504
528
  function planModeVerifyPrompt(ctx) {
505
529
  const pd = planDir(ctx.issueIid);
506
530
  const kv = getKnowledgeForPrompt();
507
- return t("prompt.planModeVerify", {
531
+ const wsSection = buildWorkspaceSection(ctx.workspace);
532
+ const base = t("prompt.planModeVerify", {
508
533
  iid: ctx.issueIid,
509
534
  title: ctx.issueTitle,
510
535
  planDir: pd,
511
536
  ...kv
512
537
  });
538
+ return wsSection ? `${base}
539
+
540
+ ${wsSection}` : base;
513
541
  }
514
542
  function planPrompt(ctx) {
515
543
  const supplementSection = ctx.supplementText ? `
516
544
 
517
545
  ${ctx.supplementText}` : "";
546
+ const wsSection = buildWorkspaceSection(ctx.workspace);
518
547
  const pd = planDir(ctx.issueIid);
519
- return t("prompt.plan", {
548
+ const base = t("prompt.plan", {
520
549
  iid: ctx.issueIid,
521
550
  title: ctx.issueTitle,
522
551
  description: ctx.issueDescription,
523
552
  supplement: supplementSection,
524
553
  planDir: pd
525
554
  });
555
+ return wsSection ? `${base}
556
+
557
+ ${wsSection}` : base;
526
558
  }
527
559
  function buildPrompt(ctx) {
528
560
  const pd = planDir(ctx.issueIid);
529
561
  const kv = getKnowledgeForPrompt();
530
- return t("prompt.build", {
562
+ const wsSection = buildWorkspaceSection(ctx.workspace);
563
+ const base = t("prompt.build", {
531
564
  iid: ctx.issueIid,
532
565
  title: ctx.issueTitle,
533
566
  planDir: pd,
534
567
  ...kv
535
568
  });
569
+ return wsSection ? `${base}
570
+
571
+ ${wsSection}` : base;
536
572
  }
537
573
  function rePlanPrompt(ctx, history) {
538
574
  const supplementSection = ctx.supplementText ? `
539
575
 
540
576
  ${ctx.supplementText}` : "";
577
+ const wsSection = buildWorkspaceSection(ctx.workspace);
541
578
  const pd = planDir(ctx.issueIid);
542
579
  const feedbackLines = history.map(
543
580
  (r) => t("prompt.rePlanRound", { round: r.round, timestamp: r.timestamp, feedback: r.feedback })
544
581
  ).join("\n\n");
545
- return t("prompt.rePlan", {
582
+ const base = t("prompt.rePlan", {
546
583
  iid: ctx.issueIid,
547
584
  title: ctx.issueTitle,
548
585
  description: ctx.issueDescription,
@@ -551,8 +588,137 @@ ${ctx.supplementText}` : "";
551
588
  feedbackLines,
552
589
  planDir: pd
553
590
  });
591
+ return wsSection ? `${base}
592
+
593
+ ${wsSection}` : base;
594
+ }
595
+ function loadUatSkill(vendorDir) {
596
+ const skillsDir = path2.join(vendorDir, ".cursor/skills");
597
+ if (fs2.existsSync(skillsDir)) {
598
+ try {
599
+ const entries = fs2.readdirSync(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
600
+ const parts = [];
601
+ for (const entry of entries) {
602
+ const skillFile = path2.join(skillsDir, entry.name, "SKILL.md");
603
+ if (fs2.existsSync(skillFile)) {
604
+ try {
605
+ parts.push(fs2.readFileSync(skillFile, "utf-8"));
606
+ } catch {
607
+ }
608
+ }
609
+ }
610
+ if (parts.length > 0) return parts.join("\n\n---\n\n");
611
+ } catch {
612
+ }
613
+ }
614
+ const fallback = path2.join(vendorDir, "SKILL.md");
615
+ if (fs2.existsSync(fallback)) {
616
+ try {
617
+ return fs2.readFileSync(fallback, "utf-8");
618
+ } catch {
619
+ }
620
+ }
621
+ return null;
554
622
  }
555
- function e2eVerifyPromptSuffix(ctx, ports) {
623
+ function e2eVerifyPromptSuffix(ctx, ports, uatTool) {
624
+ if (uatTool?.vendorDir) {
625
+ return buildOaUatPrompt(ctx, uatTool, ports);
626
+ }
627
+ return buildGenericE2ePrompt(ctx, ports);
628
+ }
629
+ function loadUatConfigPrompt(configFile) {
630
+ if (!configFile || !fs2.existsSync(configFile)) return null;
631
+ try {
632
+ const raw = JSON.parse(fs2.readFileSync(configFile, "utf-8"));
633
+ if (typeof raw.e2e_prompt === "string" && raw.e2e_prompt.trim()) {
634
+ return raw.e2e_prompt.trim();
635
+ }
636
+ } catch {
637
+ }
638
+ return null;
639
+ }
640
+ function buildOaUatPrompt(ctx, uatTool, ports) {
641
+ const vendorDir = uatTool.vendorDir;
642
+ const configFile = uatTool.configFile || (fs2.existsSync(path2.join(vendorDir, "config.json")) ? path2.join(vendorDir, "config.json") : "");
643
+ const skillContent = loadUatSkill(vendorDir);
644
+ const skillSection = skillContent ? `
645
+
646
+ <uat-skills>
647
+ ${skillContent}
648
+ </uat-skills>` : "";
649
+ const configArg = configFile ? ` --config ${configFile}` : "";
650
+ const outputDirArg = ` --output-dir ${vendorDir}/outputs/issue-${ctx.issueIid}`;
651
+ const portHint = ports ? `
652
+
653
+ **Preview \u73AF\u5883\u5DF2\u542F\u52A8\uFF1A**
654
+ - \u540E\u7AEF: http://${ports.host}:${ports.backendPort}
655
+ - \u524D\u7AEF: https://${ports.host}:${ports.frontendPort}` : "";
656
+ const customPrompt = loadUatConfigPrompt(configFile);
657
+ const cmdOverride = `
658
+
659
+ > **\u26A0\uFE0F \u547D\u4EE4\u53C2\u6570\u8986\u76D6**\uFF1A\u9879\u76EE\u6307\u5F15\u4E2D\u51FA\u73B0\u7684\u6240\u6709 \`python3 .../main.py\` \u547D\u4EE4\uFF0C\u90FD\u5FC5\u987B\u66FF\u6362\u4E3A\u4EE5\u4E0B\u6807\u51C6\u683C\u5F0F\uFF1A
660
+ > - \u6267\u884C\u811A\u672C\uFF1A\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\`
661
+ > - REPL \u6392\u969C\uFF1A\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} -i --headless\`
662
+ `;
663
+ const instructionSection = customPrompt ? `
664
+
665
+ ### \u9879\u76EE E2E \u6307\u5F15
666
+
667
+ ${customPrompt}
668
+ ${cmdOverride}` : "";
669
+ const defaultSteps = `
670
+ ### \u6267\u884C\u8981\u70B9
671
+
672
+ 1. \u5F71\u54CD\u9762\u5206\u6790\uFF1A\u57FA\u4E8E \`git diff\` \u5206\u6790\u672C\u6B21\u53D8\u66F4\u5F71\u54CD\u7684\u529F\u80FD\u70B9
673
+ 2. \u589E\u91CF\u5267\u672C\u751F\u6210\uFF1A\u626B\u63CF \`uat-scripts/\` \u76EE\u5F55\uFF0C\u4EC5\u4E3A\u672A\u8986\u76D6\u573A\u666F\u751F\u6210\u65B0\u5267\u672C
674
+ 3. \u6267\u884C\u9A8C\u8BC1\u811A\u672C\uFF1A
675
+ \`\`\`bash
676
+ python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless
677
+ \`\`\`
678
+ 4. \u5931\u8D25\u65F6\u4F7F\u7528 REPL \u6392\u969C\uFF1A
679
+ \`\`\`bash
680
+ python3 ${vendorDir}/main.py${configArg}${outputDirArg} -i --headless
681
+ \`\`\`
682
+ 5. \u8F93\u51FA\u7ED3\u6784\u5316\u9A8C\u8BC1\u603B\u89C8\uFF08\u5305\u542B\u622A\u56FE\u8DEF\u5F84\u3001\u6267\u884C\u7ED3\u679C\u3001\u8017\u65F6\uFF09`;
683
+ return `
684
+
685
+ ## E2E UI \u9A8C\u8BC1\uFF08\u5DF2\u542F\u7528 - OA Web UAT\uFF09
686
+
687
+ \u672C\u6B21\u53D8\u66F4\u5DF2\u5F00\u542F E2E UI \u81EA\u52A8\u9A8C\u6536\uFF0C\u4F7F\u7528 oa_pc_uat \u589E\u91CF\u9A8C\u8BC1\u6D41\u6C34\u7EBF\u3002${portHint}
688
+
689
+ **\u5DE5\u5177\u4F4D\u7F6E**: \`${vendorDir}\`
690
+ ${configFile ? `**\u914D\u7F6E\u6587\u4EF6**: \`${configFile}\`` : ""}
691
+ **\u4EA7\u7269\u8F93\u51FA\u76EE\u5F55**: \`${vendorDir}/outputs/issue-${ctx.issueIid}\`\uFF08\u6240\u6709 --script \u548C -i \u547D\u4EE4\u5FC5\u987B\u643A\u5E26 \`${outputDirArg.trim()}\` \u53C2\u6570\uFF09
692
+
693
+ \u8BF7\u4E25\u683C\u6309\u7167\u4EE5\u4E0B oa-web-uat skill \u7684\u6D41\u7A0B\u6267\u884C\u7AEF\u5BF9\u7AEF\u9A8C\u8BC1\uFF1A
694
+ ${skillSection}
695
+ ${instructionSection}
696
+ ${customPrompt ? "" : defaultSteps}
697
+
698
+ ### \u26D4 \u9636\u6BB5 Gate \u7EA6\u675F\uFF08\u5FC5\u987B\u4E25\u683C\u9075\u5B88\uFF09
699
+
700
+ E2E \u9A8C\u8BC1\u5FC5\u987B**\u4E25\u683C\u6309\u987A\u5E8F**\u7ECF\u8FC7\u4E09\u4E2A\u9636\u6BB5\uFF0C**\u7981\u6B62\u8DF3\u8FC7\u6216\u5408\u5E76**\uFF1A
701
+
702
+ **Gate 1 \u2014 \u5F71\u54CD\u9762\u5206\u6790\u5B8C\u6210\u68C0\u67E5\u70B9**
703
+ - \u5FC5\u987B\u5148\u901A\u8FC7 \`git diff\` \u5206\u6790\u51FA\u5177\u4F53\u7684\u529F\u80FD\u70B9\u6E05\u5355
704
+ - \u4EA7\u51FA\u7269\uFF1A\u529F\u80FD\u70B9\u6E05\u5355\uFF08\u5217\u51FA\u6BCF\u4E2A\u53D7\u5F71\u54CD\u7684\u529F\u80FD\u540D\u79F0\u3001\u5F71\u54CD\u6A21\u5757\u3001\u9700\u9A8C\u8BC1\u8DEF\u5F84\uFF09
705
+ - \u274C \u5982\u679C\u6CA1\u6709\u4EA7\u51FA\u529F\u80FD\u70B9\u6E05\u5355\uFF0C\u7981\u6B62\u8FDB\u5165\u9636\u6BB5 2
706
+
707
+ **Gate 2 \u2014 \u5267\u672C\u751F\u6210\u5B8C\u6210\u68C0\u67E5\u70B9**
708
+ - \u5FC5\u987B\u5148\u626B\u63CF \`uat-scripts/\` \u5DF2\u6709\u811A\u672C\uFF0C\u5224\u65AD\u589E\u91CF\u9700\u6C42
709
+ - \u5BF9\u4E8E\u9700\u8981\u65B0\u589E\u7684\u573A\u666F\uFF0C\u5FC5\u987B\u5B8C\u6210\uFF1A\u7F16\u5199 Markdown \u7528\u4F8B \u2192 \u6267\u884C \`scenario-gen\` \u751F\u6210 result.json \u2192 \u57FA\u4E8E result.json \u7F16\u5199 Python \u811A\u672C
710
+ - \u4EA7\u51FA\u7269\uFF1A\u6240\u6709\u9700\u8981\u6267\u884C\u7684 .py \u811A\u672C\u6587\u4EF6\uFF08\u5DF2\u6709 + \u65B0\u589E\uFF09\u5FC5\u987B\u5B9E\u9645\u5B58\u5728\u4E8E\u78C1\u76D8\u4E0A
711
+ - \u2705 \u9A8C\u8BC1\u65B9\u5F0F\uFF1A\`ls -la\` \u786E\u8BA4\u6BCF\u4E2A\u811A\u672C\u6587\u4EF6\u5B58\u5728
712
+ - \u274C \u5982\u679C\u811A\u672C\u6587\u4EF6\u4E0D\u5B58\u5728\u6216\u672A\u751F\u6210\uFF0C\u7981\u6B62\u8FDB\u5165\u9636\u6BB5 3
713
+
714
+ **Gate 3 \u2014 \u6267\u884C\u9636\u6BB5**
715
+ - \u53EA\u6709 Gate 2 \u901A\u8FC7\u540E\u624D\u53EF\u4EE5\u6267\u884C \`python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\`
716
+ - \u6BCF\u4E2A\u811A\u672C\u5355\u72EC\u6267\u884C\uFF0C\u9010\u4E00\u8BB0\u5F55\u7ED3\u679C
717
+ - \u5931\u8D25\u65F6\u8FDB\u5165 REPL \u6392\u969C\uFF0C\u4FEE\u590D\u540E\u91CD\u65B0\u6267\u884C
718
+
719
+ \u5C06 E2E \u6D4B\u8BD5\u7ED3\u679C\u5199\u5165\u9A8C\u8BC1\u62A5\u544A\u7684 **E2E UI \u6D4B\u8BD5\u7ED3\u679C** \u7AE0\u8282\u3002`;
720
+ }
721
+ function buildGenericE2ePrompt(ctx, ports) {
556
722
  const kv = getKnowledgeForPrompt();
557
723
  const frontendDir = kv.frontendDir;
558
724
  const e2eDir = kv.e2eDir;
@@ -597,6 +763,20 @@ function conflictResolvePrompt(ctx) {
597
763
  conflictFilesList
598
764
  });
599
765
  }
766
+ function buildMultiRepoContextSection(repos, issueIid) {
767
+ if (!isMultiRepo(repos)) return null;
768
+ const primary = repos.find((r) => r.isPrimary);
769
+ const repoLines = repos.map((r) => {
770
+ const tag = r.isPrimary ? "\uFF08\u4E3B\u4ED3\u5E93\uFF09" : "\uFF08\u5173\u8054\u4ED3\u5E93\uFF09";
771
+ return `- **${r.name}/** \u2014 ${r.projectPath} ${tag}`;
772
+ }).join("\n");
773
+ return `## \u591A\u4ED3\u5E93\u5DE5\u4F5C\u533A
774
+
775
+ \u672C\u6B21 Issue \u6D89\u53CA\u591A\u4E2A\u4EE3\u7801\u4ED3\u5E93\uFF0C\u5B83\u4EEC\u4F4D\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u7684\u5B50\u76EE\u5F55\u4E2D\uFF1A
776
+ ${repoLines}
777
+
778
+ \u8BF7\u6839\u636E\u9700\u6C42\u5728\u5BF9\u5E94\u7684\u4ED3\u5E93\u5B50\u76EE\u5F55\u4E2D\u8FDB\u884C\u4FEE\u6539\u3002\u8BA1\u5212\u6587\u4EF6\u8BF7\u7EDF\u4E00\u5199\u5728 ${primary.name}/${primary.projectSubDir ? primary.projectSubDir + "/" : ""}.claude-plan/issue-${issueIid}/ \u76EE\u5F55\u4E0B\u3002`;
779
+ }
600
780
  function issueProgressComment(phase, status, detail) {
601
781
  const emoji = {
602
782
  analysis: "\u{1F50D}",
@@ -694,8 +874,9 @@ export {
694
874
  buildPrompt,
695
875
  rePlanPrompt,
696
876
  e2eVerifyPromptSuffix,
877
+ buildMultiRepoContextSection,
697
878
  issueProgressComment,
698
879
  AsyncMutex,
699
880
  ConflictResolver
700
881
  };
701
- //# sourceMappingURL=chunk-SH5RIZIS.js.map
882
+ //# sourceMappingURL=chunk-DSXXSWKK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/git/GitOperations.ts","../src/events/EventBus.ts","../src/utils/AsyncMutex.ts","../src/tracker/BaseTracker.ts","../src/tracker/IssueRecordHelper.ts","../src/tracker/ExecutableTask.ts","../src/prompts/templates.ts","../src/git/ConflictResolver.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { logger as rootLogger } from '../logger.js';\n\nconst execFileAsync = promisify(execFile);\nconst logger = rootLogger.child('GitOperations');\n\nexport class GitOperations {\n private workDir: string;\n\n constructor(workDir: string) {\n this.workDir = workDir;\n }\n\n private async exec(args: string[]): Promise<string> {\n logger.debug('git exec', { args });\n const { stdout } = await execFileAsync('git', args, {\n cwd: this.workDir,\n maxBuffer: 10 * 1024 * 1024,\n env: { ...process.env, HUSKY: '0' },\n });\n return stdout.trim();\n }\n\n async fetchAndPull(branch: string): Promise<void> {\n await this.exec(['fetch', 'origin']);\n await this.exec(['checkout', '-f', branch]);\n await this.exec(['pull', 'origin', branch]);\n logger.info('Fetched and pulled', { branch });\n }\n\n async fetch(): Promise<void> {\n await this.exec(['fetch', 'origin']);\n logger.info('Fetched from origin');\n }\n\n async createBranch(name: string, from: string): Promise<void> {\n // Create branch from origin/<from> without needing to checkout <from> first\n // Use -f to force checkout when untracked files conflict with the target\n await this.exec(['checkout', '-f', '-b', name, `origin/${from}`]);\n logger.info('Branch created', { name, from: `origin/${from}` });\n }\n\n async checkout(branch: string): Promise<void> {\n await this.exec(['checkout', '-f', branch]);\n logger.info('Checked out', { branch });\n }\n\n async add(files: string[]): Promise<void> {\n await this.exec(['add', ...files]);\n }\n\n async commit(message: string): Promise<void> {\n await this.exec(['commit', '--no-verify', '-m', message]);\n logger.info('Committed', { message: message.slice(0, 80) });\n }\n\n async push(branch: string): Promise<void> {\n await this.exec(['push', '--no-verify', '-u', 'origin', branch]);\n logger.info('Pushed', { branch });\n }\n\n async branchExists(name: string): Promise<boolean> {\n try {\n await this.exec(['rev-parse', '--verify', name]);\n return true;\n } catch {\n return false;\n }\n }\n\n async remoteBranchExists(name: string): Promise<boolean> {\n try {\n await this.exec(['ls-remote', '--exit-code', '--heads', 'origin', name]);\n return true;\n } catch {\n return false;\n }\n }\n\n async getCurrentBranch(): Promise<string> {\n return this.exec(['rev-parse', '--abbrev-ref', 'HEAD']);\n }\n\n async stash(): Promise<void> {\n await this.exec(['stash']);\n }\n\n async stashPop(): Promise<void> {\n await this.exec(['stash', 'pop']);\n }\n\n async hasChanges(): Promise<boolean> {\n const status = await this.exec(['status', '--porcelain']);\n return status.length > 0;\n }\n\n async addAndCommit(files: string[], message: string): Promise<void> {\n await this.add(files);\n await this.commit(message);\n }\n\n async checkoutTrack(remoteBranch: string): Promise<void> {\n await this.exec(['checkout', '-f', '--track', `origin/${remoteBranch}`]);\n logger.info('Checked out remote tracking branch', { remoteBranch });\n }\n\n async addCommitAndPush(files: string[], message: string, branch: string): Promise<void> {\n await this.add(files);\n await this.commit(message);\n await this.push(branch);\n }\n\n async deleteBranch(name: string): Promise<void> {\n await this.exec(['branch', '-D', name]);\n logger.info('Branch deleted', { name });\n }\n\n async deleteRemoteBranch(name: string): Promise<void> {\n await this.exec(['push', 'origin', '--delete', name]);\n logger.info('Remote branch deleted', { name });\n }\n\n async worktreeAdd(dir: string, newBranch: string, startPoint: string): Promise<void> {\n await this.exec(['worktree', 'add', '-b', newBranch, dir, startPoint]);\n logger.info('Worktree added (new branch)', { dir, newBranch, startPoint });\n }\n\n async worktreeAddExisting(dir: string, branch: string): Promise<void> {\n await this.exec(['worktree', 'add', dir, branch]);\n logger.info('Worktree added (existing branch)', { dir, branch });\n }\n\n async worktreeAddTracking(dir: string, remoteBranch: string): Promise<void> {\n await this.exec(['worktree', 'add', '--track', '-b', remoteBranch, dir, `origin/${remoteBranch}`]);\n logger.info('Worktree added (tracking remote)', { dir, remoteBranch });\n }\n\n async worktreeRemove(dir: string, force = false): Promise<void> {\n const args = ['worktree', 'remove', dir];\n if (force) args.push('--force');\n await this.exec(args);\n logger.info('Worktree removed', { dir, force });\n }\n\n async worktreePrune(): Promise<void> {\n await this.exec(['worktree', 'prune']);\n logger.info('Worktree pruned stale entries');\n }\n\n async worktreeList(): Promise<string[]> {\n const output = await this.exec(['worktree', 'list', '--porcelain']);\n return output\n .split('\\n')\n .filter((line) => line.startsWith('worktree '))\n .map((line) => line.replace('worktree ', ''));\n }\n\n async showFile(ref: string, filePath: string): Promise<string | null> {\n try {\n return await this.exec(['show', `${ref}:${filePath}`]);\n } catch {\n return null;\n }\n }\n\n async getConflictFiles(): Promise<string[]> {\n const output = await this.exec(['diff', '--name-only', '--diff-filter=U']);\n if (!output) return [];\n return output.split('\\n').filter(Boolean);\n }\n\n async rebase(targetRef: string): Promise<{ success: boolean; conflictFiles: string[] }> {\n try {\n await this.exec(['rebase', targetRef]);\n return { success: true, conflictFiles: [] };\n } catch (err) {\n const msg = (err as Error).message || '';\n if (msg.includes('CONFLICT') || msg.includes('could not apply')) {\n const conflictFiles = await this.getConflictFiles();\n return { success: false, conflictFiles };\n }\n throw err;\n }\n }\n\n async rebaseContinue(): Promise<{ done: boolean; conflictFiles: string[] }> {\n try {\n await this.exec(['-c', 'core.editor=true', 'rebase', '--continue']);\n return { done: true, conflictFiles: [] };\n } catch (err) {\n const msg = (err as Error).message || '';\n if (msg.includes('CONFLICT') || msg.includes('could not apply')) {\n const conflictFiles = await this.getConflictFiles();\n return { done: false, conflictFiles };\n }\n throw err;\n }\n }\n\n async rebaseAbort(): Promise<void> {\n await this.exec(['rebase', '--abort']);\n logger.info('Rebase aborted');\n }\n\n async isRebaseInProgress(): Promise<boolean> {\n const status = await this.exec(['status']);\n return status.includes('rebase in progress');\n }\n\n async forcePush(branch: string): Promise<void> {\n await this.exec(['push', '--no-verify', '--force-with-lease', '-u', 'origin', branch]);\n logger.info('Force pushed', { branch });\n }\n\n async mergeFF(branch: string): Promise<void> {\n await this.exec(['merge', '--ff-only', branch]);\n logger.info('Fast-forward merged', { branch });\n }\n\n async merge(branch: string, message: string): Promise<void> {\n await this.exec(['merge', '--no-ff', '--no-verify', '-m', message, branch]);\n logger.info('Merged', { branch, message: message.slice(0, 80) });\n }\n}\n","import { EventEmitter } from 'node:events';\n\nexport type EventType =\n | 'issue:created'\n | 'issue:stateChanged'\n | 'issue:failed'\n | 'issue:deleted'\n | 'issue:resetForRetry'\n | 'issue:restarted'\n | 'issue:retryFromPhase'\n | 'poll:tick'\n | 'heartbeat'\n | 'agent:output'\n | 'pipeline:progress'\n | 'review:requested'\n | 'review:approved'\n | 'review:rejected'\n | 'conflict:started'\n | 'conflict:resolved'\n | 'conflict:failed'\n | 'update:checking'\n | 'update:available'\n | 'update:downloading'\n | 'update:completed'\n | 'update:failed'\n // Braindump events\n | 'braindump:created'\n | 'braindump:split:done'\n | 'braindump:confirmed'\n | 'braindump:task:started'\n | 'braindump:task:completed'\n | 'braindump:task:merging'\n | 'braindump:task:merged'\n | 'braindump:task:failed'\n | 'braindump:completed'\n | 'braindump:failed'\n // Distill events\n | 'distill:diary:created'\n | 'distill:started'\n | 'distill:memory:updated'\n | 'distill:rule:generated'\n | 'distill:completed'\n | 'distill:failed'\n // Verify-fix loop events\n | 'verify:loopStarted'\n | 'verify:iterationComplete'\n | 'verify:loopExhausted';\n\nexport interface EventPayload {\n type: EventType;\n data: unknown;\n timestamp: string;\n}\n\n/**\n * Typed event bus based on EventEmitter.\n *\n * Exported as a class so consumers can receive an instance via dependency\n * injection instead of relying on the global singleton.\n */\nexport class EventBus extends EventEmitter {\n emit(event: string | symbol, ...args: unknown[]): boolean {\n super.emit('*', event, ...args);\n return super.emit(event, ...args);\n }\n\n emitTyped(type: EventType, data: unknown): void {\n const payload: EventPayload = {\n type,\n data,\n timestamp: new Date().toISOString(),\n };\n this.emit(type, payload);\n }\n}\n\n/**\n * Global singleton — preserved for backward compatibility.\n *\n * New code should prefer receiving an `EventBus` instance via constructor\n * injection. The global singleton can be passed as the default value.\n */\nexport const eventBus = new EventBus();\n","/**\n * Promise-based async mutex for serializing access to shared resources\n * in a single Node.js process (e.g. mainGit operations, tracker file writes).\n */\nexport class AsyncMutex {\n private queue: Array<() => void> = [];\n private locked = false;\n\n async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n await this.acquire();\n try {\n return await fn();\n } finally {\n this.release();\n }\n }\n\n private acquire(): Promise<void> {\n if (!this.locked) {\n this.locked = true;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n this.queue.push(resolve);\n });\n }\n\n private release(): void {\n const next = this.queue.shift();\n if (next) {\n next();\n } else {\n this.locked = false;\n }\n }\n\n get isLocked(): boolean {\n return this.locked;\n }\n\n get queueLength(): number {\n return this.queue.length;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger as rootLogger } from '../logger.js';\n\n/**\n * BaseTracker — JSON 文件持久化的泛型 Tracker 基类。\n *\n * 集中公共的 load/save/get/getAll/delete 逻辑。\n * 子类通过构造函数参数注入 collectionKey 和 trackerName。\n */\nexport abstract class BaseTracker<TRecord> {\n protected readonly filePath: string;\n protected data: Record<string, Record<string, TRecord>>;\n\n /** JSON 根字段名(如 'issues' 或 'batches')*/\n protected readonly collectionKey: string;\n /** 临时文件前缀(如 'tracker' 或 'braindump-tracker')*/\n protected readonly trackerName: string;\n\n constructor(dataDir: string, filename: string, collectionKey: string, trackerName: string) {\n this.collectionKey = collectionKey;\n this.trackerName = trackerName;\n this.filePath = path.join(dataDir, filename);\n this.data = this.load();\n }\n\n protected load(): Record<string, Record<string, TRecord>> {\n try {\n if (fs.existsSync(this.filePath)) {\n const raw = fs.readFileSync(this.filePath, 'utf-8');\n return JSON.parse(raw);\n }\n } catch (err) {\n rootLogger.child(this.trackerName).error(\n 'Failed to load tracker data',\n { error: (err as Error).message },\n );\n }\n return { [this.collectionKey]: {} };\n }\n\n protected save(): void {\n const dir = path.dirname(this.filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const tmpPath = path.join(dir,\n `.${this.trackerName}-${process.pid}-${Date.now()}.tmp`);\n fs.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2), 'utf-8');\n fs.renameSync(tmpPath, this.filePath);\n }\n\n /** 获取记录集合的引用 */\n protected get collection(): Record<string, TRecord> {\n return this.data[this.collectionKey] as Record<string, TRecord>;\n }\n\n protected getByKey(key: string): TRecord | undefined {\n return this.collection[key];\n }\n\n protected getAllRecords(): TRecord[] {\n return Object.values(this.collection);\n }\n\n protected setRecord(key: string, record: TRecord): void {\n this.collection[key] = record;\n }\n\n protected deleteByKey(key: string): boolean {\n if (!this.collection[key]) return false;\n delete this.collection[key];\n this.save();\n return true;\n }\n}\n","import type { IssueRecord } from './IssueState.js';\n\n/**\n * IssueRecord 身份字段辅助函数。\n *\n * 从 demandSpec 读取身份信息。\n */\n\n/** 获取显示 IID(工蜂 Issue IID) */\nexport function getIid(record: IssueRecord): number {\n return Number(record.demandSpec!.sourceRef.displayId);\n}\n\n/** 获取外部 ID(工蜂 Issue ID) */\nexport function getExternalId(record: IssueRecord): number {\n return Number(record.demandSpec!.sourceRef.externalId);\n}\n\n/** 获取标题 */\nexport function getTitle(record: IssueRecord): string {\n return record.demandSpec!.title;\n}\n","import type { IssueRecord } from './IssueState.js';\nimport type { BraindumpTask } from '../braindump/BraindumpState.js';\nimport type { ActionLifecycleManager } from '../lifecycle/ActionLifecycleManager.js';\nimport { getIid, getTitle } from './IssueRecordHelper.js';\n\n/**\n * UnifiedTaskStatus — 所有任务类型共享的通用状态枚举。\n *\n * 与 IssueState/BatchStatus/TaskStatus 共存,不替代它们。\n * 用于跨任务类型的通用逻辑(如统一 dashboard、统一恢复)。\n */\nexport type UnifiedTaskStatus =\n | 'idle' // 尚未开始\n | 'preparing' // 准备中(创建分支、安装依赖等)\n | 'running' // 执行中\n | 'waiting' // 等待外部输入(审核等)\n | 'merging' // 合并中\n | 'completed' // 完成\n | 'failed'; // 失败\n\n/**\n * ExecutableTask — 所有可执行任务的统一接口。\n *\n * IssueRecord 和 BraindumpTask 都可以投影为此接口,\n * 用于跨任务类型的通用操作。\n */\nexport interface ExecutableTask {\n /** 任务类型标识 */\n readonly kind: 'issue' | 'braindump-task';\n /** 唯一标识(issue: string(issueIid), braindump: taskId) */\n readonly taskId: string;\n /** 显示标题 */\n readonly title: string;\n /** 统一状态 */\n readonly status: UnifiedTaskStatus;\n /** 重试次数 */\n readonly attempts: number;\n /** 最后错误 */\n readonly lastError?: string;\n /** 创建时间 */\n readonly createdAt: string;\n /** 最后更新时间 */\n readonly updatedAt: string;\n /** 特性分支名 */\n readonly branchName?: string;\n /** 原始状态值(IssueState 或 TaskStatus) */\n readonly sourceState?: string;\n /** 过滤分类:active/completed/failed/blocked/idle/skipped */\n readonly stateCategory?: string;\n /** 预计算的状态展示标签(由后端投影时通过 ActionLifecycleManager.resolveLabel 生成) */\n readonly displayLabel?: string;\n\n /** 阶段进度快照(由后端投影时预计算)。\n * 各阶段按定义顺序排列,status 为 pending/in_progress/completed/failed。\n * 未提供时前端不渲染阶段进度列。 */\n readonly phaseProgress?: { name: string; label: string; status: 'pending' | 'in_progress' | 'completed' | 'failed' }[];\n}\n\n/**\n * ActionStatus → UnifiedTaskStatus 映射。\n *\n * 使用 ActionLifecycleManager 的 resolve() 返回的 ActionStatus 做语义映射:\n * - idle/skipped → 'idle'\n * - ready → 'preparing'\n * - running → 'running'\n * - waiting → 'waiting'\n * - done → 'completed'\n * - failed → 'failed'\n */\nexport function issueStateToUnified(actionStatus: string): UnifiedTaskStatus {\n switch (actionStatus) {\n case 'idle':\n case 'skipped':\n return 'idle';\n case 'ready':\n return 'preparing';\n case 'running':\n return 'running';\n case 'waiting':\n return 'waiting';\n case 'done':\n return 'completed';\n case 'failed':\n return 'failed';\n default:\n return 'idle';\n }\n}\n\n/**\n * TaskStatus → UnifiedTaskStatus 映射。\n */\nexport function taskStatusToUnified(status: string): UnifiedTaskStatus {\n switch (status) {\n case 'pending':\n case 'blocked':\n return 'idle';\n case 'running':\n return 'running';\n case 'done':\n return 'preparing'; // done but not yet merged\n case 'merging':\n case 'conflict_resolving':\n return 'merging';\n case 'merged':\n return 'completed';\n case 'failed':\n return 'failed';\n default:\n return 'idle';\n }\n}\n\n/** 从 UnifiedTaskStatus 派生 stateCategory(用于前端过滤) */\nexport function unifiedStatusToCategory(status: UnifiedTaskStatus): string {\n switch (status) {\n case 'running':\n case 'preparing':\n case 'merging':\n return 'active';\n case 'waiting':\n return 'blocked';\n case 'completed':\n return 'completed';\n case 'failed':\n return 'failed';\n case 'idle':\n default:\n return 'idle';\n }\n}\n\n/**\n * 为 Issue 计算 stateCategory(精确版,使用 ActionLifecycleManager)。\n * 比 unifiedStatusToCategory 更精准,能区分 skipped 等状态。\n */\nexport function issueStateCategory(record: IssueRecord, lm: ActionLifecycleManager): string {\n if (lm.isTerminal(record.state)) {\n if (record.state === 'failed') return 'failed';\n if (record.state === 'completed') return 'completed';\n return 'skipped';\n }\n if (lm.isBlocked(record.state)) return 'blocked';\n return 'active';\n}\n\n// ── Adapters ──\n\n/** 将 IssueRecord 投影为 ExecutableTask */\nexport function issueToExecutableTask(\n record: IssueRecord,\n lm: ActionLifecycleManager,\n): ExecutableTask {\n const actionState = lm.resolve(record.state, record.currentPhase);\n\n // 预计算阶段进度快照\n const phaseStatusMap = lm.derivePhaseStatuses(record.state, record.currentPhase);\n const phaseDefs = lm.getPhaseDefs();\n const phaseProgress = phaseDefs.map(p => ({\n name: p.name,\n label: p.label,\n status: phaseStatusMap[p.name] ?? 'pending' as const,\n }));\n\n return {\n kind: 'issue',\n taskId: String(getIid(record)),\n title: getTitle(record),\n status: issueStateToUnified(actionState.status),\n attempts: record.attempts,\n lastError: record.lastError,\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n branchName: record.branchName,\n sourceState: record.state,\n stateCategory: issueStateCategory(record, lm),\n displayLabel: lm.resolveLabel(record.state, record.currentPhase),\n phaseProgress,\n };\n}\n\n/** 将 BraindumpTask 投影为 ExecutableTask */\nexport function braindumpTaskToExecutableTask(\n task: BraindumpTask,\n batch: { createdAt: string; updatedAt: string },\n): ExecutableTask {\n const status = taskStatusToUnified(task.status);\n return {\n kind: 'braindump-task',\n taskId: task.id,\n title: task.title,\n status,\n attempts: task.attempts,\n lastError: task.lastError,\n createdAt: task.startedAt ?? batch.createdAt,\n updatedAt: task.completedAt ?? task.startedAt ?? batch.updatedAt,\n branchName: task.branchName,\n sourceState: task.status,\n stateCategory: unifiedStatusToCategory(status),\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { t } from '../i18n/index.js';\nimport { getProjectKnowledge } from '../knowledge/index.js';\nimport { KNOWLEDGE_DEFAULTS } from '../knowledge/KnowledgeDefaults.js';\nimport type { ProjectKnowledge } from '../knowledge/ProjectKnowledge.js';\nimport type { DemandSpec } from '../demand/DemandSpec.js';\nimport type { RepoSpec } from '../repo/RepoSpec.js';\nimport { isMultiRepo } from '../repo/RepoSpec.js';\nimport type { RepoContext } from '../workspace/index.js';\n\nexport interface WorkspaceLayout {\n repos: RepoContext[];\n workspaceRoot: string;\n}\n\nexport interface PromptContext {\n issueTitle: string;\n issueDescription: string;\n issueIid: number;\n supplementText?: string;\n workspace?: WorkspaceLayout;\n}\n\nfunction planDir(iid: number): string {\n return `.claude-plan/issue-${iid}`;\n}\n\n/**\n * Build template variable map from knowledge config (or defaults).\n */\nexport function getKnowledgeForPrompt(): Record<string, string> {\n const k: ProjectKnowledge = getProjectKnowledge() ?? KNOWLEDGE_DEFAULTS;\n\n const codeStyleParts: string[] = [];\n if (k.codeStyle.indentStyle === 'spaces') {\n codeStyleParts.push(`${k.codeStyle.indentSize}空格缩进`);\n } else {\n codeStyleParts.push('Tab缩进');\n }\n codeStyleParts.push(`${k.codeStyle.lineWidth}字符行宽`);\n codeStyleParts.push('命名规范等');\n if (k.codeStyle.additionalRules?.length) {\n codeStyleParts.push(...k.codeStyle.additionalRules);\n }\n\n const knownIssueLines = k.knownIssues.map(issue => `- ${issue.description}${issue.advice ? `,${issue.advice}` : ''}`);\n\n return {\n dependencyCheckPath: k.toolchain.dependencyCheckPath ?? 'node_modules/.bin/eslint',\n installCommand: k.toolchain.installCommand,\n installFallbackCommand: k.toolchain.installFallbackCommand ?? `${k.toolchain.installCommand} --ignore-scripts`,\n lintCommand: k.toolchain.lintCommand ?? 'npm run lint',\n buildCommand: k.toolchain.buildCommand ?? 'npm run build',\n testCommand: k.toolchain.testCommand ?? 'npm test',\n testFilesCommand: k.toolchain.testFilesCommand\n ?? `${k.toolchain.testCommand ?? 'npm test'} -- <涉及变更的测试文件>`,\n knownIssuesSection: knownIssueLines.length > 0\n ? knownIssueLines.join('\\n')\n : '- 无已知预存问题',\n codeStyleDescription: codeStyleParts.join('、'),\n // E2E related\n e2eDir: k.structure.e2eDir ?? 'e2e',\n e2eTool: k.structure.e2eTool ?? 'E2E',\n frontendDir: k.structure.frontendDir ?? 'frontend',\n };\n}\n\n/**\n * 从 DemandSpec 构建 prompt 上下文,统一补充信息的格式化。\n */\nexport function demandToPromptContext(demand: DemandSpec): {\n title: string;\n description: string;\n displayId: string;\n supplementText: string;\n} {\n const parts: string[] = [];\n const s = demand.supplement;\n if (s?.requirements) parts.push(`### 补充需求说明\\n${s.requirements}`);\n if (s?.acceptanceCriteria) parts.push(`### 验收标准\\n${s.acceptanceCriteria}`);\n if (s?.scope) parts.push(`### 变更范围\\n${s.scope}`);\n if (s?.constraints) parts.push(`### 约束条件\\n${s.constraints}`);\n if (s?.references) parts.push(`### 参考链接\\n${s.references}`);\n if (s?.freeText) parts.push(`### 其他补充\\n${s.freeText}`);\n return {\n title: demand.title,\n description: demand.description,\n displayId: demand.sourceRef.displayId ?? demand.demandId,\n supplementText: parts.length ? `## 补充信息\\n\\n${parts.join('\\n\\n')}` : '',\n };\n}\n\n/**\n * Build a workspace layout section for multi-repo prompts.\n * Returns empty string when workspace has only one repo.\n */\nexport function buildWorkspaceSection(workspace?: WorkspaceLayout): string {\n if (!workspace || workspace.repos.length <= 1) return '';\n\n const lines: string[] = [\n '## 多仓库工作区',\n '',\n '当前工作区包含多个关联仓库,你可以跨仓库读写文件。各仓库相对于工作区根目录的布局如下:',\n '',\n '| 仓库 | 相对路径 | 角色 |',\n '|------|---------|------|',\n ];\n\n for (const repo of workspace.repos) {\n const relPath = repo.name + (repo.workDir !== repo.gitRootDir\n ? ` (项目目录: ${repo.name}/${repo.workDir.slice(repo.gitRootDir.length + 1)})`\n : '');\n const roleText = repo.role || (repo.isPrimary ? '主仓库' : '关联仓库');\n lines.push(`| ${repo.name} | \\`${relPath}\\` | ${roleText} |`);\n }\n\n lines.push('');\n lines.push('修改任一仓库的代码时,使用对应仓库的相对路径即可。请先阅读各仓库根目录下的 CLAUDE.md(如有)了解项目约定。');\n\n return lines.join('\\n');\n}\n\nexport function analysisPrompt(ctx: PromptContext): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const pd = planDir(ctx.issueIid);\n return t('prompt.analysis', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n description: ctx.issueDescription,\n supplement: supplementSection,\n planDir: pd,\n });\n}\n\nexport function designPrompt(ctx: PromptContext): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const pd = planDir(ctx.issueIid);\n return t('prompt.design', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n supplement: supplementSection,\n planDir: pd,\n });\n}\n\nexport function implementPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n return t('prompt.implement', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n}\n\nexport function verifyPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n return t('prompt.verify', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n}\n\nexport function planModeVerifyPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const base = t('prompt.planModeVerify', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport function planPrompt(ctx: PromptContext): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const pd = planDir(ctx.issueIid);\n const base = t('prompt.plan', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n description: ctx.issueDescription,\n supplement: supplementSection,\n planDir: pd,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport function buildPrompt(ctx: PromptContext): string {\n const pd = planDir(ctx.issueIid);\n const kv = getKnowledgeForPrompt();\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const base = t('prompt.build', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n planDir: pd,\n ...kv,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport interface ReviewRoundForPrompt {\n round: number;\n feedback: string;\n timestamp: string;\n}\n\nexport function rePlanPrompt(ctx: PromptContext, history: ReviewRoundForPrompt[]): string {\n const supplementSection = ctx.supplementText ? `\\n\\n${ctx.supplementText}` : '';\n const wsSection = buildWorkspaceSection(ctx.workspace);\n const pd = planDir(ctx.issueIid);\n const feedbackLines = history.map(\n r => t('prompt.rePlanRound', { round: r.round, timestamp: r.timestamp, feedback: r.feedback })\n ).join('\\n\\n');\n const base = t('prompt.rePlan', {\n iid: ctx.issueIid,\n title: ctx.issueTitle,\n description: ctx.issueDescription,\n supplement: supplementSection,\n historyCount: history.length,\n feedbackLines,\n planDir: pd,\n });\n return wsSection ? `${base}\\n\\n${wsSection}` : base;\n}\n\nexport interface E2ePromptPorts {\n backendPort: number;\n frontendPort: number;\n host: string;\n}\n\nexport interface UatToolConfig {\n vendorDir: string;\n configFile: string;\n}\n\n/**\n * Load all SKILL.md files from the vendor .cursor/skills/ directory.\n * Scans every sub-directory for SKILL.md, concatenates them in sorted order.\n * Falls back to a top-level SKILL.md if .cursor/skills/ doesn't exist.\n */\nfunction loadUatSkill(vendorDir: string): string | null {\n const skillsDir = path.join(vendorDir, '.cursor/skills');\n\n if (fs.existsSync(skillsDir)) {\n try {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .sort((a, b) => a.name.localeCompare(b.name));\n\n const parts: string[] = [];\n for (const entry of entries) {\n const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');\n if (fs.existsSync(skillFile)) {\n try {\n parts.push(fs.readFileSync(skillFile, 'utf-8'));\n } catch { /* skip unreadable */ }\n }\n }\n if (parts.length > 0) return parts.join('\\n\\n---\\n\\n');\n } catch { /* fall through */ }\n }\n\n const fallback = path.join(vendorDir, 'SKILL.md');\n if (fs.existsSync(fallback)) {\n try { return fs.readFileSync(fallback, 'utf-8'); } catch { /* skip */ }\n }\n return null;\n}\n\nexport function e2eVerifyPromptSuffix(\n ctx: PromptContext,\n ports?: E2ePromptPorts,\n uatTool?: UatToolConfig,\n): string {\n if (uatTool?.vendorDir) {\n return buildOaUatPrompt(ctx, uatTool, ports);\n }\n return buildGenericE2ePrompt(ctx, ports);\n}\n\n/**\n * Load the \"e2e_prompt\" field from the oa_pc_uat config.json.\n * Returns the custom prompt string or null if not found / not set.\n */\nfunction loadUatConfigPrompt(configFile: string): string | null {\n if (!configFile || !fs.existsSync(configFile)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(configFile, 'utf-8'));\n if (typeof raw.e2e_prompt === 'string' && raw.e2e_prompt.trim()) {\n return raw.e2e_prompt.trim();\n }\n } catch { /* ignore parse errors */ }\n return null;\n}\n\nfunction buildOaUatPrompt(\n ctx: PromptContext,\n uatTool: UatToolConfig,\n ports?: E2ePromptPorts,\n): string {\n const vendorDir = uatTool.vendorDir;\n const configFile = uatTool.configFile\n || (fs.existsSync(path.join(vendorDir, 'config.json'))\n ? path.join(vendorDir, 'config.json')\n : '');\n\n const skillContent = loadUatSkill(vendorDir);\n const skillSection = skillContent\n ? `\\n\\n<uat-skills>\\n${skillContent}\\n</uat-skills>`\n : '';\n\n const configArg = configFile ? ` --config ${configFile}` : '';\n const outputDirArg = ` --output-dir ${vendorDir}/outputs/issue-${ctx.issueIid}`;\n const portHint = ports\n ? `\\n\\n**Preview 环境已启动:**\\n- 后端: http://${ports.host}:${ports.backendPort}\\n- 前端: https://${ports.host}:${ports.frontendPort}`\n : '';\n\n const customPrompt = loadUatConfigPrompt(configFile);\n\n const cmdOverride = `\n\n> **⚠️ 命令参数覆盖**:项目指引中出现的所有 \\`python3 .../main.py\\` 命令,都必须替换为以下标准格式:\n> - 执行脚本:\\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\\`\n> - REPL 排障:\\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} -i --headless\\`\n`;\n\n const instructionSection = customPrompt\n ? `\\n\\n### 项目 E2E 指引\\n\\n${customPrompt}\\n${cmdOverride}`\n : '';\n\n const defaultSteps = `\n### 执行要点\n\n1. 影响面分析:基于 \\`git diff\\` 分析本次变更影响的功能点\n2. 增量剧本生成:扫描 \\`uat-scripts/\\` 目录,仅为未覆盖场景生成新剧本\n3. 执行验证脚本:\n \\`\\`\\`bash\n python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\n \\`\\`\\`\n4. 失败时使用 REPL 排障:\n \\`\\`\\`bash\n python3 ${vendorDir}/main.py${configArg}${outputDirArg} -i --headless\n \\`\\`\\`\n5. 输出结构化验证总览(包含截图路径、执行结果、耗时)`;\n\n return `\n\n## E2E UI 验证(已启用 - OA Web UAT)\n\n本次变更已开启 E2E UI 自动验收,使用 oa_pc_uat 增量验证流水线。${portHint}\n\n**工具位置**: \\`${vendorDir}\\`\n${configFile ? `**配置文件**: \\`${configFile}\\`` : ''}\n**产物输出目录**: \\`${vendorDir}/outputs/issue-${ctx.issueIid}\\`(所有 --script 和 -i 命令必须携带 \\`${outputDirArg.trim()}\\` 参数)\n\n请严格按照以下 oa-web-uat skill 的流程执行端对端验证:\n${skillSection}\n${instructionSection}\n${customPrompt ? '' : defaultSteps}\n\n### ⛔ 阶段 Gate 约束(必须严格遵守)\n\nE2E 验证必须**严格按顺序**经过三个阶段,**禁止跳过或合并**:\n\n**Gate 1 — 影响面分析完成检查点**\n- 必须先通过 \\`git diff\\` 分析出具体的功能点清单\n- 产出物:功能点清单(列出每个受影响的功能名称、影响模块、需验证路径)\n- ❌ 如果没有产出功能点清单,禁止进入阶段 2\n\n**Gate 2 — 剧本生成完成检查点**\n- 必须先扫描 \\`uat-scripts/\\` 已有脚本,判断增量需求\n- 对于需要新增的场景,必须完成:编写 Markdown 用例 → 执行 \\`scenario-gen\\` 生成 result.json → 基于 result.json 编写 Python 脚本\n- 产出物:所有需要执行的 .py 脚本文件(已有 + 新增)必须实际存在于磁盘上\n- ✅ 验证方式:\\`ls -la\\` 确认每个脚本文件存在\n- ❌ 如果脚本文件不存在或未生成,禁止进入阶段 3\n\n**Gate 3 — 执行阶段**\n- 只有 Gate 2 通过后才可以执行 \\`python3 ${vendorDir}/main.py${configArg}${outputDirArg} --script <script.py> --headless\\`\n- 每个脚本单独执行,逐一记录结果\n- 失败时进入 REPL 排障,修复后重新执行\n\n将 E2E 测试结果写入验证报告的 **E2E UI 测试结果** 章节。`;\n}\n\nfunction buildGenericE2ePrompt(ctx: PromptContext, ports?: E2ePromptPorts): string {\n const kv = getKnowledgeForPrompt();\n const frontendDir = kv.frontendDir;\n const e2eDir = kv.e2eDir;\n const e2eTool = kv.e2eTool;\n\n const serverSection = ports\n ? `\n**Preview 环境已启动(由系统管理,无需手动启动):**\n- 后端: http://${ports.host}:${ports.backendPort}\n- 前端: https://${ports.host}:${ports.frontendPort}\n\n执行 E2E 测试时请使用以下环境变量来连接已启动的服务:\n\\`\\`\\`bash\nE2E_PORT=${ports.frontendPort} E2E_HOST=${ports.host} E2E_BASE_URL=https://${ports.host}:${ports.frontendPort} \\\\\n cd ${frontendDir} && npx ${e2eTool.toLowerCase()} test\n\\`\\`\\`\n\n**注意**: 不要使用 pnpm test:e2e(它会尝试自行启动 webServer),直接用 npx ${e2eTool.toLowerCase()} test 即可复用已启动的前端。`\n : `\n执行 E2E 测试:\n\\`\\`\\`bash\ncd ${frontendDir} && pnpm test:e2e\n\\`\\`\\``;\n\n return `\n\n## E2E UI 验证(已启用)\n\n本次变更已开启 E2E UI 自动验收,请额外执行以下步骤:\n\n6. 如果本次变更涉及前端页面(${frontendDir}/ 目录有改动),请执行 UI E2E 验证:\n a. 在 ${e2eDir}/ 目录下编写针对本次变更的 ${e2eTool} 测试\n b. ${serverSection.trim()}\n c. 如果测试失败,分析失败原因并尝试修复\n7. 将 E2E 测试结果写入验证报告的 **E2E UI 测试结果** 章节,包括:\n - 冒烟测试通过数 / 总数\n - 专项测试结果列表\n - 失败截图路径(如有)`;\n}\n\nexport interface ConflictResolveContext {\n issueIid: number;\n branchName: string;\n baseBranch: string;\n conflictFiles: string[];\n}\n\nexport function conflictResolvePrompt(ctx: ConflictResolveContext): string {\n const conflictFilesList = ctx.conflictFiles.map(f => `- \\`${f}\\``).join('\\n');\n return t('prompt.conflictResolve', {\n iid: ctx.issueIid,\n branch: ctx.branchName,\n baseBranch: ctx.baseBranch,\n conflictFilesList,\n });\n}\n\n/**\n * 多仓库模式下的工作区上下文 section。\n * 注入到 prompt 中,告知 AI Agent 当前工作区包含多个仓库子目录。\n */\nexport function buildMultiRepoContextSection(\n repos: RepoSpec[],\n issueIid: number,\n): string | null {\n if (!isMultiRepo(repos)) return null;\n\n const primary = repos.find(r => r.isPrimary)!;\n const repoLines = repos\n .map(r => {\n const tag = r.isPrimary ? '(主仓库)' : '(关联仓库)';\n return `- **${r.name}/** — ${r.projectPath} ${tag}`;\n })\n .join('\\n');\n\n return `## 多仓库工作区\n\n本次 Issue 涉及多个代码仓库,它们位于当前工作目录的子目录中:\n${repoLines}\n\n请根据需求在对应的仓库子目录中进行修改。计划文件请统一写在 ${primary.name}/${primary.projectSubDir ? primary.projectSubDir + '/' : ''}.claude-plan/issue-${issueIid}/ 目录下。`;\n}\n\nexport function issueProgressComment(phase: string, status: string, detail?: string): string {\n const emoji: Record<string, string> = {\n analysis: '🔍', design: '📐', implement: '💻', verify: '✅',\n plan: '📋', review: '👀', build: '🔨',\n };\n const icon = emoji[phase] || '📋';\n const statusKey = status === 'completed' ? 'progress.completed' : status === 'failed' ? 'progress.failed' : 'progress.inProgress';\n const statusText = t(statusKey);\n let msg = t('progress.comment', { icon, phase, status: statusText });\n if (detail) {\n msg += `\\n\\n${detail}`;\n }\n return msg;\n}\n","import { GitOperations } from './GitOperations.js';\nimport type { AIRunner } from '../ai-runner/index.js';\nimport { conflictResolvePrompt } from '../prompts/templates.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('ConflictResolver');\n\nexport interface ConflictResolveOptions {\n wtGit: GitOperations;\n targetRef: string;\n workDir: string;\n branchName: string;\n /** Identifier for logging and events (e.g. issueIid or taskId) */\n contextId: string | number;\n maxAttempts?: number;\n phaseTimeoutMs: number;\n onEvent?: (event: unknown) => void;\n}\n\n/**\n * Shared conflict resolution logic: rebase onto target, resolve conflicts with AI.\n * Extracted from PipelineOrchestrator.resolveConflict() for reuse by BraindumpOrchestrator.\n */\nexport class ConflictResolver {\n constructor(private aiRunner: AIRunner) {}\n\n /**\n * Rebase the current branch onto `targetRef` and resolve any conflicts using AI.\n * After successful resolution, the caller is responsible for force-pushing.\n *\n * @throws if conflicts cannot be resolved within maxAttempts\n */\n async resolve(opts: ConflictResolveOptions): Promise<void> {\n const { wtGit, targetRef, workDir, branchName, contextId, phaseTimeoutMs, onEvent } = opts;\n const maxAttempts = opts.maxAttempts ?? 20;\n\n // Abort residual rebase if any\n if (await wtGit.isRebaseInProgress()) {\n logger.warn('Found residual rebase in progress, aborting', { contextId });\n await wtGit.rebaseAbort();\n }\n\n // Attempt rebase\n const rebaseResult = await wtGit.rebase(targetRef);\n\n if (rebaseResult.success) {\n logger.info('Rebase succeeded without conflicts', { contextId });\n return;\n }\n\n // Has conflicts — resolve with AI\n let conflictFiles = rebaseResult.conflictFiles;\n let attempt = 0;\n\n while (conflictFiles.length > 0 && attempt < maxAttempts) {\n attempt++;\n logger.info('Resolving conflicts with AI', { contextId, attempt, conflictFiles });\n\n const prompt = conflictResolvePrompt({\n issueIid: typeof contextId === 'number' ? contextId : 0,\n branchName,\n baseBranch: targetRef.replace(/^origin\\//, ''),\n conflictFiles,\n });\n\n await this.aiRunner.run({\n prompt,\n workDir,\n timeoutMs: phaseTimeoutMs,\n onStreamEvent: onEvent ? (event: unknown) => onEvent(event) : undefined,\n });\n\n // Stage resolved files\n await wtGit.add(conflictFiles);\n\n // Continue rebase\n const continueResult = await wtGit.rebaseContinue();\n if (continueResult.done) {\n conflictFiles = [];\n } else {\n conflictFiles = continueResult.conflictFiles;\n }\n }\n\n if (conflictFiles.length > 0) {\n await wtGit.rebaseAbort();\n throw new Error(\n `Failed to resolve all conflicts after ${maxAttempts} attempts. Remaining: ${conflictFiles.join(', ')}`,\n );\n }\n\n logger.info('All conflicts resolved', { contextId, totalAttempts: attempt });\n }\n}\n"],"mappings":"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","names":["logger","fs","path","path","fs","logger"]}
@@ -69,6 +69,9 @@ var zhCN = {
69
69
  "orchestrator.previewComment.expiry": "Preview \u5C06\u5728 MR \u5408\u5E76\u540E\u81EA\u52A8\u6E05\u7406\uFF0C\u6216 {hours}h \u540E\u8FC7\u671F\u3002",
70
70
  // BasePhase messages
71
71
  "basePhase.aiStarting": "\u6B63\u5728\u542F\u52A8 AI Agent ({label})...",
72
+ "basePhase.aiResuming": "\u6B63\u5728\u6062\u590D\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684 AI \u4F1A\u8BDD ({label})...",
73
+ "basePhase.resumePrompt": "\u4E0A\u4E00\u6B21\u6267\u884C\u56E0\u4E2D\u65AD\u800C\u7EC8\u6B62\u3002\u8BF7\u68C0\u67E5\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u4E2D\u5DF2\u6709\u7684\u8FDB\u5EA6\uFF08\u5305\u62EC\u5DF2\u4FEE\u6539\u7684\u6587\u4EF6\u548C\u4EA7\u7269\uFF09\uFF0C\u4ECE\u4E2D\u65AD\u5904\u7EE7\u7EED\u5B8C\u6210\u4EFB\u52A1\u3002\u4E0D\u8981\u91CD\u590D\u5DF2\u7ECF\u5B8C\u6210\u7684\u5DE5\u4F5C\u3002",
74
+ "basePhase.resumeFallback": "\u4F1A\u8BDD\u6062\u590D\u5931\u8D25\uFF0C\u964D\u7EA7\u4E3A\u5168\u65B0\u6267\u884C",
72
75
  "basePhase.rulesSection": "## \u9879\u76EE\u5F00\u53D1\u89C4\u8303\u53C2\u8003\n\u4EE5\u4E0B\u662F\u4E0E\u672C\u6B21\u4EFB\u52A1\u76F8\u5173\u7684\u5F00\u53D1\u89C4\u8303\uFF0C\u8BF7\u5728\u7F16\u7801\u65F6\u4E25\u683C\u9075\u5FAA\uFF1A\n\n{rules}",
73
76
  "basePhase.error": "\u9519\u8BEF: {message}",
74
77
  // IssuePoller messages
@@ -566,6 +569,9 @@ var en = {
566
569
  "orchestrator.previewComment.expiry": "Preview will be cleaned up after MR merge, or expires in {hours}h.",
567
570
  // BasePhase messages
568
571
  "basePhase.aiStarting": "Starting AI Agent ({label})...",
572
+ "basePhase.aiResuming": "Resuming previous AI session ({label})...",
573
+ "basePhase.resumePrompt": "The previous execution was interrupted. Please check the existing progress in the working directory (including modified files and artifacts), and continue from where it left off. Do not repeat work that has already been completed.",
574
+ "basePhase.resumeFallback": "Session resume failed, falling back to fresh execution",
569
575
  "basePhase.rulesSection": "## Project Development Guidelines\nThe following are development guidelines related to this task. Please strictly follow them when coding:\n\n{rules}",
570
576
  "basePhase.error": "Error: {message}",
571
577
  // IssuePoller messages
@@ -1023,4 +1029,4 @@ export {
1023
1029
  setLocale,
1024
1030
  t
1025
1031
  };
1026
- //# sourceMappingURL=chunk-MEDXZ5JM.js.map
1032
+ //# sourceMappingURL=chunk-EB6TGE3H.js.map