@joshuafolkken/kit 1.1035.0 → 1.1094.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 (605) hide show
  1. package/.claude/settings.json +9 -8
  2. package/.claude/skills/epic-commands/SKILL.md +5 -7
  3. package/.claude/skills/verify-ui/SKILL.md +1 -1
  4. package/.claude/skills/workflow-commands/SKILL.md +21 -13
  5. package/.claude/skills/workflow-commands/background-commands.md +5 -4
  6. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -3
  7. package/.claude/skills/workflow-commands/backlogrun-lanes.md +9 -7
  8. package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
  9. package/.claude/skills/workflow-commands/backlogrun-progress.md +31 -37
  10. package/.claude/skills/workflow-commands/backlogrun-steps.md +25 -25
  11. package/.claude/skills/workflow-commands/backlogrun.md +2 -2
  12. package/.claude/skills/workflow-commands/chain-rule.md +6 -4
  13. package/.claude/skills/workflow-commands/delegation.md +3 -3
  14. package/.claude/skills/workflow-commands/followup-reference.md +3 -3
  15. package/.claude/skills/workflow-commands/followup.md +7 -3
  16. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -2
  17. package/.claude/skills/workflow-commands/fullrun.md +3 -2
  18. package/.claude/skills/workflow-commands/halfrun.md +16 -19
  19. package/.claude/skills/workflow-commands/issue-comments.md +2 -2
  20. package/.claude/skills/workflow-commands/issue-fold-existing.md +10 -10
  21. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +9 -4
  23. package/.claude/skills/workflow-commands/latest-gate.md +1 -1
  24. package/.claude/skills/workflow-commands/needs-human-review.md +1 -1
  25. package/.claude/skills/workflow-commands/observation-filing.md +44 -89
  26. package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
  27. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  28. package/.claude/skills/workflow-commands/prrun.md +36 -0
  29. package/.claude/skills/workflow-commands/retrospective.md +1 -1
  30. package/.claude/skills/workflow-commands/working-tree-hold.md +24 -4
  31. package/.codex/hooks.json +16 -20
  32. package/.github/actions/setup-pnpm/action.yml +93 -0
  33. package/.github/dependabot.yml +5 -1
  34. package/.github/workflows/auto-tag.yml +21 -0
  35. package/.github/workflows/ci.yml +26 -128
  36. package/.github/workflows/pr-classification.yml +5 -41
  37. package/.github/workflows/publish.yml +42 -48
  38. package/.github/workflows/sonar-qube.yml +37 -4
  39. package/AGENTS.md +12 -2
  40. package/CLAUDE.md +15 -16
  41. package/GEMINI.md +14 -2
  42. package/README.md +59 -18
  43. package/SECURITY.md +36 -6
  44. package/cspell/index.yaml +2 -1
  45. package/dist/CLAUDE.md +15 -16
  46. package/dist/config-merge/index.js +15 -3
  47. package/dist/hooks/batch-guard.js +4 -4
  48. package/dist/hooks/{chunk-Y27M6A7X.js → chunk-3C4YCCEF.js} +5 -13
  49. package/dist/hooks/{chunk-D67KVGGY.js → chunk-7ZPFAVR7.js} +17 -88
  50. package/dist/hooks/{chunk-HOYJU5RM.js → chunk-E54KGFEQ.js} +11 -4
  51. package/dist/hooks/{chunk-KG6PZD72.js → chunk-IL27YTPH.js} +2 -2
  52. package/dist/hooks/{chunk-GJT4BNVE.js → chunk-KHWHKJOM.js} +3 -3
  53. package/dist/hooks/{chunk-HU66PWWL.js → chunk-M2XDCUKD.js} +1076 -509
  54. package/dist/hooks/{chunk-GLBDBJOK.js → chunk-NKOG3XAV.js} +199 -304
  55. package/dist/hooks/{chunk-ZXNE6NY3.js → chunk-O4LL4HTI.js} +2 -7
  56. package/dist/hooks/{chunk-XPLP27WA.js → chunk-OIZUHPJI.js} +5 -27
  57. package/dist/hooks/{chunk-EIFCAJRB.js → chunk-UD24EAIL.js} +2546 -1636
  58. package/dist/hooks/codex-hook-adapter.js +10 -11
  59. package/dist/hooks/duplicate-read-guard.js +5 -5
  60. package/dist/hooks/format-edited.js +15 -34
  61. package/dist/hooks/inputs.json +279 -0
  62. package/dist/hooks/investigation-guard.js +5 -5
  63. package/dist/hooks/pretool-guard.js +23 -16
  64. package/dist/hooks/session-lang.js +0 -1
  65. package/dist/hooks/stop-guard.js +188 -509
  66. package/dist/josh.js +166 -225
  67. package/dist/managed-marker/index.js +2 -1
  68. package/dist/version/index.js +412 -64
  69. package/eslint/rules/promise.js +5 -0
  70. package/package.json +13 -8
  71. package/prompts/collaboration-workflow/file-edits.md +6 -53
  72. package/prompts/collaboration-workflow/issue-citation.md +5 -5
  73. package/prompts/collaboration-workflow/operating-rules.md +4 -4
  74. package/prompts/collaboration-workflow/overview.md +13 -1
  75. package/prompts/collaboration-workflow/report-format.md +1 -1
  76. package/prompts/collaboration-workflow/residency.md +4 -4
  77. package/prompts/collaboration-workflow/rule-delivery.md +5 -36
  78. package/prompts/collaboration-workflow/shell-body.md +7 -9
  79. package/prompts/collaboration-workflow/turn-batching.md +21 -37
  80. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  81. package/prompts/collaboration-workflow/wip-cap.md +36 -36
  82. package/prompts/collaboration-workflow.md +5 -8
  83. package/prompts/review-rubric.md +1 -1
  84. package/prompts/review.md +3 -3
  85. package/prompts/sonar-hotspot-handling.md +12 -4
  86. package/prompts/testing-guide.md +3 -3
  87. package/scripts/adopt/adopt-toolkits.ts +2 -2
  88. package/scripts/adopt/adopt.ts +0 -2
  89. package/scripts/agent/agent-argv.ts +0 -2
  90. package/scripts/agent/agent-diagnostics.ts +1 -1
  91. package/scripts/agent/agent-role-profile.ts +0 -7
  92. package/scripts/agent/claude-agent-argv.ts +0 -2
  93. package/scripts/agent/codex-agent-argv.ts +1 -1
  94. package/scripts/agent/codex-hooks.ts +92 -0
  95. package/scripts/agent/interactive-ask.ts +1 -1
  96. package/scripts/auto-ok/auto-ok-cli.ts +15 -25
  97. package/scripts/backlog/backlog-budget.ts +0 -2
  98. package/scripts/backlog/backlog-defect-priority.ts +2 -2
  99. package/scripts/backlog/backlog-drive-cli.ts +10 -8
  100. package/scripts/backlog/backlog-drive-named-offer.ts +16 -3
  101. package/scripts/backlog/backlog-drive-named.ts +2 -1
  102. package/scripts/backlog/backlog-drive.ts +76 -16
  103. package/scripts/backlog/backlog-named.ts +0 -3
  104. package/scripts/backlog/backlog-next.ts +32 -22
  105. package/scripts/backlog/backlog-offer-cli.ts +0 -5
  106. package/scripts/backlog/backlog-plan-cli.ts +6 -8
  107. package/scripts/backlog/backlog-plan.ts +1 -1
  108. package/scripts/backlog/backlog-pool.ts +34 -17
  109. package/scripts/backlog/backlog-rank.ts +106 -0
  110. package/scripts/backlog/backlog-ready.ts +19 -3
  111. package/scripts/backlog/backlog-scope.ts +1 -3
  112. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  113. package/scripts/backlog/backlog-stalled.ts +0 -1
  114. package/scripts/backlog/backlog-waves.ts +47 -31
  115. package/scripts/behavior/behavior-cli.ts +1 -1
  116. package/scripts/build/build-codex-hooks.ts +26 -0
  117. package/scripts/build/build-hooks.ts +92 -11
  118. package/scripts/build/hook-bundle-stamp.ts +111 -0
  119. package/scripts/cases/cases-cli.ts +1 -1
  120. package/scripts/cases/cases-logic.ts +1 -1
  121. package/scripts/ci/composite-actions.ts +30 -0
  122. package/scripts/ci/pr-classification.ts +3 -3
  123. package/scripts/claude/workflow-destination.ts +10 -1
  124. package/scripts/clone/clone-fingerprint.ts +0 -1
  125. package/scripts/clone/clone-scan-cli.ts +1 -1
  126. package/scripts/clone/clone-scan.ts +1 -1
  127. package/scripts/config-merge/json-format.ts +0 -1
  128. package/scripts/config-merge/patch-yaml-list.ts +1 -1
  129. package/scripts/cost-runtime/codex-usage.ts +2 -9
  130. package/scripts/cost-runtime/cost-attribute.ts +0 -1
  131. package/scripts/cost-runtime/cost-cli.ts +0 -3
  132. package/scripts/cost-runtime/cost-corpus.ts +0 -2
  133. package/scripts/cost-runtime/cost-format.ts +0 -3
  134. package/scripts/cost-runtime/cost-pricing.ts +0 -1
  135. package/scripts/cost-runtime/cost-tokens.ts +0 -1
  136. package/scripts/delegation/delegation-cli.ts +1 -1
  137. package/scripts/delegation/delegation-policy.ts +11 -1
  138. package/scripts/delegation/duplicate-read-guard.ts +0 -8
  139. package/scripts/delegation/fanout-cli.ts +1 -1
  140. package/scripts/delegation/fanout.ts +0 -2
  141. package/scripts/delegation/investigation-reads.ts +1 -164
  142. package/scripts/discovery/repo-map-logic.ts +0 -1
  143. package/scripts/discovery/repo-party-cli.ts +1 -1
  144. package/scripts/doctor/doctor-consumer.ts +4 -6
  145. package/scripts/doctor/doctor-logic.ts +0 -4
  146. package/scripts/doctor/doctor.ts +14 -9
  147. package/scripts/document/document-byte-budget.ts +8 -9
  148. package/scripts/document/document-byte-check.ts +0 -1
  149. package/scripts/document/document-read-cli.ts +0 -2
  150. package/scripts/document/document-section-cli.ts +1 -1
  151. package/scripts/document/document-section.ts +0 -3
  152. package/scripts/document/edit-files-cli.ts +0 -1
  153. package/scripts/document/entry-read-budget.ts +13 -4
  154. package/scripts/document/lane-child-read-set.ts +1 -6
  155. package/scripts/document/read-files-cli.ts +0 -4
  156. package/scripts/document/read-set-cli.ts +0 -2
  157. package/scripts/document/read-set-point-of-use.ts +1 -0
  158. package/scripts/document/read-set-pricing.ts +0 -1
  159. package/scripts/document/read-set-trim.ts +1 -1
  160. package/scripts/epic/epic-add.ts +1 -1
  161. package/scripts/epic/epic-audit-cli.ts +1 -4
  162. package/scripts/epic/epic-audit-orphans.ts +1 -3
  163. package/scripts/epic/epic-audit-rationale.ts +0 -1
  164. package/scripts/epic/epic-audit-report.ts +0 -2
  165. package/scripts/epic/epic-audit.ts +5 -4
  166. package/scripts/epic/epic-bundle-cli.ts +1 -1
  167. package/scripts/epic/epic-bundle-evidence.ts +0 -1
  168. package/scripts/epic/epic-bundle-referenced.ts +1 -5
  169. package/scripts/epic/epic-bundle.ts +0 -4
  170. package/scripts/epic/epic-busy.ts +4 -5
  171. package/scripts/epic/epic-candidate-confirm.ts +0 -5
  172. package/scripts/epic/epic-classify.ts +2 -2
  173. package/scripts/epic/epic-cli-argv.ts +0 -1
  174. package/scripts/epic/epic-close-comment.ts +1 -1
  175. package/scripts/epic/epic-close.ts +5 -2
  176. package/scripts/epic/epic-cross-repo.ts +2 -4
  177. package/scripts/epic/epic-decision.ts +0 -1
  178. package/scripts/epic/epic-fetch.ts +2 -2
  179. package/scripts/epic/epic-index.ts +2 -3
  180. package/scripts/epic/epic-issue.ts +1 -15
  181. package/scripts/epic/epic-lane-offer.ts +20 -30
  182. package/scripts/epic/epic-nested.ts +1 -1
  183. package/scripts/epic/epic-next-read.ts +1 -3
  184. package/scripts/epic/epic-next-views.ts +0 -1
  185. package/scripts/epic/epic-next.ts +1 -10
  186. package/scripts/epic/epic-parse.ts +0 -1
  187. package/scripts/epic/epic-promote.ts +0 -9
  188. package/scripts/epic/epic-rank.ts +37 -0
  189. package/scripts/epic/epic-read.ts +1 -2
  190. package/scripts/epic/epic-reconcile.ts +1 -1
  191. package/scripts/epic/epic-reference.ts +1 -13
  192. package/scripts/epic/epic-relation-recheck.ts +1 -1
  193. package/scripts/epic/epic-relations.ts +1 -1
  194. package/scripts/epic/epic-remove.ts +1 -1
  195. package/scripts/epic/epic-report.ts +0 -1
  196. package/scripts/epic/epic-run.ts +2 -3
  197. package/scripts/epic/epic-shape.ts +1 -1
  198. package/scripts/epic/epic-solo-stale.ts +107 -0
  199. package/scripts/epic/epic-solo.ts +32 -30
  200. package/scripts/epic/epic-triage.ts +1 -1
  201. package/scripts/epic/epic-validate.ts +1 -1
  202. package/scripts/epic/epic.ts +1 -1
  203. package/scripts/exports/unused-members-cli.ts +53 -0
  204. package/scripts/{git → followup}/followup-issue-number.ts +1 -6
  205. package/scripts/{git → followup}/git-followup-cleanup.ts +2 -1
  206. package/scripts/{git → followup}/git-followup-finish.ts +1 -3
  207. package/scripts/{git → followup}/git-followup-flush.ts +2 -2
  208. package/scripts/{git → followup}/git-followup-issue-close.ts +2 -2
  209. package/scripts/{git → followup}/git-followup-label.ts +4 -4
  210. package/scripts/followup/git-followup-merged.ts +112 -0
  211. package/scripts/{git → followup}/git-followup-pending.ts +1 -1
  212. package/scripts/{git → followup}/git-followup-workflow.ts +19 -11
  213. package/scripts/{git → followup}/git-pr-followup-wrapup.ts +9 -3
  214. package/scripts/{git → followup}/git-pr-followup.ts +53 -16
  215. package/scripts/gate/core-budget.ts +3 -9
  216. package/scripts/gate/gate-plan.ts +7 -4
  217. package/scripts/gate/gate-report.ts +0 -6
  218. package/scripts/gate/gate-tree.ts +1 -1
  219. package/scripts/gate/pre-commit-type-check.ts +0 -6
  220. package/scripts/gate/project-checks.ts +7 -8
  221. package/scripts/gate/scoped-green.ts +0 -1
  222. package/scripts/gate/type-check-step.ts +1 -1
  223. package/scripts/gate/verification-gate.ts +0 -2
  224. package/scripts/gh/fix-gh-packages.ts +15 -12
  225. package/scripts/gh/gh-spawn.ts +2 -2
  226. package/scripts/{git → gh}/git-ai-review-scan.ts +1 -5
  227. package/scripts/{git → gh}/git-gh-check.ts +2 -1
  228. package/scripts/{git → gh}/git-gh-exec.ts +1 -1
  229. package/scripts/{git → gh}/git-gh-failure.ts +1 -1
  230. package/scripts/{git → gh}/git-gh-helpers.ts +0 -1
  231. package/scripts/{git → gh}/git-gh-issue-list.ts +2 -0
  232. package/scripts/{git → gh}/git-gh-issue-read.ts +1 -1
  233. package/scripts/{git → gh}/git-gh-issue-rest.ts +6 -3
  234. package/scripts/{git → gh}/git-gh-pr-read.ts +24 -0
  235. package/scripts/{git → gh}/git-gh-pr-rest.ts +5 -2
  236. package/scripts/{git → gh}/git-gh-pr-review.ts +2 -2
  237. package/scripts/{git → gh}/git-gh-pr-rollup.ts +1 -3
  238. package/scripts/{git → gh}/git-gh-pr.ts +3 -1
  239. package/scripts/{git → gh}/git-pr-ai-review.ts +2 -5
  240. package/scripts/{git → gh}/git-pr-checks-eval.ts +0 -10
  241. package/scripts/{git → gh}/git-pr-checks-parse.ts +4 -20
  242. package/scripts/{git → gh}/git-pr-checks.ts +2 -1
  243. package/scripts/{git → gh}/git-pr-coderabbit.ts +2 -5
  244. package/scripts/{git → gh}/git-pr-confirmation.ts +1 -3
  245. package/scripts/{git → gh}/git-pr-managed-config.ts +2 -3
  246. package/scripts/{git → gh}/git-pr.ts +3 -3
  247. package/scripts/git/changed-file-scope.ts +0 -1
  248. package/scripts/git/git-branch.ts +3 -6
  249. package/scripts/git/git-command.ts +1 -1
  250. package/scripts/git/git-commit.ts +1 -1
  251. package/scripts/git/git-common-directory.ts +80 -24
  252. package/scripts/git/git-error.ts +66 -9
  253. package/scripts/git/git-execa-error.ts +6 -1
  254. package/scripts/git/git-issue.ts +1 -1
  255. package/scripts/git/git-preflight.ts +24 -6
  256. package/scripts/git/git-prompt.ts +6 -31
  257. package/scripts/git/git-push-transport.ts +12 -69
  258. package/scripts/git/git-push.ts +1 -1
  259. package/scripts/git/git-spawn.ts +79 -3
  260. package/scripts/git/git-ssh-keepalive.ts +74 -0
  261. package/scripts/git/git-staging.ts +1 -0
  262. package/scripts/git/git-status.ts +1 -1
  263. package/scripts/git/git-workflow.ts +1 -1
  264. package/scripts/git/gone-branch.ts +4 -2
  265. package/scripts/git/listing-cutoff.ts +1 -1
  266. package/scripts/git/main-merge.ts +1 -1
  267. package/scripts/git/main-sync.ts +19 -1
  268. package/scripts/git/parse-json-array.ts +0 -3
  269. package/scripts/git/repository-lock.ts +16 -16
  270. package/scripts/git/{git-stash.ts → stash/git-stash.ts} +1 -1
  271. package/scripts/git/{stash-pop-args.ts → stash/stash-pop-args.ts} +1 -1
  272. package/scripts/git/{stash-pop-cli.ts → stash/stash-pop-cli.ts} +0 -1
  273. package/scripts/git/{stash-sweep-lock.ts → stash/stash-sweep-lock.ts} +3 -3
  274. package/scripts/hooks/codex-hook-adapter.ts +0 -1
  275. package/scripts/hooks/edited-cspell.ts +1 -1
  276. package/scripts/hooks/format-edited-cli.ts +15 -0
  277. package/scripts/hooks/format-edited-file.ts +4 -9
  278. package/scripts/hooks/pre-push-unit.ts +0 -4
  279. package/scripts/hooks/pretool-guard-cli.ts +21 -0
  280. package/scripts/hooks/pretool-guard.ts +36 -19
  281. package/scripts/hooks/reserved-run.ts +1 -1
  282. package/scripts/hooks/step-zero-notice.ts +76 -0
  283. package/scripts/hooks/stop-guard.ts +47 -21
  284. package/scripts/init/claude-plugin-config.ts +4 -5
  285. package/scripts/init/hook-command-rewrite.ts +16 -8
  286. package/scripts/init/hook-launch.ts +41 -15
  287. package/scripts/init/init-basic.ts +2 -5
  288. package/scripts/init/init-logic.ts +7 -0
  289. package/scripts/init/init.ts +0 -3
  290. package/scripts/init/project-profile.ts +6 -11
  291. package/scripts/init/start-exec.ts +8 -2
  292. package/scripts/init/start-plan.ts +18 -5
  293. package/scripts/init/start-setup-pr.ts +52 -25
  294. package/scripts/init/start-steps.ts +42 -9
  295. package/scripts/init/start.ts +40 -10
  296. package/scripts/issue/behavior-change-lint.ts +0 -1
  297. package/scripts/issue/defect-rate-cli.ts +3 -3
  298. package/scripts/issue/defect-rate.ts +1 -2
  299. package/scripts/{git → issue}/git-next-issues.ts +37 -8
  300. package/scripts/issue/issue-backlinks-cli.ts +1 -5
  301. package/scripts/issue/issue-cite-cli.ts +2 -6
  302. package/scripts/issue/issue-cite.ts +19 -3
  303. package/scripts/issue/issue-classification.ts +1 -9
  304. package/scripts/issue/issue-closing-pr.ts +4 -4
  305. package/scripts/issue/issue-comment-cli.ts +2 -2
  306. package/scripts/issue/issue-file-cli.ts +5 -5
  307. package/scripts/issue/issue-file.ts +2 -2
  308. package/scripts/issue/issue-fold-cli.ts +1 -1
  309. package/scripts/issue/issue-fold-existing-cli.ts +1 -1
  310. package/scripts/{git → issue}/issue-labels.ts +20 -0
  311. package/scripts/issue/issue-lint-cli.ts +1 -1
  312. package/scripts/issue/issue-merged.ts +2 -2
  313. package/scripts/issue/issue-rank.ts +89 -0
  314. package/scripts/issue/issue-read-cli.ts +8 -47
  315. package/scripts/issue/issue-read.ts +0 -2
  316. package/scripts/issue/issue-report-failures.ts +80 -0
  317. package/scripts/issue/issue-scout-cli.ts +1 -7
  318. package/scripts/issue/issue-scout-closed.ts +2 -2
  319. package/scripts/issue/issue-scout.ts +0 -2
  320. package/scripts/issue/issue-state-cli.ts +5 -46
  321. package/scripts/issue/issue-state.ts +1 -5
  322. package/scripts/issue/markdown-section.ts +1 -1
  323. package/scripts/issue/measure-rerun-cli.ts +1 -1
  324. package/scripts/issue/reproduction-measure.ts +1 -1
  325. package/scripts/josh/cli-body.ts +1 -1
  326. package/scripts/josh/file-map-stamp.ts +0 -3
  327. package/scripts/josh/josh-command-map.ts +2 -102
  328. package/scripts/josh/josh-commands-ai.ts +9 -102
  329. package/scripts/josh/josh-commands-backlog.ts +5 -1
  330. package/scripts/josh/josh-commands-development.ts +10 -3
  331. package/scripts/josh/josh-commands-issue.ts +2 -2
  332. package/scripts/josh/josh-commands-maintenance.ts +7 -0
  333. package/scripts/josh/josh-commands-project.ts +7 -1
  334. package/scripts/josh/josh-commands-run.ts +114 -0
  335. package/scripts/josh/josh-commands-versioning.ts +8 -0
  336. package/scripts/josh/josh-commands-workflow.ts +2 -2
  337. package/scripts/josh/path-decision.ts +0 -3
  338. package/scripts/josh/process-owner.ts +16 -0
  339. package/scripts/josh/session-language-cli.ts +0 -1
  340. package/scripts/lane/lane-await.ts +3 -6
  341. package/scripts/lane/lane-capacity.ts +6 -3
  342. package/scripts/lane/lane-child-invocation.ts +20 -7
  343. package/scripts/lane/lane-child-marker.ts +16 -2
  344. package/scripts/lane/lane-cli.ts +33 -18
  345. package/scripts/lane/lane-close.ts +1 -3
  346. package/scripts/lane/lane-dispatch.ts +3 -4
  347. package/scripts/lane/lane-environment.ts +0 -9
  348. package/scripts/lane/lane-handoff.ts +38 -0
  349. package/scripts/lane/lane-launch-cli.ts +21 -11
  350. package/scripts/lane/lane-open.ts +0 -3
  351. package/scripts/lane/lane-output.ts +0 -1
  352. package/scripts/lane/lane-registry.ts +9 -0
  353. package/scripts/lane/lane-report.ts +10 -4
  354. package/scripts/lane/openai-lane-ship.ts +2 -0
  355. package/scripts/lane/openai-lane-supervisor-decision.ts +2 -1
  356. package/scripts/lane/openai-lane-supervisor.ts +6 -4
  357. package/scripts/lane/openai-review-broker.ts +3 -0
  358. package/scripts/lib/bounded-pool.ts +1 -0
  359. package/scripts/lib/json-value.ts +5 -4
  360. package/scripts/lib/poll.ts +3 -1
  361. package/scripts/lib/timed-fetch.ts +15 -0
  362. package/scripts/lib/timeouts.ts +13 -0
  363. package/scripts/{yaml → lib}/yaml-document.ts +3 -1
  364. package/scripts/{bytes → lines}/bytes-command.ts +0 -2
  365. package/scripts/lines/lines-command.ts +0 -1
  366. package/scripts/lint/lint-related-scope.ts +0 -1
  367. package/scripts/lint/lint-related.ts +1 -1
  368. package/scripts/{git → notify}/telegram-notify.ts +2 -2
  369. package/scripts/{git → notify}/telegram-test.ts +4 -4
  370. package/scripts/observations/observation-ledger-home.ts +0 -1
  371. package/scripts/observations/observation-ledger-line.ts +0 -1
  372. package/scripts/observations/observation-ledger-migrate.ts +1 -1
  373. package/scripts/observations/observations-flush-landing.ts +3 -3
  374. package/scripts/observations/observations-flush.ts +2 -2
  375. package/scripts/overrides/lockfile-overrides.ts +1 -1
  376. package/scripts/overrides/overrides-logic.ts +1 -1
  377. package/scripts/package/package-scout-cli.ts +2 -7
  378. package/scripts/package/package-scout.ts +0 -2
  379. package/scripts/propagate/propagate-git-failure.ts +0 -1
  380. package/scripts/propagate/propagate-git.ts +0 -2
  381. package/scripts/propagate/propagate-plan.ts +185 -0
  382. package/scripts/propagate/propagate-publish.ts +2 -3
  383. package/scripts/propagate/propagate-run.ts +5 -3
  384. package/scripts/propagate/propagate-steps.ts +19 -6
  385. package/scripts/propagate/propagate-targets.ts +13 -6
  386. package/scripts/propagate/propagate.ts +60 -46
  387. package/scripts/refactor/import-graph.ts +0 -1
  388. package/scripts/refactor/refactor-lint.ts +0 -2
  389. package/scripts/refactor/refactor-scan.ts +0 -1
  390. package/scripts/refactor/refactor-targets.ts +0 -1
  391. package/scripts/registry-migration/migrate-logic.ts +1 -2
  392. package/scripts/registry-migration/migrate.ts +19 -9
  393. package/scripts/release/github-release-cli.ts +8 -6
  394. package/scripts/release/github-release-environment.ts +46 -0
  395. package/scripts/release/github-release.ts +151 -94
  396. package/scripts/release/release-history.ts +0 -1
  397. package/scripts/release/release-plan.ts +0 -1
  398. package/scripts/release/release-publish.ts +2 -3
  399. package/scripts/release/release-scope-cli.ts +1 -2
  400. package/scripts/release/release-tag.ts +2 -4
  401. package/scripts/repo/auto-merge-setting-logic.ts +1 -4
  402. package/scripts/repo/repo-setting.ts +1 -1
  403. package/scripts/repo/repository-labels.ts +3 -3
  404. package/scripts/repo/required-checks-logic.ts +206 -0
  405. package/scripts/repo/required-checks-report.ts +71 -0
  406. package/scripts/repo/ruleset-check-cli.ts +53 -0
  407. package/scripts/repo/ruleset-check.ts +147 -0
  408. package/scripts/report/report-lint-cli.ts +0 -4
  409. package/scripts/review/disposition-cli.ts +1 -1
  410. package/scripts/review/live-evidence.ts +1 -1
  411. package/scripts/review/review-attest-cli.ts +0 -6
  412. package/scripts/review/review-attest.ts +48 -16
  413. package/scripts/review/review-brief-cli.ts +72 -8
  414. package/scripts/review/review-brief.ts +67 -9
  415. package/scripts/review/review-diff-parts.ts +89 -0
  416. package/scripts/review/review-diff-write.ts +97 -0
  417. package/scripts/review/review-finding-ledger.ts +0 -1
  418. package/scripts/review/review-record-cli.ts +1 -1
  419. package/scripts/review/review-record.ts +0 -1
  420. package/scripts/review/review-round2-cli.ts +1 -1
  421. package/scripts/review/review-stamps.ts +6 -2
  422. package/scripts/review/review-tree.ts +14 -2
  423. package/scripts/rules/decision-oracle-stage.ts +18 -0
  424. package/scripts/rules/decision-oracle.ts +10 -7
  425. package/scripts/rules/delivered-rules.ts +9 -1
  426. package/scripts/rules/destructive-command.ts +119 -0
  427. package/scripts/rules/direct-filing.ts +1 -1
  428. package/scripts/rules/file-body.ts +0 -3
  429. package/scripts/rules/filing-cap.ts +0 -2
  430. package/scripts/rules/gh-api.ts +7 -2
  431. package/scripts/rules/git-argv.ts +18 -7
  432. package/scripts/rules/git-force.ts +0 -2
  433. package/scripts/rules/implementation-cut.ts +1 -2
  434. package/scripts/rules/index-guard.ts +71 -0
  435. package/scripts/rules/issue-citation.ts +50 -25
  436. package/scripts/rules/issue-fold.ts +1 -2
  437. package/scripts/rules/lane-background.ts +15 -7
  438. package/scripts/rules/lane-carry-conflict.ts +1 -2
  439. package/scripts/rules/lane-park.ts +14 -8
  440. package/scripts/rules/last-prompt.ts +1 -0
  441. package/scripts/rules/oracle-consulted.ts +1 -1
  442. package/scripts/rules/oracle-firing.ts +1 -0
  443. package/scripts/rules/oracle-list-cli.ts +0 -3
  444. package/scripts/rules/permission-guards.ts +14 -0
  445. package/scripts/rules/pre-gate-cut.ts +2 -2
  446. package/scripts/rules/protected-files.ts +74 -0
  447. package/scripts/rules/reply-language.ts +1 -1
  448. package/scripts/rules/rule-body-guard.ts +0 -3
  449. package/scripts/rules/rule-value-cli.ts +0 -1
  450. package/scripts/rules/shell-body-trigger.ts +0 -1
  451. package/scripts/rules/shell-segments.ts +0 -1
  452. package/scripts/rules/stop-rules.ts +13 -5
  453. package/scripts/rules/tail-commands.ts +1 -1
  454. package/scripts/rules/test-declared-commit.ts +1 -1
  455. package/scripts/rules/third-party-write.ts +0 -1
  456. package/scripts/rules/worktree-guard.ts +26 -9
  457. package/scripts/run/run-carry-cli.ts +0 -2
  458. package/scripts/run/run-carry-stash.ts +2 -2
  459. package/scripts/run/run-carry-streak.ts +1 -1
  460. package/scripts/run/run-cut-cli.ts +0 -7
  461. package/scripts/run/run-cut.ts +8 -54
  462. package/scripts/run/run-ending-cli.ts +1 -1
  463. package/scripts/run/run-ending.ts +1 -4
  464. package/scripts/run/run-entry-cli.ts +101 -24
  465. package/scripts/run/run-entry.ts +0 -1
  466. package/scripts/run/run-event-cli.ts +0 -2
  467. package/scripts/run/run-event-follow.ts +1 -0
  468. package/scripts/run/run-event-render.ts +1 -1
  469. package/scripts/run/run-event-scope.ts +53 -4
  470. package/scripts/run/run-event-stream.ts +0 -3
  471. package/scripts/run/run-event-watch.ts +1 -0
  472. package/scripts/run/run-halfrun-resume.ts +20 -7
  473. package/scripts/run/run-headless.ts +24 -8
  474. package/scripts/run/run-hold-cli.ts +33 -10
  475. package/scripts/run/run-hold.ts +25 -5
  476. package/scripts/run/run-issue-number.ts +1 -1
  477. package/scripts/run/run-liveness-cli.ts +1 -1
  478. package/scripts/run/run-liveness.ts +2 -3
  479. package/scripts/run/run-merge-cli.ts +5 -13
  480. package/scripts/run/run-merge-steps.ts +5 -5
  481. package/scripts/run/run-merge.ts +1 -1
  482. package/scripts/run/run-next-cli.ts +1 -1
  483. package/scripts/run/run-parent-cut-hook.ts +143 -0
  484. package/scripts/run/run-preflight.ts +2 -9
  485. package/scripts/run/run-prep-cli.ts +2 -2
  486. package/scripts/run/run-prep-locate.ts +1 -1
  487. package/scripts/run/run-progress-cli.ts +4 -7
  488. package/scripts/run/run-progress-clock.ts +6 -34
  489. package/scripts/run/run-progress-config.ts +0 -4
  490. package/scripts/run/run-progress-read.ts +3 -3
  491. package/scripts/run/run-progress.ts +22 -10
  492. package/scripts/run/run-prrun-resume.ts +117 -0
  493. package/scripts/run/run-report-cli.ts +1 -2
  494. package/scripts/run/run-report.ts +0 -1
  495. package/scripts/run/run-retrospective.ts +1 -1
  496. package/scripts/run/run-review-cli.ts +0 -3
  497. package/scripts/run/run-review-steps.ts +2 -0
  498. package/scripts/run/run-review.ts +0 -2
  499. package/scripts/run/run-ship-cli.ts +27 -77
  500. package/scripts/run/run-ship-detach.ts +1 -0
  501. package/scripts/run/run-ship-next.ts +106 -0
  502. package/scripts/run/run-ship-preflight.ts +87 -0
  503. package/scripts/run/run-ship-return.ts +8 -3
  504. package/scripts/run/run-ship-review-steps.ts +23 -30
  505. package/scripts/run/run-ship-review.ts +20 -0
  506. package/scripts/run/run-ship-scoped.ts +45 -0
  507. package/scripts/run/run-ship-stage.ts +5 -1
  508. package/scripts/run/run-ship-steps.ts +105 -0
  509. package/scripts/run/run-ship.ts +2 -1
  510. package/scripts/run/run-stage-read.ts +62 -0
  511. package/scripts/run/run-stage.ts +160 -0
  512. package/scripts/run/run-status-cli.ts +1 -1
  513. package/scripts/run/run-step-cli.ts +21 -14
  514. package/scripts/run/run-step.ts +1 -1
  515. package/scripts/run/run-stop-notify.ts +1 -3
  516. package/scripts/run/run-stranded-cli.ts +0 -4
  517. package/scripts/run/run-stranded-detect.ts +2 -2
  518. package/scripts/run/run-tail-cli.ts +17 -11
  519. package/scripts/run/run-tail.ts +2 -0
  520. package/scripts/run/run-tidy-lanes.ts +2 -1
  521. package/scripts/run/run-tidy-stashes.ts +12 -12
  522. package/scripts/run/run-wake-cli.ts +4 -5
  523. package/scripts/run/run-wake-describe.ts +19 -1
  524. package/scripts/run/run-wake-loop.ts +2 -2
  525. package/scripts/run/run-wake.ts +2 -4
  526. package/scripts/run/run-watcher-guard-cli.ts +0 -4
  527. package/scripts/run/run-watcher-guard.ts +9 -7
  528. package/scripts/run/run-watcher-hook.ts +1 -1
  529. package/scripts/safe-chain/ci-installer-pin.ts +11 -5
  530. package/scripts/safe-chain/project-config.ts +1 -1
  531. package/scripts/security/security-audit-provision-logic.ts +0 -1
  532. package/scripts/security/security-audit-provision.ts +3 -2
  533. package/scripts/security/security-audit.ts +0 -1
  534. package/scripts/security/security-updates-logic.ts +0 -1
  535. package/scripts/sonar/sonar-hotspots-cli.ts +10 -52
  536. package/scripts/sonar/sonar-hotspots.ts +0 -1
  537. package/scripts/sonar/sonar-new-code-cli.ts +150 -0
  538. package/scripts/sonar/sonar-new-code.ts +79 -0
  539. package/scripts/sonar/sonar-project.ts +97 -0
  540. package/scripts/split/split-assess-cli.ts +1 -1
  541. package/scripts/split/split-assess.ts +2 -5
  542. package/scripts/sync/managed-config-scope-cli.ts +1 -1
  543. package/scripts/sync/skill-migration.ts +0 -1
  544. package/scripts/sync/sync-hook-safety.ts +2 -3
  545. package/scripts/sync/sync.ts +1 -8
  546. package/scripts/sync/template-source-logic.ts +0 -1
  547. package/scripts/sync/workflow-pin-logic.ts +16 -28
  548. package/scripts/test/test-declared-logic.ts +0 -1
  549. package/scripts/test/test-declared.ts +0 -2
  550. package/scripts/test/test-red-commit.ts +2 -2
  551. package/scripts/test/test-red.ts +14 -7
  552. package/scripts/test/test-type-logic.ts +1 -1
  553. package/scripts/test/unit-worker-share.ts +7 -16
  554. package/scripts/time-runtime/time-batch-guard.ts +1 -2
  555. package/scripts/time-runtime/time-bundle-call.ts +2 -3
  556. package/scripts/time-runtime/time-bundles.ts +0 -4
  557. package/scripts/time-runtime/time-density-hook.ts +0 -2
  558. package/scripts/time-runtime/time-followup-stage.ts +1 -1
  559. package/scripts/time-runtime/time-format.ts +0 -73
  560. package/scripts/time-runtime/time-round-trips.ts +0 -4
  561. package/scripts/time-runtime/time-single-check.ts +1 -1
  562. package/scripts/time-runtime/time-spans.ts +1 -1
  563. package/scripts/time-runtime/time-transcript-line.ts +1 -2
  564. package/scripts/ui/ui-routes-cli.ts +0 -5
  565. package/scripts/ui/ui-routes.ts +0 -5
  566. package/scripts/version/effective-upstream.ts +2 -2
  567. package/scripts/version/latest-corepack.ts +7 -9
  568. package/scripts/version/latest-scope-cli.ts +0 -2
  569. package/scripts/version/latest-update.ts +0 -2
  570. package/scripts/version/npm-registry.ts +97 -0
  571. package/scripts/version/publishable-range-check.ts +1 -1
  572. package/scripts/version/release-hold.ts +0 -4
  573. package/scripts/version/running-binary.ts +2 -2
  574. package/scripts/version/version-check-logic.ts +0 -2
  575. package/scripts/version/version-commands.ts +5 -4
  576. package/scripts/version/version-remote.ts +28 -14
  577. package/scripts/version/version-targets.ts +4 -4
  578. package/templates/sonar-project.properties +28 -1
  579. package/templates/workflows/dependabot-auto-merge.yml +5 -3
  580. package/templates/workflows/github-release.yml +81 -0
  581. package/prompts/collaboration-workflow/cross-repo-epic.md +0 -5
  582. package/prompts/collaboration-workflow/epic-audit.md +0 -5
  583. package/prompts/collaboration-workflow/epic-bundle.md +0 -5
  584. package/scripts/init/plugin-install-hint.ts +0 -47
  585. package/scripts/version/parse-json.ts +0 -13
  586. /package/scripts/{git → followup}/git-followup-stages.ts +0 -0
  587. /package/scripts/{git → gh}/git-gh-api-path.ts +0 -0
  588. /package/scripts/{git → gh}/git-gh-command.ts +0 -0
  589. /package/scripts/{git → gh}/git-gh-issue-write.ts +0 -0
  590. /package/scripts/{git → gh}/git-gh-issue.ts +0 -0
  591. /package/scripts/{git → gh}/git-gh-pr-auto-merge.ts +0 -0
  592. /package/scripts/{git → gh}/git-gh-pr-snapshot.ts +0 -0
  593. /package/scripts/{git → gh}/git-gh-reachability.ts +0 -0
  594. /package/scripts/{git → gh}/git-gh-repo.ts +0 -0
  595. /package/scripts/{git → gh}/git-gh-rest-state.ts +0 -0
  596. /package/scripts/{git → gh}/git-pr-checks-watch.ts +0 -0
  597. /package/scripts/{git → gh}/git-pr-error.ts +0 -0
  598. /package/scripts/{git → gh}/git-pr-messages.ts +0 -0
  599. /package/scripts/{git → gh}/github-issue-url.ts +0 -0
  600. /package/scripts/git/{stash-orphans.ts → stash/stash-orphans.ts} +0 -0
  601. /package/scripts/{git → issue}/issue-reference.ts +0 -0
  602. /package/scripts/{git → lib}/animation-helpers.ts +0 -0
  603. /package/scripts/{git → lib}/git-animation.ts +0 -0
  604. /package/scripts/{git → notify}/git-notify.ts +0 -0
  605. /package/scripts/{git → notify}/telegram-test-logic.ts +0 -0
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env tsx
2
+ import { text } from 'node:stream/consumers'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { hook_decision } from '#scripts/josh/hook-decision'
5
+ import { pretool_outcome_async } from './pretool-guard'
6
+
7
+ // The `PreToolUse` hook's entry, kept apart from the composition in `pretool-guard.ts`
8
+ // (joshuafolkken/kit#2922). The hook bundles are built with `splitting: true`, which moves a module
9
+ // imported by two entries into a shared chunk — and `codex-hook-adapter.ts` imports `pretool-guard.ts`.
10
+ // Its self-invoke went into that chunk, whose `import.meta.url` never equals the launched
11
+ // `dist/hooks/pretool-guard.js`, so the bundled hook ran no guard at all and every refusal failed open.
12
+ // An entry nothing imports keeps the self-invoke in the file that is actually launched.
13
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
14
+ if (process.stdin.isTTY) hook_decision.report_no_payload('pretool:guard')
15
+ else {
16
+ const raw_payload = await text(process.stdin)
17
+
18
+ hook_decision.load_environment_file()
19
+ hook_decision.emit_outcome(await pretool_outcome_async(raw_payload))
20
+ }
21
+ }
@@ -1,12 +1,11 @@
1
- #!/usr/bin/env tsx
2
- import { text } from 'node:stream/consumers'
3
- import { fileURLToPath } from 'node:url'
4
1
  import { duplicate_read_outcome } from '#scripts/delegation/duplicate-read-guard'
5
2
  import { investigation_refusal } from '#scripts/delegation/investigation-guard'
6
- import { hook_decision, type GuardOutcome } from '#scripts/josh/hook-decision'
3
+ import type { GuardOutcome } from '#scripts/josh/hook-decision'
7
4
  import { delivered_rules } from '#scripts/rules/delivered-rules'
5
+ import { run_parent_cut_hook } from '#scripts/run/run-parent-cut-hook'
8
6
  import { run_watcher_hook } from '#scripts/run/run-watcher-hook'
9
7
  import { batch_outcome } from './batch-guard'
8
+ import { step_zero_notice } from './step-zero-notice'
10
9
 
11
10
  // One PreToolUse process for the three guards that used to be three (joshuafolkken/kit#1930). A Bash
12
11
  // call used to spawn `batch:guard`, `investigation:guard` and `rule:guard` separately — three pnpm
@@ -51,10 +50,20 @@ function with_duplicate_notice(combined: GuardOutcome, notice: string | undefine
51
50
  return { reason: undefined, notice, fault: undefined }
52
51
  }
53
52
 
53
+ // The Step 0 reminder is the last word (joshuafolkken/kit#2994): asked only where every guard stayed
54
+ // clear, so a refusal or another notice never spends its once-per-session stamp unseen.
55
+ function with_step_zero_notice(outcome: GuardOutcome, raw_payload: string): GuardOutcome {
56
+ if (!is_clear(outcome)) return outcome
57
+
58
+ const notice = step_zero_notice.notice(raw_payload)
59
+
60
+ return notice === undefined ? outcome : { reason: undefined, notice, fault: undefined }
61
+ }
62
+
54
63
  // All guards run unconditionally, exactly as the separate PreToolUse entries did: each records its own
55
64
  // once-per-run stamp, so short-circuiting on the first refusal would change which guard is allowed to
56
65
  // fire on a later call.
57
- function pretool_outcome(raw_payload: string): GuardOutcome {
66
+ function guard_outcome(raw_payload: string): GuardOutcome {
58
67
  const batch = batch_outcome(raw_payload)
59
68
  const investigation = investigation_refusal(raw_payload)
60
69
  const duplicate = duplicate_read_outcome(raw_payload)
@@ -65,32 +74,40 @@ function pretool_outcome(raw_payload: string): GuardOutcome {
65
74
  return with_duplicate_notice(combined, duplicate.notice)
66
75
  }
67
76
 
77
+ // The synchronous verdict the Codex adapter asks for, with the Step 0 reminder as its last layer.
78
+ function pretool_outcome(raw_payload: string): GuardOutcome {
79
+ return with_step_zero_notice(guard_outcome(raw_payload), raw_payload)
80
+ }
81
+
68
82
  // The watcher guard is the one composed rule that cannot answer synchronously — it reads the lane
69
83
  // registry and the watcher's life record off disk (joshuafolkken/kit#2353). So it is asked after the
70
84
  // synchronous guards and only where they stayed clear: a refusal from any of them wins first, exactly
71
85
  // as `combine_outcomes` orders them, and the watcher's refusal fills a clear verdict rather than
72
86
  // overriding one. It fires once per run itself (`run-watcher-hook.ts`), so a stale watcher refuses the
73
87
  // first call but not the `pnpm josh run:progress --wait` that fixes it.
88
+ //
89
+ // **The parent hand-off guard is the second asynchronous rule, asked after the watcher's**
90
+ // (joshuafolkken/kit#2947). It reads the carry record to tell the `backlogrun` parent apart, so it sits
91
+ // beside the watcher rather than among the synchronous rows; a stale watcher is the cheaper fix and is
92
+ // surfaced first.
93
+ async function async_reason(raw_payload: string): Promise<string | undefined> {
94
+ const watcher = await run_watcher_hook.watcher_hook_reason(raw_payload)
95
+
96
+ return watcher ?? (await run_parent_cut_hook.parent_cut_reason(raw_payload))
97
+ }
98
+
74
99
  async function pretool_outcome_async(raw_payload: string): Promise<GuardOutcome> {
75
- const base = pretool_outcome(raw_payload)
100
+ const base = guard_outcome(raw_payload)
76
101
 
77
102
  if (!is_clear(base)) return base
78
103
 
79
- const reason = await run_watcher_hook.watcher_hook_reason(raw_payload)
80
-
81
- return reason === undefined ? base : { reason, notice: undefined, fault: undefined }
82
- }
83
-
84
- const pretool_guard = { combine_outcomes, pretool_outcome, pretool_outcome_async }
104
+ const reason = await async_reason(raw_payload)
85
105
 
86
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
87
- if (process.stdin.isTTY) hook_decision.report_no_payload('pretool:guard')
88
- else {
89
- const raw_payload = await text(process.stdin)
106
+ if (reason !== undefined) return { reason, notice: undefined, fault: undefined }
90
107
 
91
- hook_decision.load_environment_file()
92
- hook_decision.emit_outcome(await pretool_outcome_async(raw_payload))
93
- }
108
+ return with_step_zero_notice(base, raw_payload)
94
109
  }
95
110
 
111
+ const pretool_guard = { combine_outcomes, pretool_outcome }
112
+
96
113
  export { pretool_guard, pretool_outcome, pretool_outcome_async }
@@ -85,6 +85,6 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
85
85
  process.exitCode = await run_reserved(process.argv.slice(FIRST_ARGUMENT_INDEX))
86
86
  }
87
87
 
88
- const reserved_run = { parse_arguments, run_command, run_reserved }
88
+ const reserved_run = { parse_arguments, run_reserved }
89
89
 
90
90
  export { reserved_run }
@@ -0,0 +1,76 @@
1
+ import path from 'node:path'
2
+ import { hook_decision } from '#scripts/josh/hook-decision'
3
+ import { json_value } from '#scripts/lib/json-value'
4
+ import { test_declared_logic } from '#scripts/test/test-declared-logic'
5
+
6
+ // The Code Change Rules Step 0 reminder, delivered at the moment implementation code is about to be
7
+ // written rather than on every prompt (joshuafolkken/kit#2994). As a `UserPromptSubmit` echo it was
8
+ // injected into every turn — a question that writes nothing included — and re-read as accumulated
9
+ // context on each turn after it. The trigger the rule names is "before writing any implementation
10
+ // code", so the hook that delivers it is the one that sees that write: the first `Edit` / `Write` of a
11
+ // runtime file in the session.
12
+ //
13
+ // **A runtime file only.** What counts is `test_declared_logic.is_exempt`, the same classification
14
+ // `pnpm josh test:declared` answers at the commit, so a doc or prompt edit neither shows the notice nor
15
+ // spends it. A path outside the working tree (a memory file, a scratchpad) is not this project's code.
16
+ //
17
+ // **Once per session**, on a stamp keyed by the transcript like every other guard record here. A stamp
18
+ // that cannot be written stays silent rather than repeating the notice on every edit.
19
+ const STAMP_PREFIX = 'josh-step-zero-notice-'
20
+ const EDIT_TOOLS: ReadonlySet<string> = new Set(['Edit', 'Write'])
21
+ const PARENT_PREFIX = '..'
22
+
23
+ const NOTICE =
24
+ 'Before writing any implementation code: present the two-layer work summary (Now / Change / Check, then every change with its test) once per Issue. Full rule: Code Change Rules Step 0 in CLAUDE.md.'
25
+
26
+ const STAMP = hook_decision.create_refusal_stamp(STAMP_PREFIX)
27
+
28
+ // A Codex `apply_patch` arrives translated with its first file as `file_path` and every file in
29
+ // `file_paths`, so a patch that leads with a doc and also writes code is still a runtime write.
30
+ function edited_paths(tool_input: unknown): Array<string> {
31
+ if (!json_value.is_record(tool_input)) return []
32
+
33
+ const { file_path, file_paths } = tool_input
34
+ const listed: ReadonlyArray<unknown> = Array.isArray(file_paths) ? file_paths : []
35
+
36
+ return [file_path, ...listed].filter(
37
+ (entry): entry is string => typeof entry === 'string' && entry.length > 0,
38
+ )
39
+ }
40
+
41
+ function is_runtime_file(file_path: string, root: string): boolean {
42
+ const relative = path.relative(root, path.resolve(root, file_path))
43
+
44
+ if (relative.startsWith(PARENT_PREFIX) || path.isAbsolute(relative)) return false
45
+
46
+ return !test_declared_logic.is_exempt(relative.split(path.sep).join('/'))
47
+ }
48
+
49
+ function is_candidate(raw_payload: string, root: string): string | undefined {
50
+ const payload = hook_decision.parse_hook_payload(raw_payload)
51
+
52
+ if (payload === undefined || !EDIT_TOOLS.has(payload.tool_name)) return undefined
53
+
54
+ const has_runtime_file = edited_paths(payload.tool_input).some((file_path) =>
55
+ is_runtime_file(file_path, root),
56
+ )
57
+
58
+ return has_runtime_file ? payload.transcript_path : undefined
59
+ }
60
+
61
+ function notice(raw_payload: string, root: string = process.cwd()): string | undefined {
62
+ const transcript = is_candidate(raw_payload, root)
63
+
64
+ if (transcript === undefined) return undefined
65
+
66
+ const target = STAMP.path(transcript)
67
+
68
+ if (STAMP.last_ms(target) !== hook_decision.NEVER_MS) return undefined
69
+ if (!STAMP.record(target, Date.now())) return undefined
70
+
71
+ return NOTICE
72
+ }
73
+
74
+ const step_zero_notice = { NOTICE, notice, stamp_path: STAMP.path }
75
+
76
+ export { step_zero_notice }
@@ -7,12 +7,15 @@ import { repo_party } from '#scripts/discovery/repo-party'
7
7
  import { hook_decision } from '#scripts/josh/hook-decision'
8
8
  import { session_language } from '#scripts/josh/session-language'
9
9
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
10
+ import { lane_handoff } from '#scripts/lane/lane-handoff'
10
11
  import { filing_cap } from '#scripts/rules/filing-cap'
11
12
  import { lane_background } from '#scripts/rules/lane-background'
12
13
  import { lane_park } from '#scripts/rules/lane-park'
13
14
  import { last_prompt } from '#scripts/rules/last-prompt'
14
15
  import { stop_rules, type StopContext, type StopOutcome } from '#scripts/rules/stop-rules'
16
+ import { run_carry } from '#scripts/run/run-carry'
15
17
  import { run_cut } from '#scripts/run/run-cut'
18
+ import { run_halfrun_resume } from '#scripts/run/run-halfrun-resume'
16
19
  import { run_headless } from '#scripts/run/run-headless'
17
20
  import { run_hold } from '#scripts/run/run-hold'
18
21
  import { run_stranded_detect } from '#scripts/run/run-stranded-detect'
@@ -29,16 +32,17 @@ import { time_hook_transcript } from '#scripts/time-runtime/time-hook-transcript
29
32
  // resolving (send the notify, release the hold) and `stop_hook_active` is the backstop, so failing
30
33
  // open here costs at most a missed nudge, never a stuck run.
31
34
 
32
- // The hold record for this working tree is still in place. `unreadable` reads as absent: the safe
33
- // direction for a stop guard is not to block, and an unreadable record is not proof a run is holding.
34
- async function is_hold_present(): Promise<boolean> {
35
+ // The hold record for this working tree is still in place, and whether it is a `prrun` stop's
36
+ // (joshuafolkken/kit#3023). `unreadable` reads as absent: the safe direction for a stop guard is not to
37
+ // block, and an unreadable record is not proof a run is holding.
38
+ async function hold_facts(): Promise<Pick<StopContext, 'hold_present' | 'prrun_stopped'>> {
35
39
  const directory = await run_hold.worktree_directory()
40
+ const hold =
41
+ directory === undefined
42
+ ? undefined
43
+ : run_halfrun_resume.hold_of(run_hold.read_hold(run_hold.hold_path(directory)))
36
44
 
37
- if (directory === undefined) return false
38
-
39
- const read = run_hold.read_hold(run_hold.hold_path(directory))
40
-
41
- return read.kind === 'held' || read.kind === 'stale'
45
+ return { hold_present: hold !== undefined, prrun_stopped: hold?.prrun_stop_head !== undefined }
42
46
  }
43
47
 
44
48
  // A `confirmation` notify sits on this run's transcript tail. The tail is derived exactly as the
@@ -67,7 +71,7 @@ async function build_context(
67
71
  const tail = transcript_tail(transcript_path)
68
72
 
69
73
  return {
70
- hold_present: await is_hold_present(),
74
+ ...(await hold_facts()),
71
75
  tree_clean: !(await run_hold.is_tree_dirty()),
72
76
  notified: lane_park.mentions_confirmation_notify(tail),
73
77
  message: payload_tail.message,
@@ -81,6 +85,7 @@ async function build_context(
81
85
  lane_child: lane_child_marker.is_child_of(process.cwd()),
82
86
  background_pending: lane_background.pending_background_ids(tail).length > 0,
83
87
  agent_pending: lane_background.pending_agent_ids(tail).length > 0,
88
+ handed_off: lane_handoff.is_handed_off(process.cwd()),
84
89
  session_lang: session_lang(),
85
90
  }
86
91
  }
@@ -106,22 +111,43 @@ async function outcome_of(raw_payload: string): Promise<StopOutcome> {
106
111
  }
107
112
  }
108
113
 
114
+ // A live carry record is in place — `carried` alone reads as a run. Both report checks below are about
115
+ // a carried run: the strand judge reads this very record and returns on anything but `carried`, and a
116
+ // stall needs a backlog driver, which begins one before its plan. A spent (`expired`) or `unreadable`
117
+ // record a crashed run left behind is no run either, so it does not re-arm the stall's backlog probe
118
+ // on every stop (joshuafolkken/kit#2995).
119
+ // A failed git read — outside a repository, `git rev-parse` exits non-zero — reads as no run, since this
120
+ // gate sits ahead of the checks' own swallowing and must not throw into the stop decision either.
121
+ async function has_run_record(): Promise<boolean> {
122
+ try {
123
+ const directory = await run_carry.repository_directory()
124
+
125
+ if (directory === undefined) return false
126
+
127
+ return run_carry.read_carry(run_carry.carry_path(directory)).kind === 'carried'
128
+ } catch {
129
+ return false
130
+ }
131
+ }
132
+
133
+ // The stall check rides the Stop hook because the stop *is* the loop boundary: a run alive but not
134
+ // advancing ends turns without dispatching (joshuafolkken/kit#2359). The strand check rides the same
135
+ // boundary and is the step before the stall (joshuafolkken/kit#2375): it fires when the driver is gone
136
+ // — the budget handed off, the owner dead, no supervisor watching. Both only report and swallow their
137
+ // own failures, so neither can change the stop decision. An ordinary stop with no run record skips
138
+ // both, so it pays one directory read rather than their reads and a stale stream's backlog probe.
139
+ async function run_report_checks(): Promise<void> {
140
+ if (!(await has_run_record())) return
141
+
142
+ await backlog_stalled_detect.run_stall_check(backlog_ready.DEFAULT_PORTS)
143
+ await run_stranded_detect.run_stranded_check()
144
+ }
145
+
109
146
  // Nothing reaches stdout on an ordinary stop, so what the harness parses stays empty unless the stop
110
147
  // is being held — all three rules block, so a bare `#N` is reported the same way (joshuafolkken/kit#2247).
111
148
  async function write_stop_decision(raw_payload: string): Promise<void> {
112
149
  hook_decision.load_environment_file()
113
-
114
- // The stall check rides the Stop hook because the stop *is* the loop boundary: a run alive but not
115
- // advancing ends turns without dispatching (joshuafolkken/kit#2359). It only reports — leaves a
116
- // marker, sends a notification — and swallows its own failures, so it can never change the decision
117
- // below or hold the stop.
118
- await backlog_stalled_detect.run_stall_check(backlog_ready.DEFAULT_PORTS)
119
-
120
- // The strand check rides the same boundary for the same reason, and is the step before the stall
121
- // (joshuafolkken/kit#2375): the stall detector needs a live driver that could dispatch, and this one
122
- // fires when that driver is gone — the budget handed off, the owner dead, no supervisor watching. It
123
- // too only reports and swallows its own failures, so it never touches the stop decision below.
124
- await run_stranded_detect.run_stranded_check()
150
+ await run_report_checks()
125
151
 
126
152
  const { reason } = await outcome_of(raw_payload)
127
153
 
@@ -13,9 +13,10 @@ const PLUGIN_ID = 'kit@kit'
13
13
  const MARKETPLACE_PATH = './node_modules/@joshuafolkken/kit'
14
14
  const CLAUDE_SETTINGS_DESTINATION = path.join('.claude', 'settings.json')
15
15
 
16
- // `enabledPlugins` declares the plugin; `extraKnownMarketplaces` says where to find it. The CLI does
17
- // not auto-install from these alone — a consumer runs `claude plugin install kit@kit` once — so the
18
- // declaration is what makes that one command resolve without further arguments.
16
+ // `enabledPlugins` declares the plugin; `extraKnownMarketplaces` says where to find it. Nothing needs
17
+ // installing: in a trusted workspace Claude Code registers the marketplace from this declaration and
18
+ // loads the skills — an interactive session from its first session, a headless one (`claude -p`) from
19
+ // its second (joshuafolkken/kit#2990).
19
20
  const PLUGIN_SETTINGS: Record<string, unknown> = {
20
21
  extraKnownMarketplaces: {
21
22
  [MARKETPLACE_NAME]: { source: { source: 'directory', path: MARKETPLACE_PATH } },
@@ -39,11 +40,9 @@ function apply_plugin_config_for_destination(destination_path: string, content:
39
40
  }
40
41
 
41
42
  const claude_plugin_config = {
42
- MARKETPLACE_NAME,
43
43
  PLUGIN_ID,
44
44
  MARKETPLACE_PATH,
45
45
  CLAUDE_SETTINGS_DESTINATION,
46
- PLUGIN_SETTINGS,
47
46
  inject_plugin_config,
48
47
  apply_plugin_config_for_destination,
49
48
  }
@@ -1,4 +1,3 @@
1
- import { GIT_LOCATION_VARIABLES } from '#scripts/git/git-location-environment'
2
1
  import { claude_plugin_config } from './claude-plugin-config'
3
2
  import { hook_launch } from './hook-launch'
4
3
 
@@ -27,8 +26,11 @@ const MISSING_INSTALL_NOTICE = 'kit hooks inactive: run pnpm install, then rerea
27
26
  const MISSING_INSTALL_GUARD = `if [ ! -f ${CONSUMER_PACKAGE_MANIFEST} ]; then echo '${MISSING_INSTALL_NOTICE}' >&2; exit 0; fi; `
28
27
  const CODEX_ADAPTER_BUNDLE = `node ${CONSUMER_HOOK_BUNDLE_DIR}codex-hook-adapter.js`
29
28
  const CODEX_HOOKS_DESTINATION = '.codex/hooks.json'
30
- const GIT_UNSET_OPTIONS = GIT_LOCATION_VARIABLES.map((name) => `-u ${name}`).join(' ')
31
- const PROJECT_ROOT_PREFIX = String.raw`if project_root=\"$(env ${GIT_UNSET_OPTIONS} git rev-parse --show-toplevel 2>/dev/null)\"; then unset ${GIT_LOCATION_VARIABLES.join(' ')}; else project_root=\"$(git rev-parse --show-toplevel)\" || exit 1; fi; cd \"$project_root\" && `
29
+ // The values below are matched inside raw JSON text, so the root prefix is taken in its escaped form.
30
+ const PROJECT_ROOT_PREFIX = JSON.stringify(hook_launch.PROJECT_ROOT_PREFIX).slice(1, -1)
31
+ // The installed package ships its bundles built, so they cannot be stale against a source the consumer
32
+ // never has: the ready gate kit's own commands pass (joshuafolkken/kit#2984) becomes a presence check.
33
+ const CONSUMER_READY_GATE = `[ -d ${CONSUMER_HOOK_BUNDLE_DIR} ]`
32
34
  // Capture the value of every `"command"` field, escapes and all, so the rewrites below touch command
33
35
  // values alone — never an echo reminder's prose or a `"description"` that merely mentions the string.
34
36
  const COMMAND_FIELD = /("command":\s*")((?:[^"\\]|\\.)*)(")/gu
@@ -49,6 +51,8 @@ function rebase_bundle_directory(value: string): string {
49
51
  // holds no `pnpm josh `), so the whole rewrite is safe to run more than once.
50
52
  function rewrite_command_value(value: string): string {
51
53
  const rewritten = rebase_bundle_directory(value)
54
+ .split(hook_launch.BUNDLE_READY_GATE)
55
+ .join(CONSUMER_READY_GATE)
52
56
  .split(PNPM_JOSH_PREFIX)
53
57
  .join(BUNDLE_INVOCATION)
54
58
  .split(CODEX_ADAPTER_SOURCE)
@@ -60,12 +64,18 @@ function rewrite_command_value(value: string): string {
60
64
  return `${MISSING_INSTALL_GUARD}${rewritten}`
61
65
  }
62
66
 
67
+ // kit's own commands already start from the root (`hook-launch.ts`), and so does an already-rewritten
68
+ // value. The prefix is taken off before the rewrite and put back after it, so the install guard sits
69
+ // behind the `cd` it depends on and a second pass never doubles the prefix.
70
+ function without_root_prefix(value: string): string {
71
+ return value.startsWith(PROJECT_ROOT_PREFIX) ? value.slice(PROJECT_ROOT_PREFIX.length) : value
72
+ }
73
+
63
74
  function rewrite_hook_commands(content: string, is_codex = false): string {
64
75
  return content.replaceAll(COMMAND_FIELD, (_match, open: string, value: string, close: string) => {
65
- const rewritten = rewrite_command_value(value)
76
+ const rewritten = rewrite_command_value(without_root_prefix(value))
66
77
  const should_select_root = is_codex || rewritten.includes(claude_plugin_config.MARKETPLACE_PATH)
67
- const prefix =
68
- should_select_root && !rewritten.startsWith(PROJECT_ROOT_PREFIX) ? PROJECT_ROOT_PREFIX : ''
78
+ const prefix = should_select_root ? PROJECT_ROOT_PREFIX : ''
69
79
 
70
80
  return `${open}${prefix}${rewritten}${close}`
71
81
  })
@@ -89,8 +99,6 @@ function apply_hook_command_rewrite_for_destination(
89
99
 
90
100
  const hook_command_rewrite = {
91
101
  apply_hook_command_rewrite_for_destination,
92
- BUNDLE_INVOCATION,
93
- PNPM_JOSH_PREFIX,
94
102
  rewrite_hook_commands,
95
103
  }
96
104
 
@@ -1,31 +1,57 @@
1
+ import { GIT_LOCATION_VARIABLES } from '#scripts/git/git-location-environment'
2
+
1
3
  // The shell command a Claude Code hook is launched with, so it starts without a `pnpm` wrapper — and,
2
4
  // for a consumer, without the `dist/josh.js` dispatcher re-spawning `tsx` for the hook's `.ts`
3
5
  // (joshuafolkken/kit#2023). Each hook is pre-built to its own `dist/hooks/<name>.js`
4
6
  // (`scripts/build/build-hooks.ts`) and launched directly with `node`.
5
7
  //
6
- // **The fallback is a real branch, not a courtesy.** A fresh clone has no `dist/hooks/` until
7
- // `pnpm build` runs, so the command prefers the bundle and drops to `pnpm josh <command>` (the live
8
- // `tsx` source) when the bundle is absent, rather than passing silently. **An `if`/`else`, never
9
- // `&&`/`||`**: a guard's refusal is a non-zero exit, and a `node … || pnpm josh …` chain would read
10
- // that refusal as a missing bundle and run the hook twice. `[ -f ]` decides on the file alone and
11
- // leaves the chosen branch's exit code untouched.
12
-
13
- // Relative to the working directory a hook runs in — kit's own root, where the built `dist/hooks/`
14
- // sits. The consumer rewrite (`hook-command-rewrite.ts`) rebases this onto the installed package.
8
+ // **The command starts from the project root, never from wherever the session happens to be**
9
+ // (joshuafolkken/kit#2984). Every path after the prefix is relative, so a hook fired from a
10
+ // subdirectory would otherwise miss its bundle. The root is git's, with the location variables a git
11
+ // hook exports cleared first — the same resolution the consumer rewrite (`hook-command-rewrite.ts`)
12
+ // applies, so both sides stay one mechanism for Claude Code and Codex alike.
13
+ //
14
+ // **The bundle runs only once the ready gate passes** (`scripts/hooks/hook-bundle-ready.ts`): it
15
+ // rebuilds `dist/hooks/` when the source beside it has changed, so an edited guard is never shadowed by
16
+ // its old bundle. The gate failing — a checkout that cannot build — drops to the fallback, the live
17
+ // source. **An `if`/`else`, never `&&`/`||`**: a guard's refusal is a non-zero exit, and a
18
+ // `node … || pnpm josh …` chain would read that refusal as a missing bundle and run the hook twice.
19
+
15
20
  const HOOK_DIST_DIR = 'dist/hooks'
21
+ const UNSET_OPTIONS = GIT_LOCATION_VARIABLES.map((name) => `-u ${name}`).join(' ')
22
+ const PROJECT_ROOT_PREFIX = `if project_root="$(env ${UNSET_OPTIONS} git rev-parse --show-toplevel 2>/dev/null)"; then unset ${GIT_LOCATION_VARIABLES.join(' ')}; else project_root="$(git rev-parse --show-toplevel)" || exit 1; fi; cd "$project_root" && `
23
+ // Plain `node` runs the gate's TypeScript directly; the flag keeps Node 22's type-stripping notice off
24
+ // every hook's stderr.
25
+ const BUNDLE_READY_GATE =
26
+ 'node --disable-warning=ExperimentalWarning scripts/hooks/hook-bundle-ready.ts'
16
27
 
17
- // Prefer `primary_path` (run as `node <path>`) when it exists, else run `fallback_command` verbatim.
18
- // Kept generic so a test can exercise the branch selection with stub commands rather than real hooks.
19
- function launch_command(primary_path: string, fallback_command: string): string {
20
- return `if [ -f ${primary_path} ]; then node ${primary_path}; else ${fallback_command}; fi`
28
+ // Run `primary_command` when `gate` exits 0, else `fallback_command` — both verbatim. Kept generic so
29
+ // a test can exercise the branch selection with stub commands rather than real hooks.
30
+ function launch_command(gate: string, primary_command: string, fallback_command: string): string {
31
+ return `if ${gate}; then ${primary_command}; else ${fallback_command}; fi`
32
+ }
33
+
34
+ // `invocation` is the hook's file under `dist/hooks` plus any arguments (e.g. `pretool-guard.js`);
35
+ // `fallback_command` runs the same hook from source.
36
+ function bundle_launch_command(invocation: string, fallback_command: string): string {
37
+ const primary = `node ${HOOK_DIST_DIR}/${invocation}`
38
+
39
+ return `${PROJECT_ROOT_PREFIX}${launch_command(BUNDLE_READY_GATE, primary, fallback_command)}`
21
40
  }
22
41
 
23
42
  // `bundle` is the hook's file under `dist/hooks` (e.g. `pretool-guard.js`); `command` is the josh
24
43
  // subcommand the fallback runs (e.g. `pretool:guard`).
25
44
  function hook_launch_command(bundle: string, command: string): string {
26
- return launch_command(`${HOOK_DIST_DIR}/${bundle}`, `pnpm josh ${command}`)
45
+ return bundle_launch_command(bundle, `pnpm josh ${command}`)
27
46
  }
28
47
 
29
- const hook_launch = { HOOK_DIST_DIR, hook_launch_command, launch_command }
48
+ const hook_launch = {
49
+ BUNDLE_READY_GATE,
50
+ bundle_launch_command,
51
+ HOOK_DIST_DIR,
52
+ hook_launch_command,
53
+ launch_command,
54
+ PROJECT_ROOT_PREFIX,
55
+ }
30
56
 
31
57
  export { hook_launch }
@@ -1,4 +1,5 @@
1
1
  import { json_format } from '#scripts/config-merge/json-format'
2
+ import { json_value } from '#scripts/lib/json-value'
2
3
  import { z } from 'zod'
3
4
  import { init_logic } from './init-logic'
4
5
  import type { ProjectShape } from './project-profile'
@@ -6,10 +7,6 @@ import type { ProjectShape } from './project-profile'
6
7
  const KIT_PACKAGE_NAME = '@joshuafolkken/kit'
7
8
  const record_schema = z.record(z.string(), z.unknown())
8
9
 
9
- function is_record(value: unknown): value is Record<string, unknown> {
10
- return typeof value === 'object' && value !== null && !Array.isArray(value)
11
- }
12
-
13
10
  interface BasicVersions {
14
11
  kit: string
15
12
  prettier: string
@@ -23,7 +20,7 @@ function with_recorded_profile(content: string, profile: ProjectShape['profile']
23
20
  const parsed: unknown = JSON.parse(content)
24
21
  const manifest = record_schema.parse(parsed)
25
22
  const existing = manifest['josh']
26
- const josh = is_record(existing) ? existing : {}
23
+ const josh = json_value.is_record(existing) ? existing : {}
27
24
 
28
25
  manifest['josh'] = { ...josh, profile }
29
26
 
@@ -92,6 +92,10 @@ const AI_COPY_FILES: ReadonlyArray<string> = [
92
92
  '.github/workflows/production.yml',
93
93
  '.github/workflows/sonar-qube.yml',
94
94
  '.github/workflows/pr-classification.yml',
95
+ // Called by `pr-classification.yml` as `uses: ./.github/actions/setup-pnpm`; without it the
96
+ // consumer's job cannot resolve the action and the Release classification check fails
97
+ // (joshuafolkken/kit#3013).
98
+ '.github/actions/setup-pnpm/action.yml',
95
99
  '.github/pull_request_template.md',
96
100
  '.github/release.yml',
97
101
  '.github/dependabot.yml',
@@ -118,6 +122,9 @@ const AI_COPY_FILE_MAPPINGS: ReadonlyArray<FileCopyMapping> = [
118
122
  src: 'templates/workflows/dependabot-auto-merge.yml',
119
123
  dest: '.github/workflows/dependabot-auto-merge.yml',
120
124
  },
125
+ // A template rather than a copy of kit's own file: kit releases from its publish.yml, so the file
126
+ // must not run in kit as well (joshuafolkken/kit#3007).
127
+ { src: 'templates/workflows/github-release.yml', dest: '.github/workflows/github-release.yml' },
121
128
  ]
122
129
 
123
130
  // Empty since joshuafolkken/kit#1879: the five skill directories kit used to copy whole
@@ -22,7 +22,6 @@ import { init_install } from './init-install'
22
22
  import { init_logic } from './init-logic'
23
23
  import { PACKAGE_DIR, PROJECT_ROOT } from './init-paths'
24
24
  import { kit_setup_state } from './kit-setup-state'
25
- import { plugin_install_hint_module } from './plugin-install-hint'
26
25
  import { project_profile, type ProjectProfile, type ProjectShape } from './project-profile'
27
26
 
28
27
  const PACKAGE_JSON = 'package.json'
@@ -295,8 +294,6 @@ function run_ai_file_actions(shape: ProjectShape): void {
295
294
  const name_with_owner = init_ai_copy.run_ai_copies(shape)
296
295
 
297
296
  if (shape.has_github) report_repository_settings(name_with_owner)
298
-
299
- if (shape.profile === 'full') plugin_install_hint_module.report_plugin_install_hint()
300
297
  }
301
298
 
302
299
  function initialize_project(shape: ProjectShape): void {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readdirSync, readFileSync, type Dirent } from 'node:fs'
2
2
  import path from 'node:path'
3
3
  import { repo_origin } from '#scripts/discovery/repo-origin'
4
+ import { json_value } from '#scripts/lib/json-value'
4
5
  import { KIT_PACKAGE_NAME } from '#scripts/version/kit-descriptor'
5
6
  import { execaSync } from 'execa'
6
7
 
@@ -73,32 +74,26 @@ function read_manifest(root: string): Record<string, unknown> | undefined {
73
74
  if (!existsSync(filename)) return undefined
74
75
  const parsed: unknown = JSON.parse(readFileSync(filename, 'utf8'))
75
76
 
76
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
77
- throw new Error('package.json must contain an object')
78
- }
79
-
80
- return parsed as Record<string, unknown>
81
- }
77
+ if (!json_value.is_record(parsed)) throw new Error('package.json must contain an object')
82
78
 
83
- function is_record(value: unknown): value is Record<string, unknown> {
84
- return typeof value === 'object' && value !== null && !Array.isArray(value)
79
+ return parsed
85
80
  }
86
81
 
87
82
  function recorded_profile(manifest: Record<string, unknown>): ProjectProfile | undefined {
88
83
  const { josh } = manifest
89
84
 
90
- return is_record(josh) ? parse_profile(josh['profile']) : undefined
85
+ return json_value.is_record(josh) ? parse_profile(josh['profile']) : undefined
91
86
  }
92
87
 
93
88
  function has_build_script(value: unknown): boolean {
94
- return is_record(value) && ('build' in value || 'dev' in value)
89
+ return json_value.is_record(value) && ('build' in value || 'dev' in value)
95
90
  }
96
91
 
97
92
  // kit itself is not evidence of a Node toolchain: `pnpm add -D @joshuafolkken/kit` before
98
93
  // `josh init` is the documented order, and it must still leave an `index.html` site on the basic
99
94
  // profile (joshuafolkken/kit#2693).
100
95
  function has_project_dependencies(value: unknown): boolean {
101
- if (!is_record(value)) return false
96
+ if (!json_value.is_record(value)) return false
102
97
 
103
98
  return Object.keys(value).some((name) => name !== KIT_PACKAGE_NAME)
104
99
  }
@@ -1,4 +1,4 @@
1
- import { git_gh_exec } from '#scripts/git/git-gh-exec'
1
+ import { git_gh_exec } from '#scripts/gh/git-gh-exec'
2
2
  import { execaSync } from 'execa'
3
3
 
4
4
  // The three spawn shapes every `josh start` step uses, shared by `start-steps.ts`, the setup pull
@@ -23,6 +23,12 @@ function run(command: string, args: ReadonlyArray<string>, root: string): void {
23
23
  execaSync(command, args, { ...git_gh_exec.direct_environment(), cwd: root, stdio: 'inherit' })
24
24
  }
25
25
 
26
- const start_exec = { succeeds, read_output, run }
26
+ // A caller's own initialize command stands in for the in-process `josh init`, so it keeps the
27
+ // environment that setup would have had — its install goes through a scanner's proxy as usual.
28
+ function run_local(command: string, args: ReadonlyArray<string>, root: string): void {
29
+ execaSync(command, args, { cwd: root, stdio: 'inherit' })
30
+ }
31
+
32
+ const start_exec = { succeeds, read_output, run, run_local }
27
33
 
28
34
  export { start_exec }