issue-flow 0.11.1 → 0.13.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 (180) hide show
  1. package/README.md +79 -274
  2. package/dist/agent-BXRIEKVM.js +238 -0
  3. package/dist/agent-BXRIEKVM.js.map +1 -0
  4. package/dist/{analyze-XV3K25K3.js → analyze-WS47UYGN.js} +32 -15
  5. package/dist/analyze-WS47UYGN.js.map +1 -0
  6. package/dist/{apply-WUJHDD7A.js → apply-OS62UWRN.js} +15 -5
  7. package/dist/{apply-WUJHDD7A.js.map → apply-OS62UWRN.js.map} +1 -1
  8. package/dist/bench-SA3BOME5.js +906 -0
  9. package/dist/bench-SA3BOME5.js.map +1 -0
  10. package/dist/chunk-37N2RJW2.js +120 -0
  11. package/dist/chunk-37N2RJW2.js.map +1 -0
  12. package/dist/chunk-3XLCNVMD.js +180 -0
  13. package/dist/chunk-3XLCNVMD.js.map +1 -0
  14. package/dist/chunk-5TB6KLC2.js +72 -0
  15. package/dist/chunk-5TB6KLC2.js.map +1 -0
  16. package/dist/{chunk-OBLVLIUS.js → chunk-664Y3NDX.js} +25 -13
  17. package/dist/chunk-664Y3NDX.js.map +1 -0
  18. package/dist/chunk-6FDFRNXZ.js +357 -0
  19. package/dist/chunk-6FDFRNXZ.js.map +1 -0
  20. package/dist/chunk-7KD66LIM.js +658 -0
  21. package/dist/chunk-7KD66LIM.js.map +1 -0
  22. package/dist/chunk-7Z4Y3VKM.js +26 -0
  23. package/dist/chunk-7Z4Y3VKM.js.map +1 -0
  24. package/dist/chunk-ARFVHC2F.js +1111 -0
  25. package/dist/chunk-ARFVHC2F.js.map +1 -0
  26. package/dist/{chunk-RKTEV6EG.js → chunk-BHJCGWQX.js} +141 -12
  27. package/dist/chunk-BHJCGWQX.js.map +1 -0
  28. package/dist/chunk-BQDQTIM7.js +74 -0
  29. package/dist/chunk-BQDQTIM7.js.map +1 -0
  30. package/dist/chunk-CNBYCMSU.js +2709 -0
  31. package/dist/chunk-CNBYCMSU.js.map +1 -0
  32. package/dist/chunk-CRGIQFUQ.js +83 -0
  33. package/dist/chunk-CRGIQFUQ.js.map +1 -0
  34. package/dist/chunk-DIG5XKQQ.js +298 -0
  35. package/dist/chunk-DIG5XKQQ.js.map +1 -0
  36. package/dist/chunk-FZDR7CGH.js +71 -0
  37. package/dist/chunk-FZDR7CGH.js.map +1 -0
  38. package/dist/chunk-HEF2RMKN.js +413 -0
  39. package/dist/chunk-HEF2RMKN.js.map +1 -0
  40. package/dist/{chunk-IVHZEYAX.js → chunk-HJGGNLWI.js} +25 -19
  41. package/dist/chunk-HJGGNLWI.js.map +1 -0
  42. package/dist/chunk-HQAHNIAR.js +283 -0
  43. package/dist/chunk-HQAHNIAR.js.map +1 -0
  44. package/dist/{chunk-GANSDJV7.js → chunk-IHIEXUTC.js} +46 -24
  45. package/dist/chunk-IHIEXUTC.js.map +1 -0
  46. package/dist/chunk-IV5SS4JN.js +84 -0
  47. package/dist/chunk-IV5SS4JN.js.map +1 -0
  48. package/dist/{chunk-HC2MT7BX.js → chunk-IYV6KNVF.js} +158 -592
  49. package/dist/chunk-IYV6KNVF.js.map +1 -0
  50. package/dist/{chunk-LVKQ2AKT.js → chunk-J62QSVP6.js} +10 -8
  51. package/dist/chunk-J62QSVP6.js.map +1 -0
  52. package/dist/chunk-KMMASOA6.js +134 -0
  53. package/dist/chunk-KMMASOA6.js.map +1 -0
  54. package/dist/chunk-KQHB7ZT3.js +559 -0
  55. package/dist/chunk-KQHB7ZT3.js.map +1 -0
  56. package/dist/chunk-L6HSKTQE.js +77 -0
  57. package/dist/chunk-L6HSKTQE.js.map +1 -0
  58. package/dist/{chunk-6N7LZZ5U.js → chunk-LZ6GFQAG.js} +474 -160
  59. package/dist/chunk-LZ6GFQAG.js.map +1 -0
  60. package/dist/{chunk-HQYXVIH6.js → chunk-MIVHK5YI.js} +5 -5
  61. package/dist/chunk-NEZZ6KDR.js +1217 -0
  62. package/dist/chunk-NEZZ6KDR.js.map +1 -0
  63. package/dist/{run-DBF2VWUN.js → chunk-OPNGPUKJ.js} +747 -594
  64. package/dist/chunk-OPNGPUKJ.js.map +1 -0
  65. package/dist/chunk-P3LRUNC6.js +16 -0
  66. package/dist/chunk-P3LRUNC6.js.map +1 -0
  67. package/dist/{chunk-BXCPXRBR.js → chunk-PIVGJM6K.js} +129 -32
  68. package/dist/chunk-PIVGJM6K.js.map +1 -0
  69. package/dist/{chunk-AAY6KM7N.js → chunk-PV4OX5TC.js} +167 -196
  70. package/dist/chunk-PV4OX5TC.js.map +1 -0
  71. package/dist/chunk-QCXPNSEL.js +141 -0
  72. package/dist/chunk-QCXPNSEL.js.map +1 -0
  73. package/dist/{chunk-34WNTK3K.js → chunk-QXDNBYQF.js} +53 -13
  74. package/dist/chunk-QXDNBYQF.js.map +1 -0
  75. package/dist/chunk-SNTTOQAD.js +30 -0
  76. package/dist/chunk-SNTTOQAD.js.map +1 -0
  77. package/dist/{chunk-EY6WMHV2.js → chunk-TM67EHSV.js} +67 -23
  78. package/dist/chunk-TM67EHSV.js.map +1 -0
  79. package/dist/chunk-TVN2JVOF.js +77 -0
  80. package/dist/chunk-TVN2JVOF.js.map +1 -0
  81. package/dist/chunk-TX5WIVYP.js +129 -0
  82. package/dist/chunk-TX5WIVYP.js.map +1 -0
  83. package/dist/{chunk-KQ4JQN7T.js → chunk-UJ7XWK7J.js} +83 -21
  84. package/dist/chunk-UJ7XWK7J.js.map +1 -0
  85. package/dist/{chunk-47VIN33V.js → chunk-UOCPC2Y2.js} +596 -17
  86. package/dist/chunk-UOCPC2Y2.js.map +1 -0
  87. package/dist/chunk-XEYH6ZZI.js +289 -0
  88. package/dist/chunk-XEYH6ZZI.js.map +1 -0
  89. package/dist/cli.js +390 -40
  90. package/dist/cli.js.map +1 -1
  91. package/dist/config-MEM2IWWN.js +76 -0
  92. package/dist/contract-JH2WBIO6.js +8 -0
  93. package/dist/conventions-WV4NDXGH.js +143 -0
  94. package/dist/conventions-WV4NDXGH.js.map +1 -0
  95. package/dist/execute-NITIHHCK.js +31 -0
  96. package/dist/{generate-TBVTLMOP.js → generate-MVQI7A45.js} +39 -19
  97. package/dist/{generate-TBVTLMOP.js.map → generate-MVQI7A45.js.map} +1 -1
  98. package/dist/git-YRVRUN3C.js +31 -0
  99. package/dist/init-OKWEC3EV.js +21 -0
  100. package/dist/operations-LU6JA3BX.js +374 -0
  101. package/dist/operations-LU6JA3BX.js.map +1 -0
  102. package/dist/permissions-7NMDFU27.js +18 -0
  103. package/dist/plan-LEJJF3DE.js +31 -0
  104. package/dist/{policy-XQW5NUIE.js → policy-JSYEK5B2.js} +17 -4
  105. package/dist/policy-JSYEK5B2.js.map +1 -0
  106. package/dist/pr-HPVUZ7S3.js +36 -0
  107. package/dist/pr-review-R2QZBT3F.js +29 -0
  108. package/dist/pr-review-R2QZBT3F.js.map +1 -0
  109. package/dist/prd-5O6GYCC6.js +30 -0
  110. package/dist/prd-5O6GYCC6.js.map +1 -0
  111. package/dist/ps-TNE44R5V.js +90 -0
  112. package/dist/ps-TNE44R5V.js.map +1 -0
  113. package/dist/recorder-XGF55WXI.js +31 -0
  114. package/dist/recorder-XGF55WXI.js.map +1 -0
  115. package/dist/registry-LGPE53QD.js +15 -0
  116. package/dist/registry-LGPE53QD.js.map +1 -0
  117. package/dist/resume-QMEPMSZW.js +238 -0
  118. package/dist/resume-QMEPMSZW.js.map +1 -0
  119. package/dist/review-YKWD6SNN.js +32 -0
  120. package/dist/review-YKWD6SNN.js.map +1 -0
  121. package/dist/routing-2ZUMZPJB.js +91 -0
  122. package/dist/routing-2ZUMZPJB.js.map +1 -0
  123. package/dist/run-YU4TR62R.js +50 -0
  124. package/dist/run-YU4TR62R.js.map +1 -0
  125. package/dist/state-manager-X3PT3FGT.js +36 -0
  126. package/dist/state-manager-X3PT3FGT.js.map +1 -0
  127. package/dist/usage-VCFLDA5Y.js +174 -0
  128. package/dist/usage-VCFLDA5Y.js.map +1 -0
  129. package/dist/{web-ANQQTTRD.js → web-XVHLX62B.js} +43 -27
  130. package/dist/web-XVHLX62B.js.map +1 -0
  131. package/package.json +6 -2
  132. package/prompts/execute.md +7 -8
  133. package/prompts/plan.md +4 -5
  134. package/prompts/pr.md +13 -6
  135. package/web/AGENTS.md +3 -3
  136. package/web/public/app.css +38 -0
  137. package/web/public/app.js +132 -10
  138. package/web/public/index.html +24 -0
  139. package/dist/analyze-XV3K25K3.js.map +0 -1
  140. package/dist/chunk-34WNTK3K.js.map +0 -1
  141. package/dist/chunk-3BOHTH4V.js +0 -58
  142. package/dist/chunk-3BOHTH4V.js.map +0 -1
  143. package/dist/chunk-47VIN33V.js.map +0 -1
  144. package/dist/chunk-6N7LZZ5U.js.map +0 -1
  145. package/dist/chunk-AAY6KM7N.js.map +0 -1
  146. package/dist/chunk-BXCPXRBR.js.map +0 -1
  147. package/dist/chunk-EWDJENFW.js +0 -303
  148. package/dist/chunk-EWDJENFW.js.map +0 -1
  149. package/dist/chunk-EY6WMHV2.js.map +0 -1
  150. package/dist/chunk-GANSDJV7.js.map +0 -1
  151. package/dist/chunk-HC2MT7BX.js.map +0 -1
  152. package/dist/chunk-HDV3YDTG.js +0 -52
  153. package/dist/chunk-HDV3YDTG.js.map +0 -1
  154. package/dist/chunk-IVHZEYAX.js.map +0 -1
  155. package/dist/chunk-KQ4JQN7T.js.map +0 -1
  156. package/dist/chunk-LVKQ2AKT.js.map +0 -1
  157. package/dist/chunk-OBLVLIUS.js.map +0 -1
  158. package/dist/chunk-RKTEV6EG.js.map +0 -1
  159. package/dist/chunk-VQ5VN7DF.js +0 -1018
  160. package/dist/chunk-VQ5VN7DF.js.map +0 -1
  161. package/dist/config-4THHOXNN.js +0 -41
  162. package/dist/execute-5OEKLM5X.js +0 -15
  163. package/dist/init-PRMOTQ7N.js +0 -9
  164. package/dist/plan-Y2BYZD24.js +0 -17
  165. package/dist/policy-XQW5NUIE.js.map +0 -1
  166. package/dist/pr-H6GLOSXF.js +0 -21
  167. package/dist/pr-review-CK5FR6WX.js +0 -17
  168. package/dist/prd-4PWLCGH5.js +0 -17
  169. package/dist/review-QGVAHEZF.js +0 -15
  170. package/dist/run-DBF2VWUN.js.map +0 -1
  171. package/dist/web-ANQQTTRD.js.map +0 -1
  172. /package/dist/{chunk-HQYXVIH6.js.map → chunk-MIVHK5YI.js.map} +0 -0
  173. /package/dist/{config-4THHOXNN.js.map → config-MEM2IWWN.js.map} +0 -0
  174. /package/dist/{execute-5OEKLM5X.js.map → contract-JH2WBIO6.js.map} +0 -0
  175. /package/dist/{init-PRMOTQ7N.js.map → execute-NITIHHCK.js.map} +0 -0
  176. /package/dist/{plan-Y2BYZD24.js.map → git-YRVRUN3C.js.map} +0 -0
  177. /package/dist/{pr-H6GLOSXF.js.map → init-OKWEC3EV.js.map} +0 -0
  178. /package/dist/{pr-review-CK5FR6WX.js.map → permissions-7NMDFU27.js.map} +0 -0
  179. /package/dist/{prd-4PWLCGH5.js.map → plan-LEJJF3DE.js.map} +0 -0
  180. /package/dist/{review-QGVAHEZF.js.map → pr-HPVUZ7S3.js.map} +0 -0
@@ -1,37 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ listPullRequests
4
+ } from "./chunk-J62QSVP6.js";
5
+ import {
6
+ isTransientFailure,
3
7
  runPhaseWithRetry
4
- } from "./chunk-3BOHTH4V.js";
8
+ } from "./chunk-IV5SS4JN.js";
5
9
  import {
6
10
  issuePlaceholders,
7
11
  resolveCommandIssue
8
- } from "./chunk-AAY6KM7N.js";
9
- import {
10
- DEFAULT_HEADLESS_TIMEOUT_MS,
11
- runHeadless
12
- } from "./chunk-EWDJENFW.js";
13
- import {
14
- isTransientFailure
15
- } from "./chunk-HDV3YDTG.js";
12
+ } from "./chunk-FZDR7CGH.js";
16
13
  import {
17
14
  publishPhaseMetrics,
18
15
  resolvePolicyPlaceholders
19
- } from "./chunk-OBLVLIUS.js";
16
+ } from "./chunk-664Y3NDX.js";
20
17
  import {
21
18
  applyPlaceholders,
22
19
  loadPrompt
23
- } from "./chunk-HQYXVIH6.js";
20
+ } from "./chunk-MIVHK5YI.js";
21
+ import {
22
+ DEFAULT_HEADLESS_TIMEOUT_MS,
23
+ runHeadless
24
+ } from "./chunk-ARFVHC2F.js";
25
+ import {
26
+ issueReferenceLines,
27
+ loadRepositoryPolicy,
28
+ pullRequestTitle,
29
+ resolveChangeType
30
+ } from "./chunk-UOCPC2Y2.js";
24
31
  import {
25
32
  resolveIssuePaths
26
- } from "./chunk-VQ5VN7DF.js";
33
+ } from "./chunk-HQAHNIAR.js";
27
34
  import {
28
35
  getGlobalTimeout,
36
+ printError,
37
+ printSuccess
38
+ } from "./chunk-IYV6KNVF.js";
39
+ import {
29
40
  isoNow,
30
41
  loadTaskPlan,
31
- printError,
32
- printSuccess,
33
42
  saveTaskPlan
34
- } from "./chunk-HC2MT7BX.js";
43
+ } from "./chunk-DIG5XKQQ.js";
35
44
 
36
45
  // src/commands/pr.ts
37
46
  import { execa } from "execa";
@@ -43,25 +52,48 @@ function parsePrNumber(url) {
43
52
  const match = url.match(/\/pull\/(\d+)/);
44
53
  return match?.[1] === void 0 ? null : Number(match[1]);
45
54
  }
46
- function issueClosesLine(issue, fallbackId) {
55
+ function issueClosesLine(issue, fallbackId, complete) {
47
56
  if (issue.remoteRef === null) {
48
57
  return "";
49
58
  }
50
- return `Closes #${issue.number ?? fallbackId}`;
59
+ const number = issue.number ?? (/^\d+$/.test(fallbackId) ? Number(fallbackId) : null);
60
+ if (number === null) {
61
+ return "";
62
+ }
63
+ return issueReferenceLines({ references: [{ number, complete }] });
51
64
  }
52
65
  function issueRef(entry) {
53
66
  return entry.number === null ? entry.id : `#${entry.number}`;
54
67
  }
55
68
  function issueClosesLines(issues) {
56
- return issues.filter((entry) => entry.source === "github" && entry.number !== null).map((entry) => `Closes #${entry.number}`).join("\n");
69
+ return issueReferenceLines({
70
+ references: issues.filter((entry) => entry.source === "github" && entry.number !== null).map((entry) => ({
71
+ number: entry.number,
72
+ complete: entry.complete !== false
73
+ }))
74
+ });
57
75
  }
58
76
  function multiIssueContext(queue) {
59
77
  if (queue === void 0 || queue.issues.length <= 1) {
60
78
  return "";
61
79
  }
62
- const order = queue.issues.map(
63
- (entry, index) => `${index + 1}. ${issueRef(entry)}${entry.title ? ` \u2014 ${entry.title}` : ""}`
64
- ).join("\n");
80
+ const byId = new Map(queue.issues.map((entry) => [entry.id, entry]));
81
+ const depthOf = (entry) => {
82
+ let depth = 0;
83
+ let parent = entry.parent ?? null;
84
+ const seen = /* @__PURE__ */ new Set();
85
+ while (parent !== null && !seen.has(parent)) {
86
+ seen.add(parent);
87
+ depth += 1;
88
+ parent = byId.get(parent)?.parent ?? null;
89
+ }
90
+ return depth;
91
+ };
92
+ const order = queue.issues.map((entry, index) => {
93
+ const indent = " ".repeat(depthOf(entry));
94
+ const role = entry.role === "container" ? " (container)" : "";
95
+ return `${index + 1}. ${indent}${issueRef(entry)}${entry.title ? ` \u2014 ${entry.title}` : ""}${role}`;
96
+ }).join("\n");
65
97
  const pending = [
66
98
  ...queue.excluded.map(
67
99
  (entry) => `- ${issueRef(entry)}${entry.title ? ` \u2014 ${entry.title}` : ""}: ${entry.reason}`
@@ -88,6 +120,34 @@ function multiIssueContext(queue) {
88
120
  "how you tell which change belongs to which issue in `git log`."
89
121
  ].join("\n");
90
122
  }
123
+ async function adoptExistingPullRequest(branchName, tasksPath, issueNumber) {
124
+ let open;
125
+ try {
126
+ open = await listPullRequests(branchName, { state: "open" });
127
+ } catch {
128
+ return false;
129
+ }
130
+ const existing = open[0];
131
+ if (existing === void 0) return false;
132
+ try {
133
+ const plan = await loadTaskPlan(tasksPath);
134
+ plan.pipeline.prCreated = true;
135
+ plan.pullRequest = {
136
+ number: existing.number,
137
+ url: existing.url,
138
+ headBranch: branchName,
139
+ // The Pull Request predates this attempt; what is recorded is when this
140
+ // run adopted it, which is the only timestamp this process can vouch for.
141
+ createdAt: isoNow()
142
+ };
143
+ await saveTaskPlan(tasksPath, plan);
144
+ } catch {
145
+ }
146
+ printSuccess(
147
+ `Pull Request already open for ${branchName}: ${existing.url}. Adopting it instead of opening a second one for issue #${issueNumber}.`
148
+ );
149
+ return true;
150
+ }
91
151
  async function runPr(issue, resolvedIssue, options = {}) {
92
152
  const issueNumber = issue.replace(/^#/, "");
93
153
  const paths = await resolveIssuePaths(issueNumber);
@@ -108,8 +168,28 @@ async function runPr(issue, resolvedIssue, options = {}) {
108
168
  printError("Failed to get current branch");
109
169
  return 1;
110
170
  }
171
+ const adopted = await adoptExistingPullRequest(branchName, tasksPath, issueNumber);
172
+ if (adopted) return 0;
111
173
  const queue = options.queue;
112
174
  const consolidating = queue !== void 0 && queue.issues.length > 1;
175
+ let planComplete = false;
176
+ try {
177
+ const existing = await loadTaskPlan(tasksPath);
178
+ planComplete = existing.userStories.every((story) => story.passes) && (existing.lastReviewFindings === null || existing.lastReviewFindings === "");
179
+ } catch {
180
+ planComplete = false;
181
+ }
182
+ const policy = await loadRepositoryPolicy();
183
+ const change = resolveChangeType({
184
+ labels: resolution.resolved.issue.labels,
185
+ title: resolution.resolved.issue.title,
186
+ titleConvention: policy.issues.titleConvention,
187
+ typeMap: policy.git.typeMap
188
+ });
189
+ const prTitle = pullRequestTitle({
190
+ type: change.type,
191
+ subject: resolution.resolved.issue.title.replace(/^\s*\[[^\]]+\]\s*/, "")
192
+ });
113
193
  const template = await loadPrompt("pr");
114
194
  const prompt = applyPlaceholders(template, {
115
195
  // The repository's own conventions. Empty when it declares none, which is
@@ -118,7 +198,8 @@ async function runPr(issue, resolvedIssue, options = {}) {
118
198
  __ISSUE_NUMBER__: issueNumber,
119
199
  __BRANCH_NAME__: branchName,
120
200
  __TASKS_PATH__: tasksPath,
121
- __ISSUE_CLOSES__: consolidating ? issueClosesLines(queue.issues) : issueClosesLine(resolution.resolved.issue, issueNumber),
201
+ __PR_TITLE_CONVENTION__: prTitle,
202
+ __ISSUE_REFERENCE__: consolidating ? issueClosesLines(queue.issues) : issueClosesLine(resolution.resolved.issue, issueNumber, planComplete),
122
203
  __MULTI_ISSUE_CONTEXT__: multiIssueContext(queue),
123
204
  ...issuePlaceholders(resolution.resolved)
124
205
  });
@@ -131,18 +212,24 @@ async function runPr(issue, resolvedIssue, options = {}) {
131
212
  prompt,
132
213
  maxTurns: 15,
133
214
  timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,
215
+ timeoutHistory: {
216
+ phase: "pr",
217
+ journalFiles: [paths.rotatedEventsFile, paths.eventsFile]
218
+ },
134
219
  // json (not text) so the CLI reports usage: the envelope's `result`
135
220
  // field carries the same assistant text parsePrUrl() already consumed.
136
221
  outputFormat: "json",
137
222
  allowedTools: ["Bash", "Read", "Glob", "Grep"],
138
223
  addDirs: [paths.issueDir],
139
- statusMessage: `Creating PR for issue #${issueNumber}...`
224
+ statusMessage: `Creating PR for issue #${issueNumber}...`,
225
+ phase: "pr",
226
+ permission: "workspace"
140
227
  });
141
- publishPhaseMetrics("pr", result.cost, startedAtMs);
228
+ publishPhaseMetrics("pr", result.cost, startedAtMs, result.agent?.provider);
142
229
  if (!result.success) {
143
230
  return {
144
231
  ok: false,
145
- transient: isTransientFailure(1, result.error ?? ""),
232
+ transient: result.retryExhausted !== true && isTransientFailure(1, result.error ?? ""),
146
233
  error: `PR creation failed: ${result.error}`
147
234
  };
148
235
  }
@@ -155,14 +242,24 @@ async function runPr(issue, resolvedIssue, options = {}) {
155
242
  return 1;
156
243
  }
157
244
  const prUrl = parsePrUrl(headlessOutput);
245
+ const parsedPrNumber = prUrl === null ? null : parsePrNumber(prUrl);
246
+ let pullRequest = prUrl === null || parsedPrNumber === null ? null : { number: parsedPrNumber, url: prUrl };
247
+ if (pullRequest === null) {
248
+ try {
249
+ const [discovered] = await listPullRequests(branchName, { state: "open" });
250
+ if (discovered !== void 0) {
251
+ pullRequest = { number: discovered.number, url: discovered.url };
252
+ }
253
+ } catch {
254
+ }
255
+ }
158
256
  try {
159
257
  const plan = await loadTaskPlan(tasksPath);
160
258
  plan.pipeline.prCreated = true;
161
- const prNumber = prUrl === null ? null : parsePrNumber(prUrl);
162
- if (prUrl !== null && prNumber !== null) {
259
+ if (pullRequest !== null) {
163
260
  plan.pullRequest = {
164
- number: prNumber,
165
- url: prUrl,
261
+ number: pullRequest.number,
262
+ url: pullRequest.url,
166
263
  headBranch: branchName,
167
264
  createdAt: isoNow()
168
265
  };
@@ -170,8 +267,8 @@ async function runPr(issue, resolvedIssue, options = {}) {
170
267
  await saveTaskPlan(tasksPath, plan);
171
268
  } catch {
172
269
  }
173
- if (prUrl) {
174
- printSuccess(`PR created: ${prUrl}`);
270
+ if (pullRequest !== null) {
271
+ printSuccess(`PR created: ${pullRequest.url}`);
175
272
  } else {
176
273
  printSuccess("PR creation completed");
177
274
  }
@@ -183,4 +280,4 @@ export {
183
280
  multiIssueContext,
184
281
  runPr
185
282
  };
186
- //# sourceMappingURL=chunk-BXCPXRBR.js.map
283
+ //# sourceMappingURL=chunk-PIVGJM6K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/pr.ts"],"sourcesContent":["import { execa } from 'execa';\nimport {\n issueReferenceLines,\n pullRequestTitle,\n resolveChangeType,\n} from '../conventions/git/index.js';\nimport { DEFAULT_HEADLESS_TIMEOUT_MS, runHeadless } from '../core/headless.js';\nimport { runPhaseWithRetry } from '../core/phase-runner.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { listPullRequests } from '../core/session-git.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, IssueSource, ResolvedIssue } from '../issues/types.js';\nimport { loadRepositoryPolicy } from '../policy/index.js';\nimport { resolvePolicyPlaceholders } from '../policy/placeholders.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, complete: boolean): string {\n if (issue.remoteRef === null) {\n return '';\n }\n const number = issue.number ?? (/^\\d+$/.test(fallbackId) ? Number(fallbackId) : null);\n if (number === null) {\n return '';\n }\n return issueReferenceLines({ references: [{ number, complete }] });\n}\n\n/** One Issue of a queue, as the consolidated Pull Request has to describe it. */\nexport interface PrQueueIssue {\n id: string;\n number: number | null;\n title: string;\n /** `null` when the Issue was never read — see {@link issueClosesLines}. */\n url: string | null;\n /** Origin the Issue came from; `github` is what GitHub can close. */\n source: IssueSource;\n parent?: string | null;\n role?: 'executable' | 'container';\n /** When false, the body uses `Refs` instead of `Closes`. */\n complete?: boolean;\n}\n\n/**\n * What a multi-issue queue hands to the `pr` phase.\n *\n * Absent for every standalone run, and that absence is what guarantees the\n * single-issue Pull Request is byte-for-byte the one this command has always\n * produced: the extra placeholders resolve to empty strings.\n */\nexport interface PrQueueContext {\n /** Issues of the queue, in the order they were executed. */\n issues: PrQueueIssue[];\n /** Issues discovered but not executed, with the reason. */\n excluded: { id: string; number: number | null; title: string; reason: string }[];\n /** Anything else worth reporting as pending (unresolved review findings). */\n pending: string[];\n}\n\nexport interface RunPrOptions {\n /** Set only when the Pull Request consolidates a queue. */\n queue?: PrQueueContext;\n}\n\n/** `#51` when the Issue has a number, its raw identifier otherwise. */\nfunction issueRef(entry: { id: string; number: number | null }): string {\n return entry.number === null ? entry.id : `#${entry.number}`;\n}\n\n/**\n * Every `Closes #N` line of a consolidated Pull Request, one per line.\n *\n * Issues that GitHub does not host are skipped for the same reason a\n * single-issue Pull Request skips them — it cannot close what it does not\n * host — and a queue that mixes both origins still gets a valid body.\n *\n * The test is the origin and the number, not the URL: an Issue whose read\n * failed during discovery reaches the queue with `url: null`, and it is still\n * executed and still closed by the queue. Filtering on the URL would leave it\n * out of the very body that is supposed to reference it.\n */\nexport function issueClosesLines(issues: readonly PrQueueIssue[]): string {\n return issueReferenceLines({\n references: issues\n .filter((entry) => entry.source === 'github' && entry.number !== null)\n .map((entry) => ({\n number: entry.number as number,\n complete: entry.complete !== false,\n })),\n });\n}\n\n/**\n * The extra instructions the prompt receives when the Pull Request covers a\n * whole queue.\n *\n * Returns `''` for a standalone run, which is what keeps `pr.md` rendering\n * exactly as before for the single-issue path — no empty \"Issues implemented\"\n * section, no redundant ordering of a list of one.\n */\nexport function multiIssueContext(queue: PrQueueContext | undefined): string {\n if (queue === undefined || queue.issues.length <= 1) {\n return '';\n }\n\n const byId = new Map(queue.issues.map((entry) => [entry.id, entry]));\n const depthOf = (entry: PrQueueIssue): number => {\n let depth = 0;\n let parent = entry.parent ?? null;\n const seen = new Set<string>();\n while (parent !== null && !seen.has(parent)) {\n seen.add(parent);\n depth += 1;\n parent = byId.get(parent)?.parent ?? null;\n }\n return depth;\n };\n const order = queue.issues\n .map((entry, index) => {\n const indent = ' '.repeat(depthOf(entry));\n const role = entry.role === 'container' ? ' (container)' : '';\n return `${index + 1}. ${indent}${issueRef(entry)}${entry.title ? ` — ${entry.title}` : ''}${role}`;\n })\n .join('\\n');\n\n const pending = [\n ...queue.excluded.map(\n (entry) => `- ${issueRef(entry)}${entry.title ? ` — ${entry.title}` : ''}: ${entry.reason}`,\n ),\n ...queue.pending.map((note) => `- ${note}`),\n ].join('\\n');\n\n return [\n '',\n 'This Pull Request consolidates several issues implemented on this same branch,',\n 'in this execution order:',\n '',\n order,\n '',\n 'The PR body MUST additionally contain:',\n '- an \"Issues implemented\" section listing every issue above, in that order,',\n ' with one line per issue describing what it delivered;',\n '- a \"Pending\" section with the items below, verbatim, plus anything you find',\n ' unfinished while reviewing the diff. Write \"None\" when the list below is',\n ' empty and you find nothing else;',\n '',\n pending === '' ? '(no known pending items)' : pending,\n '',\n 'The commits of this branch are scoped per issue (`feat(issue-N): …`), which is',\n 'how you tell which change belongs to which issue in `git log`.',\n ].join('\\n');\n}\n\n/**\n * Adopt the Pull Request that is already open for this branch, if there is one.\n *\n * \"Already open\" is the only safe reading: a *closed* Pull Request for the same\n * branch is a decision someone made, and reopening or reusing it would undo it.\n * A merged one means the work is done and a new branch is the answer.\n *\n * Returns `true` when the phase has nothing left to do. Never throws — a `gh`\n * that cannot answer leaves the phase to run exactly as it did before.\n */\nasync function adoptExistingPullRequest(\n branchName: string,\n tasksPath: string,\n issueNumber: string,\n): Promise<boolean> {\n let open: Awaited<ReturnType<typeof listPullRequests>>;\n try {\n open = await listPullRequests(branchName, { state: 'open' });\n } catch {\n return false;\n }\n\n const existing = open[0];\n if (existing === undefined) return false;\n\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prCreated = true;\n plan.pullRequest = {\n number: existing.number,\n url: existing.url,\n headBranch: branchName,\n // The Pull Request predates this attempt; what is recorded is when this\n // run adopted it, which is the only timestamp this process can vouch for.\n createdAt: isoNow(),\n };\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // No plan yet: the adoption still stands, there is just nowhere to note it.\n }\n\n printSuccess(\n `Pull Request already open for ${branchName}: ${existing.url}. Adopting it instead of opening a second one for issue #${issueNumber}.`,\n );\n return true;\n}\n\nexport async function runPr(\n issue: string,\n resolvedIssue?: ResolvedIssue,\n options: RunPrOptions = {},\n): 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 // Idempotence, before anything is invoked.\n //\n // `runPhaseWithRetry` retries this phase up to three times, and the timeout\n // that used to end a run is now correctly classified as transient — so an\n // attempt that created the Pull Request and *then* timed out would have the\n // next attempt create a second one. Asking GitHub what already exists is the\n // cheap half of the fix; the expensive half would be undoing a duplicate.\n const adopted = await adoptExistingPullRequest(branchName, tasksPath, issueNumber);\n if (adopted) return 0;\n\n const queue = options.queue;\n const consolidating = queue !== undefined && queue.issues.length > 1;\n\n let planComplete = false;\n try {\n const existing = await loadTaskPlan(tasksPath);\n planComplete =\n existing.userStories.every((story) => story.passes) &&\n (existing.lastReviewFindings === null || existing.lastReviewFindings === '');\n } catch {\n planComplete = false;\n }\n\n const policy = await loadRepositoryPolicy();\n const change = resolveChangeType({\n labels: resolution.resolved.issue.labels,\n title: resolution.resolved.issue.title,\n titleConvention: policy.issues.titleConvention,\n typeMap: policy.git.typeMap,\n });\n const prTitle = pullRequestTitle({\n type: change.type,\n subject: resolution.resolved.issue.title.replace(/^\\s*\\[[^\\]]+\\]\\s*/, ''),\n });\n\n const template = await loadPrompt('pr');\n const prompt = applyPlaceholders(template, {\n // The repository's own conventions. Empty when it declares none, which is\n // what keeps the rendered prompt identical to the pre-policy one.\n ...(await resolvePolicyPlaceholders()),\n __ISSUE_NUMBER__: issueNumber,\n __BRANCH_NAME__: branchName,\n __TASKS_PATH__: tasksPath,\n __PR_TITLE_CONVENTION__: prTitle,\n __ISSUE_REFERENCE__: consolidating\n ? issueClosesLines(queue.issues)\n : issueClosesLine(resolution.resolved.issue, issueNumber, planComplete),\n __MULTI_ISSUE_CONTEXT__: multiIssueContext(queue),\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() ?? DEFAULT_HEADLESS_TIMEOUT_MS,\n timeoutHistory: {\n phase: 'pr',\n journalFiles: [paths.rotatedEventsFile, paths.eventsFile],\n },\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 phase: 'pr',\n permission: 'workspace',\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('pr', result.cost, startedAtMs, result.agent?.provider);\n\n if (!result.success) {\n return {\n ok: false,\n transient: result.retryExhausted !== true && 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 const parsedPrNumber = prUrl === null ? null : parsePrNumber(prUrl);\n let pullRequest =\n prUrl === null || parsedPrNumber === null ? null : { number: parsedPrNumber, url: prUrl };\n\n // The agent may create the Pull Request successfully but omit (or mangle)\n // its URL in the final answer. Ask GitHub again after the side effect so the\n // durable plan never loses a trustworthy PR that now exists for this branch.\n if (pullRequest === null) {\n try {\n const [discovered] = await listPullRequests(branchName, { state: 'open' });\n if (discovered !== undefined) {\n pullRequest = { number: discovered.number, url: discovered.url };\n }\n } catch {\n // With no trustworthy answer, keep the historical successful-but-unlinked state.\n }\n }\n\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prCreated = true;\n // Without an output URL or a post-create GitHub match the PR is still\n // assumed created (the phase succeeded), but no number is invented.\n if (pullRequest !== null) {\n plan.pullRequest = {\n number: pullRequest.number,\n url: pullRequest.url,\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 (pullRequest !== null) {\n printSuccess(`PR created: ${pullRequest.url}`);\n } else {\n printSuccess('PR creation completed');\n }\n return 0;\n}\n"],"mappings":"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","names":[]}