@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,99 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
import { CORPORA } from '@/claude/skills-audit'
|
|
4
|
+
|
|
5
|
+
const FRONTMATTER = /^---\n([\s\S]*?)\n---/
|
|
6
|
+
|
|
7
|
+
export interface SkillListing {
|
|
8
|
+
readonly name: string
|
|
9
|
+
readonly description: string
|
|
10
|
+
readonly requirement: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SkillsCorpus {
|
|
14
|
+
/**
|
|
15
|
+
* The corpus spelling in POSIX form, so a report reads the same on Windows.
|
|
16
|
+
* `CorpusReport.rel` in `skills-audit.ts` is the same spelling left as `join`
|
|
17
|
+
* produced it, so the two verbs disagree there. Normalizing it is a contract
|
|
18
|
+
* change on an existing JSON field and belongs to a branch reading the audit.
|
|
19
|
+
*/
|
|
20
|
+
readonly rel: string
|
|
21
|
+
/** The folder a listing reads, absolute against the root it was resolved at. */
|
|
22
|
+
readonly dir: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The skill corpus a measure reads at a given root: the shipped tree in this
|
|
27
|
+
* repository and a target's own `.claude/skills/` in a project that consumes
|
|
28
|
+
* it. `CORPORA` order settles a tree carrying both, so every reading taken
|
|
29
|
+
* here still comes from `claude/skills/`.
|
|
30
|
+
*
|
|
31
|
+
* Kept apart from `listSkills` deliberately. `src/counts/catalogs.ts` counts
|
|
32
|
+
* the shipped catalog through that function, so teaching it to read both
|
|
33
|
+
* corpora would move the reported total off the tree that installs and
|
|
34
|
+
* falsify every sentence in the corpus stating it.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveSkillsCorpus(root: string): SkillsCorpus | undefined {
|
|
37
|
+
for (const rel of CORPORA) {
|
|
38
|
+
const dir = join(root, rel)
|
|
39
|
+
if (existsSync(dir)) return { rel: rel.replaceAll('\\', '/'), dir }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Enumerates the plugin skill catalog, which is the corpus under `claude/`
|
|
47
|
+
* rather than the internal skills under `.claude/`. Only the former installs
|
|
48
|
+
* into a target, so a count taken across both overstates what ships.
|
|
49
|
+
*
|
|
50
|
+
* The folder name wins over the frontmatter `name` when they disagree, because
|
|
51
|
+
* Claude Code invokes a skill by its directory.
|
|
52
|
+
*
|
|
53
|
+
* `requirement` reports whether the folder carries `REQUIREMENT.md`. Every skill
|
|
54
|
+
* is meant to carry one, so a false is a gap rather than a recorded exemption.
|
|
55
|
+
* `canon claude skills audit` is what fails on it, across both corpora.
|
|
56
|
+
*/
|
|
57
|
+
export function listSkills(root: string): SkillListing[] {
|
|
58
|
+
return listSkillsAt(join(root, 'claude', 'skills'))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The same enumeration against a corpus folder the caller already resolved,
|
|
63
|
+
* which is what `resolveSkillsCorpus` hands a measure that reaches a target.
|
|
64
|
+
*/
|
|
65
|
+
export function listSkillsAt(skillsRoot: string): SkillListing[] {
|
|
66
|
+
if (!existsSync(skillsRoot)) return []
|
|
67
|
+
|
|
68
|
+
const paths = [
|
|
69
|
+
...new Bun.Glob('*/SKILL.md').scanSync({
|
|
70
|
+
cwd: skillsRoot,
|
|
71
|
+
onlyFiles: true,
|
|
72
|
+
}),
|
|
73
|
+
].sort()
|
|
74
|
+
|
|
75
|
+
return paths.map((path) => ({
|
|
76
|
+
name: dirname(path),
|
|
77
|
+
description: readDescription(join(skillsRoot, path)),
|
|
78
|
+
requirement: existsSync(join(skillsRoot, dirname(path), 'REQUIREMENT.md')),
|
|
79
|
+
}))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns an empty description rather than throwing on a skill whose
|
|
84
|
+
* frontmatter is missing or unparseable, so one malformed file does not hide
|
|
85
|
+
* the rest of the catalog from a caller counting it.
|
|
86
|
+
*/
|
|
87
|
+
function readDescription(path: string): string {
|
|
88
|
+
const match = FRONTMATTER.exec(readFileSync(path, 'utf8'))
|
|
89
|
+
if (!match) return ''
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const parsed = Bun.YAML.parse(match[1])
|
|
93
|
+
if (typeof parsed !== 'object' || parsed === null) return ''
|
|
94
|
+
const { description } = parsed as Record<string, unknown>
|
|
95
|
+
return typeof description === 'string' ? description : ''
|
|
96
|
+
} catch {
|
|
97
|
+
return ''
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { listSkillsAt, resolveSkillsCorpus } from '@/claude/skills-list'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Whether a prompt reaches the right skill, measured by TF-IDF cosine
|
|
6
|
+
* similarity over the shipped catalog's own frontmatter descriptions. This is
|
|
7
|
+
* a necessary condition rather than a report of real routing behavior: it
|
|
8
|
+
* asks whether the descriptions are separable by the words they use, and
|
|
9
|
+
* Claude Code does not route this way.
|
|
10
|
+
*
|
|
11
|
+
* Ported from `.claude/groundwork/42-ai-blueprint/evidence/rank.ts`, which
|
|
12
|
+
* ran once against this catalog and named the collisions this measure now
|
|
13
|
+
* tracks on a cadence.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const STOP = new Set(
|
|
17
|
+
'a about after again all also and any are as at be before but by can do does for from has have help how in into is it its just make not of on or our so that the then there these this to use used uses using want was what when where which who why with you your run'.split(
|
|
18
|
+
' ',
|
|
19
|
+
),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export interface RankedSkill {
|
|
23
|
+
readonly name: string
|
|
24
|
+
readonly description: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** One prompt and the skill it should reach. */
|
|
28
|
+
export interface SkillCase {
|
|
29
|
+
readonly prompt: string
|
|
30
|
+
readonly expect: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A case whose prompt did not rank its expected skill first. */
|
|
34
|
+
export interface Miss {
|
|
35
|
+
readonly prompt: string
|
|
36
|
+
readonly expect: string
|
|
37
|
+
/** The skill the ranker placed first instead. */
|
|
38
|
+
readonly won: string
|
|
39
|
+
/** Where `expect` placed, or 0 when it never appears in the catalog. */
|
|
40
|
+
readonly rank: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Why a measure produced no reading, which is never the same as a clean one. */
|
|
44
|
+
export type RankRefusal = 'no-skills' | 'no-cases' | 'bad-cases'
|
|
45
|
+
|
|
46
|
+
/** The refusals a case corpus read produces, which the scan itself cannot raise. */
|
|
47
|
+
export type CaseCorpusRefusal = Extract<RankRefusal, 'no-cases' | 'bad-cases'>
|
|
48
|
+
|
|
49
|
+
export type CaseCorpusReport =
|
|
50
|
+
| { readonly kind: 'cases'; readonly cases: readonly SkillCase[] }
|
|
51
|
+
| {
|
|
52
|
+
readonly kind: 'refused'
|
|
53
|
+
readonly reason: CaseCorpusRefusal
|
|
54
|
+
/** What the caller has to change, which the reason alone never says. */
|
|
55
|
+
readonly detail: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type RankReport =
|
|
59
|
+
| {
|
|
60
|
+
readonly kind: 'measured'
|
|
61
|
+
/** The corpus spelling measured, since a root can carry either one. */
|
|
62
|
+
readonly corpus: string
|
|
63
|
+
readonly skills: number
|
|
64
|
+
readonly cases: number
|
|
65
|
+
readonly rank1: number
|
|
66
|
+
readonly top3: number
|
|
67
|
+
readonly misses: readonly Miss[]
|
|
68
|
+
/** Cases whose prompt carried no vocabulary to score. */
|
|
69
|
+
readonly unmeasurable: readonly SkillCase[]
|
|
70
|
+
}
|
|
71
|
+
| { readonly kind: 'refused'; readonly reason: RankRefusal }
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Reads a project's own case corpus, which is JSON in the shape `SKILL_CASES`
|
|
75
|
+
* already holds. A target authors its own skills and its own vocabulary, so
|
|
76
|
+
* the toolkit corpus answers a question no other project asked.
|
|
77
|
+
*
|
|
78
|
+
* No standard stands behind the shape until a third project needs one, so
|
|
79
|
+
* every way the file fails is reported with what to change rather than
|
|
80
|
+
* measured against a spec. An empty array refuses for the reason a missing
|
|
81
|
+
* file does: a corpus of nothing scores 0 of 0 and reads as a clean pass.
|
|
82
|
+
*/
|
|
83
|
+
export function loadCaseCorpus(path: string): CaseCorpusReport {
|
|
84
|
+
if (!existsSync(path)) {
|
|
85
|
+
return { kind: 'refused', reason: 'no-cases', detail: path }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let parsed: unknown
|
|
89
|
+
try {
|
|
90
|
+
parsed = JSON.parse(readFileSync(path, 'utf8'))
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const detail = error instanceof Error ? error.message : String(error)
|
|
93
|
+
return { kind: 'refused', reason: 'bad-cases', detail }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!Array.isArray(parsed)) {
|
|
97
|
+
return {
|
|
98
|
+
kind: 'refused',
|
|
99
|
+
reason: 'bad-cases',
|
|
100
|
+
detail: 'the file holds something other than an array of cases',
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const cases: SkillCase[] = []
|
|
105
|
+
for (const [index, entry] of parsed.entries()) {
|
|
106
|
+
const record = entry as Record<string, unknown> | null
|
|
107
|
+
if (
|
|
108
|
+
typeof record !== 'object' ||
|
|
109
|
+
record === null ||
|
|
110
|
+
typeof record.prompt !== 'string' ||
|
|
111
|
+
typeof record.expect !== 'string'
|
|
112
|
+
) {
|
|
113
|
+
return {
|
|
114
|
+
kind: 'refused',
|
|
115
|
+
reason: 'bad-cases',
|
|
116
|
+
detail: `case ${index} carries no string prompt or expect`,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
cases.push({ prompt: record.prompt, expect: record.expect })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (cases.length === 0) {
|
|
124
|
+
return {
|
|
125
|
+
kind: 'refused',
|
|
126
|
+
reason: 'bad-cases',
|
|
127
|
+
detail: 'the file holds no cases at all',
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { kind: 'cases', cases }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Every shipped skill's frontmatter description, read the way a prompt is
|
|
136
|
+
* matched against it: whole, including the quoted trigger phrases it states.
|
|
137
|
+
* A skill whose frontmatter carries no description contributes no vocabulary
|
|
138
|
+
* and never wins a rank, so it is dropped rather than scored on nothing.
|
|
139
|
+
*/
|
|
140
|
+
export function loadCatalog(root: string): RankedSkill[] {
|
|
141
|
+
const corpus = resolveSkillsCorpus(root)
|
|
142
|
+
return corpus === undefined ? [] : loadCatalogAt(corpus.dir)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The same read against a corpus folder the caller already resolved. */
|
|
146
|
+
export function loadCatalogAt(skillsRoot: string): RankedSkill[] {
|
|
147
|
+
return listSkillsAt(skillsRoot)
|
|
148
|
+
.filter((skill) => skill.description !== '')
|
|
149
|
+
.map((skill) => ({ name: skill.name, description: skill.description }))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function tokenize(text: string): string[] {
|
|
153
|
+
return text
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
156
|
+
.split(/\s+/)
|
|
157
|
+
.filter((token) => token.length > 2 && !STOP.has(token))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function termCounts(tokens: readonly string[]): Map<string, number> {
|
|
161
|
+
const counts = new Map<string, number>()
|
|
162
|
+
for (const token of tokens) counts.set(token, (counts.get(token) ?? 0) + 1)
|
|
163
|
+
return counts
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function buildIdf(
|
|
167
|
+
docs: readonly Map<string, number>[],
|
|
168
|
+
): (term: string) => number {
|
|
169
|
+
const total = docs.length
|
|
170
|
+
const documentFrequency = new Map<string, number>()
|
|
171
|
+
for (const doc of docs) {
|
|
172
|
+
for (const term of doc.keys()) {
|
|
173
|
+
documentFrequency.set(term, (documentFrequency.get(term) ?? 0) + 1)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return (term) =>
|
|
177
|
+
Math.log((total + 1) / ((documentFrequency.get(term) ?? 0) + 1)) + 1
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function tfIdfVector(
|
|
181
|
+
terms: Map<string, number>,
|
|
182
|
+
idf: (term: string) => number,
|
|
183
|
+
): Map<string, number> {
|
|
184
|
+
const vec = new Map<string, number>()
|
|
185
|
+
for (const [term, frequency] of terms) vec.set(term, frequency * idf(term))
|
|
186
|
+
return vec
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function cosineSimilarity(
|
|
190
|
+
a: Map<string, number>,
|
|
191
|
+
b: Map<string, number>,
|
|
192
|
+
): number {
|
|
193
|
+
let dot = 0
|
|
194
|
+
for (const [term, weight] of a) dot += weight * (b.get(term) ?? 0)
|
|
195
|
+
if (dot === 0) return 0
|
|
196
|
+
|
|
197
|
+
const norm = (vec: Map<string, number>) =>
|
|
198
|
+
Math.sqrt(
|
|
199
|
+
[...vec.values()].reduce((sum, weight) => sum + weight * weight, 0),
|
|
200
|
+
)
|
|
201
|
+
const denominator = norm(a) * norm(b)
|
|
202
|
+
return denominator === 0 ? 0 : dot / denominator
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface RankedResult {
|
|
206
|
+
readonly name: string
|
|
207
|
+
readonly score: number
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Every skill's TF-IDF vector over the catalog it was loaded with, and the
|
|
212
|
+
* IDF weighting that both the catalog and a scored prompt read from. A model
|
|
213
|
+
* is built once per catalog and reused across every case, since the IDF
|
|
214
|
+
* weights are the same question asked of the same corpus each time.
|
|
215
|
+
*/
|
|
216
|
+
export interface RankModel {
|
|
217
|
+
readonly rank: (prompt: string) => readonly RankedResult[]
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function buildModel(catalog: readonly RankedSkill[]): RankModel {
|
|
221
|
+
const docs = catalog.map((skill) => termCounts(tokenize(skill.description)))
|
|
222
|
+
const idf = buildIdf(docs)
|
|
223
|
+
const vectors = new Map(
|
|
224
|
+
catalog.map((skill, index) => [skill.name, tfIdfVector(docs[index], idf)]),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
rank: (prompt: string): readonly RankedResult[] => {
|
|
229
|
+
const promptVector = tfIdfVector(termCounts(tokenize(prompt)), idf)
|
|
230
|
+
// A prompt built entirely from stopwords and short words tokenizes to
|
|
231
|
+
// nothing, so every skill would score 0 and the sort would fall through
|
|
232
|
+
// to `localeCompare`, handing the alphabetically first skill a win no
|
|
233
|
+
// description earned. Reporting no ranking at all is what keeps that
|
|
234
|
+
// tie-break from reading as a measurement.
|
|
235
|
+
if (promptVector.size === 0) return []
|
|
236
|
+
|
|
237
|
+
return [...vectors]
|
|
238
|
+
.map(([name, vector]) => ({
|
|
239
|
+
name,
|
|
240
|
+
score: cosineSimilarity(promptVector, vector),
|
|
241
|
+
}))
|
|
242
|
+
.sort(
|
|
243
|
+
(left, right) =>
|
|
244
|
+
right.score - left.score || left.name.localeCompare(right.name),
|
|
245
|
+
)
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Scores every case against the catalog's own descriptions, which is the
|
|
252
|
+
* production mechanism: Claude Code matches a prompt against the whole
|
|
253
|
+
* description a skill ships, triggers included.
|
|
254
|
+
*/
|
|
255
|
+
export function measureCases(
|
|
256
|
+
catalog: readonly RankedSkill[],
|
|
257
|
+
cases: readonly SkillCase[],
|
|
258
|
+
): {
|
|
259
|
+
readonly rank1: number
|
|
260
|
+
readonly top3: number
|
|
261
|
+
readonly misses: readonly Miss[]
|
|
262
|
+
readonly unmeasurable: readonly SkillCase[]
|
|
263
|
+
} {
|
|
264
|
+
const model = buildModel(catalog)
|
|
265
|
+
let rank1 = 0
|
|
266
|
+
let top3 = 0
|
|
267
|
+
const misses: Miss[] = []
|
|
268
|
+
const unmeasurable: SkillCase[] = []
|
|
269
|
+
|
|
270
|
+
for (const skillCase of cases) {
|
|
271
|
+
const ranked = model.rank(skillCase.prompt)
|
|
272
|
+
if (ranked.length === 0) {
|
|
273
|
+
unmeasurable.push(skillCase)
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const at = ranked.findIndex((entry) => entry.name === skillCase.expect) + 1
|
|
278
|
+
|
|
279
|
+
if (at === 1) rank1 += 1
|
|
280
|
+
if (at > 0 && at <= 3) top3 += 1
|
|
281
|
+
if (at !== 1) {
|
|
282
|
+
misses.push({
|
|
283
|
+
prompt: skillCase.prompt,
|
|
284
|
+
expect: skillCase.expect,
|
|
285
|
+
won: ranked[0]?.name ?? '',
|
|
286
|
+
rank: at,
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return { rank1, top3, misses, unmeasurable }
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Reads whichever skill corpus the root carries and scores it against the
|
|
296
|
+
* given cases. Measures the cwd's catalog rather than the toolkit root,
|
|
297
|
+
* matching the reach and audit verbs, so a linked worktree reads its own
|
|
298
|
+
* branch and a target reads the skills it wrote itself.
|
|
299
|
+
*/
|
|
300
|
+
export function scanRank(
|
|
301
|
+
root: string,
|
|
302
|
+
cases: readonly SkillCase[],
|
|
303
|
+
): RankReport {
|
|
304
|
+
const corpus = resolveSkillsCorpus(root)
|
|
305
|
+
if (corpus === undefined) return { kind: 'refused', reason: 'no-skills' }
|
|
306
|
+
|
|
307
|
+
const catalog = loadCatalogAt(corpus.dir)
|
|
308
|
+
const { rank1, top3, misses, unmeasurable } = measureCases(catalog, cases)
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
kind: 'measured',
|
|
312
|
+
corpus: corpus.rel,
|
|
313
|
+
skills: catalog.length,
|
|
314
|
+
cases: cases.length,
|
|
315
|
+
rank1,
|
|
316
|
+
top3,
|
|
317
|
+
misses,
|
|
318
|
+
unmeasurable,
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { resolveSkillsCorpus } from '@/claude/skills-list'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The authoring roots this repository owns and no install channel delivers.
|
|
7
|
+
*
|
|
8
|
+
* Every entry is a folder a target never holds under that spelling. Standards
|
|
9
|
+
* and snippets install nowhere and are reached through the plugin corpus,
|
|
10
|
+
* rules install under `.claude/rules/`, and the rest are this repository's
|
|
11
|
+
* own source, docs, and catalogs.
|
|
12
|
+
*
|
|
13
|
+
* `src/`, `scripts/`, and bare `docs/` are deliberately absent. A body naming
|
|
14
|
+
* one of those is describing the reader's own tree, so listing them would
|
|
15
|
+
* report a correct citation on every run and bury the defect this measures.
|
|
16
|
+
* `docs/agents/` is the exception, being the CLI contract pages that exist
|
|
17
|
+
* here alone.
|
|
18
|
+
*/
|
|
19
|
+
const AUTHORING_ROOTS = [
|
|
20
|
+
'.claude/context/',
|
|
21
|
+
'claude/',
|
|
22
|
+
'docs/agents/',
|
|
23
|
+
'governance/',
|
|
24
|
+
'internal/',
|
|
25
|
+
'snippets/',
|
|
26
|
+
'standards/',
|
|
27
|
+
'tooling/',
|
|
28
|
+
'wiki/',
|
|
29
|
+
] as const
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What marks a citation as deliberately naming this repository's own copy.
|
|
33
|
+
*
|
|
34
|
+
* The word rather than a notation, matching the three bodies that already
|
|
35
|
+
* spell it and the repair the plan settled on. A parser-visible syntax was the
|
|
36
|
+
* alternative and it invents a spelling for a handful of lines while leaving
|
|
37
|
+
* the shipped precedent unreadable.
|
|
38
|
+
*/
|
|
39
|
+
const QUALIFIER = /toolkit/i
|
|
40
|
+
|
|
41
|
+
/** A backticked token, which is how every body spells a path it cites. */
|
|
42
|
+
const TOKEN = /`([^`\s]+)`/g
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A path a reader could open, which is the only kind worth measuring.
|
|
46
|
+
*
|
|
47
|
+
* Requires an extension and a separator, and admits no `<`, `$`, or `*`. A
|
|
48
|
+
* body writes `.claude/context/<domain>.md` to name a shape rather than a
|
|
49
|
+
* file, and `${CLAUDE_SKILL_DIR}/../../standards/markdown.md` to resolve
|
|
50
|
+
* against the plugin root, which is self-contained by construction.
|
|
51
|
+
*/
|
|
52
|
+
const CONCRETE = /^[.A-Za-z0-9_][A-Za-z0-9._/-]*\.[a-z]{1,4}$/
|
|
53
|
+
|
|
54
|
+
export interface Citation {
|
|
55
|
+
readonly file: string
|
|
56
|
+
/** One-based, matching the `file:line` form a reader clicks. */
|
|
57
|
+
readonly line: number
|
|
58
|
+
readonly path: string
|
|
59
|
+
readonly qualified: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Why a scan produced no corpus, which is never the same as a clean one. */
|
|
63
|
+
export type ReachRefusal = 'no-skills'
|
|
64
|
+
|
|
65
|
+
export type ReachReport =
|
|
66
|
+
| {
|
|
67
|
+
readonly kind: 'measured'
|
|
68
|
+
/** The corpus spelling read, since a root can carry either one. */
|
|
69
|
+
readonly corpus: string
|
|
70
|
+
/** Files opened, so a report can state what the verdict covers. */
|
|
71
|
+
readonly bodies: number
|
|
72
|
+
readonly qualified: readonly Citation[]
|
|
73
|
+
readonly unqualified: readonly Citation[]
|
|
74
|
+
}
|
|
75
|
+
| { readonly kind: 'refused'; readonly reason: ReachRefusal }
|
|
76
|
+
|
|
77
|
+
export function isQualified(line: string): boolean {
|
|
78
|
+
return QUALIFIER.test(line)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The roots that belong to the toolkit rather than to the reader, read
|
|
83
|
+
* against the corpus being measured.
|
|
84
|
+
*
|
|
85
|
+
* A target's `.claude/context/` is the reader's own tree. A seed put the
|
|
86
|
+
* entries there and the project owns them afterward, so a body under
|
|
87
|
+
* `.claude/skills/` citing one names a file its reader holds. Measuring it
|
|
88
|
+
* would report a correct citation on every run, which is exactly why `src/`
|
|
89
|
+
* and `scripts/` are absent from the list above. In this repository the seed
|
|
90
|
+
* tree settles the same question through `readReceivedPaths`, which a target
|
|
91
|
+
* carrying no `tooling/` folder cannot answer at all, so the root comes out
|
|
92
|
+
* by corpus instead.
|
|
93
|
+
*/
|
|
94
|
+
export function authoringRootsFor(corpus: string): readonly string[] {
|
|
95
|
+
if (!corpus.startsWith('.claude/')) return AUTHORING_ROOTS
|
|
96
|
+
|
|
97
|
+
return AUTHORING_ROOTS.filter((root) => !root.startsWith('.claude/'))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Every path a seed lands on in a target, spelled the way a body would cite it.
|
|
102
|
+
*
|
|
103
|
+
* Read off the seed tree rather than listed, so a seed added to any stack
|
|
104
|
+
* clears its own citations without this module being edited. Dotfiles are in
|
|
105
|
+
* scope because the whole seeded context corpus sits under `.claude/`.
|
|
106
|
+
*/
|
|
107
|
+
export function readReceivedPaths(root: string): Set<string> {
|
|
108
|
+
const toolingRoot = join(root, 'tooling')
|
|
109
|
+
if (!existsSync(toolingRoot)) return new Set()
|
|
110
|
+
|
|
111
|
+
const received = new Set<string>()
|
|
112
|
+
for (const path of new Bun.Glob('*/seeds/**/*').scanSync({
|
|
113
|
+
cwd: toolingRoot,
|
|
114
|
+
onlyFiles: true,
|
|
115
|
+
dot: true,
|
|
116
|
+
})) {
|
|
117
|
+
const posix = path.replaceAll('\\', '/')
|
|
118
|
+
received.add(posix.replace(/^[^/]+\/seeds\//, ''))
|
|
119
|
+
}
|
|
120
|
+
return received
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Whether a cited path is this repository's own rather than the reader's.
|
|
125
|
+
*
|
|
126
|
+
* A seeded path is disowned twice over: under its own name, and under the
|
|
127
|
+
* folder spelling it takes once a target splits the entry. A domain that
|
|
128
|
+
* outgrows one file becomes `<domain>/`, which is still the entry the seed
|
|
129
|
+
* delivered, so reporting the split form would fail a target for growing.
|
|
130
|
+
*/
|
|
131
|
+
export function isToolkitOwned(
|
|
132
|
+
path: string,
|
|
133
|
+
received: Set<string>,
|
|
134
|
+
roots: readonly string[] = AUTHORING_ROOTS,
|
|
135
|
+
): boolean {
|
|
136
|
+
if (received.has(path)) return false
|
|
137
|
+
|
|
138
|
+
for (const seeded of received) {
|
|
139
|
+
const stem = seeded.replace(/\.md$/, '')
|
|
140
|
+
if (stem !== seeded && path.startsWith(`${stem}/`)) return false
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return roots.some((prefix) => path.startsWith(prefix))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Every toolkit-owned path one shipped file cites, with the line's verdict.
|
|
148
|
+
*
|
|
149
|
+
* Existence is not checked here. A body may name a path this repository once
|
|
150
|
+
* held, and separating the shape test from the disk read is what lets the
|
|
151
|
+
* shape be tested without a tree on disk.
|
|
152
|
+
*/
|
|
153
|
+
export function citationsIn(
|
|
154
|
+
file: string,
|
|
155
|
+
text: string,
|
|
156
|
+
received: Set<string>,
|
|
157
|
+
roots: readonly string[] = AUTHORING_ROOTS,
|
|
158
|
+
): Citation[] {
|
|
159
|
+
const citations: Citation[] = []
|
|
160
|
+
|
|
161
|
+
for (const [index, line] of text.split('\n').entries()) {
|
|
162
|
+
const qualified = isQualified(line)
|
|
163
|
+
|
|
164
|
+
for (const match of line.matchAll(TOKEN)) {
|
|
165
|
+
const path = match[1]
|
|
166
|
+
if (!CONCRETE.test(path) || !path.includes('/')) continue
|
|
167
|
+
if (!isToolkitOwned(path, received, roots)) continue
|
|
168
|
+
|
|
169
|
+
citations.push({ file, line: index + 1, path, qualified })
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return citations
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Reads every body in the root's skill corpus for a path its reader cannot
|
|
178
|
+
* open.
|
|
179
|
+
*
|
|
180
|
+
* A citation of a path this repository does not hold is dropped rather than
|
|
181
|
+
* reported. The measure asks whether a claim true here is false in a target,
|
|
182
|
+
* and a path true in neither is a different defect that `canon context audit`
|
|
183
|
+
* already reports against its own corpus.
|
|
184
|
+
*
|
|
185
|
+
* `resolveSkillsCorpus` prefers `claude/skills/`, which is the tree that
|
|
186
|
+
* installs into a target, so this repository's own reading is the one it
|
|
187
|
+
* always was. A project carrying `.claude/skills/` alone has no shipped tree
|
|
188
|
+
* and its own skills are the whole corpus a reader there opens.
|
|
189
|
+
*/
|
|
190
|
+
export function scanReach(root: string): ReachReport {
|
|
191
|
+
const corpus = resolveSkillsCorpus(root)
|
|
192
|
+
if (corpus === undefined) return { kind: 'refused', reason: 'no-skills' }
|
|
193
|
+
|
|
194
|
+
const received = readReceivedPaths(root)
|
|
195
|
+
const roots = authoringRootsFor(corpus.rel)
|
|
196
|
+
const files = [
|
|
197
|
+
...new Bun.Glob('**/*.md').scanSync({ cwd: corpus.dir, onlyFiles: true }),
|
|
198
|
+
].sort()
|
|
199
|
+
|
|
200
|
+
const qualified: Citation[] = []
|
|
201
|
+
const unqualified: Citation[] = []
|
|
202
|
+
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
const posix = file.replaceAll('\\', '/')
|
|
205
|
+
const text = readFileSync(join(corpus.dir, file), 'utf8')
|
|
206
|
+
|
|
207
|
+
for (const citation of citationsIn(
|
|
208
|
+
`${corpus.rel}/${posix}`,
|
|
209
|
+
text,
|
|
210
|
+
received,
|
|
211
|
+
roots,
|
|
212
|
+
)) {
|
|
213
|
+
if (!existsSync(join(root, citation.path))) continue
|
|
214
|
+
|
|
215
|
+
if (citation.qualified) qualified.push(citation)
|
|
216
|
+
else unqualified.push(citation)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
kind: 'measured',
|
|
222
|
+
corpus: corpus.rel,
|
|
223
|
+
bodies: files.length,
|
|
224
|
+
qualified,
|
|
225
|
+
unqualified,
|
|
226
|
+
}
|
|
227
|
+
}
|
package/src/cli-run.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { execa } from 'execa'
|
|
3
|
+
|
|
4
|
+
export interface CliRunOptions {
|
|
5
|
+
/** Sets `CANON_NON_INTERACTIVE=1` for the child. Defaults to false. */
|
|
6
|
+
readonly nonInteractive?: boolean
|
|
7
|
+
/** Defaults to `inherit`, so a child prompt still reaches the operator. */
|
|
8
|
+
readonly stdin?: 'ignore' | 'inherit'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function cliPath(root: string): string {
|
|
12
|
+
return join(root, 'src', 'cli.ts')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Spawns a `canon` subcommand as its own process, by path rather than through
|
|
17
|
+
* the global binary, so a linked worktree exercises its own code.
|
|
18
|
+
*
|
|
19
|
+
* Both callers want a separately failable child rather than an in-process call:
|
|
20
|
+
* `canon init` reports each domain's outcome independently, and `canon sync`
|
|
21
|
+
* relies on each domain opening its own frame. Returns whether the child
|
|
22
|
+
* succeeded rather than throwing, because a failed domain is a reportable
|
|
23
|
+
* outcome and not an abort.
|
|
24
|
+
*/
|
|
25
|
+
export function cliRun(
|
|
26
|
+
path: string,
|
|
27
|
+
args: readonly string[],
|
|
28
|
+
options: CliRunOptions = {},
|
|
29
|
+
): () => Promise<boolean> {
|
|
30
|
+
return async () => {
|
|
31
|
+
const env = options.nonInteractive
|
|
32
|
+
? { ...process.env, CANON_NON_INTERACTIVE: '1' }
|
|
33
|
+
: process.env
|
|
34
|
+
|
|
35
|
+
const result = await execa('bun', [path, ...args], {
|
|
36
|
+
stdio: [options.stdin ?? 'inherit', 'inherit', 'inherit'],
|
|
37
|
+
env,
|
|
38
|
+
reject: false,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return result.exitCode === 0
|
|
42
|
+
}
|
|
43
|
+
}
|