agentvibes 5.6.0 → 5.6.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.
Files changed (101) hide show
  1. package/.agentvibes/config.json +3 -38
  2. package/.claude/config/audio-effects.cfg +1 -1
  3. package/.claude/config/background-music-enabled.txt +1 -1
  4. package/.claude/config/background-music-position.txt +6 -6
  5. package/.claude/github-star-reminder.txt +1 -1
  6. package/.claude/hooks/play-tts-ssh-remote.sh +119 -42
  7. package/.claude/hooks/play-tts-windows-receiver.sh +31 -0
  8. package/.claude/hooks/stop.sh +2 -27
  9. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -108
  10. package/.claude/hooks-windows/play-tts.ps1 +58 -8
  11. package/.claude/piper-voices-dir.txt +1 -1
  12. package/.clawdbot/skill/README.md +326 -0
  13. package/.mcp.json +17 -27
  14. package/README.md +15 -2
  15. package/RELEASE_NOTES.md +64 -0
  16. package/bin/agent-vibes +39 -39
  17. package/package.json +1 -1
  18. package/src/bmad-detector.js +71 -71
  19. package/src/cli/list-personalities.js +110 -110
  20. package/src/cli/list-voices.js +114 -114
  21. package/src/commands/bmad-voices.js +394 -394
  22. package/src/commands/install-mcp.js +476 -476
  23. package/src/console/brand-colors.js +13 -13
  24. package/src/console/constants/personalities.js +44 -44
  25. package/src/console/modals/modal-overlay.js +247 -247
  26. package/src/console/navigation.js +5 -1
  27. package/src/console/tabs/agents-tab.js +5 -5
  28. package/src/console/tabs/help-tab.js +314 -314
  29. package/src/console/tabs/readme-tab.js +272 -272
  30. package/src/console/tabs/setup-tab.js +32 -17
  31. package/src/console/tabs/voices-tab.js +2 -2
  32. package/src/console/widgets/destroy-list.js +25 -25
  33. package/src/console/widgets/notice.js +55 -55
  34. package/src/console/widgets/personality-picker.js +213 -213
  35. package/src/console/widgets/reverb-picker.js +97 -97
  36. package/src/console/widgets/track-picker.js +1 -1
  37. package/src/i18n/de.js +202 -202
  38. package/src/i18n/es.js +202 -202
  39. package/src/i18n/fr.js +202 -202
  40. package/src/i18n/hi.js +202 -202
  41. package/src/i18n/ja.js +202 -202
  42. package/src/i18n/ko.js +202 -202
  43. package/src/i18n/pt.js +202 -202
  44. package/src/i18n/strings.js +54 -54
  45. package/src/i18n/zh-CN.js +202 -202
  46. package/src/installer/language-screen.js +31 -31
  47. package/src/installer/music-file-input.js +304 -304
  48. package/src/services/agent-voice-store.js +420 -423
  49. package/src/services/config-service.js +264 -264
  50. package/src/services/language-service.js +47 -47
  51. package/src/services/llm-provider-service.js +11 -4
  52. package/src/services/navigation-service.js +34 -10
  53. package/src/services/provider-service.js +143 -143
  54. package/src/utils/audio-duration-validator.js +298 -298
  55. package/src/utils/audio-format-validator.js +277 -277
  56. package/src/utils/dependency-checker.js +469 -469
  57. package/src/utils/file-ownership-verifier.js +358 -358
  58. package/src/utils/list-formatter.js +194 -194
  59. package/src/utils/music-file-validator.js +285 -285
  60. package/src/utils/preview-list-prompt.js +136 -136
  61. package/src/utils/secure-music-storage.js +412 -412
  62. package/.agentvibes/LITE-MODE.md +0 -236
  63. package/.agentvibes/README.md +0 -136
  64. package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +0 -141
  65. package/.agentvibes/backups/agents/analyst_20260204_144958.md +0 -78
  66. package/.agentvibes/backups/agents/architect_20260204_144958.md +0 -72
  67. package/.agentvibes/backups/agents/dev_20260204_144958.md +0 -74
  68. package/.agentvibes/backups/agents/pm_20260204_144958.md +0 -72
  69. package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +0 -64
  70. package/.agentvibes/backups/agents/sm_20260204_144958.md +0 -87
  71. package/.agentvibes/backups/agents/tea_20260204_144958.md +0 -79
  72. package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +0 -82
  73. package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +0 -80
  74. package/.agentvibes/config/README-personality-defaults.md +0 -162
  75. package/.agentvibes/config/agentvibes.json +0 -1
  76. package/.agentvibes/config/mode.txt +0 -1
  77. package/.agentvibes/config/personality-voice-defaults.default.json +0 -21
  78. package/.agentvibes/config/save-audio.txt +0 -1
  79. package/.agentvibes/config/voice-metadata.json +0 -160
  80. package/.agentvibes/hooks/help.sh +0 -191
  81. package/.agentvibes/hooks/post-tool-use-lite.sh +0 -111
  82. package/.agentvibes/hooks/save-audio-manager.sh +0 -162
  83. package/.agentvibes/hooks/session-start-full-optimized.sh +0 -102
  84. package/.agentvibes/hooks/session-start-full.sh +0 -142
  85. package/.agentvibes/hooks/session-start-lite-v2.sh +0 -34
  86. package/.agentvibes/hooks/session-start-lite.sh +0 -29
  87. package/.agentvibes/hooks/stop-lite.sh +0 -115
  88. package/.agentvibes/hooks/switch-mode.sh +0 -215
  89. package/.agentvibes/output-styles/audio-summary.md +0 -30
  90. package/.claude/audio/voice-samples/piper/alan.wav +0 -0
  91. package/.claude/audio/voice-samples/piper/amy.wav +0 -0
  92. package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
  93. package/.claude/audio/voice-samples/piper/joe.wav +0 -0
  94. package/.claude/audio/voice-samples/piper/john.wav +0 -0
  95. package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
  96. package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
  97. package/.claude/audio/voice-samples/piper/linda.wav +0 -0
  98. package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
  99. package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
  100. package/.claude/hooks/post-response.sh +0 -41
  101. package/bin/ensure-soprano-running.sh +0 -43
@@ -1,40 +1,5 @@
1
1
  {
2
- "defaultVoice": "libritts-speaker-925",
3
- "ttsProvider": "piper",
4
- "provider": "soprano",
5
- "backgroundMusic": {
6
- "enabled": false,
7
- "track": "agent_vibes_japanese_city_pop_v1_loop.mp3",
8
- "volume": 20
9
- },
10
- "music": {
11
- "enabled": true,
12
- "track": "agentvibes_nature_sounds_rain.mp3"
13
- },
14
- "musicFavorites": [
15
- "agentvibes_blues_smooth_guitar.mp3",
16
- "agentvibes_lofi_beats_coding.mp3",
17
- "agentvibes_nature_sounds_rain.mp3",
18
- "agent_vibes_ganawa_ambient_v2_loop.mp3"
19
- ],
20
- "effects": {
21
- "reverb": true,
22
- "reverbAmount": 0.6,
23
- "pitch": 2,
24
- "reverbPreset": "medium"
25
- },
26
- "verbosity": "high",
27
- "personality": "grandpa",
28
- "voice": "en_US-ljspeech-high",
29
- "pretext": "Agent Vibes Here",
30
- "setupCompleted": true,
31
- "ttsEngine": "piper",
32
- "thumbsUp": [
33
- "en_US-libritts-high::Adam",
34
- "en_US-libritts_r-medium::Adam-2"
35
- ],
36
- "favorites": [
37
- "en_US-libritts-high::Adam",
38
- "en_US-libritts_r-medium::Adam-2"
39
- ]
2
+ "audio_destination": "remote",
3
+ "audio_ssh_alias": "laptop-win",
4
+ "setupCompleted": true
40
5
  }
@@ -55,7 +55,7 @@ default|reverb 20 50 50|agentvibes_soft_flamenco_loop.mp3|0.30
55
55
  # Format: llm:<name>|REVERB_PRESET|BACKGROUND_FILE|BACKGROUND_VOLUME|VOICE|PRETEXT|ENGINE|||
56
56
  |||
57
57
  # Claude Code (claude-code CLI via CLAUDECODE=1 env or AGENTVIBES_LLM=claude-code)|||
58
- llm:claude-code|||0.15||Agent Vibes Here|piper
58
+ llm:claude-code|off||||Agent Vibes Here|piper
59
59
  |||
60
60
  # GitHub Copilot Chat (VS Code MCP or AGENTVIBES_LLM=copilot)|||
61
61
  llm:copilot|off||||Agent Vibes Here|piper
@@ -1 +1 @@
1
- true
1
+ true
@@ -16,12 +16,12 @@ Agent Vibes Ganawa Ambient v2-loop.mp3:.00000000000000000002815996
16
16
  Agent Vibes Tabla Dream Pop v1-loop.mp3:.00000000000000000009067943
17
17
  Agent Vibes ChillWave v2-loop.mp3:.00000000000000000007080511
18
18
  Agent Vibes Harpsichord v2-loop.mp3:.00000000000000000013140818
19
- agent_vibes_japanese_city_pop_v1_loop.mp3:6.054512
20
19
  agent_vibes_bossa_nova_v2_loop.mp3:5.369524
21
20
  agent_vibes_salsa_v2_loop.mp3:9.972790
22
- agent_vibes_cumbia_v1_loop.mp3:5.717823
23
21
  agent_vibes_arabic_v2_loop.mp3:.00000000000000000006132724
24
- agent_vibes_chillwave_v2_loop.mp3:14.628390
25
- agent_vibes_bachata_v1_loop.mp3:.00000000000000000005344000
26
- agent_vibes_goa_trance_v2_loop.mp3:.00000000000000000002499918
27
- agentvibes_soft_flamenco_loop.mp3:9.432018
22
+ agent_vibes_cumbia_v1_loop.mp3:.00000000000000000010256000
23
+ agentvibes_soft_flamenco_loop.mp3:7.389660
24
+ agent_vibes_bachata_v1_loop.mp3:5.125714
25
+ agent_vibes_chillwave_v2_loop.mp3:.00000000000000000003308191
26
+ Midnight Charleston Stomp.mp3:.00000000000000000010960000
27
+ agent_vibes_japanese_city_pop_v1_loop.mp3:11.702675
@@ -1 +1 @@
1
- 20260428
1
+ 20260502
@@ -2,8 +2,11 @@
2
2
  #
3
3
  # File: .claude/hooks/play-tts-ssh-remote.sh
4
4
  #
5
- # AgentVibes - SSH-Remote TTS Provider
6
- # Sends text to remote device via SSH for local AgentVibes playback
5
+ # AgentVibes - SSH-Remote TTS Provider (v2 — JSON payload)
6
+ # Sends text + effects config to remote device via SSH for local playback
7
+ #
8
+ # The sender reads local audio-effects.cfg and bundles everything into a
9
+ # single base64-encoded JSON payload. The receiver is a thin executor.
7
10
  #
8
11
  # Copyright (c) 2025 Paul Preibisch
9
12
  # Licensed under the Apache License, Version 2.0
@@ -17,74 +20,148 @@ AGENT_NAME="${3:-default}"
17
20
 
18
21
  # Validate required input
19
22
  if [[ -z "$TEXT" ]]; then
20
- echo "❌ No text provided" >&2
21
23
  echo "Usage: $0 <text> [voice] [agent_name]" >&2
22
24
  exit 1
23
25
  fi
24
26
 
25
- # Get script directory
27
+ # Get script directory and project root
26
28
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
27
29
  PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
28
30
 
31
+ # Derive project name from directory
32
+ PROJECT_NAME=$(basename "$PROJECT_ROOT")
33
+
34
+ # ---------------------------------------------------------------------------
29
35
  # Get SSH host from config
36
+ # ---------------------------------------------------------------------------
37
+
30
38
  SSH_HOST=$(cat "$PROJECT_ROOT/.claude/ssh-remote-host.txt" 2>/dev/null || \
31
39
  cat "$HOME/.claude/ssh-remote-host.txt" 2>/dev/null || echo "")
32
40
 
33
41
  if [[ -z "$SSH_HOST" ]]; then
34
- echo "SSH-Remote host not configured" >&2
35
- echo "💡 Set host: echo 'android' > ~/.claude/ssh-remote-host.txt" >&2
42
+ echo "SSH-Remote host not configured" >&2
43
+ echo "Set host: echo 'my-host' > .claude/ssh-remote-host.txt" >&2
36
44
  exit 1
37
45
  fi
38
46
 
39
- # SECURITY: Validate SSH_HOST to prevent option injection
40
- # Must be a valid hostname, IP address, or SSH config alias (alphanumeric, dots, hyphens, underscores)
47
+ # SECURITY: Validate SSH_HOST format
41
48
  if [[ ! "$SSH_HOST" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
42
- echo "Invalid SSH host format: $SSH_HOST" >&2
43
- echo "💡 Host must be alphanumeric (may contain dots, hyphens, underscores)" >&2
44
- exit 1
45
- fi
46
-
47
- # SECURITY: Reject hosts starting with hyphen (SSH option injection)
48
- if [[ "$SSH_HOST" == -* ]]; then
49
- echo "❌ Invalid SSH host: cannot start with hyphen" >&2
49
+ echo "Invalid SSH host format: $SSH_HOST" >&2
50
50
  exit 1
51
51
  fi
52
52
 
53
- # SECURITY: Validate VOICE to prevent injection (alphanumeric, hyphens, underscores only)
53
+ # SECURITY: Validate VOICE
54
54
  if [[ ! "$VOICE" =~ ^[a-zA-Z0-9_-]+$ ]]; then
55
- echo "Invalid voice format: $VOICE" >&2
55
+ echo "Invalid voice format: $VOICE" >&2
56
56
  exit 1
57
57
  fi
58
58
 
59
- # SECURITY: Validate AGENT_NAME to prevent injection (alphanumeric, hyphens, underscores, spaces only)
59
+ # SECURITY: Validate AGENT_NAME
60
60
  if [[ ! "$AGENT_NAME" =~ ^[a-zA-Z0-9_\ -]+$ ]]; then
61
- echo "Invalid agent name format: $AGENT_NAME" >&2
61
+ echo "Invalid agent name format: $AGENT_NAME" >&2
62
62
  exit 1
63
63
  fi
64
64
 
65
- # SECURITY: Encode text and agent name as base64 to prevent command injection
66
- # The receiver will decode these safely
67
- ENCODED_TEXT=$(printf '%s' "$TEXT" | base64 -w 0)
68
- ENCODED_AGENT=$(printf '%s' "$AGENT_NAME" | base64 -w 0)
69
-
70
- # Send text to remote for local AgentVibes playback
71
- echo "📱 Sending to $SSH_HOST for local playback..." >&2
72
-
73
- # Determine which receiver script exists and send encoded text, voice, and agent name
74
- # SECURITY: Base64-encoded values are safe to pass as arguments (no shell metacharacters)
75
- # The receiver auto-detects and decodes base64 input
76
- if ssh "$SSH_HOST" "test -f ~/.termux/agentvibes-play.sh" 2>/dev/null; then
77
- ssh "$SSH_HOST" "bash ~/.termux/agentvibes-play.sh '$ENCODED_TEXT' '$VOICE' '$ENCODED_AGENT'" &
78
- SSH_PID=$!
79
- elif ssh "$SSH_HOST" "test -f ~/.agentvibes/play-remote.sh" 2>/dev/null; then
80
- ssh "$SSH_HOST" "bash ~/.agentvibes/play-remote.sh '$ENCODED_TEXT' '$VOICE' '$ENCODED_AGENT'" &
81
- SSH_PID=$!
65
+ # ---------------------------------------------------------------------------
66
+ # Read audio effects config for this agent
67
+ # ---------------------------------------------------------------------------
68
+
69
+ SOX_EFFECTS=""
70
+ BG_FILE=""
71
+ BG_VOLUME="0.10"
72
+
73
+ EFFECTS_CFG="$PROJECT_ROOT/.claude/config/audio-effects.cfg"
74
+ if [[ -f "$EFFECTS_CFG" ]]; then
75
+ CONFIG_LINE=$(grep "^${AGENT_NAME}|" "$EFFECTS_CFG" 2>/dev/null || \
76
+ grep "^default|" "$EFFECTS_CFG" 2>/dev/null || true)
77
+ if [[ -n "$CONFIG_LINE" ]]; then
78
+ IFS='|' read -r _ SOX_EFFECTS BG_FILE BG_VOLUME <<< "$CONFIG_LINE"
79
+ fi
80
+ fi
81
+
82
+ # Read pretext if configured
83
+ PRETEXT=""
84
+ PRETEXT_FILE="$PROJECT_ROOT/.agentvibes/config/pretext.txt"
85
+ if [[ -f "$PRETEXT_FILE" ]]; then
86
+ PRETEXT=$(cat "$PRETEXT_FILE" 2>/dev/null || true)
87
+ fi
88
+
89
+ # Read speed if configured
90
+ SPEED=""
91
+ SPEED_FILE="$PROJECT_ROOT/.agentvibes/config/speed.txt"
92
+ if [[ -f "$SPEED_FILE" ]]; then
93
+ SPEED=$(cat "$SPEED_FILE" 2>/dev/null || true)
94
+ fi
95
+
96
+ # Read the TTS provider the RECEIVER should use to generate audio.
97
+ # This is separate from the sender's own provider (which is "ssh-remote").
98
+ # Check receiver-provider.txt first, then fall back to "piper".
99
+ PROVIDER=""
100
+ RECEIVER_PROVIDER_FILE="$PROJECT_ROOT/.agentvibes/config/receiver-provider.txt"
101
+ if [[ -f "$RECEIVER_PROVIDER_FILE" ]]; then
102
+ PROVIDER=$(cat "$RECEIVER_PROVIDER_FILE" 2>/dev/null || true)
103
+ fi
104
+ # Also check home-level config
105
+ if [[ -z "$PROVIDER" ]]; then
106
+ RECEIVER_PROVIDER_FILE="$HOME/.agentvibes/config/receiver-provider.txt"
107
+ if [[ -f "$RECEIVER_PROVIDER_FILE" ]]; then
108
+ PROVIDER=$(cat "$RECEIVER_PROVIDER_FILE" 2>/dev/null || true)
109
+ fi
110
+ fi
111
+ # Validate — only known TTS providers (not transport providers like ssh-remote)
112
+ case "${PROVIDER:-}" in
113
+ piper|soprano|macos|windows-sapi) ;;
114
+ *) PROVIDER="piper" ;;
115
+ esac
116
+
117
+ # ---------------------------------------------------------------------------
118
+ # Build JSON payload
119
+ # ---------------------------------------------------------------------------
120
+
121
+ # SECURITY: Use jq if available for safe JSON construction, else manual escaping
122
+ build_json_payload() {
123
+ if command -v jq &>/dev/null; then
124
+ jq -n \
125
+ --arg text "$TEXT" \
126
+ --arg voice "$VOICE" \
127
+ --arg effects "$SOX_EFFECTS" \
128
+ --arg music "$BG_FILE" \
129
+ --arg volume "$BG_VOLUME" \
130
+ --arg project "$PROJECT_NAME" \
131
+ --arg pretext "$PRETEXT" \
132
+ --arg speed "$SPEED" \
133
+ --arg provider "$PROVIDER" \
134
+ '{text: $text, voice: $voice, effects: $effects, music: $music, volume: $volume, project: $project, pretext: $pretext, speed: $speed, provider: $provider}'
135
+ else
136
+ # Manual JSON — escape double quotes and backslashes in text
137
+ local escaped_text
138
+ escaped_text=$(printf '%s' "$TEXT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')
139
+ local escaped_pretext
140
+ escaped_pretext=$(printf '%s' "$PRETEXT" | sed 's/\\/\\\\/g; s/"/\\"/g')
141
+ printf '{"text":"%s","voice":"%s","effects":"%s","music":"%s","volume":"%s","project":"%s","pretext":"%s","speed":"%s","provider":"%s"}' \
142
+ "$escaped_text" "$VOICE" "$SOX_EFFECTS" "$BG_FILE" "$BG_VOLUME" "$PROJECT_NAME" "$escaped_pretext" "$SPEED" "$PROVIDER"
143
+ fi
144
+ }
145
+
146
+ JSON_PAYLOAD=$(build_json_payload)
147
+
148
+ # SECURITY: Base64-encode entire payload — safe for SSH transport
149
+ # base64 -w 0 is Linux (GNU coreutils), -b 0 is macOS (BSD)
150
+ if base64 --help 2>&1 | grep -q '\-w'; then
151
+ ENCODED_PAYLOAD=$(printf '%s' "$JSON_PAYLOAD" | base64 -w 0)
82
152
  else
83
- echo "⚠️ Receiver script not found on $SSH_HOST" >&2
84
- echo "💡 Install: agentvibes install --ssh-receiver" >&2
85
- exit 1
153
+ ENCODED_PAYLOAD=$(printf '%s' "$JSON_PAYLOAD" | base64 -b 0 2>/dev/null || printf '%s' "$JSON_PAYLOAD" | base64 | tr -d '\n')
86
154
  fi
87
155
 
88
- # Log the background PID for debugging (non-blocking)
89
- echo "✓ Sent to $SSH_HOST (PID: $SSH_PID, playing remotely)" >&2
156
+ # ---------------------------------------------------------------------------
157
+ # Send to receiver via SSH (fire and forget — backgrounded)
158
+ # ---------------------------------------------------------------------------
159
+
160
+ echo "Sending to $SSH_HOST..." >&2
161
+
162
+ # ForceCommand receiver: SSH_ORIGINAL_COMMAND passes the payload directly
163
+ ssh "$SSH_HOST" "$ENCODED_PAYLOAD" &
164
+ SSH_PID=$!
165
+
166
+ echo "Sent to $SSH_HOST (PID: $SSH_PID)" >&2
90
167
  exit 0
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # play-tts-windows-receiver.sh
4
+ # Sends TTS to Windows AgentVibes receiver via SSH.
5
+ # The Windows receiver expects bare base64-encoded text as the SSH command.
6
+ #
7
+ set -euo pipefail
8
+
9
+ TEXT="${1:-}"
10
+ VOICE="${2:-}"
11
+
12
+ [[ -z "$TEXT" ]] && { echo 'No text' >&2; exit 1; }
13
+
14
+ # Get host
15
+ HOST=$(cat "$HOME/.claude/windows-receiver-host.txt" 2>/dev/null || echo '')
16
+ [[ -z "$HOST" ]] && { echo '❌ No host: set ~/.claude/windows-receiver-host.txt' >&2; exit 1; }
17
+
18
+ # Validate host
19
+ [[ "$HOST" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]] || { echo '❌ Invalid host' >&2; exit 1; }
20
+
21
+ # Strip markdown/emojis
22
+ TEXT=$(printf '%s' "$TEXT" | perl -CSD -pe '
23
+ s/[\x{1F300}-\x{1F9FF}]//g;
24
+ s/[\x{2600}-\x{27BF}]//g;
25
+ s/\*+//g; s/#+\s*//g; s/`//g;
26
+ ' 2>/dev/null || printf '%s' "$TEXT")
27
+
28
+ ENCODED=$(printf '%s' "$TEXT" | base64 -w 0)
29
+
30
+ echo "🖥️ Sending to Windows receiver ($HOST)…" >&2
31
+ RESULT=$(ssh "$HOST" "$ENCODED" 2>&1) && echo "✓ $RESULT" >&2 || echo "⚠️ $RESULT" >&2 &
@@ -32,32 +32,7 @@ fi
32
32
  # Only run stop hook in LITE mode
33
33
  # (Full mode uses tool calls for TTS, not stop hooks)
34
34
  if [[ "$CURRENT_MODE" == "lite" ]]; then
35
- # Claude Code sends JSON on stdin: {"transcript_path": "..."}
36
- HOOK_INPUT=$(cat)
37
- TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path // empty' 2>/dev/null || true)
38
-
39
- [[ -z "$TRANSCRIPT_PATH" ]] && exit 0
40
- [[ ! -f "$TRANSCRIPT_PATH" ]] && exit 0
41
-
42
- # Extract Audio Summary ONLY from the most recent assistant response.
43
- # Key insight: we must NOT search earlier responses — if the latest response
44
- # has no Audio Summary: marker, we stay silent (no stale replay).
45
- SUMMARY=$(jq --slurp -r '
46
- [.[] | try select(.message.role == "assistant")] | last // null
47
- | if . == null then "" else
48
- [.message.content[]? | select(.type == "text") | .text] | join("\n")
49
- end
50
- | if test("[Aa]udio [Ss]ummary:") then . else "" end
51
- ' "$TRANSCRIPT_PATH" 2>/dev/null \
52
- | sed -n 's/.*[Aa]udio [Ss]ummary:[[:space:]]*\(.*\)/\1/p' \
53
- | head -1 \
54
- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
55
-
56
- [[ -z "$SUMMARY" ]] && exit 0
57
-
58
- # Speak via AgentVibes play-tts.sh
59
- if [[ -f "$PROJECT_ROOT/.claude/hooks/play-tts.sh" ]]; then
60
- AGENTVIBES_MIN_TOKENS=3 AGENTVIBES_SHORT_TOKENS=50 \
61
- bash "$PROJECT_ROOT/.claude/hooks/play-tts.sh" "$SUMMARY" >/dev/null 2>&1 &
35
+ if [[ -f "$AGENTVIBES_HOOKS/stop-lite.sh" ]]; then
36
+ bash "$AGENTVIBES_HOOKS/stop-lite.sh" "$@"
62
37
  fi
63
38
  fi
@@ -1,108 +1,108 @@
1
- #
2
- # File: .claude/hooks-windows/play-tts-windows-sapi.ps1
3
- #
4
- # AgentVibes - Windows SAPI TTS Provider (Zero Dependencies)
5
- # Uses built-in Windows System.Speech API
6
- #
7
-
8
- param(
9
- [Parameter(Mandatory = $true)]
10
- [string]$Text,
11
-
12
- [Parameter(Mandatory = $false)]
13
- [string]$VoiceOverride
14
- )
15
-
16
- # Configuration paths
17
- $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
18
- $ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
19
-
20
- if (Test-Path $ProjectClaudeDir) {
21
- $ClaudeDir = $ProjectClaudeDir
22
- } else {
23
- $ClaudeDir = "$env:USERPROFILE\.claude"
24
- }
25
-
26
- $AudioDir = "$ClaudeDir\audio"
27
- $VoiceFile = "$ClaudeDir\tts-voice-sapi.txt"
28
-
29
- # Ensure directories exist
30
- if (-not (Test-Path $AudioDir)) {
31
- New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
32
- }
33
-
34
- # Load System.Speech assembly
35
- try {
36
- Add-Type -AssemblyName System.Speech
37
- }
38
- catch {
39
- Write-Host "[ERROR] System.Speech assembly not available" -ForegroundColor Red
40
- exit 1
41
- }
42
-
43
- # Determine voice to use
44
- $VoiceName = ""
45
-
46
- if ($VoiceOverride) {
47
- $VoiceName = $VoiceOverride
48
- }
49
- elseif (Test-Path $VoiceFile) {
50
- $VoiceName = (Get-Content $VoiceFile -Raw).Trim()
51
- }
52
-
53
- # Initialize speech synthesizer
54
- $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
55
-
56
- # Set voice if specified
57
- if ($VoiceName) {
58
- try {
59
- $synth.SelectVoice($VoiceName)
60
- }
61
- catch {
62
- Write-Host "[WARNING] Voice '$VoiceName' not found, using default" -ForegroundColor Yellow
63
- }
64
- }
65
-
66
- # Sanitize text for speech - strip shell metacharacters, PS special chars, and SSML tags
67
- $Text = $Text -replace '\\', ' '
68
- $Text = $Text -replace '[{}<>|`~^$;"''()]', ''
69
- $Text = $Text -replace '&[a-zA-Z]+;', ''
70
- $Text = $Text -replace '\s+', ' '
71
- $Text = $Text.Trim()
72
-
73
- # Get actual voice name (after selection or default)
74
- $ActualVoice = $synth.Voice.Name
75
-
76
- # Create audio file path
77
- $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
78
- $AudioFile = "$AudioDir\tts-$Timestamp.wav"
79
-
80
- # Save to WAV file with proper resource cleanup
81
- $player = $null
82
- try {
83
- $synth.SetOutputToWaveFile($AudioFile)
84
- $synth.Speak($Text)
85
-
86
- # Display results
87
- Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
88
- Write-Host "[VOICE] Voice used: $ActualVoice (Windows SAPI)" -ForegroundColor Green
89
-
90
- # Play the audio using built-in Windows audio player (skip if AGENTVIBES_NO_PLAY is set)
91
- if (-not $env:AGENTVIBES_NO_PLAY) {
92
- try {
93
- $player = New-Object System.Media.SoundPlayer $AudioFile
94
- $player.PlaySync()
95
- }
96
- catch {
97
- Write-Host "[WARNING] Could not play audio (SoundPlayer unavailable)" -ForegroundColor Yellow
98
- }
99
- }
100
- }
101
- catch {
102
- Write-Host "[ERROR] Error synthesizing speech: $_" -ForegroundColor Red
103
- exit 1
104
- }
105
- finally {
106
- if ($synth) { $synth.Dispose() }
107
- if ($player) { $player.Dispose() }
108
- }
1
+ #
2
+ # File: .claude/hooks-windows/play-tts-windows-sapi.ps1
3
+ #
4
+ # AgentVibes - Windows SAPI TTS Provider (Zero Dependencies)
5
+ # Uses built-in Windows System.Speech API
6
+ #
7
+
8
+ param(
9
+ [Parameter(Mandatory = $true)]
10
+ [string]$Text,
11
+
12
+ [Parameter(Mandatory = $false)]
13
+ [string]$VoiceOverride
14
+ )
15
+
16
+ # Configuration paths
17
+ $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
18
+ $ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
19
+
20
+ if (Test-Path $ProjectClaudeDir) {
21
+ $ClaudeDir = $ProjectClaudeDir
22
+ } else {
23
+ $ClaudeDir = "$env:USERPROFILE\.claude"
24
+ }
25
+
26
+ $AudioDir = "$ClaudeDir\audio"
27
+ $VoiceFile = "$ClaudeDir\tts-voice-sapi.txt"
28
+
29
+ # Ensure directories exist
30
+ if (-not (Test-Path $AudioDir)) {
31
+ New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
32
+ }
33
+
34
+ # Load System.Speech assembly
35
+ try {
36
+ Add-Type -AssemblyName System.Speech
37
+ }
38
+ catch {
39
+ Write-Host "[ERROR] System.Speech assembly not available" -ForegroundColor Red
40
+ exit 1
41
+ }
42
+
43
+ # Determine voice to use
44
+ $VoiceName = ""
45
+
46
+ if ($VoiceOverride) {
47
+ $VoiceName = $VoiceOverride
48
+ }
49
+ elseif (Test-Path $VoiceFile) {
50
+ $VoiceName = (Get-Content $VoiceFile -Raw).Trim()
51
+ }
52
+
53
+ # Initialize speech synthesizer
54
+ $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
55
+
56
+ # Set voice if specified
57
+ if ($VoiceName) {
58
+ try {
59
+ $synth.SelectVoice($VoiceName)
60
+ }
61
+ catch {
62
+ Write-Host "[WARNING] Voice '$VoiceName' not found, using default" -ForegroundColor Yellow
63
+ }
64
+ }
65
+
66
+ # Sanitize text for speech - strip shell metacharacters, PS special chars, and SSML tags
67
+ $Text = $Text -replace '\\', ' '
68
+ $Text = $Text -replace '[{}<>|`~^$;"''()]', ''
69
+ $Text = $Text -replace '&[a-zA-Z]+;', ''
70
+ $Text = $Text -replace '\s+', ' '
71
+ $Text = $Text.Trim()
72
+
73
+ # Get actual voice name (after selection or default)
74
+ $ActualVoice = $synth.Voice.Name
75
+
76
+ # Create audio file path
77
+ $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
78
+ $AudioFile = "$AudioDir\tts-$Timestamp.wav"
79
+
80
+ # Save to WAV file with proper resource cleanup
81
+ $player = $null
82
+ try {
83
+ $synth.SetOutputToWaveFile($AudioFile)
84
+ $synth.Speak($Text)
85
+
86
+ # Display results
87
+ Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
88
+ Write-Host "[VOICE] Voice used: $ActualVoice (Windows SAPI)" -ForegroundColor Green
89
+
90
+ # Play the audio using built-in Windows audio player (skip if AGENTVIBES_NO_PLAY is set)
91
+ if (-not $env:AGENTVIBES_NO_PLAY) {
92
+ try {
93
+ $player = New-Object System.Media.SoundPlayer $AudioFile
94
+ $player.PlaySync()
95
+ }
96
+ catch {
97
+ Write-Host "[WARNING] Could not play audio (SoundPlayer unavailable)" -ForegroundColor Yellow
98
+ }
99
+ }
100
+ }
101
+ catch {
102
+ Write-Host "[ERROR] Error synthesizing speech: $_" -ForegroundColor Red
103
+ exit 1
104
+ }
105
+ finally {
106
+ if ($synth) { $synth.Dispose() }
107
+ if ($player) { $player.Dispose() }
108
+ }