Skip to content

Plugin CLI reference

Every claude plugin and claude plugin marketplace subcommand, the /plugin and /reload-plugins session commands, and the flags that sideload a plugin.

Plugins can be managed from two places: the claude plugin command in your shell (good for scripts, CI and dotfiles), and the /plugin and /reload-plugins commands inside an interactive session. This page lists both, with flags, outputs and exit codes, plus the two launch flags that load a plugin for a single session.

The flag tables cover the options you will actually reach for. Your installed version is the source of truth: claude plugin --help lists the subcommands it has, and claude plugin <subcommand> --help lists every option.

For the step-by-step version of these operations see Install plugins; for what they change on disk see How plugins load; and for error messages see Plugin troubleshooting.

Conventions for claude plugin

claude plugins is an alias for claude plugin. Across all subcommands:

  • Exit codes: 0 for success, 1 for failure. validate adds 2 for an internal error; eval has its own set.
  • Plugin arguments: <plugin> means name or name@marketplace. Use the qualified form when two marketplaces offer the same name. configure only accepts the qualified form.
  • Scopes: --scope accepts user, project or local and names the settings file to write. update additionally accepts managed.

Authoring commands

plugin init

Scaffolds a plugin at ~/.claude/skills/<name>/, which loads in your next session as <name>@skills-dir with nothing to install. Alias: new.

claude plugin init invoice-helper --with skills hooks --description "Draft invoice chasers"
FlagPurpose
--description <text>Manifest description
--author <name>Defaults to git config user.name
--author-email <email>Defaults to git config user.email
--with <components...>Starter files for any of skills, agents, hooks, mcp, lsp, output-style, channel
-f, --forceOverwrite an existing .claude-plugin/ at the target

<name> becomes both the folder name and the manifest name. There is no flag for another location; to scaffold inside a project, follow Create a plugin. On success the command validates its output and prints Created plugin "<name>" at ~/.claude/skills/<name>, the id it will load as, and the claude plugin disable command to switch it off. It exits 1 without writing for an unknown --with value, an existing scaffold without --force, or a managed policy that blocks skills-directory plugins.

plugin validate

Validates a plugin manifest, a marketplace manifest, or loose skills, agents and commands, with an exit code suitable for CI.

claude plugin validate ./invoice-helper --strict
FlagPurpose
--strictTreat warnings (unknown fields, missing metadata) as failures
--jsonEmit the report as one JSON object, same exit codes. v2.1.259 or later

What gets validated when you pass a folder:

  1. .claude-plugin/marketplace.json if present;
  2. otherwise .claude-plugin/plugin.json;
  3. otherwise component files, chosen by folder name (v2.1.233 or later): a folder named skills, agents or commands validates its contents; a folder named .claude validates those three inside it; any other folder validates those three under its .claude.

If both manifests exist side by side, the marketplace and the plugin's manifest and components are all checked (v2.1.289 or later).

Symlinks inside the target are not followed. A linked skills, agents or commands folder at the plugin or .claude root produces a warning that nothing in it was read; linked entries inside those folders are skipped with a per-folder count of what a session would have loaded; and if the folder you named (or its parent .claude) is itself a symlink, that is an error and nothing is checked.

Things a run does not read: a SKILL.md at a plugin's root; plugins that a marketplace lists in other folders (validate each plugin folder separately). A CLAUDE.md at a plugin root triggers a warning.

ExitVerdictMeaning
0Validation passed / Validation passed with warningsLoads; with --strict, also warning-free
1Validation failed / Validation failed (--strict treats warnings as errors)Error, or warning under --strict
2Unexpected error during validation: <reason>Validator itself failed, e.g. unreadable path

The --json report has top-level success, strict, target, manifest (or null with no manifest) and contents, a per-file list with file, errors, warnings and notes. For mods, a gatingHooks list reports, per hook that can refuse an action, its module, pattern, hook and whether it hasCatch (v2.1.290 or later). On exit 2, stdout is empty and the error goes to stderr.

What each manifest check means is covered in the manifest reference and marketplace reference.

plugin details

Shows a loaded plugin's component inventory and estimated token cost. The plugin must be installed, in a skills directory, or passed with --plugin-dir/--plugin-url in the same command. No flags besides --help.

claude plugin details invoice-helper

Output: name, version, description and source, then Component inventory, Projected token cost (always-on tokens added to every session) and, if there are skills, agents or commands, Per-component (rounded) with always-on and on-invoke estimates. Measure plugins explains the figures. An unloaded plugin gets a not-found message suggesting claude plugin list or --plugin-dir, and exit 1.

plugin tag

Creates an annotated git tag <name>--v<version> after checking that plugin.json and any marketplace entry agree on the version. The path defaults to the current folder; the marketplace entry is found by walking up to a .claude-plugin/marketplace.json that lists the plugin.

claude plugin tag plugins/invoice-helper --dry-run
FlagPurpose
--pushPush the tag after creating it
--dry-runShow the plan only
-f, --forceSkip the dirty-tree and existing-tag checks
-m, --message <msg>Annotation; %s is replaced by the version. Default <name> <version>
--remote <name>Remote for --push. Default origin

The dry run prints the plugin name, the version and which file supplied it, the matching entry, the tag name, and the git tag and git push commands. A real run prints Created tag <name>--v<version> then either Pushed to origin or the push command to run. A failed push leaves the local tag in place and exits with an error. Common refusals: no version anywhere, tag exists, dirty working tree. Publish a plugin covers when to tag.

plugin test

Runs a mod's tests without a session, sign-in or network: every *.test.ts and *.test.tsx under the folder (default: current). Exits 1 on any failure. See Test a mod.

claude plugin test ./status-pane-mod

plugin eval

Runs a plugin's eval cases and scores them (v2.1.269 or later). Each case runs several times in an isolated session with only the target plugin loaded, and by default also without the plugin so you can see the difference. Full details are on Plugin evals.

claude plugin eval ./invoice-helper --runs 5 -j 4 --threshold 0.8

The target defaults to the current folder and can be a plugin folder, a single prompt.md or case.yaml, an installed name or name@marketplace, or name@skills-dir. Put it before --tag, --allow-tools or --json, since those consume the following words as values.

OptionPurposeDefault
--runs <n>Runs per case in each armCase's runs, else 3
-j, --concurrency <n>Parallel sessions, 1 to 8 (they share your rate limit)1
--model <model>Model under testCase's model, else ANTHROPIC_MODEL, else the default
--judge-model <model>Model for llm and baseline gradersThe background-task model
--ablation <mode>none or with-withoutDecided per case
--threshold <0..1>Exit 1 if any case scores lower1.0
--max-cost-usd <usd>Stop before the next run at this spend, exit 2, report partial resultsUnlimited
--allow-tools <tools...>Grant tools beyond read-only, e.g. Bash, Edit, "mcp__plugin_<plugin>_<server>__*"
--scaffoldRun each case's scaffold_scriptOff
--trust-pluginSkip the first-run trust prompt (for CI)Off
--mocks <mode>record or offrecord
--eval-dir <dir>Case folder below the pluginManifest's experimental.evals, else evals
--json [path]Result document to stdout or a .json file
--no-publishKeep the HTML report local

Further options (--case, --tag, --output-dir, --report, --allow-real-servers, --keep-temp, --verbose) are in --help.

ExitMeaning
0All cases met the threshold
1A failing case, a load error, or an untrusted plugin folder
2Partial run
130Interrupted
143Terminated

plugin eval init

Creates an eval suite for the plugin in the current folder (v2.1.269 or later). Run it from the plugin root (where .claude-plugin/plugin.json or the skill's SKILL.md lives), or pass --eval-dir.

In a terminal it starts an interactive authoring interview: Claude reads the plugin, asks what it should do well, proposes cases and graders, writes them, then runs them and reviews the grades with you. With --bare or no terminal it writes a blank single-case template instead. Run by Claude from inside a session, it prints the interview instructions for that session to follow.

OptionPurpose
--bareWrite a blank prompt.md and graders/criteria.md for <name>
-i, --interactiveInsist on the interview; fail without a terminal
--eval-dir <dir>Where to write cases. Default: manifest's experimental.evals, else evals

The optional name (required with --bare or without a terminal) must start with a letter or digit and use only letters, digits, ., _ and -. Names Windows cannot store, such as con or a trailing ., are refused everywhere.

Install and lifecycle commands

plugin install

Installs from one of your marketplaces. Alias: i.

claude plugin install invoice-helper@studio-plugins --scope project --config currency=GBP
FlagPurpose
-s, --scope <scope>user (default), project or local
--config <key=value>Set a declared userConfig option; repeat per option. <server>.<key> targets a setting declared by a bundled MCP server instead (v2.1.285 or later)
-y, --yesAccept a displayed install command without the prompt. Ignored when run from inside a Claude Code session. v2.1.229 or later
--accept-command <sha256>Accept exactly the command a previous --json run reported. Not combinable with -y. v2.1.271 or later
--jsonOne JSON result object on the last stdout line. v2.1.268 or later

Most installs never prompt. Entries with a command source, or a headersHelper on their download, first print the command and ask Run this command now? [y/N]. Without a TTY and without -y or --accept-command, the install is refused with exit 1. When Claude runs the command through its Bash tool, -y is ignored, so run it yourself.

Outcomes:

SituationOutputExit
InstalledSuccessfully installed plugin: <id> (scope: <scope>)0
Already therePlugin "<id>" is already installed (scope: <scope>)0
You declined a command-source promptAborted.1
You declined (or could not confirm) a headersHelper promptAborted, noting the command was not run1

The JSON result object

With --json, parse only the last line of stdout, because any marketplace-declared command is printed above it. Always present: command (e.g. install), outcome (ok or failed) and message. Optional fields such as pluginId, scope and failureCode appear when relevant. uninstall, update, enable and disable use the same object with their own extra fields. Usage errors (an invalid --scope, say) print no result line, write the reason to stderr and exit 1.

The marketplace commands add, remove and update emit the same shape, with command set to marketplace-add, marketplace-remove or marketplace-update, plus marketplace and failureCode where relevant:

{"command":"marketplace-update","outcome":"ok","marketplace":"studio-plugins","message":"Successfully updated marketplace: studio-plugins"}

For the reserved name anthropic-plugin-directory, add and remove may print no result line, so check the exit code instead.

Accepting a command non-interactively

When a --json run shows a marketplace-declared command without running it, its failed result includes a shownCommand object with the command text, the plugin and the command's sha256. Re-run from your own terminal with --accept-command <sha256> (the flag does nothing inside a session). The hash only counts for that exact command, plugin and catalogue; if any changed (including through the run's own catalogue refresh) you are shown the command again. shownCommand.acceptCommandMatched: false means the hash you passed no longer matches what is displayed, so review the new command first.

plugin uninstall

Removes a plugin from one scope. Aliases: remove, rm.

FlagPurpose
-s, --scope <scope>user (default), project or local
--keep-dataKeep ~/.claude/plugins/data/<id>/
--pruneAlso remove auto-installed dependencies nothing else needs
-y, --yesSkip the --prune confirmation (required without a TTY)
--jsonJSON result; not combinable with --prune. v2.1.268 or later

Success prints Successfully uninstalled plugin: <name> (scope: <scope>). If it is not installed at that scope you get a Failed to uninstall plugin "<id>": line and exit 1. If that line continues with "<name>" was not uninstalled: and names a settings file, Claude Code could not confirm the scope's settings no longer enable the plugin, so it left everything in place (failureCode: "settings_still_on" in JSON; v2.1.282 or later).

What is deleted. Uninstalling from the last scope also deletes the plugin's saved options and secrets and its data folder, except when --keep-data is passed (data kept), when another installed plugin shares the folder (for instance an id differing only by case), or when the install list cannot be read back afterwards, in which case options, secrets and data are all kept and the result carries savedKept: "install_records_unreadable". JSON output reports keptData, and /plugin shows · data preserved when data stayed.

plugin enable and plugin disable

claude plugin disable invoice-helper --scope local
claude plugin enable invoice-helper
FlagApplies toPurpose
-s, --scope <scope>bothuser, project or local. Auto-detected if omitted
-a, --alldisableDisable every enabled plugin. Not combinable with a name or --scope
--jsonbothJSON result. v2.1.268 or later

For synced plugins pass <name>@synced.

Auto-detection checks local, then project, then user, and uses the first that mentions the plugin. If you name a scope that does not declare it, a scope with higher precedence gets an override written (so disable --scope local switches off a project-enabled plugin just for you); any other scope fails with a message telling you which scope to use.

Enabling or disabling something already in that state prints Plugin "<name>" is already enabled (or disabled) and exits 1. With --json you get "failureCode": "already_in_goal_state" and "alreadyInGoalState": true, so scripts can treat it as success. disable with no name and no --all exits 1 asking you to specify one.

Enable also enables declared dependencies, and fails if one is not installed (printing the install command for each), is blocked by policy, or is set false at a higher-precedence scope. Disable fails if another enabled plugin depends on the target (it names them) or if your organisation requires it as a synced plugin.

plugin update

Moves a plugin to the newest version its marketplace offers. The new version loads next session or after /reload-plugins.

FlagPurpose
-s, --scope <scope>user, project, local or managed. Auto-detected if omitted
-y, --yesAccept a changed command-source command; required without a TTY unless --accept-command is used. v2.1.229 or later
--accept-command <sha256>As for install. v2.1.271 or later
--jsonJSON result. v2.1.268 or later

Without --scope, the most specific scope the plugin is installed at for the current project is used (local, project, user, then managed). Before v2.1.281 it defaulted to user, so project-only installs needed an explicit --scope. managed can be updated but never installed to.

Bare names are matched against installed plugins (v2.1.246 or later); if two marketplaces supply the same name, the command lists the qualified commands to run instead.

Output starts Checking for updates for plugin "<id>"…. If nothing is newer: <name> is already at the latest version (<version>). with exit 0, unless it retries an unfinished dependency install in the cached copy and that fails, giving Failed to update plugin "<id>" and exit 1 (retry behaviour from v2.1.287).

plugin list

claude plugin list --json --available
FlagPurpose
--jsonJSON output
--availableWith --json, also list uninstalled plugins from your marketplaces
--data-size [plugin]With --json, measure data folders (all, or one name@marketplace). A name with no install record exits 1. v2.1.285 or later

Human output is grouped under Installed plugins:, Session-only plugins (--plugin-dir / --plugin-url): (only when those flags precede the subcommand, e.g. claude --plugin-dir ./x plugin list), Skills-directory plugins (.claude/skills/*): and Synced from claude.ai. With nothing at all, it suggests claude plugin install.

JSON is an array with one object per installation:

FieldNotes
idname@marketplace, name@inline, name@skills-dir or name@synced
versionComputed version for installs; manifest version (or unknown) otherwise
scopeuser/project/local/managed for installs, user/project for skills-directory, session, or synced
enabledEffective enabled state after merging settings
installPathLoad folder (absent for in-place marketplace plugins)
readFromFolderFor in-place plugins, the source folder. v2.1.289 or later
folderVersionWith readFromFolder, the version as loaded from there. v2.1.289 or later
installedAt, lastUpdatedISO timestamps; marketplace installs only
projectPathFor project and local scope
mcpServersServer definitions, if any
errors, errorDetailsLoad errors, and structured detail for each (v2.1.268 or later)
notes, noteDetailsNon-fatal warnings, and structured detail (v2.1.268 or later)
hasUserConfigtrue when a loaded plugin declares userConfig; values never included. v2.1.285 or later
projectEnabledWhether the shared project settings enable it. v2.1.285 or later
dataDirSize, dataDirUnreadableWith --data-size: { bytes, human }, or true if unmeasurable. v2.1.285 or later

With --available, the output becomes an object with installed (the array above) and available, each item having pluginId, name, marketplaceName, source, and where known description, version and installCount.

plugin configure

Shows or sets an installed plugin's userConfig options (v2.1.285 or later). Requires the full name@marketplace id.

echo '{"currency": "GBP", "late_fee_days": "30"}' | claude plugin configure invoice-helper@studio-plugins --values-stdin
FlagPurpose
--values-stdinRead a JSON object of single-line string values from stdin and save them; unspecified options keep their values
--jsonWithout --values-stdin: schema, choices, starting inputs, and configured/unconfigured names. With it: saved names and, if readable, unconfigured

Plain output labels each option required/optional, sensitive where applicable, and set/not set, without showing values. JSON includes non-sensitive saved values but never sensitive ones. Saving validates each value against its type, then prints Configuration saved. Restart Claude Code to apply it. An undeclared key or invalid value saves nothing, prints Failed to save configuration: with the reason and exits 1 (JSON adds a refused object with message and, where relevant, option). An unknown id prints No installed plugin has the id "<plugin>".

Bundled MCP server settings are set with plugin install --config or the Configure item in /plugin instead.

plugin prune

Removes auto-installed dependencies that nothing needs any more; never removes plugins you installed yourself. Alias: autoremove.

FlagPurpose
-s, --scope <scope>user (default), project or local
--dry-runList without removing; ends by noting it was a dry run and nothing was removed
-y, --yesSkip confirmation; required without a TTY

Interactively without -y it asks Remove? [y/N]. With -y it prints Removed N auto-installed plugins: <names>. Without a TTY and without -y it lists them, tells you to rerun with -y, and removes nothing. With nothing to do, output begins Nothing to prune. Exit is 0 whatever you answer.

Marketplace commands

claude plugin marketplace <subcommand> follows the same exit codes. Its --scope has no -s short form.

plugin marketplace add

Registers a marketplace and declares it in a settings file, then installs any dependencies your installed plugins were missing.

FlagPurpose
--scope <scope>user (default), project or local
--sparse <paths...>Sparse checkout of just these folders (github and git only)
--claudeaiTreat the argument as the name of a claude.ai-hosted marketplace. v2.1.273 or later
--jsonJSON result; no effect with --claudeai. v2.1.287 or later

How the argument is interpreted:

ArgumentSource typeFetch behaviour
owner/repo, owner/repo#ref, owner/repo@refgithubClone, pinned to ref if given
user@host:path[.git][#ref]gitSSH clone
http(s):// URL ending .git[#ref] or containing /_git/gitClone (Azure DevOps included)
http(s)://github.com/owner/repo or http(s)://gitlab.com/namespace/projectgitClone after appending .git; nested GitLab subgroups work
Any other http(s):// URLurlFetched as a marketplace.json. Append .git to clone instead
./, ../, / or ~/ path to a folderdirectoryRead in place (Windows .\, ..\, C:\ forms too)
Same path forms to a .json filefileRead in place

Hosts whose clone URLs lack .git (AWS CodeCommit, for example) should be declared as a git entry in extraKnownMarketplaces, which clones regardless of suffix.

claude plugin marketplace add cshields/studio-plugins --scope project --sparse .claude-plugin plugins

Prints Successfully added marketplace: <name> (declared in project settings), using the catalogue's own name. Repeat adds print Marketplace '<name>' already on disk, plus where it is declared (exit 0). Unrecognised input prints Invalid marketplace source format. Try: owner/repo, https://..., or ./path (exit 1). A bare host such as git.acme.internal/team/plugins is rejected as invalid shorthand with advice to add https://.

For a claude.ai-hosted marketplace, use the name shown under From claude.ai: in claude plugin marketplace list, e.g. claude plugin marketplace add --claudeai claudeai-organization-library. --scope and --sparse are refused with --claudeai, and because it is tied to your account rather than a settings file, it cannot be shared via project settings.

plugin marketplace list

Prints Configured marketplaces: with a Source: line each, or No marketplaces configured. --json gives an array of string fields: name, source (github, git, url, directory, file or claudeai), repo (github), url (git, url), path (directory, file), ref (when pinned) and installLocation. claude.ai marketplaces carry marketplaceId and organizationUuid instead of installLocation, plus status and scope where recorded.

If your terminal syncs plugins from claude.ai, the text output ends with a From claude.ai: section listing marketplaces available to your account that you have not added (v2.1.273 or later). JSON output omits that section.

plugin marketplace remove

Removes a marketplace declaration. Alias: rm. Takes the marketplace name (as shown by list), not the original source.

Warning: Removing a marketplace from its last declaring scope deletes its cache and uninstalls every plugin you installed from it, along with their saved options, secrets and data where possible. To refresh without losing anything, use plugin marketplace update.

FlagPurpose
--scope <scope>Remove from one scope only. Default: every scope
--jsonJSON result. v2.1.287 or later

Prints Successfully removed marketplace: <name>, plus a list under a line such as Also uninstalled 2 plugins from this marketplace: when relevant. Scoping to a file that does not declare it fails with a message suggesting you omit --scope.

plugin marketplace update

Refreshes one marketplace, or all of them, from source. A marketplace pinned to a ref updates to the latest commit of that ref.

claude plugin marketplace update studio-plugins

Prints Successfully updated marketplace: <name>, or a count such as Successfully updated 3 marketplaces when no name is given. --json (v2.1.287 or later) requires a name.

/plugin in a session

/plugin opens the plugin panel; /plugins and /marketplace are aliases. It only works in interactive terminal sessions; in claude -p you are told it is unavailable. Which surfaces have it and what each tab shows is on Install plugins. init, update, details, prune, eval, eval init and test have no session form.

CommandAliasesEffect
/pluginOpens Discover. Unrecognised subcommands do the same
/plugin help--help, -hLists subcommands
/plugin list [--enabled|--disabled]lsPrints marketplace-installed plugins inline; pending state changes carry a note to run /reload-plugins to apply them
/plugin installiOpens Discover
/plugin install <plugin>iOpens the plugin's details (in that marketplace's list when qualified)
/plugin install <source>iA path, URL or owner/repo gives a marketplace-not-found error and installs nothing
/plugin install <plugin> --marketplace <source>iAdds the marketplace after confirmation if needed, then opens the details. v2.1.275 or later
/plugin manageOpens Installed
/plugin statsOpens Stats where /skill-doctor is available, otherwise Discover
/plugin enable <plugin>Enables it in Installed
/plugin disable <plugin>Disables it in Installed
/plugin uninstall <plugin>Uninstalls it in Installed
/plugin configure <plugin>configOpens its userConfig dialog, or says it has none
/plugin validate <path>Inline validation report
/plugin tag [path] [--push] [--dry-run] [--force]Same as claude plugin tag; other flags print usage
/plugin marketplacemarketNothing by itself
/plugin marketplace add [source]market addAdds source, or opens the Add marketplace input
/plugin marketplace listmarket listPrints names inline
/plugin marketplace update [name]market updateOpens Marketplaces and refreshes name if given
/plugin marketplace remove [name]market remove, market rm, marketplace rmOpens Marketplaces and removes name if given

enable, disable, uninstall and configure on a plugin not installed in this project print Plugin "<plugin>" is not installed in this project.

/reload-plugins

Applies pending plugin changes (installs, updates, enables, disables, on-disk edits) to the running session. Closing the /plugin panel with pending changes runs it automatically; run it yourself after changes made elsewhere, such as a claude plugin command in another terminal.

/reload-plugins [--force]

--force (or plain force) applies a reload even if it would invalidate the prompt cache.

The summary line reads Reloaded: N plugins · N skills · N agents · N hooks · N plugin MCP servers · N plugin LSP servers (the MCP count is omitted without an interactive terminal), with N errors during load. Run /plugin for details. appended on failures. The skills count includes plugin commands/ entries; the agents count includes non-plugin agents. Missing dependencies are installed and reloaded, adding (+ N dependencies: <names>) resolved.

If the reload would add or remove a plugin MCP server or the LSP tool in a way that invalidates the prompt cache, it is held back with a message explaining that your next message would re-read the whole conversation, and asking you to rerun with --force. Prompt caching explains the cost.

From v2.1.260 it also works without an interactive terminal (desktop app, Agent SDK, -p), but only when you type it into the session yourself. Arriving through Remote Control or a relayed message such as from Slack, it replies that it is not available over a remote connection. In those sessions it does not connect or disconnect plugin MCP servers; that happens next session.

Loading a plugin for one session

FlagBehaviourExample
--plugin-dir <path>Load from a folder or a .zip of one. A folder of plugins loads every child with .claude-plugin/plugin.json. One path per flag; repeatableclaude --plugin-dir ./invoice-helper --plugin-dir ~/plugins/extra.zip
--plugin-url <url>Fetch a plugin .zip. Repeat the flag or pass several URLs space-separated in one quoted valueclaude --plugin-url "https://cdn.example.com/a.zip https://cdn.example.com/b.zip"

Plugins loaded this way are session-only: <name>@inline, shown by claude plugin list only when the same flag precedes it, with scope: "session" in JSON. They replace a same-named installed plugin for that session, unless you disabled the session copy with claude plugin disable <name>@inline or managed settings lock that name (see How plugins load).

Administrators can reject both flags, and folders in CLAUDE_CODE_PLUGIN_DIRS, with the managed disableSideloadFlags setting; Claude Code then exits 1 saying the flag is disabled by your organisation's managed settings. In the Agent SDK, the plugins option is the equivalent of --plugin-dir (see SDK plugins).