Ever found yourself staring at your terminal, waiting for Claude Code to finish some complex task, wondering if it crashed or if itās just thinking really hard? Yeah, me too. Thatās when I discovered Claude Code hooks and decided to make my AI coding buddy a bit more⦠chatty.
What Are These āHooksā Anyway?
Think of hooks as little event listeners that let you run custom scripts when specific things happen in Claude Code. Itās like having a butler who announces āSir Claude has finished reading your filesā or āMaster Claude is about to write some code.ā
The beauty is that you can make Claude Code integrate with whatever weird workflow youāve cobbled together over the years. Want to post to Slack when Claude finishes? Sure. Want to play a sound effect when it starts writing code? Why not. Want it to literally talk to you? Thatās exactly what I did.
My Setup: Making Claude Code Speak
Iāve configured two simple hooks that give me audio feedback during my Claude sessions using Pythonās pyttsx3
library. Now my computer tells me when Claude is doing stuff, even when Iām grabbing coffee or pretending to pay attention in meetings.
Why pyttsx3?
For this basic setup, I chose pyttsx3
because itās:
- Completely offline: No API calls or internet dependency
- Cross-platform: Works on macOS, Windows, and Linux
- Zero configuration: Just install and go
- Fast: Immediate response, no network latency
The downside? The voice sounds⦠well, like a robot from 1995. But hey, it gets the job done!
Hereās what my ~/.claude/settings.json
looks like:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "uv run ~/.claude/hooks/notification.py"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "uv run ~/.claude/hooks/stop.py"
}
]
}
]
}
}
Simple, right? Two hooks: one for notifications and one for when Claude stops working.
The Notification Script - Claudeās Voice
Hereās the script that handles general notifications (~/.claude/hooks/notification.py
):
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pyttsx3",
# ]
# ///
import sys
import json
import pyttsx3
from typing import Dict, Any
def main() -> None:
"""Main function to handle Claude hook notifications."""
try:
input_data = sys.stdin.read().strip()
if not input_data:
print("No data received from stdin", file=sys.stderr)
sys.exit(1)
hook_data = json.loads(input_data)
handle_notification(hook_data)
except json.JSONDecodeError as e:
print(f"Error parsing hook data: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error handling notification: {e}", file=sys.stderr)
sys.exit(1)
def handle_notification(data: Dict[str, Any]) -> None:
"""Handle the notification with text-to-speech."""
message = data.get("message", "Claude Code notification")
# Initialize TTS engine
engine = pyttsx3.init()
# Make it speak a bit slower so I can understand
rate = engine.getProperty('rate')
engine.setProperty('rate', rate - 50)
# Actually speak the message
engine.say(message)
engine.runAndWait()
print(f"Notification: {message}")
if __name__ == "__main__":
main()
The Stop Script - āIām Done!ā
And hereās the completion announcement (~/.claude/hooks/stop.py
):
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pyttsx3",
# ]
# ///
import pyttsx3
def main() -> None:
"""Tell me when Claude is done working."""
engine = pyttsx3.init()
# Slow it down so it's not jarring
rate = engine.getProperty('rate')
engine.setProperty('rate', rate - 50)
# The magic words
message = "Claude Code is done"
engine.say(message)
engine.runAndWait()
print(f"Stop event: {message}")
if __name__ == "__main__":
main()
Why This Actually Rocks
UV Makes Everything Easy
Notice Iām using UVās inline script feature? That #!/usr/bin/env -S uv run --script
shebang with the dependency block is pure magic. No virtual environments to manage, no requirements.txt files to forget about. UV just handles it all.
Multitasking Like a Pro
Now I can ask Claude to refactor my entire codebase and go make a sandwich. When I hear āClaude Code is done,ā I know itās time to come back and see what chaos⦠I mean, beautiful code it created.
The Unexpected Benefits
- No more zombie terminals: I know immediately if Claude is stuck or actually working
- Context switching: Perfect for when youāre juggling multiple tasks
- Meeting survival: Claude can work while youāre in āthat meeting that could have been an emailā
- Coffee break optimization: Time your caffeine runs perfectly
Other Fun Ideas for Hooks
Once you get the basic audio working, the possibilities are endless:
Git Paranoia Mode
Auto-commit before any major file changes:
{
"PreToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "git add -A && git commit -m 'Pre-Claude backup'"
}
]
}
]
}
Slack Integration
Tell your team when youāre being productive:
#!/bin/bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Claude just finished helping me code š¤"}' \
YOUR_SLACK_WEBHOOK_URL
Usage Analytics
Track how much youāre using Claude:
# Log to a CSV file
import csv
from datetime import datetime
with open('claude_usage.csv', 'a') as f:
writer = csv.writer(f)
writer.writerow([datetime.now(), tool_name, "completed"])
Hook Events You Can Play With
Claude Code gives you these events to hook into:
PreToolUse
: Before Claude uses any tool (great for validation)PostToolUse
: After a tool completes successfullyUserPromptSubmit
: When you hit enter on a promptNotification
: System notifications (what Iām using)Stop
: When Claude finishes responding (my completion hook)SessionStart
: When you start a new Claude session
Level Up: Better Voice Options
While pyttsx3
is perfect for getting started, you might eventually want something that sounds less like a 1990s computer. Once youāre hooked on audio notifications (pun intended), consider upgrading to:
ElevenLabs TTS
The Rolls Royce of text-to-speech. Want Claudeās notifications to sound like Morgan Freeman? ElevenLabs can make that happen. The voice quality is absolutely stunning, but itāll cost you per character.
OpenAI TTS
The sweet spot between quality and cost. OpenAIās voices sound natural and human-like without breaking the bank. Six different voices to choose from, and way more affordable than ElevenLabs for regular use.
Both options require API keys and internet connectivity, unlike our trusty pyttsx3
setup. But the voice quality upgrade is genuinely impressive - itās like going from dial-up to fiber internet for your ears.
Getting Started
- Create the hooks directory:
mkdir -p ~/.claude/hooks
- Copy my scripts or write your own
- Update your
~/.claude/settings.json
- Make your scripts executable:
chmod +x ~/.claude/hooks/*.py
- Test it out, then maybe upgrade to fancy voices later!
A Word of Caution
Hooks run shell commands, so donāt go copy-pasting random scripts from the internet (except mine, obviously š). Always review what youāre running, especially if it has network access or file system permissions.
Conclusion
Claude Code hooks turned my AI coding sessions from silent, mysterious processes into interactive, fun experiences. Now my computer talks to me, I never miss when Claude finishes a task, and I feel like Iām living in the future.
The best part? This is just scratching the surface. You could integrate with Discord, trigger CI/CD pipelines, update project management tools, or even make your smart lights change color when Claude is working.
So go ahead, make your Claude Code setup more fun. Your productivity will thank you, and youāll finally have a good reason to explain to your coworkers why your computer is talking to you.
Now if youāll excuse me, I need to go ask Claude to refactor something so I can hear my computer tell me itās done. It never gets old.