@joshuafolkken/kit 1.1471.0 → 1.1514.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 (267) hide show
  1. package/.claude/settings.json +6 -6
  2. package/.claude/skills/epic-commands/SKILL.md +2 -14
  3. package/.claude/skills/epic-commands/epic-bundle.md +9 -5
  4. package/.claude/skills/epic-commands/execution-waves.md +1 -2
  5. package/.claude/skills/verify-ui/SKILL.md +1 -2
  6. package/.claude/skills/workflow-commands/SKILL.md +7 -9
  7. package/.claude/skills/workflow-commands/background-commands.md +3 -7
  8. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -10
  9. package/.claude/skills/workflow-commands/backlogrun-lanes.md +6 -15
  10. package/.claude/skills/workflow-commands/backlogrun-park.md +2 -8
  11. package/.claude/skills/workflow-commands/backlogrun-progress.md +10 -19
  12. package/.claude/skills/workflow-commands/backlogrun-recovery.md +8 -8
  13. package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -10
  14. package/.claude/skills/workflow-commands/backlogrun.md +31 -50
  15. package/.claude/skills/workflow-commands/chain-rule.md +7 -6
  16. package/.claude/skills/workflow-commands/delegation.md +12 -8
  17. package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
  18. package/.claude/skills/workflow-commands/followup.md +3 -6
  19. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
  20. package/.claude/skills/workflow-commands/fullrun.md +4 -6
  21. package/.claude/skills/workflow-commands/halfrun.md +3 -7
  22. package/.claude/skills/workflow-commands/into-target.md +1 -2
  23. package/.claude/skills/workflow-commands/issue-comments.md +3 -3
  24. package/.claude/skills/workflow-commands/issue-scout.md +3 -2
  25. package/.claude/skills/workflow-commands/latest-gate.md +2 -6
  26. package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
  27. package/.claude/skills/workflow-commands/observation-filing.md +0 -14
  28. package/.claude/skills/workflow-commands/observation-ledger.md +4 -9
  29. package/.claude/skills/workflow-commands/pre-gate-cut.md +11 -16
  30. package/.claude/skills/workflow-commands/prerequisite.md +1 -2
  31. package/.claude/skills/workflow-commands/progress-watcher.md +2 -7
  32. package/.claude/skills/workflow-commands/prrun.md +3 -5
  33. package/.claude/skills/workflow-commands/retrospective.md +4 -4
  34. package/.claude/skills/workflow-commands/split-assessment.md +1 -3
  35. package/.claude/skills/workflow-commands/target-repository.md +1 -2
  36. package/.claude/skills/workflow-commands/working-tree-hold.md +2 -4
  37. package/.codex/hooks.json +6 -6
  38. package/.github/actions/setup-node/action.yml +1 -1
  39. package/.github/workflows/ci.yml +1 -1
  40. package/.github/workflows/publish.yml +2 -2
  41. package/CLAUDE.md +1 -1
  42. package/cspell/index.yaml +7 -0
  43. package/dist/CLAUDE.md +1 -1
  44. package/dist/commands/document-section-cli.js +67 -34
  45. package/dist/hooks/batch-guard.js +6 -6
  46. package/dist/hooks/chunk-4KKD2UTK.js +66 -0
  47. package/dist/hooks/{chunk-J47HU5H5.js → chunk-6TS2JD6D.js} +3 -3
  48. package/dist/hooks/chunk-7DSBJVFH.js +50 -0
  49. package/dist/hooks/{chunk-YHBZAJSV.js → chunk-A65Y4YOR.js} +3 -3
  50. package/dist/hooks/{chunk-E7OIKX5Z.js → chunk-APUO6U4J.js} +85 -7
  51. package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
  52. package/dist/hooks/chunk-GP4IEC4O.js +20 -0
  53. package/dist/hooks/{chunk-SHGPANFB.js → chunk-JP45SOCJ.js} +3 -3
  54. package/dist/hooks/{chunk-K4EOONYM.js → chunk-LAOV7NLM.js} +1 -1
  55. package/dist/hooks/chunk-M4HGZV7U.js +83 -0
  56. package/dist/hooks/{chunk-KC5AK2GF.js → chunk-O6GDRNJB.js} +3 -3
  57. package/dist/hooks/{chunk-YYPI7SJZ.js → chunk-PF2NZL2A.js} +5 -5
  58. package/dist/hooks/{chunk-H5HVWYXK.js → chunk-PTICX4OL.js} +3 -4
  59. package/dist/hooks/{chunk-IIGFYXKC.js → chunk-RT27QRJW.js} +581 -1438
  60. package/dist/hooks/{chunk-43ZHT22P.js → chunk-SYOTDYCI.js} +1127 -1089
  61. package/dist/hooks/{chunk-AS74OU3D.js → chunk-TNUMY6NL.js} +1507 -1336
  62. package/dist/hooks/chunk-UPUZF3AN.js +279 -0
  63. package/dist/hooks/{chunk-EUMACUK2.js → chunk-WK6SMBIC.js} +1948 -806
  64. package/dist/hooks/codex-hook-adapter.js +17 -14
  65. package/dist/hooks/duplicate-read-guard.js +7 -7
  66. package/dist/hooks/format-edited.js +5 -4
  67. package/dist/hooks/inputs.json +317 -264
  68. package/dist/hooks/investigation-guard.js +8 -11
  69. package/dist/hooks/pretool-guard.js +16 -13
  70. package/dist/hooks/session-lang.js +370 -25
  71. package/dist/hooks/stop-guard.js +2576 -176
  72. package/dist/josh.js +2124 -277
  73. package/lefthook/base.yml +5 -3
  74. package/package.json +12 -10
  75. package/prompts/collaboration-workflow/file-edits.md +1 -3
  76. package/prompts/collaboration-workflow/gh-rest.md +1 -2
  77. package/prompts/collaboration-workflow/issue-citation.md +2 -4
  78. package/prompts/collaboration-workflow/issue-template.md +10 -2
  79. package/prompts/collaboration-workflow/lane-instructions.md +35 -0
  80. package/prompts/collaboration-workflow/operating-rules.md +2 -6
  81. package/prompts/collaboration-workflow/output-bounds.md +3 -3
  82. package/prompts/collaboration-workflow/overview.md +1 -1
  83. package/prompts/collaboration-workflow/principles.md +14 -8
  84. package/prompts/collaboration-workflow/report-format.md +9 -0
  85. package/prompts/collaboration-workflow/residency.md +12 -8
  86. package/prompts/collaboration-workflow/rule-delivery.md +3 -11
  87. package/prompts/collaboration-workflow/shell-body.md +6 -6
  88. package/prompts/collaboration-workflow/turn-batching.md +5 -5
  89. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  90. package/prompts/collaboration-workflow/wip-cap.md +6 -6
  91. package/prompts/collaboration-workflow.md +1 -0
  92. package/prompts/review.md +4 -5
  93. package/prompts/testing-guide.md +2 -2
  94. package/scripts/agent/agent-argv.ts +20 -4
  95. package/scripts/agent/agent-role-profile.ts +101 -45
  96. package/scripts/agent/agent-safe-text.ts +60 -0
  97. package/scripts/agent/claude-agent-argv.ts +14 -0
  98. package/scripts/agent/codex-agent-argv.ts +2 -0
  99. package/scripts/agent/lane-instructions.ts +144 -0
  100. package/scripts/backlog/backlog-budget.ts +4 -4
  101. package/scripts/backlog/backlog-drive-named.ts +1 -1
  102. package/scripts/backlog/backlog-drive-restore.ts +9 -1
  103. package/scripts/backlog/backlog-occupied.ts +56 -0
  104. package/scripts/backlog/backlog-ready.ts +47 -10
  105. package/scripts/delegation/duplicate-read-guard.ts +2 -2
  106. package/scripts/delegation/investigation-guard.ts +15 -65
  107. package/scripts/delegation/investigation-reads.ts +0 -1
  108. package/scripts/document/document-byte-budget.ts +6 -5
  109. package/scripts/document/document-section-cli.ts +17 -5
  110. package/scripts/document/entry-read-budget.ts +2 -2
  111. package/scripts/document/read-set-cli.ts +4 -1
  112. package/scripts/epic/epic-audit-orphans.ts +5 -8
  113. package/scripts/epic/epic-audit.ts +52 -1
  114. package/scripts/epic/epic-bundle-evidence.ts +49 -26
  115. package/scripts/epic/epic-bundle-referenced.ts +14 -3
  116. package/scripts/epic/epic-bundle.ts +17 -9
  117. package/scripts/followup/git-followup-workflow.ts +19 -1
  118. package/scripts/gate/check-inputs.ts +129 -0
  119. package/scripts/gate/check-reuse.ts +162 -0
  120. package/scripts/gate/core-budget.ts +93 -44
  121. package/scripts/gate/core-wait.ts +130 -0
  122. package/scripts/gate/free-cores.ts +49 -0
  123. package/scripts/gate/gate-ledger.ts +17 -3
  124. package/scripts/gate/gate-plan.ts +23 -1
  125. package/scripts/gate/gate-report.ts +15 -2
  126. package/scripts/gate/gate-run-steps.ts +150 -0
  127. package/scripts/gate/held-mark.ts +55 -0
  128. package/scripts/gate/machine-capacity.ts +70 -11
  129. package/scripts/gate/pre-commit-type-check.ts +14 -1
  130. package/scripts/gate/verification-gate.ts +58 -125
  131. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  132. package/scripts/gh/git-gh-issue-write.ts +24 -2
  133. package/scripts/gh/git-pr.ts +13 -1
  134. package/scripts/git/main-merge-regenerate.ts +78 -0
  135. package/scripts/git/main-merge.ts +14 -5
  136. package/scripts/hooks/batch-guard.ts +5 -8
  137. package/scripts/hooks/pre-commit-lint.ts +91 -0
  138. package/scripts/hooks/pre-push-unit.ts +1 -1
  139. package/scripts/hooks/pretool-guard.ts +27 -52
  140. package/scripts/hooks/reserved-run.ts +1 -0
  141. package/scripts/hooks/run-hook.sh +10 -12
  142. package/scripts/hooks/stop-actions.ts +204 -0
  143. package/scripts/hooks/stop-guard.ts +62 -21
  144. package/scripts/init/hook-launch.ts +12 -7
  145. package/scripts/issue/expected-effect-lint.ts +59 -0
  146. package/scripts/issue/issue-cite-cli.ts +35 -15
  147. package/scripts/issue/issue-classification.ts +1 -1
  148. package/scripts/issue/issue-lint-cli.ts +5 -3
  149. package/scripts/issue/issue-scout-cli.ts +19 -14
  150. package/scripts/issue/markdown-section.ts +19 -5
  151. package/scripts/josh/command-usage.ts +110 -0
  152. package/scripts/josh/josh-command-map.ts +2 -0
  153. package/scripts/josh/josh-command-types.ts +55 -9
  154. package/scripts/josh/josh-commands-ai.ts +13 -0
  155. package/scripts/josh/josh-commands-development.ts +1 -1
  156. package/scripts/josh/josh-commands-hooks.ts +6 -0
  157. package/scripts/josh/josh-commands-lane.ts +10 -1
  158. package/scripts/josh/josh-commands-workflow.ts +5 -0
  159. package/scripts/josh/josh-logic.ts +6 -15
  160. package/scripts/josh/josh-usage-automation.ts +121 -0
  161. package/scripts/josh/josh-usage-backlog.ts +180 -0
  162. package/scripts/josh/josh-usage-developer.ts +230 -0
  163. package/scripts/josh/josh-usage-hooks.ts +38 -0
  164. package/scripts/josh/josh-usage-issue.ts +205 -0
  165. package/scripts/josh/josh-usage-review.ts +101 -0
  166. package/scripts/josh/josh-usage-run.ts +252 -0
  167. package/scripts/josh/josh-usage-session.ts +238 -0
  168. package/scripts/josh/josh-usage-workflow.ts +212 -0
  169. package/scripts/josh/josh.ts +14 -5
  170. package/scripts/josh/package-directory.ts +20 -0
  171. package/scripts/lane/lane-capacity.ts +8 -2
  172. package/scripts/lane/lane-check-stats.ts +56 -0
  173. package/scripts/lane/lane-child-invocation.ts +35 -4
  174. package/scripts/lane/lane-dispatch.ts +3 -3
  175. package/scripts/lane/lane-guard-policy.ts +2 -2
  176. package/scripts/lane/lane-handoff.ts +8 -1
  177. package/scripts/lane/lane-install.ts +12 -4
  178. package/scripts/lane/lane-ledger.ts +43 -1
  179. package/scripts/lane/lane-limit-cli.ts +8 -9
  180. package/scripts/lane/lane-measure-cli.ts +2 -0
  181. package/scripts/lane/lane-preserve.ts +29 -1
  182. package/scripts/lane/lane-registry.ts +9 -7
  183. package/scripts/lane/lane-restart.ts +5 -26
  184. package/scripts/lane/lane-session-action.ts +146 -0
  185. package/scripts/lane/lane-session-broker.ts +57 -138
  186. package/scripts/lane/lane-withdraw-cli.ts +71 -0
  187. package/scripts/lane/lane-withdraw.ts +182 -0
  188. package/scripts/lint/format.ts +4 -4
  189. package/scripts/lint/lint-parallel.ts +10 -3
  190. package/scripts/lint/lint-related-scope.ts +4 -1
  191. package/scripts/notify/telegram-test.ts +10 -5
  192. package/scripts/report/report-format-reference.ts +2 -0
  193. package/scripts/review/acceptance-map-check.ts +61 -0
  194. package/scripts/review/acceptance-map.ts +205 -0
  195. package/scripts/review/review-brief-cli.ts +1 -1
  196. package/scripts/rules/agent-model.ts +59 -0
  197. package/scripts/rules/delivered-rules.ts +11 -2
  198. package/scripts/rules/filing-cap.ts +1 -0
  199. package/scripts/rules/firing-point.ts +6 -5
  200. package/scripts/rules/guard-remedy.ts +117 -0
  201. package/scripts/rules/issue-comments.ts +67 -6
  202. package/scripts/rules/last-prompt.ts +50 -5
  203. package/scripts/rules/oracle-list-cli.ts +7 -0
  204. package/scripts/rules/piped-verification.ts +3 -1
  205. package/scripts/rules/rule-list.ts +4 -1
  206. package/scripts/rules/rule-registry-rows.ts +10 -0
  207. package/scripts/rules/rule-registry.ts +7 -4
  208. package/scripts/rules/rule-rewrite.ts +114 -0
  209. package/scripts/rules/rule-value.ts +7 -1
  210. package/scripts/rules/shell-segments.ts +15 -0
  211. package/scripts/rules/stop-decision.ts +77 -0
  212. package/scripts/rules/stop-rules.ts +147 -47
  213. package/scripts/rules/tail-commands.ts +15 -1
  214. package/scripts/run/board/run-board-cli.ts +2 -0
  215. package/scripts/run/board/run-board-closed.ts +8 -2
  216. package/scripts/run/board/run-board-github.ts +9 -1
  217. package/scripts/run/board/run-board-header.ts +4 -1
  218. package/scripts/run/board/run-board-labels.ts +2 -0
  219. package/scripts/run/board/run-board-layout.ts +1 -1
  220. package/scripts/run/board/run-board-read.ts +16 -0
  221. package/scripts/run/board/run-board-reason.ts +10 -1
  222. package/scripts/run/board/run-board-render-legend.ts +1 -0
  223. package/scripts/run/board/run-board-render-slot.ts +1 -1
  224. package/scripts/run/board/run-board-render-waits.ts +41 -0
  225. package/scripts/run/board/run-board-render.ts +6 -1
  226. package/scripts/run/board/run-board-row-yield.ts +1 -0
  227. package/scripts/run/board/run-board-state.ts +5 -0
  228. package/scripts/run/board/run-board-status.ts +72 -21
  229. package/scripts/run/board/run-board-tick.ts +5 -1
  230. package/scripts/run/cut/run-cut-cli.ts +3 -2
  231. package/scripts/run/cut/run-cut-report.ts +16 -0
  232. package/scripts/run/detached-launch.ts +23 -4
  233. package/scripts/run/entry/run-entry-cli.ts +8 -1
  234. package/scripts/run/event/run-event-render.ts +1 -0
  235. package/scripts/run/event/run-event-scope.ts +19 -1
  236. package/scripts/run/event/run-event-stream-emit.ts +13 -6
  237. package/scripts/run/event/run-event-stream.ts +4 -0
  238. package/scripts/run/hold/run-hold-session.ts +46 -0
  239. package/scripts/run/hold/run-hold.ts +7 -1
  240. package/scripts/run/launch-priority.ts +32 -0
  241. package/scripts/run/merge/run-merge-cli.ts +16 -65
  242. package/scripts/run/merge/run-merge-signals.ts +99 -0
  243. package/scripts/run/merge/run-merge-steps.ts +11 -1
  244. package/scripts/run/merge/run-merge.ts +21 -9
  245. package/scripts/run/run-liveness.ts +1 -1
  246. package/scripts/run/run-poll-hook.ts +157 -0
  247. package/scripts/run/run-step-cli.ts +22 -15
  248. package/scripts/run/run-step-section.ts +85 -0
  249. package/scripts/run/ship/run-ship-cli.ts +5 -12
  250. package/scripts/run/ship/run-ship-detach.ts +9 -0
  251. package/scripts/run/ship/run-ship-next.ts +10 -2
  252. package/scripts/run/ship/run-ship-overlap.ts +195 -0
  253. package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
  254. package/scripts/run/ship/run-ship-preflight.ts +28 -2
  255. package/scripts/run/ship/run-ship-return.ts +31 -7
  256. package/scripts/run/ship/run-ship-review-steps.ts +12 -10
  257. package/scripts/run/ship/run-ship-steps.ts +1 -1
  258. package/scripts/run/ship/run-ship-stop-context.ts +206 -0
  259. package/scripts/run/ship/run-ship-sync.ts +14 -6
  260. package/scripts/test/test-e2e-guard.ts +30 -5
  261. package/scripts/test/test-unit-guard.ts +16 -1
  262. package/scripts/test/unit-worker-share.ts +26 -4
  263. package/scripts/time-runtime/time-shell.ts +29 -4
  264. package/scripts/time-runtime/time-spans.ts +1 -0
  265. package/scripts/time-runtime/time-tool-call.ts +7 -0
  266. package/templates/gitignore +6 -2
  267. package/dist/hooks/chunk-K4LMBQC2.js +0 -68
@@ -1,60 +1,105 @@
1
1
  #!/usr/bin/env tsx
2
2
  import {
3
+ IN_PROGRESS_LABEL,
4
+ PROCESS_ALIVE,
5
+ PROCESS_NONE,
6
+ PROCESS_UNKNOWN,
3
7
  RUN_LANE_LABEL,
4
8
  RUN_SOLO_LABEL,
9
+ bounded_pool,
5
10
  filing_cap,
6
11
  gh_spawn,
7
12
  git_gh_command,
13
+ git_gh_issue_read,
14
+ git_gh_repo,
8
15
  has_label_name,
9
- issue_citation,
16
+ issue_state,
17
+ label_name_of,
10
18
  lane_background,
11
19
  lane_handoff,
12
20
  lane_park,
21
+ pre_gate_cut,
13
22
  repo_party,
14
23
  run_cut,
15
- run_hold
16
- } from "./chunk-43ZHT22P.js";
24
+ run_cut_handoff,
25
+ run_liveness,
26
+ tail_commands
27
+ } from "./chunk-SYOTDYCI.js";
28
+ import {
29
+ LEDGER_FILE_EXTENSION,
30
+ LEGACY_LEDGER_FILE,
31
+ LEGACY_OBSERVATION_LEDGER_DIRECTORY,
32
+ LEGACY_OBSERVATION_LEDGER_PATHS,
33
+ MIGRATION_CLAIM_SUFFIX,
34
+ OBSERVATION_LEDGER_DIRECTORY,
35
+ observation_ledger
36
+ } from "./chunk-4KKD2UTK.js";
17
37
  import {
18
38
  agent_headless,
19
- agent_role_profile,
39
+ git_gh_api_path,
40
+ git_gh_exec,
41
+ git_worktree,
42
+ has_stderr_field,
43
+ issue_citation,
20
44
  issue_cite,
45
+ lane_child_invocation,
46
+ lane_reap,
21
47
  lane_registry,
48
+ parse_json_object_or_undefined,
49
+ process_tools,
50
+ repository_lock,
22
51
  run_event_stream,
23
52
  run_event_stream_emit,
24
53
  run_headless,
54
+ run_hold,
55
+ run_preflight,
56
+ session_cite,
25
57
  session_language
26
- } from "./chunk-EUMACUK2.js";
58
+ } from "./chunk-WK6SMBIC.js";
27
59
  import {
28
60
  timed_fetch
29
61
  } from "./chunk-723HKZO5.js";
30
- import "./chunk-CDY4NFQG.js";
62
+ import {
63
+ error_text
64
+ } from "./chunk-CDY4NFQG.js";
65
+ import {
66
+ agent_role_profile
67
+ } from "./chunk-UPUZF3AN.js";
31
68
  import {
32
69
  hook_decision
33
- } from "./chunk-J47HU5H5.js";
70
+ } from "./chunk-6TS2JD6D.js";
34
71
  import {
35
72
  time_density_hook,
36
73
  time_hook_transcript,
37
74
  time_reported_failure
38
- } from "./chunk-AS74OU3D.js";
75
+ } from "./chunk-TNUMY6NL.js";
39
76
  import {
77
+ josh_environment_file,
40
78
  lane_child_marker
41
- } from "./chunk-K4EOONYM.js";
79
+ } from "./chunk-LAOV7NLM.js";
42
80
  import {
43
81
  backlog_budget,
82
+ cli_flags,
83
+ file_reader,
84
+ git_command,
85
+ git_spawn,
86
+ issue_label_schema,
44
87
  issue_number_shape,
45
88
  json_value,
89
+ lane_paths,
46
90
  process_identity,
47
91
  process_owner_schema,
48
92
  run_carry,
49
93
  run_invocation
50
- } from "./chunk-E7OIKX5Z.js";
94
+ } from "./chunk-APUO6U4J.js";
51
95
  import {
96
+ PROBE_TIMEOUT_MS,
52
97
  stamp_file
53
98
  } from "./chunk-DGT54IH5.js";
54
99
 
55
100
  // scripts/hooks/stop-guard.ts
56
101
  import { text } from "node:stream/consumers";
57
- import { fileURLToPath } from "node:url";
102
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
58
103
 
59
104
  // scripts/josh/josh-run.ts
60
105
  import { execa } from "execa";
@@ -83,8 +128,8 @@ async function carry_target() {
83
128
  return repository === void 0 ? void 0 : run_carry.carry_path(repository);
84
129
  }
85
130
  function live_carry(target) {
86
- const read = run_carry.read_carry(target);
87
- return read.kind === "carried" ? read.carry : void 0;
131
+ const read2 = run_carry.read_carry(target);
132
+ return read2.kind === "carried" ? read2.carry : void 0;
88
133
  }
89
134
  async function read_override() {
90
135
  const target = await carry_target();
@@ -152,8 +197,10 @@ async function lane_limit(environment = process.env, read_override2 = lane_limit
152
197
  function seated_limit(limit) {
153
198
  return Math.min(limit, lane_seed_policy.LAST_LANE_SEAT);
154
199
  }
155
- function free_lanes(limit, occupied) {
156
- return Math.max(NO_LANES, seated_limit(limit) - occupied);
200
+ function free_lanes(limit, occupied, seated = occupied) {
201
+ const by_limit = seated_limit(limit) - occupied;
202
+ const by_seats = lane_seed_policy.LAST_LANE_SEAT - seated;
203
+ return Math.max(NO_LANES, Math.min(by_limit, by_seats));
157
204
  }
158
205
  var lane_capacity = {
159
206
  LANE_LIMIT_KEY,
@@ -163,6 +210,304 @@ var lane_capacity = {
163
210
  seated_limit
164
211
  };
165
212
 
213
+ // scripts/backlog/backlog-drive-restore.ts
214
+ var ISSUE_IN_EVENT = /#([1-9]\d*)\b/u;
215
+ var SETTLED_KINDS = /* @__PURE__ */ new Set([
216
+ run_event_stream.EVENT_KIND.MERGE,
217
+ run_event_stream.EVENT_KIND.PARK,
218
+ run_event_stream.EVENT_KIND.SPLIT,
219
+ run_event_stream.EVENT_KIND.WITHDRAW,
220
+ run_event_stream.EVENT_KIND.OUTAGE
221
+ ]);
222
+ var DONE_KINDS = /* @__PURE__ */ new Set([
223
+ run_event_stream.EVENT_KIND.MERGE,
224
+ run_event_stream.EVENT_KIND.PARK,
225
+ run_event_stream.EVENT_KIND.SPLIT,
226
+ run_event_stream.EVENT_KIND.WITHDRAW
227
+ ]);
228
+ function issue_of(event) {
229
+ return ISSUE_IN_EVENT.exec(event.text)?.[1];
230
+ }
231
+ function update_active(active, event) {
232
+ const issue = issue_of(event);
233
+ if (issue === void 0) return;
234
+ if (event.kind === run_event_stream.EVENT_KIND.CHILD_LAUNCH) active.add(issue);
235
+ if (SETTLED_KINDS.has(event.kind)) active.delete(issue);
236
+ }
237
+ function active_issues(events) {
238
+ const active = /* @__PURE__ */ new Set();
239
+ for (const event of events) update_active(active, event);
240
+ return active;
241
+ }
242
+ function settled_issues(events) {
243
+ const settled = events.filter((event) => DONE_KINDS.has(event.kind));
244
+ return new Set(
245
+ settled.map((event) => issue_of(event)).filter((issue) => issue !== void 0).map(Number)
246
+ );
247
+ }
248
+ function parked_issues(events) {
249
+ return new Set(
250
+ events.filter((event) => event.kind === run_event_stream.EVENT_KIND.PARK).map((event) => issue_of(event)).filter((issue) => issue !== void 0).map(Number)
251
+ );
252
+ }
253
+ function mentioned_issues(events) {
254
+ const tracked = events.filter(
255
+ (event) => event.kind === run_event_stream.EVENT_KIND.CHILD_LAUNCH || SETTLED_KINDS.has(event.kind)
256
+ );
257
+ return new Set(tracked.map((event) => issue_of(event)).filter((issue) => issue !== void 0));
258
+ }
259
+ function restore(lanes, events, merged_issues, dispatched = []) {
260
+ const active = active_issues(events);
261
+ const mentioned = mentioned_issues(events);
262
+ const carried2 = new Set(dispatched.filter((issue) => !mentioned.has(issue)));
263
+ return lanes.filter(
264
+ (issue) => (active.has(issue) || carried2.has(issue)) && !merged_issues.includes(Number(issue))
265
+ );
266
+ }
267
+ var backlog_drive_restore = {
268
+ active_issues,
269
+ mentioned_issues,
270
+ restore,
271
+ settled_issues,
272
+ parked_issues
273
+ };
274
+
275
+ // scripts/lane/lane-await.ts
276
+ import { spawnSync } from "node:child_process";
277
+ import { setTimeout as sleep } from "node:timers/promises";
278
+ import { parseArgs } from "node:util";
279
+ var RECONFIRM_MS = 15e3;
280
+ var DEFAULT_POLL_MS = 5e3;
281
+ var NEVER_APPEARED_TIMEOUT_MS = 6e5;
282
+ var MS_PER_SECOND = 1e3;
283
+ var PROCESS_FOUND = 0;
284
+ var PROCESS_NOT_FOUND = 1;
285
+ var PGREP_COMMAND = "pgrep";
286
+ function process_trace_default(issue) {
287
+ const pattern = lane_child_invocation.process_pattern(issue);
288
+ const result = spawnSync(PGREP_COMMAND, ["-f", pattern], {
289
+ encoding: "utf8",
290
+ timeout: PROBE_TIMEOUT_MS
291
+ });
292
+ process_tools.throw_if_missing(PGREP_COMMAND, result.error);
293
+ if (result.status === PROCESS_FOUND || lane_handoff.is_ship_running(issue, process.cwd())) {
294
+ return PROCESS_ALIVE;
295
+ }
296
+ return result.status === PROCESS_NOT_FOUND ? PROCESS_NONE : PROCESS_UNKNOWN;
297
+ }
298
+ function is_process_running_default(issue) {
299
+ return process_trace_default(issue) === PROCESS_ALIVE;
300
+ }
301
+ async function is_settled_default(issue) {
302
+ return await run_liveness.read_child_settled(issue) === true;
303
+ }
304
+ function read_await_args(rest) {
305
+ try {
306
+ return parseArgs({
307
+ args: [...rest],
308
+ allowPositionals: true,
309
+ strict: true,
310
+ options: { owner: { type: "string" } }
311
+ });
312
+ } catch {
313
+ return void 0;
314
+ }
315
+ }
316
+ function is_valid(raw) {
317
+ const { owner } = raw.values;
318
+ const numbers = owner === void 0 ? raw.positionals : [...raw.positionals, owner];
319
+ return raw.positionals.length > 0 && numbers.every((value) => issue_number_shape.is_issue_number(value));
320
+ }
321
+ function parse_arguments(rest) {
322
+ const raw = read_await_args(rest);
323
+ if (raw === void 0 || !is_valid(raw)) return void 0;
324
+ const { owner } = raw.values;
325
+ return { issues: raw.positionals, owner: owner === void 0 ? void 0 : Number(owner) };
326
+ }
327
+ function make_initial_state(is_settled) {
328
+ return { appeared: false, disappeared_at: void 0, first_polled_at: void 0, is_settled };
329
+ }
330
+ async function make_states(issues, is_settled) {
331
+ const settled = await Promise.all(issues.map(async (issue) => await is_settled(issue)));
332
+ return new Map(issues.map((issue, index) => [issue, make_initial_state(settled[index] === true)]));
333
+ }
334
+ function resolve_timing(options) {
335
+ return {
336
+ poll_ms: options.poll_ms ?? DEFAULT_POLL_MS,
337
+ reconfirm_ms: options.reconfirm_ms ?? RECONFIRM_MS,
338
+ never_appeared_timeout_ms: options.never_appeared_timeout_ms ?? NEVER_APPEARED_TIMEOUT_MS
339
+ };
340
+ }
341
+ function resolve_options(options) {
342
+ return {
343
+ is_running: options.is_running ?? is_process_running_default,
344
+ is_settled: options.is_settled ?? is_settled_default,
345
+ now: options.now ?? Date.now,
346
+ sleep: options.sleep ?? sleep,
347
+ ...resolve_timing(options)
348
+ };
349
+ }
350
+ function update_liveness(state, is_live) {
351
+ if (!is_live) return;
352
+ state.appeared = true;
353
+ state.disappeared_at = void 0;
354
+ }
355
+ function check_not_appeared(issue, state, now_ms, timeout_ms) {
356
+ if (state.is_settled === true) return issue;
357
+ state.first_polled_at ??= now_ms;
358
+ const elapsed_ms = now_ms - state.first_polled_at;
359
+ if (elapsed_ms >= timeout_ms) {
360
+ throw new Error(
361
+ `lane child ${issue} never appeared within ${String(timeout_ms / MS_PER_SECOND)}s`
362
+ );
363
+ }
364
+ return void 0;
365
+ }
366
+ function check_issue(issue, state, now_ms, config) {
367
+ const is_live = config.is_running(issue);
368
+ update_liveness(state, is_live);
369
+ if (is_live) return void 0;
370
+ if (!state.appeared) {
371
+ return check_not_appeared(issue, state, now_ms, config.never_appeared_timeout_ms);
372
+ }
373
+ if (state.disappeared_at === void 0) {
374
+ state.disappeared_at = now_ms;
375
+ return void 0;
376
+ }
377
+ return now_ms - state.disappeared_at >= config.reconfirm_ms ? issue : void 0;
378
+ }
379
+ function check_any(states, now_ms, config) {
380
+ for (const [issue, state] of states) {
381
+ const result = check_issue(issue, state, now_ms, config);
382
+ if (result !== void 0) return result;
383
+ }
384
+ return void 0;
385
+ }
386
+ async function wait_for_any(issues, options = {}) {
387
+ const config = resolve_options(options);
388
+ const states = await make_states(issues, config.is_settled);
389
+ for (; ; ) {
390
+ const completed = check_any(states, config.now(), config);
391
+ if (completed !== void 0) return completed;
392
+ await config.sleep(config.poll_ms);
393
+ }
394
+ }
395
+ var lane_await = {
396
+ NEVER_APPEARED_TIMEOUT_MS,
397
+ RECONFIRM_MS,
398
+ check_issue,
399
+ is_process_running_default,
400
+ parse_arguments,
401
+ process_trace_default,
402
+ wait_for_any
403
+ };
404
+
405
+ // scripts/lane/lane-spent.ts
406
+ var STATE_FIELD = "state";
407
+ var CLOSED_STATE = "CLOSED";
408
+ async function read_issue_state(issue) {
409
+ const json = await git_gh_issue_read.issue_view_json(issue, STATE_FIELD);
410
+ return json === void 0 ? void 0 : issue_state.parse_issue_state(json)?.state;
411
+ }
412
+ async function read_unpushed_commits(branch) {
413
+ try {
414
+ return await git_command.commit_count_unpushed(branch);
415
+ } catch {
416
+ return void 0;
417
+ }
418
+ }
419
+ async function read_in_flight() {
420
+ return backlog_drive_restore.active_issues(await run_event_stream_emit.current_events());
421
+ }
422
+ var LIVE_PROBES = {
423
+ process_trace: lane_await.process_trace_default,
424
+ issue_state: read_issue_state,
425
+ in_flight: read_in_flight,
426
+ is_tree_dirty: run_hold.is_tree_dirty,
427
+ unpushed_commits: read_unpushed_commits
428
+ };
429
+ function trace_of(lane, probes) {
430
+ return lane.is_stranded ? PROCESS_NONE : probes.process_trace(lane.issue);
431
+ }
432
+ async function child_reason(lane, probes) {
433
+ const trace = probes.process_trace(lane.issue);
434
+ if (trace === PROCESS_NONE) return void 0;
435
+ return trace === PROCESS_ALIVE ? "a child is still working in it" : "whether a child is working in it could not be read";
436
+ }
437
+ async function issue_reason(lane, probes) {
438
+ const state = await probes.issue_state(lane.issue);
439
+ if (state === CLOSED_STATE) return void 0;
440
+ return state === void 0 ? "its issue could not be read" : "its issue is still open";
441
+ }
442
+ async function run_reason(lane, probes) {
443
+ const in_flight = await probes.in_flight();
444
+ return in_flight.has(lane.issue) ? "the running backlogrun has not collected it yet (`pnpm josh run:merge` does)" : void 0;
445
+ }
446
+ async function tree_reason(lane, probes) {
447
+ return await probes.is_tree_dirty(lane.directory) ? "it has uncommitted changes" : void 0;
448
+ }
449
+ async function commit_reason(lane, probes) {
450
+ const count = await probes.unpushed_commits(lane.branch);
451
+ if (count === 0) return void 0;
452
+ return count === void 0 ? "its commits could not be counted" : `it has ${String(count)} commit(s) that are on no remote`;
453
+ }
454
+ var CHECKS = [
455
+ child_reason,
456
+ issue_reason,
457
+ run_reason,
458
+ tree_reason,
459
+ commit_reason
460
+ ];
461
+ async function kept_reason(lane, probes = LIVE_PROBES) {
462
+ const reasons = await Promise.all(CHECKS.map(async (check) => await check(lane, probes)));
463
+ return reasons.find((reason) => reason !== void 0);
464
+ }
465
+ async function sort_lanes(lanes, probes = LIVE_PROBES) {
466
+ const live = lanes.filter((lane) => !lane.is_stranded);
467
+ const reasons = await Promise.all(live.map(async (lane) => await kept_reason(lane, probes)));
468
+ const sorted = { spent: [], kept: [] };
469
+ for (const [index, lane] of live.entries()) {
470
+ const reason = reasons[index];
471
+ if (reason === void 0) sorted.spent.push(lane);
472
+ else sorted.kept.push({ issue: lane.issue, reason });
473
+ }
474
+ return sorted;
475
+ }
476
+ function issues_traced(traced, trace) {
477
+ return traced.filter(([, answer2]) => answer2 === trace).map(([issue]) => issue);
478
+ }
479
+ function traced_of(lanes, probes) {
480
+ return lanes.map((lane) => [lane.issue, trace_of(lane, probes)]);
481
+ }
482
+ async function lanes_in_use(probes = LIVE_PROBES) {
483
+ const traced = traced_of(await lane_registry.list_lanes(), probes);
484
+ return {
485
+ working: issues_traced(traced, PROCESS_ALIVE),
486
+ unread: issues_traced(traced, PROCESS_UNKNOWN)
487
+ };
488
+ }
489
+ function idle_lanes(lanes, probes = LIVE_PROBES) {
490
+ return issues_traced(traced_of(lanes, probes), PROCESS_NONE);
491
+ }
492
+ var lane_spent = { idle_lanes, kept_reason, lanes_in_use, sort_lanes };
493
+
494
+ // scripts/backlog/backlog-occupied.ts
495
+ function owned_issues(read2) {
496
+ const { invocation, events } = read2;
497
+ const is_only = invocation !== void 0 && run_invocation.has_only(invocation);
498
+ if (!is_only || events === void 0) return void 0;
499
+ const named = (run_invocation.issue_numbers(invocation) ?? []).map(String);
500
+ return /* @__PURE__ */ new Set([...named, ...backlog_drive_restore.mentioned_issues(events)]);
501
+ }
502
+ function occupied_lanes(lanes, read2) {
503
+ const live = lanes.filter((lane) => !lane.is_stranded);
504
+ const owned = owned_issues(read2);
505
+ if (owned === void 0) return live;
506
+ const idle = new Set(read2.idle(live));
507
+ return live.filter((lane) => owned.has(lane.issue) || !idle.has(lane.issue));
508
+ }
509
+ var backlog_occupied = { occupied_lanes };
510
+
166
511
  // scripts/epic/epic-triage.ts
167
512
  var TRIAGE_VERDICT = "triage";
168
513
  function has_triage_label(labels) {
@@ -240,16 +585,33 @@ var backlog_stalled = {
240
585
  // scripts/backlog/backlog-ready.ts
241
586
  var NO_FREE = 0;
242
587
  var NONE2 = 0;
243
- async function live_lane_count() {
244
- return lane_registry.live_count(await lane_registry.list_lanes());
588
+ async function occupied_read() {
589
+ const carry = await run_headless.current_carry();
590
+ return {
591
+ invocation: carry?.invocation,
592
+ events: carry === void 0 ? [] : await run_event_stream_emit.scoped_current_events(),
593
+ idle: lane_spent.idle_lanes
594
+ };
595
+ }
596
+ async function occupied_lanes2(lanes) {
597
+ const open = lanes ?? await lane_registry.list_lanes();
598
+ return backlog_occupied.occupied_lanes(open, await occupied_read());
599
+ }
600
+ async function lane_use(lanes) {
601
+ const open = lanes ?? await lane_registry.list_lanes();
602
+ const occupied = await occupied_lanes2(open);
603
+ return { occupied: occupied.length, seated: open.filter((lane) => !lane.is_stranded).length };
604
+ }
605
+ function free_of(limit, use) {
606
+ return lane_capacity.free_lanes(limit, use.occupied, use.seated);
245
607
  }
246
608
  async function free_lanes_beside(lanes) {
247
609
  const limit = await lane_capacity.lane_limit();
248
610
  if (limit.kind !== "limit") return void 0;
249
- return lane_capacity.free_lanes(limit.limit, lane_registry.live_count(lanes));
611
+ return free_of(limit.limit, await lane_use(lanes));
250
612
  }
251
613
  async function free_under(limit) {
252
- return lane_capacity.free_lanes(limit, await live_lane_count());
614
+ return free_of(limit, await lane_use());
253
615
  }
254
616
  async function free_lane_count() {
255
617
  return await free_lanes_beside(await lane_registry.list_lanes()) ?? NO_FREE;
@@ -278,8 +640,8 @@ async function read_ready(ports = DEFAULT_PORTS) {
278
640
  }
279
641
  function ready_line(reading, slug) {
280
642
  if (reading.issues.length === NONE2 || reading.free_lanes <= NO_FREE) return void 0;
281
- const cite = issue_cite.citer(slug, /* @__PURE__ */ new Map());
282
- const issues = reading.issues.map((issue) => cite(issue)).join(" ");
643
+ const cite3 = issue_cite.citer(slug, /* @__PURE__ */ new Map());
644
+ const issues = reading.issues.map((issue) => cite3(issue)).join(" ");
283
645
  return `ready ${issues} \xB7 free lanes ${String(reading.free_lanes)}`;
284
646
  }
285
647
  async function parent_ready_line(ports, is_parent, read_repo) {
@@ -310,7 +672,8 @@ var backlog_ready = {
310
672
  print_offer_hint,
311
673
  free_lane_count,
312
674
  free_lanes_beside,
313
- live_lane_count,
675
+ lane_use,
676
+ occupied_lanes: occupied_lanes2,
314
677
  drive_free_lane_count,
315
678
  drains_pool,
316
679
  print_ready_line,
@@ -663,46 +1026,400 @@ async function run_stall_check(ready = backlog_ready.DEFAULT_PORTS) {
663
1026
  }
664
1027
  var backlog_stalled_detect = { detect_and_report, run_stall_check };
665
1028
 
666
- // scripts/rules/last-prompt.ts
1029
+ // scripts/issue/issue-cite-cli.ts
1030
+ import { fileURLToPath } from "node:url";
667
1031
  import { z as z2 } from "zod";
668
- var WORKFLOW_KEYWORDS = /* @__PURE__ */ new Set([
669
- "kickoff",
670
- "fullrun",
671
- "halfrun",
672
- "prrun",
673
- "backlogrun"
674
- ]);
675
- var NO_PROMPT = "";
676
- var HUMAN_ORIGIN = "human";
677
- var KEYWORD_BEFORE_ARGUMENT = -2;
678
- var PROMPT_LINE_SCHEMA = z2.object({
679
- isMeta: z2.boolean().nullish(),
680
- origin: z2.object({ kind: z2.string().nullish() }).nullish(),
681
- message: z2.object({ content: z2.unknown() })
682
- });
683
- function is_person(line) {
684
- if (line.isMeta === true) return false;
685
- return line.origin === null || line.origin === void 0 || line.origin.kind === HUMAN_ORIGIN;
1032
+ var SUCCESS_EXIT_CODE = 0;
1033
+ var FAILURE_EXIT_CODE = 1;
1034
+ var ARGV_OFFSET = 2;
1035
+ var READ_CONCURRENCY = 8;
1036
+ var TITLE_FIELD = "title";
1037
+ var LINE_SEPARATOR = "\n";
1038
+ var USAGE = "Usage: josh issue:cite <issue-number|owner/repo#N> [ ... ] [--repo <owner/repo>]";
1039
+ var OPTIONS = { repo: { type: "string" } };
1040
+ var title_schema = z2.looseObject({ title: z2.string() });
1041
+ function parse_title(json) {
1042
+ const title = json_value.parse_with(json, title_schema)?.title.trim();
1043
+ return title === "" ? void 0 : title;
1044
+ }
1045
+ function to_line(target, slug, read2) {
1046
+ if (read2.kind === "missing") return { kind: "fail", line: issue_cite.missing_line(target) };
1047
+ if (read2.kind !== "read") return { kind: "fail", line: issue_cite.unreadable_line(target) };
1048
+ const title = parse_title(read2.json);
1049
+ if (title === void 0) return { kind: "fail", line: issue_cite.unreadable_line(target) };
1050
+ return { kind: "ok", line: issue_cite.citation_line(slug, target.number, title) };
1051
+ }
1052
+ async function cite_target(target, local_slug) {
1053
+ const slug = target.repo ?? local_slug;
1054
+ if (slug === void 0) return { kind: "fail", line: issue_cite.no_repo_line(target) };
1055
+ const read2 = await git_gh_command.issue_view_json_classified(
1056
+ target.number,
1057
+ TITLE_FIELD,
1058
+ target.repo
1059
+ );
1060
+ return to_line(target, slug, read2);
1061
+ }
1062
+ function parse_targets(positionals, default_repo) {
1063
+ const targets = [];
1064
+ for (const token of positionals) {
1065
+ const target = issue_cite.parse_target(token, default_repo);
1066
+ if (target === void 0) return void 0;
1067
+ targets.push(target);
1068
+ }
1069
+ return targets.length === 0 ? void 0 : targets;
686
1070
  }
687
- function person_text(line) {
688
- const parsed = PROMPT_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line));
689
- if (!parsed.success || !is_person(parsed.data)) return void 0;
690
- return time_reported_failure.result_text(parsed.data.message.content);
1071
+ async function local_slug_for(targets) {
1072
+ const has_bare_target = targets.some((target) => target.repo === void 0);
1073
+ return has_bare_target ? await git_gh_command.repo_get_name_with_owner() : void 0;
691
1074
  }
692
- function is_invocation(prompt) {
693
- const words = prompt.trim().split(/\s+/u);
694
- return [words.at(0), words.at(KEYWORD_BEFORE_ARGUMENT)].some(
695
- (word) => WORKFLOW_KEYWORDS.has(word ?? NO_PROMPT)
1075
+ function split_results(results) {
1076
+ return {
1077
+ lines: results.filter((result) => result.kind === "ok").map((result) => result.line),
1078
+ failures: results.filter((result) => result.kind === "fail").map((result) => result.line)
1079
+ };
1080
+ }
1081
+ function report2(cited) {
1082
+ if (cited.lines.length > 0) console.info(cited.lines.join(LINE_SEPARATOR));
1083
+ for (const failure of cited.failures) console.error(failure);
1084
+ return cited.failures.length === 0 ? SUCCESS_EXIT_CODE : FAILURE_EXIT_CODE;
1085
+ }
1086
+ function read_targets(args) {
1087
+ const parsed = cli_flags.arguments_of(args, OPTIONS);
1088
+ if (parsed === void 0) return void 0;
1089
+ return parse_targets(parsed.positionals, parsed.values.repo);
1090
+ }
1091
+ async function cite(args) {
1092
+ const targets = read_targets(args);
1093
+ if (targets === void 0) return void 0;
1094
+ const local_slug = await local_slug_for(targets);
1095
+ const results = await bounded_pool.bounded_map(
1096
+ targets,
1097
+ READ_CONCURRENCY,
1098
+ async (target) => await cite_target(target, local_slug)
696
1099
  );
1100
+ return split_results(results);
697
1101
  }
698
- function prompt_line_text(line) {
699
- return filing_cap.is_prompt_line(line) ? person_text(line) : void 0;
1102
+ async function run(args) {
1103
+ const cited = await cite(args);
1104
+ if (cited === void 0) {
1105
+ console.error(USAGE);
1106
+ return FAILURE_EXIT_CODE;
1107
+ }
1108
+ return report2(cited);
700
1109
  }
701
- function prompt_text(tail) {
702
- const text2 = tail.split("\n").map((line) => prompt_line_text(line)).findLast((one) => one !== void 0);
703
- return text2 === void 0 || is_invocation(text2) ? NO_PROMPT : text2;
1110
+ async function main(args) {
1111
+ process.exitCode = await run(args);
1112
+ }
1113
+ var issue_cite_cli = {
1114
+ USAGE,
1115
+ cite,
1116
+ parse_targets,
1117
+ run
1118
+ };
1119
+ if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET));
1120
+
1121
+ // scripts/notify/telegram-test.ts
1122
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1123
+
1124
+ // scripts/gh/github-issue-url.ts
1125
+ var NAME_WITH_OWNER_SEPARATOR = "/";
1126
+ var GITHUB_URL_PREFIX = "https://github.com/";
1127
+ var GITHUB_URL_PREFIX_SOURCE = GITHUB_URL_PREFIX.replaceAll(".", String.raw`\.`);
1128
+ var REPOSITORY_SOURCE = `^${GITHUB_URL_PREFIX_SOURCE}([^/]+)/([^/]+)`;
1129
+ var ISSUE_URL_PATTERN = new RegExp(String.raw`${REPOSITORY_SOURCE}/issues/(\d+)\b`, "u");
1130
+ var PULL_URL_PATTERN = new RegExp(String.raw`${REPOSITORY_SOURCE}/pull/(\d+)\b`, "u");
1131
+ function build_target(match) {
1132
+ const [, owner = "", repo = "", number = ""] = match;
1133
+ const name_with_owner = `${owner}${NAME_WITH_OWNER_SEPARATOR}${repo}`;
1134
+ return {
1135
+ target: {
1136
+ owner,
1137
+ repo,
1138
+ name_with_owner,
1139
+ base_url: `${GITHUB_URL_PREFIX}${name_with_owner}`
1140
+ },
1141
+ number
1142
+ };
1143
+ }
1144
+ function parse_with(pattern, url = "") {
1145
+ const match = pattern.exec(url);
1146
+ if (match === null) return void 0;
1147
+ return build_target(match);
1148
+ }
1149
+ function parse(issue_url) {
1150
+ const parsed = parse_with(ISSUE_URL_PATTERN, issue_url);
1151
+ if (parsed === void 0) return void 0;
1152
+ return { ...parsed.target, issue_number: parsed.number };
1153
+ }
1154
+ function parse_pull(pull_url) {
1155
+ const parsed = parse_with(PULL_URL_PATTERN, pull_url);
1156
+ if (parsed === void 0) return void 0;
1157
+ return { ...parsed.target, pull_number: parsed.number };
1158
+ }
1159
+ var github_issue_url = { parse, parse_pull };
1160
+
1161
+ // scripts/git/git-error.ts
1162
+ function get_stderr_from_error(cause) {
1163
+ if (!has_stderr_field(cause)) return void 0;
1164
+ const stderr = cause.stderr.trim();
1165
+ return stderr.length > 0 ? stderr : void 0;
1166
+ }
1167
+ function get_error_message_from_cause(cause) {
1168
+ const message3 = cause.message.trim();
1169
+ if (message3.length > 0) return message3;
1170
+ return get_stderr_from_error(cause);
1171
+ }
1172
+ function get_cause_message(cause) {
1173
+ if (cause instanceof Error) return get_error_message_from_cause(cause);
1174
+ if (typeof cause === "string") return cause.trim();
1175
+ return void 0;
1176
+ }
1177
+ function display_error_details(cause) {
1178
+ const cause_message = get_cause_message(cause);
1179
+ if (cause_message === void 0 || cause_message.length === 0) return;
1180
+ console.error("");
1181
+ console.error("\u{1F4A1} Details:", cause_message);
1182
+ }
1183
+ var FAILURE_EXIT_CODE2 = 1;
1184
+ var OPERATION_CANCELLED_MESSAGE = "\u{1F4A1} Operation cancelled.";
1185
+ var BranchMismatchError = class extends Error {
1186
+ current_branch;
1187
+ target_branch_name;
1188
+ constructor(mismatch, options) {
1189
+ super(
1190
+ `Branch mismatch: on ${mismatch.current_branch}, expected ${mismatch.target_branch_name}`,
1191
+ options
1192
+ );
1193
+ this.name = "BranchMismatchError";
1194
+ this.current_branch = mismatch.current_branch;
1195
+ this.target_branch_name = mismatch.target_branch_name;
1196
+ }
1197
+ };
1198
+ var OperationCancelledError = class extends Error {
1199
+ constructor() {
1200
+ super("Operation cancelled");
1201
+ this.name = "OperationCancelledError";
1202
+ }
1203
+ };
1204
+ function display_failure(error) {
1205
+ const error_message = error_text.message_of(error);
1206
+ console.error("");
1207
+ console.error("\u274C Error:", error_message);
1208
+ if (error instanceof Error && error.cause !== void 0) {
1209
+ display_error_details(error.cause);
1210
+ }
1211
+ console.error("");
1212
+ }
1213
+ function display_branch_mismatch(error) {
1214
+ console.error("");
1215
+ console.error("\u274C Branch mismatch detected");
1216
+ console.error("");
1217
+ console.error(`Current branch: ${error.current_branch}`);
1218
+ console.error(`Expected branch: ${error.target_branch_name}`);
1219
+ console.error("");
1220
+ console.error("\u{1F4A1} Please update main branch to the latest and try again.");
1221
+ }
1222
+ function display_cancelled() {
1223
+ console.info(OPERATION_CANCELLED_MESSAGE);
1224
+ console.info("");
1225
+ }
1226
+ function display(error) {
1227
+ if (error instanceof BranchMismatchError) {
1228
+ display_branch_mismatch(error);
1229
+ return;
1230
+ }
1231
+ if (error instanceof OperationCancelledError) {
1232
+ display_cancelled();
1233
+ return;
1234
+ }
1235
+ display_failure(error);
1236
+ }
1237
+ function handle(error) {
1238
+ display(error);
1239
+ process.exitCode = FAILURE_EXIT_CODE2;
1240
+ }
1241
+ var git_error = {
1242
+ handle
1243
+ };
1244
+
1245
+ // scripts/josh/cli-body.ts
1246
+ import { readFileSync, statSync } from "node:fs";
1247
+ import path from "node:path";
1248
+ var STDIN_PATH = "-";
1249
+ var STDIN_FD = 0;
1250
+ var ESCAPED_NEWLINE = String.raw`\n`;
1251
+ function read_body_file(raw_path) {
1252
+ const resolved = path.resolve(raw_path);
1253
+ const stats = statSync(resolved, { throwIfNoEntry: false });
1254
+ if (stats === void 0 || stats.isDirectory()) {
1255
+ throw new Error(`Not a readable file: ${resolved}`);
1256
+ }
1257
+ return readFileSync(resolved, "utf8");
1258
+ }
1259
+ function read_file_or_stdin(raw_path) {
1260
+ return raw_path === STDIN_PATH ? readFileSync(STDIN_FD, "utf8") : read_body_file(raw_path);
1261
+ }
1262
+ function expand_escaped_newlines(raw) {
1263
+ return raw.replaceAll(ESCAPED_NEWLINE, "\n");
1264
+ }
1265
+ function has_value(raw) {
1266
+ return raw !== void 0 && raw.length > 0;
1267
+ }
1268
+ function resolve_inline(inline) {
1269
+ return inline === void 0 ? void 0 : expand_escaped_newlines(inline.trim());
1270
+ }
1271
+ function resolve(input) {
1272
+ const { inline, file_path: file_path2 } = input;
1273
+ if (file_path2 === void 0 || file_path2.length === 0) return resolve_inline(inline);
1274
+ if (has_value(inline)) {
1275
+ throw new Error(`Pass ${input.inline_flag} or ${input.file_flag}, not both.`);
1276
+ }
1277
+ return read_file_or_stdin(file_path2);
1278
+ }
1279
+ var cli_body = { read_file_or_stdin, has_value, resolve };
1280
+
1281
+ // scripts/notify/telegram-test-logic.ts
1282
+ var VALID_TASK_TYPES = [
1283
+ "planning",
1284
+ "completion",
1285
+ "failure",
1286
+ "warning",
1287
+ "kickoff_retry",
1288
+ "confirmation",
1289
+ "stalled"
1290
+ ];
1291
+ var DEFAULT_TASK_TYPE = "planning";
1292
+ function parse_task_type(raw) {
1293
+ if (raw === void 0) return DEFAULT_TASK_TYPE;
1294
+ const matched = VALID_TASK_TYPES.find((candidate) => candidate === raw);
1295
+ if (matched === void 0) {
1296
+ throw new Error(`Invalid --task-type: ${raw}. Expected one of ${VALID_TASK_TYPES.join(", ")}.`);
1297
+ }
1298
+ return matched;
1299
+ }
1300
+ var has_flag_value = cli_body.has_value;
1301
+ function coalesce(primary, fallback) {
1302
+ if (has_flag_value(primary)) return primary;
1303
+ return fallback;
1304
+ }
1305
+ function normalize_body(values) {
1306
+ return cli_body.resolve({
1307
+ inline: values.body,
1308
+ file_path: values["body-file"],
1309
+ inline_flag: "--body",
1310
+ file_flag: "--body-file"
1311
+ });
1312
+ }
1313
+ function require_recommended(values) {
1314
+ const recommended = values.recommended?.trim() ?? "";
1315
+ if (recommended.length === 0) {
1316
+ throw new Error('--task-type confirmation needs --recommended "<the recommended choice>".');
1317
+ }
1318
+ return recommended;
1319
+ }
1320
+ function given_alternatives(values) {
1321
+ return (values.alternative ?? []).filter((alternative) => alternative.trim().length > 0);
1322
+ }
1323
+ function build_choices(values, task_type) {
1324
+ if (task_type !== "confirmation") return void 0;
1325
+ return confirmation_options.block({
1326
+ recommended: require_recommended(values),
1327
+ alternatives: given_alternatives(values)
1328
+ });
1329
+ }
1330
+ function build_input(input) {
1331
+ const task_type = parse_task_type(input.values["task-type"]);
1332
+ return {
1333
+ task_type,
1334
+ repo_name: coalesce(input.values["repo-name"], input.context.repo_name),
1335
+ issue_title: coalesce(input.values["issue-title"], input.context.issue_title),
1336
+ body: normalize_body(input.values),
1337
+ choices: build_choices(input.values, task_type),
1338
+ issue_url: input.values["issue-url"],
1339
+ pr_url: input.values["pr-url"]
1340
+ };
1341
+ }
1342
+ var telegram_test_logic = {
1343
+ build_input,
1344
+ parse_task_type,
1345
+ has_flag_value
1346
+ };
1347
+
1348
+ // scripts/notify/telegram-test.ts
1349
+ var REPO_NAME_SEPARATOR = "/";
1350
+ var ARGV_OFFSET2 = 2;
1351
+ var FAILURE_EXIT_CODE3 = 1;
1352
+ var USAGE2 = "Usage: josh notify [--task-type <type>] [--body <text> | --body-file <path>] [--issue-url <url>] [--pr-url <url>] [--issue-title <text>] [--repo-name <name>] [--recommended <text>] [--alternative <text>]...";
1353
+ var FREE_TEXT_FLAGS = ["--body", "--issue-title", "--recommended", "--alternative"];
1354
+ var OPTIONS2 = {
1355
+ "task-type": { type: "string" },
1356
+ "repo-name": { type: "string" },
1357
+ "issue-title": { type: "string" },
1358
+ body: { type: "string" },
1359
+ "body-file": { type: "string" },
1360
+ "issue-url": { type: "string" },
1361
+ "pr-url": { type: "string" },
1362
+ recommended: { type: "string" },
1363
+ alternative: { type: "string", multiple: true }
1364
+ };
1365
+ function parse_cli_arguments() {
1366
+ return cli_flags.values_of(
1367
+ cli_flags.attach_values(process.argv.slice(ARGV_OFFSET2), FREE_TEXT_FLAGS),
1368
+ OPTIONS2
1369
+ );
1370
+ }
1371
+ function refuse_usage() {
1372
+ console.error(USAGE2);
1373
+ process.exitCode = FAILURE_EXIT_CODE3;
1374
+ }
1375
+ async function fetch_repo_name() {
1376
+ const name_with_owner = await git_gh_repo.repo_get_name_with_owner();
1377
+ if (name_with_owner === void 0) return void 0;
1378
+ return name_with_owner.split(REPO_NAME_SEPARATOR).at(-1);
1379
+ }
1380
+ async function fetch_issue_title(target) {
1381
+ if (target === void 0) return void 0;
1382
+ const title = await git_gh_issue_read.issue_get_title(target.issue_number, target.name_with_owner);
1383
+ if (title === void 0) {
1384
+ console.warn(
1385
+ `\u26A0\uFE0F Could not read ${session_cite.issue(target.issue_number, void 0, target.name_with_owner)}.`
1386
+ );
1387
+ }
1388
+ return title;
1389
+ }
1390
+ async function resolve_issue_title(values, target) {
1391
+ if (telegram_test_logic.has_flag_value(values["issue-title"])) return void 0;
1392
+ return await fetch_issue_title(target);
1393
+ }
1394
+ async function resolve_context(values) {
1395
+ const target = github_issue_url.parse(values["issue-url"]);
1396
+ const pull_target = github_issue_url.parse_pull(values["pr-url"]);
1397
+ const repo_name = target?.repo ?? pull_target?.repo ?? await fetch_repo_name();
1398
+ const issue_title = await resolve_issue_title(values, target);
1399
+ return { repo_name, issue_title };
1400
+ }
1401
+ async function send_values(values) {
1402
+ const context = await resolve_context(values);
1403
+ await telegram_notify.send(telegram_test_logic.build_input({ values, context }));
1404
+ }
1405
+ async function main2() {
1406
+ josh_environment_file.load_environment_file();
1407
+ const values = parse_cli_arguments();
1408
+ if (values === void 0) {
1409
+ refuse_usage();
1410
+ return;
1411
+ }
1412
+ await send_values(values);
704
1413
  }
705
- var last_prompt = { prompt_text };
1414
+ async function run_cli() {
1415
+ try {
1416
+ await main2();
1417
+ } catch (error) {
1418
+ git_error.handle(error);
1419
+ }
1420
+ }
1421
+ if (process.argv[1] === fileURLToPath2(import.meta.url)) await run_cli();
1422
+ var telegram_test = { fetch_repo_name, fetch_issue_title, resolve_context, run_cli, send_values };
706
1423
 
707
1424
  // scripts/rules/stop-rules.ts
708
1425
  import { z as z3 } from "zod";
@@ -721,21 +1438,21 @@ var OWNER_SEPARATOR = "/";
721
1438
  function is_offer_line(line) {
722
1439
  return OFFER_PATTERNS.some((pattern) => pattern.test(line));
723
1440
  }
724
- function offers_filing(message2) {
725
- return issue_citation.prose_lines(message2).some((line) => is_offer_line(line));
1441
+ function offers_filing(message3) {
1442
+ return issue_citation.prose_lines(message3).some((line) => is_offer_line(line));
726
1443
  }
727
- function url_owners(message2) {
728
- return [...message2.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
1444
+ function url_owners(message3) {
1445
+ return [...message3.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
729
1446
  }
730
- function qualified_owners(message2) {
731
- return issue_citation.bare_references(message2).filter((reference) => reference.includes(OWNER_SEPARATOR)).map((reference) => reference.split(OWNER_SEPARATOR)[0] ?? "");
1447
+ function qualified_owners(message3) {
1448
+ return issue_citation.bare_references(message3).filter((reference) => reference.includes(OWNER_SEPARATOR)).map((reference) => reference.split(OWNER_SEPARATOR)[0] ?? "");
732
1449
  }
733
- function mentioned_owners(message2) {
734
- return [...url_owners(message2), ...qualified_owners(message2)];
1450
+ function mentioned_owners(message3) {
1451
+ return [...url_owners(message3), ...qualified_owners(message3)];
735
1452
  }
736
- function is_first_party_target(message2, session_owner) {
1453
+ function is_first_party_target(message3, session_owner) {
737
1454
  if (session_owner === void 0) return false;
738
- return mentioned_owners(message2).every(
1455
+ return mentioned_owners(message3).every(
739
1456
  (owner) => repo_party.classify(session_owner, owner) === repo_party.FIRST_PARTY
740
1457
  );
741
1458
  }
@@ -777,10 +1494,10 @@ function letter_pattern(script) {
777
1494
  return void 0;
778
1495
  }
779
1496
  }
780
- function prose_of(message2) {
781
- let text2 = message2;
1497
+ function prose_of(message3) {
1498
+ let text2 = message3;
782
1499
  for (const pattern of CODE_PATTERNS) text2 = text2.replaceAll(pattern, " ");
783
- return text2.replaceAll(ASCII_RUN_PATTERN, (run) => run.includes("/") ? " " : run);
1500
+ return text2.replaceAll(ASCII_RUN_PATTERN, (run3) => run3.includes("/") ? " " : run3);
784
1501
  }
785
1502
  function letter_count(text2) {
786
1503
  return text2.match(LETTER_PATTERN)?.length ?? 0;
@@ -799,17 +1516,46 @@ function matches_share(prose, script, pattern) {
799
1516
  const matched = lines.filter((line) => is_line_in_script(line, script, pattern));
800
1517
  return matched.length / lines.length;
801
1518
  }
802
- function is_mismatch(message2, lang) {
1519
+ function is_mismatch(message3, lang) {
803
1520
  const script = script_of(lang);
804
1521
  if (script === void 0) return false;
805
1522
  const pattern = letter_pattern(script);
806
1523
  if (pattern === void 0) return false;
807
- const prose = prose_of(message2);
1524
+ const prose = prose_of(message3);
808
1525
  if (letter_count(prose) < MIN_PROSE_LETTERS) return false;
809
1526
  return matches_share(prose, script, pattern) < MATCH_SHARE_FLOOR;
810
1527
  }
811
1528
  var reply_language = { is_mismatch, script_of };
812
1529
 
1530
+ // scripts/rules/stop-decision.ts
1531
+ var RECOMMENDED_LABEL = /^recommended[*_]*:/iu;
1532
+ var ALTERNATIVE_LABEL = /^alternative[*_]*:/iu;
1533
+ var LEADING_EMPHASIS = /^[*_]+/u;
1534
+ var TRAILING_EMPHASIS = /(?<![*_])[*_]+$/u;
1535
+ var BULLET = /^[-*]\s+/u;
1536
+ var QUESTION_ENDING = /(?:[??]|か|かな|かね)[\s。.]*$/u;
1537
+ function unwrapped(text2) {
1538
+ return text2.trim().replace(LEADING_EMPHASIS, "").replace(TRAILING_EMPHASIS, "").trim();
1539
+ }
1540
+ function bare_line(line) {
1541
+ return unwrapped(line.trim().replace(BULLET, ""));
1542
+ }
1543
+ function choice_of(message3, label) {
1544
+ const line = message3.split("\n").map((one) => bare_line(one)).find((one) => label.test(one));
1545
+ const choice = line === void 0 ? void 0 : unwrapped(line.replace(label, ""));
1546
+ return choice === "" ? void 0 : choice;
1547
+ }
1548
+ function decision_of(message3) {
1549
+ const recommended = choice_of(message3, RECOMMENDED_LABEL);
1550
+ if (recommended === void 0) return void 0;
1551
+ return { recommended, alternative: choice_of(message3, ALTERNATIVE_LABEL) };
1552
+ }
1553
+ function is_question(prompt) {
1554
+ return QUESTION_ENDING.test(prompt.trim());
1555
+ }
1556
+ var DECISION_REASON = "\u26D4 stop decision: this working tree is held by your run and your reply names no decision. If the run waits on a person, end the reply with a `Recommended: <choice>` line and, where there is one, an `Alternative: <choice>` line \u2014 the hook sends the `confirmation` Telegram from them. If it waits on nobody, continue the run, or say in one line that nothing is awaited. Do not repeat your previous reply.";
1557
+ var stop_decision = { DECISION_REASON, decision_of, is_question };
1558
+
813
1559
  // scripts/rules/stop-rules.ts
814
1560
  var SWITCH_ENV_KEY = "JOSH_STOP_GUARD";
815
1561
  var BLOCK_DECISION = "block";
@@ -818,7 +1564,7 @@ var stop_payload_schema = z3.object({
818
1564
  stop_hook_active: z3.boolean().nullish(),
819
1565
  last_assistant_message: z3.string().nullish()
820
1566
  });
821
- var NO_OUTCOME = { reason: void 0 };
1567
+ var NO_OUTCOME = { reason: void 0, actions: [], blocks_on_failure: false };
822
1568
  var STOP_NOTIFY_REASON = '\u26D4 mid-workflow stop notification: this working tree is still held by a run and no `confirmation` Telegram was sent this turn. Send `pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\' --recommended "<choice>" --alternative "<choice>"` (`CLAUDE.md` \u2192 "Mid-workflow stop notification"), then confirm in one line that it was sent \u2014 do not repeat your previous reply.';
823
1569
  var HOLD_RELEASE_REASON = "\u26D4 working-tree hold not released: this working tree is clean but its `run:hold` record is still in place. Release it with `pnpm josh run:release <N>` (bare for a `new` entry; `working-tree-hold.md`), then confirm in one line that it was released \u2014 do not repeat your previous reply.";
824
1570
  var ISSUE_CITATION_REASON = "Session-facing output cites an Issue as a number-link so the reader can click through and see which repository it is (`CLAUDE.md`, `prompts/collaboration-workflow/issue-citation.md`).";
@@ -836,8 +1582,8 @@ var HEADLESS_WAIT_REASON = `${HEADLESS_WAIT_MARKER}${HEADLESS_WAIT_BODY}`;
836
1582
  var INDEX_COMMAND_PATTERN = /git (?:add|stage|rm --cached|restore --staged|restore -S)\b/u;
837
1583
  var PERMISSION_PATTERN = /許可|承認|permission|approve|approval|authorization/iu;
838
1584
  var LANE_INDEX_REASON = "\u26D4 lane index permission: this is a dispatched lane child (`JOSH_LANE_CHILD`) and your reply asks a person for permission to change the git index. That is Tier A here (`chain-rule.md`): remove any conflict markers, run `pnpm josh git -y` (it stages and commits, a conflicted merge included) and continue the run \u2014 do not repeat your previous reply.";
839
- function asks_index_permission(message2) {
840
- return INDEX_COMMAND_PATTERN.test(message2) && PERMISSION_PATTERN.test(message2);
1585
+ function asks_index_permission(message3) {
1586
+ return INDEX_COMMAND_PATTERN.test(message3) && PERMISSION_PATTERN.test(message3);
841
1587
  }
842
1588
  function needs_index_route(context) {
843
1589
  return context.lane_child && asks_index_permission(context.message);
@@ -860,26 +1606,29 @@ function needs_filing(context) {
860
1606
  return filing_offer.offers_filing(context.message) && filing_offer.is_first_party_target(context.message, context.session_owner);
861
1607
  }
862
1608
  function is_held_idle(context) {
863
- return context.hold_present && !context.agent_pending && !context.handed_off;
1609
+ if (context.hold_issue === void 0 || !context.hold_owned) return false;
1610
+ return !context.agent_pending && !context.handed_off;
864
1611
  }
865
1612
  function needs_notify(context) {
866
- return is_held_idle(context) && !context.notified;
1613
+ return is_held_idle(context) && !context.notified && !context.answers_question;
1614
+ }
1615
+ function needs_decision(context) {
1616
+ return needs_notify(context) && stop_decision.decision_of(context.message) === void 0;
867
1617
  }
868
1618
  function needs_release(context) {
869
1619
  return is_held_idle(context) && context.tree_clean && !context.prrun_stopped;
870
1620
  }
871
- function citation_reason(context) {
872
- const references = issue_citation.unquoted_references(context.message, context.prompt);
873
- if (references.length === 0) return void 0;
874
- return build_citation_reason(references);
875
- }
876
- function wording_reason(context) {
877
- if (context.headless_agent) return void 0;
878
- return citation_reason(context) ?? language_reason(context);
1621
+ function bare_references(context) {
1622
+ if (context.headless_agent) return [];
1623
+ return issue_citation.unquoted_references(context.message, context.prompt);
879
1624
  }
880
1625
  function reply_reason(context) {
881
1626
  if (needs_filing(context)) return FILING_OFFER_REASON;
882
- return wording_reason(context);
1627
+ if (context.headless_agent) return void 0;
1628
+ return language_reason(context);
1629
+ }
1630
+ function decision_reason(context) {
1631
+ return needs_decision(context) ? stop_decision.DECISION_REASON : void 0;
883
1632
  }
884
1633
  function is_headless_held(context) {
885
1634
  if (!context.headless_waiting) return false;
@@ -892,23 +1641,61 @@ function count_headless_refusals(tail) {
892
1641
  function needs_background_wait(context) {
893
1642
  return context.lane_child && context.background_pending;
894
1643
  }
895
- function hold_reason(context) {
1644
+ function lane_reason(context) {
896
1645
  if (needs_index_route(context)) return LANE_INDEX_REASON;
897
1646
  if (needs_background_wait(context)) return lane_background.LANE_BACKGROUND_STOP_REASON;
898
- if (needs_notify(context)) return STOP_NOTIFY_REASON;
899
- if (needs_release(context)) return HOLD_RELEASE_REASON;
900
1647
  return void 0;
901
1648
  }
1649
+ function model_reason(context) {
1650
+ const lane = lane_reason(context);
1651
+ if (lane !== void 0) return lane;
1652
+ const reasons = [reply_reason(context), decision_reason(context)].filter(
1653
+ (reason) => reason !== void 0
1654
+ );
1655
+ return reasons.length === 0 ? void 0 : reasons.join("\n\n");
1656
+ }
1657
+ function notify_actions(context, issue) {
1658
+ const decision = stop_decision.decision_of(context.message);
1659
+ if (decision === void 0 || !needs_notify(context)) return [];
1660
+ return [{ kind: "notify", issue, message: context.message, decision }];
1661
+ }
1662
+ function hold_actions(context) {
1663
+ const issue = context.hold_issue;
1664
+ if (issue === void 0) return [];
1665
+ const notify2 = notify_actions(context, issue);
1666
+ return needs_release(context) ? [...notify2, { kind: "release", issue }] : notify2;
1667
+ }
1668
+ function stop_actions(context) {
1669
+ const references = bare_references(context);
1670
+ const cite3 = references.length > 0 ? [{ kind: "cite", references }] : [];
1671
+ return [...hold_actions(context), ...cite3];
1672
+ }
902
1673
  function block_reason(context) {
903
1674
  if (is_headless_held(context)) return HEADLESS_WAIT_REASON;
904
1675
  if (context.stop_hook_active || context.cut_pending) return void 0;
905
- return hold_reason(context) ?? reply_reason(context);
1676
+ return model_reason(context);
1677
+ }
1678
+ function runs_actions(context, reason) {
1679
+ return reason === void 0 && !context.cut_pending && lane_reason(context) === void 0;
906
1680
  }
907
1681
  function stop_outcome(context) {
908
- return { reason: block_reason(context) };
1682
+ const reason = block_reason(context);
1683
+ if (!runs_actions(context, reason)) return { ...NO_OUTCOME, reason };
1684
+ const actions = stop_actions(context);
1685
+ if (actions.length === 0) return NO_OUTCOME;
1686
+ return { reason, actions, blocks_on_failure: !context.stop_hook_active };
909
1687
  }
910
- function block_envelope(reason, lang) {
911
- return JSON.stringify({ decision: BLOCK_DECISION, reason: `${reason} ${language_note(lang)}` });
1688
+ function fallback_reason(action) {
1689
+ if (action.kind === "notify") return STOP_NOTIFY_REASON;
1690
+ if (action.kind === "release") return HOLD_RELEASE_REASON;
1691
+ return build_citation_reason(action.references);
1692
+ }
1693
+ function block_envelope(reason, lang, notice) {
1694
+ const refusal = { decision: BLOCK_DECISION, reason: `${reason} ${language_note(lang)}` };
1695
+ return JSON.stringify(notice === void 0 ? refusal : { ...refusal, systemMessage: notice });
1696
+ }
1697
+ function notice_envelope(notice) {
1698
+ return JSON.stringify({ systemMessage: notice });
912
1699
  }
913
1700
  function parse_stop_payload(raw_payload) {
914
1701
  const parsed = stop_payload_schema.safeParse(JSON.parse(raw_payload));
@@ -930,32 +1717,51 @@ var stop_rules = {
930
1717
  block_envelope,
931
1718
  build_language_reason,
932
1719
  count_headless_refusals,
1720
+ fallback_reason,
933
1721
  is_enabled,
1722
+ notice_envelope,
934
1723
  parse_stop_payload,
935
1724
  stop_outcome
936
1725
  };
937
1726
 
1727
+ // scripts/run/hold/run-hold-cli.ts
1728
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
1729
+
1730
+ // scripts/run/run-cli-fault.ts
1731
+ async function directory_of(where, read2) {
1732
+ try {
1733
+ return await read2();
1734
+ } catch (error) {
1735
+ error_text.trace_swallowed(where, error);
1736
+ return void 0;
1737
+ }
1738
+ }
1739
+ function message2(command, error) {
1740
+ return `${command} failed and established nothing: ${error_text.message_of(error)}`;
1741
+ }
1742
+ var run_cli_fault = { directory_of, message: message2 };
1743
+
938
1744
  // scripts/run/run-halfrun-resume.ts
939
1745
  var MARKED = "marked";
940
1746
  var FOREIGN = "foreign";
941
1747
  var UNREADABLE2 = "unreadable";
942
- function hold_of(read) {
943
- return read.kind === "held" || read.kind === "stale" ? read.hold : void 0;
1748
+ function hold_of(read2) {
1749
+ return read2.kind === "held" || read2.kind === "stale" ? read2.hold : void 0;
944
1750
  }
945
- function is_halfrun_stop(read, issue, is_dirty) {
946
- const hold = hold_of(read);
1751
+ function is_halfrun_stop(read2, issue, is_dirty) {
1752
+ const hold = hold_of(read2);
947
1753
  return is_dirty && hold?.issue === issue && hold.is_halfrun_stop === true;
948
1754
  }
949
- function is_own_record(read, issue) {
950
- const hold = hold_of(read);
1755
+ function is_own_record(read2, issue) {
1756
+ const hold = hold_of(read2);
951
1757
  return hold === void 0 || hold.issue === issue || hold.issue === run_hold.UNNUMBERED_ISSUE;
952
1758
  }
953
- function mark_stop_at(target, issue, mark = run_hold.HALFRUN_STOP_FIELDS) {
954
- const read = run_hold.read_hold(target);
955
- if (read.kind === "unreadable") return UNREADABLE2;
956
- if (!is_own_record(read, issue)) return FOREIGN;
1759
+ function mark_stop_at(target, issue, mark2 = run_hold.HALFRUN_STOP_FIELDS) {
1760
+ const read2 = run_hold.read_hold(target);
1761
+ if (read2.kind === "unreadable") return UNREADABLE2;
1762
+ if (!is_own_record(read2, issue)) return FOREIGN;
957
1763
  run_hold.release_hold(target);
958
- return run_hold.create_stop_hold(target, issue, mark) ? MARKED : FOREIGN;
1764
+ return run_hold.create_stop_hold(target, issue, mark2) ? MARKED : FOREIGN;
959
1765
  }
960
1766
  function take_over(target, issue) {
961
1767
  run_hold.release_hold(target);
@@ -996,54 +1802,1627 @@ var run_halfrun_resume = {
996
1802
  take_over
997
1803
  };
998
1804
 
999
- // scripts/run/carry/run-carry-ended.ts
1000
- import path from "node:path";
1805
+ // scripts/followup/git-followup-label.ts
1001
1806
  import { z as z4 } from "zod";
1002
- var ENDED_PREFIX = "josh-run-ended-";
1003
- var TRANSCRIPT_SUFFIX = ".jsonl";
1004
- var ended_run_schema = z4.object({
1005
- invocation: z4.string(),
1006
- started_at: z4.string(),
1007
- ended_at: z4.string(),
1008
- stopped: z4.string().optional(),
1009
- session: z4.string().optional()
1010
- });
1011
- function ended_path(git_directory) {
1012
- return stamp_file.stamp_path(ENDED_PREFIX, git_directory);
1807
+ var labels_read_schema = z4.object({ labels: z4.array(issue_label_schema).optional() });
1808
+ function unreadable_error(issue_number) {
1809
+ return new Error(
1810
+ `gh api answered no readable labels for issue ${session_cite.issue(issue_number)}`
1811
+ );
1013
1812
  }
1014
- function session_of(transcript) {
1015
- return transcript === void 0 ? void 0 : path.basename(transcript, TRANSCRIPT_SUFFIX);
1813
+ async function read_label_names(issue_number) {
1814
+ const raw = await git_gh_command.issue_get_labels_and_body(issue_number);
1815
+ if (raw === void 0) throw unreadable_error(issue_number);
1816
+ const parsed = parse_json_object_or_undefined(raw, labels_read_schema);
1817
+ if (parsed === void 0) throw unreadable_error(issue_number);
1818
+ return (parsed.labels ?? []).map((label) => label.name);
1819
+ }
1820
+ async function strip_in_progress(issue_number) {
1821
+ if (issue_number === void 0) return;
1822
+ const stored = label_name_of(await read_label_names(issue_number), IN_PROGRESS_LABEL);
1823
+ if (stored === void 0) return;
1824
+ await git_gh_command.issue_remove_label(issue_number, stored);
1825
+ }
1826
+ var git_followup_label = {
1827
+ strip_in_progress
1828
+ };
1829
+
1830
+ // scripts/run/run-label.ts
1831
+ var MARK_RECOVERY = "gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=in-progress'";
1832
+ var UNMARK_RECOVERY = "gh api -X DELETE repos/{owner}/{repo}/issues/<N>/labels/in-progress";
1833
+ function warn2(action, issue, recovery) {
1834
+ console.warn(
1835
+ `\u26A0 could not ${action} \`in-progress\` on ${session_cite.issue(issue)} \u2014 run by hand: ${recovery}`
1836
+ );
1016
1837
  }
1017
- function stop_of(transcript, stopped) {
1018
- return stopped === void 0 ? {} : { stopped, session: session_of(transcript) };
1838
+ async function mark(issue) {
1839
+ const is_marked = await git_gh_command.issue_add_label(issue, IN_PROGRESS_LABEL);
1840
+ if (!is_marked) warn2("apply", issue, MARK_RECOVERY);
1841
+ return is_marked;
1019
1842
  }
1020
- function record_ended(target, read, now = /* @__PURE__ */ new Date(), stopped) {
1021
- if (read.kind !== "carried") return;
1022
- const { invocation, started_at, owner_transcript } = read.carry;
1023
- const ended_at = now.toISOString();
1024
- stamp_file.replace_stamp(target, {
1025
- invocation,
1026
- started_at,
1027
- ended_at,
1028
- ...stop_of(owner_transcript, stopped)
1029
- });
1843
+ async function unmark(issue) {
1844
+ try {
1845
+ await git_followup_label.strip_in_progress(issue);
1846
+ return true;
1847
+ } catch {
1848
+ warn2("remove", issue, UNMARK_RECOVERY);
1849
+ return false;
1850
+ }
1030
1851
  }
1031
- function parse_ended(raw) {
1032
- return json_value.parse_with(raw, ended_run_schema);
1852
+ var run_label = {
1853
+ mark,
1854
+ unmark
1855
+ };
1856
+
1857
+ // scripts/lib/git-animation.ts
1858
+ import ora from "ora";
1859
+ var SUCCESS_ICON = "\u2705";
1860
+ function create_stop_function(spinner, message3) {
1861
+ return (result, icon) => {
1862
+ if (result === void 0) {
1863
+ spinner.stop();
1864
+ return;
1865
+ }
1866
+ const symbol = (icon ?? SUCCESS_ICON).trimEnd();
1867
+ spinner.stopAndPersist({ symbol, text: `${message3} ${result}` });
1868
+ };
1033
1869
  }
1034
- function read_ended(target) {
1035
- const raw = stamp_file.read_stamp_text(target);
1036
- return raw === void 0 ? void 0 : parse_ended(raw);
1870
+ function create_animation(message3) {
1871
+ const spinner = ora({ text: message3 }).start();
1872
+ return {
1873
+ stop: create_stop_function(spinner, message3),
1874
+ pause: () => {
1875
+ spinner.stop();
1876
+ }
1877
+ };
1037
1878
  }
1038
- function has_ended(target, started_at) {
1039
- const ended = read_ended(target);
1040
- return ended !== void 0 && ended.started_at === started_at;
1879
+ var git_animation = {
1880
+ create_animation
1881
+ };
1882
+
1883
+ // scripts/lib/animation-helpers.ts
1884
+ function get_error_message(error_message = "Operation failed") {
1885
+ return error_message;
1886
+ }
1887
+ function get_display_result(result, formatter) {
1888
+ return formatter?.(result) ?? String(result);
1889
+ }
1890
+ function handle_animation_success(animation, result, options) {
1891
+ const icon = options?.icon_selector?.(result);
1892
+ const display_result = get_display_result(result, options?.result_formatter);
1893
+ animation.stop(display_result, icon);
1894
+ }
1895
+ function handle_animation_error(animation, error, error_message) {
1896
+ animation.stop();
1897
+ if (error_message === void 0 || error_message.length === 0) {
1898
+ if (error instanceof Error) {
1899
+ throw error;
1900
+ }
1901
+ throw new Error(String(error), { cause: error });
1902
+ }
1903
+ throw new Error(get_error_message(error_message), { cause: error });
1041
1904
  }
1042
- var run_carry_ended = { ended_path, has_ended, read_ended, record_ended };
1905
+ async function execute_with_animation(message3, command_executor, options) {
1906
+ const animation = git_animation.create_animation(message3);
1907
+ try {
1908
+ const result = await command_executor(() => {
1909
+ animation.pause();
1910
+ });
1911
+ handle_animation_success(animation, result, options);
1912
+ return result;
1913
+ } catch (error) {
1914
+ return handle_animation_error(animation, error, options?.error_message);
1915
+ }
1916
+ }
1917
+ function create_git_operation_config(error_message) {
1918
+ return {
1919
+ error_message,
1920
+ icon_selector: () => "\u2705",
1921
+ result_formatter: (message3) => message3
1922
+ };
1923
+ }
1924
+ var animation_helpers = {
1925
+ execute_with_animation
1926
+ };
1043
1927
 
1044
- // scripts/run/wake/run-wake.ts
1045
- import { z as z5 } from "zod";
1046
- var WAKE_PREFIX = "josh-run-wake-";
1928
+ // scripts/git/git-branch.ts
1929
+ async function current() {
1930
+ const config = {
1931
+ error_message: "Failed to get current branch"
1932
+ };
1933
+ return await animation_helpers.execute_with_animation(
1934
+ "Getting current branch...",
1935
+ git_command.branch,
1936
+ config
1937
+ );
1938
+ }
1939
+ async function create(branch_name) {
1940
+ const config = create_git_operation_config("Failed to create branch");
1941
+ await animation_helpers.execute_with_animation(
1942
+ `Creating branch: ${branch_name}...`,
1943
+ async () => {
1944
+ await git_command.checkout_b(branch_name);
1945
+ return `Branch created: ${branch_name}`;
1946
+ },
1947
+ config
1948
+ );
1949
+ }
1950
+ async function switch_to(branch_name) {
1951
+ const config = create_git_operation_config("Failed to switch branch");
1952
+ await animation_helpers.execute_with_animation(
1953
+ `Switching to branch: ${branch_name}...`,
1954
+ async () => {
1955
+ await git_command.checkout(branch_name);
1956
+ return `Switched to branch: ${branch_name}`;
1957
+ },
1958
+ config
1959
+ );
1960
+ }
1961
+ async function exists(branch_name) {
1962
+ const config = {
1963
+ error_message: "Failed to check branch existence",
1964
+ result_formatter: (is_exists) => is_exists ? "Exists" : "Not found"
1965
+ };
1966
+ return await animation_helpers.execute_with_animation(
1967
+ "Checking if branch exists...",
1968
+ async () => {
1969
+ return await git_command.branch_exists(branch_name);
1970
+ },
1971
+ config
1972
+ );
1973
+ }
1974
+ async function pull_latest() {
1975
+ const config = create_git_operation_config("Failed to pull latest from remote");
1976
+ await animation_helpers.execute_with_animation(
1977
+ "Pulling latest from remote...",
1978
+ async () => {
1979
+ await git_command.pull_fast_forward();
1980
+ return "Pulled latest from remote";
1981
+ },
1982
+ config
1983
+ );
1984
+ }
1985
+ var ISSUE_PREFIX_PATTERN = /^(\d+)-/u;
1986
+ function has_same_issue_prefix(branch_a, branch_b) {
1987
+ const prefix_a = ISSUE_PREFIX_PATTERN.exec(branch_a)?.[0];
1988
+ const prefix_b = ISSUE_PREFIX_PATTERN.exec(branch_b)?.[0];
1989
+ return prefix_a !== void 0 && prefix_a === prefix_b;
1990
+ }
1991
+ function issue_from_branch(branch) {
1992
+ const digits = ISSUE_PREFIX_PATTERN.exec(branch)?.[1];
1993
+ return digits === void 0 ? void 0 : Number(digits);
1994
+ }
1995
+ function is_mismatch2(current_branch2, target_branch_name, default_branch) {
1996
+ if (current_branch2 === default_branch || current_branch2 === target_branch_name) return false;
1997
+ return !has_same_issue_prefix(current_branch2, target_branch_name);
1998
+ }
1999
+ async function handle_default_branch(target_branch_name) {
2000
+ await pull_latest();
2001
+ const is_branch_exists = await exists(target_branch_name);
2002
+ await (is_branch_exists ? switch_to : create)(target_branch_name);
2003
+ }
2004
+ async function check_and_create_branch(current_branch2, target_branch_name) {
2005
+ const default_branch = await git_command.get_default_branch();
2006
+ if (current_branch2 === default_branch) {
2007
+ await handle_default_branch(target_branch_name);
2008
+ return target_branch_name;
2009
+ }
2010
+ if (is_mismatch2(current_branch2, target_branch_name, default_branch)) {
2011
+ throw new BranchMismatchError({ current_branch: current_branch2, target_branch_name });
2012
+ }
2013
+ return current_branch2;
2014
+ }
2015
+ var git_branch = {
2016
+ current,
2017
+ check_and_create_branch,
2018
+ is_mismatch: is_mismatch2,
2019
+ // Exported so a lane's branch name can be checked against the real matcher rather than against a
2020
+ // second copy of the pattern. A copy is what let `lane/<N>` ship: nothing
2021
+ // asserted that the name `lane:open` writes is one this function accepts.
2022
+ has_same_issue_prefix,
2023
+ issue_from_branch
2024
+ };
2025
+
2026
+ // scripts/run/run-prrun-resume.ts
2027
+ var PRRUN_MERGED = "prrun-merged";
2028
+ var PRRUN_MERGE = "prrun-merge";
2029
+ var PRRUN_GATE = "prrun-gate";
2030
+ function is_untouched(facts) {
2031
+ return facts.head === facts.stop_head && facts.pr_head === facts.stop_head && !facts.is_dirty;
2032
+ }
2033
+ function decide(facts) {
2034
+ if (facts.is_merged) return PRRUN_MERGED;
2035
+ return is_untouched(facts) ? PRRUN_MERGE : PRRUN_GATE;
2036
+ }
2037
+ function stop_head_of(read2, issue) {
2038
+ const hold = run_halfrun_resume.hold_of(read2);
2039
+ return hold?.issue === issue ? hold.prrun_stop_head : void 0;
2040
+ }
2041
+ async function read_merge_state() {
2042
+ return await git_gh_command.pr_get_merge_state(await git_branch.current());
2043
+ }
2044
+ async function read_facts(stop_head) {
2045
+ const [state, head, is_dirty] = await Promise.all([
2046
+ read_merge_state(),
2047
+ git_command.head_commit(),
2048
+ run_hold.is_tree_dirty()
2049
+ ]);
2050
+ return {
2051
+ is_merged: state?.is_merged === true,
2052
+ stop_head,
2053
+ head,
2054
+ pr_head: state?.head_sha,
2055
+ is_dirty
2056
+ };
2057
+ }
2058
+ async function resume_token(issue) {
2059
+ const target = await run_halfrun_resume.hold_target();
2060
+ if (target === void 0) return void 0;
2061
+ const stop_head = stop_head_of(run_hold.read_hold(target), issue);
2062
+ return stop_head === void 0 ? void 0 : decide(await read_facts(stop_head));
2063
+ }
2064
+ async function adopt2(issue) {
2065
+ const target = await run_halfrun_resume.hold_target();
2066
+ if (target === void 0 || stop_head_of(run_hold.read_hold(target), issue) === void 0) {
2067
+ return false;
2068
+ }
2069
+ return run_halfrun_resume.take_over(target, issue);
2070
+ }
2071
+ async function mark_stop_at2(target, issue) {
2072
+ const head = await git_command.head_commit();
2073
+ return run_halfrun_resume.mark_stop_at(target, issue, { prrun_stop_head: head });
2074
+ }
2075
+ var run_prrun_resume = {
2076
+ PRRUN_GATE,
2077
+ PRRUN_MERGE,
2078
+ PRRUN_MERGED,
2079
+ adopt: adopt2,
2080
+ decide,
2081
+ mark_stop_at: mark_stop_at2,
2082
+ resume_token,
2083
+ stop_head_of
2084
+ };
2085
+
2086
+ // scripts/run/tidy/run-tidy-cli.ts
2087
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
2088
+
2089
+ // scripts/issue/issue-merged.ts
2090
+ var CLOSED_EVENT = "closed";
2091
+ var REOPENED_EVENT = "reopened";
2092
+ var MERGED_REFERENCE = "merged-reference";
2093
+ var COMPLETED_REASONS = /* @__PURE__ */ new Set(["", "completed"]);
2094
+ var FIELD_SEPARATOR = " ";
2095
+ var LINE_SEPARATOR2 = "\n";
2096
+ var TIMELINE_FILTER = [
2097
+ ".[]",
2098
+ '| if .event == "cross-referenced" and .source.issue.pull_request.merged_at != null',
2099
+ String.raw`then "${MERGED_REFERENCE}\t"`,
2100
+ `elif .event == "${CLOSED_EVENT}" or .event == "${REOPENED_EVENT}"`,
2101
+ String.raw`then "\(.event)\t\(.state_reason // "")"`,
2102
+ "else empty end"
2103
+ ].join(" ");
2104
+ function parse_rows(raw) {
2105
+ return raw.split(LINE_SEPARATOR2).filter((line) => line !== "").map((line) => {
2106
+ const [event = "", reason = ""] = line.split(FIELD_SEPARATOR);
2107
+ return { event, reason };
2108
+ });
2109
+ }
2110
+ function is_merged_close(rows) {
2111
+ const last = rows.findLast((row) => row.event === CLOSED_EVENT || row.event === REOPENED_EVENT);
2112
+ if (last?.event !== CLOSED_EVENT || !COMPLETED_REASONS.has(last.reason)) return false;
2113
+ return rows.some((row) => row.event === MERGED_REFERENCE);
2114
+ }
2115
+ async function read_merge_state2(issue) {
2116
+ try {
2117
+ const raw = await git_gh_exec.exec_gh_api({
2118
+ path: `${git_gh_api_path.issue_api_path(issue)}/timeline?per_page=100`,
2119
+ should_paginate: true,
2120
+ jq_filter: TIMELINE_FILTER
2121
+ });
2122
+ return is_merged_close(parse_rows(raw));
2123
+ } catch {
2124
+ return void 0;
2125
+ }
2126
+ }
2127
+ async function read_merged(issue) {
2128
+ return await read_merge_state2(issue) ?? false;
2129
+ }
2130
+ var issue_merged = { is_merged_close, parse_rows, read_merge_state: read_merge_state2, read_merged };
2131
+
2132
+ // scripts/observations/observation-ledger-line.ts
2133
+ var KEY_PREFIX = "- k:";
2134
+ var FIELD_SEPARATOR2 = " | ";
2135
+ var FIELD_COUNT = 5;
2136
+ var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
2137
+ var DEPTH_PATTERN = /^d[1-9]\d*$/u;
2138
+ var DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
2139
+ var SLUG_REASON = "the slug must be lowercase words joined by `-`";
2140
+ var DEPTH_REASON = "the depth must be `d1` or higher (never `d0`)";
2141
+ var DATE_REASON = "the date must be `YYYY-MM-DD`";
2142
+ var WHERE_REASON = "the `where` field is empty";
2143
+ var WHAT_REASON = "the `what` field is empty";
2144
+ function is_ledger_entry_line(line) {
2145
+ return line.trimStart().startsWith(KEY_PREFIX);
2146
+ }
2147
+ function slug_of(first_field) {
2148
+ return first_field.trimStart().slice(KEY_PREFIX.length);
2149
+ }
2150
+ function is_present(field = "") {
2151
+ return field.trim().length > 0;
2152
+ }
2153
+ function matches(pattern, field = "") {
2154
+ return pattern.test(field);
2155
+ }
2156
+ function slug_ok(key_field = "") {
2157
+ return SLUG_PATTERN.test(slug_of(key_field));
2158
+ }
2159
+ function field_reason(fields) {
2160
+ const [key_field, depth, date, where, what] = fields;
2161
+ const checks = [
2162
+ [slug_ok(key_field), SLUG_REASON],
2163
+ [matches(DEPTH_PATTERN, depth), DEPTH_REASON],
2164
+ [matches(DATE_PATTERN, date), DATE_REASON],
2165
+ [is_present(where), WHERE_REASON],
2166
+ [is_present(what), WHAT_REASON]
2167
+ ];
2168
+ return checks.find(([is_valid2]) => !is_valid2)?.[1];
2169
+ }
2170
+ function line_reason(line) {
2171
+ const fields = line.split(FIELD_SEPARATOR2);
2172
+ if (fields.length !== FIELD_COUNT) {
2173
+ return `expected ${String(FIELD_COUNT)} fields separated by " | " (a \`|\` in the last field is not allowed)`;
2174
+ }
2175
+ return field_reason(fields);
2176
+ }
2177
+ function broken_ledger_lines(content) {
2178
+ return content.split("\n").filter((line) => is_ledger_entry_line(line)).map((line) => ({ line, reason: line_reason(line) })).filter((entry) => entry.reason !== void 0);
2179
+ }
2180
+ function missing_lines(candidates, ledger) {
2181
+ const present = new Set(ledger.split("\n"));
2182
+ return candidates.filter((line) => !present.has(line));
2183
+ }
2184
+ var observation_ledger_line = {
2185
+ broken_ledger_lines,
2186
+ is_ledger_entry_line,
2187
+ line_reason,
2188
+ missing_lines
2189
+ };
2190
+
2191
+ // scripts/run/tidy/run-tidy.ts
2192
+ var LINE_SEPARATOR3 = "\n";
2193
+ var CLEANED_HEADER = "run:tidy \u2014 cleaned:";
2194
+ var KEPT_HEADER = "run:tidy \u2014 kept:";
2195
+ var CHANGES_REASON = "uncommitted changes";
2196
+ var UNPUSHED_REASON = "commits no remote has";
2197
+ var RUNNING_REASON = "held by a live run";
2198
+ var CLEAN = { kind: "clean" };
2199
+ function keep(reason) {
2200
+ return { kind: "keep", reason };
2201
+ }
2202
+ function lane_verdict(facts) {
2203
+ if (!facts.is_merged) return void 0;
2204
+ if (facts.has_changes) return keep(CHANGES_REASON);
2205
+ if (facts.has_unpushed) return keep(UNPUSHED_REASON);
2206
+ return facts.is_running ? keep(RUNNING_REASON) : CLEAN;
2207
+ }
2208
+ function unmerged_reason(unmerged) {
2209
+ const named = unmerged.map((issue) => session_cite.issue(issue)).join(", ");
2210
+ return `${named} not merged`;
2211
+ }
2212
+ function stash_verdict(facts, merged) {
2213
+ const unmerged = facts.issues.filter((issue) => !merged.has(issue));
2214
+ if (unmerged.length === facts.issues.length) return void 0;
2215
+ return unmerged.length === 0 ? CLEAN : keep(unmerged_reason(unmerged));
2216
+ }
2217
+ function ledger_carry(added, ledger) {
2218
+ const entries = added.filter((line) => observation_ledger_line.is_ledger_entry_line(line));
2219
+ return observation_ledger_line.missing_lines([...new Set(entries)], ledger);
2220
+ }
2221
+ function format_outcome(outcome) {
2222
+ const suffix = outcome.verdict.kind === "keep" ? ` \u2014 ${outcome.verdict.reason}` : "";
2223
+ return ` ${outcome.target}${suffix}`;
2224
+ }
2225
+ function section(header, outcomes) {
2226
+ return outcomes.length === 0 ? [] : [header, ...outcomes.map((item) => format_outcome(item))];
2227
+ }
2228
+ function format_report(outcomes) {
2229
+ const cleaned = outcomes.filter((outcome) => outcome.verdict.kind === "clean");
2230
+ const kept = outcomes.filter((outcome) => outcome.verdict.kind === "keep");
2231
+ const lines = [...section(CLEANED_HEADER, cleaned), ...section(KEPT_HEADER, kept)];
2232
+ return lines.length === 0 ? void 0 : lines.join(LINE_SEPARATOR3);
2233
+ }
2234
+ var run_tidy = { CLEAN, format_report, keep, lane_verdict, ledger_carry, stash_verdict };
2235
+
2236
+ // scripts/git/stash/git-stash.ts
2237
+ var ENTRY_SEPARATOR = "\n";
2238
+ var FIELD_SEPARATOR3 = "\0";
2239
+ var LIST_FORMAT = "--format=%gd%x00%gs";
2240
+ var SUBJECT_MESSAGE_SEPARATOR = ": ";
2241
+ var HASH_FORMAT = "--format=%H%x00%gs";
2242
+ var HASH_POSITION_FORMAT = "--format=%H%x00%gd";
2243
+ var NAME_ONLY_FLAG = "--name-only";
2244
+ var ADDED_PREFIX = "+";
2245
+ var FILE_HEADER_PREFIX = "+++";
2246
+ function to_entry(line) {
2247
+ const [selector, subject] = line.split(FIELD_SEPARATOR3);
2248
+ if (selector === void 0 || subject === void 0) return void 0;
2249
+ return { selector, subject };
2250
+ }
2251
+ function parse_entries(raw) {
2252
+ if (raw === "") return [];
2253
+ return raw.split(ENTRY_SEPARATOR).map((line) => to_entry(line)).filter((entry) => entry !== void 0);
2254
+ }
2255
+ function matches2(subject, message3) {
2256
+ return subject === message3 || subject.endsWith(`${SUBJECT_MESSAGE_SEPARATOR}${message3}`);
2257
+ }
2258
+ function select(entries, message3) {
2259
+ const found = entries.filter((entry) => matches2(entry.subject, message3));
2260
+ const [first] = found;
2261
+ if (first === void 0) return { kind: "none" };
2262
+ if (found.length > 1) {
2263
+ return { kind: "ambiguous", selectors: found.map((entry) => entry.selector) };
2264
+ }
2265
+ return { kind: "match", selector: first.selector };
2266
+ }
2267
+ function git_args(directory, args) {
2268
+ return directory === void 0 ? args : ["-C", directory, ...args];
2269
+ }
2270
+ async function list(directory) {
2271
+ return parse_entries(await git_spawn.read(git_args(directory, ["stash", "list", LIST_FORMAT])));
2272
+ }
2273
+ async function pop(selector, directory) {
2274
+ await git_spawn.read(git_args(directory, ["stash", "pop", selector]));
2275
+ }
2276
+ async function has_conflict(directory) {
2277
+ const unmerged = await git_spawn.read(
2278
+ git_args(directory, ["diff", NAME_ONLY_FLAG, "--diff-filter=U"])
2279
+ );
2280
+ return unmerged !== "";
2281
+ }
2282
+ async function has_changes(directory) {
2283
+ return await git_spawn.read(git_args(directory, ["status", "--porcelain"])) !== "";
2284
+ }
2285
+ async function push(message3, directory) {
2286
+ await git_spawn.read(git_args(directory, ["stash", "push", "-u", "-m", message3]));
2287
+ }
2288
+ async function list_by_hash(directory) {
2289
+ return parse_entries(await git_spawn.read(git_args(directory, ["stash", "list", HASH_FORMAT])));
2290
+ }
2291
+ async function changed_paths(hash, directory) {
2292
+ const raw = await git_spawn.read(
2293
+ git_args(directory, ["stash", "show", "--include-untracked", NAME_ONLY_FLAG, hash])
2294
+ );
2295
+ return raw === "" ? [] : raw.split(ENTRY_SEPARATOR);
2296
+ }
2297
+ async function untracked_lines(hash, file, directory) {
2298
+ try {
2299
+ const raw = await git_spawn.read(git_args(directory, ["show", `${hash}^3:${file}`]));
2300
+ return raw === "" ? [] : raw.split(ENTRY_SEPARATOR);
2301
+ } catch {
2302
+ return [];
2303
+ }
2304
+ }
2305
+ async function added_lines(hash, file, directory) {
2306
+ const diff = await git_spawn.read(git_args(directory, ["diff", `${hash}^1`, hash, "--", file]));
2307
+ if (diff === "") return await untracked_lines(hash, file, directory);
2308
+ return diff.split(ENTRY_SEPARATOR).filter((line) => line.startsWith(ADDED_PREFIX) && !line.startsWith(FILE_HEADER_PREFIX)).map((line) => line.slice(ADDED_PREFIX.length));
2309
+ }
2310
+ async function drop_by_hash(hash, directory) {
2311
+ const raw = await git_spawn.read(git_args(directory, ["stash", "list", HASH_POSITION_FORMAT]));
2312
+ const entry = parse_entries(raw).find((candidate) => candidate.selector === hash);
2313
+ if (entry === void 0) return false;
2314
+ await git_spawn.read(git_args(directory, ["stash", "drop", entry.subject]));
2315
+ return true;
2316
+ }
2317
+ function work_message(issue, occasion) {
2318
+ return `${issue}: uncommitted work at ${occasion}`;
2319
+ }
2320
+ var git_stash = {
2321
+ added_lines,
2322
+ changed_paths,
2323
+ drop_by_hash,
2324
+ has_changes,
2325
+ has_conflict,
2326
+ list,
2327
+ list_by_hash,
2328
+ matches: matches2,
2329
+ parse_entries,
2330
+ pop,
2331
+ push,
2332
+ select,
2333
+ work_message
2334
+ };
2335
+
2336
+ // scripts/lane/lane-close.ts
2337
+ import { existsSync, rmSync } from "node:fs";
2338
+ async function ignore_failure(action) {
2339
+ try {
2340
+ await action();
2341
+ } catch {
2342
+ }
2343
+ }
2344
+ function lane_targets(root, issue, lane) {
2345
+ return {
2346
+ directory: lane?.directory ?? lane_paths.lane_directory(root, issue),
2347
+ branch: lane?.branch ?? lane_paths.lane_branch(issue)
2348
+ };
2349
+ }
2350
+ function remove_directory(directory) {
2351
+ try {
2352
+ rmSync(directory, { force: true, recursive: true });
2353
+ } catch {
2354
+ }
2355
+ }
2356
+ async function remove_lane(targets) {
2357
+ await ignore_failure(async () => await git_worktree.worktree_remove(targets.directory));
2358
+ remove_directory(targets.directory);
2359
+ await ignore_failure(async () => await git_worktree.worktree_prune());
2360
+ await ignore_failure(async () => await git_worktree.branch_delete(targets.branch));
2361
+ }
2362
+ async function residue(targets) {
2363
+ const left = [];
2364
+ if (existsSync(targets.directory)) left.push(targets.directory);
2365
+ if (await git_command.branch_exists(targets.branch)) left.push(targets.branch);
2366
+ return left;
2367
+ }
2368
+ function close_kind(did_exist, left_behind) {
2369
+ if (left_behind.length > 0) return "incomplete";
2370
+ return did_exist ? "closed" : "none";
2371
+ }
2372
+ async function lane_root_directory() {
2373
+ return lane_paths.lane_root(await lane_registry.main_repository_root());
2374
+ }
2375
+ async function resolve_lane(issue) {
2376
+ const root = await lane_root_directory();
2377
+ const existing = lane_registry.find_lane(await lane_registry.list_lanes(), issue);
2378
+ return { existing, targets: lane_targets(root, issue, existing) };
2379
+ }
2380
+ async function close_lane(issue) {
2381
+ const { existing, targets } = await resolve_lane(issue);
2382
+ const did_exist = existing !== void 0 || existsSync(targets.directory);
2383
+ const reaped = lane_reap.reap_child(issue);
2384
+ await remove_lane(targets);
2385
+ const left_behind = await residue(targets);
2386
+ return { issue, kind: close_kind(did_exist, left_behind), left_behind, reaped };
2387
+ }
2388
+ async function try_close(issue) {
2389
+ try {
2390
+ const outcome = await close_lane(issue);
2391
+ return outcome.kind !== "incomplete";
2392
+ } catch (error) {
2393
+ error_text.trace_swallowed("lane_close.try_close", error);
2394
+ return false;
2395
+ }
2396
+ }
2397
+ async function close_each(issues) {
2398
+ const sweep2 = { closed: [], failed: [] };
2399
+ for (const issue of issues) {
2400
+ const bucket = await try_close(issue) ? sweep2.closed : sweep2.failed;
2401
+ bucket.push(issue);
2402
+ }
2403
+ return sweep2;
2404
+ }
2405
+ async function prune_lanes() {
2406
+ const lanes = await lane_registry.list_lanes();
2407
+ const { spent, kept } = await lane_spent.sort_lanes(lanes);
2408
+ const doomed = lanes.filter((lane) => lane.is_stranded || spent.includes(lane));
2409
+ const sweep2 = await close_each(doomed.map((lane) => lane.issue));
2410
+ return { ...sweep2, kept };
2411
+ }
2412
+ async function close_all_lanes() {
2413
+ const lanes = await lane_registry.list_lanes();
2414
+ return await close_each(lanes.map((lane) => lane.issue));
2415
+ }
2416
+ var lane_close = {
2417
+ close_all_lanes,
2418
+ close_lane,
2419
+ prune_lanes,
2420
+ resolve_lane
2421
+ };
2422
+
2423
+ // scripts/run/tidy/run-tidy-lanes.ts
2424
+ var CLOSED_KIND = "closed";
2425
+ var INCOMPLETE_REASON = "the close left files behind";
2426
+ var NOT_MERGED_REASON = "not merged";
2427
+ async function has_unpushed(directory) {
2428
+ try {
2429
+ const count = await git_spawn.read([
2430
+ "-C",
2431
+ directory,
2432
+ "rev-list",
2433
+ "--count",
2434
+ "HEAD",
2435
+ "--not",
2436
+ "--remotes"
2437
+ ]);
2438
+ return count !== "0";
2439
+ } catch (error) {
2440
+ error_text.trace_swallowed("run_tidy_lanes.has_unpushed", error);
2441
+ return true;
2442
+ }
2443
+ }
2444
+ async function hold_path_of(directory) {
2445
+ const git_directory = await git_spawn.read(["-C", directory, "rev-parse", "--absolute-git-dir"]);
2446
+ return run_hold.hold_path(git_directory);
2447
+ }
2448
+ function is_running(hold_path) {
2449
+ const read2 = run_hold.read_hold(hold_path);
2450
+ return read2.kind === "held" || read2.kind === "unreadable";
2451
+ }
2452
+ async function lane_verdict2(lane, hold_path) {
2453
+ const verdict = run_tidy.lane_verdict({
2454
+ issue: lane.issue,
2455
+ is_merged: true,
2456
+ has_changes: await git_stash.has_changes(lane.directory),
2457
+ has_unpushed: await has_unpushed(lane.directory),
2458
+ is_running: is_running(hold_path)
2459
+ });
2460
+ return verdict ?? run_tidy.keep(NOT_MERGED_REASON);
2461
+ }
2462
+ async function close(issue, hold_path) {
2463
+ const outcome = await lane_close.close_lane(issue);
2464
+ if (outcome.kind !== CLOSED_KIND) return run_tidy.keep(INCOMPLETE_REASON);
2465
+ run_hold.release_hold(hold_path);
2466
+ return run_tidy.CLEAN;
2467
+ }
2468
+ async function settle(lane) {
2469
+ try {
2470
+ const hold_path = await hold_path_of(lane.directory);
2471
+ const verdict = await lane_verdict2(lane, hold_path);
2472
+ return verdict.kind === "clean" ? await close(lane.issue, hold_path) : verdict;
2473
+ } catch (error) {
2474
+ return run_tidy.keep(String(error));
2475
+ }
2476
+ }
2477
+ async function tidy_lane(lane, is_merged, in_flight) {
2478
+ if (in_flight.has(lane.issue) || lane.is_stranded || !await is_merged(lane.issue)) {
2479
+ return void 0;
2480
+ }
2481
+ return { target: `lane ${session_cite.issue(lane.issue)}`, verdict: await settle(lane) };
2482
+ }
2483
+ async function tidy_lanes(is_merged, in_flight = /* @__PURE__ */ new Set()) {
2484
+ const outcomes = [];
2485
+ const lanes = await lane_registry.list_lanes();
2486
+ for (const lane of lanes) {
2487
+ const outcome = await tidy_lane(lane, is_merged, in_flight);
2488
+ if (outcome !== void 0) outcomes.push(outcome);
2489
+ }
2490
+ return outcomes;
2491
+ }
2492
+ var run_tidy_lanes = { has_unpushed, tidy_lanes };
2493
+
2494
+ // scripts/git/stash/stash-orphans.ts
2495
+ var HASH_PATTERN = /#(?<issue>\d+)\b/u;
2496
+ var LEADING_PATTERN = /(?:^|: )(?<issue>\d+): /u;
2497
+ var MESSAGE_PATTERNS = [HASH_PATTERN, LEADING_PATTERN];
2498
+ var ALL_ISSUE_PATTERN = new RegExp(HASH_PATTERN.source, "gu");
2499
+ var BRANCH_PATTERN = /^(?:WIP on|On) (?<issue>\d+)-lane: /u;
2500
+ var WIP_PREFIX = "WIP on ";
2501
+ var NO_PATTERNS = [];
2502
+ var HEADER = "Stashes whose issue is closed, or whose owner cannot be read \u2014 decide for each whether to revive or discard it (none is dropped automatically):";
2503
+ var FOOTER = 'Revive: `pnpm josh stash:pop "<message>"` \xB7 Discard: `git stash drop <selector>` \u2014 re-read the selector first, the stack is shared by every lane.';
2504
+ var UNKNOWN_OWNER = "owner unknown";
2505
+ var COLUMN_SEPARATOR = " ";
2506
+ var LINE_SEPARATOR4 = "\n";
2507
+ function patterns_for(subject) {
2508
+ if (subject.includes(run_preflight.STASH_LABEL_PREFIX)) return NO_PATTERNS;
2509
+ return subject.startsWith(WIP_PREFIX) ? [BRANCH_PATTERN] : [...MESSAGE_PATTERNS, BRANCH_PATTERN];
2510
+ }
2511
+ function issue_of2(subject) {
2512
+ for (const pattern of patterns_for(subject)) {
2513
+ const issue = pattern.exec(subject)?.groups?.["issue"];
2514
+ if (issue !== void 0) return issue;
2515
+ }
2516
+ return void 0;
2517
+ }
2518
+ function issues_of(entries) {
2519
+ const issues = entries.map((entry) => issue_of2(entry.subject));
2520
+ return [...new Set(issues.filter((issue) => issue !== void 0))];
2521
+ }
2522
+ function message_issues(subject) {
2523
+ const hashed = [...subject.matchAll(ALL_ISSUE_PATTERN)].map((match) => match.groups?.["issue"]);
2524
+ return [...hashed, LEADING_PATTERN.exec(subject)?.groups?.["issue"]];
2525
+ }
2526
+ function issues_named(subject) {
2527
+ const branch = BRANCH_PATTERN.exec(subject)?.groups?.["issue"];
2528
+ const named = subject.startsWith(WIP_PREFIX) ? [] : message_issues(subject);
2529
+ const found = [...named, branch].filter((issue) => issue !== void 0);
2530
+ return [...new Set(found)];
2531
+ }
2532
+ function orphans(entries, closed) {
2533
+ return entries.map((entry) => ({ ...entry, issue: issue_of2(entry.subject) })).filter((entry) => entry.issue === void 0 || closed.has(entry.issue));
2534
+ }
2535
+ function format_line(orphan) {
2536
+ const owner = orphan.issue === void 0 ? UNKNOWN_OWNER : `${session_cite.issue(orphan.issue)} closed`;
2537
+ return ` ${[orphan.selector, owner, orphan.subject].join(COLUMN_SEPARATOR)}`;
2538
+ }
2539
+ function format_report2(found) {
2540
+ if (found.length === 0) return void 0;
2541
+ return [HEADER, ...found.map((orphan) => format_line(orphan)), FOOTER].join(LINE_SEPARATOR4);
2542
+ }
2543
+ var stash_orphans = { format_report: format_report2, issue_of: issue_of2, issues_named, issues_of, orphans };
2544
+
2545
+ // scripts/git/stash/stash-sweep-lock.ts
2546
+ var LOCK_PREFIX = "josh-stash-sweep-lock-";
2547
+ var MAX_WAIT_MS = 3e4;
2548
+ function lock_path(cwd) {
2549
+ return repository_lock.lock_path(LOCK_PREFIX, cwd);
2550
+ }
2551
+ async function with_lock(work, target = lock_path(), max_wait_ms = MAX_WAIT_MS) {
2552
+ return await repository_lock.with_lock(work, target, max_wait_ms);
2553
+ }
2554
+ var stash_sweep_lock = { clear_stale: repository_lock.clear_stale, with_lock };
2555
+
2556
+ // scripts/observations/observation-ledger-home.ts
2557
+ import { appendFile, mkdir, readdir, readFile } from "node:fs/promises";
2558
+ import path3 from "node:path";
2559
+
2560
+ // scripts/observations/observation-ledger-migrate.ts
2561
+ import {
2562
+ appendFileSync,
2563
+ mkdirSync,
2564
+ readdirSync,
2565
+ readFileSync as readFileSync2,
2566
+ renameSync,
2567
+ rmdirSync,
2568
+ rmSync as rmSync2
2569
+ } from "node:fs";
2570
+ import path2 from "node:path";
2571
+ var NEWLINE = "\n";
2572
+ function claim_path(legacy) {
2573
+ return `${legacy}.${String(process.pid)}${MIGRATION_CLAIM_SUFFIX}`;
2574
+ }
2575
+ function claim(from, to) {
2576
+ try {
2577
+ renameSync(from, to);
2578
+ return true;
2579
+ } catch {
2580
+ return false;
2581
+ }
2582
+ }
2583
+ function is_stale_claim(name, legacy_name) {
2584
+ const prefix = `${legacy_name}.`;
2585
+ if (!name.startsWith(prefix) || !name.endsWith(MIGRATION_CLAIM_SUFFIX)) return false;
2586
+ const pid = Number(name.slice(prefix.length, -MIGRATION_CLAIM_SUFFIX.length));
2587
+ return Number.isSafeInteger(pid) && !process_identity.is_present_pid(pid);
2588
+ }
2589
+ function stale_claims(legacy) {
2590
+ const directory = path2.dirname(legacy);
2591
+ try {
2592
+ return readdirSync(directory).filter((name) => is_stale_claim(name, path2.basename(legacy))).map((name) => path2.join(directory, name));
2593
+ } catch {
2594
+ return [];
2595
+ }
2596
+ }
2597
+ function terminated(content) {
2598
+ return content.length === 0 || content.endsWith(NEWLINE) ? content : `${content}${NEWLINE}`;
2599
+ }
2600
+ function carried(content, ledger) {
2601
+ if (ledger === void 0) return terminated(content);
2602
+ const lines = content.split(NEWLINE).filter((line) => line.length > 0);
2603
+ const missing = observation_ledger_line.missing_lines(lines, ledger);
2604
+ return missing.length === 0 ? "" : `${missing.join(NEWLINE)}${NEWLINE}`;
2605
+ }
2606
+ function absorb(claimed, target) {
2607
+ mkdirSync(path2.dirname(target), { recursive: true });
2608
+ appendFileSync(
2609
+ target,
2610
+ carried(readFileSync2(claimed, "utf8"), file_reader.read_if_readable(target)),
2611
+ "utf8"
2612
+ );
2613
+ rmSync2(claimed);
2614
+ }
2615
+ function absorb_stale(legacy, target) {
2616
+ let count = 0;
2617
+ for (const stale of stale_claims(legacy)) {
2618
+ const claimed = claim_path(legacy);
2619
+ if (!claim(stale, claimed)) continue;
2620
+ absorb(claimed, target);
2621
+ count += 1;
2622
+ }
2623
+ return count;
2624
+ }
2625
+ function migrate_one(legacy, target) {
2626
+ const recovered = absorb_stale(legacy, target);
2627
+ const claimed = claim_path(legacy);
2628
+ if (!claim(legacy, claimed)) return recovered > 0;
2629
+ absorb(claimed, target);
2630
+ return true;
2631
+ }
2632
+ function source_name(name) {
2633
+ const end = name.indexOf(LEDGER_FILE_EXTENSION);
2634
+ return end === -1 ? void 0 : name.slice(0, end + LEDGER_FILE_EXTENSION.length);
2635
+ }
2636
+ function legacy_directory_names(directory) {
2637
+ try {
2638
+ const names = readdirSync(directory).map((name) => source_name(name));
2639
+ return [...new Set(names.filter((name) => name !== void 0))];
2640
+ } catch {
2641
+ return [];
2642
+ }
2643
+ }
2644
+ function migrate_directory(root) {
2645
+ const legacy_directory = path2.join(root, LEGACY_OBSERVATION_LEDGER_DIRECTORY);
2646
+ const did_move = legacy_directory_names(legacy_directory).map(
2647
+ (name) => migrate_one(
2648
+ path2.join(legacy_directory, name),
2649
+ path2.join(root, OBSERVATION_LEDGER_DIRECTORY, name)
2650
+ )
2651
+ ).some(Boolean);
2652
+ try {
2653
+ rmdirSync(legacy_directory);
2654
+ } catch {
2655
+ }
2656
+ return did_move;
2657
+ }
2658
+ function migrate(root) {
2659
+ const target = path2.join(root, OBSERVATION_LEDGER_DIRECTORY, LEGACY_LEDGER_FILE);
2660
+ const moved_files = LEGACY_OBSERVATION_LEDGER_PATHS.map(
2661
+ (legacy) => migrate_one(path2.join(root, legacy), target)
2662
+ );
2663
+ return [...moved_files, migrate_directory(root)].some(Boolean);
2664
+ }
2665
+ var observation_ledger_migrate = { migrate };
2666
+
2667
+ // scripts/observations/observation-ledger-home.ts
2668
+ var DATE_END = 10;
2669
+ function ledger_root(cwd = process.cwd()) {
2670
+ return cwd;
2671
+ }
2672
+ function ledger_directory(cwd = process.cwd()) {
2673
+ const root = ledger_root(cwd);
2674
+ observation_ledger_migrate.migrate(root);
2675
+ return path3.join(root, OBSERVATION_LEDGER_DIRECTORY);
2676
+ }
2677
+ function file_path(name, cwd) {
2678
+ ledger_directory(cwd);
2679
+ return path3.join(ledger_root(cwd), observation_ledger.ledger_file(name));
2680
+ }
2681
+ function issue_path(issue, cwd = process.cwd()) {
2682
+ return file_path(issue, cwd);
2683
+ }
2684
+ async function current_branch(cwd) {
2685
+ try {
2686
+ return await git_spawn.read(["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
2687
+ } catch {
2688
+ return "";
2689
+ }
2690
+ }
2691
+ function ledger_date(now) {
2692
+ return now.toISOString().slice(0, DATE_END);
2693
+ }
2694
+ async function writer_path(now, cwd = process.cwd()) {
2695
+ const issue = git_branch.issue_from_branch(await current_branch(cwd));
2696
+ return file_path(issue ?? ledger_date(now), cwd);
2697
+ }
2698
+ async function read_file(target) {
2699
+ try {
2700
+ const content = await readFile(target, "utf8");
2701
+ return content.length === 0 || content.endsWith("\n") ? content : `${content}
2702
+ `;
2703
+ } catch {
2704
+ return "";
2705
+ }
2706
+ }
2707
+ async function ledger_files(directory) {
2708
+ try {
2709
+ const names = await readdir(directory);
2710
+ return names.filter((name) => observation_ledger.is_ledger_file_name(name)).toSorted((left, right) => left.localeCompare(right));
2711
+ } catch {
2712
+ return void 0;
2713
+ }
2714
+ }
2715
+ async function read(cwd = process.cwd()) {
2716
+ const directory = ledger_directory(cwd);
2717
+ const files = await ledger_files(directory);
2718
+ if (files === void 0) return void 0;
2719
+ const contents = await Promise.all(
2720
+ files.map(async (name) => await read_file(path3.join(directory, name)))
2721
+ );
2722
+ return contents.join("");
2723
+ }
2724
+ async function append(target, lines) {
2725
+ if (lines.length === 0) return;
2726
+ await mkdir(path3.dirname(target), { recursive: true });
2727
+ await appendFile(target, `${lines.join("\n")}
2728
+ `, "utf8");
2729
+ }
2730
+ var observation_ledger_home = {
2731
+ append,
2732
+ file_path,
2733
+ issue_path,
2734
+ ledger_date,
2735
+ ledger_root,
2736
+ read,
2737
+ writer_path
2738
+ };
2739
+
2740
+ // scripts/run/tidy/run-tidy-stashes.ts
2741
+ var READ_CONCURRENCY2 = 8;
2742
+ var GONE_REASON = "already dropped by another run";
2743
+ var BUSY_REASON = "another run held the stash sweep lock";
2744
+ async function added_ledger_lines(hash, ledger_paths) {
2745
+ const added = await bounded_pool.bounded_map(
2746
+ ledger_paths,
2747
+ READ_CONCURRENCY2,
2748
+ async (ledger_path) => await git_stash.added_lines(hash, ledger_path)
2749
+ );
2750
+ return added.flat();
2751
+ }
2752
+ async function carry_ledger(hash) {
2753
+ const changed = await git_stash.changed_paths(hash);
2754
+ const ledger_paths = changed.filter((file_path2) => observation_ledger.is_ledger_path(file_path2));
2755
+ if (ledger_paths.length === 0) return 0;
2756
+ const ledger_path = await observation_ledger_home.writer_path(/* @__PURE__ */ new Date());
2757
+ const added = await added_ledger_lines(hash, ledger_paths);
2758
+ const carried2 = run_tidy.ledger_carry(added, await observation_ledger_home.read() ?? "");
2759
+ await observation_ledger_home.append(ledger_path, carried2);
2760
+ return carried2.length;
2761
+ }
2762
+ function carried_note(count) {
2763
+ return count === 0 ? "" : ` (${String(count)} observation line(s) carried to the ledger)`;
2764
+ }
2765
+ async function drop(entry) {
2766
+ const carried2 = await carry_ledger(entry.selector);
2767
+ const target = `stash "${entry.subject}"${carried_note(carried2)}`;
2768
+ const is_dropped = await git_stash.drop_by_hash(entry.selector);
2769
+ return { target, verdict: is_dropped ? run_tidy.CLEAN : run_tidy.keep(GONE_REASON) };
2770
+ }
2771
+ async function merged_set(issues, is_merged) {
2772
+ const answers = await bounded_pool.bounded_map(
2773
+ issues,
2774
+ READ_CONCURRENCY2,
2775
+ async (issue) => await is_merged(issue)
2776
+ );
2777
+ return new Set(issues.filter((_issue, index) => answers[index] === true));
2778
+ }
2779
+ function entry_verdict(entry, merged) {
2780
+ const issues = stash_orphans.issues_named(entry.subject);
2781
+ return run_tidy.stash_verdict({ subject: entry.subject, issues }, merged);
2782
+ }
2783
+ async function tidy_entry(entry, merged) {
2784
+ const verdict = entry_verdict(entry, merged);
2785
+ if (verdict === void 0) return void 0;
2786
+ if (verdict.kind === "keep") return { target: `stash "${entry.subject}"`, verdict };
2787
+ try {
2788
+ return await drop(entry);
2789
+ } catch (error) {
2790
+ return { target: `stash "${entry.subject}"`, verdict: run_tidy.keep(String(error)) };
2791
+ }
2792
+ }
2793
+ async function tidy_entries(entries, merged) {
2794
+ const outcomes = [];
2795
+ for (const entry of entries) {
2796
+ const outcome = await tidy_entry(entry, merged);
2797
+ if (outcome !== void 0) outcomes.push(outcome);
2798
+ }
2799
+ return outcomes;
2800
+ }
2801
+ function busy_outcome(entry, merged) {
2802
+ const verdict = entry_verdict(entry, merged);
2803
+ if (verdict === void 0) return void 0;
2804
+ return {
2805
+ target: `stash "${entry.subject}"`,
2806
+ verdict: verdict.kind === "keep" ? verdict : run_tidy.keep(BUSY_REASON)
2807
+ };
2808
+ }
2809
+ function busy_outcomes(entries, merged) {
2810
+ return entries.map((entry) => busy_outcome(entry, merged)).filter((outcome) => outcome !== void 0);
2811
+ }
2812
+ async function tidy_stashes(is_merged) {
2813
+ const entries = await git_stash.list_by_hash();
2814
+ const issues = [...new Set(entries.flatMap((entry) => stash_orphans.issues_named(entry.subject)))];
2815
+ const merged = await merged_set(issues, is_merged);
2816
+ const outcomes = await stash_sweep_lock.with_lock(async () => await tidy_entries(entries, merged));
2817
+ return outcomes ?? busy_outcomes(entries, merged);
2818
+ }
2819
+ var run_tidy_stashes = { tidy_stashes };
2820
+
2821
+ // scripts/run/tidy/run-tidy-cli.ts
2822
+ var FAILURE_NOTE = "run:tidy could not finish; merged lanes and stashes may remain:";
2823
+ function memoized(read2) {
2824
+ const answers = /* @__PURE__ */ new Map();
2825
+ return async function read_once(issue) {
2826
+ const cached = answers.get(issue) ?? read2(issue);
2827
+ answers.set(issue, cached);
2828
+ return await cached;
2829
+ };
2830
+ }
2831
+ async function sweep() {
2832
+ try {
2833
+ const is_merged = memoized(issue_merged.read_merged);
2834
+ const in_flight = backlog_drive_restore.active_issues(
2835
+ await run_event_stream_emit.current_events()
2836
+ );
2837
+ const lanes = await run_tidy_lanes.tidy_lanes(is_merged, in_flight);
2838
+ const stashes = await run_tidy_stashes.tidy_stashes(is_merged);
2839
+ const report6 = run_tidy.format_report([...lanes, ...stashes]);
2840
+ if (report6 !== void 0) console.error(report6);
2841
+ } catch (error) {
2842
+ console.error(FAILURE_NOTE, error);
2843
+ }
2844
+ }
2845
+ var run_tidy_cli = { sweep };
2846
+ if (process.argv[1] === fileURLToPath3(import.meta.url)) await sweep();
2847
+
2848
+ // scripts/run/hold/run-hold-cli.ts
2849
+ var SUCCESS_EXIT_CODE2 = 0;
2850
+ var FAILURE_EXIT_CODE4 = 1;
2851
+ var ARGV_OFFSET3 = 2;
2852
+ var RELEASE_FLAG = "--release";
2853
+ var FORCE_FLAG = "--force";
2854
+ var FULLRUN_FLAG = "--fullrun";
2855
+ var HALFRUN_STOP_FLAG = "--halfrun-stop";
2856
+ var PRRUN_STOP_FLAG = "--prrun-stop";
2857
+ var MAX_CLAIM_ARGUMENTS = 2;
2858
+ var USAGE3 = "Usage: josh run:hold [<issue-number> [--fullrun | --halfrun-stop | --prrun-stop]] | josh run:release [<issue-number> | --force]";
2859
+ var COMMAND = "run:hold";
2860
+ var HOLD_VERDICT = "hold";
2861
+ var BUSY_VERDICT = "busy";
2862
+ var UNKNOWN_VERDICT = "unknown";
2863
+ var RELEASED_VERDICT = "released";
2864
+ var NONE_VERDICT = "none";
2865
+ var HELD_VERDICT = "held";
2866
+ var FORCE_RELEASE_KIND = "force-release";
2867
+ var HALFRUN_STOP_KIND = "halfrun-stop";
2868
+ var PRRUN_STOP_KIND = "prrun-stop";
2869
+ function is_stop_request(request) {
2870
+ return request.kind === HALFRUN_STOP_KIND || request.kind === PRRUN_STOP_KIND;
2871
+ }
2872
+ var FORCE_RELEASE_REQUEST = { kind: FORCE_RELEASE_KIND };
2873
+ function release_request(claimant) {
2874
+ return { kind: "release", claimant };
2875
+ }
2876
+ var STOP_KIND_BY_FLAG = /* @__PURE__ */ new Map([
2877
+ [HALFRUN_STOP_FLAG, HALFRUN_STOP_KIND],
2878
+ [PRRUN_STOP_FLAG, PRRUN_STOP_KIND]
2879
+ ]);
2880
+ function numbered_claim(issue, flag) {
2881
+ if (flag === void 0) return { kind: "claim", issue };
2882
+ const stop_kind = STOP_KIND_BY_FLAG.get(flag);
2883
+ if (stop_kind !== void 0) return { kind: stop_kind, issue };
2884
+ return flag === FULLRUN_FLAG ? { kind: "claim", issue, is_fullrun: true } : void 0;
2885
+ }
2886
+ function parse_claim(argv) {
2887
+ const [first, flag] = argv;
2888
+ if (first === void 0) return { kind: "claim", issue: run_hold.UNNUMBERED_ISSUE };
2889
+ if (argv.length > MAX_CLAIM_ARGUMENTS || !issue_number_shape.ISSUE_NUMBER_PATTERN.test(first)) {
2890
+ return void 0;
2891
+ }
2892
+ return numbered_claim(first, flag);
2893
+ }
2894
+ function parse_release_argument(first) {
2895
+ if (first === FORCE_FLAG) return FORCE_RELEASE_REQUEST;
2896
+ return issue_number_shape.ISSUE_NUMBER_PATTERN.test(first) ? release_request(first) : void 0;
2897
+ }
2898
+ function parse_release(argv) {
2899
+ const [first] = argv;
2900
+ if (first === void 0) return release_request(run_hold.UNNUMBERED_ISSUE);
2901
+ return argv.length > 1 ? void 0 : parse_release_argument(first);
2902
+ }
2903
+ function parse_request(argv) {
2904
+ if (argv[0] === RELEASE_FLAG) return parse_release(argv.slice(1));
2905
+ return parse_claim(argv);
2906
+ }
2907
+ function report3(message3, verdict, code) {
2908
+ console.error(message3);
2909
+ console.info(verdict);
2910
+ return code;
2911
+ }
2912
+ function report_busy(message3) {
2913
+ return report3(message3, BUSY_VERDICT, SUCCESS_EXIT_CODE2);
2914
+ }
2915
+ function report_hold() {
2916
+ console.info(HOLD_VERDICT);
2917
+ return SUCCESS_EXIT_CODE2;
2918
+ }
2919
+ function held_block(hold, issue) {
2920
+ return run_hold.is_reentry(hold, issue) ? void 0 : run_hold.held_message(hold);
2921
+ }
2922
+ function is_own_unstopped(hold, issue) {
2923
+ return run_hold.is_reentry(hold, issue) && !run_hold.is_stop_marked(hold);
2924
+ }
2925
+ async function stale_block(hold, issue) {
2926
+ if (is_own_unstopped(hold, issue)) return void 0;
2927
+ return await run_hold.is_tree_dirty() ? run_hold.uncommitted_message(hold) : void 0;
2928
+ }
2929
+ async function blocking_message(read2, issue) {
2930
+ if (read2.kind === "held") return held_block(read2.hold, issue);
2931
+ if (read2.kind === "unreadable") return run_hold.unreadable_message();
2932
+ return read2.kind === "stale" ? await stale_block(read2.hold, issue) : void 0;
2933
+ }
2934
+ function take_free_tree(target, request) {
2935
+ const is_taken = run_hold.create_hold(target, request.issue, /* @__PURE__ */ new Date(), request.is_fullrun);
2936
+ if (is_taken) return report_hold();
2937
+ return report_busy(run_hold.race_message(run_hold.read_hold(target), target));
2938
+ }
2939
+ function replace_stale(target, request, hold) {
2940
+ console.error(run_hold.stale_message(hold));
2941
+ run_hold.release_hold(target);
2942
+ return take_free_tree(target, request);
2943
+ }
2944
+ function take_tree(target, request, read2) {
2945
+ if (read2.kind === "held") return report_hold();
2946
+ return read2.kind === "stale" ? replace_stale(target, request, read2.hold) : take_free_tree(target, request);
2947
+ }
2948
+ function report_preflight(decision) {
2949
+ console.error(`${decision.reason}
2950
+ ${decision.advice}`);
2951
+ console.info(decision.verdict);
2952
+ return SUCCESS_EXIT_CODE2;
2953
+ }
2954
+ async function preflight_gate(issue, is_linked) {
2955
+ if (is_linked || issue === run_hold.UNNUMBERED_ISSUE) return void 0;
2956
+ const decision = await run_preflight.check(issue);
2957
+ if (decision.verdict === run_preflight.CLEAN_VERDICT) return void 0;
2958
+ return report_preflight(decision);
2959
+ }
2960
+ async function claim2(target, request, is_linked) {
2961
+ const gate = await preflight_gate(request.issue, is_linked);
2962
+ if (gate !== void 0) return gate;
2963
+ const read2 = run_hold.read_hold(target);
2964
+ const blocked = await blocking_message(read2, request.issue);
2965
+ if (blocked !== void 0) return report_busy(blocked);
2966
+ const code = take_tree(target, request, read2);
2967
+ if (code === SUCCESS_EXIT_CODE2) await run_tidy_cli.sweep();
2968
+ return code;
2969
+ }
2970
+ function remove_record(target, was_present) {
2971
+ run_hold.release_hold(target);
2972
+ console.info(was_present ? RELEASED_VERDICT : NONE_VERDICT);
2973
+ return SUCCESS_EXIT_CODE2;
2974
+ }
2975
+ function report_held(hold) {
2976
+ return report3(run_hold.foreign_release_message(hold), HELD_VERDICT, FAILURE_EXIT_CODE4);
2977
+ }
2978
+ function report_unreadable_record() {
2979
+ return report3(run_hold.unreadable_message(), UNKNOWN_VERDICT, FAILURE_EXIT_CODE4);
2980
+ }
2981
+ function release_record(target, claimant) {
2982
+ const read2 = run_hold.read_hold(target);
2983
+ if (read2.kind === "free") return remove_record(target, false);
2984
+ if (read2.kind === "unreadable") return report_unreadable_record();
2985
+ if (!run_hold.is_own_hold(read2.hold, claimant)) return report_held(read2.hold);
2986
+ return remove_record(target, true);
2987
+ }
2988
+ async function release(target, claimant) {
2989
+ const code = release_record(target, claimant);
2990
+ const is_own_end = code === SUCCESS_EXIT_CODE2 && claimant !== run_hold.UNNUMBERED_ISSUE;
2991
+ if (is_own_end) await run_label.unmark(claimant);
2992
+ return code;
2993
+ }
2994
+ function force_release(target) {
2995
+ const read2 = run_hold.read_hold(target);
2996
+ if (read2.kind === "held" || read2.kind === "stale") {
2997
+ console.error(run_hold.forced_release_message(read2.hold));
2998
+ }
2999
+ return remove_record(target, read2.kind !== "free");
3000
+ }
3001
+ function report_unknown() {
3002
+ return report3(run_hold.unknown_message(), UNKNOWN_VERDICT, FAILURE_EXIT_CODE4);
3003
+ }
3004
+ function report_usage() {
3005
+ console.error(USAGE3);
3006
+ return FAILURE_EXIT_CODE4;
3007
+ }
3008
+ async function mark_stop(target, request) {
3009
+ const mark2 = request.kind === PRRUN_STOP_KIND ? await run_prrun_resume.mark_stop_at(target, request.issue) : run_halfrun_resume.mark_stop_at(target, request.issue);
3010
+ if (mark2 === run_halfrun_resume.MARKED) return report_hold();
3011
+ if (mark2 === run_halfrun_resume.UNREADABLE) return report_unreadable_record();
3012
+ return report_busy(run_hold.race_message(run_hold.read_hold(target), target));
3013
+ }
3014
+ async function dispatch(request, target, is_linked) {
3015
+ if (request.kind === "claim") return await claim2(target, request, is_linked);
3016
+ if (is_stop_request(request)) return await mark_stop(target, request);
3017
+ if (request.kind === FORCE_RELEASE_KIND) return force_release(target);
3018
+ return await release(target, request.claimant);
3019
+ }
3020
+ async function answer(request) {
3021
+ const directory = await run_cli_fault.directory_of(COMMAND, run_hold.worktree_directory);
3022
+ if (directory === void 0) return report_unknown();
3023
+ const is_linked = await run_hold.is_linked_worktree();
3024
+ return await dispatch(request, run_hold.hold_path(directory), is_linked);
3025
+ }
3026
+ async function run2(argv) {
3027
+ const request = parse_request(argv);
3028
+ if (request === void 0) return report_usage();
3029
+ try {
3030
+ return await answer(request);
3031
+ } catch (error) {
3032
+ return report3(run_cli_fault.message(COMMAND, error), UNKNOWN_VERDICT, FAILURE_EXIT_CODE4);
3033
+ }
3034
+ }
3035
+ async function main3(argv) {
3036
+ process.exitCode = await run2(argv);
3037
+ }
3038
+ var run_hold_cli = {
3039
+ BUSY_VERDICT,
3040
+ HELD_VERDICT,
3041
+ HOLD_VERDICT,
3042
+ NONE_VERDICT,
3043
+ RELEASED_VERDICT,
3044
+ UNKNOWN_VERDICT,
3045
+ USAGE: USAGE3,
3046
+ parse_request,
3047
+ run: run2
3048
+ };
3049
+ if (process.argv[1] === fileURLToPath4(import.meta.url)) await main3(process.argv.slice(ARGV_OFFSET3));
3050
+
3051
+ // scripts/run/hold/run-hold-session.ts
3052
+ function is_same_start(recorded, own) {
3053
+ return recorded === void 0 || own === void 0 || recorded === own;
3054
+ }
3055
+ function is_session_hold(hold, owner = run_hold.session_owner()) {
3056
+ if (hold.owner_pid === void 0 || owner.owner_pid === void 0) return true;
3057
+ return hold.owner_pid === owner.owner_pid && is_same_start(hold.owner_start, owner.owner_start);
3058
+ }
3059
+ function is_notified_since(hold, pause_start2) {
3060
+ if (pause_start2 === void 0 || hold.notified_at === void 0) return false;
3061
+ return Date.parse(hold.notified_at) >= Date.parse(pause_start2);
3062
+ }
3063
+ function mark_notified(target, now = /* @__PURE__ */ new Date()) {
3064
+ const hold = run_halfrun_resume.hold_of(run_hold.read_hold(target, now));
3065
+ if (hold === void 0) return;
3066
+ stamp_file.write_stamp(target, { ...hold, notified_at: now.toISOString() });
3067
+ }
3068
+ var run_hold_session = { is_notified_since, is_session_hold, mark_notified };
3069
+
3070
+ // scripts/hooks/stop-actions.ts
3071
+ var SUCCESS_EXIT_CODE3 = 0;
3072
+ var NOTICE_SEPARATOR = "\n";
3073
+ var SUMMARY_CHARS = 600;
3074
+ var TOKEN_CHAR = /^[!-~]$/u;
3075
+ var EMPTY_SUMMARY = "The run stopped and is waiting for you.";
3076
+ var SEQUENTIAL = 1;
3077
+ function token_start(text2) {
3078
+ let index = text2.length;
3079
+ while (index > 0 && TOKEN_CHAR.test(text2.charAt(index - 1))) index -= 1;
3080
+ return index;
3081
+ }
3082
+ function summary_of(message3) {
3083
+ const trimmed = message3.trim();
3084
+ if (trimmed === "") return EMPTY_SUMMARY;
3085
+ if (trimmed.length <= SUMMARY_CHARS) return trimmed;
3086
+ const overshoot = trimmed.slice(0, SUMMARY_CHARS + 1);
3087
+ const cut = overshoot.slice(0, Math.min(token_start(overshoot), SUMMARY_CHARS));
3088
+ return cut === "" ? trimmed.slice(0, SUMMARY_CHARS) : cut;
3089
+ }
3090
+ async function issue_url_of(issue) {
3091
+ if (issue === run_hold.UNNUMBERED_ISSUE) return void 0;
3092
+ const slug = await git_gh_repo.repo_get_name_with_owner();
3093
+ return slug === void 0 ? void 0 : issue_cite.issue_url(slug, issue);
3094
+ }
3095
+ function notify_values(issue_url, action) {
3096
+ const { recommended, alternative } = action.decision;
3097
+ const values = {
3098
+ "task-type": "confirmation",
3099
+ body: session_cite.text(summary_of(action.message)),
3100
+ recommended,
3101
+ ...alternative !== void 0 && { alternative: [alternative] }
3102
+ };
3103
+ return issue_url === void 0 ? values : { ...values, "issue-url": issue_url };
3104
+ }
3105
+ async function mark_notified2() {
3106
+ try {
3107
+ const directory = await run_hold.worktree_directory();
3108
+ if (directory !== void 0) run_hold_session.mark_notified(run_hold.hold_path(directory));
3109
+ } catch (error) {
3110
+ console.warn(`stop:guard could not stamp the notify on the hold: ${String(error)}`);
3111
+ }
3112
+ }
3113
+ async function notify(action) {
3114
+ const issue_url = await issue_url_of(action.issue);
3115
+ await telegram_test.send_values(notify_values(issue_url, action));
3116
+ await mark_notified2();
3117
+ return `\u{1F4F1} stop:guard sent the confirmation notify (${issue_url ?? action.issue}).`;
3118
+ }
3119
+ async function release2(issue) {
3120
+ const argv = issue === run_hold.UNNUMBERED_ISSUE ? ["--release"] : ["--release", issue];
3121
+ const code = await run_hold_cli.run(argv);
3122
+ return code === SUCCESS_EXIT_CODE3 ? "\u{1F513} stop:guard released the run:hold record." : void 0;
3123
+ }
3124
+ async function cite2(references) {
3125
+ const cited = await issue_cite_cli.cite(issue_citation.cite_arguments(references));
3126
+ if (cited === void 0 || cited.failures.length > 0) return void 0;
3127
+ return ["Issue citations:", ...cited.lines].join(NOTICE_SEPARATOR);
3128
+ }
3129
+ async function run_action(action) {
3130
+ if (action.kind === "notify") return await notify(action);
3131
+ if (action.kind === "release") return await release2(action.issue);
3132
+ return await cite2(action.references);
3133
+ }
3134
+ function set_stdout_write(stream_write) {
3135
+ process.stdout.write = stream_write;
3136
+ }
3137
+ async function on_stderr(work) {
3138
+ const saved = { write: process.stdout.write.bind(process.stdout) };
3139
+ set_stdout_write(process.stderr.write.bind(process.stderr));
3140
+ try {
3141
+ return await work();
3142
+ } finally {
3143
+ set_stdout_write(saved.write);
3144
+ }
3145
+ }
3146
+ async function attempt(action) {
3147
+ try {
3148
+ return await run_action(action);
3149
+ } catch {
3150
+ return void 0;
3151
+ }
3152
+ }
3153
+ function is_notice(notice) {
3154
+ return notice !== void 0;
3155
+ }
3156
+ async function settle2(actions) {
3157
+ const notices = await on_stderr(
3158
+ async () => await bounded_pool.bounded_map(actions, SEQUENTIAL, attempt)
3159
+ );
3160
+ const failed = actions.find((_action, index) => notices[index] === void 0);
3161
+ return {
3162
+ notices: notices.filter((notice) => is_notice(notice)),
3163
+ failure: failed === void 0 ? void 0 : stop_rules.fallback_reason(failed)
3164
+ };
3165
+ }
3166
+ function joined(notices) {
3167
+ return notices.length === 0 ? void 0 : notices.join(NOTICE_SEPARATOR);
3168
+ }
3169
+ function settled_envelope(settled, is_blocking, lang) {
3170
+ const notice = joined(settled.notices);
3171
+ if (is_blocking && settled.failure !== void 0) {
3172
+ return stop_rules.block_envelope(settled.failure, lang, notice);
3173
+ }
3174
+ return notice === void 0 ? void 0 : stop_rules.notice_envelope(notice);
3175
+ }
3176
+ async function envelope_of(outcome, lang) {
3177
+ if (outcome.reason !== void 0) return stop_rules.block_envelope(outcome.reason, lang);
3178
+ const settled = await settle2(outcome.actions);
3179
+ return settled_envelope(settled, outcome.blocks_on_failure, lang);
3180
+ }
3181
+ var stop_actions2 = { envelope_of, summary_of };
3182
+
3183
+ // scripts/rules/last-prompt.ts
3184
+ import { z as z5 } from "zod";
3185
+ var WORKFLOW_KEYWORDS = /* @__PURE__ */ new Set([
3186
+ "kickoff",
3187
+ "fullrun",
3188
+ "halfrun",
3189
+ "prrun",
3190
+ "backlogrun"
3191
+ ]);
3192
+ var NO_PROMPT = "";
3193
+ var HUMAN_ORIGIN = "human";
3194
+ var KEYWORD_BEFORE_ARGUMENT = -2;
3195
+ var PROMPT_LINE_SCHEMA = z5.object({
3196
+ isMeta: z5.boolean().nullish(),
3197
+ timestamp: z5.string().nullish(),
3198
+ origin: z5.object({ kind: z5.string().nullish() }).nullish(),
3199
+ message: z5.object({ content: z5.unknown() })
3200
+ });
3201
+ var TIMED_LINE_SCHEMA = z5.object({ timestamp: z5.string() });
3202
+ function is_person(line) {
3203
+ if (line.isMeta === true) return false;
3204
+ return line.origin === null || line.origin === void 0 || line.origin.kind === HUMAN_ORIGIN;
3205
+ }
3206
+ function person_line(line) {
3207
+ const parsed = PROMPT_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line));
3208
+ return parsed.success && is_person(parsed.data) ? parsed.data : void 0;
3209
+ }
3210
+ function person_text(line) {
3211
+ const person = person_line(line);
3212
+ if (person === void 0) return void 0;
3213
+ return time_reported_failure.result_text(person.message.content);
3214
+ }
3215
+ function is_invocation(prompt) {
3216
+ const words = prompt.trim().split(/\s+/u);
3217
+ return [words.at(0), words.at(KEYWORD_BEFORE_ARGUMENT)].some(
3218
+ (word) => WORKFLOW_KEYWORDS.has(word ?? NO_PROMPT)
3219
+ );
3220
+ }
3221
+ function prompt_line_text(line) {
3222
+ return filing_cap.is_prompt_line(line) ? person_text(line) : void 0;
3223
+ }
3224
+ function prompt_text(tail) {
3225
+ const text2 = tail.split("\n").map((line) => prompt_line_text(line)).findLast((one) => one !== void 0);
3226
+ return text2 === void 0 || is_invocation(text2) ? NO_PROMPT : text2;
3227
+ }
3228
+ function turn_prompt(tail) {
3229
+ const opener = tail.split("\n").findLast((line) => filing_cap.is_prompt_line(line));
3230
+ if (opener === void 0) return NO_PROMPT;
3231
+ return person_text(opener) ?? NO_PROMPT;
3232
+ }
3233
+ function prompt_time(tail) {
3234
+ return tail.split("\n").filter((line) => filing_cap.is_prompt_line(line)).map((line) => person_line(line)?.timestamp ?? void 0).findLast((time) => time !== void 0);
3235
+ }
3236
+ function tail_time(tail) {
3237
+ return tail.split("\n").map((line) => TIMED_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line)).data?.timestamp).find((time) => time !== void 0);
3238
+ }
3239
+ function pause_start(tail) {
3240
+ return prompt_time(tail) ?? tail_time(tail);
3241
+ }
3242
+ var last_prompt = { pause_start, prompt_text, turn_prompt };
3243
+
3244
+ // scripts/run/cut/run-cut-report.ts
3245
+ var SUCCESS_EXIT_CODE4 = 0;
3246
+ var FAILURE_EXIT_CODE5 = 1;
3247
+ var CUT_VERDICT = "cut";
3248
+ var RESUME_VERDICT = "resume";
3249
+ var RESUME_IMPL_VERDICT = "resume-impl";
3250
+ var FRESH_VERDICT = "fresh";
3251
+ var UNDER_THRESHOLD_VERDICT = "under-threshold";
3252
+ var STALE_VERDICT = "stale";
3253
+ var INCOMPLETE_VERDICT = "incomplete";
3254
+ var BAD_HANDOFF_VERDICT = "bad-handoff";
3255
+ var HANDED_OFF_VERDICT = "handed-off";
3256
+ var BUSY_VERDICT2 = "busy";
3257
+ var NOT_A_LANE_VERDICT = "not-a-lane";
3258
+ var UNREADY_VERDICT = "unready";
3259
+ var FAILED_VERDICT = "failed";
3260
+ var ENDED_VERDICT = "ended";
3261
+ var UNREADABLE_VERDICT = "unreadable";
3262
+ var UNKNOWN_VERDICT2 = "unknown";
3263
+ var OVER_VERDICT = "over";
3264
+ function report4(verdict, code) {
3265
+ console.info(verdict);
3266
+ return code;
3267
+ }
3268
+ function report_stale(cut_record) {
3269
+ console.error(run_cut.stale_message(cut_record));
3270
+ return report4(STALE_VERDICT, FAILURE_EXIT_CODE5);
3271
+ }
3272
+ function report_busy2(cut_record) {
3273
+ console.error(run_cut.busy_message(cut_record));
3274
+ return report4(BUSY_VERDICT2, FAILURE_EXIT_CODE5);
3275
+ }
3276
+ function report_incomplete(cut_record) {
3277
+ console.error(run_cut.incomplete_message(cut_record));
3278
+ return report4(INCOMPLETE_VERDICT, FAILURE_EXIT_CODE5);
3279
+ }
3280
+ var HANDOFF_OVERFLOW_NOTE = "--handoff <path> would grow the cut record past its byte bound";
3281
+ var HANDOFF_MISSING_NOTE = "this cut resumes into implementation and needs --handoff <path>";
3282
+ function report_bad_handoff(note) {
3283
+ console.error(
3284
+ `${note}. Nothing was cut; write the handoff file at ${run_cut_handoff.HANDOFF_PATH} and reissue.`
3285
+ );
3286
+ return report4(BAD_HANDOFF_VERDICT, FAILURE_EXIT_CODE5);
3287
+ }
3288
+ function report_handed_off(cut_record) {
3289
+ console.error(run_cut.handed_off_message(cut_record));
3290
+ return report4(HANDED_OFF_VERDICT, SUCCESS_EXIT_CODE4);
3291
+ }
3292
+ function report_unreadable() {
3293
+ console.error(run_cut.unreadable_message());
3294
+ return report4(UNREADABLE_VERDICT, FAILURE_EXIT_CODE5);
3295
+ }
3296
+ function report_fault(message3) {
3297
+ console.error(message3);
3298
+ return report4(UNKNOWN_VERDICT2, FAILURE_EXIT_CODE5);
3299
+ }
3300
+ function report_unknown2() {
3301
+ return report_fault(run_cut.unknown_message());
3302
+ }
3303
+ function refuse_unready(state) {
3304
+ console.error(
3305
+ `Not ready to cut on ${state.branch} (dirty: ${String(state.is_dirty)}); a cut needs an uncommitted lane branch. Nothing was cut.`
3306
+ );
3307
+ return report4(UNREADY_VERDICT, FAILURE_EXIT_CODE5);
3308
+ }
3309
+ function report_under_threshold() {
3310
+ console.error(
3311
+ "The current context is under the shared cut threshold, so nothing was cut; a resume would cost more than the accumulation it would drop. Continue to the gate."
3312
+ );
3313
+ return report4(UNDER_THRESHOLD_VERDICT, SUCCESS_EXIT_CODE4);
3314
+ }
3315
+ function report_reviewer() {
3316
+ console.error(
3317
+ "This session is a ship reviewer, which its supervisor is waiting on, so nothing was cut and nothing was relaunched. Write the findings file and finish the review in this process."
3318
+ );
3319
+ return report4(NOT_A_LANE_VERDICT, SUCCESS_EXIT_CODE4);
3320
+ }
3321
+ function report_held_cut(issue) {
3322
+ console.error(
3323
+ `The cut is recorded for ${session_cite.issue(issue)} outside a lane, so nothing was relaunched. Keep the hold, send a \`confirmation\` Telegram whose body names the resume command \`fullrun ${issue_cite.plain(issue)}\`, and end the turn; the fresh session's \`pnpm josh run:cut --resume ${issue}\` answers \`resume-impl\` with the handoff.`
3324
+ );
3325
+ return report4(CUT_VERDICT, SUCCESS_EXIT_CODE4);
3326
+ }
3327
+ var RELAUNCHED_CUT_MARKER = "cut-relaunched";
3328
+ function report_relaunched_cut() {
3329
+ console.error(RELAUNCHED_CUT_MARKER);
3330
+ return report4(CUT_VERDICT, SUCCESS_EXIT_CODE4);
3331
+ }
3332
+ function report_over() {
3333
+ console.error(
3334
+ "This session is over the shared context-cut threshold (`pnpm josh cost --cut` answers `over`), so the implementation cut was not resumed here and its record is kept. End this conversation and type the invocation again in a fresh session."
3335
+ );
3336
+ return report4(OVER_VERDICT, FAILURE_EXIT_CODE5);
3337
+ }
3338
+ var run_cut_report = {
3339
+ BAD_HANDOFF_VERDICT,
3340
+ BUSY_VERDICT: BUSY_VERDICT2,
3341
+ CUT_VERDICT,
3342
+ ENDED_VERDICT,
3343
+ FAILED_VERDICT,
3344
+ FAILURE_EXIT_CODE: FAILURE_EXIT_CODE5,
3345
+ FRESH_VERDICT,
3346
+ HANDED_OFF_VERDICT,
3347
+ HANDOFF_MISSING_NOTE,
3348
+ HANDOFF_OVERFLOW_NOTE,
3349
+ INCOMPLETE_VERDICT,
3350
+ NOT_A_LANE_VERDICT,
3351
+ OVER_VERDICT,
3352
+ RELAUNCHED_CUT_MARKER,
3353
+ RESUME_IMPL_VERDICT,
3354
+ RESUME_VERDICT,
3355
+ STALE_VERDICT,
3356
+ SUCCESS_EXIT_CODE: SUCCESS_EXIT_CODE4,
3357
+ UNDER_THRESHOLD_VERDICT,
3358
+ UNKNOWN_VERDICT: UNKNOWN_VERDICT2,
3359
+ UNREADABLE_VERDICT,
3360
+ UNREADY_VERDICT,
3361
+ refuse_unready,
3362
+ report: report4,
3363
+ report_bad_handoff,
3364
+ report_busy: report_busy2,
3365
+ report_fault,
3366
+ report_handed_off,
3367
+ report_held_cut,
3368
+ report_incomplete,
3369
+ report_over,
3370
+ report_relaunched_cut,
3371
+ report_reviewer,
3372
+ report_stale,
3373
+ report_under_threshold,
3374
+ report_unknown: report_unknown2,
3375
+ report_unreadable
3376
+ };
3377
+
3378
+ // scripts/run/carry/run-carry-ended.ts
3379
+ import path4 from "node:path";
3380
+ import { z as z6 } from "zod";
3381
+ var ENDED_PREFIX = "josh-run-ended-";
3382
+ var TRANSCRIPT_SUFFIX = ".jsonl";
3383
+ var ended_run_schema = z6.object({
3384
+ invocation: z6.string(),
3385
+ started_at: z6.string(),
3386
+ ended_at: z6.string(),
3387
+ stopped: z6.string().optional(),
3388
+ session: z6.string().optional()
3389
+ });
3390
+ function ended_path(git_directory) {
3391
+ return stamp_file.stamp_path(ENDED_PREFIX, git_directory);
3392
+ }
3393
+ function session_of(transcript) {
3394
+ return transcript === void 0 ? void 0 : path4.basename(transcript, TRANSCRIPT_SUFFIX);
3395
+ }
3396
+ function stop_of(transcript, stopped) {
3397
+ return stopped === void 0 ? {} : { stopped, session: session_of(transcript) };
3398
+ }
3399
+ function record_ended(target, read2, now = /* @__PURE__ */ new Date(), stopped) {
3400
+ if (read2.kind !== "carried") return;
3401
+ const { invocation, started_at, owner_transcript } = read2.carry;
3402
+ const ended_at = now.toISOString();
3403
+ stamp_file.replace_stamp(target, {
3404
+ invocation,
3405
+ started_at,
3406
+ ended_at,
3407
+ ...stop_of(owner_transcript, stopped)
3408
+ });
3409
+ }
3410
+ function parse_ended(raw) {
3411
+ return json_value.parse_with(raw, ended_run_schema);
3412
+ }
3413
+ function read_ended(target) {
3414
+ const raw = stamp_file.read_stamp_text(target);
3415
+ return raw === void 0 ? void 0 : parse_ended(raw);
3416
+ }
3417
+ function has_ended(target, started_at) {
3418
+ const ended = read_ended(target);
3419
+ return ended !== void 0 && ended.started_at === started_at;
3420
+ }
3421
+ var run_carry_ended = { ended_path, has_ended, read_ended, record_ended };
3422
+
3423
+ // scripts/run/wake/run-wake.ts
3424
+ import { z as z7 } from "zod";
3425
+ var WAKE_PREFIX = "josh-run-wake-";
1047
3426
  var WAKE_LOG_PREFIX = "josh-run-wake-log-";
1048
3427
  var WAKE_LOG_SUFFIX = ".log";
1049
3428
  var WAKE_GRACE_MS = 6e5;
@@ -1061,15 +3440,15 @@ var STOP_REASONS = {
1061
3440
  unreadable: "unreadable"
1062
3441
  };
1063
3442
  var run_wake_schema = process_owner_schema.extend({
1064
- invocation: z5.string(),
1065
- started_at: z5.string(),
1066
- woke: z5.number(),
1067
- woke_at: z5.string().optional(),
3443
+ invocation: z7.string(),
3444
+ started_at: z7.string(),
3445
+ woke: z7.number(),
3446
+ woke_at: z7.string().optional(),
1068
3447
  // Every optional `RunWake` field must be listed: `z.object` strips undeclared ones on the round trip.
1069
- attempts: z5.number().optional(),
1070
- woke_pid: z5.number().optional(),
1071
- spawned: z5.array(z5.string()).optional(),
1072
- idle_since: z5.string().optional(),
3448
+ attempts: z7.number().optional(),
3449
+ woke_pid: z7.number().optional(),
3450
+ spawned: z7.array(z7.string()).optional(),
3451
+ idle_since: z7.string().optional(),
1073
3452
  profile: agent_role_profile.PROFILE_SCHEMA.optional()
1074
3453
  });
1075
3454
  function is_overdue(marked_at, now, limit_ms = WAKE_GRACE_MS) {
@@ -1091,7 +3470,7 @@ function decide_not_handed_off(input) {
1091
3470
  if (input.is_owner_live) return WAIT_DECISION;
1092
3471
  return decide_launch(input);
1093
3472
  }
1094
- function decide(input) {
3473
+ function decide2(input) {
1095
3474
  if (input.read.kind !== "carried") return { kind: "stop", reason: STOP_REASONS[input.read.kind] };
1096
3475
  if (input.read.carry.is_handed_off !== true) return decide_not_handed_off(input);
1097
3476
  return decide_launch(input);
@@ -1147,7 +3526,7 @@ function reclaim(target, wake) {
1147
3526
  remove_wake(target);
1148
3527
  return stamp_file.create_stamp(target, wake) ? wake : void 0;
1149
3528
  }
1150
- function claim(target, invocation, now, profile) {
3529
+ function claim3(target, invocation, now, profile) {
1151
3530
  const existing = read_wake(target);
1152
3531
  if (is_held_by_live_supervisor(existing)) return void 0;
1153
3532
  const wake = { ...fresh_wake(invocation, now, profile), ...carried_state(existing, invocation) };
@@ -1202,10 +3581,10 @@ var run_wake = {
1202
3581
  IDLE_CEILING_MS,
1203
3582
  MAX_WAKE_ATTEMPTS,
1204
3583
  WAKE_GRACE_MS,
1205
- claim,
3584
+ claim: claim3,
1206
3585
  count_claim,
1207
3586
  count_wake,
1208
- decide,
3587
+ decide: decide2,
1209
3588
  fresh_wake,
1210
3589
  is_supervisor_live,
1211
3590
  mark_idle,
@@ -1287,11 +3666,11 @@ var DEFAULT_PORTS3 = {
1287
3666
  notify_stranded: real_notify_stranded
1288
3667
  };
1289
3668
  function input_at(ports, targets) {
1290
- const read = ports.read_carry(targets.carry);
1291
- if (read.kind !== "carried") return void 0;
3669
+ const read2 = ports.read_carry(targets.carry);
3670
+ if (read2.kind !== "carried") return void 0;
1292
3671
  return {
1293
- read,
1294
- is_owner_live: ports.is_owner_live(read.carry),
3672
+ read: read2,
3673
+ is_owner_live: ports.is_owner_live(read2.carry),
1295
3674
  supervisor: ports.supervisor_liveness(ports.read_wake(targets.wake))
1296
3675
  };
1297
3676
  }
@@ -1299,7 +3678,7 @@ async function gather2(ports) {
1299
3678
  const targets = await ports.resolve_targets();
1300
3679
  return targets === void 0 ? void 0 : input_at(ports, targets);
1301
3680
  }
1302
- async function report2(ports, input) {
3681
+ async function report5(ports, input) {
1303
3682
  if (input.read.kind !== "carried") return;
1304
3683
  const text2 = run_stranded.describe(input.read.carry);
1305
3684
  if (await ports.emit_stranded(text2)) await ports.notify_stranded(text2);
@@ -1308,7 +3687,7 @@ async function detect_and_report2(ports = DEFAULT_PORTS3) {
1308
3687
  const input = await gather2(ports);
1309
3688
  if (input === void 0) return run_stranded.OK;
1310
3689
  const is_run_stranded = run_stranded.is_stranded(input);
1311
- if (is_run_stranded) await report2(ports, input);
3690
+ if (is_run_stranded) await report5(ports, input);
1312
3691
  return run_stranded.verdict_of(is_run_stranded);
1313
3692
  }
1314
3693
  async function run_stranded_check() {
@@ -1321,12 +3700,12 @@ function is_stranded_at(directory, ports = DEFAULT_PORTS3) {
1321
3700
  const input = input_at(ports, targets_of(directory));
1322
3701
  return input !== void 0 && run_stranded.is_stranded(input);
1323
3702
  }
1324
- function is_left_stranded(run, ports = DEFAULT_PORTS3) {
1325
- const read = ports.read_carry(run.carry_target);
1326
- if (read.kind !== "carried") return !ports.has_ended(run.ended_target, run.started_at);
3703
+ function is_left_stranded(run3, ports = DEFAULT_PORTS3) {
3704
+ const read2 = ports.read_carry(run3.carry_target);
3705
+ if (read2.kind !== "carried") return !ports.has_ended(run3.ended_target, run3.started_at);
1327
3706
  return run_stranded.is_stranded({
1328
- read,
1329
- is_owner_live: run_carry.is_held_by_another(read.carry, ports.is_owner_live),
3707
+ read: read2,
3708
+ is_owner_live: run_carry.is_held_by_another(read2.carry, ports.is_owner_live),
1330
3709
  supervisor: run_stranded.GONE
1331
3710
  });
1332
3711
  }
@@ -1338,10 +3717,23 @@ var run_stranded_detect = {
1338
3717
  };
1339
3718
 
1340
3719
  // scripts/hooks/stop-guard.ts
1341
- async function hold_facts() {
3720
+ async function held_record() {
1342
3721
  const directory = await run_hold.worktree_directory();
1343
- const hold = directory === void 0 ? void 0 : run_halfrun_resume.hold_of(run_hold.read_hold(run_hold.hold_path(directory)));
1344
- return { hold_present: hold !== void 0, prrun_stopped: hold?.prrun_stop_head !== void 0 };
3722
+ if (directory === void 0) return void 0;
3723
+ return run_halfrun_resume.hold_of(run_hold.read_hold(run_hold.hold_path(directory)));
3724
+ }
3725
+ function was_notified(hold, tail) {
3726
+ if (lane_park.mentions_confirmation_notify(tail)) return true;
3727
+ return hold !== void 0 && run_hold_session.is_notified_since(hold, last_prompt.pause_start(tail));
3728
+ }
3729
+ async function hold_facts(tail) {
3730
+ const hold = await held_record();
3731
+ return {
3732
+ hold_issue: hold?.issue,
3733
+ hold_owned: hold === void 0 || run_hold_session.is_session_hold(hold),
3734
+ notified: was_notified(hold, tail),
3735
+ prrun_stopped: hold?.prrun_stop_head !== void 0
3736
+ };
1345
3737
  }
1346
3738
  function transcript_tail(transcript_path) {
1347
3739
  const transcript = time_hook_transcript.transcript_of(transcript_path, void 0);
@@ -1350,6 +3742,14 @@ function transcript_tail(transcript_path) {
1350
3742
  function was_filed(tail) {
1351
3743
  return filing_cap.turn_filing_count(tail) > 0;
1352
3744
  }
3745
+ function is_cut_stop(tail) {
3746
+ if (run_cut.carried_cut_sync() !== void 0) return true;
3747
+ return tail_commands.answered(
3748
+ filing_cap.current_turn(tail),
3749
+ pre_gate_cut.takes_the_cut,
3750
+ run_cut_report.RELAUNCHED_CUT_MARKER
3751
+ );
3752
+ }
1353
3753
  function session_lang() {
1354
3754
  return session_language.resolve_session_lang().lang;
1355
3755
  }
@@ -1364,13 +3764,13 @@ async function session_role_facts() {
1364
3764
  async function build_context(transcript_path, payload_tail) {
1365
3765
  const tail = transcript_tail(transcript_path);
1366
3766
  return {
1367
- ...await hold_facts(),
3767
+ ...await hold_facts(tail),
1368
3768
  tree_clean: !await run_hold.is_tree_dirty(),
1369
- notified: lane_park.mentions_confirmation_notify(tail),
1370
3769
  message: payload_tail.message,
1371
3770
  prompt: last_prompt.prompt_text(tail),
3771
+ answers_question: stop_decision.is_question(last_prompt.turn_prompt(tail)),
1372
3772
  stop_hook_active: payload_tail.stop_hook_active,
1373
- cut_pending: run_cut.carried_cut_sync() !== void 0,
3773
+ cut_pending: is_cut_stop(tail),
1374
3774
  filed: was_filed(tail),
1375
3775
  session_owner: repo_party.current_owner(),
1376
3776
  ...await session_role_facts(),
@@ -1401,8 +3801,8 @@ async function carried_owner_live() {
1401
3801
  try {
1402
3802
  const directory = await run_carry.repository_directory();
1403
3803
  if (directory === void 0) return void 0;
1404
- const read = run_carry.read_carry(run_carry.carry_path(directory));
1405
- return read.kind === "carried" ? run_carry.is_owner_live(read.carry) : void 0;
3804
+ const read2 = run_carry.read_carry(run_carry.carry_path(directory));
3805
+ return read2.kind === "carried" ? run_carry.is_owner_live(read2.carry) : void 0;
1406
3806
  } catch {
1407
3807
  return void 0;
1408
3808
  }
@@ -1416,13 +3816,13 @@ async function run_report_checks() {
1416
3816
  async function write_stop_decision(raw_payload) {
1417
3817
  hook_decision.load_environment_file();
1418
3818
  await run_report_checks();
1419
- const { reason } = await outcome_of(raw_payload);
1420
- if (reason === void 0) return;
1421
- process.stdout.write(`${stop_rules.block_envelope(reason, session_lang())}
3819
+ const envelope = await stop_actions2.envelope_of(await outcome_of(raw_payload), session_lang());
3820
+ if (envelope === void 0) return;
3821
+ process.stdout.write(`${envelope}
1422
3822
  `);
1423
3823
  }
1424
3824
  var stop_guard = { outcome_of, stop_outcome_for_payload };
1425
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
3825
+ if (process.argv[1] === fileURLToPath5(import.meta.url)) {
1426
3826
  if (process.stdin.isTTY) hook_decision.report_no_payload("stop:guard");
1427
3827
  else await write_stop_decision(await text(process.stdin));
1428
3828
  }