issue-flow 0.6.0 → 0.7.1

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 (72) hide show
  1. package/README.md +3 -1
  2. package/dist/{analyze-VVG7UABS.js → analyze-R25HDGBJ.js} +10 -6
  3. package/dist/analyze-R25HDGBJ.js.map +1 -0
  4. package/dist/chunk-23WOEBGN.js +725 -0
  5. package/dist/chunk-23WOEBGN.js.map +1 -0
  6. package/dist/{chunk-4HT4EVYV.js → chunk-2AVZZAC6.js} +181 -13
  7. package/dist/chunk-2AVZZAC6.js.map +1 -0
  8. package/dist/{chunk-FPGHTTVV.js → chunk-ASMCLXQ2.js} +12 -7
  9. package/dist/chunk-ASMCLXQ2.js.map +1 -0
  10. package/dist/{chunk-2CSHLHYP.js → chunk-EHVKXFFN.js} +2 -2
  11. package/dist/{chunk-JTSAL3TI.js → chunk-EM43T462.js} +18 -5
  12. package/dist/chunk-EM43T462.js.map +1 -0
  13. package/dist/{chunk-KWGRBNUG.js → chunk-HJ7JT777.js} +26 -7
  14. package/dist/chunk-HJ7JT777.js.map +1 -0
  15. package/dist/{chunk-WZDCTAWZ.js → chunk-JNEHVQH5.js} +39 -2
  16. package/dist/chunk-JNEHVQH5.js.map +1 -0
  17. package/dist/{chunk-SVOWWZQJ.js → chunk-RN2FAMUU.js} +5 -280
  18. package/dist/chunk-RN2FAMUU.js.map +1 -0
  19. package/dist/chunk-TCOS2KFB.js +283 -0
  20. package/dist/chunk-TCOS2KFB.js.map +1 -0
  21. package/dist/chunk-WHIBTNYI.js +72 -0
  22. package/dist/chunk-WHIBTNYI.js.map +1 -0
  23. package/dist/{chunk-7W5VGYXW.js → chunk-YGEWIEAI.js} +45 -69
  24. package/dist/chunk-YGEWIEAI.js.map +1 -0
  25. package/dist/{chunk-YM3G5BLR.js → chunk-YLXPLHEL.js} +2 -2
  26. package/dist/{chunk-EHX4J5G5.js → chunk-ZAEBDO3Q.js} +12 -7
  27. package/dist/chunk-ZAEBDO3Q.js.map +1 -0
  28. package/dist/cli.js +65 -20
  29. package/dist/cli.js.map +1 -1
  30. package/dist/{config-B564P45C.js → config-SJAQMFWB.js} +11 -3
  31. package/dist/execute-VZ7SHLP4.js +13 -0
  32. package/dist/{generate-IN62ARBS.js → generate-LUK3XTIP.js} +7 -5
  33. package/dist/{generate-IN62ARBS.js.map → generate-LUK3XTIP.js.map} +1 -1
  34. package/dist/init-5KXCG36P.js +9 -0
  35. package/dist/plan-R34SAWRK.js +15 -0
  36. package/dist/pr-P4BDQXUQ.js +15 -0
  37. package/dist/pr-review-TOSBE6ZS.js +15 -0
  38. package/dist/prd-RYLULM75.js +15 -0
  39. package/dist/review-ZSC5ELQA.js +13 -0
  40. package/dist/review-ZSC5ELQA.js.map +1 -0
  41. package/dist/{run-GPBICAD6.js → run-SDRQDMSZ.js} +150 -55
  42. package/dist/run-SDRQDMSZ.js.map +1 -0
  43. package/package.json +1 -1
  44. package/prompts/execute.md +17 -5
  45. package/prompts/plan.md +1 -0
  46. package/prompts/pr-review.md +144 -0
  47. package/prompts/review.md +2 -0
  48. package/dist/analyze-VVG7UABS.js.map +0 -1
  49. package/dist/chunk-4HT4EVYV.js.map +0 -1
  50. package/dist/chunk-7W5VGYXW.js.map +0 -1
  51. package/dist/chunk-EHX4J5G5.js.map +0 -1
  52. package/dist/chunk-FPGHTTVV.js.map +0 -1
  53. package/dist/chunk-JTSAL3TI.js.map +0 -1
  54. package/dist/chunk-KWGRBNUG.js.map +0 -1
  55. package/dist/chunk-SVOWWZQJ.js.map +0 -1
  56. package/dist/chunk-WZDCTAWZ.js.map +0 -1
  57. package/dist/execute-PNOFNLZG.js +0 -12
  58. package/dist/init-5242XSYC.js +0 -9
  59. package/dist/plan-6KLONWPR.js +0 -14
  60. package/dist/pr-JLJLMFN3.js +0 -14
  61. package/dist/prd-TVJDYZXH.js +0 -14
  62. package/dist/review-FRV4ZMS2.js +0 -12
  63. package/dist/run-GPBICAD6.js.map +0 -1
  64. /package/dist/{chunk-2CSHLHYP.js.map → chunk-EHVKXFFN.js.map} +0 -0
  65. /package/dist/{chunk-YM3G5BLR.js.map → chunk-YLXPLHEL.js.map} +0 -0
  66. /package/dist/{config-B564P45C.js.map → config-SJAQMFWB.js.map} +0 -0
  67. /package/dist/{execute-PNOFNLZG.js.map → execute-VZ7SHLP4.js.map} +0 -0
  68. /package/dist/{init-5242XSYC.js.map → init-5KXCG36P.js.map} +0 -0
  69. /package/dist/{plan-6KLONWPR.js.map → plan-R34SAWRK.js.map} +0 -0
  70. /package/dist/{pr-JLJLMFN3.js.map → pr-P4BDQXUQ.js.map} +0 -0
  71. /package/dist/{prd-TVJDYZXH.js.map → pr-review-TOSBE6ZS.js.map} +0 -0
  72. /package/dist/{review-FRV4ZMS2.js.map → prd-RYLULM75.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/engine.ts","../src/ui/progress.ts","../src/ui/summary.ts","../src/core/executor.ts","../src/commands/execute.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { cp, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EngineConfig, ResolvedPaths, TaskPlan } from '../types.js';\nimport { printError, printInfo, printRetry, printSuccess, printWarning } from '../ui/logger.js';\nimport { printIterationHeader } from '../ui/progress.js';\nimport { printStartupHeader, printSummaryBox } from '../ui/summary.js';\nimport { isTransientFailure, retryDelaySeconds, sleep } from '../utils/retry.js';\nimport { executeClaude } from './executor.js';\nimport { applyPlaceholders, loadPrompt } from './prompt-resolver.js';\nimport { publishGitState } from './session-git.js';\nimport { getSessionPublisher } from './session-publisher.js';\nimport {\n allStoriesPass,\n clearLastError,\n hasPendingCorrection,\n initializeState,\n isoNow,\n loadTaskPlan,\n markIssueCompleted,\n markIssueInProgress,\n saveTaskPlan,\n setLastError,\n trimErrorMessage,\n} from './state-manager.js';\nimport { getOutputCallback, getStoryUpdateCallback, isVerbose } from './verbose.js';\n\n/**\n * Emit a message through the output callback if available, otherwise console.log.\n * Used for bare console.log calls in engine functions so they route through listr2.\n */\nfunction emitLog(message: string): void {\n const cb = getOutputCallback();\n if (cb) {\n // Skip empty lines in listr2 context — they don't render meaningfully\n if (message) {\n cb(message);\n }\n } else {\n console.log(message);\n }\n}\n\n/**\n * Initialize the progress file if it doesn't exist.\n */\nasync function ensureProgressFile(progressFile: string): Promise<void> {\n if (!existsSync(progressFile)) {\n const content = `# Issue Flow Progress Log\\nStarted: ${new Date().toString()}\\n---\\n`;\n await writeFile(progressFile, content, 'utf-8');\n }\n}\n\n/**\n * Archive previous run artifacts if the branch has changed.\n */\nasync function archiveIfBranchChanged(plan: TaskPlan, paths: ResolvedPaths): Promise<void> {\n const { lastBranchFile, archiveDir, prdFile, progressFile } = paths;\n\n if (!existsSync(lastBranchFile)) {\n return;\n }\n\n const currentBranch = plan.branchName ?? '';\n let lastBranch = '';\n\n try {\n lastBranch = (await readFile(lastBranchFile, 'utf-8')).trim();\n } catch {\n return;\n }\n\n if (currentBranch && lastBranch && currentBranch !== lastBranch) {\n const dateStr = new Date().toISOString().split('T')[0];\n const folderName = lastBranch.replace(/^issue\\//, '').replace(/[<>:\"|?*\\\\]/g, '_');\n const archiveFolder = join(archiveDir, `${dateStr}-${folderName}`);\n\n printInfo(`Archiving previous run: ${lastBranch}`);\n await mkdir(archiveFolder, { recursive: true });\n\n if (existsSync(prdFile)) {\n await cp(prdFile, join(archiveFolder, 'tasks.json'));\n }\n if (existsSync(progressFile)) {\n await cp(progressFile, join(archiveFolder, 'progress.txt'));\n }\n\n printInfo(` Archived to: ${archiveFolder}`);\n\n // Reset progress file for new run\n await writeFile(\n progressFile,\n `# Issue Flow Progress Log\\nStarted: ${new Date().toString()}\\n---\\n`,\n 'utf-8',\n );\n }\n}\n\n/**\n * Write the current branch to the last-branch tracking file.\n */\nasync function trackBranch(plan: TaskPlan, lastBranchFile: string): Promise<void> {\n const branch = plan.branchName ?? '';\n if (branch) {\n await writeFile(lastBranchFile, `${branch}\\n`, 'utf-8');\n }\n}\n\n/**\n * Run the issue-flow engine loop.\n *\n * This replicates the full execution flow:\n * 1. Load and initialize task plan state\n * 2. Check for early exit (already complete)\n * 3. Archive previous run if branch changed\n * 4. Resolve prompt\n * 5. Main loop: iterate, execute Claude, handle results\n * 6. Print summary\n */\nexport async function runEngine(config: EngineConfig, paths: ResolvedPaths): Promise<number> {\n // Load task plan\n if (!existsSync(paths.prdFile)) {\n printError(`PRD file not found at ${paths.prdFile}`);\n if (config.issueNumber) {\n emitLog(`Have you run the resolve-issue skill for issue #${config.issueNumber} first?`);\n }\n return 1;\n }\n\n let plan = await loadTaskPlan(paths.prdFile);\n plan = initializeState(plan);\n await saveTaskPlan(paths.prdFile, plan);\n\n // Check if already completed. A pending correction (a failed review whose\n // findings haven't been addressed yet) always overrides this, even though\n // every userStories[].passes is already true — otherwise a correction\n // cycle's re-run of execute would exit here without ever looking at what\n // the review found.\n if (plan.issueStatus === 'completed' && allStoriesPass(plan) && !hasPendingCorrection(plan)) {\n emitLog(`Issue already marked complete in ${paths.prdFile}`);\n return 0;\n }\n\n // Warn if marked complete but stories still pending\n if (plan.issueStatus === 'completed' && !allStoriesPass(plan)) {\n printWarning(\n 'Issue marked completed but some stories are still pending. Resetting to in_progress.',\n );\n plan = markIssueInProgress(plan);\n plan = setLastError(\n plan,\n 'invalid_completion_state',\n 'tasks.json claimed the issue was completed before every story had passes=true.',\n );\n await saveTaskPlan(paths.prdFile, plan);\n }\n\n // Check if all stories already pass\n if (allStoriesPass(plan) && !hasPendingCorrection(plan)) {\n emitLog('All user stories already pass. Marking issue as completed.');\n plan = markIssueCompleted(plan);\n await saveTaskPlan(paths.prdFile, plan);\n return 0;\n }\n\n // Archive previous run if branch changed\n await archiveIfBranchChanged(plan, paths);\n\n // Track current branch\n await trackBranch(plan, paths.lastBranchFile);\n\n // Initialize progress file\n await ensureProgressFile(paths.progressFile);\n\n // Load prompt template\n const promptTemplate = await loadPrompt('execute');\n\n // Print startup header\n printStartupHeader(config, plan);\n\n const startTime = Date.now();\n let i = 0;\n let retryCount = 0;\n let totalRetryCount = 0;\n\n // Main loop\n while (true) {\n // Check iteration limit\n if (config.maxIterations !== undefined && i >= config.maxIterations) {\n break;\n }\n\n i++;\n getSessionPublisher().publish({ type: 'iteration:start', at: isoNow(), iteration: i });\n\n // Re-read plan to get latest state\n plan = await loadTaskPlan(paths.prdFile);\n\n if (isVerbose()) {\n printIterationHeader(i, config.maxIterations, plan.userStories);\n }\n\n // Apply placeholders to prompt\n const prompt = applyPlaceholders(promptTemplate, {\n __PRD_FILE__: paths.prdFile,\n __PROGRESS_FILE__: paths.progressFile,\n });\n\n const iterationStartedAt = isoNow();\n plan = markIssueInProgress(plan, iterationStartedAt);\n await saveTaskPlan(paths.prdFile, plan);\n\n // Execute Claude\n const result = await executeClaude(prompt);\n\n if (result.exitCode !== 0) {\n const errorMessage = trimErrorMessage(result.output);\n\n if (isTransientFailure(result.exitCode, result.output)) {\n retryCount++;\n totalRetryCount++;\n plan = await loadTaskPlan(paths.prdFile);\n plan = setLastError(plan, 'transient_claude_failure', errorMessage);\n await saveTaskPlan(paths.prdFile, plan);\n\n if (!config.retryForever && retryCount > config.retryLimit) {\n const elapsed = Math.floor((Date.now() - startTime) / 1000);\n plan = await loadTaskPlan(paths.prdFile);\n printSummaryBox(\n 'failed',\n i,\n totalRetryCount,\n elapsed,\n plan,\n `Exceeded retry limit (${config.retryLimit}) on transient errors`,\n );\n return result.exitCode;\n }\n\n const delaySeconds = retryDelaySeconds(\n retryCount,\n config.backoffBaseSeconds,\n config.backoffMaxSeconds,\n );\n getSessionPublisher().publish({\n type: 'retry',\n at: isoNow(),\n attempt: retryCount,\n delaySeconds,\n reason: 'transient_claude_failure',\n });\n\n if (isVerbose()) {\n emitLog('');\n }\n printRetry(\n `Transient Claude failure on iteration ${i} (attempt ${retryCount}). Retrying in ${delaySeconds}s.`,\n );\n\n // Stay within current iteration budget\n i--;\n await sleep(delaySeconds);\n continue;\n }\n\n // Fatal failure\n plan = await loadTaskPlan(paths.prdFile);\n plan = setLastError(plan, 'fatal_claude_failure', errorMessage);\n await saveTaskPlan(paths.prdFile, plan);\n\n const elapsed = Math.floor((Date.now() - startTime) / 1000);\n printSummaryBox(\n 'failed',\n i,\n totalRetryCount,\n elapsed,\n plan,\n `Claude CLI failed with exit code ${result.exitCode}`,\n );\n return result.exitCode;\n }\n\n // Success — reset retry counter\n retryCount = 0;\n plan = await loadTaskPlan(paths.prdFile);\n plan = clearLastError(plan, iterationStartedAt);\n await saveTaskPlan(paths.prdFile, plan);\n\n // Notify story progress listeners (e.g., listr2 subtasks)\n const storyUpdateCb = getStoryUpdateCallback();\n if (storyUpdateCb) {\n storyUpdateCb(plan.userStories);\n }\n getSessionPublisher().publish({\n type: 'stories:update',\n at: isoNow(),\n stories: plan.userStories,\n });\n getSessionPublisher().publish({ type: 'iteration:end', at: isoNow(), iteration: i });\n // Low-frequency commit/PR enrichment: iteration end is one of the only\n // two sanctioned points (the other is phase boundaries in run.ts).\n await publishGitState(getSessionPublisher());\n\n // Check for completion signal\n if (result.output.includes('<promise>COMPLETE</promise>')) {\n plan = await loadTaskPlan(paths.prdFile);\n if (allStoriesPass(plan) && !hasPendingCorrection(plan)) {\n plan = markIssueCompleted(plan);\n await saveTaskPlan(paths.prdFile, plan);\n\n const elapsed = Math.floor((Date.now() - startTime) / 1000);\n printSummaryBox('success', i, totalRetryCount, elapsed, plan);\n return 0;\n }\n\n plan = setLastError(\n plan,\n 'invalid_completion_signal',\n hasPendingCorrection(plan)\n ? 'Claude returned <promise>COMPLETE</promise> while lastReviewFindings was still set.'\n : 'Claude returned <promise>COMPLETE</promise> before every story had passes=true.',\n );\n await saveTaskPlan(paths.prdFile, plan);\n\n emitLog('');\n printWarning(\n hasPendingCorrection(plan)\n ? 'Claude returned a completion signal, but lastReviewFindings is still set. Ignoring completion and continuing.'\n : 'Claude returned a completion signal, but tasks.json still has pending stories. Ignoring completion and continuing.',\n );\n }\n\n if (isVerbose()) {\n printSuccess(`Iteration ${i} complete. Continuing...`);\n }\n await sleep(2);\n }\n\n // Reached max iterations\n const elapsed = Math.floor((Date.now() - startTime) / 1000);\n plan = await loadTaskPlan(paths.prdFile);\n printSummaryBox(\n 'incomplete',\n config.maxIterations ?? i,\n totalRetryCount,\n elapsed,\n plan,\n 'Reached max iterations without completing all tasks.',\n );\n return 1;\n}\n","import chalk from 'chalk';\nimport { getOutputCallback } from '../core/verbose.js';\nimport type { UserStory } from '../types.js';\nimport { getIcons, useColor, useUnicode } from './logger.js';\n\n/**\n * Print a visual progress bar showing passed/total stories.\n */\nexport function printProgressBar(passed: number, total: number): string {\n const barWidth = 20;\n let pct = 0;\n let filled = 0;\n\n if (total > 0) {\n pct = Math.floor((passed * 100) / total);\n filled = Math.floor((passed * barWidth) / total);\n }\n\n const empty = barWidth - filled;\n const fillChar = useUnicode() ? '\\u2588' : '#';\n const emptyChar = useUnicode() ? '\\u2591' : '-';\n\n const bar = fillChar.repeat(filled) + emptyChar.repeat(empty);\n const text = `${passed}/${total} (${pct}%)`;\n\n if (useColor()) {\n return `${chalk.green(bar)} ${text}`;\n }\n return `${bar} ${text}`;\n}\n\n/**\n * Print the iteration header showing story statuses and progress bar.\n *\n * When a global output callback is set (e.g., inside a listr2 task),\n * output is routed through it instead of console.log.\n */\nexport function printIterationHeader(\n iteration: number,\n maxIter: number | undefined,\n stories: UserStory[],\n): void {\n const icons = getIcons();\n const colored = useColor();\n const cb = getOutputCallback();\n\n const iterLabel = maxIter ? `Iteration ${iteration} of ${maxIter}` : `Iteration ${iteration}`;\n\n const total = stories.length;\n const passed = stories.filter((s) => s.passes).length;\n\n const lines: string[] = [];\n\n if (colored) {\n lines.push(chalk.blue(`\\u2501\\u2501\\u2501 ${icons.start} ${iterLabel} \\u2501\\u2501\\u2501`));\n } else {\n lines.push(`--- ${icons.start} ${iterLabel} ---`);\n }\n\n // Display each story status\n let foundFirstPending = false;\n for (const story of stories) {\n let icon: string;\n let colorFn: (s: string) => string;\n\n if (story.passes) {\n icon = icons.success;\n colorFn = colored ? chalk.green : (s: string) => s;\n } else if (!foundFirstPending) {\n // First non-passing story = current in-progress\n icon = icons.pending;\n colorFn = colored ? chalk.yellow : (s: string) => s;\n foundFirstPending = true;\n } else {\n // Not yet reached\n icon = icons.notReached;\n colorFn = colored ? chalk.gray : (s: string) => s;\n }\n\n lines.push(colorFn(` ${icon} ${story.id}: ${story.title}`));\n }\n\n lines.push(` ${printProgressBar(passed, total)}`);\n\n if (cb) {\n // Send as single output to listr2 task context\n cb(lines.join('\\n'));\n } else {\n console.log('');\n for (const line of lines) {\n console.log(line);\n }\n console.log('');\n }\n}\n","import chalk from 'chalk';\nimport type { EngineConfig, PrReviewRecommendation, TaskPlan } from '../types.js';\nimport {\n formatDuration,\n getIcons,\n getTermWidth,\n printSuccess,\n printWarning,\n useColor,\n useUnicode,\n} from './logger.js';\n\n/**\n * Truncate or pad a string to fit within a given width.\n */\nfunction fitLine(text: string, width: number): string {\n if (text.length > width) {\n return text.substring(0, width);\n }\n return text.padEnd(width);\n}\n\n/**\n * Print a box with border characters around content lines.\n * Lines equal to \"---\" render as separator rows.\n */\nexport function printBox(lines: string[]): void {\n const colored = useColor();\n const unicode = useUnicode();\n const termWidth = getTermWidth();\n\n // Find max content width\n let maxContentWidth = 0;\n for (const line of lines) {\n if (line !== '---' && line.length > maxContentWidth) {\n maxContentWidth = line.length;\n }\n }\n\n // Cap to terminal width (border + padding = 4 chars)\n const available = termWidth - 4;\n if (maxContentWidth > available) {\n maxContentWidth = available;\n }\n if (maxContentWidth < 20) {\n maxContentWidth = 20;\n }\n\n // Box drawing characters\n const tl = unicode ? '\\u256D' : '+';\n const tr = unicode ? '\\u256E' : '+';\n const bl = unicode ? '\\u2570' : '+';\n const br = unicode ? '\\u256F' : '+';\n const h = unicode ? '\\u2500' : '-';\n const v = unicode ? '\\u2502' : '|';\n const sepL = unicode ? '\\u251C' : '+';\n const sepR = unicode ? '\\u2524' : '+';\n\n const hrule = h.repeat(maxContentWidth + 2);\n\n const blue = colored ? chalk.blue : (s: string) => s;\n const _reset = (s: string) => s;\n\n // Top border\n console.log(blue(`${tl}${hrule}${tr}`));\n\n // Content lines\n for (const line of lines) {\n if (line === '---') {\n console.log(blue(`${sepL}${hrule}${sepR}`));\n } else {\n const fitted = fitLine(line, maxContentWidth);\n console.log(`${blue(v)} ${fitted} ${blue(v)}`);\n }\n }\n\n // Bottom border\n console.log(blue(`${bl}${hrule}${br}`));\n}\n\n/**\n * Print the startup header box showing engine configuration.\n */\nexport function printStartupHeader(config: EngineConfig, plan: TaskPlan): void {\n const icons = getIcons();\n\n const storiesTotal = plan.userStories.length;\n const storiesPassing = plan.userStories.filter((s) => s.passes).length;\n const branchName = plan.branchName ?? 'N/A';\n\n const issueLabel = config.issueNumber ? `Issue #${config.issueNumber}` : 'Standalone mode';\n\n const maxIterLabel =\n config.maxIterations !== undefined ? String(config.maxIterations) : 'unlimited';\n\n const retryLabel = config.retryForever\n ? 'unlimited retries'\n : `${config.retryLimit} consecutive retries`;\n\n printBox([\n `${icons.start} Issue Flow`,\n '---',\n `Issue: ${issueLabel}`,\n `Branch: ${branchName}`,\n `Stories: ${storiesPassing}/${storiesTotal} passing`,\n `Iterations: ${maxIterLabel}`,\n `Retries: ${retryLabel}`,\n ]);\n}\n\n// Re-export formatDuration from logger to maintain backwards compatibility\nexport { formatDuration } from './logger.js';\n\n/**\n * Print the final summary box.\n */\nexport function printSummaryBox(\n status: 'success' | 'incomplete' | 'failed',\n iterations: number,\n totalRetries: number,\n elapsedSeconds: number,\n plan: TaskPlan,\n extraInfo?: string,\n): void {\n const icons = getIcons();\n\n const storiesTotal = plan.userStories.length;\n const storiesPassing = plan.userStories.filter((s) => s.passes).length;\n const duration = formatDuration(elapsedSeconds);\n\n let statusIcon: string;\n let statusLabel: string;\n\n switch (status) {\n case 'success':\n statusIcon = icons.success;\n statusLabel = 'Completed';\n break;\n case 'incomplete':\n statusIcon = icons.warn;\n statusLabel = 'Incomplete';\n break;\n case 'failed':\n statusIcon = icons.fail;\n statusLabel = 'Failed';\n break;\n }\n\n const boxLines = [\n `${icons.end} Issue Flow Summary`,\n '---',\n `Status: ${statusIcon} ${statusLabel}`,\n `Stories: ${storiesPassing}/${storiesTotal} passing`,\n `Iterations: ${iterations}`,\n `Duration: ${duration}`,\n `Retries: ${totalRetries}`,\n ];\n\n if (extraInfo) {\n boxLines.push('---');\n boxLines.push(extraInfo);\n }\n\n console.log('');\n printBox(boxLines);\n}\n\n/**\n * What the optional `pr-review` phase produced, as far as the summary cares.\n * `null` in `recommendation` means the verdict could not be recovered from\n * disk — the exit code still told us whether changes were requested.\n */\nexport interface RunSummaryPrReview {\n requestedChanges: boolean;\n recommendation: PrReviewRecommendation | null;\n reportPath: string | null;\n}\n\n/**\n * Everything the final `run` summary prints, already resolved by the caller.\n */\nexport interface RunSummaryInfo {\n issueNumber: string;\n /** 'unknown' when the branch could not be detected. */\n branchName: string;\n /** Pipelines run with --no-branch open no Pull Request. */\n noBranch: boolean;\n storyCount: number;\n elapsedSeconds: number;\n /** 'unknown' when no Pull Request could be resolved. */\n prUrl: string;\n /** Only set when the pr-review phase actually ran. */\n prReview?: RunSummaryPrReview | null;\n}\n\n/**\n * Detail lines of the final `run` summary (everything below the headline).\n *\n * Kept separate from the printing so the shape is testable without capturing\n * stdout. Without `prReview` the lines are exactly the ones the pipeline has\n * always printed.\n */\nexport function buildRunSummaryLines(info: RunSummaryInfo): string[] {\n const lines = [\n ` Branch: ${info.branchName}${info.noBranch ? ' (current)' : ''}`,\n ` Stories: ${info.storyCount}`,\n ` Duration: ${formatDuration(info.elapsedSeconds)}`,\n ];\n\n if (!info.noBranch) {\n lines.push(` PR: ${info.prUrl}`);\n }\n\n const review = info.prReview;\n if (review) {\n // The exit code is the source of truth for the verdict, so a report whose\n // recommendation could not be read still reports REQUEST_CHANGES.\n const verdict =\n review.recommendation ?? (review.requestedChanges ? 'REQUEST_CHANGES' : 'unknown');\n lines.push(` Review: ${verdict}`);\n if (review.reportPath !== null) {\n lines.push(` Report: ${review.reportPath}`);\n }\n }\n\n return lines;\n}\n\n/**\n * Print the final summary of a `run` (the flat listing, not the box used by\n * the execute engine).\n */\nexport function printRunSummary(info: RunSummaryInfo): void {\n console.log('');\n if (info.prReview?.requestedChanges) {\n printWarning(\n `Pipeline finished for issue #${info.issueNumber}, but the PR review requested changes.`,\n );\n } else {\n printSuccess(`Pipeline complete for issue #${info.issueNumber}!`);\n }\n for (const line of buildRunSummaryLines(info)) {\n console.log(line);\n }\n}\n","import { execa } from 'execa';\nimport type { ClaudeResult } from '../types.js';\nimport { getOutputCallback } from './verbose.js';\n\n/**\n * Execute Claude CLI with a prompt piped to stdin.\n *\n * Runs: echo $PROMPT | claude --dangerously-skip-permissions --print\n *\n * Captures combined stdout+stderr output. Does not throw on non-zero exit codes.\n * Output is returned via ClaudeResult and routed through the output callback infrastructure.\n */\nexport async function executeClaude(prompt: string): Promise<ClaudeResult> {\n const result = await execa('claude', ['--dangerously-skip-permissions', '--print'], {\n input: prompt,\n reject: false,\n timeout: 0, // No timeout — let the engine handle iteration limits\n stripFinalNewline: false,\n });\n\n // Combine stdout and stderr to match Bash's 2>&1 behavior\n const stdout = result.stdout?.toString() ?? '';\n const stderr = result.stderr?.toString() ?? '';\n const output = stdout + (stderr ? `\\n${stderr}` : '');\n\n // Forward output through the global callback (listr2 renderer) when active\n const onOutput = getOutputCallback();\n if (onOutput) {\n const trimmed = output.trim();\n if (trimmed) {\n onOutput(trimmed);\n }\n }\n\n return {\n exitCode: result.exitCode ?? 1,\n output,\n };\n}\n","import { createConfig, resolvePaths, validateDependencies } from '../config.js';\nimport { runEngine } from '../core/engine.js';\nimport { allStoriesPass, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { printError } from '../ui/logger.js';\n\nexport interface ExecuteOptions {\n issue?: string;\n maxIterations?: number;\n retryLimit?: number;\n retryForever?: boolean;\n}\n\nexport async function runExecute(\n positionalMaxIter: number | undefined,\n options: ExecuteOptions,\n): Promise<number> {\n const errors = await validateDependencies();\n if (errors.length > 0) {\n printError('The following required tools are not installed:');\n for (const err of errors) {\n console.log(err);\n }\n return 1;\n }\n\n const maxIterations = options.maxIterations ?? positionalMaxIter;\n\n const config = createConfig({\n issueNumber: options.issue,\n maxIterations,\n retryLimit: options.retryLimit,\n retryForever: options.retryForever,\n });\n\n const paths = await resolvePaths(config);\n const exitCode = await runEngine(config, paths);\n\n // Update pipeline state if all stories pass\n if (exitCode === 0 && config.issueNumber) {\n try {\n const plan = await loadTaskPlan(paths.prdFile);\n if (allStoriesPass(plan)) {\n plan.pipeline.executionCompleted = true;\n await saveTaskPlan(paths.prdFile, plan);\n }\n } catch {\n // Non-critical — engine already handled state\n }\n }\n\n return exitCode;\n}\n"],"mappings":"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","names":["chalk","chalk","elapsed"]}
@@ -4,7 +4,7 @@ import {
4
4
  printInfo,
5
5
  printSuccess,
6
6
  printWarning
7
- } from "./chunk-WZDCTAWZ.js";
7
+ } from "./chunk-JNEHVQH5.js";
8
8
 
9
9
  // src/commands/init.ts
10
10
  import { execa } from "execa";
@@ -132,4 +132,4 @@ async function runInit(source = "github") {
132
132
  export {
133
133
  runInit
134
134
  };
135
- //# sourceMappingURL=chunk-YM3G5BLR.js.map
135
+ //# sourceMappingURL=chunk-YLXPLHEL.js.map
@@ -2,33 +2,38 @@
2
2
  import {
3
3
  readFileWithGrace,
4
4
  runPhaseWithRetry
5
- } from "./chunk-2CSHLHYP.js";
5
+ } from "./chunk-EHVKXFFN.js";
6
6
  import {
7
7
  issuePlaceholders,
8
- resolveCommandIssue,
9
- runHeadless
10
- } from "./chunk-SVOWWZQJ.js";
8
+ resolveCommandIssue
9
+ } from "./chunk-RN2FAMUU.js";
11
10
  import {
12
11
  isTransientFailure
13
12
  } from "./chunk-HDV3YDTG.js";
13
+ import {
14
+ runHeadless
15
+ } from "./chunk-TCOS2KFB.js";
14
16
  import {
15
17
  applyPlaceholders,
16
18
  loadPrompt
17
19
  } from "./chunk-SLHZPB3C.js";
20
+ import {
21
+ getIssueDir
22
+ } from "./chunk-2AVZZAC6.js";
18
23
  import {
19
24
  getGlobalTimeout,
20
25
  loadTaskPlan,
21
26
  printError,
22
27
  printSuccess,
23
28
  saveTaskPlan
24
- } from "./chunk-WZDCTAWZ.js";
29
+ } from "./chunk-JNEHVQH5.js";
25
30
 
26
31
  // src/commands/prd.ts
27
32
  import { mkdir } from "fs/promises";
28
33
  import { join } from "path";
29
34
  async function runPrd(issue, resolvedIssue) {
30
35
  const issueNumber = issue.replace(/^#/, "");
31
- const issueDir = join("issues", issueNumber);
36
+ const issueDir = await getIssueDir(issueNumber);
32
37
  const prdPath = join(issueDir, "prd.md");
33
38
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
34
39
  if (!resolution.ok) {
@@ -88,4 +93,4 @@ async function runPrd(issue, resolvedIssue) {
88
93
  export {
89
94
  runPrd
90
95
  };
91
- //# sourceMappingURL=chunk-EHX4J5G5.js.map
96
+ //# sourceMappingURL=chunk-ZAEBDO3Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/prd.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { readFileWithGrace, runPhaseWithRetry } from '../core/phase-runner.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.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 { printError, printSuccess } from '../ui/logger.js';\nimport { getIssueDir } from '../utils/git.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\nexport async function runPrd(issue: string, resolvedIssue?: ResolvedIssue): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = await getIssueDir(issueNumber);\n const prdPath = join(issueDir, 'prd.md');\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n await mkdir(issueDir, { recursive: true });\n\n const template = await loadPrompt('prd');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __PRD_PATH__: prdPath,\n ...issuePlaceholders(resolution.resolved),\n });\n\n const outcome = await runPhaseWithRetry({\n phase: 'prd',\n attempt: async () => {\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n statusMessage: `Generating PRD for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `PRD generation failed: ${result.error}`,\n };\n }\n\n // Verify the file was created. A brief grace period absorbs a lag\n // between the Write tool completing and this process seeing it; if it\n // still isn't there, treat it as recoverable and let the phase retry.\n try {\n const content = await readFileWithGrace(prdPath);\n if (content.length < 10) {\n return { ok: false, transient: true, error: 'PRD file was created but appears empty' };\n }\n } catch {\n return { ok: false, transient: true, error: `PRD file was not created at ${prdPath}` };\n }\n\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `PRD generation failed for issue #${issueNumber}`);\n return 1;\n }\n\n // Update pipeline state\n const tasksPath = join(issueDir, 'tasks.json');\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prdCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist yet\n }\n\n printSuccess(`PRD saved to ${prdPath}`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,YAAY;AAYrB,eAAsB,OAAO,OAAe,eAAgD;AAC1F,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,MAAM,YAAY,WAAW;AAC9C,QAAM,UAAU,KAAK,UAAU,QAAQ;AAEvC,QAAM,aAAa,MAAM,oBAAoB,aAAa,aAAa;AACvE,MAAI,CAAC,WAAW,IAAI;AAClB,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAEzC,QAAM,WAAW,MAAM,WAAW,KAAK;AACvC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,cAAc;AAAA,IACd,GAAG,kBAAkB,WAAW,QAAQ;AAAA,EAC1C,CAAC;AAED,QAAM,UAAU,MAAM,kBAAkB;AAAA,IACtC,OAAO;AAAA,IACP,SAAS,YAAY;AACnB,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B;AAAA,QACA,UAAU;AAAA,QACV,SAAS,iBAAiB,KAAK;AAAA,QAC/B,cAAc;AAAA,QACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,QACtD,eAAe,6BAA6B,WAAW;AAAA,MACzD,CAAC;AAED,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,WAAW,mBAAmB,GAAG,OAAO,SAAS,EAAE;AAAA,UACnD,OAAO,0BAA0B,OAAO,KAAK;AAAA,QAC/C;AAAA,MACF;AAKA,UAAI;AACF,cAAM,UAAU,MAAM,kBAAkB,OAAO;AAC/C,YAAI,QAAQ,SAAS,IAAI;AACvB,iBAAO,EAAE,IAAI,OAAO,WAAW,MAAM,OAAO,yCAAyC;AAAA,QACvF;AAAA,MACF,QAAQ;AACN,eAAO,EAAE,IAAI,OAAO,WAAW,MAAM,OAAO,+BAA+B,OAAO,GAAG;AAAA,MACvF;AAEA,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,QAAQ,IAAI;AACf,eAAW,QAAQ,SAAS,oCAAoC,WAAW,EAAE;AAC7E,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,eAAe;AAC7B,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,eAAa,gBAAgB,OAAO,EAAE;AACtC,SAAO;AACT;","names":[]}
package/dist/cli.js CHANGED
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  setIssuesCliOverrides,
4
4
  setWebCliOverrides
5
- } from "./chunk-4HT4EVYV.js";
5
+ } from "./chunk-2AVZZAC6.js";
6
6
  import {
7
7
  printError,
8
8
  setGlobalTimeout,
9
9
  setVerbose
10
- } from "./chunk-WZDCTAWZ.js";
10
+ } from "./chunk-JNEHVQH5.js";
11
11
 
12
12
  // src/cli.ts
13
13
  import { createRequire } from "module";
@@ -17,6 +17,22 @@ import { Command, InvalidArgumentError } from "commander";
17
17
  function resolveNoBranch(options) {
18
18
  return options.branch === false ? true : void 0;
19
19
  }
20
+ var CliFlagError = class extends Error {
21
+ constructor(message) {
22
+ super(message);
23
+ this.name = "CliFlagError";
24
+ }
25
+ };
26
+ function resolveRunPhaseFlags(options) {
27
+ const noBranch = resolveNoBranch(options);
28
+ const prReview = options.prReview === true ? true : void 0;
29
+ if (prReview === true && noBranch === true) {
30
+ throw new CliFlagError(
31
+ "--pr-review cannot be combined with --no-branch: --no-branch skips the pr phase, so there is no Pull Request to review."
32
+ );
33
+ }
34
+ return { noBranch, prReview };
35
+ }
20
36
 
21
37
  // src/issues/cli-flags.ts
22
38
  var IssueFlagError = class extends Error {
@@ -143,8 +159,8 @@ withIssueOptions(
143
159
  program.command("init").description("Verify that all prerequisites (claude, gh, git) are met")
144
160
  )
145
161
  ).action(async () => {
146
- const { loadIssuesConfig } = await import("./config-B564P45C.js");
147
- const { runInit } = await import("./init-5242XSYC.js");
162
+ const { loadIssuesConfig } = await import("./config-SJAQMFWB.js");
163
+ const { runInit } = await import("./init-5KXCG36P.js");
148
164
  const { preferredProvider } = await loadIssuesConfig();
149
165
  const code = await runInit(preferredProvider);
150
166
  process.exit(code);
@@ -162,30 +178,47 @@ withGlobalOptions(
162
178
  }
163
179
  throw error;
164
180
  }
165
- const { runGenerate } = await import("./generate-IN62ARBS.js");
181
+ const { runGenerate } = await import("./generate-LUK3XTIP.js");
166
182
  const code = await runGenerate(options.prompt, target);
167
183
  process.exit(code);
168
184
  });
169
185
  withWebOptions(
170
186
  withIssueOptions(
171
187
  withGlobalOptions(
172
- program.command("run").description("Execute the full pipeline: prd \u2192 plan \u2192 execute \u2192 review \u2192 pr").argument("<issue>", "Issue number").option("--mode <mode>", "Execution mode: auto | manual", "auto").option("--from <phase>", "Resume from a specific phase").option(
173
- "--no-branch",
174
- "Run pipeline on current branch without creating a new branch or PR"
175
- )
188
+ program.command("run").description(
189
+ "Execute the full pipeline: prd \u2192 plan \u2192 execute \u2192 review \u2192 pr (\u2192 pr-review, optional)"
190
+ ).argument("<issue>", "Issue number").option("--mode <mode>", "Execution mode: auto | manual", "auto").option("--from <phase>", "Resume from a specific phase").option("--no-branch", "Run pipeline on current branch without creating a new branch or PR").option("--pr-review", "Review the created Pull Request after the pr phase")
176
191
  )
177
192
  )
178
- ).action(async (issue, options) => {
179
- const { runPipeline } = await import("./run-GPBICAD6.js");
180
- const code = await runPipeline(issue, options.mode, options.from, resolveNoBranch(options));
181
- process.exit(code);
182
- });
193
+ ).action(
194
+ async (issue, options) => {
195
+ let phases;
196
+ try {
197
+ phases = resolveRunPhaseFlags(options);
198
+ } catch (error) {
199
+ if (error instanceof CliFlagError) {
200
+ printError(error.message);
201
+ process.exit(1);
202
+ }
203
+ throw error;
204
+ }
205
+ const { runPipeline } = await import("./run-SDRQDMSZ.js");
206
+ const code = await runPipeline(
207
+ issue,
208
+ options.mode,
209
+ options.from,
210
+ phases.noBranch,
211
+ phases.prReview
212
+ );
213
+ process.exit(code);
214
+ }
215
+ );
183
216
  withIssueOptions(
184
217
  withGlobalOptions(
185
218
  program.command("analyze").description("Analyze an issue via Claude Code Headless").argument("<issue>", "Issue number")
186
219
  )
187
220
  ).action(async (issue) => {
188
- const { runAnalyze } = await import("./analyze-VVG7UABS.js");
221
+ const { runAnalyze } = await import("./analyze-R25HDGBJ.js");
189
222
  const code = await runAnalyze(issue);
190
223
  process.exit(code);
191
224
  });
@@ -194,7 +227,7 @@ withIssueOptions(
194
227
  program.command("prd").description("Generate a PRD from an analyzed issue via Claude Code Headless").argument("<issue>", "Issue number")
195
228
  )
196
229
  ).action(async (issue) => {
197
- const { runPrd } = await import("./prd-TVJDYZXH.js");
230
+ const { runPrd } = await import("./prd-RYLULM75.js");
198
231
  const code = await runPrd(issue);
199
232
  process.exit(code);
200
233
  });
@@ -203,7 +236,7 @@ withIssueOptions(
203
236
  program.command("plan").description("Convert a PRD to a tasks.json task plan via Claude Code Headless").argument("<issue>", "Issue number")
204
237
  )
205
238
  ).action(async (issue) => {
206
- const { runPlan } = await import("./plan-6KLONWPR.js");
239
+ const { runPlan } = await import("./plan-R34SAWRK.js");
207
240
  const code = await runPlan(issue);
208
241
  process.exit(code);
209
242
  });
@@ -222,7 +255,7 @@ withWebOptions(
222
255
  ).action(
223
256
  async (positionalMaxIter, options) => {
224
257
  try {
225
- const { runExecute } = await import("./execute-PNOFNLZG.js");
258
+ const { runExecute } = await import("./execute-VZ7SHLP4.js");
226
259
  const code = await runExecute(positionalMaxIter, options);
227
260
  process.exit(code);
228
261
  } catch (error) {
@@ -236,7 +269,7 @@ withIssueOptions(
236
269
  program.command("review").description("Validate an issue resolution via Claude Code Headless").argument("<issue>", "Issue number")
237
270
  )
238
271
  ).action(async (issue) => {
239
- const { runReview } = await import("./review-FRV4ZMS2.js");
272
+ const { runReview } = await import("./review-ZSC5ELQA.js");
240
273
  const code = await runReview(issue);
241
274
  process.exit(code);
242
275
  });
@@ -245,9 +278,21 @@ withIssueOptions(
245
278
  program.command("pr").description("Create a pull request via Claude Code Headless").argument("<issue>", "Issue number")
246
279
  )
247
280
  ).action(async (issue) => {
248
- const { runPr } = await import("./pr-JLJLMFN3.js");
281
+ const { runPr } = await import("./pr-P4BDQXUQ.js");
249
282
  const code = await runPr(issue);
250
283
  process.exit(code);
251
284
  });
285
+ withGlobalOptions(
286
+ program.command("pr-review").description("Review a Pull Request as a whole via Claude Code Headless").argument("[pr]", "Pull Request number (discovered from the session when omitted)").option("--issue <n>", "Issue the Pull Request belongs to").option("--round <n>", "Rewrite a specific review round instead of appending a new one").option("--yes", "Skip the confirmation of the discovered Pull Request").option(
287
+ "--fail-on <level>",
288
+ "Verdict that fails the command: request-changes | suggestions | none"
289
+ )
290
+ ).action(
291
+ async (pr, options) => {
292
+ const { runPrReview } = await import("./pr-review-TOSBE6ZS.js");
293
+ const code = await runPrReview(pr, options);
294
+ process.exit(code);
295
+ }
296
+ );
252
297
  program.parse();
253
298
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/cli-options.ts","../src/issues/cli-flags.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { resolveNoBranch } from './cli-options.js';\nimport { setIssuesCliOverrides, setWebCliOverrides } from './config.js';\nimport { setGlobalTimeout, setVerbose } from './core/verbose.js';\nimport {\n IssueFlagError,\n resolveGenerateTarget,\n resolveIssuesOverrides,\n} from './issues/cli-flags.js';\nimport type { IssueGenerateTarget } from './issues/types.js';\nimport type { WebConfig } from './schemas.js';\nimport { printError } from './ui/logger.js';\n\nconst require = createRequire(import.meta.url);\nconst { version } = require('../package.json') as { version: string };\n\n/**\n * Parse a numeric string, throwing InvalidArgumentError if not a valid number.\n */\nfunction parseInteger(value: string): number {\n const parsed = parseInt(value, 10);\n if (Number.isNaN(parsed) || parsed < 0) {\n throw new InvalidArgumentError('Must be a non-negative integer.');\n }\n return parsed;\n}\n\n/**\n * Add shared options (--verbose) to a subcommand.\n */\nfunction withGlobalOptions(cmd: Command): Command {\n return cmd\n .option('-v, --verbose', 'Show Claude progress output in real time')\n .option(\n '-t, --timeout <seconds>',\n 'Override headless timeout in seconds (0 = no limit)',\n parseInteger,\n );\n}\n\n/**\n * Add web monitoring options to a subcommand (run and execute only).\n * The values are resolved into CLI overrides by the preAction hook below;\n * loadWebConfig() applies the flag > env > file > defaults precedence.\n */\nfunction withWebOptions(cmd: Command): Command {\n return cmd\n .option('--web', 'Enable the web monitoring server')\n .option('--serve', 'Alias for --web')\n .option('--port <n>', 'Web server port (default: 3737)', parseInteger)\n .option('--host <h>', 'Web server host (default: 0.0.0.0)')\n .option('--refresh <s>', 'Suggested UI polling interval in seconds', parseInteger)\n .option('--web-log-limit <n>', 'Max log entries kept in the snapshot', parseInteger)\n .option('--web-no-logs', 'Exclude logs from the published snapshot');\n}\n\n/**\n * Add the Issue provider options to a subcommand.\n *\n * Declared once here (like withWebOptions) so no command repeats the flag\n * list; the preAction hook below turns them into config overrides, and\n * loadIssuesConfig() applies the flag > .issue-flow.json > defaults precedence.\n */\nfunction withIssueOptions(cmd: Command): Command {\n return cmd\n .option('--local', 'Prefer the local file Issue provider')\n .option('--github', 'Prefer the GitHub Issue provider')\n .option('--prefer-local', 'On divergence, use the local version without asking')\n .option('--prefer-github', 'On divergence, use the GitHub version without asking')\n .option('--ask', 'On divergence, ask which version to use (interactive only)');\n}\n\n/**\n * Extract the web-related CLI flags from a command's options, keeping only\n * the ones the user actually set.\n */\nfunction resolveWebOverrides(opts: Record<string, unknown>): Partial<WebConfig> {\n const overrides: Partial<WebConfig> = {};\n if (opts.web === true || opts.serve === true) {\n overrides.enabled = true;\n }\n if (opts.port !== undefined) {\n overrides.port = opts.port as number;\n }\n if (opts.host !== undefined) {\n overrides.host = opts.host as string;\n }\n if (opts.refresh !== undefined) {\n overrides.refreshSeconds = opts.refresh as number;\n }\n if (opts.webLogLimit !== undefined) {\n overrides.logLimit = opts.webLogLimit as number;\n }\n if (opts.webNoLogs === true) {\n overrides.includeLogs = false;\n }\n return overrides;\n}\n\nconst program = new Command();\n\nprogram\n .name('issue-flow')\n .description(\n 'Unified CLI for orchestrating the full issue-flow pipeline via Claude Code Headless.',\n )\n .version(version);\n\nprogram.hook('preAction', (_thisCommand, actionCommand) => {\n const opts = actionCommand.opts();\n if (opts.verbose) {\n setVerbose(true);\n }\n if (opts.timeout !== undefined) {\n setGlobalTimeout(opts.timeout * 1000);\n }\n setWebCliOverrides(resolveWebOverrides(opts));\n try {\n setIssuesCliOverrides(resolveIssuesOverrides(opts));\n } catch (error) {\n if (error instanceof IssueFlagError) {\n printError(error.message);\n process.exit(1);\n }\n throw error;\n }\n});\n\n// ── init ────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program.command('init').description('Verify that all prerequisites (claude, gh, git) are met'),\n ),\n).action(async () => {\n const { loadIssuesConfig } = await import('./config.js');\n const { runInit } = await import('./commands/init.js');\n const { preferredProvider } = await loadIssuesConfig();\n const code = await runInit(preferredProvider);\n process.exit(code);\n});\n\n// ── generate ────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('generate')\n .description('Draft an issue via Claude Code Headless and create it')\n .requiredOption('--prompt <text>', 'Issue description text')\n .option('--github', 'Create the issue on GitHub')\n .option('--local', 'Create the issue under issues/<n>/ only')\n .option('--both', 'Create the issue on GitHub and mirror it locally'),\n).action(async (options: { prompt: string; github?: boolean; local?: boolean; both?: boolean }) => {\n let target: IssueGenerateTarget | undefined;\n try {\n target = resolveGenerateTarget(options);\n } catch (error) {\n if (error instanceof IssueFlagError) {\n printError(error.message);\n process.exit(1);\n }\n throw error;\n }\n\n const { runGenerate } = await import('./commands/generate.js');\n const code = await runGenerate(options.prompt, target);\n process.exit(code);\n});\n\n// ── run ─────────────────────────────────────────────────────────────────────\nwithWebOptions(\n withIssueOptions(\n withGlobalOptions(\n program\n .command('run')\n .description('Execute the full pipeline: prd → plan → execute → review → pr')\n .argument('<issue>', 'Issue number')\n .option('--mode <mode>', 'Execution mode: auto | manual', 'auto')\n .option('--from <phase>', 'Resume from a specific phase')\n .option(\n '--no-branch',\n 'Run pipeline on current branch without creating a new branch or PR',\n ),\n ),\n ),\n).action(async (issue: string, options: { mode: string; from?: string; branch?: boolean }) => {\n const { runPipeline } = await import('./commands/run.js');\n const code = await runPipeline(issue, options.mode, options.from, resolveNoBranch(options));\n process.exit(code);\n});\n\n// ── analyze ─────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('analyze')\n .description('Analyze an issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runAnalyze } = await import('./commands/analyze.js');\n const code = await runAnalyze(issue);\n process.exit(code);\n});\n\n// ── prd ─────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('prd')\n .description('Generate a PRD from an analyzed issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runPrd } = await import('./commands/prd.js');\n const code = await runPrd(issue);\n process.exit(code);\n});\n\n// ── plan ────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('plan')\n .description('Convert a PRD to a tasks.json task plan via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runPlan } = await import('./commands/plan.js');\n const code = await runPlan(issue);\n process.exit(code);\n});\n\n// ── execute ─────────────────────────────────────────────────────────────────\nwithWebOptions(\n withGlobalOptions(\n program\n .command('execute')\n .description('Run the iterative story execution loop (issue-flow engine)')\n .option('--issue <number>', 'Issue number — reads artifacts from issues/N/')\n .option('--max-iterations <number>', 'Stop after N iterations', parseInteger)\n .option(\n '--retry-limit <number>',\n 'Retry transient Claude failures up to N consecutive times',\n parseInteger,\n )\n .option('--retry-forever', 'Retry transient Claude failures indefinitely')\n .argument(\n '[max-iterations]',\n 'Backward-compatible alias for --max-iterations N',\n parseInteger,\n ),\n ),\n).action(\n async (\n positionalMaxIter: number | undefined,\n options: {\n issue?: string;\n maxIterations?: number;\n retryLimit?: number;\n retryForever?: boolean;\n },\n ) => {\n try {\n const { runExecute } = await import('./commands/execute.js');\n const code = await runExecute(positionalMaxIter, options);\n process.exit(code);\n } catch (error) {\n printError(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n },\n);\n\n// ── review ──────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('review')\n .description('Validate an issue resolution via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runReview } = await import('./commands/review.js');\n const code = await runReview(issue);\n process.exit(code);\n});\n\n// ── pr ──────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('pr')\n .description('Create a pull request via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runPr } = await import('./commands/pr.js');\n const code = await runPr(issue);\n process.exit(code);\n});\n\nprogram.parse();\n","/**\n * Pure mappings from parsed CLI options to command arguments.\n *\n * They live outside cli.ts because that module runs `program.parse()` on\n * import, so anything defined there is untestable.\n */\n\n/** Shape commander produces for a negated boolean option (`--no-branch`). */\nexport interface BranchFlagOptions {\n branch?: boolean;\n}\n\n/**\n * Translate commander's `--no-branch` into the tri-state `runPipeline` expects.\n *\n * Commander stores a negated option under its positive name: `--no-branch`\n * yields `{ branch: false }` and never a `noBranch` key. Reading `noBranch`\n * silently dropped the flag, so the pipeline always created a branch and opened\n * a PR.\n *\n * `undefined` (flag absent) is preserved on purpose: `runPipeline` uses it to\n * tell \"the user said nothing, keep the mode persisted in tasks.json\" apart\n * from an explicit choice.\n */\nexport function resolveNoBranch(options: BranchFlagOptions): boolean | undefined {\n return options.branch === false ? true : undefined;\n}\n","import type { IssueGenerateTarget, IssuesConfig } from './types.js';\n\n/**\n * Mapping from the Issue provider CLI flags to configuration overrides.\n *\n * Kept out of cli.ts (which runs `program.parse()` on import) so the\n * precedence rules stay unit-testable. Only the keys the user actually passed\n * are returned: a key present with `undefined` would override .issue-flow.json\n * when the layers are merged.\n */\n\nexport interface IssueCliFlags {\n local?: boolean;\n github?: boolean;\n preferLocal?: boolean;\n preferGithub?: boolean;\n ask?: boolean;\n}\n\n/** Invalid flag combination, reported to the user as a CLI error. */\nexport class IssueFlagError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueFlagError';\n }\n}\n\n/**\n * Translate the parsed options of a command into `IssuesConfig` overrides.\n *\n * @throws IssueFlagError when mutually exclusive flags are combined.\n */\nexport function resolveIssuesOverrides(opts: IssueCliFlags): Partial<IssuesConfig> {\n const overrides: Partial<IssuesConfig> = {};\n\n if (opts.local === true && opts.github === true) {\n throw new IssueFlagError('--local and --github are mutually exclusive; pass only one.');\n }\n if (opts.local === true) {\n overrides.preferredProvider = 'local';\n }\n if (opts.github === true) {\n overrides.preferredProvider = 'github';\n }\n\n const conflictFlags = [\n opts.preferLocal === true ? '--prefer-local' : null,\n opts.preferGithub === true ? '--prefer-github' : null,\n opts.ask === true ? '--ask' : null,\n ].filter((flag): flag is string => flag !== null);\n\n if (conflictFlags.length > 1) {\n throw new IssueFlagError(\n `${conflictFlags.join(', ')} are mutually exclusive; pass only one conflict policy.`,\n );\n }\n if (opts.preferLocal === true) {\n overrides.conflictPolicy = 'prefer-local';\n }\n if (opts.preferGithub === true) {\n overrides.conflictPolicy = 'prefer-github';\n }\n if (opts.ask === true) {\n overrides.conflictPolicy = 'ask';\n }\n\n return overrides;\n}\n\n/** Destination flags accepted by `generate`. */\nexport interface GenerateTargetFlags {\n github?: boolean;\n local?: boolean;\n both?: boolean;\n}\n\n/**\n * Destination the generated Issue is created in, or `undefined` when the user\n * passed no flag (the caller then falls back to `defaultGenerateTarget`).\n *\n * @throws IssueFlagError when more than one destination is requested.\n */\nexport function resolveGenerateTarget(opts: GenerateTargetFlags): IssueGenerateTarget | undefined {\n const selected: IssueGenerateTarget[] = [];\n if (opts.github === true) selected.push('github');\n if (opts.local === true) selected.push('local');\n if (opts.both === true) selected.push('both');\n\n if (selected.length > 1) {\n throw new IssueFlagError(\n `${selected.map((target) => `--${target}`).join(', ')} are mutually exclusive; ` +\n 'pass only one destination.',\n );\n }\n\n return selected[0];\n}\n"],"mappings":"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","names":["require"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/cli-options.ts","../src/issues/cli-flags.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { CliFlagError, resolveRunPhaseFlags } from './cli-options.js';\nimport { setIssuesCliOverrides, setWebCliOverrides } from './config.js';\nimport { setGlobalTimeout, setVerbose } from './core/verbose.js';\nimport {\n IssueFlagError,\n resolveGenerateTarget,\n resolveIssuesOverrides,\n} from './issues/cli-flags.js';\nimport type { IssueGenerateTarget } from './issues/types.js';\nimport type { WebConfig } from './schemas.js';\nimport { printError } from './ui/logger.js';\n\nconst require = createRequire(import.meta.url);\nconst { version } = require('../package.json') as { version: string };\n\n/**\n * Parse a numeric string, throwing InvalidArgumentError if not a valid number.\n */\nfunction parseInteger(value: string): number {\n const parsed = parseInt(value, 10);\n if (Number.isNaN(parsed) || parsed < 0) {\n throw new InvalidArgumentError('Must be a non-negative integer.');\n }\n return parsed;\n}\n\n/**\n * Add shared options (--verbose) to a subcommand.\n */\nfunction withGlobalOptions(cmd: Command): Command {\n return cmd\n .option('-v, --verbose', 'Show Claude progress output in real time')\n .option(\n '-t, --timeout <seconds>',\n 'Override headless timeout in seconds (0 = no limit)',\n parseInteger,\n );\n}\n\n/**\n * Add web monitoring options to a subcommand (run and execute only).\n * The values are resolved into CLI overrides by the preAction hook below;\n * loadWebConfig() applies the flag > env > file > defaults precedence.\n */\nfunction withWebOptions(cmd: Command): Command {\n return cmd\n .option('--web', 'Enable the web monitoring server')\n .option('--serve', 'Alias for --web')\n .option('--port <n>', 'Web server port (default: 3737)', parseInteger)\n .option('--host <h>', 'Web server host (default: 0.0.0.0)')\n .option('--refresh <s>', 'Suggested UI polling interval in seconds', parseInteger)\n .option('--web-log-limit <n>', 'Max log entries kept in the snapshot', parseInteger)\n .option('--web-no-logs', 'Exclude logs from the published snapshot');\n}\n\n/**\n * Add the Issue provider options to a subcommand.\n *\n * Declared once here (like withWebOptions) so no command repeats the flag\n * list; the preAction hook below turns them into config overrides, and\n * loadIssuesConfig() applies the flag > .issue-flow.json > defaults precedence.\n */\nfunction withIssueOptions(cmd: Command): Command {\n return cmd\n .option('--local', 'Prefer the local file Issue provider')\n .option('--github', 'Prefer the GitHub Issue provider')\n .option('--prefer-local', 'On divergence, use the local version without asking')\n .option('--prefer-github', 'On divergence, use the GitHub version without asking')\n .option('--ask', 'On divergence, ask which version to use (interactive only)');\n}\n\n/**\n * Extract the web-related CLI flags from a command's options, keeping only\n * the ones the user actually set.\n */\nfunction resolveWebOverrides(opts: Record<string, unknown>): Partial<WebConfig> {\n const overrides: Partial<WebConfig> = {};\n if (opts.web === true || opts.serve === true) {\n overrides.enabled = true;\n }\n if (opts.port !== undefined) {\n overrides.port = opts.port as number;\n }\n if (opts.host !== undefined) {\n overrides.host = opts.host as string;\n }\n if (opts.refresh !== undefined) {\n overrides.refreshSeconds = opts.refresh as number;\n }\n if (opts.webLogLimit !== undefined) {\n overrides.logLimit = opts.webLogLimit as number;\n }\n if (opts.webNoLogs === true) {\n overrides.includeLogs = false;\n }\n return overrides;\n}\n\nconst program = new Command();\n\nprogram\n .name('issue-flow')\n .description(\n 'Unified CLI for orchestrating the full issue-flow pipeline via Claude Code Headless.',\n )\n .version(version);\n\nprogram.hook('preAction', (_thisCommand, actionCommand) => {\n const opts = actionCommand.opts();\n if (opts.verbose) {\n setVerbose(true);\n }\n if (opts.timeout !== undefined) {\n setGlobalTimeout(opts.timeout * 1000);\n }\n setWebCliOverrides(resolveWebOverrides(opts));\n try {\n setIssuesCliOverrides(resolveIssuesOverrides(opts));\n } catch (error) {\n if (error instanceof IssueFlagError) {\n printError(error.message);\n process.exit(1);\n }\n throw error;\n }\n});\n\n// ── init ────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program.command('init').description('Verify that all prerequisites (claude, gh, git) are met'),\n ),\n).action(async () => {\n const { loadIssuesConfig } = await import('./config.js');\n const { runInit } = await import('./commands/init.js');\n const { preferredProvider } = await loadIssuesConfig();\n const code = await runInit(preferredProvider);\n process.exit(code);\n});\n\n// ── generate ────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('generate')\n .description('Draft an issue via Claude Code Headless and create it')\n .requiredOption('--prompt <text>', 'Issue description text')\n .option('--github', 'Create the issue on GitHub')\n .option('--local', 'Create the issue under issues/<n>/ only')\n .option('--both', 'Create the issue on GitHub and mirror it locally'),\n).action(async (options: { prompt: string; github?: boolean; local?: boolean; both?: boolean }) => {\n let target: IssueGenerateTarget | undefined;\n try {\n target = resolveGenerateTarget(options);\n } catch (error) {\n if (error instanceof IssueFlagError) {\n printError(error.message);\n process.exit(1);\n }\n throw error;\n }\n\n const { runGenerate } = await import('./commands/generate.js');\n const code = await runGenerate(options.prompt, target);\n process.exit(code);\n});\n\n// ── run ─────────────────────────────────────────────────────────────────────\nwithWebOptions(\n withIssueOptions(\n withGlobalOptions(\n program\n .command('run')\n .description(\n 'Execute the full pipeline: prd → plan → execute → review → pr (→ pr-review, optional)',\n )\n .argument('<issue>', 'Issue number')\n .option('--mode <mode>', 'Execution mode: auto | manual', 'auto')\n .option('--from <phase>', 'Resume from a specific phase')\n .option('--no-branch', 'Run pipeline on current branch without creating a new branch or PR')\n .option('--pr-review', 'Review the created Pull Request after the pr phase'),\n ),\n ),\n).action(\n async (\n issue: string,\n options: { mode: string; from?: string; branch?: boolean; prReview?: boolean },\n ) => {\n let phases: ReturnType<typeof resolveRunPhaseFlags>;\n try {\n phases = resolveRunPhaseFlags(options);\n } catch (error) {\n if (error instanceof CliFlagError) {\n printError(error.message);\n process.exit(1);\n }\n throw error;\n }\n\n const { runPipeline } = await import('./commands/run.js');\n const code = await runPipeline(\n issue,\n options.mode,\n options.from,\n phases.noBranch,\n phases.prReview,\n );\n process.exit(code);\n },\n);\n\n// ── analyze ─────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('analyze')\n .description('Analyze an issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runAnalyze } = await import('./commands/analyze.js');\n const code = await runAnalyze(issue);\n process.exit(code);\n});\n\n// ── prd ─────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('prd')\n .description('Generate a PRD from an analyzed issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runPrd } = await import('./commands/prd.js');\n const code = await runPrd(issue);\n process.exit(code);\n});\n\n// ── plan ────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('plan')\n .description('Convert a PRD to a tasks.json task plan via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runPlan } = await import('./commands/plan.js');\n const code = await runPlan(issue);\n process.exit(code);\n});\n\n// ── execute ─────────────────────────────────────────────────────────────────\nwithWebOptions(\n withGlobalOptions(\n program\n .command('execute')\n .description('Run the iterative story execution loop (issue-flow engine)')\n .option('--issue <number>', 'Issue number — reads artifacts from issues/N/')\n .option('--max-iterations <number>', 'Stop after N iterations', parseInteger)\n .option(\n '--retry-limit <number>',\n 'Retry transient Claude failures up to N consecutive times',\n parseInteger,\n )\n .option('--retry-forever', 'Retry transient Claude failures indefinitely')\n .argument(\n '[max-iterations]',\n 'Backward-compatible alias for --max-iterations N',\n parseInteger,\n ),\n ),\n).action(\n async (\n positionalMaxIter: number | undefined,\n options: {\n issue?: string;\n maxIterations?: number;\n retryLimit?: number;\n retryForever?: boolean;\n },\n ) => {\n try {\n const { runExecute } = await import('./commands/execute.js');\n const code = await runExecute(positionalMaxIter, options);\n process.exit(code);\n } catch (error) {\n printError(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n },\n);\n\n// ── review ──────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('review')\n .description('Validate an issue resolution via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runReview } = await import('./commands/review.js');\n const code = await runReview(issue);\n process.exit(code);\n});\n\n// ── pr ──────────────────────────────────────────────────────────────────────\nwithIssueOptions(\n withGlobalOptions(\n program\n .command('pr')\n .description('Create a pull request via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\n ),\n).action(async (issue: string) => {\n const { runPr } = await import('./commands/pr.js');\n const code = await runPr(issue);\n process.exit(code);\n});\n\n// ── pr-review ───────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('pr-review')\n .description('Review a Pull Request as a whole via Claude Code Headless')\n .argument('[pr]', 'Pull Request number (discovered from the session when omitted)')\n .option('--issue <n>', 'Issue the Pull Request belongs to')\n .option('--round <n>', 'Rewrite a specific review round instead of appending a new one')\n .option('--yes', 'Skip the confirmation of the discovered Pull Request')\n .option(\n '--fail-on <level>',\n 'Verdict that fails the command: request-changes | suggestions | none',\n ),\n).action(\n async (\n pr: string | undefined,\n options: { issue?: string; round?: string; yes?: boolean; failOn?: string },\n ) => {\n const { runPrReview } = await import('./commands/pr-review.js');\n const code = await runPrReview(pr, options);\n process.exit(code);\n },\n);\n\nprogram.parse();\n","/**\n * Pure mappings from parsed CLI options to command arguments.\n *\n * They live outside cli.ts because that module runs `program.parse()` on\n * import, so anything defined there is untestable.\n */\n\n/** Shape commander produces for a negated boolean option (`--no-branch`). */\nexport interface BranchFlagOptions {\n branch?: boolean;\n}\n\n/**\n * Translate commander's `--no-branch` into the tri-state `runPipeline` expects.\n *\n * Commander stores a negated option under its positive name: `--no-branch`\n * yields `{ branch: false }` and never a `noBranch` key. Reading `noBranch`\n * silently dropped the flag, so the pipeline always created a branch and opened\n * a PR.\n *\n * `undefined` (flag absent) is preserved on purpose: `runPipeline` uses it to\n * tell \"the user said nothing, keep the mode persisted in tasks.json\" apart\n * from an explicit choice.\n */\nexport function resolveNoBranch(options: BranchFlagOptions): boolean | undefined {\n return options.branch === false ? true : undefined;\n}\n\n/** Invalid flag combination on a command, reported as a CLI error. */\nexport class CliFlagError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CliFlagError';\n }\n}\n\n/** Options `run` accepts that select which phases the pipeline executes. */\nexport interface RunPhaseFlagOptions extends BranchFlagOptions {\n prReview?: boolean;\n}\n\n/** Phase selection `runPipeline` expects, both entries tri-state. */\nexport interface RunPhaseFlags {\n noBranch: boolean | undefined;\n prReview: boolean | undefined;\n}\n\n/**\n * Resolve the phase-selection flags of `run`, rejecting the one combination\n * that cannot work.\n *\n * `--no-branch` skips the `pr` phase, so there is no Pull Request for\n * `--pr-review` to review. Failing here — before any phase runs — beats\n * discovering it at the end of a full pipeline.\n *\n * Both values stay `undefined` when the user passed nothing: `runPipeline`\n * uses that to tell \"no opinion, keep what tasks.json persisted\" apart from an\n * explicit choice.\n *\n * @throws CliFlagError when `--pr-review` is combined with `--no-branch`.\n */\nexport function resolveRunPhaseFlags(options: RunPhaseFlagOptions): RunPhaseFlags {\n const noBranch = resolveNoBranch(options);\n const prReview = options.prReview === true ? true : undefined;\n\n if (prReview === true && noBranch === true) {\n throw new CliFlagError(\n '--pr-review cannot be combined with --no-branch: --no-branch skips the pr phase, ' +\n 'so there is no Pull Request to review.',\n );\n }\n\n return { noBranch, prReview };\n}\n","import type { IssueGenerateTarget, IssuesConfig } from './types.js';\n\n/**\n * Mapping from the Issue provider CLI flags to configuration overrides.\n *\n * Kept out of cli.ts (which runs `program.parse()` on import) so the\n * precedence rules stay unit-testable. Only the keys the user actually passed\n * are returned: a key present with `undefined` would override .issue-flow.json\n * when the layers are merged.\n */\n\nexport interface IssueCliFlags {\n local?: boolean;\n github?: boolean;\n preferLocal?: boolean;\n preferGithub?: boolean;\n ask?: boolean;\n}\n\n/** Invalid flag combination, reported to the user as a CLI error. */\nexport class IssueFlagError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueFlagError';\n }\n}\n\n/**\n * Translate the parsed options of a command into `IssuesConfig` overrides.\n *\n * @throws IssueFlagError when mutually exclusive flags are combined.\n */\nexport function resolveIssuesOverrides(opts: IssueCliFlags): Partial<IssuesConfig> {\n const overrides: Partial<IssuesConfig> = {};\n\n if (opts.local === true && opts.github === true) {\n throw new IssueFlagError('--local and --github are mutually exclusive; pass only one.');\n }\n if (opts.local === true) {\n overrides.preferredProvider = 'local';\n }\n if (opts.github === true) {\n overrides.preferredProvider = 'github';\n }\n\n const conflictFlags = [\n opts.preferLocal === true ? '--prefer-local' : null,\n opts.preferGithub === true ? '--prefer-github' : null,\n opts.ask === true ? '--ask' : null,\n ].filter((flag): flag is string => flag !== null);\n\n if (conflictFlags.length > 1) {\n throw new IssueFlagError(\n `${conflictFlags.join(', ')} are mutually exclusive; pass only one conflict policy.`,\n );\n }\n if (opts.preferLocal === true) {\n overrides.conflictPolicy = 'prefer-local';\n }\n if (opts.preferGithub === true) {\n overrides.conflictPolicy = 'prefer-github';\n }\n if (opts.ask === true) {\n overrides.conflictPolicy = 'ask';\n }\n\n return overrides;\n}\n\n/** Destination flags accepted by `generate`. */\nexport interface GenerateTargetFlags {\n github?: boolean;\n local?: boolean;\n both?: boolean;\n}\n\n/**\n * Destination the generated Issue is created in, or `undefined` when the user\n * passed no flag (the caller then falls back to `defaultGenerateTarget`).\n *\n * @throws IssueFlagError when more than one destination is requested.\n */\nexport function resolveGenerateTarget(opts: GenerateTargetFlags): IssueGenerateTarget | undefined {\n const selected: IssueGenerateTarget[] = [];\n if (opts.github === true) selected.push('github');\n if (opts.local === true) selected.push('local');\n if (opts.both === true) selected.push('both');\n\n if (selected.length > 1) {\n throw new IssueFlagError(\n `${selected.map((target) => `--${target}`).join(', ')} are mutually exclusive; ` +\n 'pass only one destination.',\n );\n }\n\n return selected[0];\n}\n"],"mappings":"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","names":["require"]}
@@ -1,29 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DEFAULTS,
4
+ GLOBAL_CONFIG_FILENAME,
4
5
  PROJECT_CONFIG_FILENAME,
5
6
  WEB_CONFIG_FILENAME,
6
7
  createConfig,
7
8
  getInstallHint,
9
+ loadGlobalConfig,
8
10
  loadIssuesConfig,
11
+ loadPrReviewConfig,
9
12
  loadWebConfig,
13
+ mergeConfigLayers,
10
14
  resolvePaths,
11
15
  setIssuesCliOverrides,
12
16
  setWebCliOverrides,
13
17
  validateDependencies
14
- } from "./chunk-4HT4EVYV.js";
15
- import "./chunk-WZDCTAWZ.js";
18
+ } from "./chunk-2AVZZAC6.js";
19
+ import "./chunk-JNEHVQH5.js";
16
20
  export {
17
21
  DEFAULTS,
22
+ GLOBAL_CONFIG_FILENAME,
18
23
  PROJECT_CONFIG_FILENAME,
19
24
  WEB_CONFIG_FILENAME,
20
25
  createConfig,
21
26
  getInstallHint,
27
+ loadGlobalConfig,
22
28
  loadIssuesConfig,
29
+ loadPrReviewConfig,
23
30
  loadWebConfig,
31
+ mergeConfigLayers,
24
32
  resolvePaths,
25
33
  setIssuesCliOverrides,
26
34
  setWebCliOverrides,
27
35
  validateDependencies
28
36
  };
29
- //# sourceMappingURL=config-B564P45C.js.map
37
+ //# sourceMappingURL=config-SJAQMFWB.js.map
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runExecute
4
+ } from "./chunk-YGEWIEAI.js";
5
+ import "./chunk-HDV3YDTG.js";
6
+ import "./chunk-WHIBTNYI.js";
7
+ import "./chunk-SLHZPB3C.js";
8
+ import "./chunk-2AVZZAC6.js";
9
+ import "./chunk-JNEHVQH5.js";
10
+ export {
11
+ runExecute
12
+ };
13
+ //# sourceMappingURL=execute-VZ7SHLP4.js.map
@@ -2,22 +2,24 @@
2
2
  import {
3
3
  ensureProvidersRegistered,
4
4
  getProvider,
5
- localIssueRef,
5
+ localIssueRef
6
+ } from "./chunk-RN2FAMUU.js";
7
+ import {
6
8
  runHeadless
7
- } from "./chunk-SVOWWZQJ.js";
9
+ } from "./chunk-TCOS2KFB.js";
8
10
  import {
9
11
  applyPlaceholders,
10
12
  loadPrompt
11
13
  } from "./chunk-SLHZPB3C.js";
12
14
  import {
13
15
  loadIssuesConfig
14
- } from "./chunk-4HT4EVYV.js";
16
+ } from "./chunk-2AVZZAC6.js";
15
17
  import {
16
18
  getGlobalTimeout,
17
19
  isoNow,
18
20
  printError,
19
21
  printSuccess
20
- } from "./chunk-WZDCTAWZ.js";
22
+ } from "./chunk-JNEHVQH5.js";
21
23
 
22
24
  // src/issues/draft.ts
23
25
  var DRAFT_TAG = "issue-draft";
@@ -140,4 +142,4 @@ async function runGenerate(promptText, target) {
140
142
  export {
141
143
  runGenerate
142
144
  };
143
- //# sourceMappingURL=generate-IN62ARBS.js.map
145
+ //# sourceMappingURL=generate-LUK3XTIP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/issues/draft.ts","../src/commands/generate.ts"],"sourcesContent":["import type { IssueDraft } from './types.js';\n\n/**\n * Structured Issue draft emitted by the `generate` prompt.\n *\n * The agent no longer creates the Issue itself: it returns the content and the\n * providers persist it, so the same draft can reach GitHub, the local files, or\n * both. Capturing the result therefore has to be exact — the old approach of\n * fishing an issue URL out of free-form prose only ever worked for GitHub.\n *\n * The block mirrors the `<issue-body>` convention already used by the phase\n * templates, which keeps a multi-line markdown body readable and free of the\n * escaping a JSON payload would demand.\n */\n\n/** Wrapper the agent must emit around the draft. */\nexport const DRAFT_TAG = 'issue-draft';\n\n/** Failure to capture a draft from the agent output. */\nexport class IssueDraftParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueDraftParseError';\n }\n}\n\n/**\n * Last occurrence of `<tag>...</tag>` in `source`, or `null`.\n *\n * The last one wins: an agent that shows the format before filling it in (or\n * restates the draft after a correction) would otherwise have its first,\n * throwaway attempt captured. The closing tag is searched from the end for the\n * same reason a body may legitimately quote one.\n */\nfunction extractTag(source: string, tag: string): string | null {\n const open = `<${tag}>`;\n const close = `</${tag}>`;\n\n const start = source.lastIndexOf(open);\n if (start === -1) return null;\n\n const end = source.lastIndexOf(close);\n if (end === -1 || end < start) return null;\n\n return source.slice(start + open.length, end);\n}\n\nfunction parseLabels(raw: string | null): string[] {\n if (raw === null) return [];\n return raw\n .split(',')\n .map((label) => label.trim())\n .filter((label) => label.length > 0 && label.toLowerCase() !== '(none)');\n}\n\n/**\n * Read the draft the agent produced.\n *\n * @throws IssueDraftParseError when the block is absent or has no usable\n * title/body, so a malformed answer fails loudly instead of creating an empty\n * Issue.\n */\nexport function parseIssueDraft(output: string): IssueDraft {\n const normalized = output.replace(/\\r\\n?/g, '\\n');\n const block = extractTag(normalized, DRAFT_TAG);\n\n if (block === null) {\n throw new IssueDraftParseError(\n `The agent did not emit an <${DRAFT_TAG}> block, so there is no Issue to create. ` +\n `Output was: ${normalized.trim().slice(0, 300) || '(empty)'}`,\n );\n }\n\n const title = (extractTag(block, 'title') ?? '').trim();\n const body = (extractTag(block, 'body') ?? '').trim();\n\n if (title.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <title>.`);\n }\n if (body.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <body>.`);\n }\n\n return { title, body, labels: parseLabels(extractTag(block, 'labels')) };\n}\n","import { loadIssuesConfig } from '../config.js';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { ensureProvidersRegistered } from '../issues/bootstrap.js';\nimport { localIssueRef } from '../issues/context.js';\nimport { IssueDraftParseError, parseIssueDraft } from '../issues/draft.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueDraft, IssueGenerateTarget } from '../issues/types.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\n/** Human-readable pointer to a created Issue. */\nfunction issueLocation(issue: Issue): string {\n return issue.remoteRef ?? localIssueRef(issue.id);\n}\n\n/**\n * Ask the agent for the Issue content.\n *\n * The agent only drafts: creation belongs to the providers, so the same draft\n * can be persisted to GitHub, to the local files, or to both.\n */\nasync function draftIssue(promptText: string): Promise<IssueDraft> {\n const template = await loadPrompt('generate');\n const prompt = applyPlaceholders(template, {\n __USER_PROMPT__: promptText,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: 'Drafting issue...',\n });\n\n if (!result.success) {\n throw new Error(`Issue creation failed: ${result.error}`);\n }\n\n return parseIssueDraft(result.result);\n}\n\n/**\n * Create the GitHub Issue and mirror it locally.\n *\n * The remote comes first on purpose: it owns the identifier, and the mirror has\n * to reuse it so both copies describe one demand. A remote failure therefore\n * leaves nothing behind, and a mirror failure is reported with the remote\n * reference that already exists.\n */\nasync function createBoth(draft: IssueDraft): Promise<Issue[]> {\n const remote = await getProvider('github').create(draft);\n\n try {\n const mirror = await getProvider('local').create({\n ...draft,\n id: remote.id,\n ...(remote.remoteRef\n ? {\n remote: {\n provider: 'github' as const,\n ref: remote.remoteRef,\n syncedAt: isoNow(),\n syncedContentHash: remote.contentHash,\n },\n }\n : {}),\n });\n return [remote, mirror];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `GitHub issue created at ${issueLocation(remote)}, but the local mirror was not written: ` +\n `${message}. Nothing else was persisted; re-run with --local to create the mirror.`,\n );\n }\n}\n\nasync function createIssues(target: IssueGenerateTarget, draft: IssueDraft): Promise<Issue[]> {\n if (target === 'both') {\n return createBoth(draft);\n }\n return [await getProvider(target).create(draft)];\n}\n\nexport async function runGenerate(\n promptText: string,\n target?: IssueGenerateTarget,\n): Promise<number> {\n const config = await loadIssuesConfig();\n const destination = target ?? config.defaultGenerateTarget;\n\n ensureProvidersRegistered();\n\n let draft: IssueDraft;\n try {\n draft = await draftIssue(promptText);\n } catch (err) {\n if (err instanceof IssueDraftParseError) {\n printError(`Could not read the generated Issue draft: ${err.message}`);\n } else {\n printError(err instanceof Error ? err.message : String(err));\n }\n return 1;\n }\n\n let created: Issue[];\n try {\n created = await createIssues(destination, draft);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n for (const issue of created) {\n printSuccess(`Issue created (${issue.source}): ${issueLocation(issue)}`);\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/issues/draft.ts","../src/commands/generate.ts"],"sourcesContent":["import type { IssueDraft } from './types.js';\n\n/**\n * Structured Issue draft emitted by the `generate` prompt.\n *\n * The agent no longer creates the Issue itself: it returns the content and the\n * providers persist it, so the same draft can reach GitHub, the local files, or\n * both. Capturing the result therefore has to be exact — the old approach of\n * fishing an issue URL out of free-form prose only ever worked for GitHub.\n *\n * The block mirrors the `<issue-body>` convention already used by the phase\n * templates, which keeps a multi-line markdown body readable and free of the\n * escaping a JSON payload would demand.\n */\n\n/** Wrapper the agent must emit around the draft. */\nexport const DRAFT_TAG = 'issue-draft';\n\n/** Failure to capture a draft from the agent output. */\nexport class IssueDraftParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueDraftParseError';\n }\n}\n\n/**\n * Last occurrence of `<tag>...</tag>` in `source`, or `null`.\n *\n * The last one wins: an agent that shows the format before filling it in (or\n * restates the draft after a correction) would otherwise have its first,\n * throwaway attempt captured. The closing tag is searched from the end for the\n * same reason a body may legitimately quote one.\n */\nfunction extractTag(source: string, tag: string): string | null {\n const open = `<${tag}>`;\n const close = `</${tag}>`;\n\n const start = source.lastIndexOf(open);\n if (start === -1) return null;\n\n const end = source.lastIndexOf(close);\n if (end === -1 || end < start) return null;\n\n return source.slice(start + open.length, end);\n}\n\nfunction parseLabels(raw: string | null): string[] {\n if (raw === null) return [];\n return raw\n .split(',')\n .map((label) => label.trim())\n .filter((label) => label.length > 0 && label.toLowerCase() !== '(none)');\n}\n\n/**\n * Read the draft the agent produced.\n *\n * @throws IssueDraftParseError when the block is absent or has no usable\n * title/body, so a malformed answer fails loudly instead of creating an empty\n * Issue.\n */\nexport function parseIssueDraft(output: string): IssueDraft {\n const normalized = output.replace(/\\r\\n?/g, '\\n');\n const block = extractTag(normalized, DRAFT_TAG);\n\n if (block === null) {\n throw new IssueDraftParseError(\n `The agent did not emit an <${DRAFT_TAG}> block, so there is no Issue to create. ` +\n `Output was: ${normalized.trim().slice(0, 300) || '(empty)'}`,\n );\n }\n\n const title = (extractTag(block, 'title') ?? '').trim();\n const body = (extractTag(block, 'body') ?? '').trim();\n\n if (title.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <title>.`);\n }\n if (body.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <body>.`);\n }\n\n return { title, body, labels: parseLabels(extractTag(block, 'labels')) };\n}\n","import { loadIssuesConfig } from '../config.js';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { ensureProvidersRegistered } from '../issues/bootstrap.js';\nimport { localIssueRef } from '../issues/context.js';\nimport { IssueDraftParseError, parseIssueDraft } from '../issues/draft.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueDraft, IssueGenerateTarget } from '../issues/types.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\n/** Human-readable pointer to a created Issue. */\nfunction issueLocation(issue: Issue): string {\n return issue.remoteRef ?? localIssueRef(issue.id);\n}\n\n/**\n * Ask the agent for the Issue content.\n *\n * The agent only drafts: creation belongs to the providers, so the same draft\n * can be persisted to GitHub, to the local files, or to both.\n */\nasync function draftIssue(promptText: string): Promise<IssueDraft> {\n const template = await loadPrompt('generate');\n const prompt = applyPlaceholders(template, {\n __USER_PROMPT__: promptText,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: 'Drafting issue...',\n });\n\n if (!result.success) {\n throw new Error(`Issue creation failed: ${result.error}`);\n }\n\n return parseIssueDraft(result.result);\n}\n\n/**\n * Create the GitHub Issue and mirror it locally.\n *\n * The remote comes first on purpose: it owns the identifier, and the mirror has\n * to reuse it so both copies describe one demand. A remote failure therefore\n * leaves nothing behind, and a mirror failure is reported with the remote\n * reference that already exists.\n */\nasync function createBoth(draft: IssueDraft): Promise<Issue[]> {\n const remote = await getProvider('github').create(draft);\n\n try {\n const mirror = await getProvider('local').create({\n ...draft,\n id: remote.id,\n ...(remote.remoteRef\n ? {\n remote: {\n provider: 'github' as const,\n ref: remote.remoteRef,\n syncedAt: isoNow(),\n syncedContentHash: remote.contentHash,\n },\n }\n : {}),\n });\n return [remote, mirror];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `GitHub issue created at ${issueLocation(remote)}, but the local mirror was not written: ` +\n `${message}. Nothing else was persisted; re-run with --local to create the mirror.`,\n );\n }\n}\n\nasync function createIssues(target: IssueGenerateTarget, draft: IssueDraft): Promise<Issue[]> {\n if (target === 'both') {\n return createBoth(draft);\n }\n return [await getProvider(target).create(draft)];\n}\n\nexport async function runGenerate(\n promptText: string,\n target?: IssueGenerateTarget,\n): Promise<number> {\n const config = await loadIssuesConfig();\n const destination = target ?? config.defaultGenerateTarget;\n\n ensureProvidersRegistered();\n\n let draft: IssueDraft;\n try {\n draft = await draftIssue(promptText);\n } catch (err) {\n if (err instanceof IssueDraftParseError) {\n printError(`Could not read the generated Issue draft: ${err.message}`);\n } else {\n printError(err instanceof Error ? err.message : String(err));\n }\n return 1;\n }\n\n let created: Issue[];\n try {\n created = await createIssues(destination, draft);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n for (const issue of created) {\n printSuccess(`Issue created (${issue.source}): ${issueLocation(issue)}`);\n }\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAgBO,IAAM,YAAY;AAGlB,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUA,SAAS,WAAW,QAAgB,KAA4B;AAC9D,QAAM,OAAO,IAAI,GAAG;AACpB,QAAM,QAAQ,KAAK,GAAG;AAEtB,QAAM,QAAQ,OAAO,YAAY,IAAI;AACrC,MAAI,UAAU,GAAI,QAAO;AAEzB,QAAM,MAAM,OAAO,YAAY,KAAK;AACpC,MAAI,QAAQ,MAAM,MAAM,MAAO,QAAO;AAEtC,SAAO,OAAO,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9C;AAEA,SAAS,YAAY,KAA8B;AACjD,MAAI,QAAQ,KAAM,QAAO,CAAC;AAC1B,SAAO,IACJ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,KAAK,MAAM,YAAY,MAAM,QAAQ;AAC3E;AASO,SAAS,gBAAgB,QAA4B;AAC1D,QAAM,aAAa,OAAO,QAAQ,UAAU,IAAI;AAChD,QAAM,QAAQ,WAAW,YAAY,SAAS;AAE9C,MAAI,UAAU,MAAM;AAClB,UAAM,IAAI;AAAA,MACR,8BAA8B,SAAS,wDACtB,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK,SAAS;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,SAAS,WAAW,OAAO,OAAO,KAAK,IAAI,KAAK;AACtD,QAAM,QAAQ,WAAW,OAAO,MAAM,KAAK,IAAI,KAAK;AAEpD,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,qBAAqB,QAAQ,SAAS,yBAAyB;AAAA,EAC3E;AACA,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,qBAAqB,QAAQ,SAAS,wBAAwB;AAAA,EAC1E;AAEA,SAAO,EAAE,OAAO,MAAM,QAAQ,YAAY,WAAW,OAAO,QAAQ,CAAC,EAAE;AACzE;;;ACvEA,SAAS,cAAc,OAAsB;AAC3C,SAAO,MAAM,aAAa,cAAc,MAAM,EAAE;AAClD;AAQA,eAAe,WAAW,YAAyC;AACjE,QAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,iBAAiB;AAAA,EACnB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7C,eAAe;AAAA,EACjB,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,0BAA0B,OAAO,KAAK,EAAE;AAAA,EAC1D;AAEA,SAAO,gBAAgB,OAAO,MAAM;AACtC;AAUA,eAAe,WAAW,OAAqC;AAC7D,QAAM,SAAS,MAAM,YAAY,QAAQ,EAAE,OAAO,KAAK;AAEvD,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,OAAO,EAAE,OAAO;AAAA,MAC/C,GAAG;AAAA,MACH,IAAI,OAAO;AAAA,MACX,GAAI,OAAO,YACP;AAAA,QACE,QAAQ;AAAA,UACN,UAAU;AAAA,UACV,KAAK,OAAO;AAAA,UACZ,UAAU,OAAO;AAAA,UACjB,mBAAmB,OAAO;AAAA,QAC5B;AAAA,MACF,IACA,CAAC;AAAA,IACP,CAAC;AACD,WAAO,CAAC,QAAQ,MAAM;AAAA,EACxB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI;AAAA,MACR,2BAA2B,cAAc,MAAM,CAAC,2CAC3C,OAAO;AAAA,IACd;AAAA,EACF;AACF;AAEA,eAAe,aAAa,QAA6B,OAAqC;AAC5F,MAAI,WAAW,QAAQ;AACrB,WAAO,WAAW,KAAK;AAAA,EACzB;AACA,SAAO,CAAC,MAAM,YAAY,MAAM,EAAE,OAAO,KAAK,CAAC;AACjD;AAEA,eAAsB,YACpB,YACA,QACiB;AACjB,QAAM,SAAS,MAAM,iBAAiB;AACtC,QAAM,cAAc,UAAU,OAAO;AAErC,4BAA0B;AAE1B,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,WAAW,UAAU;AAAA,EACrC,SAAS,KAAK;AACZ,QAAI,eAAe,sBAAsB;AACvC,iBAAW,6CAA6C,IAAI,OAAO,EAAE;AAAA,IACvE,OAAO;AACL,iBAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7D;AACA,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,aAAa,aAAa,KAAK;AAAA,EACjD,SAAS,KAAK;AACZ,eAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC3D,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,SAAS;AAC3B,iBAAa,kBAAkB,MAAM,MAAM,MAAM,cAAc,KAAK,CAAC,EAAE;AAAA,EACzE;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runInit
4
+ } from "./chunk-YLXPLHEL.js";
5
+ import "./chunk-JNEHVQH5.js";
6
+ export {
7
+ runInit
8
+ };
9
+ //# sourceMappingURL=init-5KXCG36P.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPlan
4
+ } from "./chunk-ASMCLXQ2.js";
5
+ import "./chunk-EHVKXFFN.js";
6
+ import "./chunk-RN2FAMUU.js";
7
+ import "./chunk-HDV3YDTG.js";
8
+ import "./chunk-TCOS2KFB.js";
9
+ import "./chunk-SLHZPB3C.js";
10
+ import "./chunk-2AVZZAC6.js";
11
+ import "./chunk-JNEHVQH5.js";
12
+ export {
13
+ runPlan
14
+ };
15
+ //# sourceMappingURL=plan-R34SAWRK.js.map