用 CLI Agent 跑 TDD 工作流
用 CLI Agent 跑"红 - 绿 - 重构"循环:先写测试 → 让 Agent 写实现 → 自动跑测试 → 验证通过 → 重构的完整工作流
用 CLI Agent 审查 PR 的提示词模板、产出格式、严重度分级与 GitHub PR 的衔接,含本地 diff 与远程 PR 两种场景
内容摘要
代码审查(Code Review)是把"另一个人读一遍你写的代码"这件事制度化。它的目的不是装样子,而是在合并之前抓出三类问题:**会上线就出事的 bug**、**没考虑到的边界情况**、**今天不改将来一定会还回来的技术债**。CLI Agent 做代码审查的核心价值不是替代人,而是把"每次 PR 都跑一遍同样的 12 项检查表"这件事自动化——人类 reviewer 状态好的时候记得 9 项,状态差的时候记得 3 项,CLI Agent 永远是 12 项。
代码审查(Code Review)是把"另一个人读一遍你写的代码"这件事制度化。它的目的不是装样子,而是在合并之前抓出三类问题:会上线就出事的 bug、没考虑到的边界情况、今天不改将来一定会还回来的技术债。CLI Agent 做代码审查的核心价值不是替代人,而是把"每次 PR 都跑一遍同样的 12 项检查表"这件事自动化——人类 reviewer 状态好的时候记得 9 项,状态差的时候记得 3 项,CLI Agent 永远是 12 项。
为什么要在终端里做、而不是直接用平台内置的 AI 审查(GitHub Copilot Code Review、CodeRabbit、Graphite Reviewer 等)?三个原因:
CLAUDE.md、本地 node_modules、.env.example、未提交的草稿、最近 30 条 git log。云端审查只看到推上去的那个 diff,看不到"这个函数其实是上周临时占位准备删的"。REVIEW.md、改 hook,下一秒生效。它和 SonarQube / Coverity / DeepSource 这类静态分析工具也不一样。静态分析是"基于规则匹配",能稳定抓 null pointer、未初始化、SQL 注入这种语法层面问题;CLI Agent 是"基于语义理解",能看出"这个函数名叫 markPaid,但里面没有写数据库 commit"这种规则匹配抓不到的事。两者互补,不互斥——线上 CI 跑 SonarQube 兜底语法层,本地用 CLI Agent 兜底语义层。
| 要素 | 作用 |
|---|---|
| 审查目标 | 一个 git diff,可以来自本地 git diff main...HEAD、也可以来自远程 PR gh pr diff <num> |
| 上下文供给 | CLAUDE.md / REVIEW.md / 仓库目录树 / 改动文件的相邻代码 / git log;上下文越准,假阳性越少 |
| 提示词模板 | 固定结构:变更概览 → 分文件分点评 → 严重度分级 → 推荐动作。结构稳定才方便后续脚本消费 |
| 严重度分级 | Critical / Major / Minor / Nit 四档(业界主流,Claude Code 官方简化为 Important / Nit / Pre-existing 三档) |
| 产出格式 | 终端 Markdown / JSON 两种:人看 Markdown,CI 消费 JSON 做门禁 |
| PR 衔接 | 通过 gh pr comment / gh pr review 把审查结果回写到 GitHub PR 页面 |
注意几件事:
CLAUDE.md / 相邻代码这一步不能省——少了这一步,Agent 会建议你"加个错误处理吧",但你这个函数本来就在外层 try/catch 里。下面给两个最常用的场景。第一个是"PR 还没推上去,先在本地审一遍";第二个是"别人开了个 PR,我用 CLI Agent 帮自己提前过一遍"。两个场景共用同一套提示词模板。
最简单的做法:在仓库根目录起一个 Claude Code 会话,直接喂提示词。
# 1. 看一眼当前分支相对 main 的改动范围
git diff --stat main...HEAD
# 2. 把 diff 喂给 Claude Code 审一遍(也可以直接在交互模式下贴提示词)
claude -p "$(cat <<'EOF'
请对当前分支相对 main 的改动做一次代码审查,按以下结构输出:
## 1. 变更概览
- 一句话讲清这个 PR 在做什么
- 影响面:动了哪些模块/包
- 风险等级(low / medium / high)
## 2. 分文件审查
对每个改动文件按下面格式输出:
### <文件路径>
- [严重度] <一句话问题描述>
- 行号:<L<起>-L<止>>
- 原因:<为什么是问题,引用 CLAUDE.md/规范条款时直接抄原文>
- 建议:<具体改法,能给 diff 就给 diff>
严重度只用四档:Critical / Major / Minor / Nit
- Critical:合并后会上线出事(数据丢失、鉴权绕过、崩溃)
- Major:影响正确性或可维护性(边界没考虑、命名误导、测试缺失)
- Minor:风格/重构建议
- Nit:可改可不改
## 3. 推荐动作
- 必须修改(blocker 列表)
- 建议修改(major/minor 列表)
- 可忽略(nit 列表)
约束:
- 只看 git diff main...HEAD 范围内的改动
- 不要重复 lint/format 工具能抓的问题
- 没有把握的发现宁可不报,假阳性比漏报代价大
- 引用代码时必须给出文件:行号
EOF
)"
预期产出(截选):
## 1. 变更概览
- 给订单接口加上幂等键支持,避免重复下单
- 影响:src/order/service.ts、src/order/repository.ts、对应单测
- 风险等级:medium(涉及钱,但只新增不改老链路)
## 2. 分文件审查
### src/order/service.ts
- [Critical] 幂等键命中后直接返回旧订单,但没校验请求 body 是否一致
- 行号:L88-L102
- 原因:CLAUDE.md 第 4.2 条要求"幂等键 + 不同 payload 视为冲突"
- 建议:命中后比对 payload hash,不一致则返回 409
- [Major] 新加的 `idempotencyKey` 入参没有长度校验
...
## 3. 推荐动作
- 必须修改:service.ts L88-L102(幂等冲突检测)
- 建议修改:service.ts L75 长度校验、repository.ts L40 索引补 unique
- 可忽略:测试用例命名风格
如果你用的是 Claude Code 而不是其它 CLI,可以直接调官方内置的 /review skill,它会跑一套多 agent 并行的审查管线,产出和上面结构基本一致。
把上面的 prompt 包成一个 shell 函数,专门审远程 PR:
# ~/.bashrc 或 ~/.zshrc
review_pr() {
local pr_num="$1"
if [ -z "$pr_num" ]; then
echo "用法: review_pr <PR 号>" >&2; return 1
fi
# 1. 用 gh CLI 拉 PR 元信息和 diff
local pr_meta diff
pr_meta=$(gh pr view "$pr_num" --json title,body,author,baseRefName,headRefName,files)
diff=$(gh pr diff "$pr_num")
# 2. 把 PR 元信息 + diff 一起喂给 Claude Code
claude -p "请审查下面这个 PR:
PR 元信息(JSON):
\`\`\`json
$pr_meta
\`\`\`
完整 diff:
\`\`\`diff
$diff
\`\`\`
按以下结构输出审查结果(结构同本地审查模板):
1. 变更概览
2. 分文件审查(带严重度 Critical/Major/Minor/Nit)
3. 推荐动作
要求引用 PR 标题/描述里声明的目标,判断改动是否对得上 PR 的初衷。
"
}
把审查结果回写到 PR 上,有两种粒度:
# 粒度 1:作为整段 PR 评论回写(最常用,gh pr comment 原生支持)
review_pr 123 | tee /tmp/review.md
gh pr comment 123 --body-file /tmp/review.md
# 粒度 2:走 review 体系(带 approve / request-changes 状态)
gh pr review 123 --comment --body-file /tmp/review.md # 只评论不阻拦
gh pr review 123 --request-changes --body "见上文 Critical 项" # 显式 request changes
gh pr review 123 --approve --body "LGTM, ship it" # 通过
# 粒度 3:行级 inline 评论(gh CLI 原生不支持,需要 gh extension 或 gh api 直调)
gh extension install agynio/gh-pr-review # 一次性安装,提供 inline 评论
官方背景小知识:Anthropic 也在云端提供托管的 Code Review 服务(仓库装 GitHub App 后,PR 一打开就自动跑),它的产出走"🔴 Important / 🟡 Nit / 🟣 Pre-existing"三色严重度,而且会把每次 review 写到名为
Claude Code Review的 check run 里——可以通过gh api repos/OWNER/REPO/check-runs/<id>拉到机器可读的 severity tally。本卡聚焦本地 CLI 自助审查,云端托管和本地 CLI 是同一套思路、不同部署形态。
如果你想做"Critical 数 ≥ 1 就阻断 merge"这类硬门禁,让 Agent 直接产 JSON:
claude -p "审查 git diff main...HEAD,只输出 JSON,不要任何解释文字。schema 如下:
{
\"summary\": \"一句话变更说明\",
\"risk\": \"low|medium|high\",
\"findings\": [
{
\"file\": \"src/foo.ts\",
\"line_start\": 12,
\"line_end\": 18,
\"severity\": \"critical|major|minor|nit\",
\"category\": \"bug|security|perf|style|test|docs\",
\"message\": \"问题描述\",
\"suggestion\": \"具体改法\"
}
]
}" | jq '.findings | map(select(.severity == "critical")) | length'
# 输出 0 → 通过;>0 → CI 退出非 0
| 维度 | 人工审查 | IDE 内 AI 审查(Cursor / Copilot Chat) | CLI Agent 审查(本卡) | 平台内置 AI 审查(GitHub Copilot CR / CodeRabbit) |
|---|---|---|---|---|
| 启动成本 | 高(需要 reviewer 排期) | 低(IDE 内一键) | 低(一条命令) | 零(PR 打开自动跑) |
| 上下文范围 | 看人,资深 reviewer 能联想全仓库 | 当前打开文件 + 编辑器索引的范围 | 全仓库 + git history + CLAUDE.md / REVIEW.md | 推到云端的 diff + 部分仓库快照 |
| 规则可定制性 | 写在团队 wiki 里,靠人记 | 改 IDE 设置 | 改提示词 / REVIEW.md / hook,立即生效 | 网页后台配规则,改完要等同步 |
| 假阳性控制 | 高(reviewer 自带判断) | 中 | 中-高(可写"宁可漏报不要假阳性"约束) | 低-中(默认追求覆盖率) |
| 产出回写到 PR | 直接评论 | 多数无 | 通过 gh pr comment / gh pr review 显式回写 | 自动以 bot 身份回写 |
| 隐私 | 高 | 中(看 IDE 厂商策略) | 高(除调模型 API 外不出本机) | 低(代码上云端 SaaS) |
| 适合场景 | 关键合并、架构变更 | 写代码过程中边写边问 | 推 PR 前自审、本地把关、敏感仓库 | 全员仓库的兜底审查、规模化 |
核心区别一句话:人工审查抓"判断与品味",IDE AI 抓"我正在写的这一段对不对",CLI Agent 抓"这个 PR 整体能不能合",平台内置 AI 抓"所有 PR 都跑一遍的兜底"。这四档不互斥,重要 PR 通常四个都跑一遍。
| 误区 | 准确理解 |
|---|---|
| 以为 CLI Agent 审查可以直接全自动 merge | 永远要保留"人按合并键"这一步。Agent 抓得到 80% 的 bug,但漏掉的 20% 里可能藏的是上线就回滚的事故。Agent 是 reviewer,不是 approver |
| Prompt 写"帮我 review 一下" 就开干 | 这种宽泛 prompt 出的几乎都是泛泛而谈的客套话。一定要在 prompt 里指定"产出结构 / 严重度分级 / 假阳性约束 / 引用代码必须带行号",结构越死,产出越能用 |
| 只喂 diff,不喂上下文 | 只看 diff 等于让 reviewer 蒙眼看代码片段。CLAUDE.md、REVIEW.md、改动文件的整文件、最近 5 条相关 commit 都要给。Claude Code 内置 /review 之所以质量高,就是因为它默认会扫 CLAUDE.md 和相邻代码 |
| 漏审 git history | 改动看似无害,但配合最近几次 commit 才看得出问题(例如这次新加的字段刚好和上周删的字段同名)。审查时跑一句 git log --oneline -20 -- <改动文件> 喂给 Agent 是低成本高收益的动作 |
| 不分严重度,把所有发现一锅炖 | 没分级的审查报告等于没审。开发者看到 20 条评论会直接 close 掉。强制按 Critical / Major / Minor / Nit 分四档,并在末尾给"必须修 / 建议修 / 可忽略"三组动作清单 |
| 把 CLI Agent 审查替代静态分析 / 测试 | 不是替代关系。Lint / 类型检查 / 单测 / 静态分析这些 CI 该跑还是要跑,CLI Agent 审的是"语义层面 reviewer 才能抓的问题"。让 Agent 抓 lint 能抓的问题是浪费 token |
| 一次喂超大 diff(几千行) | 超过模型上下文窗口或者超出有效注意力范围,产出质量会断崖式下跌。大 PR 应该按文件 / 按 commit 拆分多次审查,最后做一次"跨文件耦合"的总结审查 |
| 优势 | 劣势 |
|---|---|
| 结构稳定:同一套 prompt 模板每次产出格式一致,下游脚本/CI 可以直接消费 | token 成本不便宜:一个中等 PR 的审查动辄几千到几万 token,跑 100 个 PR 是真金白银。Anthropic 官方托管 Code Review 公开报价是每次 PR 平均 $15-25 |
| 能读全量本地上下文:CLAUDE.md、未提交草稿、git log 都在,云端审查看不到 | 依赖提示词质量:prompt 写得糙,产出就泛泛而谈。提示词模板需要团队沉淀,不是一次写完不动 |
| 隐私可控:除调模型 API 外,diff 不出本机;敏感仓库可以走自建 / 本地推理 | 大 PR 表现差:超过几千行的 diff 会让 Agent 抓不住重点,需要拆分审查再做总结 |
规则可即时迭代:发现假阳性多了就改 REVIEW.md,下一次生效 | 难处理跨仓库 / 跨服务的影响:CLI Agent 默认只看一个仓库,微服务架构下"这个改动会不会影响下游"判断不出来 |
| 和 gh / git 原生工具链无缝衔接:管道一接,审查结果直接回写 PR | 无 GUI 行级评论:gh pr comment 只能整段回写,行级 inline 要装 gh extension 或直调 gh api |
| 可组合到自定义 workflow:可作为 PreToolUse / Stop hook 的一环,做"每次 commit 前自审" | 不能替代人对架构和品味的判断:Agent 抓得到 bug,抓不到"这个抽象本来就不该存在" |
参考答案:
通常远程 gh pr diff 审查的产出会更好,但前提是 prompt 里把 PR 元信息(标题、描述、author、base/head 分支)一起喂给 Agent。原因有两点:
但反过来,本地 diff 审查也有独家优势:能看到未提交的工作树状态和最近本地 commit。比如你刚改了一半,跑本地审查能在推之前抓到问题,省一次推-审-改-推循环。
实践上的最佳节奏是:写完先跑本地 diff 审查→修一遍→推上去→再跑一次远程 PR 审查(这次带上 PR 描述)→修第二遍→请人 review。
参考答案:
技术上可行,但默认不建议直接做硬门禁,更稳的做法是软门禁 + 人工 override 通道。
可行的实现路径:
claude -p "...只输出 JSON..." 让 Agent 产 JSON 报告。jq 提取 findings | map(select(.severity == "critical")) | length。需要避开的坑:
temperature: 0),要么允许"重跑一次取并集"。REVIEW.md 里写明"在 commit message 加 [skip-review] 可绕过",或在 PR 评论 @claude review skip 触发跳过。否则真出现假阳性时只能改 prompt 重跑,整个团队会被堵住。REVIEW.md 里写清"在我们仓库,Critical 仅指:1) 数据丢失 2) 鉴权绕过 3) 上线即崩",越窄越好。更稳的折衷做法:CI 跑审查 → 输出 JSON → 写到 PR 评论里 → 不阻断 merge。让人看着这份报告决定要不要改、要不要合。这是 Anthropic 官方托管 Code Review 默认的做法(check run 永远是 neutral 结论),值得借鉴。
参考答案:
三者的本质能力不同,分工应该按"抓什么、什么时候抓、抓不到怎么办"来切:
| 角色 | 抓什么 | 何时跑 | 失效兜底 |
|---|---|---|---|
| 静态分析(SonarQube/CodeQL) | 规则可枚举的语法/安全模式(SQL 注入、空指针、未使用变量、CWE Top 25 等) | 每次 push 跑 CI | Agent 审查 + 人工 |
| CLI Agent 审查 | 语义层判断("函数名和实现不一致"、"边界没考虑"、"违反 CLAUDE.md 中的领域约定") | PR open / synchronize / 本地推前 | 人工 |
| 人工审查 | 架构、品味、领域判断("这个抽象不该存在"、"和上周那个改动有冲突") | 重要 PR / 关键模块 | 无 |
避免打架的几条设计原则:
最后一条非技术原则:CLI Agent 审查不能用来 PUA 人类 reviewer。"Agent 都 LGTM 了你为什么还挑刺"是反模式。Agent 的判断不替代 reviewer 的最终签字权,三者关系是"互相补盲",不是"上级评下级"。
code-review plugin 仓库(多 agent 并行 review 提示词、产出格式、假阳性过滤策略):https://github.com/anthropics/claude-code/blob/main/plugins/code-review/commands/code-review.mdgh pr review 手册(approve / request-changes / comment 三种 review 类型与 flag 用法):https://cli.github.com/manual/gh_pr_reviewgh pr 全部子命令索引(pr view / pr diff / pr comment / pr review / pr checks):https://cli.github.com/manual/gh_prgh pr comment 手册(PR 整段评论的 --body / --body-file 用法):https://cli.github.com/manual/gh_pr_commentgh-pr-review 扩展(CLI 行级 inline review 与 LLM 适配,弥补 gh pr comment 不支持行级评论):https://github.com/agynio/gh-pr-review优先展示同分类且标签更接近的内容,方便继续串联学习。
用 CLI Agent 跑"红 - 绿 - 重构"循环:先写测试 → 让 Agent 写实现 → 自动跑测试 → 验证通过 → 重构的完整工作流
用 CLI Agent 系统化定位 bug 的五步走 —— 复现 → 隔离 → root cause → fix → 回归,附 prompt 模板与避坑