My setup for Claude Code + Codex Notifications in iTerm2 and VS Code
Here is how I set up Claude Code and Codex to give me desktop notifications on Mac when a turn completes or when they need approval.
I set this up to work in VS Code integrated terminal and in iTerm2.
Caveat: this info is as of March 2026. I wouldn’t be surprised if it became out of date, as the coding agent harnesses are constantly being updated and features added.
Tell your agent to follow the instructions on this site, and report back any issues it had, and let me know!
This setup uses:
1. A Claude Code Stop hook and a PermissionRequest hook that emit OSC 9.
2. A Codex notify script plus TUI notification settings.
3. The VS Code extension that listens for terminal OSC notifications.
1. Set up Claude Code notifications
Your Claude settings are at ~/.claude/settings.json.
Add a Stop hook that emits OSC 9 with a shortened preview of the last assistant message, and a PermissionRequest hook that fires when Claude is waiting for you to approve a tool call:
{
"hooks": {
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "STDIN=$(cat); TOOL=$(echo \"$STDIN\" | jq -r '.tool_name'); CMD=$(echo \"$STDIN\" | jq -r '.tool_input.command // \"\"' | head -c 60); MSG=\"Claude needs approval: $TOOL\"; if [ -n \"$CMD\" ]; then MSG=\"$MSG: $CMD\"; fi; printf '\\e]9;%s\\e\\\\' \"$MSG\" > /dev/tty"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "STDIN=$(cat); TRANSCRIPT=$(echo \"$STDIN\" | jq -r '.transcript_path'); sleep 0.5; FULL=$(tac \"$TRANSCRIPT\" | jq -r 'select(.type==\"assistant\") | .message.content[]? | select(.type==\"text\") | .text' 2>/dev/null | head -1); MSG=$(printf '%.50s' \"$FULL\"); if [ \"$MSG\" != \"$FULL\" ]; then MSG=\"$MSG...\"; fi; printf '\\e]9;Claude: %s\\e\\\\' \"$MSG\" > /dev/tty"
}
]
}
]
}
}
The PermissionRequest hook fires before Claude shows an approval dialog — it tells you which tool needs approval and, if it’s a shell command, shows a short preview of the command.
The sleep 0.5 in the Stop hook gives the transcript file a moment to flush before reading it.
2. Set up Codex notifications
Your Codex config is at ~/.codex/config.toml.
There are two notification mechanisms in Codex:
notify = [...]— an external script hook. Fires when a turn completes and receives a JSON payload with the last assistant message. Good for custom previews.tui.notifications— built-in TUI notifications. Supportsagent-turn-completeandapproval-requested.
The key thing to know: the notify external hook fires only for agent-turn-complete, not for approval-requested. So my setup is:
Use
notifyfor a richer turn-complete notification (e.g. with a message preview).Use
tui.notifications = [“approval-requested”]for approval prompts, so you get pinged when Codex is waiting on you.
Here is what it looks like in the config file:
notify = [”bash”, “/path/to/notify-osc9.sh”]
[tui]
notifications = [”approval-requested”]
notification_method = “osc9”This is what the notify-osc9.sh script looks like:
#!/usr/bin/env bash
set -euo pipefail
NOTIFICATION_JSON="${1:-{}}"
MSG="$(printf '%s' "$NOTIFICATION_JSON" | jq -r '.["last-assistant-message"] // ""' 2>/dev/null || true)"
MSG="${MSG//$'\n'/ }"
MSG="$(printf '%s' "$MSG" | tr -s '[:space:]' ' ' | sed 's/^ //; s/ $//')"
if [ -z "$MSG" ]; then
MSG="Turn complete"
fi
MAX_LEN=50
if [ "${#MSG}" -gt "$MAX_LEN" ]; then
MSG="${MSG:0:$MAX_LEN}..."
fi
printf '\e]9;Codex: %s\e\\' "$MSG" > /dev/tty 2>/dev/null || trueMake it executable: chmod +x /path/to/notify-osc9.sh
3. Install the VS Code extension
Install Terminal Notification:
Marketplace: <https://marketplace.visualstudio.com/items?itemName=wenbopan.vscode-terminal-osc-notifier>
Or command line:
code --install-extension wenbopan.vscode-terminal-osc-notifier
This extension makes VSCode show notifications when your terminal prints an OSC sequence like OSC 9.
4. Dependencies
Both setups use jq, and the Claude hook also uses tac.
5. Testing
In the VS Code integrated terminal, run: printf ‘\e]9;Test notification\e\\’
If that pops a notification, your extension path is working. Then run one turn in Codex and Claude Code to verify end-to-end.
Are there other hooks or notification events to use?
For Codex: As of March 2026, the only notification event names are agent-turn-complete and approval-requested. The external notify hook only fires for agent-turn-complete. There is no way to hook into other Codex lifecycle events — those two are all that exist as of Codex CLI 0.107.0.
Codex seems to be actively working on this, might change soon! See https://github.com/openai/codex/issues/2109
For Claude Code: Claude Code has a full hook system with many events. Here is the full list, with notes on which are useful for notifications:
SessionStart— Session begins or resumesUserPromptSubmit— When you submit a promptPreToolUse— Before any tool call (can block it)PermissionRequest— When an approval dialog appearsPostToolUse— After a tool call succeedsPostToolUseFailure— After a tool call failsNotification— When Claude Code sends a notificationSubagentStart— When a subagent is spawnedSubagentStop— When a subagent finishesStop— When Claude finishes respondingTeammateIdle— Agent team teammate going idleTaskCompleted— A task is marked completedConfigChange— Config file changes during sessionWorktreeCreate— Worktree being createdWorktreeRemove— Worktree being removedPreCompact— Before context compactionSessionEnd— Session terminates
For most setups, Stop and PermissionRequest are all you need. The Notification hook is also interesting — it supports a matcher on type, and the idle_prompt type fires when Claude is waiting for your input after finishing a long session.


The notification gap was driving me mad; you kick off a Codex task and just sit there wondering if it's done or waiting on you. One thing that pairs well with this setup is being deliberate about your approval mode. I start in suggest on a new codebase and graduate to auto-edit once the AGENTS.md is dialled in. Covered that progression here: https://reading.sh/how-to-actually-get-good-results-from-codex-cli-98d412eb3ff7?sk=be1b8b631f39c5ac4edec722c9eb111d