Claude Codeの/simplifyで差分を4つのエージェントが並列にクリーンアップする — /code-reviewはその後に実行する

Claude Codeの/simplifyスキルは、4つのエージェントで差分を並列にレビューして修正を適用します。そのあと/code-reviewで、修正後のコードにバグがないかを確認します。

白いカードにテラコッタ色のClaudeの星形ロゴと黒のロゴタイプ
目次

はじめに

Claude Codeに同梱されているスキル/simplifyを、私は何週間も使っていましたが、それが何をしているのかは知りませんでした。知っていたのは、/code-reviewという双子のようなスキルがあり、2つを組み合わせて使うよう勧める人が多いということだけです。2つのスキルはよく似て見えましたが、どう役割を分けているのかは説明できませんでした。

/simplifyはクリーンアップ専用のレビューです。4つのエージェントが差分を並列に読み、それぞれが1つの観点を担当します。観点は再利用、簡素化、効率、そして抽象度(変更が適切な層にあるか)です。修正はそのままワーキングツリーに書き込まれ、適用する修正を事前に一覧から選ぶ機会はありません。

/simplifyは正しさに関するバグを一切探さないので、/code-reviewはその後に、/simplifyが残したコードに対して実行します。このブログの実際のコミットで両方を実行し、それぞれが何を見つけるかを確かめました。

/simplifyが実行する内容

/simplifyはバンドルスキルです。Claude Codeに組み込まれたプロンプトで、モデルが自分のツールを使ってレビューと修正をどう進めるかを指示します。コマンドリファレンスでは、次のように説明されています(拙訳)。

変更されたコードをクリーンアップの観点でレビューし、修正を適用します。4つのレビューエージェントが並列に動き、既存ヘルパーの再利用、簡素化、効率、変更が適切な抽象度にあるかを確認します。正しさに関するバグは探しません。バグを見つけるには/code-reviewを使ってください。

読み込まれるプロンプトは3つのフェーズで進みます。

  1. 差分を集める。upstreamブランチに対するgit diffに、コミット前の変更を加えたものです。引数でパスやプルリクエストを渡した場合はそれが対象になります。
  2. レビューする。4つのエージェントが同時に起動し、それぞれがステップ1で集めた差分と、担当する観点を受け取ります。
  3. 適用する。同じ行を指す指摘は1つにまとめられ、そのうえで1件ずつワーキングツリー上で修正されます。

各エージェントは、プロンプトからそれぞれ1つの担当を与えられます。

エージェントプロンプトが探すよう指示している内容後述の実行での例
再利用コードベースにすでにあるヘルパーを、新しいコードが作り直していないか。共有モジュールや近くのファイルを検索して確認するscripts/indexnow.tsでもnew URL(SITE)を作っているが、解析済みの値をエクスポートしている場所はなく、新しいコードから読み込めるものはない
簡素化冗長な状態や導出できる状態、少しだけ違うコピー&ペースト、深いネスト、消し忘れたデッドコードresolveMarkが組み立てたオブジェクトを、唯一の呼び出し側がすぐに分解していた
効率繰り返される計算やI/O、独立しているのに順番に実行される処理、頻繁に通る経路に追加された処理luminance(DARK_BACKGROUND)がすべてのページのすべてのアイコンで計算し直されており、モジュール読み込み時に1回計算すれば済む
抽象度根本原因が1つ深い層にあるのに表面で症状を直していないか、共有コードに特殊ケースを積み重ねていないかブログの旧ホスト名が、site.tsではなくマークの表に直書きされていた

適用フェーズを担うのは4つのレビュー担当ではなくメインのエージェントで、指摘を見送ってよい条件が書かれています。プロンプトから引用します(拙訳)。

修正が意図した動作を変える指摘、レビュー対象の差分から大きく外れた変更が必要な指摘、誤検知と判断した指摘は見送ってください。反論はせず、見送ったことを記録してください。

バグを探すのは、同じくバンドルスキルである/code-reviewの役割です。

/simplify/code-review
探すもの再利用、簡素化、効率、抽象度正しさに関するバグ。モデルとeffortレベルによってはクリーンアップも
ファイルの編集修正を常に適用する--fixを渡さない限り報告のみ
実行形態4つのレビューエージェントが並列専用のコンテキストウィンドウを持つバックグラウンドのサブエージェント
引数パス、またはプルリクエストの番号かURLlow〜maxまたはultraのeffortレベル、--fix、--comment、--max-findings、プルリクエスト・ブランチ・パス

その3月の記事は、Rubyの静的解析・フォーマットツールであるRuboCopの作者、Bozhidar Batsov氏によるものです。同氏は/simplifyを「最もよく使うスキル」と呼び、「大きめのリファクタリングの直後や、AIが生成したコードをまとめて受け入れた直後」に実行しています(引用は拙訳)。

実際の差分で/simplifyを実行する

私が選んだのは、すでに本番公開済みの差分です。rehype-link-icons.tsは、リンクの横にブランドマークを描くこのブログのrehypeプラグイン(HTMLの構文木を加工するプラグイン)で、選んだ差分はそこに4つの新しいマークを追加したものでした。Qiita、Zenn、DevelopersIOの3つと、このブログへのリンクに付けるこのブログ自身の印(判子)です。

過去のコミットを別のワークツリーにコミット前の変更として再適用し、/simplifyが本来のコードや作業中の変更に触れずに自由に編集できるようにしました。

ターミナルウィンドウ
git worktree add --detach ../simplify-trial <commit>^
git -C ../simplify-trial cherry-pick -n <commit>

そのワークツリーの中で、Claude Code 2.1.310を使って次を実行しました。

/simplify src/lib/rehype-link-icons.ts

4つのエージェントが返した指摘は9件で、/simplifyはそのうち3件を適用しました。

エージェント指摘適用
再利用00
簡素化32
効率00
抽象度61

再利用と効率が0なのは、この2つのエージェントが報告したのが前の表の例のような所見だけで、今回の差分で直すべき点はなかったからです。

適用された3つの修正

最も大きな修正は、上の表の簡素化の例にあたるMarkSymbol型の削除でした。この型の役割は、idフィールドをresolveMarkから唯一の呼び出し側に渡すことだけでした。その呼び出し側であるスプライト(すべてのマークをまとめた非表示の<svg>)を組み立てるコードは、戻り値を受け取った瞬間に分解していました。

src/lib/rehype-link-icons.ts (before)
function resolveMark(key: MarkKey): MarkSymbol {
if (isLocal(key)) return { id: symbolId(key), ...LOCAL_MARKS[key]() };
return { id: symbolId(key), viewBox: "0 0 24 24", children: [markPath(key)] };
}
// …
children: [...used].map((key) => {
const { id, viewBox, children } = resolveMark(key);
return element("symbol", { id, viewBox }, children);
}),

/simplifyはresolveMarkを、<symbol>ノードそのものを返すsymbol()に置き換え、MarkSymbol型はなくなりました。

src/lib/rehype-link-icons.ts (after)
function symbol(key: MarkKey): HastNode {
const { viewBox, children } = isLocal(key)
? LOCAL_MARKS[key]()
: { viewBox: "0 0 24 24", children: [markPath(key)] };
return element("symbol", { id: symbolId(key), viewBox }, children);
}
// …
children: [...used].map(symbol),

残りの2つは小さな修正です。

  • faviconの読み取り方:プラグインは印の形状をfaviconから読み取っています。これまで<svg>タグだけ別の行で読み、<circle>・<g>・<path>は1つの.mapで読んでいました。今は4つとも同じ.mapで読み取り、結果は変わりません。
  • 旧ホスト名:ブログの旧アドレスoharu-tech-blog.vercel.appがマークの表に直書きされていましたが、サイトのアドレスはsite.tsに置くべきものです。上の表の抽象度の例がこれです。今はsite.tsのLEGACY_HOSTSに移り、アドレスが変わってもsite.tsだけを直せば済みます。

見送られた6つの指摘

/simplifyが見送った6件の指摘と、その理由です。

指摘の提案見送った理由
自前で描いたマーク(DevelopersIOと印)にもコントラストのチェックを通す動作が変わる:ダークモードでこの2つのマークの色が変わる
リンクをパースしてから分類し、./other/のようなリンクにも印を付ける動作が変わる:今はアイコンのないリンクに印が付くようになる
各アイコンの外側の<svg>から24×24のviewBoxを外す動作が変わる:アイコンの表示サイズが変わる可能性がある
public/favicon.svgを正規表現で解析する代わりに、ブランドのジェネレーターから印の形状をモジュールとして出力させる差分の外側に及ぶ:変更先はfaviconを生成する別のスクリプトになる
新しいelement()ヘルパーを、以前からあるノードビルダーでも使う差分の外側に及ぶ:今回のコミットが触れていないコードを変更することになる
isLocalの分岐を、形の揃った1つのマークオブジェクトに置き換える一部は対応済み:symbol()への変更で、この分岐が出てくる場所の1つはすでに消えている

/simplifyは、自分が編集したコードを実行しません。そこでClaude Codeが、3つの修正でページ上の表示が何も変わっていないことを別途確認しました。6種類のリンク(GitHub、ルート相対、旧ホスト、DevelopersIO、Zenn、脚注)を修正前と修正後のプラグインにそれぞれ通したところ、出力は完全に一致しました。

/simplifyが残したコードに/code-reviewを実行する

/simplifyが編集したrehype-link-icons.tsとsite.tsの2ファイルに/code-reviewを実行すると、指摘は10件でした。

そのうち9件は、本番公開されたときのコミットにすでにあったものです。この9件のうち3件はそのまま再現できます。

入力プラグインの動作理由
href="/\github.com/x"GitHubのマークを描くルート相対の判定は//を除外するが/\は除外せず、URLのパースではバックスラッシュがスラッシュとして読まれる
href="http://constructor/"TypeError: key.slice is not a functionを投げ、ビルド全体が失敗するマークの表がプレーンなオブジェクトなので、ホスト名からObject.prototype.constructorが引ける
DevelopersIOの#cbd31eのセグメントライトテーマの白背景に対するコントラスト比が1.63:1で、WCAGが求める3:1を下回る自前で描いたマークはコントラストのチェックを通らない

抽象度のエージェントもmarkForにあるルート相対判定の正規表現を読んでいましたが、指摘したのは判定を置く層が不適切だという点だけでした。この正規表現が不正なhrefを通してしまうかどうかは正しさの問題で、このエージェントの担当外です。そのため、バックスラッシュのケースは/code-reviewまで見つかりませんでした。

/simplifyと/code-reviewはいつ実行するか

/simplifyを先に、/code-reviewを最後に実行します。/simplifyはワーキングツリーのコードを直接書き換え、レビューが見るのは実行した時点のコードだけです。順番を逆にすると、/simplifyによる変更はレビューされないままコミットされます。

/simplify が先、/code-review が後4 枚のカードが左から右に並ぶ。差分はまず /simplify に渡り、4 つのエージェントが再利用・簡素化・効率・抽象度の観点で並列にレビューして、クリーンアップの修正をワーキングツリーに適用する。その結果を /code-review が読み、/simplify 適用後のコードにあるバグを報告する。最後に自分で指摘を修正し、git diff を確認する。/simplify がファイルを編集するので、レビューはその後に実行する変更差分変更したコード(コミット前でも可)/simplifyクリーンアップの修正をワーキングツリーに適用/code-review/simplify 適用後のコードのバグを報告自分指摘を修正し、git diff を確認4 つのエージェントが並列再利用簡素化効率抽象度
/simplifyが差分を編集するので、/code-reviewはその結果を読みます。逆の順番ではありません。

Batsov氏の3月の記事で紹介されているワークフローは逆の順番で、「変更を加え、/reviewで問題を見つけ、指摘をすべて直してから/simplifyで整える」(拙訳)という流れです。

まとめ

/simplifyは差分を4つのエージェントに並列に渡し、再利用、簡素化、効率、抽象度の観点で修正を適用します。動作を変える修正や差分の外側に及ぶ修正は見送ります。バグは一切探さず、編集したコードを実行することもありません。

/code-reviewはその後に、/simplifyが残したコードに対して実行します。すでに本番公開したコミットでこの2つを試すなら、git cherry-pick -nでそのコミットを別のワークツリーに再現し、そこで実行してください。

参考リンク

この記事をシェア