@joshuafolkken/kit 1.1178.0 → 1.1229.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/settings.json +2 -2
  3. package/.claude/skills/dependency-update/SKILL.md +4 -8
  4. package/.claude/skills/epic-commands/SKILL.md +2 -2
  5. package/.claude/skills/epic-commands/epic-bundle.md +1 -1
  6. package/.claude/skills/workflow-commands/SKILL.md +2 -4
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +6 -2
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -2
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
  13. package/.claude/skills/workflow-commands/chain-rule.md +1 -1
  14. package/.claude/skills/workflow-commands/delegation.md +2 -2
  15. package/.claude/skills/workflow-commands/followup.md +5 -0
  16. package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +1 -1
  18. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  19. package/.claude/skills/workflow-commands/kickoff.md +1 -1
  20. package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
  21. package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
  22. package/.claude/skills/workflow-commands/prerequisite.md +2 -2
  23. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  24. package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
  25. package/.codex/hooks.json +2 -2
  26. package/.cursorrules +6 -41
  27. package/.github/actions/setup-node/action.yml +1 -1
  28. package/.github/workflows/auto-tag.yml +3 -4
  29. package/.github/workflows/ci.yml +32 -31
  30. package/.github/workflows/dependabot-auto-merge.yml +13 -6
  31. package/.github/workflows/publish.yml +48 -4
  32. package/.github/workflows/sonar-qube.yml +3 -0
  33. package/.pnpmfile.mjs +4 -3
  34. package/.prettierignore +8 -2
  35. package/CLAUDE.md +18 -20
  36. package/README.md +26 -19
  37. package/SECURITY.md +15 -1
  38. package/dist/CLAUDE.md +18 -20
  39. package/dist/commands/document-section-cli.js +734 -0
  40. package/dist/hooks/audit-provision.js +346 -0
  41. package/dist/hooks/batch-guard.js +5 -4
  42. package/dist/hooks/{chunk-S6X6WYKR.js → chunk-55HHG5PX.js} +1210 -711
  43. package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-5VMKCCC4.js} +25 -10
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/{chunk-32GQTL4F.js → chunk-6HJJXRBX.js} +4 -2
  46. package/dist/hooks/chunk-CDY4NFQG.js +15 -0
  47. package/dist/hooks/chunk-ELCN5LCH.js +135 -0
  48. package/dist/hooks/{chunk-VESKRQOF.js → chunk-F56MFS4B.js} +3 -3
  49. package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-FIVDHQGT.js} +7 -6
  50. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  51. package/dist/hooks/{chunk-G6KRCKEC.js → chunk-JUDKDCDP.js} +496 -378
  52. package/dist/hooks/{chunk-GDS5N2KB.js → chunk-NV2JRZZO.js} +718 -681
  53. package/dist/hooks/{chunk-VQLLNFNT.js → chunk-RNWHN5BP.js} +3 -3
  54. package/dist/hooks/{chunk-DO6NWWHU.js → chunk-TQNPDZNH.js} +4 -4
  55. package/dist/hooks/{chunk-I4XSL4TT.js → chunk-VC3PZPZO.js} +1 -1
  56. package/dist/hooks/codex-hook-adapter.js +14 -12
  57. package/dist/hooks/duplicate-read-guard.js +6 -5
  58. package/dist/hooks/format-edited.js +4 -3
  59. package/dist/hooks/inputs.json +217 -195
  60. package/dist/hooks/investigation-guard.js +6 -5
  61. package/dist/hooks/pretool-guard.js +12 -10
  62. package/dist/hooks/session-lang.js +2 -4
  63. package/dist/hooks/stop-guard.js +66 -58
  64. package/dist/josh.js +868 -74
  65. package/dist/version/index.d.ts +7 -2
  66. package/dist/version/index.js +152 -75
  67. package/eslint/base.js +23 -1
  68. package/eslint/rules/code-quality.js +3 -0
  69. package/eslint/rules/early-return-one-liner.js +98 -0
  70. package/eslint/rules/unicorn.js +16 -1
  71. package/package.json +7 -6
  72. package/pnpm-workspace.yaml +9 -4
  73. package/prettier/basic.d.ts +3 -1
  74. package/prettier/index.d.ts +3 -1
  75. package/prettier/static.d.ts +3 -1
  76. package/prompts/coding-standards.md +1 -1
  77. package/prompts/collaboration-workflow/file-edits.md +4 -4
  78. package/prompts/collaboration-workflow/glossary.md +1 -1
  79. package/prompts/collaboration-workflow/operating-rules.md +17 -9
  80. package/prompts/collaboration-workflow/overview.md +2 -2
  81. package/prompts/collaboration-workflow/principles.md +14 -12
  82. package/prompts/collaboration-workflow/report-format.md +1 -1
  83. package/prompts/collaboration-workflow/residency.md +1 -1
  84. package/prompts/collaboration-workflow/rule-delivery.md +6 -6
  85. package/prompts/collaboration-workflow/shell-body.md +6 -6
  86. package/prompts/collaboration-workflow/turn-batching.md +1 -1
  87. package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
  88. package/prompts/collaboration-workflow/wip-cap.md +10 -10
  89. package/prompts/review-rubric.md +1 -2
  90. package/prompts/review.md +1 -1
  91. package/scripts/agent/agent-headless.ts +29 -0
  92. package/scripts/agent/agent-launch-environment.ts +6 -2
  93. package/scripts/agent/codex-agent-argv.ts +2 -1
  94. package/scripts/auto-ok/auto-ok-cli.ts +1 -1
  95. package/scripts/backlog/backlog-arrival.ts +3 -3
  96. package/scripts/backlog/backlog-budget.ts +1 -1
  97. package/scripts/backlog/backlog-drive-args.ts +135 -0
  98. package/scripts/backlog/backlog-drive-cli.ts +11 -137
  99. package/scripts/backlog/backlog-drive-launch.ts +39 -0
  100. package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
  101. package/scripts/backlog/backlog-drive-named.ts +3 -3
  102. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  103. package/scripts/backlog/backlog-drive-owner.ts +1 -1
  104. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  105. package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
  106. package/scripts/backlog/backlog-drive.ts +1 -1
  107. package/scripts/backlog/backlog-next.ts +16 -5
  108. package/scripts/backlog/backlog-offer-cli.ts +6 -6
  109. package/scripts/backlog/backlog-offer.ts +1 -1
  110. package/scripts/backlog/backlog-plan-cli.ts +1 -1
  111. package/scripts/backlog/backlog-pool.ts +57 -11
  112. package/scripts/backlog/backlog-ready.ts +1 -1
  113. package/scripts/backlog/backlog-scope.ts +1 -1
  114. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  115. package/scripts/backlog/backlog-stalled.ts +1 -1
  116. package/scripts/build/build-codex-hooks.ts +3 -1
  117. package/scripts/build/build-commands.ts +66 -0
  118. package/scripts/build/build-hooks.ts +2 -1
  119. package/scripts/build/build-library.ts +12 -3
  120. package/scripts/build/build.ts +70 -0
  121. package/scripts/build/local-bin.ts +3 -13
  122. package/scripts/delegation/investigation-reads.ts +3 -1
  123. package/scripts/discovery/repo-origin.ts +5 -5
  124. package/scripts/doctor/doctor-consumer.ts +2 -2
  125. package/scripts/doctor/doctor-io.ts +2 -3
  126. package/scripts/document/capped-print-part.ts +30 -0
  127. package/scripts/document/capped-print.ts +3 -4
  128. package/scripts/document/document-byte-budget.ts +11 -2
  129. package/scripts/document/document-scan.ts +117 -0
  130. package/scripts/document/document-section.ts +143 -16
  131. package/scripts/document/entry-read-budget.ts +5 -3
  132. package/scripts/document/entry-read-set.ts +10 -89
  133. package/scripts/document/read-set-cost.ts +98 -0
  134. package/scripts/epic/epic-add-body.ts +1 -0
  135. package/scripts/epic/epic-audit-checks.ts +1 -1
  136. package/scripts/epic/epic-audit-rationale.ts +4 -3
  137. package/scripts/epic/epic-bundle-cli.ts +14 -3
  138. package/scripts/epic/epic-bundle-poll.ts +41 -0
  139. package/scripts/epic/epic-busy.ts +1 -1
  140. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  141. package/scripts/epic/epic-classify.ts +1 -1
  142. package/scripts/epic/epic-cli-argv.ts +78 -46
  143. package/scripts/epic/epic-cli-remove.ts +11 -19
  144. package/scripts/epic/epic-cli.ts +74 -73
  145. package/scripts/epic/epic-close-comment.ts +1 -1
  146. package/scripts/epic/epic-close.ts +1 -1
  147. package/scripts/epic/epic-cross-repo.ts +5 -5
  148. package/scripts/epic/epic-graph.ts +1 -1
  149. package/scripts/epic/epic-index.ts +1 -1
  150. package/scripts/epic/epic-issue.ts +2 -6
  151. package/scripts/epic/epic-lane-offer.ts +2 -2
  152. package/scripts/epic/epic-next.ts +29 -42
  153. package/scripts/epic/epic-solo-stale.ts +1 -1
  154. package/scripts/epic/epic-triage.ts +8 -4
  155. package/scripts/epic/epic-validate.ts +1 -1
  156. package/scripts/followup/git-followup-finish.ts +2 -2
  157. package/scripts/followup/git-followup-label.ts +2 -2
  158. package/scripts/followup/git-followup-merged.ts +2 -2
  159. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  160. package/scripts/followup/git-pr-followup.ts +4 -129
  161. package/scripts/gate/core-budget.ts +69 -1
  162. package/scripts/gate/gate-plan.ts +47 -7
  163. package/scripts/gate/gate-skip.ts +38 -7
  164. package/scripts/gate/hook-gate-reuse.ts +12 -10
  165. package/scripts/gate/pre-commit-type-check.ts +4 -1
  166. package/scripts/gate/project-checks.ts +6 -9
  167. package/scripts/gate/type-check-step.ts +2 -3
  168. package/scripts/gate/verification-gate.ts +27 -8
  169. package/scripts/gh/git-gh-api-path.ts +2 -2
  170. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  171. package/scripts/gh/git-gh-issue-write.ts +53 -7
  172. package/scripts/gh/git-gh-pr-read.ts +64 -19
  173. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  174. package/scripts/gh/git-gh-pr.ts +5 -1
  175. package/scripts/gh/git-pr-ai-review.ts +1 -1
  176. package/scripts/gh/git-pr-checks-eval.ts +1 -3
  177. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  178. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  179. package/scripts/gh/git-pr-checks.ts +7 -5
  180. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  181. package/scripts/gh/git-pr-error.ts +1 -3
  182. package/scripts/gh/git-pr.ts +9 -13
  183. package/scripts/gh/github-issue-url.ts +2 -2
  184. package/scripts/git/constants.ts +2 -6
  185. package/scripts/git/git-command.ts +20 -218
  186. package/scripts/git/git-diff-reads.ts +225 -0
  187. package/scripts/git/git-error.ts +4 -12
  188. package/scripts/git/git-pre-push-hook.ts +139 -0
  189. package/scripts/git/git-prompt.ts +2 -6
  190. package/scripts/git/git-spawn-sync.ts +3 -1
  191. package/scripts/git/git-spawn.ts +1 -1
  192. package/scripts/git/git-staging.ts +4 -6
  193. package/scripts/git/git-status.ts +3 -9
  194. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  195. package/scripts/hooks/edited-cspell.ts +2 -4
  196. package/scripts/hooks/format-edited-file.ts +3 -12
  197. package/scripts/hooks/pre-push-unit.ts +31 -9
  198. package/scripts/hooks/reserved-run.ts +2 -2
  199. package/scripts/hooks/run-hook.sh +5 -4
  200. package/scripts/hooks/stop-guard.ts +5 -3
  201. package/scripts/init/init-actions.ts +0 -5
  202. package/scripts/init/init-ai-copy.ts +13 -2
  203. package/scripts/init/init-logic-json-merge.ts +19 -0
  204. package/scripts/init/init-logic-secretlint.ts +7 -7
  205. package/scripts/init/init-logic-workspace.ts +41 -2
  206. package/scripts/init/init-logic.ts +16 -26
  207. package/scripts/init/init.ts +5 -15
  208. package/scripts/init/initial-commit.ts +4 -4
  209. package/scripts/init/kit-development-versions.ts +40 -0
  210. package/scripts/init/kit-setup-state.ts +2 -2
  211. package/scripts/init/start-exec.ts +68 -19
  212. package/scripts/init/start-setup-pr.ts +9 -9
  213. package/scripts/init/start-steps.ts +6 -10
  214. package/scripts/issue/defect-rate-cli.ts +1 -1
  215. package/scripts/issue/git-next-issues.ts +1 -1
  216. package/scripts/issue/issue-auto-ok.ts +1 -1
  217. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  218. package/scripts/issue/issue-backlinks.ts +9 -9
  219. package/scripts/issue/issue-comment-cli.ts +19 -13
  220. package/scripts/issue/issue-file-cli.ts +45 -18
  221. package/scripts/issue/issue-file.ts +24 -1
  222. package/scripts/issue/issue-scout-closed.ts +1 -1
  223. package/scripts/issue/issue-state-cli.ts +30 -74
  224. package/scripts/josh/josh-command-map.ts +2 -0
  225. package/scripts/josh/josh-command-types.ts +11 -0
  226. package/scripts/josh/josh-commands-ai.ts +4 -4
  227. package/scripts/josh/josh-commands-backlog.ts +4 -1
  228. package/scripts/josh/josh-commands-development.ts +10 -0
  229. package/scripts/josh/josh-commands-document.ts +1 -0
  230. package/scripts/josh/josh-commands-lane.ts +22 -0
  231. package/scripts/josh/josh-commands-maintenance.ts +4 -4
  232. package/scripts/josh/josh-commands-run.ts +12 -7
  233. package/scripts/josh/josh-in-process.ts +28 -9
  234. package/scripts/josh/josh-logic.ts +33 -15
  235. package/scripts/josh/process-identity.ts +1 -3
  236. package/scripts/josh/session-language-cli.ts +3 -4
  237. package/scripts/lane/lane-await.ts +5 -1
  238. package/scripts/lane/lane-child-invocation.ts +2 -2
  239. package/scripts/lane/lane-cli.ts +3 -3
  240. package/scripts/lane/lane-dispatch.ts +14 -10
  241. package/scripts/lane/lane-environment.ts +5 -1
  242. package/scripts/lane/lane-handoff.ts +1 -1
  243. package/scripts/lane/lane-install.ts +2 -13
  244. package/scripts/lane/lane-launch-cli.ts +15 -1
  245. package/scripts/lane/lane-ledger.ts +145 -0
  246. package/scripts/lane/lane-leftover.ts +1 -1
  247. package/scripts/lane/lane-load.ts +108 -0
  248. package/scripts/lane/lane-measure-cli.ts +149 -0
  249. package/scripts/lane/lane-occupancy.ts +3 -0
  250. package/scripts/lane/lane-reap.ts +1 -3
  251. package/scripts/lane/lane-relaunch.ts +1 -1
  252. package/scripts/lane/lane-stats.ts +224 -0
  253. package/scripts/lane/lane-vacant.ts +36 -0
  254. package/scripts/lane/openai-lane-ship.ts +2 -2
  255. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  256. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  257. package/scripts/lane/openai-review-broker.ts +2 -4
  258. package/scripts/lib/ancestor-directories.ts +35 -0
  259. package/scripts/lib/animation-helpers.ts +2 -2
  260. package/scripts/lib/buffered-process.ts +3 -7
  261. package/scripts/lib/cli-flags.ts +71 -1
  262. package/scripts/lib/spawn-exit.ts +2 -2
  263. package/scripts/lib/timeouts.ts +35 -2
  264. package/scripts/lines/line-budget.ts +4 -6
  265. package/scripts/notify/telegram-test.ts +6 -0
  266. package/scripts/observations/observation-ledger-line.ts +6 -6
  267. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  268. package/scripts/observations/observation-ledger.ts +26 -7
  269. package/scripts/overrides/lockfile-overrides.ts +1 -1
  270. package/scripts/overrides/overrides-logic.ts +1 -1
  271. package/scripts/propagate/propagate-git.ts +1 -1
  272. package/scripts/propagate/propagate-publish.ts +2 -2
  273. package/scripts/propagate/propagate-steps.ts +20 -15
  274. package/scripts/propagate/propagate.ts +36 -27
  275. package/scripts/refactor/import-graph.ts +18 -1
  276. package/scripts/refactor/refactor-lint.ts +4 -4
  277. package/scripts/repo/repository-labels.ts +2 -2
  278. package/scripts/repo/required-checks-logic.ts +1 -3
  279. package/scripts/review/live-evidence.ts +2 -4
  280. package/scripts/review/review-brief-cli.ts +1 -1
  281. package/scripts/review/review-finding-ledger.ts +1 -1
  282. package/scripts/review/review-findings-cli.ts +1 -3
  283. package/scripts/rules/decision-oracle-batch.ts +128 -0
  284. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  285. package/scripts/rules/decision-oracle.ts +8 -120
  286. package/scripts/rules/delivered-rules.ts +26 -182
  287. package/scripts/rules/direct-filing.ts +1 -1
  288. package/scripts/rules/early-heartbeat.ts +3 -3
  289. package/scripts/rules/git-argv.ts +1 -1
  290. package/scripts/rules/git-force.ts +1 -1
  291. package/scripts/rules/implementation-cut.ts +2 -2
  292. package/scripts/rules/index-guard.ts +1 -1
  293. package/scripts/rules/issue-comments.ts +118 -0
  294. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  295. package/scripts/rules/lane-background.ts +2 -2
  296. package/scripts/rules/lane-park.ts +18 -0
  297. package/scripts/rules/lane-split-park.ts +128 -0
  298. package/scripts/rules/oracle-firing.ts +1 -1
  299. package/scripts/rules/poll-loop.ts +1 -1
  300. package/scripts/rules/pre-gate-cut.ts +1 -1
  301. package/scripts/rules/protected-files.ts +59 -4
  302. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  303. package/scripts/rules/shell-body-trigger.ts +1 -1
  304. package/scripts/rules/stop-rules.ts +18 -7
  305. package/scripts/rules/test-declared-commit.ts +4 -4
  306. package/scripts/rules/wip-cap.ts +63 -0
  307. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  308. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  309. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  310. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
  311. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  312. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +2 -2
  313. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  314. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  315. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  316. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  317. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  318. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +16 -2
  319. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  320. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  321. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  322. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  323. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  324. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  325. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  326. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  327. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  328. package/scripts/run/run-ending.ts +2 -2
  329. package/scripts/run/run-halfrun-resume.ts +6 -1
  330. package/scripts/run/run-headless.ts +1 -1
  331. package/scripts/run/run-invocation.ts +85 -84
  332. package/scripts/run/run-liveness.ts +1 -1
  333. package/scripts/run/run-parent-cut-hook.ts +1 -1
  334. package/scripts/run/run-preflight.ts +6 -6
  335. package/scripts/run/run-prep-cli.ts +1 -1
  336. package/scripts/run/run-prrun-resume.ts +1 -1
  337. package/scripts/run/run-report-cli.ts +4 -4
  338. package/scripts/run/run-report.ts +2 -2
  339. package/scripts/run/run-status-cli.ts +1 -1
  340. package/scripts/run/run-step-cli.ts +3 -3
  341. package/scripts/run/run-step.ts +2 -2
  342. package/scripts/run/run-stop-notify.ts +1 -1
  343. package/scripts/run/run-stranded-detect.ts +4 -4
  344. package/scripts/run/run-stranded.ts +2 -2
  345. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  346. package/scripts/run/run-watcher-guard.ts +5 -3
  347. package/scripts/run/run-watcher-hook.ts +1 -1
  348. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
  349. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  350. package/scripts/run/ship/run-ship-lock.ts +50 -0
  351. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  352. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  353. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
  354. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  355. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  356. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
  357. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  358. package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
  359. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  360. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +23 -8
  361. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +3 -3
  362. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
  363. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  364. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +8 -2
  365. package/scripts/run/wake/run-wake-restart.ts +58 -0
  366. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  367. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +1 -1
  368. package/scripts/safe-chain/preinstall-command.ts +20 -21
  369. package/scripts/safe-chain/project-config.ts +19 -15
  370. package/scripts/sync/sync-ai-files.ts +1 -1
  371. package/scripts/sync/sync-configs.ts +2 -1
  372. package/scripts/sync/sync.ts +15 -2
  373. package/scripts/test/unit-worker-share.ts +1 -0
  374. package/scripts/time-runtime/time-background.ts +5 -1
  375. package/scripts/time-runtime/time-instant.ts +3 -1
  376. package/scripts/time-runtime/time-spans.ts +6 -139
  377. package/scripts/time-runtime/time-tool-call.ts +160 -0
  378. package/scripts/version/effective-upstream.ts +2 -20
  379. package/scripts/version/latest-corepack.ts +17 -17
  380. package/scripts/version/latest-regression.ts +1 -1
  381. package/scripts/version/latest-update.ts +4 -4
  382. package/scripts/version/npm-registry.ts +20 -5
  383. package/scripts/version/publishable-range-check.ts +2 -2
  384. package/scripts/version/publishable-range.ts +1 -1
  385. package/scripts/version/release-age.ts +48 -38
  386. package/scripts/version/release-hold.ts +1 -1
  387. package/scripts/version/safe-chain-version-update.ts +40 -0
  388. package/scripts/version/upgrade-shell-command.ts +48 -8
  389. package/scripts/version/version-check-logic.ts +23 -14
  390. package/scripts/version/version-commands.ts +23 -11
  391. package/scripts/version/version-remote.ts +28 -13
  392. package/templates/pnpm-workspace.basic.yaml +4 -0
  393. package/templates/workflows/dependabot-auto-merge.yml +5 -5
  394. package/templates/workflows/github-release.yml +2 -0
  395. package/scripts/build/build-config-merge.ts +0 -27
  396. package/scripts/build/build-managed-marker.ts +0 -27
  397. package/scripts/build/build-self-sync-guard.ts +0 -27
  398. package/scripts/build/build-version.ts +0 -26
  399. package/scripts/version/preinstall-version-update.ts +0 -86
  400. /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
  401. /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  402. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  403. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  404. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  405. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  406. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  407. /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
  408. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  409. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  410. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  411. /package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +0 -0
  412. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  413. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  414. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  415. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  416. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  417. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  418. /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
  419. /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
  420. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  421. /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
  422. /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
  423. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  424. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  425. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -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.
@@ -0,0 +1,160 @@
1
+ import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
2
+ import { time_background } from './time-background'
3
+ import { time_bundle_call } from './time-bundle-call'
4
+ import { time_markers, type PhaseMarker } from './time-markers'
5
+ import { time_shell } from './time-shell'
6
+ import { time_single_check } from './time-single-check'
7
+ import { time_transcript_line } from './time-transcript-line'
8
+ import { time_writes } from './time-writes'
9
+
10
+ // Reading a `tool_use` block into the label a span carries — split out of `time-spans.ts` when it
11
+ // reached its line limit (joshuafolkken/kit#3264). `time_spans` re-exports `UNKNOWN_TOOL` and
12
+ // `to_tool_call` under the names they always had, so the move changed no call site.
13
+
14
+ const { NO_MESSAGE_ID } = time_transcript_line
15
+
16
+ const UNKNOWN_TOOL = 'unknown'
17
+
18
+ // What a tool span is labelled with. `josh_command` is empty for everything that is not a
19
+ // `pnpm josh <cmd>` invocation, and the report drops empty labels rather than printing a bucket.
20
+ //
21
+ // `marker` names the workflow boundary the call is, for the phase breakdown that slices the same
22
+ // spans by stage (joshuafolkken/kit#1269). It is carried here rather than re-derived later because
23
+ // the tool's *input* is what decides it, and a span keeps no input — only the label read off it.
24
+ //
25
+ // `is_bundleable` and `targets` are carried for exactly that reason too (joshuafolkken/kit#1344).
26
+ // Whether a call could have gone out beside another, and what it names, are both read off the input —
27
+ // so a module asking about them after the fact would have nothing to read. `time-bundle-call.ts`
28
+ // decides both; the rule each one follows is stated there.
29
+ //
30
+ // `check_key` is the fourth, and the third time this reason has applied (joshuafolkken/kit#1383). Two
31
+ // runs of one verification check are the same call only if they named the same files, and the files
32
+ // are in the input — so `josh_command` alone cannot say, and nothing downstream could recover it.
33
+ // `time-single-check.ts` decides it.
34
+ //
35
+ // `message_id` is the fifth, and it is the one that says which *turn* a call belongs to
36
+ // (joshuafolkken/kit#1406). It is read off the line the `tool_use` block sat on rather than off the
37
+ // result that closes the span, because a `tool_result` line carries no message id at all — so a span
38
+ // that did not keep it here could never be attributed to the turn that issued it.
39
+ //
40
+ // `writes` is the sixth, and it is what `targets` could not say (joshuafolkken/kit#1472). `targets`
41
+ // names what a call mentioned; `marker` says a call was *an* edit but not of what, and for a tool
42
+ // outside `BUNDLEABLE_TOOLS` it arrived with no target at all. So a consumer subtracting edits from
43
+ // reads — `investigation-reads.ts` — had nothing to subtract for `MultiEdit` / `NotebookEdit`, and
44
+ // nothing at all to tell an in-place `sed -i` from the `sed -n` it shares a label with.
45
+ // `time-writes.ts` decides it, from the input this span is about to discard.
46
+ // `issue` is the seventh, and it is the one `branch` could not carry (joshuafolkken/kit#1617). Under
47
+ // lanes the session writing the transcript stays on the default branch while the work runs in a linked
48
+ // work tree, so `branch` names no issue on any line of the run; the `in-progress` label call does, and
49
+ // like every field above it that answer is read off the *input* a span is about to discard.
50
+ // `time_markers.NO_ISSUE` for every call that declares nothing, which is all but one call per run.
51
+ interface ToolCall {
52
+ label: string
53
+ josh_command: string
54
+ // Every josh subcommand the call ran, where `josh_command` is only the first (joshuafolkken/kit#1883).
55
+ // A chained `pnpm josh lint:related && pnpm josh test:related` carries both; the count tables expand
56
+ // it so the ones `josh_command` drops are still counted.
57
+ josh_commands: ReadonlyArray<string>
58
+ check_key: string
59
+ marker: PhaseMarker
60
+ is_bundleable: boolean
61
+ targets: ReadonlyArray<string>
62
+ // Carried beside `targets` because sharing a target means something different depending on it: a
63
+ // second write to one file is independent work, a read of a file just written is not
64
+ // (joshuafolkken/kit#1509).
65
+ is_writing: boolean
66
+ // The conservative write answer `is_read_only_call` asks of a live call, carried so a reader of the
67
+ // transcript asks the same read-only question as the guard (joshuafolkken/kit#3139).
68
+ may_write: boolean
69
+ // Whether this is a subagent launch whose prompt builds on an earlier launch's finding
70
+ // (joshuafolkken/kit#1854). Carried for the reason every field here is — the prompt it is read from
71
+ // is the input, and a span keeps none. `time-agent-bundles.ts` reads it.
72
+ has_prior_reference: boolean
73
+ writes: ReadonlyArray<string>
74
+ message_id: string
75
+ issue: number
76
+ // The background run this call reads the output of, and `''` for every other call
77
+ // (joshuafolkken/kit#1662). It is the eighth field carried for the reason the seven above are:
78
+ // the id sits in the command string, which a span does not keep, and it is the only thing that
79
+ // says a `tail` two turns later is the join of a command started minutes ago.
80
+ reads_background: string
81
+ }
82
+
83
+ const NO_CALL: ToolCall = {
84
+ label: '',
85
+ josh_command: '',
86
+ josh_commands: [],
87
+ check_key: time_single_check.NO_CHECK,
88
+ marker: time_markers.NO_MARKER,
89
+ message_id: NO_MESSAGE_ID,
90
+ writes: [],
91
+ issue: time_markers.NO_ISSUE,
92
+ reads_background: time_background.NO_BACKGROUND,
93
+ ...time_bundle_call.not_bundleable(),
94
+ }
95
+ const UNKNOWN_CALL: ToolCall = {
96
+ label: UNKNOWN_TOOL,
97
+ josh_command: '',
98
+ josh_commands: [],
99
+ check_key: time_single_check.NO_CHECK,
100
+ marker: time_markers.NO_MARKER,
101
+ message_id: NO_MESSAGE_ID,
102
+ writes: [],
103
+ issue: time_markers.NO_ISSUE,
104
+ reads_background: time_background.NO_BACKGROUND,
105
+ ...time_bundle_call.not_bundleable(),
106
+ }
107
+
108
+ // Everything but Bash: the tool's own name is the label, and nothing it runs is a shell command to
109
+ // read a check key, a josh command or a declared issue off.
110
+ function non_bash_call(name: string, input: unknown, message_id: string): ToolCall {
111
+ return {
112
+ label: name,
113
+ josh_command: '',
114
+ josh_commands: [],
115
+ check_key: time_single_check.NO_CHECK,
116
+ marker: time_markers.tool_marker(name, input),
117
+ message_id,
118
+ writes: time_writes.tool_writes(name, input),
119
+ issue: time_markers.NO_ISSUE,
120
+ // A non-Bash tool joins a backgrounded command too — `BashOutput` carries the shell id in a
121
+ // field of its own, `Monitor` names the same `…/tasks/<id>.output` path in a command — so the
122
+ // same reader is asked here rather than the field being left empty for every tool but `Bash`.
123
+ reads_background: time_background.read_id_of(input),
124
+ ...time_bundle_call.tool_facts(name, input),
125
+ }
126
+ }
127
+
128
+ function bash_call(input: unknown, message_id: string): ToolCall {
129
+ const command = time_shell.bash_command(input)
130
+ const josh_command = time_shell.josh_command_of(command)
131
+
132
+ return {
133
+ label: time_shell.bash_label(command),
134
+ josh_command,
135
+ josh_commands: time_shell.josh_commands_of(command),
136
+ check_key: time_single_check.check_key(josh_command, command),
137
+ marker: time_markers.bash_marker(command),
138
+ message_id,
139
+ writes: time_writes.bash_writes(command),
140
+ issue: time_markers.bash_issue(command),
141
+ reads_background: time_background.read_id_of(input),
142
+ ...time_bundle_call.bash_facts(command),
143
+ }
144
+ }
145
+
146
+ function to_tool_call(name: string, input: unknown, message_id: string): ToolCall {
147
+ if (name !== cost_blocks.BASH_TOOL) return non_bash_call(name, input, message_id)
148
+
149
+ return bash_call(input, message_id)
150
+ }
151
+
152
+ const time_tool_call = {
153
+ UNKNOWN_TOOL,
154
+ NO_CALL,
155
+ UNKNOWN_CALL,
156
+ to_tool_call,
157
+ }
158
+
159
+ export type { ToolCall }
160
+ export { time_tool_call }
@@ -1,13 +1,11 @@
1
1
  import { existsSync, readFileSync } from 'node:fs'
2
2
  import { createRequire } from 'node:module'
3
3
  import path from 'node:path'
4
+ import { ancestor_directories } from '#scripts/lib/ancestor-directories'
4
5
  import { json_value } from '#scripts/lib/json-value'
5
6
  import { package_named_version_schema } from '#scripts/lib/schemas'
6
7
 
7
8
  const PACKAGE_JSON = 'package.json'
8
- // Node module paths never nest anywhere near this deep; the bound only guards against an unexpected
9
- // filesystem cycle so the walk-up cannot loop forever.
10
- const MAX_WALK_DEPTH = 64
11
9
 
12
10
  // Options for `resolve_effective_upstream_version`. `resolve_marker` is a subpath specifier used for
13
11
  // `createRequire().resolve()` when the package root itself is not directly resolvable — e.g. kit does
@@ -31,26 +29,10 @@ function read_matching_version(manifest_path: string, package_name: string): str
31
29
  return parsed.data.version
32
30
  }
33
31
 
34
- // The directory itself and each of its ancestors up to the filesystem root, nearest-first. Bounded
35
- // by MAX_WALK_DEPTH so an unexpected filesystem cycle cannot produce an unbounded list.
36
- function ancestor_directories(start_directory: string): Array<string> {
37
- const directories: Array<string> = []
38
- let current = start_directory
39
-
40
- for (let depth = 0; depth < MAX_WALK_DEPTH; depth += 1) {
41
- directories.push(current)
42
- const parent = path.dirname(current)
43
- if (parent === current) break
44
- current = parent
45
- }
46
-
47
- return directories
48
- }
49
-
50
32
  // Walk up from a resolved file's directory to the package root, returning the version of the first
51
33
  // ancestor package.json whose `name` matches the target — or undefined when no matching root exists.
52
34
  function walk_up_for_version(start_directory: string, package_name: string): string | undefined {
53
- for (const directory of ancestor_directories(start_directory)) {
35
+ for (const directory of ancestor_directories.list(start_directory)) {
54
36
  const version = read_matching_version(path.join(directory, PACKAGE_JSON), package_name)
55
37
  if (version !== undefined) return version
56
38
  }
@@ -11,12 +11,12 @@
11
11
  * major equals the `packageManager` pin keeps both invariants: never below the adopted
12
12
  * major, and advancing while that major is the current one.
13
13
  *
14
- * The `minimum-release-age` quarantine is applied natively from `.npmrc` (kit#768).
15
- * safe-chain filters the registry only when the process tree was launched through one of
16
- * its wrapped shell commands, so `josh latest` and `pnpm josh latest` used to resolve
14
+ * The `minimumReleaseAge` quarantine is applied natively from `pnpm-workspace.yaml` (kit#768,
15
+ * #3267). safe-chain filters the registry only when the process tree was launched through one
16
+ * of its wrapped shell commands, so `josh latest` and `pnpm josh latest` used to resolve
17
17
  * different answers and the pin oscillated. Reading the window from the repo-managed
18
- * `.npmrc` and filtering by publish timestamp makes the resolution identical in every
19
- * invocation context; right after a pnpm release the answer is simply the previous
18
+ * `pnpm-workspace.yaml` and filtering by publish timestamp makes the resolution identical in
19
+ * every invocation context; right after a pnpm release the answer is simply the previous
20
20
  * release, exactly as the filtered view behaved.
21
21
  *
22
22
  * The resolved version is floored at the current `packageManager` pin: a filtered registry
@@ -38,6 +38,7 @@
38
38
  import { readFileSync, writeFileSync } from 'node:fs'
39
39
  import { fileURLToPath } from 'node:url'
40
40
  import { json_value } from '#scripts/lib/json-value'
41
+ import { COMMAND_TIMEOUT_MS } from '#scripts/lib/timeouts'
41
42
  import { execaSync } from 'execa'
42
43
  import semver from 'semver'
43
44
  import { z } from 'zod'
@@ -49,8 +50,7 @@ const PACKAGE_MANAGER_RE = /"packageManager"\s*:\s*"pnpm@(\d+)(?:[^\d]|$)/u
49
50
  const PINNED_VERSION_RE = /"packageManager"\s*:\s*"pnpm@([^"+]+)/u
50
51
  const TARGET_PREFIX = 'pnpm@'
51
52
  const FAILURE_EXIT_CODE = 1
52
- const NPMRC_PATH = '.npmrc'
53
- const VIEW_TIMEOUT_MS = 30_000
53
+ const WORKSPACE_PATH = 'pnpm-workspace.yaml'
54
54
  const SHA512_BYTES = 64
55
55
  const INTEGRITY_RE = /^sha512-([A-Za-z0-9+/]+={0,2})$/u
56
56
  const PACKAGE_MANAGER_VALUE_RE = /("packageManager"\s*:\s*")pnpm@[^"]+(")/u
@@ -121,7 +121,7 @@ function extract_times_json(stdout: string): Record<string, string> | undefined
121
121
  function query_release_times(): Record<string, string> | undefined {
122
122
  const result = execaSync('pnpm', ['view', 'pnpm', 'time', '--json'], {
123
123
  reject: false,
124
- timeout: VIEW_TIMEOUT_MS,
124
+ timeout: COMMAND_TIMEOUT_MS,
125
125
  })
126
126
  if ((result.exitCode ?? FAILURE_EXIT_CODE) !== 0) return undefined
127
127
 
@@ -134,14 +134,14 @@ function query_major_latest_version(major: string): string | undefined {
134
134
  const times = query_release_times()
135
135
  if (times === undefined) return undefined
136
136
 
137
- // The project's own `.npmrc`, deliberately not the upward walk the version check uses: `josh
138
- // latest` reads `package.json` and writes the pnpm pin relative to the working directory, so it
139
- // has no subdirectory case — and honouring a user-level policy here could freeze pnpm bumps in a
140
- // project that declares none (joshuafolkken/kit#808).
137
+ // The project's own `pnpm-workspace.yaml`, deliberately not the upward walk the version check
138
+ // uses: `josh latest` reads `package.json` and writes the pnpm pin relative to the working
139
+ // directory, so it has no subdirectory case — and honouring an ancestor's policy here could freeze
140
+ // pnpm bumps in a project that declares none (joshuafolkken/kit#808).
141
141
  return release_age.select_aged_version(
142
142
  times,
143
143
  major,
144
- release_age.read_minimum_release_age(NPMRC_PATH),
144
+ release_age.read_minimum_release_age(WORKSPACE_PATH),
145
145
  Date.now(),
146
146
  )
147
147
  }
@@ -156,7 +156,7 @@ function parse_latest_version(stdout: string): string | undefined {
156
156
  function query_latest_version(): string | undefined {
157
157
  const result = execaSync('pnpm', ['view', 'pnpm', 'version', '--json'], {
158
158
  reject: false,
159
- timeout: VIEW_TIMEOUT_MS,
159
+ timeout: COMMAND_TIMEOUT_MS,
160
160
  })
161
161
  if ((result.exitCode ?? FAILURE_EXIT_CODE) !== 0) return undefined
162
162
 
@@ -179,8 +179,8 @@ function warn_skip(reason: string): void {
179
179
  console.warn(`⚠ Skipped pnpm bump (${reason}); the chain continues.`)
180
180
  }
181
181
 
182
- function warn_unresolved(major: string | undefined): void {
183
- warn_skip(`no pnpm ${major ?? ''} release resolvable from the registry`)
182
+ function warn_unresolved(major: string | undefined = ''): void {
183
+ warn_skip(`no pnpm ${major} release resolvable from the registry`)
184
184
  }
185
185
 
186
186
  function run_pnpm_update(target: string): number {
@@ -247,7 +247,7 @@ function extract_encoded_integrity(stdout: string): string | undefined {
247
247
  function query_integrity(target: string): string | undefined {
248
248
  const result = execaSync('pnpm', ['view', target, 'dist.integrity', '--json'], {
249
249
  reject: false,
250
- timeout: VIEW_TIMEOUT_MS,
250
+ timeout: COMMAND_TIMEOUT_MS,
251
251
  })
252
252
  if ((result.exitCode ?? FAILURE_EXIT_CODE) !== 0) return undefined
253
253