Fixing Claude Code Notifications in tmux + Ghostty
The Fix
The solution uses Claude Code's hook system to intercept notification events and re-emit them in the DCS-wrapped format that tmux expects.
Step 1: Enable passthrough in tmux
Add this to your ~/.tmux.conf if you haven't already:
set -g allow-passthrough on
Reload with tmux source-file ~/.tmux.conf or set it live in your current session with Ctrl-b : (or your prefix key) followed by set -g allow-passthrough on.
Step 2: Create the notification hook script
mkdir -p ~/.claude/hooks
cat > ~/.claude/hooks/tmux-notify.sh << 'EOF'
#!/bin/bash
# Only wrap for tmux - let Claude Code handle non-tmux natively
[ -z "$TMUX" ] && exit 0
read -r input
message=$(echo "$input" | jq -r '.message // "Claude Code"')
# Must output to /dev/tty - hook stdout is captured by Claude Code
printf '\033Ptmux;\033\033]9;%s\007\033\\' "$message" > /dev/tty
EOF
chmod +x ~/.claude/hooks/tmux-notify.sh
The script checks for the $TMUX environment variable and only wraps the sequence when running inside tmux. It reads the notification payload from Claude Code, extracts the message with jq, and writes the DCS-wrapped OSC 9 sequence directly to /dev/tty (since hook stdout is captured by Claude Code).
You'll need jq installed — brew install jq on macOS or apt install jq on Linux.
Step 3: Register the hook with Claude Code
Add the hook to your Claude Code settings. You can put this in ~/.claude/settings.json for a global config, or .claude/settings.json in a specific project:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/tmux-notify.sh"
}
]
}
]
}
}
Step 4: Verify it works
From inside tmux, run this to confirm the DCS passthrough is working:
sleep 3 && printf '\033Ptmux;\033\033]9;Test notification\007\033\\' > /dev/tty
Switch away from Ghostty before the 3 seconds are up. Ghostty suppresses notifications for the focused window, so you need to be looking at another app to see the banner.