agentvibes 5.2.1 → 5.4.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 (222) hide show
  1. package/.agentvibes/LITE-MODE.md +236 -0
  2. package/.agentvibes/README.md +136 -0
  3. package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +141 -0
  4. package/.agentvibes/backups/agents/analyst_20260204_144958.md +78 -0
  5. package/.agentvibes/backups/agents/architect_20260204_144958.md +72 -0
  6. package/.agentvibes/backups/agents/dev_20260204_144958.md +74 -0
  7. package/.agentvibes/backups/agents/pm_20260204_144958.md +72 -0
  8. package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +64 -0
  9. package/.agentvibes/backups/agents/sm_20260204_144958.md +87 -0
  10. package/.agentvibes/backups/agents/tea_20260204_144958.md +79 -0
  11. package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +82 -0
  12. package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +80 -0
  13. package/.agentvibes/bmad/bmad-voices.md +69 -69
  14. package/.agentvibes/config/README-personality-defaults.md +162 -0
  15. package/.agentvibes/config/mode.txt +1 -0
  16. package/.agentvibes/config/personality-voice-defaults.default.json +21 -0
  17. package/.agentvibes/config/save-audio.txt +1 -0
  18. package/.agentvibes/config/voice-metadata.json +160 -0
  19. package/.agentvibes/config.json +24 -15
  20. package/.agentvibes/hooks/help.sh +191 -0
  21. package/.agentvibes/hooks/post-tool-use-lite.sh +111 -0
  22. package/.agentvibes/hooks/save-audio-manager.sh +162 -0
  23. package/.agentvibes/hooks/session-start-full-optimized.sh +102 -0
  24. package/.agentvibes/hooks/session-start-full.sh +142 -0
  25. package/.agentvibes/hooks/session-start-lite-v2.sh +34 -0
  26. package/.agentvibes/hooks/session-start-lite.sh +29 -0
  27. package/.agentvibes/hooks/stop-lite.sh +115 -0
  28. package/.agentvibes/hooks/switch-mode.sh +215 -0
  29. package/.agentvibes/output-styles/audio-summary.md +30 -0
  30. package/.claude/activation-instructions +54 -54
  31. package/.claude/audio/voice-samples/piper/alan.wav +0 -0
  32. package/.claude/audio/voice-samples/piper/amy.wav +0 -0
  33. package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
  34. package/.claude/audio/voice-samples/piper/joe.wav +0 -0
  35. package/.claude/audio/voice-samples/piper/john.wav +0 -0
  36. package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
  37. package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
  38. package/.claude/audio/voice-samples/piper/linda.wav +0 -0
  39. package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
  40. package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
  41. package/.claude/commands/agent-vibes/add.md +21 -21
  42. package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
  43. package/.claude/commands/agent-vibes/agent.md +79 -79
  44. package/.claude/commands/agent-vibes/background-music.md +111 -111
  45. package/.claude/commands/agent-vibes/bmad.md +198 -198
  46. package/.claude/commands/agent-vibes/clean.md +18 -18
  47. package/.claude/commands/agent-vibes/cleanup.md +18 -18
  48. package/.claude/commands/agent-vibes/commands.json +145 -145
  49. package/.claude/commands/agent-vibes/effects.md +97 -97
  50. package/.claude/commands/agent-vibes/get.md +9 -9
  51. package/.claude/commands/agent-vibes/hide.md +91 -91
  52. package/.claude/commands/agent-vibes/language.md +23 -23
  53. package/.claude/commands/agent-vibes/learn.md +67 -67
  54. package/.claude/commands/agent-vibes/list.md +13 -13
  55. package/.claude/commands/agent-vibes/mute.md +37 -37
  56. package/.claude/commands/agent-vibes/preview.md +17 -17
  57. package/.claude/commands/agent-vibes/provider.md +68 -68
  58. package/.claude/commands/agent-vibes/replay-target.md +14 -14
  59. package/.claude/commands/agent-vibes/sample.md +12 -12
  60. package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
  61. package/.claude/commands/agent-vibes/set-pretext.md +65 -65
  62. package/.claude/commands/agent-vibes/set-speed.md +41 -41
  63. package/.claude/commands/agent-vibes/show.md +84 -84
  64. package/.claude/commands/agent-vibes/switch.md +87 -87
  65. package/.claude/commands/agent-vibes/target-voice.md +26 -26
  66. package/.claude/commands/agent-vibes/target.md +30 -30
  67. package/.claude/commands/agent-vibes/translate.md +68 -68
  68. package/.claude/commands/agent-vibes/unmute.md +45 -45
  69. package/.claude/commands/agent-vibes/whoami.md +7 -7
  70. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  71. package/.claude/commands/agent-vibes-rdp.md +24 -24
  72. package/.claude/config/audio-effects.cfg +4 -11
  73. package/.claude/config/audio-effects.cfg.sample +52 -52
  74. package/.claude/config/background-music-position.txt +27 -0
  75. package/.claude/config/background-music-volume.txt +1 -1
  76. package/.claude/config/background-music.cfg +1 -0
  77. package/.claude/config/background-music.txt +1 -0
  78. package/.claude/config/tts-speech-rate.txt +1 -4
  79. package/.claude/config/tts-verbosity.txt +1 -0
  80. package/.claude/docs/TERMUX_SETUP.md +408 -408
  81. package/.claude/github-star-reminder.txt +1 -1
  82. package/.claude/hooks/README-TTS-QUEUE.md +135 -135
  83. package/.claude/hooks/audio-cache-utils.sh +0 -0
  84. package/.claude/hooks/audio-processor.sh +60 -14
  85. package/.claude/hooks/background-music-manager.sh +0 -0
  86. package/.claude/hooks/bmad-party-manager.sh +225 -0
  87. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  88. package/.claude/hooks/bmad-speak.sh +6 -13
  89. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  90. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  91. package/.claude/hooks/clawdbot-receiver-SECURE.sh +25 -23
  92. package/.claude/hooks/clawdbot-receiver.sh +4 -28
  93. package/.claude/hooks/clean-audio-cache.sh +0 -0
  94. package/.claude/hooks/cleanup-cache.sh +0 -0
  95. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  96. package/.claude/hooks/download-extra-voices.sh +0 -0
  97. package/.claude/hooks/effects-manager.sh +0 -0
  98. package/.claude/hooks/github-star-reminder.sh +0 -0
  99. package/.claude/hooks/language-manager.sh +0 -0
  100. package/.claude/hooks/learn-manager.sh +0 -0
  101. package/.claude/hooks/macos-voice-manager.sh +0 -0
  102. package/.claude/hooks/migrate-background-music.sh +0 -0
  103. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  104. package/.claude/hooks/optimize-background-music.sh +0 -0
  105. package/.claude/hooks/personality-manager.sh +0 -0
  106. package/.claude/hooks/piper-download-voices.sh +0 -0
  107. package/.claude/hooks/piper-installer.sh +1 -1
  108. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  109. package/.claude/hooks/piper-voice-manager.sh +0 -0
  110. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  111. package/.claude/hooks/play-tts-macos.sh +6 -12
  112. package/.claude/hooks/play-tts-piper.sh +50 -79
  113. package/.claude/hooks/play-tts-soprano.sh +9 -43
  114. package/.claude/hooks/play-tts-ssh-remote.sh +42 -120
  115. package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
  116. package/.claude/hooks/play-tts.sh +48 -37
  117. package/.claude/hooks/post-response.sh +41 -0
  118. package/.claude/hooks/prepare-release.sh +0 -0
  119. package/.claude/hooks/provider-commands.sh +0 -0
  120. package/.claude/hooks/provider-manager.sh +0 -0
  121. package/.claude/hooks/replay-target-audio.sh +0 -0
  122. package/.claude/hooks/requirements.txt +6 -6
  123. package/.claude/hooks/sentiment-manager.sh +0 -0
  124. package/.claude/hooks/session-start-tts.sh +56 -39
  125. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  126. package/.claude/hooks/speed-manager.sh +0 -0
  127. package/.claude/hooks/stop.sh +63 -0
  128. package/.claude/hooks/termux-installer.sh +0 -0
  129. package/.claude/hooks/translate-manager.sh +0 -0
  130. package/.claude/hooks/translator.py +237 -237
  131. package/.claude/hooks/tts-queue-worker.sh +0 -0
  132. package/.claude/hooks/tts-queue.sh +0 -0
  133. package/.claude/hooks/verbosity-manager.sh +0 -0
  134. package/.claude/hooks/voice-manager.sh +26 -4
  135. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  136. package/.claude/hooks-windows/bmad-party-speak.ps1 +278 -274
  137. package/.claude/hooks-windows/bmad-speak.ps1 +264 -264
  138. package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -53
  139. package/.claude/hooks-windows/effects-manager.ps1 +294 -294
  140. package/.claude/hooks-windows/language-manager.ps1 +193 -193
  141. package/.claude/hooks-windows/learn-manager.ps1 +241 -241
  142. package/.claude/hooks-windows/personality-manager.ps1 +266 -266
  143. package/.claude/hooks-windows/play-tts-soprano.ps1 +5 -5
  144. package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -138
  145. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +164 -0
  146. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -0
  147. package/.claude/hooks-windows/play-tts.ps1 +104 -481
  148. package/.claude/hooks-windows/provider-manager.ps1 +158 -192
  149. package/.claude/hooks-windows/session-start-tts.ps1 +55 -46
  150. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  151. package/.claude/hooks-windows/speed-manager.ps1 +166 -166
  152. package/.claude/hooks-windows/voice-manager-windows.ps1 +176 -260
  153. package/.claude/output-styles/agent-vibes.md +202 -202
  154. package/.claude/personalities/angry.md +14 -14
  155. package/.claude/personalities/annoying.md +14 -14
  156. package/.claude/personalities/crass.md +14 -14
  157. package/.claude/personalities/dramatic.md +14 -14
  158. package/.claude/personalities/dry-humor.md +50 -50
  159. package/.claude/personalities/flirty.md +20 -20
  160. package/.claude/personalities/funny.md +14 -14
  161. package/.claude/personalities/grandpa.md +32 -32
  162. package/.claude/personalities/millennial.md +14 -14
  163. package/.claude/personalities/moody.md +14 -14
  164. package/.claude/personalities/normal.md +16 -16
  165. package/.claude/personalities/pirate.md +14 -14
  166. package/.claude/personalities/poetic.md +14 -14
  167. package/.claude/personalities/professional.md +14 -14
  168. package/.claude/personalities/rapper.md +55 -55
  169. package/.claude/personalities/robot.md +14 -14
  170. package/.claude/personalities/sarcastic.md +38 -38
  171. package/.claude/personalities/sassy.md +14 -14
  172. package/.claude/personalities/surfer-dude.md +14 -14
  173. package/.claude/personalities/zen.md +14 -14
  174. package/.claude/piper-voices-dir.txt +1 -0
  175. package/.claude/settings.json +25 -15
  176. package/.claude/verbosity.txt +1 -1
  177. package/.clawdbot/README.md +105 -105
  178. package/.clawdbot/skill/SKILL.md +149 -145
  179. package/.mcp.json +30 -11
  180. package/CLAUDE.md +170 -215
  181. package/README.md +206 -515
  182. package/RELEASE_NOTES.md +1132 -1884
  183. package/WINDOWS-SETUP.md +208 -208
  184. package/bin/agent-vibes +0 -0
  185. package/bin/agentvibes-voice-browser.js +64 -1289
  186. package/bin/agentvibes.js +0 -0
  187. package/bin/ensure-soprano-running.sh +43 -0
  188. package/bin/mcp-server.js +121 -121
  189. package/bin/mcp-server.sh +0 -0
  190. package/bin/test-bmad-pr +78 -78
  191. package/mcp-server/QUICK_START.md +203 -203
  192. package/mcp-server/README.md +345 -345
  193. package/mcp-server/WINDOWS_SETUP.md +260 -260
  194. package/mcp-server/docs/troubleshooting-audio.md +313 -313
  195. package/mcp-server/examples/claude_desktop_config.json +11 -11
  196. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  197. package/mcp-server/examples/custom_instructions.md +169 -169
  198. package/mcp-server/install-deps.js +130 -130
  199. package/mcp-server/pyproject.toml +52 -52
  200. package/mcp-server/requirements.txt +2 -2
  201. package/mcp-server/server.py +1451 -1578
  202. package/mcp-server/test_server.py +395 -395
  203. package/package.json +1 -3
  204. package/setup-windows.ps1 +815 -815
  205. package/src/console/tabs/setup-tab.js +9 -6
  206. package/src/console/tabs/voices-tab.js +9 -3
  207. package/src/installer.js +42 -5
  208. package/src/services/llm-provider-service.js +13 -0
  209. package/templates/agentvibes-receiver.sh +158 -483
  210. package/templates/audio/welcome-music.mp3 +0 -0
  211. package/.agentvibes/bmad-voice-map.json +0 -104
  212. package/.agentvibes/copilot-sessions.log +0 -4
  213. package/.claude/config/audio-effects-bmad.cfg +0 -50
  214. package/.claude/config/background-music-enabled.txt +0 -1
  215. package/.claude/config/intro-text.txt +0 -1
  216. package/.claude/config/personality.txt +0 -1
  217. package/.claude/config/piper-speech-rate.txt +0 -4
  218. package/.claude/config/piper-target-speech-rate.txt +0 -1
  219. package/.claude/config/reverb-level.txt +0 -1
  220. package/.claude/config/tts-target-speech-rate.txt +0 -1
  221. package/voice-assignments.json +0 -8245
  222. /package/{.claude → .agentvibes}/config/agentvibes.json +0 -0
@@ -26,19 +26,20 @@ VOICE="${2:-en_US-lessac-medium}"
26
26
  ENCODED_AGENT="${3:-}"
27
27
  ENCODED_INTRO="${4:-}"
28
28
 
29
+ # SECURITY: Whitelist of allowed voice names
30
+ ALLOWED_VOICES="en_US-amy-medium|en_US-lessac-medium|es_ES-mls_9972-low|es_ES-davefx-medium|en_US-joe-medium"
31
+
29
32
  # Validate inputs
30
33
  if [[ -z "$ENCODED_TEXT" ]]; then
31
- echo "Error: No encoded text provided" >&2
34
+ echo " No encoded text provided" >&2
32
35
  echo "Usage: $0 <base64_text> <voice> <base64_agent_name> [base64_intro]" >&2
33
36
  exit 1
34
37
  fi
35
38
 
36
- # SECURITY: Validate base64 format (reject shell metacharacters)
37
- [[ "$ENCODED_TEXT" =~ ^[A-Za-z0-9+/=]+$ ]] || { echo "Error: invalid base64 text" >&2; exit 1; }
38
-
39
- # SECURITY: Validate VOICE parameter format (alphanumeric, hyphens, underscores only)
40
- if [[ ! "$VOICE" =~ ^[a-zA-Z0-9_-]+$ ]]; then
41
- echo "Error: Invalid voice name format: $VOICE" >&2
39
+ # SECURITY: Validate VOICE parameter against whitelist
40
+ if ! [[ "$VOICE" =~ ^($ALLOWED_VOICES)$ ]]; then
41
+ echo "❌ Invalid voice name: $VOICE" >&2
42
+ echo "Allowed voices: $(echo "$ALLOWED_VOICES" | tr '|' ', ')" >&2
42
43
  exit 1
43
44
  fi
44
45
 
@@ -57,30 +58,31 @@ fi
57
58
 
58
59
  DECODED_AGENT="default"
59
60
  if [[ -n "$ENCODED_AGENT" ]]; then
60
- # SECURITY: Validate base64 format before decoding
61
- [[ "$ENCODED_AGENT" =~ ^[A-Za-z0-9+/=]+$ ]] || ENCODED_AGENT=""
62
- if [[ -n "$ENCODED_AGENT" ]]; then
63
- DECODED_AGENT=$(echo -n "$ENCODED_AGENT" | base64 -d 2>/dev/null) || DECODED_AGENT="default"
64
- fi
65
-
61
+ DECODED_AGENT=$(echo -n "$ENCODED_AGENT" | base64 -d 2>/dev/null) || DECODED_AGENT="default"
62
+
66
63
  # SECURITY: Validate agent name format (alphanumeric, dash, underscore only)
67
- [[ "$DECODED_AGENT" =~ ^[a-zA-Z0-9_-]+$ ]] || DECODED_AGENT="default"
68
-
64
+ if ! [[ "$DECODED_AGENT" =~ ^[a-zA-Z0-9_-]+$ ]]; then
65
+ echo "⚠️ Invalid agent name format, using 'default'" >&2
66
+ DECODED_AGENT="default"
67
+ fi
68
+
69
69
  # SECURITY: Enforce length limit on agent name
70
- [[ ${#DECODED_AGENT} -le 50 ]] || DECODED_AGENT="default"
70
+ if [[ ${#DECODED_AGENT} -gt 50 ]]; then
71
+ echo "⚠️ Agent name too long, using 'default'" >&2
72
+ DECODED_AGENT="default"
73
+ fi
71
74
  fi
72
75
 
73
76
  # Decode and prepend intro if provided
74
77
  DECODED_INTRO=""
75
78
  if [[ -n "$ENCODED_INTRO" ]]; then
76
- # SECURITY: Validate base64 format before decoding
77
- [[ "$ENCODED_INTRO" =~ ^[A-Za-z0-9+/=]+$ ]] || ENCODED_INTRO=""
78
- if [[ -n "$ENCODED_INTRO" ]]; then
79
- DECODED_INTRO=$(echo -n "$ENCODED_INTRO" | base64 -d 2>/dev/null) || DECODED_INTRO=""
80
- fi
81
-
79
+ DECODED_INTRO=$(echo -n "$ENCODED_INTRO" | base64 -d 2>/dev/null) || DECODED_INTRO=""
80
+
82
81
  # SECURITY: Enforce length limit on intro message
83
- [[ ${#DECODED_INTRO} -le 200 ]] || DECODED_INTRO="${DECODED_INTRO:0:200}"
82
+ if [[ ${#DECODED_INTRO} -gt 200 ]]; then
83
+ echo "⚠️ Intro message too long, truncating" >&2
84
+ DECODED_INTRO="${DECODED_INTRO:0:200}"
85
+ fi
84
86
  fi
85
87
 
86
88
  # Prepend intro to text if configured
@@ -30,50 +30,26 @@ ENCODED_INTRO="${4:-}"
30
30
 
31
31
  # Validate inputs
32
32
  if [[ -z "$ENCODED_TEXT" ]]; then
33
- echo "Error: No encoded text provided" >&2
33
+ echo " No encoded text provided" >&2
34
34
  echo "Usage: $0 <base64_text> <voice> <base64_agent_name> [base64_intro]" >&2
35
35
  exit 1
36
36
  fi
37
37
 
38
- # SECURITY: Validate base64 format (reject shell metacharacters)
39
- [[ "$ENCODED_TEXT" =~ ^[A-Za-z0-9+/=]+$ ]] || { echo "Error: invalid base64 text" >&2; exit 1; }
40
-
41
- # SECURITY: Validate voice name format (alphanumeric, hyphens, underscores only)
42
- [[ "$VOICE" =~ ^[a-zA-Z0-9_-]+$ ]] || { echo "Error: invalid voice format" >&2; exit 1; }
43
-
44
38
  # SECURITY: Decode base64 safely
45
39
  DECODED_TEXT=$(echo -n "$ENCODED_TEXT" | base64 -d 2>/dev/null) || {
46
- echo "Error: Failed to decode text (invalid base64)" >&2
40
+ echo " Failed to decode text (invalid base64)" >&2
47
41
  exit 1
48
42
  }
49
43
 
50
- # SECURITY: Enforce length limit on decoded text (10KB max)
51
- if [[ ${#DECODED_TEXT} -gt 10000 ]]; then
52
- echo "Error: Decoded text too long (${#DECODED_TEXT} chars, max 10000)" >&2
53
- exit 1
54
- fi
55
-
56
44
  DECODED_AGENT="default"
57
45
  if [[ -n "$ENCODED_AGENT" ]]; then
58
- [[ "$ENCODED_AGENT" =~ ^[A-Za-z0-9+/=]+$ ]] || ENCODED_AGENT=""
59
- if [[ -n "$ENCODED_AGENT" ]]; then
60
- DECODED_AGENT=$(echo -n "$ENCODED_AGENT" | base64 -d 2>/dev/null) || DECODED_AGENT="default"
61
- fi
62
- # Validate agent name format (alphanumeric, dash, underscore only)
63
- [[ "$DECODED_AGENT" =~ ^[a-zA-Z0-9_-]+$ ]] || DECODED_AGENT="default"
64
- # Enforce length limit
65
- [[ ${#DECODED_AGENT} -le 50 ]] || DECODED_AGENT="default"
46
+ DECODED_AGENT=$(echo -n "$ENCODED_AGENT" | base64 -d 2>/dev/null) || DECODED_AGENT="default"
66
47
  fi
67
48
 
68
49
  # Decode and prepend intro if provided
69
50
  DECODED_INTRO=""
70
51
  if [[ -n "$ENCODED_INTRO" ]]; then
71
- [[ "$ENCODED_INTRO" =~ ^[A-Za-z0-9+/=]+$ ]] || ENCODED_INTRO=""
72
- if [[ -n "$ENCODED_INTRO" ]]; then
73
- DECODED_INTRO=$(echo -n "$ENCODED_INTRO" | base64 -d 2>/dev/null) || DECODED_INTRO=""
74
- fi
75
- # Enforce length limit on intro (200 chars max)
76
- [[ ${#DECODED_INTRO} -le 200 ]] || DECODED_INTRO="${DECODED_INTRO:0:200}"
52
+ DECODED_INTRO=$(echo -n "$ENCODED_INTRO" | base64 -d 2>/dev/null) || DECODED_INTRO=""
77
53
  fi
78
54
 
79
55
  # Prepend intro to text if configured
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -67,7 +67,7 @@ if [[ -d "/data/data/com.termux" ]]; then
67
67
  echo ""
68
68
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
69
69
  if [[ -f "$SCRIPT_DIR/termux-installer.sh" ]]; then
70
- exec bash "$SCRIPT_DIR/termux-installer.sh" "$@"
70
+ exec "$SCRIPT_DIR/termux-installer.sh" "$@"
71
71
  else
72
72
  echo "❌ Error: termux-installer.sh not found"
73
73
  echo " Please download it from the AgentVibes repository"
File without changes
File without changes
File without changes
@@ -48,18 +48,12 @@ fi
48
48
  TEXT="$1"
49
49
  VOICE_OVERRIDE="$2" # Optional: voice name (e.g., "Samantha", "Daniel")
50
50
 
51
- # Strip emojis, asterisks, and markdown formatting
52
- TEXT=$(printf '%s' "$TEXT" | perl -CSD -pe '
53
- s/[\x{1F300}-\x{1F9FF}]//g;
54
- s/[\x{2600}-\x{27BF}]//g;
55
- s/[\x{FE00}-\x{FE0F}]//g;
56
- s/[\x{200D}]//g;
57
- s/[\x{2500}-\x{257F}]//g;
58
- s/[\x{2580}-\x{259F}]//g;
59
- s/\*+//g; s/#+\s*//g; s/`//g; s/~+//g; s/^\s*[-]\s*//g;
60
- ')
61
-
62
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
51
+ # Use readlink -f to handle symlinks correctly
52
+ SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
53
+ SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
54
+
55
+ # Source audio cache utilities
56
+ source "$SCRIPT_DIR/audio-cache-utils.sh"
63
57
 
64
58
  # Source audio cache utilities
65
59
  source "$SCRIPT_DIR/audio-cache-utils.sh"
@@ -74,7 +74,9 @@ TEXT=$(printf '%s' "$TEXT" | perl -CSD -pe '
74
74
  ')
75
75
 
76
76
  # Source voice manager and language manager
77
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
77
+ # Use readlink -f to handle symlinks correctly
78
+ SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
79
+ SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
78
80
  source "$SCRIPT_DIR/piper-voice-manager.sh"
79
81
  source "$SCRIPT_DIR/language-manager.sh"
80
82
  source "$SCRIPT_DIR/audio-cache-utils.sh"
@@ -518,10 +520,14 @@ fi
518
520
  touch "$LOCK_FILE"
519
521
 
520
522
  # Create write lock file in audio directory to signal file is in-use (prevents race condition in cleanup)
521
- _TEMP_DIR="${TEMP_FILE%/*}"
522
- WRITE_LOCK_FILE="$_TEMP_DIR/$(basename "$TEMP_FILE" .wav).lock"
523
+ AUDIO_DIR="${TEMP_FILE%/*}"
524
+ WRITE_LOCK_FILE="$AUDIO_DIR/$(basename "$TEMP_FILE" .wav).lock"
523
525
  touch "$WRITE_LOCK_FILE"
524
- _CLEANUP_FILES+=("$LOCK_FILE" "$WRITE_LOCK_FILE")
526
+
527
+ # Get audio duration for proper lock timing
528
+ DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$TEMP_FILE" 2>/dev/null)
529
+ DURATION=${DURATION%.*} # Round to integer
530
+ DURATION=${DURATION:-1} # Default to 1 second if detection fails
525
531
 
526
532
  # Get audio duration for proper lock timing
527
533
  DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$TEMP_FILE" 2>/dev/null || true)
@@ -558,95 +564,65 @@ disown
558
564
  # Get audio cache path
559
565
  AUDIO_DIR_PATH=$(get_audio_dir)
560
566
 
561
- # Color codes (safe to use — WAV path is passed via AGENTVIBES_WAV_OUTPATH, not parsed from stdout)
567
+ # Color codes
562
568
  BLUE='\033[0;34m'
563
569
  YELLOW='\033[1;33m'
564
570
  PURPLE='\033[0;35m'
571
+ LIGHT_PURPLE='\033[1;35m'
565
572
  RED='\033[0;31m'
566
573
  GREEN='\033[0;32m'
567
574
  ORANGE='\033[0;33m'
568
575
  WHITE='\033[1;37m'
576
+ MAGENTA='\033[0;35m'
569
577
  CYAN='\033[0;36m'
570
578
  GOLD='\033[38;5;226m'
571
579
  NC='\033[0m'
572
580
 
573
- # Check if banner is enabled (default: on)
574
- _BANNER_ENABLED=true
575
- if [[ -f "$HOME/.agentvibes/banner-disabled" ]]; then
576
- _BANNER_ENABLED=false
577
- elif [[ -f "${PROJECT_ROOT:-/nonexistent}/.agentvibes/banner-disabled" ]]; then
578
- _BANNER_ENABLED=false
579
- fi
580
-
581
- # Run auto-cleanup off the critical path: only every 10th call, in background after playback starts.
582
- # Counter file lives in the secure lock dir (user-specific, already created above).
581
+ # CRITICAL: Run auto-cleanup FIRST (before calculating size)
582
+ # This ensures we display the POST-cleanup size, not pre-cleanup size
583
583
  AUTO_CLEAN_THRESHOLD=$(get_auto_clean_threshold)
584
- _CALL_COUNTER_FILE="$_LOCK_DIR/agentvibes-tts-call-count"
585
- _CALL_COUNT=$(cat "$_CALL_COUNTER_FILE" 2>/dev/null || echo "0")
586
- # SECURITY: Validate counter is numeric before arithmetic
587
- if ! [[ "$_CALL_COUNT" =~ ^[0-9]+$ ]]; then _CALL_COUNT=0; fi
588
- _CALL_COUNT=$((_CALL_COUNT + 1))
589
- echo "$_CALL_COUNT" > "$_CALL_COUNTER_FILE"
590
-
591
- if (( _CALL_COUNT % 10 == 0 )); then
592
- # Capture values needed inside the subshell before forking
593
- _CLEANUP_AUDIO_DIR="$AUDIO_DIR_PATH"
594
- _CLEANUP_THRESHOLD="$AUTO_CLEAN_THRESHOLD"
595
- _CLEANUP_BANNER="$_BANNER_ENABLED"
596
- # Source the utils inside the subshell (functions are not exported)
597
- _CLEANUP_UTILS="$SCRIPT_DIR/audio-cache-utils.sh"
598
- (
599
- source "$_CLEANUP_UTILS" 2>/dev/null || exit 0
600
- _INITIAL_SIZE=$(calculate_tts_size_bytes "$_CLEANUP_AUDIO_DIR")
601
- if [[ $_INITIAL_SIZE -gt $((_CLEANUP_THRESHOLD * 1048576)) ]]; then
602
- _DELETED=$(auto_clean_old_files "$_CLEANUP_AUDIO_DIR" "$_CLEANUP_THRESHOLD")
603
- if [[ ${_DELETED:-0} -gt 0 ]] && [[ "$_CLEANUP_BANNER" == "true" ]]; then
604
- echo -e "\033[0;33m🧹 Auto-cleaned $_DELETED old files\033[0m"
605
- fi
606
- fi
607
- ) &
608
- disown
584
+ INITIAL_SIZE=$(calculate_tts_size_bytes "$AUDIO_DIR_PATH")
585
+ if [[ $INITIAL_SIZE -gt $((AUTO_CLEAN_THRESHOLD * 1048576)) ]]; then
586
+ DELETED=$(auto_clean_old_files "$AUDIO_DIR_PATH" "$AUTO_CLEAN_THRESHOLD")
587
+ if [[ $DELETED -gt 0 ]]; then
588
+ echo -e "${ORANGE}🧹 Auto-cleaned $DELETED old files${NC}"
589
+ fi
609
590
  fi
610
591
 
611
- # Write output path for play-tts-enhanced.sh (avoids stdout parsing — colors are safe)
612
- if [[ -n "${AGENTVIBES_WAV_OUTPATH:-}" ]]; then
613
- echo "$TEMP_FILE" > "$AGENTVIBES_WAV_OUTPATH"
592
+ # NOW calculate cache stats after cleanup
593
+ FILE_COUNT=$(count_tts_files "$AUDIO_DIR_PATH")
594
+ SIZE_BYTES=$(calculate_tts_size_bytes "$AUDIO_DIR_PATH")
595
+ SIZE_HUMAN=$(bytes_to_human "$SIZE_BYTES")
596
+
597
+ # Dynamic color coding based on cache size
598
+ # Green: < 500MB (small)
599
+ # Yellow: 500MB - 3GB (lots)
600
+ # Red: > 3GB (extreme)
601
+ CACHE_COLOR=$GREEN
602
+ if [[ $SIZE_BYTES -gt 3221225472 ]]; then # > 3GB
603
+ CACHE_COLOR=$RED
604
+ elif [[ $SIZE_BYTES -gt 524288000 ]]; then # > 500MB
605
+ CACHE_COLOR=$YELLOW
614
606
  fi
615
607
 
616
- if [[ "$_BANNER_ENABLED" == "true" ]]; then
617
- FILE_COUNT=$(count_tts_files "$AUDIO_DIR_PATH")
618
- SIZE_BYTES=$(calculate_tts_size_bytes "$AUDIO_DIR_PATH")
619
- SIZE_HUMAN=$(bytes_to_human "$SIZE_BYTES")
620
-
621
- # Dynamic color coding based on cache size
622
- CACHE_COLOR=$GREEN
623
- if [[ $SIZE_BYTES -gt 3221225472 ]]; then
624
- CACHE_COLOR=$RED
625
- elif [[ $SIZE_BYTES -gt 524288000 ]]; then
626
- CACHE_COLOR=$YELLOW
627
- fi
608
+ # Display with file count (now showing accurate post-cleanup size)
609
+ echo -e "${WHITE}💾 Saved to:${NC} ${CYAN}$TEMP_FILE${NC} ${YELLOW}$FILE_COUNT${NC} ${WHITE}🗄️${NC} ${CACHE_COLOR}$SIZE_HUMAN${NC} ${WHITE}🧹${NC}${GOLD}[${AUTO_CLEAN_THRESHOLD}mb]${NC}"
628
610
 
629
- echo -e "${WHITE}💾 Saved to:${NC} ${CYAN}$TEMP_FILE${NC} ${YELLOW}$FILE_COUNT${NC} ${WHITE}🗄️${NC} ${CACHE_COLOR}$SIZE_HUMAN${NC} ${WHITE}🧹${NC}${GOLD}[${AUTO_CLEAN_THRESHOLD}mb]${NC}"
630
-
631
- if [[ -n "$BACKGROUND_MUSIC" ]]; then
632
- echo -e "${WHITE}🎵 Background music:${NC} ${PURPLE}$(basename "$BACKGROUND_MUSIC")${NC}"
633
- fi
634
- if [[ -n "${SPEAKER_ID:-}" ]] && [[ -n "${FILE_VOICE:-}" ]]; then
635
- echo -e "${WHITE}🎤 Voice used:${NC} ${BLUE}$FILE_VOICE${NC} ${WHITE}(Piper TTS)${NC}"
636
- else
637
- echo -e "${WHITE}🎤 Voice used:${NC} ${BLUE}$VOICE_MODEL${NC} ${WHITE}(Piper TTS)${NC}"
638
- fi
639
-
640
- PERSONALITY=$(cat "${PROJECT_ROOT:-/nonexistent}/.claude/tts-personality.txt" 2>/dev/null || cat "$HOME/.claude/tts-personality.txt" 2>/dev/null || echo "")
641
- if [[ -n "$PERSONALITY" ]] && [[ "$PERSONALITY" != "none" ]] && [[ "$PERSONALITY" != "normal" ]]; then
642
- echo -e "${WHITE}💫 Personality:${NC} ${YELLOW}$PERSONALITY${NC}"
643
- fi
611
+ if [[ -n "$BACKGROUND_MUSIC" ]]; then
612
+ # Extract just the filename to save space
613
+ MUSIC_FILENAME=$(basename "$BACKGROUND_MUSIC")
614
+ echo -e "${WHITE}🎵 Background music:${NC} ${PURPLE}$MUSIC_FILENAME${NC}"
615
+ fi
616
+ echo -e "${WHITE}🎤 Voice used:${NC} ${BLUE}$VOICE_MODEL${NC} ${WHITE}(Piper TTS)${NC}"
644
617
 
645
- echo -e "\033[38;5;240mSay: \"Turn off banner\" to hide this output\033[0m"
618
+ # Show personality if configured
619
+ PERSONALITY=$(cat "$PROJECT_ROOT/.claude/tts-personality.txt" 2>/dev/null || cat "$HOME/.claude/tts-personality.txt" 2>/dev/null || echo "")
620
+ if [[ -n "$PERSONALITY" ]] && [[ "$PERSONALITY" != "none" ]] && [[ "$PERSONALITY" != "normal" ]]; then
621
+ echo -e "${WHITE}💫 Personality:${NC} ${YELLOW}$PERSONALITY${NC}"
646
622
  fi
647
623
 
648
624
  # Check audio folder size and warn if getting large
649
- if [[ "$_BANNER_ENABLED" == "true" ]] && [[ -d "$AUDIO_DIR_PATH" ]]; then
625
+ if [[ -d "$AUDIO_DIR_PATH" ]]; then
650
626
  AUDIO_SIZE=$(du -sm "$AUDIO_DIR_PATH" 2>/dev/null | cut -f1)
651
627
  if [[ -n "$AUDIO_SIZE" ]] && [[ "$AUDIO_SIZE" -gt 100 ]]; then
652
628
  echo -e "\033[0;31m⚠️ Audio cache is ${AUDIO_SIZE}MB - Run: /agent-vibes:cleanup\033[0m"
@@ -673,14 +649,9 @@ fi
673
649
  if [[ -z "$BACKGROUND_MUSIC" ]]; then
674
650
  _bg_enabled=false
675
651
  if [[ -f "$BACKGROUND_ENABLED_FILE" ]] && grep -q "true" "$BACKGROUND_ENABLED_FILE" 2>/dev/null; then
676
- _bg_enabled=true
677
- elif [[ -f "$GLOBAL_BACKGROUND_ENABLED_FILE" ]] && grep -q "true" "$GLOBAL_BACKGROUND_ENABLED_FILE" 2>/dev/null; then
678
- _bg_enabled=true
679
- fi
680
- if [[ "$_bg_enabled" == "true" ]]; then
681
- echo "🎵 Background music: Enabled but not playing (check config)"
652
+ echo -e "${WHITE}🎵 Background music:${NC} ${PURPLE}Enabled but not playing (check config)${NC}"
682
653
  else
683
- echo "🎵 Background music: Disabled"
654
+ echo -e "${WHITE}🎵 Background music:${NC} ${PURPLE}Disabled${NC}"
684
655
  fi
685
656
  fi
686
657
 
@@ -50,18 +50,9 @@ export LC_ALL=C
50
50
  TEXT="$1"
51
51
  VOICE_OVERRIDE="$2" # Ignored — Soprano has a single voice, kept for provider contract
52
52
 
53
- # Strip emojis, asterisks, and markdown formatting
54
- TEXT=$(printf '%s' "$TEXT" | perl -CSD -pe '
55
- s/[\x{1F300}-\x{1F9FF}]//g;
56
- s/[\x{2600}-\x{27BF}]//g;
57
- s/[\x{FE00}-\x{FE0F}]//g;
58
- s/[\x{200D}]//g;
59
- s/[\x{2500}-\x{257F}]//g;
60
- s/[\x{2580}-\x{259F}]//g;
61
- s/\*+//g; s/#+\s*//g; s/`//g; s/~+//g; s/^\s*[-]\s*//g;
62
- ')
63
-
64
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
53
+ # Use readlink -f to handle symlinks correctly
54
+ SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
55
+ SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
65
56
  source "$SCRIPT_DIR/audio-cache-utils.sh"
66
57
 
67
58
  SOPRANO_PORT="${SOPRANO_PORT:-7860}"
@@ -114,11 +105,7 @@ fi
114
105
  # @intent Find appropriate directory for audio file storage
115
106
  # @why Supports project-local and global storage
116
107
  # @returns Sets $AUDIO_DIR global variable
117
- # SECURITY: Canonicalize path to prevent traversal (#128)
118
- if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
119
- CLAUDE_PROJECT_DIR=$(cd "${CLAUDE_PROJECT_DIR}" 2>/dev/null && pwd -P) || CLAUDE_PROJECT_DIR=""
120
- fi
121
- if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
108
+ if [[ -n "$CLAUDE_PROJECT_DIR" ]]; then
122
109
  AUDIO_DIR="$CLAUDE_PROJECT_DIR/.claude/audio"
123
110
  else
124
111
  CURRENT_DIR="$PWD"
@@ -135,8 +122,7 @@ else
135
122
  fi
136
123
 
137
124
  mkdir -p "$AUDIO_DIR"
138
- # SECURITY: Use mktemp for unpredictable filenames (#130)
139
- _tmp=$(mktemp "$AUDIO_DIR/tts-XXXXXX"); TEMP_FILE="${_tmp}.wav"; mv "$_tmp" "$TEMP_FILE"
125
+ TEMP_FILE="$AUDIO_DIR/tts-$(date +%s).wav"
140
126
 
141
127
  # @function synthesize_speech
142
128
  # @intent Generate speech using best available Soprano mode
@@ -153,12 +139,9 @@ if check_webui_server; then
153
139
  elif check_api_server; then
154
140
  # OpenAI-compatible API mode — direct curl
155
141
  SYNTH_MODE="api"
156
- # SECURITY: Use proper JSON encoding to prevent injection (#133)
157
- _JSON_PAYLOAD=$(printf '%s' "$TEXT" | python3 -c 'import sys,json; print(json.dumps({"input":sys.stdin.read()}))' 2>/dev/null) || \
158
- _JSON_PAYLOAD=$(printf '{"input":"%s"}' "$(printf '%s' "$TEXT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')")
159
142
  curl -sf "http://127.0.0.1:${SOPRANO_PORT}/v1/audio/speech" \
160
143
  -H "Content-Type: application/json" \
161
- -d "$_JSON_PAYLOAD" \
144
+ -d "$(printf '{"input":"%s"}' "$(echo "$TEXT" | sed 's/"/\\"/g')")" \
162
145
  --output "$TEMP_FILE" 2>/dev/null
163
146
  else
164
147
  # CLI fallback — reloads model each call (slowest)
@@ -187,7 +170,7 @@ fi
187
170
  # @intent Compress TTS audio for remote sessions (SSH/RDP)
188
171
  # @why Reduces bandwidth and prevents choppy playback
189
172
  if [[ "${AGENTVIBES_RDP_MODE:-false}" == "true" ]] && command -v ffmpeg &>/dev/null; then
190
- _tmp=$(mktemp "$AUDIO_DIR/tts-compressed-XXXXXX"); COMPRESSED_FILE="${_tmp}.wav"; mv "$_tmp" "$COMPRESSED_FILE"
173
+ COMPRESSED_FILE="$AUDIO_DIR/tts-compressed-$(date +%s).wav"
191
174
  ffmpeg -i "$TEMP_FILE" -ac 1 -ar 22050 -b:a 64k -y "$COMPRESSED_FILE" 2>/dev/null
192
175
  if [[ -f "$COMPRESSED_FILE" ]]; then
193
176
  rm -f "$TEMP_FILE"
@@ -199,7 +182,7 @@ fi
199
182
  # @intent Add silence to prevent WSL audio static
200
183
  # @why WSL audio subsystem cuts off first ~200ms
201
184
  if command -v ffmpeg &>/dev/null; then
202
- _tmp=$(mktemp "$AUDIO_DIR/tts-padded-XXXXXX"); PADDED_FILE="${_tmp}.wav"; mv "$_tmp" "$PADDED_FILE"
185
+ PADDED_FILE="$AUDIO_DIR/tts-padded-$(date +%s).wav"
203
186
  ffmpeg -f lavfi -i anullsrc=r=44100:cl=stereo:d=0.2 -i "$TEMP_FILE" \
204
187
  -filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[out]" \
205
188
  -map "[out]" -y "$PADDED_FILE" 2>/dev/null
@@ -231,24 +214,7 @@ fi
231
214
  # @function play_audio
232
215
  # @intent Play generated audio using available player with sequential playback
233
216
  # @why Support multiple audio players and prevent overlapping audio
234
- # SECURITY: Use user-isolated lock directory (#129)
235
- _LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp/agentvibes-$(id -u)}"
236
- mkdir -p "$_LOCK_DIR"
237
- chmod 700 "$_LOCK_DIR"
238
- LOCK_FILE="$_LOCK_DIR/agentvibes-audio.lock"
239
-
240
- # Auto-remove stale lock files (older than 30 seconds)
241
- if [ -f "$LOCK_FILE" ]; then
242
- if [[ "$(uname)" == "Darwin" ]]; then
243
- _lock_mtime=$(stat -f %m "$LOCK_FILE" 2>/dev/null || echo 0)
244
- else
245
- _lock_mtime=$(stat -c %Y "$LOCK_FILE" 2>/dev/null || echo 0)
246
- fi
247
- _lock_age=$(( $(date +%s) - _lock_mtime ))
248
- if [[ $_lock_age -gt 30 ]]; then
249
- rm -f "$LOCK_FILE"
250
- fi
251
- fi
217
+ LOCK_FILE="/tmp/agentvibes-audio.lock"
252
218
 
253
219
  for i in {1..4}; do
254
220
  if [ ! -f "$LOCK_FILE" ]; then