@joshuafolkken/kit 1.1455.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 (374) hide show
  1. package/.claude/settings.json +6 -6
  2. package/.claude/skills/dependency-update/SKILL.md +26 -55
  3. package/.claude/skills/epic-commands/SKILL.md +83 -156
  4. package/.claude/skills/epic-commands/epic-bundle.md +51 -79
  5. package/.claude/skills/epic-commands/execution-waves.md +43 -81
  6. package/.claude/skills/verify-ui/SKILL.md +24 -50
  7. package/.claude/skills/workflow-commands/SKILL.md +7 -9
  8. package/.claude/skills/workflow-commands/background-commands.md +31 -46
  9. package/.claude/skills/workflow-commands/backlogrun-child.md +65 -85
  10. package/.claude/skills/workflow-commands/backlogrun-lanes.md +86 -125
  11. package/.claude/skills/workflow-commands/backlogrun-park.md +68 -118
  12. package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -28
  13. package/.claude/skills/workflow-commands/backlogrun-recovery.md +17 -26
  14. package/.claude/skills/workflow-commands/backlogrun-steps.md +35 -52
  15. package/.claude/skills/workflow-commands/backlogrun.md +31 -50
  16. package/.claude/skills/workflow-commands/chain-rule.md +31 -40
  17. package/.claude/skills/workflow-commands/delegation.md +12 -8
  18. package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
  19. package/.claude/skills/workflow-commands/followup.md +55 -77
  20. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
  21. package/.claude/skills/workflow-commands/fullrun.md +4 -6
  22. package/.claude/skills/workflow-commands/halfrun.md +3 -7
  23. package/.claude/skills/workflow-commands/into-target.md +1 -2
  24. package/.claude/skills/workflow-commands/issue-comments.md +43 -60
  25. package/.claude/skills/workflow-commands/issue-scout.md +3 -2
  26. package/.claude/skills/workflow-commands/kickoff.md +65 -74
  27. package/.claude/skills/workflow-commands/latest-gate.md +2 -6
  28. package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
  29. package/.claude/skills/workflow-commands/observation-filing.md +59 -118
  30. package/.claude/skills/workflow-commands/observation-ledger.md +52 -112
  31. package/.claude/skills/workflow-commands/pre-gate-cut.md +88 -142
  32. package/.claude/skills/workflow-commands/prerequisite.md +11 -17
  33. package/.claude/skills/workflow-commands/progress-watcher.md +23 -33
  34. package/.claude/skills/workflow-commands/prrun.md +3 -5
  35. package/.claude/skills/workflow-commands/retrospective.md +35 -54
  36. package/.claude/skills/workflow-commands/split-assessment.md +32 -58
  37. package/.claude/skills/workflow-commands/target-repository.md +1 -2
  38. package/.claude/skills/workflow-commands/working-tree-hold.md +54 -73
  39. package/.codex/hooks.json +6 -6
  40. package/.github/actions/setup-node/action.yml +1 -1
  41. package/.github/workflows/ci.yml +1 -1
  42. package/.github/workflows/publish.yml +2 -2
  43. package/CLAUDE.md +7 -6
  44. package/cspell/index.yaml +7 -0
  45. package/dist/CLAUDE.md +7 -6
  46. package/dist/commands/document-section-cli.js +67 -34
  47. package/dist/hooks/batch-guard.js +6 -6
  48. package/dist/hooks/chunk-4KKD2UTK.js +66 -0
  49. package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-6TS2JD6D.js} +14 -3
  50. package/dist/hooks/chunk-7DSBJVFH.js +50 -0
  51. package/dist/hooks/{chunk-5H4SSDSD.js → chunk-A65Y4YOR.js} +3 -3
  52. package/dist/hooks/{chunk-3C4XRYG6.js → chunk-APUO6U4J.js} +102 -19
  53. package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
  54. package/dist/hooks/chunk-GP4IEC4O.js +20 -0
  55. package/dist/hooks/chunk-JP45SOCJ.js +56 -0
  56. package/dist/hooks/{chunk-OLMO72ON.js → chunk-LAOV7NLM.js} +1 -1
  57. package/dist/hooks/chunk-M4HGZV7U.js +83 -0
  58. package/dist/hooks/{chunk-TBJFTSUY.js → chunk-O6GDRNJB.js} +9 -7
  59. package/dist/hooks/{chunk-HFGWYGUV.js → chunk-PF2NZL2A.js} +5 -5
  60. package/dist/hooks/{chunk-WNLVGT7E.js → chunk-PTICX4OL.js} +4 -5
  61. package/dist/hooks/{chunk-HHI6FO3R.js → chunk-RT27QRJW.js} +763 -1515
  62. package/dist/hooks/{chunk-7WQBPLUN.js → chunk-SYOTDYCI.js} +1149 -1095
  63. package/dist/hooks/{chunk-LIAFCXL7.js → chunk-TNUMY6NL.js} +1519 -1332
  64. package/dist/hooks/chunk-UPUZF3AN.js +279 -0
  65. package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-WK6SMBIC.js} +1932 -784
  66. package/dist/hooks/codex-hook-adapter.js +17 -14
  67. package/dist/hooks/duplicate-read-guard.js +7 -7
  68. package/dist/hooks/format-edited.js +5 -4
  69. package/dist/hooks/inputs.json +302 -247
  70. package/dist/hooks/investigation-guard.js +8 -11
  71. package/dist/hooks/pretool-guard.js +16 -13
  72. package/dist/hooks/session-lang.js +372 -26
  73. package/dist/hooks/stop-guard.js +2593 -189
  74. package/dist/josh.js +2128 -282
  75. package/dist/managed-marker/index.js +1 -2
  76. package/dist/version/index.js +15 -12
  77. package/eslint/base.js +2 -1
  78. package/eslint/rules/unicorn.js +16 -0
  79. package/lefthook/base.yml +5 -3
  80. package/package.json +14 -12
  81. package/prompts/coding-standards.md +12 -22
  82. package/prompts/collaboration-workflow/file-edits.md +25 -45
  83. package/prompts/collaboration-workflow/gh-rest.md +19 -36
  84. package/prompts/collaboration-workflow/glossary.md +5 -7
  85. package/prompts/collaboration-workflow/issue-citation.md +6 -14
  86. package/prompts/collaboration-workflow/issue-template.md +40 -32
  87. package/prompts/collaboration-workflow/lane-instructions.md +35 -0
  88. package/prompts/collaboration-workflow/operating-rules.md +33 -46
  89. package/prompts/collaboration-workflow/output-bounds.md +15 -15
  90. package/prompts/collaboration-workflow/overview.md +14 -19
  91. package/prompts/collaboration-workflow/plan-comment.md +13 -13
  92. package/prompts/collaboration-workflow/principles.md +57 -54
  93. package/prompts/collaboration-workflow/proposal-request.md +1 -9
  94. package/prompts/collaboration-workflow/report-format.md +41 -49
  95. package/prompts/collaboration-workflow/residency.md +15 -11
  96. package/prompts/collaboration-workflow/rule-delivery.md +7 -15
  97. package/prompts/collaboration-workflow/shell-body.md +10 -20
  98. package/prompts/collaboration-workflow/turn-batching.md +8 -14
  99. package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
  100. package/prompts/collaboration-workflow/wip-cap.md +26 -32
  101. package/prompts/collaboration-workflow.md +25 -25
  102. package/prompts/refactoring.md +33 -43
  103. package/prompts/review-rubric.md +84 -129
  104. package/prompts/review.md +110 -141
  105. package/prompts/sonar-hotspot-handling.md +22 -50
  106. package/prompts/testing-guide.md +48 -99
  107. package/scripts/adopt/adopt-toolkits.ts +2 -4
  108. package/scripts/agent/agent-argv.ts +20 -4
  109. package/scripts/agent/agent-exit-record.ts +23 -1
  110. package/scripts/agent/agent-role-profile.ts +101 -45
  111. package/scripts/agent/agent-safe-text.ts +60 -0
  112. package/scripts/agent/claude-agent-argv.ts +14 -0
  113. package/scripts/agent/codex-agent-argv.ts +9 -2
  114. package/scripts/agent/lane-instructions.ts +144 -0
  115. package/scripts/backlog/backlog-budget-cli.ts +1 -2
  116. package/scripts/backlog/backlog-budget.ts +4 -4
  117. package/scripts/backlog/backlog-drive-carried.ts +26 -0
  118. package/scripts/backlog/backlog-drive-cli.ts +10 -5
  119. package/scripts/backlog/backlog-drive-named.ts +3 -5
  120. package/scripts/backlog/backlog-drive-restore.ts +28 -2
  121. package/scripts/backlog/backlog-drive-seats.ts +27 -0
  122. package/scripts/backlog/backlog-drive.ts +11 -6
  123. package/scripts/backlog/backlog-occupied.ts +56 -0
  124. package/scripts/backlog/backlog-ready.ts +59 -15
  125. package/scripts/behavior/behavior-rules.ts +3 -2
  126. package/scripts/cost-runtime/codex-usage.ts +4 -2
  127. package/scripts/cost-runtime/cost-cli.ts +1 -2
  128. package/scripts/delegation/duplicate-read-guard.ts +2 -2
  129. package/scripts/delegation/investigation-guard.ts +15 -65
  130. package/scripts/delegation/investigation-reads.ts +1 -2
  131. package/scripts/discovery/repo-discovery.ts +4 -2
  132. package/scripts/discovery/repo-origin.ts +2 -4
  133. package/scripts/doctor/doctor-logic.ts +6 -3
  134. package/scripts/document/document-byte-budget.ts +28 -27
  135. package/scripts/document/document-byte-check.ts +1 -2
  136. package/scripts/document/document-section-cli.ts +17 -5
  137. package/scripts/document/entry-read-budget.ts +3 -3
  138. package/scripts/document/read-set-cli.ts +4 -1
  139. package/scripts/epic/epic-audit-orphans.ts +5 -8
  140. package/scripts/epic/epic-audit-rationale.ts +8 -2
  141. package/scripts/epic/epic-audit.ts +52 -1
  142. package/scripts/epic/epic-bundle-cli.ts +1 -2
  143. package/scripts/epic/epic-bundle-evidence.ts +49 -26
  144. package/scripts/epic/epic-bundle-referenced.ts +14 -3
  145. package/scripts/epic/epic-bundle.ts +17 -9
  146. package/scripts/epic/epic-chains.ts +6 -2
  147. package/scripts/epic/epic-close-comment.ts +1 -2
  148. package/scripts/followup/git-followup-workflow.ts +19 -1
  149. package/scripts/gate/check-inputs.ts +129 -0
  150. package/scripts/gate/check-reuse.ts +162 -0
  151. package/scripts/gate/core-budget.ts +93 -44
  152. package/scripts/gate/core-wait.ts +130 -0
  153. package/scripts/gate/free-cores.ts +49 -0
  154. package/scripts/gate/gate-ledger.ts +17 -3
  155. package/scripts/gate/gate-plan.ts +23 -1
  156. package/scripts/gate/gate-report.ts +15 -2
  157. package/scripts/gate/gate-run-steps.ts +150 -0
  158. package/scripts/gate/held-mark.ts +55 -0
  159. package/scripts/gate/machine-capacity.ts +70 -11
  160. package/scripts/gate/pre-commit-type-check.ts +14 -1
  161. package/scripts/gate/verification-gate.ts +65 -127
  162. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  163. package/scripts/gh/git-gh-issue-write.ts +24 -2
  164. package/scripts/gh/git-pr.ts +19 -3
  165. package/scripts/git/git-common-directory.ts +3 -3
  166. package/scripts/git/git-diff-reads.ts +16 -9
  167. package/scripts/git/git-execa-error.ts +1 -2
  168. package/scripts/git/main-merge-regenerate.ts +78 -0
  169. package/scripts/git/main-merge.ts +14 -5
  170. package/scripts/hooks/batch-guard.ts +5 -8
  171. package/scripts/hooks/format-edited-file.ts +10 -4
  172. package/scripts/hooks/pre-commit-lint.ts +91 -0
  173. package/scripts/hooks/pre-push-unit.ts +15 -4
  174. package/scripts/hooks/pretool-guard.ts +48 -79
  175. package/scripts/hooks/reserved-run.ts +1 -0
  176. package/scripts/hooks/run-hook.sh +10 -12
  177. package/scripts/hooks/step-zero-notice.ts +3 -2
  178. package/scripts/hooks/stop-actions.ts +204 -0
  179. package/scripts/hooks/stop-guard.ts +62 -21
  180. package/scripts/init/hook-command-rewrite.ts +6 -2
  181. package/scripts/init/hook-launch.ts +12 -7
  182. package/scripts/init/init-logic-deploy-vps.ts +4 -2
  183. package/scripts/issue/behavior-change-lint.ts +12 -6
  184. package/scripts/issue/expected-effect-lint.ts +59 -0
  185. package/scripts/issue/issue-cite-cli.ts +35 -15
  186. package/scripts/issue/issue-classification.ts +1 -1
  187. package/scripts/issue/issue-lint-cli.ts +5 -3
  188. package/scripts/issue/issue-scout-cli.ts +22 -16
  189. package/scripts/issue/markdown-section.ts +19 -5
  190. package/scripts/josh/command-usage.ts +110 -0
  191. package/scripts/josh/file-map-stamp.ts +1 -3
  192. package/scripts/josh/hook-decision.ts +17 -2
  193. package/scripts/josh/josh-command-map.ts +2 -0
  194. package/scripts/josh/josh-command-types.ts +55 -9
  195. package/scripts/josh/josh-commands-ai.ts +13 -0
  196. package/scripts/josh/josh-commands-development.ts +1 -1
  197. package/scripts/josh/josh-commands-hooks.ts +6 -0
  198. package/scripts/josh/josh-commands-lane.ts +10 -1
  199. package/scripts/josh/josh-commands-workflow.ts +5 -0
  200. package/scripts/josh/josh-logic.ts +6 -15
  201. package/scripts/josh/josh-usage-automation.ts +121 -0
  202. package/scripts/josh/josh-usage-backlog.ts +180 -0
  203. package/scripts/josh/josh-usage-developer.ts +230 -0
  204. package/scripts/josh/josh-usage-hooks.ts +38 -0
  205. package/scripts/josh/josh-usage-issue.ts +205 -0
  206. package/scripts/josh/josh-usage-review.ts +101 -0
  207. package/scripts/josh/josh-usage-run.ts +252 -0
  208. package/scripts/josh/josh-usage-session.ts +238 -0
  209. package/scripts/josh/josh-usage-workflow.ts +212 -0
  210. package/scripts/josh/josh.ts +14 -5
  211. package/scripts/josh/package-directory.ts +20 -0
  212. package/scripts/lane/lane-await.ts +3 -2
  213. package/scripts/lane/lane-capacity.ts +8 -2
  214. package/scripts/lane/lane-check-stats.ts +56 -0
  215. package/scripts/lane/lane-child-invocation.ts +49 -5
  216. package/scripts/lane/lane-dispatch.ts +3 -3
  217. package/scripts/lane/lane-guard-policy.ts +5 -5
  218. package/scripts/lane/lane-handoff.ts +8 -1
  219. package/scripts/lane/lane-install.ts +12 -4
  220. package/scripts/lane/lane-ledger.ts +52 -1
  221. package/scripts/lane/lane-limit-cli.ts +8 -9
  222. package/scripts/lane/lane-load.ts +9 -2
  223. package/scripts/lane/lane-measure-cli.ts +4 -0
  224. package/scripts/lane/lane-parked.ts +18 -0
  225. package/scripts/lane/lane-preserve.ts +29 -1
  226. package/scripts/lane/lane-process-load.ts +125 -0
  227. package/scripts/lane/lane-process-stats.ts +93 -0
  228. package/scripts/lane/lane-registry.ts +17 -10
  229. package/scripts/lane/lane-restart.ts +5 -26
  230. package/scripts/lane/lane-session-action.ts +146 -0
  231. package/scripts/lane/lane-session-broker.ts +68 -125
  232. package/scripts/lane/lane-spent.ts +14 -3
  233. package/scripts/lane/lane-stats.ts +1 -0
  234. package/scripts/lane/lane-withdraw-cli.ts +71 -0
  235. package/scripts/lane/lane-withdraw.ts +182 -0
  236. package/scripts/lint/format.ts +4 -4
  237. package/scripts/lint/lint-parallel.ts +10 -3
  238. package/scripts/lint/lint-related-scope.ts +4 -1
  239. package/scripts/managed-marker/managed-marker-logic.ts +1 -2
  240. package/scripts/notify/telegram-test.ts +10 -5
  241. package/scripts/overrides/lockfile-overrides.ts +1 -3
  242. package/scripts/propagate/propagate.ts +1 -2
  243. package/scripts/release/release-progress.ts +3 -2
  244. package/scripts/report/report-format-reference.ts +2 -0
  245. package/scripts/report/report-lint.ts +7 -4
  246. package/scripts/review/acceptance-map-check.ts +61 -0
  247. package/scripts/review/acceptance-map.ts +205 -0
  248. package/scripts/review/review-brief-cli.ts +1 -1
  249. package/scripts/review/review-record-cli.ts +6 -2
  250. package/scripts/rules/agent-model.ts +59 -0
  251. package/scripts/rules/delivered-rules.ts +11 -2
  252. package/scripts/rules/destructive-command.ts +6 -9
  253. package/scripts/rules/direct-filing.ts +3 -7
  254. package/scripts/rules/early-heartbeat.ts +7 -18
  255. package/scripts/rules/file-body.ts +6 -13
  256. package/scripts/rules/filing-cap.ts +4 -6
  257. package/scripts/rules/firing-point.ts +6 -5
  258. package/scripts/rules/git-force.ts +5 -9
  259. package/scripts/rules/guard-remedy.ts +117 -0
  260. package/scripts/rules/implementation-cut.ts +37 -35
  261. package/scripts/rules/index-guard.ts +4 -7
  262. package/scripts/rules/issue-comments.ts +67 -16
  263. package/scripts/rules/josh-git-bare.ts +3 -6
  264. package/scripts/rules/lane-background.ts +10 -21
  265. package/scripts/rules/lane-carry-conflict.ts +7 -17
  266. package/scripts/rules/lane-interactive-ask.ts +6 -11
  267. package/scripts/rules/lane-park.ts +5 -13
  268. package/scripts/rules/lane-split-park.ts +2 -7
  269. package/scripts/rules/lane-switch-main.ts +9 -12
  270. package/scripts/rules/last-prompt.ts +50 -5
  271. package/scripts/rules/oracle-consulted.ts +4 -5
  272. package/scripts/rules/oracle-list-cli.ts +7 -0
  273. package/scripts/rules/piped-verification.ts +84 -32
  274. package/scripts/rules/poll-loop.ts +7 -17
  275. package/scripts/rules/pre-gate-cut.ts +7 -21
  276. package/scripts/rules/prior-comment-read.ts +7 -5
  277. package/scripts/rules/protected-files.ts +5 -9
  278. package/scripts/rules/raw-field-body.ts +3 -7
  279. package/scripts/rules/rule-body-guard.ts +4 -11
  280. package/scripts/rules/rule-list.ts +4 -1
  281. package/scripts/rules/rule-registry-rows.ts +10 -0
  282. package/scripts/rules/rule-registry.ts +7 -4
  283. package/scripts/rules/rule-rewrite.ts +114 -0
  284. package/scripts/rules/rule-value.ts +7 -1
  285. package/scripts/rules/run-tail-rule.ts +6 -22
  286. package/scripts/rules/shell-body-trigger.ts +4 -6
  287. package/scripts/rules/shell-segments.ts +22 -0
  288. package/scripts/rules/stop-decision.ts +77 -0
  289. package/scripts/rules/stop-rules.ts +164 -77
  290. package/scripts/rules/tail-commands.ts +15 -1
  291. package/scripts/rules/test-declared-commit.ts +4 -10
  292. package/scripts/rules/third-party-write.ts +5 -10
  293. package/scripts/rules/worktree-guard.ts +5 -12
  294. package/scripts/run/board/run-board-cli.ts +2 -0
  295. package/scripts/run/board/run-board-closed.ts +8 -2
  296. package/scripts/run/board/run-board-github.ts +19 -3
  297. package/scripts/run/board/run-board-header.ts +4 -1
  298. package/scripts/run/board/run-board-idle.ts +31 -0
  299. package/scripts/run/board/run-board-labels.ts +2 -0
  300. package/scripts/run/board/run-board-layout.ts +1 -1
  301. package/scripts/run/board/run-board-read.ts +25 -12
  302. package/scripts/run/board/run-board-reason.ts +17 -4
  303. package/scripts/run/board/run-board-render-legend.ts +1 -0
  304. package/scripts/run/board/run-board-render-slot.ts +1 -1
  305. package/scripts/run/board/run-board-render-waits.ts +41 -0
  306. package/scripts/run/board/run-board-render.ts +6 -1
  307. package/scripts/run/board/run-board-row-yield.ts +1 -0
  308. package/scripts/run/board/run-board-state.ts +9 -0
  309. package/scripts/run/board/run-board-status.ts +89 -23
  310. package/scripts/run/board/run-board-thanks.ts +7 -2
  311. package/scripts/run/board/run-board-tick.ts +37 -8
  312. package/scripts/run/carry/run-carry-args.ts +1 -3
  313. package/scripts/run/carry/run-carry-conversation.ts +7 -2
  314. package/scripts/run/cut/run-cut-cli.ts +18 -22
  315. package/scripts/run/cut/run-cut-decline.ts +72 -0
  316. package/scripts/run/cut/run-cut-ready.ts +74 -0
  317. package/scripts/run/cut/run-cut-report.ts +29 -0
  318. package/scripts/run/cut/run-cut.ts +10 -1
  319. package/scripts/run/detached-launch.ts +23 -4
  320. package/scripts/run/entry/run-entry-cli.ts +8 -1
  321. package/scripts/run/event/run-event-render.ts +1 -0
  322. package/scripts/run/event/run-event-scope.ts +19 -1
  323. package/scripts/run/event/run-event-stream-emit.ts +13 -6
  324. package/scripts/run/event/run-event-stream.ts +4 -0
  325. package/scripts/run/hold/run-hold-cli.ts +14 -3
  326. package/scripts/run/hold/run-hold-session.ts +46 -0
  327. package/scripts/run/hold/run-hold.ts +13 -4
  328. package/scripts/run/launch-priority.ts +32 -0
  329. package/scripts/run/merge/run-merge-cli.ts +16 -42
  330. package/scripts/run/merge/run-merge-ship.ts +59 -1
  331. package/scripts/run/merge/run-merge-signals.ts +99 -0
  332. package/scripts/run/merge/run-merge-steps.ts +11 -1
  333. package/scripts/run/merge/run-merge.ts +22 -9
  334. package/scripts/run/run-ending.ts +31 -3
  335. package/scripts/run/run-liveness.ts +1 -1
  336. package/scripts/run/run-parent-cut-hook.ts +5 -10
  337. package/scripts/run/run-poll-hook.ts +157 -0
  338. package/scripts/run/run-preflight.ts +1 -2
  339. package/scripts/run/run-step-cli.ts +22 -15
  340. package/scripts/run/run-step-section.ts +85 -0
  341. package/scripts/run/run-stop-notify.ts +1 -2
  342. package/scripts/run/run-watcher-guard.ts +6 -3
  343. package/scripts/run/ship/run-ship-cli.ts +5 -12
  344. package/scripts/run/ship/run-ship-detach.ts +9 -0
  345. package/scripts/run/ship/run-ship-next.ts +10 -2
  346. package/scripts/run/ship/run-ship-overlap.ts +195 -0
  347. package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
  348. package/scripts/run/ship/run-ship-preflight.ts +28 -2
  349. package/scripts/run/ship/run-ship-return.ts +31 -7
  350. package/scripts/run/ship/run-ship-review-steps.ts +25 -14
  351. package/scripts/run/ship/run-ship-review.ts +15 -2
  352. package/scripts/run/ship/run-ship-steps.ts +1 -1
  353. package/scripts/run/ship/run-ship-stop-context.ts +206 -0
  354. package/scripts/run/ship/run-ship-sync.ts +14 -6
  355. package/scripts/run/tidy/run-tidy-cli.ts +3 -3
  356. package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
  357. package/scripts/safe-chain/project-config.ts +7 -2
  358. package/scripts/sync/sync-hook-safety.ts +8 -2
  359. package/scripts/test/test-e2e-guard.ts +30 -5
  360. package/scripts/test/test-unit-guard.ts +16 -1
  361. package/scripts/test/unit-worker-share.ts +26 -3
  362. package/scripts/time-runtime/time-batch-guard.ts +3 -3
  363. package/scripts/time-runtime/time-hook-transcript.ts +3 -2
  364. package/scripts/time-runtime/time-shell.ts +39 -4
  365. package/scripts/time-runtime/time-spans.ts +1 -0
  366. package/scripts/time-runtime/time-tool-call.ts +7 -0
  367. package/scripts/version/effective-upstream.ts +3 -4
  368. package/scripts/version/kit-version-config.ts +8 -2
  369. package/scripts/version/latest-corepack.ts +1 -2
  370. package/scripts/version/release-hold.ts +11 -4
  371. package/scripts/version/version-commands.ts +12 -4
  372. package/templates/gitignore +6 -2
  373. package/dist/hooks/chunk-D5QBTOCZ.js +0 -68
  374. package/dist/hooks/chunk-UT7YC3N2.js +0 -56
package/dist/josh.js CHANGED
@@ -131,20 +131,20 @@ var ABSENT_CODE = "ENOENT";
131
131
  function is_absent(error) {
132
132
  return error instanceof Error && "code" in error && error.code === ABSENT_CODE;
133
133
  }
134
- function read_optional(path12) {
134
+ function read_optional(path16) {
135
135
  try {
136
- return readFileSync(path12, "utf8");
136
+ return readFileSync(path16, "utf8");
137
137
  } catch (error) {
138
138
  if (is_absent(error)) return void 0;
139
139
  throw error;
140
140
  }
141
141
  }
142
- function read_file_or_empty(path12) {
143
- return read_optional(path12) ?? "";
142
+ function read_file_or_empty(path16) {
143
+ return read_optional(path16) ?? "";
144
144
  }
145
- function read_if_readable(path12) {
145
+ function read_if_readable(path16) {
146
146
  try {
147
- return readFileSync(path12, "utf8");
147
+ return readFileSync(path16, "utf8");
148
148
  } catch {
149
149
  return void 0;
150
150
  }
@@ -267,93 +267,6 @@ var doctor_consumer = {
267
267
  secretlint_line
268
268
  };
269
269
 
270
- // scripts/josh/josh-logic.ts
271
- import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
272
- import path11 from "node:path";
273
- import { fileURLToPath as fileURLToPath2 } from "node:url";
274
-
275
- // scripts/build/local-bin.ts
276
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
277
- import { createRequire } from "node:module";
278
- import path3 from "node:path";
279
-
280
- // scripts/lib/ancestor-directories.ts
281
- import path2 from "node:path";
282
- var MAX_DEPTH = 64;
283
- function list(start_directory) {
284
- const directories = [];
285
- let current = path2.resolve(start_directory);
286
- while (directories.length < MAX_DEPTH && !directories.includes(current)) {
287
- directories.push(current);
288
- current = path2.dirname(current);
289
- }
290
- return directories;
291
- }
292
- function nearest(start_directory, is_match) {
293
- return list(start_directory).find((directory) => is_match(directory));
294
- }
295
- var ancestor_directories = { list, nearest };
296
-
297
- // scripts/lib/schemas.ts
298
- import { z as z2 } from "zod";
299
- var overrides_snapshot_schema = z2.record(z2.string(), z2.string());
300
- var package_version_schema = z2.object({ version: z2.string() });
301
- var package_named_version_schema = z2.looseObject({ name: z2.string(), version: z2.string() });
302
- var package_with_version_schema = z2.looseObject({ version: z2.string() });
303
- var ls_global_dependency_schema = z2.looseObject({ version: z2.string() });
304
- var ls_global_dependencies_schema = z2.record(z2.string(), ls_global_dependency_schema).optional();
305
- var pnpm_ls_global_schema = z2.array(
306
- z2.looseObject({
307
- dependencies: ls_global_dependencies_schema
308
- })
309
- );
310
- var vscode_settings_schema = z2.record(z2.string(), z2.unknown());
311
- var with_scripts_schema = z2.looseObject({
312
- scripts: z2.record(z2.string(), z2.string()).optional()
313
- });
314
- var with_development_deps_schema = z2.looseObject({
315
- devDependencies: z2.record(z2.string(), z2.string()).optional()
316
- });
317
- var with_package_manager_schema = z2.looseObject({
318
- packageManager: z2.string().optional()
319
- });
320
- var with_development_engine_schema = z2.looseObject({
321
- devEngines: z2.record(z2.string(), z2.unknown()).optional()
322
- });
323
- var bin_entry_schema = z2.union([z2.string(), z2.record(z2.string(), z2.string())]).optional();
324
- var package_bin_schema = z2.looseObject({ bin: bin_entry_schema });
325
- var json_object_schema = z2.record(z2.string(), z2.unknown());
326
- var string_array_schema = z2.array(z2.string());
327
- var string_record_schema = z2.record(z2.string(), z2.string());
328
-
329
- // scripts/build/local-bin.ts
330
- var NODE_MODULES2 = "node_modules";
331
- var BIN_DIR = ".bin";
332
- var WINDOWS_PLATFORM = "win32";
333
- var WINDOWS_SHIM_SUFFIX = ".cmd";
334
- var PACKAGE_JSON = "package.json";
335
- function resolve_local_bin(project_root, bin_name) {
336
- const shim = process.platform === WINDOWS_PLATFORM ? `${bin_name}${WINDOWS_SHIM_SUFFIX}` : bin_name;
337
- return path3.join(project_root, NODE_MODULES2, BIN_DIR, shim);
338
- }
339
- function read_bin_entry(manifest_path, package_name, bin_name) {
340
- const { bin } = package_bin_schema.parse(JSON.parse(readFileSync2(manifest_path, "utf8")));
341
- if (typeof bin === "string") return bin_name === path3.basename(package_name) ? bin : void 0;
342
- return bin?.[bin_name];
343
- }
344
- function resolve_package_bin(base_directory, package_name, bin_name) {
345
- try {
346
- const resolve_from = createRequire(path3.join(base_directory, PACKAGE_JSON));
347
- const manifest_path = resolve_from.resolve(`${package_name}/${PACKAGE_JSON}`);
348
- const bin_entry = read_bin_entry(manifest_path, package_name, bin_name);
349
- if (bin_entry === void 0) return void 0;
350
- const cli_entry = path3.join(path3.dirname(manifest_path), bin_entry);
351
- return existsSync2(cli_entry) ? cli_entry : void 0;
352
- } catch {
353
- return void 0;
354
- }
355
- }
356
-
357
270
  // scripts/gate/core-budget.ts
358
271
  import { randomUUID as randomUUID3 } from "node:crypto";
359
272
  import { readdirSync as readdirSync2 } from "node:fs";
@@ -364,7 +277,7 @@ import path8 from "node:path";
364
277
  import { accessSync, constants } from "node:fs";
365
278
  import { tmpdir } from "node:os";
366
279
  var POSIX_TEMP_ROOT = "/tmp";
367
- var WINDOWS_PLATFORM2 = "win32";
280
+ var WINDOWS_PLATFORM = "win32";
368
281
  function is_writable_directory(directory) {
369
282
  try {
370
283
  accessSync(directory, constants.W_OK);
@@ -374,7 +287,7 @@ function is_writable_directory(directory) {
374
287
  }
375
288
  }
376
289
  function resolve_temporary_root(platform2, can_write = is_writable_directory) {
377
- if (platform2 === WINDOWS_PLATFORM2) return tmpdir();
290
+ if (platform2 === WINDOWS_PLATFORM) return tmpdir();
378
291
  return can_write(POSIX_TEMP_ROOT) ? POSIX_TEMP_ROOT : tmpdir();
379
292
  }
380
293
  var TEMP_ROOT_KEY = "JOSH_TEMP_ROOT";
@@ -386,9 +299,9 @@ var PLATFORM_TEMP_ROOT = temporary_root(process.env[TEMP_ROOT_KEY], process.plat
386
299
  // scripts/josh/process-identity.ts
387
300
  import { execFileSync, spawnSync } from "node:child_process";
388
301
  import { randomUUID } from "node:crypto";
389
- import { readFileSync as readFileSync3, rmSync } from "node:fs";
302
+ import { readFileSync as readFileSync2, rmSync } from "node:fs";
390
303
  import { createServer } from "node:net";
391
- import path4 from "node:path";
304
+ import path2 from "node:path";
392
305
  var PS_COMMAND = "/bin/ps";
393
306
  var FORMAT_FLAG = "-o";
394
307
  var START_TIME_FORMAT = "lstart=";
@@ -403,7 +316,7 @@ var BEACON_SUFFIX = ".sock";
403
316
  var BEACON_PREFIX = `josh-process-identity-${String(process.getuid?.() ?? "")}-`;
404
317
  var BEACON_ID_LENGTH = 36;
405
318
  var BEACON_ID_PATTERN = /^[\da-f-]+$/u;
406
- var WINDOWS_PLATFORM3 = "win32";
319
+ var WINDOWS_PLATFORM2 = "win32";
407
320
  var BEACON_LIVE_STATUS = 0;
408
321
  var BEACON_UNKNOWN_STATUS = 1;
409
322
  var BEACON_GONE_STATUS = 2;
@@ -426,7 +339,7 @@ function is_present_pid(pid) {
426
339
  }
427
340
  function read_proc_start(pid) {
428
341
  try {
429
- const stat = readFileSync3(`/proc/${String(pid)}/stat`, "utf8");
342
+ const stat = readFileSync2(`/proc/${String(pid)}/stat`, "utf8");
430
343
  const fields = stat.slice(stat.lastIndexOf(")") + 1).trim().split(/\s+/u);
431
344
  const start = fields[PROC_START_FIELD_INDEX];
432
345
  return start === void 0 ? void 0 : `proc:${start}`;
@@ -491,7 +404,7 @@ function try_bind_beacon(bind, target) {
491
404
  }
492
405
  }
493
406
  function open_beacon(bind = bind_beacon) {
494
- const target = path4.join(PLATFORM_TEMP_ROOT, `${BEACON_PREFIX}${randomUUID()}${BEACON_SUFFIX}`);
407
+ const target = path2.join(PLATFORM_TEMP_ROOT, `${BEACON_PREFIX}${randomUUID()}${BEACON_SUFFIX}`);
495
408
  const token = `${BEACON_SCHEME}${target}`;
496
409
  const server = try_bind_beacon(bind, target);
497
410
  if (server === void 0) return void 0;
@@ -502,7 +415,7 @@ function open_beacon(bind = bind_beacon) {
502
415
  }
503
416
  function resolve_own_start(probes = START_PROBES, platform2 = process.platform) {
504
417
  const start = read_start(process.pid, probes);
505
- if (start !== void 0 || platform2 === WINDOWS_PLATFORM3) return start;
418
+ if (start !== void 0 || platform2 === WINDOWS_PLATFORM2) return start;
506
419
  return open_beacon();
507
420
  }
508
421
  function is_valid_beacon_name(name) {
@@ -513,8 +426,8 @@ function is_valid_beacon_name(name) {
513
426
  function beacon_target(token) {
514
427
  if (!token.startsWith(BEACON_SCHEME)) return void 0;
515
428
  const target = token.slice(BEACON_SCHEME.length);
516
- if (path4.dirname(target) !== PLATFORM_TEMP_ROOT) return void 0;
517
- return is_valid_beacon_name(path4.basename(target)) ? target : void 0;
429
+ if (path2.dirname(target) !== PLATFORM_TEMP_ROOT) return void 0;
430
+ return is_valid_beacon_name(path2.basename(target)) ? target : void 0;
518
431
  }
519
432
  function run_socket_probe(target) {
520
433
  return spawnSync(process.execPath, ["-e", SOCKET_PROBE_SOURCE, target], {
@@ -573,18 +486,18 @@ var process_identity = {
573
486
  };
574
487
 
575
488
  // scripts/josh/process-owner.ts
576
- import { z as z3 } from "zod";
577
- var process_owner_schema = z3.object({ pid: z3.number(), process_start: z3.string().optional() });
489
+ import { z as z2 } from "zod";
490
+ var process_owner_schema = z2.object({ pid: z2.number(), process_start: z2.string().optional() });
578
491
 
579
492
  // scripts/josh/stamp-file.ts
580
493
  import { createHash, randomUUID as randomUUID2 } from "node:crypto";
581
- import { lstatSync, readFileSync as readFileSync4, renameSync, rmSync as rmSync2, writeFileSync } from "node:fs";
582
- import path6 from "node:path";
494
+ import { lstatSync, readFileSync as readFileSync3, renameSync, rmSync as rmSync2, writeFileSync } from "node:fs";
495
+ import path4 from "node:path";
583
496
 
584
497
  // scripts/init/init-paths.ts
585
- import path5 from "node:path";
498
+ import path3 from "node:path";
586
499
  import { fileURLToPath } from "node:url";
587
- var PACKAGE_DIR = path5.join(path5.dirname(fileURLToPath(import.meta.url)), "..", "..");
500
+ var PACKAGE_DIR = path3.join(path3.dirname(fileURLToPath(import.meta.url)), "..", "..");
588
501
  var PROJECT_ROOT = process.cwd();
589
502
 
590
503
  // scripts/josh/stamp-file.ts
@@ -604,7 +517,7 @@ function account_key() {
604
517
  }
605
518
  function stamp_path(prefix, root = PACKAGE_DIR, suffix = STAMP_SUFFIX) {
606
519
  const key = digest(`${account_key()}${ACCOUNT_KEY_SEPARATOR}${root}`).slice(0, STAMP_KEY_LENGTH);
607
- return path6.join(PLATFORM_TEMP_ROOT, `${prefix}${key}${suffix}`);
520
+ return path4.join(PLATFORM_TEMP_ROOT, `${prefix}${key}${suffix}`);
608
521
  }
609
522
  function write_exclusively(target, content) {
610
523
  rmSync2(target, { force: true });
@@ -664,7 +577,7 @@ function remove_stamp(target) {
664
577
  function read_stamp_text(source) {
665
578
  try {
666
579
  if (!is_own_regular_file(source)) return void 0;
667
- return readFileSync4(source, "utf8");
580
+ return readFileSync3(source, "utf8");
668
581
  } catch {
669
582
  return void 0;
670
583
  }
@@ -685,7 +598,7 @@ var stamp_file = {
685
598
  // scripts/test/unit-worker-share.ts
686
599
  import { readdirSync } from "node:fs";
687
600
  import { availableParallelism } from "node:os";
688
- import path7 from "node:path";
601
+ import path5 from "node:path";
689
602
  var RUN_PREFIX = "josh-unit-running-";
690
603
  var WORKER_FLAG = "--maxWorkers";
691
604
  var WORKER_FLAGS = [WORKER_FLAG, "--max-workers"];
@@ -724,7 +637,7 @@ function is_marker_live(source) {
724
637
  return false;
725
638
  }
726
639
  function live_run_count(directory = PLATFORM_TEMP_ROOT) {
727
- return marker_files(directory).filter((name) => is_marker_live(path7.join(directory, name))).length;
640
+ return marker_files(directory).filter((name) => is_marker_live(path5.join(directory, name))).length;
728
641
  }
729
642
  var NESTED_KEY = "JOSH_UNIT_RUN_MARKED";
730
643
  var NESTED_VALUE = "1";
@@ -735,16 +648,23 @@ function resolve_unit_workers(available_cores, runs) {
735
648
  if (runs <= SOLO_RUNS) return void 0;
736
649
  return Math.max(MIN_WORKERS, Math.floor(available_cores / runs));
737
650
  }
738
- function current_share(available_cores = availableParallelism(), live_runs = live_run_count()) {
651
+ function cap_to_free(workers, free_cores, available_cores) {
652
+ if (free_cores === void 0 || free_cores >= (workers ?? available_cores)) return workers;
653
+ return Math.max(MIN_WORKERS, Math.floor(free_cores));
654
+ }
655
+ function current_share(available_cores = availableParallelism(), live_runs = live_run_count(), free_cores) {
739
656
  const own_run = is_nested_run() ? 0 : SOLO_RUNS;
740
- return resolve_unit_workers(available_cores, live_runs + own_run);
657
+ const share = resolve_unit_workers(available_cores, live_runs + own_run);
658
+ return cap_to_free(share, free_cores, available_cores);
741
659
  }
742
660
  function is_worker_flag(argument) {
743
661
  return WORKER_FLAGS.some((flag) => argument === flag || argument.startsWith(`${flag}=`));
744
662
  }
663
+ function has_worker_flag(vitest_arguments) {
664
+ return vitest_arguments.some((argument) => is_worker_flag(argument));
665
+ }
745
666
  function worker_arguments(vitest_arguments, share) {
746
- if (share === void 0) return [];
747
- if (vitest_arguments.some((argument) => is_worker_flag(argument))) return [];
667
+ if (share === void 0 || has_worker_flag(vitest_arguments)) return [];
748
668
  return [`${WORKER_FLAG}=${String(share)}`];
749
669
  }
750
670
  function mark_run_started(target) {
@@ -771,7 +691,9 @@ var unit_worker_share = {
771
691
  NESTED_VALUE,
772
692
  RUN_PREFIX,
773
693
  SOLO_RUNS,
694
+ cap_to_free,
774
695
  current_share,
696
+ has_worker_flag,
775
697
  is_nested_run,
776
698
  live_run_count,
777
699
  marker_path,
@@ -782,7 +704,7 @@ var unit_worker_share = {
782
704
  };
783
705
 
784
706
  // scripts/gate/core-budget.ts
785
- import { z as z4 } from "zod";
707
+ import { z as z3 } from "zod";
786
708
 
787
709
  // scripts/gate/core-admission.ts
788
710
  var MS_PER_SECOND = 1e3;
@@ -855,6 +777,151 @@ var core_admission = {
855
777
  sum_load
856
778
  };
857
779
 
780
+ // scripts/gate/core-wait.ts
781
+ import path7 from "node:path";
782
+
783
+ // scripts/lane/lane-paths.ts
784
+ import path6 from "node:path";
785
+
786
+ // scripts/issue/issue-number-shape.ts
787
+ var ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/u;
788
+ var BAD_ISSUE_MESSAGE = "Not an issue number: ";
789
+ function is_issue_number(value) {
790
+ return ISSUE_NUMBER_PATTERN.test(value);
791
+ }
792
+ function require_issue_number(issue) {
793
+ if (!is_issue_number(issue)) throw new Error(`${BAD_ISSUE_MESSAGE}${issue}`);
794
+ }
795
+ var issue_number_shape = { ISSUE_NUMBER_PATTERN, is_issue_number, require_issue_number };
796
+
797
+ // scripts/lane/lane-paths.ts
798
+ var LANE_ROOT_KEY = "JOSH_LANE_ROOT";
799
+ var LANE_DIRECTORY_SUFFIX = "-lanes";
800
+ var LANE_BRANCH_SUFFIX = "-lane";
801
+ function default_lane_root(repository_root) {
802
+ const name = path6.basename(repository_root);
803
+ return path6.join(path6.dirname(repository_root), `.${name}${LANE_DIRECTORY_SUFFIX}`);
804
+ }
805
+ function lane_root(repository_root, environment = process.env) {
806
+ const configured = environment[LANE_ROOT_KEY]?.trim() ?? "";
807
+ return configured.length === 0 ? default_lane_root(repository_root) : path6.resolve(configured);
808
+ }
809
+ function lane_directory(root, issue) {
810
+ return path6.join(root, issue);
811
+ }
812
+ var DEFAULT_LANE_ROOT_PATTERN = new RegExp(String.raw`^\..+${LANE_DIRECTORY_SUFFIX}$`, "u");
813
+ function is_lane_root(directory, environment) {
814
+ const configured = environment[LANE_ROOT_KEY]?.trim() ?? "";
815
+ if (configured.length > 0) return path6.resolve(configured) === directory;
816
+ return DEFAULT_LANE_ROOT_PATTERN.test(path6.basename(directory));
817
+ }
818
+ function lane_issue_of(directory, environment = process.env) {
819
+ const resolved = path6.resolve(directory);
820
+ const issue = path6.basename(resolved);
821
+ if (!issue_number_shape.ISSUE_NUMBER_PATTERN.test(issue)) return void 0;
822
+ return is_lane_root(path6.dirname(resolved), environment) ? issue : void 0;
823
+ }
824
+ function lane_branch(issue) {
825
+ return `${issue}${LANE_BRANCH_SUFFIX}`;
826
+ }
827
+ var SEAT_LOCK_DIR = ".seat-locks";
828
+ function is_lane_entry(name) {
829
+ return issue_number_shape.ISSUE_NUMBER_PATTERN.test(name) || DEFAULT_LANE_ROOT_PATTERN.test(name);
830
+ }
831
+ var lane_paths = {
832
+ LANE_BRANCH_SUFFIX,
833
+ LANE_ROOT_KEY,
834
+ SEAT_LOCK_DIR,
835
+ default_lane_root,
836
+ is_lane_entry,
837
+ lane_branch,
838
+ lane_directory,
839
+ lane_issue_of,
840
+ lane_root
841
+ };
842
+
843
+ // scripts/gate/core-wait.ts
844
+ var WAIT_REASONS = ["cores", "memory", "pressure"];
845
+ var REASON_TEXT = {
846
+ cores: "not enough free cores",
847
+ memory: "not enough free memory",
848
+ pressure: "memory under pressure"
849
+ };
850
+ function wait_reason(key, reservations, budget) {
851
+ const cores_only = { cores: budget.cores, memory_mb: Infinity };
852
+ if (!core_admission.is_admitted(key, reservations, cores_only)) return "cores";
853
+ return budget.memory_pressured === true ? "pressure" : "memory";
854
+ }
855
+ function wait_line(command, reason) {
856
+ return `\u23F3 ${command ?? "a step"} waits for the core budget \u2014 ${REASON_TEXT[reason]}`;
857
+ }
858
+ function lanes_root_of(checkout) {
859
+ if (lane_paths.lane_issue_of(checkout) === void 0) return lane_paths.lane_root(checkout);
860
+ return path7.dirname(path7.resolve(checkout));
861
+ }
862
+ function where_of(checkout) {
863
+ return { checkout, lane: lane_paths.lane_issue_of(checkout), lanes_root: lanes_root_of(checkout) };
864
+ }
865
+ function place_of(marker, home_root) {
866
+ const { checkout, lane, lanes_root } = marker;
867
+ if (checkout === void 0 || lanes_root === void 0 || lanes_root === home_root) {
868
+ return { lane, elsewhere: void 0 };
869
+ }
870
+ const name = lane === void 0 ? path7.basename(checkout) : `${path7.basename(lanes_root)}/${lane}`;
871
+ return { lane: void 0, elsewhere: name };
872
+ }
873
+ function wait_of(marker, now, home_root) {
874
+ const { command, wait_reason: reason } = marker;
875
+ if (reason === void 0 || marker.admitted_at !== void 0) return [];
876
+ return [{ command, ...place_of(marker, home_root), waited_ms: now - marker.claimed_at, reason }];
877
+ }
878
+ function waits_of(entries, now, home) {
879
+ const home_root = lanes_root_of(home);
880
+ return entries.flatMap((entry) => wait_of(entry.reservation, now, home_root)).toSorted((left, right) => right.waited_ms - left.waited_ms);
881
+ }
882
+ var core_wait = {
883
+ REASON_TEXT,
884
+ WAIT_REASONS,
885
+ wait_line,
886
+ wait_reason,
887
+ waits_of,
888
+ where_of
889
+ };
890
+
891
+ // scripts/gate/held-mark.ts
892
+ var HELD_KEY = "JOSH_CORE_RESERVED";
893
+ var HELD_VALUE = "1";
894
+ var HELD_RESERVATION_KEY = "JOSH_CORE_RESERVATION";
895
+ function is_held() {
896
+ return process.env[HELD_KEY] === HELD_VALUE;
897
+ }
898
+ function held_reservation() {
899
+ const key = process.env[HELD_RESERVATION_KEY] ?? "";
900
+ return key === "" ? void 0 : key;
901
+ }
902
+ function set_held_mark(value, reservation) {
903
+ process.env[HELD_KEY] = value;
904
+ process.env[HELD_RESERVATION_KEY] = reservation;
905
+ }
906
+ async function with_held_mark(run2, reservation = "") {
907
+ const previous = process.env[HELD_KEY] ?? "";
908
+ const previous_reservation = process.env[HELD_RESERVATION_KEY] ?? "";
909
+ set_held_mark(HELD_VALUE, reservation);
910
+ try {
911
+ return await run2();
912
+ } finally {
913
+ set_held_mark(previous, previous_reservation);
914
+ }
915
+ }
916
+ var held_mark = {
917
+ HELD_KEY,
918
+ HELD_RESERVATION_KEY,
919
+ HELD_VALUE,
920
+ held_reservation,
921
+ is_held,
922
+ with_held_mark
923
+ };
924
+
858
925
  // scripts/gate/machine-capacity.ts
859
926
  import { readFile } from "node:fs/promises";
860
927
  import { availableParallelism as availableParallelism2, cpus, totalmem } from "node:os";
@@ -873,9 +940,20 @@ var PAGE_SIZE_ARGUMENTS = ["PAGESIZE"];
873
940
  var LEVEL_NAME = "kern.memorystatus_level";
874
941
  var SWAP_IN_NAME = "vm.compressor.swapper.swapins_total";
875
942
  var SWAP_OUT_NAME = "vm.compressor.swapper.swapouts_total";
943
+ var COMPRESSED_NAME = "vm.compressor_input_bytes";
876
944
  var PAGE_SIZE_NAME = "hw.pagesize";
877
945
  var PRESSURE_NAME = "kern.memorystatus_vm_pressure_level";
878
- var SYSCTL_ARGUMENTS = [LEVEL_NAME, SWAP_IN_NAME, SWAP_OUT_NAME, PAGE_SIZE_NAME, PRESSURE_NAME];
946
+ var PRESSURE_WARNING = 2;
947
+ var SWAP_PRESSURE_MB = 16;
948
+ var COMPRESSION_PRESSURE_MB = 64;
949
+ var SYSCTL_ARGUMENTS = [
950
+ LEVEL_NAME,
951
+ SWAP_IN_NAME,
952
+ SWAP_OUT_NAME,
953
+ COMPRESSED_NAME,
954
+ PAGE_SIZE_NAME,
955
+ PRESSURE_NAME
956
+ ];
879
957
  var SYSCTL_LINE = /^(?<name>[\w.]+): (?<value>\d+)$/gmu;
880
958
  function cpu_times() {
881
959
  let busy = 0;
@@ -904,9 +982,9 @@ function sysctl_values(output) {
904
982
  function swapped_pages(pages_in, pages_out) {
905
983
  return pages_in === void 0 || pages_out === void 0 ? void 0 : pages_in + pages_out;
906
984
  }
907
- function swapped_mb(pages, page_bytes) {
908
- if (pages === void 0 || page_bytes === void 0) return void 0;
909
- return pages * page_bytes / BYTES_PER_MB;
985
+ function mb_of(units, unit_bytes) {
986
+ if (units === void 0 || unit_bytes === void 0) return void 0;
987
+ return units * unit_bytes / BYTES_PER_MB;
910
988
  }
911
989
  function swapped_between(from, to) {
912
990
  if (from === void 0 || to === void 0) return void 0;
@@ -917,11 +995,12 @@ function parse_darwin_memory(output, total_bytes) {
917
995
  const level = values.get(LEVEL_NAME);
918
996
  return {
919
997
  available_mb: level === void 0 ? void 0 : level / PERCENT * (total_bytes / BYTES_PER_MB),
920
- swapped_mb: swapped_mb(
998
+ swapped_mb: mb_of(
921
999
  swapped_pages(values.get(SWAP_IN_NAME), values.get(SWAP_OUT_NAME)),
922
1000
  values.get(PAGE_SIZE_NAME)
923
1001
  ),
924
- pressure_level: values.get(PRESSURE_NAME)
1002
+ pressure_level: values.get(PRESSURE_NAME),
1003
+ compressed_mb: mb_of(values.get(COMPRESSED_NAME), 1)
925
1004
  };
926
1005
  }
927
1006
  function parse_linux_available(meminfo) {
@@ -934,7 +1013,7 @@ function pages_of(pattern, vmstat) {
934
1013
  }
935
1014
  function parse_linux_swapped(vmstat, page_bytes) {
936
1015
  const pages = swapped_pages(pages_of(LINUX_SWAP_IN, vmstat), pages_of(LINUX_SWAP_OUT, vmstat));
937
- return swapped_mb(pages, page_bytes);
1016
+ return mb_of(pages, page_bytes);
938
1017
  }
939
1018
  async function settled(read2) {
940
1019
  try {
@@ -984,11 +1063,26 @@ async function default_sleep(ms) {
984
1063
  setTimeout(resolve, ms);
985
1064
  });
986
1065
  }
1066
+ function moved_over(from, to, limit) {
1067
+ return (swapped_between(from, to) ?? 0) > limit;
1068
+ }
1069
+ function is_pressured(before, after) {
1070
+ return (after.pressure_level ?? 0) >= PRESSURE_WARNING || moved_over(before.swapped_mb, after.swapped_mb, SWAP_PRESSURE_MB) || moved_over(before.compressed_mb, after.compressed_mb, COMPRESSION_PRESSURE_MB);
1071
+ }
1072
+ function pressured_available(before, after) {
1073
+ if (after.available_mb === void 0) return void 0;
1074
+ return is_pressured(before, after) ? 0 : after.available_mb;
1075
+ }
987
1076
  async function read_machine(sleep = default_sleep) {
988
1077
  const before = cpu_times();
989
- const [memory] = await Promise.all([read_memory(), sleep(SAMPLE_WINDOW_MS)]);
1078
+ const [memory_before] = await Promise.all([read_memory(), sleep(SAMPLE_WINDOW_MS)]);
1079
+ const memory_after = await read_memory();
990
1080
  const busy_cores = is_unquoted() ? busy_cores_between(before, cpu_times(), cpus().length) : void 0;
991
- return { busy_cores, available_mb: memory.available_mb };
1081
+ return {
1082
+ busy_cores,
1083
+ available_mb: pressured_available(memory_before, memory_after),
1084
+ memory_pressured: is_pressured(memory_before, memory_after)
1085
+ };
992
1086
  }
993
1087
  async function read_sample() {
994
1088
  return { cpu: cpu_times(), memory: await read_memory(), total_mb: totalmem() / BYTES_PER_MB };
@@ -1000,21 +1094,25 @@ function external_cores(busy_cores, ledger_cores) {
1000
1094
  function machine_budget(cores, reading, ledger) {
1001
1095
  return {
1002
1096
  cores: cores - external_cores(reading.busy_cores, ledger.cores),
1003
- memory_mb: reading.available_mb === void 0 ? Infinity : reading.available_mb + ledger.memory_mb
1097
+ memory_mb: reading.available_mb === void 0 ? Infinity : reading.available_mb + ledger.memory_mb,
1098
+ memory_pressured: reading.memory_pressured
1004
1099
  };
1005
1100
  }
1006
1101
  var machine_capacity = {
1007
1102
  BASELINE_CORES,
1008
1103
  busy_cores_between,
1009
1104
  busy_share_between,
1105
+ COMPRESSION_PRESSURE_MB,
1010
1106
  external_cores,
1011
1107
  machine_budget,
1012
1108
  parse_darwin_memory,
1013
1109
  parse_linux_available,
1014
1110
  parse_linux_swapped,
1111
+ pressured_available,
1015
1112
  read_machine,
1016
1113
  read_memory,
1017
1114
  read_sample,
1115
+ SWAP_PRESSURE_MB,
1018
1116
  swapped_between
1019
1117
  };
1020
1118
 
@@ -1028,22 +1126,32 @@ var CORE_WEIGHTS = {
1028
1126
  lint: 2,
1029
1127
  type_check: 1,
1030
1128
  spell_check: 1,
1031
- eslint_scan: 1
1129
+ eslint_scan: 1,
1130
+ format: 1,
1131
+ retrospective: 1,
1132
+ install: 1,
1133
+ e2e: 2
1032
1134
  };
1033
1135
  var MEMORY_MB = {
1034
1136
  lint: 2400,
1035
1137
  type_check: 1300,
1036
1138
  spell_check: 350,
1037
1139
  eslint_scan: 2400,
1038
- unit_worker: 350
1140
+ unit_worker: 350,
1141
+ format: 300,
1142
+ retrospective: 900,
1143
+ install: 100
1039
1144
  };
1040
- var HELD_KEY = "JOSH_CORE_RESERVED";
1041
- var HELD_VALUE = "1";
1042
1145
  var reservation_schema = process_owner_schema.extend({
1043
- weight: z4.number(),
1044
- claimed_at: z4.number(),
1045
- memory_mb: z4.number().optional(),
1046
- admitted_at: z4.number().optional()
1146
+ weight: z3.number(),
1147
+ claimed_at: z3.number(),
1148
+ memory_mb: z3.number().optional(),
1149
+ admitted_at: z3.number().optional(),
1150
+ command: z3.string().optional(),
1151
+ checkout: z3.string().optional(),
1152
+ lane: z3.string().optional(),
1153
+ lanes_root: z3.string().optional(),
1154
+ wait_reason: z3.enum(core_wait.WAIT_REASONS).optional()
1047
1155
  });
1048
1156
  function marker_name(key) {
1049
1157
  return `${RESERVED_PREFIX}${key}${RESERVED_SUFFIX}`;
@@ -1095,6 +1203,10 @@ async function default_sleep2(ms) {
1095
1203
  function write_reservation(target, fields) {
1096
1204
  stamp_file.replace_stamp(target, { ...process_identity.own_fields(), ...fields });
1097
1205
  }
1206
+ function report_wait(line) {
1207
+ process.stderr.write(`${line}
1208
+ `);
1209
+ }
1098
1210
  function resolve_timing(options) {
1099
1211
  return {
1100
1212
  now: options.now ?? Date.now,
@@ -1121,6 +1233,13 @@ function overflow_of(context, reservations, budget) {
1121
1233
  budget
1122
1234
  };
1123
1235
  }
1236
+ function mark_waiting(context, reason) {
1237
+ const previous = context.fields.wait_reason;
1238
+ if (previous === reason) return;
1239
+ context.fields = { ...context.fields, wait_reason: reason };
1240
+ write_reservation(context.target, context.fields);
1241
+ if (previous === void 0) context.report(core_wait.wait_line(context.fields.command, reason));
1242
+ }
1124
1243
  async function poll_admission(context) {
1125
1244
  const reservations = live_reservations(context.directory);
1126
1245
  const budget = await current_budget(context, reservations);
@@ -1128,6 +1247,7 @@ async function poll_admission(context) {
1128
1247
  if (context.now() >= context.deadline) {
1129
1248
  return admit(context, overflow_of(context, reservations, budget));
1130
1249
  }
1250
+ mark_waiting(context, core_wait.wait_reason(context.key, reservations, budget));
1131
1251
  return void 0;
1132
1252
  }
1133
1253
  async function await_admission(context) {
@@ -1138,13 +1258,23 @@ async function await_admission(context) {
1138
1258
  }
1139
1259
  return admission;
1140
1260
  }
1261
+ function resolve_ports(options) {
1262
+ return {
1263
+ read_machine: options.read_machine ?? machine_capacity.read_machine,
1264
+ report: options.report ?? report_wait
1265
+ };
1266
+ }
1267
+ function claim_fields(weight, claimed_at, options) {
1268
+ const { memory_mb, command } = options;
1269
+ return { weight, claimed_at, memory_mb, command, ...core_wait.where_of(process.cwd()) };
1270
+ }
1141
1271
  async function reserve(weight, options = {}) {
1142
1272
  const timing = resolve_timing(options);
1143
1273
  const directory = options.directory ?? PLATFORM_TEMP_ROOT;
1144
1274
  const claimed_at = timing.now();
1145
1275
  const name = marker_name(randomUUID3());
1146
1276
  const target = path8.join(directory, name);
1147
- const fields = { weight, claimed_at, memory_mb: options.memory_mb };
1277
+ const fields = claim_fields(weight, claimed_at, options);
1148
1278
  write_reservation(target, fields);
1149
1279
  const admission = await await_admission({
1150
1280
  ...timing,
@@ -1154,92 +1284,134 @@ async function reserve(weight, options = {}) {
1154
1284
  budget: normalize_budget(options.budget ?? availableParallelism3()),
1155
1285
  deadline: claimed_at + timing.wait_cap_ms,
1156
1286
  fields,
1157
- read_machine: options.read_machine ?? machine_capacity.read_machine
1287
+ ...resolve_ports(options)
1158
1288
  });
1159
1289
  return { target, admission };
1160
1290
  }
1291
+ function budget_waits(directory = PLATFORM_TEMP_ROOT, now = Date.now(), home = process.cwd()) {
1292
+ return core_wait.waits_of(live_reservations(directory), now, home);
1293
+ }
1161
1294
  function release(handle) {
1162
1295
  sweep_marker2(handle.target);
1163
1296
  }
1164
1297
  async function with_core_reservation(weight, run2, options = {}) {
1165
1298
  const handle = await reserve(weight, options);
1166
1299
  try {
1167
- return await run2(handle.admission);
1300
+ return await run2(handle.admission, path8.basename(handle.target));
1168
1301
  } finally {
1169
1302
  release(handle);
1170
1303
  }
1171
1304
  }
1172
- function is_held() {
1173
- return process.env[HELD_KEY] === HELD_VALUE;
1174
- }
1175
- function set_held_mark(value) {
1176
- process.env[HELD_KEY] = value;
1177
- }
1178
- async function with_held_mark(run2) {
1179
- const previous = process.env[HELD_KEY] ?? "";
1180
- set_held_mark(HELD_VALUE);
1181
- try {
1182
- return await run2();
1183
- } finally {
1184
- set_held_mark(previous);
1185
- }
1186
- }
1187
1305
  async function with_command_reservation(weight, run2, options = {}) {
1188
- if (is_held() || unit_worker_share.is_nested_run()) return await run2();
1189
- return await with_core_reservation(weight, async () => await with_held_mark(run2), options);
1306
+ if (held_mark.is_held() || unit_worker_share.is_nested_run()) return await run2();
1307
+ return await with_core_reservation(
1308
+ weight,
1309
+ async (_admission, key) => await held_mark.with_held_mark(run2, key),
1310
+ options
1311
+ );
1190
1312
  }
1191
1313
  var core_budget = {
1192
1314
  CORE_WEIGHTS,
1193
- HELD_KEY,
1194
- HELD_VALUE,
1315
+ HELD_KEY: held_mark.HELD_KEY,
1316
+ HELD_VALUE: held_mark.HELD_VALUE,
1195
1317
  MEMORY_MB,
1196
1318
  RESERVED_PREFIX,
1197
1319
  admitted_keys: core_admission.admitted_keys,
1198
1320
  admitted_load: core_admission.admitted_load,
1321
+ budget_waits,
1199
1322
  describe_overflow: core_admission.describe_overflow,
1200
- is_held,
1323
+ is_held: held_mark.is_held,
1201
1324
  live_reservations,
1202
1325
  release,
1203
1326
  reserve,
1204
1327
  with_command_reservation,
1205
1328
  with_core_reservation,
1206
- with_held_mark
1329
+ with_held_mark: held_mark.with_held_mark
1207
1330
  };
1208
1331
 
1209
- // scripts/josh/josh-logic.ts
1210
- import { execaSync as execaSync2 } from "execa";
1211
-
1212
- // scripts/josh/command-suggest.ts
1213
- var MAX_SUGGESTION_DISTANCE = 3;
1214
- function at(values, index) {
1215
- return values[index] ?? 0;
1332
+ // ports/index.js
1333
+ import { existsSync as existsSync2 } from "node:fs";
1334
+ import path9 from "node:path";
1335
+ var PORT_SEED_KEY = "PORT_SEED";
1336
+ var LANE_SEAT_KEY = "JOSH_LANE_SEAT";
1337
+ var ENV_FILE_NAME = ".env";
1338
+ var PACKAGE_FILE_NAME = "package.json";
1339
+ var PROJECT_ENVIRONMENT_KEYS = /* @__PURE__ */ new Set([PORT_SEED_KEY, LANE_SEAT_KEY, "PLAYWRIGHT_REUSE_SERVER"]);
1340
+ var DEV_PORT_BASE = 5173;
1341
+ var PREVIEW_PORT_BASE = 4173;
1342
+ var MIN_SEED = 0;
1343
+ var SEED_LANE_MULTIPLIER = 10;
1344
+ var MAX_LANE = 9;
1345
+ var MAX_SEED = 99;
1346
+ var INTEGER_PATTERN = /^\d+$/u;
1347
+ function fail_invalid(key, raw, max) {
1348
+ throw new Error(
1349
+ `${key} must be an integer between ${MIN_SEED} and ${max}, got ${JSON.stringify(raw)}. Unset it to use the default ports (dev ${DEV_PORT_BASE}, preview ${PREVIEW_PORT_BASE}).`
1350
+ );
1216
1351
  }
1217
- function fill_distance_row(source, target, row, previous) {
1218
- const current = [row];
1219
- for (let col = 1; col <= target.length; col++) {
1220
- const cost = source[row - 1] === target[col - 1] ? 0 : 1;
1221
- const deletion = at(previous, col) + 1;
1222
- const insertion = at(current, col - 1) + 1;
1223
- const substitution = at(previous, col - 1) + cost;
1224
- current.push(Math.min(deletion, insertion, substitution));
1352
+ function read_trimmed(environment, key) {
1353
+ const raw = environment[key];
1354
+ return raw === void 0 ? "" : raw.trim();
1355
+ }
1356
+ function resolve_bounded(environment, key, max) {
1357
+ const trimmed = read_trimmed(environment, key);
1358
+ if (trimmed.length === 0) return MIN_SEED;
1359
+ if (!INTEGER_PATTERN.test(trimmed)) fail_invalid(key, trimmed, max);
1360
+ const value = Number(trimmed);
1361
+ if (value > max) fail_invalid(key, trimmed, max);
1362
+ return value;
1363
+ }
1364
+ function resolve_seed(environment = process.env) {
1365
+ return resolve_bounded(environment, PORT_SEED_KEY, MAX_SEED);
1366
+ }
1367
+ function resolve_lane(environment = process.env) {
1368
+ return resolve_bounded(environment, LANE_SEAT_KEY, MAX_LANE);
1369
+ }
1370
+ function resolve_offset(environment = process.env) {
1371
+ return resolve_seed(environment) * SEED_LANE_MULTIPLIER + resolve_lane(environment);
1372
+ }
1373
+ function offset_from(base, environment) {
1374
+ return base + resolve_offset(environment);
1375
+ }
1376
+ function resolve_development_port(environment = process.env) {
1377
+ return offset_from(DEV_PORT_BASE, environment);
1378
+ }
1379
+ function resolve_preview_port(environment = process.env) {
1380
+ return offset_from(PREVIEW_PORT_BASE, environment);
1381
+ }
1382
+ function resolve_project_directory(directory) {
1383
+ const start = path9.resolve(directory);
1384
+ let current = start;
1385
+ while (!existsSync2(path9.join(current, PACKAGE_FILE_NAME))) {
1386
+ const parent = path9.dirname(current);
1387
+ if (parent === current) return start;
1388
+ current = parent;
1225
1389
  }
1226
1390
  return current;
1227
1391
  }
1228
- function edit_distance(source, target) {
1229
- let previous = Array.from({ length: target.length + 1 }, (_unused, index) => index);
1230
- for (let row = 1; row <= source.length; row++) {
1231
- previous = fill_distance_row(source, target, row, previous);
1392
+ function discard_loaded_keys(snapshot) {
1393
+ for (const key of Object.keys(process.env)) {
1394
+ if (!PROJECT_ENVIRONMENT_KEYS.has(key) && !Object.hasOwn(snapshot, key)) {
1395
+ Reflect.deleteProperty(process.env, key);
1396
+ }
1232
1397
  }
1233
- return at(previous, target.length);
1234
- }
1235
- function closest_command(input, commands) {
1236
- const within = commands.map((command) => ({ command, distance: edit_distance(input, command) })).filter((scored) => scored.distance <= MAX_SUGGESTION_DISTANCE).toSorted((left, right) => left.distance - right.distance);
1237
- return within[0]?.command;
1238
1398
  }
1239
- var command_suggest = { closest_command, edit_distance };
1240
-
1241
- // ports/index.js
1242
- var ENV_FILE_NAME = ".env";
1399
+ function load_environment_file(directory = process.cwd()) {
1400
+ const file = path9.join(resolve_project_directory(directory), ENV_FILE_NAME);
1401
+ if (!existsSync2(file)) return false;
1402
+ const snapshot = { ...process.env };
1403
+ process.loadEnvFile(file);
1404
+ discard_loaded_keys(snapshot);
1405
+ return true;
1406
+ }
1407
+ var ports = {
1408
+ load_environment_file,
1409
+ resolve_project_directory,
1410
+ resolve_seed,
1411
+ resolve_lane,
1412
+ resolve_development_port,
1413
+ resolve_preview_port
1414
+ };
1243
1415
 
1244
1416
  // scripts/josh/josh-command-types.ts
1245
1417
  var OPTIONAL_ENV_FILE_FLAGS = [`--env-file-if-exists=${ENV_FILE_NAME}`];
@@ -1249,6 +1421,10 @@ var TS_BUILD_INFO_FILE = ".tsbuildinfo";
1249
1421
  var CSPELL_CACHE_FILE = ".cspellcache";
1250
1422
  var ESLINT_EDIT_CACHE_FILE = ".eslintcache.edit";
1251
1423
  var ESLINT_RELATED_CACHE_FILE = ".eslintcache.related";
1424
+ var PRETTIER_CACHE_FILE = ".prettiercache";
1425
+ var PRETTIER_RELATED_CACHE_FILE = ".prettiercache.related";
1426
+ var CSPELL_COMMIT_CACHE_FILE = ".cspellcache.commit";
1427
+ var ESLINT_COMMIT_CACHE_FILE = ".eslintcache.commit";
1252
1428
  var ROOTED_JSON_PORTABILITY = "rooted-json";
1253
1429
  var ESLINT_CACHE_SPEC = {
1254
1430
  cache_file: ESLINT_CACHE_FILE,
@@ -1271,7 +1447,11 @@ var GATE_CACHE_FILES = GATE_CACHE_SPECS.map((spec) => spec.cache_file);
1271
1447
  var IGNORED_CACHE_FILES = [
1272
1448
  ...GATE_CACHE_FILES,
1273
1449
  ESLINT_EDIT_CACHE_FILE,
1274
- ESLINT_RELATED_CACHE_FILE
1450
+ ESLINT_RELATED_CACHE_FILE,
1451
+ PRETTIER_CACHE_FILE,
1452
+ PRETTIER_RELATED_CACHE_FILE,
1453
+ CSPELL_COMMIT_CACHE_FILE,
1454
+ ESLINT_COMMIT_CACHE_FILE
1275
1455
  ];
1276
1456
  var CONTENT_CACHE_FLAGS = ["--cache", "--cache-strategy", "content"];
1277
1457
  var CACHE_LOCATION_FLAG = "--cache-location";
@@ -1282,6 +1462,10 @@ var ESLINT_CACHE_FLAGS = content_cache_flags(ESLINT_CACHE_FILE);
1282
1462
  var ESLINT_EDIT_CACHE_FLAGS = content_cache_flags(ESLINT_EDIT_CACHE_FILE);
1283
1463
  var ESLINT_RELATED_CACHE_FLAGS = content_cache_flags(ESLINT_RELATED_CACHE_FILE);
1284
1464
  var CSPELL_CACHE_FLAGS = content_cache_flags(CSPELL_CACHE_FILE);
1465
+ var PRETTIER_CACHE_FLAGS = content_cache_flags(PRETTIER_CACHE_FILE);
1466
+ var PRETTIER_RELATED_CACHE_FLAGS = content_cache_flags(PRETTIER_RELATED_CACHE_FILE);
1467
+ var CSPELL_COMMIT_CACHE_FLAGS = content_cache_flags(CSPELL_COMMIT_CACHE_FILE);
1468
+ var ESLINT_COMMIT_CACHE_FLAGS = content_cache_flags(ESLINT_COMMIT_CACHE_FILE);
1285
1469
 
1286
1470
  // scripts/josh/josh-commands-backlog.ts
1287
1471
  var BACKLOG_COMMANDS = {
@@ -1545,6 +1729,14 @@ var LANE_COMMANDS = {
1545
1729
  category: "AI tools",
1546
1730
  reference: ["<issue>", "developer", ["files", "git", "processes", "network"]]
1547
1731
  },
1732
+ // `.env` is read for `JOSH_LANE_ROOT`, as every lane command reads it, so the lane is found.
1733
+ "lane:withdraw": {
1734
+ script: "scripts/lane/lane-withdraw-cli.ts",
1735
+ tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
1736
+ description: "Stop a running lane child, stash its uncommitted work, close its issue as not planned and close the lane \u2014 booked withdrawn, not as an outage or a failure",
1737
+ category: "AI tools",
1738
+ reference: ["<issue> --reason <text>", "developer", ["files", "git", "processes", "network"]]
1739
+ },
1548
1740
  // A live run's lane limit: `.env` is read so the bare form reports the
1549
1741
  // `JOSH_LANE_LIMIT` a person set there when no override stands.
1550
1742
  "lane:limit": {
@@ -1565,7 +1757,7 @@ var LANE_COMMANDS = {
1565
1757
  },
1566
1758
  "lane:stats": {
1567
1759
  ...MEASURE_ARGUMENTS,
1568
- description: "Print one table row of lane throughput, gate duration and machine load over a period, the per-stage durations of a lane under it, and the wait after each ship stop reason under those",
1760
+ description: "Print one table row of lane throughput, gate duration and machine load over a period, the per-stage durations of a lane under it, the wait after each ship stop reason under those, and the duration and reuse count of each gate check last",
1569
1761
  category: "AI tools",
1570
1762
  reference: ["--period <days> [--limit <lane-limit>]", "maintainer", ["none"]],
1571
1763
  default_script_arguments: ["stats"]
@@ -1771,6 +1963,15 @@ var AI_COMMANDS = {
1771
1963
  category: "AI tools",
1772
1964
  reference: ["(--cut | --over <tokens-per-request>) [--path <dir>]", "automation", ["none"]]
1773
1965
  },
1966
+ "cost:audit": {
1967
+ script: "scripts/cost-audit/cost-audit-cli.ts",
1968
+ description: "Report AI cost over a period by session kind, tool category and guard, with cost per merge and regressions against the period before",
1969
+ category: "AI tools",
1970
+ reference: ["[--period <days>]", "maintainer", ["none"]],
1971
+ // Kit-only: it reads kit's own transcript store, like `josh retrospective`, which carries its
1972
+ // summary line.
1973
+ is_kit_only: true
1974
+ },
1774
1975
  ...DOCUMENT_COMMANDS,
1775
1976
  time: {
1776
1977
  script: "scripts/time/time-cli.ts",
@@ -1798,6 +1999,8 @@ var AI_COMMANDS = {
1798
1999
  description: "Aggregate a finished run's cost, review findings, observation ledger and events into one digest",
1799
2000
  category: "AI tools",
1800
2001
  reference: ["", "maintainer", ["none"]],
2002
+ core_weight: core_budget.CORE_WEIGHTS.retrospective,
2003
+ memory_mb: core_budget.MEMORY_MB.retrospective,
1801
2004
  // Kit-only: it reads kit's own development run — the transcript store, the review ledger and the
1802
2005
  // run event stream — so it means nothing in a consumer project and is dropped from a consumer's
1803
2006
  // help, through the same declaration `josh time` uses for the same reason.
@@ -1999,6 +2202,7 @@ var TYPE_CHECK_LABEL = "check";
1999
2202
  var UNIT_LABEL = "test:unit";
2000
2203
  var BEHAVIOR_LABEL = "behavior";
2001
2204
  var METRICS_LABEL = "metrics";
2205
+ var EXPORTS_LABEL = "exports";
2002
2206
  var WARNING_CHECKER_LABELS = [LINT_LABEL, TYPE_CHECK_LABEL];
2003
2207
  var STATIC_CHECKS = [
2004
2208
  {
@@ -2024,7 +2228,7 @@ var STATIC_CHECKS = [
2024
2228
  // program and walks it for about 5s, and it is over well before lint and the type check. A
2025
2229
  // reservation would only take a place from the four-core CI runner's fan-out and a worker from the
2026
2230
  // unit suite for the whole gate.
2027
- { label: "exports", target: "exports:unused", reserved_cores: 0 },
2231
+ { label: EXPORTS_LABEL, target: "exports:unused", reserved_cores: 0 },
2028
2232
  // **The metrics ratchet is kit's own**: it holds kit's repository-wide
2029
2233
  // totals to the merge-base's. It is one in-process eslint pass with only `max-lines` enabled, over
2030
2234
  // in a few seconds, so it reserves no core for the reason the unused-member check above does not.
@@ -2113,6 +2317,15 @@ function resolve_gate_plan(available_cores = availableParallelism4(), concurrent
2113
2317
  unit_worker_cap: unit_cap_for(checks, available_cores, concurrent_runs)
2114
2318
  };
2115
2319
  }
2320
+ function bound_by_free(plan, available_cores, free_cores) {
2321
+ if (!has_unit_check(plan.checks)) return plan;
2322
+ const unit_worker_cap = unit_worker_share.cap_to_free(
2323
+ plan.unit_worker_cap,
2324
+ free_cores,
2325
+ available_cores
2326
+ );
2327
+ return { ...plan, unit_worker_cap };
2328
+ }
2116
2329
  function format_machine(available_cores, concurrent_runs) {
2117
2330
  const cores = `${String(available_cores)} cores`;
2118
2331
  if (concurrent_runs <= unit_worker_share.SOLO_RUNS) return cores;
@@ -2131,6 +2344,7 @@ function format_gate_plan(plan, available_cores = availableParallelism4(), concu
2131
2344
  return `${josh_verdict.GATE_OPENING_PREFIX}${width}, ${cap} (${machine})`;
2132
2345
  }
2133
2346
  var gate_plan = {
2347
+ EXPORTS_LABEL,
2134
2348
  GATE_CHECKS,
2135
2349
  LINT_LABEL,
2136
2350
  MEASURED_CORES,
@@ -2140,6 +2354,7 @@ var gate_plan = {
2140
2354
  TYPE_CHECK_LABEL,
2141
2355
  UNIT_LABEL,
2142
2356
  WARNING_CHECKER_LABELS,
2357
+ bound_by_free,
2143
2358
  check_memory,
2144
2359
  check_weight,
2145
2360
  direct_unit_memory,
@@ -2272,7 +2487,7 @@ var DEV_COMMANDS = {
2272
2487
  },
2273
2488
  "stop:guard": {
2274
2489
  script: "scripts/hooks/stop-guard.ts",
2275
- description: "Claude Code Stop hook: deliver the three stop-time rules \u2014 hold notify/release and issue citation (reads the Stop payload on stdin)",
2490
+ description: "Claude Code Stop hook: send the hold notify, release the hold and print the issue citations itself; block on the rules that need the model (reads the Stop payload on stdin)",
2276
2491
  category: "Development",
2277
2492
  reference: ["", "automation", ["none"]]
2278
2493
  // **No `tsx_arguments`, deliberately**, the same as `pretool:guard` above: this runs at every
@@ -2406,6 +2621,12 @@ var HOOKS_COMMANDS = {
2406
2621
  category: "Git hooks",
2407
2622
  reference: ["[paths...]", "automation", ["none"]]
2408
2623
  },
2624
+ "pre-commit-lint": {
2625
+ script: "scripts/hooks/pre-commit-lint.ts",
2626
+ description: "Git hook: run cspell, prettier or eslint over staged files from a content cache",
2627
+ category: "Git hooks",
2628
+ reference: ["<cspell|prettier|eslint> [paths...]", "automation", ["files"]]
2629
+ },
2409
2630
  "pre-push-unit": {
2410
2631
  script: "scripts/hooks/pre-push-unit.ts",
2411
2632
  description: "Git hook: run unit tests, reusing a green gate recorded on the pushed tree",
@@ -2811,6 +3032,11 @@ var WORKFLOW_COMMANDS = {
2811
3032
  // A script rather than an `sh -c` chain, because the strategy has to be named rather than left to
2812
3033
  // the caller's git configuration: a bare `git pull` aborts with `Need to specify how to reconcile
2813
3034
  // divergent branches` on exactly the diverged branch the command exists for.
3035
+ //
3036
+ // **It declares no core weight, by measurement** (joshuafolkken/kit#3944): its own work is git, and
3037
+ // the catalog it may regenerate took 0.6s of CPU. The heavy processes seen beside it are commit
3038
+ // hooks — its regenerated catalog's commit runs them — which reserve themselves, and a weight here
3039
+ // would set the held mark they inherit, folding the parallel hook tools into this one place.
2814
3040
  "main:merge": {
2815
3041
  script: "scripts/git/main-merge.ts",
2816
3042
  description: "Merge origin default branch into the current branch",
@@ -2886,6 +3112,7 @@ var ALIASES = {
2886
3112
  tm: "time",
2887
3113
  tmd: "time:density",
2888
3114
  rtr: "retrospective",
3115
+ ca: "cost:audit",
2889
3116
  rvf: "review:findings",
2890
3117
  ruv: "rule:value",
2891
3118
  ev: "eval",
@@ -2895,75 +3122,1687 @@ var ALIASES = {
2895
3122
  lst: "lane:stats",
2896
3123
  lli: "lane:limit",
2897
3124
  lrs: "lane:restart",
3125
+ lwd: "lane:withdraw",
2898
3126
  blr: "backlogrun"
2899
3127
  };
2900
-
2901
- // scripts/josh/josh-composite-arguments.ts
2902
- var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash"]);
2903
- var SHELL_COMMAND_FLAG = "-c";
2904
- var PATH_SEPARATOR = "/";
2905
- var USAGE_ERROR_EXIT_CODE = 1;
2906
- function is_shell_interpreter(executable) {
2907
- return SHELL_INTERPRETERS.has(executable.split(PATH_SEPARATOR).at(-1) ?? executable);
2908
- }
2909
- function is_composite_shell(shell) {
2910
- const [executable, flag] = shell ?? [];
2911
- if (executable === void 0) return false;
2912
- return is_shell_interpreter(executable) && flag === SHELL_COMMAND_FLAG;
3128
+ function canonical_command(name) {
3129
+ if (!Object.hasOwn(ALIASES, name)) return name;
3130
+ return ALIASES[name] ?? name;
2913
3131
  }
2914
- function format_targets(targets) {
2915
- return targets.map((target) => `josh ${target}`).join(" or ");
2916
- }
2917
- function format_rejection(cmd, targets) {
2918
- const hint = targets.length > 0 ? `pass them to ${format_targets(targets)} instead` : "it runs a fixed sequence and forwards nothing";
2919
- return `josh ${cmd} takes no extra arguments \u2014 ${hint}`;
2920
- }
2921
- function reject_extra_arguments(cmd, entry, extra) {
2922
- if (extra.length === 0 || !is_composite_shell(entry.shell)) return void 0;
2923
- return format_rejection(cmd, entry.argument_targets ?? []);
2924
- }
2925
- var composite_arguments = { format_rejection, is_composite_shell, reject_extra_arguments };
2926
3132
 
2927
- // scripts/josh/josh-in-process.ts
2928
- import { realpathSync } from "node:fs";
2929
- import path9 from "node:path";
2930
- import { pathToFileURL } from "node:url";
2931
- var TYPESCRIPT_EXTENSION = ".ts";
2932
- var JAVASCRIPT_EXTENSION = ".js";
2933
- var BUNDLED_COMMAND_DIRECTORY = path9.join("dist", "commands");
2934
- var FAILURE_EXIT_CODE = 1;
2935
- function is_typescript_dispatcher(dispatcher_url) {
2936
- return dispatcher_url.endsWith(TYPESCRIPT_EXTENSION);
2937
- }
2938
- function bundled_script_path(package_directory, script) {
2939
- const name = `${path9.basename(script, TYPESCRIPT_EXTENSION)}${JAVASCRIPT_EXTENSION}`;
2940
- return path9.join(package_directory, BUNDLED_COMMAND_DIRECTORY, name);
2941
- }
2942
- function in_process_target(entry, package_directory, dispatcher_url) {
2943
- if (entry.script === void 0 || entry.tsx_arguments !== void 0) return void 0;
2944
- if (is_typescript_dispatcher(dispatcher_url)) return path9.join(package_directory, entry.script);
2945
- return entry.is_bundled === true ? bundled_script_path(package_directory, entry.script) : void 0;
2946
- }
2947
- function read_exit_code() {
2948
- const code = process.exitCode;
2949
- return typeof code === "number" ? code : 0;
2950
- }
2951
- function resolve_real_path(script_path) {
2952
- try {
2953
- return realpathSync(script_path);
2954
- } catch {
2955
- return script_path;
2956
- }
2957
- }
2958
- async function run_in_process(script_path, script_arguments) {
2959
- const resolved_path = resolve_real_path(script_path);
2960
- process.argv = [process.execPath, resolved_path, ...script_arguments];
2961
- try {
2962
- await import(pathToFileURL(resolved_path).href);
2963
- } catch (error) {
2964
- console.error(error);
2965
- return FAILURE_EXIT_CODE;
2966
- }
3133
+ // scripts/josh/josh-usage-automation.ts
3134
+ var STAGED = "--staged";
3135
+ var JSON_FLAG = "--json";
3136
+ var BRANCH_DIFF = "the branch diff";
3137
+ var STAGED_DIFF = "the staged diff instead";
3138
+ var PR_NUMBER = "42";
3139
+ var DRY_RUN = "--dry-run";
3140
+ var SKIP_PUBLISH_WAIT = "--skip-publish-wait";
3141
+ var AUTOMATION_USAGE = {
3142
+ "refactor:scan": { examples: [{ arguments: "" }] },
3143
+ "time:density": {
3144
+ examples: [{ arguments: "" }, { arguments: "--lanes 5", note: "the five most recent lanes" }],
3145
+ flags: [
3146
+ {
3147
+ names: ["--lanes <n>"],
3148
+ description: "average over a different count of recent qualifying lanes (default 10)."
3149
+ },
3150
+ {
3151
+ names: ["--path <dir>"],
3152
+ description: "read another project's lane transcripts, as `josh time` does. Run it from the main checkout \u2014 a lane reads only its own transcript."
3153
+ }
3154
+ ]
3155
+ },
3156
+ "e2e:retry-check": { examples: [{ arguments: "" }] },
3157
+ "sync:scope": {
3158
+ examples: [
3159
+ { arguments: "", note: BRANCH_DIFF },
3160
+ { arguments: STAGED, note: STAGED_DIFF },
3161
+ { arguments: JSON_FLAG, note: '{"scope":"managed","reason":"..."}' }
3162
+ ],
3163
+ flags: [
3164
+ { names: [STAGED], description: "inspect the staged diff instead of the branch diff." },
3165
+ { names: [JSON_FLAG], description: 'emit `{"scope","reason"}` as JSON.' }
3166
+ ]
3167
+ },
3168
+ "dogfood:commit": { examples: [{ arguments: "~/Development/kit-test-html-start" }] },
3169
+ "fresh:run": {
3170
+ examples: [
3171
+ { arguments: "--list", note: "kit's six patterns" },
3172
+ { arguments: "kit-test-html-init", note: "one pattern, the packed checkout" },
3173
+ {
3174
+ arguments: "kit-test-html-start",
3175
+ note: "a GitHub pattern, the latest published version"
3176
+ },
3177
+ { arguments: "--spec fresh/html-init.json", note: "a scenario another toolkit wrote" }
3178
+ ],
3179
+ flags: [
3180
+ { names: ["--list"], description: "print the pattern names and run nothing." },
3181
+ {
3182
+ names: ["--spec <file.json>"],
3183
+ description: "run the scenario in this file instead of a named pattern; the path is relative to the project root."
3184
+ },
3185
+ {
3186
+ names: ["--package-version <version>"],
3187
+ description: "the published version a `package_source: published` scenario installs. Default: the registry's latest version of the package the command is run from."
3188
+ },
3189
+ {
3190
+ names: ["--log-dir <dir>"],
3191
+ description: "where the log is written. Default: `.claude/tmp/fresh-machine`."
3192
+ },
3193
+ { names: ["--rebuild"], description: "build the machine image without the layer cache." }
3194
+ ]
3195
+ },
3196
+ "auth:setup": {
3197
+ examples: [
3198
+ { arguments: "", note: "sign in and save the token" },
3199
+ { arguments: "--check", note: "ok: \u2026 saved on <date>, or missing: \u2026 and exit 1" }
3200
+ ]
3201
+ },
3202
+ "sonar:hotspots": { examples: [{ arguments: PR_NUMBER }] },
3203
+ "sonar:new-code": { examples: [{ arguments: PR_NUMBER }] },
3204
+ "ui:routes": {
3205
+ examples: [
3206
+ { arguments: "", note: BRANCH_DIFF },
3207
+ { arguments: STAGED, note: STAGED_DIFF }
3208
+ ]
3209
+ },
3210
+ propagate: {
3211
+ examples: [
3212
+ { arguments: "" },
3213
+ { arguments: DRY_RUN, note: "report targets and steps, write nothing" },
3214
+ { arguments: SKIP_PUBLISH_WAIT, note: "release already known to be published" },
3215
+ { arguments: "--target app-kit", note: "carry the release into one consumer only" }
3216
+ ],
3217
+ flags: [
3218
+ {
3219
+ names: [DRY_RUN],
3220
+ description: "report targets and steps without writing; skips the publish wait, opens no issues."
3221
+ },
3222
+ {
3223
+ names: [SKIP_PUBLISH_WAIT],
3224
+ description: "skip the registry poll for an already-published release."
3225
+ },
3226
+ {
3227
+ names: ["--target <repo>"],
3228
+ description: "process only that consumer (`app-kit` or `joshuafolkken/app-kit`); the rest are reported `skipped` and left untouched. An unknown, ambiguous or non-dependent name fails before the publish wait, writing nothing."
3229
+ }
3230
+ ]
3231
+ },
3232
+ "repo:party": {
3233
+ examples: [
3234
+ { arguments: "joshuafolkken/kit", note: "\u2192 first-party (in the kit session)" },
3235
+ { arguments: "sveltejs/kit", note: "\u2192 third-party" },
3236
+ { arguments: "", note: "no argument: the session's own repository" }
3237
+ ]
3238
+ }
3239
+ };
3240
+
3241
+ // scripts/josh/josh-usage-backlog.ts
3242
+ var EXCLUDE = "--exclude <N>";
3243
+ var BUDGET = '--answer candidates --started "$started" --active "$active"';
3244
+ var REPO = "--repo <owner/repo>";
3245
+ var JUST_MERGED = "skip the issue just merged";
3246
+ var EXCLUDE_1630 = "--exclude 1630";
3247
+ var BACKLOG_USAGE = {
3248
+ epic: {
3249
+ examples: [
3250
+ { arguments: '"Epic: split the parser work" 101 102 103' },
3251
+ { arguments: '"Epic: staged rollout" 101 102 --ordered' },
3252
+ { arguments: '"Epic: ..." 101 102 --rationale-file rationale.md' },
3253
+ { arguments: "--promote 858 101 102 103", note: "turn an existing issue into an epic" },
3254
+ { arguments: "--add 893 894", note: "track #894, declaring no order for it" },
3255
+ { arguments: "--add 893 894 --before 891", note: "#894 must finish before #891" },
3256
+ { arguments: "--add 893 894 --after 890", note: "#894 starts once #890 is done" },
3257
+ { arguments: "--add 893 894 --decision-file why.md", note: "\u2026and record why, in one call" },
3258
+ {
3259
+ arguments: "--reconcile 2184",
3260
+ note: "rewrite the body to match the recorded blocked-by, and record any order the body declared"
3261
+ }
3262
+ ],
3263
+ flags: [
3264
+ {
3265
+ names: ["--ordered"],
3266
+ description: "argument order is the dependency order; writes the arrow chain, records the matching `blocked-by` relations, and adds a `### Declared order` entry under `## Decisions`."
3267
+ },
3268
+ {
3269
+ names: ["--rationale-file <path|->"],
3270
+ description: "split rationale prose (`-` reads stdin)."
3271
+ },
3272
+ {
3273
+ names: ["--origin <owner/repo#N>"],
3274
+ description: "backlink when the split originated in another repository."
3275
+ },
3276
+ {
3277
+ names: ["--before <M>"],
3278
+ description: "with `--add`: insert before `#M`, re-pointing what `#M` was waiting on so the chain is never broken."
3279
+ },
3280
+ {
3281
+ names: ["--after <M>"],
3282
+ description: "with `--add`: start after `#M`; branches when `#M` already has a successor, extends when it is a tail."
3283
+ },
3284
+ {
3285
+ names: ["--order-before <M>", "--order-after <M>"],
3286
+ description: "with `--add`: move the task-list row only; write no `blocked-by` relation."
3287
+ },
3288
+ {
3289
+ names: ["--decision-file <path|->"],
3290
+ description: "with `--add`: record why the child was placed."
3291
+ },
3292
+ {
3293
+ names: ["--remove <E> <M> <N> [<N2> \u2026]"],
3294
+ description: "delete a declared order from the body and the relations; each consecutive pair is one link."
3295
+ }
3296
+ ]
3297
+ },
3298
+ "epic:next": {
3299
+ examples: [
3300
+ { arguments: "858" },
3301
+ { arguments: "858 --repo joshuafolkken/kit", note: "just the next child for one repository" },
3302
+ { arguments: "858 --repo joshuafolkken/kit --lanes", note: "one child per free lane there" }
3303
+ ],
3304
+ flags: [
3305
+ {
3306
+ names: [REPO],
3307
+ description: "answer for one repository; stdout carries one token (an issue number, or `wait`/`stop`/`complete`), everything else on stderr."
3308
+ },
3309
+ {
3310
+ names: ["--lanes"],
3311
+ description: "print one issue number per free lane (requires `--repo`); `JOSH_LANE_LIMIT` sets the ceiling, default 4. Prints `triage` instead while any candidate carries neither `run:solo` nor `run:lane` (the triage gate in `josh backlog:next`)."
3312
+ }
3313
+ ]
3314
+ },
3315
+ "epic:bundle": { examples: [{ arguments: "874" }] },
3316
+ "epic:audit": { examples: [{ arguments: "858" }] },
3317
+ "epic:check": { examples: [{ arguments: "700" }] },
3318
+ "auto-ok:next": {
3319
+ examples: [{ arguments: "" }, { arguments: "--exclude 906", note: JUST_MERGED }],
3320
+ flags: [
3321
+ {
3322
+ names: [EXCLUDE],
3323
+ description: "drop issues from the answer; comma-separated, repeatable."
3324
+ }
3325
+ ]
3326
+ },
3327
+ "auto-ok:judge": {
3328
+ examples: [
3329
+ { arguments: "", note: "the issues that still owe a judgement" },
3330
+ { arguments: "--apply 912 --lane", note: "opt it in, with its run label" },
3331
+ { arguments: '--decline 913 --reason "<why>"', note: "record why it stays out" }
3332
+ ],
3333
+ flags: [
3334
+ {
3335
+ names: ["--apply <N> --lane|--solo"],
3336
+ description: "checks the exclusions again against the labels as they stand, applies the run label and then `auto-ok`, and notes it on the progress board."
3337
+ },
3338
+ { names: ["--decline <N> --reason <text>"], description: "applies nothing." }
3339
+ ]
3340
+ },
3341
+ "backlog:next": {
3342
+ examples: [{ arguments: "" }, { arguments: EXCLUDE_1630, note: JUST_MERGED }],
3343
+ flags: [
3344
+ {
3345
+ names: [EXCLUDE],
3346
+ description: "drop issues from every bucket; comma-separated, repeatable."
3347
+ }
3348
+ ]
3349
+ },
3350
+ "backlog:plan": {
3351
+ examples: [
3352
+ { arguments: "" },
3353
+ { arguments: EXCLUDE_1630, note: "after #1630 merged" },
3354
+ { arguments: "--waves", note: "the run order, wave by wave" }
3355
+ ],
3356
+ flags: [
3357
+ { names: [EXCLUDE], description: "same exclusion as `backlog:next`." },
3358
+ {
3359
+ names: ["--waves"],
3360
+ description: "print the order the run takes, wave by wave, assuming each wave merges before the next. Read-only."
3361
+ }
3362
+ ]
3363
+ },
3364
+ "backlog:stalled": { examples: [{ arguments: "" }] },
3365
+ "backlog:budget": {
3366
+ examples: [
3367
+ { arguments: BUDGET },
3368
+ { arguments: '--answer exhausted --started "$started" --active "$active" --idle 60' },
3369
+ { arguments: `${BUDGET} --merged 3 --running 2 --max 5` }
3370
+ ],
3371
+ flags: [
3372
+ {
3373
+ names: ["--answer <candidates|exhausted|blocked|parked|unreadable|untriaged>"],
3374
+ description: "`backlog:next`'s answer, mapped."
3375
+ },
3376
+ {
3377
+ names: ["--started", "--active"],
3378
+ description: "when the invocation began / when it last had work (required unless the watch is off)."
3379
+ },
3380
+ {
3381
+ names: ["--idle <minutes>"],
3382
+ description: "once nothing can proceed, keep polling this long, counted from `--active` (default 480, 8 hours; `--idle 0` turns the watch off)."
3383
+ },
3384
+ {
3385
+ names: ["--max <count>"],
3386
+ description: "issues one invocation may take (default unlimited); `--merged` and `--running` count against it."
3387
+ },
3388
+ {
3389
+ names: ["--json"],
3390
+ description: 'collapse verdict and reason into `{"budget": "<verdict>", "reason": "\u2026"}`.'
3391
+ }
3392
+ ]
3393
+ },
3394
+ "backlog:offer": {
3395
+ examples: [{ arguments: '--started "$started" --active "$active" --running 2 --retries 1' }]
3396
+ },
3397
+ "backlog:drive": {
3398
+ examples: [{ arguments: '--owner "$PPID" [--max <n>] [--idle <minutes>] [--only]' }]
3399
+ }
3400
+ };
3401
+
3402
+ // scripts/josh/josh-usage-developer.ts
3403
+ var UNSEEDED = {};
3404
+ var NARROWED = { arguments: "scripts/thing.ts", note: "narrow by the given files instead" };
3405
+ var DEVELOPER_USAGE = {
3406
+ gate: {
3407
+ examples: [
3408
+ { arguments: "" },
3409
+ { arguments: "--verbose", note: "every check's output, passing ones included" },
3410
+ { arguments: "--force", note: "re-run even on a tree already recorded green" },
3411
+ { arguments: "--no-unit", note: "the static checks only (CI only)" }
3412
+ ]
3413
+ },
3414
+ lint: { examples: [{ arguments: "" }] },
3415
+ "lint:related": { examples: [{ arguments: "" }, NARROWED] },
3416
+ lines: {
3417
+ examples: [{ arguments: "scripts/hooks/format-edited-file.ts scripts/josh/josh-logic.ts" }]
3418
+ },
3419
+ bytes: {
3420
+ examples: [
3421
+ { arguments: "docs/josh-commands.md", note: "one document" },
3422
+ { arguments: "", note: "scan: every budgeted document near its ceiling" }
3423
+ ]
3424
+ },
3425
+ metrics: {
3426
+ examples: [
3427
+ { arguments: "", note: "print the totals and check them against the merge-base's" },
3428
+ { arguments: '--accept --reason "<why>"', note: "record this branch's growth and why" }
3429
+ ]
3430
+ },
3431
+ format: { examples: [{ arguments: "" }] },
3432
+ "cspell:dot": { examples: [{ arguments: "", note: "includes dotfiles" }] },
3433
+ behavior: { examples: [{ arguments: "" }] },
3434
+ "exports:unused": { examples: [{ arguments: "" }] },
3435
+ "test:unit": { examples: [{ arguments: "" }] },
3436
+ "test:related": {
3437
+ examples: [
3438
+ { arguments: "" },
3439
+ NARROWED,
3440
+ { arguments: "--silent", note: "flags are forwarded to vitest" }
3441
+ ]
3442
+ },
3443
+ "test:declared": {
3444
+ examples: [
3445
+ { arguments: "" },
3446
+ { arguments: "--match", note: "check Step 0 declarations on stdin" },
3447
+ { arguments: "--help", note: "print usage, including the --match stdin form" }
3448
+ ]
3449
+ },
3450
+ "test:e2e": { examples: [{ arguments: "" }] },
3451
+ test: { examples: [{ arguments: "" }] },
3452
+ check: { examples: [{ arguments: "" }] },
3453
+ port: {
3454
+ examples: [
3455
+ {
3456
+ arguments: "dev",
3457
+ note: `${String(ports.resolve_development_port(UNSEEDED))} with no seed set`
3458
+ },
3459
+ {
3460
+ arguments: "preview",
3461
+ note: `${String(ports.resolve_preview_port(UNSEEDED))} with no seed set`
3462
+ }
3463
+ ]
3464
+ },
3465
+ profile: { examples: [{ arguments: "" }] },
3466
+ init: {
3467
+ examples: [
3468
+ {
3469
+ arguments: "",
3470
+ note: "create/merge config files, install and format (--no-install skips both, --dry-run only prints the plan)"
3471
+ }
3472
+ ]
3473
+ },
3474
+ "registry:migrate": { examples: [{ arguments: "" }] },
3475
+ sync: { examples: [{ arguments: "", note: "overwrite managed files" }] },
3476
+ uninstall: {
3477
+ examples: [
3478
+ { arguments: "", note: "show the plan, then ask" },
3479
+ { arguments: "--yes", note: "remove without asking" }
3480
+ ]
3481
+ },
3482
+ adopt: {
3483
+ examples: [
3484
+ { arguments: "" },
3485
+ { arguments: "--dry-run", note: "report the steps without touching anything" }
3486
+ ],
3487
+ flags: [{ names: ["--dry-run"], description: "report the plan without writing." }]
3488
+ },
3489
+ git: {
3490
+ examples: [
3491
+ { arguments: "", note: "interactive" },
3492
+ { arguments: "-y", note: "unattended (no TTY required)" },
3493
+ { arguments: '-y "title"', note: "set commit message prefix" },
3494
+ { arguments: '-y "<title> #<N>"', note: "follow-up commit on the same branch" },
3495
+ {
3496
+ arguments: "-y --skip-commit --skip-push",
3497
+ note: "open the PR without committing/pushing"
3498
+ }
3499
+ ],
3500
+ flags: [
3501
+ {
3502
+ names: ["-y", "--yes"],
3503
+ description: "run non-interactively; works without a TTY (agent or CI shell)."
3504
+ },
3505
+ {
3506
+ names: ["--skip-commit", "--skip-push"],
3507
+ description: "recover/open a PR without a new commit or push."
3508
+ }
3509
+ ]
3510
+ },
3511
+ pr: { examples: [{ arguments: "", note: "open the PR for the current <N>-<slug> branch" }] },
3512
+ "review:findings": { examples: [{ arguments: "" }] },
3513
+ "main:sync": { examples: [{ arguments: "" }] },
3514
+ "main:merge": { examples: [{ arguments: "" }] },
3515
+ version: {
3516
+ examples: [
3517
+ { arguments: "" },
3518
+ {
3519
+ arguments: "--upgrade",
3520
+ note: "upgrade global + project to latest, then re-run fix-gh-packages"
3521
+ }
3522
+ ],
3523
+ flags: [
3524
+ {
3525
+ names: ["--upgrade"],
3526
+ description: "upgrade both the global install (`pnpm add -g`) and the project devDependency (`pnpm add -D` + `fix-gh-packages`); already-current or not-installed targets are skipped."
3527
+ }
3528
+ ]
3529
+ },
3530
+ ranges: { examples: [{ arguments: "" }] },
3531
+ doctor: {
3532
+ examples: [
3533
+ { arguments: "", note: "diagnose only" },
3534
+ { arguments: "--fix", note: "reclaim the global josh by removing a stale kit shim" },
3535
+ { arguments: "--ports", note: "also print the per-repository port-seed table" }
3536
+ ],
3537
+ flags: [
3538
+ {
3539
+ names: ["--fix"],
3540
+ description: "remove a shadowing binary **only if it is a kit shim** (its body references `@joshuafolkken/kit` or the removed `install-bin` script); any other binary is left and reported for manual review. Also the one-shot recovery for an old project that re-created the global shim."
3541
+ },
3542
+ { names: ["--ports"], description: "additionally print the port-seed table (see below)." }
3543
+ ]
3544
+ },
3545
+ audit: { examples: [{ arguments: "" }] },
3546
+ overrides: {
3547
+ examples: [
3548
+ { arguments: "", note: "verify overrides unchanged" },
3549
+ { arguments: "--save", note: "snapshot current merged overrides" }
3550
+ ],
3551
+ flags: [
3552
+ {
3553
+ names: ["--save"],
3554
+ description: "write the current merged overrides to `.overrides-snapshot.json` (gitignored); later runs compare against it and exit non-zero on any add, removal, or change."
3555
+ }
3556
+ ]
3557
+ },
3558
+ backlogrun: {
3559
+ examples: [
3560
+ { arguments: "", note: "start in Claude, then show the board" },
3561
+ { arguments: "3437 --only", note: "the arguments go to backlogrun as typed" },
3562
+ { arguments: "--agent codex", note: "start in Codex" },
3563
+ { arguments: "--stop", note: "stop the run in this repository" }
3564
+ ]
3565
+ },
3566
+ "lane:limit": {
3567
+ examples: [
3568
+ { arguments: "8", note: "raise or lower the live run's limit to 8" },
3569
+ { arguments: "", note: "print the limit, the lanes in use and the free lanes" },
3570
+ { arguments: "--reset", note: "clear the override; JOSH_LANE_LIMIT applies again" }
3571
+ ]
3572
+ },
3573
+ "lane:withdraw": {
3574
+ examples: [{ arguments: '3769 --reason "folded into a broader issue"' }]
3575
+ },
3576
+ "test:red": { examples: [{ arguments: "", note: "red, green, no-test or test-only" }] },
3577
+ start: {
3578
+ examples: [
3579
+ { arguments: "", note: "asks for the profile and the repository" },
3580
+ {
3581
+ arguments: "--yes --github --profile basic",
3582
+ note: "unattended, including the GitHub repository"
3583
+ }
3584
+ ],
3585
+ flags: [
3586
+ { names: ["--profile basic|full"], description: "the profile, without asking." },
3587
+ { names: ["--yes"], description: "take the defaults \u2014 not consent to write to GitHub." },
3588
+ { names: ["--github"], description: "create the GitHub repository and push `main`." },
3589
+ { names: ["--public"], description: "create the repository public instead of private." },
3590
+ {
3591
+ names: ['--init-command "<command>"'],
3592
+ description: "run this setup command in place of `josh init`."
3593
+ },
3594
+ {
3595
+ names: ['--push-preflight-command "<command>"'],
3596
+ description: "a check run before the first step when the plan pushes; a failure stops the run before it writes anything."
3597
+ },
3598
+ { names: ["--dry-run"], description: "print the plan and change nothing." }
3599
+ ]
3600
+ },
3601
+ latest: { examples: [{ arguments: "", note: "full update (update + audit + pnpm)" }] },
3602
+ "latest:corepack": { examples: [{ arguments: "", note: "update pnpm only" }] },
3603
+ "latest:update": { examples: [{ arguments: "", note: "update dependencies only" }] },
3604
+ "lane:restart": {
3605
+ examples: [{ arguments: "3769", note: "prints the fresh child's pid" }]
3606
+ },
3607
+ "rule:value": {
3608
+ examples: [
3609
+ { arguments: "" },
3610
+ {
3611
+ arguments: "--refresh",
3612
+ note: "write the reading to the loop-head cache instead of stdout"
3613
+ }
3614
+ ]
3615
+ }
3616
+ };
3617
+
3618
+ // scripts/josh/josh-usage-hooks.ts
3619
+ var PRE_TOOL_USE = "run by the PreToolUse hook, the pending tool call as JSON on stdin";
3620
+ var IN_PRETOOL_GUARD = "run inside pretool:guard, not wired on its own";
3621
+ var HOOK_USAGE = {
3622
+ "format:edited": {
3623
+ examples: [
3624
+ { arguments: "", note: "run by the PostToolUse hook, the tool call as JSON on stdin" }
3625
+ ]
3626
+ },
3627
+ "batch:guard": { examples: [{ arguments: "", note: IN_PRETOOL_GUARD }] },
3628
+ "investigation:guard": { examples: [{ arguments: "", note: PRE_TOOL_USE }] },
3629
+ "duplicate-read:guard": { examples: [{ arguments: "", note: IN_PRETOOL_GUARD }] },
3630
+ "rule:guard": { examples: [{ arguments: "", note: PRE_TOOL_USE }] },
3631
+ "rule:list": { examples: [{ arguments: "", note: "print every trigger-delivered rule" }] },
3632
+ "pretool:guard": { examples: [{ arguments: "", note: PRE_TOOL_USE }] },
3633
+ "codex:hook-adapter": {
3634
+ examples: [
3635
+ { arguments: "pretool", note: "a Codex PreToolUse payload on stdin" },
3636
+ { arguments: "posttool", note: "a Codex PostToolUse payload on stdin" }
3637
+ ]
3638
+ },
3639
+ "stop:guard": {
3640
+ examples: [{ arguments: "", note: "run by the Stop hook, the stop payload as JSON on stdin" }]
3641
+ },
3642
+ "session:lang": {
3643
+ examples: [{ arguments: "", note: "run by the UserPromptSubmit hook; `ja` prints nothing" }]
3644
+ }
3645
+ };
3646
+
3647
+ // scripts/josh/josh-usage-issue.ts
3648
+ var REPO2 = "--repo <owner/repo>";
3649
+ var BODY_FILE = "--body-file <path>";
3650
+ var CONCURRENT = "several numbers, read concurrently";
3651
+ var REPO_FLAG = {
3652
+ names: [REPO2],
3653
+ description: "read a child in another repository; applies to every number."
3654
+ };
3655
+ var JSON_FLAG2 = { names: ["--json"], description: "print the verdict and reason as JSON." };
3656
+ var ISSUE_USAGE = {
3657
+ "issue:read": {
3658
+ examples: [{ arguments: "1715" }, { arguments: "1715 1567 1605", note: CONCURRENT }]
3659
+ },
3660
+ "issue:ack": { examples: [{ arguments: "3773" }] },
3661
+ "issue:state": {
3662
+ examples: [
3663
+ { arguments: "42", note: "single-number shape" },
3664
+ { arguments: "42 43 44", note: "each block headed by `issue:`" },
3665
+ { arguments: "42 43 --repo joshuafolkken/app-kit", note: "a child in another repo" }
3666
+ ],
3667
+ flags: [REPO_FLAG]
3668
+ },
3669
+ "issue:release": {
3670
+ examples: [
3671
+ { arguments: "3360" },
3672
+ { arguments: "3716 --wait", note: "blocked by the release issue instead" }
3673
+ ],
3674
+ flags: [
3675
+ {
3676
+ names: ["--wait"],
3677
+ description: "the issue is blocked by the release issue instead, so `backlog:next` offers it again once the release ships (`.claude/skills/workflow-commands/prerequisite.md`)."
3678
+ }
3679
+ ]
3680
+ },
3681
+ "issue:scout": {
3682
+ examples: [
3683
+ { arguments: '"Stop the gate re-running after every edit"' },
3684
+ { arguments: '"<title>" --body "parent: #1246"' },
3685
+ { arguments: '"<title>" --body-file draft.md' }
3686
+ ],
3687
+ flags: [
3688
+ {
3689
+ names: ['--body "<text>"'],
3690
+ description: "supply references (`#N`) in a `parent: #N` line or an acceptance-criteria section so the epic half has a number to work from; without one there (a background mention does not count) it prints `Epic: not asked`."
3691
+ },
3692
+ {
3693
+ names: [BODY_FILE],
3694
+ description: 'pass the whole draft, including its purpose, requirements and acceptance criteria. A file that cannot be read ends the command rather than being treated as "no candidates".'
3695
+ }
3696
+ ]
3697
+ },
3698
+ "issue:file": {
3699
+ examples: [
3700
+ { arguments: '"<title>" --body-file body.md --depth 1' },
3701
+ {
3702
+ arguments: '"<title>" --body-file body.md --depth 0 --route tier-a --repo joshuafolkken/kit'
3703
+ },
3704
+ { arguments: '"<title>" --body-file body.md --depth 1 --distinct 2801,2795' }
3705
+ ],
3706
+ flags: [
3707
+ { names: [BODY_FILE], description: "the body. Required." },
3708
+ {
3709
+ names: ["--depth <0|1|2>"],
3710
+ description: 'the depth label. Required. The criteria are `.claude/skills/workflow-commands/observation-filing.md` \u2192 "The depth test".'
3711
+ },
3712
+ {
3713
+ names: ["--route <tier-a|split|interrupt|review-cap>"],
3714
+ description: "the `route:` label naming the filing route. Omit it for a filing with no route."
3715
+ },
3716
+ {
3717
+ names: ["--label <name>"],
3718
+ description: "an extra label (for example `epic`). Repeatable."
3719
+ },
3720
+ {
3721
+ names: [REPO2],
3722
+ description: "the target repository. Defaults to this repository."
3723
+ },
3724
+ {
3725
+ names: ["--distinct <N,\u2026>"],
3726
+ description: "duplicate candidates you read and judged distinct."
3727
+ },
3728
+ {
3729
+ names: ["--over-cap"],
3730
+ description: "the run is blocked by this filing; the cap lets it through."
3731
+ },
3732
+ {
3733
+ names: ["--no-auto-ok"],
3734
+ description: "needs a person's judgement (Tier B / C); no `auto-ok`."
3735
+ },
3736
+ {
3737
+ names: ["--release"],
3738
+ description: "a consumer can use the change only once it is published. The filed issue becomes a blocker of the target's release issue (step 10)."
3739
+ }
3740
+ ]
3741
+ },
3742
+ "issue:fold-existing": { examples: [{ arguments: "assessment.json --json" }] },
3743
+ "issue:fold": {
3744
+ examples: [
3745
+ {
3746
+ arguments: '"First finding" "Second finding"',
3747
+ note: "fold | separate | no-fold-needed | undetermined"
3748
+ },
3749
+ { arguments: '"a" "b" --not-separable', note: "the pair is really one deliverable \u2192 fold" },
3750
+ { arguments: '"a" "b" --json', note: "the verdict and reason, machine-readable" }
3751
+ ],
3752
+ flags: [
3753
+ {
3754
+ names: ["--not-separable"],
3755
+ description: "declare the judgement half: the findings are one deliverable, so they fold whatever their size."
3756
+ },
3757
+ JSON_FLAG2
3758
+ ]
3759
+ },
3760
+ "issue:cite": {
3761
+ examples: [
3762
+ { arguments: "2220" },
3763
+ { arguments: "2220 1758 1252", note: CONCURRENT },
3764
+ { arguments: "45 --repo joshuafolkken/app-kit", note: "bare numbers in another repository" },
3765
+ { arguments: "joshuafolkken/app-kit#45 2220", note: "per-token owner/repo#N notation" }
3766
+ ],
3767
+ flags: [
3768
+ {
3769
+ names: [REPO2],
3770
+ description: "the repository for every bare number; a token written `owner/repo#N` overrides it for itself."
3771
+ }
3772
+ ]
3773
+ },
3774
+ "issue:comment": {
3775
+ examples: [
3776
+ {
3777
+ arguments: "2304 --body-file .claude/tmp/park.md",
3778
+ note: "the body from a file \u2014 no shell evaluates it"
3779
+ },
3780
+ {
3781
+ arguments: '2304 --body "a short note"',
3782
+ note: "inline, for a body with no backticks or $"
3783
+ }
3784
+ ],
3785
+ flags: [
3786
+ {
3787
+ names: ["--body <text>"],
3788
+ description: "the comment body, inline. Trimmed, with `\\n` expanded to a newline."
3789
+ },
3790
+ {
3791
+ names: [BODY_FILE],
3792
+ description: "read the body from a file, or from stdin with `-`. The safe form the rule steers every caller toward."
3793
+ }
3794
+ ]
3795
+ },
3796
+ "pkg:scout": {
3797
+ examples: [{ arguments: '"date formatting" --size 5' }],
3798
+ flags: [
3799
+ { names: ["--size <n>"], description: "how many candidates to fetch and rank (default 10)." }
3800
+ ]
3801
+ },
3802
+ "issue:lint": { examples: [{ arguments: ".claude/tmp/issue-body.md" }] },
3803
+ "defect:rate": {
3804
+ examples: [{ arguments: "", note: "the last 14 days" }, { arguments: "--days 30" }],
3805
+ flags: [
3806
+ {
3807
+ names: ["--days <n>"],
3808
+ description: "the window in whole days (default 14, at most 3650). Anything else prints the usage and exits 1."
3809
+ }
3810
+ ]
3811
+ },
3812
+ "issue:backlinks": { examples: [{ arguments: "2123" }] },
3813
+ "report:lint": { examples: [{ arguments: "< summary.md" }] },
3814
+ "stash:pop": {
3815
+ examples: [
3816
+ { arguments: '"backlogrun: parked #2028"' },
3817
+ {
3818
+ arguments: '"backlogrun: josh latest before lanes" --dir "$dir"',
3819
+ note: "into a lane's work tree"
3820
+ }
3821
+ ],
3822
+ flags: [
3823
+ {
3824
+ names: ["--dir <path>"],
3825
+ description: "applies the pop in that work tree (`git -C <path>`); without it the pop lands in the current checkout. The stack is shared, so it reads the same either way."
3826
+ }
3827
+ ]
3828
+ }
3829
+ };
3830
+
3831
+ // scripts/josh/josh-usage-review.ts
3832
+ var MACHINE_READABLE = "the verdict and the reason, machine-readable";
3833
+ var LEVEL_ONLY = "--level-only";
3834
+ var JSON_FLAG3 = "--json";
3835
+ var CHECK = "--check";
3836
+ var ROUND_1_CLOSED = "--round-1-closed";
3837
+ var REVIEW_USAGE = {
3838
+ "review:brief": {
3839
+ examples: [
3840
+ { arguments: "", note: "round 1" },
3841
+ { arguments: "--round 2", note: "verification pass, scoped to the fix delta" },
3842
+ { arguments: LEVEL_ONLY, note: "the level alone (pre-commit review)" },
3843
+ { arguments: "--level-only --json", note: "level and reason, machine-readable" }
3844
+ ],
3845
+ flags: [
3846
+ {
3847
+ names: ["--round 2"],
3848
+ description: "target is round 1's fixes and nothing else (widened back to the whole change when the record's change base no longer matches)."
3849
+ },
3850
+ {
3851
+ names: [LEVEL_ONLY],
3852
+ description: "print the level alone (level to stdout, reason to stderr); bypasses the scoped-green refusal."
3853
+ },
3854
+ {
3855
+ names: ["--staged", "--json"],
3856
+ description: "the staged diff; machine-readable level and reason."
3857
+ }
3858
+ ]
3859
+ },
3860
+ "review:round2": {
3861
+ examples: [
3862
+ { arguments: "", note: "\u2192 required" },
3863
+ { arguments: ROUND_1_CLOSED, note: "\u2192 required | skip" },
3864
+ { arguments: JSON_FLAG3, note: MACHINE_READABLE }
3865
+ ],
3866
+ flags: [
3867
+ {
3868
+ names: [ROUND_1_CLOSED],
3869
+ description: "the caller states every round-1 High/Medium finding closed and none was filed or deferred; its absence answers `required`."
3870
+ },
3871
+ { names: [JSON_FLAG3], description: "machine-readable verdict and reason." }
3872
+ ]
3873
+ },
3874
+ disposition: { examples: [{ arguments: "<path...>", note: "\u2192 runtime | non-runtime" }] },
3875
+ "review:attest": {
3876
+ examples: [
3877
+ { arguments: "<nonce>", note: "run by the review, from the checkout it read" },
3878
+ { arguments: CHECK, note: "run by the run, before it acts on the review" }
3879
+ ],
3880
+ flags: [
3881
+ { names: ["<nonce>"], description: "attest the checkout the review read." },
3882
+ {
3883
+ names: [CHECK],
3884
+ description: "verify before acting; `josh followup` asks again before merging."
3885
+ }
3886
+ ]
3887
+ },
3888
+ delegate: {
3889
+ examples: [
3890
+ { arguments: "gate-fix", note: "\u2192 delegate" },
3891
+ { arguments: "review", note: "\u2192 keep" },
3892
+ { arguments: "--list", note: "the enumeration, and what was rejected and why" }
3893
+ ]
3894
+ },
3895
+ fanout: {
3896
+ examples: [
3897
+ { arguments: "scripts/a.ts,scripts/a.test.ts scripts/b.ts", note: "\u2192 parallel" },
3898
+ {
3899
+ arguments: "scripts/a.ts,shared.ts scripts/b.ts,shared.ts",
3900
+ note: "\u2192 serial (units share shared.ts)"
3901
+ },
3902
+ { arguments: "scripts/a.ts", note: "\u2192 serial (fewer than two units)" }
3903
+ ]
3904
+ },
3905
+ "split:assess": {
3906
+ examples: [
3907
+ { arguments: "", note: "\u2192 single" },
3908
+ { arguments: JSON_FLAG3, note: MACHINE_READABLE }
3909
+ ]
3910
+ },
3911
+ "oracle:list": { examples: [{ arguments: "" }] },
3912
+ "clone:scan": { examples: [{ arguments: "" }] },
3913
+ cases: {
3914
+ examples: [
3915
+ { arguments: "scripts/a.ts scripts/b.ts", note: "the boundaries these paths cross" }
3916
+ ]
3917
+ }
3918
+ };
3919
+
3920
+ // scripts/josh/josh-usage-run.ts
3921
+ var ISSUE = { arguments: "<N>" };
3922
+ var OUTPUT = "--output <path>";
3923
+ var OWNER = "--owner <pid>";
3924
+ var REPO_FLAG2 = { names: ["--repo <owner/name>"], description: "a child in another repository." };
3925
+ var RUN_USAGE = {
3926
+ "run:hold": {
3927
+ examples: [
3928
+ { arguments: "1091", note: "claim for issue 1091" },
3929
+ { arguments: "1091 --fullrun", note: "claim as `fullrun #1091` (what `run:entry` runs)" }
3930
+ ],
3931
+ flags: [
3932
+ { names: ["--fullrun"], description: "marks the record as `fullrun #N`'s." },
3933
+ {
3934
+ names: ["--halfrun-stop"],
3935
+ description: "marks a `halfrun` stop for `run:entry` to adopt."
3936
+ },
3937
+ {
3938
+ names: ["--prrun-stop"],
3939
+ description: "marks a `prrun` stop with the commit its pull request is on."
3940
+ }
3941
+ ]
3942
+ },
3943
+ "run:release": {
3944
+ examples: [
3945
+ { arguments: "1091", note: "release this run's own record" },
3946
+ { arguments: "--force", note: "clear a record left by a run that has ended" }
3947
+ ],
3948
+ flags: [
3949
+ {
3950
+ names: ["--force"],
3951
+ description: "removes a record this run did not write, clearing another run's stale claim."
3952
+ }
3953
+ ]
3954
+ },
3955
+ "run:tidy": { examples: [{ arguments: "" }] },
3956
+ "run:carry": {
3957
+ examples: [
3958
+ { arguments: '--begin "backlogrun --max 5" --owner "$PPID"' },
3959
+ { arguments: "--json", note: "read the record back in a resumed session" },
3960
+ { arguments: '--cut --owner "$PPID"', note: "hand the record off before a cut" },
3961
+ {
3962
+ arguments: '--retrospective --summary "0 filed; dropped #2240 already merged" --owner "$PPID"',
3963
+ note: "close the end-of-run retrospective"
3964
+ },
3965
+ {
3966
+ arguments: '--resume "backlogrun --max 5" --owner "$PPID"',
3967
+ note: "adopt a record no cut handed off"
3968
+ },
3969
+ {
3970
+ arguments: '--end --stopped "epic #2126: everything is blocked behind parked #2118"',
3971
+ note: "end + push"
3972
+ }
3973
+ ],
3974
+ flags: [
3975
+ {
3976
+ names: [OWNER],
3977
+ description: "the long-lived process spending the budget (`$PPID` under a loop); required by counts and `--begin` / `--resume`. A live PID stays `busy` if probes fail."
3978
+ },
3979
+ { names: ["--done <issue>"], description: "shrinks a named-issue run's `remaining` list." },
3980
+ {
3981
+ names: ["--merged <issue>"],
3982
+ description: "names the merged issue and counts it once against `merged_issues`, so a merge `run:merge` already recorded is not counted twice."
3983
+ },
3984
+ { names: ["--filed", "--cut"], description: "increments, never totals." },
3985
+ {
3986
+ names: ["--retrospective"],
3987
+ description: "marks the end-of-run retrospective run, once per invocation, and requires `--summary <text>` \u2014 the same close writes that result as one `retrospective` event on the run's event stream (best-effort), so a run that filed zero improvements reads apart from one whose retrospective never ran. Either flag without the other is refused."
3988
+ },
3989
+ {
3990
+ names: ["--stopped <reason>"],
3991
+ description: "rides on `--end`: the run stopped rather than finished, so one \u23F8\uFE0F confirmation is pushed with the reason."
3992
+ },
3993
+ {
3994
+ names: ["--end"],
3995
+ description: "over a live record flushes pending ledger lines once (a failed flush goes to stderr and the record is still cleared); first collects each lane the run launched whose issue merged, exactly as `run:merge` would; keeps the run it removes as the last ended run \u2014 its invocation, start and end, one record overwritten by the next `--end` \u2014 so `run:board` can still draw a finished run."
3996
+ }
3997
+ ]
3998
+ },
3999
+ "run:add": {
4000
+ examples: [
4001
+ { arguments: "101 102", note: "ahead of the rest of the queue (`#N` works too)" },
4002
+ { arguments: "101 --no-priority", note: "at the end of the queue" }
4003
+ ]
4004
+ },
4005
+ "run:wake": {
4006
+ examples: [
4007
+ { arguments: "--start", note: "launch the detached supervisor" },
4008
+ { arguments: "--list", note: "supervisor state, cut/wake counts, latest progress line" },
4009
+ { arguments: "--stop", note: "stop the supervisor alone" },
4010
+ { arguments: "--loop --interval 30", note: "run the loop body in the foreground" }
4011
+ ]
4012
+ },
4013
+ "run:cut": {
4014
+ examples: [
4015
+ { arguments: "1839", note: "take the cut and hand it to a fresh process" },
4016
+ {
4017
+ arguments: "--impl 1839 --handoff .claude/tmp/handoff-1839.json",
4018
+ note: "cut mid-implementation, carrying the instruction"
4019
+ },
4020
+ { arguments: "--resume 1839", note: "a fresh process's entry check" },
4021
+ { arguments: "--end", note: "clear the record" }
4022
+ ]
4023
+ },
4024
+ "run:liveness": {
4025
+ examples: [
4026
+ { arguments: "1169 --output <path>" },
4027
+ { arguments: "1169 --output <path> --window 45 --gap 2 --repo joshuafolkken/app-kit" }
4028
+ ],
4029
+ flags: [
4030
+ {
4031
+ names: [OUTPUT],
4032
+ description: "absolute, under the home or temp directory; a symlink is followed and size compared as well as mtime."
4033
+ },
4034
+ {
4035
+ names: ["--process alive | none"],
4036
+ description: "overrides the process trace, which the command otherwise reads itself: the child's own command line (`fullrun #<N>`) or its detached ship supervisor."
4037
+ },
4038
+ {
4039
+ names: ["--window <min>"],
4040
+ description: "silent window the file must be frozen for (default 30)."
4041
+ },
4042
+ { names: ["--gap <sec>"], description: "spacing between samples (default 5)." },
4043
+ REPO_FLAG2
4044
+ ]
4045
+ },
4046
+ "run:ending": {
4047
+ examples: [
4048
+ { arguments: "2118 --output <path>" },
4049
+ { arguments: "2118 --output <path> --repo joshuafolkken/app-kit" }
4050
+ ],
4051
+ flags: [
4052
+ {
4053
+ names: [OUTPUT],
4054
+ description: "the child's transcript, absolute and under the home or temp directory (validated the same way `run:liveness --output` is)."
4055
+ },
4056
+ REPO_FLAG2
4057
+ ]
4058
+ },
4059
+ "run:prep": { examples: [ISSUE] },
4060
+ "run:entry": {
4061
+ examples: [
4062
+ { arguments: "<N>", note: "a `fullrun`" },
4063
+ { arguments: "<N> --to halfrun", note: "a run that goes as far as `halfrun`" }
4064
+ ]
4065
+ },
4066
+ "run:status": {
4067
+ examples: [ISSUE, { arguments: "<N> --repo <owner/repo>", note: "a cross-repo child" }]
4068
+ },
4069
+ "run:next": { examples: [ISSUE] },
4070
+ "run:step": {
4071
+ examples: [
4072
+ { arguments: "<N>", note: "the run's next action for issue <N>" },
4073
+ { arguments: "", note: "the `backlogrun` parent's next action" }
4074
+ ]
4075
+ },
4076
+ "run:merge": {
4077
+ examples: [
4078
+ {
4079
+ arguments: '<N> --epic <E> --repo <owner/repo> --owner "$PPID" --output <path>',
4080
+ note: "prints the next child number, or a verdict"
4081
+ },
4082
+ { arguments: '<N> --owner "$PPID"', note: "backlog offer (no epic)" }
4083
+ ],
4084
+ flags: [
4085
+ {
4086
+ names: ["--epic <E>", "--repo <owner/repo>"],
4087
+ description: "offer the epic's next children; omit both for the opted-in backlog."
4088
+ },
4089
+ {
4090
+ names: [OWNER],
4091
+ description: "the parent's process, so the carry count respects the ownership guard."
4092
+ },
4093
+ {
4094
+ names: [OUTPUT],
4095
+ description: "the transcript the outage split reads; absent, it is off."
4096
+ }
4097
+ ]
4098
+ },
4099
+ "run:review": {
4100
+ examples: [
4101
+ {
4102
+ arguments: "",
4103
+ note: "detach the gate, print the brief; then launch the /code-review subagent"
4104
+ },
4105
+ { arguments: "--join", note: "after the review returns: join the gate, check its verdict" }
4106
+ ]
4107
+ },
4108
+ "run:tail": {
4109
+ examples: [
4110
+ { arguments: "<N>", note: "the closed issue" },
4111
+ { arguments: "<N> <follow-up-N>", note: "and a follow-up filed this run" }
4112
+ ]
4113
+ },
4114
+ ship: {
4115
+ examples: [
4116
+ { arguments: '"<title> #<N>" --body-file <evidence.md>' },
4117
+ { arguments: '"<title> #<N>" --review', note: "both review rounds inside the ship" },
4118
+ { arguments: '--detach "<title> #<N>"', note: "hand the ship to a supervisor" },
4119
+ { arguments: "--log <N>", note: "a detached ship's log" }
4120
+ ],
4121
+ flags: [
4122
+ {
4123
+ names: ["--body-file <path>"],
4124
+ description: "the PR body carrying the live-execution evidence `followup` gates on; forwarded to `git -y` alone."
4125
+ },
4126
+ { names: ["--notify-message <text>"], description: "forwarded to `followup` alone." },
4127
+ {
4128
+ names: ["--review"],
4129
+ description: "runs review round 1 beside the gate, and round 2 after the commit when `review:round2` requires it; a High or an unfixed Medium stops the ship."
4130
+ },
4131
+ {
4132
+ names: ["--detach"],
4133
+ description: "hands the ship to a supervisor (implied in a lane child); a stop emits `ship-stop` (`--log <N>`). The preflight, type check, unused exports, cspell and guard tests run before the hand-off; a stop returns to the same session."
4134
+ }
4135
+ ]
4136
+ },
4137
+ "run:report": {
4138
+ examples: [{ arguments: "", note: "print the report; the same text josh notify sends" }]
4139
+ },
4140
+ "run:event": {
4141
+ examples: [
4142
+ { arguments: "--append <kind> <text>", note: "append one event; prints its position" },
4143
+ { arguments: "--from <position>", note: "every event after <position>, in order (JSON)" },
4144
+ { arguments: "--follow <position>", note: "new events, waiting for one; position on stderr" },
4145
+ { arguments: "--watch [<position>]", note: "every new event, rendered, until interrupted" },
4146
+ { arguments: "--last", note: "the newest event alone" }
4147
+ ]
4148
+ }
4149
+ };
4150
+
4151
+ // scripts/josh/josh-usage-session.ts
4152
+ var BARE = { arguments: "" };
4153
+ var PATH_DIR = "--path <dir>";
4154
+ var SESSION_USAGE = {
4155
+ "run:progress": {
4156
+ examples: [
4157
+ { arguments: "--output <path>" },
4158
+ { arguments: "--once", note: "five labelled lines now, whatever the clock says" },
4159
+ { arguments: "--interval 20 --repo joshuafolkken/app-kit --hours 4" }
4160
+ ],
4161
+ flags: [
4162
+ {
4163
+ names: ["--mark"],
4164
+ description: "record that a real report happened without printing a line (keeps the last line for `run:wake --list`)."
4165
+ },
4166
+ {
4167
+ names: ["--path"],
4168
+ description: 'print the ambient heartbeat log\'s path and exit. Every heartbeat line is mirrored there, so `tail -F "$(pnpm josh run:progress --path)"` follows the run across a `backlogrun` session cut. Reads no run state, so it answers in a lane child too.'
4169
+ },
4170
+ {
4171
+ names: ["--interval <min>"],
4172
+ description: "silence interval (default 20; also `JOSH_PROGRESS_INTERVAL_MINUTES`, then `josh.progress_interval_minutes`)."
4173
+ },
4174
+ {
4175
+ names: ["--wait"],
4176
+ description: "start once in the background: each report goes to the event stream and ambient log, never stdout; exits only on an arrival, `josh followup` ending the run, or `--hours`."
4177
+ },
4178
+ {
4179
+ names: ["--hours <n>"],
4180
+ description: "how long the watcher lives (default 8 with `--wait`, 1 otherwise)."
4181
+ },
4182
+ { names: ["--repo <owner/name>"], description: "scopes the read." }
4183
+ ]
4184
+ },
4185
+ "run:board": {
4186
+ examples: [
4187
+ { arguments: "", note: "redraw until interrupted" },
4188
+ { arguments: "--once", note: "one frame, then exit" },
4189
+ { arguments: "--chat", note: "one frame for a chat, recorded as a progress report" },
4190
+ {
4191
+ arguments: "--every 5",
4192
+ note: "the --chat frame as a \u{1F4CA} Progress Telegram every 5 minutes, until the run ends"
4193
+ }
4194
+ ],
4195
+ flags: [
4196
+ {
4197
+ names: ["--chat"],
4198
+ description: "answers a `backlogrun` progress question: the frame `--once` draws, with only `\u{1F9E0}` on the machine line and no escapes; it records the report as `run:progress --mark` does."
4199
+ },
4200
+ {
4201
+ names: ["--every <minutes>"],
4202
+ description: "sends the `--chat` frame as a `\u{1F4CA} Progress` Telegram now and every `<minutes>` until the run ends \u2014 only when a person asks for it."
4203
+ }
4204
+ ]
4205
+ },
4206
+ "run:watcher:guard": { examples: [BARE] },
4207
+ "run:stranded": { examples: [BARE] },
4208
+ "lane:open": { examples: [{ arguments: "1490", note: "prints the lane directory on stdout" }] },
4209
+ "lane:close": {
4210
+ examples: [{ arguments: "1490" }, { arguments: "--all" }],
4211
+ flags: [{ names: ["--all"], description: "closes every lane." }]
4212
+ },
4213
+ "lane:list": { examples: [BARE] },
4214
+ "lane:prune": { examples: [BARE] },
4215
+ "lane:output": {
4216
+ examples: [
4217
+ { arguments: "1713 /abs/path/to/agent-7.jsonl", note: "record it; prints the path back" },
4218
+ { arguments: "1713", note: "read it; prints the path, or `none`" }
4219
+ ]
4220
+ },
4221
+ "lane:dispatch": {
4222
+ examples: [
4223
+ {
4224
+ arguments: "1749",
4225
+ note: "prints the child's pid; a refusal is an empty capture and exit 1"
4226
+ }
4227
+ ],
4228
+ flags: [
4229
+ {
4230
+ names: ["JOSH_{SCHEDULER,WORKER,REVIEWER}_MODEL"],
4231
+ description: "Claude Code role overrides; Anthropic defaults are respectively `claude-opus-5-5`, `claude-opus-5-5`, and `claude-opus-5-5`. Codex keeps its provider-specific model."
4232
+ },
4233
+ {
4234
+ names: ["JOSH_{SCHEDULER,WORKER,REVIEWER}_EFFORT"],
4235
+ description: "role effort overrides for either provider; defaults are `medium`, `medium`, and `high`."
4236
+ },
4237
+ {
4238
+ names: ["JOSH_REVIEWER_VERIFICATION_{MODEL,EFFORT}"],
4239
+ description: "review round 2 and later (the verification pass); Anthropic defaults are `claude-sonnet-5-5` / `high`, falling back to `JOSH_REVIEWER_*`."
4240
+ }
4241
+ ]
4242
+ },
4243
+ "lane:await": {
4244
+ examples: [
4245
+ { arguments: "1749 1750", note: "block until either lane completes" },
4246
+ {
4247
+ arguments: '1749 1750 --owner "$PPID"',
4248
+ note: "reclaim the carry record for a resumed conversation first"
4249
+ }
4250
+ ]
4251
+ },
4252
+ "lane:launch": {
4253
+ examples: [
4254
+ { arguments: "1749", note: "every lane after the first" },
4255
+ {
4256
+ arguments: '1749 --stash "backlogrun: josh latest before lanes"',
4257
+ note: "the first lane only"
4258
+ }
4259
+ ]
4260
+ },
4261
+ "lane:sample": { examples: [{ arguments: "", note: "one machine-load reading" }] },
4262
+ "lane:stats": {
4263
+ examples: [{ arguments: "--period 7", note: "a 7-day period's stage medians and maxima" }]
4264
+ },
4265
+ cost: {
4266
+ examples: [
4267
+ {
4268
+ arguments: "--cut",
4269
+ note: "compare billed input per request with the shared 135,000 context-cut threshold"
4270
+ },
4271
+ { arguments: "--over <tokens>", note: "compare with an explicit limit" },
4272
+ { arguments: `--cut ${PATH_DIR}`, note: "Anthropic project or current OpenAI worktree" }
4273
+ ],
4274
+ flags: [
4275
+ {
4276
+ names: [PATH_DIR],
4277
+ description: "selects another Anthropic project; OpenAI accepts only linked checkouts of the current project."
4278
+ }
4279
+ ]
4280
+ },
4281
+ "doc:section": {
4282
+ examples: [
4283
+ { arguments: '<file.md> "<heading>"' },
4284
+ { arguments: 'backlogrun.md "The hand-off"' }
4285
+ ]
4286
+ },
4287
+ "read:set": {
4288
+ examples: [
4289
+ { arguments: "", note: "every entry point" },
4290
+ { arguments: "backlogrun", note: "one of them" },
4291
+ { arguments: "backlogrun --json" }
4292
+ ]
4293
+ },
4294
+ "doc:read": {
4295
+ examples: [
4296
+ { arguments: "CLAUDE.md", note: "under the cap: prints the document" },
4297
+ { arguments: "backlogrun.md", note: "over the cap: prints a directive, no content" }
4298
+ ]
4299
+ },
4300
+ "read:files": {
4301
+ examples: [
4302
+ { arguments: "a.ts b.ts c.ts", note: "under the cap: prints each file under its own header" },
4303
+ {
4304
+ arguments: "a.ts big.ts",
4305
+ note: "over the cap: prints a directive to Read them in one turn, no content"
4306
+ }
4307
+ ]
4308
+ },
4309
+ "edit:files": {
4310
+ examples: [
4311
+ {
4312
+ arguments: "plan.txt",
4313
+ note: "applies every block in the plan, one line per edit reporting the outcome"
4314
+ },
4315
+ { arguments: "- <<'EDITS'", note: "plan from stdin, then EDITS" }
4316
+ ]
4317
+ },
4318
+ time: {
4319
+ examples: [
4320
+ { arguments: "", note: "the last run tree, wall clock and cost by role" },
4321
+ { arguments: "--run", note: "the same run-tree scope, named explicitly" },
4322
+ { arguments: "--json", note: "the same figures, machine-readable" },
4323
+ { arguments: PATH_DIR, note: "read another project's transcripts from this checkout" }
4324
+ ],
4325
+ flags: [
4326
+ {
4327
+ names: ["--run"],
4328
+ description: '(default) the whole run tree, wall clock led beside dollars. It is the only scope; any other flag is a usage error (exit 1). History: `docs/maintainers/josh-commands-run-rationale.md` \u2192 "`josh cost` and `josh time` lost their report scopes".'
4329
+ },
4330
+ {
4331
+ names: [PATH_DIR],
4332
+ description: "aim the read at another project (absolute path); keeps the `cwd` behavior when absent."
4333
+ },
4334
+ { names: ["--json"], description: "the run tree, machine-readable." }
4335
+ ]
4336
+ },
4337
+ "cost:audit": {
4338
+ examples: [
4339
+ { arguments: "", note: "the last 7 days against the 7 before" },
4340
+ { arguments: "--period 30", note: "the last 30 days against the 30 before" }
4341
+ ],
4342
+ flags: [
4343
+ {
4344
+ names: ["--period <days>"],
4345
+ description: "the length of each period, a positive whole number of days (default 7)."
4346
+ }
4347
+ ]
4348
+ },
4349
+ retrospective: {
4350
+ examples: [{ arguments: "", note: "the digest for the run this checkout carries" }]
4351
+ },
4352
+ eval: {
4353
+ examples: [
4354
+ { arguments: "", note: "every scenario" },
4355
+ { arguments: "consult-not-execute", note: "one scenario by name" }
4356
+ ],
4357
+ flags: [
4358
+ { names: ["JOSH_EVAL_MODEL"], description: "the model to run against (default `sonnet`)." },
4359
+ {
4360
+ names: ["JOSH_EVAL_CONCURRENCY"],
4361
+ description: "how many sessions run at once (default `5`); a non-positive-integer value is refused."
4362
+ }
4363
+ ]
4364
+ }
4365
+ };
4366
+
4367
+ // scripts/josh/josh-usage-workflow.ts
4368
+ var PR_TITLE = '"PR title #N"';
4369
+ var ISSUE_URL = '--issue-url "https://..."';
4370
+ var REVIEW_ISSUE = "--issue 2325";
4371
+ var CHECK2 = "--check";
4372
+ var JSON_FLAG4 = "--json";
4373
+ var BODY_FILE_NOTE = "(`-` reads stdin); use this whenever the body carries a backtick or `$`. Passing both forms is refused.";
4374
+ var WORKFLOW_USAGE = {
4375
+ followup: {
4376
+ examples: [
4377
+ { arguments: PR_TITLE, note: "merges (default)" },
4378
+ { arguments: String.raw`${PR_TITLE} --notify-message "Implemented X:\n- change 1"` },
4379
+ { arguments: `${PR_TITLE} --notify-message-file completion.md` },
4380
+ { arguments: `${PR_TITLE} --ai-review-ignore-reason "false positive"` },
4381
+ { arguments: `${PR_TITLE} --no-merge`, note: "do the work, leave the PR open" }
4382
+ ],
4383
+ flags: [
4384
+ {
4385
+ names: ["--no-merge"],
4386
+ description: "do the follow-up work but do not merge; leaves the PR open (the only flag that stops the merge). `--merge` is a deprecated no-op. A PR already merged by hand skips the CI wait, the AI-review scan and the merge and runs only the post-merge tail; a completion report already on the Issue is not posted again."
4387
+ },
4388
+ {
4389
+ names: ["--notify-message"],
4390
+ description: "inline completion body; `\\n` expands to newlines."
4391
+ },
4392
+ {
4393
+ names: ["--notify-message-file"],
4394
+ description: `read the completion body from a file ${BODY_FILE_NOTE}`
4395
+ },
4396
+ {
4397
+ names: ["--ai-review-ignore-reason"],
4398
+ description: "reason to dismiss an AI-review finding."
4399
+ }
4400
+ ]
4401
+ },
4402
+ notify: {
4403
+ examples: [
4404
+ { arguments: String.raw`--task-type planning ${ISSUE_URL} --body="- bullet 1\n- bullet 2"` },
4405
+ {
4406
+ arguments: `--task-type confirmation ${ISSUE_URL} --body-file r.md --recommended "Go" --alternative "Wait"`
4407
+ }
4408
+ ],
4409
+ flags: [
4410
+ {
4411
+ names: ["--task-type"],
4412
+ description: "one of `planning` \u{1F4CB}, `completion` \u2705, `failure` \u274C, `warning` \u26A0\uFE0F, `kickoff_retry` \u{1F504}, `confirmation` \u23F8\uFE0F. Do not send `completion` or `warning` by hand \u2014 `josh followup` sends them."
4413
+ },
4414
+ {
4415
+ names: ["--body", "--body=$'\u2026'"],
4416
+ description: "inline body; use `--body=$'\u2026'` when it starts with `-`."
4417
+ },
4418
+ { names: ["--body-file"], description: `read the body from a file ${BODY_FILE_NOTE}` },
4419
+ {
4420
+ names: ["--repo-name", "--issue-url", "--pr-url"],
4421
+ description: "header repository, resolved in that order, then the working directory. The issue title is read from `--issue-url`."
4422
+ },
4423
+ {
4424
+ names: ["--recommended", "--alternative"],
4425
+ description: "the choices appended under `JOSH_SESSION_LANG` headings; `--recommended` is required for `confirmation`, `--alternative` is repeatable."
4426
+ }
4427
+ ]
4428
+ },
4429
+ "observations:flush": { examples: [{ arguments: "" }] },
4430
+ "measure:rerun": { examples: [{ arguments: "2212" }] },
4431
+ "review:record": {
4432
+ examples: [
4433
+ { arguments: `${REVIEW_ISSUE} bug-risks:medium:src/foo.ts:42 tests:low:a.test.ts` },
4434
+ { arguments: REVIEW_ISSUE, note: "a zero-finding round \u2014 records one `none` line" },
4435
+ {
4436
+ arguments: `${REVIEW_ISSUE} --comment`,
4437
+ note: "a round that passed after the PR opened \u2014 an Issue comment, no append"
4438
+ },
4439
+ { arguments: `${CHECK2} ${REVIEW_ISSUE}`, note: "the merge gate: was it recorded?" }
4440
+ ]
4441
+ },
4442
+ bump: { examples: [{ arguments: "major" }, { arguments: "minor" }, { arguments: "patch" }] },
4443
+ release: {
4444
+ examples: [
4445
+ { arguments: "" },
4446
+ { arguments: "--dry-run", note: "count and report, write nothing" }
4447
+ ],
4448
+ flags: [
4449
+ {
4450
+ names: ["--dry-run"],
4451
+ description: "count and report only; writes nothing, fetches nothing, and creates no work tree."
4452
+ },
4453
+ {
4454
+ names: ["--help", "-h"],
4455
+ description: "print the usage line and exit 0 without fetching or releasing. Any other argument is refused with the usage line and a non-zero exit, before anything is fetched."
4456
+ }
4457
+ ]
4458
+ },
4459
+ "release:scope": {
4460
+ examples: [
4461
+ { arguments: "", note: "\u2192 required | skip | unknown" },
4462
+ { arguments: "--json", note: '{"scope":"\u2026","reason":"\u2026"} on one line' }
4463
+ ]
4464
+ },
4465
+ "ruleset:check": {
4466
+ examples: [
4467
+ { arguments: "", note: "report only" },
4468
+ { arguments: "--apply", note: "add the missing checks to the existing rule" }
4469
+ ],
4470
+ flags: [
4471
+ {
4472
+ names: ["--apply"],
4473
+ description: "append the missing checks to the ruleset's required-checks rule (or to the branch protection's contexts). It never creates a ruleset: with none, create one under Settings \u2192 Rules \u2192 Rulesets, then rerun. Writing a repository setting needs admin access, so only this flag writes \u2014 `josh sync` and `josh doctor` never do."
4474
+ }
4475
+ ]
4476
+ },
4477
+ "audit:provision": {
4478
+ examples: [
4479
+ { arguments: "", note: "no-op if a scanner is already present" },
4480
+ { arguments: "--force", note: "ignore backoff, allow a longer download" }
4481
+ ],
4482
+ flags: [
4483
+ {
4484
+ names: ["--force"],
4485
+ description: "ignore the 6-hour failure backoff and allow a longer download window; the command to run after fixing a network problem."
4486
+ }
4487
+ ]
4488
+ },
4489
+ "reconcile-templates": {
4490
+ examples: [
4491
+ { arguments: "", note: "regenerate copy templates + record tripwire hashes" },
4492
+ { arguments: CHECK2, note: "verify templates are in sync; non-zero on drift" }
4493
+ ],
4494
+ flags: [
4495
+ {
4496
+ names: [CHECK2],
4497
+ description: "verify only; exits non-zero on drift. A pre-commit hook runs this when a tracked source or copy template is staged."
4498
+ }
4499
+ ]
4500
+ },
4501
+ "pre-push-unit": { examples: [{ arguments: "" }] },
4502
+ "reserved-run": {
4503
+ examples: [
4504
+ { arguments: "2 -- pnpm install", note: "hold 2 cores while installing" },
4505
+ { arguments: "1 -- pnpm josh audit", note: "hold 1 core while scanning" }
4506
+ ]
4507
+ },
4508
+ "pre-commit-type-check": { examples: [{ arguments: "" }] },
4509
+ "pr:classification": {
4510
+ examples: [{ arguments: "", note: "run by CI on a pull request; fails without one label" }]
4511
+ },
4512
+ "observation:record": {
4513
+ examples: [
4514
+ { arguments: "flaky-e2e d1 'tests/e2e/login.test.ts' 'times out on CI only'" },
4515
+ {
4516
+ arguments: "flaky-e2e d1 'src/app.ts' 'fails there too' --checkout ~/Development/app",
4517
+ note: "another repository's ledger"
4518
+ }
4519
+ ],
4520
+ flags: [
4521
+ {
4522
+ names: ["--checkout <path>"],
4523
+ description: "count and append in that repository's `.josh/observations/` instead."
4524
+ }
4525
+ ]
4526
+ },
4527
+ "release:github": {
4528
+ examples: [
4529
+ { arguments: "", note: "run by CI with GH_TOKEN, RELEASE_TAG and GITHUB_REPOSITORY set" }
4530
+ ]
4531
+ },
4532
+ "latest:scope": {
4533
+ examples: [
4534
+ { arguments: "", note: "required or skip, the reason on stderr" },
4535
+ { arguments: "--record", note: "note a finished josh latest run" },
4536
+ { arguments: JSON_FLAG4, note: "the verdict and the reason as JSON" }
4537
+ ],
4538
+ flags: [
4539
+ { names: ["--record"], description: "write the completion record `josh latest` ends with." },
4540
+ { names: [JSON_FLAG4], description: "emit the verdict and its reason as JSON." }
4541
+ ]
4542
+ },
4543
+ "latest:guard": { examples: [{ arguments: "", note: "refuses inside a lane" }] },
4544
+ "prevent-main-commit": { examples: [{ arguments: "", note: "run by the pre-commit hook" }] },
4545
+ "check-commit-message": {
4546
+ examples: [{ arguments: ".git/COMMIT_EDITMSG", note: "run by the commit-msg hook" }]
4547
+ },
4548
+ "secretlint-scan": {
4549
+ examples: [{ arguments: "src/app.ts .env.example", note: "the staged paths" }]
4550
+ },
4551
+ "pre-commit-lint": {
4552
+ examples: [
4553
+ { arguments: "cspell src/app.ts" },
4554
+ { arguments: "prettier src/app.ts" },
4555
+ { arguments: "eslint src/app.ts" }
4556
+ ]
4557
+ }
4558
+ };
4559
+
4560
+ // scripts/josh/command-usage.ts
4561
+ var USAGE_PAGE_TABLES = {
4562
+ "docs/josh-commands.md": [DEVELOPER_USAGE],
4563
+ "docs/josh-commands-run.md": [RUN_USAGE, SESSION_USAGE],
4564
+ "docs/josh-commands-backlog.md": [ISSUE_USAGE, BACKLOG_USAGE, REVIEW_USAGE],
4565
+ "docs/josh-commands-automation.md": [AUTOMATION_USAGE, WORKFLOW_USAGE, HOOK_USAGE]
4566
+ };
4567
+ var COMMAND_USAGE = Object.fromEntries(
4568
+ Object.values(USAGE_PAGE_TABLES).flat().flatMap((table) => Object.entries(table))
4569
+ );
4570
+ var NOTE_GAP = 3;
4571
+ function usage_for(name) {
4572
+ return Object.hasOwn(COMMAND_USAGE, name) ? COMMAND_USAGE[name] : void 0;
4573
+ }
4574
+ function example_line(name, example) {
4575
+ const command = example.arguments ? `pnpm josh ${name} ${example.arguments}` : `pnpm josh ${name}`;
4576
+ return { command, note: example.note };
4577
+ }
4578
+ function format_examples(name, usage) {
4579
+ const lines = usage.examples.map((example) => example_line(name, example));
4580
+ const width = Math.max(...lines.map((line) => line.command.length)) + NOTE_GAP;
4581
+ const rendered = lines.map(
4582
+ (line) => line.note === void 0 ? line.command : `${line.command.padEnd(width)}# ${line.note}`
4583
+ );
4584
+ return ["```bash", ...rendered, "```"].join("\n");
4585
+ }
4586
+ function code_span(text) {
4587
+ return `\`${text}\``;
4588
+ }
4589
+ function format_flags(flags) {
4590
+ const items = flags.map(
4591
+ (flag) => `- ${flag.names.map((spelling) => code_span(spelling)).join(" / ")} \u2014 ${flag.description}`
4592
+ );
4593
+ return ["**Options:**", "", ...items].join("\n");
4594
+ }
4595
+ function format_usage(name) {
4596
+ const usage = usage_for(name);
4597
+ if (usage === void 0) return void 0;
4598
+ const flags = usage.flags === void 0 ? [] : [format_flags(usage.flags)];
4599
+ return [format_examples(name, usage), ...flags].join("\n\n");
4600
+ }
4601
+ function format_command_help(input) {
4602
+ const name = canonical_command(input);
4603
+ const entry = Object.hasOwn(COMMAND_MAP, name) ? COMMAND_MAP[name] : void 0;
4604
+ if (entry === void 0) return void 0;
4605
+ const [synopsis] = entry.reference;
4606
+ const head = synopsis ? `josh ${name} ${synopsis}` : `josh ${name}`;
4607
+ const usage = format_usage(name);
4608
+ return [head, "", entry.description, ...usage === void 0 ? [] : ["", usage]].join("\n");
4609
+ }
4610
+ function page_commands(page) {
4611
+ const tables = Object.hasOwn(USAGE_PAGE_TABLES, page) ? USAGE_PAGE_TABLES[page] : void 0;
4612
+ return (tables ?? []).flatMap((table) => Object.keys(table));
4613
+ }
4614
+ var command_usage = {
4615
+ USAGE_PAGES: Object.keys(USAGE_PAGE_TABLES),
4616
+ format_command_help,
4617
+ format_usage,
4618
+ page_commands
4619
+ };
4620
+
4621
+ // scripts/josh/josh-logic.ts
4622
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
4623
+ import path15 from "node:path";
4624
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
4625
+
4626
+ // scripts/build/local-bin.ts
4627
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
4628
+ import { createRequire } from "node:module";
4629
+ import path11 from "node:path";
4630
+
4631
+ // scripts/lib/ancestor-directories.ts
4632
+ import path10 from "node:path";
4633
+ var MAX_DEPTH = 64;
4634
+ function list(start_directory) {
4635
+ const directories = [];
4636
+ let current = path10.resolve(start_directory);
4637
+ while (directories.length < MAX_DEPTH && !directories.includes(current)) {
4638
+ directories.push(current);
4639
+ current = path10.dirname(current);
4640
+ }
4641
+ return directories;
4642
+ }
4643
+ function nearest(start_directory, is_match) {
4644
+ return list(start_directory).find((directory) => is_match(directory));
4645
+ }
4646
+ var ancestor_directories = { list, nearest };
4647
+
4648
+ // scripts/lib/schemas.ts
4649
+ import { z as z4 } from "zod";
4650
+ var overrides_snapshot_schema = z4.record(z4.string(), z4.string());
4651
+ var package_version_schema = z4.object({ version: z4.string() });
4652
+ var package_named_version_schema = z4.looseObject({ name: z4.string(), version: z4.string() });
4653
+ var package_with_version_schema = z4.looseObject({ version: z4.string() });
4654
+ var ls_global_dependency_schema = z4.looseObject({ version: z4.string() });
4655
+ var ls_global_dependencies_schema = z4.record(z4.string(), ls_global_dependency_schema).optional();
4656
+ var pnpm_ls_global_schema = z4.array(
4657
+ z4.looseObject({
4658
+ dependencies: ls_global_dependencies_schema
4659
+ })
4660
+ );
4661
+ var vscode_settings_schema = z4.record(z4.string(), z4.unknown());
4662
+ var with_scripts_schema = z4.looseObject({
4663
+ scripts: z4.record(z4.string(), z4.string()).optional()
4664
+ });
4665
+ var with_development_deps_schema = z4.looseObject({
4666
+ devDependencies: z4.record(z4.string(), z4.string()).optional()
4667
+ });
4668
+ var with_package_manager_schema = z4.looseObject({
4669
+ packageManager: z4.string().optional()
4670
+ });
4671
+ var with_development_engine_schema = z4.looseObject({
4672
+ devEngines: z4.record(z4.string(), z4.unknown()).optional()
4673
+ });
4674
+ var bin_entry_schema = z4.union([z4.string(), z4.record(z4.string(), z4.string())]).optional();
4675
+ var package_bin_schema = z4.looseObject({ bin: bin_entry_schema });
4676
+ var json_object_schema = z4.record(z4.string(), z4.unknown());
4677
+ var string_array_schema = z4.array(z4.string());
4678
+ var string_record_schema = z4.record(z4.string(), z4.string());
4679
+
4680
+ // scripts/build/local-bin.ts
4681
+ var NODE_MODULES2 = "node_modules";
4682
+ var BIN_DIR = ".bin";
4683
+ var WINDOWS_PLATFORM3 = "win32";
4684
+ var WINDOWS_SHIM_SUFFIX = ".cmd";
4685
+ var PACKAGE_JSON = "package.json";
4686
+ function resolve_local_bin(project_root, bin_name) {
4687
+ const shim = process.platform === WINDOWS_PLATFORM3 ? `${bin_name}${WINDOWS_SHIM_SUFFIX}` : bin_name;
4688
+ return path11.join(project_root, NODE_MODULES2, BIN_DIR, shim);
4689
+ }
4690
+ function read_bin_entry(manifest_path, package_name, bin_name) {
4691
+ const { bin } = package_bin_schema.parse(JSON.parse(readFileSync4(manifest_path, "utf8")));
4692
+ if (typeof bin === "string") return bin_name === path11.basename(package_name) ? bin : void 0;
4693
+ return bin?.[bin_name];
4694
+ }
4695
+ function resolve_package_bin(base_directory, package_name, bin_name) {
4696
+ try {
4697
+ const resolve_from = createRequire(path11.join(base_directory, PACKAGE_JSON));
4698
+ const manifest_path = resolve_from.resolve(`${package_name}/${PACKAGE_JSON}`);
4699
+ const bin_entry = read_bin_entry(manifest_path, package_name, bin_name);
4700
+ if (bin_entry === void 0) return void 0;
4701
+ const cli_entry = path11.join(path11.dirname(manifest_path), bin_entry);
4702
+ return existsSync3(cli_entry) ? cli_entry : void 0;
4703
+ } catch {
4704
+ return void 0;
4705
+ }
4706
+ }
4707
+
4708
+ // scripts/josh/josh-logic.ts
4709
+ import { execaSync as execaSync2 } from "execa";
4710
+
4711
+ // scripts/josh/command-suggest.ts
4712
+ var MAX_SUGGESTION_DISTANCE = 3;
4713
+ function at(values, index) {
4714
+ return values[index] ?? 0;
4715
+ }
4716
+ function fill_distance_row(source, target, row, previous) {
4717
+ const current = [row];
4718
+ for (let col = 1; col <= target.length; col++) {
4719
+ const cost = source[row - 1] === target[col - 1] ? 0 : 1;
4720
+ const deletion = at(previous, col) + 1;
4721
+ const insertion = at(current, col - 1) + 1;
4722
+ const substitution = at(previous, col - 1) + cost;
4723
+ current.push(Math.min(deletion, insertion, substitution));
4724
+ }
4725
+ return current;
4726
+ }
4727
+ function edit_distance(source, target) {
4728
+ let previous = Array.from({ length: target.length + 1 }, (_unused, index) => index);
4729
+ for (let row = 1; row <= source.length; row++) {
4730
+ previous = fill_distance_row(source, target, row, previous);
4731
+ }
4732
+ return at(previous, target.length);
4733
+ }
4734
+ function closest_command(input, commands) {
4735
+ const within = commands.map((command) => ({ command, distance: edit_distance(input, command) })).filter((scored) => scored.distance <= MAX_SUGGESTION_DISTANCE).toSorted((left, right) => left.distance - right.distance);
4736
+ return within[0]?.command;
4737
+ }
4738
+ var command_suggest = { closest_command, edit_distance };
4739
+
4740
+ // scripts/josh/josh-composite-arguments.ts
4741
+ var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash"]);
4742
+ var SHELL_COMMAND_FLAG = "-c";
4743
+ var PATH_SEPARATOR = "/";
4744
+ var USAGE_ERROR_EXIT_CODE = 1;
4745
+ function is_shell_interpreter(executable) {
4746
+ return SHELL_INTERPRETERS.has(executable.split(PATH_SEPARATOR).at(-1) ?? executable);
4747
+ }
4748
+ function is_composite_shell(shell) {
4749
+ const [executable, flag] = shell ?? [];
4750
+ if (executable === void 0) return false;
4751
+ return is_shell_interpreter(executable) && flag === SHELL_COMMAND_FLAG;
4752
+ }
4753
+ function format_targets(targets) {
4754
+ return targets.map((target) => `josh ${target}`).join(" or ");
4755
+ }
4756
+ function format_rejection(cmd, targets) {
4757
+ const hint = targets.length > 0 ? `pass them to ${format_targets(targets)} instead` : "it runs a fixed sequence and forwards nothing";
4758
+ return `josh ${cmd} takes no extra arguments \u2014 ${hint}`;
4759
+ }
4760
+ function reject_extra_arguments(cmd, entry, extra) {
4761
+ if (extra.length === 0 || !is_composite_shell(entry.shell)) return void 0;
4762
+ return format_rejection(cmd, entry.argument_targets ?? []);
4763
+ }
4764
+ var composite_arguments = { format_rejection, is_composite_shell, reject_extra_arguments };
4765
+
4766
+ // scripts/josh/josh-in-process.ts
4767
+ import { realpathSync } from "node:fs";
4768
+ import path12 from "node:path";
4769
+ import { pathToFileURL } from "node:url";
4770
+ var TYPESCRIPT_EXTENSION = ".ts";
4771
+ var JAVASCRIPT_EXTENSION = ".js";
4772
+ var BUNDLED_COMMAND_DIRECTORY = path12.join("dist", "commands");
4773
+ var FAILURE_EXIT_CODE = 1;
4774
+ function is_typescript_dispatcher(dispatcher_url) {
4775
+ return dispatcher_url.endsWith(TYPESCRIPT_EXTENSION);
4776
+ }
4777
+ function bundled_script_path(package_directory, script) {
4778
+ const name = `${path12.basename(script, TYPESCRIPT_EXTENSION)}${JAVASCRIPT_EXTENSION}`;
4779
+ return path12.join(package_directory, BUNDLED_COMMAND_DIRECTORY, name);
4780
+ }
4781
+ function in_process_target(entry, package_directory, dispatcher_url) {
4782
+ if (entry.script === void 0 || entry.tsx_arguments !== void 0) return void 0;
4783
+ if (is_typescript_dispatcher(dispatcher_url)) return path12.join(package_directory, entry.script);
4784
+ return entry.is_bundled === true ? bundled_script_path(package_directory, entry.script) : void 0;
4785
+ }
4786
+ function read_exit_code() {
4787
+ const code = process.exitCode;
4788
+ return typeof code === "number" ? code : 0;
4789
+ }
4790
+ function resolve_real_path(script_path) {
4791
+ try {
4792
+ return realpathSync(script_path);
4793
+ } catch {
4794
+ return script_path;
4795
+ }
4796
+ }
4797
+ async function run_in_process(script_path, script_arguments) {
4798
+ const resolved_path = resolve_real_path(script_path);
4799
+ process.argv = [process.execPath, resolved_path, ...script_arguments];
4800
+ try {
4801
+ await import(pathToFileURL(resolved_path).href);
4802
+ } catch (error) {
4803
+ console.error(error);
4804
+ return FAILURE_EXIT_CODE;
4805
+ }
2967
4806
  return read_exit_code();
2968
4807
  }
2969
4808
  var josh_in_process = { bundled_script_path, in_process_target, run_in_process };
@@ -2984,9 +4823,20 @@ var kit_only = {
2984
4823
  notice: kit_only_notice
2985
4824
  };
2986
4825
 
4826
+ // scripts/josh/package-directory.ts
4827
+ import { existsSync as existsSync4 } from "node:fs";
4828
+ import path13 from "node:path";
4829
+ var PACKAGE_JSON2 = "package.json";
4830
+ function find_package_directory(start_directory) {
4831
+ return ancestor_directories.nearest(
4832
+ start_directory,
4833
+ (directory) => existsSync4(path13.join(directory, PACKAGE_JSON2))
4834
+ ) ?? start_directory;
4835
+ }
4836
+
2987
4837
  // scripts/josh/run-github-prerequisite.ts
2988
- import { existsSync as existsSync3 } from "node:fs";
2989
- import path10 from "node:path";
4838
+ import { existsSync as existsSync5 } from "node:fs";
4839
+ import path14 from "node:path";
2990
4840
 
2991
4841
  // scripts/discovery/repo-origin.ts
2992
4842
  var GITHUB_HOST = "github.com";
@@ -3024,8 +4874,7 @@ function parse_scp_like(url) {
3024
4874
  const user_end = url.indexOf(USER_SEPARATOR);
3025
4875
  const authority = user_end === NOT_FOUND ? url : url.slice(user_end + 1);
3026
4876
  const host_end = authority.indexOf(HOST_SEPARATOR);
3027
- if (host_end === NOT_FOUND) return void 0;
3028
- if (!is_github_ssh_host(authority.slice(0, host_end))) return void 0;
4877
+ if (host_end === NOT_FOUND || !is_github_ssh_host(authority.slice(0, host_end))) return void 0;
3029
4878
  return to_identity(authority.slice(host_end + 1));
3030
4879
  }
3031
4880
  function parse_url_like(url) {
@@ -3053,8 +4902,7 @@ function origin_section_lines(lines) {
3053
4902
  }
3054
4903
  function url_value(line) {
3055
4904
  const separator = line.indexOf("=");
3056
- if (separator === NOT_FOUND) return void 0;
3057
- if (line.slice(0, separator).trim() !== URL_KEY) return void 0;
4905
+ if (separator === NOT_FOUND || line.slice(0, separator).trim() !== URL_KEY) return void 0;
3058
4906
  return line.slice(separator + 1).trim();
3059
4907
  }
3060
4908
  function parse_origin_from_config(content) {
@@ -3097,7 +4945,7 @@ var GITHUB_RUN_COMMANDS = /* @__PURE__ */ new Set([
3097
4945
  ]);
3098
4946
  function explanation(root, command) {
3099
4947
  if (!GITHUB_RUN_COMMANDS.has(command)) return void 0;
3100
- if (!existsSync3(path10.join(root, ".git"))) {
4948
+ if (!existsSync5(path14.join(root, ".git"))) {
3101
4949
  return `${command} requires Git and a GitHub origin. Run git init and add a GitHub origin first.`;
3102
4950
  }
3103
4951
  const url = git_spawn_sync.origin_url(root);
@@ -3111,21 +4959,15 @@ var COLUMN_WIDTH = 26;
3111
4959
  var ALIAS_PAD_WIDTH = 2;
3112
4960
  var TSX_BIN = "tsx";
3113
4961
  var TSX_PACKAGE = "tsx";
3114
- var PACKAGE_JSON2 = "package.json";
4962
+ var PACKAGE_JSON3 = "package.json";
3115
4963
  var UNKNOWN_COMMAND_EXIT_CODE = -1;
3116
- function find_package_directory(start_directory) {
3117
- return ancestor_directories.nearest(
3118
- start_directory,
3119
- (directory) => existsSync4(path11.join(directory, PACKAGE_JSON2))
3120
- ) ?? start_directory;
3121
- }
3122
- var PACKAGE_DIR2 = find_package_directory(path11.dirname(fileURLToPath2(import.meta.url)));
4964
+ var PACKAGE_DIR2 = find_package_directory(path15.dirname(fileURLToPath2(import.meta.url)));
3123
4965
  function resolve_tsx_executable() {
3124
4966
  const candidates = [
3125
4967
  resolve_local_bin(PACKAGE_DIR2, TSX_BIN),
3126
4968
  resolve_local_bin(process.cwd(), TSX_BIN)
3127
4969
  ];
3128
- return candidates.find(existsSync4) ?? TSX_BIN;
4970
+ return candidates.find(existsSync6) ?? TSX_BIN;
3129
4971
  }
3130
4972
  function resolve_tsx_cli_entry() {
3131
4973
  for (const base_directory of [PACKAGE_DIR2, process.cwd()]) {
@@ -3142,7 +4984,7 @@ function resolve_tsx_runner() {
3142
4984
  return { executable: resolve_tsx_executable(), leading_arguments: [] };
3143
4985
  }
3144
4986
  function read_package_version() {
3145
- const raw = readFileSync5(path11.join(PACKAGE_DIR2, PACKAGE_JSON2), "utf8");
4987
+ const raw = readFileSync5(path15.join(PACKAGE_DIR2, PACKAGE_JSON3), "utf8");
3146
4988
  return package_version_schema.parse(JSON.parse(raw)).version;
3147
4989
  }
3148
4990
  var HEADER = `josh v${read_package_version()} \u2014 Joshua Folkken's dev toolkit`;
@@ -3229,7 +5071,7 @@ async function run_script_entry(entry, subcommand_arguments) {
3229
5071
  const script_arguments = [...entry.default_script_arguments ?? [], ...subcommand_arguments];
3230
5072
  const target = josh_in_process.in_process_target(entry, PACKAGE_DIR2, import.meta.url);
3231
5073
  if (target !== void 0) return await josh_in_process.run_in_process(target, script_arguments);
3232
- return spawn_script_entry(entry, path11.join(PACKAGE_DIR2, entry.script ?? ""), script_arguments);
5074
+ return spawn_script_entry(entry, path15.join(PACKAGE_DIR2, entry.script ?? ""), script_arguments);
3233
5075
  }
3234
5076
  function github_prerequisite_exit(resolved, is_consumer) {
3235
5077
  if (!is_consumer) return void 0;
@@ -3261,13 +5103,13 @@ async function run_entry(entry, subcommand_arguments) {
3261
5103
  function declared(value) {
3262
5104
  return typeof value === "function" ? value() : value;
3263
5105
  }
3264
- async function dispatch_entry(entry, subcommand_arguments) {
5106
+ async function dispatch_entry(command, entry, subcommand_arguments) {
3265
5107
  const weight = declared(entry.core_weight);
3266
5108
  if (weight === void 0) return await run_entry(entry, subcommand_arguments);
3267
5109
  return await core_budget.with_command_reservation(
3268
5110
  weight,
3269
5111
  async () => await run_entry(entry, subcommand_arguments),
3270
- { memory_mb: declared(entry.memory_mb) }
5112
+ { memory_mb: declared(entry.memory_mb), command }
3271
5113
  );
3272
5114
  }
3273
5115
  async function run_command(cmd, subcommand_arguments, is_consumer = false) {
@@ -3276,7 +5118,7 @@ async function run_command(cmd, subcommand_arguments, is_consumer = false) {
3276
5118
  if (!entry) return UNKNOWN_COMMAND_EXIT_CODE;
3277
5119
  const early_exit = pre_dispatch_exit(resolved, entry, subcommand_arguments, is_consumer);
3278
5120
  if (early_exit !== void 0) return early_exit;
3279
- return await dispatch_entry(entry, subcommand_arguments);
5121
+ return await dispatch_entry(resolved, entry, subcommand_arguments);
3280
5122
  }
3281
5123
  var josh_logic = {
3282
5124
  format_help,
@@ -3290,13 +5132,17 @@ var josh_logic = {
3290
5132
  var ARGV_OFFSET = 2;
3291
5133
  var HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "--help", "-h"]);
3292
5134
  var ALL_FLAG = "--all";
3293
- function print_help(is_all, is_consumer) {
3294
- console.info(josh_logic.format_help(is_all, is_consumer));
3295
- }
3296
5135
  function handle_unknown(cmd) {
3297
5136
  console.error(josh_logic.format_unknown_command(cmd));
3298
5137
  process.exit(1);
3299
5138
  }
5139
+ function print_help(is_all, is_consumer, topic) {
5140
+ if (topic === void 0 || topic.startsWith("-")) {
5141
+ console.info(josh_logic.format_help(is_all, is_consumer));
5142
+ return;
5143
+ }
5144
+ console.info(command_usage.format_command_help(topic) ?? handle_unknown(topic));
5145
+ }
3300
5146
  function record_exit_code(exit_code) {
3301
5147
  if (exit_code !== 0) process.exitCode = exit_code;
3302
5148
  }
@@ -3305,7 +5151,7 @@ async function main() {
3305
5151
  const is_all = process.argv.includes(ALL_FLAG);
3306
5152
  const is_consumer = doctor_consumer.is_kit_consumer(find_package_directory(process.cwd()));
3307
5153
  if (!cmd || cmd === ALL_FLAG || HELP_COMMANDS.has(cmd)) {
3308
- print_help(is_all, is_consumer);
5154
+ print_help(is_all, is_consumer, process.argv[ARGV_OFFSET + 1]);
3309
5155
  return;
3310
5156
  }
3311
5157
  const subcommand_arguments = process.argv.slice(ARGV_OFFSET + 1);