The first 13 lessons taught you to add capabilities to your own Claude Code; this one teaches you to package those capabilities as a plugin, so a new machine, a new project, or a new colleague never means re-copying config by hand. By the end you'll have closed the loop from "I can use it" to "the whole team uses it".
Why this lesson comes today
Look back at the syllabus: D7 taught Skills, D8 taught Hooks, D9 taught MCP, D10 taught Subagents. These four capabilities share one trait: their configuration is scattered across your local .claude/ directory or settings.json. To share them with a colleague, you'd have to copy files by hand — no versions, no update mechanism, no install command.
OfficialPlugins are the official packaging answer: a self-contained directory that bundles skills, agents, hooks, MCP servers, LSP servers and other components, and — with a manifest — can be installed, updated, and distributed through a marketplace. Our takeIt sits in the final lesson because a plugin introduces no new capability of its own — it's the shipping container for everything that came before; understand the contents first, then learn the packing, and the order makes sense. D13's CI scenario also gets its closing piece here: the official seed-directory mechanism lets container images pre-install plugins at build time.
Core concepts, explained
One box for everything you've learned
OfficialPhysically, a plugin is just a directory: metadata lives in .claude-plugin/plugin.json (the manifest), and every other component sits in its default location under the plugin root. The docs warn specifically: .claude-plugin/ holds only plugin.json — stuffing skills/, agents/, hooks/ and the like into it is the most common structural mistake.
.claude-plugin/; components all sit at the root. The first four components are exactly the capabilities you learned in D7–D10.OfficialThe manifest itself is optional: without one, Claude Code auto-discovers components in their default locations and the plugin takes its directory name; with one, only name is required. Skills inside a plugin carry a namespace prefix — hello inside plugin my-plugin becomes /my-plugin:hello — so same-named skills in different plugins don't collide.
Standalone config or a plugin?
OfficialThe docs give explicit selection criteria:
| Approach | Skill name | Best for |
|---|---|---|
Standalone config (.claude/ directory) | /hello | Personal workflows, single-project customization, quick experiments |
| Plugin (self-contained directory + manifest) | /my-plugin:hello | Sharing with a team/community, cross-project reuse, versioned releases, marketplace distribution |
OfficialThe recommended path: iterate quickly in .claude/ first, then convert to a plugin to share once it matures. Migration is mostly copying .claude/skills/ and agents/ into the plugin root, and moving hooks from settings into hooks/hooks.json (same format). Our takeRemember to delete the originals from .claude/ after migrating: a same-named agent gets overridden by the project-level definition, so the plugin version would never take effect. For choosing among the five extension mechanisms, see the tip Skill / Hook / MCP / Subagent / Plugin: which to use.
Analogy: a marketplace is an app store, a plugin is an app
OfficialThe docs use this analogy themselves: adding a marketplace is like adding an app store — you've only registered the catalog and can browse it, nothing is installed yet; from it you then pick individual plugins to install. So distribution is always two steps: /plugin marketplace add registers the catalog, /plugin install plugin-name@marketplace-name installs.
A marketplace's physical form is .claude-plugin/marketplace.json at the repo root, with three required fields: name, owner, plugins; each entry in the plugins array needs at least name and source. source supports five kinds: an in-repo relative path (./plugins/xx), github (owner/repo), url (any git address), git-subdir (a monorepo subdirectory, sparse clone), and npm (an npm package). OfficialKeep the two layers straight: the marketplace source is "where the catalog itself lives"; a plugin source is "where each plugin in the catalog is fetched from" — the two can point at different repos and pin versions independently.
~/.claude/plugins/cache.Versioning and the cache: the two easiest traps to hit
Trap one: "I pushed a new commit, so users will get the update." OfficialVersions resolve in this order: 1. version in plugin.json → 2. version on the marketplace entry → 3. the git commit SHA of the plugin source. Once you've written "version": "1.0.0", no number of extra commits matters — until you bump that field, users running /plugin update just get "already up to date": the version string is the cache key. Official advice: fast-iterating internal plugins should simply omit version, so every commit counts as a new release; explicit semantic versions are for stable public releases. Also don't set it in both plugin.json and the marketplace entry: the former silently wins, so a stale manifest version will mask the number you changed in the catalog.
Trap two: "a plugin can reference files elsewhere in the repo." OfficialWhen installed from a marketplace, the plugin directory is copied into the local cache at ~/.claude/plugins/cache, not used in place. Files reached through out-of-bounds paths like ../shared-utils don't get copied, so they are guaranteed broken after install. To reference the plugin's own files, use the ${CLAUDE_PLUGIN_ROOT} variable (it points at the install directory and changes on every update); for data that must persist across versions and updates (such as node_modules or caches), use ${CLAUDE_PLUGIN_DATA}. If files truly must be shared within one marketplace, the official escape hatch is symlinks.
Three routes for team distribution
Our takeOrdered from smallest audience to largest:
- Bundled with the project repo (the team default). OfficialWrite
extraKnownMarketplaces(declaring the marketplace) plusenabledPlugins(declaring which plugins are enabled by default) into the project's.claude/settings.json; teammates get an install prompt once they trust the directory. The install scopes confirm this design:user(you, all projects),project(written into.claude/settings.json, shared with the repo),local(you, this project),managed(admin-controlled, read-only). - A private marketplace repo (company-internal). OfficialInstalling from private git repos is supported — git permissions are your access control; GitHub, GitLab, and self-hosted git all work. For CI/container scenarios,
CLAUDE_CODE_PLUGIN_SEED_DIRpre-seeds the marketplace and plugin cache at image build time, for zero-clone startup at runtime — the companion piece to D13's headless mode. - Public release (community). OfficialAnthropic maintains two public marketplaces:
claude-plugins-officialis curated by Anthropic, auto-registered on first interactive startup, with no submission channel;claude-communityaccepts third-party submissions, included after automated validation and security screening, and added manually by users with/plugin marketplace add anthropics/claude-plugins-community. Submissions go through the form on claude.ai or the Console; runclaude plugin validate ./your-pluginlocally before submitting.
claude plugin details plugin-name shows its component inventory and estimated token overhead; organizations can control which marketplaces may be added with strictKnownMarketplaces.Hands-on: doable today
Walk the official quickstart end to end — create the plugin → test locally → create a marketplace → install — about 30 minutes.
- Create the plugin directory and manifest. Expected result:
my-first-plugin/.claude-plugin/plugin.json:mkdir -p my-first-plugin/.claude-plugin cat > my-first-plugin/.claude-plugin/plugin.json <<'EOF' { "name": "my-first-plugin", "description": "A greeting plugin to learn the basics", "version": "1.0.0" } EOF - Add a skill. Create
my-first-plugin/skills/hello/SKILL.md:--- description: Greet the user with a friendly message disable-model-invocation: true --- Greet the user warmly and ask how you can help them today. - Load it locally for testing: start
claude --plugin-dir ./my-first-plugin, then type/my-first-plugin:hello. Expected: Claude replies with a greeting; the Custom commands tab of/helplists the skill under its namespace. After edits, run/reload-pluginsto hot-reload — no restart needed. - Validate the structure:
claude plugin validate ./my-first-plugin. Expected: it printsValidation passedwith a check mark; add--strictto treat warnings as errors too — good for CI. - Create a local marketplace and install from it. First build the catalog:
Then, inside Claude Code, runmkdir -p my-marketplace/.claude-plugin mv my-first-plugin my-marketplace/plugins/ cat > my-marketplace/.claude-plugin/marketplace.json <<'EOF' { "name": "my-plugins", "owner": { "name": "Your Name" }, "plugins": [ { "name": "my-first-plugin", "source": "./plugins/my-first-plugin", "description": "A greeting plugin to learn the basics" } ] } EOF/plugin marketplace add ./my-marketplace, then/plugin install my-first-plugin@my-plugins, and pick a scope on the details page that appears. Expected: the install summary shows the plugin is active, or promptsRun /reload-plugins to activate.— do so. To distribute publicly, pushmy-marketplaceto GitHub, and others can add it with/plugin marketplace add owner/repo. - Package your own existing config. Let Claude Code do the work with the prompt below:
Migrate the skills, agents, and the hooks in settings from this project's .claude/ directory into a Claude Code plugin named team-toolkit:
1. Create team-toolkit/.claude-plugin/plugin.json, leaving out the version field for now (use commit SHAs as versions to make iteration easy);
2. Copy .claude/skills/ and .claude/agents/ into the plugin root, move the hooks config into hooks/hooks.json, and switch script paths to ${CLAUDE_PLUGIN_ROOT};
3. Run claude plugin validate ./team-toolkit and make it pass, fixing the warnings too;
4. List which originals I should delete from .claude/ after the migration to avoid same-name overrides — don't delete anything yet.
How to know you've learned it
- You can articulate the difference and trade-offs between standalone config (
.claude/) and a plugin: namespacing (/hellovs/plugin:hello), distributability, versioning. - You can sketch the plugin directory layout and point out the most common mistake — putting component directories inside
.claude-plugin/. - You've run your first plugin with
--plugin-dir, passedclaude plugin validate, and completed one install from a local marketplace. - You can explain what
/plugin marketplace addand/plugin installeach do, and the difference between a marketplace source and a plugin source. - Self-test: you shipped with
"version": "1.0.0"inplugin.json, then pushed three bug-fix commits. What do users see when they run/plugin update, and how do you fix it? (Answer: they see "already up to date", because the version string is the cache key and it hasn't changed; either bumpversionon every release, or delete the field and let the commit SHA serve as the version.)
And that wraps the 14-day course. The through-line, looking back: D1–D6 laid the foundations (the loop, context, permissions, memory, planning, sessions), D7–D10 the four extension mechanisms, D11–D13 parallelism and automation, D14 packaging and distribution. From "using it" to "using it well" to "getting the whole team on it" — the loop is closed.