AgentSync: one agent instructions template, everywhere
Why I built AgentSync, how it works, and how you can use it to keep AI coding assistants aligned across tools.
I’ve been meaning to add a blog to this site for a while. I had all these grand ideas about what the first post “should” be.
Instead, here we are: a tiny CLI I wrote because I kept tripping over the same stupid problem and I didn’t feel like tripping over it again.
The problem
If you use more than one “agent” tool (or even just bounce between them on different days), you’ll notice they all want the same thing from you:
“Put your global instructions in our special file, in our special place.”
For example:
- OpenAI Codex CLI:
AGENTS.md - Claude Code:
CLAUDE.md - GitHub Copilot:
copilot-instructions.md
At first it’s fine. You copy the same text into three files and feel very organized.
Then you change one line. Or add a new “house rule” you really care about (like “don’t run destructive commands unless I say so”).
And now one of the files is out of date. Always. You just don’t know which one until an assistant confidently does the exact thing you told it not to do.
That’s what github.com/claaslange/agentsync is for.
Here’s the short version I used when introducing it to some colleagues:
If you’re like me and you’re connecting the Copilot subscription to different harnesses like opencode, pi and the GitHub native Copilot, you might like this mini-project I built:
github.com/claaslange/agentsync.It allows you to have a centralized
AGENTS.mdwith variables and simple logic and “sync” its contents to all the different places the harnesses are reading it from (e.g.~/.copilot/copilot-instructions.md,~/.pi/agent/AGENTS.mdand~/.config/opencode/AGENTS.md).I use those files to add context for agents about local commands that only I might have installed like zellij, ast-grep (grep with code understanding) or the gh command line tool.
What AgentSync is
AgentSync is a CLI that copies one “source of truth” instruction template into all the places different tools look for it.
You keep one template (for me it’s basically a “how I work” doc), and you tell AgentSync where each harness expects it. AgentSync renders the template and writes the output files.
That’s it. No magic, just fewer chances to forget a copy/paste.
Why I built it
I wanted my setup to feel the same no matter which tool I was in. Same expectations. Same boundaries. Same “please don’t do that” rules.
I also wanted to feed assistants some my-machine-specific context. Stuff like:
zellijfor long-running processessg(ast-grep) for code-aware searching/refactorsghfor GitHub workflows
Humans on a team can ask “how do you run that locally?” Agents can’t. They just guess. So I’d rather tell them… once.
Once I had more than one harness in my daily flow, “just copy the file” stopped being reliable.
How it works
AgentSync uses a template (defaults to ~/.agentsync/AGENTS_TEMPLATE.md) and a config file (defaults to ~/.agentsync/agentsync.config.json). For each configured target, it renders the template using Liquid and writes the result to the target path.
It also has:
- dry-run (show what would change)
- check (CI-friendly; fails if something would change)
Install
Using npm:
npm i -g @claaslange/agentsync
agentsync help
Or Bun:
bun add -g @claaslange/agentsync
agentsync help
Quickstart
Create ~/.agentsync/ and copy the example files from the repo:
mkdir -p ~/.agentsync
cp ./example/agentsync.config.json ~/.agentsync/agentsync.config.json
cp ./example/AGENTS_TEMPLATE.md ~/.agentsync/AGENTS_TEMPLATE.md
Then:
agentsync dry-run
agentsync sync
Config example
Here’s a minimal config that targets multiple tools:
{
"$schema": "https://raw.githubusercontent.com/claaslange/agentsync/main/src/agentsync.schema.json",
"template_path": "AGENTS_TEMPLATE.md",
"targets": [
{ "agent": "codex", "path": "~/.codex/AGENTS.md" },
{ "agent": "claude_code", "path": "~/.claude/CLAUDE.md", "enabled": false },
{
"agent": "github_copilot",
"path": "~/.copilot/copilot-instructions.md",
"variables": { "AGENT_NAME": "GitHub Copilot" }
}
]
}
Notes:
targetsis a list of{ agent, path, enabled?, variables? }variablesare per-target (there are no global variables)- you can turn targets on/off via
enabled
Template example (Liquid)
Templates are rendered using Liquid. You can use variables and control flow. For example:
# Instructions for {{ AGENT_NAME }}
These instructions were generated for:
- Agent: {{ AGENT_NAME }}
- Target path: {{ TARGET_PATH }}
- Generated at: {{ RUN_TIMESTAMP }}
{% if AGENT_NAME == "GitHub Copilot" %}
Keep responses short and avoid long multi-step plans.
{% endif %}
AgentSync provides a few built-in variables for every target:
AGENT_NAME(defaults to the target’sagent)TARGET_PATH(resolved destination path)TEMPLATE_PATH(resolved template path)RUN_TIMESTAMP(UTC timestamp)
What you can use it for
This is what I actually use it for day-to-day:
Keep your “house rules” consistent
If you have strong preferences (code style, testing habits, commit messages, safety rules), you probably want those rules to follow you around.
Keep small tool-specific quirks
Some tools need a different filename. Some behave better with slightly different wording. With per-target variables and a bit of Liquid you can keep one template and still tailor the final output.
Make drift obvious
If you commit your generated instruction files to a dotfiles repo, you can verify they’re up-to-date:
agentsync check
If it exits with code 1, you know something would change and you probably forgot to run agentsync sync.
A concrete workflow (example)
I like to keep my canonical template and config in a dotfiles repo, then symlink or copy them into ~/.agentsync/.
When I update the template:
agentsync sync
And before pushing dotfiles changes:
agentsync check
Closing thoughts
AgentSync is intentionally small. It’s not a dotfiles manager. It just handles the “same instructions everywhere” problem in a way that’s hard to screw up.
If you’re juggling multiple AI coding tools, this is the sort of thing that’s boring when it works—which is exactly what I want.