> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ziro-agent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugins

> Install Claude Code-compatible plugins that add skills, subagents, commands, MCP servers, and hooks, all under an explicit trust and policy gate.

A plugin is a folder of components that fold into your running agent. Ziro reads
the Claude Code plugin format directly, so plugins written for Claude Code work
here without conversion.

The system has three independent layers, and confusing them is the main source of
"why is my plugin not showing up":

| Layer          | Question it answers                      | Where it lives                        |
| -------------- | ---------------------------------------- | ------------------------------------- |
| **Install**    | Is it on this machine at all?            | `installed.json` ledger               |
| **Trust**      | May its dangerous parts run?             | `trusted.json`, content-hashed        |
| **Activation** | Is it on for this agent, in this thread? | `plugin_config.yaml` gate, then state |

## What a plugin can surface

Enabled plugin components fold into the agent namespaced as
`<plugin-id-last-segment>:<name>`. Native components always shadow a plugin
component on a name clash.

| Component   | Source               | How it appears                                                     |
| ----------- | -------------------- | ------------------------------------------------------------------ |
| Skills      | `skills/**/SKILL.md` | Joins the skill index; found via `search_skills` and `/skills`     |
| Agents      | `agents/*.md`        | Spawn-only subagents, subject to the usual rights intersection     |
| Commands    | `commands/*.md`      | Prompt macros: `/<plugin>:<name> args` expands into a user message |
| MCP servers | `.mcp.json`          | Folded into the live MCP manager                                   |
| Hooks       | `hooks/hooks.json`   | Mapped onto Ziro lifecycle events                                  |

Prompt macros expand `$ARGUMENTS`, `$1`–`$9`, and `@file` references, then run as
a normal turn. Macros whose body is a shell (`!bash`) command are filtered out:
a command file is a prompt template, not an execution channel.

Hook events are mapped from Claude Code's vocabulary onto Ziro's. An event with
no Ziro equivalent is skipped rather than guessed at.

## Discovery roots

Ziro scans, in order:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
<project>/.ziro/plugins        # native, this repo
~/.ziro/plugins                # native, this machine
~/.claude/plugins/installed_plugins.json   # imported from Claude Code
```

The Claude Code import is read-only: Ziro reads your existing installation and
surfaces those plugins, it does not rewrite anything Claude Code owns.

Every parse and fetch failure is non-fatal. A malformed plugin is skipped with a
warning; it never blocks startup.

## Trust and consent

This is the security spine of the whole system. Third-party plugin code can run
shell commands and spawn subprocesses, so those capabilities are **inert until
you explicitly trust the plugin.**

During import, Ziro extracts a **grant** for each capability a plugin wants.
Three grant kinds are treated as dangerous:

| Grant              | Emitted by                            | Why it is dangerous                     |
| ------------------ | ------------------------------------- | --------------------------------------- |
| `shell_exec`       | Each shell hook, each `!bash` command | Runs arbitrary commands on your machine |
| `process_spawn`    | Each stdio MCP server                 | Launches a long-lived subprocess        |
| `permission_widen` | A request for broader tool rights     | Enlarges what the agent may do          |

Network access and http/sse MCP servers are **not** dangerous grants. They
surface without a trust decision, because they cannot execute local code.

Before anything installs, Ziro renders a consent manifest listing exactly what
the plugin wants, and waits for your decision.

<Steps>
  <Step title="Stage">
    Clone or copy the plugin at a pinned ref, import it, and build the consent
    manifest. Nothing is active yet.
  </Step>

  <Step title="Decide">
    `install` records trust and enables everything. `safe_only` installs with the
    dangerous parts inert. `cancel` discards the staged copy.
  </Step>

  <Step title="Commit">
    Write the ledger entry and reload the registry.
  </Step>
</Steps>

### Trust is content-hashed

Trust is keyed on **plugin id plus a hash of its contents**. Edit any file in a
trusted plugin (including via an update) and the hash drifts, which
auto-revokes trust until you confirm again. A plugin cannot be trusted once and
then quietly changed underneath you.

<Warning>
  Dangerous grants are **force-inert** under `ZIRO_STRICT_SECURITY`, and in any
  headless run. A background job or a `-p` one-shot has no human at the keyboard
  to consent, so it fails closed. This is threaded through the TUI runner (when
  not interactive) and the background worker pool (always).
</Warning>

Revoking is symmetric: remove the plugin, or disable it, and its hooks and stdio
MCP servers stop being surfaced.

## Installing

<CodeGroup>
  ```bash From git theme={"theme":{"light":"github-light","dark":"github-dark"}}
  ziro plugin install https://github.com/acme/my-ziro-plugin
  ziro plugin install https://github.com/acme/my-ziro-plugin v1.2.0
  ```

  ```bash From a local path theme={"theme":{"light":"github-light","dark":"github-dark"}}
  ziro plugin install ./plugins/my-plugin
  ```

  ```bash From a marketplace theme={"theme":{"light":"github-light","dark":"github-dark"}}
  ziro plugin marketplace add acme https://github.com/acme/ziro-marketplace
  ziro plugin marketplace search git
  ziro plugin install acme:git-helper
  ```
</CodeGroup>

Two flags short-circuit the consent prompt, and both are decisions you are making
in advance:

| Flag     | Meaning                                          |
| -------- | ------------------------------------------------ |
| `--yes`  | Trust the plugin, including its dangerous grants |
| `--safe` | Install with dangerous grants inert              |

Without a flag, consent is interactive on a TTY and fails closed otherwise.

### Managing

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
ziro plugin list
ziro plugin info <id>
ziro plugin update <id>
ziro plugin remove <id>
ziro plugin enable <id>
ziro plugin disable <id>
```

## Marketplaces

A marketplace is a registry of plugin listings, parsed from a Claude Code
`marketplace.json`. Registered marketplaces live in a JSON file under your user
root.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
ziro plugin marketplace add <name> <source>
ziro plugin marketplace list
ziro plugin marketplace search <query>
ziro plugin marketplace remove <name>
```

Listings resolve to a concrete source: a `local` listing points at a subdirectory
of the marketplace repo, a `github` or `git` listing at a URL. Installing then
uses the normal staged-consent flow.

<Note>
  `preapproved_marketplaces` in `plugin_config.yaml` is **discovery only**. It
  auto-registers a fetch origin so `ziro plugin install <market>:<plugin>`
  resolves without a manual `marketplace add`. It never skips the consent
  manifest and never grants trust.
</Note>

## In chat

`/plugins` (alias `/plugin`) is one command covering the whole surface.

| Subcommand                              | Effect                                                                   |
| --------------------------------------- | ------------------------------------------------------------------------ |
| `/plugins`                              | In the TUI, open a scrollable panel with inline toggles and an info peek |
| `/plugins install <src> [ref]`          | Stage an install and render the consent manifest                         |
| `/plugins confirm [safe]`               | Commit the staged install                                                |
| `/plugins cancel`                       | Discard the staged install                                               |
| `/plugins enable <id>` / `disable <id>` | Toggle activation **for this thread**                                    |
| `/plugins info <id>`                    | Show components and grants                                               |
| `/plugins update <id>` / `remove <id>`  | Manage an installed plugin                                               |
| `/plugins reload`                       | Re-scan the discovery roots                                              |
| `/plugins marketplace ...`              | Manage marketplaces                                                      |

Install uses a staged-then-confirm flow rather than a blocking prompt, so it
works identically in the TUI and the REPL.

Changes hot-load without a restart: skills reindex, MCP servers reconcile through
`sync_servers()`, and prompt macros re-register.

## Thread-scoped activation

Installation and trust are **global**. Activation is **per thread**.

A thread entry in `threads.json` carries an optional per-plugin override map.
Activation resolves per plugin:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
thread override → global default (plugins_state.json) → installed-on
```

So `/plugins disable foo` in one thread turns `foo` off for that conversation
only, leaving your other threads untouched. To change the global default, use the
CLI (`ziro plugin enable|disable`) or install the plugin.

Enforcement is hybrid, applied at each point of use rather than by filtering one
list:

* Skill hits owned by an inactive plugin are dropped from search results.
* A hook belonging to an inactive plugin no-ops when invoked.
* A prompt macro declines at dispatch.
* Spawning an inactive plugin's subagent is rejected. A spawned child inherits
  the parent's active set.
* MCP servers are re-synced on a toggle rather than filtered at call time.

The TUI sidebar has an **Active plugins** section reflecting the thread-effective
set.

## The `plugin_config.yaml` policy gate

Everything above describes what is *currently on*. `plugin_config.yaml` describes
what is *permitted at all*. It is a ceiling, not a switch: a thread override can
narrow within it, but can never widen past it.

<Warning>
  **An agent that no config layer supplies a `plugin_config.yaml` for surfaces
  ZERO plugins.** Plugins are per-agent opt-in. A newly scaffolded agent does not
  silently inherit whatever happens to be installed on the machine.
</Warning>

This applies to the **absence** of the file, not to an unset `enabled:` key
inside one. A file that only narrows with `allow` / `deny` still means the author
wanted plugins, and stays permissive within its allowlist.

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Master switch. `true` (or omitting the key) permits plugins, subject to the
# rules below. `false` surfaces NO plugins for this agent at all.
enabled: true

# Optional allowlist: plugin ids or globs, matched with permission semantics.
# EMPTY MEANS "no allowlist, everything passes", not "nothing passes".
allow: []
# allow:
#   - claude-plugins-official/*

# Deny WINS over allow. Denying a not-yet-installed plugin is legitimate.
deny: []

# Fetch origins pre-approved for `ziro plugin install <market>:<plugin>`.
# Discovery only: does not grant trust, does not skip consent.
preapproved_marketplaces: []
```

The file resolves through the standard chain, most-specific first:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
<project>/.ziro/agents/<id>/plugin_config.yaml
<project>/.ziro/plugin_config.yaml
~/.ziro/agents/<id>/plugin_config.yaml
~/.ziro/plugin_config.yaml
the bundled default
```

Of the shipped agents, only `default` (the Generalist) ships this file, and it
ships permissive: discovering and using whatever a task needs is its whole job.
`researcher` and `researcher_docker` ship no file and are therefore plugin-free
until you add one.

<Danger>
  A malformed `plugin_config.yaml` **fails closed**: no plugins, plus a loud
  warning. The file existing means someone meant to constrain something, so a
  parse error must not be read as "no constraints".
</Danger>

### Project layers are trust-gated too

`plugin_config.yaml` is a dangerous project file. A project-layer copy is ignored
wholesale until you trust the folder, and folder trust is content-hashed the same
way plugin trust is. A cloned untrusted repo cannot set plugin policy on you.

## The SDK is hermetic

A code-first `create_agent(...)` surfaces **zero** plugins by default, so the same
script behaves identically on two machines regardless of what each has installed.
Opt in explicitly:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from ziro import create_agent

create_agent(plugins=True)              # all installed plugins
create_agent(plugins=["cc/git-helper"]) # an allowlist
create_agent(plugins=False)             # none (the default)
```

Passing a bare string raises `TypeError`, so wrap it in a list. An empty list
normalizes to "no plugins", kept distinct from `True`.

## Next steps

<Columns cols={3}>
  <Card title="Plugin CLI reference" icon="terminal" href="/reference/plugin-cli">
    Every `ziro plugin` subcommand and flag.
  </Card>

  <Card title="Skills" icon="book-open" href="/guides/skills">
    How plugin skills join the skill index.
  </Card>

  <Card title="MCP servers" icon="plug" href="/guides/mcp">
    How plugin MCP servers fold into the live manager.
  </Card>
</Columns>
