agentstow

Canonical configs (skills, MCP, memory, etc.), fanned out to all your AI coding agents.

One Store at ~/.agents/ holds the single real copy of every config you share — skills, instructions, MCP servers, slash commands, subagents, hooks. agentstow sync fans it out to all ten agents you have installed: symlinks where the bytes can be identical, a rendered key-merge where they can't. There is no state file, and there never will be.

# the Store — one real copy of everything you share
~/.agents/
├── skills/          research/  tdd/  code-review/
├── commands/        ship.md  triage.md
├── subagents/       reviewer.md
├── AGENTS.md        your instructions, once
├── mcp.json         standard mcpServers shape
└── hooks/           SessionStart.toml  PreToolUse.toml

Fan-out

Edit one file. Every agent sees it.

$ ls -l ~/.claude/skills/
research    -> ../../.agents/skills/research
tdd         -> ../../.agents/skills/tdd
code-review -> ../../.agents/skills/code-review

$ ls -l ~/.codex/skills/
research    -> ../../.agents/skills/research
tdd         -> ../../.agents/skills/tdd
code-review -> ../../.agents/skills/code-review

A skill added to the Store appears in Claude Code, Codex, Cursor and pi as a relative symlink — computed, not hand-typed, so the tree survives being moved or restored somewhere else.

opencode and oh-my-pi already read ~/.agents/ themselves, so agentstow deliberately writes nothing for them. A link of ours that dangles gets pruned; a link pointing anywhere else is Foreign and is left alone.

Two mechanisms

Symlinked where it can be, rendered where it can't.

Skills, instructions, commands and subagents can be byte-identical everywhere, so they are one file seen from ten places — drift is impossible by construction. MCP servers and hooks live inside files the agent also owns, in formats no two agents share, so those are rendered and key-merged: your other keys are preserved, and only the entries named in the Store are rewritten.

# ~/.agents/mcp.json — you write this once
{
  "mcpServers": {
    "serena": {
      "type": "stdio",
      "command": "uvx",
      "args": ["--from", "serena-agent", "serena"]
    }
  }
}
# ~/.codex/config.toml — merged, not replaced
model = "gpt-5.3-codex"        # untouched

[mcp_servers.serena]
command = "uvx"
args = ["--from", "serena-agent", "serena"]

[projects."/Users/you/work"]   # untouched
trust_level = "trusted"

No state

The filesystem is the state.

Other tools in this space copy, render, and remember what they own in a record beside your configs. When that record disagrees with the disk, your hand-edits lose. agentstow keeps no record at all, because ownership is legible from the disk itself.

Link identity
A symlink that resolves into the Store is ours. It gets canonicalised, and pruned when it dangles.
Name identity
An MCP server, or a hook whose command appears in the Store, is ours. Every other name in the same file is Foreign and survives untouched.
Marker identity
A wholly generated file carries one comment line saying so. Marked is ours; unmarked was written by somebody else.

Restraint

Variants are a feature, not a conflict.

$ agentstow status
claude (.claude/skills)  12 linked
  variant           plannotator — left alone
  variant-identical old-notes — identical to the
                    Store, could be re-linked
  foreign           vendor-thing — not ours

mcp (mcp.json)
  managed           serena → claude
  foreign           xapi → claude — not in the
                    Store — left alone

2 items need attention — run `agentstow sync`.

A real directory shadowing a Store entry is a Variant: deliberate, preserved forever, and flagged only when its contents are identical to the Store copy, so you can dedupe on purpose rather than by accident.

Anything agentstow did not write is Foreign — reported, never modified. Exit codes are the contract: 0 clean, 1 error, 2 something to do.

Coverage

Ten agents, six families.

AgentSkills InstructionsMCP CommandsSubagents Hooks
Claude Codefan-outimport-linekey-mergefan-outfan-outkey-merge
Codexfan-outsymlinkkey-merge (TOML)fan-outnonekey-merge
opencodenativesymlinkkey-mergefan-outfan-outnone
pifan-outsymlinknonenonenonenone
oh-my-pinativesymlinknativenativenonenone
Gemini CLInonesymlinkkey-mergerendernonekey-merge
Cursorfan-outnonekey-mergefan-outnonenone
Windsurfnonesymlinkkey-mergefan-outnonenone
Roononerules-dir linknonefan-outnonenone
Clinenonenonekey-mergenonenonenone
What each agent supports, and by which mechanism. none means that agent has no such config surface — not that agentstow skipped it. native means the agent reads the Store itself, so writing anything would only duplicate it.

Safety

Safe to run before you trust it.

$ agentstow sync --dry-run
dry run — no changes will be made

claude (.claude/skills)
  missing   research
  missing   tdd
mcp (mcp.json)
  missing   serena → codex — not in this agent's config yet

3 changes would be made.
A second sync is a no-op
Run it twice and the second run reports Everything is up to date with the files byte-identical.
Every write is atomic
A private temp file, fsync, then an atomic rename — resolved through a symlinked destination, so dotfiles wiring survives.
Secrets stay out of the Store and out of your terminal
${env:VAR} is resolved at sync and redacted from every line of output, so the Store stays committable.
It reports to CI
status --json and mcp list --json, with exit code 2 reserved for “there is something to do”.

Install

macOS and Linux.

# prebuilt binary — no toolchain, no postinstall script
npm install -g agentstow

# or from source, needs Rust 1.97+
cargo install agentstow

# then, on a machine you have already been using
agentstow init

init creates the Store and then tells you what of your existing config it could take over — per family, with the command that would do it.

Limits

What it does not do.

No cross-machine sync. The Store is an ordinary directory — version it with git or chezmoi. Building sync would mean building conflict resolution, and git already won that.

No memory sync. Agent memory is not a defined artifact, and agentstow will not pretend otherwise.

No Windows, no GUI, no daemon, no file watcher. It does not install skills — it fans out whatever is in the Store, whoever put it there. There is no mcp add, because adding a server is editing a documented standard file.