# 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 ` 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 ` 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 `, 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//`, where `` 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… /plugin trust neat-plugin . /plugin trust neat-plugin # 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 ` — 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 # re-download, byte-compare, atomic swap if changed /plugin disable # required before uninstall /plugin uninstall # 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 `. - `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.