@joshuafolkken/kit 1.1005.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 -58
  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 +32 -6
  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 +23 -14
  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,13 +0,0 @@
1
- ## エージェント規則の単一ソースは `CLAUDE.md`(`AGENTS.md` / `GEMINI.md` は導線)
2
-
3
- **規則の本体は `CLAUDE.md` にしか無い。** `AGENTS.md` と `GEMINI.md` は「規則は `CLAUDE.md` にある。作業前に全文を読むこと」だけを書いた短い導線文書であり、規則を 1 行も持たない(joshuafolkken/kit#963)。
4
-
5
- - **規則の追加・仕様変更・文言修正・節の追加は `CLAUDE.md` に 1 回だけ書く。** 他の 2 本には触れない
6
- - **導線文書に規則を書き戻さない。** そこに書かれた規則は、次の改訂で更新し忘れる 4 つ目の置き場になる
7
- - `scripts/document/ai-document-pointers.test.ts` が機械的に守る。導線文書に規則本体が再出現した場合、導線の一文が消えた場合、`CLAUDE.md` が単一ソースであることの記述が消えた場合に落ちる
8
-
9
- **なぜこうなったか。** 3 本はほぼ同一の複製で、規則を 1 つ変えるたびに 3 回書いて 3 回レビューする必要があり、しかも 3 本とも常駐上限まで 1KB を切っていた。これは `CLAUDE.md` 自身が「No clones — single-source」で禁じているクローンそのものであり、禁止を書いている文書がその禁止に違反している状態だった。
10
-
11
- **他ツールでの検証は行っていない。** Codex / Cursor / Gemini CLI が導線を辿るかは未確認で、現在 Claude しか使っていないという判断による(joshuafolkken/kit#970 の `## Decisions`)。導線を辿らないツールを使い始める時点で、そのツールについて別途扱う。
12
-
13
- - このルールは横断ドキュメント(CLAUDE.md「Doc Sync Rules」)のカノニカル参照
@@ -1,25 +0,0 @@
1
- // The two `sh` fragments both unit-suite shims are built from. Single-sourced because a guard's own
2
- // defects fail **open**: a path that lost its quoting leaves the shim a syntax error that records
3
- // nothing, and a `printf` that lost its `>>` truncates the record every call — in either case the
4
- // run is reported clean while the calls keep going out. One spelling, used by every generator, is
5
- // what keeps that impossible to get wrong in only one of them.
6
-
7
- const SINGLE_QUOTE = "'"
8
-
9
- // One `sh` word, quoted. A checkout under `/Users/o'brien` is not exotic, and an unquoted path there
10
- // would close the quote and leave the rest of the line as syntax.
11
- function quoted(value: string): string {
12
- // `sh` has no escape inside single quotes, so an apostrophe is written by closing the quote,
13
- // emitting an escaped one, and opening a new quote: `'\''`.
14
- return SINGLE_QUOTE + value.replaceAll("'", String.raw`'\''`) + SINGLE_QUOTE
15
- }
16
-
17
- // Append one line of prose to the shared record. `>>` rather than `>`, because every worker of the
18
- // run writes to the same file and the last one must not erase what the others found.
19
- function record_line(log_file: string, recorded: string): string {
20
- return String.raw`printf '%s\n' "${recorded}" >> ${quoted(log_file)}`
21
- }
22
-
23
- const shim_shell = { quoted, record_line }
24
-
25
- export { shim_shell }
@@ -1,37 +0,0 @@
1
- // Where a skill lives and how its frontmatter is read (joshuafolkken/kit#854, single-sourced for
2
- // joshuafolkken/kit#1151).
3
- //
4
- // The suites that assert a skill ships correctly and the `josh cost` resident breakdown that sizes
5
- // the skills index both need the same two facts: which directory holds the skills, and what the
6
- // entry file's frontmatter says. This module is where those live, so the pattern is not written
7
- // twice — `skill-fixture.ts` is a test fixture and is excluded from the published package, so
8
- // shipped code cannot read it.
9
-
10
- const SKILL_ROOT = '.claude/skills'
11
- const SKILL_ENTRY_FILE = 'SKILL.md'
12
- const FRONTMATTER_PATTERN = /^---\n([\S\s]*?)\n---\n/u
13
- const DESCRIPTION_PATTERN = /^description: (.+)$/mu
14
-
15
- function has_frontmatter(content: string): boolean {
16
- return FRONTMATTER_PATTERN.test(content)
17
- }
18
-
19
- function frontmatter_of(content: string): string {
20
- return FRONTMATTER_PATTERN.exec(content)?.[1] ?? ''
21
- }
22
-
23
- // The description is the trigger for every agent that discovers a skill by reading descriptions
24
- // rather than by being told a path, so it is read out rather than merely asserted present.
25
- function description_of(content: string): string {
26
- return DESCRIPTION_PATTERN.exec(frontmatter_of(content))?.[1] ?? ''
27
- }
28
-
29
- const skill_meta = {
30
- SKILL_ROOT,
31
- SKILL_ENTRY_FILE,
32
- has_frontmatter,
33
- frontmatter_of,
34
- description_of,
35
- }
36
-
37
- export { skill_meta }
@@ -1,108 +0,0 @@
1
- // Pulling machine-checkable references out of the distributed documents, so a few structural scans
2
- // can replace the ~140 per-phrase marker suites the documents used to carry (joshuafolkken/kit#1923).
3
- // Every function here is a pure string extractor: it takes document text and returns the references
4
- // it names, which is what lets the scans that call it be checked on a synthetic string with a
5
- // deliberately broken reference in it.
6
-
7
- // A command reference, only inside a code span. `` `pnpm josh gate` `` and `` `josh review:brief` ``
8
- // are the copy-and-paste form; the leading backtick keeps prose like "josh will decide" out, which
9
- // would otherwise read "will" as a command name.
10
- const COMMAND_PATTERN = /`(?:pnpm )?josh ([a-z][a-z0-9:-]+)/gu
11
-
12
- // A label the documents operate on — applied (`labels[]=…`), removed (`…/labels/<name>`) or created
13
- // (`labels -f name=…`). Prose that merely names a label is not scanned; a mistyped name only breaks
14
- // where a command carries it.
15
- const LABEL_PATTERN = /(?:labels\[\]=|\/labels\/|labels -f name=)([a-z][a-z0-9:_-]*)/gu
16
-
17
- // A section reference: a markdown file in a code span, an arrow, and the cited heading in straight or
18
- // Japanese quotes — `` `backlogrun-progress.md` → "The hand-off" ``.
19
- const SECTION_PATTERN = /`([\w./-]+\.md)`\s*→\s*["「]([^"」]+)["」]/gu
20
-
21
- // A relative markdown link target, with an optional `#anchor`. External `https://…` links carry no
22
- // `.md`, so the pattern never matches them.
23
- const LINK_PATTERN = /\]\(([\w./-]+\.md)(?:#[\w-]+)?\)/gu
24
-
25
- // Filenames that appear in the documents as illustrations of the reference form itself, never as real
26
- // targets — the doc-section explanation cites `` `path.md` → "Heading" `` and `` `X.md` → "Heading" ``.
27
- const PLACEHOLDER_FILES: ReadonlySet<string> = new Set(['x.md', 'foo.md', 'file.md', 'path.md'])
28
-
29
- function matches(text: string, pattern: RegExp): Array<RegExpMatchArray> {
30
- return [...text.matchAll(pattern)]
31
- }
32
-
33
- // A trailing colon is a placeholder for the sub-command that follows — `` `josh epic:` `` stands for
34
- // `epic:audit`, `epic:next` and the rest — so it names no command of its own.
35
- function command_references(text: string): Array<string> {
36
- return matches(text, COMMAND_PATTERN)
37
- .map((match) => match[1] ?? '')
38
- .filter((name) => !name.endsWith(':'))
39
- }
40
-
41
- // A `<placeholder>` inside a command, and `depth:<n>` after the char class stops at `<`, both leave a
42
- // token that is not a real label. Real labels never end in a colon and never carry angle brackets.
43
- function is_real_label(token: string): boolean {
44
- return token.length > 0 && !token.endsWith(':') && !token.includes('<')
45
- }
46
-
47
- function label_references(text: string): Array<string> {
48
- return matches(text, LABEL_PATTERN)
49
- .map((match) => match[1] ?? '')
50
- .filter((token) => is_real_label(token))
51
- }
52
-
53
- function base_name(path: string): string {
54
- return path.split('/').at(-1) ?? path
55
- }
56
-
57
- function is_placeholder_file(name: string): boolean {
58
- return name.includes('<') || PLACEHOLDER_FILES.has(base_name(name).toLowerCase())
59
- }
60
-
61
- interface SectionReference {
62
- file: string
63
- heading: string
64
- }
65
-
66
- function section_references(text: string): Array<SectionReference> {
67
- return matches(text, SECTION_PATTERN)
68
- .map(([, file, heading]) => ({ file: file ?? '', heading: heading ?? '' }))
69
- .filter((reference) => !is_placeholder_file(reference.file))
70
- }
71
-
72
- function link_targets(text: string): Array<string> {
73
- return matches(text, LINK_PATTERN)
74
- .map((match) => match[1] ?? '')
75
- .filter((file) => !is_placeholder_file(file))
76
- }
77
-
78
- // A cited heading and the heading it names are compared with backticks dropped and whitespace
79
- // collapsed, because a reference wraps at the column the prose wraps at — sometimes across a
80
- // blockquote `> ` continuation — while the heading it points at is one unbroken line.
81
- function normalize_reference(heading: string): string {
82
- return heading
83
- .replaceAll('`', '')
84
- .split(/\s+/u)
85
- .filter((word) => word !== '>')
86
- .join(' ')
87
- }
88
-
89
- // The two forms a heading can be cited by: with its leading number (`2b. Delegating…`) and without
90
- // (`Delegating…`), since a document's own numbering is a prefix a reference may or may not carry.
91
- function reference_forms(title: string): Array<string> {
92
- const normalized = normalize_reference(title)
93
-
94
- return [normalized, normalized.replace(/^\d+[a-z]?\. /u, '')]
95
- }
96
-
97
- const document_scan = {
98
- command_references,
99
- is_placeholder_file,
100
- label_references,
101
- link_targets,
102
- normalize_reference,
103
- reference_forms,
104
- section_references,
105
- }
106
-
107
- export type { SectionReference }
108
- export { document_scan }
@@ -1,69 +0,0 @@
1
- import { writeFileSync } from 'node:fs'
2
- import { fileURLToPath } from 'node:url'
3
- import {
4
- ALIASES,
5
- CATEGORY_ORDER,
6
- COMMAND_MAP,
7
- type CommandCategory,
8
- type CommandEntry,
9
- } from '#scripts/josh/josh-command-map'
10
-
11
- const CATALOG_FILE = fileURLToPath(new URL('../../docs/josh-command-catalog.md', import.meta.url))
12
-
13
- const PREAMBLE = `# josh CLI — Command Catalog
14
-
15
- Auto-generated — do not edit.
16
- Run \`tsx scripts/document/generate-catalog.ts\` to regenerate.
17
-
18
- `
19
-
20
- function aliases_for(command_name: string): ReadonlyArray<string> {
21
- return Object.entries(ALIASES)
22
- .filter(([, target]) => target === command_name)
23
- .map(([alias]) => alias)
24
- .toSorted((x, y) => x.localeCompare(y))
25
- }
26
-
27
- function format_alias_heading(name: string, aliases: ReadonlyArray<string>): string {
28
- if (aliases.length === 0) return `### \`josh ${name}\``
29
- const alias_spans = aliases.map((al) => `\`josh ${al}\``).join(' · ')
30
-
31
- return `### \`josh ${name}\` · ${alias_spans}`
32
- }
33
-
34
- function format_command_entry(name: string, entry: CommandEntry): string {
35
- const [synopsis, audience, side_effects] = entry.reference
36
- const aliases = aliases_for(name)
37
- const kit_note = entry.is_kit_only === true ? ' · **kit only**' : ''
38
- const heading = format_alias_heading(name, aliases)
39
- const meta = `> **Audience:** ${audience} · **Side effects:** ${side_effects.join(', ')}${kit_note}`
40
- const synopsis_line = synopsis ? `\`${synopsis}\`` : '_No arguments._'
41
-
42
- return [heading, '', meta, '', synopsis_line, '', entry.description].join('\n')
43
- }
44
-
45
- function entries_for_category(category: CommandCategory): Array<[string, CommandEntry]> {
46
- return Object.entries(COMMAND_MAP)
47
- .filter(([, entry]) => entry.category === category)
48
- .toSorted(([name_a], [name_b]) => name_a.localeCompare(name_b))
49
- }
50
-
51
- function generate_category_section(category: CommandCategory): string {
52
- const entries = entries_for_category(category)
53
- if (entries.length === 0) return ''
54
- const command_blocks = entries.map(([name, entry]) => format_command_entry(name, entry))
55
-
56
- return `## ${category}\n\n${command_blocks.join('\n\n---\n\n')}\n\n`
57
- }
58
-
59
- function generate_catalog(): string {
60
- const content = PREAMBLE + CATEGORY_ORDER.map((cat) => generate_category_section(cat)).join('')
61
-
62
- return `${content.trimEnd()}\n`
63
- }
64
-
65
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
66
- writeFileSync(CATALOG_FILE, generate_catalog())
67
- }
68
-
69
- export { CATALOG_FILE, generate_catalog }
@@ -1,143 +0,0 @@
1
- // Phrases retracted from the distributed documents, kept so a clone or a reverted decision cannot
2
- // creep back unnoticed (joshuafolkken/kit#1923). This is the small negative table that replaced the
3
- // ~140 scattered `not.toContain` assertions the per-rule marker suites carried: each phrase is
4
- // checked absent from the whole document corpus at once by `document-markers.test.ts`.
5
- //
6
- // Whitespace is collapsed on both sides before the comparison, so a phrase that the formatter would
7
- // wrap across a line break is still caught. A phrase here must stay genuinely gone everywhere; if one
8
- // is ever a legitimate part of a new document, remove its row rather than scoping it.
9
- const RETIRED_PHRASES: ReadonlyArray<string> = [
10
- '"Implemented <title>:',
11
- '"dev": "vite dev --port $(josh port dev)',
12
- '"dev": "vite dev --port $(pnpm josh port dev)',
13
- '"preview": "wrangler dev --port $(josh port preview)',
14
- '"preview": "wrangler dev --port $(pnpm josh port preview)',
15
- '"preview:stop": "kill-port $(josh port preview)',
16
- '"preview:stop": "kill-port $(pnpm josh port preview)',
17
- '### セッション対話の言語(`JOSH_SESSION_LANG`)',
18
- '**2 つ目を作ってはならない。**',
19
- '**2 件目以降で `--exclude` を省略してはならない。**',
20
- '**EPIC 作成へ落ちてはならない。**',
21
- '**`#N` が宣言された順序に載っていなくても構わない。**',
22
- '**`--ordered` は必須であって好みの問題ではない。**',
23
- '**`-u` は省略できない。**',
24
- '**■ Overview (plain language — always first)**',
25
- '**書き換えられた lock ファイルは最初の子と一緒に入る。**',
26
- '--- 技術詳細 ---',
27
- '2 件以上に分割したら常に epic を作る。件数の閾値も、実行順序の有無による分岐も無い',
28
- '3-6 line work summary',
29
- '3–6 line work summary',
30
- 'Artifact languages are unaffected',
31
- 'Ask the user to run `pnpm josh test` and share the output',
32
- 'CI only runs the suite it can find, so specs stay under',
33
- 'CI status check indicates a managed config file was updated',
34
- "Cross-package problems → interrupt with a new Issue, don't patch locally.",
35
- 'EPIC 全体で 1 回の質問',
36
- 'Fix in place and re-run `/code-review` at the level `pnpm josh review:level` prints on `git diff main`',
37
- 'Implement + PR',
38
- 'Invoking `halfrun` authorizes commit, push, and PR creation',
39
- 'Issue / PR / Telegram の言語は英語のまま変更なし',
40
- 'Issue plan comments MUST be written in English',
41
- 'Issue 本文に「`halfrun` で実行すること」と書いても強制力が無く',
42
- 'Neither toolkit carries `shot` yet',
43
- 'No toolkit ships that command yet',
44
- 'Plan comments MUST be in English',
45
- 'Poll at the polling interval; ask',
46
- 'Post the agreed plan in English',
47
- 'Post the plan in English',
48
- 'Print the headings exactly as written',
49
- 'Re-run after applying fixes until **no high or medium findings remain**.',
50
- 'The recommended default when in doubt is the root-cause interrupt',
51
- 'The user runs `pnpm josh test` and shares the full output',
52
- String.raw`Today that\n> is every consumer`,
53
- 'Typing `halfrun` approved implementing and merging',
54
- 'Verify **every** rule below. These are non-standard, so call out any violation.',
55
- 'What still waits for a person, today',
56
- '`into owner/repo#N` は正しい書式である',
57
- '`queue` との違いはひとつだけである',
58
- 'about 60 characters in Japanese, 15 words in English',
59
- 'artifact languages (Issue / PR / Telegram = English) are unchanged',
60
- 'ask them to run **`pnpm josh test`** locally',
61
- 'blocker を見る前に',
62
- 'chain-rule decision table in \\',
63
- 'code-reviewer',
64
- 'compose an implementation summary in English',
65
- 'exactly what it was) → `pnpm josh git -y` →',
66
- 'exists in neither toolkit yet',
67
- 'fix them in place and re-run `/code-review` at the level `pnpm josh review:level` prints. Nothing is committed yet',
68
- 'fresh-context',
69
- 'fresh-context subagent',
70
- 'gh issue list --label review-round2-skipped',
71
- 'including commit, push, PR creation',
72
- 'is a separate deliverable',
73
- "it asks only after a child that ran in the parent's own context",
74
- 'its own `pnpm josh gate` join',
75
- 'join it, and `pnpm josh git -y`; on `skip`',
76
- 'kit exports that rule for consumers and does not apply it to itself',
77
- 'operating-rules.md` → "Auto-merge',
78
- 'park したことで人が必要になる',
79
- 'park しない',
80
- 'park は依存として表現できない場合に限る',
81
- 'post the final review markdown as a PR comment',
82
- 'queue #101',
83
- 'runs the same pipeline through PR creation',
84
- 'sleep the polling interval and go back to step 1',
85
- 'stay in English regardless of `JOSH_SESSION_LANG`',
86
- 'stop before merge',
87
- 'stop with the PR OPEN',
88
- 'the artifact language stays English',
89
- 'the completed PR diff',
90
- "the threshold is the same number the epic's own measurement produced",
91
- 'through PR creation',
92
- 'until the user confirms E2E passed',
93
- 'while the PR is still OPEN',
94
- '→ `pnpm josh git -y` → the follow-up filing',
95
- '→ join → `pnpm josh git -y`,',
96
- '■ これからやること',
97
- 'この停止は残す',
98
- 'これが無いと、判断が返ったのに子が永久に止まる',
99
- 'これは advisory であり、アトミックではない',
100
- 'これは分割ではない',
101
- 'これは意図した差である',
102
- 'そのたびに人が口頭で起票先を指示していた',
103
- 'どちらを取るか迷う場合は、**根本対応(割り込み Issue)を既定とする**',
104
- 'コミット後かつ `pnpm josh followup` 実行前',
105
- 'マップが答えるのは「チェックアウトがどこにあるか」であって',
106
- 'ラウンドごとのコミットや CI 再実行も発生しない',
107
- 'ラベルはリポジトリごとに一度作る',
108
- 'レビュー自体はコミットの前のままで',
109
- '今こうなっている: <1文',
110
- '位置の判断は対象 EPIC が既に採ってきた基準に従う',
111
- '候補画像からの採用決定など',
112
- '入口ごとに条件が違う状態は欠陥である',
113
- '入口だけがこの記法から取り残されていた',
114
- '勝手に clone することは人の作業機の配置を勝手に決めることである',
115
- '子 1 件の `fullrun` は分単位。これより短くしても API 消費が増えるだけ',
116
- '子が 13 件なら stash も 13 個になる',
117
- '子を実装するリポジトリではない',
118
- '実行手順の正典は本節であり',
119
- '常駐判定の問いは',
120
- '成果物が存在した時点で入れる',
121
- '成果物の良し悪しは機械では測れない',
122
- '成果物は常に英語',
123
- '打ち忘れた回の Issue はどの EPIC にも属さない',
124
- '排他はリポジトリ単位であり、それを適用するのは `epic:next` である',
125
- '推薦は信号が弱ければ何もしないが',
126
- '新規 EPIC を作る前に、`#N` が既に EPIC の子でないかを確かめる',
127
- '日本語で 60 字程度',
128
- '昇格が他人の Issue を容れ物へ書き換える構造変更であり',
129
- '最後の行が最も重要である',
130
- '検討した 3 案のうち',
131
- '片方だけでは後から辿れない',
132
- '理由は綴りである',
133
- '省略可能な確認ではない',
134
- '矛盾を抱えた計画に対して一括決定を行っても、決定ごと作り直しになる',
135
- '確認という歯止めを外す以上、上限を付ける',
136
- '禁じられているのは裸の',
137
- '自分で外せる印は印として機能せず',
138
- '自動投稿される Issue コメント文面は英語で記載する',
139
- '読み取りにこそ付け忘れやすく、付け忘れは黙って通る',
140
- '閉じていない blocker を宣言している候補を除外する',
141
- ]
142
-
143
- export { RETIRED_PHRASES }
@@ -1,46 +0,0 @@
1
- import { read_unwrapped } from './ai-document-fixture'
2
-
3
- // The `/code-review` subagent's agent type is named once, in `chain-rule.md` step 1, so a run stops
4
- // guessing a name like `code-reviewer` and receiving `Agent type not found` (joshuafolkken/kit#2297).
5
- // This reads that name back and checks it is a real Claude Code agent type rather than a plausible
6
- // invention.
7
-
8
- const CHAIN_RULE = '.claude/skills/workflow-commands/chain-rule.md'
9
-
10
- // The built-in Claude Code agent types the `Agent` tool resolves. `general-purpose` is the one the
11
- // review uses — it carries every tool, so it can load the review skill and, with `--fix`, apply
12
- // findings. The set is the source of truth the document test measures the named type against; there is
13
- // no runtime registry to derive it from, so it is enumerated here.
14
- const KNOWN_AGENT_TYPES: ReadonlySet<string> = new Set([
15
- 'general-purpose',
16
- 'Explore',
17
- 'Plan',
18
- 'claude',
19
- 'claude-code-guide',
20
- 'statusline-setup',
21
- ])
22
-
23
- // The type named as `` `<name>` agent type ``, read from the unwrapped document so a reflow that moved
24
- // the words to a new line does not hide it.
25
- const AGENT_TYPE_PATTERN = /`([\w-]+)` agent type/u
26
-
27
- function named_agent_type(text: string = read_unwrapped(CHAIN_RULE)): string | undefined {
28
- return AGENT_TYPE_PATTERN.exec(text)?.[1]
29
- }
30
-
31
- // Whether the document names an agent type and that type is a real one. A missing name and an invented
32
- // name are both failures — the first leaves the run guessing, the second is the guess made canonical.
33
- function names_known_type(text: string = read_unwrapped(CHAIN_RULE)): boolean {
34
- const named = named_agent_type(text)
35
-
36
- return named !== undefined && KNOWN_AGENT_TYPES.has(named)
37
- }
38
-
39
- const review_agent_type = {
40
- CHAIN_RULE,
41
- KNOWN_AGENT_TYPES,
42
- named_agent_type,
43
- names_known_type,
44
- }
45
-
46
- export { review_agent_type }
@@ -1,73 +0,0 @@
1
- // The single-source check a document-rule test runs to keep one piece of prose in one place
2
- // (joshuafolkken/kit#2188). Epic #2166 trims the entry read by turning prose a reader interprets into
3
- // commands and reference-only pointers; that only holds if the body moved to its command's help does
4
- // not get pasted back into the prose. This is the framework a later child pins each such move with —
5
- // give it the body marker and the file allowed to carry it, and it names every other document that
6
- // still carries the body.
7
- //
8
- // **It is a pure function of the corpus, so the reader is injected.** Reading the repository is the
9
- // test's job (`ai-document-fixture.read_repo_file`); this module takes a `read` and answers from what
10
- // it returns, so the same logic verifies synthetic inputs in its own test and the real corpus in a
11
- // document-rule suite.
12
-
13
- const SINGLE = 1
14
-
15
- interface SingleSourceRule {
16
- // The prose fragment that must live in exactly one place.
17
- marker: string
18
- // The one document allowed to carry it; every reference elsewhere points here rather than copying.
19
- canonical: string
20
- }
21
-
22
- type ReadDocument = (document_path: string) => string
23
-
24
- // Prose is re-wrapped by the formatter, so a marker that spans a line break would miss on a reflow
25
- // that changed nothing. Collapsing whitespace on both sides pins the words rather than the column,
26
- // exactly as `read_unwrapped` does for the marker suites.
27
- function collapsed(text: string): string {
28
- return text.replaceAll(/\s+/gu, ' ')
29
- }
30
-
31
- function carries(text: string, marker: string): boolean {
32
- return collapsed(text).includes(collapsed(marker))
33
- }
34
-
35
- // Every document whose text carries the marker, canonical included — the primitive the two checks
36
- // below read from.
37
- function carriers(
38
- marker: string,
39
- documents: ReadonlyArray<string>,
40
- read: ReadDocument,
41
- ): Array<string> {
42
- return documents.filter((document_path) => carries(read(document_path), marker))
43
- }
44
-
45
- // The documents that carry the body but are not its canonical home — a non-empty result is the
46
- // violation a single-source suite asserts against.
47
- function violations(
48
- rule: SingleSourceRule,
49
- documents: ReadonlyArray<string>,
50
- read: ReadDocument,
51
- ): Array<string> {
52
- return carriers(rule.marker, documents, read).filter(
53
- (document_path) => document_path !== rule.canonical,
54
- )
55
- }
56
-
57
- // True only when the canonical carries the body and nothing else does. A body missing from its
58
- // canonical home is as wrong as one copied elsewhere — a pointer left with nothing to point at — so
59
- // both halves are checked here rather than only the copies.
60
- function is_single_sourced(
61
- rule: SingleSourceRule,
62
- documents: ReadonlyArray<string>,
63
- read: ReadDocument,
64
- ): boolean {
65
- const found = carriers(rule.marker, documents, read)
66
-
67
- return found.length === SINGLE && found[0] === rule.canonical
68
- }
69
-
70
- const single_source = { carriers, is_single_sourced, violations }
71
-
72
- export type { ReadDocument, SingleSourceRule }
73
- export { single_source }
@@ -1,88 +0,0 @@
1
- import { mkdtempSync } from 'node:fs'
2
- import { rm } from 'node:fs/promises'
3
- import { tmpdir } from 'node:os'
4
- import path from 'node:path'
5
- import { execa } from 'execa'
6
- import { git_location_environment } from './git-location-environment'
7
-
8
- // The scaffolding a test needs to drive **real git** in a throwaway repository
9
- // (joshuafolkken/kit#1533).
10
- //
11
- // Two suites need it — `lane-change-base.test.ts` builds a lane topology, `rename-changed-paths.test.ts`
12
- // builds a branch that renames a file away — and neither can use a mocked git, because what each one
13
- // asserts is a property of git itself rather than of an argument list this repository assembles.
14
- //
15
- // **It is one module rather than one copy per suite because this scaffolding has already had to be
16
- // hardened once.** joshuafolkken/kit#1530 found these fixtures committing into the repository the
17
- // suite was running in: under a git hook, `GIT_DIR` and `GIT_INDEX_FILE` beat `cwd` outright, and
18
- // `git config user.email` wrote the fixture's identity into a `--local` config that every linked work
19
- // tree shares. Both halves of that fix live below. A second copy would take the next such hardening
20
- // in one place and silently keep the old behavior in the other — the clone `CLAUDE.md` prohibits.
21
-
22
- // `restore_environment` is declared as "present, possibly undefined" rather than optional:
23
- // `exactOptionalPropertyTypes` is on, so an optional property cannot be *assigned* `undefined`, and
24
- // every suite here starts from a literal that has not opened a workspace yet.
25
- interface FixtureWorkspace {
26
- workspace: string
27
- previous_cwd: string
28
- restore_environment: (() => void) | undefined
29
- }
30
-
31
- const MAIN_BRANCH = '--initial-branch=main'
32
-
33
- // The name commits are authored under, exported because a suite asserts it: proving the identity
34
- // reached the commit is half of proving it was never written into a config file.
35
- const AUTHOR_NAME = 'Kit Fixture'
36
-
37
- // **The identity rides on `-c` rather than being written with `git config`** (joshuafolkken/kit#1530).
38
- // A `--local` write outlives the test and is shared by every work tree of the repository it lands in;
39
- // an option is scoped to the one command.
40
- const IDENTITY_OPTIONS: ReadonlyArray<string> = [
41
- '-c',
42
- 'user.email=fixture@example.test',
43
- '-c',
44
- `user.name=${AUTHOR_NAME}`,
45
- '-c',
46
- 'commit.gpgsign=false',
47
- ]
48
-
49
- // **The environment is cleared rather than inherited** (joshuafolkken/kit#1530). Run from a git hook,
50
- // this process holds `GIT_DIR` and `GIT_INDEX_FILE` pointing at the repository being pushed, and both
51
- // beat `cwd` — so every command here would act on that repository instead of on the fixture.
52
- async function git(cwd: string, arguments_: ReadonlyArray<string>): Promise<string> {
53
- const { stdout } = await execa('git', [...IDENTITY_OPTIONS, ...arguments_], {
54
- cwd,
55
- env: git_location_environment.location_free_environment(),
56
- extendEnv: true,
57
- })
58
-
59
- return stdout.trimEnd()
60
- }
61
-
62
- // Cleared for this process too, not only for the children `git` spawns: a suite's assertions drive
63
- // `git_command`, which spawns git with no environment of its own and so inherits this one.
64
- function open_workspace(prefix: string): FixtureWorkspace {
65
- return {
66
- restore_environment: git_location_environment.clear_git_location_variables(),
67
- previous_cwd: process.cwd(),
68
- workspace: mkdtempSync(path.join(tmpdir(), prefix)),
69
- }
70
- }
71
-
72
- async function close_workspace(fixture: FixtureWorkspace): Promise<void> {
73
- process.chdir(fixture.previous_cwd)
74
- fixture.restore_environment?.()
75
- await rm(fixture.workspace, { force: true, recursive: true })
76
- }
77
-
78
- const git_fixture_workspace = {
79
- AUTHOR_NAME,
80
- close_workspace,
81
- git,
82
- IDENTITY_OPTIONS,
83
- MAIN_BRANCH,
84
- open_workspace,
85
- }
86
-
87
- export type { FixtureWorkspace }
88
- export { git_fixture_workspace }