context-plugins
v0.12.0
Published
Install a plugin from a plugin marketplace into Claude Code, Cursor, VS Code, and Codex with one command.
Maintainers
Readme
context-plugins
Install a plugin from a plugin marketplace into every AI coding assistant on the machine — Claude Code, Cursor, VS Code (Copilot), and Codex — with one command.
npx context-plugins install <plugin>For example, to install the PayPal SDK plugin:
npx context-plugins install paypalnpx context-plugins list shows every plugin the marketplace offers. Assistants that aren't
installed are skipped. Nothing is installed globally; npx runs the CLI from a cache.
You can also install a plugin that is not in the marketplace at all — a folder on your own machine, a GitHub repository that is itself a plugin, or a zip or tarball of one:
npx context-plugins install ./my-plugin
npx context-plugins install acme/my-plugin
npx context-plugins install https://acme.com/my-plugin.zipSee Installing from a folder, a repository or an archive.
Requirements
- Node.js 18 or newer — the only requirement for the CLI itself.
- At least one assistant. Each has its own prerequisite: Claude Code needs the
claudeCLI onPATH, Cursor needs~/.cursor, VS Code needs its user directory, Codex needs thecodexCLI onPATH. Missing ones are skipped, so an install succeeds as long as one is present. gitis optional — it makes fetching faster; without it the CLI uses the GitHub API instead.
Commands
context-plugins install <plugin> [options] # install into the assistants you choose
context-plugins install <path> [options] # install a plugin from a folder on this machine
context-plugins install <owner/repo> [options] # install a plugin from a GitHub repository
context-plugins install <url.zip> [options] # install a plugin from a zip or tarball
context-plugins uninstall <plugin> [options] # remove it again
context-plugins update # refresh everything already installed
context-plugins list # what the marketplace offers
context-plugins installed # what this machine has
context-plugins doctor # check this machine can install
context-plugins telemetry [status|enable|disable] # anonymous usage data, see belowOptions
| Option | Default | Description |
| -------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| --repo <owner/repo> | the bundled marketplace | Install from a different marketplace |
| --ref <branch\|tag\|sha> | main | Version to install from |
| --marketplace <name> | read from marketplace.json | Marketplace name |
| --targets <list> | ask | claude, cursor, vscode, codex, or all. On install/uninstall it picks the editors and skips the prompt; on installed it lists only what is recorded for them |
| -y, --yes | off | Accept every detected assistant without asking |
| --force | off | Replace a plugin from a different marketplace; on uninstall, drop an unconfirmed record |
| --long | off | Show plugin descriptions in list |
| --json | off | Machine-readable output for list / installed / doctor |
| --verbose / --quiet | off | More or less progress detail |
| -h, --help | — | Show usage and exit |
| -v, --version | — | Print the version and exit |
With --json, stdout carries the payload and nothing else — warnings and --verbose detail go
to stderr, so ... --json | jq is safe to script against. The commands that read installed.json show only what
this version understands: anything in it they cannot read — a whole entry, or a single target of
one — is named in a warning and left on disk for the version that owns it.
Environment equivalents: CP_PLUGIN, CP_REPO, CP_REF, CP_MARKETPLACE.
GITHUB_TOKEN raises the GitHub API rate limit. CP_STATE_DIR moves the state directory.
CP_TELEMETRY=off or DO_NOT_TRACK=1 turns off usage data.
Defaults can also be kept in a .contextpluginsrc file, in the current directory or your home
directory:
{ "repo": "your-org/plugin-marketplace", "telemetry": false }Choosing where to install
install looks for each assistant and asks before touching the ones it finds:
[Harnesses]
? Install into Claude Code? [Y/n] y
? Install into Cursor? [Y/n] n
? Install into VS Code? [Y/n] yOnly the ones you accept are installed. Assistants that aren't detected are never offered, and the plugin is downloaded after you answer — decline everything and nothing is fetched, written, or recorded.
Detection is a directory check, not a true install check: Claude Code and Codex are found by
looking for the claude and codex CLIs on PATH, but Cursor and VS Code are found by the
presence of their user directories.
A leftover directory from an uninstalled editor still counts as present, and an editor that has
never been launched may not be found yet.
The question is skipped when the answer is already known: with --targets, with -y, during
update (which reuses your earlier choices), and in a non-interactive shell such as CI, where it
falls back to every detected assistant rather than waiting on input.
Installing from a folder, a repository or an archive
install also takes a plugin that no marketplace lists: a directory on this machine, a GitHub
repository — or a folder inside one — that is itself a plugin, or a .zip or .tar.gz of one.
npx context-plugins install ./my-plugin # relative to where you are
npx context-plugins install ~/dev/my-plugin # absolute, or under your home
npx context-plugins install . --targets claude # the folder you are in
npx context-plugins install acme/my-plugin # the repository is the plugin
npx context-plugins install acme/monorepo/tools/foo # a folder inside it
npx context-plugins install acme/[email protected] # at a tag, branch or commit
npx context-plugins install https://github.com/acme/monorepo/tree/v2/tools/foo
npx context-plugins install https://acme.com/my-plugin.zip # an archive at a URL
npx context-plugins install ./my-plugin.tar.gz # or one you already have
npx context-plugins install https://acme.com/mono.zip#tools/foo # a folder inside itWhich one you meant is read off the argument, so there is no extra flag: anything starting with
., /, ~ or a drive letter is a path, an http or https URL whose path ends in .zip,
.tar.gz, .tgz or .tar is an archive, anything holding a / after that is a repository, and a
plain name is a plugin in the marketplace as it has always been. An @ref on a repository wins over --ref,
on a URL or an scp address as much as on the short owner/repo form.
Of the links github.com hands out, the one for a folder - .../tree/<ref>/<folder> - is the one
to copy. A link to a file (/blob/, /raw/) or to a page such as issues is refused by name
rather than read as a folder that is not there. A release asset and a Download ZIP link both end
in an archive extension, so both install as archives.
An archive is opened where you would expect: if its plugin sits at the root, that is the plugin; if
everything is inside one wrapping folder — which is how every archive GitHub builds is shaped — that
folder is unwrapped for you. Name a folder inside it after a #, as the page showed it to you:
.../archive/refs/heads/main.zip#tools/foo works without your having to know the wrapper's name.
Either way the plugin needs a manifest — .claude-plugin/plugin.json, or the Cursor or root
equivalent — and the name in it is what the plugin is called. The folder's or the repository's
own name is not used, so renaming either does not rename the plugin.
A few things worth knowing:
- You are asked first. A plugin can run commands through its hooks and MCP servers, so a
source outside the marketplace is confirmed before anything at all is read, fetched or copied.
The question names the source you typed rather than the plugin — nothing has been read yet,
which is what makes declining cost nothing.
-yskips it. - It is a snapshot. The files are copied as they are now.
context-plugins updatere-takes it — re-reading the folder, re-fetching the repository at the ref the row recorded, or downloading the archive again — so editing a plugin and runningupdateis the loop. A folder or an archive you have since moved or deleted is reported and skipped rather than failing the run; install it again from its new home, or uninstall it. - An archive is fetched with no credential. Nothing is sent that could identify you, so a private archive is not installable by URL: download it yourself and install the file. A link that expires — a presigned one, say — works while it works, query string and all.
- Prefer https. An
http://link installs, but you are warned first: nothing checks what arrives over it, and a plugin can run commands. Anhttps://link is never followed down to plainhttp— a redirect that tries ends the download. - What comes out of an archive is checked. A file that would land outside the plugin's folder, a name Windows cannot hold, or the same name twice (in any case) ends the install with nothing written; links are skipped, and their absence is reported. An archive is refused above 200 MB downloaded, 1 GB unpacked, 64 MB in any one file, or 50,000 files.
- GitHub's
.zipcarries no permissions, so a hook that has to be executable arrives without the bit. Its.tar.gzof the same commit does carry it, and is the link to prefer. A zip you made yourself on Linux or macOS keeps its modes, and they are applied — Windows ignores the bit either way. - Claude Code and Codex need a marketplace, so one is generated at
~/.context-plugins/marketplace/holding every plugin you installed this way. It appears once inclaude plugin marketplace listandcodex plugin marketplace list, ascontext-plugins-local, and goes away when the last such plugin is uninstalled from both. - Uninstall by name, not by path or repository:
context-plugins uninstall my-plugin. Your source folder is never touched. - A marketplace is still named by
--repo.install acme/plugin-marketplacereads that repository as a plugin and tells you it is a marketplace;install <plugin> --repo acme/plugin-marketplaceis how you install from one.
What it does per assistant
| Assistant | Mechanism | Location |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| Claude Code | Adds the marketplace, or updates it if you already had it, then claude plugin install <plugin>@<marketplace> --scope user | Managed by Claude Code |
| Cursor | Copies the plugin folder into the local-plugin directory | ~/.cursor/plugins/local/<plugin>/ |
| VS Code | Copies the folder to the state directory and registers it in chat.pluginLocations | ~/.context-plugins/vscode/<plugin>/ |
| Codex | Adds the marketplace, or upgrades it if you already had it, then codex plugin add <plugin>@<marketplace> | Managed by Codex (~/.codex, or $CODEX_HOME) |
Everything is installed for the current user, so it is available in every project you open.
After installing, reload the editor: Ctrl+Shift+P (Cmd+Shift+P) → Developer: Reload Window.
In Claude Code, run /reload-plugins to load the plugin without restarting — or start a new
claude session. In Codex, start a new codex session.
settings.json is edited as text and never reparsed, so comments and trailing commas survive —
and it is backed up to settings.json.bak-<timestamp> before any change. If the file is shaped in
a way the edit cannot safely splice into, the install says so and prints the one line to paste,
rather than reporting a change it did not make.
Where things live
~/.context-plugins/
installed.json what is installed, and from which marketplace
telemetry.json a random machine id and your telemetry choice
vscode/<plugin>/ the copy VS Code points at
marketplace/ a generated marketplace, only if you installed from a folderTelemetry
When install, update, or uninstall finishes, the CLI sends one anonymous event per plugin
and editor, so the marketplace maintainers can see which plugins people use and where. Each event
carries:
- the plugin id and the editor it went into; for a failure, the step it failed at and whether that was a usage error or a crash, never the message
- the marketplace, only when it is the built-in one; any
--repoyou pass is reported ascustom - the command (
install,update, oruninstall), whether the editors were named with--targets, how long the install took, and whether the run was interactive or in CI - OS, CPU architecture, Node major version, and CLI version
- a random id for this machine, kept in
~/.context-plugins/telemetry.json - an approximate location (city, region, and country) that Mixpanel derives from the address the request came from; the address itself is discarded at ingestion and never stored
Each request also carries the Mixpanel project token, a public routing key that says which project the events belong to. It never includes file paths, usernames, hostnames, IP addresses, error messages, environment variables, or anything from the plugin itself. A notice is printed on stderr the first time anything is sent.
Turn it off in any of these ways; the first that applies wins:
export DO_NOT_TRACK=1 # the cross-tool convention
export CP_TELEMETRY=off # this CLI only
echo '{ "telemetry": false }' > ~/.contextpluginsrc
npx context-plugins telemetry disable # remembered in telemetry.jsoncontext-plugins telemetry status shows what is in effect. CP_TELEMETRY=log prints every event
to stderr instead of sending it, so you can see exactly what would leave the machine.
Something not working?
doctor checks everything an install depends on and says which part is unhappy:
$ npx context-plugins doctor
# example output from Linux - versions, paths, and counts will differ on your machine
Environment
✓ Node.js v20.11.0
✓ git git version 2.43.0
Editors
✓ Claude Code claude on PATH
✓ Cursor ~/.cursor
! VS Code not installed (looked in ~/.config/Code/User)
Marketplace
✓ Reachable raw.githubusercontent.com
✓ Registry context-plugins, 29 plugins
✓ API budget 59 of 60 requests left
Local state
✓ State directory ~/.context-plugins (writable)
✓ Installed 2 plugins
✓ Telemetry enabledThe VS Code path on that last Editors line is platform-specific:
| Platform | VS Code user directory |
| -------- | ------------------------------------------------------- |
| macOS | ~/Library/Application Support/Code/User |
| Linux | ~/.config/Code/User (or $XDG_CONFIG_HOME/Code/User) |
| Windows | %APPDATA%\Code\User |
✓ fine, ! works but worth knowing, x blocks an install. It exits non-zero
only when something blocks, so it can be used in a script; add --json for machine-readable
output.
The live plugin list and count are at the Context Plugins directory, or run npx context-plugins list.
Troubleshooting
| Symptom | Cause / fix |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 'claude' CLI not on PATH - skipping | Claude Code isn't installed, or its CLI isn't on PATH. Other assistants still install. |
| GitHub API request failed (403) | Unauthenticated API limit (60/hour) with no git available. Install git, or set GITHUB_TOKEN. |
| Could not determine the marketplace name | The repository has no .claude-plugin/marketplace.json. Pass --marketplace <name>. |
| Claude Code would not register '<name>' from <source> | Claude refused the marketplace and said why on the same line - most often a marketplace.json it will not parse. The run stops there rather than reporting the plugin missing from something that was never added. |
| Claude Code already has a marketplace named '<name>', from <other-repo> | An unrelated marketplace occupies that name. Remove it with claude plugin marketplace remove <name>, then re-run. |
| 'codex' CLI not on PATH - skipping | Codex isn't installed, or its CLI isn't on PATH. Other assistants still install. |
| Codex already has a marketplace named '<name>', from <source> | An unrelated marketplace occupies that name in Codex. Remove it with codex plugin marketplace remove <name>, then re-run. |
| '<plugin>' is not listed in <marketplace> | Wrong id, or the plugin was renamed upstream — the message suggests the closest match, and list shows them all. |
| A plugin fails during update | Usually a plugin renamed upstream, so the recorded id no longer exists: uninstall <old-id>, then install <new-id>. |
| Nothing was installed in <assistant> - cleared that from the record | The record claimed an assistant that no longer had the plugin (removed by hand, or renamed upstream). The record is now correct. |
| Still recorded for <assistant> | Nothing on this machine could confirm either way - usually the assistant is not installed here. Re-run with --force to drop it from the record anyway. |
| <owner/repo> has no plugin manifest, but <owner/repo> is a marketplace | You pointed at a marketplace and spelled it as a plugin. Use --repo <owner/repo> install <plugin>, or list --repo <owner/repo> to see what it offers. |
| <source> does not look like a plugin | No .claude-plugin/plugin.json (or the Cursor or root equivalent) there. Check the folder or the owner/repo/folder you named, and that the ref you asked for has it. |
| '<folder>' is not a usable folder name | A folder inside a repository may hold letters, digits, dots, dashes and underscores. Anything else could change the meaning of the request this tool makes for it. |
| '<url>' is a link to a file, not to a plugin | A /blob/, /raw/, /blame/ or /edit/ link names a file. Copy the /tree/ link GitHub shows for the folder holding the plugin, or write owner/repo/folder. |
| '<word>' in '<url>' is a github.com view | A github.com page - issues, releases, actions and the like - rather than a folder in the repository. |
| <path> is where installing '<plugin>' would write | The source folder is one of the places this install copies to. Move the plugin somewhere else — installing it there would delete it before reading it. |
| '<plugin>' is already installed from a different source | The same plugin id from another marketplace, folder or repository; they share one destination folder. Uninstall the first, or --force to replace it. |
| <settings.json> names <path> in a form this tool did not write | A chat.pluginLocations entry was hand-edited, so it cannot be spliced out safely. Delete that line yourself - the uninstall did everything else it could. |
| Plugin doesn't appear after install | Reload the editor window. For VS Code, check the entry in chat.pluginLocations. |
| Could not edit <settings.json> - add this entry yourself | The file has no JSON object to splice into. Paste the printed line into settings.json; the plugin files are already in place. |
License
MIT
