Type to search the docs and updates.

Browse documentation

Agents

agents/ defines subagents that handle tasks for the main agent. Each has its own instructions, tools, and model. For example, a reviewer can have read-only access, while a test writer uses a cheaper model.

Give the agent one responsibility. Name the files or decisions it owns, the result it should return, and how to check that result.

Use description to say when to delegate to it ("use proactively" makes Claude Code delegate more often). Keep detailed instructions in the body, which loads only when the agent runs. Descriptions load in every session.

When not to write one

  • Keep work in the main conversation when it needs frequent back-and-forth, or when planning, implementation, and testing share one context. An agent starts fresh and returns a summary.
  • Keep a quick change, or a task where speed matters, in the main conversation. An agent may need time to gather context first.

Write one

agnostic-ai new agent code-reviewer

Edit .agnostic-ai/agents/code-reviewer.md (or its configured source path). The YAML frontmatter configures the agent; the body is its prompt.

---
name: code-reviewer
description: Reviews diffs for bugs, style, and architectural issues. Use after a change set is complete.
can: [read, shell(git diff *)]
model: {claude: sonnet, codex: sol}
---

You are a code reviewer. Report concise findings with `file:line` references.

Claude Code gets tools: [Read, Bash] and can run any command, because it ignores the pattern (capabilities). Codex drops can with a note (tool support).

Run agnostic-ai explain agents/code-reviewer.md to inspect the translations, then agnostic-ai sync and agnostic-ai sync --check.

Fields

FieldRequiredDefaultDescription
namenofilename without .mdAgent identifier and output filename.
descriptionnoempty; the name for Claude Code, Qoder, Antigravity, and Gemini CLIWhen to hand a task to the agent. Claude Code skips an agent without one, so sync writes the name there with a note. lint warns when it is missing (LINT048).
cannounsetWhat the agent may do, in tool-neutral names. See capabilities.
toolsnounsetThe same list in Claude Code tool names. Set can or tools, not both. See support by target.
modelnounsetA string, a map per target, or a tier name. See per-target model and effort.
effortnounsetA string or integer, or a map per target. Same section.
colornounsetBadge color. See color support by target.
readonlynounsettrue limits the agent to reading; false does nothing. See readonly by target.
memorynounsetPersistent memory directory scope: user, project, or local. Claude Code uses it, Qoder gets the key (unconfirmed), and Junie gets it as written. Every other tool drops it.
mcpServersnounsetMCP servers this agent may reach. See support by target.
maxTurnsnounsetThe most turns before the agent stops, a whole number of 1 or more. See support by target.
backgroundnounsettrue runs the agent without making the main session wait. Same section.
permissionModenounsetHow much this agent may do without approval. See support by target.
hooksnounsetLifecycle hooks for this agent only. Same section.

Any other frontmatter field is written as given.

readonly by target

TargetEffect
CursorRestricted
Claude CodedisallowedTools: Write, Edit, NotebookEdit (Bash stays allowed)
Codexsandbox_mode = "read-only", which current Codex ignores (the agent keeps the session sandbox), and a sync note
Qwen CodedisallowedTools: [write_file, edit, notebook_edit] (shell stays allowed)
Factorytools: read-only with mcpServers: [] unless servers are listed. Wins over a portable tools list
Other targetsSync note

An explicit x-claude.disallowedTools, x-qwen.disallowedTools, x-codex.sandbox_mode, or x-factory.tools wins.

Capabilities

can lists what an agent may do, in names that belong to no one tool:

can: [read(src/**), edit(src/**), shell(go test *), mcp:github]
can valueGrantsClaude Code name
read, read(<path>)Read files, or matching filesRead; the path is dropped
writeCreate or overwrite filesWrite
edit, edit(<path>)Edit files, or matching filesEdit; the path is dropped
deleteDelete filesNo native tool
shellRun any commandBash
shell(<pattern>)Run the commands that match the patternBash; the pattern is dropped
webFetch pages and search the webWebFetch, WebSearch
mcp:<server>Use every tool of one MCP servermcp__<server>
mcp:<server>/<tool>Use one MCP toolmcp__<server>__<tool>
  • Each capability becomes the tool's own name. delete raises a sync note on tools with no delete tool. The names match the hook match: tool kinds.
  • Claude Code names work as aliases, and a list can mix both: can: [read, Grep]. Agent(<type>) and x-claude.tools are written as given.
  • Claude Code reads an agent's tools by name only, so sync drops a path or pattern there and prints a note such as claude: .agnostic-ai/agents/code-reviewer.md: shell(git diff *) becomes Bash; Claude Code does not limit an agent's tools to a pattern. on-unsupported: error fails on it. To limit commands there, add a PreToolUse hook to the agent's hooks, or deny commands in a settings spec's permissions, which apply to every agent. A skill's allowed-tools keeps the pattern.
  • Claude Code refuses to launch an agent with an empty tool list, such as can: [] or can: [delete]. Sync leaves it out of .claude/agents/ with a note.
  • For Claude Code, a server name that contains __ or ends in _, such as reports__v2, becomes mcp__<server>__*, the only whole-server form Claude Code matches for it.
  • validate, lint (LINT036), and sync stop on an unknown capability, on can beside tools, and on can under x-<target>.
  • A capability is only as strong as the tool's own permission system. agnostic-ai adds no sandbox.

agnostic-ai migrate --only capabilities rewrites tools as can; names with no capability of their own stay as aliases. import writes capabilities when each tool name matches one. lint --suggest-capabilities suggests neutral names for aliases (LINT037), off by default.

Per-target model and effort

model: and effort: each take a single value or a map keyed by target name, with an optional default. The first match wins:

  1. x-<target>.<key> (null deletes the key)
  2. <key>.<target>
  3. <key>.default
  4. Nothing is written, and the tool uses its own default.

A target value that is not a single value falls back to default.

---
name: architect
description: Designs the change before anyone codes it.
model:
  claude: opus
  cursor: "claude-opus-5[effort=high]"
  default: gpt-6.1-sol
effort:
  claude: xhigh
  qoder: 8000
  default: high
x-codex:
  model_reasoning_effort: xhigh
---

Claude gets opus and xhigh. Qoder gets gpt-6.1-sol and 8000. Cursor gets its model id and no effort. Codex gets gpt-6.1-sol with effort xhigh from x-codex.

effort values by target. Without effort, the agent uses the session's level.

TargetValuesOutput
Claude Codelow, medium, high, xhigh, max, model dependenteffort, not checked
QoderSame five names, or a positive integereffort
JunieAlias of reasoningLeveleffort
Factorylow, medium, highreasoningEffort; other values raise a sync note
CodexAny stringmodel_reasoning_effort; an integer raises a note
CursornoneSync note. Put it in the model id, as above
CopilotnoneSync note. x-copilot.effort still passes through
Augmentoff, minimal, low, medium, high, xhighthinking (Auggie 1.x); other values raise a sync note, and x-augment.thinking wins
Every other targetnoneSync note

Claude model names on other targets. A shared model (a single value or default) set to a Claude model name is left out, with a sync note, on a tool that cannot load it, so that tool uses its own default. The note suggests model: {claude: <name>}, or names the tier to fix when the name sits in a tier's default. on-unsupported: error fails the sync instead. A value under model.<target> or x-<target>.model is written as given. import claude writes these names as model: {claude: <name>}, and import codex adds a Codex agent model as model.codex.

TargetClaude names that raise the note
Codex, Gemini, OpenCode, Kilo CodeThe aliases, inherit, and claude-* ids
Cursor, Factory, KiroThe aliases

Claude Code's aliases are sonnet, opus, haiku, fable, best, opusplan, sonnet[1m], and opus[1m]. The value default resets Claude's model instead of naming one, so it raises no note.

Model tiers

A tier names a model role once in agnostic-ai.yaml, such as strong: {claude: opus, codex: sol, effort: high}. Write model: strong in an agent, and each target the tier names gets its model; the others keep their default. A tier's description tells the main agent when to pick it. For model, the first match wins:

  1. x-<target>.model
  2. model.<target> in the spec
  3. The tier's entry for the target
  4. The tier's default
  5. The tool default

To override one target, write the tier as the map's default: model: {codex: gpt-6-luna, default: strong}.

  • The tier's effort applies only when the spec sets no effort, and is skipped for a target whose model the spec sets itself.
  • A value under model.<target> or x-<target>.model is never a tier name. model.<target> can be a vendor alias such as codex: sol.
  • explain agents/<name>.md lists the model and effort each configured tool gets. models lists the tier lint checks (LINT025, LINT026).
  • import claude suggests a tier when two or more agents set the same Claude model. import claude and import codex keep model: strong when the imported model and effort match what the tier gives that tool.

Agents as skills

Sync drops agent specs for Amp, Crush, Warp, and Zed by default. Set outputs.<target>.agents: skill to write each agent as a skill, which loads only when the model needs it.

outputs:
  amp:
    agents: skill
  • The skill is <skills-dir>/<agent>/SKILL.md, with the agent's name and description. Its body starts with a note: the model plays the role in the main session and says so. Nothing enforces that separation.
  • Fields a skill cannot carry, such as tools and model, are dropped with a note.
  • An agent reference in skills, agents, and commands becomes that tool's skill phrase. A rule in a shared entry-point file such as AGENTS.md keeps the neutral phrase.
  • An agent and a skill with the same name would share a folder, so validate fails on them.
  • These four tools write .agents/skills/, which Codex, Copilot, Gemini, Cline, Cursor, OpenCode, Junie, and Qwen read too, and several other tools with subagents write there. When one of those is enabled, sync keeps the agents out of that directory and names the tool. Set outputs.<target>.skills-dir to a separate directory to use this option beside them.
  • The key cannot be combined with rules-file or, on Warp, workflows-dir. Other targets reject the key.

can and tools support by target

A tool that cannot enforce the list prints a sync note. Check these notes before relying on can: [read] to restrict an agent.

TargetBehavior
Claude Code, Copilot, JunieWritten as a YAML list
Qoder, TraeWritten as a comma-separated string (tools: Read, Bash)
Windsurf, Kiro, Factory, Gemini, Kilo Code, OpenCodeChanged to the tool's own names or a permission map
Antigravity, OpenHands, Goose, Codex, Cursor, AugmentDropped with a note

On Trae, an explicitly empty list disables all tools, and an unset field loads every tool. Use x-trae: {tools: ""} to restrict only Trae.

Translation can grant extra access: on Kiro, edit also allows delete_file. Sync prints a note naming the extra access, and on-unsupported: error fails. explain agents/<name>.md lists each tool's names and any widening. Most tools accept their own names through x-<target>.tools, which skips the change.

mcpServers support by target

mcpServers names the servers, usually from mcps/, that one agent may reach. Without it, the agent gets the session's full set. An agent-only server reaches only the agents that name it.

TargetBehavior
Claude Code, QoderServer names in the agent file. An agent-only server is written in full instead
KiroEvery named server written in full, under mcpServers
OpenHandsEvery named server written in full, under mcp_servers. Not when Goose shares .agents/agents/: set outputs.openhands.agents-dir
Junie, FactoryServer names in the agent file. An agent-only server's name is left out, except on Junie when it is the only name
AntigravityDropped; its docs give no shape for an inline server. Set x-antigravity.mcpServers
Gemini, CopilotDropped. Set x-gemini.mcp_servers or x-copilot.mcp-servers
Every other targetDropped, and agent-only servers left out, with a sync note

An x-<target> key for the list, such as x-kiro.mcpServers, replaces what sync would write. On Kiro and OpenHands, a name with no MCP spec is dropped with a note.

An empty list keeps every server on Junie and excludes every server on Factory, so list the servers you want.

maxTurns and background by target

A native key under x-<target>, such as x-gemini.max_turns, x-kilo.steps, or x-cursor.is_background, wins. An x-<target> override with the wrong type raises a sync note, and the shared value is used. validate, lint (LINT050), and sync stop on a maxTurns that is not a whole number of 1 or more, or a background that is not true or false.

TargetmaxTurnsbackground
Claude CodemaxTurnsbackground
QodermaxTurnsbackground, when background subagent sessions are on
Qwen CodemaxTurnsbackground
JuniemaxTurnsSync note
Gemini CLImax_turnsSync note
Kilo Code, OpenCodestepsSync note
CursorSync noteis_background
Every other targetSync noteSync note

import reads each native key back into maxTurns or background. When the spec already sets the value under x-<target>, import writes it back there and keeps the shared value.

permissionMode and agent hooks support by target

Without either field, the agent uses the session's setting. On Claude Code, a session in bypassPermissions, acceptEdits, or auto mode runs the agent in that mode and ignores its value, and an agent's own bypassPermissions never applies.

TargetpermissionModeAgent hooks
Claude CodeSeven values; manual is an alias of defaultWritten to the agent file
QoderSix values. Any other value is reportedSeven events. A wider setting is reported
OpenHandsDifferent names (always_confirm, never_confirm, confirm_risky). Set x-openhandsSix snake_case events, command handlers only. Set x-openhands
Qwen CodeNot written, with a note: Qwen maps Claude's names to its own approval modes with different strictness. Set x-qwen.approvalModeWritten to the agent file; Stop becomes SubagentStop. Project agent hooks need a trusted folder
CodeBuddyFive values; any other value stops syncWritten to the agent file. Running them needs the user's allowUntrustedFrontmatterHooks choice
Grok BuildWritten to the agent fileWritten to the agent file
JunieWritten to the agent fileCopied with the frontmatter, with a note: Junie documents no agent hook field
Every other targetSync noteSync note

On Qoder, bypassPermissions becomes acceptEdits when security policy disables it, and an agent runs fewer hook events than the project does.

color support by target

color is written as given and not checked. An unrecognized value affects only the badge.

TargetValues
AugmentFree text, an ANSI color name
Kilo CodeHex or a theme token, so color: blue may not show as expected
Claude Code, QoderOne of eight names: red, blue, green, yellow, purple, orange, pink, cyan
OpenHandsDropped with a note, since it shares .agents/agents/ with Goose, which has no color. Set x-openhands.color to a Rich color name