返回博客

小白如何入门 Claude Code 的 /goal 命令

作者 约 3 分钟读完

Claude Code v2.1.139 新增的 /goal 命令,能让 AI 在客观可验证的条件下持续工作直到完成。本文用 TinyPA 项目的三个实战案例,讲清楚怎么写一个能跑通的条件、几个真踩到的坑、以及什么时候别用。


小白如何入门 Claude Code 的 /goal 命令

Claude Code 最近新出了个 /goal 命令,我结合 TinyPA 这个项目使用了下,整理出一篇小白也能看得懂的入门级 goal 命令使用指南。

goal 命令到底是什么

官方对这个命令的介绍:设定一个完成条件,Claude 会一直工作直到条件被满足。

平时和 Claude Code 的对话是一来一回:你说一句,它做一段,停下来等下一句。但有些任务你心里清楚"做完的标志是什么"——pnpm build 退出 0、某个 grep 返空、issue 队列清空。有些任务每次干完一轮回来问你确认,次数多了觉得挺烦的。

/goal 干的事就是:你给一个客观可验证的终止条件,每个 turn 跑完之后,Claude Code 派一个小模型(默认 Haiku)来判断条件满没满足;没满足自动开下一轮,满足了自动停。

整个过程不用插手。

/goal 的工作循环

(题外话,它和 /loop、Stop hook 的区别:/loop 是按时间间隔触发,Stop hook 是写在 settings 里的常驻规则,/goal 本质上是 Stop hook 的一个 session 内快捷方式。)

如何开始使用?

下面,我将以三个使用例子来说明如何使用 /goal 命令。

清 README 和 .env.example

/goal README.md 不再出现 NVIDIA NIM / Gemma 4 字样,
技术栈和环境变量章节准确反映当前的 Gemini provider;
grep -i "nim\|gemma" 在 README 和 .env.example 里返回为空

开始执行命令后,Claude Code 立刻 grep 残留 → 改"技术栈"段 → 改"环境变量"段 → 再 grep 验证 → 退出。两轮搞定,我什么都没做。这一类任务最适合 /goal:改动局部,终止信号是一个简单的 grep。

清整个仓库的残留 + 重命名 + 改导入

/goal 全仓 grep -riE "nim|gemma|nvidia" 排除 node_modules 等后返回为空;
重命名 lib/llm/gemma.ts 为 lib/llm/index.ts 并更新所有 import;
删除 test-nim.mjs; pnpm build 退出 0

它建了个 9 项的小任务清单(删文件、重命名、改导入、清各文件的 NIM 残留、改 DEPLOY.md、跑 build),一项一项推进。

这次出了个有意思的事故:build 通过了,文件改完了,但 evaluator 说"条件未达成"。

为什么?我那条 grep 没加 -w 词边界,结果它把 animation、animate-leaf-sway、Minimal 这些单词里的 "nim" 子串当成残留报了 21 条。NIM/Gemma/NVIDIA 的引用早就清干净了,但条件字面上没满足。

没跟 evaluator 较劲——硬要清这 21 条得把 Tailwind 的 animate-* class 全部重命名,那是真破坏。直接 /goal clear 手动停。

写 grep 条件记得加 -w,否则人话和机器话会对不上。

迁移 ESLint 配置

我发现 pnpm lint 其实是坏的——next lint 在 Next.js 16 弃用了,跑起来直接进交互式 wizard。让 Claude Code:

/goal pnpm lint 退出 0;
package.json 的 lint script 改成调用 ESLint CLI 而不是 next lint;
eslint.config.mjs 存在且包含 @next/eslint-plugin-next;
不引入新的 lint error

这轮挺折腾——@next/eslint-plugin-next v16 改了 API,flat config 从 flatConfig.coreWebVitals 搬到了 configs["core-web-vitals"],按旧文档写就报 Cannot read properties of undefined。Claude Code 自己 node -e 探了下 module 结构,发现 v16 的 default 导出改了,改完后又冒出两个 lint error,顺手修了。

全程我只回了一个"收到"。最终 lint 通过、build 通过,evaluator 一次性放行。

怎么写一个能跑通的条件

文档说一个好条件有三块:一个可衡量的结束状态、一个明确的检查方式、不能改变的约束。三块都写进去 evaluator 才能稳定判断。

一个好条件的三块结构

今天最顺的那个 goal 长这样:

pnpm lint 退出 0;
lint script 改成 eslint CLI;
eslint.config.mjs 存在且 import @next/eslint-plugin-next;
不引入新的 lint error

四条都是客观能验证的,没有"代码质量提升"这种主观词。反过来,像"把代码重构得更清晰一点"这种 evaluator 根本没法判断,会无限刷下去——这种就别用 /goal,正常对话就行。

几个我真的踩到的坑

加个 turn 上限。在条件里写 or stop after 15 turns,避免它哪天卡死刷 200 轮。

词边界要写清楚。grep -i "nim" 会匹配到 animation,用 -w 或者干脆 \b(nim|gemma)\b。

evaluator 看不到磁盘,只看对话。你必须让 Claude Code 把验证命令的输出 surface 出来——比如让它跑 pnpm build && echo "build_exit=$?",结果显示在 transcript 里 evaluator 才看得到。如果它在背后默默跑了命令但没贴出来,evaluator 直接判"证据不足"再开一轮。

条件写错了 /goal clear 就完事,不要硬扛着让 Claude 把不该改的东西也改了。

一个 session 只能有一个 active goal,想换条件重新 set 就行,旧的会被替换。

什么时候别用

任务的"做完"是主观的——比如"重构得好看一点",这种走普通对话。

你需要逐步确认每一步——/goal 一旦开了就闷头干,你看到结果时它已经做完 8 件事了。

任务很短——一个 turn 能干完的事不需要 /goal,正常对话更快。