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
@@ -1,395 +1,395 @@
1
- #!/usr/bin/env python3
2
- """
3
- File: mcp-server/test_server.py
4
-
5
- AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
6
- Website: https://agentvibes.org
7
- Repository: https://github.com/paulpreibisch/AgentVibes
8
-
9
- Co-created by Paul Preibisch with Claude AI
10
- Copyright (c) 2025 Paul Preibisch
11
-
12
- Licensed under the Apache License, Version 2.0 (the "License");
13
- you may not use this file except in compliance with the License.
14
- You may obtain a copy of the License at
15
-
16
- http://www.apache.org/licenses/LICENSE-2.0
17
-
18
- Unless required by applicable law or agreed to in writing, software
19
- distributed under the License is distributed on an "AS IS" BASIS,
20
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
- See the License for the specific language governing permissions and
22
- limitations under the License.
23
-
24
- DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
- express or implied, including but not limited to the warranties of
26
- merchantability, fitness for a particular purpose and noninfringement.
27
- In no event shall the authors or copyright holders be liable for any claim,
28
- damages or other liability, whether in an action of contract, tort or
29
- otherwise, arising from, out of or in connection with the software or the
30
- use or other dealings in the software.
31
-
32
- ---
33
-
34
- @fileoverview Test suite for AgentVibes MCP Server
35
- @context Validates that MCP server can be imported and initialized correctly
36
- @architecture Unit tests for server initialization, imports, and helper methods
37
- @dependencies mcp-server/server.py, Python unittest framework
38
- @entrypoints Run directly via `python test_server.py` or from test runners
39
- @patterns Test-driven validation, import testing, path resolution testing
40
- @related mcp-server/server.py, docs/ai-optimized-documentation-standards.md
41
- """
42
-
43
- import os
44
- import sys
45
- from pathlib import Path
46
-
47
- # Add the mcp-server directory to path
48
- sys.path.insert(0, str(Path(__file__).parent))
49
-
50
-
51
- def test_imports():
52
- """Test that all required modules can be imported"""
53
- print("Testing imports...")
54
- try:
55
- from server import AgentVibesServer, app
56
- print("✅ Server imports successful")
57
- return True
58
- except ImportError as e:
59
- print(f"❌ Import failed: {e}")
60
- print(" Note: MCP library may not be installed")
61
- print(" Run: pip install mcp")
62
- return False
63
-
64
-
65
- def test_server_init():
66
- """Test that the server can be initialized"""
67
- print("\nTesting server initialization...")
68
- try:
69
- from server import AgentVibesServer
70
-
71
- server = AgentVibesServer()
72
- print(f"✅ Server initialized")
73
- print(f" Claude dir: {server.claude_dir}")
74
- print(f" Hooks dir: {server.hooks_dir}")
75
-
76
- # Check if hooks directory exists
77
- if server.hooks_dir.exists():
78
- print(f"✅ Hooks directory found")
79
- else:
80
- print(f"⚠️ Hooks directory not found (expected for testing)")
81
-
82
- return True
83
- except Exception as e:
84
- print(f"❌ Server init failed: {e}")
85
- return False
86
-
87
-
88
- def test_helper_methods():
89
- """Test helper methods"""
90
- print("\nTesting helper methods...")
91
- try:
92
- from server import AgentVibesServer
93
-
94
- server = AgentVibesServer()
95
-
96
- # Test _find_claude_dir
97
- claude_dir = server._find_claude_dir()
98
- print(f"✅ Found claude directory: {claude_dir}")
99
-
100
- return True
101
- except Exception as e:
102
- print(f"❌ Helper method test failed: {e}")
103
- return False
104
-
105
-
106
- def test_mute_unmute():
107
- """Test mute/unmute functionality"""
108
- print("\nTesting mute/unmute functionality...")
109
- try:
110
- from server import AgentVibesServer
111
- import asyncio
112
- import tempfile
113
- import os
114
-
115
- server = AgentVibesServer()
116
-
117
- # Use a temporary home directory for testing
118
- original_home = Path.home()
119
- test_mute_file = original_home / ".agentvibes-muted"
120
-
121
- # Clean up any existing mute file before testing
122
- if test_mute_file.exists():
123
- test_mute_file.unlink()
124
- print(" Cleaned up existing mute file")
125
-
126
- # Test 1: Initial state should be unmuted
127
- async def run_tests():
128
- result = await server.is_muted()
129
- assert "ACTIVE" in result, f"Expected TTS to be active initially, got: {result}"
130
- print("✅ Test 1: Initial state is unmuted")
131
-
132
- # Test 2: Mute should create the mute file
133
- result = await server.mute()
134
- assert "muted" in result.lower(), f"Expected mute confirmation, got: {result}"
135
- assert test_mute_file.exists(), "Mute file should exist after muting"
136
- print("✅ Test 2: Mute creates mute file")
137
-
138
- # Test 3: is_muted should report muted state
139
- result = await server.is_muted()
140
- assert "MUTED" in result, f"Expected TTS to be muted, got: {result}"
141
- print("✅ Test 3: is_muted correctly reports muted state")
142
-
143
- # Test 4: Unmute should remove the mute file
144
- result = await server.unmute()
145
- assert "unmuted" in result.lower() or "restored" in result.lower(), f"Expected unmute confirmation, got: {result}"
146
- assert not test_mute_file.exists(), "Mute file should not exist after unmuting"
147
- print("✅ Test 4: Unmute removes mute file")
148
-
149
- # Test 5: is_muted should report active state after unmute
150
- result = await server.is_muted()
151
- assert "ACTIVE" in result, f"Expected TTS to be active after unmute, got: {result}"
152
- print("✅ Test 5: is_muted correctly reports active state after unmute")
153
-
154
- # Test 6: Unmute when not muted should handle gracefully
155
- result = await server.unmute()
156
- assert "not muted" in result.lower() or "active" in result.lower(), f"Expected graceful handling, got: {result}"
157
- print("✅ Test 6: Unmute handles already-unmuted state gracefully")
158
-
159
- asyncio.run(run_tests())
160
-
161
- # Clean up
162
- if test_mute_file.exists():
163
- test_mute_file.unlink()
164
-
165
- print("✅ All mute/unmute tests passed")
166
- return True
167
-
168
- except AssertionError as e:
169
- print(f"❌ Assertion failed: {e}")
170
- # Clean up on failure
171
- test_mute_file = Path.home() / ".agentvibes-muted"
172
- if test_mute_file.exists():
173
- test_mute_file.unlink()
174
- return False
175
- except Exception as e:
176
- print(f"❌ Mute/unmute test failed: {e}")
177
- # Clean up on failure
178
- test_mute_file = Path.home() / ".agentvibes-muted"
179
- if test_mute_file.exists():
180
- test_mute_file.unlink()
181
- return False
182
-
183
-
184
- def test_play_tts_mute_check():
185
- """Test that play-tts script respects mute files (platform-aware)"""
186
- import platform
187
- is_windows = platform.system() == "Windows" and not os.environ.get("WSL_DISTRO_NAME")
188
-
189
- if is_windows:
190
- print("\nTesting play-tts.ps1 mute file detection...")
191
- script_path = Path(__file__).parent.parent / ".claude" / "hooks-windows" / "play-tts.ps1"
192
- shell_cmd = lambda s, msg: ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", str(s), msg]
193
- else:
194
- print("\nTesting play-tts.sh mute file detection...")
195
- script_path = Path(__file__).parent.parent / ".claude" / "hooks" / "play-tts.sh"
196
- shell_cmd = lambda s, msg: ["bash", str(s), msg]
197
-
198
- try:
199
- import subprocess
200
-
201
- if not script_path.exists():
202
- print(f"⚠️ {script_path.name} not found at {script_path}, skipping shell test")
203
- return True # Not a failure, just can't test
204
-
205
- # On Windows, play-tts.ps1 checks .claude/tts-muted.txt for "true"
206
- # On Unix, play-tts.sh checks ~/.agentvibes-muted file existence
207
- if is_windows:
208
- test_mute_file = Path.home() / ".claude" / "tts-muted.txt"
209
- test_mute_file.parent.mkdir(parents=True, exist_ok=True)
210
- else:
211
- test_mute_file = Path.home() / ".agentvibes-muted"
212
-
213
- # Clean up first
214
- if test_mute_file.exists():
215
- test_mute_file.unlink()
216
-
217
- # Test 1: With mute file, script should exit early silently
218
- if is_windows:
219
- test_mute_file.write_text("true")
220
- else:
221
- test_mute_file.touch()
222
- try:
223
- result = subprocess.run(
224
- shell_cmd(script_path, "Test message"),
225
- capture_output=True,
226
- text=True,
227
- timeout=5
228
- )
229
- output = result.stdout + result.stderr
230
- # When muted, script should exit 0 silently (no TTS output)
231
- assert result.returncode == 0, f"Expected exit code 0 when muted, got {result.returncode}"
232
- print(f"✅ Test 1: {script_path.name} respects mute file (exit code 0)")
233
- finally:
234
- if test_mute_file.exists():
235
- test_mute_file.unlink()
236
-
237
- # Test 2: Without mute file, script should proceed (we won't check full TTS, just that it doesn't say muted)
238
- # Note: This test may produce audio output
239
- print("✅ Test 2: Skipping audio test (would produce sound)")
240
-
241
- print(f"✅ {script_path.name} mute detection tests passed")
242
- return True
243
-
244
- except subprocess.TimeoutExpired:
245
- print("⚠️ Script timed out (might be running TTS)")
246
- # Clean up both possible mute files
247
- for f in [Path.home() / ".agentvibes-muted", Path.home() / ".claude" / "tts-muted.txt"]:
248
- if f.exists():
249
- f.unlink()
250
- return True # Timeout is acceptable if TTS is running
251
- except Exception as e:
252
- print(f"❌ {script_path.name} mute test failed: {e}")
253
- # Clean up both possible mute files
254
- for f in [Path.home() / ".agentvibes-muted", Path.home() / ".claude" / "tts-muted.txt"]:
255
- if f.exists():
256
- f.unlink()
257
- return False
258
-
259
-
260
- def test_set_provider():
261
- """Test set_provider MCP functionality (platform-aware)"""
262
- print("\nTesting set_provider MCP functionality...")
263
- try:
264
- from server import AgentVibesServer
265
- import asyncio
266
- import platform
267
- from pathlib import Path
268
-
269
- server = AgentVibesServer()
270
- is_windows = platform.system() == "Windows" and not os.environ.get("WSL_DISTRO_NAME")
271
-
272
- # Test provider switching with platform-appropriate providers
273
- async def run_tests():
274
- if is_windows:
275
- # Windows providers
276
- result = await server.set_provider("windows-sapi")
277
- assert "OK" in result or "switched" in result.lower(), f"Expected success switching to windows-sapi, got: {result}"
278
- print("✅ Test 1: Successfully switched to windows-sapi")
279
-
280
- result = await server.set_provider("windows-piper")
281
- assert "OK" in result or "switched" in result.lower() or "WARNING" in result, f"Expected success/warning switching to windows-piper, got: {result}"
282
- print("✅ Test 2: Switched to windows-piper (or warned if not installed)")
283
-
284
- result = await server.set_provider("soprano")
285
- assert "OK" in result or "switched" in result.lower() or "Script not found" in result, f"Expected success switching to soprano, got: {result}"
286
- print("✅ Test 3: Switched to soprano (or script not found)")
287
- else:
288
- # Unix providers
289
- result = await server.set_provider("piper")
290
- assert "\u2713" in result or "switched" in result.lower(), f"Expected success switching to piper, got: {result}"
291
- print("✅ Test 1: Successfully switched to piper")
292
-
293
- result = await server.set_provider("macos")
294
- assert "\u2713" in result or "switched" in result.lower(), f"Expected success switching to macos, got: {result}"
295
- print("✅ Test 2: Successfully switched to macos")
296
-
297
- result = await server.set_provider("termux-ssh")
298
- assert "\u2713" in result or "switched" in result.lower(), f"Expected success switching to termux-ssh, got: {result}"
299
- print("✅ Test 3: Successfully switched to termux-ssh")
300
-
301
- # Test 4: Invalid provider should fail
302
- result = await server.set_provider("invalid-provider")
303
- assert "Invalid" in result or "Error" in result, f"Expected error for invalid provider, got: {result}"
304
- print("✅ Test 4: Correctly rejected invalid provider")
305
-
306
- # Test 5: Case insensitivity
307
- test_provider = "WINDOWS-SAPI" if is_windows else "PIPER"
308
- expected_lower = "windows-sapi" if is_windows else "piper"
309
- result = await server.set_provider(test_provider)
310
- assert "OK" in result or "\u2713" in result or "switched" in result.lower(), f"Expected success with uppercase {test_provider}, got: {result}"
311
- print("✅ Test 5: Case-insensitive provider switching works")
312
-
313
- # Test 6: Verify provider file was written
314
- # On Windows, provider-manager.ps1 writes to $USERPROFILE\.claude\
315
- # On Unix, provider-manager.sh may write to project dir or home dir
316
- if is_windows:
317
- provider_file = Path.home() / ".claude" / "tts-provider.txt"
318
- else:
319
- provider_file = server.claude_dir / "tts-provider.txt"
320
- if not provider_file.exists():
321
- provider_file = Path.home() / ".claude" / "tts-provider.txt"
322
-
323
- if provider_file.exists():
324
- # Strip BOM that PowerShell's Set-Content may add on Windows
325
- provider_content = provider_file.read_text(encoding="utf-8-sig").strip()
326
- assert provider_content == expected_lower, f"Expected provider file to contain '{expected_lower}', got: {provider_content}"
327
- print("✅ Test 6: Provider file correctly written")
328
- else:
329
- print("⚠️ Test 6: Provider file not found (may be in project dir)")
330
-
331
- asyncio.run(run_tests())
332
-
333
- print("✅ All set_provider tests passed")
334
- return True
335
-
336
- except AssertionError as e:
337
- print(f"❌ Assertion failed: {e}")
338
- return False
339
- except Exception as e:
340
- print(f"❌ set_provider test failed: {e}")
341
- import traceback
342
- traceback.print_exc()
343
- return False
344
-
345
-
346
- def main():
347
- """Run all tests"""
348
- print("=" * 60)
349
- print("AgentVibes MCP Server Test Suite")
350
- print("=" * 60)
351
-
352
- tests = [
353
- ("Imports", test_imports),
354
- ("Server Initialization", test_server_init),
355
- ("Helper Methods", test_helper_methods),
356
- ("Mute/Unmute Functionality", test_mute_unmute),
357
- ("play-tts Mute Detection", test_play_tts_mute_check),
358
- ("set_provider MCP Function", test_set_provider),
359
- ]
360
-
361
- results = []
362
- for name, test_func in tests:
363
- try:
364
- result = test_func()
365
- results.append((name, result))
366
- except Exception as e:
367
- print(f"\n❌ Test '{name}' crashed: {e}")
368
- results.append((name, False))
369
-
370
- print("\n" + "=" * 60)
371
- print("Test Results Summary")
372
- print("=" * 60)
373
-
374
- passed = sum(1 for _, result in results if result)
375
- total = len(results)
376
-
377
- for name, result in results:
378
- status = "✅ PASS" if result else "❌ FAIL"
379
- print(f"{status} - {name}")
380
-
381
- print("=" * 60)
382
- print(f"Total: {passed}/{total} tests passed")
383
-
384
- if passed == total:
385
- print("\n🎉 All tests passed!")
386
- return 0
387
- else:
388
- print(f"\n⚠️ {total - passed} test(s) failed")
389
- print("\nNote: Some failures may be expected if MCP library is not installed")
390
- print("Install with: pip install mcp")
391
- return 1
392
-
393
-
394
- if __name__ == "__main__":
395
- sys.exit(main())
1
+ #!/usr/bin/env python3
2
+ """
3
+ File: mcp-server/test_server.py
4
+
5
+ AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
6
+ Website: https://agentvibes.org
7
+ Repository: https://github.com/paulpreibisch/AgentVibes
8
+
9
+ Co-created by Paul Preibisch with Claude AI
10
+ Copyright (c) 2025 Paul Preibisch
11
+
12
+ Licensed under the Apache License, Version 2.0 (the "License");
13
+ you may not use this file except in compliance with the License.
14
+ You may obtain a copy of the License at
15
+
16
+ http://www.apache.org/licenses/LICENSE-2.0
17
+
18
+ Unless required by applicable law or agreed to in writing, software
19
+ distributed under the License is distributed on an "AS IS" BASIS,
20
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ See the License for the specific language governing permissions and
22
+ limitations under the License.
23
+
24
+ DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ express or implied, including but not limited to the warranties of
26
+ merchantability, fitness for a particular purpose and noninfringement.
27
+ In no event shall the authors or copyright holders be liable for any claim,
28
+ damages or other liability, whether in an action of contract, tort or
29
+ otherwise, arising from, out of or in connection with the software or the
30
+ use or other dealings in the software.
31
+
32
+ ---
33
+
34
+ @fileoverview Test suite for AgentVibes MCP Server
35
+ @context Validates that MCP server can be imported and initialized correctly
36
+ @architecture Unit tests for server initialization, imports, and helper methods
37
+ @dependencies mcp-server/server.py, Python unittest framework
38
+ @entrypoints Run directly via `python test_server.py` or from test runners
39
+ @patterns Test-driven validation, import testing, path resolution testing
40
+ @related mcp-server/server.py, docs/ai-optimized-documentation-standards.md
41
+ """
42
+
43
+ import os
44
+ import sys
45
+ from pathlib import Path
46
+
47
+ # Add the mcp-server directory to path
48
+ sys.path.insert(0, str(Path(__file__).parent))
49
+
50
+
51
+ def test_imports():
52
+ """Test that all required modules can be imported"""
53
+ print("Testing imports...")
54
+ try:
55
+ from server import AgentVibesServer, app
56
+ print("✅ Server imports successful")
57
+ return True
58
+ except ImportError as e:
59
+ print(f"❌ Import failed: {e}")
60
+ print(" Note: MCP library may not be installed")
61
+ print(" Run: pip install mcp")
62
+ return False
63
+
64
+
65
+ def test_server_init():
66
+ """Test that the server can be initialized"""
67
+ print("\nTesting server initialization...")
68
+ try:
69
+ from server import AgentVibesServer
70
+
71
+ server = AgentVibesServer()
72
+ print(f"✅ Server initialized")
73
+ print(f" Claude dir: {server.claude_dir}")
74
+ print(f" Hooks dir: {server.hooks_dir}")
75
+
76
+ # Check if hooks directory exists
77
+ if server.hooks_dir.exists():
78
+ print(f"✅ Hooks directory found")
79
+ else:
80
+ print(f"⚠️ Hooks directory not found (expected for testing)")
81
+
82
+ return True
83
+ except Exception as e:
84
+ print(f"❌ Server init failed: {e}")
85
+ return False
86
+
87
+
88
+ def test_helper_methods():
89
+ """Test helper methods"""
90
+ print("\nTesting helper methods...")
91
+ try:
92
+ from server import AgentVibesServer
93
+
94
+ server = AgentVibesServer()
95
+
96
+ # Test _find_claude_dir
97
+ claude_dir = server._find_claude_dir()
98
+ print(f"✅ Found claude directory: {claude_dir}")
99
+
100
+ return True
101
+ except Exception as e:
102
+ print(f"❌ Helper method test failed: {e}")
103
+ return False
104
+
105
+
106
+ def test_mute_unmute():
107
+ """Test mute/unmute functionality"""
108
+ print("\nTesting mute/unmute functionality...")
109
+ try:
110
+ from server import AgentVibesServer
111
+ import asyncio
112
+ import tempfile
113
+ import os
114
+
115
+ server = AgentVibesServer()
116
+
117
+ # Use a temporary home directory for testing
118
+ original_home = Path.home()
119
+ test_mute_file = original_home / ".agentvibes-muted"
120
+
121
+ # Clean up any existing mute file before testing
122
+ if test_mute_file.exists():
123
+ test_mute_file.unlink()
124
+ print(" Cleaned up existing mute file")
125
+
126
+ # Test 1: Initial state should be unmuted
127
+ async def run_tests():
128
+ result = await server.is_muted()
129
+ assert "ACTIVE" in result, f"Expected TTS to be active initially, got: {result}"
130
+ print("✅ Test 1: Initial state is unmuted")
131
+
132
+ # Test 2: Mute should create the mute file
133
+ result = await server.mute()
134
+ assert "muted" in result.lower(), f"Expected mute confirmation, got: {result}"
135
+ assert test_mute_file.exists(), "Mute file should exist after muting"
136
+ print("✅ Test 2: Mute creates mute file")
137
+
138
+ # Test 3: is_muted should report muted state
139
+ result = await server.is_muted()
140
+ assert "MUTED" in result, f"Expected TTS to be muted, got: {result}"
141
+ print("✅ Test 3: is_muted correctly reports muted state")
142
+
143
+ # Test 4: Unmute should remove the mute file
144
+ result = await server.unmute()
145
+ assert "unmuted" in result.lower() or "restored" in result.lower(), f"Expected unmute confirmation, got: {result}"
146
+ assert not test_mute_file.exists(), "Mute file should not exist after unmuting"
147
+ print("✅ Test 4: Unmute removes mute file")
148
+
149
+ # Test 5: is_muted should report active state after unmute
150
+ result = await server.is_muted()
151
+ assert "ACTIVE" in result, f"Expected TTS to be active after unmute, got: {result}"
152
+ print("✅ Test 5: is_muted correctly reports active state after unmute")
153
+
154
+ # Test 6: Unmute when not muted should handle gracefully
155
+ result = await server.unmute()
156
+ assert "not muted" in result.lower() or "active" in result.lower(), f"Expected graceful handling, got: {result}"
157
+ print("✅ Test 6: Unmute handles already-unmuted state gracefully")
158
+
159
+ asyncio.run(run_tests())
160
+
161
+ # Clean up
162
+ if test_mute_file.exists():
163
+ test_mute_file.unlink()
164
+
165
+ print("✅ All mute/unmute tests passed")
166
+ return True
167
+
168
+ except AssertionError as e:
169
+ print(f"❌ Assertion failed: {e}")
170
+ # Clean up on failure
171
+ test_mute_file = Path.home() / ".agentvibes-muted"
172
+ if test_mute_file.exists():
173
+ test_mute_file.unlink()
174
+ return False
175
+ except Exception as e:
176
+ print(f"❌ Mute/unmute test failed: {e}")
177
+ # Clean up on failure
178
+ test_mute_file = Path.home() / ".agentvibes-muted"
179
+ if test_mute_file.exists():
180
+ test_mute_file.unlink()
181
+ return False
182
+
183
+
184
+ def test_play_tts_mute_check():
185
+ """Test that play-tts script respects mute files (platform-aware)"""
186
+ import platform
187
+ is_windows = platform.system() == "Windows" and not os.environ.get("WSL_DISTRO_NAME")
188
+
189
+ if is_windows:
190
+ print("\nTesting play-tts.ps1 mute file detection...")
191
+ script_path = Path(__file__).parent.parent / ".claude" / "hooks-windows" / "play-tts.ps1"
192
+ shell_cmd = lambda s, msg: ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", str(s), msg]
193
+ else:
194
+ print("\nTesting play-tts.sh mute file detection...")
195
+ script_path = Path(__file__).parent.parent / ".claude" / "hooks" / "play-tts.sh"
196
+ shell_cmd = lambda s, msg: ["bash", str(s), msg]
197
+
198
+ try:
199
+ import subprocess
200
+
201
+ if not script_path.exists():
202
+ print(f"⚠️ {script_path.name} not found at {script_path}, skipping shell test")
203
+ return True # Not a failure, just can't test
204
+
205
+ # On Windows, play-tts.ps1 checks .claude/tts-muted.txt for "true"
206
+ # On Unix, play-tts.sh checks ~/.agentvibes-muted file existence
207
+ if is_windows:
208
+ test_mute_file = Path.home() / ".claude" / "tts-muted.txt"
209
+ test_mute_file.parent.mkdir(parents=True, exist_ok=True)
210
+ else:
211
+ test_mute_file = Path.home() / ".agentvibes-muted"
212
+
213
+ # Clean up first
214
+ if test_mute_file.exists():
215
+ test_mute_file.unlink()
216
+
217
+ # Test 1: With mute file, script should exit early silently
218
+ if is_windows:
219
+ test_mute_file.write_text("true")
220
+ else:
221
+ test_mute_file.touch()
222
+ try:
223
+ result = subprocess.run(
224
+ shell_cmd(script_path, "Test message"),
225
+ capture_output=True,
226
+ text=True,
227
+ timeout=5
228
+ )
229
+ output = result.stdout + result.stderr
230
+ # When muted, script should exit 0 silently (no TTS output)
231
+ assert result.returncode == 0, f"Expected exit code 0 when muted, got {result.returncode}"
232
+ print(f"✅ Test 1: {script_path.name} respects mute file (exit code 0)")
233
+ finally:
234
+ if test_mute_file.exists():
235
+ test_mute_file.unlink()
236
+
237
+ # Test 2: Without mute file, script should proceed (we won't check full TTS, just that it doesn't say muted)
238
+ # Note: This test may produce audio output
239
+ print("✅ Test 2: Skipping audio test (would produce sound)")
240
+
241
+ print(f"✅ {script_path.name} mute detection tests passed")
242
+ return True
243
+
244
+ except subprocess.TimeoutExpired:
245
+ print("⚠️ Script timed out (might be running TTS)")
246
+ # Clean up both possible mute files
247
+ for f in [Path.home() / ".agentvibes-muted", Path.home() / ".claude" / "tts-muted.txt"]:
248
+ if f.exists():
249
+ f.unlink()
250
+ return True # Timeout is acceptable if TTS is running
251
+ except Exception as e:
252
+ print(f"❌ {script_path.name} mute test failed: {e}")
253
+ # Clean up both possible mute files
254
+ for f in [Path.home() / ".agentvibes-muted", Path.home() / ".claude" / "tts-muted.txt"]:
255
+ if f.exists():
256
+ f.unlink()
257
+ return False
258
+
259
+
260
+ def test_set_provider():
261
+ """Test set_provider MCP functionality (platform-aware)"""
262
+ print("\nTesting set_provider MCP functionality...")
263
+ try:
264
+ from server import AgentVibesServer
265
+ import asyncio
266
+ import platform
267
+ from pathlib import Path
268
+
269
+ server = AgentVibesServer()
270
+ is_windows = platform.system() == "Windows" and not os.environ.get("WSL_DISTRO_NAME")
271
+
272
+ # Test provider switching with platform-appropriate providers
273
+ async def run_tests():
274
+ if is_windows:
275
+ # Windows providers
276
+ result = await server.set_provider("windows-sapi")
277
+ assert "OK" in result or "switched" in result.lower(), f"Expected success switching to windows-sapi, got: {result}"
278
+ print("✅ Test 1: Successfully switched to windows-sapi")
279
+
280
+ result = await server.set_provider("windows-piper")
281
+ assert "OK" in result or "switched" in result.lower() or "WARNING" in result, f"Expected success/warning switching to windows-piper, got: {result}"
282
+ print("✅ Test 2: Switched to windows-piper (or warned if not installed)")
283
+
284
+ result = await server.set_provider("soprano")
285
+ assert "OK" in result or "switched" in result.lower() or "Script not found" in result, f"Expected success switching to soprano, got: {result}"
286
+ print("✅ Test 3: Switched to soprano (or script not found)")
287
+ else:
288
+ # Unix providers
289
+ result = await server.set_provider("piper")
290
+ assert "\u2713" in result or "switched" in result.lower(), f"Expected success switching to piper, got: {result}"
291
+ print("✅ Test 1: Successfully switched to piper")
292
+
293
+ result = await server.set_provider("macos")
294
+ assert "\u2713" in result or "switched" in result.lower(), f"Expected success switching to macos, got: {result}"
295
+ print("✅ Test 2: Successfully switched to macos")
296
+
297
+ result = await server.set_provider("termux-ssh")
298
+ assert "\u2713" in result or "switched" in result.lower(), f"Expected success switching to termux-ssh, got: {result}"
299
+ print("✅ Test 3: Successfully switched to termux-ssh")
300
+
301
+ # Test 4: Invalid provider should fail
302
+ result = await server.set_provider("invalid-provider")
303
+ assert "Invalid" in result or "Error" in result, f"Expected error for invalid provider, got: {result}"
304
+ print("✅ Test 4: Correctly rejected invalid provider")
305
+
306
+ # Test 5: Case insensitivity
307
+ test_provider = "WINDOWS-SAPI" if is_windows else "PIPER"
308
+ expected_lower = "windows-sapi" if is_windows else "piper"
309
+ result = await server.set_provider(test_provider)
310
+ assert "OK" in result or "\u2713" in result or "switched" in result.lower(), f"Expected success with uppercase {test_provider}, got: {result}"
311
+ print("✅ Test 5: Case-insensitive provider switching works")
312
+
313
+ # Test 6: Verify provider file was written
314
+ # On Windows, provider-manager.ps1 writes to $USERPROFILE\.claude\
315
+ # On Unix, provider-manager.sh may write to project dir or home dir
316
+ if is_windows:
317
+ provider_file = Path.home() / ".claude" / "tts-provider.txt"
318
+ else:
319
+ provider_file = server.claude_dir / "tts-provider.txt"
320
+ if not provider_file.exists():
321
+ provider_file = Path.home() / ".claude" / "tts-provider.txt"
322
+
323
+ if provider_file.exists():
324
+ # Strip BOM that PowerShell's Set-Content may add on Windows
325
+ provider_content = provider_file.read_text(encoding="utf-8-sig").strip()
326
+ assert provider_content == expected_lower, f"Expected provider file to contain '{expected_lower}', got: {provider_content}"
327
+ print("✅ Test 6: Provider file correctly written")
328
+ else:
329
+ print("⚠️ Test 6: Provider file not found (may be in project dir)")
330
+
331
+ asyncio.run(run_tests())
332
+
333
+ print("✅ All set_provider tests passed")
334
+ return True
335
+
336
+ except AssertionError as e:
337
+ print(f"❌ Assertion failed: {e}")
338
+ return False
339
+ except Exception as e:
340
+ print(f"❌ set_provider test failed: {e}")
341
+ import traceback
342
+ traceback.print_exc()
343
+ return False
344
+
345
+
346
+ def main():
347
+ """Run all tests"""
348
+ print("=" * 60)
349
+ print("AgentVibes MCP Server Test Suite")
350
+ print("=" * 60)
351
+
352
+ tests = [
353
+ ("Imports", test_imports),
354
+ ("Server Initialization", test_server_init),
355
+ ("Helper Methods", test_helper_methods),
356
+ ("Mute/Unmute Functionality", test_mute_unmute),
357
+ ("play-tts Mute Detection", test_play_tts_mute_check),
358
+ ("set_provider MCP Function", test_set_provider),
359
+ ]
360
+
361
+ results = []
362
+ for name, test_func in tests:
363
+ try:
364
+ result = test_func()
365
+ results.append((name, result))
366
+ except Exception as e:
367
+ print(f"\n❌ Test '{name}' crashed: {e}")
368
+ results.append((name, False))
369
+
370
+ print("\n" + "=" * 60)
371
+ print("Test Results Summary")
372
+ print("=" * 60)
373
+
374
+ passed = sum(1 for _, result in results if result)
375
+ total = len(results)
376
+
377
+ for name, result in results:
378
+ status = "✅ PASS" if result else "❌ FAIL"
379
+ print(f"{status} - {name}")
380
+
381
+ print("=" * 60)
382
+ print(f"Total: {passed}/{total} tests passed")
383
+
384
+ if passed == total:
385
+ print("\n🎉 All tests passed!")
386
+ return 0
387
+ else:
388
+ print(f"\n⚠️ {total - passed} test(s) failed")
389
+ print("\nNote: Some failures may be expected if MCP library is not installed")
390
+ print("Install with: pip install mcp")
391
+ return 1
392
+
393
+
394
+ if __name__ == "__main__":
395
+ sys.exit(main())