@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,184 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
import { $ } from 'bun'
|
|
4
|
+
import type { Command } from 'commander'
|
|
5
|
+
import { exitCodeFor, type RegenResult, regenOne } from '@/indexes/regen'
|
|
6
|
+
import { gitEnv } from '@/git-env'
|
|
7
|
+
import { findIndexedAncestor, isIgnored, listIndexes } from '@/indexes/walk'
|
|
8
|
+
import { intro, logAdd, logInfo, logStep, logWarn, outro } from '@/ui'
|
|
9
|
+
|
|
10
|
+
interface RegenCommandOptions {
|
|
11
|
+
readonly dryRun?: boolean
|
|
12
|
+
readonly json?: boolean
|
|
13
|
+
readonly root?: string
|
|
14
|
+
readonly stage?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function register(program: Command): void {
|
|
18
|
+
const indexes = program
|
|
19
|
+
.command('indexes')
|
|
20
|
+
.description('Regenerate index.md files from sibling frontmatter')
|
|
21
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
22
|
+
|
|
23
|
+
indexes
|
|
24
|
+
.command('regen')
|
|
25
|
+
.description('Regenerate index.md files from sibling frontmatter')
|
|
26
|
+
.argument(
|
|
27
|
+
'[paths...]',
|
|
28
|
+
'Paths to resolve up to their nearest indexed folder',
|
|
29
|
+
)
|
|
30
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
31
|
+
.option('--dry-run', 'Report changes without writing')
|
|
32
|
+
.option('--json', 'Emit a machine-readable record per index on stdout')
|
|
33
|
+
.option('--root <path>', 'Walk-up boundary for positional paths')
|
|
34
|
+
.option('--no-stage', 'Skip the auto git add on modified indexes')
|
|
35
|
+
.addHelpText(
|
|
36
|
+
'after',
|
|
37
|
+
[
|
|
38
|
+
'',
|
|
39
|
+
'Exit codes:',
|
|
40
|
+
' 0 no drift, nothing to do',
|
|
41
|
+
' 1 frontmatter error or missing index.md',
|
|
42
|
+
' 2 --dry-run found folders that would change',
|
|
43
|
+
'',
|
|
44
|
+
'Examples:',
|
|
45
|
+
' canon indexes regen',
|
|
46
|
+
' canon indexes regen --dry-run',
|
|
47
|
+
' canon indexes regen --json docs/',
|
|
48
|
+
'',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
)
|
|
51
|
+
.action(async (paths: string[], opts: RegenCommandOptions) => {
|
|
52
|
+
process.exitCode = await runRegen(paths, opts)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function runRegen(
|
|
57
|
+
paths: string[],
|
|
58
|
+
opts: RegenCommandOptions,
|
|
59
|
+
): Promise<number> {
|
|
60
|
+
const root = resolve(opts.root ?? process.cwd())
|
|
61
|
+
const dryRun = opts.dryRun ?? false
|
|
62
|
+
const emitJson = opts.json ?? false
|
|
63
|
+
|
|
64
|
+
if (!(await isDirectory(root))) {
|
|
65
|
+
if (emitJson) {
|
|
66
|
+
process.stdout.write(
|
|
67
|
+
`${JSON.stringify({ error: `root not a directory: ${root}` })}\n`,
|
|
68
|
+
)
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
process.stderr.write(`Root is not a directory: ${root}\n`)
|
|
72
|
+
return 1
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const dirs =
|
|
76
|
+
paths.length > 0
|
|
77
|
+
? collectFromPaths(paths, root)
|
|
78
|
+
: (await listIndexes(root)).map(dirname)
|
|
79
|
+
|
|
80
|
+
const shouldStage =
|
|
81
|
+
paths.length > 0 && opts.stage !== false && !dryRun && !emitJson
|
|
82
|
+
|
|
83
|
+
const results: RegenResult[] = []
|
|
84
|
+
|
|
85
|
+
if (!emitJson) {
|
|
86
|
+
intro('canon indexes')
|
|
87
|
+
logStep(dryRun ? 'Indexes (dry-run)' : 'Indexes')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const dir of dirs) {
|
|
91
|
+
const result = await regenOne(dir, { dryRun })
|
|
92
|
+
results.push(result)
|
|
93
|
+
|
|
94
|
+
for (const message of result.errors ?? []) {
|
|
95
|
+
process.stderr.write(`ERROR: ${message}\n`)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!emitJson) await reportResult(result, root, shouldStage)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (emitJson) {
|
|
102
|
+
process.stdout.write(
|
|
103
|
+
`${JSON.stringify({
|
|
104
|
+
root,
|
|
105
|
+
dryRun,
|
|
106
|
+
results: results.map(({ path, action, reason }) =>
|
|
107
|
+
reason ? { path, action, reason } : { path, action },
|
|
108
|
+
),
|
|
109
|
+
})}\n`,
|
|
110
|
+
)
|
|
111
|
+
} else {
|
|
112
|
+
outro()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return exitCodeFor(results, { dryRun })
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function collectFromPaths(paths: string[], root: string): string[] {
|
|
119
|
+
const dirs: string[] = []
|
|
120
|
+
|
|
121
|
+
for (const path of paths) {
|
|
122
|
+
const dir = findIndexedAncestor(resolve(root, path), root)
|
|
123
|
+
if (!dir) {
|
|
124
|
+
process.stderr.write(
|
|
125
|
+
`WARN: ${path} has no index.md ancestor under ${root}\n`,
|
|
126
|
+
)
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
if (!dirs.includes(dir)) dirs.push(dir)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return dirs
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function reportResult(
|
|
136
|
+
result: RegenResult,
|
|
137
|
+
root: string,
|
|
138
|
+
shouldStage: boolean,
|
|
139
|
+
): Promise<void> {
|
|
140
|
+
const rel = result.path.startsWith(`${root}/`)
|
|
141
|
+
? result.path.slice(root.length + 1)
|
|
142
|
+
: result.path
|
|
143
|
+
|
|
144
|
+
switch (result.action) {
|
|
145
|
+
case 'written':
|
|
146
|
+
logAdd(rel)
|
|
147
|
+
if (shouldStage) await stage(result.path, rel, root)
|
|
148
|
+
break
|
|
149
|
+
case 'would-write':
|
|
150
|
+
logWarn(`${rel} would change`)
|
|
151
|
+
break
|
|
152
|
+
case 'unchanged':
|
|
153
|
+
logInfo(`${rel} unchanged`)
|
|
154
|
+
break
|
|
155
|
+
case 'skipped':
|
|
156
|
+
logInfo(`${rel} skipped (auto:false)`)
|
|
157
|
+
break
|
|
158
|
+
case 'error':
|
|
159
|
+
logWarn(`${rel} error`)
|
|
160
|
+
break
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function stage(path: string, rel: string, root: string): Promise<void> {
|
|
165
|
+
if (await isIgnored(root, path)) return
|
|
166
|
+
|
|
167
|
+
const staged = await $`git -C ${root} add -- ${path}`
|
|
168
|
+
.env(gitEnv())
|
|
169
|
+
.quiet()
|
|
170
|
+
.nothrow()
|
|
171
|
+
.then((result) => result.exitCode === 0)
|
|
172
|
+
|
|
173
|
+
if (staged) {
|
|
174
|
+
logInfo(`staged ${rel}`)
|
|
175
|
+
} else {
|
|
176
|
+
logWarn(`failed to stage ${rel}`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function isDirectory(path: string): Promise<boolean> {
|
|
181
|
+
return stat(path)
|
|
182
|
+
.then((info) => info.isDirectory())
|
|
183
|
+
.catch(() => false)
|
|
184
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Command } from 'commander'
|
|
2
|
+
import { cliPath, cliRun } from '@/cli-run'
|
|
3
|
+
import { PROJECT_ROOT } from '@/project-root'
|
|
4
|
+
import { applyInitOptions, flagsProvided } from '@/init/flags'
|
|
5
|
+
import { type InitFlags, parseSkip, planInit } from '@/init/plan'
|
|
6
|
+
import { runDomains } from '@/init/run'
|
|
7
|
+
import { buildSteps } from '@/init/steps'
|
|
8
|
+
import { resolveTarget } from '@/target'
|
|
9
|
+
import { intro, logInfo, logStep, logWarn, outro, palette, select } from '@/ui'
|
|
10
|
+
|
|
11
|
+
interface InitOptions {
|
|
12
|
+
/** Always present: the option falls back to `DEFAULT_STACK`. */
|
|
13
|
+
readonly stack: string
|
|
14
|
+
readonly add?: string
|
|
15
|
+
readonly skip?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function register(program: Command): void {
|
|
19
|
+
const command = program
|
|
20
|
+
.command('init')
|
|
21
|
+
.description('Bootstrap a project with base tooling and toolkit domains')
|
|
22
|
+
.argument('[target]', 'Target directory', '.')
|
|
23
|
+
|
|
24
|
+
applyInitOptions(command)
|
|
25
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
26
|
+
.addHelpText(
|
|
27
|
+
'after',
|
|
28
|
+
[
|
|
29
|
+
'',
|
|
30
|
+
'Passing any option skips the confirmation prompt.',
|
|
31
|
+
'',
|
|
32
|
+
'Examples:',
|
|
33
|
+
' canon init',
|
|
34
|
+
' canon init ../my-app',
|
|
35
|
+
' canon init --stack astro --add 260-shadcn ../my-app',
|
|
36
|
+
' canon init --skip governance ../my-app',
|
|
37
|
+
'',
|
|
38
|
+
].join('\n'),
|
|
39
|
+
)
|
|
40
|
+
.action(async (target: string, options: InitOptions, cmd: Command) => {
|
|
41
|
+
process.exitCode = await runInit(target, options, flagsProvided(cmd))
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function runInit(
|
|
46
|
+
target: string,
|
|
47
|
+
options: InitOptions,
|
|
48
|
+
skipPrompt: boolean,
|
|
49
|
+
): Promise<number> {
|
|
50
|
+
intro('canon init')
|
|
51
|
+
|
|
52
|
+
const resolved = resolveTarget(target, PROJECT_ROOT)
|
|
53
|
+
if (typeof resolved === 'number') return resolved
|
|
54
|
+
|
|
55
|
+
const skip = parseSkip(options.skip)
|
|
56
|
+
for (const value of skip.unknown) {
|
|
57
|
+
logWarn(`Unknown --skip value: ${value} (ignoring)`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const flags: InitFlags = {
|
|
61
|
+
stack: options.stack,
|
|
62
|
+
add: options.add,
|
|
63
|
+
skip,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const plan = planInit(flags)
|
|
67
|
+
|
|
68
|
+
logStep('Core domains')
|
|
69
|
+
for (const line of plan.preview) {
|
|
70
|
+
if (line.level === 'info') logInfo(line.text)
|
|
71
|
+
else logWarn(line.text)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!skipPrompt) {
|
|
75
|
+
const shouldInstall = await select({
|
|
76
|
+
message: `Install ${plan.total} domains to ${target}?`,
|
|
77
|
+
options: [
|
|
78
|
+
{ value: true, label: 'Yes' },
|
|
79
|
+
{ value: false, label: 'Cancel' },
|
|
80
|
+
],
|
|
81
|
+
nonInteractiveDefault: true,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
if (!shouldInstall) {
|
|
85
|
+
logWarn('Cancelled')
|
|
86
|
+
outro()
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const path = cliPath(PROJECT_ROOT)
|
|
92
|
+
const child = (args: readonly string[]): (() => Promise<boolean>) =>
|
|
93
|
+
cliRun(path, args, { nonInteractive: true, stdin: 'ignore' })
|
|
94
|
+
|
|
95
|
+
const failed = await runDomains(buildSteps(target, resolved, flags, child))
|
|
96
|
+
|
|
97
|
+
outro()
|
|
98
|
+
process.stderr.write('\n')
|
|
99
|
+
|
|
100
|
+
const { GREEN, NC, YELLOW } = palette(process.stderr)
|
|
101
|
+
|
|
102
|
+
if (failed.length === 0) {
|
|
103
|
+
process.stderr.write(
|
|
104
|
+
`${GREEN}✓ Project initialized (${plan.total} domains)${NC}\n`,
|
|
105
|
+
)
|
|
106
|
+
return 0
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
process.stderr.write(
|
|
110
|
+
`${YELLOW}! Project initialized with ${failed.length} failure(s): ${failed.join(', ')}${NC}\n`,
|
|
111
|
+
)
|
|
112
|
+
return 1
|
|
113
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { relative } from 'node:path'
|
|
2
|
+
import type { Command } from 'commander'
|
|
3
|
+
import {
|
|
4
|
+
type AnswerOutcome,
|
|
5
|
+
answerItems,
|
|
6
|
+
type IntakeRefused,
|
|
7
|
+
type ListOutcome,
|
|
8
|
+
listFolders,
|
|
9
|
+
type ReadOutcome,
|
|
10
|
+
readFolder,
|
|
11
|
+
type Selection,
|
|
12
|
+
} from '@/intake/folder'
|
|
13
|
+
import { isUnread } from '@/intake/items'
|
|
14
|
+
import {
|
|
15
|
+
intro,
|
|
16
|
+
logAdd,
|
|
17
|
+
logError,
|
|
18
|
+
logInfo,
|
|
19
|
+
logStep,
|
|
20
|
+
logWarn,
|
|
21
|
+
outro,
|
|
22
|
+
pipeOutput,
|
|
23
|
+
} from '@/ui'
|
|
24
|
+
import { mainWorktreeRoot } from '@/worktree'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A selection as the command line spells it, splitting on the first `=`. The
|
|
28
|
+
* label accepts a letter suffix, matching the headings a cluster file carries,
|
|
29
|
+
* and everything after the separator is the answer, so one carrying its own
|
|
30
|
+
* `=` survives intact.
|
|
31
|
+
*/
|
|
32
|
+
const SELECTION = /^(\d+[a-z]*)=([\s\S]*)$/i
|
|
33
|
+
|
|
34
|
+
interface ListCommandOptions {
|
|
35
|
+
readonly json?: boolean
|
|
36
|
+
readonly unread?: boolean
|
|
37
|
+
readonly root?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface AnswerCommandOptions {
|
|
41
|
+
readonly cluster?: string
|
|
42
|
+
readonly json?: boolean
|
|
43
|
+
readonly root?: string
|
|
44
|
+
readonly set?: readonly string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function register(program: Command): void {
|
|
48
|
+
const intake = program
|
|
49
|
+
.command('intake')
|
|
50
|
+
.description('Read and answer intake folders in .claude/intake/')
|
|
51
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
52
|
+
|
|
53
|
+
intake
|
|
54
|
+
.command('list')
|
|
55
|
+
.description('List intake folders, or the items one folder holds')
|
|
56
|
+
.argument('[slug]', 'Intake folder name, as in toolkit-overview')
|
|
57
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
58
|
+
.option(
|
|
59
|
+
'--unread',
|
|
60
|
+
'Keep only what is unread, as folders carrying one or as empty slots',
|
|
61
|
+
)
|
|
62
|
+
.option('--json', 'Emit a machine-readable record on stdout')
|
|
63
|
+
.option('--root <path>', 'Intake root, defaulting to the main worktree')
|
|
64
|
+
.addHelpText(
|
|
65
|
+
'after',
|
|
66
|
+
[
|
|
67
|
+
'',
|
|
68
|
+
'Exit codes:',
|
|
69
|
+
' 0 the folders or items were listed',
|
|
70
|
+
' 1 refused, with the reason on stderr or in the JSON record',
|
|
71
|
+
'',
|
|
72
|
+
'With no slug it reports per-folder counts. With one it reports every',
|
|
73
|
+
'item grouped by the cluster file holding it. An empty answer slot',
|
|
74
|
+
'means unread and never agreement, so a count is the report and no',
|
|
75
|
+
'verb here decides one.',
|
|
76
|
+
'',
|
|
77
|
+
'An item carrying no answer slot is counted apart from both, since the',
|
|
78
|
+
'answer verb cannot reach it and folding it into either count hides a',
|
|
79
|
+
'file that needs fixing.',
|
|
80
|
+
'',
|
|
81
|
+
'Examples:',
|
|
82
|
+
' canon intake list',
|
|
83
|
+
' canon intake list toolkit-overview --unread --json',
|
|
84
|
+
'',
|
|
85
|
+
].join('\n'),
|
|
86
|
+
)
|
|
87
|
+
.action(async (slug: string | undefined, opts: ListCommandOptions) => {
|
|
88
|
+
process.exitCode = await runList(slug, opts)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
intake
|
|
92
|
+
.command('answer')
|
|
93
|
+
.description("Write selections into a cluster's answer slots")
|
|
94
|
+
.argument('<slug>', 'Intake folder name, as in toolkit-overview')
|
|
95
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
96
|
+
.option('--cluster <file>', 'Cluster file the items live in')
|
|
97
|
+
.option(
|
|
98
|
+
'--set <item=answer>',
|
|
99
|
+
'Answer to land on an item, repeatable',
|
|
100
|
+
collectSelection,
|
|
101
|
+
[] as string[],
|
|
102
|
+
)
|
|
103
|
+
.option('--json', 'Emit a machine-readable record on stdout')
|
|
104
|
+
.option('--root <path>', 'Intake root, defaulting to the main worktree')
|
|
105
|
+
.addHelpText(
|
|
106
|
+
'after',
|
|
107
|
+
[
|
|
108
|
+
'',
|
|
109
|
+
'Exit codes:',
|
|
110
|
+
' 0 every named item now carries its answer',
|
|
111
|
+
' 1 refused, with the reason on stderr or in the JSON record',
|
|
112
|
+
'',
|
|
113
|
+
'Items are numbered per cluster file, so a selection names the cluster',
|
|
114
|
+
'and the number together. One call writes one cluster, which is what',
|
|
115
|
+
'keeps a batch from racing on the file every selection shares.',
|
|
116
|
+
'',
|
|
117
|
+
'An item already carrying an answer is refused rather than overwritten.',
|
|
118
|
+
'A filled slot is a decision already made.',
|
|
119
|
+
'',
|
|
120
|
+
'Examples:',
|
|
121
|
+
' canon intake answer toolkit-overview --cluster 05-coverage.md --set 3=ok',
|
|
122
|
+
' canon intake answer toolkit-overview --cluster 05-coverage.md --set 3=ok --set 4="not worth it" --json',
|
|
123
|
+
'',
|
|
124
|
+
].join('\n'),
|
|
125
|
+
)
|
|
126
|
+
.action(async (slug: string, opts: AnswerCommandOptions) => {
|
|
127
|
+
process.exitCode = await runAnswer(slug, opts)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function collectSelection(value: string, previous: string[]): string[] {
|
|
132
|
+
return [...previous, value]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function runList(
|
|
136
|
+
slug: string | undefined,
|
|
137
|
+
opts: ListCommandOptions,
|
|
138
|
+
): Promise<number> {
|
|
139
|
+
const emitJson = opts.json ?? false
|
|
140
|
+
const root = opts.root ?? (await mainWorktreeRoot())
|
|
141
|
+
|
|
142
|
+
if (slug === undefined) {
|
|
143
|
+
return reportList(
|
|
144
|
+
await listFolders(root),
|
|
145
|
+
emitJson,
|
|
146
|
+
root,
|
|
147
|
+
opts.unread ?? false,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return reportFolder(
|
|
152
|
+
await readFolder(root, slug),
|
|
153
|
+
emitJson,
|
|
154
|
+
root,
|
|
155
|
+
opts.unread ?? false,
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function runAnswer(
|
|
160
|
+
slug: string,
|
|
161
|
+
opts: AnswerCommandOptions,
|
|
162
|
+
): Promise<number> {
|
|
163
|
+
const emitJson = opts.json ?? false
|
|
164
|
+
|
|
165
|
+
if (!opts.cluster) {
|
|
166
|
+
return reportRefusal(
|
|
167
|
+
'canon intake answer',
|
|
168
|
+
{
|
|
169
|
+
ok: false,
|
|
170
|
+
reason: 'bad-input',
|
|
171
|
+
message: 'No cluster named. Pass --cluster <file>.',
|
|
172
|
+
detail: [],
|
|
173
|
+
},
|
|
174
|
+
emitJson,
|
|
175
|
+
process.cwd(),
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const raw = opts.set ?? []
|
|
180
|
+
|
|
181
|
+
if (raw.length === 0) {
|
|
182
|
+
return reportRefusal(
|
|
183
|
+
'canon intake answer',
|
|
184
|
+
{
|
|
185
|
+
ok: false,
|
|
186
|
+
reason: 'bad-input',
|
|
187
|
+
message: 'No selection given. Pass --set <item>=<answer>.',
|
|
188
|
+
detail: [],
|
|
189
|
+
},
|
|
190
|
+
emitJson,
|
|
191
|
+
process.cwd(),
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const selections: Selection[] = []
|
|
196
|
+
const invalid: string[] = []
|
|
197
|
+
|
|
198
|
+
for (const entry of raw) {
|
|
199
|
+
const match = SELECTION.exec(entry)
|
|
200
|
+
const answer = match?.[2].trim()
|
|
201
|
+
|
|
202
|
+
if (!match || !answer) {
|
|
203
|
+
invalid.push(entry)
|
|
204
|
+
continue
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
selections.push({ label: match[1].toLowerCase(), answer })
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (invalid.length > 0) {
|
|
211
|
+
return reportRefusal(
|
|
212
|
+
'canon intake answer',
|
|
213
|
+
{
|
|
214
|
+
ok: false,
|
|
215
|
+
reason: 'bad-input',
|
|
216
|
+
message: `Not an item and answer: ${invalid.join(', ')}`,
|
|
217
|
+
detail: invalid,
|
|
218
|
+
},
|
|
219
|
+
emitJson,
|
|
220
|
+
process.cwd(),
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const duplicated = selections
|
|
225
|
+
.map((selection) => selection.label)
|
|
226
|
+
.filter((label, index, all) => all.indexOf(label) !== index)
|
|
227
|
+
|
|
228
|
+
if (duplicated.length > 0) {
|
|
229
|
+
return reportRefusal(
|
|
230
|
+
'canon intake answer',
|
|
231
|
+
{
|
|
232
|
+
ok: false,
|
|
233
|
+
reason: 'bad-input',
|
|
234
|
+
message: `Two answers for item ${[...new Set(duplicated)].join(', ')}.`,
|
|
235
|
+
detail: [],
|
|
236
|
+
},
|
|
237
|
+
emitJson,
|
|
238
|
+
process.cwd(),
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const root = opts.root ?? (await mainWorktreeRoot())
|
|
243
|
+
const outcome = await answerItems(root, slug, opts.cluster, selections)
|
|
244
|
+
|
|
245
|
+
return reportAnswer(outcome, emitJson, root)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function reportRefusal(
|
|
249
|
+
title: string,
|
|
250
|
+
refused: IntakeRefused,
|
|
251
|
+
emitJson: boolean,
|
|
252
|
+
root: string,
|
|
253
|
+
): number {
|
|
254
|
+
// The framed branch reaches stderr through logError, so the bare write is
|
|
255
|
+
// what keeps the JSON mode from reporting the reason on stdout alone.
|
|
256
|
+
if (emitJson) {
|
|
257
|
+
process.stderr.write(`${refused.message}\n`)
|
|
258
|
+
process.stdout.write(
|
|
259
|
+
`${JSON.stringify({
|
|
260
|
+
ok: false,
|
|
261
|
+
root,
|
|
262
|
+
reason: refused.reason,
|
|
263
|
+
message: refused.message,
|
|
264
|
+
detail: refused.detail,
|
|
265
|
+
})}\n`,
|
|
266
|
+
)
|
|
267
|
+
return 1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
intro(title)
|
|
271
|
+
logStep('Refused')
|
|
272
|
+
logError(refused.message)
|
|
273
|
+
if (refused.detail.length > 0) pipeOutput(refused.detail.join('\n'))
|
|
274
|
+
outro()
|
|
275
|
+
|
|
276
|
+
return 1
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function reportList(
|
|
280
|
+
outcome: ListOutcome,
|
|
281
|
+
emitJson: boolean,
|
|
282
|
+
root: string,
|
|
283
|
+
unreadOnly: boolean,
|
|
284
|
+
): number {
|
|
285
|
+
if (!outcome.ok) {
|
|
286
|
+
return reportRefusal('canon intake list', outcome, emitJson, root)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Malformed items are counted over every folder rather than the filtered
|
|
290
|
+
// set, since a folder whose only defect is an item nobody can answer carries
|
|
291
|
+
// no unread count to survive the filter and is exactly what the warning is
|
|
292
|
+
// for.
|
|
293
|
+
const listed = unreadOnly
|
|
294
|
+
? outcome.folders.filter((folder) => folder.unread > 0)
|
|
295
|
+
: outcome.folders
|
|
296
|
+
|
|
297
|
+
if (emitJson) {
|
|
298
|
+
process.stdout.write(
|
|
299
|
+
`${JSON.stringify({ ok: true, root, folders: listed })}\n`,
|
|
300
|
+
)
|
|
301
|
+
return 0
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
intro('canon intake list')
|
|
305
|
+
logStep(listed.length > 0 ? 'Folders' : 'No folders')
|
|
306
|
+
|
|
307
|
+
for (const folder of listed) {
|
|
308
|
+
logInfo(
|
|
309
|
+
`${folder.slug}: ${folder.items} item(s), ${folder.open} open, ${folder.unread} unread`,
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const malformed = outcome.folders.filter((folder) => folder.malformed > 0)
|
|
314
|
+
|
|
315
|
+
if (malformed.length > 0) {
|
|
316
|
+
logStep('Carrying no answer slot')
|
|
317
|
+
for (const folder of malformed) {
|
|
318
|
+
logWarn(
|
|
319
|
+
`${folder.slug}: ${folder.malformed} item(s), which none of these verbs can answer`,
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
outro()
|
|
325
|
+
|
|
326
|
+
return 0
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function reportFolder(
|
|
330
|
+
outcome: ReadOutcome,
|
|
331
|
+
emitJson: boolean,
|
|
332
|
+
root: string,
|
|
333
|
+
unreadOnly: boolean,
|
|
334
|
+
): number {
|
|
335
|
+
if (!outcome.ok) {
|
|
336
|
+
return reportRefusal('canon intake list', outcome, emitJson, root)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const clusters = outcome.clusters
|
|
340
|
+
.map((cluster) => ({
|
|
341
|
+
cluster: cluster.cluster,
|
|
342
|
+
items: unreadOnly ? cluster.items.filter(isUnread) : cluster.items,
|
|
343
|
+
}))
|
|
344
|
+
.filter((cluster) => cluster.items.length > 0)
|
|
345
|
+
|
|
346
|
+
if (emitJson) {
|
|
347
|
+
process.stdout.write(
|
|
348
|
+
`${JSON.stringify({ ok: true, root, slug: outcome.slug, clusters })}\n`,
|
|
349
|
+
)
|
|
350
|
+
return 0
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
intro('canon intake list')
|
|
354
|
+
|
|
355
|
+
for (const cluster of clusters) {
|
|
356
|
+
logStep(cluster.cluster)
|
|
357
|
+
for (const item of cluster.items) {
|
|
358
|
+
const state = isUnread(item) ? 'unread' : (item.answer ?? 'no slot')
|
|
359
|
+
logInfo(`${item.label}. ${item.title} (${state})`)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (clusters.length === 0) {
|
|
364
|
+
logStep(unreadOnly ? 'Nothing unread' : 'No items')
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
outro()
|
|
368
|
+
|
|
369
|
+
return 0
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function reportAnswer(
|
|
373
|
+
outcome: AnswerOutcome,
|
|
374
|
+
emitJson: boolean,
|
|
375
|
+
root: string,
|
|
376
|
+
): number {
|
|
377
|
+
if (!outcome.ok) {
|
|
378
|
+
return reportRefusal('canon intake answer', outcome, emitJson, root)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (emitJson) {
|
|
382
|
+
process.stdout.write(
|
|
383
|
+
`${JSON.stringify({
|
|
384
|
+
ok: true,
|
|
385
|
+
root,
|
|
386
|
+
slug: outcome.slug,
|
|
387
|
+
cluster: outcome.cluster,
|
|
388
|
+
path: relative(root, outcome.path),
|
|
389
|
+
answered: outcome.answered,
|
|
390
|
+
})}\n`,
|
|
391
|
+
)
|
|
392
|
+
return 0
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
intro('canon intake answer')
|
|
396
|
+
logStep('Answered')
|
|
397
|
+
|
|
398
|
+
for (const entry of outcome.answered) {
|
|
399
|
+
logInfo(`${entry.label}. ${entry.answer}`)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
logAdd(relative(root, outcome.path))
|
|
403
|
+
outro()
|
|
404
|
+
|
|
405
|
+
return 0
|
|
406
|
+
}
|