OceanCode
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.
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.
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); 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); the verbs are the same shape.
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:
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, or midnight passing) 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.
Plugins and marketplaces
A plugin bundles skills, agents and hooks under one installable tree. Manage them from the CLI:
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.
Importing from Claude Code
If you already use Claude Code, one command brings its setup here:
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 pluginsWhat 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.jsonsays. Without--trustthey 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'shasTrustDialogAcceptedbecomesrepo=true(pass--no-repo-trustto opt out), servers indisabledMcpServers/disabledMcpjsonServersare held disabled,enabledMcpjsonServersare 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.jsonare 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. Without a session:
oceancode skill list # id tier trusted|untrusted <state> description
oceancode skill list --json # each row carries "id", "enabled" and a "reason" tag when disabledThe 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).
See also
- 10 · Hooks — the one capability that gates on trust and can change a run.
- 11 · MCP servers — the other trust-gated capability, and its own translation table.
- 19 · Ocean pager —
/plugins,/skills,/mcps,/hooksin the terminal UI; the pager's other commands are inocean-pager/crates/ocean-pager-ui/docs/user-guide/. - 07 · Headless and server — every management subcommand in one table.