Claude Code + Tmux: How I got notifications working

Davy Cardinaal · · Updated

I run multiple Claude Code sessions in tmux throughout the day. One pane has Claude refactoring a component, another is debugging an API issue, and I'm actively working in a third. The problem? I kept missing when Claude finished or needed permission to continue working, switching back only to find it had been waiting for minutes. After some digging, I figured out how to get notifications working properly with tmux. Here's what I learned.

Why the default setup doesn't work

Claude Code has built-in notification support for iTerm2, but when I enabled it, nothing happened in my tmux setup. Turns out there are two issues:

  1. Tmux buffers output from background panes, so the notification escape sequences never reach iTerm2
  2. Hook output doesn't automatically go to the terminal

The fix

The solution combines Claude Code's hook system with tmux's bell forwarding. When Claude stops or requests permission, we send a bell character that tmux forwards to iTerm2 and triggers a notification.

Step 1: Configure tmux

I added these lines to my ~/.tmux.conf:

# Allow escape sequences to pass through to iTerm2
set -g allow-passthrough on

# Forward bells from ALL panes to iTerm2 (not just the active one)
set -g bell-action any

# Enable bell monitoring
set -g monitor-bell on

Then reload the config: tmux source-file ~/.tmux.conf

Step 2: Set up Claude Code hooks

This is where I got stuck for a while. I added hooks to my ~/.claude/settings.json:

{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "printf '\\a' > /dev/tty"
}
]
}
],
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "printf '\\a' > /dev/tty"
}
]
}
]
}
}

The crucial part I missed initially: > /dev/tty. Without this redirect, the bell character doesn't reach the terminal. Once I added it, everything clicked.

What these hooks do

  • Stop: Fires when Claude finishes and waits for my next prompt
  • PermissionRequest: Fires when Claude needs permission to run a command or edit a file

Step 3: Configure iTerm2

  1. Open iTerm2 → Preferences → Profiles → Terminal
  2. Enable alerts for bell events under Notifications
  3. Optionally enable "Send Notification Center alerts" for visual notifications

How it all connects

┌─────────────────────────────────────────────────────────┐
│ Claude Code (background pane) │
│ └─> Hook triggers printf '\a' > /dev/tty │
│ └─> Bell reaches pane's TTY │
│ └─> tmux (bell-action any) forwards to iTerm2 │
│ └─> iTerm2 sends macOS notification │
└─────────────────────────────────────────────────────────┘

The result

Now I hear a ping whenever any of my Claude sessions need attention. No more context-switching to check on progress, no more wasted time waiting on a session that's been idle. It's a small fix, but it's made my multi-session workflow significantly smoother.

Troubleshooting

If you're not getting notifications, here's what I'd check:

No sound at all?

  • Test with printf '\a' in any pane. You should hear a bell
  • Check iTerm2's bell settings aren't silenced

Sound works manually but not from hooks?

  • Make sure you included > /dev/tty in your hook command
  • Verify your settings.json syntax is valid JSON

Sound only works in the active pane?

  • Confirm bell-action any is set: tmux show-options -g bell-action
  • Reload tmux config after making changes


If you're juggling multiple Claude Code sessions like I do, this setup is worth the few minutes it takes to configure.