agentstow

Documentation

agentstow keeps one copy of every config you share and puts it where each agent expects to find it. This page is the reference: what lives in the Store, how each family reaches each agent, and what the tool will and will not touch.

Install

# prebuilt binary for macOS and Linux — no toolchain, no postinstall script,
# so it installs offline and inside a sandboxed CI
npm install -g agentstow

# or build from source — needs Rust 1.97 or newer
cargo install agentstow

The npm package is a small launcher; the real binary ships in one platform package per target, declared as optional dependencies so npm installs exactly the one matching your machine. On a platform with no prebuilt binary you get a message naming what it looked for and pointing at cargo install, rather than a missing-file crash.

Windows is not supported. v1 is macOS and Linux. The symlink fan-out that makes drift impossible needs symlink creation to be unprivileged.

First five minutes

$ agentstow init
Created the Store at ~/.agents
  skills/
  commands/
  subagents/

Found 6 agents: claude, codex, opencode, pi, oh-my-pi, gemini

3 skills not in the Store:
  claude/skills/my-workflow
  claude/skills/review-loop
  codex/skills/notes
  take one with `agentstow adopt <path>`

4 MCP servers not in the Store: serena, xapi, node_repl, computer-use
  take them all with `agentstow mcp adopt --all`

init answers the question a used machine actually poses: not what agentstow does, but what of your existing config it could take over. Adopt what you want shared, then sync.

$ agentstow adopt ~/.claude/skills/my-workflow
adopted my-workflow from claude into the Store (skills)

$ agentstow mcp adopt --all
adopted serena from claude
  scoped to claude — remove the allowlist in agentstow.toml to share it

$ agentstow sync

adopt moves the real file into the Store and leaves a link behind. If the Store already holds that name with identical contents it collapses the duplicate into a link; if the contents differ it refuses and explains, because choosing which side to discard is not agentstow's call.

The Store

~/.agents/
├── skills/<name>/         a directory per skill
├── commands/<name>.md     Claude-dialect markdown
├── subagents/<name>.md    Claude-dialect markdown
├── AGENTS.md              your shared instructions
├── mcp.json               the standard mcpServers shape
└── hooks/<Event>.toml     [[hook]] entries: command, matcher, timeout

The Store is fixed at ~/.agents/. It holds ecosystem-standard content only — nothing agentstow-specific lives there, which is what lets other tools read it directly, as opencode and oh-my-pi already do. Tool configuration lives separately in ~/.agentstow/agentstow.toml.

The six families

Skills, commands, subagents
Symlink fan-out. One canonical relative link per entry in each agent's directory; links of ours that dangle are pruned; anything else is left alone.
Instructions
Three mechanisms, because agents differ in kind. A plain symlink where the whole file can be ours (Codex, pi, oh-my-pi, Windsurf). An import line for Claude, whose CLAUDE.md holds your own content, so agentstow only ensures @~/.agents/AGENTS.md is present — additive and idempotent. A rules-dir link for Roo, which globs a directory.
MCP servers
Rendered into each agent's dialect and key-merged. See below.
Hooks
Command-hooks declared once in hooks/<Event>.toml and merged into Claude, Codex and Gemini by command string: agentstow owns exactly the array elements whose command matches a Store hook, and other tools' hooks in the same event array survive untouched. Codex trust hashes are never written — approving code execution stays your decision.

A file another tool already owns — claude-mem's boilerplate in opencode's AGENTS.md, say — is a Conflict: reported with the remedy named, never overwritten.

Ownership, without a state file

Everything agentstow writes must be recognisable as its own on a later run, with no database to consult. Three identities cover every mechanism.

Link identity
A symlink resolving into the Store is ours — canonicalised, and pruned when it dangles. A link resolving anywhere else is Foreign and is never touched.
Name identity
An entry key-merged into a shared file is ours when its name appears in the Store; for hooks the command string is the name. Unknown names in the same file are Foreign. One consequence is deliberate: sync never deletes an MCP server, because a name that vanished from the Store cannot be told apart from one you added by hand. Removal is agentstow mcp remove.
Marker identity
A wholly generated file carries one comment line saying so. That is what makes pruning safe: removing a marked file returns the machine to a state agentstow created. An unmarked file at the same path was written by somebody else.

Reading status

StateMeans
linkedA canonical link onto the right Store entry. Nothing to do.
missingThe Store has it; this agent does not, yet.
staleOur link, but not in canonical form — it will be rewritten.
danglingOur link, pointing at a Store entry that is gone. It will be pruned.
variantA real file or directory shadowing a Store entry, on purpose. Preserved.
variant-identicalSame, but byte-identical to the Store copy — a duplicate you can collapse.
foreignNot ours. Reported, never modified.
conflictAnother tool owns a file agentstow would otherwise write.
Exit codes: 0 clean, 1 error, 2 something actionable. A Variant that diverges and a Foreign entry are settled states, not work, so neither makes the exit code 2.

MCP servers

One mcp.json in the standard shape is rendered into each agent's dialect: Codex takes TOML tables with http_headers; opencode wants local/remote with the command flattened into one array and env renamed environment; Gemini distinguishes SSE from streamable HTTP by which URL key it uses; Windsurf calls it serverUrl.

# ~/.agentstow/agentstow.toml
[mcp.serena]
agents = ["claude", "codex"]      # default: every capable agent

[mcp.node_repl.tweaks.codex]
startup_timeout_sec = 120         # reaches Codex alone

Secrets are written as ${env:VAR} in the Store, resolved from the environment at sync, and redacted from every line of output — so the Store stays committable. If a resolved secret would land in a file others can read, agentstow says so.

agentstow mcp adopt claude/serena reverse-translates an existing server into the Store, turning agent-specific knobs into Tweaks. It proves the result first: if re-rendering it would not reproduce what you already have, it refuses rather than reporting a success that the next sync would undo.

CLI

CommandDoes
initCreate the Store skeleton and report what could be adopted.
syncFan the Store out to every installed agent. --dry-run previews.
statusWhat is in sync, what is not, what is not ours. --json.
adopt <path>Absorb an existing config into the Store, leaving a link.
doctorInstalled agents, Store usability, hygiene warnings.
mcp listStore servers and their state in every agent. --json.
mcp adopt<agent>/<server>, or --all.
mcp removeDelete a server from the Store and from every agent.

agentstow.toml

Optional, at ~/.agentstow/agentstow.toml. Absent means defaults. It never lives in the Store, so the Store stays ecosystem-standard content only.

[targets]
cursor = false                    # leave this agent alone entirely

[custom.myagent]                  # an agent the registry does not know
root = ".myagent"
skills = ".myagent/skills"

[mcp.heavy-server]
agents = ["codex"]

Many machines

agentstow does not sync across machines, deliberately — that would mean building conflict resolution, and git already won that argument. The Store is an ordinary directory, so version it:

# as a git repo
cd ~/.agents && git init && git add . && git commit -m "my agent config"

# or with chezmoi
chezmoi add ~/.agents ~/.agentstow

On the second machine, clone it and run agentstow sync.

Vocabulary

Store
The canonical directory ~/.agents/ holding the single real copy of everything agentstow syncs.
Target
One agent's config surface that receives fan-out.
Fan-out
One Store entry materialised into every Target as a symlink.
Native
An agent that reads the Store directly and therefore needs no fan-out.
Variant
A real file or directory in a Target that intentionally shadows the Store copy for that agent alone. First-class and preserved, never clobbered.
Adopt
Absorbing an existing real file or directory from a Target into the Store, leaving a link behind.
Foreign
A link, file or server entry in a Target that agentstow does not own. Never touched, listed by status.
Conflict
A Foreign file occupying a destination agentstow would otherwise write — reported with the remedy named, never overwritten.
Render
Translating a Store entry into one agent's native format and merging it into that agent's config file.
Managed
An entry whose name appears in the Store: agentstow owns it in every targeted config, and the Store wins.
Tweak
A per-agent addition merged into one agent's rendering, declared in agentstow.toml, never in the Store.
Marker
The one-line comment in every generated file, making it recognisable as agentstow's without a state file.