@joshuafolkken/kit 1.1094.0 → 1.1136.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 (177) hide show
  1. package/.claude/agents/investigator.md +17 -0
  2. package/.claude/settings.json +1 -0
  3. package/.claude/skills/workflow-commands/SKILL.md +1 -1
  4. package/.claude/skills/workflow-commands/background-commands.md +36 -85
  5. package/.claude/skills/workflow-commands/backlogrun-progress.md +31 -34
  6. package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -2
  7. package/.claude/skills/workflow-commands/delegation.md +5 -1
  8. package/.claude/skills/workflow-commands/fullrun.md +5 -4
  9. package/.claude/skills/workflow-commands/halfrun.md +5 -4
  10. package/.claude/skills/workflow-commands/latest-gate.md +7 -15
  11. package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
  12. package/.claude/skills/workflow-commands/split-assessment.md +4 -18
  13. package/.github/actions/setup-node/action.yml +33 -0
  14. package/.github/actions/setup-pnpm/action.yml +4 -4
  15. package/.github/workflows/auto-tag.yml +2 -3
  16. package/.github/workflows/ci.yml +7 -8
  17. package/.github/workflows/dependabot-auto-merge.yml +1 -0
  18. package/.github/workflows/pr-classification.yml +1 -3
  19. package/.github/workflows/production.yml +1 -0
  20. package/.github/workflows/publish.yml +10 -9
  21. package/.github/workflows/sonar-qube.yml +17 -13
  22. package/.pnpmfile.mjs +29 -0
  23. package/AGENTS.md +4 -11
  24. package/CLAUDE.md +24 -17
  25. package/GEMINI.md +4 -13
  26. package/README.md +8 -3
  27. package/SECURITY.md +2 -0
  28. package/cspell/index.yaml +4 -0
  29. package/dist/CLAUDE.basic.md +6 -6
  30. package/dist/CLAUDE.md +24 -17
  31. package/dist/CLAUDE.static.md +6 -6
  32. package/dist/config-merge/index.js +33 -9
  33. package/dist/hooks/batch-guard.js +4 -4
  34. package/dist/hooks/{chunk-OIZUHPJI.js → chunk-FO2I7UT7.js} +1 -1
  35. package/dist/hooks/{chunk-NKOG3XAV.js → chunk-GDU26K3J.js} +11 -3
  36. package/dist/hooks/{chunk-O4LL4HTI.js → chunk-HANSKHES.js} +2 -2
  37. package/dist/hooks/{chunk-3C4YCCEF.js → chunk-HBKZLITF.js} +4 -4
  38. package/dist/hooks/{chunk-UD24EAIL.js → chunk-JKKTVDAG.js} +1984 -146
  39. package/dist/hooks/{chunk-M2XDCUKD.js → chunk-KBXZLMRH.js} +89 -1803
  40. package/dist/hooks/{chunk-IL27YTPH.js → chunk-KOKOAV6D.js} +2 -2
  41. package/dist/hooks/{chunk-7ZPFAVR7.js → chunk-LJBLGHEH.js} +59 -19
  42. package/dist/hooks/{chunk-E54KGFEQ.js → chunk-NFZGFGOR.js} +1 -1
  43. package/dist/hooks/{chunk-KHWHKJOM.js → chunk-RFWWUI5F.js} +4 -4
  44. package/dist/hooks/codex-hook-adapter.js +12 -47
  45. package/dist/hooks/duplicate-read-guard.js +5 -5
  46. package/dist/hooks/format-edited.js +2 -2
  47. package/dist/hooks/inputs.json +175 -173
  48. package/dist/hooks/investigation-guard.js +5 -5
  49. package/dist/hooks/pretool-guard.js +9 -9
  50. package/dist/hooks/stop-guard.js +17 -12
  51. package/dist/josh.js +87 -31
  52. package/dist/version/index.js +36 -20
  53. package/eslint/rules/unicorn.js +3 -2
  54. package/package.json +23 -5
  55. package/pnpm-workspace.yaml +5 -7
  56. package/prompts/collaboration-workflow/glossary.md +17 -0
  57. package/prompts/collaboration-workflow/issue-template.md +1 -1
  58. package/prompts/collaboration-workflow/operating-rules.md +1 -1
  59. package/prompts/collaboration-workflow/principles.md +9 -0
  60. package/prompts/collaboration-workflow.md +1 -0
  61. package/scripts/backlog/backlog-arrival.ts +30 -10
  62. package/scripts/backlog/backlog-budget-cli.ts +15 -16
  63. package/scripts/backlog/backlog-offer-cli.ts +6 -14
  64. package/scripts/backlog/backlog-plan.ts +29 -18
  65. package/scripts/backlog/backlog-ready.ts +20 -7
  66. package/scripts/config-merge/parse-jsonc.ts +33 -3
  67. package/scripts/config-merge/patch-json-key.ts +13 -6
  68. package/scripts/cost-runtime/cost-cli.ts +3 -7
  69. package/scripts/delegation/delegation-policy.ts +9 -1
  70. package/scripts/delegation/investigation-guard.ts +1 -1
  71. package/scripts/delegation/investigation-reads.ts +105 -19
  72. package/scripts/doctor/doctor-consumer.ts +3 -5
  73. package/scripts/doctor/doctor-io.ts +9 -13
  74. package/scripts/document/backlogrun-parent-read-set.ts +12 -1
  75. package/scripts/document/capped-print.ts +74 -0
  76. package/scripts/document/document-byte-budget.ts +1 -0
  77. package/scripts/document/document-section-cli.ts +4 -3
  78. package/scripts/document/entry-read-budget.ts +13 -7
  79. package/scripts/document/entry-read-set.ts +32 -8
  80. package/scripts/document/lane-child-read-set.ts +7 -0
  81. package/scripts/document/read-set-point-of-use.ts +8 -3
  82. package/scripts/document/read-set-trim.ts +3 -1
  83. package/scripts/epic/epic-audit.ts +3 -18
  84. package/scripts/gate/scoped-green.ts +25 -14
  85. package/scripts/gh/git-gh-issue-list.ts +12 -11
  86. package/scripts/gh/git-gh-issue-write.ts +4 -1
  87. package/scripts/gh/git-gh-pr-auto-merge.ts +4 -1
  88. package/scripts/git/git-command.ts +6 -1
  89. package/scripts/git/git-spawn-sync.ts +65 -0
  90. package/scripts/git/git-spawn.ts +5 -4
  91. package/scripts/git/stash/stash-pop-args.ts +1 -6
  92. package/scripts/init/claude-kit-only-settings.ts +29 -0
  93. package/scripts/init/init-copy-content.ts +8 -2
  94. package/scripts/init/init-file-action.ts +54 -0
  95. package/scripts/init/init-logic-migrate.ts +35 -17
  96. package/scripts/init/init-logic-secretlint.ts +1 -1
  97. package/scripts/init/init-logic.ts +6 -22
  98. package/scripts/init/init.ts +11 -68
  99. package/scripts/init/initial-commit.ts +24 -0
  100. package/scripts/init/kit-written-paths.ts +2 -0
  101. package/scripts/init/project-profile.ts +3 -6
  102. package/scripts/init/start-exec.ts +16 -3
  103. package/scripts/init/start-steps.ts +10 -13
  104. package/scripts/issue/defect-rate-cli.ts +61 -20
  105. package/scripts/issue/issue-backlinks.ts +7 -14
  106. package/scripts/issue/issue-cite.ts +12 -6
  107. package/scripts/issue/issue-file.ts +5 -12
  108. package/scripts/issue/issue-read-cli.ts +1 -0
  109. package/scripts/issue/issue-report-failures.ts +6 -2
  110. package/scripts/issue/issue-state-cli.ts +1 -1
  111. package/scripts/issue/markdown-section.ts +21 -6
  112. package/scripts/issue/measure-rerun-cli.ts +73 -14
  113. package/scripts/josh/josh-commands-lane.ts +1 -1
  114. package/scripts/josh/josh-commands-project.ts +8 -1
  115. package/scripts/josh/josh-commands-workflow.ts +1 -1
  116. package/scripts/josh/run-github-prerequisite.ts +3 -6
  117. package/scripts/lane/lane-await.ts +89 -15
  118. package/scripts/lane/lane-cli.ts +11 -13
  119. package/scripts/lane/lane-close.ts +4 -1
  120. package/scripts/lane/lane-launch-cli.ts +10 -16
  121. package/scripts/lane/openai-review-broker.ts +4 -1
  122. package/scripts/lib/cli-flags.ts +31 -2
  123. package/scripts/lib/error-message.ts +12 -1
  124. package/scripts/lines/line-targets.ts +0 -11
  125. package/scripts/lines/lines-command.ts +5 -3
  126. package/scripts/propagate/propagate-git.ts +3 -8
  127. package/scripts/refactor/refactor-scan.ts +2 -1
  128. package/scripts/registry-migration/migrate-logic.ts +28 -19
  129. package/scripts/registry-migration/migrate.ts +6 -5
  130. package/scripts/release/github-release-cli.ts +14 -3
  131. package/scripts/release/github-release-environment.ts +1 -0
  132. package/scripts/release/github-release.ts +103 -17
  133. package/scripts/release/release-publish.ts +36 -2
  134. package/scripts/release/release-worktree.ts +97 -2
  135. package/scripts/review/review-checkout.ts +3 -2
  136. package/scripts/review/review-record-cli.ts +9 -16
  137. package/scripts/rules/early-heartbeat.ts +4 -3
  138. package/scripts/rules/lane-park.ts +9 -0
  139. package/scripts/rules/stop-rules.ts +7 -4
  140. package/scripts/run/run-carry-args.ts +8 -12
  141. package/scripts/run/run-carry-cli.ts +3 -1
  142. package/scripts/run/run-carry-conversation.ts +94 -0
  143. package/scripts/run/run-carry-reclaim.ts +37 -0
  144. package/scripts/run/run-carry.ts +21 -5
  145. package/scripts/run/run-cut-args.ts +1 -6
  146. package/scripts/run/run-ending-cli.ts +1 -5
  147. package/scripts/run/run-entry-cli.ts +40 -9
  148. package/scripts/run/run-entry-stop.ts +104 -0
  149. package/scripts/run/run-event-stream-emit.ts +7 -2
  150. package/scripts/run/run-hold.ts +4 -1
  151. package/scripts/run/run-liveness-cli.ts +1 -5
  152. package/scripts/run/run-merge-cli.ts +2 -10
  153. package/scripts/run/run-merge-steps.ts +4 -1
  154. package/scripts/run/run-parent-cut-hook.ts +4 -1
  155. package/scripts/run/run-prep-cli.ts +4 -1
  156. package/scripts/run/run-progress-args.ts +20 -4
  157. package/scripts/run/run-progress-cli.ts +31 -29
  158. package/scripts/run/run-progress-clock.ts +4 -1
  159. package/scripts/run/run-ship-cli.ts +8 -16
  160. package/scripts/run/run-ship-probe.ts +4 -1
  161. package/scripts/run/run-status-cli.ts +2 -15
  162. package/scripts/run/run-tidy-lanes.ts +4 -1
  163. package/scripts/run/run-wake-cli.ts +2 -10
  164. package/scripts/run/run-watcher-guard.ts +9 -5
  165. package/scripts/safe-chain/pack-hook.ts +92 -0
  166. package/scripts/sync/sync-ai-files.ts +4 -0
  167. package/scripts/test/test-declared-changed.ts +13 -16
  168. package/scripts/test/test-declared.ts +3 -7
  169. package/scripts/test/test-related.ts +1 -6
  170. package/scripts/test/test-unit-guard.ts +21 -7
  171. package/scripts/time-runtime/time-spans.ts +4 -0
  172. package/scripts/version/version-commands.ts +11 -1
  173. package/templates/CLAUDE.basic.md +6 -6
  174. package/templates/workflows/ci.yml +16 -82
  175. package/templates/workflows/dependabot-auto-merge.yml +1 -0
  176. package/templates/workflows/github-release.yml +14 -28
  177. package/tsconfig.sonar.json +4 -22
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  lane_child_marker,
3
3
  run_issue_number
4
- } from "./chunk-E54KGFEQ.js";
4
+ } from "./chunk-NFZGFGOR.js";
5
5
  import {
6
6
  PLATFORM_TEMP_ROOT,
7
7
  cost_blocks,
8
+ cost_transcript,
8
9
  cost_usage,
9
10
  file_reader,
10
11
  json_value,
@@ -13,7 +14,7 @@ import {
13
14
  time_density_hook,
14
15
  time_shell,
15
16
  time_transcript_line
16
- } from "./chunk-NKOG3XAV.js";
17
+ } from "./chunk-GDU26K3J.js";
17
18
  import {
18
19
  ENV_FILE_NAME,
19
20
  LANE_SEAT_KEY,
@@ -21,6 +22,41 @@ import {
21
22
  ports
22
23
  } from "./chunk-RKUAD3I6.js";
23
24
 
25
+ // scripts/lib/bounded-pool.ts
26
+ function pool_width(limit, total) {
27
+ if (!Number.isFinite(limit)) return 1;
28
+ return Math.max(1, Math.min(Math.floor(limit), total));
29
+ }
30
+ function throw_first_failure(failures) {
31
+ if (failures.length === 0) return;
32
+ const [first] = failures;
33
+ throw first;
34
+ }
35
+ async function drain(pool, worker) {
36
+ for (let entry = pool.queue.shift(); entry !== void 0 && pool.failures.length === 0; entry = pool.queue.shift()) {
37
+ const [index, item] = entry;
38
+ try {
39
+ pool.results.push({ index, value: await worker(item, index) });
40
+ } catch (error) {
41
+ pool.failures.push(error);
42
+ }
43
+ }
44
+ }
45
+ async function bounded_map(items, limit, worker) {
46
+ const pool = {
47
+ failures: [],
48
+ queue: items.map((item, index) => [index, item]),
49
+ results: []
50
+ };
51
+ const consumers = Array.from({ length: pool_width(limit, items.length) }, async () => {
52
+ await drain(pool, worker);
53
+ });
54
+ await Promise.all(consumers);
55
+ throw_first_failure(pool.failures);
56
+ return pool.results.toSorted((left, right) => left.index - right.index).map((result) => result.value);
57
+ }
58
+ var bounded_pool = { bounded_map, pool_width };
59
+
24
60
  // scripts/rules/shell-segments.ts
25
61
  var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
26
62
  function segments_of(command) {
@@ -108,8 +144,8 @@ function current_turn(tail) {
108
144
  function turn_filing_count(tail) {
109
145
  return prior_filing_count(current_turn(tail));
110
146
  }
111
- function decide(_call, run2) {
112
- return prior_filing_count(time_density_hook.read_tail(run2.transcript)) >= CAP_REACHED;
147
+ function decide(_call, run3) {
148
+ return prior_filing_count(time_density_hook.read_tail(run3.transcript)) >= CAP_REACHED;
113
149
  }
114
150
  var FILING_CAP_REASON = `\u26D4 filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling \u2014 on reaching it, stop and report rather than filing more. Automatic filing is capped so a run cannot manufacture Issues faster than they can be closed (\`.claude/skills/workflow-commands/SKILL.md\` \u2192 \xA72d, \`observation-filing.md\`). A further Issue that genuinely must exist is a person's call, not this run's. **This rule fires on every filing past the cap, not once per run**, so reissuing the same filing will be refused again.`;
115
151
  var ROW = {
@@ -149,9 +185,6 @@ var git_utilities = {
149
185
  get_git_command_for_spawn
150
186
  };
151
187
 
152
- // scripts/git/git-push-transport.ts
153
- import { execa as execa2 } from "execa";
154
-
155
188
  // scripts/git/git-execa-error.ts
156
189
  function get_exit_code(error) {
157
190
  if (typeof error !== "object" || error === null) return void 0;
@@ -170,6 +203,9 @@ function create_spawn_error(command, exit_code) {
170
203
  return new Error(error_message, { cause: { exit_code: exit_code_string } });
171
204
  }
172
205
 
206
+ // scripts/git/git-push-transport.ts
207
+ import { execa as execa2 } from "execa";
208
+
173
209
  // scripts/git/git-ssh-keepalive.ts
174
210
  import { execa } from "execa";
175
211
  var SSH_KEEPALIVE_OPTIONS = "-o ServerAliveInterval=15 -o ServerAliveCountMax=3";
@@ -300,6 +336,44 @@ var git_spawn = {
300
336
  with_output_remote
301
337
  };
302
338
 
339
+ // scripts/lib/spawn-exit.ts
340
+ function read_spawn_stdout(result) {
341
+ return result.stdout ?? "";
342
+ }
343
+ function read_spawn_stderr(result) {
344
+ return result.stderr ?? "";
345
+ }
346
+
347
+ // scripts/git/git-spawn-sync.ts
348
+ import { execaSync } from "execa";
349
+ var GIT_TIMEOUT_MS = 1e4;
350
+ var SUCCESS_EXIT_CODE = 0;
351
+ var C_LOCALE = { LC_ALL: "C", LANGUAGE: "C" };
352
+ function run(args, options = {}) {
353
+ const result = execaSync(git_utilities.get_git_command_for_spawn(), args, {
354
+ ...options,
355
+ extendEnv: true,
356
+ reject: false,
357
+ timeout: options.timeout ?? GIT_TIMEOUT_MS
358
+ });
359
+ return {
360
+ exit_code: result.exitCode,
361
+ stdout: read_spawn_stdout(result),
362
+ stderr: read_spawn_stderr(result)
363
+ };
364
+ }
365
+ function read2(args, options = {}) {
366
+ const result = run(args, options);
367
+ return result.exit_code === SUCCESS_EXIT_CODE ? result.stdout.trim() : void 0;
368
+ }
369
+ function origin_url(cwd) {
370
+ return read2(["config", "--get", "remote.origin.url"], { cwd });
371
+ }
372
+ function toplevel(timeout = GIT_TIMEOUT_MS) {
373
+ return run(["rev-parse", "--show-toplevel"], { env: C_LOCALE, timeout });
374
+ }
375
+ var git_spawn_sync = { GIT_TIMEOUT_MS, C_LOCALE, run, read: read2, origin_url, toplevel };
376
+
303
377
  // scripts/git/git-command.ts
304
378
  async function branch() {
305
379
  return await git_spawn.read(["rev-parse", "--abbrev-ref", "HEAD"]);
@@ -309,7 +383,9 @@ async function status(directory) {
309
383
  return await git_spawn.read([...location, "status", PORCELAIN_FLAG, UNTRACKED_FILES_FLAG]);
310
384
  }
311
385
  async function repository_root() {
312
- return await git_spawn.read(["rev-parse", "--show-toplevel"]);
386
+ const result = git_spawn_sync.toplevel();
387
+ if (result.exit_code !== 0) throw create_spawn_error("rev-parse", result.exit_code);
388
+ return result.stdout.trim();
313
389
  }
314
390
  async function head_commit() {
315
391
  return await git_spawn.read(["rev-parse", "HEAD"]);
@@ -342,9 +418,9 @@ async function get_default_branch() {
342
418
  return DEFAULT_BRANCH_FALLBACK;
343
419
  }
344
420
  var MERGE_BASE_COMMAND = "merge-base";
345
- async function reference_exists(reference) {
421
+ async function reference_exists(reference2) {
346
422
  try {
347
- await git_spawn.read(["rev-parse", "--verify", "--quiet", reference]);
423
+ await git_spawn.read(["rev-parse", "--verify", "--quiet", reference2]);
348
424
  return true;
349
425
  } catch {
350
426
  return false;
@@ -582,6 +658,18 @@ var git_command = {
582
658
  is_upstream_not_set_error
583
659
  };
584
660
 
661
+ // scripts/lib/error-message.ts
662
+ function message_of(error) {
663
+ return error instanceof Error ? error.message : String(error);
664
+ }
665
+ var DEBUG_ENV_KEY = "JOSH_DEBUG";
666
+ function trace_swallowed(where, error) {
667
+ if ((process.env[DEBUG_ENV_KEY] ?? "").trim() === "") return;
668
+ process.stderr.write(`josh debug: ${where}: ${message_of(error)}
669
+ `);
670
+ }
671
+ var error_text = { message_of, trace_swallowed };
672
+
585
673
  // scripts/run/run-hold.ts
586
674
  import { z } from "zod";
587
675
  var HOLD_PREFIX = "josh-run-hold-";
@@ -657,7 +745,8 @@ async function is_tree_dirty(directory) {
657
745
  try {
658
746
  const status2 = await git_command.status(directory);
659
747
  return status2.trim() !== "";
660
- } catch {
748
+ } catch (error) {
749
+ error_text.trace_swallowed("run_hold.is_tree_dirty", error);
661
750
  return true;
662
751
  }
663
752
  }
@@ -692,8 +781,8 @@ function stale_message(hold) {
692
781
  function uncommitted_message(hold) {
693
782
  return `This working tree still has uncommitted changes, and its run record \u2014 held by ${describe_holder(hold)} \u2014 has expired. That is a run which stopped for a person rather than one that finished, so the tree is not free: commit or stash the work, or run \`${own_release_command(hold.issue)}\` once you are done with it, then ask again.`;
694
783
  }
695
- function race_message(read2, target) {
696
- if (read2.kind === "held") return held_message(read2.hold);
784
+ function race_message(read3, target) {
785
+ if (read3.kind === "held") return held_message(read3.hold);
697
786
  return `A record already exists at ${target} and this run did not write it \u2014 ${NOT_IDLE}. Either another run claimed the tree at the same moment, or the record belongs to another account. Run \`${FORCE_RELEASE_COMMAND}\`, and remove that file by hand if it is still there.`;
698
787
  }
699
788
  function unknown_message() {
@@ -1300,9 +1389,9 @@ function directories(cwd) {
1300
1389
  const key2 = cache_key(cwd);
1301
1390
  const known = answers.get(key2);
1302
1391
  if (known !== void 0) return known;
1303
- const read2 = read_directories(cwd);
1304
- if (read2.length > 0) answers.set(key2, read2);
1305
- return read2;
1392
+ const read3 = read_directories(cwd);
1393
+ if (read3.length > 0) answers.set(key2, read3);
1394
+ return read3;
1306
1395
  }
1307
1396
  function own(cwd) {
1308
1397
  return directories(cwd)[OWN_GIT_DIRECTORY];
@@ -1616,8 +1705,8 @@ function worktree_git_directory_sync() {
1616
1705
  }
1617
1706
  function carried_cut_sync(now = /* @__PURE__ */ new Date(), directory = worktree_git_directory_sync()) {
1618
1707
  if (directory === void 0) return void 0;
1619
- const read2 = read_cut(cut_path(directory), now);
1620
- return read2.kind === "carried" ? read2.cut : void 0;
1708
+ const read3 = read_cut(cut_path(directory), now);
1709
+ return read3.kind === "carried" ? read3.cut : void 0;
1621
1710
  }
1622
1711
  function lane_cut_sync(lane_directory) {
1623
1712
  const directory = git_common_directory.own(lane_directory);
@@ -1684,8 +1773,8 @@ function end_cut(target) {
1684
1773
  }
1685
1774
  function hold_of(directory) {
1686
1775
  if (directory === void 0) return void 0;
1687
- const read2 = run_hold.read_hold(run_hold.hold_path(directory));
1688
- return read2.kind === "held" || read2.kind === "stale" ? read2.hold : void 0;
1776
+ const read3 = run_hold.read_hold(run_hold.hold_path(directory));
1777
+ return read3.kind === "held" || read3.kind === "stale" ? read3.hold : void 0;
1689
1778
  }
1690
1779
  function fullrun_issue(hold) {
1691
1780
  return hold?.is_fullrun === true ? hold.issue : void 0;
@@ -1989,13 +2078,13 @@ function is_live_beacon(target) {
1989
2078
  timeout: PROBE_TIMEOUT_MS2
1990
2079
  }).status === 0;
1991
2080
  }
1992
- function has_matching_start(pid, recorded_start, read2) {
1993
- const observed = read2(pid);
2081
+ function has_matching_start(pid, recorded_start, read3) {
2082
+ const observed = read3(pid);
1994
2083
  return observed === void 0 ? void 0 : observed === recorded_start;
1995
2084
  }
1996
- function matches_recorded_start(pid, recorded_start, read2) {
2085
+ function matches_recorded_start(pid, recorded_start, read3) {
1997
2086
  const target = beacon_target(recorded_start);
1998
- return target === void 0 ? has_matching_start(pid, recorded_start, read2) : is_live_beacon(target);
2087
+ return target === void 0 ? has_matching_start(pid, recorded_start, read3) : is_live_beacon(target);
1999
2088
  }
2000
2089
  var own_probe = /* @__PURE__ */ new Map();
2001
2090
  function own_start() {
@@ -2006,10 +2095,10 @@ function own_fields() {
2006
2095
  const start = own_start();
2007
2096
  return { pid: process.pid, ...start !== void 0 && { process_start: start } };
2008
2097
  }
2009
- function is_same_process(pid, recorded_start, read2 = read_start) {
2098
+ function is_same_process(pid, recorded_start, read3 = read_start) {
2010
2099
  if (pid === void 0 || !is_live_pid(pid)) return false;
2011
2100
  if (recorded_start === void 0) return void 0;
2012
- return matches_recorded_start(pid, recorded_start, read2);
2101
+ return matches_recorded_start(pid, recorded_start, read3);
2013
2102
  }
2014
2103
  function is_own_process(pid, recorded_start) {
2015
2104
  return pid === process.pid && recorded_start === own_start();
@@ -2111,6 +2200,67 @@ function apply_change(target, carry, change, now = /* @__PURE__ */ new Date()) {
2111
2200
  }
2112
2201
  var run_carry_change = { apply_change };
2113
2202
 
2203
+ // scripts/run/run-carry-conversation.ts
2204
+ import { existsSync, statSync } from "node:fs";
2205
+ import path2 from "node:path";
2206
+
2207
+ // scripts/cost-runtime/own-session.ts
2208
+ var NOT_FOUND = -1;
2209
+ function session_id_of(environment2) {
2210
+ const value = environment2[agent_session_environment.SESSION_ID_KEY];
2211
+ return value === void 0 || value === "" ? void 0 : value;
2212
+ }
2213
+ function select_own_session(files, environment2) {
2214
+ const session_id = session_id_of(environment2);
2215
+ if (session_id === void 0) {
2216
+ return { kind: "own", index: cost_transcript.latest_own_index(files) };
2217
+ }
2218
+ const index = files.findIndex((file) => file.session_id === session_id);
2219
+ return index === NOT_FOUND ? { kind: "absent", session_id } : { kind: "own", index };
2220
+ }
2221
+ var own_session = {
2222
+ session_id_of,
2223
+ select_own_session
2224
+ };
2225
+
2226
+ // scripts/run/run-carry-conversation.ts
2227
+ var CONVERSATION_QUIET_MS = 6e5;
2228
+ function own_transcript(environment2 = process.env, directories2 = cost_transcript.transcript_directories(process.cwd())) {
2229
+ const session_id = own_session.session_id_of(environment2);
2230
+ if (session_id === void 0) return void 0;
2231
+ const name = `${session_id}${cost_transcript.TRANSCRIPT_EXTENSION}`;
2232
+ return directories2.map((directory) => path2.join(directory, name)).find((file) => existsSync(file));
2233
+ }
2234
+ function modified_ms(transcript) {
2235
+ try {
2236
+ return statSync(transcript).mtimeMs;
2237
+ } catch {
2238
+ return void 0;
2239
+ }
2240
+ }
2241
+ function is_recent(modified, now) {
2242
+ return modified !== void 0 && now.getTime() - modified <= CONVERSATION_QUIET_MS;
2243
+ }
2244
+ function is_conversation_live(carry, now = /* @__PURE__ */ new Date(), read_modified = modified_ms) {
2245
+ if (carry.is_handed_off === true || carry.owner_transcript === void 0) return false;
2246
+ return is_recent(read_modified(carry.owner_transcript), now);
2247
+ }
2248
+ function is_same_conversation(carry, owner) {
2249
+ return owner.transcript !== void 0 && owner.transcript === carry.owner_transcript;
2250
+ }
2251
+ function reclaim_owner(carry, owner) {
2252
+ if (carry.is_handed_off === true || !is_same_conversation(carry, owner)) return carry;
2253
+ if (owner.pid === carry.owner_pid) return carry;
2254
+ return { ...carry, owner_pid: owner.pid, owner_start: owner.start };
2255
+ }
2256
+ var run_carry_conversation = {
2257
+ CONVERSATION_QUIET_MS,
2258
+ own_transcript,
2259
+ is_conversation_live,
2260
+ is_same_conversation,
2261
+ reclaim_owner
2262
+ };
2263
+
2114
2264
  // scripts/backlog/backlog-budget-cli.ts
2115
2265
  import { fileURLToPath } from "node:url";
2116
2266
 
@@ -2188,11 +2338,20 @@ function parse_or_undefined(config) {
2188
2338
  return void 0;
2189
2339
  }
2190
2340
  }
2191
- var cli_flags = { refuse_unknown_flags, parse_or_undefined };
2341
+ function values_of(argv, options) {
2342
+ return parse_or_undefined({ args: [...argv], options })?.values;
2343
+ }
2344
+ function arguments_of(argv, options) {
2345
+ return parse_or_undefined({ args: [...argv], options, allowPositionals: true });
2346
+ }
2347
+ function string_of(value) {
2348
+ return typeof value === "string" ? value : void 0;
2349
+ }
2350
+ var cli_flags = { refuse_unknown_flags, parse_or_undefined, values_of, arguments_of, string_of };
2192
2351
 
2193
2352
  // scripts/backlog/backlog-budget-cli.ts
2194
2353
  var ARGV_OFFSET = 2;
2195
- var SUCCESS_EXIT_CODE = 0;
2354
+ var SUCCESS_EXIT_CODE2 = 0;
2196
2355
  var FAILURE_EXIT_CODE2 = 1;
2197
2356
  var JSON_KEY = "budget";
2198
2357
  var USAGE = `Usage: josh backlog:budget --answer <${backlog_budget.ANSWERS.join("|")}> --started <ISO-8601> [--active <ISO-8601>, required unless the watch is off] [--merged <count>] [--running <count>] [--max <count>] [--idle <minutes>, 0 to turn the watch off] [--json]`;
@@ -2207,10 +2366,7 @@ var OPTIONS = {
2207
2366
  started: { type: "string" }
2208
2367
  };
2209
2368
  function read_arguments(argv) {
2210
- return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values;
2211
- }
2212
- function text_of(value) {
2213
- return typeof value === "string" ? value : void 0;
2369
+ return cli_flags.values_of(argv, OPTIONS);
2214
2370
  }
2215
2371
  function to_answer(raw) {
2216
2372
  return backlog_budget.ANSWERS.find((answer) => answer === raw);
@@ -2232,14 +2388,14 @@ function is_readable(raw, parsed) {
2232
2388
  var COUNT_NAMES = ["idle", "max", "merged", "running"];
2233
2389
  function counts_of(values) {
2234
2390
  return {
2235
- idle: to_count(text_of(values.idle)),
2236
- max: to_count(text_of(values.max)),
2237
- merged: to_count(text_of(values.merged)),
2238
- running: to_count(text_of(values.running))
2391
+ idle: to_count(cli_flags.string_of(values.idle)),
2392
+ max: to_count(cli_flags.string_of(values.max)),
2393
+ merged: to_count(cli_flags.string_of(values.merged)),
2394
+ running: to_count(cli_flags.string_of(values.running))
2239
2395
  };
2240
2396
  }
2241
2397
  function are_counts_readable(values, counts) {
2242
- return COUNT_NAMES.every((name) => is_readable(text_of(values[name]), counts[name]));
2398
+ return COUNT_NAMES.every((name) => is_readable(cli_flags.string_of(values[name]), counts[name]));
2243
2399
  }
2244
2400
  var NO_IDLE_MINUTES = 0;
2245
2401
  function to_idle_ms(idle_minutes) {
@@ -2248,17 +2404,20 @@ function to_idle_ms(idle_minutes) {
2248
2404
  return idle_minutes * backlog_budget.MS_PER_MINUTE;
2249
2405
  }
2250
2406
  function is_watch_readable(values) {
2251
- if (to_idle_ms(to_count(text_of(values.idle))) === void 0) return true;
2252
- return text_of(values.active) !== void 0;
2407
+ if (to_idle_ms(to_count(cli_flags.string_of(values.idle))) === void 0) return true;
2408
+ return cli_flags.string_of(values.active) !== void 0;
2253
2409
  }
2254
2410
  function are_values_readable(values) {
2255
- return is_readable(text_of(values.active), to_time_ms(text_of(values.active))) && is_watch_readable(values) && are_counts_readable(values, counts_of(values));
2411
+ return is_readable(
2412
+ cli_flags.string_of(values.active),
2413
+ to_time_ms(cli_flags.string_of(values.active))
2414
+ ) && is_watch_readable(values) && are_counts_readable(values, counts_of(values));
2256
2415
  }
2257
2416
  function input_of(values, base) {
2258
2417
  const counts = counts_of(values);
2259
2418
  return {
2260
2419
  ...base,
2261
- active_at_ms: to_time_ms(text_of(values.active)) ?? base.started_at_ms,
2420
+ active_at_ms: to_time_ms(cli_flags.string_of(values.active)) ?? base.started_at_ms,
2262
2421
  merged: counts.merged ?? 0,
2263
2422
  running: counts.running ?? 0,
2264
2423
  max_issues: counts.max,
@@ -2266,9 +2425,9 @@ function input_of(values, base) {
2266
2425
  };
2267
2426
  }
2268
2427
  function build_input(values, now_ms) {
2269
- const answer = to_answer(text_of(values.answer));
2428
+ const answer = to_answer(cli_flags.string_of(values.answer));
2270
2429
  if (answer === void 0) return void 0;
2271
- const started_at_ms = to_time_ms(text_of(values.started));
2430
+ const started_at_ms = to_time_ms(cli_flags.string_of(values.started));
2272
2431
  if (started_at_ms === void 0) return void 0;
2273
2432
  if (!are_values_readable(values)) return void 0;
2274
2433
  return input_of(values, { answer, now_ms, started_at_ms });
@@ -2277,7 +2436,7 @@ function refuse() {
2277
2436
  console.error(USAGE);
2278
2437
  return FAILURE_EXIT_CODE2;
2279
2438
  }
2280
- function run(argv, now_ms = Date.now()) {
2439
+ function run2(argv, now_ms = Date.now()) {
2281
2440
  const values = read_arguments(argv);
2282
2441
  if (values === void 0) return refuse();
2283
2442
  const input = build_input(values, now_ms);
@@ -2287,23 +2446,23 @@ function run(argv, now_ms = Date.now()) {
2287
2446
  console.info(
2288
2447
  JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: backlog_budget.is_finish(input) })
2289
2448
  );
2290
- return SUCCESS_EXIT_CODE;
2449
+ return SUCCESS_EXIT_CODE2;
2291
2450
  }
2292
2451
  path_decision.print_decision(JSON_KEY, verdict, reason, false);
2293
- return SUCCESS_EXIT_CODE;
2452
+ return SUCCESS_EXIT_CODE2;
2294
2453
  }
2295
2454
  function main(argv) {
2296
- process.exitCode = run(argv);
2455
+ process.exitCode = run2(argv);
2297
2456
  }
2298
2457
  var backlog_budget_cli = {
2299
2458
  JSON_KEY,
2300
2459
  OPTIONS,
2301
- SUCCESS_EXIT_CODE,
2460
+ SUCCESS_EXIT_CODE: SUCCESS_EXIT_CODE2,
2302
2461
  USAGE,
2303
2462
  build_input,
2304
2463
  main,
2305
2464
  read_arguments,
2306
- run,
2465
+ run: run2,
2307
2466
  to_answer,
2308
2467
  to_count,
2309
2468
  to_time_ms
@@ -2322,7 +2481,7 @@ var VALUE_OFFSET = 1;
2322
2481
  var FLAG_PAIR_LENGTH = 2;
2323
2482
  var ONLY_STEP = 1;
2324
2483
  var EMPTY_LENGTH2 = 0;
2325
- var NOT_FOUND = -1;
2484
+ var NOT_FOUND2 = -1;
2326
2485
  function tokens_of(invocation) {
2327
2486
  return invocation.split(TOKEN_SEPARATOR).filter((token) => token.length > EMPTY_LENGTH2);
2328
2487
  }
@@ -2342,10 +2501,10 @@ function flag_tokens(tokens, start) {
2342
2501
  const rendered = [];
2343
2502
  let index = start;
2344
2503
  while (index < tokens.length) {
2345
- const read2 = read_flag(tokens, index);
2346
- if (read2 === void 0) return void 0;
2347
- rendered.push(read2.part);
2348
- index += read2.advance;
2504
+ const read3 = read_flag(tokens, index);
2505
+ if (read3 === void 0) return void 0;
2506
+ rendered.push(read3.part);
2507
+ index += read3.advance;
2349
2508
  }
2350
2509
  return rendered;
2351
2510
  }
@@ -2358,7 +2517,7 @@ function issue_of(token) {
2358
2517
  }
2359
2518
  function take_leading_prefixed(args) {
2360
2519
  const stop = args.findIndex((token) => !token.startsWith(ISSUE_PREFIX));
2361
- return args.slice(FIRST_TOKEN, stop === NOT_FOUND ? args.length : stop);
2520
+ return args.slice(FIRST_TOKEN, stop === NOT_FOUND2 ? args.length : stop);
2362
2521
  }
2363
2522
  function leading_issues(tokens) {
2364
2523
  const issues = [];
@@ -2438,6 +2597,7 @@ var run_carry_schema = z6.object({
2438
2597
  // which is the not-provably-live answer rather than a live one.
2439
2598
  owner_pid: z6.number().optional(),
2440
2599
  owner_start: z6.string().optional(),
2600
+ owner_transcript: z6.string().optional(),
2441
2601
  is_handed_off: z6.boolean().optional(),
2442
2602
  done: z6.array(z6.number()).optional(),
2443
2603
  // Optional, so a record written before the retrospective existed still parses (joshuafolkken/kit#2328).
@@ -2472,8 +2632,8 @@ function classify3(raw, now) {
2472
2632
  function read_carry(target, now = /* @__PURE__ */ new Date()) {
2473
2633
  return classify3(stamp_file.read_stamp_text(target), now);
2474
2634
  }
2475
- function owner_of(pid, read2 = process_identity.read_start) {
2476
- return { pid, start: read2(pid) };
2635
+ function owner_of(pid, read3 = process_identity.read_start, transcript = run_carry_conversation.own_transcript()) {
2636
+ return { pid, start: read3(pid), transcript };
2477
2637
  }
2478
2638
  function fresh_carry(invocation, owner, now) {
2479
2639
  return {
@@ -2485,7 +2645,8 @@ function fresh_carry(invocation, owner, now) {
2485
2645
  failures: NO_INCREMENT3,
2486
2646
  outages: NO_INCREMENT3,
2487
2647
  owner_pid: owner.pid,
2488
- owner_start: owner.start
2648
+ owner_start: owner.start,
2649
+ owner_transcript: owner.transcript
2489
2650
  };
2490
2651
  }
2491
2652
  function begin_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */ new Date()) {
@@ -2501,15 +2662,18 @@ function adopt_carry(target, carry, owner = NO_OWNER) {
2501
2662
  ...carry,
2502
2663
  owner_pid: owner.pid,
2503
2664
  owner_start: owner.start,
2665
+ owner_transcript: owner.transcript,
2504
2666
  is_handed_off: false
2505
2667
  };
2506
2668
  stamp_file.remove_stamp(target);
2507
2669
  return stamp_file.create_stamp(target, next) ? next : void 0;
2508
2670
  }
2509
- function is_owner_live(carry) {
2510
- return process_identity.is_same_process(carry.owner_pid, carry.owner_start) !== false;
2671
+ function is_owner_live(carry, now = /* @__PURE__ */ new Date()) {
2672
+ if (process_identity.is_same_process(carry.owner_pid, carry.owner_start) !== false) return true;
2673
+ return run_carry_conversation.is_conversation_live(carry, now);
2511
2674
  }
2512
2675
  function is_owned_by(carry, owner) {
2676
+ if (run_carry_conversation.is_same_conversation(carry, owner)) return true;
2513
2677
  return carry.owner_pid === owner.pid && carry.owner_start === owner.start;
2514
2678
  }
2515
2679
  function is_foreign_live_owner(carry, owner) {
@@ -2533,9 +2697,9 @@ function is_at_cut_cap(carry) {
2533
2697
  function end_carry(target) {
2534
2698
  stamp_file.remove_stamp(target);
2535
2699
  }
2536
- function retrospective_done_of(read2) {
2537
- if (read2.kind !== "carried" && read2.kind !== "expired") return false;
2538
- return read2.carry.retrospective === true;
2700
+ function retrospective_done_of(read3) {
2701
+ if (read3.kind !== "carried" && read3.kind !== "expired") return false;
2702
+ return read3.carry.retrospective === true;
2539
2703
  }
2540
2704
  function remaining_of(carry) {
2541
2705
  if (carry === void 0) return void 0;
@@ -2630,15 +2794,15 @@ import { spawn } from "node:child_process";
2630
2794
  import { closeSync, constants, openSync, writeSync } from "node:fs";
2631
2795
 
2632
2796
  // scripts/agent/agent-launch-environment.ts
2633
- import { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync, unlinkSync } from "node:fs";
2634
- import path3 from "node:path";
2797
+ import { existsSync as existsSync2, lstatSync, mkdirSync, readlinkSync, symlinkSync, unlinkSync } from "node:fs";
2798
+ import path4 from "node:path";
2635
2799
 
2636
2800
  // scripts/gh/gh-cli-token.ts
2637
- import { execaSync } from "execa";
2801
+ import { execaSync as execaSync2 } from "execa";
2638
2802
  var GH_CLI_TIMEOUT_MS = 5e3;
2639
2803
  function get() {
2640
2804
  try {
2641
- const { stdout } = execaSync("gh", ["auth", "token"], { timeout: GH_CLI_TIMEOUT_MS });
2805
+ const { stdout } = execaSync2("gh", ["auth", "token"], { timeout: GH_CLI_TIMEOUT_MS });
2642
2806
  const token = stdout.trim();
2643
2807
  return token.length > 0 ? token : void 0;
2644
2808
  } catch {
@@ -2649,9 +2813,9 @@ var gh_cli_token = { get };
2649
2813
 
2650
2814
  // scripts/agent/codex-home-source.ts
2651
2815
  import os from "node:os";
2652
- import path2 from "node:path";
2816
+ import path3 from "node:path";
2653
2817
  function default_home() {
2654
- return path2.join(os.homedir(), ".codex");
2818
+ return path3.join(os.homedir(), ".codex");
2655
2819
  }
2656
2820
  var codex_home_source = { default_home };
2657
2821
 
@@ -2668,18 +2832,18 @@ function remove_old_home_link(source, target) {
2668
2832
  const current = lstatSync(target, { throwIfNoEntry: false });
2669
2833
  if (current === void 0) return false;
2670
2834
  if (!current.isSymbolicLink()) throw new Error(`Reviewer home file is not a link: ${target}`);
2671
- if (existsSync(source) && readlinkSync(target) === source) return true;
2835
+ if (existsSync2(source) && readlinkSync(target) === source) return true;
2672
2836
  unlinkSync(target);
2673
2837
  return false;
2674
2838
  }
2675
2839
  function link_home_file(source_home, target_home, name) {
2676
- const source = path3.join(source_home, name);
2677
- const target = path3.join(target_home, name);
2678
- if (!remove_old_home_link(source, target) && existsSync(source)) symlinkSync(source, target);
2840
+ const source = path4.join(source_home, name);
2841
+ const target = path4.join(target_home, name);
2842
+ if (!remove_old_home_link(source, target) && existsSync2(source)) symlinkSync(source, target);
2679
2843
  }
2680
2844
  function reviewer_home(cwd, environment2) {
2681
2845
  const source = environment2["CODEX_HOME"] ?? process.env["CODEX_HOME"] ?? codex_home_source.default_home();
2682
- const target = path3.resolve(cwd, ...REVIEWER_HOME);
2846
+ const target = path4.resolve(cwd, ...REVIEWER_HOME);
2683
2847
  mkdirSync(target, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });
2684
2848
  for (const name of HOME_FILES) link_home_file(source, target, name);
2685
2849
  return target;
@@ -2979,9 +3143,9 @@ function scoped_events(events, scope) {
2979
3143
  if (floor === void 0) return void 0;
2980
3144
  return events.filter((event) => is_within(event, floor));
2981
3145
  }
2982
- function scope_of(read2) {
2983
- if (read2.kind === "carried" || read2.kind === "expired") {
2984
- return { kind: SINCE_SCOPE, started_at: read2.carry.started_at };
3146
+ function scope_of(read3) {
3147
+ if (read3.kind === "carried" || read3.kind === "expired") {
3148
+ return { kind: SINCE_SCOPE, started_at: read3.carry.started_at };
2985
3149
  }
2986
3150
  return UNKNOWN_EVENT_SCOPE;
2987
3151
  }
@@ -3060,7 +3224,8 @@ async function emit_once(kind, text) {
3060
3224
  const target = await stream_target();
3061
3225
  if (target === void 0 || run_event_stream.read_last(target)?.kind === kind) return false;
3062
3226
  return run_event_stream.append(target, kind, text, now_iso()).appended;
3063
- } catch {
3227
+ } catch (error) {
3228
+ error_text.trace_swallowed("run_event_stream_emit.emit_once", error);
3064
3229
  return false;
3065
3230
  }
3066
3231
  }
@@ -3078,7 +3243,8 @@ async function emit_once_since(kind, text, reset_kind) {
3078
3243
  return false;
3079
3244
  }
3080
3245
  return run_event_stream.append(target, kind, text, now_iso()).appended;
3081
- } catch {
3246
+ } catch (error) {
3247
+ error_text.trace_swallowed("run_event_stream_emit.emit_once_since", error);
3082
3248
  return false;
3083
3249
  }
3084
3250
  }
@@ -3089,8 +3255,8 @@ async function current_events() {
3089
3255
  try {
3090
3256
  const repository2 = await run_carry.repository_directory();
3091
3257
  if (repository2 === void 0) return [];
3092
- const read2 = run_carry.read_carry(run_carry.carry_path(repository2));
3093
- const scope = run_event_scope.scope_of(read2);
3258
+ const read3 = run_carry.read_carry(run_carry.carry_path(repository2));
3259
+ const scope = run_event_scope.scope_of(read3);
3094
3260
  const events = run_event_stream.read_events(run_event_stream.target_of(repository2));
3095
3261
  return run_event_scope.scoped_events(events, scope) ?? [];
3096
3262
  } catch {
@@ -3474,7 +3640,9 @@ function is_confirmation_notify(command) {
3474
3640
  );
3475
3641
  }
3476
3642
  var NOTIFY_INVOCATION = /josh\s+(?:notify|nf)\b/u;
3643
+ var COMMAND_NOTIFY_MARKER = "stop notified: a confirmation Telegram was sent by this command";
3477
3644
  function mentions_confirmation_notify(text) {
3645
+ if (text.includes(COMMAND_NOTIFY_MARKER)) return true;
3478
3646
  return NOTIFY_INVOCATION.test(text) && CONFIRMATION_TASK_TYPE.test(text);
3479
3647
  }
3480
3648
  function is_parking_child(directory = process.cwd()) {
@@ -3493,6 +3661,7 @@ function records_the_park(command) {
3493
3661
  }
3494
3662
  var LANE_PARK_REASON = '\u26D4 lane child park: this session is a dispatched lane child, and a stop that waits on a person must leave the question on the Issue before it sends this Telegram \u2014 otherwise the parent finds an OPEN Issue with `in-progress` and no question, and a person cannot tell what to answer (joshuafolkken/kit#2034 measured this twice in one run). The child is the only process that knows the question, the options and whether it stashed, so it records the park itself rather than leaving the parent to guess from a log. Before you notify and stop: apply `needs-decision` and post a comment carrying the question, the options, and whether work was stashed \u2014 `gh api repos/{owner}/{repo}/issues/<N>/labels -f \'labels[]=needs-decision\'` then `pnpm josh issue:comment <N> --body-file <path>`. If this stop is a `needs-human-review` or `already-done` one, that label is already on the Issue and there is nothing to add. Then reissue this notify \u2014 it fires once per run and cannot repeat on the call in hand. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` \u2192 "A lane child records its park before it stops", and the park itself is `backlogrun-park.md` \u2192 "park and continue".';
3495
3663
  var lane_park = {
3664
+ COMMAND_NOTIFY_MARKER,
3496
3665
  LANE_PARK_REASON,
3497
3666
  is_confirmation_notify,
3498
3667
  is_parking_child,
@@ -3502,8 +3671,8 @@ var lane_park = {
3502
3671
  };
3503
3672
 
3504
3673
  // scripts/discovery/repo-discovery.ts
3505
- import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3, statSync } from "node:fs";
3506
- import path4 from "node:path";
3674
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
3675
+ import path5 from "node:path";
3507
3676
 
3508
3677
  // scripts/discovery/repo-origin.ts
3509
3678
  var GITHUB_HOST = "github.com";
@@ -3515,7 +3684,7 @@ var HOST_SEPARATOR = ":";
3515
3684
  var OWNER_INDEX = 0;
3516
3685
  var REPO_INDEX = 1;
3517
3686
  var REPO_PATH_SEGMENTS = 2;
3518
- var NOT_FOUND2 = -1;
3687
+ var NOT_FOUND3 = -1;
3519
3688
  var SECTION_PREFIX = "[";
3520
3689
  var ORIGIN_SECTION = /^\[remote\s+"origin"\]$/u;
3521
3690
  var URL_KEY = "url";
@@ -3538,9 +3707,9 @@ function to_identity(repository_path) {
3538
3707
  }
3539
3708
  function parse_scp_like(url) {
3540
3709
  const user_end = url.indexOf(USER_SEPARATOR);
3541
- const authority = user_end === NOT_FOUND2 ? url : url.slice(user_end + 1);
3710
+ const authority = user_end === NOT_FOUND3 ? url : url.slice(user_end + 1);
3542
3711
  const host_end = authority.indexOf(HOST_SEPARATOR);
3543
- if (host_end === NOT_FOUND2) return void 0;
3712
+ if (host_end === NOT_FOUND3) return void 0;
3544
3713
  if (!is_github_ssh_host(authority.slice(0, host_end))) return void 0;
3545
3714
  return to_identity(authority.slice(host_end + 1));
3546
3715
  }
@@ -3562,14 +3731,14 @@ function is_section_header(line) {
3562
3731
  }
3563
3732
  function origin_section_lines(lines) {
3564
3733
  const start = lines.findIndex((line) => ORIGIN_SECTION.test(line));
3565
- if (start === NOT_FOUND2) return [];
3734
+ if (start === NOT_FOUND3) return [];
3566
3735
  const rest = lines.slice(start + 1);
3567
3736
  const end = rest.findIndex((line) => is_section_header(line));
3568
- return end === NOT_FOUND2 ? rest : rest.slice(0, end);
3737
+ return end === NOT_FOUND3 ? rest : rest.slice(0, end);
3569
3738
  }
3570
3739
  function entry_value(line, key2) {
3571
3740
  const separator = line.indexOf("=");
3572
- if (separator === NOT_FOUND2) return void 0;
3741
+ if (separator === NOT_FOUND3) return void 0;
3573
3742
  if (line.slice(0, separator).trim() !== key2) return void 0;
3574
3743
  return line.slice(separator + 1).trim();
3575
3744
  }
@@ -3598,9 +3767,9 @@ var PATH_INDEX = 1;
3598
3767
  function is_same_owner(identity, current_owner2) {
3599
3768
  return identity.owner.toLowerCase() === current_owner2.toLowerCase();
3600
3769
  }
3601
- function add_entry(target, origin_url, repository_path, current_owner2) {
3602
- if (origin_url === void 0) return;
3603
- const identity = repo_origin.parse_origin_url(origin_url);
3770
+ function add_entry(target, origin_url2, repository_path, current_owner2) {
3771
+ if (origin_url2 === void 0) return;
3772
+ const identity = repo_origin.parse_origin_url(origin_url2);
3604
3773
  if (identity === void 0 || !is_same_owner(identity, current_owner2)) return;
3605
3774
  target.set(repo_origin.format_identity(identity), repository_path);
3606
3775
  }
@@ -3636,10 +3805,10 @@ var GITDIR_PREFIX = "gitdir:";
3636
3805
  var COMMON_DIRECTORY_FILE = "commondir";
3637
3806
  var OVERRIDE_ENV_KEY = "JOSH_REPO_PATHS";
3638
3807
  function resolve_common_directory(git_directory) {
3639
- const pointer = path4.join(git_directory, COMMON_DIRECTORY_FILE);
3640
- if (!existsSync2(pointer)) return git_directory;
3808
+ const pointer = path5.join(git_directory, COMMON_DIRECTORY_FILE);
3809
+ if (!existsSync3(pointer)) return git_directory;
3641
3810
  try {
3642
- return path4.resolve(git_directory, readFileSync3(pointer, "utf8").trim());
3811
+ return path5.resolve(git_directory, readFileSync3(pointer, "utf8").trim());
3643
3812
  } catch {
3644
3813
  return git_directory;
3645
3814
  }
@@ -3647,13 +3816,13 @@ function resolve_common_directory(git_directory) {
3647
3816
  function follow_git_file(repository_path, git_file) {
3648
3817
  const pointer = readFileSync3(git_file, "utf8").trim();
3649
3818
  if (!pointer.startsWith(GITDIR_PREFIX)) return void 0;
3650
- return path4.resolve(repository_path, pointer.slice(GITDIR_PREFIX.length).trim());
3819
+ return path5.resolve(repository_path, pointer.slice(GITDIR_PREFIX.length).trim());
3651
3820
  }
3652
3821
  function resolve_git_directory(repository_path) {
3653
- const candidate = path4.join(repository_path, GIT_DIRECTORY);
3654
- if (!existsSync2(candidate)) return void 0;
3822
+ const candidate = path5.join(repository_path, GIT_DIRECTORY);
3823
+ if (!existsSync3(candidate)) return void 0;
3655
3824
  try {
3656
- if (statSync(candidate).isDirectory()) return candidate;
3825
+ if (statSync2(candidate).isDirectory()) return candidate;
3657
3826
  const linked = follow_git_file(repository_path, candidate);
3658
3827
  return linked === void 0 ? void 0 : resolve_common_directory(linked);
3659
3828
  } catch {
@@ -3665,7 +3834,7 @@ function read_origin_url(repository_path) {
3665
3834
  if (git_directory === void 0) return void 0;
3666
3835
  try {
3667
3836
  return repo_origin.parse_origin_from_config(
3668
- readFileSync3(path4.join(git_directory, GIT_CONFIG), "utf8")
3837
+ readFileSync3(path5.join(git_directory, GIT_CONFIG), "utf8")
3669
3838
  );
3670
3839
  } catch {
3671
3840
  return void 0;
@@ -3673,7 +3842,7 @@ function read_origin_url(repository_path) {
3673
3842
  }
3674
3843
  function sibling_directories(parent) {
3675
3844
  try {
3676
- return readdirSync(parent, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path4.join(parent, entry.name)).toSorted((left, right) => left.localeCompare(right));
3845
+ return readdirSync(parent, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path5.join(parent, entry.name)).toSorted((left, right) => left.localeCompare(right));
3677
3846
  } catch {
3678
3847
  return [];
3679
3848
  }
@@ -3685,22 +3854,22 @@ function scan_siblings(parent) {
3685
3854
  }));
3686
3855
  }
3687
3856
  function resolve_current_owner(repository_path) {
3688
- const origin_url = read_origin_url(repository_path);
3689
- if (origin_url === void 0) return void 0;
3690
- return repo_origin.parse_origin_url(origin_url)?.owner;
3857
+ const origin_url2 = read_origin_url(repository_path);
3858
+ if (origin_url2 === void 0) return void 0;
3859
+ return repo_origin.parse_origin_url(origin_url2)?.owner;
3691
3860
  }
3692
3861
  function main_worktree(repository_path) {
3693
3862
  const git_directory = resolve_git_directory(repository_path);
3694
3863
  if (git_directory === void 0) return repository_path;
3695
- if (path4.basename(git_directory) !== GIT_DIRECTORY) return repository_path;
3696
- return path4.dirname(git_directory);
3864
+ if (path5.basename(git_directory) !== GIT_DIRECTORY) return repository_path;
3865
+ return path5.dirname(git_directory);
3697
3866
  }
3698
3867
  function discover_repositories(repository_path, environment2 = process.env) {
3699
3868
  const main2 = main_worktree(repository_path);
3700
3869
  const current_owner2 = resolve_current_owner(main2);
3701
3870
  if (current_owner2 === void 0) return /* @__PURE__ */ new Map();
3702
3871
  return repo_map_logic.build_repository_map(
3703
- scan_siblings(path4.dirname(main2)),
3872
+ scan_siblings(path5.dirname(main2)),
3704
3873
  current_owner2,
3705
3874
  environment2[OVERRIDE_ENV_KEY]
3706
3875
  );
@@ -3810,8 +3979,8 @@ var lane_reap = {
3810
3979
  };
3811
3980
 
3812
3981
  // scripts/lane/lane-registry.ts
3813
- import { existsSync as existsSync3 } from "node:fs";
3814
- import path5 from "node:path";
3982
+ import { existsSync as existsSync4 } from "node:fs";
3983
+ import path6 from "node:path";
3815
3984
 
3816
3985
  // scripts/git/git-ls-remote.ts
3817
3986
  var LS_REMOTE_COMMAND = "ls-remote";
@@ -4089,7 +4258,7 @@ function held_seat(content) {
4089
4258
  return seat_from_content(content) ?? EMPTY_SEAT;
4090
4259
  }
4091
4260
  function read_environment(directory) {
4092
- return file_reader.read_if_readable(path5.join(directory, ENV_FILE_NAME));
4261
+ return file_reader.read_if_readable(path6.join(directory, ENV_FILE_NAME));
4093
4262
  }
4094
4263
  function branch_issue(branch2) {
4095
4264
  if (!branch2?.endsWith(lane_paths.LANE_BRANCH_SUFFIX)) return void 0;
@@ -4097,7 +4266,7 @@ function branch_issue(branch2) {
4097
4266
  return ISSUE_PATTERN.test(issue) ? issue : void 0;
4098
4267
  }
4099
4268
  function is_gone(directory) {
4100
- return !existsSync3(directory);
4269
+ return !existsSync4(directory);
4101
4270
  }
4102
4271
  function build_lane(issue, branch2, directory, is_stranded) {
4103
4272
  const content = is_stranded ? void 0 : read_environment(directory);
@@ -4181,8 +4350,8 @@ function environment() {
4181
4350
  function is_headless(source = process.env) {
4182
4351
  return source[HEADLESS_ENV_KEY] === HEADLESS_VALUE;
4183
4352
  }
4184
- function is_driving(read2) {
4185
- return read2?.kind === "carried" && read2.carry.is_handed_off !== true;
4353
+ function is_driving(read3) {
4354
+ return read3?.kind === "carried" && read3.carry.is_handed_off !== true;
4186
4355
  }
4187
4356
  async function read_carry_here() {
4188
4357
  const repository2 = await run_carry.repository_directory();
@@ -4194,15 +4363,15 @@ async function must_keep_waiting(source = process.env) {
4194
4363
  return is_driving(await read_carry_here());
4195
4364
  }
4196
4365
  async function current_carry() {
4197
- const read2 = await read_carry_here();
4198
- return read2?.kind === "carried" ? read2.carry : void 0;
4366
+ const read3 = await read_carry_here();
4367
+ return read3?.kind === "carried" ? read3.carry : void 0;
4199
4368
  }
4200
4369
  function is_owned_here(carry, ancestry) {
4201
4370
  return carry.owner_pid !== void 0 && ancestry().has(carry.owner_pid);
4202
4371
  }
4203
- function driven_here(read2, ancestry) {
4204
- if (read2?.kind !== "carried" || !is_driving(read2)) return void 0;
4205
- return is_owned_here(read2.carry, ancestry) ? read2.carry : void 0;
4372
+ function driven_here(read3, ancestry) {
4373
+ if (read3?.kind !== "carried" || !is_driving(read3)) return void 0;
4374
+ return is_owned_here(read3.carry, ancestry) ? read3.carry : void 0;
4206
4375
  }
4207
4376
  async function driving_carry(source = process.env, ancestry = lane_reap.own_ancestry) {
4208
4377
  if (lane_child_marker.marked_issue(source) !== void 0) return void 0;
@@ -4234,11 +4403,13 @@ function issue_url(slug, number) {
4234
4403
  function citation_line(slug, number, summary) {
4235
4404
  return `[#${number}](${issue_url(slug, number)})${SUMMARY_SEPARATOR}${summary}`;
4236
4405
  }
4406
+ function reference(slug, number, title) {
4407
+ if (slug === void 0) return `#${number}`;
4408
+ return title === void 0 ? `[#${number}](${issue_url(slug, number)})` : citation_line(slug, number, title);
4409
+ }
4237
4410
  function citer(slug, titles) {
4238
4411
  return function cite(number) {
4239
- if (slug === void 0) return `#${number}`;
4240
- const title = titles.get(number);
4241
- return title === void 0 ? `[#${number}](${issue_url(slug, number)})` : citation_line(slug, number, title);
4412
+ return reference(slug, number, titles.get(number));
4242
4413
  };
4243
4414
  }
4244
4415
  function qualified_target(token) {
@@ -4275,6 +4446,7 @@ var issue_cite = {
4275
4446
  missing_line,
4276
4447
  no_repo_line,
4277
4448
  parse_target,
4449
+ reference,
4278
4450
  unreadable_line
4279
4451
  };
4280
4452
 
@@ -4340,19 +4512,13 @@ var git_gh_api_path = {
4340
4512
  commit_status_api_path
4341
4513
  };
4342
4514
 
4515
+ // scripts/gh/git-gh-exec.ts
4516
+ import { execa as execa5, execaSync as execaSync3 } from "execa";
4517
+
4343
4518
  // scripts/lib/timeouts.ts
4344
4519
  var FETCH_TIMEOUT_MS = 1e4;
4345
4520
  var COMMAND_TIMEOUT_MS = 3e4;
4346
4521
 
4347
- // scripts/lib/error-message.ts
4348
- function message_of(error) {
4349
- return error instanceof Error ? error.message : String(error);
4350
- }
4351
- var error_text = { message_of };
4352
-
4353
- // scripts/gh/git-gh-exec.ts
4354
- import { execa as execa5, execaSync as execaSync2 } from "execa";
4355
-
4356
4522
  // scripts/gh/git-gh-check.ts
4357
4523
  import { execa as execa4 } from "execa";
4358
4524
  var GH_NOT_INSTALLED_MSG = "gh CLI is not installed. Install it from https://cli.github.com/";
@@ -4497,20 +4663,20 @@ function parse_status_line(output) {
4497
4663
  function status_of_failure(error) {
4498
4664
  return has_stdout_field(error) ? parse_status_line(error.stdout) : void 0;
4499
4665
  }
4500
- async function exec_gh_api_status(path6, timeout_ms) {
4666
+ async function exec_gh_api_status(path7, timeout_ms) {
4501
4667
  const options = to_spawn_options(timeout_ms);
4502
4668
  try {
4503
4669
  await check_gh_installed();
4504
- const { stdout } = await execa5("gh", ["api", "--include", "--silent", path6], options);
4670
+ const { stdout } = await execa5("gh", ["api", "--include", "--silent", path7], options);
4505
4671
  return parse_status_line(stdout);
4506
4672
  } catch (error) {
4507
4673
  return status_of_failure(error);
4508
4674
  }
4509
4675
  }
4510
- function exec_gh_api_status_sync(path6, timeout_ms) {
4676
+ function exec_gh_api_status_sync(path7, timeout_ms) {
4511
4677
  const options = to_spawn_options(timeout_ms);
4512
4678
  try {
4513
- const { stdout } = execaSync2("gh", ["api", "--include", "--silent", path6], options);
4679
+ const { stdout } = execaSync3("gh", ["api", "--include", "--silent", path7], options);
4514
4680
  return parse_status_line(stdout);
4515
4681
  } catch (error) {
4516
4682
  return status_of_failure(error);
@@ -4554,7 +4720,7 @@ function exec_gh_api_sync(request) {
4554
4720
  const body_option = request.body === void 0 ? {} : { input: request.body };
4555
4721
  const options = { ...body_option, ...to_spawn_options(request.timeout_ms, request.cwd) };
4556
4722
  try {
4557
- const { stdout } = execaSync2("gh", to_gh_api_args(request), options);
4723
+ const { stdout } = execaSync3("gh", to_gh_api_args(request), options);
4558
4724
  return stdout.trimEnd();
4559
4725
  } catch (error) {
4560
4726
  throw to_gh_error(error);
@@ -4579,33 +4745,1706 @@ var git_gh_exec = {
4579
4745
  parse_status_line
4580
4746
  };
4581
4747
 
4748
+ // scripts/gh/git-gh-helpers.ts
4749
+ function parse_pr_state_string(result) {
4750
+ const trimmed2 = result.trim();
4751
+ return trimmed2.length > 0 ? trimmed2 : void 0;
4752
+ }
4753
+ function is_pr_already_exists_message(error_message) {
4754
+ return error_message.toLowerCase().includes("already exists");
4755
+ }
4756
+ function get_error_message_with_stderr(error) {
4757
+ if (!(error instanceof Error)) return String(error);
4758
+ if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
4759
+ ${error.stderr}`;
4760
+ return error.message;
4761
+ }
4762
+ function handle_pr_create_error(error) {
4763
+ const error_message = get_error_message_with_stderr(error);
4764
+ if (is_pr_already_exists_message(error_message)) {
4765
+ throw new Error("PR_ALREADY_EXISTS");
4766
+ }
4767
+ throw error;
4768
+ }
4769
+ var git_gh_helpers = {
4770
+ parse_pr_state_string,
4771
+ get_error_message_with_stderr,
4772
+ handle_pr_create_error
4773
+ };
4774
+
4775
+ // scripts/git/schemas.ts
4776
+ import { z as z11 } from "zod";
4777
+ var package_name_schema = z11.object({ name: z11.string().min(1) });
4778
+ var rollup_item_schema = z11.looseObject({
4779
+ // eslint-disable-next-line @typescript-eslint/naming-convention -- GraphQL field name from GitHub API
4780
+ __typename: z11.string().optional(),
4781
+ state: z11.string().optional(),
4782
+ status: z11.string().optional(),
4783
+ conclusion: z11.string().optional(),
4784
+ name: z11.string().optional(),
4785
+ context: z11.string().optional()
4786
+ });
4787
+ var pr_raw_schema = z11.object({
4788
+ statusCheckRollup: z11.array(rollup_item_schema).optional(),
4789
+ mergeStateStatus: z11.string().optional(),
4790
+ reviewDecision: z11.string().optional()
4791
+ });
4792
+ var pull_comment_schema = z11.object({
4793
+ body: z11.string().optional(),
4794
+ html_url: z11.string().optional(),
4795
+ user: z11.object({ login: z11.string().optional() }).optional()
4796
+ });
4797
+ var ai_review_pull_comment_schema = z11.object({
4798
+ body: z11.string().optional(),
4799
+ url: z11.string().optional(),
4800
+ author: z11.object({ login: z11.string().optional() }).optional()
4801
+ });
4802
+ var pr_info_schema = z11.object({
4803
+ mergeable: z11.union([z11.boolean(), z11.string()]).nullable().optional(),
4804
+ mergeStateStatus: z11.string().nullable().optional(),
4805
+ state: z11.string().optional()
4806
+ });
4807
+ var epic_issue_schema = z11.object({
4808
+ number: z11.number(),
4809
+ body: z11.string().optional()
4810
+ });
4811
+ var blocking_issue_schema = z11.object({
4812
+ number: z11.number(),
4813
+ state: z11.string().optional(),
4814
+ repository_url: z11.string().optional()
4815
+ });
4816
+ var blocked_by_schema = z11.object({
4817
+ nodes: z11.array(blocking_issue_schema).default([]),
4818
+ totalCount: z11.number().optional()
4819
+ }).optional();
4820
+ var epic_child_schema = z11.object({
4821
+ state: z11.string().optional(),
4822
+ blockedBy: blocked_by_schema
4823
+ });
4824
+ var rest_comment_schema = z11.looseObject({
4825
+ body: z11.string().nullish(),
4826
+ html_url: z11.string().optional(),
4827
+ user: z11.looseObject({ login: z11.string().optional() }).nullish()
4828
+ });
4829
+ var issue_label_schema = z11.object({ name: z11.string() });
4830
+ var epic_subject_schema = z11.object({
4831
+ number: z11.number(),
4832
+ state: z11.string().optional(),
4833
+ labels: z11.array(issue_label_schema).optional(),
4834
+ body: z11.string().optional()
4835
+ });
4836
+ var open_issue_schema = z11.object({
4837
+ number: z11.number(),
4838
+ title: z11.string(),
4839
+ labels: z11.array(issue_label_schema).optional(),
4840
+ createdAt: z11.string(),
4841
+ blockedBy: blocked_by_schema
4842
+ });
4843
+
4844
+ // scripts/gh/git-gh-issue-rest.ts
4845
+ import { z as z12 } from "zod";
4846
+
4847
+ // scripts/gh/git-gh-rest-state.ts
4848
+ var MERGED_STATE = "MERGED";
4849
+ function to_gh_state(state) {
4850
+ return state?.toUpperCase();
4851
+ }
4852
+
4853
+ // scripts/gh/git-gh-issue-rest.ts
4854
+ var rest_issue_schema = z12.looseObject({
4855
+ number: z12.number(),
4856
+ issue_dependencies_summary: z12.looseObject({ total_blocked_by: z12.number().optional() }).nullish(),
4857
+ // Present only on a pull request, which this endpoint serves as readily as an issue.
4858
+ pull_request: z12.looseObject({ merged_at: z12.string().nullish() }).nullish()
4859
+ });
4860
+ var REPOSITORY_URL = /\/repos\/([\w.-]+\/[\w.-]+)$/u;
4861
+ var REPOSITORY_URL_GROUP = 1;
4862
+ var BLOCKED_BY_FIELD = "blockedBy";
4863
+ var BLOCKED_BY_COUNT_FIELD = "blocked_by_count";
4864
+ var STATE_FIELD = "state";
4865
+ var BODY_FIELD = "body";
4866
+ var FIELD_SEPARATOR2 = ",";
4867
+ var EMPTY_BODY = "";
4868
+ var REST_FIELD_NAMES = /* @__PURE__ */ new Map([
4869
+ ["url", "html_url"],
4870
+ ["createdAt", "created_at"]
4871
+ ]);
4872
+ var NOT_AN_ISSUE_MESSAGE = "gh api answered something other than an issue object";
4873
+ var NOT_AN_ISSUE_LISTING_MESSAGE = "gh api answered something other than an issue listing";
4874
+ var NOT_A_BLOCKER_LISTING_MESSAGE = "gh api answered something other than a blocked-by listing";
4875
+ function rest_field_name(field) {
4876
+ return REST_FIELD_NAMES.get(field) ?? field;
4877
+ }
4878
+ function split_fields(fields) {
4879
+ return fields.split(FIELD_SEPARATOR2).map((field) => field.trim()).filter((field) => field.length > 0);
4880
+ }
4881
+ function to_issue_state(rest, value) {
4882
+ if (typeof rest.pull_request?.merged_at === "string") return MERGED_STATE;
4883
+ return typeof value === "string" ? to_gh_state(value) : value;
4884
+ }
4885
+ function total_blocked_by(rest) {
4886
+ return rest.issue_dependencies_summary?.total_blocked_by;
4887
+ }
4888
+ function to_gh_field_value(field, rest) {
4889
+ if (field === BLOCKED_BY_COUNT_FIELD) return total_blocked_by(rest);
4890
+ const value = rest[rest_field_name(field)];
4891
+ if (field === STATE_FIELD) return to_issue_state(rest, value);
4892
+ return field === BODY_FIELD ? value ?? EMPTY_BODY : value;
4893
+ }
4894
+ function parse_rest_issue(rest_json) {
4895
+ const parsed = parse_json_object_safe(rest_json, rest_issue_schema);
4896
+ if (parsed === void 0) throw new Error(NOT_AN_ISSUE_MESSAGE);
4897
+ return parsed;
4898
+ }
4899
+ function is_pull_request(rest) {
4900
+ return rest.pull_request !== void 0 && rest.pull_request !== null;
4901
+ }
4902
+ function parse_rest_issues(rest_json) {
4903
+ const parsed = parse_json_array_or_undefined(rest_json, rest_issue_schema);
4904
+ if (parsed === void 0) throw new Error(NOT_AN_ISSUE_LISTING_MESSAGE);
4905
+ return parsed;
4906
+ }
4907
+ function empty_blocked_by() {
4908
+ return { nodes: [], totalCount: 0 };
4909
+ }
4910
+ function repo_of_url(repository_url) {
4911
+ const match = REPOSITORY_URL.exec(repository_url ?? "");
4912
+ return match?.[REPOSITORY_URL_GROUP];
4913
+ }
4914
+ function to_blocked_by(blockers_json, exact_total) {
4915
+ const parsed = parse_json_array_or_undefined(blockers_json, blocking_issue_schema);
4916
+ if (parsed === void 0) throw new Error(NOT_A_BLOCKER_LISTING_MESSAGE);
4917
+ const nodes = parsed.map((blocker) => ({ ...blocker, state: to_gh_state(blocker.state) }));
4918
+ return { nodes, totalCount: exact_total ?? nodes.length };
4919
+ }
4920
+ function to_gh_issue(rest, fields, blocked_by) {
4921
+ return Object.fromEntries(
4922
+ fields.map((field) => [
4923
+ field,
4924
+ field === BLOCKED_BY_FIELD ? blocked_by : to_gh_field_value(field, rest)
4925
+ ])
4926
+ );
4927
+ }
4928
+ function to_field_text(value) {
4929
+ if (typeof value === "string") return value;
4930
+ return value === void 0 || value === null ? EMPTY_BODY : JSON.stringify(value);
4931
+ }
4932
+ var CLOSED = "CLOSED";
4933
+ function reference_of_blocker(node, fallback_repo) {
4934
+ const repo = repo_of_url(node.repository_url) ?? fallback_repo;
4935
+ if (node.state === void 0) return { repo, number: node.number };
4936
+ return { repo, number: node.number, state: node.state.toUpperCase() === CLOSED ? CLOSED : "OPEN" };
4937
+ }
4938
+ var git_gh_issue_rest = {
4939
+ empty_blocked_by,
4940
+ repo_of_url,
4941
+ reference_of_blocker,
4942
+ split_fields,
4943
+ is_pull_request,
4944
+ parse_rest_issue,
4945
+ parse_rest_issues,
4946
+ total_blocked_by,
4947
+ to_blocked_by,
4948
+ to_gh_issue,
4949
+ to_field_text
4950
+ };
4951
+
4952
+ // scripts/gh/git-gh-reachability.ts
4953
+ var RATE_LIMITED_STATUS = 429;
4954
+ var SERVER_ERROR_FLOOR = 500;
4955
+ function is_retryable_status(status2) {
4956
+ if (status2 >= SERVER_ERROR_FLOOR) return true;
4957
+ return status2 === RATE_LIMITED_STATUS;
4958
+ }
4959
+ function classify_status(status2) {
4960
+ if (status2 === void 0) return "unreachable";
4961
+ return is_retryable_status(status2) ? "unreachable" : "reachable";
4962
+ }
4963
+ var gh_reachability = { classify_status };
4964
+
4965
+ // scripts/gh/git-gh-issue-read.ts
4966
+ async function read_blocked_by_listing(issue_number, repo, exact_total) {
4967
+ const json = await git_gh_exec.exec_gh_api({
4968
+ path: `${git_gh_api_path.blocked_by_api_path(issue_number, repo)}${git_gh_api_path.FULL_PAGE_QUERY}`
4969
+ });
4970
+ return git_gh_issue_rest.to_blocked_by(json, exact_total);
4971
+ }
4972
+ async function read_blocked_by(issue_number, rest, repo) {
4973
+ if (git_gh_issue_rest.is_pull_request(rest)) return git_gh_issue_rest.empty_blocked_by();
4974
+ const exact_total = git_gh_issue_rest.total_blocked_by(rest);
4975
+ if (exact_total === 0) return git_gh_issue_rest.empty_blocked_by();
4976
+ return await read_blocked_by_listing(issue_number, repo, exact_total);
4977
+ }
4978
+ function mark_followup(error) {
4979
+ if (!(error instanceof Error)) return error;
4980
+ const failure = gh_failure.failure_of(error);
4981
+ if (failure === void 0) return error;
4982
+ return gh_failure.attach(error, { ...failure, is_followup: true });
4983
+ }
4984
+ async function read_relations(issue_number, rest, repo) {
4985
+ try {
4986
+ return await read_blocked_by(issue_number, rest, repo);
4987
+ } catch (error) {
4988
+ throw mark_followup(error);
4989
+ }
4990
+ }
4991
+ async function read_issue_fields(issue_number, fields, repo) {
4992
+ const rest = git_gh_issue_rest.parse_rest_issue(
4993
+ await git_gh_exec.exec_gh_api({ path: git_gh_api_path.issue_api_path(issue_number, repo) })
4994
+ );
4995
+ const requested = git_gh_issue_rest.split_fields(fields);
4996
+ const blocked_by = requested.includes(BLOCKED_BY_FIELD) ? await read_relations(issue_number, rest, repo) : void 0;
4997
+ return git_gh_issue_rest.to_gh_issue(rest, requested, blocked_by);
4998
+ }
4999
+ var NOT_FOUND_STATUS = 404;
5000
+ function is_number_unresolved(failure) {
5001
+ return failure.status === NOT_FOUND_STATUS && failure.is_followup !== true;
5002
+ }
5003
+ function to_unreadable_reason(status2) {
5004
+ return gh_reachability.classify_status(status2) === "unreachable" ? "unreachable" : "rejected";
5005
+ }
5006
+ function to_read_failure(error) {
5007
+ const failure = gh_failure.failure_of(error);
5008
+ if (failure === void 0) return { kind: "unreadable", reason: "malformed", status: void 0 };
5009
+ if (is_number_unresolved(failure)) return { kind: "missing" };
5010
+ return {
5011
+ kind: "unreadable",
5012
+ reason: to_unreadable_reason(failure.status),
5013
+ status: failure.status
5014
+ };
5015
+ }
5016
+ async function issue_view_json_classified(issue_number, fields, repo) {
5017
+ try {
5018
+ const issue = await read_issue_fields(issue_number, fields, repo);
5019
+ return { kind: "read", json: JSON.stringify(issue) };
5020
+ } catch (error) {
5021
+ return to_read_failure(error);
5022
+ }
5023
+ }
5024
+ async function issue_view_field_classified(issue_number, field, repo) {
5025
+ try {
5026
+ const issue = await read_issue_fields(issue_number, field, repo);
5027
+ return { kind: "read", text: git_gh_issue_rest.to_field_text(issue[field]) };
5028
+ } catch (error) {
5029
+ return to_read_failure(error);
5030
+ }
5031
+ }
5032
+ async function issue_view_field(issue_number, field, repo) {
5033
+ const read3 = await issue_view_field_classified(issue_number, field, repo);
5034
+ return read3.kind === "read" ? read3.text : void 0;
5035
+ }
5036
+ async function issue_get_title(issue_number, repo) {
5037
+ const result = await issue_view_field(issue_number, "title", repo);
5038
+ return result === void 0 ? void 0 : git_gh_helpers.parse_pr_state_string(result);
5039
+ }
5040
+ async function issue_get_body(issue_number, repo) {
5041
+ return await issue_view_field(issue_number, "body", repo);
5042
+ }
5043
+ async function issue_get_body_classified(issue_number, repo) {
5044
+ return await issue_view_field_classified(issue_number, "body", repo);
5045
+ }
5046
+ async function issue_view_json(issue_number, fields, repo) {
5047
+ const read3 = await issue_view_json_classified(issue_number, fields, repo);
5048
+ return read3.kind === "read" ? read3.json : void 0;
5049
+ }
5050
+ var STATE_AND_RELATIONS_FIELDS = "number,state,labels,blockedBy";
5051
+ async function issue_get_state_and_relations(issue_number, repo) {
5052
+ return await issue_view_json(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
5053
+ }
5054
+ async function issue_get_state_and_relations_classified(issue_number, repo) {
5055
+ return await issue_view_json_classified(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
5056
+ }
5057
+ function is_unreachable_read(failure) {
5058
+ return failure.kind === "unreadable" && failure.reason === "unreachable";
5059
+ }
5060
+ var PLAN_FIELDS = "number,title,body,state,url,labels,blockedBy";
5061
+ async function issue_get_plan_fields(issue_number) {
5062
+ return await issue_view_json(issue_number, PLAN_FIELDS);
5063
+ }
5064
+ async function issue_get_plan_fields_classified(issue_number) {
5065
+ return await issue_view_json_classified(issue_number, PLAN_FIELDS);
5066
+ }
5067
+ async function issue_get_labels_and_body(issue_number) {
5068
+ return await issue_view_json(issue_number, "number,state,labels,body");
5069
+ }
5070
+ async function issue_list_comments(issue_number) {
5071
+ const path7 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
5072
+ try {
5073
+ return await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true });
5074
+ } catch {
5075
+ return void 0;
5076
+ }
5077
+ }
5078
+ async function has_no_relations_endpoint(issue_number, repo) {
5079
+ const status2 = await git_gh_exec.exec_gh_api_status(
5080
+ git_gh_api_path.blocked_by_api_path(issue_number, repo)
5081
+ );
5082
+ return status2 === NOT_FOUND_STATUS;
5083
+ }
5084
+ async function issue_blocked_by_references(issue_number, own_repo, repo) {
5085
+ try {
5086
+ const listing = await read_blocked_by_listing(issue_number, repo);
5087
+ return listing.nodes.map((blocker) => git_gh_issue_rest.reference_of_blocker(blocker, own_repo));
5088
+ } catch (error) {
5089
+ if (await has_no_relations_endpoint(issue_number, repo)) return [];
5090
+ throw error;
5091
+ }
5092
+ }
5093
+ async function issue_blocked_by_numbers(issue_number, repo) {
5094
+ const references = await issue_blocked_by_references(issue_number, "", repo);
5095
+ return references.map((blocker) => blocker.number);
5096
+ }
5097
+ var git_gh_issue_read = {
5098
+ issue_blocked_by_numbers,
5099
+ issue_blocked_by_references,
5100
+ issue_get_title,
5101
+ issue_get_body,
5102
+ issue_get_body_classified,
5103
+ issue_view_json,
5104
+ issue_view_json_classified,
5105
+ issue_get_state_and_relations,
5106
+ issue_get_state_and_relations_classified,
5107
+ is_unreachable_read,
5108
+ issue_get_plan_fields,
5109
+ issue_get_plan_fields_classified,
5110
+ issue_get_labels_and_body,
5111
+ issue_list_comments
5112
+ };
5113
+
5114
+ // scripts/gh/git-gh-issue-list.ts
5115
+ var PER_PAGE = 100;
5116
+ var FIRST_PAGE = 1;
5117
+ var DEFAULT_STATE = "open";
5118
+ var DEFAULT_SORT = "created";
5119
+ var LISTING_DIRECTION = "direction=desc";
5120
+ function listing_query(request) {
5121
+ const state = request.state ?? DEFAULT_STATE;
5122
+ const sort = request.sort ?? DEFAULT_SORT;
5123
+ return `state=${state}&sort=${sort}&${LISTING_DIRECTION}`;
5124
+ }
5125
+ function listing_path(request, page) {
5126
+ const label2 = request.label === void 0 ? "" : `&labels=${encodeURIComponent(request.label)}`;
5127
+ const paging = `&per_page=${String(PER_PAGE)}&page=${String(page)}`;
5128
+ return `${git_gh_api_path.issues_api_path(request.repo)}?${listing_query(request)}${label2}${paging}`;
5129
+ }
5130
+ var DIGIT_PATTERN = /\d/u;
5131
+ function has_token_occurrence(body, term) {
5132
+ let index = body.indexOf(term);
5133
+ while (index !== -1) {
5134
+ if (!DIGIT_PATTERN.test(body.charAt(index + term.length))) return true;
5135
+ index = body.indexOf(term, index + 1);
5136
+ }
5137
+ return false;
5138
+ }
5139
+ function matches_body(rest, term) {
5140
+ if (term === void 0) return true;
5141
+ const { body } = rest;
5142
+ return typeof body === "string" && has_token_occurrence(body, term);
5143
+ }
5144
+ function is_selected(rest, request) {
5145
+ return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
5146
+ }
5147
+ async function fetch_page(request, page) {
5148
+ const path7 = listing_path(request, page);
5149
+ const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path7 }));
5150
+ return {
5151
+ selected: rows.filter((row) => is_selected(row, request)),
5152
+ is_full: rows.length >= PER_PAGE
5153
+ };
5154
+ }
5155
+ var MAX_PAGES = 5;
5156
+ var MAX_SCANNED = MAX_PAGES * PER_PAGE;
5157
+ function wants_more(has_more, selected_count, limit) {
5158
+ return has_more && selected_count < limit;
5159
+ }
5160
+ async function fetch_selected(request) {
5161
+ const selected = [];
5162
+ let page = FIRST_PAGE;
5163
+ let has_more = true;
5164
+ while (page <= MAX_PAGES && wants_more(has_more, selected.length, request.limit)) {
5165
+ const result = await fetch_page(request, page);
5166
+ selected.push(...result.selected);
5167
+ has_more = result.is_full;
5168
+ page += 1;
5169
+ }
5170
+ return {
5171
+ rows: selected.slice(0, request.limit),
5172
+ is_capped: wants_more(has_more, selected.length, request.limit)
5173
+ };
5174
+ }
5175
+ var RELATION_CONCURRENCY = 8;
5176
+ async function read_relations2(rows, repo) {
5177
+ return await bounded_pool.bounded_map(
5178
+ rows,
5179
+ RELATION_CONCURRENCY,
5180
+ async (row) => await read_blocked_by(String(row.number), row, repo)
5181
+ );
5182
+ }
5183
+ async function issue_list(request) {
5184
+ try {
5185
+ const { rows, is_capped } = await fetch_selected(request);
5186
+ const fields = git_gh_issue_rest.split_fields(request.json_fields);
5187
+ const relations = fields.includes(BLOCKED_BY_FIELD) ? await read_relations2(rows, request.repo) : void 0;
5188
+ const mapped = rows.map(
5189
+ (row, index) => git_gh_issue_rest.to_gh_issue(row, fields, relations?.[index])
5190
+ );
5191
+ return { json: JSON.stringify(mapped), is_capped };
5192
+ } catch {
5193
+ return { json: void 0, is_capped: false };
5194
+ }
5195
+ }
5196
+ var git_gh_issue_list = {
5197
+ issue_list
5198
+ };
5199
+
5200
+ // scripts/gh/git-gh-issue-write.ts
5201
+ var PATCH_METHOD = "PATCH";
5202
+ var DELETE_METHOD = "DELETE";
5203
+ var HTML_URL_FILTER = ".html_url";
5204
+ var ID_FILTER = ".id";
5205
+ var LABELS_SEGMENT = "/labels";
5206
+ var COMMENTS_SEGMENT = "/comments";
5207
+ var CLOSED_STATE = "closed";
5208
+ var COLOR_HASH = "#";
5209
+ async function did_write_succeed(write) {
5210
+ try {
5211
+ await write();
5212
+ return true;
5213
+ } catch (error) {
5214
+ error_text.trace_swallowed("git_gh_issue_write.did_write_succeed", error);
5215
+ return false;
5216
+ }
5217
+ }
5218
+ async function issue_edit_body(issue_number, body) {
5219
+ return await git_gh_exec.exec_gh_api({
5220
+ path: git_gh_api_path.issue_api_path(issue_number),
5221
+ method: PATCH_METHOD,
5222
+ body: JSON.stringify({ body }),
5223
+ jq_filter: HTML_URL_FILTER
5224
+ });
5225
+ }
5226
+ async function issue_comment(issue_number, body) {
5227
+ return await git_gh_exec.exec_gh_api({
5228
+ path: `${git_gh_api_path.issue_api_path(issue_number)}${COMMENTS_SEGMENT}`,
5229
+ body: JSON.stringify({ body }),
5230
+ jq_filter: HTML_URL_FILTER
5231
+ });
5232
+ }
5233
+ async function issue_try_comment(issue_number, body) {
5234
+ return await did_write_succeed(async () => await issue_comment(issue_number, body));
5235
+ }
5236
+ async function issue_close(issue_number, comment) {
5237
+ return await did_write_succeed(async () => {
5238
+ if (comment !== void 0) await issue_comment(issue_number, comment);
5239
+ await git_gh_exec.exec_gh_api({
5240
+ path: git_gh_api_path.issue_api_path(issue_number),
5241
+ method: PATCH_METHOD,
5242
+ body: JSON.stringify({ state: CLOSED_STATE })
5243
+ });
5244
+ });
5245
+ }
5246
+ function to_label_color(color) {
5247
+ return color.startsWith(COLOR_HASH) ? color.slice(COLOR_HASH.length) : color;
5248
+ }
5249
+ async function label_ensure(input) {
5250
+ try {
5251
+ await git_gh_exec.exec_gh_api({
5252
+ path: `${git_gh_api_path.repo_api_path()}${LABELS_SEGMENT}`,
5253
+ body: JSON.stringify({
5254
+ name: input.name,
5255
+ color: to_label_color(input.color),
5256
+ description: input.description
5257
+ })
5258
+ });
5259
+ } catch {
5260
+ }
5261
+ }
5262
+ function issue_create_request(input) {
5263
+ const { labels } = input;
5264
+ return {
5265
+ path: git_gh_api_path.issues_api_path(input.repo),
5266
+ body: JSON.stringify({
5267
+ title: input.title,
5268
+ ...labels !== void 0 && { labels },
5269
+ body: input.body
5270
+ }),
5271
+ jq_filter: HTML_URL_FILTER
5272
+ };
5273
+ }
5274
+ async function issue_create_with_label(input) {
5275
+ return await git_gh_exec.exec_gh_api(
5276
+ issue_create_request({ title: input.title, body: input.body, labels: [input.label] })
5277
+ );
5278
+ }
5279
+ async function issue_add_label(issue_number, label2) {
5280
+ return await did_write_succeed(
5281
+ async () => await git_gh_exec.exec_gh_api({
5282
+ path: `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`,
5283
+ body: JSON.stringify({ labels: [label2] })
5284
+ })
5285
+ );
5286
+ }
5287
+ async function issue_remove_label(issue_number, label2) {
5288
+ const labels_path = `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`;
5289
+ await git_gh_exec.exec_gh_api({
5290
+ path: `${labels_path}/${encodeURIComponent(label2)}`,
5291
+ method: DELETE_METHOD
5292
+ });
5293
+ }
5294
+ async function read_issue_id(issue_number) {
5295
+ const raw = await git_gh_exec.exec_gh_api({
5296
+ path: git_gh_api_path.issue_api_path(issue_number),
5297
+ jq_filter: ID_FILTER
5298
+ });
5299
+ const issue_id = Number(raw.trim());
5300
+ if (!Number.isSafeInteger(issue_id) || issue_id <= 0) {
5301
+ throw new Error(`gh api answered no database id for issue #${issue_number}`);
5302
+ }
5303
+ return issue_id;
5304
+ }
5305
+ async function issue_add_blocked_by(issue_number, blocker) {
5306
+ return await did_write_succeed(async () => {
5307
+ const issue_id = await read_issue_id(blocker);
5308
+ await git_gh_exec.exec_gh_api({
5309
+ path: git_gh_api_path.blocked_by_api_path(issue_number),
5310
+ body: JSON.stringify({ issue_id })
5311
+ });
5312
+ });
5313
+ }
5314
+ async function issue_remove_blocked_by(issue_number, blocker) {
5315
+ return await did_write_succeed(async () => {
5316
+ const issue_id = await read_issue_id(blocker);
5317
+ const relations_path = git_gh_api_path.blocked_by_api_path(issue_number);
5318
+ await git_gh_exec.exec_gh_api({
5319
+ path: `${relations_path}/${String(issue_id)}`,
5320
+ method: DELETE_METHOD
5321
+ });
5322
+ });
5323
+ }
5324
+ var git_gh_issue_write = {
5325
+ issue_edit_body,
5326
+ issue_comment,
5327
+ issue_try_comment,
5328
+ issue_close,
5329
+ label_ensure,
5330
+ issue_create_request,
5331
+ issue_create_with_label,
5332
+ issue_add_label,
5333
+ issue_remove_label,
5334
+ issue_add_blocked_by,
5335
+ issue_remove_blocked_by
5336
+ };
5337
+
5338
+ // scripts/gh/git-gh-issue.ts
5339
+ var NUMBER_AND_BODY_FIELDS = "number,body";
5340
+ var BACKLOG_SCAN_FIELDS = `number,title,body,${BLOCKED_BY_COUNT_FIELD}`;
5341
+ var NUMBER_TITLE_AND_LABELS_FIELDS = "number,title,labels";
5342
+ var SUMMARY_FIELDS = "number,title,labels,createdAt";
5343
+ var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy`;
5344
+ var { issue_list: issue_list2 } = git_gh_issue_list;
5345
+ async function issue_list_recent(limit) {
5346
+ return await issue_list2({ json_fields: SUMMARY_FIELDS, limit });
5347
+ }
5348
+ async function issue_list_open_bodies(limit) {
5349
+ return await issue_list2({ json_fields: BACKLOG_SCAN_FIELDS, limit });
5350
+ }
5351
+ async function issue_list_recently_closed(limit) {
5352
+ return await issue_list2({
5353
+ json_fields: NUMBER_TITLE_AND_LABELS_FIELDS,
5354
+ limit,
5355
+ state: "closed",
5356
+ sort: "updated"
5357
+ });
5358
+ }
5359
+ async function issue_search_body(term, limit) {
5360
+ return await issue_list2({
5361
+ json_fields: NUMBER_AND_BODY_FIELDS,
5362
+ limit,
5363
+ body_term: term
5364
+ });
5365
+ }
5366
+ async function issue_list_by_label(label2, limit) {
5367
+ return await issue_list2({ json_fields: NUMBER_AND_BODY_FIELDS, limit, label: label2 });
5368
+ }
5369
+ async function issue_list_by_label_summary(label2, limit, options) {
5370
+ return await issue_list2({
5371
+ json_fields: options?.json_fields ?? PICKUP_FIELDS,
5372
+ limit,
5373
+ label: label2
5374
+ });
5375
+ }
5376
+ async function issue_list_by_label_in_repo(label2, limit, repo) {
5377
+ return await issue_list2({ json_fields: SUMMARY_FIELDS, limit, label: label2, repo });
5378
+ }
5379
+ var git_gh_issue = {
5380
+ ...git_gh_issue_read,
5381
+ ...git_gh_issue_write,
5382
+ issue_list_recent,
5383
+ issue_list_by_label,
5384
+ issue_list_by_label_in_repo,
5385
+ issue_list_by_label_summary,
5386
+ issue_search_body,
5387
+ issue_list_open_bodies,
5388
+ issue_list_recently_closed
5389
+ };
5390
+
5391
+ // scripts/ci/pr-classification.ts
5392
+ import { readFileSync as readFileSync4 } from "node:fs";
5393
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5394
+ import { z as z13 } from "zod";
5395
+ var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
5396
+ var ISSUE_BUG_LABEL = "bug";
5397
+ var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
5398
+ var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
5399
+ var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
5400
+ var LABEL = z13.object({ name: z13.string() });
5401
+ var USER = z13.object({ login: z13.string() });
5402
+ var PULL_REQUEST = z13.object({ number: z13.number(), user: USER });
5403
+ var EVENT = z13.object({
5404
+ pull_request: PULL_REQUEST,
5405
+ repository: z13.object({ full_name: z13.string() })
5406
+ });
5407
+ function classification_error(labels, author) {
5408
+ const legacy = labels.filter((label2) => LEGACY_LABELS.has(label2.toLowerCase()));
5409
+ if (legacy.length > 0) return `Remove legacy release labels: ${legacy.join(", ")}`;
5410
+ const selected = labels.filter((label2) => CLASSIFICATION_SET.has(label2.toLowerCase()));
5411
+ if (selected.length > 1) {
5412
+ return `Choose exactly one release classification; found: ${selected.join(", ")}`;
5413
+ }
5414
+ if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
5415
+ return `Choose one release classification: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}`;
5416
+ }
5417
+ function declaration(label2) {
5418
+ return `${DECLARATION_PREFIX}${label2}`;
5419
+ }
5420
+ function is_classification(value) {
5421
+ return CLASSIFICATION_SET.has(value);
5422
+ }
5423
+ function release_candidates(candidates) {
5424
+ if (!candidates.includes(BREAKING_CHANGE_LABEL)) return candidates;
5425
+ return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
5426
+ }
5427
+ function issue_candidates(issue_json) {
5428
+ const issue = z13.object({ labels: z13.array(LABEL), body: z13.string().nullable() }).parse(JSON.parse(issue_json));
5429
+ const names = issue.labels.map((label2) => label2.name.toLowerCase());
5430
+ const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
5431
+ const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
5432
+ const unknown = declared.filter((value) => !is_classification(value));
5433
+ if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
5434
+ if (names.includes(ISSUE_BUG_LABEL) && !candidates.includes(BREAKING_CHANGE_LABEL)) {
5435
+ candidates.push(BUGFIX_LABEL);
5436
+ }
5437
+ return candidates;
5438
+ }
5439
+ function select_issue_classification(issue_json) {
5440
+ const candidates = issue_candidates(issue_json);
5441
+ const selected = [...new Set(candidates)];
5442
+ const [label2] = selected;
5443
+ if (label2 === void 0 || selected.length > 1) {
5444
+ throw new Error(
5445
+ `Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "${DECLARATION_PREFIX}<label>" to the Issue body.`
5446
+ );
5447
+ }
5448
+ return label2;
5449
+ }
5450
+ function fetch_current_labels(repository2, pull_number) {
5451
+ const path7 = `repos/${repository2}/issues/${String(pull_number)}/labels`;
5452
+ try {
5453
+ const names = git_gh_exec.exec_gh_api_sync({ path: path7, jq_filter: ".[].name" });
5454
+ return names.split("\n").filter((name) => name !== "");
5455
+ } catch (error) {
5456
+ const detail = error_text.message_of(error);
5457
+ throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error });
5458
+ }
5459
+ }
5460
+ function check_event(event_path, read_labels = fetch_current_labels) {
5461
+ const event = EVENT.parse(JSON.parse(readFileSync4(event_path, "utf8")));
5462
+ const labels = read_labels(event.repository.full_name, event.pull_request.number);
5463
+ return classification_error(labels, event.pull_request.user.login);
5464
+ }
5465
+ if (process.argv[1] === fileURLToPath2(import.meta.url)) {
5466
+ const event_path = process.env["GITHUB_EVENT_PATH"];
5467
+ if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
5468
+ const error = check_event(event_path);
5469
+ if (error) {
5470
+ console.error(`::error::${error}`);
5471
+ process.exitCode = 1;
5472
+ }
5473
+ }
5474
+ var pr_classification = {
5475
+ classification_error,
5476
+ check_event,
5477
+ fetch_current_labels,
5478
+ declaration,
5479
+ is_classification,
5480
+ select_issue_classification
5481
+ };
5482
+
5483
+ // scripts/lib/poll.ts
5484
+ async function sleep3(duration_ms) {
5485
+ await new Promise((resolve3) => {
5486
+ setTimeout(resolve3, duration_ms);
5487
+ });
5488
+ }
5489
+ async function wait_between(wait, interval_ms, attempt, attempts) {
5490
+ if (attempt < attempts - 1) await wait(interval_ms);
5491
+ }
5492
+ async function poll_until(is_done2, options) {
5493
+ const wait = options.sleeper ?? sleep3;
5494
+ for (let attempt = 0; attempt < options.attempts; attempt += 1) {
5495
+ if (await is_done2()) return true;
5496
+ await wait_between(wait, options.interval_ms, attempt, options.attempts);
5497
+ }
5498
+ return false;
5499
+ }
5500
+ var poll = { poll_until, sleep: sleep3 };
5501
+
5502
+ // scripts/gh/git-gh-pr.ts
5503
+ import { z as z17 } from "zod";
5504
+
5505
+ // scripts/gh/git-gh-pr-rest.ts
5506
+ import { z as z14 } from "zod";
5507
+ var rest_repo_schema = z14.looseObject({ full_name: z14.string().optional() }).nullish();
5508
+ var rest_pull_side_schema = z14.looseObject({ repo: rest_repo_schema });
5509
+ var rest_pull_schema = z14.looseObject({
5510
+ number: z14.number(),
5511
+ html_url: z14.string().optional(),
5512
+ body: z14.string().nullish(),
5513
+ state: z14.string().optional(),
5514
+ merged: z14.boolean().nullish(),
5515
+ merged_at: z14.string().nullish(),
5516
+ mergeable: z14.boolean().nullish(),
5517
+ mergeable_state: z14.string().optional(),
5518
+ // `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
5519
+ // REST hangs the check runs and the status contexts off the commit rather than off the pull
5520
+ // request (joshuafolkken/kit#1028).
5521
+ head: z14.looseObject({ ref: z14.string().optional(), sha: z14.string().optional(), repo: rest_repo_schema }).nullish(),
5522
+ base: rest_pull_side_schema.nullish(),
5523
+ // `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
5524
+ // is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`
5525
+ // (joshuafolkken/kit#2497).
5526
+ node_id: z14.string().optional(),
5527
+ auto_merge: z14.looseObject({}).nullish()
5528
+ });
5529
+ var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
5530
+ var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
5531
+ var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
5532
+ var OPEN_STATE = "open";
5533
+ var EMPTY_BODY2 = "";
5534
+ function parse_rest_pull(rest_json) {
5535
+ const parsed = parse_json_object_safe(rest_json, rest_pull_schema);
5536
+ if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
5537
+ return parsed;
5538
+ }
5539
+ function parse_rest_pulls(rest_json) {
5540
+ const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
5541
+ if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
5542
+ return parsed;
5543
+ }
5544
+ function select_pull(pulls) {
5545
+ return pulls.find((pull) => pull.state === OPEN_STATE) ?? pulls[0];
5546
+ }
5547
+ function is_merged(pull) {
5548
+ return pull.merged === true || typeof pull.merged_at === "string";
5549
+ }
5550
+ function to_pr_state(pull) {
5551
+ return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
5552
+ }
5553
+ function to_repo_name(side) {
5554
+ return side?.repo?.full_name;
5555
+ }
5556
+ function is_same_repository_head(pull) {
5557
+ const head_repo = to_repo_name(pull.head);
5558
+ return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
5559
+ }
5560
+ function to_pr_info(pull) {
5561
+ return {
5562
+ mergeable: pull.mergeable,
5563
+ mergeStateStatus: to_gh_state(pull.mergeable_state),
5564
+ state: to_pr_state(pull)
5565
+ };
5566
+ }
5567
+ function to_pr_comments(rest_json) {
5568
+ const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
5569
+ if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
5570
+ return parsed.map((comment) => ({
5571
+ body: comment.body ?? EMPTY_BODY2,
5572
+ url: comment.html_url,
5573
+ author: { login: comment.user?.login }
5574
+ }));
5575
+ }
5576
+ var git_gh_pr_rest = {
5577
+ parse_rest_pull,
5578
+ parse_rest_pulls,
5579
+ select_pull,
5580
+ is_same_repository_head,
5581
+ is_merged,
5582
+ to_pr_state,
5583
+ to_pr_info,
5584
+ to_pr_comments
5585
+ };
5586
+
5587
+ // scripts/gh/git-gh-pr-read.ts
5588
+ var pr_number_by_branch = /* @__PURE__ */ new Map();
5589
+ var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
5590
+ var COMMENTS_QUERY = FULL_PAGE_QUERY;
5591
+ var OWNER_PLACEHOLDER = "{owner}";
5592
+ var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
5593
+ var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
5594
+ function lookup_path(branch_name) {
5595
+ const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
5596
+ return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
5597
+ }
5598
+ async function fetch_pr_number(branch_name) {
5599
+ const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
5600
+ return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
5601
+ }
5602
+ var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
5603
+ async function fetch_pr_number_read(branch_name) {
5604
+ try {
5605
+ const pr_number = await fetch_pr_number(branch_name);
5606
+ if (pr_number === void 0) return { kind: "missing" };
5607
+ pr_number_by_branch.set(branch_name, pr_number);
5608
+ return { kind: "read", pr_number };
5609
+ } catch (error) {
5610
+ return { kind: "unreadable", cause: error };
5611
+ }
5612
+ }
5613
+ async function start_pr_number_read(branch_name) {
5614
+ const pending = pending_pr_number_by_branch.get(branch_name);
5615
+ if (pending !== void 0) return await pending;
5616
+ const lookup = fetch_pr_number_read(branch_name);
5617
+ pending_pr_number_by_branch.set(branch_name, lookup);
5618
+ try {
5619
+ return await lookup;
5620
+ } finally {
5621
+ pending_pr_number_by_branch.delete(branch_name);
5622
+ }
5623
+ }
5624
+ function forget_pr_numbers() {
5625
+ pr_number_by_branch.clear();
5626
+ pending_pr_number_by_branch.clear();
5627
+ }
5628
+ async function read_pr_number(branch_name) {
5629
+ const cached = pr_number_by_branch.get(branch_name);
5630
+ if (cached !== void 0) return { kind: "read", pr_number: cached };
5631
+ return await start_pr_number_read(branch_name);
5632
+ }
5633
+ async function resolve_pr_number(branch_name) {
5634
+ const read3 = await read_pr_number(branch_name);
5635
+ return read3.kind === "read" ? read3.pr_number : void 0;
5636
+ }
5637
+ function to_unreadable_error(branch_name, cause) {
5638
+ return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
5639
+ }
5640
+ async function require_pr_number(branch_name) {
5641
+ const read3 = await read_pr_number(branch_name);
5642
+ if (read3.kind === "read") return read3.pr_number;
5643
+ if (read3.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
5644
+ throw to_unreadable_error(branch_name, read3.cause);
5645
+ }
5646
+ async function read_pull(pr_number) {
5647
+ const json = await git_gh_exec.exec_gh_api({
5648
+ path: git_gh_api_path.pull_api_path(String(pr_number))
5649
+ });
5650
+ return git_gh_pr_rest.parse_rest_pull(json);
5651
+ }
5652
+ async function read_pull_of_branch(branch_name) {
5653
+ const pr_number = await resolve_pr_number(branch_name);
5654
+ if (pr_number === void 0) return void 0;
5655
+ try {
5656
+ return await read_pull(pr_number);
5657
+ } catch {
5658
+ return void 0;
5659
+ }
5660
+ }
5661
+ async function pr_exists(branch_name) {
5662
+ const read3 = await read_pr_number(branch_name);
5663
+ if (read3.kind === "unreadable") throw to_unreadable_error(branch_name, read3.cause);
5664
+ return read3.kind === "read";
5665
+ }
5666
+ async function pr_get_number(branch_name) {
5667
+ return await resolve_pr_number(branch_name);
5668
+ }
5669
+ async function pr_get_url(branch_name) {
5670
+ const pull = await read_pull_of_branch(branch_name);
5671
+ if (pull?.html_url === void 0) return void 0;
5672
+ return git_gh_helpers.parse_pr_state_string(pull.html_url);
5673
+ }
5674
+ async function pr_get_merge_state(branch_name) {
5675
+ const pull = await read_pull_of_branch(branch_name);
5676
+ if (pull === void 0) return void 0;
5677
+ return {
5678
+ is_merged: git_gh_pr_rest.is_merged(pull),
5679
+ merged_at: pull.merged_at ?? void 0,
5680
+ head_sha: pull.head?.sha
5681
+ };
5682
+ }
5683
+ async function pr_get_body(branch_name) {
5684
+ const pull = await read_pull_of_branch(branch_name);
5685
+ const body = pull?.body;
5686
+ return typeof body === "string" && body.length > 0 ? body : void 0;
5687
+ }
5688
+ async function pr_view(branch_name) {
5689
+ const pull = await read_pull_of_branch(branch_name);
5690
+ if (pull === void 0) return "";
5691
+ return JSON.stringify(git_gh_pr_rest.to_pr_info(pull));
5692
+ }
5693
+ async function read_comments(branch_name, to_path, to_json) {
5694
+ const pr_number = await resolve_pr_number(branch_name);
5695
+ if (pr_number === void 0) return void 0;
5696
+ const path7 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
5697
+ try {
5698
+ return to_json(await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true }));
5699
+ } catch {
5700
+ return void 0;
5701
+ }
5702
+ }
5703
+ function to_comments_json(raw) {
5704
+ return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
5705
+ }
5706
+ function as_served(raw) {
5707
+ return raw;
5708
+ }
5709
+ async function pr_get_comments(branch_name) {
5710
+ return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
5711
+ }
5712
+ async function pr_get_review_comments(branch_name) {
5713
+ return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
5714
+ }
5715
+ var git_gh_pr_read = {
5716
+ pr_exists,
5717
+ pr_get_number,
5718
+ pr_get_url,
5719
+ pr_get_body,
5720
+ pr_get_merge_state,
5721
+ pr_view,
5722
+ pr_get_comments,
5723
+ pr_get_review_comments
5724
+ };
5725
+
5726
+ // scripts/gh/git-gh-pr-review.ts
5727
+ import { z as z15 } from "zod";
5728
+ var REVIEW_APPROVED = "APPROVED";
5729
+ var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
5730
+ var REVIEW_NONE = "";
5731
+ var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
5732
+ var rest_review_schema = z15.looseObject({
5733
+ state: z15.string().optional(),
5734
+ user: z15.looseObject({ login: z15.string().optional() }).nullish()
5735
+ });
5736
+ var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
5737
+ function parse_rest_reviews(reviews_json) {
5738
+ const parsed = parse_json_array_or_undefined(reviews_json, rest_review_schema);
5739
+ if (parsed === void 0) throw new Error(NOT_A_REVIEW_LISTING);
5740
+ return parsed;
5741
+ }
5742
+ var UNATTRIBUTED_REVIEWER = "(no author)";
5743
+ function read_reviewer(review) {
5744
+ const login = review.user?.login;
5745
+ return typeof login === "string" && login.length > 0 ? login : UNATTRIBUTED_REVIEWER;
5746
+ }
5747
+ function read_state_changing(review) {
5748
+ const state = review.state?.toUpperCase();
5749
+ return state !== void 0 && STATE_CHANGING_REVIEWS.has(state) ? state : void 0;
5750
+ }
5751
+ function fold_latest_states(reviews) {
5752
+ const latest = /* @__PURE__ */ new Map();
5753
+ for (const review of reviews) {
5754
+ const state = read_state_changing(review);
5755
+ if (state !== void 0) latest.set(read_reviewer(review), state);
5756
+ }
5757
+ return latest;
5758
+ }
5759
+ function to_review_decision(reviews_json) {
5760
+ const latest = new Set(fold_latest_states(parse_rest_reviews(reviews_json)).values());
5761
+ if (latest.has(REVIEW_CHANGES_REQUESTED)) return REVIEW_CHANGES_REQUESTED;
5762
+ return latest.has(REVIEW_APPROVED) ? REVIEW_APPROVED : REVIEW_NONE;
5763
+ }
5764
+ var git_gh_pr_review = { to_review_decision };
5765
+
5766
+ // scripts/gh/git-gh-pr-rollup.ts
5767
+ import { z as z16 } from "zod";
5768
+ var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
5769
+ var TYPE_NAME_KEY = "__typename";
5770
+ var rollup_element_schema = z16.looseObject({});
5771
+ var check_runs_page_schema = z16.looseObject({
5772
+ // Required, not optional: GitHub always names the key, and accepting a page without one would
5773
+ // answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
5774
+ check_runs: z16.array(rollup_element_schema)
5775
+ });
5776
+ var id_holder_schema = z16.object({ id: z16.number() });
5777
+ var status_page_schema = z16.looseObject({
5778
+ statuses: z16.array(rollup_element_schema)
5779
+ });
5780
+ var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
5781
+ var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
5782
+ function read_pages(raw_json, schema, message) {
5783
+ const pages = parse_json_array_or_undefined(raw_json, schema);
5784
+ if (pages === void 0) throw new Error(message);
5785
+ return pages;
5786
+ }
5787
+ var NO_SUITE = -1;
5788
+ function read_id(value) {
5789
+ const parsed = id_holder_schema.safeParse(value);
5790
+ return parsed.success ? parsed.data.id : void 0;
5791
+ }
5792
+ function check_run_group_key(run3) {
5793
+ const { name, app } = run3;
5794
+ if (typeof name !== "string") return void 0;
5795
+ return JSON.stringify([name, read_id(app)]);
5796
+ }
5797
+ function read_rank(run3) {
5798
+ const id = read_id(run3);
5799
+ if (id === void 0) return void 0;
5800
+ return { suite: read_id(run3["check_suite"]) ?? NO_SUITE, run: id };
5801
+ }
5802
+ function is_ranked_above(candidate, current) {
5803
+ if (candidate.suite !== current.suite) return candidate.suite > current.suite;
5804
+ return candidate.run > current.run;
5805
+ }
5806
+ function should_replace(previous, rank) {
5807
+ const previous_rank = previous === void 0 ? void 0 : read_rank(previous);
5808
+ return previous_rank === void 0 || is_ranked_above(rank, previous_rank);
5809
+ }
5810
+ function remember_newest_run(run3, newest) {
5811
+ const key2 = check_run_group_key(run3);
5812
+ const rank = read_rank(run3);
5813
+ if (key2 === void 0 || rank === void 0) return;
5814
+ if (should_replace(newest.get(key2), rank)) newest.set(key2, run3);
5815
+ }
5816
+ function is_newest_run(run3, newest) {
5817
+ const key2 = check_run_group_key(run3);
5818
+ if (key2 === void 0 || read_rank(run3) === void 0) return true;
5819
+ return newest.get(key2) === run3;
5820
+ }
5821
+ function to_check_run_items(check_runs_json) {
5822
+ const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
5823
+ const runs = pages.flatMap((page) => page.check_runs);
5824
+ const newest = /* @__PURE__ */ new Map();
5825
+ for (const run3 of runs) remember_newest_run(run3, newest);
5826
+ return runs.filter((run3) => is_newest_run(run3, newest));
5827
+ }
5828
+ function to_status_context_items(status_json) {
5829
+ const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
5830
+ return pages.flatMap((page) => page.statuses).map((status2) => ({ ...status2, [TYPE_NAME_KEY]: STATUS_CONTEXT_TYPE_NAME }));
5831
+ }
5832
+ function to_status_check_rollup(input) {
5833
+ return [
5834
+ ...to_check_run_items(input.check_runs_json),
5835
+ ...to_status_context_items(input.status_json)
5836
+ ];
5837
+ }
5838
+ var git_gh_pr_rollup = {
5839
+ to_status_check_rollup
5840
+ };
5841
+
5842
+ // scripts/gh/git-gh-pr-snapshot.ts
5843
+ var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
5844
+ async function read_object_pages(path7) {
5845
+ return await git_gh_exec.exec_gh_api({
5846
+ path: `${path7}${FULL_PAGE_QUERY}`,
5847
+ should_paginate: true,
5848
+ should_slurp: true
5849
+ });
5850
+ }
5851
+ async function read_array_pages(path7) {
5852
+ return await git_gh_exec.exec_gh_api({
5853
+ path: `${path7}${FULL_PAGE_QUERY}`,
5854
+ should_paginate: true
5855
+ });
5856
+ }
5857
+ function read_head_sha(pull) {
5858
+ const sha = pull.head?.sha;
5859
+ if (sha === void 0) throw new Error(NO_HEAD_SHA_MESSAGE);
5860
+ return sha;
5861
+ }
5862
+ async function read_status_check_rollup(commit_sha) {
5863
+ const [check_runs_json, status_json] = await Promise.all([
5864
+ read_object_pages(git_gh_api_path.commit_check_runs_api_path(commit_sha)),
5865
+ read_object_pages(git_gh_api_path.commit_status_api_path(commit_sha))
5866
+ ]);
5867
+ return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
5868
+ }
5869
+ async function pr_get_review_decision(pr_number) {
5870
+ const path7 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
5871
+ return git_gh_pr_review.to_review_decision(await read_array_pages(path7));
5872
+ }
5873
+ async function build_checks_fields(pull) {
5874
+ return {
5875
+ statusCheckRollup: await read_status_check_rollup(read_head_sha(pull)),
5876
+ mergeStateStatus: to_gh_state(pull.mergeable_state)
5877
+ };
5878
+ }
5879
+ async function resolve_pull(branch_name) {
5880
+ const pr_number = await require_pr_number(branch_name);
5881
+ return { pr_number, pull: await read_pull(pr_number) };
5882
+ }
5883
+ async function pr_get_checks_snapshot(branch_name) {
5884
+ const { pr_number, pull } = await resolve_pull(branch_name);
5885
+ return { pr_number, snapshot_json: JSON.stringify(await build_checks_fields(pull)) };
5886
+ }
5887
+ async function pr_get_state_snapshot(branch_name) {
5888
+ const { pr_number, pull } = await resolve_pull(branch_name);
5889
+ const [fields, review_decision] = await Promise.all([
5890
+ build_checks_fields(pull),
5891
+ pr_get_review_decision(pr_number)
5892
+ ]);
5893
+ return JSON.stringify({ ...fields, reviewDecision: review_decision });
5894
+ }
5895
+ var git_gh_pr_snapshot = {
5896
+ pr_get_state_snapshot,
5897
+ pr_get_checks_snapshot,
5898
+ pr_get_review_decision
5899
+ };
5900
+
5901
+ // scripts/gh/git-gh-pr.ts
5902
+ var HTML_URL_FILTER2 = ".html_url";
5903
+ var PUT_METHOD = "PUT";
5904
+ var PATCH_METHOD2 = "PATCH";
5905
+ var MERGE_COMMIT_METHOD = "merge";
5906
+ var MERGE_REQUEST_TIMEOUT_MS = 18e4;
5907
+ var MERGE_UNCONFIRMED_MESSAGE = "gh api could not read the pull request back after the merge request failed";
5908
+ var MERGE_READ_BACK_ATTEMPTS = 3;
5909
+ var MERGE_READ_BACK_INTERVAL_MS = 1e3;
5910
+ var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
5911
+ var LABELS_SEGMENT2 = "/labels";
5912
+ var PR_LABELS = z17.array(z17.object({ name: z17.string() }));
5913
+ async function add_pr_classification(number, label2) {
5914
+ await git_gh_exec.exec_gh_api({
5915
+ path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
5916
+ body: JSON.stringify({ labels: [label2] })
5917
+ });
5918
+ }
5919
+ async function pr_get_classification(branch_name) {
5920
+ const number = await require_pr_number(branch_name);
5921
+ const response = await git_gh_exec.exec_gh_api({
5922
+ path: `${git_gh_api_path.issue_api_path(String(number))}${LABELS_SEGMENT2}`
5923
+ });
5924
+ const names = PR_LABELS.parse(JSON.parse(response)).map((label2) => label2.name.toLowerCase());
5925
+ const selected = names.filter((name) => pr_classification.is_classification(name));
5926
+ if (selected.length === 0) return void 0;
5927
+ const error = pr_classification.classification_error(names, "human");
5928
+ if (error !== void 0) throw new Error(error);
5929
+ return selected[0];
5930
+ }
5931
+ async function pr_ensure_classification(branch_name, label2) {
5932
+ if (await pr_get_classification(branch_name) !== void 0) return;
5933
+ const number = await require_pr_number(branch_name);
5934
+ await add_pr_classification(String(number), label2);
5935
+ }
5936
+ async function post_pull_request(title, body) {
5937
+ const base = await git_command.get_default_branch();
5938
+ const head = await git_command.branch();
5939
+ forget_pr_numbers();
5940
+ try {
5941
+ return await git_gh_exec.exec_gh_api({
5942
+ path: git_gh_api_path.pulls_api_path(),
5943
+ body: JSON.stringify({ title, body, head, base }),
5944
+ jq_filter: HTML_URL_FILTER2
5945
+ });
5946
+ } catch (error) {
5947
+ return git_gh_helpers.handle_pr_create_error(error);
5948
+ }
5949
+ }
5950
+ async function pr_create(title, body, label2) {
5951
+ const classification_error2 = pr_classification.classification_error([label2], "human");
5952
+ if (classification_error2 !== void 0) throw new Error(classification_error2);
5953
+ const url = await post_pull_request(title, body);
5954
+ const number = PR_NUMBER_PATTERN.exec(url)?.[1];
5955
+ if (number === void 0) throw new Error(`Could not identify the created pull request: ${url}`);
5956
+ await add_pr_classification(number, label2);
5957
+ return url;
5958
+ }
5959
+ async function pr_comment(branch_name, body) {
5960
+ const pr_number = await require_pr_number(branch_name);
5961
+ return await git_gh_issue_write.issue_comment(String(pr_number), body);
5962
+ }
5963
+ async function pr_update_body(branch_name, body) {
5964
+ const pr_number = await require_pr_number(branch_name);
5965
+ await git_gh_exec.exec_gh_api({
5966
+ path: git_gh_api_path.pull_api_path(String(pr_number)),
5967
+ method: PATCH_METHOD2,
5968
+ body: JSON.stringify({ body })
5969
+ });
5970
+ }
5971
+ async function put_merge(pr_number) {
5972
+ await git_gh_exec.exec_gh_api({
5973
+ path: git_gh_api_path.pull_merge_api_path(String(pr_number)),
5974
+ method: PUT_METHOD,
5975
+ body: JSON.stringify({ merge_method: MERGE_COMMIT_METHOD }),
5976
+ timeout_ms: MERGE_REQUEST_TIMEOUT_MS
5977
+ });
5978
+ }
5979
+ async function read_merge_state(pr_number) {
5980
+ try {
5981
+ return git_gh_pr_rest.is_merged(await read_pull(pr_number));
5982
+ } catch (error) {
5983
+ console.warn(`Could not read the pull request back: ${String(error)}`);
5984
+ return void 0;
5985
+ }
5986
+ }
5987
+ async function sleep_between_read_backs(attempt) {
5988
+ if (attempt < MERGE_READ_BACK_ATTEMPTS - 1) await poll.sleep(MERGE_READ_BACK_INTERVAL_MS);
5989
+ }
5990
+ async function has_merge_landed(pr_number, merge_error) {
5991
+ for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
5992
+ const is_merged2 = await read_merge_state(pr_number);
5993
+ if (is_merged2 !== void 0) return is_merged2;
5994
+ await sleep_between_read_backs(attempt);
5995
+ }
5996
+ throw new Error(MERGE_UNCONFIRMED_MESSAGE, { cause: merge_error });
5997
+ }
5998
+ async function pr_merge(branch_name) {
5999
+ const pr_number = await require_pr_number(branch_name);
6000
+ try {
6001
+ await put_merge(pr_number);
6002
+ } catch (error) {
6003
+ if (!await has_merge_landed(pr_number, error)) throw error;
6004
+ }
6005
+ }
6006
+ var git_gh_pr = {
6007
+ ...git_gh_pr_read,
6008
+ ...git_gh_pr_snapshot,
6009
+ pr_create,
6010
+ pr_get_classification,
6011
+ pr_ensure_classification,
6012
+ pr_comment,
6013
+ pr_merge,
6014
+ pr_update_body
6015
+ };
6016
+
6017
+ // scripts/gh/git-gh-pr-auto-merge.ts
6018
+ var GRAPHQL_PATH = "graphql";
6019
+ var OPEN_PULLS_QUERY = `${git_gh_api_path.FULL_PAGE_QUERY}&state=open`;
6020
+ var MISSING_NODE_ID_MESSAGE = "gh api answered a pull request without a node_id";
6021
+ var ENABLE_AUTO_MERGE_MUTATION = "mutation($id: ID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE }) { clientMutationId } }";
6022
+ async function pr_enable_auto_merge(branch_name) {
6023
+ const pull = await read_pull(await require_pr_number(branch_name));
6024
+ if (pull.node_id === void 0) throw new Error(MISSING_NODE_ID_MESSAGE);
6025
+ await git_gh_exec.exec_gh_api({
6026
+ path: GRAPHQL_PATH,
6027
+ body: JSON.stringify({ query: ENABLE_AUTO_MERGE_MUTATION, variables: { id: pull.node_id } })
6028
+ });
6029
+ }
6030
+ async function pr_is_merged(branch_name) {
6031
+ try {
6032
+ return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)));
6033
+ } catch (error) {
6034
+ error_text.trace_swallowed("git_gh_pr_auto_merge.pr_is_merged", error);
6035
+ return false;
6036
+ }
6037
+ }
6038
+ function head_reference_of(pull) {
6039
+ return pull.head?.ref ?? "";
6040
+ }
6041
+ function has_head_prefix(pull, prefix) {
6042
+ return git_gh_pr_rest.is_same_repository_head(pull) && head_reference_of(pull).startsWith(prefix);
6043
+ }
6044
+ function to_open_pull(pull) {
6045
+ return {
6046
+ number: pull.number,
6047
+ head_ref: head_reference_of(pull),
6048
+ has_auto_merge: pull.auto_merge !== null && pull.auto_merge !== void 0
6049
+ };
6050
+ }
6051
+ async function pr_list_open_with_head_prefix(prefix) {
6052
+ const json = await git_gh_exec.exec_gh_api({
6053
+ path: `${git_gh_api_path.pulls_api_path()}${OPEN_PULLS_QUERY}`,
6054
+ should_paginate: true
6055
+ });
6056
+ return git_gh_pr_rest.parse_rest_pulls(json).filter((pull) => has_head_prefix(pull, prefix)).map((pull) => to_open_pull(pull));
6057
+ }
6058
+ var git_gh_pr_auto_merge = {
6059
+ pr_enable_auto_merge,
6060
+ pr_is_merged,
6061
+ pr_list_open_with_head_prefix
6062
+ };
6063
+
6064
+ // scripts/gh/git-gh-repo.ts
6065
+ async function repo_get_name_with_owner() {
6066
+ try {
6067
+ const result = await git_gh_exec.exec_gh_api({
6068
+ path: git_gh_api_path.repo_api_path(),
6069
+ jq_filter: ".full_name"
6070
+ });
6071
+ return git_gh_helpers.parse_pr_state_string(result);
6072
+ } catch {
6073
+ return void 0;
6074
+ }
6075
+ }
6076
+ var git_gh_repo = {
6077
+ repo_get_name_with_owner
6078
+ };
6079
+
6080
+ // scripts/gh/git-pr-checks-parse.ts
6081
+ var CHECK_STATUS_PASS = "pass";
6082
+ var CHECK_STATUS_PENDING = "pending";
6083
+ var CHECK_STATUS_FAIL = "fail";
6084
+ var CHECK_STATUS_MISSING = "missing";
6085
+ var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["success", "skipped", "neutral"]);
6086
+ var KEY_TYPE_NAME = "__typename";
6087
+ var KEY_STATE = "state";
6088
+ var KEY_STATUS = "status";
6089
+ var KEY_CONCLUSION = "conclusion";
6090
+ var KEY_NAME = "name";
6091
+ var KEY_CONTEXT = "context";
6092
+ function read_string(value) {
6093
+ if (typeof value !== "string") return void 0;
6094
+ const trimmed2 = value.trim();
6095
+ if (trimmed2.length === 0) return void 0;
6096
+ return trimmed2;
6097
+ }
6098
+ var PENDING_STATUS_STATES = /* @__PURE__ */ new Set(["pending", "expected"]);
6099
+ function parse_status_context(item) {
6100
+ const state = read_string(item[KEY_STATE])?.toLowerCase();
6101
+ if (state === "success") return CHECK_STATUS_PASS;
6102
+ if (state !== void 0 && PENDING_STATUS_STATES.has(state)) return CHECK_STATUS_PENDING;
6103
+ return CHECK_STATUS_FAIL;
6104
+ }
6105
+ function is_passing_conclusion(item) {
6106
+ const conclusion = read_string(item[KEY_CONCLUSION])?.toLowerCase();
6107
+ return conclusion !== void 0 && PASSING_CONCLUSIONS.has(conclusion);
6108
+ }
6109
+ function parse_check_run(item) {
6110
+ const status2 = read_string(item[KEY_STATUS])?.toLowerCase();
6111
+ if (status2 !== "completed") return CHECK_STATUS_PENDING;
6112
+ return is_passing_conclusion(item) ? CHECK_STATUS_PASS : CHECK_STATUS_FAIL;
6113
+ }
6114
+ function parse_rollup_status(item) {
6115
+ const type_name = read_string(item[KEY_TYPE_NAME]);
6116
+ if (type_name === "StatusContext") return parse_status_context(item);
6117
+ return parse_check_run(item);
6118
+ }
6119
+ function read_rollup_array(parsed) {
6120
+ const result = pr_raw_schema.safeParse(parsed);
6121
+ if (!result.success) return [];
6122
+ return result.data.statusCheckRollup ?? [];
6123
+ }
6124
+ function parse_rollup_item(item) {
6125
+ const name = read_string(item[KEY_NAME]) ?? read_string(item[KEY_CONTEXT]);
6126
+ if (name === void 0) return void 0;
6127
+ return { name, status: parse_rollup_status(item) };
6128
+ }
6129
+ function parse_rollup_checks(raw_json) {
6130
+ const parsed = json_value.parse_or_undefined(raw_json);
6131
+ const rollup = read_rollup_array(parsed);
6132
+ const checks = [];
6133
+ for (const item of rollup) {
6134
+ const parsed_item = parse_rollup_item(item);
6135
+ if (parsed_item !== void 0) checks.push(parsed_item);
6136
+ }
6137
+ return checks;
6138
+ }
6139
+ function parse_pr_state_snapshot(raw_json) {
6140
+ const parsed = json_value.parse_or_undefined(raw_json);
6141
+ const result = pr_raw_schema.safeParse(parsed);
6142
+ const data = result.success ? result.data : void 0;
6143
+ return {
6144
+ rollup: parse_rollup_checks(raw_json),
6145
+ merge_state_status: read_string(data?.mergeStateStatus),
6146
+ review_decision: read_string(data?.reviewDecision)
6147
+ };
6148
+ }
6149
+
6150
+ // scripts/gh/git-pr-checks-eval.ts
6151
+ var DEFAULT_REQUIRED_CHECKS = ["SonarQube"];
6152
+ var CODERABBIT_CHECK_NAME = "CodeRabbit";
6153
+ var REQUIRED_CHECKS_ENV_VAR = "JOSH_REQUIRED_CHECKS";
6154
+ function parse_required_checks() {
6155
+ const raw = process.env[REQUIRED_CHECKS_ENV_VAR];
6156
+ if (!raw) return DEFAULT_REQUIRED_CHECKS;
6157
+ return raw.split(",").map((check) => check.trim()).filter((check) => check.length > 0);
6158
+ }
6159
+ var REQUIRED_CHECKS = parse_required_checks();
6160
+ var MERGE_STATE_CLEAN = "CLEAN";
6161
+ var MERGE_STATE_UNSTABLE = "UNSTABLE";
6162
+ var MERGE_STATE_DIRTY = "DIRTY";
6163
+ var REVIEW_CHANGES_REQUESTED2 = "CHANGES_REQUESTED";
6164
+ var CHECK_SUITE_SEPARATOR = " / ";
6165
+ var FAILURE_MESSAGE_PREFIX = "PR checks failed";
6166
+ var FAILURE_REASON_REVIEW = "review requested changes";
6167
+ var FAILURE_REASON_CHECKS = "failed checks";
6168
+ var FAILURE_REASON_CONFLICT = "merge conflict";
6169
+ function is_required_match(check_name, required_name) {
6170
+ if (check_name === required_name) return true;
6171
+ return check_name.startsWith(`${required_name}${CHECK_SUITE_SEPARATOR}`);
6172
+ }
6173
+ function find_required_check(checks, required_name) {
6174
+ return checks.find((check) => is_required_match(check.name, required_name));
6175
+ }
6176
+ function read_required_statuses(checks) {
6177
+ return REQUIRED_CHECKS.map(
6178
+ (name) => find_required_check(checks, name)?.status ?? CHECK_STATUS_MISSING
6179
+ );
6180
+ }
6181
+ function is_review_blocked(review_decision) {
6182
+ return review_decision === REVIEW_CHANGES_REQUESTED2;
6183
+ }
6184
+ function is_merge_conflict(merge_state_status) {
6185
+ return merge_state_status === MERGE_STATE_DIRTY;
6186
+ }
6187
+ function evaluate_failure_state(input) {
6188
+ if (is_merge_conflict(input.merge_state_status) || is_review_blocked(input.review_decision) || input.failed_checks.length > 0) {
6189
+ return "failure";
6190
+ }
6191
+ return void 0;
6192
+ }
6193
+ function is_merge_state_clean(merge_state_status) {
6194
+ return merge_state_status === MERGE_STATE_CLEAN;
6195
+ }
6196
+ function is_every_required_passing(statuses) {
6197
+ return statuses.every((status2) => status2 === CHECK_STATUS_PASS);
6198
+ }
6199
+ function is_coderabbit_check(check_name) {
6200
+ return is_required_match(check_name, CODERABBIT_CHECK_NAME);
6201
+ }
6202
+ function is_unstable_only_from_coderabbit(snapshot) {
6203
+ if (snapshot.merge_state_status !== MERGE_STATE_UNSTABLE) return false;
6204
+ const non_passing = snapshot.rollup.filter((check) => check.status !== CHECK_STATUS_PASS);
6205
+ if (non_passing.length === 0) return false;
6206
+ return non_passing.every((check) => is_coderabbit_check(check.name));
6207
+ }
6208
+ function is_required_check(check_name) {
6209
+ return REQUIRED_CHECKS.some((required) => is_required_match(check_name, required));
6210
+ }
6211
+ function is_blocking_failure(check) {
6212
+ if (check.status !== CHECK_STATUS_FAIL) return false;
6213
+ return !is_coderabbit_check(check.name) || is_required_check(check.name);
6214
+ }
6215
+ function collect_blocking_failures(snapshot) {
6216
+ return snapshot.rollup.filter((check) => is_blocking_failure(check)).map((check) => check.name);
6217
+ }
6218
+ function is_mergeable_state(snapshot) {
6219
+ if (is_merge_state_clean(snapshot.merge_state_status)) return true;
6220
+ return is_unstable_only_from_coderabbit(snapshot);
6221
+ }
6222
+ function collect_failure_reasons(snapshot) {
6223
+ const reasons = [];
6224
+ if (is_merge_conflict(snapshot.merge_state_status)) reasons.push(FAILURE_REASON_CONFLICT);
6225
+ if (is_review_blocked(snapshot.review_decision)) reasons.push(FAILURE_REASON_REVIEW);
6226
+ const failed = collect_blocking_failures(snapshot);
6227
+ if (failed.length > 0) reasons.push(`${FAILURE_REASON_CHECKS}: ${failed.join(", ")}`);
6228
+ return reasons;
6229
+ }
6230
+ function describe_pr_failure(snapshot) {
6231
+ const reasons = collect_failure_reasons(snapshot);
6232
+ if (reasons.length === 0) return `${FAILURE_MESSAGE_PREFIX}.`;
6233
+ return `${FAILURE_MESSAGE_PREFIX} (${reasons.join("; ")}).`;
6234
+ }
6235
+ function evaluate_pr_state(snapshot) {
6236
+ const statuses = read_required_statuses(snapshot.rollup);
6237
+ const failure = evaluate_failure_state({
6238
+ review_decision: snapshot.review_decision,
6239
+ failed_checks: collect_blocking_failures(snapshot),
6240
+ merge_state_status: snapshot.merge_state_status
6241
+ });
6242
+ if (failure !== void 0) return failure;
6243
+ if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) {
6244
+ return "success";
6245
+ }
6246
+ return "pending";
6247
+ }
6248
+ function is_review_decision_decisive(snapshot) {
6249
+ return evaluate_pr_state(snapshot) !== "pending";
6250
+ }
6251
+
6252
+ // scripts/gh/git-pr-checks.ts
6253
+ var SECONDS_TO_MS = 1e3;
6254
+ var CHECK_WAIT_INTERVAL_MS = 1e4;
6255
+ var DEFAULT_TIMEOUT_SECONDS = 1920;
6256
+ function compute_max_attempts(timeout_seconds, interval_ms) {
6257
+ return Math.ceil(timeout_seconds / (interval_ms / SECONDS_TO_MS)) + 1;
6258
+ }
6259
+ var DEFAULT_MAX_ATTEMPTS = compute_max_attempts(DEFAULT_TIMEOUT_SECONDS, CHECK_WAIT_INTERVAL_MS);
6260
+ function get_configured_max_attempts() {
6261
+ const environment_seconds = Number(process.env["JOSH_CI_TIMEOUT_SECONDS"]);
6262
+ return Number.isFinite(environment_seconds) && environment_seconds > 0 ? compute_max_attempts(environment_seconds, CHECK_WAIT_INTERVAL_MS) : DEFAULT_MAX_ATTEMPTS;
6263
+ }
6264
+ var CHECK_MAX_ATTEMPTS = get_configured_max_attempts();
6265
+ var DEFAULT_STABLE_READS = 2;
6266
+ var MAX_CONSECUTIVE_READ_FAILURES = 3;
6267
+ var PR_CHECKS_TIMEOUT_MESSAGE = "Timed out while waiting for PR checks to complete.";
6268
+ function is_pr_checks_timeout(error) {
6269
+ return error instanceof Error && error.message === PR_CHECKS_TIMEOUT_MESSAGE;
6270
+ }
6271
+ var SHOULD_ALWAYS_READ_REVIEW_DECISION = () => true;
6272
+ var MERGE_GATE_EVALUATOR = {
6273
+ evaluate: evaluate_pr_state,
6274
+ describe: describe_pr_failure,
6275
+ should_read_review_decision: is_review_decision_decisive
6276
+ };
6277
+ function advance_stable_count(previous, state) {
6278
+ return state === "success" ? previous + 1 : 0;
6279
+ }
6280
+ function classify_poll_result(input) {
6281
+ const state = input.evaluator.evaluate(input.snapshot);
6282
+ if (state === "failure") throw new Error(input.evaluator.describe(input.snapshot));
6283
+ const next_stable_count = advance_stable_count(input.stable_count, state);
6284
+ return {
6285
+ is_done: next_stable_count >= input.required_stable_reads,
6286
+ next_stable_count
6287
+ };
6288
+ }
6289
+ async function read_pr_state(input, evaluator) {
6290
+ try {
6291
+ const snapshot = await input.options.fetcher(
6292
+ input.options.branch_name,
6293
+ evaluator.should_read_review_decision ?? SHOULD_ALWAYS_READ_REVIEW_DECISION
6294
+ );
6295
+ return { kind: "read", snapshot };
6296
+ } catch (error) {
6297
+ return { kind: "unreadable", cause: error };
6298
+ }
6299
+ }
6300
+ async function sleep_before_next_attempt(input) {
6301
+ if (input.attempt < input.options.max_attempts - 1) {
6302
+ await poll.sleep(input.options.interval_ms);
6303
+ }
6304
+ }
6305
+ async function retry_after_unreadable(input, cause) {
6306
+ const next_failure_count = input.failure_count + 1;
6307
+ if (next_failure_count >= MAX_CONSECUTIVE_READ_FAILURES) throw cause;
6308
+ console.warn(
6309
+ `Could not read the PR state (${String(next_failure_count)}/${String(MAX_CONSECUTIVE_READ_FAILURES)}); asking again on the next poll: ${String(cause)}`
6310
+ );
6311
+ await sleep_before_next_attempt(input);
6312
+ return { next_stable_count: 0, next_failure_count, read_failure: cause };
6313
+ }
6314
+ function to_exhaustion_error(last_read_failure) {
6315
+ return last_read_failure ?? new Error(PR_CHECKS_TIMEOUT_MESSAGE);
6316
+ }
6317
+ async function attempt_pr_success_poll(input) {
6318
+ const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
6319
+ const read3 = await read_pr_state(input, evaluator);
6320
+ if (read3.kind === "unreadable") return await retry_after_unreadable(input, read3.cause);
6321
+ const classification = classify_poll_result({
6322
+ snapshot: read3.snapshot,
6323
+ stable_count: input.stable_count,
6324
+ required_stable_reads: input.options.required_stable_reads,
6325
+ evaluator
6326
+ });
6327
+ if (classification.is_done) {
6328
+ return { snapshot: read3.snapshot, next_stable_count: 0, next_failure_count: 0 };
6329
+ }
6330
+ await sleep_before_next_attempt(input);
6331
+ return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
6332
+ }
6333
+ async function wait_for_pr_success(options) {
6334
+ let carried = { next_stable_count: 0, next_failure_count: 0 };
6335
+ for (let attempt = 0; attempt < options.max_attempts; attempt += 1) {
6336
+ console.info(`Checking PR status\u2026 (${String(attempt + 1)}/${String(options.max_attempts)})`);
6337
+ carried = await attempt_pr_success_poll({
6338
+ options,
6339
+ stable_count: carried.next_stable_count,
6340
+ failure_count: carried.next_failure_count,
6341
+ attempt
6342
+ });
6343
+ if (carried.snapshot !== void 0) return carried.snapshot;
6344
+ }
6345
+ throw to_exhaustion_error(carried.read_failure);
6346
+ }
6347
+ async function default_fetch_pr_state(branch_name, should_read_review_decision = SHOULD_ALWAYS_READ_REVIEW_DECISION) {
6348
+ const checks = await git_gh_pr_snapshot.pr_get_checks_snapshot(branch_name);
6349
+ const snapshot = parse_pr_state_snapshot(checks.snapshot_json);
6350
+ if (!should_read_review_decision(snapshot)) return snapshot;
6351
+ const decision = await git_gh_pr_snapshot.pr_get_review_decision(checks.pr_number);
6352
+ return { ...snapshot, review_decision: read_string(decision) };
6353
+ }
6354
+
6355
+ // scripts/gh/git-pr-checks-watch.ts
6356
+ var PR_CHECKS_WATCH_TIMEOUT_MS = 12e4;
6357
+ var WATCH_TIMED_OUT_NOTE = "\u23F1\uFE0F pr checks --watch timed out.";
6358
+ var NO_CHECKS_MESSAGE = "gh api reported no checks on the branch";
6359
+ var CHECKS_FAILED_MESSAGE = "gh api reported a failing check on the branch";
6360
+ function is_awaited_check(check) {
6361
+ return !is_coderabbit_check(check.name) || is_required_check(check.name);
6362
+ }
6363
+ function evaluate_checks_settled(snapshot) {
6364
+ const awaited = snapshot.rollup.filter((check) => is_awaited_check(check));
6365
+ const considered = awaited.length === 0 ? snapshot.rollup : awaited;
6366
+ if (considered.length === 0 || considered.some((check) => check.status === CHECK_STATUS_PENDING)) {
6367
+ return "pending";
6368
+ }
6369
+ return snapshot.rollup.some((check) => check.status === CHECK_STATUS_FAIL) ? "failure" : "success";
6370
+ }
6371
+ function describe_checks_failure(snapshot) {
6372
+ const failed = snapshot.rollup.filter((check) => check.status === CHECK_STATUS_FAIL);
6373
+ return `${CHECKS_FAILED_MESSAGE}: ${failed.map((check) => check.name).join(", ")}`;
6374
+ }
6375
+ var SHOULD_NEVER_READ_REVIEW_DECISION = () => false;
6376
+ var CHECKS_SETTLED_EVALUATOR = {
6377
+ evaluate: evaluate_checks_settled,
6378
+ describe: describe_checks_failure,
6379
+ should_read_review_decision: SHOULD_NEVER_READ_REVIEW_DECISION
6380
+ };
6381
+ async function watch_until_settled(branch_name) {
6382
+ await wait_for_pr_success({
6383
+ branch_name,
6384
+ fetcher: default_fetch_pr_state,
6385
+ interval_ms: CHECK_WAIT_INTERVAL_MS,
6386
+ max_attempts: compute_max_attempts(
6387
+ PR_CHECKS_WATCH_TIMEOUT_MS / SECONDS_TO_MS,
6388
+ CHECK_WAIT_INTERVAL_MS
6389
+ ),
6390
+ required_stable_reads: DEFAULT_STABLE_READS,
6391
+ evaluator: CHECKS_SETTLED_EVALUATOR
6392
+ });
6393
+ }
6394
+ async function fail_when_no_checks(branch_name) {
6395
+ const snapshot = await default_fetch_pr_state(branch_name, SHOULD_NEVER_READ_REVIEW_DECISION);
6396
+ if (snapshot.rollup.length === 0) throw new Error(NO_CHECKS_MESSAGE);
6397
+ }
6398
+ async function pr_checks_watch(branch_name) {
6399
+ try {
6400
+ await watch_until_settled(branch_name);
6401
+ return { timed_out: false };
6402
+ } catch (error) {
6403
+ if (!is_pr_checks_timeout(error)) throw error;
6404
+ await fail_when_no_checks(branch_name);
6405
+ console.info(WATCH_TIMED_OUT_NOTE);
6406
+ return { timed_out: true };
6407
+ }
6408
+ }
6409
+ var git_pr_checks_watch = { pr_checks_watch };
6410
+
6411
+ // scripts/gh/git-gh-command.ts
6412
+ var git_gh_command = {
6413
+ ...git_gh_pr,
6414
+ ...git_gh_pr_auto_merge,
6415
+ pr_checks_watch: git_pr_checks_watch.pr_checks_watch,
6416
+ ...git_gh_issue,
6417
+ ...git_gh_repo
6418
+ };
6419
+
4582
6420
  export {
6421
+ bounded_pool,
4583
6422
  shell_segments,
4584
6423
  bash_triggers,
4585
6424
  issue_cite,
6425
+ PORCELAIN_FLAG,
6426
+ git_spawn_sync,
4586
6427
  git_command,
4587
6428
  git_common_directory,
6429
+ error_text,
4588
6430
  tail_commands,
4589
6431
  filing_cap,
4590
6432
  CONTEXT_CUT_THRESHOLD,
4591
6433
  RECENT_REQUEST_WINDOW,
4592
- agent_session_environment,
4593
6434
  agent_role_profile,
6435
+ cli_flags,
6436
+ own_session,
4594
6437
  backlog_budget,
4595
6438
  run_hold,
4596
6439
  run_cut,
4597
6440
  run_tail,
4598
6441
  lane_background,
4599
- ENHANCEMENT_LABEL,
4600
- BREAKING_CHANGE_LABEL,
4601
6442
  IN_PROGRESS_LABEL,
4602
6443
  NEEDS_DECISION_LABEL,
4603
6444
  RUN_SOLO_LABEL,
4604
6445
  RUN_LANE_LABEL,
4605
6446
  NEEDS_HUMAN_REVIEW_LABEL,
4606
6447
  ALREADY_DONE_LABEL,
4607
- BUGFIX_LABEL,
4608
- RELEASE_CLASSIFICATION_NAMES,
4609
6448
  has_any_label,
4610
6449
  has_label_name,
4611
6450
  process_identity,
@@ -4620,14 +6459,13 @@ export {
4620
6459
  process_owner_schema,
4621
6460
  lane_registry,
4622
6461
  run_headless,
4623
- FULL_PAGE_QUERY,
4624
6462
  git_gh_api_path,
4625
- error_text,
4626
6463
  FETCH_TIMEOUT_MS,
4627
- parse_json_array_or_undefined,
4628
6464
  read_json_listing,
4629
6465
  parse_json_object_safe,
4630
- gh_failure,
4631
- has_stderr_field,
4632
- git_gh_exec
6466
+ git_gh_exec,
6467
+ open_issue_schema,
6468
+ git_gh_issue_read,
6469
+ git_gh_pr_read,
6470
+ git_gh_command
4633
6471
  };