@joshuafolkken/kit 1.1094.0 → 1.1128.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 (149) hide show
  1. package/.claude/skills/workflow-commands/SKILL.md +1 -1
  2. package/.claude/skills/workflow-commands/background-commands.md +36 -85
  3. package/.claude/skills/workflow-commands/backlogrun-progress.md +29 -32
  4. package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -2
  5. package/.claude/skills/workflow-commands/fullrun.md +5 -4
  6. package/.claude/skills/workflow-commands/halfrun.md +5 -4
  7. package/.claude/skills/workflow-commands/latest-gate.md +7 -15
  8. package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
  9. package/.claude/skills/workflow-commands/split-assessment.md +4 -18
  10. package/.github/actions/setup-node/action.yml +33 -0
  11. package/.github/actions/setup-pnpm/action.yml +4 -4
  12. package/.github/workflows/auto-tag.yml +2 -3
  13. package/.github/workflows/ci.yml +7 -8
  14. package/.github/workflows/dependabot-auto-merge.yml +1 -0
  15. package/.github/workflows/pr-classification.yml +1 -3
  16. package/.github/workflows/production.yml +1 -0
  17. package/.github/workflows/publish.yml +10 -9
  18. package/.github/workflows/sonar-qube.yml +17 -13
  19. package/.pnpmfile.mjs +29 -0
  20. package/AGENTS.md +4 -11
  21. package/CLAUDE.md +24 -17
  22. package/GEMINI.md +4 -13
  23. package/README.md +8 -3
  24. package/SECURITY.md +1 -0
  25. package/cspell/index.yaml +4 -0
  26. package/dist/CLAUDE.basic.md +6 -6
  27. package/dist/CLAUDE.md +24 -17
  28. package/dist/CLAUDE.static.md +6 -6
  29. package/dist/config-merge/index.js +33 -9
  30. package/dist/hooks/batch-guard.js +4 -4
  31. package/dist/hooks/{chunk-NKOG3XAV.js → chunk-4JZDP5X5.js} +2 -2
  32. package/dist/hooks/{chunk-3C4YCCEF.js → chunk-5SD2YM7Z.js} +4 -4
  33. package/dist/hooks/{chunk-OIZUHPJI.js → chunk-77J7IEQM.js} +1 -1
  34. package/dist/hooks/{chunk-KHWHKJOM.js → chunk-7HVB7SZG.js} +3 -3
  35. package/dist/hooks/{chunk-UD24EAIL.js → chunk-AIOIOMPX.js} +1875 -104
  36. package/dist/hooks/{chunk-M2XDCUKD.js → chunk-M7QKEKWO.js} +88 -1783
  37. package/dist/hooks/{chunk-E54KGFEQ.js → chunk-MKVGBQPA.js} +1 -1
  38. package/dist/hooks/{chunk-7ZPFAVR7.js → chunk-NXYMNPUQ.js} +3 -3
  39. package/dist/hooks/{chunk-O4LL4HTI.js → chunk-QVLT52OV.js} +2 -2
  40. package/dist/hooks/{chunk-IL27YTPH.js → chunk-XYUIQOW3.js} +2 -2
  41. package/dist/hooks/codex-hook-adapter.js +12 -47
  42. package/dist/hooks/duplicate-read-guard.js +5 -5
  43. package/dist/hooks/format-edited.js +2 -2
  44. package/dist/hooks/inputs.json +180 -179
  45. package/dist/hooks/investigation-guard.js +5 -5
  46. package/dist/hooks/pretool-guard.js +9 -9
  47. package/dist/hooks/stop-guard.js +17 -12
  48. package/dist/josh.js +79 -30
  49. package/dist/version/index.js +36 -20
  50. package/eslint/rules/unicorn.js +3 -2
  51. package/package.json +18 -2
  52. package/pnpm-workspace.yaml +5 -7
  53. package/prompts/collaboration-workflow/glossary.md +17 -0
  54. package/prompts/collaboration-workflow/issue-template.md +1 -1
  55. package/prompts/collaboration-workflow/operating-rules.md +1 -1
  56. package/prompts/collaboration-workflow/principles.md +9 -0
  57. package/prompts/collaboration-workflow.md +1 -0
  58. package/scripts/backlog/backlog-arrival.ts +30 -10
  59. package/scripts/backlog/backlog-budget-cli.ts +15 -16
  60. package/scripts/backlog/backlog-offer-cli.ts +6 -14
  61. package/scripts/backlog/backlog-plan.ts +29 -18
  62. package/scripts/backlog/backlog-ready.ts +20 -7
  63. package/scripts/config-merge/parse-jsonc.ts +33 -3
  64. package/scripts/config-merge/patch-json-key.ts +13 -6
  65. package/scripts/cost-runtime/cost-cli.ts +3 -7
  66. package/scripts/doctor/doctor-consumer.ts +3 -5
  67. package/scripts/doctor/doctor-io.ts +9 -13
  68. package/scripts/document/backlogrun-parent-read-set.ts +12 -1
  69. package/scripts/document/document-byte-budget.ts +1 -0
  70. package/scripts/document/entry-read-budget.ts +13 -7
  71. package/scripts/document/entry-read-set.ts +32 -8
  72. package/scripts/document/lane-child-read-set.ts +7 -0
  73. package/scripts/document/read-set-point-of-use.ts +8 -3
  74. package/scripts/document/read-set-trim.ts +3 -1
  75. package/scripts/epic/epic-audit.ts +3 -18
  76. package/scripts/gh/git-gh-issue-list.ts +12 -11
  77. package/scripts/gh/git-gh-issue-write.ts +4 -1
  78. package/scripts/gh/git-gh-pr-auto-merge.ts +4 -1
  79. package/scripts/git/git-command.ts +6 -1
  80. package/scripts/git/git-spawn-sync.ts +65 -0
  81. package/scripts/git/git-spawn.ts +5 -4
  82. package/scripts/git/stash/stash-pop-args.ts +1 -6
  83. package/scripts/init/init-file-action.ts +54 -0
  84. package/scripts/init/init-logic-migrate.ts +35 -17
  85. package/scripts/init/init-logic-secretlint.ts +1 -1
  86. package/scripts/init/init-logic.ts +6 -22
  87. package/scripts/init/init.ts +11 -68
  88. package/scripts/init/kit-written-paths.ts +2 -0
  89. package/scripts/init/project-profile.ts +3 -6
  90. package/scripts/init/start-steps.ts +2 -1
  91. package/scripts/issue/defect-rate-cli.ts +61 -20
  92. package/scripts/issue/issue-backlinks.ts +7 -14
  93. package/scripts/issue/issue-cite.ts +12 -6
  94. package/scripts/issue/issue-file.ts +5 -12
  95. package/scripts/issue/markdown-section.ts +21 -6
  96. package/scripts/issue/measure-rerun-cli.ts +73 -14
  97. package/scripts/josh/josh-commands-project.ts +1 -1
  98. package/scripts/josh/josh-commands-workflow.ts +1 -1
  99. package/scripts/josh/run-github-prerequisite.ts +3 -6
  100. package/scripts/lane/lane-await.ts +43 -13
  101. package/scripts/lane/lane-close.ts +4 -1
  102. package/scripts/lane/lane-launch-cli.ts +10 -16
  103. package/scripts/lane/openai-review-broker.ts +4 -1
  104. package/scripts/lib/cli-flags.ts +31 -2
  105. package/scripts/lib/error-message.ts +12 -1
  106. package/scripts/lines/line-targets.ts +0 -11
  107. package/scripts/lines/lines-command.ts +5 -3
  108. package/scripts/propagate/propagate-git.ts +3 -8
  109. package/scripts/refactor/refactor-scan.ts +2 -1
  110. package/scripts/registry-migration/migrate-logic.ts +28 -19
  111. package/scripts/registry-migration/migrate.ts +6 -5
  112. package/scripts/release/github-release-cli.ts +14 -3
  113. package/scripts/release/release-publish.ts +36 -2
  114. package/scripts/release/release-worktree.ts +97 -2
  115. package/scripts/review/review-checkout.ts +3 -2
  116. package/scripts/review/review-record-cli.ts +9 -16
  117. package/scripts/rules/early-heartbeat.ts +4 -3
  118. package/scripts/rules/lane-park.ts +9 -0
  119. package/scripts/rules/stop-rules.ts +7 -4
  120. package/scripts/run/run-carry-args.ts +8 -12
  121. package/scripts/run/run-cut-args.ts +1 -6
  122. package/scripts/run/run-ending-cli.ts +1 -5
  123. package/scripts/run/run-entry-cli.ts +40 -9
  124. package/scripts/run/run-entry-stop.ts +104 -0
  125. package/scripts/run/run-event-stream-emit.ts +7 -2
  126. package/scripts/run/run-hold.ts +4 -1
  127. package/scripts/run/run-liveness-cli.ts +1 -5
  128. package/scripts/run/run-merge-cli.ts +2 -10
  129. package/scripts/run/run-parent-cut-hook.ts +4 -1
  130. package/scripts/run/run-prep-cli.ts +4 -1
  131. package/scripts/run/run-progress-args.ts +20 -4
  132. package/scripts/run/run-progress-cli.ts +31 -29
  133. package/scripts/run/run-progress-clock.ts +4 -1
  134. package/scripts/run/run-ship-cli.ts +8 -16
  135. package/scripts/run/run-ship-probe.ts +4 -1
  136. package/scripts/run/run-status-cli.ts +2 -15
  137. package/scripts/run/run-tidy-lanes.ts +4 -1
  138. package/scripts/run/run-wake-cli.ts +2 -10
  139. package/scripts/run/run-watcher-guard.ts +9 -5
  140. package/scripts/safe-chain/pack-hook.ts +92 -0
  141. package/scripts/sync/sync-ai-files.ts +4 -0
  142. package/scripts/test/test-declared-changed.ts +13 -16
  143. package/scripts/test/test-declared.ts +3 -7
  144. package/scripts/version/version-commands.ts +11 -1
  145. package/templates/CLAUDE.basic.md +6 -6
  146. package/templates/workflows/ci.yml +16 -82
  147. package/templates/workflows/dependabot-auto-merge.yml +1 -0
  148. package/templates/workflows/github-release.yml +1 -3
  149. package/tsconfig.sonar.json +4 -22
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  lane_child_marker,
3
3
  run_issue_number
4
- } from "./chunk-E54KGFEQ.js";
4
+ } from "./chunk-MKVGBQPA.js";
5
5
  import {
6
6
  PLATFORM_TEMP_ROOT,
7
7
  cost_blocks,
@@ -13,7 +13,7 @@ import {
13
13
  time_density_hook,
14
14
  time_shell,
15
15
  time_transcript_line
16
- } from "./chunk-NKOG3XAV.js";
16
+ } from "./chunk-4JZDP5X5.js";
17
17
  import {
18
18
  ENV_FILE_NAME,
19
19
  LANE_SEAT_KEY,
@@ -21,6 +21,41 @@ import {
21
21
  ports
22
22
  } from "./chunk-RKUAD3I6.js";
23
23
 
24
+ // scripts/lib/bounded-pool.ts
25
+ function pool_width(limit, total) {
26
+ if (!Number.isFinite(limit)) return 1;
27
+ return Math.max(1, Math.min(Math.floor(limit), total));
28
+ }
29
+ function throw_first_failure(failures) {
30
+ if (failures.length === 0) return;
31
+ const [first] = failures;
32
+ throw first;
33
+ }
34
+ async function drain(pool, worker) {
35
+ for (let entry = pool.queue.shift(); entry !== void 0 && pool.failures.length === 0; entry = pool.queue.shift()) {
36
+ const [index, item] = entry;
37
+ try {
38
+ pool.results.push({ index, value: await worker(item, index) });
39
+ } catch (error) {
40
+ pool.failures.push(error);
41
+ }
42
+ }
43
+ }
44
+ async function bounded_map(items, limit, worker) {
45
+ const pool = {
46
+ failures: [],
47
+ queue: items.map((item, index) => [index, item]),
48
+ results: []
49
+ };
50
+ const consumers = Array.from({ length: pool_width(limit, items.length) }, async () => {
51
+ await drain(pool, worker);
52
+ });
53
+ await Promise.all(consumers);
54
+ throw_first_failure(pool.failures);
55
+ return pool.results.toSorted((left, right) => left.index - right.index).map((result) => result.value);
56
+ }
57
+ var bounded_pool = { bounded_map, pool_width };
58
+
24
59
  // scripts/rules/shell-segments.ts
25
60
  var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
26
61
  function segments_of(command) {
@@ -108,8 +143,8 @@ function current_turn(tail) {
108
143
  function turn_filing_count(tail) {
109
144
  return prior_filing_count(current_turn(tail));
110
145
  }
111
- function decide(_call, run2) {
112
- return prior_filing_count(time_density_hook.read_tail(run2.transcript)) >= CAP_REACHED;
146
+ function decide(_call, run3) {
147
+ return prior_filing_count(time_density_hook.read_tail(run3.transcript)) >= CAP_REACHED;
113
148
  }
114
149
  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
150
  var ROW = {
@@ -149,9 +184,6 @@ var git_utilities = {
149
184
  get_git_command_for_spawn
150
185
  };
151
186
 
152
- // scripts/git/git-push-transport.ts
153
- import { execa as execa2 } from "execa";
154
-
155
187
  // scripts/git/git-execa-error.ts
156
188
  function get_exit_code(error) {
157
189
  if (typeof error !== "object" || error === null) return void 0;
@@ -170,6 +202,9 @@ function create_spawn_error(command, exit_code) {
170
202
  return new Error(error_message, { cause: { exit_code: exit_code_string } });
171
203
  }
172
204
 
205
+ // scripts/git/git-push-transport.ts
206
+ import { execa as execa2 } from "execa";
207
+
173
208
  // scripts/git/git-ssh-keepalive.ts
174
209
  import { execa } from "execa";
175
210
  var SSH_KEEPALIVE_OPTIONS = "-o ServerAliveInterval=15 -o ServerAliveCountMax=3";
@@ -300,6 +335,44 @@ var git_spawn = {
300
335
  with_output_remote
301
336
  };
302
337
 
338
+ // scripts/lib/spawn-exit.ts
339
+ function read_spawn_stdout(result) {
340
+ return result.stdout ?? "";
341
+ }
342
+ function read_spawn_stderr(result) {
343
+ return result.stderr ?? "";
344
+ }
345
+
346
+ // scripts/git/git-spawn-sync.ts
347
+ import { execaSync } from "execa";
348
+ var GIT_TIMEOUT_MS = 1e4;
349
+ var SUCCESS_EXIT_CODE = 0;
350
+ var C_LOCALE = { LC_ALL: "C", LANGUAGE: "C" };
351
+ function run(args, options = {}) {
352
+ const result = execaSync(git_utilities.get_git_command_for_spawn(), args, {
353
+ ...options,
354
+ extendEnv: true,
355
+ reject: false,
356
+ timeout: options.timeout ?? GIT_TIMEOUT_MS
357
+ });
358
+ return {
359
+ exit_code: result.exitCode,
360
+ stdout: read_spawn_stdout(result),
361
+ stderr: read_spawn_stderr(result)
362
+ };
363
+ }
364
+ function read2(args, options = {}) {
365
+ const result = run(args, options);
366
+ return result.exit_code === SUCCESS_EXIT_CODE ? result.stdout.trim() : void 0;
367
+ }
368
+ function origin_url(cwd) {
369
+ return read2(["config", "--get", "remote.origin.url"], { cwd });
370
+ }
371
+ function toplevel(timeout = GIT_TIMEOUT_MS) {
372
+ return run(["rev-parse", "--show-toplevel"], { env: C_LOCALE, timeout });
373
+ }
374
+ var git_spawn_sync = { GIT_TIMEOUT_MS, C_LOCALE, run, read: read2, origin_url, toplevel };
375
+
303
376
  // scripts/git/git-command.ts
304
377
  async function branch() {
305
378
  return await git_spawn.read(["rev-parse", "--abbrev-ref", "HEAD"]);
@@ -309,7 +382,9 @@ async function status(directory) {
309
382
  return await git_spawn.read([...location, "status", PORCELAIN_FLAG, UNTRACKED_FILES_FLAG]);
310
383
  }
311
384
  async function repository_root() {
312
- return await git_spawn.read(["rev-parse", "--show-toplevel"]);
385
+ const result = git_spawn_sync.toplevel();
386
+ if (result.exit_code !== 0) throw create_spawn_error("rev-parse", result.exit_code);
387
+ return result.stdout.trim();
313
388
  }
314
389
  async function head_commit() {
315
390
  return await git_spawn.read(["rev-parse", "HEAD"]);
@@ -342,9 +417,9 @@ async function get_default_branch() {
342
417
  return DEFAULT_BRANCH_FALLBACK;
343
418
  }
344
419
  var MERGE_BASE_COMMAND = "merge-base";
345
- async function reference_exists(reference) {
420
+ async function reference_exists(reference2) {
346
421
  try {
347
- await git_spawn.read(["rev-parse", "--verify", "--quiet", reference]);
422
+ await git_spawn.read(["rev-parse", "--verify", "--quiet", reference2]);
348
423
  return true;
349
424
  } catch {
350
425
  return false;
@@ -582,6 +657,18 @@ var git_command = {
582
657
  is_upstream_not_set_error
583
658
  };
584
659
 
660
+ // scripts/lib/error-message.ts
661
+ function message_of(error) {
662
+ return error instanceof Error ? error.message : String(error);
663
+ }
664
+ var DEBUG_ENV_KEY = "JOSH_DEBUG";
665
+ function trace_swallowed(where, error) {
666
+ if ((process.env[DEBUG_ENV_KEY] ?? "").trim() === "") return;
667
+ process.stderr.write(`josh debug: ${where}: ${message_of(error)}
668
+ `);
669
+ }
670
+ var error_text = { message_of, trace_swallowed };
671
+
585
672
  // scripts/run/run-hold.ts
586
673
  import { z } from "zod";
587
674
  var HOLD_PREFIX = "josh-run-hold-";
@@ -657,7 +744,8 @@ async function is_tree_dirty(directory) {
657
744
  try {
658
745
  const status2 = await git_command.status(directory);
659
746
  return status2.trim() !== "";
660
- } catch {
747
+ } catch (error) {
748
+ error_text.trace_swallowed("run_hold.is_tree_dirty", error);
661
749
  return true;
662
750
  }
663
751
  }
@@ -692,8 +780,8 @@ function stale_message(hold) {
692
780
  function uncommitted_message(hold) {
693
781
  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
782
  }
695
- function race_message(read2, target) {
696
- if (read2.kind === "held") return held_message(read2.hold);
783
+ function race_message(read3, target) {
784
+ if (read3.kind === "held") return held_message(read3.hold);
697
785
  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
786
  }
699
787
  function unknown_message() {
@@ -1300,9 +1388,9 @@ function directories(cwd) {
1300
1388
  const key2 = cache_key(cwd);
1301
1389
  const known = answers.get(key2);
1302
1390
  if (known !== void 0) return known;
1303
- const read2 = read_directories(cwd);
1304
- if (read2.length > 0) answers.set(key2, read2);
1305
- return read2;
1391
+ const read3 = read_directories(cwd);
1392
+ if (read3.length > 0) answers.set(key2, read3);
1393
+ return read3;
1306
1394
  }
1307
1395
  function own(cwd) {
1308
1396
  return directories(cwd)[OWN_GIT_DIRECTORY];
@@ -1616,8 +1704,8 @@ function worktree_git_directory_sync() {
1616
1704
  }
1617
1705
  function carried_cut_sync(now = /* @__PURE__ */ new Date(), directory = worktree_git_directory_sync()) {
1618
1706
  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;
1707
+ const read3 = read_cut(cut_path(directory), now);
1708
+ return read3.kind === "carried" ? read3.cut : void 0;
1621
1709
  }
1622
1710
  function lane_cut_sync(lane_directory) {
1623
1711
  const directory = git_common_directory.own(lane_directory);
@@ -1684,8 +1772,8 @@ function end_cut(target) {
1684
1772
  }
1685
1773
  function hold_of(directory) {
1686
1774
  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;
1775
+ const read3 = run_hold.read_hold(run_hold.hold_path(directory));
1776
+ return read3.kind === "held" || read3.kind === "stale" ? read3.hold : void 0;
1689
1777
  }
1690
1778
  function fullrun_issue(hold) {
1691
1779
  return hold?.is_fullrun === true ? hold.issue : void 0;
@@ -1989,13 +2077,13 @@ function is_live_beacon(target) {
1989
2077
  timeout: PROBE_TIMEOUT_MS2
1990
2078
  }).status === 0;
1991
2079
  }
1992
- function has_matching_start(pid, recorded_start, read2) {
1993
- const observed = read2(pid);
2080
+ function has_matching_start(pid, recorded_start, read3) {
2081
+ const observed = read3(pid);
1994
2082
  return observed === void 0 ? void 0 : observed === recorded_start;
1995
2083
  }
1996
- function matches_recorded_start(pid, recorded_start, read2) {
2084
+ function matches_recorded_start(pid, recorded_start, read3) {
1997
2085
  const target = beacon_target(recorded_start);
1998
- return target === void 0 ? has_matching_start(pid, recorded_start, read2) : is_live_beacon(target);
2086
+ return target === void 0 ? has_matching_start(pid, recorded_start, read3) : is_live_beacon(target);
1999
2087
  }
2000
2088
  var own_probe = /* @__PURE__ */ new Map();
2001
2089
  function own_start() {
@@ -2006,10 +2094,10 @@ function own_fields() {
2006
2094
  const start = own_start();
2007
2095
  return { pid: process.pid, ...start !== void 0 && { process_start: start } };
2008
2096
  }
2009
- function is_same_process(pid, recorded_start, read2 = read_start) {
2097
+ function is_same_process(pid, recorded_start, read3 = read_start) {
2010
2098
  if (pid === void 0 || !is_live_pid(pid)) return false;
2011
2099
  if (recorded_start === void 0) return void 0;
2012
- return matches_recorded_start(pid, recorded_start, read2);
2100
+ return matches_recorded_start(pid, recorded_start, read3);
2013
2101
  }
2014
2102
  function is_own_process(pid, recorded_start) {
2015
2103
  return pid === process.pid && recorded_start === own_start();
@@ -2188,11 +2276,20 @@ function parse_or_undefined(config) {
2188
2276
  return void 0;
2189
2277
  }
2190
2278
  }
2191
- var cli_flags = { refuse_unknown_flags, parse_or_undefined };
2279
+ function values_of(argv, options) {
2280
+ return parse_or_undefined({ args: [...argv], options })?.values;
2281
+ }
2282
+ function arguments_of(argv, options) {
2283
+ return parse_or_undefined({ args: [...argv], options, allowPositionals: true });
2284
+ }
2285
+ function string_of(value) {
2286
+ return typeof value === "string" ? value : void 0;
2287
+ }
2288
+ var cli_flags = { refuse_unknown_flags, parse_or_undefined, values_of, arguments_of, string_of };
2192
2289
 
2193
2290
  // scripts/backlog/backlog-budget-cli.ts
2194
2291
  var ARGV_OFFSET = 2;
2195
- var SUCCESS_EXIT_CODE = 0;
2292
+ var SUCCESS_EXIT_CODE2 = 0;
2196
2293
  var FAILURE_EXIT_CODE2 = 1;
2197
2294
  var JSON_KEY = "budget";
2198
2295
  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 +2304,7 @@ var OPTIONS = {
2207
2304
  started: { type: "string" }
2208
2305
  };
2209
2306
  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;
2307
+ return cli_flags.values_of(argv, OPTIONS);
2214
2308
  }
2215
2309
  function to_answer(raw) {
2216
2310
  return backlog_budget.ANSWERS.find((answer) => answer === raw);
@@ -2232,14 +2326,14 @@ function is_readable(raw, parsed) {
2232
2326
  var COUNT_NAMES = ["idle", "max", "merged", "running"];
2233
2327
  function counts_of(values) {
2234
2328
  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))
2329
+ idle: to_count(cli_flags.string_of(values.idle)),
2330
+ max: to_count(cli_flags.string_of(values.max)),
2331
+ merged: to_count(cli_flags.string_of(values.merged)),
2332
+ running: to_count(cli_flags.string_of(values.running))
2239
2333
  };
2240
2334
  }
2241
2335
  function are_counts_readable(values, counts) {
2242
- return COUNT_NAMES.every((name) => is_readable(text_of(values[name]), counts[name]));
2336
+ return COUNT_NAMES.every((name) => is_readable(cli_flags.string_of(values[name]), counts[name]));
2243
2337
  }
2244
2338
  var NO_IDLE_MINUTES = 0;
2245
2339
  function to_idle_ms(idle_minutes) {
@@ -2248,17 +2342,20 @@ function to_idle_ms(idle_minutes) {
2248
2342
  return idle_minutes * backlog_budget.MS_PER_MINUTE;
2249
2343
  }
2250
2344
  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;
2345
+ if (to_idle_ms(to_count(cli_flags.string_of(values.idle))) === void 0) return true;
2346
+ return cli_flags.string_of(values.active) !== void 0;
2253
2347
  }
2254
2348
  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));
2349
+ return is_readable(
2350
+ cli_flags.string_of(values.active),
2351
+ to_time_ms(cli_flags.string_of(values.active))
2352
+ ) && is_watch_readable(values) && are_counts_readable(values, counts_of(values));
2256
2353
  }
2257
2354
  function input_of(values, base) {
2258
2355
  const counts = counts_of(values);
2259
2356
  return {
2260
2357
  ...base,
2261
- active_at_ms: to_time_ms(text_of(values.active)) ?? base.started_at_ms,
2358
+ active_at_ms: to_time_ms(cli_flags.string_of(values.active)) ?? base.started_at_ms,
2262
2359
  merged: counts.merged ?? 0,
2263
2360
  running: counts.running ?? 0,
2264
2361
  max_issues: counts.max,
@@ -2266,9 +2363,9 @@ function input_of(values, base) {
2266
2363
  };
2267
2364
  }
2268
2365
  function build_input(values, now_ms) {
2269
- const answer = to_answer(text_of(values.answer));
2366
+ const answer = to_answer(cli_flags.string_of(values.answer));
2270
2367
  if (answer === void 0) return void 0;
2271
- const started_at_ms = to_time_ms(text_of(values.started));
2368
+ const started_at_ms = to_time_ms(cli_flags.string_of(values.started));
2272
2369
  if (started_at_ms === void 0) return void 0;
2273
2370
  if (!are_values_readable(values)) return void 0;
2274
2371
  return input_of(values, { answer, now_ms, started_at_ms });
@@ -2277,7 +2374,7 @@ function refuse() {
2277
2374
  console.error(USAGE);
2278
2375
  return FAILURE_EXIT_CODE2;
2279
2376
  }
2280
- function run(argv, now_ms = Date.now()) {
2377
+ function run2(argv, now_ms = Date.now()) {
2281
2378
  const values = read_arguments(argv);
2282
2379
  if (values === void 0) return refuse();
2283
2380
  const input = build_input(values, now_ms);
@@ -2287,23 +2384,23 @@ function run(argv, now_ms = Date.now()) {
2287
2384
  console.info(
2288
2385
  JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: backlog_budget.is_finish(input) })
2289
2386
  );
2290
- return SUCCESS_EXIT_CODE;
2387
+ return SUCCESS_EXIT_CODE2;
2291
2388
  }
2292
2389
  path_decision.print_decision(JSON_KEY, verdict, reason, false);
2293
- return SUCCESS_EXIT_CODE;
2390
+ return SUCCESS_EXIT_CODE2;
2294
2391
  }
2295
2392
  function main(argv) {
2296
- process.exitCode = run(argv);
2393
+ process.exitCode = run2(argv);
2297
2394
  }
2298
2395
  var backlog_budget_cli = {
2299
2396
  JSON_KEY,
2300
2397
  OPTIONS,
2301
- SUCCESS_EXIT_CODE,
2398
+ SUCCESS_EXIT_CODE: SUCCESS_EXIT_CODE2,
2302
2399
  USAGE,
2303
2400
  build_input,
2304
2401
  main,
2305
2402
  read_arguments,
2306
- run,
2403
+ run: run2,
2307
2404
  to_answer,
2308
2405
  to_count,
2309
2406
  to_time_ms
@@ -2342,10 +2439,10 @@ function flag_tokens(tokens, start) {
2342
2439
  const rendered = [];
2343
2440
  let index = start;
2344
2441
  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;
2442
+ const read3 = read_flag(tokens, index);
2443
+ if (read3 === void 0) return void 0;
2444
+ rendered.push(read3.part);
2445
+ index += read3.advance;
2349
2446
  }
2350
2447
  return rendered;
2351
2448
  }
@@ -2472,8 +2569,8 @@ function classify3(raw, now) {
2472
2569
  function read_carry(target, now = /* @__PURE__ */ new Date()) {
2473
2570
  return classify3(stamp_file.read_stamp_text(target), now);
2474
2571
  }
2475
- function owner_of(pid, read2 = process_identity.read_start) {
2476
- return { pid, start: read2(pid) };
2572
+ function owner_of(pid, read3 = process_identity.read_start) {
2573
+ return { pid, start: read3(pid) };
2477
2574
  }
2478
2575
  function fresh_carry(invocation, owner, now) {
2479
2576
  return {
@@ -2533,9 +2630,9 @@ function is_at_cut_cap(carry) {
2533
2630
  function end_carry(target) {
2534
2631
  stamp_file.remove_stamp(target);
2535
2632
  }
2536
- function retrospective_done_of(read2) {
2537
- if (read2.kind !== "carried" && read2.kind !== "expired") return false;
2538
- return read2.carry.retrospective === true;
2633
+ function retrospective_done_of(read3) {
2634
+ if (read3.kind !== "carried" && read3.kind !== "expired") return false;
2635
+ return read3.carry.retrospective === true;
2539
2636
  }
2540
2637
  function remaining_of(carry) {
2541
2638
  if (carry === void 0) return void 0;
@@ -2634,11 +2731,11 @@ import { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync, unlinkSync
2634
2731
  import path3 from "node:path";
2635
2732
 
2636
2733
  // scripts/gh/gh-cli-token.ts
2637
- import { execaSync } from "execa";
2734
+ import { execaSync as execaSync2 } from "execa";
2638
2735
  var GH_CLI_TIMEOUT_MS = 5e3;
2639
2736
  function get() {
2640
2737
  try {
2641
- const { stdout } = execaSync("gh", ["auth", "token"], { timeout: GH_CLI_TIMEOUT_MS });
2738
+ const { stdout } = execaSync2("gh", ["auth", "token"], { timeout: GH_CLI_TIMEOUT_MS });
2642
2739
  const token = stdout.trim();
2643
2740
  return token.length > 0 ? token : void 0;
2644
2741
  } catch {
@@ -2979,9 +3076,9 @@ function scoped_events(events, scope) {
2979
3076
  if (floor === void 0) return void 0;
2980
3077
  return events.filter((event) => is_within(event, floor));
2981
3078
  }
2982
- function scope_of(read2) {
2983
- if (read2.kind === "carried" || read2.kind === "expired") {
2984
- return { kind: SINCE_SCOPE, started_at: read2.carry.started_at };
3079
+ function scope_of(read3) {
3080
+ if (read3.kind === "carried" || read3.kind === "expired") {
3081
+ return { kind: SINCE_SCOPE, started_at: read3.carry.started_at };
2985
3082
  }
2986
3083
  return UNKNOWN_EVENT_SCOPE;
2987
3084
  }
@@ -3060,7 +3157,8 @@ async function emit_once(kind, text) {
3060
3157
  const target = await stream_target();
3061
3158
  if (target === void 0 || run_event_stream.read_last(target)?.kind === kind) return false;
3062
3159
  return run_event_stream.append(target, kind, text, now_iso()).appended;
3063
- } catch {
3160
+ } catch (error) {
3161
+ error_text.trace_swallowed("run_event_stream_emit.emit_once", error);
3064
3162
  return false;
3065
3163
  }
3066
3164
  }
@@ -3078,7 +3176,8 @@ async function emit_once_since(kind, text, reset_kind) {
3078
3176
  return false;
3079
3177
  }
3080
3178
  return run_event_stream.append(target, kind, text, now_iso()).appended;
3081
- } catch {
3179
+ } catch (error) {
3180
+ error_text.trace_swallowed("run_event_stream_emit.emit_once_since", error);
3082
3181
  return false;
3083
3182
  }
3084
3183
  }
@@ -3089,8 +3188,8 @@ async function current_events() {
3089
3188
  try {
3090
3189
  const repository2 = await run_carry.repository_directory();
3091
3190
  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);
3191
+ const read3 = run_carry.read_carry(run_carry.carry_path(repository2));
3192
+ const scope = run_event_scope.scope_of(read3);
3094
3193
  const events = run_event_stream.read_events(run_event_stream.target_of(repository2));
3095
3194
  return run_event_scope.scoped_events(events, scope) ?? [];
3096
3195
  } catch {
@@ -3474,7 +3573,9 @@ function is_confirmation_notify(command) {
3474
3573
  );
3475
3574
  }
3476
3575
  var NOTIFY_INVOCATION = /josh\s+(?:notify|nf)\b/u;
3576
+ var COMMAND_NOTIFY_MARKER = "stop notified: a confirmation Telegram was sent by this command";
3477
3577
  function mentions_confirmation_notify(text) {
3578
+ if (text.includes(COMMAND_NOTIFY_MARKER)) return true;
3478
3579
  return NOTIFY_INVOCATION.test(text) && CONFIRMATION_TASK_TYPE.test(text);
3479
3580
  }
3480
3581
  function is_parking_child(directory = process.cwd()) {
@@ -3493,6 +3594,7 @@ function records_the_park(command) {
3493
3594
  }
3494
3595
  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
3596
  var lane_park = {
3597
+ COMMAND_NOTIFY_MARKER,
3496
3598
  LANE_PARK_REASON,
3497
3599
  is_confirmation_notify,
3498
3600
  is_parking_child,
@@ -3598,9 +3700,9 @@ var PATH_INDEX = 1;
3598
3700
  function is_same_owner(identity, current_owner2) {
3599
3701
  return identity.owner.toLowerCase() === current_owner2.toLowerCase();
3600
3702
  }
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);
3703
+ function add_entry(target, origin_url2, repository_path, current_owner2) {
3704
+ if (origin_url2 === void 0) return;
3705
+ const identity = repo_origin.parse_origin_url(origin_url2);
3604
3706
  if (identity === void 0 || !is_same_owner(identity, current_owner2)) return;
3605
3707
  target.set(repo_origin.format_identity(identity), repository_path);
3606
3708
  }
@@ -3685,9 +3787,9 @@ function scan_siblings(parent) {
3685
3787
  }));
3686
3788
  }
3687
3789
  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;
3790
+ const origin_url2 = read_origin_url(repository_path);
3791
+ if (origin_url2 === void 0) return void 0;
3792
+ return repo_origin.parse_origin_url(origin_url2)?.owner;
3691
3793
  }
3692
3794
  function main_worktree(repository_path) {
3693
3795
  const git_directory = resolve_git_directory(repository_path);
@@ -4181,8 +4283,8 @@ function environment() {
4181
4283
  function is_headless(source = process.env) {
4182
4284
  return source[HEADLESS_ENV_KEY] === HEADLESS_VALUE;
4183
4285
  }
4184
- function is_driving(read2) {
4185
- return read2?.kind === "carried" && read2.carry.is_handed_off !== true;
4286
+ function is_driving(read3) {
4287
+ return read3?.kind === "carried" && read3.carry.is_handed_off !== true;
4186
4288
  }
4187
4289
  async function read_carry_here() {
4188
4290
  const repository2 = await run_carry.repository_directory();
@@ -4194,15 +4296,15 @@ async function must_keep_waiting(source = process.env) {
4194
4296
  return is_driving(await read_carry_here());
4195
4297
  }
4196
4298
  async function current_carry() {
4197
- const read2 = await read_carry_here();
4198
- return read2?.kind === "carried" ? read2.carry : void 0;
4299
+ const read3 = await read_carry_here();
4300
+ return read3?.kind === "carried" ? read3.carry : void 0;
4199
4301
  }
4200
4302
  function is_owned_here(carry, ancestry) {
4201
4303
  return carry.owner_pid !== void 0 && ancestry().has(carry.owner_pid);
4202
4304
  }
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;
4305
+ function driven_here(read3, ancestry) {
4306
+ if (read3?.kind !== "carried" || !is_driving(read3)) return void 0;
4307
+ return is_owned_here(read3.carry, ancestry) ? read3.carry : void 0;
4206
4308
  }
4207
4309
  async function driving_carry(source = process.env, ancestry = lane_reap.own_ancestry) {
4208
4310
  if (lane_child_marker.marked_issue(source) !== void 0) return void 0;
@@ -4234,11 +4336,13 @@ function issue_url(slug, number) {
4234
4336
  function citation_line(slug, number, summary) {
4235
4337
  return `[#${number}](${issue_url(slug, number)})${SUMMARY_SEPARATOR}${summary}`;
4236
4338
  }
4339
+ function reference(slug, number, title) {
4340
+ if (slug === void 0) return `#${number}`;
4341
+ return title === void 0 ? `[#${number}](${issue_url(slug, number)})` : citation_line(slug, number, title);
4342
+ }
4237
4343
  function citer(slug, titles) {
4238
4344
  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);
4345
+ return reference(slug, number, titles.get(number));
4242
4346
  };
4243
4347
  }
4244
4348
  function qualified_target(token) {
@@ -4275,6 +4379,7 @@ var issue_cite = {
4275
4379
  missing_line,
4276
4380
  no_repo_line,
4277
4381
  parse_target,
4382
+ reference,
4278
4383
  unreadable_line
4279
4384
  };
4280
4385
 
@@ -4340,19 +4445,13 @@ var git_gh_api_path = {
4340
4445
  commit_status_api_path
4341
4446
  };
4342
4447
 
4448
+ // scripts/gh/git-gh-exec.ts
4449
+ import { execa as execa5, execaSync as execaSync3 } from "execa";
4450
+
4343
4451
  // scripts/lib/timeouts.ts
4344
4452
  var FETCH_TIMEOUT_MS = 1e4;
4345
4453
  var COMMAND_TIMEOUT_MS = 3e4;
4346
4454
 
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
4455
  // scripts/gh/git-gh-check.ts
4357
4456
  import { execa as execa4 } from "execa";
4358
4457
  var GH_NOT_INSTALLED_MSG = "gh CLI is not installed. Install it from https://cli.github.com/";
@@ -4510,7 +4609,7 @@ async function exec_gh_api_status(path6, timeout_ms) {
4510
4609
  function exec_gh_api_status_sync(path6, timeout_ms) {
4511
4610
  const options = to_spawn_options(timeout_ms);
4512
4611
  try {
4513
- const { stdout } = execaSync2("gh", ["api", "--include", "--silent", path6], options);
4612
+ const { stdout } = execaSync3("gh", ["api", "--include", "--silent", path6], options);
4514
4613
  return parse_status_line(stdout);
4515
4614
  } catch (error) {
4516
4615
  return status_of_failure(error);
@@ -4554,7 +4653,7 @@ function exec_gh_api_sync(request) {
4554
4653
  const body_option = request.body === void 0 ? {} : { input: request.body };
4555
4654
  const options = { ...body_option, ...to_spawn_options(request.timeout_ms, request.cwd) };
4556
4655
  try {
4557
- const { stdout } = execaSync2("gh", to_gh_api_args(request), options);
4656
+ const { stdout } = execaSync3("gh", to_gh_api_args(request), options);
4558
4657
  return stdout.trimEnd();
4559
4658
  } catch (error) {
4560
4659
  throw to_gh_error(error);
@@ -4579,33 +4678,1706 @@ var git_gh_exec = {
4579
4678
  parse_status_line
4580
4679
  };
4581
4680
 
4681
+ // scripts/gh/git-gh-helpers.ts
4682
+ function parse_pr_state_string(result) {
4683
+ const trimmed2 = result.trim();
4684
+ return trimmed2.length > 0 ? trimmed2 : void 0;
4685
+ }
4686
+ function is_pr_already_exists_message(error_message) {
4687
+ return error_message.toLowerCase().includes("already exists");
4688
+ }
4689
+ function get_error_message_with_stderr(error) {
4690
+ if (!(error instanceof Error)) return String(error);
4691
+ if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
4692
+ ${error.stderr}`;
4693
+ return error.message;
4694
+ }
4695
+ function handle_pr_create_error(error) {
4696
+ const error_message = get_error_message_with_stderr(error);
4697
+ if (is_pr_already_exists_message(error_message)) {
4698
+ throw new Error("PR_ALREADY_EXISTS");
4699
+ }
4700
+ throw error;
4701
+ }
4702
+ var git_gh_helpers = {
4703
+ parse_pr_state_string,
4704
+ get_error_message_with_stderr,
4705
+ handle_pr_create_error
4706
+ };
4707
+
4708
+ // scripts/git/schemas.ts
4709
+ import { z as z11 } from "zod";
4710
+ var package_name_schema = z11.object({ name: z11.string().min(1) });
4711
+ var rollup_item_schema = z11.looseObject({
4712
+ // eslint-disable-next-line @typescript-eslint/naming-convention -- GraphQL field name from GitHub API
4713
+ __typename: z11.string().optional(),
4714
+ state: z11.string().optional(),
4715
+ status: z11.string().optional(),
4716
+ conclusion: z11.string().optional(),
4717
+ name: z11.string().optional(),
4718
+ context: z11.string().optional()
4719
+ });
4720
+ var pr_raw_schema = z11.object({
4721
+ statusCheckRollup: z11.array(rollup_item_schema).optional(),
4722
+ mergeStateStatus: z11.string().optional(),
4723
+ reviewDecision: z11.string().optional()
4724
+ });
4725
+ var pull_comment_schema = z11.object({
4726
+ body: z11.string().optional(),
4727
+ html_url: z11.string().optional(),
4728
+ user: z11.object({ login: z11.string().optional() }).optional()
4729
+ });
4730
+ var ai_review_pull_comment_schema = z11.object({
4731
+ body: z11.string().optional(),
4732
+ url: z11.string().optional(),
4733
+ author: z11.object({ login: z11.string().optional() }).optional()
4734
+ });
4735
+ var pr_info_schema = z11.object({
4736
+ mergeable: z11.union([z11.boolean(), z11.string()]).nullable().optional(),
4737
+ mergeStateStatus: z11.string().nullable().optional(),
4738
+ state: z11.string().optional()
4739
+ });
4740
+ var epic_issue_schema = z11.object({
4741
+ number: z11.number(),
4742
+ body: z11.string().optional()
4743
+ });
4744
+ var blocking_issue_schema = z11.object({
4745
+ number: z11.number(),
4746
+ state: z11.string().optional(),
4747
+ repository_url: z11.string().optional()
4748
+ });
4749
+ var blocked_by_schema = z11.object({
4750
+ nodes: z11.array(blocking_issue_schema).default([]),
4751
+ totalCount: z11.number().optional()
4752
+ }).optional();
4753
+ var epic_child_schema = z11.object({
4754
+ state: z11.string().optional(),
4755
+ blockedBy: blocked_by_schema
4756
+ });
4757
+ var rest_comment_schema = z11.looseObject({
4758
+ body: z11.string().nullish(),
4759
+ html_url: z11.string().optional(),
4760
+ user: z11.looseObject({ login: z11.string().optional() }).nullish()
4761
+ });
4762
+ var issue_label_schema = z11.object({ name: z11.string() });
4763
+ var epic_subject_schema = z11.object({
4764
+ number: z11.number(),
4765
+ state: z11.string().optional(),
4766
+ labels: z11.array(issue_label_schema).optional(),
4767
+ body: z11.string().optional()
4768
+ });
4769
+ var open_issue_schema = z11.object({
4770
+ number: z11.number(),
4771
+ title: z11.string(),
4772
+ labels: z11.array(issue_label_schema).optional(),
4773
+ createdAt: z11.string(),
4774
+ blockedBy: blocked_by_schema
4775
+ });
4776
+
4777
+ // scripts/gh/git-gh-issue-rest.ts
4778
+ import { z as z12 } from "zod";
4779
+
4780
+ // scripts/gh/git-gh-rest-state.ts
4781
+ var MERGED_STATE = "MERGED";
4782
+ function to_gh_state(state) {
4783
+ return state?.toUpperCase();
4784
+ }
4785
+
4786
+ // scripts/gh/git-gh-issue-rest.ts
4787
+ var rest_issue_schema = z12.looseObject({
4788
+ number: z12.number(),
4789
+ issue_dependencies_summary: z12.looseObject({ total_blocked_by: z12.number().optional() }).nullish(),
4790
+ // Present only on a pull request, which this endpoint serves as readily as an issue.
4791
+ pull_request: z12.looseObject({ merged_at: z12.string().nullish() }).nullish()
4792
+ });
4793
+ var REPOSITORY_URL = /\/repos\/([\w.-]+\/[\w.-]+)$/u;
4794
+ var REPOSITORY_URL_GROUP = 1;
4795
+ var BLOCKED_BY_FIELD = "blockedBy";
4796
+ var BLOCKED_BY_COUNT_FIELD = "blocked_by_count";
4797
+ var STATE_FIELD = "state";
4798
+ var BODY_FIELD = "body";
4799
+ var FIELD_SEPARATOR2 = ",";
4800
+ var EMPTY_BODY = "";
4801
+ var REST_FIELD_NAMES = /* @__PURE__ */ new Map([
4802
+ ["url", "html_url"],
4803
+ ["createdAt", "created_at"]
4804
+ ]);
4805
+ var NOT_AN_ISSUE_MESSAGE = "gh api answered something other than an issue object";
4806
+ var NOT_AN_ISSUE_LISTING_MESSAGE = "gh api answered something other than an issue listing";
4807
+ var NOT_A_BLOCKER_LISTING_MESSAGE = "gh api answered something other than a blocked-by listing";
4808
+ function rest_field_name(field) {
4809
+ return REST_FIELD_NAMES.get(field) ?? field;
4810
+ }
4811
+ function split_fields(fields) {
4812
+ return fields.split(FIELD_SEPARATOR2).map((field) => field.trim()).filter((field) => field.length > 0);
4813
+ }
4814
+ function to_issue_state(rest, value) {
4815
+ if (typeof rest.pull_request?.merged_at === "string") return MERGED_STATE;
4816
+ return typeof value === "string" ? to_gh_state(value) : value;
4817
+ }
4818
+ function total_blocked_by(rest) {
4819
+ return rest.issue_dependencies_summary?.total_blocked_by;
4820
+ }
4821
+ function to_gh_field_value(field, rest) {
4822
+ if (field === BLOCKED_BY_COUNT_FIELD) return total_blocked_by(rest);
4823
+ const value = rest[rest_field_name(field)];
4824
+ if (field === STATE_FIELD) return to_issue_state(rest, value);
4825
+ return field === BODY_FIELD ? value ?? EMPTY_BODY : value;
4826
+ }
4827
+ function parse_rest_issue(rest_json) {
4828
+ const parsed = parse_json_object_safe(rest_json, rest_issue_schema);
4829
+ if (parsed === void 0) throw new Error(NOT_AN_ISSUE_MESSAGE);
4830
+ return parsed;
4831
+ }
4832
+ function is_pull_request(rest) {
4833
+ return rest.pull_request !== void 0 && rest.pull_request !== null;
4834
+ }
4835
+ function parse_rest_issues(rest_json) {
4836
+ const parsed = parse_json_array_or_undefined(rest_json, rest_issue_schema);
4837
+ if (parsed === void 0) throw new Error(NOT_AN_ISSUE_LISTING_MESSAGE);
4838
+ return parsed;
4839
+ }
4840
+ function empty_blocked_by() {
4841
+ return { nodes: [], totalCount: 0 };
4842
+ }
4843
+ function repo_of_url(repository_url) {
4844
+ const match = REPOSITORY_URL.exec(repository_url ?? "");
4845
+ return match?.[REPOSITORY_URL_GROUP];
4846
+ }
4847
+ function to_blocked_by(blockers_json, exact_total) {
4848
+ const parsed = parse_json_array_or_undefined(blockers_json, blocking_issue_schema);
4849
+ if (parsed === void 0) throw new Error(NOT_A_BLOCKER_LISTING_MESSAGE);
4850
+ const nodes = parsed.map((blocker) => ({ ...blocker, state: to_gh_state(blocker.state) }));
4851
+ return { nodes, totalCount: exact_total ?? nodes.length };
4852
+ }
4853
+ function to_gh_issue(rest, fields, blocked_by) {
4854
+ return Object.fromEntries(
4855
+ fields.map((field) => [
4856
+ field,
4857
+ field === BLOCKED_BY_FIELD ? blocked_by : to_gh_field_value(field, rest)
4858
+ ])
4859
+ );
4860
+ }
4861
+ function to_field_text(value) {
4862
+ if (typeof value === "string") return value;
4863
+ return value === void 0 || value === null ? EMPTY_BODY : JSON.stringify(value);
4864
+ }
4865
+ var CLOSED = "CLOSED";
4866
+ function reference_of_blocker(node, fallback_repo) {
4867
+ const repo = repo_of_url(node.repository_url) ?? fallback_repo;
4868
+ if (node.state === void 0) return { repo, number: node.number };
4869
+ return { repo, number: node.number, state: node.state.toUpperCase() === CLOSED ? CLOSED : "OPEN" };
4870
+ }
4871
+ var git_gh_issue_rest = {
4872
+ empty_blocked_by,
4873
+ repo_of_url,
4874
+ reference_of_blocker,
4875
+ split_fields,
4876
+ is_pull_request,
4877
+ parse_rest_issue,
4878
+ parse_rest_issues,
4879
+ total_blocked_by,
4880
+ to_blocked_by,
4881
+ to_gh_issue,
4882
+ to_field_text
4883
+ };
4884
+
4885
+ // scripts/gh/git-gh-reachability.ts
4886
+ var RATE_LIMITED_STATUS = 429;
4887
+ var SERVER_ERROR_FLOOR = 500;
4888
+ function is_retryable_status(status2) {
4889
+ if (status2 >= SERVER_ERROR_FLOOR) return true;
4890
+ return status2 === RATE_LIMITED_STATUS;
4891
+ }
4892
+ function classify_status(status2) {
4893
+ if (status2 === void 0) return "unreachable";
4894
+ return is_retryable_status(status2) ? "unreachable" : "reachable";
4895
+ }
4896
+ var gh_reachability = { classify_status };
4897
+
4898
+ // scripts/gh/git-gh-issue-read.ts
4899
+ async function read_blocked_by_listing(issue_number, repo, exact_total) {
4900
+ const json = await git_gh_exec.exec_gh_api({
4901
+ path: `${git_gh_api_path.blocked_by_api_path(issue_number, repo)}${git_gh_api_path.FULL_PAGE_QUERY}`
4902
+ });
4903
+ return git_gh_issue_rest.to_blocked_by(json, exact_total);
4904
+ }
4905
+ async function read_blocked_by(issue_number, rest, repo) {
4906
+ if (git_gh_issue_rest.is_pull_request(rest)) return git_gh_issue_rest.empty_blocked_by();
4907
+ const exact_total = git_gh_issue_rest.total_blocked_by(rest);
4908
+ if (exact_total === 0) return git_gh_issue_rest.empty_blocked_by();
4909
+ return await read_blocked_by_listing(issue_number, repo, exact_total);
4910
+ }
4911
+ function mark_followup(error) {
4912
+ if (!(error instanceof Error)) return error;
4913
+ const failure = gh_failure.failure_of(error);
4914
+ if (failure === void 0) return error;
4915
+ return gh_failure.attach(error, { ...failure, is_followup: true });
4916
+ }
4917
+ async function read_relations(issue_number, rest, repo) {
4918
+ try {
4919
+ return await read_blocked_by(issue_number, rest, repo);
4920
+ } catch (error) {
4921
+ throw mark_followup(error);
4922
+ }
4923
+ }
4924
+ async function read_issue_fields(issue_number, fields, repo) {
4925
+ const rest = git_gh_issue_rest.parse_rest_issue(
4926
+ await git_gh_exec.exec_gh_api({ path: git_gh_api_path.issue_api_path(issue_number, repo) })
4927
+ );
4928
+ const requested = git_gh_issue_rest.split_fields(fields);
4929
+ const blocked_by = requested.includes(BLOCKED_BY_FIELD) ? await read_relations(issue_number, rest, repo) : void 0;
4930
+ return git_gh_issue_rest.to_gh_issue(rest, requested, blocked_by);
4931
+ }
4932
+ var NOT_FOUND_STATUS = 404;
4933
+ function is_number_unresolved(failure) {
4934
+ return failure.status === NOT_FOUND_STATUS && failure.is_followup !== true;
4935
+ }
4936
+ function to_unreadable_reason(status2) {
4937
+ return gh_reachability.classify_status(status2) === "unreachable" ? "unreachable" : "rejected";
4938
+ }
4939
+ function to_read_failure(error) {
4940
+ const failure = gh_failure.failure_of(error);
4941
+ if (failure === void 0) return { kind: "unreadable", reason: "malformed", status: void 0 };
4942
+ if (is_number_unresolved(failure)) return { kind: "missing" };
4943
+ return {
4944
+ kind: "unreadable",
4945
+ reason: to_unreadable_reason(failure.status),
4946
+ status: failure.status
4947
+ };
4948
+ }
4949
+ async function issue_view_json_classified(issue_number, fields, repo) {
4950
+ try {
4951
+ const issue = await read_issue_fields(issue_number, fields, repo);
4952
+ return { kind: "read", json: JSON.stringify(issue) };
4953
+ } catch (error) {
4954
+ return to_read_failure(error);
4955
+ }
4956
+ }
4957
+ async function issue_view_field_classified(issue_number, field, repo) {
4958
+ try {
4959
+ const issue = await read_issue_fields(issue_number, field, repo);
4960
+ return { kind: "read", text: git_gh_issue_rest.to_field_text(issue[field]) };
4961
+ } catch (error) {
4962
+ return to_read_failure(error);
4963
+ }
4964
+ }
4965
+ async function issue_view_field(issue_number, field, repo) {
4966
+ const read3 = await issue_view_field_classified(issue_number, field, repo);
4967
+ return read3.kind === "read" ? read3.text : void 0;
4968
+ }
4969
+ async function issue_get_title(issue_number, repo) {
4970
+ const result = await issue_view_field(issue_number, "title", repo);
4971
+ return result === void 0 ? void 0 : git_gh_helpers.parse_pr_state_string(result);
4972
+ }
4973
+ async function issue_get_body(issue_number, repo) {
4974
+ return await issue_view_field(issue_number, "body", repo);
4975
+ }
4976
+ async function issue_get_body_classified(issue_number, repo) {
4977
+ return await issue_view_field_classified(issue_number, "body", repo);
4978
+ }
4979
+ async function issue_view_json(issue_number, fields, repo) {
4980
+ const read3 = await issue_view_json_classified(issue_number, fields, repo);
4981
+ return read3.kind === "read" ? read3.json : void 0;
4982
+ }
4983
+ var STATE_AND_RELATIONS_FIELDS = "number,state,labels,blockedBy";
4984
+ async function issue_get_state_and_relations(issue_number, repo) {
4985
+ return await issue_view_json(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
4986
+ }
4987
+ async function issue_get_state_and_relations_classified(issue_number, repo) {
4988
+ return await issue_view_json_classified(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
4989
+ }
4990
+ function is_unreachable_read(failure) {
4991
+ return failure.kind === "unreadable" && failure.reason === "unreachable";
4992
+ }
4993
+ var PLAN_FIELDS = "number,title,body,state,url,labels,blockedBy";
4994
+ async function issue_get_plan_fields(issue_number) {
4995
+ return await issue_view_json(issue_number, PLAN_FIELDS);
4996
+ }
4997
+ async function issue_get_plan_fields_classified(issue_number) {
4998
+ return await issue_view_json_classified(issue_number, PLAN_FIELDS);
4999
+ }
5000
+ async function issue_get_labels_and_body(issue_number) {
5001
+ return await issue_view_json(issue_number, "number,state,labels,body");
5002
+ }
5003
+ async function issue_list_comments(issue_number) {
5004
+ const path6 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
5005
+ try {
5006
+ return await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true });
5007
+ } catch {
5008
+ return void 0;
5009
+ }
5010
+ }
5011
+ async function has_no_relations_endpoint(issue_number, repo) {
5012
+ const status2 = await git_gh_exec.exec_gh_api_status(
5013
+ git_gh_api_path.blocked_by_api_path(issue_number, repo)
5014
+ );
5015
+ return status2 === NOT_FOUND_STATUS;
5016
+ }
5017
+ async function issue_blocked_by_references(issue_number, own_repo, repo) {
5018
+ try {
5019
+ const listing = await read_blocked_by_listing(issue_number, repo);
5020
+ return listing.nodes.map((blocker) => git_gh_issue_rest.reference_of_blocker(blocker, own_repo));
5021
+ } catch (error) {
5022
+ if (await has_no_relations_endpoint(issue_number, repo)) return [];
5023
+ throw error;
5024
+ }
5025
+ }
5026
+ async function issue_blocked_by_numbers(issue_number, repo) {
5027
+ const references = await issue_blocked_by_references(issue_number, "", repo);
5028
+ return references.map((blocker) => blocker.number);
5029
+ }
5030
+ var git_gh_issue_read = {
5031
+ issue_blocked_by_numbers,
5032
+ issue_blocked_by_references,
5033
+ issue_get_title,
5034
+ issue_get_body,
5035
+ issue_get_body_classified,
5036
+ issue_view_json,
5037
+ issue_view_json_classified,
5038
+ issue_get_state_and_relations,
5039
+ issue_get_state_and_relations_classified,
5040
+ is_unreachable_read,
5041
+ issue_get_plan_fields,
5042
+ issue_get_plan_fields_classified,
5043
+ issue_get_labels_and_body,
5044
+ issue_list_comments
5045
+ };
5046
+
5047
+ // scripts/gh/git-gh-issue-list.ts
5048
+ var PER_PAGE = 100;
5049
+ var FIRST_PAGE = 1;
5050
+ var DEFAULT_STATE = "open";
5051
+ var DEFAULT_SORT = "created";
5052
+ var LISTING_DIRECTION = "direction=desc";
5053
+ function listing_query(request) {
5054
+ const state = request.state ?? DEFAULT_STATE;
5055
+ const sort = request.sort ?? DEFAULT_SORT;
5056
+ return `state=${state}&sort=${sort}&${LISTING_DIRECTION}`;
5057
+ }
5058
+ function listing_path(request, page) {
5059
+ const label2 = request.label === void 0 ? "" : `&labels=${encodeURIComponent(request.label)}`;
5060
+ const paging = `&per_page=${String(PER_PAGE)}&page=${String(page)}`;
5061
+ return `${git_gh_api_path.issues_api_path(request.repo)}?${listing_query(request)}${label2}${paging}`;
5062
+ }
5063
+ var DIGIT_PATTERN = /\d/u;
5064
+ function has_token_occurrence(body, term) {
5065
+ let index = body.indexOf(term);
5066
+ while (index !== -1) {
5067
+ if (!DIGIT_PATTERN.test(body.charAt(index + term.length))) return true;
5068
+ index = body.indexOf(term, index + 1);
5069
+ }
5070
+ return false;
5071
+ }
5072
+ function matches_body(rest, term) {
5073
+ if (term === void 0) return true;
5074
+ const { body } = rest;
5075
+ return typeof body === "string" && has_token_occurrence(body, term);
5076
+ }
5077
+ function is_selected(rest, request) {
5078
+ return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
5079
+ }
5080
+ async function fetch_page(request, page) {
5081
+ const path6 = listing_path(request, page);
5082
+ const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path6 }));
5083
+ return {
5084
+ selected: rows.filter((row) => is_selected(row, request)),
5085
+ is_full: rows.length >= PER_PAGE
5086
+ };
5087
+ }
5088
+ var MAX_PAGES = 5;
5089
+ var MAX_SCANNED = MAX_PAGES * PER_PAGE;
5090
+ function wants_more(has_more, selected_count, limit) {
5091
+ return has_more && selected_count < limit;
5092
+ }
5093
+ async function fetch_selected(request) {
5094
+ const selected = [];
5095
+ let page = FIRST_PAGE;
5096
+ let has_more = true;
5097
+ while (page <= MAX_PAGES && wants_more(has_more, selected.length, request.limit)) {
5098
+ const result = await fetch_page(request, page);
5099
+ selected.push(...result.selected);
5100
+ has_more = result.is_full;
5101
+ page += 1;
5102
+ }
5103
+ return {
5104
+ rows: selected.slice(0, request.limit),
5105
+ is_capped: wants_more(has_more, selected.length, request.limit)
5106
+ };
5107
+ }
5108
+ var RELATION_CONCURRENCY = 8;
5109
+ async function read_relations2(rows, repo) {
5110
+ return await bounded_pool.bounded_map(
5111
+ rows,
5112
+ RELATION_CONCURRENCY,
5113
+ async (row) => await read_blocked_by(String(row.number), row, repo)
5114
+ );
5115
+ }
5116
+ async function issue_list(request) {
5117
+ try {
5118
+ const { rows, is_capped } = await fetch_selected(request);
5119
+ const fields = git_gh_issue_rest.split_fields(request.json_fields);
5120
+ const relations = fields.includes(BLOCKED_BY_FIELD) ? await read_relations2(rows, request.repo) : void 0;
5121
+ const mapped = rows.map(
5122
+ (row, index) => git_gh_issue_rest.to_gh_issue(row, fields, relations?.[index])
5123
+ );
5124
+ return { json: JSON.stringify(mapped), is_capped };
5125
+ } catch {
5126
+ return { json: void 0, is_capped: false };
5127
+ }
5128
+ }
5129
+ var git_gh_issue_list = {
5130
+ issue_list
5131
+ };
5132
+
5133
+ // scripts/gh/git-gh-issue-write.ts
5134
+ var PATCH_METHOD = "PATCH";
5135
+ var DELETE_METHOD = "DELETE";
5136
+ var HTML_URL_FILTER = ".html_url";
5137
+ var ID_FILTER = ".id";
5138
+ var LABELS_SEGMENT = "/labels";
5139
+ var COMMENTS_SEGMENT = "/comments";
5140
+ var CLOSED_STATE = "closed";
5141
+ var COLOR_HASH = "#";
5142
+ async function did_write_succeed(write) {
5143
+ try {
5144
+ await write();
5145
+ return true;
5146
+ } catch (error) {
5147
+ error_text.trace_swallowed("git_gh_issue_write.did_write_succeed", error);
5148
+ return false;
5149
+ }
5150
+ }
5151
+ async function issue_edit_body(issue_number, body) {
5152
+ return await git_gh_exec.exec_gh_api({
5153
+ path: git_gh_api_path.issue_api_path(issue_number),
5154
+ method: PATCH_METHOD,
5155
+ body: JSON.stringify({ body }),
5156
+ jq_filter: HTML_URL_FILTER
5157
+ });
5158
+ }
5159
+ async function issue_comment(issue_number, body) {
5160
+ return await git_gh_exec.exec_gh_api({
5161
+ path: `${git_gh_api_path.issue_api_path(issue_number)}${COMMENTS_SEGMENT}`,
5162
+ body: JSON.stringify({ body }),
5163
+ jq_filter: HTML_URL_FILTER
5164
+ });
5165
+ }
5166
+ async function issue_try_comment(issue_number, body) {
5167
+ return await did_write_succeed(async () => await issue_comment(issue_number, body));
5168
+ }
5169
+ async function issue_close(issue_number, comment) {
5170
+ return await did_write_succeed(async () => {
5171
+ if (comment !== void 0) await issue_comment(issue_number, comment);
5172
+ await git_gh_exec.exec_gh_api({
5173
+ path: git_gh_api_path.issue_api_path(issue_number),
5174
+ method: PATCH_METHOD,
5175
+ body: JSON.stringify({ state: CLOSED_STATE })
5176
+ });
5177
+ });
5178
+ }
5179
+ function to_label_color(color) {
5180
+ return color.startsWith(COLOR_HASH) ? color.slice(COLOR_HASH.length) : color;
5181
+ }
5182
+ async function label_ensure(input) {
5183
+ try {
5184
+ await git_gh_exec.exec_gh_api({
5185
+ path: `${git_gh_api_path.repo_api_path()}${LABELS_SEGMENT}`,
5186
+ body: JSON.stringify({
5187
+ name: input.name,
5188
+ color: to_label_color(input.color),
5189
+ description: input.description
5190
+ })
5191
+ });
5192
+ } catch {
5193
+ }
5194
+ }
5195
+ function issue_create_request(input) {
5196
+ const { labels } = input;
5197
+ return {
5198
+ path: git_gh_api_path.issues_api_path(input.repo),
5199
+ body: JSON.stringify({
5200
+ title: input.title,
5201
+ ...labels !== void 0 && { labels },
5202
+ body: input.body
5203
+ }),
5204
+ jq_filter: HTML_URL_FILTER
5205
+ };
5206
+ }
5207
+ async function issue_create_with_label(input) {
5208
+ return await git_gh_exec.exec_gh_api(
5209
+ issue_create_request({ title: input.title, body: input.body, labels: [input.label] })
5210
+ );
5211
+ }
5212
+ async function issue_add_label(issue_number, label2) {
5213
+ return await did_write_succeed(
5214
+ async () => await git_gh_exec.exec_gh_api({
5215
+ path: `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`,
5216
+ body: JSON.stringify({ labels: [label2] })
5217
+ })
5218
+ );
5219
+ }
5220
+ async function issue_remove_label(issue_number, label2) {
5221
+ const labels_path = `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`;
5222
+ await git_gh_exec.exec_gh_api({
5223
+ path: `${labels_path}/${encodeURIComponent(label2)}`,
5224
+ method: DELETE_METHOD
5225
+ });
5226
+ }
5227
+ async function read_issue_id(issue_number) {
5228
+ const raw = await git_gh_exec.exec_gh_api({
5229
+ path: git_gh_api_path.issue_api_path(issue_number),
5230
+ jq_filter: ID_FILTER
5231
+ });
5232
+ const issue_id = Number(raw.trim());
5233
+ if (!Number.isSafeInteger(issue_id) || issue_id <= 0) {
5234
+ throw new Error(`gh api answered no database id for issue #${issue_number}`);
5235
+ }
5236
+ return issue_id;
5237
+ }
5238
+ async function issue_add_blocked_by(issue_number, blocker) {
5239
+ return await did_write_succeed(async () => {
5240
+ const issue_id = await read_issue_id(blocker);
5241
+ await git_gh_exec.exec_gh_api({
5242
+ path: git_gh_api_path.blocked_by_api_path(issue_number),
5243
+ body: JSON.stringify({ issue_id })
5244
+ });
5245
+ });
5246
+ }
5247
+ async function issue_remove_blocked_by(issue_number, blocker) {
5248
+ return await did_write_succeed(async () => {
5249
+ const issue_id = await read_issue_id(blocker);
5250
+ const relations_path = git_gh_api_path.blocked_by_api_path(issue_number);
5251
+ await git_gh_exec.exec_gh_api({
5252
+ path: `${relations_path}/${String(issue_id)}`,
5253
+ method: DELETE_METHOD
5254
+ });
5255
+ });
5256
+ }
5257
+ var git_gh_issue_write = {
5258
+ issue_edit_body,
5259
+ issue_comment,
5260
+ issue_try_comment,
5261
+ issue_close,
5262
+ label_ensure,
5263
+ issue_create_request,
5264
+ issue_create_with_label,
5265
+ issue_add_label,
5266
+ issue_remove_label,
5267
+ issue_add_blocked_by,
5268
+ issue_remove_blocked_by
5269
+ };
5270
+
5271
+ // scripts/gh/git-gh-issue.ts
5272
+ var NUMBER_AND_BODY_FIELDS = "number,body";
5273
+ var BACKLOG_SCAN_FIELDS = `number,title,body,${BLOCKED_BY_COUNT_FIELD}`;
5274
+ var NUMBER_TITLE_AND_LABELS_FIELDS = "number,title,labels";
5275
+ var SUMMARY_FIELDS = "number,title,labels,createdAt";
5276
+ var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy`;
5277
+ var { issue_list: issue_list2 } = git_gh_issue_list;
5278
+ async function issue_list_recent(limit) {
5279
+ return await issue_list2({ json_fields: SUMMARY_FIELDS, limit });
5280
+ }
5281
+ async function issue_list_open_bodies(limit) {
5282
+ return await issue_list2({ json_fields: BACKLOG_SCAN_FIELDS, limit });
5283
+ }
5284
+ async function issue_list_recently_closed(limit) {
5285
+ return await issue_list2({
5286
+ json_fields: NUMBER_TITLE_AND_LABELS_FIELDS,
5287
+ limit,
5288
+ state: "closed",
5289
+ sort: "updated"
5290
+ });
5291
+ }
5292
+ async function issue_search_body(term, limit) {
5293
+ return await issue_list2({
5294
+ json_fields: NUMBER_AND_BODY_FIELDS,
5295
+ limit,
5296
+ body_term: term
5297
+ });
5298
+ }
5299
+ async function issue_list_by_label(label2, limit) {
5300
+ return await issue_list2({ json_fields: NUMBER_AND_BODY_FIELDS, limit, label: label2 });
5301
+ }
5302
+ async function issue_list_by_label_summary(label2, limit, options) {
5303
+ return await issue_list2({
5304
+ json_fields: options?.json_fields ?? PICKUP_FIELDS,
5305
+ limit,
5306
+ label: label2
5307
+ });
5308
+ }
5309
+ async function issue_list_by_label_in_repo(label2, limit, repo) {
5310
+ return await issue_list2({ json_fields: SUMMARY_FIELDS, limit, label: label2, repo });
5311
+ }
5312
+ var git_gh_issue = {
5313
+ ...git_gh_issue_read,
5314
+ ...git_gh_issue_write,
5315
+ issue_list_recent,
5316
+ issue_list_by_label,
5317
+ issue_list_by_label_in_repo,
5318
+ issue_list_by_label_summary,
5319
+ issue_search_body,
5320
+ issue_list_open_bodies,
5321
+ issue_list_recently_closed
5322
+ };
5323
+
5324
+ // scripts/ci/pr-classification.ts
5325
+ import { readFileSync as readFileSync4 } from "node:fs";
5326
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5327
+ import { z as z13 } from "zod";
5328
+ var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
5329
+ var ISSUE_BUG_LABEL = "bug";
5330
+ var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
5331
+ var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
5332
+ var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
5333
+ var LABEL = z13.object({ name: z13.string() });
5334
+ var USER = z13.object({ login: z13.string() });
5335
+ var PULL_REQUEST = z13.object({ number: z13.number(), user: USER });
5336
+ var EVENT = z13.object({
5337
+ pull_request: PULL_REQUEST,
5338
+ repository: z13.object({ full_name: z13.string() })
5339
+ });
5340
+ function classification_error(labels, author) {
5341
+ const legacy = labels.filter((label2) => LEGACY_LABELS.has(label2.toLowerCase()));
5342
+ if (legacy.length > 0) return `Remove legacy release labels: ${legacy.join(", ")}`;
5343
+ const selected = labels.filter((label2) => CLASSIFICATION_SET.has(label2.toLowerCase()));
5344
+ if (selected.length > 1) {
5345
+ return `Choose exactly one release classification; found: ${selected.join(", ")}`;
5346
+ }
5347
+ if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
5348
+ return `Choose one release classification: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}`;
5349
+ }
5350
+ function declaration(label2) {
5351
+ return `${DECLARATION_PREFIX}${label2}`;
5352
+ }
5353
+ function is_classification(value) {
5354
+ return CLASSIFICATION_SET.has(value);
5355
+ }
5356
+ function release_candidates(candidates) {
5357
+ if (!candidates.includes(BREAKING_CHANGE_LABEL)) return candidates;
5358
+ return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
5359
+ }
5360
+ function issue_candidates(issue_json) {
5361
+ const issue = z13.object({ labels: z13.array(LABEL), body: z13.string().nullable() }).parse(JSON.parse(issue_json));
5362
+ const names = issue.labels.map((label2) => label2.name.toLowerCase());
5363
+ const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
5364
+ const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
5365
+ const unknown = declared.filter((value) => !is_classification(value));
5366
+ if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
5367
+ if (names.includes(ISSUE_BUG_LABEL) && !candidates.includes(BREAKING_CHANGE_LABEL)) {
5368
+ candidates.push(BUGFIX_LABEL);
5369
+ }
5370
+ return candidates;
5371
+ }
5372
+ function select_issue_classification(issue_json) {
5373
+ const candidates = issue_candidates(issue_json);
5374
+ const selected = [...new Set(candidates)];
5375
+ const [label2] = selected;
5376
+ if (label2 === void 0 || selected.length > 1) {
5377
+ throw new Error(
5378
+ `Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "${DECLARATION_PREFIX}<label>" to the Issue body.`
5379
+ );
5380
+ }
5381
+ return label2;
5382
+ }
5383
+ function fetch_current_labels(repository2, pull_number) {
5384
+ const path6 = `repos/${repository2}/issues/${String(pull_number)}/labels`;
5385
+ try {
5386
+ const names = git_gh_exec.exec_gh_api_sync({ path: path6, jq_filter: ".[].name" });
5387
+ return names.split("\n").filter((name) => name !== "");
5388
+ } catch (error) {
5389
+ const detail = error_text.message_of(error);
5390
+ throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error });
5391
+ }
5392
+ }
5393
+ function check_event(event_path, read_labels = fetch_current_labels) {
5394
+ const event = EVENT.parse(JSON.parse(readFileSync4(event_path, "utf8")));
5395
+ const labels = read_labels(event.repository.full_name, event.pull_request.number);
5396
+ return classification_error(labels, event.pull_request.user.login);
5397
+ }
5398
+ if (process.argv[1] === fileURLToPath2(import.meta.url)) {
5399
+ const event_path = process.env["GITHUB_EVENT_PATH"];
5400
+ if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
5401
+ const error = check_event(event_path);
5402
+ if (error) {
5403
+ console.error(`::error::${error}`);
5404
+ process.exitCode = 1;
5405
+ }
5406
+ }
5407
+ var pr_classification = {
5408
+ classification_error,
5409
+ check_event,
5410
+ fetch_current_labels,
5411
+ declaration,
5412
+ is_classification,
5413
+ select_issue_classification
5414
+ };
5415
+
5416
+ // scripts/lib/poll.ts
5417
+ async function sleep3(duration_ms) {
5418
+ await new Promise((resolve3) => {
5419
+ setTimeout(resolve3, duration_ms);
5420
+ });
5421
+ }
5422
+ async function wait_between(wait, interval_ms, attempt, attempts) {
5423
+ if (attempt < attempts - 1) await wait(interval_ms);
5424
+ }
5425
+ async function poll_until(is_done2, options) {
5426
+ const wait = options.sleeper ?? sleep3;
5427
+ for (let attempt = 0; attempt < options.attempts; attempt += 1) {
5428
+ if (await is_done2()) return true;
5429
+ await wait_between(wait, options.interval_ms, attempt, options.attempts);
5430
+ }
5431
+ return false;
5432
+ }
5433
+ var poll = { poll_until, sleep: sleep3 };
5434
+
5435
+ // scripts/gh/git-gh-pr.ts
5436
+ import { z as z17 } from "zod";
5437
+
5438
+ // scripts/gh/git-gh-pr-rest.ts
5439
+ import { z as z14 } from "zod";
5440
+ var rest_repo_schema = z14.looseObject({ full_name: z14.string().optional() }).nullish();
5441
+ var rest_pull_side_schema = z14.looseObject({ repo: rest_repo_schema });
5442
+ var rest_pull_schema = z14.looseObject({
5443
+ number: z14.number(),
5444
+ html_url: z14.string().optional(),
5445
+ body: z14.string().nullish(),
5446
+ state: z14.string().optional(),
5447
+ merged: z14.boolean().nullish(),
5448
+ merged_at: z14.string().nullish(),
5449
+ mergeable: z14.boolean().nullish(),
5450
+ mergeable_state: z14.string().optional(),
5451
+ // `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
5452
+ // REST hangs the check runs and the status contexts off the commit rather than off the pull
5453
+ // request (joshuafolkken/kit#1028).
5454
+ head: z14.looseObject({ ref: z14.string().optional(), sha: z14.string().optional(), repo: rest_repo_schema }).nullish(),
5455
+ base: rest_pull_side_schema.nullish(),
5456
+ // `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
5457
+ // is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`
5458
+ // (joshuafolkken/kit#2497).
5459
+ node_id: z14.string().optional(),
5460
+ auto_merge: z14.looseObject({}).nullish()
5461
+ });
5462
+ var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
5463
+ var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
5464
+ var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
5465
+ var OPEN_STATE = "open";
5466
+ var EMPTY_BODY2 = "";
5467
+ function parse_rest_pull(rest_json) {
5468
+ const parsed = parse_json_object_safe(rest_json, rest_pull_schema);
5469
+ if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
5470
+ return parsed;
5471
+ }
5472
+ function parse_rest_pulls(rest_json) {
5473
+ const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
5474
+ if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
5475
+ return parsed;
5476
+ }
5477
+ function select_pull(pulls) {
5478
+ return pulls.find((pull) => pull.state === OPEN_STATE) ?? pulls[0];
5479
+ }
5480
+ function is_merged(pull) {
5481
+ return pull.merged === true || typeof pull.merged_at === "string";
5482
+ }
5483
+ function to_pr_state(pull) {
5484
+ return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
5485
+ }
5486
+ function to_repo_name(side) {
5487
+ return side?.repo?.full_name;
5488
+ }
5489
+ function is_same_repository_head(pull) {
5490
+ const head_repo = to_repo_name(pull.head);
5491
+ return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
5492
+ }
5493
+ function to_pr_info(pull) {
5494
+ return {
5495
+ mergeable: pull.mergeable,
5496
+ mergeStateStatus: to_gh_state(pull.mergeable_state),
5497
+ state: to_pr_state(pull)
5498
+ };
5499
+ }
5500
+ function to_pr_comments(rest_json) {
5501
+ const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
5502
+ if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
5503
+ return parsed.map((comment) => ({
5504
+ body: comment.body ?? EMPTY_BODY2,
5505
+ url: comment.html_url,
5506
+ author: { login: comment.user?.login }
5507
+ }));
5508
+ }
5509
+ var git_gh_pr_rest = {
5510
+ parse_rest_pull,
5511
+ parse_rest_pulls,
5512
+ select_pull,
5513
+ is_same_repository_head,
5514
+ is_merged,
5515
+ to_pr_state,
5516
+ to_pr_info,
5517
+ to_pr_comments
5518
+ };
5519
+
5520
+ // scripts/gh/git-gh-pr-read.ts
5521
+ var pr_number_by_branch = /* @__PURE__ */ new Map();
5522
+ var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
5523
+ var COMMENTS_QUERY = FULL_PAGE_QUERY;
5524
+ var OWNER_PLACEHOLDER = "{owner}";
5525
+ var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
5526
+ var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
5527
+ function lookup_path(branch_name) {
5528
+ const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
5529
+ return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
5530
+ }
5531
+ async function fetch_pr_number(branch_name) {
5532
+ const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
5533
+ return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
5534
+ }
5535
+ var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
5536
+ async function fetch_pr_number_read(branch_name) {
5537
+ try {
5538
+ const pr_number = await fetch_pr_number(branch_name);
5539
+ if (pr_number === void 0) return { kind: "missing" };
5540
+ pr_number_by_branch.set(branch_name, pr_number);
5541
+ return { kind: "read", pr_number };
5542
+ } catch (error) {
5543
+ return { kind: "unreadable", cause: error };
5544
+ }
5545
+ }
5546
+ async function start_pr_number_read(branch_name) {
5547
+ const pending = pending_pr_number_by_branch.get(branch_name);
5548
+ if (pending !== void 0) return await pending;
5549
+ const lookup = fetch_pr_number_read(branch_name);
5550
+ pending_pr_number_by_branch.set(branch_name, lookup);
5551
+ try {
5552
+ return await lookup;
5553
+ } finally {
5554
+ pending_pr_number_by_branch.delete(branch_name);
5555
+ }
5556
+ }
5557
+ function forget_pr_numbers() {
5558
+ pr_number_by_branch.clear();
5559
+ pending_pr_number_by_branch.clear();
5560
+ }
5561
+ async function read_pr_number(branch_name) {
5562
+ const cached = pr_number_by_branch.get(branch_name);
5563
+ if (cached !== void 0) return { kind: "read", pr_number: cached };
5564
+ return await start_pr_number_read(branch_name);
5565
+ }
5566
+ async function resolve_pr_number(branch_name) {
5567
+ const read3 = await read_pr_number(branch_name);
5568
+ return read3.kind === "read" ? read3.pr_number : void 0;
5569
+ }
5570
+ function to_unreadable_error(branch_name, cause) {
5571
+ return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
5572
+ }
5573
+ async function require_pr_number(branch_name) {
5574
+ const read3 = await read_pr_number(branch_name);
5575
+ if (read3.kind === "read") return read3.pr_number;
5576
+ if (read3.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
5577
+ throw to_unreadable_error(branch_name, read3.cause);
5578
+ }
5579
+ async function read_pull(pr_number) {
5580
+ const json = await git_gh_exec.exec_gh_api({
5581
+ path: git_gh_api_path.pull_api_path(String(pr_number))
5582
+ });
5583
+ return git_gh_pr_rest.parse_rest_pull(json);
5584
+ }
5585
+ async function read_pull_of_branch(branch_name) {
5586
+ const pr_number = await resolve_pr_number(branch_name);
5587
+ if (pr_number === void 0) return void 0;
5588
+ try {
5589
+ return await read_pull(pr_number);
5590
+ } catch {
5591
+ return void 0;
5592
+ }
5593
+ }
5594
+ async function pr_exists(branch_name) {
5595
+ const read3 = await read_pr_number(branch_name);
5596
+ if (read3.kind === "unreadable") throw to_unreadable_error(branch_name, read3.cause);
5597
+ return read3.kind === "read";
5598
+ }
5599
+ async function pr_get_number(branch_name) {
5600
+ return await resolve_pr_number(branch_name);
5601
+ }
5602
+ async function pr_get_url(branch_name) {
5603
+ const pull = await read_pull_of_branch(branch_name);
5604
+ if (pull?.html_url === void 0) return void 0;
5605
+ return git_gh_helpers.parse_pr_state_string(pull.html_url);
5606
+ }
5607
+ async function pr_get_merge_state(branch_name) {
5608
+ const pull = await read_pull_of_branch(branch_name);
5609
+ if (pull === void 0) return void 0;
5610
+ return {
5611
+ is_merged: git_gh_pr_rest.is_merged(pull),
5612
+ merged_at: pull.merged_at ?? void 0,
5613
+ head_sha: pull.head?.sha
5614
+ };
5615
+ }
5616
+ async function pr_get_body(branch_name) {
5617
+ const pull = await read_pull_of_branch(branch_name);
5618
+ const body = pull?.body;
5619
+ return typeof body === "string" && body.length > 0 ? body : void 0;
5620
+ }
5621
+ async function pr_view(branch_name) {
5622
+ const pull = await read_pull_of_branch(branch_name);
5623
+ if (pull === void 0) return "";
5624
+ return JSON.stringify(git_gh_pr_rest.to_pr_info(pull));
5625
+ }
5626
+ async function read_comments(branch_name, to_path, to_json) {
5627
+ const pr_number = await resolve_pr_number(branch_name);
5628
+ if (pr_number === void 0) return void 0;
5629
+ const path6 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
5630
+ try {
5631
+ return to_json(await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true }));
5632
+ } catch {
5633
+ return void 0;
5634
+ }
5635
+ }
5636
+ function to_comments_json(raw) {
5637
+ return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
5638
+ }
5639
+ function as_served(raw) {
5640
+ return raw;
5641
+ }
5642
+ async function pr_get_comments(branch_name) {
5643
+ return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
5644
+ }
5645
+ async function pr_get_review_comments(branch_name) {
5646
+ return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
5647
+ }
5648
+ var git_gh_pr_read = {
5649
+ pr_exists,
5650
+ pr_get_number,
5651
+ pr_get_url,
5652
+ pr_get_body,
5653
+ pr_get_merge_state,
5654
+ pr_view,
5655
+ pr_get_comments,
5656
+ pr_get_review_comments
5657
+ };
5658
+
5659
+ // scripts/gh/git-gh-pr-review.ts
5660
+ import { z as z15 } from "zod";
5661
+ var REVIEW_APPROVED = "APPROVED";
5662
+ var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
5663
+ var REVIEW_NONE = "";
5664
+ var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
5665
+ var rest_review_schema = z15.looseObject({
5666
+ state: z15.string().optional(),
5667
+ user: z15.looseObject({ login: z15.string().optional() }).nullish()
5668
+ });
5669
+ var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
5670
+ function parse_rest_reviews(reviews_json) {
5671
+ const parsed = parse_json_array_or_undefined(reviews_json, rest_review_schema);
5672
+ if (parsed === void 0) throw new Error(NOT_A_REVIEW_LISTING);
5673
+ return parsed;
5674
+ }
5675
+ var UNATTRIBUTED_REVIEWER = "(no author)";
5676
+ function read_reviewer(review) {
5677
+ const login = review.user?.login;
5678
+ return typeof login === "string" && login.length > 0 ? login : UNATTRIBUTED_REVIEWER;
5679
+ }
5680
+ function read_state_changing(review) {
5681
+ const state = review.state?.toUpperCase();
5682
+ return state !== void 0 && STATE_CHANGING_REVIEWS.has(state) ? state : void 0;
5683
+ }
5684
+ function fold_latest_states(reviews) {
5685
+ const latest = /* @__PURE__ */ new Map();
5686
+ for (const review of reviews) {
5687
+ const state = read_state_changing(review);
5688
+ if (state !== void 0) latest.set(read_reviewer(review), state);
5689
+ }
5690
+ return latest;
5691
+ }
5692
+ function to_review_decision(reviews_json) {
5693
+ const latest = new Set(fold_latest_states(parse_rest_reviews(reviews_json)).values());
5694
+ if (latest.has(REVIEW_CHANGES_REQUESTED)) return REVIEW_CHANGES_REQUESTED;
5695
+ return latest.has(REVIEW_APPROVED) ? REVIEW_APPROVED : REVIEW_NONE;
5696
+ }
5697
+ var git_gh_pr_review = { to_review_decision };
5698
+
5699
+ // scripts/gh/git-gh-pr-rollup.ts
5700
+ import { z as z16 } from "zod";
5701
+ var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
5702
+ var TYPE_NAME_KEY = "__typename";
5703
+ var rollup_element_schema = z16.looseObject({});
5704
+ var check_runs_page_schema = z16.looseObject({
5705
+ // Required, not optional: GitHub always names the key, and accepting a page without one would
5706
+ // answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
5707
+ check_runs: z16.array(rollup_element_schema)
5708
+ });
5709
+ var id_holder_schema = z16.object({ id: z16.number() });
5710
+ var status_page_schema = z16.looseObject({
5711
+ statuses: z16.array(rollup_element_schema)
5712
+ });
5713
+ var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
5714
+ var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
5715
+ function read_pages(raw_json, schema, message) {
5716
+ const pages = parse_json_array_or_undefined(raw_json, schema);
5717
+ if (pages === void 0) throw new Error(message);
5718
+ return pages;
5719
+ }
5720
+ var NO_SUITE = -1;
5721
+ function read_id(value) {
5722
+ const parsed = id_holder_schema.safeParse(value);
5723
+ return parsed.success ? parsed.data.id : void 0;
5724
+ }
5725
+ function check_run_group_key(run3) {
5726
+ const { name, app } = run3;
5727
+ if (typeof name !== "string") return void 0;
5728
+ return JSON.stringify([name, read_id(app)]);
5729
+ }
5730
+ function read_rank(run3) {
5731
+ const id = read_id(run3);
5732
+ if (id === void 0) return void 0;
5733
+ return { suite: read_id(run3["check_suite"]) ?? NO_SUITE, run: id };
5734
+ }
5735
+ function is_ranked_above(candidate, current) {
5736
+ if (candidate.suite !== current.suite) return candidate.suite > current.suite;
5737
+ return candidate.run > current.run;
5738
+ }
5739
+ function should_replace(previous, rank) {
5740
+ const previous_rank = previous === void 0 ? void 0 : read_rank(previous);
5741
+ return previous_rank === void 0 || is_ranked_above(rank, previous_rank);
5742
+ }
5743
+ function remember_newest_run(run3, newest) {
5744
+ const key2 = check_run_group_key(run3);
5745
+ const rank = read_rank(run3);
5746
+ if (key2 === void 0 || rank === void 0) return;
5747
+ if (should_replace(newest.get(key2), rank)) newest.set(key2, run3);
5748
+ }
5749
+ function is_newest_run(run3, newest) {
5750
+ const key2 = check_run_group_key(run3);
5751
+ if (key2 === void 0 || read_rank(run3) === void 0) return true;
5752
+ return newest.get(key2) === run3;
5753
+ }
5754
+ function to_check_run_items(check_runs_json) {
5755
+ const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
5756
+ const runs = pages.flatMap((page) => page.check_runs);
5757
+ const newest = /* @__PURE__ */ new Map();
5758
+ for (const run3 of runs) remember_newest_run(run3, newest);
5759
+ return runs.filter((run3) => is_newest_run(run3, newest));
5760
+ }
5761
+ function to_status_context_items(status_json) {
5762
+ const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
5763
+ return pages.flatMap((page) => page.statuses).map((status2) => ({ ...status2, [TYPE_NAME_KEY]: STATUS_CONTEXT_TYPE_NAME }));
5764
+ }
5765
+ function to_status_check_rollup(input) {
5766
+ return [
5767
+ ...to_check_run_items(input.check_runs_json),
5768
+ ...to_status_context_items(input.status_json)
5769
+ ];
5770
+ }
5771
+ var git_gh_pr_rollup = {
5772
+ to_status_check_rollup
5773
+ };
5774
+
5775
+ // scripts/gh/git-gh-pr-snapshot.ts
5776
+ var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
5777
+ async function read_object_pages(path6) {
5778
+ return await git_gh_exec.exec_gh_api({
5779
+ path: `${path6}${FULL_PAGE_QUERY}`,
5780
+ should_paginate: true,
5781
+ should_slurp: true
5782
+ });
5783
+ }
5784
+ async function read_array_pages(path6) {
5785
+ return await git_gh_exec.exec_gh_api({
5786
+ path: `${path6}${FULL_PAGE_QUERY}`,
5787
+ should_paginate: true
5788
+ });
5789
+ }
5790
+ function read_head_sha(pull) {
5791
+ const sha = pull.head?.sha;
5792
+ if (sha === void 0) throw new Error(NO_HEAD_SHA_MESSAGE);
5793
+ return sha;
5794
+ }
5795
+ async function read_status_check_rollup(commit_sha) {
5796
+ const [check_runs_json, status_json] = await Promise.all([
5797
+ read_object_pages(git_gh_api_path.commit_check_runs_api_path(commit_sha)),
5798
+ read_object_pages(git_gh_api_path.commit_status_api_path(commit_sha))
5799
+ ]);
5800
+ return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
5801
+ }
5802
+ async function pr_get_review_decision(pr_number) {
5803
+ const path6 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
5804
+ return git_gh_pr_review.to_review_decision(await read_array_pages(path6));
5805
+ }
5806
+ async function build_checks_fields(pull) {
5807
+ return {
5808
+ statusCheckRollup: await read_status_check_rollup(read_head_sha(pull)),
5809
+ mergeStateStatus: to_gh_state(pull.mergeable_state)
5810
+ };
5811
+ }
5812
+ async function resolve_pull(branch_name) {
5813
+ const pr_number = await require_pr_number(branch_name);
5814
+ return { pr_number, pull: await read_pull(pr_number) };
5815
+ }
5816
+ async function pr_get_checks_snapshot(branch_name) {
5817
+ const { pr_number, pull } = await resolve_pull(branch_name);
5818
+ return { pr_number, snapshot_json: JSON.stringify(await build_checks_fields(pull)) };
5819
+ }
5820
+ async function pr_get_state_snapshot(branch_name) {
5821
+ const { pr_number, pull } = await resolve_pull(branch_name);
5822
+ const [fields, review_decision] = await Promise.all([
5823
+ build_checks_fields(pull),
5824
+ pr_get_review_decision(pr_number)
5825
+ ]);
5826
+ return JSON.stringify({ ...fields, reviewDecision: review_decision });
5827
+ }
5828
+ var git_gh_pr_snapshot = {
5829
+ pr_get_state_snapshot,
5830
+ pr_get_checks_snapshot,
5831
+ pr_get_review_decision
5832
+ };
5833
+
5834
+ // scripts/gh/git-gh-pr.ts
5835
+ var HTML_URL_FILTER2 = ".html_url";
5836
+ var PUT_METHOD = "PUT";
5837
+ var PATCH_METHOD2 = "PATCH";
5838
+ var MERGE_COMMIT_METHOD = "merge";
5839
+ var MERGE_REQUEST_TIMEOUT_MS = 18e4;
5840
+ var MERGE_UNCONFIRMED_MESSAGE = "gh api could not read the pull request back after the merge request failed";
5841
+ var MERGE_READ_BACK_ATTEMPTS = 3;
5842
+ var MERGE_READ_BACK_INTERVAL_MS = 1e3;
5843
+ var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
5844
+ var LABELS_SEGMENT2 = "/labels";
5845
+ var PR_LABELS = z17.array(z17.object({ name: z17.string() }));
5846
+ async function add_pr_classification(number, label2) {
5847
+ await git_gh_exec.exec_gh_api({
5848
+ path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
5849
+ body: JSON.stringify({ labels: [label2] })
5850
+ });
5851
+ }
5852
+ async function pr_get_classification(branch_name) {
5853
+ const number = await require_pr_number(branch_name);
5854
+ const response = await git_gh_exec.exec_gh_api({
5855
+ path: `${git_gh_api_path.issue_api_path(String(number))}${LABELS_SEGMENT2}`
5856
+ });
5857
+ const names = PR_LABELS.parse(JSON.parse(response)).map((label2) => label2.name.toLowerCase());
5858
+ const selected = names.filter((name) => pr_classification.is_classification(name));
5859
+ if (selected.length === 0) return void 0;
5860
+ const error = pr_classification.classification_error(names, "human");
5861
+ if (error !== void 0) throw new Error(error);
5862
+ return selected[0];
5863
+ }
5864
+ async function pr_ensure_classification(branch_name, label2) {
5865
+ if (await pr_get_classification(branch_name) !== void 0) return;
5866
+ const number = await require_pr_number(branch_name);
5867
+ await add_pr_classification(String(number), label2);
5868
+ }
5869
+ async function post_pull_request(title, body) {
5870
+ const base = await git_command.get_default_branch();
5871
+ const head = await git_command.branch();
5872
+ forget_pr_numbers();
5873
+ try {
5874
+ return await git_gh_exec.exec_gh_api({
5875
+ path: git_gh_api_path.pulls_api_path(),
5876
+ body: JSON.stringify({ title, body, head, base }),
5877
+ jq_filter: HTML_URL_FILTER2
5878
+ });
5879
+ } catch (error) {
5880
+ return git_gh_helpers.handle_pr_create_error(error);
5881
+ }
5882
+ }
5883
+ async function pr_create(title, body, label2) {
5884
+ const classification_error2 = pr_classification.classification_error([label2], "human");
5885
+ if (classification_error2 !== void 0) throw new Error(classification_error2);
5886
+ const url = await post_pull_request(title, body);
5887
+ const number = PR_NUMBER_PATTERN.exec(url)?.[1];
5888
+ if (number === void 0) throw new Error(`Could not identify the created pull request: ${url}`);
5889
+ await add_pr_classification(number, label2);
5890
+ return url;
5891
+ }
5892
+ async function pr_comment(branch_name, body) {
5893
+ const pr_number = await require_pr_number(branch_name);
5894
+ return await git_gh_issue_write.issue_comment(String(pr_number), body);
5895
+ }
5896
+ async function pr_update_body(branch_name, body) {
5897
+ const pr_number = await require_pr_number(branch_name);
5898
+ await git_gh_exec.exec_gh_api({
5899
+ path: git_gh_api_path.pull_api_path(String(pr_number)),
5900
+ method: PATCH_METHOD2,
5901
+ body: JSON.stringify({ body })
5902
+ });
5903
+ }
5904
+ async function put_merge(pr_number) {
5905
+ await git_gh_exec.exec_gh_api({
5906
+ path: git_gh_api_path.pull_merge_api_path(String(pr_number)),
5907
+ method: PUT_METHOD,
5908
+ body: JSON.stringify({ merge_method: MERGE_COMMIT_METHOD }),
5909
+ timeout_ms: MERGE_REQUEST_TIMEOUT_MS
5910
+ });
5911
+ }
5912
+ async function read_merge_state(pr_number) {
5913
+ try {
5914
+ return git_gh_pr_rest.is_merged(await read_pull(pr_number));
5915
+ } catch (error) {
5916
+ console.warn(`Could not read the pull request back: ${String(error)}`);
5917
+ return void 0;
5918
+ }
5919
+ }
5920
+ async function sleep_between_read_backs(attempt) {
5921
+ if (attempt < MERGE_READ_BACK_ATTEMPTS - 1) await poll.sleep(MERGE_READ_BACK_INTERVAL_MS);
5922
+ }
5923
+ async function has_merge_landed(pr_number, merge_error) {
5924
+ for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
5925
+ const is_merged2 = await read_merge_state(pr_number);
5926
+ if (is_merged2 !== void 0) return is_merged2;
5927
+ await sleep_between_read_backs(attempt);
5928
+ }
5929
+ throw new Error(MERGE_UNCONFIRMED_MESSAGE, { cause: merge_error });
5930
+ }
5931
+ async function pr_merge(branch_name) {
5932
+ const pr_number = await require_pr_number(branch_name);
5933
+ try {
5934
+ await put_merge(pr_number);
5935
+ } catch (error) {
5936
+ if (!await has_merge_landed(pr_number, error)) throw error;
5937
+ }
5938
+ }
5939
+ var git_gh_pr = {
5940
+ ...git_gh_pr_read,
5941
+ ...git_gh_pr_snapshot,
5942
+ pr_create,
5943
+ pr_get_classification,
5944
+ pr_ensure_classification,
5945
+ pr_comment,
5946
+ pr_merge,
5947
+ pr_update_body
5948
+ };
5949
+
5950
+ // scripts/gh/git-gh-pr-auto-merge.ts
5951
+ var GRAPHQL_PATH = "graphql";
5952
+ var OPEN_PULLS_QUERY = `${git_gh_api_path.FULL_PAGE_QUERY}&state=open`;
5953
+ var MISSING_NODE_ID_MESSAGE = "gh api answered a pull request without a node_id";
5954
+ var ENABLE_AUTO_MERGE_MUTATION = "mutation($id: ID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE }) { clientMutationId } }";
5955
+ async function pr_enable_auto_merge(branch_name) {
5956
+ const pull = await read_pull(await require_pr_number(branch_name));
5957
+ if (pull.node_id === void 0) throw new Error(MISSING_NODE_ID_MESSAGE);
5958
+ await git_gh_exec.exec_gh_api({
5959
+ path: GRAPHQL_PATH,
5960
+ body: JSON.stringify({ query: ENABLE_AUTO_MERGE_MUTATION, variables: { id: pull.node_id } })
5961
+ });
5962
+ }
5963
+ async function pr_is_merged(branch_name) {
5964
+ try {
5965
+ return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)));
5966
+ } catch (error) {
5967
+ error_text.trace_swallowed("git_gh_pr_auto_merge.pr_is_merged", error);
5968
+ return false;
5969
+ }
5970
+ }
5971
+ function head_reference_of(pull) {
5972
+ return pull.head?.ref ?? "";
5973
+ }
5974
+ function has_head_prefix(pull, prefix) {
5975
+ return git_gh_pr_rest.is_same_repository_head(pull) && head_reference_of(pull).startsWith(prefix);
5976
+ }
5977
+ function to_open_pull(pull) {
5978
+ return {
5979
+ number: pull.number,
5980
+ head_ref: head_reference_of(pull),
5981
+ has_auto_merge: pull.auto_merge !== null && pull.auto_merge !== void 0
5982
+ };
5983
+ }
5984
+ async function pr_list_open_with_head_prefix(prefix) {
5985
+ const json = await git_gh_exec.exec_gh_api({
5986
+ path: `${git_gh_api_path.pulls_api_path()}${OPEN_PULLS_QUERY}`,
5987
+ should_paginate: true
5988
+ });
5989
+ return git_gh_pr_rest.parse_rest_pulls(json).filter((pull) => has_head_prefix(pull, prefix)).map((pull) => to_open_pull(pull));
5990
+ }
5991
+ var git_gh_pr_auto_merge = {
5992
+ pr_enable_auto_merge,
5993
+ pr_is_merged,
5994
+ pr_list_open_with_head_prefix
5995
+ };
5996
+
5997
+ // scripts/gh/git-gh-repo.ts
5998
+ async function repo_get_name_with_owner() {
5999
+ try {
6000
+ const result = await git_gh_exec.exec_gh_api({
6001
+ path: git_gh_api_path.repo_api_path(),
6002
+ jq_filter: ".full_name"
6003
+ });
6004
+ return git_gh_helpers.parse_pr_state_string(result);
6005
+ } catch {
6006
+ return void 0;
6007
+ }
6008
+ }
6009
+ var git_gh_repo = {
6010
+ repo_get_name_with_owner
6011
+ };
6012
+
6013
+ // scripts/gh/git-pr-checks-parse.ts
6014
+ var CHECK_STATUS_PASS = "pass";
6015
+ var CHECK_STATUS_PENDING = "pending";
6016
+ var CHECK_STATUS_FAIL = "fail";
6017
+ var CHECK_STATUS_MISSING = "missing";
6018
+ var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["success", "skipped", "neutral"]);
6019
+ var KEY_TYPE_NAME = "__typename";
6020
+ var KEY_STATE = "state";
6021
+ var KEY_STATUS = "status";
6022
+ var KEY_CONCLUSION = "conclusion";
6023
+ var KEY_NAME = "name";
6024
+ var KEY_CONTEXT = "context";
6025
+ function read_string(value) {
6026
+ if (typeof value !== "string") return void 0;
6027
+ const trimmed2 = value.trim();
6028
+ if (trimmed2.length === 0) return void 0;
6029
+ return trimmed2;
6030
+ }
6031
+ var PENDING_STATUS_STATES = /* @__PURE__ */ new Set(["pending", "expected"]);
6032
+ function parse_status_context(item) {
6033
+ const state = read_string(item[KEY_STATE])?.toLowerCase();
6034
+ if (state === "success") return CHECK_STATUS_PASS;
6035
+ if (state !== void 0 && PENDING_STATUS_STATES.has(state)) return CHECK_STATUS_PENDING;
6036
+ return CHECK_STATUS_FAIL;
6037
+ }
6038
+ function is_passing_conclusion(item) {
6039
+ const conclusion = read_string(item[KEY_CONCLUSION])?.toLowerCase();
6040
+ return conclusion !== void 0 && PASSING_CONCLUSIONS.has(conclusion);
6041
+ }
6042
+ function parse_check_run(item) {
6043
+ const status2 = read_string(item[KEY_STATUS])?.toLowerCase();
6044
+ if (status2 !== "completed") return CHECK_STATUS_PENDING;
6045
+ return is_passing_conclusion(item) ? CHECK_STATUS_PASS : CHECK_STATUS_FAIL;
6046
+ }
6047
+ function parse_rollup_status(item) {
6048
+ const type_name = read_string(item[KEY_TYPE_NAME]);
6049
+ if (type_name === "StatusContext") return parse_status_context(item);
6050
+ return parse_check_run(item);
6051
+ }
6052
+ function read_rollup_array(parsed) {
6053
+ const result = pr_raw_schema.safeParse(parsed);
6054
+ if (!result.success) return [];
6055
+ return result.data.statusCheckRollup ?? [];
6056
+ }
6057
+ function parse_rollup_item(item) {
6058
+ const name = read_string(item[KEY_NAME]) ?? read_string(item[KEY_CONTEXT]);
6059
+ if (name === void 0) return void 0;
6060
+ return { name, status: parse_rollup_status(item) };
6061
+ }
6062
+ function parse_rollup_checks(raw_json) {
6063
+ const parsed = json_value.parse_or_undefined(raw_json);
6064
+ const rollup = read_rollup_array(parsed);
6065
+ const checks = [];
6066
+ for (const item of rollup) {
6067
+ const parsed_item = parse_rollup_item(item);
6068
+ if (parsed_item !== void 0) checks.push(parsed_item);
6069
+ }
6070
+ return checks;
6071
+ }
6072
+ function parse_pr_state_snapshot(raw_json) {
6073
+ const parsed = json_value.parse_or_undefined(raw_json);
6074
+ const result = pr_raw_schema.safeParse(parsed);
6075
+ const data = result.success ? result.data : void 0;
6076
+ return {
6077
+ rollup: parse_rollup_checks(raw_json),
6078
+ merge_state_status: read_string(data?.mergeStateStatus),
6079
+ review_decision: read_string(data?.reviewDecision)
6080
+ };
6081
+ }
6082
+
6083
+ // scripts/gh/git-pr-checks-eval.ts
6084
+ var DEFAULT_REQUIRED_CHECKS = ["SonarQube"];
6085
+ var CODERABBIT_CHECK_NAME = "CodeRabbit";
6086
+ var REQUIRED_CHECKS_ENV_VAR = "JOSH_REQUIRED_CHECKS";
6087
+ function parse_required_checks() {
6088
+ const raw = process.env[REQUIRED_CHECKS_ENV_VAR];
6089
+ if (!raw) return DEFAULT_REQUIRED_CHECKS;
6090
+ return raw.split(",").map((check) => check.trim()).filter((check) => check.length > 0);
6091
+ }
6092
+ var REQUIRED_CHECKS = parse_required_checks();
6093
+ var MERGE_STATE_CLEAN = "CLEAN";
6094
+ var MERGE_STATE_UNSTABLE = "UNSTABLE";
6095
+ var MERGE_STATE_DIRTY = "DIRTY";
6096
+ var REVIEW_CHANGES_REQUESTED2 = "CHANGES_REQUESTED";
6097
+ var CHECK_SUITE_SEPARATOR = " / ";
6098
+ var FAILURE_MESSAGE_PREFIX = "PR checks failed";
6099
+ var FAILURE_REASON_REVIEW = "review requested changes";
6100
+ var FAILURE_REASON_CHECKS = "failed checks";
6101
+ var FAILURE_REASON_CONFLICT = "merge conflict";
6102
+ function is_required_match(check_name, required_name) {
6103
+ if (check_name === required_name) return true;
6104
+ return check_name.startsWith(`${required_name}${CHECK_SUITE_SEPARATOR}`);
6105
+ }
6106
+ function find_required_check(checks, required_name) {
6107
+ return checks.find((check) => is_required_match(check.name, required_name));
6108
+ }
6109
+ function read_required_statuses(checks) {
6110
+ return REQUIRED_CHECKS.map(
6111
+ (name) => find_required_check(checks, name)?.status ?? CHECK_STATUS_MISSING
6112
+ );
6113
+ }
6114
+ function is_review_blocked(review_decision) {
6115
+ return review_decision === REVIEW_CHANGES_REQUESTED2;
6116
+ }
6117
+ function is_merge_conflict(merge_state_status) {
6118
+ return merge_state_status === MERGE_STATE_DIRTY;
6119
+ }
6120
+ function evaluate_failure_state(input) {
6121
+ if (is_merge_conflict(input.merge_state_status) || is_review_blocked(input.review_decision) || input.failed_checks.length > 0) {
6122
+ return "failure";
6123
+ }
6124
+ return void 0;
6125
+ }
6126
+ function is_merge_state_clean(merge_state_status) {
6127
+ return merge_state_status === MERGE_STATE_CLEAN;
6128
+ }
6129
+ function is_every_required_passing(statuses) {
6130
+ return statuses.every((status2) => status2 === CHECK_STATUS_PASS);
6131
+ }
6132
+ function is_coderabbit_check(check_name) {
6133
+ return is_required_match(check_name, CODERABBIT_CHECK_NAME);
6134
+ }
6135
+ function is_unstable_only_from_coderabbit(snapshot) {
6136
+ if (snapshot.merge_state_status !== MERGE_STATE_UNSTABLE) return false;
6137
+ const non_passing = snapshot.rollup.filter((check) => check.status !== CHECK_STATUS_PASS);
6138
+ if (non_passing.length === 0) return false;
6139
+ return non_passing.every((check) => is_coderabbit_check(check.name));
6140
+ }
6141
+ function is_required_check(check_name) {
6142
+ return REQUIRED_CHECKS.some((required) => is_required_match(check_name, required));
6143
+ }
6144
+ function is_blocking_failure(check) {
6145
+ if (check.status !== CHECK_STATUS_FAIL) return false;
6146
+ return !is_coderabbit_check(check.name) || is_required_check(check.name);
6147
+ }
6148
+ function collect_blocking_failures(snapshot) {
6149
+ return snapshot.rollup.filter((check) => is_blocking_failure(check)).map((check) => check.name);
6150
+ }
6151
+ function is_mergeable_state(snapshot) {
6152
+ if (is_merge_state_clean(snapshot.merge_state_status)) return true;
6153
+ return is_unstable_only_from_coderabbit(snapshot);
6154
+ }
6155
+ function collect_failure_reasons(snapshot) {
6156
+ const reasons = [];
6157
+ if (is_merge_conflict(snapshot.merge_state_status)) reasons.push(FAILURE_REASON_CONFLICT);
6158
+ if (is_review_blocked(snapshot.review_decision)) reasons.push(FAILURE_REASON_REVIEW);
6159
+ const failed = collect_blocking_failures(snapshot);
6160
+ if (failed.length > 0) reasons.push(`${FAILURE_REASON_CHECKS}: ${failed.join(", ")}`);
6161
+ return reasons;
6162
+ }
6163
+ function describe_pr_failure(snapshot) {
6164
+ const reasons = collect_failure_reasons(snapshot);
6165
+ if (reasons.length === 0) return `${FAILURE_MESSAGE_PREFIX}.`;
6166
+ return `${FAILURE_MESSAGE_PREFIX} (${reasons.join("; ")}).`;
6167
+ }
6168
+ function evaluate_pr_state(snapshot) {
6169
+ const statuses = read_required_statuses(snapshot.rollup);
6170
+ const failure = evaluate_failure_state({
6171
+ review_decision: snapshot.review_decision,
6172
+ failed_checks: collect_blocking_failures(snapshot),
6173
+ merge_state_status: snapshot.merge_state_status
6174
+ });
6175
+ if (failure !== void 0) return failure;
6176
+ if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) {
6177
+ return "success";
6178
+ }
6179
+ return "pending";
6180
+ }
6181
+ function is_review_decision_decisive(snapshot) {
6182
+ return evaluate_pr_state(snapshot) !== "pending";
6183
+ }
6184
+
6185
+ // scripts/gh/git-pr-checks.ts
6186
+ var SECONDS_TO_MS = 1e3;
6187
+ var CHECK_WAIT_INTERVAL_MS = 1e4;
6188
+ var DEFAULT_TIMEOUT_SECONDS = 1920;
6189
+ function compute_max_attempts(timeout_seconds, interval_ms) {
6190
+ return Math.ceil(timeout_seconds / (interval_ms / SECONDS_TO_MS)) + 1;
6191
+ }
6192
+ var DEFAULT_MAX_ATTEMPTS = compute_max_attempts(DEFAULT_TIMEOUT_SECONDS, CHECK_WAIT_INTERVAL_MS);
6193
+ function get_configured_max_attempts() {
6194
+ const environment_seconds = Number(process.env["JOSH_CI_TIMEOUT_SECONDS"]);
6195
+ return Number.isFinite(environment_seconds) && environment_seconds > 0 ? compute_max_attempts(environment_seconds, CHECK_WAIT_INTERVAL_MS) : DEFAULT_MAX_ATTEMPTS;
6196
+ }
6197
+ var CHECK_MAX_ATTEMPTS = get_configured_max_attempts();
6198
+ var DEFAULT_STABLE_READS = 2;
6199
+ var MAX_CONSECUTIVE_READ_FAILURES = 3;
6200
+ var PR_CHECKS_TIMEOUT_MESSAGE = "Timed out while waiting for PR checks to complete.";
6201
+ function is_pr_checks_timeout(error) {
6202
+ return error instanceof Error && error.message === PR_CHECKS_TIMEOUT_MESSAGE;
6203
+ }
6204
+ var SHOULD_ALWAYS_READ_REVIEW_DECISION = () => true;
6205
+ var MERGE_GATE_EVALUATOR = {
6206
+ evaluate: evaluate_pr_state,
6207
+ describe: describe_pr_failure,
6208
+ should_read_review_decision: is_review_decision_decisive
6209
+ };
6210
+ function advance_stable_count(previous, state) {
6211
+ return state === "success" ? previous + 1 : 0;
6212
+ }
6213
+ function classify_poll_result(input) {
6214
+ const state = input.evaluator.evaluate(input.snapshot);
6215
+ if (state === "failure") throw new Error(input.evaluator.describe(input.snapshot));
6216
+ const next_stable_count = advance_stable_count(input.stable_count, state);
6217
+ return {
6218
+ is_done: next_stable_count >= input.required_stable_reads,
6219
+ next_stable_count
6220
+ };
6221
+ }
6222
+ async function read_pr_state(input, evaluator) {
6223
+ try {
6224
+ const snapshot = await input.options.fetcher(
6225
+ input.options.branch_name,
6226
+ evaluator.should_read_review_decision ?? SHOULD_ALWAYS_READ_REVIEW_DECISION
6227
+ );
6228
+ return { kind: "read", snapshot };
6229
+ } catch (error) {
6230
+ return { kind: "unreadable", cause: error };
6231
+ }
6232
+ }
6233
+ async function sleep_before_next_attempt(input) {
6234
+ if (input.attempt < input.options.max_attempts - 1) {
6235
+ await poll.sleep(input.options.interval_ms);
6236
+ }
6237
+ }
6238
+ async function retry_after_unreadable(input, cause) {
6239
+ const next_failure_count = input.failure_count + 1;
6240
+ if (next_failure_count >= MAX_CONSECUTIVE_READ_FAILURES) throw cause;
6241
+ console.warn(
6242
+ `Could not read the PR state (${String(next_failure_count)}/${String(MAX_CONSECUTIVE_READ_FAILURES)}); asking again on the next poll: ${String(cause)}`
6243
+ );
6244
+ await sleep_before_next_attempt(input);
6245
+ return { next_stable_count: 0, next_failure_count, read_failure: cause };
6246
+ }
6247
+ function to_exhaustion_error(last_read_failure) {
6248
+ return last_read_failure ?? new Error(PR_CHECKS_TIMEOUT_MESSAGE);
6249
+ }
6250
+ async function attempt_pr_success_poll(input) {
6251
+ const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
6252
+ const read3 = await read_pr_state(input, evaluator);
6253
+ if (read3.kind === "unreadable") return await retry_after_unreadable(input, read3.cause);
6254
+ const classification = classify_poll_result({
6255
+ snapshot: read3.snapshot,
6256
+ stable_count: input.stable_count,
6257
+ required_stable_reads: input.options.required_stable_reads,
6258
+ evaluator
6259
+ });
6260
+ if (classification.is_done) {
6261
+ return { snapshot: read3.snapshot, next_stable_count: 0, next_failure_count: 0 };
6262
+ }
6263
+ await sleep_before_next_attempt(input);
6264
+ return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
6265
+ }
6266
+ async function wait_for_pr_success(options) {
6267
+ let carried = { next_stable_count: 0, next_failure_count: 0 };
6268
+ for (let attempt = 0; attempt < options.max_attempts; attempt += 1) {
6269
+ console.info(`Checking PR status\u2026 (${String(attempt + 1)}/${String(options.max_attempts)})`);
6270
+ carried = await attempt_pr_success_poll({
6271
+ options,
6272
+ stable_count: carried.next_stable_count,
6273
+ failure_count: carried.next_failure_count,
6274
+ attempt
6275
+ });
6276
+ if (carried.snapshot !== void 0) return carried.snapshot;
6277
+ }
6278
+ throw to_exhaustion_error(carried.read_failure);
6279
+ }
6280
+ async function default_fetch_pr_state(branch_name, should_read_review_decision = SHOULD_ALWAYS_READ_REVIEW_DECISION) {
6281
+ const checks = await git_gh_pr_snapshot.pr_get_checks_snapshot(branch_name);
6282
+ const snapshot = parse_pr_state_snapshot(checks.snapshot_json);
6283
+ if (!should_read_review_decision(snapshot)) return snapshot;
6284
+ const decision = await git_gh_pr_snapshot.pr_get_review_decision(checks.pr_number);
6285
+ return { ...snapshot, review_decision: read_string(decision) };
6286
+ }
6287
+
6288
+ // scripts/gh/git-pr-checks-watch.ts
6289
+ var PR_CHECKS_WATCH_TIMEOUT_MS = 12e4;
6290
+ var WATCH_TIMED_OUT_NOTE = "\u23F1\uFE0F pr checks --watch timed out.";
6291
+ var NO_CHECKS_MESSAGE = "gh api reported no checks on the branch";
6292
+ var CHECKS_FAILED_MESSAGE = "gh api reported a failing check on the branch";
6293
+ function is_awaited_check(check) {
6294
+ return !is_coderabbit_check(check.name) || is_required_check(check.name);
6295
+ }
6296
+ function evaluate_checks_settled(snapshot) {
6297
+ const awaited = snapshot.rollup.filter((check) => is_awaited_check(check));
6298
+ const considered = awaited.length === 0 ? snapshot.rollup : awaited;
6299
+ if (considered.length === 0 || considered.some((check) => check.status === CHECK_STATUS_PENDING)) {
6300
+ return "pending";
6301
+ }
6302
+ return snapshot.rollup.some((check) => check.status === CHECK_STATUS_FAIL) ? "failure" : "success";
6303
+ }
6304
+ function describe_checks_failure(snapshot) {
6305
+ const failed = snapshot.rollup.filter((check) => check.status === CHECK_STATUS_FAIL);
6306
+ return `${CHECKS_FAILED_MESSAGE}: ${failed.map((check) => check.name).join(", ")}`;
6307
+ }
6308
+ var SHOULD_NEVER_READ_REVIEW_DECISION = () => false;
6309
+ var CHECKS_SETTLED_EVALUATOR = {
6310
+ evaluate: evaluate_checks_settled,
6311
+ describe: describe_checks_failure,
6312
+ should_read_review_decision: SHOULD_NEVER_READ_REVIEW_DECISION
6313
+ };
6314
+ async function watch_until_settled(branch_name) {
6315
+ await wait_for_pr_success({
6316
+ branch_name,
6317
+ fetcher: default_fetch_pr_state,
6318
+ interval_ms: CHECK_WAIT_INTERVAL_MS,
6319
+ max_attempts: compute_max_attempts(
6320
+ PR_CHECKS_WATCH_TIMEOUT_MS / SECONDS_TO_MS,
6321
+ CHECK_WAIT_INTERVAL_MS
6322
+ ),
6323
+ required_stable_reads: DEFAULT_STABLE_READS,
6324
+ evaluator: CHECKS_SETTLED_EVALUATOR
6325
+ });
6326
+ }
6327
+ async function fail_when_no_checks(branch_name) {
6328
+ const snapshot = await default_fetch_pr_state(branch_name, SHOULD_NEVER_READ_REVIEW_DECISION);
6329
+ if (snapshot.rollup.length === 0) throw new Error(NO_CHECKS_MESSAGE);
6330
+ }
6331
+ async function pr_checks_watch(branch_name) {
6332
+ try {
6333
+ await watch_until_settled(branch_name);
6334
+ return { timed_out: false };
6335
+ } catch (error) {
6336
+ if (!is_pr_checks_timeout(error)) throw error;
6337
+ await fail_when_no_checks(branch_name);
6338
+ console.info(WATCH_TIMED_OUT_NOTE);
6339
+ return { timed_out: true };
6340
+ }
6341
+ }
6342
+ var git_pr_checks_watch = { pr_checks_watch };
6343
+
6344
+ // scripts/gh/git-gh-command.ts
6345
+ var git_gh_command = {
6346
+ ...git_gh_pr,
6347
+ ...git_gh_pr_auto_merge,
6348
+ pr_checks_watch: git_pr_checks_watch.pr_checks_watch,
6349
+ ...git_gh_issue,
6350
+ ...git_gh_repo
6351
+ };
6352
+
4582
6353
  export {
6354
+ bounded_pool,
4583
6355
  shell_segments,
4584
6356
  bash_triggers,
4585
6357
  issue_cite,
6358
+ PORCELAIN_FLAG,
6359
+ git_spawn_sync,
4586
6360
  git_command,
4587
6361
  git_common_directory,
6362
+ error_text,
4588
6363
  tail_commands,
4589
6364
  filing_cap,
4590
6365
  CONTEXT_CUT_THRESHOLD,
4591
6366
  RECENT_REQUEST_WINDOW,
4592
6367
  agent_session_environment,
4593
6368
  agent_role_profile,
6369
+ cli_flags,
4594
6370
  backlog_budget,
4595
6371
  run_hold,
4596
6372
  run_cut,
4597
6373
  run_tail,
4598
6374
  lane_background,
4599
- ENHANCEMENT_LABEL,
4600
- BREAKING_CHANGE_LABEL,
4601
6375
  IN_PROGRESS_LABEL,
4602
6376
  NEEDS_DECISION_LABEL,
4603
6377
  RUN_SOLO_LABEL,
4604
6378
  RUN_LANE_LABEL,
4605
6379
  NEEDS_HUMAN_REVIEW_LABEL,
4606
6380
  ALREADY_DONE_LABEL,
4607
- BUGFIX_LABEL,
4608
- RELEASE_CLASSIFICATION_NAMES,
4609
6381
  has_any_label,
4610
6382
  has_label_name,
4611
6383
  process_identity,
@@ -4620,14 +6392,13 @@ export {
4620
6392
  process_owner_schema,
4621
6393
  lane_registry,
4622
6394
  run_headless,
4623
- FULL_PAGE_QUERY,
4624
6395
  git_gh_api_path,
4625
- error_text,
4626
6396
  FETCH_TIMEOUT_MS,
4627
- parse_json_array_or_undefined,
4628
6397
  read_json_listing,
4629
6398
  parse_json_object_safe,
4630
- gh_failure,
4631
- has_stderr_field,
4632
- git_gh_exec
6399
+ git_gh_exec,
6400
+ open_issue_schema,
6401
+ git_gh_issue_read,
6402
+ git_gh_pr_read,
6403
+ git_gh_command
4633
6404
  };