Portable, git-backed cross-machine sync for Claude Code per-project memory.
The problem: Claude Code stores memory at
~/.claude/projects/<encoded-absolute-path>/memory/. The encoded path embeds your username, so the same project resolves to a different directory on every machine. You cannot just sync~/.claude/projects/across devices.
This tool: decouples a stable project key from the per-machine encoded path, and symlinks the encoded
memory/directory to a shared, portable git store. Claude keeps writing where it always writes. Your machines stay in sync.
See docs/why.md for the open Claude Code GitHub issues this
addresses, and docs/encoding.md for how the path
encoding actually works.
- A
sync-claude-memoryCLI withsyncanddoctorsubcommands - Two Claude Code hooks: auto-pull on
SessionStart, auto-commit + push onStop - An installer that wires everything up idempotently
Quick install (clones the repo to ~/sync-claude-memory for you):
curl -fsSL https://raw.githubusercontent.com/ryzs/sync-claude-memory/main/install.sh | bashOr do it manually if you want to read the script first:
git clone https://github.com/ryzs/sync-claude-memory.git ~/sync-claude-memory
cd ~/sync-claude-memory
./install.shEither way produces the same result. To update later: cd ~/sync-claude-memory && git pull && ./install.sh.
Override the clone location with SYNC_CLAUDE_MEMORY_DIR=/some/path if you don't want it at ~/sync-claude-memory.
The installer:
- Symlinks
bin/sync-claude-memory->~/.local/bin/sync-claude-memory - Symlinks
hooks/*.sh->~/.claude/hooks/ - Optionally scaffolds an empty
~/claude-memory/git store with a startermemory-map.toml - Prints the JSON snippet to paste into
~/.claude/settings.jsonto wire up the hooks
It does not edit your settings.json - paste the snippet yourself so you can see what changes.
Tip: if any of this is unfamiliar, just hand the install steps to Claude Code (or another AI coding assistant) running in this repo and ask it to walk you through setup. It can clone the repo, run
install.sh, paste the hook snippet into your~/.claude/settings.json, create the private memory store repo viagh, and add your firstmemory-map.tomlentries. Try a prompt like:"Set up sync-claude-memory on this machine. Create a private GitHub repo for my memory store, wire up the hooks in
~/.claude/settings.json, and add entries for the projects under~/code/."
The store is a separate repo you own. Treat it as private - it contains your actual project memory, which often includes client details, internal notes, and other context you do not want public.
# 1. Create a private repo on GitHub (or your host of choice)
gh repo create my-claude-memory --private
# 2. Point the local store at it
cd ~/claude-memory # scaffolded by install.sh
git remote add origin git@github.com:<you>/my-claude-memory.git
# 3. Edit memory-map.toml to list your projects
$EDITOR memory-map.toml
# 4. Run the sync
sync-claude-memorymemory-map.toml looks like:
[my-project]
path = "code/my-project" # relative to $HOME
[work-thing]
path = "work/work-thing"
["dotted.project"] # quote keys with dots
path = "code/dotted.project"With the hooks installed, you do nothing. Each Claude session pulls on start and pushes on stop. To switch machines, just open a session and the latest memory pulls before Claude reads it.
To do it manually:
sync-claude-memory # sync + doctor (default)
sync-claude-memory sync # symlinks only
sync-claude-memory doctor # health-check onlyIf a project lives at different paths on different machines (code/foo
on one, work/foo on another), create memory-map.local.toml beside the
main map. Same TOML shape. Local entries override the main map for that
machine only. The file is gitignored, so it never spreads.
# ~/claude-memory/memory-map.local.toml (this machine only)
[my-project]
path = "work/my-project"A cleaner pattern: keep paths uniform across machines so you never need an override. Worth doing if you can.
# 1. Open Claude Code in the project once (creates ~/.claude/projects/<encoded>/)
cd ~/code/my-new-thing && claude
# 2. Add an entry to the map
$EDITOR ~/claude-memory/memory-map.toml
# add:
# [my-new-thing]
# path = "code/my-new-thing"
# 3. Sync (migrates existing memory into the store, then symlinks)
sync-claude-memory
# 4. Commit
cd ~/claude-memory && git add . && git commit -m "add my-new-thing" && git push| Symptom | Likely cause | Fix |
|---|---|---|
memory store not found |
Store dir missing | Re-run install.sh and accept the scaffold prompt |
store already has content for <key> |
Local memory exists for a project the store also tracks | Inspect both, merge by hand, then re-run |
| Symlink looks correct but Claude isn't reading memory | Project not actually at the mapped path on this machine | Check $HOME/<map.path> matches reality, or add a memory-map.local.toml override |
| New project's memory not syncing | Forgot to add to memory-map.toml |
Add it, run sync |
| Encoded directory has unexpected name | Path contains /, _, or . |
All three encode to -. Verify: printf '%s' "<path>" | tr '/_.' '---' |
claude-mem is an in-session
memory persistence plugin (tree-sitter parsers, MCP). It enriches what
Claude remembers on one machine.
sync-claude-memory is a cross-machine sync layer for the per-project
memory directory. It works alongside claude-mem, or alone with vanilla
Claude Code.
Different problem. Different layer. Complementary.
MIT. See LICENSE.