Get a tmux status-line notification whenever Claude Code finishes a task, needs your input, completes a long-running command, or finishes a background subagent — no external services, no macOS-specific tools, just tmux.
| Hook event | Matcher | Behavior |
|---|---|---|
Stop |
all | ✅ Claude finished in my-project |
Notification |
all | 👀 Claude needs input in my-project |
PreToolUse |
Bash |
Silently records start timestamp |
PostToolUse |
Bash |
⚡ Done (47s): npm test (≥ threshold only) |
PreToolUse |
Task |
Stashes run_in_background flag (silent) |
SubagentStop |
all | 🏁 Background task done: explore auth module |
Bash commands that finish in under 10 seconds produce no notification.
Tune the threshold with export CLAUDE_NOTIFY_THRESHOLD=15 (seconds).
Foreground subagents produce no notification — Claude is already waiting
on them. Only subagents launched with run_in_background: true trigger a
🏁 message when they complete.
All hooks run with "async": true so they never block Claude's execution.
# 1. Put the script on your PATH
mkdir -p ~/.local/bin
cp claude-tmux-notify.sh ~/.local/bin/claude-tmux-notify
chmod +x ~/.local/bin/claude-tmux-notify
# 2. Merge the hooks into your Claude Code user settings
# If you already have hooks, merge the event arrays manually.
# Otherwise just copy the file:
cp claude-hooks-settings.json ~/.claude/settings.json
# Or, to merge into an existing settings.json, use jq:
# jq -s '.[0] * .[1]' ~/.claude/settings.json claude-hooks-settings.json > /tmp/merged.json \
# && mv /tmp/merged.json ~/.claude/settings.json- tmux (any modern version)
- jq (recommended; the script has a grep/sed fallback but jq is more reliable)
- Claude Code with hooks support
- Claude Code fires a hook event and passes a JSON payload on stdin.
- Stop / Notification — the script always shows a tmux message.
- Bash commands — PreToolUse silently writes a start timestamp to
$TMPDIR/claude-tmux-notify/. When PostToolUse fires, the script computes elapsed time and only notifies if the command ran ≥ CLAUDE_NOTIFY_THRESHOLD seconds (default 10). - Background subagents — PreToolUse for the Task tool checks
tool_input.run_in_background. If true, it stashes the task description. When SubagentStop fires, the script checks for the stashed flag and only notifies for background tasks; foreground subagents are silent. - State files are keyed on
session_id + TMUX_PANE, so parallel Claude sessions in different panes don't collide. - Every hook handler sets
"async": trueso the notification never blocks Claude from continuing.
Uncomment the two lines near the bottom of the script to also set the
pane border title (visible with pane-border-status enabled in
tmux.conf). This gives you a lasting indicator until you clear it:
set -g pane-border-status top
set -g pane-border-format " #{pane_title} "
- Threshold:
export CLAUDE_NOTIFY_THRESHOLD=30to only notify for commands taking 30+ seconds. Set to0to notify on every bash command. - Sound: add
printf '\a'to the script for a terminal bell, orpaplay /usr/share/sounds/...for a Linux desktop sound. - Desktop notification: chain
notify-send(Linux) orterminal-notifier(macOS) after the tmux message for cross-app alerts.