Skip to content

Commit 9e6cebc

Browse files
StuBehanclaude
andcommitted
fix: migrate to stackvox 0.3.x unified CLI; voice was silently broken on fresh installs
stackvox 0.3.x consolidated its CLI — there is no separate `stackvox-say` console_script anymore; speech goes through `stackvox say <text>` as a subcommand. notify.sh and Speaker.swift both still pointed at the old binary, so on fresh installs (where only `stackvox` exists in the venv) the existence guard bailed silently and voice never fired. install looks healthy, daemon is running, models are downloaded, the manual `stackvox speak` works — but hooks never speak. Reported in #14 by @OMauriStkOne. notify.sh - STACKVOX_SAY removed; speak_notification now invokes "$STACKVOX" say - voice_phrase_for gates on STACKVOX existence (was STACKVOX_SAY) - nudge_debug() helper writes to stderr only when STACKNUDGE_DEBUG=true. Logs the missing-binary and missing-daemon-socket cases that were previously hidden by the silent bail. The "voice silently no-ops" failure mode is now diagnosable by setting STACKNUDGE_DEBUG=true. Speaker.swift - Same migration on the Swift side (used by the menu bar's voice toggle preview and the voice-cycle preview in Settings) - Removed the now-redundant separate stackvox / stackvoxSay paths Verified locally with stackvox 0.3.x in the venv: `stackvox say --voice af_aoede ...` runs cleanly; notify.sh's voice path completes without error when STACKNUDGE_VOICE=true. Closes #14 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a037e27 commit 9e6cebc

2 files changed

Lines changed: 29 additions & 16 deletions

File tree

notify.sh

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ voice_phrase_for() {
146146
local event="$1"
147147
local lang repo
148148

149-
if [[ -x "$STACKVOX_SAY" ]]; then
149+
if [[ -x "$STACKVOX" ]]; then
150150
lang=$(voice_to_lang "$VOICE_NAME")
151151
repo=$(repo_name_raw)
152152
else
@@ -196,25 +196,37 @@ agent_label() {
196196
esac
197197
}
198198

199-
# Bundled voice engine paths
199+
# Bundled voice engine paths. stackvox 0.3.x consolidated the CLI — there
200+
# is no separate `stackvox-say` console script anymore; speech goes through
201+
# `stackvox say <text>` as a subcommand.
200202
VENV="${HOME}/.stack-nudge/venv"
201203
STACKVOX="${VENV}/bin/stackvox"
202-
STACKVOX_SAY="${VENV}/bin/stackvox-say"
204+
205+
# Log a debug line when STACKNUDGE_DEBUG=true. Used for "voice was
206+
# requested but couldn't fire" cases that previously failed silently.
207+
nudge_debug() {
208+
[[ "${STACKNUDGE_DEBUG:-}" == "true" ]] || return 0
209+
printf '[stack-nudge] %s\n' "$*" >&2
210+
}
203211

204212
# Speak a message aloud via the bundled StackVox daemon.
205213
# Auto-starts the daemon if it isn't running. Falls back silently if the
206-
# venv isn't installed or the daemon fails to respond.
214+
# venv isn't installed or the daemon fails to respond — set STACKNUDGE_DEBUG=true
215+
# to surface why.
207216
speak_notification() {
208217
[[ "${VOICE_ENABLED}" != "true" ]] && return
209-
[[ ! -x "$STACKVOX_SAY" ]] && return
218+
if [[ ! -x "$STACKVOX" ]]; then
219+
nudge_debug "voice requested but stackvox not found at $STACKVOX"
220+
return
221+
fi
210222
local text="$1"
211-
# Start daemon if socket doesn't exist yet
212223
if [[ ! -S "${HOME}/.cache/stackvox/daemon.sock" ]]; then
224+
nudge_debug "stackvox daemon socket missing — starting daemon"
213225
nohup "$STACKVOX" serve >/dev/null 2>&1 &
214226
fi
215227
local kokoro_lang
216228
kokoro_lang=$(voice_to_kokoro_lang "$VOICE_NAME")
217-
"$STACKVOX_SAY" --voice "${VOICE_NAME}" --lang "${kokoro_lang}" --speed "${VOICE_SPEED}" "${text}" 2>/dev/null &
229+
"$STACKVOX" say --voice "${VOICE_NAME}" --lang "${kokoro_lang}" --speed "${VOICE_SPEED}" "${text}" 2>/dev/null &
218230
}
219231

220232
# Locate one of our .app bundles. Searches ~/Applications, the script's

panel/Speaker.swift

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import Foundation
22

3-
// Thin wrapper around stackvox-say. Spawns the daemon if its socket isn't
4-
// up yet (mirrors notify.sh's auto-start) and falls back to a no-op if
5-
// stackvox isn't installed in the venv.
3+
// Thin wrapper around the stackvox CLI. Spawns the daemon if its socket
4+
// isn't up yet (mirrors notify.sh's auto-start) and falls back to a no-op
5+
// if stackvox isn't installed in the venv.
6+
//
7+
// stackvox 0.3.x consolidated its CLI — there's no separate `stackvox-say`
8+
// binary anymore; speech goes through `stackvox say <text>` as a subcommand.
69
enum Speaker {
710

811
static func speak(_ text: String, voice: String? = nil, speed: String? = nil) {
912
let venvBin = "\(NSHomeDirectory())/.stack-nudge/venv/bin"
10-
let stackvoxSay = "\(venvBin)/stackvox-say"
1113
let stackvox = "\(venvBin)/stackvox"
1214
let socketPath = "\(NSHomeDirectory())/.cache/stackvox/daemon.sock"
13-
guard FileManager.default.isExecutableFile(atPath: stackvoxSay) else { return }
15+
guard FileManager.default.isExecutableFile(atPath: stackvox) else { return }
1416

15-
if !FileManager.default.fileExists(atPath: socketPath),
16-
FileManager.default.isExecutableFile(atPath: stackvox) {
17+
if !FileManager.default.fileExists(atPath: socketPath) {
1718
let serve = Process()
1819
serve.executableURL = URL(fileURLWithPath: stackvox)
1920
serve.arguments = ["serve"]
@@ -24,8 +25,8 @@ enum Speaker {
2425
let resolvedVoice = voice ?? config["STACKNUDGE_VOICE_NAME"] ?? "af_aoede"
2526
let resolvedSpeed = speed ?? config["STACKNUDGE_VOICE_SPEED"] ?? "1.1"
2627
let say = Process()
27-
say.executableURL = URL(fileURLWithPath: stackvoxSay)
28-
say.arguments = ["--voice", resolvedVoice, "--speed", resolvedSpeed, text]
28+
say.executableURL = URL(fileURLWithPath: stackvox)
29+
say.arguments = ["say", "--voice", resolvedVoice, "--speed", resolvedSpeed, text]
2930
try? say.run()
3031
}
3132
}

0 commit comments

Comments
 (0)