@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,92 +1,99 @@
1
1
  import {
2
2
  duplicate_read_outcome
3
- } from "./chunk-YYPI7SJZ.js";
3
+ } from "./chunk-PF2NZL2A.js";
4
4
  import {
5
5
  ALREADY_DONE_LABEL,
6
6
  CONTEXT_CUT_THRESHOLD,
7
7
  IN_PROGRESS_LABEL,
8
8
  NEEDS_DECISION_LABEL,
9
- NEEDS_HUMAN_REVIEW_LABEL,
10
9
  bash_triggers,
10
+ cost_cli,
11
+ cost_verdict,
11
12
  filing_cap,
12
- git_gh_api_path,
13
13
  git_gh_command,
14
- git_gh_exec,
15
14
  git_gh_issue_read,
16
- git_gh_pr_read,
17
15
  has_any_label,
18
- has_stdout_field,
16
+ interactive_ask,
19
17
  issue_watch_diff,
20
18
  lane_background,
21
19
  lane_park,
22
- parse_json_object_or_undefined,
23
- read_json_listing,
20
+ pre_gate_cut,
24
21
  repo_party,
25
22
  run_cut,
26
23
  run_cut_handoff,
27
- run_hold,
24
+ run_liveness,
28
25
  run_tail_rule,
29
- session_cite,
30
26
  shell_segments,
31
27
  tail_commands
32
- } from "./chunk-43ZHT22P.js";
28
+ } from "./chunk-SYOTDYCI.js";
33
29
  import {
34
- ancestor_directories,
35
30
  find_local_bin_upwards,
36
31
  json_object_schema,
37
32
  string_array_schema,
38
33
  string_record_schema,
39
34
  vscode_settings_schema
40
- } from "./chunk-IX6TTTI6.js";
35
+ } from "./chunk-EFXE6C37.js";
36
+ import {
37
+ distributed_paths
38
+ } from "./chunk-M4HGZV7U.js";
41
39
  import {
42
- agent_role_profile,
43
- agent_session_role,
44
40
  git_common_directory,
41
+ git_gh_api_path,
42
+ git_gh_exec,
43
+ has_stdout_field,
45
44
  issue_cite,
46
45
  lane_registry,
46
+ read_json_listing,
47
47
  repo_origin,
48
48
  run_event_scope,
49
49
  run_event_stream,
50
50
  run_event_stream_emit,
51
51
  run_headless,
52
+ run_hold,
53
+ run_preflight,
54
+ session_cite,
52
55
  session_language
53
- } from "./chunk-EUMACUK2.js";
56
+ } from "./chunk-WK6SMBIC.js";
57
+ import {
58
+ ancestor_directories
59
+ } from "./chunk-GP4IEC4O.js";
54
60
  import {
55
61
  error_text
56
62
  } from "./chunk-CDY4NFQG.js";
57
63
  import {
58
64
  batch_outcome
59
- } from "./chunk-YHBZAJSV.js";
65
+ } from "./chunk-A65Y4YOR.js";
60
66
  import {
61
67
  investigation_outcome
62
- } from "./chunk-K4LMBQC2.js";
68
+ } from "./chunk-7DSBJVFH.js";
63
69
  import {
64
70
  investigation_reads
65
- } from "./chunk-H5HVWYXK.js";
71
+ } from "./chunk-PTICX4OL.js";
72
+ import {
73
+ agent_role_profile,
74
+ agent_session_role
75
+ } from "./chunk-UPUZF3AN.js";
66
76
  import {
67
77
  lane_guard_policy
68
- } from "./chunk-SHGPANFB.js";
78
+ } from "./chunk-JP45SOCJ.js";
69
79
  import {
70
80
  hook_decision
71
- } from "./chunk-J47HU5H5.js";
81
+ } from "./chunk-6TS2JD6D.js";
72
82
  import {
73
- cost_attribute,
74
83
  cost_blocks,
75
- open_issue_schema,
76
84
  time_batch_guard,
77
85
  time_density_hook,
78
- time_shell
79
- } from "./chunk-AS74OU3D.js";
86
+ time_hook_transcript,
87
+ time_shell,
88
+ time_spans
89
+ } from "./chunk-TNUMY6NL.js";
80
90
  import {
81
91
  lane_child_marker
82
- } from "./chunk-K4EOONYM.js";
92
+ } from "./chunk-LAOV7NLM.js";
83
93
  import {
84
94
  GIT_LOCATION_VARIABLES,
85
95
  PORCELAIN_FLAG,
86
96
  UNTRACKED_FILES_FLAG,
87
- cli_flags,
88
- cost_transcript,
89
- cost_usage,
90
97
  file_reader,
91
98
  git_command,
92
99
  git_diff_reads,
@@ -94,11 +101,10 @@ import {
94
101
  issue_number_shape,
95
102
  json_value,
96
103
  lane_paths,
97
- own_session,
104
+ open_issue_schema,
98
105
  run_carry
99
- } from "./chunk-E7OIKX5Z.js";
106
+ } from "./chunk-APUO6U4J.js";
100
107
  import {
101
- PLATFORM_TEMP_ROOT,
102
108
  PROBE_TIMEOUT_MS,
103
109
  PROJECT_ROOT,
104
110
  package_path,
@@ -116,9 +122,9 @@ var HEADER_BODY_SEPARATOR = /\r?\n\r?\n/u;
116
122
  var ETAG_HEADER = /^etag: *(?<etag>[^\r\n]+)/imu;
117
123
  var UNCHANGED = { kind: "unchanged" };
118
124
  var UNREADABLE = { kind: "unreadable" };
119
- function to_args(path16, etag) {
125
+ function to_args(path13, etag) {
120
126
  const header = etag === void 0 ? [] : [HEADER_FLAG, `${IF_NONE_MATCH}${etag}`];
121
- return ["api", INCLUDE_FLAG, ...header, path16];
127
+ return ["api", INCLUDE_FLAG, ...header, path13];
122
128
  }
123
129
  function to_changed(output) {
124
130
  const [headers = "", ...rest] = output.split(HEADER_BODY_SEPARATOR);
@@ -132,9 +138,9 @@ function failure_status(error) {
132
138
  const cause = error instanceof Error ? error.cause : void 0;
133
139
  return has_stdout_field(cause) ? git_gh_exec.parse_status_line(cause.stdout) : void 0;
134
140
  }
135
- async function read_conditional(path16, etag, timeout_ms) {
141
+ async function read_conditional(path13, etag, timeout_ms) {
136
142
  try {
137
- return to_read(await git_gh_exec.exec_gh_command(to_args(path16, etag), timeout_ms));
143
+ return to_read(await git_gh_exec.exec_gh_command(to_args(path13, etag), timeout_ms));
138
144
  } catch (error) {
139
145
  return failure_status(error) === NOT_MODIFIED_STATUS ? UNCHANGED : UNREADABLE;
140
146
  }
@@ -178,16 +184,16 @@ async function read_snapshot(request2, body, etag) {
178
184
  return comments === void 0 ? UNREADABLE2 : { kind: "read", snapshot: { body, comments }, etag };
179
185
  }
180
186
  async function fetch_issue(request2) {
181
- const read3 = await git_gh_conditional.read_conditional(
187
+ const read2 = await git_gh_conditional.read_conditional(
182
188
  git_gh_api_path.issue_api_path(request2.issue),
183
189
  request2.etag,
184
190
  request2.timeout_ms
185
191
  );
186
- if (read3.kind !== "changed") return read3;
187
- const fields = json_value.parse_with(read3.body, issue_schema);
192
+ if (read2.kind !== "changed") return read2;
193
+ const fields = json_value.parse_with(read2.body, issue_schema);
188
194
  if (fields === void 0) return UNREADABLE2;
189
195
  if (fields.state === CLOSED_STATE) return { kind: "closed" };
190
- return await read_snapshot(request2, fields.body ?? "", read3.etag);
196
+ return await read_snapshot(request2, fields.body ?? "", read2.etag);
191
197
  }
192
198
  function fresh_record(issue, fetched, now, acknowledged_at = now.toISOString()) {
193
199
  const baseline = issue_watch_diff.baseline_of(fetched.snapshot);
@@ -542,12 +548,41 @@ async function mark_implement(directory = process.cwd(), source = process.env) {
542
548
  }
543
549
  var lane_phase = { mark_implement };
544
550
 
551
+ // scripts/rules/agent-model.ts
552
+ var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["Agent", "Task"]);
553
+ var GENERAL_PURPOSE = "general-purpose";
554
+ function is_general_purpose(call) {
555
+ if (!SUBAGENT_TOOLS.has(call.name) || !json_value.is_record(call.input)) return false;
556
+ const type = call.input["subagent_type"];
557
+ return type === void 0 || type === GENERAL_PURPOSE;
558
+ }
559
+ function names_model(call) {
560
+ const model = json_value.is_record(call.input) ? call.input["model"] : void 0;
561
+ return typeof model === "string" && model.length > 0;
562
+ }
563
+ function is_unnamed_model(call) {
564
+ return is_general_purpose(call) && !names_model(call);
565
+ }
566
+ function keeps_model(call) {
567
+ return is_general_purpose(call) && names_model(call);
568
+ }
569
+ var AGENT_MODEL_REASON = "\u26D4 agent model: a `general-purpose` subagent with no `model` inherits the parent model. Reissue the call with `model` set \u2014 `sonnet` for routine work, `opus` for judgment such as a review. It fires on every occurrence (`.claude/skills/workflow-commands/delegation.md`).";
570
+ var ROW = {
571
+ id: "agent-model",
572
+ is_trigger: is_unnamed_model,
573
+ reason: AGENT_MODEL_REASON,
574
+ decide: () => true,
575
+ keeps: keeps_model,
576
+ reaches: is_general_purpose
577
+ };
578
+ var agent_model = { AGENT_MODEL_REASON, ROW, is_unnamed_model };
579
+
545
580
  // scripts/rules/direct-filing.ts
546
581
  var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2>`, never with `gh api \u2026/issues` or `gh issue create` \u2014 it runs the scout, the lint, the labels and `epic:bundle` itself. It fires on every occurrence (`docs/josh-commands-backlog.md` \u2192 "`josh issue:file`").';
547
582
  function is_any_filing(command) {
548
583
  return bash_triggers.is_direct_filing(command) || bash_triggers.is_issue_filing(command);
549
584
  }
550
- var ROW = {
585
+ var ROW2 = {
551
586
  id: "direct-filing",
552
587
  is_trigger: bash_triggers.on_bash_command(bash_triggers.is_direct_filing),
553
588
  reason: DIRECT_FILING_REASON,
@@ -555,7 +590,7 @@ var ROW = {
555
590
  keeps: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
556
591
  reaches: bash_triggers.on_bash_command(is_any_filing)
557
592
  };
558
- var direct_filing = { DIRECT_FILING_REASON, ROW };
593
+ var direct_filing = { DIRECT_FILING_REASON, ROW: ROW2 };
559
594
 
560
595
  // scripts/run/progress/run-progress.ts
561
596
  var DEFAULT_INTERVAL_MINUTES = 20;
@@ -892,12 +927,12 @@ function landing_ms(command, now_ms, last_report_ms) {
892
927
  const lands_ms = now_ms + duration_ms;
893
928
  return run_progress.is_due(last_report_ms, lands_ms, interval_ms) ? lands_ms : void 0;
894
929
  }
895
- function decide(call, run2, can_record) {
930
+ function decide(call, run, can_record) {
896
931
  const last_report_ms = run_progress_clock.read_last_report_sync();
897
932
  if (last_report_ms === void 0) return false;
898
- const target = ARM_RECORD.path(run2.transcript);
899
- if (is_armed(target, run2.now_ms)) return true;
900
- const lands_ms = landing_ms(time_shell.bash_command(call.input), run2.now_ms, last_report_ms);
933
+ const target = ARM_RECORD.path(run.transcript);
934
+ if (is_armed(target, run.now_ms)) return true;
935
+ const lands_ms = landing_ms(time_shell.bash_command(call.input), run.now_ms, last_report_ms);
901
936
  if (lands_ms === void 0) return true;
902
937
  if (can_record) ARM_RECORD.record(target, lands_ms);
903
938
  return false;
@@ -1001,7 +1036,7 @@ function carries_a_file_body(command, has_file = existsSync) {
1001
1036
  );
1002
1037
  }
1003
1038
  var FILE_BODY_REASON = "\u26D4 file body in a shell command: this carries a file's new text inline, which is re-read on every later request. Edit a region with the Edit tool, rewrite a whole file with the Write tool, and pass a body by path (`--body-file <path>`); a new-file `cat > <new-file> <<EOF` stays allowed. This rule fires on every occurrence (`prompts/collaboration-workflow/file-edits.md`).";
1004
- var ROW2 = {
1039
+ var ROW3 = {
1005
1040
  id: "file-body",
1006
1041
  is_trigger: bash_triggers.on_bash_command((command) => carries_a_file_body(command)),
1007
1042
  reason: FILE_BODY_REASON,
@@ -1009,7 +1044,7 @@ var ROW2 = {
1009
1044
  };
1010
1045
  var file_body = {
1011
1046
  FILE_BODY_REASON,
1012
- ROW: ROW2,
1047
+ ROW: ROW3,
1013
1048
  carries_a_file_body
1014
1049
  };
1015
1050
 
@@ -1112,7 +1147,7 @@ function is_force_or_delete(command) {
1112
1147
  return shell_segments.segments_of(command).some((segment) => is_destructive_segment(segment));
1113
1148
  }
1114
1149
  var GIT_FORCE_REASON = "\u26D4 force push or branch delete: this rewrites or removes shared history, which is Tier C and needs explicit current-turn user instruction. Push with `pnpm josh git`; leave a force push or branch delete to the user's own terminal. This rule fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md`).";
1115
- var ROW3 = {
1150
+ var ROW4 = {
1116
1151
  id: "git-force",
1117
1152
  is_trigger: bash_triggers.on_bash_command(is_force_or_delete),
1118
1153
  reason: GIT_FORCE_REASON,
@@ -1120,376 +1155,10 @@ var ROW3 = {
1120
1155
  };
1121
1156
  var git_force = {
1122
1157
  GIT_FORCE_REASON,
1123
- ROW: ROW3,
1158
+ ROW: ROW4,
1124
1159
  is_force_or_delete
1125
1160
  };
1126
1161
 
1127
- // scripts/cost-runtime/cost-cli.ts
1128
- import { fileURLToPath } from "node:url";
1129
-
1130
- // scripts/cost-runtime/codex-usage.ts
1131
- import { globSync, readFileSync as readFileSync2 } from "node:fs";
1132
- import path2 from "node:path";
1133
- import { z as z4 } from "zod";
1134
- var THREAD_ID_PATTERN = /^[A-Za-z0-9-]+$/u;
1135
- var SESSION_META_SCHEMA = z4.looseObject({
1136
- type: z4.literal("session_meta"),
1137
- payload: z4.looseObject({ id: z4.string(), cwd: z4.string() })
1138
- });
1139
- var TOTAL_USAGE_SCHEMA = z4.looseObject({ input_tokens: z4.number().nonnegative() });
1140
- var TOKEN_COUNT_SCHEMA = z4.looseObject({
1141
- type: z4.literal("event_msg"),
1142
- payload: z4.looseObject({
1143
- type: z4.literal("token_count"),
1144
- info: z4.looseObject({
1145
- total_token_usage: TOTAL_USAGE_SCHEMA,
1146
- last_token_usage: TOTAL_USAGE_SCHEMA
1147
- })
1148
- })
1149
- });
1150
- function same_project(cwd, target) {
1151
- return cost_transcript.session_cwd(cwd) === cost_transcript.session_cwd(target);
1152
- }
1153
- function billed_per_request(usages) {
1154
- return usages.map((usage) => usage.last_token_usage.input_tokens);
1155
- }
1156
- function measurement_from(content, target, thread_id) {
1157
- const values = content.split("\n").map((line) => json_value.parse_or_undefined(line));
1158
- const metadata = values.flatMap((value) => {
1159
- const parsed = SESSION_META_SCHEMA.safeParse(value);
1160
- return parsed.success ? [parsed.data] : [];
1161
- });
1162
- const usages = values.flatMap((value) => {
1163
- const parsed = TOKEN_COUNT_SCHEMA.safeParse(value);
1164
- return parsed.success ? [parsed.data.payload.info] : [];
1165
- });
1166
- const session = metadata.find((entry) => entry.payload.id === thread_id);
1167
- if (session === void 0 || usages.length === 0 || !same_project(session.payload.cwd, target)) {
1168
- return void 0;
1169
- }
1170
- return { billed_input_per_request: billed_per_request(usages) };
1171
- }
1172
- function rollout_files(home, thread_id) {
1173
- if (!THREAD_ID_PATTERN.test(thread_id)) return [];
1174
- return globSync(`sessions/**/*${thread_id}.jsonl`, { cwd: home }).map(
1175
- (file) => path2.join(home, file)
1176
- );
1177
- }
1178
- function read_measurement(file, target, thread_id) {
1179
- try {
1180
- return measurement_from(readFileSync2(file, "utf8"), target, thread_id);
1181
- } catch {
1182
- return void 0;
1183
- }
1184
- }
1185
- function latest_measurement(files, target, thread_id) {
1186
- for (const file of files.toReversed()) {
1187
- const measured = read_measurement(file, target, thread_id);
1188
- if (measured !== void 0) return measured;
1189
- }
1190
- return void 0;
1191
- }
1192
- function measurement(target, default_home, environment = process.env) {
1193
- const thread_id = environment["CODEX_THREAD_ID"];
1194
- if (thread_id === void 0) return void 0;
1195
- const home = environment["CODEX_HOME"] ?? path2.join(default_home, ".codex");
1196
- return latest_measurement(rollout_files(home, thread_id), target, thread_id);
1197
- }
1198
- var codex_usage = { measurement };
1199
-
1200
- // scripts/cost-runtime/cost-corpus.ts
1201
- function accumulate_missing(sessions) {
1202
- return {
1203
- no_usage_lines: sessions.reduce((sum, session) => sum + session.no_usage_lines, 0),
1204
- malformed_lines: sessions.reduce((sum, session) => sum + session.malformed_lines, 0),
1205
- unreadable_sessions: sessions.filter((session) => !session.is_readable).length,
1206
- // Filled by the attribution, which is the only step that knows a unit could not follow its
1207
- // parent; a per-session tally cannot see it, so it is zero here and set for an issue scope below.
1208
- unattributed_sessions: 0
1209
- };
1210
- }
1211
- function load_corpus(cwd) {
1212
- const files = cost_transcript.list_sessions_across(cost_transcript.transcript_directories(cwd));
1213
- const sessions = files.map((file) => cost_transcript.read_session(file));
1214
- return { sessions, files, missing: accumulate_missing(sessions) };
1215
- }
1216
- function session_meta(corpus) {
1217
- return new Map(
1218
- corpus.files.map((file) => [
1219
- file.session_id,
1220
- { is_delegated: file.is_delegated, owner: cost_transcript.owning_session_id(file) }
1221
- ])
1222
- );
1223
- }
1224
- function issue_set(records) {
1225
- const issues = cost_attribute.group_by_issue(records).map((group) => group.issue).filter((issue) => issue !== cost_attribute.UNATTRIBUTED_KEY);
1226
- return new Set(issues);
1227
- }
1228
- function sole_of(issues) {
1229
- if (issues.size !== 1) return cost_attribute.UNATTRIBUTED_KEY;
1230
- return [...issues][0] ?? cost_attribute.UNATTRIBUTED_KEY;
1231
- }
1232
- function owner_sets(corpus, meta) {
1233
- const own = corpus.sessions.filter(
1234
- (session) => meta.get(session.session_id)?.is_delegated !== true
1235
- );
1236
- return new Map(own.map((session) => [session.session_id, issue_set(session.records)]));
1237
- }
1238
- function pairs_with_inherited(session, inherited, is_delegated) {
1239
- return cost_attribute.group_by_issue(session.records).flatMap(
1240
- (group) => group.records.map((record2) => ({
1241
- record: record2,
1242
- issue: group.issue === cost_attribute.UNATTRIBUTED_KEY ? inherited : group.issue,
1243
- baseline_tokens: session.baseline_tokens,
1244
- session_id: session.session_id,
1245
- is_delegated
1246
- }))
1247
- );
1248
- }
1249
- var NO_ISSUES = /* @__PURE__ */ new Set();
1250
- function parent_issues(session, meta, owners) {
1251
- const entry = meta.get(session.session_id);
1252
- if (entry?.is_delegated !== true) return NO_ISSUES;
1253
- return owners.get(entry.owner) ?? NO_ISSUES;
1254
- }
1255
- function pairs_of_session(session, meta, owners) {
1256
- const is_delegated = meta.get(session.session_id)?.is_delegated === true;
1257
- const candidates = parent_issues(session, meta, owners);
1258
- const inherited = is_delegated ? sole_of(candidates) : cost_attribute.UNATTRIBUTED_KEY;
1259
- const pairs = pairs_with_inherited(session, inherited, is_delegated);
1260
- const has_unattributed = pairs.some((pair) => pair.issue === cost_attribute.UNATTRIBUTED_KEY);
1261
- if (!is_delegated || !has_unattributed) return { pairs };
1262
- return { pairs, unattributed_candidates: candidates };
1263
- }
1264
- function attribute_corpus(corpus) {
1265
- const meta = session_meta(corpus);
1266
- const owners = owner_sets(corpus, meta);
1267
- const per_session = corpus.sessions.map((session) => pairs_of_session(session, meta, owners));
1268
- return {
1269
- pairs: per_session.flatMap((one) => one.pairs),
1270
- unattributed: per_session.flatMap(
1271
- (one) => one.unattributed_candidates === void 0 ? [] : [one.unattributed_candidates]
1272
- )
1273
- };
1274
- }
1275
- function floor_for_issue(unattributed, issue_number) {
1276
- return unattributed.filter((set) => set.size === 0 || set.has(issue_number)).length;
1277
- }
1278
- function is_better(candidate, existing) {
1279
- const { UNATTRIBUTED_KEY } = cost_attribute;
1280
- return existing.issue === UNATTRIBUTED_KEY && candidate.issue !== UNATTRIBUTED_KEY;
1281
- }
1282
- function values_of(best) {
1283
- const collected = [];
1284
- for (const [, pair] of best) collected.push(pair);
1285
- return collected;
1286
- }
1287
- function dedupe_across_sessions(pairs) {
1288
- const best = /* @__PURE__ */ new Map();
1289
- for (const pair of pairs) {
1290
- const existing = best.get(pair.record.request_id);
1291
- if (existing === void 0 || is_better(pair, existing)) {
1292
- best.set(pair.record.request_id, pair);
1293
- }
1294
- }
1295
- return values_of(best);
1296
- }
1297
- function attributed(corpus) {
1298
- return dedupe_across_sessions(attribute_corpus(corpus).pairs);
1299
- }
1300
- function mainline_records(pairs) {
1301
- const groups = /* @__PURE__ */ new Map();
1302
- for (const pair of pairs) {
1303
- if (pair.is_delegated) continue;
1304
- groups.set(pair.session_id, [...groups.get(pair.session_id) ?? [], pair.record]);
1305
- }
1306
- return [...groups.values()];
1307
- }
1308
- var cost_corpus = {
1309
- load_corpus,
1310
- attribute_corpus,
1311
- floor_for_issue,
1312
- attributed,
1313
- mainline_records
1314
- };
1315
-
1316
- // scripts/cost-runtime/cost-verdict.ts
1317
- var FAILURE_EXIT_CODE = 1;
1318
- var OVER_VERDICT = "over";
1319
- var UNDER_VERDICT = "under";
1320
- function per_request_cost(measurement2) {
1321
- return measurement2.billed_input_per_request.at(-1) ?? 0;
1322
- }
1323
- function classify(measurement2, limit) {
1324
- return per_request_cost(measurement2) > limit ? OVER_VERDICT : UNDER_VERDICT;
1325
- }
1326
- function over_summary(measurement2, limit) {
1327
- const total = measurement2.billed_input_per_request.length;
1328
- return `${String(per_request_cost(measurement2))} billed input tokens per request, on the newest of ${String(total)} request(s); limit ${String(limit)}`;
1329
- }
1330
- function report_over(measurement2, limit) {
1331
- if (measurement2 === void 0 || measurement2.billed_input_per_request.length === 0) {
1332
- console.error("No requests in this session; there is nothing to hand off.");
1333
- return FAILURE_EXIT_CODE;
1334
- }
1335
- console.info(classify(measurement2, limit));
1336
- console.error(over_summary(measurement2, limit));
1337
- return 0;
1338
- }
1339
- var cost_verdict = {
1340
- OVER_VERDICT,
1341
- UNDER_VERDICT,
1342
- classify,
1343
- per_request_cost,
1344
- report_over
1345
- };
1346
-
1347
- // scripts/cost-runtime/transcript-cwd.ts
1348
- import path3 from "node:path";
1349
- function resolve(target, cwd) {
1350
- return target === void 0 ? cwd : path3.resolve(target);
1351
- }
1352
- var transcript_cwd = { resolve };
1353
-
1354
- // scripts/cost-runtime/cost-cli.ts
1355
- var ARGV_OFFSET = 2;
1356
- var FAILURE_EXIT_CODE2 = 1;
1357
- var USAGE = "Usage: josh cost (--cut | --over <tokens-per-request>) [--path <dir>]";
1358
- var UNMEASURABLE_VERDICT = "unmeasurable";
1359
- function to_threshold(raw) {
1360
- if (raw === void 0 || raw.trim() === "") return void 0;
1361
- const parsed = Number(raw);
1362
- return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
1363
- }
1364
- function is_unparsed(raw, parsed) {
1365
- return parsed === void 0 && raw !== void 0;
1366
- }
1367
- function selected_threshold(values) {
1368
- if (values.cut === true) return CONTEXT_CUT_THRESHOLD;
1369
- return to_threshold(values.over);
1370
- }
1371
- function threshold_option(over) {
1372
- return over === void 0 ? {} : { over };
1373
- }
1374
- function path_option(path16) {
1375
- return path16 === void 0 ? {} : { path: path16 };
1376
- }
1377
- function to_options(values) {
1378
- if (values.cut === true && values.over !== void 0) return void 0;
1379
- const over = selected_threshold(values);
1380
- if (is_unparsed(values.over, over)) return void 0;
1381
- return { ...threshold_option(over), ...path_option(values.path) };
1382
- }
1383
- var PARSE_ARGS_OPTIONS = {
1384
- cut: { type: "boolean" },
1385
- over: { type: "string" },
1386
- path: { type: "string" }
1387
- };
1388
- function parse_options(argv) {
1389
- const values = cli_flags.values_of(argv, PARSE_ARGS_OPTIONS);
1390
- return values === void 0 ? void 0 : to_options(values);
1391
- }
1392
- function report_empty(cwd, session_id) {
1393
- const searched = cost_transcript.searched_directories(cost_transcript.transcript_directories(cwd));
1394
- for (const line of cost_transcript.missing_message(searched, session_id)) console.error(line);
1395
- return FAILURE_EXIT_CODE2;
1396
- }
1397
- function provider_of(environment) {
1398
- const resolved = agent_role_profile.resolve_provider(environment);
1399
- if (resolved.kind !== "provider" || resolved.is_default === true) return void 0;
1400
- return resolved.provider;
1401
- }
1402
- function measurement_of(session) {
1403
- return {
1404
- billed_input_per_request: session.records.map(
1405
- (record2) => cost_usage.billed_input(record2.totals)
1406
- )
1407
- };
1408
- }
1409
- function anthropic_own(target, environment) {
1410
- const corpus = cost_corpus.load_corpus(target);
1411
- const selection = own_session.select_own_session(corpus.files, environment);
1412
- if (selection.kind === "absent") return { kind: "absent", session_id: selection.session_id };
1413
- const session = corpus.sessions[selection.index];
1414
- if (session === void 0) return { kind: "empty" };
1415
- return { kind: "measured", measurement: measurement_of(session) };
1416
- }
1417
- function anthropic_measurement(target, environment) {
1418
- const own = anthropic_own(target, environment);
1419
- return own.kind === "measured" ? own.measurement : void 0;
1420
- }
1421
- function same_openai_project(target, cwd) {
1422
- return cost_transcript.session_cwd(target) === cost_transcript.session_cwd(cwd);
1423
- }
1424
- function reject_openai_cross_project(context, target, cwd) {
1425
- if (context.provider !== "openai" || context.path === void 0 || same_openai_project(target, cwd)) {
1426
- return false;
1427
- }
1428
- console.error(
1429
- "OpenAI --path cannot select a thread from another project; run josh cost in that project."
1430
- );
1431
- return true;
1432
- }
1433
- function measure(target, provider, environment) {
1434
- return provider === "openai" ? codex_usage.measurement(target, cost_transcript.home_directory(), environment) : anthropic_measurement(target, environment);
1435
- }
1436
- function run_over_anthropic(own, target, limit) {
1437
- if (own.kind === "measured") return cost_verdict.report_over(own.measurement, limit);
1438
- return report_empty(target, own.kind === "absent" ? own.session_id : void 0);
1439
- }
1440
- function run_over_codex(target, limit, environment) {
1441
- const measurement2 = measure(target, "openai", environment);
1442
- if (measurement2 !== void 0) return cost_verdict.report_over(measurement2, limit);
1443
- console.error(`No Codex usage found for the current OpenAI thread under ${target}.`);
1444
- return FAILURE_EXIT_CODE2;
1445
- }
1446
- function run_over(target, limit, provider, environment) {
1447
- if (provider === "anthropic") {
1448
- return run_over_anthropic(anthropic_own(target, environment), target, limit);
1449
- }
1450
- return run_over_codex(target, limit, environment);
1451
- }
1452
- function verdict_of(measurement2) {
1453
- if (measurement2 === void 0 || measurement2.billed_input_per_request.length === 0) {
1454
- return UNMEASURABLE_VERDICT;
1455
- }
1456
- return cost_verdict.classify(measurement2, CONTEXT_CUT_THRESHOLD);
1457
- }
1458
- function session_verdict(cwd = process.cwd(), environment = process.env) {
1459
- const provider = provider_of(environment);
1460
- if (provider === void 0) return UNMEASURABLE_VERDICT;
1461
- return verdict_of(measure(cwd, provider, environment));
1462
- }
1463
- function run_context(argv, environment) {
1464
- const options = parse_options(argv);
1465
- if (options?.over === void 0) return void 0;
1466
- const provider = provider_of(environment);
1467
- if (provider === void 0) return void 0;
1468
- return { over: options.over, path: options.path, provider };
1469
- }
1470
- function run(argv, cwd = process.cwd(), environment = process.env) {
1471
- const context = run_context(argv, environment);
1472
- if (context === void 0) {
1473
- console.error(USAGE);
1474
- return FAILURE_EXIT_CODE2;
1475
- }
1476
- const target = transcript_cwd.resolve(context.path, cwd);
1477
- if (reject_openai_cross_project(context, target, cwd)) return FAILURE_EXIT_CODE2;
1478
- return run_over(target, context.over, context.provider, environment);
1479
- }
1480
- function main(argv) {
1481
- process.exitCode = run(argv);
1482
- }
1483
- var cost_cli = {
1484
- UNMEASURABLE_VERDICT,
1485
- USAGE,
1486
- parse_options,
1487
- to_threshold,
1488
- run,
1489
- session_verdict
1490
- };
1491
- if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET));
1492
-
1493
1162
  // scripts/cost-runtime/cost-format.ts
1494
1163
  var USD_DECIMALS = 4;
1495
1164
  var PERCENT_SCALE = 100;
@@ -1511,9 +1180,9 @@ var cost_format = {
1511
1180
  };
1512
1181
 
1513
1182
  // scripts/run/cut/run-cut-decline.ts
1514
- import { z as z5 } from "zod";
1183
+ import { z as z4 } from "zod";
1515
1184
  var DECLINE_PREFIX = "josh-run-cut-decline-";
1516
- var decline_schema = z5.object({ issue: z5.string(), hold_taken_at: z5.string().optional() });
1185
+ var decline_schema = z4.object({ issue: z4.string(), hold_taken_at: z4.string().optional() });
1517
1186
  function decline_path(git_directory) {
1518
1187
  return stamp_file.stamp_path(DECLINE_PREFIX, git_directory);
1519
1188
  }
@@ -1567,14 +1236,14 @@ function read_tree_sync() {
1567
1236
  const reference = git_spawn_sync.read(git_diff_reads.DEFAULT_BRANCH_ARGUMENTS);
1568
1237
  return tree_of(status, git_diff_reads.default_branch_of(reference));
1569
1238
  }
1570
- function is_ready_sync(read3 = read_tree_sync) {
1571
- const tree = read3();
1239
+ function is_ready_sync(read2 = read_tree_sync) {
1240
+ const tree = read2();
1572
1241
  return tree === void 0 || is_ready(tree);
1573
1242
  }
1574
1243
  var run_cut_ready = { is_lane_branch, is_ready, is_ready_sync, tree_of };
1575
1244
 
1576
1245
  // scripts/rules/implementation-cut-verdict.ts
1577
- import { z as z6 } from "zod";
1246
+ import { z as z5 } from "zod";
1578
1247
  var VERDICT_CACHE_PREFIX = "josh-impl-cut-verdict-";
1579
1248
  var VERDICT_REUSE_MS = 5e3;
1580
1249
  var VERDICT_VALUES = [
@@ -1582,7 +1251,7 @@ var VERDICT_VALUES = [
1582
1251
  "under",
1583
1252
  "unmeasurable"
1584
1253
  ];
1585
- var cache_schema = z6.object({ read_at_ms: z6.number(), verdict: z6.enum(VERDICT_VALUES) });
1254
+ var cache_schema = z5.object({ read_at_ms: z5.number(), verdict: z5.enum(VERDICT_VALUES) });
1586
1255
  function cache_path(directory) {
1587
1256
  return stamp_file.stamp_path(VERDICT_CACHE_PREFIX, directory);
1588
1257
  }
@@ -1593,11 +1262,11 @@ function cached_verdict(target, now_ms) {
1593
1262
  if (cached === void 0) return void 0;
1594
1263
  return now_ms - cached.read_at_ms < VERDICT_REUSE_MS ? cached.verdict : void 0;
1595
1264
  }
1596
- function reused_verdict(read3, directory = process.cwd(), now_ms = Date.now()) {
1265
+ function reused_verdict(read2, directory = process.cwd(), now_ms = Date.now()) {
1597
1266
  const target = cache_path(directory);
1598
1267
  const cached = cached_verdict(target, now_ms);
1599
1268
  if (cached !== void 0) return cached;
1600
- const value = read3();
1269
+ const value = read2();
1601
1270
  stamp_file.replace_stamp(target, { read_at_ms: now_ms, verdict: value });
1602
1271
  return value;
1603
1272
  }
@@ -1670,10 +1339,10 @@ var REISSUE_WINDOW_MS = 9e4;
1670
1339
  function is_reissued_refusal(now_ms, delivered_at_ms) {
1671
1340
  return now_ms - delivered_at_ms < REISSUE_WINDOW_MS;
1672
1341
  }
1673
- function decide2(_call, run2, _can_record, delivered_at_ms) {
1674
- return !is_reissued_refusal(run2.now_ms, delivered_at_ms);
1342
+ function decide2(_call, run, _can_record, delivered_at_ms) {
1343
+ return !is_reissued_refusal(run.now_ms, delivered_at_ms);
1675
1344
  }
1676
- var ROW4 = {
1345
+ var ROW5 = {
1677
1346
  id: "implementation-cut",
1678
1347
  is_trigger: is_over_threshold_edit,
1679
1348
  reason: IMPLEMENTATION_CUT_REASON,
@@ -1688,7 +1357,7 @@ var ROW4 = {
1688
1357
  var implementation_cut = {
1689
1358
  IMPLEMENTATION_CUT_REASON,
1690
1359
  REISSUE_WINDOW_MS,
1691
- ROW: ROW4,
1360
+ ROW: ROW5,
1692
1361
  decide: decide2,
1693
1362
  is_over_threshold_edit,
1694
1363
  is_reissued_refusal,
@@ -1815,19 +1484,48 @@ function is_body_only_issue_read(command) {
1815
1484
  function reads_issue_comments(command) {
1816
1485
  return shell_segments.segments_of(command).some((segment) => fetches_issue_comments(segment));
1817
1486
  }
1818
- var ISSUE_COMMENTS_REASON = "\u26D4 issue comments: pnpm josh issue:read <N>\nRun that in place of this read \u2014 an Issue's comments are part of it, and a later comment supersedes the body it contradicts; refused until that command is on the transcript. Without josh, read `gh api repos/{owner}/{repo}/issues/<N>/comments` beside the body (`.claude/skills/workflow-commands/issue-comments.md`).";
1819
- var ROW5 = {
1487
+ var RULE_DOC = "`.claude/skills/workflow-commands/issue-comments.md`";
1488
+ var COMMENTS_FALLBACK = "gh api repos/{owner}/{repo}/issues/<N>/comments";
1489
+ var ISSUE_COMMENTS_REASON = `\u26D4 issue comments: pnpm josh issue:read <N>
1490
+ Run that in place of this read \u2014 an Issue's comments are part of it, and a later comment supersedes the body it contradicts; refused until that command is on the transcript. Without josh, read \`${COMMENTS_FALLBACK}\` beside the body (${RULE_DOC}).`;
1491
+ var COMMENTS_JQ = String.raw`'.[] | "— \(.user.login) \(.created_at)\n\(.body)\n"'`;
1492
+ var COMMENTS_NOTE = `\u21BB issue comments: this read returned only the Issue's body, so the Issue's comments were fetched on the same line \u2014 a later comment supersedes the body it contradicts (${RULE_DOC}).`;
1493
+ function rewritable_path(command) {
1494
+ const read2 = shell_segments.first_of(command);
1495
+ if (read2 === void 0 || ISSUE_VIEW_COMMAND.test(read2)) return void 0;
1496
+ return is_body_read_segment(read2) ? ISSUE_BODY_PATH.exec(read2)?.[0] : void 0;
1497
+ }
1498
+ function comments_command(command) {
1499
+ if (!is_body_only_issue_read(command)) return void 0;
1500
+ const path13 = rewritable_path(command);
1501
+ if (path13 === void 0) return void 0;
1502
+ return `gh api --paginate ${path13}/comments --jq ${COMMENTS_JQ}
1503
+ ${command}`;
1504
+ }
1505
+ function comments_input(call, tail) {
1506
+ if (call.name !== cost_blocks.BASH_TOOL || !json_value.is_record(call.input)) return void 0;
1507
+ const command = comments_command(time_shell.bash_command(call.input));
1508
+ if (command === void 0 || prior_comment_read.already_read_for_call(tail(), call)) {
1509
+ return void 0;
1510
+ }
1511
+ return { ...call.input, command };
1512
+ }
1513
+ var ROW6 = {
1820
1514
  id: "issue-comments",
1821
1515
  is_trigger: bash_triggers.on_bash_command(is_body_only_issue_read),
1822
1516
  reason: ISSUE_COMMENTS_REASON,
1823
1517
  already_satisfied: prior_comment_read.already_read_for_call,
1824
1518
  keeps: bash_triggers.on_bash_command(reads_issue_comments),
1519
+ rewrite: comments_input,
1520
+ rewrite_note: COMMENTS_NOTE,
1825
1521
  // The opening the reason carried before it led with the command.
1826
1522
  former_reasons: ["\u26D4 an Issue's comments are part of the Issue: read them before implementing"]
1827
1523
  };
1828
1524
  var issue_comments = {
1525
+ COMMENTS_NOTE,
1829
1526
  ISSUE_COMMENTS_REASON,
1830
- ROW: ROW5,
1527
+ ROW: ROW6,
1528
+ comments_input,
1831
1529
  is_body_only_issue_read
1832
1530
  };
1833
1531
 
@@ -1845,7 +1543,7 @@ function is_bare_josh_git(command) {
1845
1543
  return shell_segments.segments_of(command).some((segment) => is_bare_segment(segment));
1846
1544
  }
1847
1545
  var JOSH_GIT_BARE_REASON = '\u26D4 bare `pnpm josh git`: this prompts to confirm the staging, and with no TTY the prompt falls to its cancel branch. Run `pnpm josh git -y "<title> #<N>"`, or `pnpm josh git -y --skip-commit --skip-push` to open the PR for a push that already landed. It fires on every occurrence.';
1848
- var ROW6 = {
1546
+ var ROW7 = {
1849
1547
  id: "josh-git-bare",
1850
1548
  is_trigger: bash_triggers.on_bash_command(is_bare_josh_git),
1851
1549
  reason: JOSH_GIT_BARE_REASON,
@@ -1853,7 +1551,7 @@ var ROW6 = {
1853
1551
  };
1854
1552
  var josh_git_bare = {
1855
1553
  JOSH_GIT_BARE_REASON,
1856
- ROW: ROW6,
1554
+ ROW: ROW7,
1857
1555
  is_bare_josh_git
1858
1556
  };
1859
1557
 
@@ -1878,7 +1576,7 @@ function is_carry_conflict(command, is_lane_child = lane_child_marker.is_child_o
1878
1576
  return is_lane_child;
1879
1577
  }
1880
1578
  var LANE_CARRY_CONFLICT_REASON = "\u26D4 lane child carry conflict: this session is a dispatched lane child, and `run:merge` / `run:carry` are the parent budget commands; the live record belongs to the parent, not to a competing run. Continue implementing this Issue: no `needs-decision`, no Telegram. Live `run:carry` evidence takes a fresh `JOSH_TEMP_ROOT`; `run:merge` is refused under any root, on every occurrence.";
1881
- var ROW7 = {
1579
+ var ROW8 = {
1882
1580
  id: "lane-carry-conflict",
1883
1581
  is_trigger: bash_triggers.on_bash_command(is_carry_conflict),
1884
1582
  reason: LANE_CARRY_CONFLICT_REASON,
@@ -1886,66 +1584,24 @@ var ROW7 = {
1886
1584
  };
1887
1585
  var lane_carry_conflict = {
1888
1586
  LANE_CARRY_CONFLICT_REASON,
1889
- ROW: ROW7,
1587
+ ROW: ROW8,
1890
1588
  invokes_budget_command,
1891
1589
  is_carry_conflict
1892
1590
  };
1893
1591
 
1894
- // scripts/agent/interactive-ask.ts
1895
- import { z as z7 } from "zod";
1896
- var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion"]);
1897
- function is_interactive_tool(name) {
1898
- return INTERACTIVE_TOOLS.has(name);
1899
- }
1900
- var OPTION_SCHEMA = z7.looseObject({ label: z7.string().optional() });
1901
- var QUESTION_SCHEMA = z7.looseObject({
1902
- question: z7.string().optional(),
1903
- options: z7.array(OPTION_SCHEMA).optional()
1904
- });
1905
- var ASK_INPUT_SCHEMA = z7.looseObject({ questions: z7.array(QUESTION_SCHEMA).optional() });
1906
- var DENIAL_SCHEMA = z7.looseObject({
1907
- tool_name: z7.string().optional(),
1908
- tool_input: z7.unknown().optional()
1909
- });
1910
- var UNKNOWN_QUESTION = "(question text unavailable)";
1911
- var OPTION_SEPARATOR = " / ";
1912
- var QUESTION_SEPARATOR = "; ";
1913
- function labels_of(question) {
1914
- return (question.options ?? []).map((option) => option.label ?? "").filter((label) => label !== "");
1915
- }
1916
- function render_question(question) {
1917
- const labels = labels_of(question);
1918
- const text = question.question ?? UNKNOWN_QUESTION;
1919
- return labels.length === 0 ? text : `${text} [${labels.join(OPTION_SEPARATOR)}]`;
1920
- }
1921
- function interactive_input(raw) {
1922
- const denial = DENIAL_SCHEMA.safeParse(raw);
1923
- if (!denial.success || !is_interactive_tool(denial.data.tool_name ?? "")) return void 0;
1924
- const input = ASK_INPUT_SCHEMA.safeParse(denial.data.tool_input);
1925
- return input.success ? input.data : void 0;
1926
- }
1927
- function refused_ask_of(permission_denials) {
1928
- if (!Array.isArray(permission_denials)) return void 0;
1929
- const input = permission_denials.map((raw) => interactive_input(raw)).find((one) => one !== void 0);
1930
- const questions = input?.questions ?? [];
1931
- if (questions.length === 0) return void 0;
1932
- return questions.map((question) => render_question(question)).join(QUESTION_SEPARATOR);
1933
- }
1934
- var interactive_ask = { is_interactive_tool, refused_ask_of };
1935
-
1936
1592
  // scripts/rules/lane-interactive-ask.ts
1937
1593
  function is_interactive_ask(call, is_lane_child = lane_child_marker.is_child_of(process.cwd())) {
1938
1594
  if (!interactive_ask.is_interactive_tool(call.name)) return false;
1939
1595
  return is_lane_child;
1940
1596
  }
1941
1597
  var LANE_INTERACTIVE_ASK_REASON = "\u26D4 lane child interactive ask: this session is a dispatched lane child and cannot ask a person, so park instead: `gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=needs-decision'`, `pnpm josh issue:comment <N> --body-file <path>`, the `confirmation` Telegram, stop. It fires on every occurrence (`.claude/skills/workflow-commands/pre-gate-cut.md`, `backlogrun-park.md`).";
1942
- var ROW8 = {
1598
+ var ROW9 = {
1943
1599
  id: "lane-interactive-ask",
1944
1600
  is_trigger: is_interactive_ask,
1945
1601
  reason: LANE_INTERACTIVE_ASK_REASON,
1946
1602
  decide: () => true
1947
1603
  };
1948
- var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW8, is_interactive_ask };
1604
+ var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW9, is_interactive_ask };
1949
1605
 
1950
1606
  // scripts/rules/lane-split-park.ts
1951
1607
  var EPIC_COMMANDS = /* @__PURE__ */ new Set(["epic"]);
@@ -1989,13 +1645,13 @@ function is_candidate(command) {
1989
1645
  if (!is_park_of(command, issue) && !is_notify_of(command, issue)) return false;
1990
1646
  return lane_child_marker.is_child_of(process.cwd());
1991
1647
  }
1992
- function decide3(_call, run2) {
1648
+ function decide3(_call, run) {
1993
1649
  const issue = lane_child_marker.marked_issue();
1994
- const commands = tail_commands.prior_bash_commands(time_density_hook.read_tail(run2.transcript));
1650
+ const commands = tail_commands.prior_bash_commands(time_density_hook.read_tail(run.transcript));
1995
1651
  return issue !== void 0 && promoted_issues(commands).has(issue);
1996
1652
  }
1997
1653
  var LANE_SPLIT_PARK_REASON = '\u26D4 lane split park: this session is a dispatched lane child that promoted its own Issue to an epic this run, and a split is not a park: no `needs-decision` on the epic, no `confirmation` Telegram, on every occurrence. Park a child issue if the rest needs a person, else end the turn (`.claude/skills/workflow-commands/backlogrun-park.md` \u2192 "Splitting a child mid-run").';
1998
- var ROW9 = {
1654
+ var ROW10 = {
1999
1655
  id: "lane-split-park",
2000
1656
  is_trigger: bash_triggers.on_bash_command(is_candidate),
2001
1657
  reason: LANE_SPLIT_PARK_REASON,
@@ -2003,7 +1659,7 @@ var ROW9 = {
2003
1659
  };
2004
1660
  var lane_split_park = {
2005
1661
  LANE_SPLIT_PARK_REASON,
2006
- ROW: ROW9,
1662
+ ROW: ROW10,
2007
1663
  promoted_issues
2008
1664
  };
2009
1665
 
@@ -2037,7 +1693,7 @@ function is_switch_away_from_lane(command, is_lane_child = lane_child_marker.is_
2037
1693
  return switch_targets(command).some((target) => target !== own_branch);
2038
1694
  }
2039
1695
  var LANE_SWITCH_MAIN_REASON = "\u26D4 lane child on `git switch main`: this session is a dispatched lane child, and switching to the default branch here always fails (`already used by worktree`: the primary checkout holds it). Skip this step and keep implementing on the current branch; `pnpm josh main:merge` brings the default in during the gate. Refused on every occurrence (`backlogrun-lanes.md`, `backlogrun-child.md`).";
2040
- var ROW10 = {
1696
+ var ROW11 = {
2041
1697
  id: "lane-switch-main",
2042
1698
  is_trigger: bash_triggers.on_bash_command(is_switch_away_from_lane),
2043
1699
  reason: LANE_SWITCH_MAIN_REASON,
@@ -2045,7 +1701,7 @@ var ROW10 = {
2045
1701
  };
2046
1702
  var lane_switch_main = {
2047
1703
  LANE_SWITCH_MAIN_REASON,
2048
- ROW: ROW10,
1704
+ ROW: ROW11,
2049
1705
  is_switch_away_from_lane,
2050
1706
  switch_target,
2051
1707
  switch_targets
@@ -2615,7 +2271,7 @@ function is_destructive_command(command) {
2615
2271
  return shell_segments.segments_of(command).some((segment) => is_destructive_segment2(segment));
2616
2272
  }
2617
2273
  var DESTRUCTIVE_COMMAND_REASON = "\u26D4 destructive command: a recursive forced `rm`, a `gh repo delete` / `archive` / visibility edit, `gh pr close` or a `gh api -X DELETE` cannot be undone, which is Tier C. Remove one path at a time without `-f`, or ask the user to run it. It fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md`).";
2618
- var ROW11 = {
2274
+ var ROW12 = {
2619
2275
  id: "destructive-command",
2620
2276
  is_trigger: bash_triggers.on_bash_command(is_destructive_command),
2621
2277
  reason: DESTRUCTIVE_COMMAND_REASON,
@@ -2623,7 +2279,7 @@ var ROW11 = {
2623
2279
  // The opening the reason carried before it was cut to the action and the next command.
2624
2280
  former_reasons: ["\u26D4 destructive command: a recursive forced `rm` (`rm -rf`"]
2625
2281
  };
2626
- var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW11, is_destructive_command };
2282
+ var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW12, is_destructive_command };
2627
2283
 
2628
2284
  // scripts/rules/direct-pr-create.ts
2629
2285
  var GH_COMMAND2 = "gh";
@@ -2678,13 +2334,13 @@ function is_direct_pr_create(command) {
2678
2334
  return shell_segments.segments_of(command).some((segment) => is_pr_create_segment(segment));
2679
2335
  }
2680
2336
  var DIRECT_PR_CREATE_REASON = "\u26D4 direct PR creation: `gh pr create` (or a `gh api` POST to `repos/<o>/<r>/pulls`) bypasses `build_body`, which writes the `closes #N` line \u2014 the Issue would stay open after the merge (`CLAUDE.md` \u2192 Git Rules). Open the PR with `pnpm josh pr` instead; after a failed push, fix, push, then run `pnpm josh pr`. **This rule fires on every occurrence, not once per run.**";
2681
- var ROW12 = {
2337
+ var ROW13 = {
2682
2338
  id: "direct-pr-create",
2683
2339
  is_trigger: bash_triggers.on_bash_command(is_direct_pr_create),
2684
2340
  reason: DIRECT_PR_CREATE_REASON,
2685
2341
  decide: () => true
2686
2342
  };
2687
- var direct_pr_create = { DIRECT_PR_CREATE_REASON, ROW: ROW12, is_direct_pr_create };
2343
+ var direct_pr_create = { DIRECT_PR_CREATE_REASON, ROW: ROW13, is_direct_pr_create };
2688
2344
 
2689
2345
  // scripts/rules/worktree-guard.ts
2690
2346
  var CHECKOUT_SUBCOMMAND = "checkout";
@@ -2746,14 +2402,14 @@ function is_unauthorized_worktree_change(command) {
2746
2402
  return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
2747
2403
  }
2748
2404
  var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout --`, `git restore`, `git clean -f`, `git stash`. A documented flow stashes with `git stash push -u -m "<message>"`; a handed-over stash is `pnpm josh stash:pop "<message>"`; record the substitution on the Issue (Tier A) unless the plan says why the owner keeps the stash: then stop and confirm. It fires on every occurrence (`operating-rules.md`).';
2749
- var ROW13 = {
2405
+ var ROW14 = {
2750
2406
  id: "worktree-mutation",
2751
2407
  is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
2752
2408
  reason: WORKTREE_MUTATION_REASON,
2753
2409
  decide: () => true
2754
2410
  };
2755
2411
  var worktree_guard = {
2756
- ROW: ROW13,
2412
+ ROW: ROW14,
2757
2413
  WORKTREE_MUTATION_REASON,
2758
2414
  is_unauthorized_worktree_change,
2759
2415
  touches_worktree
@@ -2782,45 +2438,45 @@ function is_index_mutation(command) {
2782
2438
  return shell_segments.segments_of(command).some((segment) => is_index_segment(segment));
2783
2439
  }
2784
2440
  var INDEX_MUTATION_REASON = "\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user's staged snapshot. Commit with `pnpm josh git -y`; any other staging is the user's, on explicit instruction. It fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md` \u2192 \"no-self-staging\").";
2785
- var ROW14 = {
2441
+ var ROW15 = {
2786
2442
  id: "index-mutation",
2787
2443
  is_trigger: bash_triggers.on_bash_command(is_index_mutation),
2788
2444
  reason: INDEX_MUTATION_REASON,
2789
2445
  decide: () => true
2790
2446
  };
2791
- var index_guard = { INDEX_MUTATION_REASON, ROW: ROW14, is_index_mutation };
2447
+ var index_guard = { INDEX_MUTATION_REASON, ROW: ROW15, is_index_mutation };
2792
2448
 
2793
2449
  // scripts/rules/protected-files.ts
2794
- import path5 from "node:path";
2450
+ import path3 from "node:path";
2795
2451
 
2796
2452
  // scripts/doctor/doctor-consumer.ts
2797
2453
  import { existsSync as existsSync2 } from "node:fs";
2798
- import path4 from "node:path";
2454
+ import path2 from "node:path";
2799
2455
 
2800
2456
  // scripts/version/kit-descriptor.ts
2801
2457
  var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
2802
2458
 
2803
2459
  // scripts/doctor/doctor-consumer.ts
2804
- import { z as z8 } from "zod";
2460
+ import { z as z6 } from "zod";
2805
2461
  var NODE_MODULES = "node_modules";
2806
- var SETTINGS_PATH = path4.join(".claude", "settings.json");
2462
+ var SETTINGS_PATH = path2.join(".claude", "settings.json");
2807
2463
  var CLAUDE_MD = "CLAUDE.md";
2808
2464
  var PLUGIN_ID = "kit@kit";
2809
2465
  var CLAUDE_MD_POINTER = `@${NODE_MODULES}/${KIT_PACKAGE_NAME}/dist/${CLAUDE_MD}`;
2810
- var SECRETLINT_BIN = path4.join(NODE_MODULES, ".bin", "secretlint");
2811
- var manifest_schema = z8.object({
2812
- name: z8.string().optional(),
2813
- dependencies: z8.record(z8.string(), z8.string()).optional(),
2814
- devDependencies: z8.record(z8.string(), z8.string()).optional()
2466
+ var SECRETLINT_BIN = path2.join(NODE_MODULES, ".bin", "secretlint");
2467
+ var manifest_schema = z6.object({
2468
+ name: z6.string().optional(),
2469
+ dependencies: z6.record(z6.string(), z6.string()).optional(),
2470
+ devDependencies: z6.record(z6.string(), z6.string()).optional()
2815
2471
  });
2816
- var settings_schema = z8.object({
2817
- enabledPlugins: z8.record(z8.string(), z8.boolean()).optional()
2472
+ var settings_schema = z6.object({
2473
+ enabledPlugins: z6.record(z6.string(), z6.boolean()).optional()
2818
2474
  });
2819
2475
  var STATE_OK = "ok";
2820
2476
  var STATE_TARGET_MISSING = "target-missing";
2821
2477
  var STATE_MISSING = "missing";
2822
2478
  function parse_manifest(root) {
2823
- const content = file_reader.read_if_readable(path4.join(root, "package.json"));
2479
+ const content = file_reader.read_if_readable(path2.join(root, "package.json"));
2824
2480
  if (content === void 0) return void 0;
2825
2481
  const parsed = manifest_schema.safeParse(json_value.parse_or_undefined(content));
2826
2482
  return parsed.success ? parsed.data : void 0;
@@ -2832,10 +2488,10 @@ function lists_kit_dependency(manifest) {
2832
2488
  function is_kit_consumer(root) {
2833
2489
  const manifest = parse_manifest(root);
2834
2490
  if (manifest?.name === KIT_PACKAGE_NAME) return false;
2835
- return lists_kit_dependency(manifest) || existsSync2(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME));
2491
+ return lists_kit_dependency(manifest) || existsSync2(path2.join(root, NODE_MODULES, KIT_PACKAGE_NAME));
2836
2492
  }
2837
2493
  function is_plugin_declared(root) {
2838
- const content = file_reader.read_if_readable(path4.join(root, SETTINGS_PATH));
2494
+ const content = file_reader.read_if_readable(path2.join(root, SETTINGS_PATH));
2839
2495
  if (content === void 0) return false;
2840
2496
  const parsed = settings_schema.safeParse(json_value.parse_or_undefined(content));
2841
2497
  return parsed.success && parsed.data.enabledPlugins?.[PLUGIN_ID] === true;
@@ -2847,13 +2503,13 @@ function configured_hooks_path(root) {
2847
2503
  return value === "" ? void 0 : value;
2848
2504
  }
2849
2505
  function claude_md_state(root) {
2850
- const content = file_reader.read_if_readable(path4.join(root, CLAUDE_MD));
2506
+ const content = file_reader.read_if_readable(path2.join(root, CLAUDE_MD));
2851
2507
  if (content === void 0) return STATE_MISSING;
2852
2508
  if (!content.includes(CLAUDE_MD_POINTER)) return STATE_OK;
2853
- return existsSync2(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME, "dist", CLAUDE_MD)) ? STATE_OK : STATE_TARGET_MISSING;
2509
+ return existsSync2(path2.join(root, NODE_MODULES, KIT_PACKAGE_NAME, "dist", CLAUDE_MD)) ? STATE_OK : STATE_TARGET_MISSING;
2854
2510
  }
2855
2511
  function is_secretlint_runnable(root) {
2856
- return existsSync2(path4.join(root, SECRETLINT_BIN));
2512
+ return existsSync2(path2.join(root, SECRETLINT_BIN));
2857
2513
  }
2858
2514
  function status_line(is_ok, ok_text, warn_text) {
2859
2515
  return is_ok ? ` \u2713 ${ok_text}` : ` \u26A0 ${warn_text}`;
@@ -2919,7 +2575,7 @@ var EDIT_TOOLS2 = /* @__PURE__ */ new Set(["Edit", "Write"]);
2919
2575
  var SECRET_FILES = /* @__PURE__ */ new Set([".env", ".dev.vars"]);
2920
2576
  var ENVIRONMENT_VARIANT_PREFIX = ".env.";
2921
2577
  var TRACKED_VARIANTS = /* @__PURE__ */ new Set([".env.example", ".env.test"]);
2922
- var SETTINGS_FILE = path5.join(".claude", "settings.json");
2578
+ var SETTINGS_FILE = path3.join(".claude", "settings.json");
2923
2579
  var WHITESPACE3 = /\s+/u;
2924
2580
  var TOKEN_QUOTES = /["']/gu;
2925
2581
  var INPUT_REDIRECT = "<";
@@ -2949,7 +2605,7 @@ function file_path_of(call) {
2949
2605
  return typeof value === "string" ? value : "";
2950
2606
  }
2951
2607
  function is_environment_path(token) {
2952
- const name = path5.basename(token.replaceAll(TOKEN_QUOTES, ""));
2608
+ const name = path3.basename(token.replaceAll(TOKEN_QUOTES, ""));
2953
2609
  if (TRACKED_VARIANTS.has(name)) return false;
2954
2610
  return SECRET_FILES.has(name) || name.startsWith(ENVIRONMENT_VARIANT_PREFIX);
2955
2611
  }
@@ -2970,15 +2626,15 @@ function is_environment_read(call) {
2970
2626
  }
2971
2627
  function is_consumer_settings_edit(call, is_kit_consumer2) {
2972
2628
  if (!EDIT_TOOLS2.has(call.name)) return false;
2973
- const file_path = path5.resolve(file_path_of(call));
2974
- if (!file_path.endsWith(`${path5.sep}${SETTINGS_FILE}`)) return false;
2975
- return is_kit_consumer2(path5.dirname(path5.dirname(file_path)));
2629
+ const file_path = path3.resolve(file_path_of(call));
2630
+ if (!file_path.endsWith(`${path3.sep}${SETTINGS_FILE}`)) return false;
2631
+ return is_kit_consumer2(path3.dirname(path3.dirname(file_path)));
2976
2632
  }
2977
2633
  function is_protected_file_call(call, is_kit_consumer2 = doctor_consumer.is_kit_consumer) {
2978
2634
  return is_environment_read(call) || is_consumer_settings_edit(call, is_kit_consumer2);
2979
2635
  }
2980
2636
  var PROTECTED_FILE_REASON = "\u26D4 protected file: `.env`, a `.env.*` variant and `.dev.vars` hold secrets, and a consumer `.claude/settings.json` is owned by `josh sync` (a change belongs upstream in kit). Leave these files alone, or ask the user. It fires on every occurrence.";
2981
- var ROW15 = {
2637
+ var ROW16 = {
2982
2638
  id: "protected-file",
2983
2639
  is_trigger: is_protected_file_call,
2984
2640
  reason: PROTECTED_FILE_REASON,
@@ -2986,7 +2642,7 @@ var ROW15 = {
2986
2642
  // The opening the reason carried before it was cut to the action and the next command.
2987
2643
  former_reasons: ["\u26D4 protected file: `.env` holds the Telegram bot token"]
2988
2644
  };
2989
- var protected_files = { ROW: ROW15, is_protected_file_call };
2645
+ var protected_files = { ROW: ROW16, is_protected_file_call };
2990
2646
 
2991
2647
  // scripts/rules/permission-guards.ts
2992
2648
  var ROWS2 = [
@@ -3037,7 +2693,7 @@ function keeps_verdict_intact(command) {
3037
2693
  var PIPEFAIL_OPEN = "(set -o pipefail; ";
3038
2694
  var PIPEFAIL_CLOSE = ")";
3039
2695
  var COMMENT_START = /(?:^|\s)#/u;
3040
- var FULL_READING_FILTER = /^(?:tail|grep)\b/u;
2696
+ var FULL_READING_FILTER = /^(?:tail|grep|cut|sort|uniq|wc)\b/u;
3041
2697
  var EARLY_EXIT_GREP = /^grep\b.*\s(?:-[a-zA-Z]*[lLmq]|--(?:quiet|silent|max-count|files-with(?:out)?-match))/u;
3042
2698
  var ALLOWED_OPERATORS = /2>&1|&&/gu;
3043
2699
  var SIDE_EFFECT_SYNTAX = /[&<>`]|\$\(/u;
@@ -3124,7 +2780,7 @@ function is_poll_loop(command) {
3124
2780
  return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain);
3125
2781
  }
3126
2782
  var POLL_LOOP_REASON = "\u26D4 output poll loop: this hand-writes a `while`/`until` wait loop over a command's output, a marker file or a process, and the loop does not know when the command finished. Issue the long command with `run_in_background` set and end the turn \u2014 its exit re-invokes you; for CI, `pnpm josh followup` (`.claude/skills/workflow-commands/background-commands.md`). It fires on every occurrence.";
3127
- var ROW16 = {
2783
+ var ROW17 = {
3128
2784
  id: "poll-loop",
3129
2785
  is_trigger: bash_triggers.on_bash_command(is_poll_loop),
3130
2786
  reason: POLL_LOOP_REASON,
@@ -3132,86 +2788,23 @@ var ROW16 = {
3132
2788
  };
3133
2789
  var poll_loop = {
3134
2790
  POLL_LOOP_REASON,
3135
- ROW: ROW16,
2791
+ ROW: ROW17,
3136
2792
  is_poll_loop
3137
2793
  };
3138
2794
 
3139
- // scripts/rules/pre-gate-cut.ts
3140
- var GATE_COMMANDS = /* @__PURE__ */ new Set(["gate"]);
3141
- var CUT_COMMANDS2 = /* @__PURE__ */ new Set(["run:cut"]);
3142
- var CUT_MODE_FLAG = /(?:^|\s)--(?:resume|end|json)(?:[=\s]|$)/u;
3143
- var HOLD_COMMANDS = /* @__PURE__ */ new Set(["run:hold"]);
3144
- var HOLD_MODE_FLAG = /(?:^|\s)--release(?:[=\s]|$)/u;
3145
- function invokes_josh(command, names, mode_flag) {
3146
- return shell_segments.segments_of(command).some(
3147
- (segment) => shell_segments.is_josh_command(segment, names) && mode_flag?.test(segment) !== true
3148
- );
3149
- }
3150
- function runs_the_gate(command) {
3151
- return invokes_josh(command, GATE_COMMANDS);
3152
- }
3153
- function takes_the_cut(command) {
3154
- return invokes_josh(command, CUT_COMMANDS2, CUT_MODE_FLAG);
3155
- }
3156
- function claims_the_hold(command) {
3157
- return invokes_josh(command, HOLD_COMMANDS, HOLD_MODE_FLAG);
3158
- }
3159
- function asks_about_the_cut(command) {
3160
- return invokes_josh(command, CUT_COMMANDS2);
3161
- }
3162
- function current_state2() {
3163
- return {
3164
- directory: process.cwd(),
3165
- carried: run_cut.carried_cut_sync,
3166
- marked_issue: lane_child_marker.marked_issue(),
3167
- is_reviewer: agent_session_role.is_reviewer(),
3168
- context_verdict: cost_cli.session_verdict
3169
- };
3170
- }
3171
- function dispatched_issue(state) {
3172
- return state.is_reviewer ? void 0 : state.marked_issue;
3173
- }
3174
- function uncut_lane_issue(state) {
3175
- const issue = lane_paths.lane_issue_of(state.directory);
3176
- if (issue === void 0 || dispatched_issue(state) !== issue) return void 0;
3177
- return state.carried()?.issue === issue ? void 0 : issue;
3178
- }
3179
- function warrants_the_cut2(verdict) {
3180
- return verdict !== cost_verdict.UNDER_VERDICT;
3181
- }
3182
- function is_uncut_gate(command, state = current_state2()) {
3183
- if (!runs_the_gate(command) || uncut_lane_issue(state) === void 0) return false;
3184
- return warrants_the_cut2(state.context_verdict());
3185
- }
3186
- var PRE_GATE_CUT_REASON = "\u26D4 pre-gate cut: this checkout is a lane dispatched for this issue and the gate is the boundary to cut at. Issue `pnpm josh run:cut <N>`: on `cut`, end the turn immediately \u2014 no Telegram is owed; on any other verdict (`not-a-lane`, `busy`, \u2026), reissue the gate \u2014 this fires once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
3187
- function reaches_the_pre_gate_boundary(call, _turn, run2) {
3188
- if (!bash_triggers.on_bash_command(asks_about_the_cut)(call)) return false;
3189
- return run2.some((issued) => bash_triggers.on_bash_command(claims_the_hold)(issued));
3190
- }
3191
- var pre_gate_cut = {
3192
- PRE_GATE_CUT_REASON,
3193
- asks_about_the_cut,
3194
- claims_the_hold,
3195
- is_uncut_gate,
3196
- reaches_the_pre_gate_boundary,
3197
- runs_the_gate,
3198
- takes_the_cut,
3199
- uncut_lane_issue
3200
- };
3201
-
3202
2795
  // scripts/rules/raw-field-body.ts
3203
2796
  var RAW_FIELD_BODY_AT = /(?:^|\s)(?:-f|--raw-field)[\s=]+['"]?body=['"]?@/u;
3204
2797
  function posts_at_path_literally(command) {
3205
2798
  return RAW_FIELD_BODY_AT.test(command);
3206
2799
  }
3207
2800
  var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which posts the string `@<path>` rather than the file. Post the comment with `pnpm josh issue:comment <N> --body-file <path>`; this rule fires on every occurrence (`prompts/collaboration-workflow/shell-body.md`).";
3208
- var ROW17 = {
2801
+ var ROW18 = {
3209
2802
  id: "raw-field-body",
3210
2803
  is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
3211
2804
  reason: RAW_FIELD_BODY_REASON,
3212
2805
  decide: () => true
3213
2806
  };
3214
- var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW17, posts_at_path_literally };
2807
+ var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW18, posts_at_path_literally };
3215
2808
 
3216
2809
  // scripts/rules/rule-body-guard.ts
3217
2810
  var EDIT_TOOL = "Edit";
@@ -3238,10 +2831,10 @@ function string_field2(input, key) {
3238
2831
  function read_existing(file_path) {
3239
2832
  return file_reader.read_file_or_empty(file_path);
3240
2833
  }
3241
- function texts_of(name, input, read3) {
2834
+ function texts_of(name, input, read2) {
3242
2835
  if (name === WRITE_TOOL) {
3243
2836
  return {
3244
- old_text: read3(string_field2(input, "file_path")),
2837
+ old_text: read2(string_field2(input, "file_path")),
3245
2838
  new_text: string_field2(input, "content")
3246
2839
  };
3247
2840
  }
@@ -3257,10 +2850,10 @@ function rule_document_input(call) {
3257
2850
  if (!is_edit_tool2(call.name) || !json_value.is_record(call.input)) return void 0;
3258
2851
  return is_rule_document(string_field2(call.input, "file_path")) ? call.input : void 0;
3259
2852
  }
3260
- function writes_rule_prose(call, read3 = read_existing) {
2853
+ function writes_rule_prose(call, read2 = read_existing) {
3261
2854
  const input = rule_document_input(call);
3262
2855
  if (input === void 0) return false;
3263
- const { old_text, new_text } = texts_of(call.name, input, read3);
2856
+ const { old_text, new_text } = texts_of(call.name, input, read2);
3264
2857
  return adds_rule_prose(old_text, new_text);
3265
2858
  }
3266
2859
  var RULE_BODY_REASON = "\u26D4 rule written into prose: this edit adds rule text to CLAUDE.md, a `prompts/**` doc or a skill body. Answer question 0 (`pnpm josh oracle:list`) and the ordering question (`pnpm josh run:step`) of `prompts/collaboration-workflow/residency.md`; refused until both commands are on the transcript, then reissue this as a trigger-plus-pointer line (`prompts/collaboration-workflow/rule-delivery.md`).";
@@ -3282,7 +2875,7 @@ function runs_placement_oracle(command) {
3282
2875
  (names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
3283
2876
  );
3284
2877
  }
3285
- var ROW18 = {
2878
+ var ROW19 = {
3286
2879
  id: "rule-body",
3287
2880
  is_trigger: writes_rule_prose,
3288
2881
  reason: RULE_BODY_REASON,
@@ -3291,7 +2884,7 @@ var ROW18 = {
3291
2884
  };
3292
2885
  var rule_body_guard = {
3293
2886
  RULE_BODY_REASON,
3294
- ROW: ROW18,
2887
+ ROW: ROW19,
3295
2888
  answered_placement,
3296
2889
  is_rule_document,
3297
2890
  writes_rule_prose
@@ -3368,12 +2961,12 @@ var shell_body_trigger = {
3368
2961
  };
3369
2962
 
3370
2963
  // scripts/gate/project-checks.ts
3371
- import { existsSync as existsSync7, globSync as globSync2, readFileSync as readFileSync6 } from "node:fs";
3372
- import path13 from "node:path";
2964
+ import { existsSync as existsSync7, globSync, readFileSync as readFileSync5 } from "node:fs";
2965
+ import path11 from "node:path";
3373
2966
 
3374
2967
  // scripts/init/own-formatter.ts
3375
2968
  import { existsSync as existsSync3 } from "node:fs";
3376
- import path6 from "node:path";
2969
+ import path4 from "node:path";
3377
2970
  var OWN_FORMATTER_CONFIGS = [
3378
2971
  "biome.json",
3379
2972
  "biome.jsonc",
@@ -3383,18 +2976,18 @@ var OWN_FORMATTER_CONFIGS = [
3383
2976
  ".dprint.jsonc"
3384
2977
  ];
3385
2978
  function find_config(root) {
3386
- return OWN_FORMATTER_CONFIGS.find((name) => existsSync3(path6.join(root, name)));
2979
+ return OWN_FORMATTER_CONFIGS.find((name) => existsSync3(path4.join(root, name)));
3387
2980
  }
3388
2981
  var own_formatter = { find_config };
3389
2982
 
3390
2983
  // scripts/init/project-profile.ts
3391
- import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync5 } from "node:fs";
3392
- import path12 from "node:path";
2984
+ import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync4 } from "node:fs";
2985
+ import path10 from "node:path";
3393
2986
 
3394
2987
  // scripts/safe-chain/pack-hook.ts
3395
- import { z as z9 } from "zod";
2988
+ import { z as z7 } from "zod";
3396
2989
  var PACK_HOOK_FILE = ".pnpmfile.mjs";
3397
- var manifest_schema2 = z9.looseObject({ private: z9.boolean().optional() });
2990
+ var manifest_schema2 = z7.looseObject({ private: z7.boolean().optional() });
3398
2991
  var MESSAGES = {
3399
2992
  written: ` \u2714 synced ${PACK_HOOK_FILE} (run \`pnpm install\` to refresh pnpmfileChecksum)`,
3400
2993
  unchanged: ` \u2714 unchanged ${PACK_HOOK_FILE}`,
@@ -3430,15 +3023,15 @@ var yaml_document = { is_mapping_document, parse_yaml, parse_yaml_documents };
3430
3023
 
3431
3024
  // scripts/version/release-age.ts
3432
3025
  import semver from "semver";
3433
- import { z as z10 } from "zod";
3434
- var workspace_age_schema = z10.object({ minimumReleaseAge: z10.number().int().nonnegative() });
3435
- var release_times_schema = z10.record(z10.string(), z10.string());
3026
+ import { z as z8 } from "zod";
3027
+ var workspace_age_schema = z8.object({ minimumReleaseAge: z8.number().int().nonnegative() });
3028
+ var release_times_schema = z8.record(z8.string(), z8.string());
3436
3029
 
3437
3030
  // scripts/safe-chain/project-config.ts
3438
3031
  import { dump } from "js-yaml";
3439
- import { z as z11 } from "zod";
3032
+ import { z as z9 } from "zod";
3440
3033
  var PROJECT_CONFIG_FILE = ".aikido";
3441
- var workspace_schema = z11.object({ minimumReleaseAgeExclude: z11.array(z11.string()).optional() });
3034
+ var workspace_schema = z9.object({ minimumReleaseAgeExclude: z9.array(z9.string()).optional() });
3442
3035
 
3443
3036
  // scripts/config-merge/json-format.ts
3444
3037
  var PRINT_WIDTH = 100;
@@ -3483,9 +3076,9 @@ function inline_short_arrays(text) {
3483
3076
  const output = [];
3484
3077
  let index = 0;
3485
3078
  while (index < lines.length) {
3486
- const rewrite = rewrite_at(lines, index, lines[index] ?? "");
3487
- output.push(...rewrite.lines);
3488
- index = rewrite.next;
3079
+ const rewrite3 = rewrite_at(lines, index, lines[index] ?? "");
3080
+ output.push(...rewrite3.lines);
3081
+ index = rewrite3.next;
3489
3082
  }
3490
3083
  return output.join("\n");
3491
3084
  }
@@ -3520,86 +3113,6 @@ var safe_chain_preinstall = {
3520
3113
  migrate_preinstall
3521
3114
  };
3522
3115
 
3523
- // scripts/init/distributed-paths.ts
3524
- var KIT_PACKAGE_PATH_PREFIX = "node_modules/@joshuafolkken/kit/";
3525
- var PROMPTS_PACKAGE_PREFIX = `${KIT_PACKAGE_PATH_PREFIX}prompts/`;
3526
- var ESLINT_PACKAGE_PREFIX = `${KIT_PACKAGE_PATH_PREFIX}eslint/`;
3527
- var GITHUB_REPO_BLOB_BASE = "https://github.com/joshuafolkken/kit/blob/main/";
3528
- var GITHUB_REPO_TREE_BASE = "https://github.com/joshuafolkken/kit/tree/main/";
3529
- var CLAUDE_MD_IMPORT_LINE = "@node_modules/@joshuafolkken/kit/dist/CLAUDE.md";
3530
- var OLD_CLAUDE_MD_BOOTSTRAP = "> Fresh checkout: if kit is not installed, run `pnpm install` first, then reread this file before doing any other work.";
3531
- var CLAUDE_MD_BOOTSTRAP = '> Fresh checkout: before `pnpm install`, run `pnpm config get "@joshuafolkken:registry"` from the project root to check the effective registry, including user-level settings. If it points to GitHub Packages, [set up authentication](https://github.com/joshuafolkken/kit/blob/main/docs/authentication.md#1-get-a-token-from-the-gh-cli) first. Then run `pnpm install` and reread this file before doing any other work.';
3532
- function transform_prompt_paths(content) {
3533
- return content.replaceAll(
3534
- /`prompts\/([^`*]+)`/gu,
3535
- (_match, prompt_path) => `\`${PROMPTS_PACKAGE_PREFIX}${prompt_path}\``
3536
- );
3537
- }
3538
- function transform_eslint_paths(content) {
3539
- return content.replaceAll(
3540
- /`eslint\/([^`*]+)`/gu,
3541
- (_match, eslint_path) => `\`${ESLINT_PACKAGE_PREFIX}${eslint_path}\``
3542
- );
3543
- }
3544
- function transform_test_file_paths(content) {
3545
- return content.replaceAll(
3546
- /`([^`*]+\.(?:test|spec)\.ts)`/gu,
3547
- (_match, test_path) => `\`${GITHUB_REPO_BLOB_BASE}${test_path}\``
3548
- );
3549
- }
3550
- function github_url_base(reference_path) {
3551
- return reference_path.endsWith("/") ? GITHUB_REPO_TREE_BASE : GITHUB_REPO_BLOB_BASE;
3552
- }
3553
- function transform_documentation_paths(content) {
3554
- return content.replaceAll(
3555
- /`(docs\/[^`*]*)`/gu,
3556
- (_match, documentation_path) => `\`${github_url_base(documentation_path)}${documentation_path}\``
3557
- );
3558
- }
3559
- function transform_distributed_paths(content) {
3560
- const with_prompts = transform_prompt_paths(content);
3561
- const with_eslint = transform_eslint_paths(with_prompts);
3562
- const with_tests = transform_test_file_paths(with_eslint);
3563
- return transform_documentation_paths(with_tests);
3564
- }
3565
- function ensure_claude_md_import(existing) {
3566
- if (existing === void 0) return `${CLAUDE_MD_BOOTSTRAP}
3567
-
3568
- ${CLAUDE_MD_IMPORT_LINE}
3569
- `;
3570
- const updated = existing.replace(OLD_CLAUDE_MD_BOOTSTRAP, () => CLAUDE_MD_BOOTSTRAP);
3571
- if (updated.includes(CLAUDE_MD_IMPORT_LINE)) {
3572
- if (updated.includes(CLAUDE_MD_BOOTSTRAP)) return updated;
3573
- return `${CLAUDE_MD_BOOTSTRAP}
3574
-
3575
- ${updated}`;
3576
- }
3577
- return `${CLAUDE_MD_BOOTSTRAP}
3578
-
3579
- ${CLAUDE_MD_IMPORT_LINE}
3580
-
3581
- ${updated}`;
3582
- }
3583
- var FULL_CLAUDE_MD_LINES = /* @__PURE__ */ new Set([
3584
- CLAUDE_MD_BOOTSTRAP,
3585
- OLD_CLAUDE_MD_BOOTSTRAP,
3586
- CLAUDE_MD_IMPORT_LINE
3587
- ]);
3588
- function remove_claude_md_import(content) {
3589
- const lines = content.split("\n");
3590
- return lines.filter((line, index) => {
3591
- if (FULL_CLAUDE_MD_LINES.has(line)) return false;
3592
- return line !== "" || !FULL_CLAUDE_MD_LINES.has(lines[index - 1] ?? "");
3593
- }).join("\n");
3594
- }
3595
- var distributed_paths = {
3596
- transform_prompt_paths,
3597
- transform_distributed_paths,
3598
- ensure_claude_md_import,
3599
- remove_claude_md_import,
3600
- CLAUDE_MD_IMPORT_LINE
3601
- };
3602
-
3603
3116
  // scripts/init/init-logic-deploy-vps.ts
3604
3117
  import semver2 from "semver";
3605
3118
  var PNPM_INSTALL_PATTERN = /npm install -g pnpm@\d[\d.]{0,19}/gu;
@@ -3881,10 +3394,10 @@ function set_json_key(content, key, value) {
3881
3394
  const indented = fit_value(content, value_text, line_indent(content, existing.offset));
3882
3395
  return with_trailing_newline(replace_range(content, existing, indented));
3883
3396
  }
3884
- function resolve_parent(content, path16) {
3397
+ function resolve_parent(content, path13) {
3885
3398
  const root = parse_root(content);
3886
3399
  if (root === void 0) return void 0;
3887
- const parent = path16.length > 1 ? findNodeAtLocation(root, path16.slice(0, -1)) : root;
3400
+ const parent = path13.length > 1 ? findNodeAtLocation(root, path13.slice(0, -1)) : root;
3888
3401
  return parent?.type === "object" ? parent : void 0;
3889
3402
  }
3890
3403
  function cut_ranges(content, parent, ranges) {
@@ -3895,24 +3408,24 @@ function cut_ranges(content, parent, ranges) {
3895
3408
  }
3896
3409
  return next;
3897
3410
  }
3898
- function rebalance_new_last(content, path16) {
3899
- const parent = resolve_parent(content, path16);
3411
+ function rebalance_new_last(content, path13) {
3412
+ const parent = resolve_parent(content, path13);
3900
3413
  const last = parent?.children?.at(-1);
3901
3414
  if (parent === void 0 || last === void 0) return content;
3902
3415
  return rebalance(content, last, has_separator_after(content, parent.offset, parent.offset));
3903
3416
  }
3904
- function cut_property(content, parent, path16) {
3905
- const index = property_index(parent, path16.at(-1) ?? "");
3417
+ function cut_property(content, parent, path13) {
3418
+ const index = property_index(parent, path13.at(-1) ?? "");
3906
3419
  const ranges = removal_ranges(content, parent, index);
3907
3420
  if (ranges.length === 0) return content;
3908
3421
  const cut = cut_ranges(content, parent, ranges);
3909
3422
  const did_end_object = index === (parent.children ?? []).length - 1;
3910
- return did_end_object ? rebalance_new_last(cut, path16) : cut;
3423
+ return did_end_object ? rebalance_new_last(cut, path13) : cut;
3911
3424
  }
3912
- function remove_json_path(content, path16) {
3913
- const parent = resolve_parent(content, path16);
3425
+ function remove_json_path(content, path13) {
3426
+ const parent = resolve_parent(content, path13);
3914
3427
  if (parent === void 0) return content;
3915
- const next = cut_property(content, parent, path16);
3428
+ const next = cut_property(content, parent, path13);
3916
3429
  return next === content ? content : with_trailing_newline(next);
3917
3430
  }
3918
3431
  function remove_json_key(content, key) {
@@ -4143,13 +3656,13 @@ var kit_base_preset = {
4143
3656
 
4144
3657
  // scripts/init/tsconfig-preset-migration.ts
4145
3658
  import { existsSync as existsSync4 } from "node:fs";
4146
- import path7 from "node:path";
3659
+ import path5 from "node:path";
4147
3660
  var LEGACY_PRESET_ENTRY = /@joshuafolkken\/[^/]+\/tsconfig\/[^/]+\.jsonc$/u;
4148
3661
  var LEGACY_SUFFIX = ".jsonc";
4149
3662
  var CURRENT_SUFFIX = ".json";
4150
3663
  var EXTENDS_FIELD = "extends";
4151
3664
  function is_migration_target_present(base_directory, entry) {
4152
- return existsSync4(path7.resolve(base_directory, entry));
3665
+ return existsSync4(path5.resolve(base_directory, entry));
4153
3666
  }
4154
3667
  function migrate_entry(base_directory, entry) {
4155
3668
  if (!LEGACY_PRESET_ENTRY.test(entry)) return entry;
@@ -4381,15 +3894,15 @@ var init_logic_json_merge = {
4381
3894
  };
4382
3895
 
4383
3896
  // scripts/init/kit-development-versions.ts
4384
- import { readFileSync as readFileSync3 } from "node:fs";
4385
- import { z as z12 } from "zod";
3897
+ import { readFileSync as readFileSync2 } from "node:fs";
3898
+ import { z as z10 } from "zod";
4386
3899
  var PACKAGE_JSON = "package.json";
4387
- var kit_manifest_schema = z12.object({
4388
- peerDependencies: z12.record(z12.string(), z12.string()).default({}),
4389
- devDependencies: z12.record(z12.string(), z12.string())
3900
+ var kit_manifest_schema = z10.object({
3901
+ peerDependencies: z10.record(z10.string(), z10.string()).default({}),
3902
+ devDependencies: z10.record(z10.string(), z10.string())
4390
3903
  });
4391
3904
  function read_kit_manifest() {
4392
- const content = readFileSync3(package_path(PACKAGE_JSON), "utf8");
3905
+ const content = readFileSync2(package_path(PACKAGE_JSON), "utf8");
4393
3906
  return kit_manifest_schema.parse(JSON.parse(content));
4394
3907
  }
4395
3908
  function versions_of(names) {
@@ -4550,9 +4063,9 @@ function merge_sonar_properties(existing, template_content) {
4550
4063
  return key !== void 0 && !existing_keys.has(key);
4551
4064
  });
4552
4065
  if (lines_to_add.length === 0) return merged_existing;
4553
- const normalized2 = merged_existing.endsWith("\n") ? merged_existing : `${merged_existing}
4066
+ const normalized = merged_existing.endsWith("\n") ? merged_existing : `${merged_existing}
4554
4067
  `;
4555
- return `${normalized2}
4068
+ return `${normalized}
4556
4069
  ${lines_to_add.join("\n")}
4557
4070
  `;
4558
4071
  }
@@ -4565,8 +4078,8 @@ var init_logic_sonar = {
4565
4078
  };
4566
4079
 
4567
4080
  // scripts/init/init-logic-templates.ts
4568
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
4569
- import path8 from "node:path";
4081
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
4082
+ import path6 from "node:path";
4570
4083
  var ESLINT_VANILLA = `import { create_vanilla_config } from '@joshuafolkken/kit/eslint/vanilla'
4571
4084
 
4572
4085
  export default create_vanilla_config({
@@ -4651,7 +4164,7 @@ function merge_eslint_config(existing) {
4651
4164
  return apply_rules_template(rules_blocks.join(",\n"));
4652
4165
  }
4653
4166
  function detect_tailwind_stylesheet(project_root2) {
4654
- return existsSync5(path8.join(project_root2, DEFAULT_TAILWIND_STYLESHEET)) ? DEFAULT_TAILWIND_STYLESHEET : void 0;
4167
+ return existsSync5(path6.join(project_root2, DEFAULT_TAILWIND_STYLESHEET)) ? DEFAULT_TAILWIND_STYLESHEET : void 0;
4655
4168
  }
4656
4169
  function generate_prettier_config(stylesheet) {
4657
4170
  const stylesheet_line = stylesheet === void 0 ? "" : ` tailwindStylesheet: '${stylesheet}',
@@ -4665,14 +4178,14 @@ ${stylesheet_line}}
4665
4178
  }
4666
4179
  function existing_stylesheet(existing, project_root2) {
4667
4180
  const named = TAILWIND_STYLESHEET_PATTERN.exec(existing)?.[1];
4668
- return named !== void 0 && existsSync5(path8.resolve(project_root2, named)) ? named : void 0;
4181
+ return named !== void 0 && existsSync5(path6.resolve(project_root2, named)) ? named : void 0;
4669
4182
  }
4670
4183
  function merge_prettier_config(existing, project_root2) {
4671
4184
  const stylesheet = existing_stylesheet(existing, project_root2) ?? detect_tailwind_stylesheet(project_root2);
4672
4185
  return generate_prettier_config(stylesheet);
4673
4186
  }
4674
4187
  function generate_playwright_config() {
4675
- return readFileSync4(PLAYWRIGHT_TEMPLATE_PATH, "utf8");
4188
+ return readFileSync3(PLAYWRIGHT_TEMPLATE_PATH, "utf8");
4676
4189
  }
4677
4190
  var init_logic_templates = {
4678
4191
  generate_eslint_config,
@@ -4744,9 +4257,9 @@ function extract_yaml_block(content, key) {
4744
4257
  }
4745
4258
  function append_user_blocks(base, user_keys, existing) {
4746
4259
  const user_blocks = user_keys.map((k) => extract_yaml_block(existing, k)).filter(Boolean).join("\n");
4747
- const normalized2 = base.endsWith("\n") ? base : `${base}
4260
+ const normalized = base.endsWith("\n") ? base : `${base}
4748
4261
  `;
4749
- return `${normalized2}
4262
+ return `${normalized}
4750
4263
  ${user_blocks}
4751
4264
  `;
4752
4265
  }
@@ -4839,9 +4352,9 @@ function carry_npmrc_settings(template, npmrc) {
4839
4352
  }
4840
4353
  function merge_workspace_yaml(existing, template) {
4841
4354
  if (!existing.trim()) return template;
4842
- const normalized2 = existing.endsWith("\n") ? existing : `${existing}
4355
+ const normalized = existing.endsWith("\n") ? existing : `${existing}
4843
4356
  `;
4844
- const answered = answer_build_placeholders(remove_deprecated_yaml_keys(normalized2), template);
4357
+ const answered = answer_build_placeholders(remove_deprecated_yaml_keys(normalized), template);
4845
4358
  const cleaned = add_missing_build_entries(answered, template);
4846
4359
  if (!cleaned.trim()) return template;
4847
4360
  const existing_keys = new Set(extract_yaml_top_level_keys(cleaned));
@@ -5180,23 +4693,23 @@ var AI_COPY_DIRECTORIES_LIST = "AI_COPY_DIRECTORIES";
5180
4693
  var SYNCED_PATHS_LIST = "SYNCED_PATHS";
5181
4694
  var PLUGIN_SKILL_DIRECTORIES_LIST = "PLUGIN_SKILL_DIRECTORIES";
5182
4695
  var PATH_SEPARATOR = "/";
5183
- function is_inside(path16, directory) {
5184
- return path16 === directory || path16.startsWith(`${directory}${PATH_SEPARATOR}`);
4696
+ function is_inside(path13, directory) {
4697
+ return path13 === directory || path13.startsWith(`${directory}${PATH_SEPARATOR}`);
5185
4698
  }
5186
- function matches_file(path16) {
5187
- return init_logic.get_ai_copy_files().includes(path16);
4699
+ function matches_file(path13) {
4700
+ return init_logic.get_ai_copy_files().includes(path13);
5188
4701
  }
5189
- function matches_mapping(path16) {
5190
- return init_logic.get_ai_copy_file_mappings().some((mapping) => mapping.src === path16 || mapping.dest === path16);
4702
+ function matches_mapping(path13) {
4703
+ return init_logic.get_ai_copy_file_mappings().some((mapping) => mapping.src === path13 || mapping.dest === path13);
5191
4704
  }
5192
- function matches_directory(path16) {
5193
- return init_logic.get_ai_copy_directories().some((directory) => is_inside(path16, directory));
4705
+ function matches_directory(path13) {
4706
+ return init_logic.get_ai_copy_directories().some((directory) => is_inside(path13, directory));
5194
4707
  }
5195
- function matches_plugin_skill(path16) {
5196
- return PLUGIN_SKILL_DIRECTORIES.some((directory) => is_inside(path16, directory));
4708
+ function matches_plugin_skill(path13) {
4709
+ return PLUGIN_SKILL_DIRECTORIES.some((directory) => is_inside(path13, directory));
5197
4710
  }
5198
- function matches_synced_path(path16) {
5199
- return synced_paths.get_synced_paths().includes(path16);
4711
+ function matches_synced_path(path13) {
4712
+ return synced_paths.get_synced_paths().includes(path13);
5200
4713
  }
5201
4714
  var MATCHERS = [
5202
4715
  { list: AI_COPY_FILES_LIST, matches: matches_file },
@@ -5205,18 +4718,18 @@ var MATCHERS = [
5205
4718
  { list: PLUGIN_SKILL_DIRECTORIES_LIST, matches: matches_plugin_skill },
5206
4719
  { list: SYNCED_PATHS_LIST, matches: matches_synced_path }
5207
4720
  ];
5208
- function find_list(path16) {
5209
- return MATCHERS.find((matcher) => matcher.matches(path16))?.list;
4721
+ function find_list(path13) {
4722
+ return MATCHERS.find((matcher) => matcher.matches(path13))?.list;
5210
4723
  }
5211
- function to_hit(path16) {
5212
- const list = find_list(path16);
5213
- return list === void 0 ? void 0 : { path: path16, list };
4724
+ function to_hit(path13) {
4725
+ const list = find_list(path13);
4726
+ return list === void 0 ? void 0 : { path: path13, list };
5214
4727
  }
5215
4728
  function find_managed_paths(paths) {
5216
- return paths.flatMap((path16) => to_hit(path16) ?? []);
4729
+ return paths.flatMap((path13) => to_hit(path13) ?? []);
5217
4730
  }
5218
4731
  function has_managed_path(paths) {
5219
- return paths.some((path16) => find_list(path16) !== void 0);
4732
+ return paths.some((path13) => find_list(path13) !== void 0);
5220
4733
  }
5221
4734
  function format_hit(hit) {
5222
4735
  return `${hit.path} (${hit.list})`;
@@ -5232,8 +4745,8 @@ var managed_config_scope = {
5232
4745
  };
5233
4746
 
5234
4747
  // scripts/init/session-language-environment.ts
5235
- import path9 from "node:path";
5236
- var CLAUDE_SETTINGS_PATH = path9.join(".claude", "settings.json");
4748
+ import path7 from "node:path";
4749
+ var CLAUDE_SETTINGS_PATH = path7.join(".claude", "settings.json");
5237
4750
  var KEY_LINE_PATTERN = new RegExp(
5238
4751
  String.raw`^\s*(?:export\s+)?${session_language.ENV_KEY}\s*=`,
5239
4752
  "mu"
@@ -5243,8 +4756,8 @@ var KEY_LINE_PATTERN = new RegExp(
5243
4756
  import { findNodeAtLocation as findNodeAtLocation2, getNodeValue as getNodeValue2, parseTree as parseTree3 } from "jsonc-parser";
5244
4757
 
5245
4758
  // scripts/init/vscode-tasks.ts
5246
- import { z as z13 } from "zod";
5247
- var task_array_schema = z13.array(json_object_schema);
4759
+ import { z as z11 } from "zod";
4760
+ var task_array_schema = z11.array(json_object_schema);
5248
4761
 
5249
4762
  // scripts/init/init-actions.ts
5250
4763
  var BASIC_PRETTIER_CONFIG = "prettier.config.mjs";
@@ -5254,11 +4767,11 @@ var MARKER_TOKEN = "josh-managed-workflow:";
5254
4767
  var MARKER_PREFIX = `# ${MARKER_TOKEN}`;
5255
4768
 
5256
4769
  // scripts/init/claude-plugin-config.ts
5257
- import path10 from "node:path";
4770
+ import path8 from "node:path";
5258
4771
  var MARKETPLACE_NAME = "kit";
5259
4772
  var PLUGIN_ID2 = "kit@kit";
5260
4773
  var MARKETPLACE_PATH = "./node_modules/@joshuafolkken/kit";
5261
- var CLAUDE_SETTINGS_DESTINATION = path10.join(".claude", "settings.json");
4774
+ var CLAUDE_SETTINGS_DESTINATION = path8.join(".claude", "settings.json");
5262
4775
  var PLUGIN_SETTINGS = {
5263
4776
  extraKnownMarketplaces: {
5264
4777
  [MARKETPLACE_NAME]: { source: { source: "directory", path: MARKETPLACE_PATH } }
@@ -5282,9 +4795,9 @@ var claude_plugin_config = {
5282
4795
  // scripts/init/hook-launch.ts
5283
4796
  var HOOK_DIST_DIR = "dist/hooks";
5284
4797
  var RUN_HOOK_SCRIPT = "scripts/hooks/run-hook.sh";
5285
- var UNSET_OPTIONS = GIT_LOCATION_VARIABLES.map((name) => `-u ${name}`).join(" ");
4798
+ var LOCATION_VARIABLES = GIT_LOCATION_VARIABLES.join(" ");
5286
4799
  var ROOT_LOOKUP = "git rev-parse --show-toplevel";
5287
- var PROJECT_ROOT_PREFIX = `project_root="$(env ${UNSET_OPTIONS} ${ROOT_LOOKUP} 2>/dev/null || ${ROOT_LOOKUP})" && cd "$project_root" && `;
4800
+ var PROJECT_ROOT_PREFIX = `if project_root="$(unset ${LOCATION_VARIABLES}; ${ROOT_LOOKUP} 2>/dev/null)"; then unset ${LOCATION_VARIABLES}; else project_root="$(${ROOT_LOOKUP})"; fi && cd "$project_root" && `;
5288
4801
  function hook_launch_command(name) {
5289
4802
  return `${PROJECT_ROOT_PREFIX}sh ${RUN_HOOK_SCRIPT} ${name}`;
5290
4803
  }
@@ -5320,7 +4833,7 @@ var PLAIN_KIT_COMMANDS = new Set(
5320
4833
  var PROJECT_ROOT_PREFIX2 = JSON.stringify(hook_launch.PROJECT_ROOT_PREFIX).slice(1, -1);
5321
4834
 
5322
4835
  // scripts/init/security-policy.ts
5323
- import path11 from "node:path";
4836
+ import path9 from "node:path";
5324
4837
  var SECURITY_MD = "SECURITY.md";
5325
4838
  var SECURITY_TEMPLATE = "templates/SECURITY.md";
5326
4839
  var REPOSITORY_PLACEHOLDER = "{{REPOSITORY}}";
@@ -5336,15 +4849,15 @@ function apply_repository(content, repository) {
5336
4849
  return content.replaceAll(REPOSITORY_PLACEHOLDER, () => repository);
5337
4850
  }
5338
4851
  function apply_repository_for_destination(destination_path, content) {
5339
- if (path11.basename(destination_path) !== SECURITY_MD) return content;
5340
- const repository = repository_at(path11.dirname(destination_path));
4852
+ if (path9.basename(destination_path) !== SECURITY_MD) return content;
4853
+ const repository = repository_at(path9.dirname(destination_path));
5341
4854
  if (repository === void 0) {
5342
4855
  throw new Error(`${SECURITY_MD} needs a GitHub origin to name the repository it reports to`);
5343
4856
  }
5344
4857
  return apply_repository(content, repository);
5345
4858
  }
5346
4859
  function does_report_to_kit(root) {
5347
- const existing = file_reader.read_if_readable(path11.join(root, SECURITY_MD)) ?? "";
4860
+ const existing = file_reader.read_if_readable(path9.join(root, SECURITY_MD)) ?? "";
5348
4861
  return new Set(existing.split(LINK_DELIMITER)).has(KIT_ADVISORY_URL);
5349
4862
  }
5350
4863
  function unresolved_warning(root) {
@@ -5464,9 +4977,9 @@ function requested_profile(args, usage = "josh init [--profile basic|full]") {
5464
4977
  throw new Error("Profile must be basic or full");
5465
4978
  }
5466
4979
  function read_manifest(root) {
5467
- const filename = path12.join(root, "package.json");
4980
+ const filename = path10.join(root, "package.json");
5468
4981
  if (!existsSync6(filename)) return void 0;
5469
- const parsed = JSON.parse(readFileSync5(filename, "utf8"));
4982
+ const parsed = JSON.parse(readFileSync4(filename, "utf8"));
5470
4983
  if (!json_value.is_record(parsed)) throw new Error("package.json must contain an object");
5471
4984
  return parsed;
5472
4985
  }
@@ -5507,12 +5020,12 @@ function should_visit(entry) {
5507
5020
  function scan_directory(directory, pending) {
5508
5021
  const entries = readdirSync(directory, { withFileTypes: true });
5509
5022
  pending.push(
5510
- ...entries.filter((entry) => should_visit(entry)).map((entry) => path12.join(directory, entry.name))
5023
+ ...entries.filter((entry) => should_visit(entry)).map((entry) => path10.join(directory, entry.name))
5511
5024
  );
5512
- return entries.filter((entry) => entry.isFile()).map((entry) => path12.join(directory, entry.name));
5025
+ return entries.filter((entry) => entry.isFile()).map((entry) => path10.join(directory, entry.name));
5513
5026
  }
5514
5027
  function is_project_file(root, file) {
5515
- const relative_path = path12.relative(root, file).split(path12.sep).join(path12.posix.sep);
5028
+ const relative_path = path10.relative(root, file).split(path10.sep).join(path10.posix.sep);
5516
5029
  return !kit_written_paths.is_kit_written(relative_path);
5517
5030
  }
5518
5031
  function list_files(root) {
@@ -5524,7 +5037,7 @@ function list_files(root) {
5524
5037
  return files;
5525
5038
  }
5526
5039
  function file_extensions(root) {
5527
- return list_files(root).filter((file) => is_project_file(root, file)).map((file) => path12.extname(file));
5040
+ return list_files(root).filter((file) => is_project_file(root, file)).map((file) => path10.extname(file));
5528
5041
  }
5529
5042
  function scan_files(root) {
5530
5043
  const extensions = file_extensions(root);
@@ -5538,7 +5051,7 @@ function has_github_remote(root) {
5538
5051
  return url !== void 0 && repo_origin.parse_origin_url(url) !== void 0;
5539
5052
  }
5540
5053
  function has_git(root) {
5541
- return existsSync6(path12.join(root, ".git"));
5054
+ return existsSync6(path10.join(root, ".git"));
5542
5055
  }
5543
5056
  function has_workflow(root) {
5544
5057
  return workflow_mark.is_requested() || workflow_mark.is_recorded(read_manifest(root));
@@ -5571,7 +5084,7 @@ var SKIP_MARKER = "\u2014 skipping";
5571
5084
  var NOT_USED_MARKER = "which this project does not use";
5572
5085
 
5573
5086
  // scripts/gate/project-checks.ts
5574
- import { z as z14 } from "zod";
5087
+ import { z as z12 } from "zod";
5575
5088
  var PACKAGE_JSON2 = "package.json";
5576
5089
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
5577
5090
  "node_modules",
@@ -5606,11 +5119,11 @@ var CSPELL_CONFIGS = [
5606
5119
  ];
5607
5120
  var TYPE_CONFIGS = ["tsconfig.json", "jsconfig.json"];
5608
5121
  var DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
5609
- var object_schema = z14.record(z14.string(), z14.unknown());
5122
+ var object_schema = z12.record(z12.string(), z12.unknown());
5610
5123
  function project_root(directory) {
5611
5124
  return ancestor_directories.nearest(
5612
5125
  directory,
5613
- (candidate) => existsSync7(path13.join(candidate, PACKAGE_JSON2))
5126
+ (candidate) => existsSync7(path11.join(candidate, PACKAGE_JSON2))
5614
5127
  ) ?? directory;
5615
5128
  }
5616
5129
  function is_kit_repository(directory) {
@@ -5620,10 +5133,10 @@ function is_ignored(entry) {
5620
5133
  return entry.split(/[/\\]/u).some((part) => IGNORED_DIRS.has(part));
5621
5134
  }
5622
5135
  function has_files(directory, pattern) {
5623
- return globSync2(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
5136
+ return globSync(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
5624
5137
  }
5625
5138
  function has_config(directory, names) {
5626
- return names.some((name) => existsSync7(path13.join(project_root(directory), name)));
5139
+ return names.some((name) => existsSync7(path11.join(project_root(directory), name)));
5627
5140
  }
5628
5141
  function has_bin(directory, bin_name) {
5629
5142
  return find_local_bin_upwards(directory, bin_name) !== void 0;
@@ -5638,8 +5151,8 @@ function is_basic_manifest(manifest) {
5638
5151
  }
5639
5152
  function read_manifest2(directory) {
5640
5153
  try {
5641
- const manifest_path = path13.join(project_root(directory), PACKAGE_JSON2);
5642
- return json_value.parse_or_undefined(readFileSync6(manifest_path, "utf8"));
5154
+ const manifest_path = path11.join(project_root(directory), PACKAGE_JSON2);
5155
+ return json_value.parse_or_undefined(readFileSync5(manifest_path, "utf8"));
5643
5156
  } catch {
5644
5157
  return void 0;
5645
5158
  }
@@ -5659,11 +5172,11 @@ function missing_tool_reason(directory, package_name, bin_name) {
5659
5172
  if (has_bin(directory, bin_name) || is_declared2(directory, package_name)) return void 0;
5660
5173
  return `${package_name} is not installed`;
5661
5174
  }
5662
- function skip_notice(check5, reason) {
5663
- return `josh ${check5}: ${reason} ${SKIP_MARKER} ${check5}, ${NOT_USED_MARKER}.`;
5175
+ function skip_notice(check3, reason) {
5176
+ return `josh ${check3}: ${reason} ${SKIP_MARKER} ${check3}, ${NOT_USED_MARKER}.`;
5664
5177
  }
5665
- function ran_notice(check5) {
5666
- return `josh ${check5}: ran and reported no problems.`;
5178
+ function ran_notice(check3) {
5179
+ return `josh ${check3}: ran and reported no problems.`;
5667
5180
  }
5668
5181
  function type_check_skip_reason(directory) {
5669
5182
  if (!is_basic(directory)) return void 0;
@@ -5727,40 +5240,40 @@ var BASIC_REQUIRED_SUFFIXES = [
5727
5240
  ".toml"
5728
5241
  ];
5729
5242
  function normalize(paths) {
5730
- return paths.map((path16) => path16.trim()).filter((path16) => path16 !== "");
5243
+ return paths.map((path13) => path13.trim()).filter((path13) => path13 !== "");
5731
5244
  }
5732
- function is_test_file(path16) {
5733
- return TEST_SUFFIXES.some((suffix) => path16.endsWith(suffix));
5245
+ function is_test_file(path13) {
5246
+ return TEST_SUFFIXES.some((suffix) => path13.endsWith(suffix));
5734
5247
  }
5735
- function is_basic_manual(path16, is_basic2) {
5736
- const lower = path16.toLowerCase();
5248
+ function is_basic_manual(path13, is_basic2) {
5249
+ const lower = path13.toLowerCase();
5737
5250
  return is_basic2 && BASIC_REQUIRED_SUFFIXES.every((suffix) => !lower.endsWith(suffix));
5738
5251
  }
5739
- function is_basic_visual(path16) {
5740
- const lower = path16.toLowerCase();
5252
+ function is_basic_visual(path13) {
5253
+ const lower = path13.toLowerCase();
5741
5254
  return BASIC_VISUAL_SUFFIXES.some((suffix) => lower.endsWith(suffix));
5742
5255
  }
5743
- function is_exempt(path16, is_basic2 = false) {
5744
- if (EXEMPT_PATHS.includes(path16) || is_basic_manual(path16, is_basic2)) return true;
5745
- return EXEMPT_SUFFIXES.some((suffix) => path16.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path16.startsWith(prefix));
5256
+ function is_exempt(path13, is_basic2 = false) {
5257
+ if (EXEMPT_PATHS.includes(path13) || is_basic_manual(path13, is_basic2)) return true;
5258
+ return EXEMPT_SUFFIXES.some((suffix) => path13.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path13.startsWith(prefix));
5746
5259
  }
5747
- function is_runtime(path16, is_basic2 = false) {
5748
- return !is_test_file(path16) && !is_exempt(path16, is_basic2);
5260
+ function is_runtime(path13, is_basic2 = false) {
5261
+ return !is_test_file(path13) && !is_exempt(path13, is_basic2);
5749
5262
  }
5750
5263
  function verdict_for2(paths, is_basic2 = false) {
5751
5264
  const changed = normalize(paths);
5752
- if (changed.some((path16) => is_test_file(path16))) return "satisfied";
5753
- if (changed.some((path16) => is_runtime(path16, is_basic2))) return "required";
5265
+ if (changed.some((path13) => is_test_file(path13))) return "satisfied";
5266
+ if (changed.some((path13) => is_runtime(path13, is_basic2))) return "required";
5754
5267
  return "exempt";
5755
5268
  }
5756
5269
  function runtime_files(paths, is_basic2 = false) {
5757
- return normalize(paths).filter((path16) => is_runtime(path16, is_basic2));
5270
+ return normalize(paths).filter((path13) => is_runtime(path13, is_basic2));
5758
5271
  }
5759
5272
  function exempt_files(paths, is_basic2 = false) {
5760
- return normalize(paths).filter((path16) => is_exempt(path16, is_basic2));
5273
+ return normalize(paths).filter((path13) => is_exempt(path13, is_basic2));
5761
5274
  }
5762
5275
  function manual_check_files(paths, is_basic2) {
5763
- return normalize(paths).filter((path16) => is_exempt(path16, is_basic2) && !is_exempt(path16));
5276
+ return normalize(paths).filter((path13) => is_exempt(path13, is_basic2) && !is_exempt(path13));
5764
5277
  }
5765
5278
  var test_declared_logic = {
5766
5279
  EXEMPT_PATHS,
@@ -5820,8 +5333,8 @@ function is_untested_commit(call) {
5820
5333
  return test_declared_changed.current_verdict() === "required";
5821
5334
  }
5822
5335
  var REASON = "\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads it `required` and names the files. Tests are required for ALL code changes: add the test and reissue, or, for a change that touches no runtime path, declare the non-runtime exception in your Step 0 work summary and reissue. It fires once per run (`CLAUDE.md` \u2192 Code Change Rules).";
5823
- var ROW19 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
5824
- var test_declared_commit = { REASON, ROW: ROW19 };
5336
+ var ROW20 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
5337
+ var test_declared_commit = { REASON, ROW: ROW20 };
5825
5338
 
5826
5339
  // scripts/rules/third-party-write.ts
5827
5340
  function is_third_party_write_segment(segment, session_owner, resolve_placeholder_owners) {
@@ -5859,14 +5372,14 @@ function writes_third_party(command, resolve_owner = repo_party.current_owner, r
5859
5372
  );
5860
5373
  }
5861
5374
  var THIRD_PARTY_WRITE_REASON = "\u26D4 third-party write: this `gh api` call writes to a repository whose owner is not this session, which is Tier C and needs explicit current-turn user instruction. Check with `pnpm josh repo:party <owner/repo>`, then record the finding on our own Issue and draft the artifact for a person to post. This rule fires on every occurrence (`prompts/collaboration-workflow/upstream-interrupt.md`).";
5862
- var ROW20 = {
5375
+ var ROW21 = {
5863
5376
  id: "third-party-write",
5864
5377
  is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
5865
5378
  reason: THIRD_PARTY_WRITE_REASON,
5866
5379
  decide: () => true
5867
5380
  };
5868
5381
  var third_party_write = {
5869
- ROW: ROW20,
5382
+ ROW: ROW21,
5870
5383
  THIRD_PARTY_WRITE_REASON,
5871
5384
  writes_third_party
5872
5385
  };
@@ -5898,7 +5411,8 @@ var DELIVERED_RULES = [
5898
5411
  reason: piped_verification.PIPED_VERIFICATION_REASON,
5899
5412
  keeps: on_bash_command(piped_verification.keeps_verdict_intact),
5900
5413
  reaches: on_bash_command(piped_verification.runs_verification),
5901
- // A `| tail` / `| grep` masking is rewritten under `pipefail` by `pretool-guard.ts`, never refused.
5414
+ // A masking whose filter reads to the end is rewritten under `pipefail`, never refused.
5415
+ rewrite: piped_verification.pipefail_input,
5902
5416
  rewrite_note: piped_verification.PIPEFAIL_NOTE
5903
5417
  },
5904
5418
  {
@@ -5937,6 +5451,7 @@ var DELIVERED_RULES = [
5937
5451
  lane_split_park.ROW,
5938
5452
  lane_park.ROW,
5939
5453
  lane_interactive_ask.ROW,
5454
+ agent_model.ROW,
5940
5455
  lane_carry_conflict.ROW,
5941
5456
  lane_switch_main.ROW,
5942
5457
  // Disjoint from `run-tail` by the `-y` flag.
@@ -5976,22 +5491,22 @@ var MEASURED_RULES = [
5976
5491
  ];
5977
5492
  var BATCH_STAMP = hook_decision.create_refusal_stamp(time_batch_guard.STAMP_PREFIX);
5978
5493
  var BATCH_REFUSAL_WINDOW_MS = 1e4;
5979
- function will_batch_guard_refuse(tail, call, run2) {
5494
+ function will_batch_guard_refuse(tail, call, run) {
5980
5495
  if (lane_guard_policy.mode_here("batching") !== "refuse") return false;
5981
- const refused_at_ms = BATCH_STAMP.last_ms(BATCH_STAMP.path(run2.transcript));
5982
- if (run2.now_ms - refused_at_ms < BATCH_REFUSAL_WINDOW_MS) return true;
5496
+ const refused_at_ms = BATCH_STAMP.last_ms(BATCH_STAMP.path(run.transcript));
5497
+ if (run.now_ms - refused_at_ms < BATCH_REFUSAL_WINDOW_MS) return true;
5983
5498
  return time_batch_guard.should_block(tail, call, refused_at_ms);
5984
5499
  }
5985
- function is_first_delivery(tail, call, delivered_at_ms, run2) {
5986
- return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run2);
5500
+ function is_first_delivery(tail, call, delivered_at_ms, run) {
5501
+ return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run);
5987
5502
  }
5988
5503
  function delivery_decision(rule) {
5989
- const { decide: decide7, already_satisfied } = rule;
5990
- if (decide7 === void 0 && already_satisfied === void 0) return is_first_delivery;
5991
- return function should_block(tail, call, delivered_at_ms, run2) {
5504
+ const { decide: decide5, already_satisfied } = rule;
5505
+ if (decide5 === void 0 && already_satisfied === void 0) return is_first_delivery;
5506
+ return function should_block(tail, call, delivered_at_ms, run) {
5992
5507
  if (already_satisfied?.(tail, call) === true) return false;
5993
- if (decide7 === void 0) return true;
5994
- return decide7(call, run2, !will_batch_guard_refuse(tail, call, run2), delivered_at_ms);
5508
+ if (decide5 === void 0) return true;
5509
+ return decide5(call, run, !will_batch_guard_refuse(tail, call, run), delivered_at_ms);
5995
5510
  };
5996
5511
  }
5997
5512
  function guard_of(rule) {
@@ -6044,6 +5559,144 @@ var delivered_rules = {
6044
5559
  is_issue_filing
6045
5560
  };
6046
5561
 
5562
+ // scripts/rules/guard-remedy.ts
5563
+ var REWRITTEN = "the hook makes the same call right itself; it refuses only a spelling it cannot rewrite";
5564
+ var ANOTHER_TOOL = "the right form is another tool, which `updatedInput` cannot switch to";
5565
+ var ANOTHER_COMMAND = "the right act is another command, whose arguments the call does not carry";
5566
+ var NO_RUN = "the call must not run at all, or only on a person's word \u2014 refusing is the remedy";
5567
+ var STOP_NOTIFY = "the hook sends the notify itself; it blocks only when the send fails";
5568
+ var HOLD_RELEASE = "the hook releases the hold itself; it blocks only when that release fails";
5569
+ var ISSUE_CITATION = "the hook prints the citation lines itself; it blocks only on a number it cannot cite";
5570
+ var FILING_OFFER = "the filing needs a title and a body only the model can write";
5571
+ function rewrite() {
5572
+ return { remedy: "rewrite", why: REWRITTEN };
5573
+ }
5574
+ function judgment(why) {
5575
+ return { remedy: "judgment", why };
5576
+ }
5577
+ var GUARD_REMEDIES = {
5578
+ // The rewrites: each fix is the same call made right.
5579
+ "piped-verification": rewrite(),
5580
+ // wrapped in `(set -o pipefail; …)`
5581
+ "issue-comments": rewrite(),
5582
+ // the same Issue's comments read on the same line
5583
+ "lane-background": rewrite(),
5584
+ // a lane child's `ship` run in the foreground (`pretool-guard.ts`)
5585
+ "file-body": judgment(ANOTHER_TOOL),
5586
+ // Edit or Write
5587
+ "shell-body": judgment(
5588
+ "which part of the body the shell was meant to expand is the author's, not the hook's"
5589
+ ),
5590
+ "raw-field-body": judgment("the body moves to a file the call does not name"),
5591
+ "rule-body": judgment("a trigger-plus-pointer line is written, not derived"),
5592
+ "direct-filing": judgment(ANOTHER_COMMAND),
5593
+ "direct-pr-create": judgment(ANOTHER_COMMAND),
5594
+ "josh-git-bare": judgment(ANOTHER_COMMAND),
5595
+ "run-tail": judgment(ANOTHER_COMMAND),
5596
+ "pre-gate-cut": judgment(ANOTHER_COMMAND),
5597
+ "implementation-cut": judgment(ANOTHER_COMMAND),
5598
+ "early-heartbeat": judgment(ANOTHER_COMMAND),
5599
+ "poll-loop": judgment(ANOTHER_COMMAND),
5600
+ "lane-park": judgment(ANOTHER_COMMAND),
5601
+ "lane-split-park": judgment(ANOTHER_COMMAND),
5602
+ "lane-interactive-ask": judgment(ANOTHER_COMMAND),
5603
+ "agent-model": judgment("which model fits the unit is the author's call"),
5604
+ "lane-carry-conflict": judgment(ANOTHER_COMMAND),
5605
+ "lane-switch-main": judgment(ANOTHER_COMMAND),
5606
+ "oracle-consulted": judgment(ANOTHER_COMMAND),
5607
+ "test-declared": judgment(ANOTHER_COMMAND),
5608
+ batching: judgment(ANOTHER_COMMAND),
5609
+ investigation: judgment(ANOTHER_COMMAND),
5610
+ "duplicate-read": judgment(ANOTHER_COMMAND),
5611
+ "run-watcher": judgment(ANOTHER_COMMAND),
5612
+ "parent-cut": judgment(ANOTHER_COMMAND),
5613
+ "issue-watch": judgment(ANOTHER_COMMAND),
5614
+ "third-party-write": judgment(NO_RUN),
5615
+ "filing-cap": judgment(NO_RUN),
5616
+ "git-force": judgment(NO_RUN),
5617
+ "worktree-mutation": judgment(NO_RUN),
5618
+ "index-mutation": judgment(NO_RUN),
5619
+ "destructive-command": judgment(NO_RUN),
5620
+ "protected-file": judgment(NO_RUN),
5621
+ // The `Stop` rows: the hook judges a finished reply, so it has no call to rewrite.
5622
+ "stop-notify": judgment(STOP_NOTIFY),
5623
+ "issue-citation": judgment(ISSUE_CITATION),
5624
+ "filing-offer": judgment(FILING_OFFER),
5625
+ "hold-release": judgment(HOLD_RELEASE)
5626
+ };
5627
+ var FAMILY_SEPARATOR = ":";
5628
+ function entry_of(id) {
5629
+ return GUARD_REMEDIES[id] ?? GUARD_REMEDIES[id.split(FAMILY_SEPARATOR)[0] ?? ""];
5630
+ }
5631
+ function remedy_of(id) {
5632
+ return entry_of(id)?.remedy;
5633
+ }
5634
+ function why_of(id) {
5635
+ return entry_of(id)?.why;
5636
+ }
5637
+ function is_rewrite(id) {
5638
+ return remedy_of(id) === "rewrite";
5639
+ }
5640
+ var guard_remedy = {
5641
+ GUARD_REMEDIES,
5642
+ is_rewrite,
5643
+ remedy_of,
5644
+ why_of
5645
+ };
5646
+
5647
+ // scripts/rules/rule-rewrite.ts
5648
+ var REWRITING_RULES = delivered_rules.DELIVERED_RULES.filter(
5649
+ (rule) => guard_remedy.is_rewrite(rule.id) && rule.rewrite !== void 0
5650
+ );
5651
+ var NOTE_SEPARATOR = "\n\n";
5652
+ function read_payload_tail(raw_payload) {
5653
+ try {
5654
+ const payload = hook_decision.parse_hook_payload(raw_payload);
5655
+ return payload ? time_density_hook.read_tail(
5656
+ time_hook_transcript.transcript_of(payload.transcript_path, payload.agent_id)
5657
+ ) : "";
5658
+ } catch {
5659
+ return "";
5660
+ }
5661
+ }
5662
+ function tail_reader(raw_payload) {
5663
+ const cache = {};
5664
+ return function tail() {
5665
+ cache.tail ??= read_payload_tail(raw_payload);
5666
+ return cache.tail;
5667
+ };
5668
+ }
5669
+ function fold(name, typed, tail) {
5670
+ const folded = { input: typed, notes: [] };
5671
+ for (const rule of REWRITING_RULES) {
5672
+ const input = rule.rewrite?.({ name, input: folded.input }, tail);
5673
+ if (input === void 0) {
5674
+ continue;
5675
+ }
5676
+ folded.input = input;
5677
+ folded.notes = [...folded.notes, rule.rewrite_note ?? ""];
5678
+ }
5679
+ return folded;
5680
+ }
5681
+ function typed_payload(raw_payload) {
5682
+ const fields = json_value.parse_or_undefined(raw_payload);
5683
+ if (!json_value.is_record(fields)) return void 0;
5684
+ const input = fields["tool_input"];
5685
+ return json_value.is_record(input) ? { fields, name: String(fields["tool_name"]), input } : void 0;
5686
+ }
5687
+ function rewrite2(raw_payload) {
5688
+ const typed = delivered_rules.is_enabled() ? typed_payload(raw_payload) : void 0;
5689
+ if (typed === void 0) return void 0;
5690
+ const { input, notes } = fold(typed.name, typed.input, tail_reader(raw_payload));
5691
+ if (notes.length === 0) return void 0;
5692
+ const note = notes.join(NOTE_SEPARATOR);
5693
+ return { payload: JSON.stringify({ ...typed.fields, tool_input: input }), input, note };
5694
+ }
5695
+ var rule_rewrite = {
5696
+ NOTE_SEPARATOR,
5697
+ rewrite: rewrite2
5698
+ };
5699
+
6047
5700
  // scripts/run/progress/run-progress-read.ts
6048
5701
  import { loadavg } from "node:os";
6049
5702
 
@@ -6059,8 +5712,8 @@ function format_holder(issue, repo, ages) {
6059
5712
  function format_holders(issues, repo, ages) {
6060
5713
  return issues.map((issue) => format_holder(issue, repo, ages)).join(", ");
6061
5714
  }
6062
- function occupied_lanes(read3) {
6063
- return read3.kind === "busy" ? read3.issues.length : 0;
5715
+ function occupied_lanes(read2) {
5716
+ return read2.kind === "busy" ? read2.issues.length : 0;
6064
5717
  }
6065
5718
  function occupancy_message(issues, repo, limit, ages = NO_AGES) {
6066
5719
  return `${String(issues.length)} of ${String(limit)} lanes in use in ${repo}: ${format_holders(issues, repo, ages)}.`;
@@ -6078,18 +5731,18 @@ var BUSY_REASONS = {
6078
5731
  unreadable: unreadable_message,
6079
5732
  truncated: truncated_message
6080
5733
  };
6081
- function busy_reason(read3, repo, limit, ages = NO_AGES) {
6082
- if (read3.kind === "busy") return lanes_full_message(read3.issues, repo, limit, ages);
6083
- if (read3.kind === "idle") return "";
6084
- return BUSY_REASONS[read3.kind](repo);
5734
+ function busy_reason(read2, repo, limit, ages = NO_AGES) {
5735
+ if (read2.kind === "busy") return lanes_full_message(read2.issues, repo, limit, ages);
5736
+ if (read2.kind === "idle") return "";
5737
+ return BUSY_REASONS[read2.kind](repo);
6085
5738
  }
6086
5739
  function is_parked(issue) {
6087
5740
  return has_any_label(issue.labels, PARKED_LABELS);
6088
5741
  }
6089
5742
  function parse_listing(raw, is_capped) {
6090
- const read3 = read_json_listing(raw, open_issue_schema);
6091
- if (read3.kind !== "read") return { kind: "unreadable" };
6092
- const holders = read3.rows.filter((row) => !is_parked(row));
5743
+ const read2 = read_json_listing(raw, open_issue_schema);
5744
+ if (read2.kind !== "read") return { kind: "unreadable" };
5745
+ const holders = read2.rows.filter((row) => !is_parked(row));
6093
5746
  if (holders.length > 0) return { kind: "busy", issues: holders };
6094
5747
  return { kind: is_capped ? "truncated" : "idle" };
6095
5748
  }
@@ -6114,411 +5767,16 @@ var epic_busy = {
6114
5767
  read_repository
6115
5768
  };
6116
5769
 
6117
- // scripts/run/run-liveness.ts
6118
- import { readFileSync as readFileSync7, realpathSync, statSync } from "node:fs";
6119
- import { homedir, tmpdir } from "node:os";
6120
- import path14 from "node:path";
6121
- import { setTimeout as sleep } from "node:timers/promises";
6122
-
6123
- // scripts/agent/agent-event.ts
6124
- import { z as z16 } from "zod";
6125
-
6126
- // scripts/agent/claude-result-event.ts
6127
- import { z as z15 } from "zod";
6128
- var RESULT_EVENT_SCHEMA = z15.looseObject({
6129
- type: z15.literal("result"),
6130
- is_error: z15.unknown().optional(),
6131
- subtype: z15.unknown().optional(),
6132
- num_turns: z15.unknown().optional(),
6133
- permission_denials: z15.unknown().optional(),
6134
- result: z15.unknown().optional(),
6135
- session_id: z15.unknown().optional(),
6136
- usage: z15.unknown().optional()
6137
- });
6138
- var USAGE_SCHEMA = z15.looseObject({
6139
- input_tokens: z15.unknown().optional(),
6140
- cache_read_input_tokens: z15.unknown().optional(),
6141
- output_tokens: z15.unknown().optional()
6142
- });
6143
- function usable_reason(value) {
6144
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
6145
- }
6146
- function numeric_value(value) {
6147
- return typeof value === "number" ? value : void 0;
6148
- }
6149
- function denial_count(value) {
6150
- return Array.isArray(value) ? value.length : 0;
6151
- }
6152
- function usage_of(value) {
6153
- const parsed = USAGE_SCHEMA.safeParse(value);
6154
- if (!parsed.success) return void 0;
6155
- const usage = {
6156
- input_tokens: numeric_value(parsed.data.input_tokens),
6157
- cached_input_tokens: numeric_value(parsed.data.cache_read_input_tokens),
6158
- output_tokens: numeric_value(parsed.data.output_tokens)
6159
- };
6160
- return Object.values(usage).some((count) => count !== void 0) ? usage : void 0;
6161
- }
6162
- function decode(value) {
6163
- const parsed = RESULT_EVENT_SCHEMA.safeParse(value);
6164
- if (!parsed.success) return void 0;
6165
- const is_error = parsed.data.is_error === true;
6166
- return {
6167
- is_error,
6168
- subtype: usable_reason(parsed.data.subtype),
6169
- num_turns: numeric_value(parsed.data.num_turns),
6170
- permission_denials: denial_count(parsed.data.permission_denials),
6171
- refused_ask: interactive_ask.refused_ask_of(parsed.data.permission_denials),
6172
- reason: is_error ? usable_reason(parsed.data.result) ?? usable_reason(parsed.data.subtype) : void 0,
6173
- session_id: usable_reason(parsed.data.session_id),
6174
- usage: usage_of(parsed.data.usage)
6175
- };
6176
- }
6177
- var claude_result_event = { decode };
6178
-
6179
- // scripts/agent/agent-event.ts
6180
- var TEXT_BLOCK_SCHEMA = z16.looseObject({ type: z16.literal("text"), text: z16.string() });
6181
- var CLAUDE_MESSAGE_SCHEMA = z16.looseObject({ content: z16.array(z16.unknown()).optional() });
6182
- var CLAUDE_INIT_SCHEMA = z16.looseObject({ type: z16.literal("system"), subtype: z16.literal("init") });
6183
- var CLAUDE_ASSISTANT_SCHEMA = z16.looseObject({
6184
- type: z16.literal("assistant"),
6185
- message: CLAUDE_MESSAGE_SCHEMA
6186
- });
6187
- var USAGE_SCHEMA2 = z16.looseObject({
6188
- input_tokens: z16.number().optional(),
6189
- cached_input_tokens: z16.number().optional(),
6190
- output_tokens: z16.number().optional()
6191
- });
6192
- var CODEX_START_SCHEMA = z16.looseObject({ type: z16.enum(["thread.started", "turn.started"]) });
6193
- var CODEX_PROGRESS_SCHEMA = z16.looseObject({
6194
- type: z16.enum(["item.started", "item.updated", "item.completed"]),
6195
- item: z16.looseObject({ type: z16.string(), text: z16.string().optional() }).optional()
6196
- });
6197
- var CODEX_COMPLETE_SCHEMA = z16.looseObject({
6198
- type: z16.literal("turn.completed"),
6199
- usage: USAGE_SCHEMA2.optional()
6200
- });
6201
- var CODEX_FAILURE_SCHEMA = z16.looseObject({
6202
- type: z16.enum(["turn.failed", "error"]),
6203
- error: z16.looseObject({ message: z16.string().optional() }).optional(),
6204
- message: z16.string().optional()
6205
- });
6206
- var EMPTY_STATE = {
6207
- provider: void 0,
6208
- launch: "pending",
6209
- progress: void 0,
6210
- result: "running",
6211
- reason: void 0,
6212
- usage: void 0
6213
- };
6214
- var LAUNCH_HEADER_PREFIX = "=== ";
6215
- var LAUNCH_HEADER_MARKER = " \xB7 started by process ";
6216
- function usable_text(value) {
6217
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
6218
- }
6219
- function claude_progress(content) {
6220
- return content.map((block) => TEXT_BLOCK_SCHEMA.safeParse(block)).filter((parsed) => parsed.success).map((parsed) => parsed.data.text).findLast((text) => text.trim() !== "");
6221
- }
6222
- function claude_result(value, state) {
6223
- const result = claude_result_event.decode(value);
6224
- if (result === void 0) return void 0;
6225
- return {
6226
- ...state,
6227
- provider: "anthropic",
6228
- result: result.is_error ? "failed" : "completed",
6229
- reason: result.is_error ? result.reason ?? state.reason : state.reason,
6230
- usage: result.usage
6231
- };
6232
- }
6233
- function claude_assistant(value, state) {
6234
- const assistant = CLAUDE_ASSISTANT_SCHEMA.safeParse(value);
6235
- if (!assistant.success) return void 0;
6236
- const progress = claude_progress(assistant.data.message.content ?? []) ?? state.progress;
6237
- return { ...state, provider: "anthropic", progress };
6238
- }
6239
- function claude_terminal(value, state) {
6240
- return claude_result(value, state);
6241
- }
6242
- function claude_event(value, state) {
6243
- if (CLAUDE_INIT_SCHEMA.safeParse(value).success) {
6244
- return { ...state, provider: "anthropic", launch: "started" };
6245
- }
6246
- return claude_assistant(value, state) ?? claude_terminal(value, state);
6247
- }
6248
- function codex_progress(value, state) {
6249
- const parsed = CODEX_PROGRESS_SCHEMA.safeParse(value);
6250
- if (!parsed.success) return void 0;
6251
- const progress = usable_text(parsed.data.item?.text) ?? state.progress;
6252
- return { ...state, provider: "openai", progress };
6253
- }
6254
- function codex_completed(value, state) {
6255
- const completed = CODEX_COMPLETE_SCHEMA.safeParse(value);
6256
- if (!completed.success) return void 0;
6257
- return { ...state, provider: "openai", result: "completed", usage: completed.data.usage };
6258
- }
6259
- function codex_failed(value, state) {
6260
- const failed = CODEX_FAILURE_SCHEMA.safeParse(value);
6261
- if (!failed.success) return void 0;
6262
- const reason = usable_text(failed.data.error?.message) ?? usable_text(failed.data.message);
6263
- return { ...state, provider: "openai", result: "failed", reason: reason ?? state.reason };
6264
- }
6265
- function codex_terminal(value, state) {
6266
- return codex_completed(value, state) ?? codex_failed(value, state);
6267
- }
6268
- function codex_event(value, state) {
6269
- if (CODEX_START_SCHEMA.safeParse(value).success) {
6270
- return { ...state, provider: "openai", launch: "started" };
6271
- }
6272
- return codex_progress(value, state) ?? codex_terminal(value, state);
6273
- }
6274
- function normalized(value, state) {
6275
- return claude_event(value, state) ?? codex_event(value, state) ?? state;
6276
- }
6277
- function next_state2(line, state) {
6278
- const is_header = line.startsWith(LAUNCH_HEADER_PREFIX) && line.includes(LAUNCH_HEADER_MARKER);
6279
- if (is_header) return EMPTY_STATE;
6280
- return normalized(json_value.parse_or_undefined(line), state);
6281
- }
6282
- function read2(transcript) {
6283
- let state = EMPTY_STATE;
6284
- for (const line of transcript.split("\n")) state = next_state2(line, state);
6285
- return state;
6286
- }
6287
- function describe(state) {
6288
- const progress = state.progress === void 0 ? "" : ` progress=${state.progress}`;
6289
- const reason = state.reason === void 0 ? "" : ` reason=${state.reason}`;
6290
- return `launch=${state.launch} result=${state.result}${progress}${reason}`;
6291
- }
6292
- var agent_event = { describe, read: read2 };
6293
-
6294
- // scripts/issue/issue-state.ts
6295
- import { z as z17 } from "zod";
6296
- var NO_LABELS = "(none)";
6297
- var LABEL_SEPARATOR = ", ";
6298
- var STATE_LABEL = "state: ";
6299
- var LABELS_LABEL = "labels: ";
6300
- var HUMAN_REVIEW_LABEL_LINE = "human_review: ";
6301
- var YES = "yes";
6302
- var NO = "no";
6303
- var ISSUE_LABEL = "issue: ";
6304
- var HUMAN_REVIEW_LABELS = /* @__PURE__ */ new Set([NEEDS_HUMAN_REVIEW_LABEL]);
6305
- var label_schema = z17.looseObject({ name: z17.string() });
6306
- var issue_state_schema = z17.looseObject({
6307
- state: z17.string(),
6308
- labels: label_schema.array().optional()
6309
- });
6310
- function parse_issue_state(json) {
6311
- try {
6312
- const parsed = parse_json_object_or_undefined(json, issue_state_schema);
6313
- if (parsed === void 0) return void 0;
6314
- const labels = parsed.labels ?? [];
6315
- return {
6316
- state: parsed.state,
6317
- labels: labels.map((label) => label.name),
6318
- is_human_review: has_any_label(labels, HUMAN_REVIEW_LABELS)
6319
- };
6320
- } catch {
6321
- return void 0;
6322
- }
6323
- }
6324
- function format_labels(labels) {
6325
- return labels.length === 0 ? NO_LABELS : labels.join(LABEL_SEPARATOR);
6326
- }
6327
- function format_issue_state(state) {
6328
- const human_review = `${HUMAN_REVIEW_LABEL_LINE}${state.is_human_review ? YES : NO}`;
6329
- return `${STATE_LABEL}${state.state}
6330
- ${LABELS_LABEL}${format_labels(state.labels)}
6331
- ${human_review}`;
6332
- }
6333
- function format_attributed_issue_state(issue_number, state) {
6334
- return `${ISSUE_LABEL}${issue_number}
6335
- ${format_issue_state(state)}`;
6336
- }
6337
- var issue_state = {
6338
- NO_LABELS,
6339
- parse_issue_state,
6340
- format_issue_state,
6341
- format_attributed_issue_state
6342
- };
6343
-
6344
- // scripts/run/run-liveness.ts
6345
- var ALIVE_VERDICT = "alive";
6346
- var SETTLED_VERDICT = "settled";
6347
- var STOPPED_VERDICT = "stopped";
6348
- var UNDETERMINED_VERDICT = "undetermined";
6349
- var PROCESS_ALIVE = "alive";
6350
- var PROCESS_UNKNOWN = "unknown";
6351
- var DEFAULT_SILENT_MINUTES = 30;
6352
- var DEFAULT_GAP_SECONDS = 5;
6353
- var MS_PER_MINUTE3 = 6e4;
6354
- var MS_PER_SECOND2 = 1e3;
6355
- var OPEN_STATE = "OPEN";
6356
- var STATE_FIELDS = "state,labels";
6357
- var REASONS = {
6358
- [ALIVE_VERDICT]: "The unit is still working: its output moved, or a process of the child is alive.",
6359
- [SETTLED_VERDICT]: "The child is closed, parked with `needs-decision`, or marked `already-done`, so there is nothing here to recover.",
6360
- [STOPPED_VERDICT]: "The output has been frozen past the silent window and no process of the child is alive in that checkout.",
6361
- [UNDETERMINED_VERDICT]: "A trace could not be read, so this says nothing about the unit. A trace that could not be read answers `undetermined`, never `stopped`."
6362
- };
6363
- var ALIVE_ADVICE = "Keep polling; do not touch the child.";
6364
- var SETTLED_ADVICE = "Read the child with `pnpm josh issue:state <N>` and take the branch its state says.";
6365
- var STOPPED_CLEAN_ADVICE = 'The unit ended \u2014 classify how before booking it. Run `pnpm josh run:merge <N> --output <path>` (add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic): it reads the child\'s exit record without waiting for the unit to return, re-dispatches a child that could not reach the API (counting the outage; a run of them trips the environment guard and stops the run), and parks an abandoned child with `needs-decision` against the consecutive-failure guard. The checkout is clean, so there is nothing to stash.';
6366
- var STOPPED_DIRTY_ADVICE = 'Stash the half-finished work with `git stash push -u -m "backlogrun: stopped unit for #<N>"` and record it on the Issue, then classify how the unit ended with `pnpm josh run:merge <N> --output <path>` (add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic): it re-dispatches an API-outage child (counting the outage; a run of them trips the environment guard) and parks an abandoned child with `needs-decision` against the consecutive-failure guard, without waiting for the unit to return.';
6367
- var UNDETERMINED_ADVICE = "Read the trace that failed and ask again. Where the process trace was given as `unknown`, leave `--process` out so the command reads it. Two of these in a row is a fault in the check itself rather than a slow unit: stop polling and report it.";
6368
- function is_output_moving(traces) {
6369
- return traces.is_output_frozen === false;
6370
- }
6371
- function is_agent_state_finished(state) {
6372
- return state?.result === "completed" || state?.result === "failed";
6373
- }
6374
- function is_agent_finished(traces) {
6375
- return is_agent_state_finished(traces.agent_state);
6376
- }
6377
- function definitive_verdict(traces) {
6378
- if (traces.is_child_settled === true) return SETTLED_VERDICT;
6379
- if (is_agent_finished(traces)) return STOPPED_VERDICT;
6380
- if (is_output_moving(traces)) return ALIVE_VERDICT;
6381
- return void 0;
6382
- }
6383
- function is_check_running(traces) {
6384
- return traces.process_trace === PROCESS_ALIVE;
6385
- }
6386
- function has_unreadable_trace(traces) {
6387
- return traces.is_child_settled === void 0 || traces.is_output_frozen === void 0 || traces.process_trace === PROCESS_UNKNOWN;
6388
- }
6389
- function to_verdict(traces) {
6390
- const definitive = definitive_verdict(traces);
6391
- if (definitive !== void 0) return definitive;
6392
- if (has_unreadable_trace(traces)) return UNDETERMINED_VERDICT;
6393
- return is_check_running(traces) ? ALIVE_VERDICT : STOPPED_VERDICT;
6394
- }
6395
- function to_advice(verdict, has_work_to_stash) {
6396
- if (verdict === ALIVE_VERDICT) return ALIVE_ADVICE;
6397
- if (verdict === SETTLED_VERDICT) return SETTLED_ADVICE;
6398
- if (verdict === UNDETERMINED_VERDICT) return UNDETERMINED_ADVICE;
6399
- return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
6400
- }
6401
- function decide5(traces) {
6402
- const verdict = to_verdict(traces);
6403
- const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
6404
- return {
6405
- advice: to_advice(verdict, has_work_to_stash),
6406
- has_work_to_stash,
6407
- reason: traces.agent_state?.reason ?? REASONS[verdict],
6408
- verdict
6409
- };
6410
- }
6411
- function to_resolved_root(root) {
6412
- try {
6413
- return realpathSync(root);
6414
- } catch {
6415
- return root;
6416
- }
6417
- }
6418
- var ALLOWED_ROOTS = [
6419
- ...new Set(
6420
- [homedir(), tmpdir(), PLATFORM_TEMP_ROOT].flatMap((root) => [root, to_resolved_root(root)])
6421
- )
6422
- ];
6423
- function is_within(candidate, root) {
6424
- const relative = path14.relative(root, candidate);
6425
- return relative !== "" && !relative.startsWith("..") && !path14.isAbsolute(relative);
6426
- }
6427
- function to_safe_path(output_path) {
6428
- const normalized2 = path14.normalize(output_path);
6429
- if (!path14.isAbsolute(normalized2)) return void 0;
6430
- return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
6431
- }
6432
- function sample_output(output_path) {
6433
- const safe_path = to_safe_path(output_path);
6434
- if (safe_path === void 0) return void 0;
6435
- const stats = statSync(safe_path, { throwIfNoEntry: false });
6436
- if (!stats?.isFile()) return void 0;
6437
- return { mtime_ms: stats.mtimeMs, size: stats.size };
6438
- }
6439
- function read_agent_state(output_path) {
6440
- const safe_path = to_safe_path(output_path);
6441
- if (safe_path === void 0) return void 0;
6442
- try {
6443
- const state = agent_event.read(readFileSync7(safe_path, "utf8"));
6444
- return state.provider === void 0 ? void 0 : state;
6445
- } catch {
6446
- return void 0;
6447
- }
6448
- }
6449
- function describe_agent_state(output_path) {
6450
- const state = read_agent_state(output_path);
6451
- return state === void 0 ? void 0 : `agent: ${state.provider ?? "unknown"} ${agent_event.describe(state)}`;
6452
- }
6453
- async function read_output_frozen(request2) {
6454
- const first = sample_output(request2.output_path);
6455
- if (first === void 0) return void 0;
6456
- if (request2.now_ms - first.mtime_ms < request2.silent_window_ms) return false;
6457
- await sleep(request2.gap_ms);
6458
- const second = sample_output(request2.output_path);
6459
- if (second === void 0) return void 0;
6460
- return second.mtime_ms === first.mtime_ms && second.size === first.size;
6461
- }
6462
- function carries_label(labels, wanted) {
6463
- return labels.some((label) => label.toLowerCase() === wanted);
6464
- }
6465
- var SETTLED_LABELS = [NEEDS_DECISION_LABEL, ALREADY_DONE_LABEL];
6466
- function carries_settled_label(labels) {
6467
- return SETTLED_LABELS.some((label) => carries_label(labels, label));
6468
- }
6469
- async function read_child_settled(issue, repo) {
6470
- const json = await git_gh_issue_read.issue_view_json(issue, STATE_FIELDS, repo);
6471
- if (json === void 0) return void 0;
6472
- const state = issue_state.parse_issue_state(json);
6473
- if (state === void 0) return void 0;
6474
- return state.state !== OPEN_STATE || carries_settled_label(state.labels);
6475
- }
6476
- async function read_traces(request2) {
6477
- const [is_initial_child_settled, is_output_frozen, is_tree_dirty] = await Promise.all([
6478
- read_child_settled(request2.issue, request2.repo),
6479
- read_output_frozen({
6480
- gap_ms: request2.gap_ms ?? DEFAULT_GAP_SECONDS * MS_PER_SECOND2,
6481
- now_ms: Date.now(),
6482
- output_path: request2.output_path,
6483
- silent_window_ms: request2.silent_window_ms ?? DEFAULT_SILENT_MINUTES * MS_PER_MINUTE3
6484
- }),
6485
- run_hold.is_tree_dirty()
6486
- ]);
6487
- const agent_state = read_agent_state(request2.output_path);
6488
- const is_latest_child_settled = is_agent_state_finished(agent_state) ? await read_child_settled(request2.issue, request2.repo) : void 0;
6489
- const is_child_settled = is_latest_child_settled ?? is_initial_child_settled;
6490
- return {
6491
- agent_state,
6492
- is_child_settled,
6493
- is_output_frozen,
6494
- is_tree_dirty,
6495
- process_trace: request2.process_trace
6496
- };
6497
- }
6498
- async function check2(request2) {
6499
- issue_number_shape.require_issue_number(request2.issue);
6500
- return decide5(await read_traces(request2));
6501
- }
6502
- var run_liveness = {
6503
- check: check2,
6504
- decide: decide5,
6505
- describe_agent_state,
6506
- read_child_settled,
6507
- read_output_frozen,
6508
- sample_output,
6509
- to_safe_path
6510
- };
6511
-
6512
5770
  // scripts/lane/lane-report.ts
6513
5771
  var NO_LANES2 = "No lanes are open.";
6514
- var OPEN_STATE2 = "open";
5772
+ var OPEN_STATE = "open";
6515
5773
  var STRANDED_STATE = "stranded";
6516
5774
  var UNREADABLE_STATE = "unreadable";
6517
5775
  var UNKNOWN_VALUE = "-";
6518
5776
  var COLUMN_SEPARATOR = " ";
6519
5777
  function lane_state(lane) {
6520
5778
  if (lane.is_stranded) return STRANDED_STATE;
6521
- return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE2;
5779
+ return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE;
6522
5780
  }
6523
5781
  function value_or_unknown(value) {
6524
5782
  return value === void 0 ? UNKNOWN_VALUE : String(value);
@@ -6562,7 +5820,7 @@ function describe_opened(lane) {
6562
5820
  }
6563
5821
  var lane_report = {
6564
5822
  NO_LANES: NO_LANES2,
6565
- OPEN_STATE: OPEN_STATE2,
5823
+ OPEN_STATE,
6566
5824
  STRANDED_STATE,
6567
5825
  UNREADABLE_STATE,
6568
5826
  describe_lane,
@@ -6571,197 +5829,6 @@ var lane_report = {
6571
5829
  lane_state
6572
5830
  };
6573
5831
 
6574
- // scripts/run/run-preflight.ts
6575
- import { z as z18 } from "zod";
6576
- var CLEAN_VERDICT = "clean";
6577
- var RECLAIM_VERDICT = "reclaim";
6578
- var RESUME_VERDICT = "resume";
6579
- var PARK_VERDICT = "park";
6580
- var NO_PR = "none";
6581
- var OPEN_PR = "open";
6582
- var MERGED_PR = "merged";
6583
- var CLOSED_PR = "closed";
6584
- var NO_CHILD_WORK = { branch_name: void 0, pr_state: NO_PR };
6585
- var pr_info_schema = z18.object({ state: z18.string().optional() });
6586
- var GH_PR_STATES = /* @__PURE__ */ new Map([
6587
- ["CLOSED", CLOSED_PR],
6588
- ["MERGED", MERGED_PR],
6589
- ["OPEN", OPEN_PR]
6590
- ]);
6591
- var CLEAN_REASON = "Nothing was left behind: the tree is clean, HEAD is on the default branch, and this issue has no pull request and no branch holding work.";
6592
- var CLEAN_ADVICE = "Start the child.";
6593
- var RESUME_ADVICE = "Reuse the branch and run the whole verification gate from the start \u2014 refactor, `pnpm josh gate`, `/code-review` \u2014 never only the part the interrupted run had not reached. Nobody verified what it already committed.";
6594
- var PARK_ADVICE = "Park the child: add `needs-decision` and comment what was found. Do not delete the branch, do not reopen the pull request, and do not commit on top of it.";
6595
- var STASH_LABEL_PREFIX = "run:hold reclaimed before #";
6596
- var { require_issue_number } = issue_number_shape;
6597
- var UNREADABLE_PR_MESSAGE = "The pull request could not be read for branch ";
6598
- function needs_reclaim(tree) {
6599
- return tree.is_dirty || tree.branch !== tree.default_branch;
6600
- }
6601
- function is_pr_decided(state) {
6602
- return state === MERGED_PR || state === CLOSED_PR;
6603
- }
6604
- function has_leftover_work(child) {
6605
- return child.branch_name !== void 0 || child.pr_state === OPEN_PR;
6606
- }
6607
- function to_verdict2(tree, child) {
6608
- if (needs_reclaim(tree)) return RECLAIM_VERDICT;
6609
- if (is_pr_decided(child.pr_state)) return PARK_VERDICT;
6610
- if (has_leftover_work(child)) return RESUME_VERDICT;
6611
- return CLEAN_VERDICT;
6612
- }
6613
- function reclaim_reason(tree) {
6614
- const findings = [
6615
- tree.is_dirty ? "uncommitted changes" : void 0,
6616
- tree.branch === tree.default_branch ? void 0 : `HEAD on ${tree.branch}`
6617
- ].filter((entry) => entry !== void 0);
6618
- return `An earlier run left ${findings.join(" and ")} in this working tree.`;
6619
- }
6620
- function park_reason(state) {
6621
- return `The pull request for this issue is ${state}, so whether any work is still owed is a person's call.`;
6622
- }
6623
- function resume_reason(child) {
6624
- const branch_part = child.branch_name === void 0 ? "" : ` on branch ${child.branch_name}`;
6625
- const pr_part = child.pr_state === OPEN_PR ? ", and its pull request is still open" : "";
6626
- return `An earlier run left a partial implementation${branch_part}${pr_part}.`;
6627
- }
6628
- function to_reason(verdict, tree, child) {
6629
- if (verdict === RECLAIM_VERDICT) return reclaim_reason(tree);
6630
- if (verdict === PARK_VERDICT) return park_reason(child.pr_state);
6631
- if (verdict === RESUME_VERDICT) return resume_reason(child);
6632
- return CLEAN_REASON;
6633
- }
6634
- function reclaim_steps(tree, issue) {
6635
- if (!tree.is_dirty) return [];
6636
- return [
6637
- `git stash push -u -m "${STASH_LABEL_PREFIX}${issue}"`,
6638
- `Record the stash on ${session_cite.issue(issue)} \u2014 the comment is what gets it popped; nothing pops it for you.`
6639
- ];
6640
- }
6641
- function reclaim_advice(tree, issue) {
6642
- return [
6643
- ...reclaim_steps(tree, issue),
6644
- "pnpm josh ms",
6645
- "Then ask this command again: it is re-askable, and the clean tree gets its own answer."
6646
- ].join("\n");
6647
- }
6648
- function to_advice2(verdict, issue, tree) {
6649
- if (verdict === RECLAIM_VERDICT) return reclaim_advice(tree, issue);
6650
- if (verdict === RESUME_VERDICT) return RESUME_ADVICE;
6651
- if (verdict === PARK_VERDICT) return PARK_ADVICE;
6652
- return CLEAN_ADVICE;
6653
- }
6654
- function decide6(tree, child, issue) {
6655
- const verdict = to_verdict2(tree, child);
6656
- return {
6657
- advice: to_advice2(verdict, issue, tree),
6658
- reason: to_reason(verdict, tree, child),
6659
- verdict
6660
- };
6661
- }
6662
- function read_pr_field(raw) {
6663
- return json_value.parse_with(raw, pr_info_schema)?.state;
6664
- }
6665
- function to_pr_state(raw) {
6666
- const state = read_pr_field(raw);
6667
- return (state === void 0 ? void 0 : GH_PR_STATES.get(state)) ?? NO_PR;
6668
- }
6669
- async function read_tree_state() {
6670
- const [is_dirty, current, default_branch] = await Promise.all([
6671
- run_hold.is_tree_dirty(),
6672
- git_command.branch(),
6673
- git_command.get_default_branch()
6674
- ]);
6675
- return { branch: current, default_branch, is_dirty };
6676
- }
6677
- async function read_pr_state(branch_name) {
6678
- if (!await git_gh_pr_read.pr_exists(branch_name)) return NO_PR;
6679
- const raw = await git_gh_pr_read.pr_view(branch_name);
6680
- if (raw === "") throw new Error(`${UNREADABLE_PR_MESSAGE}${branch_name}`);
6681
- return to_pr_state(raw);
6682
- }
6683
- function is_pr_open(state) {
6684
- return state === OPEN_PR;
6685
- }
6686
- async function read_pr_of_branch(branch_name) {
6687
- return { branch_name, pr_state: await read_pr_state(branch_name) };
6688
- }
6689
- function pick_child_state(reads) {
6690
- const decided = reads.find((read3) => is_pr_decided(read3.pr_state));
6691
- const open = reads.find((read3) => is_pr_open(read3.pr_state));
6692
- return decided ?? open ?? { branch_name: reads[0]?.branch_name, pr_state: NO_PR };
6693
- }
6694
- var ISSUE_BRANCH_SUFFIX = "-*";
6695
- var ANY_REMOTE_PREFIX = "*/";
6696
- var REMOTE_SEPARATOR = "/";
6697
- var NOT_FOUND_INDEX = -1;
6698
- function to_head_branch(name) {
6699
- const separator_index = name.indexOf(REMOTE_SEPARATOR);
6700
- return separator_index === NOT_FOUND_INDEX ? name : name.slice(separator_index + 1);
6701
- }
6702
- function group_by_head(pairs) {
6703
- const groups = /* @__PURE__ */ new Map();
6704
- for (const [name, reference] of pairs) groups.set(name, [...groups.get(name) ?? [], reference]);
6705
- return groups;
6706
- }
6707
- async function read_branch_candidates(issue) {
6708
- const pattern = `${issue}${ISSUE_BRANCH_SUFFIX}`;
6709
- const [local, remote] = await Promise.all([
6710
- git_command.branch_names(pattern),
6711
- git_command.branch_names_remote(`${ANY_REMOTE_PREFIX}${pattern}`)
6712
- ]);
6713
- return group_by_head([
6714
- ...local.map((name) => [name, name]),
6715
- ...remote.map((reference) => [to_head_branch(reference), reference])
6716
- ]);
6717
- }
6718
- async function has_commits_beyond(base, references) {
6719
- const counts = await Promise.all(
6720
- references.map(async (reference) => await git_command.commit_count_beyond(base, reference))
6721
- );
6722
- return counts.some((count) => count > 0);
6723
- }
6724
- async function is_held_dirty(name, lane) {
6725
- if (lane?.branch !== name || lane.is_stranded) return false;
6726
- return await run_hold.is_tree_dirty(lane.directory);
6727
- }
6728
- async function read_candidate(name, references, context) {
6729
- const state = await read_pr_of_branch(name);
6730
- if (state.pr_state !== NO_PR || await has_commits_beyond(context.base, references)) return state;
6731
- return await is_held_dirty(name, context.lane) ? state : void 0;
6732
- }
6733
- async function read_child_state(issue) {
6734
- const candidates = await read_branch_candidates(issue);
6735
- if (candidates.size === 0) return NO_CHILD_WORK;
6736
- const [base, lane] = await Promise.all([
6737
- git_command.default_branch_reference(),
6738
- lane_registry.find_open_lane(issue)
6739
- ]);
6740
- const reads = await Promise.all(
6741
- [...candidates].map(
6742
- async ([name, references]) => await read_candidate(name, references, { base, lane })
6743
- )
6744
- );
6745
- return pick_child_state(reads.filter((read3) => read3 !== void 0));
6746
- }
6747
- async function check3(issue) {
6748
- require_issue_number(issue);
6749
- const tree = await read_tree_state();
6750
- if (needs_reclaim(tree)) return decide6(tree, NO_CHILD_WORK, issue);
6751
- return decide6(tree, await read_child_state(issue), issue);
6752
- }
6753
- var run_preflight = {
6754
- CLEAN_VERDICT,
6755
- RESUME_ADVICE,
6756
- MERGED_PR,
6757
- STASH_LABEL_PREFIX,
6758
- check: check3,
6759
- decide: decide6,
6760
- read_child_state,
6761
- read_pr_state,
6762
- to_pr_state
6763
- };
6764
-
6765
5832
  // scripts/run/progress/run-progress-read.ts
6766
5833
  var { mark: mark2, parse_stamp: parse_stamp2, read_last_report: read_last_report2 } = run_progress_clock;
6767
5834
  var FIRST_LOAD_AVERAGE = 0;
@@ -6789,10 +5856,10 @@ async function to_child(issue) {
6789
5856
  return { issue: number, title: issue.title, labels: to_labels(issue), pr_state: child.pr_state };
6790
5857
  }
6791
5858
  async function read_children(repo) {
6792
- const read3 = await epic_busy.read_repository(repo);
6793
- if (read3.kind === "idle") return [];
6794
- if (read3.kind !== "busy") return void 0;
6795
- return await Promise.all(read3.issues.map(async (issue) => await to_child(issue)));
5859
+ const read2 = await epic_busy.read_repository(repo);
5860
+ if (read2.kind === "idle") return [];
5861
+ if (read2.kind !== "busy") return void 0;
5862
+ return await Promise.all(read2.issues.map(async (issue) => await to_child(issue)));
6796
5863
  }
6797
5864
  async function read_lanes() {
6798
5865
  const lanes = await lane_registry.list_lanes();
@@ -6806,11 +5873,11 @@ function read_record_age(paths, now_ms) {
6806
5873
  if (times.length === 0) return void 0;
6807
5874
  return now_ms - Math.max(...times);
6808
5875
  }
6809
- function is_hold_present(read3) {
6810
- return read3.kind === "held" || read3.kind === "stale";
5876
+ function is_hold_present(read2) {
5877
+ return read2.kind === "held" || read2.kind === "stale";
6811
5878
  }
6812
- function is_carry_present(read3) {
6813
- return read3.kind === "carried";
5879
+ function is_carry_present(read2) {
5880
+ return read2.kind === "carried";
6814
5881
  }
6815
5882
  function hold_present(worktree) {
6816
5883
  return worktree !== void 0 && is_hold_present(run_hold.read_hold(run_hold.hold_path(worktree)));
@@ -6854,21 +5921,21 @@ var run_progress_read = {
6854
5921
  };
6855
5922
 
6856
5923
  // scripts/run/run-watcher-hook.ts
6857
- import { z as z19 } from "zod";
5924
+ import { z as z13 } from "zod";
6858
5925
 
6859
5926
  // scripts/run/run-watcher-guard.ts
6860
5927
  var WATCHER_TICK_MS = 3e4;
6861
5928
  var TICK_MULTIPLIER = 3;
6862
- var MS_PER_SECOND3 = 1e3;
5929
+ var MS_PER_SECOND2 = 1e3;
6863
5930
  var STALE_THRESHOLD_MS = WATCHER_TICK_MS * TICK_MULTIPLIER;
6864
- var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND3;
5931
+ var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND2;
6865
5932
  var STALE_NOTE = `Lane children are in-flight but the progress watcher has not pinged in the last ${String(STALE_THRESHOLD_S)}s \u2014 run \`pnpm josh run:progress --wait\` in the background before proceeding. It reports to the run event stream on its own and exits only when newly runnable work arrives, so start it once and relay nothing`;
6866
5933
  var OK_RESULT = { kind: "ok" };
6867
5934
  async function owes_watcher() {
6868
5935
  if (run_headless.is_headless() || !await run_headless.is_backlog_parent()) return false;
6869
5936
  return await lane_registry.has_lanes_in_flight();
6870
5937
  }
6871
- async function check4(life_target) {
5938
+ async function check2(life_target) {
6872
5939
  if (!await owes_watcher() || run_progress_clock.is_life_fresh(life_target, STALE_THRESHOLD_MS)) {
6873
5940
  return OK_RESULT;
6874
5941
  }
@@ -6877,13 +5944,13 @@ async function check4(life_target) {
6877
5944
  var run_watcher_guard = {
6878
5945
  STALE_NOTE,
6879
5946
  STALE_THRESHOLD_MS,
6880
- check: check4
5947
+ check: check2
6881
5948
  };
6882
5949
 
6883
5950
  // scripts/run/run-watcher-hook.ts
6884
5951
  var SWITCH_ENV_KEY2 = "JOSH_WATCHER_GUARD";
6885
5952
  var STAMP_PREFIX2 = "josh-watcher-guard-";
6886
- var payload_schema = z19.object({ transcript_path: z19.string().min(1) });
5953
+ var payload_schema = z13.object({ transcript_path: z13.string().min(1) });
6887
5954
  function transcript_of(raw_payload) {
6888
5955
  return json_value.parse_with(raw_payload, payload_schema)?.transcript_path;
6889
5956
  }
@@ -6913,7 +5980,7 @@ var run_watcher_hook = { transcript_of, watcher_hook_reason };
6913
5980
  var SWITCH_ENV_KEY3 = "JOSH_PARENT_CUT_GUARD";
6914
5981
  var STAMP_PREFIX3 = "josh-parent-cut-guard-";
6915
5982
  var QUIET_WINDOW_MS = 6e5;
6916
- function current_state3() {
5983
+ function current_state2() {
6917
5984
  return {
6918
5985
  carry: async () => await run_headless.driving_carry(),
6919
5986
  verdict: () => implementation_cut_verdict.reused_verdict(() => cost_cli.session_verdict())
@@ -6947,7 +6014,7 @@ function is_unit_call(raw_payload) {
6947
6014
  function is_exempt2(raw_payload) {
6948
6015
  return !hook_decision.is_switch_enabled(SWITCH_ENV_KEY3) || is_unit_call(raw_payload);
6949
6016
  }
6950
- async function parent_cut_reason(raw_payload, now_ms = Date.now(), state = current_state3()) {
6017
+ async function parent_cut_reason(raw_payload, now_ms = Date.now(), state = current_state2()) {
6951
6018
  if (is_exempt2(raw_payload)) return void 0;
6952
6019
  const transcript = run_watcher_hook.transcript_of(raw_payload);
6953
6020
  if (transcript === void 0) return void 0;
@@ -6960,8 +6027,95 @@ var run_parent_cut_hook = {
6960
6027
  parent_cut_reason
6961
6028
  };
6962
6029
 
6030
+ // scripts/run/run-poll-hook.ts
6031
+ var SWITCH_ENV_KEY4 = "JOSH_POLL_GUARD";
6032
+ var BASH_TOOL = "Bash";
6033
+ var STATE_READS = /* @__PURE__ */ new Set([
6034
+ "josh lane:list",
6035
+ "josh lane:output",
6036
+ "josh run:board",
6037
+ "josh run:carry",
6038
+ "josh run:liveness",
6039
+ "josh run:progress",
6040
+ "josh run:wake"
6041
+ ]);
6042
+ var POLL_REASON = '\u26D4 poll: this `backlogrun` session repeats a state read with nothing decided in between, on every occurrence. Wait on `pnpm josh backlog:drive`, or `pnpm josh lane:await <N...> --owner "$PPID"` in the background, and end the turn; its exit is the wake.';
6043
+ var WRITING_FLAGS = /* @__PURE__ */ new Set([
6044
+ "--begin",
6045
+ "--cut",
6046
+ "--done",
6047
+ "--end",
6048
+ "--every",
6049
+ "--filed",
6050
+ "--loop",
6051
+ "--mark",
6052
+ "--merged",
6053
+ "--resume",
6054
+ "--retrospective",
6055
+ "--start",
6056
+ "--stop",
6057
+ "--wait"
6058
+ ]);
6059
+ var FLAG_PREFIX4 = "-";
6060
+ var NAME_WORDS = 2;
6061
+ var SIGNATURE_SEPARATOR = " ";
6062
+ function read_key(signature) {
6063
+ const words = signature.split(SIGNATURE_SEPARATOR);
6064
+ const name = words.slice(0, NAME_WORDS).join(SIGNATURE_SEPARATOR);
6065
+ if (!STATE_READS.has(name) || words.some((word) => WRITING_FLAGS.has(word))) return void 0;
6066
+ return words.filter((word) => !word.startsWith(FLAG_PREFIX4)).join(SIGNATURE_SEPARATOR);
6067
+ }
6068
+ function read_keys(signatures) {
6069
+ return signatures.map((signature) => read_key(signature)).filter((key) => key !== void 0);
6070
+ }
6071
+ function state_reads_of(command) {
6072
+ return read_keys(time_shell.josh_signatures_of(command));
6073
+ }
6074
+ function command_of(input) {
6075
+ if (typeof input !== "object" || input === null || !("command" in input)) return "";
6076
+ return typeof input.command === "string" ? input.command : "";
6077
+ }
6078
+ function candidate_reads(raw_payload) {
6079
+ const payload = hook_decision.parse_hook_payload(raw_payload);
6080
+ if (payload?.tool_name !== BASH_TOOL || (payload.agent_id ?? "") !== "") return [];
6081
+ return state_reads_of(command_of(payload.tool_input));
6082
+ }
6083
+ function last_step(tail) {
6084
+ return time_spans.parse_timeline(tail).spans.findLast((span) => span.category !== time_spans.MODEL_CATEGORY);
6085
+ }
6086
+ function repeats_last_call(tail, reads) {
6087
+ const last = last_step(tail);
6088
+ if (last?.category !== time_spans.TOOL_CATEGORY) return false;
6089
+ const last_reads = read_keys(last.josh_signatures);
6090
+ return reads.some((key) => last_reads.includes(key));
6091
+ }
6092
+ function is_repeat(raw_payload) {
6093
+ const reads = hook_decision.is_switch_enabled(SWITCH_ENV_KEY4) ? candidate_reads(raw_payload) : [];
6094
+ const transcript = reads.length === 0 ? void 0 : run_watcher_hook.transcript_of(raw_payload);
6095
+ if (transcript === void 0) return false;
6096
+ return repeats_last_call(time_density_hook.read_tail(transcript), reads);
6097
+ }
6098
+ async function poll_reason(raw_payload, is_parent = run_headless.is_backlog_parent) {
6099
+ if (!is_repeat(raw_payload)) return void 0;
6100
+ return await is_parent() ? POLL_REASON : void 0;
6101
+ }
6102
+ async function safe_poll_reason(raw_payload) {
6103
+ try {
6104
+ return await poll_reason(raw_payload);
6105
+ } catch (error) {
6106
+ error_text.trace_swallowed("run_poll_hook.poll_reason", error);
6107
+ return void 0;
6108
+ }
6109
+ }
6110
+ var run_poll_hook = {
6111
+ POLL_REASON,
6112
+ SWITCH_ENV_KEY: SWITCH_ENV_KEY4,
6113
+ poll_reason,
6114
+ safe_poll_reason
6115
+ };
6116
+
6963
6117
  // scripts/hooks/step-zero-notice.ts
6964
- import path15 from "node:path";
6118
+ import path12 from "node:path";
6965
6119
  var STAMP_PREFIX4 = "josh-step-zero-notice-";
6966
6120
  var EDIT_TOOLS3 = /* @__PURE__ */ new Set(["Edit", "Write"]);
6967
6121
  var PARENT_PREFIX = "..";
@@ -6976,9 +6130,9 @@ function edited_paths(tool_input) {
6976
6130
  );
6977
6131
  }
6978
6132
  function is_runtime_file(file_path, root) {
6979
- const relative = path15.relative(root, path15.resolve(root, file_path));
6980
- if (relative.startsWith(PARENT_PREFIX) || path15.isAbsolute(relative)) return false;
6981
- return !test_declared_logic.is_exempt(relative.split(path15.sep).join("/"));
6133
+ const relative = path12.relative(root, path12.resolve(root, file_path));
6134
+ if (relative.startsWith(PARENT_PREFIX) || path12.isAbsolute(relative)) return false;
6135
+ return !test_declared_logic.is_exempt(relative.split(path12.sep).join("/"));
6982
6136
  }
6983
6137
  function is_candidate2(raw_payload, root) {
6984
6138
  const payload = hook_decision.parse_hook_payload(raw_payload);
@@ -7031,7 +6185,8 @@ async function mark_phase(outcome) {
7031
6185
  async function async_reason(raw_payload) {
7032
6186
  const watcher = await run_watcher_hook.watcher_hook_reason(raw_payload);
7033
6187
  const cut = watcher ?? await run_parent_cut_hook.parent_cut_reason(raw_payload);
7034
- return cut ?? await issue_watch_hook.watch_reason();
6188
+ const poll2 = cut ?? await run_poll_hook.safe_poll_reason(raw_payload);
6189
+ return poll2 ?? await issue_watch_hook.watch_reason();
7035
6190
  }
7036
6191
  async function pretool_outcome_async(raw_payload) {
7037
6192
  const base = guard_outcome(raw_payload);
@@ -7063,38 +6218,26 @@ function foreground_rewrite(outcome, raw_payload, is_lane_child) {
7063
6218
  rewrite_context(lane_background.FOREGROUND_NOTE, outcome)
7064
6219
  );
7065
6220
  }
7066
- function pipefail_rewrite(raw_payload) {
7067
- if (!delivered_rules.is_enabled()) return void 0;
7068
- const call = guarded_call(raw_payload);
7069
- const input = call === void 0 ? void 0 : piped_verification.pipefail_input(call);
7070
- const payload = json_value.parse_or_undefined(raw_payload);
7071
- if (input === void 0 || !json_value.is_record(payload)) return void 0;
7072
- return { payload: JSON.stringify({ ...payload, tool_input: input }), input };
7073
- }
7074
- function pipefail_envelope(outcome, input) {
7075
- if (input === void 0 || outcome.reason !== void 0) return void 0;
7076
- return hook_decision.flow_rewrite_envelope(
7077
- input,
7078
- rewrite_context(piped_verification.PIPEFAIL_NOTE, outcome)
7079
- );
6221
+ function rule_envelope(outcome, rewrite3) {
6222
+ if (rewrite3 === void 0 || outcome.reason !== void 0) return void 0;
6223
+ return hook_decision.flow_rewrite_envelope(rewrite3.input, rewrite_context(rewrite3.note, outcome));
7080
6224
  }
7081
- function emit(raw_payload, outcome, pipefail_input2) {
7082
- const rewrite = pipefail_envelope(outcome, pipefail_input2) ?? foreground_rewrite(outcome, raw_payload);
7083
- if (rewrite === void 0) hook_decision.emit_outcome(outcome);
7084
- else process.stdout.write(`${rewrite}
6225
+ function emit(raw_payload, outcome, rewrite3) {
6226
+ const envelope = rule_envelope(outcome, rewrite3) ?? foreground_rewrite(outcome, raw_payload);
6227
+ if (envelope === void 0) hook_decision.emit_outcome(outcome);
6228
+ else process.stdout.write(`${envelope}
7085
6229
  `);
7086
6230
  }
7087
6231
  async function respond(raw_payload) {
7088
- const pipefail = pipefail_rewrite(raw_payload);
7089
- const judged = pipefail?.payload ?? raw_payload;
7090
- emit(judged, await pretool_outcome_async(judged), pipefail?.input);
6232
+ const rewrite3 = rule_rewrite.rewrite(raw_payload);
6233
+ const judged = rewrite3?.payload ?? raw_payload;
6234
+ emit(judged, await pretool_outcome_async(judged), rewrite3);
7091
6235
  }
7092
6236
  var pretool_guard = {
7093
6237
  combine_outcomes,
7094
6238
  emit,
7095
6239
  foreground_rewrite,
7096
6240
  mark_phase,
7097
- pipefail_rewrite,
7098
6241
  pretool_outcome,
7099
6242
  respond
7100
6243
  };