This document is the mental model behind how a Claude plugin gets installed. Normal plugin instructions tell you what to do; this document tells you why it works, what each piece actually is, and how the parts fit together. Reading it should take maybe ten minutes and save you hours of confusion later when something doesn't behave the way you'd expect from reading a plugin's surface docs alone.
The single most useful idea for understanding Claude Code plugins is that the system has three layers, and they're independent.
The first is declaration — what the project says should be
installed. It lives in this repo's .claude/settings.json, committed
alongside the code. A collaborator who clones the repo gets the
declaration as part of the source tree.
The second is install — what's actually on disk on your machine. It
lives in ~/.claude/, user-scoped, never committed. The declaration
says "this plugin should be present"; the install is the act of cloning
the plugin's code into your home directory and recording that it's
active.
The third is runtime — what's loaded into a given Claude session. It's constructed each time a session starts by reading the user-level install and applying it. The runtime is ephemeral; the install is durable.
This three-layer split is the same idea you already know from package
managers: a package.json declares dependencies, npm install fetches
them into node_modules, and a running process loads them. The
difference is that the install is user-scoped rather than project-scoped
— there's no per-project node_modules equivalent. Every plugin you
install lands in one shared ~/.claude/plugins/ directory and applies
to every Claude Code session on that machine.
The practical consequence: a new collaborator clones the repo and gets the declaration but not the install. The first Claude Code session they open in the repo notices the discrepancy and prompts them to install the declared plugin. Once they accept, the plugin lives at the user level on their machine and every Claude Code session on that machine — CLI, desktop app, Zed adapter — sees it from then on.
A Claude Code plugin is a directory with a .claude-plugin/plugin.json
manifest. The manifest declares a name, version, and what the plugin
contributes. The pieces a plugin can ship are skills, hooks, slash
commands, and MCP servers.
Skills are markdown files with YAML frontmatter that describe a
workflow or pattern. Each skill has a name and a description written
in trigger language ("Use when..."). The agent reads the available
skills' descriptions and decides — heuristically, based on your message
— whether to invoke one. Skills are the most common plugin output.
Superpowers, for example, is mostly a skills library: brainstorming,
writing-plans, test-driven-development, systematic-debugging, and so on.
Hooks are scripts that fire on lifecycle events. session-start is
the most useful: it runs when a Claude session begins, lets the plugin
announce itself, register state, or prepare context. Hooks run with your
real shell privileges and can be written in any language. They're how a
plugin makes itself feel present without depending on the model to
discover it.
Slash commands are user-typed shortcuts that expand to predefined
prompts or actions. Plugins can register them under the / namespace.
MCP servers are tools that extend what the agent can call — connectors to external systems, custom file readers, database query interfaces. A plugin can bundle one or more MCP servers and they get started on session launch.
Most plugins ship some combination of these. Superpowers is heavy on skills, has a session-start hook for bootstrap, and (as of v5.1.0) does not ship MCP servers.
Plugins don't have a central registry like npmjs.com. They're discovered
through marketplaces, which are themselves just JSON files
(.claude-plugin/marketplace.json) hosted somewhere — typically a
GitHub repo — that list one or more plugins and where to fetch them.
When Claude Code reads an extraKnownMarketplaces entry from your
project's settings.json, it clones the referenced GitHub repo, reads
its marketplace.json, and learns about the plugins available there.
Each plugin entry in the marketplace points to a source, often the same
repo, sometimes a subdirectory or a different repo entirely.
The Superpowers repo is structured so the marketplace.json and the plugin code live in the same place. That's not required — a marketplace can index plugins from anywhere — but it's the simplest pattern.
Anthropic also publishes a curated catalog of plugins of their own.
That's why you'll see two Superpowers entries in the desktop app's
Customize → Plugins panel: one under the Code tab from Jesse Vincent's
upstream (the superpowers-dev marketplace), and one under Anthropic &
Partners from Anthropic's curated catalog. They're separate
distributions of overlapping content. This repo uses Jesse's upstream
because we can version-pin it directly at a GitHub commit.
Here's the part that surprised me coming from package-manager-land:
Claude Code has no lockfile. The enabledPlugins declaration in
settings.json is just a boolean — it says "install this" but not
"install this exact version." Without further intervention, two
developers installing at different times will resolve to whatever was at
the marketplace's default branch HEAD when each ran the install. That's
not acceptable for a repo where you want every collaborator running the
same plugin code.
The lever you have is the ref field on the marketplace source. It
accepts a branch, tag, or commit SHA and pins the marketplace fetch.
Since Superpowers' marketplace.json and plugin code live in the same
repo, pinning the marketplace effectively pins the plugin. Our
.claude/settings.json uses "ref": "v5.1.0" to lock everyone to that
release tag.
Tags are mutable in git — a determined upstream could re-point one — so
for maximum determinism you can replace the tag with the underlying
commit SHA (find it with
git ls-remote https://github.com/obra/superpowers v5.1.0). For this
repo, the tag is currently sufficient; if the threat model shifts,
tightening to a SHA is a one-line change.
To pick up a newer Superpowers release, you bump the ref value in
.claude/settings.json, commit the change, and collaborators see the
new version on their next session start. There's no auto-update path,
and we wouldn't want one — silent updates to code that registers hooks
and runs in your shell would be bad. The friction of a manual bump is a
feature.
You can interact with Claude Code through several front-ends. They all
sit on top of the same Claude Code binary and read the same ~/.claude/
state. Understanding this is what makes "install once, available
everywhere" work.
The Claude Code CLI is the canonical implementation. Run claude in
a terminal from any project directory. Every other harness invokes this
binary under the hood. Plugins, hooks, slash commands, settings — all
behave exactly as documented.
The Claude desktop app's Code tab is a UI shell around the same
Claude Code binary. Same plugins, same skills, same hooks. The slash
command surface is not fully exposed in the desktop app's chat input —
/plugin marketplace add won't autocomplete or fire there — but
graphical equivalents exist in the Customize panel. For plugin installs
specifically, the project-level .claude/settings.json plus the install
prompt is the path to use; you don't generally need the slash command.
Zed's Claude Agent is Zed's adapter that invokes Claude Code as a
subprocess and talks to it via the
Agent Client Protocol (ACP).
Same binary, same config files, same plugin behavior. You don't install
plugins "in Zed" — you install them once into ~/.claude/, and Zed's
Claude Agent picks them up the next time you open a thread.
The Claude desktop app's Cowork tab is a different surface entirely
— it runs in a sandboxed Linux container rather than on your real
machine, and is aimed at general-purpose document/data workflows rather
than development. It's not where dev work on this repo happens. If you
find yourself running git or invoking uv in Cowork, you're in the
wrong tab.
The "install once, available everywhere" property is the architectural payoff. You don't need a separate install ritual for each harness. The plugin install is user-scoped; the harness is just a window onto it.
When you open a Claude Code session — whether by running claude in a
terminal, clicking into the desktop app's Code tab, or starting a Zed
Claude Agent thread — the following happens, roughly in order. Claude
Code reads ~/.claude/settings.json for user-level defaults and enabled
plugins. It reads the project's .claude/settings.json if one exists,
merging it on top. For each enabled plugin, it loads the plugin from
~/.claude/plugins/, reads its .claude-plugin/plugin.json, and runs
any registered session-start hook. The plugin's skills are surfaced
into the model's available-skills list. The plugin's slash commands are
registered. The plugin's MCP servers (if any) are started and their
tools become callable. Only then does the model begin its first turn,
with all of the above in context.
This sequence is the same regardless of harness. It's also why an install that worked in the desktop app's Code tab "just works" the next time you open the repo in Zed — Zed's adapter is calling into the same code path.
Skills aren't deterministic gates. The agent sees each skill's
description field at session start and decides, turn by turn, whether
your current message warrants invoking one. The skill description is the
trigger contract — phrasing like "Use when implementing any feature or
bugfix, before writing implementation code" tells the agent when to call
it.
This means two things in practice.
First, the framing of your message matters. "Let's figure out what to do next" admits design space and triggers brainstorming. "Implement task 6" doesn't, and goes straight to code. Both are correct; they just engage different skills.
Second, CLAUDE.md is for soft invitations, not hard rules. A
CLAUDE.md instruction saying "always use brainstorming before any code
change" doesn't override skill triggering — it nudges, but the agent
still decides. For things you want guaranteed (test coverage, formatter
compliance, commit conventions), the right enforcement layer is CI,
pre-commit hooks, or repository code review, not a CLAUDE.md
instruction. Skills are practice prompts. They're not control systems.
Plugins fetch code from a GitHub repo and run hooks in your shell.
That's nontrivial trust. The system handles it by making the install
an explicit consent moment: when Claude Code sees a
declared-but-not-installed plugin (either from /plugin install or from
a project's .claude/settings.json), it prompts you to approve the
install before fetching anything. Once installed, subsequent session
starts use the cached install without re-prompting. A version bump
(changing the ref) re-triggers the prompt because the new code hasn't
been seen yet.
The implication is real: a hostile commit to this repo's
.claude/settings.json cannot silently install a malicious plugin on a
collaborator's machine. They'd see the prompt and have a chance to read
the diff. Treat the prompt as a code-review moment, not a click-through.
If you want stronger isolation — never trust upstream's tag movements
even with the install prompt — fork obra/superpowers to your own
GitHub org, pin to a SHA in your fork, and have extraKnownMarketplaces
point at the fork. Upstream changes then require an explicit
pull-and-review-and-bump in your fork before they can reach anyone.
| Layer | Path | Purpose | Committed? |
|---|---|---|---|
| Project declaration | .claude/settings.json |
Declares marketplaces and enabled plugins for the repo | Yes |
| Project local | .claude/settings.local.json |
Per-machine overrides (allowlists, etc.) | No (gitignored) |
| User install record | ~/.claude/settings.json |
Records installed plugins on this machine | No (not in repo) |
| Plugin code | ~/.claude/plugins/ |
Cloned plugin repositories | No (not in repo) |
| Plugin manifest | ~/.claude/plugins/<plugin>/.claude-plugin/plugin.json |
What the plugin contributes | No |
| Marketplace manifest | ~/.claude/plugins/<plugin>/.claude-plugin/marketplace.json |
What plugins a marketplace exposes | No |
The simplest check is to start a Claude Code session in the repo (via the CLI, the desktop app's Code tab, or Zed's Claude Agent) and ask an open-ended planning question — "What should we work on next from IMPROVEMENTS.md?" If Superpowers loaded, the agent will invoke a skill like brainstorming before responding. If you get a direct answer without any skill invocation, something is off and Superpowers' skills aren't reaching the model.
For a more definitive check, ask the agent directly: "List the skills currently available to you." It should enumerate the Superpowers ones (brainstorming, writing-plans, test-driven-development, systematic-debugging, and the others) alongside any others. Absence of those tells you the install didn't take.
A few common failure modes, in rough order of likelihood.
The install prompt didn't appear when you opened the repo and the plugin doesn't show as installed in Customize → Plugins. Quit and relaunch the desktop app (not just a new chat session) so it re-reads project settings. If that still doesn't trigger the prompt, install manually via the Customize panel — pick "Superpowers" by Jesse Vincent under the Code marketplace tab, not the Anthropic-curated version under Anthropic & Partners.
The plugin is installed but skills aren't surfacing. Check that the
marketplace and plugin names in .claude/settings.json match what's
actually published. The plugin name is superpowers; the marketplace
name is superpowers-dev. Mismatches fail silently — no error appears,
the install just doesn't happen.
Hooks aren't firing. Verify the install at the user level —
~/.claude/plugins/ should contain a superpowers-dev directory with a
hooks/ subdirectory and a session-start script. If it's missing, the
install didn't complete; reinstall via the Customize panel.
Skills surface but the agent doesn't invoke them. This is usually the
framing of the message, not a config problem. Try asking an open-ended
question that admits design space. If it still doesn't trigger, check
that CLAUDE.md isn't actively contradicting the skill — instructions
like "be terse, just answer" can suppress skill invocation.
The authoritative Claude Code documentation lives at code.claude.com. The pages below are the ones worth reading directly when this doc's mental model isn't enough.
For the plugin system itself, in roughly the order you'd want to
encounter them:
Plugins overview introduces
what a plugin is and how to author one.
Plugin marketplaces
explains the marketplace layer — how to create one, how plugins are
listed, and how the ref field controls version resolution.
Discover and install plugins
covers the install workflow from the user's perspective, including the
auto-prompt behavior when a project declares a plugin that isn't yet
installed. The
plugins reference
is the schema reference — open it when you need to know what fields are
valid in plugin.json, marketplace.json, or hooks.json.
For configuration: the
settings reference documents
the full settings.json schema, including extraKnownMarketplaces,
enabledPlugins, permissions, and the relationship between user-level
and project-level files.
Skills covers how skills are
structured and surfaced to the model — useful background even if you're
consuming skills rather than authoring them.
For the harness layer:
Desktop application covers
the Claude desktop app's Code tab and where it differs from the
standalone CLI.
External agents in Zed
describes Zed's overall integration with Claude Code and other agents.
Claude Code in Zed via ACP
is the technical writeup of how Zed's Claude Agent invokes Claude Code
via the Agent Client Protocol — read this if you want to understand why
an install at ~/.claude/ automatically propagates to Zed.
For the plugin this repo actually uses:
obra/superpowers is Jesse
Vincent's upstream repository. The README documents the individual
skills, the plugin's design philosophy, and the hook bootstrap
mechanism. The repo's RELEASE-NOTES.md is the source for "what changed
in v5.x.y" when you're deciding whether to bump the pinned ref.
For the protocol that underlies MCP servers (one of the four plugin contribution types): Model Context Protocol is the protocol spec. Worth reading if you ever author or evaluate a plugin that ships an MCP server, since the protocol determines what kinds of tool integrations are possible.