issue-flow 0.7.1 → 0.7.2

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 (58) hide show
  1. package/README.md +11 -7
  2. package/dist/{analyze-R25HDGBJ.js → analyze-XMEULTT2.js} +10 -10
  3. package/dist/analyze-XMEULTT2.js.map +1 -0
  4. package/dist/{chunk-23WOEBGN.js → chunk-6UBA7CSJ.js} +21 -18
  5. package/dist/chunk-6UBA7CSJ.js.map +1 -0
  6. package/dist/{chunk-ZAEBDO3Q.js → chunk-CYU6RSDJ.js} +13 -10
  7. package/dist/chunk-CYU6RSDJ.js.map +1 -0
  8. package/dist/{chunk-RN2FAMUU.js → chunk-DKMBLNGO.js} +70 -21
  9. package/dist/chunk-DKMBLNGO.js.map +1 -0
  10. package/dist/{chunk-2AVZZAC6.js → chunk-G3TJHMAS.js} +331 -26
  11. package/dist/chunk-G3TJHMAS.js.map +1 -0
  12. package/dist/{chunk-TCOS2KFB.js → chunk-GOZT2YP5.js} +18 -15
  13. package/dist/chunk-GOZT2YP5.js.map +1 -0
  14. package/dist/{chunk-EM43T462.js → chunk-MWUF4LMF.js} +8 -8
  15. package/dist/chunk-MWUF4LMF.js.map +1 -0
  16. package/dist/{chunk-WHIBTNYI.js → chunk-PRVWJZV4.js} +2 -2
  17. package/dist/{chunk-HJ7JT777.js → chunk-RGCU2KE6.js} +8 -8
  18. package/dist/chunk-RGCU2KE6.js.map +1 -0
  19. package/dist/{chunk-YGEWIEAI.js → chunk-WQ4SUZ4P.js} +3 -3
  20. package/dist/{chunk-ASMCLXQ2.js → chunk-WUITOU5B.js} +10 -10
  21. package/dist/chunk-WUITOU5B.js.map +1 -0
  22. package/dist/cli.js +11 -11
  23. package/dist/{config-SJAQMFWB.js → config-2NDV7Y72.js} +2 -2
  24. package/dist/{execute-VZ7SHLP4.js → execute-BOQPTRH4.js} +4 -4
  25. package/dist/{generate-LUK3XTIP.js → generate-YEQRH36H.js} +10 -6
  26. package/dist/generate-YEQRH36H.js.map +1 -0
  27. package/dist/{plan-R34SAWRK.js → plan-4I47CQM5.js} +5 -5
  28. package/dist/{pr-P4BDQXUQ.js → pr-F672VPMM.js} +5 -5
  29. package/dist/pr-review-GKP4TJRJ.js +15 -0
  30. package/dist/{prd-RYLULM75.js → prd-3QME4DYX.js} +5 -5
  31. package/dist/review-RG2UIPOB.js +13 -0
  32. package/dist/{run-SDRQDMSZ.js → run-EFXDXSB4.js} +26 -21
  33. package/dist/run-EFXDXSB4.js.map +1 -0
  34. package/package.json +1 -1
  35. package/prompts/generate.md +1 -1
  36. package/prompts/prd.md +1 -1
  37. package/dist/analyze-R25HDGBJ.js.map +0 -1
  38. package/dist/chunk-23WOEBGN.js.map +0 -1
  39. package/dist/chunk-2AVZZAC6.js.map +0 -1
  40. package/dist/chunk-ASMCLXQ2.js.map +0 -1
  41. package/dist/chunk-EM43T462.js.map +0 -1
  42. package/dist/chunk-HJ7JT777.js.map +0 -1
  43. package/dist/chunk-RN2FAMUU.js.map +0 -1
  44. package/dist/chunk-TCOS2KFB.js.map +0 -1
  45. package/dist/chunk-ZAEBDO3Q.js.map +0 -1
  46. package/dist/generate-LUK3XTIP.js.map +0 -1
  47. package/dist/pr-review-TOSBE6ZS.js +0 -15
  48. package/dist/review-ZSC5ELQA.js +0 -13
  49. package/dist/run-SDRQDMSZ.js.map +0 -1
  50. /package/dist/{chunk-WHIBTNYI.js.map → chunk-PRVWJZV4.js.map} +0 -0
  51. /package/dist/{chunk-YGEWIEAI.js.map → chunk-WQ4SUZ4P.js.map} +0 -0
  52. /package/dist/{config-SJAQMFWB.js.map → config-2NDV7Y72.js.map} +0 -0
  53. /package/dist/{execute-VZ7SHLP4.js.map → execute-BOQPTRH4.js.map} +0 -0
  54. /package/dist/{plan-R34SAWRK.js.map → plan-4I47CQM5.js.map} +0 -0
  55. /package/dist/{pr-P4BDQXUQ.js.map → pr-F672VPMM.js.map} +0 -0
  56. /package/dist/{pr-review-TOSBE6ZS.js.map → pr-review-GKP4TJRJ.js.map} +0 -0
  57. /package/dist/{prd-RYLULM75.js.map → prd-3QME4DYX.js.map} +0 -0
  58. /package/dist/{review-ZSC5ELQA.js.map → review-RG2UIPOB.js.map} +0 -0
@@ -15,6 +15,12 @@ import {
15
15
  import { createInterface } from "readline";
16
16
  import chalk from "chalk";
17
17
  import { execa } from "execa";
18
+ function pushRepeatedFlag(args, flag, values) {
19
+ if (!values || values.length === 0) return;
20
+ for (const value of values) {
21
+ args.push(flag, value);
22
+ }
23
+ }
18
24
  function getToolContext(name, input) {
19
25
  switch (name) {
20
26
  case "Read":
@@ -87,7 +93,8 @@ function printStreamEvent(line, state, onOutput) {
87
93
  }
88
94
  }
89
95
  }
90
- async function runHeadlessVerbose(prompt, maxTurns, timeout, allowedTools, statusMessage, onOutput) {
96
+ async function runHeadlessVerbose(options) {
97
+ const { prompt, maxTurns, timeout, allowedTools, addDirs, statusMessage, onOutput } = options;
91
98
  const icons = getIcons();
92
99
  const colored = useColor();
93
100
  const emit = onOutput ?? ((msg) => process.stderr.write(`${msg}
@@ -108,11 +115,8 @@ async function runHeadlessVerbose(prompt, maxTurns, timeout, allowedTools, statu
108
115
  "--max-turns",
109
116
  String(maxTurns)
110
117
  ];
111
- if (allowedTools && allowedTools.length > 0) {
112
- for (const tool of allowedTools) {
113
- args.push("--allowedTools", tool);
114
- }
115
- }
118
+ pushRepeatedFlag(args, "--allowedTools", allowedTools);
119
+ pushRepeatedFlag(args, "--add-dir", addDirs);
116
120
  const subprocess = execa("claude", args, {
117
121
  stdin: "ignore",
118
122
  reject: false,
@@ -174,19 +178,21 @@ async function runHeadless(options) {
174
178
  timeout = 3e5,
175
179
  outputFormat = "json",
176
180
  allowedTools,
181
+ addDirs,
177
182
  statusMessage,
178
183
  onOutput
179
184
  } = options;
180
185
  if (isVerbose()) {
181
186
  const effectiveOnOutput = onOutput ?? getOutputCallback();
182
- return runHeadlessVerbose(
187
+ return runHeadlessVerbose({
183
188
  prompt,
184
189
  maxTurns,
185
190
  timeout,
186
191
  allowedTools,
192
+ addDirs,
187
193
  statusMessage,
188
- effectiveOnOutput
189
- );
194
+ onOutput: effectiveOnOutput
195
+ });
190
196
  }
191
197
  const spinner = statusMessage ? createSpinner(statusMessage).start() : null;
192
198
  let timer = null;
@@ -203,11 +209,8 @@ async function runHeadless(options) {
203
209
  "--max-turns",
204
210
  String(maxTurns)
205
211
  ];
206
- if (allowedTools && allowedTools.length > 0) {
207
- for (const tool of allowedTools) {
208
- args.push("--allowedTools", tool);
209
- }
210
- }
212
+ pushRepeatedFlag(args, "--allowedTools", allowedTools);
213
+ pushRepeatedFlag(args, "--add-dir", addDirs);
211
214
  try {
212
215
  const proc = await execa("claude", args, {
213
216
  stdin: "ignore",
@@ -280,4 +283,4 @@ async function runHeadless(options) {
280
283
  export {
281
284
  runHeadless
282
285
  };
283
- //# sourceMappingURL=chunk-TCOS2KFB.js.map
286
+ //# sourceMappingURL=chunk-GOZT2YP5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/headless.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 /**\n * Extra directories the headless session may read from and write to, passed\n * through as `--add-dir`. Required whenever a prompt points at a path outside\n * the working tree (e.g. the global issue storage under `~/.issue-flow`),\n * since `claude -p` otherwise refuses the access.\n */\n addDirs?: 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/* ── argument helpers ───────────────────────────────────────────────────── */\n\n/**\n * Append one `<flag> <value>` pair per value. An empty or absent list leaves\n * `args` untouched, so callers that pass nothing keep the exact same argv.\n */\nfunction pushRepeatedFlag(args: string[], flag: string, values: string[] | undefined): void {\n if (!values || values.length === 0) return;\n for (const value of values) {\n args.push(flag, value);\n }\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(options: {\n prompt: string;\n maxTurns: number;\n timeout: number;\n allowedTools?: string[];\n addDirs?: string[];\n statusMessage?: string;\n onOutput?: (line: string) => void;\n}): Promise<HeadlessResult> {\n const { prompt, maxTurns, timeout, allowedTools, addDirs, statusMessage, onOutput } = options;\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 pushRepeatedFlag(args, '--allowedTools', allowedTools);\n pushRepeatedFlag(args, '--add-dir', addDirs);\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 addDirs,\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 addDirs,\n statusMessage,\n onOutput: 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 pushRepeatedFlag(args, '--allowedTools', allowedTools);\n pushRepeatedFlag(args, '--add-dir', addDirs);\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"],"mappings":"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","names":["elapsed","dur"]}
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  issuePlaceholders,
4
4
  resolveCommandIssue
5
- } from "./chunk-RN2FAMUU.js";
5
+ } from "./chunk-DKMBLNGO.js";
6
6
  import {
7
7
  runHeadless
8
- } from "./chunk-TCOS2KFB.js";
8
+ } from "./chunk-GOZT2YP5.js";
9
9
  import {
10
10
  applyPlaceholders,
11
11
  loadPrompt
12
12
  } from "./chunk-SLHZPB3C.js";
13
13
  import {
14
- getIssueDir
15
- } from "./chunk-2AVZZAC6.js";
14
+ resolveIssuePaths
15
+ } from "./chunk-G3TJHMAS.js";
16
16
  import {
17
17
  getGlobalTimeout,
18
18
  loadTaskPlan,
@@ -22,7 +22,6 @@ import {
22
22
  } from "./chunk-JNEHVQH5.js";
23
23
 
24
24
  // src/commands/review.ts
25
- import { join } from "path";
26
25
  function parseReviewResult(output) {
27
26
  const match = output.match(/<review-result>([\s\S]*?)<\/review-result>/);
28
27
  if (!match) {
@@ -47,8 +46,8 @@ function parseReviewResult(output) {
47
46
  }
48
47
  async function runReview(issue, resolvedIssue) {
49
48
  const issueNumber = issue.replace(/^#/, "");
50
- const issueDir = await getIssueDir(issueNumber);
51
- const tasksPath = join(issueDir, "tasks.json");
49
+ const paths = await resolveIssuePaths(issueNumber);
50
+ const tasksPath = paths.tasksFile;
52
51
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
53
52
  if (!resolution.ok) {
54
53
  return resolution.code;
@@ -65,6 +64,7 @@ async function runReview(issue, resolvedIssue) {
65
64
  timeout: getGlobalTimeout() ?? 3e5,
66
65
  outputFormat: "text",
67
66
  allowedTools: ["Bash", "Read", "Glob", "Grep"],
67
+ addDirs: [paths.issueDir],
68
68
  statusMessage: `Reviewing issue #${issueNumber} resolution...`
69
69
  });
70
70
  if (!result.success) {
@@ -100,4 +100,4 @@ async function runReview(issue, resolvedIssue) {
100
100
  export {
101
101
  runReview
102
102
  };
103
- //# sourceMappingURL=chunk-EM43T462.js.map
103
+ //# sourceMappingURL=chunk-MWUF4LMF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/review.ts"],"sourcesContent":["import { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport interface ReviewResult {\n status: 'PASS' | 'FAIL';\n findings?: string;\n}\n\n/**\n * Parse the <review-result> block from headless output.\n */\nfunction parseReviewResult(output: string): ReviewResult {\n const match = output.match(/<review-result>([\\s\\S]*?)<\\/review-result>/);\n if (!match) {\n // Try to detect PASS/FAIL from the raw output\n if (/STATUS:\\s*PASS/i.test(output)) {\n return { status: 'PASS' };\n }\n if (/STATUS:\\s*FAIL/i.test(output)) {\n const findingsMatch = output.match(/FINDINGS:([\\s\\S]*?)(?:$|<\\/)/);\n return { status: 'FAIL', findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings' };\n }\n // Default to PASS if no explicit status found and no errors\n return { status: 'PASS' };\n }\n\n const block = match[1];\n if (/STATUS:\\s*PASS/i.test(block)) {\n return { status: 'PASS' };\n }\n\n const findingsMatch = block.match(/FINDINGS:([\\s\\S]*)/);\n return {\n status: 'FAIL',\n findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings',\n };\n}\n\nexport async function runReview(issue: string, resolvedIssue?: ResolvedIssue): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n const tasksPath = paths.tasksFile;\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n const template = await loadPrompt('review');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __TASKS_PATH__: tasksPath,\n ...issuePlaceholders(resolution.resolved),\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [paths.issueDir],\n statusMessage: `Reviewing issue #${issueNumber} resolution...`,\n });\n\n if (!result.success) {\n printError(`Review failed: ${result.error}`);\n return 1;\n }\n\n const review = parseReviewResult(result.result);\n\n if (review.status === 'PASS') {\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.reviewCompleted = true;\n plan.lastReviewFindings = null;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n printSuccess('Review PASSED — implementation meets acceptance criteria');\n return 0;\n }\n\n printError('Review FAILED — issues found:');\n if (review.findings) {\n console.log(review.findings);\n }\n\n // Persist the findings so a correction cycle can act on them: without this,\n // a re-run of execute sees every userStories[].passes already true and\n // exits immediately without attempting any fix (see engine.ts's\n // \"already complete\" guards).\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.reviewCompleted = false;\n plan.lastReviewFindings = review.findings ?? 'Unknown findings';\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n return 1;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAiBA,SAAS,kBAAkB,QAA8B;AACvD,QAAM,QAAQ,OAAO,MAAM,4CAA4C;AACvE,MAAI,CAAC,OAAO;AAEV,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,aAAO,EAAE,QAAQ,OAAO;AAAA,IAC1B;AACA,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,YAAMA,iBAAgB,OAAO,MAAM,8BAA8B;AACjE,aAAO,EAAE,QAAQ,QAAQ,UAAUA,iBAAgB,CAAC,GAAG,KAAK,KAAK,mBAAmB;AAAA,IACtF;AAEA,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,QAAQ,MAAM,CAAC;AACrB,MAAI,kBAAkB,KAAK,KAAK,GAAG;AACjC,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,gBAAgB,MAAM,MAAM,oBAAoB;AACtD,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU,gBAAgB,CAAC,GAAG,KAAK,KAAK;AAAA,EAC1C;AACF;AAEA,eAAsB,UAAU,OAAe,eAAgD;AAC7F,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,QAAQ,MAAM,kBAAkB,WAAW;AACjD,QAAM,YAAY,MAAM;AAExB,QAAM,aAAa,MAAM,oBAAoB,aAAa,aAAa;AACvE,MAAI,CAAC,WAAW,IAAI;AAClB,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM,WAAW,MAAM,WAAW,QAAQ;AAC1C,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,GAAG,kBAAkB,WAAW,QAAQ;AAAA,EAC1C,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7C,SAAS,CAAC,MAAM,QAAQ;AAAA,IACxB,eAAe,oBAAoB,WAAW;AAAA,EAChD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,kBAAkB,OAAO,KAAK,EAAE;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,kBAAkB,OAAO,MAAM;AAE9C,MAAI,OAAO,WAAW,QAAQ;AAE5B,QAAI;AACF,YAAM,OAAO,MAAM,aAAa,SAAS;AACzC,WAAK,SAAS,kBAAkB;AAChC,WAAK,qBAAqB;AAC1B,YAAM,aAAa,WAAW,IAAI;AAAA,IACpC,QAAQ;AAAA,IAER;AAEA,iBAAa,+DAA0D;AACvE,WAAO;AAAA,EACT;AAEA,aAAW,oCAA+B;AAC1C,MAAI,OAAO,UAAU;AACnB,YAAQ,IAAI,OAAO,QAAQ;AAAA,EAC7B;AAMA,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,kBAAkB;AAChC,SAAK,qBAAqB,OAAO,YAAY;AAC7C,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;","names":["findingsMatch"]}
@@ -4,7 +4,7 @@ import {
4
4
  getCommitsSince,
5
5
  getCurrentBranch,
6
6
  run
7
- } from "./chunk-2AVZZAC6.js";
7
+ } from "./chunk-G3TJHMAS.js";
8
8
  import {
9
9
  NullPublisher,
10
10
  isoNow
@@ -69,4 +69,4 @@ export {
69
69
  listPullRequests,
70
70
  publishGitState
71
71
  };
72
- //# sourceMappingURL=chunk-WHIBTNYI.js.map
72
+ //# sourceMappingURL=chunk-PRVWJZV4.js.map
@@ -5,20 +5,20 @@ import {
5
5
  import {
6
6
  issuePlaceholders,
7
7
  resolveCommandIssue
8
- } from "./chunk-RN2FAMUU.js";
8
+ } from "./chunk-DKMBLNGO.js";
9
9
  import {
10
10
  isTransientFailure
11
11
  } from "./chunk-HDV3YDTG.js";
12
12
  import {
13
13
  runHeadless
14
- } from "./chunk-TCOS2KFB.js";
14
+ } from "./chunk-GOZT2YP5.js";
15
15
  import {
16
16
  applyPlaceholders,
17
17
  loadPrompt
18
18
  } from "./chunk-SLHZPB3C.js";
19
19
  import {
20
- getIssueDir
21
- } from "./chunk-2AVZZAC6.js";
20
+ resolveIssuePaths
21
+ } from "./chunk-G3TJHMAS.js";
22
22
  import {
23
23
  getGlobalTimeout,
24
24
  isoNow,
@@ -29,7 +29,6 @@ import {
29
29
  } from "./chunk-JNEHVQH5.js";
30
30
 
31
31
  // src/commands/pr.ts
32
- import { join } from "path";
33
32
  import { execa } from "execa";
34
33
  function parsePrUrl(output) {
35
34
  const match = output.match(/(https:\/\/github\.com\/[^\s]+\/pull\/\d+)/);
@@ -47,8 +46,8 @@ function issueClosesLine(issue, fallbackId) {
47
46
  }
48
47
  async function runPr(issue, resolvedIssue) {
49
48
  const issueNumber = issue.replace(/^#/, "");
50
- const issueDir = await getIssueDir(issueNumber);
51
- const tasksPath = join(issueDir, "tasks.json");
49
+ const paths = await resolveIssuePaths(issueNumber);
50
+ const tasksPath = paths.tasksFile;
52
51
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
53
52
  if (!resolution.ok) {
54
53
  return resolution.code;
@@ -83,6 +82,7 @@ async function runPr(issue, resolvedIssue) {
83
82
  timeout: getGlobalTimeout() ?? 3e5,
84
83
  outputFormat: "text",
85
84
  allowedTools: ["Bash", "Read", "Glob", "Grep"],
85
+ addDirs: [paths.issueDir],
86
86
  statusMessage: `Creating PR for issue #${issueNumber}...`
87
87
  });
88
88
  if (!result.success) {
@@ -127,4 +127,4 @@ async function runPr(issue, resolvedIssue) {
127
127
  export {
128
128
  runPr
129
129
  };
130
- //# sourceMappingURL=chunk-HJ7JT777.js.map
130
+ //# sourceMappingURL=chunk-RGCU2KE6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/pr.ts"],"sourcesContent":["import { execa } from 'execa';\nimport { runHeadless } from '../core/headless.js';\nimport { runPhaseWithRetry } from '../core/phase-runner.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { Issue, ResolvedIssue } from '../issues/types.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\n/**\n * Extract a PR URL from headless output.\n */\nfunction parsePrUrl(output: string): string | null {\n const match = output.match(/(https:\\/\\/github\\.com\\/[^\\s]+\\/pull\\/\\d+)/);\n return match?.[1] ?? null;\n}\n\n/**\n * The numeric id inside a PR URL, so later phases (`pr-review`) address the\n * Pull Request without querying GitHub again.\n */\nfunction parsePrNumber(url: string): number | null {\n const match = url.match(/\\/pull\\/(\\d+)/);\n return match?.[1] === undefined ? null : Number(match[1]);\n}\n\n/**\n * The `Closes #N` line for the PR body, empty when the Issue has no remote\n * counterpart: GitHub only understands the reference for Issues it hosts, and\n * an invented `#N` would silently point at an unrelated Issue.\n */\nfunction issueClosesLine(issue: Issue, fallbackId: string): string {\n if (issue.remoteRef === null) {\n return '';\n }\n return `Closes #${issue.number ?? fallbackId}`;\n}\n\nexport async function runPr(issue: string, resolvedIssue?: ResolvedIssue): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n const tasksPath = paths.tasksFile;\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n // Get current branch\n let branchName: string;\n try {\n const proc = await execa('git', ['branch', '--show-current'], { reject: false });\n branchName = proc.stdout?.toString().trim() ?? '';\n if (!branchName) {\n printError('Could not determine current branch');\n return 1;\n }\n } catch {\n printError('Failed to get current branch');\n return 1;\n }\n\n const template = await loadPrompt('pr');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __BRANCH_NAME__: branchName,\n __TASKS_PATH__: tasksPath,\n __ISSUE_CLOSES__: issueClosesLine(resolution.resolved.issue, issueNumber),\n ...issuePlaceholders(resolution.resolved),\n });\n\n let headlessOutput = '';\n\n const outcome = await runPhaseWithRetry({\n phase: 'pr',\n attempt: async () => {\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [paths.issueDir],\n statusMessage: `Creating PR for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `PR creation failed: ${result.error}`,\n };\n }\n\n headlessOutput = result.result;\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `PR creation failed for issue #${issueNumber}`);\n return 1;\n }\n\n const prUrl = parsePrUrl(headlessOutput);\n\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prCreated = true;\n const prNumber = prUrl === null ? null : parsePrNumber(prUrl);\n // Without a URL the PR is still assumed created (the phase succeeded), but\n // there is nothing trustworthy to record: an invented number would send\n // `pr-review` at an unrelated Pull Request.\n if (prUrl !== null && prNumber !== null) {\n plan.pullRequest = {\n number: prNumber,\n url: prUrl,\n headBranch: branchName,\n createdAt: isoNow(),\n };\n }\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n if (prUrl) {\n printSuccess(`PR created: ${prUrl}`);\n } else {\n printSuccess('PR creation completed');\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-HDV3YDTG.js";
7
7
  import {
8
8
  publishGitState
9
- } from "./chunk-WHIBTNYI.js";
9
+ } from "./chunk-PRVWJZV4.js";
10
10
  import {
11
11
  applyPlaceholders,
12
12
  loadPrompt
@@ -15,7 +15,7 @@ import {
15
15
  createConfig,
16
16
  resolvePaths,
17
17
  validateDependencies
18
- } from "./chunk-2AVZZAC6.js";
18
+ } from "./chunk-G3TJHMAS.js";
19
19
  import {
20
20
  allStoriesPass,
21
21
  clearLastError,
@@ -541,4 +541,4 @@ export {
541
541
  printRunSummary,
542
542
  runExecute
543
543
  };
544
- //# sourceMappingURL=chunk-YGEWIEAI.js.map
544
+ //# sourceMappingURL=chunk-WQ4SUZ4P.js.map
@@ -6,20 +6,20 @@ import {
6
6
  import {
7
7
  issuePlaceholders,
8
8
  resolveCommandIssue
9
- } from "./chunk-RN2FAMUU.js";
9
+ } from "./chunk-DKMBLNGO.js";
10
10
  import {
11
11
  isTransientFailure
12
12
  } from "./chunk-HDV3YDTG.js";
13
13
  import {
14
14
  runHeadless
15
- } from "./chunk-TCOS2KFB.js";
15
+ } from "./chunk-GOZT2YP5.js";
16
16
  import {
17
17
  applyPlaceholders,
18
18
  loadPrompt
19
19
  } from "./chunk-SLHZPB3C.js";
20
20
  import {
21
- getIssueDir
22
- } from "./chunk-2AVZZAC6.js";
21
+ resolveIssuePaths
22
+ } from "./chunk-G3TJHMAS.js";
23
23
  import {
24
24
  getGlobalTimeout,
25
25
  loadTaskPlan,
@@ -31,15 +31,14 @@ import {
31
31
 
32
32
  // src/commands/plan.ts
33
33
  import { mkdir, readFile } from "fs/promises";
34
- import { join } from "path";
35
34
  async function runPlan(issue, resolvedIssue) {
36
35
  const issueNumber = issue.replace(/^#/, "");
37
- const issueDir = await getIssueDir(issueNumber);
36
+ const paths = await resolveIssuePaths(issueNumber);
38
37
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
39
38
  if (!resolution.ok) {
40
39
  return resolution.code;
41
40
  }
42
- const prdPath = join(issueDir, "prd.md");
41
+ const prdPath = paths.prdFile;
43
42
  let prdContent;
44
43
  try {
45
44
  prdContent = await readFile(prdPath, "utf-8");
@@ -47,7 +46,7 @@ async function runPlan(issue, resolvedIssue) {
47
46
  printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);
48
47
  return 1;
49
48
  }
50
- const tasksPath = join(issueDir, "tasks.json");
49
+ const tasksPath = paths.tasksFile;
51
50
  const template = await loadPrompt("plan");
52
51
  const prompt = applyPlaceholders(template, {
53
52
  __ISSUE_NUMBER__: issueNumber,
@@ -55,7 +54,7 @@ async function runPlan(issue, resolvedIssue) {
55
54
  __TASKS_PATH__: tasksPath,
56
55
  ...issuePlaceholders(resolution.resolved)
57
56
  });
58
- await mkdir(issueDir, { recursive: true });
57
+ await mkdir(paths.issueDir, { recursive: true });
59
58
  const outcome = await runPhaseWithRetry({
60
59
  phase: "plan",
61
60
  attempt: async () => {
@@ -65,6 +64,7 @@ async function runPlan(issue, resolvedIssue) {
65
64
  timeout: getGlobalTimeout() ?? 3e5,
66
65
  outputFormat: "text",
67
66
  allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
67
+ addDirs: [paths.issueDir],
68
68
  statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`
69
69
  });
70
70
  if (!result.success) {
@@ -113,4 +113,4 @@ ${issues}`
113
113
  export {
114
114
  runPlan
115
115
  };
116
- //# sourceMappingURL=chunk-ASMCLXQ2.js.map
116
+ //# sourceMappingURL=chunk-WUITOU5B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/plan.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { runHeadless } from '../core/headless.js';\nimport { readFileWithGrace, runPhaseWithRetry } from '../core/phase-runner.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { taskPlanSchema } from '../schemas.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\nexport async function runPlan(issue: string, resolvedIssue?: ResolvedIssue): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n // Read the PRD\n const prdPath = paths.prdFile;\n let prdContent: string;\n try {\n prdContent = await readFile(prdPath, 'utf-8');\n } catch {\n printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);\n return 1;\n }\n\n const tasksPath = paths.tasksFile;\n\n const template = await loadPrompt('plan');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __PRD_CONTENT__: prdContent,\n __TASKS_PATH__: tasksPath,\n ...issuePlaceholders(resolution.resolved),\n });\n\n await mkdir(paths.issueDir, { recursive: true });\n\n const outcome = await runPhaseWithRetry({\n phase: 'plan',\n attempt: async () => {\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n addDirs: [paths.issueDir],\n statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `Task plan generation failed: ${result.error}`,\n };\n }\n\n // Verify the file was created, tolerating a brief FS-visibility lag.\n let rawContent: string;\n try {\n rawContent = await readFileWithGrace(tasksPath);\n } catch {\n return { ok: false, transient: true, error: `tasks.json was not created at ${tasksPath}` };\n }\n\n // Validate JSON structure — a content defect, not a timing issue, but\n // still worth a bounded retry since it's a fresh Claude invocation.\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawContent);\n } catch {\n return { ok: false, transient: true, error: 'tasks.json contains invalid JSON' };\n }\n\n // Validate with zod schema\n const validation = taskPlanSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues\n .map((i) => ` - ${i.path.join('.')}: ${i.message}`)\n .join('\\n');\n return {\n ok: false,\n transient: true,\n error: `tasks.json does not match expected schema:\\n${issues}`,\n };\n }\n\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `Task plan generation failed for issue #${issueNumber}`);\n return 1;\n }\n\n // Ensure pipeline state reflects completion of this phase\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.jsonCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n\n printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,gBAAgB;AAahC,eAAsB,QAAQ,OAAe,eAAgD;AAC3F,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,QAAQ,MAAM,kBAAkB,WAAW;AAEjD,QAAM,aAAa,MAAM,oBAAoB,aAAa,aAAa;AACvE,MAAI,CAAC,WAAW,IAAI;AAClB,WAAO,WAAW;AAAA,EACpB;AAGA,QAAM,UAAU,MAAM;AACtB,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,SAAS,SAAS,OAAO;AAAA,EAC9C,QAAQ;AACN,eAAW,oBAAoB,OAAO,yBAAyB,WAAW,UAAU;AACpF,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,MAAM;AAExB,QAAM,WAAW,MAAM,WAAW,MAAM;AACxC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,GAAG,kBAAkB,WAAW,QAAQ;AAAA,EAC1C,CAAC;AAED,QAAM,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAE/C,QAAM,UAAU,MAAM,kBAAkB;AAAA,IACtC,OAAO;AAAA,IACP,SAAS,YAAY;AACnB,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B;AAAA,QACA,UAAU;AAAA,QACV,SAAS,iBAAiB,KAAK;AAAA,QAC/B,cAAc;AAAA,QACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,QACtD,SAAS,CAAC,MAAM,QAAQ;AAAA,QACxB,eAAe,0CAA0C,WAAW;AAAA,MACtE,CAAC;AAED,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,WAAW,mBAAmB,GAAG,OAAO,SAAS,EAAE;AAAA,UACnD,OAAO,gCAAgC,OAAO,KAAK;AAAA,QACrD;AAAA,MACF;AAGA,UAAI;AACJ,UAAI;AACF,qBAAa,MAAM,kBAAkB,SAAS;AAAA,MAChD,QAAQ;AACN,eAAO,EAAE,IAAI,OAAO,WAAW,MAAM,OAAO,iCAAiC,SAAS,GAAG;AAAA,MAC3F;AAIA,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,UAAU;AAAA,MAChC,QAAQ;AACN,eAAO,EAAE,IAAI,OAAO,WAAW,MAAM,OAAO,mCAAmC;AAAA,MACjF;AAGA,YAAM,aAAa,eAAe,UAAU,MAAM;AAClD,UAAI,CAAC,WAAW,SAAS;AACvB,cAAM,SAAS,WAAW,MAAM,OAC7B,IAAI,CAAC,MAAM,OAAO,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAClD,KAAK,IAAI;AACZ,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,WAAW;AAAA,UACX,OAAO;AAAA,EAA+C,MAAM;AAAA,QAC9D;AAAA,MACF;AAEA,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,QAAQ,IAAI;AACf,eAAW,QAAQ,SAAS,0CAA0C,WAAW,EAAE;AACnF,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,MAAM,aAAa,SAAS;AACzC,OAAK,SAAS,gBAAgB;AAC9B,QAAM,aAAa,WAAW,IAAI;AAElC,eAAa,sBAAsB,SAAS,KAAK,KAAK,YAAY,MAAM,WAAW;AACnF,SAAO;AACT;","names":[]}
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  setIssuesCliOverrides,
4
4
  setWebCliOverrides
5
- } from "./chunk-2AVZZAC6.js";
5
+ } from "./chunk-G3TJHMAS.js";
6
6
  import {
7
7
  printError,
8
8
  setGlobalTimeout,
@@ -159,7 +159,7 @@ withIssueOptions(
159
159
  program.command("init").description("Verify that all prerequisites (claude, gh, git) are met")
160
160
  )
161
161
  ).action(async () => {
162
- const { loadIssuesConfig } = await import("./config-SJAQMFWB.js");
162
+ const { loadIssuesConfig } = await import("./config-2NDV7Y72.js");
163
163
  const { runInit } = await import("./init-5KXCG36P.js");
164
164
  const { preferredProvider } = await loadIssuesConfig();
165
165
  const code = await runInit(preferredProvider);
@@ -178,7 +178,7 @@ withGlobalOptions(
178
178
  }
179
179
  throw error;
180
180
  }
181
- const { runGenerate } = await import("./generate-LUK3XTIP.js");
181
+ const { runGenerate } = await import("./generate-YEQRH36H.js");
182
182
  const code = await runGenerate(options.prompt, target);
183
183
  process.exit(code);
184
184
  });
@@ -202,7 +202,7 @@ withWebOptions(
202
202
  }
203
203
  throw error;
204
204
  }
205
- const { runPipeline } = await import("./run-SDRQDMSZ.js");
205
+ const { runPipeline } = await import("./run-EFXDXSB4.js");
206
206
  const code = await runPipeline(
207
207
  issue,
208
208
  options.mode,
@@ -218,7 +218,7 @@ withIssueOptions(
218
218
  program.command("analyze").description("Analyze an issue via Claude Code Headless").argument("<issue>", "Issue number")
219
219
  )
220
220
  ).action(async (issue) => {
221
- const { runAnalyze } = await import("./analyze-R25HDGBJ.js");
221
+ const { runAnalyze } = await import("./analyze-XMEULTT2.js");
222
222
  const code = await runAnalyze(issue);
223
223
  process.exit(code);
224
224
  });
@@ -227,7 +227,7 @@ withIssueOptions(
227
227
  program.command("prd").description("Generate a PRD from an analyzed issue via Claude Code Headless").argument("<issue>", "Issue number")
228
228
  )
229
229
  ).action(async (issue) => {
230
- const { runPrd } = await import("./prd-RYLULM75.js");
230
+ const { runPrd } = await import("./prd-3QME4DYX.js");
231
231
  const code = await runPrd(issue);
232
232
  process.exit(code);
233
233
  });
@@ -236,7 +236,7 @@ withIssueOptions(
236
236
  program.command("plan").description("Convert a PRD to a tasks.json task plan via Claude Code Headless").argument("<issue>", "Issue number")
237
237
  )
238
238
  ).action(async (issue) => {
239
- const { runPlan } = await import("./plan-R34SAWRK.js");
239
+ const { runPlan } = await import("./plan-4I47CQM5.js");
240
240
  const code = await runPlan(issue);
241
241
  process.exit(code);
242
242
  });
@@ -255,7 +255,7 @@ withWebOptions(
255
255
  ).action(
256
256
  async (positionalMaxIter, options) => {
257
257
  try {
258
- const { runExecute } = await import("./execute-VZ7SHLP4.js");
258
+ const { runExecute } = await import("./execute-BOQPTRH4.js");
259
259
  const code = await runExecute(positionalMaxIter, options);
260
260
  process.exit(code);
261
261
  } catch (error) {
@@ -269,7 +269,7 @@ withIssueOptions(
269
269
  program.command("review").description("Validate an issue resolution via Claude Code Headless").argument("<issue>", "Issue number")
270
270
  )
271
271
  ).action(async (issue) => {
272
- const { runReview } = await import("./review-ZSC5ELQA.js");
272
+ const { runReview } = await import("./review-RG2UIPOB.js");
273
273
  const code = await runReview(issue);
274
274
  process.exit(code);
275
275
  });
@@ -278,7 +278,7 @@ withIssueOptions(
278
278
  program.command("pr").description("Create a pull request via Claude Code Headless").argument("<issue>", "Issue number")
279
279
  )
280
280
  ).action(async (issue) => {
281
- const { runPr } = await import("./pr-P4BDQXUQ.js");
281
+ const { runPr } = await import("./pr-F672VPMM.js");
282
282
  const code = await runPr(issue);
283
283
  process.exit(code);
284
284
  });
@@ -289,7 +289,7 @@ withGlobalOptions(
289
289
  )
290
290
  ).action(
291
291
  async (pr, options) => {
292
- const { runPrReview } = await import("./pr-review-TOSBE6ZS.js");
292
+ const { runPrReview } = await import("./pr-review-GKP4TJRJ.js");
293
293
  const code = await runPrReview(pr, options);
294
294
  process.exit(code);
295
295
  }
@@ -15,7 +15,7 @@ import {
15
15
  setIssuesCliOverrides,
16
16
  setWebCliOverrides,
17
17
  validateDependencies
18
- } from "./chunk-2AVZZAC6.js";
18
+ } from "./chunk-G3TJHMAS.js";
19
19
  import "./chunk-JNEHVQH5.js";
20
20
  export {
21
21
  DEFAULTS,
@@ -34,4 +34,4 @@ export {
34
34
  setWebCliOverrides,
35
35
  validateDependencies
36
36
  };
37
- //# sourceMappingURL=config-SJAQMFWB.js.map
37
+ //# sourceMappingURL=config-2NDV7Y72.js.map
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runExecute
4
- } from "./chunk-YGEWIEAI.js";
4
+ } from "./chunk-WQ4SUZ4P.js";
5
5
  import "./chunk-HDV3YDTG.js";
6
- import "./chunk-WHIBTNYI.js";
6
+ import "./chunk-PRVWJZV4.js";
7
7
  import "./chunk-SLHZPB3C.js";
8
- import "./chunk-2AVZZAC6.js";
8
+ import "./chunk-G3TJHMAS.js";
9
9
  import "./chunk-JNEHVQH5.js";
10
10
  export {
11
11
  runExecute
12
12
  };
13
- //# sourceMappingURL=execute-VZ7SHLP4.js.map
13
+ //# sourceMappingURL=execute-BOQPTRH4.js.map
@@ -3,17 +3,18 @@ import {
3
3
  ensureProvidersRegistered,
4
4
  getProvider,
5
5
  localIssueRef
6
- } from "./chunk-RN2FAMUU.js";
6
+ } from "./chunk-DKMBLNGO.js";
7
7
  import {
8
8
  runHeadless
9
- } from "./chunk-TCOS2KFB.js";
9
+ } from "./chunk-GOZT2YP5.js";
10
10
  import {
11
11
  applyPlaceholders,
12
12
  loadPrompt
13
13
  } from "./chunk-SLHZPB3C.js";
14
14
  import {
15
- loadIssuesConfig
16
- } from "./chunk-2AVZZAC6.js";
15
+ loadIssuesConfig,
16
+ resolveProjectPaths
17
+ } from "./chunk-G3TJHMAS.js";
17
18
  import {
18
19
  getGlobalTimeout,
19
20
  isoNow,
@@ -66,9 +67,11 @@ function issueLocation(issue) {
66
67
  return issue.remoteRef ?? localIssueRef(issue.id);
67
68
  }
68
69
  async function draftIssue(promptText) {
70
+ const { issuesDir } = await resolveProjectPaths();
69
71
  const template = await loadPrompt("generate");
70
72
  const prompt = applyPlaceholders(template, {
71
- __USER_PROMPT__: promptText
73
+ __USER_PROMPT__: promptText,
74
+ __LOCAL_ISSUES_DIR__: issuesDir
72
75
  });
73
76
  const result = await runHeadless({
74
77
  prompt,
@@ -76,6 +79,7 @@ async function draftIssue(promptText) {
76
79
  timeout: getGlobalTimeout() ?? 3e5,
77
80
  outputFormat: "text",
78
81
  allowedTools: ["Bash", "Read", "Glob", "Grep"],
82
+ addDirs: [issuesDir],
79
83
  statusMessage: "Drafting issue..."
80
84
  });
81
85
  if (!result.success) {
@@ -142,4 +146,4 @@ async function runGenerate(promptText, target) {
142
146
  export {
143
147
  runGenerate
144
148
  };
145
- //# sourceMappingURL=generate-LUK3XTIP.js.map
149
+ //# sourceMappingURL=generate-YEQRH36H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/issues/draft.ts","../src/commands/generate.ts"],"sourcesContent":["import type { IssueDraft } from './types.js';\n\n/**\n * Structured Issue draft emitted by the `generate` prompt.\n *\n * The agent no longer creates the Issue itself: it returns the content and the\n * providers persist it, so the same draft can reach GitHub, the local files, or\n * both. Capturing the result therefore has to be exact — the old approach of\n * fishing an issue URL out of free-form prose only ever worked for GitHub.\n *\n * The block mirrors the `<issue-body>` convention already used by the phase\n * templates, which keeps a multi-line markdown body readable and free of the\n * escaping a JSON payload would demand.\n */\n\n/** Wrapper the agent must emit around the draft. */\nexport const DRAFT_TAG = 'issue-draft';\n\n/** Failure to capture a draft from the agent output. */\nexport class IssueDraftParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueDraftParseError';\n }\n}\n\n/**\n * Last occurrence of `<tag>...</tag>` in `source`, or `null`.\n *\n * The last one wins: an agent that shows the format before filling it in (or\n * restates the draft after a correction) would otherwise have its first,\n * throwaway attempt captured. The closing tag is searched from the end for the\n * same reason a body may legitimately quote one.\n */\nfunction extractTag(source: string, tag: string): string | null {\n const open = `<${tag}>`;\n const close = `</${tag}>`;\n\n const start = source.lastIndexOf(open);\n if (start === -1) return null;\n\n const end = source.lastIndexOf(close);\n if (end === -1 || end < start) return null;\n\n return source.slice(start + open.length, end);\n}\n\nfunction parseLabels(raw: string | null): string[] {\n if (raw === null) return [];\n return raw\n .split(',')\n .map((label) => label.trim())\n .filter((label) => label.length > 0 && label.toLowerCase() !== '(none)');\n}\n\n/**\n * Read the draft the agent produced.\n *\n * @throws IssueDraftParseError when the block is absent or has no usable\n * title/body, so a malformed answer fails loudly instead of creating an empty\n * Issue.\n */\nexport function parseIssueDraft(output: string): IssueDraft {\n const normalized = output.replace(/\\r\\n?/g, '\\n');\n const block = extractTag(normalized, DRAFT_TAG);\n\n if (block === null) {\n throw new IssueDraftParseError(\n `The agent did not emit an <${DRAFT_TAG}> block, so there is no Issue to create. ` +\n `Output was: ${normalized.trim().slice(0, 300) || '(empty)'}`,\n );\n }\n\n const title = (extractTag(block, 'title') ?? '').trim();\n const body = (extractTag(block, 'body') ?? '').trim();\n\n if (title.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <title>.`);\n }\n if (body.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <body>.`);\n }\n\n return { title, body, labels: parseLabels(extractTag(block, 'labels')) };\n}\n","import { loadIssuesConfig } from '../config.js';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { ensureProvidersRegistered } from '../issues/bootstrap.js';\nimport { localIssueRef } from '../issues/context.js';\nimport { IssueDraftParseError, parseIssueDraft } from '../issues/draft.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueDraft, IssueGenerateTarget } from '../issues/types.js';\nimport { resolveProjectPaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\n/** Human-readable pointer to a created Issue. */\nfunction issueLocation(issue: Issue): string {\n return issue.remoteRef ?? localIssueRef(issue.id);\n}\n\n/**\n * Ask the agent for the Issue content.\n *\n * The agent only drafts: creation belongs to the providers, so the same draft\n * can be persisted to GitHub, to the local files, or to both.\n */\nasync function draftIssue(promptText: string): Promise<IssueDraft> {\n // The duplicate check reads the local issues, which live in the global\n // storage: the path has to be handed over (and allowed) explicitly, since it\n // is outside the working directory the agent is started in.\n const { issuesDir } = await resolveProjectPaths();\n\n const template = await loadPrompt('generate');\n const prompt = applyPlaceholders(template, {\n __USER_PROMPT__: promptText,\n __LOCAL_ISSUES_DIR__: issuesDir,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [issuesDir],\n statusMessage: 'Drafting issue...',\n });\n\n if (!result.success) {\n throw new Error(`Issue creation failed: ${result.error}`);\n }\n\n return parseIssueDraft(result.result);\n}\n\n/**\n * Create the GitHub Issue and mirror it locally.\n *\n * The remote comes first on purpose: it owns the identifier, and the mirror has\n * to reuse it so both copies describe one demand. A remote failure therefore\n * leaves nothing behind, and a mirror failure is reported with the remote\n * reference that already exists.\n */\nasync function createBoth(draft: IssueDraft): Promise<Issue[]> {\n const remote = await getProvider('github').create(draft);\n\n try {\n const mirror = await getProvider('local').create({\n ...draft,\n id: remote.id,\n ...(remote.remoteRef\n ? {\n remote: {\n provider: 'github' as const,\n ref: remote.remoteRef,\n syncedAt: isoNow(),\n syncedContentHash: remote.contentHash,\n },\n }\n : {}),\n });\n return [remote, mirror];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `GitHub issue created at ${issueLocation(remote)}, but the local mirror was not written: ` +\n `${message}. Nothing else was persisted; re-run with --local to create the mirror.`,\n );\n }\n}\n\nasync function createIssues(target: IssueGenerateTarget, draft: IssueDraft): Promise<Issue[]> {\n if (target === 'both') {\n return createBoth(draft);\n }\n return [await getProvider(target).create(draft)];\n}\n\nexport async function runGenerate(\n promptText: string,\n target?: IssueGenerateTarget,\n): Promise<number> {\n const config = await loadIssuesConfig();\n const destination = target ?? config.defaultGenerateTarget;\n\n ensureProvidersRegistered();\n\n let draft: IssueDraft;\n try {\n draft = await draftIssue(promptText);\n } catch (err) {\n if (err instanceof IssueDraftParseError) {\n printError(`Could not read the generated Issue draft: ${err.message}`);\n } else {\n printError(err instanceof Error ? err.message : String(err));\n }\n return 1;\n }\n\n let created: Issue[];\n try {\n created = await createIssues(destination, draft);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n for (const issue of created) {\n printSuccess(`Issue created (${issue.source}): ${issueLocation(issue)}`);\n }\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgBO,IAAM,YAAY;AAGlB,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUA,SAAS,WAAW,QAAgB,KAA4B;AAC9D,QAAM,OAAO,IAAI,GAAG;AACpB,QAAM,QAAQ,KAAK,GAAG;AAEtB,QAAM,QAAQ,OAAO,YAAY,IAAI;AACrC,MAAI,UAAU,GAAI,QAAO;AAEzB,QAAM,MAAM,OAAO,YAAY,KAAK;AACpC,MAAI,QAAQ,MAAM,MAAM,MAAO,QAAO;AAEtC,SAAO,OAAO,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9C;AAEA,SAAS,YAAY,KAA8B;AACjD,MAAI,QAAQ,KAAM,QAAO,CAAC;AAC1B,SAAO,IACJ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,KAAK,MAAM,YAAY,MAAM,QAAQ;AAC3E;AASO,SAAS,gBAAgB,QAA4B;AAC1D,QAAM,aAAa,OAAO,QAAQ,UAAU,IAAI;AAChD,QAAM,QAAQ,WAAW,YAAY,SAAS;AAE9C,MAAI,UAAU,MAAM;AAClB,UAAM,IAAI;AAAA,MACR,8BAA8B,SAAS,wDACtB,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK,SAAS;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,SAAS,WAAW,OAAO,OAAO,KAAK,IAAI,KAAK;AACtD,QAAM,QAAQ,WAAW,OAAO,MAAM,KAAK,IAAI,KAAK;AAEpD,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,qBAAqB,QAAQ,SAAS,yBAAyB;AAAA,EAC3E;AACA,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,qBAAqB,QAAQ,SAAS,wBAAwB;AAAA,EAC1E;AAEA,SAAO,EAAE,OAAO,MAAM,QAAQ,YAAY,WAAW,OAAO,QAAQ,CAAC,EAAE;AACzE;;;ACtEA,SAAS,cAAc,OAAsB;AAC3C,SAAO,MAAM,aAAa,cAAc,MAAM,EAAE;AAClD;AAQA,eAAe,WAAW,YAAyC;AAIjE,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAEhD,QAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,EACxB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7C,SAAS,CAAC,SAAS;AAAA,IACnB,eAAe;AAAA,EACjB,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,0BAA0B,OAAO,KAAK,EAAE;AAAA,EAC1D;AAEA,SAAO,gBAAgB,OAAO,MAAM;AACtC;AAUA,eAAe,WAAW,OAAqC;AAC7D,QAAM,SAAS,MAAM,YAAY,QAAQ,EAAE,OAAO,KAAK;AAEvD,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,OAAO,EAAE,OAAO;AAAA,MAC/C,GAAG;AAAA,MACH,IAAI,OAAO;AAAA,MACX,GAAI,OAAO,YACP;AAAA,QACE,QAAQ;AAAA,UACN,UAAU;AAAA,UACV,KAAK,OAAO;AAAA,UACZ,UAAU,OAAO;AAAA,UACjB,mBAAmB,OAAO;AAAA,QAC5B;AAAA,MACF,IACA,CAAC;AAAA,IACP,CAAC;AACD,WAAO,CAAC,QAAQ,MAAM;AAAA,EACxB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI;AAAA,MACR,2BAA2B,cAAc,MAAM,CAAC,2CAC3C,OAAO;AAAA,IACd;AAAA,EACF;AACF;AAEA,eAAe,aAAa,QAA6B,OAAqC;AAC5F,MAAI,WAAW,QAAQ;AACrB,WAAO,WAAW,KAAK;AAAA,EACzB;AACA,SAAO,CAAC,MAAM,YAAY,MAAM,EAAE,OAAO,KAAK,CAAC;AACjD;AAEA,eAAsB,YACpB,YACA,QACiB;AACjB,QAAM,SAAS,MAAM,iBAAiB;AACtC,QAAM,cAAc,UAAU,OAAO;AAErC,4BAA0B;AAE1B,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,WAAW,UAAU;AAAA,EACrC,SAAS,KAAK;AACZ,QAAI,eAAe,sBAAsB;AACvC,iBAAW,6CAA6C,IAAI,OAAO,EAAE;AAAA,IACvE,OAAO;AACL,iBAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7D;AACA,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,aAAa,aAAa,KAAK;AAAA,EACjD,SAAS,KAAK;AACZ,eAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC3D,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,SAAS;AAC3B,iBAAa,kBAAkB,MAAM,MAAM,MAAM,cAAc,KAAK,CAAC,EAAE;AAAA,EACzE;AACA,SAAO;AACT;","names":[]}
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runPlan
4
- } from "./chunk-ASMCLXQ2.js";
4
+ } from "./chunk-WUITOU5B.js";
5
5
  import "./chunk-EHVKXFFN.js";
6
- import "./chunk-RN2FAMUU.js";
6
+ import "./chunk-DKMBLNGO.js";
7
7
  import "./chunk-HDV3YDTG.js";
8
- import "./chunk-TCOS2KFB.js";
8
+ import "./chunk-GOZT2YP5.js";
9
9
  import "./chunk-SLHZPB3C.js";
10
- import "./chunk-2AVZZAC6.js";
10
+ import "./chunk-G3TJHMAS.js";
11
11
  import "./chunk-JNEHVQH5.js";
12
12
  export {
13
13
  runPlan
14
14
  };
15
- //# sourceMappingURL=plan-R34SAWRK.js.map
15
+ //# sourceMappingURL=plan-4I47CQM5.js.map