@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
package/src/ui.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
export interface Palette {
|
|
2
|
+
readonly GREEN: string
|
|
3
|
+
readonly RED: string
|
|
4
|
+
readonly YELLOW: string
|
|
5
|
+
readonly WHITE: string
|
|
6
|
+
readonly GREY: string
|
|
7
|
+
readonly NC: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const COLOR: Palette = {
|
|
11
|
+
GREEN: '\x1b[0;32m',
|
|
12
|
+
RED: '\x1b[0;31m',
|
|
13
|
+
YELLOW: '\x1b[0;33m',
|
|
14
|
+
WHITE: '\x1b[1;37m',
|
|
15
|
+
GREY: '\x1b[0;90m',
|
|
16
|
+
NC: '\x1b[0m',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The blank palette keeps every frame character and drops only the escapes, so
|
|
21
|
+
* a captured run still reads as one block.
|
|
22
|
+
*/
|
|
23
|
+
const PLAIN: Palette = {
|
|
24
|
+
GREEN: '',
|
|
25
|
+
RED: '',
|
|
26
|
+
YELLOW: '',
|
|
27
|
+
WHITE: '',
|
|
28
|
+
GREY: '',
|
|
29
|
+
NC: '',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `NO_COLOR` follows the published convention, where any non-empty value turns
|
|
34
|
+
* color off whatever the value says.
|
|
35
|
+
*/
|
|
36
|
+
export function supportsColor(stream: { isTTY?: boolean }): boolean {
|
|
37
|
+
const optOut = process.env.NO_COLOR
|
|
38
|
+
if (optOut !== undefined && optOut !== '') return false
|
|
39
|
+
return stream.isTTY === true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The question is asked per stream rather than once for the process. The framed
|
|
44
|
+
* output goes to stderr and a structured record to stdout, so a run piping only
|
|
45
|
+
* its data keeps a terminal on stderr and keeps its color there. This is a
|
|
46
|
+
* third question again from `isNonInteractive`, which answers whether a caller
|
|
47
|
+
* can be prompted rather than whether a destination renders escapes.
|
|
48
|
+
*
|
|
49
|
+
* Read at write time rather than at import, so nothing freezes an answer taken
|
|
50
|
+
* before the caller's environment was in place.
|
|
51
|
+
*/
|
|
52
|
+
export function palette(stream: { isTTY?: boolean }): Palette {
|
|
53
|
+
return supportsColor(stream) ? COLOR : PLAIN
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function intro(title: string): void {
|
|
57
|
+
const { GREY, NC, WHITE } = palette(process.stderr)
|
|
58
|
+
process.stderr.write(`${GREY}┌${NC}\n${GREY}│${NC} ${WHITE}${title}${NC}\n`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Timeline log levels matching `scripts/lib/ui.sh`, so a migrated command
|
|
63
|
+
* prints the same frame as the bash it replaced. All output is stderr,
|
|
64
|
+
* leaving stdout clean for JSON and lists.
|
|
65
|
+
*/
|
|
66
|
+
export function logInfo(message: string): void {
|
|
67
|
+
const { GREEN, GREY, NC } = palette(process.stderr)
|
|
68
|
+
process.stderr.write(`${GREY}│${NC} ${GREEN}✓${NC} ${message}\n`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function logWarn(message: string): void {
|
|
72
|
+
const { GREY, NC, YELLOW } = palette(process.stderr)
|
|
73
|
+
process.stderr.write(`${GREY}│${NC} ${YELLOW}!${NC} ${message}\n`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function logAdd(message: string): void {
|
|
77
|
+
const { GREEN, GREY, NC } = palette(process.stderr)
|
|
78
|
+
process.stderr.write(`${GREY}│${NC} ${GREEN}+${NC} ${message}\n`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function logRemove(message: string): void {
|
|
82
|
+
const { GREY, NC, RED } = palette(process.stderr)
|
|
83
|
+
process.stderr.write(`${GREY}│${NC} ${RED}-${NC} ${message}\n`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Renders the `✗` shape `docs/agents/output-shape.md` specifies for a failure inside an
|
|
88
|
+
* open frame. It does not exit, so the caller closes the frame and returns an
|
|
89
|
+
* exit code rather than terminating mid-write.
|
|
90
|
+
*/
|
|
91
|
+
export function logError(message: string): void {
|
|
92
|
+
const { GREY, NC, RED } = palette(process.stderr)
|
|
93
|
+
process.stderr.write(`${GREY}│${NC} ${RED}✗${NC} ${message}\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function logStep(message: string): void {
|
|
97
|
+
const { GREY, NC, WHITE } = palette(process.stderr)
|
|
98
|
+
process.stderr.write(`${GREY}│${NC}\n${GREY}├${NC} ${WHITE}${message}${NC}\n`)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function outro(): void {
|
|
102
|
+
const { GREY, NC } = palette(process.stderr)
|
|
103
|
+
process.stderr.write(`${GREY}└${NC}\n`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Indents borrowed output inside an open frame, matching `pipe_output` in
|
|
108
|
+
* `scripts/lib/ui.sh`. Used for a document the command did not compose itself,
|
|
109
|
+
* such as a pull request body or the output of a git mutation.
|
|
110
|
+
*/
|
|
111
|
+
export function pipeOutput(text: string): void {
|
|
112
|
+
const { GREY, NC } = palette(process.stderr)
|
|
113
|
+
const lines = text.replace(/\n$/, '').split('\n')
|
|
114
|
+
process.stderr.write(
|
|
115
|
+
`${lines.map((line) => `${GREY}│${NC} ${line}`).join('\n')}\n`,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Counts a noun for a report line, where the plural is the bare `s` form. */
|
|
120
|
+
export function plural(count: number, noun: string): string {
|
|
121
|
+
return `${count} ${noun}${count === 1 ? '' : 's'}`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function frameError(message: string): void {
|
|
125
|
+
const { GREY, NC, RED } = palette(process.stderr)
|
|
126
|
+
process.stderr.write(
|
|
127
|
+
`${GREY}┌${NC}\n${GREY}│${NC} ${RED}✗${NC} ${message}\n${GREY}└${NC}\n`,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function frameSuccess(command: string, target: string): void {
|
|
132
|
+
const { GREEN, GREY, NC, WHITE } = palette(process.stderr)
|
|
133
|
+
process.stderr.write(
|
|
134
|
+
`${GREY}┌${NC}\n${GREY}│${NC} ${WHITE}${command}${NC}\n${GREY}│${NC}\n${GREY}│${NC} ${GREEN}✓${NC} ${target}\n${GREY}└${NC}\n`,
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The retired spelling is still honored, and permanently.
|
|
140
|
+
*
|
|
141
|
+
* This variable is the documented way to drive any command headlessly, so
|
|
142
|
+
* every skill body, CI job, and operator script already written sets the old
|
|
143
|
+
* name. Those callers live in other repositories and in plugin caches this
|
|
144
|
+
* release cannot reach, and the failure mode of dropping the fallback is a
|
|
145
|
+
* command that quietly waits for a TTY nobody is watching rather than one that
|
|
146
|
+
* refuses and says why.
|
|
147
|
+
*/
|
|
148
|
+
export function isNonInteractive(): boolean {
|
|
149
|
+
return (
|
|
150
|
+
process.env.CANON_NON_INTERACTIVE === '1' ||
|
|
151
|
+
// canon-keep-retired
|
|
152
|
+
process.env.AITK_NON_INTERACTIVE === '1'
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* `nonInteractiveDefault` opts a prompt into the `select_option` behavior from
|
|
158
|
+
* `scripts/lib/ui.sh`, where `CANON_NON_INTERACTIVE=1` resolves to the first
|
|
159
|
+
* option instead of failing. Callers that would rather fail loudly than pick
|
|
160
|
+
* for the user leave it off.
|
|
161
|
+
*/
|
|
162
|
+
export async function select<Value>(opts: {
|
|
163
|
+
message: string
|
|
164
|
+
options: { value: Value; label: string; hint?: string }[]
|
|
165
|
+
nonInteractiveDefault?: boolean
|
|
166
|
+
}): Promise<Value> {
|
|
167
|
+
const { message, options } = opts
|
|
168
|
+
const { GREEN, GREY, NC, RED, WHITE } = palette(process.stderr)
|
|
169
|
+
const count = options.length
|
|
170
|
+
let cursor = 0
|
|
171
|
+
|
|
172
|
+
if (opts.nonInteractiveDefault && isNonInteractive()) {
|
|
173
|
+
process.stderr.write(
|
|
174
|
+
`${GREY}│${NC}\n${GREY}◇${NC} ${message} ${WHITE}${options[0].label}${NC}\n`,
|
|
175
|
+
)
|
|
176
|
+
return options[0].value
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const render = (): void => {
|
|
180
|
+
let out = `${GREY}│${NC}\n${GREEN}◆${NC} ${message}\n`
|
|
181
|
+
for (let i = 0; i < count; i++) {
|
|
182
|
+
const label = options[i].label
|
|
183
|
+
const hint = options[i].hint ? ` ${GREY}${options[i].hint}${NC}` : ''
|
|
184
|
+
if (i === cursor) {
|
|
185
|
+
out += `${GREY}│${NC} ${GREEN}❯ ${label}${NC}${hint}\n`
|
|
186
|
+
} else {
|
|
187
|
+
out += `${GREY}│${NC} ${GREY}${label}${NC}${hint}\n`
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
process.stderr.write(out)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const clear = (): void => {
|
|
194
|
+
process.stderr.write(`\x1b[${count + 2}A\x1b[J`)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return new Promise<Value>((resolve) => {
|
|
198
|
+
if (!process.stdin.isTTY) {
|
|
199
|
+
process.stderr.write(
|
|
200
|
+
`${GREY}│${NC} ${RED}✗${NC} ${message} requires a TTY. Pass an argument or set CANON_NON_INTERACTIVE=1.\n${GREY}└${NC}\n`,
|
|
201
|
+
)
|
|
202
|
+
process.exit(1)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const wasRaw = process.stdin.isRaw
|
|
206
|
+
process.stdin.setRawMode(true)
|
|
207
|
+
process.stdin.resume()
|
|
208
|
+
|
|
209
|
+
render()
|
|
210
|
+
|
|
211
|
+
const onData = (data: Buffer): void => {
|
|
212
|
+
const key = data.toString()
|
|
213
|
+
|
|
214
|
+
if (key === '\x1b[A' || key === 'k') {
|
|
215
|
+
cursor = (cursor - 1 + count) % count
|
|
216
|
+
clear()
|
|
217
|
+
render()
|
|
218
|
+
} else if (key === '\x1b[B' || key === 'j') {
|
|
219
|
+
cursor = (cursor + 1) % count
|
|
220
|
+
clear()
|
|
221
|
+
render()
|
|
222
|
+
} else if (key === '\r' || key === '\n') {
|
|
223
|
+
cleanup()
|
|
224
|
+
clear()
|
|
225
|
+
process.stderr.write(
|
|
226
|
+
`${GREY}│${NC}\n${GREY}◇${NC} ${message} ${WHITE}${options[cursor].label}${NC}\n`,
|
|
227
|
+
)
|
|
228
|
+
resolve(options[cursor].value)
|
|
229
|
+
} else if (key === '\x1b' || key === 'q' || key === '\x03') {
|
|
230
|
+
cleanup()
|
|
231
|
+
clear()
|
|
232
|
+
process.stderr.write(
|
|
233
|
+
`${GREY}│${NC}\n${GREY}◇${NC} ${message} ${RED}Cancelled${NC}\n${GREY}└${NC}\n`,
|
|
234
|
+
)
|
|
235
|
+
process.exit(1)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const cleanup = (): void => {
|
|
240
|
+
process.stdin.removeListener('data', onData)
|
|
241
|
+
process.stdin.setRawMode(wasRaw ?? false)
|
|
242
|
+
process.stdin.pause()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
process.stdin.on('data', onData)
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export async function confirm(opts: {
|
|
250
|
+
message: string
|
|
251
|
+
active?: string
|
|
252
|
+
inactive?: string
|
|
253
|
+
}): Promise<boolean> {
|
|
254
|
+
const active = opts.active ?? 'Yes'
|
|
255
|
+
const inactive = opts.inactive ?? 'No'
|
|
256
|
+
|
|
257
|
+
const result = await select({
|
|
258
|
+
message: opts.message,
|
|
259
|
+
options: [
|
|
260
|
+
{ value: true, label: active },
|
|
261
|
+
{ value: false, label: inactive },
|
|
262
|
+
],
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
return result
|
|
266
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `major.minor.patch` core with an optional prerelease tail. The registry
|
|
3
|
+
* publishes both shapes under the same dist tag, so a comparison that only
|
|
4
|
+
* understood the core would read `1.0.0-rc.1` as unparseable and report the
|
|
5
|
+
* whole lookup as unknown.
|
|
6
|
+
*/
|
|
7
|
+
const VERSION =
|
|
8
|
+
/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/
|
|
9
|
+
|
|
10
|
+
export interface ParsedVersion {
|
|
11
|
+
readonly major: number
|
|
12
|
+
readonly minor: number
|
|
13
|
+
readonly patch: number
|
|
14
|
+
/** Absent on a release, which sorts above every prerelease of the same core. */
|
|
15
|
+
readonly prerelease?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function parseVersion(raw: string): ParsedVersion | undefined {
|
|
19
|
+
const match = VERSION.exec(raw.trim())
|
|
20
|
+
if (match === null) return undefined
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
major: Number(match[1]),
|
|
24
|
+
minor: Number(match[2]),
|
|
25
|
+
patch: Number(match[3]),
|
|
26
|
+
...(match[4] === undefined ? {} : { prerelease: match[4] }),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Negative when `left` is older, positive when it is newer, zero when the two
|
|
32
|
+
* name the same version.
|
|
33
|
+
*
|
|
34
|
+
* Prerelease identifiers compare as whole strings rather than dot segment by
|
|
35
|
+
* dot segment, which is narrower than semver states. Every version this repo
|
|
36
|
+
* publishes is a plain core, so the ordering inside a prerelease series decides
|
|
37
|
+
* nothing here, and the one comparison that matters is that any prerelease
|
|
38
|
+
* sorts below the release sharing its core.
|
|
39
|
+
*/
|
|
40
|
+
export function compareVersions(
|
|
41
|
+
left: ParsedVersion,
|
|
42
|
+
right: ParsedVersion,
|
|
43
|
+
): number {
|
|
44
|
+
if (left.major !== right.major) return left.major - right.major
|
|
45
|
+
if (left.minor !== right.minor) return left.minor - right.minor
|
|
46
|
+
if (left.patch !== right.patch) return left.patch - right.patch
|
|
47
|
+
|
|
48
|
+
if (left.prerelease === right.prerelease) return 0
|
|
49
|
+
if (left.prerelease === undefined) return 1
|
|
50
|
+
if (right.prerelease === undefined) return -1
|
|
51
|
+
|
|
52
|
+
return left.prerelease < right.prerelease ? -1 : 1
|
|
53
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { PROJECT_ROOT } from '@/project-root'
|
|
4
|
+
|
|
5
|
+
/** What a reader prints in place of a field the manifest did not carry. */
|
|
6
|
+
export const UNKNOWN_LABEL = 'unknown'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Either field is absent when the manifest could not be read or did not carry
|
|
10
|
+
* it. They are optional rather than sentinel strings so a caller has to narrow
|
|
11
|
+
* before using one, which is what makes a name the manifest never supplied
|
|
12
|
+
* impossible to interpolate into a shell command. A sentinel typed `string`
|
|
13
|
+
* reads as an ordinary value at every call site and hides that case.
|
|
14
|
+
*/
|
|
15
|
+
export interface InstalledPackage {
|
|
16
|
+
readonly name?: string
|
|
17
|
+
readonly version?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Read at runtime rather than inlined, because a literal is a second place the
|
|
22
|
+
* version lives and it stopped tracking `package.json` at `0.1.0`. The release
|
|
23
|
+
* tool writes one file and this follows it. `package.json` ships in every npm
|
|
24
|
+
* tarball regardless of the `files` list, so the read resolves from a registry
|
|
25
|
+
* install as well as from a clone.
|
|
26
|
+
*
|
|
27
|
+
* This sits apart from the skew read so `src/cli.ts` can name the version on
|
|
28
|
+
* every invocation without pulling the registry lookup into the startup import
|
|
29
|
+
* graph of a CLI that compiles nothing ahead of time.
|
|
30
|
+
*/
|
|
31
|
+
export function readInstalled(root: string = PROJECT_ROOT): InstalledPackage {
|
|
32
|
+
try {
|
|
33
|
+
const raw = readFileSync(join(root, 'package.json'), 'utf8')
|
|
34
|
+
const parsed = JSON.parse(raw) as { name?: string; version?: string }
|
|
35
|
+
|
|
36
|
+
return { name: parsed.name, version: parsed.version }
|
|
37
|
+
} catch {
|
|
38
|
+
return {}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export type ManagerId = 'bun' | 'pnpm' | 'yarn' | 'npm'
|
|
2
|
+
|
|
3
|
+
export interface Manager {
|
|
4
|
+
readonly id: ManagerId
|
|
5
|
+
/** The path segment the detection matched, so a wrong read is correctable. */
|
|
6
|
+
readonly evidence: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Segments that only appear in one manager's global install tree, checked
|
|
11
|
+
* before the `node_modules` fallback because every one of these trees contains
|
|
12
|
+
* a `node_modules` too.
|
|
13
|
+
*/
|
|
14
|
+
const SIGNATURES: readonly (readonly [ManagerId, string])[] = [
|
|
15
|
+
['bun', '.bun'],
|
|
16
|
+
['pnpm', 'pnpm'],
|
|
17
|
+
['pnpm', '.pnpm'],
|
|
18
|
+
['yarn', 'yarn'],
|
|
19
|
+
['yarn', '.yarn'],
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Which package manager installed the package rooted at `root`, read off the
|
|
24
|
+
* install path rather than guessed from what is on `PATH`.
|
|
25
|
+
*
|
|
26
|
+
* Removing the guess is the whole case for the upgrade verb, so a detection
|
|
27
|
+
* that cannot be read back is worth no more than the guess it replaced. The
|
|
28
|
+
* evidence travels with the answer and the verb prints it before running
|
|
29
|
+
* anything, which lets an operator correct a wrong read without the detection
|
|
30
|
+
* having to be right every time.
|
|
31
|
+
*
|
|
32
|
+
* Returns `undefined` for a path outside any install tree, which is a source
|
|
33
|
+
* checkout. That is not a case to guess at either: reinstalling over a clone
|
|
34
|
+
* would replace what the operator is working in.
|
|
35
|
+
*/
|
|
36
|
+
export function detectManager(root: string): Manager | undefined {
|
|
37
|
+
const segments = root.split(/[/\\]/).filter((segment) => segment !== '')
|
|
38
|
+
|
|
39
|
+
for (const [id, signature] of SIGNATURES) {
|
|
40
|
+
if (segments.includes(signature)) return { id, evidence: signature }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (segments.includes('node_modules')) {
|
|
44
|
+
return { id: 'npm', evidence: 'node_modules' }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The global reinstall each manager spells, pinned to the newest published. */
|
|
51
|
+
export function installCommand(
|
|
52
|
+
manager: ManagerId,
|
|
53
|
+
name: string,
|
|
54
|
+
): readonly string[] {
|
|
55
|
+
const spec = `${name}@latest`
|
|
56
|
+
|
|
57
|
+
switch (manager) {
|
|
58
|
+
case 'bun':
|
|
59
|
+
return ['bun', 'add', '--global', spec]
|
|
60
|
+
case 'pnpm':
|
|
61
|
+
return ['pnpm', 'add', '--global', spec]
|
|
62
|
+
case 'yarn':
|
|
63
|
+
return ['yarn', 'global', 'add', spec]
|
|
64
|
+
case 'npm':
|
|
65
|
+
return ['npm', 'install', '--global', spec]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { PROJECT_ROOT } from '@/project-root'
|
|
2
|
+
import { compareVersions, parseVersion } from '@/version/compare'
|
|
3
|
+
import {
|
|
4
|
+
type InstalledPackage,
|
|
5
|
+
readInstalled,
|
|
6
|
+
UNKNOWN_LABEL,
|
|
7
|
+
} from '@/version/installed'
|
|
8
|
+
import { detectManager } from '@/version/manager'
|
|
9
|
+
|
|
10
|
+
const REGISTRY = 'https://registry.npmjs.org'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Short enough that a check waiting on a dead network still returns inside the
|
|
14
|
+
* time an operator would give the command anyway. The skew line is one section
|
|
15
|
+
* of a report the rest of which needs no network at all, so the budget is set
|
|
16
|
+
* against how long the report may be held up rather than against how long the
|
|
17
|
+
* registry usually takes.
|
|
18
|
+
*/
|
|
19
|
+
const LOOKUP_TIMEOUT_MS = 3_000
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Three states and no fourth. `unknown` covers every way the question could not
|
|
23
|
+
* be answered and carries the reason, which is what a caller reports instead of
|
|
24
|
+
* a version.
|
|
25
|
+
*
|
|
26
|
+
* An installed version ahead of the published one reports `current`. That is a
|
|
27
|
+
* source checkout between a release commit and the publish job, or a local
|
|
28
|
+
* build, and neither is skew. Giving it a state of its own would fire a warning
|
|
29
|
+
* on every maintainer run for a condition with no remedy.
|
|
30
|
+
*/
|
|
31
|
+
export type SkewState = 'current' | 'behind' | 'unknown'
|
|
32
|
+
|
|
33
|
+
interface SkewBase {
|
|
34
|
+
readonly name: string
|
|
35
|
+
readonly installed: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A union rather than one shape with two optional fields, so a `behind` report
|
|
40
|
+
* cannot exist without the version it is behind and an `unknown` one cannot
|
|
41
|
+
* exist without its reason. Both are what `describeSkew` renders into a line an
|
|
42
|
+
* operator reads, and an optional field renders the word `undefined` there.
|
|
43
|
+
*/
|
|
44
|
+
export type SkewReport =
|
|
45
|
+
| (SkewBase & {
|
|
46
|
+
readonly state: 'current' | 'behind'
|
|
47
|
+
readonly latest: string
|
|
48
|
+
})
|
|
49
|
+
| (SkewBase & { readonly state: 'unknown'; readonly reason: string })
|
|
50
|
+
|
|
51
|
+
/** Resolves the newest published version, or throws for `readSkew` to absorb. */
|
|
52
|
+
export type LatestLookup = (name: string) => Promise<string>
|
|
53
|
+
|
|
54
|
+
export interface SkewOptions {
|
|
55
|
+
readonly installed?: InstalledPackage
|
|
56
|
+
readonly lookup?: LatestLookup
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The installed version against the newest published one.
|
|
61
|
+
*
|
|
62
|
+
* Never rejects and never reports through an exit code. `canon sync --check
|
|
63
|
+
* --exit-code` gates CI on drift it measured locally, so a lookup that failed
|
|
64
|
+
* the caller would turn an offline machine into a failing check and the check
|
|
65
|
+
* would be routed around. Every failure lands in `unknown` with its reason.
|
|
66
|
+
*/
|
|
67
|
+
export async function readSkew(options: SkewOptions = {}): Promise<SkewReport> {
|
|
68
|
+
const installed = options.installed ?? readInstalled()
|
|
69
|
+
const lookup = options.lookup ?? fetchLatest
|
|
70
|
+
const { name, version } = installed
|
|
71
|
+
|
|
72
|
+
if (version === undefined) {
|
|
73
|
+
return unknown(
|
|
74
|
+
installed,
|
|
75
|
+
'No version in the package manifest, so there is nothing to compare.',
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (name === undefined) {
|
|
80
|
+
return unknown(
|
|
81
|
+
installed,
|
|
82
|
+
'No name in the package manifest, so the registry has nothing to look up.',
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const local = parseVersion(version)
|
|
87
|
+
if (local === undefined) {
|
|
88
|
+
return unknown(
|
|
89
|
+
installed,
|
|
90
|
+
`Installed version ${version} is not a version this can parse.`,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let raw: string
|
|
95
|
+
try {
|
|
96
|
+
raw = await lookup(name)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return unknown(installed, `Registry lookup failed: ${describe(error)}`)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const published = parseVersion(raw)
|
|
102
|
+
if (published === undefined) {
|
|
103
|
+
return unknown(
|
|
104
|
+
installed,
|
|
105
|
+
`Registry reported ${raw} as the newest version, which is not a version this can parse.`,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
state: compareVersions(local, published) < 0 ? 'behind' : 'current',
|
|
111
|
+
name,
|
|
112
|
+
installed: version,
|
|
113
|
+
latest: raw,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function unknown(installed: InstalledPackage, reason: string): SkewReport {
|
|
118
|
+
return {
|
|
119
|
+
state: 'unknown',
|
|
120
|
+
name: installed.name ?? UNKNOWN_LABEL,
|
|
121
|
+
installed: installed.version ?? UNKNOWN_LABEL,
|
|
122
|
+
reason,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The newest published version, or `undefined` when it could not be read. */
|
|
127
|
+
export function latestOf(report: SkewReport): string | undefined {
|
|
128
|
+
return report.state === 'unknown' ? undefined : report.latest
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function describe(error: unknown): string {
|
|
132
|
+
return error instanceof Error ? error.message : String(error)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The dist-tag endpoint rather than the full packument, which for this package
|
|
137
|
+
* carries every published manifest and is the larger part of a megabyte. The
|
|
138
|
+
* question is one string and this is the endpoint that answers only it.
|
|
139
|
+
*/
|
|
140
|
+
async function fetchLatest(name: string): Promise<string> {
|
|
141
|
+
const response = await fetch(
|
|
142
|
+
`${REGISTRY}/-/package/${encodeURIComponent(name)}/dist-tags`,
|
|
143
|
+
{
|
|
144
|
+
signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS),
|
|
145
|
+
headers: { accept: 'application/json' },
|
|
146
|
+
},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
throw new Error(`registry returned ${response.status}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const tags = (await response.json()) as Record<string, unknown>
|
|
154
|
+
const latest = tags.latest
|
|
155
|
+
|
|
156
|
+
if (typeof latest !== 'string') {
|
|
157
|
+
throw new Error('registry reported no latest dist-tag')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return latest
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* One line naming the state, for a caller that renders the skew beside sections
|
|
165
|
+
* it does not own. Held here so `canon sync --check` and `canon claude skills
|
|
166
|
+
* drift` cannot word the same three states differently.
|
|
167
|
+
*
|
|
168
|
+
* The remedy is chosen by the same detection `canon upgrade` runs, because both
|
|
169
|
+
* callers run from a source checkout routinely and that is where the verb
|
|
170
|
+
* refuses. Naming it unconditionally sends a contributor whose clone sits a
|
|
171
|
+
* release behind to a command that declines. The read is a match against the
|
|
172
|
+
* root string rather than a filesystem call, so the line stays cheap.
|
|
173
|
+
*/
|
|
174
|
+
export function describeSkew(
|
|
175
|
+
report: SkewReport,
|
|
176
|
+
root: string = PROJECT_ROOT,
|
|
177
|
+
): string {
|
|
178
|
+
if (report.state === 'unknown') {
|
|
179
|
+
return `Installed ${report.installed}, published unknown. ${report.reason}`
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (report.state === 'current') {
|
|
183
|
+
return `Installed ${report.installed}, which is the newest published.`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const remedy =
|
|
187
|
+
detectManager(root) === undefined
|
|
188
|
+
? 'This is a source checkout, so pull rather than reinstalling.'
|
|
189
|
+
: 'Run `canon upgrade`.'
|
|
190
|
+
|
|
191
|
+
return `Installed ${report.installed}, published ${report.latest}. ${remedy}`
|
|
192
|
+
}
|
package/src/wiki/init.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, statSync } from 'node:fs'
|
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
const WIKI_DIR = join('.claude', 'wiki')
|
|
6
|
+
const LEGACY_WIKI_DIR = 'wiki'
|
|
7
|
+
const INDEX_FILE = 'index.md'
|
|
8
|
+
|
|
9
|
+
export const WIKI_DIR_REL = `${WIKI_DIR}/`
|
|
10
|
+
export const WIKI_INDEX_REL = join(WIKI_DIR, INDEX_FILE)
|
|
11
|
+
export const LEGACY_WIKI_DIR_REL = `${LEGACY_WIKI_DIR}/`
|
|
12
|
+
|
|
13
|
+
const INDEX_SEED = `---
|
|
14
|
+
title: Wiki
|
|
15
|
+
subtitle: Reference pages for tools, workflows, and concepts
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Wiki
|
|
19
|
+
|
|
20
|
+
Reference pages for tools, workflows, and concepts.
|
|
21
|
+
`
|
|
22
|
+
|
|
23
|
+
export type WikiChange = 'dir' | 'index'
|
|
24
|
+
|
|
25
|
+
export interface WikiPlan {
|
|
26
|
+
readonly changes: readonly WikiChange[]
|
|
27
|
+
readonly hasIndex: boolean
|
|
28
|
+
readonly hasLegacyWiki: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function wikiDir(target: string): string {
|
|
32
|
+
return join(target, WIKI_DIR)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A target scaffolded before the wiki moved under `.claude/` keeps its pages at
|
|
37
|
+
* the root. The verb reports that folder rather than moving it, because a move
|
|
38
|
+
* is a migration and the `migration-*` skills own those.
|
|
39
|
+
*/
|
|
40
|
+
export function legacyWikiDir(target: string): string {
|
|
41
|
+
return join(target, LEGACY_WIKI_DIR)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Stands in for the `cd "$target"` in the bash `guard_root`, which validated
|
|
46
|
+
* the target existed as a side effect. Without it a typo'd path scaffolds a
|
|
47
|
+
* whole new tree, since the apply step creates parents.
|
|
48
|
+
*/
|
|
49
|
+
export function isWikiTarget(target: string): boolean {
|
|
50
|
+
try {
|
|
51
|
+
return statSync(target).isDirectory()
|
|
52
|
+
} catch {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reports what a scaffold would create without writing. The directory and the
|
|
59
|
+
* index are tracked separately because a target can carry one without the
|
|
60
|
+
* other, and only the index has content worth preserving.
|
|
61
|
+
*/
|
|
62
|
+
export function planWikiInit(target: string): WikiPlan {
|
|
63
|
+
const dir = wikiDir(target)
|
|
64
|
+
const index = join(dir, INDEX_FILE)
|
|
65
|
+
const changes: WikiChange[] = []
|
|
66
|
+
|
|
67
|
+
if (!existsSync(dir)) changes.push('dir')
|
|
68
|
+
|
|
69
|
+
const hasIndex = existsSync(index)
|
|
70
|
+
if (!hasIndex) changes.push('index')
|
|
71
|
+
|
|
72
|
+
return { changes, hasIndex, hasLegacyWiki: existsSync(legacyWikiDir(target)) }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function applyWikiInit(
|
|
76
|
+
target: string,
|
|
77
|
+
plan: WikiPlan,
|
|
78
|
+
): Promise<void> {
|
|
79
|
+
const dir = wikiDir(target)
|
|
80
|
+
await mkdir(dir, { recursive: true })
|
|
81
|
+
|
|
82
|
+
if (plan.changes.includes('index')) {
|
|
83
|
+
await writeFile(join(dir, INDEX_FILE), INDEX_SEED)
|
|
84
|
+
}
|
|
85
|
+
}
|