HookStack
Back to catalogue
NotificationNotification

TTS voice notification

Hear it out loud when the agent needs you

Announces out loud via the system text-to-speech (say/espeak) when the agent needs the user's attention.

What does the TTS voice notification hook do?

TTS voice notification is a Claude Code Notification hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Hear it out loud when the agent needs you.

As a Notification hook it runs after the action, reacting to what just happened rather than blocking it. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.

Use cases

  • Alert the user during long autonomous runs
  • Audible notification when the agent awaits a validation

Tags

#tts#notification#voice#audio#attention

settings.json fragment

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/notification-tts.mjs",
            "type": "command"
          }
        ],
        "matcher": ""
      }
    ]
  }
}

Script · .claude/hooks/notification-tts.mjs

#!/usr/bin/env node
// @hookstack notification-tts-voice
import { execSync } from "node:child_process";
// @hookstack notification-tts-voice
// Lit les notifications Claude à voix haute via le TTS système (Notification)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

function defaultExec(cmd) {
	execSync(cmd, { timeout: 15_000, stdio: "ignore", shell: true });
}

export function run(
	input,
	{ exec = defaultExec, platform = process.platform } = {},
) {
	const message = input.message ?? input.notification ?? "";
	if (!message) return null;

	const text = message.replace(/[`*_#]/g, "").slice(0, 200);
	const safe = text.replace(/"/g, '\\"');

	try {
		// macOS: say, Linux: espeak / spd-say
		if (platform === "darwin") exec(`say "${safe}"`);
		else exec(`espeak "${safe}" 2>/dev/null || spd-say "${safe}"`);
	} catch {
		// TTS absent ou erreur — non bloquant
	}
	return text;
}

/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
	const input = JSON.parse(readFileSync(0, "utf8"));
	run(input);
}

Learn more

Related hooks