@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,5 +1,6 @@
1
1
  import { readFileSync } from 'node:fs'
2
2
  import { fileURLToPath } from 'node:url'
3
+ import { git_gh_exec } from '#scripts/git/git-gh-exec'
3
4
  import {
4
5
  BREAKING_CHANGE_LABEL as BREAKING,
5
6
  BUGFIX_LABEL as BUGFIX,
@@ -7,7 +8,7 @@ import {
7
8
  ENHANCEMENT_LABEL,
8
9
  type ReleaseClassification,
9
10
  } from '#scripts/git/issue-labels'
10
- import { execaSync } from 'execa'
11
+ import { error_text } from '#scripts/lib/error-message'
11
12
  import { z } from 'zod'
12
13
 
13
14
  const CLASSIFICATION_SET: ReadonlySet<string> = new Set(CLASSIFICATION_LABELS)
@@ -95,17 +96,17 @@ function select_issue_classification(issue_json: string): ReleaseClassification
95
96
  // payload, that run fails, and when it lands in the newer check suite GitHub keeps its red result —
96
97
  // a rerun replays the same payload and fails again (joshuafolkken/kit#2712).
97
98
  function fetch_current_labels(repository: string, pull_number: number): ReadonlyArray<string> {
98
- const result = execaSync(
99
- 'gh',
100
- ['api', `repos/${repository}/issues/${String(pull_number)}/labels`, '--jq', '.[].name'],
101
- { reject: false },
102
- )
103
-
104
- if (result.exitCode !== 0) {
105
- throw new Error(`Could not read the pull request labels: ${result.stderr}`)
106
- }
99
+ const path = `repos/${repository}/issues/${String(pull_number)}/labels`
100
+
101
+ try {
102
+ const names = git_gh_exec.exec_gh_api_sync({ path, jq_filter: '.[].name' })
107
103
 
108
- return result.stdout.split('\n').filter((name) => name !== '')
104
+ return names.split('\n').filter((name) => name !== '')
105
+ } catch (error) {
106
+ const detail = error_text.message_of(error)
107
+
108
+ throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error })
109
+ }
109
110
  }
110
111
 
111
112
  function check_event(
@@ -133,6 +134,7 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
133
134
  const pr_classification = {
134
135
  classification_error,
135
136
  check_event,
137
+ fetch_current_labels,
136
138
  declaration,
137
139
  is_classification,
138
140
  select_issue_classification,
@@ -2,6 +2,7 @@ import { readdirSync, readFileSync, statSync } from 'node:fs'
2
2
  import { homedir } from 'node:os'
3
3
  import path from 'node:path'
4
4
  import { lane_paths } from '#scripts/lane/lane-paths'
5
+ import { file_reader } from '#scripts/lib/read-file'
5
6
  import { cost_usage, type UsageRecord } from './cost-usage'
6
7
 
7
8
  // Finding and reading Claude Code's session transcripts (joshuafolkken/kit#962).
@@ -404,16 +405,8 @@ function tally(content: string): Omit<SessionUsage, 'session_id'> {
404
405
  }
405
406
  }
406
407
 
407
- function read_text(file_path: string): string | undefined {
408
- try {
409
- return readFileSync(file_path, 'utf8')
410
- } catch {
411
- return undefined
412
- }
413
- }
414
-
415
408
  function read_session(file: SessionFile): SessionUsage {
416
- const content = read_text(file.path)
409
+ const content = file_reader.read_if_readable(file.path)
417
410
 
418
411
  if (content === undefined) return { session_id: file.session_id, ...empty_session() }
419
412
 
@@ -426,7 +419,7 @@ function read_session(file: SessionFile): SessionUsage {
426
419
  // "could not be read" is already reported through `is_readable`, and throwing here would turn one
427
420
  // missing session into a failed command.
428
421
  function read_raw(file: SessionFile): string {
429
- return read_text(file.path) ?? ''
422
+ return file_reader.read_if_readable(file.path) ?? ''
430
423
  }
431
424
 
432
425
  // The same read, with the failure left visible (joshuafolkken/kit#1439). **An unreadable transcript
@@ -435,7 +428,7 @@ function read_raw(file: SessionFile): string {
435
428
  // measured* rather than as measured at zero. `is_readable` on `SessionUsage` is the same distinction
436
429
  // on the cost side; this is it for a caller that wants the text.
437
430
  function read_optional(file: SessionFile): string | undefined {
438
- return read_text(file.path)
431
+ return file_reader.read_if_readable(file.path)
439
432
  }
440
433
 
441
434
  // One directory the search looked in, and whether it was there to look in. `exists` tells a directory
@@ -92,6 +92,17 @@ const DELEGATABLE_STEPS: ReadonlyArray<DelegatableStep> = [
92
92
  verifier:
93
93
  'the parent opens the cited lines; a conclusion those lines do not support fails there, and reading a handful of cited regions costs far less than redoing the reading. The unit reports what the code does and never what to change — deciding that is `diagnosis`, which stays kept, so a unit returning a root cause would be delegating the rejected row under this name',
94
94
  },
95
+ {
96
+ name: 'lane-failure-investigation',
97
+ // The `backlogrun` parent's counterpart of `investigation` (joshuafolkken/kit#2947). The parent
98
+ // orchestrates and never implements, yet it read a failed lane's logs, records and sources in its
99
+ // own context — about 150 grep / cat / sed calls on 2026-10-02 — and every later request of the
100
+ // batch re-read them. The verifier is `investigation`'s, and so is the boundary: the unit reports
101
+ // what happened, and what the parent does about it (park, re-dispatch, file) stays its own call.
102
+ does: "find out why a lane's child stopped, failed or conflicted — its output log, run records, PR checks and the files they name — and return the conclusion plus the `file:line` or log-line citations that support it, never the text read, so the batch parent's context stays the size its orchestration needs",
103
+ verifier:
104
+ 'the parent opens the cited lines and re-reads the child with `pnpm josh issue:state`; a conclusion those do not support fails there. The unit reports what happened and never what to do — parking, re-dispatching or filing stays with the parent, so a wrong account is caught before it is acted on',
105
+ },
95
106
  ]
96
107
 
97
108
  // Steps that were considered and are **not** delegatable, kept as a list rather than dropped: the
@@ -1,5 +1,6 @@
1
- import { existsSync, readFileSync } from 'node:fs'
1
+ import { existsSync } from 'node:fs'
2
2
  import path from 'node:path'
3
+ import { file_reader } from '#scripts/lib/read-file'
3
4
  import { read_spawn_stdout } from '#scripts/lib/spawn-exit'
4
5
  import { KIT_PACKAGE_NAME } from '#scripts/version/kit-descriptor'
5
6
  import { safe_json_parse } from '#scripts/version/parse-json'
@@ -36,16 +37,8 @@ const STATE_TARGET_MISSING = 'target-missing'
36
37
  const STATE_MISSING = 'missing'
37
38
  type ClaudeMdState = typeof STATE_OK | typeof STATE_TARGET_MISSING | typeof STATE_MISSING
38
39
 
39
- function read_optional(file_path: string): string | undefined {
40
- try {
41
- return existsSync(file_path) ? readFileSync(file_path, 'utf8') : undefined
42
- } catch {
43
- return undefined
44
- }
45
- }
46
-
47
40
  function parse_manifest(root: string): ManifestShape | undefined {
48
- const content = read_optional(path.join(root, 'package.json'))
41
+ const content = file_reader.read_if_readable(path.join(root, 'package.json'))
49
42
  if (content === undefined) return undefined
50
43
  const parsed = manifest_schema.safeParse(safe_json_parse(content))
51
44
 
@@ -74,7 +67,7 @@ function is_kit_consumer(root: string): boolean {
74
67
  }
75
68
 
76
69
  function is_plugin_declared(root: string): boolean {
77
- const content = read_optional(path.join(root, SETTINGS_PATH))
70
+ const content = file_reader.read_if_readable(path.join(root, SETTINGS_PATH))
78
71
  if (content === undefined) return false
79
72
  const parsed = settings_schema.safeParse(safe_json_parse(content))
80
73
 
@@ -94,7 +87,7 @@ function configured_hooks_path(root: string): string | undefined {
94
87
  }
95
88
 
96
89
  function claude_md_state(root: string): ClaudeMdState {
97
- const content = read_optional(path.join(root, CLAUDE_MD))
90
+ const content = file_reader.read_if_readable(path.join(root, CLAUDE_MD))
98
91
  if (content === undefined) return STATE_MISSING
99
92
  if (!content.includes(CLAUDE_MD_POINTER)) return STATE_OK
100
93
 
@@ -1,7 +1,7 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
1
  import path from 'node:path'
3
2
  import { ENV_FILE_NAME, PORT_SEED_KEY, ports } from '#ports'
4
3
  import { lane_environment } from '#scripts/lane/lane-environment'
4
+ import { file_reader } from '#scripts/lib/read-file'
5
5
 
6
6
  // Each discovered repository's port seed and the dev / preview ports it resolves to, plus which
7
7
  // repositories share a seed (joshuafolkken/kit#1494).
@@ -40,7 +40,7 @@ interface SharedSeed {
40
40
  // the one key that is actually wrong.
41
41
  function read_repository_ports(name: string, repository_path: string): RepoPorts {
42
42
  const file = path.join(repository_path, ENV_FILE_NAME)
43
- const content = existsSync(file) ? readFileSync(file, 'utf8') : ''
43
+ const content = file_reader.read_file_or_empty(file)
44
44
 
45
45
  try {
46
46
  const seed = lane_environment.read_root_seed(content)
@@ -65,7 +65,7 @@ function block_ceiling(size: number): number {
65
65
  // unreached file, so the definition cannot rot as documents move on or off the execution path.
66
66
  const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
67
67
  { path: '.claude/skills/dependency-update/SKILL.md', bytes: 8192 },
68
- { path: '.claude/skills/epic-commands/SKILL.md', bytes: 32_768 },
68
+ { path: '.claude/skills/epic-commands/SKILL.md', bytes: 36_864 },
69
69
  { path: '.claude/skills/verify-ui/SKILL.md', bytes: 8192 },
70
70
  { path: '.claude/skills/workflow-commands/followup-reference.md', bytes: 16_384 },
71
71
  { path: '.claude/skills/workflow-commands/fullrun-steps.md', bytes: 8192 },
@@ -73,46 +73,38 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
73
73
  { path: '.claude/skills/workflow-commands/issue-comments.md', bytes: 8192 },
74
74
  { path: '.claude/skills/workflow-commands/issue-fold-existing.md', bytes: 4096 },
75
75
  { path: '.claude/skills/workflow-commands/needs-human-review.md', bytes: 8192 },
76
- { path: '.claude/skills/workflow-commands/observation-filing.md', bytes: 28_672 },
76
+ { path: '.claude/skills/workflow-commands/observation-filing.md', bytes: 24_576 },
77
77
  // `pre-gate-cut.md` left this per-document budget in joshuafolkken/kit#2289: it became a point-of-use
78
78
  // document, so its bytes are now held by every entry's total read (`entry-read-budget.ts`) and the
79
79
  // two budgets must not hold it twice — the reachability line moved it from `unreached` to
80
80
  // `point-of-use`.
81
81
  { path: '.claude/skills/workflow-commands/prerequisite.md', bytes: 8192 },
82
- { path: '.claude/skills/workflow-commands/rule-residency.md', bytes: 24_576 },
83
82
  { path: '.claude/skills/workflow-commands/target-repository.md', bytes: 8192 },
84
83
  { path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
85
- { path: 'docs/josh-commands.md', bytes: 180_224 },
86
- { path: 'prompts/agent-rules.md', bytes: 4096 },
87
- { path: 'prompts/coding-standards.md', bytes: 16_384 },
84
+ { path: 'docs/josh-commands.md', bytes: 184_320 },
85
+ { path: 'prompts/coding-standards.md', bytes: 8192 },
88
86
  { path: 'prompts/collaboration-workflow.md', bytes: 8192 },
89
- { path: 'prompts/collaboration-workflow/consultation-vs-execution.md', bytes: 4096 },
90
- { path: 'prompts/collaboration-workflow/cross-repo-epic.md', bytes: 8192 },
91
- { path: 'prompts/collaboration-workflow/distributed-docs.md', bytes: 4096 },
92
- { path: 'prompts/collaboration-workflow/durable-rules.md', bytes: 4096 },
93
- { path: 'prompts/collaboration-workflow/epic-audit.md', bytes: 12_288 },
94
- { path: 'prompts/collaboration-workflow/epic-bundle.md', bytes: 24_576 },
87
+ { path: 'prompts/collaboration-workflow/cross-repo-epic.md', bytes: 4096 },
88
+ { path: 'prompts/collaboration-workflow/epic-audit.md', bytes: 4096 },
89
+ { path: 'prompts/collaboration-workflow/epic-bundle.md', bytes: 4096 },
95
90
  { path: 'prompts/collaboration-workflow/file-edits.md', bytes: 12_288 },
96
91
  { path: 'prompts/collaboration-workflow/gh-rest.md', bytes: 8192 },
97
92
  { path: 'prompts/collaboration-workflow/issue-citation.md', bytes: 8192 },
98
93
  // #2662 adds the feature and compatibility declarations to the canonical filing procedure.
99
94
  { path: 'prompts/collaboration-workflow/issue-template.md', bytes: 28_672 },
100
- { path: 'prompts/collaboration-workflow/latest-first.md', bytes: 4096 },
101
- { path: 'prompts/collaboration-workflow/no-clones.md', bytes: 4096 },
102
- { path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 24_576 },
95
+ { path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 16_384 },
103
96
  { path: 'prompts/collaboration-workflow/output-bounds.md', bytes: 16_384 },
104
97
  { path: 'prompts/collaboration-workflow/overview.md', bytes: 8192 },
105
98
  { path: 'prompts/collaboration-workflow/plan-comment.md', bytes: 20_480 },
99
+ { path: 'prompts/collaboration-workflow/principles.md', bytes: 12_288 },
106
100
  { path: 'prompts/collaboration-workflow/proposal-request.md', bytes: 4096 },
107
101
  { path: 'prompts/collaboration-workflow/report-format.md', bytes: 28_672 },
108
- { path: 'prompts/collaboration-workflow/residency.md', bytes: 20_480 },
109
- { path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 57_344 },
102
+ { path: 'prompts/collaboration-workflow/residency.md', bytes: 28_672 },
103
+ { path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 45_056 },
110
104
  { path: 'prompts/collaboration-workflow/shell-body.md', bytes: 12_288 },
111
- { path: 'prompts/collaboration-workflow/simplicity-first.md', bytes: 4096 },
112
- { path: 'prompts/collaboration-workflow/single-source-rules.md', bytes: 4096 },
113
- { path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 24_576 },
105
+ { path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 20_480 },
114
106
  { path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes: 12_288 },
115
- { path: 'prompts/collaboration-workflow/wip-cap.md', bytes: 20_480 },
107
+ { path: 'prompts/collaboration-workflow/wip-cap.md', bytes: 16_384 },
116
108
  { path: 'prompts/refactoring.md', bytes: 8192 },
117
109
  { path: 'prompts/review-rubric.md', bytes: 20_480 },
118
110
  { path: 'prompts/review.md', bytes: 20_480 },
@@ -12,7 +12,7 @@
12
12
  // is the distinction joshuafolkken/kit#1344 and joshuafolkken/kit#1460 were written after: a rule
13
13
  // moved to "read it later" is a rule that measurably never fires, and this moves nothing to later.
14
14
 
15
- import { readFileSync } from 'node:fs'
15
+ import { file_reader } from '#scripts/lib/read-file'
16
16
 
17
17
  // `#` to `######`, one space, then the title. Written with a literal space rather than `\s+` and a
18
18
  // lazy title so it cannot backtrack: `sonarjs/super-linear-regex` rejects the `\s+…+?\s*$` shape,
@@ -134,11 +134,7 @@ function candidates(markdown: string, wanted: string): Array<string> {
134
134
  }
135
135
 
136
136
  function read_optional(file_path: string): string | undefined {
137
- try {
138
- return readFileSync(file_path, 'utf8')
139
- } catch {
140
- return undefined
141
- }
137
+ return file_reader.read_if_readable(file_path)
142
138
  }
143
139
 
144
140
  const document_section = {
@@ -77,12 +77,13 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
77
77
  // planning entries): kickoff has no headroom, so the addition was offset by tightening that file's
78
78
  // heartbeat prose, keeping every entry within its existing block.
79
79
  // joshuafolkken/kit#2762 moved the rationale of five point-of-use documents to
80
- // `docs/maintainers/*-rationale.md`, and the ratchet holds the reduction.
80
+ // `docs/maintainers/*-rationale.md`, and the ratchet holds the reduction. joshuafolkken/kit#2895 did
81
+ // the same for eight more, lowering fullrun, halfrun and backlogrun one block each.
81
82
  { entry: 'kickoff', bytes: 229_376 },
82
- { entry: 'fullrun', bytes: 229_376 },
83
- { entry: 'halfrun', bytes: 229_376 },
84
- { entry: 'backlogrun', bytes: 233_472 },
85
- { entry: LANE_CHILD, bytes: 81_920 },
83
+ { entry: 'fullrun', bytes: 225_280 },
84
+ { entry: 'halfrun', bytes: 225_280 },
85
+ { entry: 'backlogrun', bytes: 229_376 },
86
+ { entry: LANE_CHILD, bytes: 77_824 },
86
87
  ]
87
88
 
88
89
  function byte_size(root: string, relative_path: string): number {
@@ -1,12 +1,12 @@
1
- import { git_epic_chains, type Chains, type InsertPosition } from './git-epic-chains'
2
- import { git_epic_decision } from './git-epic-decision'
3
- import { git_epic_parse, UNORDERED_DEPENDENCIES } from './git-epic-parse'
1
+ import { epic_chains, type Chains, type InsertPosition } from './epic-chains'
2
+ import { epic_decision } from './epic-decision'
3
+ import { epic_parse, UNORDERED_DEPENDENCIES } from './epic-parse'
4
4
  import {
5
5
  format_dependency_link,
6
6
  format_issue_references,
7
7
  to_issue_reference,
8
- } from './git-epic-reference'
9
- import { git_epic_sections, type BodyLines, type SectionRange } from './git-epic-sections'
8
+ } from './epic-reference'
9
+ import { epic_sections, type BodyLines, type SectionRange } from './epic-sections'
10
10
 
11
11
  // Rewriting an epic body so its task list and its dependency declaration both name a newly inserted
12
12
  // child, and refusing to hand back a body that would not.
@@ -50,7 +50,7 @@ interface RewriteInput {
50
50
 
51
51
  type RewriteOutcome = { body: string } | { error: string }
52
52
 
53
- const { find_indices, is_in_range, to_body_lines } = git_epic_sections
53
+ const { find_indices, is_in_range, to_body_lines } = epic_sections
54
54
 
55
55
  function to_task_row(issue_number: number): string {
56
56
  return `- [ ] ${to_issue_reference(issue_number)}`
@@ -66,7 +66,7 @@ function to_task_rows(placed: ReadonlyArray<number>): Array<string> {
66
66
  // insertion that declared no order takes, and the only one it can take: with no position named there
67
67
  // is nothing for a row to be relative to.
68
68
  function append_task_rows(input: BodyLines, placed: ReadonlyArray<number>): Array<string> {
69
- const rows = find_indices(input, (line) => git_epic_parse.is_task_list_line(line))
69
+ const rows = find_indices(input, (line) => epic_parse.is_task_list_line(line))
70
70
  const last = rows.at(-1)
71
71
  if (last === undefined) return [...input.lines]
72
72
 
@@ -80,7 +80,7 @@ function append_task_rows(input: BodyLines, placed: ReadonlyArray<number>): Arra
80
80
  // The issue a task-list row names, read through the parser rather than a second pattern of its own —
81
81
  // a row this recognizes is exactly a row `epic:next` and the auto-close read.
82
82
  function to_row_number(line: string): number | undefined {
83
- const [issue_number] = git_epic_parse.parse_task_list_issue_numbers(line)
83
+ const [issue_number] = epic_parse.parse_task_list_issue_numbers(line)
84
84
 
85
85
  return issue_number
86
86
  }
@@ -136,7 +136,7 @@ function to_placed_lines(input: RewriteInput): Array<string> {
136
136
  const DEPENDENCIES_HEADING = /^#{1,6}[ \t]+Dependencies\b/u
137
137
 
138
138
  function find_dependencies_range(input: BodyLines): SectionRange | undefined {
139
- return git_epic_sections.find_section_range(input, DEPENDENCIES_HEADING)
139
+ return epic_sections.find_section_range(input, DEPENDENCIES_HEADING)
140
140
  }
141
141
 
142
142
  function splice_declaration(
@@ -162,7 +162,7 @@ function replace_declaration(
162
162
  const range = find_dependencies_range(input)
163
163
  if (range === undefined) return undefined
164
164
 
165
- const found = find_indices(input, (line) => git_epic_parse.is_declaration_line(line))
165
+ const found = find_indices(input, (line) => epic_parse.is_declaration_line(line))
166
166
  const declarations = found.filter((index) => is_in_range(index, range))
167
167
  const [first] = declarations
168
168
 
@@ -182,7 +182,7 @@ function replace_declaration(
182
182
  // The unordered sentence parses to no chain, and its own constant stands in for it: that branch has no
183
183
  // numbers to render, and the sentence is a literal this module already shares with the parser.
184
184
  function to_safe_declaration(line: string): string {
185
- const [rendered] = git_epic_chains.render_chains(git_epic_parse.parse_dependency_chains(line))
185
+ const [rendered] = epic_chains.render_chains(epic_parse.parse_dependency_chains(line))
186
186
 
187
187
  return rendered ?? UNORDERED_DEPENDENCIES
188
188
  }
@@ -194,7 +194,7 @@ function find_stray_declaration(lines: ReadonlyArray<string>): string | undefine
194
194
  const input = to_body_lines(lines)
195
195
  const range = find_dependencies_range(input)
196
196
  if (range === undefined) return undefined
197
- const stray = find_indices(input, (line) => git_epic_parse.is_declaration_line(line)).find(
197
+ const stray = find_indices(input, (line) => epic_parse.is_declaration_line(line)).find(
198
198
  (index) => !is_in_range(index, range),
199
199
  )
200
200
 
@@ -212,7 +212,7 @@ function to_written_lines(input: RewriteInput): Array<string> {
212
212
  const { decision } = input
213
213
  if (decision === undefined) return with_rows
214
214
 
215
- return git_epic_decision.append_decision(with_rows.join('\n'), decision).split('\n')
215
+ return epic_decision.append_decision(with_rows.join('\n'), decision).split('\n')
216
216
  }
217
217
 
218
218
  // Whether the body already declares exactly the order the caller computed, chain structure included:
@@ -222,9 +222,9 @@ function is_declaration_unchanged(
222
222
  lines: ReadonlyArray<string>,
223
223
  rendered: ReadonlyArray<string>,
224
224
  ): boolean {
225
- const declared = git_epic_parse.parse_dependency_chains(lines.join('\n'))
225
+ const declared = epic_parse.parse_dependency_chains(lines.join('\n'))
226
226
 
227
- return git_epic_chains.render_chains(declared).join('\n') === rendered.join('\n')
227
+ return epic_chains.render_chains(declared).join('\n') === rendered.join('\n')
228
228
  }
229
229
 
230
230
  // The declaration work, once the task rows and any record are in.
@@ -261,7 +261,7 @@ function to_declared_body(
261
261
  // say — the unordered sentence, since an emptied section carries no machine-readable declaration at
262
262
  // all (joshuafolkken/kit#1712).
263
263
  function to_rendered_declaration(input: RewriteInput): Array<string> {
264
- const rendered = git_epic_chains.render_chains(input.chains_after)
264
+ const rendered = epic_chains.render_chains(input.chains_after)
265
265
  if (rendered.length > 0) return rendered
266
266
 
267
267
  return input.does_clear_declaration === true ? [UNORDERED_DEPENDENCIES] : []
@@ -277,7 +277,7 @@ function build_body(input: RewriteInput): BodyOutcome {
277
277
  }
278
278
 
279
279
  function to_link_keys(chains: Chains): Array<string> {
280
- return git_epic_chains
280
+ return epic_chains
281
281
  .links_of(chains)
282
282
  .map((link) => format_dependency_link(link))
283
283
  .toSorted((left, right) => left.localeCompare(right))
@@ -287,7 +287,7 @@ function to_link_keys(chains: Chains): Array<string> {
287
287
  // key lists rather than by reading the text back by eye — the round trip is the only evidence that
288
288
  // the rewrite and the parser agree.
289
289
  function has_intended_links(body: string, chains_after: Chains): boolean {
290
- const written = to_link_keys(git_epic_parse.parse_dependency_chains(body))
290
+ const written = to_link_keys(epic_parse.parse_dependency_chains(body))
291
291
  const intended = to_link_keys(chains_after)
292
292
 
293
293
  return (
@@ -296,7 +296,7 @@ function has_intended_links(body: string, chains_after: Chains): boolean {
296
296
  }
297
297
 
298
298
  function missing_rows(body: string, expected: ReadonlyArray<number>): Array<number> {
299
- const tracked = git_epic_parse.parse_task_list_issue_numbers(body)
299
+ const tracked = epic_parse.parse_task_list_issue_numbers(body)
300
300
 
301
301
  return expected.filter((issue_number) => !tracked.includes(issue_number))
302
302
  }
@@ -319,7 +319,7 @@ function to_placement_start(position: InsertPosition, target: number, count: num
319
319
  function has_intended_rows(body: string, input: RewriteInput): boolean {
320
320
  const { position, placed } = input
321
321
  if (position === undefined || placed.length === 0) return true
322
- const tracked = git_epic_parse.parse_task_list_issue_numbers(body)
322
+ const tracked = epic_parse.parse_task_list_issue_numbers(body)
323
323
  const target = tracked.indexOf(position.target)
324
324
  if (target === -1) return false
325
325
  const start = to_placement_start(position, target, placed.length)
@@ -334,7 +334,7 @@ function find_rewrite_error(body: string, input: RewriteInput): string | undefin
334
334
  return `The rewritten body would not track ${format_issue_references(missing)} as a task-list row.`
335
335
  }
336
336
 
337
- if (!git_epic_parse.has_machine_readable_declaration(body)) {
337
+ if (!epic_parse.has_machine_readable_declaration(body)) {
338
338
  return 'The rewritten body would carry no unambiguous machine-readable `Dependencies` declaration.'
339
339
  }
340
340
 
@@ -357,9 +357,9 @@ function rewrite_body(input: RewriteInput): RewriteOutcome {
357
357
  return error === undefined ? built : { error }
358
358
  }
359
359
 
360
- const git_epic_add_body = {
360
+ const epic_add_body = {
361
361
  rewrite_body,
362
362
  }
363
363
 
364
- export { git_epic_add_body }
364
+ export { epic_add_body }
365
365
  export type { RewriteInput, RewriteOutcome }
@@ -1,10 +1,10 @@
1
- import { epic_graph, type EpicChild } from '#scripts/epic/epic-graph'
2
- import { git_epic_add_body, type RewriteInput } from './git-epic-add-body'
3
- import { git_epic_chains, type InsertOutcome, type InsertPosition } from './git-epic-chains'
4
- import { git_epic_decision } from './git-epic-decision'
5
- import { git_epic_parse, type DependencyLink } from './git-epic-parse'
6
- import { format_dependency_links, to_issue_reference } from './git-epic-reference'
7
- import { git_epic_shape } from './git-epic-shape'
1
+ import { epic_add_body, type RewriteInput } from './epic-add-body'
2
+ import { epic_chains, type InsertOutcome, type InsertPosition } from './epic-chains'
3
+ import { epic_decision } from './epic-decision'
4
+ import { epic_graph, type EpicChild } from './epic-graph'
5
+ import { epic_parse, type DependencyLink } from './epic-parse'
6
+ import { format_dependency_links, to_issue_reference } from './epic-reference'
7
+ import { epic_shape } from './epic-shape'
8
8
 
9
9
  // Everything `josh epic --add` decides before it writes anything.
10
10
  //
@@ -273,16 +273,13 @@ function to_contradicted(
273
273
  }
274
274
 
275
275
  function to_plan(context: PlanContext): PlanOutcome {
276
- const { removed: replaced } = git_epic_chains.diff_links(
277
- context.chains_before,
278
- context.chains_after,
279
- )
280
- const decision = git_epic_decision.append_replacements(context.input.decision, replaced)
281
- const rewritten = git_epic_add_body.rewrite_body(to_rewrite_input(context, decision))
276
+ const { removed: replaced } = epic_chains.diff_links(context.chains_before, context.chains_after)
277
+ const decision = epic_decision.append_replacements(context.input.decision, replaced)
278
+ const rewritten = epic_add_body.rewrite_body(to_rewrite_input(context, decision))
282
279
  if ('error' in rewritten) return { error: rewritten.error }
283
280
 
284
- const links_after = git_epic_chains.links_of(context.chains_after)
285
- const result_rows = git_epic_parse.parse_task_list_issue_numbers(rewritten.body)
281
+ const links_after = epic_chains.links_of(context.chains_after)
282
+ const result_rows = epic_parse.parse_task_list_issue_numbers(rewritten.body)
286
283
 
287
284
  return {
288
285
  plan: {
@@ -304,7 +301,7 @@ function to_plan(context: PlanContext): PlanOutcome {
304
301
  // given because the record has to exist, so writing the insertion without it would report success for
305
302
  // half the job (joshuafolkken/kit#1350). `undefined` is "none was asked for", which is not a refusal.
306
303
  function find_decision_error(decision: string | undefined): string | undefined {
307
- return decision === undefined ? undefined : git_epic_decision.find_decision_error(decision)
304
+ return decision === undefined ? undefined : epic_decision.find_decision_error(decision)
308
305
  }
309
306
 
310
307
  // Asked of the declaration as it stands, because a relocation's removal can collapse the very
@@ -320,7 +317,7 @@ function find_ambiguity_error(
320
317
  ): string | undefined {
321
318
  if (input.is_order_only === true) return undefined
322
319
 
323
- return git_epic_chains.find_position_ambiguity(chains_before, input.position)
320
+ return epic_chains.find_position_ambiguity(chains_before, input.position)
324
321
  }
325
322
 
326
323
  // The refusals about what is being placed and where: the position may not name one of the issues
@@ -355,9 +352,9 @@ function find_input_error(
355
352
  chains_before: ReadonlyArray<ReadonlyArray<number>>,
356
353
  ): string | undefined {
357
354
  return (
358
- git_epic_shape.find_subject_error(input, tracked) ??
355
+ epic_shape.find_subject_error(input, tracked) ??
359
356
  find_placement_error(input, tracked, chains_before) ??
360
- find_relation_error(git_epic_chains.links_of(chains_before), input.recorded, input.repo) ??
357
+ find_relation_error(epic_chains.links_of(chains_before), input.recorded, input.repo) ??
361
358
  find_decision_error(input.decision)
362
359
  )
363
360
  }
@@ -377,10 +374,10 @@ function to_chains_after(
377
374
  moves: { placed: ReadonlyArray<number>; relocations: ReadonlyArray<number> },
378
375
  tracked: ReadonlyArray<number>,
379
376
  ): InsertOutcome {
380
- if (input.is_order_only === true) return git_epic_chains.keep_declaration(chains_before)
377
+ if (input.is_order_only === true) return epic_chains.keep_declaration(chains_before)
381
378
 
382
- return git_epic_chains.insert_children(
383
- git_epic_chains.remove_children(chains_before, moves.relocations),
379
+ return epic_chains.insert_children(
380
+ epic_chains.remove_children(chains_before, moves.relocations),
384
381
  moves.placed,
385
382
  input.position,
386
383
  tracked,
@@ -388,8 +385,8 @@ function to_chains_after(
388
385
  }
389
386
 
390
387
  function build_plan(input: PlanInput): PlanOutcome {
391
- const tracked = git_epic_parse.parse_task_list_issue_numbers(input.body)
392
- const chains_before = git_epic_parse.parse_dependency_chains(input.body)
388
+ const tracked = epic_parse.parse_task_list_issue_numbers(input.body)
389
+ const chains_before = epic_parse.parse_dependency_chains(input.body)
393
390
  const error = find_input_error(input, tracked, chains_before)
394
391
  if (error !== undefined) return { error }
395
392
 
@@ -413,9 +410,9 @@ function build_plan(input: PlanInput): PlanOutcome {
413
410
  })
414
411
  }
415
412
 
416
- const git_epic_add_plan = {
413
+ const epic_add_plan = {
417
414
  build_plan,
418
415
  }
419
416
 
420
- export { git_epic_add_plan }
417
+ export { epic_add_plan }
421
418
  export type { AddPlan, PlanInput, PlanOutcome }
@@ -1,16 +1,16 @@
1
1
  import { backlog_ready } from '#scripts/backlog/backlog-ready'
2
- import { epic_issue } from '#scripts/epic/epic-issue'
3
- import { git_epic_add_plan, type AddPlan } from './git-epic-add-plan'
4
- import type { InsertPosition } from './git-epic-chains'
5
- import { git_epic_decision } from './git-epic-decision'
6
- import { git_epic_read, type EpicReading } from './git-epic-read'
2
+ import { git_gh_command } from '#scripts/git/git-gh-command'
3
+ import { epic_add_plan, type AddPlan } from './epic-add-plan'
4
+ import type { InsertPosition } from './epic-chains'
5
+ import { epic_decision } from './epic-decision'
6
+ import { epic_issue } from './epic-issue'
7
+ import { epic_read, type EpicReading } from './epic-read'
7
8
  import {
8
9
  format_issue_references,
9
10
  format_replaced_relations,
10
11
  to_issue_reference,
11
- } from './git-epic-reference'
12
- import { git_epic_relations } from './git-epic-relations'
13
- import { git_gh_command } from './git-gh-command'
12
+ } from './epic-reference'
13
+ import { epic_relations } from './epic-relations'
14
14
 
15
15
  // `josh epic --add <E> <N...> [--before <M> | --after <M> | --order-before <M> | --order-after <M>]
16
16
  // [--decision-file <path|->]` — insert children into an existing epic.
@@ -65,7 +65,7 @@ interface AddChildrenInput {
65
65
  function report_relations(plan: AddPlan, failures: { added: number; removed: number }): void {
66
66
  if (plan.removed.length > 0) {
67
67
  console.info(
68
- git_epic_relations.format_relation_report({
68
+ epic_relations.format_relation_report({
69
69
  links: plan.removed,
70
70
  failures: failures.removed,
71
71
  action: 'drop',
@@ -76,7 +76,7 @@ function report_relations(plan: AddPlan, failures: { added: number; removed: num
76
76
  if (plan.added.length === 0) return
77
77
 
78
78
  console.info(
79
- git_epic_relations.format_relation_report({
79
+ epic_relations.format_relation_report({
80
80
  links: plan.added,
81
81
  failures: failures.added,
82
82
  action: 'record',
@@ -87,8 +87,8 @@ function report_relations(plan: AddPlan, failures: { added: number; removed: num
87
87
  // Dropped before recorded: inserting `#N` between `#B` and `#M` replaces `#B -> #M`, and applying the
88
88
  // new link first would leave `#M` momentarily blocked by both.
89
89
  async function apply_plan(plan: AddPlan): Promise<void> {
90
- const removed = await git_epic_relations.apply_relations(plan.removed, 'drop')
91
- const added = await git_epic_relations.apply_relations(plan.added, 'record')
90
+ const removed = await epic_relations.apply_relations(plan.removed, 'drop')
91
+ const added = await epic_relations.apply_relations(plan.added, 'record')
92
92
 
93
93
  report_relations(plan, { added, removed })
94
94
  }
@@ -167,7 +167,7 @@ async function comment_decision(children: ReadonlyArray<number>, decision: strin
167
167
  )
168
168
 
169
169
  console.info(
170
- git_epic_decision.format_decision_report({
170
+ epic_decision.format_decision_report({
171
171
  total: children.length,
172
172
  failures: posted.filter((is_posted) => !is_posted).length,
173
173
  }),
@@ -200,7 +200,7 @@ async function write_plan(input: AddChildrenInput, plan: AddPlan): Promise<void>
200
200
  // (joshuafolkken/kit#2337). Both reasons come back as a bare string the one caller prefixes `✖` onto,
201
201
  // so the two refusals read identically on stderr.
202
202
  async function read_open_epic(epic_number: number): Promise<EpicReading | { error: string }> {
203
- const epic = await git_epic_read.read_epic(epic_number)
203
+ const epic = await epic_read.read_epic(epic_number)
204
204
  if ('error' in epic) return epic
205
205
  if (is_closed_epic(epic.subject.state)) return { error: closed_epic_error(epic_number) }
206
206
 
@@ -218,7 +218,7 @@ async function add_children(input: AddChildrenInput): Promise<number> {
218
218
  return FAILURE_EXIT_CODE
219
219
  }
220
220
 
221
- const outcome = git_epic_add_plan.build_plan({
221
+ const outcome = epic_add_plan.build_plan({
222
222
  epic_number: input.epic_number,
223
223
  body: epic.subject.body,
224
224
  labels: epic.subject.labels,
@@ -241,9 +241,9 @@ async function add_children(input: AddChildrenInput): Promise<number> {
241
241
  return SUCCESS_EXIT_CODE
242
242
  }
243
243
 
244
- const git_epic_add = {
244
+ const epic_add = {
245
245
  add_children,
246
246
  }
247
247
 
248
- export { git_epic_add }
248
+ export { epic_add }
249
249
  export type { AddChildrenInput }