@joshuafolkken/kit 1.1050.0 → 1.1128.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 (630) hide show
  1. package/.claude/settings.json +9 -8
  2. package/.claude/skills/epic-commands/SKILL.md +5 -7
  3. package/.claude/skills/workflow-commands/SKILL.md +20 -12
  4. package/.claude/skills/workflow-commands/background-commands.md +36 -84
  5. package/.claude/skills/workflow-commands/backlogrun-child.md +1 -1
  6. package/.claude/skills/workflow-commands/backlogrun-lanes.md +8 -6
  7. package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -33
  8. package/.claude/skills/workflow-commands/backlogrun-steps.md +12 -12
  9. package/.claude/skills/workflow-commands/backlogrun.md +2 -2
  10. package/.claude/skills/workflow-commands/chain-rule.md +4 -2
  11. package/.claude/skills/workflow-commands/delegation.md +3 -3
  12. package/.claude/skills/workflow-commands/followup-reference.md +1 -1
  13. package/.claude/skills/workflow-commands/followup.md +7 -3
  14. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -2
  15. package/.claude/skills/workflow-commands/fullrun.md +8 -6
  16. package/.claude/skills/workflow-commands/halfrun.md +19 -21
  17. package/.claude/skills/workflow-commands/issue-comments.md +1 -1
  18. package/.claude/skills/workflow-commands/issue-fold-existing.md +10 -10
  19. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  20. package/.claude/skills/workflow-commands/kickoff.md +9 -4
  21. package/.claude/skills/workflow-commands/latest-gate.md +8 -16
  22. package/.claude/skills/workflow-commands/needs-human-review.md +1 -1
  23. package/.claude/skills/workflow-commands/observation-filing.md +3 -3
  24. package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
  25. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  26. package/.claude/skills/workflow-commands/prrun.md +36 -0
  27. package/.claude/skills/workflow-commands/retrospective.md +1 -1
  28. package/.claude/skills/workflow-commands/split-assessment.md +4 -18
  29. package/.claude/skills/workflow-commands/working-tree-hold.md +22 -2
  30. package/.codex/hooks.json +16 -20
  31. package/.github/actions/setup-node/action.yml +33 -0
  32. package/.github/actions/setup-pnpm/action.yml +93 -0
  33. package/.github/dependabot.yml +5 -1
  34. package/.github/workflows/auto-tag.yml +2 -3
  35. package/.github/workflows/ci.yml +31 -134
  36. package/.github/workflows/dependabot-auto-merge.yml +1 -0
  37. package/.github/workflows/pr-classification.yml +6 -44
  38. package/.github/workflows/production.yml +1 -0
  39. package/.github/workflows/publish.yml +43 -47
  40. package/.github/workflows/sonar-qube.yml +41 -4
  41. package/.pnpmfile.mjs +29 -0
  42. package/AGENTS.md +5 -2
  43. package/CLAUDE.md +33 -27
  44. package/GEMINI.md +5 -2
  45. package/README.md +66 -20
  46. package/SECURITY.md +37 -6
  47. package/cspell/index.yaml +6 -1
  48. package/dist/CLAUDE.basic.md +6 -6
  49. package/dist/CLAUDE.md +33 -27
  50. package/dist/CLAUDE.static.md +6 -6
  51. package/dist/config-merge/index.js +48 -12
  52. package/dist/hooks/batch-guard.js +4 -4
  53. package/dist/hooks/{chunk-7VPDUBRN.js → chunk-4JZDP5X5.js} +152 -155
  54. package/dist/hooks/{chunk-HJIISEAC.js → chunk-5SD2YM7Z.js} +5 -13
  55. package/dist/hooks/{chunk-NAS4C7GP.js → chunk-77J7IEQM.js} +2 -2
  56. package/dist/hooks/{chunk-DMQN45FN.js → chunk-7HVB7SZG.js} +3 -3
  57. package/dist/hooks/{chunk-RLDX5SOC.js → chunk-AIOIOMPX.js} +4413 -1970
  58. package/dist/hooks/{chunk-5BK3K7V4.js → chunk-M7QKEKWO.js} +873 -2049
  59. package/dist/hooks/{chunk-WQ4OSBSS.js → chunk-MKVGBQPA.js} +2 -2
  60. package/dist/hooks/{chunk-EJDX2TOB.js → chunk-NXYMNPUQ.js} +7 -88
  61. package/dist/hooks/{chunk-NJQOBE6L.js → chunk-QVLT52OV.js} +2 -7
  62. package/dist/hooks/{chunk-GQ7KP45M.js → chunk-XYUIQOW3.js} +2 -2
  63. package/dist/hooks/codex-hook-adapter.js +12 -48
  64. package/dist/hooks/duplicate-read-guard.js +5 -5
  65. package/dist/hooks/format-edited.js +2 -2
  66. package/dist/hooks/inputs.json +280 -0
  67. package/dist/hooks/investigation-guard.js +5 -5
  68. package/dist/hooks/pretool-guard.js +9 -9
  69. package/dist/hooks/session-lang.js +0 -1
  70. package/dist/hooks/stop-guard.js +192 -333
  71. package/dist/josh.js +240 -148
  72. package/dist/managed-marker/index.js +2 -1
  73. package/dist/version/index.js +70 -78
  74. package/eslint/rules/promise.js +5 -0
  75. package/eslint/rules/unicorn.js +3 -2
  76. package/package.json +26 -5
  77. package/pnpm-workspace.yaml +5 -7
  78. package/prompts/collaboration-workflow/glossary.md +17 -0
  79. package/prompts/collaboration-workflow/issue-citation.md +5 -5
  80. package/prompts/collaboration-workflow/issue-template.md +1 -1
  81. package/prompts/collaboration-workflow/operating-rules.md +5 -5
  82. package/prompts/collaboration-workflow/overview.md +13 -1
  83. package/prompts/collaboration-workflow/principles.md +9 -0
  84. package/prompts/collaboration-workflow/report-format.md +1 -1
  85. package/prompts/collaboration-workflow/residency.md +3 -3
  86. package/prompts/collaboration-workflow/rule-delivery.md +1 -1
  87. package/prompts/collaboration-workflow/turn-batching.md +3 -3
  88. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  89. package/prompts/collaboration-workflow/wip-cap.md +25 -22
  90. package/prompts/collaboration-workflow.md +6 -8
  91. package/prompts/review-rubric.md +1 -1
  92. package/prompts/review.md +3 -3
  93. package/prompts/sonar-hotspot-handling.md +11 -3
  94. package/prompts/testing-guide.md +3 -3
  95. package/scripts/adopt/adopt-toolkits.ts +0 -2
  96. package/scripts/adopt/adopt.ts +0 -2
  97. package/scripts/agent/agent-argv.ts +0 -2
  98. package/scripts/agent/agent-diagnostics.ts +1 -1
  99. package/scripts/agent/agent-role-profile.ts +0 -7
  100. package/scripts/agent/claude-agent-argv.ts +0 -2
  101. package/scripts/agent/codex-agent-argv.ts +1 -1
  102. package/scripts/agent/codex-hooks.ts +92 -0
  103. package/scripts/agent/interactive-ask.ts +1 -1
  104. package/scripts/auto-ok/auto-ok-cli.ts +6 -16
  105. package/scripts/backlog/backlog-arrival.ts +30 -10
  106. package/scripts/backlog/backlog-budget-cli.ts +15 -16
  107. package/scripts/backlog/backlog-budget.ts +0 -2
  108. package/scripts/backlog/backlog-defect-priority.ts +2 -2
  109. package/scripts/backlog/backlog-drive-cli.ts +10 -8
  110. package/scripts/backlog/backlog-drive-named-offer.ts +16 -3
  111. package/scripts/backlog/backlog-drive-named.ts +2 -1
  112. package/scripts/backlog/backlog-drive.ts +76 -16
  113. package/scripts/backlog/backlog-named.ts +0 -3
  114. package/scripts/backlog/backlog-next.ts +9 -15
  115. package/scripts/backlog/backlog-offer-cli.ts +6 -19
  116. package/scripts/backlog/backlog-plan-cli.ts +1 -7
  117. package/scripts/backlog/backlog-plan.ts +30 -19
  118. package/scripts/backlog/backlog-pool.ts +3 -6
  119. package/scripts/backlog/backlog-rank.ts +2 -2
  120. package/scripts/backlog/backlog-ready.ts +39 -10
  121. package/scripts/backlog/backlog-scope.ts +1 -3
  122. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  123. package/scripts/backlog/backlog-stalled.ts +0 -1
  124. package/scripts/backlog/backlog-waves.ts +5 -2
  125. package/scripts/behavior/behavior-cli.ts +1 -1
  126. package/scripts/build/build-codex-hooks.ts +26 -0
  127. package/scripts/build/build-hooks.ts +85 -9
  128. package/scripts/build/hook-bundle-stamp.ts +111 -0
  129. package/scripts/cases/cases-cli.ts +1 -1
  130. package/scripts/cases/cases-logic.ts +1 -1
  131. package/scripts/ci/composite-actions.ts +30 -0
  132. package/scripts/ci/pr-classification.ts +3 -3
  133. package/scripts/claude/workflow-destination.ts +10 -1
  134. package/scripts/clone/clone-fingerprint.ts +0 -1
  135. package/scripts/clone/clone-scan-cli.ts +1 -1
  136. package/scripts/clone/clone-scan.ts +1 -1
  137. package/scripts/config-merge/json-format.ts +0 -1
  138. package/scripts/config-merge/parse-jsonc.ts +33 -3
  139. package/scripts/config-merge/patch-json-key.ts +13 -6
  140. package/scripts/config-merge/patch-yaml-list.ts +1 -1
  141. package/scripts/cost-runtime/codex-usage.ts +2 -9
  142. package/scripts/cost-runtime/cost-attribute.ts +0 -1
  143. package/scripts/cost-runtime/cost-cli.ts +3 -10
  144. package/scripts/cost-runtime/cost-corpus.ts +0 -2
  145. package/scripts/cost-runtime/cost-format.ts +0 -3
  146. package/scripts/cost-runtime/cost-pricing.ts +0 -1
  147. package/scripts/cost-runtime/cost-tokens.ts +0 -1
  148. package/scripts/delegation/delegation-cli.ts +1 -1
  149. package/scripts/delegation/delegation-policy.ts +0 -1
  150. package/scripts/delegation/duplicate-read-guard.ts +0 -8
  151. package/scripts/delegation/fanout-cli.ts +1 -1
  152. package/scripts/delegation/fanout.ts +0 -2
  153. package/scripts/delegation/investigation-reads.ts +1 -164
  154. package/scripts/discovery/repo-map-logic.ts +0 -1
  155. package/scripts/discovery/repo-party-cli.ts +1 -1
  156. package/scripts/doctor/doctor-consumer.ts +6 -10
  157. package/scripts/doctor/doctor-io.ts +9 -13
  158. package/scripts/doctor/doctor-logic.ts +0 -4
  159. package/scripts/doctor/doctor.ts +14 -9
  160. package/scripts/document/backlogrun-parent-read-set.ts +12 -1
  161. package/scripts/document/document-byte-budget.ts +5 -5
  162. package/scripts/document/document-byte-check.ts +0 -1
  163. package/scripts/document/document-read-cli.ts +0 -2
  164. package/scripts/document/document-section-cli.ts +1 -1
  165. package/scripts/document/document-section.ts +0 -3
  166. package/scripts/document/edit-files-cli.ts +0 -1
  167. package/scripts/document/entry-read-budget.ts +21 -7
  168. package/scripts/document/entry-read-set.ts +32 -8
  169. package/scripts/document/lane-child-read-set.ts +8 -6
  170. package/scripts/document/read-files-cli.ts +0 -4
  171. package/scripts/document/read-set-cli.ts +0 -2
  172. package/scripts/document/read-set-point-of-use.ts +9 -3
  173. package/scripts/document/read-set-pricing.ts +0 -1
  174. package/scripts/document/read-set-trim.ts +4 -2
  175. package/scripts/epic/epic-add.ts +1 -1
  176. package/scripts/epic/epic-audit-cli.ts +1 -4
  177. package/scripts/epic/epic-audit-orphans.ts +1 -3
  178. package/scripts/epic/epic-audit-rationale.ts +0 -1
  179. package/scripts/epic/epic-audit-report.ts +0 -2
  180. package/scripts/epic/epic-audit.ts +8 -22
  181. package/scripts/epic/epic-bundle-cli.ts +1 -1
  182. package/scripts/epic/epic-bundle-evidence.ts +0 -1
  183. package/scripts/epic/epic-bundle-referenced.ts +1 -5
  184. package/scripts/epic/epic-bundle.ts +0 -4
  185. package/scripts/epic/epic-busy.ts +4 -5
  186. package/scripts/epic/epic-candidate-confirm.ts +0 -5
  187. package/scripts/epic/epic-classify.ts +2 -2
  188. package/scripts/epic/epic-cli-argv.ts +0 -1
  189. package/scripts/epic/epic-close-comment.ts +1 -1
  190. package/scripts/epic/epic-close.ts +5 -2
  191. package/scripts/epic/epic-cross-repo.ts +2 -4
  192. package/scripts/epic/epic-decision.ts +0 -1
  193. package/scripts/epic/epic-fetch.ts +2 -2
  194. package/scripts/epic/epic-index.ts +2 -3
  195. package/scripts/epic/epic-issue.ts +1 -15
  196. package/scripts/epic/epic-lane-offer.ts +9 -8
  197. package/scripts/epic/epic-nested.ts +1 -1
  198. package/scripts/epic/epic-next-read.ts +1 -3
  199. package/scripts/epic/epic-next-views.ts +0 -1
  200. package/scripts/epic/epic-next.ts +1 -10
  201. package/scripts/epic/epic-parse.ts +0 -1
  202. package/scripts/epic/epic-promote.ts +0 -9
  203. package/scripts/epic/epic-rank.ts +2 -2
  204. package/scripts/epic/epic-read.ts +1 -2
  205. package/scripts/epic/epic-reconcile.ts +1 -1
  206. package/scripts/epic/epic-reference.ts +1 -13
  207. package/scripts/epic/epic-relation-recheck.ts +1 -1
  208. package/scripts/epic/epic-relations.ts +1 -1
  209. package/scripts/epic/epic-remove.ts +1 -1
  210. package/scripts/epic/epic-report.ts +0 -1
  211. package/scripts/epic/epic-run.ts +2 -3
  212. package/scripts/epic/epic-shape.ts +1 -1
  213. package/scripts/epic/epic-solo-stale.ts +107 -0
  214. package/scripts/epic/epic-solo.ts +3 -2
  215. package/scripts/epic/epic-triage.ts +1 -1
  216. package/scripts/epic/epic-validate.ts +1 -1
  217. package/scripts/epic/epic.ts +1 -1
  218. package/scripts/exports/unused-members-cli.ts +53 -0
  219. package/scripts/{git → followup}/followup-issue-number.ts +1 -6
  220. package/scripts/{git → followup}/git-followup-cleanup.ts +2 -1
  221. package/scripts/{git → followup}/git-followup-finish.ts +1 -3
  222. package/scripts/{git → followup}/git-followup-flush.ts +2 -2
  223. package/scripts/{git → followup}/git-followup-issue-close.ts +2 -2
  224. package/scripts/{git → followup}/git-followup-label.ts +4 -4
  225. package/scripts/followup/git-followup-merged.ts +112 -0
  226. package/scripts/{git → followup}/git-followup-pending.ts +1 -1
  227. package/scripts/{git → followup}/git-followup-workflow.ts +19 -11
  228. package/scripts/{git → followup}/git-pr-followup-wrapup.ts +9 -3
  229. package/scripts/{git → followup}/git-pr-followup.ts +53 -16
  230. package/scripts/gate/core-budget.ts +3 -9
  231. package/scripts/gate/gate-plan.ts +7 -4
  232. package/scripts/gate/gate-report.ts +0 -6
  233. package/scripts/gate/gate-tree.ts +1 -1
  234. package/scripts/gate/pre-commit-type-check.ts +0 -6
  235. package/scripts/gate/project-checks.ts +7 -8
  236. package/scripts/gate/scoped-green.ts +0 -1
  237. package/scripts/gate/type-check-step.ts +1 -1
  238. package/scripts/gate/verification-gate.ts +0 -2
  239. package/scripts/gh/fix-gh-packages.ts +15 -12
  240. package/scripts/gh/gh-spawn.ts +2 -2
  241. package/scripts/{git → gh}/git-ai-review-scan.ts +1 -5
  242. package/scripts/{git → gh}/git-gh-check.ts +2 -1
  243. package/scripts/{git → gh}/git-gh-exec.ts +1 -1
  244. package/scripts/{git → gh}/git-gh-failure.ts +1 -1
  245. package/scripts/{git → gh}/git-gh-helpers.ts +0 -1
  246. package/scripts/{git → gh}/git-gh-issue-list.ts +13 -10
  247. package/scripts/{git → gh}/git-gh-issue-read.ts +1 -1
  248. package/scripts/{git → gh}/git-gh-issue-rest.ts +6 -3
  249. package/scripts/{git → gh}/git-gh-issue-write.ts +4 -1
  250. package/scripts/{git → gh}/git-gh-pr-auto-merge.ts +4 -1
  251. package/scripts/{git → gh}/git-gh-pr-read.ts +24 -0
  252. package/scripts/{git → gh}/git-gh-pr-rest.ts +5 -2
  253. package/scripts/{git → gh}/git-gh-pr-review.ts +2 -2
  254. package/scripts/{git → gh}/git-gh-pr-rollup.ts +1 -3
  255. package/scripts/{git → gh}/git-gh-pr.ts +3 -1
  256. package/scripts/{git → gh}/git-pr-ai-review.ts +2 -5
  257. package/scripts/{git → gh}/git-pr-checks-eval.ts +0 -10
  258. package/scripts/{git → gh}/git-pr-checks-parse.ts +4 -20
  259. package/scripts/{git → gh}/git-pr-checks.ts +2 -1
  260. package/scripts/{git → gh}/git-pr-coderabbit.ts +2 -5
  261. package/scripts/{git → gh}/git-pr-confirmation.ts +1 -3
  262. package/scripts/{git → gh}/git-pr-managed-config.ts +2 -3
  263. package/scripts/{git → gh}/git-pr.ts +3 -3
  264. package/scripts/git/changed-file-scope.ts +0 -1
  265. package/scripts/git/git-branch.ts +3 -6
  266. package/scripts/git/git-command.ts +6 -1
  267. package/scripts/git/git-commit.ts +1 -1
  268. package/scripts/git/git-common-directory.ts +80 -24
  269. package/scripts/git/git-error.ts +66 -9
  270. package/scripts/git/git-issue.ts +1 -1
  271. package/scripts/git/git-preflight.ts +2 -2
  272. package/scripts/git/git-prompt.ts +6 -31
  273. package/scripts/git/git-push.ts +1 -1
  274. package/scripts/git/git-spawn-sync.ts +65 -0
  275. package/scripts/git/git-spawn.ts +5 -4
  276. package/scripts/git/git-staging.ts +1 -0
  277. package/scripts/git/git-status.ts +1 -1
  278. package/scripts/git/git-workflow.ts +1 -1
  279. package/scripts/git/gone-branch.ts +1 -0
  280. package/scripts/git/listing-cutoff.ts +1 -1
  281. package/scripts/git/main-merge.ts +1 -1
  282. package/scripts/git/main-sync.ts +19 -1
  283. package/scripts/git/parse-json-array.ts +0 -3
  284. package/scripts/git/repository-lock.ts +16 -16
  285. package/scripts/git/{git-stash.ts → stash/git-stash.ts} +1 -1
  286. package/scripts/git/{stash-pop-args.ts → stash/stash-pop-args.ts} +2 -7
  287. package/scripts/git/{stash-pop-cli.ts → stash/stash-pop-cli.ts} +0 -1
  288. package/scripts/git/{stash-sweep-lock.ts → stash/stash-sweep-lock.ts} +3 -3
  289. package/scripts/hooks/codex-hook-adapter.ts +0 -1
  290. package/scripts/hooks/edited-cspell.ts +1 -1
  291. package/scripts/hooks/format-edited-file.ts +2 -0
  292. package/scripts/hooks/pre-push-unit.ts +0 -4
  293. package/scripts/hooks/pretool-guard.ts +22 -4
  294. package/scripts/hooks/reserved-run.ts +1 -1
  295. package/scripts/hooks/step-zero-notice.ts +76 -0
  296. package/scripts/hooks/stop-guard.ts +47 -21
  297. package/scripts/init/claude-plugin-config.ts +4 -5
  298. package/scripts/init/hook-command-rewrite.ts +16 -8
  299. package/scripts/init/hook-launch.ts +41 -15
  300. package/scripts/init/init-basic.ts +2 -5
  301. package/scripts/init/init-file-action.ts +54 -0
  302. package/scripts/init/init-logic-migrate.ts +35 -17
  303. package/scripts/init/init-logic-secretlint.ts +1 -1
  304. package/scripts/init/init-logic.ts +10 -19
  305. package/scripts/init/init.ts +11 -71
  306. package/scripts/init/kit-written-paths.ts +2 -0
  307. package/scripts/init/project-profile.ts +9 -17
  308. package/scripts/init/start-exec.ts +1 -1
  309. package/scripts/init/start-setup-pr.ts +6 -6
  310. package/scripts/init/start-steps.ts +3 -1
  311. package/scripts/issue/behavior-change-lint.ts +0 -1
  312. package/scripts/issue/defect-rate-cli.ts +64 -23
  313. package/scripts/issue/defect-rate.ts +1 -2
  314. package/scripts/{git → issue}/git-next-issues.ts +3 -3
  315. package/scripts/issue/issue-backlinks-cli.ts +1 -5
  316. package/scripts/issue/issue-backlinks.ts +7 -14
  317. package/scripts/issue/issue-cite-cli.ts +2 -6
  318. package/scripts/issue/issue-cite.ts +12 -8
  319. package/scripts/issue/issue-classification.ts +1 -9
  320. package/scripts/issue/issue-closing-pr.ts +4 -4
  321. package/scripts/issue/issue-comment-cli.ts +2 -2
  322. package/scripts/issue/issue-file-cli.ts +5 -5
  323. package/scripts/issue/issue-file.ts +7 -14
  324. package/scripts/issue/issue-fold-cli.ts +1 -1
  325. package/scripts/issue/issue-fold-existing-cli.ts +1 -1
  326. package/scripts/{git → issue}/issue-labels.ts +8 -0
  327. package/scripts/issue/issue-lint-cli.ts +1 -1
  328. package/scripts/issue/issue-merged.ts +2 -2
  329. package/scripts/{git → issue}/issue-rank.ts +1 -1
  330. package/scripts/issue/issue-read-cli.ts +8 -47
  331. package/scripts/issue/issue-read.ts +0 -2
  332. package/scripts/issue/issue-report-failures.ts +80 -0
  333. package/scripts/issue/issue-scout-cli.ts +1 -7
  334. package/scripts/issue/issue-scout-closed.ts +2 -2
  335. package/scripts/issue/issue-scout.ts +0 -2
  336. package/scripts/issue/issue-state-cli.ts +5 -46
  337. package/scripts/issue/issue-state.ts +1 -5
  338. package/scripts/issue/markdown-section.ts +21 -6
  339. package/scripts/issue/measure-rerun-cli.ts +74 -15
  340. package/scripts/issue/reproduction-measure.ts +1 -1
  341. package/scripts/josh/cli-body.ts +1 -1
  342. package/scripts/josh/file-map-stamp.ts +0 -3
  343. package/scripts/josh/josh-command-map.ts +2 -0
  344. package/scripts/josh/josh-commands-ai.ts +9 -102
  345. package/scripts/josh/josh-commands-backlog.ts +5 -1
  346. package/scripts/josh/josh-commands-development.ts +8 -1
  347. package/scripts/josh/josh-commands-issue.ts +2 -2
  348. package/scripts/josh/josh-commands-maintenance.ts +7 -0
  349. package/scripts/josh/josh-commands-project.ts +7 -1
  350. package/scripts/josh/josh-commands-run.ts +114 -0
  351. package/scripts/josh/josh-commands-versioning.ts +8 -0
  352. package/scripts/josh/josh-commands-workflow.ts +3 -3
  353. package/scripts/josh/path-decision.ts +0 -3
  354. package/scripts/josh/process-owner.ts +16 -0
  355. package/scripts/josh/run-github-prerequisite.ts +3 -6
  356. package/scripts/josh/session-language-cli.ts +0 -1
  357. package/scripts/lane/lane-await.ts +46 -19
  358. package/scripts/lane/lane-capacity.ts +6 -3
  359. package/scripts/lane/lane-child-invocation.ts +20 -7
  360. package/scripts/lane/lane-cli.ts +1 -4
  361. package/scripts/lane/lane-close.ts +5 -4
  362. package/scripts/lane/lane-dispatch.ts +3 -4
  363. package/scripts/lane/lane-environment.ts +0 -9
  364. package/scripts/lane/lane-handoff.ts +38 -0
  365. package/scripts/lane/lane-launch-cli.ts +31 -27
  366. package/scripts/lane/lane-open.ts +0 -3
  367. package/scripts/lane/lane-output.ts +0 -1
  368. package/scripts/lane/lane-registry.ts +9 -0
  369. package/scripts/lane/openai-lane-ship.ts +2 -0
  370. package/scripts/lane/openai-lane-supervisor-decision.ts +2 -1
  371. package/scripts/lane/openai-lane-supervisor.ts +6 -4
  372. package/scripts/lane/openai-review-broker.ts +7 -1
  373. package/scripts/lib/bounded-pool.ts +1 -0
  374. package/scripts/lib/cli-flags.ts +31 -2
  375. package/scripts/lib/error-message.ts +12 -1
  376. package/scripts/lib/json-value.ts +5 -4
  377. package/scripts/lib/poll.ts +3 -1
  378. package/scripts/lib/timed-fetch.ts +15 -0
  379. package/scripts/lib/timeouts.ts +13 -0
  380. package/scripts/{yaml → lib}/yaml-document.ts +3 -1
  381. package/scripts/{bytes → lines}/bytes-command.ts +0 -2
  382. package/scripts/lines/line-targets.ts +0 -11
  383. package/scripts/lines/lines-command.ts +5 -4
  384. package/scripts/lint/lint-related-scope.ts +0 -1
  385. package/scripts/lint/lint-related.ts +1 -1
  386. package/scripts/{git → notify}/telegram-notify.ts +2 -2
  387. package/scripts/{git → notify}/telegram-test.ts +4 -4
  388. package/scripts/observations/observation-ledger-home.ts +0 -1
  389. package/scripts/observations/observation-ledger-line.ts +0 -1
  390. package/scripts/observations/observation-ledger-migrate.ts +1 -1
  391. package/scripts/observations/observations-flush-landing.ts +3 -3
  392. package/scripts/observations/observations-flush.ts +2 -2
  393. package/scripts/overrides/lockfile-overrides.ts +1 -1
  394. package/scripts/overrides/overrides-logic.ts +1 -1
  395. package/scripts/package/package-scout-cli.ts +2 -7
  396. package/scripts/package/package-scout.ts +0 -2
  397. package/scripts/propagate/propagate-git-failure.ts +0 -1
  398. package/scripts/propagate/propagate-git.ts +3 -10
  399. package/scripts/propagate/propagate-publish.ts +2 -3
  400. package/scripts/propagate/propagate-run.ts +0 -2
  401. package/scripts/propagate/propagate-steps.ts +3 -5
  402. package/scripts/propagate/propagate-targets.ts +0 -4
  403. package/scripts/propagate/propagate.ts +0 -4
  404. package/scripts/refactor/import-graph.ts +0 -1
  405. package/scripts/refactor/refactor-lint.ts +0 -2
  406. package/scripts/refactor/refactor-scan.ts +2 -2
  407. package/scripts/refactor/refactor-targets.ts +0 -1
  408. package/scripts/registry-migration/migrate-logic.ts +28 -21
  409. package/scripts/registry-migration/migrate.ts +25 -14
  410. package/scripts/release/github-release-cli.ts +19 -6
  411. package/scripts/release/github-release-environment.ts +46 -0
  412. package/scripts/release/github-release.ts +151 -94
  413. package/scripts/release/release-history.ts +0 -1
  414. package/scripts/release/release-plan.ts +0 -1
  415. package/scripts/release/release-publish.ts +38 -5
  416. package/scripts/release/release-scope-cli.ts +1 -2
  417. package/scripts/release/release-tag.ts +2 -4
  418. package/scripts/release/release-worktree.ts +97 -2
  419. package/scripts/repo/auto-merge-setting-logic.ts +1 -4
  420. package/scripts/repo/repo-setting.ts +1 -1
  421. package/scripts/repo/repository-labels.ts +3 -3
  422. package/scripts/repo/required-checks-logic.ts +206 -0
  423. package/scripts/repo/required-checks-report.ts +71 -0
  424. package/scripts/repo/ruleset-check-cli.ts +53 -0
  425. package/scripts/repo/ruleset-check.ts +147 -0
  426. package/scripts/report/report-lint-cli.ts +0 -4
  427. package/scripts/review/disposition-cli.ts +1 -1
  428. package/scripts/review/live-evidence.ts +1 -1
  429. package/scripts/review/review-attest-cli.ts +0 -6
  430. package/scripts/review/review-attest.ts +4 -16
  431. package/scripts/review/review-brief-cli.ts +23 -6
  432. package/scripts/review/review-brief.ts +21 -8
  433. package/scripts/review/review-checkout.ts +3 -2
  434. package/scripts/review/review-diff-parts.ts +89 -0
  435. package/scripts/review/review-diff-write.ts +97 -0
  436. package/scripts/review/review-finding-ledger.ts +0 -1
  437. package/scripts/review/review-record-cli.ts +10 -17
  438. package/scripts/review/review-record.ts +0 -1
  439. package/scripts/review/review-round2-cli.ts +1 -1
  440. package/scripts/review/review-stamps.ts +0 -3
  441. package/scripts/review/review-tree.ts +14 -2
  442. package/scripts/rules/decision-oracle-stage.ts +18 -0
  443. package/scripts/rules/decision-oracle.ts +10 -7
  444. package/scripts/rules/delivered-rules.ts +9 -1
  445. package/scripts/rules/destructive-command.ts +119 -0
  446. package/scripts/rules/direct-filing.ts +1 -1
  447. package/scripts/rules/early-heartbeat.ts +4 -3
  448. package/scripts/rules/file-body.ts +0 -3
  449. package/scripts/rules/filing-cap.ts +0 -2
  450. package/scripts/rules/gh-api.ts +7 -2
  451. package/scripts/rules/git-argv.ts +18 -7
  452. package/scripts/rules/git-force.ts +0 -2
  453. package/scripts/rules/implementation-cut.ts +0 -1
  454. package/scripts/rules/index-guard.ts +71 -0
  455. package/scripts/rules/issue-citation.ts +50 -25
  456. package/scripts/rules/lane-background.ts +15 -7
  457. package/scripts/rules/lane-park.ts +22 -6
  458. package/scripts/rules/last-prompt.ts +1 -0
  459. package/scripts/rules/oracle-consulted.ts +1 -1
  460. package/scripts/rules/oracle-firing.ts +1 -0
  461. package/scripts/rules/oracle-list-cli.ts +0 -3
  462. package/scripts/rules/permission-guards.ts +14 -0
  463. package/scripts/rules/protected-files.ts +74 -0
  464. package/scripts/rules/reply-language.ts +1 -1
  465. package/scripts/rules/rule-body-guard.ts +0 -3
  466. package/scripts/rules/rule-value-cli.ts +0 -1
  467. package/scripts/rules/shell-body-trigger.ts +0 -1
  468. package/scripts/rules/shell-segments.ts +0 -1
  469. package/scripts/rules/stop-rules.ts +20 -9
  470. package/scripts/rules/tail-commands.ts +1 -1
  471. package/scripts/rules/test-declared-commit.ts +1 -1
  472. package/scripts/rules/third-party-write.ts +0 -1
  473. package/scripts/rules/worktree-guard.ts +26 -9
  474. package/scripts/run/run-carry-args.ts +8 -12
  475. package/scripts/run/run-carry-cli.ts +0 -2
  476. package/scripts/run/run-carry-stash.ts +2 -2
  477. package/scripts/run/run-carry-streak.ts +1 -1
  478. package/scripts/run/run-cut-args.ts +1 -6
  479. package/scripts/run/run-cut-cli.ts +0 -7
  480. package/scripts/run/run-cut.ts +8 -54
  481. package/scripts/run/run-ending-cli.ts +2 -6
  482. package/scripts/run/run-ending.ts +1 -4
  483. package/scripts/run/run-entry-cli.ts +133 -25
  484. package/scripts/run/run-entry-stop.ts +104 -0
  485. package/scripts/run/run-entry.ts +0 -1
  486. package/scripts/run/run-event-cli.ts +0 -2
  487. package/scripts/run/run-event-follow.ts +1 -0
  488. package/scripts/run/run-event-render.ts +1 -1
  489. package/scripts/run/run-event-scope.ts +53 -4
  490. package/scripts/run/run-event-stream-emit.ts +7 -2
  491. package/scripts/run/run-event-stream.ts +0 -3
  492. package/scripts/run/run-event-watch.ts +1 -0
  493. package/scripts/run/run-halfrun-resume.ts +20 -7
  494. package/scripts/run/run-headless.ts +2 -2
  495. package/scripts/run/run-hold-cli.ts +33 -10
  496. package/scripts/run/run-hold.ts +29 -6
  497. package/scripts/run/run-issue-number.ts +1 -1
  498. package/scripts/run/run-liveness-cli.ts +2 -6
  499. package/scripts/run/run-liveness.ts +2 -3
  500. package/scripts/run/run-merge-cli.ts +7 -23
  501. package/scripts/run/run-merge-steps.ts +5 -5
  502. package/scripts/run/run-merge.ts +1 -1
  503. package/scripts/run/run-next-cli.ts +1 -1
  504. package/scripts/run/run-parent-cut-hook.ts +4 -2
  505. package/scripts/run/run-preflight.ts +2 -9
  506. package/scripts/run/run-prep-cli.ts +6 -3
  507. package/scripts/run/run-prep-locate.ts +1 -1
  508. package/scripts/run/run-progress-args.ts +20 -4
  509. package/scripts/run/run-progress-cli.ts +35 -36
  510. package/scripts/run/run-progress-clock.ts +10 -35
  511. package/scripts/run/run-progress-config.ts +0 -4
  512. package/scripts/run/run-progress-read.ts +1 -2
  513. package/scripts/run/run-progress.ts +1 -2
  514. package/scripts/run/run-prrun-resume.ts +117 -0
  515. package/scripts/run/run-report-cli.ts +1 -2
  516. package/scripts/run/run-report.ts +0 -1
  517. package/scripts/run/run-retrospective.ts +1 -1
  518. package/scripts/run/run-review-cli.ts +0 -3
  519. package/scripts/run/run-review-steps.ts +2 -0
  520. package/scripts/run/run-review.ts +0 -2
  521. package/scripts/run/run-ship-cli.ts +29 -19
  522. package/scripts/run/run-ship-detach.ts +1 -0
  523. package/scripts/run/run-ship-next.ts +106 -0
  524. package/scripts/run/run-ship-preflight.ts +1 -1
  525. package/scripts/run/run-ship-probe.ts +4 -1
  526. package/scripts/run/run-ship-return.ts +8 -3
  527. package/scripts/run/run-ship-review-steps.ts +18 -4
  528. package/scripts/run/run-ship-review.ts +11 -0
  529. package/scripts/run/run-ship-scoped.ts +1 -0
  530. package/scripts/run/run-ship-steps.ts +1 -1
  531. package/scripts/run/run-ship.ts +0 -1
  532. package/scripts/run/run-stage-read.ts +62 -0
  533. package/scripts/run/run-stage.ts +160 -0
  534. package/scripts/run/run-status-cli.ts +3 -16
  535. package/scripts/run/run-step-cli.ts +21 -14
  536. package/scripts/run/run-step.ts +1 -1
  537. package/scripts/run/run-stop-notify.ts +1 -3
  538. package/scripts/run/run-stranded-cli.ts +0 -4
  539. package/scripts/run/run-stranded-detect.ts +2 -2
  540. package/scripts/run/run-tail-cli.ts +17 -11
  541. package/scripts/run/run-tail.ts +2 -0
  542. package/scripts/run/run-tidy-lanes.ts +6 -2
  543. package/scripts/run/run-tidy-stashes.ts +12 -12
  544. package/scripts/run/run-wake-cli.ts +4 -13
  545. package/scripts/run/run-wake-loop.ts +2 -2
  546. package/scripts/run/run-wake.ts +2 -4
  547. package/scripts/run/run-watcher-guard-cli.ts +0 -4
  548. package/scripts/run/run-watcher-guard.ts +18 -12
  549. package/scripts/run/run-watcher-hook.ts +1 -1
  550. package/scripts/safe-chain/ci-installer-pin.ts +11 -5
  551. package/scripts/safe-chain/pack-hook.ts +92 -0
  552. package/scripts/safe-chain/project-config.ts +1 -1
  553. package/scripts/security/security-audit-provision-logic.ts +0 -1
  554. package/scripts/security/security-audit-provision.ts +3 -2
  555. package/scripts/security/security-audit.ts +0 -1
  556. package/scripts/security/security-updates-logic.ts +0 -1
  557. package/scripts/sonar/sonar-hotspots-cli.ts +10 -52
  558. package/scripts/sonar/sonar-hotspots.ts +0 -1
  559. package/scripts/sonar/sonar-new-code-cli.ts +150 -0
  560. package/scripts/sonar/sonar-new-code.ts +79 -0
  561. package/scripts/sonar/sonar-project.ts +97 -0
  562. package/scripts/split/split-assess-cli.ts +1 -1
  563. package/scripts/split/split-assess.ts +2 -5
  564. package/scripts/sync/managed-config-scope-cli.ts +1 -1
  565. package/scripts/sync/skill-migration.ts +0 -1
  566. package/scripts/sync/sync-ai-files.ts +4 -0
  567. package/scripts/sync/sync-hook-safety.ts +2 -3
  568. package/scripts/sync/sync.ts +1 -8
  569. package/scripts/sync/template-source-logic.ts +0 -1
  570. package/scripts/sync/workflow-pin-logic.ts +16 -28
  571. package/scripts/test/test-declared-changed.ts +13 -16
  572. package/scripts/test/test-declared-logic.ts +0 -1
  573. package/scripts/test/test-declared.ts +3 -9
  574. package/scripts/test/test-red-commit.ts +2 -2
  575. package/scripts/test/test-red.ts +14 -7
  576. package/scripts/test/test-type-logic.ts +1 -1
  577. package/scripts/test/unit-worker-share.ts +7 -16
  578. package/scripts/time-runtime/time-batch-guard.ts +1 -2
  579. package/scripts/time-runtime/time-bundle-call.ts +0 -1
  580. package/scripts/time-runtime/time-bundles.ts +0 -4
  581. package/scripts/time-runtime/time-density-hook.ts +0 -2
  582. package/scripts/time-runtime/time-followup-stage.ts +1 -1
  583. package/scripts/time-runtime/time-format.ts +0 -73
  584. package/scripts/time-runtime/time-round-trips.ts +0 -4
  585. package/scripts/time-runtime/time-single-check.ts +1 -1
  586. package/scripts/time-runtime/time-spans.ts +1 -1
  587. package/scripts/time-runtime/time-transcript-line.ts +1 -2
  588. package/scripts/ui/ui-routes-cli.ts +0 -5
  589. package/scripts/ui/ui-routes.ts +0 -5
  590. package/scripts/version/effective-upstream.ts +2 -2
  591. package/scripts/version/latest-corepack.ts +7 -9
  592. package/scripts/version/latest-scope-cli.ts +0 -2
  593. package/scripts/version/latest-update.ts +0 -2
  594. package/scripts/version/npm-registry.ts +2 -2
  595. package/scripts/version/publishable-range-check.ts +1 -1
  596. package/scripts/version/release-hold.ts +0 -4
  597. package/scripts/version/running-binary.ts +2 -2
  598. package/scripts/version/version-check-logic.ts +0 -2
  599. package/scripts/version/version-commands.ts +11 -1
  600. package/scripts/version/version-remote.ts +3 -3
  601. package/scripts/version/version-targets.ts +4 -4
  602. package/templates/CLAUDE.basic.md +6 -6
  603. package/templates/sonar-project.properties +28 -1
  604. package/templates/workflows/ci.yml +16 -82
  605. package/templates/workflows/dependabot-auto-merge.yml +6 -3
  606. package/templates/workflows/github-release.yml +79 -0
  607. package/tsconfig.sonar.json +4 -22
  608. package/prompts/collaboration-workflow/cross-repo-epic.md +0 -5
  609. package/prompts/collaboration-workflow/epic-audit.md +0 -5
  610. package/prompts/collaboration-workflow/epic-bundle.md +0 -5
  611. package/scripts/init/plugin-install-hint.ts +0 -47
  612. package/scripts/version/parse-json.ts +0 -13
  613. /package/scripts/{git → followup}/git-followup-stages.ts +0 -0
  614. /package/scripts/{git → gh}/git-gh-api-path.ts +0 -0
  615. /package/scripts/{git → gh}/git-gh-command.ts +0 -0
  616. /package/scripts/{git → gh}/git-gh-issue.ts +0 -0
  617. /package/scripts/{git → gh}/git-gh-pr-snapshot.ts +0 -0
  618. /package/scripts/{git → gh}/git-gh-reachability.ts +0 -0
  619. /package/scripts/{git → gh}/git-gh-repo.ts +0 -0
  620. /package/scripts/{git → gh}/git-gh-rest-state.ts +0 -0
  621. /package/scripts/{git → gh}/git-pr-checks-watch.ts +0 -0
  622. /package/scripts/{git → gh}/git-pr-error.ts +0 -0
  623. /package/scripts/{git → gh}/git-pr-messages.ts +0 -0
  624. /package/scripts/{git → gh}/github-issue-url.ts +0 -0
  625. /package/scripts/git/{stash-orphans.ts → stash/stash-orphans.ts} +0 -0
  626. /package/scripts/{git → issue}/issue-reference.ts +0 -0
  627. /package/scripts/{git → lib}/animation-helpers.ts +0 -0
  628. /package/scripts/{git → lib}/git-animation.ts +0 -0
  629. /package/scripts/{git → notify}/git-notify.ts +0 -0
  630. /package/scripts/{git → notify}/telegram-test-logic.ts +0 -0
@@ -1,6 +1,7 @@
1
1
  import { agent_role_profile, type AgentProfile } from '#scripts/agent/agent-role-profile'
2
2
  import { file_map_stamp, type FileMapStamp } from '#scripts/josh/file-map-stamp'
3
3
  import { review_checkout, type ReviewCheckout } from './review-checkout'
4
+ import { review_diff_parts, type DiffParts } from './review-diff-parts'
4
5
 
5
6
  // The text `josh review:brief` prints — the `/code-review` invocation, composed from what the run
6
7
  // already knows (joshuafolkken/kit#1241).
@@ -56,7 +57,7 @@ function attest_line(nonce: string): string {
56
57
  const RUBRIC_RELATIVE_PATH = 'prompts/review-rubric.md'
57
58
 
58
59
  function rubric_line(rubric_path: string): string {
59
- return `Rubric: read \`${rubric_path}\` first and apply it — it is the severity tests, the round output format, the nine categories and the stop conditions this review is scored against. You do not carry them otherwise, so a review that skips it is scored on the wrong rules.`
60
+ return `Rubric: read \`${rubric_path}\` first, with the Read tool rather than \`cat\` — it is larger than the Bash output cap, so a Bash print comes back truncated (joshuafolkken/kit#2963) — and apply it: it is the severity tests, the round output format, the nine categories and the stop conditions this review is scored against. You do not carry them otherwise, so a review that skips it is scored on the wrong rules.`
60
61
  }
61
62
 
62
63
  function checkout_block(checkout: ReviewCheckout, nonce: string): string {
@@ -413,6 +414,9 @@ interface BriefInput {
413
414
  // The tree a finished, clean review of this branch was briefed on, so round 1 is a resume
414
415
  // (joshuafolkken/kit#2945). Absent, round 1 reads the whole change.
415
416
  resumed_from?: FileMapStamp | undefined
417
+ // The change written as parts under the Bash output cap (joshuafolkken/kit#2963). Absent when the
418
+ // write failed, and the target line alone then names the change.
419
+ diff?: { parts: DiffParts; cap: number } | undefined
416
420
  }
417
421
 
418
422
  const SECOND_ROUND = 2
@@ -431,10 +435,24 @@ function target_block(input: BriefInput): string {
431
435
  return round_two_block(input.stamps.round_one, input.tree, input.checkout.root, input.base)
432
436
  }
433
437
 
438
+ // **Only beside a whole-change target** (joshuafolkken/kit#2963): each part is a path's diff against
439
+ // the base, so beside a narrowed round 2 or a resumed round 1 the list would hand the reviewer the
440
+ // code that narrowing exists to keep out of scope. Decided by the target actually printed, not by
441
+ // the round: every round that cannot narrow widens through `whole_change_target`, and that round
442
+ // needs the parts as much as a first one does.
443
+ function diff_block(input: BriefInput, target: string): ReadonlyArray<string> {
444
+ const is_whole_change = target.includes(whole_change_target(input.checkout.root, input.base))
445
+
446
+ if (!is_whole_change || input.diff === undefined) return []
447
+
448
+ return ['', review_diff_parts.reading_block(input.diff.parts, input.diff.cap)]
449
+ }
450
+
434
451
  // The level alone on the first line, because the level-only mode's contract — a caller reading the
435
452
  // answer with `$(...)` — is the one thing a brief must not break.
436
453
  function compose(input: BriefInput): string {
437
454
  const profile = input.profile ?? agent_role_profile.DEFAULT_PROFILES.reviewer
455
+ const target = target_block(input)
438
456
 
439
457
  return [
440
458
  input.level,
@@ -448,29 +466,24 @@ function compose(input: BriefInput): string {
448
466
  gate_line(input.stamps, input.tree, input.base),
449
467
  TEST_COMMAND_LINE,
450
468
  '',
451
- target_block(input),
469
+ target,
470
+ ...diff_block(input, target),
452
471
  ].join('\n')
453
472
  }
454
473
 
455
474
  const review_brief = {
456
475
  attest_line,
457
476
  BASE_MOVED_PREFIX,
458
- base_moved_line,
459
477
  carried_line,
460
- checkout_block,
461
478
  checkout_warning,
462
479
  compose,
463
480
  dropped_line,
464
481
  EMPTY_DELTA_LINE,
465
482
  gate_line,
466
- green_stamp,
467
- in_flight_line,
468
- live_marker,
469
483
  RESUMED_HEADING,
470
484
  matching_stamp,
471
485
  no_snapshot_line,
472
486
  NOTHING_LEFT_LINE,
473
- NOT_VERIFIED_LINE,
474
487
  rubric_line,
475
488
  RUBRIC_RELATIVE_PATH,
476
489
  ROUND_TWO_HEADING,
@@ -21,8 +21,9 @@ interface ReviewCheckout {
21
21
  head: string
22
22
  }
23
23
 
24
- // Three git spawns that do not depend on each other, so they are issued together rather than one
25
- // after another: the brief is printed in front of a review the run is waiting on.
24
+ // Three git reads that do not depend on each other, so they are issued together rather than one after
25
+ // another: the brief is printed in front of a review the run is waiting on. The root is a near-instant
26
+ // synchronous `rev-parse` (joshuafolkken/kit#3065); the branch and head still overlap.
26
27
  async function read_checkout(): Promise<ReviewCheckout> {
27
28
  const [root, branch, head] = await Promise.all([
28
29
  git_command.repository_root(),
@@ -0,0 +1,89 @@
1
+ // The change handed to the reviewer as files no larger than the Bash output cap
2
+ // (joshuafolkken/kit#2963). The brief used to name the whole change as one `git diff` and the rubric
3
+ // as one path, and the forked reviewer printed both through Bash: every print past
4
+ // `BASH_MAX_OUTPUT_LENGTH` came back as a truncated preview plus a saved file it then re-read, and both
5
+ // stayed in the cached prefix of every later request — 120 overflows and 68 re-reads in one review.
6
+ //
7
+ // **The diff is split, not shortened.** Each changed path gets its own parts, every part fits under the
8
+ // cap, and the parts of one path concatenate back to exactly its diff — so the scope a review covers
9
+ // is the one `git diff` lists, and only the size of a single read changes.
10
+
11
+ // A map from each changed path to the absolute part files that carry its diff, in order.
12
+ type DiffParts = Readonly<Record<string, ReadonlyArray<string>>>
13
+
14
+ const LINE_BREAK = '\n'
15
+
16
+ // A line longer than the budget is cut by characters: a minified file or a lockfile line would
17
+ // otherwise produce one part past the cap, which is the read this module exists to remove.
18
+ function cut_line(line: string, budget: number): Array<string> {
19
+ const pieces: Array<string> = []
20
+
21
+ for (let start = 0; start < line.length; start += budget) {
22
+ pieces.push(line.slice(start, start + budget))
23
+ }
24
+
25
+ return pieces
26
+ }
27
+
28
+ function pieces_of(text: string, budget: number): Array<string> {
29
+ return text.split(/(?<=\n)/u).flatMap((line) => cut_line(line, budget))
30
+ }
31
+
32
+ // Line-preserving where a line fits, so a part ends on a line boundary and a reader never sees a hunk
33
+ // cut mid-line unless that one line is itself over the budget.
34
+ function is_full(current: string, piece: string, budget: number): boolean {
35
+ return current.length > 0 && current.length + piece.length > budget
36
+ }
37
+
38
+ function split_text(text: string, budget: number): Array<string> {
39
+ const parts: Array<string> = []
40
+ let current = ''
41
+
42
+ for (const piece of pieces_of(text, budget)) {
43
+ if (is_full(current, piece, budget)) {
44
+ parts.push(current)
45
+ current = ''
46
+ }
47
+
48
+ current += piece
49
+ }
50
+
51
+ return current.length > 0 ? [...parts, current] : parts
52
+ }
53
+
54
+ function part_header(relative: string, index: number, count: number): string {
55
+ return `# ${relative} — part ${String(index + 1)} of ${String(count)}${LINE_BREAK}`
56
+ }
57
+
58
+ // The header is counted inside the cap, so the file a reader opens — header included — is what fits.
59
+ // The count in the header is the final one, so it is sized against the widest header it can carry.
60
+ function part_texts(relative: string, diff: string, cap: number): Array<string> {
61
+ const budget = Math.max(1, cap - part_header(relative, diff.length, diff.length).length)
62
+ const bodies = split_text(diff, budget)
63
+
64
+ return bodies.map((body, index) => `${part_header(relative, index, bodies.length)}${body}`)
65
+ }
66
+
67
+ function body_of(part: string): string {
68
+ return part.slice(part.indexOf(LINE_BREAK) + 1)
69
+ }
70
+
71
+ function part_list(parts: DiffParts): string {
72
+ return Object.entries(parts)
73
+ .flatMap(([relative, files]) => [` ${relative}`, ...files.map((file) => ` ${file}`)])
74
+ .join(LINE_BREAK)
75
+ }
76
+
77
+ // **The read instruction names the tool as well as the files.** A part under the cap would survive a
78
+ // `cat`, but the Read tool is the one read that is never truncated, and naming it keeps the reviewer
79
+ // from reaching back for the whole `git diff` the target line defines the change by.
80
+ function reading_block(parts: DiffParts, cap: number): string {
81
+ return [
82
+ `Reading the diff: Bash output is cut at ${String(cap)} characters, so the change is written below as one or more parts per path, each under that cap, and together exactly the diff. Read the parts of every path you review with the Read tool. Do not print the diff, or any file this brief names, through Bash (\`git diff\`, \`cat\`).`,
83
+ part_list(parts),
84
+ ].join(LINE_BREAK)
85
+ }
86
+
87
+ const review_diff_parts = { body_of, part_texts, reading_block, split_text }
88
+
89
+ export { review_diff_parts, type DiffParts }
@@ -0,0 +1,97 @@
1
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { git_spawn } from '#scripts/git/git-spawn'
4
+ import { stamp_file } from '#scripts/josh/stamp-file'
5
+ import { review_diff_parts, type DiffParts } from './review-diff-parts'
6
+
7
+ // Writes the change as the parts `review-diff-parts.ts` cuts it into (joshuafolkken/kit#2963).
8
+ //
9
+ // **One diff per path, read the way the target line defines the change** — `git -C <root> diff
10
+ // <base> -- <path>` for a tracked path, and the file's own content for an untracked one, which no
11
+ // `git diff` lists. The parts are written to the temp directory keyed per checkout, the same place the
12
+ // brief's other records live, and the directory is replaced on every brief so a part from an earlier
13
+ // tree is never offered beside this one's.
14
+
15
+ const DIRECTORY_PREFIX = 'josh-review-diff-'
16
+ const DIRECTORY_MODE = 0o700
17
+ const FILE_MODE = 0o600
18
+ const PART_SUFFIX = '.diff'
19
+ const INDEX_WIDTH = 3
20
+ const NUL = '\0'
21
+ const SLUG_SEPARATOR = '__'
22
+
23
+ function untracked_text(root: string, relative: string): string {
24
+ try {
25
+ const content = readFileSync(path.join(root, relative), 'utf8')
26
+
27
+ if (content.includes(NUL)) return `untracked binary file ${relative}, not shown\n`
28
+
29
+ return `untracked file ${relative}, shown whole:\n${content}`
30
+ } catch {
31
+ return `${relative} is listed by the change but holds no diff and no file\n`
32
+ }
33
+ }
34
+
35
+ async function path_diff(root: string, base: string, relative: string): Promise<string> {
36
+ const diff = await git_spawn.read(['-C', root, 'diff', '--no-color', base, '--', relative])
37
+
38
+ return diff.length > 0 ? diff : untracked_text(root, relative)
39
+ }
40
+
41
+ function part_name(position: number, relative: string, index: number): string {
42
+ const prefix = String(position).padStart(INDEX_WIDTH, '0')
43
+
44
+ return `${prefix}-${relative.split('/').join(SLUG_SEPARATOR)}-${String(index + 1)}${PART_SUFFIX}`
45
+ }
46
+
47
+ interface PathText {
48
+ relative: string
49
+ text: string
50
+ }
51
+
52
+ function write_path(
53
+ directory: string,
54
+ position: number,
55
+ entry: PathText,
56
+ cap: number,
57
+ ): Array<string> {
58
+ return review_diff_parts.part_texts(entry.relative, entry.text, cap).map((part, index) => {
59
+ const file = path.join(directory, part_name(position, entry.relative, index))
60
+
61
+ writeFileSync(file, part, { flag: 'wx', mode: FILE_MODE })
62
+
63
+ return file
64
+ })
65
+ }
66
+
67
+ function fresh_directory(root: string): string {
68
+ const directory = stamp_file.stamp_path(DIRECTORY_PREFIX, root, '')
69
+
70
+ rmSync(directory, { recursive: true, force: true })
71
+ mkdirSync(directory, { mode: DIRECTORY_MODE })
72
+
73
+ return directory
74
+ }
75
+
76
+ interface DiffRequest {
77
+ root: string
78
+ base: string
79
+ paths: ReadonlyArray<string>
80
+ cap: number
81
+ }
82
+
83
+ async function write_parts(request: DiffRequest, directory?: string): Promise<DiffParts> {
84
+ const { root, base, paths, cap } = request
85
+ const texts = await Promise.all(
86
+ paths.map(async (relative) => ({ relative, text: await path_diff(root, base, relative) })),
87
+ )
88
+ const target = directory ?? fresh_directory(root)
89
+
90
+ return Object.fromEntries(
91
+ texts.map((entry, position) => [entry.relative, write_path(target, position, entry, cap)]),
92
+ )
93
+ }
94
+
95
+ const review_diff_write = { path_diff, write_parts }
96
+
97
+ export { review_diff_write }
@@ -151,7 +151,6 @@ function has_issue_record(content: string, issue: number): boolean {
151
151
 
152
152
  const review_finding_ledger = {
153
153
  CATEGORIES,
154
- SEVERITIES,
155
154
  category_counts,
156
155
  finding_line,
157
156
  has_issue_record,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { parseArgs } from 'node:util'
3
+ import { cli_flags } from '#scripts/lib/cli-flags'
4
4
  import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
5
5
  import { review_finding_ledger, type Finding } from './review-finding-ledger'
6
6
  import { review_record, type RecordVerdict } from './review-record'
@@ -71,21 +71,14 @@ function parse_findings(specs: ReadonlyArray<string>): ReadonlyArray<Finding> |
71
71
  }
72
72
 
73
73
  function parse_argv(argv: ReadonlyArray<string>): Parsed | undefined {
74
- try {
75
- const parsed = parseArgs({
76
- args: [...argv],
77
- options: OPTIONS,
78
- strict: true,
79
- allowPositionals: true,
80
- })
81
-
82
- return {
83
- check: parsed.values.check === true,
84
- issue: parsed.values.issue,
85
- positionals: parsed.positionals,
86
- }
87
- } catch {
88
- return undefined
74
+ const parsed = cli_flags.arguments_of(argv, OPTIONS)
75
+
76
+ if (parsed === undefined) return undefined
77
+
78
+ return {
79
+ check: parsed.values.check === true,
80
+ issue: parsed.values.issue,
81
+ positionals: parsed.positionals,
89
82
  }
90
83
  }
91
84
 
@@ -186,7 +179,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
186
179
  process.exitCode = await run(argv, new Date())
187
180
  }
188
181
 
189
- const review_record_cli = { USAGE, build_lines, parse_finding, run }
182
+ const review_record_cli = { build_lines, parse_finding, run }
190
183
 
191
184
  if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
192
185
 
@@ -55,7 +55,6 @@ function refusal_message(issue: number): string {
55
55
  }
56
56
 
57
57
  const review_record = {
58
- MISSING_REASON,
59
58
  check,
60
59
  refusal_message,
61
60
  }
@@ -77,7 +77,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
77
77
  process.exitCode = await run(argv)
78
78
  }
79
79
 
80
- const review_round2_cli = { CLOSED_FLAG, JSON_KEY, KNOWN_FLAGS, main, parse_options, run, USAGE }
80
+ const review_round2_cli = { CLOSED_FLAG, parse_options, run }
81
81
 
82
82
  if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
83
83
 
@@ -102,18 +102,15 @@ function clear_round_one(target?: string): void {
102
102
  }
103
103
 
104
104
  const review_stamps = {
105
- BRIEFED_PREFIX,
106
105
  briefed_stamp,
107
106
  clear_round_one,
108
107
  GATE_PREFIX,
109
108
  gate_stamp,
110
109
  IN_FLIGHT_PREFIX,
111
110
  in_flight_stamp,
112
- LINT_RELATED_PREFIX,
113
111
  lint_related_stamp,
114
112
  ROUND_ONE_PREFIX,
115
113
  round_one_stamp,
116
- TEST_RELATED_PREFIX,
117
114
  test_related_stamp,
118
115
  }
119
116
 
@@ -3,6 +3,7 @@ import path from 'node:path'
3
3
  import { changed_paths } from '#scripts/git/changed-paths'
4
4
  import { git_command } from '#scripts/git/git-command'
5
5
  import { stamp_file } from '#scripts/josh/stamp-file'
6
+ import { observation_ledger } from '#scripts/observations/observation-ledger'
6
7
 
7
8
  // The tree `josh review:brief` records: every path the change touches, and the digest of its content
8
9
  // (joshuafolkken/kit#1241).
@@ -30,8 +31,18 @@ function digest_of(root: string, relative: string): string {
30
31
 
31
32
  // Sorted so two readings of one tree produce the same record byte for byte, which is what lets a
32
33
  // stored stamp be compared against a fresh reading without a normalization step in between.
34
+ //
35
+ // **The observation ledger is left out of the map**, for the reason `hook-gate-reuse.ts` drops it from
36
+ // the status it reads: it is no code any check runs (joshuafolkken/kit#1756). Kept in, a review's line
37
+ // appended after the gate moved the map, so the ledger commit `pnpm josh followup` pushes carried a
38
+ // tree the record no longer matched — the pre-push hook re-ran the whole unit suite inside the push's
39
+ // 120-second budget, the timed-out push was retried over the still-running suite, and the commit's
40
+ // undo then moved the tree under both (joshuafolkken/kit#2998). Filtered here, every reader of the map
41
+ // — the gate's record, both hooks, the brief and the round-2 fix delta — agrees on it.
33
42
  function tree_of(root: string, paths: ReadonlyArray<string>): Record<string, string> {
34
- const sorted = [...paths].toSorted((left, right) => left.localeCompare(right))
43
+ const sorted = paths
44
+ .filter((relative) => !observation_ledger.is_ledger_path(relative))
45
+ .toSorted((left, right) => left.localeCompare(right))
35
46
 
36
47
  return Object.fromEntries(sorted.map((relative) => [relative, digest_of(root, relative)]))
37
48
  }
@@ -43,6 +54,7 @@ function tree_of(root: string, paths: ReadonlyArray<string>): Record<string, str
43
54
  // say the gate had verified an arbitrarily edited tree, and `--round 2` would report an empty fix
44
55
  // delta. **A defect that answers "all clear" is the one shape this record cannot take**, which is why
45
56
  // the root is resolved rather than assumed (measured from `scripts/`: 18 of 25 entries `absent`).
57
+ // The observation ledger is dropped inside `tree_of`, above (joshuafolkken/kit#3017).
46
58
  async function read_changed_tree(
47
59
  paths?: ReadonlyArray<string>,
48
60
  root?: string,
@@ -53,6 +65,6 @@ async function read_changed_tree(
53
65
  )
54
66
  }
55
67
 
56
- const review_tree = { ABSENT_DIGEST, digest_of, read_changed_tree, tree_of }
68
+ const review_tree = { ABSENT_DIGEST, read_changed_tree, tree_of }
57
69
 
58
70
  export { review_tree }
@@ -0,0 +1,18 @@
1
+ import { run_stage } from '#scripts/run/run-stage'
2
+ import type { DecisionOracle } from './decision-oracle'
3
+
4
+ // The stage-ladder oracle (joshuafolkken/kit#3042), kept beside the enumeration rather than inside it
5
+ // because `decision-oracle.ts` sits at its file-size limit. Where a run on an issue starts — and whether
6
+ // the command typed has already been reached — is computed by `run:entry` from the issue and the tree's
7
+ // hold, so the skill's stage table points at the command rather than re-deciding it in prose. The
8
+ // vocabulary is `run_stage`'s own start tokens, never a second copy.
9
+ const RUN_ENTRY_ORACLE: DecisionOracle = {
10
+ name: 'run:entry',
11
+ decision:
12
+ 'Where a run on an issue starts, and whether the typed command has already been reached',
13
+ args: '<N> [--to kickoff|halfrun|prrun|fullrun]',
14
+ vocabulary: run_stage.START_TOKENS,
15
+ single_source: '.claude/skills/workflow-commands/SKILL.md → §2k',
16
+ }
17
+
18
+ export { RUN_ENTRY_ORACLE }
@@ -6,6 +6,7 @@
6
6
  // not by prose. Single source: `prompts/collaboration-workflow/residency.md` → question 0.
7
7
  //
8
8
  // Anything not on this list is answered by prose or by a hook-delivered rule, never by a command.
9
+ import { RUN_ENTRY_ORACLE } from './decision-oracle-stage'
9
10
 
10
11
  // Paths that appear in more than one entry's `single_source` field.
11
12
  const CHAIN_RULE_MD = '.claude/skills/workflow-commands/chain-rule.md'
@@ -22,11 +23,11 @@ const BACKLOGRUN_MD = '.claude/skills/workflow-commands/backlogrun.md'
22
23
  // now has its verdict contract only in this reference. That makes it the single source and the section
23
24
  // a reader is routed to, in the same `file.md → \`josh <command>\`` form the `epic:reconcile` and
24
25
  // `lane:list` entries already use. joshuafolkken/kit#2254.
25
- const RUN_MERGE_REFERENCE = 'docs/josh-commands.md → `josh run:merge`'
26
- const EPIC_NEXT_REFERENCE = 'docs/josh-commands.md → `josh epic:next`'
27
- const BACKLOG_BUDGET_REFERENCE = 'docs/josh-commands.md → `josh backlog:budget`'
28
- const RUN_LIVENESS_REFERENCE = 'docs/josh-commands.md → `josh run:liveness`'
29
- const AUTO_OK_NEXT_REFERENCE = 'docs/josh-commands.md → `josh auto-ok:next`'
26
+ const RUN_MERGE_REFERENCE = 'docs/josh-commands-automation.md → `josh run:merge`'
27
+ const EPIC_NEXT_REFERENCE = 'docs/josh-commands-automation.md → `josh epic:next`'
28
+ const BACKLOG_BUDGET_REFERENCE = 'docs/josh-commands-automation.md → `josh backlog:budget`'
29
+ const RUN_LIVENESS_REFERENCE = 'docs/josh-commands-automation.md → `josh run:liveness`'
30
+ const AUTO_OK_NEXT_REFERENCE = 'docs/josh-commands-automation.md → `josh auto-ok:next`'
30
31
 
31
32
  // Command used by two separate oracle entries (run:cut:resume and run:cut:gate).
32
33
  const RUN_CUT_CMD = 'run:cut'
@@ -311,7 +312,7 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
311
312
  "Whether an epic's declaration matches its recorded relations, and the repair when it does not",
312
313
  args: '--reconcile <E>',
313
314
  vocabulary: [RECONCILED, NOTHING_TO_RECONCILE],
314
- single_source: 'docs/josh-commands.md → `josh epic --reconcile`',
315
+ single_source: 'docs/josh-commands-automation.md → `josh epic --reconcile`',
315
316
  },
316
317
  {
317
318
  name: 'lane:list',
@@ -319,8 +320,9 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
319
320
  'Whether each in-progress issue holds a live lane, and the two-directional difference',
320
321
  args: '',
321
322
  vocabulary: [LIVE, STOPPED, UNKNOWN],
322
- single_source: 'docs/josh-commands.md → The `in-progress` / lane difference',
323
+ single_source: 'docs/josh-commands-automation.md → The `in-progress` / lane difference',
323
324
  },
325
+ RUN_ENTRY_ORACLE,
324
326
  ]
325
327
 
326
328
  // Returns the command name for an oracle entry. When `command` is omitted from the entry,
@@ -339,4 +341,5 @@ const decision_oracle = {
339
341
  get_command,
340
342
  }
341
343
 
344
+ export type { DecisionOracle }
342
345
  export { decision_oracle }
@@ -18,6 +18,7 @@ import { lane_interactive_ask } from './lane-interactive-ask'
18
18
  import { lane_park } from './lane-park'
19
19
  import { lane_switch_main } from './lane-switch-main'
20
20
  import { oracle_consulted } from './oracle-consulted'
21
+ import { permission_guards } from './permission-guards'
21
22
  import { piped_verification } from './piped-verification'
22
23
  import { poll_loop } from './poll-loop'
23
24
  import { pre_gate_cut } from './pre-gate-cut'
@@ -143,8 +144,10 @@ const { is_issue_filing, on_bash_command } = bash_triggers
143
144
  // exemptions and the three tests that decide the second one. The three tests are spelled out rather
144
145
  // than named, because a delivery that said only "an interrupt is exempt" would hand the deciding back
145
146
  // to judgement at exactly the moment nothing else is open to read (joshuafolkken/kit#1518).
147
+ // `WIP_CAP` is the number's single source; `wip-cap.md` states it once and a test pins the two equal.
148
+ const WIP_CAP = 30
146
149
  const WIP_CAP_REASON =
147
- "⛔ backlog WIP cap: count the target repository's open Issues before filing. With more than 30 " +
150
+ `⛔ backlog WIP cap: count the target repository's open Issues before filing. With more than ${String(WIP_CAP)} ` +
148
151
  'open, close one first; nothing honestly closable means do not file. Two filings are exempt and ' +
149
152
  'proceed while stating the overage — one the run is blocked by, and an interrupt, decided by ' +
150
153
  'three tests rather than judgement: a verification answers wrongly, a documented workflow cannot ' +
@@ -482,6 +485,10 @@ const DELIVERED_RULES: ReadonlyArray<DeliveredRule> = [
482
485
  git_force.ROW,
483
486
  worktree_guard.ROW,
484
487
  file_body.ROW,
488
+ // **The deny-list bypasses closed by argv and path rather than glob** (joshuafolkken/kit#2983) — an
489
+ // index mutation, a recursive forced `rm` or destructive `gh` call, and a protected file. Each
490
+ // claims a command or file no row above does and refuses every occurrence (`permission-guards.ts`).
491
+ ...permission_guards.ROWS,
485
492
  // **The hand-written wait loop a lane child improvises over a backgrounded command's output**
486
493
  // (joshuafolkken/kit#2371). `early-heartbeat` refuses a bare `sleep` and stands down on any loop
487
494
  // keyword; this is the loop it left — a `while`/`until` that sleeps between probes of an output
@@ -788,6 +795,7 @@ const delivered_rules = {
788
795
  SHELL_BODY_REASON,
789
796
  SWITCH_ENV_KEY,
790
797
  THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
798
+ WIP_CAP,
791
799
  WIP_CAP_REASON,
792
800
  WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
793
801
  delivery,
@@ -0,0 +1,119 @@
1
+ import { bash_triggers } from './bash-triggers'
2
+ import { gh_api } from './gh-api'
3
+ import { git_argv } from './git-argv'
4
+ import { shell_segments } from './shell-segments'
5
+
6
+ // The trigger and the delivered text behind the `destructive-command` row of `delivered-rules.ts`
7
+ // (joshuafolkken/kit#2983): a recursive forced `rm`, and the `gh` calls that delete or close shared
8
+ // state — none of which the deny list stopped, or stopped only in one spelling (`Bash(rm -rf *)`
9
+ // misses `rm -fr`, `rm -r -f` and `rm --recursive --force`). The argv is read through `git-argv.ts`'s
10
+ // wrapper cut, so `sudo rm -fr` and `env gh pr close` are seen too.
11
+ //
12
+ // **What is refused is the decision recorded on the Issue, not every destructive act.** `gh issue
13
+ // close` passes — the WIP-cap procedure closes Issues legitimately — and so does removing a label from
14
+ // an Issue (`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`), which the `in-progress` and
15
+ // `needs-decision` steps of the workflow documents issue. Every other `DELETE repos/…` is refused.
16
+ //
17
+ // **It fires on every occurrence**, for the reason `git-force.ts` does.
18
+
19
+ const RM_COMMAND = 'rm'
20
+ const RECURSIVE_LONG = '--recursive'
21
+ const RECURSIVE_LETTERS = 'rR'
22
+ const FORCE_LONG = '--force'
23
+ const FORCE_LETTER = 'f'
24
+
25
+ function has_flag(args: ReadonlyArray<string>, long: string, letters: string): boolean {
26
+ return args.some((argument) => argument === long || git_argv.short_cluster_has(argument, letters))
27
+ }
28
+
29
+ function is_recursive_forced_rm(segment: string): boolean {
30
+ const args = git_argv.arguments_of(segment, RM_COMMAND)
31
+
32
+ if (args === undefined) return false
33
+
34
+ return (
35
+ has_flag(args, RECURSIVE_LONG, RECURSIVE_LETTERS) && has_flag(args, FORCE_LONG, FORCE_LETTER)
36
+ )
37
+ }
38
+
39
+ const GH_COMMAND = 'gh'
40
+ const REPO_GROUP = 'repo'
41
+ const REPO_REMOVALS: ReadonlySet<string> = new Set(['delete', 'archive'])
42
+ const REPO_EDIT = 'edit'
43
+ const VISIBILITY_FLAG = '--visibility'
44
+ const PR_GROUP = 'pr'
45
+ const PR_CLOSE = 'close'
46
+
47
+ // `gh repo delete` / `archive`, and `gh repo edit --visibility …`.
48
+ function is_destructive_repo_action(action: string, args: ReadonlyArray<string>): boolean {
49
+ if (REPO_REMOVALS.has(action)) return true
50
+
51
+ return action === REPO_EDIT && args.some((argument) => argument.startsWith(VISIBILITY_FLAG))
52
+ }
53
+
54
+ function is_destructive_gh_command(args: ReadonlyArray<string>): boolean {
55
+ const [group, action = ''] = args
56
+
57
+ if (group === PR_GROUP) return action === PR_CLOSE
58
+
59
+ return group === REPO_GROUP && is_destructive_repo_action(action, args)
60
+ }
61
+
62
+ const DELETE_METHOD = 'DELETE'
63
+ const REPOSITORY_PATH = 'repos/'
64
+ // Removing one label from one Issue — the workflow's own `in-progress` / `needs-decision` step.
65
+ // Anchored on the whole argument, so a label path smuggled into a field value (`-f x=repos/…/labels/a`)
66
+ // does not exempt the real endpoint beside it.
67
+ const ISSUE_LABEL_PATH =
68
+ /^['"]?\/?repos\/[^/\s'"]+\/[^/\s'"]+\/issues\/\d+\/labels\/[^/\s'"]+['"]?$/u
69
+
70
+ // Judged on the call with its wrapper cut off, since `gh_api.is_gh_api` anchors on a leading `gh` and
71
+ // would otherwise miss `env gh api -X DELETE …` that the `gh pr close` branch already sees. Every
72
+ // argument naming a repository path must be a label removal for the call to pass.
73
+ function is_destructive_api_call(args: ReadonlyArray<string>): boolean {
74
+ const call = [GH_COMMAND, ...args].join(' ')
75
+
76
+ if (!gh_api.is_gh_api(call) || gh_api.method_of(call) !== DELETE_METHOD) return false
77
+
78
+ return args.some(
79
+ (argument) => argument.includes(REPOSITORY_PATH) && !ISSUE_LABEL_PATH.test(argument),
80
+ )
81
+ }
82
+
83
+ function is_destructive_gh(segment: string): boolean {
84
+ const args = git_argv.arguments_of(segment, GH_COMMAND)
85
+
86
+ if (args === undefined) return false
87
+
88
+ return is_destructive_gh_command(args) || is_destructive_api_call(args)
89
+ }
90
+
91
+ function is_destructive_segment(segment: string): boolean {
92
+ return is_recursive_forced_rm(segment) || is_destructive_gh(segment)
93
+ }
94
+
95
+ function is_destructive_command(command: string): boolean {
96
+ return shell_segments.segments_of(command).some((segment) => is_destructive_segment(segment))
97
+ }
98
+
99
+ const DESTRUCTIVE_COMMAND_REASON =
100
+ '⛔ destructive command: a recursive forced `rm` (`rm -rf`, `rm -fr`, `rm -r -f`, `rm --recursive ' +
101
+ '--force`), `gh repo delete` / `archive`, `gh repo edit --visibility`, `gh pr close`, or a `gh api ' +
102
+ '-X DELETE repos/…` call deletes or closes something that cannot be restored, which is Tier C and ' +
103
+ 'needs explicit current-turn user instruction (`prompts/collaboration-workflow/operating-rules.md`). ' +
104
+ 'The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row ' +
105
+ 'reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user ' +
106
+ 'to run the command in their own terminal. `gh issue close` and removing a label from an Issue ' +
107
+ '(`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every ' +
108
+ 'occurrence, not once per run.**'
109
+
110
+ const ROW = {
111
+ id: 'destructive-command',
112
+ is_trigger: bash_triggers.on_bash_command(is_destructive_command),
113
+ reason: DESTRUCTIVE_COMMAND_REASON,
114
+ decide: (): boolean => true,
115
+ }
116
+
117
+ const destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW, is_destructive_command }
118
+
119
+ export { destructive_command }
@@ -14,7 +14,7 @@ const DIRECT_FILING_REASON =
14
14
  'lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing ' +
15
15
  'into another repository, and `epic:bundle` after the create — so none of them is a step to remember. ' +
16
16
  'When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,…>` ' +
17
- 'for the ones that are separate deliverables. Reference: `docs/josh-commands.md` → "`josh issue:file`". ' +
17
+ 'for the ones that are separate deliverables. Reference: `docs/josh-commands-automation.md` → "`josh issue:file`". ' +
18
18
  '**This rule fires on every occurrence, not once per run.**'
19
19
 
20
20
  function is_any_filing(command: string): boolean {