July 28, 2025
Claude Code Memory Guide 2025: The Four-Layer Memory Stack
A complete beginner's guide to making Claude Code remember everything across sessions using CLAUDE.md, primer.md, memory.sh, and git hooks - four simple layers that solve the context persistence problem.
The Problem
Claude Code has no memory between sessions by default. Every new session starts fresh, losing context about your rules, preferences, project history, and next steps. The solution is a four-layer memory system using text files and a shell script.
---
The Four Layers
Layer 1 - CLAUDE.md (Static)
Your permanent rules, code style preferences, and project conventions written once.
Layer 2 - primer.md (Dynamic)
The current state of your project that Claude rewrites at the end of every session.
Layer 3 - memory.sh (Live)
A shell script run at session start that pulls fresh data from git and injects context.
Layer 4 - Git Hook (Automatic)
A post-commit hook that logs every commit to a memory file with zero manual effort.
---
Layer 1: CLAUDE.md - Your Permanent Rulebook
Claude Code automatically reads this file before every session. It's your "briefing document" describing who you are, how you work, and your project details.
What to Include
- Project Identity - Description, purpose, tech stack, users
- Coding Conventions - Tabs vs spaces, quote style, language preferences
- Architectural Decisions - Key structural choices and rationale
- Things Claude Must Never Do - Explicit restrictions
- Primer Rewrite Instruction - Critical instruction to update primer.md every session
Example CLAUDE.md
# Project: My SaaS App
## What This Project Is
A subscription-based task manager for small teams.
Backend: Node.js + Express. Database: PostgreSQL.
Frontend: React + TypeScript. Auth: JWT tokens.
Deployed on: Railway (backend), Vercel (frontend).
## Code Conventions
- Always use TypeScript
- Use single quotes for strings
- 2-space indentation
- All async functions must have try/catch
- Use named exports, not default exports
## Architecture Rules
- All DB queries go through /src/db/queries/
- All API calls from frontend go through /src/lib/api.ts
- Environment variables accessed only through /src/config.ts
## Never Do
- Never use any type in TypeScript
- Never store secrets in code
- Never write inline CSS styles
- Never modify DB schema without discussion
## SESSION MANAGEMENT (CRITICAL)
At the end of EVERY session, you must rewrite primer.md completely.
Include:
1. Current state of the project
2. What was accomplished this session
3. Immediate next steps (specific, actionable)
4. Any open blockers or unresolved issues
5. Any important decisions made this session
---
Layer 2: primer.md - The Living State Document
This is the most powerful layer. Claude reads primer.md at session start alongside CLAUDE.md, then completely rewrites it at session end. This ensures the next session starts with full context.
What a Good primer.md Contains
# Project State - Updated: 2025-06-14
## Current Status
Working on the team invitation flow. The invite email
sending is complete and tested. The invite acceptance
page (/accept-invite) is 70% done - form renders
but the token validation endpoint is not yet wired up.
## What Was Done This Session
- Built POST /api/invites endpoint (creates invite + sends email)
- Created InviteEmail template in /src/emails/InviteEmail.tsx
- Added invite record to the DB (new table: team_invites)
- Wrote unit tests for the invite creation logic (all passing)
## Immediate Next Steps
1. Build POST /api/invites/accept endpoint
2. Wire up the AcceptInvitePage form to call endpoint
3. Handle expired token case (tokens expire after 48 hours)
4. Add invite list to team settings page
## Open Blockers
- Resend (email provider) has a sending limit of 100/day
- AcceptInvitePage redirect logic incomplete for new users
## Key Decisions Made
- Invite tokens are UUIDs stored hashed in the DB
- Invites expire after 48 hours (configurable)
- A user can be re-invited after expiry
Initial primer.md
For your first session, create a simple starter:
# Project State
Project is in early development.
No sessions completed yet.
Starting fresh - see CLAUDE.md for project overview.
---
Layer 3: memory.sh - Live Git Context at Launch
A shell script that queries git history, finds recent changes, checks for errors, and injects all this as context at session start.
The Script
#!/bin/bash
# memory.sh - Inject live git context into Claude Code
echo "=== CLAUDE CODE SESSION CONTEXT ==="
echo ""
# Current git branch
echo "Current branch:"
git branch --show-current
echo ""
# Last 5 commits
echo "Recent commits (last 5):"
git log --oneline -5
echo ""
# Uncommitted changes
echo "Files modified (not yet committed):"
git status --short
echo ""
# Changes in last 24 hours
echo "Files changed in last 24 hours:"
git diff --name-only HEAD@{1.day.ago} HEAD 2>/dev/null || echo "No changes"
echo ""
# Recent errors
echo "Recent errors (last 10 lines of error.log):"
if [ -f "error.log" ]; then
tail -10 error.log
else
echo "No error.log found"
fi
echo ""
echo "=== END OF SESSION CONTEXT ==="
echo "Paste the above output into Claude Code when starting your session."
How to Use
- Make executable (one-time):
chmod +x memory.sh - Run at session start:
bash memory.sh - Copy output and paste into Claude Code before requesting work
---
Layer 4: Git Hook - Automatic Commit Logging
Every commit is automatically logged to project-memory.md with zero manual effort, creating a complete history.
Setting Up the Post-Commit Hook
Create .git/hooks/post-commit:
#!/bin/bash
# Auto-log every commit to project-memory.md
COMMIT_HASH=$(git log -1 --format="%h")
COMMIT_MSG=$(git log -1 --format="%s")
COMMIT_DATE=$(git log -1 --format="%ci")
MEMORY_FILE="project-memory.md"
# Create file with header if it doesn't exist
if [ ! -f "$MEMORY_FILE" ]; then
echo "# Project Memory Log" > "$MEMORY_FILE"
echo "Auto-generated by git post-commit hook." >> "$MEMORY_FILE"
echo "" >> "$MEMORY_FILE"
fi
# Append the new commit
echo "- [$COMMIT_DATE] $COMMIT_HASH: $COMMIT_MSG" >> "$MEMORY_FILE"
Make it executable: chmod +x .git/hooks/post-commit
---
Final Project Structure
your-project/
CLAUDE.md Layer 1: permanent rules
primer.md Layer 2: current state (Claude rewrites)
memory.sh Layer 3: live context injector
project-memory.md Layer 4: auto commit log
.git/
hooks/
post-commit Layer 4: hook that writes log
src/ your code
---
Session Workflow
Session Start
- Run
bash memory.shand paste output to Claude - Claude automatically reads CLAUDE.md + primer.md
- You have full context - start working
During Session
- Work normally with Claude
- Git hook logs each commit to project-memory.md automatically
Session End
- Tell Claude: "End of session - please rewrite primer.md now"
- Claude rewrites primer.md with current state, next steps, blockers
Next Session
- Run
bash memory.sh, paste output - Claude picks up exactly where you left off
---
Quick Reference
- CLAUDE.md - Static, you write once. Rules, conventions, preferences.
- primer.md - Dynamic, Claude rewrites every session. Current state, next steps, blockers.
- memory.sh - Live, you run at session start. Git branch, recent commits, changed files.
- project-memory.md - Automatic, git hook writes every commit. Timestamped commit log.
---
Getting Started
You don't need a perfect CLAUDE.md on day one. Start with your project description and the primer rewrite instruction, then add more rules as you discover what Claude needs to know.
One-Command Install
curl -sL tunerlabs.com/tools/claude-code-memory/install.sh | bash
This creates all four files and the git hook automatically.
Setup Checklist
- Create CLAUDE.md with rules + primer rewrite instruction
- Create initial primer.md (even just a few lines)
- Create memory.sh and run
chmod +x memory.sh - Create .git/hooks/post-commit and run
chmod +x post-commit - Commit all new files to git
- At end of first session, ask Claude to rewrite primer.md
Written by Shyam Achuthan