@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,5 +1,5 @@
1
- import { chain_links, type DependencyLink } from './git-epic-parse'
2
- import { DEPENDENCY_ARROW, format_dependency_link, to_issue_reference } from './git-epic-reference'
1
+ import { chain_links, type DependencyLink } from './epic-parse'
2
+ import { DEPENDENCY_ARROW, format_dependency_link, to_issue_reference } from './epic-reference'
3
3
 
4
4
  // The declared dependency order of an epic, as the shape an insertion needs.
5
5
  //
@@ -392,7 +392,7 @@ function insert_children(
392
392
  return duplicated === undefined ? outcome : { error: already_declared_error(duplicated) }
393
393
  }
394
394
 
395
- const git_epic_chains = {
395
+ const epic_chains = {
396
396
  render_chains,
397
397
  links_of,
398
398
  diff_links,
@@ -407,5 +407,5 @@ const git_epic_chains = {
407
407
  insert_children,
408
408
  }
409
409
 
410
- export { git_epic_chains }
410
+ export { epic_chains }
411
411
  export type { Chain, Chains, InsertKind, InsertOutcome, InsertPosition, LinkDiff }
@@ -2,10 +2,11 @@
2
2
  /**
3
3
  * Check an existing epic Issue against the four requirements, reporting each as pass or fail.
4
4
  *
5
- * Usage: tsx scripts-ai/epic-check.ts <issue-number>
5
+ * Usage: tsx scripts/epic/epic-check.ts <issue-number>
6
6
  */
7
- import { git_epic_run } from '../scripts/git/git-epic-run'
7
+ import { error_text } from '#scripts/lib/error-message'
8
8
  import { epic_cli } from './epic-cli'
9
+ import { epic_run } from './epic-run'
9
10
 
10
11
  const ARGV_OFFSET = 2
11
12
  const USAGE = 'Usage: josh epic:check <issue-number>'
@@ -19,7 +20,7 @@ async function main(): Promise<void> {
19
20
  process.exit(FAILURE_EXIT_CODE)
20
21
  }
21
22
 
22
- const exit_code = await git_epic_run.check_epic(epic_number)
23
+ const exit_code = await epic_run.check_epic(epic_number)
23
24
 
24
25
  if (exit_code !== 0) process.exit(exit_code)
25
26
  }
@@ -29,6 +30,6 @@ async function main(): Promise<void> {
29
30
  try {
30
31
  await main()
31
32
  } catch (error) {
32
- console.error(`✖ ${error instanceof Error ? error.message : String(error)}`)
33
+ console.error(`✖ ${error_text.message_of(error)}`)
33
34
  process.exit(FAILURE_EXIT_CODE)
34
35
  }
@@ -1,8 +1,8 @@
1
- import type { InsertKind, InsertPosition } from '../scripts/git/git-epic-chains'
2
- import { git_epic_parse, type ExternalChild } from '../scripts/git/git-epic-parse'
3
- import { cli_body } from '../scripts/josh/cli-body'
1
+ import { cli_body } from '#scripts/josh/cli-body'
2
+ import type { InsertKind, InsertPosition } from './epic-chains'
4
3
  import { epic_cli_argv, ISSUE_NUMBER_PATTERN } from './epic-cli-argv'
5
4
  import { epic_cli_remove } from './epic-cli-remove'
5
+ import { epic_parse, type ExternalChild } from './epic-parse'
6
6
 
7
7
  // Parsing lives apart from the entry point so the argument rules can be asserted without spawning a
8
8
  // process or reaching GitHub. The entry point is then a thin shell around these two functions.
@@ -290,7 +290,7 @@ interface CrossRepoAddTarget {
290
290
  function find_cross_repo_add_target(argv: ReadonlyArray<string>): CrossRepoAddTarget | undefined {
291
291
  const [raw_epic] = to_positional_arguments(argv, ADD_VALUE_FLAGS)
292
292
  if (raw_epic === undefined) return undefined
293
- const epic = git_epic_parse.parse_external_reference(raw_epic)
293
+ const epic = epic_parse.parse_external_reference(raw_epic)
294
294
  if (epic === undefined) return undefined
295
295
 
296
296
  const local = parse_add_arguments(
@@ -1,12 +1,12 @@
1
- import { git_epic_parse, type ExternalChild } from './git-epic-parse'
2
- import { git_gh_command } from './git-gh-command'
3
- import { read_json_listing } from './parse-json-array'
4
- import { rest_comment_schema, type RestCommentData } from './schemas'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { read_json_listing } from '#scripts/git/parse-json-array'
3
+ import { rest_comment_schema, type RestCommentData } from '#scripts/git/schemas'
4
+ import { epic_parse, type ExternalChild } from './epic-parse'
5
5
 
6
6
  // The comment the epic auto-close announces itself with, and how a run recognizes the one a previous
7
7
  // run already posted.
8
8
  //
9
- // Kept apart from `git-epic-close.ts` because the two answer different questions — that file decides
9
+ // Kept apart from `epic-close.ts` because the two answer different questions — that file decides
10
10
  // *whether* an epic may close, this one decides what the announcement says and whether it is already
11
11
  // there — and because the file it was split from is within twenty lines of the three hundred a file
12
12
  // may hold (joshuafolkken/kit#1039).
@@ -131,7 +131,7 @@ function is_legacy_announcement(body: string): boolean {
131
131
  // both: a listing that could not be read says nothing about what is on the issue, and reporting it
132
132
  // as "nothing is there" would have the run state a fact it never established — the misread
133
133
  // joshuafolkken/kit#973 and joshuafolkken/kit#959 were each about. Keeping it apart is what lets
134
- // `git-epic-close.ts` say out loud that the duplicate check did not run, which is the whole
134
+ // `epic-close.ts` say out loud that the duplicate check did not run, which is the whole
135
135
  // difference between the two answers there.
136
136
  //
137
137
  // **Both of `read_json_listing`'s gaps arrive as `unreadable`**, rows the schema rejects included.
@@ -147,7 +147,7 @@ type CloseCommentState = 'present' | 'absent' | 'unreadable'
147
147
  // verbatim — a fenced one caught by the mask, an indented one by the indent check. `fence_mask` is the same walk every other body predicate
148
148
  // in this codebase reads through, rather than a second one written here (joshuafolkken/kit#890).
149
149
  function has_marker(body: string, children: ReadonlyArray<string>): boolean {
150
- const mask = git_epic_parse.fence_mask(body)
150
+ const mask = epic_parse.fence_mask(body)
151
151
 
152
152
  return body.split('\n').some((line, index) => {
153
153
  if (mask[index] !== true) return false
@@ -190,10 +190,10 @@ async function read_close_comment_state(
190
190
  return has_announcement(listing.rows, child_references(epic)) ? 'present' : 'absent'
191
191
  }
192
192
 
193
- const git_epic_close_comment = {
193
+ const epic_close_comment = {
194
194
  build_close_comment,
195
195
  read_close_comment_state,
196
196
  }
197
197
 
198
198
  export type { CloseCommentState, EpicChildren }
199
- export { git_epic_close_comment, CLOSE_ANNOUNCEMENT }
199
+ export { epic_close_comment, CLOSE_ANNOUNCEMENT }
@@ -1,10 +1,14 @@
1
- import { git_epic_close_comment } from './git-epic-close-comment'
2
- import { git_epic_parse, type ExternalChild } from './git-epic-parse'
3
- import { git_gh_command } from './git-gh-command'
4
- import { EPIC_LABEL } from './issue-labels'
5
- import { cutoff_cause, cutoff_of, type ScanCutoff } from './listing-cutoff'
6
- import { parse_json_array_or_undefined, parse_json_object_safe } from './parse-json-array'
7
- import { epic_child_schema, epic_issue_schema, type EpicChildData } from './schemas'
1
+ import { git_gh_command } from '#scripts/git/git-gh-command'
2
+ import { EPIC_LABEL } from '#scripts/git/issue-labels'
3
+ import { cutoff_cause, cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
4
+ import {
5
+ parse_json_array_or_undefined,
6
+ parse_json_object_safe,
7
+ } from '#scripts/git/parse-json-array'
8
+ import { epic_child_schema, epic_issue_schema, type EpicChildData } from '#scripts/git/schemas'
9
+ import { error_text } from '#scripts/lib/error-message'
10
+ import { epic_close_comment } from './epic-close-comment'
11
+ import { epic_parse, type ExternalChild } from './epic-parse'
8
12
 
9
13
  // How many open epics the auto-close will look at. `git-gh-issue-list.ts` pages until `limit` rows
10
14
  // have been selected or the backlog runs out, so this is what keeps the ordinary case at exactly one
@@ -44,9 +48,9 @@ interface SiblingState {
44
48
  function to_epic_issue(raw: { number: number; body?: string | undefined }): EpicIssue {
45
49
  return {
46
50
  number: raw.number,
47
- children: git_epic_parse.parse_task_list_issue_numbers(raw.body),
48
- external_children: git_epic_parse.parse_external_task_list_children(raw.body),
49
- has_declared_order: git_epic_parse.has_declared_dependency_chain(raw.body),
51
+ children: epic_parse.parse_task_list_issue_numbers(raw.body),
52
+ external_children: epic_parse.parse_external_task_list_children(raw.body),
53
+ has_declared_order: epic_parse.has_declared_dependency_chain(raw.body),
50
54
  }
51
55
  }
52
56
 
@@ -91,7 +95,7 @@ function to_sibling_state(raw_json: string | undefined): SiblingState {
91
95
  const child = parse_child(raw_json)
92
96
 
93
97
  return {
94
- is_closed: git_epic_parse.is_state_closed(child?.state),
98
+ is_closed: epic_parse.is_state_closed(child?.state),
95
99
  has_blocked_by: has_blocked_by(child),
96
100
  }
97
101
  }
@@ -124,7 +128,7 @@ async function inspect_siblings(
124
128
  }
125
129
 
126
130
  // `pnpm josh epic --ordered` and `--add` record the declared chain themselves, through the REST
127
- // dependencies endpoint (`git-epic-relations.ts`, joshuafolkken/kit#1026), so the default route
131
+ // dependencies endpoint (`epic-relations.ts`, joshuafolkken/kit#1026), so the default route
128
132
  // arrives here with the relations already in place. Three routes still reach a declared order with
129
133
  // none recorded: an epic body written by hand, the manual fallback in `issue-template.md`, and
130
134
  // `apply_relations` failing — it counts the refusals, reports that the body is intact and lets the
@@ -200,11 +204,11 @@ async function close_epic_with(epic_number: string, comment: string | undefined)
200
204
  // `absent` and one of `unreadable` both post it, and the second says so.
201
205
  async function close_epic(epic: EpicIssue): Promise<boolean> {
202
206
  const epic_number = String(epic.number)
203
- const state = await git_epic_close_comment.read_close_comment_state(epic_number, epic)
207
+ const state = await epic_close_comment.read_close_comment_state(epic_number, epic)
204
208
 
205
209
  if (state === 'unreadable') console.info(`ℹ️ Epic #${epic_number}${UNREADABLE_COMMENTS_NOTE}`)
206
210
 
207
- const comment = state === 'present' ? undefined : git_epic_close_comment.build_close_comment(epic)
211
+ const comment = state === 'present' ? undefined : epic_close_comment.build_close_comment(epic)
208
212
 
209
213
  return await close_epic_with(epic_number, comment)
210
214
  }
@@ -256,7 +260,7 @@ async function close_epic_isolated(epic: EpicIssue, merged_number: number): Prom
256
260
  try {
257
261
  return await close_epic_when_complete(epic, merged_number)
258
262
  } catch (error) {
259
- const message = error instanceof Error ? error.message : String(error)
263
+ const message = error_text.message_of(error)
260
264
 
261
265
  console.info(`⚠️ Skipped epic #${String(epic.number)}: ${message}`)
262
266
 
@@ -265,7 +269,7 @@ async function close_epic_isolated(epic: EpicIssue, merged_number: number): Prom
265
269
  }
266
270
 
267
271
  function parents_of(open_epics: ReadonlyArray<EpicIssue>, child_number: number): Array<EpicIssue> {
268
- return open_epics.filter((epic) => git_epic_parse.has_child(epic.children, child_number))
272
+ return open_epics.filter((epic) => epic_parse.has_child(epic.children, child_number))
269
273
  }
270
274
 
271
275
  // Closes one epic unless a cycle or a shared child already reached it — the closed number, or
@@ -351,7 +355,7 @@ async function resolve_and_close_safely(merged_number: number): Promise<void> {
351
355
  try {
352
356
  await resolve_and_close(merged_number)
353
357
  } catch (error) {
354
- const message = error instanceof Error ? error.message : String(error)
358
+ const message = error_text.message_of(error)
355
359
 
356
360
  console.info(`⚠️ Skipped the epic auto-close check: ${message}`)
357
361
  }
@@ -376,12 +380,12 @@ async function close_completed_epics(input: {
376
380
  await resolve_and_close_safely(merged_number)
377
381
  }
378
382
 
379
- const git_epic_close = {
383
+ const epic_close = {
380
384
  close_completed_epics,
381
385
  }
382
386
 
383
387
  export {
384
- git_epic_close,
388
+ epic_close,
385
389
  close_completed_epics,
386
390
  truncated_epic_list_note,
387
391
  EPIC_LIST_LIMIT,
@@ -4,7 +4,6 @@ import { git_gh_api_path } from '#scripts/git/git-gh-api-path'
4
4
  import { git_gh_exec } from '#scripts/git/git-gh-exec'
5
5
  import { propagate_publish } from '#scripts/propagate/propagate-publish'
6
6
  import { derive_versions_endpoint } from '#scripts/version/version-command-config'
7
- import { execaSync } from 'execa'
8
7
  import { load } from 'js-yaml'
9
8
  import type { DependencyVerdict } from './epic-classify'
10
9
  import type { EpicChild } from './epic-graph'
@@ -71,7 +70,6 @@ function is_published(repo: string, version: string): boolean {
71
70
  }
72
71
 
73
72
  const GH_TIMEOUT_MS = 20_000
74
- const SUCCESS_EXIT_CODE = 0
75
73
  // The manifest arrives base64-encoded in the API's `content` field; `jq` decodes and reads it.
76
74
  const VERSION_JQ = '.content | @base64d | fromjson | {version, private, workspaces} | tojson'
77
75
  const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'
@@ -147,24 +145,20 @@ function pnpm_workspace_path(repo: string): string {
147
145
 
148
146
  // A read that failed for any reason other than a missing file leaves the layout unknown.
149
147
  function classify_workspace_failure(repo: string): WorkspaceCheck {
150
- const probe = execaSync('gh', ['api', '--include', '--silent', pnpm_workspace_path(repo)], {
151
- ...git_gh_exec.direct_environment(),
152
- reject: false,
153
- timeout: GH_TIMEOUT_MS,
154
- })
148
+ const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo), GH_TIMEOUT_MS)
155
149
 
156
- return git_gh_exec.parse_status_line(probe.stdout) === NOT_FOUND_STATUS ? 'single' : 'unreadable'
150
+ return status === NOT_FOUND_STATUS ? 'single' : 'unreadable'
157
151
  }
158
152
 
159
153
  function read_pnpm_workspace(repo: string): WorkspaceCheck {
160
- const result = execaSync('gh', ['api', pnpm_workspace_path(repo), '--jq', WORKSPACE_JQ], {
161
- ...git_gh_exec.direct_environment(),
162
- reject: false,
163
- timeout: GH_TIMEOUT_MS,
154
+ const stdout = git_gh_exec.read_gh_api_sync({
155
+ path: pnpm_workspace_path(repo),
156
+ jq_filter: WORKSPACE_JQ,
157
+ timeout_ms: GH_TIMEOUT_MS,
164
158
  })
165
- if (result.exitCode !== SUCCESS_EXIT_CODE) return classify_workspace_failure(repo)
159
+ if (stdout === undefined) return classify_workspace_failure(repo)
166
160
 
167
- return declares_packages(result.stdout) ? 'workspace' : 'single'
161
+ return declares_packages(stdout) ? 'workspace' : 'single'
168
162
  }
169
163
 
170
164
  // Whether the repository declares a workspace. `workspaces` covers npm and yarn; pnpm keeps its
@@ -227,38 +221,27 @@ function to_manifest(repo: string, stdout: string): ManifestAnswer {
227
221
  }
228
222
 
229
223
  // Why the read failed, asked only when it did. The status line is the protocol rather than prose, so
230
- // a missing manifest is told from a rate limit by matching a contract instead of a message — through
231
- // `git_gh_exec.parse_status_line` rather than a second copy of it.
224
+ // a missing manifest is told from a rate limit by matching a contract instead of a message.
232
225
  //
233
- // `git_gh_exec.exec_gh_api_status` asks the same question and is **not** reused: it is async, and
234
- // every caller down to `resolve_cross_repo` is synchronous. What is worth sharing is the parser,
235
- // which is shared; the spawn is four lines and duplicating it costs less than turning the
236
- // classifier, the resolver and `epic_classify` async for one probe. A 404 here is trustworthy in a way
237
- // a registry 404 is not: the repository's issues are already being read, so access is established and
238
- // what is missing is the file.
226
+ // The probe is `exec_gh_api_status_sync`, the synchronous twin of `exec_gh_api_status`: every caller
227
+ // down to `resolve_cross_repo` is synchronous, and turning the classifier, the resolver and
228
+ // `epic_classify` async for one probe would cost more than the twin (joshuafolkken/kit#2901). A 404
229
+ // here is trustworthy in a way a registry 404 is not: the repository's issues are already being read,
230
+ // so access is established and what is missing is the file.
239
231
  function classify_manifest_failure(repo: string): ManifestAnswer {
240
- const probe = execaSync('gh', ['api', '--include', '--silent', manifest_path(repo)], {
241
- ...git_gh_exec.direct_environment(),
242
- reject: false,
243
- timeout: GH_TIMEOUT_MS,
244
- })
232
+ const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo), GH_TIMEOUT_MS)
245
233
 
246
- return {
247
- kind:
248
- git_gh_exec.parse_status_line(probe.stdout) === NOT_FOUND_STATUS ? 'absent' : 'unreadable',
249
- }
234
+ return { kind: status === NOT_FOUND_STATUS ? 'absent' : 'unreadable' }
250
235
  }
251
236
 
252
237
  function fetch_manifest(repo: string): ManifestAnswer {
253
- const result = execaSync('gh', ['api', manifest_path(repo), '--jq', VERSION_JQ], {
254
- ...git_gh_exec.direct_environment(),
255
- reject: false,
256
- timeout: GH_TIMEOUT_MS,
238
+ const stdout = git_gh_exec.read_gh_api_sync({
239
+ path: manifest_path(repo),
240
+ jq_filter: VERSION_JQ,
241
+ timeout_ms: GH_TIMEOUT_MS,
257
242
  })
258
243
 
259
- return result.exitCode === SUCCESS_EXIT_CODE
260
- ? to_manifest(repo, result.stdout)
261
- : classify_manifest_failure(repo)
244
+ return stdout === undefined ? classify_manifest_failure(repo) : to_manifest(repo, stdout)
262
245
  }
263
246
 
264
247
  // Read once per pass, for the reason `published_cache` holds the other half: every edge to one
@@ -1,6 +1,6 @@
1
- import { git_epic_parse, type DependencyLink } from './git-epic-parse'
2
- import { format_replaced_relations } from './git-epic-reference'
3
- import { git_epic_sections, type BodyLines, type SectionRange } from './git-epic-sections'
1
+ import { epic_parse, type DependencyLink } from './epic-parse'
2
+ import { format_replaced_relations } from './epic-reference'
3
+ import { epic_sections, type BodyLines, type SectionRange } from './epic-sections'
4
4
 
5
5
  // Placing a decision record inside an epic body's `## Decisions` section.
6
6
  //
@@ -45,10 +45,8 @@ function to_declaration_error(line_number: number): string {
45
45
  function find_decision_error(record: string): string | undefined {
46
46
  if (record.trim().length === 0) return EMPTY_RECORD
47
47
 
48
- const input = git_epic_sections.to_body_lines(record)
49
- const [found] = git_epic_sections.find_indices(input, (line) =>
50
- git_epic_parse.is_declaration_line(line),
51
- )
48
+ const input = epic_sections.to_body_lines(record)
49
+ const [found] = epic_sections.find_indices(input, (line) => epic_parse.is_declaration_line(line))
52
50
 
53
51
  return found === undefined ? undefined : to_declaration_error(found + FIRST_LINE_NUMBER)
54
52
  }
@@ -81,8 +79,8 @@ function last_content_index(input: BodyLines, range: SectionRange): number {
81
79
  // The section's *end* rather than its start: the entries are a log, and a reader looking for the most
82
80
  // recent decision reads down. Nothing else in the body moves.
83
81
  function append_decision(body: string, record: string): string {
84
- const input = git_epic_sections.to_body_lines(body)
85
- const range = git_epic_sections.find_section_range(input, DECISIONS_HEADING_PATTERN)
82
+ const input = epic_sections.to_body_lines(body)
83
+ const range = epic_sections.find_section_range(input, DECISIONS_HEADING_PATTERN)
86
84
  if (range === undefined) return to_new_section(input.lines, record).join('\n')
87
85
 
88
86
  const at = last_content_index(input, range)
@@ -129,13 +127,13 @@ function format_decision_report(input: { total: number; failures: number }): str
129
127
  }
130
128
 
131
129
  // The heading a creation writes its reasoning under. Beside a pattern exactly as `DECISIONS_HEADING`
132
- // is, and tied to `git_epic_body`'s own literal by a test that reads a body the builder produced
130
+ // is, and tied to `epic_body`'s own literal by a test that reads a body the builder produced
133
131
  // rather than by a string comparison (joshuafolkken/kit#1712).
134
132
  const RATIONALE_HEADING_PATTERN = /^#{1,6}[ \t]+Split rationale\b/u
135
133
 
136
134
  function read_section(body: string, heading: RegExp): string {
137
- const input = git_epic_sections.to_body_lines(body)
138
- const range = git_epic_sections.find_section_range(input, heading)
135
+ const input = epic_sections.to_body_lines(body)
136
+ const range = epic_sections.find_section_range(input, heading)
139
137
  if (range === undefined) return ''
140
138
 
141
139
  return input.lines.slice(range.start, range.end).join('\n')
@@ -159,7 +157,7 @@ function read_recorded_reasons(body: string | undefined): string {
159
157
  ].join('\n')
160
158
  }
161
159
 
162
- const git_epic_decision = {
160
+ const epic_decision = {
163
161
  DECISIONS_HEADING,
164
162
  append_decision,
165
163
  append_replacements,
@@ -168,4 +166,4 @@ const git_epic_decision = {
168
166
  format_decision_report,
169
167
  }
170
168
 
171
- export { git_epic_decision }
169
+ export { epic_decision }
@@ -1,9 +1,9 @@
1
- import { git_epic_parse, type ExternalChild } from '#scripts/git/git-epic-parse'
2
1
  import { git_gh_command } from '#scripts/git/git-gh-command'
3
2
  import type { IssueReadFailure } from '#scripts/git/git-gh-issue-read'
4
3
  import { epic_cross_repo } from './epic-cross-repo'
5
4
  import type { EpicChild, IssueReference } from './epic-graph'
6
5
  import { epic_issue, type EpicIssue } from './epic-issue'
6
+ import { epic_parse, type ExternalChild } from './epic-parse'
7
7
  import { epic_relation_recheck } from './epic-relation-recheck'
8
8
 
9
9
  // Reading an epic and its children from GitHub.
@@ -73,8 +73,8 @@ async function fetch_epic_body(epic_number: number, scope?: string): Promise<Epi
73
73
  return {
74
74
  body,
75
75
  body_failure: read.kind === 'read' ? undefined : read,
76
- child_numbers: git_epic_parse.parse_task_list_issue_numbers(body),
77
- external: git_epic_parse.parse_external_task_list_children(body),
76
+ child_numbers: epic_parse.parse_task_list_issue_numbers(body),
77
+ external: epic_parse.parse_external_task_list_children(body),
78
78
  }
79
79
  }
80
80
 
@@ -1,10 +1,10 @@
1
- import type { DependencyLink } from '#scripts/git/git-epic-parse'
1
+ import type { DependencyLink } from './epic-parse'
2
2
  import {
3
3
  format_dependency_link,
4
4
  join_references,
5
5
  to_issue_reference,
6
6
  type IssueReference,
7
- } from '#scripts/git/git-epic-reference'
7
+ } from './epic-reference'
8
8
 
9
9
  // The dependency graph an epic's children form, and the two ways it can be wrong.
10
10
  //
@@ -275,5 +275,5 @@ const epic_graph = {
275
275
  }
276
276
 
277
277
  export type { EpicChild, GraphAnomaly, GraphAnomalyKind }
278
- export type { IssueReference } from '#scripts/git/git-epic-reference'
278
+ export type { IssueReference } from './epic-reference'
279
279
  export { epic_graph }
@@ -1,10 +1,10 @@
1
- import { git_epic_parse } from '#scripts/git/git-epic-parse'
2
1
  import { git_gh_command } from '#scripts/git/git-gh-command'
3
2
  import { EPIC_LABEL, has_any_label } from '#scripts/git/issue-labels'
4
3
  import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
5
4
  import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
6
5
  import type { OpenIssueData } from '#scripts/git/schemas'
7
6
  import { z } from 'zod'
7
+ import { epic_parse } from './epic-parse'
8
8
 
9
9
  // Which epic tracks which issue — the one answer, in one place (joshuafolkken/kit#1633).
10
10
  //
@@ -40,7 +40,7 @@ function build_tracking_index(
40
40
  const index = new Map<number, ReadonlyArray<number>>()
41
41
 
42
42
  for (const epic of epics) {
43
- for (const child of git_epic_parse.parse_task_list_issue_numbers(epic.body)) {
43
+ for (const child of epic_parse.parse_task_list_issue_numbers(epic.body)) {
44
44
  index.set(child, [...(index.get(child) ?? []), epic.number])
45
45
  }
46
46
  }
@@ -1,8 +1,8 @@
1
- import type { IssueReference } from '#scripts/git/git-epic-reference'
2
1
  import { git_gh_issue_rest } from '#scripts/git/git-gh-issue-rest'
3
2
  import { parse_json_object_safe } from '#scripts/git/parse-json-array'
4
3
  import { blocked_by_schema } from '#scripts/git/schemas'
5
4
  import { z } from 'zod'
5
+ import type { IssueReference } from './epic-reference'
6
6
 
7
7
  // The shapes one issue is read as, and the argument every epic command takes.
8
8
  //
@@ -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 }