@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
@@ -3,8 +3,8 @@ import { backlog_budget } from '#scripts/backlog/backlog-budget'
3
3
  import { process_identity } from '#scripts/josh/process-identity'
4
4
  import { process_owner_schema } from '#scripts/josh/process-owner'
5
5
  import { stamp_file } from '#scripts/josh/stamp-file'
6
+ import type { CarryRead } from '#scripts/run/carry/run-carry'
6
7
  import { z } from 'zod'
7
- import type { CarryRead } from './run-carry'
8
8
 
9
9
  // joshuafolkken/kit#1719. joshuafolkken/kit#1714 made a `backlogrun`'s budget survive the session cut
10
10
  // and left out the one thing that starts the next session, so the record was carried and the
@@ -126,14 +126,6 @@ interface RunWake {
126
126
  // session that is merely slow is still doing the run's work, and a supervisor that killed what it
127
127
  // could not account for would destroy exactly the work it exists to keep going.
128
128
  woke_pid?: number | undefined
129
- // When the wait on a still-running predecessor began. **It is its own field rather than a second
130
- // use of `woke_at`, and that is the whole of the bound working as described.** Marked on `woke_at`,
131
- // the first pass of a wait was the last one that could see the predecessor exit — every later pass
132
- // read the mark as an outstanding wake and answered `pending` — so a predecessor that ended two
133
- // seconds after the hold still cost the full grace window on a supervisor whose point is not to
134
- // stall. Kept apart, the predecessor's liveness is read on every pass and the mark only bounds how
135
- // long the wait may last.
136
- held_at?: string | undefined
137
129
  // The transcript ids of every session this supervisor forced with `--session-id`
138
130
  // (joshuafolkken/kit#2407). `josh time --run` attributes a whiff only to a session in this list, so
139
131
  // an unrelated read-only session that moved while the supervisor was alive is no longer counted
@@ -164,7 +156,6 @@ type WakeDecision =
164
156
  | { kind: 'idle' }
165
157
  | { kind: 'wait' }
166
158
  | { kind: 'pending' }
167
- | { kind: 'hold' }
168
159
  | { kind: 'failed' }
169
160
  | { kind: 'stop'; reason: WakeStopReason }
170
161
 
@@ -172,16 +163,16 @@ interface WakeDecisionInput {
172
163
  read: CarryRead
173
164
  woke_at: string | undefined
174
165
  attempts: number
175
- // Whether the process named on the carry record is still running. **A cut is the run's last write,
176
- // not the moment its process is gone** — so a supervisor that woke the instant it saw the hand-off
177
- // could have the new session answered `busy`, because `run-carry.ts` → `classify_claim` tests
178
- // `is_foreign_live_owner` before it tests the hand-off. Answered `busy`, the woken session stops
179
- // without claiming anything, and the supervisor would eventually call its own wake a failure and end
180
- // the run. So the predecessor is waited out first.
166
+ // Whether the process named on the carry record is still running — read only for a record no cut
167
+ // handed off, where it tells a session at work from a crashed one (`decide_not_handed_off`).
168
+ //
169
+ // **A handed-off record is launched from whether its cutting process lives or not**
170
+ // (joshuafolkken/kit#3363). The supervisor used to wait that process out, for up to the grace
171
+ // window, because `run-carry.ts` → `classify_claim` once tested liveness before the hand-off and
172
+ // would have answered the successor `busy`. joshuafolkken/kit#1935 put the hand-off first, so the
173
+ // wait guarded nothing — and an interactive session outlives its own cut, so every cut paid the
174
+ // whole ten minutes before the driver started.
181
175
  is_owner_live: boolean
182
- // When the wait on that predecessor began, so the wait can be bounded without spending the mark
183
- // that measures a launch.
184
- held_at: string | undefined
185
176
  // **Whether a woken session would find anything to do** (joshuafolkken/kit#2417). Before this the
186
177
  // decision read the carry record alone, so a session was launched whether or not the backlog held a
187
178
  // runnable issue or a lane was free — and a session that found nothing ended without touching
@@ -198,7 +189,6 @@ const WAKE_DECISION: WakeDecision = { kind: 'wake' }
198
189
  const IDLE_DECISION: WakeDecision = { kind: 'idle' }
199
190
  const WAIT_DECISION: WakeDecision = { kind: 'wait' }
200
191
  const PENDING_DECISION: WakeDecision = { kind: 'pending' }
201
- const HOLD_DECISION: WakeDecision = { kind: 'hold' }
202
192
  const FAILED_DECISION: WakeDecision = { kind: 'failed' }
203
193
 
204
194
  // The three carry reads that are not `carried`, each mapped to why the supervisor stops. `none` is the
@@ -220,7 +210,6 @@ const run_wake_schema = process_owner_schema.extend({
220
210
  // reads as "no attempt has been made yet" and would restart the retry count on every pass.
221
211
  attempts: z.number().optional(),
222
212
  woke_pid: z.number().optional(),
223
- held_at: z.string().optional(),
224
213
  spawned: z.array(z.string()).optional(),
225
214
  idle_since: z.string().optional(),
226
215
  profile: agent_role_profile.PROFILE_SCHEMA.optional(),
@@ -279,32 +268,10 @@ function decide_not_handed_off(input: WakeDecisionInput): WakeDecision {
279
268
  return decide_launch(input)
280
269
  }
281
270
 
282
- // Only before the first launch for this cut. Once a wake is out, the record's owner is whatever the
283
- // woken session declares, and waiting on it again would stall the grace window indefinitely.
284
- //
285
- // **The wait is bounded, and the bound is not decoration.** The owner is the agent process of the
286
- // session that cut, and an interactive one often outlives its own cut — waited on without a bound, the
287
- // supervisor would pend every interval for the record's whole life, wake nothing, and end on `expired`,
288
- // which sends no warning. That is a silent overnight failure, which is worse than the `busy` race this
289
- // wait exists to avoid: a `busy` merely costs one attempt, and attempts are retried. So the wait is
290
- // marked when it starts and expires into an ordinary wake through the same grace window everything
291
- // else uses.
292
- //
293
- // **And the wait ends the moment the predecessor does, because its liveness is read on every pass.**
294
- // The bound is the ceiling on the wait, never its length: a predecessor that exits two seconds after
295
- // the hold is waited two seconds. That is what `held_at` is a separate field for — marked on `woke_at`,
296
- // this test could not fire a second time, so every wait cost the whole ceiling.
297
- function is_predecessor_exiting(input: WakeDecisionInput): boolean {
298
- if (input.woke_at !== undefined || !input.is_owner_live) return false
299
-
300
- return input.held_at === undefined || !is_overdue(input.held_at, input.now)
301
- }
302
-
303
- // The whole policy, in four lines. Nothing else in this module decides whether to wake.
271
+ // The whole policy, in three lines. Nothing else in this module decides whether to wake.
304
272
  function decide(input: WakeDecisionInput): WakeDecision {
305
273
  if (input.read.kind !== 'carried') return { kind: 'stop', reason: STOP_REASONS[input.read.kind] }
306
274
  if (input.read.carry.is_handed_off !== true) return decide_not_handed_off(input)
307
- if (is_predecessor_exiting(input)) return HOLD_DECISION
308
275
 
309
276
  return decide_launch(input)
310
277
  }
@@ -367,10 +334,9 @@ function is_supervisor_live(wake: RunWake): boolean {
367
334
  // inside the grace window would otherwise see no wake mark, launch a second session for the *same*
368
335
  // cut, count it as another cut served, and leave two sessions racing for one record. The invocation
369
336
  // has to match — a different one is a different run, and its state is not this one's.
370
- // **`woke_pid` and `held_at` come across too.** Without the first, a restart inside the grace window
371
- // that then spends its retries reports "the last one was none" in the warning — the one fact the
372
- // warning exists to hand the person, dropped exactly where it is needed. Without the second, a restart
373
- // restarts the bounded wait, which is the ceiling sliding by another road.
337
+ // **`woke_pid` comes across too.** Without it, a restart inside the grace window that then spends its
338
+ // retries reports "the last one was none" in the warning — the one fact the warning exists to hand the
339
+ // person, dropped exactly where it is needed.
374
340
  function carried_state(existing: RunWake | undefined, invocation: string): Partial<RunWake> {
375
341
  if (existing?.invocation !== invocation) return { woke: NO_WAKES }
376
342
 
@@ -379,7 +345,6 @@ function carried_state(existing: RunWake | undefined, invocation: string): Parti
379
345
  woke_at: existing.woke_at,
380
346
  attempts: existing.attempts,
381
347
  woke_pid: existing.woke_pid,
382
- held_at: existing.held_at,
383
348
  // **`spawned` carries across too, or a restart mid-run would lose the ids of every session it
384
349
  // already started** (joshuafolkken/kit#2407) — and `josh time --run`, reading the record after
385
350
  // the restart, would under-count the whiffs and read the loss as a saving.
@@ -447,24 +412,12 @@ function count_wake(wake: RunWake, now: Date, pid: number, session_id: string):
447
412
  spawned,
448
413
  woke_at,
449
414
  woke_pid: pid,
450
- held_at: undefined,
451
415
  idle_since: undefined,
452
416
  }
453
417
  }
454
418
 
455
- // Starts the clock on a wait without launching anything. `attempts` is deliberately untouched, so the
456
- // bounded wait costs no retry: what it buys is that the wait expires into an ordinary wake instead of
457
- // running until the carry record does.
458
- //
459
- // **Marked once, so the ceiling does not slide.** A mark rewritten on every pass would measure from the
460
- // latest pass rather than from the start of the wait, and a predecessor that never exits would then be
461
- // waited on for ever — the unbounded wait this bound exists to prevent, arrived at by another road.
462
- function mark_wait(wake: RunWake, now: Date): RunWake {
463
- return wake.held_at === undefined ? { ...wake, held_at: now.toISOString() } : wake
464
- }
465
-
466
- // Starts the idle stretch a deferred launch is bounded by (joshuafolkken/kit#2417). Marked once, for
467
- // the reason `mark_wait` is: rewritten each pass, the ceiling would slide and never be reached.
419
+ // Starts the idle stretch a deferred launch is bounded by (joshuafolkken/kit#2417). Marked once:
420
+ // rewritten each pass, the ceiling would slide and never be reached.
468
421
  function mark_idle(wake: RunWake, now: Date): RunWake {
469
422
  return wake.idle_since === undefined ? { ...wake, idle_since: now.toISOString() } : wake
470
423
  }
@@ -489,7 +442,6 @@ function count_claim(wake: RunWake): RunWake {
489
442
  woke,
490
443
  woke_at: undefined,
491
444
  attempts: undefined,
492
- held_at: undefined,
493
445
  idle_since: undefined,
494
446
  }
495
447
  }
@@ -589,7 +541,6 @@ const run_wake = {
589
541
  fresh_wake,
590
542
  is_supervisor_live,
591
543
  mark_idle,
592
- mark_wait,
593
544
  parse_wake,
594
545
  read_own_wake,
595
546
  read_wake,
@@ -1,38 +1,37 @@
1
- // `setup-ci` only writes shims and adds them to PATH on a CI runner, so a local `pnpm install` is
2
- // scanned only when the developer has enabled safe-chain's own shell integration. That integration
3
- // runs pnpm behind safe-chain's registry proxy and hands `GLOBAL_AGENT_HTTP_PROXY` to it, which a bare
4
- // pnpm never sets — the one signal a lifecycle script can read (joshuafolkken/kit#2707).
5
- const SETUP_CI_CMD = 'pnpm dlx @aikidosec/safe-chain setup-ci'
6
- const LEGACY_PREINSTALL_RE = /^pnpm dlx @aikidosec\/safe-chain(?:@\S+)? setup-ci$/u
1
+ // The `preinstall` kit writes fetches nothing: it only checks that safe-chain is scanning the install
2
+ // and warns when it is not (joshuafolkken/kit#3269). It used to run `pnpm dlx @aikidosec/safe-chain
3
+ // setup-ci`, which downloaded an unverified package on every install and — `setup-ci` acting only on
4
+ // a CI runner, where kit installs with `--ignore-scripts` — protected nothing. safe-chain's shell
5
+ // integration runs pnpm behind its registry proxy and hands `GLOBAL_AGENT_HTTP_PROXY` to it, which a
6
+ // bare pnpm never sets — the one signal a lifecycle script can read (joshuafolkken/kit#2707).
7
+ const INSTALL_GUIDE_ANCHOR = 'installing-safe-chain'
8
+ const INSTALL_GUIDE_URL = `https://github.com/joshuafolkken/kit/blob/main/SECURITY.md#${INSTALL_GUIDE_ANCHOR}`
9
+ // `.pnpmfile.mjs` recognizes the command by the package name, so the hint names it.
7
10
  const INTEGRATION_HINT =
8
- 'Warning: safe-chain is not scanning this install because its shell integration is not active.' +
9
- ' Install safe-chain (https://github.com/AikidoSec/safe-chain#installation) or run: safe-chain setup' +
11
+ 'Warning: safe-chain (@aikidosec/safe-chain) is not scanning this install because its shell' +
12
+ ` integration is not active. Install it with the hash-verified steps at ${INSTALL_GUIDE_URL}` +
10
13
  ' - then restart your terminal.'
11
14
  // `preinstall` runs before any dependency is installed, kit included, so the check is plain Node with
12
15
  // no imports. It never fails the install and stays silent on CI, where the runner is set up instead.
13
16
  // The source avoids double quotes, `$` and backticks so the shell passes it to Node verbatim.
14
17
  const LOCAL_INTEGRATION_CHECK_SOURCE = `if(!process.env.CI&&!process.env.GLOBAL_AGENT_HTTP_PROXY)console.warn('${INTEGRATION_HINT}')`
15
- const LOCAL_INTEGRATION_CHECK_CMD = `node -e "${LOCAL_INTEGRATION_CHECK_SOURCE}"`
18
+ const SAFE_CHAIN_CMD = `node -e "${LOCAL_INTEGRATION_CHECK_SOURCE}"`
19
+ // Every `preinstall` an earlier kit wrote: `setup-ci` alone, pinned or not, or followed by the
20
+ // `node -e` check of any earlier wording.
21
+ const LEGACY_PREINSTALL_RE =
22
+ /^pnpm dlx @aikidosec\/safe-chain(?:@\S+)? setup-ci(?: && node -e "[^"]*")?$/u
16
23
 
17
- function with_local_check(setup_command: string): string {
18
- return `${setup_command} && ${LOCAL_INTEGRATION_CHECK_CMD}`
19
- }
20
-
21
- // Upgrade a `preinstall` kit wrote before the check existed, pinned version included; any other
22
- // value is the consumer's own and is left exactly as it is.
24
+ // Replace a `preinstall` kit wrote earlier; any other value is the consumer's own and is left exactly
25
+ // as it is.
23
26
  function migrate_preinstall(value: string): string {
24
- if (!LEGACY_PREINSTALL_RE.test(value)) return value
25
-
26
- return with_local_check(value)
27
+ return LEGACY_PREINSTALL_RE.test(value) ? SAFE_CHAIN_CMD : value
27
28
  }
28
29
 
29
- const SAFE_CHAIN_CMD = with_local_check(SETUP_CI_CMD)
30
-
31
30
  const safe_chain_preinstall = {
32
31
  SAFE_CHAIN_CMD,
33
- LOCAL_INTEGRATION_CHECK_CMD,
34
32
  LOCAL_INTEGRATION_CHECK_SOURCE,
35
33
  INTEGRATION_HINT,
34
+ INSTALL_GUIDE_ANCHOR,
36
35
  migrate_preinstall,
37
36
  }
38
37
 
@@ -8,7 +8,6 @@ import { z } from 'zod'
8
8
 
9
9
  const WORKSPACE_FILE = 'pnpm-workspace.yaml'
10
10
  const PROJECT_CONFIG_FILE = '.aikido'
11
- const NPMRC_FILE = '.npmrc'
12
11
  const EXCLUSIONS_FIELD = 'minimumPackageAgeExclusions'
13
12
  const AGE_FIELD = 'minimumPackageAgeHours'
14
13
  const MINUTES_PER_HOUR = 60
@@ -78,18 +77,22 @@ function replace_safe_chain_block(existing: string, safe_chain: Record<string, u
78
77
  }
79
78
 
80
79
  // Safe Chain enforces its own minimum package age (48 hours by default), so CI refused lockfiles pnpm
81
- // had resolved under `.npmrc`'s shorter window (kit #2743). `.npmrc` is the source: its minutes are
82
- // floored to whole hours so Safe Chain is never stricter than pnpm, and an undeclared window writes
83
- // nothing; an explicit `0` opt-out is written as 0 so an earlier synchronized age cannot outlive it.
84
- function package_age_hours(npmrc: string): number | undefined {
85
- const minutes = release_age.parse_declared_minimum_release_age(npmrc)
80
+ // had resolved under pnpm's shorter window (kit #2743). The workspace's `minimumReleaseAge` is the
81
+ // source: its minutes are floored to whole hours so Safe Chain is never stricter than pnpm, and an
82
+ // undeclared window writes nothing; an explicit `0` opt-out is written as 0 so an earlier
83
+ // synchronized age cannot outlive it.
84
+ function package_age_hours(workspace: string): number | undefined {
85
+ const minutes = release_age.parse_declared_minimum_release_age(workspace)
86
86
  if (minutes === undefined) return undefined
87
87
 
88
88
  return Math.floor(minutes / MINUTES_PER_HOUR)
89
89
  }
90
90
 
91
- function merge_age(safe_chain: Record<string, unknown>, npmrc: string): Record<string, unknown> {
92
- const hours = package_age_hours(npmrc)
91
+ function merge_age(
92
+ safe_chain: Record<string, unknown>,
93
+ workspace: string,
94
+ ): Record<string, unknown> {
95
+ const hours = package_age_hours(workspace)
93
96
  if (hours === undefined) return safe_chain
94
97
 
95
98
  return { ...safe_chain, [AGE_FIELD]: hours }
@@ -107,22 +110,24 @@ function merge_exclusions(
107
110
  function has_nothing_to_write(
108
111
  existing: string,
109
112
  exclusions: ReadonlyArray<string>,
110
- npmrc: string,
113
+ workspace: string,
111
114
  ): boolean {
112
- return existing.length === 0 && exclusions.length === 0 && package_age_hours(npmrc) === undefined
115
+ return (
116
+ existing.length === 0 && exclusions.length === 0 && package_age_hours(workspace) === undefined
117
+ )
113
118
  }
114
119
 
115
120
  function read_exclusions(workspace: string): ReadonlyArray<string> {
116
121
  return workspace_schema.parse(yaml_document.parse_yaml(workspace)).minimumReleaseAgeExclude ?? []
117
122
  }
118
123
 
119
- function merge_project_config(existing: string, workspace: string, npmrc = ''): string {
124
+ function merge_project_config(existing: string, workspace: string): string {
120
125
  const exclusions = read_exclusions(workspace)
121
126
  const aikido = yaml_document.parse_yaml(existing)
122
127
  const safe_chain = read_mapping(aikido['safe-chain'], 'safe-chain')
123
- const merged = merge_age(merge_exclusions(safe_chain, exclusions), npmrc)
128
+ const merged = merge_age(merge_exclusions(safe_chain, exclusions), workspace)
124
129
  if (JSON.stringify(merged) === JSON.stringify(safe_chain)) return existing
125
- if (has_nothing_to_write(existing, exclusions, npmrc)) return existing
130
+ if (has_nothing_to_write(existing, exclusions, workspace)) return existing
126
131
 
127
132
  return replace_safe_chain_block(existing, merged)
128
133
  }
@@ -133,8 +138,7 @@ function sync_project_config(root: string): boolean {
133
138
  const config_path = path.join(root, PROJECT_CONFIG_FILE)
134
139
  const existing = file_reader.read_file_or_empty(config_path)
135
140
  const workspace = readFileSync(workspace_path, 'utf8')
136
- const npmrc = file_reader.read_file_or_empty(path.join(root, NPMRC_FILE))
137
- const merged = merge_project_config(existing, workspace, npmrc)
141
+ const merged = merge_project_config(existing, workspace)
138
142
  if (merged === existing) return false
139
143
  writeFileSync(config_path, merged)
140
144
 
@@ -71,7 +71,7 @@ function sync_workspace_yaml(
71
71
  destination_path: string,
72
72
  is_force = false,
73
73
  ): boolean {
74
- const template = readFileSync(template_path, 'utf8')
74
+ const template = init_ai_copy.read_workspace_template(template_path, destination_path)
75
75
  const existing = is_force ? '' : file_reader.read_file_or_empty(destination_path)
76
76
  const merged = init_logic.merge_workspace_yaml(existing, template)
77
77
 
@@ -29,7 +29,8 @@ function sync_with_merge(
29
29
  console.info(` ✔ synced ${destination_name}`)
30
30
  }
31
31
 
32
- // Append-only, and deliberately so: a `_authToken` line in the consumer's file is left alone.
32
+ // Removes only the exact settings lines earlier kit releases wrote, which pnpm 12 ignores in
33
+ // `.npmrc` (joshuafolkken/kit#3267); every other line, a `_authToken` line included, is left alone.
33
34
  // Sync used to remove the env-var form, which broke every project that had opted its project
34
35
  // .npmrc in via `npmrcAuthFile` — there the line is the live credential, and the opt-in itself
35
36
  // can live in a deploy platform's dashboard where sync cannot see it (joshuafolkken/kit#759).
@@ -192,12 +192,23 @@ function sync_prepare_lefthook_warning(destination_path: string): void {
192
192
  )
193
193
  }
194
194
 
195
+ // The `preinstall` earlier kits wrote fetched safe-chain on every install (joshuafolkken/kit#3269);
196
+ // `josh init` replaces it, and this covers everyone already initialized.
197
+ function sync_safe_chain_preinstall(destination_path: string): void {
198
+ sync_package_json_with(
199
+ destination_path,
200
+ (existing) => init_logic.upgrade_safe_chain_preinstall(existing),
201
+ ' ✔ synced preinstall safe-chain check',
202
+ )
203
+ }
204
+
195
205
  // Every migration an already-initialized consumer needs applied to its own manifest, run as one
196
206
  // group so the next one is added here rather than at the call site.
197
207
  function sync_package_json_migrations(destination_path: string): void {
198
208
  sync_package_manager_version(destination_path)
199
209
  sync_secretlint_development_deps(destination_path)
200
210
  sync_prepare_lefthook_warning(destination_path)
211
+ sync_safe_chain_preinstall(destination_path)
201
212
  }
202
213
 
203
214
  // Two distributed artifacts each depend on a repository setting kit cannot write, and both reports
@@ -230,8 +241,8 @@ function sync_project_artifacts(is_force: boolean): void {
230
241
 
231
242
  sync_sonar_with_template(name_with_owner, is_force)
232
243
  sync_config_files()
233
- // After `sync_config_files`, which may add the `.npmrc` window `.aikido`'s age is derived from
234
- // (joshuafolkken/kit#2743).
244
+ // After `sync_ai_copy_all`, which may add the `pnpm-workspace.yaml` window `.aikido`'s age is
245
+ // derived from (joshuafolkken/kit#2743, #3267).
235
246
  project_config.sync_project_config(PROJECT_ROOT)
236
247
  sync_package_json_migrations(path.join(PROJECT_ROOT, PACKAGE_JSON))
237
248
  report_repository_settings(name_with_owner)
@@ -277,6 +288,8 @@ const sync = {
277
288
  sync_package_manager_version,
278
289
  sync_secretlint_development_deps,
279
290
  sync_prepare_lefthook_warning,
291
+ sync_safe_chain_preinstall,
292
+ sync_package_json_migrations,
280
293
  migrate_prettierrc: did_migrate_prettierrc,
281
294
  }
282
295
 
@@ -227,6 +227,7 @@ async function with_run_marker<T>(run: () => Promise<T>): Promise<T> {
227
227
  const unit_worker_share = {
228
228
  MIN_WORKERS,
229
229
  NESTED_KEY,
230
+ NESTED_VALUE,
230
231
  RUN_PREFIX,
231
232
  SOLO_RUNS,
232
233
  current_share,
@@ -49,7 +49,11 @@ const NO_FINISH = 0
49
49
 
50
50
  // The launch's own result body, which is the only place the harness writes the id it assigns. Read
51
51
  // as a derived fact and the body discarded, exactly as `has_failure_line` and `followup_stages` are.
52
- const LAUNCH_PATTERN = /running in background with ID:\s*([\w-]+)/iu
52
+ // **A foreground call that outran its timeout is a launch too** (joshuafolkken/kit#3304): the harness
53
+ // moves it to the background and says so as `moved to the background (ID: <id>)`. Missing that spelling
54
+ // let the #3273 lane child end its turn on a running task the stop hook never saw.
55
+ const LAUNCH_PATTERN =
56
+ /(?:running in background with ID:|moved to the background \(ID:)\s*([\w-]+)/iu
53
57
  // A backgrounded subagent's launch result, which names its id on an `agentId:` line of its own.
54
58
  const AGENT_LAUNCH_PATTERN = /^Async agent launched[\s\S]*?\bagentId:\s*([\w-]+)/u
55
59
  // The notice the harness writes when a task it took into the background ends
@@ -10,7 +10,9 @@
10
10
  // clone `CLAUDE.md` prohibits, and the two copies would disagree the first time one of them learned
11
11
  // to accept another format.
12
12
  function parse_instant(raw: string | null | undefined): number | undefined {
13
- const parsed = Date.parse(raw ?? '')
13
+ if (raw === null || raw === undefined) return undefined
14
+
15
+ const parsed = Date.parse(raw)
14
16
 
15
17
  return Number.isNaN(parsed) ? undefined : parsed
16
18
  }
@@ -1,16 +1,14 @@
1
1
  import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
2
2
  import type { FollowupStage } from '#scripts/followup/git-followup-stages'
3
3
  import { time_background } from './time-background'
4
- import { time_bundle_call } from './time-bundle-call'
5
4
  import { time_followup_stage } from './time-followup-stage'
6
- import { time_markers, type PhaseMarker } from './time-markers'
7
5
  import { time_reported_failure } from './time-reported-failure'
8
6
  import { time_shell } from './time-shell'
9
- import { time_single_check } from './time-single-check'
7
+ import { time_tool_call, type ToolCall } from './time-tool-call'
10
8
  import { time_transcript_line, type Block, type TranscriptLine } from './time-transcript-line'
11
- import { time_writes } from './time-writes'
12
9
 
13
10
  const { NO_MESSAGE_ID, parse_line } = time_transcript_line
11
+ const { NO_CALL, UNKNOWN_CALL, UNKNOWN_TOOL, to_tool_call } = time_tool_call
14
12
 
15
13
  // Turning a session transcript into timed spans (joshuafolkken/kit#1267).
16
14
  //
@@ -24,6 +22,10 @@ const { NO_MESSAGE_ID, parse_line } = time_transcript_line
24
22
  // the move changed no call site. `cost_blocks`' three type constants are still imported rather than
25
23
  // restated.
26
24
  //
25
+ // **Reading a `tool_use` block into a `ToolCall` is `time-tool-call.ts`'s** since
26
+ // joshuafolkken/kit#3264, when this file reached its length limit again. `UNKNOWN_TOOL` and
27
+ // `to_tool_call` are re-exported below, so that move changed no call site either.
28
+ //
27
29
  // **The partition is by gap, not by pair.** Every span is the interval between two consecutive
28
30
  // events, classified by the *later* one: a span ending at an assistant line is model wait, one
29
31
  // ending at a tool result is that tool's execution, one ending at a user prompt is human wait.
@@ -34,7 +36,6 @@ const { NO_MESSAGE_ID, parse_line } = time_transcript_line
34
36
 
35
37
  const ASSISTANT_TYPE = 'assistant'
36
38
  const USER_TYPE = 'user'
37
- const UNKNOWN_TOOL = 'unknown'
38
39
 
39
40
  type SpanCategory = 'model' | 'tool' | 'human'
40
41
 
@@ -90,96 +91,6 @@ const NO_RESULT: ResultFacts = {
90
91
  followup_stages: time_followup_stage.NO_STAGES,
91
92
  }
92
93
 
93
- // What a tool span is labelled with. `josh_command` is empty for everything that is not a
94
- // `pnpm josh <cmd>` invocation, and the report drops empty labels rather than printing a bucket.
95
- //
96
- // `marker` names the workflow boundary the call is, for the phase breakdown that slices the same
97
- // spans by stage (joshuafolkken/kit#1269). It is carried here rather than re-derived later because
98
- // the tool's *input* is what decides it, and a span keeps no input — only the label read off it.
99
- //
100
- // `is_bundleable` and `targets` are carried for exactly that reason too (joshuafolkken/kit#1344).
101
- // Whether a call could have gone out beside another, and what it names, are both read off the input —
102
- // so a module asking about them after the fact would have nothing to read. `time-bundle-call.ts`
103
- // decides both; the rule each one follows is stated there.
104
- //
105
- // `check_key` is the fourth, and the third time this reason has applied (joshuafolkken/kit#1383). Two
106
- // runs of one verification check are the same call only if they named the same files, and the files
107
- // are in the input — so `josh_command` alone cannot say, and nothing downstream could recover it.
108
- // `time-single-check.ts` decides it.
109
- //
110
- // `message_id` is the fifth, and it is the one that says which *turn* a call belongs to
111
- // (joshuafolkken/kit#1406). It is read off the line the `tool_use` block sat on rather than off the
112
- // result that closes the span, because a `tool_result` line carries no message id at all — so a span
113
- // that did not keep it here could never be attributed to the turn that issued it.
114
- //
115
- // `writes` is the sixth, and it is what `targets` could not say (joshuafolkken/kit#1472). `targets`
116
- // names what a call mentioned; `marker` says a call was *an* edit but not of what, and for a tool
117
- // outside `BUNDLEABLE_TOOLS` it arrived with no target at all. So a consumer subtracting edits from
118
- // reads — `investigation-reads.ts` — had nothing to subtract for `MultiEdit` / `NotebookEdit`, and
119
- // nothing at all to tell an in-place `sed -i` from the `sed -n` it shares a label with.
120
- // `time-writes.ts` decides it, from the input this span is about to discard.
121
- // `issue` is the seventh, and it is the one `branch` could not carry (joshuafolkken/kit#1617). Under
122
- // lanes the session writing the transcript stays on the default branch while the work runs in a linked
123
- // work tree, so `branch` names no issue on any line of the run; the `in-progress` label call does, and
124
- // like every field above it that answer is read off the *input* a span is about to discard.
125
- // `time_markers.NO_ISSUE` for every call that declares nothing, which is all but one call per run.
126
- interface ToolCall {
127
- label: string
128
- josh_command: string
129
- // Every josh subcommand the call ran, where `josh_command` is only the first (joshuafolkken/kit#1883).
130
- // A chained `pnpm josh lint:related && pnpm josh test:related` carries both; the count tables expand
131
- // it so the ones `josh_command` drops are still counted.
132
- josh_commands: ReadonlyArray<string>
133
- check_key: string
134
- marker: PhaseMarker
135
- is_bundleable: boolean
136
- targets: ReadonlyArray<string>
137
- // Carried beside `targets` because sharing a target means something different depending on it: a
138
- // second write to one file is independent work, a read of a file just written is not
139
- // (joshuafolkken/kit#1509).
140
- is_writing: boolean
141
- // The conservative write answer `is_read_only_call` asks of a live call, carried so a reader of the
142
- // transcript asks the same read-only question as the guard (joshuafolkken/kit#3139).
143
- may_write: boolean
144
- // Whether this is a subagent launch whose prompt builds on an earlier launch's finding
145
- // (joshuafolkken/kit#1854). Carried for the reason every field here is — the prompt it is read from
146
- // is the input, and a span keeps none. `time-agent-bundles.ts` reads it.
147
- has_prior_reference: boolean
148
- writes: ReadonlyArray<string>
149
- message_id: string
150
- issue: number
151
- // The background run this call reads the output of, and `''` for every other call
152
- // (joshuafolkken/kit#1662). It is the eighth field carried for the reason the seven above are:
153
- // the id sits in the command string, which a span does not keep, and it is the only thing that
154
- // says a `tail` two turns later is the join of a command started minutes ago.
155
- reads_background: string
156
- }
157
-
158
- const NO_CALL: ToolCall = {
159
- label: '',
160
- josh_command: '',
161
- josh_commands: [],
162
- check_key: time_single_check.NO_CHECK,
163
- marker: time_markers.NO_MARKER,
164
- message_id: NO_MESSAGE_ID,
165
- writes: [],
166
- issue: time_markers.NO_ISSUE,
167
- reads_background: time_background.NO_BACKGROUND,
168
- ...time_bundle_call.not_bundleable(),
169
- }
170
- const UNKNOWN_CALL: ToolCall = {
171
- label: UNKNOWN_TOOL,
172
- josh_command: '',
173
- josh_commands: [],
174
- check_key: time_single_check.NO_CHECK,
175
- marker: time_markers.NO_MARKER,
176
- message_id: NO_MESSAGE_ID,
177
- writes: [],
178
- issue: time_markers.NO_ISSUE,
179
- reads_background: time_background.NO_BACKGROUND,
180
- ...time_bundle_call.not_bundleable(),
181
- }
182
-
183
94
  // `ended_ms` is the absolute instant the span closed, so `[ended_ms - duration_ms, ended_ms]` is the
184
95
  // interval it occupied. A duration alone cannot say *when*, and two things need that: the CI wait,
185
96
  // which is the part of the PR-open→merge window no span covers (joshuafolkken/kit#1268), and the
@@ -242,50 +153,6 @@ interface Timeline {
242
153
  spans: Array<Span>
243
154
  }
244
155
 
245
- // Everything but Bash: the tool's own name is the label, and nothing it runs is a shell command to
246
- // read a check key, a josh command or a declared issue off.
247
- function non_bash_call(name: string, input: unknown, message_id: string): ToolCall {
248
- return {
249
- label: name,
250
- josh_command: '',
251
- josh_commands: [],
252
- check_key: time_single_check.NO_CHECK,
253
- marker: time_markers.tool_marker(name, input),
254
- message_id,
255
- writes: time_writes.tool_writes(name, input),
256
- issue: time_markers.NO_ISSUE,
257
- // A non-Bash tool joins a backgrounded command too — `BashOutput` carries the shell id in a
258
- // field of its own, `Monitor` names the same `…/tasks/<id>.output` path in a command — so the
259
- // same reader is asked here rather than the field being left empty for every tool but `Bash`.
260
- reads_background: time_background.read_id_of(input),
261
- ...time_bundle_call.tool_facts(name, input),
262
- }
263
- }
264
-
265
- function bash_call(input: unknown, message_id: string): ToolCall {
266
- const command = time_shell.bash_command(input)
267
- const josh_command = time_shell.josh_command_of(command)
268
-
269
- return {
270
- label: time_shell.bash_label(command),
271
- josh_command,
272
- josh_commands: time_shell.josh_commands_of(command),
273
- check_key: time_single_check.check_key(josh_command, command),
274
- marker: time_markers.bash_marker(command),
275
- message_id,
276
- writes: time_writes.bash_writes(command),
277
- issue: time_markers.bash_issue(command),
278
- reads_background: time_background.read_id_of(input),
279
- ...time_bundle_call.bash_facts(command),
280
- }
281
- }
282
-
283
- function to_tool_call(name: string, input: unknown, message_id: string): ToolCall {
284
- if (name !== cost_blocks.BASH_TOOL) return non_bash_call(name, input, message_id)
285
-
286
- return bash_call(input, message_id)
287
- }
288
-
289
156
  // Identified calls only. A `tool_use` written without an `id` would otherwise be registered under
290
157
  // the empty string, where the next result that carries no `tool_use_id` would match it and be
291
158
  // labelled with an unrelated tool — a wrong name where `UNKNOWN_TOOL` is the honest one.