@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,31 +1,29 @@
1
1
  import {
2
2
  ALREADY_DONE_LABEL,
3
- BREAKING_CHANGE_LABEL,
4
- BUGFIX_LABEL,
5
3
  CONTEXT_CUT_THRESHOLD,
6
- ENHANCEMENT_LABEL,
7
- FULL_PAGE_QUERY,
8
4
  IN_PROGRESS_LABEL,
9
5
  NEEDS_DECISION_LABEL,
10
6
  NEEDS_HUMAN_REVIEW_LABEL,
7
+ PORCELAIN_FLAG,
11
8
  RECENT_REQUEST_WINDOW,
12
- RELEASE_CLASSIFICATION_NAMES,
13
9
  agent_role_profile,
14
10
  agent_session_environment,
15
11
  bash_triggers,
12
+ cli_flags,
16
13
  error_text,
17
14
  filing_cap,
18
- gh_failure,
19
15
  git_command,
20
- git_gh_api_path,
21
- git_gh_exec,
16
+ git_common_directory,
17
+ git_gh_command,
18
+ git_gh_issue_read,
19
+ git_gh_pr_read,
20
+ git_spawn_sync,
22
21
  has_any_label,
23
- has_stderr_field,
24
22
  issue_cite,
25
23
  lane_background,
26
24
  lane_park,
27
25
  lane_registry,
28
- parse_json_array_or_undefined,
26
+ open_issue_schema,
29
27
  parse_json_object_safe,
30
28
  read_json_listing,
31
29
  repo_origin,
@@ -34,36 +32,34 @@ import {
34
32
  run_cut,
35
33
  run_headless,
36
34
  run_hold,
37
- run_progress_clock,
38
35
  run_tail,
39
- run_watcher_guard,
40
36
  shell_segments,
41
37
  tail_commands
42
- } from "./chunk-RLDX5SOC.js";
38
+ } from "./chunk-AIOIOMPX.js";
43
39
  import {
44
40
  find_local_bin_upwards
45
41
  } from "./chunk-3V37IID6.js";
46
42
  import {
47
43
  batch_outcome
48
- } from "./chunk-GQ7KP45M.js";
44
+ } from "./chunk-XYUIQOW3.js";
49
45
  import {
50
46
  investigation_refusal
51
- } from "./chunk-DMQN45FN.js";
47
+ } from "./chunk-7HVB7SZG.js";
52
48
  import {
53
49
  duplicate_read_outcome
54
- } from "./chunk-HJIISEAC.js";
50
+ } from "./chunk-5SD2YM7Z.js";
55
51
  import {
56
52
  investigation_reads
57
- } from "./chunk-EJDX2TOB.js";
53
+ } from "./chunk-NXYMNPUQ.js";
58
54
  import {
59
55
  lane_guard_policy,
60
56
  time_batch_guard
61
- } from "./chunk-NJQOBE6L.js";
57
+ } from "./chunk-QVLT52OV.js";
62
58
  import {
63
59
  hook_decision,
64
60
  lane_child_marker,
65
61
  run_issue_number
66
- } from "./chunk-WQ4OSBSS.js";
62
+ } from "./chunk-MKVGBQPA.js";
67
63
  import {
68
64
  PLATFORM_TEMP_ROOT,
69
65
  cost_attribute,
@@ -75,10 +71,10 @@ import {
75
71
  lane_paths,
76
72
  stamp_file,
77
73
  time_shell
78
- } from "./chunk-7VPDUBRN.js";
74
+ } from "./chunk-4JZDP5X5.js";
79
75
 
80
76
  // scripts/rules/direct-filing.ts
81
- var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
77
+ var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands-automation.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
82
78
  function is_any_filing(command) {
83
79
  return bash_triggers.is_direct_filing(command) || bash_triggers.is_issue_filing(command);
84
80
  }
@@ -204,7 +200,6 @@ var run_progress = {
204
200
  format_line,
205
201
  format_next_line,
206
202
  format_next_report,
207
- format_record,
208
203
  format_report,
209
204
  is_due,
210
205
  minutes_from,
@@ -212,16 +207,140 @@ var run_progress = {
212
207
  observation_key
213
208
  };
214
209
 
210
+ // scripts/run/run-progress-clock.ts
211
+ import { z } from "zod";
212
+ var PROGRESS_PREFIX = "josh-run-progress-";
213
+ var LIFE_PREFIX = "josh-run-progress-life-";
214
+ var LOG_SUFFIX = ".log";
215
+ var LOG_KEEP = 12;
216
+ var MS_PER_MINUTE2 = 6e4;
217
+ var LOG_STALE_MINUTES = 90;
218
+ var LOG_STALE_MS = LOG_STALE_MINUTES * MS_PER_MINUTE2;
219
+ var BLOCK_SEPARATOR = "\n\n";
220
+ var report_stamp_schema = z.object({ reported_at: z.string(), line: z.string().optional() });
221
+ function parse_stamp(raw) {
222
+ const parsed = report_stamp_schema.safeParse(JSON.parse(raw));
223
+ if (!parsed.success) return void 0;
224
+ const reported_at = Date.parse(parsed.data.reported_at);
225
+ return Number.isNaN(reported_at) ? void 0 : reported_at;
226
+ }
227
+ function read_last_line(target) {
228
+ const raw = stamp_file.read_stamp_text(target);
229
+ if (raw === void 0) return void 0;
230
+ try {
231
+ return report_stamp_schema.safeParse(JSON.parse(raw)).data?.line;
232
+ } catch {
233
+ return void 0;
234
+ }
235
+ }
236
+ function read_last_report(target) {
237
+ const raw = stamp_file.read_stamp_text(target);
238
+ if (raw === void 0) return void 0;
239
+ try {
240
+ return parse_stamp(raw);
241
+ } catch {
242
+ return void 0;
243
+ }
244
+ }
245
+ function log_path_of(target) {
246
+ return `${target}${LOG_SUFFIX}`;
247
+ }
248
+ function is_log_stale(target, now_ms) {
249
+ const previous_ms = read_last_report(target);
250
+ return previous_ms === void 0 || now_ms - previous_ms > LOG_STALE_MS;
251
+ }
252
+ function recent_blocks(log_target2, is_fresh) {
253
+ if (is_fresh) return [];
254
+ const raw = stamp_file.read_stamp_text(log_target2);
255
+ if (raw === void 0) return [];
256
+ return raw.split(BLOCK_SEPARATOR).map((block) => block.trimEnd()).filter((block) => block.length > 0);
257
+ }
258
+ function append_line(log_target2, reported_at, line, is_fresh) {
259
+ const block = `${reported_at}
260
+ ${line}`;
261
+ const blocks = [...recent_blocks(log_target2, is_fresh), block].slice(-LOG_KEEP);
262
+ stamp_file.write_text_stamp(log_target2, `${blocks.join(BLOCK_SEPARATOR)}
263
+ `);
264
+ }
265
+ function mark(target, now_ms, line) {
266
+ const reported_at = new Date(now_ms).toISOString();
267
+ const kept = line ?? read_last_line(target);
268
+ if (line !== void 0) {
269
+ append_line(log_path_of(target), reported_at, line, is_log_stale(target, now_ms));
270
+ }
271
+ stamp_file.write_stamp(target, kept === void 0 ? { reported_at } : { reported_at, line: kept });
272
+ }
273
+ function stamp_target_of(git_directory) {
274
+ return stamp_file.stamp_path(PROGRESS_PREFIX, git_directory);
275
+ }
276
+ function life_target_of(git_directory) {
277
+ return stamp_file.stamp_path(LIFE_PREFIX, git_directory);
278
+ }
279
+ var life_schema = z.object({ alive: z.literal(true), pinged_at: z.string().optional() });
280
+ function begin_life(target) {
281
+ stamp_file.write_stamp(target, { alive: true, pinged_at: (/* @__PURE__ */ new Date()).toISOString() });
282
+ }
283
+ function ping_life(target) {
284
+ begin_life(target);
285
+ }
286
+ function parse_pinged_at(raw) {
287
+ const parsed = life_schema.safeParse(JSON.parse(raw));
288
+ if (!parsed.success || parsed.data.pinged_at === void 0) return void 0;
289
+ const pinged_ms = Date.parse(parsed.data.pinged_at);
290
+ return Number.isNaN(pinged_ms) ? void 0 : pinged_ms;
291
+ }
292
+ function is_life_fresh(target, threshold_ms) {
293
+ const raw = stamp_file.read_stamp_text(target);
294
+ if (raw === void 0) return false;
295
+ try {
296
+ const pinged_ms = parse_pinged_at(raw);
297
+ return pinged_ms !== void 0 && Date.now() - pinged_ms < threshold_ms;
298
+ } catch (error) {
299
+ error_text.trace_swallowed("run_progress_clock.is_life_fresh", error);
300
+ return false;
301
+ }
302
+ }
303
+ function is_life_ended(target) {
304
+ return stamp_file.read_stamp_text(target) === void 0;
305
+ }
306
+ function end_life(target) {
307
+ stamp_file.remove_stamp(target);
308
+ }
309
+ function stamp_target_sync() {
310
+ const directory = git_common_directory.own();
311
+ return directory === void 0 ? void 0 : stamp_target_of(directory);
312
+ }
313
+ function read_last_report_sync() {
314
+ const target = stamp_target_sync();
315
+ return target === void 0 ? void 0 : read_last_report(target);
316
+ }
317
+ var run_progress_clock = {
318
+ LIFE_PREFIX,
319
+ LOG_KEEP,
320
+ LOG_STALE_MS,
321
+ begin_life,
322
+ end_life,
323
+ is_life_ended,
324
+ is_life_fresh,
325
+ life_target_of,
326
+ log_path_of,
327
+ mark,
328
+ parse_stamp,
329
+ ping_life,
330
+ read_last_line,
331
+ read_last_report,
332
+ read_last_report_sync,
333
+ stamp_target_of
334
+ };
335
+
215
336
  // scripts/run/run-progress-config.ts
216
337
  import { readFileSync } from "node:fs";
217
338
  import path from "node:path";
218
- import { z } from "zod";
339
+ import { z as z2 } from "zod";
219
340
  var PACKAGE_FILE = "package.json";
220
- var CONFIG_FIELD = "josh";
221
- var INTERVAL_FIELD = "progress_interval_minutes";
222
341
  var MAX_DEPTH = 10;
223
- var package_schema = z.object({
224
- josh: z.object({ progress_interval_minutes: z.number().positive() }).optional()
342
+ var package_schema = z2.object({
343
+ josh: z2.object({ progress_interval_minutes: z2.number().positive() }).optional()
225
344
  });
226
345
  function ancestors_of(start) {
227
346
  const directories = [];
@@ -258,8 +377,6 @@ function configured_interval_ms(directory = process.cwd()) {
258
377
  return resolve_interval_ms(void 0, directory);
259
378
  }
260
379
  var run_progress_config = {
261
- CONFIG_FIELD,
262
- INTERVAL_FIELD,
263
380
  PACKAGE_FILE,
264
381
  configured_interval_ms,
265
382
  read_package_minutes,
@@ -323,7 +440,7 @@ function decide(call, run2, can_record) {
323
440
  if (can_record) ARM_RECORD.record(target, lands_ms);
324
441
  return false;
325
442
  }
326
- var EARLY_HEARTBEAT_REASON = '\u26D4 early heartbeat: this call arms a wait timer of its own, and the progress clock is not yours to keep. `pnpm josh run:progress --wait` waits the interval out for you (`JOSH_PROGRESS_INTERVAL_MINUTES`, or `josh.progress_interval_minutes` in `package.json`, default 20 minutes), prints the five labelled lines and exits \u2014 start it in the background, relay the lines it printed when it exits, and start the next one. Two timers armed at once is what produced reports 3\u20135 minutes apart on a 15-minute setting (joshuafolkken/kit#1570): a new one was armed on the turn a timer fired and again on the turn a child\'s completion woke the run, and `--mark` recorded each report without anything refusing the early ones. **An explicit ask is not a heartbeat**: if the person asked for progress now, run `pnpm josh run:progress --once`, which prints the five labelled lines whatever the clock says. If you are waiting on something rather than on a clock, wait on the thing itself \u2014 `pnpm josh followup` waits for CI. The procedure is `.claude/skills/workflow-commands/backlogrun.md` \u2192 "Progress while the run is quiet". **This rule fires on every early arm, not once per run**, so reissuing the same sleep will be refused again.';
443
+ var EARLY_HEARTBEAT_REASON = "\u26D4 early heartbeat: this call arms a wait timer of its own, and the progress clock is not yours to keep. `pnpm josh run:progress --wait` keeps it for you (`JOSH_PROGRESS_INTERVAL_MINUTES`, or `josh.progress_interval_minutes` in `package.json`, default 20 minutes): start it once in the background and it reports the five labelled lines to the run's event stream on its own, without waking you \u2014 relay nothing, and start it again only after it exits, which it does when newly runnable work arrives. Two timers armed at once is what produced reports 3\u20135 minutes apart on a 15-minute setting (joshuafolkken/kit#1570): a new one was armed on the turn a timer fired and again on the turn a child's completion woke the run, and `--mark` recorded each report without anything refusing the early ones. **An explicit ask is not a heartbeat**: if the person asked for progress now, run `pnpm josh run:progress --once`, which prints the five labelled lines whatever the clock says. If you are waiting on something rather than on a clock, wait on the thing itself \u2014 `pnpm josh followup` waits for CI. The procedure is `.claude/skills/workflow-commands/backlogrun.md` \u2192 \"Progress while the run is quiet\". **This rule fires on every early arm, not once per run**, so reissuing the same sleep will be refused again.";
327
444
  var PROGRESS_NAMES = /* @__PURE__ */ new Set(["run:progress"]);
328
445
  var WATCH_FLAG = /(?:^|\s)--(?:wait|once)(?=\s|$)/u;
329
446
  function is_progress_watch_segment(segment) {
@@ -406,10 +523,7 @@ var ROW2 = {
406
523
  var file_body = {
407
524
  FILE_BODY_REASON,
408
525
  ROW: ROW2,
409
- carries_a_file_body,
410
- is_in_place_perl,
411
- is_interpreter_write,
412
- writes_existing_file
526
+ carries_a_file_body
413
527
  };
414
528
 
415
529
  // scripts/rules/gh-api.ts
@@ -424,8 +538,11 @@ var REPO_GROUP = 2;
424
538
  function is_gh_api(segment) {
425
539
  return GH_API_COMMAND.test(segment);
426
540
  }
541
+ function method_of(segment) {
542
+ return API_METHOD.exec(segment)?.[1]?.toUpperCase();
543
+ }
427
544
  function is_read(segment) {
428
- const method = API_METHOD.exec(segment)?.[1];
545
+ const method = method_of(segment);
429
546
  if (method !== void 0) return method.toUpperCase() === READ_METHOD;
430
547
  return !API_FIELD.test(segment);
431
548
  }
@@ -437,7 +554,7 @@ function repo_target(segment) {
437
554
  if (match === null) return void 0;
438
555
  return { owner: match[OWNER_GROUP] ?? "", repo: match[REPO_GROUP] ?? "" };
439
556
  }
440
- var gh_api = { GH_API_COMMAND, GH_FLAGS, is_gh_api, is_read, is_write, repo_target };
557
+ var gh_api = { GH_FLAGS, is_gh_api, is_read, is_write, method_of, repo_target };
441
558
 
442
559
  // scripts/rules/git-argv.ts
443
560
  var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/u;
@@ -447,7 +564,8 @@ var WRAPPER_WORDS = /* @__PURE__ */ new Set([
447
564
  "exec",
448
565
  "run",
449
566
  "sudo",
450
- "command"
567
+ "command",
568
+ "env"
451
569
  ]);
452
570
  var SUBSHELL_OPEN = "(";
453
571
  var VALUE_TAKING_GLOBALS = /* @__PURE__ */ new Set([
@@ -487,11 +605,15 @@ function after_globals(words, start) {
487
605
  }
488
606
  return index;
489
607
  }
490
- function parse(segment) {
608
+ function arguments_of(segment, command) {
491
609
  const words = words_of(segment);
492
- const git_index = after_wrapper(words);
493
- if (words[git_index] !== GIT_COMMAND) return void 0;
494
- const subcommand_index = after_globals(words, git_index + NEXT);
610
+ const command_index = after_wrapper(words);
611
+ return words[command_index] === command ? words.slice(command_index + NEXT) : void 0;
612
+ }
613
+ function parse(segment) {
614
+ const words = arguments_of(segment, GIT_COMMAND);
615
+ if (words === void 0) return void 0;
616
+ const subcommand_index = after_globals(words, 0);
495
617
  return { subcommand: words[subcommand_index], args: words.slice(subcommand_index + NEXT) };
496
618
  }
497
619
  var SHORT_CLUSTER = /^-[A-Za-z]+$/u;
@@ -503,7 +625,7 @@ function short_cluster_has(token, letters) {
503
625
  }
504
626
  return false;
505
627
  }
506
- var git_argv = { parse, short_cluster_has, words_of };
628
+ var git_argv = { arguments_of, parse, short_cluster_has };
507
629
 
508
630
  // scripts/rules/git-force.ts
509
631
  var FORCE_LONG_PREFIX = "--force";
@@ -542,42 +664,32 @@ var ROW3 = {
542
664
  var git_force = {
543
665
  GIT_FORCE_REASON,
544
666
  ROW: ROW3,
545
- is_branch_delete,
546
- is_force_or_delete,
547
- is_force_or_delete_push
667
+ is_force_or_delete
548
668
  };
549
669
 
550
670
  // scripts/cost-runtime/cost-cli.ts
551
671
  import { fileURLToPath } from "node:url";
552
- import { parseArgs } from "node:util";
553
672
 
554
673
  // scripts/cost-runtime/codex-usage.ts
555
674
  import { globSync, readFileSync as readFileSync2 } from "node:fs";
556
675
  import path2 from "node:path";
557
- import { z as z2 } from "zod";
676
+ import { z as z3 } from "zod";
558
677
  var THREAD_ID_PATTERN = /^[A-Za-z0-9-]+$/u;
559
- var SESSION_META_SCHEMA = z2.looseObject({
560
- type: z2.literal("session_meta"),
561
- payload: z2.looseObject({ id: z2.string(), cwd: z2.string() })
678
+ var SESSION_META_SCHEMA = z3.looseObject({
679
+ type: z3.literal("session_meta"),
680
+ payload: z3.looseObject({ id: z3.string(), cwd: z3.string() })
562
681
  });
563
- var TOTAL_USAGE_SCHEMA = z2.looseObject({ input_tokens: z2.number().nonnegative() });
564
- var TOKEN_COUNT_SCHEMA = z2.looseObject({
565
- type: z2.literal("event_msg"),
566
- payload: z2.looseObject({
567
- type: z2.literal("token_count"),
568
- info: z2.looseObject({
682
+ var TOTAL_USAGE_SCHEMA = z3.looseObject({ input_tokens: z3.number().nonnegative() });
683
+ var TOKEN_COUNT_SCHEMA = z3.looseObject({
684
+ type: z3.literal("event_msg"),
685
+ payload: z3.looseObject({
686
+ type: z3.literal("token_count"),
687
+ info: z3.looseObject({
569
688
  total_token_usage: TOTAL_USAGE_SCHEMA,
570
689
  last_token_usage: TOTAL_USAGE_SCHEMA
571
690
  })
572
691
  })
573
692
  });
574
- function decode(line) {
575
- try {
576
- return JSON.parse(line);
577
- } catch {
578
- return void 0;
579
- }
580
- }
581
693
  function same_project(cwd, target) {
582
694
  return cost_transcript.session_cwd(cwd) === cost_transcript.session_cwd(target);
583
695
  }
@@ -585,7 +697,7 @@ function billed_per_request(usages) {
585
697
  return usages.map((usage) => usage.last_token_usage.input_tokens);
586
698
  }
587
699
  function measurement_from(content, target, thread_id) {
588
- const values = content.split("\n").map((line) => decode(line));
700
+ const values = content.split("\n").map((line) => json_value.parse_or_undefined(line));
589
701
  const metadata = values.flatMap((value) => {
590
702
  const parsed = SESSION_META_SCHEMA.safeParse(value);
591
703
  return parsed.success ? [parsed.data] : [];
@@ -736,11 +848,9 @@ function mainline_records(pairs) {
736
848
  return [...groups.values()];
737
849
  }
738
850
  var cost_corpus = {
739
- accumulate_missing,
740
851
  load_corpus,
741
852
  attribute_corpus,
742
853
  floor_for_issue,
743
- dedupe_across_sessions,
744
854
  attributed,
745
855
  mainline_records
746
856
  };
@@ -830,8 +940,8 @@ function selected_threshold(values) {
830
940
  function threshold_option(over) {
831
941
  return over === void 0 ? {} : { over };
832
942
  }
833
- function path_option(path7) {
834
- return path7 === void 0 ? {} : { path: path7 };
943
+ function path_option(path10) {
944
+ return path10 === void 0 ? {} : { path: path10 };
835
945
  }
836
946
  function to_options(values) {
837
947
  if (values.cut === true && values.over !== void 0) return void 0;
@@ -845,12 +955,8 @@ var PARSE_ARGS_OPTIONS = {
845
955
  path: { type: "string" }
846
956
  };
847
957
  function parse_options(argv) {
848
- try {
849
- const { values } = parseArgs({ args: [...argv], options: PARSE_ARGS_OPTIONS, strict: true });
850
- return to_options(values);
851
- } catch {
852
- return void 0;
853
- }
958
+ const values = cli_flags.values_of(argv, PARSE_ARGS_OPTIONS);
959
+ return values === void 0 ? void 0 : to_options(values);
854
960
  }
855
961
  function report_empty(cwd, session_id) {
856
962
  const searched = cost_transcript.searched_directories(cost_transcript.transcript_directories(cwd));
@@ -947,11 +1053,8 @@ var cost_cli = {
947
1053
  USAGE,
948
1054
  parse_options,
949
1055
  to_threshold,
950
- load_corpus: cost_corpus.load_corpus,
951
- attributed: cost_corpus.attributed,
952
1056
  run,
953
- session_verdict,
954
- main
1057
+ session_verdict
955
1058
  };
956
1059
  if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET));
957
1060
 
@@ -970,16 +1073,13 @@ function format_share(part, whole) {
970
1073
  return `${(part / whole * PERCENT_SCALE).toFixed(PERCENT_DECIMALS)}%`;
971
1074
  }
972
1075
  var cost_format = {
973
- USD_DECIMALS,
974
- PERCENT_SCALE,
975
- PERCENT_DECIMALS,
976
1076
  format_usd,
977
1077
  format_tokens,
978
1078
  format_share
979
1079
  };
980
1080
 
981
1081
  // scripts/rules/implementation-cut-verdict.ts
982
- import { z as z3 } from "zod";
1082
+ import { z as z4 } from "zod";
983
1083
  var VERDICT_CACHE_PREFIX = "josh-impl-cut-verdict-";
984
1084
  var VERDICT_REUSE_MS = 5e3;
985
1085
  var VERDICT_VALUES = [
@@ -987,7 +1087,7 @@ var VERDICT_VALUES = [
987
1087
  "under",
988
1088
  "unmeasurable"
989
1089
  ];
990
- var cache_schema = z3.object({ read_at_ms: z3.number(), verdict: z3.enum(VERDICT_VALUES) });
1090
+ var cache_schema = z4.object({ read_at_ms: z4.number(), verdict: z4.enum(VERDICT_VALUES) });
991
1091
  function cache_path(directory) {
992
1092
  return stamp_file.stamp_path(VERDICT_CACHE_PREFIX, directory);
993
1093
  }
@@ -1083,7 +1183,6 @@ var implementation_cut = {
1083
1183
  REISSUE_WINDOW_MS,
1084
1184
  ROW: ROW4,
1085
1185
  decide: decide2,
1086
- is_edit_tool,
1087
1186
  is_over_threshold_edit,
1088
1187
  is_reissued_refusal,
1089
1188
  takes_the_impl_cut,
@@ -1159,20 +1258,20 @@ var lane_carry_conflict = {
1159
1258
  };
1160
1259
 
1161
1260
  // scripts/agent/interactive-ask.ts
1162
- import { z as z4 } from "zod";
1261
+ import { z as z5 } from "zod";
1163
1262
  var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion"]);
1164
1263
  function is_interactive_tool(name) {
1165
1264
  return INTERACTIVE_TOOLS.has(name);
1166
1265
  }
1167
- var OPTION_SCHEMA = z4.looseObject({ label: z4.string().optional() });
1168
- var QUESTION_SCHEMA = z4.looseObject({
1169
- question: z4.string().optional(),
1170
- options: z4.array(OPTION_SCHEMA).optional()
1266
+ var OPTION_SCHEMA = z5.looseObject({ label: z5.string().optional() });
1267
+ var QUESTION_SCHEMA = z5.looseObject({
1268
+ question: z5.string().optional(),
1269
+ options: z5.array(OPTION_SCHEMA).optional()
1171
1270
  });
1172
- var ASK_INPUT_SCHEMA = z4.looseObject({ questions: z4.array(QUESTION_SCHEMA).optional() });
1173
- var DENIAL_SCHEMA = z4.looseObject({
1174
- tool_name: z4.string().optional(),
1175
- tool_input: z4.unknown().optional()
1271
+ var ASK_INPUT_SCHEMA = z5.looseObject({ questions: z5.array(QUESTION_SCHEMA).optional() });
1272
+ var DENIAL_SCHEMA = z5.looseObject({
1273
+ tool_name: z5.string().optional(),
1274
+ tool_input: z5.unknown().optional()
1176
1275
  });
1177
1276
  var UNKNOWN_QUESTION = "(question text unavailable)";
1178
1277
  var OPTION_SEPARATOR = " / ";
@@ -1198,7 +1297,7 @@ function refused_ask_of(permission_denials) {
1198
1297
  if (questions.length === 0) return void 0;
1199
1298
  return questions.map((question) => render_question(question)).join(QUESTION_SEPARATOR);
1200
1299
  }
1201
- var interactive_ask = { INTERACTIVE_TOOLS, is_interactive_tool, refused_ask_of };
1300
+ var interactive_ask = { is_interactive_tool, refused_ask_of };
1202
1301
 
1203
1302
  // scripts/rules/lane-interactive-ask.ts
1204
1303
  function is_interactive_ask(call, is_lane_child = lane_child_marker.is_child_of(process.cwd())) {
@@ -1257,6 +1356,108 @@ var lane_switch_main = {
1257
1356
  switch_targets
1258
1357
  };
1259
1358
 
1359
+ // scripts/run/run-stage.ts
1360
+ var KICKOFF = "kickoff";
1361
+ var HALFRUN = "halfrun";
1362
+ var PRRUN = "prrun";
1363
+ var FULLRUN = "fullrun";
1364
+ var COMMANDS = [KICKOFF, HALFRUN, PRRUN, FULLRUN];
1365
+ var DEFAULT_COMMAND = FULLRUN;
1366
+ var FRESH = "fresh";
1367
+ var PLANNED = "planned";
1368
+ var HALFRUN_STOPPED = "halfrun-stopped";
1369
+ var PRRUN_STOPPED = "prrun-stopped";
1370
+ var MERGED = "merged";
1371
+ var STATES = [FRESH, PLANNED, HALFRUN_STOPPED, PRRUN_STOPPED, MERGED];
1372
+ var REACHED = "reached";
1373
+ var STARTS = {
1374
+ [FRESH]: "plan",
1375
+ [PLANNED]: "implement",
1376
+ [HALFRUN_STOPPED]: "gate",
1377
+ [PRRUN_STOPPED]: "followup",
1378
+ [MERGED]: REACHED
1379
+ };
1380
+ var START_TOKENS = [...new Set(Object.values(STARTS))];
1381
+ var ENDS = {
1382
+ [KICKOFF]: "plan posted",
1383
+ [HALFRUN]: "gated, before the commit",
1384
+ [PRRUN]: "green, mergeable PR",
1385
+ [FULLRUN]: "merged"
1386
+ };
1387
+ var NEXT_PREFIX = "Next: ";
1388
+ var NEXT_SEPARATOR = " | ";
1389
+ var STATE_HEADER = "Issue state";
1390
+ var CELL_ARROW = " \u2192 ";
1391
+ var REACHED_CELL = "reached \u2014 report and stop";
1392
+ function to_command(value) {
1393
+ return COMMANDS.find((command) => command === value);
1394
+ }
1395
+ function state_of(facts) {
1396
+ if (facts.is_closed) return MERGED;
1397
+ if (facts.is_prrun_stopped) return PRRUN_STOPPED;
1398
+ if (facts.is_halfrun_stopped) return HALFRUN_STOPPED;
1399
+ return facts.is_planned ? PLANNED : FRESH;
1400
+ }
1401
+ function decide3(state, command) {
1402
+ const is_reached = STATES.indexOf(state) > COMMANDS.indexOf(command);
1403
+ const start = is_reached ? REACHED : STARTS[state];
1404
+ return { state, command, start, end: ENDS[command], is_reached };
1405
+ }
1406
+ function next_commands(command) {
1407
+ return COMMANDS.slice(COMMANDS.indexOf(command) + 1);
1408
+ }
1409
+ function next_line(command, issue) {
1410
+ const commands = next_commands(command).map((next) => `${next} #${issue}`);
1411
+ return `${NEXT_PREFIX}${commands.join(NEXT_SEPARATOR)}`;
1412
+ }
1413
+ function format_decision(issue, decision) {
1414
+ const fields = [`at: ${decision.state}`, `to: ${decision.command}`, `start: ${decision.start}`];
1415
+ return `stage #${issue} \u2014 ${fields.join(" \xB7 ")}`;
1416
+ }
1417
+ function cell(state, command) {
1418
+ const decision = decide3(state, command);
1419
+ return decision.is_reached ? REACHED_CELL : `${decision.start}${CELL_ARROW}${decision.end}`;
1420
+ }
1421
+ function table_rows() {
1422
+ const header = [STATE_HEADER, ...COMMANDS.map((command) => `\`${command}\``)];
1423
+ const rows = STATES.map((state) => [
1424
+ `\`${state}\``,
1425
+ ...COMMANDS.map((command) => cell(state, command))
1426
+ ]);
1427
+ return [header, ...rows];
1428
+ }
1429
+ var run_stage = {
1430
+ COMMANDS,
1431
+ DEFAULT_COMMAND,
1432
+ FRESH,
1433
+ HALFRUN,
1434
+ HALFRUN_STOPPED,
1435
+ KICKOFF,
1436
+ MERGED,
1437
+ PLANNED,
1438
+ PRRUN,
1439
+ PRRUN_STOPPED,
1440
+ REACHED,
1441
+ START_TOKENS,
1442
+ STATES,
1443
+ decide: decide3,
1444
+ format_decision,
1445
+ next_commands,
1446
+ next_line,
1447
+ state_of,
1448
+ table_rows,
1449
+ to_command
1450
+ };
1451
+
1452
+ // scripts/rules/decision-oracle-stage.ts
1453
+ var RUN_ENTRY_ORACLE = {
1454
+ name: "run:entry",
1455
+ decision: "Where a run on an issue starts, and whether the typed command has already been reached",
1456
+ args: "<N> [--to kickoff|halfrun|prrun|fullrun]",
1457
+ vocabulary: run_stage.START_TOKENS,
1458
+ single_source: ".claude/skills/workflow-commands/SKILL.md \u2192 \xA72k"
1459
+ };
1460
+
1260
1461
  // scripts/rules/decision-oracle.ts
1261
1462
  var CHAIN_RULE_MD = ".claude/skills/workflow-commands/chain-rule.md";
1262
1463
  var BACKLOGRUN_PROGRESS_MD = ".claude/skills/workflow-commands/backlogrun-progress.md";
@@ -1264,11 +1465,11 @@ var SKILL_2E = ".claude/skills/workflow-commands/SKILL.md \u2192 \xA72e";
1264
1465
  var SPLIT_ASSESSMENT_QUESTION = ".claude/skills/workflow-commands/split-assessment.md \u2192 The question";
1265
1466
  var PRE_GATE_CUT_MD = ".claude/skills/workflow-commands/pre-gate-cut.md";
1266
1467
  var BACKLOGRUN_MD = ".claude/skills/workflow-commands/backlogrun.md";
1267
- var RUN_MERGE_REFERENCE = "docs/josh-commands.md \u2192 `josh run:merge`";
1268
- var EPIC_NEXT_REFERENCE = "docs/josh-commands.md \u2192 `josh epic:next`";
1269
- var BACKLOG_BUDGET_REFERENCE = "docs/josh-commands.md \u2192 `josh backlog:budget`";
1270
- var RUN_LIVENESS_REFERENCE = "docs/josh-commands.md \u2192 `josh run:liveness`";
1271
- var AUTO_OK_NEXT_REFERENCE = "docs/josh-commands.md \u2192 `josh auto-ok:next`";
1468
+ var RUN_MERGE_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh run:merge`";
1469
+ var EPIC_NEXT_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh epic:next`";
1470
+ var BACKLOG_BUDGET_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh backlog:budget`";
1471
+ var RUN_LIVENESS_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh run:liveness`";
1472
+ var AUTO_OK_NEXT_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh auto-ok:next`";
1272
1473
  var RUN_CUT_CMD = "run:cut";
1273
1474
  var REQUIRED = "required";
1274
1475
  var SKIP = "skip";
@@ -1520,15 +1721,16 @@ var DECISION_ORACLES = [
1520
1721
  decision: "Whether an epic's declaration matches its recorded relations, and the repair when it does not",
1521
1722
  args: "--reconcile <E>",
1522
1723
  vocabulary: [RECONCILED, NOTHING_TO_RECONCILE],
1523
- single_source: "docs/josh-commands.md \u2192 `josh epic --reconcile`"
1724
+ single_source: "docs/josh-commands-automation.md \u2192 `josh epic --reconcile`"
1524
1725
  },
1525
1726
  {
1526
1727
  name: "lane:list",
1527
1728
  decision: "Whether each in-progress issue holds a live lane, and the two-directional difference",
1528
1729
  args: "",
1529
1730
  vocabulary: [LIVE, STOPPED, UNKNOWN],
1530
- single_source: "docs/josh-commands.md \u2192 The `in-progress` / lane difference"
1531
- }
1731
+ single_source: "docs/josh-commands-automation.md \u2192 The `in-progress` / lane difference"
1732
+ },
1733
+ RUN_ENTRY_ORACLE
1532
1734
  ];
1533
1735
  function get_command(oracle) {
1534
1736
  return oracle.command ?? oracle.name;
@@ -1591,6 +1793,7 @@ var NOT_NAMED = /* @__PURE__ */ new Map([
1591
1793
  ["epic:next", READ],
1592
1794
  ["run:merge", PHASE],
1593
1795
  ["run:step", SELF],
1796
+ ["run:entry", SELF],
1594
1797
  ["refactor:scan", PHASE],
1595
1798
  ["split:assess", PHASE],
1596
1799
  ["sonar:hotspots", READ],
@@ -1643,7 +1846,330 @@ function row_for(oracle, firing_point) {
1643
1846
  };
1644
1847
  }
1645
1848
  var ROWS = oracle_firing.firing_oracles().map(({ oracle, firing_point }) => row_for(oracle, firing_point));
1646
- var oracle_consulted = { ROWS, command_line, reason_for, row_for, runs_the_oracle };
1849
+ var oracle_consulted = { ROWS, reason_for };
1850
+
1851
+ // scripts/rules/destructive-command.ts
1852
+ var RM_COMMAND = "rm";
1853
+ var RECURSIVE_LONG = "--recursive";
1854
+ var RECURSIVE_LETTERS = "rR";
1855
+ var FORCE_LONG = "--force";
1856
+ var FORCE_LETTER = "f";
1857
+ function has_flag(args, long, letters) {
1858
+ return args.some((argument) => argument === long || git_argv.short_cluster_has(argument, letters));
1859
+ }
1860
+ function is_recursive_forced_rm(segment) {
1861
+ const args = git_argv.arguments_of(segment, RM_COMMAND);
1862
+ if (args === void 0) return false;
1863
+ return has_flag(args, RECURSIVE_LONG, RECURSIVE_LETTERS) && has_flag(args, FORCE_LONG, FORCE_LETTER);
1864
+ }
1865
+ var GH_COMMAND = "gh";
1866
+ var REPO_GROUP2 = "repo";
1867
+ var REPO_REMOVALS = /* @__PURE__ */ new Set(["delete", "archive"]);
1868
+ var REPO_EDIT = "edit";
1869
+ var VISIBILITY_FLAG = "--visibility";
1870
+ var PR_GROUP = "pr";
1871
+ var PR_CLOSE = "close";
1872
+ function is_destructive_repo_action(action, args) {
1873
+ if (REPO_REMOVALS.has(action)) return true;
1874
+ return action === REPO_EDIT && args.some((argument) => argument.startsWith(VISIBILITY_FLAG));
1875
+ }
1876
+ function is_destructive_gh_command(args) {
1877
+ const [group, action = ""] = args;
1878
+ if (group === PR_GROUP) return action === PR_CLOSE;
1879
+ return group === REPO_GROUP2 && is_destructive_repo_action(action, args);
1880
+ }
1881
+ var DELETE_METHOD = "DELETE";
1882
+ var REPOSITORY_PATH = "repos/";
1883
+ var ISSUE_LABEL_PATH = /^['"]?\/?repos\/[^/\s'"]+\/[^/\s'"]+\/issues\/\d+\/labels\/[^/\s'"]+['"]?$/u;
1884
+ function is_destructive_api_call(args) {
1885
+ const call = [GH_COMMAND, ...args].join(" ");
1886
+ if (!gh_api.is_gh_api(call) || gh_api.method_of(call) !== DELETE_METHOD) return false;
1887
+ return args.some(
1888
+ (argument) => argument.includes(REPOSITORY_PATH) && !ISSUE_LABEL_PATH.test(argument)
1889
+ );
1890
+ }
1891
+ function is_destructive_gh(segment) {
1892
+ const args = git_argv.arguments_of(segment, GH_COMMAND);
1893
+ if (args === void 0) return false;
1894
+ return is_destructive_gh_command(args) || is_destructive_api_call(args);
1895
+ }
1896
+ function is_destructive_segment2(segment) {
1897
+ return is_recursive_forced_rm(segment) || is_destructive_gh(segment);
1898
+ }
1899
+ function is_destructive_command(command) {
1900
+ return shell_segments.segments_of(command).some((segment) => is_destructive_segment2(segment));
1901
+ }
1902
+ var DESTRUCTIVE_COMMAND_REASON = "\u26D4 destructive command: a recursive forced `rm` (`rm -rf`, `rm -fr`, `rm -r -f`, `rm --recursive --force`), `gh repo delete` / `archive`, `gh repo edit --visibility`, `gh pr close`, or a `gh api -X DELETE repos/\u2026` call deletes or closes something that cannot be restored, which is Tier C and needs explicit current-turn user instruction (`prompts/collaboration-workflow/operating-rules.md`). The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user to run the command in their own terminal. `gh issue close` and removing a label from an Issue (`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every occurrence, not once per run.**";
1903
+ var ROW10 = {
1904
+ id: "destructive-command",
1905
+ is_trigger: bash_triggers.on_bash_command(is_destructive_command),
1906
+ reason: DESTRUCTIVE_COMMAND_REASON,
1907
+ decide: () => true
1908
+ };
1909
+ var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW10, is_destructive_command };
1910
+
1911
+ // scripts/rules/worktree-guard.ts
1912
+ var CHECKOUT_SUBCOMMAND = "checkout";
1913
+ var RESTORE_SUBCOMMAND = "restore";
1914
+ var STASH_SUBCOMMAND = "stash";
1915
+ var PATHSPEC_SEPARATOR = "--";
1916
+ function is_worktree_checkout(args) {
1917
+ return args.includes(PATHSPEC_SEPARATOR);
1918
+ }
1919
+ var STAGED_LONG = "--staged";
1920
+ var STAGED_LETTER = "S";
1921
+ var WORKTREE_LONG = "--worktree";
1922
+ var WORKTREE_LETTER = "W";
1923
+ function touches_worktree(args) {
1924
+ const is_staged = args.some(
1925
+ (argument) => argument === STAGED_LONG || git_argv.short_cluster_has(argument, STAGED_LETTER)
1926
+ );
1927
+ const is_worktree = args.some(
1928
+ (argument) => argument === WORKTREE_LONG || git_argv.short_cluster_has(argument, WORKTREE_LETTER)
1929
+ );
1930
+ return !is_staged || is_worktree;
1931
+ }
1932
+ var PUSH_STASH = "push";
1933
+ var MESSAGE_LONG = "--message";
1934
+ var MESSAGE_SHORT_LETTER = "m";
1935
+ var READ_ONLY_STASH = /* @__PURE__ */ new Set(["list", "show"]);
1936
+ function has_message(args) {
1937
+ return args.some(
1938
+ (argument) => argument === MESSAGE_LONG || argument.startsWith(`${MESSAGE_LONG}=`) || git_argv.short_cluster_has(argument, MESSAGE_SHORT_LETTER)
1939
+ );
1940
+ }
1941
+ function is_unauthorized_stash(args) {
1942
+ const [stash_subcommand, ...rest] = args;
1943
+ if (stash_subcommand === void 0) return true;
1944
+ if (READ_ONLY_STASH.has(stash_subcommand)) return false;
1945
+ if (stash_subcommand === PUSH_STASH) return !has_message(rest);
1946
+ return true;
1947
+ }
1948
+ var CLEAN_SUBCOMMAND = "clean";
1949
+ var FORCE_LONG2 = "--force";
1950
+ var FORCE_LETTER2 = "f";
1951
+ function is_forced_clean(args) {
1952
+ return args.some(
1953
+ (argument) => argument === FORCE_LONG2 || git_argv.short_cluster_has(argument, FORCE_LETTER2)
1954
+ );
1955
+ }
1956
+ var JUDGED_SUBCOMMANDS = /* @__PURE__ */ new Map([
1957
+ [CHECKOUT_SUBCOMMAND, is_worktree_checkout],
1958
+ [RESTORE_SUBCOMMAND, touches_worktree],
1959
+ [CLEAN_SUBCOMMAND, is_forced_clean],
1960
+ [STASH_SUBCOMMAND, is_unauthorized_stash]
1961
+ ]);
1962
+ function is_unauthorized_segment(segment) {
1963
+ const call = git_argv.parse(segment);
1964
+ if (call === void 0) return false;
1965
+ return JUDGED_SUBCOMMANDS.get(call.subcommand ?? "")?.(call.args) === true;
1966
+ }
1967
+ function is_unauthorized_worktree_change(command) {
1968
+ return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
1969
+ }
1970
+ var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>`, a forced `git clean` and an unauthorized `git stash` discard work or mutate the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
1971
+ var ROW11 = {
1972
+ id: "worktree-mutation",
1973
+ is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
1974
+ reason: WORKTREE_MUTATION_REASON,
1975
+ decide: () => true
1976
+ };
1977
+ var worktree_guard = {
1978
+ ROW: ROW11,
1979
+ WORKTREE_MUTATION_REASON,
1980
+ is_unauthorized_worktree_change,
1981
+ touches_worktree
1982
+ };
1983
+
1984
+ // scripts/rules/index-guard.ts
1985
+ var INDEX_SUBCOMMANDS = /* @__PURE__ */ new Set([
1986
+ "add",
1987
+ "commit",
1988
+ "mv",
1989
+ "reset",
1990
+ "rm",
1991
+ "stage"
1992
+ ]);
1993
+ var RESTORE_SUBCOMMAND2 = "restore";
1994
+ function is_index_restore(args) {
1995
+ return !worktree_guard.touches_worktree(args);
1996
+ }
1997
+ function is_index_segment(segment) {
1998
+ const call = git_argv.parse(segment);
1999
+ if (call === void 0) return false;
2000
+ if (INDEX_SUBCOMMANDS.has(call.subcommand ?? "")) return true;
2001
+ return call.subcommand === RESTORE_SUBCOMMAND2 && is_index_restore(call.args);
2002
+ }
2003
+ function is_index_mutation(command) {
2004
+ return shell_segments.segments_of(command).some((segment) => is_index_segment(segment));
2005
+ }
2006
+ var INDEX_MUTATION_REASON = '\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user\'s staged snapshot, which `CLAUDE.md` \u2192 Git Rules forbids on your own judgement (`prompts/collaboration-workflow/operating-rules.md` \u2192 "git index \u3092\u52DD\u624B\u306B\u5909\u66F4\u3057\u306A\u3044"). The `.claude/settings.json` deny list matches prefix globs, so it misses `git -C . commit`, `git -c k=v commit` and `env git commit`; this row reads the argv instead, so the spelling does not matter. A commit goes through `pnpm josh git -y`; any other staging needs explicit current-turn user instruction, run by the user in their own terminal. **This rule fires on every occurrence, not once per run.**';
2007
+ var ROW12 = {
2008
+ id: "index-mutation",
2009
+ is_trigger: bash_triggers.on_bash_command(is_index_mutation),
2010
+ reason: INDEX_MUTATION_REASON,
2011
+ decide: () => true
2012
+ };
2013
+ var index_guard = { INDEX_MUTATION_REASON, ROW: ROW12, is_index_mutation };
2014
+
2015
+ // scripts/rules/protected-files.ts
2016
+ import path5 from "node:path";
2017
+
2018
+ // scripts/doctor/doctor-consumer.ts
2019
+ import { existsSync as existsSync2 } from "node:fs";
2020
+ import path4 from "node:path";
2021
+
2022
+ // scripts/version/kit-descriptor.ts
2023
+ var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
2024
+
2025
+ // scripts/doctor/doctor-consumer.ts
2026
+ import { z as z6 } from "zod";
2027
+ var NODE_MODULES = "node_modules";
2028
+ var SETTINGS_PATH = path4.join(".claude", "settings.json");
2029
+ var CLAUDE_MD = "CLAUDE.md";
2030
+ var PLUGIN_ID = "kit@kit";
2031
+ var CLAUDE_MD_POINTER = `@${NODE_MODULES}/${KIT_PACKAGE_NAME}/dist/${CLAUDE_MD}`;
2032
+ var SECRETLINT_BIN = path4.join(NODE_MODULES, ".bin", "secretlint");
2033
+ var GIT_TIMEOUT_MS = 2e3;
2034
+ var manifest_schema = z6.object({
2035
+ name: z6.string().optional(),
2036
+ dependencies: z6.record(z6.string(), z6.string()).optional(),
2037
+ devDependencies: z6.record(z6.string(), z6.string()).optional()
2038
+ });
2039
+ var settings_schema = z6.object({
2040
+ enabledPlugins: z6.record(z6.string(), z6.boolean()).optional()
2041
+ });
2042
+ var STATE_OK = "ok";
2043
+ var STATE_TARGET_MISSING = "target-missing";
2044
+ var STATE_MISSING = "missing";
2045
+ function parse_manifest(root) {
2046
+ const content = file_reader.read_if_readable(path4.join(root, "package.json"));
2047
+ if (content === void 0) return void 0;
2048
+ const parsed = manifest_schema.safeParse(json_value.parse_or_undefined(content));
2049
+ return parsed.success ? parsed.data : void 0;
2050
+ }
2051
+ function lists_kit_dependency(manifest) {
2052
+ if (manifest === void 0) return false;
2053
+ return Object.hasOwn(manifest.dependencies ?? {}, KIT_PACKAGE_NAME) || Object.hasOwn(manifest.devDependencies ?? {}, KIT_PACKAGE_NAME);
2054
+ }
2055
+ function is_kit_consumer(root) {
2056
+ const manifest = parse_manifest(root);
2057
+ if (manifest?.name === KIT_PACKAGE_NAME) return false;
2058
+ return lists_kit_dependency(manifest) || existsSync2(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME));
2059
+ }
2060
+ function is_plugin_declared(root) {
2061
+ const content = file_reader.read_if_readable(path4.join(root, SETTINGS_PATH));
2062
+ if (content === void 0) return false;
2063
+ const parsed = settings_schema.safeParse(json_value.parse_or_undefined(content));
2064
+ return parsed.success && parsed.data.enabledPlugins?.[PLUGIN_ID] === true;
2065
+ }
2066
+ function configured_hooks_path(root) {
2067
+ const value = git_spawn_sync.read(["-C", root, "config", "--get", "core.hooksPath"], {
2068
+ timeout: GIT_TIMEOUT_MS
2069
+ });
2070
+ return value === "" ? void 0 : value;
2071
+ }
2072
+ function claude_md_state(root) {
2073
+ const content = file_reader.read_if_readable(path4.join(root, CLAUDE_MD));
2074
+ if (content === void 0) return STATE_MISSING;
2075
+ if (!content.includes(CLAUDE_MD_POINTER)) return STATE_OK;
2076
+ return existsSync2(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME, "dist", CLAUDE_MD)) ? STATE_OK : STATE_TARGET_MISSING;
2077
+ }
2078
+ function is_secretlint_runnable(root) {
2079
+ return existsSync2(path4.join(root, SECRETLINT_BIN));
2080
+ }
2081
+ function status_line(is_ok, ok_text, warn_text) {
2082
+ return is_ok ? ` \u2713 ${ok_text}` : ` \u26A0 ${warn_text}`;
2083
+ }
2084
+ function plugin_line(is_declared2) {
2085
+ return status_line(
2086
+ is_declared2,
2087
+ "kit plugin declared (skills load in a trusted workspace; headless from its second session)",
2088
+ "kit plugin not declared in .claude/settings.json \u2014 run `pnpm josh sync`"
2089
+ );
2090
+ }
2091
+ function hooks_path_line(hooks_path) {
2092
+ return status_line(
2093
+ hooks_path === void 0,
2094
+ "git hooks install normally (core.hooksPath unset)",
2095
+ `core.hooksPath is set (${hooks_path ?? ""}) \u2014 lefthook installs no git hooks; unset it`
2096
+ );
2097
+ }
2098
+ function claude_md_line(state) {
2099
+ if (state === STATE_OK) return " \u2713 CLAUDE.md resolves to the installed kit rules";
2100
+ if (state === STATE_TARGET_MISSING) {
2101
+ return " \u26A0 CLAUDE.md points at rules that are not installed \u2014 run `pnpm install`";
2102
+ }
2103
+ return " \u26A0 CLAUDE.md is missing \u2014 run `pnpm josh init`";
2104
+ }
2105
+ function secretlint_line(is_runnable) {
2106
+ return status_line(
2107
+ is_runnable,
2108
+ "secretlint is runnable",
2109
+ "secretlint is not installed \u2014 run `pnpm install`"
2110
+ );
2111
+ }
2112
+ function consumer_setup_lines(root) {
2113
+ return [
2114
+ plugin_line(is_plugin_declared(root)),
2115
+ hooks_path_line(configured_hooks_path(root)),
2116
+ claude_md_line(claude_md_state(root)),
2117
+ secretlint_line(is_secretlint_runnable(root))
2118
+ ];
2119
+ }
2120
+ function report_consumer_setup(root) {
2121
+ if (!is_kit_consumer(root)) return;
2122
+ console.info("");
2123
+ console.info("Consumer setup:");
2124
+ for (const line of consumer_setup_lines(root)) console.info(line);
2125
+ }
2126
+ var doctor_consumer = {
2127
+ claude_md_line,
2128
+ claude_md_state,
2129
+ configured_hooks_path,
2130
+ hooks_path_line,
2131
+ is_kit_consumer,
2132
+ is_plugin_declared,
2133
+ is_secretlint_runnable,
2134
+ plugin_line,
2135
+ report_consumer_setup,
2136
+ secretlint_line
2137
+ };
2138
+
2139
+ // scripts/rules/protected-files.ts
2140
+ var READ_TOOL = "Read";
2141
+ var EDIT_TOOLS2 = /* @__PURE__ */ new Set(["Edit", "Write"]);
2142
+ var ENVIRONMENT_FILE = ".env";
2143
+ var SETTINGS_FILE = path5.join(".claude", "settings.json");
2144
+ function file_path_of(call) {
2145
+ if (!json_value.is_record(call.input)) return "";
2146
+ const value = call.input["file_path"];
2147
+ return typeof value === "string" ? value : "";
2148
+ }
2149
+ function is_environment_read(call) {
2150
+ return call.name === READ_TOOL && path5.basename(file_path_of(call)) === ENVIRONMENT_FILE;
2151
+ }
2152
+ function is_consumer_settings_edit(call, is_kit_consumer2) {
2153
+ if (!EDIT_TOOLS2.has(call.name)) return false;
2154
+ const file_path = path5.resolve(file_path_of(call));
2155
+ if (!file_path.endsWith(`${path5.sep}${SETTINGS_FILE}`)) return false;
2156
+ return is_kit_consumer2(path5.dirname(path5.dirname(file_path)));
2157
+ }
2158
+ function is_protected_file_call(call, is_kit_consumer2 = doctor_consumer.is_kit_consumer) {
2159
+ return is_environment_read(call) || is_consumer_settings_edit(call, is_kit_consumer2);
2160
+ }
2161
+ var PROTECTED_FILE_REASON = '\u26D4 protected file: `.env` holds the Telegram bot token, so reading it puts a secret in the transcript \u2014 the scripts that need it load it in their own process. `.claude/settings.json` in a consumer repository is the deny list and the hooks every other rule rests on, so a run must not rewrite it; `josh sync` owns it, and a change belongs upstream in kit (`CLAUDE.md` \u2192 "Route distributed-doc / config changes upstream to kit"). Leave both files alone, or ask the user. **This rule fires on every occurrence, not once per run.**';
2162
+ var ROW13 = {
2163
+ id: "protected-file",
2164
+ is_trigger: is_protected_file_call,
2165
+ reason: PROTECTED_FILE_REASON,
2166
+ decide: () => true
2167
+ };
2168
+ var protected_files = { ROW: ROW13, is_protected_file_call };
2169
+
2170
+ // scripts/rules/permission-guards.ts
2171
+ var ROWS2 = [index_guard.ROW, destructive_command.ROW, protected_files.ROW];
2172
+ var permission_guards = { ROWS: ROWS2 };
1647
2173
 
1648
2174
  // scripts/rules/piped-verification.ts
1649
2175
  var VERIFICATION_COMMANDS = /* @__PURE__ */ new Set([
@@ -1713,7 +2239,7 @@ function is_poll_loop(command) {
1713
2239
  return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain);
1714
2240
  }
1715
2241
  var POLL_LOOP_REASON = '\u26D4 output poll loop: this hand-writes a `while`/`until` wait loop over a command\'s output, a marker file or a process \u2014 with a `sleep` or, worse, without one \u2014 and the loop does not know when the command finished. A regex that never matches waits to the harness block limit; one that matches a mid-run line exits too early \u2014 either way the command exit is not what ends the wait, and each pass spends a full-context round trip (joshuafolkken/kit#2371 measured about 45 minutes, 13% of six lanes, lost to exactly this). Do not poll the output file. A command known to take minutes \u2014 `pnpm josh gate`, `pnpm josh followup`, `pnpm josh format` \u2014 is started detached from the first with `run_in_background` set, and the harness re-invokes you when it exits: its completion notification is what ends the wait, on the task exit rather than a regex or the block limit. So issue the long command in the background and end the turn \u2014 do not arm a loop; the wake-up brings you back. To glance at interim output, `Read` the output file once, never in a sleep loop. If you are waiting on CI, `pnpm josh followup` waits on it for you. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every occurrence, not once per run.**';
1716
- var ROW10 = {
2242
+ var ROW14 = {
1717
2243
  id: "poll-loop",
1718
2244
  is_trigger: bash_triggers.on_bash_command(is_poll_loop),
1719
2245
  reason: POLL_LOOP_REASON,
@@ -1721,7 +2247,7 @@ var ROW10 = {
1721
2247
  };
1722
2248
  var poll_loop = {
1723
2249
  POLL_LOOP_REASON,
1724
- ROW: ROW10,
2250
+ ROW: ROW14,
1725
2251
  is_poll_loop
1726
2252
  };
1727
2253
 
@@ -1841,13 +2367,13 @@ function posts_at_path_literally(command) {
1841
2367
  return RAW_FIELD_BODY_AT.test(command);
1842
2368
  }
1843
2369
  var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which sends the value verbatim \u2014 the string `@<path>` is posted as the comment, not the file it points at. `-F` / `--field` is the flag that reads the file, and the two spellings differ by one character while `gh` exits 0 and returns a comment URL either way, so the break is invisible until a person reads the issue (joshuafolkken/kit#2304 lost two park comments this way). Post an issue or PR comment with `pnpm josh issue:comment <N> --body-file <path>` \u2014 the one spelling, which cannot take the wrong flag. The rule and the safe forms are in `prompts/collaboration-workflow/shell-body.md`. This rule fires on every occurrence, not once per run.";
1844
- var ROW11 = {
2370
+ var ROW15 = {
1845
2371
  id: "raw-field-body",
1846
2372
  is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
1847
2373
  reason: RAW_FIELD_BODY_REASON,
1848
2374
  decide: () => true
1849
2375
  };
1850
- var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW11, posts_at_path_literally };
2376
+ var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW15, posts_at_path_literally };
1851
2377
 
1852
2378
  // scripts/rules/rule-body-guard.ts
1853
2379
  var EDIT_TOOL = "Edit";
@@ -1918,7 +2444,7 @@ function runs_placement_oracle(command) {
1918
2444
  (names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
1919
2445
  );
1920
2446
  }
1921
- var ROW12 = {
2447
+ var ROW16 = {
1922
2448
  id: "rule-body",
1923
2449
  is_trigger: writes_rule_prose,
1924
2450
  reason: RULE_BODY_REASON,
@@ -1927,12 +2453,9 @@ var ROW12 = {
1927
2453
  };
1928
2454
  var rule_body_guard = {
1929
2455
  RULE_BODY_REASON,
1930
- ROW: ROW12,
1931
- adds_rule_prose,
2456
+ ROW: ROW16,
1932
2457
  answered_placement,
1933
2458
  is_rule_document,
1934
- prose_of,
1935
- runs_placement_oracle,
1936
2459
  writes_rule_prose
1937
2460
  };
1938
2461
 
@@ -2002,27 +2525,17 @@ var SHELL_BODY_REASON = "\u26D4 shell-evaluated body: this command carries a bod
2002
2525
  var shell_body_trigger = {
2003
2526
  SHELL_BODY_REASON,
2004
2527
  carries_a_body,
2005
- is_safe_body_form,
2006
2528
  is_shell_evaluated_body,
2007
2529
  keeps_body_safe
2008
2530
  };
2009
2531
 
2010
- // scripts/test/test-declared-changed.ts
2011
- import { execFileSync } from "node:child_process";
2012
-
2013
2532
  // scripts/gate/project-checks.ts
2014
- import { existsSync as existsSync3, globSync as globSync2, readFileSync as readFileSync4 } from "node:fs";
2015
- import path5 from "node:path";
2016
-
2017
- // scripts/init/project-profile.ts
2018
- import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3 } from "node:fs";
2019
- import path4 from "node:path";
2020
-
2021
- // scripts/version/kit-descriptor.ts
2022
- var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
2533
+ import { existsSync as existsSync4, globSync as globSync2, readFileSync as readFileSync4 } from "node:fs";
2534
+ import path7 from "node:path";
2023
2535
 
2024
2536
  // scripts/init/project-profile.ts
2025
- import { execaSync } from "execa";
2537
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "node:fs";
2538
+ import path6 from "node:path";
2026
2539
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".svelte-kit"]);
2027
2540
  var WEB_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".css", ".js", ".jsx", ".mjs", ".cjs"]);
2028
2541
  var TYPESCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx"]);
@@ -2054,26 +2567,21 @@ function requested_profile(args, usage = "josh init [--profile basic|full]") {
2054
2567
  throw new Error("Profile must be basic or full");
2055
2568
  }
2056
2569
  function read_manifest(root) {
2057
- const filename = path4.join(root, "package.json");
2058
- if (!existsSync2(filename)) return void 0;
2570
+ const filename = path6.join(root, "package.json");
2571
+ if (!existsSync3(filename)) return void 0;
2059
2572
  const parsed = JSON.parse(readFileSync3(filename, "utf8"));
2060
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2061
- throw new Error("package.json must contain an object");
2062
- }
2573
+ if (!json_value.is_record(parsed)) throw new Error("package.json must contain an object");
2063
2574
  return parsed;
2064
2575
  }
2065
- function is_record(value) {
2066
- return typeof value === "object" && value !== null && !Array.isArray(value);
2067
- }
2068
2576
  function recorded_profile(manifest) {
2069
2577
  const { josh } = manifest;
2070
- return is_record(josh) ? parse_profile(josh["profile"]) : void 0;
2578
+ return json_value.is_record(josh) ? parse_profile(josh["profile"]) : void 0;
2071
2579
  }
2072
2580
  function has_build_script(value) {
2073
- return is_record(value) && ("build" in value || "dev" in value);
2581
+ return json_value.is_record(value) && ("build" in value || "dev" in value);
2074
2582
  }
2075
2583
  function has_project_dependencies(value) {
2076
- if (!is_record(value)) return false;
2584
+ if (!json_value.is_record(value)) return false;
2077
2585
  return Object.keys(value).some((name) => name !== KIT_PACKAGE_NAME);
2078
2586
  }
2079
2587
  function inferred_profile(manifest) {
@@ -2099,9 +2607,9 @@ function should_visit(entry) {
2099
2607
  function scan_directory(directory, pending) {
2100
2608
  const entries = readdirSync(directory, { withFileTypes: true });
2101
2609
  pending.push(
2102
- ...entries.filter((entry) => should_visit(entry)).map((entry) => path4.join(directory, entry.name))
2610
+ ...entries.filter((entry) => should_visit(entry)).map((entry) => path6.join(directory, entry.name))
2103
2611
  );
2104
- return entries.filter((entry) => entry.isFile()).map((entry) => path4.extname(entry.name));
2612
+ return entries.filter((entry) => entry.isFile()).map((entry) => path6.extname(entry.name));
2105
2613
  }
2106
2614
  function file_extensions(root) {
2107
2615
  const pending = [root];
@@ -2118,16 +2626,13 @@ function scan_files(root) {
2118
2626
  return { has_web, has_typescript };
2119
2627
  }
2120
2628
  function has_github_remote(root) {
2121
- const result = execaSync("git", ["config", "--get", "remote.origin.url"], {
2122
- cwd: root,
2123
- reject: false
2124
- });
2125
- return result.exitCode === 0 && repo_origin.parse_origin_url(result.stdout) !== void 0;
2629
+ const url = git_spawn_sync.origin_url(root);
2630
+ return url !== void 0 && repo_origin.parse_origin_url(url) !== void 0;
2126
2631
  }
2127
2632
  function inspect_project(root, requested) {
2128
2633
  const profile = resolve_profile(root, requested);
2129
2634
  const files = scan_files(root);
2130
- const has_git = existsSync2(path4.join(root, ".git"));
2635
+ const has_git = existsSync3(path6.join(root, ".git"));
2131
2636
  return { ...profile, ...files, has_git, has_github: has_git && has_github_remote(root) };
2132
2637
  }
2133
2638
  var project_profile = {
@@ -2142,7 +2647,7 @@ var project_profile = {
2142
2647
  var SKIP_MARKER = "\u2014 skipping";
2143
2648
 
2144
2649
  // scripts/gate/project-checks.ts
2145
- import { z as z5 } from "zod";
2650
+ import { z as z7 } from "zod";
2146
2651
  var PACKAGE_JSON = "package.json";
2147
2652
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
2148
2653
  "node_modules",
@@ -2177,11 +2682,11 @@ var CSPELL_CONFIGS = [
2177
2682
  ];
2178
2683
  var TYPE_CONFIGS = ["tsconfig.json", "jsconfig.json"];
2179
2684
  var DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
2180
- var object_schema = z5.record(z5.string(), z5.unknown());
2685
+ var object_schema = z7.record(z7.string(), z7.unknown());
2181
2686
  function project_root(directory) {
2182
2687
  let current = directory;
2183
- while (!existsSync3(path5.join(current, PACKAGE_JSON))) {
2184
- const parent = path5.dirname(current);
2688
+ while (!existsSync4(path7.join(current, PACKAGE_JSON))) {
2689
+ const parent = path7.dirname(current);
2185
2690
  if (parent === current) return directory;
2186
2691
  current = parent;
2187
2692
  }
@@ -2194,24 +2699,23 @@ function has_files(directory, pattern) {
2194
2699
  return globSync2(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
2195
2700
  }
2196
2701
  function has_config(directory, names) {
2197
- return names.some((name) => existsSync3(path5.join(project_root(directory), name)));
2702
+ return names.some((name) => existsSync4(path7.join(project_root(directory), name)));
2198
2703
  }
2199
2704
  function has_bin(directory, bin_name) {
2200
2705
  return find_local_bin_upwards(directory, bin_name) !== void 0;
2201
2706
  }
2202
2707
  function josh_settings(manifest) {
2203
- if (typeof manifest !== "object" || manifest === null || !("josh" in manifest)) return void 0;
2204
- return manifest.josh;
2708
+ return json_value.is_record(manifest) ? manifest["josh"] : void 0;
2205
2709
  }
2206
2710
  function is_basic_manifest(manifest) {
2207
2711
  const josh = josh_settings(manifest);
2208
- if (typeof josh !== "object" || josh === null || !("profile" in josh)) return false;
2209
- return project_profile.parse_profile(josh.profile) === "basic";
2712
+ if (!json_value.is_record(josh)) return false;
2713
+ return project_profile.parse_profile(josh["profile"]) === "basic";
2210
2714
  }
2211
2715
  function read_manifest2(directory) {
2212
2716
  try {
2213
- const manifest_path = path5.join(project_root(directory), PACKAGE_JSON);
2214
- return JSON.parse(readFileSync4(manifest_path, "utf8"));
2717
+ const manifest_path = path7.join(project_root(directory), PACKAGE_JSON);
2718
+ return json_value.parse_or_undefined(readFileSync4(manifest_path, "utf8"));
2215
2719
  } catch {
2216
2720
  return void 0;
2217
2721
  }
@@ -2231,8 +2735,8 @@ function missing_tool_reason(directory, package_name, bin_name) {
2231
2735
  if (has_bin(directory, bin_name) || is_declared(directory, package_name)) return void 0;
2232
2736
  return `${package_name} is not installed`;
2233
2737
  }
2234
- function skip_notice(check3, reason) {
2235
- return `josh ${check3}: ${reason} ${SKIP_MARKER} ${check3}.`;
2738
+ function skip_notice(check4, reason) {
2739
+ return `josh ${check4}: ${reason} ${SKIP_MARKER} ${check4}.`;
2236
2740
  }
2237
2741
  function type_check_skip_reason(directory) {
2238
2742
  if (!is_basic(directory)) return void 0;
@@ -2254,8 +2758,6 @@ function eslint_skip_reason(directory) {
2254
2758
  var project_checks = {
2255
2759
  CSPELL_CONFIGS,
2256
2760
  ESLINT_CONFIGS,
2257
- SCRIPT_FILES,
2258
- TYPE_CONFIGS,
2259
2761
  TYPE_FILES,
2260
2762
  WEB_FILES,
2261
2763
  eslint_skip_reason,
@@ -2293,46 +2795,45 @@ var BASIC_REQUIRED_SUFFIXES = [
2293
2795
  ".toml"
2294
2796
  ];
2295
2797
  function normalize(paths) {
2296
- return paths.map((path7) => path7.trim()).filter((path7) => path7 !== "");
2798
+ return paths.map((path10) => path10.trim()).filter((path10) => path10 !== "");
2297
2799
  }
2298
- function is_test_file(path7) {
2299
- return TEST_SUFFIXES.some((suffix) => path7.endsWith(suffix));
2800
+ function is_test_file(path10) {
2801
+ return TEST_SUFFIXES.some((suffix) => path10.endsWith(suffix));
2300
2802
  }
2301
- function is_basic_manual(path7, is_basic2) {
2302
- const lower = path7.toLowerCase();
2803
+ function is_basic_manual(path10, is_basic2) {
2804
+ const lower = path10.toLowerCase();
2303
2805
  return is_basic2 && BASIC_REQUIRED_SUFFIXES.every((suffix) => !lower.endsWith(suffix));
2304
2806
  }
2305
- function is_basic_visual(path7) {
2306
- const lower = path7.toLowerCase();
2807
+ function is_basic_visual(path10) {
2808
+ const lower = path10.toLowerCase();
2307
2809
  return BASIC_VISUAL_SUFFIXES.some((suffix) => lower.endsWith(suffix));
2308
2810
  }
2309
- function is_exempt(path7, is_basic2 = false) {
2310
- if (EXEMPT_PATHS.includes(path7) || is_basic_manual(path7, is_basic2)) return true;
2311
- return EXEMPT_SUFFIXES.some((suffix) => path7.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path7.startsWith(prefix));
2811
+ function is_exempt(path10, is_basic2 = false) {
2812
+ if (EXEMPT_PATHS.includes(path10) || is_basic_manual(path10, is_basic2)) return true;
2813
+ return EXEMPT_SUFFIXES.some((suffix) => path10.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path10.startsWith(prefix));
2312
2814
  }
2313
- function is_runtime(path7, is_basic2 = false) {
2314
- return !is_test_file(path7) && !is_exempt(path7, is_basic2);
2815
+ function is_runtime(path10, is_basic2 = false) {
2816
+ return !is_test_file(path10) && !is_exempt(path10, is_basic2);
2315
2817
  }
2316
2818
  function verdict_for(paths, is_basic2 = false) {
2317
2819
  const changed = normalize(paths);
2318
- if (changed.some((path7) => is_test_file(path7))) return "satisfied";
2319
- if (changed.some((path7) => is_runtime(path7, is_basic2))) return "required";
2820
+ if (changed.some((path10) => is_test_file(path10))) return "satisfied";
2821
+ if (changed.some((path10) => is_runtime(path10, is_basic2))) return "required";
2320
2822
  return "exempt";
2321
2823
  }
2322
2824
  function runtime_files(paths, is_basic2 = false) {
2323
- return normalize(paths).filter((path7) => is_runtime(path7, is_basic2));
2825
+ return normalize(paths).filter((path10) => is_runtime(path10, is_basic2));
2324
2826
  }
2325
2827
  function exempt_files(paths, is_basic2 = false) {
2326
- return normalize(paths).filter((path7) => is_exempt(path7, is_basic2));
2828
+ return normalize(paths).filter((path10) => is_exempt(path10, is_basic2));
2327
2829
  }
2328
2830
  function manual_check_files(paths, is_basic2) {
2329
- return normalize(paths).filter((path7) => is_exempt(path7, is_basic2) && !is_exempt(path7));
2831
+ return normalize(paths).filter((path10) => is_exempt(path10, is_basic2) && !is_exempt(path10));
2330
2832
  }
2331
2833
  var test_declared_logic = {
2332
2834
  EXEMPT_PATHS,
2333
2835
  EXEMPT_PREFIXES,
2334
2836
  EXEMPT_SUFFIXES,
2335
- TEST_SUFFIXES,
2336
2837
  exempt_files,
2337
2838
  is_basic_visual,
2338
2839
  is_exempt,
@@ -2352,15 +2853,9 @@ function path_of(line) {
2352
2853
  return arrow === -1 ? body : body.slice(arrow + RENAME_ARROW.length);
2353
2854
  }
2354
2855
  function read_changed_paths_sync() {
2355
- try {
2356
- const output = execFileSync("git", ["status", "--porcelain"], {
2357
- encoding: "utf8",
2358
- stdio: ["ignore", "pipe", "ignore"]
2359
- });
2360
- return output.split("\n").filter((line) => line.length > STATUS_PREFIX_LENGTH).map((line) => path_of(line));
2361
- } catch {
2362
- return [];
2363
- }
2856
+ const output = git_spawn_sync.run(["status", PORCELAIN_FLAG], { timeout: 0 });
2857
+ if (output.exit_code !== 0) return [];
2858
+ return output.stdout.split("\n").filter((line) => line.length > STATUS_PREFIX_LENGTH).map((line) => path_of(line));
2364
2859
  }
2365
2860
  var injected = {};
2366
2861
  function source_paths() {
@@ -2393,8 +2888,8 @@ function is_untested_commit(call) {
2393
2888
  return test_declared_changed.current_verdict() === "required";
2394
2889
  }
2395
2890
  var REASON = '\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads the working tree as `required` (a changed file that is neither a test \u2014 `*.test.ts` / `*.e2e.ts` \u2014 nor a mechanically-exempt non-runtime path). `CLAUDE.md` \u2192 Code Change Rules requires a test for ALL code changes: a bug fix gets a regression test, a refactor gets tests pinning behavior first, logic gets a unit test, an observable UI/timing change gets an E2E. Add the declared test and reissue this commit. **If the change genuinely touches no executable runtime path** it is the pre-approved non-runtime exception \u2014 declare it in your Step 0 work summary with the reason it is exempt (non-executable config, a cosmetic asset swap) and reissue; the exception is a presentation, not a stop. Run `pnpm josh test:declared` to see the files it names. The procedure is `prompts/collaboration-workflow/rule-delivery.md` \u2192 "\u914D\u9001\u3055\u308C\u3066\u3044\u308B\u898F\u5247". This fires once per run, so reissuing will not repeat on the call in hand.';
2396
- var ROW13 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
2397
- var test_declared_commit = { REASON, ROW: ROW13, is_untested_commit };
2891
+ var ROW17 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
2892
+ var test_declared_commit = { REASON, ROW: ROW17 };
2398
2893
 
2399
2894
  // scripts/rules/third-party-write.ts
2400
2895
  function is_third_party_write_segment(segment, session_owner) {
@@ -2410,101 +2905,38 @@ function writes_third_party(command, resolve_owner = repo_party.current_owner) {
2410
2905
  return targets.some((segment) => is_third_party_write_segment(segment, session_owner));
2411
2906
  }
2412
2907
  var THIRD_PARTY_WRITE_REASON = '\u26D4 third-party write: this `gh api` call writes to a repository whose owner is not this session \u2014 a tracker we do not own is Tier C, and every write there (Issue, comment, PR, Discussion, review) needs explicit current-turn user instruction; typing the target, or holding a correct diagnosis, is not that instruction (`CLAUDE.md` \u2192 "Third-party repositories are Tier C", `prompts/collaboration-workflow/upstream-interrupt.md`). Confirm the party with `pnpm josh repo:party <owner/repo>` \u2014 owner equality, computed not judged. A read (`-X GET`, or no field flag) passes untouched; only a write (`-f`/`-F`/`--field`/`--raw-field`/`--input`, or `-X POST|PATCH|PUT|DELETE`) is refused. Record the finding on our own Issue and draft the artifact for a person to post \u2014 do not publish it yourself. **This rule fires on every occurrence, not once per run.**';
2413
- var ROW14 = {
2908
+ var ROW18 = {
2414
2909
  id: "third-party-write",
2415
2910
  is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
2416
2911
  reason: THIRD_PARTY_WRITE_REASON,
2417
2912
  decide: () => true
2418
2913
  };
2419
2914
  var third_party_write = {
2420
- ROW: ROW14,
2915
+ ROW: ROW18,
2421
2916
  THIRD_PARTY_WRITE_REASON,
2422
- is_third_party_write_segment,
2423
2917
  writes_third_party
2424
2918
  };
2425
2919
 
2426
- // scripts/rules/worktree-guard.ts
2427
- var CHECKOUT_SUBCOMMAND = "checkout";
2428
- var RESTORE_SUBCOMMAND = "restore";
2429
- var STASH_SUBCOMMAND = "stash";
2430
- var PATHSPEC_SEPARATOR = "--";
2431
- function is_worktree_checkout(args) {
2432
- return args.includes(PATHSPEC_SEPARATOR);
2920
+ // scripts/rules/delivered-rules.ts
2921
+ var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
2922
+ var STAMP_PREFIX = "josh-rule-guard-";
2923
+ var { is_issue_filing, on_bash_command } = bash_triggers;
2924
+ var WIP_CAP = 30;
2925
+ var WIP_CAP_REASON = `\u26D4 backlog WIP cap: count the target repository's open Issues before filing. With more than ${String(WIP_CAP)} open, close one first; nothing honestly closable means do not file. Two filings are exempt and proceed while stating the overage \u2014 one the run is blocked by, and an interrupt, decided by three tests rather than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost or written outside the repository. Meeting none of the three, the finding is discretionary and waits. The count command and both procedures are in \`prompts/collaboration-workflow/wip-cap.md\`. Reissue this call once you have counted \u2014 it fires once per run and cannot repeat on the call in hand.`;
2926
+ var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
2927
+ var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
2928
+ var FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u;
2929
+ var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
2930
+ var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
2931
+ var NAMES_THE_BODY = /\bbody\b/u;
2932
+ function projects_away_body(segment) {
2933
+ const projection = FIELD_PROJECTION.exec(segment);
2934
+ return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? "");
2433
2935
  }
2434
- var STAGED_LONG = "--staged";
2435
- var STAGED_LETTER = "S";
2436
- var WORKTREE_LONG = "--worktree";
2437
- var WORKTREE_LETTER = "W";
2438
- function touches_worktree(args) {
2439
- const is_staged = args.some(
2440
- (argument) => argument === STAGED_LONG || git_argv.short_cluster_has(argument, STAGED_LETTER)
2441
- );
2442
- const is_worktree = args.some(
2443
- (argument) => argument === WORKTREE_LONG || git_argv.short_cluster_has(argument, WORKTREE_LETTER)
2444
- );
2445
- return !is_staged || is_worktree;
2446
- }
2447
- var PUSH_STASH = "push";
2448
- var MESSAGE_LONG = "--message";
2449
- var MESSAGE_SHORT_LETTER = "m";
2450
- var READ_ONLY_STASH = /* @__PURE__ */ new Set(["list", "show"]);
2451
- function has_message(args) {
2452
- return args.some(
2453
- (argument) => argument === MESSAGE_LONG || argument.startsWith(`${MESSAGE_LONG}=`) || git_argv.short_cluster_has(argument, MESSAGE_SHORT_LETTER)
2454
- );
2455
- }
2456
- function is_unauthorized_stash(args) {
2457
- const [stash_subcommand, ...rest] = args;
2458
- if (stash_subcommand === void 0) return true;
2459
- if (READ_ONLY_STASH.has(stash_subcommand)) return false;
2460
- if (stash_subcommand === PUSH_STASH) return !has_message(rest);
2461
- return true;
2462
- }
2463
- function is_unauthorized_segment(segment) {
2464
- const call = git_argv.parse(segment);
2465
- if (call === void 0) return false;
2466
- if (call.subcommand === CHECKOUT_SUBCOMMAND) return is_worktree_checkout(call.args);
2467
- if (call.subcommand === RESTORE_SUBCOMMAND) return touches_worktree(call.args);
2468
- return call.subcommand === STASH_SUBCOMMAND && is_unauthorized_stash(call.args);
2469
- }
2470
- function is_unauthorized_worktree_change(command) {
2471
- return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
2472
- }
2473
- var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>` and an unauthorized `git stash` mutate the working tree or the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
2474
- var ROW15 = {
2475
- id: "worktree-mutation",
2476
- is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
2477
- reason: WORKTREE_MUTATION_REASON,
2478
- decide: () => true
2479
- };
2480
- var worktree_guard = {
2481
- ROW: ROW15,
2482
- WORKTREE_MUTATION_REASON,
2483
- is_unauthorized_stash,
2484
- is_unauthorized_worktree_change,
2485
- is_worktree_checkout,
2486
- touches_worktree
2487
- };
2488
-
2489
- // scripts/rules/delivered-rules.ts
2490
- var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
2491
- var STAMP_PREFIX = "josh-rule-guard-";
2492
- var { is_issue_filing, on_bash_command } = bash_triggers;
2493
- var WIP_CAP_REASON = "\u26D4 backlog WIP cap: count the target repository's open Issues before filing. With more than 30 open, close one first; nothing honestly closable means do not file. Two filings are exempt and proceed while stating the overage \u2014 one the run is blocked by, and an interrupt, decided by three tests rather than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost or written outside the repository. Meeting none of the three, the finding is discretionary and waits. The count command and both procedures are in `prompts/collaboration-workflow/wip-cap.md`. Reissue this call once you have counted \u2014 it fires once per run and cannot repeat on the call in hand.";
2494
- var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
2495
- var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
2496
- var FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u;
2497
- var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
2498
- var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
2499
- var NAMES_THE_BODY = /\bbody\b/u;
2500
- function projects_away_body(segment) {
2501
- const projection = FIELD_PROJECTION.exec(segment);
2502
- return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? "");
2503
- }
2504
- function is_body_read_segment(segment) {
2505
- if (projects_away_body(segment)) return false;
2506
- if (ISSUE_VIEW_COMMAND.test(segment)) return true;
2507
- return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
2936
+ function is_body_read_segment(segment) {
2937
+ if (projects_away_body(segment)) return false;
2938
+ if (ISSUE_VIEW_COMMAND.test(segment)) return true;
2939
+ return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
2508
2940
  }
2509
2941
  function fetches_issue_comments(segment) {
2510
2942
  if (!FETCHES_COMMENTS.test(segment)) return false;
@@ -2727,6 +3159,10 @@ var DELIVERED_RULES = [
2727
3159
  git_force.ROW,
2728
3160
  worktree_guard.ROW,
2729
3161
  file_body.ROW,
3162
+ // **The deny-list bypasses closed by argv and path rather than glob** (joshuafolkken/kit#2983) — an
3163
+ // index mutation, a recursive forced `rm` or destructive `gh` call, and a protected file. Each
3164
+ // claims a command or file no row above does and refuses every occurrence (`permission-guards.ts`).
3165
+ ...permission_guards.ROWS,
2730
3166
  // **The hand-written wait loop a lane child improvises over a backgrounded command's output**
2731
3167
  // (joshuafolkken/kit#2371). `early-heartbeat` refuses a bare `sleep` and stands down on any loop
2732
3168
  // keyword; this is the loop it left — a `while`/`until` that sleeps between probes of an output
@@ -2819,12 +3255,12 @@ function is_first_delivery(tail, call, delivered_at_ms, run2) {
2819
3255
  return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run2);
2820
3256
  }
2821
3257
  function delivery_decision(rule) {
2822
- const { decide: decide5, already_satisfied } = rule;
2823
- if (decide5 === void 0 && already_satisfied === void 0) return is_first_delivery;
3258
+ const { decide: decide6, already_satisfied } = rule;
3259
+ if (decide6 === void 0 && already_satisfied === void 0) return is_first_delivery;
2824
3260
  return function should_block(tail, call, delivered_at_ms, run2) {
2825
3261
  if (already_satisfied?.(tail, call) === true) return false;
2826
- if (decide5 === void 0) return true;
2827
- return decide5(call, run2, !will_batch_guard_refuse(tail, call, run2), delivered_at_ms);
3262
+ if (decide6 === void 0) return true;
3263
+ return decide6(call, run2, !will_batch_guard_refuse(tail, call, run2), delivered_at_ms);
2828
3264
  };
2829
3265
  }
2830
3266
  function guard_of(rule) {
@@ -2870,6 +3306,7 @@ var delivered_rules = {
2870
3306
  SHELL_BODY_REASON: SHELL_BODY_REASON2,
2871
3307
  SWITCH_ENV_KEY,
2872
3308
  THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
3309
+ WIP_CAP,
2873
3310
  WIP_CAP_REASON,
2874
3311
  WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
2875
3312
  delivery,
@@ -2880,1680 +3317,11 @@ var delivered_rules = {
2880
3317
  };
2881
3318
 
2882
3319
  // scripts/run/run-watcher-hook.ts
2883
- import { z as z17 } from "zod";
3320
+ import { z as z12 } from "zod";
2884
3321
 
2885
3322
  // scripts/run/run-progress-read.ts
2886
3323
  import { loadavg } from "node:os";
2887
3324
 
2888
- // scripts/git/git-gh-helpers.ts
2889
- function parse_pr_state_string(result) {
2890
- const trimmed = result.trim();
2891
- return trimmed.length > 0 ? trimmed : void 0;
2892
- }
2893
- function is_pr_already_exists_message(error_message) {
2894
- return error_message.toLowerCase().includes("already exists");
2895
- }
2896
- function get_error_message_with_stderr(error) {
2897
- if (!(error instanceof Error)) return String(error);
2898
- if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
2899
- ${error.stderr}`;
2900
- return error.message;
2901
- }
2902
- function handle_pr_create_error(error) {
2903
- const error_message = get_error_message_with_stderr(error);
2904
- if (is_pr_already_exists_message(error_message)) {
2905
- throw new Error("PR_ALREADY_EXISTS");
2906
- }
2907
- throw error;
2908
- }
2909
- var git_gh_helpers = {
2910
- parse_pr_state_string,
2911
- is_pr_already_exists_message,
2912
- get_error_message_with_stderr,
2913
- handle_pr_create_error
2914
- };
2915
-
2916
- // scripts/git/git-gh-issue-rest.ts
2917
- import { z as z7 } from "zod";
2918
-
2919
- // scripts/git/git-gh-rest-state.ts
2920
- var MERGED_STATE = "MERGED";
2921
- function to_gh_state(state) {
2922
- return state?.toUpperCase();
2923
- }
2924
-
2925
- // scripts/git/schemas.ts
2926
- import { z as z6 } from "zod";
2927
- var package_name_schema = z6.object({ name: z6.string().min(1) });
2928
- var rollup_item_schema = z6.looseObject({
2929
- // eslint-disable-next-line @typescript-eslint/naming-convention -- GraphQL field name from GitHub API
2930
- __typename: z6.string().optional(),
2931
- state: z6.string().optional(),
2932
- status: z6.string().optional(),
2933
- conclusion: z6.string().optional(),
2934
- name: z6.string().optional(),
2935
- context: z6.string().optional()
2936
- });
2937
- var pr_raw_schema = z6.object({
2938
- statusCheckRollup: z6.array(rollup_item_schema).optional(),
2939
- mergeStateStatus: z6.string().optional(),
2940
- reviewDecision: z6.string().optional()
2941
- });
2942
- var pull_comment_schema = z6.object({
2943
- body: z6.string().optional(),
2944
- html_url: z6.string().optional(),
2945
- user: z6.object({ login: z6.string().optional() }).optional()
2946
- });
2947
- var ai_review_pull_comment_schema = z6.object({
2948
- body: z6.string().optional(),
2949
- url: z6.string().optional(),
2950
- author: z6.object({ login: z6.string().optional() }).optional()
2951
- });
2952
- var pr_info_schema = z6.object({
2953
- mergeable: z6.union([z6.boolean(), z6.string()]).nullable().optional(),
2954
- mergeStateStatus: z6.string().nullable().optional(),
2955
- state: z6.string().optional()
2956
- });
2957
- var epic_issue_schema = z6.object({
2958
- number: z6.number(),
2959
- body: z6.string().optional()
2960
- });
2961
- var blocking_issue_schema = z6.object({
2962
- number: z6.number(),
2963
- state: z6.string().optional(),
2964
- repository_url: z6.string().optional()
2965
- });
2966
- var blocked_by_schema = z6.object({
2967
- nodes: z6.array(blocking_issue_schema).default([]),
2968
- totalCount: z6.number().optional()
2969
- }).optional();
2970
- var epic_child_schema = z6.object({
2971
- state: z6.string().optional(),
2972
- blockedBy: blocked_by_schema
2973
- });
2974
- var rest_comment_schema = z6.looseObject({
2975
- body: z6.string().nullish(),
2976
- html_url: z6.string().optional(),
2977
- user: z6.looseObject({ login: z6.string().optional() }).nullish()
2978
- });
2979
- var issue_label_schema = z6.object({ name: z6.string() });
2980
- var epic_subject_schema = z6.object({
2981
- number: z6.number(),
2982
- state: z6.string().optional(),
2983
- labels: z6.array(issue_label_schema).optional(),
2984
- body: z6.string().optional()
2985
- });
2986
- var open_issue_schema = z6.object({
2987
- number: z6.number(),
2988
- title: z6.string(),
2989
- labels: z6.array(issue_label_schema).optional(),
2990
- createdAt: z6.string(),
2991
- blockedBy: blocked_by_schema
2992
- });
2993
-
2994
- // scripts/git/git-gh-issue-rest.ts
2995
- var rest_issue_schema = z7.looseObject({
2996
- number: z7.number(),
2997
- issue_dependencies_summary: z7.looseObject({ total_blocked_by: z7.number().optional() }).nullish(),
2998
- // Present only on a pull request, which this endpoint serves as readily as an issue.
2999
- pull_request: z7.looseObject({ merged_at: z7.string().nullish() }).nullish()
3000
- });
3001
- var REPOSITORY_URL = /\/repos\/([\w.-]+\/[\w.-]+)$/u;
3002
- var REPOSITORY_URL_GROUP = 1;
3003
- var BLOCKED_BY_FIELD = "blockedBy";
3004
- var BLOCKED_BY_COUNT_FIELD = "blocked_by_count";
3005
- var STATE_FIELD = "state";
3006
- var BODY_FIELD = "body";
3007
- var FIELD_SEPARATOR = ",";
3008
- var EMPTY_BODY = "";
3009
- var REST_FIELD_NAMES = /* @__PURE__ */ new Map([
3010
- ["url", "html_url"],
3011
- ["createdAt", "created_at"]
3012
- ]);
3013
- var NOT_AN_ISSUE_MESSAGE = "gh api answered something other than an issue object";
3014
- var NOT_AN_ISSUE_LISTING_MESSAGE = "gh api answered something other than an issue listing";
3015
- var NOT_A_BLOCKER_LISTING_MESSAGE = "gh api answered something other than a blocked-by listing";
3016
- function rest_field_name(field) {
3017
- return REST_FIELD_NAMES.get(field) ?? field;
3018
- }
3019
- function split_fields(fields) {
3020
- return fields.split(FIELD_SEPARATOR).map((field) => field.trim()).filter((field) => field.length > 0);
3021
- }
3022
- function to_issue_state(rest, value) {
3023
- if (typeof rest.pull_request?.merged_at === "string") return MERGED_STATE;
3024
- return typeof value === "string" ? to_gh_state(value) : value;
3025
- }
3026
- function total_blocked_by(rest) {
3027
- return rest.issue_dependencies_summary?.total_blocked_by;
3028
- }
3029
- function to_gh_field_value(field, rest) {
3030
- if (field === BLOCKED_BY_COUNT_FIELD) return total_blocked_by(rest);
3031
- const value = rest[rest_field_name(field)];
3032
- if (field === STATE_FIELD) return to_issue_state(rest, value);
3033
- return field === BODY_FIELD ? value ?? EMPTY_BODY : value;
3034
- }
3035
- function parse_rest_issue(rest_json) {
3036
- const parsed = parse_json_object_safe(rest_json, rest_issue_schema);
3037
- if (parsed === void 0) throw new Error(NOT_AN_ISSUE_MESSAGE);
3038
- return parsed;
3039
- }
3040
- function is_pull_request(rest) {
3041
- return rest.pull_request !== void 0 && rest.pull_request !== null;
3042
- }
3043
- function parse_rest_issues(rest_json) {
3044
- const parsed = parse_json_array_or_undefined(rest_json, rest_issue_schema);
3045
- if (parsed === void 0) throw new Error(NOT_AN_ISSUE_LISTING_MESSAGE);
3046
- return parsed;
3047
- }
3048
- function empty_blocked_by() {
3049
- return { nodes: [], totalCount: 0 };
3050
- }
3051
- function repo_of_url(repository_url) {
3052
- const match = REPOSITORY_URL.exec(repository_url ?? "");
3053
- return match?.[REPOSITORY_URL_GROUP];
3054
- }
3055
- function to_blocked_by(blockers_json, exact_total) {
3056
- const parsed = parse_json_array_or_undefined(blockers_json, blocking_issue_schema);
3057
- if (parsed === void 0) throw new Error(NOT_A_BLOCKER_LISTING_MESSAGE);
3058
- const nodes = parsed.map((blocker) => ({ ...blocker, state: to_gh_state(blocker.state) }));
3059
- return { nodes, totalCount: exact_total ?? nodes.length };
3060
- }
3061
- function to_gh_issue(rest, fields, blocked_by) {
3062
- return Object.fromEntries(
3063
- fields.map((field) => [
3064
- field,
3065
- field === BLOCKED_BY_FIELD ? blocked_by : to_gh_field_value(field, rest)
3066
- ])
3067
- );
3068
- }
3069
- function to_field_text(value) {
3070
- if (typeof value === "string") return value;
3071
- return value === void 0 || value === null ? EMPTY_BODY : JSON.stringify(value);
3072
- }
3073
- var CLOSED = "CLOSED";
3074
- function reference_of_blocker(node, fallback_repo) {
3075
- const repo = repo_of_url(node.repository_url) ?? fallback_repo;
3076
- if (node.state === void 0) return { repo, number: node.number };
3077
- return { repo, number: node.number, state: node.state.toUpperCase() === CLOSED ? CLOSED : "OPEN" };
3078
- }
3079
- var git_gh_issue_rest = {
3080
- empty_blocked_by,
3081
- repo_of_url,
3082
- reference_of_blocker,
3083
- split_fields,
3084
- is_pull_request,
3085
- parse_rest_issue,
3086
- parse_rest_issues,
3087
- total_blocked_by,
3088
- to_blocked_by,
3089
- to_gh_issue,
3090
- to_field_text
3091
- };
3092
-
3093
- // scripts/git/git-gh-reachability.ts
3094
- var RATE_LIMITED_STATUS = 429;
3095
- var SERVER_ERROR_FLOOR = 500;
3096
- function is_retryable_status(status) {
3097
- if (status >= SERVER_ERROR_FLOOR) return true;
3098
- return status === RATE_LIMITED_STATUS;
3099
- }
3100
- function classify_status(status) {
3101
- if (status === void 0) return "unreachable";
3102
- return is_retryable_status(status) ? "unreachable" : "reachable";
3103
- }
3104
- var gh_reachability = { classify_status };
3105
-
3106
- // scripts/git/git-gh-issue-read.ts
3107
- async function read_blocked_by_listing(issue_number, repo, exact_total) {
3108
- const json = await git_gh_exec.exec_gh_api({
3109
- path: `${git_gh_api_path.blocked_by_api_path(issue_number, repo)}${git_gh_api_path.FULL_PAGE_QUERY}`
3110
- });
3111
- return git_gh_issue_rest.to_blocked_by(json, exact_total);
3112
- }
3113
- async function read_blocked_by(issue_number, rest, repo) {
3114
- if (git_gh_issue_rest.is_pull_request(rest)) return git_gh_issue_rest.empty_blocked_by();
3115
- const exact_total = git_gh_issue_rest.total_blocked_by(rest);
3116
- if (exact_total === 0) return git_gh_issue_rest.empty_blocked_by();
3117
- return await read_blocked_by_listing(issue_number, repo, exact_total);
3118
- }
3119
- function mark_followup(error) {
3120
- if (!(error instanceof Error)) return error;
3121
- const failure = gh_failure.failure_of(error);
3122
- if (failure === void 0) return error;
3123
- return gh_failure.attach(error, { ...failure, is_followup: true });
3124
- }
3125
- async function read_relations(issue_number, rest, repo) {
3126
- try {
3127
- return await read_blocked_by(issue_number, rest, repo);
3128
- } catch (error) {
3129
- throw mark_followup(error);
3130
- }
3131
- }
3132
- async function read_issue_fields(issue_number, fields, repo) {
3133
- const rest = git_gh_issue_rest.parse_rest_issue(
3134
- await git_gh_exec.exec_gh_api({ path: git_gh_api_path.issue_api_path(issue_number, repo) })
3135
- );
3136
- const requested = git_gh_issue_rest.split_fields(fields);
3137
- const blocked_by = requested.includes(BLOCKED_BY_FIELD) ? await read_relations(issue_number, rest, repo) : void 0;
3138
- return git_gh_issue_rest.to_gh_issue(rest, requested, blocked_by);
3139
- }
3140
- var NOT_FOUND_STATUS = 404;
3141
- function is_number_unresolved(failure) {
3142
- return failure.status === NOT_FOUND_STATUS && failure.is_followup !== true;
3143
- }
3144
- function to_unreadable_reason(status) {
3145
- return gh_reachability.classify_status(status) === "unreachable" ? "unreachable" : "rejected";
3146
- }
3147
- function to_read_failure(error) {
3148
- const failure = gh_failure.failure_of(error);
3149
- if (failure === void 0) return { kind: "unreadable", reason: "malformed", status: void 0 };
3150
- if (is_number_unresolved(failure)) return { kind: "missing" };
3151
- return {
3152
- kind: "unreadable",
3153
- reason: to_unreadable_reason(failure.status),
3154
- status: failure.status
3155
- };
3156
- }
3157
- async function issue_view_json_classified(issue_number, fields, repo) {
3158
- try {
3159
- const issue = await read_issue_fields(issue_number, fields, repo);
3160
- return { kind: "read", json: JSON.stringify(issue) };
3161
- } catch (error) {
3162
- return to_read_failure(error);
3163
- }
3164
- }
3165
- async function issue_view_field_classified(issue_number, field, repo) {
3166
- try {
3167
- const issue = await read_issue_fields(issue_number, field, repo);
3168
- return { kind: "read", text: git_gh_issue_rest.to_field_text(issue[field]) };
3169
- } catch (error) {
3170
- return to_read_failure(error);
3171
- }
3172
- }
3173
- async function issue_view_field(issue_number, field, repo) {
3174
- const read2 = await issue_view_field_classified(issue_number, field, repo);
3175
- return read2.kind === "read" ? read2.text : void 0;
3176
- }
3177
- async function issue_get_title(issue_number, repo) {
3178
- const result = await issue_view_field(issue_number, "title", repo);
3179
- return result === void 0 ? void 0 : git_gh_helpers.parse_pr_state_string(result);
3180
- }
3181
- async function issue_get_body(issue_number, repo) {
3182
- return await issue_view_field(issue_number, "body", repo);
3183
- }
3184
- async function issue_get_body_classified(issue_number, repo) {
3185
- return await issue_view_field_classified(issue_number, "body", repo);
3186
- }
3187
- async function issue_view_json(issue_number, fields, repo) {
3188
- const read2 = await issue_view_json_classified(issue_number, fields, repo);
3189
- return read2.kind === "read" ? read2.json : void 0;
3190
- }
3191
- var STATE_AND_RELATIONS_FIELDS = "number,state,labels,blockedBy";
3192
- async function issue_get_state_and_relations(issue_number, repo) {
3193
- return await issue_view_json(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
3194
- }
3195
- async function issue_get_state_and_relations_classified(issue_number, repo) {
3196
- return await issue_view_json_classified(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
3197
- }
3198
- function is_unreachable_read(failure) {
3199
- return failure.kind === "unreadable" && failure.reason === "unreachable";
3200
- }
3201
- var PLAN_FIELDS = "number,title,body,state,url,labels,blockedBy";
3202
- async function issue_get_plan_fields(issue_number) {
3203
- return await issue_view_json(issue_number, PLAN_FIELDS);
3204
- }
3205
- async function issue_get_plan_fields_classified(issue_number) {
3206
- return await issue_view_json_classified(issue_number, PLAN_FIELDS);
3207
- }
3208
- async function issue_get_labels_and_body(issue_number) {
3209
- return await issue_view_json(issue_number, "number,state,labels,body");
3210
- }
3211
- async function issue_list_comments(issue_number) {
3212
- const path7 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
3213
- try {
3214
- return await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true });
3215
- } catch {
3216
- return void 0;
3217
- }
3218
- }
3219
- async function has_no_relations_endpoint(issue_number, repo) {
3220
- const status = await git_gh_exec.exec_gh_api_status(
3221
- git_gh_api_path.blocked_by_api_path(issue_number, repo)
3222
- );
3223
- return status === NOT_FOUND_STATUS;
3224
- }
3225
- async function issue_blocked_by_references(issue_number, own_repo, repo) {
3226
- try {
3227
- const listing = await read_blocked_by_listing(issue_number, repo);
3228
- return listing.nodes.map((blocker) => git_gh_issue_rest.reference_of_blocker(blocker, own_repo));
3229
- } catch (error) {
3230
- if (await has_no_relations_endpoint(issue_number, repo)) return [];
3231
- throw error;
3232
- }
3233
- }
3234
- async function issue_blocked_by_numbers(issue_number, repo) {
3235
- const references = await issue_blocked_by_references(issue_number, "", repo);
3236
- return references.map((blocker) => blocker.number);
3237
- }
3238
- var git_gh_issue_read = {
3239
- issue_blocked_by_numbers,
3240
- issue_blocked_by_references,
3241
- issue_get_title,
3242
- issue_get_body,
3243
- issue_get_body_classified,
3244
- issue_view_json,
3245
- issue_view_json_classified,
3246
- issue_get_state_and_relations,
3247
- issue_get_state_and_relations_classified,
3248
- is_unreachable_read,
3249
- issue_get_plan_fields,
3250
- issue_get_plan_fields_classified,
3251
- issue_get_labels_and_body,
3252
- issue_list_comments
3253
- };
3254
-
3255
- // scripts/git/git-gh-issue-list.ts
3256
- var PER_PAGE = 100;
3257
- var FIRST_PAGE = 1;
3258
- var DEFAULT_STATE = "open";
3259
- var DEFAULT_SORT = "created";
3260
- var LISTING_DIRECTION = "direction=desc";
3261
- function listing_query(request) {
3262
- const state = request.state ?? DEFAULT_STATE;
3263
- const sort = request.sort ?? DEFAULT_SORT;
3264
- return `state=${state}&sort=${sort}&${LISTING_DIRECTION}`;
3265
- }
3266
- function listing_path(request, page) {
3267
- const label = request.label === void 0 ? "" : `&labels=${encodeURIComponent(request.label)}`;
3268
- const paging = `&per_page=${String(PER_PAGE)}&page=${String(page)}`;
3269
- return `${git_gh_api_path.issues_api_path(request.repo)}?${listing_query(request)}${label}${paging}`;
3270
- }
3271
- var DIGIT_PATTERN = /\d/u;
3272
- function has_token_occurrence(body, term) {
3273
- let index = body.indexOf(term);
3274
- while (index !== -1) {
3275
- if (!DIGIT_PATTERN.test(body.charAt(index + term.length))) return true;
3276
- index = body.indexOf(term, index + 1);
3277
- }
3278
- return false;
3279
- }
3280
- function matches_body(rest, term) {
3281
- if (term === void 0) return true;
3282
- const { body } = rest;
3283
- return typeof body === "string" && has_token_occurrence(body, term);
3284
- }
3285
- function is_selected(rest, request) {
3286
- return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
3287
- }
3288
- async function fetch_page(request, page) {
3289
- const path7 = listing_path(request, page);
3290
- const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path7 }));
3291
- return {
3292
- selected: rows.filter((row) => is_selected(row, request)),
3293
- is_full: rows.length >= PER_PAGE
3294
- };
3295
- }
3296
- var MAX_PAGES = 5;
3297
- var MAX_SCANNED = MAX_PAGES * PER_PAGE;
3298
- function wants_more(has_more, selected_count, limit) {
3299
- return has_more && selected_count < limit;
3300
- }
3301
- async function fetch_selected(request) {
3302
- const selected = [];
3303
- let page = FIRST_PAGE;
3304
- let has_more = true;
3305
- while (page <= MAX_PAGES && wants_more(has_more, selected.length, request.limit)) {
3306
- const result = await fetch_page(request, page);
3307
- selected.push(...result.selected);
3308
- has_more = result.is_full;
3309
- page += 1;
3310
- }
3311
- return {
3312
- rows: selected.slice(0, request.limit),
3313
- is_capped: wants_more(has_more, selected.length, request.limit)
3314
- };
3315
- }
3316
- async function read_relations2(rows, repo) {
3317
- const relations = [];
3318
- for (const row of rows) {
3319
- relations.push(await read_blocked_by(String(row.number), row, repo));
3320
- }
3321
- return relations;
3322
- }
3323
- async function issue_list(request) {
3324
- try {
3325
- const { rows, is_capped } = await fetch_selected(request);
3326
- const fields = git_gh_issue_rest.split_fields(request.json_fields);
3327
- const relations = fields.includes(BLOCKED_BY_FIELD) ? await read_relations2(rows, request.repo) : void 0;
3328
- const mapped = rows.map(
3329
- (row, index) => git_gh_issue_rest.to_gh_issue(row, fields, relations?.[index])
3330
- );
3331
- return { json: JSON.stringify(mapped), is_capped };
3332
- } catch {
3333
- return { json: void 0, is_capped: false };
3334
- }
3335
- }
3336
- var git_gh_issue_list = {
3337
- issue_list
3338
- };
3339
-
3340
- // scripts/git/git-gh-issue-write.ts
3341
- var PATCH_METHOD = "PATCH";
3342
- var DELETE_METHOD = "DELETE";
3343
- var HTML_URL_FILTER = ".html_url";
3344
- var ID_FILTER = ".id";
3345
- var LABELS_SEGMENT = "/labels";
3346
- var COMMENTS_SEGMENT = "/comments";
3347
- var CLOSED_STATE = "closed";
3348
- var COLOR_HASH = "#";
3349
- async function did_write_succeed(write) {
3350
- try {
3351
- await write();
3352
- return true;
3353
- } catch {
3354
- return false;
3355
- }
3356
- }
3357
- async function issue_edit_body(issue_number, body) {
3358
- return await git_gh_exec.exec_gh_api({
3359
- path: git_gh_api_path.issue_api_path(issue_number),
3360
- method: PATCH_METHOD,
3361
- body: JSON.stringify({ body }),
3362
- jq_filter: HTML_URL_FILTER
3363
- });
3364
- }
3365
- async function issue_comment(issue_number, body) {
3366
- return await git_gh_exec.exec_gh_api({
3367
- path: `${git_gh_api_path.issue_api_path(issue_number)}${COMMENTS_SEGMENT}`,
3368
- body: JSON.stringify({ body }),
3369
- jq_filter: HTML_URL_FILTER
3370
- });
3371
- }
3372
- async function issue_try_comment(issue_number, body) {
3373
- return await did_write_succeed(async () => await issue_comment(issue_number, body));
3374
- }
3375
- async function issue_close(issue_number, comment) {
3376
- return await did_write_succeed(async () => {
3377
- if (comment !== void 0) await issue_comment(issue_number, comment);
3378
- await git_gh_exec.exec_gh_api({
3379
- path: git_gh_api_path.issue_api_path(issue_number),
3380
- method: PATCH_METHOD,
3381
- body: JSON.stringify({ state: CLOSED_STATE })
3382
- });
3383
- });
3384
- }
3385
- function to_label_color(color) {
3386
- return color.startsWith(COLOR_HASH) ? color.slice(COLOR_HASH.length) : color;
3387
- }
3388
- async function label_ensure(input) {
3389
- try {
3390
- await git_gh_exec.exec_gh_api({
3391
- path: `${git_gh_api_path.repo_api_path()}${LABELS_SEGMENT}`,
3392
- body: JSON.stringify({
3393
- name: input.name,
3394
- color: to_label_color(input.color),
3395
- description: input.description
3396
- })
3397
- });
3398
- } catch {
3399
- }
3400
- }
3401
- function issue_create_request(input) {
3402
- const { labels } = input;
3403
- return {
3404
- path: git_gh_api_path.issues_api_path(input.repo),
3405
- body: JSON.stringify({
3406
- title: input.title,
3407
- ...labels !== void 0 && { labels },
3408
- body: input.body
3409
- }),
3410
- jq_filter: HTML_URL_FILTER
3411
- };
3412
- }
3413
- async function issue_create_with_label(input) {
3414
- return await git_gh_exec.exec_gh_api(
3415
- issue_create_request({ title: input.title, body: input.body, labels: [input.label] })
3416
- );
3417
- }
3418
- async function issue_add_label(issue_number, label) {
3419
- return await did_write_succeed(
3420
- async () => await git_gh_exec.exec_gh_api({
3421
- path: `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`,
3422
- body: JSON.stringify({ labels: [label] })
3423
- })
3424
- );
3425
- }
3426
- async function issue_remove_label(issue_number, label) {
3427
- const labels_path = `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`;
3428
- await git_gh_exec.exec_gh_api({
3429
- path: `${labels_path}/${encodeURIComponent(label)}`,
3430
- method: DELETE_METHOD
3431
- });
3432
- }
3433
- async function read_issue_id(issue_number) {
3434
- const raw = await git_gh_exec.exec_gh_api({
3435
- path: git_gh_api_path.issue_api_path(issue_number),
3436
- jq_filter: ID_FILTER
3437
- });
3438
- const issue_id = Number(raw.trim());
3439
- if (!Number.isSafeInteger(issue_id) || issue_id <= 0) {
3440
- throw new Error(`gh api answered no database id for issue #${issue_number}`);
3441
- }
3442
- return issue_id;
3443
- }
3444
- async function issue_add_blocked_by(issue_number, blocker) {
3445
- return await did_write_succeed(async () => {
3446
- const issue_id = await read_issue_id(blocker);
3447
- await git_gh_exec.exec_gh_api({
3448
- path: git_gh_api_path.blocked_by_api_path(issue_number),
3449
- body: JSON.stringify({ issue_id })
3450
- });
3451
- });
3452
- }
3453
- async function issue_remove_blocked_by(issue_number, blocker) {
3454
- return await did_write_succeed(async () => {
3455
- const issue_id = await read_issue_id(blocker);
3456
- const relations_path = git_gh_api_path.blocked_by_api_path(issue_number);
3457
- await git_gh_exec.exec_gh_api({
3458
- path: `${relations_path}/${String(issue_id)}`,
3459
- method: DELETE_METHOD
3460
- });
3461
- });
3462
- }
3463
- var git_gh_issue_write = {
3464
- issue_edit_body,
3465
- issue_comment,
3466
- issue_try_comment,
3467
- issue_close,
3468
- label_ensure,
3469
- issue_create_request,
3470
- issue_create_with_label,
3471
- issue_add_label,
3472
- issue_remove_label,
3473
- issue_add_blocked_by,
3474
- issue_remove_blocked_by
3475
- };
3476
-
3477
- // scripts/git/git-gh-issue.ts
3478
- var NUMBER_AND_BODY_FIELDS = "number,body";
3479
- var BACKLOG_SCAN_FIELDS = `number,title,body,${BLOCKED_BY_COUNT_FIELD}`;
3480
- var NUMBER_TITLE_AND_LABELS_FIELDS = "number,title,labels";
3481
- var SUMMARY_FIELDS = "number,title,labels,createdAt";
3482
- var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy`;
3483
- var { issue_list: issue_list2 } = git_gh_issue_list;
3484
- async function issue_list_recent(limit) {
3485
- return await issue_list2({ json_fields: SUMMARY_FIELDS, limit });
3486
- }
3487
- async function issue_list_open_bodies(limit) {
3488
- return await issue_list2({ json_fields: BACKLOG_SCAN_FIELDS, limit });
3489
- }
3490
- async function issue_list_recently_closed(limit) {
3491
- return await issue_list2({
3492
- json_fields: NUMBER_TITLE_AND_LABELS_FIELDS,
3493
- limit,
3494
- state: "closed",
3495
- sort: "updated"
3496
- });
3497
- }
3498
- async function issue_search_body(term, limit) {
3499
- return await issue_list2({
3500
- json_fields: NUMBER_AND_BODY_FIELDS,
3501
- limit,
3502
- body_term: term
3503
- });
3504
- }
3505
- async function issue_list_by_label(label, limit) {
3506
- return await issue_list2({ json_fields: NUMBER_AND_BODY_FIELDS, limit, label });
3507
- }
3508
- async function issue_list_by_label_summary(label, limit, options) {
3509
- return await issue_list2({
3510
- json_fields: options?.json_fields ?? PICKUP_FIELDS,
3511
- limit,
3512
- label
3513
- });
3514
- }
3515
- async function issue_list_by_label_in_repo(label, limit, repo) {
3516
- return await issue_list2({ json_fields: SUMMARY_FIELDS, limit, label, repo });
3517
- }
3518
- var git_gh_issue = {
3519
- ...git_gh_issue_read,
3520
- ...git_gh_issue_write,
3521
- issue_list_recent,
3522
- issue_list_by_label,
3523
- issue_list_by_label_in_repo,
3524
- issue_list_by_label_summary,
3525
- issue_search_body,
3526
- issue_list_open_bodies,
3527
- issue_list_recently_closed
3528
- };
3529
-
3530
- // scripts/ci/pr-classification.ts
3531
- import { readFileSync as readFileSync5 } from "node:fs";
3532
- import { fileURLToPath as fileURLToPath2 } from "node:url";
3533
- import { z as z8 } from "zod";
3534
- var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
3535
- var ISSUE_BUG_LABEL = "bug";
3536
- var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
3537
- var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
3538
- var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
3539
- var LABEL = z8.object({ name: z8.string() });
3540
- var USER = z8.object({ login: z8.string() });
3541
- var PULL_REQUEST = z8.object({ number: z8.number(), user: USER });
3542
- var EVENT = z8.object({
3543
- pull_request: PULL_REQUEST,
3544
- repository: z8.object({ full_name: z8.string() })
3545
- });
3546
- function classification_error(labels, author) {
3547
- const legacy = labels.filter((label) => LEGACY_LABELS.has(label.toLowerCase()));
3548
- if (legacy.length > 0) return `Remove legacy release labels: ${legacy.join(", ")}`;
3549
- const selected = labels.filter((label) => CLASSIFICATION_SET.has(label.toLowerCase()));
3550
- if (selected.length > 1) {
3551
- return `Choose exactly one release classification; found: ${selected.join(", ")}`;
3552
- }
3553
- if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
3554
- return `Choose one release classification: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}`;
3555
- }
3556
- function declaration(label) {
3557
- return `${DECLARATION_PREFIX}${label}`;
3558
- }
3559
- function is_classification(value) {
3560
- return CLASSIFICATION_SET.has(value);
3561
- }
3562
- function release_candidates(candidates) {
3563
- if (!candidates.includes(BREAKING_CHANGE_LABEL)) return candidates;
3564
- return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
3565
- }
3566
- function issue_candidates(issue_json) {
3567
- const issue = z8.object({ labels: z8.array(LABEL), body: z8.string().nullable() }).parse(JSON.parse(issue_json));
3568
- const names = issue.labels.map((label) => label.name.toLowerCase());
3569
- const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
3570
- const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
3571
- const unknown = declared.filter((value) => !is_classification(value));
3572
- if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
3573
- if (names.includes(ISSUE_BUG_LABEL) && !candidates.includes(BREAKING_CHANGE_LABEL)) {
3574
- candidates.push(BUGFIX_LABEL);
3575
- }
3576
- return candidates;
3577
- }
3578
- function select_issue_classification(issue_json) {
3579
- const candidates = issue_candidates(issue_json);
3580
- const selected = [...new Set(candidates)];
3581
- const [label] = selected;
3582
- if (label === void 0 || selected.length > 1) {
3583
- throw new Error(
3584
- `Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "${DECLARATION_PREFIX}<label>" to the Issue body.`
3585
- );
3586
- }
3587
- return label;
3588
- }
3589
- function fetch_current_labels(repository, pull_number) {
3590
- const path7 = `repos/${repository}/issues/${String(pull_number)}/labels`;
3591
- try {
3592
- const names = git_gh_exec.exec_gh_api_sync({ path: path7, jq_filter: ".[].name" });
3593
- return names.split("\n").filter((name) => name !== "");
3594
- } catch (error) {
3595
- const detail = error_text.message_of(error);
3596
- throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error });
3597
- }
3598
- }
3599
- function check_event(event_path, read_labels = fetch_current_labels) {
3600
- const event = EVENT.parse(JSON.parse(readFileSync5(event_path, "utf8")));
3601
- const labels = read_labels(event.repository.full_name, event.pull_request.number);
3602
- return classification_error(labels, event.pull_request.user.login);
3603
- }
3604
- if (process.argv[1] === fileURLToPath2(import.meta.url)) {
3605
- const event_path = process.env["GITHUB_EVENT_PATH"];
3606
- if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
3607
- const error = check_event(event_path);
3608
- if (error) {
3609
- console.error(`::error::${error}`);
3610
- process.exitCode = 1;
3611
- }
3612
- }
3613
- var pr_classification = {
3614
- classification_error,
3615
- check_event,
3616
- fetch_current_labels,
3617
- declaration,
3618
- is_classification,
3619
- select_issue_classification
3620
- };
3621
-
3622
- // scripts/lib/poll.ts
3623
- async function sleep(duration_ms) {
3624
- await new Promise((resolve2) => {
3625
- setTimeout(resolve2, duration_ms);
3626
- });
3627
- }
3628
- async function wait_between(wait, interval_ms, attempt, attempts) {
3629
- if (attempt < attempts - 1) await wait(interval_ms);
3630
- }
3631
- async function poll_until(is_done, options) {
3632
- const wait = options.sleeper ?? sleep;
3633
- for (let attempt = 0; attempt < options.attempts; attempt += 1) {
3634
- if (await is_done()) return true;
3635
- await wait_between(wait, options.interval_ms, attempt, options.attempts);
3636
- }
3637
- return false;
3638
- }
3639
- var poll = { poll_until, sleep };
3640
-
3641
- // scripts/git/git-gh-pr.ts
3642
- import { z as z12 } from "zod";
3643
-
3644
- // scripts/git/git-gh-pr-rest.ts
3645
- import { z as z9 } from "zod";
3646
- var rest_repo_schema = z9.looseObject({ full_name: z9.string().optional() }).nullish();
3647
- var rest_pull_side_schema = z9.looseObject({ repo: rest_repo_schema });
3648
- var rest_pull_schema = z9.looseObject({
3649
- number: z9.number(),
3650
- html_url: z9.string().optional(),
3651
- body: z9.string().nullish(),
3652
- state: z9.string().optional(),
3653
- merged: z9.boolean().nullish(),
3654
- merged_at: z9.string().nullish(),
3655
- mergeable: z9.boolean().nullish(),
3656
- mergeable_state: z9.string().optional(),
3657
- // `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
3658
- // REST hangs the check runs and the status contexts off the commit rather than off the pull
3659
- // request (joshuafolkken/kit#1028).
3660
- head: z9.looseObject({ ref: z9.string().optional(), sha: z9.string().optional(), repo: rest_repo_schema }).nullish(),
3661
- base: rest_pull_side_schema.nullish(),
3662
- // `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
3663
- // is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`
3664
- // (joshuafolkken/kit#2497).
3665
- node_id: z9.string().optional(),
3666
- auto_merge: z9.looseObject({}).nullish()
3667
- });
3668
- var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
3669
- var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
3670
- var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
3671
- var OPEN_STATE2 = "open";
3672
- var EMPTY_BODY2 = "";
3673
- function parse_rest_pull(rest_json) {
3674
- const parsed = parse_json_object_safe(rest_json, rest_pull_schema);
3675
- if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
3676
- return parsed;
3677
- }
3678
- function parse_rest_pulls(rest_json) {
3679
- const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
3680
- if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
3681
- return parsed;
3682
- }
3683
- function select_pull(pulls) {
3684
- return pulls.find((pull) => pull.state === OPEN_STATE2) ?? pulls[0];
3685
- }
3686
- function is_merged(pull) {
3687
- return pull.merged === true || typeof pull.merged_at === "string";
3688
- }
3689
- function to_pr_state(pull) {
3690
- return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
3691
- }
3692
- function to_repo_name(side) {
3693
- return side?.repo?.full_name;
3694
- }
3695
- function is_same_repository_head(pull) {
3696
- const head_repo = to_repo_name(pull.head);
3697
- return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
3698
- }
3699
- function to_pr_info(pull) {
3700
- return {
3701
- mergeable: pull.mergeable,
3702
- mergeStateStatus: to_gh_state(pull.mergeable_state),
3703
- state: to_pr_state(pull)
3704
- };
3705
- }
3706
- function to_pr_comments(rest_json) {
3707
- const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
3708
- if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
3709
- return parsed.map((comment) => ({
3710
- body: comment.body ?? EMPTY_BODY2,
3711
- url: comment.html_url,
3712
- author: { login: comment.user?.login }
3713
- }));
3714
- }
3715
- var git_gh_pr_rest = {
3716
- parse_rest_pull,
3717
- parse_rest_pulls,
3718
- select_pull,
3719
- is_same_repository_head,
3720
- is_merged,
3721
- to_pr_state,
3722
- to_pr_info,
3723
- to_pr_comments
3724
- };
3725
-
3726
- // scripts/git/git-gh-pr-read.ts
3727
- var pr_number_by_branch = /* @__PURE__ */ new Map();
3728
- var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
3729
- var COMMENTS_QUERY = FULL_PAGE_QUERY;
3730
- var OWNER_PLACEHOLDER = "{owner}";
3731
- var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
3732
- var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
3733
- function lookup_path(branch_name) {
3734
- const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
3735
- return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
3736
- }
3737
- async function fetch_pr_number(branch_name) {
3738
- const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
3739
- return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
3740
- }
3741
- var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
3742
- async function fetch_pr_number_read(branch_name) {
3743
- try {
3744
- const pr_number = await fetch_pr_number(branch_name);
3745
- if (pr_number === void 0) return { kind: "missing" };
3746
- pr_number_by_branch.set(branch_name, pr_number);
3747
- return { kind: "read", pr_number };
3748
- } catch (error) {
3749
- return { kind: "unreadable", cause: error };
3750
- }
3751
- }
3752
- async function start_pr_number_read(branch_name) {
3753
- const pending = pending_pr_number_by_branch.get(branch_name);
3754
- if (pending !== void 0) return await pending;
3755
- const lookup = fetch_pr_number_read(branch_name);
3756
- pending_pr_number_by_branch.set(branch_name, lookup);
3757
- try {
3758
- return await lookup;
3759
- } finally {
3760
- pending_pr_number_by_branch.delete(branch_name);
3761
- }
3762
- }
3763
- function forget_pr_numbers() {
3764
- pr_number_by_branch.clear();
3765
- pending_pr_number_by_branch.clear();
3766
- }
3767
- async function read_pr_number(branch_name) {
3768
- const cached = pr_number_by_branch.get(branch_name);
3769
- if (cached !== void 0) return { kind: "read", pr_number: cached };
3770
- return await start_pr_number_read(branch_name);
3771
- }
3772
- async function resolve_pr_number(branch_name) {
3773
- const read2 = await read_pr_number(branch_name);
3774
- return read2.kind === "read" ? read2.pr_number : void 0;
3775
- }
3776
- function to_unreadable_error(branch_name, cause) {
3777
- return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
3778
- }
3779
- async function require_pr_number(branch_name) {
3780
- const read2 = await read_pr_number(branch_name);
3781
- if (read2.kind === "read") return read2.pr_number;
3782
- if (read2.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
3783
- throw to_unreadable_error(branch_name, read2.cause);
3784
- }
3785
- async function read_pull(pr_number) {
3786
- const json = await git_gh_exec.exec_gh_api({
3787
- path: git_gh_api_path.pull_api_path(String(pr_number))
3788
- });
3789
- return git_gh_pr_rest.parse_rest_pull(json);
3790
- }
3791
- async function read_pull_of_branch(branch_name) {
3792
- const pr_number = await resolve_pr_number(branch_name);
3793
- if (pr_number === void 0) return void 0;
3794
- try {
3795
- return await read_pull(pr_number);
3796
- } catch {
3797
- return void 0;
3798
- }
3799
- }
3800
- async function pr_exists(branch_name) {
3801
- const read2 = await read_pr_number(branch_name);
3802
- if (read2.kind === "unreadable") throw to_unreadable_error(branch_name, read2.cause);
3803
- return read2.kind === "read";
3804
- }
3805
- async function pr_get_number(branch_name) {
3806
- return await resolve_pr_number(branch_name);
3807
- }
3808
- async function pr_get_url(branch_name) {
3809
- const pull = await read_pull_of_branch(branch_name);
3810
- if (pull?.html_url === void 0) return void 0;
3811
- return git_gh_helpers.parse_pr_state_string(pull.html_url);
3812
- }
3813
- async function pr_get_body(branch_name) {
3814
- const pull = await read_pull_of_branch(branch_name);
3815
- const body = pull?.body;
3816
- return typeof body === "string" && body.length > 0 ? body : void 0;
3817
- }
3818
- async function pr_view(branch_name) {
3819
- const pull = await read_pull_of_branch(branch_name);
3820
- if (pull === void 0) return "";
3821
- return JSON.stringify(git_gh_pr_rest.to_pr_info(pull));
3822
- }
3823
- async function read_comments(branch_name, to_path, to_json) {
3824
- const pr_number = await resolve_pr_number(branch_name);
3825
- if (pr_number === void 0) return void 0;
3826
- const path7 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
3827
- try {
3828
- return to_json(await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true }));
3829
- } catch {
3830
- return void 0;
3831
- }
3832
- }
3833
- function to_comments_json(raw) {
3834
- return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
3835
- }
3836
- function as_served(raw) {
3837
- return raw;
3838
- }
3839
- async function pr_get_comments(branch_name) {
3840
- return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
3841
- }
3842
- async function pr_get_review_comments(branch_name) {
3843
- return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
3844
- }
3845
- var git_gh_pr_read = {
3846
- pr_exists,
3847
- pr_get_number,
3848
- pr_get_url,
3849
- pr_get_body,
3850
- pr_view,
3851
- pr_get_comments,
3852
- pr_get_review_comments
3853
- };
3854
-
3855
- // scripts/git/git-gh-pr-review.ts
3856
- import { z as z10 } from "zod";
3857
- var REVIEW_APPROVED = "APPROVED";
3858
- var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
3859
- var REVIEW_NONE = "";
3860
- var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
3861
- var rest_review_schema = z10.looseObject({
3862
- state: z10.string().optional(),
3863
- user: z10.looseObject({ login: z10.string().optional() }).nullish()
3864
- });
3865
- var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
3866
- function parse_rest_reviews(reviews_json) {
3867
- const parsed = parse_json_array_or_undefined(reviews_json, rest_review_schema);
3868
- if (parsed === void 0) throw new Error(NOT_A_REVIEW_LISTING);
3869
- return parsed;
3870
- }
3871
- var UNATTRIBUTED_REVIEWER = "(no author)";
3872
- function read_reviewer(review) {
3873
- const login = review.user?.login;
3874
- return typeof login === "string" && login.length > 0 ? login : UNATTRIBUTED_REVIEWER;
3875
- }
3876
- function read_state_changing(review) {
3877
- const state = review.state?.toUpperCase();
3878
- return state !== void 0 && STATE_CHANGING_REVIEWS.has(state) ? state : void 0;
3879
- }
3880
- function fold_latest_states(reviews) {
3881
- const latest = /* @__PURE__ */ new Map();
3882
- for (const review of reviews) {
3883
- const state = read_state_changing(review);
3884
- if (state !== void 0) latest.set(read_reviewer(review), state);
3885
- }
3886
- return latest;
3887
- }
3888
- function to_review_decision(reviews_json) {
3889
- const latest = new Set(fold_latest_states(parse_rest_reviews(reviews_json)).values());
3890
- if (latest.has(REVIEW_CHANGES_REQUESTED)) return REVIEW_CHANGES_REQUESTED;
3891
- return latest.has(REVIEW_APPROVED) ? REVIEW_APPROVED : REVIEW_NONE;
3892
- }
3893
- var git_gh_pr_review = { to_review_decision, parse_rest_reviews };
3894
-
3895
- // scripts/git/git-gh-pr-rollup.ts
3896
- import { z as z11 } from "zod";
3897
- var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
3898
- var TYPE_NAME_KEY = "__typename";
3899
- var rollup_element_schema = z11.looseObject({});
3900
- var check_runs_page_schema = z11.looseObject({
3901
- // Required, not optional: GitHub always names the key, and accepting a page without one would
3902
- // answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
3903
- check_runs: z11.array(rollup_element_schema)
3904
- });
3905
- var id_holder_schema = z11.object({ id: z11.number() });
3906
- var status_page_schema = z11.looseObject({
3907
- statuses: z11.array(rollup_element_schema)
3908
- });
3909
- var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
3910
- var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
3911
- function read_pages(raw_json, schema, message) {
3912
- const pages = parse_json_array_or_undefined(raw_json, schema);
3913
- if (pages === void 0) throw new Error(message);
3914
- return pages;
3915
- }
3916
- var NO_SUITE = -1;
3917
- function read_id(value) {
3918
- const parsed = id_holder_schema.safeParse(value);
3919
- return parsed.success ? parsed.data.id : void 0;
3920
- }
3921
- function check_run_group_key(run2) {
3922
- const { name, app } = run2;
3923
- if (typeof name !== "string") return void 0;
3924
- return JSON.stringify([name, read_id(app)]);
3925
- }
3926
- function read_rank(run2) {
3927
- const id = read_id(run2);
3928
- if (id === void 0) return void 0;
3929
- return { suite: read_id(run2["check_suite"]) ?? NO_SUITE, run: id };
3930
- }
3931
- function is_ranked_above(candidate, current) {
3932
- if (candidate.suite !== current.suite) return candidate.suite > current.suite;
3933
- return candidate.run > current.run;
3934
- }
3935
- function should_replace(previous, rank) {
3936
- const previous_rank = previous === void 0 ? void 0 : read_rank(previous);
3937
- return previous_rank === void 0 || is_ranked_above(rank, previous_rank);
3938
- }
3939
- function remember_newest_run(run2, newest) {
3940
- const key = check_run_group_key(run2);
3941
- const rank = read_rank(run2);
3942
- if (key === void 0 || rank === void 0) return;
3943
- if (should_replace(newest.get(key), rank)) newest.set(key, run2);
3944
- }
3945
- function is_newest_run(run2, newest) {
3946
- const key = check_run_group_key(run2);
3947
- if (key === void 0 || read_rank(run2) === void 0) return true;
3948
- return newest.get(key) === run2;
3949
- }
3950
- function to_check_run_items(check_runs_json) {
3951
- const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
3952
- const runs = pages.flatMap((page) => page.check_runs);
3953
- const newest = /* @__PURE__ */ new Map();
3954
- for (const run2 of runs) remember_newest_run(run2, newest);
3955
- return runs.filter((run2) => is_newest_run(run2, newest));
3956
- }
3957
- function to_status_context_items(status_json) {
3958
- const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
3959
- return pages.flatMap((page) => page.statuses).map((status) => ({ ...status, [TYPE_NAME_KEY]: STATUS_CONTEXT_TYPE_NAME }));
3960
- }
3961
- function to_status_check_rollup(input) {
3962
- return [
3963
- ...to_check_run_items(input.check_runs_json),
3964
- ...to_status_context_items(input.status_json)
3965
- ];
3966
- }
3967
- var git_gh_pr_rollup = {
3968
- to_check_run_items,
3969
- to_status_context_items,
3970
- to_status_check_rollup
3971
- };
3972
-
3973
- // scripts/git/git-gh-pr-snapshot.ts
3974
- var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
3975
- async function read_object_pages(path7) {
3976
- return await git_gh_exec.exec_gh_api({
3977
- path: `${path7}${FULL_PAGE_QUERY}`,
3978
- should_paginate: true,
3979
- should_slurp: true
3980
- });
3981
- }
3982
- async function read_array_pages(path7) {
3983
- return await git_gh_exec.exec_gh_api({
3984
- path: `${path7}${FULL_PAGE_QUERY}`,
3985
- should_paginate: true
3986
- });
3987
- }
3988
- function read_head_sha(pull) {
3989
- const sha = pull.head?.sha;
3990
- if (sha === void 0) throw new Error(NO_HEAD_SHA_MESSAGE);
3991
- return sha;
3992
- }
3993
- async function read_status_check_rollup(commit_sha) {
3994
- const [check_runs_json, status_json] = await Promise.all([
3995
- read_object_pages(git_gh_api_path.commit_check_runs_api_path(commit_sha)),
3996
- read_object_pages(git_gh_api_path.commit_status_api_path(commit_sha))
3997
- ]);
3998
- return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
3999
- }
4000
- async function pr_get_review_decision(pr_number) {
4001
- const path7 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
4002
- return git_gh_pr_review.to_review_decision(await read_array_pages(path7));
4003
- }
4004
- async function build_checks_fields(pull) {
4005
- return {
4006
- statusCheckRollup: await read_status_check_rollup(read_head_sha(pull)),
4007
- mergeStateStatus: to_gh_state(pull.mergeable_state)
4008
- };
4009
- }
4010
- async function resolve_pull(branch_name) {
4011
- const pr_number = await require_pr_number(branch_name);
4012
- return { pr_number, pull: await read_pull(pr_number) };
4013
- }
4014
- async function pr_get_checks_snapshot(branch_name) {
4015
- const { pr_number, pull } = await resolve_pull(branch_name);
4016
- return { pr_number, snapshot_json: JSON.stringify(await build_checks_fields(pull)) };
4017
- }
4018
- async function pr_get_state_snapshot(branch_name) {
4019
- const { pr_number, pull } = await resolve_pull(branch_name);
4020
- const [fields, review_decision] = await Promise.all([
4021
- build_checks_fields(pull),
4022
- pr_get_review_decision(pr_number)
4023
- ]);
4024
- return JSON.stringify({ ...fields, reviewDecision: review_decision });
4025
- }
4026
- var git_gh_pr_snapshot = {
4027
- pr_get_state_snapshot,
4028
- pr_get_checks_snapshot,
4029
- pr_get_review_decision
4030
- };
4031
-
4032
- // scripts/git/git-gh-pr.ts
4033
- var HTML_URL_FILTER2 = ".html_url";
4034
- var PUT_METHOD = "PUT";
4035
- var PATCH_METHOD2 = "PATCH";
4036
- var MERGE_COMMIT_METHOD = "merge";
4037
- var MERGE_REQUEST_TIMEOUT_MS = 18e4;
4038
- var MERGE_UNCONFIRMED_MESSAGE = "gh api could not read the pull request back after the merge request failed";
4039
- var MERGE_READ_BACK_ATTEMPTS = 3;
4040
- var MERGE_READ_BACK_INTERVAL_MS = 1e3;
4041
- var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
4042
- var LABELS_SEGMENT2 = "/labels";
4043
- var PR_LABELS = z12.array(z12.object({ name: z12.string() }));
4044
- async function add_pr_classification(number, label) {
4045
- await git_gh_exec.exec_gh_api({
4046
- path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
4047
- body: JSON.stringify({ labels: [label] })
4048
- });
4049
- }
4050
- async function pr_get_classification(branch_name) {
4051
- const number = await require_pr_number(branch_name);
4052
- const response = await git_gh_exec.exec_gh_api({
4053
- path: `${git_gh_api_path.issue_api_path(String(number))}${LABELS_SEGMENT2}`
4054
- });
4055
- const names = PR_LABELS.parse(JSON.parse(response)).map((label) => label.name.toLowerCase());
4056
- const selected = names.filter((name) => pr_classification.is_classification(name));
4057
- if (selected.length === 0) return void 0;
4058
- const error = pr_classification.classification_error(names, "human");
4059
- if (error !== void 0) throw new Error(error);
4060
- return selected[0];
4061
- }
4062
- async function pr_ensure_classification(branch_name, label) {
4063
- if (await pr_get_classification(branch_name) !== void 0) return;
4064
- const number = await require_pr_number(branch_name);
4065
- await add_pr_classification(String(number), label);
4066
- }
4067
- async function post_pull_request(title, body) {
4068
- const base = await git_command.get_default_branch();
4069
- const head = await git_command.branch();
4070
- forget_pr_numbers();
4071
- try {
4072
- return await git_gh_exec.exec_gh_api({
4073
- path: git_gh_api_path.pulls_api_path(),
4074
- body: JSON.stringify({ title, body, head, base }),
4075
- jq_filter: HTML_URL_FILTER2
4076
- });
4077
- } catch (error) {
4078
- return git_gh_helpers.handle_pr_create_error(error);
4079
- }
4080
- }
4081
- async function pr_create(title, body, label) {
4082
- const classification_error2 = pr_classification.classification_error([label], "human");
4083
- if (classification_error2 !== void 0) throw new Error(classification_error2);
4084
- const url = await post_pull_request(title, body);
4085
- const number = PR_NUMBER_PATTERN.exec(url)?.[1];
4086
- if (number === void 0) throw new Error(`Could not identify the created pull request: ${url}`);
4087
- await add_pr_classification(number, label);
4088
- return url;
4089
- }
4090
- async function pr_comment(branch_name, body) {
4091
- const pr_number = await require_pr_number(branch_name);
4092
- return await git_gh_issue_write.issue_comment(String(pr_number), body);
4093
- }
4094
- async function pr_update_body(branch_name, body) {
4095
- const pr_number = await require_pr_number(branch_name);
4096
- await git_gh_exec.exec_gh_api({
4097
- path: git_gh_api_path.pull_api_path(String(pr_number)),
4098
- method: PATCH_METHOD2,
4099
- body: JSON.stringify({ body })
4100
- });
4101
- }
4102
- async function put_merge(pr_number) {
4103
- await git_gh_exec.exec_gh_api({
4104
- path: git_gh_api_path.pull_merge_api_path(String(pr_number)),
4105
- method: PUT_METHOD,
4106
- body: JSON.stringify({ merge_method: MERGE_COMMIT_METHOD }),
4107
- timeout_ms: MERGE_REQUEST_TIMEOUT_MS
4108
- });
4109
- }
4110
- async function read_merge_state(pr_number) {
4111
- try {
4112
- return git_gh_pr_rest.is_merged(await read_pull(pr_number));
4113
- } catch (error) {
4114
- console.warn(`Could not read the pull request back: ${String(error)}`);
4115
- return void 0;
4116
- }
4117
- }
4118
- async function sleep_between_read_backs(attempt) {
4119
- if (attempt < MERGE_READ_BACK_ATTEMPTS - 1) await poll.sleep(MERGE_READ_BACK_INTERVAL_MS);
4120
- }
4121
- async function has_merge_landed(pr_number, merge_error) {
4122
- for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
4123
- const is_merged2 = await read_merge_state(pr_number);
4124
- if (is_merged2 !== void 0) return is_merged2;
4125
- await sleep_between_read_backs(attempt);
4126
- }
4127
- throw new Error(MERGE_UNCONFIRMED_MESSAGE, { cause: merge_error });
4128
- }
4129
- async function pr_merge(branch_name) {
4130
- const pr_number = await require_pr_number(branch_name);
4131
- try {
4132
- await put_merge(pr_number);
4133
- } catch (error) {
4134
- if (!await has_merge_landed(pr_number, error)) throw error;
4135
- }
4136
- }
4137
- var git_gh_pr = {
4138
- ...git_gh_pr_read,
4139
- ...git_gh_pr_snapshot,
4140
- pr_create,
4141
- pr_get_classification,
4142
- pr_ensure_classification,
4143
- pr_comment,
4144
- pr_merge,
4145
- pr_update_body
4146
- };
4147
-
4148
- // scripts/git/git-gh-pr-auto-merge.ts
4149
- var GRAPHQL_PATH = "graphql";
4150
- var OPEN_PULLS_QUERY = `${git_gh_api_path.FULL_PAGE_QUERY}&state=open`;
4151
- var MISSING_NODE_ID_MESSAGE = "gh api answered a pull request without a node_id";
4152
- var ENABLE_AUTO_MERGE_MUTATION = "mutation($id: ID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE }) { clientMutationId } }";
4153
- async function pr_enable_auto_merge(branch_name) {
4154
- const pull = await read_pull(await require_pr_number(branch_name));
4155
- if (pull.node_id === void 0) throw new Error(MISSING_NODE_ID_MESSAGE);
4156
- await git_gh_exec.exec_gh_api({
4157
- path: GRAPHQL_PATH,
4158
- body: JSON.stringify({ query: ENABLE_AUTO_MERGE_MUTATION, variables: { id: pull.node_id } })
4159
- });
4160
- }
4161
- async function pr_is_merged(branch_name) {
4162
- try {
4163
- return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)));
4164
- } catch {
4165
- return false;
4166
- }
4167
- }
4168
- function head_reference_of(pull) {
4169
- return pull.head?.ref ?? "";
4170
- }
4171
- function has_head_prefix(pull, prefix) {
4172
- return git_gh_pr_rest.is_same_repository_head(pull) && head_reference_of(pull).startsWith(prefix);
4173
- }
4174
- function to_open_pull(pull) {
4175
- return {
4176
- number: pull.number,
4177
- head_ref: head_reference_of(pull),
4178
- has_auto_merge: pull.auto_merge !== null && pull.auto_merge !== void 0
4179
- };
4180
- }
4181
- async function pr_list_open_with_head_prefix(prefix) {
4182
- const json = await git_gh_exec.exec_gh_api({
4183
- path: `${git_gh_api_path.pulls_api_path()}${OPEN_PULLS_QUERY}`,
4184
- should_paginate: true
4185
- });
4186
- return git_gh_pr_rest.parse_rest_pulls(json).filter((pull) => has_head_prefix(pull, prefix)).map((pull) => to_open_pull(pull));
4187
- }
4188
- var git_gh_pr_auto_merge = {
4189
- pr_enable_auto_merge,
4190
- pr_is_merged,
4191
- pr_list_open_with_head_prefix
4192
- };
4193
-
4194
- // scripts/git/git-gh-repo.ts
4195
- async function repo_get_name_with_owner() {
4196
- try {
4197
- const result = await git_gh_exec.exec_gh_api({
4198
- path: git_gh_api_path.repo_api_path(),
4199
- jq_filter: ".full_name"
4200
- });
4201
- return git_gh_helpers.parse_pr_state_string(result);
4202
- } catch {
4203
- return void 0;
4204
- }
4205
- }
4206
- var git_gh_repo = {
4207
- repo_get_name_with_owner
4208
- };
4209
-
4210
- // scripts/git/git-pr-checks-parse.ts
4211
- var CHECK_STATUS_PASS = "pass";
4212
- var CHECK_STATUS_PENDING = "pending";
4213
- var CHECK_STATUS_FAIL = "fail";
4214
- var CHECK_STATUS_MISSING = "missing";
4215
- var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["success", "skipped", "neutral"]);
4216
- var KEY_TYPE_NAME = "__typename";
4217
- var KEY_STATE = "state";
4218
- var KEY_STATUS = "status";
4219
- var KEY_CONCLUSION = "conclusion";
4220
- var KEY_NAME = "name";
4221
- var KEY_CONTEXT = "context";
4222
- function read_string(value) {
4223
- if (typeof value !== "string") return void 0;
4224
- const trimmed = value.trim();
4225
- if (trimmed.length === 0) return void 0;
4226
- return trimmed;
4227
- }
4228
- var PENDING_STATUS_STATES = /* @__PURE__ */ new Set(["pending", "expected"]);
4229
- function parse_status_context(item) {
4230
- const state = read_string(item[KEY_STATE])?.toLowerCase();
4231
- if (state === "success") return CHECK_STATUS_PASS;
4232
- if (state !== void 0 && PENDING_STATUS_STATES.has(state)) return CHECK_STATUS_PENDING;
4233
- return CHECK_STATUS_FAIL;
4234
- }
4235
- function is_passing_conclusion(item) {
4236
- const conclusion = read_string(item[KEY_CONCLUSION])?.toLowerCase();
4237
- return conclusion !== void 0 && PASSING_CONCLUSIONS.has(conclusion);
4238
- }
4239
- function parse_check_run(item) {
4240
- const status = read_string(item[KEY_STATUS])?.toLowerCase();
4241
- if (status !== "completed") return CHECK_STATUS_PENDING;
4242
- return is_passing_conclusion(item) ? CHECK_STATUS_PASS : CHECK_STATUS_FAIL;
4243
- }
4244
- function parse_rollup_status(item) {
4245
- const type_name = read_string(item[KEY_TYPE_NAME]);
4246
- if (type_name === "StatusContext") return parse_status_context(item);
4247
- return parse_check_run(item);
4248
- }
4249
- function read_rollup_array(parsed) {
4250
- const result = pr_raw_schema.safeParse(parsed);
4251
- if (!result.success) return [];
4252
- return result.data.statusCheckRollup ?? [];
4253
- }
4254
- function parse_json_safe(raw_json) {
4255
- try {
4256
- return JSON.parse(raw_json);
4257
- } catch {
4258
- return void 0;
4259
- }
4260
- }
4261
- function parse_rollup_item(item) {
4262
- const name = read_string(item[KEY_NAME]) ?? read_string(item[KEY_CONTEXT]);
4263
- if (name === void 0) return void 0;
4264
- return { name, status: parse_rollup_status(item) };
4265
- }
4266
- function parse_rollup_checks(raw_json) {
4267
- const parsed = parse_json_safe(raw_json);
4268
- const rollup = read_rollup_array(parsed);
4269
- const checks = [];
4270
- for (const item of rollup) {
4271
- const parsed_item = parse_rollup_item(item);
4272
- if (parsed_item !== void 0) checks.push(parsed_item);
4273
- }
4274
- return checks;
4275
- }
4276
- function parse_pr_state_snapshot(raw_json) {
4277
- const parsed = parse_json_safe(raw_json);
4278
- const result = pr_raw_schema.safeParse(parsed);
4279
- const data = result.success ? result.data : void 0;
4280
- return {
4281
- rollup: parse_rollup_checks(raw_json),
4282
- merge_state_status: read_string(data?.mergeStateStatus),
4283
- review_decision: read_string(data?.reviewDecision)
4284
- };
4285
- }
4286
-
4287
- // scripts/git/git-pr-checks-eval.ts
4288
- var DEFAULT_REQUIRED_CHECKS = ["SonarQube"];
4289
- var CODERABBIT_CHECK_NAME = "CodeRabbit";
4290
- var REQUIRED_CHECKS_ENV_VAR = "JOSH_REQUIRED_CHECKS";
4291
- function parse_required_checks() {
4292
- const raw = process.env[REQUIRED_CHECKS_ENV_VAR];
4293
- if (!raw) return DEFAULT_REQUIRED_CHECKS;
4294
- return raw.split(",").map((check3) => check3.trim()).filter((check3) => check3.length > 0);
4295
- }
4296
- var REQUIRED_CHECKS = parse_required_checks();
4297
- var MERGE_STATE_CLEAN = "CLEAN";
4298
- var MERGE_STATE_UNSTABLE = "UNSTABLE";
4299
- var MERGE_STATE_DIRTY = "DIRTY";
4300
- var REVIEW_CHANGES_REQUESTED2 = "CHANGES_REQUESTED";
4301
- var CHECK_SUITE_SEPARATOR = " / ";
4302
- var FAILURE_MESSAGE_PREFIX = "PR checks failed";
4303
- var FAILURE_REASON_REVIEW = "review requested changes";
4304
- var FAILURE_REASON_CHECKS = "failed checks";
4305
- var FAILURE_REASON_CONFLICT = "merge conflict";
4306
- function is_required_match(check_name, required_name) {
4307
- if (check_name === required_name) return true;
4308
- return check_name.startsWith(`${required_name}${CHECK_SUITE_SEPARATOR}`);
4309
- }
4310
- function find_required_check(checks, required_name) {
4311
- return checks.find((check3) => is_required_match(check3.name, required_name));
4312
- }
4313
- function read_required_statuses(checks) {
4314
- return REQUIRED_CHECKS.map(
4315
- (name) => find_required_check(checks, name)?.status ?? CHECK_STATUS_MISSING
4316
- );
4317
- }
4318
- function is_review_blocked(review_decision) {
4319
- return review_decision === REVIEW_CHANGES_REQUESTED2;
4320
- }
4321
- function is_merge_conflict(merge_state_status) {
4322
- return merge_state_status === MERGE_STATE_DIRTY;
4323
- }
4324
- function evaluate_failure_state(input) {
4325
- if (is_merge_conflict(input.merge_state_status) || is_review_blocked(input.review_decision) || input.failed_checks.length > 0) {
4326
- return "failure";
4327
- }
4328
- return void 0;
4329
- }
4330
- function is_merge_state_clean(merge_state_status) {
4331
- return merge_state_status === MERGE_STATE_CLEAN;
4332
- }
4333
- function is_every_required_passing(statuses) {
4334
- return statuses.every((status) => status === CHECK_STATUS_PASS);
4335
- }
4336
- function is_coderabbit_check(check_name) {
4337
- return is_required_match(check_name, CODERABBIT_CHECK_NAME);
4338
- }
4339
- function is_unstable_only_from_coderabbit(snapshot) {
4340
- if (snapshot.merge_state_status !== MERGE_STATE_UNSTABLE) return false;
4341
- const non_passing = snapshot.rollup.filter((check3) => check3.status !== CHECK_STATUS_PASS);
4342
- if (non_passing.length === 0) return false;
4343
- return non_passing.every((check3) => is_coderabbit_check(check3.name));
4344
- }
4345
- function is_required_check(check_name) {
4346
- return REQUIRED_CHECKS.some((required) => is_required_match(check_name, required));
4347
- }
4348
- function is_blocking_failure(check3) {
4349
- if (check3.status !== CHECK_STATUS_FAIL) return false;
4350
- return !is_coderabbit_check(check3.name) || is_required_check(check3.name);
4351
- }
4352
- function collect_blocking_failures(snapshot) {
4353
- return snapshot.rollup.filter((check3) => is_blocking_failure(check3)).map((check3) => check3.name);
4354
- }
4355
- function is_mergeable_state(snapshot) {
4356
- if (is_merge_state_clean(snapshot.merge_state_status)) return true;
4357
- return is_unstable_only_from_coderabbit(snapshot);
4358
- }
4359
- function collect_failure_reasons(snapshot) {
4360
- const reasons = [];
4361
- if (is_merge_conflict(snapshot.merge_state_status)) reasons.push(FAILURE_REASON_CONFLICT);
4362
- if (is_review_blocked(snapshot.review_decision)) reasons.push(FAILURE_REASON_REVIEW);
4363
- const failed = collect_blocking_failures(snapshot);
4364
- if (failed.length > 0) reasons.push(`${FAILURE_REASON_CHECKS}: ${failed.join(", ")}`);
4365
- return reasons;
4366
- }
4367
- function describe_pr_failure(snapshot) {
4368
- const reasons = collect_failure_reasons(snapshot);
4369
- if (reasons.length === 0) return `${FAILURE_MESSAGE_PREFIX}.`;
4370
- return `${FAILURE_MESSAGE_PREFIX} (${reasons.join("; ")}).`;
4371
- }
4372
- function evaluate_pr_state(snapshot) {
4373
- const statuses = read_required_statuses(snapshot.rollup);
4374
- const failure = evaluate_failure_state({
4375
- review_decision: snapshot.review_decision,
4376
- failed_checks: collect_blocking_failures(snapshot),
4377
- merge_state_status: snapshot.merge_state_status
4378
- });
4379
- if (failure !== void 0) return failure;
4380
- if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) {
4381
- return "success";
4382
- }
4383
- return "pending";
4384
- }
4385
- function is_review_decision_decisive(snapshot) {
4386
- return evaluate_pr_state(snapshot) !== "pending";
4387
- }
4388
-
4389
- // scripts/git/git-pr-checks.ts
4390
- var SECONDS_TO_MS = 1e3;
4391
- var CHECK_WAIT_INTERVAL_MS = 1e4;
4392
- var DEFAULT_TIMEOUT_SECONDS = 1920;
4393
- function compute_max_attempts(timeout_seconds, interval_ms) {
4394
- return Math.ceil(timeout_seconds / (interval_ms / SECONDS_TO_MS)) + 1;
4395
- }
4396
- var DEFAULT_MAX_ATTEMPTS = compute_max_attempts(DEFAULT_TIMEOUT_SECONDS, CHECK_WAIT_INTERVAL_MS);
4397
- function get_configured_max_attempts() {
4398
- const environment_seconds = Number(process.env["JOSH_CI_TIMEOUT_SECONDS"]);
4399
- return Number.isFinite(environment_seconds) && environment_seconds > 0 ? compute_max_attempts(environment_seconds, CHECK_WAIT_INTERVAL_MS) : DEFAULT_MAX_ATTEMPTS;
4400
- }
4401
- var CHECK_MAX_ATTEMPTS = get_configured_max_attempts();
4402
- var DEFAULT_STABLE_READS = 2;
4403
- var MAX_CONSECUTIVE_READ_FAILURES = 3;
4404
- var PR_CHECKS_TIMEOUT_MESSAGE = "Timed out while waiting for PR checks to complete.";
4405
- function is_pr_checks_timeout(error) {
4406
- return error instanceof Error && error.message === PR_CHECKS_TIMEOUT_MESSAGE;
4407
- }
4408
- var SHOULD_ALWAYS_READ_REVIEW_DECISION = () => true;
4409
- var MERGE_GATE_EVALUATOR = {
4410
- evaluate: evaluate_pr_state,
4411
- describe: describe_pr_failure,
4412
- should_read_review_decision: is_review_decision_decisive
4413
- };
4414
- function advance_stable_count(previous, state) {
4415
- return state === "success" ? previous + 1 : 0;
4416
- }
4417
- function classify_poll_result(input) {
4418
- const state = input.evaluator.evaluate(input.snapshot);
4419
- if (state === "failure") throw new Error(input.evaluator.describe(input.snapshot));
4420
- const next_stable_count = advance_stable_count(input.stable_count, state);
4421
- return {
4422
- is_done: next_stable_count >= input.required_stable_reads,
4423
- next_stable_count
4424
- };
4425
- }
4426
- async function read_pr_state(input, evaluator) {
4427
- try {
4428
- const snapshot = await input.options.fetcher(
4429
- input.options.branch_name,
4430
- evaluator.should_read_review_decision ?? SHOULD_ALWAYS_READ_REVIEW_DECISION
4431
- );
4432
- return { kind: "read", snapshot };
4433
- } catch (error) {
4434
- return { kind: "unreadable", cause: error };
4435
- }
4436
- }
4437
- async function sleep_before_next_attempt(input) {
4438
- if (input.attempt < input.options.max_attempts - 1) {
4439
- await poll.sleep(input.options.interval_ms);
4440
- }
4441
- }
4442
- async function retry_after_unreadable(input, cause) {
4443
- const next_failure_count = input.failure_count + 1;
4444
- if (next_failure_count >= MAX_CONSECUTIVE_READ_FAILURES) throw cause;
4445
- console.warn(
4446
- `Could not read the PR state (${String(next_failure_count)}/${String(MAX_CONSECUTIVE_READ_FAILURES)}); asking again on the next poll: ${String(cause)}`
4447
- );
4448
- await sleep_before_next_attempt(input);
4449
- return { next_stable_count: 0, next_failure_count, read_failure: cause };
4450
- }
4451
- function to_exhaustion_error(last_read_failure) {
4452
- return last_read_failure ?? new Error(PR_CHECKS_TIMEOUT_MESSAGE);
4453
- }
4454
- async function attempt_pr_success_poll(input) {
4455
- const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
4456
- const read2 = await read_pr_state(input, evaluator);
4457
- if (read2.kind === "unreadable") return await retry_after_unreadable(input, read2.cause);
4458
- const classification = classify_poll_result({
4459
- snapshot: read2.snapshot,
4460
- stable_count: input.stable_count,
4461
- required_stable_reads: input.options.required_stable_reads,
4462
- evaluator
4463
- });
4464
- if (classification.is_done) {
4465
- return { snapshot: read2.snapshot, next_stable_count: 0, next_failure_count: 0 };
4466
- }
4467
- await sleep_before_next_attempt(input);
4468
- return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
4469
- }
4470
- async function wait_for_pr_success(options) {
4471
- let carried = { next_stable_count: 0, next_failure_count: 0 };
4472
- for (let attempt = 0; attempt < options.max_attempts; attempt += 1) {
4473
- console.info(`Checking PR status\u2026 (${String(attempt + 1)}/${String(options.max_attempts)})`);
4474
- carried = await attempt_pr_success_poll({
4475
- options,
4476
- stable_count: carried.next_stable_count,
4477
- failure_count: carried.next_failure_count,
4478
- attempt
4479
- });
4480
- if (carried.snapshot !== void 0) return carried.snapshot;
4481
- }
4482
- throw to_exhaustion_error(carried.read_failure);
4483
- }
4484
- async function default_fetch_pr_state(branch_name, should_read_review_decision = SHOULD_ALWAYS_READ_REVIEW_DECISION) {
4485
- const checks = await git_gh_pr_snapshot.pr_get_checks_snapshot(branch_name);
4486
- const snapshot = parse_pr_state_snapshot(checks.snapshot_json);
4487
- if (!should_read_review_decision(snapshot)) return snapshot;
4488
- const decision = await git_gh_pr_snapshot.pr_get_review_decision(checks.pr_number);
4489
- return { ...snapshot, review_decision: read_string(decision) };
4490
- }
4491
-
4492
- // scripts/git/git-pr-checks-watch.ts
4493
- var PR_CHECKS_WATCH_TIMEOUT_MS = 12e4;
4494
- var WATCH_TIMED_OUT_NOTE = "\u23F1\uFE0F pr checks --watch timed out.";
4495
- var NO_CHECKS_MESSAGE = "gh api reported no checks on the branch";
4496
- var CHECKS_FAILED_MESSAGE = "gh api reported a failing check on the branch";
4497
- function is_awaited_check(check3) {
4498
- return !is_coderabbit_check(check3.name) || is_required_check(check3.name);
4499
- }
4500
- function evaluate_checks_settled(snapshot) {
4501
- const awaited = snapshot.rollup.filter((check3) => is_awaited_check(check3));
4502
- const considered = awaited.length === 0 ? snapshot.rollup : awaited;
4503
- if (considered.length === 0 || considered.some((check3) => check3.status === CHECK_STATUS_PENDING)) {
4504
- return "pending";
4505
- }
4506
- return snapshot.rollup.some((check3) => check3.status === CHECK_STATUS_FAIL) ? "failure" : "success";
4507
- }
4508
- function describe_checks_failure(snapshot) {
4509
- const failed = snapshot.rollup.filter((check3) => check3.status === CHECK_STATUS_FAIL);
4510
- return `${CHECKS_FAILED_MESSAGE}: ${failed.map((check3) => check3.name).join(", ")}`;
4511
- }
4512
- var SHOULD_NEVER_READ_REVIEW_DECISION = () => false;
4513
- var CHECKS_SETTLED_EVALUATOR = {
4514
- evaluate: evaluate_checks_settled,
4515
- describe: describe_checks_failure,
4516
- should_read_review_decision: SHOULD_NEVER_READ_REVIEW_DECISION
4517
- };
4518
- async function watch_until_settled(branch_name) {
4519
- await wait_for_pr_success({
4520
- branch_name,
4521
- fetcher: default_fetch_pr_state,
4522
- interval_ms: CHECK_WAIT_INTERVAL_MS,
4523
- max_attempts: compute_max_attempts(
4524
- PR_CHECKS_WATCH_TIMEOUT_MS / SECONDS_TO_MS,
4525
- CHECK_WAIT_INTERVAL_MS
4526
- ),
4527
- required_stable_reads: DEFAULT_STABLE_READS,
4528
- evaluator: CHECKS_SETTLED_EVALUATOR
4529
- });
4530
- }
4531
- async function fail_when_no_checks(branch_name) {
4532
- const snapshot = await default_fetch_pr_state(branch_name, SHOULD_NEVER_READ_REVIEW_DECISION);
4533
- if (snapshot.rollup.length === 0) throw new Error(NO_CHECKS_MESSAGE);
4534
- }
4535
- async function pr_checks_watch(branch_name) {
4536
- try {
4537
- await watch_until_settled(branch_name);
4538
- return { timed_out: false };
4539
- } catch (error) {
4540
- if (!is_pr_checks_timeout(error)) throw error;
4541
- await fail_when_no_checks(branch_name);
4542
- console.info(WATCH_TIMED_OUT_NOTE);
4543
- return { timed_out: true };
4544
- }
4545
- }
4546
- var git_pr_checks_watch = { pr_checks_watch };
4547
-
4548
- // scripts/git/git-gh-command.ts
4549
- var git_gh_command = {
4550
- ...git_gh_pr,
4551
- ...git_gh_pr_auto_merge,
4552
- pr_checks_watch: git_pr_checks_watch.pr_checks_watch,
4553
- ...git_gh_issue,
4554
- ...git_gh_repo
4555
- };
4556
-
4557
3325
  // scripts/epic/epic-busy.ts
4558
3326
  var LISTING_LIMIT = 100;
4559
3327
  var PARKED_LABELS = /* @__PURE__ */ new Set([NEEDS_DECISION_LABEL, ALREADY_DONE_LABEL]);
@@ -4610,37 +3378,36 @@ var epic_busy = {
4610
3378
  unreadable_message,
4611
3379
  truncated_message,
4612
3380
  busy_reason,
4613
- format_holders,
4614
3381
  is_parked,
4615
3382
  parse_listing,
4616
3383
  read_repository
4617
3384
  };
4618
3385
 
4619
3386
  // scripts/run/run-liveness.ts
4620
- import { readFileSync as readFileSync6, realpathSync, statSync } from "node:fs";
3387
+ import { readFileSync as readFileSync5, realpathSync, statSync } from "node:fs";
4621
3388
  import { homedir, tmpdir } from "node:os";
4622
- import path6 from "node:path";
4623
- import { setTimeout as sleep2 } from "node:timers/promises";
3389
+ import path8 from "node:path";
3390
+ import { setTimeout as sleep } from "node:timers/promises";
4624
3391
 
4625
3392
  // scripts/agent/agent-event.ts
4626
- import { z as z14 } from "zod";
3393
+ import { z as z9 } from "zod";
4627
3394
 
4628
3395
  // scripts/agent/claude-result-event.ts
4629
- import { z as z13 } from "zod";
4630
- var RESULT_EVENT_SCHEMA = z13.looseObject({
4631
- type: z13.literal("result"),
4632
- is_error: z13.unknown().optional(),
4633
- subtype: z13.unknown().optional(),
4634
- num_turns: z13.unknown().optional(),
4635
- permission_denials: z13.unknown().optional(),
4636
- result: z13.unknown().optional(),
4637
- session_id: z13.unknown().optional(),
4638
- usage: z13.unknown().optional()
3396
+ import { z as z8 } from "zod";
3397
+ var RESULT_EVENT_SCHEMA = z8.looseObject({
3398
+ type: z8.literal("result"),
3399
+ is_error: z8.unknown().optional(),
3400
+ subtype: z8.unknown().optional(),
3401
+ num_turns: z8.unknown().optional(),
3402
+ permission_denials: z8.unknown().optional(),
3403
+ result: z8.unknown().optional(),
3404
+ session_id: z8.unknown().optional(),
3405
+ usage: z8.unknown().optional()
4639
3406
  });
4640
- var USAGE_SCHEMA = z13.looseObject({
4641
- input_tokens: z13.unknown().optional(),
4642
- cache_read_input_tokens: z13.unknown().optional(),
4643
- output_tokens: z13.unknown().optional()
3407
+ var USAGE_SCHEMA = z8.looseObject({
3408
+ input_tokens: z8.unknown().optional(),
3409
+ cache_read_input_tokens: z8.unknown().optional(),
3410
+ output_tokens: z8.unknown().optional()
4644
3411
  });
4645
3412
  function usable_reason(value) {
4646
3413
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
@@ -4661,7 +3428,7 @@ function usage_of(value) {
4661
3428
  };
4662
3429
  return Object.values(usage).some((count) => count !== void 0) ? usage : void 0;
4663
3430
  }
4664
- function decode2(value) {
3431
+ function decode(value) {
4665
3432
  const parsed = RESULT_EVENT_SCHEMA.safeParse(value);
4666
3433
  if (!parsed.success) return void 0;
4667
3434
  const is_error = parsed.data.is_error === true;
@@ -4676,34 +3443,34 @@ function decode2(value) {
4676
3443
  usage: usage_of(parsed.data.usage)
4677
3444
  };
4678
3445
  }
4679
- var claude_result_event = { decode: decode2 };
3446
+ var claude_result_event = { decode };
4680
3447
 
4681
3448
  // scripts/agent/agent-event.ts
4682
- var TEXT_BLOCK_SCHEMA = z14.looseObject({ type: z14.literal("text"), text: z14.string() });
4683
- var CLAUDE_MESSAGE_SCHEMA = z14.looseObject({ content: z14.array(z14.unknown()).optional() });
4684
- var CLAUDE_INIT_SCHEMA = z14.looseObject({ type: z14.literal("system"), subtype: z14.literal("init") });
4685
- var CLAUDE_ASSISTANT_SCHEMA = z14.looseObject({
4686
- type: z14.literal("assistant"),
3449
+ var TEXT_BLOCK_SCHEMA = z9.looseObject({ type: z9.literal("text"), text: z9.string() });
3450
+ var CLAUDE_MESSAGE_SCHEMA = z9.looseObject({ content: z9.array(z9.unknown()).optional() });
3451
+ var CLAUDE_INIT_SCHEMA = z9.looseObject({ type: z9.literal("system"), subtype: z9.literal("init") });
3452
+ var CLAUDE_ASSISTANT_SCHEMA = z9.looseObject({
3453
+ type: z9.literal("assistant"),
4687
3454
  message: CLAUDE_MESSAGE_SCHEMA
4688
3455
  });
4689
- var USAGE_SCHEMA2 = z14.looseObject({
4690
- input_tokens: z14.number().optional(),
4691
- cached_input_tokens: z14.number().optional(),
4692
- output_tokens: z14.number().optional()
3456
+ var USAGE_SCHEMA2 = z9.looseObject({
3457
+ input_tokens: z9.number().optional(),
3458
+ cached_input_tokens: z9.number().optional(),
3459
+ output_tokens: z9.number().optional()
4693
3460
  });
4694
- var CODEX_START_SCHEMA = z14.looseObject({ type: z14.enum(["thread.started", "turn.started"]) });
4695
- var CODEX_PROGRESS_SCHEMA = z14.looseObject({
4696
- type: z14.enum(["item.started", "item.updated", "item.completed"]),
4697
- item: z14.looseObject({ type: z14.string(), text: z14.string().optional() }).optional()
3461
+ var CODEX_START_SCHEMA = z9.looseObject({ type: z9.enum(["thread.started", "turn.started"]) });
3462
+ var CODEX_PROGRESS_SCHEMA = z9.looseObject({
3463
+ type: z9.enum(["item.started", "item.updated", "item.completed"]),
3464
+ item: z9.looseObject({ type: z9.string(), text: z9.string().optional() }).optional()
4698
3465
  });
4699
- var CODEX_COMPLETE_SCHEMA = z14.looseObject({
4700
- type: z14.literal("turn.completed"),
3466
+ var CODEX_COMPLETE_SCHEMA = z9.looseObject({
3467
+ type: z9.literal("turn.completed"),
4701
3468
  usage: USAGE_SCHEMA2.optional()
4702
3469
  });
4703
- var CODEX_FAILURE_SCHEMA = z14.looseObject({
4704
- type: z14.enum(["turn.failed", "error"]),
4705
- error: z14.looseObject({ message: z14.string().optional() }).optional(),
4706
- message: z14.string().optional()
3470
+ var CODEX_FAILURE_SCHEMA = z9.looseObject({
3471
+ type: z9.enum(["turn.failed", "error"]),
3472
+ error: z9.looseObject({ message: z9.string().optional() }).optional(),
3473
+ message: z9.string().optional()
4707
3474
  });
4708
3475
  var EMPTY_STATE = {
4709
3476
  provider: void 0,
@@ -4794,7 +3561,7 @@ function describe(state) {
4794
3561
  var agent_event = { describe, read };
4795
3562
 
4796
3563
  // scripts/issue/issue-state.ts
4797
- import { z as z15 } from "zod";
3564
+ import { z as z10 } from "zod";
4798
3565
  var NO_LABELS = "(none)";
4799
3566
  var LABEL_SEPARATOR = ", ";
4800
3567
  var STATE_LABEL = "state: ";
@@ -4804,9 +3571,9 @@ var YES = "yes";
4804
3571
  var NO = "no";
4805
3572
  var ISSUE_LABEL = "issue: ";
4806
3573
  var HUMAN_REVIEW_LABELS = /* @__PURE__ */ new Set([NEEDS_HUMAN_REVIEW_LABEL]);
4807
- var label_schema = z15.looseObject({ name: z15.string() });
4808
- var issue_state_schema = z15.looseObject({
4809
- state: z15.string(),
3574
+ var label_schema = z10.looseObject({ name: z10.string() });
3575
+ var issue_state_schema = z10.looseObject({
3576
+ state: z10.string(),
4810
3577
  labels: label_schema.array().optional()
4811
3578
  });
4812
3579
  function parse_issue_state(json) {
@@ -4838,10 +3605,6 @@ ${format_issue_state(state)}`;
4838
3605
  }
4839
3606
  var issue_state = {
4840
3607
  NO_LABELS,
4841
- STATE_LABEL,
4842
- LABELS_LABEL,
4843
- HUMAN_REVIEW_LABEL_LINE,
4844
- ISSUE_LABEL,
4845
3608
  parse_issue_state,
4846
3609
  format_issue_state,
4847
3610
  format_attributed_issue_state
@@ -4856,9 +3619,9 @@ var PROCESS_ALIVE = "alive";
4856
3619
  var PROCESS_UNKNOWN = "unknown";
4857
3620
  var DEFAULT_SILENT_MINUTES = 30;
4858
3621
  var DEFAULT_GAP_SECONDS = 5;
4859
- var MS_PER_MINUTE2 = 6e4;
3622
+ var MS_PER_MINUTE3 = 6e4;
4860
3623
  var MS_PER_SECOND2 = 1e3;
4861
- var OPEN_STATE3 = "OPEN";
3624
+ var OPEN_STATE2 = "OPEN";
4862
3625
  var STATE_FIELDS = "state,labels";
4863
3626
  var REASONS = {
4864
3627
  [ALIVE_VERDICT]: "The unit is still working: its output moved, or a process of the child is alive.",
@@ -4904,7 +3667,7 @@ function to_advice(verdict, has_work_to_stash) {
4904
3667
  if (verdict === UNDETERMINED_VERDICT) return UNDETERMINED_ADVICE;
4905
3668
  return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
4906
3669
  }
4907
- function decide3(traces) {
3670
+ function decide4(traces) {
4908
3671
  const verdict = to_verdict(traces);
4909
3672
  const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
4910
3673
  return {
@@ -4927,12 +3690,12 @@ var ALLOWED_ROOTS = [
4927
3690
  )
4928
3691
  ];
4929
3692
  function is_within(candidate, root) {
4930
- const relative = path6.relative(root, candidate);
4931
- return relative !== "" && !relative.startsWith("..") && !path6.isAbsolute(relative);
3693
+ const relative = path8.relative(root, candidate);
3694
+ return relative !== "" && !relative.startsWith("..") && !path8.isAbsolute(relative);
4932
3695
  }
4933
3696
  function to_safe_path(output_path) {
4934
- const normalized2 = path6.normalize(output_path);
4935
- if (!path6.isAbsolute(normalized2)) return void 0;
3697
+ const normalized2 = path8.normalize(output_path);
3698
+ if (!path8.isAbsolute(normalized2)) return void 0;
4936
3699
  return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
4937
3700
  }
4938
3701
  function sample_output(output_path) {
@@ -4946,7 +3709,7 @@ function read_agent_state(output_path) {
4946
3709
  const safe_path = to_safe_path(output_path);
4947
3710
  if (safe_path === void 0) return void 0;
4948
3711
  try {
4949
- const state = agent_event.read(readFileSync6(safe_path, "utf8"));
3712
+ const state = agent_event.read(readFileSync5(safe_path, "utf8"));
4950
3713
  return state.provider === void 0 ? void 0 : state;
4951
3714
  } catch {
4952
3715
  return void 0;
@@ -4960,7 +3723,7 @@ async function read_output_frozen(request) {
4960
3723
  const first = sample_output(request.output_path);
4961
3724
  if (first === void 0) return void 0;
4962
3725
  if (request.now_ms - first.mtime_ms < request.silent_window_ms) return false;
4963
- await sleep2(request.gap_ms);
3726
+ await sleep(request.gap_ms);
4964
3727
  const second = sample_output(request.output_path);
4965
3728
  if (second === void 0) return void 0;
4966
3729
  return second.mtime_ms === first.mtime_ms && second.size === first.size;
@@ -4977,7 +3740,7 @@ async function read_child_settled(issue, repo) {
4977
3740
  if (json === void 0) return void 0;
4978
3741
  const state = issue_state.parse_issue_state(json);
4979
3742
  if (state === void 0) return void 0;
4980
- return state.state !== OPEN_STATE3 || carries_settled_label(state.labels);
3743
+ return state.state !== OPEN_STATE2 || carries_settled_label(state.labels);
4981
3744
  }
4982
3745
  async function read_traces(request) {
4983
3746
  const [is_initial_child_settled, is_output_frozen, is_tree_dirty] = await Promise.all([
@@ -4986,7 +3749,7 @@ async function read_traces(request) {
4986
3749
  gap_ms: request.gap_ms ?? DEFAULT_GAP_SECONDS * MS_PER_SECOND2,
4987
3750
  now_ms: Date.now(),
4988
3751
  output_path: request.output_path,
4989
- silent_window_ms: request.silent_window_ms ?? DEFAULT_SILENT_MINUTES * MS_PER_MINUTE2
3752
+ silent_window_ms: request.silent_window_ms ?? DEFAULT_SILENT_MINUTES * MS_PER_MINUTE3
4990
3753
  }),
4991
3754
  run_hold.is_tree_dirty()
4992
3755
  ]);
@@ -5003,14 +3766,13 @@ async function read_traces(request) {
5003
3766
  }
5004
3767
  async function check(request) {
5005
3768
  run_issue_number.require_issue_number(request.issue);
5006
- return decide3(await read_traces(request));
3769
+ return decide4(await read_traces(request));
5007
3770
  }
5008
3771
  var run_liveness = {
5009
3772
  check,
5010
- decide: decide3,
3773
+ decide: decide4,
5011
3774
  describe_agent_state,
5012
3775
  read_child_settled,
5013
- read_agent_state,
5014
3776
  read_output_frozen,
5015
3777
  sample_output,
5016
3778
  to_safe_path
@@ -5018,14 +3780,14 @@ var run_liveness = {
5018
3780
 
5019
3781
  // scripts/lane/lane-report.ts
5020
3782
  var NO_LANES2 = "No lanes are open.";
5021
- var OPEN_STATE4 = "open";
3783
+ var OPEN_STATE3 = "open";
5022
3784
  var STRANDED_STATE = "stranded";
5023
3785
  var UNREADABLE_STATE = "unreadable";
5024
3786
  var UNKNOWN_VALUE = "-";
5025
3787
  var COLUMN_SEPARATOR = " ";
5026
3788
  function lane_state(lane) {
5027
3789
  if (lane.is_stranded) return STRANDED_STATE;
5028
- return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE4;
3790
+ return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE3;
5029
3791
  }
5030
3792
  function value_or_unknown(value) {
5031
3793
  return value === void 0 ? UNKNOWN_VALUE : String(value);
@@ -5069,7 +3831,7 @@ function describe_opened(lane) {
5069
3831
  }
5070
3832
  var lane_report = {
5071
3833
  NO_LANES: NO_LANES2,
5072
- OPEN_STATE: OPEN_STATE4,
3834
+ OPEN_STATE: OPEN_STATE3,
5073
3835
  STRANDED_STATE,
5074
3836
  UNREADABLE_STATE,
5075
3837
  describe_lane,
@@ -5079,7 +3841,7 @@ var lane_report = {
5079
3841
  };
5080
3842
 
5081
3843
  // scripts/run/run-preflight.ts
5082
- import { z as z16 } from "zod";
3844
+ import { z as z11 } from "zod";
5083
3845
  var CLEAN_VERDICT = "clean";
5084
3846
  var RECLAIM_VERDICT = "reclaim";
5085
3847
  var RESUME_VERDICT = "resume";
@@ -5089,7 +3851,7 @@ var OPEN_PR = "open";
5089
3851
  var MERGED_PR = "merged";
5090
3852
  var CLOSED_PR = "closed";
5091
3853
  var NO_CHILD_WORK = { branch_name: void 0, pr_state: NO_PR };
5092
- var pr_info_schema2 = z16.object({ state: z16.string().optional() });
3854
+ var pr_info_schema = z11.object({ state: z11.string().optional() });
5093
3855
  var GH_PR_STATES = /* @__PURE__ */ new Map([
5094
3856
  ["CLOSED", CLOSED_PR],
5095
3857
  ["MERGED", MERGED_PR],
@@ -5100,7 +3862,7 @@ var CLEAN_ADVICE = "Start the child.";
5100
3862
  var RESUME_ADVICE = "Reuse the branch and run the whole verification gate from the start \u2014 refactor, `pnpm josh gate`, `/code-review` \u2014 never only the part the interrupted run had not reached. Nobody verified what it already committed.";
5101
3863
  var PARK_ADVICE = "Park the child: add `needs-decision` and comment what was found. Do not delete the branch, do not reopen the pull request, and do not commit on top of it.";
5102
3864
  var STASH_LABEL_PREFIX = "run:hold reclaimed before #";
5103
- var { ISSUE_NUMBER_PATTERN, require_issue_number } = run_issue_number;
3865
+ var { require_issue_number } = run_issue_number;
5104
3866
  var UNREADABLE_PR_MESSAGE = "The pull request could not be read for branch ";
5105
3867
  function needs_reclaim(tree) {
5106
3868
  return tree.is_dirty || tree.branch !== tree.default_branch;
@@ -5158,7 +3920,7 @@ function to_advice2(verdict, issue, tree) {
5158
3920
  if (verdict === PARK_VERDICT) return PARK_ADVICE;
5159
3921
  return CLEAN_ADVICE;
5160
3922
  }
5161
- function decide4(tree, child, issue) {
3923
+ function decide5(tree, child, issue) {
5162
3924
  const verdict = to_verdict2(tree, child);
5163
3925
  return {
5164
3926
  advice: to_advice2(verdict, issue, tree),
@@ -5168,12 +3930,12 @@ function decide4(tree, child, issue) {
5168
3930
  }
5169
3931
  function read_pr_field(raw) {
5170
3932
  try {
5171
- return pr_info_schema2.parse(JSON.parse(raw)).state;
3933
+ return pr_info_schema.parse(JSON.parse(raw)).state;
5172
3934
  } catch {
5173
3935
  return void 0;
5174
3936
  }
5175
3937
  }
5176
- function to_pr_state2(raw) {
3938
+ function to_pr_state(raw) {
5177
3939
  const state = read_pr_field(raw);
5178
3940
  return (state === void 0 ? void 0 : GH_PR_STATES.get(state)) ?? NO_PR;
5179
3941
  }
@@ -5185,17 +3947,17 @@ async function read_tree_state() {
5185
3947
  ]);
5186
3948
  return { branch: current, default_branch, is_dirty };
5187
3949
  }
5188
- async function read_pr_state2(branch_name) {
3950
+ async function read_pr_state(branch_name) {
5189
3951
  if (!await git_gh_pr_read.pr_exists(branch_name)) return NO_PR;
5190
3952
  const raw = await git_gh_pr_read.pr_view(branch_name);
5191
3953
  if (raw === "") throw new Error(`${UNREADABLE_PR_MESSAGE}${branch_name}`);
5192
- return to_pr_state2(raw);
3954
+ return to_pr_state(raw);
5193
3955
  }
5194
3956
  function is_pr_open(state) {
5195
3957
  return state === OPEN_PR;
5196
3958
  }
5197
3959
  async function read_pr_of_branch(branch_name) {
5198
- return { branch_name, pr_state: await read_pr_state2(branch_name) };
3960
+ return { branch_name, pr_state: await read_pr_state(branch_name) };
5199
3961
  }
5200
3962
  function pick_child_state(reads) {
5201
3963
  const decided = reads.find((read2) => is_pr_decided(read2.pr_state));
@@ -5259,30 +4021,23 @@ async function read_child_state(issue) {
5259
4021
  async function check2(issue) {
5260
4022
  require_issue_number(issue);
5261
4023
  const tree = await read_tree_state();
5262
- if (needs_reclaim(tree)) return decide4(tree, NO_CHILD_WORK, issue);
5263
- return decide4(tree, await read_child_state(issue), issue);
4024
+ if (needs_reclaim(tree)) return decide5(tree, NO_CHILD_WORK, issue);
4025
+ return decide5(tree, await read_child_state(issue), issue);
5264
4026
  }
5265
4027
  var run_preflight = {
5266
- CLEAN_ADVICE,
5267
- CLEAN_REASON,
5268
4028
  CLEAN_VERDICT,
5269
- ISSUE_NUMBER_PATTERN,
5270
- PARK_ADVICE,
5271
- PARK_VERDICT,
5272
- RECLAIM_VERDICT,
5273
4029
  RESUME_ADVICE,
5274
- RESUME_VERDICT,
5275
4030
  MERGED_PR,
5276
4031
  STASH_LABEL_PREFIX,
5277
4032
  check: check2,
5278
- decide: decide4,
4033
+ decide: decide5,
5279
4034
  read_child_state,
5280
- read_pr_state: read_pr_state2,
5281
- to_pr_state: to_pr_state2
4035
+ read_pr_state,
4036
+ to_pr_state
5282
4037
  };
5283
4038
 
5284
4039
  // scripts/run/run-progress-read.ts
5285
- var { PROGRESS_PREFIX, mark, parse_stamp, read_last_report } = run_progress_clock;
4040
+ var { mark: mark2, parse_stamp: parse_stamp2, read_last_report: read_last_report2 } = run_progress_clock;
5286
4041
  var FIRST_LOAD_AVERAGE = 0;
5287
4042
  var IDLE_READ = { kind: "idle" };
5288
4043
  var UNREADABLE_READ = { kind: "unreadable" };
@@ -5359,24 +4114,46 @@ async function read_observations(request) {
5359
4114
  return { kind: "observed", observations };
5360
4115
  }
5361
4116
  var run_progress_read = {
5362
- PROGRESS_PREFIX,
5363
4117
  has_run_started,
5364
4118
  live_target,
5365
4119
  log_target,
5366
- mark,
5367
- parse_stamp,
4120
+ mark: mark2,
4121
+ parse_stamp: parse_stamp2,
5368
4122
  read_children,
5369
- read_last_report,
4123
+ read_last_report: read_last_report2,
5370
4124
  read_lanes,
5371
4125
  read_observations,
5372
4126
  read_record_age,
5373
4127
  stamp_target
5374
4128
  };
5375
4129
 
4130
+ // scripts/run/run-watcher-guard.ts
4131
+ var WATCHER_TICK_MS = 3e4;
4132
+ var TICK_MULTIPLIER = 3;
4133
+ var MS_PER_SECOND3 = 1e3;
4134
+ var STALE_THRESHOLD_MS = WATCHER_TICK_MS * TICK_MULTIPLIER;
4135
+ var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND3;
4136
+ var STALE_NOTE = `Lane children are in-flight but the progress watcher has not pinged in the last ${String(STALE_THRESHOLD_S)}s \u2014 run \`pnpm josh run:progress --wait\` in the background before proceeding. It reports to the run event stream on its own and exits only when newly runnable work arrives, so start it once and relay nothing`;
4137
+ var OK_RESULT = { kind: "ok" };
4138
+ async function owes_watcher() {
4139
+ if (!await run_headless.is_backlog_parent()) return false;
4140
+ return await lane_registry.has_lanes_in_flight();
4141
+ }
4142
+ async function check3(life_target) {
4143
+ if (!await owes_watcher()) return OK_RESULT;
4144
+ if (run_progress_clock.is_life_fresh(life_target, STALE_THRESHOLD_MS)) return OK_RESULT;
4145
+ return { kind: "stale", note: STALE_NOTE };
4146
+ }
4147
+ var run_watcher_guard = {
4148
+ STALE_NOTE,
4149
+ STALE_THRESHOLD_MS,
4150
+ check: check3
4151
+ };
4152
+
5376
4153
  // scripts/run/run-watcher-hook.ts
5377
4154
  var SWITCH_ENV_KEY2 = "JOSH_WATCHER_GUARD";
5378
4155
  var STAMP_PREFIX2 = "josh-watcher-guard-";
5379
- var payload_schema = z17.object({ transcript_path: z17.string().min(1) });
4156
+ var payload_schema = z12.object({ transcript_path: z12.string().min(1) });
5380
4157
  function transcript_of(raw_payload) {
5381
4158
  try {
5382
4159
  const parsed = payload_schema.safeParse(JSON.parse(raw_payload));
@@ -5405,7 +4182,7 @@ async function watcher_hook_reason(raw_payload, now_ms = Date.now()) {
5405
4182
  if (transcript === void 0) return void 0;
5406
4183
  return await stale_reason(transcript, now_ms);
5407
4184
  }
5408
- var run_watcher_hook = { SWITCH_ENV_KEY: SWITCH_ENV_KEY2, STAMP_PREFIX: STAMP_PREFIX2, transcript_of, watcher_hook_reason };
4185
+ var run_watcher_hook = { transcript_of, watcher_hook_reason };
5409
4186
 
5410
4187
  // scripts/run/run-parent-cut-hook.ts
5411
4188
  var SWITCH_ENV_KEY3 = "JOSH_PARENT_CUT_GUARD";
@@ -5437,7 +4214,8 @@ async function refusal_for(transcript, now_ms, state) {
5437
4214
  function is_unit_call(raw_payload) {
5438
4215
  try {
5439
4216
  return (hook_decision.parse_hook_payload(raw_payload)?.agent_id ?? "") !== "";
5440
- } catch {
4217
+ } catch (error) {
4218
+ error_text.trace_swallowed("run_parent_cut_hook.is_unit_call", error);
5441
4219
  return false;
5442
4220
  }
5443
4221
  }
@@ -5453,11 +4231,48 @@ async function parent_cut_reason(raw_payload, now_ms = Date.now(), state = curre
5453
4231
  var run_parent_cut_hook = {
5454
4232
  PARENT_CUT_REASON,
5455
4233
  QUIET_WINDOW_MS,
5456
- STAMP_PREFIX: STAMP_PREFIX3,
5457
4234
  SWITCH_ENV_KEY: SWITCH_ENV_KEY3,
5458
4235
  parent_cut_reason
5459
4236
  };
5460
4237
 
4238
+ // scripts/hooks/step-zero-notice.ts
4239
+ import path9 from "node:path";
4240
+ var STAMP_PREFIX4 = "josh-step-zero-notice-";
4241
+ var EDIT_TOOLS3 = /* @__PURE__ */ new Set(["Edit", "Write"]);
4242
+ var PARENT_PREFIX = "..";
4243
+ var NOTICE = "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.";
4244
+ var STAMP = hook_decision.create_refusal_stamp(STAMP_PREFIX4);
4245
+ function edited_paths(tool_input) {
4246
+ if (!json_value.is_record(tool_input)) return [];
4247
+ const { file_path, file_paths } = tool_input;
4248
+ const listed = Array.isArray(file_paths) ? file_paths : [];
4249
+ return [file_path, ...listed].filter(
4250
+ (entry) => typeof entry === "string" && entry.length > 0
4251
+ );
4252
+ }
4253
+ function is_runtime_file(file_path, root) {
4254
+ const relative = path9.relative(root, path9.resolve(root, file_path));
4255
+ if (relative.startsWith(PARENT_PREFIX) || path9.isAbsolute(relative)) return false;
4256
+ return !test_declared_logic.is_exempt(relative.split(path9.sep).join("/"));
4257
+ }
4258
+ function is_candidate(raw_payload, root) {
4259
+ const payload = hook_decision.parse_hook_payload(raw_payload);
4260
+ if (payload === void 0 || !EDIT_TOOLS3.has(payload.tool_name)) return void 0;
4261
+ const has_runtime_file = edited_paths(payload.tool_input).some(
4262
+ (file_path) => is_runtime_file(file_path, root)
4263
+ );
4264
+ return has_runtime_file ? payload.transcript_path : void 0;
4265
+ }
4266
+ function notice(raw_payload, root = process.cwd()) {
4267
+ const transcript = is_candidate(raw_payload, root);
4268
+ if (transcript === void 0) return void 0;
4269
+ const target = STAMP.path(transcript);
4270
+ if (STAMP.last_ms(target) !== hook_decision.NEVER_MS) return void 0;
4271
+ if (!STAMP.record(target, Date.now())) return void 0;
4272
+ return NOTICE;
4273
+ }
4274
+ var step_zero_notice = { NOTICE, notice, stamp_path: STAMP.path };
4275
+
5461
4276
  // scripts/hooks/pretool-guard.ts
5462
4277
  function combine_outcomes(batch, extra_reason) {
5463
4278
  if (batch.reason !== void 0) return batch;
@@ -5469,11 +4284,16 @@ function combine_outcomes(batch, extra_reason) {
5469
4284
  function is_clear(outcome) {
5470
4285
  return outcome.reason === void 0 && outcome.notice === void 0 && outcome.fault === void 0;
5471
4286
  }
5472
- function with_duplicate_notice(combined, notice) {
5473
- if (notice === void 0 || !is_clear(combined)) return combined;
5474
- return { reason: void 0, notice, fault: void 0 };
4287
+ function with_duplicate_notice(combined, notice2) {
4288
+ if (notice2 === void 0 || !is_clear(combined)) return combined;
4289
+ return { reason: void 0, notice: notice2, fault: void 0 };
5475
4290
  }
5476
- function pretool_outcome(raw_payload) {
4291
+ function with_step_zero_notice(outcome, raw_payload) {
4292
+ if (!is_clear(outcome)) return outcome;
4293
+ const notice2 = step_zero_notice.notice(raw_payload);
4294
+ return notice2 === void 0 ? outcome : { reason: void 0, notice: notice2, fault: void 0 };
4295
+ }
4296
+ function guard_outcome(raw_payload) {
5477
4297
  const batch = batch_outcome(raw_payload);
5478
4298
  const investigation = investigation_refusal(raw_payload);
5479
4299
  const duplicate = duplicate_read_outcome(raw_payload);
@@ -5482,17 +4302,21 @@ function pretool_outcome(raw_payload) {
5482
4302
  const combined = combine_outcomes(batch, investigation ?? duplicate_refusal ?? rule);
5483
4303
  return with_duplicate_notice(combined, duplicate.notice);
5484
4304
  }
4305
+ function pretool_outcome(raw_payload) {
4306
+ return with_step_zero_notice(guard_outcome(raw_payload), raw_payload);
4307
+ }
5485
4308
  async function async_reason(raw_payload) {
5486
4309
  const watcher = await run_watcher_hook.watcher_hook_reason(raw_payload);
5487
4310
  return watcher ?? await run_parent_cut_hook.parent_cut_reason(raw_payload);
5488
4311
  }
5489
4312
  async function pretool_outcome_async(raw_payload) {
5490
- const base = pretool_outcome(raw_payload);
4313
+ const base = guard_outcome(raw_payload);
5491
4314
  if (!is_clear(base)) return base;
5492
4315
  const reason = await async_reason(raw_payload);
5493
- return reason === void 0 ? base : { reason, notice: void 0, fault: void 0 };
4316
+ if (reason !== void 0) return { reason, notice: void 0, fault: void 0 };
4317
+ return with_step_zero_notice(base, raw_payload);
5494
4318
  }
5495
- var pretool_guard = { combine_outcomes, pretool_outcome, pretool_outcome_async };
4319
+ var pretool_guard = { combine_outcomes, pretool_outcome };
5496
4320
 
5497
4321
  export {
5498
4322
  pretool_outcome_async,