Type to search the docs and updates.

Browse documentation
DocsReference

Configuration

agnostic-ai.yaml lives at the project root and is read from the current working directory. Every section is optional. The legacy filename agnostic.config.yaml still loads, with a deprecation warning.

Minimal project config

version: 1
targets: [claude, cursor]
gitignore:
  enabled: true

agnostic-ai init creates one with your selected tools.

For directory-specific instructions, add scope to a rule: agnostic-ai new rule payments-context --scope services/payments. See scoped context. Set on-unsupported: error when every target must preserve scope.

Find a setting

ChangeSection
Select toolsTargets
Change source or output pathsSources and Outputs
Keep generated files out of GitGitignore
Customize sync behaviorSync
Run project behavior checks after model or CLI changesVerify
Keep a hand-written file at a generated pathsync.unmanaged
Override settings on one machineLocal overrides
Understand which value winsPrecedence and Layered specs
Share personal instructions across projectsGlobal configuration
Inspect all fieldsTop-level fields or JSON Schema

Local overrides

agnostic-ai.local.yaml holds per-machine tweaks. It deep-merges over the base: scalars and lists replace, maps merge recursively. agnostic-ai init adds it to .gitignore.

# agnostic-ai.local.yaml (never committed)
on-unsupported: error
outputs:
  claude:
    dir: .claude-local   # overrides base; rules-file from base survives

Editor validation

init adds this comment so YAML Language Server editors validate against docs/schemas/config.schema.json:

# yaml-language-server: $schema=https://raw.githubusercontent.com/Chemaclass/agnostic-ai/main/docs/schemas/config.schema.json

Top-level fields

FieldTypeDefaultDescription
versionint1Schema version, reserved for migrations.
sourcesmap.agnostic-ai/<kind>/Source directories.
targetslist20 adaptersAdapters to emit.
outputsmapper targetOutput path overrides.
on-unsupportedstringwarnUnsupported kind handling.
gitignoremapenabled: falseManaged .gitignore block.
syncmapsee sectionSync behavior.
verifymapdisabledExternal behavior gate.
importmapper sourceImport behavior.

sources

Paths are relative to the config file. Missing directories are skipped silently.

FieldDefaultDescription
agentsagents*.md agent specs.
skillsskills*.md skill specs (or nested <name>/SKILL.md).
rulesrules*.md rule specs.
hookshooks*.yaml hook specs.
mcpsmcps*.yaml MCP server specs.

outputs

outputs.<target>.* overrides where one target writes; unknown fields are ignored. Target pages list the keys and defaults, starting at the targets index. Claude Code and Codex also accept settings blocks.

outputs:
  claude:
    rules-dir: .claude/rules
  cursor:
    mcp-file: .cursor/mcp.json

targets

Default: every adapter except amp, warp, jules, goose, and augment (20 in total). Enabling those alongside codex is safe; the shared AGENTS.md body is written once. Unknown targets log a warning and are skipped. -t/--target overrides the list for one run.

agnostic-ai init and the first agnostic-ai sync can write this list through a picker. See init and the first-sync target picker.

sync

Per-target overrides live in outputs.<target>. Per-run flags such as --diff, --format, and --jobs have no config key; see sync, parallel emission, and reading a failing --check.

KeyDefaultEffect
collision-policypromptWhat happens when two targets write the same path.
target-overviewfalseAppend a generated-locations section to each entry-point file.
resolve-importspassthroughHow @path lines reach targets that cannot resolve them.
dropped-summaryfalsePrint a per-target summary of dropped and downgraded kinds.
shared-skillsfalseSymlink byte-identical skill folders to one copy.
unmanagedemptyPaths sync never touches.

sync.collision-policy

Applies when two targets write different content to one path, such as outputs.codex.rules-file: AGENTS.md and outputs.amp.rules-file: AGENTS.md. Per-target override: outputs.<target>.collision-policy.

ValueBehavior
promptDefault. Fail with an output collision error and a hint, which in CI suggests a non-interactive policy.
prefer-specSkip the collision check. Last adapter wins. Use in CI when the overlap is intentional.
failHard error with no resolution hint.
sync:
  collision-policy: prefer-spec

sync.target-overview

When true, each entry-point file (CLAUDE.md, AGENTS.md, GEMINI.md, ...) gets an appendix listing where that tool's generated artifacts live (rules dir, agents dir, MCP file, ...), honoring outputs.<target>.* overrides.

sync:
  target-overview: true
  • Only the appendix differs per file. A shared entry point such as AGENTS.md lists each reader in its own section.
  • The appendix sits between <!-- agnostic-ai:target-overview:start --> and <!-- agnostic-ai:target-overview:end -->. import strips it, so the AGNOSTIC_AI.md round-trip stays lossless. .agnostic-ai/AGNOSTIC_AI.md never carries it. Every sync regenerates it, so do not hand-edit it.
  • Aider, whose artifacts all flow through the entry point, gets no appendix. External adapters (agnostic-ai-adapter-<name> binaries) get no section.

sync.resolve-imports

Controls how a line holding only an @path import in AGNOSTIC_AI.md reaches targets that cannot resolve it. CLAUDE.md always keeps it, since Claude resolves imports. An @mention inside a sentence is untouched. Paths resolve from the project root.

ValueNon-resolving targets get
passthroughDefault. The @-line verbatim, as a dead reference.
stripNothing: the line is dropped.
inlineThe referenced file's content between <!-- agnostic-ai:import:start <path> --> and <!-- agnostic-ai:import:end -->. import restores the @-line. A missing or unreadable file fails the sync.
sync:
  resolve-imports: inline

sync.dropped-summary

When true, sync ends by listing, per target, kinds with no surface (dropped) or emitted only behind an opt-in key or source-dir only (downgraded). It regroups capability warnings and coverage notes by target.

sync:
  dropped-summary: true
  dropped summary (per target):
    cursor: 2 hooks dropped (unsupported)
    gemini: 3 skills via outputs.gemini.emit-skills-as-commands

sync.shared-skills

When true, targets sharing the Agent Skills layout (<dir>/<name>/SKILL.md plus assets: Claude, Cursor, Codex, Amp) keep one real tree per skill; the others get relative symlinks.

sync:
  shared-skills: true
  • The canonical copy is .agents/skills/<name> when emitted (Codex and Amp scan it natively), otherwise the first emitted target's tree.
  • Only identical rendered folders link. Overrides such as x-cursor keys or codex-only agents/openai.yaml keep real copies for diverging targets.
  • Links are per skill folder, so hand-authored skills are untouched. Turning the option off, or divergence, restores real trees on the next sync. Removing a skill sweeps its tree and links.
  • Without symlink support (Windows without the privilege), sync warns once and keeps real copies.

sync.unmanaged

Paths you own. sync never writes, merges, copies, or removes them.

sync:
  unmanaged:
    - .cursor/rules/legacy.mdc        # exact path
    - .claude/agents/hand-*.md        # glob; `*` stays inside one path segment
    - .claude/skills/legacy/          # trailing slash: everything under the directory
  • Entries are project-relative with forward slashes; \ is a glob escape. A leading ./ or / is ignored. Globs use Go path.Match; ** is not supported. A malformed glob or an entry naming no path (., ./, /, empty) fails config load.
  • sync prints ~ skip (unmanaged) <path> (--json: under skipped, action "unmanaged"). sync --check, status, and doctor never count it as drift; doctor lists it under User-owned, not Unmanaged config. revert and doctor --fix never touch it.
  • It stays out of the sync ledger, so removing the entry deletes nothing; the next sync rewrites the file with the provenance header.
  • The .gitignore block lists generated files one per line in a directory that could hold a match, instead of collapsing it.
  • With sync.shared-skills, such a skill folder is never linked; an existing link becomes a real copy, keeping edits.
  • The list is project-wide. agnostic-ai.local.yaml replaces it whole.
  • Not covered yet: hook script bodies copied from .agnostic-ai/scripts/ into .<tool>/hooks/.

verify

verify.command is the argv list agnostic-ai verify runs. It starts the executable directly, without a shell, so pipes and redirects belong in your script.

verify:
  command:
    - ./scripts/verify-harness
    - --strict

See the verify command for the drift check, the JSON input, and exit codes.

import

Per-source knobs for the import command. Empty blocks use per-source defaults.

import.codex.shred

Controls how agnostic-ai import codex treats AGENTS.md.

ValueBehavior
trueDefault. One rule spec per ## heading.
falseOne rule spec per AGENTS.md, full body verbatim. Use when it duplicates standalone rules and you want it as a reference doc.
import:
  codex:
    shred: false

on-unsupported

Applies when an adapter receives a spec kind it does not support (e.g. hooks for Cursor or mcps for Cline).

ValueBehavior
warnDefault. Log to stderr and continue.
errorFail the sync.
silentSkip without logging.

Coverage notes

sync prints a note: line when specs of a kind exist but a target emits them only behind an inactive opt-in key, or not at all:

  note: 2 skills reach gemini, opencode only via outputs.<target>.emit-skills-as-commands
  note: 1 agent reaches warp only via outputs.warp.workflows-dir

Setting the named key clears the note. Notes matching the previous sync are suppressed; delete .agnostic-ai/.sync-state to show them again.

TargetKindSet this to emit
geminiskillsoutputs.gemini.emit-skills-as-commands
opencodeskillsoutputs.opencode.emit-skills-as-commands
warpagentsoutputs.warp.workflows-dir
aideragents, skillsoutputs.aider.rules-file
zedhooksoutputs.zed.tasks-file
kiloagents with toolsNone. Use x-kilo: {permission: {...}}.
geminiagents with tools beyond Read/Write/Edit/Bash/Grep/Glob/WebFetch/WebSearchNone. Use x-gemini: {tools: [...]} or drop tools.
crushhooks not on PreToolUseNone. Crush runs PreToolUse only.

gitignore

FieldDefaultDescription
enabledfalse when absent; agnostic-ai init writes trueEvery sync rewrites a managed .gitignore block listing every path the configured adapters emit.
path.gitignoreAnother file, for monorepos or local-only ignore files.
allowemptyGitignore globs written verbatim as ! lines at the end of the block, so a tracked file (e.g. a testdata/AGENTS.md fixture) is not ignored.

An existing config with no gitignore key stays false. sync --gitignore and init --gitignore override it per run; see the CLI reference.

The block sits between # >>> agnostic-ai (managed) >>> and # <<< agnostic-ai (managed) <<<. Lines outside it are kept, and an unchanged sync keeps the file mtime. Its header says to edit specs, and that a fresh clone or git worktree lacks these paths until sync runs (see post-checkout hook).

  • Entries are root-anchored (/AGENTS.md, not AGENTS.md), so nested same-named files are not ignored.
  • Files collapse to their generated subdirectory (/.claude/rules/), never higher, so siblings such as .claude/settings.json or .claude/hooks/ stay visible.
  • The block always holds agnostic-ai.local.yaml, /.agnostic-ai/.sync-state, and /.agnostic-ai/packs/, seeded by init even with gitignore.enabled: false. init, sync, or packs add moves old loose copies into the block.
  • A target can add entries of its own, such as Claude Code's local settings and agent memory.

Watched inputs

sync --watch re-emits when the config files, any sources directory, .agnostic-ai.local/, or .agnostic-ai/overlays/ change. Overlays hold keys the spec layer does not own, such as Claude statusLine or Codex [profiles.*]. See sync --watch.

Path semantics

  • sources and outputs paths are relative to the directory holding agnostic-ai.yaml.
  • Output directories are created on demand. Existing files are overwritten.

Entry-point files

sync writes .agnostic-ai/AGNOSTIC_AI.md plus one root entry-point file per enabled target, all sharing the canonical pointer body. See the per-target table.

Setting outputs.<target>.rules-file: <path> restores the legacy layout: the adapter writes one merged document at <path> and sync skips the pointer body for that target. Two adapters writing different content to one path fail unless you set sync.collision-policy: prefer-spec.

Per-target paragraphs

.agnostic-ai/AGNOSTIC_AI.md accepts the ::target / ::targets / ::end fences from spec bodies.

Shared conventions for every tool.

::target gemini
Gemini reads `GEMINI.md` only. Load rules from `.gemini/rules/`.
::end

::targets codex amp
Run `make preflight` before you stop.
::end
  • Unfenced content goes to every entry-point file.
  • A fenced block reaches a file when any of its readers is listed, so ::target codex reaches every AGENTS.md reader. A shared file is never split.
  • Markers never reach output and must start at column 0; indent a sample that shows one.
  • agnostic-ai validate flags a fence naming an unknown target, or a built-in target that reads no entry-point file.
  • agnostic-ai import <tool> keeps a fenced source when the imported file equals what sync renders (under the default sync.resolve-imports: passthrough); otherwise it overwrites the source and warns.

Precedence

Last wins:

  1. Built-in defaults
  2. agnostic-ai.yaml
  3. agnostic-ai.local.yaml
  4. CLI flags (e.g. agnostic-ai sync -t claude)

Layered specs

Specs load from three layers, lowest first. Higher layers override by spec name per kind; new names append. agnostic-ai list shows each spec's layer.

LayerRootLoaded when
packs.agnostic-ai/packs/ from agnostic.packs.lockpacks are installed
projectagnostic-ai.yaml sources pathsalways
project-user<project>/.agnostic-ai.localdirectory exists

Only project honors custom sources paths; project-user uses fixed kind directories. Add .agnostic-ai.local/ to .gitignore. $AGNOSTIC_AI_HOME is not a project layer.

Global configuration

agnostic-ai sync --global installs user-level instructions, rules, hooks, and skills for 22 of the 25 targets (global output lists paths). It works from any directory and loads no agnostic-ai.yaml, packs, local overrides, or project specs.

Source root: $AGNOSTIC_AI_HOME, or ~/.agnostic-ai/ when AGNOSTIC_AI_HOME is unset.

~/.agnostic-ai/
├── AGNOSTIC_AI.md
├── rules/*.md
├── hooks/*.yaml
└── skills/<name>/SKILL.md
  • It targets every supported tool by default. Which sync flags it accepts is in the CLI reference.
  • Nested rules and rules with scope, path, glob, or target conditions are rejected. Agents, commands, MCP servers, settings, inheritance, and merging with project specs are unsupported.
  • Output is real files, never symlinks. Ownership is recorded per target in $AGNOSTIC_AI_HOME/state/global.json. Sync keeps unrelated text, JSON keys, hooks, and skills, and removes only recorded artifacts for the targets in the run, so --only never sweeps another target.
  • An unmanaged skill or rule collision, damaged marker, invalid native JSON, or corrupt state stops the run before writes.
  • Empty surfaces create nothing: no instructions file (a recorded one is removed) and no hooks file.
  • Native tool precedence applies when global and project configuration both exist.

Ordinary agnostic-ai sync does not load ~/.agnostic-ai/. Move project-only defaults, and any agents, MCP servers, commands, settings, reviews, environments, or ignore specs, into a project's .agnostic-ai/ or a pack. A repository's .agnostic-ai/ stays project-specific despite the shared basename.