🔁

Claude Codeの実装計画をCodexにレビューさせるカスタムコマンドの構築

に公開

AIが書いた実装計画を、別系統のAIに自動でレビューさせる仕組みを、CLIで自作した話です。

実装役に Claude Code、レビュー役に Codex を使い、plan.md(計画)と feedback.md(レビュー結果)という2枚のマークダウンファイルだけをやり取りするシンプルな設計にしました。最初は GUI自動操作で作って挫折し、CLI化したら安定した、という移行の経緯も含めて書きます。

なぜ作ったか

スマホ向けのメモ/日記アプリ(PHP + MySQL の小さな個人開発)を1人で作っています。開発には Claude Code を主に使っているのですが、1人開発なので「実装してから間違いに気づく」コストが地味に痛い。

そこで思いついたのが、実装に着手する前の「計画」の段階で、別のAIに批判的レビューをさせるというワークフローです。

計画段階でレビューするのには理由があります。

  • コードができてからのレビューより、計画段階での手戻りのほうが圧倒的に安い
  • 「設計の方向性」「見落としているエッジケース」「セキュリティ観点」を着手前に潰せる
  • AI同士なので、24時間いつでも数十秒〜数分でレビューが返ってくる

レビュー役には Codex(OpenAI の GPT系コーディングエージェント)を採用しました。実装役の Claude とは別系統のAIにすることで、同じモデルの思い込みに引きずられない=クロスレビューになる、という狙いです。

仕組み

登場人物

役割 担当 やること
実装・計画 Claude Code 機能の実装計画を plan.md に書く/レビューを反映する
レビュー Codex CLI(codex exec plan.md を4観点でレビューし feedback.md に書き出す

受け渡しはただのファイル

やり取りは codex-review/plan.md(計画)と codex-review/feedback.md(レビュー結果)という2つのマークダウンファイルだけです。特別なAPIや連携サーバはありません。ファイルを介した非同期レビューという、ごくシンプルな設計にしました。

トリガーは Claude Code のスキル

Claude Code には「スキル(カスタムスラッシュコマンド)」という拡張機構があります。SKILL.md というファイルに名前・説明・手順を書いておくと、/コマンド名 で呼び出せます。

今回は /crossreview というスキルを定義し、「plan.md を探す → Codex でレビュー → 結果を要約」までを自動化しました。

/crossreview スキルの5ステップ

  1. 対象の特定codex-review/plan.md を探す(複数プロジェクトがあればユーザーに選択させる)
  2. Codex CLIでレビュー実行codex exec を読み取り専用で起動し、4観点レビューを feedback.md に書き出す
  3. 結果の確認・要約feedback.md を読み、総合判定(Approve / Approve with changes / Request changes)と主要な指摘を要約報告
  4. 実行ログの記録:いつ・どのプロジェクトを・どのモデルで・何秒でレビューしたかを YAML でログ
  5. リフレクション:「今回のレビューで気になった点は?」を聞き、知見を LESSONS.md に蓄積

レビューの「4観点」を固定する

プロンプトでレビュー観点を固定しています。

  1. 設計・アーキテクチャ(同意点 / 懸念点 / 改善提案)
  2. バグ・論理エラー(想定リスク / エッジケース / 改善提案)
  3. セキュリティ(脆弱性候補 / 入力検証の懸念 / 改善提案)
  4. パフォーマンス・可読性(効率の懸念 / 命名・スタイル / 改善提案)

最後に総合判定と最重要アクション3点を出させます。観点を固定することで毎回ブレない構造化レビューが得られ、feedback.md をそのまま plan.md に反映しやすくなります。

GUI自動化で挫折して、CLIに切り替えた

最初の実装は GUI自動操作(Claude の Computer Use 機能)で Codex のデスクトップアプリを動かすものを試してみましたが、これは脆くてうまくいきませんでした。

そこで GUI操作ではなく、codex exec(非対話モードのCLI)でヘッドレス実行する方式に切り替えました。1コマンドで「レビュー実行 → 結果をファイルに保存」まで完結し、Bash一発で環境に依存せず動きます。iPhoneからのリモートコントロールでも同じように動作します。

これが今回いちばんの学びでした。「AIアプリをAIに操作させる」より「AIにCLIを叩かせる」ほうが、速くて壊れにくい。 GUI自動化は最後の手段で、CLIやAPIが用意されているなら必ずそちらを使うべきです。

技術詳細

使ったもの

  • Codex CLI@openai/codex(OpenAI公式のCLI。npm i -g @openai/codex でグローバルインストール)
  • 認証codex login で ChatGPTアカウントにログイン。デスクトップアプリと同じサブスクを共有するので API課金は発生しない
  • 実行:非対話モードの codex exec(対話TUIではなく、1回投げて結果を受け取る使い方)

主要フラグ

フラグ 意味
-s read-only モデルが生成するコマンドを読み取り専用サンドボックスで実行(ファイルを書き換えない=レビューに安全)
-C <dir> 作業ルートをプロジェクトに固定(カレントディレクトリ依存を回避)
-o <file> エージェントの最終メッセージ(=レビュー本文)を指定ファイルに保存
-c key=value ~/.codex/config.toml の設定を一時上書き
-m <model> 使うモデルを指定

検証済みの実行コマンド

<projectPath><トピック名> はプレースホルダです。実際に試す際は、前者を対象プロジェクトの絶対パスに、後者をレビュー対象の機能名に置き換えてください。

OUT="<projectPath>/codex-review/feedback.md"
LOG="/tmp/crossreview-codex.log"

codex exec -s read-only -C "<projectPath>" \
  -c 'model_reasoning_effort="medium"' \
  -o "$OUT" \
  "$(cat <<'PROMPT'
codex-review/plan.md の計画書をレビューしてください。必要に応じてリポジトリ内のファイルや git diff も参照してOK。ファイルは一切変更しないこと。
以下の4観点で構造化レビューし、レビュー結果の Markdown だけを出力してください(前置き不要):

# Codex レビュー: <トピック名>

## 1. 設計・アーキテクチャ(同意点 / 懸念点 / 改善提案)
## 2. バグ・論理エラー(想定リスク / エッジケース / 改善提案)
## 3. セキュリティ(脆弱性候補 / 入力検証の懸念 / 改善提案)
## 4. パフォーマンス・可読性(効率の懸念 / 命名・スタイル / 改善提案)

最後に総合判定(Approve / Approve with changes / Request changes)と最重要アクション3点。
PROMPT
)" < /dev/null > "$LOG" 2>&1 &
CPID=$!
# 180秒ウォッチドッグ(macOSにtimeoutが無いため)
( sleep 180; kill -0 $CPID 2>/dev/null && { pkill -TERM -f "codex exec -s read-only"; sleep 2; pkill -KILL -f "codex exec -s read-only"; } ) &
WPID=$!
wait $CPID; EXIT=$?
kill "$WPID" 2>/dev/null; wait "$WPID" 2>/dev/null
echo "codex exit=$EXIT / ${SECONDS}s"

ハマりどころ

非対話CLIは < /dev/null を付ける

バックグラウンドで codex exec を実行したら、いつまでも終わらないことがありました。プロセスは生きているのに ps で見ると CPU使用時間がごくわずか、つまり計算しておらず何かを待っている状態です。

実行ログの末尾を見ると、ヒントがありました。

Reading additional input from stdin...

原因は、codex exec がプロンプトを引数で渡していても標準入力(stdin)の追加入力=EOF を待つことでした。対話実行ならキーボードから EOF(Ctrl-D)が来ますが、バックグラウンド/非対話実行では stdin が開きっぱなしで EOF が来ないため、無限にブロックします。

解決はコマンド末尾に < /dev/null を付けるだけです。stdin を即 EOF にできます。これでハングが解消し、数十秒で完了するようになりました。

ちなみに「重い処理で遅い」と思い込んでいましたが、CPU時間がほぼ0という事実が「計算ではなく待ち」を示していました。プロセスが遅いときは経過時間だけでなく CPU時間を見ると、計算が重いのか待っているだけなのかを切り分けられます。

macOSに timeout コマンドが無い

Linux の timeout(coreutils)は macOS 標準には入っていません。代わりにバックグラウンド実行+sleeppkill で自前のウォッチドッグを組みました。pkill -f "codex exec -s read-only" で子プロセス(実体バイナリ)ごと確実に止めるのがポイントです。親だけ kill すると実体が孤児化します。

なお pkill -f はコマンドライン全体にマッチするため、同じ文字列を含む無関係なプロセスを巻き込む危険があります。マッチ文字列は今回のコマンドに固有の長めの文字列にしておくと安全です。心配なら pkill の前に pgrep -f "..." で対象を確認するとよいでしょう。

既定モデルが重すぎて遅い

~/.codex/config.toml の設定が model_reasoning_effort = "xhigh"(最大の推論深度)だったため、計画レビューだと推論が深すぎて時間がかかっていました。日常用途では -c 'model_reasoning_effort="medium"' で上書きすると、実用速度と十分な品質が両立します。最大品質が欲しいときだけ上書きを外せばOKです。

運用上の工夫

レビューは上書きせず逐次保存

当初 feedback.md を毎回上書きしていて、過去のレビューを1回消してしまいました(会話履歴から復元する羽目に)。

以降は、レビュー1サイクルが揃った時点で codex-review/archive/YYYY-MM-DD-<topic>-plan.md / -feedback.md にトピック別で永久保存する運用に変えました。top-level の plan.md / feedback.md は常に「最新の作業中ペア」を指すようにしています。

実行ログとリフレクションで「スキルが育つ」

各実行を runs/YYYY-MM-DD_HH-MM.log(YAML)に記録し、実行後に「気になった点は?」を聞いて LESSONS.md に蓄積しています。

スキルは実行前に必ず LESSONS.md を読むので、過去のハマりどころ(例:< /dev/null 必須)を毎回踏まえて動きます。使うほど賢くなる自己改善ループになっているわけです。

実例:ピン留め機能の計画をレビューさせてみた

題材は「投稿の星マークをタップするとホーム先頭に固定表示する」機能の実装計画(plan.md、約4KB)です。model_reasoning_effort=medium で実行したところ、約78秒で完了し、8.7KBの構造化レビューが feedback.md に出力されました。総合判定は Approve with changes。

実際に有用だった指摘は次の3点です。

  1. offset方式ページネーションとソート順変更の相性問題:固定/解除でDOM並びを変えると、「もっと読み込む」で投稿の取りこぼし・重複が起き得る。cursor/keyset pagination化を提案
  2. 入力の厳格化bookmark 入力を 0/1 のみ許可し、不正値は 400 を返すべき
  3. 複合インデックスの追加ORDER BY bookmark, created_at, id に対応するインデックスが無く、件数増で filesort が発生し得る

「形だけのレビュー」ではなく、実際の設計トレードオフを突いてくることが確認できました。しかも一部は、計画側で意図的に「v1では許容」と判断していた箇所と一致していました。

得られた学び

  1. AIにGUIアプリを操作させるより、AIにCLIを叩かせるほうが堅牢で速い
  2. 遅い処理はCPU時間を見る。「経過時間が長い」≠「計算が重い」。CPUがほぼ0なら待ちを疑う
  3. 非対話CLIは < /dev/null を付ける。stdin待ちハングの定番回避
  4. AI同士のクロスレビューは実用になる。実装役と別系統のAIに観点固定でレビューさせると、着手前に設計の穴を潰せる
  5. ワークフローの記憶(LESSONS)を仕組みに組み込むと、ツールが自己改善する

1人開発でレビュー相手がいないという課題に対して、「別系統のAIに計画段階でレビューさせる」というアプローチは思った以上に実用的でした。ファイル2枚を介すだけのシンプルな仕組みなので、同じように個人開発をしていて興味の湧いた方は是非試してみてください。

Discussion