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 +0,0 @@
1
- {"version":3,"sources":["../src/commands/run.ts","../src/core/decompose.ts","../src/execution/confirm.ts","../src/execution/discovery.ts","../src/execution/queue.ts","../src/issues/args.ts","../src/ui/pipeline-renderer.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { execa } from 'execa';\nimport {\n getActiveResilienceConfig,\n initResilienceConfig,\n loadIssuesConfig,\n loadWebConfig,\n} from '../config.js';\nimport {\n assessDecomposition,\n buildDecompositionReport,\n proposeSubIssues,\n} from '../core/decompose.js';\nimport { JournalPublisher, MultiPublisher } from '../core/journal.js';\nimport {\n PIPELINE_PHASES,\n PIPELINE_PHASES_NO_BRANCH,\n PIPELINE_PHASES_WITH_PR_REVIEW,\n PipelineManager,\n type PipelinePhase,\n} from '../core/pipeline.js';\nimport { mostRecent } from '../core/pr-review/discovery.js';\nimport {\n type PrReviewRoundEntry,\n prReviewDir,\n readPrReviewIndex,\n} from '../core/pr-review/report.js';\nimport { listPullRequests, publishGitState } from '../core/session-git.js';\nimport { beginUsageScope, getRunUsageTotals } from '../core/session-metrics.js';\nimport { setSessionPublisher } from '../core/session-publisher.js';\nimport { FilePublisher, NullPublisher, type SessionPublisher } from '../core/session-state.js';\nimport { onShutdown } from '../core/shutdown.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getInactivityTimeout, isVerbose, setInactivityTimeout } from '../core/verbose.js';\nimport {\n markQueueIssueBlocked,\n markQueueIssueCompleted,\n markQueueIssueFailed,\n markQueueIssueInProgress,\n markQueueIssueSkipped,\n nextQueueIssue,\n saveExecutionPlan,\n setQueueBranch,\n setQueuePullRequest,\n} from '../execution/plan.js';\nimport { planQueue, type QueueDecision } from '../execution/queue.js';\nimport type { ExecutionPlan } from '../execution/types.js';\nimport { parseIssueArguments } from '../issues/args.js';\nimport { resolveCommandIssue } from '../issues/context.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueSource, ResolvedIssue } from '../issues/types.js';\nimport { acquireRunLock, describeRunLockOwner } from '../storage/lock.js';\nimport type { IssuePaths } from '../storage/paths.js';\nimport { resolveIssuePaths, resolveProjectPaths, resolveQueuePaths } from '../storage/resolve.js';\nimport type { RunLock } from '../storage/schemas.js';\nimport type { PullRequestRef, TaskPlan, UserStory } from '../types.js';\nimport { printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\nimport { runPipelineWithRenderer } from '../ui/pipeline-renderer.js';\nimport {\n printQueueSummary,\n printRunSummary,\n type QueueIssueSummary,\n type RunSummaryPrReview,\n} from '../ui/summary.js';\nimport {\n committedStoryIds,\n describePreflight,\n getBaseBranch,\n preflightRepository,\n} from '../utils/git.js';\nimport { run } from '../utils/shell.js';\nimport { ensureWebMonitor } from '../web/lock.js';\nimport { runExecute } from './execute.js';\nimport { runInit } from './init.js';\nimport { runPlan } from './plan.js';\nimport { type PrQueueContext, runPr } from './pr.js';\nimport { runPrReview } from './pr-review.js';\nimport { runPrd } from './prd.js';\nimport { runReview } from './review.js';\n\n/**\n * Numeric form of an identifier, or `null` when the origin uses a non-numeric\n * one. Published as-is in session.json: a local id like 'auth-refactor' has no\n * number, and reporting it as 0 would claim an Issue that does not exist.\n */\nfunction toIssueNumber(id: string): number | null {\n const parsed = Number.parseInt(id, 10);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Seed the snapshot with the stories a `tasks.json` already holds on disk, so\n * the monitor shows the plan instead of \"no user story yet\" until the first\n * execute iteration republishes them.\n *\n * Must be called **after** `session:start`, which resets the snapshot through\n * `createInitialSnapshot()`. An empty plan publishes nothing: the event would\n * bump the publisher's version without adding any content.\n *\n * Returns whether anything was published.\n */\nexport function publishStorySeed(\n publisher: SessionPublisher,\n stories: readonly UserStory[],\n at: string,\n): boolean {\n if (stories.length === 0) return false;\n publisher.publish({ type: 'stories:update', at, stories: [...stories] });\n return true;\n}\n\n/**\n * Publish the Issue's structural data (title, description, labels, state) so\n * the panel shows what is being implemented without a detour through GitHub.\n *\n * Same window as the story seed: right after `session:start`, which resets the\n * snapshot. The data comes from the `ResolvedIssue` the run already holds — no\n * extra provider call — and the description goes out whole, untruncated.\n */\nexport function publishIssueDetails(publisher: SessionPublisher, issue: Issue, at: string): void {\n publisher.publish({\n type: 'issue:update',\n at,\n number: issue.number,\n // Left undefined (rather than null) when the origin has no remote, so the\n // reducer keeps whatever URL session:start already published.\n url: issue.remoteRef ?? undefined,\n title: issue.title,\n description: issue.body,\n labels: issue.labels,\n state: issue.state,\n });\n}\n\n/** Runnable phase lists (excluding 'init' which is handled separately). */\nconst RUNNABLE_PHASES: PipelinePhase[] = ['prd', 'plan', 'execute', 'review', 'pr'];\nconst RUNNABLE_PHASES_NO_BRANCH: PipelinePhase[] = ['prd', 'plan', 'execute', 'review'];\nconst RUNNABLE_PHASES_WITH_PR_REVIEW: PipelinePhase[] = [...RUNNABLE_PHASES, 'pr-review'];\n\n/**\n * Phases of a queue's closing pass: the work is already committed by the\n * per-issue runs, so all that is left is the single consolidated Pull Request.\n */\nconst QUEUE_PR_PHASES = ['init', 'pr'] as const satisfies readonly PipelinePhase[];\nconst QUEUE_PR_PHASES_WITH_REVIEW = [\n 'init',\n 'pr',\n 'pr-review',\n] as const satisfies readonly PipelinePhase[];\nconst RUNNABLE_QUEUE_PR_PHASES: PipelinePhase[] = ['pr'];\nconst RUNNABLE_QUEUE_PR_PHASES_WITH_REVIEW: PipelinePhase[] = ['pr', 'pr-review'];\n\n/**\n * What the `pr-review` phase left behind, for the steps that run after it: the\n * automatic issue close, the highlighted warning and the final summary.\n *\n * Same shape the summary consumes: `requestedChanges` drives the close\n * suppression and is true on exit code 2 even when the plan is gone.\n */\ntype PrReviewOutcome = RunSummaryPrReview;\n\n/**\n * Recover the verdict and the report path the `pr-review` phase produced.\n *\n * Best-effort by design: the exit code already tells whether changes were\n * requested, so a missing plan or index costs the summary a detail, never the\n * decision to keep the issue open.\n */\nasync function readPrReviewOutcome(\n issue: string,\n tasksPath: string,\n requestedChanges: boolean,\n): Promise<PrReviewOutcome> {\n const outcome: PrReviewOutcome = { requestedChanges, recommendation: null, reportPath: null };\n try {\n const plan = await loadTaskPlan(tasksPath);\n outcome.recommendation = plan.prReview?.lastRecommendation ?? null;\n\n const pullRequest = plan.prReview?.pullRequestNumber;\n if (pullRequest !== undefined) {\n const dir = await prReviewDir({ issue, pullRequest });\n const index = await readPrReviewIndex(dir);\n const last = index?.rounds.reduce<PrReviewRoundEntry | null>(\n (latest, entry) => (latest === null || entry.round > latest.round ? entry : latest),\n null,\n );\n if (last) {\n outcome.reportPath = join(dir, last.reportPath);\n outcome.recommendation ??= last.recommendation;\n }\n }\n } catch {\n /* non-critical */\n }\n return outcome;\n}\n\n/** Options `run` accepts on top of the phase selection ones. */\nexport interface RunPipelineOptions {\n /** `--yes`: accept the discovered hierarchy without confirmation. */\n yes?: boolean;\n /** `--only`: run just the issues informed, skipping discovery. */\n only?: boolean;\n /** `--continue`: name the (automatic) User Story numbering continuity. */\n continueNumbering?: boolean;\n /** `--start-us <n>`: force the first plan of this run to start at `n`. */\n startUs?: number;\n /**\n * `--retry-limit <n>`: consecutive retries the `execute` phase may spend on a\n * transient failure. Absent means the engine default (`DEFAULTS.retryLimit`),\n * which is what every release before this flag existed used.\n */\n retryLimit?: number;\n /** `--retry-forever`: lift the retry count of the `execute` phase. */\n retryForever?: boolean;\n /**\n * `--on-issue-failure <mode>`: what one failing Issue does to the rest of a\n * queue. `stop` is the default and the behaviour of every release before the\n * flag existed.\n */\n onIssueFailure?: QueueFailureMode;\n}\n\n/** What one failing Issue does to the rest of the queue. */\nexport type QueueFailureMode =\n | /** End the run where it failed. The behaviour that has always been. */ 'stop'\n | /** Set it aside, run the independent work, come back to it. */ 'skip'\n | /** Set it aside for a human, and never come back to it. */ 'block';\n\n/** Attempts an Issue gets before the queue stops handing it out. */\nconst DEFAULT_MAX_ISSUE_ATTEMPTS = 2;\n\n/**\n * Everything a queue hands to the run of one of its issues.\n *\n * Its presence is what tells `runPipelinePhases` it is a member of a queue\n * rather than a standalone pipeline: the Pull Request moves to the end of the\n * queue, the branch is shared, commits carry the issue scope, and the terminal\n * summary is the queue's, not the issue's.\n */\ninterface QueueRunContext {\n /** State of the queue, for the branch every issue of it shares. */\n plan: ExecutionPlan;\n /** True when `init` already ran in this process for the queue. */\n preChecked: boolean;\n /** Issue already resolved by the planner, if this is the primary one. */\n resolved?: ResolvedIssue;\n /**\n * Set on the final pass of a queue, which runs no implementation phase at\n * all: only `pr` (and the optional `pr-review`), for the single Pull Request\n * that consolidates every issue.\n */\n finalPr?: PrQueueContext;\n}\n\n/** What one issue's run reports back to the caller. */\ninterface IssueRunResult {\n code: number;\n /** Phase that failed, `null` on success. */\n failedPhase: string | null;\n /** `branchName` of the issue's plan once the `plan` phase produced one. */\n branchName: string | null;\n storyCount: number;\n elapsedSeconds: number;\n /** Verdict of the `pr-review` phase, when it ran. */\n review?: PrReviewOutcome | null;\n /** Set when the run stopped to hand control over to a queue. */\n queue?: { plan: ExecutionPlan; resumed: boolean; resolved: ResolvedIssue };\n}\n\n/**\n * Entry point of `issue-flow run`.\n *\n * Accepts one issue (the historical form, untouched) or several. The first\n * attempt runs as a plain single-issue pipeline; only if the planner decides\n * this invocation is really a queue does it hand control over to\n * {@link runQueue} — and that decision is taken before any session is\n * published, so nothing was written on the way.\n */\nexport async function runPipeline(\n issue: string | readonly string[],\n mode: string,\n from?: string,\n noBranch?: boolean,\n prReview?: boolean,\n options: RunPipelineOptions = {},\n): Promise<number> {\n let requested: string[];\n try {\n requested = parseIssueArguments(typeof issue === 'string' ? [issue] : issue);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n // Ownership of the run, for the whole invocation — a queue is one run, not\n // one per issue. Two invocations in the same repository share a working tree\n // and a branch, so \"a different issue\" is not a different lock.\n const ownership = await claimRunOwnership(requested[0] as string);\n if (!ownership.ok) {\n printError(\n `Another issue-flow run owns this project: ${describeRunLockOwner(ownership.owner)}.`,\n );\n printInfo('Wait for it to finish, or stop that process before running again.');\n return 1;\n }\n\n try {\n const first = await runIssueSession(requested[0] as string, mode, {\n from,\n noBranch,\n prReview,\n requested,\n runOptions: options,\n ...(ownership.interruptedBy === null ? {} : { interruptedBy: ownership.interruptedBy }),\n });\n\n if (first.queue === undefined) {\n return first.code;\n }\n\n return runQueue(\n first.queue.plan,\n { mode, from, noBranch, prReview, runOptions: options },\n first.queue.resolved,\n );\n } finally {\n await ownership.release();\n }\n}\n\n/**\n * Phases that write to the repository — the working tree, the branch, or the\n * remote. The read-only ones (`init`, `prd`, `review`) produce artifacts under\n * the global storage and cannot be hurt by, nor hurt, a repository mid-rebase.\n */\nconst WRITING_PHASES: ReadonlySet<string> = new Set(['plan', 'execute', 'pr']);\n\n/**\n * Refuse to hand the repository to an agent while it is in a state a human has\n * to settle first.\n *\n * **Nothing is repaired here.** A rebase in progress, an unresolved conflict, a\n * detached HEAD or a branch that is not the plan's are reported with the\n * command that gets out of them, and the phase fails. That is the Epic's second\n * limit: no destructive operation is ever run automatically to fix state, and\n * \"the tool aborted my rebase overnight\" is exactly the outcome it forbids.\n *\n * Two of the checks are deliberately left to `resume` rather than run here:\n *\n * - a **dirty tree** does not block mid-pipeline, because the phases of one run\n * follow each other by design and uncommitted work between them is the\n * pipeline's own doing;\n * - the **branch** is not compared either, because within a run the `plan`\n * phase is what creates and checks it out, and a queue adopts a shared branch\n * after its own plan ran.\n *\n * `resume` reads both strictly, because there the repository may have been\n * touched by anything at all in between.\n */\nasync function ensureRepositoryWritable(phase: string): Promise<void> {\n if (!WRITING_PHASES.has(phase)) return;\n\n const preflight = await preflightRepository({ intent: 'resume-same-phase' });\n if (preflight.ok) return;\n\n for (const line of describePreflight(preflight)) {\n printError(line);\n }\n throw new Error(`The repository is not in a state the ${phase} phase can write to`);\n}\n\ntype RunOwnership =\n | { ok: true; interruptedBy: RunLock | null; release: () => Promise<void> }\n | { ok: false; owner: RunLock };\n\n/**\n * Take the project's run lock, or report who holds it.\n *\n * A project whose storage cannot be resolved at all (no git repository yet, no\n * home directory) runs **without** a lock rather than not running: the guard\n * exists to stop two runs from colliding, and it must never be the reason a\n * single run cannot start.\n */\nasync function claimRunOwnership(target: string): Promise<RunOwnership> {\n let lockFile: string;\n try {\n lockFile = (await resolveProjectPaths()).runLockFile;\n } catch {\n return { ok: true, interruptedBy: null, release: async () => {} };\n }\n\n const result = await acquireRunLock(lockFile, { target });\n if (!result.ok) return result;\n\n return {\n ok: true,\n interruptedBy: result.handle.reclaimedFrom,\n release: () => result.handle.release(),\n };\n}\n\ninterface IssueSessionInput {\n from?: string;\n noBranch?: boolean;\n prReview?: boolean;\n /** Identifiers the user asked for; only the standalone attempt needs them. */\n requested?: string[];\n runOptions?: RunPipelineOptions;\n queue?: QueueRunContext;\n /**\n * The dead owner whose lock this run took over. Recorded in the journal as\n * an interrupted run: something was executing here and never finished, and\n * that is the difference between a resume and a fresh start.\n */\n interruptedBy?: RunLock;\n}\n\n/**\n * Run one issue with its own session publisher and web monitor registration.\n *\n * This is the body `runPipeline` always had; a queue calls it once per issue,\n * which is what gives each of them its own `session.json` (and therefore its\n * own card in the monitor) inside a single process.\n */\nasync function runIssueSession(\n issueNumber: string,\n mode: string,\n input: IssueSessionInput,\n): Promise<IssueRunResult> {\n // Resolved once, at the top: every phase that runs below shares the process\n // cache, so the git call and the legacy migration happen a single time for\n // the whole run instead of once per phase.\n const paths = await resolveIssuePaths(issueNumber);\n\n // The `resilience` key, installed once for the whole run. Every `gh` call\n // below reads it synchronously (`getActiveResilienceConfig()`), so it has to\n // be in place before the first phase resolves the Issue — and the journal\n // decision below is the first thing that reads it. Absent configuration\n // leaves the base table, so this is a no-op for a project that configured\n // nothing.\n const resilience = await initResilienceConfig();\n\n // The watchdog budget, when the project configured one and the CLI did not\n // override it. A flag wins because it is the higher rung of the same ladder.\n const configuredInactivity = resilience.watchdog?.inactivityTimeoutMs;\n if (configuredInactivity !== undefined && getInactivityTimeout() === undefined) {\n setInactivityTimeout(configuredInactivity);\n }\n\n // Read per issue rather than cached for the process: the configuration is\n // per project and cheap to read, and a cached value would leak a `--web`\n // decision from one invocation into the next inside the same process.\n const webConfig = await loadWebConfig();\n\n // Two independent surfaces over one event stream: the snapshot the dashboard\n // reads, and the append-only journal an audit reads. Neither implies the\n // other — `--web` without a journal is the common case, and a journal\n // without `--web` is what an unattended run wants.\n const surfaces: SessionPublisher[] = [];\n const journalEnabled = resilience.journal?.enabled === true;\n if (webConfig.enabled || journalEnabled) {\n // resolveIssuePaths never creates directories, and a run may well be the\n // first thing to touch this issue's global folder — so the writer creates\n // it. Only when a surface asked for it: with monitoring off and no journal\n // the pipeline still creates nothing at all (issue 25, US-009).\n await mkdir(paths.issueDir, { recursive: true });\n }\n if (webConfig.enabled) {\n surfaces.push(\n new FilePublisher(paths.sessionFile, {\n logLimit: webConfig.logLimit,\n includeLogs: webConfig.includeLogs,\n }),\n );\n }\n if (journalEnabled) {\n surfaces.push(\n new JournalPublisher(paths.eventsFile, paths.rotatedEventsFile, {\n logLimit: webConfig.logLimit,\n includeLogs: webConfig.includeLogs,\n ...(resilience.journal?.maxFileBytes === undefined\n ? {}\n : { maxFileBytes: resilience.journal.maxFileBytes }),\n }),\n );\n }\n // The snapshot writer stays the primary surface, so `snapshot()` and\n // `version()` keep answering exactly what the dashboard answered before.\n const publisher: SessionPublisher =\n surfaces.length === 0\n ? new NullPublisher()\n : surfaces.length === 1\n ? (surfaces[0] as SessionPublisher)\n : new MultiPublisher(surfaces);\n setSessionPublisher(publisher);\n\n // Recorded through the publisher rather than printed, so it lands in the\n // journal beside the events of the run that replaced it.\n if (input.interruptedBy !== undefined) {\n const previous = input.interruptedBy;\n publisher.publish({\n type: 'log',\n at: isoNow(),\n level: 'warn',\n message: `Previous run interrupted: ${describeRunLockOwner(previous)}. Its lock was stale and has been taken over.`,\n });\n }\n\n // A null handle (port in use, ...) means the pipeline runs without a server.\n // ensureWebMonitor reuses an already-running, healthy instance instead of\n // binding a second one (US-001), or spawns it detached when none exists\n // (US-002) — either way the returned handle never owns a local server that\n // this process would need to close.\n if (webConfig.enabled) {\n await ensureWebMonitor({\n publisher,\n port: webConfig.port,\n host: webConfig.host,\n refreshSeconds: webConfig.refreshSeconds,\n });\n }\n\n let result: IssueRunResult = {\n code: 1,\n failedPhase: null,\n branchName: null,\n storyCount: 0,\n elapsedSeconds: 0,\n };\n\n // What a `Ctrl+C` leaves behind. Registered for the duration of this issue\n // only — a queue runs several, and each must checkpoint its own plan — and\n // deliberately split across the two shutdown phases: the state is written\n // while the agent is still alive, and the surfaces are closed after it is\n // gone, so nothing the checkpoint published is lost on the way out.\n const releaseCheckpoint = onShutdown({\n phase: 'checkpoint',\n run: async () => {\n await pauseIssue(paths.tasksFile, issueNumber);\n publisher.publish({\n type: 'log',\n at: isoNow(),\n level: 'warn',\n message: `Interrupted during issue #${issueNumber}. A checkpoint was saved; resume with \\`issue-flow resume ${issueNumber}\\`.`,\n });\n publisher.publish({ type: 'session:end', at: isoNow(), status: 'failed' });\n },\n });\n const releaseClose = onShutdown({\n phase: 'close',\n run: async () => {\n await publisher.close();\n },\n });\n\n try {\n result = await runPipelinePhases(issueNumber, paths, mode, publisher, input);\n if (result.code !== 0) {\n await reportIfOversized(issueNumber, paths, result);\n }\n return result;\n } finally {\n releaseCheckpoint();\n releaseClose();\n // A run that only decided to become a queue published nothing: closing the\n // session here would write a `session.json` for a pipeline that never ran.\n if (result.queue === undefined) {\n publisher.publish({\n type: 'session:end',\n at: isoNow(),\n status: result.code === 0 ? 'completed' : 'failed',\n });\n }\n // The web monitor is no longer this process's to close (US-002): it is a\n // detached, single machine-wide instance meant to outlive the pipeline\n // and serve other invocations. Only this run's own publication ends here.\n await publisher.close();\n setSessionPublisher(undefined);\n }\n}\n\n/**\n * Write a decomposition report when the failure looks like \"too much work\",\n * and say nothing when it does not.\n *\n * The guard that matters is the one *before* the assessment: a run that died\n * because the network went down is not too large, and reacting to that with\n * \"have you considered splitting this issue?\" is worse than silence. Only a\n * failure the resilience layer could not absorb — and that carries at least two\n * independent size signals — gets a report.\n *\n * Nothing is split here. The report is written and the issue is marked\n * `blocked` with a pointer to it, because splitting an issue is a product\n * decision and the tool does not get to make it.\n */\nasync function reportIfOversized(\n issueNumber: string,\n paths: IssuePaths,\n result: IssueRunResult,\n): Promise<void> {\n try {\n const journal = `${await readFileIfPresent(paths.rotatedEventsFile)}${await readFileIfPresent(paths.eventsFile)}`;\n const plan = await loadTaskPlan(paths.tasksFile).catch(() => null);\n\n const assessment = assessDecomposition({\n journal,\n plan,\n filesTouched: await countChangedFiles(),\n hitMaxIterations: result.failedPhase === 'execute' && plan?.issueStatus !== 'completed',\n });\n if (!assessment.oversized) return;\n\n const report = buildDecompositionReport({\n issueNumber,\n assessment,\n plan,\n at: isoNow(),\n });\n const reportFile = paths.decompositionFile;\n await mkdir(paths.issueDir, { recursive: true });\n await writeFile(reportFile, report, 'utf-8');\n\n printWarning(`Issue #${issueNumber} looks larger than one run. Report: ${reportFile}`);\n for (const signal of assessment.signals) {\n printWarning(` ${signal.detail}`);\n }\n\n await maybeCreateSubIssues(issueNumber, plan, reportFile);\n\n if (plan !== null) {\n await saveTaskPlan(paths.tasksFile, {\n ...plan,\n runState: {\n currentPhase: plan.runState?.currentPhase ?? result.failedPhase,\n attempt: plan.runState?.attempt ?? 0,\n owner: null,\n status: 'blocked',\n blockedReason: `Looks larger than one run; see ${reportFile}`,\n lastHeartbeatAt: isoNow(),\n },\n });\n }\n } catch {\n // A report is a courtesy. Failing to write one must not change the exit\n // code of a run that already failed for its own reasons.\n }\n}\n\n/**\n * Create the proposed sub-issues, but only when asked and only when it is safe.\n *\n * Two conditions, and the second is the one that matters: `--auto-decompose`\n * has to be on, **and** the branch must carry no committed story yet. Splitting\n * an issue whose work is half done leaves commits that belong to no issue and\n * sub-issues that describe work already merged — a mess nobody asked for, made\n * automatically at 3am. With commits present the report still stands; only the\n * acting stops.\n */\nasync function maybeCreateSubIssues(\n issueNumber: string,\n plan: TaskPlan | null,\n reportFile: string,\n): Promise<void> {\n if (getActiveResilienceConfig().decompose?.auto !== true) {\n printInfo(`Nothing was split. Read ${reportFile} and decide.`);\n return;\n }\n\n const committed = await committedStoryIds(await getBaseBranch()).catch(() => new Set<string>());\n if (committed.size > 0) {\n printWarning(\n `--auto-decompose did not run: ${committed.size} story(ies) are already committed on this branch. ` +\n 'Splitting on top of committed work needs a person.',\n );\n return;\n }\n\n const proposals = proposeSubIssues(plan);\n if (proposals.length === 0) {\n printInfo('Nothing left to split: no pending story.');\n return;\n }\n\n const { runGenerate } = await import('./generate.js');\n for (const proposal of proposals) {\n const instruction = [\n `Create a sub-issue of #${issueNumber}: ${proposal.title}.`,\n proposal.dependsOn.length === 0 ? '' : `It depends on: ${proposal.dependsOn.join(', ')}.`,\n 'It covers exactly these user stories, and nothing else:',\n ...proposal.stories.map((story) => `- ${story.id} ${story.title}: ${story.description}`),\n ]\n .filter((line) => line !== '')\n .join('\\n');\n\n const code = await runGenerate(instruction);\n if (code !== 0) {\n printWarning(\n `Could not create \"${proposal.title}\". The report at ${reportFile} still stands.`,\n );\n return;\n }\n }\n}\n\n/** Files changed on this branch, or `0` when git cannot say. */\nasync function countChangedFiles(): Promise<number> {\n try {\n const base = await getBaseBranch();\n const result = await run('git', ['diff', '--name-only', `${base}...HEAD`]);\n if (result.exitCode !== 0) return 0;\n return result.stdout.split('\\n').filter((line) => line.trim() !== '').length;\n } catch {\n return 0;\n }\n}\n\nasync function readFileIfPresent(path: string): Promise<string> {\n try {\n return await readFile(path, 'utf-8');\n } catch {\n return '';\n }\n}\n\n/**\n * Mark an interrupted issue as paused, in the one place resumption reads.\n *\n * `pipeline` still says which phases finished — that is what `resume` continues\n * from — and `runState` is what says *why* the run is not running: paused by a\n * person, not failed, not still going. Before this field, a `Ctrl+C` left\n * `issueStatus: 'in_progress'` and nothing else, and the difference between\n * \"someone stopped it\" and \"it died\" was unrecoverable.\n *\n * Never throws: a checkpoint that cannot be written must not stop the rest of\n * the shutdown, and the phases already marked complete are still on disk.\n */\nasync function pauseIssue(tasksFile: string, issueNumber: string): Promise<void> {\n try {\n const plan = await loadTaskPlan(tasksFile);\n await saveTaskPlan(tasksFile, {\n ...plan,\n runState: {\n ...(plan.runState ?? {\n currentPhase: null,\n attempt: 0,\n lastHeartbeatAt: null,\n blockedReason: null,\n owner: null,\n }),\n status: 'paused',\n lastHeartbeatAt: isoNow(),\n },\n });\n } catch {\n // No plan yet (interrupted before the `plan` phase), or an unreadable one.\n printWarning(`Could not write a checkpoint for issue #${issueNumber}.`);\n }\n}\n\nasync function runPipelinePhases(\n issueNumber: string,\n paths: IssuePaths,\n mode: string,\n publisher: SessionPublisher,\n input: IssueSessionInput,\n): Promise<IssueRunResult> {\n const { from, noBranch, prReview, queue } = input;\n // User Story numbering flags (issue #36). `--start-us` names a starting\n // point, so the queue only ever hands it to the first issue it runs —\n // applying it to each one would give them all the same ids, the very\n // collision #36 set out to remove. Every later issue continues from history,\n // which by then already includes the plans written earlier in this run.\n const continueNumbering = input.runOptions?.continueNumbering;\n const startUs = input.runOptions?.startUs;\n // Retry budget of the `execute` phase (`--retry-limit`, `--retry-forever`).\n // Left `undefined` when the flags are absent so `createConfig()` applies the\n // engine defaults — passing a number here would make `run` diverge from\n // `execute` the moment one of those defaults changes.\n const executeRetry = {\n retryLimit: input.runOptions?.retryLimit,\n retryForever: input.runOptions?.retryForever,\n };\n const tasksPath = paths.tasksFile;\n const sessionId = randomUUID();\n\n // Refined with the provider's own number once the Issue is resolved.\n let publishedIssueNumber = toIssueNumber(issueNumber);\n\n const publishSessionStart = (\n phases: readonly string[],\n at: string,\n info?: { issueUrl?: string; branch?: string },\n ): void => {\n publisher.publish({\n type: 'session:start',\n at,\n sessionId,\n issueNumber: publishedIssueNumber,\n issueUrl: info?.issueUrl,\n branch: info?.branch,\n phases: [...phases],\n environment: { node: process.version, platform: process.platform },\n });\n };\n\n printInfo(`Starting pipeline for issue #${issueNumber} (mode: ${mode})`);\n\n // Loaded before the checks so init knows which origin the user is heading\n // for: with a local one, a missing gh must not fail the environment.\n const issuesConfig = await loadIssuesConfig();\n\n // Phase 1: Init check. Inside a queue it already ran for the whole run, so\n // the environment is not probed once per issue — the phase is still\n // published, keeping every issue's session shape identical.\n const sessionStartedAt = isoNow();\n if (queue?.preChecked !== true) {\n printInfo('Running prerequisite checks...');\n const initCode = await runInit(issuesConfig.preferredProvider);\n if (initCode !== 0) {\n publishSessionStart(PIPELINE_PHASES, sessionStartedAt);\n publisher.publish({ type: 'phase:start', at: sessionStartedAt, phase: 'init' });\n publisher.publish({\n type: 'phase:end',\n at: isoNow(),\n phase: 'init',\n success: false,\n error: 'Prerequisites not met',\n });\n printError('Prerequisites not met. Fix the issues above and try again.');\n return failure(1);\n }\n }\n\n // The origin is settled once, here, and the decision travels to every phase.\n // Resolving per phase would query the providers five times and could ask the\n // user about the same divergence five times.\n const resolution = await resolveCommandIssue(issueNumber, queue?.resolved, {\n config: issuesConfig,\n });\n if (!resolution.ok) {\n return failure(resolution.code);\n }\n const resolvedIssue = resolution.resolved;\n publishedIssueNumber = resolvedIssue.issue.number;\n\n // Everything above is what a queue needs to exist: prerequisites checked and\n // the primary Issue resolved, but not a single phase run and not a single\n // event published. This is where a run learns it is really a queue.\n if (queue === undefined) {\n const decision = await decideQueue({\n requested: input.requested ?? [issueNumber],\n resolved: resolvedIssue,\n noBranch: noBranch ?? false,\n prReview: prReview ?? false,\n runOptions: input.runOptions ?? {},\n });\n if (decision.kind === 'stop') {\n return failure(decision.code);\n }\n if (decision.kind === 'queue') {\n return {\n ...failure(0),\n queue: { plan: decision.plan, resumed: decision.resumed, resolved: resolvedIssue },\n };\n }\n }\n\n // Resolve noBranch mode: persisted value takes precedence on resume\n let effectiveNoBranch = noBranch ?? false;\n // Resolve pr-review mode: flag > persisted value > default (off). Unlike\n // --no-branch, the flag wins: the phase adds a step at the end instead of\n // changing what the earlier phases did, so opting in on resume is safe.\n let effectivePrReview = prReview ?? false;\n let planIssueUrl: string | undefined;\n let planBranch: string | undefined;\n // Kept from the same read as planIssueUrl/planBranch — seeding the snapshot\n // must not cost a second trip to disk.\n let planStories: UserStory[] = [];\n try {\n const existingPlan = await loadTaskPlan(tasksPath);\n const persistedNoBranch = existingPlan.noBranch ?? false;\n planIssueUrl = existingPlan.issueUrl || undefined;\n planBranch = existingPlan.branchName || undefined;\n planStories = existingPlan.userStories;\n effectivePrReview = prReview ?? existingPlan.prReview?.enabled ?? false;\n\n // Only warn when the user explicitly passed a flag that conflicts with the persisted value\n if (noBranch !== undefined && noBranch !== persistedNoBranch) {\n if (persistedNoBranch) {\n printWarning(\n 'This pipeline was started with --no-branch. Ignoring current flag; using persisted mode.',\n );\n } else {\n printWarning(\n 'This pipeline was started without --no-branch. Ignoring current flag; using persisted mode.',\n );\n }\n }\n\n // Persisted mode wins on resume\n effectiveNoBranch = persistedNoBranch;\n } catch {\n // No tasks.json yet — use the CLI flag as-is\n }\n\n // The CLI rejects --pr-review with --no-branch, but the persisted no-branch\n // mode can only be known here. Without a pr phase there is no Pull Request\n // to review, so the opt-in is dropped instead of failing a resumed run.\n if (effectiveNoBranch && effectivePrReview) {\n printWarning(\n 'This pipeline runs with --no-branch and opens no PR. Skipping the pr-review phase.',\n );\n effectivePrReview = false;\n }\n\n // Persist the opt-in as soon as we know it, not only after the `plan` phase.\n // A mid-pipeline `--pr-review` (e.g. `--from pr --pr-review`) never re-enters\n // the plan runner, and without `enabled: true` a later resume without the flag\n // would drop the phase even when `prReviewCompleted` is still false.\n if (effectivePrReview) {\n try {\n const plan = await loadTaskPlan(tasksPath);\n if (plan.prReview?.enabled !== true) {\n plan.prReview = { ...plan.prReview, enabled: true, rounds: plan.prReview?.rounds ?? 0 };\n await saveTaskPlan(tasksPath, plan);\n }\n } catch {\n // No tasks.json yet — the plan runner persists it after creating the file.\n }\n }\n\n // Inside a queue the Pull Request is opened once, after the last issue, so\n // `pr` (and with it `pr-review`) leaves the per-issue phase list. The list is\n // the same one `--no-branch` uses, but the branch is still created: what\n // changes is who opens the Pull Request, not whether there is a branch.\n const inQueue = queue !== undefined;\n // The queue's last pass implements nothing: it only opens the single Pull\n // Request that covers every issue already committed to the shared branch.\n const finalPr = queue?.finalPr;\n const activePhases = finalPr\n ? effectivePrReview\n ? QUEUE_PR_PHASES_WITH_REVIEW\n : QUEUE_PR_PHASES\n : inQueue\n ? PIPELINE_PHASES_NO_BRANCH\n : effectiveNoBranch\n ? PIPELINE_PHASES_NO_BRANCH\n : effectivePrReview\n ? PIPELINE_PHASES_WITH_PR_REVIEW\n : PIPELINE_PHASES;\n const phaseOrder = finalPr\n ? effectivePrReview\n ? RUNNABLE_QUEUE_PR_PHASES_WITH_REVIEW\n : RUNNABLE_QUEUE_PR_PHASES\n : inQueue\n ? RUNNABLE_PHASES_NO_BRANCH\n : effectiveNoBranch\n ? RUNNABLE_PHASES_NO_BRANCH\n : effectivePrReview\n ? RUNNABLE_PHASES_WITH_PR_REVIEW\n : RUNNABLE_PHASES;\n\n // Commit scope for this issue's stories: only a queue needs one, because\n // only there do several issues share a branch (and therefore a `git log`).\n const queueCommitScope = queue === undefined ? undefined : `issue-${issueNumber}`;\n // Branch this issue will work on, reported back so the queue can adopt the\n // first issue's choice for every later one.\n let producedBranch: string | null = null;\n\n // Adopted **before** the phases run, not only after `plan`: an issue whose\n // plan phase is already complete (it was run standalone before joining the\n // queue, or the queue is being resumed) never re-enters the plan runner, and\n // would otherwise send `execute` at a branch of its own.\n if (queue !== undefined && finalPr === undefined && !effectiveNoBranch) {\n producedBranch = await adoptQueueBranch(tasksPath, queue.plan.branchName);\n if (producedBranch !== null) {\n planBranch = producedBranch;\n }\n }\n\n // The phase list is only known after resolving --no-branch, so the init\n // phase (which already ran) is published retroactively with real timestamps.\n publishSessionStart(activePhases, sessionStartedAt, {\n issueUrl: planIssueUrl ?? resolvedIssue.issue.remoteRef ?? undefined,\n branch: planBranch,\n });\n // Right after session:start (which resets the snapshot) and before any phase\n // event, so the first /api/status poll already answers with the Issue and\n // the plan.\n publishIssueDetails(publisher, resolvedIssue.issue, sessionStartedAt);\n publishStorySeed(publisher, planStories, sessionStartedAt);\n publisher.publish({ type: 'phase:start', at: sessionStartedAt, phase: 'init' });\n publisher.publish({ type: 'phase:end', at: isoNow(), phase: 'init', success: true });\n await publishGitState(publisher);\n\n // Determine starting phase\n let startPhase: PipelinePhase = 'prd';\n if (from) {\n if (!(activePhases as readonly string[]).includes(from)) {\n const validPhases = activePhases.filter((p) => p !== 'init').join(', ');\n // Check if the phase exists in the full set but is excluded by --no-branch\n if (effectiveNoBranch && (PIPELINE_PHASES as readonly string[]).includes(from)) {\n printError(\n `The '${from}' phase is not available in --no-branch mode. Valid phases: ${validPhases}`,\n );\n } else {\n printError(`Invalid phase: ${from}. Valid phases: ${validPhases}`);\n }\n return failure(1);\n }\n startPhase = from as PipelinePhase;\n } else {\n // Try to auto-resume from pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n const mgr = new PipelineManager(plan, tasksPath, activePhases);\n const nextPhase = mgr.getNextPhase();\n if (nextPhase && nextPhase !== 'init') {\n startPhase = nextPhase;\n printInfo(`Resuming from phase: ${startPhase}`);\n }\n } catch {\n // No tasks.json yet — start from beginning\n }\n }\n\n // Validate resume prerequisites if starting from a later phase\n if (from) {\n try {\n const plan = await loadTaskPlan(tasksPath);\n const mgr = new PipelineManager(plan, tasksPath, activePhases);\n if (!mgr.canResume(startPhase)) {\n printError(`Cannot resume from ${startPhase}: prerequisite phases not complete`);\n return failure(1);\n }\n } catch {\n if (startPhase !== 'prd') {\n printError(`Cannot resume from ${startPhase}: no pipeline state found`);\n return failure(1);\n }\n }\n }\n\n // A phase that is not part of this run's list (the closing pass of a queue\n // runs only `pr`) starts the renderer at the beginning rather than at -1.\n const startIdx = Math.max(phaseOrder.indexOf(startPhase), 0);\n\n // Build phase runner functions that throw on failure\n const makeRunner = (fn: () => Promise<number>, phase: string) => async () => {\n await ensureRepositoryWritable(phase);\n const code = await fn();\n if (code !== 0) {\n throw new Error(`Phase ${phase} failed with exit code ${code}`);\n }\n };\n\n const runners: Record<string, () => Promise<void>> = {\n prd: makeRunner(() => runPrd(issueNumber, resolvedIssue), 'prd'),\n plan: async () => {\n await makeRunner(\n () => runPlan(issueNumber, resolvedIssue, { continueFlag: continueNumbering, startUs }),\n 'plan',\n )();\n // Persist the phase-selection modes into the newly created tasks.json\n if (effectiveNoBranch || effectivePrReview) {\n try {\n const plan = await loadTaskPlan(tasksPath);\n if (effectiveNoBranch) {\n plan.noBranch = true;\n }\n if (effectivePrReview) {\n plan.prReview = { ...plan.prReview, enabled: true, rounds: plan.prReview?.rounds ?? 0 };\n }\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical: tasks.json may not exist yet if plan phase didn't create it */\n }\n }\n // A queue shares one branch: the first issue's plan decides it, every\n // later issue has it written over whatever slug the agent derived from\n // its own title. The `execute` prompt then finds the branch already\n // checked out instead of creating a second one — the creation logic\n // itself is untouched.\n if (queue !== undefined && !effectiveNoBranch) {\n producedBranch = await adoptQueueBranch(tasksPath, queue.plan.branchName);\n }\n },\n execute: makeRunner(\n () =>\n runExecute(undefined, {\n issue: issueNumber,\n commitScope: queueCommitScope,\n ...executeRetry,\n }),\n 'execute',\n ),\n review: async () => {\n // Read maxCorrectionCycles\n let maxCycles = 3;\n try {\n const plan = await loadTaskPlan(tasksPath);\n maxCycles = plan.maxCorrectionCycles;\n } catch {\n /* use default */\n }\n\n let code = await runReview(issueNumber, resolvedIssue);\n\n // Auto-correction loop on failure\n let cycle = 0;\n while (code !== 0 && cycle < maxCycles) {\n cycle++;\n printWarning(`Review failed. Starting correction cycle ${cycle}/${maxCycles}...`);\n publisher.publish({ type: 'correction:cycle', at: isoNow(), cycle, maxCycles });\n\n // Update correction cycle in tasks.json\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.correctionCycle = cycle;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical */\n }\n\n // Re-execute\n const execCode = await runExecute(undefined, {\n issue: issueNumber,\n commitScope: queueCommitScope,\n ...executeRetry,\n });\n if (execCode !== 0) {\n throw new Error('Correction execution failed');\n }\n\n // Re-review\n code = await runReview(issueNumber, resolvedIssue);\n }\n\n if (code !== 0) {\n throw new Error(`Review failed after ${maxCycles} correction cycles`);\n }\n },\n pr: makeRunner(\n // The options argument is omitted outside a queue so a standalone run\n // calls `runPr` exactly as it always did.\n () =>\n finalPr === undefined\n ? runPr(issueNumber, resolvedIssue)\n : runPr(issueNumber, resolvedIssue, { queue: finalPr }),\n 'pr',\n ),\n };\n\n // Filled by the pr-review runner; read after the pipeline finishes. Held in a\n // box because a `let` written only inside a closure keeps its initial `null`\n // narrowing at the read site.\n const reviewBox: { outcome: PrReviewOutcome | null } = { outcome: null };\n if (effectivePrReview) {\n runners['pr-review'] = async () => {\n // `yes` because the run is autonomous: the phase must never stop to ask\n // which Pull Request it is reviewing.\n const code = await runPrReview(undefined, { issue: issueNumber, yes: true });\n if (code === 1) {\n throw new Error('Phase pr-review failed with exit code 1');\n }\n // Exit code 2 is a verdict, not a failure: the review ran, the report is\n // on disk and the pipeline keeps going.\n reviewBox.outcome = await readPrReviewOutcome(issueNumber, tasksPath, code === 2);\n };\n }\n\n // Publish phase:start/phase:end around every runner without touching the\n // listr2 renderer (pipeline-renderer.ts stays publication-free). Commit/PR\n // enrichment happens only at these boundaries (and at iteration end, in\n // engine.ts) — never per HTTP request.\n const instrumentedRunners = Object.fromEntries(\n Object.entries(runners).map(([phase, fn]) => [\n phase,\n async () => {\n publisher.publish({ type: 'phase:start', at: isoNow(), phase });\n try {\n await fn();\n await publishGitState(publisher);\n publisher.publish({ type: 'phase:end', at: isoNow(), phase, success: true });\n } catch (err) {\n await publishGitState(publisher);\n publisher.publish({\n type: 'phase:end',\n at: isoNow(),\n phase,\n success: false,\n error: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n },\n ]),\n );\n\n // Run pipeline with listr2 renderer — startup header printed above, summary below\n const result = await runPipelineWithRenderer({\n phases: phaseOrder,\n startIndex: startIdx,\n verbose: isVerbose(),\n runners: instrumentedRunners,\n tasksPath,\n });\n\n if (!result.success) {\n printError(`Phase ${result.failedPhase} failed`);\n return {\n ...failure(1),\n failedPhase: result.failedPhase ?? null,\n branchName: producedBranch,\n elapsedSeconds: result.overallElapsedSeconds,\n };\n }\n\n // A PR review asking for changes is not a pipeline failure, but the work is\n // not done either: the warning is highlighted and the issue stays open.\n const review = reviewBox.outcome;\n if (review?.requestedChanges) {\n console.log('');\n printWarning('PR review requested changes — the Pull Request is not ready to merge.');\n if (review.reportPath !== null) {\n console.log(` Report: ${review.reportPath}`);\n }\n }\n\n // Close the issue through whoever owns it. A provider without close() (a\n // read-only origin) has nothing to do here, so the step is simply skipped.\n // REQUEST_CHANGES also leaves the local plan unfinished: marking\n // `issueStatus: completed` while `prReviewCompleted` is false would lie to\n // every tool that keys off the local status.\n // Inside a queue the issues are closed once, after the consolidated Pull\n // Request: closing one here would announce it as done while the branch that\n // carries its work has not even been proposed yet.\n if (review?.requestedChanges) {\n printInfo('Issue left open until the review blockers are addressed.');\n } else if (!inQueue) {\n await closeIssue(issueNumber, resolvedIssue.source);\n }\n\n // Get branch and story count\n let branchName = 'unknown';\n try {\n const proc = await execa('git', ['branch', '--show-current'], { reject: false });\n branchName = proc.stdout?.toString().trim() ?? 'unknown';\n } catch {\n /* non-critical */\n }\n\n let storyCount = 0;\n let planPrUrl: string | null = null;\n try {\n const plan = await loadTaskPlan(tasksPath);\n storyCount = plan.userStories.length;\n // The `pr` phase records the Pull Request it opened; trusting it spares a\n // round-trip to the GitHub CLI below.\n planPrUrl = plan.pullRequest?.url ?? null;\n\n plan.lastAttemptAt = isoNow();\n if (!review?.requestedChanges) {\n plan.issueStatus = 'completed';\n plan.completedAt = isoNow();\n }\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical */\n }\n\n // Inside a queue the run is not over: the summary, the Pull Request and the\n // totals belong to the queue, which prints them once at the end.\n if (inQueue) {\n if (finalPr === undefined) {\n printSuccess(`Issue #${issueNumber} completed (${storyCount} stories).`);\n }\n return {\n code: 0,\n failedPhase: null,\n branchName: producedBranch ?? (branchName === 'unknown' ? null : branchName),\n storyCount,\n elapsedSeconds: result.overallElapsedSeconds,\n review,\n };\n }\n\n // Get PR URL for summary. Only GitHub-hosted Issues have a pull request to\n // look up, and --no-branch never opens one. Falling back to the GitHub CLI\n // uses the branch actually checked out: a query without a head branch would\n // return an unrelated Pull Request.\n let prUrl = 'unknown';\n if (!effectiveNoBranch && resolvedIssue.source === 'github') {\n if (planPrUrl !== null) {\n prUrl = planPrUrl;\n } else if (branchName !== 'unknown' && branchName !== '') {\n try {\n const latest = mostRecent(await listPullRequests(branchName));\n if (latest !== null) {\n prUrl = latest.url;\n }\n } catch {\n /* non-critical */\n }\n }\n }\n\n printRunSummary({\n issueNumber,\n branchName,\n noBranch: effectiveNoBranch,\n storyCount,\n elapsedSeconds: result.overallElapsedSeconds,\n prUrl,\n prReview: review,\n // Process-owned counters: the session snapshot is empty whenever web\n // monitoring is off, so it cannot be the source of these totals.\n usage: getRunUsageTotals(),\n });\n\n return {\n code: 0,\n failedPhase: null,\n branchName: branchName === 'unknown' ? null : branchName,\n storyCount,\n elapsedSeconds: result.overallElapsedSeconds,\n };\n}\n\ninterface DecideQueueInput {\n requested: string[];\n resolved: ResolvedIssue;\n noBranch: boolean;\n prReview: boolean;\n runOptions: RunPipelineOptions;\n}\n\n/**\n * Ask the planner whether this invocation is a queue, resolving the storage\n * paths it needs.\n *\n * A failure here degrades to a single-issue run when only one issue was asked\n * for — discovery is an enrichment, and losing it must never cost the user the\n * pipeline. With several issues informed there is nothing to degrade to: the\n * user asked for a queue, so the error is reported and the run stops.\n */\nasync function decideQueue(input: DecideQueueInput): Promise<QueueDecision> {\n const primary = input.requested[0] as string;\n const single = input.requested.length === 1;\n\n if (single && input.runOptions.only === true) {\n return { kind: 'single' };\n }\n\n try {\n const [project, queuePaths] = await Promise.all([\n resolveProjectPaths(),\n resolveQueuePaths(primary),\n ]);\n\n return await planQueue({\n requested: input.requested,\n source: input.resolved.source,\n known: [input.resolved.issue],\n projectId: project.projectId,\n planFile: queuePaths.planFile,\n noBranch: input.noBranch,\n prReview: input.prReview,\n confirm: { yes: input.runOptions.yes, only: input.runOptions.only },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (single) {\n printWarning(\n `Could not inspect related issues (${message}). Running issue #${primary} alone.`,\n );\n return { kind: 'single' };\n }\n printError(`Could not plan the execution of ${input.requested.length} issues: ${message}`);\n return { kind: 'stop', code: 1 };\n }\n}\n\n/**\n * Run a queue of issues: one shared branch, one process, one issue at a time.\n *\n * Each issue goes through the very same phases a single-issue run does\n * (`prd` → `plan` → `execute` → `review`), with its own `tasks.json`, its own\n * session and its own usage scope. The queue owns only what is shared: the\n * order, the branch, the Pull Request and the consolidated totals.\n *\n * A failure stops the queue where it happened, records the issue and the phase,\n * and leaves every commit and the branch exactly as they are — the next\n * invocation resumes from the failed issue without redoing the ones before it.\n */\nasync function runQueue(\n initialPlan: ExecutionPlan,\n options: {\n mode: string;\n from?: string;\n noBranch?: boolean;\n prReview?: boolean;\n runOptions?: RunPipelineOptions;\n },\n resolvedPrimary?: ResolvedIssue,\n): Promise<number> {\n const planFile = (await resolveQueuePaths(initialPlan.id)).planFile;\n let plan = initialPlan;\n\n const queueUsage = beginUsageScope();\n const startedAtMs = Date.now();\n const summaries: QueueIssueSummary[] = [];\n let firstOfThisRun = true;\n\n const resilience = getActiveResilienceConfig();\n const failureMode: QueueFailureMode =\n options.runOptions?.onIssueFailure ??\n (resilience.queue?.onIssueFailure as QueueFailureMode | undefined) ??\n 'stop';\n const maxIssueAttempts = resilience.queue?.maxIssueAttempts ?? DEFAULT_MAX_ISSUE_ATTEMPTS;\n\n // Ids this invocation is done with. Without it, an Issue that exhausted its\n // attempts would be handed back out on the very next lookup — `failed` comes\n // before `pending` in the resumption policy, which is right *across*\n // invocations and wrong inside one.\n const exhausted = new Set<string>();\n const failedIds = new Set<string>();\n\n try {\n while (true) {\n const entry = nextQueueIssue(plan, { exclude: exhausted });\n if (entry === null) break;\n\n plan = markQueueIssueInProgress(plan, entry.id);\n await saveExecutionPlan(planFile, plan);\n\n printInfo(\n `Queue ${entry.position}/${plan.issues.length}: issue #${entry.id}${entry.title === '' ? '' : ` — ${entry.title}`}`,\n );\n\n const issueUsage = beginUsageScope();\n let result: IssueRunResult;\n try {\n result = await runIssueSession(entry.id, options.mode, {\n // `--from` addresses the issue the queue is resuming, never the ones\n // that come after it — those start from their own beginning.\n from: firstOfThisRun ? options.from : undefined,\n noBranch: options.noBranch,\n // `--start-us` belongs to the first issue this invocation runs; the\n // ones after it continue from the history those plans just wrote.\n runOptions: {\n ...options.runOptions,\n startUs: firstOfThisRun ? options.runOptions?.startUs : undefined,\n },\n queue: {\n plan,\n preChecked: true,\n resolved: entry.id === initialPlan.id ? resolvedPrimary : undefined,\n },\n });\n } finally {\n firstOfThisRun = false;\n // Ending here (not only on the happy path) keeps a thrown error from\n // leaving an orphan accumulator on top of the scope stack, where it\n // would silently become \"the current scope\" for everything after it.\n issueUsage.end();\n }\n const usage = issueUsage.totals();\n\n if (result.code !== 0) {\n const failure = {\n phase: result.failedPhase,\n error: {\n category: 'queue_issue_failed',\n message: `Issue #${entry.id} failed${result.failedPhase === null ? '' : ` in phase ${result.failedPhase}`}`,\n at: isoNow(),\n },\n };\n const where = result.failedPhase === null ? '' : ` (phase ${result.failedPhase})`;\n\n if (failureMode === 'stop') {\n plan = markQueueIssueFailed(plan, entry.id, failure);\n await saveExecutionPlan(planFile, plan);\n printError(\n `Queue stopped at issue #${entry.id}${where}. ` +\n 'The branch and every commit made so far were kept.',\n );\n printInfo(`Resume with: issue-flow run ${plan.requested.join(',')}`);\n return result.code;\n }\n\n if (failureMode === 'block') {\n plan = markQueueIssueBlocked(\n plan,\n entry.id,\n `Failed${where} and --on-issue-failure block was in force`,\n failure,\n );\n await saveExecutionPlan(planFile, plan);\n exhausted.add(entry.id);\n failedIds.add(entry.id);\n printWarning(\n `Issue #${entry.id} failed${where} and is blocked for review. Continuing with the rest of the queue.`,\n );\n continue;\n }\n\n // `skip`: not a verdict on the eleven other Issues in the queue.\n const attempts = entry.attempts + 1;\n if (attempts >= maxIssueAttempts) {\n plan = markQueueIssueFailed(plan, entry.id, failure);\n exhausted.add(entry.id);\n failedIds.add(entry.id);\n printWarning(\n `Issue #${entry.id} failed${where} after ${attempts} attempt(s). Continuing with the rest of the queue.`,\n );\n } else {\n plan = markQueueIssueSkipped(plan, entry.id, failure);\n printWarning(\n `Issue #${entry.id} failed${where}. Skipping it for now and coming back at the end of the queue.`,\n );\n }\n await saveExecutionPlan(planFile, plan);\n continue;\n }\n\n // The first issue names the branch; every later one is made to adopt it.\n if (plan.branchName === null && result.branchName !== null) {\n plan = setQueueBranch(plan, result.branchName);\n }\n plan = markQueueIssueCompleted(plan, entry.id);\n await saveExecutionPlan(planFile, plan);\n\n summaries.push({\n id: entry.id,\n title: entry.title,\n storyCount: result.storyCount,\n elapsedSeconds: result.elapsedSeconds,\n usage,\n });\n }\n\n const code = await finishQueue(\n plan,\n planFile,\n summaries,\n startedAtMs,\n queueUsage,\n options,\n resolvedPrimary,\n );\n\n if (failedIds.size > 0) {\n // The queue went as far as it could, and that is worth reporting as a\n // failure even though the independent work landed.\n printError(\n `${failedIds.size} issue(s) did not finish: ${[...failedIds].map((id) => `#${id}`).join(', ')}.`,\n );\n printInfo(`Resume with: issue-flow run ${plan.requested.join(',')}`);\n return code === 0 ? 1 : code;\n }\n return code;\n } finally {\n queueUsage.end();\n }\n}\n\n/**\n * Everything that happens once every issue of the queue is done: the single\n * consolidated Pull Request, closing the issues and reporting. Split out so the\n * loop above stays about scheduling.\n */\nasync function finishQueue(\n plan: ExecutionPlan,\n planFile: string,\n summaries: QueueIssueSummary[],\n startedAtMs: number,\n /** Read only after the consolidated Pull Request ran, so its cost counts. */\n queueUsage: { totals: () => ReturnType<typeof getRunUsageTotals> },\n options: { mode: string; noBranch?: boolean; prReview?: boolean },\n resolvedPrimary?: ResolvedIssue,\n): Promise<number> {\n let current = plan;\n let review: PrReviewOutcome | null = null;\n\n // One Pull Request for the whole queue, and only when there is a branch to\n // propose and no Pull Request has been opened for this queue yet.\n const alreadyOpened = current.pullRequest !== undefined || (await primaryPrCreated(current));\n if (!current.noBranch && options.noBranch !== true && !alreadyOpened) {\n const outcome = await runIssueSession(current.id, options.mode, {\n prReview: options.prReview ?? current.prReview,\n queue: {\n plan: current,\n preChecked: true,\n resolved: resolvedPrimary,\n finalPr: await buildPrQueueContext(current),\n },\n });\n review = outcome.review ?? null;\n\n if (outcome.code !== 0) {\n printError('The consolidated Pull Request could not be created.');\n await saveExecutionPlan(planFile, current);\n return outcome.code;\n }\n\n const pullRequest = await propagatePullRequest(current);\n if (pullRequest !== null) {\n current = setQueuePullRequest(current, pullRequest);\n }\n }\n\n await saveExecutionPlan(planFile, current);\n\n // A review asking for changes leaves every issue open, exactly as it does for\n // a single-issue run: the work is proposed, not accepted.\n if (review?.requestedChanges === true) {\n printInfo('Issues left open until the review blockers are addressed.');\n } else {\n for (const entry of current.issues) {\n await closeIssue(entry.id, entry.source);\n }\n }\n\n printQueueSummary({\n queueId: current.id,\n branchName: current.branchName,\n issues: summaries,\n excluded: current.excluded.map((entry) => ({ id: entry.id, title: entry.title })),\n elapsedSeconds: Math.round((Date.now() - startedAtMs) / 1000),\n prUrl: current.pullRequest?.url ?? null,\n // Read here, after the consolidated Pull Request (and the optional\n // pr-review) already ran: reading it before would report a queue total\n // that leaves out the phase that just spent tokens.\n usage: queueUsage.totals(),\n prReview: review,\n });\n\n return 0;\n}\n\n/**\n * Whether the `pr` phase already ran for this queue, even if it could not\n * report a URL.\n *\n * `plan.pullRequest` on the queue is the happy path; this is the safety net for\n * the case where the phase succeeded but no URL could be parsed from its output\n * — re-running it on a resume would open a second Pull Request for the same\n * branch, which is exactly what the whole feature exists to avoid.\n */\nasync function primaryPrCreated(plan: ExecutionPlan): Promise<boolean> {\n try {\n const paths = await resolveIssuePaths(plan.id);\n return (await loadTaskPlan(paths.tasksFile)).pipeline.prCreated;\n } catch {\n return false;\n }\n}\n\n/**\n * The context the consolidated Pull Request needs: what was implemented, in\n * which order, and what is knowingly left pending.\n *\n * \"Pending\" is the honest half of the report — the issues the user chose not to\n * run, and any review findings still recorded on an issue's plan. Both would\n * otherwise be invisible to whoever reviews the Pull Request.\n */\nasync function buildPrQueueContext(plan: ExecutionPlan): Promise<PrQueueContext> {\n const pending: string[] = [];\n\n for (const entry of plan.issues) {\n try {\n const paths = await resolveIssuePaths(entry.id);\n const taskPlan = await loadTaskPlan(paths.tasksFile);\n if (taskPlan.lastReviewFindings !== null && taskPlan.lastReviewFindings !== '') {\n pending.push(`Issue #${entry.id} has unresolved review findings`);\n }\n } catch {\n // No plan on disk for this issue: nothing to report about it.\n }\n }\n\n return {\n issues: plan.issues.map((entry) => ({\n id: entry.id,\n number: entry.number,\n title: entry.title,\n url: entry.url,\n source: entry.source,\n })),\n excluded: plan.excluded.map((entry) => ({\n id: entry.id,\n number: entry.number,\n title: entry.title,\n reason: entry.reason,\n })),\n pending,\n };\n}\n\n/**\n * Copy the Pull Request the `pr` phase recorded on the primary issue's plan\n * onto every other issue of the queue.\n *\n * The queue's `execution-plan.json` is the source of truth, but `pr-review`\n * discovers a Pull Request from `plan.pullRequest` in `tasks.json`\n * (`core/pr-review/discovery.ts`), so replicating it is what keeps\n * `pr-review --issue <any issue of the queue>` working without a special case.\n * A write that fails is reported and skipped: the Pull Request exists either\n * way, and the queue file already records it.\n */\nasync function propagatePullRequest(plan: ExecutionPlan): Promise<PullRequestRef | null> {\n let pullRequest: PullRequestRef | null = null;\n try {\n const primaryPaths = await resolveIssuePaths(plan.id);\n pullRequest = (await loadTaskPlan(primaryPaths.tasksFile)).pullRequest ?? null;\n } catch {\n return null;\n }\n if (pullRequest === null) return null;\n\n for (const entry of plan.issues) {\n if (entry.id === plan.id) continue;\n try {\n const paths = await resolveIssuePaths(entry.id);\n const taskPlan = await loadTaskPlan(paths.tasksFile);\n taskPlan.pullRequest = pullRequest;\n taskPlan.pipeline.prCreated = true;\n await saveTaskPlan(paths.tasksFile, taskPlan);\n } catch {\n printWarning(`Could not record the Pull Request on issue #${entry.id}'s task plan.`);\n }\n }\n\n return pullRequest;\n}\n\n/** An {@link IssueRunResult} carrying nothing but an exit code. */\nfunction failure(code: number): IssueRunResult {\n return { code, failedPhase: null, branchName: null, storyCount: 0, elapsedSeconds: 0 };\n}\n\n/**\n * Close an Issue through whoever owns it.\n *\n * A provider without `close()` (a read-only origin) has nothing to do here, so\n * the step is simply skipped, and a failure is a warning: the work is done\n * either way, and an Issue left open is a nuisance, not a broken pipeline.\n */\nasync function closeIssue(issueNumber: string, source: IssueSource): Promise<void> {\n try {\n const provider = getProvider(source);\n if (provider.close !== undefined) {\n printInfo('Closing issue...');\n await provider.close(issueNumber);\n }\n } catch {\n printWarning('Failed to close issue automatically');\n }\n}\n\n/**\n * Make an issue's task plan use the queue's branch.\n *\n * With no branch decided yet, whatever the `plan` phase produced becomes the\n * queue's branch (the first issue names it, from its own title). From then on\n * the value is written over every later issue's plan, so `execute.md`'s\n * \"check it out or create from main\" finds the branch already there.\n */\nasync function adoptQueueBranch(\n tasksPath: string,\n queueBranch: string | null,\n): Promise<string | null> {\n try {\n const plan = await loadTaskPlan(tasksPath);\n if (queueBranch === null || queueBranch === '') {\n return plan.branchName === '' ? null : plan.branchName;\n }\n if (plan.branchName !== queueBranch) {\n plan.branchName = queueBranch;\n await saveTaskPlan(tasksPath, plan);\n }\n return queueBranch;\n } catch {\n // No tasks.json (the plan phase failed to write one): the queue keeps the\n // branch it already had, and the phase failure is reported on its own.\n return queueBranch;\n }\n}\n","import type { TaskPlan, UserStory } from '../types.js';\nimport { parseJournal } from './journal.js';\n\n/**\n * \"This issue is too large\" — detected, reported, and left to a person.\n *\n * A phase that keeps timing out, a plan with thirty stories, five iterations in\n * a row that finish nothing: each of those is ambiguous on its own, and any one\n * of them can also be a slow afternoon. Together they are the same thing said\n * three ways, and what they are saying is that the demand was never one issue.\n *\n * The tool's job here stops at saying so. **Splitting an issue is a product\n * decision**, and the default is a report plus `blocked`, not an act — which is\n * also why a failure that is merely *infrastructural* must never reach this\n * code path: an issue whose run died because the network went down is not too\n * large, and breaking it up would be the worst possible response.\n */\n\n/** The thresholds, in one place, because every one of them is a judgement. */\nexport const DECOMPOSITION_THRESHOLDS = {\n /** Timeouts on the same phase. Two is already a pattern, not bad luck. */\n timeoutsPerPhase: 2,\n /** Stories in the plan. Past this, the plan is a backlog. */\n stories: 15,\n /** Iterations in a row that completed no story. */\n barrenIterations: 5,\n /** Files touched on the branch. */\n filesTouched: 40,\n /** Characters in the issue body. */\n issueBodyChars: 20_000,\n} as const;\n\n/** How many signals have to agree before the report is written. */\nexport const DECOMPOSITION_MIN_SIGNALS = 2;\n\nexport interface DecompositionInput {\n /** Contents of `events.jsonl` (both generations), oldest first. */\n journal?: string;\n plan?: TaskPlan | null;\n /** Body of the issue as the provider resolved it. */\n issueBody?: string;\n /** Files changed on the branch, from `git diff --name-only`. */\n filesTouched?: number;\n /** Whether the execute loop stopped because it ran out of iterations. */\n hitMaxIterations?: boolean;\n}\n\nexport interface DecompositionSignal {\n id: string;\n /** One line a person can act on, naming the number that crossed the line. */\n detail: string;\n}\n\nexport interface DecompositionAssessment {\n signals: DecompositionSignal[];\n /** Two or more signals agreeing. */\n oversized: boolean;\n}\n\n/* ── the signals ────────────────────────────────────────────────────────── */\n\n/** Timeouts per phase, read off the journal. */\nfunction timeoutsByPhase(journal: string): Map<string, number> {\n const counts = new Map<string, number>();\n let phase = '(unknown)';\n\n for (const { event } of parseJournal(journal)) {\n if (event.type === 'phase:start') {\n phase = event.phase;\n continue;\n }\n if (event.type !== 'retry') continue;\n const kind = (event as { kind?: string }).kind;\n const reason = (event as { reason?: string }).reason ?? '';\n const timedOut = kind === 'timeout' || kind === 'stalled' || /timed out|stalled/i.test(reason);\n if (!timedOut) continue;\n counts.set(phase, (counts.get(phase) ?? 0) + 1);\n }\n return counts;\n}\n\n/**\n * The longest run of iterations that completed no story.\n *\n * An iteration that finishes nothing is not a failure — the agent may be\n * halfway through a story — but five of them in a row means the unit of work is\n * bigger than the loop was designed for.\n */\nfunction longestBarrenRun(journal: string): number {\n let passing = 0;\n let current = 0;\n let longest = 0;\n\n for (const { event } of parseJournal(journal)) {\n if (event.type === 'iteration:start') {\n current++;\n longest = Math.max(longest, current);\n continue;\n }\n if (event.type !== 'stories:update') continue;\n const nowPassing = event.stories.filter((story) => story.passes).length;\n if (nowPassing > passing) current = 0;\n passing = Math.max(passing, nowPassing);\n }\n return longest;\n}\n\n/**\n * Weigh the evidence.\n *\n * Every signal is a number crossing a documented line, and the report quotes\n * that number: \"this is too big\" is not an argument, and \"the `prd` phase timed\n * out three times\" is.\n */\nexport function assessDecomposition(input: DecompositionInput): DecompositionAssessment {\n const signals: DecompositionSignal[] = [];\n const journal = input.journal ?? '';\n\n for (const [phase, count] of timeoutsByPhase(journal)) {\n if (count >= DECOMPOSITION_THRESHOLDS.timeoutsPerPhase) {\n signals.push({\n id: 'repeated-timeouts',\n detail: `The '${phase}' phase timed out ${count} times (threshold: ${DECOMPOSITION_THRESHOLDS.timeoutsPerPhase}).`,\n });\n }\n }\n\n const stories = input.plan?.userStories.length ?? 0;\n if (stories > DECOMPOSITION_THRESHOLDS.stories) {\n signals.push({\n id: 'story-count',\n detail: `The plan has ${stories} user stories (threshold: ${DECOMPOSITION_THRESHOLDS.stories}).`,\n });\n }\n\n const barren = longestBarrenRun(journal);\n if (barren >= DECOMPOSITION_THRESHOLDS.barrenIterations) {\n signals.push({\n id: 'barren-iterations',\n detail: `${barren} iterations in a row completed no story (threshold: ${DECOMPOSITION_THRESHOLDS.barrenIterations}).`,\n });\n }\n\n const files = input.filesTouched ?? 0;\n if (files > DECOMPOSITION_THRESHOLDS.filesTouched) {\n signals.push({\n id: 'files-touched',\n detail: `${files} files were touched on this branch (threshold: ${DECOMPOSITION_THRESHOLDS.filesTouched}).`,\n });\n }\n\n const body = input.issueBody?.length ?? 0;\n if (body > DECOMPOSITION_THRESHOLDS.issueBodyChars) {\n signals.push({\n id: 'issue-size',\n detail: `The issue body is ${body} characters (threshold: ${DECOMPOSITION_THRESHOLDS.issueBodyChars}).`,\n });\n }\n\n if (input.hitMaxIterations === true) {\n signals.push({\n id: 'max-iterations',\n detail: 'The execute loop stopped because it ran out of iterations, not because it finished.',\n });\n }\n\n return { signals, oversized: signals.length >= DECOMPOSITION_MIN_SIGNALS };\n}\n\n/* ── the report ─────────────────────────────────────────────────────────── */\n\n/** Stories per proposed sub-issue. Small enough to finish in one run. */\nconst STORIES_PER_SUBISSUE = 5;\n\nexport interface ProposedSubIssue {\n title: string;\n stories: UserStory[];\n /** Titles of the sub-issues this one has to wait for. */\n dependsOn: string[];\n}\n\n/**\n * Cut the remaining work into pieces, in priority order.\n *\n * Priority order matters twice: it is the order the execute loop already works\n * in, and it makes each piece depend only on the one before it — which is the\n * only dependency shape that can be derived without understanding the code.\n * Anything cleverer would be a guess dressed as a plan.\n */\nexport function proposeSubIssues(plan: TaskPlan | null | undefined): ProposedSubIssue[] {\n const pending = [...(plan?.userStories ?? [])]\n .filter((story) => !story.passes)\n .sort((a, b) => a.priority - b.priority);\n if (pending.length === 0) return [];\n\n const chunks: ProposedSubIssue[] = [];\n for (let index = 0; index < pending.length; index += STORIES_PER_SUBISSUE) {\n const stories = pending.slice(index, index + STORIES_PER_SUBISSUE);\n const first = stories[0];\n const last = stories[stories.length - 1];\n const range =\n first === undefined || last === undefined || first.id === last.id\n ? (first?.id ?? '')\n : `${first.id}–${last.id}`;\n chunks.push({\n title: `${plan?.description ?? 'Issue'} — part ${chunks.length + 1} (${range})`,\n stories,\n dependsOn: chunks.length === 0 ? [] : [chunks[chunks.length - 1]?.title ?? ''],\n });\n }\n return chunks;\n}\n\nexport interface DecompositionReportInput {\n issueNumber: string;\n assessment: DecompositionAssessment;\n plan: TaskPlan | null | undefined;\n at: string;\n}\n\n/**\n * The report itself: what was detected, what is proposed, and what to do.\n *\n * Written for a person to disagree with. It states each signal with its number,\n * proposes a cut it can justify, and stops — the decision is not the tool's.\n */\nexport function buildDecompositionReport(input: DecompositionReportInput): string {\n const { issueNumber, assessment, plan, at } = input;\n const proposals = proposeSubIssues(plan);\n const lines: string[] = [];\n\n lines.push(`# Issue #${issueNumber} looks larger than one run`, '');\n lines.push(`Detected at ${at}.`, '');\n lines.push(\n 'This is a report, not an action. Nothing has been split, and nothing will be',\n 'without you asking for it.',\n '',\n );\n\n lines.push('## What was detected', '');\n for (const signal of assessment.signals) {\n lines.push(`- **${signal.id}** — ${signal.detail}`);\n }\n lines.push('');\n\n if (proposals.length === 0) {\n lines.push('## Proposed split', '');\n lines.push(\n 'No pending user stories were found, so there is nothing to cut here.',\n 'The signals above are about the run rather than about the plan.',\n '',\n );\n return `${lines.join('\\n')}\\n`;\n }\n\n lines.push('## Proposed split', '');\n lines.push(\n `The ${proposals.reduce((total, proposal) => total + proposal.stories.length, 0)} pending` +\n ` stories, in priority order, cut into ${proposals.length} sub-issues of at most` +\n ` ${STORIES_PER_SUBISSUE}. Each depends on the one before it, which is the only`,\n 'dependency shape that can be derived from the plan alone — read it as a starting',\n 'point, not as an answer.',\n '',\n );\n\n for (const proposal of proposals) {\n lines.push(`### ${proposal.title}`, '');\n if (proposal.dependsOn.length > 0) {\n lines.push(`Depends on: ${proposal.dependsOn.join(', ')}`, '');\n }\n for (const story of proposal.stories) {\n lines.push(`- \\`${story.id}\\` ${story.title}`);\n }\n lines.push('');\n }\n\n lines.push('## What to do next', '');\n lines.push(\n '- Adjust the cut above until it matches how you would actually split the work;',\n '- create the sub-issues (`issue-flow generate \"…\"`) and run them as a queue;',\n '- or decide the issue is fine as it is and re-run it — nothing here blocks that,',\n ' beyond the report you are reading.',\n '',\n );\n\n return `${lines.join('\\n')}\\n`;\n}\n","import { createInterface } from 'node:readline';\nimport { printInfo, printWarning } from '../ui/logger.js';\nimport type { ExecutionPlan, ExecutionPlanIssue } from './types.js';\n\n/**\n * The one place a multi-issue run stops and asks.\n *\n * Discovery may well turn `issue-flow run 50` into four Issues; implementing\n * them without saying so would be the worst possible outcome of this feature.\n * So the pipeline halts **before** any phase runs, prints what it found and\n * lets the user pick the scope.\n */\n\n/** What the user decided about the discovered hierarchy. */\nexport type QueueChoice = 'requested' | 'all' | 'cancel';\n\n/** How many invalid answers are tolerated before the prompt gives up. */\nconst MAX_PROMPT_ATTEMPTS = 3;\n\n/** A confirmation that cannot be asked for and was not answered by a flag. */\nexport class QueueConfirmationError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = 'QueueConfirmationError';\n this.exitCode = exitCode;\n }\n}\n\nexport interface ConfirmQueueOptions {\n /** `--yes`: accept the suggested plan (the whole hierarchy) without asking. */\n yes?: boolean;\n /** `--only`: run just the requested Issues, without asking. */\n only?: boolean;\n /**\n * Whether a prompt may be shown. Defaults to a real TTY on both ends outside\n * CI, mirroring `pr-review`'s discovery confirmation.\n */\n interactive?: boolean;\n /**\n * Whether the user asked for a single Issue. Only the hierarchy around it is\n * up for confirmation, so a non-interactive run falls back to that Issue\n * alone instead of failing: running exactly what was asked for can never\n * implement something nobody approved.\n */\n singleRequest?: boolean;\n stdin?: NodeJS.ReadableStream;\n stdout?: NodeJS.WritableStream;\n info?: (message: string) => void;\n warn?: (message: string) => void;\n}\n\nfunction isInteractiveByDefault(): boolean {\n const ci = process.env.CI;\n const inCi = ci !== undefined && ci !== '' && ci !== '0' && ci.toLowerCase() !== 'false';\n return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && !inCi;\n}\n\n/** `#50` for a numeric identifier, `auth-refactor` for anything else. */\nexport function issueLabel(entry: { id: string; number: number | null }): string {\n return entry.number === null ? entry.id : `#${entry.number}`;\n}\n\n/** Why this Issue is in the queue, in a few words. */\nfunction reasonFor(entry: ExecutionPlanIssue): string {\n const parts: string[] = [];\n if (entry.origin === 'requested') {\n parts.push('requested');\n }\n if (entry.dependsOn.length > 0) {\n parts.push(`after ${entry.dependsOn.map((id) => `#${id}`).join(', ')}`);\n }\n if (entry.parent !== null) {\n parts.push(`sub-issue of #${entry.parent}`);\n }\n if (entry.priority !== null) {\n parts.push(entry.priority);\n }\n return parts.join(', ');\n}\n\n/**\n * The summary printed before the prompt.\n *\n * Returned as lines rather than printed so the shape is testable without\n * capturing stdout — the same split `buildRunSummaryLines` uses.\n */\nexport function buildQueueSummaryLines(plan: ExecutionPlan): string[] {\n const primary = plan.issues.find((entry) => entry.id === plan.id) ?? plan.issues[0];\n const lines: string[] = [];\n\n if (primary !== undefined) {\n lines.push(` Main issue: ${issueLabel(primary)}${primary.title ? ` ${primary.title}` : ''}`);\n }\n lines.push(` Total issues: ${plan.issues.length}`);\n lines.push(' Suggested order:');\n\n for (const entry of plan.issues) {\n const reason = reasonFor(entry);\n const flag = entry.heuristic ? ' ~' : '';\n const title = entry.title === '' ? '' : ` ${entry.title}`;\n lines.push(\n ` ${entry.position}. ${issueLabel(entry)}${title}${reason === '' ? '' : ` (${reason})`}${flag}`,\n );\n }\n\n if (plan.issues.some((entry) => entry.heuristic)) {\n lines.push(' ~ relation found only in the issue text, which may be a false positive');\n }\n if (plan.truncated) {\n lines.push(' Note: discovery hit its limit — the hierarchy may be larger than shown');\n }\n\n return lines;\n}\n\n/**\n * Ask, in an interactive terminal, which scope to run.\n *\n * Outside a TTY the answer must come from a flag: `--yes` for the whole\n * hierarchy, `--only` for just what was asked for. Neither is a *default* —\n * silently picking one would either implement Issues nobody approved or ignore\n * a dependency the user was never told about. The command fails instead, which\n * is what keeps an unattended CI run honest.\n */\nexport async function confirmQueue(\n plan: ExecutionPlan,\n requestedOnlyCount: number,\n options: ConfirmQueueOptions = {},\n): Promise<QueueChoice> {\n const info = options.info ?? printInfo;\n const warn = options.warn ?? printWarning;\n\n info(`Issue ${plan.requested.map((id) => `#${id}`).join(', ')} is part of a larger structure:`);\n for (const line of buildQueueSummaryLines(plan)) {\n (options.stdout ?? process.stdout).write(`${line}\\n`);\n }\n\n if (options.only === true) {\n info(`--only: running just the ${requestedOnlyCount} issue(s) you asked for.`);\n return 'requested';\n }\n if (options.yes === true) {\n info(`--yes: running the whole hierarchy (${plan.issues.length} issues).`);\n return 'all';\n }\n\n const interactive = options.interactive ?? isInteractiveByDefault();\n if (!interactive && options.singleRequest === true) {\n warn(\n 'A larger structure was found, but the terminal is not interactive: running just ' +\n `issue ${plan.requested.map((id) => `#${id}`).join(', ')}. ` +\n 'Re-run with --yes to execute the whole hierarchy.',\n );\n return 'requested';\n }\n if (!interactive) {\n throw new QueueConfirmationError(\n 'This run involves more than one issue and the terminal is not interactive. ' +\n 'Re-run with --yes to execute the whole hierarchy, or --only to execute just the ' +\n 'issues you informed.',\n );\n }\n\n const query =\n `Which scope should run? [1] Only the issues informed (${requestedOnlyCount}) ` +\n `[2] The whole hierarchy (${plan.issues.length}) [3] Cancel: `;\n\n return await ask(query, options.stdin ?? process.stdin, options.stdout ?? process.stdout, warn);\n}\n\n/**\n * Numbered prompt, sharing the queued-lines discipline of `issues/resolver.ts`:\n * `rl.question` drops input that arrives before the prompt is written, which\n * silently loses a piped answer. EOF answers `cancel` — never consent.\n */\nasync function ask(\n query: string,\n stdin: NodeJS.ReadableStream,\n stdout: NodeJS.WritableStream,\n warn: (message: string) => void,\n): Promise<QueueChoice> {\n const rl = createInterface({ input: stdin, output: stdout });\n const queued: string[] = [];\n let pending: ((line: string | null) => void) | null = null;\n let closed = false;\n\n rl.on('line', (line: string) => {\n if (pending !== null) {\n const resolve = pending;\n pending = null;\n resolve(line);\n return;\n }\n queued.push(line);\n });\n rl.on('close', () => {\n closed = true;\n if (pending !== null) {\n const resolve = pending;\n pending = null;\n resolve(null);\n }\n });\n\n const read = (): Promise<string | null> =>\n new Promise<string | null>((resolve) => {\n const buffered = queued.shift();\n if (buffered !== undefined) {\n resolve(buffered);\n return;\n }\n if (closed) {\n resolve(null);\n return;\n }\n stdout.write(query);\n pending = resolve;\n });\n\n try {\n for (let attempt = 0; attempt < MAX_PROMPT_ATTEMPTS; attempt++) {\n const answer = await read();\n if (answer === null) {\n return 'cancel';\n }\n const choice = answer.trim();\n if (choice === '1') return 'requested';\n // An empty answer accepts the suggestion, which is the whole point of\n // having computed an order: the same convention as `pr-review`'s (Y/n).\n if (choice === '' || choice === '2') return 'all';\n if (choice === '3') return 'cancel';\n warn(`Invalid choice: '${choice}'. Enter 1, 2 or 3.`);\n }\n return 'cancel';\n } finally {\n rl.close();\n }\n}\n","import {\n type BuildDependencyGraphOptions,\n buildDependencyGraph,\n type DependencyGraph,\n} from '../issues/graph.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueRelations, IssueSource } from '../issues/types.js';\n\n/**\n * Bridge between the Issue providers and the dependency graph.\n *\n * Discovery is a *best-effort enrichment* of a run, never a prerequisite: an\n * origin with no `fetchRelations`, an unregistered provider or a failing API all\n * degrade to \"this issue relates to nothing\", which is exactly the single-issue\n * pipeline that existed before. That is what makes the feature additive.\n */\n\nexport interface DiscoverGraphOptions extends BuildDependencyGraphOptions {\n /** Origin to ask about relations. Defaults to the first known Issue's source. */\n source?: IssueSource;\n}\n\n/** Whether an origin can answer questions about related Issues at all. */\nexport function supportsRelations(source: IssueSource): boolean {\n try {\n return typeof getProvider(source).fetchRelations === 'function';\n } catch {\n return false;\n }\n}\n\n/**\n * Walk the hierarchy of `roots` through the provider of `source`.\n *\n * Discovered Issues are read from the same origin: a queue is resolved against\n * one provider, so an id found in a GitHub hierarchy is a GitHub Issue. The\n * Issues the caller already resolved are passed through `known`, so the roots\n * never cost a second round-trip.\n */\nexport async function discoverIssueGraph(\n roots: readonly string[],\n options: DiscoverGraphOptions = {},\n): Promise<DependencyGraph> {\n const source = options.source ?? [...(options.known ?? [])][0]?.source ?? 'github';\n\n let fetchRelations: ((id: string) => Promise<IssueRelations | null>) | null = null;\n let fetchIssue: ((id: string) => Promise<Issue | null>) | null = null;\n try {\n const provider = getProvider(source);\n const relations = provider.fetchRelations?.bind(provider);\n if (relations !== undefined) {\n fetchRelations = (id) => relations(id);\n fetchIssue = (id) => provider.get(id);\n }\n } catch {\n // Unknown origin: nothing to discover, and the run continues single-issue.\n }\n\n if (fetchRelations === null) {\n return buildDependencyGraph(roots, async () => null, { ...options, fetchIssue: undefined });\n }\n\n return buildDependencyGraph(roots, fetchRelations, {\n ...options,\n fetchIssue: options.fetchIssue ?? fetchIssue ?? undefined,\n });\n}\n","import type { Issue, IssueSource } from '../issues/types.js';\nimport { printInfo, printWarning } from '../ui/logger.js';\nimport { type ConfirmQueueOptions, confirmQueue, QueueConfirmationError } from './confirm.js';\nimport { discoverIssueGraph, supportsRelations } from './discovery.js';\nimport { computeExecutionOrder, describeCycles } from './order.js';\nimport {\n buildExecutionPlan,\n isQueueComplete,\n loadExecutionPlan,\n saveExecutionPlan,\n} from './plan.js';\nimport type { ExecutionPlan } from './types.js';\n\n/**\n * Deciding, before any phase runs, whether this invocation is a single-issue\n * pipeline or a queue — and, when it is a queue, what exactly it will run.\n *\n * Everything here happens *before* the session starts, so a run that turns out\n * to be a plain single-issue pipeline is indistinguishable from what the CLI\n * did before this feature existed: no prompt, no artifact, no extra output.\n */\n\nexport type QueueDecision =\n /** Nothing to coordinate: run the single issue exactly as before. */\n | { kind: 'single' }\n | { kind: 'queue'; plan: ExecutionPlan; resumed: boolean }\n /** The run must stop; `code` is what the CLI returns. */\n | { kind: 'stop'; code: number };\n\nexport interface PlanQueueInput {\n /** Identifiers the user asked for, in order. */\n requested: string[];\n /** Origin the run resolved, used to ask the right provider about relations. */\n source: IssueSource;\n /** Issues already resolved by the caller, so the roots are not re-read. */\n known: Issue[];\n projectId: string;\n /** Where the queue's `execution-plan.json` lives. */\n planFile: string;\n noBranch: boolean;\n prReview: boolean;\n /** `--yes` / `--only`, forwarded to the confirmation. */\n confirm?: ConfirmQueueOptions;\n info?: (message: string) => void;\n warn?: (message: string) => void;\n}\n\n/** A persisted queue that matches this invocation, or `null`. */\nasync function loadResumableQueue(\n planFile: string,\n requested: readonly string[],\n): Promise<ExecutionPlan | null> {\n let plan: ExecutionPlan;\n try {\n plan = await loadExecutionPlan(planFile);\n } catch {\n // Absent (the common case) or unreadable: plan the queue from scratch. A\n // corrupted file must not strand the user — the plan is derived state.\n return null;\n }\n\n // The stored queue answers a different question than the one being asked.\n const sameRequest =\n plan.requested.length === requested.length &&\n plan.requested.every((id, index) => id === requested[index]);\n\n return sameRequest ? plan : null;\n}\n\n/**\n * Work out what this invocation runs.\n *\n * The order of the checks is what keeps the feature additive:\n *\n * 1. an unfinished queue for the very same request is resumed, never\n * re-confirmed — the user already answered that question;\n * 2. `--only` with a single issue skips discovery altogether, so an automation\n * that knows what it wants pays nothing for the feature;\n * 3. discovery runs; if it finds nothing beyond the requested issue, the run\n * stays single-issue and no artifact is created;\n * 4. only a genuinely larger structure reaches the confirmation.\n */\nexport async function planQueue(input: PlanQueueInput): Promise<QueueDecision> {\n const info = input.info ?? printInfo;\n const warn = input.warn ?? printWarning;\n const single = input.requested.length === 1;\n\n const resumable = await loadResumableQueue(input.planFile, input.requested);\n // A queue that already finished has nothing left to run, and re-planning it\n // would overwrite its `execution-plan.json` — losing the Pull Request and\n // the record of what ran. Report it and stop successfully instead.\n if (resumable !== null && isQueueComplete(resumable)) {\n const pr = resumable.pullRequest?.url;\n info(\n `The execution queue of issue #${resumable.id} is already complete ` +\n `(${resumable.issues.length} issues)${pr === undefined ? '' : ` — ${pr}`}. Nothing to run.`,\n );\n return { kind: 'stop', code: 0 };\n }\n if (resumable !== null) {\n const done = resumable.issues.filter((entry) => entry.status === 'completed').length;\n info(\n `Resuming the execution queue of issue #${resumable.id}: ` +\n `${done}/${resumable.issues.length} issues already completed.`,\n );\n return { kind: 'queue', plan: resumable, resumed: true };\n }\n\n // `--only` on a single issue is the explicit \"do not look around\" answer.\n if (single && input.confirm?.only === true) {\n return { kind: 'single' };\n }\n\n if (single && !supportsRelations(input.source)) {\n // An origin with no notion of related Issues (the local provider) can only\n // ever produce a single-issue run.\n return { kind: 'single' };\n }\n\n const graph = await discoverIssueGraph(input.requested, {\n source: input.source,\n known: input.known,\n warn,\n });\n\n if (single && graph.nodes.size <= 1) {\n return { kind: 'single' };\n }\n\n const suggested = computeExecutionOrder(graph);\n if (!suggested.ok) {\n // A cycle anywhere in the discovered hierarchy must not take down a run\n // the user asked for on a single issue: they never opted into the\n // hierarchy, so degrade to the pipeline that has always worked and say\n // why. Only an explicitly multi-issue request is refused outright.\n if (single) {\n warn(\n `Dependency cycle between issues: ${describeCycles(suggested.cycles)}. ` +\n 'Running just the issue you informed; fix the dependencies on GitHub to run the hierarchy.',\n );\n return { kind: 'single' };\n }\n warn(\n `Dependency cycle between issues: ${describeCycles(suggested.cycles)}. ` +\n 'Fix the dependencies on GitHub, or re-run with --only to execute just the issues you informed.',\n );\n return { kind: 'stop', code: 1 };\n }\n\n const requestedOrder = computeExecutionOrder(graph, { include: input.requested });\n const requestedCount = requestedOrder.ok ? requestedOrder.order.length : input.requested.length;\n\n const suggestedPlan = buildExecutionPlan({\n projectId: input.projectId,\n requested: input.requested,\n graph,\n order: suggested.order,\n noBranch: input.noBranch,\n prReview: input.prReview,\n });\n\n let choice: 'all' | 'requested' | 'cancel';\n try {\n choice = await confirmQueue(suggestedPlan, requestedCount, {\n ...(input.confirm ?? {}),\n singleRequest: single,\n });\n } catch (err) {\n if (err instanceof QueueConfirmationError) {\n warn(err.message);\n return { kind: 'stop', code: err.exitCode };\n }\n throw err;\n }\n\n if (choice === 'cancel') {\n info('Cancelled: nothing was executed.');\n return { kind: 'stop', code: 1 };\n }\n\n let plan = suggestedPlan;\n if (choice === 'requested') {\n if (!requestedOrder.ok) {\n warn(\n `Dependency cycle between the issues you informed: ${describeCycles(requestedOrder.cycles)}.`,\n );\n return { kind: 'stop', code: 1 };\n }\n // A single issue after trimming is not a queue at all: fall back to the\n // untouched single-issue pipeline instead of creating an artifact for it.\n // But that fallback only makes sense when one issue was requested — with\n // several, trimming to one means the others never made it into the graph\n // (unreadable issue, node limit), and silently running a subset of what\n // the user asked for is worse than stopping.\n if (requestedOrder.order.length <= 1) {\n if (input.requested.length > 1) {\n const missing = input.requested.filter((id) => !requestedOrder.order.includes(id));\n warn(\n `Only issue #${requestedOrder.order[0] ?? '—'} of the ones you informed could be read; ` +\n `${missing.map((id) => `#${id}`).join(', ')} did not make it into the dependency graph. ` +\n 'Nothing was executed — check the identifiers and try again.',\n );\n return { kind: 'stop', code: 1 };\n }\n return { kind: 'single' };\n }\n plan = buildExecutionPlan({\n projectId: input.projectId,\n requested: input.requested,\n graph,\n order: requestedOrder.order,\n noBranch: input.noBranch,\n prReview: input.prReview,\n });\n }\n\n await saveExecutionPlan(input.planFile, plan);\n return { kind: 'queue', plan, resumed: false };\n}\n","/**\n * Parsing of the issue identifiers a command receives on the command line.\n *\n * `run` accepts both spellings of the same list — `42,43,50` and `42 43 50` —\n * because both are what a user naturally types, and commander gives them to us\n * as different shapes (one argument with commas, or several arguments). Normal-\n * izing here keeps every downstream layer working on a plain `string[]`.\n */\n\n/** Malformed issue arguments, reported as a CLI error. */\nexport class IssueArgumentError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueArgumentError';\n }\n}\n\n/**\n * Rejected before an identifier ever becomes a path segment or an API call.\n * Mirrors `normalizeIssueNumber` in `storage/paths.ts`: the storage layer would\n * refuse these anyway, and failing here names the offending argument instead.\n */\nconst INVALID_ID = /[/\\\\\\s]/;\n\n/**\n * Normalize the positional arguments of `run` into a list of issue identifiers.\n *\n * - splits on commas, so `42,43` and `42 43` produce the same list;\n * - strips a leading `#` and surrounding whitespace;\n * - preserves the order the user typed, which is what a tie-break falls back to;\n * - drops exact duplicates silently (`42 42` is a typo with an obvious intent),\n * but rejects an empty or malformed identifier, which is not.\n *\n * @throws IssueArgumentError when no identifier survives or one is malformed.\n */\nexport function parseIssueArguments(values: readonly string[]): string[] {\n const ids: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n for (const raw of value.split(',')) {\n const id = raw.trim().replace(/^#/, '').trim();\n if (id === '') {\n // An empty slot is always a typo: `42,,43` or a stray comma.\n if (raw.trim() !== '' || value.includes(',')) {\n throw new IssueArgumentError(\n `Invalid issue list '${value}': it has an empty entry. Use '42,43' or '42 43'.`,\n );\n }\n continue;\n }\n if (INVALID_ID.test(id) || id === '.' || id === '..') {\n throw new IssueArgumentError(`Invalid issue identifier '${raw.trim()}'.`);\n }\n if (seen.has(id)) continue;\n seen.add(id);\n ids.push(id);\n }\n }\n\n if (ids.length === 0) {\n throw new IssueArgumentError('No issue was informed. Pass at least one issue number.');\n }\n\n return ids;\n}\n","import { Listr, type ListrTask, PRESET_TIMER, PRESET_TIMESTAMP } from 'listr2';\nimport type { PipelinePhase } from '../core/pipeline.js';\nimport { loadTaskPlan } from '../core/state-manager.js';\nimport {\n setOutputCallback,\n setStoryStageCallback,\n setStoryUpdateCallback,\n} from '../core/verbose.js';\nimport type { UserStory } from '../types.js';\n\n/** Minimal interface for the listr2 task wrapper properties we use. */\ninterface TaskContext {\n title: string;\n output: string;\n newListr(tasks: ListrTask[], options?: Record<string, unknown>): Listr;\n}\n\n/**\n * Result returned after the pipeline renderer finishes.\n */\nexport interface PipelineResult {\n success: boolean;\n failedPhase?: string;\n overallElapsedSeconds: number;\n}\n\n/**\n * Options for runPipelineWithRenderer.\n */\nexport interface PipelineRendererOptions {\n /** All pipeline phases in order */\n phases: PipelinePhase[];\n /** Index of the first phase to run (phases before this are skipped) */\n startIndex: number;\n /** Whether verbose mode is enabled */\n verbose: boolean;\n /** Map of phase name to its async runner function */\n runners: Record<string, () => Promise<void>>;\n /** Path to tasks.json — enables execute-phase subtask progress when set */\n tasksPath?: string;\n}\n\n/**\n * Phase labels for display.\n *\n * Typed as `Record<string, string>` on purpose (phases arrive as plain strings\n * here), which also means adding a phase to `PipelinePhase` does *not* break\n * the build when its label is missing — it silently falls back to the\n * capitalized phase name. Hence `phaseLabel()` below being covered by a test.\n */\nconst PHASE_LABELS: Record<string, string> = {\n prd: 'PRD',\n plan: 'Plan',\n execute: 'Execute',\n review: 'Review',\n pr: 'PR',\n 'pr-review': 'PR Review',\n};\n\n/**\n * Display label for a pipeline phase, falling back to the capitalized phase\n * name for anything not in the table.\n */\nexport function phaseLabel(phase: string): string {\n return PHASE_LABELS[phase] ?? phase.charAt(0).toUpperCase() + phase.slice(1);\n}\n\n/**\n * Select the appropriate listr2 renderer based on environment.\n *\n * - TTY + not verbose -> 'default' (animated spinners)\n * - TTY + verbose -> 'verbose' (sequential lines)\n * - Non-TTY / CI -> 'simple' (plain timestamped output)\n *\n * NO_COLOR is handled by listr2's own color support and the useColor() utility,\n * not by switching renderers — a TTY with NO_COLOR still benefits from the\n * default renderer's layout and spinners.\n */\nfunction selectRenderer(verbose: boolean): 'default' | 'verbose' | 'simple' {\n const isTTY = !!process.stdout.isTTY;\n const isCI = !!process.env.CI;\n\n if (!isTTY || isCI) {\n return 'simple';\n }\n\n return verbose ? 'verbose' : 'default';\n}\n\n/**\n * Plain subtask title for a story, optionally suffixed to show it is the one\n * `execute` is currently working on. Exported standalone for testing without\n * spinning up a `listr2` renderer.\n */\nexport function storySubtaskTitle(\n story: Pick<UserStory, 'id' | 'title'>,\n executing: boolean,\n): string {\n const base = `${story.id}: ${story.title}`;\n return executing ? `${base} → Executando...` : base;\n}\n\n/** Minimal view of a listr2 task the tracker needs: a writable title. */\ninterface TitledTask {\n title: string;\n}\n\nexport interface ActiveStoryTracker {\n /** The story `execute` is now working on, or `undefined` for none. */\n setActive(storyId: string | undefined): void;\n /** Nothing is executing any more — drop the suffix from whoever holds it. */\n clear(): void;\n}\n\n/**\n * Keeps the \"executing\" suffix on exactly one story subtask at a time.\n *\n * `clear()` matters as much as `setActive()`: the engine leaves its loop the\n * moment the last story passes, so no further `iteration:start` ever arrives\n * to move the suffix off the story that just finished. Without an explicit\n * clear at the end of the phase, the terminal would keep showing a completed\n * story as executing while the web panel already shows it awaiting review.\n */\nexport function createActiveStoryTracker(\n stories: ReadonlyArray<Pick<UserStory, 'id' | 'title'>>,\n storyTasks: ReadonlyMap<string, TitledTask>,\n): ActiveStoryTracker {\n let activeStoryId: string | undefined;\n\n const retitle = (storyId: string, executing: boolean): void => {\n const task = storyTasks.get(storyId);\n if (!task) return;\n task.title = storySubtaskTitle(\n stories.find((s) => s.id === storyId) ?? { id: storyId, title: '' },\n executing,\n );\n };\n\n return {\n setActive(storyId: string | undefined): void {\n if (activeStoryId !== undefined && activeStoryId !== storyId) {\n retitle(activeStoryId, false);\n }\n activeStoryId = storyId;\n if (storyId !== undefined) retitle(storyId, true);\n },\n clear(): void {\n if (activeStoryId === undefined) return;\n retitle(activeStoryId, false);\n activeStoryId = undefined;\n },\n };\n}\n\n/**\n * Build the execute phase task with dynamic subtasks for each user story.\n *\n * Stories that already pass are displayed as skipped. Pending stories wait for\n * the engine to complete them via the story update callback. The parent task\n * title is updated with aggregate progress (e.g., \"Execute (3/5 stories passing)\").\n */\nfunction buildExecutePhaseTask(runner: () => Promise<void>, tasksPath: string, verbose: boolean) {\n return async (_ctx: unknown, task: TaskContext) => {\n let stories: UserStory[];\n try {\n const plan = await loadTaskPlan(tasksPath);\n stories = [...plan.userStories].sort((a, b) => a.priority - b.priority);\n } catch {\n // If we can't read the plan, fall back to running without subtasks\n setOutputCallback((line: string) => {\n task.output = line;\n });\n try {\n await runner();\n } finally {\n setOutputCallback(undefined);\n }\n return;\n }\n\n const totalStories = stories.length;\n const initialPassed = stories.filter((s) => s.passes).length;\n task.title = `Execute (${initialPassed}/${totalStories} stories passing)`;\n\n // Create promise resolvers for pending stories\n const resolvers = new Map<string, { resolve: () => void; reject: (err: Error) => void }>();\n\n // Set up story update callback — engine calls this after each iteration\n setStoryUpdateCallback((updatedStories: UserStory[]) => {\n const passed = updatedStories.filter((s) => s.passes).length;\n task.title = `Execute (${passed}/${totalStories} stories passing)`;\n\n for (const s of updatedStories) {\n if (s.passes && resolvers.has(s.id)) {\n resolvers.get(s.id)!.resolve();\n resolvers.delete(s.id);\n }\n }\n });\n\n // Own listr2 task handle per story subtask, so the story-stage callback\n // below can update a title after the subtask has already started — the\n // resolvers map alone has no reference back into the renderer.\n const storyTasks = new Map<string, TaskContext>();\n const tracker = createActiveStoryTracker(stories, storyTasks);\n\n // Engine calls this alongside every iteration:start, with the same\n // storyId the published event and the session snapshot carry — never a\n // second, independently computed \"who is active\" heuristic. Only ever\n // meaningful for a story still pending (an already-passing one has no\n // subtask left to update: it already resolved and disappeared from view).\n setStoryStageCallback((storyId: string | undefined) => {\n tracker.setActive(storyId);\n });\n\n // Build subtask definitions for each story\n const subtaskDefs = stories.map((story) => ({\n title: storySubtaskTitle(story, false),\n skip: story.passes ? 'already passing' : false,\n task: async (_c: unknown, subTask: TaskContext) => {\n storyTasks.set(story.id, subTask);\n await new Promise<void>((resolve, reject) => {\n resolvers.set(story.id, { resolve, reject });\n });\n },\n rendererOptions: {\n timer: PRESET_TIMER,\n },\n }));\n\n // Engine runner subtask — drives the execution loop\n const engineSubtask = {\n title: 'Running engine',\n task: async (_c: unknown, engineTask: TaskContext) => {\n setOutputCallback((line: string) => {\n engineTask.output = line;\n });\n try {\n await runner();\n } finally {\n setOutputCallback(undefined);\n setStoryUpdateCallback(undefined);\n setStoryStageCallback(undefined);\n // The engine is done — nothing is executing any more, whether it\n // finished the plan or failed mid-story.\n tracker.clear();\n // Resolve any remaining pending stories (engine finished successfully)\n for (const [, { resolve }] of resolvers) {\n resolve();\n }\n resolvers.clear();\n }\n },\n rendererOptions: {\n timer: PRESET_TIMER,\n outputBar: verbose ? Infinity : false,\n persistentOutput: false,\n bottomBar: verbose ? Infinity : 3,\n },\n };\n\n // Run engine + story subtasks concurrently:\n // - Engine subtask runs the actual execution loop\n // - Story subtasks resolve as the engine completes each story\n // Cleanup of setStoryUpdateCallback is handled in engineSubtask's finally block\n //\n // exitOnError must stay true: the per-story subtasks never reject (they\n // only ever get resolve()d, either by the story-update callback or by\n // engineSubtask's finally block once the engine is done), so the only way\n // this nested Listr can fail is engineSubtask itself throwing. With\n // exitOnError: false that failure was swallowed here — the outer \"execute\"\n // phase task then resolved successfully even though the engine failed,\n // and the pipeline moved on to `review` with 0 stories passing instead of\n // stopping. exitOnError: true lets the failure propagate out of\n // task.newListr(...) so the outer phase list (which does have\n // exitOnError: true) aborts before ever starting `review`.\n return task.newListr([engineSubtask, ...subtaskDefs], {\n concurrent: true,\n exitOnError: true,\n rendererOptions: {\n timer: PRESET_TIMER,\n collapseSkips: false,\n },\n });\n };\n}\n\n/**\n * Run the pipeline phases through listr2 for single-writer terminal output.\n *\n * Phases before startIndex are displayed as \"skipped\".\n * Each running phase shows an animated spinner with elapsed time.\n * Completed phases show a checkmark with final duration.\n * The execute phase shows per-story subtask progress when tasksPath is provided.\n */\nexport async function runPipelineWithRenderer(\n options: PipelineRendererOptions,\n): Promise<PipelineResult> {\n const { phases, startIndex, verbose, runners, tasksPath } = options;\n const overallStart = Date.now();\n let currentPhase: string | undefined;\n const renderer = selectRenderer(verbose);\n const isSimple = renderer === 'simple';\n\n const tasks = new Listr(\n phases.map((phase, index) => {\n const label = phaseLabel(phase);\n const isExecutePhase = phase === 'execute' && tasksPath;\n\n return {\n title: label,\n skip: index < startIndex ? 'skipped' : false,\n task: isExecutePhase\n ? async (_ctx: unknown, task: TaskContext) => {\n currentPhase = phase;\n const runner = runners[phase];\n if (!runner) {\n throw new Error(`No runner defined for phase: ${phase}`);\n }\n return buildExecutePhaseTask(runner, tasksPath, verbose)(_ctx, task);\n }\n : async (_ctx: unknown, task: TaskContext) => {\n currentPhase = phase;\n const runner = runners[phase];\n if (!runner) {\n throw new Error(`No runner defined for phase: ${phase}`);\n }\n // Route all output through task.output so listr2 controls rendering\n setOutputCallback((line: string) => {\n task.output = line;\n });\n try {\n await runner();\n } finally {\n setOutputCallback(undefined);\n }\n },\n rendererOptions: {\n timer: PRESET_TIMER,\n outputBar: verbose ? Infinity : false,\n persistentOutput: false,\n },\n };\n }),\n {\n renderer,\n rendererOptions: {\n timer: PRESET_TIMER,\n collapseSkips: false,\n ...(isSimple ? { timestamp: PRESET_TIMESTAMP } : {}),\n },\n exitOnError: true,\n },\n );\n\n let failedPhase: string | undefined;\n\n try {\n await tasks.run();\n } catch {\n failedPhase = currentPhase ?? phases[startIndex];\n } finally {\n // Ensure global callbacks are always cleaned up, even on unexpected errors\n setOutputCallback(undefined);\n setStoryUpdateCallback(undefined);\n setStoryStageCallback(undefined);\n }\n\n const overallElapsedSeconds = Math.floor((Date.now() - overallStart) / 1000);\n\n return {\n success: !failedPhase,\n failedPhase,\n overallElapsedSeconds,\n };\n}\n"],"mappings":"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","names":["failure"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/plan.ts","../src/storage/user-story-numbering.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { DEFAULT_HEADLESS_TIMEOUT_MS, runHeadless } from '../core/headless.js';\nimport { readFileWithGrace, runPhaseWithRetry } from '../core/phase-runner.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { publishPhaseMetrics } from '../core/session-metrics.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { resolvePolicyPlaceholders } from '../policy/placeholders.js';\nimport { taskPlanSchema } from '../schemas.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport {\n determineUserStoryNumbering,\n formatUserStoryId,\n parseUserStoryNumber,\n} from '../storage/user-story-numbering.js';\nimport { printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\n/** `--continue` / `--start-us <n>` override of the numbering cascade (issue #36). */\nexport interface PlanUserStoryNumberingOptions {\n continueFlag?: boolean;\n startUs?: number;\n}\n\nexport async function runPlan(\n issue: string,\n resolvedIssue?: ResolvedIssue,\n numbering?: PlanUserStoryNumberingOptions,\n): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n // Read the PRD\n const prdPath = paths.prdFile;\n let prdContent: string;\n try {\n prdContent = await readFile(prdPath, 'utf-8');\n } catch {\n printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);\n return 1;\n }\n\n const tasksPath = paths.tasksFile;\n\n // Resolve the User Story numbering continuity (issue #36), log where the\n // decision came from — never silently — and persist it into the project's\n // metadata.json for audit before the prompt is even built, so the record on\n // disk always matches what the prompt was told.\n const {\n message: numberingMessage,\n nextUserStoryId,\n decision: { nextNumber },\n } = await determineUserStoryNumbering({\n issueNumber,\n continueFlag: numbering?.continueFlag,\n startUs: numbering?.startUs,\n });\n printInfo(numberingMessage);\n\n const template = await loadPrompt('plan');\n const prompt = applyPlaceholders(template, {\n // The repository's own conventions. Empty when it declares none, which is\n // what keeps the rendered prompt identical to the pre-policy one.\n ...(await resolvePolicyPlaceholders()),\n __ISSUE_NUMBER__: issueNumber,\n __PRD_CONTENT__: prdContent,\n __TASKS_PATH__: tasksPath,\n __NEXT_US_NUMBER__: nextUserStoryId,\n ...issuePlaceholders(resolution.resolved),\n });\n\n await mkdir(paths.issueDir, { recursive: true });\n\n const outcome = await runPhaseWithRetry({\n phase: 'plan',\n attempt: async () => {\n const startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,\n // json (not text) so the CLI reports usage: the envelope's `result`\n // field carries the same assistant text this phase already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n addDirs: [paths.issueDir],\n statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`,\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('plan', result.cost, startedAtMs);\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `Task plan generation failed: ${result.error}`,\n };\n }\n\n // Verify the file was created, tolerating a brief FS-visibility lag.\n let rawContent: string;\n try {\n rawContent = await readFileWithGrace(tasksPath);\n } catch {\n return { ok: false, transient: true, error: `tasks.json was not created at ${tasksPath}` };\n }\n\n // Validate JSON structure — a content defect, not a timing issue, but\n // still worth a bounded retry since it's a fresh Claude invocation.\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawContent);\n } catch {\n return { ok: false, transient: true, error: 'tasks.json contains invalid JSON' };\n }\n\n // Validate with zod schema\n const validation = taskPlanSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues\n .map((i) => ` - ${i.path.join('.')}: ${i.message}`)\n .join('\\n');\n return {\n ok: false,\n transient: true,\n error: `tasks.json does not match expected schema:\\n${issues}`,\n };\n }\n\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `Task plan generation failed for issue #${issueNumber}`);\n return 1;\n }\n\n // Ensure pipeline state reflects completion of this phase\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.jsonCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n\n // The numbering is a prompt instruction, not a programmatic rewrite, so the\n // generated plan can still ignore it. Say so out loud instead of leaving the\n // log and metadata.json claiming a numbering the file on disk contradicts.\n const generatedLowest = plan.userStories\n .map((story) => parseUserStoryNumber(story.id))\n .filter((value): value is number => value !== null)\n .reduce<number | null>(\n (lowest, value) => (lowest === null ? value : Math.min(lowest, value)),\n null,\n );\n if (generatedLowest !== null && generatedLowest < nextNumber) {\n printWarning(\n `The generated plan starts at ${formatUserStoryId(generatedLowest)}, not the requested ` +\n `${nextUserStoryId}. User Story ids may collide with earlier issues of this project — ` +\n `re-run 'issue-flow plan ${issueNumber} --start-us ${nextNumber}' if that matters.`,\n );\n }\n\n printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);\n return 0;\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { isoNow } from '../core/state-manager.js';\nimport { getProjectRoot } from '../utils/git.js';\nimport { recordUserStoryNumbering } from './compat.js';\nimport { getIssuePaths } from './paths.js';\nimport { type ResolveIssuePathsOptions, resolveProjectPaths } from './resolve.js';\nimport type { UserStoryNumberingDecision } from './schemas.js';\n\n/**\n * Continuity of `US-NNN` numbering across `plan` executions of the same\n * project (issue #36).\n *\n * The User Story `id` field is free-form — `userStorySchema` in\n * `src/schemas.ts` only requires a non-empty string, no `US-NNN` regex is\n * enforced — so every reader here tolerates an id that does not carry a\n * trailing number instead of throwing on it.\n */\n\nconst USER_STORY_ID_PAD = 3;\n\n/** `16` -> `'US-016'`. Numbers past 999 are not padded further. */\nexport function formatUserStoryId(n: number): string {\n return `US-${String(n).padStart(USER_STORY_ID_PAD, '0')}`;\n}\n\n/**\n * Extract the last run of digits from a User Story id, or `null` when none is\n * found. Tolerant by design: an id like `US-001` yields `1`, but so does a\n * free-form one such as `story-1` or `v2-US-007` (the last digit run, `007`,\n * wins over the `2` version prefix) — only an id with no digits at all (e.g.\n * `add-auth`) is ignored.\n */\nexport function parseUserStoryNumber(id: string): number | null {\n const matches = id.match(/\\d+/g);\n if (!matches || matches.length === 0) return null;\n\n const value = Number.parseInt(matches[matches.length - 1], 10);\n return Number.isNaN(value) ? null : value;\n}\n\nexport interface HighestUserStoryNumberResult {\n number: number;\n /** Issue directory name the winning story came from. */\n issueId: string;\n /** Verbatim id of the winning story. */\n storyId: string;\n}\n\n/**\n * Loose read of one `tasks.json`: only cares about `userStories[].id` and\n * tolerates every failure (missing file, invalid JSON, unexpected shape) by\n * returning `null` — a sibling issue mid-migration, or with a corrupt plan,\n * must not abort the whole project scan.\n */\nasync function readUserStoryIds(tasksPath: string): Promise<string[] | null> {\n let raw: string;\n try {\n raw = await readFile(tasksPath, 'utf-8');\n } catch {\n return null;\n }\n\n try {\n const parsed: unknown = JSON.parse(raw);\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n !Array.isArray((parsed as { userStories?: unknown }).userStories)\n ) {\n return null;\n }\n\n return (parsed as { userStories: unknown[] }).userStories\n .map((story) =>\n typeof story === 'object' &&\n story !== null &&\n typeof (story as { id?: unknown }).id === 'string'\n ? (story as { id: string }).id\n : null,\n )\n .filter((id): id is string => id !== null);\n } catch {\n return null;\n }\n}\n\nexport interface FindHighestUserStoryNumberOptions extends ResolveIssuePathsOptions {\n /**\n * Issue directory to leave out of the scan — the issue the numbering is\n * being resolved for. Re-planning an issue overwrites its own `tasks.json`,\n * so counting the plan about to be replaced would push the numbering forward\n * on every `plan` re-run of the same issue.\n */\n excludeIssueId?: string;\n}\n\n/**\n * Highest User Story number already used anywhere in the project, scanning\n * every `<issuesDir>/<id>/tasks.json` regardless of which issue produced it —\n * that project-wide scope is what makes `US-NNN` unambiguous across issues.\n * The issue named by `excludeIssueId` is skipped, so re-planning is idempotent.\n *\n * `null` means no readable `tasks.json` was found (the project's first `plan`\n * run ever, or a brand-new project directory). A storage directory that exists\n * but cannot be read throws instead: silently restarting at `US-001` because\n * `~/.issue-flow` was momentarily unreadable is the worst outcome this feature\n * can produce.\n */\nexport async function findHighestUserStoryNumber(\n options: FindHighestUserStoryNumberOptions = {},\n): Promise<HighestUserStoryNumberResult | null> {\n const { excludeIssueId, ...rootOptions } = options;\n const { projectId, issuesDir } = await resolveProjectPaths(rootOptions);\n\n let entries: string[];\n try {\n entries = (await readdir(issuesDir, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n } catch (error) {\n // ENOENT is the legitimate \"no history yet\" case. Any other failure\n // (EACCES, EIO, an unavailable mount) must not be reported as an empty\n // history, or the numbering would restart silently.\n if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') return null;\n throw new Error(\n `Could not scan the project's User Story history at ${issuesDir}: ` +\n `${(error as Error).message}. Fix the storage directory, or pass --start-us <n> ` +\n 'to set the numbering explicitly.',\n { cause: error },\n );\n }\n\n let best: HighestUserStoryNumberResult | null = null;\n\n for (const issueId of entries) {\n if (excludeIssueId !== undefined && issueId === excludeIssueId) continue;\n // Never hand-build an artifact path (see src/storage/AGENTS.md): asking\n // getIssuePaths() keeps a rename of `tasks.json` a one-file change instead\n // of silently making this scan find nothing.\n const { tasksFile } = getIssuePaths(projectId, issueId, { env: options.env });\n const ids = await readUserStoryIds(tasksFile);\n if (!ids) continue;\n\n for (const id of ids) {\n const value = parseUserStoryNumber(id);\n if (value === null) continue;\n if (best === null || value > best.number) {\n best = { number: value, issueId, storyId: id };\n }\n }\n }\n\n return best;\n}\n\nexport interface ResolveUserStoryNumberingOptions extends ResolveIssuePathsOptions {\n /** Issue the decision is being made for — carried into the audit record. */\n issueNumber: string;\n /** `--continue` was passed explicitly. Purely cosmetic on the log message:\n * the resolved number is identical to the automatic default. */\n continueFlag?: boolean;\n /** `--start-us <n>` was passed. Wins outright over any history. */\n startUs?: number;\n /** Timestamp source, injectable for tests. */\n now?: () => string;\n}\n\nexport interface UserStoryNumberingOutcome {\n decision: UserStoryNumberingDecision;\n /** Ready-to-print explanation of where the decision came from — the CLI\n * must never resolve this silently (issue #36). */\n message: string;\n}\n\n/**\n * Resolve the numbering cascade of #36:\n *\n * 1. `--start-us <n>` wins outright — history is not even scanned, since the\n * whole point is to ignore it.\n * 2. Otherwise the project's history is scanned for the highest number used\n * so far. This is what running with no flag at all does automatically,\n * and what `--continue` names explicitly (the two produce the same\n * number; the flag only changes the log message).\n * 3. No history found (the project's first `plan` ever) starts at `US-001`.\n *\n * Pure with respect to `metadata.json` — this only reads `tasks.json` files,\n * never writes. Persisting the decision is {@link determineUserStoryNumbering}\n * below, via `recordUserStoryNumbering()` in `compat.ts`.\n */\nexport async function resolveUserStoryNumbering(\n options: ResolveUserStoryNumberingOptions,\n): Promise<UserStoryNumberingOutcome> {\n const { issueNumber, continueFlag, startUs, now, ...rootOptions } = options;\n const decidedAt = (now ?? isoNow)();\n\n if (startUs !== undefined) {\n const decision: UserStoryNumberingDecision = {\n nextNumber: startUs,\n source: 'start-us',\n issueNumber,\n decidedAt,\n };\n return {\n decision,\n message: `User Story numbering forced to ${formatUserStoryId(startUs)} via --start-us.`,\n };\n }\n\n // The issue being planned is excluded from the scan: `plan` overwrites its\n // own `tasks.json`, so counting the plan it is about to replace would push\n // the numbering forward on every re-run of `plan` for the same issue.\n const highest = await findHighestUserStoryNumber({ ...rootOptions, excludeIssueId: issueNumber });\n\n if (highest === null) {\n const decision: UserStoryNumberingDecision = {\n nextNumber: 1,\n source: 'none',\n issueNumber,\n decidedAt,\n };\n return {\n decision,\n message: `Starting User Story numbering at ${formatUserStoryId(1)} (no previous history found for this project).`,\n };\n }\n\n const nextNumber = highest.number + 1;\n const decision: UserStoryNumberingDecision = {\n nextNumber,\n source: 'history',\n issueNumber,\n decidedAt,\n detail: `${highest.storyId} (issue #${highest.issueId})`,\n };\n const via = continueFlag ? ' via --continue' : '';\n return {\n decision,\n message:\n `Continuing User Story numbering from ${formatUserStoryId(nextNumber)}${via} ` +\n `— last used was ${highest.storyId} (issue #${highest.issueId}).`,\n };\n}\n\nexport interface DetermineUserStoryNumberingOptions extends ResolveUserStoryNumberingOptions {\n /** Repository root. Defaults to the git toplevel of the current directory. */\n projectRoot?: string;\n}\n\nexport interface UserStoryNumberingResult extends UserStoryNumberingOutcome {\n /** `decision.nextNumber` formatted as `US-NNN`, ready for the prompt. */\n nextUserStoryId: string;\n}\n\n/**\n * The entry point commands use: resolve the cascade and persist the decision\n * into `metadata.json` in one call, so no call site can log/prompt with one\n * decision and audit a different one.\n */\nexport async function determineUserStoryNumbering(\n options: DetermineUserStoryNumberingOptions,\n): Promise<UserStoryNumberingResult> {\n const { projectRoot: projectRootOption, ...rest } = options;\n const projectRoot = projectRootOption ?? (await getProjectRoot());\n\n const outcome = await resolveUserStoryNumbering({ ...rest, projectRoot });\n await recordUserStoryNumbering(projectRoot, outcome.decision, {\n env: options.env,\n now: options.now,\n });\n\n return { ...outcome, nextUserStoryId: formatUserStoryId(outcome.decision.nextNumber) };\n}\n"],"mappings":"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","names":["readFile","decision","readFile"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/review.ts"],"sourcesContent":["import { DEFAULT_HEADLESS_TIMEOUT_MS, runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { publishPhaseMetrics } from '../core/session-metrics.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { resolvePolicyPlaceholders } from '../policy/placeholders.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport interface ReviewResult {\n status: 'PASS' | 'FAIL';\n findings?: string;\n}\n\n/**\n * Parse the <review-result> block from headless output.\n */\nfunction parseReviewResult(output: string): ReviewResult {\n const match = output.match(/<review-result>([\\s\\S]*?)<\\/review-result>/);\n if (!match) {\n // Try to detect PASS/FAIL from the raw output\n if (/STATUS:\\s*PASS/i.test(output)) {\n return { status: 'PASS' };\n }\n if (/STATUS:\\s*FAIL/i.test(output)) {\n const findingsMatch = output.match(/FINDINGS:([\\s\\S]*?)(?:$|<\\/)/);\n return { status: 'FAIL', findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings' };\n }\n // Default to PASS if no explicit status found and no errors\n return { status: 'PASS' };\n }\n\n const block = match[1];\n if (/STATUS:\\s*PASS/i.test(block)) {\n return { status: 'PASS' };\n }\n\n const findingsMatch = block.match(/FINDINGS:([\\s\\S]*)/);\n return {\n status: 'FAIL',\n findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings',\n };\n}\n\nexport async function runReview(issue: string, resolvedIssue?: ResolvedIssue): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n const tasksPath = paths.tasksFile;\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n const template = await loadPrompt('review');\n const prompt = applyPlaceholders(template, {\n // The repository's own conventions. Empty when it declares none, which is\n // what keeps the rendered prompt identical to the pre-policy one.\n ...(await resolvePolicyPlaceholders()),\n __ISSUE_NUMBER__: issueNumber,\n __TASKS_PATH__: tasksPath,\n ...issuePlaceholders(resolution.resolved),\n });\n\n const startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,\n // json (not text) so the CLI reports usage: the envelope's `result` field\n // carries the same assistant text parseReviewResult() already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [paths.issueDir],\n statusMessage: `Reviewing issue #${issueNumber} resolution...`,\n });\n // Before the success check: the tokens were spent either way. A correction\n // cycle calls runReview() again and the reducer sums both invocations.\n publishPhaseMetrics('review', result.cost, startedAtMs);\n\n if (!result.success) {\n printError(`Review failed: ${result.error}`);\n return 1;\n }\n\n const review = parseReviewResult(result.result);\n\n if (review.status === 'PASS') {\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.reviewCompleted = true;\n plan.lastReviewFindings = null;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n printSuccess('Review PASSED — implementation meets acceptance criteria');\n return 0;\n }\n\n printError('Review FAILED — issues found:');\n if (review.findings) {\n console.log(review.findings);\n }\n\n // Persist the findings so a correction cycle can act on them: without this,\n // a re-run of execute sees every userStories[].passes already true and\n // exits immediately without attempting any fix (see engine.ts's\n // \"already complete\" guards).\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.reviewCompleted = false;\n plan.lastReviewFindings = review.findings ?? 'Unknown findings';\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n return 1;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,SAAS,kBAAkB,QAA8B;AACvD,QAAM,QAAQ,OAAO,MAAM,4CAA4C;AACvE,MAAI,CAAC,OAAO;AAEV,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,aAAO,EAAE,QAAQ,OAAO;AAAA,IAC1B;AACA,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,YAAMA,iBAAgB,OAAO,MAAM,8BAA8B;AACjE,aAAO,EAAE,QAAQ,QAAQ,UAAUA,iBAAgB,CAAC,GAAG,KAAK,KAAK,mBAAmB;AAAA,IACtF;AAEA,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,QAAQ,MAAM,CAAC;AACrB,MAAI,kBAAkB,KAAK,KAAK,GAAG;AACjC,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,gBAAgB,MAAM,MAAM,oBAAoB;AACtD,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU,gBAAgB,CAAC,GAAG,KAAK,KAAK;AAAA,EAC1C;AACF;AAEA,eAAsB,UAAU,OAAe,eAAgD;AAC7F,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,QAAQ,MAAM,kBAAkB,WAAW;AACjD,QAAM,YAAY,MAAM;AAExB,QAAM,aAAa,MAAM,oBAAoB,aAAa,aAAa;AACvE,MAAI,CAAC,WAAW,IAAI;AAClB,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM,WAAW,MAAM,WAAW,QAAQ;AAC1C,QAAM,SAAS,kBAAkB,UAAU;AAAA;AAAA;AAAA,IAGzC,GAAI,MAAM,0BAA0B;AAAA,IACpC,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,GAAG,kBAAkB,WAAW,QAAQ;AAAA,EAC1C,CAAC;AAED,QAAM,cAAc,KAAK,IAAI;AAC7B,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA;AAAA;AAAA,IAG/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7C,SAAS,CAAC,MAAM,QAAQ;AAAA,IACxB,eAAe,oBAAoB,WAAW;AAAA,EAChD,CAAC;AAGD,sBAAoB,UAAU,OAAO,MAAM,WAAW;AAEtD,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,kBAAkB,OAAO,KAAK,EAAE;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,kBAAkB,OAAO,MAAM;AAE9C,MAAI,OAAO,WAAW,QAAQ;AAE5B,QAAI;AACF,YAAM,OAAO,MAAM,aAAa,SAAS;AACzC,WAAK,SAAS,kBAAkB;AAChC,WAAK,qBAAqB;AAC1B,YAAM,aAAa,WAAW,IAAI;AAAA,IACpC,QAAQ;AAAA,IAER;AAEA,iBAAa,+DAA0D;AACvE,WAAO;AAAA,EACT;AAEA,aAAW,oCAA+B;AAC1C,MAAI,OAAO,UAAU;AACnB,YAAQ,IAAI,OAAO,QAAQ;AAAA,EAC7B;AAMA,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,kBAAkB;AAChC,SAAK,qBAAqB,OAAO,YAAY;AAC7C,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;","names":["findingsMatch"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/phase-runner.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { type ClassifiedFailure, classify } from '../resilience/errors.js';\nimport { fixedBackoffPolicy, withRetry } from '../resilience/retry.js';\nimport { printRetry } from '../ui/logger.js';\nimport { getSessionPublisher } from './session-publisher.js';\nimport { getShutdownSignal } from './shutdown.js';\nimport { isoNow } from './state-manager.js';\n\nexport interface PhaseAttemptResult {\n ok: boolean;\n /** Whether a failed attempt is worth retrying. Ignored when ok is true. */\n transient?: boolean;\n error?: string;\n}\n\nexport interface PhaseRetryOptions {\n phase: string;\n attempt: (attemptNumber: number) => Promise<PhaseAttemptResult>;\n retryLimit?: number;\n backoffBaseSeconds?: number;\n backoffMaxSeconds?: number;\n /** Cuts a pending backoff short, so a phase stops waiting on shutdown. */\n signal?: AbortSignal;\n}\n\nconst DEFAULT_PHASE_RETRY_LIMIT = 3;\nconst DEFAULT_BACKOFF_BASE_SECONDS = 15;\nconst DEFAULT_BACKOFF_MAX_SECONDS = 120;\n\n/**\n * Bounded retry wrapper for single-shot phases (prd, plan, pr) that invoke\n * the Claude CLI once and validate an artifact. Applied to phases that\n * previously aborted the whole pipeline on the first failure with no chance\n * to recover. Non-transient failures (or exhausting the retry budget) still\n * end the phase — this bounds recovery attempts, it never retries forever.\n *\n * The loop itself is `resilience/retry.ts:withRetry`, shared with the\n * `execute` loop of `core/engine.ts`. What stays here is what is specific to a\n * phase: the caller's `transient` verdict, the `retry` event and the printed\n * line. With no options supplied the effective numbers are the ones this\n * module has always used — 3 attempts, 15s doubling to 120s, no jitter.\n */\nexport async function runPhaseWithRetry(options: PhaseRetryOptions): Promise<PhaseAttemptResult> {\n const retryLimit = options.retryLimit ?? DEFAULT_PHASE_RETRY_LIMIT;\n const backoffBaseSeconds = options.backoffBaseSeconds ?? DEFAULT_BACKOFF_BASE_SECONDS;\n const backoffMaxSeconds = options.backoffMaxSeconds ?? DEFAULT_BACKOFF_MAX_SECONDS;\n\n if (retryLimit < 1) return { ok: false, error: 'Phase never attempted' };\n\n const outcome = await withRetry<PhaseAttemptResult>((attempt) => options.attempt(attempt), {\n policy: fixedBackoffPolicy(retryLimit, backoffBaseSeconds, backoffMaxSeconds),\n // The caller's own signal when it has one, and the process-wide shutdown\n // otherwise: a backoff nobody can interrupt is a `Ctrl+C` that appears to\n // do nothing for two minutes.\n signal: options.signal ?? getShutdownSignal(),\n evaluate: (result) => (result.ok ? null : phaseFailure(result)),\n onAttempt: ({ attempt, failure, willRetry, delayMs }) => {\n if (failure === null || !willRetry) return;\n\n const delaySeconds = delayMs / 1000;\n getSessionPublisher().publish({\n type: 'retry',\n at: isoNow(),\n attempt,\n delaySeconds,\n reason: failure.message,\n kind: failure.kind,\n });\n printRetry(\n `Phase ${options.phase} hit a recoverable failure (attempt ${attempt}/${retryLimit}): ${failure.message}. Retrying in ${delaySeconds}s.`,\n );\n },\n });\n\n return outcome.value;\n}\n\n/**\n * Turn a phase's own verdict into the classified failure `withRetry` decides\n * on.\n *\n * `transient` stays authoritative: the phases build it with\n * `isTransientFailure()` over the CLI's output, but a phase can also fail on\n * something it alone can see — a PRD file that never appeared — and that\n * judgement is not recoverable from the error text. `classify()` is consulted\n * only to name the *kind*, which is what the `retry` event now reports.\n */\nfunction phaseFailure(result: PhaseAttemptResult): ClassifiedFailure {\n const message = result.error ?? 'Phase failed';\n return {\n ...classify({ source: 'agent', stdout: message }),\n message,\n retryable: result.transient === true,\n };\n}\n\n/**\n * Re-read a file a few times with short delays before giving up. Absorbs a\n * brief filesystem-visibility lag between the Claude CLI's Write tool\n * completing and this process observing the result, without spending a full\n * phase retry (and a new Claude invocation) on what may just be a timing gap.\n */\nexport async function readFileWithGrace(\n path: string,\n delaysMs: number[] = [300, 800],\n): Promise<string> {\n for (let i = 0; ; i++) {\n try {\n return await readFile(path, 'utf-8');\n } catch (err) {\n if (i >= delaysMs.length) throw err;\n await new Promise((resolve) => setTimeout(resolve, delaysMs[i]));\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAyBzB,IAAM,4BAA4B;AAClC,IAAM,+BAA+B;AACrC,IAAM,8BAA8B;AAepC,eAAsB,kBAAkB,SAAyD;AAC/F,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,qBAAqB,QAAQ,sBAAsB;AACzD,QAAM,oBAAoB,QAAQ,qBAAqB;AAEvD,MAAI,aAAa,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,wBAAwB;AAEvE,QAAM,UAAU,MAAM,UAA8B,CAAC,YAAY,QAAQ,QAAQ,OAAO,GAAG;AAAA,IACzF,QAAQ,mBAAmB,YAAY,oBAAoB,iBAAiB;AAAA;AAAA;AAAA;AAAA,IAI5E,QAAQ,QAAQ,UAAU,kBAAkB;AAAA,IAC5C,UAAU,CAAC,WAAY,OAAO,KAAK,OAAO,aAAa,MAAM;AAAA,IAC7D,WAAW,CAAC,EAAE,SAAS,SAAS,WAAW,QAAQ,MAAM;AACvD,UAAI,YAAY,QAAQ,CAAC,UAAW;AAEpC,YAAM,eAAe,UAAU;AAC/B,0BAAoB,EAAE,QAAQ;AAAA,QAC5B,MAAM;AAAA,QACN,IAAI,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,MAChB,CAAC;AACD;AAAA,QACE,SAAS,QAAQ,KAAK,uCAAuC,OAAO,IAAI,UAAU,MAAM,QAAQ,OAAO,iBAAiB,YAAY;AAAA,MACtI;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,QAAQ;AACjB;AAYA,SAAS,aAAa,QAA+C;AACnE,QAAM,UAAU,OAAO,SAAS;AAChC,SAAO;AAAA,IACL,GAAG,SAAS,EAAE,QAAQ,SAAS,QAAQ,QAAQ,CAAC;AAAA,IAChD;AAAA,IACA,WAAW,OAAO,cAAc;AAAA,EAClC;AACF;AAQA,eAAsB,kBACpB,MACA,WAAqB,CAAC,KAAK,GAAG,GACb;AACjB,WAAS,IAAI,KAAK,KAAK;AACrB,QAAI;AACF,aAAO,MAAM,SAAS,MAAM,OAAO;AAAA,IACrC,SAAS,KAAK;AACZ,UAAI,KAAK,SAAS,OAAQ,OAAM;AAChC,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,SAAS,CAAC,CAAC,CAAC;AAAA,IACjE;AAAA,EACF;AACF;","names":[]}
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- runExecute
4
- } from "./chunk-3QHK4YQA.js";
5
- import "./chunk-TSOLWK7Y.js";
6
- import "./chunk-LWRSMTTZ.js";
7
- import "./chunk-MJULIKQU.js";
8
- import "./chunk-LAFNLLQP.js";
9
- import "./chunk-M66SJHQQ.js";
10
- import "./chunk-EZATO3DW.js";
11
- import "./chunk-42PJFMAR.js";
12
- import "./chunk-DIDUVL6O.js";
13
- import "./chunk-BYZDPR7N.js";
14
- import "./chunk-CD5ULRVC.js";
15
- export {
16
- runExecute
17
- };
18
- //# sourceMappingURL=execute-XBNVL753.js.map
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- runInit
4
- } from "./chunk-NS6R37II.js";
5
- import "./chunk-BYZDPR7N.js";
6
- import "./chunk-CD5ULRVC.js";
7
- export {
8
- runInit
9
- };
10
- //# sourceMappingURL=init-AJHCXJBP.js.map
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- runPlan
4
- } from "./chunk-QCPGHBSZ.js";
5
- import "./chunk-Y5ZDJ4PJ.js";
6
- import "./chunk-73ATPIYX.js";
7
- import "./chunk-4JSOYE4G.js";
8
- import "./chunk-KMMASOA6.js";
9
- import "./chunk-LWRSMTTZ.js";
10
- import "./chunk-MJULIKQU.js";
11
- import "./chunk-LAFNLLQP.js";
12
- import "./chunk-M66SJHQQ.js";
13
- import "./chunk-EZATO3DW.js";
14
- import "./chunk-42PJFMAR.js";
15
- import "./chunk-DIDUVL6O.js";
16
- import "./chunk-BYZDPR7N.js";
17
- import "./chunk-CD5ULRVC.js";
18
- export {
19
- runPlan
20
- };
21
- //# sourceMappingURL=plan-QOZ3RIO6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/policy.ts"],"sourcesContent":["import type { PolicySource, RepositoryPolicy } from '../policy/index.js';\nimport { loadRepositoryPolicy } from '../policy/index.js';\nimport { printError, printInfo, printWarning } from '../ui/logger.js';\n\n/**\n * `issue-flow policy [--scope <dir>] [--json]`.\n *\n * The inspection surface of the policy layer, and the only one for now: this\n * issue delivers the foundation, so no prompt and no skill consumes\n * `loadRepositoryPolicy()` yet.\n *\n * It exists for two audiences. A human debugging a wrong discovery reads the\n * default output, where every section names the files it came from. The Agent\n * Skills read `--json`: they are markdown and cannot import TypeScript, so a\n * versioned JSON document on stdout is the only bridge available to them —\n * which is why `schemaVersion` is part of the payload rather than of the CLI.\n *\n * That makes `--json` a **published contract**, not a debugging convenience.\n * `skills/_shared/repository-policy.md` documents the payload, and\n * `policy-parity.test.ts` pins the fields the skills read: renaming or dropping\n * one silently makes every skill fall back to its defaults, and nothing would\n * fail — the two paths would simply start deciding differently. Adding a field\n * is safe (readers ignore what they do not know); removing or renaming one\n * bumps `schemaVersion`.\n */\n\nexport interface PolicyCommandOptions {\n scope?: string;\n json?: boolean;\n}\n\n/** Group the provenance entries by kind, preserving discovery order. */\nfunction groupSources(sources: PolicySource[]): Map<PolicySource['kind'], PolicySource[]> {\n const grouped = new Map<PolicySource['kind'], PolicySource[]>();\n for (const source of sources) {\n const bucket = grouped.get(source.kind);\n if (bucket === undefined) {\n grouped.set(source.kind, [source]);\n } else {\n bucket.push(source);\n }\n }\n return grouped;\n}\n\nfunction describeSource(source: PolicySource): string {\n const where = source.path ?? source.origin;\n const detail = source.detail === null ? '' : ` — ${source.detail}`;\n return `${source.status === 'found' ? '' : `[${source.status}] `}${where}${detail}`;\n}\n\n/**\n * Human-readable rendering.\n *\n * Content is deliberately never printed: a resolved policy carries whole\n * `AGENTS.md` and template bodies, and dumping them would bury the one thing\n * this view is for — which file each value came from. `--json` is where the\n * content lives.\n */\nfunction render(policy: RepositoryPolicy): void {\n const lines: string[] = [];\n const push = (line = ''): void => {\n lines.push(line);\n };\n\n push(`Repository: ${policy.root}`);\n push(`Scope: ${policy.scope ?? '(root)'}`);\n if (!policy.enabled) {\n push();\n push('Policy discovery is disabled (policy.enabled = false).');\n console.log(lines.join('\\n'));\n return;\n }\n\n push();\n push('Issues');\n push(` templates: ${policy.issues.templates.length}`);\n for (const template of policy.issues.templates) {\n const labels = template.labels.length > 0 ? ` labels=[${template.labels.join(', ')}]` : '';\n const type = template.type === null ? '' : ` type=${template.type}`;\n push(` - ${template.path} (${template.format}, ${template.origin})${type}${labels}`);\n }\n push(` types: ${policy.issues.types.length > 0 ? policy.issues.types.join(', ') : '(none)'}`);\n push(` labels: ${policy.issues.labels.length}`);\n for (const label of policy.issues.labels) {\n push(` - ${label.name}${label.description === null ? '' : ` — ${label.description}`}`);\n }\n push(` titleConvention: ${policy.issues.titleConvention ?? '(none)'}`);\n\n push();\n push('Pull Requests');\n push(` template: ${policy.pullRequests.templates[0]?.path ?? '(none)'}`);\n for (const template of policy.pullRequests.templates.slice(1)) {\n push(` - ${template.path}`);\n }\n push(` baseBranch: ${policy.pullRequests.baseBranch ?? '(none)'}`);\n push(` titleConvention: ${policy.pullRequests.titleConvention ?? '(none)'}`);\n\n push();\n push('Git');\n push(` branchConvention: ${policy.git.branchConvention ?? '(none)'}`);\n push(` commitConvention: ${policy.git.commitConvention ?? '(none)'}`);\n\n push();\n push(`Documents: ${policy.docs.length}`);\n for (const doc of policy.docs) {\n const scope = doc.scope === '' ? 'root' : doc.scope;\n const from = doc.referencedFrom === null ? '' : ` ← ${doc.referencedFrom}`;\n push(` - ${doc.path} (${doc.kind}, scope=${scope})${from}`);\n }\n\n push();\n push(`CODEOWNERS: ${policy.codeowners === null ? '(none)' : 'found'}`);\n\n push();\n push('Sources');\n for (const [kind, entries] of groupSources(policy.sources)) {\n push(` ${kind}`);\n for (const entry of entries) {\n push(` - ${describeSource(entry)}`);\n }\n }\n\n console.log(lines.join('\\n'));\n}\n\n/**\n * Resolve and print the repository policy. Returns the process exit code.\n *\n * Only a repository that cannot be located at all is a failure: an empty policy\n * is a legitimate — and common — answer, printed like any other.\n */\nexport async function runPolicy(options: PolicyCommandOptions = {}): Promise<number> {\n let policy: RepositoryPolicy;\n try {\n policy = await loadRepositoryPolicy({ scope: options.scope ?? null });\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n if (options.json === true) {\n console.log(JSON.stringify(policy, null, 2));\n return 0;\n }\n\n render(policy);\n\n const unavailable = policy.sources.filter((source) => source.status === 'unavailable');\n if (unavailable.length > 0) {\n console.log('');\n printWarning(\n `${unavailable.length} source(s) could not be consulted; the policy above is incomplete.`,\n );\n } else if (policy.sources.length === 0) {\n console.log('');\n printInfo('This repository declares no policy sources; Issue Flow defaults apply.');\n }\n\n return 0;\n}\n"],"mappings":"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","names":[]}
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- issueClosesLines,
4
- multiIssueContext,
5
- runPr
6
- } from "./chunk-FADTCQGM.js";
7
- import "./chunk-TSOLWK7Y.js";
8
- import "./chunk-Y5ZDJ4PJ.js";
9
- import "./chunk-73ATPIYX.js";
10
- import "./chunk-4JSOYE4G.js";
11
- import "./chunk-KMMASOA6.js";
12
- import "./chunk-LWRSMTTZ.js";
13
- import "./chunk-MJULIKQU.js";
14
- import "./chunk-LAFNLLQP.js";
15
- import "./chunk-M66SJHQQ.js";
16
- import "./chunk-EZATO3DW.js";
17
- import "./chunk-42PJFMAR.js";
18
- import "./chunk-DIDUVL6O.js";
19
- import "./chunk-BYZDPR7N.js";
20
- import "./chunk-CD5ULRVC.js";
21
- export {
22
- issueClosesLines,
23
- multiIssueContext,
24
- runPr
25
- };
26
- //# sourceMappingURL=pr-EBBHD2ZX.js.map
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- exitCodeFor,
4
- runPrReview
5
- } from "./chunk-A4SLHOW7.js";
6
- import "./chunk-TSOLWK7Y.js";
7
- import "./chunk-4JSOYE4G.js";
8
- import "./chunk-MJULIKQU.js";
9
- import "./chunk-LAFNLLQP.js";
10
- import "./chunk-M66SJHQQ.js";
11
- import "./chunk-EZATO3DW.js";
12
- import "./chunk-42PJFMAR.js";
13
- import "./chunk-DIDUVL6O.js";
14
- import "./chunk-BYZDPR7N.js";
15
- import "./chunk-CD5ULRVC.js";
16
- export {
17
- exitCodeFor,
18
- runPrReview
19
- };
20
- //# sourceMappingURL=pr-review-VVMW5PO4.js.map