Module 3: CLAUDE.md and the memory system
Auto Memory and Settings: What Claude Learns and How You Configure It
Auto Memory and Settings: What Claude Learns and How You Configure It
Overview
So far we've covered explicit memory — what you write in CLAUDE.md. But Claude Code has another form of memory: auto memory, where Claude learns automatically from your corrections and preferences without you writing anything in a file.
On top of that, Claude Code has a settings system with 3 scopes (global, project, local) that controls permissions, allowed tools, and agent behavior. Settings aren't contextual memory like CLAUDE.md — they're technical configuration that defines what Claude Code can and can't do in your environment.
This capsule covers both systems: how auto memory works, where it's stored, how to manage it, and when it beats CLAUDE.md. Then the settings system: what to configure in each scope, how to do it, and the best practices for teams.
Auto memory: automatic learning
What auto memory is
Auto memory is Claude Code's ability to remember corrections and preferences across sessions without you writing them into CLAUDE.md. When you correct Claude, that correction can be saved as a "memory" that applies in future sessions.
How it works
Session 1:
You: "Create a Button component"
Claude: [creates it with a function declaration]
You: "Don't use function declarations. Use arrow functions
with const. It's the project convention."
Claude: [corrects it, uses an arrow function]
Claude: [internally] → Saves: "This user prefers
arrow functions with const for components"
═══════════════════════════════════════════════
Session 2 (days later):
You: "Create a Card component"
Claude: [uses an arrow function with const automatically]
→ Applies the learned correction without you repeating it
The kinds of things it learns
Auto memory captures correction patterns:
| What you say | What Claude learns |
|---|---|
| "Don't use var, use const" | Preference: const over var |
| "Imports go through the @/ alias" | Convention: import aliases |
| "Always add a return type" | Rule: explicit return types |
| "Tests go in tests/, not tests/" | Structure: test location |
| "Answer in Spanish" | Preference: Spanish language |
| "Don't generate obvious comments" | Style: minimal comments |
Version requirement
Auto memory requires Claude Code v2.1.59 or higher. Check with:
claude --version
If your version is older, update with claude update.
Turning auto memory on/off
Auto memory is on by default. To turn it off:
Option 1 — Interactive toggle:
> /memory
Then use the auto memory toggle in the interface.
Option 2 — Settings:
{
"autoMemoryEnabled": false
}
Option 3 — Environment variable:
export CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
Where auto memories are stored
Auto memories live in a local directory inside ~/.claude/, organized by project:
~/.claude/projects/<project>/memory/
├── MEMORY.md → Concise index (the first 200 lines or 25KB, whichever comes first, load in every session)
├── debugging.md → Detailed notes on debugging patterns
├── api-conventions.md → API design decisions
└── ... → Other topic files Claude creates as it needs them
MEMORY.md acts as the directory's index. Claude reads and writes files in this directory during your session. The topic files load on demand, not at startup.
Auto memory is local to your machine. All worktrees and subdirectories inside the same git repository share one auto memory directory.
How to see your auto memories
Use the /memory command inside a session:
> /memory
/memory lists every CLAUDE.md and rules file loaded in your current session, lets you turn auto memory on and off, and opens a link to the auto memory folder. Select any file to open it in your editor.
When you ask Claude to "remember" something (e.g., "always use pnpm, not npm"), Claude saves it in auto memory. To add instructions to CLAUDE.md instead, ask for it explicitly: "add this to CLAUDE.md."
How to delete or edit auto memories
If Claude learned something incorrect or out of date:
# From the interactive interface
> /memory
# Claude lists the memories
# You can ask it to delete a specific one:
> "Delete the memory about using tabs instead of spaces.
We changed that convention."
You can also edit the files in .claude/ directly if you prefer manual control.
When auto memory helps vs when CLAUDE.md is better
Auto memory is better when:
1. Personal preferences you discover while working:
You: "I don't like long answers. Be more concise."
→ Auto memory: perfect. It's a personal preference Claude
learns and applies in future sessions.
→ CLAUDE.md: doesn't fit. It's your preference, not the project's.
2. Minor corrections that don't deserve a formal rule:
You: "I prefer if/else over ternaries for complex conditions"
→ Auto memory: captures the preference with no bureaucracy.
→ CLAUDE.md: too granular for a formal rule.
3. Habits that emerge with use:
After 5 corrections, Claude notices:
- This user always wants tests after implementing
- This user prefers async/await over .then()
- This user wants detailed logs in development
→ These patterns get internalized as auto memory.
CLAUDE.md is better when:
1. Project rules the whole team must follow:
"Don't use any in TypeScript" → CLAUDE.md
Because it applies to the whole team, not just you.
Auto memory is personal, CLAUDE.md is shared.
2. Factual information about the project:
"Stack: FastAPI, PostgreSQL, Alembic" → CLAUDE.md
Because it's a fact, not a preference.
Auto memory is for corrections, not for facts.
3. Conventions you need to document:
"Files in kebab-case, classes in PascalCase" → CLAUDE.md
Because you need it explicit and visible to the team.
Auto memory is implicit — nobody else sees it.
Comparison table
| Aspect | Auto Memory | CLAUDE.md |
|---|---|---|
| Who creates it | Claude, automatically | You, manually |
| Visibility | Only you | The whole team (if committed) |
| Persistence | Across sessions | Permanent |
| Scope | Personal | Project or directory |
| Maintenance | Automatic (but it can pile up) | Manual (you update it) |
| Precision | Variable (Claude interprets) | Exact (you write it) |
| Ideal for | Personal preferences | Project rules |
Best practices for auto memory
Let Claude learn naturally
Don't try to "program" auto memory. Just work with Claude Code, correct it when needed, and let the corrections accumulate.
# GOOD — a natural correction:
You: "Use const instead of let here, the variable isn't reassigned."
# BAD — forcing a "memory":
You: "Remember permanently: always use const over let
when the variable isn't reassigned. Save it."
Review it periodically
Every few weeks, review the auto memories that have accumulated:
> /memory
Delete the ones that no longer apply (maybe you changed a convention) and check that there are no contradictions.
If something matters, put it in CLAUDE.md
If a correction you made really matters to the project, don't rely on auto memory alone — add it to CLAUDE.md:
You: "Never use console.log in production. Use the logger."
→ Good: Claude remembers it as auto memory.
→ Better: Add "Don't use console.log — use loguru" to CLAUDE.md
→ Best: Both. Auto memory for you, CLAUDE.md for the team.
Watch out for contradictory memories
If you said "use tabs" in January and "use spaces" in February, both corrections can coexist as auto memories. Claude has to resolve the contradiction — and it may pick wrong.
Fix: review and clean up memories whenever you change a convention.
The settings system
What settings are
Claude Code's settings are JSON files that configure the agent's technical behavior: which tools it can use, which permissions it has, and how it operates. They aren't context about your project — they're configuration for the agent itself.
The 3 scopes
┌────────────────────────────────────────────────┐
│ GLOBAL (~/.claude/settings.json) │
│ Applies to ALL your projects │
│ Personal, not shared │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ PROJECT (.claude/settings.json) │ │
│ │ Applies only to THIS project │ │
│ │ Committed to the repo (shared) │ │
│ │ │ │
│ │ ┌────────────────────────────────┐ │ │
│ │ │ LOCAL │ │ │
│ │ │ (.claude/settings.local.json) │ │ │
│ │ │ Your personal overrides │ │ │
│ │ │ NOT committed │ │ │
│ │ └────────────────────────────────┘ │ │
│ └────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘
Precedence: Local > Project > Global
Scope 1: Global settings
Location
~/.claude/settings.json
What to configure here
Preferences that apply to all your projects, on all your machines:
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"LS"
],
"deny": []
},
"preferences": {
"verbose": false,
"theme": "dark"
}
}
When to use global settings
- Tools you always want to allow (read, grep, ls)
- UI preferences that apply everywhere
- Behavior defaults you like
What NOT to put here
- Tools specific to one project (like a particular linter)
- Permissions that only apply to certain projects
- Configuration you don't want on all your machines
Scope 2: Project settings
Location
my-project/.claude/settings.json
What to configure here
Configuration shared with the team. It gets committed to git:
{
"permissions": {
"allow": [
"Read",
"Write",
"Glob",
"Grep",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npx prisma migrate dev)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(npm publish)"
]
}
}
allowedTools: pre-approving tools
The most important setting in project settings. It defines which commands Claude can run without asking you for confirmation:
{
"permissions": {
"allow": [
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run format)",
"Bash(npx prisma generate)"
]
}
}
With this configuration, when Claude wants to run npm test, it just runs it without asking "Allow? [y/n]". This speeds up the workflow significantly.
denyTools: blocking dangerous tools
{
"permissions": {
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)",
"Bash(npm publish)",
"Bash(docker rm)"
]
}
}
These commands will never run, not even if Claude or you ask for them. It's a safety net.
When to use project settings
- Tools the whole team should pre-approve (test, lint)
- Tools nobody should be able to run by accident (rm -rf, force push)
- Configuration that must be consistent across every developer on the team
Committing it to the repo
Project settings get committed so the whole team shares them:
git add .claude/settings.json
git commit -m "chore: configure claude code project settings"
Scope 3: Local settings
Location
my-project/.claude/settings.local.json
What to configure here
Your personal overrides, the ones you do NOT share with the team:
{
"permissions": {
"allow": [
"Bash(docker compose up -d)",
"Bash(psql)"
]
},
"preferences": {
"verbose": true
}
}
When to use local settings
- Tools only you use (maybe you have Docker and your teammate doesn't)
- Extra permissions for your local environment
- Overrides for personal preferences
.gitignore
Local settings should not be committed:
echo ".claude/settings.local.json" >> .gitignore
Comparisons and decisions
Auto memory vs CLAUDE.md vs Settings
┌─────────────────────────────────────────────────────────┐
│ │
│ Auto Memory CLAUDE.md Settings │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ LEARNS │ │ CONTEXT │ │ CONFIGURES │ │
│ │ │ │ │ │ │ │
│ │ "Don't use │ │ "Stack: │ │ "Allow │ │
│ │ var" │ │ FastAPI" │ │ npm test" │ │
│ │ │ │ │ │ │ │
│ │ Implicit │ │ Explicit │ │ Technical │ │
│ │ Personal │ │ Project │ │ Permissions│ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
│ "What "What "What │
│ Claude you define Claude CAN │
│ learns from about the or CANNOT │
│ corrections" project" do" │
│ │
└─────────────────────────────────────────────────────────┘
| Aspect | Auto Memory | CLAUDE.md | Settings |
|---|---|---|---|
| Purpose | Learn preferences | Provide context | Configure permissions |
| Who creates it | Claude | You | You |
| Format | Internal | Markdown | JSON |
| Shared | No (personal) | Yes (git) | Depends on the scope |
| Kind of info | Corrections, preferences | Stack, conventions, rules | Tools, permissions |
| Example | "Prefers const" | "Stack: FastAPI" | "allow: npm test" |
When to use each one
| You need to... | Use |
|---|---|
| Have Claude remember a correction | Auto memory (it happens on its own) |
| Define the project rules | CLAUDE.md |
| Pre-approve tools | Settings (project) |
| Set your personal style preference | Auto memory or CLAUDE.local.md |
| Block dangerous commands | Settings (project deny) |
| Share context with the team | CLAUDE.md + project settings |
| Keep your private configuration | Local settings + CLAUDE.local.md |
Common patterns
Pattern 1: "Standardized team settings"
Every project on the team shares consistent settings:
{
"permissions": {
"allow": [
"Read",
"Write",
"Glob",
"Grep",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run format)",
"Bash(npm run build)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)",
"Bash(npm publish)"
]
}
}
Pattern 2: "Auto memory + CLAUDE.md = intentional redundancy"
The corrections that matter go in both places:
Session: "Don't use console.log, use the logger"
→ Claude saves it as auto memory
→ You add it to CLAUDE.md
Redundant? Yes. Bad? No.
- Auto memory: covers you if you forget to update CLAUDE.md
- CLAUDE.md: covers the team, who don't have your auto memory
Pattern 3: "Local settings for development"
Your local settings add permissions for your development environment:
{
"permissions": {
"allow": [
"Bash(docker compose up -d)",
"Bash(docker compose down)",
"Bash(psql -h localhost)",
"Bash(redis-cli)"
]
}
}
Your teammate who uses SQLite doesn't need Docker permissions.
Pattern 4: "Periodic auto memory cleanup"
Every month, review and clean up:
> /memory
Claude: "Current memories:
1. Prefers arrow functions for components
2. Uses tabs with 2 spaces [NOTE: you switched to 4 two weeks ago]
3. Don't use moment.js, use date-fns
4. Tests with the describe/it pattern
5. Answers in Spanish"
You: "Delete #2, I use 4 spaces now. And update #1:
I don't use arrow functions for components anymore, I use function
declarations (we changed the convention)."
Pattern 5: "Progressive settings"
Start permissive, restrict as you go:
// Week 1 — just the basics
{
"permissions": {
"allow": ["Read", "Write", "Glob", "Grep"]
}
}
// Week 2 — you add safe commands
{
"permissions": {
"allow": [
"Read", "Write", "Glob", "Grep",
"Bash(npm test)",
"Bash(npm run lint)"
]
}
}
// Week 3 — you block the dangerous ones
{
"permissions": {
"allow": [...],
"deny": ["Bash(rm -rf *)", "Bash(git push --force)"]
}
}
Pitfalls and edge cases
Pitfall 1: Relying on auto memory alone for critical rules
# Dangerous:
You tell Claude "never modify the payments table directly"
Claude saves it as auto memory
Your teammate uses Claude Code → doesn't have that memory → breaks payments
# Fix: critical rules go in CLAUDE.md (shared)
Pitfall 2: Contradictory auto memories piling up
January: "Use Express for APIs"
March: "Use Fastify instead of Express"
June: "Let's try Hono"
→ Three auto memories about frameworks that contradict each other
→ Claude can get confused about which one to follow
Fix: clean up memories when you change stack or convention
Pitfall 3: Settings that are too permissive
{
"permissions": {
"allow": ["Bash(*)"]
}
}
This lets Claude run ANY command without asking for confirmation. Including rm -rf /, git push --force, npm publish. Don't do this.
Pitfall 4: Forgetting the .gitignore for settings.local.json
# If you commit settings.local.json:
# - Your teammate inherits your personal permissions
# - It can cause conflicts if you have different environments
# - It defeats the whole point of "local"
# Fix:
echo ".claude/settings.local.json" >> .gitignore
Pitfall 5: Not using settings when you should
# If Claude asks you every single time:
"Claude wants to run: npm test. Allow? [y/n]"
"Claude wants to run: npm test. Allow? [y/n]"
"Claude wants to run: npm test. Allow? [y/n]"
# ...and you always say "y", you should add it to settings:
{
"permissions": {
"allow": ["Bash(npm test)"]
}
}
# Now npm test runs without confirmation
Edge case: auto memory vs CLAUDE.md in conflict
CLAUDE.md: "Use tabs for indentation"
Auto memory: "This user prefers spaces"
Which one wins?
It depends on the implementation, but generally:
- CLAUDE.md carries more weight as an explicit source
- But if the correction was recent, it can prevail
Fix: when you spot a conflict, resolve one of them:
- Update CLAUDE.md so it matches your preference
- Or delete the auto memory that contradicts CLAUDE.md
Complete worked example
Scenario: configuring a full project with all 3 systems
A FastAPI project with a team of 3 developers. The complete setup:
1. CLAUDE.md (project context):
# Invoice API
Electronic invoicing API. Python 3.12, FastAPI, PostgreSQL.
## Stack
- FastAPI 0.109, SQLAlchemy 2.0, Alembic
- Pytest + httpx, Pydantic v2
- Redis for cache, Celery for background tasks
## Conventions
- snake_case, type hints required
- Schemas: InvoiceCreate, InvoiceUpdate, InvoiceResponse
- Don't use print(), use loguru
## Commands
- Dev: `uvicorn src.main:app --reload`
- Test: `pytest -v`
- Lint: `ruff check src/`
- Migrate: `alembic upgrade head`
## Rules
- Do NOT modify alembic/versions/ by hand
- Do NOT write raw SQL queries (use SQLAlchemy)
- Run the tests after changes in services/
2. Project settings (.claude/settings.json):
{
"permissions": {
"allow": [
"Read",
"Write",
"Glob",
"Grep",
"Bash(pytest -v)",
"Bash(pytest)",
"Bash(ruff check src/)",
"Bash(ruff format src/)",
"Bash(alembic upgrade head)",
"Bash(uvicorn src.main:app --reload)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)",
"Bash(pip install * --break-system-packages)",
"Bash(alembic downgrade *)"
]
}
}
3. Local settings (.claude/settings.local.json) — just for you:
{
"permissions": {
"allow": [
"Bash(docker compose up -d)",
"Bash(docker compose down)",
"Bash(pgcli)",
"Bash(redis-cli)"
]
}
}
4. Auto memory (accumulates with use):
After 2 weeks of use, Claude has learned:
- You prefer to see verbose test output
- You don't want long explanations, just the code
- You always want the Pydantic schema before the endpoint
- You prefer async generators over lists for large responses
Result: A complete system where:
- CLAUDE.md provides the project context (shared)
- Project settings define the team's permissions (shared)
- Local settings add your tools (personal)
- Auto memory refines the experience over time (personal)
Practice exercises
Exercise 1: Generate auto memories on purpose
Open Claude Code and work on a task. During the session, make 3 explicit corrections:
- Correct a naming convention
- Correct a style preference (e.g., "don't use complex ternaries")
- Correct an output preference (e.g., "be more concise")
In a later session, check whether Claude applies the corrections automatically.
What to watch for
If auto memory is working correctly:
- Claude should apply all 3 corrections without you repeating them
- You can confirm with
/memorythat they were saved - If one didn't get saved, the correction may not have been clear enough
Tip: the clearest, most direct corrections get saved best:
- ✅ "Don't use var, always const or let"
- ❌ "It might be better if you used something other than var"
Exercise 2: Configure project settings
Create .claude/settings.json in your project with:
- 4-5 pre-approved commands (test, lint, build)
- 2-3 blocked commands (rm -rf, force push)
Check that it works: ask Claude to run npm test and see whether it asks for confirmation.
Template
{
"permissions": {
"allow": [
"Read",
"Write",
"Glob",
"Grep",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run build)",
"Bash(npx prisma generate)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)",
"Bash(npm publish)"
]
}
}
After creating the file:
- Start a new Claude Code session
- Ask: "Run the tests"
- If it doesn't ask for confirmation → the settings work
- If it asks for confirmation → check the file is at the right path
Exercise 3: Compare auto memory vs CLAUDE.md
- Start a new session
- Make a correction to Claude (e.g., "don't use semicolons")
- In the next session, check whether it remembers (auto memory)
- Add the same rule to CLAUDE.md
- Delete the auto memory
- Verify the rule still applies (now from CLAUDE.md)
This shows that both sources produce the same result, but CLAUDE.md is visible and shareable.
What to watch for
- Auto memory: it works, but it's invisible to the team
- CLAUDE.md: it works, and any developer can see it
- Best practice: both, for the rules that matter
- Auto memory alone: for minor personal preferences
If the rule matters to the project, CLAUDE.md is the right source. If it's a preference of yours, auto memory is enough.
Exercise 4: Configure all 3 settings scopes
Create the 3 settings files:
- Global (
~/.claude/settings.json): Basic permissions you want in every project - Project (
.claude/settings.json): Permissions specific to this project - Local (
.claude/settings.local.json): Your personal overrides
Verify the local override works: add a permission in local that isn't in project.
Step-by-step guide
# 1. Global settings
mkdir -p ~/.claude
cat > ~/.claude/settings.json << 'EOF'
{
"permissions": {
"allow": ["Read", "Glob", "Grep"]
}
}
EOF
# 2. Project settings
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
"permissions": {
"allow": [
"Bash(npm test)",
"Bash(npm run lint)"
]
}
}
EOF
# 3. Local settings
cat > .claude/settings.local.json << 'EOF'
{
"permissions": {
"allow": [
"Bash(docker compose up -d)"
]
}
}
EOF
# 4. Add local to .gitignore
echo ".claude/settings.local.json" >> .gitignore
Verify:
npm testruns without confirmation (project)docker compose upruns without confirmation (local)rm -rf somethingasks for confirmation (it's not in allow)
Exercise 5: Auto memory cleanup
Review your current auto memories and clean house:
> /memory
- Identify memories that no longer apply
- Identify memories that contradict CLAUDE.md
- Delete the obsolete ones
- Migrate the important ones to CLAUDE.md if they aren't there yet
Cleanup checklist
For each auto memory, ask yourself:
- Is it still valid? (Did you change the convention?)
- Does it contradict something in CLAUDE.md? (If so, one of the two has to change)
- Is it important enough that it should live in CLAUDE.md? (If so, add it)
- Is it a minor personal preference? (If so, leave it as auto memory)
- Does it only apply to an old project? (If so, delete it)
Summary
- Auto memory is implicit learning: Claude remembers corrections and preferences across sessions.
- It kicks in naturally when you correct Claude. You don't have to force it.
- Auto memory is personal, it isn't shared. For team rules, use CLAUDE.md.
- Review it periodically with
/memoryand clean out obsolete or contradictory memories. - Settings have 3 scopes: Global (all your machines), Project (shared with the team), Local (just you).
- allowedTools pre-approves commands to speed up the workflow.
- denyTools blocks dangerous commands as a safety net.
- Project settings get committed to git. Local settings go in
.gitignore. - Auto memory + CLAUDE.md + Settings = a complete memory and configuration system.
- When something matters: put it in CLAUDE.md (explicit, shared). When it's personal: let auto memory handle it, or use CLAUDE.local.md.
Next capsule: 05 - CLAUDE.local.md — personal preferences you don't share with the team.
Additional resources
- Claude Code Memory — Anthropic Docs — Official documentation on auto memory and CLAUDE.md
- Claude Code Settings — Scope configuration: global, project, local
- Claude Code Best Practices — Recommendations for managing auto memory and permissions
- Claude Code CLI Reference — Commands for managing memory and settings
- Claude Code Interactive Mode — Permission management in interactive sessions
- Claude Code Overview — Claude Code's general architecture