期限のある移行が、いちばん事故りやすい
2026年6月18日に Gemini CLI と Gemini Code Assist の IDE 拡張がリクエスト受付を停止し、後継として Go で書き直された Antigravity CLI に一本化されました。手元で対話的に使うだけなら、新しいコマンドに慣れれば済む話です。
問題になるのは、CLI を CI やスケジュール実行のなかに埋め込んでいる場合です。私自身、個人開発で複数のブログサイトを自動運用しており、記事生成や検証の一部を CLI 経由で回しています。こうした仕組みは「動いていることが当たり前」になっているぶん、ある朝とつぜんコマンドが見つからないと、原因の切り分けから始めることになります。
期限のある移行で起きがちな失敗は、締め切り当日にまとめて差し替えてしまうことです。差し替えた直後に挙動が変わって、しかも切り戻す手順を用意していない。今回は移行で既存環境が一度に変わったという報告も出ています。だからこそ、止めない段取りを先に決めておきたいところです。
まず「どこで CLI を呼んでいるか」を洗い出す
移行の前に、自分のシステムのどこが CLI に依存しているかを棚卸しします。意外と本人も忘れている箇所があるものです。
# リポジトリ全体から CLI 呼び出し箇所を洗い出す
# シェルスクリプト・CI 定義・cron・Makefile を対象にする
grep -rn "gemini " \
--include="*.sh" \
--include="*.yml" \
--include="*.yaml" \
--include="Makefile" \
--include="*.mjs" \
. 2>/dev/null
# crontab に直接書いている場合も確認する
crontab -l 2>/dev/null | grep -i "gemini"
洗い出した箇所を、影響度で3つに分けます。止まると即座にユーザー影響が出るもの、止まっても翌日に気づけば足りるもの、手元の補助スクリプトで止まっても困らないもの。この優先順位が、後の切替順序になります。
コマンドと出力の差異を吸収するラッパーを置く
呼び出し箇所を直接書き換えるのではなく、間に薄いラッパーを1枚はさみます。こうしておくと、内部実装をあとから差し替えても、呼び出し側のスクリプトは触らずに済みます。私はこの方式を好みます。切替の単位が1ファイルに集約され、ロールバックがファイル1つの差し替えで完結するからです。
#!/usr/bin/env bash
# ai-cli.sh — CLI 実装を吸収するラッパー
# 環境変数 AI_CLI_BACKEND で実装を切り替える(gemini / antigravity)
set -euo pipefail
BACKEND="${AI_CLI_BACKEND:-antigravity}"
run_prompt() {
local prompt="$1"
case "$BACKEND" in
antigravity)
# Antigravity CLI の呼び出し形に合わせる
antigravity run --prompt "$prompt" --format json
;;
gemini)
# 旧 Gemini CLI(移行期間中のフォールバック用)
gemini generate --text "$prompt" --output json
;;
*)
echo "unknown backend: $BACKEND" >&2
return 2
;;
esac
}
run_prompt "$@"
ここでのポイントは、出力フォーマットを json に固定して、呼び出し側がパースする形を揃えておくことです。コマンド名やフラグ名は実装ごとに異なりますが、構造化された出力を1か所で整形しておけば、下流の差異を最小化できます。実際のフラグ名は手元の antigravity --help で確認してから合わせてください。
切り替え前に確認しておく3点
ラッパーを書いたら、新旧の実装でいくつかの挙動を実際に観測しておきます。私はこの確認を、移行の山場だと考えています。ここを飛ばすと、本番でつまずいてから初めて差異に気づくことになります。
終了コードの差異を記録する
自動化で最も見落とされがちなのが、終了コードの差異です。対話的に使っているときは画面の文字を読めば判断できますが、スクリプトは終了コードしか見ていません。実装が変わると、エラー時の終了コードや、レート制限時の挙動が変わることがあります。
# 呼び出し側は終了コードで分岐する設計にしておく
if ! result=$(./ai-cli.sh "$PROMPT" 2>err.log); then
code=$?
case "$code" in
1) echo "一般エラー。err.log を確認して再試行" >&2 ;;
2) echo "ラッパーの設定ミス。BACKEND を確認" >&2 ;;
*) echo "未知の終了コード $code。手動確認が必要" >&2 ;;
esac
exit "$code"
fi
新旧それぞれで「正常系」「入力ミス」「レート制限」を試し、終了コードを記録しておきます。表にすると差異が見えやすくなります。
| 状況 | 記録すべき項目 | 確認方法 |
| 正常終了 | 終了コード・標準出力の構造 | サンプルプロンプトを1件実行 |
| 入力ミス | 終了コード・エラーメッセージの出力先 | わざと不正な引数を渡す |
| レート制限 | 終了コード・リトライ可否の判別方法 | 短時間に連続実行して観測 |
出力構造の差異を吸収する
正常終了しても、返ってくる JSON のキー名や入れ子の形が変わっていることがあります。下流がそのキーに依存していると、エラーにならないまま空の結果が流れる、という厄介な事故になります。これはエラーで止まるよりたちが悪いので、注意点として先に潰しておきます。切替前に1件ずつ出力を目で比べ、必要ならラッパー内で整形して形を揃えるのが回避策です。
レート制限時の振る舞いを決める
レート制限にぶつかったとき、即座に失敗するのか、内部で待って再試行するのか。実装ごとに違うことがあります。本番運用では、ここを曖昧にしておくと、混雑する時間帯にだけ説明のつかない失敗が出ます。私の場合は「ラッパー側で一定回数だけ指数バックオフ再試行し、それでも駄目なら明示的に失敗させる」方針を採用しています。
3段階で切り替える
ここまで準備できたら、実際の切替に入ります。一度に全部を変えないことが、止めないための核心です。
- 第1段階(並走): 優先度の低い補助スクリプトだけ
AI_CLI_BACKEND=antigravity にして、本番の重要経路は旧実装のまま残します。数日動かして、出力構造と終了コードが想定どおりかを観察します。
- 第2段階(一部切替): 止まっても翌日に気づけば足りる経路を新実装へ移します。このとき、切替前後で生成物に差が出ていないかを必ず比べます。私はこの比較を省略して痛い目を見たことがあり、いまは差分チェックを段取りに必ず入れています。
- 第3段階(全面切替): ラッパーから旧実装の分岐を消すのは、新実装が一定期間安定してからにします。急いで消すと、いざというときの切り戻し先を自分で潰すことになります。
# 段階ごとの切替は環境変数の出し分けで行う
# 第1段階: 補助スクリプトのみ
AI_CLI_BACKEND=antigravity ./scripts/optional-helper.sh
# 第3段階: 既定値を antigravity にしてフォールバックを残す
export AI_CLI_BACKEND=antigravity
この段階を踏む場合は、各段階の境目で必ずいったん止まって観察します。次へ進む判断は、前段で問題が出ていないことを確認してからにするのを推奨します。
ロールバックできる状態を保つ
移行初期にセットアップが壊れたという報告が出ているなかで、いちばん大切なのは「いつでも戻せる」状態を保つことだと考えています。ラッパー方式を採るのは、まさにこの一点のためです。
旧実装をすぐに環境から消さない、ラッパーの分岐を残しておく、切替は環境変数の出し分けだけで完結させる。この3つを守っておけば、何か起きても呼び出し側のスクリプトを触らずに切り戻せます。共通のエージェントハーネスへ寄せていく利点は確かに大きいのですが、その恩恵を安全に受け取るには、慌てて橋を落とさないことが先決です。
次に手を動かすなら、まずは grep での棚卸しから始めてみてください。自分のシステムが CLI のどこに依存しているかが見えるだけで、移行の見通しは一気に立てやすくなります。