read

Codex is really slow, with it’s high reasoning model much slower than Claude Opus.

Codex CLI is still great… until it asks for approval or finishes a long task and you miss it because there’s no obvious “ping me” setup.

Here’s the setup that made notifications work on macOS:

  • OSC 9 notifications via iTerm2 (Codex TUI → macOS Notification Center)
  • A notify hook that calls terminal-notifier (with sound)

Oh yea, not sure why they didn’t bake notification in.

1) Enable iTerm2 “escape sequence” notifications

In iTerm2, go to:

Settings → Profiles → Terminal → Notifications → Enable ✅ Send escape sequence-generated alerts

This matters because Codex can emit OSC 9 notifications, and iTerm2 can turn those into Notification Center alerts.

Quick OSC9 sanity test

Run this in iTerm2:

printf '\e]9;OSC9 test notification\a'

If you see a macOS notification, iTerm2 is correctly configured.

The iTerm notification is good for Approval prompts (approval-requested).

2) Configure Codex TUI notifications (approval + completion)

Codex’s tui.notification_method is specifically for unfocused terminal notifications (so don’t be surprised if it only pops when you’ve Cmd-Tabbed away).

Add this to ~/.codex/config.toml:

[tui]
notifications = ["agent-turn-complete", "approval-requested"]
notification_method = "osc9"

3) Add the notify hook (always-on completion + sound)

Codex supports a notify hook to run a program when supported events fire — currently only agent-turn-complete.

Important TOML gotcha

Codex’s sample config explicitly notes: Root keys must appear before tables in TOML.

So put notify = ... near the top (before any [tui], [mcp_servers.*], etc).

Example ~/.codex/config.toml skeleton:

# Root keys (keep these before any [tables])
notify = ["/bin/bash", "/Users/YOU/.codex/hooks/notify.sh"]

4) The notify script (the “actually works every time” version)

Install terminal-notifier with brew install terminal-notifier

Create: ~/.codex/hooks/notify.sh

#!/bin/bash
set -euo pipefail

# Hooks often run with a minimal PATH
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

NOTIFIER="$(command -v terminal-notifier || true)"
LOG="/tmp/codex-notify.log"

# Log for debugging (and to avoid spam in the Codex UI)
exec >>"$LOG" 2>&1
echo "----- $(date) -----"
echo "argv: $*"

TITLE="Codex CLI"
MESSAGE="Codex finished a task."

# Codex passes JSON as argv[1] (not stdin)
PAYLOAD="${1:-}"
if [[ -n "$PAYLOAD" ]]; then
  MSG_FROM_JSON="$(python3 - <<'PY' "$PAYLOAD" 2>/dev/null || true
import json,sys
j=json.loads(sys.argv[1])
print((j.get("last-assistant-message") or "").strip())
PY
)"
  if [[ -n "${MSG_FROM_JSON:-}" ]]; then
    MESSAGE="$MSG_FROM_JSON"
  fi
fi

# terminal-notifier can treat messages starting with '-' like flags (and show help)
if [[ "$MESSAGE" == "-"* ]]; then
  MESSAGE="• ${MESSAGE#- }"
fi

# Optional: trim super long messages
MESSAGE="${MESSAGE:0:220}"

if [[ -n "$NOTIFIER" ]]; then
  "$NOTIFIER" \
    -title "$TITLE" \
    -message "$MESSAGE" \
    -sound default \
    -activate "com.googlecode.iterm2" \
    >/dev/null 2>&1 || true
else
  echo "terminal-notifier not found" >&2
fi

Make it executable chmod +x ~/.codex/hooks/notify.sh.

Test it manually

~/.codex/hooks/notify.sh '{"last-assistant-message":"- done (test)"}'
tail -n 20 /tmp/codex-notify.log

If that shows a notification, your script is fine.

That’s it. With OSC9 (iTerm2) you’ll catch approval prompts, and with the notify hook you’ll get a dependable completion alert + sound (even if you’re not watching the terminal).


Image

@samwize

¯\_(ツ)_/¯

Back to Home