@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,18 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { execa } from 'execa'
3
+
4
+ export async function openDeck(path: string): Promise<boolean> {
5
+ const windowsPath = await execa('wslpath', ['-w', path], { reject: false })
6
+ if (windowsPath.exitCode === 0) {
7
+ const cwd = existsSync('/mnt/c') ? '/mnt/c' : undefined
8
+ const result = await execa(
9
+ 'cmd.exe',
10
+ ['/c', 'start', '', windowsPath.stdout.trim()],
11
+ { cwd, reject: false },
12
+ )
13
+ return result.exitCode === 0
14
+ }
15
+ const opener = process.platform === 'darwin' ? 'open' : 'xdg-open'
16
+ const result = await execa(opener, [path], { reject: false })
17
+ return result.exitCode === 0
18
+ }
@@ -0,0 +1,84 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import type { Variant } from '@/slides/styles'
3
+
4
+ export interface DeckMeta {
5
+ title: string
6
+ variant: Variant
7
+ }
8
+
9
+ export interface Slide {
10
+ layout: string
11
+ title: string
12
+ content: string[]
13
+ }
14
+
15
+ export interface Deck {
16
+ meta: DeckMeta
17
+ slides: Slide[]
18
+ }
19
+
20
+ const DEFAULT_META: DeckMeta = {
21
+ title: '',
22
+ variant: 'light',
23
+ }
24
+
25
+ export function parseSlidesDoc(path: string): Deck {
26
+ return parseSlides(readFileSync(path, 'utf8'))
27
+ }
28
+
29
+ export function parseSlides(raw: string): Deck {
30
+ const { meta, body } = splitFrontmatter(raw)
31
+ const slides = body
32
+ .split(/^---\s*$/m)
33
+ .map(parseSlide)
34
+ .filter((slide): slide is Slide => slide !== null)
35
+ return { meta, slides }
36
+ }
37
+
38
+ function splitFrontmatter(raw: string): { meta: DeckMeta; body: string } {
39
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n?/)
40
+ if (!match) return { meta: { ...DEFAULT_META }, body: raw }
41
+ return { meta: parseMeta(match[1]), body: raw.slice(match[0].length) }
42
+ }
43
+
44
+ function parseMeta(block: string): DeckMeta {
45
+ const meta: DeckMeta = { ...DEFAULT_META }
46
+ for (const line of block.split('\n')) {
47
+ const match = line.match(/^(\w+):\s*(.+?)\s*$/)
48
+ if (!match) continue
49
+ const [, key, value] = match
50
+ if (key === 'title') meta.title = value
51
+ if (key === 'variant' && (value === 'light' || value === 'dark')) {
52
+ meta.variant = value
53
+ }
54
+ }
55
+ return meta
56
+ }
57
+
58
+ function parseSlide(chunk: string): Slide | null {
59
+ let title = ''
60
+ let layout = ''
61
+ const content: string[] = []
62
+ for (const line of chunk.split('\n')) {
63
+ const heading = line.match(/^#\s+(.+?)\s*$/)
64
+ const declared = line.match(/^layout:\s*(.+?)\s*$/)
65
+ if (heading && !title) {
66
+ title = heading[1]
67
+ continue
68
+ }
69
+ if (declared && !layout) {
70
+ layout = declared[1].trim()
71
+ continue
72
+ }
73
+ content.push(line)
74
+ }
75
+ const trimmed = content.filter((line) => line.trim())
76
+ if (!title && !layout && trimmed.length === 0) return null
77
+ return { layout: layout || inferLayout(trimmed), title, content: trimmed }
78
+ }
79
+
80
+ function inferLayout(content: string[]): string {
81
+ return content.some((line) => line.trim().startsWith('-'))
82
+ ? 'bullets'
83
+ : 'title'
84
+ }
@@ -0,0 +1,88 @@
1
+ import { copyFileSync, mkdirSync } from 'node:fs'
2
+ import { basename, join } from 'node:path'
3
+ import PptxGenJS from 'pptxgenjs'
4
+ import {
5
+ addFooter,
6
+ type DeckNav,
7
+ renderDeckSlide,
8
+ renderToc,
9
+ } from '@/slides/layouts'
10
+ import { type Deck, parseSlidesDoc } from '@/slides/parse'
11
+ import { buildTheme, type Variant } from '@/slides/styles'
12
+
13
+ export interface RenderResult {
14
+ pptxPath: string
15
+ mirrorPath?: string
16
+ slideCount: number
17
+ }
18
+
19
+ export interface RenderOptions {
20
+ variant?: Variant
21
+ mirror?: string
22
+ }
23
+
24
+ export async function renderSlidesDoc(
25
+ sourcePath: string,
26
+ outDir: string,
27
+ options: RenderOptions = {},
28
+ ): Promise<RenderResult> {
29
+ const deck = parseSlidesDoc(sourcePath)
30
+ const variant = options.variant ?? deck.meta.variant
31
+ const theme = buildTheme(variant)
32
+
33
+ const pptx = new PptxGenJS()
34
+ pptx.layout = 'LAYOUT_WIDE'
35
+ if (deck.meta.title) pptx.title = deck.meta.title
36
+
37
+ const nav = buildNav(deck)
38
+ for (const slide of deck.slides) {
39
+ const target = pptx.addSlide()
40
+ target.background = { color: theme.background }
41
+ if (slide.layout === 'toc') {
42
+ renderToc(target, slide, theme, nav)
43
+ } else {
44
+ renderDeckSlide(target, slide, theme)
45
+ }
46
+ addFooter(target, slide, theme, nav)
47
+ }
48
+
49
+ mkdirSync(outDir, { recursive: true })
50
+ const fileName = outputName(sourcePath)
51
+ const pptxPath = join(outDir, fileName)
52
+ await pptx.writeFile({ fileName: pptxPath })
53
+
54
+ const mirrorPath = options.mirror
55
+ ? mirrorDeck(pptxPath, options.mirror, fileName)
56
+ : undefined
57
+ return { pptxPath, mirrorPath, slideCount: deck.slides.length }
58
+ }
59
+
60
+ function mirrorDeck(
61
+ pptxPath: string,
62
+ mirrorDir: string,
63
+ fileName: string,
64
+ ): string {
65
+ mkdirSync(mirrorDir, { recursive: true })
66
+ const mirrorPath = join(mirrorDir, fileName)
67
+ copyFileSync(pptxPath, mirrorPath)
68
+ return mirrorPath
69
+ }
70
+
71
+ export function buildNav(deck: Deck): DeckNav {
72
+ let tocSlide: number | undefined
73
+ const sections: DeckNav['sections'] = []
74
+ deck.slides.forEach((slide, index) => {
75
+ const slideNumber = index + 1
76
+ if (slide.layout === 'toc' && tocSlide === undefined) {
77
+ tocSlide = slideNumber
78
+ }
79
+ if (slide.layout === 'section') {
80
+ sections.push({ title: slide.title, slideNumber })
81
+ }
82
+ })
83
+ return { deckTitle: deck.meta.title, tocSlide, sections }
84
+ }
85
+
86
+ function outputName(sourcePath: string): string {
87
+ return basename(sourcePath).replace(/\.md$/, '') + '.pptx'
88
+ }
@@ -0,0 +1,44 @@
1
+ export type Variant = 'light' | 'dark'
2
+
3
+ export interface Theme {
4
+ background: string
5
+ surface: string
6
+ ink: string
7
+ muted: string
8
+ accent: string
9
+ }
10
+
11
+ const LIGHT: Theme = {
12
+ background: 'FAF7F2',
13
+ surface: 'F4EFE6',
14
+ ink: '1A1815',
15
+ muted: '7A736A',
16
+ accent: 'A4471C',
17
+ }
18
+
19
+ const DARK: Theme = {
20
+ background: '1A1815',
21
+ surface: '23201C',
22
+ ink: 'F4EFE6',
23
+ muted: 'A39C92',
24
+ accent: 'C8602E',
25
+ }
26
+
27
+ export const FONTS = {
28
+ heading: 'Arial',
29
+ body: 'Calibri',
30
+ } as const
31
+
32
+ export const TYPE = {
33
+ cover: 44,
34
+ section: 40,
35
+ title: 30,
36
+ body: 16,
37
+ stat: 54,
38
+ quote: 30,
39
+ caption: 11,
40
+ } as const
41
+
42
+ export function buildTheme(variant: Variant): Theme {
43
+ return variant === 'dark' ? DARK : LIGHT
44
+ }
@@ -0,0 +1,66 @@
1
+ import { readdirSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { isDirectory } from '@/target'
4
+
5
+ export const BASE_CATEGORY = 'base'
6
+ /** A real category resolving to zero entries, for a caller declining on purpose. */
7
+ export const NONE_CATEGORY = 'none'
8
+
9
+ export function snippetsSourceDir(root: string): string {
10
+ return join(root, 'snippets')
11
+ }
12
+
13
+ /**
14
+ * Lists the folder categories. `base` is the synthetic name for snippets
15
+ * sitting directly under `snippets/`, so it is not a directory and callers
16
+ * prepend it themselves.
17
+ *
18
+ * Nothing is filtered: toolkit-internal snippets are authored under `internal/`,
19
+ * which this never reads, so every category found here is publishable.
20
+ */
21
+ export function listFolderCategories(root: string): string[] {
22
+ const dir = snippetsSourceDir(root)
23
+ if (!isDirectory(dir)) return []
24
+
25
+ return readdirSync(dir, { withFileTypes: true })
26
+ .filter((entry) => entry.isDirectory())
27
+ .map((entry) => entry.name)
28
+ .sort()
29
+ }
30
+
31
+ export function listCategories(root: string): string[] {
32
+ return [BASE_CATEGORY, ...listFolderCategories(root)]
33
+ }
34
+
35
+ export function categoryDir(root: string, category: string): string {
36
+ const source = snippetsSourceDir(root)
37
+ return category === BASE_CATEGORY ? source : join(source, category)
38
+ }
39
+
40
+ /**
41
+ * Tests for a directory rather than mere existence. `snippets/` holds files
42
+ * alongside its category folders, so an argument naming one of them resolves
43
+ * to a real path that cannot be scanned as a category.
44
+ *
45
+ * An empty category refuses rather than resolving, since `join(source, '')`
46
+ * is `source` and would otherwise report the base directory as the category,
47
+ * widening an unset value to every base entry instead of naming none.
48
+ */
49
+ export function categoryExists(root: string, category: string): boolean {
50
+ if (category === '') return false
51
+ return isDirectory(categoryDir(root, category))
52
+ }
53
+
54
+ /**
55
+ * Lists one category's entry slugs. Only the immediate level is read, matching
56
+ * the `find -maxdepth 1` the bash used, so a nested folder is its own category
57
+ * rather than part of its parent.
58
+ */
59
+ export function listEntries(root: string, category: string): string[] {
60
+ const dir = categoryDir(root, category)
61
+ if (!isDirectory(dir)) return []
62
+
63
+ return [...new Bun.Glob('*.md').scanSync({ cwd: dir, onlyFiles: true })]
64
+ .map((name) => name.slice(0, -'.md'.length))
65
+ .sort()
66
+ }
@@ -0,0 +1,32 @@
1
+ import { listCategories, listEntries } from '@/snippets/categories'
2
+ import { loadPresets } from '@/snippets/presets'
3
+
4
+ export interface CategoryCatalogEntry {
5
+ readonly name: string
6
+ readonly entries: readonly string[]
7
+ }
8
+
9
+ export interface SnippetsCatalog {
10
+ readonly presets: readonly {
11
+ readonly name: string
12
+ readonly slugs: readonly string[]
13
+ }[]
14
+ readonly categories: readonly CategoryCatalogEntry[]
15
+ }
16
+
17
+ /**
18
+ * Builds the catalog both output modes read, so the human list and the `--json`
19
+ * one cannot describe different corpora.
20
+ */
21
+ export function buildSnippetsCatalog(root: string): SnippetsCatalog {
22
+ return {
23
+ presets: loadPresets(root).map((preset) => ({
24
+ name: preset.name,
25
+ slugs: preset.slugs,
26
+ })),
27
+ categories: listCategories(root).map((name) => ({
28
+ name,
29
+ entries: listEntries(root, name),
30
+ })),
31
+ }
32
+ }
@@ -0,0 +1,50 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { snippetsSourceDir } from '@/snippets/categories'
4
+
5
+ export interface Preset {
6
+ readonly name: string
7
+ readonly slugs: readonly string[]
8
+ }
9
+
10
+ export function presetsPath(root: string): string {
11
+ return join(snippetsSourceDir(root), 'snippets.toml')
12
+ }
13
+
14
+ /**
15
+ * Reads every preset from `snippets.toml`. The bash read section headers with
16
+ * `grep '^\['` and then re-walked the file per preset with a `BASH_REMATCH`
17
+ * loop, so a name carrying a bracket or a slug split across lines parsed by
18
+ * accident rather than by rule.
19
+ */
20
+ export function loadPresets(root: string): Preset[] {
21
+ const path = presetsPath(root)
22
+ if (!existsSync(path)) return []
23
+
24
+ const parsed = Bun.TOML.parse(readFileSync(path, 'utf8')) as Record<
25
+ string,
26
+ unknown
27
+ >
28
+ const presets: Preset[] = []
29
+
30
+ for (const [name, table] of Object.entries(parsed)) {
31
+ if (typeof table !== 'object' || table === null || Array.isArray(table)) {
32
+ continue
33
+ }
34
+ const names = (table as Record<string, unknown>).names
35
+ presets.push({
36
+ name,
37
+ slugs: Array.isArray(names)
38
+ ? names.filter(
39
+ (slug): slug is string => typeof slug === 'string' && slug !== '',
40
+ )
41
+ : [],
42
+ })
43
+ }
44
+
45
+ return presets.sort((left, right) => left.name.localeCompare(right.name))
46
+ }
47
+
48
+ export function findPreset(root: string, name: string): Preset | undefined {
49
+ return loadPresets(root).find((preset) => preset.name === name)
50
+ }
@@ -0,0 +1,132 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs'
2
+ import { basename, join } from 'node:path'
3
+ import { $ } from 'bun'
4
+ import { gitEnv } from '@/git-env'
5
+ import { resolveBaseRef } from '@/git-files'
6
+ import { INDEX_FILE, standardsSourceDir } from '@/standards/read'
7
+
8
+ /** Returned when a standard new to this branch carries no `## Success criterion` section, the gating check. */
9
+ export const EXIT_MISSING_CRITERION = 2
10
+
11
+ /** Matched at any casing, level-2 only, per the heading `standards/standard.md` itself uses. */
12
+ const CRITERION_HEADING = /^##\s+success criterion\s*$/im
13
+
14
+ /**
15
+ * The reasons an audit produces no reading. `no-corpus` is the ordinary state
16
+ * of a target that authors no standards of its own, the same absence the
17
+ * skills audit reads as its own `no-corpus`. The other two are a broken git
18
+ * invocation rather than a project stating nothing.
19
+ */
20
+ export type StandardsAuditRefusal =
21
+ | 'no-corpus'
22
+ | 'no-base'
23
+ | 'unreadable-arrivals'
24
+
25
+ export type StandardsAudit =
26
+ | {
27
+ readonly kind: 'measured'
28
+ readonly base: string
29
+ readonly standards: readonly string[]
30
+ readonly withCriterion: readonly string[]
31
+ readonly withoutCriterion: readonly string[]
32
+ readonly arrivals: readonly string[]
33
+ readonly arrivalsWithoutCriterion: readonly string[]
34
+ }
35
+ | { readonly kind: 'refused'; readonly reason: StandardsAuditRefusal }
36
+
37
+ /**
38
+ * Measures the corpus authored at `standards/` under `root` against the
39
+ * `## Success criterion` gate `standards/standard.md` states, and names which
40
+ * of those files are new since the branch's merge base.
41
+ *
42
+ * Reads the working-root corpus alone, never the packaged fallback
43
+ * `src/standards/read.ts` falls through to for a name lookup, since a target
44
+ * with no authored standards of its own has nothing here to gate.
45
+ */
46
+ export async function auditStandards(root: string): Promise<StandardsAudit> {
47
+ const dir = standardsSourceDir(root)
48
+ if (!existsSync(dir)) return { kind: 'refused', reason: 'no-corpus' }
49
+
50
+ const standards = readdirSync(dir, { withFileTypes: true })
51
+ .filter(
52
+ (entry) =>
53
+ entry.isFile() &&
54
+ entry.name.endsWith('.md') &&
55
+ entry.name !== INDEX_FILE,
56
+ )
57
+ .map((entry) => entry.name)
58
+ .sort()
59
+
60
+ const withCriterion: string[] = []
61
+ const withoutCriterion: string[] = []
62
+ for (const name of standards) {
63
+ const body = readFileSync(join(dir, name), 'utf8')
64
+ ;(CRITERION_HEADING.test(body) ? withCriterion : withoutCriterion).push(
65
+ name,
66
+ )
67
+ }
68
+
69
+ const base = await resolveBaseRef(root)
70
+ if (base === undefined) return { kind: 'refused', reason: 'no-base' }
71
+
72
+ const arrived = await arrivedStandards(root, base)
73
+ if (arrived === undefined) {
74
+ return { kind: 'refused', reason: 'unreadable-arrivals' }
75
+ }
76
+
77
+ const arrivals = standards.filter((name) => arrived.has(name))
78
+ const arrivalsWithoutCriterion = arrivals.filter((name) =>
79
+ withoutCriterion.includes(name),
80
+ )
81
+
82
+ return {
83
+ kind: 'measured',
84
+ base,
85
+ standards,
86
+ withCriterion,
87
+ withoutCriterion,
88
+ arrivals,
89
+ arrivalsWithoutCriterion,
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Only an arrival missing the section sets a failing code. Every other
95
+ * standard without one is a known gap `standards/standard.md` names rather
96
+ * than a violation, so failing the push on the 26 already there teaches
97
+ * contributors to route around the stage.
98
+ */
99
+ export function auditExitCode(audit: StandardsAudit): number {
100
+ if (audit.kind === 'refused') return 1
101
+ return audit.arrivalsWithoutCriterion.length > 0 ? EXIT_MISSING_CRITERION : 0
102
+ }
103
+
104
+ /**
105
+ * Filenames under `standards/` present in the working tree and absent at
106
+ * `base`: a plain add, with rename detection forced off so a standard moved
107
+ * into the corpus from elsewhere counts the same as one authored fresh.
108
+ */
109
+ async function arrivedStandards(
110
+ root: string,
111
+ base: string,
112
+ ): Promise<Set<string> | undefined> {
113
+ const [added, untracked] = await Promise.all([
114
+ $`git -C ${root} diff --no-renames --name-only --diff-filter=A ${base} -- standards`
115
+ .env(gitEnv())
116
+ .quiet()
117
+ .nothrow(),
118
+ $`git -C ${root} ls-files --others --exclude-standard -- standards`
119
+ .env(gitEnv())
120
+ .quiet()
121
+ .nothrow(),
122
+ ])
123
+
124
+ if (added.exitCode !== 0 || untracked.exitCode !== 0) return undefined
125
+
126
+ const paths = [
127
+ ...added.text().split('\n'),
128
+ ...untracked.text().split('\n'),
129
+ ].filter(Boolean)
130
+
131
+ return new Set(paths.map((path) => basename(path)))
132
+ }
@@ -0,0 +1,100 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
2
+ import { basename, join } from 'node:path'
3
+ import { stripFrontmatter } from '@/frontmatter'
4
+ import { PROJECT_ROOT } from '@/project-root'
5
+
6
+ export const INDEX_FILE = 'index.md'
7
+
8
+ /** The folder a repository authors standards in, toolkit or project alike. */
9
+ export function standardsSourceDir(root: string): string {
10
+ return join(root, 'standards')
11
+ }
12
+
13
+ /**
14
+ * Spells the package root in a report where every other root spells a
15
+ * project-relative path. The `source` field promises a path inside the project
16
+ * and a package copy is the one source that promise cannot cover, so the
17
+ * spelling has to be one nothing will join to a project root.
18
+ */
19
+ const PACKAGE_LABEL = '<canon>'
20
+
21
+ export interface StandardRoot {
22
+ /** Absolute directory to search. */
23
+ readonly dir: string
24
+ /** How a report spells a copy found under this root. */
25
+ readonly label: string
26
+ }
27
+
28
+ export interface ResolvedStandard {
29
+ readonly path: string
30
+ /** The label of the root that won, joined to the filename. */
31
+ readonly source: string
32
+ }
33
+
34
+ /**
35
+ * The roots a standard resolves against. No toolkit standard installs into a
36
+ * project any more, so `.claude/standards/` is gone from the list, no repository
37
+ * generates one, and a target
38
+ * carries neither entry below: the package corpus is the only root that
39
+ * answers there, which is what leaves no precedence to reason about.
40
+ *
41
+ * The authoring root stays ahead of the package corpus for the repository that
42
+ * writes standards, where an edit in progress has to win over the copy
43
+ * `package.json` shipped. A project that authors standards of its own is the
44
+ * same case under the same folder.
45
+ */
46
+ export function standardRoots(root: string): StandardRoot[] {
47
+ return [
48
+ { dir: standardsSourceDir(root), label: 'standards' },
49
+ {
50
+ dir: standardsSourceDir(PROJECT_ROOT),
51
+ label: join(PACKAGE_LABEL, 'standards'),
52
+ },
53
+ ]
54
+ }
55
+
56
+ function filename(name: string): string {
57
+ return name.endsWith('.md') ? name : `${name}.md`
58
+ }
59
+
60
+ /** Resolves a standard by name, with or without the extension. */
61
+ export function resolveStandard(
62
+ root: string,
63
+ name: string,
64
+ ): ResolvedStandard | undefined {
65
+ const file = filename(name)
66
+
67
+ for (const { dir, label } of standardRoots(root)) {
68
+ const path = join(dir, file)
69
+ if (existsSync(path)) return { path, source: join(label, file) }
70
+ }
71
+
72
+ return undefined
73
+ }
74
+
75
+ /**
76
+ * Names every standard a resolve could reach, deduplicated across the roots and
77
+ * sorted. Shown when a name misses, so it doubles as the answer to what the
78
+ * caller should have typed.
79
+ */
80
+ export function listStandards(root: string): string[] {
81
+ const names = new Set<string>()
82
+
83
+ for (const { dir } of standardRoots(root)) {
84
+ if (!existsSync(dir)) continue
85
+
86
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
87
+ if (!entry.isFile()) continue
88
+ if (!entry.name.endsWith('.md')) continue
89
+ if (entry.name === INDEX_FILE) continue
90
+
91
+ names.add(basename(entry.name, '.md'))
92
+ }
93
+ }
94
+
95
+ return [...names].sort()
96
+ }
97
+
98
+ export function readStandard(standard: ResolvedStandard): string {
99
+ return stripFrontmatter(readFileSync(standard.path, 'utf8'))
100
+ }