
I created a system to manage Claude Code's memory with git, enabling cross-device and cross-project sharing
This page has been translated by machine translation. View original
Introduction
Claude Code has a memory system that can retain information across sessions. This is a mechanism that saves feedback like "use pnpm in this project" or "don't add Co-Authored-By to commits" as Markdown files, and automatically references them in subsequent sessions.
However, this memory is isolated per project. This leads to issues such as repeatedly teaching the same corrections across multiple projects, or starting with zero memory when working on a different machine.
To address this, I created a system to manage global memory in a GitHub repository, enabling sharing across devices and projects.
Prerequisites/Environment
- Claude Code (VS Code extension or CLI)
- GitHub CLI (
gh) installed and authenticated - macOS (should work similarly on Linux)
Basics of Claude Code's Memory System
First, let's understand how Claude Code's memory works.
Memory Storage Location
Project-specific memories are stored at:
~/.claude/projects/<project-path-hash>/memory/
├── MEMORY.md # Index (always loaded into context)
├── feedback_foo.md # Individual memory files
├── user_bar.md
└── fixes_baz.md
Memory Loading Mechanism
MEMORY.md(index) is always loaded at the start of a session- Individual memory files are read on-demand by Claude as needed, based on the description in the index
- This means not everything is loaded at once, but rather through a semi-selective mechanism
Types of Memory
| Type | Purpose | Example |
|---|---|---|
user |
User role/preferences | "Senior engineer, React beginner" |
feedback |
Correction instructions for Claude | "Don't add Co-Authored-By to commits" |
project |
Project-specific context | "Merge freeze starting 3/5" |
reference |
Pointers to external resources | "Bugs managed in LINEAR's INGEST project" |
fixes |
Error patterns and solutions | "ESLint 9 requires flat config" |
Challenge: Memory is Project-Confined
In practical use, I noticed these issues:
- Teaching the same feedback repeatedly — After teaching "don't add Co-Authored-By to commits" in Project A, having to teach it again in Project B
- Cannot share across devices — Memory accumulated on Machine A doesn't exist on Machine B
- Cannot reuse fix patterns — The lesson "next lint is deprecated in Next.js 16" learned in Project A isn't applied in Project B
Solution: 2-Tier Memory + Git Sync
I solved this with the following design.
Architecture
2-Tier Memory Structure
| Tier | Storage Location | Content | Example |
|---|---|---|---|
| Global | ~/.claude/global-memory/ |
User-common settings/feedback | "No Co-Authored-By", "Web search before package changes" |
| Project | .claude/projects/*/memory/ |
Project-specific pitfalls | "backend/data/ is gitignore target" |
Promotion Rules
One-way only: Project → Global
- Discover new pitfall in Project A → Save to Project A's memory
- Same pitfall occurs in Project B → Promotion signal. Move to Global, remove from both projects
- Global memory becomes outdated (framework updates, etc.) → Remove from Global
We don't do the reverse (Global → Project copying) to avoid drift from duplicate management.
Setup Process
1. Create a private GitHub repository
gh repo create claude-memory --private --description "Cross-project Claude Code memory system"
2. Clone locally
# Using gh CLI avoids needing SSH keys (works immediately if gh is authenticated)
gh repo clone <your-username>/claude-memory ~/.claude/global-memory
3. Create global memory files
First, create MEMORY.md (index):
## Feedback
- [feedback_no_coauthor.md](feedback_no_coauthor.md) — No Co-Authored-By in commits unless asked
- [feedback_save_error_learnings.md](feedback_save_error_learnings.md) — Save error fixes to memory proactively
- [feedback_websearch_packages.md](feedback_websearch_packages.md) — Always web search before updating package configs
Each memory file is Markdown with frontmatter:
---
name: no-co-authored-by
description: Do not add Co-Authored-By trailer to commit messages unless explicitly asked
type: feedback
---
Do not add `Co-Authored-By: Claude ...` to commit messages.
The user finds it unnecessary. Only add it if explicitly requested.
4. Add global memory loading instructions to ~/.claude/CLAUDE.md
~/.claude/CLAUDE.md is a global configuration file that Claude Code reads for all projects. Add global memory reference instructions here:
# Global Memory
Cross-project memories are stored in `~/.claude/global-memory/`.
This directory is a git repo synced to GitHub for cross-device access.
At the start of relevant tasks, read `~/.claude/global-memory/MEMORY.md`
for the index, then load specific memory files as needed.
## Memory tiers
- **Global** (`~/.claude/global-memory/`): User preferences, universal feedback,
cross-project fixes (promoted after hitting 2+ projects)
- **Project** (`.claude/projects/*/memory/`): Project-specific pitfalls,
references, goals
Promotion is one-directional: project → global only.
Never copy global memories down to project scope.
## Syncing global memory
Always use HTTPS via `gh` for this private repo (gh is always authenticated).
- Clone: `gh repo clone <your-username>/claude-memory ~/.claude/global-memory`
- Pull: `cd ~/.claude/global-memory && git pull --rebase -q`
- Push: `cd ~/.claude/global-memory && git add -A && git commit -m "update" && git push`
- Only push when the user asks to sync, or when new global memories are added
5. Initial commit & push
cd ~/.claude/global-memory
git add -A
git commit -m "init: global memory with feedback preferences"
git push -u origin main
6. Remove promoted files from project memory
Remove memory files that have been moved to Global from project scope, and update the MEMORY.md index accordingly.
Setup on a new machine
On a new machine, just one command is needed:
gh repo clone <your-username>/claude-memory ~/.claude/global-memory
You'll also need ~/.claude/CLAUDE.md, but a template is included in the README of your global memory repository, so you can copy from there or have Claude regenerate it.
Sync operations
I don't have a strict automatic sync. Sync timing is expected as follows:
- Pull: When starting work on a new device
- Push: When Claude adds a new entry to global memory
Just tell Claude "sync memory" and it will execute pull/push.
Key Design Decisions
Why not copy global memory to all projects?
Duplicate management leads to drift. Having it exist only once in Global, with Claude referring to it, is simpler.
Why are the descriptions in the index (MEMORY.md) important?
Claude Code decides "should I read this memory file?" based on the descriptions in MEMORY.md. Vague descriptions can cause unnecessary memory loading, wasting context space.
- Bad:
fixes.md — Various fixes - Good:
fixes_nextjs16_eslint.md — Next.js 16 + ESLint 9 migration pitfalls
Why not elevate project-specific fixes to Global?
A fix like "next lint is deprecated in Next.js 16" is only relevant to projects using Next.js 16. If placed in Global, it would appear in the index for every project, even Python-only projects.
The rule to promote only after encountering the same issue in 2+ projects serves as an appropriate filter.
Conclusion
- Claude Code's memory system is just Markdown files, so managing with Git is a natural choice
- 2-tier structure (Global + Project) separates common settings from specific pitfalls
- Promotion rules (Project → Global, after appearing in 2+ projects) prevent Global bloat
- Using the
ghCLI enables one-command setup on new machines without SSH keys - Hard automatic synchronization is unnecessary. Manual operation works simply
If you're using Claude Code across multiple projects and devices and find yourself repeatedly teaching the same things, give this approach a try.