You own the source
Plain Markdown and YAML in your repo. No account, no service. import brings an existing setup in.
Keep agents, skills, rules, hooks, and MCP servers in one portable source. agnostic-ai writes each tool's native files and checks them for drift.
Recommended installerAll install options
curl -fsSL https://raw.githubusercontent.com/Chemaclass/agnostic-ai/main/scripts/install.sh | bash25 targets. 10 portable spec kinds. Byte-stable output.
Every AI coding tool ships its own config file. Adopt three and your instructions live in three places, drifting apart. agnostic-ai keeps one source in your repository and writes the rest.
Plain Markdown and YAML in your repo. No account, no service. import brings an existing setup in.
Same input, same bytes, so they diff cleanly. sync --check fails CI when they drift.
Adding a target never changes what the others get. One rule routes to every native path and scope syntax.
Easy to stop using. The generated files are yours, and they keep working.
Why not just symlink one file? Each tool reads a different format at a different path. A symlink shares bytes. It cannot translate them. See how the formats diverge
Everything lives in .agnostic-ai/, next to agnostic-ai.yaml, which picks your targets. Pick an entry to see a source file and the native files sync writes from it, each in its own tool's format.
.agnostic-ai/
Also commands/, reviews/, environments/, and ignore/. See every spec kind.
Project instructions. Each tool gets the same body in its own entry file.
# Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.sync
# Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.# Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.
<!-- agnostic-ai:rules:start -->
## Rules
### testing
<!-- source: .agnostic-ai/rules/testing.md -->
One behavior per test. Mock HTTP with `msw`.
### my-style
<!-- source: .agnostic-ai/local/rules/my-style.md -->
Answer in short sentences.
<!-- agnostic-ai:rules:end --># Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.
<!-- agnostic-ai:rules:start -->
## Rules
### testing
<!-- source: .agnostic-ai/rules/testing.md -->
One behavior per test. Mock HTTP with `msw`.
### my-style
<!-- source: .agnostic-ai/local/rules/my-style.md -->
Answer in short sentences.
<!-- agnostic-ai:rules:end -->Subagents with a role, their tools, and a model per tool.
---
name: test-writer
description: Writes a failing test for a bug.
tools: [Read, Grep, Edit]
model:
claude: claude-sonnet-5
codex: gpt-6-sol
effort: medium
---
Reproduce the bug in a failing test. Do not touch production code.sync
---
name: test-writer
description: Writes a failing test for a bug.
tools:
- Read
- Grep
- Edit
model: claude-sonnet-5
effort: medium
---
Reproduce the bug in a failing test. Do not touch production code.name = "test-writer"
description = "Writes a failing test for a bug."
model = "gpt-6-sol"
model_reasoning_effort = "medium"
developer_instructions = """
Reproduce the bug in a failing test. Do not touch production code.
"""---
name: test-writer
description: Writes a failing test for a bug.
tools:
- read_file
- grep_search
- replace
---
Reproduce the bug in a failing test. Do not touch production code.Saved prompts you run by name.
---
name: review
description: Review a change against main.
targets: [kiro]
---
Review $ARGUMENTS against main and report bugs with file and line references.sync
Review $ARGUMENTS against main and report bugs with file and line references.Kiro CLI V3 reads workspace prompts as slash commands.
Docs: CommandsCommands that run on tool events, such as after every edit.
name: format
targets: [claude, codex]
event: PostToolUse
matcher: Edit|Write
command: .claude/hooks/format.shsync
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
},
"permissions": {
"allow": [
"Bash(pnpm test:*)"
],
"ask": [
"Bash(git push:*)"
],
"deny": [
"Read(.env)"
]
},
"model": "claude-opus-5-5",
"effortLevel": "xhigh",
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/format.sh"
}
]
}
]
},
"env": {
"AGNOSTIC_AI_TARGET": "claude"
}
}{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "export AGNOSTIC_AI_TARGET=codex; .codex/hooks/format.sh",
"commandWindows": ".codex/hooks/format.sh"
}
]
}
]
}
}Events are written as is. Claude Code and Codex share PostToolUse; Gemini CLI names its events its own way, such as AfterTool, so it takes a hook spec of its own.
Your own specs on this machine, ignored by Git. Sync layers them over the shared specs.
---
name: my-style
alwaysApply: true
---
Answer in short sentences.sync
---
name: my-style
---
Answer in short sentences.# Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.
<!-- agnostic-ai:rules:start -->
## Rules
### testing
<!-- source: .agnostic-ai/rules/testing.md -->
One behavior per test. Mock HTTP with `msw`.
### my-style
<!-- source: .agnostic-ai/local/rules/my-style.md -->
Answer in short sentences.
<!-- agnostic-ai:rules:end --># Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.
<!-- agnostic-ai:rules:start -->
## Rules
### testing
<!-- source: .agnostic-ai/rules/testing.md -->
One behavior per test. Mock HTTP with `msw`.
### my-style
<!-- source: .agnostic-ai/local/rules/my-style.md -->
Answer in short sentences.
<!-- agnostic-ai:rules:end -->MCP server connections, one YAML file per server. Secrets stay in environment variables.
name: github
type: http
url: https://api.githubcopilot.com/mcp/
headers:
Authorization: Bearer ${GITHUB_TOKEN}sync
{
"mcpServers": {
"github": {
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
}
}model = "gpt-6-sol"
model_reasoning_effort = "high"
[tui]
notification_condition = "always"
[mcp_servers.github]
url = "https://api.githubcopilot.com/mcp/"
bearer_token_env_var = "GITHUB_TOKEN"{
"mcpServers": {
"github": {
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"httpUrl": "https://api.githubcopilot.com/mcp/"
}
}
}Native keys no spec covers, one file per tool: the Claude Code status line, Codex notification settings. Sync merges each into that tool's own file.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}[tui]
notification_condition = "always"sync
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
},
"permissions": {
"allow": [
"Bash(pnpm test:*)"
],
"ask": [
"Bash(git push:*)"
],
"deny": [
"Read(.env)"
]
},
"model": "claude-opus-5-5",
"effortLevel": "xhigh",
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/format.sh"
}
]
}
]
},
"env": {
"AGNOSTIC_AI_TARGET": "claude"
}
}model = "gpt-6-sol"
model_reasoning_effort = "high"
[tui]
notification_condition = "always"
[mcp_servers.github]
url = "https://api.githubcopilot.com/mcp/"
bearer_token_env_var = "GITHUB_TOKEN"Conventions, always on or tied to file patterns and folders.
---
name: testing
globs: ["**/*.test.ts"]
---
One behavior per test. Mock HTTP with `msw`.sync
---
name: testing
paths:
- "**/*.test.ts"
---
One behavior per test. Mock HTTP with `msw`.# Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.
<!-- agnostic-ai:rules:start -->
## Rules
### testing
<!-- source: .agnostic-ai/rules/testing.md -->
One behavior per test. Mock HTTP with `msw`.
### my-style
<!-- source: .agnostic-ai/local/rules/my-style.md -->
Answer in short sentences.
<!-- agnostic-ai:rules:end --># Billing API
- Run `pnpm test` before every commit.
- Never edit `prisma/migrations/` by hand.
<!-- agnostic-ai:rules:start -->
## Rules
### testing
<!-- source: .agnostic-ai/rules/testing.md -->
One behavior per test. Mock HTTP with `msw`.
### my-style
<!-- source: .agnostic-ai/local/rules/my-style.md -->
Answer in short sentences.
<!-- agnostic-ai:rules:end -->Hook script bodies. Sync copies each one into the hooks folder of every tool the hook targets.
#!/bin/sh
file=$(jq -r '.tool_input.file_path // empty')
[ -z "$file" ] || npx prettier --write "$file"sync
#!/bin/sh
file=$(jq -r '.tool_input.file_path // empty')
[ -z "$file" ] || npx prettier --write "$file"#!/bin/sh
file=$(jq -r '.tool_input.file_path // empty')
[ -z "$file" ] || npx prettier --write "$file"The default model and effort per tool, and permissions.
model:
claude: claude-opus-5-5
codex: gpt-6-sol
effort:
claude: xhigh
codex: highpermissions:
allow: [Bash(pnpm test:*)]
ask: [Bash(git push:*)]
deny: [Read(.env)]sync
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
},
"permissions": {
"allow": [
"Bash(pnpm test:*)"
],
"ask": [
"Bash(git push:*)"
],
"deny": [
"Read(.env)"
]
},
"model": "claude-opus-5-5",
"effortLevel": "xhigh",
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/format.sh"
}
]
}
]
},
"env": {
"AGNOSTIC_AI_TARGET": "claude"
}
}model = "gpt-6-sol"
model_reasoning_effort = "high"
[tui]
notification_condition = "always"
[mcp_servers.github]
url = "https://api.githubcopilot.com/mcp/"
bearer_token_env_var = "GITHUB_TOKEN"Codex and Gemini CLI have no portable permission list, and the model map names no Gemini model, so sync prints a note for each instead of guessing.
Docs: SettingsReusable workflows. Files beside SKILL.md, such as check.sh, copy with it.
---
name: release
description: Cut a release and verify it.
---
Run `./check.sh`, bump the version, and push the tag.sync
---
name: release
description: Cut a release and verify it.
---
Run `./check.sh`, bump the version, and push the tag.---
name: release
description: Cut a release and verify it.
---
Run `./check.sh`, bump the version, and push the tag.---
name: release
description: Cut a release and verify it.
---
Run `./check.sh`, bump the version, and push the tag.npm install -g agnostic-aiNode 18 or newer, from your project root.
agnostic-ai init --from allImport existing AI tool files first.
agnostic-ai sync --planReview the planned changes before writing them.
agnostic-ai syncWrite every native file for the targets you chose.
What shipped in agnostic-ai, and what changed upstream in the tools it targets.
agnostic-ai init --from all