Event Hooks System
Relevant source files
The following files were used as context for generating this wiki page:
The Event Hooks system for the voice-elevenlabs plugin provides automated, hands-free voice notifications for key events within the Claude Code environment. It is designed to alert the user when a task is complete, a sub-process finishes, or when user input is required. The system works by mapping specific Claude events to shell scripts that generate and speak a context-aware message using the claude-voice-notifications CLI tool.
This system enhances user experience by providing ambient audio feedback, allowing users to be aware of Claude's status without needing to constantly monitor the terminal. It leverages the underlying text-to-speech capabilities of ElevenLabs to deliver these notifications.
Sources: README.md:65-71, hooks/hooks.json
Architecture Overview
The system is triggered by events from the Claude Code environment. A central configuration file, hooks.json, defines which events trigger which actions. For this plugin, the actions are shell scripts that process event data, construct a notification message, and pipe it to the claude-voice-notifications command-line tool, which handles the final text-to-speech conversion via the ElevenLabs API.
The following diagram illustrates the general data flow for a hook event.
This diagram shows the flow from a Claude event to the final audio notification.
Sources: hooks/hooks.json, hooks/scripts/stop-voice.sh:22, hooks/scripts/subagent-voice.sh:37
Hook Configuration
The core of the system is hooks/hooks.json, which registers the event listeners and specifies the commands to execute.
Sources: hooks/hooks.json
hooks.json Structure
The file contains a single hooks array, with each object in the array representing a single event hook.
{
"hooks": [
{
"event": "Stop",
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/stop-voice.sh",
"description": "Announce '[project]: done' when Claude finishes"
},
{
"event": "SubagentStop",
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/subagent-voice.sh",
"description": "Announce '[project]: [task]' when subagent completes"
},
{
"event": "Notification",
"type": "command",
"command": "claude-voice-notifications",
"description": "Announce when Claude needs permission or input"
}
]
}
Sources: hooks/hooks.json:1-19
Configuration Fields
| Key | Description | Example |
|---|---|---|
event | The name of the Claude event to listen for. | Stop, SubagentStop, Notification |
type | The type of action to perform. Currently always command. | command |
command | The command or script to execute. ${CLAUDE_PLUGIN_ROOT} is a variable for the plugin's root directory. | ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/stop-voice.sh |
description | A human-readable description of what the hook does. | Announce '[project]: done' when Claude finishes |
Sources: hooks/hooks.json:3-18
Registered Hooks
The plugin registers three distinct hooks for voice notifications.
| Event | Command / Script | Purpose |
|---|---|---|
Stop | stop-voice.sh | Announces that Claude has finished a general task, using the project name for context. |
SubagentStop | subagent-voice.sh | Announces the completion of a specific subagent task, including a short description of the task. |
Notification | claude-voice-notifications | Announces that Claude requires user permission or input. |
Sources: hooks/hooks.json:3-18, README.md:66-71
Hook Scripts
The Stop and SubagentStop events trigger custom shell scripts to generate more contextual notifications.
stop-voice.sh (Stop Event)
This script is executed when Claude finishes a general response. It provides a simple "done" message, contextualized with the current project's name.
Logic Flow:
- Reads JSON input from
stdin. - Extracts the current working directory (
cwd) from the input. - Determines the project name by taking the
basenameof thecwd. - Constructs a message:
"[project]: done"or"Claude done"if the project name is not available. - Pipes the message in a JSON payload to
claude-voice-notificationsfor speech synthesis.
This diagram shows the execution flow of the stop-voice.sh script.
Sources: hooks/scripts/stop-voice.sh
subagent-voice.sh (SubagentStop Event)
This script handles notifications for when a subagent completes a task. It provides a more detailed message by parsing the task description from the conversation transcript.
Logic Flow:
- Reads JSON input from
stdin. - Extracts
cwdandtranscript_path. - Gets the project name from
cwd. - If
transcript_pathis valid, it usesjqto parse the JSON transcript and find thedescriptionof the last tool use event named "Task". - Constructs a message based on the available information (project and/or task description), shortening the description to around 30-40 characters.
- Pipes the final message to
claude-voice-notifications.
This diagram shows the execution flow of the subagent-voice.sh script.
Sources: hooks/scripts/subagent-voice.sh
Notification Delivery
All hook scripts ultimately rely on the claude-voice-notifications CLI tool to generate audio. They pipe a JSON object to this command, which contains the message to be spoken. This is the same underlying mechanism used by the /speak command.
Example of the payload sent to the CLI:
echo '{"hook_event_name": "Stop", "message": "voice-elevenlabs: done"}' | claude-voice-notifications
Sources: hooks/scripts/stop-voice.sh:22, hooks/scripts/subagent-voice.sh:37, commands/speak.md:20-22