issue-flow 0.12.0 → 0.13.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 (185) hide show
  1. package/README.md +79 -274
  2. package/dist/agent-BXRIEKVM.js +238 -0
  3. package/dist/agent-BXRIEKVM.js.map +1 -0
  4. package/dist/{analyze-OEET7KGE.js → analyze-WS47UYGN.js} +28 -16
  5. package/dist/analyze-WS47UYGN.js.map +1 -0
  6. package/dist/{apply-ZIA3C52S.js → apply-OS62UWRN.js} +15 -7
  7. package/dist/{apply-ZIA3C52S.js.map → apply-OS62UWRN.js.map} +1 -1
  8. package/dist/bench-SA3BOME5.js +906 -0
  9. package/dist/bench-SA3BOME5.js.map +1 -0
  10. package/dist/chunk-37N2RJW2.js +120 -0
  11. package/dist/chunk-37N2RJW2.js.map +1 -0
  12. package/dist/chunk-3XLCNVMD.js +180 -0
  13. package/dist/chunk-3XLCNVMD.js.map +1 -0
  14. package/dist/chunk-5TB6KLC2.js +72 -0
  15. package/dist/chunk-5TB6KLC2.js.map +1 -0
  16. package/dist/{chunk-MJULIKQU.js → chunk-664Y3NDX.js} +23 -47
  17. package/dist/chunk-664Y3NDX.js.map +1 -0
  18. package/dist/chunk-6FDFRNXZ.js +357 -0
  19. package/dist/chunk-6FDFRNXZ.js.map +1 -0
  20. package/dist/{chunk-56KS3TFQ.js → chunk-7KD66LIM.js} +115 -160
  21. package/dist/chunk-7KD66LIM.js.map +1 -0
  22. package/dist/chunk-7Z4Y3VKM.js +26 -0
  23. package/dist/chunk-7Z4Y3VKM.js.map +1 -0
  24. package/dist/chunk-ARFVHC2F.js +1111 -0
  25. package/dist/chunk-ARFVHC2F.js.map +1 -0
  26. package/dist/{chunk-NS6R37II.js → chunk-BHJCGWQX.js} +141 -12
  27. package/dist/chunk-BHJCGWQX.js.map +1 -0
  28. package/dist/chunk-BQDQTIM7.js +74 -0
  29. package/dist/chunk-BQDQTIM7.js.map +1 -0
  30. package/dist/chunk-CNBYCMSU.js +2709 -0
  31. package/dist/chunk-CNBYCMSU.js.map +1 -0
  32. package/dist/chunk-CRGIQFUQ.js +83 -0
  33. package/dist/chunk-CRGIQFUQ.js.map +1 -0
  34. package/dist/chunk-DIG5XKQQ.js +298 -0
  35. package/dist/chunk-DIG5XKQQ.js.map +1 -0
  36. package/dist/chunk-FZDR7CGH.js +71 -0
  37. package/dist/chunk-FZDR7CGH.js.map +1 -0
  38. package/dist/chunk-HEF2RMKN.js +413 -0
  39. package/dist/chunk-HEF2RMKN.js.map +1 -0
  40. package/dist/{chunk-M5UCXTIT.js → chunk-HJGGNLWI.js} +22 -18
  41. package/dist/chunk-HJGGNLWI.js.map +1 -0
  42. package/dist/chunk-HQAHNIAR.js +283 -0
  43. package/dist/chunk-HQAHNIAR.js.map +1 -0
  44. package/dist/{chunk-FJP3UQGD.js → chunk-IHIEXUTC.js} +39 -19
  45. package/dist/chunk-IHIEXUTC.js.map +1 -0
  46. package/dist/{chunk-Y5ZDJ4PJ.js → chunk-IV5SS4JN.js} +18 -10
  47. package/dist/chunk-IV5SS4JN.js.map +1 -0
  48. package/dist/{chunk-BYZDPR7N.js → chunk-IYV6KNVF.js} +146 -13
  49. package/dist/chunk-IYV6KNVF.js.map +1 -0
  50. package/dist/{chunk-TSOLWK7Y.js → chunk-J62QSVP6.js} +8 -8
  51. package/dist/chunk-KQHB7ZT3.js +559 -0
  52. package/dist/chunk-KQHB7ZT3.js.map +1 -0
  53. package/dist/chunk-L6HSKTQE.js +77 -0
  54. package/dist/chunk-L6HSKTQE.js.map +1 -0
  55. package/dist/{chunk-3QHK4YQA.js → chunk-LZ6GFQAG.js} +334 -117
  56. package/dist/chunk-LZ6GFQAG.js.map +1 -0
  57. package/dist/{chunk-LAFNLLQP.js → chunk-MIVHK5YI.js} +5 -5
  58. package/dist/chunk-NEZZ6KDR.js +1217 -0
  59. package/dist/chunk-NEZZ6KDR.js.map +1 -0
  60. package/dist/{chunk-NTMMF5UD.js → chunk-OPNGPUKJ.js} +436 -254
  61. package/dist/chunk-OPNGPUKJ.js.map +1 -0
  62. package/dist/chunk-P3LRUNC6.js +16 -0
  63. package/dist/chunk-P3LRUNC6.js.map +1 -0
  64. package/dist/{chunk-FADTCQGM.js → chunk-PIVGJM6K.js} +94 -32
  65. package/dist/chunk-PIVGJM6K.js.map +1 -0
  66. package/dist/{chunk-73ATPIYX.js → chunk-PV4OX5TC.js} +36 -71
  67. package/dist/chunk-PV4OX5TC.js.map +1 -0
  68. package/dist/chunk-QCXPNSEL.js +141 -0
  69. package/dist/chunk-QCXPNSEL.js.map +1 -0
  70. package/dist/{chunk-XQUSIEKA.js → chunk-QXDNBYQF.js} +52 -14
  71. package/dist/chunk-QXDNBYQF.js.map +1 -0
  72. package/dist/chunk-SNTTOQAD.js +30 -0
  73. package/dist/chunk-SNTTOQAD.js.map +1 -0
  74. package/dist/{chunk-QCPGHBSZ.js → chunk-TM67EHSV.js} +63 -21
  75. package/dist/chunk-TM67EHSV.js.map +1 -0
  76. package/dist/chunk-TVN2JVOF.js +77 -0
  77. package/dist/chunk-TVN2JVOF.js.map +1 -0
  78. package/dist/chunk-TX5WIVYP.js +129 -0
  79. package/dist/chunk-TX5WIVYP.js.map +1 -0
  80. package/dist/{chunk-A4SLHOW7.js → chunk-UJ7XWK7J.js} +24 -18
  81. package/dist/chunk-UJ7XWK7J.js.map +1 -0
  82. package/dist/{chunk-EZATO3DW.js → chunk-UOCPC2Y2.js} +594 -17
  83. package/dist/chunk-UOCPC2Y2.js.map +1 -0
  84. package/dist/chunk-XEYH6ZZI.js +289 -0
  85. package/dist/chunk-XEYH6ZZI.js.map +1 -0
  86. package/dist/cli.js +264 -44
  87. package/dist/cli.js.map +1 -1
  88. package/dist/{config-PRJGWW7J.js → config-MEM2IWWN.js} +28 -5
  89. package/dist/contract-JH2WBIO6.js +8 -0
  90. package/dist/conventions-WV4NDXGH.js +143 -0
  91. package/dist/conventions-WV4NDXGH.js.map +1 -0
  92. package/dist/execute-NITIHHCK.js +31 -0
  93. package/dist/{generate-EGB4KV2B.js → generate-MVQI7A45.js} +34 -20
  94. package/dist/{generate-EGB4KV2B.js.map → generate-MVQI7A45.js.map} +1 -1
  95. package/dist/git-YRVRUN3C.js +31 -0
  96. package/dist/init-OKWEC3EV.js +21 -0
  97. package/dist/{operations-BYT626KQ.js → operations-LU6JA3BX.js} +24 -15
  98. package/dist/{operations-BYT626KQ.js.map → operations-LU6JA3BX.js.map} +1 -1
  99. package/dist/permissions-7NMDFU27.js +18 -0
  100. package/dist/plan-LEJJF3DE.js +31 -0
  101. package/dist/{policy-6S4GFC76.js → policy-JSYEK5B2.js} +17 -6
  102. package/dist/policy-JSYEK5B2.js.map +1 -0
  103. package/dist/pr-HPVUZ7S3.js +36 -0
  104. package/dist/pr-review-R2QZBT3F.js +29 -0
  105. package/dist/prd-5O6GYCC6.js +30 -0
  106. package/dist/ps-TNE44R5V.js +90 -0
  107. package/dist/ps-TNE44R5V.js.map +1 -0
  108. package/dist/recorder-XGF55WXI.js +31 -0
  109. package/dist/recorder-XGF55WXI.js.map +1 -0
  110. package/dist/registry-LGPE53QD.js +15 -0
  111. package/dist/registry-LGPE53QD.js.map +1 -0
  112. package/dist/{resume-PPCNQVVV.js → resume-QMEPMSZW.js} +51 -34
  113. package/dist/{resume-PPCNQVVV.js.map → resume-QMEPMSZW.js.map} +1 -1
  114. package/dist/review-YKWD6SNN.js +32 -0
  115. package/dist/review-YKWD6SNN.js.map +1 -0
  116. package/dist/routing-2ZUMZPJB.js +91 -0
  117. package/dist/routing-2ZUMZPJB.js.map +1 -0
  118. package/dist/run-YU4TR62R.js +50 -0
  119. package/dist/run-YU4TR62R.js.map +1 -0
  120. package/dist/{state-manager-2EDVDBAR.js → state-manager-X3PT3FGT.js} +4 -2
  121. package/dist/state-manager-X3PT3FGT.js.map +1 -0
  122. package/dist/usage-VCFLDA5Y.js +174 -0
  123. package/dist/usage-VCFLDA5Y.js.map +1 -0
  124. package/dist/{web-EF5WIIGG.js → web-XVHLX62B.js} +41 -30
  125. package/dist/web-XVHLX62B.js.map +1 -0
  126. package/package.json +6 -2
  127. package/prompts/execute.md +7 -8
  128. package/prompts/plan.md +4 -5
  129. package/prompts/pr.md +2 -2
  130. package/web/AGENTS.md +3 -3
  131. package/web/public/app.css +38 -0
  132. package/web/public/app.js +121 -11
  133. package/web/public/index.html +24 -0
  134. package/dist/analyze-OEET7KGE.js.map +0 -1
  135. package/dist/chunk-3QHK4YQA.js.map +0 -1
  136. package/dist/chunk-42PJFMAR.js +0 -605
  137. package/dist/chunk-42PJFMAR.js.map +0 -1
  138. package/dist/chunk-4JSOYE4G.js +0 -368
  139. package/dist/chunk-4JSOYE4G.js.map +0 -1
  140. package/dist/chunk-56KS3TFQ.js.map +0 -1
  141. package/dist/chunk-73ATPIYX.js.map +0 -1
  142. package/dist/chunk-A4SLHOW7.js.map +0 -1
  143. package/dist/chunk-BAUPQXPE.js +0 -173
  144. package/dist/chunk-BAUPQXPE.js.map +0 -1
  145. package/dist/chunk-BYZDPR7N.js.map +0 -1
  146. package/dist/chunk-CD5ULRVC.js +0 -615
  147. package/dist/chunk-CD5ULRVC.js.map +0 -1
  148. package/dist/chunk-DIDUVL6O.js +0 -1365
  149. package/dist/chunk-DIDUVL6O.js.map +0 -1
  150. package/dist/chunk-EZATO3DW.js.map +0 -1
  151. package/dist/chunk-FADTCQGM.js.map +0 -1
  152. package/dist/chunk-FJP3UQGD.js.map +0 -1
  153. package/dist/chunk-LWRSMTTZ.js +0 -18
  154. package/dist/chunk-LWRSMTTZ.js.map +0 -1
  155. package/dist/chunk-M5UCXTIT.js.map +0 -1
  156. package/dist/chunk-M66SJHQQ.js +0 -206
  157. package/dist/chunk-M66SJHQQ.js.map +0 -1
  158. package/dist/chunk-MJULIKQU.js.map +0 -1
  159. package/dist/chunk-NS6R37II.js.map +0 -1
  160. package/dist/chunk-NTMMF5UD.js.map +0 -1
  161. package/dist/chunk-QCPGHBSZ.js.map +0 -1
  162. package/dist/chunk-XQUSIEKA.js.map +0 -1
  163. package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
  164. package/dist/execute-XBNVL753.js +0 -18
  165. package/dist/init-AJHCXJBP.js +0 -10
  166. package/dist/plan-QOZ3RIO6.js +0 -21
  167. package/dist/policy-6S4GFC76.js.map +0 -1
  168. package/dist/pr-EBBHD2ZX.js +0 -26
  169. package/dist/pr-review-VVMW5PO4.js +0 -20
  170. package/dist/prd-JQSZF7PR.js +0 -21
  171. package/dist/review-3F6MARWH.js +0 -19
  172. package/dist/run-RXE5GDQO.js +0 -36
  173. package/dist/web-EF5WIIGG.js.map +0 -1
  174. /package/dist/{chunk-TSOLWK7Y.js.map → chunk-J62QSVP6.js.map} +0 -0
  175. /package/dist/{chunk-LAFNLLQP.js.map → chunk-MIVHK5YI.js.map} +0 -0
  176. /package/dist/{config-PRJGWW7J.js.map → config-MEM2IWWN.js.map} +0 -0
  177. /package/dist/{execute-XBNVL753.js.map → contract-JH2WBIO6.js.map} +0 -0
  178. /package/dist/{init-AJHCXJBP.js.map → execute-NITIHHCK.js.map} +0 -0
  179. /package/dist/{plan-QOZ3RIO6.js.map → git-YRVRUN3C.js.map} +0 -0
  180. /package/dist/{pr-EBBHD2ZX.js.map → init-OKWEC3EV.js.map} +0 -0
  181. /package/dist/{pr-review-VVMW5PO4.js.map → permissions-7NMDFU27.js.map} +0 -0
  182. /package/dist/{prd-JQSZF7PR.js.map → plan-LEJJF3DE.js.map} +0 -0
  183. /package/dist/{review-3F6MARWH.js.map → pr-HPVUZ7S3.js.map} +0 -0
  184. /package/dist/{run-RXE5GDQO.js.map → pr-review-R2QZBT3F.js.map} +0 -0
  185. /package/dist/{state-manager-2EDVDBAR.js.map → prd-5O6GYCC6.js.map} +0 -0
@@ -1,43 +1,51 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- publishGitState
4
- } from "./chunk-TSOLWK7Y.js";
3
+ ensureWebMonitor
4
+ } from "./chunk-IHIEXUTC.js";
5
5
  import {
6
- sleep
7
- } from "./chunk-LWRSMTTZ.js";
6
+ applyAcceptanceToPlan,
7
+ resolveIssueDir,
8
+ runAcceptanceGate
9
+ } from "./chunk-BQDQTIM7.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-664Y3NDX.js";
17
22
  import {
18
23
  applyPlaceholders,
19
24
  loadPrompt
20
- } from "./chunk-LAFNLLQP.js";
25
+ } from "./chunk-MIVHK5YI.js";
21
26
  import {
22
- createWatchdog,
23
- describeStall,
24
- getShutdownSignal,
25
- registerChild
26
- } from "./chunk-M66SJHQQ.js";
27
+ invokeSelectedAgent
28
+ } from "./chunk-ARFVHC2F.js";
29
+ import {
30
+ archiveFolderName,
31
+ commitMessage
32
+ } from "./chunk-UOCPC2Y2.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-CNBYCMSU.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-HQAHNIAR.js";
40
46
  import {
47
+ FilePublisher,
48
+ NullPublisher,
41
49
  formatDuration,
42
50
  getIcons,
43
51
  getInactivityTimeout,
@@ -52,9 +60,10 @@ import {
52
60
  printRetry,
53
61
  printSuccess,
54
62
  printWarning,
63
+ setSessionPublisher,
55
64
  useColor,
56
65
  useUnicode
57
- } from "./chunk-BYZDPR7N.js";
66
+ } from "./chunk-IYV6KNVF.js";
58
67
  import {
59
68
  allStoriesPass,
60
69
  applyStoryMetrics,
@@ -71,7 +80,21 @@ import {
71
80
  selectActiveStory,
72
81
  setLastError,
73
82
  trimErrorMessage
74
- } from "./chunk-CD5ULRVC.js";
83
+ } from "./chunk-DIG5XKQQ.js";
84
+ import {
85
+ committedStoryIds,
86
+ fixedBackoffPolicy,
87
+ getBaseBranch,
88
+ isWorkingTreeClean,
89
+ resolvePolicy,
90
+ withRetry
91
+ } from "./chunk-KQHB7ZT3.js";
92
+ import {
93
+ classify
94
+ } from "./chunk-XEYH6ZZI.js";
95
+
96
+ // src/commands/execute.ts
97
+ import { randomUUID } from "crypto";
75
98
 
76
99
  // src/core/engine.ts
77
100
  import { existsSync } from "fs";
@@ -188,7 +211,7 @@ function printBox(lines) {
188
211
  }
189
212
  console.log(blue(`${bl}${hrule}${br}`));
190
213
  }
191
- function printStartupHeader(config, plan) {
214
+ function printStartupHeader(config, plan, agent) {
192
215
  const icons = getIcons();
193
216
  const storiesTotal = plan.userStories.length;
194
217
  const storiesPassing = plan.userStories.filter((s) => s.passes).length;
@@ -196,7 +219,7 @@ function printStartupHeader(config, plan) {
196
219
  const issueLabel = config.issueNumber ? `Issue #${config.issueNumber}` : "Standalone mode";
197
220
  const maxIterLabel = config.maxIterations !== void 0 ? String(config.maxIterations) : "unlimited";
198
221
  const retryLabel = config.retryForever ? "unlimited retries" : `${config.retryLimit} consecutive retries`;
199
- printBox([
222
+ const lines = [
200
223
  `${icons.start} Issue Flow`,
201
224
  "---",
202
225
  `Issue: ${issueLabel}`,
@@ -204,9 +227,22 @@ function printStartupHeader(config, plan) {
204
227
  `Stories: ${storiesPassing}/${storiesTotal} passing`,
205
228
  `Iterations: ${maxIterLabel}`,
206
229
  `Retries: ${retryLabel}`
207
- ]);
230
+ ];
231
+ const remaining = storiesTotal - storiesPassing;
232
+ if (remaining > 0) {
233
+ const p50ExecuteSeconds = 518;
234
+ lines.push(
235
+ `Estimate: ~${formatDuration(remaining * p50ExecuteSeconds)} (${remaining} \xD7 p50 execute)`
236
+ );
237
+ }
238
+ if (agent) {
239
+ const model = agent.model ? ` \xB7 ${agent.model}` : "";
240
+ const detail = agent.detail ? ` (${agent.detail})` : "";
241
+ lines.push(`Agent: ${agent.provider}${model}${detail}`);
242
+ }
243
+ printBox(lines);
208
244
  }
209
- function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan, extraInfo, usage) {
245
+ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan, extraInfo, usage, usageByAgent) {
210
246
  const icons = getIcons();
211
247
  const storiesTotal = plan.userStories.length;
212
248
  const storiesPassing = plan.userStories.filter((s) => s.passes).length;
@@ -235,10 +271,8 @@ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan,
235
271
  `Iterations: ${iterations}`,
236
272
  `Duration: ${duration}`
237
273
  ];
238
- const tokens = formatTokens(usage);
239
- if (tokens !== "") {
240
- boxLines.push(`Tokens: ${tokens}`);
241
- }
274
+ boxLines.push(...tokenBoxLines(usage, usageByAgent));
275
+ boxLines.push(...executionCostLines(plan));
242
276
  boxLines.push(`Retries: ${totalRetries}`);
243
277
  if (extraInfo) {
244
278
  boxLines.push("---");
@@ -247,19 +281,79 @@ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan,
247
281
  console.log("");
248
282
  printBox(boxLines);
249
283
  }
284
+ function executionCostLines(plan, prefix = "Cost: ") {
285
+ const records = plan.executions ?? [];
286
+ if (records.length === 0) return [];
287
+ const lines = [];
288
+ const groups = groupBy(records, "harness");
289
+ let index = 0;
290
+ const pad = " ".repeat(prefix.length);
291
+ for (const [harness, summary] of groups) {
292
+ const parts = [];
293
+ if (summary.totalCost.reported > 0) {
294
+ parts.push(`$${summary.totalCost.reported.toFixed(4)} reported`);
295
+ }
296
+ if (summary.totalCost.estimated > 0) {
297
+ parts.push(`$${summary.totalCost.estimated.toFixed(4)} estimated`);
298
+ }
299
+ if (summary.totalCost.unknownExecutions > 0) {
300
+ parts.push(`${summary.totalCost.unknownExecutions} unknown`);
301
+ }
302
+ if (parts.length === 0) {
303
+ const totals = summarize(records).totalCost;
304
+ if (totals.reported === 0 && totals.estimated === 0) {
305
+ parts.push("not reported");
306
+ }
307
+ }
308
+ if (parts.length === 0) continue;
309
+ lines.push(`${index === 0 ? prefix : pad}${harness} \xB7 ${parts.join(" \xB7 ")}`);
310
+ index += 1;
311
+ }
312
+ return lines;
313
+ }
314
+ function tokenBoxLines(usage, usageByAgent) {
315
+ const agents = usageByAgent ? Object.keys(usageByAgent) : [];
316
+ if (agents.length > 1) {
317
+ const lines = [];
318
+ for (const [index, id] of agents.entries()) {
319
+ const tokens2 = formatTokens(usageByAgent?.[id]);
320
+ if (tokens2 === "") continue;
321
+ lines.push(index === 0 ? `Tokens: ${id} \xB7 ${tokens2}` : ` ${id} \xB7 ${tokens2}`);
322
+ }
323
+ return lines;
324
+ }
325
+ const tokens = formatTokens(usage);
326
+ return tokens === "" ? [] : [`Tokens: ${tokens}`];
327
+ }
328
+ function tokenSummaryLines(usage, usageByAgent, prefix = " Tokens: ") {
329
+ const agents = usageByAgent ? Object.keys(usageByAgent) : [];
330
+ if (agents.length > 1) {
331
+ const pad = " ".repeat(prefix.length);
332
+ const lines = [];
333
+ for (const [index, id] of agents.entries()) {
334
+ const tokens2 = formatTokens(usageByAgent?.[id]);
335
+ if (tokens2 === "") continue;
336
+ lines.push(`${index === 0 ? prefix : pad}${id} \xB7 ${tokens2}`);
337
+ }
338
+ return lines;
339
+ }
340
+ const tokens = formatTokens(usage);
341
+ return tokens === "" ? [] : [`${prefix}${tokens}`];
342
+ }
250
343
  function buildRunSummaryLines(info) {
251
344
  const lines = [
252
345
  ` Branch: ${info.branchName}${info.noBranch ? " (current)" : ""}`,
253
346
  ` Stories: ${info.storyCount}`,
254
347
  ` Duration: ${formatDuration(info.elapsedSeconds)}`
255
348
  ];
256
- const tokens = formatTokens(info.usage);
257
- if (tokens !== "") {
258
- lines.push(` Tokens: ${tokens}`);
259
- }
349
+ lines.push(...tokenSummaryLines(info.usage, info.usageByAgent));
260
350
  if (!info.noBranch) {
261
351
  lines.push(` PR: ${info.prUrl}`);
262
352
  }
353
+ if (info.verification != null) {
354
+ const label = info.verification.verdict === "passed" ? "passed" : info.verification.verdict === "failed" ? "failed" : "unverified";
355
+ lines.push(` Contract: ${label}`);
356
+ }
263
357
  const review = info.prReview;
264
358
  if (review) {
265
359
  const verdict = review.recommendation ?? (review.requestedChanges ? "REQUEST_CHANGES" : "unknown");
@@ -320,6 +414,14 @@ function printRunSummary(info) {
320
414
  printWarning(
321
415
  `Pipeline finished for issue #${info.issueNumber}, but the PR review requested changes.`
322
416
  );
417
+ } else if (info.verification?.verdict === "unverified") {
418
+ printWarning(
419
+ `Pipeline finished for issue #${info.issueNumber}, but the acceptance contract is unverified.`
420
+ );
421
+ } else if (info.verification?.verdict === "failed") {
422
+ printWarning(
423
+ `Pipeline finished for issue #${info.issueNumber}, but the acceptance contract failed.`
424
+ );
323
425
  } else {
324
426
  printSuccess(`Pipeline complete for issue #${info.issueNumber}!`);
325
427
  }
@@ -328,81 +430,65 @@ function printRunSummary(info) {
328
430
  }
329
431
  }
330
432
 
433
+ // src/ui/text.ts
434
+ function stripMarkdown(text) {
435
+ 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();
436
+ }
437
+ function failureExcerpt(output, limit = 8) {
438
+ return output.split("\n").map((line) => stripMarkdown(line)).filter((line) => line.length > 0).slice(-limit);
439
+ }
440
+
331
441
  // src/core/executor.ts
332
- import { execa } from "execa";
333
442
  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,
443
+ let selected;
444
+ try {
445
+ selected = await invokeSelectedAgent({
446
+ prompt,
447
+ phase: "execute",
340
448
  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) {
449
+ permission: "autonomous",
450
+ inactivityTimeoutMs: options.inactivityTimeoutMs
451
+ });
452
+ } catch (err) {
383
453
  return {
384
- exitCode: exitCode === 0 ? 1 : exitCode,
385
- output: describeStall(watchdog.silentMs),
454
+ exitCode: 1,
455
+ output: err instanceof Error ? err.message : String(err),
386
456
  cost: null
387
457
  };
388
458
  }
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
- }
459
+ const { run } = selected;
460
+ let output;
461
+ let cost = run.usage;
462
+ if (!run.success || run.exitCode !== 0) {
463
+ output = run.rawOutput || run.error || "";
464
+ cost = null;
465
+ } else {
466
+ output = run.result || run.rawOutput;
399
467
  }
468
+ emitAgentOutput(output, !(run.success && run.exitCode === 0));
400
469
  return {
401
- exitCode,
470
+ exitCode: run.exitCode === 0 && run.success ? 0 : run.exitCode === 0 ? 1 : run.exitCode,
402
471
  output,
403
472
  cost
404
473
  };
405
474
  }
475
+ function emitAgentOutput(output, failed) {
476
+ const onOutput = getOutputCallback();
477
+ if (!onOutput) return;
478
+ const trimmed = output.trim();
479
+ if (!trimmed) return;
480
+ if (isVerbose()) {
481
+ for (const line of trimmed.split("\n")) {
482
+ if (line.trim()) onOutput(line);
483
+ }
484
+ return;
485
+ }
486
+ if (failed) {
487
+ for (const line of failureExcerpt(trimmed)) {
488
+ onOutput(line);
489
+ }
490
+ }
491
+ }
406
492
 
407
493
  // src/core/engine.ts
408
494
  function emitLog(message) {
@@ -427,12 +513,50 @@ function newlyCompletedStoryIds(before, after) {
427
513
  const wasPassing = new Map(before.map((story) => [story.id, story.passes]));
428
514
  return after.filter((story) => story.passes && wasPassing.get(story.id) === false).map((story) => story.id);
429
515
  }
430
- function commitPlaceholders(scope, convention) {
431
- const suffix = scope === void 0 || scope === "" ? "" : `(${scope})`;
516
+ function numericIssue(value) {
517
+ if (value === void 0 || value === null || value === "") return void 0;
518
+ const parsed = typeof value === "number" ? value : Number(value);
519
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
520
+ }
521
+ function commitPlaceholders(scope, convention, options) {
432
522
  const declared = convention !== void 0 && convention !== null && convention !== "";
523
+ const type = options?.type ?? "feat";
524
+ const issueNumber = numericIssue(options?.issueNumber);
525
+ const resolvedScope = scope === void 0 || scope === "" ? void 0 : scope;
526
+ if (declared && options?.type === void 0) {
527
+ const suffix = resolvedScope === void 0 ? "" : `(${resolvedScope})`;
528
+ return {
529
+ __COMMIT_MESSAGE__: commitMessage({
530
+ type,
531
+ scope: resolvedScope,
532
+ subject: "[Story ID] - [Story Title]",
533
+ issueNumber,
534
+ signoff: options?.signoff
535
+ }).replace(`${type}${suffix}:`, `<type>${suffix}:`),
536
+ __FIX_COMMIT_MESSAGE__: commitMessage({
537
+ type: "fix",
538
+ scope: resolvedScope,
539
+ subject: "address review findings",
540
+ issueNumber,
541
+ signoff: options?.signoff
542
+ })
543
+ };
544
+ }
433
545
  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`
546
+ __COMMIT_MESSAGE__: commitMessage({
547
+ type,
548
+ scope: resolvedScope,
549
+ subject: "[Story ID] - [Story Title]",
550
+ issueNumber,
551
+ signoff: options?.signoff
552
+ }),
553
+ __FIX_COMMIT_MESSAGE__: commitMessage({
554
+ type: "fix",
555
+ scope: resolvedScope,
556
+ subject: "address review findings",
557
+ issueNumber,
558
+ signoff: options?.signoff
559
+ })
436
560
  };
437
561
  }
438
562
  function inactivityOptions() {
@@ -482,7 +606,7 @@ async function archiveIfBranchChanged(plan, paths) {
482
606
  }
483
607
  if (currentBranch && lastBranch && currentBranch !== lastBranch) {
484
608
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
485
- const folderName = lastBranch.replace(/^issue\//, "").replace(/[<>:"|?*\\]/g, "_");
609
+ const folderName = archiveFolderName(lastBranch);
486
610
  const archiveFolder = join(archiveDir, `${dateStr}-${folderName}`);
487
611
  printInfo(`Archiving previous run: ${lastBranch}`);
488
612
  await mkdir(archiveFolder, { recursive: true });
@@ -541,14 +665,20 @@ async function runEngine(config, paths) {
541
665
  emitLog("All user stories already pass. Marking issue as completed.");
542
666
  plan = markIssueCompleted(plan);
543
667
  await saveTaskPlan(paths.prdFile, plan);
544
- return 0;
668
+ return finishWithAcceptance(config, paths, plan);
545
669
  }
546
670
  await archiveIfBranchChanged(plan, paths);
547
671
  await trackBranch(plan, paths.lastBranchFile);
548
672
  await ensureProgressFile(paths.progressFile);
549
673
  const promptTemplate = await loadPrompt("execute", { projectRoot: paths.projectRoot });
550
674
  const policy = await resolvePolicyPlaceholders({ root: paths.projectRoot });
551
- printStartupHeader(config, plan);
675
+ const signoff = (await loadGlobalConfig()).commit?.signoff === true;
676
+ const executeAgent = await resolveAgentFor("execute");
677
+ printStartupHeader(config, plan, {
678
+ provider: executeAgent.provider,
679
+ model: executeAgent.model,
680
+ ...executeAgent.codex.reasoningEffort ? { detail: executeAgent.codex.reasoningEffort } : {}
681
+ });
552
682
  const startTime = Date.now();
553
683
  let i = 0;
554
684
  let totalRetryCount = 0;
@@ -570,6 +700,13 @@ async function runEngine(config, paths) {
570
700
  const attempted = await withRetry(
571
701
  async () => {
572
702
  plan = await loadTaskPlan(paths.prdFile);
703
+ if (plan.userStories.length > 0) {
704
+ getSessionPublisher().publish({
705
+ type: "stories:update",
706
+ at: isoNow(),
707
+ stories: plan.userStories
708
+ });
709
+ }
573
710
  const storiesBefore = plan.userStories;
574
711
  const activeStoryId = selectActiveStory(plan.userStories)?.id;
575
712
  getSessionPublisher().publish({
@@ -588,7 +725,11 @@ async function runEngine(config, paths) {
588
725
  ...policy,
589
726
  // The convention comes off the projection above rather than from a second
590
727
  // discovery: one resolution per run is the whole point of the cache.
591
- ...commitPlaceholders(config.commitScope, policy.__COMMIT_CONVENTION__)
728
+ ...commitPlaceholders(config.commitScope, policy.__COMMIT_CONVENTION__, {
729
+ issueNumber: plan.issueNumber,
730
+ signoff
731
+ }),
732
+ __STORIES_PER_ITERATION__: String(config.storiesPerIteration ?? 1)
592
733
  });
593
734
  const startedAt = isoNow();
594
735
  plan = markIssueInProgress(plan, startedAt);
@@ -617,6 +758,19 @@ async function runEngine(config, paths) {
617
758
  if (!failure.retryable) {
618
759
  plan = await loadTaskPlan(paths.prdFile);
619
760
  plan = setLastError(plan, "fatal_claude_failure", errorMessage);
761
+ if (resolvePolicy(failure.kind, getActiveResilienceConfig()).onExhausted === "block") {
762
+ plan = {
763
+ ...plan,
764
+ runState: {
765
+ currentPhase: "execute",
766
+ attempt,
767
+ owner: plan.runState?.owner ?? null,
768
+ status: "blocked",
769
+ blockedReason: errorMessage,
770
+ lastHeartbeatAt: isoNow()
771
+ }
772
+ };
773
+ }
620
774
  await saveTaskPlan(paths.prdFile, plan);
621
775
  return;
622
776
  }
@@ -691,7 +845,6 @@ async function runEngine(config, paths) {
691
845
  }
692
846
  getSessionPublisher().publish({ type: "iteration:end", at: isoNow(), iteration: i });
693
847
  publishIterationMetrics(i, result.cost, iterationSeconds);
694
- await publishGitState(getSessionPublisher());
695
848
  if (result.output.includes("<promise>COMPLETE</promise>")) {
696
849
  plan = await loadTaskPlan(paths.prdFile);
697
850
  if (allStoriesPass(plan) && !hasPendingCorrection(plan)) {
@@ -707,7 +860,7 @@ async function runEngine(config, paths) {
707
860
  void 0,
708
861
  getPhaseUsageTotals(EXECUTE_PHASE)
709
862
  );
710
- return 0;
863
+ return finishWithAcceptance(config, paths, plan);
711
864
  }
712
865
  plan = setLastError(
713
866
  plan,
@@ -723,7 +876,6 @@ async function runEngine(config, paths) {
723
876
  if (isVerbose()) {
724
877
  printSuccess(`Iteration ${i} complete. Continuing...`);
725
878
  }
726
- await sleep(2);
727
879
  }
728
880
  const elapsed = Math.floor((Date.now() - startTime) / 1e3);
729
881
  plan = await loadTaskPlan(paths.prdFile);
@@ -738,6 +890,21 @@ async function runEngine(config, paths) {
738
890
  );
739
891
  return 1;
740
892
  }
893
+ async function finishWithAcceptance(config, paths, plan) {
894
+ const issueDir = resolveIssueDir(config, paths);
895
+ const outcome = await runAcceptanceGate({
896
+ issueDir,
897
+ cwd: paths.projectRoot,
898
+ addDirs: config.issueNumber === void 0 ? void 0 : [issueDir],
899
+ skipReviewer: true
900
+ });
901
+ const applied = applyAcceptanceToPlan(plan, outcome);
902
+ if (applied.failed) {
903
+ await saveTaskPlan(paths.prdFile, applied.plan);
904
+ return 1;
905
+ }
906
+ return 0;
907
+ }
741
908
 
742
909
  // src/commands/execute.ts
743
910
  async function runExecute(positionalMaxIter, options) {
@@ -758,23 +925,73 @@ async function runExecute(positionalMaxIter, options) {
758
925
  commitScope: options.commitScope
759
926
  });
760
927
  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);
928
+ const webConfig = await loadWebConfig();
929
+ const inheritedPublisher = getSessionPublisher();
930
+ let standalonePublisher = null;
931
+ if (webConfig.enabled && config.issueNumber !== void 0 && inheritedPublisher instanceof NullPublisher) {
932
+ const issuePaths = await resolveIssuePaths(config.issueNumber, {
933
+ projectRoot: paths.projectRoot
934
+ });
935
+ standalonePublisher = new FilePublisher(issuePaths.sessionFile, {
936
+ logLimit: webConfig.logLimit,
937
+ includeLogs: webConfig.includeLogs
938
+ });
939
+ setSessionPublisher(standalonePublisher);
940
+ const numericIssue2 = /^\d+$/.test(config.issueNumber) ? Number(config.issueNumber) : null;
941
+ standalonePublisher.publish({
942
+ type: "session:start",
943
+ at: isoNow(),
944
+ sessionId: randomUUID(),
945
+ issueNumber: numericIssue2,
946
+ phases: ["execute"]
947
+ });
948
+ standalonePublisher.publish({ type: "phase:start", at: isoNow(), phase: "execute" });
949
+ await ensureWebMonitor({
950
+ publisher: standalonePublisher,
951
+ port: webConfig.port,
952
+ host: webConfig.host,
953
+ refreshSeconds: webConfig.refreshSeconds
954
+ });
955
+ }
956
+ let exitCode = null;
957
+ try {
958
+ exitCode = await runEngine(config, paths);
959
+ if (exitCode === 0 && config.issueNumber) {
960
+ try {
961
+ const plan = await loadTaskPlan(paths.prdFile);
962
+ if (allStoriesPass(plan)) {
963
+ plan.pipeline.executionCompleted = true;
964
+ await saveTaskPlan(paths.prdFile, plan);
965
+ }
966
+ } catch {
768
967
  }
769
- } catch {
968
+ }
969
+ return exitCode;
970
+ } finally {
971
+ if (standalonePublisher !== null) {
972
+ const success = exitCode === 0;
973
+ standalonePublisher.publish({
974
+ type: "phase:end",
975
+ at: isoNow(),
976
+ phase: "execute",
977
+ success,
978
+ ...success ? {} : { error: "Execute phase failed" }
979
+ });
980
+ standalonePublisher.publish({
981
+ type: "session:end",
982
+ at: isoNow(),
983
+ status: success ? "completed" : "failed"
984
+ });
985
+ await standalonePublisher.close();
986
+ setSessionPublisher(void 0);
770
987
  }
771
988
  }
772
- return exitCode;
773
989
  }
774
990
 
775
991
  export {
992
+ stripMarkdown,
776
993
  printQueueSummary,
777
994
  printRunSummary,
778
995
  runExecute
779
996
  };
780
- //# sourceMappingURL=chunk-3QHK4YQA.js.map
997
+ //# sourceMappingURL=chunk-LZ6GFQAG.js.map