agentvibes 5.9.0 → 5.10.1

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 (145) hide show
  1. package/.agentvibes/config.json +3 -12
  2. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  3. package/.claude/commands/agent-vibes-rdp.md +24 -24
  4. package/.claude/config/audio-effects.cfg +4 -5
  5. package/.claude/config/audio-effects.cfg.sample +52 -52
  6. package/.claude/config/background-music-enabled.txt +1 -1
  7. package/.claude/docs/TERMUX_SETUP.md +408 -408
  8. package/.claude/github-star-reminder.txt +1 -1
  9. package/.claude/hooks/audio-cache-utils.sh +0 -0
  10. package/.claude/hooks/audio-processor.sh +0 -0
  11. package/.claude/hooks/background-music-manager.sh +0 -0
  12. package/.claude/hooks/bmad-party-speak.sh +0 -0
  13. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  14. package/.claude/hooks/bmad-speak.sh +0 -0
  15. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  16. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  17. package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
  18. package/.claude/hooks/clawdbot-receiver.sh +0 -0
  19. package/.claude/hooks/clean-audio-cache.sh +0 -0
  20. package/.claude/hooks/cleanup-cache.sh +0 -0
  21. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  22. package/.claude/hooks/download-extra-voices.sh +0 -0
  23. package/.claude/hooks/effects-manager.sh +0 -0
  24. package/.claude/hooks/github-star-reminder.sh +0 -0
  25. package/.claude/hooks/language-manager.sh +0 -0
  26. package/.claude/hooks/learn-manager.sh +0 -0
  27. package/.claude/hooks/macos-voice-manager.sh +0 -0
  28. package/.claude/hooks/migrate-background-music.sh +0 -0
  29. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  30. package/.claude/hooks/optimize-background-music.sh +0 -0
  31. package/.claude/hooks/path-resolver.sh +0 -0
  32. package/.claude/hooks/personality-manager.sh +0 -0
  33. package/.claude/hooks/piper-download-voices.sh +0 -0
  34. package/.claude/hooks/piper-installer.sh +0 -0
  35. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  36. package/.claude/hooks/piper-voice-manager.sh +0 -0
  37. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
  38. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  39. package/.claude/hooks/play-tts-macos.sh +0 -0
  40. package/.claude/hooks/play-tts-piper.sh +20 -13
  41. package/.claude/hooks/play-tts-soprano.sh +0 -0
  42. package/.claude/hooks/play-tts-ssh-remote.sh +0 -0
  43. package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
  44. package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
  45. package/.claude/hooks/play-tts.sh +0 -0
  46. package/.claude/hooks/prepare-release.sh +0 -0
  47. package/.claude/hooks/provider-commands.sh +0 -0
  48. package/.claude/hooks/provider-manager.sh +0 -0
  49. package/.claude/hooks/replay-target-audio.sh +0 -0
  50. package/.claude/hooks/requirements.txt +6 -6
  51. package/.claude/hooks/sentiment-manager.sh +0 -0
  52. package/.claude/hooks/session-start-tts.sh +0 -0
  53. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  54. package/.claude/hooks/speed-manager.sh +0 -0
  55. package/.claude/hooks/stop-tts.sh +0 -0
  56. package/.claude/hooks/termux-installer.sh +0 -0
  57. package/.claude/hooks/translate-manager.sh +0 -0
  58. package/.claude/hooks/translator.py +237 -237
  59. package/.claude/hooks/tts-queue-worker.sh +0 -0
  60. package/.claude/hooks/tts-queue.sh +0 -0
  61. package/.claude/hooks/verbosity-manager.sh +0 -0
  62. package/.claude/hooks/voice-manager.sh +6 -0
  63. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +22 -16
  64. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  65. package/.claude/verbosity.txt +1 -1
  66. package/.clawdbot/README.md +105 -105
  67. package/.mcp.json +19 -6
  68. package/README.md +1 -1
  69. package/WINDOWS-SETUP.md +208 -208
  70. package/bin/agent-vibes +39 -39
  71. package/bin/agentvibes-voice-browser.js +0 -0
  72. package/bin/agentvibes.js +0 -0
  73. package/bin/mcp-server.js +121 -121
  74. package/bin/mcp-server.sh +0 -0
  75. package/bin/test-bmad-pr +78 -78
  76. package/mcp-server/QUICK_START.md +203 -203
  77. package/mcp-server/README.md +345 -345
  78. package/mcp-server/WINDOWS_SETUP.md +0 -0
  79. package/mcp-server/examples/claude_desktop_config.json +11 -11
  80. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  81. package/mcp-server/examples/custom_instructions.md +169 -169
  82. package/mcp-server/install-deps.js +0 -0
  83. package/mcp-server/server.py +1807 -1797
  84. package/mcp-server/test_server.py +0 -0
  85. package/package.json +2 -2
  86. package/src/cli/list-personalities.js +110 -110
  87. package/src/cli/list-voices.js +114 -114
  88. package/src/commands/bmad-voices.js +394 -394
  89. package/src/commands/install-mcp.js +730 -476
  90. package/src/console/app.js +3 -3
  91. package/src/console/brand-colors.js +13 -13
  92. package/src/console/constants/personalities.js +44 -44
  93. package/src/console/tabs/agents-tab.js +6 -6
  94. package/src/console/tabs/help-tab.js +314 -314
  95. package/src/console/tabs/music-tab.js +1 -1
  96. package/src/console/tabs/readme-tab.js +272 -272
  97. package/src/console/tabs/receiver-tab.js +13 -13
  98. package/src/console/tabs/settings-tab.js +2 -2
  99. package/src/console/tabs/setup-tab.js +10 -10
  100. package/src/console/tabs/voices-tab.js +4 -4
  101. package/src/console/widgets/destroy-list.js +25 -25
  102. package/src/console/widgets/notice.js +55 -55
  103. package/src/console/widgets/personality-picker.js +2 -2
  104. package/src/console/widgets/reverb-picker.js +1 -1
  105. package/src/i18n/de.js +202 -202
  106. package/src/i18n/es.js +202 -202
  107. package/src/i18n/fr.js +202 -202
  108. package/src/i18n/hi.js +202 -202
  109. package/src/i18n/ja.js +202 -202
  110. package/src/i18n/ko.js +202 -202
  111. package/src/i18n/pt.js +202 -202
  112. package/src/i18n/strings.js +54 -54
  113. package/src/i18n/zh-CN.js +202 -202
  114. package/src/installer/language-screen.js +31 -31
  115. package/src/installer/music-file-input.js +304 -304
  116. package/src/installer.js +32 -27
  117. package/src/services/config-service.js +264 -264
  118. package/src/services/language-service.js +47 -47
  119. package/src/services/provider-service.js +143 -143
  120. package/src/services/tts-engine-service.js +2 -2
  121. package/src/utils/audio-duration-validator.js +298 -298
  122. package/src/utils/audio-format-validator.js +277 -277
  123. package/src/utils/dependency-checker.js +469 -469
  124. package/src/utils/file-ownership-verifier.js +358 -358
  125. package/src/utils/list-formatter.js +200 -194
  126. package/src/utils/music-file-validator.js +285 -285
  127. package/src/utils/platform-resolver.js +369 -0
  128. package/src/utils/preview-list-prompt.js +136 -136
  129. package/src/utils/provider-validator.js +9 -9
  130. package/src/utils/secure-music-storage.js +412 -412
  131. package/templates/agentvibes-receiver.sh +231 -231
  132. package/templates/audio/welcome-music.mp3 +0 -0
  133. package/.agentvibes/install-manifest.json +0 -330
  134. package/.claude/config/background-music-position.txt +0 -27
  135. package/.claude/config/background-music-volume.txt +0 -1
  136. package/.claude/config/background-music.cfg +0 -1
  137. package/.claude/config/background-music.txt +0 -1
  138. package/.claude/config/language.txt +0 -1
  139. package/.claude/config/reverb-level.txt +0 -1
  140. package/.claude/config/tts-speech-rate.txt +0 -1
  141. package/.claude/config/tts-verbosity.txt +0 -1
  142. package/.claude/hooks/play-tts-agentvibes-receiver.sh +0 -1
  143. package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +0 -119
  144. package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +0 -153
  145. package/.claude/piper-voices-dir.txt +0 -1
@@ -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