agentvibes 5.3.0 → 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.
- package/.agentvibes/LITE-MODE.md +236 -0
- package/.agentvibes/README.md +136 -0
- package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +141 -0
- package/.agentvibes/backups/agents/analyst_20260204_144958.md +78 -0
- package/.agentvibes/backups/agents/architect_20260204_144958.md +72 -0
- package/.agentvibes/backups/agents/dev_20260204_144958.md +74 -0
- package/.agentvibes/backups/agents/pm_20260204_144958.md +72 -0
- package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +64 -0
- package/.agentvibes/backups/agents/sm_20260204_144958.md +87 -0
- package/.agentvibes/backups/agents/tea_20260204_144958.md +79 -0
- package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +82 -0
- package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +80 -0
- package/.agentvibes/bmad/bmad-voices.md +69 -69
- package/.agentvibes/config/README-personality-defaults.md +162 -0
- package/.agentvibes/config/mode.txt +1 -0
- package/.agentvibes/config/personality-voice-defaults.default.json +21 -0
- package/.agentvibes/config/save-audio.txt +1 -0
- package/.agentvibes/config/voice-metadata.json +160 -0
- package/.agentvibes/config.json +24 -15
- package/.agentvibes/hooks/help.sh +191 -0
- package/.agentvibes/hooks/post-tool-use-lite.sh +111 -0
- package/.agentvibes/hooks/save-audio-manager.sh +162 -0
- package/.agentvibes/hooks/session-start-full-optimized.sh +102 -0
- package/.agentvibes/hooks/session-start-full.sh +142 -0
- package/.agentvibes/hooks/session-start-lite-v2.sh +34 -0
- package/.agentvibes/hooks/session-start-lite.sh +29 -0
- package/.agentvibes/hooks/stop-lite.sh +115 -0
- package/.agentvibes/hooks/switch-mode.sh +215 -0
- package/.agentvibes/output-styles/audio-summary.md +30 -0
- package/.claude/activation-instructions +54 -54
- package/.claude/audio/voice-samples/piper/alan.wav +0 -0
- package/.claude/audio/voice-samples/piper/amy.wav +0 -0
- package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
- package/.claude/audio/voice-samples/piper/joe.wav +0 -0
- package/.claude/audio/voice-samples/piper/john.wav +0 -0
- package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
- package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
- package/.claude/audio/voice-samples/piper/linda.wav +0 -0
- package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
- package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
- package/.claude/commands/agent-vibes/add.md +21 -21
- package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
- package/.claude/commands/agent-vibes/agent.md +79 -79
- package/.claude/commands/agent-vibes/background-music.md +111 -111
- package/.claude/commands/agent-vibes/bmad.md +198 -198
- package/.claude/commands/agent-vibes/clean.md +18 -18
- package/.claude/commands/agent-vibes/cleanup.md +18 -18
- package/.claude/commands/agent-vibes/commands.json +145 -145
- package/.claude/commands/agent-vibes/effects.md +97 -97
- package/.claude/commands/agent-vibes/get.md +9 -9
- package/.claude/commands/agent-vibes/hide.md +91 -91
- package/.claude/commands/agent-vibes/language.md +23 -23
- package/.claude/commands/agent-vibes/learn.md +67 -67
- package/.claude/commands/agent-vibes/list.md +13 -13
- package/.claude/commands/agent-vibes/mute.md +37 -37
- package/.claude/commands/agent-vibes/preview.md +17 -17
- package/.claude/commands/agent-vibes/provider.md +68 -68
- package/.claude/commands/agent-vibes/replay-target.md +14 -14
- package/.claude/commands/agent-vibes/sample.md +12 -12
- package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
- package/.claude/commands/agent-vibes/set-pretext.md +65 -65
- package/.claude/commands/agent-vibes/set-speed.md +41 -41
- package/.claude/commands/agent-vibes/show.md +84 -84
- package/.claude/commands/agent-vibes/switch.md +87 -87
- package/.claude/commands/agent-vibes/target-voice.md +26 -26
- package/.claude/commands/agent-vibes/target.md +30 -30
- package/.claude/commands/agent-vibes/translate.md +68 -68
- package/.claude/commands/agent-vibes/unmute.md +45 -45
- package/.claude/commands/agent-vibes/whoami.md +7 -7
- package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
- package/.claude/commands/agent-vibes-rdp.md +24 -24
- package/.claude/config/audio-effects.cfg +4 -11
- package/.claude/config/audio-effects.cfg.sample +52 -52
- package/.claude/config/background-music-position.txt +27 -0
- package/.claude/config/background-music-volume.txt +1 -1
- package/.claude/config/background-music.cfg +1 -0
- package/.claude/config/background-music.txt +1 -0
- package/.claude/config/tts-speech-rate.txt +1 -4
- package/.claude/config/tts-verbosity.txt +1 -0
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/README-TTS-QUEUE.md +135 -135
- package/.claude/hooks/audio-cache-utils.sh +0 -0
- package/.claude/hooks/audio-processor.sh +60 -14
- package/.claude/hooks/background-music-manager.sh +0 -0
- package/.claude/hooks/bmad-party-manager.sh +225 -0
- package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
- package/.claude/hooks/bmad-speak.sh +6 -13
- package/.claude/hooks/bmad-tts-injector.sh +0 -0
- package/.claude/hooks/bmad-voice-manager.sh +0 -0
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +25 -23
- package/.claude/hooks/clawdbot-receiver.sh +4 -28
- package/.claude/hooks/clean-audio-cache.sh +0 -0
- package/.claude/hooks/cleanup-cache.sh +0 -0
- package/.claude/hooks/configure-rdp-mode.sh +0 -0
- package/.claude/hooks/download-extra-voices.sh +0 -0
- package/.claude/hooks/effects-manager.sh +0 -0
- package/.claude/hooks/github-star-reminder.sh +0 -0
- package/.claude/hooks/language-manager.sh +0 -0
- package/.claude/hooks/learn-manager.sh +0 -0
- package/.claude/hooks/macos-voice-manager.sh +0 -0
- package/.claude/hooks/migrate-background-music.sh +0 -0
- package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
- package/.claude/hooks/optimize-background-music.sh +0 -0
- package/.claude/hooks/personality-manager.sh +0 -0
- package/.claude/hooks/piper-download-voices.sh +0 -0
- package/.claude/hooks/piper-installer.sh +1 -1
- package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
- package/.claude/hooks/piper-voice-manager.sh +0 -0
- package/.claude/hooks/play-tts-enhanced.sh +0 -0
- package/.claude/hooks/play-tts-macos.sh +6 -12
- package/.claude/hooks/play-tts-piper.sh +50 -79
- package/.claude/hooks/play-tts-soprano.sh +9 -43
- package/.claude/hooks/play-tts-ssh-remote.sh +43 -215
- package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
- package/.claude/hooks/play-tts.sh +31 -31
- package/.claude/hooks/post-response.sh +41 -0
- package/.claude/hooks/prepare-release.sh +0 -0
- package/.claude/hooks/provider-commands.sh +0 -0
- package/.claude/hooks/provider-manager.sh +0 -0
- package/.claude/hooks/replay-target-audio.sh +0 -0
- package/.claude/hooks/requirements.txt +6 -6
- package/.claude/hooks/sentiment-manager.sh +0 -0
- package/.claude/hooks/session-start-tts.sh +56 -39
- package/.claude/hooks/soprano-gradio-synth.py +139 -139
- package/.claude/hooks/speed-manager.sh +0 -0
- package/.claude/hooks/stop.sh +63 -0
- package/.claude/hooks/termux-installer.sh +0 -0
- package/.claude/hooks/translate-manager.sh +0 -0
- package/.claude/hooks/translator.py +237 -237
- package/.claude/hooks/tts-queue-worker.sh +0 -0
- package/.claude/hooks/tts-queue.sh +0 -0
- package/.claude/hooks/verbosity-manager.sh +0 -0
- package/.claude/hooks/voice-manager.sh +26 -4
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/bmad-party-speak.ps1 +278 -278
- package/.claude/hooks-windows/bmad-speak.ps1 +264 -264
- package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -53
- package/.claude/hooks-windows/effects-manager.ps1 +294 -294
- package/.claude/hooks-windows/language-manager.ps1 +193 -193
- package/.claude/hooks-windows/learn-manager.ps1 +241 -241
- package/.claude/hooks-windows/personality-manager.ps1 +266 -266
- package/.claude/hooks-windows/play-tts-soprano.ps1 +5 -5
- package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -138
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +164 -0
- package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -0
- package/.claude/hooks-windows/play-tts.ps1 +104 -513
- package/.claude/hooks-windows/provider-manager.ps1 +158 -192
- package/.claude/hooks-windows/session-start-tts.ps1 +55 -46
- package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
- package/.claude/hooks-windows/speed-manager.ps1 +166 -166
- package/.claude/hooks-windows/voice-manager-windows.ps1 +176 -260
- package/.claude/output-styles/agent-vibes.md +202 -202
- package/.claude/personalities/angry.md +14 -14
- package/.claude/personalities/annoying.md +14 -14
- package/.claude/personalities/crass.md +14 -14
- package/.claude/personalities/dramatic.md +14 -14
- package/.claude/personalities/dry-humor.md +50 -50
- package/.claude/personalities/flirty.md +20 -20
- package/.claude/personalities/funny.md +14 -14
- package/.claude/personalities/grandpa.md +32 -32
- package/.claude/personalities/millennial.md +14 -14
- package/.claude/personalities/moody.md +14 -14
- package/.claude/personalities/normal.md +16 -16
- package/.claude/personalities/pirate.md +14 -14
- package/.claude/personalities/poetic.md +14 -14
- package/.claude/personalities/professional.md +14 -14
- package/.claude/personalities/rapper.md +55 -55
- package/.claude/personalities/robot.md +14 -14
- package/.claude/personalities/sarcastic.md +38 -38
- package/.claude/personalities/sassy.md +14 -14
- package/.claude/personalities/surfer-dude.md +14 -14
- package/.claude/personalities/zen.md +14 -14
- package/.claude/piper-voices-dir.txt +1 -0
- package/.claude/settings.json +25 -15
- package/.claude/verbosity.txt +1 -1
- package/.clawdbot/README.md +105 -105
- package/.clawdbot/skill/SKILL.md +149 -145
- package/.mcp.json +30 -11
- package/CLAUDE.md +170 -215
- package/README.md +206 -525
- package/RELEASE_NOTES.md +1132 -1976
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +0 -0
- package/bin/agentvibes-voice-browser.js +64 -1289
- package/bin/agentvibes.js +0 -0
- package/bin/ensure-soprano-running.sh +43 -0
- package/bin/mcp-server.js +121 -121
- package/bin/mcp-server.sh +0 -0
- package/bin/test-bmad-pr +78 -78
- package/mcp-server/QUICK_START.md +203 -203
- package/mcp-server/README.md +345 -345
- package/mcp-server/WINDOWS_SETUP.md +260 -260
- package/mcp-server/docs/troubleshooting-audio.md +313 -313
- package/mcp-server/examples/claude_desktop_config.json +11 -11
- package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
- package/mcp-server/examples/custom_instructions.md +169 -169
- package/mcp-server/install-deps.js +130 -130
- package/mcp-server/pyproject.toml +52 -52
- package/mcp-server/requirements.txt +2 -2
- package/mcp-server/server.py +1451 -1578
- package/mcp-server/test_server.py +395 -395
- package/package.json +1 -3
- package/setup-windows.ps1 +815 -815
- package/src/installer.js +42 -5
- package/templates/agentvibes-receiver.sh +158 -483
- package/templates/audio/welcome-music.mp3 +0 -0
- package/.agentvibes/bmad-voice-map.json +0 -104
- package/.agentvibes/copilot-sessions.log +0 -4
- package/.claude/config/audio-effects-bmad.cfg +0 -50
- package/.claude/config/background-music-enabled.txt +0 -1
- package/.claude/config/intro-text.txt +0 -1
- package/.claude/config/personality.txt +0 -1
- package/.claude/config/piper-speech-rate.txt +0 -4
- package/.claude/config/piper-target-speech-rate.txt +0 -1
- package/.claude/config/reverb-level.txt +0 -1
- package/.claude/config/tts-target-speech-rate.txt +0 -1
- package/voice-assignments.json +0 -8245
- /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())
|