Claude Code on WSL

Codex CLI WSL2 — “Done” Windows Notification

Windows balloon tip showing Codex's last reply

Problem

When Codex CLI finishes a turn on WSL2, the terminal gives no visual signal that it is done — you only notice if you switch back to the terminal yourself.

This is the Codex variant of the notification fix. It reuses the same ~/bin/claude-notify script as the Claude Code WSL2 setup (see claude-notify), but wires it up through Codex’s notify config instead of Claude hooks.

Trap: [tui].notifications = true is not this. That setting only controls the in-terminal (escape-code) notification and never runs an external program. The Windows balloon needs the separate top-level notify key — missing it means Codex never fires the balloon even with [tui].notifications = true enabled.


How It Works

  1. Codex finishes a turn and emits an agent-turn-complete event.
  2. It runs the program in the top-level notify key, passing the event JSON as the final argument. With ["bash", "-c", "<script>", "--"], the "--" becomes $0 and the JSON lands in $1.
  3. jq extracts last-assistant-message from the payload, so the balloon shows Codex’s actual last reply (truncated to 120 characters via jq’s .[0:120] slice — not head -c, which cuts bytes and can shred a multibyte UTF-8 char into mojibake), falling back to "Done!" if parsing fails.
  4. ~/bin/claude-notify "Codex" "<msg>" runs with a trailing & so it detaches immediately — Codex never blocks while the balloon is up. The reply is model output, so the script hands it to PowerShell base64-encoded: quotes in the reply cannot end a PowerShell string or run commands.
  5. The script’s GetForegroundWindow() check still applies: the balloon is suppressed when Windows Terminal is the active window, so it only appears when you’ve stepped away.

Setup

Prerequisite: the notify script and jq

This reuses ~/bin/claude-notify from claude-notify — install that first if you haven’t. Also install jq:

sudo apt install jq

Add the notify key

Add to the top-level section of ~/.codex/config.toml (before any [table] header — TOML requires top-level keys to precede tables):

notify = ["bash", "-c", "msg=$(printf '%s' \"$1\" | jq -r '(.\"last-assistant-message\" // \"Done!\") | .[0:120]' 2>/dev/null); ~/bin/claude-notify \"Codex\" \"${msg:-Done!}\" &", "--"]

Restart Codex for the config to take effect.


Result

When Windows Terminal is not the active window, a balloon tip appears in the system tray titled Codex with Codex’s last reply as the body. Clicking it restores and focuses Windows Terminal (handled by ~/bin/claude-notify). No notification fires if you are already looking at the terminal.


Troubleshooting

No balloon appears

Balloon appears for some replies but not for others

Balloon shows raw JSON instead of the reply

[tui].notifications = true is set but nothing happens

Verify and remove

Switch away from Windows Terminal and let Codex finish a turn. The balloon should show the start of the reply; a reply containing ’ also confirms an up-to-date script.

To remove, delete only the top-level notify entry you added and restart Codex. Keep ~/bin/claude-notify while Claude Code hooks or tmux-notify-run still use it.