Skip to content

Instantly share code, notes, and snippets.

@moritzWa
Last active March 9, 2026 01:20
Show Gist options
  • Select an option

  • Save moritzWa/24e0d05932ea7433c51462739fba1d6f to your computer and use it in GitHub Desktop.

Select an option

Save moritzWa/24e0d05932ea7433c51462739fba1d6f to your computer and use it in GitHub Desktop.
Claude Code hooks: completion sound + smart TTS announcement + auto-generated terminal tab titles via Groq (macOS)
#!/bin/bash
# Claude Code "Stop" hook: completion sound + smart TTS announcement (macOS)
#
# - Always plays a sound when Claude Code finishes
# - Announces the project name via TTS only if you're NOT focused on that project's window
# - Uses git repo root name as project name (falls back to folder name)
#
# Setup:
# 1. Save this script to ~/.claude/hooks/done-announce.sh
# 2. chmod +x ~/.claude/hooks/done-announce.sh
# 3. Add to ~/.claude/settings.json:
# {
# "hooks": {
# "Stop": [
# {
# "hooks": [
# {
# "type": "command",
# "command": "~/.claude/hooks/done-announce.sh"
# }
# ]
# }
# ]
# }
# }
#
# 4. Grant accessibility permissions for smart TTS (required to detect focused window):
# a. Open System Settings > Privacy & Security > Accessibility
# (or run: open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")
# b. Click the "+" button
# c. Press Cmd+Shift+G to open the "Go to folder" dialog
# d. Type /usr/bin/osascript and press Enter
# e. Toggle it on
# Without this, the script still works - it just always announces (can't detect focus).
#
# Customize:
# - Volume: change -v 4.0 (range: 0.0 to ~5.0)
# - Sound: swap Funk.aiff for Hero.aiff, Glass.aiff, Ping.aiff, etc.
# (available in /System/Library/Sounds/)
# - Voice: change -v Samantha to Alex, Daniel (British), etc.
# (run `say -v "?"` to list all voices)
INPUT=$(cat)
CWD=$(echo "$INPUT" | jq -r '.cwd')
GIT_ROOT=$(git -C "$CWD" rev-parse --show-toplevel 2>/dev/null)
if [ -n "$GIT_ROOT" ]; then
PROJECT=$(basename "$GIT_ROOT")
else
PROJECT=$(basename "$CWD")
fi
afplay -v 4.0 /System/Library/Sounds/Funk.aiff
FOCUSED_WINDOW=$(osascript -e 'tell application "System Events" to get name of front window of (first application process whose frontmost is true)' 2>/dev/null)
if ! echo "$FOCUSED_WINDOW" | grep -qi "$PROJECT"; then
say -v Samantha "$PROJECT finished"
fi
#!/bin/bash
# Claude Code "Stop" hook: completion sound + smart TTS announcement (macOS)
#
# - Always plays a sound when Claude Code finishes
# - If auto-title.sh has generated a session title, announces that (e.g. "fix email body finished")
# - Otherwise falls back to announcing the project/repo name
# - Only announces via TTS if you're NOT focused on that project's window
#
# Setup:
# 1. Save both scripts to ~/.claude/hooks/
# 2. chmod +x ~/.claude/hooks/done-announce.sh ~/.claude/hooks/auto-title.sh
# 3. Add to ~/.claude/settings.json:
# {
# "hooks": {
# "Stop": [
# {
# "hooks": [
# {
# "type": "command",
# "command": "~/.claude/hooks/auto-title.sh"
# },
# {
# "type": "command",
# "command": "~/.claude/hooks/done-announce.sh"
# }
# ]
# }
# ]
# }
# }
#
# 4. Grant accessibility permissions for smart TTS (required to detect focused window):
# a. Open System Settings > Privacy & Security > Accessibility
# (or run: open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")
# b. Click the "+" button
# c. Press Cmd+Shift+G to open the "Go to folder" dialog
# d. Type /usr/bin/osascript and press Enter
# e. Toggle it on
# Without this, the script still works - it just always announces (can't detect focus).
#
# Customize:
# - Volume: change -v 4.0 (range: 0.0 to ~5.0)
# - Sound: swap Funk.aiff for Hero.aiff, Glass.aiff, Ping.aiff, etc.
# (available in /System/Library/Sounds/)
# - Voice: change -v Samantha to Alex, Daniel (British), etc.
# (run `say -v "?"` to list all voices)
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')
CWD=$(echo "$INPUT" | jq -r '.cwd')
GIT_ROOT=$(git -C "$CWD" rev-parse --show-toplevel 2>/dev/null)
if [ -n "$GIT_ROOT" ]; then
PROJECT=$(basename "$GIT_ROOT")
else
PROJECT=$(basename "$CWD")
fi
# Use generated title if available, otherwise fall back to project name
TITLE_FILE="/tmp/claude-title-$SESSION_ID"
if [ -f "$TITLE_FILE" ]; then
ANNOUNCE_NAME=$(cat "$TITLE_FILE" | tr '-' ' ')
else
ANNOUNCE_NAME="$PROJECT"
fi
afplay -v 4.0 /System/Library/Sounds/Funk.aiff
FOCUSED_WINDOW=$(osascript -e 'tell application "System Events" to get name of front window of (first application process whose frontmost is true)' 2>/dev/null)
if ! echo "$FOCUSED_WINDOW" | grep -qi "$PROJECT"; then
say -v Samantha "$ANNOUNCE_NAME finished"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment