Custom Agents
A custom agent is a reusable, named agent definition: a markdown file that gives an agent a description, a specialization prompt, and optional limits on its tools, permissions, and model. Once defined, a custom agent can be used two ways:
- As a subagent type: when the agent delegates work, it can pick your custom agent by name and the subagent runs with that definition's prompt and limits.
- As the main session: launch
pw code --agent <name>to run a definition as the agent you talk to directly.
Where Definitions Live
pw code looks for .md definition files in these directories, in order (when the same agent name appears in more than one place, the earlier directory wins):
<workspace>/.agents/agents/: project definitions, checked in with your code~/.config/agents/agents/: your personal definitions (respectsXDG_CONFIG_HOME)
Directories are scanned recursively, so you can organize definitions into subfolders. An agent's identity comes from its name field (or its filename), never its path. The name fork is reserved for conversation forks; a definition named fork is ignored with a warning.
Project definitions require approval
Definition files in the workspace are part of the configuration pw code asks you to approve at launch. See Workspace Trust.
File Format
A definition is YAML frontmatter followed by a markdown body. The body is the agent's specialization prompt. It is appended to the standard system prompt, so the agent keeps all its normal abilities and gains your specialization on top.
| Field | Meaning |
|---|---|
name | The agent's name. Defaults to the filename without .md. |
description | What this agent is for. This is how the main agent decides when to delegate to it. Write it like a job description. |
tools | Comma-separated allowlist of tool names the agent may use (for example ReadFile, GrepSearch, GlobSearch, Bash). Empty means all tools. See Tool Names. |
disallowedTools | Comma-separated denylist. Deny always wins over tools. |
model | Model this agent runs on (owner:provider-name/model-name). Empty inherits; see model resolution. |
permissionMode | Requested permission mode: read-only, plan, accept-edits, or bypass-permissions. Can narrow the session's mode, never widen it (see Permission Clamping). |
isolation | worktree runs the agent's delegated tasks in a temporary git worktree, keeping its changes out of your working tree. |
maxTurns | Cap on agentic steps per delegated task. Unset uses the session's subagentMaxTurns setting, which defaults to 30. A negative value removes the cap. |
timeoutMinutes | How long a delegated task may go without making progress before it is stopped. Unset uses the session's subagentTimeoutMinutes setting, which defaults to 15 minutes. A negative value removes the budget. |
background | true forces this agent's tasks to always run detached, even when an inline result was requested. |
color | Display color for the agent's switcher dot and task blocks: blue, green, yellow, purple, cyan, orange, pink, or red. |
initialPrompt | Auto-submitted as the first message when the definition runs as the main session via --agent. |
skills | Skills preloaded into the agent's system prompt when it starts, comma-separated. See Preloaded Skills. |
mcpServers | MCP servers this agent can reach, on top of the session's. A bare name shares a server the session already configures; an inline entry defines a server the agent connects for itself. See MCP Servers for an Agent. |
Frontmatter values are single-line, though the list fields (tools, disallowedTools, skills, and mcpServers) can also be written as YAML lists. isolation, background, color, and timeoutMinutes shape how the agent behaves when it runs as a subagent; they do not apply to a --agent main session.
Tool Names
tools and disallowedTools match the agent's built-in tool names exactly (case-sensitive):
| Tool | What it does |
|---|---|
ReadFile, WriteFile, EditFile | Read, create, and edit files |
GlobSearch, GrepSearch | Find files by name and search file contents |
Bash, BashOutput, KillShell | Run shell commands and manage background shells |
WebFetch | Fetch a URL |
Task | Delegate work to a subagent |
SendMessage | Send an interim message from a subagent to the main session |
AskUserQuestion | Ask you a question (main session only) |
TodoWrite | Track a task list |
Skill | Run a skill |
ExitPlanMode | Present a plan for approval in plan mode |
MCP server tools are listed under the names their server exposes.
Allowlists remove `Task`
A tools allowlist is exhaustive: any tool not on it is removed, including Task. A custom agent with tools: ReadFile, Bash can never delegate to another subagent, even with subagent nesting enabled, and its system prompt tells it so. If a custom agent should be able to spawn subagents, add Task to its tools list or use disallowedTools instead of an allowlist.
Nested subagents inherit this: a subagent can never grant Task (or any other tool) to a subagent it spawns unless it has that tool itself.
Preloaded Skills
skills names skills whose full instructions are written into the agent's system prompt when it starts, under a # Preloaded skills heading. The agent follows them from its first turn instead of having to decide to invoke them:
skills: changelog-style, release-checklistOnly a skill the Skill tool could invoke can be preloaded. An unknown name, or a skill marked disable-model-invocation: true, is skipped and reported in the agent's task block (skills not preloaded (unknown or not model-invocable): ...); the rest of the definition still applies. Preloading is a head start, not a restriction: every other skill remains available through the Skill tool, and a preloaded one is not invoked again.
A project skill you have not trusted is still preloaded, with a notice that its tool grants and dynamic shell blocks stay disabled until you trust it. See Trust.
MCP Servers for an Agent
mcpServers gives an agent MCP servers of its own, on top of the ones the session already provides. An entry takes one of two forms:
- A bare name references a server the session already configures, and the agent shares that connection.
- An inline definition, a single-key mapping under the server's name, defines a server for this agent alone. It takes the same
type,command,args,env,url,headers, andtimeoutfields as a server in your settings files,${VAR}expansion included. Nothing else in the session sees it.
mcpServers:
- github
- sqlite:
command: uvx
args: ["mcp-server-sqlite", "--db-path", "./app.db"]References can also be written on one line, as mcpServers: github, slack.
An agent's servers are additive: a subagent still inherits the session's MCP tools and gains its own on top. Their tools are named mcp__<server>__<tool> like any others and obey the definition's tools and disallowedTools, so an agent can be narrowed to nothing but its own server's tools:
tools: mcp__sqlite__read_query
mcpServers:
- sqlite:
command: uvx
args: ["mcp-server-sqlite", "--db-path", "./app.db"]A subagent connects its own servers before its first turn and reports the outcome in its task block (mcp sqlite: 3 tools). The connection lasts as long as the subagent does and closes when the session is cleared or ends; resuming a session reconnects it on the subagent's next turn. /mcp lists the session's servers, so an agent's own servers show up there only when its definition runs as the main session with --agent, where they join the session's set and connect at startup.
An unusable entry never fails the delegation. It is skipped and named in the task block (mcp servers skipped: ...), or reported as a startup warning under --agent: a reference to a server the session does not configure, an inline name that shadows a session server (that connection is shared instead), or a malformed entry. A server that fails to connect is named the same way, and the agent runs on without its tools.
Example Definition
.agents/agents/reviewer.md:
---
name: reviewer
description: Reviews code changes for correctness, style, and missed edge cases. Use for read-only review passes over a diff or a set of files.
tools: ReadFile, GrepSearch, GlobSearch, Bash
permissionMode: read-only
model: owner:provider-name/model-name
color: purple
maxTurns: 30
---
You are a meticulous code reviewer.
Review the changes you are pointed at for:
- Correctness bugs and unhandled edge cases
- Consistency with the surrounding code's style and conventions
- Missing or misleading tests
Report findings as a prioritized list. Cite file paths and line numbers.
Do not modify any files.Using a Custom Agent for Delegated Work
You do not invoke a custom agent directly. The main agent sees every definition's name and description and picks the right type when it delegates. You can also steer it explicitly: "use the reviewer agent to check this diff."
In the agent switcher, a custom agent's row shows its type name next to its task description, and its task block in the transcript is titled with the type name instead of Task. The definition's color (or an automatically assigned one) marks its dot in both places.
Worktree Isolation
An agent defined with isolation: worktree runs its delegated tasks in a temporary git worktree (a separate checkout of your repository) so its changes never touch your working tree while you and other agents keep working. This requires the workspace to be a git repository with at least one commit; when a worktree cannot be created, the task falls back to sharing the workspace and says so in its task block.
The subagent's report names the worktree path so you (and the main agent) know where its changes live. A worktree with no changes is cleaned up automatically; a worktree holding uncommitted changes or new commits is kept, and resuming the session later puts the revived subagent back inside it.
Running a Definition as the Main Session
Launch a definition as the agent you talk to:
pw code --agent reviewer- The definition's
modelandpermissionModeare used unless you override them on the command line. - Its
tools,disallowedTools, andmaxTurnslimits apply exactly as they would to a subagent, and itsskillsare preloaded the same way.timeoutMinutesapplies only to delegated tasks, so it has no effect here. - Its
mcpServersjoin the session's own servers: they connect at startup and appear in/mcp. - Its body is appended to the system prompt as the session's specialization.
- Its
initialPrompt, if set, is submitted automatically as the first message; in a one-shot run (-p), it is prepended to your prompt instead.
Naming an agent that does not exist fails with the list of available names.
Ad-Hoc Definitions with --agents
--agents takes JSON definitions keyed by name: the same fields as frontmatter, with tools, disallowedTools, and mcpServers as arrays and the specialization body in a prompt field. They merge over the definition files (an ad-hoc definition with the same name wins) and are available both for delegation and for --agent:
pw code --agents '{
"changelog": {
"description": "Drafts changelog entries from recent commits",
"tools": ["ReadFile", "Bash"],
"permissionMode": "read-only",
"prompt": "You draft concise changelog entries. Read git log and summarize user-facing changes."
}
}'An inline MCP server is an object keyed by its name: "mcpServers": ["github", { "sqlite": { "command": "uvx", "args": ["mcp-server-sqlite"] } }].
Permission Clamping for Definition Authors
A custom agent can restrict what a subagent may do, but it can never grant more than the session it runs in:
- Modes only narrow. Modes rank
read-only<plan<accept-edits<bypass-permissions. If a definition requests a mode above the session's, it is clamped down to the session's mode, and the clamp is noted in the subagent's task block. Aread-onlydefinition stays read-only in any session. The requested mode also acts as a live ceiling: if the session's mode changes while the subagent is running, the subagent follows the change, up to the definition's requested mode and never past the session. - Tool scope only narrows. Allowlists intersect with what the spawning agent may use, and denylists accumulate across every generation, so even with nesting enabled a restricted agent can never spawn a subagent with tools it lacks itself. A definition whose
toolslist has nothing in common with the spawning agent's tools is unusable from that agent, and delegation to it fails with an explanation. - Approvals stay per-agent. When a subagent's command needs approval, the prompt names that agent, and your approval applies to that subagent only.
Write definitions assuming the narrowest session they might run in, and treat permissionMode and tools as guardrails for the agent's role: a reviewer that cannot write files, a researcher that cannot run shell commands. See Permissions for how modes and approvals work.
Related Documentation
- pw code Overview: Feature overview and quick start
- Subagents: Delegation, the agent switcher, follow-ups, and limits
- Permissions: Permission modes and approvals
- Models & Allocations: Model IDs and choosing models
- Settings: Subagent-related settings
- Project Instructions: Per-project guidance for the agent
- Non-Interactive Mode: One-shot runs with -p