issue-flow 0.7.1 → 0.7.3

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 (80) hide show
  1. package/README.md +11 -7
  2. package/dist/{analyze-R25HDGBJ.js → analyze-PJBY346H.js} +19 -14
  3. package/dist/analyze-PJBY346H.js.map +1 -0
  4. package/dist/{chunk-EM43T462.js → chunk-3ISZ7T5M.js} +17 -12
  5. package/dist/chunk-3ISZ7T5M.js.map +1 -0
  6. package/dist/{chunk-EHVKXFFN.js → chunk-4M4EWFZZ.js} +2 -2
  7. package/dist/{chunk-JNEHVQH5.js → chunk-6HGZYTAT.js} +223 -11
  8. package/dist/chunk-6HGZYTAT.js.map +1 -0
  9. package/dist/chunk-7HX2RPFY.js +123 -0
  10. package/dist/chunk-7HX2RPFY.js.map +1 -0
  11. package/dist/{chunk-RN2FAMUU.js → chunk-7YS3J4A2.js} +71 -22
  12. package/dist/chunk-7YS3J4A2.js.map +1 -0
  13. package/dist/{chunk-YGEWIEAI.js → chunk-EBJM6G2D.js} +90 -23
  14. package/dist/chunk-EBJM6G2D.js.map +1 -0
  15. package/dist/{chunk-TCOS2KFB.js → chunk-IWBHBDRU.js} +22 -23
  16. package/dist/chunk-IWBHBDRU.js.map +1 -0
  17. package/dist/{chunk-23WOEBGN.js → chunk-NVSJYJ72.js} +31 -22
  18. package/dist/chunk-NVSJYJ72.js.map +1 -0
  19. package/dist/{chunk-2AVZZAC6.js → chunk-OCXHO7YP.js} +332 -27
  20. package/dist/chunk-OCXHO7YP.js.map +1 -0
  21. package/dist/{chunk-HJ7JT777.js → chunk-R7X26MWK.js} +18 -13
  22. package/dist/chunk-R7X26MWK.js.map +1 -0
  23. package/dist/{chunk-YLXPLHEL.js → chunk-RKASM5FF.js} +2 -2
  24. package/dist/{chunk-ASMCLXQ2.js → chunk-SHG2PPN7.js} +20 -15
  25. package/dist/chunk-SHG2PPN7.js.map +1 -0
  26. package/dist/{chunk-ZAEBDO3Q.js → chunk-WEXU4R3H.js} +23 -15
  27. package/dist/chunk-WEXU4R3H.js.map +1 -0
  28. package/dist/{chunk-WHIBTNYI.js → chunk-YPIIIDTT.js} +3 -3
  29. package/dist/cli.js +13 -13
  30. package/dist/{config-SJAQMFWB.js → config-7N7UJP32.js} +3 -3
  31. package/dist/execute-EZKQKKLO.js +13 -0
  32. package/dist/{generate-LUK3XTIP.js → generate-KUQNDMMR.js} +19 -10
  33. package/dist/generate-KUQNDMMR.js.map +1 -0
  34. package/dist/init-GL6US653.js +9 -0
  35. package/dist/plan-XNXGWNGY.js +15 -0
  36. package/dist/pr-XILDNJLM.js +15 -0
  37. package/dist/pr-review-4WG2RPRP.js +15 -0
  38. package/dist/prd-BLJ2XBPL.js +15 -0
  39. package/dist/review-MRA7ZQQG.js +13 -0
  40. package/dist/{run-SDRQDMSZ.js → run-X62QZCXN.js} +35 -26
  41. package/dist/run-X62QZCXN.js.map +1 -0
  42. package/package.json +2 -2
  43. package/prompts/generate.md +1 -1
  44. package/prompts/prd.md +1 -1
  45. package/web/CLAUDE.md +35 -0
  46. package/web/public/app.css +5 -0
  47. package/web/public/app.js +80 -7
  48. package/dist/analyze-R25HDGBJ.js.map +0 -1
  49. package/dist/chunk-23WOEBGN.js.map +0 -1
  50. package/dist/chunk-2AVZZAC6.js.map +0 -1
  51. package/dist/chunk-ASMCLXQ2.js.map +0 -1
  52. package/dist/chunk-EM43T462.js.map +0 -1
  53. package/dist/chunk-HJ7JT777.js.map +0 -1
  54. package/dist/chunk-JNEHVQH5.js.map +0 -1
  55. package/dist/chunk-RN2FAMUU.js.map +0 -1
  56. package/dist/chunk-SLHZPB3C.js +0 -50
  57. package/dist/chunk-SLHZPB3C.js.map +0 -1
  58. package/dist/chunk-TCOS2KFB.js.map +0 -1
  59. package/dist/chunk-YGEWIEAI.js.map +0 -1
  60. package/dist/chunk-ZAEBDO3Q.js.map +0 -1
  61. package/dist/execute-VZ7SHLP4.js +0 -13
  62. package/dist/generate-LUK3XTIP.js.map +0 -1
  63. package/dist/init-5KXCG36P.js +0 -9
  64. package/dist/plan-R34SAWRK.js +0 -15
  65. package/dist/pr-P4BDQXUQ.js +0 -15
  66. package/dist/pr-review-TOSBE6ZS.js +0 -15
  67. package/dist/prd-RYLULM75.js +0 -15
  68. package/dist/review-ZSC5ELQA.js +0 -13
  69. package/dist/run-SDRQDMSZ.js.map +0 -1
  70. /package/dist/{chunk-EHVKXFFN.js.map → chunk-4M4EWFZZ.js.map} +0 -0
  71. /package/dist/{chunk-YLXPLHEL.js.map → chunk-RKASM5FF.js.map} +0 -0
  72. /package/dist/{chunk-WHIBTNYI.js.map → chunk-YPIIIDTT.js.map} +0 -0
  73. /package/dist/{config-SJAQMFWB.js.map → config-7N7UJP32.js.map} +0 -0
  74. /package/dist/{execute-VZ7SHLP4.js.map → execute-EZKQKKLO.js.map} +0 -0
  75. /package/dist/{init-5KXCG36P.js.map → init-GL6US653.js.map} +0 -0
  76. /package/dist/{plan-R34SAWRK.js.map → plan-XNXGWNGY.js.map} +0 -0
  77. /package/dist/{pr-P4BDQXUQ.js.map → pr-XILDNJLM.js.map} +0 -0
  78. /package/dist/{pr-review-TOSBE6ZS.js.map → pr-review-4WG2RPRP.js.map} +0 -0
  79. /package/dist/{prd-RYLULM75.js.map → prd-BLJ2XBPL.js.map} +0 -0
  80. /package/dist/{review-ZSC5ELQA.js.map → review-MRA7ZQQG.js.map} +0 -0
@@ -1,24 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runPhaseWithRetry
4
- } from "./chunk-EHVKXFFN.js";
4
+ } from "./chunk-4M4EWFZZ.js";
5
5
  import {
6
6
  issuePlaceholders,
7
7
  resolveCommandIssue
8
- } from "./chunk-RN2FAMUU.js";
8
+ } from "./chunk-7YS3J4A2.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-IWBHBDRU.js";
15
15
  import {
16
16
  applyPlaceholders,
17
- loadPrompt
18
- } from "./chunk-SLHZPB3C.js";
17
+ loadPrompt,
18
+ publishPhaseMetrics
19
+ } from "./chunk-7HX2RPFY.js";
19
20
  import {
20
- getIssueDir
21
- } from "./chunk-2AVZZAC6.js";
21
+ resolveIssuePaths
22
+ } from "./chunk-OCXHO7YP.js";
22
23
  import {
23
24
  getGlobalTimeout,
24
25
  isoNow,
@@ -26,10 +27,9 @@ import {
26
27
  printError,
27
28
  printSuccess,
28
29
  saveTaskPlan
29
- } from "./chunk-JNEHVQH5.js";
30
+ } from "./chunk-6HGZYTAT.js";
30
31
 
31
32
  // src/commands/pr.ts
32
- import { join } from "path";
33
33
  import { execa } from "execa";
34
34
  function parsePrUrl(output) {
35
35
  const match = output.match(/(https:\/\/github\.com\/[^\s]+\/pull\/\d+)/);
@@ -47,8 +47,8 @@ function issueClosesLine(issue, fallbackId) {
47
47
  }
48
48
  async function runPr(issue, resolvedIssue) {
49
49
  const issueNumber = issue.replace(/^#/, "");
50
- const issueDir = await getIssueDir(issueNumber);
51
- const tasksPath = join(issueDir, "tasks.json");
50
+ const paths = await resolveIssuePaths(issueNumber);
51
+ const tasksPath = paths.tasksFile;
52
52
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
53
53
  if (!resolution.ok) {
54
54
  return resolution.code;
@@ -77,14 +77,19 @@ async function runPr(issue, resolvedIssue) {
77
77
  const outcome = await runPhaseWithRetry({
78
78
  phase: "pr",
79
79
  attempt: async () => {
80
+ const startedAtMs = Date.now();
80
81
  const result = await runHeadless({
81
82
  prompt,
82
83
  maxTurns: 15,
83
84
  timeout: getGlobalTimeout() ?? 3e5,
84
- outputFormat: "text",
85
+ // json (not text) so the CLI reports usage: the envelope's `result`
86
+ // field carries the same assistant text parsePrUrl() already consumed.
87
+ outputFormat: "json",
85
88
  allowedTools: ["Bash", "Read", "Glob", "Grep"],
89
+ addDirs: [paths.issueDir],
86
90
  statusMessage: `Creating PR for issue #${issueNumber}...`
87
91
  });
92
+ publishPhaseMetrics("pr", result.cost, startedAtMs);
88
93
  if (!result.success) {
89
94
  return {
90
95
  ok: false,
@@ -127,4 +132,4 @@ async function runPr(issue, resolvedIssue) {
127
132
  export {
128
133
  runPr
129
134
  };
130
- //# sourceMappingURL=chunk-HJ7JT777.js.map
135
+ //# sourceMappingURL=chunk-R7X26MWK.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 { publishPhaseMetrics } from '../core/session-metrics.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 startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n // json (not text) so the CLI reports usage: the envelope's `result`\n // field carries the same assistant text parsePrUrl() already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [paths.issueDir],\n statusMessage: `Creating PR for issue #${issueNumber}...`,\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('pr', result.cost, startedAtMs);\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":[]}
@@ -4,7 +4,7 @@ import {
4
4
  printInfo,
5
5
  printSuccess,
6
6
  printWarning
7
- } from "./chunk-JNEHVQH5.js";
7
+ } from "./chunk-6HGZYTAT.js";
8
8
 
9
9
  // src/commands/init.ts
10
10
  import { execa } from "execa";
@@ -132,4 +132,4 @@ async function runInit(source = "github") {
132
132
  export {
133
133
  runInit
134
134
  };
135
- //# sourceMappingURL=chunk-YLXPLHEL.js.map
135
+ //# sourceMappingURL=chunk-RKASM5FF.js.map
@@ -2,24 +2,25 @@
2
2
  import {
3
3
  readFileWithGrace,
4
4
  runPhaseWithRetry
5
- } from "./chunk-EHVKXFFN.js";
5
+ } from "./chunk-4M4EWFZZ.js";
6
6
  import {
7
7
  issuePlaceholders,
8
8
  resolveCommandIssue
9
- } from "./chunk-RN2FAMUU.js";
9
+ } from "./chunk-7YS3J4A2.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-IWBHBDRU.js";
16
16
  import {
17
17
  applyPlaceholders,
18
- loadPrompt
19
- } from "./chunk-SLHZPB3C.js";
18
+ loadPrompt,
19
+ publishPhaseMetrics
20
+ } from "./chunk-7HX2RPFY.js";
20
21
  import {
21
- getIssueDir
22
- } from "./chunk-2AVZZAC6.js";
22
+ resolveIssuePaths
23
+ } from "./chunk-OCXHO7YP.js";
23
24
  import {
24
25
  getGlobalTimeout,
25
26
  loadTaskPlan,
@@ -27,19 +28,18 @@ import {
27
28
  printSuccess,
28
29
  saveTaskPlan,
29
30
  taskPlanSchema
30
- } from "./chunk-JNEHVQH5.js";
31
+ } from "./chunk-6HGZYTAT.js";
31
32
 
32
33
  // src/commands/plan.ts
33
34
  import { mkdir, readFile } from "fs/promises";
34
- import { join } from "path";
35
35
  async function runPlan(issue, resolvedIssue) {
36
36
  const issueNumber = issue.replace(/^#/, "");
37
- const issueDir = await getIssueDir(issueNumber);
37
+ const paths = await resolveIssuePaths(issueNumber);
38
38
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
39
39
  if (!resolution.ok) {
40
40
  return resolution.code;
41
41
  }
42
- const prdPath = join(issueDir, "prd.md");
42
+ const prdPath = paths.prdFile;
43
43
  let prdContent;
44
44
  try {
45
45
  prdContent = await readFile(prdPath, "utf-8");
@@ -47,7 +47,7 @@ async function runPlan(issue, resolvedIssue) {
47
47
  printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);
48
48
  return 1;
49
49
  }
50
- const tasksPath = join(issueDir, "tasks.json");
50
+ const tasksPath = paths.tasksFile;
51
51
  const template = await loadPrompt("plan");
52
52
  const prompt = applyPlaceholders(template, {
53
53
  __ISSUE_NUMBER__: issueNumber,
@@ -55,18 +55,23 @@ async function runPlan(issue, resolvedIssue) {
55
55
  __TASKS_PATH__: tasksPath,
56
56
  ...issuePlaceholders(resolution.resolved)
57
57
  });
58
- await mkdir(issueDir, { recursive: true });
58
+ await mkdir(paths.issueDir, { recursive: true });
59
59
  const outcome = await runPhaseWithRetry({
60
60
  phase: "plan",
61
61
  attempt: async () => {
62
+ const startedAtMs = Date.now();
62
63
  const result = await runHeadless({
63
64
  prompt,
64
65
  maxTurns: 25,
65
66
  timeout: getGlobalTimeout() ?? 3e5,
66
- outputFormat: "text",
67
+ // json (not text) so the CLI reports usage: the envelope's `result`
68
+ // field carries the same assistant text this phase already consumed.
69
+ outputFormat: "json",
67
70
  allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
71
+ addDirs: [paths.issueDir],
68
72
  statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`
69
73
  });
74
+ publishPhaseMetrics("plan", result.cost, startedAtMs);
70
75
  if (!result.success) {
71
76
  return {
72
77
  ok: false,
@@ -113,4 +118,4 @@ ${issues}`
113
118
  export {
114
119
  runPlan
115
120
  };
116
- //# sourceMappingURL=chunk-ASMCLXQ2.js.map
121
+ //# sourceMappingURL=chunk-SHG2PPN7.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 { publishPhaseMetrics } from '../core/session-metrics.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { 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 startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n // json (not text) so the CLI reports usage: the envelope's `result`\n // field carries the same assistant text this phase already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n addDirs: [paths.issueDir],\n statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`,\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('plan', result.cost, startedAtMs);\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `Task plan generation failed: ${result.error}`,\n };\n }\n\n // Verify the file was created, tolerating a brief FS-visibility lag.\n let rawContent: string;\n try {\n rawContent = await readFileWithGrace(tasksPath);\n } catch {\n return { ok: false, transient: true, error: `tasks.json was not created at ${tasksPath}` };\n }\n\n // Validate JSON structure — a content defect, not a timing issue, but\n // still worth a bounded retry since it's a fresh Claude invocation.\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawContent);\n } catch {\n return { ok: false, transient: true, error: 'tasks.json contains invalid JSON' };\n }\n\n // Validate with zod schema\n const validation = taskPlanSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues\n .map((i) => ` - ${i.path.join('.')}: ${i.message}`)\n .join('\\n');\n return {\n ok: false,\n transient: true,\n error: `tasks.json does not match expected schema:\\n${issues}`,\n };\n }\n\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `Task plan generation failed for issue #${issueNumber}`);\n return 1;\n }\n\n // Ensure pipeline state reflects completion of this phase\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.jsonCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n\n printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,gBAAgB;AAchC,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,cAAc,KAAK,IAAI;AAC7B,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B;AAAA,QACA,UAAU;AAAA,QACV,SAAS,iBAAiB,KAAK;AAAA;AAAA;AAAA,QAG/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,0BAAoB,QAAQ,OAAO,MAAM,WAAW;AAEpD,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":[]}
@@ -2,61 +2,69 @@
2
2
  import {
3
3
  readFileWithGrace,
4
4
  runPhaseWithRetry
5
- } from "./chunk-EHVKXFFN.js";
5
+ } from "./chunk-4M4EWFZZ.js";
6
6
  import {
7
7
  issuePlaceholders,
8
8
  resolveCommandIssue
9
- } from "./chunk-RN2FAMUU.js";
9
+ } from "./chunk-7YS3J4A2.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-IWBHBDRU.js";
16
16
  import {
17
17
  applyPlaceholders,
18
- loadPrompt
19
- } from "./chunk-SLHZPB3C.js";
18
+ loadPrompt,
19
+ publishPhaseMetrics
20
+ } from "./chunk-7HX2RPFY.js";
20
21
  import {
21
- getIssueDir
22
- } from "./chunk-2AVZZAC6.js";
22
+ resolveIssuePaths
23
+ } from "./chunk-OCXHO7YP.js";
23
24
  import {
24
25
  getGlobalTimeout,
25
26
  loadTaskPlan,
26
27
  printError,
27
28
  printSuccess,
28
29
  saveTaskPlan
29
- } from "./chunk-JNEHVQH5.js";
30
+ } from "./chunk-6HGZYTAT.js";
30
31
 
31
32
  // src/commands/prd.ts
32
33
  import { mkdir } from "fs/promises";
33
- import { join } from "path";
34
34
  async function runPrd(issue, resolvedIssue) {
35
35
  const issueNumber = issue.replace(/^#/, "");
36
- const issueDir = await getIssueDir(issueNumber);
37
- const prdPath = join(issueDir, "prd.md");
36
+ const paths = await resolveIssuePaths(issueNumber);
37
+ const prdPath = paths.prdFile;
38
38
  const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);
39
39
  if (!resolution.ok) {
40
40
  return resolution.code;
41
41
  }
42
- await mkdir(issueDir, { recursive: true });
42
+ await mkdir(paths.issueDir, { recursive: true });
43
43
  const template = await loadPrompt("prd");
44
44
  const prompt = applyPlaceholders(template, {
45
45
  __ISSUE_NUMBER__: issueNumber,
46
46
  __PRD_PATH__: prdPath,
47
+ // Absolute: the analysis lives in the global storage, outside the working
48
+ // directory the agent is started in.
49
+ __ANALYSIS_PATH__: paths.analysisFile,
47
50
  ...issuePlaceholders(resolution.resolved)
48
51
  });
49
52
  const outcome = await runPhaseWithRetry({
50
53
  phase: "prd",
51
54
  attempt: async () => {
55
+ const startedAtMs = Date.now();
52
56
  const result = await runHeadless({
53
57
  prompt,
54
58
  maxTurns: 25,
55
59
  timeout: getGlobalTimeout() ?? 3e5,
56
- outputFormat: "text",
60
+ // json (not text) so the CLI reports usage: the envelope's `result`
61
+ // field carries the same assistant text this phase already consumed.
62
+ outputFormat: "json",
57
63
  allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
64
+ addDirs: [paths.issueDir],
58
65
  statusMessage: `Generating PRD for issue #${issueNumber}...`
59
66
  });
67
+ publishPhaseMetrics("prd", result.cost, startedAtMs);
60
68
  if (!result.success) {
61
69
  return {
62
70
  ok: false,
@@ -79,7 +87,7 @@ async function runPrd(issue, resolvedIssue) {
79
87
  printError(outcome.error ?? `PRD generation failed for issue #${issueNumber}`);
80
88
  return 1;
81
89
  }
82
- const tasksPath = join(issueDir, "tasks.json");
90
+ const tasksPath = paths.tasksFile;
83
91
  try {
84
92
  const plan = await loadTaskPlan(tasksPath);
85
93
  plan.pipeline.prdCompleted = true;
@@ -93,4 +101,4 @@ async function runPrd(issue, resolvedIssue) {
93
101
  export {
94
102
  runPrd
95
103
  };
96
- //# sourceMappingURL=chunk-ZAEBDO3Q.js.map
104
+ //# sourceMappingURL=chunk-WEXU4R3H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/prd.ts"],"sourcesContent":["import { mkdir } 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 { publishPhaseMetrics } from '../core/session-metrics.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\nexport async function runPrd(issue: string, resolvedIssue?: ResolvedIssue): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n const prdPath = paths.prdFile;\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n await mkdir(paths.issueDir, { recursive: true });\n\n const template = await loadPrompt('prd');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __PRD_PATH__: prdPath,\n // Absolute: the analysis lives in the global storage, outside the working\n // directory the agent is started in.\n __ANALYSIS_PATH__: paths.analysisFile,\n ...issuePlaceholders(resolution.resolved),\n });\n\n const outcome = await runPhaseWithRetry({\n phase: 'prd',\n attempt: async () => {\n const startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n // json (not text) so the CLI reports usage: the envelope's `result`\n // field carries the same assistant text this phase already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n addDirs: [paths.issueDir],\n statusMessage: `Generating PRD for issue #${issueNumber}...`,\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('prd', result.cost, startedAtMs);\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `PRD generation failed: ${result.error}`,\n };\n }\n\n // Verify the file was created. A brief grace period absorbs a lag\n // between the Write tool completing and this process seeing it; if it\n // still isn't there, treat it as recoverable and let the phase retry.\n try {\n const content = await readFileWithGrace(prdPath);\n if (content.length < 10) {\n return { ok: false, transient: true, error: 'PRD file was created but appears empty' };\n }\n } catch {\n return { ok: false, transient: true, error: `PRD file was not created at ${prdPath}` };\n }\n\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `PRD generation failed for issue #${issueNumber}`);\n return 1;\n }\n\n // Update pipeline state\n const tasksPath = paths.tasksFile;\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prdCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist yet\n }\n\n printSuccess(`PRD saved to ${prdPath}`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,aAAa;AAatB,eAAsB,OAAO,OAAe,eAAgD;AAC1F,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,QAAQ,MAAM,kBAAkB,WAAW;AACjD,QAAM,UAAU,MAAM;AAEtB,QAAM,aAAa,MAAM,oBAAoB,aAAa,aAAa;AACvE,MAAI,CAAC,WAAW,IAAI;AAClB,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAE/C,QAAM,WAAW,MAAM,WAAW,KAAK;AACvC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,cAAc;AAAA;AAAA;AAAA,IAGd,mBAAmB,MAAM;AAAA,IACzB,GAAG,kBAAkB,WAAW,QAAQ;AAAA,EAC1C,CAAC;AAED,QAAM,UAAU,MAAM,kBAAkB;AAAA,IACtC,OAAO;AAAA,IACP,SAAS,YAAY;AACnB,YAAM,cAAc,KAAK,IAAI;AAC7B,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B;AAAA,QACA,UAAU;AAAA,QACV,SAAS,iBAAiB,KAAK;AAAA;AAAA;AAAA,QAG/B,cAAc;AAAA,QACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,QACtD,SAAS,CAAC,MAAM,QAAQ;AAAA,QACxB,eAAe,6BAA6B,WAAW;AAAA,MACzD,CAAC;AAED,0BAAoB,OAAO,OAAO,MAAM,WAAW;AAEnD,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,WAAW,mBAAmB,GAAG,OAAO,SAAS,EAAE;AAAA,UACnD,OAAO,0BAA0B,OAAO,KAAK;AAAA,QAC/C;AAAA,MACF;AAKA,UAAI;AACF,cAAM,UAAU,MAAM,kBAAkB,OAAO;AAC/C,YAAI,QAAQ,SAAS,IAAI;AACvB,iBAAO,EAAE,IAAI,OAAO,WAAW,MAAM,OAAO,yCAAyC;AAAA,QACvF;AAAA,MACF,QAAQ;AACN,eAAO,EAAE,IAAI,OAAO,WAAW,MAAM,OAAO,+BAA+B,OAAO,GAAG;AAAA,MACvF;AAEA,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,QAAQ,IAAI;AACf,eAAW,QAAQ,SAAS,oCAAoC,WAAW,EAAE;AAC7E,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,MAAM;AACxB,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,eAAe;AAC7B,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,eAAa,gBAAgB,OAAO,EAAE;AACtC,SAAO;AACT;","names":[]}
@@ -4,11 +4,11 @@ import {
4
4
  getCommitsSince,
5
5
  getCurrentBranch,
6
6
  run
7
- } from "./chunk-2AVZZAC6.js";
7
+ } from "./chunk-OCXHO7YP.js";
8
8
  import {
9
9
  NullPublisher,
10
10
  isoNow
11
- } from "./chunk-JNEHVQH5.js";
11
+ } from "./chunk-6HGZYTAT.js";
12
12
 
13
13
  // src/core/session-git.ts
14
14
  async function listPullRequests(branch) {
@@ -69,4 +69,4 @@ export {
69
69
  listPullRequests,
70
70
  publishGitState
71
71
  };
72
- //# sourceMappingURL=chunk-WHIBTNYI.js.map
72
+ //# sourceMappingURL=chunk-YPIIIDTT.js.map
package/dist/cli.js CHANGED
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  setIssuesCliOverrides,
4
4
  setWebCliOverrides
5
- } from "./chunk-2AVZZAC6.js";
5
+ } from "./chunk-OCXHO7YP.js";
6
6
  import {
7
7
  printError,
8
8
  setGlobalTimeout,
9
9
  setVerbose
10
- } from "./chunk-JNEHVQH5.js";
10
+ } from "./chunk-6HGZYTAT.js";
11
11
 
12
12
  // src/cli.ts
13
13
  import { createRequire } from "module";
@@ -159,8 +159,8 @@ 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");
163
- const { runInit } = await import("./init-5KXCG36P.js");
162
+ const { loadIssuesConfig } = await import("./config-7N7UJP32.js");
163
+ const { runInit } = await import("./init-GL6US653.js");
164
164
  const { preferredProvider } = await loadIssuesConfig();
165
165
  const code = await runInit(preferredProvider);
166
166
  process.exit(code);
@@ -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-KUQNDMMR.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-X62QZCXN.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-PJBY346H.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-BLJ2XBPL.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-XNXGWNGY.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-EZKQKKLO.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-MRA7ZQQG.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-XILDNJLM.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-4WG2RPRP.js");
293
293
  const code = await runPrReview(pr, options);
294
294
  process.exit(code);
295
295
  }
@@ -15,8 +15,8 @@ import {
15
15
  setIssuesCliOverrides,
16
16
  setWebCliOverrides,
17
17
  validateDependencies
18
- } from "./chunk-2AVZZAC6.js";
19
- import "./chunk-JNEHVQH5.js";
18
+ } from "./chunk-OCXHO7YP.js";
19
+ import "./chunk-6HGZYTAT.js";
20
20
  export {
21
21
  DEFAULTS,
22
22
  GLOBAL_CONFIG_FILENAME,
@@ -34,4 +34,4 @@ export {
34
34
  setWebCliOverrides,
35
35
  validateDependencies
36
36
  };
37
- //# sourceMappingURL=config-SJAQMFWB.js.map
37
+ //# sourceMappingURL=config-7N7UJP32.js.map
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runExecute
4
+ } from "./chunk-EBJM6G2D.js";
5
+ import "./chunk-HDV3YDTG.js";
6
+ import "./chunk-YPIIIDTT.js";
7
+ import "./chunk-7HX2RPFY.js";
8
+ import "./chunk-OCXHO7YP.js";
9
+ import "./chunk-6HGZYTAT.js";
10
+ export {
11
+ runExecute
12
+ };
13
+ //# sourceMappingURL=execute-EZKQKKLO.js.map
@@ -3,23 +3,25 @@ import {
3
3
  ensureProvidersRegistered,
4
4
  getProvider,
5
5
  localIssueRef
6
- } from "./chunk-RN2FAMUU.js";
6
+ } from "./chunk-7YS3J4A2.js";
7
7
  import {
8
8
  runHeadless
9
- } from "./chunk-TCOS2KFB.js";
9
+ } from "./chunk-IWBHBDRU.js";
10
10
  import {
11
11
  applyPlaceholders,
12
- loadPrompt
13
- } from "./chunk-SLHZPB3C.js";
12
+ loadPrompt,
13
+ publishPhaseMetrics
14
+ } from "./chunk-7HX2RPFY.js";
14
15
  import {
15
- loadIssuesConfig
16
- } from "./chunk-2AVZZAC6.js";
16
+ loadIssuesConfig,
17
+ resolveProjectPaths
18
+ } from "./chunk-OCXHO7YP.js";
17
19
  import {
18
20
  getGlobalTimeout,
19
21
  isoNow,
20
22
  printError,
21
23
  printSuccess
22
- } from "./chunk-JNEHVQH5.js";
24
+ } from "./chunk-6HGZYTAT.js";
23
25
 
24
26
  // src/issues/draft.ts
25
27
  var DRAFT_TAG = "issue-draft";
@@ -66,18 +68,25 @@ function issueLocation(issue) {
66
68
  return issue.remoteRef ?? localIssueRef(issue.id);
67
69
  }
68
70
  async function draftIssue(promptText) {
71
+ const { issuesDir } = await resolveProjectPaths();
69
72
  const template = await loadPrompt("generate");
70
73
  const prompt = applyPlaceholders(template, {
71
- __USER_PROMPT__: promptText
74
+ __USER_PROMPT__: promptText,
75
+ __LOCAL_ISSUES_DIR__: issuesDir
72
76
  });
77
+ const startedAtMs = Date.now();
73
78
  const result = await runHeadless({
74
79
  prompt,
75
80
  maxTurns: 15,
76
81
  timeout: getGlobalTimeout() ?? 3e5,
77
- outputFormat: "text",
82
+ // json (not text) so the CLI reports usage: the envelope's `result` field
83
+ // carries the same assistant text parseIssueDraft() already consumed.
84
+ outputFormat: "json",
78
85
  allowedTools: ["Bash", "Read", "Glob", "Grep"],
86
+ addDirs: [issuesDir],
79
87
  statusMessage: "Drafting issue..."
80
88
  });
89
+ publishPhaseMetrics("generate", result.cost, startedAtMs);
81
90
  if (!result.success) {
82
91
  throw new Error(`Issue creation failed: ${result.error}`);
83
92
  }
@@ -142,4 +151,4 @@ async function runGenerate(promptText, target) {
142
151
  export {
143
152
  runGenerate
144
153
  };
145
- //# sourceMappingURL=generate-LUK3XTIP.js.map
154
+ //# sourceMappingURL=generate-KUQNDMMR.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 { publishPhaseMetrics } from '../core/session-metrics.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 startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n // json (not text) so the CLI reports usage: the envelope's `result` field\n // carries the same assistant text parseIssueDraft() already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [issuesDir],\n statusMessage: 'Drafting issue...',\n });\n // Before the success check: the tokens were spent either way.\n publishPhaseMetrics('generate', result.cost, startedAtMs);\n\n if (!result.success) {\n throw new Error(`Issue creation failed: ${result.error}`);\n }\n\n return parseIssueDraft(result.result);\n}\n\n/**\n * Create the GitHub Issue and mirror it locally.\n *\n * The remote comes first on purpose: it owns the identifier, and the mirror has\n * to reuse it so both copies describe one demand. A remote failure therefore\n * leaves nothing behind, and a mirror failure is reported with the remote\n * reference that already exists.\n */\nasync function createBoth(draft: IssueDraft): Promise<Issue[]> {\n const remote = await getProvider('github').create(draft);\n\n try {\n const mirror = await getProvider('local').create({\n ...draft,\n id: remote.id,\n ...(remote.remoteRef\n ? {\n remote: {\n provider: 'github' as const,\n ref: remote.remoteRef,\n syncedAt: isoNow(),\n syncedContentHash: remote.contentHash,\n },\n }\n : {}),\n });\n return [remote, mirror];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `GitHub issue created at ${issueLocation(remote)}, but the local mirror was not written: ` +\n `${message}. Nothing else was persisted; re-run with --local to create the mirror.`,\n );\n }\n}\n\nasync function createIssues(target: IssueGenerateTarget, draft: IssueDraft): Promise<Issue[]> {\n if (target === 'both') {\n return createBoth(draft);\n }\n return [await getProvider(target).create(draft)];\n}\n\nexport async function runGenerate(\n promptText: string,\n target?: IssueGenerateTarget,\n): Promise<number> {\n const config = await loadIssuesConfig();\n const destination = target ?? config.defaultGenerateTarget;\n\n ensureProvidersRegistered();\n\n let draft: IssueDraft;\n try {\n draft = await draftIssue(promptText);\n } catch (err) {\n if (err instanceof IssueDraftParseError) {\n printError(`Could not read the generated Issue draft: ${err.message}`);\n } else {\n printError(err instanceof Error ? err.message : String(err));\n }\n return 1;\n }\n\n let created: Issue[];\n try {\n created = await createIssues(destination, draft);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n for (const issue of created) {\n printSuccess(`Issue created (${issue.source}): ${issueLocation(issue)}`);\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runInit
4
+ } from "./chunk-RKASM5FF.js";
5
+ import "./chunk-6HGZYTAT.js";
6
+ export {
7
+ runInit
8
+ };
9
+ //# sourceMappingURL=init-GL6US653.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPlan
4
+ } from "./chunk-SHG2PPN7.js";
5
+ import "./chunk-4M4EWFZZ.js";
6
+ import "./chunk-7YS3J4A2.js";
7
+ import "./chunk-HDV3YDTG.js";
8
+ import "./chunk-IWBHBDRU.js";
9
+ import "./chunk-7HX2RPFY.js";
10
+ import "./chunk-OCXHO7YP.js";
11
+ import "./chunk-6HGZYTAT.js";
12
+ export {
13
+ runPlan
14
+ };
15
+ //# sourceMappingURL=plan-XNXGWNGY.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPr
4
+ } from "./chunk-R7X26MWK.js";
5
+ import "./chunk-4M4EWFZZ.js";
6
+ import "./chunk-7YS3J4A2.js";
7
+ import "./chunk-HDV3YDTG.js";
8
+ import "./chunk-IWBHBDRU.js";
9
+ import "./chunk-7HX2RPFY.js";
10
+ import "./chunk-OCXHO7YP.js";
11
+ import "./chunk-6HGZYTAT.js";
12
+ export {
13
+ runPr
14
+ };
15
+ //# sourceMappingURL=pr-XILDNJLM.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ exitCodeFor,
4
+ runPrReview
5
+ } from "./chunk-NVSJYJ72.js";
6
+ import "./chunk-YPIIIDTT.js";
7
+ import "./chunk-IWBHBDRU.js";
8
+ import "./chunk-7HX2RPFY.js";
9
+ import "./chunk-OCXHO7YP.js";
10
+ import "./chunk-6HGZYTAT.js";
11
+ export {
12
+ exitCodeFor,
13
+ runPrReview
14
+ };
15
+ //# sourceMappingURL=pr-review-4WG2RPRP.js.map