Auto Memory, Imports & Path-Scoped Rules
Notes Claude writes and rules that load on demand
- Explain how auto memory works — where it lives, what loads at start, and how it differs from a CLAUDE.md you write by hand
- Toggle, disable, relocate, and seed auto memory using /memory, settings keys, an environment variable, and the # / 'remember this' shortcuts
- Compose instruction files with @-import syntax, including relative resolution, the 4-hop limit, and the secret-import approval gate
- Scope rules to file paths with .claude/rules/ YAML frontmatter so instructions load on demand instead of every session
- Keep monorepos lean and debuggable with claudeMdExcludes, the InstructionsLoaded hook, and per-worktree CLAUDE.local.md
Static CLAUDE.md is only the floor. This lesson covers the three mechanisms that scale memory in real projects: auto memory that Claude maintains itself across sessions, @-imports for sharing and composing instruction files, and path-scoped rules in .claude/rules/ that load only when relevant — keeping large repos organized without drowning every session in context.
- 1Two memory systems: one you write, one Claude writes
- 2Auto memory: notes Claude maintains for itself
- 3Controlling auto memory: toggle, disable, relocate, seed
- 4@-imports: compose and share instruction files
- 5.claude/rules/: instructions that load on demand
- 6Monorepos, exclusions, and debugging what loaded
Two memory systems: one you write, one Claude writes
Every Claude Code session starts with a fresh context window. Two complementary mechanisms carry knowledge across that gap, and the key to using them well is knowing which is which.
- CLAUDE.md files are instructions you write — coding standards, build commands, architecture. Static, version-controlled, deliberate.
- Auto memory is notes Claude writes itself — build commands it discovered, a debugging insight, a preference you corrected. Dynamic, per-repository, accumulated automatically.
Both load at the start of every conversation, and both are delivered as context, not enforced configuration — Claude reads them and tries to follow them, but compliance is advisory. (For a hard guarantee, a PreToolUse hook is the right tool, not memory.)
The mental model for this whole lesson: static CLAUDE.md is the floor, not the ceiling. Beyond it sit three mechanisms that let memory scale to a large project without bloating context — auto memory (Claude maintains it), @-imports (compose and share files), and .claude/rules/ (load instructions only when relevant).
| CLAUDE.md files | Auto memory | |
|---|---|---|
| Who writes it | You | Claude |
| What it holds | Instructions and rules | Learnings and patterns |
| Scope | Project, user, or org | Per repository, shared across worktrees |
| Loaded into | Every session (in full) | Every session (first 200 lines / 25KB) |
| Use for | Standards, workflows, architecture | Build commands, debugging insights, discovered preferences |
Key insight
Context, not config
CLAUDE.md and auto memory are injected as a user message after the system prompt. They shape behavior; they do not enforce it. When you need something to run no matter what Claude decides — a lint on save, a blocked path — reach for a hook, not a memory file.
Auto memory: notes Claude maintains for itself
Auto memory lets Claude accumulate knowledge across sessions without you writing anything. As it works, Claude decides what is worth remembering — a build command, an architecture note, a preference you corrected — and saves it for next time. It does not save something every session; it saves only what would plausibly help a future conversation.
Requirements & default. Auto memory is on by default and requires Claude Code v2.1.59 or later (check with claude --version).
Where it lives. Each project gets its own directory:
~/.claude/projects/<project>/memory/
├── MEMORY.md # concise index, loaded into every session
├── debugging.md # detailed notes, loaded on demand
├── api-conventions.md # API decisions, loaded on demand
└── ... # any other topic files Claude createsThe <project> segment is derived from the git repository, so all worktrees and subdirectories within the same repo share one memory directory. (Outside a git repo, the project root is used.)
What loads when. The first 200 lines of MEMORY.md, or the first 25KB — whichever comes first — load at the start of every conversation. MEMORY.md is an index; Claude keeps it concise by moving detail into topic files like debugging.md, which are not loaded at startup. Claude reads those on demand with its normal file tools when it needs them. (This 200-line / 25KB limit applies only to MEMORY.md — CLAUDE.md files load in full regardless of length.)
It is machine-local. Auto memory is shared across worktrees of the same repo but is not synced across machines or cloud environments. Treat it as a per-machine scratchpad, not a team artifact — for team-shared knowledge, write CLAUDE.md.
When you see "Writing memory" or "Recalled memory" in the interface, Claude is actively reading from or updating this directory. Everything there is plain markdown you can open, edit, or delete.
Note
MEMORY.md is an index, not a dump
Only MEMORY.md's first 200 lines / 25KB are auto-loaded. Detail lives in topic files that load lazily. If a memory note feels missing at session start, it is probably in a topic file Claude hasn't needed to read yet — that's by design, and it's what keeps auto memory from eating your context.
Tip
Subagents get their own memory
Subagents can maintain a separate auto memory from the main session, so an exploration agent's learnings don't pollute your primary thread. See the subagent configuration docs to enable persistent memory per agent.
Controlling auto memory: toggle, disable, relocate, seed
Auto memory has four control surfaces — a session toggle, a settings key, an env var to kill it outright, and a settings key to relocate it.
Toggle in a session. Run /memory and use the auto memory toggle.
Disable via settings (project settings.json):
{
"autoMemoryEnabled": false
}Disable via environment variable (one switch, global to that shell):
export CLAUDE_CODE_DISABLE_AUTO_MEMORY=1Relocate the storage with autoMemoryDirectory in settings.json. The value must be absolute or start with ~/, and it can be set at any settings scope:
{
"autoMemoryDirectory": "~/my-custom-memory-dir"
}Seeding memory yourself. You don't have to wait for Claude to decide. Two ways to add a note directly:
- Prefix a message with
#— e.g.# the API tests require a local Redis instance— and it goes into auto memory. - Just ask in plain language: "remember this: always use pnpm, not npm." Claude saves it to auto memory.
Want it in CLAUDE.md instead (team-shared, version-controlled)? Say so explicitly — "add this to CLAUDE.md" — or edit the file yourself via /memory.
Watch out
# adds to memory; it does not run anything
The # prefix writes a note to auto memory. Don't confuse it with the ! prefix, which runs a shell command and feeds the output into context. Different prefixes, different jobs: # = remember, ! = execute.
@-imports: compose and share instruction files
A CLAUDE.md can pull in other files with @path/to/import syntax. The referenced file is expanded and loaded into context at launch, right alongside the CLAUDE.md that imports it.
See @README for project overview and @package.json for available npm commands.
# Additional Instructions
- git workflow @docs/git-instructions.md
- personal prefs @~/.claude/my-project-instructions.mdThe rules that matter:
- Both relative and absolute paths are allowed. A relative path resolves relative to the importing file, not your working directory.
- Imported files can recursively import other files, up to a maximum depth of 4 hops.
- The first time a project uses external imports, Claude shows an approval dialog listing the files. Decline, and imports stay disabled and the dialog won't reappear. This is the secret-safety gate: an import can't silently slurp in a credentials file.
- Imports help organization, but they do not save context — every imported file still loads at launch. To actually reduce startup context, use path-scoped rules (next section).
The cross-worktree trick. A gitignored CLAUDE.local.md exists only in the worktree where you created it. If you work across multiple worktrees of the same repo and want personal instructions everywhere, don't copy the file around — import one file from your home directory:
# Individual Preferences
- @~/.claude/my-project-instructions.mdNow every worktree's CLAUDE.local.md (or CLAUDE.md) points at the same home-directory source of truth.
Bonus — AGENTS.md. Claude reads CLAUDE.md, not AGENTS.md. If your repo already has an AGENTS.md, put @AGENTS.md at the top of CLAUDE.md so both tools share one set of instructions without duplication.
Tip
Relative resolves to the file, not the cwd
If src/CLAUDE.md contains @./conventions.md, it resolves to src/conventions.md — relative to the importing file. This is the opposite of @-mentions you type in chat, which resolve from your working directory. Keep the distinction in mind when an import 'can't find' its target.
.claude/rules/: instructions that load on demand
Imports load everything at launch. Path-scoped rules are how you load instructions only when they're relevant — the single biggest lever for keeping a large project's startup context lean.
Place markdown files in .claude/rules/, one topic per file. They're discovered recursively, so you can nest them:
your-project/
├── .claude/
│ ├── CLAUDE.md # main project instructions
│ └── rules/
│ ├── code-style.md # loaded every session (no paths)
│ ├── testing.md
│ └── security.mdThe behavior hinges on one frontmatter field — paths:
- A rule with
pathsfrontmatter is conditional: it loads into context only when Claude reads a file matching the glob. - A rule without
pathsloads unconditionally at launch, with the same priority as.claude/CLAUDE.md.
---
paths:
- "src/api/**/*.ts"
---
# API Development Rules
- All API endpoints must include input validation
- Use the standard error response format
- Include OpenAPI documentation commentsThat rule stays out of context until Claude opens a matching src/api/**/*.ts file — then it appears, exactly when it's useful. Glob patterns are flexible, including brace expansion:
| Pattern | Matches |
|---|---|
**/*.ts | All TypeScript files in any directory |
src/**/* | All files under src/ |
*.md | Markdown files in the project root |
src/components/*.tsx | React components in a specific directory |
src/**/*.{ts,tsx} | TS and TSX under src/ (brace expansion) |
Two more layers worth knowing:
- User-level rules in
~/.claude/rules/apply to every project on your machine. They load before project rules, so project rules win on conflict. - Symlinks are supported. Maintain one shared rule set and link it into many projects (
ln -s ~/shared-claude-rules .claude/rules/shared); circular symlinks are detected and handled.
A good rule of thumb: if an instruction only matters for part of the codebase, it belongs in a path-scoped rule, not in CLAUDE.md.
Key insight
Rules vs. skills — both load on demand, differently
A path-scoped rule loads when Claude reads a matching file. A skill loads when you invoke it or when Claude judges it relevant to your prompt. Rules are for 'whenever you touch these files, remember this'; skills are for 'here is a procedure to run when this task comes up.' Reach for a skill when the instruction is a multi-step workflow rather than a standing constraint.
Monorepos, exclusions, and debugging what loaded
In a large monorepo, walking up the directory tree can pull in other teams' CLAUDE.md files that are noise for your work. Two tools tame this.
claudeMdExcludes skips specific CLAUDE.md (and rules) files by path or glob. Put it in .claude/settings.local.json to keep the exclusion local to your machine:
{
"claudeMdExcludes": [
"**/monorepo/CLAUDE.md",
"/home/user/monorepo/other-team/.claude/rules/**"
]
}Patterns match against absolute paths, and arrays merge across settings layers (user, project, local, managed). One firm limit: managed-policy CLAUDE.md cannot be excluded — org-wide instructions always apply. (For org deployments, the claudeMd key in managed-settings.json can embed CLAUDE.md content directly instead of shipping a separate file.)
/memory is your loaded-files inventory. Run it to:
- List every
CLAUDE.md,CLAUDE.local.md, and rules file loaded in the current session — if a file isn't listed, Claude can't see it. - Toggle auto memory on/off.
- Open the auto memory folder, or any listed file, in your editor.
InstructionsLoaded hook — the debugger. When you can't tell why a rule did or didn't load (path-scoped rules and lazily-loaded subdirectory files are the usual suspects), wire up the InstructionsLoaded hook. It logs exactly which instruction files load, when, and why — the ground truth for diagnosing a rule that silently never fired.
Example
Debugging a rule that never fires
Symptom: your paths: ["src/api/**/*.ts"] rule never seems to apply. Run /memory — is the rule even listed? If yes, it's conditional, so it only appears once Claude reads a matching file. Add the InstructionsLoaded hook and watch the log: if it never fires when you open src/api/users.ts, your glob is wrong (a common miss: src/api/*.ts won't match nested files; you need src/api/**/*.ts).
Try it: Build a path-scoped, self-maintaining memory setup
Set up the full stack of mechanisms from this lesson in a real (or scratch) git project, then prove each one works.
-
Auto memory. Confirm you're on v2.1.59+ with
claude --version. Start a session and seed a note two ways: prefix a message with# build with pnpm, not npm, and separately sayremember that the integration tests need a local Redis. Run/memory, open the auto memory folder, and verify both landed in~/.claude/projects/<project>/memory/MEMORY.md. Then ask Claude to 'add a coding convention to CLAUDE.md' and confirm it went to CLAUDE.md instead — internalize the # vs. CLAUDE.md split. -
Path-scoped rule. Create
.claude/rules/api.mdwith frontmatterpaths: ["src/api/**/*.ts"]and one concrete instruction (e.g. 'every endpoint must validate input'). Run/memoryand note whether the rule is listed. Now ask Claude to read a file undersrc/api/and confirm the rule now applies. Change the glob to a non-matching pattern and observe it no longer fires. -
Import across worktrees. Create
~/.claude/my-project-instructions.mdwith a personal preference. Add@~/.claude/my-project-instructions.mdto yourCLAUDE.local.md. If you can, create a second worktree (claude -w experiment) and add the same one-line import there — confirm both worktrees now share the single home-directory file. -
Debug what loaded. Add an
InstructionsLoadedhook that logs to a file. Trigger a session, open an API file, and inspect the log to see your path-scoped rule load on demand. Then add a junk path toclaudeMdExcludesfor an unrelated CLAUDE.md and confirm via/memorythat it disappears from the loaded list.
Write three sentences on which mechanism gave you the biggest context savings, and why path-scoped rules beat plain imports for a large repo.
Key takeaways
- 1Two systems load every session: CLAUDE.md (you write it) and auto memory (Claude writes it) — both are advisory context, not enforced config; use hooks for hard guarantees.
- 2Auto memory (default on, v2.1.59+) lives at ~/.claude/projects/<project>/memory/MEMORY.md; the first 200 lines / 25KB load at start, topic files load on demand, and it's per-repo and machine-local — never synced across machines.
- 3Control auto memory four ways: /memory toggle, autoMemoryEnabled:false, CLAUDE_CODE_DISABLE_AUTO_MEMORY=1, and autoMemoryDirectory; seed notes with a # prefix or by saying 'remember this'.
- 4@-imports compose files (@path, @~/.claude/file.md); relative paths resolve to the importing file, max 4 hops, and the first external import shows an approval dialog. Imports organize but don't save context.
- 5.claude/rules/ with paths frontmatter loads a rule only when Claude reads a matching file; no paths means it loads unconditionally — this is the real lever for trimming startup context.
- 6For big repos: claudeMdExcludes drops irrelevant ancestor files, /memory lists what loaded, and the InstructionsLoaded hook tells you exactly what loaded and why.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.Where does auto memory live, and what loads into context at the start of a session?
2.You add a rule file with the frontmatter `paths: ["src/api/**/*.ts"]`. When does it enter Claude's context?
3.In a CLAUDE.md located at src/CLAUDE.md, you write `@./conventions.md`. What does it resolve to, and what's the import depth limit?
4.Your path-scoped rule never seems to apply, and you want to confirm exactly what instruction files Claude loaded and why. Which tools fit best?
Go deeper
Hand-picked sources to keep learning
Primary source: CLAUDE.md hierarchy, @-imports, .claude/rules/ with paths frontmatter, auto memory, claudeMdExcludes, and troubleshooting.
The InstructionsLoaded lifecycle hook for logging which instruction files load, when, and why — the debugger for path-scoped rules.
Keys used in this lesson: autoMemoryEnabled, autoMemoryDirectory, claudeMdExcludes, claudeMd (managed), and settings precedence across scopes.
Full command list including /memory (list loaded files, toggle auto memory) and /init (generate a starter CLAUDE.md).
How subagents can maintain their own auto memory separate from the main session.
Issues, changelog, and release notes — useful for confirming version-gated behavior like auto memory (v2.1.59+).