你让 Claude Code 在一个中等规模仓库里做改动,结果它反复修改 src/index.ts 一个文件,完全忽略你提到的 apps/web/ 或 packages/api/;或者在 monorepo 里它根本看不出 workspace 关系,要不就是把测试写在了源码同目录而不是 __tests__/——这都是同一个根本问题:Claude Code 缺少项目地图。
Claude Code 本质是一个带文件工具的 LLM 会话,它对你项目的全部认知来自三件事:当前工作目录、CLAUDE.md(如果有)、以及你 prompt 里提到的路径。三者任一缺失或失真,它就会陷入”以管窥豹”的模式。
常见原因
按命中率从高到低:
1. 没有 CLAUDE.md,每次会话都从零开始
最高频原因。Claude Code 启动时会自动读取工作目录里的 CLAUDE.md(以及向上递归的 ~/.claude/CLAUDE.md、./.claude/CLAUDE.md),把内容注入 system prompt。没有这个文件,它只能靠 ls 一层一层探,遇到 monorepo 基本就迷路。
如何判断:在项目根跑 ls CLAUDE.md。不存在就是这种情况。
2. 工作目录起点不对
如果你从 apps/web/ 子目录启动 Claude Code,它默认只把这一层当成”项目”,看不到 packages/shared/ 里的共享类型。反过来如果从 ~/code/ 这种父目录启动,它会被几十个无关项目淹没。
如何判断:会话开始时跑 !pwd 看工作目录,对照真实项目根(通常是有 .git/ 或 package.json workspaces 的那一层)。
3. node_modules / dist / .next 等大目录干扰
虽然 Claude Code 默认有一些 ignore 规则,但它的 Grep 和 Glob 工具仍可能扫到 node_modules 里的 vendored 代码,把”框架的 Button 组件”当成你的实现。
如何判断:让 Claude Code 列出”项目里所有 Button 组件路径”,如果结果里有 node_modules/...,就是这个原因。
4. monorepo 没说明 workspace 拓扑
pnpm workspace、Turborepo、Nx 这种结构,包之间靠 workspace:* 互相引用。Claude Code 不会主动跑 pnpm-workspace.yaml 或 nx.json 推理依赖图,需要你在 CLAUDE.md 里手写。
如何判断:让 Claude Code “列出本仓库所有包及其相互依赖”,如果只能列出当前包,就是这种情况。
5. 你的 prompt 用了模糊指代
“修一下登录的 bug”——它不知道登录在哪。“那个组件不对”——它不知道哪个。模糊指代让 Claude Code 优先猜上次接触的文件,不一定对。
如何判断:回看上一轮 prompt,如果不含具体文件路径或函数名,就是这种情况。
6. 上下文窗口被低价值内容塞满
如果你早期让它读了大量日志、build 输出或长 stack trace,后续真正相关的代码就被挤出活跃窗口。Claude Code 会显得”记不住前面说过什么”。
如何判断:会话进行很久了(30+ 轮),且最近几轮的回答开始忘记早期约定。
最短修复路径
Step 1:写一份 CLAUDE.md 项目地图
在项目根创建 CLAUDE.md(不超过 100 行,太长反而稀释关键信息):
# 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
## Conventions
- TypeScript strict mode everywhere
- Tests live in `__tests__/` next to source, use Vitest
- API routes follow REST: `GET /resource`, `POST /resource`, etc.
- Never edit files under `packages/shared/dist/` — those are 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/` — managed by ops team
- `apps/web/public/legacy/` — frozen, do not modify
Step 2:从正确的工作目录启动
确认项目根(有 .git/ 或 pnpm-workspace.yaml 的那一层),从那里启动:
cd ~/code/my-monorepo
claude # 或你的等价命令
不要从 ~/code/ 启动后再 cd 进去——Claude Code 的初始扫描已经完成了。
Step 3:加 .claudeignore 或在 CLAUDE.md 里声明
如果项目有 vendored 代码或大型 generated 文件,列出来:
# .claudeignore (或在 CLAUDE.md 里写)
node_modules/
dist/
.next/
.turbo/
coverage/
*.generated.ts
public/legacy/
Step 4:第一轮 prompt 用”读这些再开工”
新会话第一句别直接派活,先:
请先读 CLAUDE.md,然后用 ls 列出 apps/ 和 packages/ 的结构,
确认你理解了 workspace 布局。然后我们再开始。
让它建立心智模型再行动,比直接派活后它又来回试错快得多。
Step 5:用绝对路径派活
不要说”修登录 bug”,说”修 apps/web/src/pages/login.tsx 第 42 行 handleSubmit 里的错误处理”。具体路径让 Claude Code 跳过猜的环节。
Step 6:长会话定期 /compact 或开新窗口
会话超过 30-40 轮、或者上下文窗口超过 60% 时,用 /compact 让 Claude Code 自己总结当前状态,然后开新窗口贴回去。比硬撑到 token 耗尽强。
预防建议
- CLAUDE.md 是活文档:新增模块、改了约定就同步更新,否则很快就过时
- 每个 workspace / 子项目都放一份
CLAUDE.md,Claude Code 会按工作目录读最近的那份 - 复杂任务用”先列计划再执行”的两段式 prompt,让它先确认理解再动手
- 重要约定(“测试要先写 in-memory mock”)写在 CLAUDE.md 而不是每次 prompt 里重复
- 长 session 主动 /compact,别等上下文塞满了才被动截断