agentvibes 3.5.9 → 4.0.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/bmad/bmad-voices-enabled.flag +0 -0
- package/.agentvibes/bmad/bmad-voices.md +69 -0
- package/.claude/config/audio-effects.cfg +1 -1
- package/.claude/config/background-music-position.txt +1 -27
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-processor.sh +32 -17
- package/.claude/hooks/bmad-speak-enhanced.sh +5 -5
- package/.claude/hooks/bmad-speak.sh +4 -4
- package/.claude/hooks/bmad-voice-manager.sh +8 -8
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +23 -25
- package/.claude/hooks/clawdbot-receiver.sh +28 -4
- package/.claude/hooks/language-manager.sh +1 -1
- package/.claude/hooks/path-resolver.sh +60 -0
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +90 -0
- package/.claude/hooks/play-tts-piper.sh +82 -24
- package/.claude/hooks/play-tts-ssh-remote.sh +13 -15
- package/.claude/hooks/play-tts.sh +16 -5
- package/.claude/hooks/session-start-tts.sh +26 -56
- package/.claude/hooks/soprano-gradio-synth.py +1 -1
- package/.claude/hooks/verbosity-manager.sh +10 -4
- package/.claude/settings.json +1 -1
- package/CLAUDE.md +129 -104
- package/README.md +418 -10
- package/RELEASE_NOTES.md +60 -1036
- package/bin/agentvibes-voice-browser.js +1827 -0
- package/bin/agentvibes.js +100 -0
- package/mcp-server/server.py +67 -3
- package/package.json +11 -2
- package/src/console/app.js +806 -0
- package/src/console/audio-env.js +123 -0
- package/src/console/brand-colors.js +13 -0
- package/src/console/footer-config.js +42 -0
- package/src/console/modals/.gitkeep +0 -0
- package/src/console/modals/modal-overlay.js +247 -0
- package/src/console/navigation.js +60 -0
- package/src/console/tabs/.gitkeep +0 -0
- package/src/console/tabs/agents-tab.js +369 -0
- package/src/console/tabs/help-tab.js +261 -0
- package/src/console/tabs/install-tab.js +990 -0
- package/src/console/tabs/music-tab.js +997 -0
- package/src/console/tabs/placeholder-tab.js +45 -0
- package/src/console/tabs/readme-tab.js +267 -0
- package/src/console/tabs/settings-tab.js +3949 -0
- package/src/console/tabs/voices-tab.js +1574 -0
- package/src/installer/music-file-input.js +304 -0
- package/src/installer.js +1353 -676
- package/src/services/.gitkeep +0 -0
- package/src/services/agent-voice-store.js +163 -0
- package/src/services/config-service.js +240 -0
- package/src/services/navigation-service.js +123 -0
- package/src/services/provider-service.js +132 -0
- package/src/services/verbosity-service.js +157 -0
- package/src/utils/audio-duration-validator.js +298 -0
- package/src/utils/audio-format-validator.js +277 -0
- package/src/utils/dependency-checker.js +3 -3
- package/src/utils/file-ownership-verifier.js +358 -0
- package/src/utils/music-file-validator.js +275 -0
- package/src/utils/preview-list-prompt.js +136 -0
- package/src/utils/provider-validator.js +144 -132
- package/src/utils/secure-music-storage.js +412 -0
- package/templates/agentvibes-receiver.sh +11 -7
- package/voice-assignments.json +8245 -0
- package/.claude/config/background-music-volume.txt +0 -1
- package/.claude/config/background-music.cfg +0 -1
- package/.claude/config/background-music.txt +0 -1
- package/.claude/config/tts-speech-rate.txt +0 -1
- package/.claude/config/tts-verbosity.txt +0 -1
- package/.claude/hooks/bmad-party-manager.sh +0 -225
- package/.claude/hooks/stop.sh +0 -38
- package/.claude/piper-voices-dir.txt +0 -1
- package/.mcp.json +0 -34
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentVibes TUI Console — Command Entry Point
|
|
4
|
+
* Story 6.5: Command Routing & Entry Points
|
|
5
|
+
*
|
|
6
|
+
* Routes CLI subcommands to the correct TUI console tab:
|
|
7
|
+
* npx agentvibes → smart detection (Settings if installed, Install if not)
|
|
8
|
+
* npx agentvibes install → Install tab
|
|
9
|
+
* npx agentvibes config → Settings tab
|
|
10
|
+
* npx agentvibes configure → Settings tab
|
|
11
|
+
* npx agentvibes <unknown> → help text + exit(1)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import { ConfigService } from '../src/services/config-service.js';
|
|
17
|
+
import { launchConsole } from '../src/console/app.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve CLI args to a TUI start tab or an error.
|
|
21
|
+
* Exported as named export for unit testing without child-process spawning.
|
|
22
|
+
*
|
|
23
|
+
* @param {string[]} args - CLI arguments (process.argv.slice(2))
|
|
24
|
+
* @param {{ isInstalled: () => boolean }} configService
|
|
25
|
+
* @returns {{ startTab: string } | { help: string } | { error: string }}
|
|
26
|
+
*/
|
|
27
|
+
export function resolveStartTab(args, configService) {
|
|
28
|
+
const cmd = args[0];
|
|
29
|
+
|
|
30
|
+
if (cmd === 'install') {
|
|
31
|
+
return { startTab: 'install' };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (cmd === 'config' || cmd === 'configure') {
|
|
35
|
+
return { startTab: 'settings' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (cmd === '--help' || cmd === '-h') {
|
|
39
|
+
return {
|
|
40
|
+
help: [
|
|
41
|
+
'AgentVibes — TTS for AI assistants with personality',
|
|
42
|
+
'',
|
|
43
|
+
'Usage:',
|
|
44
|
+
' npx agentvibes # Open console (auto-detects install state)',
|
|
45
|
+
' npx agentvibes install # Open Install tab',
|
|
46
|
+
' npx agentvibes config # Open Settings tab',
|
|
47
|
+
' npx agentvibes --help # Show this help',
|
|
48
|
+
'',
|
|
49
|
+
'Visit https://agentvibes.org for documentation.',
|
|
50
|
+
].join('\n'),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!cmd) {
|
|
55
|
+
// Smart detection: show Install tab if not installed, Settings if installed
|
|
56
|
+
return { startTab: configService.isInstalled() ? 'settings' : 'install' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Unknown command → help + error
|
|
60
|
+
return {
|
|
61
|
+
error: [
|
|
62
|
+
`Unknown command: '${cmd}'`,
|
|
63
|
+
'',
|
|
64
|
+
'Usage:',
|
|
65
|
+
' npx agentvibes # Open console (auto-detects install state)',
|
|
66
|
+
' npx agentvibes install # Open Install tab',
|
|
67
|
+
' npx agentvibes config # Open Settings tab',
|
|
68
|
+
'',
|
|
69
|
+
"Run 'npx agentvibes' to get started.",
|
|
70
|
+
].join('\n'),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Main: run only when executed directly (not imported by tests)
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
// Resolve symlinks before comparing so the guard works when run via npm link / npx
|
|
79
|
+
const _thisFile = fileURLToPath(import.meta.url);
|
|
80
|
+
const _argv1 = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
81
|
+
|
|
82
|
+
if (_argv1 === _thisFile) {
|
|
83
|
+
const configService = new ConfigService();
|
|
84
|
+
const result = resolveStartTab(process.argv.slice(2), configService);
|
|
85
|
+
|
|
86
|
+
if (result.help) {
|
|
87
|
+
process.stdout.write(result.help + '\n');
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (result.error) {
|
|
92
|
+
process.stderr.write(result.error + '\n');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
launchConsole({ startTab: result.startTab }).catch(err => {
|
|
97
|
+
process.stderr.write(`Failed to launch AgentVibes console: ${err.message}\n`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
100
|
+
}
|
package/mcp-server/server.py
CHANGED
|
@@ -41,6 +41,7 @@ use or other dealings in the software.
|
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
43
|
import asyncio
|
|
44
|
+
import json
|
|
44
45
|
import os
|
|
45
46
|
import platform
|
|
46
47
|
import subprocess
|
|
@@ -101,6 +102,62 @@ class AgentVibesServer:
|
|
|
101
102
|
# Fallback to global ~/.claude (should never happen in properly installed package)
|
|
102
103
|
return Path.home() / self.CLAUDE_DIR_NAME
|
|
103
104
|
|
|
105
|
+
def _resolve_friendly_name(self, voice_name: str) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Resolve friendly name to Piper voice ID using voice-metadata.json.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
voice_name: Friendly name (e.g., "ryan") or Piper ID
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Resolved Piper voice ID, or original voice_name if not found
|
|
114
|
+
"""
|
|
115
|
+
import re
|
|
116
|
+
|
|
117
|
+
metadata_path = self.agentvibes_root / ".agentvibes" / "config" / "voice-metadata.json"
|
|
118
|
+
|
|
119
|
+
# SECURITY: Verify file exists and is not a symlink
|
|
120
|
+
if not metadata_path.exists() or metadata_path.is_symlink():
|
|
121
|
+
return voice_name
|
|
122
|
+
|
|
123
|
+
# SECURITY: Verify file ownership matches current user (Unix only)
|
|
124
|
+
try:
|
|
125
|
+
if hasattr(os, 'getuid'):
|
|
126
|
+
stat_info = metadata_path.stat()
|
|
127
|
+
if stat_info.st_uid != os.getuid():
|
|
128
|
+
return voice_name
|
|
129
|
+
except (OSError, AttributeError):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
with open(metadata_path, 'r') as f:
|
|
134
|
+
metadata = json.load(f)
|
|
135
|
+
|
|
136
|
+
voices = metadata.get('voices', {})
|
|
137
|
+
voice_lower = voice_name.lower()
|
|
138
|
+
|
|
139
|
+
resolved_id = None
|
|
140
|
+
|
|
141
|
+
# Check if it's a friendly name key
|
|
142
|
+
if voice_lower in voices:
|
|
143
|
+
resolved_id = voices[voice_lower].get('id')
|
|
144
|
+
|
|
145
|
+
# Check if it matches a displayName
|
|
146
|
+
if not resolved_id:
|
|
147
|
+
for friendly_name, voice_data in voices.items():
|
|
148
|
+
if voice_data.get('displayName', '').lower() == voice_lower:
|
|
149
|
+
resolved_id = voice_data.get('id')
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
# SECURITY: Validate resolved ID matches safe pattern
|
|
153
|
+
if resolved_id and re.match(r'^[a-zA-Z0-9_-]+$', resolved_id):
|
|
154
|
+
return resolved_id
|
|
155
|
+
|
|
156
|
+
except (json.JSONDecodeError, KeyError, IOError, TypeError):
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
return voice_name
|
|
160
|
+
|
|
104
161
|
async def text_to_speech(
|
|
105
162
|
self,
|
|
106
163
|
text: str,
|
|
@@ -249,18 +306,25 @@ class AgentVibesServer:
|
|
|
249
306
|
|
|
250
307
|
async def set_voice(self, voice_name: str) -> str:
|
|
251
308
|
"""
|
|
252
|
-
Switch to a different voice.
|
|
309
|
+
Switch to a different voice (supports friendly names like "ryan" or "katherine").
|
|
253
310
|
|
|
254
311
|
Args:
|
|
255
|
-
voice_name:
|
|
312
|
+
voice_name: Friendly name (e.g., "ryan") or Piper voice ID
|
|
256
313
|
|
|
257
314
|
Returns:
|
|
258
315
|
Success or error message
|
|
259
316
|
"""
|
|
317
|
+
# Resolve friendly name to Piper ID
|
|
318
|
+
original_name = voice_name
|
|
319
|
+
resolved_name = self._resolve_friendly_name(voice_name)
|
|
320
|
+
|
|
260
321
|
result = await self._run_script(
|
|
261
|
-
self.VOICE_MANAGER_SCRIPT, ["switch",
|
|
322
|
+
self.VOICE_MANAGER_SCRIPT, ["switch", resolved_name, "--silent"]
|
|
262
323
|
)
|
|
324
|
+
|
|
263
325
|
if result and "✅" in result:
|
|
326
|
+
if original_name.lower() != resolved_name.lower():
|
|
327
|
+
return f"✅ Voice switched to: {original_name} ({resolved_name})"
|
|
264
328
|
return f"✅ Voice switched to: {voice_name}"
|
|
265
329
|
return f"❌ Failed to switch voice: {result}"
|
|
266
330
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "agentvibes",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"description": "Now your AI Agents can finally talk back! Professional TTS voice for Claude Code, Claude Desktop (via MCP), and Clawdbot with multi-provider support.",
|
|
6
6
|
"homepage": "https://agentvibes.org",
|
|
7
7
|
"keywords": [
|
|
@@ -27,9 +27,10 @@
|
|
|
27
27
|
"type": "module",
|
|
28
28
|
"main": "src/installer.js",
|
|
29
29
|
"bin": {
|
|
30
|
-
"agentvibes": "bin/
|
|
30
|
+
"agentvibes": "bin/agentvibes.js",
|
|
31
31
|
"agent-vibes": "bin/agent-vibes",
|
|
32
32
|
"agentvibes-mcp-server": "bin/mcp-server.js",
|
|
33
|
+
"agentvibes-voice-browser": "bin/agentvibes-voice-browser.js",
|
|
33
34
|
"test-bmad-pr": "bin/test-bmad-pr"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
@@ -62,6 +63,9 @@
|
|
|
62
63
|
".claude/piper-voices-dir.txt",
|
|
63
64
|
".claude/verbosity.txt",
|
|
64
65
|
".claude/github-star-reminder.txt",
|
|
66
|
+
".claude/audio/voice-samples/",
|
|
67
|
+
"voice-assignments.json",
|
|
68
|
+
".agentvibes/",
|
|
65
69
|
".clawdbot/",
|
|
66
70
|
".mcp.json",
|
|
67
71
|
"setup-windows.ps1",
|
|
@@ -75,6 +79,7 @@
|
|
|
75
79
|
"install-local": "node src/installer.js install",
|
|
76
80
|
"postinstall": "node mcp-server/install-deps.js",
|
|
77
81
|
"install-mcp-deps": "node mcp-server/install-deps.js",
|
|
82
|
+
"voice-browser": "node bin/agentvibes-voice-browser.js",
|
|
78
83
|
"test": "npm run test:syntax && AGENTVIBES_TEST_MODE=true bats test/unit/*.bats && npm run test:coverage",
|
|
79
84
|
"test:syntax": "node -c src/installer.js && node -c mcp-server/install-deps.js",
|
|
80
85
|
"test:bats": "AGENTVIBES_TEST_MODE=true bats test/unit/*.bats",
|
|
@@ -83,6 +88,9 @@
|
|
|
83
88
|
"test:verbose": "AGENTVIBES_TEST_MODE=true bats -t test/unit/*.bats"
|
|
84
89
|
},
|
|
85
90
|
"dependencies": {
|
|
91
|
+
"@inquirer/search": "^3.1.3",
|
|
92
|
+
"agentvibes": "^3.5.9",
|
|
93
|
+
"blessed": "^0.1.81",
|
|
86
94
|
"boxen": "^7.0.0",
|
|
87
95
|
"chalk": "^5.0.0",
|
|
88
96
|
"commander": "^10.0.0",
|
|
@@ -98,6 +106,7 @@
|
|
|
98
106
|
"access": "public"
|
|
99
107
|
},
|
|
100
108
|
"devDependencies": {
|
|
109
|
+
"bats": "^1.13.0",
|
|
101
110
|
"c8": "^10.1.3"
|
|
102
111
|
}
|
|
103
112
|
}
|