issue-flow 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/{analyze-VVG7UABS.js → analyze-WRFFG2R5.js} +10 -6
  2. package/dist/analyze-WRFFG2R5.js.map +1 -0
  3. package/dist/{chunk-WZDCTAWZ.js → chunk-2Y3UO7H2.js} +27 -2
  4. package/dist/chunk-2Y3UO7H2.js.map +1 -0
  5. package/dist/{chunk-FPGHTTVV.js → chunk-75K5IKAC.js} +12 -7
  6. package/dist/chunk-75K5IKAC.js.map +1 -0
  7. package/dist/{chunk-EHX4J5G5.js → chunk-BTBSVBE3.js} +12 -7
  8. package/dist/chunk-BTBSVBE3.js.map +1 -0
  9. package/dist/chunk-CW6UE3NW.js +72 -0
  10. package/dist/chunk-CW6UE3NW.js.map +1 -0
  11. package/dist/chunk-EWMSQQL2.js +725 -0
  12. package/dist/chunk-EWMSQQL2.js.map +1 -0
  13. package/dist/{chunk-SVOWWZQJ.js → chunk-HS4QWZAO.js} +5 -280
  14. package/dist/chunk-HS4QWZAO.js.map +1 -0
  15. package/dist/{chunk-2CSHLHYP.js → chunk-JV7P6UGG.js} +2 -2
  16. package/dist/chunk-KUDAY57D.js +283 -0
  17. package/dist/chunk-KUDAY57D.js.map +1 -0
  18. package/dist/{chunk-4HT4EVYV.js → chunk-OVFYJLOH.js} +56 -13
  19. package/dist/chunk-OVFYJLOH.js.map +1 -0
  20. package/dist/{chunk-KWGRBNUG.js → chunk-UX6YAVLT.js} +26 -7
  21. package/dist/chunk-UX6YAVLT.js.map +1 -0
  22. package/dist/{chunk-YM3G5BLR.js → chunk-VLSZFMLZ.js} +2 -2
  23. package/dist/{chunk-7W5VGYXW.js → chunk-YXJPKBPV.js} +39 -64
  24. package/dist/chunk-YXJPKBPV.js.map +1 -0
  25. package/dist/{chunk-JTSAL3TI.js → chunk-Z3UIGLZG.js} +10 -5
  26. package/dist/chunk-Z3UIGLZG.js.map +1 -0
  27. package/dist/cli.js +65 -20
  28. package/dist/cli.js.map +1 -1
  29. package/dist/{config-B564P45C.js → config-UHQ6XXX2.js} +5 -3
  30. package/dist/execute-K5B4UMUT.js +13 -0
  31. package/dist/{generate-IN62ARBS.js → generate-DUKQCADD.js} +7 -5
  32. package/dist/{generate-IN62ARBS.js.map → generate-DUKQCADD.js.map} +1 -1
  33. package/dist/init-W4HBLW7R.js +9 -0
  34. package/dist/plan-NKPU45NP.js +15 -0
  35. package/dist/pr-JYGLLIEF.js +15 -0
  36. package/dist/pr-review-6H6F4XUY.js +15 -0
  37. package/dist/prd-FC2ZCEOX.js +15 -0
  38. package/dist/review-X4NGDZZI.js +13 -0
  39. package/dist/review-X4NGDZZI.js.map +1 -0
  40. package/dist/{run-GPBICAD6.js → run-VC647DQI.js} +150 -55
  41. package/dist/run-VC647DQI.js.map +1 -0
  42. package/package.json +1 -1
  43. package/prompts/pr-review.md +144 -0
  44. package/dist/analyze-VVG7UABS.js.map +0 -1
  45. package/dist/chunk-4HT4EVYV.js.map +0 -1
  46. package/dist/chunk-7W5VGYXW.js.map +0 -1
  47. package/dist/chunk-EHX4J5G5.js.map +0 -1
  48. package/dist/chunk-FPGHTTVV.js.map +0 -1
  49. package/dist/chunk-JTSAL3TI.js.map +0 -1
  50. package/dist/chunk-KWGRBNUG.js.map +0 -1
  51. package/dist/chunk-SVOWWZQJ.js.map +0 -1
  52. package/dist/chunk-WZDCTAWZ.js.map +0 -1
  53. package/dist/execute-PNOFNLZG.js +0 -12
  54. package/dist/init-5242XSYC.js +0 -9
  55. package/dist/plan-6KLONWPR.js +0 -14
  56. package/dist/pr-JLJLMFN3.js +0 -14
  57. package/dist/prd-TVJDYZXH.js +0 -14
  58. package/dist/review-FRV4ZMS2.js +0 -12
  59. package/dist/run-GPBICAD6.js.map +0 -1
  60. /package/dist/{chunk-2CSHLHYP.js.map → chunk-JV7P6UGG.js.map} +0 -0
  61. /package/dist/{chunk-YM3G5BLR.js.map → chunk-VLSZFMLZ.js.map} +0 -0
  62. /package/dist/{config-B564P45C.js.map → config-UHQ6XXX2.js.map} +0 -0
  63. /package/dist/{execute-PNOFNLZG.js.map → execute-K5B4UMUT.js.map} +0 -0
  64. /package/dist/{init-5242XSYC.js.map → init-W4HBLW7R.js.map} +0 -0
  65. /package/dist/{plan-6KLONWPR.js.map → plan-NKPU45NP.js.map} +0 -0
  66. /package/dist/{pr-JLJLMFN3.js.map → pr-JYGLLIEF.js.map} +0 -0
  67. /package/dist/{prd-TVJDYZXH.js.map → pr-review-6H6F4XUY.js.map} +0 -0
  68. /package/dist/{review-FRV4ZMS2.js.map → prd-FC2ZCEOX.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/issues/draft.ts","../src/commands/generate.ts"],"sourcesContent":["import type { IssueDraft } from './types.js';\n\n/**\n * Structured Issue draft emitted by the `generate` prompt.\n *\n * The agent no longer creates the Issue itself: it returns the content and the\n * providers persist it, so the same draft can reach GitHub, the local files, or\n * both. Capturing the result therefore has to be exact — the old approach of\n * fishing an issue URL out of free-form prose only ever worked for GitHub.\n *\n * The block mirrors the `<issue-body>` convention already used by the phase\n * templates, which keeps a multi-line markdown body readable and free of the\n * escaping a JSON payload would demand.\n */\n\n/** Wrapper the agent must emit around the draft. */\nexport const DRAFT_TAG = 'issue-draft';\n\n/** Failure to capture a draft from the agent output. */\nexport class IssueDraftParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueDraftParseError';\n }\n}\n\n/**\n * Last occurrence of `<tag>...</tag>` in `source`, or `null`.\n *\n * The last one wins: an agent that shows the format before filling it in (or\n * restates the draft after a correction) would otherwise have its first,\n * throwaway attempt captured. The closing tag is searched from the end for the\n * same reason a body may legitimately quote one.\n */\nfunction extractTag(source: string, tag: string): string | null {\n const open = `<${tag}>`;\n const close = `</${tag}>`;\n\n const start = source.lastIndexOf(open);\n if (start === -1) return null;\n\n const end = source.lastIndexOf(close);\n if (end === -1 || end < start) return null;\n\n return source.slice(start + open.length, end);\n}\n\nfunction parseLabels(raw: string | null): string[] {\n if (raw === null) return [];\n return raw\n .split(',')\n .map((label) => label.trim())\n .filter((label) => label.length > 0 && label.toLowerCase() !== '(none)');\n}\n\n/**\n * Read the draft the agent produced.\n *\n * @throws IssueDraftParseError when the block is absent or has no usable\n * title/body, so a malformed answer fails loudly instead of creating an empty\n * Issue.\n */\nexport function parseIssueDraft(output: string): IssueDraft {\n const normalized = output.replace(/\\r\\n?/g, '\\n');\n const block = extractTag(normalized, DRAFT_TAG);\n\n if (block === null) {\n throw new IssueDraftParseError(\n `The agent did not emit an <${DRAFT_TAG}> block, so there is no Issue to create. ` +\n `Output was: ${normalized.trim().slice(0, 300) || '(empty)'}`,\n );\n }\n\n const title = (extractTag(block, 'title') ?? '').trim();\n const body = (extractTag(block, 'body') ?? '').trim();\n\n if (title.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <title>.`);\n }\n if (body.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <body>.`);\n }\n\n return { title, body, labels: parseLabels(extractTag(block, 'labels')) };\n}\n","import { loadIssuesConfig } from '../config.js';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { ensureProvidersRegistered } from '../issues/bootstrap.js';\nimport { localIssueRef } from '../issues/context.js';\nimport { IssueDraftParseError, parseIssueDraft } from '../issues/draft.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueDraft, IssueGenerateTarget } from '../issues/types.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\n/** Human-readable pointer to a created Issue. */\nfunction issueLocation(issue: Issue): string {\n return issue.remoteRef ?? localIssueRef(issue.id);\n}\n\n/**\n * Ask the agent for the Issue content.\n *\n * The agent only drafts: creation belongs to the providers, so the same draft\n * can be persisted to GitHub, to the local files, or to both.\n */\nasync function draftIssue(promptText: string): Promise<IssueDraft> {\n const template = await loadPrompt('generate');\n const prompt = applyPlaceholders(template, {\n __USER_PROMPT__: promptText,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: 'Drafting issue...',\n });\n\n if (!result.success) {\n throw new Error(`Issue creation failed: ${result.error}`);\n }\n\n return parseIssueDraft(result.result);\n}\n\n/**\n * Create the GitHub Issue and mirror it locally.\n *\n * The remote comes first on purpose: it owns the identifier, and the mirror has\n * to reuse it so both copies describe one demand. A remote failure therefore\n * leaves nothing behind, and a mirror failure is reported with the remote\n * reference that already exists.\n */\nasync function createBoth(draft: IssueDraft): Promise<Issue[]> {\n const remote = await getProvider('github').create(draft);\n\n try {\n const mirror = await getProvider('local').create({\n ...draft,\n id: remote.id,\n ...(remote.remoteRef\n ? {\n remote: {\n provider: 'github' as const,\n ref: remote.remoteRef,\n syncedAt: isoNow(),\n syncedContentHash: remote.contentHash,\n },\n }\n : {}),\n });\n return [remote, mirror];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `GitHub issue created at ${issueLocation(remote)}, but the local mirror was not written: ` +\n `${message}. Nothing else was persisted; re-run with --local to create the mirror.`,\n );\n }\n}\n\nasync function createIssues(target: IssueGenerateTarget, draft: IssueDraft): Promise<Issue[]> {\n if (target === 'both') {\n return createBoth(draft);\n }\n return [await getProvider(target).create(draft)];\n}\n\nexport async function runGenerate(\n promptText: string,\n target?: IssueGenerateTarget,\n): Promise<number> {\n const config = await loadIssuesConfig();\n const destination = target ?? config.defaultGenerateTarget;\n\n ensureProvidersRegistered();\n\n let draft: IssueDraft;\n try {\n draft = await draftIssue(promptText);\n } catch (err) {\n if (err instanceof IssueDraftParseError) {\n printError(`Could not read the generated Issue draft: ${err.message}`);\n } else {\n printError(err instanceof Error ? err.message : String(err));\n }\n return 1;\n }\n\n let created: Issue[];\n try {\n created = await createIssues(destination, draft);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n for (const issue of created) {\n printSuccess(`Issue created (${issue.source}): ${issueLocation(issue)}`);\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/issues/draft.ts","../src/commands/generate.ts"],"sourcesContent":["import type { IssueDraft } from './types.js';\n\n/**\n * Structured Issue draft emitted by the `generate` prompt.\n *\n * The agent no longer creates the Issue itself: it returns the content and the\n * providers persist it, so the same draft can reach GitHub, the local files, or\n * both. Capturing the result therefore has to be exact — the old approach of\n * fishing an issue URL out of free-form prose only ever worked for GitHub.\n *\n * The block mirrors the `<issue-body>` convention already used by the phase\n * templates, which keeps a multi-line markdown body readable and free of the\n * escaping a JSON payload would demand.\n */\n\n/** Wrapper the agent must emit around the draft. */\nexport const DRAFT_TAG = 'issue-draft';\n\n/** Failure to capture a draft from the agent output. */\nexport class IssueDraftParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'IssueDraftParseError';\n }\n}\n\n/**\n * Last occurrence of `<tag>...</tag>` in `source`, or `null`.\n *\n * The last one wins: an agent that shows the format before filling it in (or\n * restates the draft after a correction) would otherwise have its first,\n * throwaway attempt captured. The closing tag is searched from the end for the\n * same reason a body may legitimately quote one.\n */\nfunction extractTag(source: string, tag: string): string | null {\n const open = `<${tag}>`;\n const close = `</${tag}>`;\n\n const start = source.lastIndexOf(open);\n if (start === -1) return null;\n\n const end = source.lastIndexOf(close);\n if (end === -1 || end < start) return null;\n\n return source.slice(start + open.length, end);\n}\n\nfunction parseLabels(raw: string | null): string[] {\n if (raw === null) return [];\n return raw\n .split(',')\n .map((label) => label.trim())\n .filter((label) => label.length > 0 && label.toLowerCase() !== '(none)');\n}\n\n/**\n * Read the draft the agent produced.\n *\n * @throws IssueDraftParseError when the block is absent or has no usable\n * title/body, so a malformed answer fails loudly instead of creating an empty\n * Issue.\n */\nexport function parseIssueDraft(output: string): IssueDraft {\n const normalized = output.replace(/\\r\\n?/g, '\\n');\n const block = extractTag(normalized, DRAFT_TAG);\n\n if (block === null) {\n throw new IssueDraftParseError(\n `The agent did not emit an <${DRAFT_TAG}> block, so there is no Issue to create. ` +\n `Output was: ${normalized.trim().slice(0, 300) || '(empty)'}`,\n );\n }\n\n const title = (extractTag(block, 'title') ?? '').trim();\n const body = (extractTag(block, 'body') ?? '').trim();\n\n if (title.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <title>.`);\n }\n if (body.length === 0) {\n throw new IssueDraftParseError(`The <${DRAFT_TAG}> block has no <body>.`);\n }\n\n return { title, body, labels: parseLabels(extractTag(block, 'labels')) };\n}\n","import { loadIssuesConfig } from '../config.js';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { isoNow } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { ensureProvidersRegistered } from '../issues/bootstrap.js';\nimport { localIssueRef } from '../issues/context.js';\nimport { IssueDraftParseError, parseIssueDraft } from '../issues/draft.js';\nimport { getProvider } from '../issues/registry.js';\nimport type { Issue, IssueDraft, IssueGenerateTarget } from '../issues/types.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\n/** Human-readable pointer to a created Issue. */\nfunction issueLocation(issue: Issue): string {\n return issue.remoteRef ?? localIssueRef(issue.id);\n}\n\n/**\n * Ask the agent for the Issue content.\n *\n * The agent only drafts: creation belongs to the providers, so the same draft\n * can be persisted to GitHub, to the local files, or to both.\n */\nasync function draftIssue(promptText: string): Promise<IssueDraft> {\n const template = await loadPrompt('generate');\n const prompt = applyPlaceholders(template, {\n __USER_PROMPT__: promptText,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: 'Drafting issue...',\n });\n\n if (!result.success) {\n throw new Error(`Issue creation failed: ${result.error}`);\n }\n\n return parseIssueDraft(result.result);\n}\n\n/**\n * Create the GitHub Issue and mirror it locally.\n *\n * The remote comes first on purpose: it owns the identifier, and the mirror has\n * to reuse it so both copies describe one demand. A remote failure therefore\n * leaves nothing behind, and a mirror failure is reported with the remote\n * reference that already exists.\n */\nasync function createBoth(draft: IssueDraft): Promise<Issue[]> {\n const remote = await getProvider('github').create(draft);\n\n try {\n const mirror = await getProvider('local').create({\n ...draft,\n id: remote.id,\n ...(remote.remoteRef\n ? {\n remote: {\n provider: 'github' as const,\n ref: remote.remoteRef,\n syncedAt: isoNow(),\n syncedContentHash: remote.contentHash,\n },\n }\n : {}),\n });\n return [remote, mirror];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `GitHub issue created at ${issueLocation(remote)}, but the local mirror was not written: ` +\n `${message}. Nothing else was persisted; re-run with --local to create the mirror.`,\n );\n }\n}\n\nasync function createIssues(target: IssueGenerateTarget, draft: IssueDraft): Promise<Issue[]> {\n if (target === 'both') {\n return createBoth(draft);\n }\n return [await getProvider(target).create(draft)];\n}\n\nexport async function runGenerate(\n promptText: string,\n target?: IssueGenerateTarget,\n): Promise<number> {\n const config = await loadIssuesConfig();\n const destination = target ?? config.defaultGenerateTarget;\n\n ensureProvidersRegistered();\n\n let draft: IssueDraft;\n try {\n draft = await draftIssue(promptText);\n } catch (err) {\n if (err instanceof IssueDraftParseError) {\n printError(`Could not read the generated Issue draft: ${err.message}`);\n } else {\n printError(err instanceof Error ? err.message : String(err));\n }\n return 1;\n }\n\n let created: Issue[];\n try {\n created = await createIssues(destination, draft);\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n for (const issue of created) {\n printSuccess(`Issue created (${issue.source}): ${issueLocation(issue)}`);\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runInit
4
+ } from "./chunk-VLSZFMLZ.js";
5
+ import "./chunk-2Y3UO7H2.js";
6
+ export {
7
+ runInit
8
+ };
9
+ //# sourceMappingURL=init-W4HBLW7R.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPlan
4
+ } from "./chunk-75K5IKAC.js";
5
+ import "./chunk-JV7P6UGG.js";
6
+ import "./chunk-HS4QWZAO.js";
7
+ import "./chunk-HDV3YDTG.js";
8
+ import "./chunk-KUDAY57D.js";
9
+ import "./chunk-SLHZPB3C.js";
10
+ import "./chunk-OVFYJLOH.js";
11
+ import "./chunk-2Y3UO7H2.js";
12
+ export {
13
+ runPlan
14
+ };
15
+ //# sourceMappingURL=plan-NKPU45NP.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPr
4
+ } from "./chunk-UX6YAVLT.js";
5
+ import "./chunk-JV7P6UGG.js";
6
+ import "./chunk-HS4QWZAO.js";
7
+ import "./chunk-HDV3YDTG.js";
8
+ import "./chunk-KUDAY57D.js";
9
+ import "./chunk-SLHZPB3C.js";
10
+ import "./chunk-OVFYJLOH.js";
11
+ import "./chunk-2Y3UO7H2.js";
12
+ export {
13
+ runPr
14
+ };
15
+ //# sourceMappingURL=pr-JYGLLIEF.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ exitCodeFor,
4
+ runPrReview
5
+ } from "./chunk-EWMSQQL2.js";
6
+ import "./chunk-CW6UE3NW.js";
7
+ import "./chunk-KUDAY57D.js";
8
+ import "./chunk-SLHZPB3C.js";
9
+ import "./chunk-OVFYJLOH.js";
10
+ import "./chunk-2Y3UO7H2.js";
11
+ export {
12
+ exitCodeFor,
13
+ runPrReview
14
+ };
15
+ //# sourceMappingURL=pr-review-6H6F4XUY.js.map
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPrd
4
+ } from "./chunk-BTBSVBE3.js";
5
+ import "./chunk-JV7P6UGG.js";
6
+ import "./chunk-HS4QWZAO.js";
7
+ import "./chunk-HDV3YDTG.js";
8
+ import "./chunk-KUDAY57D.js";
9
+ import "./chunk-SLHZPB3C.js";
10
+ import "./chunk-OVFYJLOH.js";
11
+ import "./chunk-2Y3UO7H2.js";
12
+ export {
13
+ runPrd
14
+ };
15
+ //# sourceMappingURL=prd-FC2ZCEOX.js.map
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runReview
4
+ } from "./chunk-Z3UIGLZG.js";
5
+ import "./chunk-HS4QWZAO.js";
6
+ import "./chunk-KUDAY57D.js";
7
+ import "./chunk-SLHZPB3C.js";
8
+ import "./chunk-OVFYJLOH.js";
9
+ import "./chunk-2Y3UO7H2.js";
10
+ export {
11
+ runReview
12
+ };
13
+ //# sourceMappingURL=review-X4NGDZZI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,52 +1,62 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ runPrd
4
+ } from "./chunk-BTBSVBE3.js";
2
5
  import {
3
6
  runReview
4
- } from "./chunk-JTSAL3TI.js";
7
+ } from "./chunk-Z3UIGLZG.js";
5
8
  import {
6
9
  runInit
7
- } from "./chunk-YM3G5BLR.js";
10
+ } from "./chunk-VLSZFMLZ.js";
8
11
  import {
9
- publishGitState,
12
+ printRunSummary,
10
13
  runExecute
11
- } from "./chunk-7W5VGYXW.js";
14
+ } from "./chunk-YXJPKBPV.js";
12
15
  import {
13
16
  runPlan
14
- } from "./chunk-FPGHTTVV.js";
17
+ } from "./chunk-75K5IKAC.js";
15
18
  import {
16
19
  runPr
17
- } from "./chunk-KWGRBNUG.js";
18
- import {
19
- runPrd
20
- } from "./chunk-EHX4J5G5.js";
21
- import "./chunk-2CSHLHYP.js";
20
+ } from "./chunk-UX6YAVLT.js";
21
+ import "./chunk-JV7P6UGG.js";
22
22
  import {
23
23
  getProvider,
24
24
  resolveCommandIssue
25
- } from "./chunk-SVOWWZQJ.js";
25
+ } from "./chunk-HS4QWZAO.js";
26
26
  import "./chunk-HDV3YDTG.js";
27
+ import {
28
+ mostRecent,
29
+ prReviewDir,
30
+ readPrReviewIndex,
31
+ runPrReview
32
+ } from "./chunk-EWMSQQL2.js";
33
+ import {
34
+ listPullRequests,
35
+ publishGitState
36
+ } from "./chunk-CW6UE3NW.js";
37
+ import "./chunk-KUDAY57D.js";
27
38
  import {
28
39
  resolvePackageDir
29
40
  } from "./chunk-SLHZPB3C.js";
30
41
  import {
42
+ getIssueDir,
31
43
  loadIssuesConfig,
32
44
  loadWebConfig
33
- } from "./chunk-4HT4EVYV.js";
45
+ } from "./chunk-OVFYJLOH.js";
34
46
  import {
35
47
  FilePublisher,
36
48
  NullPublisher,
37
- formatDuration,
38
49
  isVerbose,
39
50
  isoNow,
40
51
  loadTaskPlan,
41
52
  printError,
42
53
  printInfo,
43
- printSuccess,
44
54
  printWarning,
45
55
  saveTaskPlan,
46
56
  setOutputCallback,
47
57
  setSessionPublisher,
48
58
  setStoryUpdateCallback
49
- } from "./chunk-WZDCTAWZ.js";
59
+ } from "./chunk-2Y3UO7H2.js";
50
60
 
51
61
  // src/commands/run.ts
52
62
  import { randomUUID } from "crypto";
@@ -55,6 +65,7 @@ import { execa } from "execa";
55
65
 
56
66
  // src/core/pipeline.ts
57
67
  var PIPELINE_PHASES = ["init", "prd", "plan", "execute", "review", "pr"];
68
+ var PIPELINE_PHASES_WITH_PR_REVIEW = [...PIPELINE_PHASES, "pr-review"];
58
69
  var PIPELINE_PHASES_NO_BRANCH = PIPELINE_PHASES.filter(
59
70
  (p) => p !== "pr"
60
71
  );
@@ -64,7 +75,8 @@ var PHASE_TO_FIELD = {
64
75
  plan: "jsonCompleted",
65
76
  execute: "executionCompleted",
66
77
  review: "reviewCompleted",
67
- pr: "prCreated"
78
+ pr: "prCreated",
79
+ "pr-review": "prReviewCompleted"
68
80
  };
69
81
  var PipelineManager = class {
70
82
  tasksJsonPath;
@@ -151,8 +163,12 @@ var PHASE_LABELS = {
151
163
  plan: "Plan",
152
164
  execute: "Execute",
153
165
  review: "Review",
154
- pr: "PR"
166
+ pr: "PR",
167
+ "pr-review": "PR Review"
155
168
  };
169
+ function phaseLabel(phase) {
170
+ return PHASE_LABELS[phase] ?? phase.charAt(0).toUpperCase() + phase.slice(1);
171
+ }
156
172
  function selectRenderer(verbose) {
157
173
  const isTTY = !!process.stdout.isTTY;
158
174
  const isCI = !!process.env.CI;
@@ -231,7 +247,7 @@ function buildExecutePhaseTask(runner, tasksPath, verbose) {
231
247
  };
232
248
  return task.newListr([engineSubtask, ...subtaskDefs], {
233
249
  concurrent: true,
234
- exitOnError: false,
250
+ exitOnError: true,
235
251
  rendererOptions: {
236
252
  timer: PRESET_TIMER,
237
253
  collapseSkips: false
@@ -247,7 +263,7 @@ async function runPipelineWithRenderer(options) {
247
263
  const isSimple = renderer === "simple";
248
264
  const tasks = new Listr(
249
265
  phases.map((phase, index) => {
250
- const label = PHASE_LABELS[phase] ?? phase.charAt(0).toUpperCase() + phase.slice(1);
266
+ const label = phaseLabel(phase);
251
267
  const isExecutePhase = phase === "execute" && tasksPath;
252
268
  return {
253
269
  title: label,
@@ -489,9 +505,32 @@ function toIssueNumber(id) {
489
505
  }
490
506
  var RUNNABLE_PHASES = ["prd", "plan", "execute", "review", "pr"];
491
507
  var RUNNABLE_PHASES_NO_BRANCH = ["prd", "plan", "execute", "review"];
492
- async function runPipeline(issue, mode, from, noBranch) {
508
+ var RUNNABLE_PHASES_WITH_PR_REVIEW = [...RUNNABLE_PHASES, "pr-review"];
509
+ async function readPrReviewOutcome(issue, tasksPath, requestedChanges) {
510
+ const outcome = { requestedChanges, recommendation: null, reportPath: null };
511
+ try {
512
+ const plan = await loadTaskPlan(tasksPath);
513
+ outcome.recommendation = plan.prReview?.lastRecommendation ?? null;
514
+ const pullRequest = plan.prReview?.pullRequestNumber;
515
+ if (pullRequest !== void 0) {
516
+ const dir = await prReviewDir({ issue, pullRequest });
517
+ const index = await readPrReviewIndex(dir);
518
+ const last = index?.rounds.reduce(
519
+ (latest, entry) => latest === null || entry.round > latest.round ? entry : latest,
520
+ null
521
+ );
522
+ if (last) {
523
+ outcome.reportPath = join2(dir, last.reportPath);
524
+ outcome.recommendation ??= last.recommendation;
525
+ }
526
+ }
527
+ } catch {
528
+ }
529
+ return outcome;
530
+ }
531
+ async function runPipeline(issue, mode, from, noBranch, prReview) {
493
532
  const issueNumber = issue.replace(/^#/, "");
494
- const issueDir = join2("issues", issueNumber);
533
+ const issueDir = await getIssueDir(issueNumber);
495
534
  const webConfig = await loadWebConfig();
496
535
  const publisher = webConfig.enabled ? new FilePublisher(join2(issueDir, "session.json"), {
497
536
  logLimit: webConfig.logLimit,
@@ -509,7 +548,15 @@ async function runPipeline(issue, mode, from, noBranch) {
509
548
  }
510
549
  let exitCode = 1;
511
550
  try {
512
- exitCode = await runPipelinePhases(issueNumber, issueDir, mode, publisher, from, noBranch);
551
+ exitCode = await runPipelinePhases(
552
+ issueNumber,
553
+ issueDir,
554
+ mode,
555
+ publisher,
556
+ from,
557
+ noBranch,
558
+ prReview
559
+ );
513
560
  return exitCode;
514
561
  } finally {
515
562
  publisher.publish({
@@ -522,7 +569,7 @@ async function runPipeline(issue, mode, from, noBranch) {
522
569
  setSessionPublisher(void 0);
523
570
  }
524
571
  }
525
- async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, noBranch) {
572
+ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, noBranch, prReview) {
526
573
  const tasksPath = join2(issueDir, "tasks.json");
527
574
  const sessionId = randomUUID();
528
575
  let publishedIssueNumber = toIssueNumber(issueNumber);
@@ -563,6 +610,7 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
563
610
  const resolvedIssue = resolution.resolved;
564
611
  publishedIssueNumber = resolvedIssue.issue.number;
565
612
  let effectiveNoBranch = noBranch ?? false;
613
+ let effectivePrReview = prReview ?? false;
566
614
  let planIssueUrl;
567
615
  let planBranch;
568
616
  try {
@@ -570,6 +618,7 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
570
618
  const persistedNoBranch = existingPlan.noBranch ?? false;
571
619
  planIssueUrl = existingPlan.issueUrl || void 0;
572
620
  planBranch = existingPlan.branchName || void 0;
621
+ effectivePrReview = prReview ?? existingPlan.prReview?.enabled ?? false;
573
622
  if (noBranch !== void 0 && noBranch !== persistedNoBranch) {
574
623
  if (persistedNoBranch) {
575
624
  printWarning(
@@ -584,8 +633,24 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
584
633
  effectiveNoBranch = persistedNoBranch;
585
634
  } catch {
586
635
  }
587
- const activePhases = effectiveNoBranch ? PIPELINE_PHASES_NO_BRANCH : PIPELINE_PHASES;
588
- const phaseOrder = effectiveNoBranch ? RUNNABLE_PHASES_NO_BRANCH : RUNNABLE_PHASES;
636
+ if (effectiveNoBranch && effectivePrReview) {
637
+ printWarning(
638
+ "This pipeline runs with --no-branch and opens no PR. Skipping the pr-review phase."
639
+ );
640
+ effectivePrReview = false;
641
+ }
642
+ if (effectivePrReview) {
643
+ try {
644
+ const plan = await loadTaskPlan(tasksPath);
645
+ if (plan.prReview?.enabled !== true) {
646
+ plan.prReview = { ...plan.prReview, enabled: true, rounds: plan.prReview?.rounds ?? 0 };
647
+ await saveTaskPlan(tasksPath, plan);
648
+ }
649
+ } catch {
650
+ }
651
+ }
652
+ const activePhases = effectiveNoBranch ? PIPELINE_PHASES_NO_BRANCH : effectivePrReview ? PIPELINE_PHASES_WITH_PR_REVIEW : PIPELINE_PHASES;
653
+ const phaseOrder = effectiveNoBranch ? RUNNABLE_PHASES_NO_BRANCH : effectivePrReview ? RUNNABLE_PHASES_WITH_PR_REVIEW : RUNNABLE_PHASES;
589
654
  publishSessionStart(activePhases, sessionStartedAt, {
590
655
  issueUrl: planIssueUrl ?? resolvedIssue.issue.remoteRef ?? void 0,
591
656
  branch: planBranch
@@ -645,10 +710,15 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
645
710
  prd: makeRunner(() => runPrd(issueNumber, resolvedIssue), "prd"),
646
711
  plan: async () => {
647
712
  await makeRunner(() => runPlan(issueNumber, resolvedIssue), "plan")();
648
- if (effectiveNoBranch) {
713
+ if (effectiveNoBranch || effectivePrReview) {
649
714
  try {
650
715
  const plan = await loadTaskPlan(tasksPath);
651
- plan.noBranch = true;
716
+ if (effectiveNoBranch) {
717
+ plan.noBranch = true;
718
+ }
719
+ if (effectivePrReview) {
720
+ plan.prReview = { ...plan.prReview, enabled: true, rounds: plan.prReview?.rounds ?? 0 };
721
+ }
652
722
  await saveTaskPlan(tasksPath, plan);
653
723
  } catch {
654
724
  }
@@ -686,6 +756,16 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
686
756
  },
687
757
  pr: makeRunner(() => runPr(issueNumber, resolvedIssue), "pr")
688
758
  };
759
+ const reviewBox = { outcome: null };
760
+ if (effectivePrReview) {
761
+ runners["pr-review"] = async () => {
762
+ const code = await runPrReview(void 0, { issue: issueNumber, yes: true });
763
+ if (code === 1) {
764
+ throw new Error("Phase pr-review failed with exit code 1");
765
+ }
766
+ reviewBox.outcome = await readPrReviewOutcome(issueNumber, tasksPath, code === 2);
767
+ };
768
+ }
689
769
  const instrumentedRunners = Object.fromEntries(
690
770
  Object.entries(runners).map(([phase, fn]) => [
691
771
  phase,
@@ -720,28 +800,25 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
720
800
  printError(`Phase ${result.failedPhase} failed`);
721
801
  return 1;
722
802
  }
723
- try {
724
- const provider = getProvider(resolvedIssue.source);
725
- if (provider.close !== void 0) {
726
- printInfo("Closing issue...");
727
- await provider.close(issueNumber);
803
+ const review = reviewBox.outcome;
804
+ if (review?.requestedChanges) {
805
+ console.log("");
806
+ printWarning("PR review requested changes \u2014 the Pull Request is not ready to merge.");
807
+ if (review.reportPath !== null) {
808
+ console.log(` Report: ${review.reportPath}`);
728
809
  }
729
- } catch {
730
- printWarning("Failed to close issue automatically");
731
810
  }
732
- let prUrl = "unknown";
733
- if (!effectiveNoBranch && resolvedIssue.source === "github") {
811
+ if (review?.requestedChanges) {
812
+ printInfo("Issue left open until the review blockers are addressed.");
813
+ } else {
734
814
  try {
735
- const proc = await execa(
736
- "gh",
737
- ["pr", "list", "--head", "", "--json", "url", "--limit", "1"],
738
- { reject: false }
739
- );
740
- const parsed = JSON.parse(proc.stdout?.toString() ?? "[]");
741
- if (parsed[0]?.url) {
742
- prUrl = parsed[0].url;
815
+ const provider = getProvider(resolvedIssue.source);
816
+ if (provider.close !== void 0) {
817
+ printInfo("Closing issue...");
818
+ await provider.close(issueNumber);
743
819
  }
744
820
  } catch {
821
+ printWarning("Failed to close issue automatically");
745
822
  }
746
823
  }
747
824
  let branchName = "unknown";
@@ -751,27 +828,45 @@ async function runPipelinePhases(issueNumber, issueDir, mode, publisher, from, n
751
828
  } catch {
752
829
  }
753
830
  let storyCount = 0;
831
+ let planPrUrl = null;
754
832
  try {
755
833
  const plan = await loadTaskPlan(tasksPath);
756
834
  storyCount = plan.userStories.length;
757
- plan.issueStatus = "completed";
758
- plan.completedAt = isoNow();
835
+ planPrUrl = plan.pullRequest?.url ?? null;
759
836
  plan.lastAttemptAt = isoNow();
837
+ if (!review?.requestedChanges) {
838
+ plan.issueStatus = "completed";
839
+ plan.completedAt = isoNow();
840
+ }
760
841
  await saveTaskPlan(tasksPath, plan);
761
842
  } catch {
762
843
  }
763
- const totalDuration = formatDuration(result.overallElapsedSeconds);
764
- console.log("");
765
- printSuccess(`Pipeline complete for issue #${issueNumber}!`);
766
- console.log(` Branch: ${branchName}${effectiveNoBranch ? " (current)" : ""}`);
767
- console.log(` Stories: ${storyCount}`);
768
- console.log(` Duration: ${totalDuration}`);
769
- if (!effectiveNoBranch) {
770
- console.log(` PR: ${prUrl}`);
844
+ let prUrl = "unknown";
845
+ if (!effectiveNoBranch && resolvedIssue.source === "github") {
846
+ if (planPrUrl !== null) {
847
+ prUrl = planPrUrl;
848
+ } else if (branchName !== "unknown" && branchName !== "") {
849
+ try {
850
+ const latest = mostRecent(await listPullRequests(branchName));
851
+ if (latest !== null) {
852
+ prUrl = latest.url;
853
+ }
854
+ } catch {
855
+ }
856
+ }
771
857
  }
858
+ printRunSummary({
859
+ issueNumber,
860
+ branchName,
861
+ noBranch: effectiveNoBranch,
862
+ storyCount,
863
+ elapsedSeconds: result.overallElapsedSeconds,
864
+ prUrl,
865
+ prReview: review
866
+ });
772
867
  return 0;
773
868
  }
774
869
  export {
775
870
  runPipeline
776
871
  };
777
- //# sourceMappingURL=run-GPBICAD6.js.map
872
+ //# sourceMappingURL=run-VC647DQI.js.map