Tools
Soma tools — registration, configuration via settings.json, and the bundled set
Soma tools are capabilities the agent can call during a session. They’re
registered through Pi’s tool API (pi.registerTool()) but routed through
Soma’s somaRegisterTool() wrapper so the system prompt shows each tool’s
full guidance (promptSnippet + promptGuidelines). Disable any tool via
settings.json → tools.disabled.
Two routes for a tool to enter Soma:
- Bundled — ships with Soma, defined in
repos/agent/extensions/*.ts - Project extension — dropped at
.soma/extensions/*.ts(per workspace)
Both flow through the same pipeline and render identically in the prompt.
Disabling tools — settings.json
Disable tools by name in .soma/settings.json under tools.disabled:
{
"tools": {
"disabled": ["context_status"]
}
}
It follows the body chain (project → parent → global) and the disabled sets
are unioned. Unlisted tools default to enabled, so bundled tools added in
future versions auto-register. The disable applies to every tool — bundled,
project extension, or raw pi.registerTool — because the active set is filtered
at session start, not only at registration.
Hardwired tools — currently delegate — cannot be disabled. Attempts are
ignored with a warning, preventing accidental lobotomies of systems that depend
on the tool (child-agent orchestration).
Retired:
.soma/body/_tools.md(with its## Disabled/## Overrides/## Customsections) is gone. Disable moved tosettings.json; per-tool overrides and the never-shipped markdown-custom-tools were dropped. Existing_tools.mdfiles auto-migrate on next boot (disabled entries ported to settings, the file archived tobody/_archive/).
Sub-agent scoping
Child roles narrow their toolset with a role-scoped tools.disabled in their
own settings — e.g. a read-only verifier disabling delegate, write, edit.
Bundled tools
Shipped in repos/agent/extensions/ as of v0.20.2.1.
| Tool | Extension | Purpose |
|---|---|---|
delegate | soma-delegate.ts | Spawn a focused child agent for a bounded task. Synchronous by default; set background:true for a detached terminal session. Hardwired. See guides/background-delegation.md. |
children | soma-delegate.ts | Inspect/manage background children spawned via delegate(background:true). Ops: list / tail / steer / kill / harvest. See guides/background-delegation.md. |
capabilities | soma-capabilities.ts | Introspect the tool registry. op:'list' — all tools; op:'detail', name:'<tool>' — full guidance. |
soma (cap='soma:code.find') | soma-addons/code.ts | Grep with file:line output. Respects .gitignore. |
soma (cap='soma:code.map') | soma-addons/code.ts | Function/class/method index for one file (TS/JS/CSS/Bash). |
soma (cap='soma:code.refs') | soma-addons/code.ts | Symbol references split into DEF (definition) vs USE (usage). |
soma (cap='soma:code.structure') | soma-addons/code.ts | Directory tree with sizes. Respects .gitignore. |
soma (cap='soma:code.blast') | soma-addons/code.ts | Every file touching a symbol with severity — pre-deletion check. |
soma (cap='soma:code.outline') | soma-addons/code.ts | Markdown/text headings with line numbers — cheap orientation. (Was file_outline.) |
soma (cap='soma:code.history') | soma-addons/code.ts | git log for a file as structured output (sha + date + author + subject). Replaces raw git log --format shell calls. (v0.23.0+, SX-700.) |
soma (`cap=‘soma:github.meta | files | search |
soma (cap='soma:body.slots') | soma-addons/body.ts | Slot map of _mind.md with per-slot cache-impact. Run before editing body templates. |
soma (cap='soma:body.cost') | soma-addons/body.ts | Cache-invalidation cost of editing a specific slot. args: {slot}. |
soma (cap='soma:body.audit') | soma-addons/body.ts | Heuristic audit of the body compile: duplicate slots, missing files, cache-unfriendly ordering. |
soma (`cap=‘soma:docs.list | show | search |
soma (cap='soma:browser.*') | soma-addons/browser.ts | Browser automation via CDP: 21 caps — status, tabs, version, new_tab, close_tab, activate_tab, setup, config (standalone) + navigate, screenshot, evaluate, console, accessibility, links, styles, emulate, performance + xray, click, fill, wait (bridge). Captures tabId; click uses Input.dispatchMouseEvent. |
soma (cap='soma:agent.delegate') | soma-addons/agent.ts | Spawn a child agent. args: {task, role?, model?, background?, terminal?}. Sync by default; background:true returns immediately + registers in children.json. |
soma (`cap=‘soma:agent.list | tail | steer |
soma (`cap=‘soma:focus.show | set | clear |
soma (`cap=‘soma:new.muscle | protocol’`) | soma-addons/new.ts |
soma (`cap=‘soma:terminals.list | detect | status |
soma (`cap=‘soma:inbox.list | read | actioned |
somaverse (cap='somaverse:workspace.*') | somaverse-addons/workspace.ts | Panes / channels / seams (status, send, connect, snapshot, add_pane, remove_pane, list_plugins, …). Requires bridge + paired hub. |
somaverse (`cap=‘somaverse:plugin.read | write’`) | somaverse-addons/plugin.ts |
somaverse (cap='somaverse:ai.*') | somaverse-addons/ai.ts | Local semantic search: load model, index, search, embed. |
somaverse (`cap=‘somaverse:bridge.status | config | setup |
somaverse (`cap=‘somaverse:auth.status | start | logout’`) |
Namespace migration (v0.22.0, SX-594): flat
code_*/file_outline/workspace_*/plugin_state_*/browser_*/ai_*/dev:body.*/delegate/childrentools were folded into thesoma:*+somaverse:*meta-tools. Call viasoma(op='call', cap='soma:code.find', args={...})orsomaverse(op='call', cap='somaverse:workspace.status'). Usesoma(op='list')/somaverse(op='list')to discover the full catalog. Legacy flat names archived toextensions/_archive/sx594-flat-wrappers/.Cache math: 1 meta-tool registration costs 1 tool slot in the prompt; addons are free (discovered at runtime via the factory). Flat tools cost linear: N tools = N slots. Post-SX-594 + SX-609, the prompt registers
soma/somaverse/dev/capabilities, plus Pi builtins (bash/read/write/edit), plus the 10office_*still parked as flat (SX-606). Approximately 19 slots for 100+ reachable caps. |context_status|soma-context.ts| Current context usage{percent, tokens, contextWindow}. | |search|soma-search.ts| Unified search — local ripgrep (default), Brave API, semantic (v0.20.3.1). |
Each tool defines:
name— identifier the model invokesdescription— full explanation (fallback in prompt)promptSnippet— snappy one-line tagline (primary prompt render)promptGuidelines— per-tool mechanics bullets (how/when to use it)parameters— TypeBox schema (validated by Pi)execute— the handlerexecutionMode—parallel(read-only, safe concurrent) orsequential
Read the source — each extension file has the authoritative definition and inline commentary on why each guideline exists.
Discovering what’s registered
Two surfaces (SX-558):
From a shell, without starting the agent:
soma tool # list all registered tools (one-liner each)
soma tool delegate # full guidance for one tool
soma tool --extensions # group by extension file
This runs an offline static parser over extensions/*.ts (or
dist/extensions/*.js for release installs). It shows the authored definition.
Inside a running Soma session, with overrides applied:
capabilities(op: "list")
capabilities(op: "detail", name: "delegate")
The capabilities tool reads the runtime Soma registry and reflects
active/inactive status for the current session.
Writing a new tool
See Extending Soma → Soma Tools for the full pattern. The shortest version:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { somaRegisterTool } from "../core/tool-registry.js";
export default function myExtension(pi: ExtensionAPI) {
somaRegisterTool(pi, {
name: "my_tool",
label: "My Tool",
description: "Full description the model reads.",
promptSnippet: "my_tool: one-line tagline for the prompt header",
promptGuidelines: [
"Use my_tool when X",
"Chain with Y for Z",
],
parameters: Type.Object({
input: Type.String({ description: "What to process" }),
}),
executionMode: "parallel",
execute: async (_id, { input }, _signal, _onUpdate, _ctx) => {
return {
content: [{ type: "text" as const, text: `Processed: ${input}` }],
details: undefined,
};
},
});
}
Always use somaRegisterTool over pi.registerTool directly — it
preserves promptSnippet and promptGuidelines in the compiled prompt.
(Disable still works for raw pi.registerTool tools too, via the active-set
filter at session start.)
Third-party tools that call pi.registerTool directly still work (Pi
registers them, they’re invokable) but miss the rich prompt surface.
For NEW caps (not top-level tools), prefer route.provide via
session_start. The cap lives in soma-route’s runtime registry — zero
cache cost, surfaced via the existing soma/dev/somaverse meta-tools.
See extending.md § When to add a new cap
and extending.md § Project-local caps.
somaRegisterTool remains the right call for rich top-level tools
(legacy or genuinely tier-1) where you want promptSnippet /
promptGuidelines in the compiled prompt; route.provide is for
everything else.
How tools render in the system prompt
With somaRegisterTool + the Soma compiler:
Available tools:
- soma:code.find: grep with file:line output across a codebase, respects .gitignore (cap 500)
- soma:code.map: function/class/method index for a single file with line numbers
- ...
In addition to the tools above, you may have access to other custom tools depending on the project.
Guidelines:
- [soma:code.find] output is file:line:text — chain with read(path, offset=line) to pull surrounding context
- [soma:code.find] If it returns 'capped at 500', narrow via path or ext params instead of raising limit
- [soma:code.map] Run before editing a file you haven't read recently — shows structure at a glance
- ...
- Use bash for file operations like ls, rg, find
- Be concise in your responses
Per-tool guidelines are prefixed [tool_name]. Generic guidelines
(activity-conditioned rules like “use read before edit”) follow.
Invocation vs prompt
Two registries exist:
- Pi’s invocation registry — what makes the tool callable at runtime.
pi.registerToolwrites here. - Soma’s prompt registry — what drives
buildToolSection.somaRegisterToolwrites here (and also forwards to Pi).
If tools.disabled lists a tool, it’s filtered from the active set —
the tool isn’t callable and isn’t prompted.
Related
- Extending Soma — how to write extensions, the drop-in
router (
soma-route.ts), events, APIs - System Prompt — how
_mind.mdrenders with tools - Body architecture — where settings sits in the chain