You already know how to write a . But in a big project it grows fast: commands, conventions, rules for the API, for tests, for markup. And every line loads into every session — even when you are editing the README.
Two tools help you keep it in order: imports and rules.
Imports: @path
A line with @ and a path loads another file along with CLAUDE.md:
# The linkcheck project
@AGENTS.md
- npm commands: @package.json
- Architecture: @docs/architecture.md
An import organises memory, but it does not save context: the imported file always loads, together with the file that imports it.
Two small things that trip people up:
- the path runs up to the first space — do not put a full stop right after it;
- the path is relative to the file that contains the import, not to the folder where you started Claude.
Importing a file outside the working folder, such as @~/notes.md, asks for your approval the first time it is used in a project. Decline, and it stays off.
AGENTS.md and CLAUDE.md
Many projects already keep an for other agents. Claude Code reads it on its own — but only while there is no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md in your folder or above it. As soon as one of those exists, Claude reads the CLAUDE.md files instead, and AGENTS.md is skipped.
So the day you add a CLAUDE.md for Claude-specific notes, make @AGENTS.md its first line, and the shared rules stay in one place. The other way is /config → Project instructions → claude-md-and-agents-md, which reads both. Note that a personal CLAUDE.local.md counts too: creating one in a project that relies on AGENTS.md switches AGENTS.md off for you.
Rules: .claude/rules
A rule is a Markdown file in the .claude/rules/ folder. Without a paths header, it loads at startup, like an ordinary CLAUDE.md. With the header — only when Claude works with a matching file:
---
paths:
- "test/**/*.js"
---
# Tests
- Use node:test and node:assert/strict; do not add other libraries.
Patterns are relative to the project root. * does not cross a folder boundary, **/ goes through any number of folders, and braces such as src/**/*.{ts,tsx} work too.
The main subtlety: a rule with paths applies when Claude reads, writes or edits a matching file — with the Read, Write or Edit tool, or with a plain cat, head or tail on that one file in the shell. A command that only lists files, pipes the file into something else or reads several at once does not count. So when you check, ask exactly that: “read test/config.test.js”.
What loads, and when
| Instruction file | When it loads |
|---|---|
CLAUDE.md at the root, and a rule without paths | at session start |
| AGENTS.md, when there is no CLAUDE.md | at session start |
a file pulled in by an @path import | along with the file that imports it |
a rule with paths | when Claude reads, writes or edits a matching file |
| auto memory — notes Claude keeps itself | at startup: the first 200 lines or 25 KB of the MEMORY.md index |
For personal instructions for this project there is CLAUDE.local.md. You create it by hand and do not commit it — that is exactly what the lesson on a shared team kit is about.
Why Claude does not listen
First find out whether the file loaded. Only if it did, work on the wording: make it specific and remove rules that contradict it.
The first way is /context all. Plain /context can collapse the list; all expands it. The Memory files section shows the files loaded at startup. Rules with paths appear there only after a matching file is read.
The second way is the InstructionsLoaded hook. It fires every time a CLAUDE.md, a rule or an import loads. Put it in a separate file, for example .practice/loaded-hook.json:
{
"hooks": {
"InstructionsLoaded": [
{ "hooks": [ { "type": "command", "command": "jq -r '[.load_reason, .file_path] | @tsv' >> \"$CLAUDE_PROJECT_DIR/.practice/loaded.txt\"" } ] }
]
}
}
And start a session with it: claude --settings .practice/loaded-hook.json. One gap: the hook does not fire for an AGENTS.md that Claude reads directly, only for one that a CLAUDE.md imports. Every line in the log has a reason:
session_start— loaded at startup, like CLAUDE.md;include— pulled in by an import;path_glob_match— a rule applied through its pattern.
If there is no path_glob_match line for your rule, compare the pattern with the real files. Git helps:
git ls-files ':(glob)src/api/**/*.js'
Empty output means the pattern matches no file. The classic: a rule for source/** in a project where the code lives in src/.
Editing mid-session
Claude Code reads the project’s CLAUDE.md once, at startup. Edit the file, and the change reaches the session after /clear, /compact or a restart.
And to find instructions that contradict each other or point to deleted files, run /doctor prompt-audit. It suggests edits and changes nothing until you ask.
Quick check
/context all does not show the .claude/rules/api.md rule. So far Claude has only searched the folder with grep -r. What do you do?
Careful
CLAUDE.md is context, not a setting Claude is obliged to obey. A rule that must never be broken belongs in a hook, not a line in memory. And do not bloat the file: every line loads into every session, so keep CLAUDE.md short and move long procedures for rare tasks into skills.
Practice in the terminal
Check your CLAUDE.md
Paste the file and notes appear right away. The text never leaves your browser.
- Important: Almost empty
Start with the essentials: how to build the project, how to run the tests, what not to touch. /init drafts it for you.