The muse plugins command
Use this page to look up any muse plugins verb: what it takes, what it
prints, what its --json output contains, and how it fails. The plugin
surface is part of the Developer Preview and may change. This page is long on
purpose: it is the only place every verb’s --json shape is shown.
Every output on this page was produced by Muse Code 1.3.0 against a copy of
the weather example in an empty plugin store. The
store path is shown as <data dir>, which is ~/.local/share/muse unless
you set XDG_DATA_HOME; the source directory is shown as
<absolute path to weather>, and digests inside paths are shortened. Long
outputs are trimmed where marked.
Or open the panel
Section titled “Or open the panel”You do not have to type any of these verbs. In a Muse Code session, enter
/plugins with nothing after it and a panel opens with four tabs, Discover,
Installed, Marketplaces and Errors. Tab moves between them, typing filters
the current list, and Esc closes the panel. Most of this page maps onto
those tabs:
| Verb | In the panel |
|---|---|
list |
The Installed tab; [x] marks an enabled plugin and needs review marks one with capabilities waiting for you. |
inspect |
Enter on a plugin row opens its details: status, capability rows, source and cache paths. |
enable, disable |
Space on a plugin row on the Installed tab, or the action row in its details. |
approve, reject |
In a plugin’s details, Tab to the Runtime tab; Enter approves the selected hook, MCP server, reminder or agent definition and Space rejects it. |
install <plugin>@<marketplace> |
The Discover tab lists every marketplace plugin; Enter on one installs it, as does Enter on a plugin inside a marketplace’s details. |
update, remove |
Action rows in the plugin’s details; remove asks you to confirm. |
marketplace add, marketplace list, marketplace update, marketplace remove |
The Marketplaces tab: Enter on “Add marketplace” prompts for a source, Enter on a marketplace opens it, Space removes it. |
install <path>, validate, hook test |
Command line only. The panel installs from marketplaces; a local directory is installed with the command. |
Two things exist only in the panel: a skill row in a plugin’s details can
apply that plugin’s skills to the current session, and the Errors tab
gathers every plugin diagnostic in one list. --json output and
--scope project exist only on the command line. The composer also accepts
the same verbs typed after the slash, such as /plugins install <path>,
for the rare case where a session is the only terminal you have.
Synopsis
Section titled “Synopsis”muse plugins <command>muse plugins --help prints this table. Every leaf command also answers
--help (or -h) with its own usage line and exit code 0.
| Verb | Positionals | Flags | What it does |
|---|---|---|---|
validate |
<path> |
--json |
Validate a plugin directory without installing or running anything |
install |
<path> or <plugin>@<marketplace> |
--scope user or --scope project (local path only), --json |
Copy a plugin into the store and enable it |
list |
none | --available, --json |
List installed plugins, or plugins offered by configured marketplaces |
inspect |
<id> |
--json |
Show one installed plugin, its review-gated capabilities and their status |
approve |
selector | --json |
Trust and enable the current definition of one or more capabilities |
reject |
selector | --json |
Trust and disable the current definition of one or more capabilities |
hook test |
<plugin-id>:<hook-id> |
--fixture <path> (required), --json |
Run one installed hook against a fixture file |
marketplace add |
<name> <source> |
--json |
Register a marketplace and store an offline snapshot of its catalog |
marketplace list |
none | --json |
List configured marketplaces |
marketplace update |
<name> |
--json |
Refresh a marketplace snapshot |
marketplace remove |
<name> |
--json |
Forget a marketplace and delete its snapshot |
enable |
<id> |
--json |
Enable an installed plugin for future sessions |
disable |
<id> |
--json |
Disable an installed plugin |
update |
<id> |
--json |
Re-read an installed plugin from the source it was installed from |
remove |
<id> |
--delete-data, --json |
Delete the installed record, cached package and review decisions |
muse plugins with no verb prints missing plugins command; an unknown verb
prints unsupported plugins command `<verb>` . Both exit with code 2.
Shared conventions
Section titled “Shared conventions”Human output is one line per result, fields separated by tabs, with
key=value pairs. A second line starting with warning follows when the
store has something to say about the plugin. Control characters and
bidirectional format characters are stripped from ids, versions and messages
before printing.
--json prints one pretty-printed JSON document on stdout and nothing
else on stdout. Warnings that the human form sends to stderr appear as
fields in the document.
Help is honoured only when every other argument is valid.
muse plugins list --help --nope is still a usage error.
Exit codes:
| Exit code | Meaning | Where the text goes |
|---|---|---|
| 0 | Success | stdout |
| 1 | The command ran and failed (plugin not installed, invalid package, source gone, and so on) | Human form: one line on stderr. --json: the error envelope on stdout and the same message on stderr |
| 2 | Usage error: missing or extra argument, unknown flag, bad --scope value, missing --fixture |
The message on stderr, followed by the root muse --help text |
The --json error envelope has one error object with a stable
kebab-case code and a human message. validate and install add a
details member carrying the full validation report when the package was
invalid.
{ "error": { "code": "unknown-plugin", "message": "plugin `nope` is not installed" }}The full list of codes is in Error codes.
validate
Section titled “validate”usage: muse plugins validate <path> [--json]Validates the plugin directory at <path>. Nothing is installed and no
plugin code runs. Use it before install, and on a
Claude Code plugin to see what Muse Code
will import.
$ muse plugins validate ./weathervalid weather native skills=1 commands=1 hooks=1 mcp=0 reminders=0 diagnostics=0The human line is valid, the plugin id, the manifest family (native,
claude-compatible, codex-compatible or agent-plugins), capability
counts and the number of diagnostics.
Warning diagnostics go to stderr as
diagnostic=<code> severity=<severity> path=<path> message=<text>.
An invalid package prints invalid, the id or path, and one line per
diagnostic, then exits 1:
$ muse plugins validate ./brokeninvalid ./broken diagnostics=5diagnostic=invalid-plugin-id severity=error path=./broken/.muse-plugin/plugin.json message=plugin id `Broken Plugin` is invaliddiagnostic=invalid-manifest-schema severity=error path=./broken/.muse-plugin/plugin.json message=plugin manifest must declare string field `version`diagnostic=invalid-manifest-schema severity=error path=./broken/.muse-plugin/plugin.json message=plugin manifest must declare string field `description`diagnostic=manifest-family-mismatch severity=error path=./broken/.muse-plugin/plugin.json message=plugin manifest must declare compat.manifestDirdiagnostic=invalid-manifest-schema severity=error path=./broken/.muse-plugin/plugin.json message=plugin manifest must declare capabilitiesWith --json, a valid package returns the validation report:
{ "valid": true, "source_path": "./weather", "plugin": { "id": "weather", "display_name": "Weather", "version": "0.1.0", "description": "Example plugin: a forecast skill, a forecast-brief command, and a hook that logs tool use.", "manifest_family": "native", "capabilities": { /* same shape as "capabilities" below */ }, "compatibility": { "summary": "full", "declarations": [ { "id": "skill:forecast", "kind": "skill", "classification": "supported" }, { "id": "hook:log-tool-use", "kind": "hook", "classification": "supported" }, { "id": "command:forecast-brief", "kind": "command", "classification": "supported" } ] } }, "capabilities": { "skills": [ { "id": "forecast", "path": "skills/forecast/SKILL.md", "source_path": "<absolute path to weather>/skills/forecast/SKILL.md", "enabled_default": true } ], "hooks": [ { "id": "log-tool-use", "event": "PostToolUse", "matcher": null, "command": ["sh", "hooks/log-tool-use.sh"], "shell_command": null, "source_path": "<absolute path to weather>/hooks/log-tool-use.sh", "source_relative_path": "hooks/log-tool-use.sh", "timeout_ms": 5000, "async": false, "compatibility_name": null } ], "mcp_servers": [], "commands": [ { "id": "forecast-brief", "path": "commands/forecast-brief.md", "source_path": "<absolute path to weather>/commands/forecast-brief.md", "enabled_default": true } ], "reminders": [] }, "diagnostics": []}An invalid package returns the error envelope. code and message come
from the first error-severity diagnostic, and details is the same report
shape with "valid": false and the full diagnostics array:
{ "error": { "code": "invalid-plugin-id", "message": "plugin id `Broken Plugin` is invalid", "details": { "valid": false, "source_path": "./broken", "plugin": null, "capabilities": { "skills": [], "hooks": [], "mcp_servers": [], "commands": [], "reminders": [] }, "diagnostics": [ { "code": "invalid-plugin-id", "severity": "error", "message": "plugin id `Broken Plugin` is invalid", "path": "./broken/.muse-plugin/plugin.json" } // four more ] } }}Capability entries in capabilities have these fields:
| Kind | Fields |
|---|---|
skills, commands |
id, path, source_path, enabled_default |
hooks |
id, event, matcher, command, shell_command, source_path, source_relative_path, timeout_ms, async, compatibility_name |
mcp_servers |
id, transport, command, url, source_path, source_relative_path |
reminders |
id, path, source_path, tools, blocking, default_priority, max_priority, max_child_steps, max_installs_per_run, reasoning_effort |
Diagnostic codes and their meanings are listed in
Validation diagnostics. To validate one skill directory on
its own, run muse skills validate <dir>.
Missing <path> is a usage error: missing plugin package path.
install
Section titled “install”usage: muse plugins install <path> [--scope user|project] [--json]usage: muse plugins install <plugin>@<marketplace> [--json]Validates the package, copies it into the content-addressed cache, records
it in the store and enables it. Skills and commands are usable right away;
hooks, MCP servers, reminders and agent definitions wait for
approve.
The target is a marketplace reference only when it contains @, does not
exist on disk, and is not shaped like a path (no leading . or ~, no /
or \). Anything else is a local directory.
$ muse plugins install ./weatherinstalled weather 0.1.0 enabled=true trust=user-local provenance=native-local cache=<data dir>/plugins/cache/local/weather/bacc26ed…/packagewarning third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabledThe human line is installed, id, version, an optional
marketplace=<name>, then enabled, trust (user-local or
project-trusted), provenance (native-local, foreign-import or
marketplace-user-added) and the cache path. Install-time diagnostics go
to stderr.
$ muse plugins install weather@localinstalled weather 0.1.0 marketplace=local enabled=true trust=user-local provenance=marketplace-user-added cache=<data dir>/plugins/cache/local/weather/bacc26ed…/packagewarning third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabledWith --json:
{ "installed": { "id": "weather", "display_name": "Weather", "version": "0.1.0", "description": "Example plugin: a forecast skill, a forecast-brief command, and a hook that logs tool use.", "manifest_family": "native", "enabled": true, "trust": "user-local", "source": { "provenance": "native-local", "path": "<absolute path to weather>" }, "installed_at": "2026-09-17T04:24:55.92179Z", "updated_at": "2026-09-17T04:24:58.191445Z", "manifest_sha256": "sha256:b0cf4aec5eb6cef64aae6e93804589aa574704b638ea53922dc6f329ccdf7b78", "package_sha256": "sha256:bacc26ede05bf8aa08c888ddacf24540f757fb51f5248927df265bdcd41ea62c", "cache_path": "<data dir>/plugins/cache/local/weather/bacc26ed…/package" }, "warning": "third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabled", "diagnostics": [], "plugin": { "id": "weather", "display_name": "Weather", "version": "0.1.0", "description": "Example plugin: a forecast skill, a forecast-brief command, and a hook that logs tool use.", "manifest_family": "native", "capabilities": { /* same shape as in validate */ } }, "lockfile_path": "<data dir>/plugins/installed.json"}A marketplace install adds "marketplace": "<name>" at the top level.
The installed object is the installed record; list, inspect,
enable, disable and update return the same shape:
| Field | Meaning |
|---|---|
id, display_name, version, description |
Copied from the manifest |
manifest_family |
native, claude-compatible, codex-compatible or agent-plugins |
enabled |
Whether the plugin loads in new sessions |
trust |
user-local or project-trusted (the install scope, not capability review) |
source.provenance |
native-local, foreign-import or marketplace-user-added |
source.path |
The directory update re-reads |
installed_at, updated_at |
RFC 3339 timestamps; updated_at is null until the first reinstall or update |
manifest_sha256, package_sha256 |
Digests of the manifest and of the whole package tree |
cache_path |
The immutable installed copy; plugin processes see it as MUSE_PLUGIN_ROOT |
Installing an id that is already installed from the same path is an update:
enabled and installed_at are kept and updated_at is set. Installing an
id that is already installed from a different path is refused; pass
--scope user to rebind it on purpose.
--scope accepts user or project, also as --scope=<value>; the
last occurrence wins. --scope project requires the current directory to
be a trusted workspace and <path> to be inside it, and records
trust=project-trusted. Without the flag or with --scope user the record
is user-local. Scope is recorded in your own store and is not shared with
other users of the workspace. See
Trust, review and scopes.
Notable errors:
| Message | Exit | --json code |
|---|---|---|
plugins install --scope project requires a local plugin path (marketplace target) |
2 | usage error, no envelope |
invalid plugins install --scope `nope` (expected user or project) |
2 | usage error, no envelope |
plugins install --scope requires a value |
2 | usage error, no envelope |
failed to read plugin store `./missing`: No such file or directory (os error 2) |
1 | plugin-store-read-failed |
plugin `weather` is already installed from a different source (reinstall from another path without --scope user) |
1 | invalid-plugin-package |
A validation error message, with the report in details |
1 | the first diagnostic code, or invalid-plugin-package |
marketplace `x` is not configured |
1 | unknown-marketplace |
usage: muse plugins list [--available] [--json]Without flags, lists installed plugins, one per line, or no plugins.
$ muse plugins listweather 0.1.0 enabled=true active=true trust=user-local provenance=native-local valid=true diagnostics=0warning third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabledvalid says the cached package still matches its recorded digests.
active is valid && enabled for the package as a whole; it never means a
review-gated capability is running. diagnostics counts cache and
manifest diagnostics for that record.
With --json, each entry wraps the installed record:
{ "plugins": [ { "record": { /* installed record */ }, "warning": "third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabled", "plugin": { /* descriptor: id, display_name, version, description, manifest_family, capabilities */ }, "valid": true, "active": true, "active_scope": "installed-plugin", "diagnostics": [] } ]}Each diagnostics element has code, severity, message and path.
list takes no positional; muse plugins list weather is a usage error
(unexpected argument `weather` ).
list –available
Section titled “list –available”Lists the plugins offered by every configured marketplace snapshot. This reads stored snapshots only and never touches the network.
$ muse plugins list --availableweather@local 0.1.0 status=available transport=local-pathstatus is available, deprecated or blocked; only available
entries can be installed. Marketplaces whose snapshot cannot be read are
reported on stderr as warning: marketplace `<name>` skipped: <reason>.
{ "available": [ { "marketplace": "local", "name": "weather", "version": "0.1.0", "status": "available", "install": { "transport": "local-path", "source": "<absolute path to market>/plugins/weather" }, "digest": "sha256:bacc26ede05bf8aa08c888ddacf24540f757fb51f5248927df265bdcd41ea62c" } ], "skipped": [], "warnings": []}skipped lists catalog entries that were dropped when the snapshot was
taken (marketplace, name, reason); warnings lists unreadable
marketplaces (marketplace, reason).
inspect
Section titled “inspect”usage: muse plugins inspect <id> [--json]Shows one installed plugin with capability counts, then one
runtime-capability line per review-gated capability with its review
status, then any capability-diagnostic lines.
$ muse plugins inspect weatherweather 0.1.0 enabled=true active=true trust=user-local valid=true skills=1 commands=1 hooks=1 mcp=0 reminders=0 cache=<data dir>/plugins/cache/local/weather/bacc26ed…/packagewarning third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabledruntime-capability plugin:weather:hook:log-tool-use status=review_neededstatus is one of review_needed, trusted_enabled, trusted_disabled,
modified, invalid or blocked; only trusted_enabled capabilities load.
Trust, review and scopes explains
each status and what moves a capability between them.
Agent definitions render as
runtime-capability agent_definition <scoped-id> ordinal=<n> status=<status>.
With --json, the document is the list entry plus four members:
{ "record": { /* installed record */ }, "warning": "third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabled", "plugin": { /* descriptor */ }, "valid": true, "active": true, "active_scope": "installed-plugin", "diagnostics": [], "effective_capabilities_scope": "plugin-capability-snapshot", "effective_capabilities": [ { "kind": "command", "stable_id": "plugin:weather:forecast-brief" }, { "kind": "skill", "stable_id": "plugin:weather:forecast" } ], "capability_diagnostics": [], "runtime_capabilities": [ { "candidate": { "kind": "hook", "plugin_id": "weather", "capability_id": "log-tool-use", "stable_id": "plugin:weather:hook:log-tool-use", "display_path": "plugin://weather/hook/log-tool-use", "definition_hash": "sha256:9a35bebf76782df57992d18e4f653c6e38ccbd3fd3d7121517b1e1bb6b958a2a", "source_digest": "sha256:bacc26ede05bf8aa08c888ddacf24540f757fb51f5248927df265bdcd41ea62c" }, "status": "review_needed", "diagnostic": null } ]}effective_capabilities are the skills and commands that load without
review. runtime_capabilities are the review-gated ones; definition_hash
is what approve binds to, and source_digest is the package digest that
feeds it. capability_diagnostics elements have code, message, path.
Unknown id: plugin `nope` is not installed (exit 1, code
unknown-plugin).
approve and reject
Section titled “approve and reject”usage: muse plugins approve <plugin-id[[:kind]:capability-id] | stable-id> [--json]usage: muse plugins reject <plugin-id[[:kind]:capability-id] | stable-id> [--json]approve records that you trust the current definition of the selected
capabilities and enables them; reject records the same hash and disables
them. Either way the decision is bound to definition_hash, which includes
the package digest, so any change to the package returns the capability to
modified. Decisions apply to new sessions. Skills and commands never need
review.
$ muse plugins approve weatherapprove plugin:weather:hook:log-tool-use$ muse plugins reject weatherreject plugin:weather:hook:log-tool-useOne line per capability: the verb and the stable id (agent definitions
print <scoped-id> (ordinal <n>)).
$ muse plugins approve plugin:weather:hook:log-tool-use --json{ "decision": "approve", "runtime_capabilities": [ { "stable_id": "plugin:weather:hook:log-tool-use", "trusted_definition_hash": "sha256:9a35bebf76782df57992d18e4f653c6e38ccbd3fd3d7121517b1e1bb6b958a2a", "enabled": true } ]}decision is approve or reject. Each runtime_capabilities element
has stable_id, trusted_definition_hash and enabled; an agent
definition element instead has kind: "agent_definition", plugin_id,
scoped_definition_id, original_ordinal and enabled.
approve is allowed when the status is review_needed, trusted_enabled,
trusted_disabled or modified. reject is additionally allowed on
blocked entries. Anything else fails with
runtime-capability-not-reviewable.
Selector grammar
Section titled “Selector grammar”| Selector | Matches |
|---|---|
<plugin-id> |
Every review-gated capability of that plugin |
<plugin-id>:<capability-id> |
Capabilities with that id in any kind; fails as ambiguous if more than one kind matches |
<plugin-id>:<kind>:<capability-id> |
Exactly one capability |
plugin:<plugin-id>:<kind>:<capability-id> |
The stable id as printed by inspect; the plugin: prefix is recognised only when at least three segments follow it |
<plugin-id>:agent_definition:<ordinal> |
An agent definition by the ordinal inspect prints |
Kinds: hook, reminder, mcp_server, agent_definition and
invocation_preapproval. The parser accepts the last one, but the current
release lists no reviewable capability of that kind (a Claude Code
allowed-tools declaration is inventoried as unsupported instead), so in
practice you spell one of the first four.
Notable errors:
| Message | Exit | --json code |
|---|---|---|
no runtime capabilities match `weather:bogus` |
1 | runtime-capability-not-found |
plugin `nope` is not installed |
1 | unknown-plugin |
unknown runtime capability kind `foo`; expected one of `hook`, `reminder`, `mcp_server`, `agent_definition`, `invocation_preapproval` |
1 | none (printed on stderr even with --json) |
runtime capability selector `<sel>` is ambiguous; qualify by kind, e.g. `<plugin>:<kind>:<cap>` |
1 | runtime-capability-ambiguous |
cannot approve non-reviewable runtime capabilities: <stable-id> (<status>) |
1 | runtime-capability-not-reviewable |
cached plugin `x` is invalid: <reason>; run `muse plugins inspect x` for details |
1 | plugin-cache-invalid |
settings are busy; retry runtime capability review |
1 | runtime-capability-trust-write-failed |
If the capability changed between selection and the write, nothing is
written and the command fails with runtime-capability-not-found.
hook test
Section titled “hook test”usage: muse plugins hook test <plugin-id>:<hook-id> | plugin:<plugin-id>:hook:<hook-id> --fixture <path> [--json]Runs one hook of an installed, enabled plugin against a fixture and reports
what the hook returned. The hook does not need to be approved first, and
the run does not record a decision. The hook process gets the same
environment as in a session, including MUSE_PLUGIN_ROOT and
MUSE_PLUGIN_DATA_DIR, so a hook that writes files will write them.
The fixture is a JSON file:
| Member | Required | Meaning |
|---|---|---|
event |
yes | The event the manifest declares for this hook, as PostToolUse or post_tool_use; any other event is rejected |
stdin |
yes | The JSON object the hook reads on stdin; see Hook events and payloads for each event’s keys |
matcher_input |
no | The string the hook’s matcher is tested against |
cwd |
no | Working directory for the hook process; defaults to your current directory |
{ "event": "PostToolUse", "stdin": { "hook_event_name": "PostToolUse", "tool_name": "read_file", "tool_input": { "path": "README.md" }, "tool_response": { "ok": true } }}$ muse plugins hook test weather:log-tool-use --fixture fixture.jsonhook-test weather:log-tool-use status=completedstatus is the first handler’s terminal status (completed, failed,
and so on). A hook that returns malformed output is a failed status, not
a command failure.
With --json, the output is the full run document:
{ "decision": { "should_block": false, "block_reason": null, "additional_contexts": [], "updated_input": null, "permission_decision": null, "feedback_message": null, "should_stop": false, "stop_reason": null }, "terminals": [ { "run_id": "plugin:weather:log-tool-use:1", "hook_key": "plugin:weather:log-tool-use", "event": "post_tool_use", "status_message": "Logging tool use", "system_message": null, "status": "completed", "duration_ms": 343, "exit_code": 0, "effects": [], "stdout": "", "stderr": "", "error": null } ], "records": 2}decision is the aggregate effect Muse Code would apply (should_block,
block_reason, additional_contexts, updated_input,
permission_decision, feedback_message, should_stop, stop_reason).
terminals has one entry per handler run with status, exit_code,
duration_ms, stdout, stderr, effects and error.
Notable errors:
| Message | Exit | --json code |
|---|---|---|
plugins hook test requires --fixture <path> |
2 | usage error |
missing plugins hook test selector |
2 | usage error |
plugins hook test selector `weather:mcp_server:x` has capability kind `mcp_server`; expected `hook` |
2 | usage error |
plugin hook `weather:nope` is not installed and enabled |
1 | plugin-hook-test-failed |
fixture event must be `PostToolUse` or `post_tool_use` (wording varies with the event) |
1 | plugin-hook-test-failed |
fixture must contain `stdin` |
1 | plugin-hook-test-failed |
muse plugins hook with anything other than test prints
unsupported plugins hook command `<word>` (exit 2).
enable and disable
Section titled “enable and disable”usage: muse plugins enable <id> [--json]usage: muse plugins disable <id> [--json]disable stops the plugin from loading in new sessions and touches nothing
else. enable re-checks the cached package against its recorded digests,
then turns the plugin back on. Review decisions survive both.
$ muse plugins disable weatherdisable weather enabled=false trust=user-local$ muse plugins enable weatherenable weather enabled=true trust=user-localWith --json, the document is {"disable": <record>} or
{"enable": <record>}:
{ "disable": { "id": "weather", "enabled": false, "trust": "user-local" // remaining installed record fields }}Unknown id: plugin `nope` is not installed (exit 1, unknown-plugin).
update
Section titled “update”usage: muse plugins update <id> [--json]Re-reads the plugin from the recorded source.path (the directory you
installed from, or the marketplace copy), revalidates it and replaces the
cached package. enabled and provenance are kept. If the new package
differs, every approved capability shows as modified until you approve it
again. A source manifest that now declares a different name is refused.
$ muse plugins update weatherupdated weather sha256:b0cf4aec… trust=user-local previous=sha256:b0cf4aec…warning third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabledThe line carries the new manifest_sha256 and the previous one.
{ "updated": { /* installed record */ }, "warning": "third-party plugin: hooks require review before activation; skills and commands are active without review while the plugin is enabled", "diagnostics": [], "plugin": { /* descriptor */ }, "previous_manifest_sha256": "sha256:b0cf4aec5eb6cef64aae6e93804589aa574704b638ea53922dc6f329ccdf7b78", "lockfile_path": "<data dir>/plugins/installed.json"}When the source directory is gone, the command fails and the previous record is kept:
$ muse plugins update weather --json{ "error": { "code": "plugin-source-unavailable", "message": "plugin `weather` source `<absolute path to weather>` is no longer available; run `muse plugins remove weather`, then reinstall the plugin from an available source" }}For a plugin installed from a marketplace the message names
muse plugins install <id>@<marketplace> instead. After
muse plugins marketplace update <name>, muse plugins update for a
plugin from that marketplace can report its source unavailable; remove the
plugin and reinstall it with muse plugins install <plugin>@<name>.
remove
Section titled “remove”usage: muse plugins remove <id> [--delete-data] [--json]Deletes the installed record and the cached package, and forgets every
review decision for that plugin, so a reinstall starts at review_needed.
The plugin’s data directory (MUSE_PLUGIN_DATA_DIR) is kept unless you pass
--delete-data.
$ muse plugins remove weatherremoved weather$ muse plugins remove weather --delete-data --json{ "removed": "weather", "cache_path": "<data dir>/plugins/cache/local/weather/bacc26ed…/package", "data_path": "<data dir>/plugins/data/weather", "warning": null, "lockfile_path": "<data dir>/plugins/installed.json"}data_path is null without --delete-data. If the record was removed
but a directory could not be deleted, the command still succeeds and
warning (or a warning line) names the directory that may remain.
Unknown id: plugin `nope` is not installed (exit 1, unknown-plugin).
A failure while updating settings or the store reports
plugin-remove-failed.
marketplace
Section titled “marketplace”usage: muse plugins marketplace <command>
Commands: add <name> <source> [--json] Add a local file/directory or Git marketplace source and store a snapshot list [--json] List configured marketplaces update <name> [--json] Refresh a marketplace snapshot explicitly remove <name> [--json] Remove a marketplace source and snapshotA marketplace is a catalog file plus the plugin directories it points to.
add reads the catalog once and stores an offline snapshot; list --available and install <plugin>@<name> read that snapshot and never
fetch. update takes a fresh snapshot. Names follow plugin id rules.
muse plugins marketplace with no subcommand prints
missing plugins marketplace command (exit 2).
Source forms
Section titled “Source forms”<source> |
How it is read | Recorded source.kind |
|---|---|---|
| An existing directory | The catalog is looked for inside it | local-dir |
An existing marketplace.json file |
Loaded directly | local-file |
git@host:path, ssh://, git://, file://, http://, https://, or anything ending in .git |
Cloned, then the catalog is looked for in the checkout | git |
owner/repo (GitHub shorthand; letters, digits, -, _, and . in the repo name) |
Expanded to https://github.com/owner/repo.git and cloned |
git |
Any git form may end in #<ref> to pin a branch or tag. A path that exists
on disk wins over the git interpretation unless it ends in .git.
Inside a directory or checkout the catalog is probed in this order:
marketplace.json, .agents/plugins/marketplace.json,
.claude-plugin/marketplace.json. A source with none of them fails with a
message naming all three.
marketplace add
Section titled “marketplace add”$ muse plugins marketplace add local ./marketlocal plugins=1 source=<absolute path to market>The line is the name, the number of catalog entries kept, and the resolved
source. Entries that were dropped are reported on stderr as
warning: marketplace plugin `<name>` skipped: <reason> followed by
warning: marketplace snapshot plugins=<n> skipped=<m>.
With --json (update returns the identical shape; the document below
came from update, which writes each new snapshot into a generations/
subdirectory, whereas the first add writes snapshot.json directly under
the marketplace directory):
{ "marketplace": { "name": "local", "source": { "kind": "local-dir", "path": "<absolute path to market>" }, "plugin_count": 1, "skipped": [], "snapshot_path": "<data dir>/plugins/marketplaces/local/generations/1789619275399478000/snapshot.json", "last_updated_at": "2026-09-17T04:27:55.47694Z", "fetched": true }}skipped elements have name and reason. Reserved names are refused
with marketplace `<name>` is reserved and cannot be added (exit 1).
add with fewer than two positionals prints the usage line (exit 2).
marketplace list
Section titled “marketplace list”$ muse plugins marketplace listlocal plugins=1 source=<absolute path to market>Prints no marketplaces when none is configured. A marketplace that has no
snapshot yet carries a trailing not fetched. With --json:
{ "marketplaces": [ { /* marketplace record, as in add */ } ], "warnings": []}marketplace update
Section titled “marketplace update”muse plugins marketplace update <name> re-reads the source and replaces
the snapshot; a failed update keeps the previous snapshot. Output is the
same line and --json shape as add. Unknown name:
marketplace `nope` is not configured (exit 1, unknown-marketplace).
marketplace remove
Section titled “marketplace remove”$ muse plugins marketplace remove localremoved localWith --json, {"removed": "<name>", "snapshot_path": "<path>"}. Plugins
already installed from that marketplace stay installed.
Error codes
Section titled “Error codes”Codes that appear in error.code with --json. Without --json the same
message is printed on stderr with exit code 1.
| Code | Raised by | Meaning |
|---|---|---|
invalid-plugin-package |
validate, install, update, marketplace add |
The package or catalog failed validation; details carries the report when there is one |
incompatible-plugin-package |
install, update |
A foreign-format package cannot be imported |
unknown-plugin |
inspect, approve, reject, enable, disable, update, remove |
No installed plugin with that id |
unknown-marketplace |
install <plugin>@<name>, marketplace update, marketplace remove |
No configured marketplace with that name |
plugin-source-unavailable |
update |
The recorded source path no longer exists |
plugin-store-read-failed |
any | A path (including an install target) could not be read |
plugin-store-write-failed |
any mutation | The store could not be written |
plugin-store-format-invalid |
any | The store file is not in a format this build understands |
plugin-cache-invalid |
approve, reject, enable |
The cached package no longer matches its recorded digests |
plugin-remove-failed |
remove |
The settings prune or store removal failed |
plugin-hook-test-failed |
hook test |
The hook was not found or the fixture was unusable |
runtime-capability-not-found |
approve, reject |
The selector matched nothing, or the match changed before it was written |
runtime-capability-ambiguous |
approve, reject |
A two-segment selector matched more than one kind |
runtime-capability-not-reviewable |
approve, reject |
The current status does not allow that decision |
runtime-capability-trust-write-failed |
approve, reject |
Settings were locked or could not be written |
runtime-capability-review-failed |
inspect, approve, reject |
The capability snapshot could not be built |
Validation diagnostic codes such as invalid-plugin-id can also appear as
error.code from validate and install; they are listed in
Validation diagnostics.
Environment variables
Section titled “Environment variables”Two variables change where the command reads and writes.
| Variable | Effect | Default |
|---|---|---|
XDG_DATA_HOME |
The plugin store lives at $XDG_DATA_HOME/muse/plugins |
~/.local/share/muse/plugins |
XDG_CONFIG_HOME |
Review decisions are written to $XDG_CONFIG_HOME/muse/settings.json |
~/.config/muse/settings.json |
Where state lives
Section titled “Where state lives”| Path (under the data directory) | Contents |
|---|---|
plugins/installed.json |
The installed records; lockfile_path in --json output |
plugins/cache/local/<id>/<package-digest>/package |
The immutable installed copy (cache_path, MUSE_PLUGIN_ROOT) |
plugins/data/<id> |
The plugin’s writable directory (MUSE_PLUGIN_DATA_DIR); created by the plugin, deleted only by remove --delete-data |
plugins/marketplaces.json and plugins/marketplaces/<name>/ |
Marketplace registry and snapshots |
Review decisions live in settings.json under runtime_capabilities,
keyed by stable id, each with enabled and trusted_definition_hash.
Every store mutation takes a lock, so two muse plugins commands cannot
corrupt each other. Trust, review and scopes
explains how these files relate to what a session loads;
Marketplaces and updates
covers the marketplace files.
Next steps
Section titled “Next steps”- Manifest: every field
validatechecks. - Validation diagnostics: every code
validate,install,listandinspectcan print. - Hook events and payloads: the
stdinshapes to put in ahook testfixture. - The weather plugin: the plugin every output on this page came from.
- Quickstart: your first plugin: the end-to-end walkthrough.