issue-flow 0.4.3 → 0.4.4

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 (36) hide show
  1. package/dist/{chunk-MEB3B2FI.js → analyze-PDDIU7HD.js} +2 -3
  2. package/dist/{chunk-CYWHYPTU.js → chunk-65KDGHLM.js} +2 -2
  3. package/dist/{chunk-4PZXIOXD.js → chunk-HPSU5GGE.js} +2 -2
  4. package/dist/{chunk-HYP64MZI.js → chunk-LTPXKMPE.js} +12 -5
  5. package/dist/chunk-LTPXKMPE.js.map +1 -0
  6. package/dist/{chunk-2JZCWJEQ.js → chunk-SKTTLX7R.js} +2 -14
  7. package/dist/chunk-SKTTLX7R.js.map +1 -0
  8. package/dist/{chunk-64FU2L2T.js → chunk-X5QCISNM.js} +3 -3
  9. package/dist/chunk-X5QCISNM.js.map +1 -0
  10. package/dist/{chunk-MWUIIUAS.js → chunk-ZJB2IMPU.js} +2 -2
  11. package/dist/cli.js +9 -9
  12. package/dist/cli.js.map +1 -1
  13. package/dist/{execute-GIA4IKS5.js → execute-WKYSGIGH.js} +3 -3
  14. package/dist/{plan-HWZRG5GP.js → plan-JUOBYTCD.js} +3 -3
  15. package/dist/{pr-TTOFYUCV.js → pr-CYZYJZZV.js} +3 -3
  16. package/dist/{prd-ET2QN7GB.js → prd-PZOY3FCC.js} +3 -3
  17. package/dist/{review-3QDZCOMT.js → review-MK5FTVUE.js} +3 -3
  18. package/dist/{run-PKHPULYL.js → run-A44XQ77Z.js} +82 -41
  19. package/dist/run-A44XQ77Z.js.map +1 -0
  20. package/package.json +1 -1
  21. package/prompts/prd.md +5 -4
  22. package/dist/analyze-3SN7ZAZU.js +0 -12
  23. package/dist/chunk-2JZCWJEQ.js.map +0 -1
  24. package/dist/chunk-64FU2L2T.js.map +0 -1
  25. package/dist/chunk-HYP64MZI.js.map +0 -1
  26. package/dist/review-3QDZCOMT.js.map +0 -1
  27. package/dist/run-PKHPULYL.js.map +0 -1
  28. /package/dist/{chunk-MEB3B2FI.js.map → analyze-PDDIU7HD.js.map} +0 -0
  29. /package/dist/{chunk-CYWHYPTU.js.map → chunk-65KDGHLM.js.map} +0 -0
  30. /package/dist/{chunk-4PZXIOXD.js.map → chunk-HPSU5GGE.js.map} +0 -0
  31. /package/dist/{chunk-MWUIIUAS.js.map → chunk-ZJB2IMPU.js.map} +0 -0
  32. /package/dist/{analyze-3SN7ZAZU.js.map → execute-WKYSGIGH.js.map} +0 -0
  33. /package/dist/{execute-GIA4IKS5.js.map → plan-JUOBYTCD.js.map} +0 -0
  34. /package/dist/{plan-HWZRG5GP.js.map → pr-CYZYJZZV.js.map} +0 -0
  35. /package/dist/{pr-TTOFYUCV.js.map → prd-PZOY3FCC.js.map} +0 -0
  36. /package/dist/{prd-ET2QN7GB.js.map → review-MK5FTVUE.js.map} +0 -0
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  loadTaskPlan,
7
7
  saveTaskPlan
8
- } from "./chunk-64FU2L2T.js";
8
+ } from "./chunk-X5QCISNM.js";
9
9
  import {
10
10
  applyPlaceholders,
11
11
  loadPrompt
@@ -62,8 +62,7 @@ async function runAnalyze(issue) {
62
62
  printSuccess(`Analysis saved to ${analysisPath}`);
63
63
  return 0;
64
64
  }
65
-
66
65
  export {
67
66
  runAnalyze
68
67
  };
69
- //# sourceMappingURL=chunk-MEB3B2FI.js.map
68
+ //# sourceMappingURL=analyze-PDDIU7HD.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  loadTaskPlan,
7
7
  saveTaskPlan
8
- } from "./chunk-64FU2L2T.js";
8
+ } from "./chunk-X5QCISNM.js";
9
9
  import {
10
10
  applyPlaceholders,
11
11
  loadPrompt
@@ -75,4 +75,4 @@ async function runPr(issue) {
75
75
  export {
76
76
  runPr
77
77
  };
78
- //# sourceMappingURL=chunk-CYWHYPTU.js.map
78
+ //# sourceMappingURL=chunk-65KDGHLM.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  loadTaskPlan,
7
7
  saveTaskPlan
8
- } from "./chunk-64FU2L2T.js";
8
+ } from "./chunk-X5QCISNM.js";
9
9
  import {
10
10
  applyPlaceholders,
11
11
  loadPrompt
@@ -82,4 +82,4 @@ async function runReview(issue) {
82
82
  export {
83
83
  runReview
84
84
  };
85
- //# sourceMappingURL=chunk-4PZXIOXD.js.map
85
+ //# sourceMappingURL=chunk-HPSU5GGE.js.map
@@ -10,7 +10,7 @@ import {
10
10
  saveTaskPlan,
11
11
  setLastError,
12
12
  trimErrorMessage
13
- } from "./chunk-64FU2L2T.js";
13
+ } from "./chunk-X5QCISNM.js";
14
14
  import {
15
15
  applyPlaceholders,
16
16
  loadPrompt
@@ -21,6 +21,7 @@ import {
21
21
  getOutputCallback,
22
22
  getStoryUpdateCallback,
23
23
  getTermWidth,
24
+ isVerbose,
24
25
  printError,
25
26
  printInfo,
26
27
  printRetry,
@@ -474,7 +475,9 @@ async function runEngine(config, paths) {
474
475
  }
475
476
  i++;
476
477
  plan = await loadTaskPlan(paths.prdFile);
477
- printIterationHeader(i, config.maxIterations, plan.userStories);
478
+ if (isVerbose()) {
479
+ printIterationHeader(i, config.maxIterations, plan.userStories);
480
+ }
478
481
  const prompt = applyPlaceholders(promptTemplate, {
479
482
  __PRD_FILE__: paths.prdFile,
480
483
  __PROGRESS_FILE__: paths.progressFile
@@ -509,7 +512,9 @@ async function runEngine(config, paths) {
509
512
  config.backoffBaseSeconds,
510
513
  config.backoffMaxSeconds
511
514
  );
512
- emitLog("");
515
+ if (isVerbose()) {
516
+ emitLog("");
517
+ }
513
518
  printRetry(
514
519
  `Transient Claude failure on iteration ${i} (attempt ${retryCount}). Retrying in ${delaySeconds}s.`
515
520
  );
@@ -559,7 +564,9 @@ async function runEngine(config, paths) {
559
564
  "Claude returned a completion signal, but tasks.json still has pending stories. Ignoring completion and continuing."
560
565
  );
561
566
  }
562
- printSuccess(`Iteration ${i} complete. Continuing...`);
567
+ if (isVerbose()) {
568
+ printSuccess(`Iteration ${i} complete. Continuing...`);
569
+ }
563
570
  await sleep(2);
564
571
  }
565
572
  const elapsed = Math.floor((Date.now() - startTime) / 1e3);
@@ -610,4 +617,4 @@ async function runExecute(positionalMaxIter, options) {
610
617
  export {
611
618
  runExecute
612
619
  };
613
- //# sourceMappingURL=chunk-HYP64MZI.js.map
620
+ //# sourceMappingURL=chunk-LTPXKMPE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/utils/shell.ts","../src/utils/git.ts","../src/core/engine.ts","../src/ui/progress.ts","../src/ui/summary.ts","../src/utils/retry.ts","../src/core/executor.ts","../src/commands/execute.ts"],"sourcesContent":["import { platform } from 'node:os';\nimport { join } from 'node:path';\nimport type { EngineConfig, ResolvedPaths } from './types.js';\nimport { getProjectRoot } from './utils/git.js';\nimport { run } from './utils/shell.js';\n\n/**\n * Default configuration values — matching the Bash script exactly.\n */\nexport const DEFAULTS = {\n retryLimit: 10,\n retryForever: false,\n backoffBaseSeconds: 30,\n backoffMaxSeconds: 900,\n} as const;\n\n/**\n * Create a EngineConfig with defaults merged with provided options.\n */\nexport function createConfig(options: Partial<EngineConfig>): EngineConfig {\n return {\n issueNumber: options.issueNumber,\n maxIterations: options.maxIterations,\n retryLimit: options.retryLimit ?? DEFAULTS.retryLimit,\n retryForever: options.retryForever ?? DEFAULTS.retryForever,\n backoffBaseSeconds: options.backoffBaseSeconds ?? DEFAULTS.backoffBaseSeconds,\n backoffMaxSeconds: options.backoffMaxSeconds ?? DEFAULTS.backoffMaxSeconds,\n };\n}\n\n/**\n * Resolve file paths based on issue number and project root.\n *\n * With --issue N:\n * prdFile = {projectRoot}/issues/{N}/tasks.json\n * progressFile = {projectRoot}/issues/{N}/progress.txt\n *\n * Standalone:\n * prdFile = {projectRoot}/prd.json\n * progressFile = {projectRoot}/progress.txt\n */\nexport async function resolvePaths(\n config: EngineConfig,\n scriptDir?: string,\n): Promise<ResolvedPaths> {\n const projectRoot = await getProjectRoot();\n\n if (config.issueNumber) {\n const issueDir = join(projectRoot, 'issues', config.issueNumber);\n return {\n prdFile: join(issueDir, 'tasks.json'),\n progressFile: join(issueDir, 'progress.txt'),\n archiveDir: join(issueDir, 'archive'),\n lastBranchFile: join(issueDir, '.last-branch'),\n projectRoot,\n };\n }\n\n // Standalone mode — use scriptDir if available, otherwise projectRoot\n const base = scriptDir ?? projectRoot;\n return {\n prdFile: join(base, 'prd.json'),\n progressFile: join(base, 'progress.txt'),\n archiveDir: join(base, 'archive'),\n lastBranchFile: join(base, '.last-branch'),\n projectRoot,\n };\n}\n\n/**\n * Return a platform-appropriate install hint for a given package.\n */\nexport function getInstallHint(pkg: string): string {\n const os = platform();\n\n if (os === 'darwin') {\n return `brew install ${pkg}`;\n }\n if (os === 'linux') {\n return `apt install ${pkg} (or your distro's package manager)`;\n }\n if (os === 'win32') {\n return `winget install ${pkg} (or choco install ${pkg})`;\n }\n\n return `install ${pkg} using your system package manager`;\n}\n\n/**\n * Validate that required external dependencies are available.\n * Returns an array of error messages (empty if all deps are found).\n */\nexport async function validateDependencies(): Promise<string[]> {\n const errors: string[] = [];\n\n // Check git\n const gitResult = await run('git', ['--version']);\n if (gitResult.exitCode !== 0) {\n errors.push(` - git (install with: ${getInstallHint('git')})`);\n }\n\n // Check claude\n const claudeResult = await run('claude', ['--version']);\n if (claudeResult.exitCode !== 0) {\n errors.push(' - claude (install with: npm install -g @anthropic-ai/claude-code)');\n }\n\n // Note: jq is NOT required — the TypeScript CLI handles JSON natively\n\n return errors;\n}\n","import { type Options as ExecaOptions, execa } from 'execa';\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Execute a command with arguments and capture its output.\n * Uses execa which calls execFile internally (no shell injection risk).\n * Does not throw on non-zero exit codes.\n */\nexport async function run(\n command: string,\n args: string[] = [],\n options?: ExecaOptions,\n): Promise<ExecResult> {\n const result = await execa(command, args, {\n reject: false,\n ...options,\n });\n\n return {\n stdout: result.stdout?.toString() ?? '',\n stderr: result.stderr?.toString() ?? '',\n exitCode: result.exitCode ?? 1,\n };\n}\n","import { run } from './shell.js';\n\n/**\n * Get the root directory of the current git repository.\n * Throws if not inside a git repository.\n */\nexport async function getProjectRoot(): Promise<string> {\n const result = await run('git', ['rev-parse', '--show-toplevel']);\n\n if (result.exitCode !== 0) {\n throw new Error(\n 'Not inside a git repository. Please run issue-flow from within a git project.',\n );\n }\n\n return result.stdout.trim();\n}\n\n/**\n * Get the current git branch name.\n * Returns an empty string if in detached HEAD state.\n */\nexport async function getCurrentBranch(): Promise<string> {\n const result = await run('git', ['branch', '--show-current']);\n\n if (result.exitCode !== 0) {\n throw new Error(\n 'Failed to detect git branch. Ensure git is installed and you are inside a repository.',\n );\n }\n\n return result.stdout.trim();\n}\n","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 } from '../utils/retry.js';\nimport { executeClaude } from './executor.js';\nimport { applyPlaceholders, loadPrompt } from './prompt-resolver.js';\nimport {\n allStoriesPass,\n clearLastError,\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 * Sleep for a given number of seconds.\n */\nfunction sleep(seconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n}\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\n if (plan.issueStatus === 'completed' && allStoriesPass(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)) {\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\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\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\n // Check for completion signal\n if (result.output.includes('<promise>COMPLETE</promise>')) {\n plan = await loadTaskPlan(paths.prdFile);\n if (allStoriesPass(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 'Claude returned <promise>COMPLETE</promise> before every story had passes=true.',\n );\n await saveTaskPlan(paths.prdFile, plan);\n\n emitLog('');\n printWarning(\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, TaskPlan } from '../types.js';\nimport { formatDuration, getIcons, getTermWidth, useColor, useUnicode } 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 * Transient failure patterns — matching the Bash script's detection heuristics.\n * These are checked case-insensitively against the combined output.\n */\nconst TRANSIENT_PATTERNS = [\n 'timed out',\n 'timeout',\n 'connection reset',\n 'connection refused',\n 'connection aborted',\n 'network error',\n 'network unavailable',\n 'temporary failure',\n 'temporarily unavailable',\n 'service unavailable',\n 'overloaded',\n 'rate limit',\n 'too many requests',\n 'bad gateway',\n 'gateway timeout',\n 'internal server error',\n 'http 429',\n 'http 500',\n 'http 502',\n 'http 503',\n 'http 504',\n 'econnreset',\n 'econnrefused',\n 'enotfound',\n 'etimedout',\n 'socket hang up',\n];\n\n/**\n * Determine if a Claude CLI failure is transient (retryable).\n *\n * A failure is considered transient if:\n * - Exit code is 75 (EX_TEMPFAIL)\n * - Output contains known transient error patterns\n */\nexport function isTransientFailure(exitCode: number, output: string): boolean {\n // Exit code 75 = EX_TEMPFAIL\n if (exitCode === 75) {\n return true;\n }\n\n const lowered = output.toLowerCase();\n return TRANSIENT_PATTERNS.some((pattern) => lowered.includes(pattern));\n}\n\n/**\n * Calculate retry delay using exponential backoff.\n *\n * delay = baseSeconds * 2^(attempt-1), capped at maxSeconds\n *\n * @param attempt - The retry attempt number (1-based)\n * @param baseSeconds - Base delay in seconds (default: 30)\n * @param maxSeconds - Maximum delay in seconds (default: 900)\n */\nexport function retryDelaySeconds(\n attempt: number,\n baseSeconds: number = 30,\n maxSeconds: number = 900,\n): number {\n const delay = baseSeconds * 2 ** (attempt - 1);\n return Math.min(delay, maxSeconds);\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":["join","chalk","chalk","execa","execa","join","elapsed"]}
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  loadTaskPlan,
7
7
  saveTaskPlan
8
- } from "./chunk-64FU2L2T.js";
8
+ } from "./chunk-X5QCISNM.js";
9
9
  import {
10
10
  applyPlaceholders,
11
11
  loadPrompt
@@ -24,21 +24,9 @@ async function runPrd(issue) {
24
24
  const issueDir = join("issues", issueNumber);
25
25
  const prdPath = join(issueDir, "prd.md");
26
26
  await mkdir(issueDir, { recursive: true });
27
- let analysisContext = "";
28
- const analysisPath = join(issueDir, "analysis.md");
29
- try {
30
- const content = await readFile(analysisPath, "utf-8");
31
- analysisContext = `
32
-
33
- Here is the existing analysis for this issue:
34
-
35
- ${content}`;
36
- } catch {
37
- }
38
27
  const template = await loadPrompt("prd");
39
28
  const prompt = applyPlaceholders(template, {
40
29
  __ISSUE_NUMBER__: issueNumber,
41
- __ANALYSIS_CONTEXT__: analysisContext,
42
30
  __PRD_PATH__: prdPath
43
31
  });
44
32
  const result = await runHeadless({
@@ -77,4 +65,4 @@ ${content}`;
77
65
  export {
78
66
  runPrd
79
67
  };
80
- //# sourceMappingURL=chunk-2JZCWJEQ.js.map
68
+ //# sourceMappingURL=chunk-SKTTLX7R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/prd.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport async function runPrd(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const prdPath = join(issueDir, 'prd.md');\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 });\n\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 printError(`PRD generation failed: ${result.error}`);\n return 1;\n }\n\n // Verify the file was created\n try {\n const content = await readFile(prdPath, 'utf-8');\n if (content.length < 10) {\n printError('PRD file was created but appears empty');\n return 1;\n }\n } catch {\n printError(`PRD file was not created at ${prdPath}`);\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,OAAO,gBAAgB;AAChC,SAAS,YAAY;AAOrB,eAAsB,OAAO,OAAgC;AAC3D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,UAAU,KAAK,UAAU,QAAQ;AAEvC,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,EAChB,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,QAAQ,OAAO;AAAA,IACtD,eAAe,6BAA6B,WAAW;AAAA,EACzD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,0BAA0B,OAAO,KAAK,EAAE;AACnD,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,QAAI,QAAQ,SAAS,IAAI;AACvB,iBAAW,wCAAwC;AACnD,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AACN,eAAW,+BAA+B,OAAO,EAAE;AACnD,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":[]}
@@ -12,7 +12,7 @@ var userStorySchema = z.object({
12
12
  notes: z.string()
13
13
  });
14
14
  var pipelineStateSchema = z.object({
15
- analyzeCompleted: z.boolean(),
15
+ analyzeCompleted: z.boolean().optional(),
16
16
  prdCompleted: z.boolean(),
17
17
  jsonCompleted: z.boolean(),
18
18
  executionCompleted: z.boolean(),
@@ -29,6 +29,7 @@ var taskPlanSchema = z.object({
29
29
  issueNumber: z.number().int().positive(),
30
30
  issueUrl: z.string(),
31
31
  branchName: z.string(),
32
+ noBranch: z.boolean().optional().default(false),
32
33
  description: z.string(),
33
34
  issueStatus: z.enum(["pending", "in_progress", "completed"]),
34
35
  completedAt: z.string().nullable(),
@@ -89,7 +90,6 @@ async function saveTaskPlan(path, plan) {
89
90
  }
90
91
  function initializeState(plan) {
91
92
  const defaultPipeline = {
92
- analyzeCompleted: false,
93
93
  prdCompleted: false,
94
94
  jsonCompleted: false,
95
95
  executionCompleted: false,
@@ -177,4 +177,4 @@ export {
177
177
  clearLastError,
178
178
  trimErrorMessage
179
179
  };
180
- //# sourceMappingURL=chunk-64FU2L2T.js.map
180
+ //# sourceMappingURL=chunk-X5QCISNM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas.ts","../src/core/state-manager.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Zod schemas for validating tasks.json structure and headless invocation outputs.\n */\n\nexport const userStorySchema = z.object({\n id: z.string(),\n title: z.string(),\n description: z.string(),\n acceptanceCriteria: z.array(z.string()),\n priority: z.number().int().positive(),\n passes: z.boolean(),\n notes: z.string(),\n});\n\nexport const pipelineStateSchema = z.object({\n analyzeCompleted: z.boolean().optional(),\n prdCompleted: z.boolean(),\n jsonCompleted: z.boolean(),\n executionCompleted: z.boolean(),\n reviewCompleted: z.boolean(),\n prCreated: z.boolean(),\n});\n\nconst lastErrorSchema = z.object({\n category: z.string(),\n message: z.string(),\n at: z.string(),\n});\n\nexport const taskPlanSchema = z.object({\n project: z.string(),\n issueNumber: z.number().int().positive(),\n issueUrl: z.string(),\n branchName: z.string(),\n noBranch: z.boolean().optional().default(false),\n description: z.string(),\n issueStatus: z.enum(['pending', 'in_progress', 'completed']),\n completedAt: z.string().nullable(),\n lastAttemptAt: z.string().nullable(),\n lastError: lastErrorSchema.nullable(),\n correctionCycle: z.number().int().min(0),\n maxCorrectionCycles: z.number().int().min(0),\n pipeline: pipelineStateSchema,\n userStories: z.array(userStorySchema),\n});\n\nexport const headlessResultSchema = z.object({\n success: z.boolean(),\n result: z.string(),\n cost: z\n .object({\n inputTokens: z.number().optional(),\n outputTokens: z.number().optional(),\n })\n .nullable(),\n error: z.string().nullable(),\n});\n\nexport type ValidatedTaskPlan = z.infer<typeof taskPlanSchema>;\nexport type ValidatedHeadlessResult = z.infer<typeof headlessResultSchema>;\n","import { copyFile, mkdtemp, readFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { ZodError } from 'zod';\nimport { taskPlanSchema } from '../schemas.js';\nimport type { LastError, PipelineState, TaskPlan } from '../types.js';\n\n/**\n * Get the current ISO timestamp.\n */\nexport function isoNow(): string {\n return new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n\n/**\n * Load and parse a tasks.json file.\n * Validates that it has the expected structure.\n */\nexport async function loadTaskPlan(path: string): Promise<TaskPlan> {\n const content = await readFile(path, 'utf-8');\n const raw = JSON.parse(content);\n\n try {\n return taskPlanSchema.parse(raw) as TaskPlan;\n } catch (err) {\n if (err instanceof ZodError) {\n const issues = err.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid tasks.json at ${path}:\\n${issues}`);\n }\n throw err;\n }\n}\n\n/**\n * Save a TaskPlan to disk atomically (write-to-temp + rename).\n * This prevents corruption if the process is interrupted during write.\n */\nexport async function saveTaskPlan(path: string, plan: TaskPlan): Promise<void> {\n const tmpDir = await mkdtemp(join(tmpdir(), 'issue-flow-task-plan-'));\n const tmpFile = join(tmpDir, 'tasks.json');\n\n await writeFile(tmpFile, `${JSON.stringify(plan, null, 2)}\\n`, 'utf-8');\n\n try {\n await rename(tmpFile, path);\n } catch (err: unknown) {\n // EXDEV: rename fails across different devices/drives (common on Windows)\n if ((err as NodeJS.ErrnoException).code === 'EXDEV') {\n await copyFile(tmpFile, path);\n await unlink(tmpFile);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Initialize default state fields on a TaskPlan.\n * Fills in missing fields with defaults, matching the Bash script behavior.\n */\nexport function initializeState(plan: TaskPlan): TaskPlan {\n const defaultPipeline: PipelineState = {\n prdCompleted: false,\n jsonCompleted: false,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n };\n\n return {\n ...plan,\n issueStatus: plan.issueStatus ?? 'pending',\n completedAt: plan.completedAt ?? null,\n lastAttemptAt: plan.lastAttemptAt ?? null,\n lastError: plan.lastError ?? null,\n correctionCycle: plan.correctionCycle ?? 0,\n maxCorrectionCycles: plan.maxCorrectionCycles ?? 3,\n pipeline: plan.pipeline ? { ...defaultPipeline, ...plan.pipeline } : defaultPipeline,\n };\n}\n\n/**\n * Check if all user stories have passes=true.\n * Returns false if there are no stories.\n */\nexport function allStoriesPass(plan: TaskPlan): boolean {\n if (plan.userStories.length === 0) {\n return false;\n }\n\n return plan.userStories.every((story) => story.passes === true);\n}\n\n/**\n * Mark a specific story as passing.\n */\nexport function markStoryPassing(plan: TaskPlan, storyId: string): TaskPlan {\n return {\n ...plan,\n userStories: plan.userStories.map((story) =>\n story.id === storyId ? { ...story, passes: true } : story,\n ),\n };\n}\n\n/**\n * Mark the issue as in_progress.\n */\nexport function markIssueInProgress(plan: TaskPlan, timestamp?: string): TaskPlan {\n const ts = timestamp ?? isoNow();\n return {\n ...plan,\n issueStatus: 'in_progress',\n completedAt: null,\n lastAttemptAt: ts,\n };\n}\n\n/**\n * Mark the issue as completed.\n */\nexport function markIssueCompleted(plan: TaskPlan): TaskPlan {\n const ts = isoNow();\n return {\n ...plan,\n issueStatus: 'completed',\n completedAt: ts,\n lastAttemptAt: ts,\n lastError: null,\n pipeline: plan.pipeline ? { ...plan.pipeline, executionCompleted: true } : plan.pipeline,\n };\n}\n\n/**\n * Set the lastError field on the task plan.\n */\nexport function setLastError(plan: TaskPlan, category: string, message: string): TaskPlan {\n const ts = isoNow();\n const error: LastError = {\n category,\n message,\n at: ts,\n };\n\n return {\n ...plan,\n lastAttemptAt: ts,\n lastError: error,\n };\n}\n\n/**\n * Clear the lastError field, but only if it was set before the attempt started.\n * This prevents clearing errors that were set during the current attempt.\n */\nexport function clearLastError(plan: TaskPlan, attemptStartedAt: string): TaskPlan {\n const ts = isoNow();\n\n // If the error was set after the attempt started, keep it\n if (plan.lastError && plan.lastError.at > attemptStartedAt) {\n return {\n ...plan,\n lastAttemptAt: ts,\n };\n }\n\n return {\n ...plan,\n lastAttemptAt: ts,\n lastError: null,\n };\n}\n\n/**\n * Trim an error message to at most 8 non-empty lines.\n */\nexport function trimErrorMessage(message: string): string {\n return message\n .split('\\n')\n .filter((line) => line.trim().length > 0)\n .slice(0, 8)\n .join('\\n');\n}\n"],"mappings":"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","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  loadTaskPlan,
7
7
  saveTaskPlan,
8
8
  taskPlanSchema
9
- } from "./chunk-64FU2L2T.js";
9
+ } from "./chunk-X5QCISNM.js";
10
10
  import {
11
11
  applyPlaceholders,
12
12
  loadPrompt
@@ -82,4 +82,4 @@ ${issues}`);
82
82
  export {
83
83
  runPlan
84
84
  };
85
- //# sourceMappingURL=chunk-MWUIIUAS.js.map
85
+ //# sourceMappingURL=chunk-ZJB2IMPU.js.map
package/dist/cli.js CHANGED
@@ -52,30 +52,30 @@ withGlobalOptions(
52
52
  process.exit(code);
53
53
  });
54
54
  withGlobalOptions(
55
- program.command("run").description("Execute the full pipeline: analyze \u2192 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")
55
+ 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("--no-branch", "Run pipeline on current branch without creating a new branch or PR")
56
56
  ).action(async (issue, options) => {
57
- const { runPipeline } = await import("./run-PKHPULYL.js");
58
- const code = await runPipeline(issue, options.mode, options.from);
57
+ const { runPipeline } = await import("./run-A44XQ77Z.js");
58
+ const code = await runPipeline(issue, options.mode, options.from, options.noBranch);
59
59
  process.exit(code);
60
60
  });
61
61
  withGlobalOptions(
62
62
  program.command("analyze").description("Analyze a GitHub issue via Claude Code Headless").argument("<issue>", "Issue number")
63
63
  ).action(async (issue) => {
64
- const { runAnalyze } = await import("./analyze-3SN7ZAZU.js");
64
+ const { runAnalyze } = await import("./analyze-PDDIU7HD.js");
65
65
  const code = await runAnalyze(issue);
66
66
  process.exit(code);
67
67
  });
68
68
  withGlobalOptions(
69
69
  program.command("prd").description("Generate a PRD from an analyzed issue via Claude Code Headless").argument("<issue>", "Issue number")
70
70
  ).action(async (issue) => {
71
- const { runPrd } = await import("./prd-ET2QN7GB.js");
71
+ const { runPrd } = await import("./prd-PZOY3FCC.js");
72
72
  const code = await runPrd(issue);
73
73
  process.exit(code);
74
74
  });
75
75
  withGlobalOptions(
76
76
  program.command("plan").description("Convert a PRD to a tasks.json task plan via Claude Code Headless").argument("<issue>", "Issue number")
77
77
  ).action(async (issue) => {
78
- const { runPlan } = await import("./plan-HWZRG5GP.js");
78
+ const { runPlan } = await import("./plan-JUOBYTCD.js");
79
79
  const code = await runPlan(issue);
80
80
  process.exit(code);
81
81
  });
@@ -88,7 +88,7 @@ withGlobalOptions(
88
88
  ).action(
89
89
  async (positionalMaxIter, options) => {
90
90
  try {
91
- const { runExecute } = await import("./execute-GIA4IKS5.js");
91
+ const { runExecute } = await import("./execute-WKYSGIGH.js");
92
92
  const code = await runExecute(positionalMaxIter, options);
93
93
  process.exit(code);
94
94
  } catch (error) {
@@ -100,14 +100,14 @@ withGlobalOptions(
100
100
  withGlobalOptions(
101
101
  program.command("review").description("Validate an issue resolution via Claude Code Headless").argument("<issue>", "Issue number")
102
102
  ).action(async (issue) => {
103
- const { runReview } = await import("./review-3QDZCOMT.js");
103
+ const { runReview } = await import("./review-MK5FTVUE.js");
104
104
  const code = await runReview(issue);
105
105
  process.exit(code);
106
106
  });
107
107
  withGlobalOptions(
108
108
  program.command("pr").description("Create a pull request via Claude Code Headless").argument("<issue>", "Issue number")
109
109
  ).action(async (issue) => {
110
- const { runPr } = await import("./pr-TTOFYUCV.js");
110
+ const { runPr } = await import("./pr-CYZYJZZV.js");
111
111
  const code = await runPr(issue);
112
112
  process.exit(code);
113
113
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { setGlobalTimeout, setVerbose } from './core/verbose.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\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});\n\n// ── init ────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program.command('init').description('Verify that all prerequisites (claude, gh, git) are met'),\n).action(async () => {\n const { runInit } = await import('./commands/init.js');\n const code = await runInit();\n process.exit(code);\n});\n\n// ── generate ────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('generate')\n .description('Create a GitHub issue via Claude Code Headless')\n .requiredOption('--prompt <text>', 'Issue description text'),\n).action(async (options: { prompt: string }) => {\n const { runGenerate } = await import('./commands/generate.js');\n const code = await runGenerate(options.prompt);\n process.exit(code);\n});\n\n// ── run ─────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('run')\n .description('Execute the full pipeline: analyze → 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).action(async (issue: string, options: { mode: string; from?: string }) => {\n const { runPipeline } = await import('./commands/run.js');\n const code = await runPipeline(issue, options.mode, options.from);\n process.exit(code);\n});\n\n// ── analyze ─────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('analyze')\n .description('Analyze a GitHub issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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 ─────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('prd')\n .description('Generate a PRD from an analyzed issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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 ────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\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).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 ─────────────────────────────────────────────────────────────────\nwithGlobalOptions(\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('[max-iterations]', 'Backward-compatible alias for --max-iterations N', parseInteger),\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 ──────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('review')\n .description('Validate an issue resolution via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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 ──────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('pr')\n .description('Create a pull request via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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"],"mappings":"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","names":["require"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { setGlobalTimeout, setVerbose } from './core/verbose.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\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});\n\n// ── init ────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program.command('init').description('Verify that all prerequisites (claude, gh, git) are met'),\n).action(async () => {\n const { runInit } = await import('./commands/init.js');\n const code = await runInit();\n process.exit(code);\n});\n\n// ── generate ────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('generate')\n .description('Create a GitHub issue via Claude Code Headless')\n .requiredOption('--prompt <text>', 'Issue description text'),\n).action(async (options: { prompt: string }) => {\n const { runGenerate } = await import('./commands/generate.js');\n const code = await runGenerate(options.prompt);\n process.exit(code);\n});\n\n// ── run ─────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\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('--no-branch', 'Run pipeline on current branch without creating a new branch or PR'),\n).action(async (issue: string, options: { mode: string; from?: string; noBranch?: boolean }) => {\n const { runPipeline } = await import('./commands/run.js');\n const code = await runPipeline(issue, options.mode, options.from, options.noBranch);\n process.exit(code);\n});\n\n// ── analyze ─────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('analyze')\n .description('Analyze a GitHub issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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 ─────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('prd')\n .description('Generate a PRD from an analyzed issue via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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 ────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\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).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 ─────────────────────────────────────────────────────────────────\nwithGlobalOptions(\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('[max-iterations]', 'Backward-compatible alias for --max-iterations N', parseInteger),\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 ──────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('review')\n .description('Validate an issue resolution via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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 ──────────────────────────────────────────────────────────────────────\nwithGlobalOptions(\n program\n .command('pr')\n .description('Create a pull request via Claude Code Headless')\n .argument('<issue>', 'Issue number'),\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"],"mappings":"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","names":["require"]}
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runExecute
4
- } from "./chunk-HYP64MZI.js";
5
- import "./chunk-64FU2L2T.js";
4
+ } from "./chunk-LTPXKMPE.js";
5
+ import "./chunk-X5QCISNM.js";
6
6
  import "./chunk-OZVHOVDT.js";
7
7
  import "./chunk-YAFHVFV5.js";
8
8
  export {
9
9
  runExecute
10
10
  };
11
- //# sourceMappingURL=execute-GIA4IKS5.js.map
11
+ //# sourceMappingURL=execute-WKYSGIGH.js.map
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runPlan
4
- } from "./chunk-MWUIIUAS.js";
4
+ } from "./chunk-ZJB2IMPU.js";
5
5
  import "./chunk-4OQ2ARLP.js";
6
- import "./chunk-64FU2L2T.js";
6
+ import "./chunk-X5QCISNM.js";
7
7
  import "./chunk-OZVHOVDT.js";
8
8
  import "./chunk-YAFHVFV5.js";
9
9
  export {
10
10
  runPlan
11
11
  };
12
- //# sourceMappingURL=plan-HWZRG5GP.js.map
12
+ //# sourceMappingURL=plan-JUOBYTCD.js.map
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runPr
4
- } from "./chunk-CYWHYPTU.js";
4
+ } from "./chunk-65KDGHLM.js";
5
5
  import "./chunk-4OQ2ARLP.js";
6
- import "./chunk-64FU2L2T.js";
6
+ import "./chunk-X5QCISNM.js";
7
7
  import "./chunk-OZVHOVDT.js";
8
8
  import "./chunk-YAFHVFV5.js";
9
9
  export {
10
10
  runPr
11
11
  };
12
- //# sourceMappingURL=pr-TTOFYUCV.js.map
12
+ //# sourceMappingURL=pr-CYZYJZZV.js.map
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runPrd
4
- } from "./chunk-2JZCWJEQ.js";
4
+ } from "./chunk-SKTTLX7R.js";
5
5
  import "./chunk-4OQ2ARLP.js";
6
- import "./chunk-64FU2L2T.js";
6
+ import "./chunk-X5QCISNM.js";
7
7
  import "./chunk-OZVHOVDT.js";
8
8
  import "./chunk-YAFHVFV5.js";
9
9
  export {
10
10
  runPrd
11
11
  };
12
- //# sourceMappingURL=prd-ET2QN7GB.js.map
12
+ //# sourceMappingURL=prd-PZOY3FCC.js.map
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runReview
4
- } from "./chunk-4PZXIOXD.js";
4
+ } from "./chunk-HPSU5GGE.js";
5
5
  import "./chunk-4OQ2ARLP.js";
6
- import "./chunk-64FU2L2T.js";
6
+ import "./chunk-X5QCISNM.js";
7
7
  import "./chunk-OZVHOVDT.js";
8
8
  import "./chunk-YAFHVFV5.js";
9
9
  export {
10
10
  runReview
11
11
  };
12
- //# sourceMappingURL=review-3QDZCOMT.js.map
12
+ //# sourceMappingURL=review-MK5FTVUE.js.map
@@ -1,31 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- runReview
4
- } from "./chunk-4PZXIOXD.js";
5
2
  import {
6
3
  runInit
7
4
  } from "./chunk-CSSXTPWE.js";
8
- import {
9
- runAnalyze
10
- } from "./chunk-MEB3B2FI.js";
11
5
  import {
12
6
  runExecute
13
- } from "./chunk-HYP64MZI.js";
7
+ } from "./chunk-LTPXKMPE.js";
14
8
  import {
15
9
  runPlan
16
- } from "./chunk-MWUIIUAS.js";
10
+ } from "./chunk-ZJB2IMPU.js";
17
11
  import {
18
12
  runPr
19
- } from "./chunk-CYWHYPTU.js";
13
+ } from "./chunk-65KDGHLM.js";
20
14
  import {
21
15
  runPrd
22
- } from "./chunk-2JZCWJEQ.js";
16
+ } from "./chunk-SKTTLX7R.js";
17
+ import {
18
+ runReview
19
+ } from "./chunk-HPSU5GGE.js";
23
20
  import "./chunk-4OQ2ARLP.js";
24
21
  import {
25
22
  isoNow,
26
23
  loadTaskPlan,
27
24
  saveTaskPlan
28
- } from "./chunk-64FU2L2T.js";
25
+ } from "./chunk-X5QCISNM.js";
29
26
  import "./chunk-OZVHOVDT.js";
30
27
  import {
31
28
  formatDuration,
@@ -45,16 +42,17 @@ import { execa } from "execa";
45
42
  // src/core/pipeline.ts
46
43
  var PIPELINE_PHASES = [
47
44
  "init",
48
- "analyze",
49
45
  "prd",
50
46
  "plan",
51
47
  "execute",
52
48
  "review",
53
49
  "pr"
54
50
  ];
51
+ var PIPELINE_PHASES_NO_BRANCH = PIPELINE_PHASES.filter(
52
+ (p) => p !== "pr"
53
+ );
55
54
  var PHASE_TO_FIELD = {
56
55
  init: null,
57
- analyze: "analyzeCompleted",
58
56
  prd: "prdCompleted",
59
57
  plan: "jsonCompleted",
60
58
  execute: "executionCompleted",
@@ -64,9 +62,11 @@ var PHASE_TO_FIELD = {
64
62
  var PipelineManager = class {
65
63
  tasksJsonPath;
66
64
  plan;
67
- constructor(plan, tasksJsonPath) {
65
+ activePhases;
66
+ constructor(plan, tasksJsonPath, activePhases) {
68
67
  this.plan = plan;
69
68
  this.tasksJsonPath = tasksJsonPath;
69
+ this.activePhases = activePhases ?? PIPELINE_PHASES;
70
70
  }
71
71
  /**
72
72
  * Reload state from disk.
@@ -87,7 +87,7 @@ var PipelineManager = class {
87
87
  * Returns null if all phases are complete.
88
88
  */
89
89
  getNextPhase() {
90
- for (const phase of PIPELINE_PHASES) {
90
+ for (const phase of this.activePhases) {
91
91
  if (!this.isPhaseComplete(phase)) {
92
92
  return phase;
93
93
  }
@@ -105,10 +105,10 @@ var PipelineManager = class {
105
105
  * All prerequisite phases must be complete.
106
106
  */
107
107
  canResume(fromPhase) {
108
- const idx = PIPELINE_PHASES.indexOf(fromPhase);
108
+ const idx = this.activePhases.indexOf(fromPhase);
109
109
  if (idx < 0) return false;
110
110
  for (let i = 0; i < idx; i++) {
111
- if (!this.isPhaseComplete(PIPELINE_PHASES[i])) {
111
+ if (!this.isPhaseComplete(this.activePhases[i])) {
112
112
  return false;
113
113
  }
114
114
  }
@@ -124,7 +124,6 @@ var PipelineManager = class {
124
124
  ...this.plan,
125
125
  pipeline: {
126
126
  ...this.plan.pipeline ?? {
127
- analyzeCompleted: false,
128
127
  prdCompleted: false,
129
128
  jsonCompleted: false,
130
129
  executionCompleted: false,
@@ -141,7 +140,6 @@ var PipelineManager = class {
141
140
  // src/ui/pipeline-renderer.ts
142
141
  import { Listr, PRESET_TIMER, PRESET_TIMESTAMP } from "listr2";
143
142
  var PHASE_LABELS = {
144
- analyze: "Analyze",
145
143
  prd: "PRD",
146
144
  plan: "Plan",
147
145
  execute: "Execute",
@@ -221,7 +219,7 @@ function buildExecutePhaseTask(runner, tasksPath, verbose) {
221
219
  timer: PRESET_TIMER,
222
220
  outputBar: verbose ? Infinity : false,
223
221
  persistentOutput: false,
224
- bottomBar: verbose ? Infinity : 5
222
+ bottomBar: verbose ? Infinity : 3
225
223
  }
226
224
  };
227
225
  return task.newListr([engineSubtask, ...subtaskDefs], {
@@ -304,7 +302,9 @@ async function runPipelineWithRenderer(options) {
304
302
  }
305
303
 
306
304
  // src/commands/run.ts
307
- async function runPipeline(issue, mode, from) {
305
+ var RUNNABLE_PHASES = ["prd", "plan", "execute", "review", "pr"];
306
+ var RUNNABLE_PHASES_NO_BRANCH = ["prd", "plan", "execute", "review"];
307
+ async function runPipeline(issue, mode, from, noBranch) {
308
308
  const issueNumber = issue.replace(/^#/, "");
309
309
  const issueDir = join("issues", issueNumber);
310
310
  const tasksPath = join(issueDir, "tasks.json");
@@ -315,17 +315,44 @@ async function runPipeline(issue, mode, from) {
315
315
  printError("Prerequisites not met. Fix the issues above and try again.");
316
316
  return 1;
317
317
  }
318
- let startPhase = "analyze";
318
+ let effectiveNoBranch = noBranch ?? false;
319
+ try {
320
+ const existingPlan = await loadTaskPlan(tasksPath);
321
+ const persistedNoBranch = existingPlan.noBranch ?? false;
322
+ if (noBranch !== void 0 && noBranch !== persistedNoBranch) {
323
+ if (persistedNoBranch) {
324
+ printWarning(
325
+ "This pipeline was started with --no-branch. Ignoring current flag; using persisted mode."
326
+ );
327
+ } else {
328
+ printWarning(
329
+ "This pipeline was started without --no-branch. Ignoring current flag; using persisted mode."
330
+ );
331
+ }
332
+ }
333
+ effectiveNoBranch = persistedNoBranch;
334
+ } catch {
335
+ }
336
+ const activePhases = effectiveNoBranch ? PIPELINE_PHASES_NO_BRANCH : PIPELINE_PHASES;
337
+ const phaseOrder = effectiveNoBranch ? RUNNABLE_PHASES_NO_BRANCH : RUNNABLE_PHASES;
338
+ let startPhase = "prd";
319
339
  if (from) {
320
- if (!PIPELINE_PHASES.includes(from)) {
321
- printError(`Invalid phase: ${from}. Valid phases: ${PIPELINE_PHASES.join(", ")}`);
340
+ if (!activePhases.includes(from)) {
341
+ const validPhases = activePhases.filter((p) => p !== "init").join(", ");
342
+ if (effectiveNoBranch && PIPELINE_PHASES.includes(from)) {
343
+ printError(
344
+ `The '${from}' phase is not available in --no-branch mode. Valid phases: ${validPhases}`
345
+ );
346
+ } else {
347
+ printError(`Invalid phase: ${from}. Valid phases: ${validPhases}`);
348
+ }
322
349
  return 1;
323
350
  }
324
351
  startPhase = from;
325
352
  } else {
326
353
  try {
327
354
  const plan = await loadTaskPlan(tasksPath);
328
- const mgr = new PipelineManager(plan, tasksPath);
355
+ const mgr = new PipelineManager(plan, tasksPath, activePhases);
329
356
  const nextPhase = mgr.getNextPhase();
330
357
  if (nextPhase && nextPhase !== "init") {
331
358
  startPhase = nextPhase;
@@ -337,19 +364,18 @@ async function runPipeline(issue, mode, from) {
337
364
  if (from) {
338
365
  try {
339
366
  const plan = await loadTaskPlan(tasksPath);
340
- const mgr = new PipelineManager(plan, tasksPath);
367
+ const mgr = new PipelineManager(plan, tasksPath, activePhases);
341
368
  if (!mgr.canResume(startPhase)) {
342
369
  printError(`Cannot resume from ${startPhase}: prerequisite phases not complete`);
343
370
  return 1;
344
371
  }
345
372
  } catch {
346
- if (startPhase !== "analyze") {
373
+ if (startPhase !== "prd") {
347
374
  printError(`Cannot resume from ${startPhase}: no pipeline state found`);
348
375
  return 1;
349
376
  }
350
377
  }
351
378
  }
352
- const phaseOrder = ["analyze", "prd", "plan", "execute", "review", "pr"];
353
379
  const startIdx = phaseOrder.indexOf(startPhase);
354
380
  const makeRunner = (fn, phase) => async () => {
355
381
  const code = await fn();
@@ -358,9 +384,18 @@ async function runPipeline(issue, mode, from) {
358
384
  }
359
385
  };
360
386
  const runners = {
361
- analyze: makeRunner(() => runAnalyze(issueNumber), "analyze"),
362
387
  prd: makeRunner(() => runPrd(issueNumber), "prd"),
363
- plan: makeRunner(() => runPlan(issueNumber), "plan"),
388
+ plan: async () => {
389
+ await makeRunner(() => runPlan(issueNumber), "plan")();
390
+ if (effectiveNoBranch) {
391
+ try {
392
+ const plan = await loadTaskPlan(tasksPath);
393
+ plan.noBranch = true;
394
+ await saveTaskPlan(tasksPath, plan);
395
+ } catch {
396
+ }
397
+ }
398
+ },
364
399
  execute: makeRunner(() => runExecute(void 0, { issue: issueNumber }), "execute"),
365
400
  review: async () => {
366
401
  let maxCycles = 3;
@@ -410,15 +445,19 @@ async function runPipeline(issue, mode, from) {
410
445
  printWarning("Failed to close issue automatically");
411
446
  }
412
447
  let prUrl = "unknown";
413
- try {
414
- const proc = await execa("gh", ["pr", "list", "--head", "", "--json", "url", "--limit", "1"], {
415
- reject: false
416
- });
417
- const parsed = JSON.parse(proc.stdout?.toString() ?? "[]");
418
- if (parsed[0]?.url) {
419
- prUrl = parsed[0].url;
448
+ if (!effectiveNoBranch) {
449
+ try {
450
+ const proc = await execa(
451
+ "gh",
452
+ ["pr", "list", "--head", "", "--json", "url", "--limit", "1"],
453
+ { reject: false }
454
+ );
455
+ const parsed = JSON.parse(proc.stdout?.toString() ?? "[]");
456
+ if (parsed[0]?.url) {
457
+ prUrl = parsed[0].url;
458
+ }
459
+ } catch {
420
460
  }
421
- } catch {
422
461
  }
423
462
  let branchName = "unknown";
424
463
  try {
@@ -439,13 +478,15 @@ async function runPipeline(issue, mode, from) {
439
478
  const totalDuration = formatDuration(result.overallElapsedSeconds);
440
479
  console.log("");
441
480
  printSuccess(`Pipeline complete for issue #${issueNumber}!`);
442
- console.log(` Branch: ${branchName}`);
481
+ console.log(` Branch: ${branchName}${effectiveNoBranch ? " (current)" : ""}`);
443
482
  console.log(` Stories: ${storyCount}`);
444
483
  console.log(` Duration: ${totalDuration}`);
445
- console.log(` PR: ${prUrl}`);
484
+ if (!effectiveNoBranch) {
485
+ console.log(` PR: ${prUrl}`);
486
+ }
446
487
  return 0;
447
488
  }
448
489
  export {
449
490
  runPipeline
450
491
  };
451
- //# sourceMappingURL=run-PKHPULYL.js.map
492
+ //# sourceMappingURL=run-A44XQ77Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/run.ts","../src/core/pipeline.ts","../src/ui/pipeline-renderer.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { execa } from 'execa';\nimport {\n PIPELINE_PHASES,\n PIPELINE_PHASES_NO_BRANCH,\n PipelineManager,\n type PipelinePhase,\n} from '../core/pipeline.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { isVerbose } from '../core/verbose.js';\nimport { formatDuration, printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\nimport { runPipelineWithRenderer } from '../ui/pipeline-renderer.js';\nimport { runExecute } from './execute.js';\nimport { runInit } from './init.js';\nimport { runPlan } from './plan.js';\nimport { runPr } from './pr.js';\nimport { runPrd } from './prd.js';\nimport { runReview } from './review.js';\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'];\n\nexport async function runPipeline(\n issue: string,\n mode: string,\n from?: string,\n noBranch?: boolean,\n): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const tasksPath = join(issueDir, 'tasks.json');\n\n printInfo(`Starting pipeline for issue #${issueNumber} (mode: ${mode})`);\n\n // Phase 1: Init check\n printInfo('Running prerequisite checks...');\n const initCode = await runInit();\n if (initCode !== 0) {\n printError('Prerequisites not met. Fix the issues above and try again.');\n return 1;\n }\n\n // Resolve noBranch mode: persisted value takes precedence on resume\n let effectiveNoBranch = noBranch ?? false;\n try {\n const existingPlan = await loadTaskPlan(tasksPath);\n const persistedNoBranch = existingPlan.noBranch ?? 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 const activePhases = effectiveNoBranch ? PIPELINE_PHASES_NO_BRANCH : PIPELINE_PHASES;\n const phaseOrder = effectiveNoBranch ? RUNNABLE_PHASES_NO_BRANCH : RUNNABLE_PHASES;\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 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 1;\n }\n } catch {\n if (startPhase !== 'prd') {\n printError(`Cannot resume from ${startPhase}: no pipeline state found`);\n return 1;\n }\n }\n }\n\n const startIdx = phaseOrder.indexOf(startPhase);\n\n // Build phase runner functions that throw on failure\n const makeRunner = (fn: () => Promise<number>, phase: string) => async () => {\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), 'prd'),\n plan: async () => {\n await makeRunner(() => runPlan(issueNumber), 'plan')();\n // Persist noBranch into the newly created tasks.json\n if (effectiveNoBranch) {\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.noBranch = true;\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 },\n execute: makeRunner(() => runExecute(undefined, { issue: issueNumber }), 'execute'),\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);\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\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, { issue: issueNumber });\n if (execCode !== 0) {\n throw new Error('Correction execution failed');\n }\n\n // Re-review\n code = await runReview(issueNumber);\n }\n\n if (code !== 0) {\n throw new Error(`Review failed after ${maxCycles} correction cycles`);\n }\n },\n pr: makeRunner(() => runPr(issueNumber), 'pr'),\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,\n tasksPath,\n });\n\n if (!result.success) {\n printError(`Phase ${result.failedPhase} failed`);\n return 1;\n }\n\n // Close the issue\n printInfo('Closing issue...');\n try {\n await execa('gh', ['issue', 'close', issueNumber], { reject: false });\n } catch {\n printWarning('Failed to close issue automatically');\n }\n\n // Get PR URL for summary (skip in --no-branch mode)\n let prUrl = 'unknown';\n if (!effectiveNoBranch) {\n try {\n const proc = await execa(\n 'gh',\n ['pr', 'list', '--head', '', '--json', 'url', '--limit', '1'],\n { reject: false },\n );\n const parsed = JSON.parse(proc.stdout?.toString() ?? '[]');\n if (parsed[0]?.url) {\n prUrl = parsed[0].url;\n }\n } catch {\n /* non-critical */\n }\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 try {\n const plan = await loadTaskPlan(tasksPath);\n storyCount = plan.userStories.length;\n\n // Mark as completed\n plan.issueStatus = 'completed';\n plan.completedAt = isoNow();\n plan.lastAttemptAt = isoNow();\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical */\n }\n\n const totalDuration = formatDuration(result.overallElapsedSeconds);\n\n console.log('');\n printSuccess(`Pipeline complete for issue #${issueNumber}!`);\n console.log(` Branch: ${branchName}${effectiveNoBranch ? ' (current)' : ''}`);\n console.log(` Stories: ${storyCount}`);\n console.log(` Duration: ${totalDuration}`);\n if (!effectiveNoBranch) {\n console.log(` PR: ${prUrl}`);\n }\n\n return 0;\n}\n","import type { PipelineState, TaskPlan } from '../types.js';\nimport { loadTaskPlan, saveTaskPlan } from './state-manager.js';\n\n/**\n * Ordered pipeline phases. Each phase must complete before the next can start.\n */\nexport const PIPELINE_PHASES = [\n 'init',\n 'prd',\n 'plan',\n 'execute',\n 'review',\n 'pr',\n] as const;\n\nexport type PipelinePhase = (typeof PIPELINE_PHASES)[number];\n\n/**\n * Pipeline phases excluding the 'pr' phase, for --no-branch mode.\n */\nexport const PIPELINE_PHASES_NO_BRANCH = PIPELINE_PHASES.filter(\n (p): p is Exclude<PipelinePhase, 'pr'> => p !== 'pr',\n);\n\n/**\n * Map pipeline phases to their corresponding PipelineState field.\n * 'init' has no persisted state — it's a runtime-only check.\n */\nconst PHASE_TO_FIELD: Record<PipelinePhase, keyof PipelineState | null> = {\n init: null,\n prd: 'prdCompleted',\n plan: 'jsonCompleted',\n execute: 'executionCompleted',\n review: 'reviewCompleted',\n pr: 'prCreated',\n};\n\nexport class PipelineManager {\n private tasksJsonPath: string;\n private plan: TaskPlan;\n private activePhases: readonly PipelinePhase[];\n\n constructor(plan: TaskPlan, tasksJsonPath: string, activePhases?: readonly PipelinePhase[]) {\n this.plan = plan;\n this.tasksJsonPath = tasksJsonPath;\n this.activePhases = activePhases ?? PIPELINE_PHASES;\n }\n\n /**\n * Reload state from disk.\n */\n async reload(): Promise<void> {\n this.plan = await loadTaskPlan(this.tasksJsonPath);\n }\n\n /**\n * Check if a phase is complete.\n */\n isPhaseComplete(phase: PipelinePhase): boolean {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return true; // init is always \"complete\" after running\n return this.plan.pipeline?.[field] ?? false;\n }\n\n /**\n * Get the first incomplete phase in the pipeline.\n * Returns null if all phases are complete.\n */\n getNextPhase(): PipelinePhase | null {\n for (const phase of this.activePhases) {\n if (!this.isPhaseComplete(phase)) {\n return phase;\n }\n }\n return null;\n }\n\n /**\n * Get the current phase (alias for getNextPhase).\n */\n getCurrentPhase(): PipelinePhase | null {\n return this.getNextPhase();\n }\n\n /**\n * Check whether we can resume from a specific phase.\n * All prerequisite phases must be complete.\n */\n canResume(fromPhase: PipelinePhase): boolean {\n const idx = this.activePhases.indexOf(fromPhase);\n if (idx < 0) return false;\n\n // All active phases before fromPhase must be complete\n for (let i = 0; i < idx; i++) {\n if (!this.isPhaseComplete(this.activePhases[i])) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Mark a phase as complete and persist to disk.\n */\n async markPhaseComplete(phase: PipelinePhase): Promise<void> {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return; // init has no persisted state\n\n this.plan = {\n ...this.plan,\n pipeline: {\n ...(this.plan.pipeline ?? {\n prdCompleted: false,\n jsonCompleted: false,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n }),\n [field]: true,\n },\n };\n\n await saveTaskPlan(this.tasksJsonPath, this.plan);\n }\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 { setOutputCallback, setStoryUpdateCallback } 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 */\nconst PHASE_LABELS: Record<string, string> = {\n prd: 'PRD',\n plan: 'Plan',\n execute: 'Execute',\n review: 'Review',\n pr: 'PR',\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 * 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 // Build subtask definitions for each story\n const subtaskDefs = stories.map((story) => ({\n title: `${story.id}: ${story.title}`,\n skip: story.passes ? 'already passing' : false,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n task: async () => {\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 // 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 return task.newListr([engineSubtask, ...subtaskDefs], {\n concurrent: true,\n exitOnError: false,\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 = PHASE_LABELS[phase] ?? phase.charAt(0).toUpperCase() + phase.slice(1);\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 }\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "issue-flow",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Unified CLI for orchestrating the full issue-flow pipeline via Claude Code Headless",
5
5
  "type": "module",
6
6
  "bin": {
package/prompts/prd.md CHANGED
@@ -1,9 +1,10 @@
1
- You are generating a Product Requirements Document (PRD) for GitHub issue #__ISSUE_NUMBER__ in this repository.__ANALYSIS_CONTEXT__
1
+ You are generating a Product Requirements Document (PRD) for GitHub issue #__ISSUE_NUMBER__ in this repository.
2
2
 
3
3
  Steps:
4
- 1. If no analysis was provided above, fetch the issue data using: gh issue view __ISSUE_NUMBER__ --json title,body,labels,comments
5
- 2. Analyze the codebase to understand the context
6
- 3. Generate a structured PRD
4
+ 1. Fetch the issue data using: gh issue view __ISSUE_NUMBER__ --json title,body,labels,comments
5
+ 2. If the file issues/__ISSUE_NUMBER__/analysis.md exists, read it for additional context
6
+ 3. Analyze the codebase to understand the context
7
+ 4. Generate a structured PRD
7
8
 
8
9
  Save the PRD to __PRD_PATH__ with this structure:
9
10
 
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- runAnalyze
4
- } from "./chunk-MEB3B2FI.js";
5
- import "./chunk-4OQ2ARLP.js";
6
- import "./chunk-64FU2L2T.js";
7
- import "./chunk-OZVHOVDT.js";
8
- import "./chunk-YAFHVFV5.js";
9
- export {
10
- runAnalyze
11
- };
12
- //# sourceMappingURL=analyze-3SN7ZAZU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/prd.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport async function runPrd(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const prdPath = join(issueDir, 'prd.md');\n\n await mkdir(issueDir, { recursive: true });\n\n // Read analysis if it exists\n let analysisContext = '';\n const analysisPath = join(issueDir, 'analysis.md');\n try {\n const content = await readFile(analysisPath, 'utf-8');\n analysisContext = `\\n\\nHere is the existing analysis for this issue:\\n\\n${content}`;\n } catch {\n // No analysis available — that's OK\n }\n\n const template = await loadPrompt('prd');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __ANALYSIS_CONTEXT__: analysisContext,\n __PRD_PATH__: prdPath,\n });\n\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 printError(`PRD generation failed: ${result.error}`);\n return 1;\n }\n\n // Verify the file was created\n try {\n const content = await readFile(prdPath, 'utf-8');\n if (content.length < 10) {\n printError('PRD file was created but appears empty');\n return 1;\n }\n } catch {\n printError(`PRD file was not created at ${prdPath}`);\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,OAAO,gBAAgB;AAChC,SAAS,YAAY;AAOrB,eAAsB,OAAO,OAAgC;AAC3D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,UAAU,KAAK,UAAU,QAAQ;AAEvC,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAGzC,MAAI,kBAAkB;AACtB,QAAM,eAAe,KAAK,UAAU,aAAa;AACjD,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,cAAc,OAAO;AACpD,sBAAkB;AAAA;AAAA;AAAA;AAAA,EAAwD,OAAO;AAAA,EACnF,QAAQ;AAAA,EAER;AAEA,QAAM,WAAW,MAAM,WAAW,KAAK;AACvC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,sBAAsB;AAAA,IACtB,cAAc;AAAA,EAChB,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,QAAQ,OAAO;AAAA,IACtD,eAAe,6BAA6B,WAAW;AAAA,EACzD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,0BAA0B,OAAO,KAAK,EAAE;AACnD,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,QAAI,QAAQ,SAAS,IAAI;AACvB,iBAAW,wCAAwC;AACnD,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AACN,eAAW,+BAA+B,OAAO,EAAE;AACnD,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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schemas.ts","../src/core/state-manager.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Zod schemas for validating tasks.json structure and headless invocation outputs.\n */\n\nexport const userStorySchema = z.object({\n id: z.string(),\n title: z.string(),\n description: z.string(),\n acceptanceCriteria: z.array(z.string()),\n priority: z.number().int().positive(),\n passes: z.boolean(),\n notes: z.string(),\n});\n\nexport const pipelineStateSchema = z.object({\n analyzeCompleted: z.boolean(),\n prdCompleted: z.boolean(),\n jsonCompleted: z.boolean(),\n executionCompleted: z.boolean(),\n reviewCompleted: z.boolean(),\n prCreated: z.boolean(),\n});\n\nconst lastErrorSchema = z.object({\n category: z.string(),\n message: z.string(),\n at: z.string(),\n});\n\nexport const taskPlanSchema = z.object({\n project: z.string(),\n issueNumber: z.number().int().positive(),\n issueUrl: z.string(),\n branchName: z.string(),\n description: z.string(),\n issueStatus: z.enum(['pending', 'in_progress', 'completed']),\n completedAt: z.string().nullable(),\n lastAttemptAt: z.string().nullable(),\n lastError: lastErrorSchema.nullable(),\n correctionCycle: z.number().int().min(0),\n maxCorrectionCycles: z.number().int().min(0),\n pipeline: pipelineStateSchema,\n userStories: z.array(userStorySchema),\n});\n\nexport const headlessResultSchema = z.object({\n success: z.boolean(),\n result: z.string(),\n cost: z\n .object({\n inputTokens: z.number().optional(),\n outputTokens: z.number().optional(),\n })\n .nullable(),\n error: z.string().nullable(),\n});\n\nexport type ValidatedTaskPlan = z.infer<typeof taskPlanSchema>;\nexport type ValidatedHeadlessResult = z.infer<typeof headlessResultSchema>;\n","import { copyFile, mkdtemp, readFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { ZodError } from 'zod';\nimport { taskPlanSchema } from '../schemas.js';\nimport type { LastError, PipelineState, TaskPlan } from '../types.js';\n\n/**\n * Get the current ISO timestamp.\n */\nexport function isoNow(): string {\n return new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n\n/**\n * Load and parse a tasks.json file.\n * Validates that it has the expected structure.\n */\nexport async function loadTaskPlan(path: string): Promise<TaskPlan> {\n const content = await readFile(path, 'utf-8');\n const raw = JSON.parse(content);\n\n try {\n return taskPlanSchema.parse(raw) as TaskPlan;\n } catch (err) {\n if (err instanceof ZodError) {\n const issues = err.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid tasks.json at ${path}:\\n${issues}`);\n }\n throw err;\n }\n}\n\n/**\n * Save a TaskPlan to disk atomically (write-to-temp + rename).\n * This prevents corruption if the process is interrupted during write.\n */\nexport async function saveTaskPlan(path: string, plan: TaskPlan): Promise<void> {\n const tmpDir = await mkdtemp(join(tmpdir(), 'issue-flow-task-plan-'));\n const tmpFile = join(tmpDir, 'tasks.json');\n\n await writeFile(tmpFile, `${JSON.stringify(plan, null, 2)}\\n`, 'utf-8');\n\n try {\n await rename(tmpFile, path);\n } catch (err: unknown) {\n // EXDEV: rename fails across different devices/drives (common on Windows)\n if ((err as NodeJS.ErrnoException).code === 'EXDEV') {\n await copyFile(tmpFile, path);\n await unlink(tmpFile);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Initialize default state fields on a TaskPlan.\n * Fills in missing fields with defaults, matching the Bash script behavior.\n */\nexport function initializeState(plan: TaskPlan): TaskPlan {\n const defaultPipeline: PipelineState = {\n analyzeCompleted: false,\n prdCompleted: false,\n jsonCompleted: false,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n };\n\n return {\n ...plan,\n issueStatus: plan.issueStatus ?? 'pending',\n completedAt: plan.completedAt ?? null,\n lastAttemptAt: plan.lastAttemptAt ?? null,\n lastError: plan.lastError ?? null,\n correctionCycle: plan.correctionCycle ?? 0,\n maxCorrectionCycles: plan.maxCorrectionCycles ?? 3,\n pipeline: plan.pipeline ? { ...defaultPipeline, ...plan.pipeline } : defaultPipeline,\n };\n}\n\n/**\n * Check if all user stories have passes=true.\n * Returns false if there are no stories.\n */\nexport function allStoriesPass(plan: TaskPlan): boolean {\n if (plan.userStories.length === 0) {\n return false;\n }\n\n return plan.userStories.every((story) => story.passes === true);\n}\n\n/**\n * Mark a specific story as passing.\n */\nexport function markStoryPassing(plan: TaskPlan, storyId: string): TaskPlan {\n return {\n ...plan,\n userStories: plan.userStories.map((story) =>\n story.id === storyId ? { ...story, passes: true } : story,\n ),\n };\n}\n\n/**\n * Mark the issue as in_progress.\n */\nexport function markIssueInProgress(plan: TaskPlan, timestamp?: string): TaskPlan {\n const ts = timestamp ?? isoNow();\n return {\n ...plan,\n issueStatus: 'in_progress',\n completedAt: null,\n lastAttemptAt: ts,\n };\n}\n\n/**\n * Mark the issue as completed.\n */\nexport function markIssueCompleted(plan: TaskPlan): TaskPlan {\n const ts = isoNow();\n return {\n ...plan,\n issueStatus: 'completed',\n completedAt: ts,\n lastAttemptAt: ts,\n lastError: null,\n pipeline: plan.pipeline ? { ...plan.pipeline, executionCompleted: true } : plan.pipeline,\n };\n}\n\n/**\n * Set the lastError field on the task plan.\n */\nexport function setLastError(plan: TaskPlan, category: string, message: string): TaskPlan {\n const ts = isoNow();\n const error: LastError = {\n category,\n message,\n at: ts,\n };\n\n return {\n ...plan,\n lastAttemptAt: ts,\n lastError: error,\n };\n}\n\n/**\n * Clear the lastError field, but only if it was set before the attempt started.\n * This prevents clearing errors that were set during the current attempt.\n */\nexport function clearLastError(plan: TaskPlan, attemptStartedAt: string): TaskPlan {\n const ts = isoNow();\n\n // If the error was set after the attempt started, keep it\n if (plan.lastError && plan.lastError.at > attemptStartedAt) {\n return {\n ...plan,\n lastAttemptAt: ts,\n };\n }\n\n return {\n ...plan,\n lastAttemptAt: ts,\n lastError: null,\n };\n}\n\n/**\n * Trim an error message to at most 8 non-empty lines.\n */\nexport function trimErrorMessage(message: string): string {\n return message\n .split('\\n')\n .filter((line) => line.trim().length > 0)\n .slice(0, 8)\n .join('\\n');\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config.ts","../src/utils/shell.ts","../src/utils/git.ts","../src/core/engine.ts","../src/ui/progress.ts","../src/ui/summary.ts","../src/utils/retry.ts","../src/core/executor.ts","../src/commands/execute.ts"],"sourcesContent":["import { platform } from 'node:os';\nimport { join } from 'node:path';\nimport type { EngineConfig, ResolvedPaths } from './types.js';\nimport { getProjectRoot } from './utils/git.js';\nimport { run } from './utils/shell.js';\n\n/**\n * Default configuration values — matching the Bash script exactly.\n */\nexport const DEFAULTS = {\n retryLimit: 10,\n retryForever: false,\n backoffBaseSeconds: 30,\n backoffMaxSeconds: 900,\n} as const;\n\n/**\n * Create a EngineConfig with defaults merged with provided options.\n */\nexport function createConfig(options: Partial<EngineConfig>): EngineConfig {\n return {\n issueNumber: options.issueNumber,\n maxIterations: options.maxIterations,\n retryLimit: options.retryLimit ?? DEFAULTS.retryLimit,\n retryForever: options.retryForever ?? DEFAULTS.retryForever,\n backoffBaseSeconds: options.backoffBaseSeconds ?? DEFAULTS.backoffBaseSeconds,\n backoffMaxSeconds: options.backoffMaxSeconds ?? DEFAULTS.backoffMaxSeconds,\n };\n}\n\n/**\n * Resolve file paths based on issue number and project root.\n *\n * With --issue N:\n * prdFile = {projectRoot}/issues/{N}/tasks.json\n * progressFile = {projectRoot}/issues/{N}/progress.txt\n *\n * Standalone:\n * prdFile = {projectRoot}/prd.json\n * progressFile = {projectRoot}/progress.txt\n */\nexport async function resolvePaths(\n config: EngineConfig,\n scriptDir?: string,\n): Promise<ResolvedPaths> {\n const projectRoot = await getProjectRoot();\n\n if (config.issueNumber) {\n const issueDir = join(projectRoot, 'issues', config.issueNumber);\n return {\n prdFile: join(issueDir, 'tasks.json'),\n progressFile: join(issueDir, 'progress.txt'),\n archiveDir: join(issueDir, 'archive'),\n lastBranchFile: join(issueDir, '.last-branch'),\n projectRoot,\n };\n }\n\n // Standalone mode — use scriptDir if available, otherwise projectRoot\n const base = scriptDir ?? projectRoot;\n return {\n prdFile: join(base, 'prd.json'),\n progressFile: join(base, 'progress.txt'),\n archiveDir: join(base, 'archive'),\n lastBranchFile: join(base, '.last-branch'),\n projectRoot,\n };\n}\n\n/**\n * Return a platform-appropriate install hint for a given package.\n */\nexport function getInstallHint(pkg: string): string {\n const os = platform();\n\n if (os === 'darwin') {\n return `brew install ${pkg}`;\n }\n if (os === 'linux') {\n return `apt install ${pkg} (or your distro's package manager)`;\n }\n if (os === 'win32') {\n return `winget install ${pkg} (or choco install ${pkg})`;\n }\n\n return `install ${pkg} using your system package manager`;\n}\n\n/**\n * Validate that required external dependencies are available.\n * Returns an array of error messages (empty if all deps are found).\n */\nexport async function validateDependencies(): Promise<string[]> {\n const errors: string[] = [];\n\n // Check git\n const gitResult = await run('git', ['--version']);\n if (gitResult.exitCode !== 0) {\n errors.push(` - git (install with: ${getInstallHint('git')})`);\n }\n\n // Check claude\n const claudeResult = await run('claude', ['--version']);\n if (claudeResult.exitCode !== 0) {\n errors.push(' - claude (install with: npm install -g @anthropic-ai/claude-code)');\n }\n\n // Note: jq is NOT required — the TypeScript CLI handles JSON natively\n\n return errors;\n}\n","import { type Options as ExecaOptions, execa } from 'execa';\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Execute a command with arguments and capture its output.\n * Uses execa which calls execFile internally (no shell injection risk).\n * Does not throw on non-zero exit codes.\n */\nexport async function run(\n command: string,\n args: string[] = [],\n options?: ExecaOptions,\n): Promise<ExecResult> {\n const result = await execa(command, args, {\n reject: false,\n ...options,\n });\n\n return {\n stdout: result.stdout?.toString() ?? '',\n stderr: result.stderr?.toString() ?? '',\n exitCode: result.exitCode ?? 1,\n };\n}\n","import { run } from './shell.js';\n\n/**\n * Get the root directory of the current git repository.\n * Throws if not inside a git repository.\n */\nexport async function getProjectRoot(): Promise<string> {\n const result = await run('git', ['rev-parse', '--show-toplevel']);\n\n if (result.exitCode !== 0) {\n throw new Error(\n 'Not inside a git repository. Please run issue-flow from within a git project.',\n );\n }\n\n return result.stdout.trim();\n}\n\n/**\n * Get the current git branch name.\n * Returns an empty string if in detached HEAD state.\n */\nexport async function getCurrentBranch(): Promise<string> {\n const result = await run('git', ['branch', '--show-current']);\n\n if (result.exitCode !== 0) {\n throw new Error(\n 'Failed to detect git branch. Ensure git is installed and you are inside a repository.',\n );\n }\n\n return result.stdout.trim();\n}\n","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 } from '../utils/retry.js';\nimport { executeClaude } from './executor.js';\nimport { applyPlaceholders, loadPrompt } from './prompt-resolver.js';\nimport {\n allStoriesPass,\n clearLastError,\n initializeState,\n isoNow,\n loadTaskPlan,\n markIssueCompleted,\n markIssueInProgress,\n saveTaskPlan,\n setLastError,\n trimErrorMessage,\n} from './state-manager.js';\nimport { getOutputCallback, getStoryUpdateCallback } from './verbose.js';\n\n/**\n * Sleep for a given number of seconds.\n */\nfunction sleep(seconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n}\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\n if (plan.issueStatus === 'completed' && allStoriesPass(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)) {\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\n // Re-read plan to get latest state\n plan = await loadTaskPlan(paths.prdFile);\n\n printIterationHeader(i, config.maxIterations, plan.userStories);\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\n emitLog('');\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\n // Check for completion signal\n if (result.output.includes('<promise>COMPLETE</promise>')) {\n plan = await loadTaskPlan(paths.prdFile);\n if (allStoriesPass(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 'Claude returned <promise>COMPLETE</promise> before every story had passes=true.',\n );\n await saveTaskPlan(paths.prdFile, plan);\n\n emitLog('');\n printWarning(\n 'Claude returned a completion signal, but tasks.json still has pending stories. Ignoring completion and continuing.',\n );\n }\n\n printSuccess(`Iteration ${i} complete. Continuing...`);\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, TaskPlan } from '../types.js';\nimport { formatDuration, getIcons, getTermWidth, useColor, useUnicode } 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 * Transient failure patterns — matching the Bash script's detection heuristics.\n * These are checked case-insensitively against the combined output.\n */\nconst TRANSIENT_PATTERNS = [\n 'timed out',\n 'timeout',\n 'connection reset',\n 'connection refused',\n 'connection aborted',\n 'network error',\n 'network unavailable',\n 'temporary failure',\n 'temporarily unavailable',\n 'service unavailable',\n 'overloaded',\n 'rate limit',\n 'too many requests',\n 'bad gateway',\n 'gateway timeout',\n 'internal server error',\n 'http 429',\n 'http 500',\n 'http 502',\n 'http 503',\n 'http 504',\n 'econnreset',\n 'econnrefused',\n 'enotfound',\n 'etimedout',\n 'socket hang up',\n];\n\n/**\n * Determine if a Claude CLI failure is transient (retryable).\n *\n * A failure is considered transient if:\n * - Exit code is 75 (EX_TEMPFAIL)\n * - Output contains known transient error patterns\n */\nexport function isTransientFailure(exitCode: number, output: string): boolean {\n // Exit code 75 = EX_TEMPFAIL\n if (exitCode === 75) {\n return true;\n }\n\n const lowered = output.toLowerCase();\n return TRANSIENT_PATTERNS.some((pattern) => lowered.includes(pattern));\n}\n\n/**\n * Calculate retry delay using exponential backoff.\n *\n * delay = baseSeconds * 2^(attempt-1), capped at maxSeconds\n *\n * @param attempt - The retry attempt number (1-based)\n * @param baseSeconds - Base delay in seconds (default: 30)\n * @param maxSeconds - Maximum delay in seconds (default: 900)\n */\nexport function retryDelaySeconds(\n attempt: number,\n baseSeconds: number = 30,\n maxSeconds: number = 900,\n): number {\n const delay = baseSeconds * 2 ** (attempt - 1);\n return Math.min(delay, maxSeconds);\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":["join","chalk","chalk","execa","execa","join","elapsed"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/run.ts","../src/core/pipeline.ts","../src/ui/pipeline-renderer.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { execa } from 'execa';\nimport { PIPELINE_PHASES, PipelineManager, type PipelinePhase } from '../core/pipeline.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { isVerbose } from '../core/verbose.js';\nimport { formatDuration, printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\nimport { runPipelineWithRenderer } from '../ui/pipeline-renderer.js';\nimport { runAnalyze } from './analyze.js';\nimport { runExecute } from './execute.js';\nimport { runInit } from './init.js';\nimport { runPlan } from './plan.js';\nimport { runPr } from './pr.js';\nimport { runPrd } from './prd.js';\nimport { runReview } from './review.js';\n\nexport async function runPipeline(issue: string, mode: string, from?: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const tasksPath = join(issueDir, 'tasks.json');\n\n printInfo(`Starting pipeline for issue #${issueNumber} (mode: ${mode})`);\n\n // Phase 1: Init check\n printInfo('Running prerequisite checks...');\n const initCode = await runInit();\n if (initCode !== 0) {\n printError('Prerequisites not met. Fix the issues above and try again.');\n return 1;\n }\n\n // Determine starting phase\n let startPhase: PipelinePhase = 'analyze';\n if (from) {\n if (!PIPELINE_PHASES.includes(from as PipelinePhase)) {\n printError(`Invalid phase: ${from}. Valid phases: ${PIPELINE_PHASES.join(', ')}`);\n return 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);\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);\n if (!mgr.canResume(startPhase)) {\n printError(`Cannot resume from ${startPhase}: prerequisite phases not complete`);\n return 1;\n }\n } catch {\n if (startPhase !== 'analyze') {\n printError(`Cannot resume from ${startPhase}: no pipeline state found`);\n return 1;\n }\n }\n }\n\n const phaseOrder: PipelinePhase[] = ['analyze', 'prd', 'plan', 'execute', 'review', 'pr'];\n const startIdx = phaseOrder.indexOf(startPhase);\n\n // Build phase runner functions that throw on failure\n const makeRunner = (fn: () => Promise<number>, phase: string) => async () => {\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 analyze: makeRunner(() => runAnalyze(issueNumber), 'analyze'),\n prd: makeRunner(() => runPrd(issueNumber), 'prd'),\n plan: makeRunner(() => runPlan(issueNumber), 'plan'),\n execute: makeRunner(() => runExecute(undefined, { issue: issueNumber }), 'execute'),\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);\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\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, { issue: issueNumber });\n if (execCode !== 0) {\n throw new Error('Correction execution failed');\n }\n\n // Re-review\n code = await runReview(issueNumber);\n }\n\n if (code !== 0) {\n throw new Error(`Review failed after ${maxCycles} correction cycles`);\n }\n },\n pr: makeRunner(() => runPr(issueNumber), 'pr'),\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,\n tasksPath,\n });\n\n if (!result.success) {\n printError(`Phase ${result.failedPhase} failed`);\n return 1;\n }\n\n // Close the issue\n printInfo('Closing issue...');\n try {\n await execa('gh', ['issue', 'close', issueNumber], { reject: false });\n } catch {\n printWarning('Failed to close issue automatically');\n }\n\n // Get PR URL for summary\n let prUrl = 'unknown';\n try {\n const proc = await execa('gh', ['pr', 'list', '--head', '', '--json', 'url', '--limit', '1'], {\n reject: false,\n });\n const parsed = JSON.parse(proc.stdout?.toString() ?? '[]');\n if (parsed[0]?.url) {\n prUrl = parsed[0].url;\n }\n } catch {\n /* non-critical */\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 try {\n const plan = await loadTaskPlan(tasksPath);\n storyCount = plan.userStories.length;\n\n // Mark as completed\n plan.issueStatus = 'completed';\n plan.completedAt = isoNow();\n plan.lastAttemptAt = isoNow();\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical */\n }\n\n const totalDuration = formatDuration(result.overallElapsedSeconds);\n\n console.log('');\n printSuccess(`Pipeline complete for issue #${issueNumber}!`);\n console.log(` Branch: ${branchName}`);\n console.log(` Stories: ${storyCount}`);\n console.log(` Duration: ${totalDuration}`);\n console.log(` PR: ${prUrl}`);\n\n return 0;\n}\n","import type { PipelineState, TaskPlan } from '../types.js';\nimport { loadTaskPlan, saveTaskPlan } from './state-manager.js';\n\n/**\n * Ordered pipeline phases. Each phase must complete before the next can start.\n */\nexport const PIPELINE_PHASES = [\n 'init',\n 'analyze',\n 'prd',\n 'plan',\n 'execute',\n 'review',\n 'pr',\n] as const;\n\nexport type PipelinePhase = (typeof PIPELINE_PHASES)[number];\n\n/**\n * Map pipeline phases to their corresponding PipelineState field.\n * 'init' has no persisted state — it's a runtime-only check.\n */\nconst PHASE_TO_FIELD: Record<PipelinePhase, keyof PipelineState | null> = {\n init: null,\n analyze: 'analyzeCompleted',\n prd: 'prdCompleted',\n plan: 'jsonCompleted',\n execute: 'executionCompleted',\n review: 'reviewCompleted',\n pr: 'prCreated',\n};\n\nexport class PipelineManager {\n private tasksJsonPath: string;\n private plan: TaskPlan;\n\n constructor(plan: TaskPlan, tasksJsonPath: string) {\n this.plan = plan;\n this.tasksJsonPath = tasksJsonPath;\n }\n\n /**\n * Reload state from disk.\n */\n async reload(): Promise<void> {\n this.plan = await loadTaskPlan(this.tasksJsonPath);\n }\n\n /**\n * Check if a phase is complete.\n */\n isPhaseComplete(phase: PipelinePhase): boolean {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return true; // init is always \"complete\" after running\n return this.plan.pipeline?.[field] ?? false;\n }\n\n /**\n * Get the first incomplete phase in the pipeline.\n * Returns null if all phases are complete.\n */\n getNextPhase(): PipelinePhase | null {\n for (const phase of PIPELINE_PHASES) {\n if (!this.isPhaseComplete(phase)) {\n return phase;\n }\n }\n return null;\n }\n\n /**\n * Get the current phase (alias for getNextPhase).\n */\n getCurrentPhase(): PipelinePhase | null {\n return this.getNextPhase();\n }\n\n /**\n * Check whether we can resume from a specific phase.\n * All prerequisite phases must be complete.\n */\n canResume(fromPhase: PipelinePhase): boolean {\n const idx = PIPELINE_PHASES.indexOf(fromPhase);\n if (idx < 0) return false;\n\n // All phases before fromPhase must be complete\n for (let i = 0; i < idx; i++) {\n if (!this.isPhaseComplete(PIPELINE_PHASES[i])) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Mark a phase as complete and persist to disk.\n */\n async markPhaseComplete(phase: PipelinePhase): Promise<void> {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return; // init has no persisted state\n\n this.plan = {\n ...this.plan,\n pipeline: {\n ...(this.plan.pipeline ?? {\n analyzeCompleted: false,\n prdCompleted: false,\n jsonCompleted: false,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n }),\n [field]: true,\n },\n };\n\n await saveTaskPlan(this.tasksJsonPath, this.plan);\n }\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 { setOutputCallback, setStoryUpdateCallback } 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 */\nconst PHASE_LABELS: Record<string, string> = {\n analyze: 'Analyze',\n prd: 'PRD',\n plan: 'Plan',\n execute: 'Execute',\n review: 'Review',\n pr: 'PR',\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 * 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 // Build subtask definitions for each story\n const subtaskDefs = stories.map((story) => ({\n title: `${story.id}: ${story.title}`,\n skip: story.passes ? 'already passing' : false,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n task: async () => {\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 // 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 : 5,\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 return task.newListr([engineSubtask, ...subtaskDefs], {\n concurrent: true,\n exitOnError: false,\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 = PHASE_LABELS[phase] ?? phase.charAt(0).toUpperCase() + phase.slice(1);\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 }\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":[]}