mcpField
R
review-deep

review-deep

github.com/jimweller测试与质量45 次安装
代码仓库

关于 review-deep

review-deep 是一项代码审查技能,使用三个模型从九个视角并行审查,并另行扫描文件覆盖情况。它面向代码库审查,提供核验审查运行、文件覆盖、引用和问题的流程。

主要功能

  1. 01三个模型覆盖九个审查视角
  2. 02并行运行 27 个审查者
  3. 03单独执行逐文件覆盖扫描
  4. 04核验运行状态、覆盖率与引用
  5. 05汇总不同审查者的发现

适用场景

  1. 01从多个视角审查代码库
  2. 02检查审查区域和文件是否覆盖
  3. 03汇总前验证问题引用

MCP 相关

该技能要求启用 Serena 作为审查者的导航 MCP 服务。文档还说明,因架构不兼容,Gemini 审查分支会禁用 `researcher` MCP 服务。

安装

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

$npx skills add https://github.com/jimweller/clanker-skills --skill review-deep

安装后在 Claude Code 中输入/review-deep确认已加载。

详细介绍

STARTER_CHARACTER = 🕵️‍♂️

代码审查命令

从 9 个角度,同时对照 3 个模型审查代码库。每位审查者都是独立的 opencode run 进程,因此 27 个进程会并发运行。每位审查者都通过 Serena 浏览实时工作树,并写入自己的发现文件。

不会打包任何内容。没有编排器进程,也没有 task 扇出。审查者读取磁盘上的代码,因此引用指向真实文件中的真实行。

一个 ocr scan 会同时运行。它会在自己的对话中审查每个可审查文件,因此覆盖整个仓库,但无法看到跨越两个文件的内容。审查者能够跨文件查看,并自行选择要读取的内容。两者各自覆盖对方的盲点。

参数

如果用户在调用时提供了路径,则将其视为相对于仓库根目录的目标目录。否则审查整个仓库。

模型

标签模型 ID
openaiopenai/gpt-6-sol
geminigoogle/gemini-3.8-flash
claudeaz-anthropic/claude-opus-5-5

OCR 覆盖检查使用 ~/.opencodereview/config.json 中的提供商和模型。

每个 ID 都必须存在于 configs/opencode/opencode.json 中对应的提供商下,并且也必须出现在该提供商的 whitelist 中。任一处缺少 ID,都会导致对应分支报错 Model not found,且只会以 NDJSON 中的 error 事件呈现。

jq -r '.provider | to_entries[] | .key as $p | .value.models | keys[] | "\($p)/\(.)"' ~/.config/opencode/opencode.json

gemini 分支有意使用 Flash 模型。Google 没有发布高于 gemini-3.1-pro-preview 的 pro 模型,因此目前最新的 Google 模型是 gemini-3.8-flash,比最新的 pro 高出 7 个次版本。在一个包含 1070 个文件的仓库上进行的一次安全审查中,flash 在相同的 Agent 和提示词下,给出了 11 条发现,而 pro 给出了 6 条,消耗的 token 大约是 pro 的两倍。这只是一次比较,并非基准测试。

花了两天才学会的规则

每次运行 opencode run 时,都要将标准输入重定向到 /dev/null。否则,进程会在创建会话前阻塞,写入 0 字节,日志也只会记录到 message=init,并且只有被终止后才会退出。opencode run 会先读取标准输入直至 EOF,然后才执行其他操作,因此任何让标准输入保持打开状态的启动器都会使它挂起;后台作业、CI 步骤和 Agent 测试框架也都如此。这是 anomalyco/opencode 的问题 #38723,针对 1.18.25 提出,并在此处的 1.18.20 上复现。

这种挂起看起来像是间歇性问题,实际上并非如此。是否挂起取决于启动器是否关闭标准输入。3 位报告者在 macOS arm64、Linux aarch64 和 Windows 上都进行了确定性测试:不重定向时,5 次中有 5 次挂起;重定向时,10 次中有 5 至 10 次成功。

此文件的早期版本禁止使用 timeout 包装 opencode run,并将 44% 的失败率归咎于该包装方式。这是错误的。即使完全不使用 timeout,也能复现卡顿;上游自己的复现脚本也使用了 timeout 120。移除所有限制,才让一个单行的标准输入问题演变成持续数日的调查,因此现在第 2b 步会检查每位审查者是否有会话记录。

不要对审查者设置 token 上限或轮次上限。审查大型仓库需要多久,就审查多久。

必须在 ~/.config/opencode/opencode.json 中启用 Serena,并使用 --project-from-cwd 启动。审查者只能通过它浏览代码。--dir 指向目标目录,以便 Serena 遍历并找到项目。

为什么 researcher 被禁用

configs/opencode/opencode.json 将 researcher MCP 服务设为 "enabled": false,审核 Agent 也会再次禁用它的工具。Google 会拒绝其架构。

researcher 将可选数组声明为联合类型 ["null","array"],这符合 JSON Schema。Anthropic 和 OpenAI 都接受这种写法。Google 会将 JSON Schema 转换成自己的 OpenAPI 子集 proto,把联合类型转换为 any_of,从数组分支中丢弃 items,然后又因 any_of[0].items: missing field 拒绝自己的输出。gemini 分支会在 5 秒内失败,只产生一个 error 事件和 5712 字节的 NDJSON。

任何使用这种模式的 MCP 服务都会以同样的方式导致 Google 分支失败。如果 gemini 分支开始报错 field predicate failed: $type == Type.ARRAY,请找到拥有所命名属性的服务并将其禁用。

流程

步骤 1:确定目标并做好准备
PROJECT_ROOT=$(git rev-parse --show-toplevel)
PROJECT_ROOT=$(cd -P "$PROJECT_ROOT" && pwd -P)
TARGET_PATH="<user-provided path or empty>"
[ -z "$TARGET_PATH" ] && TARGET_PATH="$PROJECT_ROOT"

TARGET_PATH=$(cd -P "$TARGET_PATH" 2>/dev/null && pwd -P) || { echo "TARGET_PATH does not exist"; exit 1; }
case "$TARGET_PATH" in
  "$PROJECT_ROOT"|"$PROJECT_ROOT"/*) ;;
  *) echo "TARGET_PATH escapes PROJECT_ROOT"; exit 1 ;;
esac

TARGET_NAME=$(basename "$TARGET_PATH")
[ "$TARGET_PATH" = "$PROJECT_ROOT" ] && TARGET_NAME="repo"

STATE_DIR="$PROJECT_ROOT/.llmtmp/review-deep"
mkdir -p "$STATE_DIR"

find "$STATE_DIR" -maxdepth 1 -name '*.md' -delete
find "$STATE_DIR" -maxdepth 1 -name 'raw-*.ndjson' -delete
find "$STATE_DIR" -maxdepth 1 \( -name 'ocr-scan.*' -o -name 'ledger.txt' -o -name 'opened.txt' \) -delete

SCOPE=""
[ "$TARGET_PATH" != "$PROJECT_ROOT" ] && SCOPE="--path ${TARGET_PATH#"$PROJECT_ROOT"/}"
ocr scan --preview --format json --repo "$PROJECT_ROOT" $SCOPE 2>/dev/null \
  | jq -r '.files[] | select(.will_review) | .path' > "$STATE_DIR/ledger.txt"

echo "PROJECT_ROOT=$PROJECT_ROOT"
echo "TARGET_PATH=$TARGET_PATH"
echo "TARGET_NAME=$TARGET_NAME"
echo "STATE_DIR=$STATE_DIR"
echo "LEDGER=$(wc -l < "$STATE_DIR/ledger.txt" | tr -d ' ') files"

ocr scan --preview 会列出所有可审核文件,且不会调用模型。ledger.txt 是每位审核者必须检查的文件清单,也是步骤 3 中计算覆盖率的分母。

使用 find -delete,而不是 rm -f。PATH 上的 safe-rm 替代程序会将路径移至废纸篓;即使使用 -f,路径不存在时也会以非零状态退出,这会导致首次运行时清理失败。

步骤 2:派发 27 位审核者并执行覆盖率检查

一个 bash 代码块会启动全部 27 位审核者和 ocr scan,然后等待它们完成。替换为步骤 1 输出的值。

AREAS="security architecture solid correctness testing ops performance quality data"
MODELS="openai:openai/gpt-6-sol gemini:google/gemini-3.8-flash claude:az-anthropic/claude-opus-5-5"
SCOPE=""
[ "$TARGET_PATH" != "$PROJECT_ROOT" ] && SCOPE="--path ${TARGET_PATH#"$PROJECT_ROOT"/}"
PIDS=""

ocr scan --audience agent --repo "$PROJECT_ROOT" $SCOPE --max-tokens 1000000 \
  --format json --output "$STATE_DIR/ocr-scan.json" \
  < /dev/null > /dev/null 2> "$STATE_DIR/ocr-scan.log" &
PIDS="$PIDS $!"

for entry in $MODELS; do
  label="${entry%%:*}"
  model="${entry#*:}"
  for area in $AREAS; do
    out="$STATE_DIR/$label-$area.md"
    prompt="Review every file under $TARGET_PATH.

Navigate with serena symbolic tools: get_symbols_overview to map a file,
find_symbol to read a definition, find_referencing_symbols to find callers,
find_declaration and find_implementations to resolve a usage. Read a whole
file only when symbolic navigation cannot answer the question.

Cite path:line from the live file and name the enclosing function, method,
or type. A line you saw counts as verified, and find_symbol returns a symbol's
line range. When you cannot verify the exact line, cite the nearest line you
saw and end the finding with `line unconfirmed`. Never drop a real defect
because its line number is uncertain.

LEDGER_PATH: $STATE_DIR/ledger.txt

LEDGER_PATH lists the reviewable source files, one path per line. Read it
first and cover every file it lists.

OUTPUT_PATH: $out

Write your findings to OUTPUT_PATH. Writing that file is mandatory and is
how your work is delivered. Do not return findings as your response."

    opencode run \
      --agent "reviewer-$area" \
      -m "$model" \
      --format json \
      --dir "$TARGET_PATH" \
      --title "Review $label $area" \
      "$prompt" \
      < /dev/null \
      > "$STATE_DIR/raw-$label-$area.ndjson" 2>"$STATE_DIR/$label-$area.log" &
    PIDS="$PIDS $!"
  done
done

echo "launched $(echo $PIDS | wc -w | tr -d ' ') processes, 27 reviewers and 1 ocr scan"

wait

echo "done, $(ls "$STATE_DIR"/*-*.md 2>/dev/null | wc -l | tr -d ' ') files written"

预计总耗时会接近最慢的单个审核者,而不是所有审核者耗时的总和。在一个有 22 个文件的 TypeScript 仓库中,单个审核者耗时 148 秒。

审核者使用 apply_patch 写入自己的文件。审核者 Agent 允许使用该工具,并禁止 bash、webfetch、context7_*、repomix_*、researcher_* 以及 Serena 的四类写入工具。它们以 mode: primary 运行,因为通过 --agent 选择子 Agent 时,opencode 会忽略其 tools 配置块。

审核者不得调用 task。在一个有 1070 个文件的仓库中,27 位审核者里有两位启动了一个始终没有返回的子 Agent;TaskTool 会以前台方式运行子 Agent,并使用阻塞式 yield,因此父级进程会挂起,步骤 2 的 wait 也会随之挂起。这些 Agent 会设置 tools.task: false 和 permission.task: {"*": deny}。修复后,这两位审核者都顺利完成,且没有调用 task。

没有任何机制能阻止同时运行两次。步骤 2 会写入固定路径,且没有锁,因此同一仓库中若启动第二次运行,就会交错写入同一个 NDJSON 文件,并覆盖同一个输出文件。2026-09-22,这导致两次尝试的结果损坏。派发任务前,请检查是否已有正在运行的任务。

提供商的限制不是瓶颈。OpenAI 报告的每分钟 token 数为 40,000,000,Azure Foundry 为 15,000,000。

步骤 2b:确认每位审核者都已启动

在步骤 2 仍在等待时,单独以前台调用运行此命令。未创建会话的审核者不会产生任何内容,之后也不会产生。

sleep 30
DB="$HOME/.local/share/opencode/opencode.db"
for label in openai gemini claude; do
  for area in security architecture solid correctness testing ops performance quality data; do
    n=$(sqlite3 -readonly "$DB" "SELECT count(*) FROM session WHERE title='Review $label $area';")
    [ "$n" -eq 0 ] && echo "NO SESSION $label $area"
  done
done
echo "checked 27"

正常运行的审核者会在启动后约 3 秒内写入自己的会话行。如果此处列出了停滞的审核者,请终止并重新启动,而不是任由其停滞。2026-09-22,有一次运行停滞了 55 分钟,输出字节数始终为零,却一直无人查看。

第 3 步:验证

某个模型有时会直接在响应中返回审查结果,而不是写入 OUTPUT_PATH。gpt-6-sol 就是如此。结果位于 NDJSON 中,因此请先将其复制出来,再进行验证。

for f in "$STATE_DIR"/raw-*.ndjson; do
  b=$(basename "$f" .ndjson); out="$STATE_DIR/${b#raw-}.md"
  [ -s "$out" ] && continue
  jq -r 'select(.type=="text") | .part.text' "$f" | awk '/^## /{p=1} p' > "$out"
done
for label in openai gemini claude; do
  echo "== $label =="
  for area in security architecture solid correctness testing ops performance quality data; do
    f="$STATE_DIR/$label-$area.md"
    if [ ! -s "$f" ]; then
      echo "  $area MISSING errors=$(grep -c '\"type\":\"error\"' "$STATE_DIR/raw-$label-$area.ndjson" 2>/dev/null)"
    else
      n=$(grep -c '^- \*\*' "$f")
      cited=$(grep -oE '^- \*\*(High|Medium|Low)\*\* `[^`]+`' "$f" | grep -c ':[0-9]')
      unconf=$(grep -c 'line unconfirmed' "$f")
      echo "  $area ok findings=$n cited=$cited unconfirmed=$unconf"
    fi
  done
done

然后按已打开的文件数而非已引用的文件数衡量覆盖情况。打开但未引用的文件表示已阅读且未发现问题。

jq -r 'select(.type=="tool_use") | .part.state.input | (.filePath // .relative_path // .path // empty)' \
  "$STATE_DIR"/raw-*.ndjson 2>/dev/null | sed "s|^$PROJECT_ROOT/||" | sort -u > "$STATE_DIR/opened.txt"
echo "ledger files opened=$(comm -12 <(sort "$STATE_DIR/ledger.txt") "$STATE_DIR/opened.txt" | wc -l | tr -d ' ') of $(wc -l < "$STATE_DIR/ledger.txt" | tr -d ' ')"
comm -23 <(sort "$STATE_DIR/ledger.txt") "$STATE_DIR/opened.txt" | sed 's/^/  never opened: /'
jq -r '"ocr status=\(.status) files=\(.summary.files_reviewed) findings=\(.comments|length)"' "$STATE_DIR/ocr-scan.json"

如果某个区域的文件缺失、为空、没有 H2,或有 H2 但既没有问题行,也没有准确的 No findings.,则视为未审查。切勿将上述任何情况记为 No findings.。

重新派发给该审查者,这只需运行一次 opencode run。重试一次后仍未覆盖的区域,应在第 4 步中报告为 Not reviewed,并注明模型和原因。

审查者有时会在 H2 前写一条过渡句。请删除第一个 ## 之前的所有内容,而不要为此重新派发。调整提示词无法解决这个问题;删除内容始终有效。

比较 cited 和 findings。如果某条问题的首个反引号字段没有 :line,则该问题不可用,因为无人能在不重新阅读整个文件的情况下核实它。2026-09-21 的测量结果:openai 引用了 16 条问题中的 16 条,claude 引用了 28 条中的 5 条。

无论审查者是否在末尾标注 line unconfirmed,只要一条问题包含 path:line,cited 就会计入它。引用位于行首,标记位于行尾,因此二者不会互相排除。用 cited 减去 unconfirmed,即可得到审查者愿意背书的问题数。2026-09-22 对 gemini 安全探测的测量结果:38 条问题有引用,0 条带有该标记。

既然审查者现在可以将问题标记为 line unconfirmed,而不必将其舍弃,那么存在差距并不一定意味着有缺陷。重新派发前,请先阅读未引用的问题。如果它们也没有符号,再重新派发。

删除未经验证的问题这一指示已于 2026-09-22 移除。对 gemini 的一个区域进行测量后发现,保留它要付出 6 条问题的代价:有该条款时为 1 条,没有时为 6 条,其中包括被抑制的运行检查过却从未报告的 6 个路径遍历缺陷。移除 Ownership 表却只找回了 1 条,因此该表予以保留。

第 4 步:扫描并综合

阅读文件前,先扫描每个文件。审查者若引用了秘密信息,敏感数据钩子会阻止该文件,而阅读过程中无法请求绕过。

SCAN="$HOME/.config/dotfiles/scripts/canary-scan.sh"
if [ -x "$SCAN" ]; then
  for f in "$STATE_DIR"/*-*.md "$STATE_DIR/ocr-scan.json"; do
    [ -s "$f" ] && { echo "== $(basename "$f") =="; "$SCAN" "$f" || true; }
  done
fi

文件无问题或插件不存在时,canary-scan.sh 会以 0 退出且不输出内容;发现命中时,则以 2 退出,并为每条规则打印一行 <ruleId> x<count>。|| true 可避免非零退出导致此步骤结束。若有命中,请指出触发了什么规则,并告知操作员:在自己的提示词中加入 [allow-pii] 后重新调用即可解除阻止。

然后阅读这 27 个文件和 ocr-scan.json,并跨模型进行比较。ocr 的评论包含 path、start_line、severity 和 category;当两处结果一致时,可将其作为第四个来源计入。

  1. 多数一致的问题,3 个模型全部确认。列出所有 3 个模型都标记的问题,并注明区域、严重性和问题描述。
  2. 多数一致的问题,3 个模型中有 2 个确认。注明意见一致的两个模型,以及意见不同的模型。
  3. 单个模型发现的问题。全部列出,并注明各自由哪个模型提出。
  4. 评估冲突。列出模型意见不一致的区域,例如一个模型标记了风险,而另一个认为没有问题。
  5. 覆盖缺口。列出第 3 步中标记为 Not reviewed 的每个区域,并注明模型和原因。多数一致的问题数只有结合覆盖该区域的模型数才有意义。列出所有没有审查者打开过的账本文件。这些文件只有 ocr 的结果可供审查。

包含每一条问题。不要跳过或概括任何项目。

预期输出文件

.llmtmp/review-deep/ 保存完整运行结果,并会在下次运行开始时清空:

  • <label>-<area>.md,每个区域的问题文件,共 27 个
  • ocr-scan.json 和 ocr-scan.log,覆盖检查
  • ledger.txt,可审查文件;以及 opened.txt,审查者打开的文件
  • raw-<label>-<area>.ndjson,每位审查者的完整事件流
  • <label>-<area>.log,每位审查者的标准错误

NDJSON 参考

每行一个 JSON 对象。跳过解析失败的行。

类型含义关键字段
step_start一个大模型轮次开始
text模型输出的文本part.text
tool_use模型调用了工具part.tool、part.state.status、part.state.input
step_finish一个轮次完成part.reason、part.cost、part.tokens
error会话级错误error.data.message

step_finish 的原因是 stop 表示已完成,tool-calls 表示仍在继续。

jq -s '[.[] | select(.type=="step_finish") | .part.cost] | add' "$NDJSON"
tac "$NDJSON" | jq -s 'first(.[] | select(.type=="step_finish")) | .part.tokens.total'
jq -r 'select(.type=="tool_use") | .part.tool' "$NDJSON" | sort | uniq -c | sort -rn
jq -r 'select(.type=="error") | .error.data.message' "$NDJSON"

Serena 在 TypeScript 仓库中承担了 181 次工具调用里的 119 次,在包含 HTML、CSS 和 Dockerfile 的仓库中承担了 28 次里的 2 次。没有语言服务器支持文件时,符号导航的价值微乎其微。

规则

  • 调用 Agent 负责启动和汇总,不执行任何自身的审查分析。只有第 4 步进行分析。
  • 在 opencode run 中始终将标准输入重定向到 /dev/null。省略此项会导致进程在创建会话前挂起,且没有任何输出或错误,这是 anomalyco/opencode issue #38723。
  • 不要为审查者设置 token 上限或轮次上限。让它完成任务。
  • 调度后约 30 秒运行第 2b 步。没有会话记录的审查者已失效,需要重新启动;仅凭耗时无法区分这种情况与审查较慢。
  • 将第 2 步作为后台 Bash 调用运行。wait 会阻塞,直到全部 27 个进程退出;放到后台可避免其阻塞主会话。
  • 在一个 bash 代码块中启动全部 27 个审查者和 ocr 扫描。它们是相互独立的进程。
  • 使用普通消息调用,不要使用 --command。--command 标志与 context7 MCP 服务存在已知问题。
  • 运行期间不要清理各区域文件、NDJSON 或日志。第 1 步会在下一次运行开始时清理它们。
  • 如果某个审查者失败,仍要等待其他审查者完成并报告结果。
  • 审查者 Agent 位于 configs/opencode/agents/ 中,并通过 ./install 使用 glob 链接。若某台机器在这些文件变更后尚未运行 install,则会报未知 Agent 错误。

评论

支持 Markdown