每来一个新同事就要口头教一遍"你得先配这个、再关那个"。这篇把我们的配置全部收进仓库,新人 clone 完就能直接用。
可复现前提: Claude Code CLI ≥ 2.0。示例仓库 praxis/cc-team-config 里的配置可以直接拷进你自己的项目,不依赖任何内部服务。
一、配置分三层,别混在一起
| 层级 | 位置 | 放什么 | 是否提交 |
|---|---|---|---|
| 项目 | ./CLAUDE.md |
项目约定、目录结构、构建命令 | 提交 |
| 项目本地 | ./.claude/settings.local.json |
个人的权限偏好 | 不提交 |
| 用户 | ~/.claude/settings.json |
跨项目的个人习惯 | 不提交 |
我们踩过的坑:一开始把个人的权限白名单提交进了仓库,结果每个人的本地改动互相覆盖,PR 里全是无关 diff。分层之后这个问题彻底消失。
二、CLAUDE.md 写什么才有用
写了半年,留下来的只有三类内容:
# 构建与测试
- 装依赖:`pnpm i`(不要用 npm,lockfile 会冲突)
- 跑单测:`pnpm test -- <file>`,全量跑要 6 分钟,改动小就别全跑
- 类型检查:`pnpm typecheck`,提交前必过
# 目录约定
- `src/domain/` 纯逻辑,不允许 import 任何 UI 或 IO
- `src/adapters/` 外部依赖的封装,测试里一律 mock 这一层
# 本项目的雷区
- `legacy/billing/` 正在迁移,改动前先问 @zhangkun
- 时间一律用 UTC 存储,展示层才转时区"雷区"这一节价值最高。 它写的是那些不看代码就不可能知道的事。
反面教材是我们第一版写的"请使用清晰的命名""注意代码质量"——这类话对输出没有任何可测量的影响,删掉之后什么都没变。
三、权限白名单:把安全的命令一次性放行
每次都弹确认框很烦,但全放开又危险。我们的做法是白名单只放只读且无副作用的命令:
{
"permissions": {
"allow": [
"Bash(pnpm test:*)",
"Bash(pnpm typecheck)",
"Bash(git status)",
"Bash(git diff:*)",
"Bash(git log:*)"
],
"deny": [
"Bash(git push:*)",
"Bash(rm -rf:*)"
]
}
}注意 deny 里的 git push:我们明确要求推送必须人来做。
四、共享 MCP 配置
把团队公用的 MCP server 写进项目配置,token 走环境变量:
{
"mcpServers": {
"tickets": {
"command": "node",
"args": ["./tools/mcp/tickets.js"],
"env": { "TICKET_TOKEN": "${TICKET_TOKEN}" }
}
}
}五、复现步骤
git clone git@internal:praxis/cc-team-config.git
cd cc-team-config
cp .env.example .env # 填 token
claude "看一下这个项目的结构,告诉我构建命令是什么"
# 预期:它会引用 CLAUDE.md 里的 pnpm 命令,而不是猜一个 npm run build最后一步的"预期"很重要:如果它回答的是 npm run build,说明 CLAUDE.md 没被读到,通常是文件位置放错了。
评论与复现反馈 3
作为新人现身说法:雷区那节直接省了我两天。之前完全不知道
legacy/billing/不能随便动。这就是写它的初衷。有新的雷区随时提 PR 加进去。
权限白名单那段想确认下:
Bash(pnpm test:*)里的:*是匹配任意参数吗?我们想放行pnpm lint的所有子命令。