Skip to content

Plugin anatomy

Every plugin shares one shape: a manifest, a skill, a command, an agent, and a ds/ snapshot. Nothing is optional and nothing is shared across plugin boundaries — each plugin installs and works standalone.

AnatomyInside every pluginplugins/patterson-deck/.claude-plugin/plugin.jsonskills/deck-template/SKILL.mdcommands/new-deck.mdagents/deck-builder.mdds/styles.css · tokens/assets/brand,fonts/templates/deck/index.htmlREADME.mdplugin.jsonManifest — the only file allowedinside .claude-plugin/.SKILL.mdBrand + workflow knowledge. Auto-invoked, or run by name.new-deck.mdSlash command: /patterson-deck:new-deck [brief] — scaffolds ds/.deck-builder.mdSubagent Claude delegates deckwork to automatically.ds/Self-contained snapshot mirroringthe source tree — ../../styles.cssalways resolves.

ds/ files reference each other with relative paths — ../../styles.css, ../../assets/brand/… — that only resolve if the folder shape stays intact. Move a file, and the path breaks in three places at once: in this repo, in the installed plugin cache, and after a consumer runs cp -R ds/ ./patterson into their own project.

Why snapshots mirror the source treeRelative paths survive the copy untouchedds/ (copied into your project as ./patterson)styles.csstokens/.cssassets/brand/.svgassets/fonts/*.woff2templates/deck/index.html../../…Inside index.html<link href="../../styles.css"><img src="../../assets/brand/patterson-logo-white.svg">Two levels up is always the snapshot root —in this repo, in the plugin cache, and in yourproject. Never flatten or move files in ds/.
  1. Never flatten, rename, or move a file inside any ds/. claude plugin validate . checks manifests — names, versions, frontmatter — not snapshot hygiene. A broken relative path is a silent failure: the page loads with no stylesheet and no logo, and nothing in CI catches it.
  2. Dual version source of truth. A content change bumps version in both plugins/<name>/.claude-plugin/plugin.json and the matching entry in .claude-plugin/marketplace.json, kept equal. tests/run-tests.sh checks that every plugin has a non-empty version — it does not check that the two numbers match.

See Contributing for the full list and the devcontainer that ships with these checks pre-wired.