@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,153 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { execa } from 'execa'
4
+
5
+ /**
6
+ * The severities the advisory index publishes, worst first.
7
+ *
8
+ * `info` is this module's own floor rather than one of theirs. An advisory
9
+ * arriving under a severity nothing here recognizes still exists, and dropping
10
+ * it would shrink the count on the day the vocabulary changed.
11
+ */
12
+ export const SEVERITIES = [
13
+ 'critical',
14
+ 'high',
15
+ 'moderate',
16
+ 'low',
17
+ 'info',
18
+ ] as const
19
+
20
+ export type Severity = (typeof SEVERITIES)[number]
21
+
22
+ export interface Advisory {
23
+ readonly package: string
24
+ readonly id: number
25
+ readonly title: string
26
+ readonly url: string
27
+ readonly severity: Severity
28
+ readonly vulnerableVersions?: string
29
+ }
30
+
31
+ /**
32
+ * Why no advisory list was produced, which is never the same as a clean one.
33
+ *
34
+ * `no-lockfile` is split out from `no-record` because the two take different
35
+ * remedies. An unreachable index is retried, and a project whose dependencies
36
+ * were never resolved is installed first, so one message naming the network
37
+ * would send half the readers at the wrong cause.
38
+ */
39
+ export type AuditRefusal = 'no-manifest' | 'no-lockfile' | 'no-record'
40
+
41
+ /** The lockfiles a resolved dependency set leaves behind, in any manager. */
42
+ const LOCKFILES = [
43
+ 'bun.lock',
44
+ 'bun.lockb',
45
+ 'package-lock.json',
46
+ 'yarn.lock',
47
+ 'pnpm-lock.yaml',
48
+ ]
49
+
50
+ export type DepsAudit =
51
+ | { readonly kind: 'audited'; readonly advisories: readonly Advisory[] }
52
+ | {
53
+ readonly kind: 'refused'
54
+ readonly reason: AuditRefusal
55
+ readonly message?: string
56
+ }
57
+
58
+ function severityOf(value: unknown): Severity {
59
+ return SEVERITIES.includes(value as Severity) ? (value as Severity) : 'info'
60
+ }
61
+
62
+ /**
63
+ * Reads the record `bun audit --json` writes to stdout.
64
+ *
65
+ * The shape is an object keyed by package name, each holding that package's
66
+ * advisories, which this flattens into one list carrying the name on every
67
+ * entry. Returning nothing on unreadable output is what keeps an unreachable
68
+ * index from reporting as a clean tree, and the caller turns it into a stated
69
+ * refusal rather than a zero.
70
+ */
71
+ export function parseAdvisories(stdout: string): Advisory[] | undefined {
72
+ let record: unknown
73
+ try {
74
+ record = JSON.parse(stdout)
75
+ } catch {
76
+ return undefined
77
+ }
78
+
79
+ if (typeof record !== 'object' || record === null || Array.isArray(record)) {
80
+ return undefined
81
+ }
82
+
83
+ const advisories: Advisory[] = []
84
+ for (const [name, entries] of Object.entries(record)) {
85
+ if (!Array.isArray(entries)) return undefined
86
+
87
+ for (const raw of entries) {
88
+ if (typeof raw !== 'object' || raw === null) return undefined
89
+ const entry = raw as Record<string, unknown>
90
+
91
+ advisories.push({
92
+ package: name,
93
+ id: typeof entry.id === 'number' ? entry.id : 0,
94
+ title: typeof entry.title === 'string' ? entry.title : '',
95
+ url: typeof entry.url === 'string' ? entry.url : '',
96
+ severity: severityOf(entry.severity),
97
+ ...(typeof entry.vulnerable_versions === 'string' && {
98
+ vulnerableVersions: entry.vulnerable_versions,
99
+ }),
100
+ })
101
+ }
102
+ }
103
+
104
+ return advisories
105
+ }
106
+
107
+ export function countBySeverity(
108
+ advisories: readonly Advisory[],
109
+ ): Record<Severity, number> {
110
+ const counts = Object.fromEntries(
111
+ SEVERITIES.map((severity) => [severity, 0]),
112
+ ) as Record<Severity, number>
113
+
114
+ for (const advisory of advisories) counts[advisory.severity] += 1
115
+ return counts
116
+ }
117
+
118
+ /**
119
+ * Shells the runtime's own advisory command rather than carrying an index.
120
+ *
121
+ * A vendored advisory database is a second corpus to keep current, and what
122
+ * this check is worth is the report rather than the data. The cost is the one
123
+ * failure mode no other audit here carries: the command reaches a network, so
124
+ * an unreachable index has to be told from a tree with nothing against it.
125
+ * That split is the return value, and the exit code is deliberately not read.
126
+ * `bun audit` exits non-zero on advisories found and on a lookup that failed,
127
+ * so the record on stdout is the only thing that separates them.
128
+ */
129
+ export async function auditDependencies(root: string): Promise<DepsAudit> {
130
+ if (!existsSync(join(root, 'package.json'))) {
131
+ return { kind: 'refused', reason: 'no-manifest' }
132
+ }
133
+
134
+ if (!LOCKFILES.some((name) => existsSync(join(root, name)))) {
135
+ return { kind: 'refused', reason: 'no-lockfile' }
136
+ }
137
+
138
+ const result = await execa('bun', ['audit', '--json'], {
139
+ cwd: root,
140
+ reject: false,
141
+ })
142
+
143
+ const advisories = parseAdvisories(result.stdout)
144
+ if (advisories === undefined) {
145
+ return {
146
+ kind: 'refused',
147
+ reason: 'no-record',
148
+ message: result.stderr.trim().split('\n').pop() ?? 'no output on stdout',
149
+ }
150
+ }
151
+
152
+ return { kind: 'audited', advisories }
153
+ }
@@ -0,0 +1,116 @@
1
+ import { readFileSync } from 'node:fs'
2
+
3
+ /**
4
+ * One table cell, split into the value a renderer emits and whether the record
5
+ * marked it as unsourced.
6
+ *
7
+ * The marker sits inside the cell rather than in a trailing column, because a
8
+ * trailing marker breaks the table parse. Splitting it out here is what keeps a
9
+ * swatch or a font sample built from the value alone.
10
+ */
11
+ export interface Cell {
12
+ value: string
13
+ tagged: boolean
14
+ }
15
+
16
+ export type Row = Record<string, Cell>
17
+
18
+ export interface DesignDoc {
19
+ personality: string
20
+ color: Row[]
21
+ typography: Row[]
22
+ spacing: Row[]
23
+ borders: Row[]
24
+ motion: string
25
+ iconography: string
26
+ }
27
+
28
+ const VERIFY_TAG = /\s*\?\s*verify\s*$/
29
+
30
+ /** A cell whose whole content is one balanced code span and nothing else. */
31
+ const CODE_SPAN = /^`([^`]*)`$/
32
+
33
+ export function parseDesignDoc(path: string): DesignDoc {
34
+ const raw = readFileSync(path, 'utf8')
35
+ const sections = splitSections(raw)
36
+ return {
37
+ personality: prose(sections['Personality']),
38
+ color: table(sections['Color']),
39
+ typography: table(sections['Typography']),
40
+ spacing: table(sections['Spacing']),
41
+ borders: table(sections['Borders']),
42
+ motion: prose(sections['Motion']),
43
+ iconography: prose(sections['Iconography']),
44
+ }
45
+ }
46
+
47
+ function splitSections(raw: string): Record<string, string> {
48
+ const out: Record<string, string> = {}
49
+ const lines = raw.split('\n')
50
+ let current: string | null = null
51
+ let buffer: string[] = []
52
+ for (const line of lines) {
53
+ const match = line.match(/^##\s+(.+?)\s*$/)
54
+ if (match) {
55
+ if (current) out[current] = buffer.join('\n')
56
+ current = match[1]
57
+ buffer = []
58
+ } else if (current) {
59
+ buffer.push(line)
60
+ }
61
+ }
62
+ if (current) out[current] = buffer.join('\n')
63
+ return out
64
+ }
65
+
66
+ function prose(body: string | undefined): string {
67
+ if (!body) return ''
68
+ return body
69
+ .split('\n')
70
+ .filter((l) => l.trim() && !l.startsWith('|') && !l.startsWith('<'))
71
+ .join('\n')
72
+ .trim()
73
+ }
74
+
75
+ function table(body: string | undefined): Row[] {
76
+ if (!body) return []
77
+ const rows = body.split('\n').filter((l) => l.trim().startsWith('|'))
78
+ if (rows.length < 2) return []
79
+ const headers = splitRow(rows[0]).map((c) => c.value)
80
+ const data = rows.slice(2)
81
+ return data.map((line) => {
82
+ const cells = splitRow(line)
83
+ const row: Row = {}
84
+ headers.forEach((h, i) => {
85
+ row[h] = cells[i] ?? emptyCell()
86
+ })
87
+ return row
88
+ })
89
+ }
90
+
91
+ function emptyCell(): Cell {
92
+ return { value: '', tagged: false }
93
+ }
94
+
95
+ function splitRow(line: string): Cell[] {
96
+ return line
97
+ .replace(/^\s*\|/, '')
98
+ .replace(/\|\s*$/, '')
99
+ .split('|')
100
+ .map(parseCell)
101
+ }
102
+
103
+ /**
104
+ * The tag is tested against the cell with any surrounding code span removed,
105
+ * since a value wrapping itself in backticks puts one after the tag and an
106
+ * end-anchored test misses it there. The span is restored around the clean
107
+ * value so an untagged cell and a tagged one carry the same formatting.
108
+ */
109
+ function parseCell(raw: string): Cell {
110
+ const trimmed = raw.trim()
111
+ const span = trimmed.match(CODE_SPAN)
112
+ const inner = span ? span[1] : trimmed
113
+ if (!VERIFY_TAG.test(inner)) return { value: trimmed, tagged: false }
114
+ const value = inner.replace(VERIFY_TAG, '')
115
+ return { value: span ? `\`${value}\`` : value, tagged: true }
116
+ }
@@ -0,0 +1,249 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import type { Cell, DesignDoc, Row } from '@/design/parse'
4
+ import { parseDesignDoc } from '@/design/parse'
5
+
6
+ export interface RenderResult {
7
+ htmlPath: string
8
+ cssPath: string
9
+ }
10
+
11
+ export function renderDesignDoc(
12
+ sourcePath: string,
13
+ outDir: string,
14
+ ): RenderResult {
15
+ const doc = parseDesignDoc(sourcePath)
16
+ mkdirSync(outDir, { recursive: true })
17
+ const cssPath = join(outDir, 'design.css')
18
+ const htmlPath = join(outDir, 'index.html')
19
+ writeFileSync(cssPath, buildCss(doc))
20
+ writeFileSync(htmlPath, buildHtml(doc))
21
+ return { htmlPath, cssPath }
22
+ }
23
+
24
+ function slug(s: string): string {
25
+ return s
26
+ .toLowerCase()
27
+ .replace(/[^a-z0-9]+/g, '-')
28
+ .replace(/(^-|-$)/g, '')
29
+ }
30
+
31
+ function cell(row: Row, key: string): Cell {
32
+ return row[key] ?? { value: '', tagged: false }
33
+ }
34
+
35
+ /** The value alone. Every swatch, sample, and custom property is built from it. */
36
+ function val(row: Row, key: string): string {
37
+ return cell(row, key).value
38
+ }
39
+
40
+ /** The marker, rendered beside a value rather than inside it. */
41
+ function mark(row: Row, key: string): string {
42
+ return cell(row, key).tagged
43
+ ? ' <span class="verify" title="No source anchors this value">? verify</span>'
44
+ : ''
45
+ }
46
+
47
+ /** A displayed cell: its escaped text, then its marker when it carries one. */
48
+ function cellText(row: Row, key: string): string {
49
+ return escape(val(row, key)) + mark(row, key)
50
+ }
51
+
52
+ interface Confidence {
53
+ tagged: number
54
+ total: number
55
+ }
56
+
57
+ /**
58
+ * The columns the confidence ratio reads, fixed by `standards/design.md`. The
59
+ * first column of each table names its row, and `Multiplier` and `When used`
60
+ * restate what the row already carries, so none of them is something a source
61
+ * could anchor and none belongs in the denominator.
62
+ */
63
+ const ANCHORABLE = {
64
+ borders: ['Radius', 'Width'],
65
+ color: ['Intent', 'Value'],
66
+ spacing: ['Value'],
67
+ typography: ['Family', 'Weight', 'Size', 'Line height'],
68
+ } as const
69
+
70
+ /**
71
+ * A cell counts when it carries a tag, or when it holds a value in a column a
72
+ * source could anchor. A blank the record left unfilled is neither, and so is a
73
+ * row name. Counting a tagged cell whichever column it sits in is what keeps a
74
+ * marker the preview draws from sitting outside the ratio printed beside it.
75
+ */
76
+ function confidence(doc: DesignDoc): Confidence {
77
+ const tables: ReadonlyArray<readonly [Row[], readonly string[]]> = [
78
+ [doc.color, ANCHORABLE.color],
79
+ [doc.typography, ANCHORABLE.typography],
80
+ [doc.spacing, ANCHORABLE.spacing],
81
+ [doc.borders, ANCHORABLE.borders],
82
+ ]
83
+ let tagged = 0
84
+ let total = 0
85
+ for (const [rows, columns] of tables) {
86
+ for (const row of rows) {
87
+ for (const [key, c] of Object.entries(row)) {
88
+ if (!c.tagged && (!c.value || !columns.includes(key))) continue
89
+ total += 1
90
+ if (c.tagged) tagged += 1
91
+ }
92
+ }
93
+ }
94
+ return { tagged, total }
95
+ }
96
+
97
+ function buildCss(doc: DesignDoc): string {
98
+ const lines: string[] = [':root {']
99
+ for (const row of doc.color) {
100
+ if (val(row, 'Value')) {
101
+ lines.push(` --color-${slug(val(row, 'Role'))}: ${val(row, 'Value')};`)
102
+ }
103
+ }
104
+ for (const row of doc.spacing) {
105
+ if (val(row, 'Value')) {
106
+ lines.push(` --space-${slug(val(row, 'Step'))}: ${val(row, 'Value')};`)
107
+ }
108
+ }
109
+ for (const row of doc.typography) {
110
+ if (val(row, 'Size')) {
111
+ lines.push(
112
+ ` --type-${slug(val(row, 'Role'))}-size: ${val(row, 'Size')};`,
113
+ )
114
+ }
115
+ if (val(row, 'Line height')) {
116
+ lines.push(
117
+ ` --type-${slug(val(row, 'Role'))}-lh: ${val(row, 'Line height')};`,
118
+ )
119
+ }
120
+ }
121
+ for (const row of doc.borders) {
122
+ if (val(row, 'Radius')) {
123
+ lines.push(` --radius-${slug(val(row, 'Role'))}: ${val(row, 'Radius')};`)
124
+ }
125
+ }
126
+ lines.push('}')
127
+ return lines.join('\n') + '\n'
128
+ }
129
+
130
+ function escape(s: string): string {
131
+ return s
132
+ .replace(/&/g, '&amp;')
133
+ .replace(/</g, '&lt;')
134
+ .replace(/>/g, '&gt;')
135
+ .replace(/"/g, '&quot;')
136
+ }
137
+
138
+ function buildHtml(doc: DesignDoc): string {
139
+ const sections = [
140
+ sectionPersonality(doc.personality),
141
+ sectionColor(doc.color),
142
+ sectionTypography(doc.typography),
143
+ sectionSpacing(doc.spacing),
144
+ sectionBorders(doc.borders),
145
+ sectionLine('Motion', doc.motion),
146
+ sectionLine('Iconography', doc.iconography),
147
+ ]
148
+ const { tagged, total } = confidence(doc)
149
+ const verifyStyle = tagged
150
+ ? '\n .verify { color: #a4471c; font-size: 12px; font-weight: 600; margin-left: 0.35rem; white-space: nowrap; }'
151
+ : ''
152
+ const verb = tagged === 1 ? 'carries' : 'carry'
153
+ const summary = tagged
154
+ ? `\n<p class="note">${total - tagged} of ${total} cells are anchored to a source. The other ${tagged} ${verb} <code>? verify</code>, so nothing anchors them yet.</p>`
155
+ : ''
156
+ return `<!doctype html>
157
+ <html lang="en">
158
+ <head>
159
+ <meta charset="utf-8">
160
+ <title>Design tokens</title>
161
+ <link rel="stylesheet" href="design.css">
162
+ <style>
163
+ body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 960px; color: #222; }
164
+ h1 { margin-top: 0; }
165
+ h2 { margin-top: 2rem; border-bottom: 1px solid #ddd; padding-bottom: 0.25rem; }
166
+ table { border-collapse: collapse; width: 100%; margin-top: 0.5rem; }
167
+ th, td { text-align: left; padding: 0.5rem 0.75rem; border-bottom: 1px solid #eee; font-size: 14px; }
168
+ th { background: #f7f7f7; font-weight: 600; }
169
+ .swatch { display: inline-block; width: 1.5rem; height: 1.5rem; border-radius: 4px; border: 1px solid #ddd; vertical-align: middle; margin-right: 0.5rem; }
170
+ .bar { display: inline-block; height: 1rem; background: #888; border-radius: 2px; vertical-align: middle; }
171
+ .note { color: #666; font-size: 13px; margin-top: 0.5rem; }
172
+ .empty { color: #999; font-style: italic; }${verifyStyle}
173
+ </style>
174
+ </head>
175
+ <body>
176
+ <h1>Design tokens</h1>
177
+ <p class="note">Generated from <code>.claude/DESIGN.md</code> by <code>canon design render</code>. Token preview only, not a screen mock.</p>${summary}
178
+ ${sections.join('\n')}
179
+ </body>
180
+ </html>
181
+ `
182
+ }
183
+
184
+ function sectionPersonality(text: string): string {
185
+ if (!text) return ''
186
+ return `<h2>Personality</h2>\n<p>${escape(text)}</p>`
187
+ }
188
+
189
+ function sectionColor(rows: Row[]): string {
190
+ if (!rows.length) return ''
191
+ const body = rows
192
+ .map((r) => {
193
+ const value = val(r, 'Value')
194
+ const swatch = value
195
+ ? `<span class="swatch" style="background:${escape(value)}"></span>`
196
+ : '<span class="swatch"></span>'
197
+ const shown = value ? escape(value) : '<span class="empty">unset</span>'
198
+ return `<tr><td>${swatch}${cellText(r, 'Role')}</td><td>${cellText(r, 'Intent')}</td><td><code>${shown}</code>${mark(r, 'Value')}</td></tr>`
199
+ })
200
+ .join('\n')
201
+ return `<h2>Color</h2>\n<table><thead><tr><th>Role</th><th>Intent</th><th>Value</th></tr></thead><tbody>${body}</tbody></table>`
202
+ }
203
+
204
+ function sectionTypography(rows: Row[]): string {
205
+ if (!rows.length) return ''
206
+ const body = rows
207
+ .map((r) => {
208
+ const family = val(r, 'Family') || 'system-ui'
209
+ const weight = val(r, 'Weight') || '400'
210
+ const size = val(r, 'Size') || '16px'
211
+ const lh = val(r, 'Line height') || '1.4'
212
+ const sample = `<span style="font-family:${escape(family)};font-weight:${escape(weight)};font-size:${escape(size)};line-height:${escape(lh)}">The quick brown fox</span>`
213
+ return `<tr><td>${cellText(r, 'Role')}</td><td>${escape(family)}${mark(r, 'Family')}</td><td>${escape(weight)}${mark(r, 'Weight')}</td><td>${escape(size)}${mark(r, 'Size')}</td><td>${escape(lh)}${mark(r, 'Line height')}</td><td>${sample}</td></tr>`
214
+ })
215
+ .join('\n')
216
+ return `<h2>Typography</h2>\n<table><thead><tr><th>Role</th><th>Family</th><th>Weight</th><th>Size</th><th>Line height</th><th>Sample</th></tr></thead><tbody>${body}</tbody></table>`
217
+ }
218
+
219
+ function sectionSpacing(rows: Row[]): string {
220
+ if (!rows.length) return ''
221
+ const body = rows
222
+ .map((r) => {
223
+ const value = val(r, 'Value')
224
+ const bar = value
225
+ ? `<span class="bar" style="width:${escape(value)}"></span>`
226
+ : '<span class="empty">unset</span>'
227
+ return `<tr><td>${cellText(r, 'Step')}</td><td>${cellText(r, 'Multiplier')}</td><td><code>${escape(value || 'unset')}</code>${mark(r, 'Value')}</td><td>${bar}</td></tr>`
228
+ })
229
+ .join('\n')
230
+ return `<h2>Spacing</h2>\n<table><thead><tr><th>Step</th><th>Multiplier</th><th>Value</th><th>Sample</th></tr></thead><tbody>${body}</tbody></table>`
231
+ }
232
+
233
+ function sectionBorders(rows: Row[]): string {
234
+ if (!rows.length) return ''
235
+ const body = rows
236
+ .map((r) => {
237
+ const radius = val(r, 'Radius') || '0'
238
+ const width = val(r, 'Width') || '1px'
239
+ const sample = `<span style="display:inline-block;width:2rem;height:1.5rem;background:#eee;border:${escape(width)} solid #888;border-radius:${escape(radius)};vertical-align:middle"></span>`
240
+ return `<tr><td>${cellText(r, 'Role')}</td><td><code>${escape(radius)}</code>${mark(r, 'Radius')}</td><td><code>${escape(width)}</code>${mark(r, 'Width')}</td><td>${cellText(r, 'When used')}</td><td>${sample}</td></tr>`
241
+ })
242
+ .join('\n')
243
+ return `<h2>Borders</h2>\n<table><thead><tr><th>Role</th><th>Radius</th><th>Width</th><th>When used</th><th>Sample</th></tr></thead><tbody>${body}</tbody></table>`
244
+ }
245
+
246
+ function sectionLine(title: string, text: string): string {
247
+ if (!text) return ''
248
+ return `<h2>${title}</h2>\n<p>${escape(text)}</p>`
249
+ }
@@ -0,0 +1,77 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { basename, dirname, join } from 'node:path'
3
+ import { stripFrontmatter } from '@/frontmatter'
4
+
5
+ const INDEX_TOPIC = 'index'
6
+
7
+ /**
8
+ * The two roots a topic resolves against, in precedence order. `docs/` holds
9
+ * consumer-facing reference and `.claude/context/` holds per-domain internal
10
+ * narrative, so a name present in both resolves to the consumer-facing copy.
11
+ */
12
+ const ROOTS: readonly string[] = ['docs', join('.claude', 'context')]
13
+
14
+ export interface ResolvedTopic {
15
+ readonly path: string
16
+ readonly rel: string
17
+ }
18
+
19
+ /**
20
+ * A domain too large for one file splits into `<domain>/` with a generated
21
+ * `index.md`, so a topic names either a sibling file or such a folder. Both
22
+ * spellings resolve to the same name a caller types.
23
+ */
24
+ export function resolveTopic(
25
+ root: string,
26
+ topic: string,
27
+ ): ResolvedTopic | undefined {
28
+ for (const dir of ROOTS) {
29
+ const candidates = [
30
+ join(dir, `${topic}.md`),
31
+ join(dir, topic, `${INDEX_TOPIC}.md`),
32
+ ]
33
+
34
+ for (const rel of candidates) {
35
+ const path = join(root, rel)
36
+ if (existsSync(path)) return { path, rel }
37
+ }
38
+ }
39
+
40
+ return undefined
41
+ }
42
+
43
+ /**
44
+ * Names every topic a `get` could resolve, listed per root in the order the
45
+ * roots are searched. Shown when a topic misses, so it doubles as the answer to
46
+ * what the caller should have typed.
47
+ */
48
+ export function listTopics(root: string): string[] {
49
+ const topics: string[] = []
50
+
51
+ for (const dir of ROOTS) {
52
+ const cwd = join(root, dir)
53
+ if (!existsSync(cwd)) continue
54
+
55
+ const files = [
56
+ ...new Bun.Glob('*.md').scanSync({ cwd, onlyFiles: true, dot: true }),
57
+ ]
58
+ .map((name) => basename(name, '.md'))
59
+ .filter((name) => name !== INDEX_TOPIC)
60
+
61
+ const folders = [
62
+ ...new Bun.Glob(`*/${INDEX_TOPIC}.md`).scanSync({
63
+ cwd,
64
+ onlyFiles: true,
65
+ dot: true,
66
+ }),
67
+ ].map((name) => dirname(name))
68
+
69
+ topics.push(...[...files, ...folders].sort())
70
+ }
71
+
72
+ return topics
73
+ }
74
+
75
+ export function readTopic(topic: ResolvedTopic): string {
76
+ return stripFrontmatter(readFileSync(topic.path, 'utf8'))
77
+ }
package/src/exec.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { join } from 'node:path'
2
+ import { execa } from 'execa'
3
+ import { PROJECT_ROOT } from '@/project-root'
4
+
5
+ export async function execScript(
6
+ script: string,
7
+ args: string[],
8
+ ): Promise<void> {
9
+ const scriptPath = join(PROJECT_ROOT, 'scripts', script)
10
+ const result = await execa(scriptPath, args, {
11
+ stdio: 'inherit',
12
+ env: { ...process.env, PROJECT_ROOT },
13
+ reject: false,
14
+ })
15
+ process.exit(result.exitCode ?? 1)
16
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Escapes every character a regular expression would read as syntax.
3
+ *
4
+ * The token became a parameter when this was extracted, and the two callers
5
+ * pass letters and hyphens alone. That is what makes escaping cheap here rather
6
+ * than a fix for a live defect: an unescaped token holding a dot matches the
7
+ * wrong lines and one holding a parenthesis throws, and neither failure is the
8
+ * caller's to anticipate.
9
+ */
10
+ function escapeForPattern(token: string): string {
11
+ return token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
12
+ }
13
+
14
+ /**
15
+ * Whether the line at `index` carries `token` with a reason after it, reading
16
+ * itself and the line above.
17
+ *
18
+ * Extracted from `src/secrets/marker.ts`, which shaped it, so the two sweeps
19
+ * that mute a line share one answer to where a marker may sit. Two lines rather
20
+ * than one, because a value is as often introduced by a preceding comment as
21
+ * annotated inline, and a format taking no trailing comment has nowhere else to
22
+ * put the marker. Nothing further up counts, so a marker cannot silence a block
23
+ * it does not sit on.
24
+ *
25
+ * Only a marker naming a reason counts. A bare token is read as a line that
26
+ * meant to say something and did not, and honoring it would let a typo mute a
27
+ * finding, where the reason is the whole value of an exemption a later reader
28
+ * has to weigh.
29
+ */
30
+ export function isMarked(
31
+ lines: readonly string[],
32
+ index: number,
33
+ token: string,
34
+ ): boolean {
35
+ const pattern = new RegExp(`${escapeForPattern(token)}:[ \\t]*\\S`)
36
+ const own = lines[index]
37
+ const above = index > 0 ? lines[index - 1] : undefined
38
+
39
+ return (
40
+ (own !== undefined && pattern.test(own)) ||
41
+ (above !== undefined && pattern.test(above))
42
+ )
43
+ }
@@ -0,0 +1,13 @@
1
+ const FRONTMATTER = /^---\n[\s\S]*?\n---\n?/
2
+
3
+ /**
4
+ * Drops a leading `---` block. Only a block starting on the first line counts,
5
+ * so a horizontal rule further down the document is left in place.
6
+ *
7
+ * The bash this replaced treated the first `---` on any line as an opening
8
+ * fence, which silently swallowed everything between two horizontal rules in a
9
+ * body. Anchoring to the first line is the correct reading.
10
+ */
11
+ export function stripFrontmatter(source: string): string {
12
+ return source.replace(FRONTMATTER, '')
13
+ }