agentvibes 5.7.6 → 5.9.0

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 (137) hide show
  1. package/.agentvibes/config.json +12 -5
  2. package/.agentvibes/install-manifest.json +188 -300
  3. package/.claude/audio/tracks/celestial_velvet.mp3 +0 -0
  4. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  5. package/.claude/commands/agent-vibes-rdp.md +24 -24
  6. package/.claude/config/audio-effects.cfg +3 -2
  7. package/.claude/config/audio-effects.cfg.sample +52 -52
  8. package/.claude/config/background-music-enabled.txt +1 -0
  9. package/.claude/config/background-music-position.txt +1 -1
  10. package/.claude/config/language.txt +1 -0
  11. package/.claude/docs/TERMUX_SETUP.md +408 -408
  12. package/.claude/hooks/audio-cache-utils.sh +0 -0
  13. package/.claude/hooks/audio-processor.sh +0 -0
  14. package/.claude/hooks/background-music-manager.sh +0 -0
  15. package/.claude/hooks/bmad-party-speak.sh +27 -6
  16. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  17. package/.claude/hooks/bmad-speak.sh +0 -0
  18. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  19. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  20. package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
  21. package/.claude/hooks/clawdbot-receiver.sh +0 -0
  22. package/.claude/hooks/clean-audio-cache.sh +0 -0
  23. package/.claude/hooks/cleanup-cache.sh +0 -0
  24. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  25. package/.claude/hooks/download-extra-voices.sh +0 -0
  26. package/.claude/hooks/effects-manager.sh +0 -0
  27. package/.claude/hooks/github-star-reminder.sh +0 -0
  28. package/.claude/hooks/language-manager.sh +0 -0
  29. package/.claude/hooks/learn-manager.sh +0 -0
  30. package/.claude/hooks/macos-voice-manager.sh +0 -0
  31. package/.claude/hooks/migrate-background-music.sh +0 -0
  32. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  33. package/.claude/hooks/optimize-background-music.sh +0 -0
  34. package/.claude/hooks/path-resolver.sh +0 -0
  35. package/.claude/hooks/personality-manager.sh +0 -0
  36. package/.claude/hooks/piper-download-voices.sh +0 -0
  37. package/.claude/hooks/piper-installer.sh +0 -0
  38. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  39. package/.claude/hooks/piper-voice-manager.sh +0 -0
  40. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
  41. package/.claude/hooks/play-tts-agentvibes-receiver.sh +1 -0
  42. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  43. package/.claude/hooks/play-tts-macos.sh +0 -0
  44. package/.claude/hooks/play-tts-piper.sh +0 -0
  45. package/.claude/hooks/play-tts-soprano.sh +0 -0
  46. package/.claude/hooks/play-tts-ssh-remote.sh +11 -8
  47. package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
  48. package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
  49. package/.claude/hooks/play-tts.sh +0 -0
  50. package/.claude/hooks/prepare-release.sh +0 -0
  51. package/.claude/hooks/provider-commands.sh +0 -0
  52. package/.claude/hooks/provider-manager.sh +0 -0
  53. package/.claude/hooks/replay-target-audio.sh +0 -0
  54. package/.claude/hooks/requirements.txt +6 -6
  55. package/.claude/hooks/sentiment-manager.sh +0 -0
  56. package/.claude/hooks/session-start-tts.sh +0 -0
  57. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  58. package/.claude/hooks/speed-manager.sh +0 -0
  59. package/.claude/hooks/stop-tts.sh +0 -0
  60. package/.claude/hooks/termux-installer.sh +0 -0
  61. package/.claude/hooks/translate-manager.sh +0 -0
  62. package/.claude/hooks/translator.py +237 -237
  63. package/.claude/hooks/tts-queue-worker.sh +0 -0
  64. package/.claude/hooks/tts-queue.sh +0 -0
  65. package/.claude/hooks/verbosity-manager.sh +0 -0
  66. package/.claude/hooks/voice-manager.sh +0 -0
  67. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  68. package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
  69. package/.claude/hooks-windows/bmad-speak.ps1 +9 -38
  70. package/.claude/hooks-windows/play-tts-soprano.ps1 +13 -2
  71. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  72. package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
  73. package/.claude/piper-voices-dir.txt +1 -1
  74. package/.claude/verbosity.txt +1 -1
  75. package/.clawdbot/README.md +105 -105
  76. package/.mcp.json +5 -14
  77. package/README.md +43 -2
  78. package/RELEASE_NOTES.md +110 -0
  79. package/WINDOWS-SETUP.md +208 -208
  80. package/bin/agent-vibes +39 -39
  81. package/bin/agentvibes-voice-browser.js +0 -0
  82. package/bin/agentvibes.js +0 -0
  83. package/bin/mcp-server.js +121 -121
  84. package/bin/mcp-server.sh +0 -0
  85. package/bin/test-bmad-pr +78 -78
  86. package/mcp-server/QUICK_START.md +203 -203
  87. package/mcp-server/README.md +345 -345
  88. package/mcp-server/WINDOWS_SETUP.md +0 -0
  89. package/mcp-server/examples/claude_desktop_config.json +11 -11
  90. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  91. package/mcp-server/examples/custom_instructions.md +169 -169
  92. package/mcp-server/install-deps.js +0 -0
  93. package/mcp-server/server.py +1797 -1797
  94. package/mcp-server/test_server.py +0 -0
  95. package/package.json +1 -1
  96. package/src/cli/list-personalities.js +110 -110
  97. package/src/cli/list-voices.js +114 -114
  98. package/src/commands/bmad-voices.js +394 -394
  99. package/src/commands/install-mcp.js +476 -476
  100. package/src/console/audio-env.js +4 -1
  101. package/src/console/brand-colors.js +13 -13
  102. package/src/console/constants/personalities.js +44 -44
  103. package/src/console/tabs/agents-tab.js +85 -62
  104. package/src/console/tabs/help-tab.js +314 -314
  105. package/src/console/tabs/music-tab.js +3 -0
  106. package/src/console/tabs/readme-tab.js +272 -272
  107. package/src/console/tabs/setup-tab.js +285 -41
  108. package/src/console/tabs/voices-tab.js +14 -2
  109. package/src/console/widgets/destroy-list.js +25 -25
  110. package/src/console/widgets/notice.js +55 -55
  111. package/src/i18n/de.js +202 -202
  112. package/src/i18n/es.js +202 -202
  113. package/src/i18n/fr.js +202 -202
  114. package/src/i18n/hi.js +202 -202
  115. package/src/i18n/ja.js +202 -202
  116. package/src/i18n/ko.js +202 -202
  117. package/src/i18n/pt.js +202 -202
  118. package/src/i18n/strings.js +54 -54
  119. package/src/i18n/zh-CN.js +202 -202
  120. package/src/installer/language-screen.js +31 -31
  121. package/src/installer/music-file-input.js +304 -304
  122. package/src/installer.js +0 -0
  123. package/src/services/config-service.js +264 -264
  124. package/src/services/language-service.js +47 -47
  125. package/src/services/provider-service.js +143 -143
  126. package/src/utils/audio-duration-validator.js +298 -298
  127. package/src/utils/audio-format-validator.js +277 -277
  128. package/src/utils/dependency-checker.js +469 -469
  129. package/src/utils/file-ownership-verifier.js +358 -358
  130. package/src/utils/list-formatter.js +194 -194
  131. package/src/utils/music-file-validator.js +285 -285
  132. package/src/utils/preview-list-prompt.js +136 -136
  133. package/src/utils/secure-music-storage.js +412 -412
  134. package/templates/agentvibes-receiver.sh +231 -231
  135. package/templates/audio/welcome-music.mp3 +0 -0
  136. package/.claude/hooks/bmad-party-manager.sh +0 -225
  137. package/.claude/hooks/stop.sh +0 -38
@@ -1,231 +1,231 @@
1
- #!/usr/bin/env bash
2
- #
3
- # File: agentvibes-receiver.sh
4
- # Location: User installs to ~/.termux/agentvibes-play.sh or ~/.agentvibes/play-remote.sh
5
- #
6
- # AgentVibes SSH-TTS Receiver
7
- # Receives base64-encoded JSON payload from remote server via SSH,
8
- # decodes it, applies voice/music/effects, and plays with local AgentVibes.
9
- #
10
- # Payload format: base64({"text","voice","effects","music","volume","pretext","speed","provider","llm"})
11
- #
12
- # SSH ForceCommand: SSH_ORIGINAL_COMMAND carries the base64 payload.
13
- # Direct invocation: pass the payload as first argument.
14
- #
15
- # Installation:
16
- # curl -sSL https://raw.githubusercontent.com/paulpreibisch/AgentVibes/main/scripts/install-ssh-receiver.sh | bash
17
- # OR
18
- # agentvibes install --ssh-receiver
19
- #
20
- # Copyright (c) 2025 Paul Preibisch
21
- # Licensed under Apache-2.0
22
- #
23
-
24
- set -euo pipefail
25
-
26
- # Handle -- argument separator (skip it if present)
27
- if [[ "${1:-}" == "--" ]]; then
28
- shift
29
- fi
30
-
31
- # ---------------------------------------------------------------------------
32
- # Resolve encoded payload: arg → SSH_ORIGINAL_COMMAND → stdin (legacy)
33
- # ---------------------------------------------------------------------------
34
-
35
- ENCODED_PAYLOAD="${1:-}"
36
- if [[ -z "$ENCODED_PAYLOAD" ]]; then
37
- ENCODED_PAYLOAD="${SSH_ORIGINAL_COMMAND:-}"
38
- fi
39
- if [[ -z "$ENCODED_PAYLOAD" ]]; then
40
- ENCODED_PAYLOAD=$(cat 2>/dev/null || true)
41
- fi
42
-
43
- if [[ -z "$ENCODED_PAYLOAD" ]]; then
44
- echo "❌ No payload provided" >&2
45
- echo "Usage: $0 <base64-json-payload>" >&2
46
- exit 1
47
- fi
48
-
49
- # Reject oversized payloads (DoS guard: 64 KB is generous for any TTS request)
50
- _MAX_PAYLOAD_BYTES=65536
51
- if [[ ${#ENCODED_PAYLOAD} -gt $_MAX_PAYLOAD_BYTES ]]; then
52
- echo "❌ Payload too large (max ${_MAX_PAYLOAD_BYTES} bytes)" >&2
53
- exit 1
54
- fi
55
-
56
- # Validate it looks like base64
57
- if [[ ! "$ENCODED_PAYLOAD" =~ ^[A-Za-z0-9+/=]+$ ]]; then
58
- echo "❌ Payload must be base64-encoded" >&2
59
- exit 1
60
- fi
61
-
62
- # Decode
63
- DECODED=$(printf '%s' "$ENCODED_PAYLOAD" | base64 -d 2>/dev/null) || {
64
- echo "❌ Failed to decode base64 payload" >&2; exit 1
65
- }
66
-
67
- # ---------------------------------------------------------------------------
68
- # Parse JSON payload — python3 is required for JSON parsing
69
- # ---------------------------------------------------------------------------
70
-
71
- if ! command -v python3 &>/dev/null; then
72
- echo "❌ python3 required for JSON parsing" >&2; exit 1
73
- fi
74
-
75
- # Parse all fields in one python call to avoid repeated process spawning
76
- # and to log a clear error if the payload is malformed JSON.
77
- _PARSE_OUT=$(python3 - "$DECODED" 2>&1 <<'PYEOF'
78
- import json, sys
79
- try:
80
- d = json.loads(sys.argv[1])
81
- for field in ("text","voice","music","volume","effects","pretext","speed","provider","llm"):
82
- print(d.get(field, ""))
83
- except json.JSONDecodeError as e:
84
- print(f"JSON_PARSE_ERROR: {e}", file=sys.stderr)
85
- sys.exit(1)
86
- PYEOF
87
- ) || { echo "❌ Invalid JSON in payload: $_PARSE_OUT" >&2; exit 1; }
88
-
89
- # Assign fields from ordered output lines
90
- TEXT=$( sed -n '1p' <<< "$_PARSE_OUT")
91
- VOICE=$( sed -n '2p' <<< "$_PARSE_OUT")
92
- MUSIC=$( sed -n '3p' <<< "$_PARSE_OUT")
93
- VOLUME=$( sed -n '4p' <<< "$_PARSE_OUT")
94
- EFFECTS=$( sed -n '5p' <<< "$_PARSE_OUT")
95
- PRETEXT=$( sed -n '6p' <<< "$_PARSE_OUT")
96
- SPEED=$( sed -n '7p' <<< "$_PARSE_OUT")
97
- PROVIDER=$(sed -n '8p' <<< "$_PARSE_OUT")
98
- LLM=$( sed -n '9p' <<< "$_PARSE_OUT")
99
-
100
- # Fall back to defaults
101
- [[ -z "$VOICE" ]] && VOICE="en_US-lessac-medium"
102
- [[ -z "$VOLUME" ]] && VOLUME="0.10"
103
- [[ -z "$PROVIDER" ]] && PROVIDER="piper"
104
-
105
- # Validate text
106
- if [[ -z "$TEXT" ]]; then
107
- echo "❌ No text in payload" >&2; exit 1
108
- fi
109
-
110
- # SECURITY: Validate voice — letters, digits, underscore, hyphen, period, colon
111
- if [[ ! "$VOICE" =~ ^[a-zA-Z0-9_.:/-]+$ ]]; then
112
- echo "❌ Invalid voice format: $VOICE" >&2; exit 1
113
- fi
114
-
115
- # Validate volume is numeric
116
- if [[ ! "$VOLUME" =~ ^[0-9]+\.?[0-9]*$ ]]; then
117
- VOLUME="0.10"
118
- fi
119
-
120
- # Validate provider
121
- case "$PROVIDER" in
122
- piper|soprano|macos|windows-sapi) ;;
123
- *) PROVIDER="piper" ;;
124
- esac
125
-
126
- # Prepend pretext if present (same logic as PS receiver)
127
- if [[ -n "$PRETEXT" ]]; then
128
- TEXT="${PRETEXT}. ${TEXT}"
129
- fi
130
-
131
- # ---------------------------------------------------------------------------
132
- # Test mode: dump what would be played without calling play-tts
133
- # ---------------------------------------------------------------------------
134
-
135
- if [[ "${AGENTVIBES_TEST_MODE:-false}" == "true" ]]; then
136
- python3 -c "
137
- import json, sys
138
- print(json.dumps({
139
- 'text': sys.argv[1],
140
- 'voice': sys.argv[2],
141
- 'music': sys.argv[3],
142
- 'volume': sys.argv[4],
143
- 'effects': sys.argv[5],
144
- 'pretext': sys.argv[6],
145
- 'provider': sys.argv[7],
146
- 'llm': sys.argv[8],
147
- }, ensure_ascii=False))
148
- " "$TEXT" "$VOICE" "$MUSIC" "$VOLUME" "$EFFECTS" "$PRETEXT" "$PROVIDER" "$LLM"
149
- exit 0
150
- fi
151
-
152
- # ---------------------------------------------------------------------------
153
- # Find AgentVibes installation
154
- # ---------------------------------------------------------------------------
155
-
156
- # Suppress GitHub star reminders (receiver mode)
157
- export AGENTVIBES_NO_REMINDERS=1
158
-
159
- find_agentvibes() {
160
- if command -v agentvibes >/dev/null 2>&1; then
161
- local bin_path
162
- bin_path=$(which agentvibes)
163
- if [[ -L "$bin_path" ]]; then
164
- bin_path=$(readlink -f "$bin_path" 2>/dev/null || realpath "$bin_path" 2>/dev/null || echo "$bin_path")
165
- fi
166
- local lib_path
167
- lib_path="$(dirname "$(dirname "$bin_path")")/lib/node_modules/agentvibes"
168
- if [[ -d "$lib_path" ]]; then
169
- echo "$lib_path"
170
- return 0
171
- fi
172
- fi
173
-
174
- local search_paths=(
175
- "$HOME/.npm-global/lib/node_modules/agentvibes"
176
- "/usr/local/lib/node_modules/agentvibes"
177
- "/data/data/com.termux/files/usr/lib/node_modules/agentvibes"
178
- )
179
-
180
- if [[ -d "$HOME/.nvm/versions/node" ]]; then
181
- local nvm_path
182
- nvm_path=$(find "$HOME/.nvm/versions/node" -maxdepth 3 -type d -name "agentvibes" -path "*/lib/node_modules/*" 2>/dev/null | head -1)
183
- if [[ -n "$nvm_path" ]] && [[ -d "$nvm_path" ]]; then
184
- echo "$nvm_path"
185
- return 0
186
- fi
187
- fi
188
-
189
- for p in "${search_paths[@]}"; do
190
- [[ -d "$p" ]] && { echo "$p"; return 0; }
191
- done
192
-
193
- return 1
194
- }
195
-
196
- AGENTVIBES_ROOT=$(find_agentvibes)
197
-
198
- if [[ -z "$AGENTVIBES_ROOT" ]]; then
199
- echo "❌ AgentVibes not found" >&2
200
- echo "💡 Install: npm install -g agentvibes" >&2
201
- exit 1
202
- fi
203
-
204
- PLAY_TTS="$AGENTVIBES_ROOT/.claude/hooks/play-tts.sh"
205
-
206
- if [[ ! -f "$PLAY_TTS" ]]; then
207
- echo "❌ play-tts.sh not found at $PLAY_TTS" >&2; exit 1
208
- fi
209
-
210
- # ---------------------------------------------------------------------------
211
- # Apply music / effects overrides via env vars for play-tts.sh
212
- # ---------------------------------------------------------------------------
213
-
214
- [[ -n "$MUSIC" ]] && export AGENTVIBES_OVERRIDE_MUSIC="$MUSIC"
215
- [[ -n "$VOLUME" ]] && export AGENTVIBES_OVERRIDE_VOLUME="$VOLUME"
216
- [[ -n "$EFFECTS" ]] && export AGENTVIBES_OVERRIDE_EFFECTS="$EFFECTS"
217
- [[ -n "$SPEED" ]] && export AGENTVIBES_OVERRIDE_SPEED="$SPEED"
218
-
219
- if [[ "${AGENTVIBES_DEBUG:-0}" == "1" ]]; then
220
- echo "[DEBUG] Voice: $VOICE" >&2
221
- echo "[DEBUG] Music: ${MUSIC:-none}" >&2
222
- echo "[DEBUG] Volume: ${VOLUME:-none}" >&2
223
- echo "[DEBUG] Effects: ${EFFECTS:-none}" >&2
224
- echo "[DEBUG] Pretext: ${PRETEXT:-none}" >&2
225
- echo "[DEBUG] Provider: $PROVIDER" >&2
226
- fi
227
-
228
- echo "🎵 Playing via AgentVibes: ${TEXT:0:50}..." >&2
229
- bash "$PLAY_TTS" "$TEXT" "$VOICE"
230
-
231
- exit 0
1
+ #!/usr/bin/env bash
2
+ #
3
+ # File: agentvibes-receiver.sh
4
+ # Location: User installs to ~/.termux/agentvibes-play.sh or ~/.agentvibes/play-remote.sh
5
+ #
6
+ # AgentVibes SSH-TTS Receiver
7
+ # Receives base64-encoded JSON payload from remote server via SSH,
8
+ # decodes it, applies voice/music/effects, and plays with local AgentVibes.
9
+ #
10
+ # Payload format: base64({"text","voice","effects","music","volume","pretext","speed","provider","llm"})
11
+ #
12
+ # SSH ForceCommand: SSH_ORIGINAL_COMMAND carries the base64 payload.
13
+ # Direct invocation: pass the payload as first argument.
14
+ #
15
+ # Installation:
16
+ # curl -sSL https://raw.githubusercontent.com/paulpreibisch/AgentVibes/main/scripts/install-ssh-receiver.sh | bash
17
+ # OR
18
+ # agentvibes install --ssh-receiver
19
+ #
20
+ # Copyright (c) 2025 Paul Preibisch
21
+ # Licensed under Apache-2.0
22
+ #
23
+
24
+ set -euo pipefail
25
+
26
+ # Handle -- argument separator (skip it if present)
27
+ if [[ "${1:-}" == "--" ]]; then
28
+ shift
29
+ fi
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Resolve encoded payload: arg → SSH_ORIGINAL_COMMAND → stdin (legacy)
33
+ # ---------------------------------------------------------------------------
34
+
35
+ ENCODED_PAYLOAD="${1:-}"
36
+ if [[ -z "$ENCODED_PAYLOAD" ]]; then
37
+ ENCODED_PAYLOAD="${SSH_ORIGINAL_COMMAND:-}"
38
+ fi
39
+ if [[ -z "$ENCODED_PAYLOAD" ]]; then
40
+ ENCODED_PAYLOAD=$(cat 2>/dev/null || true)
41
+ fi
42
+
43
+ if [[ -z "$ENCODED_PAYLOAD" ]]; then
44
+ echo "❌ No payload provided" >&2
45
+ echo "Usage: $0 <base64-json-payload>" >&2
46
+ exit 1
47
+ fi
48
+
49
+ # Reject oversized payloads (DoS guard: 64 KB is generous for any TTS request)
50
+ _MAX_PAYLOAD_BYTES=65536
51
+ if [[ ${#ENCODED_PAYLOAD} -gt $_MAX_PAYLOAD_BYTES ]]; then
52
+ echo "❌ Payload too large (max ${_MAX_PAYLOAD_BYTES} bytes)" >&2
53
+ exit 1
54
+ fi
55
+
56
+ # Validate it looks like base64
57
+ if [[ ! "$ENCODED_PAYLOAD" =~ ^[A-Za-z0-9+/=]+$ ]]; then
58
+ echo "❌ Payload must be base64-encoded" >&2
59
+ exit 1
60
+ fi
61
+
62
+ # Decode
63
+ DECODED=$(printf '%s' "$ENCODED_PAYLOAD" | base64 -d 2>/dev/null) || {
64
+ echo "❌ Failed to decode base64 payload" >&2; exit 1
65
+ }
66
+
67
+ # ---------------------------------------------------------------------------
68
+ # Parse JSON payload — python3 is required for JSON parsing
69
+ # ---------------------------------------------------------------------------
70
+
71
+ if ! command -v python3 &>/dev/null; then
72
+ echo "❌ python3 required for JSON parsing" >&2; exit 1
73
+ fi
74
+
75
+ # Parse all fields in one python call to avoid repeated process spawning
76
+ # and to log a clear error if the payload is malformed JSON.
77
+ _PARSE_OUT=$(python3 - "$DECODED" 2>&1 <<'PYEOF'
78
+ import json, sys
79
+ try:
80
+ d = json.loads(sys.argv[1])
81
+ for field in ("text","voice","music","volume","effects","pretext","speed","provider","llm"):
82
+ print(d.get(field, ""))
83
+ except json.JSONDecodeError as e:
84
+ print(f"JSON_PARSE_ERROR: {e}", file=sys.stderr)
85
+ sys.exit(1)
86
+ PYEOF
87
+ ) || { echo "❌ Invalid JSON in payload: $_PARSE_OUT" >&2; exit 1; }
88
+
89
+ # Assign fields from ordered output lines
90
+ TEXT=$( sed -n '1p' <<< "$_PARSE_OUT")
91
+ VOICE=$( sed -n '2p' <<< "$_PARSE_OUT")
92
+ MUSIC=$( sed -n '3p' <<< "$_PARSE_OUT")
93
+ VOLUME=$( sed -n '4p' <<< "$_PARSE_OUT")
94
+ EFFECTS=$( sed -n '5p' <<< "$_PARSE_OUT")
95
+ PRETEXT=$( sed -n '6p' <<< "$_PARSE_OUT")
96
+ SPEED=$( sed -n '7p' <<< "$_PARSE_OUT")
97
+ PROVIDER=$(sed -n '8p' <<< "$_PARSE_OUT")
98
+ LLM=$( sed -n '9p' <<< "$_PARSE_OUT")
99
+
100
+ # Fall back to defaults
101
+ [[ -z "$VOICE" ]] && VOICE="en_US-lessac-medium"
102
+ [[ -z "$VOLUME" ]] && VOLUME="0.10"
103
+ [[ -z "$PROVIDER" ]] && PROVIDER="piper"
104
+
105
+ # Validate text
106
+ if [[ -z "$TEXT" ]]; then
107
+ echo "❌ No text in payload" >&2; exit 1
108
+ fi
109
+
110
+ # SECURITY: Validate voice — letters, digits, underscore, hyphen, period, colon
111
+ if [[ ! "$VOICE" =~ ^[a-zA-Z0-9_.:/-]+$ ]]; then
112
+ echo "❌ Invalid voice format: $VOICE" >&2; exit 1
113
+ fi
114
+
115
+ # Validate volume is numeric
116
+ if [[ ! "$VOLUME" =~ ^[0-9]+\.?[0-9]*$ ]]; then
117
+ VOLUME="0.10"
118
+ fi
119
+
120
+ # Validate provider
121
+ case "$PROVIDER" in
122
+ piper|soprano|macos|windows-sapi) ;;
123
+ *) PROVIDER="piper" ;;
124
+ esac
125
+
126
+ # Prepend pretext if present (same logic as PS receiver)
127
+ if [[ -n "$PRETEXT" ]]; then
128
+ TEXT="${PRETEXT}. ${TEXT}"
129
+ fi
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # Test mode: dump what would be played without calling play-tts
133
+ # ---------------------------------------------------------------------------
134
+
135
+ if [[ "${AGENTVIBES_TEST_MODE:-false}" == "true" ]]; then
136
+ python3 -c "
137
+ import json, sys
138
+ print(json.dumps({
139
+ 'text': sys.argv[1],
140
+ 'voice': sys.argv[2],
141
+ 'music': sys.argv[3],
142
+ 'volume': sys.argv[4],
143
+ 'effects': sys.argv[5],
144
+ 'pretext': sys.argv[6],
145
+ 'provider': sys.argv[7],
146
+ 'llm': sys.argv[8],
147
+ }, ensure_ascii=False))
148
+ " "$TEXT" "$VOICE" "$MUSIC" "$VOLUME" "$EFFECTS" "$PRETEXT" "$PROVIDER" "$LLM"
149
+ exit 0
150
+ fi
151
+
152
+ # ---------------------------------------------------------------------------
153
+ # Find AgentVibes installation
154
+ # ---------------------------------------------------------------------------
155
+
156
+ # Suppress GitHub star reminders (receiver mode)
157
+ export AGENTVIBES_NO_REMINDERS=1
158
+
159
+ find_agentvibes() {
160
+ if command -v agentvibes >/dev/null 2>&1; then
161
+ local bin_path
162
+ bin_path=$(which agentvibes)
163
+ if [[ -L "$bin_path" ]]; then
164
+ bin_path=$(readlink -f "$bin_path" 2>/dev/null || realpath "$bin_path" 2>/dev/null || echo "$bin_path")
165
+ fi
166
+ local lib_path
167
+ lib_path="$(dirname "$(dirname "$bin_path")")/lib/node_modules/agentvibes"
168
+ if [[ -d "$lib_path" ]]; then
169
+ echo "$lib_path"
170
+ return 0
171
+ fi
172
+ fi
173
+
174
+ local search_paths=(
175
+ "$HOME/.npm-global/lib/node_modules/agentvibes"
176
+ "/usr/local/lib/node_modules/agentvibes"
177
+ "/data/data/com.termux/files/usr/lib/node_modules/agentvibes"
178
+ )
179
+
180
+ if [[ -d "$HOME/.nvm/versions/node" ]]; then
181
+ local nvm_path
182
+ nvm_path=$(find "$HOME/.nvm/versions/node" -maxdepth 3 -type d -name "agentvibes" -path "*/lib/node_modules/*" 2>/dev/null | head -1)
183
+ if [[ -n "$nvm_path" ]] && [[ -d "$nvm_path" ]]; then
184
+ echo "$nvm_path"
185
+ return 0
186
+ fi
187
+ fi
188
+
189
+ for p in "${search_paths[@]}"; do
190
+ [[ -d "$p" ]] && { echo "$p"; return 0; }
191
+ done
192
+
193
+ return 1
194
+ }
195
+
196
+ AGENTVIBES_ROOT=$(find_agentvibes)
197
+
198
+ if [[ -z "$AGENTVIBES_ROOT" ]]; then
199
+ echo "❌ AgentVibes not found" >&2
200
+ echo "💡 Install: npm install -g agentvibes" >&2
201
+ exit 1
202
+ fi
203
+
204
+ PLAY_TTS="$AGENTVIBES_ROOT/.claude/hooks/play-tts.sh"
205
+
206
+ if [[ ! -f "$PLAY_TTS" ]]; then
207
+ echo "❌ play-tts.sh not found at $PLAY_TTS" >&2; exit 1
208
+ fi
209
+
210
+ # ---------------------------------------------------------------------------
211
+ # Apply music / effects overrides via env vars for play-tts.sh
212
+ # ---------------------------------------------------------------------------
213
+
214
+ [[ -n "$MUSIC" ]] && export AGENTVIBES_OVERRIDE_MUSIC="$MUSIC"
215
+ [[ -n "$VOLUME" ]] && export AGENTVIBES_OVERRIDE_VOLUME="$VOLUME"
216
+ [[ -n "$EFFECTS" ]] && export AGENTVIBES_OVERRIDE_EFFECTS="$EFFECTS"
217
+ [[ -n "$SPEED" ]] && export AGENTVIBES_OVERRIDE_SPEED="$SPEED"
218
+
219
+ if [[ "${AGENTVIBES_DEBUG:-0}" == "1" ]]; then
220
+ echo "[DEBUG] Voice: $VOICE" >&2
221
+ echo "[DEBUG] Music: ${MUSIC:-none}" >&2
222
+ echo "[DEBUG] Volume: ${VOLUME:-none}" >&2
223
+ echo "[DEBUG] Effects: ${EFFECTS:-none}" >&2
224
+ echo "[DEBUG] Pretext: ${PRETEXT:-none}" >&2
225
+ echo "[DEBUG] Provider: $PROVIDER" >&2
226
+ fi
227
+
228
+ echo "🎵 Playing via AgentVibes: ${TEXT:0:50}..." >&2
229
+ bash "$PLAY_TTS" "$TEXT" "$VOICE"
230
+
231
+ exit 0
File without changes