@joshuafolkken/kit 1.1010.0 → 1.1035.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 (272) 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/workflow-commands/SKILL.md +8 -23
  5. package/.claude/skills/workflow-commands/backlogrun-child.md +4 -2
  6. package/.claude/skills/workflow-commands/backlogrun.md +3 -12
  7. package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
  8. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  9. package/.claude/skills/workflow-commands/halfrun.md +2 -1
  10. package/.claude/skills/workflow-commands/into-target.md +1 -1
  11. package/.claude/skills/workflow-commands/observation-filing.md +42 -43
  12. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  13. package/.codex/hooks.json +1 -5
  14. package/.github/workflows/sonar-qube.yml +1 -1
  15. package/AGENTS.md +4 -25
  16. package/CLAUDE.md +24 -38
  17. package/GEMINI.md +4 -24
  18. package/README.md +22 -56
  19. package/dist/CLAUDE.md +24 -38
  20. package/dist/hooks/batch-guard.js +4 -4
  21. package/dist/hooks/{chunk-WMIR5SNX.js → chunk-D67KVGGY.js} +3 -3
  22. package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-EIFCAJRB.js} +70 -27
  23. package/dist/hooks/{chunk-KPZN42RT.js → chunk-GJT4BNVE.js} +3 -3
  24. package/dist/hooks/{chunk-PAXMLGSS.js → chunk-GLBDBJOK.js} +44 -24
  25. package/dist/hooks/{chunk-GBJUERYK.js → chunk-HOYJU5RM.js} +1 -1
  26. package/dist/hooks/{chunk-6PSTVOLH.js → chunk-HU66PWWL.js} +33 -57
  27. package/dist/hooks/{chunk-A2H5I2VD.js → chunk-KG6PZD72.js} +2 -2
  28. package/dist/hooks/{chunk-AWTFKKFW.js → chunk-XPLP27WA.js} +3 -8
  29. package/dist/hooks/{chunk-4GDR2YTV.js → chunk-Y27M6A7X.js} +4 -4
  30. package/dist/hooks/{chunk-56LGMJJV.js → chunk-ZXNE6NY3.js} +2 -2
  31. package/dist/hooks/codex-hook-adapter.js +10 -10
  32. package/dist/hooks/duplicate-read-guard.js +5 -5
  33. package/dist/hooks/format-edited.js +2 -2
  34. package/dist/hooks/investigation-guard.js +5 -5
  35. package/dist/hooks/pretool-guard.js +9 -9
  36. package/dist/hooks/stop-guard.js +11 -15
  37. package/dist/josh.js +38 -19
  38. package/dist/version/index.js +93 -41
  39. package/eslint/base.js +2 -11
  40. package/package.json +31 -5
  41. package/prompts/coding-standards.md +15 -290
  42. package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
  43. package/prompts/collaboration-workflow/epic-audit.md +2 -53
  44. package/prompts/collaboration-workflow/epic-bundle.md +2 -106
  45. package/prompts/collaboration-workflow/issue-template.md +4 -4
  46. package/prompts/collaboration-workflow/operating-rules.md +18 -75
  47. package/prompts/collaboration-workflow/principles.md +74 -0
  48. package/prompts/collaboration-workflow/residency.md +54 -14
  49. package/prompts/collaboration-workflow/rule-delivery.md +1 -11
  50. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  51. package/prompts/collaboration-workflow.md +22 -28
  52. package/scripts/adopt/adopt-logic.ts +2 -2
  53. package/scripts/adopt/adopt-toolkits.ts +1 -1
  54. package/scripts/adopt/adopt.ts +2 -2
  55. package/scripts/agent/agent-event.ts +2 -9
  56. package/scripts/agent/agent-exit-record.ts +2 -9
  57. package/scripts/backlog/backlog-budget-cli.ts +2 -6
  58. package/scripts/backlog/backlog-drive-cli.ts +20 -10
  59. package/scripts/backlog/backlog-drive.ts +50 -16
  60. package/scripts/backlog/backlog-offer-cli.ts +6 -5
  61. package/scripts/backlog/backlog-plan.ts +1 -1
  62. package/scripts/build/build-bin.ts +2 -1
  63. package/scripts/build/build-config-merge.ts +2 -1
  64. package/scripts/build/build-hooks.ts +24 -4
  65. package/scripts/build/build-managed-marker.ts +2 -1
  66. package/scripts/build/build-self-sync-guard.ts +2 -1
  67. package/scripts/build/build-version.ts +2 -1
  68. package/scripts/cases/cases-logic.ts +2 -6
  69. package/scripts/ci/pr-classification.ts +13 -11
  70. package/scripts/cost-runtime/cost-transcript.ts +4 -11
  71. package/scripts/doctor/doctor-consumer.ts +5 -12
  72. package/scripts/doctor/doctor-ports.ts +2 -2
  73. package/scripts/document/document-byte-budget.ts +8 -16
  74. package/scripts/document/document-section.ts +2 -6
  75. package/scripts/document/entry-read-budget.ts +1 -1
  76. package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
  77. package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
  78. package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
  79. package/scripts/epic/epic-audit-cli.ts +4 -4
  80. package/scripts/epic/epic-audit-orphans.ts +2 -2
  81. package/scripts/epic/epic-audit-rationale.ts +1 -1
  82. package/scripts/epic/epic-audit.ts +1 -1
  83. package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
  84. package/scripts/epic/epic-bundle-cli.ts +1 -1
  85. package/scripts/epic/epic-bundle.ts +2 -2
  86. package/scripts/epic/epic-candidate-confirm.ts +2 -1
  87. package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
  88. package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
  89. package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
  90. package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
  91. package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
  92. package/scripts/epic/epic-cross-repo.ts +21 -38
  93. package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
  94. package/scripts/epic/epic-fetch.ts +3 -3
  95. package/scripts/epic/epic-graph.ts +3 -3
  96. package/scripts/epic/epic-index.ts +2 -2
  97. package/scripts/epic/epic-issue.ts +1 -1
  98. package/scripts/epic/epic-next.ts +3 -3
  99. package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
  100. package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
  101. package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
  102. package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
  103. package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
  104. package/scripts/epic/epic-relation-recheck.ts +4 -3
  105. package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
  106. package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
  107. package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
  108. package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
  109. package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
  110. package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
  111. package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
  112. package/{scripts-ai → scripts/epic}/epic.ts +17 -16
  113. package/scripts/gh/fix-gh-packages.ts +2 -1
  114. package/scripts/gh/gh-spawn.ts +12 -10
  115. package/scripts/git/constants.ts +1 -1
  116. package/scripts/git/git-branch.ts +10 -1
  117. package/scripts/git/git-command.ts +1 -1
  118. package/scripts/git/git-error.ts +2 -5
  119. package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
  120. package/scripts/git/git-followup-flush.ts +74 -48
  121. package/scripts/git/git-followup-issue-close.ts +2 -2
  122. package/scripts/git/git-followup-label.ts +1 -1
  123. package/scripts/git/git-followup-pending.ts +1 -1
  124. package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
  125. package/scripts/git/git-gh-exec.ts +85 -26
  126. package/scripts/git/git-gh-issue-read.ts +2 -2
  127. package/scripts/git/git-gh-issue-rest.ts +2 -2
  128. package/scripts/git/git-gh-issue-write.ts +6 -6
  129. package/scripts/git/git-gh-pr-read.ts +0 -21
  130. package/scripts/git/git-gh-pr.ts +2 -23
  131. package/scripts/git/git-pr-followup-wrapup.ts +2 -6
  132. package/scripts/git/git-preflight.ts +3 -6
  133. package/scripts/git/git-staging.ts +21 -9
  134. package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
  135. package/scripts/git/github-issue-url.ts +1 -1
  136. package/scripts/git/issue-labels.ts +1 -1
  137. package/scripts/git/issue-reference.ts +19 -0
  138. package/scripts/git/main-merge.ts +2 -1
  139. package/scripts/git/main-sync.ts +3 -2
  140. package/scripts/git/schemas.ts +1 -1
  141. package/scripts/git/stash-pop-args.ts +7 -13
  142. package/scripts/git/stash-pop-cli.ts +2 -1
  143. package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
  144. package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
  145. package/scripts/hooks/format-rewrite-notice.ts +2 -8
  146. package/scripts/init/init-ai-copy.ts +2 -5
  147. package/scripts/init/init.ts +8 -4
  148. package/scripts/init/start-steps.ts +2 -1
  149. package/scripts/init/start.ts +2 -1
  150. package/scripts/issue/issue-comment-cli.ts +2 -1
  151. package/scripts/issue/issue-file-cli.ts +2 -1
  152. package/scripts/issue/issue-fold-cli.ts +9 -16
  153. package/scripts/issue/issue-scout-cli.ts +8 -13
  154. package/scripts/issue/measure-rerun-cli.ts +7 -6
  155. package/scripts/josh/josh-commands-ai.ts +2 -2
  156. package/scripts/josh/josh-commands-backlog.ts +1 -1
  157. package/scripts/josh/josh-commands-development.ts +1 -1
  158. package/scripts/josh/josh-commands-workflow.ts +4 -4
  159. package/scripts/lane/lane-cli.ts +2 -1
  160. package/scripts/lane/lane-open.ts +3 -2
  161. package/scripts/lane/lane-registry.ts +3 -6
  162. package/scripts/lib/cli-flags.ts +20 -2
  163. package/scripts/lib/error-message.ts +9 -0
  164. package/scripts/lib/read-file.ts +26 -2
  165. package/scripts/observations/observation-ledger-home.ts +98 -26
  166. package/scripts/observations/observation-ledger-migrate.ts +26 -21
  167. package/scripts/observations/observation-ledger-prepare.ts +10 -20
  168. package/scripts/observations/observation-ledger.ts +58 -25
  169. package/scripts/observations/observations-flush-cli.ts +6 -23
  170. package/scripts/observations/observations-flush-landing.ts +2 -1
  171. package/scripts/observations/observations-flush.ts +29 -20
  172. package/scripts/package/package-scout-cli.ts +8 -16
  173. package/scripts/ports/port-command.ts +2 -1
  174. package/scripts/propagate/propagate-publish.ts +6 -7
  175. package/scripts/propagate/propagate-steps.ts +2 -1
  176. package/scripts/propagate/propagate.ts +3 -2
  177. package/scripts/refactor/import-graph.ts +2 -6
  178. package/scripts/registry-migration/migrate.ts +3 -2
  179. package/scripts/registry-migration/user-npmrc.ts +2 -2
  180. package/scripts/release/release-cli.ts +2 -1
  181. package/scripts/repo/repo-setting.ts +10 -8
  182. package/scripts/review/review-finding-ledger.ts +3 -3
  183. package/scripts/review/review-findings-cli.ts +4 -11
  184. package/scripts/review/review-record-cli.ts +14 -11
  185. package/scripts/review/review-record.ts +6 -16
  186. package/scripts/rules/decision-oracle.ts +2 -2
  187. package/scripts/rules/rule-body-guard.ts +2 -2
  188. package/scripts/rules/rule-value-cache.ts +99 -0
  189. package/scripts/rules/rule-value-cli.ts +10 -9
  190. package/scripts/rules/rule-value.ts +1 -1
  191. package/scripts/run/run-carry-args.ts +2 -6
  192. package/scripts/run/run-carry-cli.ts +5 -7
  193. package/scripts/run/run-cut-args.ts +7 -13
  194. package/scripts/run/run-ending-cli.ts +4 -7
  195. package/scripts/run/run-event-stream.ts +2 -5
  196. package/scripts/run/run-liveness-cli.ts +4 -7
  197. package/scripts/run/run-progress-args.ts +2 -6
  198. package/scripts/run/run-progress-cli.ts +2 -1
  199. package/scripts/run/run-tail-cli.ts +3 -3
  200. package/scripts/run/run-tidy-stashes.ts +7 -14
  201. package/scripts/run/run-wake-cli.ts +2 -6
  202. package/scripts/run/run-wake-driver.ts +15 -2
  203. package/scripts/run/run-wake-session.ts +14 -4
  204. package/scripts/safe-chain/project-config.ts +3 -6
  205. package/scripts/security/security-audit-provision.ts +6 -7
  206. package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
  207. package/scripts/sync/directory-copy-guard.ts +2 -1
  208. package/scripts/sync/sync-ai-files.ts +3 -3
  209. package/scripts/sync/template-source-logic.ts +3 -4
  210. package/scripts/test/e2e-retry-check.ts +3 -6
  211. package/scripts/ui/ui-routes-cli.ts +3 -6
  212. package/scripts/version/bump-version.ts +2 -1
  213. package/scripts/version/version-remote.ts +18 -27
  214. package/scripts/version/version-targets.ts +3 -3
  215. package/.claude/skills/workflow-commands/rule-residency.md +0 -267
  216. package/evals/scenarios/consult-not-execute.json +0 -20
  217. package/evals/scenarios/no-implicit-workflow.json +0 -26
  218. package/evals/scenarios/overrides-both-locations.json +0 -29
  219. package/evals/scenarios/upstream-interrupt.json +0 -23
  220. package/evals/scenarios/workflow-skill-loaded.json +0 -16
  221. package/prompts/agent-lint-protocol.json +0 -79
  222. package/prompts/agent-rules.md +0 -63
  223. package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
  224. package/prompts/collaboration-workflow/distributed-docs.md +0 -7
  225. package/prompts/collaboration-workflow/durable-rules.md +0 -24
  226. package/prompts/collaboration-workflow/latest-first.md +0 -14
  227. package/prompts/collaboration-workflow/no-clones.md +0 -11
  228. package/prompts/collaboration-workflow/simplicity-first.md +0 -9
  229. package/prompts/collaboration-workflow/single-source-rules.md +0 -13
  230. package/scripts/build/shim-shell.ts +0 -25
  231. package/scripts/claude/skill-meta.ts +0 -37
  232. package/scripts/document/document-scan.ts +0 -108
  233. package/scripts/document/generate-catalog.ts +0 -69
  234. package/scripts/document/retired-phrases.ts +0 -143
  235. package/scripts/document/review-agent-type.ts +0 -46
  236. package/scripts/document/single-source.ts +0 -73
  237. package/scripts/git/git-fixture-workspace.ts +0 -88
  238. package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
  239. package/scripts/hooks/prompt-hooks.ts +0 -38
  240. package/scripts/init/hook-command-bootstrap.ts +0 -83
  241. package/scripts/issue/issue-logic.ts +0 -50
  242. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
  243. package/scripts/josh/josh-script-reader.ts +0 -11
  244. package/scripts/package/production-dependency-graph.ts +0 -68
  245. package/scripts/retrospective/retrospective-cli.ts +0 -97
  246. package/scripts/retrospective/retrospective.ts +0 -127
  247. package/scripts/rules/delivered-rules-harness.ts +0 -72
  248. package/scripts/rules/top-level-scripts-guard.ts +0 -47
  249. package/scripts/run/run-carry-flush.ts +0 -58
  250. package/scripts/run/run-transcript.golden.txt +0 -57
  251. package/scripts/run/run-wake-work.ts +0 -108
  252. package/scripts/sync/dependabot-pin-logic.ts +0 -135
  253. package/scripts/test/classify-isolation.ts +0 -149
  254. package/scripts/test/http-fault-injection.ts +0 -79
  255. package/scripts/test/josh-harness-environment.ts +0 -260
  256. package/scripts/test/josh-harness.ts +0 -96
  257. package/scripts/test/pilot-files.ts +0 -452
  258. package/scripts/test/pilot-measure.ts +0 -78
  259. package/scripts/test/test-network-guard.ts +0 -453
  260. package/scripts/test/test-repository-guard.ts +0 -317
  261. package/scripts/test/test-state-guard.ts +0 -85
  262. package/scripts/test/test-stdout-guard.ts +0 -54
  263. package/scripts/test/test-telegram-guard.ts +0 -90
  264. package/scripts/test/unit-guard-environment.ts +0 -12
  265. package/scripts/test/unit-projects.ts +0 -139
  266. package/scripts/test/vitest-include-globs.ts +0 -12
  267. package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
  268. package/scripts-ai/completion-gate.ts +0 -13
  269. package/scripts-ai/environment-loader.ts +0 -9
  270. /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
  271. /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
  272. /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
@@ -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 },
@@ -79,37 +79,29 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
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
84
  { 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 },
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 },
102
+ { path: 'prompts/collaboration-workflow/residency.md', bytes: 28_672 },
109
103
  { path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 57_344 },
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
105
  { path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 24_576 },
114
106
  { path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes: 12_288 },
115
107
  { path: 'prompts/collaboration-workflow/wip-cap.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 = {
@@ -82,7 +82,7 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
82
82
  { entry: 'fullrun', bytes: 229_376 },
83
83
  { entry: 'halfrun', bytes: 229_376 },
84
84
  { entry: 'backlogrun', bytes: 233_472 },
85
- { entry: LANE_CHILD, bytes: 81_920 },
85
+ { entry: LANE_CHILD, bytes: 77_824 },
86
86
  ]
87
87
 
88
88
  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 }
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { git_epic_decision } from '#scripts/git/git-epic-decision'
4
- import { git_epic_parse, type DependencyLink } from '#scripts/git/git-epic-parse'
5
3
  import { git_gh_command } from '#scripts/git/git-gh-command'
6
4
  import { issue_read } from '#scripts/issue/issue-read'
7
5
  import { epic_audit_logic, type AuditFinding, type ReferenceState } from './epic-audit'
@@ -10,10 +8,12 @@ import { epic_audit_orphans, type ClaimingSearch } from './epic-audit-orphans'
10
8
  import { epic_audit_rationale, type OrderPair } from './epic-audit-rationale'
11
9
  import { epic_audit_report, type AuditResult } from './epic-audit-report'
12
10
  import { epic_cross_repo } from './epic-cross-repo'
11
+ import { epic_decision } from './epic-decision'
13
12
  import { epic_fetch, type EpicSnapshot } from './epic-fetch'
14
13
  import { epic_graph, type IssueReference } from './epic-graph'
15
14
  import { epic_issue } from './epic-issue'
16
15
  import { epic_next } from './epic-next'
16
+ import { epic_parse, type DependencyLink } from './epic-parse'
17
17
 
18
18
  // `josh epic:audit <E>` — read an epic's children against each other and report what contradicts
19
19
  // what (joshuafolkken/kit#870).
@@ -278,7 +278,7 @@ async function to_audit_input(
278
278
  epic_number: number,
279
279
  repo: string,
280
280
  ): Promise<AuditInput> {
281
- const links = git_epic_parse.parse_dependency_links(snapshot.body)
281
+ const links = epic_parse.parse_dependency_links(snapshot.body)
282
282
  const order_pairs = epic_audit_rationale.order_pairs(links, children, snapshot.repo, repo)
283
283
 
284
284
  return {
@@ -291,7 +291,7 @@ async function to_audit_input(
291
291
  anomalies: fetch_anomalies(snapshot, children, links),
292
292
  contradictions: epic_audit_checks.find_order_contradictions(children, repo),
293
293
  order_pairs,
294
- decisions: git_epic_decision.read_recorded_reasons(snapshot.body),
294
+ decisions: epic_decision.read_recorded_reasons(snapshot.body),
295
295
  order_comments: await read_order_comments(order_pairs),
296
296
  is_epic_closed: await read_epic_closed(epic_number, epic_fetch.scope_for(snapshot.repo, repo)),
297
297
  }
@@ -1,10 +1,10 @@
1
- import { git_epic_parse } from '#scripts/git/git-epic-parse'
2
1
  import { git_gh_command } from '#scripts/git/git-gh-command'
3
2
  import { cutoff_of, PAGE_CEILING_CAUSE, type ScanCutoff } from '#scripts/git/listing-cutoff'
4
3
  import { parse_json_array_safe } from '#scripts/git/parse-json-array'
5
4
  import { z } from 'zod'
6
5
  import { epic_audit_logic, type AuditFinding, type FindingLevel } from './epic-audit'
7
6
  import type { EpicSnapshot } from './epic-fetch'
7
+ import { epic_parse } from './epic-parse'
8
8
 
9
9
  // Check 4's half of `epic:audit`: an issue that names this epic as its parent but that the epic's
10
10
  // task list does not track. It would never be run, and the epic would close without it.
@@ -125,7 +125,7 @@ function claimed_numbers(search: ClaimingSearch): Array<number> {
125
125
  // reported (joshuafolkken/kit#1014). Read with the same parser `epic_fetch` reads the local rows
126
126
  // with, which never matches a `- [ ] owner/repo#N` row.
127
127
  function locally_tracked(snapshot: EpicSnapshot): Array<number> {
128
- return git_epic_parse.parse_task_list_issue_numbers(snapshot.body)
128
+ return epic_parse.parse_task_list_issue_numbers(snapshot.body)
129
129
  }
130
130
 
131
131
  const epic_audit_orphans = {
@@ -1,7 +1,7 @@
1
- import type { DependencyLink } from '#scripts/git/git-epic-parse'
2
1
  import { epic_audit_logic, type AuditFinding } from './epic-audit'
3
2
  import type { AuditChild } from './epic-audit-checks'
4
3
  import { epic_graph, type IssueReference } from './epic-graph'
4
+ import type { DependencyLink } from './epic-parse'
5
5
 
6
6
  // Check 6 — a declared order between two open children that nobody wrote a reason for
7
7
  // (joshuafolkken/kit#1712).
@@ -20,7 +20,7 @@ const REFERENCE_PATTERN = /#(\d+)\b/gu
20
20
  // What may appear in the `owner/repo` written in front of a `#`: exactly the set the previous
21
21
  // lookbehind refused a bare reference after, plus the `.` a repository name may legitimately contain.
22
22
  //
23
- // The dot is where this parse and the task-list parse used to disagree. `git_epic_parse`'s
23
+ // The dot is where this parse and the task-list parse used to disagree. `epic_parse`'s
24
24
  // `EXTERNAL_REFERENCE_SOURCE` allows one, so `- [ ] owner/site.com#40` is tracked as a genuine
25
25
  // cross-repository child; this one excluded it, so a sibling quoting `owner/site.com#40` was read
26
26
  // back as `com` — no repository — and every check skipped it in silence. A child that can be tracked
@@ -1,7 +1,7 @@
1
- import { UNORDERED_DEPENDENCIES } from './git-epic-parse'
2
- import { DEPENDENCY_ARROW, to_issue_reference } from './git-epic-reference'
1
+ import { UNORDERED_DEPENDENCIES } from './epic-parse'
2
+ import { DEPENDENCY_ARROW, to_issue_reference } from './epic-reference'
3
3
 
4
- // The epic body is the machine-readable half of the epic contract: `scripts/git/git-epic-close.ts`
4
+ // The epic body is the machine-readable half of the epic contract: `scripts/epic/epic-close.ts`
5
5
  // reads the task list to decide when the batch is finished, and the order-unrecorded warning reads
6
6
  // the `Dependencies` section. Generating both from the same input is what keeps them from
7
7
  // disagreeing with each other, or with the run command printed next to them.
@@ -25,7 +25,7 @@ interface EpicBodyInput {
25
25
  epic_number?: number | undefined
26
26
  }
27
27
 
28
- // Task-list syntax, never a bare `#N` link: `git_epic_parse.parse_task_list_issue_numbers` matches
28
+ // Task-list syntax, never a bare `#N` link: `epic_parse.parse_task_list_issue_numbers` matches
29
29
  // nothing else, and GitHub only auto-checks a row written this way.
30
30
  function format_progress(children: ReadonlyArray<number>): string {
31
31
  return children.map((child) => `- [ ] ${to_issue_reference(child)}`).join('\n')
@@ -139,12 +139,12 @@ function build_dependency_pairs(
139
139
  .filter((pair) => pair.blocked !== pair.blocker)
140
140
  }
141
141
 
142
- const git_epic_body = {
142
+ const epic_body = {
143
143
  EPIC_PLACEHOLDER,
144
144
  build_epic_body,
145
145
  build_dependency_pairs,
146
146
  format_run_command,
147
147
  }
148
148
 
149
- export { git_epic_body, SPLIT_RATIONALE_HEADING }
149
+ export { epic_body, SPLIT_RATIONALE_HEADING }
150
150
  export type { EpicBodyInput }
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import type { IssueReference } from '#scripts/git/git-epic-reference'
4
3
  import { git_gh_command } from '#scripts/git/git-gh-command'
5
4
  import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
6
5
  import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
@@ -18,6 +17,7 @@ import { epic_bundle_gaps } from './epic-bundle-gaps'
18
17
  import { epic_bundle_referenced, type ReferencedContext } from './epic-bundle-referenced'
19
18
  import { epic_index, epic_schema, type FetchedEpics } from './epic-index'
20
19
  import { epic_issue } from './epic-issue'
20
+ import type { IssueReference } from './epic-reference'
21
21
 
22
22
  // `josh epic:bundle <N>` — after an issue is filed, look at the open backlog and say whether it
23
23
  // belongs with something already there (joshuafolkken/kit#873).
@@ -1,7 +1,7 @@
1
- import type { DependencyLink } from '#scripts/git/git-epic-parse'
2
- import type { IssueReference } from '#scripts/git/git-epic-reference'
3
1
  import { epic_audit_logic } from './epic-audit'
4
2
  import { epic_graph } from './epic-graph'
3
+ import type { DependencyLink } from './epic-parse'
4
+ import type { IssueReference } from './epic-reference'
5
5
 
6
6
  // Bundling a newly filed issue with the ones it turns out to be related to.
7
7
  //
@@ -1,3 +1,4 @@
1
+ import { error_text } from '#scripts/lib/error-message'
1
2
  import { epic_classify, type ResolveDependency } from './epic-classify'
2
3
  import { epic_graph, type EpicChild, type IssueReference } from './epic-graph'
3
4
  import type { BlockersReader } from './epic-relation-recheck'
@@ -118,7 +119,7 @@ async function read_or_withhold(
118
119
  try {
119
120
  return await read_blockers(candidate)
120
121
  } catch (error) {
121
- const reason = error instanceof Error ? error.message : String(error)
122
+ const reason = error_text.message_of(error)
122
123
 
123
124
  console.warn(
124
125
  `⚠ could not confirm the blockers of #${String(candidate.number)}: ${reason}\n` +