issue-flow 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/{analyze-QVQ4CTCU.js → analyze-VVG7UABS.js} +15 -5
  2. package/dist/analyze-VVG7UABS.js.map +1 -0
  3. package/dist/{chunk-3ULIAC2J.js → chunk-2CSHLHYP.js} +2 -2
  4. package/dist/{chunk-PA3GI7LR.js → chunk-4HT4EVYV.js} +78 -32
  5. package/dist/chunk-4HT4EVYV.js.map +1 -0
  6. package/dist/{chunk-DLWRUTRL.js → chunk-7W5VGYXW.js} +11 -11
  7. package/dist/{chunk-JMSRJ43G.js → chunk-EHX4J5G5.js} +15 -8
  8. package/dist/chunk-EHX4J5G5.js.map +1 -0
  9. package/dist/{chunk-KMX3DUJN.js → chunk-FPGHTTVV.js} +15 -8
  10. package/dist/chunk-FPGHTTVV.js.map +1 -0
  11. package/dist/{chunk-OEJSMZMO.js → chunk-JTSAL3TI.js} +12 -5
  12. package/dist/chunk-JTSAL3TI.js.map +1 -0
  13. package/dist/{chunk-LJI45O5B.js → chunk-KWGRBNUG.js} +22 -8
  14. package/dist/chunk-KWGRBNUG.js.map +1 -0
  15. package/dist/chunk-SVOWWZQJ.js +982 -0
  16. package/dist/chunk-SVOWWZQJ.js.map +1 -0
  17. package/dist/{chunk-O5XI2CFF.js → chunk-WZDCTAWZ.js} +55 -20
  18. package/dist/chunk-WZDCTAWZ.js.map +1 -0
  19. package/dist/{chunk-VM7C7L3A.js → chunk-YM3G5BLR.js} +21 -9
  20. package/dist/chunk-YM3G5BLR.js.map +1 -0
  21. package/dist/cli.js +130 -29
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config-B564P45C.js +29 -0
  24. package/dist/execute-PNOFNLZG.js +12 -0
  25. package/dist/generate-IN62ARBS.js +143 -0
  26. package/dist/generate-IN62ARBS.js.map +1 -0
  27. package/dist/init-5242XSYC.js +9 -0
  28. package/dist/plan-6KLONWPR.js +14 -0
  29. package/dist/pr-JLJLMFN3.js +14 -0
  30. package/dist/prd-TVJDYZXH.js +14 -0
  31. package/dist/review-FRV4ZMS2.js +12 -0
  32. package/dist/review-FRV4ZMS2.js.map +1 -0
  33. package/dist/{run-3GJC54DB.js → run-GPBICAD6.js} +45 -26
  34. package/dist/run-GPBICAD6.js.map +1 -0
  35. package/package.json +2 -1
  36. package/prompts/analyze.md +18 -5
  37. package/prompts/generate.md +27 -6
  38. package/prompts/plan.md +19 -2
  39. package/prompts/pr.md +23 -6
  40. package/prompts/prd.md +18 -5
  41. package/prompts/review.md +18 -6
  42. package/dist/analyze-QVQ4CTCU.js.map +0 -1
  43. package/dist/chunk-JMSRJ43G.js.map +0 -1
  44. package/dist/chunk-KLBAHTXX.js +0 -283
  45. package/dist/chunk-KLBAHTXX.js.map +0 -1
  46. package/dist/chunk-KMX3DUJN.js.map +0 -1
  47. package/dist/chunk-LJI45O5B.js.map +0 -1
  48. package/dist/chunk-O5XI2CFF.js.map +0 -1
  49. package/dist/chunk-OEJSMZMO.js.map +0 -1
  50. package/dist/chunk-PA3GI7LR.js.map +0 -1
  51. package/dist/chunk-VM7C7L3A.js.map +0 -1
  52. package/dist/execute-TEJ26L4O.js +0 -12
  53. package/dist/generate-KBQ4KW4T.js +0 -48
  54. package/dist/generate-KBQ4KW4T.js.map +0 -1
  55. package/dist/init-4VMPBRMA.js +0 -9
  56. package/dist/plan-PWYZP5AN.js +0 -13
  57. package/dist/pr-CJQWZUDR.js +0 -13
  58. package/dist/prd-4KLQY7BC.js +0 -13
  59. package/dist/review-YFCWBBKY.js +0 -11
  60. package/dist/run-3GJC54DB.js.map +0 -1
  61. /package/dist/{chunk-3ULIAC2J.js.map → chunk-2CSHLHYP.js.map} +0 -0
  62. /package/dist/{chunk-DLWRUTRL.js.map → chunk-7W5VGYXW.js.map} +0 -0
  63. /package/dist/{execute-TEJ26L4O.js.map → config-B564P45C.js.map} +0 -0
  64. /package/dist/{init-4VMPBRMA.js.map → execute-PNOFNLZG.js.map} +0 -0
  65. /package/dist/{plan-PWYZP5AN.js.map → init-5242XSYC.js.map} +0 -0
  66. /package/dist/{pr-CJQWZUDR.js.map → plan-6KLONWPR.js.map} +0 -0
  67. /package/dist/{prd-4KLQY7BC.js.map → pr-JLJLMFN3.js.map} +0 -0
  68. /package/dist/{review-YFCWBBKY.js.map → prd-TVJDYZXH.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/headless.ts","../src/issues/registry.ts","../src/issues/hash.ts","../src/issues/providers/github.ts","../src/issues/providers/local.ts","../src/issues/bootstrap.ts","../src/issues/resolver.ts","../src/issues/context.ts"],"sourcesContent":["import { createInterface } from 'node:readline';\nimport chalk from 'chalk';\nimport { execa } from 'execa';\nimport { createSpinner, ElapsedTimer, formatDuration, getIcons, useColor } from '../ui/logger.js';\nimport { getSessionPublisher } from './session-publisher.js';\nimport { isoNow } from './state-manager.js';\nimport { getOutputCallback, isVerbose } from './verbose.js';\n\nexport interface HeadlessOptions {\n prompt: string;\n maxTurns?: number;\n timeout?: number;\n outputFormat?: 'json' | 'text' | 'stream-json';\n allowedTools?: string[];\n /** Status message displayed as spinner (non-verbose) or header (verbose). */\n statusMessage?: string;\n /** Optional callback for routing verbose output (e.g., through listr2 task.output). When provided, verbose stream events are sent here instead of directly to stderr. */\n onOutput?: (line: string) => void;\n}\n\nexport interface HeadlessCost {\n inputTokens?: number;\n outputTokens?: number;\n}\n\nexport interface HeadlessResult {\n success: boolean;\n result: string;\n cost: HeadlessCost | null;\n error: string | null;\n}\n\n/* ── verbose stream formatting ──────────────────────────────────────────── */\n\n/**\n * Extract a short context string from a tool_use input object.\n */\nfunction getToolContext(name: string, input: Record<string, unknown>): string {\n switch (name) {\n case 'Read':\n return shortPath(input.file_path as string);\n case 'Write':\n case 'Edit':\n return shortPath(input.file_path as string);\n case 'Glob':\n return (input.pattern as string) ?? '';\n case 'Grep':\n return (input.pattern as string) ?? '';\n case 'Bash': {\n const cmd = (input.command as string) ?? '';\n return cmd.length > 60 ? `${cmd.substring(0, 57)}...` : cmd;\n }\n default:\n return '';\n }\n}\n\n/**\n * Shorten a file path to be relative-friendly.\n */\nfunction shortPath(filePath: string | undefined): string {\n if (!filePath) return '';\n const cwd = process.cwd();\n if (filePath.startsWith(cwd)) {\n return filePath.substring(cwd.length + 1);\n }\n // Show last 2 segments for absolute paths\n const parts = filePath.split('/');\n if (parts.length > 2) {\n return `.../${parts.slice(-2).join('/')}`;\n }\n return filePath;\n}\n\n/**\n * Print a formatted stream event line to stderr.\n */\nfunction printStreamEvent(\n line: string,\n state: { turnCount: number },\n onOutput?: (line: string) => void,\n): void {\n let event: {\n type?: string;\n subtype?: string;\n message?: {\n content?: { type: string; text?: string; name?: string; input?: Record<string, unknown> }[];\n };\n };\n\n try {\n event = JSON.parse(line);\n } catch {\n return;\n }\n\n const icons = getIcons();\n const colored = useColor();\n const emit = onOutput ?? ((msg: string) => process.stderr.write(`${msg}\\n`));\n\n if (event.type === 'assistant' && event.message?.content) {\n state.turnCount++;\n\n for (const block of event.message.content) {\n if (block.type === 'text' && block.text) {\n // Wrap text with connector prefix\n const lines = block.text.split('\\n');\n for (const textLine of lines) {\n if (!textLine.trim()) continue;\n const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;\n const text = colored ? chalk.dim(textLine) : textLine;\n emit(`${prefix}${text}`);\n }\n }\n\n if (block.type === 'tool_use' && block.name) {\n const context = block.input ? getToolContext(block.name, block.input) : '';\n getSessionPublisher().publish({\n type: 'activity',\n at: isoNow(),\n tool: block.name,\n detail: context || undefined,\n });\n const toolName = block.name.padEnd(12);\n\n const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;\n const toolIcon = colored ? chalk.cyan(icons.tool) : icons.tool;\n const toolLabel = colored ? chalk.cyan(toolName) : toolName;\n const contextText = context ? (colored ? chalk.dim(context) : context) : '';\n\n emit(`${prefix}${toolIcon} ${toolLabel} ${contextText}`);\n }\n }\n }\n}\n\n/* ── verbose execution ──────────────────────────────────────────────────── */\n\n/**\n * Run headless in verbose mode using stream-json to display real-time progress.\n */\nasync function runHeadlessVerbose(\n prompt: string,\n maxTurns: number,\n timeout: number,\n allowedTools?: string[],\n statusMessage?: string,\n onOutput?: (line: string) => void,\n): Promise<HeadlessResult> {\n const icons = getIcons();\n const colored = useColor();\n const emit = onOutput ?? ((msg: string) => process.stderr.write(`${msg}\\n`));\n\n // Print header\n if (statusMessage) {\n const msg = colored\n ? chalk.blue(`${icons.start} ${statusMessage}`)\n : `${icons.start} ${statusMessage}`;\n emit(msg);\n }\n\n // Print opening connector\n const connectorLine = colored ? chalk.dim(` ${icons.connector}`) : ` ${icons.connector}`;\n emit(connectorLine);\n\n const startTime = Date.now();\n\n const args: string[] = [\n '-p',\n prompt,\n '--output-format',\n 'stream-json',\n '--verbose',\n '--max-turns',\n String(maxTurns),\n ];\n\n if (allowedTools && allowedTools.length > 0) {\n for (const tool of allowedTools) {\n args.push('--allowedTools', tool);\n }\n }\n\n const subprocess = execa('claude', args, {\n stdin: 'ignore',\n reject: false,\n timeout,\n stripFinalNewline: false,\n });\n\n let resultText = '';\n let isError = false;\n let costData: HeadlessCost | null = null;\n const state = { turnCount: 0 };\n\n if (subprocess.stdout) {\n const rl = createInterface({ input: subprocess.stdout });\n for await (const line of rl) {\n printStreamEvent(line, state, onOutput);\n\n try {\n const event = JSON.parse(line);\n if (event.type === 'result') {\n resultText = event.result ?? '';\n isError = event.is_error === true;\n if (event.total_cost_usd != null && event.usage) {\n costData = {\n inputTokens: event.usage.input_tokens,\n outputTokens: event.usage.output_tokens,\n };\n }\n }\n } catch {\n // ignore malformed lines\n }\n }\n }\n\n // Wait for the process to finish\n const proc = await subprocess;\n\n // Close connector with elapsed time\n const elapsedSec = Math.floor((Date.now() - startTime) / 1000);\n const durationStr = formatDuration(elapsedSec);\n const doneLine = colored\n ? chalk.dim(` ${icons.connector} ${chalk.italic(`Done in ${durationStr}`)}`)\n : ` ${icons.connector} Done in ${durationStr}`;\n emit(doneLine);\n emit(connectorLine);\n\n if (proc.exitCode !== 0 && !resultText) {\n const stderr = proc.stderr?.toString() ?? '';\n const isTimeout =\n proc.exitCode === 143 || ('signalName' in proc && proc.signalName === 'SIGTERM');\n const errorMsg = isTimeout\n ? `Headless invocation timed out after ${timeout}ms`\n : stderr || `claude exited with code ${proc.exitCode}`;\n return {\n success: false,\n result: '',\n cost: null,\n error: errorMsg,\n };\n }\n\n return {\n success: !isError,\n result: resultText,\n cost: costData,\n error: isError ? resultText : null,\n };\n}\n\n/* ── standard execution ─────────────────────────────────────────────────── */\n\n/**\n * Invoke Claude Code in headless mode via `claude -p`.\n *\n * Each invocation is an isolated session — no context is shared between calls.\n * Output is parsed as JSON when outputFormat is 'json' (default).\n *\n * When verbose mode is active, uses stream-json to display real-time progress.\n * Otherwise, shows a spinner while waiting.\n */\nexport async function runHeadless(options: HeadlessOptions): Promise<HeadlessResult> {\n const {\n prompt,\n maxTurns = 10,\n timeout = 300_000,\n outputFormat = 'json',\n allowedTools,\n statusMessage,\n onOutput,\n } = options;\n\n if (isVerbose()) {\n // Use explicit onOutput, fall back to global output callback, or default to stderr\n const effectiveOnOutput = onOutput ?? getOutputCallback();\n return runHeadlessVerbose(\n prompt,\n maxTurns,\n timeout,\n allowedTools,\n statusMessage,\n effectiveOnOutput,\n );\n }\n\n // Non-verbose: use spinner with elapsed timer\n const spinner = statusMessage ? createSpinner(statusMessage).start() : null;\n\n let timer: ElapsedTimer | null = null;\n if (spinner) {\n timer = new ElapsedTimer((elapsed) => {\n spinner.suffixText = useColor() ? chalk.dim(`(${elapsed})`) : `(${elapsed})`;\n }).start();\n }\n\n const args: string[] = [\n '-p',\n prompt,\n '--output-format',\n outputFormat,\n '--max-turns',\n String(maxTurns),\n ];\n\n if (allowedTools && allowedTools.length > 0) {\n for (const tool of allowedTools) {\n args.push('--allowedTools', tool);\n }\n }\n\n try {\n const proc = await execa('claude', args, {\n stdin: 'ignore',\n reject: false,\n timeout,\n stripFinalNewline: false,\n });\n\n const stdout = proc.stdout?.toString() ?? '';\n const stderr = proc.stderr?.toString() ?? '';\n\n if (proc.exitCode !== 0) {\n const elapsed = timer?.stop() ?? 0;\n const dur = useColor()\n ? chalk.dim(` (${formatDuration(elapsed)})`)\n : ` (${formatDuration(elapsed)})`;\n spinner?.fail(`${statusMessage}${dur}`);\n return {\n success: false,\n result: '',\n cost: null,\n error: stderr || stdout || `claude exited with code ${proc.exitCode}`,\n };\n }\n\n const elapsed = timer?.stop() ?? 0;\n const dur = useColor()\n ? chalk.dim(` (${formatDuration(elapsed)})`)\n : ` (${formatDuration(elapsed)})`;\n spinner?.succeed(`${statusMessage}${dur}`);\n\n if (outputFormat === 'json') {\n try {\n const parsed = JSON.parse(stdout);\n return {\n success: true,\n result: parsed.result ?? stdout,\n cost:\n parsed.cost_usd != null\n ? { inputTokens: parsed.num_input_tokens, outputTokens: parsed.num_output_tokens }\n : null,\n error: null,\n };\n } catch {\n return {\n success: true,\n result: stdout,\n cost: null,\n error: null,\n };\n }\n }\n\n return {\n success: true,\n result: stdout,\n cost: null,\n error: null,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const catchElapsed = timer?.stop() ?? 0;\n const catchDur = useColor()\n ? chalk.dim(` (${formatDuration(catchElapsed)})`)\n : ` (${formatDuration(catchElapsed)})`;\n spinner?.fail(`${statusMessage}${catchDur}`);\n\n if (message.includes('timed out') || message.includes('ETIMEDOUT')) {\n return {\n success: false,\n result: '',\n cost: null,\n error: `Headless invocation timed out after ${timeout}ms`,\n };\n }\n\n return {\n success: false,\n result: '',\n cost: null,\n error: message,\n };\n }\n}\n","import type { IssueProvider } from './provider.js';\nimport type { IssueSource } from './types.js';\n\n/**\n * Registry of Issue providers.\n *\n * Adding an origin is creating a provider file and registering one line here\n * (or from the caller's side), so nothing in src/commands/ needs to know the\n * list of sources.\n */\nconst providers = new Map<IssueSource, IssueProvider>();\n\n/**\n * Register a provider under its own `name`.\n *\n * Registering the same source twice replaces the previous entry, which is what\n * lets tests substitute a real provider with a fake one.\n */\nexport function registerProvider(provider: IssueProvider): void {\n providers.set(provider.name, provider);\n}\n\n/** Sources currently registered, in registration order. */\nexport function getRegisteredSources(): IssueSource[] {\n return [...providers.keys()];\n}\n\n/**\n * Look up a provider by origin.\n *\n * @throws when the origin is not registered, listing what is available so the\n * caller knows which sources they can pick instead.\n */\nexport function getProvider(source: IssueSource): IssueProvider {\n const provider = providers.get(source);\n if (provider === undefined) {\n const available = getRegisteredSources();\n const hint =\n available.length > 0\n ? `Available sources: ${available.join(', ')}.`\n : 'No Issue providers are registered.';\n throw new Error(`Unknown Issue source: '${source}'. ${hint}`);\n }\n return provider;\n}\n\n/** Drop every registration. Intended for test isolation. */\nexport function clearProviders(): void {\n providers.clear();\n}\n","import { createHash } from 'node:crypto';\n\n/**\n * Content hashing shared by every Issue provider.\n *\n * The hash is what lets the resolver tell \"the local and the remote Issue are\n * the same demand\" from \"they diverged\", so it has to be stable across\n * platforms: Windows checkouts store CRLF, editors add or drop trailing\n * newlines, and neither should count as a content change.\n */\n\n/** Normalizes line endings (CRLF/CR to LF) and trims the edges. */\nfunction normalizeText(value: string): string {\n return value.replace(/\\r\\n?/g, '\\n').trim();\n}\n\n/**\n * Returns `sha256:<hex>` for the given title and body.\n *\n * The payload is a canonical JSON object rather than a concatenation, so text\n * moved from the title into the body always changes the hash.\n */\nexport function hashIssueContent(title: string, body: string): string {\n const payload = JSON.stringify({\n title: normalizeText(title),\n body: normalizeText(body),\n });\n const digest = createHash('sha256').update(payload, 'utf8').digest('hex');\n return `sha256:${digest}`;\n}\n","import { run } from '../../utils/shell.js';\nimport { hashIssueContent } from '../hash.js';\nimport type { IssueProvider } from '../provider.js';\nimport type { Issue, IssueDraft, IssueState } from '../types.js';\n\n/** Fields requested from `gh issue view`, in the order the PRD specifies. */\nconst VIEW_FIELDS = 'number,title,body,labels,state,url,createdAt,updatedAt';\n\n/** Timeout for the availability probes, matching the `init` prerequisite checks. */\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * gh reports a missing Issue on stderr with one of these phrasings. The match\n * must stay issue-specific: a bare /not found/ would also swallow\n * `gh: command not found`, turning a broken environment into \"issue absent\".\n * Anything else on a non-zero exit is a real failure (network, auth, gh not\n * installed) and must surface as a thrown error.\n */\nconst NOT_FOUND_PATTERN =\n /could not resolve to an issue|no issues? found|issue not found|could not find any issue/i;\n\ninterface GhLabel {\n name?: unknown;\n}\n\ninterface GhIssuePayload {\n number?: unknown;\n title?: unknown;\n body?: unknown;\n labels?: unknown;\n state?: unknown;\n url?: unknown;\n createdAt?: unknown;\n updatedAt?: unknown;\n}\n\nfunction normalizeState(state: unknown): IssueState {\n return String(state ?? '').toUpperCase() === 'CLOSED' ? 'closed' : 'open';\n}\n\nfunction normalizeLabels(labels: unknown): string[] {\n if (!Array.isArray(labels)) return [];\n return labels\n .map((label: GhLabel | string) => (typeof label === 'string' ? label : label?.name))\n .filter((name): name is string => typeof name === 'string' && name.length > 0);\n}\n\nfunction toIssue(payload: GhIssuePayload): Issue {\n const title = typeof payload.title === 'string' ? payload.title : '';\n const body = typeof payload.body === 'string' ? payload.body : '';\n const number = typeof payload.number === 'number' ? payload.number : null;\n const url = typeof payload.url === 'string' && payload.url.length > 0 ? payload.url : null;\n\n return {\n id: number === null ? '' : String(number),\n number,\n title,\n body,\n labels: normalizeLabels(payload.labels),\n state: normalizeState(payload.state),\n source: 'github',\n remoteRef: url,\n createdAt: typeof payload.createdAt === 'string' ? payload.createdAt : '',\n updatedAt: typeof payload.updatedAt === 'string' ? payload.updatedAt : '',\n contentHash: hashIssueContent(title, body),\n raw: payload,\n };\n}\n\n/** Strip a leading `#` so both `23` and `#23` are accepted. */\nfunction normalizeId(id: string): string {\n return id.trim().replace(/^#/, '');\n}\n\nfunction extractIssueNumber(output: string): string | null {\n return output.match(/\\/issues\\/(\\d+)/)?.[1] ?? null;\n}\n\n/**\n * Issue provider backed by the GitHub CLI.\n *\n * Every call goes through `run` (execa, no shell) so tests can mock the shell\n * layer instead of the network, and so the Issue content is identical across\n * every pipeline phase.\n */\nexport class GitHubIssueProvider implements IssueProvider {\n readonly name = 'github' as const;\n\n /** True when gh is installed and authenticated. Never throws. */\n async isAvailable(): Promise<boolean> {\n try {\n const version = await run('gh', ['--version'], { timeout: PROBE_TIMEOUT_MS });\n if (version.exitCode !== 0) return false;\n\n const auth = await run('gh', ['auth', 'status'], { timeout: PROBE_TIMEOUT_MS });\n return auth.exitCode === 0;\n } catch {\n return false;\n }\n }\n\n async get(id: string): Promise<Issue | null> {\n const issueId = normalizeId(id);\n const result = await run('gh', ['issue', 'view', issueId, '--json', VIEW_FIELDS]);\n\n if (result.exitCode !== 0) {\n const detail = (result.stderr || result.stdout).trim();\n if (NOT_FOUND_PATTERN.test(detail)) return null;\n throw new Error(\n `Failed to fetch GitHub issue #${issueId}: ${detail || 'gh issue view failed'}`,\n );\n }\n\n let payload: GhIssuePayload;\n try {\n payload = JSON.parse(result.stdout);\n } catch {\n throw new Error(\n `Failed to parse gh output for issue #${issueId}: ${result.stdout.slice(0, 200)}`,\n );\n }\n\n return toIssue(payload);\n }\n\n async create(draft: IssueDraft): Promise<Issue> {\n const args = ['issue', 'create', '--title', draft.title, '--body', draft.body];\n for (const label of draft.labels) {\n args.push('--label', label);\n }\n\n const result = await run('gh', args);\n if (result.exitCode !== 0) {\n const detail = (result.stderr || result.stdout).trim();\n throw new Error(`Failed to create GitHub issue: ${detail || 'gh issue create failed'}`);\n }\n\n const number = extractIssueNumber(result.stdout);\n if (!number) {\n throw new Error(\n `GitHub issue creation did not report an issue URL: ${result.stdout.trim() || '(no output)'}`,\n );\n }\n\n const created = await this.get(number);\n if (!created) {\n throw new Error(`GitHub issue #${number} was created but could not be read back`);\n }\n return created;\n }\n\n async close(id: string): Promise<void> {\n const issueId = normalizeId(id);\n const result = await run('gh', ['issue', 'close', issueId]);\n\n if (result.exitCode !== 0) {\n const detail = (result.stderr || result.stdout).trim();\n throw new Error(\n `Failed to close GitHub issue #${issueId}: ${detail || 'gh issue close failed'}`,\n );\n }\n }\n}\n\n/** Shared instance; providers are stateless, so a singleton is enough. */\nexport const githubIssueProvider = new GitHubIssueProvider();\n","import { constants } from 'node:fs';\nimport { access, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { ZodError } from 'zod';\nimport { isoNow } from '../../core/state-manager.js';\nimport { issueMetadataSchema } from '../../schemas.js';\nimport { writeFileAtomic } from '../../utils/fs.js';\nimport { getProjectRoot } from '../../utils/git.js';\nimport { run } from '../../utils/shell.js';\nimport { hashIssueContent } from '../hash.js';\nimport type { IssueProvider } from '../provider.js';\nimport type { Issue, IssueDraft, IssueMetadata } from '../types.js';\n\n/** Directory (relative to the project root) holding one folder per Issue. */\nconst ISSUES_DIR = 'issues';\n\n/** Timeout for the optional remote probes done while allocating an identifier. */\nconst REMOTE_PROBE_TIMEOUT_MS = 10_000;\n\nfunction isNotFound(err: unknown): boolean {\n const code = (err as NodeJS.ErrnoException).code;\n return code === 'ENOENT' || code === 'ENOTDIR';\n}\n\n/**\n * Identifiers become path segments, so anything that could escape the issues\n * directory is rejected before it reaches `join`.\n */\nfunction normalizeId(id: string): string {\n const normalized = id.trim().replace(/^#/, '');\n if (normalized.length === 0) {\n throw new Error('Local issue identifier cannot be empty');\n }\n if (/[/\\\\]/.test(normalized) || normalized === '.' || normalized === '..') {\n throw new Error(`Invalid local issue identifier: '${id}'`);\n }\n return normalized;\n}\n\nfunction toNumber(id: string): number | null {\n return /^\\d+$/.test(id) ? Number.parseInt(id, 10) : null;\n}\n\n/**\n * Title lives in the leading H1, body is everything after it.\n *\n * Only the first non-empty line is considered: an H1 further down belongs to\n * the body (issue descriptions routinely use headings), so promoting it would\n * silently retitle the Issue.\n */\nexport function parseIssueMarkdown(content: string): { title: string; body: string } {\n const lines = content.replace(/\\r\\n?/g, '\\n').split('\\n');\n const headingIndex = lines.findIndex((line) => line.trim().length > 0);\n const heading = headingIndex === -1 ? undefined : lines[headingIndex].match(/^#[ \\t]+(.*)$/);\n\n if (!heading) {\n return { title: '', body: lines.join('\\n').trim() };\n }\n\n return {\n title: heading[1].trim(),\n body: lines\n .slice(headingIndex + 1)\n .join('\\n')\n .trim(),\n };\n}\n\nfunction renderIssueMarkdown(title: string, body: string): string {\n return `# ${title}\\n\\n${body.trim()}\\n`;\n}\n\n/**\n * Issue provider backed by plain files under `issues/<id>/`.\n *\n * Enables the whole pipeline without any network access: no gh, no remote, no\n * authentication. `issue.md` is the source of truth for the content and\n * `metadata.json` for everything else; the content hash is always recomputed\n * from the file so a hand-edited issue.md is never reported as unchanged.\n */\nexport class LocalFileIssueProvider implements IssueProvider {\n readonly name = 'local' as const;\n\n private readonly configuredRoot?: string;\n private cachedRoot?: string;\n\n constructor(projectRoot?: string) {\n this.configuredRoot = projectRoot;\n }\n\n /** True whenever the project directory exists and is writable. Never throws. */\n async isAvailable(): Promise<boolean> {\n try {\n await access(await this.root(), constants.W_OK);\n return true;\n } catch {\n return false;\n }\n }\n\n async get(id: string): Promise<Issue | null> {\n const issueId = normalizeId(id);\n const markdownPath = await this.issueFile(issueId);\n\n let raw: string;\n try {\n raw = await readFile(markdownPath, 'utf-8');\n } catch (err: unknown) {\n if (isNotFound(err)) return null;\n throw err;\n }\n\n const { title: headingTitle, body } = parseIssueMarkdown(raw);\n const metadata = await this.readMetadata(issueId);\n const title = headingTitle || metadata?.title || '';\n\n if (metadata) {\n return {\n id: metadata.id,\n number: metadata.number,\n title,\n body,\n labels: metadata.labels,\n state: metadata.state,\n source: 'local',\n remoteRef: metadata.remote?.ref ?? null,\n createdAt: metadata.createdAt,\n updatedAt: metadata.updatedAt,\n contentHash: hashIssueContent(title, body),\n raw: metadata,\n };\n }\n\n // metadata.json is optional: an issue.md dropped in by hand still works,\n // with timestamps taken from the file itself.\n const stats = await stat(markdownPath);\n return {\n id: issueId,\n number: toNumber(issueId),\n title,\n body,\n labels: [],\n state: 'open',\n source: 'local',\n remoteRef: null,\n createdAt: stats.birthtime.toISOString(),\n updatedAt: stats.mtime.toISOString(),\n contentHash: hashIssueContent(title, body),\n };\n }\n\n async create(draft: IssueDraft): Promise<Issue> {\n // An explicit id is how a mirror keeps the identifier of the Issue it\n // mirrors: allocating a fresh one would make `issue-flow run <n>` see two\n // unrelated Issues instead of one demand in two places.\n const id = draft.id === undefined ? String(await this.allocateNumber()) : normalizeId(draft.id);\n const dir = await this.issueDir(id);\n\n await mkdir(dir, { recursive: true });\n\n // Exclusive create (the 'wx' flag, O_EXCL under the hood) instead of a\n // check-then-write: two concurrent `create()` calls racing the same id\n // would both pass a plain existence check before either writes, and the\n // second would then silently overwrite the first through the atomic\n // temp+rename writer. The OS now rejects the loser instead.\n try {\n await writeFile(join(dir, 'issue.md'), renderIssueMarkdown(draft.title, draft.body), {\n encoding: 'utf-8',\n flag: 'wx',\n });\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(\n `Local issue '${id}' already exists at ${join(ISSUES_DIR, id, 'issue.md')}. ` +\n 'Remove it or pick another identifier before creating a new Issue.',\n );\n }\n throw err;\n }\n\n const timestamp = isoNow();\n const metadata: IssueMetadata = {\n schemaVersion: 1,\n id,\n number: toNumber(id),\n source: 'local',\n title: draft.title,\n labels: draft.labels,\n state: 'open',\n createdAt: timestamp,\n updatedAt: timestamp,\n contentHash: hashIssueContent(draft.title, draft.body),\n ...(draft.remote ? { remote: draft.remote } : {}),\n };\n\n await this.writeMetadata(id, metadata);\n\n return {\n id,\n number: metadata.number,\n title: draft.title,\n body: draft.body.trim(),\n labels: draft.labels,\n state: 'open',\n source: 'local',\n remoteRef: draft.remote?.ref ?? null,\n createdAt: timestamp,\n updatedAt: timestamp,\n contentHash: metadata.contentHash,\n raw: metadata,\n };\n }\n\n async close(id: string): Promise<void> {\n const issue = await this.get(id);\n if (!issue) {\n throw new Error(`Local issue '${normalizeId(id)}' not found under ${ISSUES_DIR}/`);\n }\n\n const issueId = normalizeId(id);\n const existing = await this.readMetadata(issueId);\n\n await this.writeMetadata(issueId, {\n schemaVersion: 1,\n id: issue.id,\n number: issue.number,\n source: existing?.source ?? 'local',\n title: issue.title,\n labels: issue.labels,\n state: 'closed',\n createdAt: issue.createdAt,\n updatedAt: isoNow(),\n contentHash: issue.contentHash,\n ...(existing?.remote ? { remote: existing.remote } : {}),\n });\n }\n\n /**\n * Next free identifier: above every local number and, when GitHub answers,\n * above every remote one too. Sharing a numbering space with the remote is\n * what makes a naive `localMax + 1` collide the moment someone opens an Issue\n * on GitHub.\n */\n async allocateNumber(): Promise<number> {\n const [local, remote] = await Promise.all([this.highestLocalNumber(), highestRemoteNumber()]);\n return Math.max(local, remote) + 1;\n }\n\n private async root(): Promise<string> {\n if (this.configuredRoot !== undefined) return this.configuredRoot;\n this.cachedRoot ??= await getProjectRoot();\n return this.cachedRoot;\n }\n\n private async issueDir(id: string): Promise<string> {\n return join(await this.root(), ISSUES_DIR, id);\n }\n\n private async issueFile(id: string): Promise<string> {\n return join(await this.issueDir(id), 'issue.md');\n }\n\n private async metadataFile(id: string): Promise<string> {\n return join(await this.issueDir(id), 'metadata.json');\n }\n\n /** Parsed metadata.json, or `null` when the file is absent. */\n private async readMetadata(id: string): Promise<IssueMetadata | null> {\n const path = await this.metadataFile(id);\n const relative = join(ISSUES_DIR, id, 'metadata.json');\n\n let raw: string;\n try {\n raw = await readFile(path, 'utf-8');\n } catch (err: unknown) {\n if (isNotFound(err)) return null;\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err: unknown) {\n throw new Error(`Invalid JSON in ${relative}: ${(err as Error).message}`);\n }\n\n try {\n return issueMetadataSchema.parse(parsed);\n } catch (err: unknown) {\n if (err instanceof ZodError) {\n const details = err.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n throw new Error(`Invalid issue metadata at ${relative}:\\n${details}`);\n }\n throw err;\n }\n }\n\n private async writeMetadata(id: string, metadata: IssueMetadata): Promise<void> {\n await writeFileAtomic(await this.metadataFile(id), `${JSON.stringify(metadata, null, 2)}\\n`);\n }\n\n /** Highest number across every issues/<id>/metadata.json and numeric directory name. */\n private async highestLocalNumber(): Promise<number> {\n const dir = join(await this.root(), ISSUES_DIR);\n\n let entries: string[];\n try {\n entries = (await readdir(dir, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n } catch (err: unknown) {\n if (isNotFound(err)) return 0;\n throw err;\n }\n\n const numbers = await Promise.all(\n entries.map(async (name) => {\n const fromName = toNumber(name) ?? 0;\n // A directory whose metadata is unreadable still occupies its number.\n const fromMetadata = await this.readMetadata(name)\n .then((metadata) => metadata?.number ?? 0)\n .catch(() => 0);\n return Math.max(fromName, fromMetadata);\n }),\n );\n\n return numbers.reduce((max, value) => Math.max(max, value), 0);\n }\n}\n\n/** How many of the most-recently-created issues/PRs to inspect per probe. */\nconst REMOTE_PROBE_SAMPLE_SIZE = 20;\n\n/**\n * Highest number already taken on GitHub, or 0 when the remote is unreachable.\n *\n * Issues and pull requests share one counter, so both are probed: allocating\n * above the newest Issue alone would still collide with an open PR. Every\n * failure degrades to 0 — being offline must not block local creation.\n *\n * `gh ... list` defaults to sorting by creation date descending, which lines\n * up with number order in the overwhelming majority of repos — but that sort\n * is an implementation detail, not a contract. Rather than trust that the\n * single most-recent entry is also the highest-numbered one, a small sample\n * is fetched and the max is taken across it, which also tolerates the rare\n * case of a transferred issue whose creation date and number disagree.\n */\nasync function highestRemoteNumber(): Promise<number> {\n const probes = [\n ['issue', 'list'],\n ['pr', 'list'],\n ];\n\n const results = await Promise.all(\n probes.map(async (args) => {\n try {\n const result = await run(\n 'gh',\n [\n ...args,\n '--state',\n 'all',\n '--limit',\n String(REMOTE_PROBE_SAMPLE_SIZE),\n '--json',\n 'number',\n ],\n { timeout: REMOTE_PROBE_TIMEOUT_MS },\n );\n if (result.exitCode !== 0) return 0;\n\n const parsed = JSON.parse(result.stdout);\n if (!Array.isArray(parsed)) return 0;\n return parsed.reduce((max: number, entry: unknown) => {\n const n = (entry as { number?: unknown } | null)?.number;\n return typeof n === 'number' ? Math.max(max, n) : max;\n }, 0);\n } catch {\n return 0;\n }\n }),\n );\n\n return results.reduce((max, value) => Math.max(max, value), 0);\n}\n\n/** Shared instance resolving the project root lazily via git. */\nexport const localFileIssueProvider = new LocalFileIssueProvider();\n","import { githubIssueProvider } from './providers/github.js';\nimport { localFileIssueProvider } from './providers/local.js';\nimport { getRegisteredSources, registerProvider } from './registry.js';\n\n/**\n * Register the providers that ship with issue-flow.\n *\n * Idempotent and non-destructive: an origin that is already registered is left\n * untouched, so a test (or a future plugin) can install its own provider before\n * the first `resolveIssue` call without having it replaced by the built-in one.\n */\nexport function ensureProvidersRegistered(): void {\n const registered = new Set(getRegisteredSources());\n\n if (!registered.has('github')) {\n registerProvider(githubIssueProvider);\n }\n if (!registered.has('local')) {\n registerProvider(localFileIssueProvider);\n }\n}\n","import { createInterface, type Interface as ReadlineInterface } from 'node:readline';\nimport { loadIssuesConfig } from '../config.js';\nimport { printInfo, printWarning } from '../ui/logger.js';\nimport { ensureProvidersRegistered } from './bootstrap.js';\nimport { getProvider, getRegisteredSources } from './registry.js';\nimport type { Issue, IssueSource, IssuesConfig, ResolvedIssue } from './types.js';\n\n/**\n * Failure to settle on an Issue. Carries the exit code the CLI should return,\n * so callers propagate it instead of inventing their own.\n */\nexport class IssueResolutionError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = 'IssueResolutionError';\n this.exitCode = exitCode;\n }\n}\n\nexport interface ResolveIssueOptions {\n /** Already-resolved provider configuration. Defaults to loadIssuesConfig(). */\n config?: IssuesConfig;\n /** Origins to query. Defaults to every registered source. */\n sources?: IssueSource[];\n /**\n * Whether a prompt may be shown. Defaults to a real TTY outside CI, which is\n * what keeps `ask` from ever blocking a headless run.\n */\n interactive?: boolean;\n /** Input stream for the conflict prompt. Defaults to process.stdin. */\n stdin?: NodeJS.ReadableStream;\n /** Output stream for the conflict prompt. Defaults to process.stdout. */\n stdout?: NodeJS.WritableStream;\n /** Informational sink. Defaults to printInfo. */\n info?: (message: string) => void;\n /** Warning sink. Defaults to printWarning. */\n warn?: (message: string) => void;\n}\n\n/** How many invalid answers are tolerated before the prompt gives up. */\nconst MAX_PROMPT_ATTEMPTS = 3;\n\n/**\n * Labels shown to users for the built-in origins. An origin registered from\n * outside this package is displayed under its own name, so a new provider needs\n * no entry here.\n */\nconst SOURCE_LABELS: Record<string, string> = {\n github: 'GitHub',\n local: 'local',\n};\n\nfunction sourceLabel(source: IssueSource): string {\n return SOURCE_LABELS[source] ?? source;\n}\n\n/** Same label, capitalized for the numbered prompt ('local' -> 'Local'). */\nfunction promptLabel(source: IssueSource): string {\n const label = sourceLabel(source);\n return label.charAt(0).toUpperCase() + label.slice(1);\n}\n\ninterface Candidate {\n source: IssueSource;\n issue: Issue | null;\n /** Why the origin produced nothing, used only to explain a total miss. */\n reason: string | null;\n}\n\n/** A candidate that actually produced an Issue. */\ninterface FoundCandidate extends Candidate {\n issue: Issue;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Read one origin without letting it break the others.\n *\n * An unavailable provider or a failed read degrades to \"no candidate\" plus a\n * reason: as long as another origin has the Issue the pipeline keeps going, and\n * when nothing is found the reasons are surfaced in the final error so a broken\n * environment is never reported as a missing Issue.\n */\nasync function fetchCandidate(\n source: IssueSource,\n id: string,\n warn: (message: string) => void,\n): Promise<Candidate> {\n let provider: ReturnType<typeof getProvider>;\n try {\n provider = getProvider(source);\n } catch (err) {\n return { source, issue: null, reason: errorMessage(err) };\n }\n\n let available: boolean;\n try {\n available = await provider.isAvailable();\n } catch (err) {\n return { source, issue: null, reason: `provider unavailable (${errorMessage(err)})` };\n }\n if (!available) {\n return { source, issue: null, reason: 'provider unavailable' };\n }\n\n try {\n const issue = await provider.get(id);\n return { source, issue, reason: issue === null ? 'not found' : null };\n } catch (err) {\n const reason = errorMessage(err);\n // A real failure (network, auth, corrupted metadata) must be visible even\n // when the other origin answers.\n warn(`Could not read issue '${id}' from ${sourceLabel(source)}: ${reason}`);\n return { source, issue: null, reason };\n }\n}\n\nfunction shortHash(hash: string): string {\n const hex = hash.startsWith('sha256:') ? hash.slice('sha256:'.length) : hash;\n return hex.slice(0, 12);\n}\n\n/** One line per origin describing what differs, without dumping every body. */\nfunction describeCandidate(source: IssueSource, issue: Issue): string {\n const lines = issue.body.length === 0 ? 0 : issue.body.split('\\n').length;\n return (\n ` ${sourceLabel(source).padEnd(6)} title: \"${issue.title}\" | body: ${lines} line(s), ` +\n `${issue.body.length} char(s) | updated: ${issue.updatedAt} | hash: ${shortHash(issue.contentHash)}`\n );\n}\n\n/** 'local and GitHub', 'local, GitHub and memory' — origins in query order. */\nfunction listSources(candidates: FoundCandidate[]): string {\n const labels = candidates.map((candidate) => sourceLabel(candidate.source));\n if (labels.length <= 1) {\n return labels.join('');\n }\n return `${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`;\n}\n\n/**\n * Turn a readline interface into an awaitable line reader.\n *\n * Lines are queued instead of read on demand: `rl.question` drops whatever\n * arrives between two questions, which silently loses a piped answer typed\n * ahead of the prompt. Resolves `null` once the input is exhausted, so EOF ends\n * the prompt instead of hanging the process.\n */\nfunction createLineReader(\n rl: ReadlineInterface,\n output: NodeJS.WritableStream,\n): (query: string) => Promise<string | null> {\n const queued: string[] = [];\n let pending: ((line: string | null) => void) | null = null;\n let closed = false;\n\n rl.on('line', (line: string) => {\n if (pending !== null) {\n const resolve = pending;\n pending = null;\n resolve(line);\n return;\n }\n queued.push(line);\n });\n rl.on('close', () => {\n closed = true;\n if (pending !== null) {\n const resolve = pending;\n pending = null;\n resolve(null);\n }\n });\n\n return (query: string) =>\n new Promise<string | null>((resolve) => {\n const buffered = queued.shift();\n if (buffered !== undefined) {\n resolve(buffered);\n return;\n }\n if (closed) {\n resolve(null);\n return;\n }\n output.write(query);\n pending = resolve;\n });\n}\n\n/**\n * Interactive choice between the divergent versions.\n *\n * The options are numbered from the origins that actually answered, so a third\n * provider simply shows up as `[3] Memory` without this function knowing which\n * origins exist. Cancel is always the last option.\n */\nasync function promptChoice(\n sources: IssueSource[],\n stdin: NodeJS.ReadableStream,\n stdout: NodeJS.WritableStream,\n warn: (message: string) => void,\n): Promise<IssueSource | 'cancel'> {\n const options = sources.map((source, index) => `[${index + 1}] ${promptLabel(source)}`);\n const cancelOption = sources.length + 1;\n const query = `Which version should be used? ${options.join(' ')} [${cancelOption}] Cancel: `;\n const numbers = Array.from({ length: cancelOption }, (_, index) => String(index + 1));\n const accepted = `${numbers.slice(0, -1).join(', ')} or ${numbers[numbers.length - 1]}`;\n\n const rl = createInterface({ input: stdin, output: stdout });\n const ask = createLineReader(rl, stdout);\n try {\n for (let attempt = 0; attempt < MAX_PROMPT_ATTEMPTS; attempt++) {\n const answer = await ask(query);\n if (answer === null) {\n return 'cancel';\n }\n const choice = answer.trim();\n const picked = sources[Number(choice) - 1];\n if (choice.length > 0 && picked !== undefined) {\n return picked;\n }\n if (choice === String(cancelOption)) {\n return 'cancel';\n }\n warn(`Invalid choice: '${choice}'. Enter ${accepted}.`);\n }\n return 'cancel';\n } finally {\n rl.close();\n }\n}\n\nfunction isInteractiveByDefault(): boolean {\n const ci = process.env.CI;\n const inCi = ci !== undefined && ci !== '' && ci !== '0' && ci.toLowerCase() !== 'false';\n return Boolean(process.stdin.isTTY) && !inCi;\n}\n\nfunction buildResolved(\n issue: Issue,\n source: IssueSource,\n local: Issue | null,\n github: Issue | null,\n divergent: boolean,\n): ResolvedIssue {\n return { issue, source, local, github, divergent };\n}\n\n/** Origin `conflictPolicy` points at, or `null` for 'ask'. */\nfunction policyTarget(policy: IssuesConfig['conflictPolicy']): IssueSource | null {\n if (policy === 'prefer-local') {\n return 'local';\n }\n if (policy === 'prefer-github') {\n return 'github';\n }\n return null;\n}\n\n/**\n * Single entry point every command uses to decide which Issue the pipeline\n * works on.\n *\n * The logic is written against the origins that answered, never against a fixed\n * list of sources: registering a new provider is enough for it to take part in\n * the resolution, be reported in a divergence and appear in the prompt.\n *\n * Scenarios:\n * - only one origin has it -> that one, no questions asked;\n * - several, same contentHash -> equivalence is reported and the preferred\n * origin wins, without a prompt;\n * - several, different content -> the divergence is reported and\n * `conflictPolicy` decides (`ask` prompts on a TTY, falls back to\n * `preferredProvider` with a warning anywhere else);\n * - none -> IssueResolutionError, which carries the CLI exit code.\n */\nexport async function resolveIssue(\n id: string,\n opts: ResolveIssueOptions = {},\n): Promise<ResolvedIssue> {\n const info = opts.info ?? printInfo;\n const warn = opts.warn ?? printWarning;\n const config = opts.config ?? (await loadIssuesConfig({ warn }));\n\n ensureProvidersRegistered();\n const sources = opts.sources ?? getRegisteredSources();\n\n // Each origin is an independent I/O call (network for GitHub, disk for\n // local); querying them concurrently instead of one-by-one keeps the total\n // latency at the slowest origin instead of the sum of all of them, without\n // changing which candidate wins (order is preserved by Promise.all).\n const candidates: Candidate[] = await Promise.all(\n sources.map((source) => fetchCandidate(source, id, warn)),\n );\n\n const issueOf = (source: IssueSource): Issue | null =>\n candidates.find((candidate) => candidate.source === source)?.issue ?? null;\n const local = issueOf('local');\n const github = issueOf('github');\n\n const found = candidates.filter(\n (candidate): candidate is FoundCandidate => candidate.issue !== null,\n );\n\n if (found.length === 0) {\n const details = candidates\n .map((candidate) => `${sourceLabel(candidate.source)}: ${candidate.reason ?? 'not found'}`)\n .join('; ');\n const where = details.length > 0 ? ` (${details})` : '';\n throw new IssueResolutionError(`Issue '${id}' not found in any registered origin${where}`);\n }\n\n const build = (candidate: FoundCandidate, divergent: boolean): ResolvedIssue =>\n buildResolved(candidate.issue, candidate.source, local, github, divergent);\n\n const firstFound = found[0] as FoundCandidate;\n if (found.length === 1) {\n return build(firstFound, false);\n }\n\n // Several origins have it. The preferred one wins whenever it is among them;\n // otherwise the first origin queried does, so an unrelated preference never\n // leaves the pipeline without an Issue.\n const preferred =\n found.find((candidate) => candidate.source === config.preferredProvider) ?? firstFound;\n\n const distinct = new Set(found.map((candidate) => candidate.issue.contentHash));\n if (distinct.size === 1) {\n info(\n `Issue '${id}' has identical content in ${listSources(found)}; using ${sourceLabel(preferred.source)}.`,\n );\n return build(preferred, false);\n }\n\n info(`Issue '${id}' differs between origins:`);\n for (const candidate of found) {\n info(describeCandidate(candidate.source, candidate.issue));\n }\n\n const target = policyTarget(config.conflictPolicy);\n if (target !== null) {\n const chosen = found.find((candidate) => candidate.source === target);\n if (chosen !== undefined) {\n info(`Conflict policy '${config.conflictPolicy}': using the ${sourceLabel(target)} version.`);\n return build(chosen, true);\n }\n // The policy names an origin that has no version of this Issue: say so\n // instead of silently picking for the user under its name.\n warn(\n `Conflict policy '${config.conflictPolicy}' does not apply to Issue '${id}' ` +\n `(${sourceLabel(target)} has no version of it); using ${sourceLabel(preferred.source)}.`,\n );\n return build(preferred, true);\n }\n\n const interactive = opts.interactive ?? isInteractiveByDefault();\n if (!interactive) {\n warn(\n `Divergent Issue '${id}' and conflict policy 'ask' in a non-interactive environment; ` +\n `using the preferred provider (${sourceLabel(preferred.source)}).`,\n );\n return build(preferred, true);\n }\n\n const choice = await promptChoice(\n found.map((candidate) => candidate.source),\n opts.stdin ?? process.stdin,\n opts.stdout ?? process.stdout,\n warn,\n );\n if (choice === 'cancel') {\n throw new IssueResolutionError(\n `Cancelled: Issue '${id}' diverges between ${listSources(found)}.`,\n );\n }\n const picked = found.find((candidate) => candidate.source === choice) as FoundCandidate;\n return build(picked, true);\n}\n","import { printError } from '../ui/logger.js';\nimport { IssueResolutionError, type ResolveIssueOptions, resolveIssue } from './resolver.js';\nimport type { Issue, ResolvedIssue } from './types.js';\n\n/**\n * Bridge between the resolved Issue and the phase prompts.\n *\n * Commands never fetch the Issue themselves: they resolve it once through\n * `resolveIssue` and hand the content to the template, so every phase sees the\n * exact same title/body regardless of where the demand came from.\n */\n\n/** Path of the markdown file a local Issue lives in. */\nexport function localIssueRef(id: string): string {\n return `issues/${id}/issue.md`;\n}\n\n/**\n * Reference a human (or an agent) can follow to read the Issue: the remote URL\n * when there is one, the local file otherwise.\n */\nexport function issueReference(issue: Issue): string {\n return issue.remoteRef ?? localIssueRef(issue.id);\n}\n\n/** Shown instead of an empty list so the prompt never renders a dangling label. */\nconst NO_LABELS = '(none)';\n\n/**\n * Placeholder values every phase template consumes, replacing the former\n * `gh issue view` instruction.\n */\nexport function issuePlaceholders(resolved: ResolvedIssue): Record<string, string> {\n const { issue } = resolved;\n return {\n __ISSUE_TITLE__: issue.title,\n __ISSUE_BODY__: issue.body,\n __ISSUE_LABELS__: issue.labels.length > 0 ? issue.labels.join(', ') : NO_LABELS,\n __ISSUE_SOURCE__: resolved.source,\n __ISSUE_URL__: issueReference(issue),\n };\n}\n\nexport type CommandIssue = { ok: true; resolved: ResolvedIssue } | { ok: false; code: number };\n\n/**\n * Resolve the Issue a command works on, turning a resolution failure into the\n * exit code the command returns.\n *\n * `preResolved` short-circuits the lookup: the pipeline decides the origin once\n * and passes the decision down, so a phase never re-queries the providers (and\n * never asks the user twice about the same divergence).\n *\n * `options` forwards resolver options — `run.ts` passes the configuration it\n * already loaded so `.issue-flow.json` is read (and warned about) only once.\n */\nexport async function resolveCommandIssue(\n id: string,\n preResolved?: ResolvedIssue,\n options?: ResolveIssueOptions,\n): Promise<CommandIssue> {\n if (preResolved !== undefined) {\n return { ok: true, resolved: preResolved };\n }\n\n try {\n return { ok: true, resolved: await resolveIssue(id, options) };\n } catch (err) {\n if (err instanceof IssueResolutionError) {\n printError(err.message);\n return { ok: false, code: err.exitCode };\n }\n const message = err instanceof Error ? err.message : String(err);\n printError(`Could not resolve issue '${id}': ${message}`);\n return { ok: false, code: 1 };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,uBAAuB;AAChC,OAAO,WAAW;AAClB,SAAS,aAAa;AAmCtB,SAAS,eAAe,MAAc,OAAwC;AAC5E,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,UAAU,MAAM,SAAmB;AAAA,IAC5C,KAAK;AAAA,IACL,KAAK;AACH,aAAO,UAAU,MAAM,SAAmB;AAAA,IAC5C,KAAK;AACH,aAAQ,MAAM,WAAsB;AAAA,IACtC,KAAK;AACH,aAAQ,MAAM,WAAsB;AAAA,IACtC,KAAK,QAAQ;AACX,YAAM,MAAO,MAAM,WAAsB;AACzC,aAAO,IAAI,SAAS,KAAK,GAAG,IAAI,UAAU,GAAG,EAAE,CAAC,QAAQ;AAAA,IAC1D;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAKA,SAAS,UAAU,UAAsC;AACvD,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,SAAS,WAAW,GAAG,GAAG;AAC5B,WAAO,SAAS,UAAU,IAAI,SAAS,CAAC;AAAA,EAC1C;AAEA,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,OAAO,MAAM,MAAM,EAAE,EAAE,KAAK,GAAG,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAKA,SAAS,iBACP,MACA,OACA,UACM;AACN,MAAI;AAQJ,MAAI;AACF,YAAQ,KAAK,MAAM,IAAI;AAAA,EACzB,QAAQ;AACN;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,SAAS;AACzB,QAAM,OAAO,aAAa,CAAC,QAAgB,QAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAE1E,MAAI,MAAM,SAAS,eAAe,MAAM,SAAS,SAAS;AACxD,UAAM;AAEN,eAAW,SAAS,MAAM,QAAQ,SAAS;AACzC,UAAI,MAAM,SAAS,UAAU,MAAM,MAAM;AAEvC,cAAM,QAAQ,MAAM,KAAK,MAAM,IAAI;AACnC,mBAAW,YAAY,OAAO;AAC5B,cAAI,CAAC,SAAS,KAAK,EAAG;AACtB,gBAAM,SAAS,UAAU,MAAM,IAAI,KAAK,MAAM,SAAS,IAAI,IAAI,KAAK,MAAM,SAAS;AACnF,gBAAM,OAAO,UAAU,MAAM,IAAI,QAAQ,IAAI;AAC7C,eAAK,GAAG,MAAM,GAAG,IAAI,EAAE;AAAA,QACzB;AAAA,MACF;AAEA,UAAI,MAAM,SAAS,cAAc,MAAM,MAAM;AAC3C,cAAM,UAAU,MAAM,QAAQ,eAAe,MAAM,MAAM,MAAM,KAAK,IAAI;AACxE,4BAAoB,EAAE,QAAQ;AAAA,UAC5B,MAAM;AAAA,UACN,IAAI,OAAO;AAAA,UACX,MAAM,MAAM;AAAA,UACZ,QAAQ,WAAW;AAAA,QACrB,CAAC;AACD,cAAM,WAAW,MAAM,KAAK,OAAO,EAAE;AAErC,cAAM,SAAS,UAAU,MAAM,IAAI,KAAK,MAAM,SAAS,IAAI,IAAI,KAAK,MAAM,SAAS;AACnF,cAAM,WAAW,UAAU,MAAM,KAAK,MAAM,IAAI,IAAI,MAAM;AAC1D,cAAM,YAAY,UAAU,MAAM,KAAK,QAAQ,IAAI;AACnD,cAAM,cAAc,UAAW,UAAU,MAAM,IAAI,OAAO,IAAI,UAAW;AAEzE,aAAK,GAAG,MAAM,GAAG,QAAQ,IAAI,SAAS,IAAI,WAAW,EAAE;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;AAOA,eAAe,mBACb,QACA,UACA,SACA,cACA,eACA,UACyB;AACzB,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,SAAS;AACzB,QAAM,OAAO,aAAa,CAAC,QAAgB,QAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAG1E,MAAI,eAAe;AACjB,UAAM,MAAM,UACR,MAAM,KAAK,GAAG,MAAM,KAAK,IAAI,aAAa,EAAE,IAC5C,GAAG,MAAM,KAAK,IAAI,aAAa;AACnC,SAAK,GAAG;AAAA,EACV;AAGA,QAAM,gBAAgB,UAAU,MAAM,IAAI,KAAK,MAAM,SAAS,EAAE,IAAI,KAAK,MAAM,SAAS;AACxF,OAAK,aAAa;AAElB,QAAM,YAAY,KAAK,IAAI;AAE3B,QAAM,OAAiB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,QAAQ;AAAA,EACjB;AAEA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,eAAW,QAAQ,cAAc;AAC/B,WAAK,KAAK,kBAAkB,IAAI;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,UAAU,MAAM;AAAA,IACvC,OAAO;AAAA,IACP,QAAQ;AAAA,IACR;AAAA,IACA,mBAAmB;AAAA,EACrB,CAAC;AAED,MAAI,aAAa;AACjB,MAAI,UAAU;AACd,MAAI,WAAgC;AACpC,QAAM,QAAQ,EAAE,WAAW,EAAE;AAE7B,MAAI,WAAW,QAAQ;AACrB,UAAM,KAAK,gBAAgB,EAAE,OAAO,WAAW,OAAO,CAAC;AACvD,qBAAiB,QAAQ,IAAI;AAC3B,uBAAiB,MAAM,OAAO,QAAQ;AAEtC,UAAI;AACF,cAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,YAAI,MAAM,SAAS,UAAU;AAC3B,uBAAa,MAAM,UAAU;AAC7B,oBAAU,MAAM,aAAa;AAC7B,cAAI,MAAM,kBAAkB,QAAQ,MAAM,OAAO;AAC/C,uBAAW;AAAA,cACT,aAAa,MAAM,MAAM;AAAA,cACzB,cAAc,MAAM,MAAM;AAAA,YAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAGA,QAAM,OAAO,MAAM;AAGnB,QAAM,aAAa,KAAK,OAAO,KAAK,IAAI,IAAI,aAAa,GAAI;AAC7D,QAAM,cAAc,eAAe,UAAU;AAC7C,QAAM,WAAW,UACb,MAAM,IAAI,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,WAAW,WAAW,EAAE,CAAC,EAAE,IAC3E,KAAK,MAAM,SAAS,aAAa,WAAW;AAChD,OAAK,QAAQ;AACb,OAAK,aAAa;AAElB,MAAI,KAAK,aAAa,KAAK,CAAC,YAAY;AACtC,UAAM,SAAS,KAAK,QAAQ,SAAS,KAAK;AAC1C,UAAM,YACJ,KAAK,aAAa,OAAQ,gBAAgB,QAAQ,KAAK,eAAe;AACxE,UAAM,WAAW,YACb,uCAAuC,OAAO,OAC9C,UAAU,2BAA2B,KAAK,QAAQ;AACtD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS,CAAC;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO,UAAU,aAAa;AAAA,EAChC;AACF;AAaA,eAAsB,YAAY,SAAmD;AACnF,QAAM;AAAA,IACJ;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI,UAAU,GAAG;AAEf,UAAM,oBAAoB,YAAY,kBAAkB;AACxD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,UAAU,gBAAgB,cAAc,aAAa,EAAE,MAAM,IAAI;AAEvE,MAAI,QAA6B;AACjC,MAAI,SAAS;AACX,YAAQ,IAAI,aAAa,CAAC,YAAY;AACpC,cAAQ,aAAa,SAAS,IAAI,MAAM,IAAI,IAAI,OAAO,GAAG,IAAI,IAAI,OAAO;AAAA,IAC3E,CAAC,EAAE,MAAM;AAAA,EACX;AAEA,QAAM,OAAiB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,QAAQ;AAAA,EACjB;AAEA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,eAAW,QAAQ,cAAc;AAC/B,WAAK,KAAK,kBAAkB,IAAI;AAAA,IAClC;AAAA,EACF;AAEA,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,UAAU,MAAM;AAAA,MACvC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR;AAAA,MACA,mBAAmB;AAAA,IACrB,CAAC;AAED,UAAM,SAAS,KAAK,QAAQ,SAAS,KAAK;AAC1C,UAAM,SAAS,KAAK,QAAQ,SAAS,KAAK;AAE1C,QAAI,KAAK,aAAa,GAAG;AACvB,YAAMA,WAAU,OAAO,KAAK,KAAK;AACjC,YAAMC,OAAM,SAAS,IACjB,MAAM,IAAI,KAAK,eAAeD,QAAO,CAAC,GAAG,IACzC,KAAK,eAAeA,QAAO,CAAC;AAChC,eAAS,KAAK,GAAG,aAAa,GAAGC,IAAG,EAAE;AACtC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO,UAAU,UAAU,2BAA2B,KAAK,QAAQ;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,KAAK,KAAK;AACjC,UAAM,MAAM,SAAS,IACjB,MAAM,IAAI,KAAK,eAAe,OAAO,CAAC,GAAG,IACzC,KAAK,eAAe,OAAO,CAAC;AAChC,aAAS,QAAQ,GAAG,aAAa,GAAG,GAAG,EAAE;AAEzC,QAAI,iBAAiB,QAAQ;AAC3B,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,MAAM;AAChC,eAAO;AAAA,UACL,SAAS;AAAA,UACT,QAAQ,OAAO,UAAU;AAAA,UACzB,MACE,OAAO,YAAY,OACf,EAAE,aAAa,OAAO,kBAAkB,cAAc,OAAO,kBAAkB,IAC/E;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF,QAAQ;AACN,eAAO;AAAA,UACL,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,eAAe,OAAO,KAAK,KAAK;AACtC,UAAM,WAAW,SAAS,IACtB,MAAM,IAAI,KAAK,eAAe,YAAY,CAAC,GAAG,IAC9C,KAAK,eAAe,YAAY,CAAC;AACrC,aAAS,KAAK,GAAG,aAAa,GAAG,QAAQ,EAAE;AAE3C,QAAI,QAAQ,SAAS,WAAW,KAAK,QAAQ,SAAS,WAAW,GAAG;AAClE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO,uCAAuC,OAAO;AAAA,MACvD;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF;AACF;;;AClYA,IAAM,YAAY,oBAAI,IAAgC;AAQ/C,SAAS,iBAAiB,UAA+B;AAC9D,YAAU,IAAI,SAAS,MAAM,QAAQ;AACvC;AAGO,SAAS,uBAAsC;AACpD,SAAO,CAAC,GAAG,UAAU,KAAK,CAAC;AAC7B;AAQO,SAAS,YAAY,QAAoC;AAC9D,QAAM,WAAW,UAAU,IAAI,MAAM;AACrC,MAAI,aAAa,QAAW;AAC1B,UAAM,YAAY,qBAAqB;AACvC,UAAM,OACJ,UAAU,SAAS,IACf,sBAAsB,UAAU,KAAK,IAAI,CAAC,MAC1C;AACN,UAAM,IAAI,MAAM,0BAA0B,MAAM,MAAM,IAAI,EAAE;AAAA,EAC9D;AACA,SAAO;AACT;;;AC5CA,SAAS,kBAAkB;AAY3B,SAAS,cAAc,OAAuB;AAC5C,SAAO,MAAM,QAAQ,UAAU,IAAI,EAAE,KAAK;AAC5C;AAQO,SAAS,iBAAiB,OAAe,MAAsB;AACpE,QAAM,UAAU,KAAK,UAAU;AAAA,IAC7B,OAAO,cAAc,KAAK;AAAA,IAC1B,MAAM,cAAc,IAAI;AAAA,EAC1B,CAAC;AACD,QAAM,SAAS,WAAW,QAAQ,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK;AACxE,SAAO,UAAU,MAAM;AACzB;;;ACvBA,IAAM,cAAc;AAGpB,IAAM,mBAAmB;AASzB,IAAM,oBACJ;AAiBF,SAAS,eAAe,OAA4B;AAClD,SAAO,OAAO,SAAS,EAAE,EAAE,YAAY,MAAM,WAAW,WAAW;AACrE;AAEA,SAAS,gBAAgB,QAA2B;AAClD,MAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AACpC,SAAO,OACJ,IAAI,CAAC,UAA6B,OAAO,UAAU,WAAW,QAAQ,OAAO,IAAK,EAClF,OAAO,CAAC,SAAyB,OAAO,SAAS,YAAY,KAAK,SAAS,CAAC;AACjF;AAEA,SAAS,QAAQ,SAAgC;AAC/C,QAAM,QAAQ,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAClE,QAAM,OAAO,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAC/D,QAAM,SAAS,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS;AACrE,QAAM,MAAM,OAAO,QAAQ,QAAQ,YAAY,QAAQ,IAAI,SAAS,IAAI,QAAQ,MAAM;AAEtF,SAAO;AAAA,IACL,IAAI,WAAW,OAAO,KAAK,OAAO,MAAM;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,gBAAgB,QAAQ,MAAM;AAAA,IACtC,OAAO,eAAe,QAAQ,KAAK;AAAA,IACnC,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAAA,IACvE,WAAW,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAAA,IACvE,aAAa,iBAAiB,OAAO,IAAI;AAAA,IACzC,KAAK;AAAA,EACP;AACF;AAGA,SAAS,YAAY,IAAoB;AACvC,SAAO,GAAG,KAAK,EAAE,QAAQ,MAAM,EAAE;AACnC;AAEA,SAAS,mBAAmB,QAA+B;AACzD,SAAO,OAAO,MAAM,iBAAiB,IAAI,CAAC,KAAK;AACjD;AASO,IAAM,sBAAN,MAAmD;AAAA,EAC/C,OAAO;AAAA;AAAA,EAGhB,MAAM,cAAgC;AACpC,QAAI;AACF,YAAM,UAAU,MAAM,IAAI,MAAM,CAAC,WAAW,GAAG,EAAE,SAAS,iBAAiB,CAAC;AAC5E,UAAI,QAAQ,aAAa,EAAG,QAAO;AAEnC,YAAM,OAAO,MAAM,IAAI,MAAM,CAAC,QAAQ,QAAQ,GAAG,EAAE,SAAS,iBAAiB,CAAC;AAC9E,aAAO,KAAK,aAAa;AAAA,IAC3B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,IAAI,IAAmC;AAC3C,UAAM,UAAU,YAAY,EAAE;AAC9B,UAAM,SAAS,MAAM,IAAI,MAAM,CAAC,SAAS,QAAQ,SAAS,UAAU,WAAW,CAAC;AAEhF,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,UAAU,OAAO,UAAU,OAAO,QAAQ,KAAK;AACrD,UAAI,kBAAkB,KAAK,MAAM,EAAG,QAAO;AAC3C,YAAM,IAAI;AAAA,QACR,iCAAiC,OAAO,KAAK,UAAU,sBAAsB;AAAA,MAC/E;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO,MAAM;AAAA,IACpC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,wCAAwC,OAAO,KAAK,OAAO,OAAO,MAAM,GAAG,GAAG,CAAC;AAAA,MACjF;AAAA,IACF;AAEA,WAAO,QAAQ,OAAO;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,OAAmC;AAC9C,UAAM,OAAO,CAAC,SAAS,UAAU,WAAW,MAAM,OAAO,UAAU,MAAM,IAAI;AAC7E,eAAW,SAAS,MAAM,QAAQ;AAChC,WAAK,KAAK,WAAW,KAAK;AAAA,IAC5B;AAEA,UAAM,SAAS,MAAM,IAAI,MAAM,IAAI;AACnC,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,UAAU,OAAO,UAAU,OAAO,QAAQ,KAAK;AACrD,YAAM,IAAI,MAAM,kCAAkC,UAAU,wBAAwB,EAAE;AAAA,IACxF;AAEA,UAAM,SAAS,mBAAmB,OAAO,MAAM;AAC/C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,sDAAsD,OAAO,OAAO,KAAK,KAAK,aAAa;AAAA,MAC7F;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,KAAK,IAAI,MAAM;AACrC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,iBAAiB,MAAM,yCAAyC;AAAA,IAClF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,IAA2B;AACrC,UAAM,UAAU,YAAY,EAAE;AAC9B,UAAM,SAAS,MAAM,IAAI,MAAM,CAAC,SAAS,SAAS,OAAO,CAAC;AAE1D,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,UAAU,OAAO,UAAU,OAAO,QAAQ,KAAK;AACrD,YAAM,IAAI;AAAA,QACR,iCAAiC,OAAO,KAAK,UAAU,uBAAuB;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,sBAAsB,IAAI,oBAAoB;;;ACrK3D,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,OAAO,SAAS,UAAU,MAAM,iBAAiB;AAClE,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAWzB,IAAM,aAAa;AAGnB,IAAM,0BAA0B;AAEhC,SAAS,WAAW,KAAuB;AACzC,QAAM,OAAQ,IAA8B;AAC5C,SAAO,SAAS,YAAY,SAAS;AACvC;AAMA,SAASC,aAAY,IAAoB;AACvC,QAAM,aAAa,GAAG,KAAK,EAAE,QAAQ,MAAM,EAAE;AAC7C,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AACA,MAAI,QAAQ,KAAK,UAAU,KAAK,eAAe,OAAO,eAAe,MAAM;AACzE,UAAM,IAAI,MAAM,oCAAoC,EAAE,GAAG;AAAA,EAC3D;AACA,SAAO;AACT;AAEA,SAAS,SAAS,IAA2B;AAC3C,SAAO,QAAQ,KAAK,EAAE,IAAI,OAAO,SAAS,IAAI,EAAE,IAAI;AACtD;AASO,SAAS,mBAAmB,SAAkD;AACnF,QAAM,QAAQ,QAAQ,QAAQ,UAAU,IAAI,EAAE,MAAM,IAAI;AACxD,QAAM,eAAe,MAAM,UAAU,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC;AACrE,QAAM,UAAU,iBAAiB,KAAK,SAAY,MAAM,YAAY,EAAE,MAAM,eAAe;AAE3F,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,OAAO,IAAI,MAAM,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,EACpD;AAEA,SAAO;AAAA,IACL,OAAO,QAAQ,CAAC,EAAE,KAAK;AAAA,IACvB,MAAM,MACH,MAAM,eAAe,CAAC,EACtB,KAAK,IAAI,EACT,KAAK;AAAA,EACV;AACF;AAEA,SAAS,oBAAoB,OAAe,MAAsB;AAChE,SAAO,KAAK,KAAK;AAAA;AAAA,EAAO,KAAK,KAAK,CAAC;AAAA;AACrC;AAUO,IAAM,yBAAN,MAAsD;AAAA,EAClD,OAAO;AAAA,EAEC;AAAA,EACT;AAAA,EAER,YAAY,aAAsB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,cAAgC;AACpC,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,KAAK,GAAG,UAAU,IAAI;AAC9C,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,IAAI,IAAmC;AAC3C,UAAM,UAAUA,aAAY,EAAE;AAC9B,UAAM,eAAe,MAAM,KAAK,UAAU,OAAO;AAEjD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,SAAS,cAAc,OAAO;AAAA,IAC5C,SAAS,KAAc;AACrB,UAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,YAAM;AAAA,IACR;AAEA,UAAM,EAAE,OAAO,cAAc,KAAK,IAAI,mBAAmB,GAAG;AAC5D,UAAM,WAAW,MAAM,KAAK,aAAa,OAAO;AAChD,UAAM,QAAQ,gBAAgB,UAAU,SAAS;AAEjD,QAAI,UAAU;AACZ,aAAO;AAAA,QACL,IAAI,SAAS;AAAA,QACb,QAAQ,SAAS;AAAA,QACjB;AAAA,QACA;AAAA,QACA,QAAQ,SAAS;AAAA,QACjB,OAAO,SAAS;AAAA,QAChB,QAAQ;AAAA,QACR,WAAW,SAAS,QAAQ,OAAO;AAAA,QACnC,WAAW,SAAS;AAAA,QACpB,WAAW,SAAS;AAAA,QACpB,aAAa,iBAAiB,OAAO,IAAI;AAAA,QACzC,KAAK;AAAA,MACP;AAAA,IACF;AAIA,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ,SAAS,OAAO;AAAA,MACxB;AAAA,MACA;AAAA,MACA,QAAQ,CAAC;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,WAAW,MAAM,UAAU,YAAY;AAAA,MACvC,WAAW,MAAM,MAAM,YAAY;AAAA,MACnC,aAAa,iBAAiB,OAAO,IAAI;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAmC;AAI9C,UAAM,KAAK,MAAM,OAAO,SAAY,OAAO,MAAM,KAAK,eAAe,CAAC,IAAIA,aAAY,MAAM,EAAE;AAC9F,UAAM,MAAM,MAAM,KAAK,SAAS,EAAE;AAElC,UAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAOpC,QAAI;AACF,YAAM,UAAU,KAAK,KAAK,UAAU,GAAG,oBAAoB,MAAM,OAAO,MAAM,IAAI,GAAG;AAAA,QACnF,UAAU;AAAA,QACV,MAAM;AAAA,MACR,CAAC;AAAA,IACH,SAAS,KAAc;AACrB,UAAK,IAA8B,SAAS,UAAU;AACpD,cAAM,IAAI;AAAA,UACR,gBAAgB,EAAE,uBAAuB,KAAK,YAAY,IAAI,UAAU,CAAC;AAAA,QAE3E;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAEA,UAAM,YAAY,OAAO;AACzB,UAAM,WAA0B;AAAA,MAC9B,eAAe;AAAA,MACf;AAAA,MACA,QAAQ,SAAS,EAAE;AAAA,MACnB,QAAQ;AAAA,MACR,OAAO,MAAM;AAAA,MACb,QAAQ,MAAM;AAAA,MACd,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,MACX,aAAa,iBAAiB,MAAM,OAAO,MAAM,IAAI;AAAA,MACrD,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IACjD;AAEA,UAAM,KAAK,cAAc,IAAI,QAAQ;AAErC,WAAO;AAAA,MACL;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB,OAAO,MAAM;AAAA,MACb,MAAM,MAAM,KAAK,KAAK;AAAA,MACtB,QAAQ,MAAM;AAAA,MACd,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW,MAAM,QAAQ,OAAO;AAAA,MAChC,WAAW;AAAA,MACX,WAAW;AAAA,MACX,aAAa,SAAS;AAAA,MACtB,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,IAA2B;AACrC,UAAM,QAAQ,MAAM,KAAK,IAAI,EAAE;AAC/B,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,gBAAgBA,aAAY,EAAE,CAAC,qBAAqB,UAAU,GAAG;AAAA,IACnF;AAEA,UAAM,UAAUA,aAAY,EAAE;AAC9B,UAAM,WAAW,MAAM,KAAK,aAAa,OAAO;AAEhD,UAAM,KAAK,cAAc,SAAS;AAAA,MAChC,eAAe;AAAA,MACf,IAAI,MAAM;AAAA,MACV,QAAQ,MAAM;AAAA,MACd,QAAQ,UAAU,UAAU;AAAA,MAC5B,OAAO,MAAM;AAAA,MACb,QAAQ,MAAM;AAAA,MACd,OAAO;AAAA,MACP,WAAW,MAAM;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,aAAa,MAAM;AAAA,MACnB,GAAI,UAAU,SAAS,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,IACxD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,iBAAkC;AACtC,UAAM,CAAC,OAAO,MAAM,IAAI,MAAM,QAAQ,IAAI,CAAC,KAAK,mBAAmB,GAAG,oBAAoB,CAAC,CAAC;AAC5F,WAAO,KAAK,IAAI,OAAO,MAAM,IAAI;AAAA,EACnC;AAAA,EAEA,MAAc,OAAwB;AACpC,QAAI,KAAK,mBAAmB,OAAW,QAAO,KAAK;AACnD,SAAK,eAAe,MAAM,eAAe;AACzC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,SAAS,IAA6B;AAClD,WAAO,KAAK,MAAM,KAAK,KAAK,GAAG,YAAY,EAAE;AAAA,EAC/C;AAAA,EAEA,MAAc,UAAU,IAA6B;AACnD,WAAO,KAAK,MAAM,KAAK,SAAS,EAAE,GAAG,UAAU;AAAA,EACjD;AAAA,EAEA,MAAc,aAAa,IAA6B;AACtD,WAAO,KAAK,MAAM,KAAK,SAAS,EAAE,GAAG,eAAe;AAAA,EACtD;AAAA;AAAA,EAGA,MAAc,aAAa,IAA2C;AACpE,UAAM,OAAO,MAAM,KAAK,aAAa,EAAE;AACvC,UAAM,WAAW,KAAK,YAAY,IAAI,eAAe;AAErD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,SAAS,MAAM,OAAO;AAAA,IACpC,SAAS,KAAc;AACrB,UAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,YAAM;AAAA,IACR;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,GAAG;AAAA,IACzB,SAAS,KAAc;AACrB,YAAM,IAAI,MAAM,mBAAmB,QAAQ,KAAM,IAAc,OAAO,EAAE;AAAA,IAC1E;AAEA,QAAI;AACF,aAAO,oBAAoB,MAAM,MAAM;AAAA,IACzC,SAAS,KAAc;AACrB,UAAI,eAAe,UAAU;AAC3B,cAAM,UAAU,IAAI,OACjB,IAAI,CAAC,UAAU,OAAO,MAAM,KAAK,KAAK,GAAG,KAAK,QAAQ,KAAK,MAAM,OAAO,EAAE,EAC1E,KAAK,IAAI;AACZ,cAAM,IAAI,MAAM,6BAA6B,QAAQ;AAAA,EAAM,OAAO,EAAE;AAAA,MACtE;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,IAAY,UAAwC;AAC9E,UAAM,gBAAgB,MAAM,KAAK,aAAa,EAAE,GAAG,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,EAC7F;AAAA;AAAA,EAGA,MAAc,qBAAsC;AAClD,UAAM,MAAM,KAAK,MAAM,KAAK,KAAK,GAAG,UAAU;AAE9C,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC,GAClD,OAAO,CAAC,UAAU,MAAM,YAAY,CAAC,EACrC,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,IAC9B,SAAS,KAAc;AACrB,UAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,YAAM;AAAA,IACR;AAEA,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,QAAQ,IAAI,OAAO,SAAS;AAC1B,cAAM,WAAW,SAAS,IAAI,KAAK;AAEnC,cAAM,eAAe,MAAM,KAAK,aAAa,IAAI,EAC9C,KAAK,CAAC,aAAa,UAAU,UAAU,CAAC,EACxC,MAAM,MAAM,CAAC;AAChB,eAAO,KAAK,IAAI,UAAU,YAAY;AAAA,MACxC,CAAC;AAAA,IACH;AAEA,WAAO,QAAQ,OAAO,CAAC,KAAK,UAAU,KAAK,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EAC/D;AACF;AAGA,IAAM,2BAA2B;AAgBjC,eAAe,sBAAuC;AACpD,QAAM,SAAS;AAAA,IACb,CAAC,SAAS,MAAM;AAAA,IAChB,CAAC,MAAM,MAAM;AAAA,EACf;AAEA,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,OAAO,IAAI,OAAO,SAAS;AACzB,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB;AAAA,UACA;AAAA,YACE,GAAG;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA,OAAO,wBAAwB;AAAA,YAC/B;AAAA,YACA;AAAA,UACF;AAAA,UACA,EAAE,SAAS,wBAAwB;AAAA,QACrC;AACA,YAAI,OAAO,aAAa,EAAG,QAAO;AAElC,cAAM,SAAS,KAAK,MAAM,OAAO,MAAM;AACvC,YAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnC,eAAO,OAAO,OAAO,CAAC,KAAa,UAAmB;AACpD,gBAAM,IAAK,OAAuC;AAClD,iBAAO,OAAO,MAAM,WAAW,KAAK,IAAI,KAAK,CAAC,IAAI;AAAA,QACpD,GAAG,CAAC;AAAA,MACN,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,QAAQ,OAAO,CAAC,KAAK,UAAU,KAAK,IAAI,KAAK,KAAK,GAAG,CAAC;AAC/D;AAGO,IAAM,yBAAyB,IAAI,uBAAuB;;;AC1X1D,SAAS,4BAAkC;AAChD,QAAM,aAAa,IAAI,IAAI,qBAAqB,CAAC;AAEjD,MAAI,CAAC,WAAW,IAAI,QAAQ,GAAG;AAC7B,qBAAiB,mBAAmB;AAAA,EACtC;AACA,MAAI,CAAC,WAAW,IAAI,OAAO,GAAG;AAC5B,qBAAiB,sBAAsB;AAAA,EACzC;AACF;;;ACpBA,SAAS,mBAAAC,wBAA4D;AAW9D,IAAM,uBAAN,cAAmC,MAAM;AAAA,EACrC;AAAA,EAET,YAAY,SAAiB,WAAW,GAAG;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EAClB;AACF;AAuBA,IAAM,sBAAsB;AAO5B,IAAM,gBAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,OAAO;AACT;AAEA,SAAS,YAAY,QAA6B;AAChD,SAAO,cAAc,MAAM,KAAK;AAClC;AAGA,SAAS,YAAY,QAA6B;AAChD,QAAM,QAAQ,YAAY,MAAM;AAChC,SAAO,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;AAcA,SAAS,aAAa,KAAsB;AAC1C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAUA,eAAe,eACb,QACA,IACA,MACoB;AACpB,MAAI;AACJ,MAAI;AACF,eAAW,YAAY,MAAM;AAAA,EAC/B,SAAS,KAAK;AACZ,WAAO,EAAE,QAAQ,OAAO,MAAM,QAAQ,aAAa,GAAG,EAAE;AAAA,EAC1D;AAEA,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,SAAS,YAAY;AAAA,EACzC,SAAS,KAAK;AACZ,WAAO,EAAE,QAAQ,OAAO,MAAM,QAAQ,yBAAyB,aAAa,GAAG,CAAC,IAAI;AAAA,EACtF;AACA,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,QAAQ,OAAO,MAAM,QAAQ,uBAAuB;AAAA,EAC/D;AAEA,MAAI;AACF,UAAM,QAAQ,MAAM,SAAS,IAAI,EAAE;AACnC,WAAO,EAAE,QAAQ,OAAO,QAAQ,UAAU,OAAO,cAAc,KAAK;AAAA,EACtE,SAAS,KAAK;AACZ,UAAM,SAAS,aAAa,GAAG;AAG/B,SAAK,yBAAyB,EAAE,UAAU,YAAY,MAAM,CAAC,KAAK,MAAM,EAAE;AAC1E,WAAO,EAAE,QAAQ,OAAO,MAAM,OAAO;AAAA,EACvC;AACF;AAEA,SAAS,UAAU,MAAsB;AACvC,QAAM,MAAM,KAAK,WAAW,SAAS,IAAI,KAAK,MAAM,UAAU,MAAM,IAAI;AACxE,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;AAGA,SAAS,kBAAkB,QAAqB,OAAsB;AACpE,QAAM,QAAQ,MAAM,KAAK,WAAW,IAAI,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE;AACnE,SACE,KAAK,YAAY,MAAM,EAAE,OAAO,CAAC,CAAC,YAAY,MAAM,KAAK,aAAa,KAAK,aACxE,MAAM,KAAK,MAAM,uBAAuB,MAAM,SAAS,YAAY,UAAU,MAAM,WAAW,CAAC;AAEtG;AAGA,SAAS,YAAY,YAAsC;AACzD,QAAM,SAAS,WAAW,IAAI,CAAC,cAAc,YAAY,UAAU,MAAM,CAAC;AAC1E,MAAI,OAAO,UAAU,GAAG;AACtB,WAAO,OAAO,KAAK,EAAE;AAAA,EACvB;AACA,SAAO,GAAG,OAAO,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,QAAQ,OAAO,OAAO,SAAS,CAAC,CAAC;AAC3E;AAUA,SAAS,iBACP,IACA,QAC2C;AAC3C,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAkD;AACtD,MAAI,SAAS;AAEb,KAAG,GAAG,QAAQ,CAAC,SAAiB;AAC9B,QAAI,YAAY,MAAM;AACpB,YAAM,UAAU;AAChB,gBAAU;AACV,cAAQ,IAAI;AACZ;AAAA,IACF;AACA,WAAO,KAAK,IAAI;AAAA,EAClB,CAAC;AACD,KAAG,GAAG,SAAS,MAAM;AACnB,aAAS;AACT,QAAI,YAAY,MAAM;AACpB,YAAM,UAAU;AAChB,gBAAU;AACV,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,CAAC;AAED,SAAO,CAAC,UACN,IAAI,QAAuB,CAAC,YAAY;AACtC,UAAM,WAAW,OAAO,MAAM;AAC9B,QAAI,aAAa,QAAW;AAC1B,cAAQ,QAAQ;AAChB;AAAA,IACF;AACA,QAAI,QAAQ;AACV,cAAQ,IAAI;AACZ;AAAA,IACF;AACA,WAAO,MAAM,KAAK;AAClB,cAAU;AAAA,EACZ,CAAC;AACL;AASA,eAAe,aACb,SACA,OACA,QACA,MACiC;AACjC,QAAM,UAAU,QAAQ,IAAI,CAAC,QAAQ,UAAU,IAAI,QAAQ,CAAC,KAAK,YAAY,MAAM,CAAC,EAAE;AACtF,QAAM,eAAe,QAAQ,SAAS;AACtC,QAAM,QAAQ,iCAAiC,QAAQ,KAAK,IAAI,CAAC,MAAM,YAAY;AACnF,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,aAAa,GAAG,CAAC,GAAG,UAAU,OAAO,QAAQ,CAAC,CAAC;AACpF,QAAM,WAAW,GAAG,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC;AAErF,QAAM,KAAKC,iBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,CAAC;AAC3D,QAAM,MAAM,iBAAiB,IAAI,MAAM;AACvC,MAAI;AACF,aAAS,UAAU,GAAG,UAAU,qBAAqB,WAAW;AAC9D,YAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AACA,YAAM,SAAS,OAAO,KAAK;AAC3B,YAAM,SAAS,QAAQ,OAAO,MAAM,IAAI,CAAC;AACzC,UAAI,OAAO,SAAS,KAAK,WAAW,QAAW;AAC7C,eAAO;AAAA,MACT;AACA,UAAI,WAAW,OAAO,YAAY,GAAG;AACnC,eAAO;AAAA,MACT;AACA,WAAK,oBAAoB,MAAM,YAAY,QAAQ,GAAG;AAAA,IACxD;AACA,WAAO;AAAA,EACT,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,SAAS,yBAAkC;AACzC,QAAM,KAAK,QAAQ,IAAI;AACvB,QAAM,OAAO,OAAO,UAAa,OAAO,MAAM,OAAO,OAAO,GAAG,YAAY,MAAM;AACjF,SAAO,QAAQ,QAAQ,MAAM,KAAK,KAAK,CAAC;AAC1C;AAEA,SAAS,cACP,OACA,QACA,OACA,QACA,WACe;AACf,SAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,UAAU;AACnD;AAGA,SAAS,aAAa,QAA4D;AAChF,MAAI,WAAW,gBAAgB;AAC7B,WAAO;AAAA,EACT;AACA,MAAI,WAAW,iBAAiB;AAC9B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmBA,eAAsB,aACpB,IACA,OAA4B,CAAC,GACL;AACxB,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,SAAS,KAAK,UAAW,MAAM,iBAAiB,EAAE,KAAK,CAAC;AAE9D,4BAA0B;AAC1B,QAAM,UAAU,KAAK,WAAW,qBAAqB;AAMrD,QAAM,aAA0B,MAAM,QAAQ;AAAA,IAC5C,QAAQ,IAAI,CAAC,WAAW,eAAe,QAAQ,IAAI,IAAI,CAAC;AAAA,EAC1D;AAEA,QAAM,UAAU,CAAC,WACf,WAAW,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM,GAAG,SAAS;AACxE,QAAM,QAAQ,QAAQ,OAAO;AAC7B,QAAM,SAAS,QAAQ,QAAQ;AAE/B,QAAM,QAAQ,WAAW;AAAA,IACvB,CAAC,cAA2C,UAAU,UAAU;AAAA,EAClE;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,UAAU,WACb,IAAI,CAAC,cAAc,GAAG,YAAY,UAAU,MAAM,CAAC,KAAK,UAAU,UAAU,WAAW,EAAE,EACzF,KAAK,IAAI;AACZ,UAAM,QAAQ,QAAQ,SAAS,IAAI,KAAK,OAAO,MAAM;AACrD,UAAM,IAAI,qBAAqB,UAAU,EAAE,uCAAuC,KAAK,EAAE;AAAA,EAC3F;AAEA,QAAM,QAAQ,CAAC,WAA2B,cACxC,cAAc,UAAU,OAAO,UAAU,QAAQ,OAAO,QAAQ,SAAS;AAE3E,QAAM,aAAa,MAAM,CAAC;AAC1B,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,MAAM,YAAY,KAAK;AAAA,EAChC;AAKA,QAAM,YACJ,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,OAAO,iBAAiB,KAAK;AAE9E,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,CAAC,cAAc,UAAU,MAAM,WAAW,CAAC;AAC9E,MAAI,SAAS,SAAS,GAAG;AACvB;AAAA,MACE,UAAU,EAAE,8BAA8B,YAAY,KAAK,CAAC,WAAW,YAAY,UAAU,MAAM,CAAC;AAAA,IACtG;AACA,WAAO,MAAM,WAAW,KAAK;AAAA,EAC/B;AAEA,OAAK,UAAU,EAAE,4BAA4B;AAC7C,aAAW,aAAa,OAAO;AAC7B,SAAK,kBAAkB,UAAU,QAAQ,UAAU,KAAK,CAAC;AAAA,EAC3D;AAEA,QAAM,SAAS,aAAa,OAAO,cAAc;AACjD,MAAI,WAAW,MAAM;AACnB,UAAM,SAAS,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM;AACpE,QAAI,WAAW,QAAW;AACxB,WAAK,oBAAoB,OAAO,cAAc,gBAAgB,YAAY,MAAM,CAAC,WAAW;AAC5F,aAAO,MAAM,QAAQ,IAAI;AAAA,IAC3B;AAGA;AAAA,MACE,oBAAoB,OAAO,cAAc,8BAA8B,EAAE,MACnE,YAAY,MAAM,CAAC,iCAAiC,YAAY,UAAU,MAAM,CAAC;AAAA,IACzF;AACA,WAAO,MAAM,WAAW,IAAI;AAAA,EAC9B;AAEA,QAAM,cAAc,KAAK,eAAe,uBAAuB;AAC/D,MAAI,CAAC,aAAa;AAChB;AAAA,MACE,oBAAoB,EAAE,+FACa,YAAY,UAAU,MAAM,CAAC;AAAA,IAClE;AACA,WAAO,MAAM,WAAW,IAAI;AAAA,EAC9B;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB,MAAM,IAAI,CAAC,cAAc,UAAU,MAAM;AAAA,IACzC,KAAK,SAAS,QAAQ;AAAA,IACtB,KAAK,UAAU,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,MAAI,WAAW,UAAU;AACvB,UAAM,IAAI;AAAA,MACR,qBAAqB,EAAE,sBAAsB,YAAY,KAAK,CAAC;AAAA,IACjE;AAAA,EACF;AACA,QAAM,SAAS,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM;AACpE,SAAO,MAAM,QAAQ,IAAI;AAC3B;;;AClXO,SAAS,cAAc,IAAoB;AAChD,SAAO,UAAU,EAAE;AACrB;AAMO,SAAS,eAAe,OAAsB;AACnD,SAAO,MAAM,aAAa,cAAc,MAAM,EAAE;AAClD;AAGA,IAAM,YAAY;AAMX,SAAS,kBAAkB,UAAiD;AACjF,QAAM,EAAE,MAAM,IAAI;AAClB,SAAO;AAAA,IACL,iBAAiB,MAAM;AAAA,IACvB,gBAAgB,MAAM;AAAA,IACtB,kBAAkB,MAAM,OAAO,SAAS,IAAI,MAAM,OAAO,KAAK,IAAI,IAAI;AAAA,IACtE,kBAAkB,SAAS;AAAA,IAC3B,eAAe,eAAe,KAAK;AAAA,EACrC;AACF;AAeA,eAAsB,oBACpB,IACA,aACA,SACuB;AACvB,MAAI,gBAAgB,QAAW;AAC7B,WAAO,EAAE,IAAI,MAAM,UAAU,YAAY;AAAA,EAC3C;AAEA,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,UAAU,MAAM,aAAa,IAAI,OAAO,EAAE;AAAA,EAC/D,SAAS,KAAK;AACZ,QAAI,eAAe,sBAAsB;AACvC,iBAAW,IAAI,OAAO;AACtB,aAAO,EAAE,IAAI,OAAO,MAAM,IAAI,SAAS;AAAA,IACzC;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAW,4BAA4B,EAAE,MAAM,OAAO,EAAE;AACxD,WAAO,EAAE,IAAI,OAAO,MAAM,EAAE;AAAA,EAC9B;AACF;","names":["elapsed","dur","normalizeId","createInterface","createInterface"]}
@@ -24,10 +24,28 @@ var lastErrorSchema = z.object({
24
24
  message: z.string(),
25
25
  at: z.string()
26
26
  });
27
+ var issueMetadataSchema = z.object({
28
+ schemaVersion: z.literal(1),
29
+ id: z.string().min(1),
30
+ number: z.number().int().positive().nullable(),
31
+ source: z.enum(["github", "local"]),
32
+ title: z.string(),
33
+ labels: z.array(z.string()),
34
+ state: z.enum(["open", "closed"]),
35
+ createdAt: z.string(),
36
+ updatedAt: z.string(),
37
+ contentHash: z.string(),
38
+ remote: z.object({
39
+ provider: z.enum(["github", "local"]),
40
+ ref: z.string(),
41
+ syncedAt: z.string(),
42
+ syncedContentHash: z.string()
43
+ }).optional()
44
+ });
27
45
  var taskPlanSchema = z.object({
28
46
  project: z.string(),
29
- issueNumber: z.number().int().positive(),
30
- issueUrl: z.string(),
47
+ issueNumber: z.union([z.number().int().positive(), z.string().min(1)]),
48
+ issueUrl: z.string().optional().default(""),
31
49
  branchName: z.string(),
32
50
  noBranch: z.boolean().optional().default(false),
33
51
  description: z.string(),
@@ -124,6 +142,12 @@ var webConfigSchema = z.object({
124
142
  logLimit: z.number().int().positive().default(200),
125
143
  includeLogs: z.boolean().default(true)
126
144
  });
145
+ var issuesConfigSchema = z.object({
146
+ defaultGenerateTarget: z.enum(["github", "local", "both"]).default("github"),
147
+ preferredProvider: z.enum(["github", "local"]).default("github"),
148
+ conflictPolicy: z.enum(["ask", "prefer-local", "prefer-github"]).default("ask"),
149
+ requireConfirmation: z.boolean().default(true)
150
+ });
127
151
 
128
152
  // src/core/session-state.ts
129
153
  import { mkdir, rename, writeFile } from "fs/promises";
@@ -521,10 +545,30 @@ function getSessionPublisher() {
521
545
  }
522
546
 
523
547
  // src/core/state-manager.ts
524
- import { copyFile, mkdtemp, readFile, rename as rename2, unlink, writeFile as writeFile2 } from "fs/promises";
525
- import { tmpdir } from "os";
526
- import { join } from "path";
548
+ import { readFile } from "fs/promises";
527
549
  import { ZodError } from "zod";
550
+
551
+ // src/utils/fs.ts
552
+ import { copyFile, mkdtemp, rename as rename2, unlink, writeFile as writeFile2 } from "fs/promises";
553
+ import { tmpdir } from "os";
554
+ import { basename, join } from "path";
555
+ async function writeFileAtomic(path, content) {
556
+ const tmpDir = await mkdtemp(join(tmpdir(), "issue-flow-"));
557
+ const tmpFile = join(tmpDir, basename(path));
558
+ await writeFile2(tmpFile, content, "utf-8");
559
+ try {
560
+ await rename2(tmpFile, path);
561
+ } catch (err) {
562
+ if (err.code === "EXDEV") {
563
+ await copyFile(tmpFile, path);
564
+ await unlink(tmpFile);
565
+ } else {
566
+ throw err;
567
+ }
568
+ }
569
+ }
570
+
571
+ // src/core/state-manager.ts
528
572
  function isoNow() {
529
573
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
530
574
  }
@@ -543,20 +587,8 @@ ${issues}`);
543
587
  }
544
588
  }
545
589
  async function saveTaskPlan(path, plan) {
546
- const tmpDir = await mkdtemp(join(tmpdir(), "issue-flow-task-plan-"));
547
- const tmpFile = join(tmpDir, "tasks.json");
548
- await writeFile2(tmpFile, `${JSON.stringify(plan, null, 2)}
549
- `, "utf-8");
550
- try {
551
- await rename2(tmpFile, path);
552
- } catch (err) {
553
- if (err.code === "EXDEV") {
554
- await copyFile(tmpFile, path);
555
- await unlink(tmpFile);
556
- } else {
557
- throw err;
558
- }
559
- }
590
+ await writeFileAtomic(path, `${JSON.stringify(plan, null, 2)}
591
+ `);
560
592
  }
561
593
  function initializeState(plan) {
562
594
  const defaultPipeline = {
@@ -810,12 +842,15 @@ var ElapsedTimer = class {
810
842
  };
811
843
 
812
844
  export {
845
+ issueMetadataSchema,
813
846
  taskPlanSchema,
814
847
  webConfigSchema,
848
+ issuesConfigSchema,
815
849
  NullPublisher,
816
850
  FilePublisher,
817
851
  setSessionPublisher,
818
852
  getSessionPublisher,
853
+ writeFileAtomic,
819
854
  isoNow,
820
855
  loadTaskPlan,
821
856
  saveTaskPlan,
@@ -847,4 +882,4 @@ export {
847
882
  formatDuration,
848
883
  ElapsedTimer
849
884
  };
850
- //# sourceMappingURL=chunk-O5XI2CFF.js.map
885
+ //# sourceMappingURL=chunk-WZDCTAWZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas.ts","../src/core/session-state.ts","../src/core/session-publisher.ts","../src/core/state-manager.ts","../src/utils/fs.ts","../src/core/verbose.ts","../src/ui/logger.ts"],"sourcesContent":["import { z } from 'zod';\nimport type { SessionSnapshot } from './core/session-state.js';\nimport type { IssueMetadata, IssuesConfig } from './issues/types.js';\n\n/**\n * Zod schemas for validating tasks.json structure, Issue metadata, headless\n * invocation outputs, the web monitoring session snapshot and the web\n * configuration.\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\n/**\n * Persisted metadata for an Issue stored on disk (issues/<id>/metadata.json).\n * `satisfies` keeps this schema in lockstep with the IssueMetadata interface in\n * src/issues/types.ts — changing one without the other fails the typecheck.\n */\nexport const issueMetadataSchema = z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n number: z.number().int().positive().nullable(),\n source: z.enum(['github', 'local']),\n title: z.string(),\n labels: z.array(z.string()),\n state: z.enum(['open', 'closed']),\n createdAt: z.string(),\n updatedAt: z.string(),\n contentHash: z.string(),\n remote: z\n .object({\n provider: z.enum(['github', 'local']),\n ref: z.string(),\n syncedAt: z.string(),\n syncedContentHash: z.string(),\n })\n .optional(),\n}) satisfies z.ZodType<IssueMetadata>;\n\n/**\n * tasks.json structure. Deliberately permissive: plans written by older\n * versions must keep loading, so `issueUrl` is optional (Issues with no remote\n * have no URL) and `issueNumber` accepts non-numeric local identifiers.\n */\nexport const taskPlanSchema = z.object({\n project: z.string(),\n issueNumber: z.union([z.number().int().positive(), z.string().min(1)]),\n issueUrl: z.string().optional().default(''),\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\nconst sessionLogEntrySchema = z.object({\n at: z.string(),\n level: z.enum(['info', 'warn', 'error']),\n message: z.string(),\n});\n\nconst sessionPhaseSchema = z.object({\n name: z.string(),\n status: z.enum(['pending', 'running', 'completed', 'failed']),\n startedAt: z.string().nullable(),\n endedAt: z.string().nullable(),\n durationSeconds: z.number().nullable(),\n error: z.string().nullable(),\n});\n\nconst sessionStorySchema = z.object({\n id: z.string(),\n title: z.string(),\n priority: z.number(),\n passes: z.boolean(),\n completedAt: z.string().nullable(),\n});\n\n/**\n * Session snapshot served by the web monitoring mode (session.json and the\n * HTTP endpoint). `satisfies` keeps this schema in lockstep with the\n * SessionSnapshot interface in src/core/session-state.ts — changing one\n * without the other fails the typecheck.\n */\nexport const sessionSnapshotSchema = z.object({\n schemaVersion: z.literal(1),\n sessionId: z.string().nullable(),\n readOnly: z.literal(true),\n capabilities: z.array(z.string()),\n issue: z.object({ number: z.number().nullable(), url: z.string().nullable() }),\n status: z.enum(['idle', 'running', 'completed', 'failed']),\n startedAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n endedAt: z.string().nullable(),\n elapsedSeconds: z.number().nullable(),\n estimatedRemainingSeconds: z.number().nullable(),\n progress: z.object({\n percent: z.number(),\n phasesCompleted: z.number(),\n phasesTotal: z.number(),\n storiesCompleted: z.number(),\n storiesTotal: z.number(),\n }),\n currentPhase: z.string().nullable(),\n currentActivity: z\n .object({\n story: z.string().nullable(),\n tool: z.string().nullable(),\n detail: z.string().nullable(),\n since: z.string(),\n })\n .nullable(),\n phases: z.array(sessionPhaseSchema),\n stories: z.array(sessionStorySchema),\n execution: z.object({\n iteration: z.number(),\n retries: z.number(),\n correctionCycle: z.number(),\n maxCorrectionCycles: z.number().nullable(),\n }),\n git: z.object({\n branch: z.string().nullable(),\n baseBranch: z.string().nullable(),\n commits: z.array(z.object({ hash: z.string(), subject: z.string() })),\n }),\n pullRequests: z.array(z.object({ number: z.number(), url: z.string(), title: z.string() })),\n logs: z.array(sessionLogEntrySchema),\n errors: z.array(sessionLogEntrySchema),\n warnings: z.array(sessionLogEntrySchema),\n lastError: z.object({ message: z.string(), at: z.string() }).nullable(),\n nextSteps: z.array(z.string()),\n environment: z.object({ node: z.string(), platform: z.string() }).nullable(),\n}) satisfies z.ZodType<SessionSnapshot>;\n\n/**\n * Resolved web monitoring configuration. Every field has a default, so\n * parsing a partial object (e.g. the `web` key of .issue-flow.json) fills in\n * the documented defaults.\n */\nexport const webConfigSchema = z.object({\n enabled: z.boolean().default(false),\n port: z.number().int().min(1).max(65535).default(3737),\n host: z.string().min(1).default('0.0.0.0'),\n refreshSeconds: z.number().positive().default(5),\n logLimit: z.number().int().positive().default(200),\n includeLogs: z.boolean().default(true),\n});\n\n/**\n * Resolved Issue provider configuration. Every field has a default that keeps\n * the GitHub-only behaviour, so parsing an empty object yields the exact\n * behaviour of releases without the provider layer.\n */\nexport const issuesConfigSchema = z.object({\n defaultGenerateTarget: z.enum(['github', 'local', 'both']).default('github'),\n preferredProvider: z.enum(['github', 'local']).default('github'),\n conflictPolicy: z.enum(['ask', 'prefer-local', 'prefer-github']).default('ask'),\n requireConfirmation: z.boolean().default(true),\n}) satisfies z.ZodType<IssuesConfig>;\n\nexport type ValidatedTaskPlan = z.infer<typeof taskPlanSchema>;\nexport type ValidatedIssueMetadata = z.infer<typeof issueMetadataSchema>;\nexport type ValidatedHeadlessResult = z.infer<typeof headlessResultSchema>;\nexport type ValidatedSessionSnapshot = z.infer<typeof sessionSnapshotSchema>;\nexport type WebConfig = z.infer<typeof webConfigSchema>;\n","import { mkdir, rename, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { stripVTControlCharacters } from 'node:util';\nimport type { UserStory } from '../types.js';\n\n/**\n * Session state publishing layer for the optional web monitoring mode.\n *\n * Instrumentation points call publish() with SessionEvents; a pure reducer\n * folds each event into an in-memory SessionSnapshot that any surface\n * (file, HTTP endpoint, future webhooks) can consume in a single format.\n *\n * Monitoring must never affect the pipeline: publish() is synchronous and\n * never throws, and all publisher I/O failures are swallowed after a\n * single warning.\n */\n\nexport type SessionLogLevel = 'info' | 'warn' | 'error';\nexport type SessionStatus = 'idle' | 'running' | 'completed' | 'failed';\nexport type SessionPhaseStatus = 'pending' | 'running' | 'completed' | 'failed';\n\n/** Default max entries retained in the logs ring buffer. */\nexport const DEFAULT_LOG_LIMIT = 200;\n\n/** Default minimum interval between FilePublisher disk writes. */\nexport const DEFAULT_THROTTLE_MS = 1000;\n\nexport type SessionEvent =\n | {\n type: 'session:start';\n at: string;\n sessionId: string;\n /** `null` for local identifiers that are not numbers. */\n issueNumber: number | null;\n issueUrl?: string;\n branch?: string;\n baseBranch?: string;\n phases: string[];\n environment?: SessionEnvironment;\n }\n | { type: 'phase:start'; at: string; phase: string }\n | { type: 'phase:end'; at: string; phase: string; success: boolean; error?: string }\n | { type: 'iteration:start'; at: string; iteration: number }\n | { type: 'iteration:end'; at: string; iteration: number }\n | { type: 'retry'; at: string; attempt: number; delaySeconds?: number; reason?: string }\n | { type: 'stories:update'; at: string; stories: UserStory[] }\n | { type: 'activity'; at: string; story?: string; tool?: string; detail?: string }\n | { type: 'log'; at: string; level: SessionLogLevel; message: string }\n | { type: 'correction:cycle'; at: string; cycle: number; maxCycles: number }\n | {\n type: 'git:update';\n at: string;\n branch?: string;\n baseBranch?: string;\n commits?: SessionCommit[];\n pullRequests?: SessionPullRequest[];\n }\n | { type: 'session:end'; at: string; status: 'completed' | 'failed'; error?: string };\n\nexport interface SessionEnvironment {\n node: string;\n platform: string;\n}\n\nexport interface SessionLogEntry {\n at: string;\n level: SessionLogLevel;\n message: string;\n}\n\nexport interface SessionPhaseSnapshot {\n name: string;\n status: SessionPhaseStatus;\n startedAt: string | null;\n endedAt: string | null;\n durationSeconds: number | null;\n error: string | null;\n}\n\nexport interface SessionStorySnapshot {\n id: string;\n title: string;\n priority: number;\n passes: boolean;\n /**\n * When the story flipped to passing during this session; null for stories\n * that were already passing at session start (their duration is unknown).\n */\n completedAt: string | null;\n}\n\nexport interface SessionActivity {\n story: string | null;\n tool: string | null;\n detail: string | null;\n since: string;\n}\n\nexport interface SessionCommit {\n hash: string;\n subject: string;\n}\n\nexport interface SessionPullRequest {\n number: number;\n url: string;\n title: string;\n}\n\nexport interface SessionSnapshot {\n schemaVersion: 1;\n sessionId: string | null;\n readOnly: true;\n capabilities: string[];\n issue: { number: number | null; url: string | null };\n status: SessionStatus;\n startedAt: string | null;\n updatedAt: string | null;\n endedAt: string | null;\n elapsedSeconds: number | null;\n estimatedRemainingSeconds: number | null;\n progress: {\n percent: number;\n phasesCompleted: number;\n phasesTotal: number;\n storiesCompleted: number;\n storiesTotal: number;\n };\n currentPhase: string | null;\n currentActivity: SessionActivity | null;\n phases: SessionPhaseSnapshot[];\n stories: SessionStorySnapshot[];\n execution: {\n iteration: number;\n retries: number;\n correctionCycle: number;\n maxCorrectionCycles: number | null;\n };\n git: { branch: string | null; baseBranch: string | null; commits: SessionCommit[] };\n pullRequests: SessionPullRequest[];\n logs: SessionLogEntry[];\n errors: SessionLogEntry[];\n warnings: SessionLogEntry[];\n lastError: { message: string; at: string } | null;\n nextSteps: string[];\n environment: SessionEnvironment | null;\n}\n\nexport interface SessionReducerOptions {\n /** Max entries retained in the logs ring buffer. */\n logLimit?: number;\n}\n\nexport function createInitialSnapshot(): SessionSnapshot {\n return {\n schemaVersion: 1,\n sessionId: null,\n readOnly: true,\n capabilities: [],\n issue: { number: null, url: null },\n status: 'idle',\n startedAt: null,\n updatedAt: null,\n endedAt: null,\n elapsedSeconds: null,\n estimatedRemainingSeconds: null,\n progress: {\n percent: 0,\n phasesCompleted: 0,\n phasesTotal: 0,\n storiesCompleted: 0,\n storiesTotal: 0,\n },\n currentPhase: null,\n currentActivity: null,\n phases: [],\n stories: [],\n execution: { iteration: 0, retries: 0, correctionCycle: 0, maxCorrectionCycles: null },\n git: { branch: null, baseBranch: null, commits: [] },\n pullRequests: [],\n logs: [],\n errors: [],\n warnings: [],\n lastError: null,\n nextSteps: [],\n environment: null,\n };\n}\n\n/**\n * Seconds between two ISO timestamps, or null when either is unparsable.\n */\nfunction secondsBetween(from: string | null, to: string): number | null {\n if (from === null) return null;\n const fromMs = Date.parse(from);\n const toMs = Date.parse(to);\n if (Number.isNaN(fromMs) || Number.isNaN(toMs)) return null;\n return Math.max(0, Math.round((toMs - fromMs) / 1000));\n}\n\nfunction computePercent(phasesCompleted: number, phasesTotal: number): number {\n if (phasesTotal === 0) return 0;\n return Math.round((phasesCompleted / phasesTotal) * 100);\n}\n\n/**\n * Estimated seconds until all stories pass: average duration of the stories\n * completed during this session × pending stories. Durations are the gaps\n * between consecutive completions (the first is measured from startedAt).\n * Published as null with fewer than two samples.\n */\nfunction estimateRemainingSeconds(snapshot: SessionSnapshot): number | null {\n const completions = snapshot.stories\n .map((story) => (story.completedAt === null ? Number.NaN : Date.parse(story.completedAt)))\n .filter((ms) => !Number.isNaN(ms))\n .sort((a, b) => a - b);\n if (completions.length < 2) return null;\n\n const startMs = snapshot.startedAt === null ? Number.NaN : Date.parse(snapshot.startedAt);\n const boundaries = Number.isNaN(startMs) ? completions : [startMs, ...completions];\n const durations: number[] = [];\n for (let i = 1; i < boundaries.length; i++) {\n durations.push(Math.max(0, boundaries[i] - boundaries[i - 1]));\n }\n if (durations.length === 0) return null;\n\n const averageMs = durations.reduce((sum, d) => sum + d, 0) / durations.length;\n const pending = snapshot.stories.filter((story) => !story.passes).length;\n return Math.round((averageMs * pending) / 1000);\n}\n\n/**\n * Remaining pipeline phases, in order. The phase list originates from the\n * session:start event, whose caller passes PIPELINE_PHASES (or\n * PIPELINE_PHASES_NO_BRANCH in --no-branch mode) from src/core/pipeline.ts.\n */\nfunction deriveNextSteps(snapshot: SessionSnapshot): string[] {\n if (snapshot.status === 'completed') return [];\n return snapshot.phases.filter((phase) => phase.status === 'pending').map((phase) => phase.name);\n}\n\n/**\n * Fold a SessionEvent into a SessionSnapshot. Pure: never mutates the input\n * and performs no I/O. Unknown event types return the snapshot unchanged.\n *\n * errors/warnings are derived slices of the logs ring buffer, recomputed on\n * each reduction — they are never accumulated separately. The same applies\n * to estimatedRemainingSeconds and nextSteps.\n */\nexport function reduceSessionEvent(\n snapshot: SessionSnapshot,\n event: SessionEvent,\n options?: SessionReducerOptions,\n): SessionSnapshot {\n const next = applyEvent(snapshot, event, options);\n if (next === snapshot) return snapshot;\n\n const elapsedSeconds = secondsBetween(next.startedAt, event.at) ?? next.elapsedSeconds;\n return {\n ...next,\n updatedAt: event.at,\n elapsedSeconds,\n estimatedRemainingSeconds: estimateRemainingSeconds(next),\n errors: next.logs.filter((entry) => entry.level === 'error'),\n warnings: next.logs.filter((entry) => entry.level === 'warn'),\n nextSteps: deriveNextSteps(next),\n };\n}\n\nfunction applyEvent(\n snapshot: SessionSnapshot,\n event: SessionEvent,\n options?: SessionReducerOptions,\n): SessionSnapshot {\n switch (event.type) {\n case 'session:start': {\n const initial = createInitialSnapshot();\n return {\n ...initial,\n sessionId: event.sessionId,\n issue: { number: event.issueNumber, url: event.issueUrl ?? null },\n status: 'running',\n startedAt: event.at,\n progress: {\n ...initial.progress,\n phasesTotal: event.phases.length,\n },\n phases: event.phases.map((name) => ({\n name,\n status: 'pending' as const,\n startedAt: null,\n endedAt: null,\n durationSeconds: null,\n error: null,\n })),\n git: { branch: event.branch ?? null, baseBranch: event.baseBranch ?? null, commits: [] },\n environment: event.environment ?? null,\n };\n }\n\n case 'phase:start': {\n const known = snapshot.phases.some((p) => p.name === event.phase);\n const phases = known\n ? snapshot.phases.map((p) =>\n p.name === event.phase\n ? {\n ...p,\n status: 'running' as const,\n startedAt: event.at,\n endedAt: null,\n error: null,\n }\n : p,\n )\n : [\n ...snapshot.phases,\n {\n name: event.phase,\n status: 'running' as const,\n startedAt: event.at,\n endedAt: null,\n durationSeconds: null,\n error: null,\n },\n ];\n return {\n ...snapshot,\n currentPhase: event.phase,\n phases,\n progress: known ? snapshot.progress : { ...snapshot.progress, phasesTotal: phases.length },\n };\n }\n\n case 'phase:end': {\n const phases = snapshot.phases.map((p) =>\n p.name === event.phase\n ? {\n ...p,\n status: event.success ? ('completed' as const) : ('failed' as const),\n endedAt: event.at,\n durationSeconds: secondsBetween(p.startedAt, event.at),\n error: event.error ? stripVTControlCharacters(event.error) : null,\n }\n : p,\n );\n const phasesCompleted = phases.filter((p) => p.status === 'completed').length;\n return {\n ...snapshot,\n currentPhase: snapshot.currentPhase === event.phase ? null : snapshot.currentPhase,\n currentActivity: null,\n phases,\n progress: {\n ...snapshot.progress,\n phasesCompleted,\n percent: computePercent(phasesCompleted, snapshot.progress.phasesTotal),\n },\n };\n }\n\n case 'iteration:start':\n return {\n ...snapshot,\n execution: { ...snapshot.execution, iteration: event.iteration },\n };\n\n case 'iteration:end':\n return { ...snapshot, currentActivity: null };\n\n case 'retry':\n return {\n ...snapshot,\n execution: { ...snapshot.execution, retries: snapshot.execution.retries + 1 },\n };\n\n case 'stories:update': {\n const previous = new Map(snapshot.stories.map((story) => [story.id, story]));\n const stories = event.stories.map((story) => {\n const before = previous.get(story.id);\n // Stamp the flip to passing; stories already passing when first seen\n // keep null (completed before this session, duration unknown).\n const completedAt = !story.passes\n ? null\n : before && !before.passes\n ? event.at\n : (before?.completedAt ?? null);\n return {\n id: story.id,\n title: story.title,\n priority: story.priority,\n passes: story.passes,\n completedAt,\n };\n });\n return {\n ...snapshot,\n stories,\n progress: {\n ...snapshot.progress,\n storiesCompleted: stories.filter((s) => s.passes).length,\n storiesTotal: stories.length,\n },\n };\n }\n\n case 'activity': {\n const story = event.story ?? null;\n const tool = event.tool ?? null;\n const detail = event.detail ?? null;\n const current = snapshot.currentActivity;\n // Same activity repeated: keep `since` so the UI can show \"for how long\".\n const since =\n current && current.story === story && current.tool === tool && current.detail === detail\n ? current.since\n : event.at;\n return { ...snapshot, currentActivity: { story, tool, detail, since } };\n }\n\n case 'log': {\n const limit = options?.logLimit ?? DEFAULT_LOG_LIMIT;\n const entry: SessionLogEntry = {\n at: event.at,\n level: event.level,\n message: stripVTControlCharacters(event.message),\n };\n const logs = [...snapshot.logs, entry].slice(-Math.max(1, limit));\n return { ...snapshot, logs };\n }\n\n case 'git:update':\n return {\n ...snapshot,\n git: {\n branch: event.branch ?? snapshot.git.branch,\n baseBranch: event.baseBranch ?? snapshot.git.baseBranch,\n commits: event.commits ?? snapshot.git.commits,\n },\n pullRequests: event.pullRequests ?? snapshot.pullRequests,\n };\n\n case 'correction:cycle':\n return {\n ...snapshot,\n execution: {\n ...snapshot.execution,\n correctionCycle: event.cycle,\n maxCorrectionCycles: event.maxCycles,\n },\n };\n\n case 'session:end':\n return {\n ...snapshot,\n status: event.status,\n endedAt: event.at,\n currentPhase: null,\n currentActivity: null,\n lastError: event.error\n ? { message: stripVTControlCharacters(event.error), at: event.at }\n : snapshot.lastError,\n };\n\n default:\n return snapshot;\n }\n}\n\nexport interface SessionPublisher {\n /**\n * Publish an event. Synchronous, never throws, never returns a promise —\n * safe to call from any instrumentation point without affecting execution.\n */\n publish(event: SessionEvent): void;\n /** Current in-memory snapshot. */\n snapshot(): SessionSnapshot;\n /** Monotonic counter, bumped on every applied event (basis for ETags). */\n version(): number;\n /** Force any pending output to be written. Never rejects. */\n flush(): Promise<void>;\n /** Flush and release resources. Never rejects. */\n close(): Promise<void>;\n}\n\n/**\n * Default publisher when monitoring is off: every call is a no-op, so each\n * instrumentation point costs a method call that returns immediately.\n */\nexport class NullPublisher implements SessionPublisher {\n private readonly empty = createInitialSnapshot();\n\n publish(_event: SessionEvent): void {}\n\n snapshot(): SessionSnapshot {\n return this.empty;\n }\n\n version(): number {\n return 0;\n }\n\n async flush(): Promise<void> {}\n\n async close(): Promise<void> {}\n}\n\nexport interface MemoryPublisherOptions extends SessionReducerOptions {\n /** Called at most once, on the first internal failure. */\n onWarn?: (message: string) => void;\n /**\n * When false, log events are dropped before reaching the snapshot, so no\n * log line is ever published (session.json or HTTP). Default true.\n */\n includeLogs?: boolean;\n}\n\n/**\n * In-memory publisher: reduces events over a snapshot and tracks a monotonic\n * version. Base class for publishers with an output surface (file, HTTP).\n */\nexport class MemoryPublisher implements SessionPublisher {\n protected state: SessionSnapshot = createInitialSnapshot();\n protected versionCounter = 0;\n private warned = false;\n private readonly onWarn: (message: string) => void;\n private readonly reducerOptions: SessionReducerOptions;\n private readonly includeLogs: boolean;\n\n constructor(options: MemoryPublisherOptions = {}) {\n this.onWarn = options.onWarn ?? ((message) => process.stderr.write(`${message}\\n`));\n this.reducerOptions = { logLimit: options.logLimit };\n this.includeLogs = options.includeLogs ?? true;\n }\n\n publish(event: SessionEvent): void {\n if (event.type === 'log' && !this.includeLogs) return;\n try {\n this.state = reduceSessionEvent(this.state, event, this.reducerOptions);\n this.versionCounter++;\n this.afterPublish(event);\n } catch (err) {\n this.warnOnce(err);\n }\n }\n\n /** Hook for subclasses; runs inside publish()'s try/catch. */\n protected afterPublish(_event: SessionEvent): void {}\n\n snapshot(): SessionSnapshot {\n return this.state;\n }\n\n version(): number {\n return this.versionCounter;\n }\n\n async flush(): Promise<void> {}\n\n async close(): Promise<void> {}\n\n protected warnOnce(err: unknown): void {\n if (this.warned) return;\n this.warned = true;\n const message = err instanceof Error ? err.message : String(err);\n try {\n this.onWarn(\n `issue-flow: web monitoring hit an error (will keep retrying silently): ${message}`,\n );\n } catch {\n // Even a failing warn callback must not propagate to the pipeline.\n }\n }\n}\n\nexport interface FilePublisherOptions extends MemoryPublisherOptions {\n /** Minimum interval between disk writes (ms). Default 1000. */\n throttleMs?: number;\n}\n\n/**\n * Publisher that mirrors the snapshot to issues/N/session.json.\n *\n * Writes are atomic (write-to-temp + rename) and throttled; terminal events\n * (phase:end, session:end) force an immediate write. All I/O failures are\n * swallowed after a single warning.\n */\nexport class FilePublisher extends MemoryPublisher {\n private readonly filePath: string;\n private readonly throttleMs: number;\n private timer: NodeJS.Timeout | null = null;\n private lastWriteStartedAt = 0;\n private lastWrittenVersion = 0;\n private writeChain: Promise<void> = Promise.resolve();\n private closed = false;\n\n constructor(filePath: string, options: FilePublisherOptions = {}) {\n super(options);\n this.filePath = filePath;\n this.throttleMs = options.throttleMs ?? DEFAULT_THROTTLE_MS;\n }\n\n protected override afterPublish(event: SessionEvent): void {\n if (this.closed) return;\n const terminal = event.type === 'phase:end' || event.type === 'session:end';\n this.scheduleWrite(terminal);\n }\n\n private scheduleWrite(force: boolean): void {\n if (force) {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.enqueueWrite();\n return;\n }\n if (this.timer !== null) return;\n const wait = Math.max(0, this.throttleMs - (Date.now() - this.lastWriteStartedAt));\n if (wait === 0) {\n this.enqueueWrite();\n return;\n }\n this.timer = setTimeout(() => {\n this.timer = null;\n this.enqueueWrite();\n }, wait);\n this.timer.unref();\n }\n\n private enqueueWrite(): void {\n this.lastWriteStartedAt = Date.now();\n this.writeChain = this.writeChain.then(async () => {\n const version = this.versionCounter;\n if (version === this.lastWrittenVersion) return;\n const payload = `${JSON.stringify(this.state, null, 2)}\\n`;\n try {\n await atomicWriteFile(this.filePath, payload);\n this.lastWrittenVersion = version;\n } catch (err) {\n this.warnOnce(err);\n }\n });\n }\n\n override async flush(): Promise<void> {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n if (this.versionCounter !== this.lastWrittenVersion) {\n this.enqueueWrite();\n }\n await this.writeChain;\n }\n\n override async close(): Promise<void> {\n await this.flush();\n this.closed = true;\n }\n}\n\n/**\n * Atomic write: write to a temp file next to the target, then rename. The\n * same-directory temp keeps the rename on a single filesystem (rename is\n * atomic; no EXDEV fallback needed, unlike an os.tmpdir() temp on Linux\n * tmpfs) and leaves nothing behind. The FilePublisher write chain is the\n * single writer, so the fixed .tmp name never races.\n *\n * The target directory (issues/N/) may not exist yet the first time a fresh\n * issue publishes — pipeline phases create it lazily, and this can be the\n * very first write. mkdir recursive is idempotent, so it's cheap to ensure\n * on every write rather than relying on call order elsewhere.\n */\nasync function atomicWriteFile(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const tmpFile = `${path}.tmp`;\n await writeFile(tmpFile, content, 'utf-8');\n await rename(tmpFile, path);\n}\n","import { NullPublisher, type SessionPublisher } from './session-state.js';\n\n/**\n * Global slot for the active SessionPublisher, following the same pattern as\n * setVerbose in verbose.ts. Instrumentation points (logger, engine, headless)\n * read the slot via getSessionPublisher(); run.ts installs a FilePublisher\n * when web monitoring is enabled. The default NullPublisher makes every\n * instrumentation point a no-op when monitoring is off.\n */\n\nconst NULL_PUBLISHER = new NullPublisher();\n\nlet _publisher: SessionPublisher = NULL_PUBLISHER;\n\n/**\n * Install the active session publisher. Pass undefined to reset to the\n * default NullPublisher.\n */\nexport function setSessionPublisher(publisher: SessionPublisher | undefined): void {\n _publisher = publisher ?? NULL_PUBLISHER;\n}\n\nexport function getSessionPublisher(): SessionPublisher {\n return _publisher;\n}\n","import { readFile } from 'node:fs/promises';\nimport { ZodError } from 'zod';\nimport { taskPlanSchema } from '../schemas.js';\nimport type { LastError, PipelineState, TaskPlan } from '../types.js';\nimport { writeFileAtomic } from '../utils/fs.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 await writeFileAtomic(path, `${JSON.stringify(plan, null, 2)}\\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","import { copyFile, mkdtemp, rename, unlink, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { basename, join } from 'node:path';\n\n/**\n * Write a file atomically (write-to-temp + rename).\n *\n * A crash mid-write leaves the previous content untouched instead of a\n * truncated file, which matters for every artifact the pipeline reloads on the\n * next run (tasks.json, metadata.json).\n */\nexport async function writeFileAtomic(path: string, content: string): Promise<void> {\n const tmpDir = await mkdtemp(join(tmpdir(), 'issue-flow-'));\n const tmpFile = join(tmpDir, basename(path));\n\n await writeFile(tmpFile, content, '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","let _verbose = false;\n\nexport function setVerbose(enabled: boolean): void {\n _verbose = enabled;\n}\n\nexport function isVerbose(): boolean {\n return _verbose;\n}\n\nlet _onOutput: ((line: string) => void) | undefined;\n\n/**\n * Set a global output callback for routing verbose output (e.g., through listr2 task.output).\n * Pass undefined to clear and fall back to direct stderr writes.\n */\nexport function setOutputCallback(callback: ((line: string) => void) | undefined): void {\n _onOutput = callback;\n}\n\nexport function getOutputCallback(): ((line: string) => void) | undefined {\n return _onOutput;\n}\n\nlet _onStoryUpdate: ((stories: import('../types.js').UserStory[]) => void) | undefined;\n\n/**\n * Set a global callback for story progress updates during engine execution.\n * Called after each iteration with the latest user stories state.\n * Pass undefined to clear.\n */\nexport function setStoryUpdateCallback(\n callback: ((stories: import('../types.js').UserStory[]) => void) | undefined,\n): void {\n _onStoryUpdate = callback;\n}\n\nexport function getStoryUpdateCallback():\n | ((stories: import('../types.js').UserStory[]) => void)\n | undefined {\n return _onStoryUpdate;\n}\n\nlet _globalTimeout: number | undefined;\n\nexport function setGlobalTimeout(ms: number): void {\n _globalTimeout = ms;\n}\n\nexport function getGlobalTimeout(): number | undefined {\n return _globalTimeout;\n}\n","import chalk from 'chalk';\nimport ora, { type Ora } from 'ora';\nimport { getSessionPublisher } from '../core/session-publisher.js';\nimport type { SessionLogLevel } from '../core/session-state.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getOutputCallback } from '../core/verbose.js';\n\n/**\n * Detect if unicode output is supported.\n */\nexport function useUnicode(): boolean {\n if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;\n if (!process.stdout.isTTY) return false;\n return true;\n}\n\n/**\n * Detect if color output is supported.\n */\nexport function useColor(): boolean {\n if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;\n if (!process.stdout.isTTY) return false;\n return true;\n}\n\nexport interface Icons {\n success: string;\n fail: string;\n pending: string;\n retry: string;\n warn: string;\n start: string;\n end: string;\n notReached: string;\n tool: string;\n connector: string;\n}\n\nexport function getIcons(): Icons {\n if (useUnicode()) {\n return {\n success: '\\u2713',\n fail: '\\u2717',\n pending: '\\u23F3',\n retry: '\\u21BB',\n warn: '\\u26A0',\n start: '\\u25B6',\n end: '\\u25A0',\n notReached: '\\u25CB',\n tool: '\\u25B8',\n connector: '\\u2502',\n };\n }\n return {\n success: '[OK]',\n fail: '[FAIL]',\n pending: '[...]',\n retry: '[RETRY]',\n warn: '[WARN]',\n start: '[START]',\n end: '[END]',\n notReached: '[ ]',\n tool: '>',\n connector: '|',\n };\n}\n\n/**\n * Get the terminal width.\n */\nexport function getTermWidth(): number {\n return process.stdout.columns ?? 80;\n}\n\n/**\n * Emit a line of output. Routes through the global output callback when\n * running inside a listr2 task context, otherwise falls back to console.log.\n *\n * Also forwards the line to the session publisher with its structured level;\n * ANSI codes are stripped by the reducer, so terminal output stays untouched.\n */\nfunction emit(line: string, level: SessionLogLevel = 'info'): void {\n getSessionPublisher().publish({ type: 'log', at: isoNow(), level, message: line });\n const cb = getOutputCallback();\n if (cb) {\n cb(line);\n } else {\n console.log(line);\n }\n}\n\nexport function printSuccess(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.green(`${icons.success} ${message}`));\n } else {\n emit(`${icons.success} ${message}`);\n }\n}\n\nexport function printError(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.red(`${icons.fail} ${message}`), 'error');\n } else {\n emit(`${icons.fail} ${message}`, 'error');\n }\n}\n\nexport function printWarning(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.yellow(`${icons.warn} ${message}`), 'warn');\n } else {\n emit(`${icons.warn} ${message}`, 'warn');\n }\n}\n\nexport function printRetry(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.yellow(`${icons.retry} ${message}`), 'warn');\n } else {\n emit(`${icons.retry} ${message}`, 'warn');\n }\n}\n\nexport function printInfo(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n emit(chalk.blue(`${icons.start} ${message}`));\n } else {\n emit(`${icons.start} ${message}`);\n }\n}\n\n/**\n * Create an ora spinner with consistent styling.\n */\nexport function createSpinner(message: string): Ora {\n return ora({\n text: message,\n color: 'blue',\n spinner: 'dots',\n });\n}\n\n/**\n * Format a duration in seconds to a human-readable string.\n */\nexport function formatDuration(totalSeconds: number): string {\n const hours = Math.floor(totalSeconds / 3600);\n const mins = Math.floor((totalSeconds % 3600) / 60);\n const secs = totalSeconds % 60;\n\n if (hours > 0) {\n return `${hours}h ${mins}m ${secs}s`;\n }\n if (mins > 0) {\n return `${mins}m ${secs}s`;\n }\n return `${secs}s`;\n}\n\n/**\n * Timer that calls a callback every second with the formatted elapsed time.\n */\nexport class ElapsedTimer {\n private startTime: number;\n private intervalId: ReturnType<typeof setInterval> | null = null;\n private cleanup = () => this.stop();\n\n constructor(private onTick: (elapsed: string) => void) {\n this.startTime = Date.now();\n }\n\n start(): this {\n this.startTime = Date.now();\n this.intervalId = setInterval(() => {\n const seconds = Math.floor((Date.now() - this.startTime) / 1000);\n this.onTick(formatDuration(seconds));\n }, 1_000);\n // Prevent the timer from keeping the process alive\n if (this.intervalId && typeof this.intervalId === 'object' && 'unref' in this.intervalId) {\n this.intervalId.unref();\n }\n process.on('exit', this.cleanup);\n return this;\n }\n\n getElapsedSeconds(): number {\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n stop(): number {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n process.removeListener('exit', this.cleanup);\n return this.getElapsedSeconds();\n }\n}\n"],"mappings":"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","names":["rename","writeFile"]}
@@ -2,8 +2,9 @@
2
2
  import {
3
3
  printError,
4
4
  printInfo,
5
- printSuccess
6
- } from "./chunk-O5XI2CFF.js";
5
+ printSuccess,
6
+ printWarning
7
+ } from "./chunk-WZDCTAWZ.js";
7
8
 
8
9
  // src/commands/init.ts
9
10
  import { execa } from "execa";
@@ -12,9 +13,10 @@ async function checkClaude() {
12
13
  const proc = await execa("claude", ["--version"], { reject: false, timeout: 1e4 });
13
14
  if (proc.exitCode === 0) {
14
15
  const version = proc.stdout?.toString().trim() ?? "unknown";
15
- return { name: "claude CLI", passed: true, detail: version };
16
+ return { key: "claude", name: "claude CLI", passed: true, detail: version };
16
17
  }
17
18
  return {
19
+ key: "claude",
18
20
  name: "claude CLI",
19
21
  passed: false,
20
22
  detail: "claude command failed",
@@ -22,6 +24,7 @@ async function checkClaude() {
22
24
  };
23
25
  } catch {
24
26
  return {
27
+ key: "claude",
25
28
  name: "claude CLI",
26
29
  passed: false,
27
30
  detail: "claude not found",
@@ -34,6 +37,7 @@ async function checkGh() {
34
37
  const proc = await execa("gh", ["--version"], { reject: false, timeout: 1e4 });
35
38
  if (proc.exitCode !== 0) {
36
39
  return {
40
+ key: "gh",
37
41
  name: "gh CLI",
38
42
  passed: false,
39
43
  detail: "gh command failed",
@@ -44,15 +48,17 @@ async function checkGh() {
44
48
  const auth = await execa("gh", ["auth", "status"], { reject: false, timeout: 1e4 });
45
49
  if (auth.exitCode !== 0) {
46
50
  return {
51
+ key: "gh",
47
52
  name: "gh CLI",
48
53
  passed: false,
49
54
  detail: `${version} (not authenticated)`,
50
55
  hint: "Run: gh auth login"
51
56
  };
52
57
  }
53
- return { name: "gh CLI", passed: true, detail: `${version} (authenticated)` };
58
+ return { key: "gh", name: "gh CLI", passed: true, detail: `${version} (authenticated)` };
54
59
  } catch {
55
60
  return {
61
+ key: "gh",
56
62
  name: "gh CLI",
57
63
  passed: false,
58
64
  detail: "gh not found",
@@ -65,6 +71,7 @@ async function checkGit() {
65
71
  const proc = await execa("git", ["--version"], { reject: false, timeout: 1e4 });
66
72
  if (proc.exitCode !== 0) {
67
73
  return {
74
+ key: "git",
68
75
  name: "git",
69
76
  passed: false,
70
77
  detail: "git command failed",
@@ -78,15 +85,17 @@ async function checkGit() {
78
85
  });
79
86
  if (repo.exitCode !== 0) {
80
87
  return {
88
+ key: "git",
81
89
  name: "git",
82
90
  passed: false,
83
91
  detail: `${version} (not a git repository)`,
84
92
  hint: "Run this command inside a git repository"
85
93
  };
86
94
  }
87
- return { name: "git", passed: true, detail: `${version} (inside repo)` };
95
+ return { key: "git", name: "git", passed: true, detail: `${version} (inside repo)` };
88
96
  } catch {
89
97
  return {
98
+ key: "git",
90
99
  name: "git",
91
100
  passed: false,
92
101
  detail: "git not found",
@@ -94,20 +103,23 @@ async function checkGit() {
94
103
  };
95
104
  }
96
105
  }
97
- async function runInit() {
106
+ async function runInit(source = "github") {
98
107
  printInfo("Checking prerequisites...\n");
99
108
  const results = await Promise.all([checkClaude(), checkGh(), checkGit()]);
109
+ const isBlocking = (r) => r.key !== "gh" || source === "github";
100
110
  for (const r of results) {
101
111
  if (r.passed) {
102
112
  printSuccess(`${r.name}: ${r.detail}`);
103
- } else {
113
+ } else if (isBlocking(r)) {
104
114
  printError(`${r.name}: ${r.detail}`);
105
115
  if (r.hint) {
106
116
  console.log(` ${r.hint}`);
107
117
  }
118
+ } else {
119
+ printWarning(`${r.name}: ${r.detail} (not required for ${source} issues)`);
108
120
  }
109
121
  }
110
- const allPassed = results.every((r) => r.passed);
122
+ const allPassed = results.filter(isBlocking).every((r) => r.passed);
111
123
  console.log("");
112
124
  if (allPassed) {
113
125
  printSuccess("All prerequisites met. Ready to run the pipeline.");
@@ -120,4 +132,4 @@ async function runInit() {
120
132
  export {
121
133
  runInit
122
134
  };
123
- //# sourceMappingURL=chunk-VM7C7L3A.js.map
135
+ //# sourceMappingURL=chunk-YM3G5BLR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["import { execa } from 'execa';\nimport type { IssueSource } from '../issues/types.js';\nimport { printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\n\n/** Stable identifier of a check, used to decide whether it blocks. */\ntype CheckKey = 'claude' | 'gh' | 'git';\n\ninterface CheckResult {\n key: CheckKey;\n name: string;\n passed: boolean;\n detail: string;\n hint?: string;\n}\n\nasync function checkClaude(): Promise<CheckResult> {\n try {\n const proc = await execa('claude', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode === 0) {\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n return { key: 'claude', name: 'claude CLI', passed: true, detail: version };\n }\n return {\n key: 'claude',\n name: 'claude CLI',\n passed: false,\n detail: 'claude command failed',\n hint: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code',\n };\n } catch {\n return {\n key: 'claude',\n name: 'claude CLI',\n passed: false,\n detail: 'claude not found',\n hint: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code',\n };\n }\n}\n\nasync function checkGh(): Promise<CheckResult> {\n try {\n const proc = await execa('gh', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n key: 'gh',\n name: 'gh CLI',\n passed: false,\n detail: 'gh command failed',\n hint: 'Install GitHub CLI: https://cli.github.com/',\n };\n }\n const version = proc.stdout?.toString().split('\\n')[0]?.trim() ?? 'unknown';\n\n // Check auth status\n const auth = await execa('gh', ['auth', 'status'], { reject: false, timeout: 10_000 });\n if (auth.exitCode !== 0) {\n return {\n key: 'gh',\n name: 'gh CLI',\n passed: false,\n detail: `${version} (not authenticated)`,\n hint: 'Run: gh auth login',\n };\n }\n return { key: 'gh', name: 'gh CLI', passed: true, detail: `${version} (authenticated)` };\n } catch {\n return {\n key: 'gh',\n name: 'gh CLI',\n passed: false,\n detail: 'gh not found',\n hint: 'Install GitHub CLI: https://cli.github.com/',\n };\n }\n}\n\nasync function checkGit(): Promise<CheckResult> {\n try {\n const proc = await execa('git', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n key: 'git',\n name: 'git',\n passed: false,\n detail: 'git command failed',\n hint: 'Install git: https://git-scm.com/',\n };\n }\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n\n // Check if current directory is a git repo\n const repo = await execa('git', ['rev-parse', '--is-inside-work-tree'], {\n reject: false,\n timeout: 5_000,\n });\n if (repo.exitCode !== 0) {\n return {\n key: 'git',\n name: 'git',\n passed: false,\n detail: `${version} (not a git repository)`,\n hint: 'Run this command inside a git repository',\n };\n }\n return { key: 'git', name: 'git', passed: true, detail: `${version} (inside repo)` };\n } catch {\n return {\n key: 'git',\n name: 'git',\n passed: false,\n detail: 'git not found',\n hint: 'Install git: https://git-scm.com/',\n };\n }\n}\n\n/**\n * Verify the prerequisites of the pipeline.\n *\n * `source` is the Issue origin the run is headed for. `gh` only blocks when\n * that origin is GitHub: no other origin shells out to it, so a missing or\n * unauthenticated gh is reported as a warning instead of failing the\n * environment. `claude` and `git` stay blocking for every origin, and the\n * default ('github') keeps the previous behaviour byte for byte.\n */\nexport async function runInit(source: IssueSource = 'github'): Promise<number> {\n printInfo('Checking prerequisites...\\n');\n\n const results = await Promise.all([checkClaude(), checkGh(), checkGit()]);\n const isBlocking = (r: CheckResult): boolean => r.key !== 'gh' || source === 'github';\n\n for (const r of results) {\n if (r.passed) {\n printSuccess(`${r.name}: ${r.detail}`);\n } else if (isBlocking(r)) {\n printError(`${r.name}: ${r.detail}`);\n if (r.hint) {\n console.log(` ${r.hint}`);\n }\n } else {\n printWarning(`${r.name}: ${r.detail} (not required for ${source} issues)`);\n }\n }\n\n const allPassed = results.filter(isBlocking).every((r) => r.passed);\n console.log('');\n if (allPassed) {\n printSuccess('All prerequisites met. Ready to run the pipeline.');\n } else {\n printError('Some prerequisites are missing. Please fix the issues above.');\n }\n\n return allPassed ? 0 : 1;\n}\n"],"mappings":"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","names":[]}