きっかけ
Codex の Plan Mode では, 確認用の選択肢が「3つ + 自由記述」で出る.
これが地味に足りなかった. 実装方針を選ぶときに, 「推奨案」「保守的な案」「速い案」だけで終わらせると, もう一つの現実的な案が自由記述に流れがちだった. 逆に, 毎回20個も候補がほしいわけではない.
最初はプロンプトに「4択で出して」と書けば済むと思っていた. ただ, 選択肢数は tool schema と実行時バリデーションにも出てくる. ここをモデルへのお願いだけで合わせると, 失敗したときにどこが悪いのか分かりにくい.
それで Codex CLI を fork して, request_user_input 周りを自分用に変えた.
変えたこと
まず既定を 4択 + Custom answer にした.
自由記述欄は, モデルが isOther のような特別な選択肢を出す方式にはしなかった. クライアント側で最後に足す. その方が, モデル出力の揺れに引っ張られない.
選択肢数は config で変えられる.
toml[tools.experimental_request_user_input]
default_options_count = 4
max_questions = 3
default_options_count は通常の選択肢数. max_questions は1回の確認UIに出せる質問数の上限.
候補を多めに並べたいなら, 次のようにもできる.
toml[tools.experimental_request_user_input]
default_options_count = 20
max_questions = 3
0 はエラーにした. 選択肢数や質問数上限が0だと UI として成立しないので, schema と実行時バリデーションの両方で落とす.
通常版を置き換えなかった理由
最初は, build したバイナリをそのまま codex として置き換える案も考えた.
やめた理由は切り分けのしやすさ. fork 版は実験用なので, 公式版と設定, 履歴, 認証情報を混ぜたくなかった. 普段使いの ~/.codex/config.toml を壊すと, Codex 全体の作業に影響する.
fork 版は codex-custom という別名にした.
ここでは fork した repository の場所を $CODEX_FORK_DIR と書く.
bashln -sf "$CODEX_FORK_DIR/codex-rs/target/debug/codex" "$HOME/.local/bin/codex-custom"
CODEX_HOME も分ける.
bashCODEX_HOME="$HOME/.codex-custom" codex-custom
通常版は ~/.codex, fork 版は ~/.codex-custom を見る. これなら, fork 版がおかしくなっても通常版へ戻れる.
alias ではなく function にした
普段は cdxyolo という zsh のショートカットで Codex を起動していた.
もともとの cdxyolo は alias だった. alias だと, -f が付いたときだけ fork 版に切り替えるような分岐を書きにくい.
そのため, cdxyolo を shell function に変えた.
zshcdxyolo() {
if [ "$1" = "-f" ]; then
shift
CODEX_HOME="$HOME/.codex-custom" codex-custom "$@"
else
codex "$@"
fi
}
これで, いつもの作業は cdxyolo, fork 版を使うときだけ cdxyolo -f にできる.
完全に別コマンドへ分ける案もあった. ただ, 使い分けの粒度は「この作業だけ fork 版で試す」くらいなので, 入口は cdxyolo のままにした.
symlink が古い作業ディレクトリを見ていた
途中で, codex-custom の symlink が古い checkout を見ていることに気づいた.
text~/.local/bin/codex-custom
-> <old-checkout>/codex-rs/target/debug/codex
この状態では, GitHub 側の fork を更新しても手元の codex-custom は変わらない. コードは新しいのに, 起動しているバイナリは古いままになる.
今後は fork した repository を正にして, symlink もその build 成果物を見るようにした.
bashln -sf "$CODEX_FORK_DIR/codex-rs/target/debug/codex" "$HOME/.local/bin/codex-custom"
公式版の更新に追従する
fork すると, 公式 Codex の更新をどう取り込むかが残る.
手でやるなら, openai/codex を fetch して merge し, conflict を直して build すればよい. ただ, これは忘れる.
なので GitHub Actions で upstream 追従用の PR を作るようにした.
workflow がやることは次の通り.
openai/codexのmainを fetch する- fork 側の
mainに merge する request_user_input周りのテストを実行するcargo build --bin codexを実行する- 通れば
bot/upstream-syncから PR を作る
GitHub-hosted Actions は自分のPCではないので, 手元の codex-custom バイナリまでは更新できない.
PR を merge したら, 手元では build と symlink 更新だけをやる.
bashgit pull
cd codex-rs
cargo build --bin codex
ln -sf "$CODEX_FORK_DIR/codex-rs/target/debug/codex" "$HOME/.local/bin/codex-custom"
codex-custom --version
PR 作成とテストまでは自動化する. 自分のPCへ反映する最後のところだけ手でやる.
作ってみて
やったこと自体は小さい. 3択を4択にし, 必要なら config で変えられるようにしただけとも言える.
ただ, 通常版と分けて cdxyolo -f で起動できるようにしたので, 普段の Codex を壊さずに試せる. ここを分けたのが一番効いている.
fork はすぐ便利になる一方で, 公式更新に追いつく手間も増える. 今回はその手間を GitHub Actions の PR 作成まで寄せて, 最後の build だけを手元に残した.