Back to blog
/5 min read/Petr Kindlmann

I Was Maintaining the Same Project Rules in Four Files

Cursor, Claude Code, Copilot, Codex — each one reads a different file. Same content, four places, all drifting. Here's the 50-line CLI that fixed it.

aideveloper-toolsclaude-codecursoragents-md

I want to confess something I've been doing for the last six months that, in retrospect, was completely insane.

For every project I work on, I had a CLAUDE.md for Claude Code, a .cursorrules file for Cursor, a .github/copilot-instructions.md for whoever opens the repo in GitHub.com, and an AGENTS.md for Codex. Four files. Same content. All slightly drifted from each other because I'd update one in a session and forget the others.

This was not a "process problem" I could solve with discipline. This was structural. I built infrastructure to fix it instead.

How I got here

You'd think the natural state of "instructions for an AI agent in a repo" would be one file. It isn't, because each agent vendor decided independently which file they'd read.

  • Cursor reads .cursorrules (legacy) and .cursor/rules/*.mdc (newer).
  • Claude Code reads CLAUDE.md.
  • GitHub Copilot reads .github/copilot-instructions.md.
  • Codex CLI reads AGENTS.md — and that's the format with the broadest cross-tool acceptance.

If you only use one agent, this is a non-problem. The moment you use two, it becomes a slow-burn maintenance tax. The moment you use three, you stop updating the third.

For a while I tried to solve it with discipline — a checklist, a pre-commit hook, a mental note. None of it stuck. The truth about engineering discipline is that it doesn't survive context-switching. You're deep in a debugging session, you discover the project's auth flow has a quirky exception, you add a line to CLAUDE.md because that's the agent you're using right now. Cursor finds out three weeks later when someone else picks up the codebase and it suggests a refactor that violates the constraint.

This is the same dynamic as keeping multiple "second brain" notebooks in sync. Nobody actually does it. They drift.

The fix turned out to be small

I added a command to my CLI that compiles one file into the other three.

AGENTS.md  →  CLAUDE.md
            →  .cursorrules
            →  .github/copilot-instructions.md

That's it. That's the entire idea.

AGENTS.md is canonical because Codex picked the most generic name and I'm willing to ride that coattail. The other three are now compilation artifacts. Each one starts with a marker comment so the compiler knows it's safe to overwrite next time. If the file isn't marked, the tool refuses to touch it without --force — your hand-written CLAUDE.md from last year doesn't get clobbered by accident.

The whole emit logic, after I stripped the test scaffolding and the CLI argument parsing, is about fifty lines of TypeScript. The hard part wasn't the code. The hard part was admitting that the problem was real and that "just be more disciplined" wasn't the answer.

Why this was worth shipping

Three reasons.

It removes a category of bug. Stale agent instructions don't just produce slightly wrong code — they produce confidently wrong code. The agent reads the file, treats it as ground truth, and commits to a refactor based on rules that haven't been current in a month. You don't notice until you're three commits deep. Removing the drift removes the bug class.

It makes one thing canonical. Before this, if you opened a repo and wanted to know "what are the rules here", you had to read four files and reconcile them. Now you read one. New contributors pick it up faster. The PR review question "is this in the rules?" has a single grep target.

It works in CI. I can run emit --check in a GitHub Action and fail any PR where someone hand-edited one of the compiled files. That's a hard guarantee, not a soft norm. Nobody can drift the files apart again, even if they try.

What it didn't fix

This compiler doesn't solve the bigger memory problem. Project rules are static. The interesting context — yesterday's debugging session, last week's architectural decision, the bug you fixed two months ago — that's still scattered. That's still the harder problem. I'm working on it, but it's a different shape.

Project rules are static. Project memory is dynamic. A compiler is enough for the first one. The second one needs an actual ingestion pipeline, classification, and a way for agents to query it. That's where Omnus comes in. But the compiler had to exist first, because if you can't even keep the static rules in sync, the dynamic memory is wishful thinking.

How to get it

The CLI is terso-cli on npm.

npm install -g terso-cli
cd ~/your-project
terso init       # scaffolds AGENTS.md if you don't have one
terso emit       # writes CLAUDE.md, .cursorrules, copilot-instructions.md

It auto-detects which agents your repo uses (from the presence of .cursor/, CLAUDE.md, etc.) and only writes to those targets. There's also a --watch mode if you want it to re-emit every time you save AGENTS.md, and a --check mode for CI.

The whole thing is MIT licensed. It works offline. It doesn't phone home. It's the smallest piece of infrastructure I've shipped this year and possibly the most useful, and I should have built it six months ago.