Type to search the docs and updates.

Browse documentation
DocsReference

Spec format

Each AI coding tool keeps its configuration in its own place and shape: .claude/agents/, .cursor/rules/, .codex/config.toml, .gemini/settings.json. A spec is one file under .agnostic-ai/ that says what you want, once. agnostic-ai sync writes it into every tool you enable, in that tool's native format.

  • One source to review. A new rule or permission is one diff, not one per tool.
  • No drift. sync --check fails when a native file no longer matches its spec.
  • Free to switch tools. Adding a tool is one entry in targets:, not a rewrite.
  • Visible gaps. When a tool cannot hold a field, sync prints a coverage note instead of dropping it silently.

Folders

Each folder holds one spec kind. Pick the one for what you want the agent to do.

FolderHoldsUse it when
agents/Subagents with their own prompt, tools, and modela task deserves a specialist with a narrow role
skills/Procedures loaded on demand, with bundled filesa workflow is long, occasional, or needs scripts
rules/Standing conventions, for the project, a directory, or a file patternthe agent must follow it every time
commands/Slash commandsa person starts the workflow by name
hooks/Commands the tool runs on lifecycle eventssomething must happen every time, not when the model remembers
mcps/MCP server connectionsthe agent needs an outside tool or data source
settings/Permissions, default model, and effortone policy should decide what agents may run
reviews/Guidance for code-review botsa review bot should check project rules
environments/Worktree setup, dev servers, and cleanupagents work in fresh worktrees
ignore/Paths the agent must not readsecrets or build output must stay out of context
overlays/Native settings and helper files that import captureda tool setting has no portable spec

Two more entries sit next to them:

  • AGNOSTIC_AI.md is the instruction body every entry-point file carries, such as AGENTS.md and CLAUDE.md. See entry-point files.
  • local/ holds personal specs that stay out of Git. See local overrides.

Paths are relative to .agnostic-ai/ by default, so rules/*.md means .agnostic-ai/rules/*.md. Override the directories with sources. The capability matrix shows which targets get each kind. Each target page shows how that tool renders it.

agnostic-ai new <kind> <name> scaffolds an agent, skill, rule, hook, or MCP server. Getting started walks through a first rule.

Discovery is recursive:

  • Every .md under agents/, skills/, rules/, commands/, reviews/, and ignore/ loads.
  • Every .yaml under hooks/, mcps/, settings/, and environments/ loads.

Nested layout: per-directory scope

A spec in a subdirectory of its source dir gets an implicit scope equal to that subpath.

rules/
├── conventional-commits.md      # scope: ""    (root)
└── backend/
    ├── auth.md                  # scope: "backend"
    └── api/limits.md            # scope: "backend/api"

For rules, scope controls native activation or directory discovery. A rule can set scope: services/payments, which wins over its folder under rules/. A folder scopes a rule only when it names a project directory. agnostic-ai new rule payments-context --scope services/payments creates one.

Scoped bodies stay out of root instruction appendices. Supported targets get native path conditions or a nested instruction file. Unsupported targets skip the rule with a warning, or fail under on-unsupported: error. Directory-specific instructions has the target matrix and selector limits.

Target scoping

Four fields limit where a spec of any kind emits.

FieldEffect
targetEmit only to this one target.
targetsEmit only to these targets.
target-excludeEmit everywhere except this target.
targets-excludeEmit everywhere except these targets.

With none set, the spec emits to every target that supports its kind. target beats targets. Exclusion wins over inclusion.

Import auto-scoping

A hook imported from a tool gets target: <tool> (codex, claude, or gemini). Delete the field to let it reach every target.

import claude and import codex do the same for agents and skills. When both .claude/ and .codex/ exist but only one has a spec, it gets target: <tool>. A spec in both stays unscoped. So does every spec in a single-tool project, so round-trips stay byte-identical.

Frontmatter rules

  • YAML between two --- lines at the top of the file.
  • Empty frontmatter (---\n---\n) means no metadata.
  • A file without frontmatter still loads; its name defaults to the filename.
  • Malformed frontmatter counts as no metadata, and the whole file becomes the body.
  • Fields not listed on a kind's page pass through on emit.

Per-target body fences

To vary prose per target, wrap the part that differs in ::target fences. Content outside a fence emits everywhere. Content inside emits only to the listed targets. Marker lines never reach the output.

Shared intro paragraph.

::target claude
Claude-only section.
::end

::targets codex gemini
Codex and Gemini section.
::end
SyntaxMeaning
::target <name>Opens a fence for one target.
::targets <a> <b>Opens a fence for several targets.
::endCloses the most recent fence. A missing ::end runs to the end of the body.
  • import round-trips keep fences intact. import codex builds them when Claude and Codex ship the same agent or skill with different bodies.
  • Fences also work in .agnostic-ai/AGNOSTIC_AI.md. A block reaches an entry-point file when any target that reads the file is listed. The whole family shares AGENTS.md, so ::target codex content reaches every reader. A shared file is never split. See Entry-point files.

Path variables: {{$NAME}}

A spec body can name a directory without hardcoding one target's layout. {{$SKILLS_DIR}} expands to .claude/skills for claude, .agents/skills for codex, and .github/skills for copilot.

VariableResolves to
{{$SKILLS_DIR}}the target's skills directory
{{$AGENTS_DIR}}the target's agents directory
{{$COMMANDS_DIR}}the target's commands directory
{{$RULES_DIR}}the target's rules directory
{{$MCP_FILE}}the target's MCP config file
  • Bodies only. Frontmatter values do not expand.
  • An outputs.<target>.<field> override wins. With outputs.claude.skills-dir: custom/skills, {{$SKILLS_DIR}} follows it.
  • A variable with no target surface stays verbatim and raises a coverage note. aider and jules resolve no variables.
  • A shared file resolves for every tool that reads it. This covers the rules block of an entry point, the review section of AGENTS.md, and a nested AGENTS.md. GEMINI.md has one reader, so it gets .gemini/skills. Codex and Amp both read AGENTS.md and use .agents/skills, so it expands. Codex and OpenCode use different paths, so the token stays verbatim and raises one coverage note naming the file.
  • Only where the target has a dedicated surface. Targets that flatten agents into rules (continue, trae, windsurf) or commands (gemini) declare no {{$AGENTS_DIR}}. Antigravity, Goose, and OpenHands resolve it to .agents/agents.
  • The $ sigil is required. Plain {{placeholder}} stays untouched, so Warp workflow arguments and Handlebars or Jinja survive. Lowercase names such as {{$skills_dir}} do not resolve.

Agent and skill references

A spec body can name another agent or skill by how each target invokes it. {{$AGENT:reviewer}} and {{$SKILL:commit}} render that target's documented phrase.

Before pushing, run {{$AGENT:reviewer}}, then {{$SKILL:commit}}.
Target{{$AGENT:reviewer}}{{$SKILL:commit}}Source
claudethe reviewer subagent/commitsubagents, skills
codexthe reviewer agent (neutral)$commitskills
cursorthe reviewer subagent/commitsubagents, skills
copilotthe reviewer agentthe /commit skillcustom agents, skills
every other targetthe reviewer agentthe commit skill
  • Bodies only, with the $ sigil and an uppercase keyword, like path variables. {{AGENT:reviewer}} stays untouched.
  • A target with no documented form renders a neutral phrase and raises one coverage note. Unlike a path variable, the token does not stay verbatim: a raw {{$AGENT:reviewer}} reads worse to a model than plain words.
  • A rule or review in a shared document, such as the rules block of AGENTS.md or a nested AGENTS.md, renders the neutral phrase, because several tools read that file. Codex reads its rules there, so a Codex rule always gets the neutral phrase. The same goes for a skill, agent, or command in a directory two enabled tools write, such as .agents/skills for Codex and Windsurf.
  • An unknown name fails lint with LINT033, which lists the known agents or skills. So does a name whose spec does not sync to every target the referring spec reaches. Only project specs count: a teammate without your global agent would get a phrase that points at nothing.

Target-specific extensions: x-<target> namespace

Use an x-<target>: block for fields only one adapter reads. Other adapters strip it, so the spec stays portable.

---
name: code-reviewer
description: Reviews diffs.
model: sonnet
x-claude:
  allowed-tools: [Read, Grep, Bash]
x-cursor:
  globs: "src/**"
  alwaysApply: false
---

For each target, sync drops all x-* keys. Then it flattens the matching x-<target> block into the top level, overriding keys with the same name.

TargetResulting frontmatter
claudename, description, model, allowed-tools
cursorname, description, model, globs, alwaysApply
geminidescription (name becomes the .toml filename; model is not emitted for commands)

Arbitrary custom keys

Any other key under x-<target> emits verbatim into that target's output, in sorted order, and never leaks across targets.

  • Validate the keys against the target's schema yourself. Each target page lists the keys its adapter manages.
  • A target with no surface for a spec kind drops custom keys for that kind.
  • Gemini TOML accepts only a string, bool, number, or string array, and skips nested tables.

On a settings spec, the block merges into the target's settings file by the rules in settings.