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

# Plugin Authors

> Guide for building plugins and custom skill handlers.

# Plugin authors guide (v0.15)

`modusbrain` discovers subagent definitions from outside this repo via
`MODUSBRAIN_PLUGIN_PATH`. If you maintain a downstream agent (your OpenClaw
deployment, a workflow host, a private tool) and want to ship custom
subagents alongside it, drop a plugin directory on that env path.

This guide is for plugin authors. The CLI user doesn't need to read it.

## Minimum viable plugin

```
/path/to/my-plugin/
├── modusbrain.plugin.json
└── subagents/
    └── my-summarizer.md
```

`modusbrain.plugin.json`:

```json theme={null}
{
  "name": "my-plugin",
  "version": "1.0.0",
  "plugin_version": "modusbrain-plugin-v1"
}
```

`subagents/my-summarizer.md`:

```markdown theme={null}
---
name: my-summarizer
model: claude-sonnet-4-6
allowed_tools:
  - brain_search
  - brain_get_page
---

You are a brain page summarizer. Given a slug, fetch the page and produce
a 3-sentence summary.
```

## Turning it on

```bash theme={null}
export MODUSBRAIN_PLUGIN_PATH="/path/to/my-plugin"
modusbrain jobs work           # worker startup prints the plugin load line
modusbrain agent run "summarize meetings/2026-04-20" --subagent-def my-summarizer
```

Multiple plugins: colon-separated, just like `$PATH`.

```bash theme={null}
export MODUSBRAIN_PLUGIN_PATH="/path/to/plugin-a:/path/to/plugin-b"
```

## Rules (strict by design)

**Path policy.** Absolute paths only. Relative paths, `~`-prefixed paths,
and URL-style paths (`https://`, `file://`) are rejected with a warning.
You control where your plugin lives on disk; `modusbrain` doesn't guess.

**Collision policy.** If two plugins ship a subagent with the same `name`,
the one listed FIRST in `MODUSBRAIN_PLUGIN_PATH` wins. The other is dropped
with a warning naming both sources.

**Trust policy.** Plugins ship subagent definitions ONLY in v0.15:

* You **cannot** declare new tools.
* You **cannot** extend the brain tool allow-list.
* You **cannot** override any `agentSafe` or similar flag.
* Your `allowed_tools:` frontmatter field MUST subset the derived brain
  tool registry. Names not in the registry are rejected at plugin load
  time (worker startup), NOT at subagent dispatch time — so a typo in
  your plugin gives you a loud startup error, not a silent "tool never
  fires" at 3am.

v0.16+ may open up plugin-declared tools with a separate contract. Don't
expect it.

## `modusbrain.plugin.json`

| field            | type   | required | notes                                                              |
| ---------------- | ------ | -------- | ------------------------------------------------------------------ |
| `name`           | string | yes      | Human-readable plugin id. Shows up in warnings and collision logs. |
| `version`        | string | yes      | Your plugin's semver. Informational.                               |
| `plugin_version` | string | yes      | Contract lock. Must equal `"modusbrain-plugin-v1"` for v0.15.      |
| `subagents`      | string | no       | Subdir name (default `subagents`). Escape-attempts are rejected.   |
| `description`    | string | no       | Shown in future `modusbrain plugin list`.                          |

## Subagent definition files

Plain markdown with YAML frontmatter. The body is the system prompt. The
frontmatter controls runtime behavior.

Recognized frontmatter fields:

| field           | type      | required | notes                                                                                  |
| --------------- | --------- | -------- | -------------------------------------------------------------------------------------- |
| `name`          | string    | no       | Subagent identifier used as `--subagent-def`. Defaults to the file basename.           |
| `model`         | string    | no       | Anthropic model id. Defaults to the handler default (sonnet).                          |
| `max_turns`     | number    | no       | Cap on assistant turns. Defaults to 20.                                                |
| `allowed_tools` | string\[] | no       | Whitelist of tool names. Must subset the derived brain registry. Rejected on mismatch. |

Unknown frontmatter fields are preserved but ignored by the handler. v0.16
may consume more of them.

## Caveats that will bite you

1. **Plugin definitions can't change during a run.** The loader reads the
   disk once at worker startup. Editing a subagent def doesn't re-take
   effect until you restart the worker. This is deliberate — live
   reloads would break crash-resumable replay.

2. **`~/.modusbrain/audit/subagent-jobs-*.jsonl` is local only.** If your
   worker runs on a different host than the `modusbrain agent logs` caller,
   the CLI won't see heartbeats from that worker. v0.16 will unify this;
   for now assume worker + CLI share a filesystem.

3. **Tool calls always run with `ctx.remote = true`.** Even on local CLI
   invocation. Tools that gate on `remote=true` (file\_upload's strict
   confinement, put\_page's namespace check) will apply. Good default; a
   subagent definition that wants local-filesystem reach beyond the brain
   can't have it.

4. **`put_page` writes are namespace-scoped.** A subagent with id 42 can
   only write under `wiki/agents/42/...`. This is enforced both in the
   tool schema (the slug pattern shown to the model) AND server-side in
   the `put_page` operation (fail-closed if `viaSubagent=true`). Don't
   try to route around it; you'll get `permission_denied`.

## Example: a downstream-OpenClaw plugin

```
~/your-openclaw/
└── modusbrain-plugin/
    ├── modusbrain.plugin.json
    └── subagents/
        ├── meeting-ingestion.md
        ├── signal-detector.md
        └── daily-task-prep.md
```

`~/your-openclaw/modusbrain-plugin/modusbrain.plugin.json`:

```json theme={null}
{
  "name": "your-openclaw",
  "version": "2026.4.20",
  "plugin_version": "modusbrain-plugin-v1",
  "description": "Your OpenClaw's personal-brain subagents"
}
```

Environment:

```bash theme={null}
export MODUSBRAIN_PLUGIN_PATH="$HOME/your-openclaw/modusbrain-plugin"
```

Then your OpenClaw calls `modusbrain agent run --subagent-def meeting-ingestion --fanout-by transcript ...` and its definitions load automatically.
