If you use Claude Code across more than one repo, you've probably hit this: you write a genuinely useful slash command or a sharp little agent in one project, and then a week later you're in a different project wishing you had it. So you copy the files over. Then you tweak one, forget to copy the change back, and now you have two slightly different versions drifting apart.
Plugins fix this. A plugin is just a folder that bundles your Claude Code extensions so you can install the same thing everywhere and update it in one place. This post walks through what they bundle, how to put several of them in a single repo, and how to turn that repo into your own private marketplace on GitHub.
Everything here was checked against the current Claude Code docs (code.claude.com/docs). Commands and file names are accurate as of June 2026.
What a plugin actually is
A plugin is a directory with a small manifest and whatever capabilities you want to ship. That can include:
Skills — model-invoked instructions Claude reaches for on its own
Agents — custom subagents with their own prompt, tools, and model
Hooks — handlers that fire on events like
PostToolUseMCP servers — external tool integrations
LSP servers, background monitors, and a
bin/of executables for the more involved cases
The manifest lives at .claude-plugin/plugin.json and is tiny:
{
"name": "my-toolkit",
"description": "My everyday Claude Code helpers",
"version": "1.0.0",
"author": { "name": "Your Name" }
}
One thing worth knowing up front: the only thing that goes inside .claude-plugin/ is plugin.json. Everything else — skills/, agents/, hooks/, .mcp.json — sits at the plugin's root, not inside .claude-plugin/. This trips up almost everyone the first time.
So a plugin with one skill and one agent looks like this:
my-toolkit/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ └── code-review/
│ └── SKILL.md
└── agents/
└── release-notes.md
The name in the manifest becomes a namespace. A skill in a plugin called my-toolkit is invoked as /my-toolkit:code-review. That namespacing is the whole reason two plugins can both ship a code-review skill without colliding.
You can test any plugin without installing it by pointing Claude Code at the folder:
claude --plugin-dir ./my-toolkit
After edits, run /reload-plugins in the session to pick them up without restarting.
Why this beats .claude/
You can keep skills and hooks loose in a project's .claude/ directory, and for a one-off project that's the right call — short names, no ceremony, quick to iterate. The trade-off is that they live and die in that one repo.
Reach for a plugin the moment you want the same capability in more than one project, want versioned updates, or want to hand it to a teammate. The mental model is simple: .claude/ is for "this project," plugins are for "me, or my team, everywhere."
Several plugins, one repo
Here's the part people don't realize: you don't need a repo per plugin. One repo can hold as many as you want, each in its own folder. A natural layout is a plugins/ directory:
my-claude-stuff/
├── .claude-plugin/
│ └── marketplace.json
└── plugins/
├── code-tools/
│ ├── .claude-plugin/plugin.json
│ └── skills/...
├── writing-tools/
│ ├── .claude-plugin/plugin.json
│ └── skills/...
└── infra-tools/
├── .claude-plugin/plugin.json
└── agents/...
The thing tying them together is that marketplace.json at the top, which brings us to the actually fun part.
GitHub as your personal marketplace
A marketplace is just a catalog file — .claude-plugin/marketplace.json at the root of a repo — that lists your plugins and where to find them. Put that file in a GitHub repo and you've built a marketplace. No service to run, no account to register. GitHub is doing the hosting.
For the layout above, the catalog lists each plugin with a relative path:
{
"name": "my-plugins",
"owner": { "name": "Your Name" },
"plugins": [
{
"name": "code-tools",
"source": "./plugins/code-tools",
"description": "Review, refactor, and test helpers"
},
{
"name": "writing-tools",
"source": "./plugins/writing-tools",
"description": "Drafting and editing skills"
},
{
"name": "infra-tools",
"source": "./plugins/infra-tools",
"description": "Deploy and ops agents"
}
]
}
Relative ./ sources resolve from the marketplace root (the directory holding .claude-plugin/), and they only work when the marketplace is added over git — which is exactly what we're doing. If you have a lot of plugins under one folder, you can set metadata.pluginRoot to "./plugins" and then write "source": "code-tools" instead of the full path.
Push that repo to GitHub, and anyone (including you, on any machine) adds it with one line:
/plugin marketplace add your-name/my-claude-stuff
Then install whichever plugins they want:
/plugin install code-tools@my-plugins
/plugin install writing-tools@my-plugins
Note the @my-plugins suffix — that's the marketplace name from the catalog, not the repo name. When you ship changes, push to the repo, and users refresh with /plugin marketplace update.
A couple of practical notes:
Versioning is a choice. Set a
versionand people only get updates when you bump it. Omit it and, on a git-hosted marketplace, every commit counts as a new version. For a personal repo, leaving it off and just pushing is the low-friction path; for a team, pinning a version is friendlier.Private repos work too. Point the marketplace at a private GitHub repo and it stays internal to whoever has access — a clean way to share a house style across a team without publishing anything.
The shape of the workflow
Once this is set up, the loop gets nice. You write a skill in a project where you needed it. When it proves itself, you move it into a plugin in your marketplace repo and push. Every other project picks it up with a single /plugin marketplace update. The thing you built once is now everywhere, and there's exactly one copy to maintain.
That's the whole pitch. A folder, a JSON file, and a GitHub repo turn your personal Claude Code tweaks into something you actually reuse instead of re-copy.