@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,115 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import { readFile, mkdir, rmdir, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { dirname, join, relative } from 'node:path'
|
|
4
|
+
import { gitEnv } from '@/git-env'
|
|
5
|
+
import type { RenamePlan, RenameSource } from '@/migrate/plan'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A file whose bytes carry a NUL is read as binary and its content is left
|
|
9
|
+
* alone, which lets the path still move. Reporting the empty string rather
|
|
10
|
+
* than a flag keeps the planner pure: an unchanged content rewrite is already
|
|
11
|
+
* how it expresses "path only", so binary needs no branch of its own there.
|
|
12
|
+
*/
|
|
13
|
+
export async function readSources(
|
|
14
|
+
root: string,
|
|
15
|
+
paths: readonly string[],
|
|
16
|
+
): Promise<RenameSource[]> {
|
|
17
|
+
const sources: RenameSource[] = []
|
|
18
|
+
|
|
19
|
+
for (const path of paths) {
|
|
20
|
+
const bytes = await readFile(join(root, path)).catch(() => undefined)
|
|
21
|
+
if (bytes === undefined) continue
|
|
22
|
+
|
|
23
|
+
const binary = bytes.includes(0)
|
|
24
|
+
sources.push({ path, text: binary ? '' : bytes.toString('utf8') })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return sources
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ApplyResult {
|
|
31
|
+
readonly written: number
|
|
32
|
+
readonly moved: number
|
|
33
|
+
readonly failed: readonly string[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Writes the plan. Content lands first and the move follows, so a failed move
|
|
38
|
+
* leaves the rewrite on a path that still exists rather than stranding content
|
|
39
|
+
* at a destination nothing points at yet.
|
|
40
|
+
*
|
|
41
|
+
* `git mv` rather than a filesystem rename, so the history follows the file and
|
|
42
|
+
* a reviewer reading the pull request sees a rename instead of a delete beside
|
|
43
|
+
* an add.
|
|
44
|
+
*/
|
|
45
|
+
export async function applyRename(
|
|
46
|
+
root: string,
|
|
47
|
+
plan: RenamePlan,
|
|
48
|
+
): Promise<ApplyResult> {
|
|
49
|
+
let written = 0
|
|
50
|
+
let moved = 0
|
|
51
|
+
const failed: string[] = []
|
|
52
|
+
|
|
53
|
+
for (const entry of plan.entries) {
|
|
54
|
+
if (entry.text !== undefined) {
|
|
55
|
+
await writeFile(join(root, entry.path), entry.text)
|
|
56
|
+
written += 1
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (entry.movesTo === undefined) continue
|
|
60
|
+
|
|
61
|
+
await mkdir(dirname(join(root, entry.movesTo)), { recursive: true })
|
|
62
|
+
const result = await $`git -C ${root} mv ${entry.path} ${entry.movesTo}`
|
|
63
|
+
.env(gitEnv())
|
|
64
|
+
.quiet()
|
|
65
|
+
.nothrow()
|
|
66
|
+
|
|
67
|
+
if (result.exitCode === 0) moved += 1
|
|
68
|
+
else failed.push(entry.path)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await pruneEmptyParents(
|
|
72
|
+
root,
|
|
73
|
+
plan.entries
|
|
74
|
+
.filter((entry) => entry.movesTo !== undefined)
|
|
75
|
+
.map((entry) => dirname(entry.path)),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return { written, moved, failed }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Removes the directories a move emptied.
|
|
83
|
+
*
|
|
84
|
+
* Git tracks files rather than directories, so moving the last file out of one
|
|
85
|
+
* leaves the directory on disk with nothing in it and nothing in the diff to
|
|
86
|
+
* say so. A renamed skill folder that keeps its old shell beside the new one
|
|
87
|
+
* reads to a person, and to a plugin loader walking the tree, as though the
|
|
88
|
+
* rename only half happened.
|
|
89
|
+
*
|
|
90
|
+
* `rmdir` refuses a directory that still holds anything, which is the guard:
|
|
91
|
+
* a folder with an untracked file in it stays, and the refusal is ignored
|
|
92
|
+
* rather than reported because it means the folder was not empty to begin
|
|
93
|
+
* with.
|
|
94
|
+
*/
|
|
95
|
+
async function pruneEmptyParents(
|
|
96
|
+
root: string,
|
|
97
|
+
directories: readonly string[],
|
|
98
|
+
): Promise<void> {
|
|
99
|
+
const deepestFirst = [...new Set(directories)].sort(
|
|
100
|
+
(left, right) => right.length - left.length,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
for (const directory of deepestFirst) {
|
|
104
|
+
let current = directory
|
|
105
|
+
|
|
106
|
+
while (current !== '.' && !current.startsWith('..')) {
|
|
107
|
+
const removed = await rmdir(join(root, current))
|
|
108
|
+
.then(() => true)
|
|
109
|
+
.catch(() => false)
|
|
110
|
+
if (!removed) break
|
|
111
|
+
|
|
112
|
+
current = relative(root, join(root, current, '..'))
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isExcludedPath,
|
|
3
|
+
renamePath,
|
|
4
|
+
renameText,
|
|
5
|
+
scanText,
|
|
6
|
+
} from '@/migrate/rename'
|
|
7
|
+
|
|
8
|
+
/** One tracked file, as the planner reads it. */
|
|
9
|
+
export interface RenameSource {
|
|
10
|
+
readonly path: string
|
|
11
|
+
readonly text: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* One file the sweep touches. `text` is absent when only the path moves, so a
|
|
16
|
+
* caller writing the plan can tell a content rewrite from a pure move and
|
|
17
|
+
* leave a binary file's bytes alone.
|
|
18
|
+
*/
|
|
19
|
+
export interface RenameEntry {
|
|
20
|
+
readonly path: string
|
|
21
|
+
readonly movesTo?: string
|
|
22
|
+
readonly text?: string
|
|
23
|
+
readonly renamed: number
|
|
24
|
+
readonly protectedCount: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RenamePlan {
|
|
28
|
+
readonly entries: readonly RenameEntry[]
|
|
29
|
+
readonly excluded: readonly string[]
|
|
30
|
+
readonly renamed: number
|
|
31
|
+
readonly protectedCount: number
|
|
32
|
+
readonly moves: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Folders a target receives from the toolkit and does not author.
|
|
37
|
+
*
|
|
38
|
+
* The rename may rewrite these in a consuming project, because their content
|
|
39
|
+
* came from here and a stale spelling in them is the toolkit's own defect.
|
|
40
|
+
* Everything else in a target is prose that project wrote, where a citation is
|
|
41
|
+
* reported for a person to decide rather than rewritten underneath them.
|
|
42
|
+
*/
|
|
43
|
+
const TOOLKIT_OWNED: readonly string[] = [
|
|
44
|
+
'.claude/aitk/',
|
|
45
|
+
'.claude/canon/',
|
|
46
|
+
'.claude/rules/',
|
|
47
|
+
'.claude/tooling/',
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
export function isToolkitOwned(path: string): boolean {
|
|
51
|
+
return TOOLKIT_OWNED.some((owned) => path.startsWith(owned))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* What the sweep would do to a set of files.
|
|
56
|
+
*
|
|
57
|
+
* Pure, so the decision and the write are separable and the same plan can be
|
|
58
|
+
* reported or applied. A file whose content and path both stay put is dropped
|
|
59
|
+
* rather than carried as a no-op entry, which keeps the reported count equal
|
|
60
|
+
* to the number of files the sweep actually changes.
|
|
61
|
+
*/
|
|
62
|
+
export function planRename(sources: readonly RenameSource[]): RenamePlan {
|
|
63
|
+
const entries: RenameEntry[] = []
|
|
64
|
+
const excluded: string[] = []
|
|
65
|
+
|
|
66
|
+
for (const source of sources) {
|
|
67
|
+
if (isExcludedPath(source.path)) {
|
|
68
|
+
excluded.push(source.path)
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const movesTo = renamePath(source.path)
|
|
73
|
+
const rewritten = renameText(source.text)
|
|
74
|
+
const counts = scanText(source.text)
|
|
75
|
+
const moved = movesTo !== source.path
|
|
76
|
+
const changed = rewritten !== source.text
|
|
77
|
+
|
|
78
|
+
if (!moved && !changed) continue
|
|
79
|
+
|
|
80
|
+
entries.push({
|
|
81
|
+
path: source.path,
|
|
82
|
+
...(moved ? { movesTo } : {}),
|
|
83
|
+
...(changed ? { text: rewritten } : {}),
|
|
84
|
+
renamed: counts.renamed,
|
|
85
|
+
protectedCount: counts.protectedCount,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
entries,
|
|
91
|
+
excluded,
|
|
92
|
+
renamed: total(entries, (entry) => entry.renamed),
|
|
93
|
+
protectedCount: total(entries, (entry) => entry.protectedCount),
|
|
94
|
+
moves: entries.filter((entry) => entry.movesTo !== undefined).length,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function total(
|
|
99
|
+
entries: readonly RenameEntry[],
|
|
100
|
+
read: (entry: RenameEntry) => number,
|
|
101
|
+
): number {
|
|
102
|
+
return entries.reduce((sum, entry) => sum + read(entry), 0)
|
|
103
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The token rewrite behind the `aitk` to `canon` rename.
|
|
3
|
+
*
|
|
4
|
+
* The sweep is mechanical and its danger is entirely in what it must not
|
|
5
|
+
* touch, so the scanner is one pass with the protected forms tried first
|
|
6
|
+
* rather than a chain of replacements. A chain reprocesses its own output,
|
|
7
|
+
* which is how a protected form that contains the token gets rewritten by a
|
|
8
|
+
* later rule that cannot see it was already decided.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Forms carrying the token that name something other than this tool, matched
|
|
13
|
+
* ahead of the token itself so they pass through untouched.
|
|
14
|
+
*
|
|
15
|
+
* `aitk-sandbox` is a separate repository that is not being renamed. It has to
|
|
16
|
+
* win against the bare token, and it also has to win against the owner-scoped
|
|
17
|
+
* spelling, since `erclx/aitk-sandbox` would otherwise rewrite to
|
|
18
|
+
* `erclx/canon-sandbox` and name a repository that does not exist.
|
|
19
|
+
*/
|
|
20
|
+
const PROTECTED = ['aitk-sandbox'] as const
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Every spelling of the token, and what each becomes. Case is carried in the
|
|
24
|
+
* map rather than derived, because the uppercase form is an environment
|
|
25
|
+
* variable prefix and the title-case form is a heading word, and a derived
|
|
26
|
+
* transform would have to guess which convention it was looking at.
|
|
27
|
+
*/
|
|
28
|
+
const REPLACEMENT: Readonly<Record<string, string>> = {
|
|
29
|
+
aitk: 'canon',
|
|
30
|
+
AITK: 'CANON',
|
|
31
|
+
Aitk: 'Canon',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* One alternation so the engine decides each position once. Group 1 is a
|
|
36
|
+
* protected form and group 2 is a token to rewrite, and the protected branch
|
|
37
|
+
* sits first because a regex alternation is ordered.
|
|
38
|
+
*/
|
|
39
|
+
const SCAN = new RegExp(
|
|
40
|
+
`(${PROTECTED.join('|')})|(${Object.keys(REPLACEMENT).join('|')})`,
|
|
41
|
+
'g',
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Files whose content is left alone entirely.
|
|
46
|
+
*
|
|
47
|
+
* The changelog is release history. Its entries record what shipped under the
|
|
48
|
+
* old name, so rewriting them falsifies the record, and the pull request links
|
|
49
|
+
* it carries keep resolving because GitHub redirects a renamed repository's
|
|
50
|
+
* old URLs.
|
|
51
|
+
*
|
|
52
|
+
* The sweep's own source is the other member, and it is not a preference. This
|
|
53
|
+
* module states the token map as literal keys, so rewriting it turns every key
|
|
54
|
+
* into its own replacement and leaves a rewriter that maps `canon` to `canon`
|
|
55
|
+
* and matches nothing. Its tests name both spellings on purpose for the same
|
|
56
|
+
* reason, and the command's help text documents the old name a caller is
|
|
57
|
+
* migrating off. Whatever these four files should say after the rename is
|
|
58
|
+
* written by hand, because the sweep cannot be the thing that decides it.
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* An eval result is a transcript. It records the commands a session actually
|
|
62
|
+
* ran and the paths it actually opened, under whatever name was current when
|
|
63
|
+
* the run happened, so rewriting one makes it testify to a session that never
|
|
64
|
+
* took place. The changelog is excluded for the same reason and differs only
|
|
65
|
+
* in living at a fixed path.
|
|
66
|
+
*/
|
|
67
|
+
const EXCLUDED_PREFIXES: readonly string[] = [
|
|
68
|
+
'src/migrate/',
|
|
69
|
+
'scripts/eval/result-',
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The four test files below exist to prove the retired spellings still
|
|
74
|
+
* resolve, so both names appear in each on purpose. Rewriting one is worse
|
|
75
|
+
* than a broken test: the retired-variable case would collapse into a copy of
|
|
76
|
+
* the current-variable case beside it and keep passing, reporting coverage for
|
|
77
|
+
* a fallback nothing exercises any more.
|
|
78
|
+
*/
|
|
79
|
+
const EXCLUDED_PATHS: readonly string[] = [
|
|
80
|
+
'CHANGELOG.md',
|
|
81
|
+
'src/commands/migrate.ts',
|
|
82
|
+
'src/sync/stamp.test.ts',
|
|
83
|
+
'src/targets/registry.test.ts',
|
|
84
|
+
'src/targets/sweep.test.ts',
|
|
85
|
+
'src/ui.test.ts',
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
export interface ScanCount {
|
|
89
|
+
readonly renamed: number
|
|
90
|
+
readonly protectedCount: number
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isExcludedPath(path: string): boolean {
|
|
94
|
+
if (EXCLUDED_PATHS.includes(path)) return true
|
|
95
|
+
return EXCLUDED_PREFIXES.some((prefix) => path.startsWith(prefix))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* An indefinite article that agreed with the retired name and no longer
|
|
100
|
+
* agrees with the replacement.
|
|
101
|
+
*
|
|
102
|
+
* The old name opens on a vowel sound and the new one does not, so every
|
|
103
|
+
* `an aitk` in the corpus reads wrong the moment the token moves. This matches
|
|
104
|
+
* against the already-rewritten text rather than the source, which is what
|
|
105
|
+
* keeps it clear of the protected forms: `an aitk-sandbox` still says
|
|
106
|
+
* `aitk-sandbox` afterward, so it never matches here.
|
|
107
|
+
*
|
|
108
|
+
* The tail rejects a following letter rather than asking for a word boundary,
|
|
109
|
+
* which is what separates `an canonical` from `an CANON_STATE_DIR`. A boundary
|
|
110
|
+
* treats the underscore as part of the word and declines the environment
|
|
111
|
+
* variable, where the whole identifier is the token continuing.
|
|
112
|
+
*/
|
|
113
|
+
const ARTICLE = /\b([Aa])n(\s+`?)(canon|CANON|Canon)(?![A-Za-z])/g
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Marks a line that names the retired spelling on purpose.
|
|
117
|
+
*
|
|
118
|
+
* A fallback path, a retired environment variable, and a dictionary entry
|
|
119
|
+
* covering the record corpora all have to keep saying the old name, and a
|
|
120
|
+
* second run over an already-renamed tree would otherwise strip exactly the
|
|
121
|
+
* compatibility this rename shipped. The marker sits on the line itself or on
|
|
122
|
+
* the one above it, which is the same placement `canon-allow-superseded`
|
|
123
|
+
* already uses in this repository.
|
|
124
|
+
*/
|
|
125
|
+
const KEEP_MARKER = 'canon-keep-retired'
|
|
126
|
+
|
|
127
|
+
/** Rewrites every unprotected spelling of the token. */
|
|
128
|
+
export function renameText(text: string): string {
|
|
129
|
+
const lines = text.split('\n')
|
|
130
|
+
const rewritten = lines.map((line, index) =>
|
|
131
|
+
isKept(lines, index) ? line : renameLine(line),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return rewritten.join('\n')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isKept(lines: readonly string[], index: number): boolean {
|
|
138
|
+
if (lines[index]?.includes(KEEP_MARKER)) return true
|
|
139
|
+
return index > 0 && (lines[index - 1]?.includes(KEEP_MARKER) ?? false)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function renameLine(line: string): string {
|
|
143
|
+
const replaced = line.replace(SCAN, (match, guarded: string | undefined) =>
|
|
144
|
+
guarded === undefined ? REPLACEMENT[match] : guarded,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return replaced.replace(
|
|
148
|
+
ARTICLE,
|
|
149
|
+
(_match, article: string, gap: string, token: string) =>
|
|
150
|
+
`${article}${gap}${token}`,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* What `renameText` would do, without doing it. Reported rather than inferred
|
|
156
|
+
* from a diff so a run can say how much it protected, which is the number a
|
|
157
|
+
* reader needs to trust that the exclusions fired at all.
|
|
158
|
+
*/
|
|
159
|
+
export function scanText(text: string): ScanCount {
|
|
160
|
+
let renamed = 0
|
|
161
|
+
let protectedCount = 0
|
|
162
|
+
const lines = text.split('\n')
|
|
163
|
+
|
|
164
|
+
for (const [index, line] of lines.entries()) {
|
|
165
|
+
if (isKept(lines, index)) continue
|
|
166
|
+
|
|
167
|
+
for (const [, guarded] of line.matchAll(SCAN)) {
|
|
168
|
+
if (guarded === undefined) renamed += 1
|
|
169
|
+
else protectedCount += 1
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { renamed, protectedCount }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The path a file moves to. Runs the same scanner as the content rewrite, so
|
|
178
|
+
* a protected form appearing in a path is protected there too and the two
|
|
179
|
+
* halves of the sweep cannot disagree about what the token means.
|
|
180
|
+
*/
|
|
181
|
+
export function renamePath(path: string): string {
|
|
182
|
+
return renameText(path)
|
|
183
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { extractKeyChangePaths, KEY_CHANGES, type PathClaim } from '@/pr/paths'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Why the comparison produced no reading.
|
|
5
|
+
*
|
|
6
|
+
* The three are apart because each names a different repair. `no-section` is a
|
|
7
|
+
* body missing the heading, `no-claims` is the extractor failing over a section
|
|
8
|
+
* full of prose, and `no-changes` is a pull request with no files. Folding any
|
|
9
|
+
* of them into a clean pass is the failure this check exists to prevent, since
|
|
10
|
+
* a reader cannot tell a check that found nothing from one that read nothing.
|
|
11
|
+
*/
|
|
12
|
+
export type BijectionRefusal = 'no-section' | 'no-claims' | 'no-changes'
|
|
13
|
+
|
|
14
|
+
export interface BijectionReport {
|
|
15
|
+
readonly kind: 'measured'
|
|
16
|
+
/** The commit the changed set was read at, which a finding has to name. */
|
|
17
|
+
readonly head: string | undefined
|
|
18
|
+
readonly changed: readonly string[]
|
|
19
|
+
readonly claims: readonly PathClaim[]
|
|
20
|
+
/**
|
|
21
|
+
* Anchored claims no changed file answers, which is the graded direction. A
|
|
22
|
+
* bullet naming an untouched file is wrong more often than not, and this
|
|
23
|
+
* corpus reported zero of them across 23 correct bodies.
|
|
24
|
+
*/
|
|
25
|
+
readonly unmet: readonly PathClaim[]
|
|
26
|
+
/**
|
|
27
|
+
* Claims whose first segment names no entry in the tree, so the comparison
|
|
28
|
+
* could not judge them either way. Reported so a run says what it declined
|
|
29
|
+
* rather than counting a partial spelling as met.
|
|
30
|
+
*/
|
|
31
|
+
readonly unresolved: readonly PathClaim[]
|
|
32
|
+
/**
|
|
33
|
+
* Changed files no claim reaches. Reported without a severity, since the
|
|
34
|
+
* class covers a real omission and equally a lockfile, a generated asset, or
|
|
35
|
+
* a regenerated index that legitimately earns no bullet.
|
|
36
|
+
*/
|
|
37
|
+
readonly unnamed: readonly string[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type Bijection =
|
|
41
|
+
| BijectionReport
|
|
42
|
+
| { readonly kind: 'refused'; readonly reason: BijectionRefusal }
|
|
43
|
+
|
|
44
|
+
export interface BijectionInput {
|
|
45
|
+
readonly body: string
|
|
46
|
+
readonly changed: readonly string[]
|
|
47
|
+
/** Top-level entries the tree holds, which decides what counts as anchored. */
|
|
48
|
+
readonly roots: ReadonlySet<string>
|
|
49
|
+
readonly head?: string
|
|
50
|
+
readonly title?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether one changed path is the file, or a file under the folder, a claim
|
|
55
|
+
* names.
|
|
56
|
+
*
|
|
57
|
+
* An unanchored claim matches on a segment-anchored suffix, which is what lets
|
|
58
|
+
* `claude-worker/SKILL.md` credit `claude/skills/claude-worker/SKILL.md`. That
|
|
59
|
+
* asymmetry is deliberate: a partial spelling can confirm a changed file was
|
|
60
|
+
* named and never accuse one of being absent, because nothing here separates a
|
|
61
|
+
* path written short from a path written wrong.
|
|
62
|
+
*/
|
|
63
|
+
function covers(claim: PathClaim, path: string): boolean {
|
|
64
|
+
if (claim.directory) {
|
|
65
|
+
return claim.anchored
|
|
66
|
+
? path.startsWith(claim.path)
|
|
67
|
+
: path.includes(`/${claim.path}`)
|
|
68
|
+
}
|
|
69
|
+
if (path === claim.path) return true
|
|
70
|
+
return !claim.anchored && path.endsWith(`/${claim.path}`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Compares what a pull request body claims to have changed against what it
|
|
75
|
+
* actually changed, in both directions.
|
|
76
|
+
*
|
|
77
|
+
* The two directions are reported apart because they want different
|
|
78
|
+
* tolerances. A claim nobody made good on is a defect in the record that
|
|
79
|
+
* squash-merges onto the trunk, and a changed file nobody recorded is often
|
|
80
|
+
* correct. Merging them into one count would either grade the second or excuse
|
|
81
|
+
* the first.
|
|
82
|
+
*
|
|
83
|
+
* Pure, so the whole judgment is testable against a fixture. The caller reads
|
|
84
|
+
* the body, the changed set, and the tree roots and hands all three in.
|
|
85
|
+
*/
|
|
86
|
+
export function compareKeyChanges(input: BijectionInput): Bijection {
|
|
87
|
+
if (input.changed.length === 0)
|
|
88
|
+
return { kind: 'refused', reason: 'no-changes' }
|
|
89
|
+
|
|
90
|
+
const read = extractKeyChangePaths(
|
|
91
|
+
input.body,
|
|
92
|
+
input.roots,
|
|
93
|
+
input.title ?? KEY_CHANGES,
|
|
94
|
+
)
|
|
95
|
+
if (read.kind === 'no-section')
|
|
96
|
+
return { kind: 'refused', reason: 'no-section' }
|
|
97
|
+
if (read.claims.length === 0) return { kind: 'refused', reason: 'no-claims' }
|
|
98
|
+
|
|
99
|
+
const unmet: PathClaim[] = []
|
|
100
|
+
const unresolved: PathClaim[] = []
|
|
101
|
+
const named = new Set<string>()
|
|
102
|
+
|
|
103
|
+
for (const claim of read.claims) {
|
|
104
|
+
const hits = input.changed.filter((path) => covers(claim, path))
|
|
105
|
+
for (const path of hits) named.add(path)
|
|
106
|
+
if (hits.length > 0) continue
|
|
107
|
+
if (claim.anchored) unmet.push(claim)
|
|
108
|
+
else unresolved.push(claim)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
kind: 'measured',
|
|
113
|
+
head: input.head,
|
|
114
|
+
changed: [...input.changed],
|
|
115
|
+
claims: read.claims,
|
|
116
|
+
unmet,
|
|
117
|
+
unresolved,
|
|
118
|
+
unnamed: input.changed.filter((path) => !named.has(path)),
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The top-level folders a claim may be anchored on: the first segment of every
|
|
124
|
+
* path the tree holds, plus the first segment of every changed path.
|
|
125
|
+
*
|
|
126
|
+
* The changed half is what admits a folder this branch created. Reading the
|
|
127
|
+
* tree alone would mark every claim under a new top-level directory unanchored,
|
|
128
|
+
* and an unanchored claim never accuses, so the first branch to open one would
|
|
129
|
+
* silently lose the graded direction.
|
|
130
|
+
*
|
|
131
|
+
* A path with no folder above it contributes nothing, since a claim carrying no
|
|
132
|
+
* slash never reaches the extractor's output and no root would ever be read
|
|
133
|
+
* against it.
|
|
134
|
+
*/
|
|
135
|
+
export function treeRoots(
|
|
136
|
+
tracked: readonly string[],
|
|
137
|
+
changed: readonly string[],
|
|
138
|
+
): Set<string> {
|
|
139
|
+
const roots = new Set<string>()
|
|
140
|
+
for (const path of [...tracked, ...changed]) {
|
|
141
|
+
const at = path.indexOf('/')
|
|
142
|
+
if (at > 0) roots.add(path.slice(0, at))
|
|
143
|
+
}
|
|
144
|
+
return roots
|
|
145
|
+
}
|