Write AGENTS.md once. Generate the file every other coding agent reads.
$ python3 agent_fanout.py
create CLAUDE.md
create .cursor/rules/from-agents-md.mdc
create .github/copilot-instructions.md
Claude Code reads CLAUDE.md. Cursor reads .cursor/rules/*.mdc. Copilot reads
.github/copilot-instructions.md. Gemini reads GEMINI.md. They mostly want the same
content in a different location.
So teams keep the same rules in five files and edit whichever one they happen to have open. Nothing enforces that the others follow. Six weeks later the agents on the same repository are running different instructions and nobody can say which is right.
Keeping one source and generating the rest removes the situation instead of managing it.
AGENTS.md is the natural source: it is the convention most of these tools already
read directly.
Nothing to install. One file, no dependencies, Python 3.8+.
curl -O https://raw.githubusercontent.com/untactit/agent-fanout/main/agent_fanout.py
python3 agent_fanout.pypython3 agent_fanout.py # detect installed tools, generate for those
python3 agent_fanout.py --all # generate for every known target
python3 agent_fanout.py --only cursor copilot
python3 agent_fanout.py --dry-run # show what would change
python3 agent_fanout.py --check # exit 1 if anything is out of date
python3 agent_fanout.py --list # show known targetsBy default it only writes for tools it can see in the repository — a .cursor/
directory, a .github/ directory, an existing CLAUDE.md. Generating rule files for
tools nobody on the team uses is just clutter.
| Tool | Generated file |
|---|---|
| Claude Code | CLAUDE.md |
| Cursor | .cursor/rules/from-agents-md.mdc |
| GitHub Copilot | .github/copilot-instructions.md |
| Gemini | GEMINI.md |
| Windsurf | .windsurfrules |
| Cline | .clinerules |
| Continue | .continuerules |
| Zed | .rules |
Cursor's file is written in .mdc form with the frontmatter it expects
(alwaysApply: true). The rest are the source content with a generated header.
Missing one? The table is a constant at the top of the file — issue or PR welcome.
Every generated file carries a header:
<!-- Generated from AGENTS.md by agent-fanout. Do not edit this file. -->
<!-- Edit AGENTS.md and re-run agent-fanout. [agent-fanout:generated a1b2c3d4e5f6] -->
If a target file exists without that header, the tool treats it as hand-written and refuses to touch it:
skip CLAUDE.md (not generated by this tool; use --force to replace)
A carefully written CLAUDE.md is never destroyed because someone ran this in the wrong
directory. --force overrides it when that is what you actually want.
- run: python3 agent_fanout.py . --check--check exits 1 when a generated file is missing or no longer matches AGENTS.md. The
build fails on the pull request that edited CLAUDE.md directly instead of the source,
which is the moment the divergence would otherwise have started.
Pairs with agent-drift, which finds copies that have already drifted apart. This one keeps them from drifting in the first place.
- No merging. The source is the source; targets are derived.
- No per-tool content. If Cursor needs a rule Claude must not see, this is the wrong tool for that rule — keep it in Cursor's own file and leave it hand-written.
- No network. Nothing leaves your machine.
MIT. See LICENSE.
The failure mode this tool addresses is written up here:
- Generating your agent rules from one file does not stop them drifting
- AGENTS.md was supposed to end the sprawl. I counted the files on one laptop.
Built while working on untactit, a control plane for the skills, rules and memory AI agents run on. This tool works without it.