little cups of claude

tips and techniques for Claude and Claude Code

Cups tagged “Tips”

Today we'll look at how Claude Code remembers things between sessions with auto memory, and how it fits next to the CLAUDE.md files we write ourselves.

The split is simple. We write CLAUDE.md to give Claude instructions. Claude writes auto memory to keep notes for itself. Auto memory is on by default in local sessions.

Let's dive in.

What Gets Saved

As it works, Claude saves four kinds of notes, and records the kind in each file's frontmatter:

  • user: our role, expertise, and how we like to work
  • feedback: corrections we give and approaches we confirm
  • project: ongoing work, deadlines, and decisions the code doesn't show
  • reference: where to find things outside the project, like an issue tracker

It skips anything it can work out from the codebase, and anything our CLAUDE.md files already say. It also doesn't save something every session, only what looks useful later. When it does, we'll see a message like "Saved 2 memories".

Where It Lives

Each project gets its own folder:

~/.claude/projects/<project>/memory/
  MEMORY.md                 # the index, one line per memory
  user_role.md              # one memory per file
  feedback_testing.md
  reference_dashboards.md

The <project> comes from the git repository, so every worktree and subdirectory of the same repo shares one memory folder. It stays on our machine, and isn't synced to other machines or cloud sessions.

What Loads

At the start of each conversation, Claude Code loads the first 200 lines or 25KB of MEMORY.md, whichever comes first. The topic files don't load at startup. Claude reads them when it needs them.

- [Prefers bun](feedback_bun.md): use bun, never node or npm
- [Staging deploys](reference_staging.md): how we ship to staging
- [Q4 freeze](project_freeze.md): no schema changes until Jan 5

That's why the index stays short. When MEMORY.md gets close to the limit, Claude Code reminds Claude to tighten it up. Neat!

The /memory Command

/memory is the control panel:

> /memory

It lists our CLAUDE.md and CLAUDE.local.md files, has a toggle for auto memory, and opens the memory folder. Picking a file opens it in our editor. Memory files are plain markdown, so we can edit or delete anything that's wrong.

Telling It What To Remember

When we say something like "remember that the API tests need a local Redis", Claude saves it to auto memory. If we want it in CLAUDE.md instead, we just ask: "add this to CLAUDE.md".

For instructions that only matter in part of the codebase, .claude/rules/ is the tidier spot:

---
paths:
  - "src/api/**/*.ts"
---

# API rules
- Validate every request body with zod
- Use the shared error response format

A rule with paths only loads once Claude reads or edits a matching file. Rules without paths load at launch, like CLAUDE.md does.

Turning It Off

The /memory toggle saves autoMemoryEnabled to our user settings. To switch it off for one project, set it in that project's settings:

{
  "autoMemoryEnabled": false
}

And autoMemoryDirectory moves the whole thing somewhere else, if we'd rather keep it in a synced folder.

Watch Out

Subagents don't get the main conversation's auto memory, apart from forks, which inherit the whole parent conversation. And memory files are never cleaned up automatically. They stay until we or Claude edit or delete them, so it's worth a look through /memory every so often.

More info about auto memory can be found at the Claude Code docs.

Today we'll check out how to build our own slash commands in Claude Code using skills.

Custom slash commands started out as single Markdown files in a commands folder. They've since been merged into skills. Both still work, and both show up in the same / menu, but skills can do quite a bit more.

Let's get started.

Commands And Skills

A file at .claude/commands/deploy.md creates /deploy. So does a folder at .claude/skills/deploy/ with a SKILL.md inside:

# Older format (still works)
.claude/commands/deploy.md

# Skill (recommended for new work)
.claude/skills/deploy/SKILL.md
.claude/skills/deploy/checklist.md
.claude/skills/deploy/scripts/smoke.sh

The big difference is the folder. A skill can carry templates, checklists, and scripts alongside its instructions, and Claude can load a skill on its own when it's relevant, without us typing the command.

If both exist with the same name, the skill wins.

Frontmatter

Skills are configured with YAML frontmatter at the top of SKILL.md:

---
name: deploy
description: Build, test and deploy the app to production
disable-model-invocation: true
argument-hint: [environment]
allowed-tools: Bash(bun run build) Bash(bun run deploy *)
---

Deploy to $ARGUMENTS following our release checklist
in checklist.md.

A few fields worth knowing:

  • name is the command we type. It defaults to the folder name.
  • description is how Claude decides when the skill applies, so put the main use case first.
  • disable-model-invocation: true means only we can run it. Good for anything with side effects.
  • allowed-tools pre-approves tools while the skill runs, so we don't get permission prompts for them.

Arguments

Everything after the command name lands in $ARGUMENTS. For individual pieces, indexes start at zero:

---
name: fix-issue
arguments: [issue, branch]
---

Fix issue #$issue on the $branch branch.
Same thing by index: #$0 on $1.

Running /fix-issue 42 main fills in 42 and main. Quotes group words, so /fix-issue 42 "release candidate" passes two arguments, not three.

Live Context

Lines with !`command` run before Claude ever sees the skill, and the output replaces the line:

---
name: commit
description: Write a commit for the staged changes
allowed-tools: Bash(git status) Bash(git diff *) Bash(git log *) Bash(git commit *)
---

## Context
- Status: !`git status`
- Staged diff: !`git diff --cached`
- Recent commits: !`git log --oneline -10`

Write one clear commit message for these changes and commit.

Claude gets the real diff already inlined, no extra tool calls needed. Neat!

${CLAUDE_SKILL_DIR} points at the skill's own folder, which makes bundled scripts easy to call from anywhere.

Bundled Skills

Claude Code ships with a set of skills of its own, like /code-review, /batch, /debug, /loop, and /claude-api. They're good examples of how to structure longer workflows, and we can turn them off with the disableBundledSkills setting.

Personal vs Project

Project skills live in .claude/skills/ and get committed for the whole team. Personal ones live in ~/.claude/skills/ and follow us into every project:

mkdir -p ~/.claude/skills/explain

cat > ~/.claude/skills/explain/SKILL.md << 'END'
---
description: Explain code for someone new to the project
---

Explain this code for a new teammate. Cover what it does,
why it's built this way, and any gotchas.
END

Watch Out

If a !`command` line fails, the whole skill invocation stops, and Claude never sees the skill. Commands that are expected to exit non-zero need || true on the end.

Those commands also never prompt for permission. Outside auto mode, anything not already allowed makes the skill fail, which is why the commit example lists its git commands in allowed-tools.

More info about skills can be found at the Claude Code docs.

Today we'll take a look at one of the most useful files in any Claude Code project: CLAUDE.md.

A CLAUDE.md file gives Claude Code instructions about our project that stick around between sessions. Claude Code loads it into context at the start of every session, so we stop repeating the same preferences over and over.

Let's dive in.

Setting It Up

The fastest way to get one is to let Claude Code write a first draft for us:

> /init

Claude looks at our codebase and writes a CLAUDE.md with the build commands, test instructions, and conventions it finds. If one already exists, /init suggests improvements instead of overwriting it.

We can also start from scratch:

# Discovery One
Use TypeScript for all new files
Use bun, not node
Run tests with `bun test`
The database is SQLite via better-sqlite3

From then on, every session in this directory starts out knowing all of that. Neat!

Where It Can Live

There are a few places a CLAUDE.md can go, each with a different scope:

# User: our preferences for every project
~/.claude/CLAUDE.md

# Project: checked into git, shared with the team
./CLAUDE.md          # or ./.claude/CLAUDE.md

# Local: personal notes for this project only
./CLAUDE.local.md    # add it to .gitignore

These don't override each other. Claude Code concatenates all of them into context, broadest first: user, then project, then local. Instructions closer to where we launched get read last.

Parent And Child Directories

Claude Code also walks up the directory tree. If we start it in apps/hal/, it loads apps/hal/CLAUDE.md and also apps/CLAUDE.md and the repo root's CLAUDE.md.

monorepo/
  CLAUDE.md            # loaded at launch
  apps/
    hal/
      CLAUDE.md        # loaded at launch (we started here)
  packages/
    ui/
      CLAUDE.md        # loaded later, on demand

Files in subdirectories below where we started load on demand, the first time Claude reads or edits something in that folder. That keeps a big monorepo from dumping every team's notes into context at once.

What To Put In It

The best CLAUDE.md files are short and full of facts Claude can't easily figure out on its own:

  • Commands: how to build, test, lint, and deploy
  • Conventions: naming, patterns to follow or avoid
  • Layout: where things live, how modules connect
  • Gotchas: the things that bit us last week
## Commands
bun dev    # dev server on :3000
bun test   # unit tests
bun lint   # eslint

## Rules
Prefer unknown over any
Never edit files in generated/
Migrations go in db/migrations, one per change

Imports

A CLAUDE.md can pull in other files with @path:

See @README.md for the project overview.
Our API conventions are in @docs/api.md.

# Personal prefs, shared across worktrees
@~/.claude/samantha-prefs.md

Imported files can import other files, up to four hops deep. Relative paths resolve from the file doing the importing, not from wherever we launched Claude Code.

Watch Out

The docs suggest aiming for under around ~200 lines per CLAUDE.md. Longer files cost context and Claude follows them less reliably. Imports help organize a long file, but they don't shrink it, since imported files load right along with it.

If something only matters for one part of the codebase, a path-scoped rule in .claude/rules/ is a better home. Those only load when Claude works with matching files.

To check what loaded, we can run /context and look under Memory files.

More info about CLAUDE.md can be found at the Claude Code docs.