@joshuafolkken/kit 1.1178.0 → 1.1229.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 (425) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/settings.json +2 -2
  3. package/.claude/skills/dependency-update/SKILL.md +4 -8
  4. package/.claude/skills/epic-commands/SKILL.md +2 -2
  5. package/.claude/skills/epic-commands/epic-bundle.md +1 -1
  6. package/.claude/skills/workflow-commands/SKILL.md +2 -4
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +6 -2
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -2
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
  13. package/.claude/skills/workflow-commands/chain-rule.md +1 -1
  14. package/.claude/skills/workflow-commands/delegation.md +2 -2
  15. package/.claude/skills/workflow-commands/followup.md +5 -0
  16. package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +1 -1
  18. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  19. package/.claude/skills/workflow-commands/kickoff.md +1 -1
  20. package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
  21. package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
  22. package/.claude/skills/workflow-commands/prerequisite.md +2 -2
  23. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  24. package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
  25. package/.codex/hooks.json +2 -2
  26. package/.cursorrules +6 -41
  27. package/.github/actions/setup-node/action.yml +1 -1
  28. package/.github/workflows/auto-tag.yml +3 -4
  29. package/.github/workflows/ci.yml +32 -31
  30. package/.github/workflows/dependabot-auto-merge.yml +13 -6
  31. package/.github/workflows/publish.yml +48 -4
  32. package/.github/workflows/sonar-qube.yml +3 -0
  33. package/.pnpmfile.mjs +4 -3
  34. package/.prettierignore +8 -2
  35. package/CLAUDE.md +18 -20
  36. package/README.md +26 -19
  37. package/SECURITY.md +15 -1
  38. package/dist/CLAUDE.md +18 -20
  39. package/dist/commands/document-section-cli.js +734 -0
  40. package/dist/hooks/audit-provision.js +346 -0
  41. package/dist/hooks/batch-guard.js +5 -4
  42. package/dist/hooks/{chunk-S6X6WYKR.js → chunk-55HHG5PX.js} +1210 -711
  43. package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-5VMKCCC4.js} +25 -10
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/{chunk-32GQTL4F.js → chunk-6HJJXRBX.js} +4 -2
  46. package/dist/hooks/chunk-CDY4NFQG.js +15 -0
  47. package/dist/hooks/chunk-ELCN5LCH.js +135 -0
  48. package/dist/hooks/{chunk-VESKRQOF.js → chunk-F56MFS4B.js} +3 -3
  49. package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-FIVDHQGT.js} +7 -6
  50. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  51. package/dist/hooks/{chunk-G6KRCKEC.js → chunk-JUDKDCDP.js} +496 -378
  52. package/dist/hooks/{chunk-GDS5N2KB.js → chunk-NV2JRZZO.js} +718 -681
  53. package/dist/hooks/{chunk-VQLLNFNT.js → chunk-RNWHN5BP.js} +3 -3
  54. package/dist/hooks/{chunk-DO6NWWHU.js → chunk-TQNPDZNH.js} +4 -4
  55. package/dist/hooks/{chunk-I4XSL4TT.js → chunk-VC3PZPZO.js} +1 -1
  56. package/dist/hooks/codex-hook-adapter.js +14 -12
  57. package/dist/hooks/duplicate-read-guard.js +6 -5
  58. package/dist/hooks/format-edited.js +4 -3
  59. package/dist/hooks/inputs.json +217 -195
  60. package/dist/hooks/investigation-guard.js +6 -5
  61. package/dist/hooks/pretool-guard.js +12 -10
  62. package/dist/hooks/session-lang.js +2 -4
  63. package/dist/hooks/stop-guard.js +66 -58
  64. package/dist/josh.js +868 -74
  65. package/dist/version/index.d.ts +7 -2
  66. package/dist/version/index.js +152 -75
  67. package/eslint/base.js +23 -1
  68. package/eslint/rules/code-quality.js +3 -0
  69. package/eslint/rules/early-return-one-liner.js +98 -0
  70. package/eslint/rules/unicorn.js +16 -1
  71. package/package.json +7 -6
  72. package/pnpm-workspace.yaml +9 -4
  73. package/prettier/basic.d.ts +3 -1
  74. package/prettier/index.d.ts +3 -1
  75. package/prettier/static.d.ts +3 -1
  76. package/prompts/coding-standards.md +1 -1
  77. package/prompts/collaboration-workflow/file-edits.md +4 -4
  78. package/prompts/collaboration-workflow/glossary.md +1 -1
  79. package/prompts/collaboration-workflow/operating-rules.md +17 -9
  80. package/prompts/collaboration-workflow/overview.md +2 -2
  81. package/prompts/collaboration-workflow/principles.md +14 -12
  82. package/prompts/collaboration-workflow/report-format.md +1 -1
  83. package/prompts/collaboration-workflow/residency.md +1 -1
  84. package/prompts/collaboration-workflow/rule-delivery.md +6 -6
  85. package/prompts/collaboration-workflow/shell-body.md +6 -6
  86. package/prompts/collaboration-workflow/turn-batching.md +1 -1
  87. package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
  88. package/prompts/collaboration-workflow/wip-cap.md +10 -10
  89. package/prompts/review-rubric.md +1 -2
  90. package/prompts/review.md +1 -1
  91. package/scripts/agent/agent-headless.ts +29 -0
  92. package/scripts/agent/agent-launch-environment.ts +6 -2
  93. package/scripts/agent/codex-agent-argv.ts +2 -1
  94. package/scripts/auto-ok/auto-ok-cli.ts +1 -1
  95. package/scripts/backlog/backlog-arrival.ts +3 -3
  96. package/scripts/backlog/backlog-budget.ts +1 -1
  97. package/scripts/backlog/backlog-drive-args.ts +135 -0
  98. package/scripts/backlog/backlog-drive-cli.ts +11 -137
  99. package/scripts/backlog/backlog-drive-launch.ts +39 -0
  100. package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
  101. package/scripts/backlog/backlog-drive-named.ts +3 -3
  102. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  103. package/scripts/backlog/backlog-drive-owner.ts +1 -1
  104. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  105. package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
  106. package/scripts/backlog/backlog-drive.ts +1 -1
  107. package/scripts/backlog/backlog-next.ts +16 -5
  108. package/scripts/backlog/backlog-offer-cli.ts +6 -6
  109. package/scripts/backlog/backlog-offer.ts +1 -1
  110. package/scripts/backlog/backlog-plan-cli.ts +1 -1
  111. package/scripts/backlog/backlog-pool.ts +57 -11
  112. package/scripts/backlog/backlog-ready.ts +1 -1
  113. package/scripts/backlog/backlog-scope.ts +1 -1
  114. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  115. package/scripts/backlog/backlog-stalled.ts +1 -1
  116. package/scripts/build/build-codex-hooks.ts +3 -1
  117. package/scripts/build/build-commands.ts +66 -0
  118. package/scripts/build/build-hooks.ts +2 -1
  119. package/scripts/build/build-library.ts +12 -3
  120. package/scripts/build/build.ts +70 -0
  121. package/scripts/build/local-bin.ts +3 -13
  122. package/scripts/delegation/investigation-reads.ts +3 -1
  123. package/scripts/discovery/repo-origin.ts +5 -5
  124. package/scripts/doctor/doctor-consumer.ts +2 -2
  125. package/scripts/doctor/doctor-io.ts +2 -3
  126. package/scripts/document/capped-print-part.ts +30 -0
  127. package/scripts/document/capped-print.ts +3 -4
  128. package/scripts/document/document-byte-budget.ts +11 -2
  129. package/scripts/document/document-scan.ts +117 -0
  130. package/scripts/document/document-section.ts +143 -16
  131. package/scripts/document/entry-read-budget.ts +5 -3
  132. package/scripts/document/entry-read-set.ts +10 -89
  133. package/scripts/document/read-set-cost.ts +98 -0
  134. package/scripts/epic/epic-add-body.ts +1 -0
  135. package/scripts/epic/epic-audit-checks.ts +1 -1
  136. package/scripts/epic/epic-audit-rationale.ts +4 -3
  137. package/scripts/epic/epic-bundle-cli.ts +14 -3
  138. package/scripts/epic/epic-bundle-poll.ts +41 -0
  139. package/scripts/epic/epic-busy.ts +1 -1
  140. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  141. package/scripts/epic/epic-classify.ts +1 -1
  142. package/scripts/epic/epic-cli-argv.ts +78 -46
  143. package/scripts/epic/epic-cli-remove.ts +11 -19
  144. package/scripts/epic/epic-cli.ts +74 -73
  145. package/scripts/epic/epic-close-comment.ts +1 -1
  146. package/scripts/epic/epic-close.ts +1 -1
  147. package/scripts/epic/epic-cross-repo.ts +5 -5
  148. package/scripts/epic/epic-graph.ts +1 -1
  149. package/scripts/epic/epic-index.ts +1 -1
  150. package/scripts/epic/epic-issue.ts +2 -6
  151. package/scripts/epic/epic-lane-offer.ts +2 -2
  152. package/scripts/epic/epic-next.ts +29 -42
  153. package/scripts/epic/epic-solo-stale.ts +1 -1
  154. package/scripts/epic/epic-triage.ts +8 -4
  155. package/scripts/epic/epic-validate.ts +1 -1
  156. package/scripts/followup/git-followup-finish.ts +2 -2
  157. package/scripts/followup/git-followup-label.ts +2 -2
  158. package/scripts/followup/git-followup-merged.ts +2 -2
  159. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  160. package/scripts/followup/git-pr-followup.ts +4 -129
  161. package/scripts/gate/core-budget.ts +69 -1
  162. package/scripts/gate/gate-plan.ts +47 -7
  163. package/scripts/gate/gate-skip.ts +38 -7
  164. package/scripts/gate/hook-gate-reuse.ts +12 -10
  165. package/scripts/gate/pre-commit-type-check.ts +4 -1
  166. package/scripts/gate/project-checks.ts +6 -9
  167. package/scripts/gate/type-check-step.ts +2 -3
  168. package/scripts/gate/verification-gate.ts +27 -8
  169. package/scripts/gh/git-gh-api-path.ts +2 -2
  170. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  171. package/scripts/gh/git-gh-issue-write.ts +53 -7
  172. package/scripts/gh/git-gh-pr-read.ts +64 -19
  173. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  174. package/scripts/gh/git-gh-pr.ts +5 -1
  175. package/scripts/gh/git-pr-ai-review.ts +1 -1
  176. package/scripts/gh/git-pr-checks-eval.ts +1 -3
  177. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  178. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  179. package/scripts/gh/git-pr-checks.ts +7 -5
  180. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  181. package/scripts/gh/git-pr-error.ts +1 -3
  182. package/scripts/gh/git-pr.ts +9 -13
  183. package/scripts/gh/github-issue-url.ts +2 -2
  184. package/scripts/git/constants.ts +2 -6
  185. package/scripts/git/git-command.ts +20 -218
  186. package/scripts/git/git-diff-reads.ts +225 -0
  187. package/scripts/git/git-error.ts +4 -12
  188. package/scripts/git/git-pre-push-hook.ts +139 -0
  189. package/scripts/git/git-prompt.ts +2 -6
  190. package/scripts/git/git-spawn-sync.ts +3 -1
  191. package/scripts/git/git-spawn.ts +1 -1
  192. package/scripts/git/git-staging.ts +4 -6
  193. package/scripts/git/git-status.ts +3 -9
  194. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  195. package/scripts/hooks/edited-cspell.ts +2 -4
  196. package/scripts/hooks/format-edited-file.ts +3 -12
  197. package/scripts/hooks/pre-push-unit.ts +31 -9
  198. package/scripts/hooks/reserved-run.ts +2 -2
  199. package/scripts/hooks/run-hook.sh +5 -4
  200. package/scripts/hooks/stop-guard.ts +5 -3
  201. package/scripts/init/init-actions.ts +0 -5
  202. package/scripts/init/init-ai-copy.ts +13 -2
  203. package/scripts/init/init-logic-json-merge.ts +19 -0
  204. package/scripts/init/init-logic-secretlint.ts +7 -7
  205. package/scripts/init/init-logic-workspace.ts +41 -2
  206. package/scripts/init/init-logic.ts +16 -26
  207. package/scripts/init/init.ts +5 -15
  208. package/scripts/init/initial-commit.ts +4 -4
  209. package/scripts/init/kit-development-versions.ts +40 -0
  210. package/scripts/init/kit-setup-state.ts +2 -2
  211. package/scripts/init/start-exec.ts +68 -19
  212. package/scripts/init/start-setup-pr.ts +9 -9
  213. package/scripts/init/start-steps.ts +6 -10
  214. package/scripts/issue/defect-rate-cli.ts +1 -1
  215. package/scripts/issue/git-next-issues.ts +1 -1
  216. package/scripts/issue/issue-auto-ok.ts +1 -1
  217. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  218. package/scripts/issue/issue-backlinks.ts +9 -9
  219. package/scripts/issue/issue-comment-cli.ts +19 -13
  220. package/scripts/issue/issue-file-cli.ts +45 -18
  221. package/scripts/issue/issue-file.ts +24 -1
  222. package/scripts/issue/issue-scout-closed.ts +1 -1
  223. package/scripts/issue/issue-state-cli.ts +30 -74
  224. package/scripts/josh/josh-command-map.ts +2 -0
  225. package/scripts/josh/josh-command-types.ts +11 -0
  226. package/scripts/josh/josh-commands-ai.ts +4 -4
  227. package/scripts/josh/josh-commands-backlog.ts +4 -1
  228. package/scripts/josh/josh-commands-development.ts +10 -0
  229. package/scripts/josh/josh-commands-document.ts +1 -0
  230. package/scripts/josh/josh-commands-lane.ts +22 -0
  231. package/scripts/josh/josh-commands-maintenance.ts +4 -4
  232. package/scripts/josh/josh-commands-run.ts +12 -7
  233. package/scripts/josh/josh-in-process.ts +28 -9
  234. package/scripts/josh/josh-logic.ts +33 -15
  235. package/scripts/josh/process-identity.ts +1 -3
  236. package/scripts/josh/session-language-cli.ts +3 -4
  237. package/scripts/lane/lane-await.ts +5 -1
  238. package/scripts/lane/lane-child-invocation.ts +2 -2
  239. package/scripts/lane/lane-cli.ts +3 -3
  240. package/scripts/lane/lane-dispatch.ts +14 -10
  241. package/scripts/lane/lane-environment.ts +5 -1
  242. package/scripts/lane/lane-handoff.ts +1 -1
  243. package/scripts/lane/lane-install.ts +2 -13
  244. package/scripts/lane/lane-launch-cli.ts +15 -1
  245. package/scripts/lane/lane-ledger.ts +145 -0
  246. package/scripts/lane/lane-leftover.ts +1 -1
  247. package/scripts/lane/lane-load.ts +108 -0
  248. package/scripts/lane/lane-measure-cli.ts +149 -0
  249. package/scripts/lane/lane-occupancy.ts +3 -0
  250. package/scripts/lane/lane-reap.ts +1 -3
  251. package/scripts/lane/lane-relaunch.ts +1 -1
  252. package/scripts/lane/lane-stats.ts +224 -0
  253. package/scripts/lane/lane-vacant.ts +36 -0
  254. package/scripts/lane/openai-lane-ship.ts +2 -2
  255. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  256. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  257. package/scripts/lane/openai-review-broker.ts +2 -4
  258. package/scripts/lib/ancestor-directories.ts +35 -0
  259. package/scripts/lib/animation-helpers.ts +2 -2
  260. package/scripts/lib/buffered-process.ts +3 -7
  261. package/scripts/lib/cli-flags.ts +71 -1
  262. package/scripts/lib/spawn-exit.ts +2 -2
  263. package/scripts/lib/timeouts.ts +35 -2
  264. package/scripts/lines/line-budget.ts +4 -6
  265. package/scripts/notify/telegram-test.ts +6 -0
  266. package/scripts/observations/observation-ledger-line.ts +6 -6
  267. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  268. package/scripts/observations/observation-ledger.ts +26 -7
  269. package/scripts/overrides/lockfile-overrides.ts +1 -1
  270. package/scripts/overrides/overrides-logic.ts +1 -1
  271. package/scripts/propagate/propagate-git.ts +1 -1
  272. package/scripts/propagate/propagate-publish.ts +2 -2
  273. package/scripts/propagate/propagate-steps.ts +20 -15
  274. package/scripts/propagate/propagate.ts +36 -27
  275. package/scripts/refactor/import-graph.ts +18 -1
  276. package/scripts/refactor/refactor-lint.ts +4 -4
  277. package/scripts/repo/repository-labels.ts +2 -2
  278. package/scripts/repo/required-checks-logic.ts +1 -3
  279. package/scripts/review/live-evidence.ts +2 -4
  280. package/scripts/review/review-brief-cli.ts +1 -1
  281. package/scripts/review/review-finding-ledger.ts +1 -1
  282. package/scripts/review/review-findings-cli.ts +1 -3
  283. package/scripts/rules/decision-oracle-batch.ts +128 -0
  284. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  285. package/scripts/rules/decision-oracle.ts +8 -120
  286. package/scripts/rules/delivered-rules.ts +26 -182
  287. package/scripts/rules/direct-filing.ts +1 -1
  288. package/scripts/rules/early-heartbeat.ts +3 -3
  289. package/scripts/rules/git-argv.ts +1 -1
  290. package/scripts/rules/git-force.ts +1 -1
  291. package/scripts/rules/implementation-cut.ts +2 -2
  292. package/scripts/rules/index-guard.ts +1 -1
  293. package/scripts/rules/issue-comments.ts +118 -0
  294. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  295. package/scripts/rules/lane-background.ts +2 -2
  296. package/scripts/rules/lane-park.ts +18 -0
  297. package/scripts/rules/lane-split-park.ts +128 -0
  298. package/scripts/rules/oracle-firing.ts +1 -1
  299. package/scripts/rules/poll-loop.ts +1 -1
  300. package/scripts/rules/pre-gate-cut.ts +1 -1
  301. package/scripts/rules/protected-files.ts +59 -4
  302. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  303. package/scripts/rules/shell-body-trigger.ts +1 -1
  304. package/scripts/rules/stop-rules.ts +18 -7
  305. package/scripts/rules/test-declared-commit.ts +4 -4
  306. package/scripts/rules/wip-cap.ts +63 -0
  307. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  308. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  309. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  310. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
  311. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  312. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +2 -2
  313. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  314. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  315. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  316. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  317. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  318. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +16 -2
  319. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  320. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  321. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  322. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  323. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  324. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  325. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  326. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  327. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  328. package/scripts/run/run-ending.ts +2 -2
  329. package/scripts/run/run-halfrun-resume.ts +6 -1
  330. package/scripts/run/run-headless.ts +1 -1
  331. package/scripts/run/run-invocation.ts +85 -84
  332. package/scripts/run/run-liveness.ts +1 -1
  333. package/scripts/run/run-parent-cut-hook.ts +1 -1
  334. package/scripts/run/run-preflight.ts +6 -6
  335. package/scripts/run/run-prep-cli.ts +1 -1
  336. package/scripts/run/run-prrun-resume.ts +1 -1
  337. package/scripts/run/run-report-cli.ts +4 -4
  338. package/scripts/run/run-report.ts +2 -2
  339. package/scripts/run/run-status-cli.ts +1 -1
  340. package/scripts/run/run-step-cli.ts +3 -3
  341. package/scripts/run/run-step.ts +2 -2
  342. package/scripts/run/run-stop-notify.ts +1 -1
  343. package/scripts/run/run-stranded-detect.ts +4 -4
  344. package/scripts/run/run-stranded.ts +2 -2
  345. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  346. package/scripts/run/run-watcher-guard.ts +5 -3
  347. package/scripts/run/run-watcher-hook.ts +1 -1
  348. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
  349. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  350. package/scripts/run/ship/run-ship-lock.ts +50 -0
  351. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  352. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  353. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
  354. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  355. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  356. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
  357. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  358. package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
  359. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  360. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +23 -8
  361. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +3 -3
  362. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
  363. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  364. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +8 -2
  365. package/scripts/run/wake/run-wake-restart.ts +58 -0
  366. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  367. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +1 -1
  368. package/scripts/safe-chain/preinstall-command.ts +20 -21
  369. package/scripts/safe-chain/project-config.ts +19 -15
  370. package/scripts/sync/sync-ai-files.ts +1 -1
  371. package/scripts/sync/sync-configs.ts +2 -1
  372. package/scripts/sync/sync.ts +15 -2
  373. package/scripts/test/unit-worker-share.ts +1 -0
  374. package/scripts/time-runtime/time-background.ts +5 -1
  375. package/scripts/time-runtime/time-instant.ts +3 -1
  376. package/scripts/time-runtime/time-spans.ts +6 -139
  377. package/scripts/time-runtime/time-tool-call.ts +160 -0
  378. package/scripts/version/effective-upstream.ts +2 -20
  379. package/scripts/version/latest-corepack.ts +17 -17
  380. package/scripts/version/latest-regression.ts +1 -1
  381. package/scripts/version/latest-update.ts +4 -4
  382. package/scripts/version/npm-registry.ts +20 -5
  383. package/scripts/version/publishable-range-check.ts +2 -2
  384. package/scripts/version/publishable-range.ts +1 -1
  385. package/scripts/version/release-age.ts +48 -38
  386. package/scripts/version/release-hold.ts +1 -1
  387. package/scripts/version/safe-chain-version-update.ts +40 -0
  388. package/scripts/version/upgrade-shell-command.ts +48 -8
  389. package/scripts/version/version-check-logic.ts +23 -14
  390. package/scripts/version/version-commands.ts +23 -11
  391. package/scripts/version/version-remote.ts +28 -13
  392. package/templates/pnpm-workspace.basic.yaml +4 -0
  393. package/templates/workflows/dependabot-auto-merge.yml +5 -5
  394. package/templates/workflows/github-release.yml +2 -0
  395. package/scripts/build/build-config-merge.ts +0 -27
  396. package/scripts/build/build-managed-marker.ts +0 -27
  397. package/scripts/build/build-self-sync-guard.ts +0 -27
  398. package/scripts/build/build-version.ts +0 -26
  399. package/scripts/version/preinstall-version-update.ts +0 -86
  400. /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
  401. /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  402. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  403. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  404. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  405. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  406. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  407. /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
  408. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  409. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  410. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  411. /package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +0 -0
  412. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  413. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  414. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  415. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  416. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  417. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  418. /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
  419. /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
  420. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  421. /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
  422. /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
  423. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  424. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  425. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -0,0 +1,139 @@
1
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises'
2
+ import { tmpdir } from 'node:os'
3
+ import path from 'node:path'
4
+ import { git_spawn } from './git-spawn'
5
+
6
+ // The pre-push hook, run ahead of the push instead of inside it (joshuafolkken/kit#3300).
7
+ //
8
+ // **The push budget is a transfer budget, and a hook is not a transfer.** `PUSH_TIMEOUT_MS` was read
9
+ // off what moving objects costs here, but `git push` runs the pre-push hook first and the budget
10
+ // counted it: a hook that ran the whole unit suite — any push the gate's record does not cover, such
11
+ // as the observation ledger commit or a tree `ship` has just synced with main — took 130 to 196
12
+ // seconds and was killed at 120 on a push that was healthy. Killing `git` did not kill the hook
13
+ // either: lefthook and vitest outlived it, and the retry started a second suite in the same work tree,
14
+ // where each `test-state-guard` read the other's temporary files as a mutation and both failed.
15
+ //
16
+ // So the hook runs here, once, with no budget, and `git_command.push` spawns the bounded transfer that
17
+ // follows with `--no-verify`. The retry then repeats the transfer alone, and a hook that is still
18
+ // running cannot exist for it to overlap.
19
+ //
20
+ // **What git hands the hook is reproduced rather than dropped.** `git hook run` executes whatever
21
+ // hook is configured — lefthook's or anyone's — and the arguments and stdin are git's pre-push
22
+ // contract: the remote's name and URL, and one `<local ref> <local oid> <remote ref> <remote oid>`
23
+ // line per ref. A hook that reads them (`git lfs pre-push` uploads exactly the objects those lines
24
+ // name) would otherwise act on nothing. Both pushes `git_command.push` makes send the current branch to
25
+ // the same name on `origin`, so that is the one line written. Like git, an up-to-date branch runs no
26
+ // hook: the remote-tracking ref already naming `HEAD` is git's "Everything up-to-date".
27
+
28
+ const REMOTE = 'origin'
29
+ const HOOK_NAME = 'pre-push'
30
+ const PROBE_HOOK_NAME = 'kit-hook-run-probe'
31
+ const ALLOW_UNKNOWN_HOOK_NAME_FLAG = '--allow-unknown-hook-name'
32
+ const HOOK_RUN = 'run'
33
+ const STDIN_FILE = 'refs'
34
+ const TEMPORARY_PREFIX = 'kit-pre-push-'
35
+ const ZERO_DIGIT = '0'
36
+
37
+ interface PushReferences {
38
+ branch: string
39
+ local_oid: string
40
+ remote_oid: string | undefined
41
+ }
42
+
43
+ // `undefined` is a branch the remote has never seen, which git reports with an all-zero object id.
44
+ async function read_remote_oid(branch: string): Promise<string | undefined> {
45
+ try {
46
+ return await git_spawn.read([
47
+ 'rev-parse',
48
+ '--verify',
49
+ '--quiet',
50
+ `refs/remotes/${REMOTE}/${branch}`,
51
+ ])
52
+ } catch {
53
+ return undefined
54
+ }
55
+ }
56
+
57
+ async function read_push_references(): Promise<PushReferences> {
58
+ const [branch, local_oid] = await Promise.all([
59
+ git_spawn.read(['rev-parse', '--abbrev-ref', 'HEAD']),
60
+ git_spawn.read(['rev-parse', 'HEAD']),
61
+ ])
62
+
63
+ return { branch, local_oid, remote_oid: await read_remote_oid(branch) }
64
+ }
65
+
66
+ // The zero id takes the local id's length, so a SHA-256 repository gets 64 digits rather than 40.
67
+ function to_stdin_line(references: PushReferences): string {
68
+ const reference = `refs/heads/${references.branch}`
69
+ const remote_oid = references.remote_oid ?? ZERO_DIGIT.repeat(references.local_oid.length)
70
+
71
+ return `${reference} ${references.local_oid} ${reference} ${remote_oid}\n`
72
+ }
73
+
74
+ function to_hook_run_arguments(stdin_path: string, hook_name: string): Array<string> {
75
+ return [HOOK_RUN, '--ignore-missing', `--to-stdin=${stdin_path}`, hook_name]
76
+ }
77
+
78
+ // **A git that cannot run the hook this way leaves it to the push** — `git hook run` is Git 2.36, and
79
+ // `--to-stdin` later still; an older git exits 129 on either. It is asked with a hook name nothing
80
+ // configures, which `--ignore-missing` turns into a clean exit on any git that understands the call,
81
+ // so the answer never depends on what the real hook does. Git 2.54 refuses a name that is not a
82
+ // native hook unless `--allow-unknown-hook-name` says otherwise, and a git before it refuses that flag,
83
+ // so the probe is asked both ways.
84
+ async function can_probe(probe_arguments: ReadonlyArray<string>): Promise<boolean> {
85
+ try {
86
+ await git_spawn.read(['hook', ...probe_arguments])
87
+
88
+ return true
89
+ } catch {
90
+ return false
91
+ }
92
+ }
93
+
94
+ async function can_run_hooks(stdin_path: string): Promise<boolean> {
95
+ const [, ...options] = to_hook_run_arguments(stdin_path, PROBE_HOOK_NAME)
96
+
97
+ if (await can_probe([HOOK_RUN, ALLOW_UNKNOWN_HOOK_NAME_FLAG, ...options])) return true
98
+
99
+ return await can_probe([HOOK_RUN, ...options])
100
+ }
101
+
102
+ async function run_with_stdin(stdin_line: string): Promise<boolean> {
103
+ const directory = await mkdtemp(path.join(tmpdir(), TEMPORARY_PREFIX))
104
+ const stdin_path = path.join(directory, STDIN_FILE)
105
+
106
+ try {
107
+ await writeFile(stdin_path, stdin_line)
108
+ if (!(await can_run_hooks(stdin_path))) return false
109
+ const url = await git_spawn.read(['remote', 'get-url', REMOTE])
110
+
111
+ await git_spawn.with_output('hook', [
112
+ ...to_hook_run_arguments(stdin_path, HOOK_NAME),
113
+ '--',
114
+ REMOTE,
115
+ url,
116
+ ])
117
+
118
+ return true
119
+ } finally {
120
+ await rm(directory, { force: true, recursive: true })
121
+ }
122
+ }
123
+
124
+ // `true` when the hook is settled here — run, or nothing to push — so the push skips it; `false` when
125
+ // this git cannot run it, so the push runs it as it always did. A hook that fails throws
126
+ // `git hook exited with code <n>`, and the push is never attempted.
127
+ async function run(): Promise<boolean> {
128
+ const references = await read_push_references()
129
+
130
+ if (references.local_oid === references.remote_oid) return true
131
+
132
+ return await run_with_stdin(to_stdin_line(references))
133
+ }
134
+
135
+ const git_pre_push_hook = {
136
+ run,
137
+ }
138
+
139
+ export { git_pre_push_hook }
@@ -62,9 +62,7 @@ function create_prompt(): Interface | undefined {
62
62
  }
63
63
 
64
64
  function handle_prompt_fallback<T>(fallback_value?: T): T {
65
- if (fallback_value !== undefined) {
66
- return fallback_value
67
- }
65
+ if (fallback_value !== undefined) return fallback_value
68
66
 
69
67
  throw new Error('TTY not available')
70
68
  }
@@ -72,9 +70,7 @@ function handle_prompt_fallback<T>(fallback_value?: T): T {
72
70
  async function with_prompt<T>(callback: PromptCallback<T>, fallback_value?: T): Promise<T> {
73
71
  const prompt = create_prompt()
74
72
 
75
- if (prompt === undefined) {
76
- return handle_prompt_fallback(fallback_value)
77
- }
73
+ if (prompt === undefined) return handle_prompt_fallback(fallback_value)
78
74
 
79
75
  try {
80
76
  // eslint-disable-next-line promise/prefer-await-to-callbacks
@@ -1,4 +1,5 @@
1
1
  import { read_spawn_stderr, read_spawn_stdout } from '#scripts/lib/spawn-exit'
2
+ import { GIT_TIMEOUT_MS } from '#scripts/lib/timeouts'
2
3
  import { execaSync } from 'execa'
3
4
  import { git_utilities } from './constants'
4
5
 
@@ -7,7 +8,6 @@ import { git_utilities } from './constants'
7
8
  // non-zero exit is returned as a result, and every call is bounded: a git that never answers is an
8
9
  // unknown the caller already handles, never a hang.
9
10
 
10
- const GIT_TIMEOUT_MS = 10_000
11
11
  const SUCCESS_EXIT_CODE = 0
12
12
  // git translates its messages, so a caller matching stderr would fail under any non-English locale.
13
13
  const C_LOCALE: Record<string, string> = { LC_ALL: 'C', LANGUAGE: 'C' }
@@ -16,6 +16,8 @@ interface GitSyncOptions {
16
16
  cwd?: string
17
17
  timeout?: number
18
18
  env?: Record<string, string | undefined>
19
+ // A step a person watches (`josh start`) shows git's own progress and hook output as it runs.
20
+ stdio?: 'inherit'
19
21
  }
20
22
 
21
23
  interface GitSyncResult {
@@ -20,7 +20,7 @@ import { git_ssh_keepalive } from './git-ssh-keepalive'
20
20
  // spawns git itself. The timeout it carries is shared here by `read_remote` / `with_output_remote`. Those two files are the spawn sites of the ordinary command
21
21
  // modules — the claim stops there. A synchronous caller goes through `git-spawn-sync.ts`, the same
22
22
  // binary resolution with a result instead of an exception (joshuafolkken/kit#3065). Other parts of
23
- // this package spawn git for their own purposes (`scripts/run/run-progress-clock.ts`,
23
+ // this package spawn git for their own purposes (`scripts/run/progress/run-progress-clock.ts`,
24
24
  // `scripts/git/git-fixture-workspace.ts`), and an audit of how the git binary is resolved has to read
25
25
  // those too.
26
26
 
@@ -1,7 +1,7 @@
1
1
  import {
2
+ LEDGER_DIRECTORIES,
2
3
  LEGACY_OBSERVATION_LEDGER_PATHS,
3
4
  observation_ledger,
4
- OBSERVATION_LEDGER_DIRECTORY,
5
5
  } from '#scripts/observations/observation-ledger'
6
6
  import { observation_ledger_prepare } from '#scripts/observations/observation-ledger-prepare'
7
7
  import { git_command } from './git-command'
@@ -29,9 +29,7 @@ async function confirm_package_json_staged(should_force = false): Promise<boolea
29
29
  async function confirm_package_json_version(should_force = false): Promise<void> {
30
30
  const is_version_updated = await git_status.check_package_json_version()
31
31
 
32
- if (is_version_updated) {
33
- return
34
- }
32
+ if (is_version_updated) return
35
33
 
36
34
  if (should_force) {
37
35
  console.info('💡 Skipping package.json version check (force).')
@@ -83,7 +81,7 @@ async function stage_untracked_files(files: ReadonlyArray<string>): Promise<void
83
81
  // own lines** (joshuafolkken/kit#2919): its writers append to its own tree's issue file, so this is
84
82
  // the step that takes them to the default branch.
85
83
  const LEDGER_PATHSPECS: ReadonlyArray<string> = [
86
- OBSERVATION_LEDGER_DIRECTORY,
84
+ ...LEDGER_DIRECTORIES,
87
85
  ...LEGACY_OBSERVATION_LEDGER_PATHS,
88
86
  ]
89
87
 
@@ -108,7 +106,7 @@ function is_stageable(file_path: string, excluded: ReadonlyArray<string>): boole
108
106
  // step dies on git's empty index with `Failed to commit changes` — a message naming nothing that
109
107
  // caused it. This line is what turns that into a diagnosis.
110
108
  //
111
- // **The paths are parsed rather than matched as substrings**: `docs/maintainers/observations-old/x.md`
109
+ // **The paths are parsed rather than matched as substrings**: `.josh/observations-old/x.md`
112
110
  // contains the ledger's path, and a hint naming a file the exclusion never touched is a hint that
113
111
  // teaches the reader to ignore it.
114
112
  function report_excluded_paths(status_output: string, excluded: ReadonlyArray<string>): void {
@@ -31,9 +31,7 @@ function has_unstaged_files(status_output: string): boolean {
31
31
  }
32
32
 
33
33
  function is_staged_file(line: string): boolean {
34
- if (line.length < REQUIRED_STATUS_LENGTH || is_untracked_file(line)) {
35
- return false
36
- }
34
+ if (line.length < REQUIRED_STATUS_LENGTH || is_untracked_file(line)) return false
37
35
 
38
36
  const [staged_status] = line
39
37
 
@@ -47,9 +45,7 @@ function has_unstaged_changes(line: string): boolean {
47
45
  function has_all_files_staged(status_output: string): boolean {
48
46
  const lines = parse_status_lines(status_output)
49
47
 
50
- if (lines.length === 0) {
51
- return true
52
- }
48
+ if (lines.length === 0) return true
53
49
 
54
50
  return lines.every((line) => !has_unstaged_changes(line))
55
51
  }
@@ -62,9 +58,7 @@ function is_package_json_staged(status_output: string): boolean {
62
58
  const lines = parse_status_lines(status_output)
63
59
 
64
60
  return lines.some((line) => {
65
- if (!is_staged_file(line)) {
66
- return false
67
- }
61
+ if (!is_staged_file(line)) return false
68
62
 
69
63
  const filename = extract_filename(line)
70
64
 
@@ -49,7 +49,7 @@ function paths_matching(command: string, pattern: RegExp): Array<string> {
49
49
  return [...command.matchAll(pattern)].flatMap((match) => {
50
50
  const file_path = match[1]?.trim()
51
51
 
52
- return file_path === undefined || file_path === '' ? [] : [file_path]
52
+ return file_path === undefined || file_path === '' ? [] : file_path
53
53
  })
54
54
  }
55
55
 
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import path from 'node:path'
3
+ import { HOOK_PROCESS_TIMEOUT_MS } from '#scripts/lib/timeouts'
3
4
  import { execa } from 'execa'
4
5
 
5
6
  // The spell half of the edit hook (joshuafolkken/kit#2296). joshuafolkken/kit#2275 gave the hook a way
@@ -47,9 +48,6 @@ const CSPELL_ARGS: ReadonlyArray<string> = [
47
48
  '--no-summary',
48
49
  ]
49
50
  const PNPM = 'pnpm'
50
- // A spell check that hangs would hold the edit's turn open; the bound is generous because reaching it
51
- // means something is already wrong, exactly as the formatter spawns in `format-edited-file.ts`.
52
- const PROCESS_TIMEOUT_MS = 15_000
53
51
 
54
52
  const HEADER =
55
53
  'The edit hook found words cspell does not know; fix them or add them to `cspell.config.yaml` now rather than at the gate:'
@@ -79,7 +77,7 @@ async function run_cspell(file_path: string, project_root: string): Promise<stri
79
77
  cwd: project_root,
80
78
  stdout: 'pipe',
81
79
  stderr: 'ignore',
82
- timeout: PROCESS_TIMEOUT_MS,
80
+ timeout: HOOK_PROCESS_TIMEOUT_MS,
83
81
  })
84
82
 
85
83
  return result.stdout
@@ -4,6 +4,7 @@ import path from 'node:path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
  import { resolve_local_bin, resolve_package_bin } from '#scripts/build/local-bin'
6
6
  import { ESLINT_EDIT_CACHE_FLAGS } from '#scripts/josh/josh-command-types'
7
+ import { HOOK_PROCESS_TIMEOUT_MS } from '#scripts/lib/timeouts'
7
8
  import { time_density_hook } from '#scripts/time-runtime/time-density-hook'
8
9
  import { execa } from 'execa'
9
10
  import { z } from 'zod'
@@ -87,15 +88,6 @@ const DAEMON_RESTART = 'restart'
87
88
  // `eslint.config.*` entry, and the rule modules under `eslint/` that it imports.
88
89
  const ESLINT_CONFIG_DIR = 'eslint'
89
90
  const ESLINT_CONFIG_ENTRY = /^eslint\.config\.[cm]?[jt]s$/u
90
- // A formatter that hangs would hold the edit's turn open, so each spawn is bounded. The bound has
91
- // to be read against the *worst-case run* rather than one spawn: an edit to a config input plans
92
- // eslint, prettier and `eslint_d restart`, and the first and last each have a second route behind
93
- // the daemon — five spawns. The hook entry in `.claude/settings.json` declares 90 seconds, so the
94
- // per-spawn bound must leave five of them under it, or the harness kill lands first and lands at a
95
- // moment this file did not choose — possibly inside `prettier --write`, which rewrites in place and
96
- // can leave the file truncated. 15s × 5 = 75s. It is still two orders of magnitude beyond the 0.84s
97
- // a warm run takes, so reaching it means something is already wrong.
98
- const PROCESS_TIMEOUT_MS = 15_000
99
91
 
100
92
  // The problems eslint could not auto-fix are handed to the model verbatim from eslint's own stdout —
101
93
  // its default formatter already prints the file, the `line:col`, and the rule name the issue asks
@@ -315,7 +307,7 @@ async function spawn_invocation(
315
307
  env: { ESLINT_D_MISS: 'fail' },
316
308
  stdout: 'pipe',
317
309
  stderr: 'ignore',
318
- timeout: PROCESS_TIMEOUT_MS,
310
+ timeout: HOOK_PROCESS_TIMEOUT_MS,
319
311
  })
320
312
 
321
313
  return { exit_code: result.exitCode ?? 1, stdout: result.stdout }
@@ -469,7 +461,7 @@ function report_no_payload(): void {
469
461
  // have (joshuafolkken/kit#2314). The rewrite, lint and spell blocks are composed together first, then
470
462
  // joined to the density line — one envelope, since a `PostToolUse` hook's stdout carries only one
471
463
  // (joshuafolkken/kit#2296). A hook killed at its 15s timeout is the one case this loses the density
472
- // line to — a run already gone pathologically wrong, per PROCESS_TIMEOUT_MS.
464
+ // line to — a run already gone pathologically wrong, per HOOK_PROCESS_TIMEOUT_MS.
473
465
  async function run_hook(payload: string): Promise<void> {
474
466
  const notice = time_density_hook.density_notice(payload)
475
467
  const file_path = parse_edited_path(payload)
@@ -500,6 +492,5 @@ export {
500
492
  select_invocation,
501
493
  ESLINT_DAEMON,
502
494
  MAX_DIAGNOSTIC_CHARS,
503
- PROCESS_TIMEOUT_MS,
504
495
  }
505
496
  export type { CommandRunner, FormatCommand }
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { gate_skip } from '#scripts/gate/gate-skip'
3
+ import { gate_skip, type GateReuse } from '#scripts/gate/gate-skip'
4
4
  import { gate_tree, type GateTree } from '#scripts/gate/gate-tree'
5
5
  import { hook_gate_reuse } from '#scripts/gate/hook-gate-reuse'
6
- import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
7
6
  import { test_unit_guard } from '#scripts/test/test-unit-guard'
8
7
 
9
8
  // The pre-push hook's unit run, which no longer re-runs a suite a green gate already covers
@@ -42,6 +41,15 @@ const FORCE_ENV = 'JOSH_PRE_PUSH_FORCE'
42
41
  // `gate-tree.ts` rather than read a second way here — plus the one this hook adds.
43
42
  interface PushTree extends GateTree {
44
43
  is_clean: boolean
44
+ // What a dirty tree reports as the reason the record was not reused (joshuafolkken/kit#3307) — the
45
+ // paths name a lock file a hook's own `pnpm install` rewrote, the case that went unexplained.
46
+ carry_miss: string
47
+ }
48
+
49
+ function carry_miss(lines: ReadonlyArray<string> | undefined): string {
50
+ if (lines === undefined) return 'git status could not be read'
51
+
52
+ return `the working tree differs from HEAD (${lines.join('; ')})`
45
53
  }
46
54
 
47
55
  // `git status --porcelain` prints one line per difference from HEAD, untracked files included — so an
@@ -57,7 +65,11 @@ async function read_push_tree(): Promise<PushTree> {
57
65
  hook_gate_reuse.read_status_lines(),
58
66
  ])
59
67
 
60
- return { ...tree, is_clean: hook_gate_reuse.is_worktree_clean(lines) }
68
+ return {
69
+ ...tree,
70
+ is_clean: hook_gate_reuse.is_worktree_clean(lines),
71
+ carry_miss: carry_miss(lines),
72
+ }
61
73
  }
62
74
 
63
75
  // `--force` is the gate's own flag, reused rather than respelled: it and `JOSH_PRE_PUSH_FORCE` are
@@ -74,14 +86,15 @@ function forwarded_arguments(extra_arguments: ReadonlyArray<string>): ReadonlyAr
74
86
  // **Any argument at all refuses the reuse**, not only `--force`. Everything else is forwarded to
75
87
  // vitest, and a caller who narrowed the run to one spec asked for that run rather than for a
76
88
  // recorded result about a whole tree. The hook's own line passes none, so this costs it nothing.
77
- function reusable_green_push(
89
+ function push_reuse(
78
90
  tree: PushTree,
79
91
  extra_arguments: ReadonlyArray<string>,
80
92
  source?: string,
81
- ): FileMapStamp | undefined {
82
- return hook_gate_reuse.reusable_green_hook({
93
+ ): GateReuse {
94
+ return hook_gate_reuse.hook_reuse({
83
95
  tree,
84
96
  is_tree_carried: tree.is_clean,
97
+ carry_miss: tree.carry_miss,
85
98
  extra_arguments,
86
99
  force_env: FORCE_ENV,
87
100
  source,
@@ -107,20 +120,29 @@ function format_skip(taken_at: string): string {
107
120
  // other commands already have. **A project that has vitest and no test file at all fails the push
108
121
  // instead** (joshuafolkken/kit#1224): the guard treats that half as a broken state rather than a
109
122
  // young project, and the hook returns what the guard returns.
123
+ // **A fall back to the whole suite says why, in one line** (joshuafolkken/kit#3307): under a crowded
124
+ // machine the suite costs minutes, and a silent miss left a moved merge base, a dirty tree and a
125
+ // mismatched record indistinguishable from one another.
126
+ function format_miss(reason: string): string {
127
+ return `↻ running the whole unit suite — the green gate record does not cover this push: ${reason}.`
128
+ }
129
+
110
130
  async function run_pre_push_unit(
111
131
  extra_arguments: ReadonlyArray<string> = [],
112
132
  source?: string,
113
133
  ): Promise<number> {
114
- const reusable = reusable_green_push(await read_push_tree(), extra_arguments, source)
134
+ const reuse = push_reuse(await read_push_tree(), extra_arguments, source)
135
+
136
+ if ('miss' in reuse) {
137
+ process.stdout.write(`${format_miss(reuse.miss)}\n`)
115
138
 
116
- if (reusable === undefined) {
117
139
  return await test_unit_guard.run_guarded_unit(
118
140
  process.cwd(),
119
141
  forwarded_arguments(extra_arguments),
120
142
  )
121
143
  }
122
144
 
123
- process.stdout.write(`${format_skip(reusable.taken_at)}\n`)
145
+ process.stdout.write(`${format_skip(reuse.stamp.taken_at)}\n`)
124
146
 
125
147
  return 0
126
148
  }
@@ -54,8 +54,8 @@ function parse_arguments(argv: ReadonlyArray<string>): ReservedCommand | undefin
54
54
  // command becomes an exit code the caller returns rather than a throw — the reservation is released by
55
55
  // `with_core_reservation`'s `finally` on either path.
56
56
  async function run_command(command: ReadonlyArray<string>): Promise<number> {
57
- const [file, ...args] = command
58
- const result = await execa(file ?? '', args, { stdio: 'inherit', reject: false })
57
+ const [file = '', ...args] = command
58
+ const result = await execa(file, args, { stdio: 'inherit', reject: false })
59
59
 
60
60
  return result.exitCode ?? FAIL_EXIT_CODE
61
61
  }
@@ -12,9 +12,10 @@
12
12
  # runs through josh, whose command is the name with its first `-` turned into `:`
13
13
  # (`pretool-guard` → `pretool:guard`).
14
14
  # - In kit's own checkout the gate is `hook-bundle-ready.ts`, which rebuilds stale bundles
15
- # (joshuafolkken/kit#2984). In an installed package it is a presence check: the published bundles
16
- # cannot be stale against a source the consumer never edits, and Node strips no types under
17
- # `node_modules`.
15
+ # (joshuafolkken/kit#2984) and then runs the bundle — or the josh fallback — itself, so a hook call
16
+ # starts node once (joshuafolkken/kit#3327). In an installed package it is a presence check: the
17
+ # published bundles cannot be stale against a source the consumer never edits, and Node strips no
18
+ # types under `node_modules`.
18
19
  # - An `if`/`else`, never `&&`/`||`: a guard's refusal is a non-zero exit, and a chain would read it as
19
20
  # a missing bundle and run the hook twice.
20
21
 
@@ -36,6 +37,6 @@ case $0 in
36
37
  if [ -d "$package_dir/dist/hooks" ]; then node "$bundle" "$@"; else node "$package_dir/dist/josh.js" "$josh_command" "$@"; fi
37
38
  ;;
38
39
  *)
39
- if node --disable-warning=ExperimentalWarning "$hooks_dir/hook-bundle-ready.ts"; then node "$bundle" "$@"; else pnpm josh "$josh_command" "$@"; fi
40
+ node --disable-warning=ExperimentalWarning "$hooks_dir/hook-bundle-ready.ts" "$hook_name" "$josh_command" "$@"
40
41
  ;;
41
42
  esac
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { text } from 'node:stream/consumers'
3
3
  import { fileURLToPath } from 'node:url'
4
+ import { agent_headless } from '#scripts/agent/agent-headless'
4
5
  import { backlog_ready } from '#scripts/backlog/backlog-ready'
5
6
  import { backlog_stalled_detect } from '#scripts/backlog/backlog-stalled-detect'
6
7
  import { repo_party } from '#scripts/discovery/repo-party'
@@ -13,11 +14,11 @@ import { lane_background } from '#scripts/rules/lane-background'
13
14
  import { lane_park } from '#scripts/rules/lane-park'
14
15
  import { last_prompt } from '#scripts/rules/last-prompt'
15
16
  import { stop_rules, type StopContext, type StopOutcome } from '#scripts/rules/stop-rules'
16
- import { run_carry } from '#scripts/run/run-carry'
17
- import { run_cut } from '#scripts/run/run-cut'
17
+ import { run_carry } from '#scripts/run/carry/run-carry'
18
+ import { run_cut } from '#scripts/run/cut/run-cut'
19
+ import { run_hold } from '#scripts/run/hold/run-hold'
18
20
  import { run_halfrun_resume } from '#scripts/run/run-halfrun-resume'
19
21
  import { run_headless } from '#scripts/run/run-headless'
20
- import { run_hold } from '#scripts/run/run-hold'
21
22
  import { run_stranded_detect } from '#scripts/run/run-stranded-detect'
22
23
  import { time_density_hook } from '#scripts/time-runtime/time-density-hook'
23
24
  import { time_hook_transcript } from '#scripts/time-runtime/time-hook-transcript'
@@ -87,6 +88,7 @@ async function build_context(
87
88
  agent_pending: lane_background.pending_agent_ids(tail).length > 0,
88
89
  handed_off: lane_handoff.is_handed_off(process.cwd()),
89
90
  session_lang: session_lang(),
91
+ headless_agent: agent_headless.is_headless(),
90
92
  }
91
93
  }
92
94
 
@@ -217,11 +217,6 @@ function build_file_actions(shape?: ProjectShape): ReadonlyArray<FileAction> {
217
217
 
218
218
  return [
219
219
  ...(shape?.has_git === false ? [] : [build_gitignore_action()]),
220
- build_action(
221
- '.npmrc',
222
- () => init_logic.generate_npmrc(),
223
- (existing) => init_logic.merge_npmrc(existing),
224
- ),
225
220
  build_eslint_action(),
226
221
  build_action(
227
222
  PRETTIER_CONFIG_JS,
@@ -104,8 +104,16 @@ function did_skip_copy_if_absent(
104
104
 
105
105
  // The merged file is written back, and reported as `updated`, only when the merge changed it — a
106
106
  // second `josh init` over an already-merged file reports `unchanged` (joshuafolkken/kit#2873).
107
+ // The workspace template with the project's own `.npmrc` values carried over: once the file holds a
108
+ // key, the merge keeps it, so this is the one chance to move the value (joshuafolkken/kit#3267).
109
+ function read_workspace_template(source_path: string, destination_path: string): string {
110
+ const npmrc = file_reader.read_file_or_empty(path.join(path.dirname(destination_path), '.npmrc'))
111
+
112
+ return init_logic.carry_npmrc_settings(readFileSync(source_path, 'utf8'), npmrc)
113
+ }
114
+
107
115
  function merge_existing_workspace_yaml(source_path: string, destination_path: string): void {
108
- const template = readFileSync(source_path, 'utf8')
116
+ const template = read_workspace_template(source_path, destination_path)
109
117
  const existing = readFileSync(destination_path, 'utf8')
110
118
  const merged = init_logic.merge_workspace_yaml(existing, template)
111
119
 
@@ -126,7 +134,9 @@ function did_skip_workspace_yaml_copy(source_path: string, destination_path: str
126
134
  return false
127
135
  }
128
136
 
129
- copy_ai_file(source_path, destination_path)
137
+ const template = read_workspace_template(source_path, destination_path)
138
+
139
+ writeFileSync(destination_path, transform_copied_content(destination_path, template))
130
140
  console.info(` ✔ created ${WORKSPACE_YAML}`)
131
141
 
132
142
  return false
@@ -385,6 +395,7 @@ const init_ai_copy = {
385
395
  ai_file_source,
386
396
  ai_file_mappings,
387
397
  ensure_basic_claude_md,
398
+ read_workspace_template,
388
399
  }
389
400
 
390
401
  export { init_ai_copy, CLAUDE_MD_FILENAME }
@@ -3,6 +3,7 @@ import { json_format } from '#scripts/config-merge/json-format'
3
3
  import { parse_jsonc } from '#scripts/config-merge/parse-jsonc'
4
4
  import { patch_json_key } from '#scripts/config-merge/patch-json-key'
5
5
  import { json_object_schema, string_array_schema, string_record_schema } from '#scripts/lib/schemas'
6
+ import { safe_chain_preinstall } from '#scripts/safe-chain/preinstall-command'
6
7
  import { apply_jf_migrations, remove_retired_scripts } from './init-logic-migrate'
7
8
  import { PACKAGE_JSON_KEY_ORDER } from './init-logic-package-key-order'
8
9
  import { kit_base_preset } from './kit-base-preset'
@@ -216,6 +217,23 @@ function merge_package_scripts(content: string, scripts: Record<string, string>)
216
217
  return serialize_package_json({ ...parsed, scripts: { ...prepend, ...migrated, ...append } })
217
218
  }
218
219
 
220
+ // Replace only a safe-chain `preinstall` an earlier kit wrote, for a consumer that never re-runs
221
+ // `josh init`; every other key and script is kept (joshuafolkken/kit#3269).
222
+ function upgrade_safe_chain_preinstall(content: string): string {
223
+ const parsed = parse_jsonc(content)
224
+ const raw = parsed['scripts']
225
+ if (raw === undefined) return content
226
+
227
+ const scripts = string_record_schema.parse(raw)
228
+ const current = scripts['preinstall']
229
+ if (current === undefined) return content
230
+
231
+ const migrated = safe_chain_preinstall.migrate_preinstall(current)
232
+ if (migrated === current) return content
233
+
234
+ return serialize_package_json({ ...parsed, scripts: { ...scripts, preinstall: migrated } })
235
+ }
236
+
219
237
  function merge_development_dependencies(
220
238
  content: string,
221
239
  additions: Record<string, string>,
@@ -327,6 +345,7 @@ const init_logic_json_merge = {
327
345
  merge_json_array_field,
328
346
  merge_json_object,
329
347
  merge_package_scripts,
348
+ upgrade_safe_chain_preinstall,
330
349
  merge_package_script_suffix,
331
350
  replace_in_package_script,
332
351
  remove_script_with_marker,
@@ -1,4 +1,5 @@
1
1
  import { init_logic_json_merge } from './init-logic-json-merge'
2
+ import { kit_development_versions } from './kit-development-versions'
2
3
 
3
4
  const SECRETLINT_CONFIG_FILENAME = '.secretlintrc.json'
4
5
  const SECRETLINT_RULE_PRESET = '@secretlint/secretlint-rule-preset-recommend'
@@ -6,12 +7,8 @@ const SECRETLINT_RULE_PRESET = '@secretlint/secretlint-rule-preset-recommend'
6
7
  // secretlint resolves both its CLI and every rule package from the project it runs in, not
7
8
  // transitively through the kit — the same constraint that forces the prettier plugins into
8
9
  // consumer devDependencies (as kit peers, see init.ts). Omitting either
9
- // entry makes the pre-commit hook fail with "Cannot find module". Versions mirror the kit's
10
- // own devDependencies.
11
- const SECRETLINT_DEV_DEPS: Record<string, string> = {
12
- secretlint: '^13.0.2',
13
- [SECRETLINT_RULE_PRESET]: '^13.0.2',
14
- }
10
+ // entry makes the pre-commit hook fail with "Cannot find module". The versions are kit's own.
11
+ const SECRETLINT_PACKAGES = ['secretlint', SECRETLINT_RULE_PRESET]
15
12
 
16
13
  function generate_secretlint_config(): string {
17
14
  const config = { rules: [{ id: SECRETLINT_RULE_PRESET }] }
@@ -24,7 +21,10 @@ function get_secretlint_config_filename(): string {
24
21
  }
25
22
 
26
23
  function merge_secretlint_development_deps(content: string): string {
27
- return init_logic_json_merge.merge_development_dependencies(content, SECRETLINT_DEV_DEPS)
24
+ return init_logic_json_merge.merge_development_dependencies(
25
+ content,
26
+ kit_development_versions.versions_of(SECRETLINT_PACKAGES),
27
+ )
28
28
  }
29
29
 
30
30
  const init_logic_secretlint = {