Most dotfiles repos sync your shell. Mine syncs my agents. The same Claude and Codex brain runs on both my machines because their global instructions live in a git-tracked symlink.
Author: James Phoenix | Date: May 2026
The Two-Machine Problem
I work across two Macs. A MacBook Pro for travel and meetings, a Mac Studio for heavy local builds and overnight runs. Both run the same dev stack. Both run Claude Code and Codex. Both need to know the same things about how I work.
For years I solved this the lazy way. I would set up the laptop properly, then half-configure the Studio when I needed it, then realise three months later that an alias I rely on does not exist there. Or worse, I would teach Claude a new convention on one machine and watch the other machine’s Claude blunder into the same mistakes a week later because it never got the memo.
That second problem is the one nobody talks about. Cross-machine sync is solved for code, dotfiles, and secrets. It is not solved for agent context. And agent context is now the most valuable thing on the machine.
What the Repo Contains
Just-Understanding-Data-Ltd/dotfiles is a private repo with a deliberately small footprint:
dotfiles/
├── install.sh # Idempotent symlink installer
├── claude/CLAUDE.md # Global Claude Code instructions
├── codex/AGENTS.md # Global Codex instructions
├── zshrc/.zshrc.shared # Shared aliases, env vars, service tokens
├── skills/ # Reusable Claude/Codex skills
├── services/ # Shared MCP server config fragments
└── tax/scenario.json # Single source of truth for my UK tax model
The shell side is unremarkable. The interesting bit is everything above zshrc/.
claude/CLAUDE.md is the global instructions file Claude Code reads at startup, on every project, on every machine. It contains my 1Password vault layout, my secrets convention, my Cloudflare credential pattern, my onboarding runbook, and a workflow for editing my personal tax scenario. Anything I want both machines’ Claude to know lives here.
codex/AGENTS.md is the equivalent for Codex. Same content, slightly different format because the two harnesses parse instructions differently.
zshrc/.zshrc.shared holds aliases (ai, cai), env vars (MAX_THINKING_TOKENS, OP_SERVICE_ACCOUNT_TOKEN), and PATH additions that should be identical everywhere. Each machine’s real ~/.zshrc is a one-liner: source ~/.zshrc.shared.
The Kicker: Symlinks, Not Copies
The install script does not copy these files into place. It symlinks them.
ln -sf "$DOTFILES_DIR/claude/CLAUDE.md" "$HOME/.claude/CLAUDE.md"
ln -sf "$DOTFILES_DIR/codex/AGENTS.md" "$HOME/.codex/AGENTS.md"
ln -sf "$DOTFILES_DIR/zshrc/.zshrc.shared" "$HOME/.zshrc.shared"
The difference matters. With copies, an edit means: change the file, then remember to also push to the repo, then remember to pull on the other machine, then run a sync script to overwrite the local copy. Four steps, three of which I will forget.
With symlinks, an edit on either machine is an edit to the repo. git add tax/scenario.json && git commit && git push. On the Studio, git pull and the change is live immediately because the path Claude reads from already points into the working tree. There is no “deploy” step. The symlink is the deploy step.
This means I can finish a Claude session on the laptop where I have just refined a workflow, codify it into claude/CLAUDE.md, push, walk to the Studio, and the Studio’s Claude already knows. My agent’s brain is a git remote.
What Stays Machine-Specific
The trap most people fall into is putting too much in the shared file. Then it breaks on the other machine because of a path or a hostname.
I split it strictly. Anything portable goes in ~/.claude/CLAUDE.md (the symlink). Anything machine-specific goes in ~/CLAUDE.md (a regular file, not synced). The two are loaded together but live separately.
Examples of machine-specific content:
- SSH details for the other machine (because they are not symmetric)
- Hostnames, IPs, usernames (they differ:
jamesaphoenixon the Pro,jamesphoenixon the Studio) - Per-machine API keys for things that need different auth per device
- Conda init paths and homebrew prefixes if they ever drift
The same split applies to the shell. ~/.zshrc.shared is portable; ~/.zshrc holds whatever is unique to this Mac.
This separation is what makes the symlink approach work without surprise blowups when something machine-specific leaks into shared config.
Why This Compounds
Three reasons this beats the obvious alternatives.
One. Every conversation on either machine starts with the same priors. I do not have to re-teach Claude about my 1Password layout, my tax workflow, or my secrets convention. I taught it once, in the repo, and both agents read it at startup forever. The cost of teaching the agent is paid once, not per machine and not per session.
Two. Updates are git operations, not deploy operations. Improving a workflow is the same gesture as committing code. Push, pull, done. There is no separate “agent config deployment” step because the symlink collapses deployment into the file system. This means I actually do it, instead of letting drift accumulate.
Three. Diffs are reviewable. Because the agent’s instructions live in git, every change has a commit message, a diff, and a history. When I change how Claude handles secrets, I can read back the commit and remember why. When I want to see what the Studio’s Claude knows, I git log claude/CLAUDE.md. This is the same advantage dotfiles repos give you for shell config, applied to the much more valuable layer above it.
The Onboarding Test
The clearest sign this works is bootstrapping a new Mac. From a clean install:
brew install --cask 1password 1password-cli
brew install claude-code just
npm install -g @openai/codex
mkdir -p ~/Desktop/projects/just-understanding-data
cd ~/Desktop/projects/just-understanding-data
git clone [email protected]:Just-Understanding-Data-Ltd/dotfiles.git
cd dotfiles && ./install.sh
echo 'source ~/.zshrc.shared' >> ~/.zshrc
Eight commands. The new machine now has my aliases, my secrets convention, my Claude brain, my Codex brain, and a working shell. The only remaining step is creating the machine-specific ~/CLAUDE.md with the SSH and hostname bits.
If I sit down at a borrowed Mac, I am productive in under five minutes because the most expensive thing to recreate, my agent’s accumulated context, was never on the old machine in the first place. It was always in the repo.
The Generalisation
Most engineers think of dotfiles as a way to keep their shell tidy. After agents, dotfiles are how you keep your agents tidy. The shell only ever knew about you. The agent knows about you, your projects, your conventions, your tax setup, your secret vaults, your way of writing code. Losing that context is much more expensive than losing a .zshrc.
The pattern is: anything an agent should know across machines, sessions, or projects belongs in version control, symlinked into the location the harness reads from. Everything else is local state.

