主题
Claude Code 工程化工作流实战
实战系列第 2 篇。不是「Claude Code 功能介绍」,而是把 CLAUDE.md、权限、Hooks、自定义命令组合成一条流水线的实际配置与取舍。所有配置片段均来自日常真实使用,可直接改造复用。
一、为什么默认用法不够
裸用 Claude Code 的典型模式是:开一个会话,口语化描述需求,看着它改完。这在小改动上没问题,但日常迭代会很快遇到三个问题:
- 每次会话都要重新交代项目约定——技术栈、目录结构、No-Go 区,说一遍 5 分钟,一天说 10 遍;
- 危险操作靠人盯着——
rm、git push --force这类命令,靠注意力防线迟早失守; - 重复动作没有沉淀——提交前跑 lint、改完跑测试,每次都要口头提醒。
这三件事分别对应 CLAUDE.md、权限配置和 Hooks。下面逐个给出配置。
二、CLAUDE.md:写给模型的项目「事实清单」
项目根目录的 CLAUDE.md 会在每次会话自动注入。有效的写法不是「介绍项目」,而是只写模型猜不到、且影响代码产出的事实:
markdown
# 项目上下文
## 技术栈与约束
- ESM("type": "module"),禁止 require()
- Node.js 20+,可直接用内置 fetch / test runner
- 配置集中在 docs/.vitepress/config.js,不新建分散配置
## 目录约定
- docs/ 下的 .md 是站点内容源文件,frontmatter 必含 title + date
- scripts/ 下的 Node 脚本独立于站点,改动不影响构建产物
## No-Go 区(禁止修改)
- docs/.vitepress/dist/ 与 cache/ 是构建产物
- .codebuddy/ 是工具目录
## 完成定义(DoD)
- npm run check 与 npm run validate 通过
- 涉及页面新增时,同步更新对应分类 index.md 与 sidebar踩坑 1:CLAUDE.md 不是越长越好。 早期我把它写成了 200 行的项目 wiki,结果模型经常「看到了但不执行」——上下文里塞太多低价值文本,关键约束的信噪比就被稀释了。实测控制在 60 行以内,且 No-Go 区放在最显眼位置,遵循率明显更高。
踩坑 2:事实要具体到可判断对错。 「代码要规范」是废话,「用 2 空格缩进、字符串用单引号」模型才能执行。写完每条规则自问:模型违反这条时,我能客观指出吗?
三、权限配置:把「注意力防线」换成「规则防线」
.claude/settings.json(项目级,提交进 Git)可以声明允许/禁止清单:
json
{
"permissions": {
"allow": [
"Bash(npm run build)",
"Bash(npm run check)",
"Bash(npm run validate)",
"Bash(git diff:*)",
"Bash(git log:*)"
],
"deny": [
"Bash(git push:*)",
"Bash(git reset --hard:*)",
"Bash(npm publish:*)",
"Read(.env*)",
"Read(**/*secret*)"
]
}
}配置哲学是白名单授权低风险动作,黑名单拦截不可逆动作:
git push一律 deny——推送是人工决策,Agent 不该有这个权力;.env一律 deny read——密钥进上下文等于进模型供应商日志;- 允许的构建/校验命令精确到具体脚本,而不是
Bash(npm run:*)一刀切放行。
踩坑 3:deny 规则要覆盖通配。 只写 deny: ["Bash(git push)"] 拦不住 git push origin main --force,要用 Bash(git push:*) 前缀通配。权限规则和 shell 注入是同构问题,按前缀匹配 + 通配收口。
四、Hooks:把口头提醒变成自动执行
Hooks 是在工具调用前后自动执行的 shell 命令。最有价值的一个场景——每次 Claude 编辑 Markdown 后自动校验 frontmatter:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "node scripts/check-frontmatter.js"
}
]
}
]
}
}配套的最小校验脚本:
javascript
// scripts/check-frontmatter.js
// 检查最近一次变更的 docs/*.md 是否包含必需 frontmatter
import { execSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
const readFileSyncSafe = (f) => {
try {
return readFileSync(f, 'utf-8')
} catch {
return ''
}
}
const files = execSync('git diff --name-only --diff-filter=ACM HEAD', {
encoding: 'utf-8'
})
.split('\n')
.filter((f) => f.startsWith('docs/') && f.endsWith('.md'))
let failed = false
for (const file of files) {
const head = readFileSyncSafe(file)?.slice(0, 200) ?? ''
if (!head.startsWith('---')) {
console.error(`[frontmatter] ${file} 缺少 frontmatter`)
failed = true
}
if (!/^\s*date:\s*\d{4}-/.test(head)) {
console.error(`[frontmatter] ${file} 缺少 date 字段`)
failed = true
}
}
process.exit(failed ? 1 : 0)Hook 退出码非 0 时,Claude Code 会把 stderr 反馈给模型,它会自己修复后重试——这正是「口头提醒」做不到的闭环。
踩坑 4:Hook 命令要快。 每次 Edit/Write 都会触发,Hook 里跑一次全量测试(30 秒)会让交互卡成幻灯片。Hook 只做毫秒级的静态校验,重检查留给「完成定义」那一步手动触发或放 CI。
五、自定义命令:沉淀高频动作
.claude/commands/ 下的 Markdown 文件会成为斜杠命令。用得最多的一个是发布前自检:
markdown
<!-- .claude/commands/precommit.md -->
对当前工作区做提交前自检,严格按顺序执行并汇报每步结果:
1. git diff 汇总本次改动
2. npm run check —— 配置校验
3. npm run validate —— 内链校验
4. 检查所有新增/修改的 .md 的 frontmatter 含 title 与真实 date
5. 给出:可提交 / 需修复清单
不要执行 git commit,检查完停下等确认。调用就是一句 /precommit。这类命令的本质是把 DoD 写成可重复执行的提示词——同样的清单,每次手动打字会漏,固化成命令不会。
踩坑 5:命令里要显式写「不要做什么」。 早期版本没写最后一句,模型自检完顺手就 commit 了。Agent 对「完成自检」的指令遵循是字面的,不含「并等待」的默认语义。
六、组合起来的完整流水线
text
CLAUDE.md(项目事实,自动注入)
+
settings.json 权限(危险操作硬拦截)
+
Hooks(编辑后毫秒级校验,自动反馈修复)
+
/precommit(交付前完整 DoD 自检)
↓
人工 review diff → 手动 commit / push分工原则:模型负责生成与自检,人负责不可逆决策。push、发布、数据库变更永远留在人的手上——这不是对模型能力的不信任,而是把审计成本降到最低的架构选择。
参考来源
- Claude Code 官方文档:Memory(CLAUDE.md)
- Claude Code 官方文档:Settings 与权限
- Claude Code 官方文档:Hooks
- Claude Code 官方文档:Slash Commands
作者:yanshaodong · 2026-10-18