Logo
Perfect Sync: Claude Code on Every Machine

Perfect Sync: Claude Code on Every Machine

I work on multiple machines. A work MacBook with a company licence, a personal MacBook with my own licence for projects like this blog and jotBook — and sometimes others too. And every time I set up a new one, the same ritual: copying Claude Code skills, commands, hooks, and rules. Tedious and error-prone. I'd come up with a great new workflow idea on my personal machine, then have to manually move it to my work laptop. Something always fell out of sync eventually.

So I built a simple solution: a Git repo as the single source of truth for my entire Claude Code setup.

The problem

Claude Code stores its configuration in ~/.claude/ — skills, commands, hooks, rules. Works great on one machine. But the moment you have two, you lose track. Did I add that new skill on my work laptop or personal one? Is CLAUDE.md up to date everywhere? Is the hook that fixed that annoying issue on both machines?

Over time my work and personal environments started to diverge. Things worked on one machine that didn't exist on the other. I stopped knowing which version of the config was "the good one".

At the same time, I didn't want work context leaking into personal projects. Skills built for company-specific needs shouldn't land on my personal MacBook — and vice versa. I needed control over what I sync and where.

The solution: a Git repo as the source of truth

I built a repo whose structure mirrors what Claude Code expects:

claude-workflows/
  commands/       # slash commands (.md files)
  skills/         # skill directories
  hooks/          # hook scripts
  rules/          # shared rules (security, voice profile, etc.)
  scripts/
    sync.sh       # the sync script
    build.sh      # builds CLAUDE.md from rules/

The key insight: symlinks, not copies. The sync script creates symlinks from ~/.claude/ pointing back into the repo. Edit a file in the repo and Claude sees the change immediately — no re-sync, no copying.

I also added a pre-push hook that checks whether any committed file contains work-specific context — project names, internal addresses, company-specific rules. If anything matches, the push is blocked. That keeps the repo genuinely generic and safe to host on GitHub.

# What sync.sh does (simplified)
git pull --ff-only
ln -s "$REPO/skills/brain-dump" "$HOME/.claude/skills/brain-dump"
ln -s "$REPO/commands/presentation.md" "$HOME/.claude/commands/presentation.md"
# ... and so on for hooks and rules

New machine? Clone the repo, run sync.sh, done. The whole environment is linked and up to date in a few minutes.

How it works in practice

I don't think about it day to day, but when I add something new on one machine, the flow looks like this:

  1. Create or modify a skill or command locally in the repo.
  2. It's active immediately — the symlink means Claude sees the change straight away.
  3. When I'm happy with the result, I push to remote.
  4. On another machine: ./scripts/sync.sh — pull and re-link.

I also added a rule to CLAUDE.md: whenever Claude creates or modifies a skill or command during a session, it asks me if I want to add it to the repo. That's important — without it, it's easy to forget a good idea that came up "along the way" during some other task.

What I'd improve

It's one-directional for now — repo to ~/.claude/. If I create something directly in ~/.claude/ on one machine (which rarely happens), I need to manually move it to the repo. Two-way sync would be more convenient, but in practice the "ask me to add it" rule covers the vast majority of cases. And as a bonus — that one-directionality gives me exactly the control I wanted: no work skill ends up in the repo unless I deliberately put it there.

I'm also thinking about adding a simple script that checks whether ~/.claude/ has any files without a counterpart in the repo — a kind of audit, so nothing slips through.

If you're using Claude Code on more than one machine, I really recommend setting something like this up. The setup is one-time, and since then I haven't had to manually copy anything.