@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.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/claude/.claude-plugin/plugin.json +19 -0
- package/claude/skills/bash-cli-script/REQUIREMENT.md +42 -0
- package/claude/skills/bash-cli-script/SKILL.md +48 -0
- package/claude/skills/bash-cli-script/references/template.md +43 -0
- package/claude/skills/bash-script/REQUIREMENT.md +36 -0
- package/claude/skills/bash-script/SKILL.md +100 -0
- package/claude/skills/bash-script/references/patterns.md +349 -0
- package/claude/skills/canon-cli/REQUIREMENT.md +41 -0
- package/claude/skills/canon-cli/SKILL.md +103 -0
- package/claude/skills/canon-feedback-file/REQUIREMENT.md +40 -0
- package/claude/skills/canon-feedback-file/SKILL.md +80 -0
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +40 -0
- package/claude/skills/canon-feedback-triage/SKILL.md +63 -0
- package/claude/skills/canon-operator/REQUIREMENT.md +61 -0
- package/claude/skills/canon-operator/SKILL.md +108 -0
- package/claude/skills/canon-rollout/REQUIREMENT.md +59 -0
- package/claude/skills/canon-rollout/SKILL.md +147 -0
- package/claude/skills/canon-screencast/REQUIREMENT.md +39 -0
- package/claude/skills/canon-screencast/SKILL.md +167 -0
- package/claude/skills/canon-slides-draft/REQUIREMENT.md +39 -0
- package/claude/skills/canon-slides-draft/SKILL.md +62 -0
- package/claude/skills/ci-workflow/REQUIREMENT.md +40 -0
- package/claude/skills/ci-workflow/SKILL.md +65 -0
- package/claude/skills/ci-workflow/references/workflows.md +98 -0
- package/claude/skills/claude-address-review/REQUIREMENT.md +57 -0
- package/claude/skills/claude-address-review/SKILL.md +212 -0
- package/claude/skills/claude-address-review/references/rebase-conflicts.md +39 -0
- package/claude/skills/claude-autoship/REQUIREMENT.md +50 -0
- package/claude/skills/claude-autoship/SKILL.md +207 -0
- package/claude/skills/claude-design-extract/REQUIREMENT.md +42 -0
- package/claude/skills/claude-design-extract/SKILL.md +102 -0
- package/claude/skills/claude-diagram/REQUIREMENT.md +45 -0
- package/claude/skills/claude-diagram/SKILL.md +177 -0
- package/claude/skills/claude-docs/REQUIREMENT.md +60 -0
- package/claude/skills/claude-docs/SKILL.md +287 -0
- package/claude/skills/claude-docs/references/anchor-sweep.md +58 -0
- package/claude/skills/claude-docs/references/wireframe-sweep.md +45 -0
- package/claude/skills/claude-feature/REQUIREMENT.md +36 -0
- package/claude/skills/claude-feature/SKILL.md +115 -0
- package/claude/skills/claude-groundwork/REQUIREMENT.md +48 -0
- package/claude/skills/claude-groundwork/SKILL.md +142 -0
- package/claude/skills/claude-intake/REQUIREMENT.md +49 -0
- package/claude/skills/claude-intake/SKILL.md +114 -0
- package/claude/skills/claude-intake-answer/REQUIREMENT.md +48 -0
- package/claude/skills/claude-intake-answer/SKILL.md +90 -0
- package/claude/skills/claude-markdown-propose/REQUIREMENT.md +48 -0
- package/claude/skills/claude-markdown-propose/SKILL.md +118 -0
- package/claude/skills/claude-markdown-propose/references/format.md +107 -0
- package/claude/skills/claude-memory-capture/REQUIREMENT.md +50 -0
- package/claude/skills/claude-memory-capture/SKILL.md +101 -0
- package/claude/skills/claude-memory-review/REQUIREMENT.md +50 -0
- package/claude/skills/claude-memory-review/SKILL.md +210 -0
- package/claude/skills/claude-memory-review/references/receipt-format.md +48 -0
- package/claude/skills/claude-orchestrate/REQUIREMENT.md +121 -0
- package/claude/skills/claude-orchestrate/SKILL.md +241 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +132 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-handoff.md +32 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +72 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +87 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +30 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-sweep.md +21 -0
- package/claude/skills/claude-orchestrate/scripts/poll.sh +373 -0
- package/claude/skills/claude-orchestrate/scripts/watch.sh +181 -0
- package/claude/skills/claude-pr-review/REQUIREMENT.md +47 -0
- package/claude/skills/claude-pr-review/SKILL.md +295 -0
- package/claude/skills/claude-review/REQUIREMENT.md +39 -0
- package/claude/skills/claude-review/SKILL.md +142 -0
- package/claude/skills/claude-seed-sync/REQUIREMENT.md +45 -0
- package/claude/skills/claude-seed-sync/SKILL.md +156 -0
- package/claude/skills/claude-standards-audit/REQUIREMENT.md +33 -0
- package/claude/skills/claude-standards-audit/SKILL.md +99 -0
- package/claude/skills/claude-tasks/REQUIREMENT.md +43 -0
- package/claude/skills/claude-tasks/SKILL.md +161 -0
- package/claude/skills/claude-teach/REQUIREMENT.md +56 -0
- package/claude/skills/claude-teach/SKILL.md +196 -0
- package/claude/skills/claude-teach/references/lesson-craft.md +59 -0
- package/claude/skills/claude-teach/references/pedagogy.md +67 -0
- package/claude/skills/claude-teach/references/promotion.md +54 -0
- package/claude/skills/claude-ui-test/REQUIREMENT.md +40 -0
- package/claude/skills/claude-ui-test/SKILL.md +77 -0
- package/claude/skills/claude-ux-audit/REQUIREMENT.md +40 -0
- package/claude/skills/claude-ux-audit/SKILL.md +79 -0
- package/claude/skills/claude-ux-measure/REQUIREMENT.md +48 -0
- package/claude/skills/claude-ux-measure/SKILL.md +122 -0
- package/claude/skills/claude-worker/REQUIREMENT.md +54 -0
- package/claude/skills/claude-worker/SKILL.md +96 -0
- package/claude/skills/claude-worktree/REQUIREMENT.md +58 -0
- package/claude/skills/claude-worktree/SKILL.md +136 -0
- package/claude/skills/create-rule/REQUIREMENT.md +45 -0
- package/claude/skills/create-rule/SKILL.md +68 -0
- package/claude/skills/create-skill/REQUIREMENT.md +38 -0
- package/claude/skills/create-skill/SKILL.md +32 -0
- package/claude/skills/create-snippet/REQUIREMENT.md +39 -0
- package/claude/skills/create-snippet/SKILL.md +30 -0
- package/claude/skills/create-standard/REQUIREMENT.md +35 -0
- package/claude/skills/create-standard/SKILL.md +29 -0
- package/claude/skills/decision-escalate/REQUIREMENT.md +45 -0
- package/claude/skills/decision-escalate/SKILL.md +79 -0
- package/claude/skills/docs-sync/REQUIREMENT.md +41 -0
- package/claude/skills/docs-sync/SKILL.md +95 -0
- package/claude/skills/git-branch/REQUIREMENT.md +38 -0
- package/claude/skills/git-branch/SKILL.md +60 -0
- package/claude/skills/git-commit/REQUIREMENT.md +36 -0
- package/claude/skills/git-commit/SKILL.md +49 -0
- package/claude/skills/git-followup/REQUIREMENT.md +43 -0
- package/claude/skills/git-followup/SKILL.md +48 -0
- package/claude/skills/git-issue/REQUIREMENT.md +38 -0
- package/claude/skills/git-issue/SKILL.md +65 -0
- package/claude/skills/git-pr/REQUIREMENT.md +50 -0
- package/claude/skills/git-pr/SKILL.md +164 -0
- package/claude/skills/git-pr/references/labels.md +95 -0
- package/claude/skills/git-ship/REQUIREMENT.md +42 -0
- package/claude/skills/git-ship/SKILL.md +55 -0
- package/claude/skills/git-split/REQUIREMENT.md +39 -0
- package/claude/skills/git-split/SKILL.md +162 -0
- package/claude/skills/git-stage/REQUIREMENT.md +39 -0
- package/claude/skills/git-stage/SKILL.md +73 -0
- package/claude/skills/git-worktree/REQUIREMENT.md +38 -0
- package/claude/skills/git-worktree/SKILL.md +130 -0
- package/claude/skills/migration-claude-md/REQUIREMENT.md +40 -0
- package/claude/skills/migration-claude-md/SKILL.md +76 -0
- package/claude/skills/migration-context/REQUIREMENT.md +36 -0
- package/claude/skills/migration-context/SKILL.md +95 -0
- package/claude/skills/migration-standards-drop/REQUIREMENT.md +55 -0
- package/claude/skills/migration-standards-drop/SKILL.md +113 -0
- package/claude/skills/migration-superseded/REQUIREMENT.md +44 -0
- package/claude/skills/migration-superseded/SKILL.md +115 -0
- package/claude/skills/project-commands/REQUIREMENT.md +42 -0
- package/claude/skills/project-commands/SKILL.md +85 -0
- package/claude/skills/restate-plainly/REQUIREMENT.md +41 -0
- package/claude/skills/restate-plainly/SKILL.md +39 -0
- package/claude/skills/session-map/REQUIREMENT.md +57 -0
- package/claude/skills/session-map/SKILL.md +70 -0
- package/claude/skills/session-resume/REQUIREMENT.md +49 -0
- package/claude/skills/session-resume/SKILL.md +51 -0
- package/claude/skills/setup-gov/REQUIREMENT.md +37 -0
- package/claude/skills/setup-gov/SKILL.md +77 -0
- package/claude/skills/setup-indexes/REQUIREMENT.md +45 -0
- package/claude/skills/setup-indexes/SKILL.md +153 -0
- package/claude/skills/setup-init/REQUIREMENT.md +45 -0
- package/claude/skills/setup-init/SKILL.md +127 -0
- package/claude/skills/setup-plugins/REQUIREMENT.md +42 -0
- package/claude/skills/setup-plugins/SKILL.md +81 -0
- package/claude/skills/setup-plugins/references/plugin-catalog.md +53 -0
- package/claude/skills/setup-verify/REQUIREMENT.md +39 -0
- package/claude/skills/setup-verify/SKILL.md +51 -0
- package/claude/skills/systematic-debugging/REQUIREMENT.md +41 -0
- package/claude/skills/systematic-debugging/SKILL.md +70 -0
- package/claude/skills/write-human/REQUIREMENT.md +46 -0
- package/claude/skills/write-human/SKILL.md +68 -0
- package/claude/skills/write-human/references/density.md +38 -0
- package/claude/skills/write-human/references/machine-tells.md +107 -0
- package/claude/skills/write-human/references/source-material.md +37 -0
- package/claude/skills/youtube-transcripts/REQUIREMENT.md +38 -0
- package/claude/skills/youtube-transcripts/SKILL.md +34 -0
- package/docs/agents/audits.md +98 -0
- package/docs/agents/capture.md +37 -0
- package/docs/agents/census.md +23 -0
- package/docs/agents/commands.md +146 -0
- package/docs/agents/comments.md +34 -0
- package/docs/agents/context-audit-checks.md +120 -0
- package/docs/agents/context-audit.md +83 -0
- package/docs/agents/counts.md +76 -0
- package/docs/agents/demo.md +86 -0
- package/docs/agents/docs.md +17 -0
- package/docs/agents/gate.md +84 -0
- package/docs/agents/index.md +47 -0
- package/docs/agents/indexes.md +35 -0
- package/docs/agents/install-and-sync.md +385 -0
- package/docs/agents/intake.md +81 -0
- package/docs/agents/key-changes.md +103 -0
- package/docs/agents/label-coverage.md +73 -0
- package/docs/agents/markdown-audit.md +197 -0
- package/docs/agents/output-shape.md +70 -0
- package/docs/agents/overview.md +26 -0
- package/docs/agents/records.md +170 -0
- package/docs/agents/restated.md +81 -0
- package/docs/agents/review-classification.md +77 -0
- package/docs/agents/routing.md +61 -0
- package/docs/agents/rule-citations.md +98 -0
- package/docs/agents/sandbox.md +71 -0
- package/docs/agents/scripting.md +149 -0
- package/docs/agents/sessions.md +120 -0
- package/docs/agents/skills-audit.md +94 -0
- package/docs/agents/skills-reach.md +64 -0
- package/docs/agents/standards-audit.md +38 -0
- package/docs/agents/state-scoped-risk.md +105 -0
- package/docs/agents/superseded.md +85 -0
- package/docs/agents/targets.md +83 -0
- package/docs/agents/tasks.md +200 -0
- package/docs/agents/teach.md +158 -0
- package/docs/agents/test-order.md +56 -0
- package/docs/agents/worktrees.md +62 -0
- package/docs/ai-workflow.md +317 -0
- package/docs/index.md +23 -0
- package/docs/operating-model.md +223 -0
- package/docs/target-projects.md +258 -0
- package/docs/visual-design-workflow.md +151 -0
- package/docs/zshrc-aliases.md +65 -0
- package/governance/rules/ci/700-ci-workflow.md +44 -0
- package/governance/rules/claude/500-prose.md +15 -0
- package/governance/rules/claude/501-markdown.md +14 -0
- package/governance/rules/claude/510-context.md +28 -0
- package/governance/rules/claude/511-indexes.md +14 -0
- package/governance/rules/claude/520-wireframes.md +12 -0
- package/governance/rules/claude/530-requirements.md +11 -0
- package/governance/rules/claude/540-architecture.md +11 -0
- package/governance/rules/claude/550-design.md +11 -0
- package/governance/rules/claude/555-tasks.md +12 -0
- package/governance/rules/claude/556-groundwork.md +11 -0
- package/governance/rules/claude/557-intake.md +11 -0
- package/governance/rules/claude/558-plan.md +22 -0
- package/governance/rules/claude/559-memory.md +11 -0
- package/governance/rules/claude/560-diagrams.md +18 -0
- package/governance/rules/claude/561-teach.md +13 -0
- package/governance/rules/claude/562-session.md +15 -0
- package/governance/rules/claude/570-skill.md +24 -0
- package/governance/rules/claude/575-hooks.md +17 -0
- package/governance/rules/claude/576-settings.md +14 -0
- package/governance/rules/claude/580-readme.md +11 -0
- package/governance/rules/claude/590-rule-authoring.md +12 -0
- package/governance/rules/claude/591-standard-authoring.md +12 -0
- package/governance/rules/claude/592-claude-md.md +19 -0
- package/governance/rules/core/000-constitution.md +30 -0
- package/governance/rules/core/005-behavior.md +27 -0
- package/governance/rules/core/010-testing.md +35 -0
- package/governance/rules/core/015-output.md +20 -0
- package/governance/rules/core/020-concurrency.md +22 -0
- package/governance/rules/core/025-indexes.md +9 -0
- package/governance/rules/core/030-error-handling.md +31 -0
- package/governance/rules/core/035-tasks.md +13 -0
- package/governance/rules/core/040-performance.md +20 -0
- package/governance/rules/core/045-memory.md +12 -0
- package/governance/rules/core/050-logging.md +20 -0
- package/governance/rules/core/055-scratch.md +9 -0
- package/governance/rules/core/060-naming.md +19 -0
- package/governance/rules/core/065-spelling.md +19 -0
- package/governance/rules/core/070-planning.md +18 -0
- package/governance/rules/core/075-dependencies.md +25 -0
- package/governance/rules/core/080-config-comments.md +22 -0
- package/governance/rules/core/085-worktrees.md +17 -0
- package/governance/rules/core/087-git.md +11 -0
- package/governance/rules/core/090-code-comments.md +39 -0
- package/governance/rules/framework/200-react.md +51 -0
- package/governance/rules/framework/210-astro.md +41 -0
- package/governance/rules/framework/220-fastapi.md +43 -0
- package/governance/rules/framework/230-nextjs.md +48 -0
- package/governance/rules/framework/250-tailwind.md +32 -0
- package/governance/rules/framework/260-shadcn.md +34 -0
- package/governance/rules/lang/100-typescript.md +40 -0
- package/governance/rules/lang/110-python.md +42 -0
- package/governance/rules/lang/120-bash.md +19 -0
- package/governance/rules/lib/300-testing-ts.md +39 -0
- package/governance/rules/lib/305-e2e-reliability.md +34 -0
- package/governance/rules/lib/306-test-scope.md +25 -0
- package/governance/rules/lib/310-zod.md +25 -0
- package/governance/rules/lib/320-tanstack-query.md +32 -0
- package/governance/rules/lib/330-testing-py.md +44 -0
- package/governance/rules/lib/340-pydantic.md +38 -0
- package/governance/rules/lib/350-security-web.md +32 -0
- package/governance/rules/lib/360-security-server.md +39 -0
- package/governance/rules/lib/370-database.md +35 -0
- package/governance/rules/snippets/505-at-references.md +9 -0
- package/governance/rules/ui/400-ui.md +36 -0
- package/governance/rules/ui/410-a11y.md +48 -0
- package/governance/rules/ui/420-forms.md +36 -0
- package/governance/rules/ui/430-ux-completeness.md +65 -0
- package/governance/rules/ui/440-surface-capture.md +34 -0
- package/governance/rules/ui/450-link-behavior.md +19 -0
- package/governance/stacks/astro.toml +2 -0
- package/governance/stacks/base.toml +8 -0
- package/governance/stacks/node-server.toml +2 -0
- package/governance/stacks/node.toml +2 -0
- package/governance/stacks/python-fastapi.toml +2 -0
- package/governance/stacks/python.toml +2 -0
- package/governance/stacks/react.toml +2 -0
- package/package.json +69 -0
- package/scripts/config.sh +11 -0
- package/scripts/core/bootstrap.sh +81 -0
- package/scripts/core/check-color-source.sh +41 -0
- package/scripts/core/check-ignore-parity.sh +162 -0
- package/scripts/core/check-plugin-boundary.sh +45 -0
- package/scripts/core/check-seed-independence.sh +59 -0
- package/scripts/core/check-skill-paths.sh +24 -0
- package/scripts/core/clean.sh +36 -0
- package/scripts/core/install-check.sh +101 -0
- package/scripts/core/list-seed-roots.sh +18 -0
- package/scripts/core/regen-claude-copies.sh +10 -0
- package/scripts/core/regen-hero.sh +217 -0
- package/scripts/core/regen-indexes.sh +10 -0
- package/scripts/core/regen-tooling-paths.sh +61 -0
- package/scripts/core/repair-bare-flag.sh +19 -0
- package/scripts/core/snapshot.sh +134 -0
- package/scripts/core/update.sh +35 -0
- package/scripts/docs/list.sh +165 -0
- package/scripts/lib/frontmatter.sh +30 -0
- package/scripts/lib/gov.sh +14 -0
- package/scripts/lib/sandbox-fixtures.sh +191 -0
- package/scripts/lib/sandbox-git.sh +125 -0
- package/scripts/lib/sandbox-path.sh +206 -0
- package/scripts/lib/tooling.sh +35 -0
- package/scripts/lib/ui.sh +266 -0
- package/scripts/lib/worktree.sh +20 -0
- package/scripts/manage-sandbox.sh +466 -0
- package/scripts/snippets/create.sh +156 -0
- package/scripts/standards/list.sh +115 -0
- package/scripts/tooling/create.sh +109 -0
- package/scripts/tooling/verify.sh +179 -0
- package/snippets/align.md +12 -0
- package/snippets/claude/decision-memo.md +39 -0
- package/snippets/claude/feature-recap.md +19 -0
- package/snippets/claude/figma-steps.md +24 -0
- package/snippets/compact-summary.md +5 -0
- package/snippets/decision-help.md +6 -0
- package/snippets/meta-prompt.md +14 -0
- package/snippets/research-prompt.md +7 -0
- package/snippets/session-notes.md +11 -0
- package/snippets/snippets.toml +5 -0
- package/snippets/step-by-step.md +10 -0
- package/snippets/web-research.md +21 -0
- package/src/audits/baseline.ts +201 -0
- package/src/audits/catalog.ts +876 -0
- package/src/audits/run.ts +204 -0
- package/src/autoship/classify.ts +75 -0
- package/src/autoship/paths.ts +51 -0
- package/src/binary.ts +16 -0
- package/src/browser/engine.ts +40 -0
- package/src/census/count.ts +113 -0
- package/src/claude/cases/all.ts +24 -0
- package/src/claude/cases/authoring.ts +53 -0
- package/src/claude/cases/claude-workflow.ts +158 -0
- package/src/claude/cases/git.ts +44 -0
- package/src/claude/cases/misc.ts +27 -0
- package/src/claude/cases/setup.ts +94 -0
- package/src/claude/gitignore.ts +51 -0
- package/src/claude/routing.ts +283 -0
- package/src/claude/seeds-list.ts +47 -0
- package/src/claude/seeds.ts +150 -0
- package/src/claude/settings.ts +151 -0
- package/src/claude/skills-audit.ts +228 -0
- package/src/claude/skills-drift.ts +156 -0
- package/src/claude/skills-list.ts +99 -0
- package/src/claude/skills-rank.ts +320 -0
- package/src/claude/skills-reach.ts +227 -0
- package/src/cli-run.ts +43 -0
- package/src/cli.ts +200 -0
- package/src/commands/audits.ts +350 -0
- package/src/commands/autoship.ts +129 -0
- package/src/commands/capture.ts +133 -0
- package/src/commands/census.ts +105 -0
- package/src/commands/claude.ts +1286 -0
- package/src/commands/comments.ts +240 -0
- package/src/commands/context.ts +857 -0
- package/src/commands/demo.ts +389 -0
- package/src/commands/deps.ts +173 -0
- package/src/commands/design.ts +36 -0
- package/src/commands/docs.ts +60 -0
- package/src/commands/feedback-format.ts +23 -0
- package/src/commands/feedback.ts +112 -0
- package/src/commands/gate.ts +189 -0
- package/src/commands/gov.ts +1265 -0
- package/src/commands/indexes.ts +184 -0
- package/src/commands/init.ts +113 -0
- package/src/commands/intake.ts +406 -0
- package/src/commands/inventory.ts +256 -0
- package/src/commands/labels.ts +361 -0
- package/src/commands/markdown.ts +544 -0
- package/src/commands/migrate.ts +175 -0
- package/src/commands/pass-through.ts +39 -0
- package/src/commands/pr.ts +411 -0
- package/src/commands/records.ts +728 -0
- package/src/commands/sandbox.ts +468 -0
- package/src/commands/secrets.ts +132 -0
- package/src/commands/serve.ts +159 -0
- package/src/commands/sessions.ts +408 -0
- package/src/commands/slides.ts +126 -0
- package/src/commands/snippets.ts +84 -0
- package/src/commands/standards.ts +247 -0
- package/src/commands/sync.ts +428 -0
- package/src/commands/targets.ts +319 -0
- package/src/commands/tasks.ts +743 -0
- package/src/commands/teach.ts +786 -0
- package/src/commands/tooling.ts +573 -0
- package/src/commands/transcripts.ts +44 -0
- package/src/commands/upgrade.ts +231 -0
- package/src/commands/wiki.ts +100 -0
- package/src/commands/worktrees.ts +191 -0
- package/src/comments/scan.ts +338 -0
- package/src/comments/trend.ts +207 -0
- package/src/comments/vocabulary.ts +85 -0
- package/src/context/architecture.ts +364 -0
- package/src/context/audit.ts +790 -0
- package/src/context/citations.ts +196 -0
- package/src/context/folders.ts +186 -0
- package/src/context/gate.ts +57 -0
- package/src/context/index-drift.ts +64 -0
- package/src/context/narration.ts +99 -0
- package/src/copy.ts +30 -0
- package/src/counts/catalogs.ts +96 -0
- package/src/counts/numbers.ts +79 -0
- package/src/counts/scan.ts +314 -0
- package/src/demo/beats.ts +135 -0
- package/src/demo/compile.ts +326 -0
- package/src/demo/container.ts +63 -0
- package/src/demo/cursors.ts +55 -0
- package/src/demo/drive.ts +357 -0
- package/src/demo/pointer.ts +178 -0
- package/src/demo/theme.ts +112 -0
- package/src/deps/audit.ts +153 -0
- package/src/design/parse.ts +116 -0
- package/src/design/render.ts +249 -0
- package/src/docs/read.ts +77 -0
- package/src/exec.ts +16 -0
- package/src/exempt-marker.ts +43 -0
- package/src/frontmatter.ts +13 -0
- package/src/gate/measures.ts +682 -0
- package/src/gate/sequencer.ts +386 -0
- package/src/gate/stages.ts +412 -0
- package/src/git-env.ts +36 -0
- package/src/git-files.ts +105 -0
- package/src/git-ignore.ts +46 -0
- package/src/github-format.ts +13 -0
- package/src/github.ts +24 -0
- package/src/gov/adapter.ts +103 -0
- package/src/gov/citations.ts +514 -0
- package/src/gov/consumed.ts +129 -0
- package/src/gov/install.ts +132 -0
- package/src/gov/list.ts +106 -0
- package/src/gov/payload.ts +39 -0
- package/src/gov/restated.ts +814 -0
- package/src/gov/stacks.ts +205 -0
- package/src/gov/superseded.ts +415 -0
- package/src/gov/test-order.ts +407 -0
- package/src/indexes/frontmatter.ts +46 -0
- package/src/indexes/regen.ts +84 -0
- package/src/indexes/render.ts +201 -0
- package/src/indexes/walk.ts +83 -0
- package/src/init/flags.ts +60 -0
- package/src/init/plan.ts +114 -0
- package/src/init/run.ts +46 -0
- package/src/init/steps.ts +77 -0
- package/src/intake/folder.ts +320 -0
- package/src/intake/items.ts +174 -0
- package/src/inventory/config.ts +117 -0
- package/src/inventory/group.ts +76 -0
- package/src/inventory/subjects.ts +114 -0
- package/src/inventory/walk.ts +129 -0
- package/src/labels/audit.ts +82 -0
- package/src/labels/coverage.ts +79 -0
- package/src/labels/map.ts +101 -0
- package/src/labels/phase.ts +95 -0
- package/src/markdown/bans.ts +94 -0
- package/src/markdown/files.ts +102 -0
- package/src/markdown/gate.ts +28 -0
- package/src/markdown/scan.ts +295 -0
- package/src/markdown/structure.ts +730 -0
- package/src/migrate/apply.ts +115 -0
- package/src/migrate/plan.ts +103 -0
- package/src/migrate/rename.ts +183 -0
- package/src/pr/bijection.ts +145 -0
- package/src/pr/paths.ts +335 -0
- package/src/process/harness.ts +167 -0
- package/src/project-root.ts +19 -0
- package/src/records/backup.ts +455 -0
- package/src/records/migrate.ts +78 -0
- package/src/records/size.ts +260 -0
- package/src/records/validate.ts +1162 -0
- package/src/sandbox/census.ts +228 -0
- package/src/sandbox/coverage.ts +115 -0
- package/src/sandbox/expect.ts +629 -0
- package/src/sandbox/tree.ts +47 -0
- package/src/secrets/marker.ts +30 -0
- package/src/secrets/patterns.ts +142 -0
- package/src/secrets/scan.ts +123 -0
- package/src/secrets/shipped.ts +111 -0
- package/src/seed-marker.ts +74 -0
- package/src/serve/static.ts +322 -0
- package/src/sessions/claim.ts +85 -0
- package/src/sessions/live.ts +79 -0
- package/src/sessions/registry.ts +137 -0
- package/src/sessions/resolve.ts +333 -0
- package/src/slides/layouts.ts +391 -0
- package/src/slides/open.ts +18 -0
- package/src/slides/parse.ts +84 -0
- package/src/slides/render.ts +88 -0
- package/src/slides/styles.ts +44 -0
- package/src/snippets/categories.ts +66 -0
- package/src/snippets/list.ts +32 -0
- package/src/snippets/presets.ts +50 -0
- package/src/standards/audit.ts +132 -0
- package/src/standards/read.ts +100 -0
- package/src/sync/check.ts +692 -0
- package/src/sync/engine.ts +576 -0
- package/src/sync/git.ts +225 -0
- package/src/sync/history.ts +123 -0
- package/src/sync/layout.ts +140 -0
- package/src/sync/reverse.ts +268 -0
- package/src/sync/seeds-report.ts +126 -0
- package/src/sync/stamp.ts +358 -0
- package/src/sync/target.ts +70 -0
- package/src/sync/workflow.ts +200 -0
- package/src/target.ts +43 -0
- package/src/targets/pulls.ts +250 -0
- package/src/targets/registry.ts +203 -0
- package/src/targets/resolve.ts +145 -0
- package/src/targets/sweep.ts +246 -0
- package/src/tasks/archive.ts +506 -0
- package/src/tasks/record.ts +311 -0
- package/src/tasks/trunk.ts +89 -0
- package/src/tasks/validate.ts +1001 -0
- package/src/teach/lesson.ts +180 -0
- package/src/teach/workspace.ts +842 -0
- package/src/tooling/gitignore.ts +122 -0
- package/src/tooling/inject.ts +193 -0
- package/src/tooling/list.ts +39 -0
- package/src/tooling/manifest.ts +176 -0
- package/src/tooling/package.ts +166 -0
- package/src/tooling/read.ts +65 -0
- package/src/tooling/scan.ts +147 -0
- package/src/tooling/stamp.ts +44 -0
- package/src/transcripts/fetch.ts +156 -0
- package/src/transcripts/metadata.ts +54 -0
- package/src/transcripts/vtt.ts +114 -0
- package/src/ui.ts +266 -0
- package/src/version/compare.ts +53 -0
- package/src/version/installed.ts +40 -0
- package/src/version/manager.ts +67 -0
- package/src/version/skew.ts +192 -0
- package/src/wiki/init.ts +85 -0
- package/src/worktree.ts +143 -0
- package/src/worktrees/reclaim.ts +306 -0
- package/standards/architecture.md +72 -0
- package/standards/branch.md +59 -0
- package/standards/commit.md +72 -0
- package/standards/context.md +151 -0
- package/standards/design.md +93 -0
- package/standards/diagrams.md +152 -0
- package/standards/glossary.md +75 -0
- package/standards/groundwork.md +211 -0
- package/standards/index.md +36 -0
- package/standards/intake.md +192 -0
- package/standards/issue.md +94 -0
- package/standards/markdown.md +137 -0
- package/standards/memory.md +144 -0
- package/standards/plan.md +172 -0
- package/standards/pr.md +139 -0
- package/standards/publish.md +51 -0
- package/standards/readme.md +208 -0
- package/standards/requirements.md +70 -0
- package/standards/rule.md +118 -0
- package/standards/session.md +109 -0
- package/standards/skill.md +300 -0
- package/standards/slug.md +39 -0
- package/standards/snippets.md +76 -0
- package/standards/standard.md +170 -0
- package/standards/tasks.md +254 -0
- package/standards/teach.md +153 -0
- package/standards/versioning.md +71 -0
- package/standards/wireframes.md +113 -0
- package/tooling/astro/configs/astro.config.mjs +31 -0
- package/tooling/astro/configs/eslint.config.js +79 -0
- package/tooling/astro/configs/playwright.config.ts +26 -0
- package/tooling/astro/configs/tsconfig.json +12 -0
- package/tooling/astro/configs/vitest.config.ts +22 -0
- package/tooling/astro/manifest.toml +34 -0
- package/tooling/astro/reference.md +60 -0
- package/tooling/base/configs/.editorconfig +5 -0
- package/tooling/base/configs/.github/pull_request_template.md +18 -0
- package/tooling/base/configs/.github/workflows/verify.yml +35 -0
- package/tooling/base/configs/.husky/commit-msg +1 -0
- package/tooling/base/configs/.husky/post-merge +61 -0
- package/tooling/base/configs/.husky/post-rewrite +21 -0
- package/tooling/base/configs/.husky/pre-commit +1 -0
- package/tooling/base/configs/.husky/pre-push +1 -0
- package/tooling/base/configs/.prettierrc +12 -0
- package/tooling/base/configs/.shellcheckrc +1 -0
- package/tooling/base/configs/.vscode/extensions.json +9 -0
- package/tooling/base/configs/.vscode/settings.json +3 -0
- package/tooling/base/configs/commitlint.config.js +11 -0
- package/tooling/base/configs/scripts/verify.sh +64 -0
- package/tooling/base/manifest.toml +30 -0
- package/tooling/base/reference.md +98 -0
- package/tooling/base/seeds/.claude/context/ci.md +33 -0
- package/tooling/base/seeds/.claude/context/development.md +37 -0
- package/tooling/base/seeds/.claude/context/index.md +11 -0
- package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
- package/tooling/base/seeds/.cspell/tech-stack.txt +19 -0
- package/tooling/base/seeds/.lintstagedrc +8 -0
- package/tooling/base/seeds/.prettierignore +0 -0
- package/tooling/base/seeds/cspell.json +20 -0
- package/tooling/claude/manifest.toml +14 -0
- package/tooling/claude/reference.md +79 -0
- package/tooling/claude/seeds/.claude/ARCHITECTURE.md +13 -0
- package/tooling/claude/seeds/.claude/DESIGN.md +62 -0
- package/tooling/claude/seeds/.claude/REQUIREMENTS.md +18 -0
- package/tooling/claude/seeds/.claude/diagrams/index.md +8 -0
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +50 -0
- package/tooling/claude/seeds/.claude/hooks/memory-index.sh +68 -0
- package/tooling/claude/seeds/.claude/hooks/path-form.sh +57 -0
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +52 -0
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +86 -0
- package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +71 -0
- package/tooling/claude/seeds/.claude/memory/index.md +8 -0
- package/tooling/claude/seeds/.claude/settings.json +47 -0
- package/tooling/claude/seeds/.claude/tasks/index.md +8 -0
- package/tooling/claude/seeds/.claude/wireframes/index.md +8 -0
- package/tooling/claude/seeds/CLAUDE.md +29 -0
- package/tooling/claude/user/settings.template.json +10 -0
- package/tooling/claude/user/statusline-command.sh +53 -0
- package/tooling/python/configs/.coveragerc +14 -0
- package/tooling/python/configs/.python-version +1 -0
- package/tooling/python/configs/mypy.ini +6 -0
- package/tooling/python/configs/pytest.ini +4 -0
- package/tooling/python/configs/ruff.toml +15 -0
- package/tooling/python/configs/scripts/verify.sh +77 -0
- package/tooling/python/manifest.toml +16 -0
- package/tooling/python/reference.md +66 -0
- package/tooling/python/seeds/.cspell/tech-stack.txt +19 -0
- package/tooling/python/seeds/tests/test_smoke.py +2 -0
- package/tooling/vite-react/configs/playwright.config.ts +26 -0
- package/tooling/vite-react/configs/tsconfig.json +35 -0
- package/tooling/vite-react/configs/vite.config.ts +24 -0
- package/tooling/vite-react/configs/vitest.config.ts +26 -0
- package/tooling/vite-react/manifest.toml +23 -0
- package/tooling/vite-react/reference.md +55 -0
- package/tooling/vite-react/seeds/.cspell/project-terms.txt +1 -0
- package/tooling/vite-react/seeds/.cspell/tech-stack.txt +1 -0
- package/tooling/web/configs/.github/workflows/verify.yml +134 -0
- package/tooling/web/configs/.vscode/extensions.json +13 -0
- package/tooling/web/configs/.vscode/settings.json +10 -0
- package/tooling/web/configs/e2e/home.spec.ts +6 -0
- package/tooling/web/configs/e2e/screenshot.ts +53 -0
- package/tooling/web/configs/eslint.config.js +82 -0
- package/tooling/web/configs/scripts/screenshot.sh +28 -0
- package/tooling/web/configs/scripts/verify.sh +80 -0
- package/tooling/web/configs/scripts/worktree-port.sh +74 -0
- package/tooling/web/configs/src/test/setup.ts +8 -0
- package/tooling/web/manifest.toml +58 -0
- package/tooling/web/reference.md +115 -0
- package/tooling/web/seeds/.cspell/tech-stack.txt +18 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { AUDITS } from '@/audits/catalog'
|
|
4
|
+
import { listSkills } from '@/claude/skills-list'
|
|
5
|
+
import { buildGovCatalog } from '@/gov/list'
|
|
6
|
+
import { buildSnippetsCatalog } from '@/snippets/list'
|
|
7
|
+
import { listStandards } from '@/standards/read'
|
|
8
|
+
|
|
9
|
+
export interface Catalog {
|
|
10
|
+
readonly id: string
|
|
11
|
+
/** The noun form a prose sentence carries, singular first. */
|
|
12
|
+
readonly nouns: readonly [string, string]
|
|
13
|
+
/**
|
|
14
|
+
* `undefined` means this tree carries nothing this catalog reads, which is
|
|
15
|
+
* ordinary for every catalog but `commands`: a target installing this CLI
|
|
16
|
+
* has no `src/cli.ts` of its own, and reading that as zero would report a
|
|
17
|
+
* finding against every project stating its command count as anything but
|
|
18
|
+
* zero, forever.
|
|
19
|
+
*/
|
|
20
|
+
readonly count: (root: string) => number | undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One line per registered top-level command. */
|
|
24
|
+
const REGISTER_IMPORT = /^import \{ register as /gm
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Every top-level `canon` command, read off the CLI entry point rather than a
|
|
28
|
+
* catalog verb, because a command carries no `--json` listing of its own
|
|
29
|
+
* siblings. `regen-hero.sh` reads its own `COMMAND_COUNT` through `gov counts`
|
|
30
|
+
* rather than a second regex on `src/cli.ts`, so this is the one place that
|
|
31
|
+
* pattern is written.
|
|
32
|
+
*
|
|
33
|
+
* This is the one catalog with no meaning outside this repository, the way
|
|
34
|
+
* `regen-hero.sh` documents itself as clone-only for the same reason.
|
|
35
|
+
*/
|
|
36
|
+
function countCommands(root: string): number | undefined {
|
|
37
|
+
const cli = join(root, 'src', 'cli.ts')
|
|
38
|
+
if (!existsSync(cli)) return undefined
|
|
39
|
+
return [...readFileSync(cli, 'utf8').matchAll(REGISTER_IMPORT)].length
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The distinct entries across every category, matching what `regen-hero.sh`
|
|
44
|
+
* counts. A snippet reachable from two categories is one snippet, not two.
|
|
45
|
+
*/
|
|
46
|
+
function countSnippets(root: string): number {
|
|
47
|
+
const catalog = buildSnippetsCatalog(root)
|
|
48
|
+
return new Set(catalog.categories.flatMap((category) => category.entries))
|
|
49
|
+
.size
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The closed set of catalogs this sweep counts a document against.
|
|
54
|
+
*
|
|
55
|
+
* Closed rather than derived from every list command this CLI ships, on the
|
|
56
|
+
* design the plan settled on: a wider set widens the false-positive class
|
|
57
|
+
* before the first run has measured how large that class already is. Each
|
|
58
|
+
* reader here walks the filesystem directly rather than shelling out to its
|
|
59
|
+
* own `canon` verb, since this module already runs inside the process that
|
|
60
|
+
* would spawn it.
|
|
61
|
+
*/
|
|
62
|
+
export const CATALOGS: readonly Catalog[] = [
|
|
63
|
+
{
|
|
64
|
+
id: 'skills',
|
|
65
|
+
nouns: ['skill', 'skills'],
|
|
66
|
+
count: (root) => listSkills(root).length,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'rules',
|
|
70
|
+
nouns: ['rule', 'rules'],
|
|
71
|
+
count: (root) => buildGovCatalog(root).rules.length,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'standards',
|
|
75
|
+
nouns: ['standard', 'standards'],
|
|
76
|
+
count: (root) => listStandards(root).length,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'snippets',
|
|
80
|
+
nouns: ['snippet', 'snippets'],
|
|
81
|
+
count: countSnippets,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'commands',
|
|
85
|
+
nouns: ['command', 'commands'],
|
|
86
|
+
count: countCommands,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'audits',
|
|
90
|
+
nouns: ['audit', 'audits'],
|
|
91
|
+
// Reads the registered set living in this same process, so the count this
|
|
92
|
+
// row reports is the tree's own audit surface rather than a stale copy of
|
|
93
|
+
// it, this entry included.
|
|
94
|
+
count: () => AUDITS.length,
|
|
95
|
+
},
|
|
96
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const ONES: Record<string, number> = {
|
|
2
|
+
zero: 0,
|
|
3
|
+
one: 1,
|
|
4
|
+
two: 2,
|
|
5
|
+
three: 3,
|
|
6
|
+
four: 4,
|
|
7
|
+
five: 5,
|
|
8
|
+
six: 6,
|
|
9
|
+
seven: 7,
|
|
10
|
+
eight: 8,
|
|
11
|
+
nine: 9,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const TEENS: Record<string, number> = {
|
|
15
|
+
ten: 10,
|
|
16
|
+
eleven: 11,
|
|
17
|
+
twelve: 12,
|
|
18
|
+
thirteen: 13,
|
|
19
|
+
fourteen: 14,
|
|
20
|
+
fifteen: 15,
|
|
21
|
+
sixteen: 16,
|
|
22
|
+
seventeen: 17,
|
|
23
|
+
eighteen: 18,
|
|
24
|
+
nineteen: 19,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const TENS: Record<string, number> = {
|
|
28
|
+
twenty: 20,
|
|
29
|
+
thirty: 30,
|
|
30
|
+
forty: 40,
|
|
31
|
+
fifty: 50,
|
|
32
|
+
sixty: 60,
|
|
33
|
+
seventy: 70,
|
|
34
|
+
eighty: 80,
|
|
35
|
+
ninety: 90,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Every spelled-out cardinal from zero through ninety-nine, keyed lowercase.
|
|
40
|
+
*
|
|
41
|
+
* Every catalog this reads is under a hundred members, so the compound tens
|
|
42
|
+
* form (`sixty-one`) is the only multi-word shape worth building: the corpus
|
|
43
|
+
* this was written against spells a catalog size in words as often as in
|
|
44
|
+
* digits, and a matcher reading digits alone would miss half its own
|
|
45
|
+
* motivating cases.
|
|
46
|
+
*/
|
|
47
|
+
export const WORD_NUMBERS: ReadonlyMap<string, number> = (() => {
|
|
48
|
+
const words = new Map<string, number>()
|
|
49
|
+
for (const [word, value] of Object.entries(ONES)) words.set(word, value)
|
|
50
|
+
for (const [word, value] of Object.entries(TEENS)) words.set(word, value)
|
|
51
|
+
for (const [tensWord, tensValue] of Object.entries(TENS)) {
|
|
52
|
+
words.set(tensWord, tensValue)
|
|
53
|
+
for (const [onesWord, onesValue] of Object.entries(ONES)) {
|
|
54
|
+
if (onesValue === 0) continue
|
|
55
|
+
words.set(`${tensWord}-${onesWord}`, tensValue + onesValue)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return words
|
|
59
|
+
})()
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Longest key first, so the alternation matches `sixty-one` whole rather than
|
|
63
|
+
* stopping at `sixty` and leaving `-one` unconsumed.
|
|
64
|
+
*/
|
|
65
|
+
const WORD_ALTERNATION = [...WORD_NUMBERS.keys()]
|
|
66
|
+
.sort((left, right) => right.length - left.length)
|
|
67
|
+
.join('|')
|
|
68
|
+
|
|
69
|
+
/** A bare digit run, capped at three since every catalog here is under 1,000. */
|
|
70
|
+
const DIGIT_PATTERN = '\\d{1,3}'
|
|
71
|
+
|
|
72
|
+
/** Either spelling a catalog size takes in this corpus, for a caller building its own regex around it. */
|
|
73
|
+
export const NUMBER_PATTERN = `(?:${DIGIT_PATTERN}|${WORD_ALTERNATION})`
|
|
74
|
+
|
|
75
|
+
/** The value a matched token names, or `undefined` for a word this map does not carry. */
|
|
76
|
+
export function parseNumber(token: string): number | undefined {
|
|
77
|
+
if (/^\d+$/.test(token)) return Number(token)
|
|
78
|
+
return WORD_NUMBERS.get(token.toLowerCase())
|
|
79
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { type Catalog, CATALOGS } from '@/counts/catalogs'
|
|
4
|
+
import { NUMBER_PATTERN, parseNumber } from '@/counts/numbers'
|
|
5
|
+
import { resolveMarkdown } from '@/markdown/files'
|
|
6
|
+
|
|
7
|
+
export interface CountFinding {
|
|
8
|
+
readonly file: string
|
|
9
|
+
readonly line: number
|
|
10
|
+
readonly catalog: string
|
|
11
|
+
readonly stated: number
|
|
12
|
+
readonly actual: number
|
|
13
|
+
readonly sentence: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Narrower than `MarkdownAuditRefusal`. This sweep takes no path arguments,
|
|
18
|
+
* so `resolveMarkdown` can never return its `no-match` reason here.
|
|
19
|
+
*/
|
|
20
|
+
export type CountsRefusal = 'no-git' | 'no-markdown'
|
|
21
|
+
|
|
22
|
+
export type CountsReport =
|
|
23
|
+
| {
|
|
24
|
+
readonly kind: 'measured'
|
|
25
|
+
readonly filesScanned: number
|
|
26
|
+
/**
|
|
27
|
+
* The true count this run read for every catalog, keyed by id.
|
|
28
|
+
* `undefined` for a catalog this tree carries nothing to count, which
|
|
29
|
+
* `commands` is on every project but this repository.
|
|
30
|
+
*/
|
|
31
|
+
readonly catalogs: Readonly<Record<string, number | undefined>>
|
|
32
|
+
readonly findings: readonly CountFinding[]
|
|
33
|
+
}
|
|
34
|
+
| { readonly kind: 'unreadable'; readonly reason: CountsRefusal }
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A calendar date, which reads a sentence as a historical record rather than
|
|
38
|
+
* a live claim about the tree. `.claude/ARCHITECTURE.md` and the context
|
|
39
|
+
* entries carry a figure this way deliberately, and every one of them stays
|
|
40
|
+
* correct forever, so a sentence carrying one is read past rather than
|
|
41
|
+
* matched.
|
|
42
|
+
*/
|
|
43
|
+
const DATE = /\b\d{4}-\d{2}-\d{2}\b/
|
|
44
|
+
|
|
45
|
+
/** A commit-length hex token in a backtick span, the other marker of a dated record. */
|
|
46
|
+
const COMMIT = /`[0-9a-f]{7,12}`/
|
|
47
|
+
|
|
48
|
+
function isDated(sentence: string): boolean {
|
|
49
|
+
return DATE.test(sentence) || COMMIT.test(sentence)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Splits a rendered line into the sentences a reader sees.
|
|
54
|
+
*
|
|
55
|
+
* A markdown paragraph in this corpus is written as one long line rather than
|
|
56
|
+
* hard-wrapped, so a line is routinely several sentences deep, and a date
|
|
57
|
+
* naming one of them must not excuse a live claim two sentences away in the
|
|
58
|
+
* same line.
|
|
59
|
+
*/
|
|
60
|
+
function sentencesOf(line: string): string[] {
|
|
61
|
+
return line
|
|
62
|
+
.split(/(?<=[.!?])\s+/)
|
|
63
|
+
.map((sentence) => sentence.trim())
|
|
64
|
+
.filter((sentence) => sentence !== '')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A verb asserting the whole of something, immediately ahead of the number.
|
|
69
|
+
*
|
|
70
|
+
* The first design matched a bare number next to a catalog noun anywhere in
|
|
71
|
+
* the tracked corpus, and running it here returned 290 findings against a
|
|
72
|
+
* repository whose Files-to-touch names one live instance. Reading the
|
|
73
|
+
* corpus behind that run showed why: `18 rules citing a standard`, `21 skill
|
|
74
|
+
* bodies`, and `eight internal skills` all pair a number with a catalog noun
|
|
75
|
+
* while naming a subset, an example, or a different catalog than the one
|
|
76
|
+
* matched, and that shape dominates ordinary prose. Every instance this
|
|
77
|
+
* module was written against reads the number as the direct object of a verb
|
|
78
|
+
* asserting the catalog's own total, `the full entry loads 59 skills` being
|
|
79
|
+
* the live one, and gating the match on that verb is what a full re-read of
|
|
80
|
+
* the 290 showed removing all of them while keeping that one.
|
|
81
|
+
*
|
|
82
|
+
* `authors` joined the list on a corpus measurement rather than on the shape
|
|
83
|
+
* argument above. `.claude/context/development/regeneration.md` states `the
|
|
84
|
+
* toolkit authors 69 rules under governance/rules/`, which is the assertion
|
|
85
|
+
* shape this list was built for with a verb the list did not carry, and the
|
|
86
|
+
* word costs one further sentence across the whole corpus and no finding.
|
|
87
|
+
* Extending the vocabulary is the narrow half of this fix and it is taken
|
|
88
|
+
* knowingly: a list of thirteen verbs still grows by whatever the next author
|
|
89
|
+
* writes, and the measurement below is what ruled out the general axis that
|
|
90
|
+
* would have replaced it.
|
|
91
|
+
*/
|
|
92
|
+
const ASSERTION_VERBS = [
|
|
93
|
+
'loads',
|
|
94
|
+
'ships',
|
|
95
|
+
'carries',
|
|
96
|
+
'holds',
|
|
97
|
+
'counts',
|
|
98
|
+
'totals',
|
|
99
|
+
'documents',
|
|
100
|
+
'declares',
|
|
101
|
+
'installs',
|
|
102
|
+
'lists',
|
|
103
|
+
'contains',
|
|
104
|
+
'comprises',
|
|
105
|
+
'authors',
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A quantifier asserting the whole of a catalog immediately ahead of the
|
|
110
|
+
* number, which is the one trigger class that states a total outright rather
|
|
111
|
+
* than by implication.
|
|
112
|
+
*
|
|
113
|
+
* `all 69 rules` names the catalog entire, where `the 69 rules` leaves a
|
|
114
|
+
* reader to decide whether a qualifier narrows it. That makes this class
|
|
115
|
+
* tighter than the articles below rather than looser, and the corpus agrees:
|
|
116
|
+
* adding it reached 9 further sentences and produced 2 further findings, both
|
|
117
|
+
* of them real staleness this repository then repaired.
|
|
118
|
+
*
|
|
119
|
+
* `every` and `both` were measured beside `all` and reached nothing, so
|
|
120
|
+
* neither ships. A quantifier earns a place here by naming a sentence in the
|
|
121
|
+
* corpus, not by belonging to the same part of speech.
|
|
122
|
+
*/
|
|
123
|
+
const QUANTIFIERS = ['all']
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* An article immediately ahead of the number, admitting the shape a bare verb
|
|
127
|
+
* gate cannot reach: `the twelve audits read gitignored folders` asserts the
|
|
128
|
+
* catalog's own total with the verb sitting after the noun rather than ahead
|
|
129
|
+
* of the number.
|
|
130
|
+
*
|
|
131
|
+
* Found on this module's own first review, against a live instance the verb
|
|
132
|
+
* gate alone reported clean: `.claude/context/development/gates.md` stated
|
|
133
|
+
* `the twelve audits` while the tree held 20, sitting inside the plausibility
|
|
134
|
+
* bound this design already carries, so nothing but the missing shape kept it
|
|
135
|
+
* from reporting.
|
|
136
|
+
*
|
|
137
|
+
* The article is looser than the verb list, and re-running against the tree
|
|
138
|
+
* once it widened produced the class this trades for the miss above: `the 21
|
|
139
|
+
* flat standards` read a real subset (a standard outside the retired
|
|
140
|
+
* `standards/bundled/` fan-out) as a claim about the whole catalog, since
|
|
141
|
+
* `flat` fills the same optional-word slot `sixty-one shipped skills` needs to
|
|
142
|
+
* match at all. No syntactic rule tells a qualifier that narrows a catalog
|
|
143
|
+
* from one that only restates it, and the sentence was true when written, so
|
|
144
|
+
* the fix was not a narrower matcher: the clause was dated as a past state,
|
|
145
|
+
* the way `.claude/ARCHITECTURE.md` dates its own figures, which is the class
|
|
146
|
+
* the date exclusion below already reads past.
|
|
147
|
+
*/
|
|
148
|
+
const ARTICLES = ['the', 'a', 'an']
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Matches a catalog's stated size: an assertion verb, a quantifier, or an
|
|
152
|
+
* article, the number, an optional single qualifying word, then the noun in
|
|
153
|
+
* either number.
|
|
154
|
+
*
|
|
155
|
+
* The optional word between the number and the noun is what reaches a form
|
|
156
|
+
* like `installs 11 shipped snippets` without also reaching past an
|
|
157
|
+
* intervening clause, since a wider gap would start pairing a number in one
|
|
158
|
+
* clause with a noun in the next. Requiring the noun's own plural form to
|
|
159
|
+
* double as its match narrows the corpus this reads without hand-listing
|
|
160
|
+
* every irregular plural, since none of these six is irregular.
|
|
161
|
+
*
|
|
162
|
+
* The gap between the trigger and the number was the second axis this comment
|
|
163
|
+
* used to name as open to widening once the false-positive rate had been
|
|
164
|
+
* measured over more than two runs. It was measured over the whole corpus at
|
|
165
|
+
* 518 files and 31,836 sentences and it is not the axis to take. Widths of
|
|
166
|
+
* one, two, and three optional words reached 77, 104, and 132 sentences
|
|
167
|
+
* against a baseline of 65, and the two live misses that motivated the
|
|
168
|
+
* measurement came back missed at every width but one: no width reaches `took
|
|
169
|
+
* all 69 rules`, whose nearest verb or article is the `The` opening the
|
|
170
|
+
* sentence, with five words standing between it and the number, and the width
|
|
171
|
+
* that does reach `the toolkit authors 69 rules` is the same
|
|
172
|
+
* width that admits `a domain of 55 skills`, which is the indirect-noun shape
|
|
173
|
+
* this module already records as out of scope. Widening the gap buys one of
|
|
174
|
+
* two misses at 60 percent more reach and a false positive of a class already
|
|
175
|
+
* named.
|
|
176
|
+
*
|
|
177
|
+
* The trigger vocabulary carries both misses instead, at 75 sentences reached
|
|
178
|
+
* against 65 and no false positive: `all` reaches the first and `authors` the
|
|
179
|
+
* second. Two of the four findings that shape reports were new, and both were
|
|
180
|
+
* real. Measured at `ffe7e7c6` on 2026-08-28.
|
|
181
|
+
*
|
|
182
|
+
* What this still drops: `denominator of sixty-one shipped skills` states a
|
|
183
|
+
* real catalog total through an indirect noun and reads past, which the gap
|
|
184
|
+
* measurement above is the argument for leaving alone rather than an
|
|
185
|
+
* oversight.
|
|
186
|
+
*/
|
|
187
|
+
function buildMatcher(catalog: Catalog): RegExp {
|
|
188
|
+
const [singular, plural] = catalog.nouns
|
|
189
|
+
const triggers = [...ASSERTION_VERBS, ...QUANTIFIERS, ...ARTICLES].join('|')
|
|
190
|
+
return new RegExp(
|
|
191
|
+
`\\b(?:${triggers})\\s+(${NUMBER_PATTERN})(?:\\s+[a-z]+)?\\s+(?:${singular}|${plural})\\b`,
|
|
192
|
+
'gi',
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const MATCHERS: readonly { catalog: Catalog; regex: RegExp }[] = CATALOGS.map(
|
|
197
|
+
(catalog) => ({ catalog, regex: buildMatcher(catalog) }),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* A stated figure within a factor of two of the true count, in either
|
|
202
|
+
* direction.
|
|
203
|
+
*
|
|
204
|
+
* The verb gate alone still passed a run against this repository at four
|
|
205
|
+
* false positives to one real finding: `carries two rules about a
|
|
206
|
+
* standard's own lifecycle`, `holds one rule or one fact`, and `documents
|
|
207
|
+
* two similar commands` each pair an assertion verb with a catalog noun
|
|
208
|
+
* used for something this tree's `rules` or `commands` catalog does not
|
|
209
|
+
* mean. What tells those apart from the live finding is magnitude. A
|
|
210
|
+
* catalog this sweep tracks drifts by a few members between the day a
|
|
211
|
+
* sentence was written and the day it is read, so a genuine staleness claim
|
|
212
|
+
* sits near the true count, and `2` beside a true count of `59` is not a
|
|
213
|
+
* catalog that shrank, it is a different `rules` entirely.
|
|
214
|
+
*
|
|
215
|
+
* Tuned against the run this module was written against rather than
|
|
216
|
+
* reasoned to from first principles, the way `restated.ts` tunes its own
|
|
217
|
+
* document-frequency ceiling. Every catalog here sits in the tens, and the
|
|
218
|
+
* bound is a property of that scale rather than a universal one.
|
|
219
|
+
*/
|
|
220
|
+
const PLAUSIBLE_RATIO = 0.5
|
|
221
|
+
|
|
222
|
+
function isPlausibleClaim(stated: number, actual: number): boolean {
|
|
223
|
+
if (actual === 0) return stated === 0
|
|
224
|
+
return (
|
|
225
|
+
stated / actual >= PLAUSIBLE_RATIO && actual / stated >= PLAUSIBLE_RATIO
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Scans every tracked markdown file for a sentence stating how many members
|
|
231
|
+
* one of the closed catalogs holds, and compares the stated figure against
|
|
232
|
+
* what the tree actually counts.
|
|
233
|
+
*
|
|
234
|
+
* What it does not measure: a delta phrased as a transition (`from fourteen
|
|
235
|
+
* to fifteen`), a fraction (`thirteen of sixteen`), and a total reached
|
|
236
|
+
* through an indirect noun (`denominator of sixty-one shipped skills`) are
|
|
237
|
+
* all catalog-size claims this corpus carries, and none matches the trigger
|
|
238
|
+
* shape this reads. Each stays a known gap. The rate that used to gate
|
|
239
|
+
* closing them is measured now, and what it showed is that the widening they
|
|
240
|
+
* would need is the one that costs a false positive rather than the one that
|
|
241
|
+
* does not, which `buildMatcher` records with its numbers.
|
|
242
|
+
*
|
|
243
|
+
* A second figure in a sentence whose first figure already matched is a
|
|
244
|
+
* fourth gap and a structural one: `regex.exec` takes one match per catalog
|
|
245
|
+
* per sentence, so `authors 69 rules under governance/rules/ and consumes 54
|
|
246
|
+
* into .claude/rules/` is read for its 69 alone. Reaching the 54 would be
|
|
247
|
+
* wrong here rather than better, since it counts the consumed mirror rather
|
|
248
|
+
* than the source catalog this sweep tracks, but a sentence stating one
|
|
249
|
+
* catalog twice would go unread the same way.
|
|
250
|
+
*/
|
|
251
|
+
export async function scanCounts(root: string): Promise<CountsReport> {
|
|
252
|
+
const scope = await resolveMarkdown(root, [])
|
|
253
|
+
if (scope.kind === 'unavailable') {
|
|
254
|
+
return { kind: 'unreadable', reason: 'no-git' }
|
|
255
|
+
}
|
|
256
|
+
if (scope.files.length === 0) {
|
|
257
|
+
return { kind: 'unreadable', reason: 'no-markdown' }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const catalogs: Record<string, number | undefined> = {}
|
|
261
|
+
for (const catalog of CATALOGS) catalogs[catalog.id] = catalog.count(root)
|
|
262
|
+
|
|
263
|
+
const findings: CountFinding[] = []
|
|
264
|
+
|
|
265
|
+
for (const file of scope.files) {
|
|
266
|
+
const text = readFileSync(join(root, file), 'utf8')
|
|
267
|
+
let fenced = false
|
|
268
|
+
|
|
269
|
+
text.split('\n').forEach((line, index) => {
|
|
270
|
+
if (line.trimStart().startsWith('```')) {
|
|
271
|
+
fenced = !fenced
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
if (fenced) return
|
|
275
|
+
|
|
276
|
+
for (const sentence of sentencesOf(line)) {
|
|
277
|
+
if (isDated(sentence)) continue
|
|
278
|
+
|
|
279
|
+
for (const { catalog, regex } of MATCHERS) {
|
|
280
|
+
regex.lastIndex = 0
|
|
281
|
+
const match = regex.exec(sentence)
|
|
282
|
+
if (match === null) continue
|
|
283
|
+
|
|
284
|
+
const stated = parseNumber(match[1])
|
|
285
|
+
if (stated === undefined) continue
|
|
286
|
+
|
|
287
|
+
const actual = catalogs[catalog.id]
|
|
288
|
+
// No true count on this tree, which is the ordinary state of the
|
|
289
|
+
// `commands` catalog outside this repository. A stated figure has
|
|
290
|
+
// nothing to be judged stale against.
|
|
291
|
+
if (actual === undefined) continue
|
|
292
|
+
if (stated === actual) continue
|
|
293
|
+
if (!isPlausibleClaim(stated, actual)) continue
|
|
294
|
+
|
|
295
|
+
findings.push({
|
|
296
|
+
file,
|
|
297
|
+
line: index + 1,
|
|
298
|
+
catalog: catalog.id,
|
|
299
|
+
stated,
|
|
300
|
+
actual,
|
|
301
|
+
sentence,
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
kind: 'measured',
|
|
310
|
+
filesScanned: scope.files.length,
|
|
311
|
+
catalogs,
|
|
312
|
+
findings,
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the human-facing draft `canon-screencast` writes. Nothing here knows
|
|
3
|
+
* about a browser: the draft is prose aimed at a person, and turning it into
|
|
4
|
+
* something executable is `@/demo/compile`'s job.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface Beat {
|
|
8
|
+
readonly index: number
|
|
9
|
+
readonly name: string
|
|
10
|
+
readonly onScreen: string
|
|
11
|
+
readonly action: string
|
|
12
|
+
readonly watchFor: string
|
|
13
|
+
readonly emphasis: string
|
|
14
|
+
readonly caption: string
|
|
15
|
+
readonly transitionOut?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Draft {
|
|
19
|
+
readonly title: string
|
|
20
|
+
readonly beats: readonly Beat[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type DraftParse =
|
|
24
|
+
| { status: 'parsed'; draft: Draft }
|
|
25
|
+
| { status: 'failed'; reason: string }
|
|
26
|
+
|
|
27
|
+
const TITLE = /^#\s+Screencast:\s*(.+)$/m
|
|
28
|
+
const BEAT_SHEET = /^##\s+.*Beat sheet.*$/im
|
|
29
|
+
const BEAT_HEADING = /^###\s+Beat\s+(\d+)\s*:\s*(.*)$/i
|
|
30
|
+
const FIELD = /^-\s+([A-Za-z][A-Za-z\s]*?)\s*:\s*(.*)$/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Labels are matched case-insensitively with whitespace collapsed, because the
|
|
34
|
+
* draft is hand-edited between being written and being compiled and a
|
|
35
|
+
* capitalization change there is not a reason to refuse the whole file.
|
|
36
|
+
*/
|
|
37
|
+
const FIELDS: Record<string, keyof Beat> = {
|
|
38
|
+
'on screen': 'onScreen',
|
|
39
|
+
action: 'action',
|
|
40
|
+
'watch for': 'watchFor',
|
|
41
|
+
emphasis: 'emphasis',
|
|
42
|
+
caption: 'caption',
|
|
43
|
+
'transition out': 'transitionOut',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function parseDraft(markdown: string): DraftParse {
|
|
47
|
+
const sheet = beatSheetSection(markdown)
|
|
48
|
+
if (sheet === undefined) {
|
|
49
|
+
return {
|
|
50
|
+
status: 'failed',
|
|
51
|
+
reason: 'no "Beat sheet" section, so the draft carries no beats to run',
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const beats = readBeats(sheet)
|
|
56
|
+
if (!beats.length) {
|
|
57
|
+
return {
|
|
58
|
+
status: 'failed',
|
|
59
|
+
reason: 'the beat sheet holds no "### Beat" heading',
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
status: 'parsed',
|
|
65
|
+
draft: { title: TITLE.exec(markdown)?.[1]?.trim() ?? '', beats },
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns the lines between the beat sheet heading and the next `##`, so a
|
|
71
|
+
* `### Beat` heading elsewhere in the draft cannot be read as a beat.
|
|
72
|
+
*/
|
|
73
|
+
function beatSheetSection(markdown: string): string[] | undefined {
|
|
74
|
+
const lines = markdown.split('\n')
|
|
75
|
+
const start = lines.findIndex((line) => BEAT_SHEET.test(line))
|
|
76
|
+
if (start === -1) return undefined
|
|
77
|
+
|
|
78
|
+
const rest = lines.slice(start + 1)
|
|
79
|
+
const end = rest.findIndex((line) => /^##\s/.test(line))
|
|
80
|
+
return end === -1 ? rest : rest.slice(0, end)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readBeats(lines: string[]): Beat[] {
|
|
84
|
+
const beats: Beat[] = []
|
|
85
|
+
let open: Partial<Beat> | undefined
|
|
86
|
+
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
const heading = BEAT_HEADING.exec(line)
|
|
89
|
+
if (heading) {
|
|
90
|
+
if (open) beats.push(sealBeat(open))
|
|
91
|
+
open = {
|
|
92
|
+
index: Number(heading[1]),
|
|
93
|
+
name: (heading[2] ?? '').trim(),
|
|
94
|
+
}
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!open) continue
|
|
99
|
+
const field = FIELD.exec(line)
|
|
100
|
+
if (!field) continue
|
|
101
|
+
|
|
102
|
+
const key = FIELDS[normalizeLabel(field[1] ?? '')]
|
|
103
|
+
if (key) open = { ...open, [key]: (field[2] ?? '').trim() }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (open) beats.push(sealBeat(open))
|
|
107
|
+
return beats
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Fills the five required fields with empty strings rather than leaving them
|
|
112
|
+
* undefined, so a downstream reader distinguishes "the operator left it blank"
|
|
113
|
+
* from "the label was misspelt" by the presence of the key alone.
|
|
114
|
+
*
|
|
115
|
+
* `transitionOut` stays optional, because the draft adds it only when it is
|
|
116
|
+
* not the default and an empty one would claim a decision nobody made.
|
|
117
|
+
*/
|
|
118
|
+
function sealBeat(open: Partial<Beat>): Beat {
|
|
119
|
+
const beat: Beat = {
|
|
120
|
+
index: open.index ?? 0,
|
|
121
|
+
name: open.name ?? '',
|
|
122
|
+
onScreen: open.onScreen ?? '',
|
|
123
|
+
action: open.action ?? '',
|
|
124
|
+
watchFor: open.watchFor ?? '',
|
|
125
|
+
emphasis: open.emphasis ?? '',
|
|
126
|
+
caption: open.caption ?? '',
|
|
127
|
+
}
|
|
128
|
+
return open.transitionOut === undefined
|
|
129
|
+
? beat
|
|
130
|
+
: { ...beat, transitionOut: open.transitionOut }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizeLabel(label: string): string {
|
|
134
|
+
return label.trim().toLowerCase().replace(/\s+/g, ' ')
|
|
135
|
+
}
|