Claude Code と Codex の連携で安定性が重要な理由
データアナリスト・データエンジニアがAIを業務に組み込む際、Claude Code から外部の Codex CLI を呼び出して複雑な実装タスクを自動化するパターンが増えています。しかし従来の companion.mjs(内部ディスパッチャ)直叩きは、プラグインのバージョンアップで容易に壊れる脆弱な設計でした。
この記事では、内部ディスパッチャ依存から公開 CLI への移行によって Claude→Codex 連携を安定化する手法を実装例と共に解説します。読者の皆様が同様のAI連携基盤を構築する際の参考になれば幸いです。
companion.mjs 直叩きの構造的問題
なぜ内部ディスパッチャは不安定なのか
従来の実装では、Claude Code の /implement スキルが codex-plugin-cc の内部ディスパッチャ companion.mjs を直接呼び出していました:
# 問題のあるパターン
node ~/.config/codex/plugins/codex-plugin-cc/companion.mjs task --prompt-file prompt.txt --json
この設計の問題点:
- パス解決の脆さ:
find ~/.config/codex -name companion.mjsでプラグイン内部パスを逆算 - API の不安定性:
Invalid requestエラー(@openai/codex 0.45.0 のthread/startAPI 未対応) - 実装の複雑性:
storedJob.payload展開、status --waitポーリング等の内部仕様への依存 - 保守性の悪さ: プラグインのマイナーバージョンアップで容易に壊れる
症状と対症療法の限界
2026年4月8日に @openai/codex@0.118.0 への強制アップデートで Invalid request は解消しましたが、これは対症療法に過ぎません。根本原因は「抽象レベルの誤り」にあります。
解決策: codex exec への移行
公開 CLI が提供する安定性
codex exec は Codex の公式 CLI であり、非対話モード用に設計された安定したインターフェースです:
# 推奨パターン
cat prompt.txt | codex exec - --sandbox read-only
この設計の利点:
- パス解決:
command -v codexで簡潔に解決 - API 安定性: 公開 CLI として後方互換性が保証される
- 実装の簡潔性: stdin パイプ + フォアグラウンド実行
- 保守性: プラグイン内部実装に依存しない
Before/After の比較
| 観点 | Before (companion.mjs) | After (codex exec) |
|---|---|---|
| パス解決 | `find ~/.config/codex -name companion.mjs \ | grep -v node_modules` |
| Planner 呼び出し | node companion.mjs task --prompt-file X --json | `cat X \ |
| 待機方式 | --background → status --wait --timeout-ms | フォアグラウンド実行 |
| 結果取得 | result --json の storedJob.payload 展開 | stdout に最終メッセージのみ |
| ファイル変更検知 | payload の touchedFiles | git diff --name-only HEAD |
実装手順
1. パス解決の簡素化
# Before: 内部パス逆算
COMPANION_PATH=$(find ~/.config/codex -name "companion.mjs" | grep -v node_modules | head -1)
if [[ ! -f "$COMPANION_PATH" ]]; then
echo "Error: companion.mjs not found"
exit 1
fi
# After: 公開 CLI 確認
if ! command -v codex &> /dev/null; then
echo "Error: codex CLI not found"
exit 1
fi
2. Planner 呼び出しの変更
# Before: companion.mjs 直叩き
node "$COMPANION_PATH" task \\
--prompt-file "$PROMPT_FILE" \\
--json \\
--background
# After: codex exec パイプ
cat "$PROMPT_FILE" | codex exec - --sandbox read-only
3. 結果処理の簡素化
# Before: payload 展開 + ポーリング
JOB_ID=$(node "$COMPANION_PATH" task --prompt-file "$PROMPT_FILE" --json --background | jq -r '.jobId')
node "$COMPANION_PATH" status --job-id "$JOB_ID" --wait --timeout-ms 600000
RESULT=$(node "$COMPANION_PATH" result --job-id "$JOB_ID" --json)
TOUCHED_FILES=$(echo "$RESULT" | jq -r '.storedJob.payload.touchedFiles[]?' 2>/dev/null || echo "")
# After: 直接実行 + git diff
cat "$PROMPT_FILE" | codex exec - --sandbox workspace-write
TOUCHED_FILES=$(git diff --name-only HEAD)
移行時の注意点
1. サンドボックス指定
codex exec では明示的にサンドボックスを指定する必要があります:
--sandbox read-only: 読み取り専用(分析・調査用)--sandbox workspace-write: ワークスペース書き込み可(実装用)
2. Codex CLI のパス
一部の環境では /opt/homebrew/bin/codex を直接指定する必要があります:
# ~/.superset/bin/codex には --enable codex_hooks が自動付与される場合
CODEX_PATH="/opt/homebrew/bin/codex"
if [[ -x "$CODEX_PATH" ]]; then
cat "$PROMPT_FILE" | "$CODEX_PATH" exec - --sandbox read-only
else
cat "$PROMPT_FILE" | codex exec - --sandbox read-only
fi
3. 認証エラーのハンドリング
# codex exec は認証エラー時に自動的にエラーステータスを返す
if ! cat "$PROMPT_FILE" | codex exec - --sandbox read-only; then
echo "Error: Codex execution failed (possibly authentication issue)"
exit 1
fi
実装結果と効果
コード削減効果
この移行により、実装は約1/3に削減されました:
- 変更規模: -224行 / +64行
- 複雑度: 内部 API 依存の除去により大幅に簡素化
実際の適用事例
以下のリポジトリで移行が完了しています:
analytics-note/signal-loop#32— 2026-04-14 マージ完了analytics-note/external-signal#56— 2026-04-14 マージ完了
両リポジトリで /implement スキルが codex exec 経由で正常動作することを確認済みです。
まとめ: AI 連携基盤の設計原則
今回の移行から得られる汎用的な教訓:
- 公開 API を優先する: 内部ディスパッチャより公開 CLI を使う
- 抽象レベルを正しく設定する: プラグイン内部実装に依存しない
- 症状ではなく原因を解決する: アップグレードで直っても構造的問題は残る
- git を活用する: プラグイン固有の payload より git diff を使う
これらの原則に従うことで、AI 連携基盤の長期安定性を確保できます。読者の皆様の環境でも、類似の内部 API 依存がないか見直してみることをお勧めします。