Skip to content

Course · Claude Code · Rules & memory

Improve an existing CLAUDE.md with /init

/init is the Claude Code command that drafts a CLAUDE.md for a repository: it reads the code and writes the project’s commands, structure, and conventions into a file that loads into every future session. On a repo with no such file, that is the whole story. Repositories you join often already have one, and there the documented behavior is different and more useful: if a CLAUDE.md already exists, /init suggests improvements rather than overwriting it (memory reference). Your job becomes an audit. Keep what is true, correct what is half-true, reject what is invented.

The example repo is a small TypeScript payments service (the example this course returns to; each page restates the facts it needs). Its root CLAUDE.md holds eight rules in three sections: money handling (integer cents, never floating point), testing (npm test, no commit without passing tests, a regression test with every bug fix), and code style (strict TypeScript, .js extensions on imports, named exports). Those rules already earn their keep. What the file lacks is the map: the dev, build, and lint commands, and the shape of the source tree.

To audit what /init proposes, you need three facts about the repo, all readable in a minute:

  • Scripts in package.json: dev is tsx watch src/index.ts, build is tsc, test is vitest run, lint is eslint src/ --ext .ts.
  • Source layout: src/index.ts holds the Express app, its routes, and the listen call. src/payments/ holds processor.ts, ledger.ts, and rate-limiter.ts. src/utils/ holds idempotency.ts. Tests live in tests/.
  • The ledger: the first line inside the class in src/payments/ledger.ts is private entries: Entry[] = [];. Balances live in an array in process memory and nothing writes them anywhere.

The third fact is the one that will matter.

Run it at the prompt of a Claude Code session opened in the repo (the getting-started chapter covers starting one):

> /init

Claude reads the codebase and the existing rules, then proposes additions. The exchange below is illustrative, not captured, though the files and commands in it are the repo’s real ones:

> /init
⏵ Read package.json
⏵ Read CLAUDE.md
⏵ Read src/index.ts
⏵ Glob src/**/*.ts
⏵ Read tests/payments.test.ts
CLAUDE.md exists - suggested additions:
## Commands
- Dev server: `npm run dev` (tsx watch src/index.ts)
- Build: `npm run build` (tsc)
- Lint: `npm run lint` (eslint src/ --ext .ts)
- Test: `npm test` (Vitest, single pass via `vitest run`)
## Architecture
- Express app, routes, and server startup in src/index.ts
- Payment domain in src/payments/ (processor, ledger, rate-limiter)
- Idempotency helper in src/utils/idempotency.ts
- Ledger persists completed payments; tests live in tests/
...

Three of the four architecture lines describe the repo as it is. One does not, and it is the most plausible line in the block.

Audit each suggestion against the file it describes

Section titled “Audit each suggestion against the file it describes”

Take them in order. The commands: open package.json and compare its scripts. Four suggestions, four exact matches, and the current CLAUDE.md documents none of them as commands - it names npm test only inside a testing rule. Keep them. A suggestion that names a real command with the wrong details is the half-true case: correct it to the file’s script. A session that knows how to lint and build stops guessing.

The architecture: src/index.ts really does hold the app, the routes, and the listen call. src/payments/ holds the three files named, and src/utils/ holds the idempotency helper. True so far.

Then the ledger line. src/payments/ledger.ts keeps its entries in an in-memory array. Restart the process and every balance is zero. The suggestion inferred “persists” from the class name, which is exactly how a fast reader reasoning from names fails. Reject it, or better, invert it: the corrected line The ledger is in-memory; balances reset on restart is worth keeping, because a constraint the directory’s name does not announce is precisely what a rule is for. Proposed edits to existing rules get the same audit, and a line that predates the draft and still passes it outranks the draft’s rewording of it.

The likely failure at this step is the rubber stamp. A wrong line in CLAUDE.md is worse than no line, because it is a confident falsehood loaded into every future session. Five minutes checking each suggestion against the file it describes is the recovery. Expect a permission prompt when /init writes the file (how it appears depends on your permission mode and saved rules), and run git diff CLAUDE.md to see exactly what changed before you commit.

Two more facts are worth taking from the memory reference rather than from recall:

  • /init also reads existing Cursor rules (.cursor/rules/, .cursorrules) and Copilot instructions (.github/copilot-instructions.md) when it finds them, and folds what they say into its suggestions. A repo that has been driven by an IDE assistant is not a blank page either.
  • An optional interactive, multi-phase /init flow sits behind the CLAUDE_CODE_NEW_INIT environment variable. Do not assume which flow your version runs by default.

The audit leaves the file true. Truth is the entry fee, not the winning criterion: every surviving line still has to justify the context it spends on every session. What actually earns a place in the file?