claude-can-speak 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -11
- package/bin/claude-can-speak +59 -16
- package/bin/postinstall.js +28 -0
- package/lib/claude-can-speak/tts-speak.sh +7 -18
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
# claude-can-speak
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
**Let Claude decide what to say out loud.** Most "speak Claude Code aloud" tools
|
|
4
|
+
read *every* reply at you. claude-can-speak leads with the opposite: a Claude
|
|
5
|
+
Code **skill** that gives the model a deliberate "say this" capability, so Claude
|
|
6
|
+
voices only what is worth hearing, a spoken "the build is done and tests passed"
|
|
7
|
+
when you have stepped away, a heads-up that a deploy needs confirmation, a short
|
|
8
|
+
shoutout you asked for, while everything else stays text-only. Selective, on
|
|
9
|
+
purpose, model-controlled.
|
|
10
|
+
|
|
11
|
+
If you *do* want the firehose, it is one command away: a Stop hook that speaks
|
|
12
|
+
every finished reply, gated on the built-in `/voice` mode so one switch controls
|
|
13
|
+
both directions (you talk to it, it talks back). But the deliberate skill is the
|
|
14
|
+
point.
|
|
15
|
+
|
|
16
|
+
- **Deliberate mode (the headline)** - the `speak` skill lets Claude choose what
|
|
17
|
+
to voice. Install with `claude-can-speak install-skill`.
|
|
18
|
+
- **Firehose mode (optional)** - a Stop hook speaks every reply while `/voice` is
|
|
19
|
+
on. Install with `claude-can-speak install-hooks`.
|
|
14
20
|
|
|
15
21
|
Speech is synthesised locally by [Kokoro](https://github.com/thewh1teagle/kokoro-onnx)
|
|
16
22
|
(natural English, the default) or [Piper](https://github.com/OHF-Voice/piper1-gpl)
|
|
@@ -138,6 +144,20 @@ behalf. **By installing and using it you accept all risk.** You are responsible
|
|
|
138
144
|
for complying with the licences of the bundled engines and the downloaded models
|
|
139
145
|
(see [THIRD_PARTY.md](THIRD_PARTY.md)).
|
|
140
146
|
|
|
147
|
+
## Related projects
|
|
148
|
+
|
|
149
|
+
Speaking Claude Code's replies aloud is a well-trodden idea, and several tools do
|
|
150
|
+
the firehose well: `claude-voice` (Kokoro plus karaoke word highlighting),
|
|
151
|
+
`claude-code-tts` (OpenAI or Kokoro auto-speak), `claude-voice-mcp` and
|
|
152
|
+
`soliloquy-tts` (MCP-based auto-speak). If all you want is "read every reply
|
|
153
|
+
aloud", any of those is a fine choice and lighter than this one (no Docker).
|
|
154
|
+
|
|
155
|
+
claude-can-speak is built around a different default: the deliberate `speak`
|
|
156
|
+
skill, so Claude voices only what is worth hearing rather than everything. It
|
|
157
|
+
also adds multilingual output (Piper for German, Turkish, and more), Docker
|
|
158
|
+
isolation so the engines never touch your host Python, and gating on the built-in
|
|
159
|
+
`/voice` switch. The firehose mode is included, but it is not the headline.
|
|
160
|
+
|
|
141
161
|
## Licence
|
|
142
162
|
|
|
143
163
|
MIT - see [LICENSE](LICENSE). Author: Ramazan Yavuz. Part of the public,
|
package/bin/claude-can-speak
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
# any damage or loss arising from its use. By using it you accept all risk.
|
|
10
10
|
set -uo pipefail
|
|
11
11
|
|
|
12
|
-
VERSION="0.1.
|
|
12
|
+
VERSION="0.1.2"
|
|
13
13
|
|
|
14
14
|
# Install layout: bundled scripts live in ../lib/claude-can-speak relative to
|
|
15
15
|
# this CLI, whether installed via npm (into the global node_modules) or run from
|
|
@@ -27,6 +27,7 @@ LIBEXEC="$SELF/../lib/claude-can-speak"
|
|
|
27
27
|
CCS_HOME="${CCS_HOME:-$HOME/.config/claude-can-speak}"
|
|
28
28
|
CONFIG="$CCS_HOME/config.env"
|
|
29
29
|
PIDFILE="$CCS_HOME/speaking.pid"
|
|
30
|
+
ENABLED_FLAG="$CCS_HOME/firehose.enabled"
|
|
30
31
|
CONTAINER="${CCS_CONTAINER:-ccs-tts}"
|
|
31
32
|
IMAGE="${CCS_IMAGE:-claude-can-speak:latest}"
|
|
32
33
|
MODELS_DIR="${CCS_MODELS_DIR:-$HOME/.cache/claude-can-speak/models}"
|
|
@@ -64,10 +65,12 @@ USAGE
|
|
|
64
65
|
claude-can-speak <command> [args]
|
|
65
66
|
|
|
66
67
|
COMMANDS
|
|
67
|
-
|
|
68
|
+
setup One-shot install: Docker check, build, skill, hook.
|
|
69
|
+
on | off Turn the firehose (speak every reply) on or off. Default off.
|
|
70
|
+
status Show firehose state, container, config, and model cache.
|
|
68
71
|
test [text] Speak a sample (or the given text) with the current voice.
|
|
69
72
|
stop Interrupt any reply currently being spoken.
|
|
70
|
-
say <text> Speak arbitrary text now (
|
|
73
|
+
say <text> Speak arbitrary text now (always speaks; used by the skill).
|
|
71
74
|
start | up Start the persistent TTS container.
|
|
72
75
|
stop-container Stop and remove the TTS container.
|
|
73
76
|
voice <name> Set the default voice (e.g. af_heart, af_bella).
|
|
@@ -81,16 +84,19 @@ COMMANDS
|
|
|
81
84
|
help | --help This text.
|
|
82
85
|
|
|
83
86
|
TWO MODES
|
|
84
|
-
|
|
85
|
-
(claude-can-speak
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
Deliberate (the headline): the 'speak' skill lets Claude choose what to
|
|
88
|
+
voice (notifications, shoutouts) via 'claude-can-speak say'.
|
|
89
|
+
Install with 'claude-can-speak install-skill'.
|
|
90
|
+
Firehose (optional): the Stop hook speaks every reply when the firehose is
|
|
91
|
+
on. Install the hook with 'claude-can-speak install-hooks', then
|
|
92
|
+
toggle with 'claude-can-speak on' / 'off'.
|
|
89
93
|
|
|
90
94
|
GATING
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
The firehose has its own explicit on/off switch ('claude-can-speak on|off',
|
|
96
|
+
default OFF), stored in ~/.config/claude-can-speak/firehose.enabled. It is
|
|
97
|
+
intentionally NOT tied to Claude Code's /voice, which is speech-IN dictation
|
|
98
|
+
and is a separate concern. The deliberate 'speak' skill always speaks when
|
|
99
|
+
invoked, regardless of the firehose switch.
|
|
94
100
|
|
|
95
101
|
DISCLAIMER
|
|
96
102
|
Provided AS IS, with NO WARRANTY. You accept all risk. See the project
|
|
@@ -179,12 +185,11 @@ cmd_test() {
|
|
|
179
185
|
|
|
180
186
|
cmd_status() {
|
|
181
187
|
echo "claude-can-speak $VERSION"
|
|
182
|
-
printf '
|
|
183
|
-
if [ -f "$
|
|
184
|
-
|
|
185
|
-
echo "ON (/voice enabled)"
|
|
188
|
+
printf 'firehose : '
|
|
189
|
+
if [ -f "$ENABLED_FLAG" ]; then
|
|
190
|
+
echo "ON (replies spoken; 'claude-can-speak off' to silence)"
|
|
186
191
|
else
|
|
187
|
-
echo "off (
|
|
192
|
+
echo "off (replies silent; 'claude-can-speak on' to enable)"
|
|
188
193
|
fi
|
|
189
194
|
printf 'engine/voice : %s / %s (%s)\n' "$ENGINE" "$VOICE" "$LANG"
|
|
190
195
|
printf 'image : '; ensure_image && echo "$IMAGE present" || echo "$IMAGE MISSING (run: build)"
|
|
@@ -233,6 +238,41 @@ EOF
|
|
|
233
238
|
fi
|
|
234
239
|
}
|
|
235
240
|
|
|
241
|
+
cmd_on() {
|
|
242
|
+
mkdir -p "$CCS_HOME"
|
|
243
|
+
: > "$ENABLED_FLAG"
|
|
244
|
+
echo "firehose ON: replies will be spoken (needs the Stop hook; run install-hooks if you have not)."
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
cmd_off() {
|
|
248
|
+
rm -f "$ENABLED_FLAG" 2>/dev/null
|
|
249
|
+
# Also stop anything currently speaking.
|
|
250
|
+
cmd_stop >/dev/null 2>&1 || true
|
|
251
|
+
echo "firehose OFF: replies will not be spoken."
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
# One-shot setup: the happy path after `npm install -g`. Docker check, build the
|
|
255
|
+
# image, install the deliberate skill and the firehose hook. Idempotent.
|
|
256
|
+
cmd_setup() {
|
|
257
|
+
echo "claude-can-speak setup"
|
|
258
|
+
require_docker
|
|
259
|
+
echo "1/3 building the TTS container image (first time pulls deps, ~2 min) ..."
|
|
260
|
+
cmd_build
|
|
261
|
+
echo "2/3 installing the 'speak' skill ..."
|
|
262
|
+
cmd_install_skill
|
|
263
|
+
echo "3/3 installing the firehose Stop hook ..."
|
|
264
|
+
cmd_install_hooks
|
|
265
|
+
cat <<EOF
|
|
266
|
+
|
|
267
|
+
Setup complete.
|
|
268
|
+
- Deliberate mode: Claude can voice notifications via the 'speak' skill (active now).
|
|
269
|
+
- Firehose mode: turn it on with 'claude-can-speak on' (off by default).
|
|
270
|
+
Restart Claude Code once so it loads the new skill and hook, then try:
|
|
271
|
+
claude-can-speak on
|
|
272
|
+
claude-can-speak test
|
|
273
|
+
EOF
|
|
274
|
+
}
|
|
275
|
+
|
|
236
276
|
cmd_install_skill() {
|
|
237
277
|
# Locate the packaged skill (deb vs git checkout).
|
|
238
278
|
local src
|
|
@@ -280,6 +320,9 @@ cmd_remove_hooks() {
|
|
|
280
320
|
}
|
|
281
321
|
|
|
282
322
|
case "${1:-help}" in
|
|
323
|
+
setup) cmd_setup ;;
|
|
324
|
+
on) cmd_on ;;
|
|
325
|
+
off) cmd_off ;;
|
|
283
326
|
status) cmd_status ;;
|
|
284
327
|
test) shift; cmd_test "$@" ;;
|
|
285
328
|
say) shift; cmd_say "$@" ;;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Printed once after `npm install -g claude-can-speak`. Deliberately does NO
|
|
3
|
+
// heavy or side-effecting work (no docker build, no editing ~/.claude): it only
|
|
4
|
+
// tells the user the single next command. The real setup is explicit and
|
|
5
|
+
// user-chosen, which keeps `npm install` fast, quiet, and unsurprising.
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
// Skip the banner in CI / non-interactive installs to avoid log noise.
|
|
9
|
+
if (process.env.CI || process.env.npm_config_loglevel === "silent") process.exit(0);
|
|
10
|
+
|
|
11
|
+
const L = [
|
|
12
|
+
"",
|
|
13
|
+
" claude-can-speak installed.",
|
|
14
|
+
"",
|
|
15
|
+
" One more step (needs Docker):",
|
|
16
|
+
" claude-can-speak setup",
|
|
17
|
+
"",
|
|
18
|
+
" That builds the local TTS container and installs the 'speak' skill",
|
|
19
|
+
" plus the optional firehose hook. Then restart Claude Code once.",
|
|
20
|
+
"",
|
|
21
|
+
" Deliberate mode (Claude voices notifications) is on after setup.",
|
|
22
|
+
" Firehose mode (speak every reply) is off by default; turn it on with:",
|
|
23
|
+
" claude-can-speak on",
|
|
24
|
+
"",
|
|
25
|
+
" Provided AS IS, no warranty. https://ra-yavuz.github.io/claude-can-speak/",
|
|
26
|
+
"",
|
|
27
|
+
];
|
|
28
|
+
process.stdout.write(L.join("\n") + "\n");
|
|
@@ -17,7 +17,6 @@ set -uo pipefail
|
|
|
17
17
|
# --- Resolve config -------------------------------------------------------
|
|
18
18
|
CCS_HOME="${CCS_HOME:-$HOME/.config/claude-can-speak}"
|
|
19
19
|
CCS_CONFIG="$CCS_HOME/config.env"
|
|
20
|
-
SETTINGS_JSON="${CLAUDE_SETTINGS:-$HOME/.claude/settings.json}"
|
|
21
20
|
CONTAINER="${CCS_CONTAINER:-ccs-tts}"
|
|
22
21
|
IMAGE="${CCS_IMAGE:-claude-can-speak:latest}"
|
|
23
22
|
MODELS_DIR="${CCS_MODELS_DIR:-$HOME/.cache/claude-can-speak/models}"
|
|
@@ -51,23 +50,13 @@ done
|
|
|
51
50
|
# --- Read the Stop hook payload ------------------------------------------
|
|
52
51
|
PAYLOAD="$(cat)"
|
|
53
52
|
|
|
54
|
-
# --- Gate: only speak when
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"$SETTINGS_JSON" 2>/dev/null)"
|
|
62
|
-
[ "$v" = "true" ]
|
|
63
|
-
return
|
|
64
|
-
fi
|
|
65
|
-
# jq-less fallback: grep the two known keys.
|
|
66
|
-
grep -Eq '"voiceEnabled"[[:space:]]*:[[:space:]]*true' "$SETTINGS_JSON" && return 0
|
|
67
|
-
grep -Eq '"enabled"[[:space:]]*:[[:space:]]*true' "$SETTINGS_JSON" && return 0
|
|
68
|
-
return 1
|
|
69
|
-
}
|
|
70
|
-
voice_on || { log "voice gate off; silent"; exit 0; }
|
|
53
|
+
# --- Gate: only speak when the firehose is explicitly ON ------------------
|
|
54
|
+
# claude-can-speak owns its own on/off state, decoupled from Claude Code's
|
|
55
|
+
# /voice (which is speech-IN dictation and is not reliably readable here).
|
|
56
|
+
# Default is OFF: the state file exists only when the user ran
|
|
57
|
+
# `claude-can-speak on`. This guarantees a real, predictable off-switch.
|
|
58
|
+
ENABLED_FLAG="${CCS_ENABLED_FLAG:-$CCS_HOME/firehose.enabled}"
|
|
59
|
+
[ -f "$ENABLED_FLAG" ] || { log "firehose off; silent"; exit 0; }
|
|
71
60
|
|
|
72
61
|
# --- Extract the reply text ----------------------------------------------
|
|
73
62
|
extract_text() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-can-speak",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Let Claude Code decide what to say out loud: a skill that voices deliberate notifications (not every reply), plus an optional speak-everything Stop hook. Local neural TTS via Kokoro/Piper in Docker, gated on /voice.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
@@ -28,8 +28,12 @@
|
|
|
28
28
|
"bin": {
|
|
29
29
|
"claude-can-speak": "bin/cli.js"
|
|
30
30
|
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"postinstall": "node bin/postinstall.js"
|
|
33
|
+
},
|
|
31
34
|
"files": [
|
|
32
35
|
"bin/cli.js",
|
|
36
|
+
"bin/postinstall.js",
|
|
33
37
|
"bin/claude-can-speak",
|
|
34
38
|
"lib/claude-can-speak/",
|
|
35
39
|
"container/",
|