Files
hmbown--codewhale/docs/PLUGINS.md
CodeWhale Bot 7abd6edb87 docs(plugins): reconcile the bundle contract with the shipped v0.9.6 boundary
PLUGIN_BUNDLES.md was still written against v0.9.1 while PLUGINS.md
described the v0.9.4 /plugin lifecycle. The bundle doc now states the
boundary as of v0.9.6, documents both manifest encodings the runtime
actually parses (native plugin.json and legacy plugin.toml), describes the
real accept/reject behavior for inactive manifest sections (inventoried,
shown in review, enable fails closed naming them), notes that
capabilities.network_hosts is enforced today, and declares ownership between
the two docs. PLUGINS.md's plugin.toml-only install claim is corrected to
match the installer.

Every behavioral claim verified against crates/tui/src/plugins/ (manifest.rs,
agent_plugin.rs, registry.rs, install/) before restating.

Implemented with Claude Code agent assistance.
2026-08-10 14:04:27 -07:00

88 lines
4.1 KiB
Markdown

# Installing plugins
This is the walkthrough for the `/plugin install` on-ramp (v0.9.4, #5182).
[PLUGIN_BUNDLES.md](PLUGIN_BUNDLES.md) remains the contract for the bundle
format (`plugin.json` or legacy `plugin.toml`), discovery, validation, and
the trust/enable lifecycle — this document covers how bits get onto disk in
the first place.
`/plugin suggest <task>` is a local, read-only companion: it ranks already
installed bundles by their validated name, description, bundled skill names,
and declared hosts. It explains the match and gives the next review/enable
step, but never installs, trusts, or enables a bundle. Codewhale deliberately
does not treat arbitrary remote archives as a plugin marketplace; a remote
catalog needs publisher and provenance policy before it can make suggestions.
## Sources
`/plugin install <spec>` accepts three source kinds:
```text
/plugin install ./path/to/bundle # local directory (copied)
/plugin install github:owner/repo # GitHub archive of the default branch
/plugin install https://example.com/x.tar.gz # direct tarball URL
```
There is no registry index and no `git clone` in v1 — tarball-only fetching
keeps the size cap and no-symlink guarantees of the installer. Downloads are
gated by the per-domain network policy: an unknown host returns a
"needs approval" error naming the host (`/network allow <host>`, then retry),
a denied host aborts without touching disk.
The fetched tree must contain **exactly one** bundle root — a directory
holding a `plugin.json` (or legacy `plugin.toml`) manifest. Bundles land in
the user plugins root at `~/.codewhale/plugins/<name>/`, where `<name>` is the
manifest's plugin name.
## The guided flow
Installing never activates anything. The command places the bits, then drops
you straight into the standard capability review:
```text
/plugin install github:someone/neat-plugin
→ Installed plugin 'neat-plugin' to ~/.codewhale/plugins/neat-plugin.
It is disabled and untrusted. Review its requested authority below…
<full inventory, permissions, MCP authority render>
/plugin trust neat-plugin <content-hash>.<capability-hash>
/plugin trust neat-plugin <paste the token> # records the hash-bound receipt
/plugin enable neat-plugin # activates for this workspace
```
This is the same review render and confirmation token as `/plugin trust
<name>` — trust is the strict hash-bound receipt flow, not an advisory marker.
If the bundle's content or declared capabilities change, the receipt stops
matching and the plugin goes inactive until you review again.
## Update and uninstall
```text
/plugin update <name> # re-download, byte-compare, atomic swap if changed
/plugin disable <name> # required before uninstall
/plugin uninstall <name> # deletes the bundle and prunes its state entry
```
- `update` re-downloads the recorded source. Identical bytes are a no-op; a
changed bundle is swapped atomically and its trust receipt is automatically
invalidated (the hash no longer matches), so re-review is forced before the
plugin can activate again. Plugins installed from a local path cannot be
re-downloaded — reinstall them with `/plugin install <path>`.
- `uninstall` refuses enabled plugins (disable first), deletes the bundle
directory, and removes its persisted trust/enablement entry.
## Safety rules
- Every install carries an `.installed-from` provenance marker. The installer
**refuses to overwrite or delete** a bundle that lacks it — hand-placed
bundles under `~/.codewhale/plugins/` are never clobbered.
- Tarballs are size-capped and extracted into a private staging directory
first; path traversal (`..`, absolute paths) and symlinks/hard links inside
the bundle are rejected, and the destination only appears via an atomic
rename after every check passes.
- Install pre-checks the name against builtin and workspace bundles so a
higher-precedence bundle cannot silently shadow (or be shadowed by) the
install.
- Newly installed bits are always **disabled and untrusted**; enablement only
ever happens through the explicit trust review above.