じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

Codex CLIをNeovimで使うcodex.nvimを作ったので、使ってみてほしい

はじめに

Neovimでコードを読んでいるとき、やりたいのはCodexをsplitで開くことではありません。開いているファイルや選択範囲を正確に渡し、会話を残したまま編集へ戻ることです。codex.nvimは、その往復を短くするためのNeovimプラグインです。

github.com

このツールが必要になる場面

codex.nvimで解決したい課題は、次のとおりです。

Neovimでコードを読んだり選んだりしているとき、ファイルや範囲を説明し直さずCodexへ正確に渡し、会話を残したまま編集へ戻りたい。そうすれば、エディタとagentの間を往復するたびに作業の前提を組み立て直さずに済む。

Codex CLIは:terminalから起動するだけでも使えます。しかし、相談するたびにファイルパスをコピーして、行番号を確認する必要があります。選択範囲の貼り付けやterminal modeからの移動も残ります。1回なら小さくても、調査、修正、確認を繰り返すほど集中を切る原因になります。

codex.nvimで前へ進められるのは、次の3つです。

進めたいこと つまずきやすい点 codex.nvimが作る状態
Codexへ相談する 対象のファイルや範囲を説明し直す Neovimで見ている対象をそのまま渡せる
会話を続ける splitを閉じるとsessionまで失う windowだけ隠し、processは残せる
編集へ戻る terminal modeとwindow移動が混ざる Codexから隣のwindowへ明示的に移動できる

一方で、導入すれば必ず速くなるわけではありません。外部terminalやtmuxからCodexを使う習慣が完成していれば、プラグインを増やす方が負担です。誤ったファイルやcwdを暗黙に渡す連携も困ります。そのためcodex.nvimは、送るcontext、sessionのcwd、windowとprocessの状態を確認できる形にしています。

Codexをsplitで開くこと自体が目的ではない

codex.nvimは、新しいAI agentでもCodex本体の代替でもありません。Neovimとインストール済みのCodex CLIをつなぐ連携層です。推論、コード変更、command execution、session historyはCodex本体へ任せます。

プラグインが担当するのは、次の3点です。

  1. 現在のファイル、選択範囲、explorerの項目をCodexの入力へ変換する
  2. Codex processを保ったまま、windowの表示とfocusを切り替える
  3. Codexがどのcwdとbackendで動いているかを確認できるようにする
Neovimで読んでいるコード
          ↓ contextを変換
Codex CLIで調査・修正を進める
          ↓ sessionを残す
Neovimの編集へ戻る

コマンド体系とside panelの操作感は、coder/claudecode.nvimを参考にしました。現在のファイルを追加する、選択範囲を送る、隠したsessionへ再びfocusするといった操作は、agentの種類が変わっても必要だからです。

github.com

ただし、内部protocolは異なります。claudecode.nvimはClaude CodeとのIDE連携を実装しています。codex.nvimはCodex CLIのterminal UIと、公式のapp-server interfaceを使います。似た外見を移すのではなく、Codexが公開している境界に合わせて実装しました。

contextとcwdは別の問題として扱う

「開いているファイルを中心にする」には、2つの意味があります。contextは今回Codexへ何を渡すかを決めます。cwdはsessionがどこからファイルを探し、履歴を読み、commandを実行するかを決めます。

たとえば、github.com/nwiizo/codex.nvim/lua/codex/init.luaを開いてCodexを起動するとします。cwd = "root"を選ぶと、最も近い.gitのあるgithub.com/nwiizo/codex.nvimから起動します。cwd = "file"なら、開いたファイルのあるlua/codexが起点です。

設定値 Codexを起動する場所
"root" 開いているファイルから最も近いroot marker。既定値は.git
"file" 開いているファイルのディレクトリ
"nvim" Neovim自身のcurrent working directory
固定パス 指定したディレクトリ
function(ctx) buffer情報から利用者が決めたディレクトリ

普段の開発なら、repository全体を調べてtestを実行できるrootが自然です。単一ファイルの場所を起点にしたい時はfile、monorepo内の独自境界を使う時はcallbackを選べます。大切なのは、開いているファイルを毎回渡しながらも、会話中の足場は動かさないことです。

codex.nvimは、cwdをsession開始時に一度だけ解決します。別のbufferへ移動しても、processを止めるまでcwdは変えません。これにより、同じ会話の途中で相対pathの意味が変わることを防ぎます。

windowを隠しても会話は残す

Neovimのterminal bufferは、入力をterminal processへ送るTerminal-modeを持ちます。通常のbufferと同じ感覚でwindowを移動しようとすると、keyがCodex側へ送られることがあります。codex.nvimではbuffer local mappingとしてAlt-h/j/k/lを設定し、terminal modeを抜けて隣のwindowへ移動します。terminal emulatorがOptionやAltをMetaとして送らない時は、別のkeyに変更できます。

neovim.io

:CodexFocusは、単純な表示のon/offではなく、sessionを保つための状態遷移として動きます。

現在の状態 :CodexFocusの結果
processがない 新しいsessionを起動してfocus
processはあるがwindowがない splitを再表示してfocus
別windowに表示中 そのwindowへfocus
Codex windowをfocus中 windowだけ隠し、processは維持

:CodexCloseは表示だけを隠し、:CodexStopだけがprocessを停止します。Codex windowから隠した時は、直前に使っていたeditor windowへ戻ります。windowを整理することと会話を終えることを分けるためです。

操作と解決する課題の対応

機能の数だけでは、導入後に何が変わるのか分かりません。日常の作業に対応させると、次のようになります。

進めたいこと 操作 得られる状態
現在のファイルについて相談する :CodexAdd composerへ@pathを追加する。まだ送信しない
選んだコードを正確に渡す Visual modeで:CodexSendVisual ファイルパスと行番号つきで送る
explorerで見つけた項目を渡す :CodexTreeAdd 選択中のファイルやディレクトリを追加する
会話へ戻る、または一時的に隠す :CodexFocus processを保ったままwindowを切り替える
過去の会話から再開する resume、continue、fork Codex自身のsession historyを使う
変更をレビューする :CodexReview uncommitted changes、base branch、commitを明示して依頼する
動作の前提を確かめる :CodexStatus、:CodexHealth cwd、process、直近のcontext、依存関係を確認する

:CodexTreeAddはnvim-tree、neo-tree、Oil、mini.files、netrw、Snacks pickerに対応します。特定のexplorerをplugin dependencyにせず、開いているfiletypeに合わせてadapterを選びます。

Visual selectionはlinewiseだけでなく、characterwiseとblockwiseも扱います。multibyte character、tab、wide character、exclusive selectionもtest対象です。contextが既定の500行または65536 bytesを超えた時は、黙って切り詰めず送信を拒否します。画面で選んだ内容とCodexへ渡った内容を一致させるためです。

:CodexStatusには、直近に渡したpathと行範囲、composerへ追加しただけか送信したか、sessionのcwdを表示します。sessionを起動していない時は、次に使うcwdを確認できます。選択したコード本文は保持せず、確認に必要なmetadataだけをsession中に残します。

terminalを既定にした理由

codex.nvimには2つのbackendがあります。既定のterminalは、Codex terminal UIをNeovimのsplitで動かします。conversation表示、approval、command executionをCodex側へ任せられるため、CLIですでに成立している操作と信頼性を保てます。

実験的なapp_server backendは、codex app-serverをstdioで起動します。streaming message、plan、command output、approval、user input、diffをNeovim native UIに表示する構成です。公式資料では、app-serverはrich client向けのinterfaceです。

developers.openai.com

native UIには、resumeとforkのpickerやdiff表示をNeovimへ馴染ませられる利点があります。一方、プラグインはresponseとnotificationを正しく扱う必要があります。approval中の入力やactive turnへのsteer、threadの切り替えも管理対象です。process終了時にはstateをresetします。Codexを使うために新しい不安定要素を増やさないよう、terminalを安定した既定値にしました。app-serverは壊れる可能性のある実験機能として分けています。

最小構成で試す

lazy.nvimでは、利用するcommandをlazy-load triggerへ登録します。

{
  "nwiizo/codex.nvim",
  cmd = {
    "Codex",
    "CodexFocus",
    "CodexAdd",
    "CodexSendVisual",
    "CodexTreeAdd",
    "CodexStatus",
    "CodexStop",
    "CodexHealth",
  },
  opts = {
    backend = "terminal",
    cwd = "root",
    focus_after_send = true,
    terminal = {
      split_side = "right",
      split_width_percentage = 0.4,
      window_navigation = {
        left = "<M-h>",
        down = "<M-j>",
        up = "<M-k>",
        right = "<M-l>",
      },
    },
  },
  keys = {
    { "<leader>ax", "<cmd>CodexFocus<cr>", desc = "Focus or hide Codex" },
  },
}

設定後は、:CodexHealthでNeovimとCodex CLIを確認し、対象repositoryで:CodexFocusを実行します。右側にCodexが開いたらAlt-hで対象ファイルへ戻り、:CodexAddを実行します。コードの一部について相談するなら、Visual modeで選択して:CodexSendVisualを実行します。focus_after_send = trueなら、contextを渡した後にCodexへfocusします。

向いていない場面

codex.nvimが合うのは、Neovimを主な編集環境にし、同じCodex sessionへファイルや選択範囲を何度も渡す人です。1つのNeovim instanceにつき1つのCodex processまたはthreadで足りることも前提です。

次の条件では、別の方法が向いています。

  • 外部terminalやtmuxだけでcontextの受け渡しに困っていない
  • 複数のagent sessionを同時に一覧、監視、操作したい
  • plugin独自のtranscriptを永続化したい
  • Windowsで使いたい
  • Neovimのファイルや選択範囲をCodexへ渡す必要がない

codex.nvimは複数sessionの同時管理、独自transcriptの永続化、Windows対応を実装していません。Codex自身のsession historyとは別に履歴を作ると、どちらがsource of truthか分かりにくくなるためです。複数sessionの監督が中心なら、このプラグインの対象とは異なります。

正しく渡せることを確認する

context連携では、便利さより「何が渡ったか」を信頼できることが重要です。codex.nvimはheadless test、StyLua、Luacheck、Lua Language Serverの型診断をCIへ入れています。Neovimは要求する最小versionの0.12.0に加え、stableとnightlyでも同じtest suiteを実行します。

make check
make integration

make integrationでは、インストール済みのCodex CLIから実際にapp-serverを起動し、initialize handshakeとthread/listを確認します。公開用tagでもqualityと3つのNeovim matrixを通しています。

github.com

このブログが良ければ読者になったり、nwiizoのXやGithubをフォローしてくれると嬉しいです。

おわりに

codex.nvimの目的は、CodexをNeovimの中へ表示することではありません。編集の文脈を正確に渡し、sessionを失わず、次の編集へ戻れるようにすることです。そのためにcontextとcwd、windowとprocess、安定したterminalと実験的なnative UIを分けました。

外部terminalだけで十分なら、このプラグインは必要ありません。反対に、ファイルパスや選択範囲を説明し直す作業と、Codex windowから戻る操作が積み重なっているなら、codex.nvimを使ってみてほしいです。良かった機能だけでなく、cwdの決め方やwindow移動で残る違和感もGitHub Issuesで教えてください。

github.com

github.com