Keep AI instruction files in sync using symlinks
Project description
Symlegion
Keep your AI instruction files in sync with zero magic — just symlinks.
Credit: Symlegion is a Python port of the original
agentlinktool by Martin Mose Hansen.
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. Symlegion 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. Since they're so important, these files are typically generated with expensive models. Why pay repeatedly to regenerate similar content across different tools?
Future-proof by design: We don't know what tomorrow brings in the AI tooling space, but symlegion is ready. New tool expects .newtool/ai-config.md? Just add it to your config. Complex nested structure like workspace/ai/tools/newframework/instructions.md? No problem. Symlegion automatically creates the directories and symlinks without any code changes needed.
Scope: instruction files only. No MCP
.mcp.jsonor chain configs. Simple on purpose.
Why Symlegion?
- One real file, many aliases — pick a source (
CLAUDE.mdorAGENTS.mdor whatever), symlink the rest. - No codegen — no templates, no transforms, no surprise diffs.
- Project + global — works in repos and under
~/.config/…. - Idempotent — re-run safely; it fixes broken/misdirected links.
- Portable — works on macOS and Linux.
- Future-ready — handles any directory structure, automatically creates paths. Tomorrow's AI tool? Just add its path.
How it works
You tell Symlegion which file or folder is the source, and which other paths should link to it. Symlegion creates/fixes symlinks accordingly.
mode: directis the original behavior and the default.mode: recursivesearches multiple roots for matching projects and creates links inside each match.
# .symlegion.yaml (in project root)
- source: CLAUDE.md
links:
- AGENTS.md # OpenCode, Codex
- .github/copilot-instructions.md # GitHub Copilot
- .cursorrules # Cursor AI
- GEMINI.md # Gemini CLI
- source: prompts/shared
links:
- .ai/prompts # Folder symlink
Result:
./CLAUDE.md # real file you edit
./AGENTS.md -> CLAUDE.md (symlink)
./.github/copilot-instructions.md -> ../CLAUDE.md (symlink)
./.cursorrules -> CLAUDE.md (symlink)
./GEMINI.md -> CLAUDE.md (symlink)
./.ai/prompts -> ../prompts/shared (symlink dir)
Global mode (in HOME) is the same idea:
# ~/.config/symlegion/config.yaml
- source: ~/.config/claude/CLAUDE.md
links:
- ~/.config/opencode/AGENTS.md
- ~/.config/some-tool/INSTRUCTIONS.md
Install
uv tool install symlegion
Or run without installing:
uv run symlegion.py --help
Usage
Getting started
# Initialize in your project
symlegion init
# Edit the created .symlegion.yaml to match your needs
# Create your source file (e.g., CLAUDE.md)
# Sync to create symlinks
symlegion sync
init creates a starter .symlegion.yaml with both direct and recursive examples.
The starter config assumes OpenCode as the source of truth and includes mappings for:
- rules:
AGENTS.md->CLAUDE.md,.claude/CLAUDE.md,.goosehints. - commands/prompts/recipes:
.opencode/commands/->.claude/commands/,.pi/prompts/,.goose/recipes/.
Commands
symlegion init
symlegion sync
symlegion check
symlegion clean
symlegion doctor
Command behavior
initcreates a starter YAML file. By default it writes.symlegion.yamlin the current directory. With--config, it writes to the provided path.syncvalidates each source and creates or repairs the configured links.checkreports source and link status without changing anything.cleanremoves only links managed by the selected config file. It never removes source files or folders.doctorchecks symlink support, binary availability, and default config locations.
Helpful flags
symlegion sync --dry-run
symlegion sync --force
symlegion --verbose sync
symlegion --config ~/somewhere/project.yaml sync
--configchooses a specific YAML file.--dry-runpreviews changes without writing anything.--forceallows Symlegion to replace wrong targets, replace existing non-symlink paths during sync, and accept a symlink as the source.--verboseprints each source and link as it is processed.
Without init (auto-config)
symlegion sync
What it does:
- If
--configis provided, Symlegion uses that file. - Otherwise it reads
.symlegion.yamlin the current directory. - It creates or fixes symlinks listed under each group so they point to that group's
source.
If there's no .symlegion.yaml in CWD:
- Falls back to
~/.config/symlegion/config.yaml(global). - If missing, it auto-creates a sane default and tells you.
Config file path resolution
symlegion sync/check/clean:- use
--config <file>when provided. - otherwise use
.symlegion.yamlin the current working directory. - otherwise fall back to
~/.config/symlegion/config.yaml.
- use
symlegion init:- creates
.symlegion.yamlin the current working directory by default. - creates the file passed through
--configwhen provided.
- creates
Config
Project config (recommended)
Place a single file at repo root:
.symlegion.yaml
- source: CLAUDE.md
links:
- AGENTS.md
- OPENCODE.md
- source: prompts/shared
links:
- .ai/prompts
Notes:
- Omitting
modeis the same asmode: direct. - Each
sourcecan be a real file or a real folder, but not a symlink unless you use--force. - A config file is a YAML list of groups. Each group has one
source, one or morelinks, and optional recursive fields.
Direct mode
direct is the default mode and matches the original Symlegion behavior.
- mode: direct
source: CLAUDE.md
links:
- AGENTS.md
- OPENCODE.md
sourcecan be absolute or relative.- Relative
sourceandlinksare resolved from the folder that contains the YAML config file. - Absolute
sourcevalues stay absolute. ~expands to$HOME.- Direct mode is best when one repo or one config file explicitly manages known paths.
Recursive mode
Use recursive when you want Symlegion to scan one or more parent folders, find matching projects, and create the same symlink layout inside each one.
- mode: recursive
source: .opencode/commands/
links:
- .claude/commands/
- .pi/prompts/
search:
- ~/koofr/workspace/
- ~/code/
depth: 3
- In recursive mode,
sourceand every entry inlinksmust be relative paths. searchentries may be absolute or relative.- Relative
searchentries are resolved from the folder that contains the YAML config file. ~insearchentries expands to$HOME.- If
depthis omitted, Symlegion uses5. - Symlegion walks each search root down to
depthlevels, looking forsourcein each candidate folder. - When it finds a match, it creates every link in
linksrelative to that matched folder root. - If one or more search paths do not exist, Symlegion prints a warning and continues.
- Recursive mode is best when one config file manages many repos that share the same layout.
How recursive matching works
Given this config:
- mode: recursive
source: .opencode/commands/
links:
- .claude/commands/
- .pi/prompts/
- .goose/recipes/
search:
- ~/code/
- ../workspace/
depth: 3
- Symlegion scans every folder under each
searchroot until depth3. - When it finds a folder containing
.opencode/commands/, that folder becomes the matched project root. - It then creates:
.claude/commands/->.opencode/commands/..pi/prompts/->.opencode/commands/..goose/recipes/->.opencode/commands/.
- If
~/code/client-a/project-xmatches, all links are created inside~/code/client-a/project-x.
Selected config examples
Starter config generated by init:
- mode: direct
source: AGENTS.md
links:
- CLAUDE.md
- .claude/CLAUDE.md
- .goosehints
- mode: direct
source: .opencode/commands/
links:
- .claude/commands/
- .pi/prompts/
- .goose/recipes/
# - mode: recursive
# source: .opencode/commands/
# links:
# - .claude/commands/
# - .pi/prompts/
# - .goose/recipes/
# search:
# - ~/koofr/workspace/
# - ~/code/
# depth: 3
Global config
~/.config/symlegion/config.yaml
- source: ~/.config/claude/CLAUDE.md
links:
- ~/.config/opencode/AGENTS.md
Platform notes
- macOS + Linux: standard POSIX symlinks (
ln -s) — works the same. - Git: symlinks are stored as links (not file copies). That's fine; teams who dislike that can add them to
.gitignore.
Gitignore patterns
Since symlegion creates multiple instruction files but only one is the real source, you can gitignore all AI instruction files except your chosen source:
# Ignore all AI instruction files
AGENTS.md
CLAUDE.md
GEMINI.md
OPENCODE.md
.cursorrules
.github/copilot-instructions.md
# But track your chosen source file (example: tracking CLAUDE.md)
!CLAUDE.md
This keeps your repository clean while ensuring your source file is version controlled. Symlegion will create the source file if it doesn't exist when running sync.
- Editors/IDEs: most follow symlinks transparently.
FAQ
Why not templates or generators?
Because 90% of the time the files should be identical. When they're not, this tool isn't the right fit (or add a second source and stop linking that one).
What if my source differs per project?
Perfect—put a .symlegion.yaml in each repo and choose the source you actually edit there.
Can the source be AGENTS.md instead of CLAUDE.md?
Yes. The source is whatever you want to edit. The others link to it.
What happens when a new AI tool comes out?
Just add its expected path to your config. If "SuperCoder AI" expects .supercoder/prompts/main.md, add that path and run symlegion sync. Directories are created automatically, and the symlink points to your chosen file or folder source. Zero code changes, zero updates needed.
MCP / .mcp.json?
Out of scope. Formats differ between tools; symlinking a single JSON to multiple consumers usually doesn't make sense.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file symlegion-0.3.1.tar.gz.
File metadata
- Download URL: symlegion-0.3.1.tar.gz
- Upload date:
- Size: 15.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24a818dddccf3fb6c88dfef2d360488961522f70270229ac972d8f2af079f3a2
|
|
| MD5 |
b9a0c88378cad12a404452234455b64f
|
|
| BLAKE2b-256 |
f26dd70da6e1ff7989a1b5b2d0a15489c872f0a88687f28b534eac0eb8185159
|
File details
Details for the file symlegion-0.3.1-py3-none-any.whl.
File metadata
- Download URL: symlegion-0.3.1-py3-none-any.whl
- Upload date:
- Size: 13.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
226fd2e32564ae9d10a3d3ef58c1b49f8414ad520c8a6247c583348a1be08a28
|
|
| MD5 |
aa39968abb5ddf3023c1567d6e05d64c
|
|
| BLAKE2b-256 |
f572d5f0907d75924b7a999a88553b25c36f06b828967e0cf57c1fcd178a06cc
|