@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,389 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
2
+ import { basename, dirname, extname, join, relative, resolve } from 'node:path'
3
+ import type { Command } from 'commander'
4
+ import { INSTALL_BROWSER, isEngineMissing } from '@/browser/engine'
5
+ import { parseDraft } from '@/demo/beats'
6
+ import { compilePlan, parsePlan, unresolved } from '@/demo/compile'
7
+ import { convertToMp4, INSTALL_CONVERTER } from '@/demo/container'
8
+ import { DEFAULT_CURSORS } from '@/demo/cursors'
9
+ import { loadCursorTheme } from '@/demo/theme'
10
+ import { intro, logError, logInfo, logStep, logWarn, outro, plural } from '@/ui'
11
+
12
+ const DEFAULT_OUT = 'demos'
13
+
14
+ /**
15
+ * Holds wiring only. Every browser reference sits behind `loadDriver`, because
16
+ * `src/cli.ts` imports this module at startup and resolving the engine there
17
+ * would put a browser launch in front of every other command.
18
+ */
19
+ type Driver = typeof import('@/demo/drive')
20
+
21
+ interface CompileOptions {
22
+ readonly out: string
23
+ readonly slug?: string
24
+ readonly force?: boolean
25
+ readonly json?: boolean
26
+ }
27
+
28
+ interface RunOptions {
29
+ readonly out?: string
30
+ readonly cursor?: string
31
+ readonly video: boolean
32
+ readonly still: boolean
33
+ readonly json?: boolean
34
+ }
35
+
36
+ export function register(program: Command): void {
37
+ const demo = program
38
+ .command('demo')
39
+ .description('Drive a running application and record what it did')
40
+ .helpOption('-h, --help', 'Show this help message')
41
+
42
+ demo
43
+ .command('compile')
44
+ .description('Turn a screencast draft into a plan a run can drive')
45
+ .argument('<draft>', 'Screencast draft written by canon-screencast')
46
+ .helpOption('-h, --help', 'Show this help message')
47
+ .option('-o, --out <dir>', 'Directory the plan is written to', DEFAULT_OUT)
48
+ .option('-s, --slug <slug>', 'Plan name, defaulting to the draft filename')
49
+ .option('--force', 'Overwrite a plan that already exists')
50
+ .option('--json', 'Add a machine-readable record on stdout')
51
+ .addHelpText(
52
+ 'after',
53
+ [
54
+ '',
55
+ 'The plan is committed rather than scratch. It carries the target, the',
56
+ 'wait condition, and the timing a beat lacks, and the timing is a',
57
+ 'starting point you tune, which is why a recompile refuses to overwrite.',
58
+ '',
59
+ 'Exit codes:',
60
+ ' 0 a plan was written',
61
+ ' 1 refused, with the reason on stderr',
62
+ '',
63
+ 'Examples:',
64
+ ' canon demo compile .claude/.tmp/screencast/inline-edit.md',
65
+ ' canon demo compile draft.md --out demos --force',
66
+ '',
67
+ ].join('\n'),
68
+ )
69
+ .action(async (draft: string, opts: CompileOptions) => {
70
+ process.exitCode = runCompile(draft, opts)
71
+ })
72
+
73
+ demo
74
+ .command('run')
75
+ .description('Drive the application a plan names and write the recording')
76
+ .argument('<plan>', 'Plan written by canon demo compile')
77
+ .helpOption('-h, --help', 'Show this help message')
78
+ .option('-o, --out <dir>', 'Directory to write into, overriding the plan')
79
+ .option(
80
+ '-c, --cursor <dir>',
81
+ 'Cursor theme folder to draw the pointer from',
82
+ )
83
+ .option('--no-video', 'Skip the recording and write only the still')
84
+ .option('--no-still', 'Skip the still and write only the recording')
85
+ .option('--json', 'Add a machine-readable record on stdout')
86
+ .addHelpText(
87
+ 'after',
88
+ [
89
+ '',
90
+ 'Needs a browser binary. Install it with:',
91
+ ` ${INSTALL_BROWSER}`,
92
+ '',
93
+ 'Writes mp4 beside the webm when ffmpeg is on PATH, and skips it',
94
+ `otherwise without failing the run. Install it with: ${INSTALL_CONVERTER}`,
95
+ '',
96
+ 'Exit codes:',
97
+ ' 0 the recording and the still were written',
98
+ ' 1 refused, with the reason on stderr',
99
+ '',
100
+ 'Examples:',
101
+ ' canon demo run demos/inline-edit.json',
102
+ ' canon demo run demos/inline-edit.json --cursor ~/cursors/theme',
103
+ '',
104
+ ].join('\n'),
105
+ )
106
+ .action(async (plan: string, opts: RunOptions) => {
107
+ process.exitCode = await runDrive(plan, opts)
108
+ })
109
+ }
110
+
111
+ function runCompile(draftPath: string, opts: CompileOptions): number {
112
+ intro('canon demo compile')
113
+
114
+ const source = resolve(process.cwd(), draftPath)
115
+ if (!existsSync(source)) {
116
+ logStep('Draft')
117
+ logError(`${draftPath} not found`)
118
+ outro()
119
+ emit(opts.json, { draft: source, reason: 'draft-missing' })
120
+ return 1
121
+ }
122
+
123
+ const parsed = parseDraft(readFileSync(source, 'utf8'))
124
+ if (parsed.status === 'failed') {
125
+ logStep('Draft')
126
+ logError(`${display(source)}: ${parsed.reason}`)
127
+ outro()
128
+ emit(opts.json, {
129
+ draft: source,
130
+ reason: 'draft-unreadable',
131
+ message: parsed.reason,
132
+ })
133
+ return 1
134
+ }
135
+
136
+ const slug = opts.slug ?? basename(source, extname(source))
137
+ const target = resolve(process.cwd(), opts.out, `${slug}.json`)
138
+
139
+ if (existsSync(target) && !opts.force) {
140
+ logStep('Plan')
141
+ logError(`${display(target)} already exists`)
142
+ // Stated rather than implied, because the value at risk is timing the
143
+ // operator tuned by watching a recording and the draft cannot reproduce it.
144
+ logWarn('Pass --force to overwrite it, losing any timing tuned by hand.')
145
+ outro()
146
+ emit(opts.json, { plan: target, reason: 'plan-exists' })
147
+ return 1
148
+ }
149
+
150
+ const plan = compilePlan(parsed.draft, { slug, outDir: opts.out })
151
+ mkdirSync(dirname(target), { recursive: true })
152
+ writeFileSync(target, `${JSON.stringify(plan, null, 2)}\n`)
153
+
154
+ logStep('Draft')
155
+ logInfo(`${display(source)} ${plural(parsed.draft.beats.length, 'beat')}`)
156
+
157
+ logStep('Plan')
158
+ logInfo(display(target))
159
+
160
+ const outstanding = unresolved(plan)
161
+ logStep('Outstanding')
162
+ if (outstanding.length === 0) {
163
+ logInfo('Nothing to fill, so the plan runs as written.')
164
+ } else {
165
+ logWarn(
166
+ `${plural(outstanding.length, 'field')} the draft could not supply:`,
167
+ )
168
+ for (const field of outstanding) logWarn(` ${field}`)
169
+ }
170
+ outro()
171
+
172
+ emit(opts.json, {
173
+ draft: source,
174
+ plan: target,
175
+ beats: parsed.draft.beats.length,
176
+ unresolved: outstanding,
177
+ })
178
+ return 0
179
+ }
180
+
181
+ async function runDrive(planPath: string, opts: RunOptions): Promise<number> {
182
+ intro('canon demo run')
183
+
184
+ const source = resolve(process.cwd(), planPath)
185
+ if (!existsSync(source)) {
186
+ logStep('Plan')
187
+ logError(`${planPath} not found`)
188
+ outro()
189
+ emit(opts.json, { plan: source, reason: 'plan-missing' })
190
+ return 1
191
+ }
192
+
193
+ const parsed = parsePlan(readFileSync(source, 'utf8'))
194
+ if (parsed.status === 'failed') {
195
+ logStep('Plan')
196
+ logError(`${display(source)}: ${parsed.reason}`)
197
+ outro()
198
+ emit(opts.json, {
199
+ plan: source,
200
+ reason: 'plan-unreadable',
201
+ message: parsed.reason,
202
+ })
203
+ return 1
204
+ }
205
+
206
+ if (!opts.video && !opts.still) {
207
+ logStep('Output')
208
+ logError('--no-video and --no-still together leave nothing to write')
209
+ outro()
210
+ emit(opts.json, { plan: source, reason: 'no-output-requested' })
211
+ return 1
212
+ }
213
+
214
+ const outstanding = unresolved(parsed.plan)
215
+ if (outstanding.length) {
216
+ logStep('Plan')
217
+ logError(
218
+ `${display(source)} has ${plural(outstanding.length, 'field')} to fill`,
219
+ )
220
+ for (const field of outstanding) logWarn(` ${field}`)
221
+ outro()
222
+ emit(opts.json, {
223
+ plan: source,
224
+ reason: 'plan-unresolved',
225
+ unresolved: outstanding,
226
+ })
227
+ return 1
228
+ }
229
+
230
+ logStep('Plan')
231
+ logInfo(`${display(source)} ${plural(parsed.plan.steps.length, 'step')}`)
232
+
233
+ const cursors = resolveCursors(opts.cursor)
234
+ if (cursors.status === 'failed') {
235
+ logError(cursors.reason)
236
+ outro()
237
+ emit(opts.json, { plan: source, reason: 'cursor-unreadable' })
238
+ return 1
239
+ }
240
+ logInfo(cursors.label)
241
+
242
+ const driver = await loadDriver()
243
+ if (!driver) {
244
+ logStep('Browser')
245
+ logError('the browser engine is not installed in this project')
246
+ logWarn(`Install it with: ${INSTALL_BROWSER}`)
247
+ outro()
248
+ emit(opts.json, {
249
+ plan: source,
250
+ reason: 'engine-missing',
251
+ install: INSTALL_BROWSER,
252
+ })
253
+ return 1
254
+ }
255
+
256
+ logStep('Recording')
257
+ const result = await driver.drive({
258
+ plan: parsed.plan,
259
+ cursors: cursors.value,
260
+ ...(opts.video
261
+ ? { videoPath: outputPath(parsed.plan.output.video, opts.out) }
262
+ : {}),
263
+ ...(opts.still
264
+ ? { stillPath: outputPath(parsed.plan.output.still, opts.out) }
265
+ : {}),
266
+ })
267
+
268
+ if (result.status === 'failed') {
269
+ logError(result.message.split('\n')[0] ?? 'the run failed')
270
+ if (result.reason === 'browser-missing') {
271
+ logWarn(`Install the browser binary with: ${INSTALL_BROWSER}`)
272
+ }
273
+ outro()
274
+ emit(opts.json, {
275
+ plan: source,
276
+ reason: result.reason,
277
+ message: result.message,
278
+ ...(result.reason === 'browser-missing'
279
+ ? { install: INSTALL_BROWSER }
280
+ : {}),
281
+ })
282
+ return 1
283
+ }
284
+
285
+ if (result.videoPath) logInfo(display(result.videoPath))
286
+ if (result.stillPath) logInfo(`${display(result.stillPath)} still`)
287
+ logInfo(
288
+ `${result.steps} steps in ${Math.round(result.durationMs / 100) / 10}s`,
289
+ )
290
+
291
+ let mp4Path: string | undefined
292
+ let mp4Reason: string | undefined
293
+ if (result.videoPath) {
294
+ logStep('Container')
295
+ const converted = await convertToMp4(result.videoPath)
296
+ if (converted.status === 'converted') {
297
+ mp4Path = converted.mp4Path
298
+ logInfo(display(mp4Path))
299
+ } else if (converted.status === 'skipped') {
300
+ mp4Reason = converted.reason
301
+ logWarn('ffmpeg is not installed, so no mp4 was written.')
302
+ logWarn(`Install it with: ${INSTALL_CONVERTER}`)
303
+ } else {
304
+ mp4Reason = converted.reason
305
+ logWarn(`mp4 conversion failed: ${converted.reason}`)
306
+ }
307
+ }
308
+
309
+ outro()
310
+
311
+ emit(opts.json, {
312
+ plan: source,
313
+ video: result.videoPath ?? null,
314
+ mp4: mp4Path ?? null,
315
+ still: result.stillPath ?? null,
316
+ steps: result.steps,
317
+ durationMs: result.durationMs,
318
+ ...(mp4Reason ? { mp4Reason } : {}),
319
+ })
320
+ return 0
321
+ }
322
+
323
+ type CursorChoice =
324
+ | { status: 'ready'; value: typeof DEFAULT_CURSORS; label: string }
325
+ | { status: 'failed'; reason: string }
326
+
327
+ /**
328
+ * A theme contributes per state rather than per folder, so a folder carrying an
329
+ * arrow and no hand still supplies its arrow and the bundled artwork covers the
330
+ * rest.
331
+ */
332
+ function resolveCursors(dir: string | undefined): CursorChoice {
333
+ if (!dir) {
334
+ return {
335
+ status: 'ready',
336
+ value: DEFAULT_CURSORS,
337
+ label: 'pointer drawn from the bundled artwork',
338
+ }
339
+ }
340
+
341
+ const loaded = loadCursorTheme(resolve(process.cwd(), dir), DEFAULT_CURSORS)
342
+ if (loaded.status === 'failed')
343
+ return { status: 'failed', reason: loaded.reason }
344
+
345
+ return {
346
+ status: 'ready',
347
+ value: loaded.cursors,
348
+ label: `pointer drawn from ${display(resolve(process.cwd(), dir))} for ${loaded.states.join(', ')}`,
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Reports absence only when the module or its engine cannot be resolved, which
354
+ * is the case a target hits before installing the browser package. Any other
355
+ * import failure is a defect inside the driver and propagates, rather than
356
+ * being reported as a missing dependency.
357
+ */
358
+ async function loadDriver(): Promise<Driver | undefined> {
359
+ try {
360
+ return await import('@/demo/drive')
361
+ } catch (error) {
362
+ if (isEngineMissing(error)) return undefined
363
+ throw error
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Resolves where one artifact lands. `--out` replaces the directory the plan
369
+ * names rather than acting as a root the plan's own directory hangs off, which
370
+ * would nest the output path inside itself on every run that passes both.
371
+ */
372
+ function outputPath(planned: string, out: string | undefined): string {
373
+ const relativePath = out ? join(out, basename(planned)) : planned
374
+ return resolve(process.cwd(), relativePath)
375
+ }
376
+
377
+ function emit(json: boolean | undefined, record: unknown): void {
378
+ if (json) process.stdout.write(`${JSON.stringify(record)}\n`)
379
+ }
380
+
381
+ /**
382
+ * Keeps a path clickable in the operator's terminal. A path outside the project
383
+ * reports absolute, since a relative path to it is a run of `..` segments no
384
+ * editor resolves.
385
+ */
386
+ function display(path: string): string {
387
+ const fromCwd = relative(process.cwd(), path)
388
+ return fromCwd.startsWith('..') ? path : fromCwd
389
+ }
@@ -0,0 +1,173 @@
1
+ import { resolve } from 'node:path'
2
+ import type { Command } from 'commander'
3
+ import {
4
+ type Advisory,
5
+ auditDependencies,
6
+ type AuditRefusal,
7
+ countBySeverity,
8
+ SEVERITIES,
9
+ } from '@/deps/audit'
10
+ import {
11
+ intro,
12
+ logInfo,
13
+ logStep,
14
+ logWarn,
15
+ outro,
16
+ pipeOutput,
17
+ plural,
18
+ } from '@/ui'
19
+
20
+ interface AuditCommandOptions {
21
+ readonly json?: boolean
22
+ }
23
+
24
+ /** What a reader does about each way the advisory list fails to arrive. */
25
+ const REFUSALS: Record<AuditRefusal, string> = {
26
+ 'no-manifest': 'No package.json here, so there is no dependency set to read.',
27
+ 'no-lockfile':
28
+ 'No lockfile beside the manifest, so no dependency set is resolved yet. Install first.',
29
+ 'no-record':
30
+ 'The advisory lookup returned no record. Check the network, then re-run.',
31
+ }
32
+
33
+ export function register(program: Command): void {
34
+ const deps = program
35
+ .command('deps')
36
+ .description('Read the installed dependency set for published advisories')
37
+ .helpOption('-h, --help', 'Show this help message')
38
+
39
+ deps
40
+ .command('audit')
41
+ .description('Report advisories against the dependencies already resolved')
42
+ .argument('[path]', 'Project to audit, defaulting to the current directory')
43
+ .helpOption('-h, --help', 'Show this help message')
44
+ .option('--json', 'Add a machine-readable record on stdout')
45
+ .addHelpText(
46
+ 'after',
47
+ [
48
+ '',
49
+ 'Scope:',
50
+ " The resolved dependency set, read through the runtime's own",
51
+ ' advisory command. This reaches a network, so a lookup that fails',
52
+ ' refuses rather than reporting a clean tree.',
53
+ '',
54
+ 'Exit codes:',
55
+ ' 0 no advisory against the resolved set',
56
+ ' 1 refused, with the reason on stderr',
57
+ ' 2 at least one advisory was published',
58
+ '',
59
+ 'Examples:',
60
+ ' canon deps audit',
61
+ ' canon deps audit --json',
62
+ '',
63
+ ].join('\n'),
64
+ )
65
+ .action(async (path: string | undefined, opts: AuditCommandOptions) => {
66
+ process.exitCode = await runAudit(path, opts)
67
+ })
68
+ }
69
+
70
+ function severityLine(counts: Record<string, number>): string {
71
+ return SEVERITIES.filter((severity) => counts[severity] !== 0)
72
+ .map((severity) => `${counts[severity]} ${severity}`)
73
+ .join(', ')
74
+ }
75
+
76
+ /**
77
+ * Groups by package, since one dependency commonly carries several advisories
78
+ * and a flat list reads as more distinct upgrades than the tree actually owes.
79
+ */
80
+ function byPackage(advisories: readonly Advisory[]): Map<string, Advisory[]> {
81
+ const grouped = new Map<string, Advisory[]>()
82
+
83
+ for (const advisory of advisories) {
84
+ const held = grouped.get(advisory.package) ?? []
85
+ held.push(advisory)
86
+ grouped.set(advisory.package, held)
87
+ }
88
+
89
+ return grouped
90
+ }
91
+
92
+ async function runAudit(
93
+ path: string | undefined,
94
+ opts: AuditCommandOptions,
95
+ ): Promise<number> {
96
+ const root = resolve(path ?? process.cwd())
97
+ const emitJson = opts.json ?? false
98
+
99
+ intro('canon deps audit')
100
+
101
+ const audit = await auditDependencies(root)
102
+
103
+ if (audit.kind === 'refused') {
104
+ logStep('Refused')
105
+ logWarn(REFUSALS[audit.reason])
106
+ if (audit.message !== undefined) logWarn(audit.message)
107
+ outro()
108
+
109
+ if (emitJson) {
110
+ process.stdout.write(
111
+ `${JSON.stringify({
112
+ root,
113
+ reason: audit.reason,
114
+ message: audit.message ?? REFUSALS[audit.reason],
115
+ })}\n`,
116
+ )
117
+ }
118
+ return 1
119
+ }
120
+
121
+ const counts = countBySeverity(audit.advisories)
122
+
123
+ logStep('Advisories')
124
+ if (audit.advisories.length === 0) {
125
+ logInfo('No advisory against the resolved dependency set.')
126
+ } else {
127
+ const grouped = byPackage(audit.advisories)
128
+ const total = audit.advisories.length
129
+
130
+ logWarn(
131
+ `${total} ${total === 1 ? 'advisory' : 'advisories'} across ${plural(grouped.size, 'package')}: ${severityLine(counts)}`,
132
+ )
133
+
134
+ // Piped rather than logged line by line, since every one of these is a
135
+ // finding and the timeline's own tick would mark each as something that
136
+ // passed. The frame stays, and the list inside it stays unmarked.
137
+ pipeOutput(
138
+ [...grouped]
139
+ .map(([name, held]) =>
140
+ [
141
+ `${name}: ${severityLine(countBySeverity(held))}`,
142
+ ...held.flatMap((advisory) => [
143
+ ` ${advisory.severity.padEnd(8)} ${advisory.title}`,
144
+ ` ${' '.repeat(8)} ${advisory.url}`,
145
+ ]),
146
+ ].join('\n'),
147
+ )
148
+ .join('\n'),
149
+ )
150
+ }
151
+
152
+ // Stated whichever way the count went. An advisory arrives when someone
153
+ // publishes one, so this number moves with no edit here, and a report that
154
+ // does not date itself reads as a fact about the tree rather than about a day.
155
+ logStep('Reading')
156
+ logInfo(
157
+ 'Measured against the advisory index at run time, not at commit time.',
158
+ )
159
+
160
+ outro()
161
+
162
+ if (emitJson) {
163
+ process.stdout.write(
164
+ `${JSON.stringify({
165
+ root,
166
+ severities: counts,
167
+ advisories: audit.advisories,
168
+ })}\n`,
169
+ )
170
+ }
171
+
172
+ return audit.advisories.length === 0 ? 0 : 2
173
+ }
@@ -0,0 +1,36 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import type { Command } from 'commander'
4
+ import { renderDesignDoc } from '@/design/render'
5
+ import { palette } from '@/ui'
6
+
7
+ export function register(program: Command): void {
8
+ const design = program
9
+ .command('design')
10
+ .description('Design system commands (render)')
11
+
12
+ design
13
+ .command('render')
14
+ .description('Render DESIGN.md tokens to HTML and CSS preview')
15
+ .option('-s, --source <path>', 'Source DESIGN.md path', '.claude/DESIGN.md')
16
+ .option('-o, --out <path>', 'Output directory', '.claude/review/design')
17
+ .action((opts: { source: string; out: string }) => {
18
+ const sourcePath = resolve(process.cwd(), opts.source)
19
+ const outDir = resolve(process.cwd(), opts.out)
20
+ const { GREEN, GREY, NC, RED, WHITE } = palette(process.stderr)
21
+ if (!existsSync(sourcePath)) {
22
+ process.stderr.write(
23
+ `${GREY}┌${NC}\n${GREY}│${NC} ${RED}✗${NC} ${opts.source} not found\n${GREY}└${NC}\n`,
24
+ )
25
+ process.exitCode = 1
26
+ return
27
+ }
28
+ process.stderr.write(
29
+ `${GREY}┌${NC}\n${GREY}│${NC} ${WHITE}Render design tokens${NC}\n`,
30
+ )
31
+ const result = renderDesignDoc(sourcePath, outDir)
32
+ process.stderr.write(
33
+ `${GREY}│${NC} ${GREEN}✓${NC} ${result.htmlPath}\n${GREY}│${NC} ${GREEN}✓${NC} ${result.cssPath}\n${GREY}└${NC}\n`,
34
+ )
35
+ })
36
+ }
@@ -0,0 +1,60 @@
1
+ import type { Command } from 'commander'
2
+ import { registerPassThroughVerbs } from '@/commands/pass-through'
3
+ import { listTopics, readTopic, resolveTopic } from '@/docs/read'
4
+ import { execScript } from '@/exec'
5
+ import { PROJECT_ROOT } from '@/project-root'
6
+ import { intro, logError, logInfo, logStep, logWarn, outro } from '@/ui'
7
+
8
+ export function register(program: Command): void {
9
+ const docs = program
10
+ .command('docs')
11
+ .description('Emit toolkit reference docs (list, <topic>)')
12
+ .argument('[topic]', 'Doc to print, by exact name')
13
+ .helpOption('-h, --help', 'Show this help message')
14
+ .addHelpText(
15
+ 'after',
16
+ [
17
+ '',
18
+ 'Examples:',
19
+ ' canon docs list',
20
+ ' canon docs list --json',
21
+ ' canon docs agents',
22
+ '',
23
+ ].join('\n'),
24
+ )
25
+ .action(async (topic: string | undefined) => {
26
+ if (topic === undefined) {
27
+ intro('canon docs')
28
+ await execScript('docs/list.sh', [])
29
+ return
30
+ }
31
+
32
+ process.exitCode = get(topic)
33
+ })
34
+
35
+ registerPassThroughVerbs(docs, 'docs', ['list'])
36
+ }
37
+
38
+ /**
39
+ * Writes the document body to stdout and every frame line to stderr, so a
40
+ * caller capturing the output with `$(...)` receives the document alone.
41
+ */
42
+ function get(topic: string): number {
43
+ intro('canon docs')
44
+
45
+ const resolved = resolveTopic(PROJECT_ROOT, topic)
46
+
47
+ if (!resolved) {
48
+ logWarn(`Unknown topic: ${topic}`)
49
+ logStep('Available topics')
50
+ for (const name of listTopics(PROJECT_ROOT)) logInfo(name)
51
+ logError("Run 'canon docs list' for descriptions.")
52
+ outro()
53
+ return 1
54
+ }
55
+
56
+ logStep(resolved.rel)
57
+ process.stdout.write(readTopic(resolved))
58
+ outro()
59
+ return 0
60
+ }
@@ -0,0 +1,23 @@
1
+ const SLUG_FALLBACK = 'general'
2
+ const SLUG_MAX_LENGTH = 40
3
+ const TITLE_MAX_LENGTH = 72
4
+
5
+ function surfaceField(body: string): string | undefined {
6
+ return body.match(/\*\*Surface:\*\*\s*([^\n]+)/i)?.[1]?.trim()
7
+ }
8
+
9
+ export function deriveSlug(body: string): string {
10
+ const slug = (surfaceField(body) ?? '')
11
+ .toLowerCase()
12
+ .replace(/[^a-z0-9]+/g, '-')
13
+ .replace(/^-+|-+$/g, '')
14
+ .slice(0, SLUG_MAX_LENGTH)
15
+ .replace(/-+$/g, '')
16
+ return slug || SLUG_FALLBACK
17
+ }
18
+
19
+ export function deriveTitle(body: string): string {
20
+ const surface = surfaceField(body)
21
+ const title = surface ? `feedback: ${surface}` : 'toolkit feedback'
22
+ return title.slice(0, TITLE_MAX_LENGTH)
23
+ }