mcpField
V
verify-cli-run

verify-cli-run

github.com/reticlehq测试与质量1.3k99 次安装今天更新
代码仓库

关于 verify-cli-run

一种无需额外工具的 CLI 验证技能,适合需要确认命令实际产生了什么变化的开发者。它预先声明预期结果,再通过文件快照和差异判断,并将证据不足与失败区分开来。

主要功能

  1. 01运行前写明预期结果
  2. 02比较执行前后的文件状态
  3. 03区分证据与命令自述
  4. 04给出是、否、未知或无责结论
  5. 05记录未观测到的范围

适用场景

  1. 01验证构建是否改写目标文件
  2. 02检查迁移是否只创建预期文件
  3. 03核实 CLI 编辑的代码变化
  4. 04判断远程操作是否需要向服务端确认

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/reticlehq/reticle --skill verify-cli-run

安装后在 Claude Code 中输入/verify-cli-run确认已加载。

详细介绍

证明命令确实做了它声称的事

命令以 0 退出,并打印了 ✓ done。你知道该工具进入了自己的成功分支,却不知道实际发生了什么。

这就是验证 CLI 的全部问题:人人都会检查的两件事,都是工具在描述自己。 退出代码由执行操作的同一段代码决定。输出也是它写的。工具对自己所做之事判断有误时,这两方面都会一致地出错。正因如此,它们一致并不能证明任何事。

这项技能无需安装任何工具。它只需要四条规则和一些 git。

决定一切的规则

证明后果的证据,不能来自执行操作的对象。

使用事实前,先给每项事实评级:

你掌握的信息等级能证明命令成功了吗?
磁盘上的文件,运行前后对比后果能。 文件系统决定写入是否落地,而不是工具
事后向 API 发起请求并得到响应后果能。 回答你的是另一方,而不是工具
退出代码存在性不能。它由工具决定
stdout / stderr上下文不能。它由工具写入
工具自己的 --verbose 报告上下文不能

底部三行中的所有信息都是真实信息,但没有一项是证据。用它们解释判定,绝不要用它们得出判定。

1. 运行前先说清后果

这是整个方法。在看到输出后写下的预期,总能被说成符合实际发生的事;提前写下的预期,只可能达成或未达成。

运行命令前,先在记录中用一行写下:

EXPECT: dist/index.js is rewritten, and no file outside dist/ changes.
EXPECT: src/utils.ts gains a function called parseConfig.
EXPECT: the migration creates 3 files under migrations/ and nothing else.

如果无法提前说清某个后果,就无法验证它。如果说不出,就如实说明并停止。这是诚实的 no-fault,不是通过。

2. 先做快照

在 git 仓库中。 这是理想情况,只需一行:

git status --porcelain > /tmp/before.txt

git status --porcelain 是近乎完美的证据渠道:结构化、成本低,而且独立于工具。Git 的索引记录的是实际写入磁盘的内容,而不是工具打算做什么。

不在 git 仓库中,或者路径位于仓库之外:

find <declared-root> -type f -newermt '1970-01-01' -exec shasum -a 256 {} + | sort > /tmp/before.txt

声明监控根目录时遵循三条规则:

  • 除非它们就是目标,否则排除 node_modules、.git/objects 和大型构建缓存。 对整个 node_modules 计算哈希会涉及数万个文件。
  • 明确声明根目录。 工具可以在任何位置写入;你监控的是自己选定的列表。
  • 列表之外的任何位置都是盲区,第 6 步要报告这一点,而不是假装它没有发生。

3. 检查目标是否已经实现

CLI 工作中最常见的假阳性:dist/index.js 已经存在,于是“构建生成了 dist/index.js”就被判定为通过,即使这次重建什么也没做。

运行前,检查预期后果是否已经成立。如果已经成立,命令就无法证明它。要么选择命令会改变的后果,要么先删除该产物,并说明你这样做了。

4. 运行命令,并分别捕获信息

<the command> > /tmp/out.txt 2> /tmp/err.txt; echo "exit=$?"

保留退出代码,但不要把它当作证据。用它解释判定,并留意它与文件系统状态不一致的情况。

需要了解三点:

  • 退出代码会回绕:sh -c 'exit 256' 会以 0 退出。很大的代码不一定代表你以为的意思。
  • 130 表示 SIGINT,137 通常表示进程被强制终止。这些只说明运行是如何_结束_的,不说明运行是否成功。
  • 有些工具什么也没做却以 0 退出。AI 编码 CLI 经常如此。

5. 检查差异,并以差异作为证据

git status --porcelain > /tmp/after.txt; diff /tmp/before.txt /tmp/after.txt
git diff -- <the paths you expected to change>

现在,只根据这份差异回答你在第 1 步的预期。

每次都值得做两项检查,因为不费吹灰之力:

  • 没有其他改动。 构建时还改写了锁文件,或格式化工具重新格式化了没人要求改动的文件,这些都应当作为发现报告。
  • 检查内容,而不只是文件是否存在。 一个存在的文件,比不上一个包含预期内容的文件。不过请注意其中的不对称性:文件是否出现由文件系统决定;文件中的内容则完全由工具写入。存在与否是比内容更有力的证据。

6. 给出四种判定之一

不是两种。多出的两个选项才是关键:它们能防止把无知一概当作好消息。

判定适用情况
yes你在第 1 步所说的结果能在差异中看到,而且此前尚未成立
no差异与之矛盾,或工具声称的结果未在文件系统中体现
unknown你看不到所需的信息。没有监视任何内容、监视窗口不对,或效果发生在你没有查看的地方
no-fault一切都在监视之中,没有异常,也没有任何声明需要验证

unknown 不是失败,绝不能将其报告为失败。 “我看不到”和“它坏了”会让人朝相反的方向行动:前者表示再检查一次,后者表示去修复某些东西。

并且要说清楚什么证据支持了 yes。"dist/index.js 在磁盘上发生了变化"是一个判定。“构建表示成功”不是;如果你只有这一点,诚实的答案就是 unknown。

7. 说明你没有看到什么

一个无法说明自己漏掉了什么的判定,与一个什么都看到了的判定无法区分。每次都要指出缺口。一行就够:

Did not observe: the network (cannot see whether it called out); writes outside ./src and ./dist;
files created and deleted during the run; anything the tool's child processes did.

有四种缺口始终存在,默认情况下值得一一指出:

  • 网络。 你无法看到命令是否拨号连接了某个端点。如果声称的结果涉及远程操作(推送、部署、PR),文件系统无法回答。
  • 已声明根目录之外的写入。 ~/.config、全局缓存、/tmp。大多数 AI 编码 CLI 每次运行都会写入自己的状态目录。
  • 临时文件。 在运行期间创建后又删除。前后快照只能看到净效果。
  • 子进程以及任何存续时间超过命令的操作:分叉进程、延迟刷新、后台上传。

两项无需成本、却能发现很多问题的操作

运行两次。 构建、格式化、代码生成步骤或迁移,在第二次运行时应产生空差异。每次重复运行都会持续改动内容的工具存在缺陷;检查这一点不需要预期值。

<command> && git status --porcelain > /tmp/a.txt && <command> && git status --porcelain > /tmp/b.txt && diff /tmp/a.txt /tmp/b.txt

向远端核实。 对于网络工具,文件系统帮不上忙,但服务端可以。gh pr view、git ls-remote,或对已部署 URL 执行 curl。这是让另一方回答你,而不是让工具自述,因此属于结果级证据,也是远程操作声明唯一可用的证明。

不要用工具自己的本地记录来替代。git push 会写入 .git/refs/remotes/origin/main,而那个 sha 是git 对其认为远端所说内容的转录。它在磁盘上,但仍然是工具在描述自己。去问远端。

示例:AI 编码 CLI

这是证据最充分的情形,因为这些工具的产品本身就是文件编辑,而它们的执行记录是最不可靠的内容。

EXPECT: src/config.ts gains a function `parseConfig`. Nothing outside src/ changes.

$ git status --porcelain > /tmp/before.txt
$ grep -c "parseConfig" src/config.ts          # already-true check → 0
$ claude -p "add a parseConfig function to src/config.ts" ; echo "exit=$?"
$ git status --porcelain > /tmp/after.txt ; diff /tmp/before.txt /tmp/after.txt
 M src/config.ts
$ git diff src/config.ts | grep "^+.*parseConfig"
+export function parseConfig(raw: string): Config {

VERDICT: yes, bought by the working-tree diff, which is independent of the agent's report.
         It was not already true (grep returned 0 before).
Did not see: writes under ~/.claude (the tool records every session there); the network;
         whether a permission prompt declined an edit it also claimed.

请注意,判定依据不是退出代码,也不是 Agent 声称已添加该函数的那段话。

何时应升级到此技能之外的方法

这种方法有四个局限,而且每个局限都有实际的解决办法:

你需要使用
查看命令是否调用了主机代理,或事后询问另一端
操作交互式面板或 TUI伪终端;shell 无法向其中输入内容
验证 Web 应用 而非 CLIReticle 和 verify-ui-change 技能
生成他人可以重新核查的结论验证产物,而非文字记录

唯一不该做的事

不要因为差异未符合预期,就降低你在第 1 步中写下的预期。看到结果后重写目标,会让验证步骤沦为自我合理化;而且事后从文字记录中看不出来,因为修改后的预期看起来和原始预期完全一样。

如果差异不符合预期,这就是发现的问题。请如实报告。

评论

支持 Markdown