@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,196 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { resolve } from 'node:path'
4
+ import { listRepositoryFiles } from '@/git-files'
5
+
6
+ /**
7
+ * Suppresses citation checking for the source line carrying it.
8
+ *
9
+ * Prose that teaches a naming pattern displays a path rather than pointing at
10
+ * one, and no syntax separates the two: an illustration and a reference are
11
+ * both inline code in a sentence. Location covers the fixture trees and fenced
12
+ * examples, and this covers what is left, which is a sentence naming a
13
+ * hypothetical entry to show the shape of the name.
14
+ */
15
+ export const IGNORE_MARKER = 'audit-ignore-citations'
16
+
17
+ /**
18
+ * Matches the marker plus an optional comma-separated path list.
19
+ *
20
+ * `<!-- audit-ignore-citations: .claude/standards/X.md -->` narrows the skip
21
+ * to the paths named. The bare form with no colon skips every citation the
22
+ * line carries, which is the only shape a line with nothing else worth
23
+ * checking needs and the shape every marker predating this pattern still
24
+ * carries.
25
+ */
26
+ const IGNORE_MARKER_LINE =
27
+ /<!--\s*audit-ignore-citations(?::\s*([^>]+?))?\s*-->/
28
+
29
+ interface IgnoredCitations {
30
+ readonly all: boolean
31
+ readonly paths: ReadonlySet<string>
32
+ }
33
+
34
+ /**
35
+ * Reads which citations on a line the marker excuses.
36
+ *
37
+ * A bare marker excuses every citation the line carries, matching the
38
+ * pre-named-path behavior. A named marker excuses only the paths listed,
39
+ * so a line carrying both a placeholder and a real reference keeps the real
40
+ * one checked rather than losing it to the placeholder beside it.
41
+ */
42
+ function ignoredCitations(line: string): IgnoredCitations | undefined {
43
+ const match = line.match(IGNORE_MARKER_LINE)
44
+ if (!match) return undefined
45
+ if (match[1] === undefined) return { all: true, paths: new Set() }
46
+
47
+ const paths = match[1]
48
+ .split(',')
49
+ .map((path) => path.trim())
50
+ .filter(Boolean)
51
+ return { all: false, paths: new Set(paths) }
52
+ }
53
+
54
+ /**
55
+ * Trees holding content authored to be parsed rather than followed.
56
+ *
57
+ * Sandbox scenarios describe paths inside their own scratch fixtures and the
58
+ * eval harness names paths in its target project. Neither is a reference into
59
+ * this repository, so an unresolved path there is correct rather than stale.
60
+ */
61
+ const FIXTURE_TREES: readonly string[] = ['scripts/sandbox/', 'scripts/eval/']
62
+
63
+ const FIXTURE_SEGMENTS: readonly string[] = ['fixtures', '__fixtures__']
64
+
65
+ const FENCE = /^\s*(```|~~~)/
66
+
67
+ export interface Citation {
68
+ readonly file: string
69
+ readonly line: number
70
+ readonly path: string
71
+ }
72
+
73
+ export function isFixture(rel: string): boolean {
74
+ if (rel.endsWith('.test.ts')) return true
75
+ if (FIXTURE_TREES.some((tree) => rel.startsWith(tree))) return true
76
+ return rel.split('/').some((segment) => FIXTURE_SEGMENTS.includes(segment))
77
+ }
78
+
79
+ export function citationPattern(folders: readonly string[]): RegExp {
80
+ const names = folders.map((name) =>
81
+ name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
82
+ )
83
+ return new RegExp(
84
+ `\\.claude/(?:${names.join('|')})/[A-Za-z0-9._/-]+\\.md`,
85
+ 'g',
86
+ )
87
+ }
88
+
89
+ /**
90
+ * A backticked filename carrying no folder, the form a reference takes when it
91
+ * names a sibling rather than a path.
92
+ *
93
+ * `citationPattern` spells the `.claude/` prefix and cannot see this shape at
94
+ * all, which is the reason the form rule exists. Widening that expression to
95
+ * admit a bare name was the alternative and it puts one match in the position of
96
+ * answering two questions, since a spelled path is a reference by construction
97
+ * and a bare name is a candidate whichever caller found it still has to test
98
+ * against the folder it sits in.
99
+ *
100
+ * The backticks are required rather than incidental. A filename written into
101
+ * running prose without them is not a reference a reader follows, and matching
102
+ * one would report every sentence that happens to name a file.
103
+ */
104
+ export const BARE_NAME = /`([A-Za-z0-9._-]+\.md)`/g
105
+
106
+ /**
107
+ * Pulls the cited paths out of one file's text.
108
+ *
109
+ * Fenced blocks are skipped for markdown only. A fence is markdown syntax, and
110
+ * applying it to a shell or TypeScript source would let a heredoc of triple
111
+ * backticks silently hide the rest of the file.
112
+ */
113
+ export function collectCitations(
114
+ rel: string,
115
+ text: string,
116
+ pattern: RegExp,
117
+ ): Citation[] {
118
+ const isMarkdown = rel.endsWith('.md')
119
+ const found: Citation[] = []
120
+ let fenced = false
121
+
122
+ for (const [index, line] of text.split('\n').entries()) {
123
+ if (isMarkdown && FENCE.test(line)) {
124
+ fenced = !fenced
125
+ continue
126
+ }
127
+ if (fenced) continue
128
+
129
+ const ignored = ignoredCitations(line)
130
+
131
+ for (const match of line.matchAll(pattern)) {
132
+ if (ignored && (ignored.all || ignored.paths.has(match[0]))) continue
133
+ found.push({ file: rel, line: index + 1, path: match[0] })
134
+ }
135
+ }
136
+
137
+ return found
138
+ }
139
+
140
+ /**
141
+ * `unavailable` is a distinct state from a clean scan.
142
+ *
143
+ * Finding nothing and being unable to look mean opposite things, and only one
144
+ * of them should let a push through.
145
+ */
146
+ export type CitationReport =
147
+ | {
148
+ readonly kind: 'scanned'
149
+ readonly scanned: number
150
+ readonly total: number
151
+ readonly unresolved: readonly Citation[]
152
+ }
153
+ | { readonly kind: 'unavailable' }
154
+
155
+ /**
156
+ * Resolves every cited path outside the fixture trees.
157
+ *
158
+ * This is the only check that gates the repository check, which makes it the
159
+ * only one whose false positives cost a contributor anything. It reports a
160
+ * finding solely for a path that does not resolve on disk, and the exclusions
161
+ * above are what keep that from firing on prose about paths.
162
+ */
163
+ export async function auditCitations(
164
+ root: string,
165
+ folders: readonly string[],
166
+ ): Promise<CitationReport> {
167
+ const pattern = citationPattern(folders)
168
+ const listed = await listRepositoryFiles(root)
169
+ if (!listed) return { kind: 'unavailable' }
170
+
171
+ const candidates = listed.filter((rel) => !isFixture(rel))
172
+ const citations: Citation[] = []
173
+
174
+ for (const rel of candidates) {
175
+ const path = resolve(root, rel)
176
+ if (!existsSync(path)) continue
177
+
178
+ let text: string
179
+ try {
180
+ text = await readFile(path, 'utf8')
181
+ } catch {
182
+ continue
183
+ }
184
+
185
+ citations.push(...collectCitations(rel, text, pattern))
186
+ }
187
+
188
+ return {
189
+ kind: 'scanned',
190
+ scanned: candidates.length,
191
+ total: citations.length,
192
+ unresolved: citations.filter(
193
+ (citation) => !existsSync(resolve(root, citation.path)),
194
+ ),
195
+ }
196
+ }
@@ -0,0 +1,186 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { dirname, relative, resolve } from 'node:path'
3
+ import { INDEX_FILE, listIndexes } from '@/indexes/walk'
4
+
5
+ /**
6
+ * Folder names under `.claude/` audited by default.
7
+ *
8
+ * A named list rather than the index-plus-entry contract read off disk, so a
9
+ * generated tree satisfying that contract is never measured against a rule
10
+ * written for per-domain narrative. This list names the folders
11
+ * `standards/context.md` governs, and `--folder` admits another without an edit
12
+ * here.
13
+ *
14
+ * It doubles as the citation check's scope, since `citationPattern` spells only
15
+ * these names. A `.claude/` folder left off the list is never resolved, so a
16
+ * path into one goes stale silently rather than failing a push.
17
+ */
18
+ export const DEFAULT_FOLDERS: readonly string[] = [
19
+ 'context',
20
+ 'diagrams',
21
+ 'wireframes',
22
+ ]
23
+
24
+ /** The base every folder in the default list sits under. */
25
+ const CLAUDE_BASE = '.claude'
26
+
27
+ /** The project root, reached only by a name the caller asked for. */
28
+ const ROOT_BASE = '.'
29
+
30
+ export interface AuditedFolder {
31
+ /**
32
+ * The requested folder name this was resolved under, which is what says
33
+ * which standard governs the entries. A nested split folder carries the name
34
+ * of the folder it sits beneath rather than its own, so
35
+ * `.claude/context/claude-plugin` is governed as `context`.
36
+ */
37
+ readonly name: string
38
+ /**
39
+ * The base the name resolved under, which is what says whether the folder is
40
+ * in the citation check's scope.
41
+ */
42
+ readonly base: string
43
+ /** Repo-relative folder path, used verbatim in every report line. */
44
+ readonly rel: string
45
+ readonly indexPath: string
46
+ /** Absolute paths of the folder's own entries, excluding its `index.md`. */
47
+ readonly entries: readonly string[]
48
+ /**
49
+ * Whether this is a sub-area a domain split into rather than the folder
50
+ * named under `.claude/`.
51
+ *
52
+ * The entries of a split folder describe one domain between them, so a rule
53
+ * about what a domain declares is answered by the folder. The entries of the
54
+ * named folder are one domain each, and a rule answered by a sibling there
55
+ * would let one entry stand in for domains it says nothing about.
56
+ */
57
+ readonly nested: boolean
58
+ }
59
+
60
+ /**
61
+ * Names the requested `.claude/` folders that actually exist, which is the
62
+ * citation check's scope.
63
+ *
64
+ * A skill or seed pointing into `.claude/wireframes/` is a live instruction for
65
+ * a project that carries the folder and says nothing about one that does not.
66
+ * Checking a path into an absent folder would fail eight shipped references
67
+ * here for the sole reason that this repository has no wireframes.
68
+ *
69
+ * A folder resolved at the project root is measured and stays out of this. The
70
+ * pattern the citation check builds spells the `.claude/` prefix, so admitting
71
+ * a root name there would check `.claude/<name>/` paths the audit never read.
72
+ * Widening the pattern to the root spelling is a separate change, since a bare
73
+ * `docs/x.md` appears in prose that references nothing.
74
+ */
75
+ export function presentNames(folders: readonly AuditedFolder[]): string[] {
76
+ return [
77
+ ...new Set(
78
+ folders
79
+ .filter((folder) => folder.base === CLAUDE_BASE)
80
+ .map((folder) => folder.name),
81
+ ),
82
+ ]
83
+ }
84
+
85
+ async function readEntries(dir: string): Promise<string[]> {
86
+ const paths: string[] = []
87
+
88
+ for await (const name of new Bun.Glob('*.md').scan({
89
+ cwd: dir,
90
+ onlyFiles: true,
91
+ dot: true,
92
+ })) {
93
+ if (name === INDEX_FILE) continue
94
+ paths.push(resolve(dir, name))
95
+ }
96
+
97
+ return paths.sort()
98
+ }
99
+
100
+ export interface FolderResolution {
101
+ /** Every folder that resolved, with the nested splits beneath each. */
102
+ readonly folders: readonly AuditedFolder[]
103
+ /**
104
+ * Requested names that resolved under no base, reported rather than dropped.
105
+ * Which absences are worth saying out loud is the caller's judgment: a
106
+ * default folder a project does not carry is ordinary, and a name passed by
107
+ * hand that resolves nowhere is a typo that would otherwise read as a pass.
108
+ */
109
+ readonly missing: readonly string[]
110
+ }
111
+
112
+ export interface ResolveOptions {
113
+ /**
114
+ * Whether a name may resolve at the project root when `.claude/` does not
115
+ * carry it. False for the default list, which names three folders a project
116
+ * is expected to hold under `.claude/` and nowhere else.
117
+ */
118
+ readonly canResolveAtRoot?: boolean
119
+ }
120
+
121
+ function locate(
122
+ root: string,
123
+ name: string,
124
+ bases: readonly string[],
125
+ ): { readonly dir: string; readonly base: string } | undefined {
126
+ for (const base of bases) {
127
+ const dir = resolve(root, base, name)
128
+ if (existsSync(`${dir}/${INDEX_FILE}`)) return { dir, base }
129
+ }
130
+
131
+ return undefined
132
+ }
133
+
134
+ /**
135
+ * Resolves the folders to audit under `root`.
136
+ *
137
+ * A named folder contributes its own entries plus those of every nested index
138
+ * folder beneath it, so a domain that outgrew one file and split is audited at
139
+ * the same grain as one that did not. Discovery of the nested folders runs
140
+ * through the shared walker, which is what keeps `.gitignore` and the vendored
141
+ * prune governing this scan as well as index regeneration. That prune is what
142
+ * lets a root folder be walked at all, since a name at the project root sits
143
+ * beside `node_modules` and a build output.
144
+ *
145
+ * The project root is reached only when the caller opts in, so a target holding
146
+ * a root `wireframes/` is not audited against a standard it never adopted by
147
+ * the mere act of running the command. `.claude/` still wins a name carried by
148
+ * both, and the scope line prints the resolved path so a caller reads which
149
+ * base was taken rather than inferring it.
150
+ *
151
+ * Nothing above this asks where a folder came from. A name that resolves at the
152
+ * root is measured by every rule that generalizes and gated out of the rules a
153
+ * single standard carries, which `governsContent` decides from the name.
154
+ */
155
+ export async function resolveFolders(
156
+ root: string,
157
+ names: readonly string[] = DEFAULT_FOLDERS,
158
+ { canResolveAtRoot = false }: ResolveOptions = {},
159
+ ): Promise<FolderResolution> {
160
+ const bases = canResolveAtRoot ? [CLAUDE_BASE, ROOT_BASE] : [CLAUDE_BASE]
161
+ const folders: AuditedFolder[] = []
162
+ const missing: string[] = []
163
+
164
+ for (const name of names) {
165
+ const found = locate(root, name, bases)
166
+ if (!found) {
167
+ missing.push(name)
168
+ continue
169
+ }
170
+
171
+ const dirs = [found.dir, ...(await listIndexes(found.dir)).map(dirname)]
172
+
173
+ for (const each of [...new Set(dirs)].sort()) {
174
+ folders.push({
175
+ name,
176
+ base: found.base,
177
+ rel: relative(root, each),
178
+ indexPath: `${each}/${INDEX_FILE}`,
179
+ entries: await readEntries(each),
180
+ nested: each !== found.dir,
181
+ })
182
+ }
183
+ }
184
+
185
+ return { folders, missing }
186
+ }
@@ -0,0 +1,57 @@
1
+ import type { SectionFinding } from '@/context/audit'
2
+ import type { FolderDrift } from '@/context/index-drift'
3
+
4
+ export interface GateInput {
5
+ /** Cited paths that resolved to nothing, which gate under either mode. */
6
+ readonly unresolvedCitations: number
7
+ /**
8
+ * Whether the architecture record is longer than the ceiling it derives for
9
+ * itself, which gates under either mode for the reason a citation does.
10
+ *
11
+ * False when the project carries no record and false under
12
+ * `--citations-only`, which never measures it. That mode runs one check by
13
+ * construction, so widening it here would gate on a reading it never took.
14
+ */
15
+ readonly recordOverLength: boolean
16
+ readonly sections: readonly SectionFinding[]
17
+ readonly drift: readonly FolderDrift[]
18
+ /**
19
+ * Whether the caller asked for the widened gate. False leaves a missing
20
+ * section and a drifted index advisory, which is what the project-root stage
21
+ * runs so a judgment threshold never fails a push.
22
+ */
23
+ readonly widened: boolean
24
+ }
25
+
26
+ /** Whether any folder disagrees with its own index. */
27
+ export function hasDrift(drift: readonly FolderDrift[]): boolean {
28
+ return drift.some(
29
+ (folder) => folder.unlisted.length > 0 || folder.missing.length > 0,
30
+ )
31
+ }
32
+
33
+ /**
34
+ * Whether the audit found something that should fail the caller.
35
+ *
36
+ * An unresolved citation is a broken pointer and gates unconditionally, and so
37
+ * does a record past its own ceiling: the record states the limit for itself
38
+ * and derives it from a count, which makes it the one measure here that is a
39
+ * fact rather than a threshold a reader weighs. The two findings `--gate` adds
40
+ * are the ones answerable from the file itself: a required section it does not
41
+ * declare, and an index disagreeing with its folder. Entry length, depth,
42
+ * bullet, table, provenance, and the record's claim coverage are judgments, so
43
+ * they stay out under both modes.
44
+ */
45
+ export function isGating({
46
+ unresolvedCitations,
47
+ recordOverLength,
48
+ sections,
49
+ drift,
50
+ widened,
51
+ }: GateInput): boolean {
52
+ if (unresolvedCitations > 0) return true
53
+ if (recordOverLength) return true
54
+ if (!widened) return false
55
+
56
+ return sections.length > 0 || hasDrift(drift)
57
+ }
@@ -0,0 +1,64 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { basename, dirname, resolve } from 'node:path'
4
+ import type { AuditedFolder } from '@/context/folders'
5
+
6
+ const ENTRY_LINK = /^-\s+\[[^\]]*\]\(([^)]+)\)/
7
+
8
+ export interface FolderDrift {
9
+ readonly rel: string
10
+ /** Entry files present in the folder that the index does not link. */
11
+ readonly unlisted: readonly string[]
12
+ /** Names the index links that resolve to nothing on disk. */
13
+ readonly missing: readonly string[]
14
+ }
15
+
16
+ /** Collects the link targets of an index's catalog bullets. */
17
+ export function listedTargets(source: string): string[] {
18
+ const targets: string[] = []
19
+
20
+ for (const line of source.split('\n')) {
21
+ const match = line.match(ENTRY_LINK)
22
+ if (match) targets.push(match[1])
23
+ }
24
+
25
+ return targets
26
+ }
27
+
28
+ /**
29
+ * Compares one folder's index against its siblings in both directions.
30
+ *
31
+ * Both halves are reported because they fail differently. An unlisted entry is
32
+ * invisible to a session reading the catalog to choose what to open, while a
33
+ * listed name with no file sends one to a path that opens nothing. Neither
34
+ * surfaces from reading the index alone.
35
+ */
36
+ export async function auditFolder(folder: AuditedFolder): Promise<FolderDrift> {
37
+ const dir = dirname(folder.indexPath)
38
+ const source = await readFile(folder.indexPath, 'utf8')
39
+ const targets = listedTargets(source)
40
+
41
+ // A sub-catalog is linked as `<name>/index.md` and its entries are audited
42
+ // as their own folder, so only the leading segment is compared here.
43
+ const listed = new Set(targets.map((target) => target.split('/')[0]))
44
+
45
+ const unlisted = folder.entries
46
+ .map((path) => basename(path))
47
+ .filter((name) => !listed.has(name))
48
+
49
+ const missing = targets.filter((target) => !existsSync(resolve(dir, target)))
50
+
51
+ return { rel: folder.rel, unlisted, missing }
52
+ }
53
+
54
+ export async function auditIndexes(
55
+ folders: readonly AuditedFolder[],
56
+ ): Promise<FolderDrift[]> {
57
+ const drift: FolderDrift[] = []
58
+
59
+ for (const folder of folders) {
60
+ drift.push(await auditFolder(folder))
61
+ }
62
+
63
+ return drift
64
+ }
@@ -0,0 +1,99 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { resolve } from 'node:path'
4
+
5
+ /**
6
+ * The headings a rule carries to publish the two sets.
7
+ *
8
+ * Discovery is anchored on these rather than on a filename for the reason
9
+ * `src/comments/vocabulary.ts` anchors its own: governance rules are numbered,
10
+ * and a renumber would empty the sets while the check kept reporting clean.
11
+ * Both open with `Narration` so neither loader can read the other's list.
12
+ */
13
+ export const PRONOUN_HEADING = '## Narration pronouns'
14
+ export const VERB_HEADING = '## Narration verbs'
15
+
16
+ /**
17
+ * Roots searched in order. The installed copy wins over the toolkit source, so
18
+ * a target project measures against the rule it actually has rather than one
19
+ * only the toolkit carries.
20
+ */
21
+ const RULE_ROOTS = ['.claude/rules', 'governance/rules']
22
+
23
+ export interface NarrationTerms {
24
+ /** Back-reference openings, matched at the start of a bullet and cased. */
25
+ readonly pronouns: readonly string[]
26
+ /** Past-tense verbs, matched anywhere in the bullet and uncased. */
27
+ readonly verbs: readonly string[]
28
+ }
29
+
30
+ /**
31
+ * Absent is a distinct state from empty, the split the comment vocabulary
32
+ * draws for the same reason: a scan with no terms finds nothing, and reporting
33
+ * that as zero findings claims the corpus is clean when nothing was looked for.
34
+ */
35
+ export type Narration =
36
+ | ({ readonly kind: 'loaded'; readonly source: string } & NarrationTerms)
37
+ | { readonly kind: 'absent' }
38
+
39
+ /** Pulls the backticked terms out of the bullets under one heading. */
40
+ export function parseTerms(
41
+ markdown: string,
42
+ heading: string,
43
+ ): string[] | undefined {
44
+ const lines = markdown.split('\n')
45
+ const start = lines.findIndex((line) => line.trim() === heading)
46
+ if (start === -1) return undefined
47
+
48
+ const terms: string[] = []
49
+
50
+ for (const line of lines.slice(start + 1)) {
51
+ if (line.startsWith('## ')) break
52
+ for (const match of line.matchAll(/`([^`]+)`/g)) {
53
+ const term = match[1].trim()
54
+ if (term && !terms.includes(term)) terms.push(term)
55
+ }
56
+ }
57
+
58
+ return terms
59
+ }
60
+
61
+ /**
62
+ * Finds the rule publishing both sets under `root`.
63
+ *
64
+ * Both come from one file rather than from whichever rule carries each. A
65
+ * pronoun set and a verb set are halves of one signal, and pairing them across
66
+ * files would let a renumber recombine them into a signal nobody wrote.
67
+ */
68
+ export async function loadNarration(root: string): Promise<Narration> {
69
+ for (const ruleRoot of RULE_ROOTS) {
70
+ const dir = resolve(root, ruleRoot)
71
+ if (!existsSync(dir)) continue
72
+
73
+ const paths: string[] = []
74
+ for await (const rel of new Bun.Glob('**/*.md').scan({
75
+ cwd: dir,
76
+ onlyFiles: true,
77
+ })) {
78
+ paths.push(rel)
79
+ }
80
+ paths.sort()
81
+
82
+ for (const rel of paths) {
83
+ const source = await readFile(resolve(dir, rel), 'utf8')
84
+ const pronouns = parseTerms(source, PRONOUN_HEADING)
85
+ const verbs = parseTerms(source, VERB_HEADING)
86
+
87
+ if (pronouns && verbs && pronouns.length > 0 && verbs.length > 0) {
88
+ return {
89
+ kind: 'loaded',
90
+ source: `${ruleRoot}/${rel}`,
91
+ pronouns,
92
+ verbs,
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ return { kind: 'absent' }
99
+ }
package/src/copy.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { chmod, copyFile, mkdir, stat } from 'node:fs/promises'
2
+ import { dirname } from 'node:path'
3
+
4
+ /**
5
+ * Copies with `cp` semantics. Two behaviors matter and neither is the default
6
+ * in the obvious alternatives. `Bun.write` drops the executable bit, which
7
+ * would land husky hooks and `scripts/verify.sh` non-executable. Plain
8
+ * `copyFile` imposes the source mode on an existing destination, so a file the
9
+ * target deliberately restricted to 600, or an earlier stack set executable,
10
+ * comes back at the source's mode. `cp` leaves an existing destination's mode
11
+ * alone.
12
+ *
13
+ * Every domain that writes into a target routes through here. The tooling
14
+ * inject path found the trap first, and the sync engine inherits it for every
15
+ * adapter.
16
+ */
17
+ export async function copyPreservingMode(
18
+ src: string,
19
+ dest: string,
20
+ ): Promise<void> {
21
+ await mkdir(dirname(dest), { recursive: true })
22
+
23
+ const existingMode = await stat(dest)
24
+ .then((info) => info.mode)
25
+ .catch(() => undefined)
26
+
27
+ await copyFile(src, dest)
28
+
29
+ if (existingMode !== undefined) await chmod(dest, existingMode)
30
+ }