Claude Code Does Not Understand Your Project — Fix It

Claude Code re-edits the same file or ignores whole workspaces? Run /init, anchor it with CLAUDE.md, and start from the repo root. Verified June 2026.

You ask Claude Code to make a change in a mid-sized repo, and it keeps editing src/index.ts over and over while completely ignoring the apps/web/ or packages/api/ you mentioned. In a monorepo it can’t tell workspace boundaries; it drops tests next to source instead of in __tests__/. Same root cause every time: Claude Code has no project map.

Fastest fix: at your repo root, run /init. Claude Code scans the codebase with a subagent and writes a starting CLAUDE.md with your build commands, test commands, and key directories. Review it, add the two or three rules it couldn’t infer (workspace graph, “don’t touch” paths), and restart the session. That alone resolves most cases. The rest of this page covers when /init isn’t enough and how to confirm the fix held.

Why this happens

Claude Code is, at heart, an LLM session with filesystem tools. Each session starts with a fresh context window, and its entire mental model of your project comes from three things: the working directory it launched in, the CLAUDE.md files it loads at startup, and whatever paths you mention in prompts. If any of those is missing or misleading, you get keyhole behavior — it discovers structure with ls one level at a time and gets lost in anything non-trivial.

Per the official memory docs, Claude Code walks up the directory tree from your working directory and loads every CLAUDE.md and CLAUDE.local.md it finds, plus ~/.claude/CLAUDE.md (user scope) and any managed-policy file. Files in subdirectories are not loaded at launch — they load on demand only when Claude reads a file in that subdirectory. That asymmetry explains a lot of “it ignored package X” reports.

Which bucket are you in?

SymptomLikely causeFirst checkJump to
Forgets conventions every new sessionNo CLAUDE.mdls CLAUDE.md returns nothingCause 1 / Step 1
Can’t see sibling packages or shared typesLaunched in a subdirectory/status or !pwd is not the repo rootCause 2 / Step 2
Returns framework code as “your” componentnode_modules / dist noiseSearch result contains node_modules/...Cause 3 / Step 3
Doesn’t know the workspace dependency graphNo workspace map written down”list every package” only names the current oneCause 4 / Step 1
Edits the last file it touched, not the right oneVague promptYour prompt has no file pathCause 5 / Step 5
Forgets early rules late in a long chatContext filled with low-value textSession is 30+ turns; /context shows high usageCause 6 / Step 6

Common causes

Ordered by hit rate, highest first.

1. No CLAUDE.md — every session starts blind

The most common cause. On startup Claude Code auto-loads the CLAUDE.md files in your directory hierarchy and injects them as context. Without one, it has to discover structure file by file and gets lost in any non-trivial monorepo.

How to spot it: run ls CLAUDE.md in the project root. Missing = this is your problem. You can also run /memory inside a session — it lists every CLAUDE.md, CLAUDE.local.md, and rules file currently loaded. If the list is empty, Claude is flying blind.

2. Wrong working directory

Starting Claude Code in apps/web/ makes it treat that folder as the entire project — it can’t see shared types in packages/shared/, because those live in a sibling directory, not an ancestor. Starting it in ~/code/ makes it drown in dozens of unrelated projects.

How to spot it: run /status (or !pwd) at the start of the session and compare against the real project root — usually the folder containing .git/ or your workspace manifest (pnpm-workspace.yaml, root package.json with a workspaces field, nx.json).

3. node_modules / dist / .next directories drown out real code

Claude Code skips a default set of paths, but Grep and Glob can still surface vendored code under node_modules. You ask for “the Button component” and it returns the framework’s, not yours.

How to spot it: ask Claude Code to list “all Button components in the repo.” If the result contains node_modules/..., this is why.

4. Monorepo without a workspace map

pnpm workspaces, Turborepo, Nx — packages reference each other via workspace:*. Claude Code won’t proactively parse pnpm-workspace.yaml or nx.json to derive the dependency graph. You have to write it down.

How to spot it: ask “list every package in this repo and what depends on what.” If it only knows the current package, this is the cause.

5. Vague references in your prompt

“Fix the login bug” — it doesn’t know where login lives. “That component is wrong” — which one? Vague prompts make Claude Code default to whatever file it last touched, which is often wrong.

How to spot it: re-read your prompt. If it has no concrete file path or function name, this is the cause.

6. Context window stuffed with low-value content

If early in the session you piped in large logs, build output, or long stack traces, the actually-relevant code gets pushed toward the edge of the active window and earlier conventions start to slip.

How to spot it: run /context — it visualizes what’s eating your window. If old logs or transcripts dominate and the session is long (30+ turns), this is it.

Shortest path to fix

Step 1: Write (or generate) a CLAUDE.md project map

Run /init first — it writes a draft from your actual code by spawning a subagent that reads source-of-truth files (package.json, requirements.txt, Makefile, README) and maps the directory tree. If a CLAUDE.md already exists, /init suggests improvements instead of overwriting. For a guided setup that also offers to scaffold skills and hooks, set CLAUDE_CODE_NEW_INIT=1 before running it — an interactive multi-phase flow that asks follow-up questions and shows a reviewable proposal before writing anything. Then hand-edit so the workspace graph and “don’t touch” rules are explicit. A project CLAUDE.md can live at either ./CLAUDE.md or ./.claude/CLAUDE.md; both are loaded. Keep it under ~200 lines — Anthropic’s docs note that longer files consume more context and reduce adherence.

# Project Map

This is a pnpm monorepo with 3 workspaces:
- `apps/web/`  Next.js frontend (port 3000)
- `apps/api/`  Hono backend (port 8787, Cloudflare Worker)
- `packages/shared/`  Zod schemas + types used by both
  - `apps/web` and `apps/api` both import from `packages/shared`; shared never imports back

## Conventions

- TypeScript strict mode everywhere
- Tests live in `__tests__/` next to source, Vitest
- API routes follow REST: `GET /resource`, `POST /resource`, etc.
- Never edit files under `packages/shared/dist/` — build output

## Commands

- `pnpm dev` — runs web + api concurrently
- `pnpm test` — runs all workspaces
- `pnpm typecheck` — must pass before commit

## Don't touch

- `infra/terraform/` — owned by ops
- `apps/web/public/legacy/` — frozen

Be specific and verifiable. “API handlers live in src/api/handlers/” beats “keep files organized.” To keep the map DRY you can import other files with @path syntax — for example See @README.md for overview — which expands at launch (relative paths resolve against the file doing the import, max depth four hops). Imports help organization but don’t save context, since they still load up front.

Step 2: Start from the right working directory

Find the project root (the folder with .git/ or your workspace manifest) and launch there:

cd ~/code/my-monorepo
claude

Don’t start in ~/code/ and then cd in — Claude Code’s startup CLAUDE.md scan has already happened, and it won’t pick up an ancestor map after the fact. If you genuinely need a sibling folder in scope (a shared config repo, say), add it explicitly: claude --add-dir ../shared-config. To also load that directory’s CLAUDE.md, set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1.

Step 3: Cut the noise — declare ignores, deny secrets

There is no official .claudeignore file as of June 2026 (it’s an open feature request, anthropics/claude-code #29455). Two real mechanisms do the job:

  1. Tell it in CLAUDE.md which generated paths to skip, so the model doesn’t waste turns on them:

    ## Ignore these (generated / vendored)
    - `node_modules/`, `dist/`, `.next/`, `.turbo/`, `coverage/`
    - `**/*.generated.ts`
    - `apps/web/public/legacy/`
  2. Hard-block sensitive files with permissions.deny in .claude/settings.json — unlike a CLAUDE.md note, this excludes matched files from discovery, search, and reads:

    \{
      "permissions": \{
        "deny": ["Read(./.env)", "Read(./.env.*)", "Read(./**/secrets/**)"]
      \}
    \}

    Treat permissions.deny as a guardrail, not a vault. As of June 2026 there are still open reports that deny rules can be bypassed — for example a secret reached through a symlink or an unusual path (anthropics/claude-code issues #6699 and #24846). Keep Claude Code on the latest release (claude --version), and if a file absolutely must never be read, enforce it with a PreToolUse hook that the docs explicitly recommend for “block an action regardless of what Claude decides” — a hook runs as a shell command and can hard-fail the Read, which configuration alone has not reliably done.

In a large monorepo where other teams’ CLAUDE.md files get pulled in and pollute context, use claudeMdExcludes in .claude/settings.local.json to skip them by glob (the value is matched against absolute paths; arrays merge across user, project, local, and managed-policy settings layers).

Step 4: First turn is “read these and confirm”

Don’t immediately delegate work. Start with:

First read CLAUDE.md, then ls the structure of apps/ and packages/
and confirm you understand the workspace layout. Then we'll begin.

Letting it build a mental model up front is faster than letting it trial-and-error after you’ve already assigned work. For monorepos, this also forces the nested CLAUDE.md files in each subdirectory to load — remember, those only load when Claude reads a file in that subtree.

Step 5: Use concrete paths in your prompts

Not “fix the login bug” — “fix apps/web/src/pages/login.tsx line 42, the error handling inside handleSubmit.” Concrete paths skip the guess-and-grep phase entirely.

Step 6: For long sessions, /compact or /clear

Claude Code auto-compacts near the top of the window, but waiting that long means it has already been working with a starved context (the exact trigger has shifted across versions and is reported anywhere from the low-80s to mid-90s percent as of June 2026, so don’t rely on it). Run /context to see usage; when you’re past 30-40 turns or the meter is high, run /compact to have Claude Code summarize state in place. You can focus it: /compact keep the auth refactor plan and the file list. For a genuinely new task, /clear wipes history entirely. Good news for both: a project-root CLAUDE.md survives /compact (Claude re-reads it from disk), so your project map is never the thing that gets lost — but nested subdirectory maps are not re-injected until Claude next reads a file there.

How to confirm it’s fixed

  1. Restart the session from the repo root and run /memory. Your project CLAUDE.md (and any nested ones) should be listed as loaded. If not, Claude can’t see it.
  2. Ask: “List every workspace in this repo and what each one depends on.” A fixed setup answers from the map without grepping; a broken one names only the current package.
  3. Ask it to find “the Button component we own.” The path should be under apps/ or packages/, never node_modules/.
  4. Assign a small real task that touches a non-default directory. If it edits the correct file on the first try instead of defaulting to the last-touched one, the map is working.

FAQ

Does Claude Code read AGENTS.md or .cursorrules?

It reads CLAUDE.md, not AGENTS.md, directly. If your repo already has an AGENTS.md, put a one-line import at the top of CLAUDE.md (@AGENTS.md) or symlink it (ln -s AGENTS.md CLAUDE.md), so both tools share one source. On Windows a symlink needs Administrator or Developer Mode, so prefer the @AGENTS.md import. Running /init in a repo that already has AGENTS.md, .cursorrules, .devin/rules/, or .windsurfrules reads them and folds the relevant parts into the generated CLAUDE.md.

My CLAUDE.md is loaded but Claude still ignores a rule. Why?

CLAUDE.md is delivered as context after the system prompt, not as enforced configuration, so adherence isn’t guaranteed — especially for vague or conflicting rules. Make the instruction concrete and verifiable, and check for contradictions across your user, project, and nested files. For something that must run at a fixed moment (before every commit, after each edit), use a hook instead, which executes regardless of what Claude decides.

Should I put one big CLAUDE.md or split it up?

For most repos, one root file under ~200 lines is best. When it grows, move topic- or path-specific guidance into .claude/rules/*.md. Rules with a paths: frontmatter glob only load when Claude touches matching files, which keeps the always-on context lean in a large codebase.

How is auto memory different from CLAUDE.md?

CLAUDE.md is what you write; auto memory is what Claude writes for itself across sessions (build commands, debugging insights, preferences it discovers), stored per repository at ~/.claude/projects/<project>/memory/. The first 200 lines (or 25KB) of its MEMORY.md load every session. Auto memory needs Claude Code v2.1.59 or later; check with claude --version. Run /memory to browse or edit what it saved.

It understood the project yesterday but not today. What changed?

Three usual suspects: you launched from a different directory (so a different set of CLAUDE.md files loaded), a teammate edited or moved the map, or yesterday’s understanding lived only in conversation and never made it into CLAUDE.md. Anything you want to persist must be in the file, not just the chat.

Prevention

  • Treat CLAUDE.md as living documentation — update it when you add modules or change conventions, and especially when you correct Claude on something twice.
  • Put a CLAUDE.md in each workspace too; Claude Code loads the nearest one when it reads files in that subtree.
  • For complex tasks, use a two-step prompt: “make a plan first, confirm understanding, then execute.”
  • Encode durable rules (“tests start with an in-memory mock”) in CLAUDE.md, not in every prompt.
  • Proactively /compact long sessions instead of waiting for the auto-compact at the edge of the window.

Tags: #Claude #Debug #Troubleshooting