claude-modules
v1.0.5
Published
Composable plugin modules for Claude Code, applied across user/project/local scopes
Maintainers
Readme
claude-modules
Composable, portable plugin bundles for Claude Code.
Docs & overview — full guide, command reference, and a plain-English tour of what claude-modules does.
Every module is:
- Self-sufficient — it carries its plugins and the marketplaces they come from, so it works on a machine that's never seen them.
- Composable —
fullstack=backend+frontend, declared once and resolved every time, not retyped. - Reusable — the same module applies to any repo, at any scope.
- Transferable —
exportpacks a module and its whole composition chain into one.tar.gz;importunpacks it on another machine.
Example workflow
Claude Code enables plugins one at a time, per scope, across three separate settings.json files.
claude-modules lets you bundle them into named modules and switch between them with a single
command. Build modules plugin by plugin, then compose them into the role you actually switch into:
# A base layer every stack builds on
claude-modules create base-dev
claude-modules plugin install base-dev context7@claude-plugins
claude-modules plugin install base-dev code-simplifier@claude-plugins
# A Quarkus-specific module
claude-modules create quarkus
claude-modules plugin install quarkus jdtls-lsp@claude-plugins
claude-modules plugin install quarkus quarkus-agent@claude-plugins
# A frontend-specific module
claude-modules create fe
claude-modules plugin install fe modern-web-guidance@claude-plugins
claude-modules plugin install fe playwright@claude-plugins
claude-modules plugin install fe typescript-lsp@claude-plugins
claude-modules plugin install fe frontend-design@claude-plugins
# Compose them into one role, declared once
claude-modules create full-dev --compose base-dev --compose quarkus --compose fe
claude-modules enable full-dev --scope project --save # transitively pulls in all three, every timeAlready have a repo configured the way you like — by hand, or through
/plugin?create <module> --from-scope <scope>captures it in one shot instead of typing everyplugin install. See Quick start below.
Don't need full-dev as a permanent unit? Name the modules directly on one enable call instead —
that's an ad-hoc union, good for one-off combinations you won't reuse:
claude-modules enable base-dev quarkus fe --saveAnd when you're switching roles rather than layering on top of what's active, add --only to make
the scope exactly the given modules instead of adding to it:
claude-modules enable full-dev --scope user --only --save
claude-modules enable base-dev --scope user --only # Monday: quarkus/fe plugins go quietLater, after a module's plugin list changes: claude-modules reload --scope project.
Modules aren't limited to coding roles, either — anything you switch into works the same way. Register the marketplace it needs, build the module, and enable it exclusively so it's the only thing active, unrelated dev modules included:
claude-modules marketplace add anthropics/financial-services-plugins --name claude-for-financial-services
claude-modules create investing
claude-modules plugin install investing wealth-management@claude-for-financial-services
claude-modules plugin install investing private-equity@claude-for-financial-services
claude-modules plugin install investing investment-banking@claude-for-financial-services
claude-modules enable investing --scope user --only --save # only investing is active nowWhy not just use /plugin?
| | Claude Code /plugin | claude-modules |
| ------------------- | -------------------------------------------------- | ------------------------------------------ |
| Unit of work | one plugin at a time | a named bundle of plugins |
| Grouping | none — enabledPlugins is a flat map | modules, which compose into bigger modules |
| Reuse across repos | re-enable each plugin, per repo | enable backend |
| Another machine | re-add marketplaces, re-enable plugins | export → import, one file |
| Switching roles | hand-edit up to three settings.json files | enable backend --only |
| Auditing what's on | read three files and apply precedence in your head | status — exit-coded, --json |
| Sharing with a team | commit .claude/settings.json wholesale | commit .claude-modules, then reload |
There's a secondary benefit too: fewer enabled plugins means fewer tools Claude has to choose between on every turn — see Concepts → Context budget for the cost this addresses and a caveat on MCP-heavy plugins.
Installation
npm install -g claude-modules # global CLIOr run it without installing:
npx claude-modules --helpFrom source:
git clone https://github.com/egelev/claude-modules.git
cd claude-modules
npm install
npm run build
npm link # puts `claude-modules` on your PATHConfiguration
| Variable | Default | What it points at |
| --------------------- | ------------------- | ------------------------------------------------------------------------ |
| CLAUDE_MODULES_HOME | ~/.claude-modules | Where your modules and the marketplace registry live |
| CLAUDE_CONFIG_DIR | ~/.claude | Claude Code's own home — read for the user scope and its plugin caches |
Quick start
Start from a repo you've already configured by hand, or through /plugin:
cd ~/projects/api-service
claude-modules create backend --from-scope localCreated module 'backend' from local scope (/repo/.claude/settings.local.json) with 3 plugin(s), 1 marketplace(s), and 0 composed module(s).That captured what was already enabled there. Check it:
claude-modules list
claude-modules info backendbackend (v1.0.0) — 3 plugin(s) enabled, 1 marketplace(s)backend (v1.0.0): 3 plugin(s):
typescript-lsp@claude-plugins (enabled)
postgres-mcp@claude-plugins (enabled)
code-review@claude-plugins (enabled)
backend: 1 marketplace(s):
claude-plugins: {"source":{"source":"github","repo":"anthropics/claude-plugins"}}Now apply it — here to project scope, so it's shared with the team, and --save so you don't have
to retype it:
claude-modules enable backend --scope project --saveEnabled module(s) [backend] in project scope (/repo/.claude/settings.json); 1 marketplace(s) known.
Enabled plugin(s):
- code-review@claude-plugins (local)
- postgres-mcp@claude-plugins (local — not cached by Claude Code — run 'claude plugin install postgres-mcp@claude-plugins --scope user -y')
- typescript-lsp@claude-plugins (local)
- code-review@claude-plugins (project)
- postgres-mcp@claude-plugins (project — not cached by Claude Code — run 'claude plugin install postgres-mcp@claude-plugins --scope user -y')
- typescript-lsp@claude-plugins (project)
Saved module selection to /repo/.claude-modules.
Modules active in project scope: [backend].These plugins are listed twice because the report covers every scope in effect, not just the one
written — they're still enabled in local too, where they started. postgres-mcp is flagged as not
cached because enabling a plugin isn't the same as caching it; --install fixes that. See
Applying modules → The report for the full format.
From here, claude-modules status audits the result at any time, and exits non-zero if something
has drifted — see the docs.
Changes apply to the next session — an already-open one needs
/reload-plugins(or a restart) to pick them up. See Applying modules for why, every command reminds you in its own output.
How it works
A module is a directory under $CLAUDE_MODULES_HOME/modules/<name>/ holding a settings.json:
{
"version": "1.2.0",
"enabledPlugins": { "typescript-lsp@claude-plugins": true },
"extraKnownMarketplaces": {
"claude-plugins": { "source": { "source": "github", "repo": "anthropics/claude-plugins" } }
},
"composedModules": ["base"]
}enabledPlugins and extraKnownMarketplaces match the exact shape Claude Code itself uses.
composedModules and version are this tool's own additions.
Scopes are the same three Claude Code uses, resolved with local > project > user:
| Scope | File | Needs a git repo? |
| --------- | ------------------------------------------------------------------------ | ----------------- |
| user | ~/.claude/settings.json (or $CLAUDE_CONFIG_DIR/settings.json) | no |
| project | <repo_root>/.claude/settings.json | yes |
| local | <repo_root>/.claude/settings.local.json, or <cwd>/... outside a repo | no |
--scope defaults to local.
Modules combine two ways. Naming several on one call (enable backend frontend) unions them for
that call only. Declaring composedModules makes the relationship part of the module itself, pulled
in transitively by every future command. Both are covered in
Concepts → Composition.
Module lists remember which modules you applied, so reload can reapply them and status can
detect drift. One per scope:
| Scope | Module list | |
| --------- | ----------------------------------- | -------------------------------------------- |
| user | $CLAUDE_MODULES_HOME/user.modules | global |
| project | <repo_root>/.claude-modules | commit it — shared with the team |
| local | <repo_root>/.claude-modules.local | gitignore it — personal to your checkout |
Commands
| Command | What it does | |
| ----------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------- |
| list | List every module with its plugin and marketplace counts | docs |
| info <module> | Show one module's plugins, marketplaces, and composition | docs |
| create <module> | Create a module — empty, seeded from a scope, or composing others | docs |
| remove <module> | Delete a module | docs |
| compose add <module> <composed...> | Make a module build on others | docs |
| compose remove <module> <composed...> | Stop building on others | docs |
| plugin install <module> <plugin>@<mp> | Enable a plugin inside a module | docs |
| plugin uninstall <module> <plugin>@<mp> | Disable a plugin inside a module | docs |
| marketplace add <spec> | Register a marketplace, globally or on a module | docs |
| marketplace remove <name> | Unregister a marketplace | docs |
| marketplace list | List registered marketplaces | docs |
| enable <module...> | Apply modules to a scope | docs |
| disable <module...> | Turn a scope's copy of those plugins off | docs |
| disable-all | Turn every plugin in a scope off | docs |
| reload | Re-apply a scope's saved module list | docs |
| update [module...] | Update modules' marketplaces then plugins to their latest versions | docs |
| status | Audit a scope; exit-coded for CI | docs |
| export <module> | Pack a module and its composition chain into a .tar.gz | docs |
| import <archive> | Unpack one on another machine | docs |
| completions <bash\|zsh> | Print a tab-completion script for your shell | docs |
Global options
| Flag | Effect |
| ----------------- | ------------------------------------------------------------------------------- |
| -h, --help | Show help. Works after any command: claude-modules plugin install --help |
| -v, --version | Print the installed version |
| --verbose | Enable debug logging |
| --dry-run | Preview a mutating command's effect — writes nothing, runs no external commands |
--dry-run is supported by all 15 mutating commands, and is a no-op on the five that never write
(list, info, status, marketplace list, completions). Running a bare group name —
claude-modules plugin, marketplace, or compose — prints that group's usage.
Known limitations
- No token-cost preview. Modules report plugin counts, not tokens. Claude Code knows the number but offers no way to aggregate it per module yet. → details
- No version pinning. A module records which plugins to enable, not which versions. Treat modules as portable role definitions, not lockfiles. → details
- Marketplace sources are snapshots. Resolved sources are copied onto the module; later registry changes don't propagate. → details
- Plugin configuration isn't captured. Per-plugin
userConfigis deliberately excluded so a committed module can't become a secret-exfiltration path. → details - Composition overrides stop after one level. An explicit
falsebeats a directly composed child, but not a grandchild. → details
Contributing
Development setup, the pull-request workflow, and the release steps live in CONTRIBUTING.md.
License
MIT © Emil Gelev
