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

# Capabilities and plugins

> Skills, sub-agents, personas, commands and MCP servers are all capabilities, and oceancode finds them the same way: one discovery walk over five tiers, across four ecosystems, with a single rule for names that collide and a separate rule for what a capability is allowed to do.


Skills, sub-agents, personas, commands and MCP servers are all **capabilities**, and `oceancode`
finds them the same way: one discovery walk over five tiers, across four ecosystems, with a
single rule for names that collide and a separate rule for what a capability is allowed to do.

## The five tiers

A capability is discovered from one of five tiers. The walk runs from the narrowest scope out to
the widest, so a name defined closer to the code wins.

| Tier | Where it comes from | Example root |
|---|---|---|
| Local | The project's own `.oceancode/` — your machine's private copy, git-ignored | `.oceancode/skills`, `.oceancode/agents` |
| Repo | The project you are in, checked into the repo | `.claude/`, `.opencode/`, `.grok/` |
| User | Your home directory, read across every ecosystem | `~/.oceancode/…`, `~/.claude/…`, `~/.grok/…` |
| Bundled | Shipped inside the binary | (built in) |
| Plugin | An installed plugin's own tree | `<plugin>/skills/`, `<plugin>/agents/` |

Plugin-tier roots are enumerated from disk — one root per installed plugin directory — so a
plugin contributes its skills, agents and hooks only while it is installed. For plugins Claude
Code installed, the roots are **exact**: only the version directories its
`~/.claude/plugins/installed_plugins.json` records for the user scope, or for this project, are
walked (one root per plugin even when the file lists the same install twice), and a plugin you
have disabled in `~/.claude/settings.json` (`enabledPlugins`) stays disabled here. A project's
own `.claude/settings.json` can never re-enable a plugin you disabled.

The `.oceancode/` directory in a project is the **local** tier — your machine's private copy,
trusted precisely because it is not shared repo content — and is git-ignored in this monorepo.

## Four ecosystems

Each tier is read for four ecosystems at once, so a capability written for another tool is picked
up without conversion:

- **OceanCode** — the native `.oceancode/` layout.
- **Claude Code** — `.claude/` (skills, agents, rules, `settings.json`).
- **OpenCode** — `.opencode/` (`opencode.json`, agents).
- **Grok Build** — `.grok/` (agents, personas, rules, memory).

Frontmatter and directory conventions from each ecosystem are translated into the one internal
shape as they are read; you do not have to rewrite a Claude Code skill or a Grok persona to use
it here. A skill marked `disable-model-invocation: true` is yours to run: `/<name>` runs it and it
counts as enabled, but the model is not offered it (no `skill_<name>` tool, no entry in the skills
list of its prompt).

Rule files may pull in other files with `@path` imports (up to four hops); an import is bounded to
the project directory and your home directory, so a rule can never inline a file from elsewhere on
disk.

A rule may scope itself to a file glob in its frontmatter — `paths: "**/*.py"` (Claude Code / OpenCode)
or `globs: "**/*.ts"` (a Cursor `.mdc`). A scoped rule is loaded only when the working tree actually
contains at least one file matching its glob; in a checkout with no match it is left out entirely, so a
TypeScript project does not carry Python or Ruby rule bodies. The check honours your ignore rules
(`.gitignore`, `.git` pruned) and runs once per session. Scope today is resolved for the whole checkout,
not per individual file the current turn touches. A rule with no `paths`/`globs` is always loaded — with
one Cursor exception: a `.mdc` under `.cursor/rules` that says `alwaysApply: false` and has no `globs` is
Cursor's agent-requested or manual rule, which Cursor attaches only when asked, and the engine leaves it
out too (a `globs:` rule keeps its scope whatever the flag says; the key means nothing outside
`.cursor/rules`).

## Workflow scripts

Workflow scripts are discovered the same way, from two OceanCode roots in the usual precedence
(**built-in > project > user**). Every `*.rhai` file under a root is one workflow, identified by
the `meta.name` its script declares — not by its file name:

| Scope | Root | Files |
|---|---|---|
| Project | `.oceancode/workflows` | `*.rhai` |
| User | `~/.config/oceancode/workflows` | `*.rhai` |

Two workflows ship **built in** to the binary — the highest tier — so they run by name with no
`*.rhai` file on disk. Built-ins **cannot be shadowed**: a project or user script with the same
`meta.name` as a built-in is ignored (dropped from the picker catalog with a warning), and the
built-in still runs — that is what "built-in > project > user, first wins" means. A duplicate
`meta.name` across two discovered files likewise keeps the first (project before user).

| Name | Args | What it does |
|---|---|---|
| `deep-research` | `query` (or `objective`); `breadth` 2–6, default 4 | Researches the query with bounded parallelism across four phases — Plan, Research, Verify, Report — independently cross-checks every candidate claim, and writes a cited report. |
| `goal` | `objective`; `skeptics` 1–5, default 3; `max_rounds`, default 10; optional `worker_model`, `verifier_model` | Proves an objective is done, not just claimed done: plans it into verifiable deliverables, works in rounds with one resumed worker, then has a panel of adversarial skeptics try to refute completion. Converges only when a strict majority of the cold panel fails to refute; pauses on identical gaps two rounds running or a spent agent budget. |
| `pipeline` | `steps: [{ prompt, label?, model?, agent_type?, output_schema? }]`; `stop_on_failure`, default true | Runs an ordered list of agent steps one after another; each step is given the previous step's output. Stops at the first failed step unless `stop_on_failure` is false. |

## Names that collide are qualified; unique names stay bare

A capability keeps its bare `name` when that name is unique across everything discovered. Only
when the same name appears more than once is it **qualified**, and only the colliding members are
rewritten:

| Situation | Qualified name |
|---|---|
| Same name at two different tiers | `tier:name` (e.g. `repo:review`) |
| Same tier, different ecosystems | `tier.<ecosystem>:name` (e.g. `repo.claude_code:review`, `repo.open_code:review`) |
| A plugin tier | `<plugin>:name` |
| A residual same-tier, same-ecosystem tie | `tier.<ecosystem>.<root-dir>:name` |

The pass reads only each item's bare name and source, so it is idempotent — running discovery
again yields the same qualified names in the same order.

The **slash name** of a plugin skill is always `<plugin>:name`, even when the name is unique —
Claude Code's convention, so `/superpowers:writing-plans` lists and runs in the Ocean pager's `/`
popup exactly as it does there. Typed as a prompt, `/<plugin>:name` and the bare `/name` both run
an enabled plugin skill; a `<plugin>:` prefix that names no installed plugin does not resolve (a warning toast; the text is then sent as a normal message).

## Enable is not trust

Two independent gates decide what a capability does:

- **Enabled** — whether the capability is discovered and offered at all. Disabling a plugin
  removes its capabilities from the walk — including its rule bodies, which no longer inject
  into the system prompt (its skills, agents, commands, hooks and MCP servers drop too).
- **Trusted** — whether a capability that can *execute code on your behalf* is allowed to run
  without a prompt. **Only hooks and MCP servers gate on trust**; a skill or a persona is inert
  data and needs no trust decision.

By default, Local, User and Bundled tiers are trusted; the Repo tier is untrusted (code that
arrived with a checkout has not been vetted by you); a Plugin is trusted only if you installed it
with trust. So enabling a repo-supplied hook is not the same as trusting it — it is discovered,
but it will not run its command until you trust it.

Trust decisions for a project persist in `~/.oceancode/trust.json`, keyed by the project's
absolute path: `repo` (may the repo tier's hooks and `.mcp.json` servers execute?), `mcp`
(per-server overrides that beat `repo`) and `disabled_mcp` (servers held disabled at any tier).
Plugin trust lives beside the plugin in `~/.oceancode/plugins/installed.json`. Set them with
`oceancode trust repo on|off`, `oceancode trust mcp <name> on|off|clear`, `oceancode plugin
trust <name> [--off]`, or from the `/hooks`, `/mcp` and `/plugins` pickers; read them with
`oceancode trust show`.

## Enabling and disabling skills

Loading every skill from every tier costs prompt tokens — one `<available_skills>` line and one
`skill_*` tool per skill, plus a disabled plugin's rule bodies. Skills, plugins and MCP servers
share **one enable interface** so you can turn off what you are not using; a disabled skill costs
nothing (it is not in `<available_skills>`, registers no `skill_*` tool, and its rules do not
inject).

A skill's effective state is the fold of four tiers, resolved in this order — the first that
turns it off wins, so a **project can disable a skill but can never re-enable one you disabled**:

| # | Tier | Turned off by | `skill list` reason |
|---|---|---|---|
| 1 | Plugin | The skill's plugin is disabled (`oceancode plugin disable`) | `disabled (plugin)` |
| 2 | User | You disabled the skill (`~/.oceancode/skill-state.json`) | `disabled (user)` |
| 3 | Project | `skills.disabled` in `.oceancode/oceancode.json` | `disabled (project)` |
| 4 | Allowlist | `skills.mode = "allowlist"` and the skill is not in `skills.enabled` | `not in allowlist` |

The per-skill **user** toggle is written by the CLI (or the `/skills` picker) into
`~/.oceancode/skill-state.json` — OceanCode's own store, never Claude's `~/.claude/settings.json`.
The **project** policy (`mode`, `disabled`, `enabled`) lives in `.oceancode/oceancode.json` under
`skills` (see [02 · Configuration](/oceancode/configuration)); default mode `all` keeps every skill a
candidate. The project policy is a **default**: an explicit enable of one skill (`Space` on its row,
a plugin-group header's `Space`, or `oceancode skill enable <id>`) is also recorded for this project
as `enabled_skills` in `~/.oceancode/trust.json` and beats the project's `skills.disabled` and
allowlist (tiers 3–4), so a skill the allowlist leaves out can be switched on without editing the
config; it never beats the master switch or a disabled plugin, a disable drops it again, and
`enable-all` stays user-tier only. MCP keeps its own `disabled_mcp` / `enabled_mcp` model
(`oceancode mcp enable|disable`, see [11 · MCP servers](/oceancode/mcp)); the verbs are the same shape.

```bash
oceancode skill enable <id>            # clear the user-tier disable; beats the project policy
oceancode skill disable <id>           # disable this skill at the user tier
oceancode skill enable-all             # clear every user-tier skill disable
oceancode skill disable-all            # disable every currently-discovered skill
```

`<id>` is a skill's **stable id** `<tier>.<eco>:<name>` — e.g. `user.claude_code:agents-sdk`,
`local.ocean_code:tdd`, `repo.open_code:deep` — shown in the `id` column of `skill list`. It is
computed the same way whether or not the name collides with another tier, so the store never loses
a disable when a session sees only one tier. You may also pass a **bare name** when exactly one
discovered skill has it (`oceancode skill disable wrangler`); a name shared by more than one skill
is refused with the candidate ids, so pass the full `id` to pick one. A bare name is still accepted
in `skills.disabled`/`skills.enabled` and as a legacy store entry (it matches every copy of that
name). If you disabled skills before this change, re-run `oceancode skill disable-all` once to
rewrite the store with stable ids.

A toggle takes effect **on the next step** of the running session. The `/skills` and capabilities
pickers, the `/skill` invocation gate and the Ocean pager's composer `/` completion update at once
(an enabled skill is offered as `/<skill>`, a disabled one is not); the `<available_skills>` block,
the `skill_*` tools, the MCP tool list and the hook dispatcher are rebuilt before the agent's next
model call, so the very next request no longer carries what you turned off. A step already running
finishes with what it started with. Each change made mid-session costs **one system-prompt cache
rewrite** (the cached prefix changes once), so flip several things together rather than one per
turn. A change made from a shell (`oceancode skill disable-all`) lands on the next step too.

**Whole groups and everything at once.** In the Ocean pager's capabilities picker (`/skills`,
`/plugins`, `/hooks`, `/mcps`) `Space` works at four levels:

| Row under the cursor | `Space` toggles | Asks first |
|---|---|---|
| A skill, plugin, hook or MCP server | that one item | no |
| A `Plugin: <name>` header (Skills, MCP tabs) or a hook source header (Hooks tab) | every row in the group | above 10 rows |
| `All skills` / `All plugins` / `All MCP` / `All hooks` (second row of the tab) | the whole kind | yes |
| `Everything` (first row of every tab) | the master switch | yes |

`Everything` off removes skills, MCP tools, hooks and plugin rules from the agent's context; the
built-in tools (bash, read, edit, …) stay. Turning it back on restores exactly the per-item state
you had. A group whose rows are all off shows `[off]` on its header, and the picker's last row
shows the live counts — `skills 22/259 · plugins 2/21 · mcp 1/10 · hooks 8/8` — with
`EVERYTHING OFF` in front while the master switch is off. Group and master state is per project,
stored next to `disabled_mcp` in `~/.oceancode/trust.json`; per-skill disables stay in
`skill-state.json`. The Plugins tab lists Claude Code's cached plugins (under `User (Claude)`)
beside the OceanCode-store ones, and `Space` turns a whole plugin — its skills, hooks, MCP servers
and rules — off or on.

**Start from nothing.** To strip the prompt down and add capabilities back deliberately, disable
everything and re-enable one by one: `oceancode plugin disable-all` (then re-enable from
`/plugins`), and `oceancode skill disable-all` with `skills.mode = "allowlist"` + a short
`skills.enabled` list for the handful of skills you actually want. With every plugin disabled and
an empty allowlist, the first turn carries no `<available_skills>` block and no `skill_*` tools at
all.

## `oceancode inspect` — where did this come from?

To see the winning source of every capability, config key, MCP server and instruction file for
the current directory:

```bash
oceancode inspect          # a human-readable provenance table
oceancode inspect --json   # a stable JSON array (for scripts)
```

Each row names the kind, the id, the layer that won, and the file it came from — the fastest way
to answer "why is *this* skill loaded and not that one?". It boots nothing and is read-only.

`instruction` rows list what the directory contributes to the system prompt: one primary file —
`CLAUDE.md`, else `AGENTS.md`, never both, so a generated `AGENTS.md` mirror is not loaded twice —
then `CLAUDE.local.md` and `CONTEXT.md` when present. The same rule applies at every level from the
worktree root down to the working directory, and to the reminder that fires when a tool first touches
a directory this session. The system prompt is fixed for the rest of a prompt, so an edit to these files
(or a URL instruction expiring) made while a prompt runs applies from the next prompt, not the next step;
switching a capability or the model still applies on the very next step. The date is not in the system
prompt: each new prompt carries `Today's date`, so midnight does not re-write the cached prompt.

Memory is imported read-only after the instructions: Claude Code's `MEMORY.md` for this project only
(`~/.claude/projects/<key>/memory/`, where `<key>` is the working directory's or the worktree root's
path with every character other than a letter or digit turned into `-`, the way Claude Code names it),
and Grok's global `~/.grok/memory/MEMORY.md`. Another project's memory never enters this one's prompt.

## Plugins and marketplaces

A plugin bundles skills, agents and hooks under one installable tree. Manage them from the CLI:

```bash
oceancode plugin marketplace add <spec>      # register a marketplace
oceancode plugin marketplace list
oceancode plugin marketplace update <name>
oceancode plugin marketplace remove <name>

oceancode plugin install <name-or-path> [--trust] [--marketplace <m>]
oceancode plugin list
oceancode plugin enable <name>               # / disable <name>
oceancode plugin enable-all                  # / disable-all — flip every installed plugin
oceancode plugin trust <name> [--off]        # grant / revoke trust
oceancode plugin validate <name>             # check the manifest
oceancode plugin update <name>               # / uninstall <name>
```

`install` without `--trust` enables the plugin but leaves it untrusted, so its hooks and MCP
servers stay dormant until you run `plugin trust`. Pass `--root <dir>` to any `plugin` subcommand
to point at a project other than the current directory.

In Ocean pager, `/plugins` opens the Plugins tab of the capabilities picker; see
[19 · Ocean pager](/oceancode/pager#what-is-wired-to-the-ocean-engine).

## Importing from Claude Code

If you already use Claude Code, one command brings its setup here:

```bash
oceancode plugin import --from claude-code --dry-run   # show what would happen, write nothing
oceancode plugin import --from claude-code --trust     # do it, trusting the imported plugins
```

What it does, and what it deliberately does not:

- **Plugins are copied** from Claude Code's cache (`~/.claude/plugins/cache/<marketplace>/<name>/<version>/`)
  into `~/.oceancode/plugins` — only the versions installed for the user scope or for this
  project — and each is enabled or disabled as your `~/.claude/settings.json` says. Without
  `--trust` they are enabled but untrusted, so their hooks and MCP servers stay dormant.
- **Marketplaces** Claude Code knows (`known_marketplaces.json`) are registered.
- **Trust is seeded** from Claude Code's own record in `~/.claude.json`: the project's
  `hasTrustDialogAccepted` becomes `repo=true` (pass `--no-repo-trust` to opt out), servers in
  `disabledMcpServers` / `disabledMcpjsonServers` are held disabled, `enabledMcpjsonServers` are
  trusted.
- **Skills, commands, agents, rules, hooks and MCP configs at the user and repo tiers are not
  copied.** `~/.claude/skills`, `~/.claude/commands`, `~/.claude/settings.json`, `.claude/**` and
  `.mcp.json` are read where they are, so they stay in sync with Claude Code.

Every line the command prints names one action (`IMPORT`, `SKIP`, `MISSING`, `MARKETPLACE`,
`TRUST`); the last line is `IMPORT_OK plugins=<n> marketplaces=<m> repo_trust=<bool>` (with
`--dry-run`, `IMPORT_DRY_RUN_OK …`). A cache tree that is not a plugin — Claude Code's
`rust-analyzer-lsp` / `typescript-lsp` entries carry neither a manifest nor a `SKILL.md` — is
reported `SKIP <name> not a plugin` and one entry that fails to install is reported
`SKIP <name> install failed: <why>`; neither stops the run, so the trust seeding at the end always
happens. Nothing under `~/.claude` is ever modified.

## `/skills` — browse and run

Every discovered skill — yours, the project's, a plugin's, from any of the four ecosystems — is a
slash command: `/<name> <arguments>` runs it as a command turn whose prompt is the skill body
prefaced by a one-line instruction, with `$ARGUMENTS` and the positional rules applied. A name
that collides across tiers is invoked by its qualified form (`/repo:review`). The completion popup
lists skills with the other commands.

In Ocean pager, `/skills` opens the Skills tab of the capabilities picker and the composer's `/`
completion lists your enabled skills; see
[19 · Ocean pager](/oceancode/pager#what-is-wired-to-the-ocean-engine). Without a session:

```bash
oceancode skill list            # id  tier  trusted|untrusted  <state>  description
oceancode skill list --json     # each row carries "id", "enabled" and a "reason" tag when disabled
```

The `id` column is the stable `<tier>.<eco>:<name>` id (`--json` also keeps the collision-resolved
`qualified` name); copy it straight into `oceancode skill enable|disable <id>`.

The state column reads `enabled`, or why the skill is off — `disabled (plugin)`,
`disabled (user)`, `disabled (project)` or `not in allowlist` (see
[Enabling and disabling skills](#enabling-and-disabling-skills)).

## See also

- [10 · Hooks](/oceancode/hooks) — the one capability that gates on trust and can change a run.
- [11 · MCP servers](/oceancode/mcp) — the other trust-gated capability, and its own translation table.
- [19 · Ocean pager](/oceancode/pager) — `/plugins`, `/skills`, `/mcps`, `/hooks` in the terminal UI; the pager's other commands are in `ocean-pager/crates/ocean-pager-ui/docs/user-guide/`.
- [07 · Headless and server](/oceancode/headless-and-server#managing-capabilities-from-the-cli) — every management subcommand in one table.
