@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
@@ -6,6 +6,7 @@ import {
6
6
  type RepoAnswer,
7
7
  } from './epic-candidate-confirm'
8
8
  import { epic_graph, type EpicChild } from './epic-graph'
9
+ import { epic_rank } from './epic-rank'
9
10
  import type { EpicVerdict } from './epic-report'
10
11
  import { epic_solo } from './epic-solo'
11
12
  import { epic_triage, type TriageVerdict } from './epic-triage'
@@ -107,6 +108,15 @@ function candidates_in(pool: RepoPool, repo: string): RepoPool {
107
108
  return { ...pool, candidates: pool.candidates.filter((child) => child.repo === repo) }
108
109
  }
109
110
 
111
+ // A pool's candidates for the repository in `epic_rank.rank` order — the ranking `backlog:next` offers
112
+ // in (joshuafolkken/kit#2928), counted against the pool's own graph. **Within a pool, not across
113
+ // pools**: the pool order is the order a person named the epics in, and that stays the outer key.
114
+ function ranked_in(pool: RepoPool, repo: string): RepoPool {
115
+ const scoped = candidates_in(pool, repo)
116
+
117
+ return { ...scoped, candidates: epic_rank.rank(scoped.candidates, pool.context.children) }
118
+ }
119
+
110
120
  // One pool's candidates, minus every one an earlier pool already carries. `seen` is mutated rather
111
121
  // than rebuilt because the earlier pools' claim is what the later one is filtered against.
112
122
  function unseen_candidates(pool: RepoPool, seen: Set<string>): RepoPool {
@@ -184,24 +194,6 @@ function solo_offer(
184
194
  return { ...answer, children: offered, notice: offered_notice(offered, read, request) }
185
195
  }
186
196
 
187
- // The confirmation walk stops at the free-lane count, so a `run:solo` candidate ranked past it — or
188
- // in a pool past it — would never reach `solo_offer` to be preferred. Into an idle repository the
189
- // first pool holding one is asked first, with it at its head through the same `epic_solo.prefer` the
190
- // offer applies (joshuafolkken/kit#2778). The candidate stays in its own pool, because a pool carries
191
- // the graph it is confirmed against.
192
- function solo_first(pools: ReadonlyArray<RepoPool>, read: BusyRead): ReadonlyArray<RepoPool> {
193
- if (read.kind !== 'idle') return pools
194
-
195
- const index = pools.findIndex((pool) => pool.candidates.some((child) => epic_solo.is_solo(child)))
196
- const pool = pools[index]
197
-
198
- if (pool === undefined) return pools
199
-
200
- const lifted = { ...pool, candidates: epic_solo.prefer(pool.candidates, read) }
201
-
202
- return [lifted, ...pools.filter((other) => other !== pool)]
203
- }
204
-
205
197
  // The repository is asked how full it is **before** any candidate is confirmed, for the reason
206
198
  // joshuafolkken/kit#1121 records: a repository with no free lane is handed nothing, so the relations
207
199
  // request that would confirm a candidate there buys an answer nobody reads — and a polling `epicrun`
@@ -247,10 +239,7 @@ async function offer_for_repo(
247
239
  }
248
240
  }
249
241
 
250
- const for_repo = solo_first(
251
- dedupe_pools(pools.map((pool) => candidates_in(pool, request.repo))),
252
- read,
253
- )
242
+ const for_repo = dedupe_pools(pools.map((pool) => ranked_in(pool, request.repo)))
254
243
 
255
244
  return solo_offer(await collect(for_repo, wanted_of(request, free)), read, request)
256
245
  }
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { repo_discovery } from '#scripts/discovery/repo-discovery'
4
- import { git_epic_parse } from '#scripts/git/git-epic-parse'
5
4
  import { git_gh_command } from '#scripts/git/git-gh-command'
6
5
  import { PROJECT_ROOT } from '#scripts/init/init-paths'
7
6
  import { josh_environment_file } from '#scripts/josh/josh-environment-file'
@@ -16,6 +15,7 @@ import { epic_lane_offer, type LaneOffer, type LaneRequest } from './epic-lane-o
16
15
  import { epic_next_read, type EpicRead, type SnapshotReads } from './epic-next-read'
17
16
  import { epic_next_views, type EpicView } from './epic-next-views'
18
17
  import { epic_outside_blocker } from './epic-outside-blocker'
18
+ import { epic_parse } from './epic-parse'
19
19
  import { epic_report, type EpicNextResult } from './epic-report'
20
20
 
21
21
  // `josh epic:next <E…>` — which of the named epics' children can be started right now, bundled per
@@ -170,7 +170,7 @@ function unreadable_anomaly(snapshot: EpicSnapshot): GraphAnomaly | undefined {
170
170
  // body whose declared chain and recorded relations agree — a worse outcome than following the chain
171
171
  // the run's own `epic:check` already flagged.
172
172
  function is_order_declared(body: string | undefined, links: ReadonlyArray<unknown>): boolean {
173
- return links.length > 0 || git_epic_parse.has_unordered_declaration(body)
173
+ return links.length > 0 || epic_parse.has_unordered_declaration(body)
174
174
  }
175
175
 
176
176
  function decide(
@@ -178,7 +178,7 @@ function decide(
178
178
  paths: ReadonlyMap<string, string> = new Map(),
179
179
  running?: ReadonlySet<string>,
180
180
  ): EpicNextResult {
181
- const links = git_epic_parse.parse_dependency_links(snapshot.body)
181
+ const links = epic_parse.parse_dependency_links(snapshot.body)
182
182
  const unreadable = body_anomaly(snapshot) ?? unreadable_anomaly(snapshot)
183
183
  const anomalies =
184
184
  unreadable === undefined
@@ -256,7 +256,7 @@ function parse_dependency_links(body: string | undefined): Array<DependencyLink>
256
256
  // `None — the children are independent` report as ordered, and `josh followup` then said the batch
257
257
  // order was never recorded on every child's merge. The narrowing joshuafolkken/kit#858 applied to
258
258
  // the link reader was the same judgement, and `epic --add` writes on that judgement too
259
- // (`git-epic-add-body.ts` protects an arrow outside the `Dependencies` section as prose) — so the
259
+ // (`epic-add-body.ts` protects an arrow outside the `Dependencies` section as prose) — so the
260
260
  // three readers of one body now recognize the same lines, instead of two patterns that could
261
261
  // disagree about which line is a declaration. They still answer different questions of those lines:
262
262
  // a self-loop (`#101 -> #101`) is a declaration here and yields no link, since `chain_links` drops
@@ -306,7 +306,7 @@ function is_state_closed(state: string | undefined): boolean {
306
306
  return state?.toUpperCase() === CLOSED_STATE
307
307
  }
308
308
 
309
- const git_epic_parse = {
309
+ const epic_parse = {
310
310
  fence_mask,
311
311
  is_task_list_line,
312
312
  is_declaration_line,
@@ -328,7 +328,7 @@ const git_epic_parse = {
328
328
 
329
329
  export type { DeclarationState, DependencyLink, ExternalChild }
330
330
  export {
331
- git_epic_parse,
331
+ epic_parse,
332
332
  fence_mask,
333
333
  is_task_list_line,
334
334
  is_declaration_line,
@@ -1,5 +1,5 @@
1
- import { git_epic_body } from './git-epic-body'
2
- import { git_epic_parse } from './git-epic-parse'
1
+ import { epic_body } from './epic-body'
2
+ import { epic_parse } from './epic-parse'
3
3
 
4
4
  // Promoting an existing issue into an epic.
5
5
  //
@@ -34,13 +34,13 @@ function has_conflicting_tracking(body: string | undefined): boolean {
34
34
  if (body === undefined) return false
35
35
  if (body.includes(PROMOTED_HEADING)) return true
36
36
 
37
- return git_epic_parse.parse_task_list_issue_numbers(body).length > 0
37
+ return epic_parse.parse_task_list_issue_numbers(body).length > 0
38
38
  }
39
39
 
40
40
  // Why the promotion was refused, phrased as what was found rather than as a rule number.
41
41
  function conflict_reason(body: string | undefined): string {
42
42
  if (body?.includes(PROMOTED_HEADING) === true) return 'it already carries the epic sections'
43
- const tracked = git_epic_parse.parse_task_list_issue_numbers(body)
43
+ const tracked = epic_parse.parse_task_list_issue_numbers(body)
44
44
  const list = tracked.map((child) => `#${String(child)}`).join(', ')
45
45
 
46
46
  return `its body already tracks ${list} as a task list, which would become a second child list`
@@ -50,7 +50,7 @@ function conflict_reason(body: string | undefined): string {
50
50
  // what happened. The original text is never rewritten — it is the record of why the split was made.
51
51
  function build_promoted_body(input: PromoteInput): string {
52
52
  const existing = (input.body ?? '').trimEnd()
53
- const epic_sections = git_epic_body.build_epic_body({
53
+ const epic_sections = epic_body.build_epic_body({
54
54
  children: input.children,
55
55
  rationale: input.rationale,
56
56
  is_ordered: input.is_ordered,
@@ -71,8 +71,8 @@ function build_promoted_body(input: PromoteInput): string {
71
71
  // so the body would declare an order and declare that there is none. Reported as "does not track
72
72
  // every child", that state left nothing to act on (joshuafolkken/kit#1155).
73
73
  function declaration_error(body: string): string | undefined {
74
- const state = git_epic_parse.read_declaration(body)
75
- if (git_epic_parse.is_declaration_readable(state)) return undefined
74
+ const state = epic_parse.read_declaration(body)
75
+ if (epic_parse.is_declaration_readable(state)) return undefined
76
76
 
77
77
  return state.has_chain
78
78
  ? 'The promoted body would declare an order and declare that there is none: the issue being promoted already carries a line that is only a chain (`#N -> #M`), and it contradicts the `Dependencies` section written for the epic. Reword that line first; nothing was written.'
@@ -80,7 +80,7 @@ function declaration_error(body: string): string | undefined {
80
80
  }
81
81
 
82
82
  function find_tracking_error(body: string, children: ReadonlyArray<number>): string | undefined {
83
- const tracked = git_epic_parse.parse_task_list_issue_numbers(body)
83
+ const tracked = epic_parse.parse_task_list_issue_numbers(body)
84
84
 
85
85
  if (children.some((child) => !tracked.includes(child))) {
86
86
  return 'The promoted body would not track every child; nothing was written.'
@@ -89,7 +89,7 @@ function find_tracking_error(body: string, children: ReadonlyArray<number>): str
89
89
  return declaration_error(body)
90
90
  }
91
91
 
92
- const git_epic_promote = {
92
+ const epic_promote = {
93
93
  EPIC_SECTION_MARKER,
94
94
  PROMOTED_HEADING,
95
95
  has_conflicting_tracking,
@@ -99,4 +99,4 @@ const git_epic_promote = {
99
99
  }
100
100
 
101
101
  export type { PromoteInput }
102
- export { git_epic_promote }
102
+ export { epic_promote }
@@ -0,0 +1,37 @@
1
+ import { issue_rank } from '#scripts/git/issue-rank'
2
+ import { epic_graph, type EpicChild } from './epic-graph'
3
+
4
+ // `issue-rank.ts`'s keys over graph children (joshuafolkken/kit#2928), for both offer paths that hand
5
+ // out children: `backlog:next` (`backlog-rank.ts`) and a named epic's `epic:next --lanes`
6
+ // (`epic-lane-offer.ts`). One ranking, so the two cannot disagree about which child goes first.
7
+
8
+ const CLOSED = 'CLOSED'
9
+
10
+ // How many open issues of the pool wait on each child. Only the pool is counted: it is every issue the
11
+ // caller may run, and an issue outside it is not one this ranking can start sooner.
12
+ function dependents_of(pool: ReadonlyArray<EpicChild>): ReadonlyMap<string, number> {
13
+ return issue_rank.count_dependents(
14
+ pool
15
+ .filter((child) => child.state !== CLOSED)
16
+ .map((child) => ({
17
+ key: epic_graph.key_of(child),
18
+ blockers: child.blocked_by.map((blocker) => epic_graph.blocker_key(blocker)),
19
+ })),
20
+ )
21
+ }
22
+
23
+ function rank(
24
+ children: ReadonlyArray<EpicChild>,
25
+ pool: ReadonlyArray<EpicChild>,
26
+ ): Array<EpicChild> {
27
+ const dependents = dependents_of(pool)
28
+
29
+ return issue_rank.rank(children, (child) => ({
30
+ labels: child.labels,
31
+ dependents: dependents.get(epic_graph.key_of(child)) ?? 0,
32
+ }))
33
+ }
34
+
35
+ const epic_rank = { dependents_of, rank }
36
+
37
+ export { epic_rank }
@@ -1,8 +1,8 @@
1
- import { epic_fetch } from '#scripts/epic/epic-fetch'
2
- import { epic_graph, type EpicChild } from '#scripts/epic/epic-graph'
3
- import { git_epic_parse } from './git-epic-parse'
4
- import { git_epic_validate, type EpicSubject } from './git-epic-validate'
5
- import { git_gh_command } from './git-gh-command'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { epic_fetch } from './epic-fetch'
3
+ import { epic_graph, type EpicChild } from './epic-graph'
4
+ import { epic_parse } from './epic-parse'
5
+ import { epic_validate, type EpicSubject } from './epic-validate'
6
6
 
7
7
  // Reading an epic and its current graph, for the two commands that edit its declared order.
8
8
  //
@@ -26,7 +26,7 @@ interface EpicReading {
26
26
  }
27
27
 
28
28
  async function read_subject(epic_number: number): Promise<EpicSubject | undefined> {
29
- return git_epic_validate.parse_epic_subject(
29
+ return epic_validate.parse_epic_subject(
30
30
  await git_gh_command.issue_get_labels_and_body(String(epic_number)),
31
31
  )
32
32
  }
@@ -39,7 +39,7 @@ async function read_recorded(
39
39
  ): Promise<{ children: ReadonlyArray<EpicChild>; repo: string } | { error: string }> {
40
40
  const repo = await git_gh_command.repo_get_name_with_owner()
41
41
  if (repo === undefined) return { error: UNKNOWN_REPO }
42
- const tracked = git_epic_parse.parse_task_list_issue_numbers(body)
42
+ const tracked = epic_parse.parse_task_list_issue_numbers(body)
43
43
  if (tracked.length === 0) return { children: [], repo }
44
44
 
45
45
  const fetched = await epic_fetch.fetch_children(tracked, repo)
@@ -64,10 +64,10 @@ async function read_epic(epic_number: number): Promise<EpicReading | { error: st
64
64
  return { subject, recorded: recorded.children, repo: recorded.repo }
65
65
  }
66
66
 
67
- const git_epic_read = {
67
+ const epic_read = {
68
68
  UNKNOWN_REPO,
69
69
  read_epic,
70
70
  }
71
71
 
72
- export { git_epic_read }
72
+ export { epic_read }
73
73
  export type { EpicReading }
@@ -1,11 +1,11 @@
1
- import { epic_graph, type EpicChild, type IssueReference } from '#scripts/epic/epic-graph'
2
- import { git_epic_add_body } from './git-epic-add-body'
3
- import { git_epic_chains } from './git-epic-chains'
4
- import { git_epic_parse, type DependencyLink } from './git-epic-parse'
5
- import { git_epic_read } from './git-epic-read'
6
- import { format_dependency_links } from './git-epic-reference'
7
- import { git_epic_relations } from './git-epic-relations'
8
- import { git_gh_command } from './git-gh-command'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { epic_add_body } from './epic-add-body'
3
+ import { epic_chains } from './epic-chains'
4
+ import { epic_graph, type EpicChild, type IssueReference } from './epic-graph'
5
+ import { epic_parse, type DependencyLink } from './epic-parse'
6
+ import { epic_read } from './epic-read'
7
+ import { format_dependency_links } from './epic-reference'
8
+ import { epic_relations } from './epic-relations'
9
9
 
10
10
  // `josh epic --reconcile <E>` — bring an epic's `## Dependencies` declaration and its native
11
11
  // `blocked-by` relations back into agreement, without a hand edit and without recording a decision
@@ -34,7 +34,7 @@ const RECONCILED = 'reconciled'
34
34
 
35
35
  interface ReconcilePlanInput {
36
36
  body: string | undefined
37
- // The epic's children with their native relations, exactly as `git_epic_read` reads them.
37
+ // The epic's children with their native relations, exactly as `epic_read` reads them.
38
38
  recorded: ReadonlyArray<EpicChild>
39
39
  // The epic's own repository — what a declared bare number names (joshuafolkken/kit#1126).
40
40
  repo: string
@@ -101,7 +101,7 @@ function to_reconciled_body(
101
101
  body: string | undefined,
102
102
  target_chains: ReadonlyArray<ReadonlyArray<number>>,
103
103
  ): { body: string } | { error: string } {
104
- return git_epic_add_body.rewrite_body({
104
+ return epic_add_body.rewrite_body({
105
105
  body: body ?? '',
106
106
  placed: [],
107
107
  position: undefined,
@@ -120,8 +120,8 @@ interface Mismatch {
120
120
  }
121
121
 
122
122
  function read_mismatch(input: ReconcilePlanInput): Mismatch {
123
- const chains_before = git_epic_parse.parse_dependency_chains(input.body)
124
- const declared = git_epic_chains.links_of(chains_before)
123
+ const chains_before = epic_parse.parse_dependency_chains(input.body)
124
+ const declared = epic_chains.links_of(chains_before)
125
125
 
126
126
  return {
127
127
  chains_before,
@@ -135,7 +135,7 @@ function read_mismatch(input: ReconcilePlanInput): Mismatch {
135
135
  function to_reconciled_plan(input: ReconcilePlanInput, mismatch: Mismatch): ReconcilePlan {
136
136
  const declared_chains = mismatch.declare.map((link) => to_chain(link))
137
137
  const target_chains = [...mismatch.chains_before, ...declared_chains]
138
- const cycle = find_cycle(git_epic_chains.links_of(target_chains), input.repo)
138
+ const cycle = find_cycle(epic_chains.links_of(target_chains), input.repo)
139
139
  if (cycle !== undefined) return { kind: 'cycle', message: cycle }
140
140
 
141
141
  const { declare, record } = mismatch
@@ -176,11 +176,9 @@ function report_declared(epic_number: number, declare: ReadonlyArray<DependencyL
176
176
  async function record_relations(record: ReadonlyArray<DependencyLink>): Promise<void> {
177
177
  if (record.length === 0) return
178
178
 
179
- const failures = await git_epic_relations.apply_relations(record, 'record')
179
+ const failures = await epic_relations.apply_relations(record, 'record')
180
180
 
181
- console.info(
182
- git_epic_relations.format_relation_report({ links: record, failures, action: 'record' }),
183
- )
181
+ console.info(epic_relations.format_relation_report({ links: record, failures, action: 'record' }))
184
182
  }
185
183
 
186
184
  async function apply_reconciliation(
@@ -218,7 +216,7 @@ async function apply_plan(epic_number: number, plan: ReconcilePlan): Promise<num
218
216
  // Every refusal — an unreadable epic, an unreadable graph, a cycle — happens before the body edit,
219
217
  // so a rejected invocation leaves the epic exactly as it was.
220
218
  async function reconcile_epic(epic_number: number): Promise<number> {
221
- const epic = await git_epic_read.read_epic(epic_number)
219
+ const epic = await epic_read.read_epic(epic_number)
222
220
 
223
221
  if ('error' in epic) {
224
222
  console.error(`✖ ${epic.error}`)
@@ -235,12 +233,12 @@ async function reconcile_epic(epic_number: number): Promise<number> {
235
233
  return await apply_plan(epic_number, plan)
236
234
  }
237
235
 
238
- const git_epic_reconcile = {
236
+ const epic_reconcile = {
239
237
  NOTHING_TO_RECONCILE,
240
238
  RECONCILED,
241
239
  build_reconcile_plan,
242
240
  reconcile_epic,
243
241
  }
244
242
 
245
- export { git_epic_reconcile }
243
+ export { epic_reconcile }
246
244
  export type { ReconcilePlan, ReconcilePlanInput, Reconciliation }
@@ -1,4 +1,4 @@
1
- import type { DependencyLink } from './git-epic-parse'
1
+ import type { DependencyLink } from './epic-parse'
2
2
 
3
3
  // How an issue and a dependency between two of them are written down.
4
4
  //
@@ -13,25 +13,6 @@ const DEPENDENCY_ARROW = ' -> '
13
13
  // and two separators would let the two disagree over something a reader reads side by side.
14
14
  const REFERENCE_SEPARATOR = ', '
15
15
 
16
- // An issue named from outside any one graph — a task-list row, a citation in prose, a blocker
17
- // relation, a child that could not be read. Repository **and** number, because a number alone cannot
18
- // identify one: issue numbers are unique per repository, so the same `#40` names two different issues
19
- // depending on who wrote it (joshuafolkken/kit#1014).
20
- //
21
- // It lives here rather than in `epic-graph` because the `blocked-by` relations are read in the git
22
- // layer and carry a repository of their own (joshuafolkken/kit#1126) — a second declaration there
23
- // would be the clone `CLAUDE.md` prohibits, and an import the other way would point the lower layer
24
- // at the higher one. `epic-graph` re-exports it, so every existing importer is unchanged.
25
- //
26
- // `state` is present only on a reference read from a `blocked-by` relation whose response carried it
27
- // (joshuafolkken/kit#1943). The classifier needs it for a blocker no named graph tracks, where there is
28
- // no child record to read the state from; every other reference leaves it out.
29
- interface IssueReference {
30
- repo: string
31
- number: number
32
- state?: 'OPEN' | 'CLOSED'
33
- }
34
-
35
16
  function to_issue_reference(issue_number: number): string {
36
17
  return `#${String(issue_number)}`
37
18
  }
@@ -73,7 +54,7 @@ function format_replaced_relations(links: ReadonlyArray<DependencyLink>): string
73
54
  return `Replaced blocked-by: ${join_references(quoted)}.`
74
55
  }
75
56
 
76
- const git_epic_reference = {
57
+ const epic_reference = {
77
58
  DEPENDENCY_ARROW,
78
59
  REFERENCE_SEPARATOR,
79
60
  to_issue_reference,
@@ -84,9 +65,9 @@ const git_epic_reference = {
84
65
  format_replaced_relations,
85
66
  }
86
67
 
87
- export type { IssueReference }
68
+ export type { IssueReference } from '#scripts/git/issue-reference'
88
69
  export {
89
- git_epic_reference,
70
+ epic_reference,
90
71
  DEPENDENCY_ARROW,
91
72
  REFERENCE_SEPARATOR,
92
73
  to_issue_reference,
@@ -1,5 +1,6 @@
1
- import { git_epic_parse } from '#scripts/git/git-epic-parse'
1
+ import { error_text } from '#scripts/lib/error-message'
2
2
  import { epic_graph, type EpicChild, type IssueReference } from './epic-graph'
3
+ import { epic_parse } from './epic-parse'
3
4
 
4
5
  // joshuafolkken/kit#1113: a second look at the relations, taken only where the first one is about to
5
6
  // become a verdict.
@@ -42,7 +43,7 @@ function suspect_children(
42
43
  body: string | undefined,
43
44
  declared_repo: string,
44
45
  ): Set<number> {
45
- const links = git_epic_parse.parse_dependency_links(body)
46
+ const links = epic_parse.parse_dependency_links(body)
46
47
  const local = children.filter((child) => child.repo === declared_repo)
47
48
 
48
49
  return new Set(
@@ -57,7 +58,7 @@ async function reread_child(child: EpicChild, read_blockers: BlockersReader): Pr
57
58
  try {
58
59
  return { ...child, blocked_by: await read_blockers(child) }
59
60
  } catch (error) {
60
- const reason = error instanceof Error ? error.message : String(error)
61
+ const reason = error_text.message_of(error)
61
62
 
62
63
  console.warn(
63
64
  `⚠ could not re-read the blockers of #${String(child.number)}: ${reason}\n` +
@@ -1,6 +1,6 @@
1
- import type { DependencyLink } from './git-epic-parse'
2
- import { format_dependency_links } from './git-epic-reference'
3
- import { git_gh_command } from './git-gh-command'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import type { DependencyLink } from './epic-parse'
3
+ import { format_dependency_links } from './epic-reference'
4
4
 
5
5
  // Recording and dropping the native `blocked-by` relations an epic's declared order implies.
6
6
  //
@@ -69,10 +69,10 @@ function format_relation_report(input: {
69
69
  return `⚠️ ${String(input.failures)} of ${total} blocked-by relation(s) could not be ${verb}; the epic body is intact.`
70
70
  }
71
71
 
72
- const git_epic_relations = {
72
+ const epic_relations = {
73
73
  apply_relations,
74
74
  format_relation_report,
75
75
  }
76
76
 
77
- export { git_epic_relations }
77
+ export { epic_relations }
78
78
  export type { RelationAction }
@@ -1,10 +1,10 @@
1
- import { epic_graph, type EpicChild } from '#scripts/epic/epic-graph'
2
- import { git_epic_add_body, type RewriteOutcome } from './git-epic-add-body'
3
- import { git_epic_chains } from './git-epic-chains'
4
- import { git_epic_decision } from './git-epic-decision'
5
- import { chain_links, git_epic_parse, type DependencyLink } from './git-epic-parse'
6
- import { format_dependency_link, format_dependency_links } from './git-epic-reference'
7
- import { git_epic_shape } from './git-epic-shape'
1
+ import { epic_add_body, type RewriteOutcome } from './epic-add-body'
2
+ import { epic_chains } from './epic-chains'
3
+ import { epic_decision } from './epic-decision'
4
+ import { epic_graph, type EpicChild } from './epic-graph'
5
+ import { chain_links, epic_parse, type DependencyLink } from './epic-parse'
6
+ import { format_dependency_link, format_dependency_links } from './epic-reference'
7
+ import { epic_shape } from './epic-shape'
8
8
 
9
9
  // Everything `josh epic --remove` decides before it writes anything.
10
10
  //
@@ -15,7 +15,7 @@ import { git_epic_shape } from './git-epic-shape'
15
15
  // `declaration_mismatch`, which stops an unattended run. Epic joshuafolkken/kit#1262 recorded
16
16
  // twenty-nine such removals before the command existed (joshuafolkken/kit#1712).
17
17
  //
18
- // Kept apart from the GitHub calls for the reason `git-epic-add-plan.ts` is: the whole decision is
18
+ // Kept apart from the GitHub calls for the reason `epic-add-plan.ts` is: the whole decision is
19
19
  // asserted without a network, and a refusal path only reachable through `gh` is one nobody tests.
20
20
 
21
21
  // The path whose consecutive pairs name the orders to delete. `--remove <E> 101 102 103` deletes
@@ -71,9 +71,7 @@ function find_undeclared_error(
71
71
  links: ReadonlyArray<DependencyLink>,
72
72
  chains: ReadonlyArray<ReadonlyArray<number>>,
73
73
  ): string | undefined {
74
- const declared = new Set(
75
- git_epic_chains.links_of(chains).map((link) => format_dependency_link(link)),
76
- )
74
+ const declared = new Set(epic_chains.links_of(chains).map((link) => format_dependency_link(link)))
77
75
  const missing = links.filter((link) => !declared.has(format_dependency_link(link)))
78
76
 
79
77
  return missing.length === 0
@@ -84,7 +82,7 @@ function find_undeclared_error(
84
82
  // A decision record that cannot be written is refused here rather than dropped, exactly as it is for
85
83
  // an insertion: `--decision-file` was given because the record has to exist.
86
84
  function find_decision_error(decision: string | undefined): string | undefined {
87
- return decision === undefined ? undefined : git_epic_decision.find_decision_error(decision)
85
+ return decision === undefined ? undefined : epic_decision.find_decision_error(decision)
88
86
  }
89
87
 
90
88
  function find_input_error(
@@ -93,7 +91,7 @@ function find_input_error(
93
91
  chains: ReadonlyArray<ReadonlyArray<number>>,
94
92
  ): string | undefined {
95
93
  return (
96
- git_epic_shape.find_subject_error(input, tracked) ??
94
+ epic_shape.find_subject_error(input, tracked) ??
97
95
  find_path_error(input.path) ??
98
96
  find_undeclared_error(to_path_links(input.path), chains) ??
99
97
  find_decision_error(input.decision)
@@ -112,7 +110,7 @@ function to_body(
112
110
  input: RemovePlanInput,
113
111
  chains_after: ReadonlyArray<ReadonlyArray<number>>,
114
112
  ): RewriteOutcome {
115
- return git_epic_add_body.rewrite_body({
113
+ return epic_add_body.rewrite_body({
116
114
  body: input.body ?? '',
117
115
  placed: [],
118
116
  chains_after,
@@ -122,13 +120,13 @@ function to_body(
122
120
  }
123
121
 
124
122
  function build_removal_plan(input: RemovePlanInput): RemoveOutcome {
125
- const tracked = git_epic_parse.parse_task_list_issue_numbers(input.body)
126
- const chains_before = git_epic_parse.parse_dependency_chains(input.body)
123
+ const tracked = epic_parse.parse_task_list_issue_numbers(input.body)
124
+ const chains_before = epic_parse.parse_dependency_chains(input.body)
127
125
  const error = find_input_error(input, tracked, chains_before)
128
126
  if (error !== undefined) return { error }
129
127
 
130
128
  const links = to_path_links(input.path)
131
- const chains_after = git_epic_chains.remove_links(chains_before, links)
129
+ const chains_after = epic_chains.remove_links(chains_before, links)
132
130
  const rewritten = to_body(input, chains_after)
133
131
  if ('error' in rewritten) return { error: rewritten.error }
134
132
 
@@ -144,9 +142,9 @@ function build_removal_plan(input: RemovePlanInput): RemoveOutcome {
144
142
  }
145
143
  }
146
144
 
147
- const git_epic_remove_plan = {
145
+ const epic_remove_plan = {
148
146
  build_removal_plan,
149
147
  }
150
148
 
151
- export { git_epic_remove_plan }
149
+ export { epic_remove_plan }
152
150
  export type { RemovePlan, RemovePlanInput, RemoveOutcome }
@@ -1,9 +1,9 @@
1
- import { git_epic_decision } from './git-epic-decision'
2
- import { git_epic_read } from './git-epic-read'
3
- import { format_dependency_links } from './git-epic-reference'
4
- import { git_epic_relations } from './git-epic-relations'
5
- import { git_epic_remove_plan, type RemovePlan } from './git-epic-remove-plan'
6
- import { git_gh_command } from './git-gh-command'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { epic_decision } from './epic-decision'
3
+ import { epic_read } from './epic-read'
4
+ import { format_dependency_links } from './epic-reference'
5
+ import { epic_relations } from './epic-relations'
6
+ import { epic_remove_plan, type RemovePlan } from './epic-remove-plan'
7
7
 
8
8
  // `josh epic --remove <E> <M> <N> [<N2> ...] [--decision-file <path|->]` — delete a declared order
9
9
  // from the epic's body and from the native `blocked-by` relations in one input
@@ -32,7 +32,7 @@ async function comment_decision(ends: ReadonlyArray<number>, decision: string):
32
32
  )
33
33
 
34
34
  console.info(
35
- git_epic_decision.format_decision_report({
35
+ epic_decision.format_decision_report({
36
36
  total: ends.length,
37
37
  failures: posted.filter((is_posted) => !is_posted).length,
38
38
  }),
@@ -43,7 +43,7 @@ function report_relations(plan: RemovePlan, failures: number): void {
43
43
  if (plan.removed.length === 0) return
44
44
 
45
45
  console.info(
46
- git_epic_relations.format_relation_report({ links: plan.removed, failures, action: 'drop' }),
46
+ epic_relations.format_relation_report({ links: plan.removed, failures, action: 'drop' }),
47
47
  )
48
48
  }
49
49
 
@@ -56,7 +56,7 @@ async function write_plan(
56
56
  console.info(
57
57
  `📋 Removed ${format_dependency_links(plan.links)} from epic #${String(epic_number)}.`,
58
58
  )
59
- report_relations(plan, await git_epic_relations.apply_relations(plan.removed, 'drop'))
59
+ report_relations(plan, await epic_relations.apply_relations(plan.removed, 'drop'))
60
60
 
61
61
  if (decision !== undefined) await comment_decision(plan.ends, decision)
62
62
  }
@@ -64,7 +64,7 @@ async function write_plan(
64
64
  // Delete a declared order, or refuse without writing anything. Every refusal happens before the body
65
65
  // edit, so a rejected invocation leaves the epic exactly as it was.
66
66
  async function remove_order(input: RemoveOrderInput): Promise<number> {
67
- const epic = await git_epic_read.read_epic(input.epic_number)
67
+ const epic = await epic_read.read_epic(input.epic_number)
68
68
 
69
69
  if ('error' in epic) {
70
70
  console.error(`✖ ${epic.error}`)
@@ -72,7 +72,7 @@ async function remove_order(input: RemoveOrderInput): Promise<number> {
72
72
  return FAILURE_EXIT_CODE
73
73
  }
74
74
 
75
- const outcome = git_epic_remove_plan.build_removal_plan({
75
+ const outcome = epic_remove_plan.build_removal_plan({
76
76
  epic_number: input.epic_number,
77
77
  body: epic.subject.body,
78
78
  labels: epic.subject.labels,
@@ -93,9 +93,9 @@ async function remove_order(input: RemoveOrderInput): Promise<number> {
93
93
  return SUCCESS_EXIT_CODE
94
94
  }
95
95
 
96
- const git_epic_remove = {
96
+ const epic_remove = {
97
97
  remove_order,
98
98
  }
99
99
 
100
- export { git_epic_remove }
100
+ export { epic_remove }
101
101
  export type { RemoveOrderInput }