配置实战——settings.json 权限 + CLAUDE.md + Rules 懒加载

配置实战------settings.json 权限 + CLAUDE.md + Rules 懒加载

前两篇讲了"怎么操作 Claude Code"和"怎么编排工作流"。这篇讲怎么配置它------让 Claude Code 一启动就懂你的项目、只做你允许的事、在 monorepo 里不乱加载无关规则。

素材来自 shanraisshan/claude-code-best-practice 仓库的真实配置。


Part 1: settings.json 权限------allow / deny / ask 三区模型

权限系统是 settings.json 的核心。每次 Claude Code 调用工具(Bash、Write、WebFetch 等),都要过这道门禁:

json 复制代码
{
  "permissions": {
    "allow": ["Bash(npm test:*)", "Bash(npm run lint:*)", "Read(*)"],
    "deny": ["Bash(curl:*)", "Bash(rm -rf:*)", "Bash(git push --force:*)"],
    "ask": ["WebFetch(*)", "Bash(npm install:*)"]
  }
}

三个区域的语义:

区域 行为 用途
allow 自动放行,不弹窗 你每次都同意的安全操作(跑测试、读文件)
deny 直接拒绝,模型看不到这个选项 绝对不能做的操作(删库、强制推送)
ask 弹窗问你 需要你判断的操作(装依赖、调外部 API)

优先级:deny > ask > allow。 如果一个操作同时匹配 deny 和 allow,deny 赢。这条规则至关重要------你不会因为 allow 规则太宽而意外放行危险操作。


实战配置:一个生产可用的权限模板

json 复制代码
{
  "permissions": {
    "allow": [
      "Read(*)",
      "Edit(*)",
      "Bash(npm test:*)",
      "Bash(npm run lint:*)",
      "Bash(npm run build:*)",
      "Bash(npx tsc:*)",
      "Bash(git status:*)",
      "Bash(git diff:*)",
      "Bash(git log:*)",
      "Bash(git branch:*)",
      "Bash(git add:*)",
      "Bash(git commit:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(git push --force:*)",
      "Bash(git reset --hard:*)",
      "Bash(curl:*)",
      "Bash(wget:*)"
    ],
    "ask": [
      "WebFetch(*)",
      "Bash(npm install:*)",
      "Bash
(git push:*)",
      "Bash(git pull:*)",
      "Bash(npm publish:*)"
    ]
  }
}

几个设计原则:

allow 不是越宽越好。 "Bash(*)" 放 allow 里等于没设权限。每个 allow 规则应该精确到你确认安全的命令。

deny 放操作后果不可逆的命令。 rm -rfpush --forcereset --hard------这些操作即使你误点了 allow,也来不及后悔。

ask 放需要你判断上下文的操作。 WebFetch 调外部 API 可能泄露代码、npm install 可能引入恶意包------你需要在每次触发时看一眼。


连接第 2 篇:跨层权限收窄模式

第 2 篇展示了 Command → Agent → Skill 三层编排中工具白名单逐层收窄的安全设计:

css 复制代码
Command 层:  Agent, Skill(不能直接 Bash、WebFetch)
Agent 层:    Read, Skill(不能直接 Bash)
Skill 层:    WebFetch(*) 或 Write(*)(权限精确到单个操作)

这个模式在 settings.json 的权限配置里同样生效------你可以给不同 agent 配置不同的 allowed-tools,确保它们只能做设计范围内的操作。settings.json 的 deny 列表则是全局底线------无论哪一层都不能越过。


Part 2: CLAUDE.md------让 Claude Code 一启动就懂你的项目

它应该写什么

CLAUDE.md 是 Claude Code 启动时加载的指令文件。它可以有多个(从祖先目录往下),但项目根目录那个是核心。一个好的 CLAUDE.md 不超过 200 行,只写三样东西:

1. Claude 不知道但需要知道的。 技术栈、命令、项目约定------这些是你项目特有的,Claude 的训练数据里没有。

2. Claude 知道但可能搞错的。 比如你说"用 named export 不用 default export"------Claude 两种都会写,但你的项目只要一种。这是纠正默认行为,不是教它基础知识。

3. 绝对不能做的。 .env 不能 commit、database schema 不能随便改、依赖不能随便加------这些是项目的"宪法"。

它不应该写什么

  • 基础知识("React 组件应该用 hooks"------Claude 本来就知道)
  • 模糊建议("写出高质量的代码"------这句话没有任何约束力)
  • 太长(超过 200 行 Claude 开始忽略后面的内容)

实战模板:一个 60 行的 CLAUDE.md

markdown 复制代码
# 项目:[你的项目名]

## 技术栈
- Frontend: React 18, TypeScript 5, Vite
- Backend: Node.js 22, Express, PostgreSQL, Prisma

## 命令
- Build: `npm run build`
- Test: `npm test -- --coverage`(必须 0 failure)
-
 Lint: `npm run lint --fix`
- Type check: `npx tsc --noEmit`

## 代码约定
- 函数组件 + hooks,不用 class
- named export,不用 default export
- 测试和源文件同级:Button.tsx → Button.test.tsx
- commit message 格式:`type(scope): description`

## 边界
- 永远不 commit .env 或 secrets
- 加依赖前先检查 bundle size
- 改 database schema、CI 配置前先问我
- commit 前必须跑测试和 lint

## 当前项目状态
- 正在做的功能:用户认证重构(auth-v2 分支)
- 已知坑:auth.ts 里有一段旧的 JWT 逻辑,本次重构不碰它

注意"当前项目状态"那段------这是 CLAUDE.md 里最容易被忽略但最有价值的部分。它告诉 Claude "我们在做什么、有什么坑",避免它在你不在的时候自创方案。


Monorepo 场景:祖先加载 + 后代加载

Claude Code 会沿着目录树加载 CLAUDE.md

bash 复制代码
你从 /monorepo/ 启动 Claude:

自动加载:
  ~/.claude/CLAUDE.md           ← 全局规则
  /monorepo/CLAUDE.md           ← 项目根(shared 规则:git 约定、CI 流程)

按需加载(碰到对应目录的文件时才加载):
  /monorepo/packages/frontend/CLAUDE.md  ← 前端规则
  /monorepo/packages/backend/CLAUDE.md   ← 后端规则

实战策略: 根目录 CLAUDE.md 放跨项目的共享约定。子目录 CLAUDE.md 放各模块特有的技术栈和 patterns。Claude 处理前端代码时自动带上前端规则,处理后端时自动带上后端规则,互不污染。


Part 3: .claude/rules/------更细粒度的指令控制

CLAUDE.md 的问题是不能按文件类型控制加载。你在 monorepo 里可能希望"TypeScript 文件有统一规范,但不影响其他文件"。

.claude/rules/ 目录 + paths: frontmatter 解决了这个问题:

markdown 复制代码
# .claude/rules/typescript.md
---
paths:
  - "src/**/*.ts"
  - "src/**/*.tsx"
---

## TypeScript 规范

- 优先使用 interface 而非 type(除非需要 union/intersection)
- 函数参数超过 3 个时使用对象参数
- 异步函数返回值始终用 Promise<T>

这个文件只在 Claude 读取 src/**/*.ts 文件时才加载。处理 Go、Python、配置文件时完全不占用 context。

什么时候用 Rules 而不是 CLAUDE.md 当一条规则只对特定文件类型或目录生效时。全局规则放 CLAUDE.md,文件级规则放 Rules。


实战落地:你能直接拿走的东西

完整的 settings.json 模板

把下面内容保存到 .claude/settings.json

json 复制代码
{
  "permissions": {
    "allow": [
      "Read(*)", "Edit(*)",
      "Bash(npm test:*)", "Bash(npm run lint:*)", "Bash(npm run build:*)",
      "Bash(git:status,diff,log,branch,add,commit:*)(!push !reset !rebase)"
    ],
    "deny": [
      "Bash(rm -rf:*)", "Bash(git push --force:*)", "Bash(curl:*)"
    ],
    "ask": [
      "WebFetch(*)", "Bash(npm install:*)", "Bash(npm publish:*)"
    ]
  },
  "attribution": {
    "commit": ""
  }
}

完整的 CLAUDE.md 模板

把上面的 60 行模板保存到项目根目录 CLAUDE.md

Rules 文件模板

bash 复制代码
mkdir -p .claude/rules
markdown 复制代码
# .claude/rules/typescript.md
---
paths:
  - "src/**/*.ts"
  - "src/**/*.tsx"
---

## TypeScript 规范
[你的规范]

验证配置生效

bash 复制代码
# 1. 检查 settings.json 有无语法错误
cat .claude/settings.json | python3 -m json.tool > /dev/null && echo "JSON valid"

# 2. 重启 Claude Code
claude

# 3. 验证 CLAUDE.md 被加载(Claude 应该知道你的项目命令和约定)
# 让它跑一下 npm test------如果不需要你解释怎么跑,说明 CLAUDE.md 生效了

⚠️ CLAUDE.md 需要持续维护。项目加了新模块、换了新技术栈、改了测试命令------这些变更要同步到 CLAUDE.md。如果你发现 Claude 频繁写出不符合约定的代码,第一反应不该是"这模型不行",而是"我的 CLAUDE.md 是不是过时了"。settings.json 的权限列表同理------新加的命令如果反复弹 ask,就加到 allow 里;新发现的危险命令加到 deny 里。


本文素材来源:shanraisshan/claude-code-best-practice