issue-flow 0.12.0 → 0.14.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 (197) hide show
  1. package/README.md +79 -274
  2. package/dist/agent-OX3KPHE4.js +30 -0
  3. package/dist/{analyze-OEET7KGE.js → analyze-WIND2SEB.js} +31 -16
  4. package/dist/analyze-WIND2SEB.js.map +1 -0
  5. package/dist/{apply-ZIA3C52S.js → apply-JNOI2LR4.js} +18 -7
  6. package/dist/{apply-ZIA3C52S.js.map → apply-JNOI2LR4.js.map} +1 -1
  7. package/dist/bench-B4VZON7E.js +910 -0
  8. package/dist/bench-B4VZON7E.js.map +1 -0
  9. package/dist/{chunk-FADTCQGM.js → chunk-2IQ6M4ZE.js} +94 -32
  10. package/dist/chunk-2IQ6M4ZE.js.map +1 -0
  11. package/dist/chunk-3BWZO4YI.js +141 -0
  12. package/dist/chunk-3BWZO4YI.js.map +1 -0
  13. package/dist/chunk-3WFZETIM.js +163 -0
  14. package/dist/chunk-3WFZETIM.js.map +1 -0
  15. package/dist/chunk-67YIU2VB.js +362 -0
  16. package/dist/chunk-67YIU2VB.js.map +1 -0
  17. package/dist/{chunk-QCPGHBSZ.js → chunk-6BENHR2I.js} +63 -21
  18. package/dist/chunk-6BENHR2I.js.map +1 -0
  19. package/dist/{chunk-BYZDPR7N.js → chunk-6QYMSNVA.js} +194 -235
  20. package/dist/chunk-6QYMSNVA.js.map +1 -0
  21. package/dist/{chunk-Y5ZDJ4PJ.js → chunk-76MMPJ53.js} +20 -10
  22. package/dist/chunk-76MMPJ53.js.map +1 -0
  23. package/dist/chunk-7Z4Y3VKM.js +26 -0
  24. package/dist/chunk-7Z4Y3VKM.js.map +1 -0
  25. package/dist/{chunk-3QHK4YQA.js → chunk-B72RQZBV.js} +346 -119
  26. package/dist/chunk-B72RQZBV.js.map +1 -0
  27. package/dist/chunk-BEZ5BA5U.js +625 -0
  28. package/dist/chunk-BEZ5BA5U.js.map +1 -0
  29. package/dist/chunk-BRCNQXYQ.js +74 -0
  30. package/dist/chunk-BRCNQXYQ.js.map +1 -0
  31. package/dist/chunk-CGKLE6WR.js +1203 -0
  32. package/dist/chunk-CGKLE6WR.js.map +1 -0
  33. package/dist/chunk-CRGIQFUQ.js +83 -0
  34. package/dist/chunk-CRGIQFUQ.js.map +1 -0
  35. package/dist/{chunk-NS6R37II.js → chunk-F6TOYSYY.js} +141 -12
  36. package/dist/chunk-F6TOYSYY.js.map +1 -0
  37. package/dist/chunk-G5EK2XRN.js +25 -0
  38. package/dist/chunk-G5EK2XRN.js.map +1 -0
  39. package/dist/chunk-HAKDLR6N.js +134 -0
  40. package/dist/chunk-HAKDLR6N.js.map +1 -0
  41. package/dist/chunk-J3SOTUVI.js +298 -0
  42. package/dist/chunk-J3SOTUVI.js.map +1 -0
  43. package/dist/chunk-JN6D3MY6.js +30 -0
  44. package/dist/chunk-JN6D3MY6.js.map +1 -0
  45. package/dist/{chunk-LAFNLLQP.js → chunk-KMRFRXIT.js} +5 -5
  46. package/dist/chunk-KT7JKOXB.js +72 -0
  47. package/dist/chunk-KT7JKOXB.js.map +1 -0
  48. package/dist/chunk-L6HSKTQE.js +77 -0
  49. package/dist/chunk-L6HSKTQE.js.map +1 -0
  50. package/dist/chunk-LBGHI4S2.js +1264 -0
  51. package/dist/chunk-LBGHI4S2.js.map +1 -0
  52. package/dist/chunk-LEW4QM74.js +180 -0
  53. package/dist/chunk-LEW4QM74.js.map +1 -0
  54. package/dist/{chunk-FJP3UQGD.js → chunk-LFEQIZLB.js} +115 -20
  55. package/dist/chunk-LFEQIZLB.js.map +1 -0
  56. package/dist/chunk-OOVG2KBY.js +77 -0
  57. package/dist/chunk-OOVG2KBY.js.map +1 -0
  58. package/dist/{chunk-56KS3TFQ.js → chunk-OR43364P.js} +115 -160
  59. package/dist/chunk-OR43364P.js.map +1 -0
  60. package/dist/chunk-P3LRUNC6.js +16 -0
  61. package/dist/chunk-P3LRUNC6.js.map +1 -0
  62. package/dist/{chunk-TSOLWK7Y.js → chunk-PBTA3QQX.js} +8 -7
  63. package/dist/{chunk-TSOLWK7Y.js.map → chunk-PBTA3QQX.js.map} +1 -1
  64. package/dist/{chunk-MJULIKQU.js → chunk-PHW2YV6N.js} +23 -47
  65. package/dist/chunk-PHW2YV6N.js.map +1 -0
  66. package/dist/{chunk-73ATPIYX.js → chunk-PZTWU277.js} +36 -71
  67. package/dist/chunk-PZTWU277.js.map +1 -0
  68. package/dist/{chunk-M5UCXTIT.js → chunk-QD4VOILZ.js} +22 -18
  69. package/dist/chunk-QD4VOILZ.js.map +1 -0
  70. package/dist/chunk-QJ5UZAH5.js +2718 -0
  71. package/dist/chunk-QJ5UZAH5.js.map +1 -0
  72. package/dist/chunk-QK7WUO7A.js +283 -0
  73. package/dist/chunk-QK7WUO7A.js.map +1 -0
  74. package/dist/{chunk-A4SLHOW7.js → chunk-QZVKRDJY.js} +27 -19
  75. package/dist/chunk-QZVKRDJY.js.map +1 -0
  76. package/dist/chunk-REO3PXXO.js +71 -0
  77. package/dist/chunk-REO3PXXO.js.map +1 -0
  78. package/dist/{chunk-EZATO3DW.js → chunk-RVO7L4FX.js} +594 -17
  79. package/dist/chunk-RVO7L4FX.js.map +1 -0
  80. package/dist/{chunk-XQUSIEKA.js → chunk-SZMHCEYV.js} +63 -14
  81. package/dist/chunk-SZMHCEYV.js.map +1 -0
  82. package/dist/chunk-TYRNTSPJ.js +243 -0
  83. package/dist/chunk-TYRNTSPJ.js.map +1 -0
  84. package/dist/chunk-UJ3L2MS7.js +238 -0
  85. package/dist/chunk-UJ3L2MS7.js.map +1 -0
  86. package/dist/chunk-V7MO5EDS.js +120 -0
  87. package/dist/chunk-V7MO5EDS.js.map +1 -0
  88. package/dist/chunk-WB2X37MO.js +415 -0
  89. package/dist/chunk-WB2X37MO.js.map +1 -0
  90. package/dist/{chunk-NTMMF5UD.js → chunk-X44FKNKP.js} +525 -252
  91. package/dist/chunk-X44FKNKP.js.map +1 -0
  92. package/dist/chunk-XEYH6ZZI.js +289 -0
  93. package/dist/chunk-XEYH6ZZI.js.map +1 -0
  94. package/dist/cli.js +267 -44
  95. package/dist/cli.js.map +1 -1
  96. package/dist/{config-PRJGWW7J.js → config-HMFMCY2M.js} +31 -5
  97. package/dist/contract-JH2WBIO6.js +8 -0
  98. package/dist/conventions-NJF2OPVQ.js +146 -0
  99. package/dist/conventions-NJF2OPVQ.js.map +1 -0
  100. package/dist/diagnostics-THH42ZMP.js +23 -0
  101. package/dist/execute-KFNNZZHI.js +35 -0
  102. package/dist/{generate-EGB4KV2B.js → generate-FW5C6NP7.js} +37 -20
  103. package/dist/{generate-EGB4KV2B.js.map → generate-FW5C6NP7.js.map} +1 -1
  104. package/dist/git-W72QQ6GT.js +33 -0
  105. package/dist/init-HTN4V67Q.js +24 -0
  106. package/dist/{operations-BYT626KQ.js → operations-BCFOBH42.js} +27 -15
  107. package/dist/{operations-BYT626KQ.js.map → operations-BCFOBH42.js.map} +1 -1
  108. package/dist/permissions-4AKNQZHK.js +18 -0
  109. package/dist/plan-SIZLGA4V.js +34 -0
  110. package/dist/{policy-6S4GFC76.js → policy-DT6Q6ZVM.js} +20 -6
  111. package/dist/policy-DT6Q6ZVM.js.map +1 -0
  112. package/dist/pr-OMWCGUOW.js +39 -0
  113. package/dist/pr-review-N24CZPKR.js +32 -0
  114. package/dist/pr-review-N24CZPKR.js.map +1 -0
  115. package/dist/prd-5OFTZAHJ.js +33 -0
  116. package/dist/prd-5OFTZAHJ.js.map +1 -0
  117. package/dist/ps-RFORTCUD.js +93 -0
  118. package/dist/ps-RFORTCUD.js.map +1 -0
  119. package/dist/recorder-KCCPMKQG.js +32 -0
  120. package/dist/recorder-KCCPMKQG.js.map +1 -0
  121. package/dist/registry-GLPXOMFG.js +15 -0
  122. package/dist/registry-GLPXOMFG.js.map +1 -0
  123. package/dist/{resume-PPCNQVVV.js → resume-M5RLKP4I.js} +55 -34
  124. package/dist/{resume-PPCNQVVV.js.map → resume-M5RLKP4I.js.map} +1 -1
  125. package/dist/review-YM4JAABY.js +35 -0
  126. package/dist/review-YM4JAABY.js.map +1 -0
  127. package/dist/routing-NNT5WKGX.js +94 -0
  128. package/dist/routing-NNT5WKGX.js.map +1 -0
  129. package/dist/run-PZUKHPQK.js +54 -0
  130. package/dist/run-PZUKHPQK.js.map +1 -0
  131. package/dist/session-publisher-T7EI3CIP.js +11 -0
  132. package/dist/session-publisher-T7EI3CIP.js.map +1 -0
  133. package/dist/{state-manager-2EDVDBAR.js → state-manager-GQH32C44.js} +4 -2
  134. package/dist/state-manager-GQH32C44.js.map +1 -0
  135. package/dist/usage-V2CHE5PY.js +177 -0
  136. package/dist/usage-V2CHE5PY.js.map +1 -0
  137. package/dist/{web-EF5WIIGG.js → web-EA3KGNFN.js} +45 -30
  138. package/dist/web-EA3KGNFN.js.map +1 -0
  139. package/package.json +6 -2
  140. package/prompts/execute.md +7 -8
  141. package/prompts/plan.md +4 -5
  142. package/prompts/pr.md +2 -2
  143. package/web/AGENTS.md +152 -13
  144. package/web/public/app.css +380 -109
  145. package/web/public/app.js +525 -25
  146. package/web/public/index.html +68 -1
  147. package/dist/analyze-OEET7KGE.js.map +0 -1
  148. package/dist/chunk-3QHK4YQA.js.map +0 -1
  149. package/dist/chunk-42PJFMAR.js +0 -605
  150. package/dist/chunk-42PJFMAR.js.map +0 -1
  151. package/dist/chunk-4JSOYE4G.js +0 -368
  152. package/dist/chunk-4JSOYE4G.js.map +0 -1
  153. package/dist/chunk-56KS3TFQ.js.map +0 -1
  154. package/dist/chunk-73ATPIYX.js.map +0 -1
  155. package/dist/chunk-A4SLHOW7.js.map +0 -1
  156. package/dist/chunk-BAUPQXPE.js +0 -173
  157. package/dist/chunk-BAUPQXPE.js.map +0 -1
  158. package/dist/chunk-BYZDPR7N.js.map +0 -1
  159. package/dist/chunk-CD5ULRVC.js +0 -615
  160. package/dist/chunk-CD5ULRVC.js.map +0 -1
  161. package/dist/chunk-DIDUVL6O.js +0 -1365
  162. package/dist/chunk-DIDUVL6O.js.map +0 -1
  163. package/dist/chunk-EZATO3DW.js.map +0 -1
  164. package/dist/chunk-FADTCQGM.js.map +0 -1
  165. package/dist/chunk-FJP3UQGD.js.map +0 -1
  166. package/dist/chunk-LWRSMTTZ.js +0 -18
  167. package/dist/chunk-LWRSMTTZ.js.map +0 -1
  168. package/dist/chunk-M5UCXTIT.js.map +0 -1
  169. package/dist/chunk-M66SJHQQ.js +0 -206
  170. package/dist/chunk-M66SJHQQ.js.map +0 -1
  171. package/dist/chunk-MJULIKQU.js.map +0 -1
  172. package/dist/chunk-NS6R37II.js.map +0 -1
  173. package/dist/chunk-NTMMF5UD.js.map +0 -1
  174. package/dist/chunk-QCPGHBSZ.js.map +0 -1
  175. package/dist/chunk-XQUSIEKA.js.map +0 -1
  176. package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
  177. package/dist/execute-XBNVL753.js +0 -18
  178. package/dist/init-AJHCXJBP.js +0 -10
  179. package/dist/plan-QOZ3RIO6.js +0 -21
  180. package/dist/policy-6S4GFC76.js.map +0 -1
  181. package/dist/pr-EBBHD2ZX.js +0 -26
  182. package/dist/pr-review-VVMW5PO4.js +0 -20
  183. package/dist/prd-JQSZF7PR.js +0 -21
  184. package/dist/review-3F6MARWH.js +0 -19
  185. package/dist/run-RXE5GDQO.js +0 -36
  186. package/dist/web-EF5WIIGG.js.map +0 -1
  187. /package/dist/{config-PRJGWW7J.js.map → agent-OX3KPHE4.js.map} +0 -0
  188. /package/dist/{chunk-LAFNLLQP.js.map → chunk-KMRFRXIT.js.map} +0 -0
  189. /package/dist/{execute-XBNVL753.js.map → config-HMFMCY2M.js.map} +0 -0
  190. /package/dist/{init-AJHCXJBP.js.map → contract-JH2WBIO6.js.map} +0 -0
  191. /package/dist/{plan-QOZ3RIO6.js.map → diagnostics-THH42ZMP.js.map} +0 -0
  192. /package/dist/{pr-EBBHD2ZX.js.map → execute-KFNNZZHI.js.map} +0 -0
  193. /package/dist/{pr-review-VVMW5PO4.js.map → git-W72QQ6GT.js.map} +0 -0
  194. /package/dist/{prd-JQSZF7PR.js.map → init-HTN4V67Q.js.map} +0 -0
  195. /package/dist/{review-3F6MARWH.js.map → permissions-4AKNQZHK.js.map} +0 -0
  196. /package/dist/{run-RXE5GDQO.js.map → plan-SIZLGA4V.js.map} +0 -0
  197. /package/dist/{state-manager-2EDVDBAR.js.map → pr-OMWCGUOW.js.map} +0 -0
@@ -1,48 +1,53 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- publishGitState
4
- } from "./chunk-TSOLWK7Y.js";
3
+ ensureWebMonitor
4
+ } from "./chunk-LFEQIZLB.js";
5
5
  import {
6
- sleep
7
- } from "./chunk-LWRSMTTZ.js";
6
+ applyAcceptanceToPlan,
7
+ resolveIssueDir,
8
+ runAcceptanceGate
9
+ } from "./chunk-BRCNQXYQ.js";
10
+ import {
11
+ groupBy,
12
+ summarize
13
+ } from "./chunk-CRGIQFUQ.js";
8
14
  import {
9
15
  EXECUTE_PHASE,
10
16
  elapsedSecondsSince,
11
17
  getPhaseUsageTotals,
12
18
  publishIterationMetrics,
13
19
  publishStoryMetrics,
14
- readClaudeStream,
15
20
  resolvePolicyPlaceholders
16
- } from "./chunk-MJULIKQU.js";
21
+ } from "./chunk-PHW2YV6N.js";
17
22
  import {
18
23
  applyPlaceholders,
19
24
  loadPrompt
20
- } from "./chunk-LAFNLLQP.js";
25
+ } from "./chunk-KMRFRXIT.js";
21
26
  import {
22
- createWatchdog,
23
- describeStall,
24
- getShutdownSignal,
25
- registerChild
26
- } from "./chunk-M66SJHQQ.js";
27
+ invokeSelectedAgent
28
+ } from "./chunk-CGKLE6WR.js";
29
+ import {
30
+ archiveFolderName,
31
+ commitMessage
32
+ } from "./chunk-RVO7L4FX.js";
27
33
  import {
28
34
  createConfig,
35
+ getActiveResilienceConfig,
36
+ getShutdownSignal,
37
+ loadGlobalConfig,
38
+ loadWebConfig,
39
+ resolveAgentFor,
29
40
  resolvePaths,
30
41
  validateDependencies
31
- } from "./chunk-42PJFMAR.js";
42
+ } from "./chunk-QJ5UZAH5.js";
32
43
  import {
33
- classify,
34
- committedStoryIds,
35
- fixedBackoffPolicy,
36
- getBaseBranch,
37
- isWorkingTreeClean,
38
- withRetry
39
- } from "./chunk-DIDUVL6O.js";
44
+ resolveIssuePaths
45
+ } from "./chunk-QK7WUO7A.js";
40
46
  import {
41
47
  formatDuration,
42
48
  getIcons,
43
49
  getInactivityTimeout,
44
50
  getOutputCallback,
45
- getSessionPublisher,
46
51
  getStoryStageCallback,
47
52
  getStoryUpdateCallback,
48
53
  getTermWidth,
@@ -54,7 +59,21 @@ import {
54
59
  printWarning,
55
60
  useColor,
56
61
  useUnicode
57
- } from "./chunk-BYZDPR7N.js";
62
+ } from "./chunk-TYRNTSPJ.js";
63
+ import {
64
+ FilePublisher,
65
+ NullPublisher,
66
+ getSessionPublisher,
67
+ setSessionPublisher
68
+ } from "./chunk-6QYMSNVA.js";
69
+ import {
70
+ committedStoryIds,
71
+ fixedBackoffPolicy,
72
+ getBaseBranch,
73
+ isWorkingTreeClean,
74
+ resolvePolicy,
75
+ withRetry
76
+ } from "./chunk-BEZ5BA5U.js";
58
77
  import {
59
78
  allStoriesPass,
60
79
  applyStoryMetrics,
@@ -71,7 +90,13 @@ import {
71
90
  selectActiveStory,
72
91
  setLastError,
73
92
  trimErrorMessage
74
- } from "./chunk-CD5ULRVC.js";
93
+ } from "./chunk-J3SOTUVI.js";
94
+ import {
95
+ classify
96
+ } from "./chunk-XEYH6ZZI.js";
97
+
98
+ // src/commands/execute.ts
99
+ import { randomUUID } from "crypto";
75
100
 
76
101
  // src/core/engine.ts
77
102
  import { existsSync } from "fs";
@@ -188,7 +213,7 @@ function printBox(lines) {
188
213
  }
189
214
  console.log(blue(`${bl}${hrule}${br}`));
190
215
  }
191
- function printStartupHeader(config, plan) {
216
+ function printStartupHeader(config, plan, agent) {
192
217
  const icons = getIcons();
193
218
  const storiesTotal = plan.userStories.length;
194
219
  const storiesPassing = plan.userStories.filter((s) => s.passes).length;
@@ -196,7 +221,7 @@ function printStartupHeader(config, plan) {
196
221
  const issueLabel = config.issueNumber ? `Issue #${config.issueNumber}` : "Standalone mode";
197
222
  const maxIterLabel = config.maxIterations !== void 0 ? String(config.maxIterations) : "unlimited";
198
223
  const retryLabel = config.retryForever ? "unlimited retries" : `${config.retryLimit} consecutive retries`;
199
- printBox([
224
+ const lines = [
200
225
  `${icons.start} Issue Flow`,
201
226
  "---",
202
227
  `Issue: ${issueLabel}`,
@@ -204,9 +229,22 @@ function printStartupHeader(config, plan) {
204
229
  `Stories: ${storiesPassing}/${storiesTotal} passing`,
205
230
  `Iterations: ${maxIterLabel}`,
206
231
  `Retries: ${retryLabel}`
207
- ]);
232
+ ];
233
+ const remaining = storiesTotal - storiesPassing;
234
+ if (remaining > 0) {
235
+ const p50ExecuteSeconds = 518;
236
+ lines.push(
237
+ `Estimate: ~${formatDuration(remaining * p50ExecuteSeconds)} (${remaining} \xD7 p50 execute)`
238
+ );
239
+ }
240
+ if (agent) {
241
+ const model = agent.model ? ` \xB7 ${agent.model}` : "";
242
+ const detail = agent.detail ? ` (${agent.detail})` : "";
243
+ lines.push(`Agent: ${agent.provider}${model}${detail}`);
244
+ }
245
+ printBox(lines);
208
246
  }
209
- function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan, extraInfo, usage) {
247
+ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan, extraInfo, usage, usageByAgent) {
210
248
  const icons = getIcons();
211
249
  const storiesTotal = plan.userStories.length;
212
250
  const storiesPassing = plan.userStories.filter((s) => s.passes).length;
@@ -235,10 +273,8 @@ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan,
235
273
  `Iterations: ${iterations}`,
236
274
  `Duration: ${duration}`
237
275
  ];
238
- const tokens = formatTokens(usage);
239
- if (tokens !== "") {
240
- boxLines.push(`Tokens: ${tokens}`);
241
- }
276
+ boxLines.push(...tokenBoxLines(usage, usageByAgent));
277
+ boxLines.push(...executionCostLines(plan));
242
278
  boxLines.push(`Retries: ${totalRetries}`);
243
279
  if (extraInfo) {
244
280
  boxLines.push("---");
@@ -247,19 +283,79 @@ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan,
247
283
  console.log("");
248
284
  printBox(boxLines);
249
285
  }
286
+ function executionCostLines(plan, prefix = "Cost: ") {
287
+ const records = plan.executions ?? [];
288
+ if (records.length === 0) return [];
289
+ const lines = [];
290
+ const groups = groupBy(records, "harness");
291
+ let index = 0;
292
+ const pad = " ".repeat(prefix.length);
293
+ for (const [harness, summary] of groups) {
294
+ const parts = [];
295
+ if (summary.totalCost.reported > 0) {
296
+ parts.push(`$${summary.totalCost.reported.toFixed(4)} reported`);
297
+ }
298
+ if (summary.totalCost.estimated > 0) {
299
+ parts.push(`$${summary.totalCost.estimated.toFixed(4)} estimated`);
300
+ }
301
+ if (summary.totalCost.unknownExecutions > 0) {
302
+ parts.push(`${summary.totalCost.unknownExecutions} unknown`);
303
+ }
304
+ if (parts.length === 0) {
305
+ const totals = summarize(records).totalCost;
306
+ if (totals.reported === 0 && totals.estimated === 0) {
307
+ parts.push("not reported");
308
+ }
309
+ }
310
+ if (parts.length === 0) continue;
311
+ lines.push(`${index === 0 ? prefix : pad}${harness} \xB7 ${parts.join(" \xB7 ")}`);
312
+ index += 1;
313
+ }
314
+ return lines;
315
+ }
316
+ function tokenBoxLines(usage, usageByAgent) {
317
+ const agents = usageByAgent ? Object.keys(usageByAgent) : [];
318
+ if (agents.length > 1) {
319
+ const lines = [];
320
+ for (const [index, id] of agents.entries()) {
321
+ const tokens2 = formatTokens(usageByAgent?.[id]);
322
+ if (tokens2 === "") continue;
323
+ lines.push(index === 0 ? `Tokens: ${id} \xB7 ${tokens2}` : ` ${id} \xB7 ${tokens2}`);
324
+ }
325
+ return lines;
326
+ }
327
+ const tokens = formatTokens(usage);
328
+ return tokens === "" ? [] : [`Tokens: ${tokens}`];
329
+ }
330
+ function tokenSummaryLines(usage, usageByAgent, prefix = " Tokens: ") {
331
+ const agents = usageByAgent ? Object.keys(usageByAgent) : [];
332
+ if (agents.length > 1) {
333
+ const pad = " ".repeat(prefix.length);
334
+ const lines = [];
335
+ for (const [index, id] of agents.entries()) {
336
+ const tokens2 = formatTokens(usageByAgent?.[id]);
337
+ if (tokens2 === "") continue;
338
+ lines.push(`${index === 0 ? prefix : pad}${id} \xB7 ${tokens2}`);
339
+ }
340
+ return lines;
341
+ }
342
+ const tokens = formatTokens(usage);
343
+ return tokens === "" ? [] : [`${prefix}${tokens}`];
344
+ }
250
345
  function buildRunSummaryLines(info) {
251
346
  const lines = [
252
347
  ` Branch: ${info.branchName}${info.noBranch ? " (current)" : ""}`,
253
348
  ` Stories: ${info.storyCount}`,
254
349
  ` Duration: ${formatDuration(info.elapsedSeconds)}`
255
350
  ];
256
- const tokens = formatTokens(info.usage);
257
- if (tokens !== "") {
258
- lines.push(` Tokens: ${tokens}`);
259
- }
351
+ lines.push(...tokenSummaryLines(info.usage, info.usageByAgent));
260
352
  if (!info.noBranch) {
261
353
  lines.push(` PR: ${info.prUrl}`);
262
354
  }
355
+ if (info.verification != null) {
356
+ const label = info.verification.verdict === "passed" ? "passed" : info.verification.verdict === "failed" ? "failed" : "unverified";
357
+ lines.push(` Contract: ${label}`);
358
+ }
263
359
  const review = info.prReview;
264
360
  if (review) {
265
361
  const verdict = review.recommendation ?? (review.requestedChanges ? "REQUEST_CHANGES" : "unknown");
@@ -320,6 +416,14 @@ function printRunSummary(info) {
320
416
  printWarning(
321
417
  `Pipeline finished for issue #${info.issueNumber}, but the PR review requested changes.`
322
418
  );
419
+ } else if (info.verification?.verdict === "unverified") {
420
+ printWarning(
421
+ `Pipeline finished for issue #${info.issueNumber}, but the acceptance contract is unverified.`
422
+ );
423
+ } else if (info.verification?.verdict === "failed") {
424
+ printWarning(
425
+ `Pipeline finished for issue #${info.issueNumber}, but the acceptance contract failed.`
426
+ );
323
427
  } else {
324
428
  printSuccess(`Pipeline complete for issue #${info.issueNumber}!`);
325
429
  }
@@ -328,81 +432,68 @@ function printRunSummary(info) {
328
432
  }
329
433
  }
330
434
 
435
+ // src/ui/text.ts
436
+ function stripMarkdown(text) {
437
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^\s*[-*+]\s+/gm, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[ \t]{2,}/g, " ").trim();
438
+ }
439
+ function failureExcerpt(output, limit = 8) {
440
+ return output.split("\n").map((line) => stripMarkdown(line)).filter((line) => line.length > 0).slice(-limit);
441
+ }
442
+
331
443
  // src/core/executor.ts
332
- import { execa } from "execa";
333
444
  async function executeClaude(prompt, options = {}) {
334
- const subprocess = execa(
335
- "claude",
336
- ["--dangerously-skip-permissions", "--print", "--output-format", "stream-json", "--verbose"],
337
- {
338
- input: prompt,
339
- reject: false,
445
+ let selected;
446
+ try {
447
+ selected = await invokeSelectedAgent({
448
+ prompt,
449
+ phase: "execute",
340
450
  timeout: 0,
341
- // No timeout — the engine bounds this by iterations
342
- stripFinalNewline: false
343
- }
344
- );
345
- const unregisterChild = registerChild({
346
- kill: (signal) => subprocess.kill(signal),
347
- done: subprocess.then(
348
- () => void 0,
349
- () => void 0
350
- )
351
- });
352
- const watchdog = createWatchdog({
353
- ...options.inactivityTimeoutMs === void 0 ? {} : { inactivityTimeoutMs: options.inactivityTimeoutMs },
354
- child: {
355
- kill: (signal) => subprocess.kill(signal),
356
- done: subprocess.then(
357
- () => void 0,
358
- () => void 0
359
- )
360
- }
361
- });
362
- let streamed = {
363
- result: "",
364
- isError: false,
365
- usage: null,
366
- events: 0,
367
- raw: ""
368
- };
369
- if (subprocess.stdout) {
370
- streamed = await readClaudeStream(subprocess.stdout, { onLine: () => watchdog.beat() });
371
- }
372
- const result = await subprocess;
373
- watchdog.stop();
374
- unregisterChild();
375
- const stdout = result.stdout?.toString() ?? "";
376
- const stderr = result.stderr?.toString() ?? "";
377
- const exitCode = result.exitCode ?? 1;
378
- const printed = streamed.raw === "" ? stdout : streamed.raw;
379
- let output = printed + (stderr ? `
380
- ${stderr}` : "");
381
- let cost = null;
382
- if (watchdog.stalled) {
451
+ permission: "autonomous",
452
+ inactivityTimeoutMs: options.inactivityTimeoutMs,
453
+ iteration: options.iteration,
454
+ correctionCycle: options.correctionCycle,
455
+ storyIds: options.storyIds
456
+ });
457
+ } catch (err) {
383
458
  return {
384
- exitCode: exitCode === 0 ? 1 : exitCode,
385
- output: describeStall(watchdog.silentMs),
459
+ exitCode: 1,
460
+ output: err instanceof Error ? err.message : String(err),
386
461
  cost: null
387
462
  };
388
463
  }
389
- if (exitCode === 0 && streamed.result !== "") {
390
- output = streamed.result;
391
- cost = streamed.usage;
392
- }
393
- const onOutput = getOutputCallback();
394
- if (onOutput) {
395
- const trimmed = output.trim();
396
- if (trimmed) {
397
- onOutput(trimmed);
398
- }
464
+ const { run } = selected;
465
+ let output;
466
+ let cost = run.usage;
467
+ if (!run.success || run.exitCode !== 0) {
468
+ output = run.rawOutput || run.error || "";
469
+ cost = null;
470
+ } else {
471
+ output = run.result || run.rawOutput;
399
472
  }
473
+ emitAgentOutput(output, !(run.success && run.exitCode === 0));
400
474
  return {
401
- exitCode,
475
+ exitCode: run.exitCode === 0 && run.success ? 0 : run.exitCode === 0 ? 1 : run.exitCode,
402
476
  output,
403
477
  cost
404
478
  };
405
479
  }
480
+ function emitAgentOutput(output, failed) {
481
+ const onOutput = getOutputCallback();
482
+ if (!onOutput) return;
483
+ const trimmed = output.trim();
484
+ if (!trimmed) return;
485
+ if (isVerbose()) {
486
+ for (const line of trimmed.split("\n")) {
487
+ if (line.trim()) onOutput(line);
488
+ }
489
+ return;
490
+ }
491
+ if (failed) {
492
+ for (const line of failureExcerpt(trimmed)) {
493
+ onOutput(line);
494
+ }
495
+ }
496
+ }
406
497
 
407
498
  // src/core/engine.ts
408
499
  function emitLog(message) {
@@ -427,12 +518,50 @@ function newlyCompletedStoryIds(before, after) {
427
518
  const wasPassing = new Map(before.map((story) => [story.id, story.passes]));
428
519
  return after.filter((story) => story.passes && wasPassing.get(story.id) === false).map((story) => story.id);
429
520
  }
430
- function commitPlaceholders(scope, convention) {
431
- const suffix = scope === void 0 || scope === "" ? "" : `(${scope})`;
521
+ function numericIssue(value) {
522
+ if (value === void 0 || value === null || value === "") return void 0;
523
+ const parsed = typeof value === "number" ? value : Number(value);
524
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
525
+ }
526
+ function commitPlaceholders(scope, convention, options) {
432
527
  const declared = convention !== void 0 && convention !== null && convention !== "";
528
+ const type = options?.type ?? "feat";
529
+ const issueNumber = numericIssue(options?.issueNumber);
530
+ const resolvedScope = scope === void 0 || scope === "" ? void 0 : scope;
531
+ if (declared && options?.type === void 0) {
532
+ const suffix = resolvedScope === void 0 ? "" : `(${resolvedScope})`;
533
+ return {
534
+ __COMMIT_MESSAGE__: commitMessage({
535
+ type,
536
+ scope: resolvedScope,
537
+ subject: "[Story ID] - [Story Title]",
538
+ issueNumber,
539
+ signoff: options?.signoff
540
+ }).replace(`${type}${suffix}:`, `<type>${suffix}:`),
541
+ __FIX_COMMIT_MESSAGE__: commitMessage({
542
+ type: "fix",
543
+ scope: resolvedScope,
544
+ subject: "address review findings",
545
+ issueNumber,
546
+ signoff: options?.signoff
547
+ })
548
+ };
549
+ }
433
550
  return {
434
- __COMMIT_MESSAGE__: declared ? `<type>${suffix}: [Story ID] - [Story Title]` : `feat${suffix}: [Story ID] - [Story Title]`,
435
- __FIX_COMMIT_MESSAGE__: `fix${suffix}: address review findings`
551
+ __COMMIT_MESSAGE__: commitMessage({
552
+ type,
553
+ scope: resolvedScope,
554
+ subject: "[Story ID] - [Story Title]",
555
+ issueNumber,
556
+ signoff: options?.signoff
557
+ }),
558
+ __FIX_COMMIT_MESSAGE__: commitMessage({
559
+ type: "fix",
560
+ scope: resolvedScope,
561
+ subject: "address review findings",
562
+ issueNumber,
563
+ signoff: options?.signoff
564
+ })
436
565
  };
437
566
  }
438
567
  function inactivityOptions() {
@@ -482,7 +611,7 @@ async function archiveIfBranchChanged(plan, paths) {
482
611
  }
483
612
  if (currentBranch && lastBranch && currentBranch !== lastBranch) {
484
613
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
485
- const folderName = lastBranch.replace(/^issue\//, "").replace(/[<>:"|?*\\]/g, "_");
614
+ const folderName = archiveFolderName(lastBranch);
486
615
  const archiveFolder = join(archiveDir, `${dateStr}-${folderName}`);
487
616
  printInfo(`Archiving previous run: ${lastBranch}`);
488
617
  await mkdir(archiveFolder, { recursive: true });
@@ -541,14 +670,20 @@ async function runEngine(config, paths) {
541
670
  emitLog("All user stories already pass. Marking issue as completed.");
542
671
  plan = markIssueCompleted(plan);
543
672
  await saveTaskPlan(paths.prdFile, plan);
544
- return 0;
673
+ return finishWithAcceptance(config, paths, plan);
545
674
  }
546
675
  await archiveIfBranchChanged(plan, paths);
547
676
  await trackBranch(plan, paths.lastBranchFile);
548
677
  await ensureProgressFile(paths.progressFile);
549
678
  const promptTemplate = await loadPrompt("execute", { projectRoot: paths.projectRoot });
550
679
  const policy = await resolvePolicyPlaceholders({ root: paths.projectRoot });
551
- printStartupHeader(config, plan);
680
+ const signoff = (await loadGlobalConfig()).commit?.signoff === true;
681
+ const executeAgent = await resolveAgentFor("execute");
682
+ printStartupHeader(config, plan, {
683
+ provider: executeAgent.provider,
684
+ model: executeAgent.model,
685
+ ...executeAgent.codex.reasoningEffort ? { detail: executeAgent.codex.reasoningEffort } : {}
686
+ });
552
687
  const startTime = Date.now();
553
688
  let i = 0;
554
689
  let totalRetryCount = 0;
@@ -570,6 +705,13 @@ async function runEngine(config, paths) {
570
705
  const attempted = await withRetry(
571
706
  async () => {
572
707
  plan = await loadTaskPlan(paths.prdFile);
708
+ if (plan.userStories.length > 0) {
709
+ getSessionPublisher().publish({
710
+ type: "stories:update",
711
+ at: isoNow(),
712
+ stories: plan.userStories
713
+ });
714
+ }
573
715
  const storiesBefore = plan.userStories;
574
716
  const activeStoryId = selectActiveStory(plan.userStories)?.id;
575
717
  getSessionPublisher().publish({
@@ -588,13 +730,22 @@ async function runEngine(config, paths) {
588
730
  ...policy,
589
731
  // The convention comes off the projection above rather than from a second
590
732
  // discovery: one resolution per run is the whole point of the cache.
591
- ...commitPlaceholders(config.commitScope, policy.__COMMIT_CONVENTION__)
733
+ ...commitPlaceholders(config.commitScope, policy.__COMMIT_CONVENTION__, {
734
+ issueNumber: plan.issueNumber,
735
+ signoff
736
+ }),
737
+ __STORIES_PER_ITERATION__: String(config.storiesPerIteration ?? 1)
592
738
  });
593
739
  const startedAt = isoNow();
594
740
  plan = markIssueInProgress(plan, startedAt);
595
741
  await saveTaskPlan(paths.prdFile, plan);
596
742
  const startedAtMs = Date.now();
597
- const result2 = await executeClaude(prompt, inactivityOptions());
743
+ const result2 = await executeClaude(prompt, {
744
+ ...inactivityOptions(),
745
+ iteration: i,
746
+ correctionCycle: plan.correctionCycle,
747
+ ...activeStoryId === void 0 ? {} : { storyIds: [activeStoryId] }
748
+ });
598
749
  const seconds = elapsedSecondsSince(startedAtMs);
599
750
  if (result2.exitCode !== 0) {
600
751
  publishIterationMetrics(i, result2.cost, seconds);
@@ -617,6 +768,19 @@ async function runEngine(config, paths) {
617
768
  if (!failure.retryable) {
618
769
  plan = await loadTaskPlan(paths.prdFile);
619
770
  plan = setLastError(plan, "fatal_claude_failure", errorMessage);
771
+ if (resolvePolicy(failure.kind, getActiveResilienceConfig()).onExhausted === "block") {
772
+ plan = {
773
+ ...plan,
774
+ runState: {
775
+ currentPhase: "execute",
776
+ attempt,
777
+ owner: plan.runState?.owner ?? null,
778
+ status: "blocked",
779
+ blockedReason: errorMessage,
780
+ lastHeartbeatAt: isoNow()
781
+ }
782
+ };
783
+ }
620
784
  await saveTaskPlan(paths.prdFile, plan);
621
785
  return;
622
786
  }
@@ -691,7 +855,6 @@ async function runEngine(config, paths) {
691
855
  }
692
856
  getSessionPublisher().publish({ type: "iteration:end", at: isoNow(), iteration: i });
693
857
  publishIterationMetrics(i, result.cost, iterationSeconds);
694
- await publishGitState(getSessionPublisher());
695
858
  if (result.output.includes("<promise>COMPLETE</promise>")) {
696
859
  plan = await loadTaskPlan(paths.prdFile);
697
860
  if (allStoriesPass(plan) && !hasPendingCorrection(plan)) {
@@ -707,7 +870,7 @@ async function runEngine(config, paths) {
707
870
  void 0,
708
871
  getPhaseUsageTotals(EXECUTE_PHASE)
709
872
  );
710
- return 0;
873
+ return finishWithAcceptance(config, paths, plan);
711
874
  }
712
875
  plan = setLastError(
713
876
  plan,
@@ -723,7 +886,6 @@ async function runEngine(config, paths) {
723
886
  if (isVerbose()) {
724
887
  printSuccess(`Iteration ${i} complete. Continuing...`);
725
888
  }
726
- await sleep(2);
727
889
  }
728
890
  const elapsed = Math.floor((Date.now() - startTime) / 1e3);
729
891
  plan = await loadTaskPlan(paths.prdFile);
@@ -738,6 +900,21 @@ async function runEngine(config, paths) {
738
900
  );
739
901
  return 1;
740
902
  }
903
+ async function finishWithAcceptance(config, paths, plan) {
904
+ const issueDir = resolveIssueDir(config, paths);
905
+ const outcome = await runAcceptanceGate({
906
+ issueDir,
907
+ cwd: paths.projectRoot,
908
+ addDirs: config.issueNumber === void 0 ? void 0 : [issueDir],
909
+ skipReviewer: true
910
+ });
911
+ const applied = applyAcceptanceToPlan(plan, outcome);
912
+ if (applied.failed) {
913
+ await saveTaskPlan(paths.prdFile, applied.plan);
914
+ return 1;
915
+ }
916
+ return 0;
917
+ }
741
918
 
742
919
  // src/commands/execute.ts
743
920
  async function runExecute(positionalMaxIter, options) {
@@ -758,23 +935,73 @@ async function runExecute(positionalMaxIter, options) {
758
935
  commitScope: options.commitScope
759
936
  });
760
937
  const paths = await resolvePaths(config);
761
- const exitCode = await runEngine(config, paths);
762
- if (exitCode === 0 && config.issueNumber) {
763
- try {
764
- const plan = await loadTaskPlan(paths.prdFile);
765
- if (allStoriesPass(plan)) {
766
- plan.pipeline.executionCompleted = true;
767
- await saveTaskPlan(paths.prdFile, plan);
938
+ const webConfig = await loadWebConfig();
939
+ const inheritedPublisher = getSessionPublisher();
940
+ let standalonePublisher = null;
941
+ if (webConfig.enabled && config.issueNumber !== void 0 && inheritedPublisher instanceof NullPublisher) {
942
+ const issuePaths = await resolveIssuePaths(config.issueNumber, {
943
+ projectRoot: paths.projectRoot
944
+ });
945
+ standalonePublisher = new FilePublisher(issuePaths.sessionFile, {
946
+ logLimit: webConfig.logLimit,
947
+ includeLogs: webConfig.includeLogs
948
+ });
949
+ setSessionPublisher(standalonePublisher);
950
+ const numericIssue2 = /^\d+$/.test(config.issueNumber) ? Number(config.issueNumber) : null;
951
+ standalonePublisher.publish({
952
+ type: "session:start",
953
+ at: isoNow(),
954
+ sessionId: randomUUID(),
955
+ issueNumber: numericIssue2,
956
+ phases: ["execute"]
957
+ });
958
+ standalonePublisher.publish({ type: "phase:start", at: isoNow(), phase: "execute" });
959
+ await ensureWebMonitor({
960
+ publisher: standalonePublisher,
961
+ port: webConfig.port,
962
+ host: webConfig.host,
963
+ refreshSeconds: webConfig.refreshSeconds
964
+ });
965
+ }
966
+ let exitCode = null;
967
+ try {
968
+ exitCode = await runEngine(config, paths);
969
+ if (exitCode === 0 && config.issueNumber) {
970
+ try {
971
+ const plan = await loadTaskPlan(paths.prdFile);
972
+ if (allStoriesPass(plan)) {
973
+ plan.pipeline.executionCompleted = true;
974
+ await saveTaskPlan(paths.prdFile, plan);
975
+ }
976
+ } catch {
768
977
  }
769
- } catch {
978
+ }
979
+ return exitCode;
980
+ } finally {
981
+ if (standalonePublisher !== null) {
982
+ const success = exitCode === 0;
983
+ standalonePublisher.publish({
984
+ type: "phase:end",
985
+ at: isoNow(),
986
+ phase: "execute",
987
+ success,
988
+ ...success ? {} : { error: "Execute phase failed" }
989
+ });
990
+ standalonePublisher.publish({
991
+ type: "session:end",
992
+ at: isoNow(),
993
+ status: success ? "completed" : "failed"
994
+ });
995
+ await standalonePublisher.close();
996
+ setSessionPublisher(void 0);
770
997
  }
771
998
  }
772
- return exitCode;
773
999
  }
774
1000
 
775
1001
  export {
1002
+ stripMarkdown,
776
1003
  printQueueSummary,
777
1004
  printRunSummary,
778
1005
  runExecute
779
1006
  };
780
- //# sourceMappingURL=chunk-3QHK4YQA.js.map
1007
+ //# sourceMappingURL=chunk-B72RQZBV.js.map