DEV Community

Cover image for One AGENTS.md, every tool: how I stopped copy-pasting CLAUDE.md everywhere
snapsynapse
snapsynapse

Posted on • Originally published at snapsynapse.com

One AGENTS.md, every tool: how I stopped copy-pasting CLAUDE.md everywhere

Every time I open a new repo, I have to decide which AI coding assistant is going to read it.

Claude Code wants CLAUDE.md. Gemini CLI wants GEMINI.md. Cursor and OpenCode read AGENTS.md. GitHub Copilot reads .github/copilot-instructions.md. Junie wants .junie/AGENTS.md. There are seventeen of these tools now, give or take, and they all expect their instruction file at a specific path with a specific name. None of them agree on what that path or name should be.

So you end up with four near-identical files in every project, all saying "prefer functional style, avoid comments that restate the code, use table-driven tests" ...and then they drift. You update CLAUDE.md after a refactor and forget to update AGENTS.md. Now Claude and Codex have different opinions about your codebase. The AI assistants start disagreeing with themselves across tool boundaries. It's a stupid problem and it wastes a surprising amount of brain.

The fix: one real file, many symlinks

agentlink is a tiny Go CLI that keeps these files in sync the boring way. You pick one file as the source (I use ~/AGENTS.md and every other tool-specific file becomes a symlink pointing back to it.

AGENTS.md                              # the real file you edit
CLAUDE.md                           -> AGENTS.md
GEMINI.md                           -> AGENTS.md
.cursorrules                        -> AGENTS.md
.github/copilot-instructions.md     -> ../AGENTS.md
Enter fullscreen mode Exit fullscreen mode

Edit the source once, every tool sees the change. No codegen, no templates, no surprise diffs. The symlinks are plain filesystem symlinks, the same mechanism that's been in Unix since 1978.

That's the whole idea. The reason this works instead of being laughably primitive is that every AI tool I've tested follows symlinks without complaining. They just read "the file at this path" and a symlink is a file at a path.

GitHub logo snapsynapse / agentlink

Sync one AGENTS.md to every AI coding tool on your machine. Symlinks, no codegen.

Agentlink

Checks License: MIT

Fork notice. This is a Snap Synapse fork of martinmose/agentlink by Martin Mose Facondini (MIT). Extended with detect, scan, hooks, automatic backup, global-config support, and integration tests. See NOTICE for full attribution. Upstream PR: martinmose/agentlink#2.

Sync one AGENTS.md to every AI coding tool on your machine -- with zero magic, just symlinks.

Different tools want different files at project root: AGENTS.md (OpenAI/Codex, OpenCode), CLAUDE.md (Claude Code), GEMINI.md, etc. There's no standard, and I'm not waiting for one. Agentlink solves the basic need: keep your personal instruction files (in ~) and your project instruction files in sync without generators. Edit one, they all reflect it.

Creating instruction files is easy with /init commands, but keeping them up to date is the hard part -- and expensive too. Good instruction files are often crucial and make a huge difference when using agentic tools…

What the fork adds

agentlink already existed as a two-command core (init and sync) written by Martin Mose Facondini (THANK YOU!) from before the Agentic Revolution kicked off, waaaaay back in August 2025. It did the minimum version well: read a yaml config, create the links, be idempotent. I forked it because I wanted it to handle five things the core didn't that have since become much more important:

🔍 agentlink detect

Walk the system and report which AI tools are actually installed. It checks ~20 tools by path (~/.claude, ~/.codex, ~/.gemini, etc.) and by $PATH commands (claude, codex, cursor, aider). Pass --generate and it writes a starter .agentlink.yaml based on what it found. Removes the step where you have to remember which tools you use before writing the config.

📁 agentlink scan [dir]

Given a directory (default ~/Git), walk into every git repo that contains an AGENTS.md and wire up the missing symlinks in-place. Useful once, when you first install agentlink and want to retrofit your whole code directory. Supports --dry-run so you can see what it would do first.

🪝 agentlink hooks install

Install automatic sync triggers so the command runs without you thinking about it. It installs three things:

  • A git hooks path (post-checkout and post-merge) via core.hooksPath, so sync runs after every branch switch and pull.
  • A zsh chpwd hook, so sync runs every time you cd into a repo.
  • A macOS launchd agent that runs sync every 60 minutes as a heartbeat.

All three are wrapped in # >>> agentlink >>> / # <<< agentlink <<< markers so agentlink hooks remove can clean them up without touching anything else you've edited.

💾 sync --backup

Before replacing an existing CLAUDE.md with a symlink, back it up to CLAUDE.md.bak (or CLAUDE.md.<timestamp>.bak if a .bak already exists). Empty files get skipped with a warning — no point backing up zero bytes. This sounds obvious but the original sync happily overwrote whatever was there; it was great until it wasn't.

🌍 Global config

A second config file at ~/.config/agentlink/config.yaml that lets you sync files across your home directory, not just within a single repo. This is the one I use most — it keeps ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, and ~/.config/opencode/AGENTS.md all pointing at a single ~/AGENTS.md that captures my personal working style across every project.

The tech stack

  • Go 1.23+, single binary, no runtime deps.
  • Standard library only for filesystem ops (os.Symlink, os.Readlink, filepath.Walk).
  • gopkg.in/yaml.v3 for config parsing.
  • Cobra for CLI scaffolding.

The only non-obvious decision: I kept the symlink creation logic in a separate internal/symlink package with its own tests, because the "is this path safe to replace with a symlink" check has a lot of edge cases (existing real file, existing broken symlink, existing symlink pointing somewhere else, target doesn't exist yet) and I wanted them all exercised by the test suite rather than discovered in production on someone's repo.

Why not just use a script?

Because the script is 30 lines and the edge cases are 300. Backup-before-replace, skip-empty-files, dry-run mode, detect-tool-presence, core.hooksPath compat with existing hook dirs, launchd plist generation, config-file discovery walking up from cwd — every one of those is two-line logic plus a test. After the fourth version of the bash script I'd written over two years, I gave up and did it properly.

I also wanted the detect command. No bash one-liner gives you "is Claude Code installed on this machine," because "installed" means different things to different tools — a config dir for some, a $PATH binary for others, a macOS app bundle for a third group. A real registry with a typed schema was the right shape.

Try it

go install github.com/snapsynapse/agentlink/cmd/agentlink@v0.2.0

# In a repo with an existing AGENTS.md:
agentlink init
agentlink sync

# Or for the whole home directory:
agentlink sync --global
Enter fullscreen mode Exit fullscreen mode

There's a landing page at agentlink.run with the full tool list and config examples. MIT licensed. The fork is at github.com/snapsynapse/agentlink; upstream is martinmose/agentlink, and the fork additions are open as upstream PR #2 — fingers crossed they land.

Closing

How do you currently handle AI instruction file drift in projects where you use more than one assistant? Are you manually copy-pasting? Do you have a script? Drop it in the comments 👇

I'm genuinely curious whether most people just pick one tool and live with it, or whether this is a real shared pain.

Top comments (0)