@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/worktree.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import { gitEnv } from '@/git-env'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the root of the checkout the caller is standing in, which is the
|
|
6
|
+
* linked worktree rather than the main one when a session is inside one.
|
|
7
|
+
*
|
|
8
|
+
* A tracked tree differs per worktree, so a verb reading one answers about the
|
|
9
|
+
* files the session has edited only if it resolves the root this way. The
|
|
10
|
+
* working directory is not a substitute, since a caller invoking from a
|
|
11
|
+
* subdirectory would resolve a root holding none of the trees a verb reads.
|
|
12
|
+
*/
|
|
13
|
+
export async function currentWorktreeRoot(): Promise<string> {
|
|
14
|
+
const result = await $`git rev-parse --show-toplevel`
|
|
15
|
+
.env(gitEnv())
|
|
16
|
+
.quiet()
|
|
17
|
+
.nothrow()
|
|
18
|
+
if (result.exitCode !== 0) return process.cwd()
|
|
19
|
+
|
|
20
|
+
return result.stdout.toString().trim() || process.cwd()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the root every shared-scratch folder lives under. `git worktree
|
|
25
|
+
* list` puts the main worktree first, and trusting the working directory
|
|
26
|
+
* instead would write a second board nothing else reads, or validate a linked
|
|
27
|
+
* worktree's empty folder and report it clean.
|
|
28
|
+
*
|
|
29
|
+
* A caller reaching this from a linked worktree is the case it exists for: the
|
|
30
|
+
* file-editing tools refuse a main-root path there, so a verb resolving the
|
|
31
|
+
* root in-process is the route a skill body has.
|
|
32
|
+
*/
|
|
33
|
+
export async function mainWorktreeRoot(): Promise<string> {
|
|
34
|
+
const result = await $`git worktree list --porcelain`
|
|
35
|
+
.env(gitEnv())
|
|
36
|
+
.quiet()
|
|
37
|
+
.nothrow()
|
|
38
|
+
if (result.exitCode !== 0) return process.cwd()
|
|
39
|
+
|
|
40
|
+
const line = result.stdout
|
|
41
|
+
.toString()
|
|
42
|
+
.split('\n')
|
|
43
|
+
.find((entry) => entry.startsWith('worktree '))
|
|
44
|
+
|
|
45
|
+
return line ? line.slice('worktree '.length).trim() : process.cwd()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface WorktreeEntry {
|
|
49
|
+
readonly path: string
|
|
50
|
+
readonly branch: string | null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RefReport {
|
|
54
|
+
/** False when the ref read itself failed, so an empty `refs` says nothing about whether the branch exists. */
|
|
55
|
+
readonly readable: boolean
|
|
56
|
+
readonly refs: readonly string[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Reports which of the local head and the `origin` remote-tracking ref already
|
|
61
|
+
* name a branch, separating an absent branch from a read that failed.
|
|
62
|
+
*
|
|
63
|
+
* `git for-each-ref` is what carries that separation. `git show-ref --verify`
|
|
64
|
+
* handed the same two ref paths exits 128 when one of them is missing, which is
|
|
65
|
+
* the code it also exits when the directory is no repository, so a caller
|
|
66
|
+
* cannot tell a half match from an unreadable tree. `for-each-ref` exits zero
|
|
67
|
+
* with empty output for absent and non-zero only for a failed read.
|
|
68
|
+
*
|
|
69
|
+
* A ref path is a pattern here rather than an exact name, so `refs/heads/x`
|
|
70
|
+
* also matches `refs/heads/x/y`. Git forbids both existing at once, so the
|
|
71
|
+
* over-match names the branch that blocks the candidate rather than a wrong
|
|
72
|
+
* one.
|
|
73
|
+
*
|
|
74
|
+
* The remote-tracking ref is read at whatever the last fetch left, so a branch
|
|
75
|
+
* pushed from another machine since then is invisible here. Closing that needs
|
|
76
|
+
* `git ls-remote`, measured at 0.438s against 0.001s for a local read.
|
|
77
|
+
*/
|
|
78
|
+
export async function branchRefs(
|
|
79
|
+
branch: string,
|
|
80
|
+
cwd: string = process.cwd(),
|
|
81
|
+
): Promise<RefReport> {
|
|
82
|
+
// Every argument is interpolated rather than written inline, since Bun's
|
|
83
|
+
// shell parses the bare parentheses in `%(refname)` as syntax of its own.
|
|
84
|
+
const args = [
|
|
85
|
+
'--format=%(refname)',
|
|
86
|
+
`refs/heads/${branch}`,
|
|
87
|
+
`refs/remotes/origin/${branch}`,
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
const result = await $`git -C ${cwd} for-each-ref ${args}`
|
|
91
|
+
.env(gitEnv())
|
|
92
|
+
.quiet()
|
|
93
|
+
.nothrow()
|
|
94
|
+
if (result.exitCode !== 0) return { readable: false, refs: [] }
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
readable: true,
|
|
98
|
+
refs: result.stdout
|
|
99
|
+
.toString()
|
|
100
|
+
.split('\n')
|
|
101
|
+
.map((line) => line.trim())
|
|
102
|
+
.filter((line) => line.length > 0),
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parses `git worktree list --porcelain`, which emits one block per worktree
|
|
108
|
+
* separated by a blank line. A detached worktree carries no `branch` line,
|
|
109
|
+
* reported here as `null` rather than a guessed name.
|
|
110
|
+
*/
|
|
111
|
+
export async function listWorktrees(
|
|
112
|
+
cwd: string = process.cwd(),
|
|
113
|
+
): Promise<readonly WorktreeEntry[]> {
|
|
114
|
+
const result = await $`git -C ${cwd} worktree list --porcelain`
|
|
115
|
+
.env(gitEnv())
|
|
116
|
+
.quiet()
|
|
117
|
+
.nothrow()
|
|
118
|
+
if (result.exitCode !== 0) return []
|
|
119
|
+
|
|
120
|
+
const entries: WorktreeEntry[] = []
|
|
121
|
+
let path: string | undefined
|
|
122
|
+
let branch: string | null = null
|
|
123
|
+
|
|
124
|
+
for (const line of result.stdout.toString().split('\n')) {
|
|
125
|
+
if (line.startsWith('worktree ')) {
|
|
126
|
+
if (path !== undefined) entries.push({ path, branch })
|
|
127
|
+
path = line.slice('worktree '.length).trim()
|
|
128
|
+
branch = null
|
|
129
|
+
continue
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (line.startsWith('branch ')) {
|
|
133
|
+
const ref = line.slice('branch '.length).trim()
|
|
134
|
+
branch = ref.startsWith('refs/heads/')
|
|
135
|
+
? ref.slice('refs/heads/'.length)
|
|
136
|
+
: ref
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (path !== undefined) entries.push({ path, branch })
|
|
141
|
+
|
|
142
|
+
return entries
|
|
143
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import { execa } from 'execa'
|
|
3
|
+
import { gitEnv } from '@/git-env'
|
|
4
|
+
import {
|
|
5
|
+
repositoryOf,
|
|
6
|
+
type ResolvedSession,
|
|
7
|
+
resolveSessions,
|
|
8
|
+
type SessionReport,
|
|
9
|
+
} from '@/sessions/resolve'
|
|
10
|
+
import { listWorktrees, type WorktreeEntry } from '@/worktree'
|
|
11
|
+
|
|
12
|
+
const GH_TIMEOUT_MS = 30_000
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* How many merged pull requests one read covers. A worktree older than this
|
|
16
|
+
* many merges reads as having none and is refused, which is the safe direction:
|
|
17
|
+
* the failure keeps a directory rather than removing one.
|
|
18
|
+
*/
|
|
19
|
+
const MERGED_LIMIT = 200
|
|
20
|
+
|
|
21
|
+
/** Why one worktree cannot be reclaimed, one entry per failing condition. */
|
|
22
|
+
export type Refusal =
|
|
23
|
+
| 'main-worktree'
|
|
24
|
+
| 'detached-head'
|
|
25
|
+
| 'no-merged-pull-request'
|
|
26
|
+
| 'uncommitted-changes'
|
|
27
|
+
| 'unreadable-worktree'
|
|
28
|
+
| 'held-by-session'
|
|
29
|
+
|
|
30
|
+
/** Why the whole reading was refused, so no verdict was produced at all. */
|
|
31
|
+
export type Unreadable = 'gh-missing' | 'gh-failed' | 'sessions-unreadable'
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Which removal shape applies. `session` removes the background session and its
|
|
35
|
+
* worktree together, and `worktree` removes a directory whose session has
|
|
36
|
+
* ended. Picking the wrong one strands state, so this is reported rather than
|
|
37
|
+
* assumed.
|
|
38
|
+
*/
|
|
39
|
+
export type Route = 'session' | 'worktree'
|
|
40
|
+
|
|
41
|
+
/** No removal shape reaches the main worktree, which is what `null` says. */
|
|
42
|
+
export type RemovalRoute = Route | null
|
|
43
|
+
|
|
44
|
+
export interface WorktreeVerdict {
|
|
45
|
+
readonly path: string
|
|
46
|
+
readonly branch: string | null
|
|
47
|
+
readonly reclaimable: boolean
|
|
48
|
+
/** Every failing condition, so a reader sees what to fix rather than a bare refusal. */
|
|
49
|
+
readonly refusals: readonly Refusal[]
|
|
50
|
+
/** The pull request that retired the branch, so a report can name what it read. */
|
|
51
|
+
readonly pullRequest: number | null
|
|
52
|
+
/** The names of the live sessions holding this worktree, which is what `claude rm` takes. */
|
|
53
|
+
readonly sessions: readonly string[]
|
|
54
|
+
readonly route: RemovalRoute
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface MergedPullRequest {
|
|
58
|
+
readonly branch: string
|
|
59
|
+
readonly number: number
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type MergedReport =
|
|
63
|
+
| { readonly kind: 'read'; readonly merged: readonly MergedPullRequest[] }
|
|
64
|
+
| {
|
|
65
|
+
readonly kind: 'unreadable'
|
|
66
|
+
readonly reason: Extract<Unreadable, 'gh-missing' | 'gh-failed'>
|
|
67
|
+
readonly detail: string
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface StatusReport {
|
|
71
|
+
/** False when the status read itself failed, so a clean `dirty` says nothing. */
|
|
72
|
+
readonly readable: boolean
|
|
73
|
+
readonly dirty: boolean
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type ReclaimReport =
|
|
77
|
+
| {
|
|
78
|
+
readonly kind: 'unreadable'
|
|
79
|
+
readonly reason: Unreadable
|
|
80
|
+
readonly detail: string
|
|
81
|
+
}
|
|
82
|
+
| { readonly kind: 'read'; readonly worktrees: readonly WorktreeVerdict[] }
|
|
83
|
+
|
|
84
|
+
export interface ReclaimOptions {
|
|
85
|
+
readonly cwd?: string
|
|
86
|
+
readonly listWorktrees?: (cwd: string) => Promise<readonly WorktreeEntry[]>
|
|
87
|
+
readonly mergedPullRequests?: (cwd: string) => Promise<MergedReport>
|
|
88
|
+
readonly worktreeStatus?: (path: string) => Promise<StatusReport>
|
|
89
|
+
readonly resolve?: () => Promise<SessionReport>
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Reads the pull request state for the whole repository in one call.
|
|
94
|
+
*
|
|
95
|
+
* One call rather than one per worktree, since the per-worktree shape is a
|
|
96
|
+
* network round trip inside a loop and the branches being matched are already
|
|
97
|
+
* known before any of them runs.
|
|
98
|
+
*/
|
|
99
|
+
async function mergedPullRequests(cwd: string): Promise<MergedReport> {
|
|
100
|
+
if (Bun.which('gh') === null) {
|
|
101
|
+
return {
|
|
102
|
+
kind: 'unreadable',
|
|
103
|
+
reason: 'gh-missing',
|
|
104
|
+
detail: 'gh is not on the path, so no merge state could be read.',
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const args = [
|
|
109
|
+
'pr',
|
|
110
|
+
'list',
|
|
111
|
+
'--state',
|
|
112
|
+
'merged',
|
|
113
|
+
'--limit',
|
|
114
|
+
String(MERGED_LIMIT),
|
|
115
|
+
'--json',
|
|
116
|
+
'headRefName,number',
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// `gh` resolves its repository through git, so it reads the same
|
|
121
|
+
// resolution variables a hook exports and they take precedence over `cwd`.
|
|
122
|
+
// A run from inside one would answer with another repository's merged
|
|
123
|
+
// branches, and a branch name that recurs across repositories would then
|
|
124
|
+
// match a merge that happened somewhere else and read a live worktree as
|
|
125
|
+
// reclaimable, which is the unsafe direction on an unrecoverable removal.
|
|
126
|
+
const result = await execa('gh', args, {
|
|
127
|
+
cwd,
|
|
128
|
+
timeout: GH_TIMEOUT_MS,
|
|
129
|
+
env: gitEnv(),
|
|
130
|
+
extendEnv: false,
|
|
131
|
+
})
|
|
132
|
+
const rows = JSON.parse(result.stdout) as readonly {
|
|
133
|
+
headRefName: string
|
|
134
|
+
number: number
|
|
135
|
+
}[]
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
kind: 'read',
|
|
139
|
+
merged: rows.map((row) => ({
|
|
140
|
+
branch: row.headRefName,
|
|
141
|
+
number: row.number,
|
|
142
|
+
})),
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
kind: 'unreadable',
|
|
147
|
+
reason: 'gh-failed',
|
|
148
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Reports whether a worktree holds work no history is behind.
|
|
155
|
+
*
|
|
156
|
+
* Untracked files count, since a worktree is gitignored scratch and a directory
|
|
157
|
+
* removed with them takes them nowhere recoverable. A failed read is separated
|
|
158
|
+
* from a clean tree, because the two produce the same empty output and only one
|
|
159
|
+
* of them is safe to act on.
|
|
160
|
+
*/
|
|
161
|
+
async function worktreeStatus(path: string): Promise<StatusReport> {
|
|
162
|
+
const result = await $`git -C ${path} status --porcelain`
|
|
163
|
+
.env(gitEnv())
|
|
164
|
+
.quiet()
|
|
165
|
+
.nothrow()
|
|
166
|
+
if (result.exitCode !== 0) return { readable: false, dirty: false }
|
|
167
|
+
|
|
168
|
+
return { readable: true, dirty: result.stdout.toString().trim().length > 0 }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Names the live sessions holding one worktree.
|
|
173
|
+
*
|
|
174
|
+
* The path match is the direct reading and the branch match is what survives a
|
|
175
|
+
* path spelled differently on either side, such as a symlinked temporary
|
|
176
|
+
* directory. The branch half is scoped to the repository, since a branch name
|
|
177
|
+
* identifies a branch inside one and nothing across a machine.
|
|
178
|
+
*/
|
|
179
|
+
function holders(
|
|
180
|
+
entry: WorktreeEntry,
|
|
181
|
+
sessions: readonly ResolvedSession[],
|
|
182
|
+
repository: string | null,
|
|
183
|
+
): readonly string[] {
|
|
184
|
+
return sessions
|
|
185
|
+
.filter(
|
|
186
|
+
(candidate) =>
|
|
187
|
+
candidate.worktree === entry.path ||
|
|
188
|
+
(entry.branch !== null &&
|
|
189
|
+
candidate.branch === entry.branch &&
|
|
190
|
+
candidate.repository === repository),
|
|
191
|
+
)
|
|
192
|
+
.map((candidate) => candidate.name)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function verdict(
|
|
196
|
+
entry: WorktreeEntry,
|
|
197
|
+
isMain: boolean,
|
|
198
|
+
status: StatusReport,
|
|
199
|
+
merged: ReadonlyMap<string, number>,
|
|
200
|
+
sessions: readonly ResolvedSession[],
|
|
201
|
+
repository: string | null,
|
|
202
|
+
): WorktreeVerdict {
|
|
203
|
+
const held = holders(entry, sessions, repository)
|
|
204
|
+
const pullRequest =
|
|
205
|
+
entry.branch === null ? null : (merged.get(entry.branch) ?? null)
|
|
206
|
+
const refusals: Refusal[] = []
|
|
207
|
+
|
|
208
|
+
if (isMain) refusals.push('main-worktree')
|
|
209
|
+
if (entry.branch === null) refusals.push('detached-head')
|
|
210
|
+
else if (pullRequest === null) refusals.push('no-merged-pull-request')
|
|
211
|
+
|
|
212
|
+
if (!status.readable) refusals.push('unreadable-worktree')
|
|
213
|
+
else if (status.dirty) refusals.push('uncommitted-changes')
|
|
214
|
+
|
|
215
|
+
if (held.length > 0) refusals.push('held-by-session')
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
path: entry.path,
|
|
219
|
+
branch: entry.branch,
|
|
220
|
+
reclaimable: refusals.length === 0,
|
|
221
|
+
refusals,
|
|
222
|
+
pullRequest,
|
|
223
|
+
sessions: held,
|
|
224
|
+
// No removal shape reaches the main worktree, and reporting one there
|
|
225
|
+
// offers a command whose only effect is to break the checkout. Deciding it
|
|
226
|
+
// here rather than in the reporter keeps the record and the framed output
|
|
227
|
+
// answering the same way, since the two consumers act on different halves.
|
|
228
|
+
route: isMain ? null : held.length > 0 ? 'session' : 'worktree',
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Reports which worktrees are reclaimable and which are not, with the reason on
|
|
234
|
+
* each.
|
|
235
|
+
*
|
|
236
|
+
* Reclaimable means all three of a merged pull request, a clean working tree,
|
|
237
|
+
* and no live session holding the directory. Each alone has a case where
|
|
238
|
+
* removal loses something, and removal is unrecoverable here since a worktree
|
|
239
|
+
* is gitignored and no history stands behind it.
|
|
240
|
+
*
|
|
241
|
+
* The pull request is what decides the first condition rather than git
|
|
242
|
+
* ancestry. A repository that squash merges never makes a merged branch an
|
|
243
|
+
* ancestor of its trunk, so the ancestry reading calls shipped work unmerged
|
|
244
|
+
* and calls an abandoned branch sitting at a release commit merged, which is
|
|
245
|
+
* wrong in the one direction that removes a directory.
|
|
246
|
+
*
|
|
247
|
+
* An unreadable input refuses the whole reading rather than producing verdicts
|
|
248
|
+
* around it. An absent merge state and a branch with no merged pull request
|
|
249
|
+
* produce the same empty answer, as do an absent session roster and a worktree
|
|
250
|
+
* nobody holds, and reporting the second when it was the first is a false clean
|
|
251
|
+
* that ends in a removal.
|
|
252
|
+
*/
|
|
253
|
+
export async function reclaimReport(
|
|
254
|
+
opts: ReclaimOptions = {},
|
|
255
|
+
): Promise<ReclaimReport> {
|
|
256
|
+
const cwd = opts.cwd ?? process.cwd()
|
|
257
|
+
const listAll = opts.listWorktrees ?? listWorktrees
|
|
258
|
+
const readMerged = opts.mergedPullRequests ?? mergedPullRequests
|
|
259
|
+
const readStatus = opts.worktreeStatus ?? worktreeStatus
|
|
260
|
+
const resolve = opts.resolve ?? resolveSessions
|
|
261
|
+
|
|
262
|
+
const [entries, merged, sessions, repository] = await Promise.all([
|
|
263
|
+
listAll(cwd),
|
|
264
|
+
readMerged(cwd),
|
|
265
|
+
resolve(),
|
|
266
|
+
repositoryOf(cwd),
|
|
267
|
+
])
|
|
268
|
+
|
|
269
|
+
if (merged.kind === 'unreadable') {
|
|
270
|
+
return {
|
|
271
|
+
kind: 'unreadable',
|
|
272
|
+
reason: merged.reason,
|
|
273
|
+
detail: merged.detail,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (sessions.kind !== 'resolved') {
|
|
278
|
+
return {
|
|
279
|
+
kind: 'unreadable',
|
|
280
|
+
reason: 'sessions-unreadable',
|
|
281
|
+
detail: `No session registry at ${sessions.dir}, so nothing was read about which worktrees are still held.`,
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const byBranch = new Map(
|
|
286
|
+
merged.merged.map((request) => [request.branch, request.number]),
|
|
287
|
+
)
|
|
288
|
+
const statuses = await Promise.all(
|
|
289
|
+
entries.map((entry) => readStatus(entry.path)),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
// `git worktree list` puts the main worktree first, which is the only signal
|
|
293
|
+
// separating it from a linked one in the porcelain output.
|
|
294
|
+
const worktrees = entries.map((entry, index) =>
|
|
295
|
+
verdict(
|
|
296
|
+
entry,
|
|
297
|
+
index === 0,
|
|
298
|
+
statuses[index] ?? { readable: false, dirty: false },
|
|
299
|
+
byBranch,
|
|
300
|
+
sessions.sessions,
|
|
301
|
+
repository,
|
|
302
|
+
),
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return { kind: 'read', worktrees }
|
|
306
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Architecture reference
|
|
3
|
+
description: Shape and content rules for .claude/ARCHITECTURE.md
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Architecture reference
|
|
7
|
+
|
|
8
|
+
Applies to `.claude/ARCHITECTURE.md`. Describes the system shape and the decisions behind it, not a tutorial, setup guide, or implementation walkthrough. Pair it with `CLAUDE.md`: principles live there, patterns and decisions live here. Update when a decision is made or a risk is resolved.
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
Governs the system-shape document at `.claude/ARCHITECTURE.md`: the overview, the decision entries, and the open risks.
|
|
13
|
+
|
|
14
|
+
Does not govern:
|
|
15
|
+
|
|
16
|
+
- Per-domain structure and narrative: `context.md`
|
|
17
|
+
- Setup commands and install instructions: `readme.md`
|
|
18
|
+
- Product scope, goals, and non-goals: `requirements.md`
|
|
19
|
+
|
|
20
|
+
## What goes in
|
|
21
|
+
|
|
22
|
+
- A high-level overview of how the system is structured and why
|
|
23
|
+
- Key technical decisions as named H3 entries: what was chosen and why over the alternatives, including stack and library choices
|
|
24
|
+
- Risks and open questions still unresolved
|
|
25
|
+
|
|
26
|
+
## What does not go in
|
|
27
|
+
|
|
28
|
+
- How individual functions work line by line. The code carries its own behavior.
|
|
29
|
+
- Full type definitions. They live in code. Reference the shape conceptually if needed.
|
|
30
|
+
|
|
31
|
+
## Sections
|
|
32
|
+
|
|
33
|
+
Use `## Overview`, `## Key technical decisions` with one named H3 per decision, and `## Risks / open questions`. Name each decision and give the reasoning, especially for non-obvious choices. Skip entries where the rationale is self-evident.
|
|
34
|
+
|
|
35
|
+
## Verification anchors
|
|
36
|
+
|
|
37
|
+
A decision's reasoning stays correct while the numbers it cites move. The anchor records what a measured claim was read against, so a reader can tell a number that was checked and held from one nobody has looked at since.
|
|
38
|
+
|
|
39
|
+
- Close a decision entry whose reasoning cites a measured number with a trailing sentence naming the short commit SHA and the ISO date that number was read: `Measured at <short-sha> on <YYYY-MM-DD>.`
|
|
40
|
+
- Anchor on the number alone. A decision citing none takes no anchor whatever its reasoning rests on, because a marker over a claim nobody can re-measure is one no reader can falsify.
|
|
41
|
+
- Anchor a decision when writing it or when amending its reasoning. Leave an entry written before the rule unanchored rather than dating it by blame, which is archaeology for a marker nothing reads back.
|
|
42
|
+
- Read an absent anchor as unchecked rather than as current. On an entry citing no number there is nothing to check. On one citing a number the number is due a read.
|
|
43
|
+
- Do not edit a claim in the pass that first anchors it. The anchor states what the claim was measured against, so changing both at once leaves nothing to check the anchor against.
|
|
44
|
+
- Refresh the anchor whenever the number is re-read, whether or not it moved. A confirmed number and an unread one are the same text without the date.
|
|
45
|
+
|
|
46
|
+
## Length
|
|
47
|
+
|
|
48
|
+
Every session pays for this file before any work starts, so it carries a budget. The budget counts decisions rather than lines, because a bare line total is satisfied by merging paragraph pairs and the merged paragraphs then fail the weight checkpoint in `markdown.md`. A file over budget is carrying too many decisions, not decisions written too long.
|
|
49
|
+
|
|
50
|
+
- Budget six lines per decision entry, being the H3, two paragraphs, and the blank lines separating them.
|
|
51
|
+
- Budget the frame outside `## Key technical decisions` separately, covering the H1, the overview, and the risks. State the number the project takes where it records the budget, since a frame carries no fixed structure to derive one from.
|
|
52
|
+
- Read the ceiling as the frame plus six lines against the decision count, rather than as the total the two multiply out to.
|
|
53
|
+
- Bring an over-budget file back by merging two decisions or retiring one, never by compressing a decision's prose.
|
|
54
|
+
- Yield the budget to the paragraph weight checkpoint when the two disagree. A paragraph past the checkpoint is a defect no budget licenses.
|
|
55
|
+
|
|
56
|
+
## Template
|
|
57
|
+
|
|
58
|
+
The anchor sentence closes a decision whose reasoning cites a measured number and is absent from one that cites none.
|
|
59
|
+
|
|
60
|
+
```markdown
|
|
61
|
+
# Architecture
|
|
62
|
+
|
|
63
|
+
## Overview
|
|
64
|
+
|
|
65
|
+
## Key technical decisions
|
|
66
|
+
|
|
67
|
+
### Decision name
|
|
68
|
+
|
|
69
|
+
Reasoning and tradeoffs, carrying the measured number the choice rested on. Measured at <short-sha> on <YYYY-MM-DD>.
|
|
70
|
+
|
|
71
|
+
## Risks / open questions
|
|
72
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Branch reference
|
|
3
|
+
description: Branch naming format and type conventions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Branch reference
|
|
7
|
+
|
|
8
|
+
## Scope
|
|
9
|
+
|
|
10
|
+
Governs a git branch name: its structure, its length, and the type vocabulary it draws from.
|
|
11
|
+
|
|
12
|
+
Does not govern:
|
|
13
|
+
|
|
14
|
+
- Commit subject format, which shares the type vocabulary: `commit.md`
|
|
15
|
+
- Pull request title and body: `pr.md`
|
|
16
|
+
- Whether a phase label may appear in a branch name: `versioning.md`
|
|
17
|
+
- Deriving a slug from a branch name for use in an output filename: `slug.md`
|
|
18
|
+
|
|
19
|
+
## Format
|
|
20
|
+
|
|
21
|
+
- Structure: `<type>/<description>` or `<type>/<ticket>-<description>`
|
|
22
|
+
- Length: 50 characters maximum
|
|
23
|
+
- Casing: kebab-case only, no underscores or camelCase
|
|
24
|
+
- Description: 2 words maximum, 3 only when genuinely needed for specificity
|
|
25
|
+
- Capture the core change, not the commit message verbatim
|
|
26
|
+
- For branches with multiple commits, use the unifying concern as the description.
|
|
27
|
+
- Do not duplicate type in description (e.g., `feat/feature-login`)
|
|
28
|
+
|
|
29
|
+
## Types
|
|
30
|
+
|
|
31
|
+
- `feat`: new feature or capability
|
|
32
|
+
- `fix`: bug fix
|
|
33
|
+
- `refactor`: structural changes (not a fix or feature)
|
|
34
|
+
- `docs`: documentation only (README)
|
|
35
|
+
- `chore`: maintenance tasks (deps, tooling, configs)
|
|
36
|
+
- `perf`: performance improvements
|
|
37
|
+
- `test`: add or modify tests
|
|
38
|
+
- `style`: code formatting (whitespace, semicolons)
|
|
39
|
+
- `build`: build system changes (webpack, npm scripts)
|
|
40
|
+
- `ci`: CI/CD pipeline changes (GitHub Actions)
|
|
41
|
+
- `revert`: revert a previous commit
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
|
|
45
|
+
### Correct
|
|
46
|
+
|
|
47
|
+
```plaintext
|
|
48
|
+
feat/jwt-expiration # clear feature scope
|
|
49
|
+
fix/AUTH-123-connection-pool # includes ticket ID
|
|
50
|
+
refactor/remove-deprecated-endpoints # clear refactor intent
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Incorrect
|
|
54
|
+
|
|
55
|
+
```plaintext
|
|
56
|
+
feature/auth_stuff # wrong type + underscore
|
|
57
|
+
feat/feature-add-login # duplicates type in description
|
|
58
|
+
fix/DB-456-fix-the-database-connection-pool-memory-leak # exceeds 50 chars + verbatim message
|
|
59
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Commit reference
|
|
3
|
+
description: Commit message format and type conventions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Commit message reference
|
|
7
|
+
|
|
8
|
+
## Scope
|
|
9
|
+
|
|
10
|
+
Governs a git commit message: subject structure, the type and scope vocabulary, and the body.
|
|
11
|
+
|
|
12
|
+
Does not govern:
|
|
13
|
+
|
|
14
|
+
- Branch naming, which shares the type vocabulary: `branch.md`
|
|
15
|
+
- Pull request title and body, which share the subject form: `pr.md`
|
|
16
|
+
- Whether a phase label or a semver tag may appear in a subject: `versioning.md`
|
|
17
|
+
|
|
18
|
+
## Format
|
|
19
|
+
|
|
20
|
+
- Structure: `<type>(<scope>): <subject>`
|
|
21
|
+
- Casing: lowercase for `<type>`, `<scope>`, and first word of `<subject>`
|
|
22
|
+
- Subject: 72 characters maximum, no trailing period
|
|
23
|
+
|
|
24
|
+
## Types
|
|
25
|
+
|
|
26
|
+
- `feat`: new feature or capability
|
|
27
|
+
- `fix`: bug fix
|
|
28
|
+
- `refactor`: structural changes (not a fix or feature)
|
|
29
|
+
- `docs`: documentation only (README)
|
|
30
|
+
- `chore`: maintenance tasks (deps, tooling, configs)
|
|
31
|
+
- `perf`: performance improvements
|
|
32
|
+
- `test`: add or modify tests
|
|
33
|
+
- `style`: code formatting (whitespace, semicolons)
|
|
34
|
+
- `build`: build system changes (webpack, npm scripts)
|
|
35
|
+
- `ci`: CI/CD pipeline changes (GitHub Actions)
|
|
36
|
+
- `revert`: revert a previous commit
|
|
37
|
+
|
|
38
|
+
## Scope vocabulary
|
|
39
|
+
|
|
40
|
+
- Single lowercase word representing a system component
|
|
41
|
+
- Prefer single word
|
|
42
|
+
- Use kebab-case only when two words are genuinely needed for specificity
|
|
43
|
+
- Do not use specific filenames as scopes
|
|
44
|
+
- Do not use a scope that duplicates the type
|
|
45
|
+
- Write scopes for release readability. They surface in `changelogithub` release notes.
|
|
46
|
+
|
|
47
|
+
## Subject
|
|
48
|
+
|
|
49
|
+
- Use imperative mood (`add` not `added`)
|
|
50
|
+
- Describe the actual technical change, not that something changed
|
|
51
|
+
- Do not use vague verbs (`improve`, `refine`, `enhance`)
|
|
52
|
+
- Do not repeat the scope in the subject line
|
|
53
|
+
- Use single quotes if quoting
|
|
54
|
+
- No backslash escaping or internal double quotes
|
|
55
|
+
- No conversational filler or introductory phrases
|
|
56
|
+
|
|
57
|
+
## Examples
|
|
58
|
+
|
|
59
|
+
### Correct
|
|
60
|
+
|
|
61
|
+
```plaintext
|
|
62
|
+
feat(api): add retry logic for failed webhooks # specific verb + clear change
|
|
63
|
+
fix(auth): update 'UserSession' validation logic # scoped + imperative + single quotes
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Incorrect
|
|
67
|
+
|
|
68
|
+
```plaintext
|
|
69
|
+
fix(user-auth): Fixed the redirect loop. # wrong casing + period + multi-word scope
|
|
70
|
+
docs(docs): update the readme. # duplicate scope + period
|
|
71
|
+
docs(api): improve documentation # vague verb + lacks specificity
|
|
72
|
+
```
|