issue-flow 0.11.1 → 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.
- package/README.md +79 -274
- package/dist/agent-BXRIEKVM.js +238 -0
- package/dist/agent-BXRIEKVM.js.map +1 -0
- package/dist/{analyze-XV3K25K3.js → analyze-WS47UYGN.js} +32 -15
- package/dist/analyze-WS47UYGN.js.map +1 -0
- package/dist/{apply-WUJHDD7A.js → apply-OS62UWRN.js} +15 -5
- package/dist/{apply-WUJHDD7A.js.map → apply-OS62UWRN.js.map} +1 -1
- package/dist/bench-SA3BOME5.js +906 -0
- package/dist/bench-SA3BOME5.js.map +1 -0
- package/dist/chunk-37N2RJW2.js +120 -0
- package/dist/chunk-37N2RJW2.js.map +1 -0
- package/dist/chunk-3XLCNVMD.js +180 -0
- package/dist/chunk-3XLCNVMD.js.map +1 -0
- package/dist/chunk-5TB6KLC2.js +72 -0
- package/dist/chunk-5TB6KLC2.js.map +1 -0
- package/dist/{chunk-OBLVLIUS.js → chunk-664Y3NDX.js} +25 -13
- package/dist/chunk-664Y3NDX.js.map +1 -0
- package/dist/chunk-6FDFRNXZ.js +357 -0
- package/dist/chunk-6FDFRNXZ.js.map +1 -0
- package/dist/chunk-7KD66LIM.js +658 -0
- package/dist/chunk-7KD66LIM.js.map +1 -0
- package/dist/chunk-7Z4Y3VKM.js +26 -0
- package/dist/chunk-7Z4Y3VKM.js.map +1 -0
- package/dist/chunk-ARFVHC2F.js +1111 -0
- package/dist/chunk-ARFVHC2F.js.map +1 -0
- package/dist/{chunk-RKTEV6EG.js → chunk-BHJCGWQX.js} +141 -12
- package/dist/chunk-BHJCGWQX.js.map +1 -0
- package/dist/chunk-BQDQTIM7.js +74 -0
- package/dist/chunk-BQDQTIM7.js.map +1 -0
- package/dist/chunk-CNBYCMSU.js +2709 -0
- package/dist/chunk-CNBYCMSU.js.map +1 -0
- package/dist/chunk-CRGIQFUQ.js +83 -0
- package/dist/chunk-CRGIQFUQ.js.map +1 -0
- package/dist/chunk-DIG5XKQQ.js +298 -0
- package/dist/chunk-DIG5XKQQ.js.map +1 -0
- package/dist/chunk-FZDR7CGH.js +71 -0
- package/dist/chunk-FZDR7CGH.js.map +1 -0
- package/dist/chunk-HEF2RMKN.js +413 -0
- package/dist/chunk-HEF2RMKN.js.map +1 -0
- package/dist/{chunk-IVHZEYAX.js → chunk-HJGGNLWI.js} +25 -19
- package/dist/chunk-HJGGNLWI.js.map +1 -0
- package/dist/chunk-HQAHNIAR.js +283 -0
- package/dist/chunk-HQAHNIAR.js.map +1 -0
- package/dist/{chunk-GANSDJV7.js → chunk-IHIEXUTC.js} +46 -24
- package/dist/chunk-IHIEXUTC.js.map +1 -0
- package/dist/chunk-IV5SS4JN.js +84 -0
- package/dist/chunk-IV5SS4JN.js.map +1 -0
- package/dist/{chunk-HC2MT7BX.js → chunk-IYV6KNVF.js} +158 -592
- package/dist/chunk-IYV6KNVF.js.map +1 -0
- package/dist/{chunk-LVKQ2AKT.js → chunk-J62QSVP6.js} +10 -8
- package/dist/chunk-J62QSVP6.js.map +1 -0
- package/dist/chunk-KMMASOA6.js +134 -0
- package/dist/chunk-KMMASOA6.js.map +1 -0
- package/dist/chunk-KQHB7ZT3.js +559 -0
- package/dist/chunk-KQHB7ZT3.js.map +1 -0
- package/dist/chunk-L6HSKTQE.js +77 -0
- package/dist/chunk-L6HSKTQE.js.map +1 -0
- package/dist/{chunk-6N7LZZ5U.js → chunk-LZ6GFQAG.js} +474 -160
- package/dist/chunk-LZ6GFQAG.js.map +1 -0
- package/dist/{chunk-HQYXVIH6.js → chunk-MIVHK5YI.js} +5 -5
- package/dist/chunk-NEZZ6KDR.js +1217 -0
- package/dist/chunk-NEZZ6KDR.js.map +1 -0
- package/dist/{run-DBF2VWUN.js → chunk-OPNGPUKJ.js} +747 -594
- package/dist/chunk-OPNGPUKJ.js.map +1 -0
- package/dist/chunk-P3LRUNC6.js +16 -0
- package/dist/chunk-P3LRUNC6.js.map +1 -0
- package/dist/{chunk-BXCPXRBR.js → chunk-PIVGJM6K.js} +129 -32
- package/dist/chunk-PIVGJM6K.js.map +1 -0
- package/dist/{chunk-AAY6KM7N.js → chunk-PV4OX5TC.js} +167 -196
- package/dist/chunk-PV4OX5TC.js.map +1 -0
- package/dist/chunk-QCXPNSEL.js +141 -0
- package/dist/chunk-QCXPNSEL.js.map +1 -0
- package/dist/{chunk-34WNTK3K.js → chunk-QXDNBYQF.js} +53 -13
- package/dist/chunk-QXDNBYQF.js.map +1 -0
- package/dist/chunk-SNTTOQAD.js +30 -0
- package/dist/chunk-SNTTOQAD.js.map +1 -0
- package/dist/{chunk-EY6WMHV2.js → chunk-TM67EHSV.js} +67 -23
- package/dist/chunk-TM67EHSV.js.map +1 -0
- package/dist/chunk-TVN2JVOF.js +77 -0
- package/dist/chunk-TVN2JVOF.js.map +1 -0
- package/dist/chunk-TX5WIVYP.js +129 -0
- package/dist/chunk-TX5WIVYP.js.map +1 -0
- package/dist/{chunk-KQ4JQN7T.js → chunk-UJ7XWK7J.js} +83 -21
- package/dist/chunk-UJ7XWK7J.js.map +1 -0
- package/dist/{chunk-47VIN33V.js → chunk-UOCPC2Y2.js} +596 -17
- package/dist/chunk-UOCPC2Y2.js.map +1 -0
- package/dist/chunk-XEYH6ZZI.js +289 -0
- package/dist/chunk-XEYH6ZZI.js.map +1 -0
- package/dist/cli.js +390 -40
- package/dist/cli.js.map +1 -1
- package/dist/config-MEM2IWWN.js +76 -0
- package/dist/contract-JH2WBIO6.js +8 -0
- package/dist/conventions-WV4NDXGH.js +143 -0
- package/dist/conventions-WV4NDXGH.js.map +1 -0
- package/dist/execute-NITIHHCK.js +31 -0
- package/dist/{generate-TBVTLMOP.js → generate-MVQI7A45.js} +39 -19
- package/dist/{generate-TBVTLMOP.js.map → generate-MVQI7A45.js.map} +1 -1
- package/dist/git-YRVRUN3C.js +31 -0
- package/dist/init-OKWEC3EV.js +21 -0
- package/dist/operations-LU6JA3BX.js +374 -0
- package/dist/operations-LU6JA3BX.js.map +1 -0
- package/dist/permissions-7NMDFU27.js +18 -0
- package/dist/plan-LEJJF3DE.js +31 -0
- package/dist/{policy-XQW5NUIE.js → policy-JSYEK5B2.js} +17 -4
- package/dist/policy-JSYEK5B2.js.map +1 -0
- package/dist/pr-HPVUZ7S3.js +36 -0
- package/dist/pr-review-R2QZBT3F.js +29 -0
- package/dist/pr-review-R2QZBT3F.js.map +1 -0
- package/dist/prd-5O6GYCC6.js +30 -0
- package/dist/prd-5O6GYCC6.js.map +1 -0
- package/dist/ps-TNE44R5V.js +90 -0
- package/dist/ps-TNE44R5V.js.map +1 -0
- package/dist/recorder-XGF55WXI.js +31 -0
- package/dist/recorder-XGF55WXI.js.map +1 -0
- package/dist/registry-LGPE53QD.js +15 -0
- package/dist/registry-LGPE53QD.js.map +1 -0
- package/dist/resume-QMEPMSZW.js +238 -0
- package/dist/resume-QMEPMSZW.js.map +1 -0
- package/dist/review-YKWD6SNN.js +32 -0
- package/dist/review-YKWD6SNN.js.map +1 -0
- package/dist/routing-2ZUMZPJB.js +91 -0
- package/dist/routing-2ZUMZPJB.js.map +1 -0
- package/dist/run-YU4TR62R.js +50 -0
- package/dist/run-YU4TR62R.js.map +1 -0
- package/dist/state-manager-X3PT3FGT.js +36 -0
- package/dist/state-manager-X3PT3FGT.js.map +1 -0
- package/dist/usage-VCFLDA5Y.js +174 -0
- package/dist/usage-VCFLDA5Y.js.map +1 -0
- package/dist/{web-ANQQTTRD.js → web-XVHLX62B.js} +43 -27
- package/dist/web-XVHLX62B.js.map +1 -0
- package/package.json +6 -2
- package/prompts/execute.md +7 -8
- package/prompts/plan.md +4 -5
- package/prompts/pr.md +13 -6
- package/web/AGENTS.md +3 -3
- package/web/public/app.css +38 -0
- package/web/public/app.js +132 -10
- package/web/public/index.html +24 -0
- package/dist/analyze-XV3K25K3.js.map +0 -1
- package/dist/chunk-34WNTK3K.js.map +0 -1
- package/dist/chunk-3BOHTH4V.js +0 -58
- package/dist/chunk-3BOHTH4V.js.map +0 -1
- package/dist/chunk-47VIN33V.js.map +0 -1
- package/dist/chunk-6N7LZZ5U.js.map +0 -1
- package/dist/chunk-AAY6KM7N.js.map +0 -1
- package/dist/chunk-BXCPXRBR.js.map +0 -1
- package/dist/chunk-EWDJENFW.js +0 -303
- package/dist/chunk-EWDJENFW.js.map +0 -1
- package/dist/chunk-EY6WMHV2.js.map +0 -1
- package/dist/chunk-GANSDJV7.js.map +0 -1
- package/dist/chunk-HC2MT7BX.js.map +0 -1
- package/dist/chunk-HDV3YDTG.js +0 -52
- package/dist/chunk-HDV3YDTG.js.map +0 -1
- package/dist/chunk-IVHZEYAX.js.map +0 -1
- package/dist/chunk-KQ4JQN7T.js.map +0 -1
- package/dist/chunk-LVKQ2AKT.js.map +0 -1
- package/dist/chunk-OBLVLIUS.js.map +0 -1
- package/dist/chunk-RKTEV6EG.js.map +0 -1
- package/dist/chunk-VQ5VN7DF.js +0 -1018
- package/dist/chunk-VQ5VN7DF.js.map +0 -1
- package/dist/config-4THHOXNN.js +0 -41
- package/dist/execute-5OEKLM5X.js +0 -15
- package/dist/init-PRMOTQ7N.js +0 -9
- package/dist/plan-Y2BYZD24.js +0 -17
- package/dist/policy-XQW5NUIE.js.map +0 -1
- package/dist/pr-H6GLOSXF.js +0 -21
- package/dist/pr-review-CK5FR6WX.js +0 -17
- package/dist/prd-4PWLCGH5.js +0 -17
- package/dist/review-QGVAHEZF.js +0 -15
- package/dist/run-DBF2VWUN.js.map +0 -1
- package/dist/web-ANQQTTRD.js.map +0 -1
- /package/dist/{chunk-HQYXVIH6.js.map → chunk-MIVHK5YI.js.map} +0 -0
- /package/dist/{config-4THHOXNN.js.map → config-MEM2IWWN.js.map} +0 -0
- /package/dist/{execute-5OEKLM5X.js.map → contract-JH2WBIO6.js.map} +0 -0
- /package/dist/{init-PRMOTQ7N.js.map → execute-NITIHHCK.js.map} +0 -0
- /package/dist/{plan-Y2BYZD24.js.map → git-YRVRUN3C.js.map} +0 -0
- /package/dist/{pr-H6GLOSXF.js.map → init-OKWEC3EV.js.map} +0 -0
- /package/dist/{pr-review-CK5FR6WX.js.map → permissions-7NMDFU27.js.map} +0 -0
- /package/dist/{prd-4PWLCGH5.js.map → plan-LEJJF3DE.js.map} +0 -0
- /package/dist/{review-QGVAHEZF.js.map → pr-HPVUZ7S3.js.map} +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
ensureWebMonitor
|
|
4
|
+
} from "./chunk-IHIEXUTC.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
applyAcceptanceToPlan,
|
|
7
|
+
resolveIssueDir,
|
|
8
|
+
runAcceptanceGate
|
|
9
|
+
} from "./chunk-BQDQTIM7.js";
|
|
10
|
+
import {
|
|
11
|
+
groupBy,
|
|
12
|
+
summarize
|
|
13
|
+
} from "./chunk-CRGIQFUQ.js";
|
|
10
14
|
import {
|
|
11
15
|
EXECUTE_PHASE,
|
|
12
16
|
elapsedSecondsSince,
|
|
@@ -14,49 +18,83 @@ import {
|
|
|
14
18
|
publishIterationMetrics,
|
|
15
19
|
publishStoryMetrics,
|
|
16
20
|
resolvePolicyPlaceholders
|
|
17
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-664Y3NDX.js";
|
|
18
22
|
import {
|
|
19
23
|
applyPlaceholders,
|
|
20
24
|
loadPrompt
|
|
21
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-MIVHK5YI.js";
|
|
26
|
+
import {
|
|
27
|
+
invokeSelectedAgent
|
|
28
|
+
} from "./chunk-ARFVHC2F.js";
|
|
29
|
+
import {
|
|
30
|
+
archiveFolderName,
|
|
31
|
+
commitMessage
|
|
32
|
+
} from "./chunk-UOCPC2Y2.js";
|
|
22
33
|
import {
|
|
23
34
|
createConfig,
|
|
35
|
+
getActiveResilienceConfig,
|
|
36
|
+
getShutdownSignal,
|
|
37
|
+
loadGlobalConfig,
|
|
38
|
+
loadWebConfig,
|
|
39
|
+
resolveAgentFor,
|
|
24
40
|
resolvePaths,
|
|
25
41
|
validateDependencies
|
|
26
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-CNBYCMSU.js";
|
|
27
43
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
resolveIssuePaths
|
|
45
|
+
} from "./chunk-HQAHNIAR.js";
|
|
46
|
+
import {
|
|
47
|
+
FilePublisher,
|
|
48
|
+
NullPublisher,
|
|
32
49
|
formatDuration,
|
|
33
|
-
formatTokens,
|
|
34
50
|
getIcons,
|
|
51
|
+
getInactivityTimeout,
|
|
35
52
|
getOutputCallback,
|
|
36
53
|
getSessionPublisher,
|
|
37
54
|
getStoryStageCallback,
|
|
38
55
|
getStoryUpdateCallback,
|
|
39
56
|
getTermWidth,
|
|
40
|
-
hasPendingCorrection,
|
|
41
|
-
initializeState,
|
|
42
57
|
isVerbose,
|
|
43
|
-
isoNow,
|
|
44
|
-
loadTaskPlan,
|
|
45
|
-
markIssueCompleted,
|
|
46
|
-
markIssueInProgress,
|
|
47
|
-
parseUsage,
|
|
48
58
|
printError,
|
|
49
59
|
printInfo,
|
|
50
60
|
printRetry,
|
|
51
61
|
printSuccess,
|
|
52
62
|
printWarning,
|
|
63
|
+
setSessionPublisher,
|
|
64
|
+
useColor,
|
|
65
|
+
useUnicode
|
|
66
|
+
} from "./chunk-IYV6KNVF.js";
|
|
67
|
+
import {
|
|
68
|
+
allStoriesPass,
|
|
69
|
+
applyStoryMetrics,
|
|
70
|
+
clearLastError,
|
|
71
|
+
divideUsage,
|
|
72
|
+
formatTokens,
|
|
73
|
+
hasPendingCorrection,
|
|
74
|
+
initializeState,
|
|
75
|
+
isoNow,
|
|
76
|
+
loadTaskPlan,
|
|
77
|
+
markIssueCompleted,
|
|
78
|
+
markIssueInProgress,
|
|
53
79
|
saveTaskPlan,
|
|
54
80
|
selectActiveStory,
|
|
55
81
|
setLastError,
|
|
56
|
-
trimErrorMessage
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
82
|
+
trimErrorMessage
|
|
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";
|
|
60
98
|
|
|
61
99
|
// src/core/engine.ts
|
|
62
100
|
import { existsSync } from "fs";
|
|
@@ -173,7 +211,7 @@ function printBox(lines) {
|
|
|
173
211
|
}
|
|
174
212
|
console.log(blue(`${bl}${hrule}${br}`));
|
|
175
213
|
}
|
|
176
|
-
function printStartupHeader(config, plan) {
|
|
214
|
+
function printStartupHeader(config, plan, agent) {
|
|
177
215
|
const icons = getIcons();
|
|
178
216
|
const storiesTotal = plan.userStories.length;
|
|
179
217
|
const storiesPassing = plan.userStories.filter((s) => s.passes).length;
|
|
@@ -181,7 +219,7 @@ function printStartupHeader(config, plan) {
|
|
|
181
219
|
const issueLabel = config.issueNumber ? `Issue #${config.issueNumber}` : "Standalone mode";
|
|
182
220
|
const maxIterLabel = config.maxIterations !== void 0 ? String(config.maxIterations) : "unlimited";
|
|
183
221
|
const retryLabel = config.retryForever ? "unlimited retries" : `${config.retryLimit} consecutive retries`;
|
|
184
|
-
|
|
222
|
+
const lines = [
|
|
185
223
|
`${icons.start} Issue Flow`,
|
|
186
224
|
"---",
|
|
187
225
|
`Issue: ${issueLabel}`,
|
|
@@ -189,9 +227,22 @@ function printStartupHeader(config, plan) {
|
|
|
189
227
|
`Stories: ${storiesPassing}/${storiesTotal} passing`,
|
|
190
228
|
`Iterations: ${maxIterLabel}`,
|
|
191
229
|
`Retries: ${retryLabel}`
|
|
192
|
-
]
|
|
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);
|
|
193
244
|
}
|
|
194
|
-
function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan, extraInfo, usage) {
|
|
245
|
+
function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan, extraInfo, usage, usageByAgent) {
|
|
195
246
|
const icons = getIcons();
|
|
196
247
|
const storiesTotal = plan.userStories.length;
|
|
197
248
|
const storiesPassing = plan.userStories.filter((s) => s.passes).length;
|
|
@@ -220,10 +271,8 @@ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan,
|
|
|
220
271
|
`Iterations: ${iterations}`,
|
|
221
272
|
`Duration: ${duration}`
|
|
222
273
|
];
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
boxLines.push(`Tokens: ${tokens}`);
|
|
226
|
-
}
|
|
274
|
+
boxLines.push(...tokenBoxLines(usage, usageByAgent));
|
|
275
|
+
boxLines.push(...executionCostLines(plan));
|
|
227
276
|
boxLines.push(`Retries: ${totalRetries}`);
|
|
228
277
|
if (extraInfo) {
|
|
229
278
|
boxLines.push("---");
|
|
@@ -232,19 +281,79 @@ function printSummaryBox(status, iterations, totalRetries, elapsedSeconds, plan,
|
|
|
232
281
|
console.log("");
|
|
233
282
|
printBox(boxLines);
|
|
234
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
|
+
}
|
|
235
343
|
function buildRunSummaryLines(info) {
|
|
236
344
|
const lines = [
|
|
237
345
|
` Branch: ${info.branchName}${info.noBranch ? " (current)" : ""}`,
|
|
238
346
|
` Stories: ${info.storyCount}`,
|
|
239
347
|
` Duration: ${formatDuration(info.elapsedSeconds)}`
|
|
240
348
|
];
|
|
241
|
-
|
|
242
|
-
if (tokens !== "") {
|
|
243
|
-
lines.push(` Tokens: ${tokens}`);
|
|
244
|
-
}
|
|
349
|
+
lines.push(...tokenSummaryLines(info.usage, info.usageByAgent));
|
|
245
350
|
if (!info.noBranch) {
|
|
246
351
|
lines.push(` PR: ${info.prUrl}`);
|
|
247
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
|
+
}
|
|
248
357
|
const review = info.prReview;
|
|
249
358
|
if (review) {
|
|
250
359
|
const verdict = review.recommendation ?? (review.requestedChanges ? "REQUEST_CHANGES" : "unknown");
|
|
@@ -305,6 +414,14 @@ function printRunSummary(info) {
|
|
|
305
414
|
printWarning(
|
|
306
415
|
`Pipeline finished for issue #${info.issueNumber}, but the PR review requested changes.`
|
|
307
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
|
+
);
|
|
308
425
|
} else {
|
|
309
426
|
printSuccess(`Pipeline complete for issue #${info.issueNumber}!`);
|
|
310
427
|
}
|
|
@@ -313,48 +430,65 @@ function printRunSummary(info) {
|
|
|
313
430
|
}
|
|
314
431
|
}
|
|
315
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
|
+
|
|
316
441
|
// src/core/executor.ts
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
input: prompt,
|
|
324
|
-
reject: false,
|
|
442
|
+
async function executeClaude(prompt, options = {}) {
|
|
443
|
+
let selected;
|
|
444
|
+
try {
|
|
445
|
+
selected = await invokeSelectedAgent({
|
|
446
|
+
prompt,
|
|
447
|
+
phase: "execute",
|
|
325
448
|
timeout: 0,
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
const onOutput = getOutputCallback();
|
|
346
|
-
if (onOutput) {
|
|
347
|
-
const trimmed = output.trim();
|
|
348
|
-
if (trimmed) {
|
|
349
|
-
onOutput(trimmed);
|
|
350
|
-
}
|
|
449
|
+
permission: "autonomous",
|
|
450
|
+
inactivityTimeoutMs: options.inactivityTimeoutMs
|
|
451
|
+
});
|
|
452
|
+
} catch (err) {
|
|
453
|
+
return {
|
|
454
|
+
exitCode: 1,
|
|
455
|
+
output: err instanceof Error ? err.message : String(err),
|
|
456
|
+
cost: null
|
|
457
|
+
};
|
|
458
|
+
}
|
|
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;
|
|
351
467
|
}
|
|
468
|
+
emitAgentOutput(output, !(run.success && run.exitCode === 0));
|
|
352
469
|
return {
|
|
353
|
-
exitCode,
|
|
470
|
+
exitCode: run.exitCode === 0 && run.success ? 0 : run.exitCode === 0 ? 1 : run.exitCode,
|
|
354
471
|
output,
|
|
355
472
|
cost
|
|
356
473
|
};
|
|
357
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
|
+
}
|
|
358
492
|
|
|
359
493
|
// src/core/engine.ts
|
|
360
494
|
function emitLog(message) {
|
|
@@ -367,18 +501,88 @@ function emitLog(message) {
|
|
|
367
501
|
console.log(message);
|
|
368
502
|
}
|
|
369
503
|
}
|
|
504
|
+
function executeRetryPolicy(config) {
|
|
505
|
+
return fixedBackoffPolicy(
|
|
506
|
+
config.retryLimit + 1,
|
|
507
|
+
config.backoffBaseSeconds,
|
|
508
|
+
config.backoffMaxSeconds,
|
|
509
|
+
{ retryForever: config.retryForever }
|
|
510
|
+
);
|
|
511
|
+
}
|
|
370
512
|
function newlyCompletedStoryIds(before, after) {
|
|
371
513
|
const wasPassing = new Map(before.map((story) => [story.id, story.passes]));
|
|
372
514
|
return after.filter((story) => story.passes && wasPassing.get(story.id) === false).map((story) => story.id);
|
|
373
515
|
}
|
|
374
|
-
function
|
|
375
|
-
|
|
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) {
|
|
376
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
|
+
}
|
|
377
545
|
return {
|
|
378
|
-
__COMMIT_MESSAGE__:
|
|
379
|
-
|
|
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
|
+
})
|
|
380
560
|
};
|
|
381
561
|
}
|
|
562
|
+
function inactivityOptions() {
|
|
563
|
+
const configured = getInactivityTimeout();
|
|
564
|
+
return configured === void 0 ? {} : { inactivityTimeoutMs: configured };
|
|
565
|
+
}
|
|
566
|
+
async function adoptCommittedStories(tasksPath) {
|
|
567
|
+
try {
|
|
568
|
+
const plan = await loadTaskPlan(tasksPath);
|
|
569
|
+
const pending = plan.userStories.filter((story) => !story.passes);
|
|
570
|
+
if (pending.length === 0) return [];
|
|
571
|
+
if (!await isWorkingTreeClean()) return [];
|
|
572
|
+
const committed = await committedStoryIds(await getBaseBranch());
|
|
573
|
+
const adopted = pending.filter((story) => committed.has(story.id)).map((story) => story.id);
|
|
574
|
+
if (adopted.length === 0) return [];
|
|
575
|
+
await saveTaskPlan(tasksPath, {
|
|
576
|
+
...plan,
|
|
577
|
+
userStories: plan.userStories.map(
|
|
578
|
+
(story) => adopted.includes(story.id) ? { ...story, passes: true } : story
|
|
579
|
+
)
|
|
580
|
+
});
|
|
581
|
+
return adopted;
|
|
582
|
+
} catch {
|
|
583
|
+
return [];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
382
586
|
async function ensureProgressFile(progressFile) {
|
|
383
587
|
if (!existsSync(progressFile)) {
|
|
384
588
|
const content = `# Issue Flow Progress Log
|
|
@@ -402,7 +606,7 @@ async function archiveIfBranchChanged(plan, paths) {
|
|
|
402
606
|
}
|
|
403
607
|
if (currentBranch && lastBranch && currentBranch !== lastBranch) {
|
|
404
608
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
405
|
-
const folderName = lastBranch
|
|
609
|
+
const folderName = archiveFolderName(lastBranch);
|
|
406
610
|
const archiveFolder = join(archiveDir, `${dateStr}-${folderName}`);
|
|
407
611
|
printInfo(`Archiving previous run: ${lastBranch}`);
|
|
408
612
|
await mkdir(archiveFolder, { recursive: true });
|
|
@@ -461,111 +665,158 @@ async function runEngine(config, paths) {
|
|
|
461
665
|
emitLog("All user stories already pass. Marking issue as completed.");
|
|
462
666
|
plan = markIssueCompleted(plan);
|
|
463
667
|
await saveTaskPlan(paths.prdFile, plan);
|
|
464
|
-
return
|
|
668
|
+
return finishWithAcceptance(config, paths, plan);
|
|
465
669
|
}
|
|
466
670
|
await archiveIfBranchChanged(plan, paths);
|
|
467
671
|
await trackBranch(plan, paths.lastBranchFile);
|
|
468
672
|
await ensureProgressFile(paths.progressFile);
|
|
469
673
|
const promptTemplate = await loadPrompt("execute", { projectRoot: paths.projectRoot });
|
|
470
674
|
const policy = await resolvePolicyPlaceholders({ root: paths.projectRoot });
|
|
471
|
-
|
|
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
|
+
});
|
|
472
682
|
const startTime = Date.now();
|
|
473
683
|
let i = 0;
|
|
474
|
-
let retryCount = 0;
|
|
475
684
|
let totalRetryCount = 0;
|
|
476
685
|
while (true) {
|
|
477
686
|
if (config.maxIterations !== void 0 && i >= config.maxIterations) {
|
|
478
687
|
break;
|
|
479
688
|
}
|
|
480
689
|
i++;
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
getStoryStageCallback()?.(activeStoryId);
|
|
491
|
-
if (isVerbose()) {
|
|
492
|
-
printIterationHeader(i, config.maxIterations, plan.userStories, activeStoryId);
|
|
690
|
+
const adopted = await adoptCommittedStories(paths.prdFile);
|
|
691
|
+
if (adopted.length > 0) {
|
|
692
|
+
printInfo(
|
|
693
|
+
`Adopting ${adopted.join(", ")}: already committed on this branch with a clean tree.`
|
|
694
|
+
);
|
|
695
|
+
plan = await loadTaskPlan(paths.prdFile);
|
|
696
|
+
if (plan.userStories.every((story) => story.passes) && plan.lastReviewFindings === null) {
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
493
699
|
}
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
__PROGRESS_FILE__: paths.progressFile,
|
|
497
|
-
...policy,
|
|
498
|
-
// The convention comes off the projection above rather than from a second
|
|
499
|
-
// discovery: one resolution per run is the whole point of the cache.
|
|
500
|
-
...commitPlaceholders(config.commitScope, policy.__COMMIT_CONVENTION__)
|
|
501
|
-
});
|
|
502
|
-
const iterationStartedAt = isoNow();
|
|
503
|
-
plan = markIssueInProgress(plan, iterationStartedAt);
|
|
504
|
-
await saveTaskPlan(paths.prdFile, plan);
|
|
505
|
-
const iterationStartedAtMs = Date.now();
|
|
506
|
-
const result = await executeClaude(prompt);
|
|
507
|
-
const iterationSeconds = elapsedSecondsSince(iterationStartedAtMs);
|
|
508
|
-
if (result.exitCode !== 0) {
|
|
509
|
-
publishIterationMetrics(i, result.cost, iterationSeconds);
|
|
510
|
-
const errorMessage = trimErrorMessage(result.output);
|
|
511
|
-
if (isTransientFailure(result.exitCode, result.output)) {
|
|
512
|
-
retryCount++;
|
|
513
|
-
totalRetryCount++;
|
|
700
|
+
const attempted = await withRetry(
|
|
701
|
+
async () => {
|
|
514
702
|
plan = await loadTaskPlan(paths.prdFile);
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
"failed",
|
|
522
|
-
i,
|
|
523
|
-
totalRetryCount,
|
|
524
|
-
elapsed3,
|
|
525
|
-
plan,
|
|
526
|
-
`Exceeded retry limit (${config.retryLimit}) on transient errors`,
|
|
527
|
-
getPhaseUsageTotals(EXECUTE_PHASE)
|
|
528
|
-
);
|
|
529
|
-
return result.exitCode;
|
|
703
|
+
if (plan.userStories.length > 0) {
|
|
704
|
+
getSessionPublisher().publish({
|
|
705
|
+
type: "stories:update",
|
|
706
|
+
at: isoNow(),
|
|
707
|
+
stories: plan.userStories
|
|
708
|
+
});
|
|
530
709
|
}
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
config.backoffBaseSeconds,
|
|
534
|
-
config.backoffMaxSeconds
|
|
535
|
-
);
|
|
710
|
+
const storiesBefore = plan.userStories;
|
|
711
|
+
const activeStoryId = selectActiveStory(plan.userStories)?.id;
|
|
536
712
|
getSessionPublisher().publish({
|
|
537
|
-
type: "
|
|
713
|
+
type: "iteration:start",
|
|
538
714
|
at: isoNow(),
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
reason: "transient_claude_failure"
|
|
715
|
+
iteration: i,
|
|
716
|
+
storyId: activeStoryId
|
|
542
717
|
});
|
|
718
|
+
getStoryStageCallback()?.(activeStoryId);
|
|
543
719
|
if (isVerbose()) {
|
|
544
|
-
|
|
720
|
+
printIterationHeader(i, config.maxIterations, plan.userStories, activeStoryId);
|
|
721
|
+
}
|
|
722
|
+
const prompt = applyPlaceholders(promptTemplate, {
|
|
723
|
+
__PRD_FILE__: paths.prdFile,
|
|
724
|
+
__PROGRESS_FILE__: paths.progressFile,
|
|
725
|
+
...policy,
|
|
726
|
+
// The convention comes off the projection above rather than from a second
|
|
727
|
+
// discovery: one resolution per run is the whole point of the cache.
|
|
728
|
+
...commitPlaceholders(config.commitScope, policy.__COMMIT_CONVENTION__, {
|
|
729
|
+
issueNumber: plan.issueNumber,
|
|
730
|
+
signoff
|
|
731
|
+
}),
|
|
732
|
+
__STORIES_PER_ITERATION__: String(config.storiesPerIteration ?? 1)
|
|
733
|
+
});
|
|
734
|
+
const startedAt = isoNow();
|
|
735
|
+
plan = markIssueInProgress(plan, startedAt);
|
|
736
|
+
await saveTaskPlan(paths.prdFile, plan);
|
|
737
|
+
const startedAtMs = Date.now();
|
|
738
|
+
const result2 = await executeClaude(prompt, inactivityOptions());
|
|
739
|
+
const seconds = elapsedSecondsSince(startedAtMs);
|
|
740
|
+
if (result2.exitCode !== 0) {
|
|
741
|
+
publishIterationMetrics(i, result2.cost, seconds);
|
|
742
|
+
}
|
|
743
|
+
return { result: result2, startedAt, seconds, storiesBefore };
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
policy: executeRetryPolicy(config),
|
|
747
|
+
// A `Ctrl+C` during a fifteen-minute backoff must stop in that instant,
|
|
748
|
+
// not fifteen minutes later. `abortableDelay` resolves `false` on the
|
|
749
|
+
// abort, which `withRetry` reports as `aborted` rather than a failure.
|
|
750
|
+
signal: getShutdownSignal(),
|
|
751
|
+
// The exit code and the output are all the evidence the CLI leaves
|
|
752
|
+
// behind, which is exactly what `isTransientFailure()` used to be given
|
|
753
|
+
// here — `classify()` is the same verdict plus the kind.
|
|
754
|
+
evaluate: ({ result: result2 }) => result2.exitCode === 0 ? null : classify({ source: "agent", exitCode: result2.exitCode, stdout: result2.output }),
|
|
755
|
+
onAttempt: async ({ value, attempt, failure, willRetry, delayMs }) => {
|
|
756
|
+
if (failure === null) return;
|
|
757
|
+
const errorMessage = trimErrorMessage(value.result.output);
|
|
758
|
+
if (!failure.retryable) {
|
|
759
|
+
plan = await loadTaskPlan(paths.prdFile);
|
|
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
|
+
}
|
|
774
|
+
await saveTaskPlan(paths.prdFile, plan);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
totalRetryCount++;
|
|
778
|
+
plan = await loadTaskPlan(paths.prdFile);
|
|
779
|
+
plan = setLastError(plan, "transient_claude_failure", errorMessage);
|
|
780
|
+
await saveTaskPlan(paths.prdFile, plan);
|
|
781
|
+
if (!willRetry) return;
|
|
782
|
+
const delaySeconds = delayMs / 1e3;
|
|
783
|
+
getSessionPublisher().publish({
|
|
784
|
+
type: "retry",
|
|
785
|
+
at: isoNow(),
|
|
786
|
+
attempt,
|
|
787
|
+
delaySeconds,
|
|
788
|
+
reason: "transient_claude_failure",
|
|
789
|
+
kind: failure.kind
|
|
790
|
+
});
|
|
791
|
+
if (isVerbose()) {
|
|
792
|
+
emitLog("");
|
|
793
|
+
}
|
|
794
|
+
printRetry(
|
|
795
|
+
`Transient Claude failure on iteration ${i} (attempt ${attempt}). Retrying in ${delaySeconds}s.`
|
|
796
|
+
);
|
|
545
797
|
}
|
|
546
|
-
printRetry(
|
|
547
|
-
`Transient Claude failure on iteration ${i} (attempt ${retryCount}). Retrying in ${delaySeconds}s.`
|
|
548
|
-
);
|
|
549
|
-
i--;
|
|
550
|
-
await sleep(delaySeconds);
|
|
551
|
-
continue;
|
|
552
798
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
799
|
+
);
|
|
800
|
+
const {
|
|
801
|
+
result,
|
|
802
|
+
seconds: iterationSeconds,
|
|
803
|
+
storiesBefore: storiesBeforeIteration
|
|
804
|
+
} = attempted.value;
|
|
805
|
+
const iterationStartedAt = attempted.value.startedAt;
|
|
806
|
+
if (result.exitCode !== 0) {
|
|
556
807
|
const elapsed2 = Math.floor((Date.now() - startTime) / 1e3);
|
|
808
|
+
plan = await loadTaskPlan(paths.prdFile);
|
|
557
809
|
printSummaryBox(
|
|
558
810
|
"failed",
|
|
559
811
|
i,
|
|
560
812
|
totalRetryCount,
|
|
561
813
|
elapsed2,
|
|
562
814
|
plan,
|
|
563
|
-
`Claude CLI failed with exit code ${result.exitCode}`,
|
|
815
|
+
attempted.exhausted ? `Exceeded retry limit (${config.retryLimit}) on transient errors` : `Claude CLI failed with exit code ${result.exitCode}`,
|
|
564
816
|
getPhaseUsageTotals(EXECUTE_PHASE)
|
|
565
817
|
);
|
|
566
818
|
return result.exitCode;
|
|
567
819
|
}
|
|
568
|
-
retryCount = 0;
|
|
569
820
|
plan = await loadTaskPlan(paths.prdFile);
|
|
570
821
|
plan = clearLastError(plan, iterationStartedAt);
|
|
571
822
|
await saveTaskPlan(paths.prdFile, plan);
|
|
@@ -594,7 +845,6 @@ async function runEngine(config, paths) {
|
|
|
594
845
|
}
|
|
595
846
|
getSessionPublisher().publish({ type: "iteration:end", at: isoNow(), iteration: i });
|
|
596
847
|
publishIterationMetrics(i, result.cost, iterationSeconds);
|
|
597
|
-
await publishGitState(getSessionPublisher());
|
|
598
848
|
if (result.output.includes("<promise>COMPLETE</promise>")) {
|
|
599
849
|
plan = await loadTaskPlan(paths.prdFile);
|
|
600
850
|
if (allStoriesPass(plan) && !hasPendingCorrection(plan)) {
|
|
@@ -610,7 +860,7 @@ async function runEngine(config, paths) {
|
|
|
610
860
|
void 0,
|
|
611
861
|
getPhaseUsageTotals(EXECUTE_PHASE)
|
|
612
862
|
);
|
|
613
|
-
return
|
|
863
|
+
return finishWithAcceptance(config, paths, plan);
|
|
614
864
|
}
|
|
615
865
|
plan = setLastError(
|
|
616
866
|
plan,
|
|
@@ -626,7 +876,6 @@ async function runEngine(config, paths) {
|
|
|
626
876
|
if (isVerbose()) {
|
|
627
877
|
printSuccess(`Iteration ${i} complete. Continuing...`);
|
|
628
878
|
}
|
|
629
|
-
await sleep(2);
|
|
630
879
|
}
|
|
631
880
|
const elapsed = Math.floor((Date.now() - startTime) / 1e3);
|
|
632
881
|
plan = await loadTaskPlan(paths.prdFile);
|
|
@@ -641,6 +890,21 @@ async function runEngine(config, paths) {
|
|
|
641
890
|
);
|
|
642
891
|
return 1;
|
|
643
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
|
+
}
|
|
644
908
|
|
|
645
909
|
// src/commands/execute.ts
|
|
646
910
|
async function runExecute(positionalMaxIter, options) {
|
|
@@ -661,23 +925,73 @@ async function runExecute(positionalMaxIter, options) {
|
|
|
661
925
|
commitScope: options.commitScope
|
|
662
926
|
});
|
|
663
927
|
const paths = await resolvePaths(config);
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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 {
|
|
671
967
|
}
|
|
672
|
-
}
|
|
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);
|
|
673
987
|
}
|
|
674
988
|
}
|
|
675
|
-
return exitCode;
|
|
676
989
|
}
|
|
677
990
|
|
|
678
991
|
export {
|
|
992
|
+
stripMarkdown,
|
|
679
993
|
printQueueSummary,
|
|
680
994
|
printRunSummary,
|
|
681
995
|
runExecute
|
|
682
996
|
};
|
|
683
|
-
//# sourceMappingURL=chunk-
|
|
997
|
+
//# sourceMappingURL=chunk-LZ6GFQAG.js.map
|