The Starting Problem
Every conversation with a coding assistant starts from scratch. Users repeat the same context over and over: their role, coding preferences, recent project decisions. This information can’t be inferred from the repository, but without it, the AI can’t provide truly coherent assistance.
Claude Code’s Memory system solves this with a persistent, structured knowledge base. It’s not a chat log archive. It’s a knowledge management system with explicit types, automatic extraction, and intelligent retrieval.
Four Memory Types
Memory categorizes knowledge into four types, each with clear boundaries and purposes:
| Type | Records | Example |
|---|---|---|
| User (persona) | Role, goals, skill level, preferences | “User is a data scientist focused on logging systems” |
| Feedback (behavioral) | Corrections or approvals of Claude’s approach | “Use real databases for integration tests, no mocks” |
| Project (dynamics) | Who’s doing what, why, deadlines | “Merge freeze from 3/5, mobile team releasing” |
| Reference (external) | Pointers to external systems: dashboards, tickets, Slack channels | “Pipeline bug tracked in Linear INGEST project” |
These four types cover essentially everything a human needs to transfer to an AI for coding work. The persona shapes how the model interacts, feedback constrains technical decisions, project dynamics provide temporal context, and external references bridge internal toolchains.
What’s Not Stored
Memory has a clear filtering principle: only record what can’t be inferred from the repository.
| Remember | Don’t Remember |
|---|---|
| You’re a data scientist focused on logging | Code architecture, file structure |
| “Don’t mock the database” | Git history, who changed what |
| Non-critical merges freeze after Thursday | Existing CLAUDE.md content |
| Bug tracking is in Linear’s INGEST project | Debug solutions (the fix is already in code) |
If the code already says it, Memory doesn’t need to repeat it. Git history, file structure, dependencies — these are all context the repository already carries. Memory only fills the gaps code can’t cover.
Storage Format
Memory files live in ~/.claude/projects/{project-path-hash}/memory/, using YAML frontmatter + Markdown format:
1 | --- |
This format requires every memory to include three elements: what it is, why it matters, how to use it. The “Why” and “How to apply” fields ensure memories aren’t isolated fact records — they’re knowledge units with actionable guidance.
MEMORY.md Index
A MEMORY.md file in the directory acts as an index, always loaded into context:
1 | # Memory Index |
This index file has hard limits: 200 lines or 25KB max, beyond which it gets truncated. This design forces the index to stay concise, with detailed content loaded on-demand through intelligent retrieval.
Auto-Extraction Mechanism
Memory doesn’t require manual maintenance. The system automatically checks whether to extract new memories each time the model completes a response (without tool_use).
The extraction process goes through four gates: confirming it’s the main agent executing, auto-memory is enabled, frequency control (default: once per turn), and mutual exclusion check (skip if the main agent already wrote memories in the current turn). After passing the gates, the system launches a forked agent to perform extraction. This forked agent shares the parent session’s prompt cache, executes at most 5 turns, and has strictly limited tool permissions.
1 | function createAutoMemCanUseTool(memoryDir: string): CanUseToolFn { |
This permission design has clear security intent: the extraction agent can only read project files and write to the memory directory. It can’t modify project code, call external services, or spawn sub-agents. Even if extraction goes wrong, the impact is contained within the memory directory.
The mutual exclusion mechanism prevents duplicate saves. If the main agent already operated on the memory directory via Write/Edit in the current turn, auto-extraction skips, avoiding the same information being recorded twice.
1 | function hasMemoryWritesSince(messages: Message[], sinceUuid: string): boolean { |
Intelligent Retrieval and Freshness Management
Memory isn’t loaded into context in full. The system uses a Sonnet model as a selector, dynamically filtering the most relevant memories for each user query.
1 | async function findRelevantMemories( |
The selector picks at most 5 memories, and doesn’t select if uncertain whether they’re useful. This design controls the extra token overhead while keeping irrelevant memories from interfering with the model.
Memories have a shelf life. A “merge freeze” recorded three months ago was likely lifted long ago, and project decisions from half a year back may have been overturned. The system attaches freshness warnings to old memories:
1 | function memoryFreshnessText(mtimeMs: number): string { |
Memories older than a day get flagged with a timeliness reminder, requiring the model to verify current code state before citing them. This solves the core tension in any memory system: keep information across sessions without letting stale data mislead current decisions.
Team Synchronization
Team members’ memories can be synced and shared via API:
1 | GET /api/claude_code/team_memory?repo={owner/repo} ← Pull |
Sync uses server-first semantics: Pull overwrites local content with server content, Push only uploads incremental content with different hashes. Local deletions don’t delete remote records — they’ll be restored on the next Pull. Conflicts trigger retries via 412 status codes, up to 2 attempts.
1 | async function pushTeamMemory(state): Promise<PushResult> { |
For security, single files are capped at 250KB, upload bodies at 200KB, and gitleaks rules scan for credentials. If keys or passwords are detected, the file is skipped to prevent sensitive information from leaking into the team shared space.
AutoDream: Background Memory Consolidation
As conversations accumulate, the Memory directory grows. AutoDream is a background task that periodically consolidates, deduplicates, and prunes memories.
Trigger conditions have four gates: at least 24 hours since last consolidation, at least 5 sessions in between, no other process currently consolidating, and a 10-minute scan throttle.
1 | async function shouldTriggerAutoDream(): Promise<boolean> { |
The consolidation process has four phases: Orientation (determine sessions to review), Collection (extract candidate memories from sessions), Consolidation (merge, deduplicate, update memory files), and Pruning (delete outdated or duplicate memories). This flow mirrors human memory consolidation during sleep, transforming fragmented short-term memories into structured long-term knowledge.
Key Source Files
| File | Responsibility |
|---|---|
src/memdir/paths.ts |
Path resolution, priority chain |
src/memdir/memdir.ts |
Prompt construction, MEMORY.md truncation |
src/memdir/memoryScan.ts |
Directory scanning, frontmatter parsing |
src/memdir/memoryTypes.ts |
Four memory type definitions |
src/memdir/findRelevantMemories.ts |
Sonnet intelligent retrieval |
src/services/extractMemories/ |
Auto-extraction service |
src/services/teamMemorySync/ |
Team memory sync |
src/services/autoDream/ |
AutoDream background consolidation |
src/utils/frontmatterParser.ts |
YAML frontmatter parsing |
Series Navigation: