@erclx/canon 4.0.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 (643) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/claude/.claude-plugin/plugin.json +19 -0
  4. package/claude/skills/bash-cli-script/REQUIREMENT.md +42 -0
  5. package/claude/skills/bash-cli-script/SKILL.md +48 -0
  6. package/claude/skills/bash-cli-script/references/template.md +43 -0
  7. package/claude/skills/bash-script/REQUIREMENT.md +36 -0
  8. package/claude/skills/bash-script/SKILL.md +100 -0
  9. package/claude/skills/bash-script/references/patterns.md +349 -0
  10. package/claude/skills/canon-cli/REQUIREMENT.md +41 -0
  11. package/claude/skills/canon-cli/SKILL.md +103 -0
  12. package/claude/skills/canon-feedback-file/REQUIREMENT.md +40 -0
  13. package/claude/skills/canon-feedback-file/SKILL.md +80 -0
  14. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +40 -0
  15. package/claude/skills/canon-feedback-triage/SKILL.md +63 -0
  16. package/claude/skills/canon-operator/REQUIREMENT.md +61 -0
  17. package/claude/skills/canon-operator/SKILL.md +108 -0
  18. package/claude/skills/canon-rollout/REQUIREMENT.md +59 -0
  19. package/claude/skills/canon-rollout/SKILL.md +147 -0
  20. package/claude/skills/canon-screencast/REQUIREMENT.md +39 -0
  21. package/claude/skills/canon-screencast/SKILL.md +167 -0
  22. package/claude/skills/canon-slides-draft/REQUIREMENT.md +39 -0
  23. package/claude/skills/canon-slides-draft/SKILL.md +62 -0
  24. package/claude/skills/ci-workflow/REQUIREMENT.md +40 -0
  25. package/claude/skills/ci-workflow/SKILL.md +65 -0
  26. package/claude/skills/ci-workflow/references/workflows.md +98 -0
  27. package/claude/skills/claude-address-review/REQUIREMENT.md +57 -0
  28. package/claude/skills/claude-address-review/SKILL.md +212 -0
  29. package/claude/skills/claude-address-review/references/rebase-conflicts.md +39 -0
  30. package/claude/skills/claude-autoship/REQUIREMENT.md +50 -0
  31. package/claude/skills/claude-autoship/SKILL.md +207 -0
  32. package/claude/skills/claude-design-extract/REQUIREMENT.md +42 -0
  33. package/claude/skills/claude-design-extract/SKILL.md +102 -0
  34. package/claude/skills/claude-diagram/REQUIREMENT.md +45 -0
  35. package/claude/skills/claude-diagram/SKILL.md +177 -0
  36. package/claude/skills/claude-docs/REQUIREMENT.md +60 -0
  37. package/claude/skills/claude-docs/SKILL.md +287 -0
  38. package/claude/skills/claude-docs/references/anchor-sweep.md +58 -0
  39. package/claude/skills/claude-docs/references/wireframe-sweep.md +45 -0
  40. package/claude/skills/claude-feature/REQUIREMENT.md +36 -0
  41. package/claude/skills/claude-feature/SKILL.md +115 -0
  42. package/claude/skills/claude-groundwork/REQUIREMENT.md +48 -0
  43. package/claude/skills/claude-groundwork/SKILL.md +142 -0
  44. package/claude/skills/claude-intake/REQUIREMENT.md +49 -0
  45. package/claude/skills/claude-intake/SKILL.md +114 -0
  46. package/claude/skills/claude-intake-answer/REQUIREMENT.md +48 -0
  47. package/claude/skills/claude-intake-answer/SKILL.md +90 -0
  48. package/claude/skills/claude-markdown-propose/REQUIREMENT.md +48 -0
  49. package/claude/skills/claude-markdown-propose/SKILL.md +118 -0
  50. package/claude/skills/claude-markdown-propose/references/format.md +107 -0
  51. package/claude/skills/claude-memory-capture/REQUIREMENT.md +50 -0
  52. package/claude/skills/claude-memory-capture/SKILL.md +101 -0
  53. package/claude/skills/claude-memory-review/REQUIREMENT.md +50 -0
  54. package/claude/skills/claude-memory-review/SKILL.md +210 -0
  55. package/claude/skills/claude-memory-review/references/receipt-format.md +48 -0
  56. package/claude/skills/claude-orchestrate/REQUIREMENT.md +121 -0
  57. package/claude/skills/claude-orchestrate/SKILL.md +241 -0
  58. package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +132 -0
  59. package/claude/skills/claude-orchestrate/references/orchestrator-handoff.md +32 -0
  60. package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +72 -0
  61. package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +87 -0
  62. package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +30 -0
  63. package/claude/skills/claude-orchestrate/references/orchestrator-sweep.md +21 -0
  64. package/claude/skills/claude-orchestrate/scripts/poll.sh +373 -0
  65. package/claude/skills/claude-orchestrate/scripts/watch.sh +181 -0
  66. package/claude/skills/claude-pr-review/REQUIREMENT.md +47 -0
  67. package/claude/skills/claude-pr-review/SKILL.md +295 -0
  68. package/claude/skills/claude-review/REQUIREMENT.md +39 -0
  69. package/claude/skills/claude-review/SKILL.md +142 -0
  70. package/claude/skills/claude-seed-sync/REQUIREMENT.md +45 -0
  71. package/claude/skills/claude-seed-sync/SKILL.md +156 -0
  72. package/claude/skills/claude-standards-audit/REQUIREMENT.md +33 -0
  73. package/claude/skills/claude-standards-audit/SKILL.md +99 -0
  74. package/claude/skills/claude-tasks/REQUIREMENT.md +43 -0
  75. package/claude/skills/claude-tasks/SKILL.md +161 -0
  76. package/claude/skills/claude-teach/REQUIREMENT.md +56 -0
  77. package/claude/skills/claude-teach/SKILL.md +196 -0
  78. package/claude/skills/claude-teach/references/lesson-craft.md +59 -0
  79. package/claude/skills/claude-teach/references/pedagogy.md +67 -0
  80. package/claude/skills/claude-teach/references/promotion.md +54 -0
  81. package/claude/skills/claude-ui-test/REQUIREMENT.md +40 -0
  82. package/claude/skills/claude-ui-test/SKILL.md +77 -0
  83. package/claude/skills/claude-ux-audit/REQUIREMENT.md +40 -0
  84. package/claude/skills/claude-ux-audit/SKILL.md +79 -0
  85. package/claude/skills/claude-ux-measure/REQUIREMENT.md +48 -0
  86. package/claude/skills/claude-ux-measure/SKILL.md +122 -0
  87. package/claude/skills/claude-worker/REQUIREMENT.md +54 -0
  88. package/claude/skills/claude-worker/SKILL.md +96 -0
  89. package/claude/skills/claude-worktree/REQUIREMENT.md +58 -0
  90. package/claude/skills/claude-worktree/SKILL.md +136 -0
  91. package/claude/skills/create-rule/REQUIREMENT.md +45 -0
  92. package/claude/skills/create-rule/SKILL.md +68 -0
  93. package/claude/skills/create-skill/REQUIREMENT.md +38 -0
  94. package/claude/skills/create-skill/SKILL.md +32 -0
  95. package/claude/skills/create-snippet/REQUIREMENT.md +39 -0
  96. package/claude/skills/create-snippet/SKILL.md +30 -0
  97. package/claude/skills/create-standard/REQUIREMENT.md +35 -0
  98. package/claude/skills/create-standard/SKILL.md +29 -0
  99. package/claude/skills/decision-escalate/REQUIREMENT.md +45 -0
  100. package/claude/skills/decision-escalate/SKILL.md +79 -0
  101. package/claude/skills/docs-sync/REQUIREMENT.md +41 -0
  102. package/claude/skills/docs-sync/SKILL.md +95 -0
  103. package/claude/skills/git-branch/REQUIREMENT.md +38 -0
  104. package/claude/skills/git-branch/SKILL.md +60 -0
  105. package/claude/skills/git-commit/REQUIREMENT.md +36 -0
  106. package/claude/skills/git-commit/SKILL.md +49 -0
  107. package/claude/skills/git-followup/REQUIREMENT.md +43 -0
  108. package/claude/skills/git-followup/SKILL.md +48 -0
  109. package/claude/skills/git-issue/REQUIREMENT.md +38 -0
  110. package/claude/skills/git-issue/SKILL.md +65 -0
  111. package/claude/skills/git-pr/REQUIREMENT.md +50 -0
  112. package/claude/skills/git-pr/SKILL.md +164 -0
  113. package/claude/skills/git-pr/references/labels.md +95 -0
  114. package/claude/skills/git-ship/REQUIREMENT.md +42 -0
  115. package/claude/skills/git-ship/SKILL.md +55 -0
  116. package/claude/skills/git-split/REQUIREMENT.md +39 -0
  117. package/claude/skills/git-split/SKILL.md +162 -0
  118. package/claude/skills/git-stage/REQUIREMENT.md +39 -0
  119. package/claude/skills/git-stage/SKILL.md +73 -0
  120. package/claude/skills/git-worktree/REQUIREMENT.md +38 -0
  121. package/claude/skills/git-worktree/SKILL.md +130 -0
  122. package/claude/skills/migration-claude-md/REQUIREMENT.md +40 -0
  123. package/claude/skills/migration-claude-md/SKILL.md +76 -0
  124. package/claude/skills/migration-context/REQUIREMENT.md +36 -0
  125. package/claude/skills/migration-context/SKILL.md +95 -0
  126. package/claude/skills/migration-standards-drop/REQUIREMENT.md +55 -0
  127. package/claude/skills/migration-standards-drop/SKILL.md +113 -0
  128. package/claude/skills/migration-superseded/REQUIREMENT.md +44 -0
  129. package/claude/skills/migration-superseded/SKILL.md +115 -0
  130. package/claude/skills/project-commands/REQUIREMENT.md +42 -0
  131. package/claude/skills/project-commands/SKILL.md +85 -0
  132. package/claude/skills/restate-plainly/REQUIREMENT.md +41 -0
  133. package/claude/skills/restate-plainly/SKILL.md +39 -0
  134. package/claude/skills/session-map/REQUIREMENT.md +57 -0
  135. package/claude/skills/session-map/SKILL.md +70 -0
  136. package/claude/skills/session-resume/REQUIREMENT.md +49 -0
  137. package/claude/skills/session-resume/SKILL.md +51 -0
  138. package/claude/skills/setup-gov/REQUIREMENT.md +37 -0
  139. package/claude/skills/setup-gov/SKILL.md +77 -0
  140. package/claude/skills/setup-indexes/REQUIREMENT.md +45 -0
  141. package/claude/skills/setup-indexes/SKILL.md +153 -0
  142. package/claude/skills/setup-init/REQUIREMENT.md +45 -0
  143. package/claude/skills/setup-init/SKILL.md +127 -0
  144. package/claude/skills/setup-plugins/REQUIREMENT.md +42 -0
  145. package/claude/skills/setup-plugins/SKILL.md +81 -0
  146. package/claude/skills/setup-plugins/references/plugin-catalog.md +53 -0
  147. package/claude/skills/setup-verify/REQUIREMENT.md +39 -0
  148. package/claude/skills/setup-verify/SKILL.md +51 -0
  149. package/claude/skills/systematic-debugging/REQUIREMENT.md +41 -0
  150. package/claude/skills/systematic-debugging/SKILL.md +70 -0
  151. package/claude/skills/write-human/REQUIREMENT.md +46 -0
  152. package/claude/skills/write-human/SKILL.md +68 -0
  153. package/claude/skills/write-human/references/density.md +38 -0
  154. package/claude/skills/write-human/references/machine-tells.md +107 -0
  155. package/claude/skills/write-human/references/source-material.md +37 -0
  156. package/claude/skills/youtube-transcripts/REQUIREMENT.md +38 -0
  157. package/claude/skills/youtube-transcripts/SKILL.md +34 -0
  158. package/docs/agents/audits.md +98 -0
  159. package/docs/agents/capture.md +37 -0
  160. package/docs/agents/census.md +23 -0
  161. package/docs/agents/commands.md +146 -0
  162. package/docs/agents/comments.md +34 -0
  163. package/docs/agents/context-audit-checks.md +120 -0
  164. package/docs/agents/context-audit.md +83 -0
  165. package/docs/agents/counts.md +76 -0
  166. package/docs/agents/demo.md +86 -0
  167. package/docs/agents/docs.md +17 -0
  168. package/docs/agents/gate.md +84 -0
  169. package/docs/agents/index.md +47 -0
  170. package/docs/agents/indexes.md +35 -0
  171. package/docs/agents/install-and-sync.md +385 -0
  172. package/docs/agents/intake.md +81 -0
  173. package/docs/agents/key-changes.md +103 -0
  174. package/docs/agents/label-coverage.md +73 -0
  175. package/docs/agents/markdown-audit.md +197 -0
  176. package/docs/agents/output-shape.md +70 -0
  177. package/docs/agents/overview.md +26 -0
  178. package/docs/agents/records.md +170 -0
  179. package/docs/agents/restated.md +81 -0
  180. package/docs/agents/review-classification.md +77 -0
  181. package/docs/agents/routing.md +61 -0
  182. package/docs/agents/rule-citations.md +98 -0
  183. package/docs/agents/sandbox.md +71 -0
  184. package/docs/agents/scripting.md +149 -0
  185. package/docs/agents/sessions.md +120 -0
  186. package/docs/agents/skills-audit.md +94 -0
  187. package/docs/agents/skills-reach.md +64 -0
  188. package/docs/agents/standards-audit.md +38 -0
  189. package/docs/agents/state-scoped-risk.md +105 -0
  190. package/docs/agents/superseded.md +85 -0
  191. package/docs/agents/targets.md +83 -0
  192. package/docs/agents/tasks.md +200 -0
  193. package/docs/agents/teach.md +158 -0
  194. package/docs/agents/test-order.md +56 -0
  195. package/docs/agents/worktrees.md +62 -0
  196. package/docs/ai-workflow.md +317 -0
  197. package/docs/index.md +23 -0
  198. package/docs/operating-model.md +223 -0
  199. package/docs/target-projects.md +258 -0
  200. package/docs/visual-design-workflow.md +151 -0
  201. package/docs/zshrc-aliases.md +65 -0
  202. package/governance/rules/ci/700-ci-workflow.md +44 -0
  203. package/governance/rules/claude/500-prose.md +15 -0
  204. package/governance/rules/claude/501-markdown.md +14 -0
  205. package/governance/rules/claude/510-context.md +28 -0
  206. package/governance/rules/claude/511-indexes.md +14 -0
  207. package/governance/rules/claude/520-wireframes.md +12 -0
  208. package/governance/rules/claude/530-requirements.md +11 -0
  209. package/governance/rules/claude/540-architecture.md +11 -0
  210. package/governance/rules/claude/550-design.md +11 -0
  211. package/governance/rules/claude/555-tasks.md +12 -0
  212. package/governance/rules/claude/556-groundwork.md +11 -0
  213. package/governance/rules/claude/557-intake.md +11 -0
  214. package/governance/rules/claude/558-plan.md +22 -0
  215. package/governance/rules/claude/559-memory.md +11 -0
  216. package/governance/rules/claude/560-diagrams.md +18 -0
  217. package/governance/rules/claude/561-teach.md +13 -0
  218. package/governance/rules/claude/562-session.md +15 -0
  219. package/governance/rules/claude/570-skill.md +24 -0
  220. package/governance/rules/claude/575-hooks.md +17 -0
  221. package/governance/rules/claude/576-settings.md +14 -0
  222. package/governance/rules/claude/580-readme.md +11 -0
  223. package/governance/rules/claude/590-rule-authoring.md +12 -0
  224. package/governance/rules/claude/591-standard-authoring.md +12 -0
  225. package/governance/rules/claude/592-claude-md.md +19 -0
  226. package/governance/rules/core/000-constitution.md +30 -0
  227. package/governance/rules/core/005-behavior.md +27 -0
  228. package/governance/rules/core/010-testing.md +35 -0
  229. package/governance/rules/core/015-output.md +20 -0
  230. package/governance/rules/core/020-concurrency.md +22 -0
  231. package/governance/rules/core/025-indexes.md +9 -0
  232. package/governance/rules/core/030-error-handling.md +31 -0
  233. package/governance/rules/core/035-tasks.md +13 -0
  234. package/governance/rules/core/040-performance.md +20 -0
  235. package/governance/rules/core/045-memory.md +12 -0
  236. package/governance/rules/core/050-logging.md +20 -0
  237. package/governance/rules/core/055-scratch.md +9 -0
  238. package/governance/rules/core/060-naming.md +19 -0
  239. package/governance/rules/core/065-spelling.md +19 -0
  240. package/governance/rules/core/070-planning.md +18 -0
  241. package/governance/rules/core/075-dependencies.md +25 -0
  242. package/governance/rules/core/080-config-comments.md +22 -0
  243. package/governance/rules/core/085-worktrees.md +17 -0
  244. package/governance/rules/core/087-git.md +11 -0
  245. package/governance/rules/core/090-code-comments.md +39 -0
  246. package/governance/rules/framework/200-react.md +51 -0
  247. package/governance/rules/framework/210-astro.md +41 -0
  248. package/governance/rules/framework/220-fastapi.md +43 -0
  249. package/governance/rules/framework/230-nextjs.md +48 -0
  250. package/governance/rules/framework/250-tailwind.md +32 -0
  251. package/governance/rules/framework/260-shadcn.md +34 -0
  252. package/governance/rules/lang/100-typescript.md +40 -0
  253. package/governance/rules/lang/110-python.md +42 -0
  254. package/governance/rules/lang/120-bash.md +19 -0
  255. package/governance/rules/lib/300-testing-ts.md +39 -0
  256. package/governance/rules/lib/305-e2e-reliability.md +34 -0
  257. package/governance/rules/lib/306-test-scope.md +25 -0
  258. package/governance/rules/lib/310-zod.md +25 -0
  259. package/governance/rules/lib/320-tanstack-query.md +32 -0
  260. package/governance/rules/lib/330-testing-py.md +44 -0
  261. package/governance/rules/lib/340-pydantic.md +38 -0
  262. package/governance/rules/lib/350-security-web.md +32 -0
  263. package/governance/rules/lib/360-security-server.md +39 -0
  264. package/governance/rules/lib/370-database.md +35 -0
  265. package/governance/rules/snippets/505-at-references.md +9 -0
  266. package/governance/rules/ui/400-ui.md +36 -0
  267. package/governance/rules/ui/410-a11y.md +48 -0
  268. package/governance/rules/ui/420-forms.md +36 -0
  269. package/governance/rules/ui/430-ux-completeness.md +65 -0
  270. package/governance/rules/ui/440-surface-capture.md +34 -0
  271. package/governance/rules/ui/450-link-behavior.md +19 -0
  272. package/governance/stacks/astro.toml +2 -0
  273. package/governance/stacks/base.toml +8 -0
  274. package/governance/stacks/node-server.toml +2 -0
  275. package/governance/stacks/node.toml +2 -0
  276. package/governance/stacks/python-fastapi.toml +2 -0
  277. package/governance/stacks/python.toml +2 -0
  278. package/governance/stacks/react.toml +2 -0
  279. package/package.json +69 -0
  280. package/scripts/config.sh +11 -0
  281. package/scripts/core/bootstrap.sh +81 -0
  282. package/scripts/core/check-color-source.sh +41 -0
  283. package/scripts/core/check-ignore-parity.sh +162 -0
  284. package/scripts/core/check-plugin-boundary.sh +45 -0
  285. package/scripts/core/check-seed-independence.sh +59 -0
  286. package/scripts/core/check-skill-paths.sh +24 -0
  287. package/scripts/core/clean.sh +36 -0
  288. package/scripts/core/install-check.sh +101 -0
  289. package/scripts/core/list-seed-roots.sh +18 -0
  290. package/scripts/core/regen-claude-copies.sh +10 -0
  291. package/scripts/core/regen-hero.sh +217 -0
  292. package/scripts/core/regen-indexes.sh +10 -0
  293. package/scripts/core/regen-tooling-paths.sh +61 -0
  294. package/scripts/core/repair-bare-flag.sh +19 -0
  295. package/scripts/core/snapshot.sh +134 -0
  296. package/scripts/core/update.sh +35 -0
  297. package/scripts/docs/list.sh +165 -0
  298. package/scripts/lib/frontmatter.sh +30 -0
  299. package/scripts/lib/gov.sh +14 -0
  300. package/scripts/lib/sandbox-fixtures.sh +191 -0
  301. package/scripts/lib/sandbox-git.sh +125 -0
  302. package/scripts/lib/sandbox-path.sh +206 -0
  303. package/scripts/lib/tooling.sh +35 -0
  304. package/scripts/lib/ui.sh +266 -0
  305. package/scripts/lib/worktree.sh +20 -0
  306. package/scripts/manage-sandbox.sh +466 -0
  307. package/scripts/snippets/create.sh +156 -0
  308. package/scripts/standards/list.sh +115 -0
  309. package/scripts/tooling/create.sh +109 -0
  310. package/scripts/tooling/verify.sh +179 -0
  311. package/snippets/align.md +12 -0
  312. package/snippets/claude/decision-memo.md +39 -0
  313. package/snippets/claude/feature-recap.md +19 -0
  314. package/snippets/claude/figma-steps.md +24 -0
  315. package/snippets/compact-summary.md +5 -0
  316. package/snippets/decision-help.md +6 -0
  317. package/snippets/meta-prompt.md +14 -0
  318. package/snippets/research-prompt.md +7 -0
  319. package/snippets/session-notes.md +11 -0
  320. package/snippets/snippets.toml +5 -0
  321. package/snippets/step-by-step.md +10 -0
  322. package/snippets/web-research.md +21 -0
  323. package/src/audits/baseline.ts +201 -0
  324. package/src/audits/catalog.ts +876 -0
  325. package/src/audits/run.ts +204 -0
  326. package/src/autoship/classify.ts +75 -0
  327. package/src/autoship/paths.ts +51 -0
  328. package/src/binary.ts +16 -0
  329. package/src/browser/engine.ts +40 -0
  330. package/src/census/count.ts +113 -0
  331. package/src/claude/cases/all.ts +24 -0
  332. package/src/claude/cases/authoring.ts +53 -0
  333. package/src/claude/cases/claude-workflow.ts +158 -0
  334. package/src/claude/cases/git.ts +44 -0
  335. package/src/claude/cases/misc.ts +27 -0
  336. package/src/claude/cases/setup.ts +94 -0
  337. package/src/claude/gitignore.ts +51 -0
  338. package/src/claude/routing.ts +283 -0
  339. package/src/claude/seeds-list.ts +47 -0
  340. package/src/claude/seeds.ts +150 -0
  341. package/src/claude/settings.ts +151 -0
  342. package/src/claude/skills-audit.ts +228 -0
  343. package/src/claude/skills-drift.ts +156 -0
  344. package/src/claude/skills-list.ts +99 -0
  345. package/src/claude/skills-rank.ts +320 -0
  346. package/src/claude/skills-reach.ts +227 -0
  347. package/src/cli-run.ts +43 -0
  348. package/src/cli.ts +200 -0
  349. package/src/commands/audits.ts +350 -0
  350. package/src/commands/autoship.ts +129 -0
  351. package/src/commands/capture.ts +133 -0
  352. package/src/commands/census.ts +105 -0
  353. package/src/commands/claude.ts +1286 -0
  354. package/src/commands/comments.ts +240 -0
  355. package/src/commands/context.ts +857 -0
  356. package/src/commands/demo.ts +389 -0
  357. package/src/commands/deps.ts +173 -0
  358. package/src/commands/design.ts +36 -0
  359. package/src/commands/docs.ts +60 -0
  360. package/src/commands/feedback-format.ts +23 -0
  361. package/src/commands/feedback.ts +112 -0
  362. package/src/commands/gate.ts +189 -0
  363. package/src/commands/gov.ts +1265 -0
  364. package/src/commands/indexes.ts +184 -0
  365. package/src/commands/init.ts +113 -0
  366. package/src/commands/intake.ts +406 -0
  367. package/src/commands/inventory.ts +256 -0
  368. package/src/commands/labels.ts +361 -0
  369. package/src/commands/markdown.ts +544 -0
  370. package/src/commands/migrate.ts +175 -0
  371. package/src/commands/pass-through.ts +39 -0
  372. package/src/commands/pr.ts +411 -0
  373. package/src/commands/records.ts +728 -0
  374. package/src/commands/sandbox.ts +468 -0
  375. package/src/commands/secrets.ts +132 -0
  376. package/src/commands/serve.ts +159 -0
  377. package/src/commands/sessions.ts +408 -0
  378. package/src/commands/slides.ts +126 -0
  379. package/src/commands/snippets.ts +84 -0
  380. package/src/commands/standards.ts +247 -0
  381. package/src/commands/sync.ts +428 -0
  382. package/src/commands/targets.ts +319 -0
  383. package/src/commands/tasks.ts +743 -0
  384. package/src/commands/teach.ts +786 -0
  385. package/src/commands/tooling.ts +573 -0
  386. package/src/commands/transcripts.ts +44 -0
  387. package/src/commands/upgrade.ts +231 -0
  388. package/src/commands/wiki.ts +100 -0
  389. package/src/commands/worktrees.ts +191 -0
  390. package/src/comments/scan.ts +338 -0
  391. package/src/comments/trend.ts +207 -0
  392. package/src/comments/vocabulary.ts +85 -0
  393. package/src/context/architecture.ts +364 -0
  394. package/src/context/audit.ts +790 -0
  395. package/src/context/citations.ts +196 -0
  396. package/src/context/folders.ts +186 -0
  397. package/src/context/gate.ts +57 -0
  398. package/src/context/index-drift.ts +64 -0
  399. package/src/context/narration.ts +99 -0
  400. package/src/copy.ts +30 -0
  401. package/src/counts/catalogs.ts +96 -0
  402. package/src/counts/numbers.ts +79 -0
  403. package/src/counts/scan.ts +314 -0
  404. package/src/demo/beats.ts +135 -0
  405. package/src/demo/compile.ts +326 -0
  406. package/src/demo/container.ts +63 -0
  407. package/src/demo/cursors.ts +55 -0
  408. package/src/demo/drive.ts +357 -0
  409. package/src/demo/pointer.ts +178 -0
  410. package/src/demo/theme.ts +112 -0
  411. package/src/deps/audit.ts +153 -0
  412. package/src/design/parse.ts +116 -0
  413. package/src/design/render.ts +249 -0
  414. package/src/docs/read.ts +77 -0
  415. package/src/exec.ts +16 -0
  416. package/src/exempt-marker.ts +43 -0
  417. package/src/frontmatter.ts +13 -0
  418. package/src/gate/measures.ts +682 -0
  419. package/src/gate/sequencer.ts +386 -0
  420. package/src/gate/stages.ts +412 -0
  421. package/src/git-env.ts +36 -0
  422. package/src/git-files.ts +105 -0
  423. package/src/git-ignore.ts +46 -0
  424. package/src/github-format.ts +13 -0
  425. package/src/github.ts +24 -0
  426. package/src/gov/adapter.ts +103 -0
  427. package/src/gov/citations.ts +514 -0
  428. package/src/gov/consumed.ts +129 -0
  429. package/src/gov/install.ts +132 -0
  430. package/src/gov/list.ts +106 -0
  431. package/src/gov/payload.ts +39 -0
  432. package/src/gov/restated.ts +814 -0
  433. package/src/gov/stacks.ts +205 -0
  434. package/src/gov/superseded.ts +415 -0
  435. package/src/gov/test-order.ts +407 -0
  436. package/src/indexes/frontmatter.ts +46 -0
  437. package/src/indexes/regen.ts +84 -0
  438. package/src/indexes/render.ts +201 -0
  439. package/src/indexes/walk.ts +83 -0
  440. package/src/init/flags.ts +60 -0
  441. package/src/init/plan.ts +114 -0
  442. package/src/init/run.ts +46 -0
  443. package/src/init/steps.ts +77 -0
  444. package/src/intake/folder.ts +320 -0
  445. package/src/intake/items.ts +174 -0
  446. package/src/inventory/config.ts +117 -0
  447. package/src/inventory/group.ts +76 -0
  448. package/src/inventory/subjects.ts +114 -0
  449. package/src/inventory/walk.ts +129 -0
  450. package/src/labels/audit.ts +82 -0
  451. package/src/labels/coverage.ts +79 -0
  452. package/src/labels/map.ts +101 -0
  453. package/src/labels/phase.ts +95 -0
  454. package/src/markdown/bans.ts +94 -0
  455. package/src/markdown/files.ts +102 -0
  456. package/src/markdown/gate.ts +28 -0
  457. package/src/markdown/scan.ts +295 -0
  458. package/src/markdown/structure.ts +730 -0
  459. package/src/migrate/apply.ts +115 -0
  460. package/src/migrate/plan.ts +103 -0
  461. package/src/migrate/rename.ts +183 -0
  462. package/src/pr/bijection.ts +145 -0
  463. package/src/pr/paths.ts +335 -0
  464. package/src/process/harness.ts +167 -0
  465. package/src/project-root.ts +19 -0
  466. package/src/records/backup.ts +455 -0
  467. package/src/records/migrate.ts +78 -0
  468. package/src/records/size.ts +260 -0
  469. package/src/records/validate.ts +1162 -0
  470. package/src/sandbox/census.ts +228 -0
  471. package/src/sandbox/coverage.ts +115 -0
  472. package/src/sandbox/expect.ts +629 -0
  473. package/src/sandbox/tree.ts +47 -0
  474. package/src/secrets/marker.ts +30 -0
  475. package/src/secrets/patterns.ts +142 -0
  476. package/src/secrets/scan.ts +123 -0
  477. package/src/secrets/shipped.ts +111 -0
  478. package/src/seed-marker.ts +74 -0
  479. package/src/serve/static.ts +322 -0
  480. package/src/sessions/claim.ts +85 -0
  481. package/src/sessions/live.ts +79 -0
  482. package/src/sessions/registry.ts +137 -0
  483. package/src/sessions/resolve.ts +333 -0
  484. package/src/slides/layouts.ts +391 -0
  485. package/src/slides/open.ts +18 -0
  486. package/src/slides/parse.ts +84 -0
  487. package/src/slides/render.ts +88 -0
  488. package/src/slides/styles.ts +44 -0
  489. package/src/snippets/categories.ts +66 -0
  490. package/src/snippets/list.ts +32 -0
  491. package/src/snippets/presets.ts +50 -0
  492. package/src/standards/audit.ts +132 -0
  493. package/src/standards/read.ts +100 -0
  494. package/src/sync/check.ts +692 -0
  495. package/src/sync/engine.ts +576 -0
  496. package/src/sync/git.ts +225 -0
  497. package/src/sync/history.ts +123 -0
  498. package/src/sync/layout.ts +140 -0
  499. package/src/sync/reverse.ts +268 -0
  500. package/src/sync/seeds-report.ts +126 -0
  501. package/src/sync/stamp.ts +358 -0
  502. package/src/sync/target.ts +70 -0
  503. package/src/sync/workflow.ts +200 -0
  504. package/src/target.ts +43 -0
  505. package/src/targets/pulls.ts +250 -0
  506. package/src/targets/registry.ts +203 -0
  507. package/src/targets/resolve.ts +145 -0
  508. package/src/targets/sweep.ts +246 -0
  509. package/src/tasks/archive.ts +506 -0
  510. package/src/tasks/record.ts +311 -0
  511. package/src/tasks/trunk.ts +89 -0
  512. package/src/tasks/validate.ts +1001 -0
  513. package/src/teach/lesson.ts +180 -0
  514. package/src/teach/workspace.ts +842 -0
  515. package/src/tooling/gitignore.ts +122 -0
  516. package/src/tooling/inject.ts +193 -0
  517. package/src/tooling/list.ts +39 -0
  518. package/src/tooling/manifest.ts +176 -0
  519. package/src/tooling/package.ts +166 -0
  520. package/src/tooling/read.ts +65 -0
  521. package/src/tooling/scan.ts +147 -0
  522. package/src/tooling/stamp.ts +44 -0
  523. package/src/transcripts/fetch.ts +156 -0
  524. package/src/transcripts/metadata.ts +54 -0
  525. package/src/transcripts/vtt.ts +114 -0
  526. package/src/ui.ts +266 -0
  527. package/src/version/compare.ts +53 -0
  528. package/src/version/installed.ts +40 -0
  529. package/src/version/manager.ts +67 -0
  530. package/src/version/skew.ts +192 -0
  531. package/src/wiki/init.ts +85 -0
  532. package/src/worktree.ts +143 -0
  533. package/src/worktrees/reclaim.ts +306 -0
  534. package/standards/architecture.md +72 -0
  535. package/standards/branch.md +59 -0
  536. package/standards/commit.md +72 -0
  537. package/standards/context.md +151 -0
  538. package/standards/design.md +93 -0
  539. package/standards/diagrams.md +152 -0
  540. package/standards/glossary.md +75 -0
  541. package/standards/groundwork.md +211 -0
  542. package/standards/index.md +36 -0
  543. package/standards/intake.md +192 -0
  544. package/standards/issue.md +94 -0
  545. package/standards/markdown.md +137 -0
  546. package/standards/memory.md +144 -0
  547. package/standards/plan.md +172 -0
  548. package/standards/pr.md +139 -0
  549. package/standards/publish.md +51 -0
  550. package/standards/readme.md +208 -0
  551. package/standards/requirements.md +70 -0
  552. package/standards/rule.md +118 -0
  553. package/standards/session.md +109 -0
  554. package/standards/skill.md +300 -0
  555. package/standards/slug.md +39 -0
  556. package/standards/snippets.md +76 -0
  557. package/standards/standard.md +170 -0
  558. package/standards/tasks.md +254 -0
  559. package/standards/teach.md +153 -0
  560. package/standards/versioning.md +71 -0
  561. package/standards/wireframes.md +113 -0
  562. package/tooling/astro/configs/astro.config.mjs +31 -0
  563. package/tooling/astro/configs/eslint.config.js +79 -0
  564. package/tooling/astro/configs/playwright.config.ts +26 -0
  565. package/tooling/astro/configs/tsconfig.json +12 -0
  566. package/tooling/astro/configs/vitest.config.ts +22 -0
  567. package/tooling/astro/manifest.toml +34 -0
  568. package/tooling/astro/reference.md +60 -0
  569. package/tooling/base/configs/.editorconfig +5 -0
  570. package/tooling/base/configs/.github/pull_request_template.md +18 -0
  571. package/tooling/base/configs/.github/workflows/verify.yml +35 -0
  572. package/tooling/base/configs/.husky/commit-msg +1 -0
  573. package/tooling/base/configs/.husky/post-merge +61 -0
  574. package/tooling/base/configs/.husky/post-rewrite +21 -0
  575. package/tooling/base/configs/.husky/pre-commit +1 -0
  576. package/tooling/base/configs/.husky/pre-push +1 -0
  577. package/tooling/base/configs/.prettierrc +12 -0
  578. package/tooling/base/configs/.shellcheckrc +1 -0
  579. package/tooling/base/configs/.vscode/extensions.json +9 -0
  580. package/tooling/base/configs/.vscode/settings.json +3 -0
  581. package/tooling/base/configs/commitlint.config.js +11 -0
  582. package/tooling/base/configs/scripts/verify.sh +64 -0
  583. package/tooling/base/manifest.toml +30 -0
  584. package/tooling/base/reference.md +98 -0
  585. package/tooling/base/seeds/.claude/context/ci.md +33 -0
  586. package/tooling/base/seeds/.claude/context/development.md +37 -0
  587. package/tooling/base/seeds/.claude/context/index.md +11 -0
  588. package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
  589. package/tooling/base/seeds/.cspell/tech-stack.txt +19 -0
  590. package/tooling/base/seeds/.lintstagedrc +8 -0
  591. package/tooling/base/seeds/.prettierignore +0 -0
  592. package/tooling/base/seeds/cspell.json +20 -0
  593. package/tooling/claude/manifest.toml +14 -0
  594. package/tooling/claude/reference.md +79 -0
  595. package/tooling/claude/seeds/.claude/ARCHITECTURE.md +13 -0
  596. package/tooling/claude/seeds/.claude/DESIGN.md +62 -0
  597. package/tooling/claude/seeds/.claude/REQUIREMENTS.md +18 -0
  598. package/tooling/claude/seeds/.claude/diagrams/index.md +8 -0
  599. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +50 -0
  600. package/tooling/claude/seeds/.claude/hooks/memory-index.sh +68 -0
  601. package/tooling/claude/seeds/.claude/hooks/path-form.sh +57 -0
  602. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +52 -0
  603. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +86 -0
  604. package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +71 -0
  605. package/tooling/claude/seeds/.claude/memory/index.md +8 -0
  606. package/tooling/claude/seeds/.claude/settings.json +47 -0
  607. package/tooling/claude/seeds/.claude/tasks/index.md +8 -0
  608. package/tooling/claude/seeds/.claude/wireframes/index.md +8 -0
  609. package/tooling/claude/seeds/CLAUDE.md +29 -0
  610. package/tooling/claude/user/settings.template.json +10 -0
  611. package/tooling/claude/user/statusline-command.sh +53 -0
  612. package/tooling/python/configs/.coveragerc +14 -0
  613. package/tooling/python/configs/.python-version +1 -0
  614. package/tooling/python/configs/mypy.ini +6 -0
  615. package/tooling/python/configs/pytest.ini +4 -0
  616. package/tooling/python/configs/ruff.toml +15 -0
  617. package/tooling/python/configs/scripts/verify.sh +77 -0
  618. package/tooling/python/manifest.toml +16 -0
  619. package/tooling/python/reference.md +66 -0
  620. package/tooling/python/seeds/.cspell/tech-stack.txt +19 -0
  621. package/tooling/python/seeds/tests/test_smoke.py +2 -0
  622. package/tooling/vite-react/configs/playwright.config.ts +26 -0
  623. package/tooling/vite-react/configs/tsconfig.json +35 -0
  624. package/tooling/vite-react/configs/vite.config.ts +24 -0
  625. package/tooling/vite-react/configs/vitest.config.ts +26 -0
  626. package/tooling/vite-react/manifest.toml +23 -0
  627. package/tooling/vite-react/reference.md +55 -0
  628. package/tooling/vite-react/seeds/.cspell/project-terms.txt +1 -0
  629. package/tooling/vite-react/seeds/.cspell/tech-stack.txt +1 -0
  630. package/tooling/web/configs/.github/workflows/verify.yml +134 -0
  631. package/tooling/web/configs/.vscode/extensions.json +13 -0
  632. package/tooling/web/configs/.vscode/settings.json +10 -0
  633. package/tooling/web/configs/e2e/home.spec.ts +6 -0
  634. package/tooling/web/configs/e2e/screenshot.ts +53 -0
  635. package/tooling/web/configs/eslint.config.js +82 -0
  636. package/tooling/web/configs/scripts/screenshot.sh +28 -0
  637. package/tooling/web/configs/scripts/verify.sh +80 -0
  638. package/tooling/web/configs/scripts/worktree-port.sh +74 -0
  639. package/tooling/web/configs/src/test/setup.ts +8 -0
  640. package/tooling/web/manifest.toml +58 -0
  641. package/tooling/web/reference.md +115 -0
  642. package/tooling/web/seeds/.cspell/tech-stack.txt +18 -0
  643. package/tsconfig.json +14 -0
@@ -0,0 +1,225 @@
1
+ import { basename } from 'node:path'
2
+ import { execa } from 'execa'
3
+ import { gitEnv } from '@/git-env'
4
+ import type { SyncDomain } from '@/sync/target'
5
+ import { pipeOutput } from '@/ui'
6
+
7
+ const FILE_LIST_MAX = 3
8
+
9
+ export type DomainVerb = 'Add' | 'Remove' | 'Update'
10
+
11
+ export interface DomainChange {
12
+ readonly domain: SyncDomain
13
+ readonly verb: DomainVerb
14
+ readonly names: readonly string[]
15
+ readonly paths: readonly string[]
16
+ }
17
+
18
+ export class GitCommandError extends Error {}
19
+
20
+ /**
21
+ * Pulls the path out of each porcelain line by taking the last whitespace
22
+ * separated field, which is what the bash `awk '{print $NF}'` did. A rename
23
+ * reports `old -> new` and so yields the destination, and a sync never
24
+ * produces one because the workflow refuses a dirty tree before it starts.
25
+ */
26
+ export function changedPaths(status: string): string[] {
27
+ const paths: string[] = []
28
+
29
+ for (const line of status.split('\n')) {
30
+ const trimmed = line.trim()
31
+ if (trimmed === '') continue
32
+ const fields = trimmed.split(/\s+/)
33
+ paths.push(fields[fields.length - 1])
34
+ }
35
+
36
+ return paths
37
+ }
38
+
39
+ export function changedNames(status: string): string[] {
40
+ const names = changedPaths(status).map((path) => basename(path))
41
+ return [...new Set(names)].sort()
42
+ }
43
+
44
+ /**
45
+ * Classifies a domain's porcelain output as one verb for the pull request body.
46
+ * Only an all-untracked change reads as `Add` and only an all-deleted one as
47
+ * `Remove`, so any mix falls through to `Update`.
48
+ */
49
+ export function classifyStatus(status: string): DomainVerb {
50
+ let hasModify = false
51
+ let hasNew = false
52
+ let hasDelete = false
53
+
54
+ for (const line of status.split('\n')) {
55
+ if (line === '') continue
56
+ const code = line.slice(0, 2)
57
+
58
+ if (code === '??') hasNew = true
59
+ else if (code === ' D' || code === 'D ' || code === 'DD') hasDelete = true
60
+ else hasModify = true
61
+ }
62
+
63
+ if (!hasModify && !hasDelete && hasNew) return 'Add'
64
+ if (!hasModify && !hasNew && hasDelete) return 'Remove'
65
+ return 'Update'
66
+ }
67
+
68
+ export function formatFileList(files: readonly string[]): string {
69
+ if (files.length === 0) return ''
70
+ if (files.length <= FILE_LIST_MAX) return files.join(', ')
71
+
72
+ const head = files.slice(0, FILE_LIST_MAX).join(', ')
73
+ return `${head}, and ${files.length - FILE_LIST_MAX} more`
74
+ }
75
+
76
+ /**
77
+ * Names the branch at minute resolution, so two syncs inside the same minute
78
+ * collide by design and the collision check turns the second one away.
79
+ */
80
+ export function syncBranchName(now: Date): string {
81
+ const pad = (value: number): string => String(value).padStart(2, '0')
82
+ const day = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`
83
+ const minute = `${pad(now.getHours())}${pad(now.getMinutes())}`
84
+
85
+ return `chore/canon-sync-${day}-${minute}`
86
+ }
87
+
88
+ export function commitMessage(domains: readonly SyncDomain[]): string {
89
+ return `chore(sync): update ${domains.join(', ')} from toolkit`
90
+ }
91
+
92
+ export function pullRequestBody(changes: readonly DomainChange[]): string {
93
+ const domains = changes.map((change) => change.domain).join(', ')
94
+ const lines = [
95
+ '## Summary',
96
+ '',
97
+ `Sync ${domains} from toolkit.`,
98
+ '',
99
+ '## Key Changes',
100
+ '',
101
+ ]
102
+
103
+ for (const change of changes) {
104
+ lines.push(
105
+ `- ${change.verb} \`${change.domain}/\` ${formatFileList(change.names)}.`,
106
+ )
107
+ }
108
+
109
+ return lines.join('\n')
110
+ }
111
+
112
+ /**
113
+ * The seam every git mutation goes through, so the workflow can be driven by a
114
+ * fake in tests without a repository behind it. Reads swallow failure the way
115
+ * the bash `2>/dev/null || true` did. Mutations throw, because a failed commit
116
+ * or push must not be reported as a completed sync.
117
+ */
118
+ export interface GitRunner {
119
+ status(paths: readonly string[]): Promise<string>
120
+ currentBranch(): Promise<string>
121
+ branchExists(name: string): Promise<boolean>
122
+ remoteBranchExists(name: string): Promise<boolean>
123
+ createBranch(name: string): Promise<void>
124
+ stage(paths: readonly string[]): Promise<void>
125
+ commit(message: string): Promise<void>
126
+ push(branch: string): Promise<void>
127
+ }
128
+
129
+ export interface PullRequestOpener {
130
+ create(title: string, body: string): Promise<string>
131
+ }
132
+
133
+ export function createGitRunner(target: string): GitRunner {
134
+ return {
135
+ status: (paths) => read(target, ['status', '--short', '--', ...paths]),
136
+ currentBranch: () => read(target, ['symbolic-ref', '--short', 'HEAD']),
137
+ branchExists: async (name) =>
138
+ (await read(target, ['branch', '--list', name])) !== '',
139
+ remoteBranchExists: async (name) =>
140
+ (await read(target, ['ls-remote', '--heads', 'origin', name])) !== '',
141
+ createBranch: async (name) => {
142
+ await mutate(target, ['checkout', '-b', name])
143
+ },
144
+ stage: async (paths) => {
145
+ await mutate(target, ['add', '--', ...paths])
146
+ },
147
+ commit: async (message) => {
148
+ await mutate(target, ['commit', '-m', message])
149
+ },
150
+ push: async (branch) => {
151
+ await mutate(target, ['push', '-u', 'origin', branch])
152
+ },
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Passes the body on stdin rather than through a temp file. The bash wrote one
158
+ * with `mktemp` and removed it afterwards, which left the file behind whenever
159
+ * `gh` failed.
160
+ */
161
+ export function createPullRequestOpener(target: string): PullRequestOpener {
162
+ return {
163
+ create: async (title, body) => {
164
+ const result = await execa(
165
+ 'gh',
166
+ ['pr', 'create', '--title', title, '--body-file', '-'],
167
+ {
168
+ cwd: target,
169
+ input: body,
170
+ reject: false,
171
+ env: gitEnv(),
172
+ extendEnv: false,
173
+ },
174
+ )
175
+
176
+ if (result.exitCode !== 0) {
177
+ throw new GitCommandError(
178
+ `gh pr create failed: ${result.stderr || result.stdout}`,
179
+ )
180
+ }
181
+
182
+ return result.stdout.trim()
183
+ },
184
+ }
185
+ }
186
+
187
+ export async function hasGh(): Promise<boolean> {
188
+ const result = await execa('command', ['-v', 'gh'], {
189
+ reject: false,
190
+ shell: true,
191
+ })
192
+
193
+ return result.exitCode === 0
194
+ }
195
+
196
+ /**
197
+ * Both seams strip git's repository-resolution variables, since a hook exports
198
+ * `GIT_DIR` into every process it runs and it beats `-C`. A sync driven from
199
+ * inside one would otherwise read the hook's repository and, through `mutate`,
200
+ * commit and push into it.
201
+ */
202
+ async function read(target: string, args: readonly string[]): Promise<string> {
203
+ const result = await execa('git', ['-C', target, ...args], {
204
+ reject: false,
205
+ env: gitEnv(),
206
+ extendEnv: false,
207
+ })
208
+ return result.exitCode === 0 ? result.stdout : ''
209
+ }
210
+
211
+ async function mutate(target: string, args: readonly string[]): Promise<void> {
212
+ const result = await execa('git', ['-C', target, ...args], {
213
+ reject: false,
214
+ env: gitEnv(),
215
+ extendEnv: false,
216
+ })
217
+
218
+ if (result.exitCode !== 0) {
219
+ throw new GitCommandError(
220
+ `git ${args[0]} failed: ${result.stderr || result.stdout}`,
221
+ )
222
+ }
223
+
224
+ if (result.stdout !== '') pipeOutput(result.stdout)
225
+ }
@@ -0,0 +1,123 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { readFileSync } from 'node:fs'
3
+ import { sep } from 'node:path'
4
+ import { execaSync } from 'execa'
5
+ import { gitEnv } from '@/git-env'
6
+
7
+ /** Blob SHA to the newest commit whose post-image for that path held it. */
8
+ export type PathHistory = ReadonlyMap<string, string>
9
+
10
+ /** Source path relative to the toolkit root, to that path's known blobs. */
11
+ export type HistoryIndex = ReadonlyMap<string, PathHistory>
12
+
13
+ const EMPTY_BLOB = '0000000000000000000000000000000000000000'
14
+
15
+ /**
16
+ * Git's own object name for a file's contents: sha1 over `blob <len>\0` and the
17
+ * bytes. Computed here rather than shelled out to `git hash-object`, which would
18
+ * cost a process per file inside the attribution pass.
19
+ *
20
+ * A repository normalizing line endings on checkout stores a blob the working
21
+ * tree never holds, so this returns a name that matches no historical version.
22
+ * The caller treats an unmatched file as unattributed, which is the safe label.
23
+ */
24
+ export function gitBlobHash(content: Buffer): string {
25
+ const header = Buffer.from(`blob ${content.length}\0`, 'utf8')
26
+ return createHash('sha1').update(header).update(content).digest('hex')
27
+ }
28
+
29
+ /**
30
+ * Reads `--raw` log output into blob-to-commit maps, one per path.
31
+ *
32
+ * Reverse-chronological input means the first commit naming a blob is the
33
+ * newest that produced it, so an existing key is never overwritten. Content
34
+ * reverted and restored therefore reports the recent commit rather than the
35
+ * original, which is the one an operator can act on.
36
+ */
37
+ export function parseRawLog(output: string): HistoryIndex {
38
+ const index = new Map<string, Map<string, string>>()
39
+ let commit = ''
40
+
41
+ for (const line of output.split('\n')) {
42
+ if (line === '') continue
43
+
44
+ if (!line.startsWith(':')) {
45
+ commit = line.trim()
46
+ continue
47
+ }
48
+
49
+ const [meta, path] = line.split('\t')
50
+ if (path === undefined || commit === '') continue
51
+
52
+ const blob = meta.trim().split(/\s+/)[3]
53
+ if (blob === undefined || blob === EMPTY_BLOB) continue
54
+
55
+ const blobs = index.get(path) ?? new Map<string, string>()
56
+ if (!blobs.has(blob)) blobs.set(blob, commit)
57
+ index.set(path, blobs)
58
+ }
59
+
60
+ return index
61
+ }
62
+
63
+ /**
64
+ * Every historical version of the given toolkit-relative paths, or `undefined`
65
+ * when this toolkit has no history to read. A package installed from the
66
+ * registry ships source without `.git`, so absence is an ordinary state rather
67
+ * than a failure, and the caller degrades to the unattributed path.
68
+ *
69
+ * Renames are disabled so a raw line always carries exactly one path. Detection
70
+ * would emit a two-path form that the parser would read as an unknown blob.
71
+ */
72
+ export function readHistoryIndex(
73
+ toolkitRoot: string,
74
+ paths: readonly string[],
75
+ ): HistoryIndex | undefined {
76
+ if (paths.length === 0) return new Map()
77
+
78
+ const result = execaSync(
79
+ 'git',
80
+ [
81
+ '-C',
82
+ toolkitRoot,
83
+ 'log',
84
+ '--all',
85
+ '--raw',
86
+ '--no-renames',
87
+ '--no-abbrev',
88
+ '--relative',
89
+ '--format=%H',
90
+ '--',
91
+ ...paths.map(toRepoPath),
92
+ ],
93
+ { reject: false, env: gitEnv(), extendEnv: false },
94
+ )
95
+
96
+ return result.exitCode === 0 ? parseRawLog(result.stdout) : undefined
97
+ }
98
+
99
+ /**
100
+ * Git prints and accepts posix separators on every platform, so a Windows
101
+ * caller's `relative()` output has to be converted before it can match a path
102
+ * the log emitted.
103
+ */
104
+ export function toRepoPath(path: string): string {
105
+ return path.split(sep).join('/')
106
+ }
107
+
108
+ /**
109
+ * The commit whose version of `sourceRel` matches what sits at `installedPath`.
110
+ * A match proves the file is untouched since it was installed, so the toolkit is
111
+ * what moved. No match means the content matches nothing this toolkit ever
112
+ * published, which is a local edit and stays unattributed.
113
+ */
114
+ export function findInstalledOrigin(
115
+ index: HistoryIndex,
116
+ sourceRel: string,
117
+ installedPath: string,
118
+ ): string | undefined {
119
+ const blobs = index.get(toRepoPath(sourceRel))
120
+ if (blobs === undefined) return undefined
121
+
122
+ return blobs.get(gitBlobHash(readFileSync(installedPath)))
123
+ }
@@ -0,0 +1,140 @@
1
+ import { existsSync, statSync } from 'node:fs'
2
+ import { basename, join } from 'node:path'
3
+ import { SUBDIRS } from '@/claude/seeds'
4
+ import type { StampDomain } from '@/sync/stamp'
5
+
6
+ const CLAUDE_DIR = '.claude'
7
+
8
+ /**
9
+ * Domains an older toolkit installed at the project root, each with the source
10
+ * folder naming what it owns. Governance is absent because its rules have always
11
+ * landed under `.claude/rules/`, so there is no earlier location to be stranded
12
+ * at. Standards and snippets are absent because no copy installs into a target
13
+ * at all now, so a root `standards/` or `snippets/` folder there is the
14
+ * project's own authoring surface and reporting it as unmigrated would propose
15
+ * moving files nothing installed.
16
+ *
17
+ * A tuple array rather than a partial record, so the domain key stays typed
18
+ * without asserting an `Object.entries` result back into the union. Empty
19
+ * now that both former entries retired their install channels. A future
20
+ * domain installing at a target's root, the way an older toolkit installed
21
+ * standards and snippets there, would be the next entry to add.
22
+ */
23
+ const ROOT_LAYOUTS: readonly (readonly [
24
+ StampDomain,
25
+ string,
26
+ (root: string) => string,
27
+ ])[] = []
28
+
29
+ /**
30
+ * A target file that a shipped seed folder replaced. Carries no source and
31
+ * queues no change, because the file holds content the project wrote and only
32
+ * the user can decide where it moves.
33
+ */
34
+ export interface SupersededEntry {
35
+ readonly rel: string
36
+ readonly replacedBy: string
37
+ }
38
+
39
+ /**
40
+ * A domain whose files sit at the root layout an older toolkit installed to,
41
+ * with nothing at the path the current one reads. Distinct from a domain that
42
+ * was never installed, which has neither.
43
+ */
44
+ export interface UnmigratedDomain {
45
+ readonly domain: StampDomain
46
+ readonly rootPath: string
47
+ readonly installPath: string
48
+ /** Root files the toolkit ships under this domain, not every file present. */
49
+ readonly files: number
50
+ }
51
+
52
+ /**
53
+ * Pairs each seed subdirectory against an uppercase-stem sibling in the target,
54
+ * so a project still holding `.claude/TASKS.md` is reported against the
55
+ * `.claude/tasks/` folder that replaced it.
56
+ *
57
+ * Deriving from the seed tree rather than from a fixed list means a folder
58
+ * added later is covered without editing this file. The cost is that only an
59
+ * exact stem matches, so a suffixed variant such as `TASKS-ARCHIVE.md` is not
60
+ * reported.
61
+ */
62
+ export function collectSuperseded(target: string): SupersededEntry[] {
63
+ const entries: SupersededEntry[] = []
64
+
65
+ for (const subdir of SUBDIRS) {
66
+ const rel = join(CLAUDE_DIR, `${subdir.toUpperCase()}.md`)
67
+ if (!isFile(join(target, rel))) continue
68
+
69
+ entries.push({ rel, replacedBy: join(CLAUDE_DIR, subdir) })
70
+ }
71
+
72
+ return entries
73
+ }
74
+
75
+ /**
76
+ * Domains the target holds at the root rather than under `.claude/`. Reported
77
+ * separately from the per-domain scan because that scan lists only domains it
78
+ * finds installed, so an unmigrated project would otherwise read as one that
79
+ * never installed the domain at all.
80
+ *
81
+ * A root folder is claimed only when it holds a file the toolkit ships under
82
+ * that domain. Presence of the folder alone is not evidence: a project can
83
+ * carry its own `snippets/` of prompts it wrote and never have installed the
84
+ * domain, and calling that unmigrated would fail `--exit-code` with no action
85
+ * that clears it.
86
+ */
87
+ export function detectUnmigrated(
88
+ toolkitRoot: string,
89
+ target: string,
90
+ ): UnmigratedDomain[] {
91
+ const found: UnmigratedDomain[] = []
92
+
93
+ for (const [domain, rootPath, sourceDir] of ROOT_LAYOUTS) {
94
+ const installPath = join(CLAUDE_DIR, rootPath)
95
+ if (isDirectoryWithFiles(join(target, installPath))) continue
96
+
97
+ const files = countToolkitOwned(
98
+ join(target, rootPath),
99
+ sourceDir(toolkitRoot),
100
+ )
101
+ if (files === 0) continue
102
+
103
+ found.push({ domain, rootPath, installPath, files })
104
+ }
105
+
106
+ return found
107
+ }
108
+
109
+ /**
110
+ * Root files whose basename matches something the toolkit ships for this domain.
111
+ * Basenames rather than relative paths, because the root layout an older toolkit
112
+ * wrote is flat while the source nests by category, and the question here is only
113
+ * whether any file is toolkit-owned rather than which source each one came from.
114
+ */
115
+ function countToolkitOwned(dir: string, sourceDir: string): number {
116
+ const owned = new Set(listMarkdown(sourceDir).map((rel) => basename(rel)))
117
+ if (owned.size === 0) return 0
118
+
119
+ return listMarkdown(dir).filter((rel) => owned.has(basename(rel))).length
120
+ }
121
+
122
+ function isFile(path: string): boolean {
123
+ return existsSync(path) && statSync(path).isFile()
124
+ }
125
+
126
+ function isDirectoryWithFiles(path: string): boolean {
127
+ return listMarkdown(path).length > 0
128
+ }
129
+
130
+ function listMarkdown(dir: string): string[] {
131
+ if (!existsSync(dir) || !statSync(dir).isDirectory()) return []
132
+
133
+ return [
134
+ ...new Bun.Glob('**/*.md').scanSync({
135
+ cwd: dir,
136
+ onlyFiles: true,
137
+ dot: true,
138
+ }),
139
+ ]
140
+ }