In my last post on custom slash commands, I covered how a single markdown file in .claude/commands/ becomes a working /command in Claude Code. That's great for personal shortcuts or one-off automations in a project. But when you want to share a collection of related commands across a team — and keep them versioned, installable, and discoverable — you need something more structured.
That's where the plugin system comes in.
Claude Code supports a marketplace model that lets you bundle commands, agents, and skills into plugins, host them in a local directory (or eventually a remote registry), and install them with a single CLI invocation. The best practices around this are still evolving, but the foundation is solid enough to build real workflows on. Here's how I've been thinking about it.
What a Plugin Actually Is
At its core, a Claude Code plugin is just a directory with a specific layout. It has a manifest file that describes it, a folder of commands, and optionally folders for agents and skills. There's no compilation step, no binary, no package manager resolution — it's all markdown files and JSON.
The directory structure looks like this:
my-plugin/├── .claude-plugin/│ └── plugin.json├── commands/│ └── my-command.md├── agents/│ └── my-agent.md├── skills/│ └── my-skill/│ └── SKILL.md├── shared/│ └── reference-doc.md└── README.md
Each subdirectory has a specific role, which I'll break down below.
Plugin Anatomy
.claude-plugin/plugin.json
This is the manifest. It gives the plugin its identity:
{"name": "my-plugin","version": "1.0.0","description": "What this plugin does","author": {"name": "Your Name","email": "you@example.com"},"keywords": ["tag1", "tag2"]}
The name and version fields are what Claude Code uses to identify the plugin and check for updates. Use semantic versioning — when you change a command's behavior, bump the version, otherwise Claude Code may serve a cached version of the old prompt.
commands/
Each .md file here registers a slash command. The filename becomes the command name: code-review.md → /code-review. The file content is the prompt Claude receives, with optional frontmatter:
---description: Run a code review against main and write a reportallowed-tools: [Bash, Read, Write, Glob]---Your prompt here. Use $ARGUMENTS for anything typed after the command.
This is the same format as project-level commands in .claude/commands/, just organized under a plugin.
agents/
Agents are named subprocesses that a command can spin up to handle a focused subtask. A command might orchestrate three agents — one that reads context, one that does the heavy lifting, and one that formats output — keeping each agent's context clean and its tool access scoped.
An agent file is just a markdown prompt. The name of the file becomes the agent identifier. A command references agents via the Agent tool with a subagent_type that matches the plugin's agent name.
<!-- agents/my-analyzer.md -->You are a specialized agent that analyzes X.Given $INPUT, produce $OUTPUT in the following format:...
Agents are great when a workflow has a natural decomposition. Instead of one giant prompt trying to do everything, you break it into stages where each agent focuses on exactly one thing.
skills/
Skills are reusable prompt modules that can be invoked from a command or used to extend another plugin's behavior. They live in subdirectories with a SKILL.md file as the entry point. A skill directory can also have a references/ folder for supporting docs the skill pulls in:
skills/└── my-workflow/├── SKILL.md└── references/├── best-practices.md└── patterns.md
The distinction between a command and a skill is subtle. A command is user-facing — you type /command-name to invoke it. A skill is more like a module — it's a reusable prompt block that can be loaded into a command or referenced across plugins. Think of it as the difference between a CLI entry point and a library function.
shared/
Shared is just a convention I've seen work well in practice. It's a place for reference documents that multiple commands or agents within the same plugin need to read — things like formatting guides, error message templates, validation rules. There's no special treatment from Claude Code; it's just a directory your prompts reference.
Building a Marketplace
When you have multiple plugins, you probably want a central place to register them all. That's the marketplace — a parent directory with its own manifest at .claude-plugin/marketplace.json:
{"$schema": "https://anthropic.com/claude-code/marketplace.schema.json","owner": {"name": "my-team"},"name": "my-marketplace","version": "1.0.0","description": "A collection of Claude Code plugins for our team workflows.","plugins": [{"name": "code-review","description": "Automated code review and feedback implementation","source": "./code-review"},{"name": "jira","description": "Jira workflow integration for creating and syncing stories","source": "./jira"}]}
The layout is a monorepo-style directory where each plugin lives in its own subfolder alongside the root marketplace manifest:
my-toolkit/├── .claude-plugin/│ └── marketplace.json├── code-review/│ ├── .claude-plugin/plugin.json│ ├── commands/│ └── ...└── jira/├── .claude-plugin/plugin.json├── commands/└── ...
This structure lets you version-control everything in one repo, install individual plugins independently, and update them one at a time.
Installing Plugins
Once you have a marketplace directory, installing is a two-step process.
First, register the marketplace:
claude plugin marketplace add /path/to/my-toolkit
Then install individual plugins from it:
claude plugin install code-review@my-marketplaceclaude plugin install jira@my-marketplace
Important: after installing or updating a plugin, you need to restart Claude Code for the changes to take effect. Claude Code loads plugin definitions at startup, so any install or uninstall during a session won't be reflected until you quit and reopen.
Team-Wide Configuration
If you want everyone on a team to have the same plugins without each person running install commands, you can pre-configure them in the project's .claude/settings.json:
{"extraKnownMarketplaces": {"my-marketplace": {"source": {"source": "local","path": "/path/to/my-toolkit"}}},"enabledPlugins": {"code-review@my-marketplace": true,"jira@my-marketplace": true}}
Check this file into version control and every team member who clones the repo gets the plugins automatically — no manual install step needed.
Updating Plugins During Development
The development loop takes a bit of getting used to. When you make changes to a command or agent, Claude Code doesn't pick them up unless you bump the plugin version and run an update:
# Bump version in .claude-plugin/plugin.json first, then:claude plugin marketplace update my-marketplaceclaude plugin update code-review@my-marketplace
If the update command reports "already at latest" (which happens if you forgot to bump the version), uninstall and reinstall:
claude plugin uninstall code-review@my-marketplaceclaude plugin install code-review@my-marketplace
Then restart Claude Code. It's a bit manual right now — the tooling around this is one of the rougher edges of the plugin system — but it works reliably once you internalize the pattern.
What's Worth Turning Into a Plugin
Not every slash command needs to be a plugin. Single-purpose commands that only make sense in one project belong in .claude/commands/, checked into that project's repo. The plugin layer makes sense when:
- You want to share commands across multiple projects
- A workflow is complex enough to warrant multiple agents
- You want versioning and update semantics
- You're distributing commands to a team who shouldn't have to know how they're built
For complex multi-step workflows — things like "analyze this MR, generate a review report, implement approved changes, and draft rejection comments for the rest" — the plugin model is the right level of abstraction. You get clean separation between stages, independent agent contexts, and a single installable unit.
Wrapping Up
The slash command system I covered last time is a great starting point. The plugin marketplace is the next step when those commands need to grow up — getting versioned, shared, and composed with agents and skills into something that feels closer to a workflow automation platform than a collection of prompt files.
The ecosystem is still new and the conventions are still solidifying, but the core primitives are there. If you've already built a few useful slash commands, the jump to packaging them as a plugin is smaller than it looks.
