@joshuafolkken/kit 1.1166.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 (437) 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 +9 -5
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +29 -9
  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 +24 -15
  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 +10 -5
  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 +47 -33
  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-DYAVAM6S.js → chunk-55HHG5PX.js} +1405 -631
  43. package/dist/hooks/{chunk-A6P7PN7P.js → chunk-5VMKCCC4.js} +25 -10
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/{chunk-5KTCMCOT.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-ENA7P5FB.js → chunk-F56MFS4B.js} +3 -3
  49. package/dist/hooks/{chunk-ANJ2WGFD.js → chunk-FIVDHQGT.js} +7 -6
  50. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  51. package/dist/hooks/{chunk-GU5MATVE.js → chunk-JUDKDCDP.js} +499 -389
  52. package/dist/hooks/{chunk-ZDZ555YH.js → chunk-NV2JRZZO.js} +743 -909
  53. package/dist/hooks/{chunk-V2BTOZHT.js → chunk-RNWHN5BP.js} +3 -3
  54. package/dist/hooks/{chunk-URJA6XY4.js → chunk-TQNPDZNH.js} +4 -4
  55. package/dist/hooks/{chunk-GYNGP7HI.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 +232 -210
  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 +81 -59
  64. package/dist/josh.js +872 -75
  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 +31 -9
  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 +7 -6
  111. package/scripts/backlog/backlog-pool.ts +57 -11
  112. package/scripts/backlog/backlog-rank.ts +18 -9
  113. package/scripts/backlog/backlog-ready.ts +1 -1
  114. package/scripts/backlog/backlog-restructure.ts +152 -0
  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/backlog/backlog-waves.ts +15 -17
  119. package/scripts/build/build-codex-hooks.ts +3 -1
  120. package/scripts/build/build-commands.ts +66 -0
  121. package/scripts/build/build-hooks.ts +2 -1
  122. package/scripts/build/build-library.ts +12 -3
  123. package/scripts/build/build.ts +70 -0
  124. package/scripts/build/local-bin.ts +3 -13
  125. package/scripts/cost-runtime/context-cut-threshold.ts +1 -9
  126. package/scripts/cost-runtime/cost-verdict.ts +15 -28
  127. package/scripts/delegation/investigation-reads.ts +3 -1
  128. package/scripts/discovery/repo-origin.ts +5 -5
  129. package/scripts/doctor/doctor-consumer.ts +2 -2
  130. package/scripts/doctor/doctor-io.ts +2 -3
  131. package/scripts/document/capped-print-part.ts +30 -0
  132. package/scripts/document/capped-print.ts +3 -4
  133. package/scripts/document/document-byte-budget.ts +11 -2
  134. package/scripts/document/document-scan.ts +117 -0
  135. package/scripts/document/document-section.ts +143 -16
  136. package/scripts/document/entry-read-budget.ts +5 -3
  137. package/scripts/document/entry-read-set.ts +10 -89
  138. package/scripts/document/read-set-cost.ts +98 -0
  139. package/scripts/epic/epic-add-body.ts +1 -0
  140. package/scripts/epic/epic-audit-checks.ts +1 -1
  141. package/scripts/epic/epic-audit-rationale.ts +4 -3
  142. package/scripts/epic/epic-bundle-cli.ts +14 -3
  143. package/scripts/epic/epic-bundle-poll.ts +41 -0
  144. package/scripts/epic/epic-busy.ts +1 -1
  145. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  146. package/scripts/epic/epic-classify.ts +1 -1
  147. package/scripts/epic/epic-cli-argv.ts +78 -46
  148. package/scripts/epic/epic-cli-remove.ts +11 -19
  149. package/scripts/epic/epic-cli.ts +74 -73
  150. package/scripts/epic/epic-close-comment.ts +1 -1
  151. package/scripts/epic/epic-close.ts +1 -1
  152. package/scripts/epic/epic-cross-repo.ts +5 -5
  153. package/scripts/epic/epic-graph.ts +1 -1
  154. package/scripts/epic/epic-index.ts +1 -1
  155. package/scripts/epic/epic-issue.ts +2 -6
  156. package/scripts/epic/epic-lane-offer.ts +2 -2
  157. package/scripts/epic/epic-next.ts +29 -42
  158. package/scripts/epic/epic-solo-stale.ts +1 -1
  159. package/scripts/epic/epic-triage.ts +8 -4
  160. package/scripts/epic/epic-validate.ts +1 -1
  161. package/scripts/followup/git-followup-finish.ts +2 -2
  162. package/scripts/followup/git-followup-label.ts +2 -2
  163. package/scripts/followup/git-followup-merged.ts +2 -2
  164. package/scripts/followup/git-followup-stages.ts +9 -0
  165. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  166. package/scripts/followup/git-pr-followup.ts +5 -130
  167. package/scripts/gate/core-budget.ts +69 -1
  168. package/scripts/gate/gate-plan.ts +47 -7
  169. package/scripts/gate/gate-skip.ts +38 -7
  170. package/scripts/gate/hook-gate-reuse.ts +12 -10
  171. package/scripts/gate/pre-commit-type-check.ts +4 -1
  172. package/scripts/gate/project-checks.ts +6 -9
  173. package/scripts/gate/type-check-step.ts +2 -3
  174. package/scripts/gate/verification-gate.ts +27 -8
  175. package/scripts/gh/git-gh-api-path.ts +2 -2
  176. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  177. package/scripts/gh/git-gh-issue-write.ts +53 -7
  178. package/scripts/gh/git-gh-issue.ts +5 -3
  179. package/scripts/gh/git-gh-pr-read.ts +68 -19
  180. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  181. package/scripts/gh/git-gh-pr.ts +5 -1
  182. package/scripts/gh/git-pr-ai-review.ts +1 -1
  183. package/scripts/gh/git-pr-checks-eval.ts +9 -3
  184. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  185. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  186. package/scripts/gh/git-pr-checks.ts +18 -6
  187. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  188. package/scripts/gh/git-pr-error.ts +1 -3
  189. package/scripts/gh/git-pr.ts +9 -13
  190. package/scripts/gh/github-issue-url.ts +2 -2
  191. package/scripts/git/constants.ts +12 -6
  192. package/scripts/git/git-command.ts +20 -218
  193. package/scripts/git/git-diff-reads.ts +225 -0
  194. package/scripts/git/git-error.ts +4 -12
  195. package/scripts/git/git-pre-push-hook.ts +139 -0
  196. package/scripts/git/git-prompt.ts +2 -6
  197. package/scripts/git/{schemas.ts → git-schemas.ts} +2 -0
  198. package/scripts/git/git-spawn-sync.ts +3 -1
  199. package/scripts/git/git-spawn.ts +1 -1
  200. package/scripts/git/git-staging.ts +4 -6
  201. package/scripts/git/git-status.ts +3 -9
  202. package/scripts/git/main-merge.ts +57 -21
  203. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  204. package/scripts/hooks/edited-cspell.ts +2 -4
  205. package/scripts/hooks/format-edited-file.ts +3 -12
  206. package/scripts/hooks/pre-push-unit.ts +31 -9
  207. package/scripts/hooks/reserved-run.ts +2 -2
  208. package/scripts/hooks/run-hook.sh +5 -4
  209. package/scripts/hooks/stop-guard.ts +5 -3
  210. package/scripts/init/init-actions.ts +0 -5
  211. package/scripts/init/init-ai-copy.ts +13 -2
  212. package/scripts/init/init-logic-json-merge.ts +19 -0
  213. package/scripts/init/init-logic-secretlint.ts +7 -7
  214. package/scripts/init/init-logic-workspace.ts +41 -2
  215. package/scripts/init/init-logic.ts +16 -26
  216. package/scripts/init/init.ts +5 -15
  217. package/scripts/init/initial-commit.ts +4 -4
  218. package/scripts/init/kit-development-versions.ts +40 -0
  219. package/scripts/init/kit-setup-state.ts +2 -2
  220. package/scripts/init/start-exec.ts +68 -19
  221. package/scripts/init/start-setup-pr.ts +9 -9
  222. package/scripts/init/start-steps.ts +6 -10
  223. package/scripts/issue/defect-rate-cli.ts +1 -1
  224. package/scripts/issue/git-next-issues.ts +1 -1
  225. package/scripts/issue/issue-auto-ok.ts +1 -1
  226. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  227. package/scripts/issue/issue-backlinks.ts +9 -9
  228. package/scripts/issue/issue-comment-cli.ts +19 -13
  229. package/scripts/issue/issue-file-cli.ts +45 -18
  230. package/scripts/issue/issue-file.ts +24 -1
  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 +11 -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 +11 -1
  238. package/scripts/josh/josh-commands-document.ts +1 -0
  239. package/scripts/josh/josh-commands-lane.ts +22 -0
  240. package/scripts/josh/josh-commands-maintenance.ts +4 -4
  241. package/scripts/josh/josh-commands-run.ts +12 -7
  242. package/scripts/josh/josh-in-process.ts +28 -9
  243. package/scripts/josh/josh-logic.ts +33 -15
  244. package/scripts/josh/process-identity.ts +1 -3
  245. package/scripts/josh/session-language-cli.ts +3 -4
  246. package/scripts/lane/lane-await.ts +5 -1
  247. package/scripts/lane/lane-child-invocation.ts +2 -2
  248. package/scripts/lane/lane-cli.ts +3 -3
  249. package/scripts/lane/lane-dispatch.ts +14 -10
  250. package/scripts/lane/lane-environment.ts +5 -1
  251. package/scripts/lane/lane-handoff.ts +1 -1
  252. package/scripts/lane/lane-install.ts +2 -13
  253. package/scripts/lane/lane-launch-cli.ts +35 -3
  254. package/scripts/lane/lane-ledger.ts +145 -0
  255. package/scripts/lane/lane-leftover.ts +1 -1
  256. package/scripts/lane/lane-load.ts +108 -0
  257. package/scripts/lane/lane-measure-cli.ts +149 -0
  258. package/scripts/lane/lane-occupancy.ts +3 -0
  259. package/scripts/lane/lane-reap.ts +1 -3
  260. package/scripts/lane/lane-relaunch.ts +1 -1
  261. package/scripts/lane/lane-stats.ts +224 -0
  262. package/scripts/lane/lane-vacant.ts +36 -0
  263. package/scripts/lane/openai-lane-ship.ts +2 -2
  264. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  265. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  266. package/scripts/lane/openai-review-broker.ts +2 -4
  267. package/scripts/lib/ancestor-directories.ts +35 -0
  268. package/scripts/lib/animation-helpers.ts +2 -2
  269. package/scripts/lib/buffered-process.ts +3 -7
  270. package/scripts/lib/cli-flags.ts +71 -1
  271. package/scripts/lib/spawn-exit.ts +2 -2
  272. package/scripts/lib/timeouts.ts +35 -2
  273. package/scripts/lines/line-budget.ts +4 -6
  274. package/scripts/notify/telegram-notify.ts +26 -1
  275. package/scripts/notify/telegram-test.ts +6 -0
  276. package/scripts/observations/observation-ledger-line.ts +6 -6
  277. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  278. package/scripts/observations/observation-ledger.ts +26 -7
  279. package/scripts/overrides/lockfile-overrides.ts +1 -1
  280. package/scripts/overrides/overrides-logic.ts +1 -1
  281. package/scripts/propagate/propagate-git.ts +1 -1
  282. package/scripts/propagate/propagate-publish.ts +2 -2
  283. package/scripts/propagate/propagate-steps.ts +20 -15
  284. package/scripts/propagate/propagate.ts +36 -27
  285. package/scripts/refactor/import-graph.ts +18 -1
  286. package/scripts/refactor/refactor-lint.ts +4 -4
  287. package/scripts/repo/repository-labels.ts +2 -2
  288. package/scripts/repo/required-checks-logic.ts +1 -3
  289. package/scripts/review/live-evidence.ts +2 -4
  290. package/scripts/review/review-brief-cli.ts +1 -1
  291. package/scripts/review/review-finding-ledger.ts +1 -1
  292. package/scripts/review/review-findings-cli.ts +1 -3
  293. package/scripts/rules/decision-oracle-batch.ts +128 -0
  294. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  295. package/scripts/rules/decision-oracle.ts +8 -120
  296. package/scripts/rules/delivered-rules.ts +26 -182
  297. package/scripts/rules/direct-filing.ts +1 -1
  298. package/scripts/rules/early-heartbeat.ts +3 -3
  299. package/scripts/rules/git-argv.ts +1 -1
  300. package/scripts/rules/git-force.ts +1 -1
  301. package/scripts/rules/implementation-cut.ts +2 -2
  302. package/scripts/rules/index-guard.ts +1 -1
  303. package/scripts/rules/issue-comments.ts +118 -0
  304. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  305. package/scripts/rules/lane-background.ts +2 -2
  306. package/scripts/rules/lane-park.ts +18 -0
  307. package/scripts/rules/lane-split-park.ts +128 -0
  308. package/scripts/rules/oracle-firing.ts +1 -1
  309. package/scripts/rules/poll-loop.ts +1 -1
  310. package/scripts/rules/pre-gate-cut.ts +3 -3
  311. package/scripts/rules/protected-files.ts +59 -4
  312. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  313. package/scripts/rules/shell-body-trigger.ts +1 -1
  314. package/scripts/rules/stop-rules.ts +18 -7
  315. package/scripts/rules/test-declared-commit.ts +4 -4
  316. package/scripts/rules/wip-cap.ts +63 -0
  317. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  318. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  319. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  320. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +12 -6
  321. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  322. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +3 -3
  323. package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +2 -2
  324. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  325. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  326. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  327. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  328. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  329. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +16 -2
  330. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  331. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  332. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  333. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  334. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  335. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  336. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  337. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  338. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  339. package/scripts/run/run-ending.ts +2 -2
  340. package/scripts/run/run-halfrun-resume.ts +6 -1
  341. package/scripts/run/run-headless.ts +1 -1
  342. package/scripts/run/run-invocation.ts +85 -84
  343. package/scripts/run/run-liveness.ts +1 -1
  344. package/scripts/run/run-parent-cut-hook.ts +1 -1
  345. package/scripts/run/run-preflight.ts +6 -6
  346. package/scripts/run/run-prep-cli.ts +1 -1
  347. package/scripts/run/run-prrun-resume.ts +1 -1
  348. package/scripts/run/run-report-cli.ts +4 -4
  349. package/scripts/run/run-report.ts +2 -2
  350. package/scripts/run/run-status-cli.ts +1 -1
  351. package/scripts/run/run-step-cli.ts +3 -3
  352. package/scripts/run/run-step.ts +2 -2
  353. package/scripts/run/run-stop-notify.ts +1 -1
  354. package/scripts/run/run-stranded-detect.ts +4 -4
  355. package/scripts/run/run-stranded.ts +2 -2
  356. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  357. package/scripts/run/run-watcher-guard.ts +5 -3
  358. package/scripts/run/run-watcher-hook.ts +1 -1
  359. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +29 -8
  360. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  361. package/scripts/run/ship/run-ship-lock.ts +50 -0
  362. package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +13 -0
  363. package/scripts/run/ship/run-ship-pre-detach.ts +66 -0
  364. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  365. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  366. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +10 -6
  367. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  368. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  369. package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +13 -0
  370. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +15 -8
  371. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  372. package/scripts/run/ship/run-ship-sync.ts +128 -0
  373. package/scripts/run/{run-ship.ts → ship/run-ship.ts} +6 -0
  374. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  375. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +31 -9
  376. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +3 -3
  377. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +99 -17
  378. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  379. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +8 -2
  380. package/scripts/run/wake/run-wake-restart.ts +58 -0
  381. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  382. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +1 -1
  383. package/scripts/safe-chain/preinstall-command.ts +20 -21
  384. package/scripts/safe-chain/project-config.ts +19 -15
  385. package/scripts/sync/sync-ai-files.ts +1 -1
  386. package/scripts/sync/sync-configs.ts +2 -1
  387. package/scripts/sync/sync.ts +15 -2
  388. package/scripts/test/test-red-logic.ts +17 -2
  389. package/scripts/test/test-red.ts +8 -7
  390. package/scripts/test/unit-worker-share.ts +1 -0
  391. package/scripts/time-runtime/time-background.ts +5 -1
  392. package/scripts/time-runtime/time-instant.ts +3 -1
  393. package/scripts/time-runtime/time-spans.ts +6 -139
  394. package/scripts/time-runtime/time-tool-call.ts +160 -0
  395. package/scripts/time-runtime/time-transcript-line.ts +27 -1
  396. package/scripts/version/effective-upstream.ts +2 -20
  397. package/scripts/version/latest-corepack.ts +17 -17
  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 +2 -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/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  419. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  420. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  421. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  422. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  423. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  424. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  425. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  426. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  427. /package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +0 -0
  428. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  429. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  430. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  431. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  432. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  433. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  434. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  435. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  436. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  437. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { issue_bug_label } from '#scripts/issue/issue-bug-label'
2
+ import { observation_ledger } from '#scripts/observations/observation-ledger'
2
3
  import { z } from 'zod'
3
4
  import { test_declared_logic } from './test-declared-logic'
4
5
 
@@ -11,7 +12,7 @@ import { test_declared_logic } from './test-declared-logic'
11
12
  // that test catches anything — the added and changed tests run against the merge-base, and at least one
12
13
  // of them has to fail there.
13
14
 
14
- type RedVerdict = 'red' | 'green' | 'no-test'
15
+ type RedVerdict = 'red' | 'green' | 'no-test' | 'test-only'
15
16
 
16
17
  // The declaration a bug-fix Issue carries under its background heading, in the same slot as the
17
18
  // behavior-change declaration (`behavior-change-lint.ts`). Read from the declaration, never inferred,
@@ -33,6 +34,18 @@ function unit_test_files(paths: ReadonlyArray<string>): Array<string> {
33
34
  .filter((path) => test_declared_logic.is_test_file(path) && path.endsWith(UNIT_TEST_SUFFIX))
34
35
  }
35
36
 
37
+ // **A change whose every path is a test file has no pre-fix code to run against**
38
+ // (joshuafolkken/kit#3214). The merge-base then runs the same code as HEAD, so its result equals HEAD's
39
+ // and says nothing about the fix. Exempt paths do not count: a document-rule test reads `*.md` and
40
+ // `prompts/` files, so a change to one of those is pre-fix content the test can be red against.
41
+ // Observation ledger paths do not count either way (joshuafolkken/kit#3230): a run commits its own
42
+ // ledger entry, and no test reads it as pre-fix content.
43
+ function is_test_only(paths: ReadonlyArray<string>): boolean {
44
+ const counted = paths.filter((path) => !observation_ledger.is_ledger_path(path))
45
+
46
+ return counted.length > 0 && counted.every((path) => test_declared_logic.is_test_file(path))
47
+ }
48
+
36
49
  // Vitest's JSON report carries one `testResults` entry per file it loaded — a file that failed to load
37
50
  // included — and none for a file its config excluded, so this is how many files actually ran. The
38
51
  // changed-path count cannot stand in for it: a file the merge-base config does not include runs nothing
@@ -58,7 +71,8 @@ function verdict_for(test_count: number, is_failed: boolean): RedVerdict {
58
71
  }
59
72
 
60
73
  // Only `green` on a declared bug fix is refused. `no-test` is already `josh test:declared`'s refusal
61
- // when a runtime file changed, and a declared fix that touched only docs has nothing to reproduce.
74
+ // when a runtime file changed, a declared fix that touched only docs has nothing to reproduce, and a
75
+ // `test-only` fix runs the same code on the merge-base as on HEAD.
62
76
  function is_refused(body: string, verdict: RedVerdict): boolean {
63
77
  return verdict === 'green' && is_bug_fix(body)
64
78
  }
@@ -67,6 +81,7 @@ const test_red_logic = {
67
81
  BUG_DECLARATION_LINE,
68
82
  is_bug_fix,
69
83
  is_refused,
84
+ is_test_only,
70
85
  ran_file_count,
71
86
  unit_test_files,
72
87
  verdict_for,
@@ -11,7 +11,7 @@ import { execa } from 'execa'
11
11
  import { test_red_logic, type RedVerdict } from './test-red-logic'
12
12
 
13
13
  // `josh test:red` — run the added and changed Vitest files against the pre-fix tree and print `red`,
14
- // `green` or `no-test` (joshuafolkken/kit#2448).
14
+ // `green`, `no-test` or `test-only` (joshuafolkken/kit#2448).
15
15
  //
16
16
  // **The pre-fix tree is a detached worktree at the merge-base, never the caller's checkout.** The tests
17
17
  // are copied in from the working tree, so what runs is the new test against the old code, and the
@@ -26,7 +26,7 @@ const REPORT_NAME = 'report.json'
26
26
  const NODE_MODULES = 'node_modules'
27
27
  const VITEST_BIN = path.join(NODE_MODULES, '.bin', 'vitest')
28
28
  const USAGE =
29
- 'Usage: josh test:red — prints red / green / no-test for the changed tests on the merge-base'
29
+ 'Usage: josh test:red — prints red / green / no-test / test-only for the changed tests on the merge-base'
30
30
 
31
31
  interface RedRun {
32
32
  verdict: RedVerdict
@@ -34,9 +34,7 @@ interface RedRun {
34
34
  }
35
35
 
36
36
  // The changed Vitest files that still exist — a deleted test has nothing to run.
37
- async function changed_tests(): Promise<Array<string>> {
38
- const paths = await changed_paths.read_changed_paths(false)
39
-
37
+ function changed_tests(paths: ReadonlyArray<string>): Array<string> {
40
38
  return test_red_logic.unit_test_files(paths).filter((file) => existsSync(file))
41
39
  }
42
40
 
@@ -126,12 +124,15 @@ async function verdict_on(base: string, files: ReadonlyArray<string>): Promise<R
126
124
  }
127
125
 
128
126
  // The verdict over the current checkout. No merge-base (not a repository, no default branch) reads as
129
- // `no-test` — there is no pre-fix tree to run against, and a guard fails open.
127
+ // `no-test` — there is no pre-fix tree to run against, and a guard fails open. A change whose every path
128
+ // is a test file is `test-only` without a run: the merge-base runs the same code as HEAD.
130
129
  async function run(): Promise<RedRun> {
131
130
  const base = await change_base.resolved()
132
- const files = await changed_tests()
131
+ const paths = await changed_paths.read_changed_paths(false)
132
+ const files = changed_tests(paths)
133
133
 
134
134
  if (base === undefined || files.length === 0) return { verdict: 'no-test', files }
135
+ if (test_red_logic.is_test_only(paths)) return { verdict: 'test-only', files }
135
136
 
136
137
  return { verdict: await verdict_on(base, files), files }
137
138
  }
@@ -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 }
@@ -79,6 +79,13 @@ const ERROR_TEXT_LIMIT = 256
79
79
  // shared constant exists because each guard writes the literal in its own reason, so the one place a
80
80
  // refusal is *detected* names it here.
81
81
  const REFUSAL_MARKER = '⛔'
82
+ // The label the harness puts in front of a PreToolUse hook's deny reason, naming the hook and the tool.
83
+ const HOOK_ERROR_LABEL = /^PreToolUse:\S+ hook error: /u
84
+ // A line that is one bare verdict token — `cut`, `busy`, `not-a-lane` — the single word a josh verdict
85
+ // command prints on standard output (joshuafolkken/kit#3223). How many are kept bounds the field the
86
+ // way `ERROR_TEXT_LIMIT` bounds `error_text`.
87
+ const TOKEN_LINE = /^[a-z][a-z-]*$/u
88
+ const TOKEN_LINE_LIMIT = 8
82
89
 
83
90
  interface Block {
84
91
  type: string
@@ -111,6 +118,11 @@ interface Block {
111
118
  // the parsed block rather than by re-scanning the body downstream. `''` where the result was not a
112
119
  // `⛔` refusal, so a reader tells "no guard spoke" from "this guard spoke" without a second parse.
113
120
  refusal_guard: string
121
+ // The bare-token lines of a result that did not fail, and `[]` for every other block
122
+ // (joshuafolkken/kit#3223). It obeys the same prohibition as `error_text` — what is kept is a few
123
+ // single words, never the body — and it exists so the verdict a josh command answered with
124
+ // (`run:cut`'s `cut`) is read from the parsed block rather than by searching the raw line.
125
+ token_lines: ReadonlyArray<string>
114
126
  // The id the harness assigned to a command it took into the background, and `''` for every other
115
127
  // block (joshuafolkken/kit#1662). It is the fourth field read off the body under the same
116
128
  // prohibition as the three above — what is kept is the id, never the text it was read from — and
@@ -159,8 +171,12 @@ function block_names(
159
171
  // before the first `:` — `⛔ pre-gate cut: …` is `pre-gate cut`. A reason with no colon (a headline
160
172
  // like `⛔ scoped checks not green on this tree`) is taken whole. Reading the opening rather than
161
173
  // searching the body is what stops a result that merely quoted a reason being read as a refusal.
174
+ //
175
+ // **The harness writes the reason behind its own label** — `PreToolUse:Edit hook error: ⛔ …` — so that
176
+ // label is stripped before the opening is read (joshuafolkken/kit#3223). Without it no live refusal
177
+ // carried a guard: the fixtures wrote the bare `⛔` body and passed while every recorded one read `''`.
162
178
  function guard_from_refusal(text: string): string {
163
- const trimmed = text.trimStart()
179
+ const trimmed = text.trimStart().replace(HOOK_ERROR_LABEL, '')
164
180
  if (!trimmed.startsWith(REFUSAL_MARKER)) return ''
165
181
 
166
182
  const headline = trimmed.slice(REFUSAL_MARKER.length).split('\n', 1)[0]?.trimStart() ?? ''
@@ -169,6 +185,14 @@ function guard_from_refusal(text: string): string {
169
185
  return (colon === -1 ? headline : headline.slice(0, colon)).trim()
170
186
  }
171
187
 
188
+ function token_lines_of(text: string): Array<string> {
189
+ return text
190
+ .split('\n')
191
+ .map((line) => line.trim())
192
+ .filter((line) => TOKEN_LINE.test(line))
193
+ .slice(0, TOKEN_LINE_LIMIT)
194
+ }
195
+
172
196
  // **The body is flattened once and both readers are handed the text.** `result_text` is what turns a
173
197
  // content field that may be a string or a list of blocks into one string, and it is idempotent on a
174
198
  // string — so passing its own output back to `has_failure_line` reads exactly as passing the raw
@@ -185,6 +209,7 @@ function to_block(raw: z.infer<typeof BLOCK_SCHEMA>): Block {
185
209
  followup_stages: time_followup_stage.read_stages(text),
186
210
  error_text: is_error === true ? text.slice(0, ERROR_TEXT_LIMIT) : '',
187
211
  refusal_guard: is_error === true ? guard_from_refusal(text) : '',
212
+ token_lines: is_error === true ? [] : token_lines_of(text),
188
213
  background_id: time_background.launch_id(text),
189
214
  agent_id: time_background.agent_launch_id(text),
190
215
  }
@@ -248,6 +273,7 @@ function parse_line(line: string): TranscriptLine | undefined {
248
273
  const time_transcript_line = {
249
274
  ERROR_TEXT_LIMIT,
250
275
  NO_MESSAGE_ID,
276
+ TOKEN_LINE_LIMIT,
251
277
  guard_from_refusal,
252
278
  parse_line,
253
279
  }
@@ -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
  }