Skip to content

feat(tts): add voice support via macOS say#25

Open
jorgeiglopez wants to merge 2 commits into
tute:mainfrom
jorgeiglopez:voice-for-claude
Open

feat(tts): add voice support via macOS say#25
jorgeiglopez wants to merge 2 commits into
tute:mainfrom
jorgeiglopez:voice-for-claude

Conversation

@jorgeiglopez

Copy link
Copy Markdown
Contributor

Claude is talking!
Three hooks wire up TTS:

  • SessionStart injects spoken-summary marker instructions,
  • Stop reads <!--TTS --> markers aloud (async, per sentence),
  • UserPromptSubmit interrupts speech cooperatively at sentence boundaries.

Controls:
/tts-enable
/tts-disable

Three hooks wire up TTS: SessionStart injects spoken-summary marker
instructions, Stop reads <\!--TTS --> markers aloud (async, per sentence),
UserPromptSubmit interrupts speech cooperatively at sentence boundaries.

/tts-enable and /tts-disable skills toggle ~/.claude/toolkit/tts-on.
Sandbox allowWrite extended to ~/.claude/toolkit for flag and token files.
@jorgeiglopez

Copy link
Copy Markdown
Contributor Author

Recommended config:

Settings > Accessibility > System Voice. Any of the new "Siri voices"

Also, speed up the "rate" slightly over the 50% mark (last screenshot)

image image image

Comment thread .claude/settings.json
"autoAllowBashIfSandboxed": true,
"filesystem": {
"allowWrite": [
"~/.claude/toolkit"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required to write the files that control on/off

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Teeny, let's scope per skill:

Suggested change
"~/.claude/toolkit"
"~/.claude/toolkit/tts"

Comment thread .claude/settings.json
]
}
],
"SessionStart": [

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Injected on session start so TTS can be switched on/off without restart the session

Comment thread .claude/hooks/speak.sh
@@ -0,0 +1,167 @@
#!/bin/bash

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of the logic:

  • Checks if the flag file ~/.claude/toolkit/tts-on exists. If not, exits immediately, nothing happens
  • Reads the transcript file path from the hook payload. If missing, falls back to finding the most recently modified transcript for the current project
  • Does an initial scan of the transcript to find the index of the last user message
  • Then polls only new bytes being appended to the transcript (up to 15 seconds)
    • If a newer user message arrives during polling, exits (the response it was waiting for is no longer the latest)
    • If it finds a marker in an assistant message that comes after the last user message, extracts the text and splits it into sentences
    • If 15 seconds pass with no marker found, exits
  • Writes a new unique token (process ID + timestamp) to the token file, claiming the "speaker" role
  • Launches a detached background process that loops over each sentence:
    • Before speaking each sentence, checks if it still owns the token. If not (interrupted by a new prompt or /tts-disable), stops
    • Checks if 120 seconds haven't passed
    • Pipes the sentence to macOS say
  • The hook itself exits immediately. The background process speaks on its own

@jorgeiglopez

Copy link
Copy Markdown
Contributor Author

@tute, nudge nudge 😬

@tute tute left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! 🎤

# Always injected; whether the markers are spoken is gated by ~/.claude/toolkit/tts-on
# (see speak.sh). The marker is an HTML comment so it cannot break the markdown
# rendering of the response.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this script exit 0 if not ~/.claude/toolkit/tts-on? Like speak.sh does

Comment thread .claude/hooks/speak.sh
# The final assistant message is flushed to the transcript slightly after Stop
# fires, so we poll for a marker NEWER than the last user prompt. Requires
# "async": true in the hook registration or polling would stall every turn.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add an early return (maybe logging) if TTS is ON but not on macOS.

Comment thread .claude/settings.json
"autoAllowBashIfSandboxed": true,
"filesystem": {
"allowWrite": [
"~/.claude/toolkit"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Teeny, let's scope per skill:

Suggested change
"~/.claude/toolkit"
"~/.claude/toolkit/tts"

@fedesapuppo fedesapuppo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done Nacho!! Good idea. I personally prefer to read myself, rather than Claude talking to me. That said, this is still a nice-to-have in the toolkit and I would try it for long 'grilling' sessions. It would be great to include TTS, with Superwhisper (https://superwhisper.com), as a complement, so we can have a real conversation with Claude 🚀

fedesapuppo added a commit to jorgeiglopez/railspilot-toolkit that referenced this pull request Jul 4, 2026
Adds /stt-enable, /stt-disable, and a symmetric voice-mode pair
on top of PR tute#25's TTS. Also fixes bin/install, which never
symlinked .claude/hooks.
bin/install never linked hooks, so speak.sh and the other TTS
hooks from PR tute#25 were never actually reachable after install.
@fedesapuppo

Copy link
Copy Markdown
Collaborator

no code additions needed to use STT, you just need to install Superwhisper and press the right command key

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants