Skip to content

Instantly share code, notes, and snippets.

@cheapsteak
Last active December 3, 2025 15:52
Show Gist options
  • Select an option

  • Save cheapsteak/615124ecf7b4d8523730ba2b216cd75b to your computer and use it in GitHub Desktop.

Select an option

Save cheapsteak/615124ecf7b4d8523730ba2b216cd75b to your computer and use it in GitHub Desktop.
#!/bin/bash
# Claude Code notification hook for macOS
#
# Setup:
# 1. Save this script to ~/.claude/hooks/notify.sh
# 2. chmod +x ~/.claude/hooks/notify.sh
# 3. brew install terminal-notifier
# 4. System Settings → Privacy & Security → Accessibility:
# - Add terminal-notifier (in /opt/homebrew/bin/ or /usr/local/bin/)
# - Add Cursor and/or VSCode
# 5. Add to ~/.claude/settings.json:
# {
# "hooks": {
# "Notification": [
# { "matcher": "permission_prompt", "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify.sh" }] },
# { "matcher": "idle_prompt", "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify.sh" }] },
# { "matcher": "elicitation_dialog", "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify.sh" }] }
# ]
# }
# }
#
# Opt-out: export CLAUDE_QUIET=1
[[ -n "$CLAUDE_QUIET" ]] && exit 0
# Read JSON from stdin and extract fields
input=$(cat)
type=$(echo "$input" | grep -o '"notification_type":"[^"]*"' | cut -d'"' -f4)
cwd=$(echo "$input" | grep -o '"cwd":"[^"]*"' | cut -d'"' -f4)
# Get folder name and git branch for window matching
folder_name=$(basename "$cwd")
branch_name=$(git -C "$cwd" rev-parse --abbrev-ref HEAD 2>/dev/null)
match_string="${branch_name:-$folder_name}"
# Set message based on notification type
case "$type" in
permission_prompt) message="Permission needed" ;;
idle_prompt) message="Waiting for input" ;;
elicitation_dialog) message="Input required" ;;
*) message="Needs attention" ;;
esac
# Detect which app spawned the terminal (VSCode or Cursor)
app_bundle="${__CFBundleIdentifier:-com.microsoft.VSCode}"
# Map bundle ID to app name for AppleScript
case "$app_bundle" in
com.todesktop.230313mzl4w4u92) app_name="Cursor" ;;
com.microsoft.VSCode) app_name="Code" ;;
*) app_name="Cursor" ;;
esac
# Check if the target window is already focused - skip notification if so
is_focused=$(osascript -e "
tell application \"System Events\"
set frontApp to name of first application process whose frontmost is true
if frontApp is \"$app_name\" then
tell process \"$app_name\"
if (count of windows) > 0 then
set winName to name of front window
if winName contains \"$match_string\" then
return \"yes\"
end if
end if
end tell
end if
return \"no\"
end tell
" 2>/dev/null)
[[ "$is_focused" == "yes" ]] && exit 0
# Use terminal-notifier if available (supports click-to-focus), otherwise osascript
if command -v terminal-notifier &>/dev/null; then
# Create AppleScript to focus the specific window via System Events
focus_script="tell application \"System Events\"
tell process \"$app_name\"
set frontmost to true
repeat with w in windows
if name of w contains \"$match_string\" then
perform action \"AXRaise\" of w
exit repeat
end if
end repeat
end tell
end tell"
terminal-notifier -title "Claude Code" -message "$message [$match_string]" \
-execute "osascript -e '$focus_script'"
else
osascript -e "display notification \"$message\" with title \"Claude Code\""
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment