Skip to content

Claude Code 工程化工作流实战 ​

实战系列第 2 篇。不是「Claude Code 功能介绍」,而是把 CLAUDE.md、权限、Hooks、自定义命令组合成一条流水线的实际配置与取舍。所有配置片段均来自日常真实使用,可直接改造复用。

一、为什么默认用法不够 ​

裸用 Claude Code 的典型模式是:开一个会话,口语化描述需求,看着它改完。这在小改动上没问题,但日常迭代会很快遇到三个问题:

  1. 每次会话都要重新交代项目约定——技术栈、目录结构、No-Go 区,说一遍 5 分钟,一天说 10 遍;
  2. 危险操作靠人盯着——rm、git push --force 这类命令,靠注意力防线迟早失守;
  3. 重复动作没有沉淀——提交前跑 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、发布、数据库变更永远留在人的手上——这不是对模型能力的不信任,而是把审计成本降到最低的架构选择。

参考来源 ​


作者:yanshaodong · 2026-10-18