用过一阵 Claude Code 的仓库,git status 里会多出 .claude/;家目录里还躺着一个 ~/.claude/。这两处混着放,常见后果有三种:把个人配置提交进 PR、同事拉下来规则不生效、改了文件却像没改过。搞清楚每个文件的归属,比记命令更重要。
下面按 地图 → 两级关系 → 记忆文件 → 进不进 git → 多仓库 → 排错 拆。
1. 一张地图:项目级与用户级
| 路径 | 作用域 | 进版本控制 | 内容 |
|---|---|---|---|
.claude/settings.json |
项目、团队 | 是 | 权限规则、env、hooks、状态栏 |
.claude/settings.local.json |
项目、仅自己 | 否 | 个人覆盖、本机路径 |
.claude/commands/*.md |
项目 | 是 | 自定义斜杠命令 |
.claude/agents/*.md |
项目 | 是 | 子代理定义(frontmatter + 提示词) |
.claude/skills/<name>/SKILL.md |
项目 | 是 | 技能包,按需加载 |
.claude/hooks/*.sh |
项目 | 是 | hook 脚本本体(配置在 settings 里) |
CLAUDE.md |
项目、团队 | 是 | 团队记忆:构建、约定、禁区 |
CLAUDE.local.md |
项目、仅自己 | 否 | 个人记忆(已标记废弃,建议改用 import) |
.mcp.json |
项目 | 是(不含密钥) | 项目级 MCP server 声明 |
~/.claude/settings.json |
用户全局 | 否 | 个人默认配置 |
~/.claude/CLAUDE.md |
用户全局 | 否 | 跨项目偏好 |
| `~/.claude/commands | agents | skills` | 用户全局 |
~/.claude.json |
用户全局 | 否 | 客户端状态(含 MCP、项目历史),别手改 |
一条容易忽略的:hooks 没有独立配置文件。hooks/ 目录只是放脚本的地方,真正决定"什么时机跑哪个脚本"的是 settings.json 里的 hooks 段。
2. 用户级与项目级:默认值层 + 覆盖层
把用户级想成"默认值",项目级想成"这个仓库的例外"。加载是两层叠加,项目级覆盖同名项;用户级存在的意义是让你在几十个仓库里不用重复写同一套偏好。
| 需求 | 该放哪 | 理由 |
|---|---|---|
| 我习惯用哪个模型 | ~/.claude/settings.json |
个人偏好,与仓库无关 |
这个仓库禁止 git push --force |
项目 settings.json |
团队都要遵守 |
| 我本机 SDK 路径 | settings.local.json |
只对本机有效 |
团队共用的 /review 命令 |
项目 commands/review.md |
随仓库分发 |
我自己的 /scratch 草稿命令 |
~/.claude/commands/ |
不想让别人看到 |
| 跨项目都要用的子代理 | ~/.claude/agents/ |
复用,避免每个仓库复制 |
同名冲突时要知道的顺序:项目级优先于用户级。这也是"我在用户级改了却没生效"的最常见原因——项目里有一份同名的把它压住了。
ls -la .claude/ # 项目里到底有什么
ls -la ~/.claude/ # 用户级有什么
find . -name 'CLAUDE.md' -not -path '*/node_modules/*' # 记忆文件分布
3. CLAUDE.md 与 CLAUDE.local.md 的分工
| 文件 | 归属 | 写什么 | 进 git |
|---|---|---|---|
CLAUDE.md |
团队 | 构建/测试命令、目录约定、禁区 | 是 |
CLAUDE.local.md |
个人 | 本机路径、临时偏好 | 否 |
~/.claude/CLAUDE.md |
个人全局 | 跨项目习惯(提交信息风格等) | 否 |
子目录 CLAUDE.md |
局部 | 某个模块的额外约定 | 视情况 |
CLAUDE.local.md 在当前版本里已被标记为废弃,官方建议的做法是在 CLAUDE.md 里用 import 引入个人文件,而不是继续用 .local.md。迁移前按当前版本核对一次行为,不要只看旧教程。
3.1 怎么写才有效:少而准
记忆文件不是文档仓库。三条经验:
- 只写模型猜不到的。 "用 pnpm 不用 npm"、"生成物在
dist/不要手改"值得写;"本项目使用 TypeScript"不值得,看一眼就知道。 - 约束前置。 长上下文里中段内容更容易被忽略,把"绝对禁止"放在开头,背景说明放后面。
- 可验证。 写"改完跑
make test",不写"注意测试"。可执行的指令能被遵守,形容词不能。
3.2 一份能用的骨架
# 项目约定
## 构建与测试
- 安装依赖:`pnpm install`
- 单测:`pnpm test -- --run <path>`
- 类型检查:`pnpm tsc --noEmit`
## 目录
- `apps/web` 前端;`services/api` 后端;`packages/shared` 公共代码
- `dist/`、`generated/` 是生成物,不手改
## 禁区
- 不改 `migrations/` 下已有文件;新增迁移用 `pnpm db:new`
- 不跨模块深层 import,公共逻辑下沉到 `packages/shared`
- 不提交 `.env*`、任何密钥
一屏能读完是实践上的合理上限(不是官方硬限制)。超过一屏,要么精简,要么拆到子目录的 CLAUDE.md,让它在对应目录才被读。
需要复用的段落用 import 引入,不要复制粘贴——改一处就够:
# 团队约定(续)
@docs/testing.md
@.claude/rules/api-style.md
4. 哪些必须进版本控制,哪些必须 ignore
| 必须进 git | 必须 ignore |
|---|---|
.claude/settings.json |
.claude/settings.local.json |
.claude/commands/、agents/、skills/ |
CLAUDE.local.md |
.claude/hooks/*.sh |
.env、.env.*(保留 .env.example) |
CLAUDE.md、子目录 CLAUDE.md |
~/.claude.json 与整个 ~/.claude/(本就在仓库外) |
.mcp.json(只放 server 声明,不放 token) |
任何含 token 的 MCP 配置副本 |
# Claude Code 个人文件
.claude/settings.local.json
.claude/*.local.json
CLAUDE.local.md
# 密钥
.env
.env.*
!.env.example
判据只有一条:这个文件里有没有"只对我成立"或"不该给别人看"的内容。有,就 local 或 ignore;没有,就提交,让团队共享同一套行为。
5. 多仓库与单体仓库怎么放
| 形态 | 做法 | 注意 |
|---|---|---|
| 单包仓库 | .claude/ 放根目录 |
简单,别在子目录再放一份 |
| monorepo | 根放团队通用,子包放局部 CLAUDE.md |
子目录 settings 是否被读取按当前版本核对,以实测为准 |
| 多仓库共享流程 | 通用命令/子代理放用户级,或打包成插件 | 别复制粘贴到每个仓库,改一次要改十处 |
| 外包/临时项目 | 个人配置一律 settings.local.json |
交付前检查有没有把本地文件提交 |
| 多分支并行开发 | 每个 worktree 有独立工作区,.claude/ 随分支切换 |
未提交的 local 配置不会跟着走 |
repo/
├── CLAUDE.md # 团队通用约定
├── .claude/
│ ├── settings.json # 提交
│ ├── settings.local.json # ignore
│ ├── commands/review.md
│ └── agents/test-fixer.md
├── apps/web/CLAUDE.md # 前端局部约定
└── services/api/CLAUDE.md # 后端局部约定
monorepo 里最容易犯的错是"根目录写一份巨长的 CLAUDE.md"。正确方向相反:根上只留跨模块规则(构建、提交、禁区),模块细节下沉到各自的 CLAUDE.md。
6. 指令为什么会被稀释
上下文是有限预算。CLAUDE.md 越长,单位 token 承载的约束越少;同时,长文本里中段内容被遵循的概率低于开头与结尾,这是长上下文的一般现象,不是哪个版本特有的 bug。
症状很好认:文件里明明写了"不要动 migrations",模型还是动了。处理顺序是先精简、再前移、最后才拆文件:
- 精简:删掉"看一眼代码就知道"的描述性内容;
- 前移:把"禁止"类约束提到文件开头;
- 拆文件:模块级约定放进子目录
CLAUDE.md,不在全局出现。
需要按需触发的长流程(发布步骤、排查手册),更适合放进命令或技能,用的时候才加载,而不是常驻记忆。
7. 排错:六个高频坑
| 现象 | 原因 | 处理 |
|---|---|---|
| 同事拉下来权限规则不生效 | settings.local.json 被提交过,或 ignore 之前已被 track |
git rm --cached .claude/settings.local.json,再确认 ignore |
改了 CLAUDE.md 没反应 |
会话启动时已装载记忆,改动不进当前会话 | 新开会话;用 /memory 核对加载了哪些文件 |
| 明确写了禁止项还是被违反 | 文件太长,约束埋在中间 | 精简 + 前置,必要时拆子目录 |
| 路径找不到 | 写了绝对路径或 ~/ 展开 |
一律用仓库相对路径 |
| hook 不执行 | 脚本没有执行权限,或相对路径基准不是仓库根 | chmod +x;脚本里用 $CLAUDE_PROJECT_DIR 拼路径 |
| 命令/子代理同名冲突 | 用户级与项目级重名 | 看优先级,改名或删掉一份 |
第 5 条值得展开:hook 脚本里写 ./scripts/lint.sh,实际执行时的工作目录不一定是你以为的那个。用 $CLAUDE_PROJECT_DIR 做基准,是唯一稳的写法。
8. 边界:这些内容不该放进 .claude/
| 内容 | 更合适的位置 | 理由 |
|---|---|---|
| 密钥、token | 环境变量或密钥管理器 | 任何提交过的密钥都算已泄露 |
| 团队流程长文档 | README / wiki | 记忆文件里塞文档只会稀释指令 |
| 需要强制执行的合规规则 | 企业管理策略 | 个人配置改得动,策略改不动 |
| 一次性任务描述 | 直接在对话里说 | 固化成文件反而增加维护成本 |
| 大段代码示例 | 仓库里的真实文件 + 路径引用 | 复制的示例会过期 |
| 个人临时开关 | settings.local.json |
别让同事继承你的实验 |
9. 自测
settings.json与settings.local.json的核心区别是什么,谁该进 git?- 用户级和项目级放了同名命令,哪个生效?
CLAUDE.md里"不要动 migrations"被无视,最先该做哪两件事?- hook 的触发配置写在哪,
hooks/目录本身承担什么角色? - monorepo 里根目录的
CLAUDE.md应该写什么、不该写什么? - 一个文件已经提交过又被加进
.gitignore,为什么 ignore 不生效,怎么修?