@joshuafolkken/kit 1.1010.0 → 1.1050.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 (350) hide show
  1. package/.claude/settings.json +1 -5
  2. package/.claude/skills/dependency-update/SKILL.md +6 -3
  3. package/.claude/skills/epic-commands/SKILL.md +46 -17
  4. package/.claude/skills/verify-ui/SKILL.md +1 -1
  5. package/.claude/skills/workflow-commands/SKILL.md +10 -25
  6. package/.claude/skills/workflow-commands/background-commands.md +1 -1
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -36
  11. package/.claude/skills/workflow-commands/backlogrun-steps.md +16 -16
  12. package/.claude/skills/workflow-commands/backlogrun.md +3 -12
  13. package/.claude/skills/workflow-commands/chain-rule.md +2 -2
  14. package/.claude/skills/workflow-commands/followup-reference.md +2 -2
  15. package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
  16. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +2 -1
  18. package/.claude/skills/workflow-commands/into-target.md +1 -1
  19. package/.claude/skills/workflow-commands/issue-comments.md +1 -1
  20. package/.claude/skills/workflow-commands/observation-filing.md +80 -126
  21. package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
  22. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  23. package/.claude/skills/workflow-commands/working-tree-hold.md +2 -2
  24. package/.codex/hooks.json +1 -5
  25. package/.github/workflows/auto-tag.yml +21 -0
  26. package/.github/workflows/publish.yml +9 -10
  27. package/.github/workflows/sonar-qube.yml +1 -1
  28. package/AGENTS.md +4 -25
  29. package/CLAUDE.md +24 -38
  30. package/GEMINI.md +4 -24
  31. package/README.md +22 -56
  32. package/dist/CLAUDE.md +24 -38
  33. package/dist/hooks/batch-guard.js +4 -4
  34. package/dist/hooks/{chunk-6PSTVOLH.js → chunk-5BK3K7V4.js} +114 -90
  35. package/dist/hooks/{chunk-PAXMLGSS.js → chunk-7VPDUBRN.js} +93 -175
  36. package/dist/hooks/{chunk-KPZN42RT.js → chunk-DMQN45FN.js} +3 -3
  37. package/dist/hooks/{chunk-WMIR5SNX.js → chunk-EJDX2TOB.js} +13 -3
  38. package/dist/hooks/{chunk-A2H5I2VD.js → chunk-GQ7KP45M.js} +2 -2
  39. package/dist/hooks/{chunk-4GDR2YTV.js → chunk-HJIISEAC.js} +4 -4
  40. package/dist/hooks/{chunk-AWTFKKFW.js → chunk-NAS4C7GP.js} +6 -33
  41. package/dist/hooks/{chunk-56LGMJJV.js → chunk-NJQOBE6L.js} +2 -2
  42. package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-RLDX5SOC.js} +1222 -941
  43. package/dist/hooks/{chunk-GBJUERYK.js → chunk-WQ4OSBSS.js} +10 -3
  44. package/dist/hooks/codex-hook-adapter.js +10 -10
  45. package/dist/hooks/duplicate-read-guard.js +5 -5
  46. package/dist/hooks/format-edited.js +15 -34
  47. package/dist/hooks/investigation-guard.js +5 -5
  48. package/dist/hooks/pretool-guard.js +23 -16
  49. package/dist/hooks/stop-guard.js +21 -200
  50. package/dist/josh.js +42 -125
  51. package/dist/version/index.js +466 -42
  52. package/eslint/base.js +2 -11
  53. package/package.json +36 -10
  54. package/prompts/coding-standards.md +15 -290
  55. package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
  56. package/prompts/collaboration-workflow/epic-audit.md +2 -53
  57. package/prompts/collaboration-workflow/epic-bundle.md +2 -106
  58. package/prompts/collaboration-workflow/file-edits.md +6 -53
  59. package/prompts/collaboration-workflow/issue-template.md +4 -4
  60. package/prompts/collaboration-workflow/operating-rules.md +18 -75
  61. package/prompts/collaboration-workflow/principles.md +74 -0
  62. package/prompts/collaboration-workflow/residency.md +55 -15
  63. package/prompts/collaboration-workflow/rule-delivery.md +5 -46
  64. package/prompts/collaboration-workflow/shell-body.md +7 -9
  65. package/prompts/collaboration-workflow/turn-batching.md +21 -37
  66. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  67. package/prompts/collaboration-workflow/wip-cap.md +15 -18
  68. package/prompts/collaboration-workflow.md +22 -28
  69. package/prompts/sonar-hotspot-handling.md +1 -1
  70. package/scripts/adopt/adopt-logic.ts +2 -2
  71. package/scripts/adopt/adopt-toolkits.ts +3 -1
  72. package/scripts/adopt/adopt.ts +2 -2
  73. package/scripts/agent/agent-event.ts +2 -9
  74. package/scripts/agent/agent-exit-record.ts +2 -9
  75. package/scripts/auto-ok/auto-ok-cli.ts +9 -9
  76. package/scripts/backlog/backlog-budget-cli.ts +2 -6
  77. package/scripts/backlog/backlog-drive-cli.ts +20 -10
  78. package/scripts/backlog/backlog-drive.ts +50 -16
  79. package/scripts/backlog/backlog-next.ts +24 -8
  80. package/scripts/backlog/backlog-offer-cli.ts +6 -5
  81. package/scripts/backlog/backlog-plan-cli.ts +5 -1
  82. package/scripts/backlog/backlog-plan.ts +1 -1
  83. package/scripts/backlog/backlog-pool.ts +31 -11
  84. package/scripts/backlog/backlog-rank.ts +106 -0
  85. package/scripts/backlog/backlog-waves.ts +42 -29
  86. package/scripts/build/build-bin.ts +2 -1
  87. package/scripts/build/build-config-merge.ts +2 -1
  88. package/scripts/build/build-hooks.ts +31 -6
  89. package/scripts/build/build-managed-marker.ts +2 -1
  90. package/scripts/build/build-self-sync-guard.ts +2 -1
  91. package/scripts/build/build-version.ts +2 -1
  92. package/scripts/cases/cases-logic.ts +2 -6
  93. package/scripts/ci/pr-classification.ts +13 -11
  94. package/scripts/cost-runtime/cost-transcript.ts +4 -11
  95. package/scripts/delegation/delegation-policy.ts +11 -0
  96. package/scripts/doctor/doctor-consumer.ts +5 -12
  97. package/scripts/doctor/doctor-ports.ts +2 -2
  98. package/scripts/document/document-byte-budget.ts +13 -21
  99. package/scripts/document/document-section.ts +2 -6
  100. package/scripts/document/entry-read-budget.ts +6 -5
  101. package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
  102. package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
  103. package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
  104. package/scripts/epic/epic-audit-cli.ts +4 -4
  105. package/scripts/epic/epic-audit-orphans.ts +2 -2
  106. package/scripts/epic/epic-audit-rationale.ts +1 -1
  107. package/scripts/epic/epic-audit.ts +1 -1
  108. package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
  109. package/scripts/epic/epic-bundle-cli.ts +1 -1
  110. package/scripts/epic/epic-bundle.ts +2 -2
  111. package/scripts/epic/epic-candidate-confirm.ts +2 -1
  112. package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
  113. package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
  114. package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
  115. package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
  116. package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
  117. package/scripts/epic/epic-cross-repo.ts +21 -38
  118. package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
  119. package/scripts/epic/epic-fetch.ts +3 -3
  120. package/scripts/epic/epic-graph.ts +3 -3
  121. package/scripts/epic/epic-index.ts +2 -2
  122. package/scripts/epic/epic-issue.ts +1 -1
  123. package/scripts/epic/epic-lane-offer.ts +11 -22
  124. package/scripts/epic/epic-next.ts +3 -3
  125. package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
  126. package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
  127. package/scripts/epic/epic-rank.ts +37 -0
  128. package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
  129. package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
  130. package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
  131. package/scripts/epic/epic-relation-recheck.ts +4 -3
  132. package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
  133. package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
  134. package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
  135. package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
  136. package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
  137. package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
  138. package/scripts/epic/epic-solo.ts +29 -28
  139. package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
  140. package/{scripts-ai → scripts/epic}/epic.ts +17 -16
  141. package/scripts/gh/fix-gh-packages.ts +2 -1
  142. package/scripts/gh/gh-spawn.ts +12 -10
  143. package/scripts/git/constants.ts +1 -1
  144. package/scripts/git/git-branch.ts +10 -1
  145. package/scripts/git/git-command.ts +2 -2
  146. package/scripts/git/git-error.ts +2 -5
  147. package/scripts/git/git-execa-error.ts +6 -1
  148. package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
  149. package/scripts/git/git-followup-flush.ts +74 -48
  150. package/scripts/git/git-followup-issue-close.ts +2 -2
  151. package/scripts/git/git-followup-label.ts +1 -1
  152. package/scripts/git/git-followup-pending.ts +1 -1
  153. package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
  154. package/scripts/git/git-gh-exec.ts +85 -26
  155. package/scripts/git/git-gh-issue-read.ts +2 -2
  156. package/scripts/git/git-gh-issue-rest.ts +2 -2
  157. package/scripts/git/git-gh-issue-write.ts +6 -6
  158. package/scripts/git/git-gh-pr-read.ts +0 -21
  159. package/scripts/git/git-gh-pr.ts +2 -23
  160. package/scripts/git/git-next-issues.ts +34 -5
  161. package/scripts/git/git-pr-followup-wrapup.ts +2 -6
  162. package/scripts/git/git-preflight.ts +25 -10
  163. package/scripts/git/git-push-transport.ts +12 -69
  164. package/scripts/git/git-spawn.ts +79 -3
  165. package/scripts/git/git-ssh-keepalive.ts +74 -0
  166. package/scripts/git/git-staging.ts +21 -9
  167. package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
  168. package/scripts/git/github-issue-url.ts +1 -1
  169. package/scripts/git/gone-branch.ts +3 -2
  170. package/scripts/git/issue-labels.ts +13 -1
  171. package/scripts/git/issue-rank.ts +89 -0
  172. package/scripts/git/issue-reference.ts +19 -0
  173. package/scripts/git/main-merge.ts +2 -1
  174. package/scripts/git/main-sync.ts +3 -2
  175. package/scripts/git/schemas.ts +1 -1
  176. package/scripts/git/stash-pop-args.ts +7 -13
  177. package/scripts/git/stash-pop-cli.ts +2 -1
  178. package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
  179. package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
  180. package/scripts/hooks/format-edited-cli.ts +15 -0
  181. package/scripts/hooks/format-edited-file.ts +2 -9
  182. package/scripts/hooks/format-rewrite-notice.ts +2 -8
  183. package/scripts/hooks/pretool-guard-cli.ts +21 -0
  184. package/scripts/hooks/pretool-guard.ts +14 -15
  185. package/scripts/init/init-ai-copy.ts +2 -5
  186. package/scripts/init/init.ts +8 -4
  187. package/scripts/init/start-exec.ts +7 -1
  188. package/scripts/init/start-plan.ts +18 -5
  189. package/scripts/init/start-setup-pr.ts +46 -19
  190. package/scripts/init/start-steps.ts +43 -10
  191. package/scripts/init/start.ts +42 -11
  192. package/scripts/issue/issue-cite.ts +19 -1
  193. package/scripts/issue/issue-comment-cli.ts +2 -1
  194. package/scripts/issue/issue-file-cli.ts +2 -1
  195. package/scripts/issue/issue-fold-cli.ts +9 -16
  196. package/scripts/issue/issue-scout-cli.ts +8 -13
  197. package/scripts/issue/measure-rerun-cli.ts +7 -6
  198. package/scripts/josh/josh-command-map.ts +0 -102
  199. package/scripts/josh/josh-commands-ai.ts +2 -2
  200. package/scripts/josh/josh-commands-backlog.ts +1 -1
  201. package/scripts/josh/josh-commands-development.ts +3 -3
  202. package/scripts/josh/josh-commands-project.ts +1 -1
  203. package/scripts/josh/josh-commands-workflow.ts +4 -4
  204. package/scripts/lane/lane-child-marker.ts +16 -2
  205. package/scripts/lane/lane-cli.ts +34 -15
  206. package/scripts/lane/lane-open.ts +3 -2
  207. package/scripts/lane/lane-registry.ts +3 -6
  208. package/scripts/lane/lane-report.ts +10 -4
  209. package/scripts/lib/cli-flags.ts +20 -2
  210. package/scripts/lib/error-message.ts +9 -0
  211. package/scripts/lib/read-file.ts +26 -2
  212. package/scripts/observations/observation-ledger-home.ts +98 -26
  213. package/scripts/observations/observation-ledger-migrate.ts +26 -21
  214. package/scripts/observations/observation-ledger-prepare.ts +10 -20
  215. package/scripts/observations/observation-ledger.ts +58 -25
  216. package/scripts/observations/observations-flush-cli.ts +6 -23
  217. package/scripts/observations/observations-flush-landing.ts +2 -1
  218. package/scripts/observations/observations-flush.ts +29 -20
  219. package/scripts/package/package-scout-cli.ts +8 -16
  220. package/scripts/ports/port-command.ts +2 -1
  221. package/scripts/propagate/propagate-plan.ts +185 -0
  222. package/scripts/propagate/propagate-publish.ts +6 -7
  223. package/scripts/propagate/propagate-run.ts +5 -1
  224. package/scripts/propagate/propagate-steps.ts +18 -2
  225. package/scripts/propagate/propagate-targets.ts +13 -2
  226. package/scripts/propagate/propagate.ts +63 -44
  227. package/scripts/refactor/import-graph.ts +2 -6
  228. package/scripts/registry-migration/migrate-logic.ts +1 -0
  229. package/scripts/registry-migration/migrate.ts +3 -2
  230. package/scripts/registry-migration/user-npmrc.ts +2 -2
  231. package/scripts/release/github-release.ts +1 -1
  232. package/scripts/release/release-cli.ts +2 -1
  233. package/scripts/repo/repo-setting.ts +10 -8
  234. package/scripts/review/review-attest.ts +44 -0
  235. package/scripts/review/review-brief-cli.ts +49 -2
  236. package/scripts/review/review-brief.ts +46 -1
  237. package/scripts/review/review-finding-ledger.ts +3 -3
  238. package/scripts/review/review-findings-cli.ts +4 -11
  239. package/scripts/review/review-record-cli.ts +14 -11
  240. package/scripts/review/review-record.ts +6 -16
  241. package/scripts/review/review-stamps.ts +7 -0
  242. package/scripts/rules/decision-oracle.ts +2 -2
  243. package/scripts/rules/implementation-cut.ts +1 -1
  244. package/scripts/rules/issue-fold.ts +1 -2
  245. package/scripts/rules/lane-carry-conflict.ts +1 -2
  246. package/scripts/rules/lane-park.ts +1 -2
  247. package/scripts/rules/pre-gate-cut.ts +2 -2
  248. package/scripts/rules/rule-body-guard.ts +2 -2
  249. package/scripts/rules/rule-value-cache.ts +99 -0
  250. package/scripts/rules/rule-value-cli.ts +10 -9
  251. package/scripts/rules/rule-value.ts +1 -1
  252. package/scripts/run/run-carry-args.ts +2 -6
  253. package/scripts/run/run-carry-cli.ts +5 -7
  254. package/scripts/run/run-cut-args.ts +7 -13
  255. package/scripts/run/run-ending-cli.ts +4 -7
  256. package/scripts/run/run-event-stream.ts +2 -5
  257. package/scripts/run/run-headless.ts +22 -6
  258. package/scripts/run/run-liveness-cli.ts +4 -7
  259. package/scripts/run/run-parent-cut-hook.ts +144 -0
  260. package/scripts/run/run-progress-args.ts +2 -6
  261. package/scripts/run/run-progress-cli.ts +2 -1
  262. package/scripts/run/run-progress-read.ts +2 -1
  263. package/scripts/run/run-progress.ts +21 -8
  264. package/scripts/run/run-ship-cli.ts +6 -74
  265. package/scripts/run/run-ship-preflight.ts +87 -0
  266. package/scripts/run/run-ship-review-steps.ts +5 -26
  267. package/scripts/run/run-ship-review.ts +9 -0
  268. package/scripts/run/run-ship-scoped.ts +44 -0
  269. package/scripts/run/run-ship-stage.ts +5 -1
  270. package/scripts/run/run-ship-steps.ts +105 -0
  271. package/scripts/run/run-ship.ts +2 -0
  272. package/scripts/run/run-tail-cli.ts +3 -3
  273. package/scripts/run/run-tidy-stashes.ts +7 -14
  274. package/scripts/run/run-wake-cli.ts +4 -8
  275. package/scripts/run/run-wake-describe.ts +19 -1
  276. package/scripts/run/run-wake-driver.ts +15 -2
  277. package/scripts/run/run-wake-session.ts +14 -4
  278. package/scripts/run/run-watcher-hook.ts +1 -1
  279. package/scripts/safe-chain/project-config.ts +3 -6
  280. package/scripts/security/security-audit-provision.ts +6 -7
  281. package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
  282. package/scripts/sync/directory-copy-guard.ts +2 -1
  283. package/scripts/sync/sync-ai-files.ts +3 -3
  284. package/scripts/sync/template-source-logic.ts +3 -4
  285. package/scripts/test/e2e-retry-check.ts +3 -6
  286. package/scripts/time-runtime/time-bundle-call.ts +2 -2
  287. package/scripts/ui/ui-routes-cli.ts +3 -6
  288. package/scripts/version/bump-version.ts +2 -1
  289. package/scripts/version/npm-registry.ts +97 -0
  290. package/scripts/version/version-commands.ts +5 -4
  291. package/scripts/version/version-remote.ts +38 -33
  292. package/scripts/version/version-targets.ts +3 -3
  293. package/.claude/skills/workflow-commands/rule-residency.md +0 -267
  294. package/evals/scenarios/consult-not-execute.json +0 -20
  295. package/evals/scenarios/no-implicit-workflow.json +0 -26
  296. package/evals/scenarios/overrides-both-locations.json +0 -29
  297. package/evals/scenarios/upstream-interrupt.json +0 -23
  298. package/evals/scenarios/workflow-skill-loaded.json +0 -16
  299. package/prompts/agent-lint-protocol.json +0 -79
  300. package/prompts/agent-rules.md +0 -63
  301. package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
  302. package/prompts/collaboration-workflow/distributed-docs.md +0 -7
  303. package/prompts/collaboration-workflow/durable-rules.md +0 -24
  304. package/prompts/collaboration-workflow/latest-first.md +0 -14
  305. package/prompts/collaboration-workflow/no-clones.md +0 -11
  306. package/prompts/collaboration-workflow/simplicity-first.md +0 -9
  307. package/prompts/collaboration-workflow/single-source-rules.md +0 -13
  308. package/scripts/build/shim-shell.ts +0 -25
  309. package/scripts/claude/skill-meta.ts +0 -37
  310. package/scripts/document/document-scan.ts +0 -108
  311. package/scripts/document/generate-catalog.ts +0 -69
  312. package/scripts/document/retired-phrases.ts +0 -143
  313. package/scripts/document/review-agent-type.ts +0 -46
  314. package/scripts/document/single-source.ts +0 -73
  315. package/scripts/git/git-fixture-workspace.ts +0 -88
  316. package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
  317. package/scripts/hooks/prompt-hooks.ts +0 -38
  318. package/scripts/init/hook-command-bootstrap.ts +0 -83
  319. package/scripts/issue/issue-logic.ts +0 -50
  320. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
  321. package/scripts/josh/josh-script-reader.ts +0 -11
  322. package/scripts/package/production-dependency-graph.ts +0 -68
  323. package/scripts/retrospective/retrospective-cli.ts +0 -97
  324. package/scripts/retrospective/retrospective.ts +0 -127
  325. package/scripts/rules/delivered-rules-harness.ts +0 -72
  326. package/scripts/rules/top-level-scripts-guard.ts +0 -47
  327. package/scripts/run/run-carry-flush.ts +0 -58
  328. package/scripts/run/run-transcript.golden.txt +0 -57
  329. package/scripts/run/run-wake-work.ts +0 -108
  330. package/scripts/sync/dependabot-pin-logic.ts +0 -135
  331. package/scripts/test/classify-isolation.ts +0 -149
  332. package/scripts/test/http-fault-injection.ts +0 -79
  333. package/scripts/test/josh-harness-environment.ts +0 -260
  334. package/scripts/test/josh-harness.ts +0 -96
  335. package/scripts/test/pilot-files.ts +0 -452
  336. package/scripts/test/pilot-measure.ts +0 -78
  337. package/scripts/test/test-network-guard.ts +0 -453
  338. package/scripts/test/test-repository-guard.ts +0 -317
  339. package/scripts/test/test-state-guard.ts +0 -85
  340. package/scripts/test/test-stdout-guard.ts +0 -54
  341. package/scripts/test/test-telegram-guard.ts +0 -90
  342. package/scripts/test/unit-guard-environment.ts +0 -12
  343. package/scripts/test/unit-projects.ts +0 -139
  344. package/scripts/test/vitest-include-globs.ts +0 -12
  345. package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
  346. package/scripts-ai/completion-gate.ts +0 -13
  347. package/scripts-ai/environment-loader.ts +0 -9
  348. /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
  349. /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
  350. /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { git_epic_decision } from '#scripts/git/git-epic-decision'
4
- import { git_epic_parse, type DependencyLink } from '#scripts/git/git-epic-parse'
5
3
  import { git_gh_command } from '#scripts/git/git-gh-command'
6
4
  import { issue_read } from '#scripts/issue/issue-read'
7
5
  import { epic_audit_logic, type AuditFinding, type ReferenceState } from './epic-audit'
@@ -10,10 +8,12 @@ import { epic_audit_orphans, type ClaimingSearch } from './epic-audit-orphans'
10
8
  import { epic_audit_rationale, type OrderPair } from './epic-audit-rationale'
11
9
  import { epic_audit_report, type AuditResult } from './epic-audit-report'
12
10
  import { epic_cross_repo } from './epic-cross-repo'
11
+ import { epic_decision } from './epic-decision'
13
12
  import { epic_fetch, type EpicSnapshot } from './epic-fetch'
14
13
  import { epic_graph, type IssueReference } from './epic-graph'
15
14
  import { epic_issue } from './epic-issue'
16
15
  import { epic_next } from './epic-next'
16
+ import { epic_parse, type DependencyLink } from './epic-parse'
17
17
 
18
18
  // `josh epic:audit <E>` — read an epic's children against each other and report what contradicts
19
19
  // what (joshuafolkken/kit#870).
@@ -278,7 +278,7 @@ async function to_audit_input(
278
278
  epic_number: number,
279
279
  repo: string,
280
280
  ): Promise<AuditInput> {
281
- const links = git_epic_parse.parse_dependency_links(snapshot.body)
281
+ const links = epic_parse.parse_dependency_links(snapshot.body)
282
282
  const order_pairs = epic_audit_rationale.order_pairs(links, children, snapshot.repo, repo)
283
283
 
284
284
  return {
@@ -291,7 +291,7 @@ async function to_audit_input(
291
291
  anomalies: fetch_anomalies(snapshot, children, links),
292
292
  contradictions: epic_audit_checks.find_order_contradictions(children, repo),
293
293
  order_pairs,
294
- decisions: git_epic_decision.read_recorded_reasons(snapshot.body),
294
+ decisions: epic_decision.read_recorded_reasons(snapshot.body),
295
295
  order_comments: await read_order_comments(order_pairs),
296
296
  is_epic_closed: await read_epic_closed(epic_number, epic_fetch.scope_for(snapshot.repo, repo)),
297
297
  }
@@ -1,10 +1,10 @@
1
- import { git_epic_parse } from '#scripts/git/git-epic-parse'
2
1
  import { git_gh_command } from '#scripts/git/git-gh-command'
3
2
  import { cutoff_of, PAGE_CEILING_CAUSE, type ScanCutoff } from '#scripts/git/listing-cutoff'
4
3
  import { parse_json_array_safe } from '#scripts/git/parse-json-array'
5
4
  import { z } from 'zod'
6
5
  import { epic_audit_logic, type AuditFinding, type FindingLevel } from './epic-audit'
7
6
  import type { EpicSnapshot } from './epic-fetch'
7
+ import { epic_parse } from './epic-parse'
8
8
 
9
9
  // Check 4's half of `epic:audit`: an issue that names this epic as its parent but that the epic's
10
10
  // task list does not track. It would never be run, and the epic would close without it.
@@ -125,7 +125,7 @@ function claimed_numbers(search: ClaimingSearch): Array<number> {
125
125
  // reported (joshuafolkken/kit#1014). Read with the same parser `epic_fetch` reads the local rows
126
126
  // with, which never matches a `- [ ] owner/repo#N` row.
127
127
  function locally_tracked(snapshot: EpicSnapshot): Array<number> {
128
- return git_epic_parse.parse_task_list_issue_numbers(snapshot.body)
128
+ return epic_parse.parse_task_list_issue_numbers(snapshot.body)
129
129
  }
130
130
 
131
131
  const epic_audit_orphans = {
@@ -1,7 +1,7 @@
1
- import type { DependencyLink } from '#scripts/git/git-epic-parse'
2
1
  import { epic_audit_logic, type AuditFinding } from './epic-audit'
3
2
  import type { AuditChild } from './epic-audit-checks'
4
3
  import { epic_graph, type IssueReference } from './epic-graph'
4
+ import type { DependencyLink } from './epic-parse'
5
5
 
6
6
  // Check 6 — a declared order between two open children that nobody wrote a reason for
7
7
  // (joshuafolkken/kit#1712).
@@ -20,7 +20,7 @@ const REFERENCE_PATTERN = /#(\d+)\b/gu
20
20
  // What may appear in the `owner/repo` written in front of a `#`: exactly the set the previous
21
21
  // lookbehind refused a bare reference after, plus the `.` a repository name may legitimately contain.
22
22
  //
23
- // The dot is where this parse and the task-list parse used to disagree. `git_epic_parse`'s
23
+ // The dot is where this parse and the task-list parse used to disagree. `epic_parse`'s
24
24
  // `EXTERNAL_REFERENCE_SOURCE` allows one, so `- [ ] owner/site.com#40` is tracked as a genuine
25
25
  // cross-repository child; this one excluded it, so a sibling quoting `owner/site.com#40` was read
26
26
  // back as `com` — no repository — and every check skipped it in silence. A child that can be tracked
@@ -1,7 +1,7 @@
1
- import { UNORDERED_DEPENDENCIES } from './git-epic-parse'
2
- import { DEPENDENCY_ARROW, to_issue_reference } from './git-epic-reference'
1
+ import { UNORDERED_DEPENDENCIES } from './epic-parse'
2
+ import { DEPENDENCY_ARROW, to_issue_reference } from './epic-reference'
3
3
 
4
- // The epic body is the machine-readable half of the epic contract: `scripts/git/git-epic-close.ts`
4
+ // The epic body is the machine-readable half of the epic contract: `scripts/epic/epic-close.ts`
5
5
  // reads the task list to decide when the batch is finished, and the order-unrecorded warning reads
6
6
  // the `Dependencies` section. Generating both from the same input is what keeps them from
7
7
  // disagreeing with each other, or with the run command printed next to them.
@@ -25,7 +25,7 @@ interface EpicBodyInput {
25
25
  epic_number?: number | undefined
26
26
  }
27
27
 
28
- // Task-list syntax, never a bare `#N` link: `git_epic_parse.parse_task_list_issue_numbers` matches
28
+ // Task-list syntax, never a bare `#N` link: `epic_parse.parse_task_list_issue_numbers` matches
29
29
  // nothing else, and GitHub only auto-checks a row written this way.
30
30
  function format_progress(children: ReadonlyArray<number>): string {
31
31
  return children.map((child) => `- [ ] ${to_issue_reference(child)}`).join('\n')
@@ -139,12 +139,12 @@ function build_dependency_pairs(
139
139
  .filter((pair) => pair.blocked !== pair.blocker)
140
140
  }
141
141
 
142
- const git_epic_body = {
142
+ const epic_body = {
143
143
  EPIC_PLACEHOLDER,
144
144
  build_epic_body,
145
145
  build_dependency_pairs,
146
146
  format_run_command,
147
147
  }
148
148
 
149
- export { git_epic_body, SPLIT_RATIONALE_HEADING }
149
+ export { epic_body, SPLIT_RATIONALE_HEADING }
150
150
  export type { EpicBodyInput }
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import type { IssueReference } from '#scripts/git/git-epic-reference'
4
3
  import { git_gh_command } from '#scripts/git/git-gh-command'
5
4
  import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
6
5
  import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
@@ -18,6 +17,7 @@ import { epic_bundle_gaps } from './epic-bundle-gaps'
18
17
  import { epic_bundle_referenced, type ReferencedContext } from './epic-bundle-referenced'
19
18
  import { epic_index, epic_schema, type FetchedEpics } from './epic-index'
20
19
  import { epic_issue } from './epic-issue'
20
+ import type { IssueReference } from './epic-reference'
21
21
 
22
22
  // `josh epic:bundle <N>` — after an issue is filed, look at the open backlog and say whether it
23
23
  // belongs with something already there (joshuafolkken/kit#873).
@@ -1,7 +1,7 @@
1
- import type { DependencyLink } from '#scripts/git/git-epic-parse'
2
- import type { IssueReference } from '#scripts/git/git-epic-reference'
3
1
  import { epic_audit_logic } from './epic-audit'
4
2
  import { epic_graph } from './epic-graph'
3
+ import type { DependencyLink } from './epic-parse'
4
+ import type { IssueReference } from './epic-reference'
5
5
 
6
6
  // Bundling a newly filed issue with the ones it turns out to be related to.
7
7
  //
@@ -1,3 +1,4 @@
1
+ import { error_text } from '#scripts/lib/error-message'
1
2
  import { epic_classify, type ResolveDependency } from './epic-classify'
2
3
  import { epic_graph, type EpicChild, type IssueReference } from './epic-graph'
3
4
  import type { BlockersReader } from './epic-relation-recheck'
@@ -118,7 +119,7 @@ async function read_or_withhold(
118
119
  try {
119
120
  return await read_blockers(candidate)
120
121
  } catch (error) {
121
- const reason = error instanceof Error ? error.message : String(error)
122
+ const reason = error_text.message_of(error)
122
123
 
123
124
  console.warn(
124
125
  `⚠ could not confirm the blockers of #${String(candidate.number)}: ${reason}\n` +
@@ -1,5 +1,5 @@
1
- import { chain_links, type DependencyLink } from './git-epic-parse'
2
- import { DEPENDENCY_ARROW, format_dependency_link, to_issue_reference } from './git-epic-reference'
1
+ import { chain_links, type DependencyLink } from './epic-parse'
2
+ import { DEPENDENCY_ARROW, format_dependency_link, to_issue_reference } from './epic-reference'
3
3
 
4
4
  // The declared dependency order of an epic, as the shape an insertion needs.
5
5
  //
@@ -392,7 +392,7 @@ function insert_children(
392
392
  return duplicated === undefined ? outcome : { error: already_declared_error(duplicated) }
393
393
  }
394
394
 
395
- const git_epic_chains = {
395
+ const epic_chains = {
396
396
  render_chains,
397
397
  links_of,
398
398
  diff_links,
@@ -407,5 +407,5 @@ const git_epic_chains = {
407
407
  insert_children,
408
408
  }
409
409
 
410
- export { git_epic_chains }
410
+ export { epic_chains }
411
411
  export type { Chain, Chains, InsertKind, InsertOutcome, InsertPosition, LinkDiff }
@@ -2,10 +2,11 @@
2
2
  /**
3
3
  * Check an existing epic Issue against the four requirements, reporting each as pass or fail.
4
4
  *
5
- * Usage: tsx scripts-ai/epic-check.ts <issue-number>
5
+ * Usage: tsx scripts/epic/epic-check.ts <issue-number>
6
6
  */
7
- import { git_epic_run } from '../scripts/git/git-epic-run'
7
+ import { error_text } from '#scripts/lib/error-message'
8
8
  import { epic_cli } from './epic-cli'
9
+ import { epic_run } from './epic-run'
9
10
 
10
11
  const ARGV_OFFSET = 2
11
12
  const USAGE = 'Usage: josh epic:check <issue-number>'
@@ -19,7 +20,7 @@ async function main(): Promise<void> {
19
20
  process.exit(FAILURE_EXIT_CODE)
20
21
  }
21
22
 
22
- const exit_code = await git_epic_run.check_epic(epic_number)
23
+ const exit_code = await epic_run.check_epic(epic_number)
23
24
 
24
25
  if (exit_code !== 0) process.exit(exit_code)
25
26
  }
@@ -29,6 +30,6 @@ async function main(): Promise<void> {
29
30
  try {
30
31
  await main()
31
32
  } catch (error) {
32
- console.error(`✖ ${error instanceof Error ? error.message : String(error)}`)
33
+ console.error(`✖ ${error_text.message_of(error)}`)
33
34
  process.exit(FAILURE_EXIT_CODE)
34
35
  }
@@ -1,8 +1,8 @@
1
- import type { InsertKind, InsertPosition } from '../scripts/git/git-epic-chains'
2
- import { git_epic_parse, type ExternalChild } from '../scripts/git/git-epic-parse'
3
- import { cli_body } from '../scripts/josh/cli-body'
1
+ import { cli_body } from '#scripts/josh/cli-body'
2
+ import type { InsertKind, InsertPosition } from './epic-chains'
4
3
  import { epic_cli_argv, ISSUE_NUMBER_PATTERN } from './epic-cli-argv'
5
4
  import { epic_cli_remove } from './epic-cli-remove'
5
+ import { epic_parse, type ExternalChild } from './epic-parse'
6
6
 
7
7
  // Parsing lives apart from the entry point so the argument rules can be asserted without spawning a
8
8
  // process or reaching GitHub. The entry point is then a thin shell around these two functions.
@@ -290,7 +290,7 @@ interface CrossRepoAddTarget {
290
290
  function find_cross_repo_add_target(argv: ReadonlyArray<string>): CrossRepoAddTarget | undefined {
291
291
  const [raw_epic] = to_positional_arguments(argv, ADD_VALUE_FLAGS)
292
292
  if (raw_epic === undefined) return undefined
293
- const epic = git_epic_parse.parse_external_reference(raw_epic)
293
+ const epic = epic_parse.parse_external_reference(raw_epic)
294
294
  if (epic === undefined) return undefined
295
295
 
296
296
  const local = parse_add_arguments(
@@ -1,12 +1,12 @@
1
- import { git_epic_parse, type ExternalChild } from './git-epic-parse'
2
- import { git_gh_command } from './git-gh-command'
3
- import { read_json_listing } from './parse-json-array'
4
- import { rest_comment_schema, type RestCommentData } from './schemas'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { read_json_listing } from '#scripts/git/parse-json-array'
3
+ import { rest_comment_schema, type RestCommentData } from '#scripts/git/schemas'
4
+ import { epic_parse, type ExternalChild } from './epic-parse'
5
5
 
6
6
  // The comment the epic auto-close announces itself with, and how a run recognizes the one a previous
7
7
  // run already posted.
8
8
  //
9
- // Kept apart from `git-epic-close.ts` because the two answer different questions — that file decides
9
+ // Kept apart from `epic-close.ts` because the two answer different questions — that file decides
10
10
  // *whether* an epic may close, this one decides what the announcement says and whether it is already
11
11
  // there — and because the file it was split from is within twenty lines of the three hundred a file
12
12
  // may hold (joshuafolkken/kit#1039).
@@ -131,7 +131,7 @@ function is_legacy_announcement(body: string): boolean {
131
131
  // both: a listing that could not be read says nothing about what is on the issue, and reporting it
132
132
  // as "nothing is there" would have the run state a fact it never established — the misread
133
133
  // joshuafolkken/kit#973 and joshuafolkken/kit#959 were each about. Keeping it apart is what lets
134
- // `git-epic-close.ts` say out loud that the duplicate check did not run, which is the whole
134
+ // `epic-close.ts` say out loud that the duplicate check did not run, which is the whole
135
135
  // difference between the two answers there.
136
136
  //
137
137
  // **Both of `read_json_listing`'s gaps arrive as `unreadable`**, rows the schema rejects included.
@@ -147,7 +147,7 @@ type CloseCommentState = 'present' | 'absent' | 'unreadable'
147
147
  // verbatim — a fenced one caught by the mask, an indented one by the indent check. `fence_mask` is the same walk every other body predicate
148
148
  // in this codebase reads through, rather than a second one written here (joshuafolkken/kit#890).
149
149
  function has_marker(body: string, children: ReadonlyArray<string>): boolean {
150
- const mask = git_epic_parse.fence_mask(body)
150
+ const mask = epic_parse.fence_mask(body)
151
151
 
152
152
  return body.split('\n').some((line, index) => {
153
153
  if (mask[index] !== true) return false
@@ -190,10 +190,10 @@ async function read_close_comment_state(
190
190
  return has_announcement(listing.rows, child_references(epic)) ? 'present' : 'absent'
191
191
  }
192
192
 
193
- const git_epic_close_comment = {
193
+ const epic_close_comment = {
194
194
  build_close_comment,
195
195
  read_close_comment_state,
196
196
  }
197
197
 
198
198
  export type { CloseCommentState, EpicChildren }
199
- export { git_epic_close_comment, CLOSE_ANNOUNCEMENT }
199
+ export { epic_close_comment, CLOSE_ANNOUNCEMENT }
@@ -1,10 +1,14 @@
1
- import { git_epic_close_comment } from './git-epic-close-comment'
2
- import { git_epic_parse, type ExternalChild } from './git-epic-parse'
3
- import { git_gh_command } from './git-gh-command'
4
- import { EPIC_LABEL } from './issue-labels'
5
- import { cutoff_cause, cutoff_of, type ScanCutoff } from './listing-cutoff'
6
- import { parse_json_array_or_undefined, parse_json_object_safe } from './parse-json-array'
7
- import { epic_child_schema, epic_issue_schema, type EpicChildData } from './schemas'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { EPIC_LABEL } from '#scripts/git/issue-labels'
3
+ import { cutoff_cause, cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
4
+ import {
5
+ parse_json_array_or_undefined,
6
+ parse_json_object_safe,
7
+ } from '#scripts/git/parse-json-array'
8
+ import { epic_child_schema, epic_issue_schema, type EpicChildData } from '#scripts/git/schemas'
9
+ import { error_text } from '#scripts/lib/error-message'
10
+ import { epic_close_comment } from './epic-close-comment'
11
+ import { epic_parse, type ExternalChild } from './epic-parse'
8
12
 
9
13
  // How many open epics the auto-close will look at. `git-gh-issue-list.ts` pages until `limit` rows
10
14
  // have been selected or the backlog runs out, so this is what keeps the ordinary case at exactly one
@@ -44,9 +48,9 @@ interface SiblingState {
44
48
  function to_epic_issue(raw: { number: number; body?: string | undefined }): EpicIssue {
45
49
  return {
46
50
  number: raw.number,
47
- children: git_epic_parse.parse_task_list_issue_numbers(raw.body),
48
- external_children: git_epic_parse.parse_external_task_list_children(raw.body),
49
- has_declared_order: git_epic_parse.has_declared_dependency_chain(raw.body),
51
+ children: epic_parse.parse_task_list_issue_numbers(raw.body),
52
+ external_children: epic_parse.parse_external_task_list_children(raw.body),
53
+ has_declared_order: epic_parse.has_declared_dependency_chain(raw.body),
50
54
  }
51
55
  }
52
56
 
@@ -91,7 +95,7 @@ function to_sibling_state(raw_json: string | undefined): SiblingState {
91
95
  const child = parse_child(raw_json)
92
96
 
93
97
  return {
94
- is_closed: git_epic_parse.is_state_closed(child?.state),
98
+ is_closed: epic_parse.is_state_closed(child?.state),
95
99
  has_blocked_by: has_blocked_by(child),
96
100
  }
97
101
  }
@@ -124,7 +128,7 @@ async function inspect_siblings(
124
128
  }
125
129
 
126
130
  // `pnpm josh epic --ordered` and `--add` record the declared chain themselves, through the REST
127
- // dependencies endpoint (`git-epic-relations.ts`, joshuafolkken/kit#1026), so the default route
131
+ // dependencies endpoint (`epic-relations.ts`, joshuafolkken/kit#1026), so the default route
128
132
  // arrives here with the relations already in place. Three routes still reach a declared order with
129
133
  // none recorded: an epic body written by hand, the manual fallback in `issue-template.md`, and
130
134
  // `apply_relations` failing — it counts the refusals, reports that the body is intact and lets the
@@ -200,11 +204,11 @@ async function close_epic_with(epic_number: string, comment: string | undefined)
200
204
  // `absent` and one of `unreadable` both post it, and the second says so.
201
205
  async function close_epic(epic: EpicIssue): Promise<boolean> {
202
206
  const epic_number = String(epic.number)
203
- const state = await git_epic_close_comment.read_close_comment_state(epic_number, epic)
207
+ const state = await epic_close_comment.read_close_comment_state(epic_number, epic)
204
208
 
205
209
  if (state === 'unreadable') console.info(`ℹ️ Epic #${epic_number}${UNREADABLE_COMMENTS_NOTE}`)
206
210
 
207
- const comment = state === 'present' ? undefined : git_epic_close_comment.build_close_comment(epic)
211
+ const comment = state === 'present' ? undefined : epic_close_comment.build_close_comment(epic)
208
212
 
209
213
  return await close_epic_with(epic_number, comment)
210
214
  }
@@ -256,7 +260,7 @@ async function close_epic_isolated(epic: EpicIssue, merged_number: number): Prom
256
260
  try {
257
261
  return await close_epic_when_complete(epic, merged_number)
258
262
  } catch (error) {
259
- const message = error instanceof Error ? error.message : String(error)
263
+ const message = error_text.message_of(error)
260
264
 
261
265
  console.info(`⚠️ Skipped epic #${String(epic.number)}: ${message}`)
262
266
 
@@ -265,7 +269,7 @@ async function close_epic_isolated(epic: EpicIssue, merged_number: number): Prom
265
269
  }
266
270
 
267
271
  function parents_of(open_epics: ReadonlyArray<EpicIssue>, child_number: number): Array<EpicIssue> {
268
- return open_epics.filter((epic) => git_epic_parse.has_child(epic.children, child_number))
272
+ return open_epics.filter((epic) => epic_parse.has_child(epic.children, child_number))
269
273
  }
270
274
 
271
275
  // Closes one epic unless a cycle or a shared child already reached it — the closed number, or
@@ -351,7 +355,7 @@ async function resolve_and_close_safely(merged_number: number): Promise<void> {
351
355
  try {
352
356
  await resolve_and_close(merged_number)
353
357
  } catch (error) {
354
- const message = error instanceof Error ? error.message : String(error)
358
+ const message = error_text.message_of(error)
355
359
 
356
360
  console.info(`⚠️ Skipped the epic auto-close check: ${message}`)
357
361
  }
@@ -376,12 +380,12 @@ async function close_completed_epics(input: {
376
380
  await resolve_and_close_safely(merged_number)
377
381
  }
378
382
 
379
- const git_epic_close = {
383
+ const epic_close = {
380
384
  close_completed_epics,
381
385
  }
382
386
 
383
387
  export {
384
- git_epic_close,
388
+ epic_close,
385
389
  close_completed_epics,
386
390
  truncated_epic_list_note,
387
391
  EPIC_LIST_LIMIT,
@@ -4,7 +4,6 @@ import { git_gh_api_path } from '#scripts/git/git-gh-api-path'
4
4
  import { git_gh_exec } from '#scripts/git/git-gh-exec'
5
5
  import { propagate_publish } from '#scripts/propagate/propagate-publish'
6
6
  import { derive_versions_endpoint } from '#scripts/version/version-command-config'
7
- import { execaSync } from 'execa'
8
7
  import { load } from 'js-yaml'
9
8
  import type { DependencyVerdict } from './epic-classify'
10
9
  import type { EpicChild } from './epic-graph'
@@ -71,7 +70,6 @@ function is_published(repo: string, version: string): boolean {
71
70
  }
72
71
 
73
72
  const GH_TIMEOUT_MS = 20_000
74
- const SUCCESS_EXIT_CODE = 0
75
73
  // The manifest arrives base64-encoded in the API's `content` field; `jq` decodes and reads it.
76
74
  const VERSION_JQ = '.content | @base64d | fromjson | {version, private, workspaces} | tojson'
77
75
  const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'
@@ -147,24 +145,20 @@ function pnpm_workspace_path(repo: string): string {
147
145
 
148
146
  // A read that failed for any reason other than a missing file leaves the layout unknown.
149
147
  function classify_workspace_failure(repo: string): WorkspaceCheck {
150
- const probe = execaSync('gh', ['api', '--include', '--silent', pnpm_workspace_path(repo)], {
151
- ...git_gh_exec.direct_environment(),
152
- reject: false,
153
- timeout: GH_TIMEOUT_MS,
154
- })
148
+ const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo), GH_TIMEOUT_MS)
155
149
 
156
- return git_gh_exec.parse_status_line(probe.stdout) === NOT_FOUND_STATUS ? 'single' : 'unreadable'
150
+ return status === NOT_FOUND_STATUS ? 'single' : 'unreadable'
157
151
  }
158
152
 
159
153
  function read_pnpm_workspace(repo: string): WorkspaceCheck {
160
- const result = execaSync('gh', ['api', pnpm_workspace_path(repo), '--jq', WORKSPACE_JQ], {
161
- ...git_gh_exec.direct_environment(),
162
- reject: false,
163
- timeout: GH_TIMEOUT_MS,
154
+ const stdout = git_gh_exec.read_gh_api_sync({
155
+ path: pnpm_workspace_path(repo),
156
+ jq_filter: WORKSPACE_JQ,
157
+ timeout_ms: GH_TIMEOUT_MS,
164
158
  })
165
- if (result.exitCode !== SUCCESS_EXIT_CODE) return classify_workspace_failure(repo)
159
+ if (stdout === undefined) return classify_workspace_failure(repo)
166
160
 
167
- return declares_packages(result.stdout) ? 'workspace' : 'single'
161
+ return declares_packages(stdout) ? 'workspace' : 'single'
168
162
  }
169
163
 
170
164
  // Whether the repository declares a workspace. `workspaces` covers npm and yarn; pnpm keeps its
@@ -227,38 +221,27 @@ function to_manifest(repo: string, stdout: string): ManifestAnswer {
227
221
  }
228
222
 
229
223
  // Why the read failed, asked only when it did. The status line is the protocol rather than prose, so
230
- // a missing manifest is told from a rate limit by matching a contract instead of a message — through
231
- // `git_gh_exec.parse_status_line` rather than a second copy of it.
224
+ // a missing manifest is told from a rate limit by matching a contract instead of a message.
232
225
  //
233
- // `git_gh_exec.exec_gh_api_status` asks the same question and is **not** reused: it is async, and
234
- // every caller down to `resolve_cross_repo` is synchronous. What is worth sharing is the parser,
235
- // which is shared; the spawn is four lines and duplicating it costs less than turning the
236
- // classifier, the resolver and `epic_classify` async for one probe. A 404 here is trustworthy in a way
237
- // a registry 404 is not: the repository's issues are already being read, so access is established and
238
- // what is missing is the file.
226
+ // The probe is `exec_gh_api_status_sync`, the synchronous twin of `exec_gh_api_status`: every caller
227
+ // down to `resolve_cross_repo` is synchronous, and turning the classifier, the resolver and
228
+ // `epic_classify` async for one probe would cost more than the twin (joshuafolkken/kit#2901). A 404
229
+ // here is trustworthy in a way a registry 404 is not: the repository's issues are already being read,
230
+ // so access is established and what is missing is the file.
239
231
  function classify_manifest_failure(repo: string): ManifestAnswer {
240
- const probe = execaSync('gh', ['api', '--include', '--silent', manifest_path(repo)], {
241
- ...git_gh_exec.direct_environment(),
242
- reject: false,
243
- timeout: GH_TIMEOUT_MS,
244
- })
232
+ const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo), GH_TIMEOUT_MS)
245
233
 
246
- return {
247
- kind:
248
- git_gh_exec.parse_status_line(probe.stdout) === NOT_FOUND_STATUS ? 'absent' : 'unreadable',
249
- }
234
+ return { kind: status === NOT_FOUND_STATUS ? 'absent' : 'unreadable' }
250
235
  }
251
236
 
252
237
  function fetch_manifest(repo: string): ManifestAnswer {
253
- const result = execaSync('gh', ['api', manifest_path(repo), '--jq', VERSION_JQ], {
254
- ...git_gh_exec.direct_environment(),
255
- reject: false,
256
- timeout: GH_TIMEOUT_MS,
238
+ const stdout = git_gh_exec.read_gh_api_sync({
239
+ path: manifest_path(repo),
240
+ jq_filter: VERSION_JQ,
241
+ timeout_ms: GH_TIMEOUT_MS,
257
242
  })
258
243
 
259
- return result.exitCode === SUCCESS_EXIT_CODE
260
- ? to_manifest(repo, result.stdout)
261
- : classify_manifest_failure(repo)
244
+ return stdout === undefined ? classify_manifest_failure(repo) : to_manifest(repo, stdout)
262
245
  }
263
246
 
264
247
  // Read once per pass, for the reason `published_cache` holds the other half: every edge to one
@@ -1,6 +1,6 @@
1
- import { git_epic_parse, type DependencyLink } from './git-epic-parse'
2
- import { format_replaced_relations } from './git-epic-reference'
3
- import { git_epic_sections, type BodyLines, type SectionRange } from './git-epic-sections'
1
+ import { epic_parse, type DependencyLink } from './epic-parse'
2
+ import { format_replaced_relations } from './epic-reference'
3
+ import { epic_sections, type BodyLines, type SectionRange } from './epic-sections'
4
4
 
5
5
  // Placing a decision record inside an epic body's `## Decisions` section.
6
6
  //
@@ -45,10 +45,8 @@ function to_declaration_error(line_number: number): string {
45
45
  function find_decision_error(record: string): string | undefined {
46
46
  if (record.trim().length === 0) return EMPTY_RECORD
47
47
 
48
- const input = git_epic_sections.to_body_lines(record)
49
- const [found] = git_epic_sections.find_indices(input, (line) =>
50
- git_epic_parse.is_declaration_line(line),
51
- )
48
+ const input = epic_sections.to_body_lines(record)
49
+ const [found] = epic_sections.find_indices(input, (line) => epic_parse.is_declaration_line(line))
52
50
 
53
51
  return found === undefined ? undefined : to_declaration_error(found + FIRST_LINE_NUMBER)
54
52
  }
@@ -81,8 +79,8 @@ function last_content_index(input: BodyLines, range: SectionRange): number {
81
79
  // The section's *end* rather than its start: the entries are a log, and a reader looking for the most
82
80
  // recent decision reads down. Nothing else in the body moves.
83
81
  function append_decision(body: string, record: string): string {
84
- const input = git_epic_sections.to_body_lines(body)
85
- const range = git_epic_sections.find_section_range(input, DECISIONS_HEADING_PATTERN)
82
+ const input = epic_sections.to_body_lines(body)
83
+ const range = epic_sections.find_section_range(input, DECISIONS_HEADING_PATTERN)
86
84
  if (range === undefined) return to_new_section(input.lines, record).join('\n')
87
85
 
88
86
  const at = last_content_index(input, range)
@@ -129,13 +127,13 @@ function format_decision_report(input: { total: number; failures: number }): str
129
127
  }
130
128
 
131
129
  // The heading a creation writes its reasoning under. Beside a pattern exactly as `DECISIONS_HEADING`
132
- // is, and tied to `git_epic_body`'s own literal by a test that reads a body the builder produced
130
+ // is, and tied to `epic_body`'s own literal by a test that reads a body the builder produced
133
131
  // rather than by a string comparison (joshuafolkken/kit#1712).
134
132
  const RATIONALE_HEADING_PATTERN = /^#{1,6}[ \t]+Split rationale\b/u
135
133
 
136
134
  function read_section(body: string, heading: RegExp): string {
137
- const input = git_epic_sections.to_body_lines(body)
138
- const range = git_epic_sections.find_section_range(input, heading)
135
+ const input = epic_sections.to_body_lines(body)
136
+ const range = epic_sections.find_section_range(input, heading)
139
137
  if (range === undefined) return ''
140
138
 
141
139
  return input.lines.slice(range.start, range.end).join('\n')
@@ -159,7 +157,7 @@ function read_recorded_reasons(body: string | undefined): string {
159
157
  ].join('\n')
160
158
  }
161
159
 
162
- const git_epic_decision = {
160
+ const epic_decision = {
163
161
  DECISIONS_HEADING,
164
162
  append_decision,
165
163
  append_replacements,
@@ -168,4 +166,4 @@ const git_epic_decision = {
168
166
  format_decision_report,
169
167
  }
170
168
 
171
- export { git_epic_decision }
169
+ export { epic_decision }
@@ -1,9 +1,9 @@
1
- import { git_epic_parse, type ExternalChild } from '#scripts/git/git-epic-parse'
2
1
  import { git_gh_command } from '#scripts/git/git-gh-command'
3
2
  import type { IssueReadFailure } from '#scripts/git/git-gh-issue-read'
4
3
  import { epic_cross_repo } from './epic-cross-repo'
5
4
  import type { EpicChild, IssueReference } from './epic-graph'
6
5
  import { epic_issue, type EpicIssue } from './epic-issue'
6
+ import { epic_parse, type ExternalChild } from './epic-parse'
7
7
  import { epic_relation_recheck } from './epic-relation-recheck'
8
8
 
9
9
  // Reading an epic and its children from GitHub.
@@ -73,8 +73,8 @@ async function fetch_epic_body(epic_number: number, scope?: string): Promise<Epi
73
73
  return {
74
74
  body,
75
75
  body_failure: read.kind === 'read' ? undefined : read,
76
- child_numbers: git_epic_parse.parse_task_list_issue_numbers(body),
77
- external: git_epic_parse.parse_external_task_list_children(body),
76
+ child_numbers: epic_parse.parse_task_list_issue_numbers(body),
77
+ external: epic_parse.parse_external_task_list_children(body),
78
78
  }
79
79
  }
80
80
 
@@ -1,10 +1,10 @@
1
- import type { DependencyLink } from '#scripts/git/git-epic-parse'
1
+ import type { DependencyLink } from './epic-parse'
2
2
  import {
3
3
  format_dependency_link,
4
4
  join_references,
5
5
  to_issue_reference,
6
6
  type IssueReference,
7
- } from '#scripts/git/git-epic-reference'
7
+ } from './epic-reference'
8
8
 
9
9
  // The dependency graph an epic's children form, and the two ways it can be wrong.
10
10
  //
@@ -275,5 +275,5 @@ const epic_graph = {
275
275
  }
276
276
 
277
277
  export type { EpicChild, GraphAnomaly, GraphAnomalyKind }
278
- export type { IssueReference } from '#scripts/git/git-epic-reference'
278
+ export type { IssueReference } from './epic-reference'
279
279
  export { epic_graph }
@@ -1,10 +1,10 @@
1
- import { git_epic_parse } from '#scripts/git/git-epic-parse'
2
1
  import { git_gh_command } from '#scripts/git/git-gh-command'
3
2
  import { EPIC_LABEL, has_any_label } from '#scripts/git/issue-labels'
4
3
  import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
5
4
  import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
6
5
  import type { OpenIssueData } from '#scripts/git/schemas'
7
6
  import { z } from 'zod'
7
+ import { epic_parse } from './epic-parse'
8
8
 
9
9
  // Which epic tracks which issue — the one answer, in one place (joshuafolkken/kit#1633).
10
10
  //
@@ -40,7 +40,7 @@ function build_tracking_index(
40
40
  const index = new Map<number, ReadonlyArray<number>>()
41
41
 
42
42
  for (const epic of epics) {
43
- for (const child of git_epic_parse.parse_task_list_issue_numbers(epic.body)) {
43
+ for (const child of epic_parse.parse_task_list_issue_numbers(epic.body)) {
44
44
  index.set(child, [...(index.get(child) ?? []), epic.number])
45
45
  }
46
46
  }
@@ -1,8 +1,8 @@
1
- import type { IssueReference } from '#scripts/git/git-epic-reference'
2
1
  import { git_gh_issue_rest } from '#scripts/git/git-gh-issue-rest'
3
2
  import { parse_json_object_safe } from '#scripts/git/parse-json-array'
4
3
  import { blocked_by_schema } from '#scripts/git/schemas'
5
4
  import { z } from 'zod'
5
+ import type { IssueReference } from './epic-reference'
6
6
 
7
7
  // The shapes one issue is read as, and the argument every epic command takes.
8
8
  //