From 718c79af702a099dae1d2a28d28b93c5eef673ca Mon Sep 17 00:00:00 2001 From: Defame1297 Date: Mon, 14 Sep 2026 16:59:42 +0000 Subject: [PATCH] chore: drop the flat content mirror and native install support (ADR-0024) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apm becomes the only supported install path. The flat mirror at each plugin root existed solely so Claude Code's native `claude plugin install` could convention-scan plugin content (ADR-0017). With no native consumers, it cost ~20,000 tracked lines plus ~2,100 lines of sync tooling and ~88s of every push to guard content apm never reads — and its only automated gate, `claude plugin validate --strict`, passes on a plugin with zero content, so it could not detect the defect ADR-0017 was created to fix. Removes the mirror (213 files), the six per-plugin manifest pairs, sync-plugin-content.sh, its 1,289-line test, the orphaned marketplace-plugins.sh, and the check-plugin-content-sync and validate-plugins pre-push hooks. The root `marketplace:` block and .claude-plugin/ catalogue stay: apm's own marketplace consumers read that same file, so `@holocron` short names keep working. tests/run-bats.sh now excludes .claude/skills/. apm installs from .apm/, which carries the tests/ dirs the mirror stripped, so deployed .bats files would otherwise be discovered and double-run. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD --- .gitignore | 4 +- .pre-commit-config.yaml | 33 +- AGENTS.md | 4 - CONTEXT.md | 24 +- README.md | 15 +- ...nt-mirror-bridges-apm-to-host-discovery.md | 12 + ...-apm-is-the-only-supported-install-path.md | 213 ++ docs/spec/architecture.md | 15 +- docs/spec/gates.md | 23 +- plugins/bin/.claude-plugin/plugin.json | 30 - plugins/bin/.github/plugin/plugin.json | 21 - plugins/bin/skills/caveman/SKILL.md | 50 - plugins/bin/skills/diagnose/SKILL.md | 91 - .../diagnose/assets/hitl-loop.template.sh | 41 - .../diagnose/references/feedback-loops.md | 40 - .../diagnose/references/regression-seams.md | 24 - plugins/bin/skills/grill-me/SKILL.md | 16 - plugins/bin/skills/grill-with-docs/SKILL.md | 93 - .../grill-with-docs/references/adr-format.md | 47 - .../references/context-format.md | 77 - .../improve-codebase-architecture/SKILL.md | 80 - .../references/deepening.md | 37 - .../references/interface-design.md | 44 - .../references/language.md | 53 - plugins/bin/skills/prototype/SKILL.md | 38 - .../bin/skills/prototype/references/logic.md | 79 - plugins/bin/skills/prototype/references/ui.md | 112 -- plugins/bin/skills/research/SKILL.md | 79 - .../skills/research/references/file-format.md | 39 - .../bin/skills/research/references/topics.md | 17 - plugins/bin/skills/tdd/SKILL.md | 114 -- .../bin/skills/tdd/references/deep-modules.md | 33 - .../skills/tdd/references/interface-design.md | 31 - plugins/bin/skills/tdd/references/mocking.md | 59 - .../bin/skills/tdd/references/refactoring.md | 10 - plugins/bin/skills/tdd/references/tests.md | 61 - plugins/bin/skills/triage/SKILL.md | 108 - .../skills/triage/references/agent-brief.md | 168 -- .../skills/triage/references/out-of-scope.md | 101 - plugins/bin/skills/write-docs/SKILL.md | 109 -- plugins/bin/skills/zoom-out/SKILL.md | 9 - plugins/core/.claude-plugin/plugin.json | 20 - plugins/core/.github/plugin/plugin.json | 20 - plugins/core/skills/agentsmd-audit/SKILL.md | 46 - .../agentsmd-audit/references/sources.md | 33 - .../skills/agentsmd-audit/scripts/README.md | 11 - .../agentsmd-audit/scripts/validate-drift.sh | 137 -- .../scripts/validate-secrets.sh | 120 -- .../scripts/validate-structure.sh | 118 -- plugins/core/skills/agentsmd-author/SKILL.md | 43 - .../references/content-guide.md | 118 -- .../agentsmd-author/references/sources.md | 25 - .../skills/provider-adapter-author/SKILL.md | 52 - .../references/provider-matrix.md | 32 - .../references/sources.md | 9 - .../provider-adapter-author/scripts/README.md | 28 - .../scripts/validate-adapter.sh | 496 ----- plugins/git/.claude-plugin/plugin.json | 19 - plugins/git/.github/plugin/plugin.json | 19 - plugins/git/agents/git-orchestrate.agent.md | 88 - .../docs/pre-commit/hooks-reference.md | 1 - plugins/git/skills/git-branches/SKILL.md | 76 - .../references/branch-operations.md | 59 - .../references/branch-patterns.md | 31 - .../references/comparing-branches.md | 16 - .../skills/git-branches/references/merging.md | 31 - .../references/orchestrator-contract.md | 20 - .../skills/git-branches/references/sources.md | 54 - plugins/git/skills/git-commits/SKILL.md | 76 - .../git-commits/references/cherry-pick.md | 12 - .../git-commits/references/commit-template.md | 66 - .../references/conventional-commits-spec.md | 170 -- .../git-commits/references/create-commit.md | 17 - .../git-commits/references/rewrite-history.md | 75 - .../skills/git-commits/references/sources.md | 40 - plugins/git/skills/git-history/SKILL.md | 66 - .../skills/git-history/references/bisect.md | 68 - .../git-history/references/git-log-format.md | 242 --- .../skills/git-history/references/sources.md | 31 - plugins/git/skills/git-remotes/SKILL.md | 60 - .../skills/git-remotes/references/fetch.md | 31 - .../git/skills/git-remotes/references/pull.md | 43 - .../git/skills/git-remotes/references/push.md | 69 - .../git-remotes/references/remote-config.md | 39 - .../skills/git-remotes/references/sources.md | 74 - plugins/git/skills/git-submodules/SKILL.md | 66 - .../git-submodules/references/removal.md | 32 - .../references/setup-and-update.md | 96 - .../git-submodules/references/sources.md | 31 - .../references/urls-and-config.md | 79 - plugins/git/skills/git-workflow/SKILL.md | 80 - .../git-workflow/references/hard-rules.md | 25 - .../skills/git-workflow/references/sources.md | 60 - plugins/git/skills/git-worktrees/SKILL.md | 68 - .../git-worktrees/references/sources.md | 16 - .../git-worktrees/references/worktrees.md | 178 -- plugins/git/skills/pc-author/SKILL.md | 38 - .../pc-author/references/create-config.md | 31 - .../pc-author/references/hooks-by-language.md | 92 - .../pc-author/references/modify-config.md | 75 - .../skills/pc-author/references/sources.md | 33 - plugins/git/skills/pc-run/SKILL.md | 62 - .../skills/pc-run/references/autoupdate.md | 17 - plugins/git/skills/pc-run/references/clean.md | 17 - .../pc-run/references/failure-patterns.md | 109 -- .../git/skills/pc-run/references/install.md | 31 - .../git/skills/pc-run/references/sources.md | 33 - plugins/gitea/.claude-plugin/plugin.json | 21 - plugins/gitea/.github/plugin/plugin.json | 21 - .../gitea/agents/gitea-orchestrate.agent.md | 96 - plugins/gitea/skills/gitea-branches/SKILL.md | 60 - .../gitea-branches/references/branches.md | 107 - .../gitea-branches/references/commits.md | 74 - .../gitea-branches/references/sources.md | 41 - plugins/gitea/skills/gitea-files/SKILL.md | 47 - .../skills/gitea-files/references/reading.md | 49 - .../skills/gitea-files/references/sources.md | 48 - .../skills/gitea-files/references/writing.md | 79 - plugins/gitea/skills/gitea-issues/SKILL.md | 74 - .../gitea-issues/references/enrichments.md | 91 - .../skills/gitea-issues/references/issues.md | 136 -- .../skills/gitea-issues/references/search.md | 39 - .../skills/gitea-issues/references/sources.md | 42 - .../skills/gitea-labels-milestones/SKILL.md | 63 - .../references/label-inference.md | 81 - .../references/labels.md | 122 -- .../references/milestones.md | 98 - .../references/sources.md | 33 - plugins/gitea/skills/gitea-prs/SKILL.md | 60 - .../skills/gitea-prs/references/merging.md | 41 - .../gitea-prs/references/pull-requests.md | 84 - .../skills/gitea-prs/references/reviews.md | 54 - .../skills/gitea-prs/references/sources.md | 33 - plugins/gitea/skills/gitea-releases/SKILL.md | 73 - .../references/call-signatures.md | 75 - .../gitea-releases/references/conventions.md | 60 - .../gitea-releases/references/sources.md | 47 - plugins/gitea/skills/gitea-workflow/SKILL.md | 43 - .../references/number-resolution.md | 21 - .../gitea-workflow/references/skill-index.md | 22 - .../gitea-workflow/references/sources.md | 33 - .../references/status-checkin.md | 15 - .../skills/agent-author/scripts/README.md | 2 +- plugins/kyberforge/.claude-plugin/plugin.json | 20 - plugins/kyberforge/.github/plugin/plugin.json | 20 - .../agents/apm-orchestrate.agent.md | 69 - plugins/kyberforge/hooks/hooks.json | 16 - .../kyberforge/skills/agent-audit/SKILL.md | 90 - .../skills/agent-audit/assets/vale/.vale.ini | 7 - .../styles/Kyberforge/CompositionNote.yml | 13 - .../styles/Kyberforge/DescriptionOpener.yml | 7 - .../vale/styles/Kyberforge/PaddingPhrase.yml | 7 - .../Kyberforge/SentenceOpenerThereIs.yml | 7 - .../vale/styles/Kyberforge/VagueWording.yml | 10 - .../KyberforgeCopilot/ProactivePhrase.yml | 7 - .../references/body-and-delegation.md | 105 - .../references/description-quality.md | 96 - .../agent-audit/references/field-inventory.md | 49 - .../references/finding-criteria.md | 98 - .../references/scope-plugin-apm.md | 58 - .../references/scope-project-user.md | 59 - .../skills/agent-audit/references/sources.md | 90 - .../references/validation-scripts.md | 76 - .../skills/agent-audit/scripts/README.md | 47 - .../skills/agent-audit/scripts/vale-wrap.sh | 526 ----- .../scripts/validate-provenance.sh | 632 ------ .../skills/agent-audit/scripts/validate.sh | 1738 ----------------- .../kyberforge/skills/agent-author/SKILL.md | 65 - .../skills/agent-author/assets/README.md | 10 - .../assets/templates/apm-agent.md | 92 - .../assets/templates/claude-code.md | 113 -- .../templates/copilot.agent.md.template | 81 - .../agent-author/references/contract.md | 163 -- .../skills/agent-author/references/create.md | 93 - .../references/deployment-modes.md | 60 - .../skills/agent-author/references/improve.md | 88 - .../agent-author/references/plugin-scope.md | 74 - .../references/project-user-scope.md | 111 -- .../skills/agent-author/references/scripts.md | 43 - .../skills/agent-author/references/sources.md | 95 - .../skills/agent-author/scripts/README.md | 13 - .../skills/agent-author/scripts/new-agent.sh | 303 --- .../kyberforge/skills/apm-install/SKILL.md | 53 - .../skills/apm-install/references/sources.md | 17 - .../kyberforge/skills/apm-workflow/SKILL.md | 34 - .../skills/apm-workflow/references/audit.md | 59 - .../skills/apm-workflow/references/compile.md | 67 - .../apm-workflow/references/configure.md | 88 - .../skills/apm-workflow/references/install.md | 22 - .../apm-workflow/references/marketplace.md | 110 -- .../skills/apm-workflow/references/sources.md | 41 - plugins/kyberforge/skills/forge/SKILL.md | 54 - .../skills/forge/references/apm-routes.md | 42 - .../skills/forge/references/author-routes.md | 44 - .../skills/forge/references/sources.md | 81 - .../skills/forge/references/version-bump.md | 40 - .../kyberforge/skills/skill-audit/SKILL.md | 87 - .../skills/skill-audit/assets/vale/.vale.ini | 4 - .../styles/Kyberforge/CompositionNote.yml | 13 - .../styles/Kyberforge/DescriptionOpener.yml | 7 - .../vale/styles/Kyberforge/PaddingPhrase.yml | 7 - .../Kyberforge/SentenceOpenerThereIs.yml | 7 - .../vale/styles/Kyberforge/VagueWording.yml | 10 - .../skill-audit/references/body-discipline.md | 209 -- .../references/description-quality.md | 87 - .../skill-audit/references/file-structure.md | 69 - .../references/finding-criteria.md | 136 -- .../references/formatting-and-scripts.md | 48 - .../skills/skill-audit/references/patterns.md | 54 - .../skills/skill-audit/references/sources.md | 59 - .../references/validation-scripts.md | 150 -- .../skills/skill-audit/scripts/vale-wrap.sh | 526 ----- .../scripts/validate-provenance.sh | 1198 ------------ .../skills/skill-audit/scripts/validate.sh | 1677 ---------------- .../kyberforge/skills/skill-author/SKILL.md | 62 - .../skill-author/assets/templates/SKILL.md | 135 -- .../assets/templates/assets/README.md | 29 - .../assets/templates/references/sources.md | 14 - .../assets/templates/scripts/README.md | 47 - .../assets/templates/tests/README.md | 33 - .../skill-author/references/contract.md | 256 --- .../skills/skill-author/references/create.md | 186 -- .../references/deployment-modes.md | 55 - .../skills/skill-author/references/improve.md | 91 - .../skills/skill-author/references/scripts.md | 89 - .../skills/skill-author/references/sources.md | 70 - .../skills/skill-author/scripts/new-skill.sh | 189 -- plugins/lint/.claude-plugin/plugin.json | 19 - plugins/lint/.github/plugin/plugin.json | 19 - plugins/lint/agents/lint-runner.agent.md | 40 - plugins/lint/skills/vale-config/SKILL.md | 63 - .../references/configuration-reference.md | 113 -- .../skills/vale-config/references/sources.md | 17 - plugins/lint/skills/vale-run/SKILL.md | 61 - .../skills/vale-run/references/sources.md | 17 - .../vale-run/references/troubleshooting.md | 191 -- scripts/lib/batch-run.sh | 11 +- scripts/lib/marketplace-plugins.sh | 86 - scripts/sync-plugin-content.sh | 813 -------- tests/run-bats.sh | 15 +- tests/run-tests.sh | 5 +- tests/test-check-vale-style-sync.sh | 3 +- tests/test-run-tests.sh | 12 +- tests/test-sync-plugin-content.sh | 1289 ------------ tests/test-vale-wrap.sh | 10 +- 245 files changed, 298 insertions(+), 22602 deletions(-) create mode 100644 docs/adr/0024-apm-is-the-only-supported-install-path.md delete mode 100644 plugins/bin/.claude-plugin/plugin.json delete mode 100644 plugins/bin/.github/plugin/plugin.json delete mode 100644 plugins/bin/skills/caveman/SKILL.md delete mode 100644 plugins/bin/skills/diagnose/SKILL.md delete mode 100644 plugins/bin/skills/diagnose/assets/hitl-loop.template.sh delete mode 100644 plugins/bin/skills/diagnose/references/feedback-loops.md delete mode 100644 plugins/bin/skills/diagnose/references/regression-seams.md delete mode 100644 plugins/bin/skills/grill-me/SKILL.md delete mode 100644 plugins/bin/skills/grill-with-docs/SKILL.md delete mode 100644 plugins/bin/skills/grill-with-docs/references/adr-format.md delete mode 100644 plugins/bin/skills/grill-with-docs/references/context-format.md delete mode 100644 plugins/bin/skills/improve-codebase-architecture/SKILL.md delete mode 100644 plugins/bin/skills/improve-codebase-architecture/references/deepening.md delete mode 100644 plugins/bin/skills/improve-codebase-architecture/references/interface-design.md delete mode 100644 plugins/bin/skills/improve-codebase-architecture/references/language.md delete mode 100644 plugins/bin/skills/prototype/SKILL.md delete mode 100644 plugins/bin/skills/prototype/references/logic.md delete mode 100644 plugins/bin/skills/prototype/references/ui.md delete mode 100644 plugins/bin/skills/research/SKILL.md delete mode 100644 plugins/bin/skills/research/references/file-format.md delete mode 100644 plugins/bin/skills/research/references/topics.md delete mode 100644 plugins/bin/skills/tdd/SKILL.md delete mode 100644 plugins/bin/skills/tdd/references/deep-modules.md delete mode 100644 plugins/bin/skills/tdd/references/interface-design.md delete mode 100644 plugins/bin/skills/tdd/references/mocking.md delete mode 100644 plugins/bin/skills/tdd/references/refactoring.md delete mode 100644 plugins/bin/skills/tdd/references/tests.md delete mode 100644 plugins/bin/skills/triage/SKILL.md delete mode 100644 plugins/bin/skills/triage/references/agent-brief.md delete mode 100644 plugins/bin/skills/triage/references/out-of-scope.md delete mode 100644 plugins/bin/skills/write-docs/SKILL.md delete mode 100644 plugins/bin/skills/zoom-out/SKILL.md delete mode 100644 plugins/core/.claude-plugin/plugin.json delete mode 100644 plugins/core/.github/plugin/plugin.json delete mode 100644 plugins/core/skills/agentsmd-audit/SKILL.md delete mode 100644 plugins/core/skills/agentsmd-audit/references/sources.md delete mode 100644 plugins/core/skills/agentsmd-audit/scripts/README.md delete mode 100755 plugins/core/skills/agentsmd-audit/scripts/validate-drift.sh delete mode 100755 plugins/core/skills/agentsmd-audit/scripts/validate-secrets.sh delete mode 100755 plugins/core/skills/agentsmd-audit/scripts/validate-structure.sh delete mode 100644 plugins/core/skills/agentsmd-author/SKILL.md delete mode 100644 plugins/core/skills/agentsmd-author/references/content-guide.md delete mode 100644 plugins/core/skills/agentsmd-author/references/sources.md delete mode 100644 plugins/core/skills/provider-adapter-author/SKILL.md delete mode 100644 plugins/core/skills/provider-adapter-author/references/provider-matrix.md delete mode 100644 plugins/core/skills/provider-adapter-author/references/sources.md delete mode 100644 plugins/core/skills/provider-adapter-author/scripts/README.md delete mode 100755 plugins/core/skills/provider-adapter-author/scripts/validate-adapter.sh delete mode 100644 plugins/git/.claude-plugin/plugin.json delete mode 100644 plugins/git/.github/plugin/plugin.json delete mode 100644 plugins/git/agents/git-orchestrate.agent.md delete mode 100644 plugins/git/skills/git-branches/SKILL.md delete mode 100644 plugins/git/skills/git-branches/references/branch-operations.md delete mode 100644 plugins/git/skills/git-branches/references/branch-patterns.md delete mode 100644 plugins/git/skills/git-branches/references/comparing-branches.md delete mode 100644 plugins/git/skills/git-branches/references/merging.md delete mode 100644 plugins/git/skills/git-branches/references/orchestrator-contract.md delete mode 100644 plugins/git/skills/git-branches/references/sources.md delete mode 100644 plugins/git/skills/git-commits/SKILL.md delete mode 100644 plugins/git/skills/git-commits/references/cherry-pick.md delete mode 100644 plugins/git/skills/git-commits/references/commit-template.md delete mode 100644 plugins/git/skills/git-commits/references/conventional-commits-spec.md delete mode 100644 plugins/git/skills/git-commits/references/create-commit.md delete mode 100644 plugins/git/skills/git-commits/references/rewrite-history.md delete mode 100644 plugins/git/skills/git-commits/references/sources.md delete mode 100644 plugins/git/skills/git-history/SKILL.md delete mode 100644 plugins/git/skills/git-history/references/bisect.md delete mode 100644 plugins/git/skills/git-history/references/git-log-format.md delete mode 100644 plugins/git/skills/git-history/references/sources.md delete mode 100644 plugins/git/skills/git-remotes/SKILL.md delete mode 100644 plugins/git/skills/git-remotes/references/fetch.md delete mode 100644 plugins/git/skills/git-remotes/references/pull.md delete mode 100644 plugins/git/skills/git-remotes/references/push.md delete mode 100644 plugins/git/skills/git-remotes/references/remote-config.md delete mode 100644 plugins/git/skills/git-remotes/references/sources.md delete mode 100644 plugins/git/skills/git-submodules/SKILL.md delete mode 100644 plugins/git/skills/git-submodules/references/removal.md delete mode 100644 plugins/git/skills/git-submodules/references/setup-and-update.md delete mode 100644 plugins/git/skills/git-submodules/references/sources.md delete mode 100644 plugins/git/skills/git-submodules/references/urls-and-config.md delete mode 100644 plugins/git/skills/git-workflow/SKILL.md delete mode 100644 plugins/git/skills/git-workflow/references/hard-rules.md delete mode 100644 plugins/git/skills/git-workflow/references/sources.md delete mode 100644 plugins/git/skills/git-worktrees/SKILL.md delete mode 100644 plugins/git/skills/git-worktrees/references/sources.md delete mode 100644 plugins/git/skills/git-worktrees/references/worktrees.md delete mode 100644 plugins/git/skills/pc-author/SKILL.md delete mode 100644 plugins/git/skills/pc-author/references/create-config.md delete mode 100644 plugins/git/skills/pc-author/references/hooks-by-language.md delete mode 100644 plugins/git/skills/pc-author/references/modify-config.md delete mode 100644 plugins/git/skills/pc-author/references/sources.md delete mode 100644 plugins/git/skills/pc-run/SKILL.md delete mode 100644 plugins/git/skills/pc-run/references/autoupdate.md delete mode 100644 plugins/git/skills/pc-run/references/clean.md delete mode 100644 plugins/git/skills/pc-run/references/failure-patterns.md delete mode 100644 plugins/git/skills/pc-run/references/install.md delete mode 100644 plugins/git/skills/pc-run/references/sources.md delete mode 100644 plugins/gitea/.claude-plugin/plugin.json delete mode 100644 plugins/gitea/.github/plugin/plugin.json delete mode 100644 plugins/gitea/agents/gitea-orchestrate.agent.md delete mode 100644 plugins/gitea/skills/gitea-branches/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-branches/references/branches.md delete mode 100644 plugins/gitea/skills/gitea-branches/references/commits.md delete mode 100644 plugins/gitea/skills/gitea-branches/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-files/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-files/references/reading.md delete mode 100644 plugins/gitea/skills/gitea-files/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-files/references/writing.md delete mode 100644 plugins/gitea/skills/gitea-issues/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-issues/references/enrichments.md delete mode 100644 plugins/gitea/skills/gitea-issues/references/issues.md delete mode 100644 plugins/gitea/skills/gitea-issues/references/search.md delete mode 100644 plugins/gitea/skills/gitea-issues/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-labels-milestones/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-labels-milestones/references/label-inference.md delete mode 100644 plugins/gitea/skills/gitea-labels-milestones/references/labels.md delete mode 100644 plugins/gitea/skills/gitea-labels-milestones/references/milestones.md delete mode 100644 plugins/gitea/skills/gitea-labels-milestones/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-prs/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-prs/references/merging.md delete mode 100644 plugins/gitea/skills/gitea-prs/references/pull-requests.md delete mode 100644 plugins/gitea/skills/gitea-prs/references/reviews.md delete mode 100644 plugins/gitea/skills/gitea-prs/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-releases/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-releases/references/call-signatures.md delete mode 100644 plugins/gitea/skills/gitea-releases/references/conventions.md delete mode 100644 plugins/gitea/skills/gitea-releases/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-workflow/SKILL.md delete mode 100644 plugins/gitea/skills/gitea-workflow/references/number-resolution.md delete mode 100644 plugins/gitea/skills/gitea-workflow/references/skill-index.md delete mode 100644 plugins/gitea/skills/gitea-workflow/references/sources.md delete mode 100644 plugins/gitea/skills/gitea-workflow/references/status-checkin.md delete mode 100644 plugins/kyberforge/.claude-plugin/plugin.json delete mode 100644 plugins/kyberforge/.github/plugin/plugin.json delete mode 100644 plugins/kyberforge/agents/apm-orchestrate.agent.md delete mode 100644 plugins/kyberforge/hooks/hooks.json delete mode 100644 plugins/kyberforge/skills/agent-audit/SKILL.md delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/.vale.ini delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/styles/Kyberforge/CompositionNote.yml delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/styles/Kyberforge/VagueWording.yml delete mode 100644 plugins/kyberforge/skills/agent-audit/assets/vale/styles/KyberforgeCopilot/ProactivePhrase.yml delete mode 100644 plugins/kyberforge/skills/agent-audit/references/body-and-delegation.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/description-quality.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/field-inventory.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/finding-criteria.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/scope-plugin-apm.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/scope-project-user.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/sources.md delete mode 100644 plugins/kyberforge/skills/agent-audit/references/validation-scripts.md delete mode 100644 plugins/kyberforge/skills/agent-audit/scripts/README.md delete mode 100755 plugins/kyberforge/skills/agent-audit/scripts/vale-wrap.sh delete mode 100755 plugins/kyberforge/skills/agent-audit/scripts/validate-provenance.sh delete mode 100755 plugins/kyberforge/skills/agent-audit/scripts/validate.sh delete mode 100644 plugins/kyberforge/skills/agent-author/SKILL.md delete mode 100644 plugins/kyberforge/skills/agent-author/assets/README.md delete mode 100644 plugins/kyberforge/skills/agent-author/assets/templates/apm-agent.md delete mode 100644 plugins/kyberforge/skills/agent-author/assets/templates/claude-code.md delete mode 100644 plugins/kyberforge/skills/agent-author/assets/templates/copilot.agent.md.template delete mode 100644 plugins/kyberforge/skills/agent-author/references/contract.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/create.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/deployment-modes.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/improve.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/plugin-scope.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/project-user-scope.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/scripts.md delete mode 100644 plugins/kyberforge/skills/agent-author/references/sources.md delete mode 100644 plugins/kyberforge/skills/agent-author/scripts/README.md delete mode 100755 plugins/kyberforge/skills/agent-author/scripts/new-agent.sh delete mode 100644 plugins/kyberforge/skills/apm-install/SKILL.md delete mode 100644 plugins/kyberforge/skills/apm-install/references/sources.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/SKILL.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/references/audit.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/references/compile.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/references/configure.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/references/install.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/references/marketplace.md delete mode 100644 plugins/kyberforge/skills/apm-workflow/references/sources.md delete mode 100644 plugins/kyberforge/skills/forge/SKILL.md delete mode 100644 plugins/kyberforge/skills/forge/references/apm-routes.md delete mode 100644 plugins/kyberforge/skills/forge/references/author-routes.md delete mode 100644 plugins/kyberforge/skills/forge/references/sources.md delete mode 100644 plugins/kyberforge/skills/forge/references/version-bump.md delete mode 100644 plugins/kyberforge/skills/skill-audit/SKILL.md delete mode 100644 plugins/kyberforge/skills/skill-audit/assets/vale/.vale.ini delete mode 100644 plugins/kyberforge/skills/skill-audit/assets/vale/styles/Kyberforge/CompositionNote.yml delete mode 100644 plugins/kyberforge/skills/skill-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml delete mode 100644 plugins/kyberforge/skills/skill-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml delete mode 100644 plugins/kyberforge/skills/skill-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml delete mode 100644 plugins/kyberforge/skills/skill-audit/assets/vale/styles/Kyberforge/VagueWording.yml delete mode 100644 plugins/kyberforge/skills/skill-audit/references/body-discipline.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/description-quality.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/file-structure.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/finding-criteria.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/formatting-and-scripts.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/patterns.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/sources.md delete mode 100644 plugins/kyberforge/skills/skill-audit/references/validation-scripts.md delete mode 100755 plugins/kyberforge/skills/skill-audit/scripts/vale-wrap.sh delete mode 100755 plugins/kyberforge/skills/skill-audit/scripts/validate-provenance.sh delete mode 100755 plugins/kyberforge/skills/skill-audit/scripts/validate.sh delete mode 100644 plugins/kyberforge/skills/skill-author/SKILL.md delete mode 100644 plugins/kyberforge/skills/skill-author/assets/templates/SKILL.md delete mode 100644 plugins/kyberforge/skills/skill-author/assets/templates/assets/README.md delete mode 100644 plugins/kyberforge/skills/skill-author/assets/templates/references/sources.md delete mode 100644 plugins/kyberforge/skills/skill-author/assets/templates/scripts/README.md delete mode 100644 plugins/kyberforge/skills/skill-author/assets/templates/tests/README.md delete mode 100644 plugins/kyberforge/skills/skill-author/references/contract.md delete mode 100644 plugins/kyberforge/skills/skill-author/references/create.md delete mode 100644 plugins/kyberforge/skills/skill-author/references/deployment-modes.md delete mode 100644 plugins/kyberforge/skills/skill-author/references/improve.md delete mode 100644 plugins/kyberforge/skills/skill-author/references/scripts.md delete mode 100644 plugins/kyberforge/skills/skill-author/references/sources.md delete mode 100755 plugins/kyberforge/skills/skill-author/scripts/new-skill.sh delete mode 100644 plugins/lint/.claude-plugin/plugin.json delete mode 100644 plugins/lint/.github/plugin/plugin.json delete mode 100644 plugins/lint/agents/lint-runner.agent.md delete mode 100644 plugins/lint/skills/vale-config/SKILL.md delete mode 100644 plugins/lint/skills/vale-config/references/configuration-reference.md delete mode 100644 plugins/lint/skills/vale-config/references/sources.md delete mode 100644 plugins/lint/skills/vale-run/SKILL.md delete mode 100644 plugins/lint/skills/vale-run/references/sources.md delete mode 100644 plugins/lint/skills/vale-run/references/troubleshooting.md delete mode 100644 scripts/lib/marketplace-plugins.sh delete mode 100755 scripts/sync-plugin-content.sh delete mode 100755 tests/test-sync-plugin-content.sh diff --git a/.gitignore b/.gitignore index 0a023db..59066f5 100644 --- a/.gitignore +++ b/.gitignore @@ -47,8 +47,8 @@ build/ # `apm pack`'s manifest for the *root* package. Emitted beside the marketplace # manifest by a bare `apm pack`, and never tracked on any branch — the repo's -# own paths hide it, since sync-plugin-content.sh redirects `apm pack -o` to a -# scratch tree and the apm-pack-check-clean pre-push hook runs --dry-run. Scoped +# own paths hide it, since the apm-pack-check-clean pre-push hook, the only +# thing that runs pack here, passes --dry-run. Scoped # to the file, not the directory: the sibling .claude-plugin/marketplace.json is # compiled output that IS committed and must stay tracked. /.claude-plugin/plugin.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c6af7e8..3dc8388 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,11 +32,14 @@ repos: # root-anchored and spells that path out. This was five `(^|/)` # any-depth alternatives plus one `^` root-only one -- a mixture with no # rationale, under which a fixture or vendored tree containing - # `.../.claude-plugin/plugin.json` would have been silently excluded from - # formatting while an equivalent `.../.agents/plugins/marketplace.json` - # would not. All fifteen real files (3 root marketplace manifests, 2 per - # plugin x 6 plugins) match; anything else is hand-authored and gets - # formatted. + # `.../.claude-plugin/marketplace.json` would have been silently excluded + # from formatting while an equivalent + # `.../.agents/plugins/marketplace.json` would not. The three root + # marketplace manifests match; anything else is hand-authored and gets + # formatted. The twelve per-plugin `plugin.json` alternatives were + # dropped with the plugin manifests themselves when native + # `claude plugin install` support was removed -- apm probes `apm.yml` + # and never reached them. # # `.claude/settings.json` is the sixteenth, and it is excluded for a # different reason: apm OWNS that file (ADR-0018, ADR-0019), and @@ -50,7 +53,7 @@ repos: # happened when the SessionStart hook first landed in 2e395a4. # Re-running `apm install` fixes the file; leaving it in scope here # would re-break it on the very commit that carries the fix. - exclude: '^(\.claude-plugin/marketplace\.json|\.agents/plugins/marketplace\.json|\.github/plugin/marketplace\.json|plugins/[^/]+/\.claude-plugin/plugin\.json|plugins/[^/]+/\.github/plugin/plugin\.json|\.claude/settings\.json)$' + exclude: '^(\.claude-plugin/marketplace\.json|\.agents/plugins/marketplace\.json|\.github/plugin/marketplace\.json|\.claude/settings\.json)$' - id: check-yaml stages: ['pre-commit'] - id: trailing-whitespace @@ -75,15 +78,6 @@ repos: pass_filenames: false always_run: true - - id: check-plugin-content-sync - name: Check plugin content sync - description: Verify each plugin's flat skills/agents/commands/hooks/hooks.json mirror is in sync with .apm/ -- Claude Code has no .apm/ awareness so this compiled mirror must stay current (see issue #90) - entry: bash scripts/sync-plugin-content.sh --check --all - language: system - stages: [pre-push] - pass_filenames: false - always_run: true - - id: check-executables-allow-sync name: Check executables allow key sync description: Verify root apm.yml's executables.allow key names kyberforge's actual version -- apm matches that key by exact "#" lookup, so a version bump on one side alone silently stops deploying kyberforge's hooks/ and bin/ and lets the apm install go stale (see ADR-0019) @@ -202,15 +196,6 @@ repos: pass_filenames: false always_run: true - - id: validate-plugins - name: Validate plugins - description: Run claude plugin validate --strict on every plugin directory - entry: bash -c 'for d in plugins/*/; do claude plugin validate --strict "$d" || exit 1; done' - language: system - stages: [pre-push] - pass_filenames: false - always_run: true - - id: validate-marketplace name: Validate marketplace manifest description: Run claude plugin validate --strict on the root marketplace manifest diff --git a/AGENTS.md b/AGENTS.md index ec9afa5..b0375f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,10 +9,6 @@ This file carries only what applies to **every** session. Setup, prerequisites, - `plugins/` — six installable plugin units, each an apm package (`apm.yml` + `.apm/`). Root `apm.yml` declares all six as `dependencies.apm`; `apm install` deploys them into `.claude/skills/` and `.claude/agents/`, both gitignored install output. - `providers/claude-code/` — Claude Code adapter, deployed to `~/.claude/` via `scripts/install.sh`. -## Edit `.apm/`, never the flat mirror - -`plugins//.apm/` is the only hand-edited source for plugin content — the generated flat mirror and manifests are discarded on the next sync and reported as drift by `check-plugin-content-sync`. What's exempt (`README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`) and why: `docs/spec/architecture.md`. - ## Prefer plugin skills over raw shell This repo dogfoods its own plugins. Before shelling out, check whether a skill already owns the operation — it usually does: diff --git a/CONTEXT.md b/CONTEXT.md index 3ebd3f7..0f4eb93 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -50,21 +50,17 @@ A reusable slash command defined as a `SKILL.md` file following the `plugins//.apm/skills//SKILL.md`. _Avoid_: command, prompt, macro -**Plugin**: -The deployable unit — one or more skills, agents, hooks, commands, and MCP servers bundled into a -single installable directory under `plugins//`, compiled from that plugin's `.apm/` source. -_Avoid_: package, bundle, module - **apm package**: -The unit apm builds and installs — `plugins//apm.yml` plus the hand-authored -`plugins//.apm/` tree it compiles from (ADR-0015). -_Avoid_: plugin directory, source tree +The deployable unit apm builds and installs — one or more skills, agents, hooks, commands, and MCP +servers under a single directory `plugins//`, consisting of that plugin's `apm.yml` plus the +hand-authored `plugins//.apm/` tree it deploys from (ADR-0015). +_Avoid_: plugin, package, bundle, module, plugin directory, source tree **Output profile**: -An `apm pack` target format for a generated *marketplace* manifest; apm has `claude` -(`.claude-plugin/marketplace.json`) and `codex` (the differently-shaped -`.agents/plugins/marketplace.json`), and none for `.github/plugin/marketplace.json` (Copilot CLI's -legacy path), which a sync script mirrors instead. Mechanics: `docs/spec/architecture.md`. +An `apm pack` target format for the generated *marketplace* catalogue compiled from root +`apm.yml`'s `marketplace:` block. Only `claude` is declared +(`.claude-plugin/marketplace.json`); apm's own marketplace consumers read that same file, so one +catalogue serves both. Mechanics: `docs/spec/architecture.md`. _Avoid_: build target, export format **Plugin marketplace**: @@ -135,8 +131,8 @@ _Avoid_: ticket, card, task ## Relationships -- A **Plugin** bundles one or more **Skills** and agents; a **Plugin marketplace** lists **Plugins**; - **holocron** is this repo wearing that hat. +- An **apm package** bundles one or more **Skills** and agents; a **Plugin marketplace** lists + **apm packages**; **holocron** is this repo wearing that hat. - **AGENTS.md** is the source of always-on rules; a **Thin adapter** imports it and originates nothing. - **Skill composition** is the caller/callee split. `forge` routes a genuinely *undecided* artifact diff --git a/README.md b/README.md index 3a1c9a5..6c5fcc6 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,10 @@ Install all of these before setting up. Each one is a hard dependency of a git h | Tool | Why | Install | | --- | --- | --- | -| `apm` CLI | Three pre-push hooks shell out to it (`apm-audit-ci`, `apm-pack-check-clean`, and `check-plugin-content-sync` via `scripts/sync-plugin-content.sh`) | The `apm-install` skill, or `curl -sSL https://aka.ms/apm-unix \| sh`. Verify with `apm --version` | -| `jq` | Required by `scripts/sync-plugin-content.sh`, pre-push | Your package manager | +| `apm` CLI | Two pre-push hooks shell out to it (`apm-audit-ci` and `apm-pack-check-clean`) | The `apm-install` skill, or `curl -sSL https://aka.ms/apm-unix \| sh`. Verify with `apm --version` | | `python3` + PyYAML | Required by `scripts/skill-size-check.sh` (the `skill-size-check` pre-commit hook), which reads folded YAML frontmatter | `python3` is usually present — pre-commit is itself a Python application. `pip install pyyaml` if the hook reports PyYAML missing | | `vale` | Required by the `vale-audit-prefilter-skill` / `-agent` pre-commit hooks and the `check-vale-style-sync` pre-push hook | `brew install vale` (macOS), `snap install vale` (Linux), `choco install vale` (Windows), or https://vale.sh/docs/vale-cli/installation/ | -| `claude` CLI | Required by the `validate-plugins` and `validate-marketplace` pre-push hooks | Claude Code | +| `claude` CLI | Required by the `validate-marketplace` pre-push hook | Claude Code | Two notes worth reading before you skip one: @@ -96,19 +95,13 @@ See [`docs/spec/gates.md`](docs/spec/gates.md) for what each hook enforces and w ## Editing plugin content -`plugins//.apm/` is the only hand-edited source for plugin content — the flat mirror directories and every `plugin.json` / `marketplace.json` manifest are generated, discarded by the next sync, and reported as drift by `check-plugin-content-sync` if hand-edited instead. Hand-authored material that is not an `.apm/` primitive (`README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`) lives at the plugin root instead and is untouched by the sync. +`plugins//.apm/` is the only hand-edited source for plugin content — the root `marketplace.json` manifest is generated by `apm pack`, and a hand-edit there is reported as drift by `apm-pack-check-clean`. Hand-authored material that is not an `.apm/` primitive (`README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`) lives at the plugin root instead. Full model, including what's exempt and why: [`docs/spec/architecture.md`](docs/spec/architecture.md). ## For external consumers -Install a plugin natively from the marketplace manifests: - -```bash -claude plugin install @holocron -``` - -Or consume the packages through apm, the way this repo does — declare them as `dependencies.apm` git+path entries against the holocron remote and run `apm install`. +Consume the packages through apm, the way this repo does — declare them as `dependencies.apm` git+path entries against the holocron remote and run `apm install`. apm is the only supported install path. ## Where to go next diff --git a/docs/adr/0017-plugin-content-mirror-bridges-apm-to-host-discovery.md b/docs/adr/0017-plugin-content-mirror-bridges-apm-to-host-discovery.md index 35b8c2d..2c16a85 100644 --- a/docs/adr/0017-plugin-content-mirror-bridges-apm-to-host-discovery.md +++ b/docs/adr/0017-plugin-content-mirror-bridges-apm-to-host-discovery.md @@ -1,5 +1,17 @@ # Plugin roots gain a compiled flat-directory mirror of `.apm/` content so Claude Code can discover it +**Superseded by:** ADR-0024 (apm is the only supported install path; the flat content mirror is +deleted). The mirror this ADR created — `plugins//{skills,agents,hooks}/` — has been deleted, +along with `scripts/sync-plugin-content.sh`, its test suite, and the `check-plugin-content-sync` +pre-push gate. Native `claude plugin install` is no longer a supported path, so the host discovery +contract this ADR bridged is no longer one this repo satisfies. The diagnosis below is still +accurate about how Claude Code's installer works; what changed is that nothing consumes it. The +`mcpServers`, `hooks`-pointer and `hooks/hooks.json` amendments below are moot with the artifacts +they governed; the symlink amendment's underlying gap is not — see ADR-0024's consequences. This +ADR's content is kept below as the historical record; it is no longer the current model. + +--- + This ADR is a follow-on correction to ADR-0015 (Microsoft APM replaces hand-authored plugin/marketplace authoring), discovered during issue #90's post-execution review. It does not restate ADR-0015's rationale for adopting `.apm/` as the authoring source of truth — see that ADR diff --git a/docs/adr/0024-apm-is-the-only-supported-install-path.md b/docs/adr/0024-apm-is-the-only-supported-install-path.md new file mode 100644 index 0000000..1f22f5b --- /dev/null +++ b/docs/adr/0024-apm-is-the-only-supported-install-path.md @@ -0,0 +1,213 @@ +# apm is the only supported install path; the flat content mirror is deleted + +**Supersedes ADR-0017** (plugin roots gain a compiled flat-directory mirror of `.apm/` content so +Claude Code can discover it). ADR-0017's diagnosis was correct and is not in dispute: Claude Code's +native installer convention-scans flat `skills/`/`agents/`/`hooks/` directories at the plugin root +and has no model of `.apm/` at all, so without a mirror a natively-installed holocron plugin reports +`Skills (0) Agents (0) Hooks (0)`. What changes here is not the mechanism but the premise — that the +native install path is worth supporting. It is not, because nobody uses it. + +**Status: accepted (2026-09-14).** The mirror, its generator, its test suite, its helper library and +its pre-push gate are removed. `.apm/` remains the sole hand-edited authoring source, unchanged from +ADR-0015. The root `marketplace:` block in `apm.yml` and the compiled +`.claude-plugin/marketplace.json` it produces are **kept** — see "Also delete the marketplace +catalogue" under considered options. + +## Context + +ADR-0018 moved this repo's own consumption of its own plugins onto `apm install`. From that point +the flat mirror had no consumer inside this repo: it existed entirely for a hypothetical third party +running `claude plugin install @holocron`. No such consumer has ever been observed. The +marketplace is on a private Gitea instance, and the repo has no telemetry, no issue traffic and no +external clone record suggesting otherwise. The honest statement is that the native path has been +maintained for an audience of zero. + +What that audience costs is measurable: + +| Artifact | Size | +|---|---| +| Tracked mirror files under `plugins/*/{skills,agents,hooks}/` | 213 files, ~20,000 lines | +| `scripts/sync-plugin-content.sh` | 813 lines | +| `tests/test-sync-plugin-content.sh` | 1,291 lines, 92 cases, ~83 seconds | +| `scripts/lib/marketplace-plugins.sh` | helper, used only by the above | +| `check-plugin-content-sync` pre-push hook | ~5 seconds per push | + +Roughly 22,000 lines of tracked content and tooling, and about 88 seconds on every push. The test +alone is close to 30% of `run-tests`' wall time — the single largest item in it. + +**The native path's automated gate does not gate anything.** ADR-0017 cites +`claude plugin validate --strict` passing on all six plugins as one of two verifications. That +verification was re-run this session against a plugin directory with **every content directory +deleted**, and it passed. `validate` reads the manifest; it never inspects content. It therefore +cannot detect the exact `Skills (0) Agents (0) Hooks (0)` defect ADR-0017 was written to fix. The +other half of ADR-0017's verification — the live behavioral test +(`claude --plugin-dir plugins/kyberforge -p "list your skills and agents"`) — is a manual step, +run by hand once in August 2026 and never since. So native-install correctness has been unguarded +for a month, and the drift gate that runs on every push guards only that the mirror matches `.apm/`, +not that the mirror works. + +**Dropping native install does not reduce host coverage.** This is the fact that makes the decision +cheap rather than a trade. apm's skills convergence deploys skills to `.agents/skills//SKILL.md`, +the shared path read by Copilot, Cursor, Codex, Gemini, OpenCode and Windsurf, with Claude Code as +the special case at `.claude/skills/`. The mirror served two hosts (Claude Code and Copilot, the +latter only ever partially — see ADR-0017's own `hooks` amendment). apm serves seven. A consumer who +installs holocron through apm gets strictly more than one who installed it natively. + +**Verified empirically, not reasoned about.** In a scratch clone with the mirror and the six +per-plugin manifest pairs deleted: + +- `apm marketplace add` still registers all 6 packages. It detects + `.claude-plugin/marketplace.json` and reads the catalogue from there. +- `apm install` still deploys 40 `SKILL.md` files across 39 skill directories, 4 agents, and + kyberforge's `SessionStart` hook — identical to the baseline install from the unmodified tree. +- `apm pack --check-versions --check-clean --dry-run` exits 0 ("Version alignment OK", + "Marketplace working tree clean"), because it governs only the **root** `.claude-plugin/` outputs. + The six per-plugin `plugin.json` pairs were never apm-pack-governed: they were generated by + `apm pack --format plugin` invoked from inside `sync-plugin-content.sh`, so deleting the script + deletes their producer and nothing is left asserting they should exist. + +**Source-level proof the per-plugin manifests are droppable.** +`apm_cli/deps/github_downloader_validation.py` probes package markers in a fixed order — +`apm.yml`, then `SKILL.md`, then `plugin.json`, then `.github/plugin/plugin.json`, then +`.claude-plugin/plugin.json` — and returns on the first hit. Every plugin here keeps its `apm.yml`, +which is the first probe, so no `plugin.json` path is ever reached. The per-plugin manifests are not +load-bearing for apm resolution; they were load-bearing only for the native installer. + +## Decision + +**apm is the only supported install path.** Concretely: + +- Delete the flat mirror at every plugin root (`plugins//skills/`, `agents/`, `hooks/`) and + the six per-plugin manifest pairs (`.claude-plugin/plugin.json`, `.github/plugin/plugin.json`). +- Delete `scripts/sync-plugin-content.sh`, `tests/test-sync-plugin-content.sh`, + `scripts/lib/marketplace-plugins.sh`, and the `check-plugin-content-sync` pre-push hook. +- **Keep** the root `marketplace:` block in `apm.yml` and the compiled + `.claude-plugin/marketplace.json`. apm's own marketplace consumers read that same file; removing + it would stop holocron being an apm marketplace at all. + +The asymmetry between those last two bullets is the whole subtlety of this ADR, and it exists +because apm deliberately reuses Claude Code's catalogue format rather than inventing one. The +catalogue is shared between the two ecosystems; the per-plugin content contract is not. Deleting the +content is what ends native support; keeping the catalogue is what preserves apm support. + +## Considered options + +**Status quo — keep mirroring on every branch (rejected).** Pays ~22,000 tracked lines and ~88 +seconds per push for a path with no users and no working gate. It is not free in author attention +either: ADR-0017 accrued four amendments in two days, every one of them about a detail of the +mirroring mechanism rather than about the content being mirrored. + +**Generate the mirror only at release, from a tag or a release branch (rejected).** Technically +supported, and it is worth recording *why* it was rejected rather than leaving it to look like an +oversight. Claude Code marketplace entries accept ref-pinned git sources, and apm already emits that +exact shape: the `mattpocock-skills` entry removed from root `apm.yml` on 2026-09-13 compiled to +`{"source": "github", "repo": ..., "ref": "v1.2.3", "sha": ..., "tag_pattern": "v{version}"}` — a +`git-subdir` source pinned to a ref. So a release-only mirror would install correctly. + +Rejected on three grounds, stacking: + +1. It keeps the 813-line script and the 1,291-line test alive in full. It reduces how often they + run, not how much there is to maintain — and the maintenance, not the runtime, is what ADR-0017's + amendment history shows to be the real cost. +2. It requires per-package tagging discipline this repo does not practise. `git tag` lists + repo-level tags (`v1.0.0`, `v2.0.0`, `v2.0.1`) matching no package version under the + `per_package` versioning mode the six plugins use. The tagging convention that would make + ref-pinning meaningful would have to be invented first. +3. **There is no CI in this repo at all.** Every gate here is a git hook on a developer's machine. + A release-time regeneration step would therefore depend on a human remembering to run it, and its + failure mode is silent: a release tag whose tree contains a stale or absent mirror installs + natively and reports zero skills, which is precisely the ADR-0017 defect, reintroduced on the + release path where it is hardest to notice. + +**Also delete the marketplace catalogue (proposed, then rejected on evidence).** The initial shape +of this decision deleted `.claude-plugin/marketplace.json` along with everything else, on the +reasoning that it is a Claude Code artifact. That is wrong. `apm marketplace add` reads +`.claude-plugin/marketplace.json` — falling back to `.github/plugin/marketplace.json`, which this +repo already deleted — and that read is what makes holocron an apm marketplace and what gives +consumers the `@holocron` short-name form. Deleting it would have broken apm consumers in +order to remove a file whose format Claude Code merely happens to share. + +**Declare holocron an apm marketplace as a new step (moot).** Considered as a follow-on to the +above, and found to be already done: the `marketplace:` block in root `apm.yml` *is* the +declaration, and `apm marketplace init` produces exactly that block. There is nothing to add. + +## Consequences + +**1. Native `claude plugin install` no longer works, and the failure is silent.** This is accepted, +not overlooked. Because apm reuses Claude Code's catalogue format by design — an APM-based +marketplace stays consumable by Claude Code's existing marketplace mechanism — a Claude Code user +can still register holocron natively, and will then install six plugins containing zero skills, +zero agents and zero hooks. No error is raised at any point; the manifests are valid and the +directories are simply empty. There is no schema change available that would prevent this, because +the catalogue format cannot express "this marketplace is not for you" — the compatibility is +structural, and it is the same compatibility that makes keeping the catalogue correct for apm. A +README note is the only available mitigation, and a note is not a gate. + +**2. Consumers now receive dev-fixture files.** apm installs from `.apm/`, and `.apm/` contains the +per-skill `tests/` directories the mirror explicitly stripped (ADR-0017's depth-scoped +`//tests` exclusion). 17 `.bats` files across 6 skills therefore now deploy into +every consumer's skill directories. Suppressing them would mean switching all six `apm.yml` files +from `includes: auto` to explicit include lists — and an explicit list that is wrong silently drops +content, which is the same failure class ADR-0017 was written to fix. Trading a cosmetic problem for +a correctness problem is a bad trade, so this is deferred deliberately rather than fixed in passing. + +**3. `tests/run-bats.sh` must exclude `.claude/skills/`** from both its `find` walk and the +`git ls-files` set-equality check that derives the expected test list. Deployed `.bats` files are +now discoverable in the install output and would otherwise be found and double-run against a root +they do not belong to — exactly the `apm_modules/` problem ADR-0018 recorded, arriving by a second +route. Any future script that walks this repo's tree needs both exclusions. + +**4. No version bumps.** ADR-0006 patch-bumps a plugin when its shipped content changes. Nothing +under `.apm/` is touched here; only compiled artifacts are removed. The shipped content is +byte-identical, so no bump is owed. This also avoids triggering the `executables.allow` +`kyberforge#` pin cascade ADR-0019 describes, which would otherwise turn a cleanup into a +multi-file coordinated edit for no functional gain. + +**5. Reintroduction recipe.** This is the insurance that made the decision acceptable, so it is +stated concretely rather than left as "it's in git". To restore native install support: recover +`scripts/sync-plugin-content.sh` from git history (`git log --diff-filter=D -- scripts/sync-plugin-content.sh` +finds the deleting commit; `git show ^:scripts/sync-plugin-content.sh` recovers it) and re-run +it with `--all`; it regenerates both the mirror and the per-plugin manifest pairs, because +`apm pack --format plugin` produces them together. Separately, apm resolves a marketplace at a git +ref — default `main`, with `--ref` pinning — so a consumer who pins an older ref still gets a tree +containing the mirror and is unaffected until they move forward. + +**6. A negative result, pinned so it is not re-litigated: this does not relax the self-containment +constraint.** The natural next thought is that with the native installer gone, the no-cross-skill- +file-sharing rule (the rule that forced ADR-0014's Vale config duplication) could be relaxed, +because that rule was read as a property of Claude Code's plugin cache-install. It is not. +`plugins/kyberforge/.apm/skills/skill-author/references/deployment-modes.md`, sourced from the +agentskills.io spec, states the constraint independently for **APM package mode**: file references +inside `.apm/skills//` must not reach outside that skill's own directory, and the spec defines +no cross-skill sharing mechanism. So cross-skill file sharing remains impossible under the only +install path that survives, and ADR-0014's duplication rationale stands unchanged. + +**7. Three of ADR-0017's four amendments become moot, and one loses its enforcement.** Recorded +because each was a decision someone spent real effort on: + +- The `mcpServers` re-injection amendment (2026-08-14) is moot. Its target was + `.github/plugin/plugin.json`, which no longer exists; `reinject_mcp_servers()` dies with the + script that called it. Its reasoning — a path string, never an inlined object, because inlining + bypasses apm's credential sanitizer — is worth carrying forward as a general rule if per-plugin + Copilot manifests ever return. +- The `hooks`-pointer amendment (2026-08-14) is moot in the same way, and its outcome was to change + nothing, so nothing is lost. +- The `hooks/hooks.json` path-correction amendment (2026-08-14) is moot: there is no mirrored hooks + file to place. +- The symlink amendment (2026-08-14) is **not** moot, and this is the one real regression. + `check_apm_symlinks()` read the `.apm/` source tree directly to report symlinks, because apm's + bundle exporter filters them out silently and the resulting content loss is invisible to any + mirror-versus-mirror diff. That check dies with the script. Whether `apm install`'s own copy path + drops symlinks the same way the bundle exporter does was **not verified this session** — the + exporter's `is_file() and not is_symlink()` test is in `apm_cli/bundle/plugin_exporter.py`, a + different code path from dependency installation. If it does, symlinked content under `.apm/` is + now silently lost with nothing reporting it. No symlink exists under any `.apm/` today, so this is + a latent gap rather than an active one, but it should be re-checked before anyone adds one. + +**8. ADR-0017's own exit condition was different from this one, and that is worth noting.** Its +final consequence anticipated deletion, but conditioned it on an upstream fix: "a future apm release +that ships a native `.apm/`-aware plugin.json compiler ... would let `sync-plugin-content.sh` and +its drift gate be deleted outright." That release has not happened. The mirror is being deleted +because the path it bridges has no users, not because apm closed the gap — the gap is still open, +and a consumer who installs natively still hits it. ADR-0017's anticipated exit remains available +and unclaimed; this ADR takes a different one. diff --git a/docs/spec/architecture.md b/docs/spec/architecture.md index bc473d2..1b8e366 100644 --- a/docs/spec/architecture.md +++ b/docs/spec/architecture.md @@ -19,13 +19,13 @@ project repo (local overrides) - **Executables** (`DEPLOY_EXECUTABLES`): `providers/claude-code/statusline-command.sh` → `~/.claude/statusline-command.sh` (with `+x`) - **Directories** (`DEPLOY_DIRS`): `core/` → `~/.claude/core/` (destination fully replaced on each deploy) -Skills are **not** deployed by `install.sh`. They are distributed as plugins and installed separately — in this repo by `apm install` against the `dependencies.apm` entries in the root `apm.yml`, which lands them in `.claude/skills/` and `.claude/agents/` (ADR-0018); elsewhere by `claude plugin install @holocron`. +Skills are **not** deployed by `install.sh`. They are distributed as plugins and installed separately — in this repo by `apm install` against the `dependencies.apm` entries in the root `apm.yml`, which lands them in `.claude/skills/` and `.claude/agents/` (ADR-0018). A consuming repo installs them the same way — apm is the only supported install path. `~/.claude/CLAUDE.md` is a thin adapter, not a content source. It imports `~/.agents/AGENTS.md` (always-on rules) and `governance.md` (always-on governance) and carries nothing else — the content index of on-demand instruction files sits in `core/AGENTS.md`, deployed to `~/.agents/AGENTS.md` and imported by it. All always-on content lives in `AGENTS.md` files so other providers can import the same source without duplication. ## Plugin model -Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently — via `apm install` here, or `claude plugin install @holocron` for a host consuming the marketplace natively (ADR-0018). Self-contained is a hard constraint, not a description: a plugin is copied to a cache on install, so nothing inside it may reference a file outside its own directory. That is why the Vale styles are duplicated across two skills rather than shared (ADR-0014), and why ADR-0020's constants are copied into three validators rather than sourced from one. Each plugin is an **apm package**: `plugins//apm.yml` plus a hand-authored `plugins//.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no hand-maintained `plugin.json` — every manifest and every host-visible content directory is compiled from that source. +Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently via `apm install`, here and in any consuming repo (ADR-0018). Self-contained is a hard constraint, not a description: a plugin is copied to a cache on install, so nothing inside it may reference a file outside its own directory. That is why the Vale styles are duplicated across two skills rather than shared (ADR-0014), and why ADR-0020's constants are copied into three validators rather than sourced from one. Each plugin is an **apm package**: `plugins//apm.yml` plus a hand-authored `plugins//.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no per-plugin `plugin.json` at all — apm reads `apm.yml`, and the repo's one generated manifest, `.claude-plugin/marketplace.json`, is compiled from that source. Which plugin a new skill belongs in follows from what each one is scoped to. The boundary that matters most in practice is `core` vs `kyberforge`: `core` is the home for cross-cutting, repo-agnostic utility skills that a consumer would want against *their* repo, while `kyberforge` is meta-tooling for the holocron marketplace itself. A skill that authors a target repo's `AGENTS.md` is `core`; a skill that audits a `SKILL.md` against this marketplace's contract is `kyberforge`. @@ -42,14 +42,15 @@ These are routing boundaries, not inventories — they answer "where does a new | `lint` | Configuring and running linters against a target repo; repo-agnostic, first linter is Vale | | `bin` | Unsorted skills that have not earned a home yet | -Two compilers produce the plugin roots you see in the tree: +One compiler produces the generated content in the tree: -- **`apm pack` compiles the manifests** (ADR-0015). Per plugin: `.claude-plugin/plugin.json` and `.github/plugin/plugin.json`, both generated from `plugins//apm.yml`. Repo-wide, from the root `apm.yml`'s `marketplace:` block: `.claude-plugin/marketplace.json` (apm's `claude` output profile) — the only marketplace manifest this repo generates or ships. Copilot CLI checks for a marketplace manifest at several conventional paths, falling back through `.github/plugin/marketplace.json` to `.claude-plugin/marketplace.json` — since this repo already generates the latter, no dedicated Copilot-path mirror is maintained. -- **`scripts/sync-plugin-content.sh` compiles the content mirror** (ADR-0017). It wraps `apm pack --format plugin` and copies the resulting bundle's flat `agents/`, `skills/`, `commands/`, `instructions/`, `extensions/`, and merged `hooks/hooks.json` back to the plugin root. Claude Code's installer convention-scans those flat paths and has no `.apm/` awareness whatsoever, so the mirror exists solely to satisfy the host's discovery contract. +- **`apm pack` compiles the marketplace manifest** (ADR-0015). Repo-wide, from the root `apm.yml`'s `marketplace:` block: `.claude-plugin/marketplace.json` (apm's `claude` output profile) — the only manifest this repo generates or ships. Copilot CLI checks for a marketplace manifest at several conventional paths, falling back through `.github/plugin/marketplace.json` to `.claude-plugin/marketplace.json` — since this repo already generates the latter, no dedicated Copilot-path mirror is maintained. -`.apm/` is the sole hand-edited authoring source for plugin content. An edit made in the flat mirror is discarded by the next sync and is reported as drift by the `check-plugin-content-sync` pre-push hook. Hand-authored material that is not an `.apm/` primitive — `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`, and per-plugin extras such as `plugins/gitea/references/` and `plugins/bin/evals/` — lives at the plugin **root** and is untouched by either compiler. +apm is the only supported install path. A flat `skills/`, `agents/`, `hooks/` mirror used to be compiled to each plugin root so Claude Code's installer could convention-scan it, alongside a per-plugin `.claude-plugin/plugin.json` and `.github/plugin/plugin.json`; both are gone, together with native `claude plugin install` support. apm reads `plugins//apm.yml` and deploys from `.apm/` directly, and never probed those manifests. -That immunity is positional, not by filename. Anything placed *inside* a mirrored directory is destroyed regardless of what it is: `sync_dir` runs `rm -rf "$dst"` before every copy, and `sync_hooks_json` does the same to `hooks/`. A hand-written `README.md` under `plugins//hooks/` or `plugins//skills/` is deleted by the next sync with no drift report, because a file with no `.apm/` counterpart is simply absent from the regenerated tree. This has already cost the repo one document — `plugins/kyberforge/hooks/README.md`, since restored to `plugins/kyberforge/docs/hooks.md`. Plugin-root documentation belongs in `docs/`. +`.apm/` is the sole hand-edited authoring source for plugin content. Hand-authored material that is not an `.apm/` primitive — `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`, and per-plugin extras such as `plugins/gitea/references/` and `plugins/bin/evals/` — lives at the plugin **root**. A hand-edit to the generated `.claude-plugin/marketplace.json` is reported as drift by the `apm-pack-check-clean` pre-push hook. + +Plugin-root documentation belongs in `docs/`. That convention is older than the mirror's removal: a hand-written `README.md` placed inside a mirrored directory used to be destroyed by the next sync with no drift report, which cost the repo one document — `plugins/kyberforge/hooks/README.md`, since restored to `plugins/kyberforge/docs/hooks.md`. ## Governance layer diff --git a/docs/spec/gates.md b/docs/spec/gates.md index a9d1640..a4db3d9 100644 --- a/docs/spec/gates.md +++ b/docs/spec/gates.md @@ -21,24 +21,24 @@ Install hooks via `pc-run`, wiring **all three stages**. This repo's `.pre-commi `default_install_hook_types`, so a plain install silently skips `commit-msg` (Conventional Commits) and `pre-push` (everything below). -The pre-push command reports **13** hooks, not 11. The extra two are pre-commit's own `meta` hooks, +The pre-push command reports **11** hooks, not 9. The extra two are pre-commit's own `meta` hooks, `check-hooks-apply` and `check-useless-excludes`: they declare no `stages:`, so they run at every stage including this one. Both are declared in this repo's `.pre-commit-config.yaml` like everything -else — what separates them is `repo: meta` (pre-commit's own built-ins) from `repo: local`. Eleven +else — what separates them is `repo: meta` (pre-commit's own built-ins) from `repo: local`. Nine is the count of hooks this repo authors itself. -**The caveat: one of those 11 is a silent no-op under that invocation.** +**The caveat: one of those 9 is a silent no-op under that invocation.** `check-release-needed` exits 0 immediately unless `PRE_COMMIT_REMOTE_BRANCH` equals `refs/heads/main`, and pre-commit exports that variable only from the real pre-push git hook during an actual `git push`. Running the stage by hand — or from a CI runner — therefore reports it `Passed` having checked nothing. That is by design for feature branches — pushing WIP must not be blocked on cutting a premature tag — but it means `--hook-stage pre-push --all-files` is a full -rehearsal of 10 hooks and a skip of the eleventh. The script's own header records the same gap for +rehearsal of 8 hooks and a skip of the ninth. The script's own header records the same gap for a PR merged through Gitea's merge button, where no local push happens at all. ## The pre-push gate -Eleven hooks, grouped below by what they guard rather than by the order `.pre-commit-config.yaml` declares them in. +Ten hooks, grouped below by what they guard rather than by the order `.pre-commit-config.yaml` declares them in. **Core checks** @@ -50,7 +50,6 @@ Eleven hooks, grouped below by what they guard rather than by the order `.pre-co | Hook | Guards | |---|---| -| `check-plugin-content-sync` | each plugin's flat `skills/agents/commands/hooks` mirror matches `.apm/` (issue #90) | | `check-vale-style-sync` | skill-audit's Vale copy matches agent-audit's canonical copy, plus six glob-coverage probes (see [Vale](#vale)) | | `check-scope-walkup-sync` | `validate.sh`, `validate-provenance.sh`, `new-agent.sh` and `new-skill.sh`'s four independent `$HOME`/`.git`/`apm.yml` walk-up ports still agree behaviorally | | `check-executables-allow-sync` | root `apm.yml`'s `executables.allow` key names kyberforge's actual version (see [apm gates](#apm-gates)) | @@ -75,7 +74,6 @@ drift in generated text. | Hook | Guards | |---|---| -| `validate-plugins` | `claude plugin validate --strict` on every plugin directory | | `validate-marketplace` | `claude plugin validate --strict` on the root marketplace manifest | **Release** @@ -84,13 +82,10 @@ drift in generated text. |---|---| | `check-release-needed` | on a real `git push` to `main` only — fails if files exposed via `.pre-commit-hooks.yaml` changed since the last tag. A no-op everywhere else, including under `pre-commit run --hook-stage pre-push` (see [the caveat above](#running-the-gates)) | -Three of these shell out to `apm`: `apm-audit-ci`, `apm-pack-check-clean`, and -`check-plugin-content-sync` (via `scripts/sync-plugin-content.sh`, which wraps `apm pack`). The -second is a bare `apm …` entry and the first is a `bash -c` loop calling `apm` once per package, so -without the CLI the push dies with an unhelpful "command not found". Install with -`apm-install`, or `curl -sSL https://aka.ms/apm-unix | sh`; verify with `apm --version`. `jq` is -needed by `scripts/sync-plugin-content.sh` — it at least fails loudly (`Error: jq is required but -not installed`). +Two of these shell out to `apm`: `apm-audit-ci` and `apm-pack-check-clean`. The second is a bare +`apm …` entry and the first is a `bash -c` loop calling `apm` once per package, so without the CLI +the push dies with an unhelpful "command not found". Install with `apm-install`, or +`curl -sSL https://aka.ms/apm-unix | sh`; verify with `apm --version`. ## Skill and agent context gates (ADR-0020) diff --git a/plugins/bin/.claude-plugin/plugin.json b/plugins/bin/.claude-plugin/plugin.json deleted file mode 100644 index ce4a195..0000000 --- a/plugins/bin/.claude-plugin/plugin.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "bin", - "version": "1.1.7", - "description": "Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.", - "author": { - "name": "Defame1297", - "email": "defame1297@rkdr.net", - "url": "https://git.dev.rkdr.net/Defame1297/" - }, - "license": "MIT", - "homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin", - "repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin", - "keywords": [ - "utility", - "diagnostics", - "prototyping", - "tdd", - "research" - ], - "mcpServers": { - "obsidian": { - "args": [ - "@bitbonsai/mcpvault@0.15.0", - "docs/" - ], - "command": "npx", - "type": "stdio" - } - } -} diff --git a/plugins/bin/.github/plugin/plugin.json b/plugins/bin/.github/plugin/plugin.json deleted file mode 100644 index bb83d28..0000000 --- a/plugins/bin/.github/plugin/plugin.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "bin", - "version": "1.1.7", - "description": "Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.", - "author": { - "name": "Defame1297", - "email": "defame1297@rkdr.net", - "url": "https://git.dev.rkdr.net/Defame1297/" - }, - "license": "MIT", - "homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin", - "repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin", - "keywords": [ - "utility", - "diagnostics", - "prototyping", - "tdd", - "research" - ], - "mcpServers": ".mcp.json" -} diff --git a/plugins/bin/skills/caveman/SKILL.md b/plugins/bin/skills/caveman/SKILL.md deleted file mode 100644 index 542810f..0000000 --- a/plugins/bin/skills/caveman/SKILL.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -name: caveman -disable-model-invocation: true -description: > - Ultra-compressed output mode that drops articles, filler and pleasantries while - keeping technical substance exact, cutting token usage by roughly 75%. -metadata: - version: "1.0.0" ---- - -Respond terse like smart caveman. All technical substance stay. Only fluff die. - -## Persistence - -ACTIVE EVERY RESPONSE once user type `/caveman`. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode". - -## Rules - -Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Abbreviate common terms (DB/auth/config/req/res/fn/impl). Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough. - -Technical terms stay exact. Code blocks unchanged. Errors quoted exact. - -Pattern: `[thing] [action] [reason]. [next step].` - -Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..." -Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:" - -### Examples - -**"Why React component re-render?"** - -> Inline obj prop -> new ref -> re-render. `useMemo`. - -**"Explain database connection pooling."** - -> Pool = reuse DB conn. Skip handshake -> fast under load. - -## Auto-Clarity Exception - -Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done. - -Example -- destructive op: - -> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone. -> -> ```sql -> DROP TABLE users; -> ``` -> -> Caveman resume. Verify backup exist first. diff --git a/plugins/bin/skills/diagnose/SKILL.md b/plugins/bin/skills/diagnose/SKILL.md deleted file mode 100644 index 38d8f4a..0000000 --- a/plugins/bin/skills/diagnose/SKILL.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -name: diagnose -description: > - Use when the user says "diagnose this" or "debug this", reports something - broken, throwing, or failing, or says something got slow. Not filing or - triaging a reported bug -> `triage`. Not test-first feature work -> `tdd`. -metadata: - version: "1.0.1" ---- - -# Diagnose - -A discipline for hard bugs. Skip phases only when explicitly justified. - -When exploring the codebase, use the domain glossary for a clear mental model of the relevant modules, and check ADRs in the area. - -## Phase 1 — Build a feedback loop - -**This is the skill.** Everything else is mechanical. If you have a fast, deterministic, agent-runnable pass/fail signal for the bug, you will find the cause — bisection, hypothesis-testing, and instrumentation all just consume that signal. If you don't have one, no amount of staring at code will save you. - -Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.** - -**If you do not yet have such a signal, read `references/feedback-loops.md`** — ten ways to build one ordered by cost, and what to ask the user for when the bug resists reproduction entirely. - -**If you do have one, it is probably not sharp enough yet.** Make it faster and more deterministic, and make it assert on the exact symptom rather than "didn't crash" — a 30-second flaky loop is barely better than no loop. If it stays slow or intermittent after that, read that file's "Iterate on the loop itself" and "Intermittent bugs" sections. - -Do not proceed to Phase 2 until you have a loop you believe in. If you cannot build one, stop and say so explicitly, listing what you tried — never hypothesise without a signal. - -## Phase 2 — Reproduce - -Run the loop. Watch the bug appear. - -Confirm: - -- [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix. -- [ ] The failure is reproducible across multiple runs. If it is intermittent, `references/feedback-loops.md` defines the rate high enough to debug against — go back to Phase 1 and raise it. -- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it. - -Do not proceed until you reproduce the bug. - -## Phase 3 — Hypothesise - -Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea. - -Each hypothesis must be **falsifiable**: state the prediction it makes. - -> Format: "If is the cause, then will make the bug disappear / will make it worse." - -If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it. - -**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK. - -## Phase 4 — Instrument - -Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.** - -Tool preference: - -1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs. -2. **Targeted logs** at the boundaries that distinguish hypotheses. -3. Never "log everything and grep". - -**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die. - -**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second. - -## Phase 5 — Fix + regression test - -Write the regression test **before the fix** — but only at a **correct seam**: one where the test exercises the real bug pattern as it occurs at the call site. If the available seam looks too shallow, or you cannot tell whether it is, read `references/regression-seams.md`. - -**If no correct seam exists, that itself is the finding.** Note it and carry it into Phase 6 — the architecture is preventing the bug from being locked down. - -At a correct seam: - -1. Turn the Phase 1 loop into a failing test at that seam, narrowed to the symptom captured in Phase 2. -2. Watch it fail. -3. Apply the fix. -4. Watch it pass. -5. Re-run the Phase 1 feedback loop against the original, un-narrowed scenario. - -## Phase 6 — Cleanup + post-mortem - -Required before declaring done: - -- [ ] Original repro no longer reproduces (re-run the Phase 1 loop) -- [ ] Regression test passes (or absence of seam is documented) -- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix) -- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location) -- [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns - -**Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started. diff --git a/plugins/bin/skills/diagnose/assets/hitl-loop.template.sh b/plugins/bin/skills/diagnose/assets/hitl-loop.template.sh deleted file mode 100644 index 40afc46..0000000 --- a/plugins/bin/skills/diagnose/assets/hitl-loop.template.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# Human-in-the-loop reproduction loop. -# Copy this file, edit the steps below, and run it. -# The agent runs the script; the user follows prompts in their terminal. -# -# Usage: -# bash hitl-loop.template.sh -# -# Two helpers: -# step "" → show instruction, wait for Enter -# capture VAR "" → show question, read response into VAR -# -# At the end, captured values are printed as KEY=VALUE for the agent to parse. - -set -euo pipefail - -step() { - printf '\n>>> %s\n' "$1" - read -r -p " [Enter when done] " _ -} - -capture() { - local var="$1" question="$2" answer - printf '\n>>> %s\n' "$question" - read -r -p " > " answer - printf -v "$var" '%s' "$answer" -} - -# --- edit below --------------------------------------------------------- - -step "Open the app at http://localhost:3000 and sign in." - -capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)" - -capture ERROR_MSG "Paste the error message (or 'none'):" - -# --- edit above --------------------------------------------------------- - -printf '\n--- Captured ---\n' -printf 'ERRORED=%s\n' "$ERRORED" -printf 'ERROR_MSG=%s\n' "$ERROR_MSG" diff --git a/plugins/bin/skills/diagnose/references/feedback-loops.md b/plugins/bin/skills/diagnose/references/feedback-loops.md deleted file mode 100644 index 8804543..0000000 --- a/plugins/bin/skills/diagnose/references/feedback-loops.md +++ /dev/null @@ -1,40 +0,0 @@ -# Constructing and sharpening a feedback loop - -A feedback loop is a fast, deterministic, agent-runnable pass/fail signal for the bug. Build the right one and the bug is 90% fixed. This file covers the whole arc: building a loop, sharpening one you already have, and escalating when the bug resists reproduction. - -## Ways to construct one — try them in roughly this order - -1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e. -2. **Curl / HTTP script** against a running dev server. -3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot. -4. **Headless browser script** (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network. -5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation. -6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call. -7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode. -8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it. -9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs. -10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `assets/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you. - -## Iterate on the loop itself - -Treat the loop as a product. Once you have _a_ loop, ask: - -- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.) -- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".) -- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.) - -A 30-second flaky loop is barely better than no loop. A 2-second deterministic loop is a debugging superpower. - -## Intermittent bugs — raise the reproduction rate - -If the loop only sometimes fails, the goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable. - -## When you genuinely cannot build a loop - -Stop and say so explicitly. List what you tried. Ask the user for: - -- access to whatever environment reproduces it, -- a captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or -- permission to add temporary production instrumentation. - -Do **not** proceed to hypothesise without a loop. A hypothesis you cannot falsify against a signal is a guess, and the fix that follows it is unverifiable. diff --git a/plugins/bin/skills/diagnose/references/regression-seams.md b/plugins/bin/skills/diagnose/references/regression-seams.md deleted file mode 100644 index 26176a8..0000000 --- a/plugins/bin/skills/diagnose/references/regression-seams.md +++ /dev/null @@ -1,24 +0,0 @@ -# Judging a regression-test seam - -Read this when Phase 5 leaves you unsure whether the seam available for the regression test is the correct one — either because the obvious seam looks shallow, or because there appears to be no seam at all. - -## What makes a seam correct - -A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site: the same entry point, the same participants, the same ordering, and the same state the real caller holds when it goes wrong. - -## Seams that are too shallow - -- A single-caller test when the bug only appears with multiple callers. -- A unit test that cannot replicate the chain of calls that triggered the bug. -- A test that reproduces the symptom by construction — asserting on a value the test itself set — rather than by driving the code path that produces it. -- A test that mocks out the collaborator the bug actually lives in. - -A regression test at a shallow seam gives false confidence. It passes forever, including after a change reintroduces the bug at the real call site, and it will be read by the next maintainer as proof the bug is locked down. - -## When there is no correct seam - -Do not force one, and do not settle for a shallow seam to have something green. Instead: - -1. Apply the fix and verify it against the Phase 1 loop directly. -2. Write down which seams you considered and why each was too shallow. -3. Carry that into Phase 6's "what would have prevented this bug" question. A missing seam is an architecture finding — tangled callers, hidden coupling, or a module with no testable boundary — and the handoff is the `improve-codebase-architecture` skill, with those specifics attached. diff --git a/plugins/bin/skills/grill-me/SKILL.md b/plugins/bin/skills/grill-me/SKILL.md deleted file mode 100644 index 70bb682..0000000 --- a/plugins/bin/skills/grill-me/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: grill-me -description: > - Use when the user says "grill me" or wants a plan or design stress-tested by - relentless interview — one question at a time, down each branch of the - decision tree. Not a plan to challenge against `CONTEXT.md` and ADRs -> - `grill-with-docs`. -metadata: - version: "1.0.0" ---- - -Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer. - -Ask the questions one at a time. - -If a question can be answered by exploring the codebase, explore the codebase instead. diff --git a/plugins/bin/skills/grill-with-docs/SKILL.md b/plugins/bin/skills/grill-with-docs/SKILL.md deleted file mode 100644 index 30df96e..0000000 --- a/plugins/bin/skills/grill-with-docs/SKILL.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -name: grill-with-docs -description: > - Use when a plan should be stress-tested against the project's domain model — - the interview challenges terms against `CONTEXT.md` and writes decisions into - it and into ADRs as they land. Not a plain interview -> `grill-me`. -metadata: - version: "1.0.0" ---- - - - -Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer. - -Ask the questions one at a time, waiting for feedback on each question before continuing. - -If a question can be answered by exploring the codebase, explore the codebase instead. - - - - - -## Domain awareness - -During codebase exploration, also look for existing documentation: - -### File structure - -Most repos have a single context: - -``` -/ -├── CONTEXT.md -├── docs/ -│ └── adr/ -│ ├── 0001-event-sourced-orders.md -│ └── 0002-postgres-for-write-model.md -└── src/ -``` - -If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives: - -``` -/ -├── CONTEXT-MAP.md -├── docs/ -│ └── adr/ ← system-wide decisions -├── src/ -│ ├── ordering/ -│ │ ├── CONTEXT.md -│ │ └── docs/adr/ ← context-specific decisions -│ └── billing/ -│ ├── CONTEXT.md -│ └── docs/adr/ -``` - -Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed. - -## During the session - -### Challenge against the glossary - -When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?" - -### Sharpen fuzzy language - -When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things." - -### Discuss concrete scenarios - -When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts. - -### Cross-reference with code - -When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?" - -### Update CONTEXT.md inline - -When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [context-format.md](references/context-format.md). - -Don't couple `CONTEXT.md` to implementation details. Only include terms that are meaningful to domain experts. - -### Offer ADRs sparingly - -Only offer to create an ADR when all three are true: - -1. **Hard to reverse** — the cost of changing your mind later is meaningful -2. **Surprising without context** — a future reader will wonder "why did they do it this way?" -3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons - -If any of the three is missing, skip the ADR. Use the format in [adr-format.md](references/adr-format.md). - - diff --git a/plugins/bin/skills/grill-with-docs/references/adr-format.md b/plugins/bin/skills/grill-with-docs/references/adr-format.md deleted file mode 100644 index da7e78e..0000000 --- a/plugins/bin/skills/grill-with-docs/references/adr-format.md +++ /dev/null @@ -1,47 +0,0 @@ -# ADR Format - -ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc. - -Create the `docs/adr/` directory lazily — only when the first ADR is needed. - -## Template - -```md -# {Short title of the decision} - -{1-3 sentences: what's the context, what did we decide, and why.} -``` - -That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections. - -## Optional sections - -Only include these when they add genuine value. Most ADRs won't need them. - -- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited -- **Considered Options** — only when the rejected alternatives are worth remembering -- **Consequences** — only when non-obvious downstream effects need to be called out - -## Numbering - -Scan `docs/adr/` for the highest existing number and increment by one. - -## When to offer an ADR - -All three of these must be true: - -1. **Hard to reverse** — the cost of changing your mind later is meaningful -2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?" -3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons - -If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing." - -### What qualifies - -- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres." -- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP." -- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out. -- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s. -- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate. -- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract." -- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months. diff --git a/plugins/bin/skills/grill-with-docs/references/context-format.md b/plugins/bin/skills/grill-with-docs/references/context-format.md deleted file mode 100644 index ddfa247..0000000 --- a/plugins/bin/skills/grill-with-docs/references/context-format.md +++ /dev/null @@ -1,77 +0,0 @@ -# CONTEXT.md Format - -## Structure - -```md -# {Context Name} - -{One or two sentence description of what this context is and why it exists.} - -## Language - -**Order**: -{A concise description of the term} -_Avoid_: Purchase, transaction - -**Invoice**: -A request for payment sent to a customer after delivery. -_Avoid_: Bill, payment request - -**Customer**: -A person or organization that places orders. -_Avoid_: Client, buyer, account - -## Relationships - -- An **Order** produces one or more **Invoices** -- An **Invoice** belongs to exactly one **Customer** - -## Example dialogue - -> **Dev:** "When a **Customer** places an **Order**, do we create the **Invoice** immediately?" -> **Domain expert:** "No — an **Invoice** is only generated once a **Fulfillment** is confirmed." - -## Flagged ambiguities - -- "account" was used to mean both **Customer** and **User** — resolved: these are distinct concepts. -``` - -## Rules - -- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others as aliases to avoid. -- **Flag conflicts explicitly.** If a term is used ambiguously, call it out in "Flagged ambiguities" with a clear resolution. -- **Keep definitions tight.** One sentence max. Define what it IS, not what it does. -- **Show relationships.** Use bold term names and express cardinality where obvious. -- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs. -- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine. -- **Write an example dialogue.** A conversation between a dev and a domain expert that demonstrates how the terms interact naturally and clarifies boundaries between related concepts. - -## Single vs multi-context repos - -**Single context (most repos):** One `CONTEXT.md` at the repo root. - -**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other: - -```md -# Context Map - -## Contexts - -- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders -- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments -- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping - -## Relationships - -- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking -- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices -- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money` -``` - -The skill infers which structure applies: - -- If `CONTEXT-MAP.md` exists, read it to find contexts -- If only a root `CONTEXT.md` exists, single context -- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved - -When multiple contexts exist, infer which one the current topic relates to. If unclear, ask. diff --git a/plugins/bin/skills/improve-codebase-architecture/SKILL.md b/plugins/bin/skills/improve-codebase-architecture/SKILL.md deleted file mode 100644 index dca8068..0000000 --- a/plugins/bin/skills/improve-codebase-architecture/SKILL.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: improve-codebase-architecture -description: > - Use when the user wants to improve architecture, find refactoring - opportunities, consolidate tightly-coupled modules, or make a codebase more - testable and AI-navigable — deepening opportunities that turn shallow modules - into deep ones, informed by `CONTEXT.md` and `docs/adr/`. Not debugging a - failure -> `diagnose`. -metadata: - version: "1.0.1" ---- - -# Improve Codebase Architecture - -Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability. - -## Glossary - -Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." - -- **Module** — anything with an interface and an implementation (function, class, package, slice). -- **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, config. Not just the type signature. -- **Implementation** — the code inside. -- **Depth** — leverage at the interface: a lot of behaviour behind a small interface. **Deep** = high leverage. **Shallow** = interface nearly as complex as the implementation. -- **Seam** — where an interface lives; a place behaviour can be altered without editing in place. (Use this, not "boundary.") -- **Adapter** — a concrete thing satisfying an interface at a seam. -- **Leverage** — what callers get from depth. -- **Locality** — what maintainers get from depth: change, bugs, knowledge concentrated in one place. - -Key principles: - -- **Deletion test**: imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. -- **The interface is the test surface.** -- **One adapter = hypothetical seam. Two adapters = real seam.** - -If a term or principle above is ambiguous in the case in front of you, or you need the definitions and the principles the two lists leave out, read `references/language.md`. - -This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. - -## Process - -### 1. Explore - -Read the domain glossary and any ADRs in the area first. - -Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction: - -- Where does understanding one concept require bouncing between many small modules? -- Where are modules **shallow** — interface nearly as complex as the implementation? -- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)? -- Where do tightly-coupled modules leak across their seams? -- Which parts of the codebase are untested, or hard to test through their current interface? - -Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. - -### 2. Present candidates - -Present a numbered list of deepening opportunities. For each candidate: - -- **Files** — which files/modules are involved -- **Problem** — why the current architecture is causing friction -- **Solution** — plain English description of what would change -- **Benefits** — explained in terms of locality and leverage, and also in how tests would improve - -**Use CONTEXT.md vocabulary for the domain, and the architecture glossary above for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service." - -**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly (e.g. _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. - -Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?" - -### 3. Grilling loop - -Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. - -Side effects happen inline as decisions crystallize: - -- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `references/context-format.md` defines. Create the file lazily if it doesn't exist. -- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. -- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `references/adr-format.md`. -- **Want to explore alternative interfaces for the deepened module?** Read `references/interface-design.md`. diff --git a/plugins/bin/skills/improve-codebase-architecture/references/deepening.md b/plugins/bin/skills/improve-codebase-architecture/references/deepening.md deleted file mode 100644 index d230fb3..0000000 --- a/plugins/bin/skills/improve-codebase-architecture/references/deepening.md +++ /dev/null @@ -1,37 +0,0 @@ -# Deepening - -How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [language.md](language.md) — **module**, **interface**, **seam**, **adapter**. - -## Dependency categories - -When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam. - -### 1. In-process - -Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed. - -### 2. Local-substitutable - -Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface. - -### 3. Remote but owned (Ports & Adapters) - -Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter. - -Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."* - -### 4. True external (Mock) - -Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter. - -## Seam discipline - -- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. -- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them. - -## Testing strategy: replace, don't layer - -- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them. -- Write new tests at the deepened module's interface. The **interface is the test surface**. -- Tests assert on observable outcomes through the interface, not internal state. -- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. diff --git a/plugins/bin/skills/improve-codebase-architecture/references/interface-design.md b/plugins/bin/skills/improve-codebase-architecture/references/interface-design.md deleted file mode 100644 index da04831..0000000 --- a/plugins/bin/skills/improve-codebase-architecture/references/interface-design.md +++ /dev/null @@ -1,44 +0,0 @@ -# Interface Design - -When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best. - -Uses the vocabulary in [language.md](language.md) — **module**, **interface**, **seam**, **adapter**, **leverage**. - -## Process - -### 1. Frame the problem space - -Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate: - -- The constraints any new interface would need to satisfy -- The dependencies it would rely on, and which category they fall into (see [deepening.md](deepening.md)) -- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete - -Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel. - -### 2. Spawn sub-agents - -Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module. - -Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [deepening.md](deepening.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: - -- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point." -- Agent 2: "Maximise flexibility — support many use cases and extension." -- Agent 3: "Optimise for the most common caller — make the default case trivial." -- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." - -Include both [language.md](language.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language. - -Each sub-agent outputs: - -1. Interface (types, methods, params — plus invariants, ordering, error modes) -2. Usage example showing how callers use it -3. What the implementation hides behind the seam -4. Dependency strategy and adapters (see [deepening.md](deepening.md)) -5. Trade-offs — where leverage is high, where it's thin - -### 3. Present and compare - -Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. - -After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu. diff --git a/plugins/bin/skills/improve-codebase-architecture/references/language.md b/plugins/bin/skills/improve-codebase-architecture/references/language.md deleted file mode 100644 index 530c276..0000000 --- a/plugins/bin/skills/improve-codebase-architecture/references/language.md +++ /dev/null @@ -1,53 +0,0 @@ -# Language - -Shared vocabulary for every suggestion this skill makes. Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point. - -## Terms - -**Module** -Anything with an interface and an implementation. Deliberately scale-agnostic — applies equally to a function, class, package, or tier-spanning slice. -_Avoid_: unit, component, service. - -**Interface** -Everything a caller must know to use the module correctly. Includes the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. -_Avoid_: API, signature (too narrow — those refer only to the type-level surface). - -**Implementation** -What's inside a module — its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. - -**Depth** -Leverage at the interface — the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation. - -**Seam** _(from Michael Feathers)_ -A place where you can alter behaviour without editing in that place. The *location* at which a module's interface lives. Choosing where to put the seam is its own design decision, distinct from what goes behind it. -_Avoid_: boundary (overloaded with DDD's bounded context). - -**Adapter** -A concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside). - -**Leverage** -What callers get from depth. More capability per unit of interface they have to learn. One implementation pays back across N call sites and M tests. - -**Locality** -What maintainers get from depth. Change, bugs, knowledge, and verification concentrate at one place rather than spreading across callers. Fix once, fixed everywhere. - -## Principles - -- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. -- **The deletion test.** Imagine deleting the module. If complexity vanishes, the module wasn't hiding anything (it was a pass-through). If complexity reappears across N callers, the module was earning its keep. -- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape. -- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. - -## Relationships - -- A **Module** has exactly one **Interface** (the surface it presents to callers and tests). -- **Depth** is a property of a **Module**, measured against its **Interface**. -- A **Seam** is where a **Module**'s **Interface** lives. -- An **Adapter** sits at a **Seam** and satisfies the **Interface**. -- **Depth** produces **Leverage** for callers and **Locality** for maintainers. - -## Rejected framings - -- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. -- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know. -- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. diff --git a/plugins/bin/skills/prototype/SKILL.md b/plugins/bin/skills/prototype/SKILL.md deleted file mode 100644 index b544772..0000000 --- a/plugins/bin/skills/prototype/SKILL.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: prototype -description: > - Use when the user wants a throwaway prototype to answer a design question about - a data model, state machine or business logic, or to mock up a UI in several - variations. Not production code -> `tdd`. Not talking a design through -> - `grill-me`. -metadata: - version: "1.0.0" ---- - -# Prototype - -A prototype is **throwaway code that answers a question**. The question decides the shape. - -## Pick a branch - -| Question being answered | Build | Reference | -|---|---|---| -| "Does this logic / state model feel right?" | A tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper | `references/logic.md` | -| "What should this look like?" | Several radically different UI variations on one route, switchable via a URL search param and a floating bottom bar | `references/ui.md` | - -Resolve the row from the user's prompt, the surrounding code, or by asking if the user is around, then read only that reference — each is self-contained. - -The two branches produce fundamentally different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype. - -## Rules that apply to both - -1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure. -2. **One command to run.** Whatever the project's existing task runner supports — `pnpm `, `python `, `bun `, etc. The user must be able to start it without thinking. -3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is *checking*, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name. -4. **Skip the polish.** No tests, no error handling beyond what makes the prototype *runnable*, no abstractions. The point is to learn something fast and then delete it. -5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed. -6. **Delete or absorb when done.** When the prototype has answered its question, either delete it or fold the validated decision into the real code — don't leave it rotting in the repo. - -## When done - -The *answer* is the only thing worth keeping from a prototype. Capture it somewhere durable (commit message, ADR, issue, or a `NOTES.md` next to the prototype) along with the question it was answering. If the user is around, that capture is a quick conversation; if not, leave the placeholder so they (or you, on the next pass) can fill in the verdict before deleting the prototype. diff --git a/plugins/bin/skills/prototype/references/logic.md b/plugins/bin/skills/prototype/references/logic.md deleted file mode 100644 index 2376fd1..0000000 --- a/plugins/bin/skills/prototype/references/logic.md +++ /dev/null @@ -1,79 +0,0 @@ -# Logic Prototype - -A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases. - -## When this is the right shape - -- "I'm not sure if this state machine handles the edge case where X then Y." -- "Does this data model actually let me represent the case where..." -- "I want to feel out what the API should look like before writing it." -- Anything where the user wants to **press buttons and watch state change**. - -If the question is "what should this look like" — wrong branch. Read `references/ui.md`. - -## Process - -### 1. State the question - -Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK. - -### 2. Pick the language - -Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask. - -Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype. - -### 3. Isolate the logic in a portable module - -Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be. - -The right shape depends on the question: - -- **A pure reducer** — `(state, action) => state`. Good when actions are discrete events and state is a single value. -- **A state machine** — explicit states and transitions. Good when "which actions are even legal right now" is part of the question. -- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations. -- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state. - -Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no `console.log` for control flow. The TUI imports it and calls into it; nothing flows the other direction. - -This is what makes the prototype useful past its own lifetime. When the question's been answered, the validated reducer / machine / function set can be lifted into the real module — the TUI shell gets deleted. - -### 4. Build the smallest TUI that exposes the state - -Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback. - -Each frame has two parts, in this order: - -1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset. No need to pull in a styling library unless one is already in the project. -2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`. Bold the key, dim the description, or vice-versa — whatever reads cleanly. - -Behaviour: - -1. **Initialise state** — a single in-memory object/struct. Render the first frame on start. -2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state. -3. **Re-render** the full frame after every action — don't append, replace. -4. **Loop until quit.** - -The whole frame should fit on one screen. - -### 5. Make it runnable in one command - -Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`). The user should run `pnpm run ` or equivalent — never need to remember a path. - -If the host project has no task runner, just put the command at the top of the prototype's README. - -### 6. Hand it over - -Give the user the run command. They'll drive it themselves; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions added, add them. Prototypes evolve. - -### 7. Capture the answer - -When the prototype has done its job, the answer to the question is the only thing worth keeping. If the user is around, ask what it taught them. If not, leave a `NOTES.md` next to the prototype so the answer can be filled in (or filled in by you, if you've watched the session) before the prototype gets deleted. - -## Anti-patterns - -- **Don't add tests.** A prototype that needs tests is no longer a prototype. -- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence. -- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question. -- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module. -- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping. diff --git a/plugins/bin/skills/prototype/references/ui.md b/plugins/bin/skills/prototype/references/ui.md deleted file mode 100644 index 9afb121..0000000 --- a/plugins/bin/skills/prototype/references/ui.md +++ /dev/null @@ -1,112 +0,0 @@ -# UI Prototype - -Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away. - -If the question is about logic/state rather than what something looks like — wrong branch. Read `references/logic.md`. - -## When this is the right shape - -- "What should this page look like?" -- "I want to see a few options for this dashboard before committing." -- "Try a different layout for the settings screen." -- Any time the user would otherwise spend a day picking between three vague mockups in their head. - -## Two sub-shapes — strongly prefer sub-shape A - -A UI prototype is much easier to judge when it's **butting up against the rest of the app** — real header, real sidebar, real data, real density. A throwaway route on its own is a vacuum: every variant looks fine in isolation. Default to sub-shape A whenever there's a plausible existing page to host the variants. Only reach for sub-shape B if the prototype genuinely has no nearby home. - -### Sub-shape A — adjustment to an existing page (preferred) - -The route already exists. Variants are rendered **on the same route**, gated by a `?variant=` URL search param. The existing data fetching, params, and auth all stay — only the rendering swaps. This is the default; pick it unless there's a specific reason not to. - -If the prototype is for something that doesn't yet have a page but *would naturally live inside one* (a new section of the dashboard, a new card on the settings screen, a new step in an existing flow) — that's still sub-shape A. Mount the variants inside the host page. - -### Sub-shape B — a new page (last resort) - -Only use this when the thing being prototyped genuinely has no existing page to live inside — e.g. an entirely new top-level surface, or a flow that can't be embedded anywhere sensible. - -Create a **throwaway route** following whatever routing convention the project already uses — don't invent a new top-level structure. Name it so it's obviously a prototype (e.g. include the word `prototype` in the path or filename). Same `?variant=` pattern. - -Before committing to sub-shape B, sanity-check: is there really no existing page this could be embedded in? An empty route hides design problems that a populated one would expose. - -In both sub-shapes the floating bottom bar is identical. - -## Process - -### 1. State the question and pick N - -Default to **3 variants**. More than 5 stops being radically different and starts being noise — cap there. - -Write down the plan in one line, in the prototype's location or a top-of-file comment: - -> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route." - -This works whether the user is here to push back or not. - -### 2. Generate radically different variants - -Draft each variant. Hold each one to: - -- The page's purpose and the data it has access to. -- The project's component library / styling system (TailwindCSS, shadcn, MUI, plain CSS, whatever). -- A clear exported component name, e.g. `VariantA`, `VariantB`, `VariantC`. - -Variants must be **structurally different** — different layout, different information hierarchy, different primary affordance, not just different colours. Three slightly-tweaked card grids isn't a UI prototype, it's wallpaper. If two drafts come out too similar, redo one with explicit "do not use a card grid" guidance. - -### 3. Wire them together - -Create a single switcher component on the route: - -```tsx -// pseudo-code — adapt to the project's framework -const variant = searchParams.get('variant') ?? 'A'; -return ( - <> - {variant === 'A' && } - {variant === 'B' && } - {variant === 'C' && } - - -); -``` - -For sub-shape A (existing page): keep all the existing data fetching above the switcher; only the rendered subtree changes per variant. - -For sub-shape B (new page): the throwaway route under `/prototype/` mounts the same switcher. - -### 4. Build the floating switcher - -A small fixed-position bar at the bottom-centre of the screen with three pieces: - -- **Left arrow** — cycles to the previous variant (wraps around). -- **Variant label** — shows the current variant key and, if the variant exports a name, that name too. e.g. `B — Sidebar layout`. -- **Right arrow** — cycles forward (wraps around). - -Behaviour: - -- Clicking an arrow updates the URL search param (use the framework's router — `router.replace` on Next, `navigate` on React Router, etc) so the variant is shareable and reload-stable. -- Keyboard: `←` and `→` arrow keys also cycle. Don't intercept arrow keys when an ``, `