Sync your Claude Code auto-memory across multiple machines with a private git repo and a single SessionStart hook.
Claude Code stores its persistent memory in ~/.claude/projects/<project>/memory/ per machine. If you run Claude Code on a laptop and a desktop, each one builds up its own independent memory, and rules you teach Claude on one machine never reach the other.
- Put the memory directory under a private git repo on GitHub (or any git host).
- Clone it on every machine.
- Add one SessionStart hook that does an
ff-onlypull at the start of every Claude Code session.
That is it. After the one-time setup, every machine pulls the latest memory automatically.
| File | Purpose |
|---|---|
hooks/memory_autopull.sh |
The SessionStart hook (drop into ~/.claude/hooks/) |
examples/settings.json.snippet |
JSON fragment to register the hook in ~/.claude/settings.json |
.gitattributes |
LF normalization for your private memory repo |
On GitHub (or your git host of choice), create a new private repo, for example your-username/claude-memory.
cd ~/.claude/projects/-Users-$USER/memory
git init -b main
git remote add origin git@github.com:your-username/claude-memory.git
cp /path/to/this-repo/.gitattributes .
git add .
git commit -m "initial memory snapshot"
git push -u origin mainmkdir -p ~/.claude/projects/-Users-$USER
cd ~/.claude/projects/-Users-$USER
# If a memory/ already exists, back it up first:
# tar czf ~/memory-pre-clone-$(date +%Y%m%d).tgz memory/
# mv memory ~/.Trash/memory-pre-clone-$(date +%Y%m%d)
git clone git@github.com:your-username/claude-memory.git memoryIf your home username differs across machines (e.g.
aliceon the laptop,alice2on the desktop), the project dir name also differs (-Users-alicevs-Users-alice2). The git content is identical, so that is fine - Claude Code keys memory by cwd-derived project name, but the file content stays in sync.
mkdir -p ~/.claude/hooks
cp hooks/memory_autopull.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/memory_autopull.shAdd this entry to the SessionStart hooks block (see examples/settings.json.snippet):
{
"type": "command",
"command": "~/.claude/hooks/memory_autopull.sh",
"timeout": 15
}Done. Open a new Claude Code session and the hook fires automatically.
The hook is intentionally conservative:
ff-onlypull - never rewrites local history, never overwrites uncommitted edits.- Dirty working tree -> skip - if you have uncommitted memory changes, the hook does not touch them. Commit (or stash), and the next session pulls.
- Atomic mkdir lock - prevents races if two Claude sessions, or a Claude session and a background script, both try to git-op simultaneously. No
flockdependency (macOS does not ship it). - Visible failures - every fetch/pull failure lands in
~/.claude/logs/memory_sync.logand prints a warning to stderr. The hook intentionally does not use|| trueto swallow errors; silent divergence is the failure mode you want to catch early. - Always exits 0 - even on failure, the hook never blocks session start.
The hook only pulls. You commit and push manually (or with a separate git_autopull.sh PreToolUse hook that rebases before git push):
cd ~/.claude/projects/-Users-$USER/memory
git add -A && git commit -m "your message" && git pushThe next session on your other machines will auto-pull the change.
| Symptom | Fix |
|---|---|
| Hook seems to do nothing | cat ~/.claude/logs/memory_sync.log - check for FETCH FAILED or non-FF errors |
fatal: could not read Username for 'https://github.com' |
Switch remote to SSH: git remote set-url origin git@github.com:user/claude-memory.git (and make sure your SSH key is registered on GitHub) |
| Lock held forever | rmdir ~/.claude/locks/memory_git.lockd (the hook auto-clears locks older than 5 minutes) |
| Working tree dirty, never pulls | Commit your local changes; the next session will pull |
MIT