@joshuafolkken/kit 1.1178.0 → 1.1240.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 (442) 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 +7 -3
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
  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 +10 -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 +2 -1
  20. package/.claude/skills/workflow-commands/latest-gate.md +1 -1
  21. package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
  22. package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
  23. package/.claude/skills/workflow-commands/prerequisite.md +2 -2
  24. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  25. package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
  26. package/.codex/hooks.json +2 -2
  27. package/.cursorrules +6 -41
  28. package/.github/actions/setup-node/action.yml +1 -1
  29. package/.github/workflows/auto-tag.yml +3 -4
  30. package/.github/workflows/ci.yml +32 -31
  31. package/.github/workflows/dependabot-auto-merge.yml +13 -6
  32. package/.github/workflows/publish.yml +50 -4
  33. package/.github/workflows/sonar-qube.yml +3 -0
  34. package/.pnpmfile.mjs +4 -3
  35. package/.prettierignore +8 -2
  36. package/CLAUDE.md +18 -20
  37. package/README.md +44 -38
  38. package/SECURITY.md +15 -1
  39. package/dist/CLAUDE.md +18 -20
  40. package/dist/commands/document-section-cli.js +734 -0
  41. package/dist/hooks/audit-provision.js +346 -0
  42. package/dist/hooks/batch-guard.js +5 -4
  43. package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/chunk-CDY4NFQG.js +15 -0
  46. package/dist/hooks/chunk-ELCN5LCH.js +135 -0
  47. package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
  48. package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
  49. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  50. package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
  51. package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
  52. package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
  53. package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
  54. package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
  55. package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
  56. package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
  57. package/dist/hooks/codex-hook-adapter.js +14 -12
  58. package/dist/hooks/duplicate-read-guard.js +6 -5
  59. package/dist/hooks/format-edited.js +4 -3
  60. package/dist/hooks/inputs.json +203 -179
  61. package/dist/hooks/investigation-guard.js +6 -5
  62. package/dist/hooks/pretool-guard.js +12 -10
  63. package/dist/hooks/session-lang.js +2 -4
  64. package/dist/hooks/stop-guard.js +66 -72
  65. package/dist/josh.js +1088 -78
  66. package/dist/version/index.d.ts +7 -2
  67. package/dist/version/index.js +152 -75
  68. package/eslint/base.js +23 -1
  69. package/eslint/rules/code-quality.js +3 -0
  70. package/eslint/rules/early-return-one-liner.js +98 -0
  71. package/eslint/rules/unicorn.js +16 -1
  72. package/package.json +7 -6
  73. package/pnpm-workspace.yaml +9 -4
  74. package/prettier/basic.d.ts +3 -1
  75. package/prettier/index.d.ts +3 -1
  76. package/prettier/static.d.ts +3 -1
  77. package/prompts/coding-standards.md +1 -1
  78. package/prompts/collaboration-workflow/file-edits.md +4 -4
  79. package/prompts/collaboration-workflow/glossary.md +1 -1
  80. package/prompts/collaboration-workflow/issue-template.md +1 -0
  81. package/prompts/collaboration-workflow/operating-rules.md +17 -9
  82. package/prompts/collaboration-workflow/overview.md +2 -2
  83. package/prompts/collaboration-workflow/principles.md +14 -12
  84. package/prompts/collaboration-workflow/report-format.md +1 -1
  85. package/prompts/collaboration-workflow/residency.md +1 -1
  86. package/prompts/collaboration-workflow/rule-delivery.md +6 -6
  87. package/prompts/collaboration-workflow/shell-body.md +6 -6
  88. package/prompts/collaboration-workflow/turn-batching.md +1 -1
  89. package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
  90. package/prompts/collaboration-workflow/wip-cap.md +10 -10
  91. package/prompts/review-rubric.md +1 -2
  92. package/prompts/review.md +1 -1
  93. package/scripts/agent/agent-headless.ts +29 -0
  94. package/scripts/agent/agent-launch-environment.ts +6 -2
  95. package/scripts/agent/codex-agent-argv.ts +2 -1
  96. package/scripts/auto-ok/auto-ok-cli.ts +1 -1
  97. package/scripts/backlog/backlog-arrival.ts +3 -3
  98. package/scripts/backlog/backlog-budget.ts +1 -1
  99. package/scripts/backlog/backlog-drive-args.ts +135 -0
  100. package/scripts/backlog/backlog-drive-cli.ts +11 -137
  101. package/scripts/backlog/backlog-drive-launch.ts +39 -0
  102. package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
  103. package/scripts/backlog/backlog-drive-named.ts +3 -3
  104. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  105. package/scripts/backlog/backlog-drive-owner.ts +1 -1
  106. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  107. package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
  108. package/scripts/backlog/backlog-drive.ts +1 -1
  109. package/scripts/backlog/backlog-next.ts +16 -5
  110. package/scripts/backlog/backlog-offer-cli.ts +6 -6
  111. package/scripts/backlog/backlog-offer.ts +1 -1
  112. package/scripts/backlog/backlog-plan-cli.ts +1 -1
  113. package/scripts/backlog/backlog-pool.ts +57 -11
  114. package/scripts/backlog/backlog-ready.ts +1 -1
  115. package/scripts/backlog/backlog-scope.ts +1 -1
  116. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  117. package/scripts/backlog/backlog-stalled.ts +1 -1
  118. package/scripts/build/build-codex-hooks.ts +3 -1
  119. package/scripts/build/build-commands.ts +66 -0
  120. package/scripts/build/build-hooks.ts +2 -1
  121. package/scripts/build/build-library.ts +12 -3
  122. package/scripts/build/build.ts +70 -0
  123. package/scripts/build/local-bin.ts +3 -13
  124. package/scripts/delegation/investigation-reads.ts +3 -1
  125. package/scripts/discovery/repo-origin.ts +5 -5
  126. package/scripts/doctor/doctor-consumer.ts +2 -2
  127. package/scripts/doctor/doctor-io.ts +2 -3
  128. package/scripts/document/capped-print-part.ts +30 -0
  129. package/scripts/document/capped-print.ts +3 -4
  130. package/scripts/document/document-byte-budget.ts +11 -2
  131. package/scripts/document/document-scan.ts +117 -0
  132. package/scripts/document/document-section.ts +143 -16
  133. package/scripts/document/entry-read-budget.ts +5 -3
  134. package/scripts/document/entry-read-set.ts +10 -89
  135. package/scripts/document/read-set-cost.ts +98 -0
  136. package/scripts/epic/epic-add-body.ts +1 -0
  137. package/scripts/epic/epic-audit-checks.ts +1 -1
  138. package/scripts/epic/epic-audit-rationale.ts +4 -3
  139. package/scripts/epic/epic-bundle-cli.ts +14 -3
  140. package/scripts/epic/epic-bundle-poll.ts +41 -0
  141. package/scripts/epic/epic-busy.ts +1 -1
  142. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  143. package/scripts/epic/epic-classify.ts +1 -1
  144. package/scripts/epic/epic-cli-argv.ts +78 -46
  145. package/scripts/epic/epic-cli-remove.ts +11 -19
  146. package/scripts/epic/epic-cli.ts +74 -73
  147. package/scripts/epic/epic-close-comment.ts +1 -1
  148. package/scripts/epic/epic-close.ts +1 -1
  149. package/scripts/epic/epic-cross-repo.ts +5 -5
  150. package/scripts/epic/epic-graph.ts +1 -1
  151. package/scripts/epic/epic-index.ts +1 -1
  152. package/scripts/epic/epic-issue.ts +2 -6
  153. package/scripts/epic/epic-lane-offer.ts +2 -2
  154. package/scripts/epic/epic-next.ts +29 -42
  155. package/scripts/epic/epic-solo-stale.ts +1 -1
  156. package/scripts/epic/epic-triage.ts +8 -4
  157. package/scripts/epic/epic-validate.ts +1 -1
  158. package/scripts/followup/git-followup-finish.ts +2 -2
  159. package/scripts/followup/git-followup-label.ts +2 -2
  160. package/scripts/followup/git-followup-merged.ts +2 -2
  161. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  162. package/scripts/followup/git-pr-followup.ts +4 -129
  163. package/scripts/gate/core-admission.ts +162 -0
  164. package/scripts/gate/core-budget.ts +190 -68
  165. package/scripts/gate/gate-plan.ts +80 -7
  166. package/scripts/gate/gate-report.ts +28 -9
  167. package/scripts/gate/gate-skip.ts +38 -7
  168. package/scripts/gate/hook-gate-reuse.ts +12 -10
  169. package/scripts/gate/machine-capacity.ts +175 -0
  170. package/scripts/gate/pre-commit-type-check.ts +4 -1
  171. package/scripts/gate/project-checks.ts +6 -9
  172. package/scripts/gate/type-check-step.ts +2 -3
  173. package/scripts/gate/verification-gate.ts +34 -10
  174. package/scripts/gh/git-gh-api-path.ts +2 -2
  175. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  176. package/scripts/gh/git-gh-issue-write.ts +53 -7
  177. package/scripts/gh/git-gh-pr-read.ts +64 -19
  178. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  179. package/scripts/gh/git-gh-pr.ts +5 -1
  180. package/scripts/gh/git-pr-ai-review.ts +1 -1
  181. package/scripts/gh/git-pr-checks-eval.ts +1 -3
  182. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  183. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  184. package/scripts/gh/git-pr-checks.ts +7 -5
  185. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  186. package/scripts/gh/git-pr-error.ts +1 -3
  187. package/scripts/gh/git-pr.ts +9 -13
  188. package/scripts/gh/github-issue-url.ts +2 -2
  189. package/scripts/git/constants.ts +2 -6
  190. package/scripts/git/git-command.ts +20 -218
  191. package/scripts/git/git-diff-reads.ts +225 -0
  192. package/scripts/git/git-error.ts +4 -12
  193. package/scripts/git/git-pre-push-hook.ts +139 -0
  194. package/scripts/git/git-prompt.ts +2 -6
  195. package/scripts/git/git-spawn-sync.ts +3 -1
  196. package/scripts/git/git-spawn.ts +1 -1
  197. package/scripts/git/git-staging.ts +4 -6
  198. package/scripts/git/git-status.ts +3 -9
  199. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  200. package/scripts/hooks/edited-cspell.ts +2 -4
  201. package/scripts/hooks/format-edited-file.ts +3 -12
  202. package/scripts/hooks/pre-push-unit.ts +31 -9
  203. package/scripts/hooks/reserved-run.ts +2 -2
  204. package/scripts/hooks/run-hook.sh +5 -4
  205. package/scripts/hooks/stop-guard.ts +5 -3
  206. package/scripts/init/init-actions.ts +0 -5
  207. package/scripts/init/init-ai-copy.ts +13 -2
  208. package/scripts/init/init-logic-json-merge.ts +19 -0
  209. package/scripts/init/init-logic-secretlint.ts +7 -7
  210. package/scripts/init/init-logic-workspace.ts +41 -2
  211. package/scripts/init/init-logic.ts +16 -26
  212. package/scripts/init/init.ts +10 -16
  213. package/scripts/init/initial-commit.ts +4 -4
  214. package/scripts/init/kit-development-versions.ts +40 -0
  215. package/scripts/init/kit-setup-state.ts +2 -2
  216. package/scripts/init/package-manager-pin.ts +35 -0
  217. package/scripts/init/start-exec.ts +68 -19
  218. package/scripts/init/start-setup-pr.ts +9 -9
  219. package/scripts/init/start-steps.ts +6 -10
  220. package/scripts/issue/defect-rate-cli.ts +1 -1
  221. package/scripts/issue/git-next-issues.ts +1 -1
  222. package/scripts/issue/issue-auto-ok.ts +32 -7
  223. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  224. package/scripts/issue/issue-backlinks.ts +9 -9
  225. package/scripts/issue/issue-comment-cli.ts +19 -13
  226. package/scripts/issue/issue-file-cli.ts +72 -22
  227. package/scripts/issue/issue-file.ts +29 -1
  228. package/scripts/issue/issue-labels.ts +13 -0
  229. package/scripts/issue/issue-release-cli.ts +155 -0
  230. package/scripts/issue/issue-release.ts +58 -0
  231. package/scripts/issue/issue-scout-closed.ts +1 -1
  232. package/scripts/issue/issue-state-cli.ts +30 -74
  233. package/scripts/josh/josh-command-map.ts +2 -0
  234. package/scripts/josh/josh-command-types.ts +14 -0
  235. package/scripts/josh/josh-commands-ai.ts +4 -4
  236. package/scripts/josh/josh-commands-backlog.ts +4 -1
  237. package/scripts/josh/josh-commands-development.ts +18 -0
  238. package/scripts/josh/josh-commands-document.ts +1 -0
  239. package/scripts/josh/josh-commands-issue.ts +8 -1
  240. package/scripts/josh/josh-commands-lane.ts +24 -1
  241. package/scripts/josh/josh-commands-maintenance.ts +6 -6
  242. package/scripts/josh/josh-commands-run.ts +12 -7
  243. package/scripts/josh/josh-in-process.ts +28 -9
  244. package/scripts/josh/josh-logic.ts +34 -15
  245. package/scripts/josh/process-identity.ts +1 -3
  246. package/scripts/josh/session-language-cli.ts +3 -4
  247. package/scripts/lane/lane-await.ts +5 -1
  248. package/scripts/lane/lane-child-invocation.ts +2 -2
  249. package/scripts/lane/lane-cli.ts +12 -4
  250. package/scripts/lane/lane-dispatch.ts +14 -10
  251. package/scripts/lane/lane-environment.ts +5 -1
  252. package/scripts/lane/lane-handoff.ts +1 -1
  253. package/scripts/lane/lane-install.ts +2 -13
  254. package/scripts/lane/lane-launch-cli.ts +15 -1
  255. package/scripts/lane/lane-ledger.ts +145 -0
  256. package/scripts/lane/lane-leftover.ts +44 -30
  257. package/scripts/lane/lane-load.ts +108 -0
  258. package/scripts/lane/lane-measure-cli.ts +149 -0
  259. package/scripts/lane/lane-occupancy.ts +3 -0
  260. package/scripts/lane/lane-open.ts +5 -5
  261. package/scripts/lane/lane-paths.ts +13 -0
  262. package/scripts/lane/lane-reap.ts +1 -3
  263. package/scripts/lane/lane-registry.ts +11 -0
  264. package/scripts/lane/lane-relaunch.ts +1 -1
  265. package/scripts/lane/lane-stats.ts +224 -0
  266. package/scripts/lane/lane-stray.ts +140 -0
  267. package/scripts/lane/lane-vacant.ts +36 -0
  268. package/scripts/lane/openai-lane-ship.ts +2 -2
  269. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  270. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  271. package/scripts/lane/openai-review-broker.ts +2 -4
  272. package/scripts/lib/ancestor-directories.ts +35 -0
  273. package/scripts/lib/animation-helpers.ts +2 -2
  274. package/scripts/lib/buffered-process.ts +3 -7
  275. package/scripts/lib/cli-flags.ts +71 -1
  276. package/scripts/lib/spawn-exit.ts +2 -2
  277. package/scripts/lib/timeouts.ts +35 -2
  278. package/scripts/lines/line-budget.ts +4 -6
  279. package/scripts/notify/telegram-test.ts +6 -0
  280. package/scripts/observations/observation-ledger-line.ts +6 -6
  281. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  282. package/scripts/observations/observation-ledger.ts +26 -7
  283. package/scripts/overrides/lockfile-overrides.ts +1 -1
  284. package/scripts/overrides/overrides-logic.ts +1 -1
  285. package/scripts/propagate/propagate-git.ts +1 -1
  286. package/scripts/propagate/propagate-publish.ts +2 -2
  287. package/scripts/propagate/propagate-steps.ts +20 -15
  288. package/scripts/propagate/propagate.ts +36 -27
  289. package/scripts/refactor/import-graph.ts +18 -1
  290. package/scripts/refactor/refactor-lint.ts +4 -4
  291. package/scripts/release/github-release-environment.ts +1 -0
  292. package/scripts/release/github-release-runs.ts +115 -0
  293. package/scripts/release/github-release.ts +13 -67
  294. package/scripts/repo/repository-labels.ts +2 -2
  295. package/scripts/repo/required-checks-logic.ts +1 -3
  296. package/scripts/review/live-evidence.ts +2 -4
  297. package/scripts/review/review-brief-cli.ts +1 -1
  298. package/scripts/review/review-finding-ledger.ts +1 -1
  299. package/scripts/review/review-findings-cli.ts +1 -3
  300. package/scripts/rules/decision-oracle-batch.ts +128 -0
  301. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  302. package/scripts/rules/decision-oracle.ts +8 -120
  303. package/scripts/rules/delivered-rules.ts +26 -182
  304. package/scripts/rules/direct-filing.ts +1 -1
  305. package/scripts/rules/early-heartbeat.ts +3 -3
  306. package/scripts/rules/git-argv.ts +1 -1
  307. package/scripts/rules/git-force.ts +1 -1
  308. package/scripts/rules/implementation-cut.ts +2 -2
  309. package/scripts/rules/index-guard.ts +1 -1
  310. package/scripts/rules/issue-comments.ts +118 -0
  311. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  312. package/scripts/rules/lane-background.ts +2 -2
  313. package/scripts/rules/lane-park.ts +18 -0
  314. package/scripts/rules/lane-split-park.ts +128 -0
  315. package/scripts/rules/oracle-firing.ts +1 -1
  316. package/scripts/rules/poll-loop.ts +1 -1
  317. package/scripts/rules/pre-gate-cut.ts +1 -1
  318. package/scripts/rules/protected-files.ts +59 -4
  319. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  320. package/scripts/rules/shell-body-trigger.ts +1 -1
  321. package/scripts/rules/stop-rules.ts +18 -7
  322. package/scripts/rules/test-declared-commit.ts +4 -4
  323. package/scripts/rules/wip-cap.ts +63 -0
  324. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  325. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  326. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  327. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
  328. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  329. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
  330. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  331. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  332. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  333. package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
  334. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  335. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  336. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
  337. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  338. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  339. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  340. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  341. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  342. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  343. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  344. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  345. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  346. package/scripts/run/run-ending.ts +2 -2
  347. package/scripts/run/run-halfrun-resume.ts +6 -1
  348. package/scripts/run/run-headless.ts +1 -1
  349. package/scripts/run/run-invocation.ts +85 -84
  350. package/scripts/run/run-liveness.ts +1 -1
  351. package/scripts/run/run-parent-cut-hook.ts +1 -1
  352. package/scripts/run/run-preflight.ts +6 -6
  353. package/scripts/run/run-prep-cli.ts +1 -1
  354. package/scripts/run/run-prrun-resume.ts +1 -1
  355. package/scripts/run/run-report-cli.ts +4 -4
  356. package/scripts/run/run-report.ts +2 -2
  357. package/scripts/run/run-status-cli.ts +1 -1
  358. package/scripts/run/run-step-cli.ts +3 -3
  359. package/scripts/run/run-step.ts +7 -4
  360. package/scripts/run/run-stop-notify.ts +1 -1
  361. package/scripts/run/run-stranded-detect.ts +4 -4
  362. package/scripts/run/run-stranded.ts +2 -2
  363. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  364. package/scripts/run/run-watcher-guard.ts +5 -3
  365. package/scripts/run/run-watcher-hook.ts +1 -1
  366. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
  367. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  368. package/scripts/run/ship/run-ship-lock.ts +50 -0
  369. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  370. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  371. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
  372. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  373. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  374. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
  375. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  376. package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
  377. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  378. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
  379. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
  380. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
  381. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  382. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
  383. package/scripts/run/wake/run-wake-restart.ts +58 -0
  384. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  385. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
  386. package/scripts/safe-chain/preinstall-command.ts +20 -21
  387. package/scripts/safe-chain/project-config.ts +19 -15
  388. package/scripts/sync/sync-ai-files.ts +1 -1
  389. package/scripts/sync/sync-configs.ts +2 -1
  390. package/scripts/sync/sync.ts +15 -2
  391. package/scripts/test/unit-worker-share.ts +1 -0
  392. package/scripts/time-runtime/time-background.ts +5 -1
  393. package/scripts/time-runtime/time-instant.ts +3 -1
  394. package/scripts/time-runtime/time-spans.ts +6 -139
  395. package/scripts/time-runtime/time-tool-call.ts +160 -0
  396. package/scripts/version/effective-upstream.ts +2 -20
  397. package/scripts/version/latest-corepack.ts +124 -93
  398. package/scripts/version/latest-regression.ts +1 -1
  399. package/scripts/version/latest-update.ts +4 -4
  400. package/scripts/version/npm-registry.ts +20 -5
  401. package/scripts/version/publishable-range-check.ts +2 -2
  402. package/scripts/version/publishable-range.ts +1 -1
  403. package/scripts/version/release-age.ts +48 -38
  404. package/scripts/version/release-hold.ts +1 -1
  405. package/scripts/version/safe-chain-version-update.ts +40 -0
  406. package/scripts/version/upgrade-shell-command.ts +48 -8
  407. package/scripts/version/version-check-logic.ts +23 -14
  408. package/scripts/version/version-commands.ts +23 -11
  409. package/scripts/version/version-remote.ts +28 -13
  410. package/templates/pnpm-workspace.basic.yaml +4 -0
  411. package/templates/workflows/dependabot-auto-merge.yml +5 -5
  412. package/templates/workflows/github-release.yml +6 -0
  413. package/scripts/build/build-config-merge.ts +0 -27
  414. package/scripts/build/build-managed-marker.ts +0 -27
  415. package/scripts/build/build-self-sync-guard.ts +0 -27
  416. package/scripts/build/build-version.ts +0 -26
  417. package/scripts/version/preinstall-version-update.ts +0 -86
  418. /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
  419. /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  420. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  421. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  422. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  423. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  424. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  425. /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
  426. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  427. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  428. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  429. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  430. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  431. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  432. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  433. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  434. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  435. /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
  436. /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
  437. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  438. /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
  439. /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
  440. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  441. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  442. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -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 = {
@@ -2,7 +2,7 @@ function extract_yaml_top_level_keys(content: string): Array<string> {
2
2
  return content.split('\n').flatMap((line) => {
3
3
  const key = /^([a-zA-Z][a-zA-Z0-9_-]*):/u.exec(line)?.[1]
4
4
 
5
- return key ? [key] : []
5
+ return key ?? []
6
6
  })
7
7
  }
8
8
 
@@ -125,6 +125,45 @@ function add_missing_build_entries(existing: string, template: string): string {
125
125
  return existing.replace(block, () => [block, ...missing].join('\n'))
126
126
  }
127
127
 
128
+ interface MovedNpmrcSetting {
129
+ npmrc_key: string
130
+ workspace_key: string
131
+ value_pattern: RegExp
132
+ }
133
+
134
+ // `.npmrc` settings pnpm 12 reads only from `pnpm-workspace.yaml`, with the value shape each takes.
135
+ // A project's own value (a longer age window, `engine-strict=false`) replaces the template's, so the
136
+ // move neither loosens nor tightens what the project chose (joshuafolkken/kit#3267).
137
+ const MOVED_NPMRC_SETTINGS: ReadonlyArray<MovedNpmrcSetting> = [
138
+ { npmrc_key: 'minimum-release-age', workspace_key: 'minimumReleaseAge', value_pattern: /^\d+$/u },
139
+ { npmrc_key: 'engine-strict', workspace_key: 'engineStrict', value_pattern: /^(?:true|false)$/u },
140
+ ]
141
+
142
+ // The last assignment wins, as it does when npm reads the file.
143
+ function npmrc_value(npmrc: string, key: string): string | undefined {
144
+ const pattern = new RegExp(String.raw`^[ \t]*${key}[ \t]*=[ \t]*(\S+)[ \t]*$`, 'gmu')
145
+
146
+ return [...npmrc.matchAll(pattern)].at(-1)?.[1]
147
+ }
148
+
149
+ function carry_npmrc_setting(template: string, npmrc: string, setting: MovedNpmrcSetting): string {
150
+ const value = npmrc_value(npmrc, setting.npmrc_key)
151
+ if (value === undefined || !setting.value_pattern.test(value)) return template
152
+ const line_pattern = new RegExp(`^${setting.workspace_key}:.*$`, 'mu')
153
+
154
+ return template.replace(line_pattern, () => `${setting.workspace_key}: ${value}`)
155
+ }
156
+
157
+ function carry_npmrc_settings(template: string, npmrc: string): string {
158
+ let result = template
159
+
160
+ for (const setting of MOVED_NPMRC_SETTINGS) {
161
+ result = carry_npmrc_setting(result, npmrc, setting)
162
+ }
163
+
164
+ return result
165
+ }
166
+
128
167
  function merge_workspace_yaml(existing: string, template: string): string {
129
168
  if (!existing.trim()) return template
130
169
  const normalized = existing.endsWith('\n') ? existing : `${existing}\n`
@@ -138,6 +177,6 @@ function merge_workspace_yaml(existing: string, template: string): string {
138
177
  return append_user_blocks(cleaned, new_keys, template)
139
178
  }
140
179
 
141
- const init_logic_workspace = { merge_workspace_yaml, template_build_values }
180
+ const init_logic_workspace = { carry_npmrc_settings, merge_workspace_yaml, template_build_values }
142
181
 
143
182
  export { init_logic_workspace }
@@ -14,16 +14,15 @@ const DEV_ENGINES_VALUE = {
14
14
  packageManager: { name: 'pnpm', version: '>=12.1.0', onFail: 'error' },
15
15
  }
16
16
 
17
- // New projects resolve kit from public npm. Existing registry and auth lines remain untouched
18
- // by merge_npmrc so consumers of other GitHub Packages can migrate separately.
19
- const NPMRC_LINES: ReadonlyArray<string> = [
17
+ // The settings lines earlier kit releases wrote into a project `.npmrc`. pnpm 12 reads only registry
18
+ // and auth settings from `.npmrc`, so all four are inert there: the age window and engine check now
19
+ // live in `pnpm-workspace.yaml`, and `confirmModulesPurge` no longer exists (joshuafolkken/kit#3267).
20
+ const LEGACY_NPMRC_LINES: ReadonlySet<string> = new Set([
20
21
  'engine-strict=true',
21
22
  'minimum-release-age=1440',
22
23
  'confirmModulesPurge=false',
23
- // Pin full GitHub Packages tarball URLs in the lockfile so pnpm >=11.5 frozen-lockfile
24
- // installs hit the correct authenticated download path (avoids ERR_PNPM_FETCH_401 on CI).
25
24
  'lockfile-include-tarball-url=true',
26
- ]
25
+ ])
27
26
 
28
27
  const CSPELL_IMPORT = '@joshuafolkken/kit/cspell'
29
28
 
@@ -254,10 +253,6 @@ function generate_cspell_config(): string {
254
253
  return `version: "0.2"\nimport:\n - "${CSPELL_IMPORT}"\nwords: []\nignorePaths: []\n`
255
254
  }
256
255
 
257
- function generate_npmrc(): string {
258
- return `${NPMRC_LINES.join('\n')}\n`
259
- }
260
-
261
256
  // Append `missing` lines to `existing`, inserting a separating newline when the base is
262
257
  // non-empty and lacks a trailing one. Returns `existing` untouched when nothing is missing.
263
258
  function append_missing_lines(existing: string, missing: ReadonlyArray<string>): string {
@@ -267,18 +262,19 @@ function append_missing_lines(existing: string, missing: ReadonlyArray<string>):
267
262
  return `${prefix}${missing.join('\n')}\n`
268
263
  }
269
264
 
270
- // Insert-only: every existing line survives verbatim, including a `_authToken` line the consumer
271
- // added themselves. Kit removed the env-var form until #759, on the premise that pnpm always
272
- // ignores it in a project .npmrc — true only while `npmrcAuthFile` is unset. A consumer that
273
- // declares the project file its trusted auth file (Cloudflare Workers Builds via
274
- // `PNPM_CONFIG_NPMRC_AUTH_FILE`, for instance) gets the credential expanded, and that setting can
275
- // live in a deploy platform's dashboard — so no reading of the repository tells an inert line
276
- // apart from a live one. Deleting it took down a working production deploy; leaving it costs at
277
- // most the warning pnpm already prints. See docs/authentication.md §4(d).
265
+ // Removes only an exact kit-written legacy line; every other line survives verbatim, including a
266
+ // `_authToken` line the consumer added themselves and a setting carrying a value of their own. Kit
267
+ // removed the env-var auth form until #759, on the premise that pnpm always ignores it in a project
268
+ // .npmrc — true only while `npmrcAuthFile` is unset. A consumer that declares the project file its
269
+ // trusted auth file (Cloudflare Workers Builds via `PNPM_CONFIG_NPMRC_AUTH_FILE`, for instance) gets
270
+ // the credential expanded, and that setting can live in a deploy platform's dashboard — so no
271
+ // reading of the repository tells an inert auth line apart from a live one. Deleting it took down a
272
+ // working production deploy. See docs/authentication.md §4(d).
278
273
  function merge_npmrc(content: string): string {
279
- const missing = NPMRC_LINES.filter((line) => !content.includes(line))
274
+ const lines = content.split('\n')
275
+ const kept = lines.filter((line) => !LEGACY_NPMRC_LINES.has(line.trim()))
280
276
 
281
- return append_missing_lines(content, missing)
277
+ return kept.length === lines.length ? content : kept.join('\n')
282
278
  }
283
279
 
284
280
  // A gitignore line worth appending during a union merge: real ignore patterns only.
@@ -349,10 +345,6 @@ function strip_kit_only_vscode_settings_content(raw: string): string {
349
345
  return json_format.format_json(stripped)
350
346
  }
351
347
 
352
- function get_npmrc_lines(): ReadonlyArray<string> {
353
- return NPMRC_LINES
354
- }
355
-
356
348
  function get_development_engines_value(): typeof DEV_ENGINES_VALUE {
357
349
  return DEV_ENGINES_VALUE
358
350
  }
@@ -436,7 +428,6 @@ const init_logic = {
436
428
  get_tsconfig_exclude_entries,
437
429
  generate_lefthook_config,
438
430
  generate_cspell_config,
439
- generate_npmrc,
440
431
  merge_npmrc,
441
432
  merge_gitignore,
442
433
  get_tsconfig_extends_entry,
@@ -450,7 +441,6 @@ const init_logic = {
450
441
  SAFE_CHAIN_CMD,
451
442
  GUARDED_LEFTHOOK_CMD,
452
443
  upgrade_prepare_lefthook_warning,
453
- get_npmrc_lines,
454
444
  get_development_engines_value,
455
445
  get_ai_copy_files,
456
446
  get_ai_copy_file_mappings,