agentvibes 5.6.8 → 5.7.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/config.json +2 -0
- 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 +2 -2
- package/.claude/config/background-music-position.txt +0 -1
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-cache-utils.sh +0 -0
- package/.claude/hooks/audio-processor.sh +0 -0
- package/.claude/hooks/background-music-manager.sh +0 -0
- package/.claude/hooks/bmad-party-manager.sh +225 -0
- package/.claude/hooks/bmad-party-speak.sh +0 -0
- package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
- package/.claude/hooks/bmad-speak.sh +0 -0
- package/.claude/hooks/bmad-tts-injector.sh +49 -21
- package/.claude/hooks/bmad-voice-manager.sh +0 -0
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
- package/.claude/hooks/clawdbot-receiver.sh +0 -0
- 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/path-resolver.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 +0 -0
- package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
- package/.claude/hooks/piper-voice-manager.sh +0 -0
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
- package/.claude/hooks/play-tts-enhanced.sh +0 -0
- package/.claude/hooks/play-tts-macos.sh +0 -0
- package/.claude/hooks/play-tts-piper.sh +1 -1
- package/.claude/hooks/play-tts-soprano.sh +0 -0
- package/.claude/hooks/play-tts-ssh-remote.sh +0 -0
- package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
- package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
- package/.claude/hooks/play-tts.sh +4 -0
- package/.claude/hooks/prepare-release.sh +0 -0
- package/.claude/hooks/provider-commands.sh +16 -4
- package/.claude/hooks/provider-manager.sh +38 -0
- package/.claude/hooks/replay-target-audio.sh +0 -0
- package/.claude/hooks/sentiment-manager.sh +0 -0
- package/.claude/hooks/session-start-tts.sh +0 -0
- package/.claude/hooks/soprano-gradio-synth.py +0 -0
- package/.claude/hooks/speed-manager.sh +0 -0
- package/.claude/hooks/stop-tts.sh +0 -0
- package/.claude/hooks/stop.sh +38 -0
- package/.claude/hooks/termux-installer.sh +0 -0
- package/.claude/hooks/translate-manager.sh +0 -0
- package/.claude/hooks/translator.py +0 -0
- 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 +50 -2
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/play-tts.ps1 +34 -1
- package/.claude/hooks-windows/tts-watcher.ps1 +122 -0
- package/.claude/piper-voices-dir.txt +1 -0
- package/.clawdbot/README.md +105 -105
- package/.mcp.json +14 -5
- package/README.md +10 -2
- package/RELEASE_NOTES.md +61 -0
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +39 -39
- package/bin/agentvibes-voice-browser.js +59 -4
- package/bin/agentvibes.js +0 -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 +177 -130
- package/mcp-server/server.py +1797 -1787
- package/mcp-server/test_server.py +0 -0
- package/package.json +1 -1
- package/src/bmad-detector.js +85 -71
- package/src/cli/list-personalities.js +110 -110
- package/src/cli/list-voices.js +114 -114
- package/src/commands/bmad-voices.js +394 -394
- package/src/commands/install-mcp.js +476 -476
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/tabs/help-tab.js +314 -314
- package/src/console/tabs/music-tab.js +18 -2
- package/src/console/tabs/readme-tab.js +272 -272
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/notice.js +55 -55
- package/src/console/widgets/personality-picker.js +213 -213
- package/src/i18n/de.js +202 -202
- package/src/i18n/es.js +202 -202
- package/src/i18n/fr.js +202 -202
- package/src/i18n/hi.js +202 -202
- package/src/i18n/ja.js +202 -202
- package/src/i18n/ko.js +202 -202
- package/src/i18n/pt.js +202 -202
- package/src/i18n/strings.js +54 -54
- package/src/i18n/zh-CN.js +202 -202
- package/src/installer/language-screen.js +31 -31
- package/src/installer/music-file-input.js +304 -304
- package/src/installer.js +70 -7
- package/src/services/agent-voice-store.js +59 -12
- package/src/services/config-service.js +264 -264
- package/src/services/language-service.js +47 -47
- package/src/services/provider-service.js +143 -143
- package/src/utils/audio-duration-validator.js +298 -298
- package/src/utils/audio-format-validator.js +277 -277
- package/src/utils/dependency-checker.js +469 -469
- package/src/utils/file-ownership-verifier.js +358 -358
- package/src/utils/list-formatter.js +194 -194
- package/src/utils/music-file-validator.js +285 -285
- package/src/utils/preview-list-prompt.js +136 -136
- package/src/utils/secure-music-storage.js +412 -412
- package/templates/agentvibes-receiver.sh +0 -0
- package/templates/audio/welcome-music.mp3 +0 -0
- package/.claude/hooks/play-tts-agentvibes-receiver.sh +0 -1
|
File without changes
|
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": "5.
|
|
4
|
+
"version": "5.7.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": [
|
package/src/bmad-detector.js
CHANGED
|
@@ -1,71 +1,85 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detect BMAD installation and version
|
|
8
|
+
* @param {string} targetDir - Directory to check
|
|
9
|
+
* @returns {Promise<Object>} Detection result with version info
|
|
10
|
+
*/
|
|
11
|
+
export async function detectBMAD(targetDir) {
|
|
12
|
+
// Check v6 first (newer version).
|
|
13
|
+
// Search order: project-local variants first, then home-dir (_bmad is the current BMAD installer default)
|
|
14
|
+
const homeDir = os.homedir();
|
|
15
|
+
const v6Candidates = [
|
|
16
|
+
// Project-local checks first
|
|
17
|
+
{ manifest: path.join(targetDir, '.bmad/_cfg/manifest.yaml'), bmadPath: '.bmad' },
|
|
18
|
+
{ manifest: path.join(targetDir, 'bmad/_cfg/manifest.yaml'), bmadPath: 'bmad' },
|
|
19
|
+
{ manifest: path.join(targetDir, '_bmad/_cfg/manifest.yaml'), bmadPath: '_bmad' },
|
|
20
|
+
{ manifest: path.join(targetDir, '_bmad/_config/manifest.yaml'), bmadPath: '_bmad' },
|
|
21
|
+
// Home-dir installs (global BMAD not inside a project) — detected but NOT injected
|
|
22
|
+
{ manifest: path.join(homeDir, '_bmad/_config/manifest.yaml'), bmadPath: '_bmad', root: homeDir, isGlobal: true },
|
|
23
|
+
{ manifest: path.join(homeDir, '_bmad/_cfg/manifest.yaml'), bmadPath: '_bmad', root: homeDir, isGlobal: true },
|
|
24
|
+
{ manifest: path.join(homeDir, '.bmad/_cfg/manifest.yaml'), bmadPath: '.bmad', root: homeDir, isGlobal: true },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
let v6Manifest = null;
|
|
28
|
+
let bmadPath = null;
|
|
29
|
+
let bmadRoot = targetDir;
|
|
30
|
+
let isGlobal = false;
|
|
31
|
+
|
|
32
|
+
for (const candidate of v6Candidates) {
|
|
33
|
+
try {
|
|
34
|
+
await fs.access(candidate.manifest);
|
|
35
|
+
v6Manifest = candidate.manifest;
|
|
36
|
+
bmadPath = candidate.bmadPath;
|
|
37
|
+
bmadRoot = candidate.root ?? targetDir;
|
|
38
|
+
isGlobal = candidate.isGlobal ?? false;
|
|
39
|
+
break;
|
|
40
|
+
} catch { /* try next */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (bmadPath) {
|
|
44
|
+
try {
|
|
45
|
+
const manifestContent = await fs.readFile(v6Manifest, 'utf8');
|
|
46
|
+
const manifest = yaml.load(manifestContent);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
version: 6,
|
|
50
|
+
detailedVersion: manifest.installation?.version || '6.0.0-alpha.x',
|
|
51
|
+
manifestPath: v6Manifest,
|
|
52
|
+
configPath: path.join(bmadRoot, bmadPath, 'core/config.yaml'),
|
|
53
|
+
bmadPath: path.join(bmadRoot, bmadPath),
|
|
54
|
+
installed: true,
|
|
55
|
+
isGlobal,
|
|
56
|
+
};
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check v4 (legacy)
|
|
61
|
+
const v4Manifest = path.join(targetDir, '.bmad-core/install-manifest.yaml');
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(v4Manifest);
|
|
64
|
+
return {
|
|
65
|
+
version: 4,
|
|
66
|
+
detailedVersion: '4.x',
|
|
67
|
+
manifestPath: v4Manifest,
|
|
68
|
+
configPath: path.join(targetDir, '.bmad-core/config.yaml'),
|
|
69
|
+
bmadPath: path.join(targetDir, '.bmad-core'),
|
|
70
|
+
installed: true
|
|
71
|
+
};
|
|
72
|
+
} catch {}
|
|
73
|
+
|
|
74
|
+
// Not installed
|
|
75
|
+
return { version: null, installed: false };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get BMAD configuration file path for detected version
|
|
80
|
+
* @param {Object} detection - Result from detectBMAD()
|
|
81
|
+
* @returns {string|null} Path to config.yaml or null
|
|
82
|
+
*/
|
|
83
|
+
export function getBMADConfigPath(detection) {
|
|
84
|
+
return detection.installed ? detection.configPath : null;
|
|
85
|
+
}
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Personality List Display - Beautiful multi-column personality listing
|
|
4
|
-
* Called by personality-manager.sh to display personalities with boxen formatting
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { formatPersonalitiesList } from '../utils/list-formatter.js';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import os from 'os';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get personality description from markdown file
|
|
14
|
-
*/
|
|
15
|
-
function getPersonalityDescription(filePath) {
|
|
16
|
-
try {
|
|
17
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
18
|
-
|
|
19
|
-
// Try to extract description from frontmatter or first paragraph
|
|
20
|
-
const descMatch = content.match(/description:\s*(.+)/i);
|
|
21
|
-
if (descMatch) {
|
|
22
|
-
return descMatch[1].trim();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Try to get first line after frontmatter
|
|
26
|
-
const lines = content.split('\n');
|
|
27
|
-
let inFrontmatter = false;
|
|
28
|
-
let frontmatterCount = 0;
|
|
29
|
-
|
|
30
|
-
for (const line of lines) {
|
|
31
|
-
if (line.trim() === '---') {
|
|
32
|
-
frontmatterCount++;
|
|
33
|
-
inFrontmatter = frontmatterCount === 1;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!inFrontmatter && frontmatterCount >= 2 && line.trim()) {
|
|
38
|
-
// First non-empty line after frontmatter
|
|
39
|
-
return line.trim().replace(/^#+\s*/, '').substring(0, 50);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return '';
|
|
44
|
-
} catch (error) {
|
|
45
|
-
return '';
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Get all personalities from directory
|
|
51
|
-
*/
|
|
52
|
-
function getPersonalities(personalitiesDir, currentPersonality) {
|
|
53
|
-
const personalities = [];
|
|
54
|
-
|
|
55
|
-
if (!fs.existsSync(personalitiesDir)) {
|
|
56
|
-
return personalities;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const files = fs.readdirSync(personalitiesDir);
|
|
60
|
-
for (const file of files) {
|
|
61
|
-
if (file.endsWith('.md')) {
|
|
62
|
-
const name = path.basename(file, '.md');
|
|
63
|
-
const filePath = path.join(personalitiesDir, file);
|
|
64
|
-
const description = getPersonalityDescription(filePath);
|
|
65
|
-
|
|
66
|
-
personalities.push({
|
|
67
|
-
name,
|
|
68
|
-
description,
|
|
69
|
-
current: name === currentPersonality
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Add special 'random' option
|
|
75
|
-
personalities.push({
|
|
76
|
-
name: 'random',
|
|
77
|
-
description: 'Picks randomly each time',
|
|
78
|
-
current: currentPersonality === 'random'
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return personalities.sort((a, b) => {
|
|
82
|
-
// Keep 'random' at the end
|
|
83
|
-
if (a.name === 'random') return 1;
|
|
84
|
-
if (b.name === 'random') return -1;
|
|
85
|
-
return a.name.localeCompare(b.name);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Main function
|
|
91
|
-
*/
|
|
92
|
-
function main() {
|
|
93
|
-
const args = process.argv.slice(2);
|
|
94
|
-
|
|
95
|
-
// Parse arguments
|
|
96
|
-
const personalitiesDir = args[0] || path.join(os.homedir(), '.claude', 'personalities');
|
|
97
|
-
const currentPersonality = args[1] || 'normal';
|
|
98
|
-
|
|
99
|
-
const personalities = getPersonalities(personalitiesDir, currentPersonality);
|
|
100
|
-
|
|
101
|
-
// Display with boxen
|
|
102
|
-
const output = formatPersonalitiesList(personalities, {
|
|
103
|
-
columns: 2,
|
|
104
|
-
showUsage: true
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
console.log(output);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Personality List Display - Beautiful multi-column personality listing
|
|
4
|
+
* Called by personality-manager.sh to display personalities with boxen formatting
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { formatPersonalitiesList } from '../utils/list-formatter.js';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get personality description from markdown file
|
|
14
|
+
*/
|
|
15
|
+
function getPersonalityDescription(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
18
|
+
|
|
19
|
+
// Try to extract description from frontmatter or first paragraph
|
|
20
|
+
const descMatch = content.match(/description:\s*(.+)/i);
|
|
21
|
+
if (descMatch) {
|
|
22
|
+
return descMatch[1].trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Try to get first line after frontmatter
|
|
26
|
+
const lines = content.split('\n');
|
|
27
|
+
let inFrontmatter = false;
|
|
28
|
+
let frontmatterCount = 0;
|
|
29
|
+
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
if (line.trim() === '---') {
|
|
32
|
+
frontmatterCount++;
|
|
33
|
+
inFrontmatter = frontmatterCount === 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!inFrontmatter && frontmatterCount >= 2 && line.trim()) {
|
|
38
|
+
// First non-empty line after frontmatter
|
|
39
|
+
return line.trim().replace(/^#+\s*/, '').substring(0, 50);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return '';
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get all personalities from directory
|
|
51
|
+
*/
|
|
52
|
+
function getPersonalities(personalitiesDir, currentPersonality) {
|
|
53
|
+
const personalities = [];
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(personalitiesDir)) {
|
|
56
|
+
return personalities;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const files = fs.readdirSync(personalitiesDir);
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
if (file.endsWith('.md')) {
|
|
62
|
+
const name = path.basename(file, '.md');
|
|
63
|
+
const filePath = path.join(personalitiesDir, file);
|
|
64
|
+
const description = getPersonalityDescription(filePath);
|
|
65
|
+
|
|
66
|
+
personalities.push({
|
|
67
|
+
name,
|
|
68
|
+
description,
|
|
69
|
+
current: name === currentPersonality
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add special 'random' option
|
|
75
|
+
personalities.push({
|
|
76
|
+
name: 'random',
|
|
77
|
+
description: 'Picks randomly each time',
|
|
78
|
+
current: currentPersonality === 'random'
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return personalities.sort((a, b) => {
|
|
82
|
+
// Keep 'random' at the end
|
|
83
|
+
if (a.name === 'random') return 1;
|
|
84
|
+
if (b.name === 'random') return -1;
|
|
85
|
+
return a.name.localeCompare(b.name);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Main function
|
|
91
|
+
*/
|
|
92
|
+
function main() {
|
|
93
|
+
const args = process.argv.slice(2);
|
|
94
|
+
|
|
95
|
+
// Parse arguments
|
|
96
|
+
const personalitiesDir = args[0] || path.join(os.homedir(), '.claude', 'personalities');
|
|
97
|
+
const currentPersonality = args[1] || 'normal';
|
|
98
|
+
|
|
99
|
+
const personalities = getPersonalities(personalitiesDir, currentPersonality);
|
|
100
|
+
|
|
101
|
+
// Display with boxen
|
|
102
|
+
const output = formatPersonalitiesList(personalities, {
|
|
103
|
+
columns: 2,
|
|
104
|
+
showUsage: true
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
console.log(output);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|
package/src/cli/list-voices.js
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Voice List Display - Beautiful multi-column voice listing
|
|
4
|
-
* Called by voice-manager.sh to display voices with boxen formatting
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { formatVoicesList } from '../utils/list-formatter.js';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import { execFileSync } from 'child_process';
|
|
11
|
-
import os from 'os';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Get Piper voices from voice directory
|
|
15
|
-
*/
|
|
16
|
-
function getPiperVoices(voiceDir, currentVoice) {
|
|
17
|
-
const voices = [];
|
|
18
|
-
|
|
19
|
-
if (!fs.existsSync(voiceDir)) {
|
|
20
|
-
return voices;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const files = fs.readdirSync(voiceDir);
|
|
24
|
-
for (const file of files) {
|
|
25
|
-
if (file.endsWith('.onnx')) {
|
|
26
|
-
const voiceName = path.basename(file, '.onnx');
|
|
27
|
-
voices.push({
|
|
28
|
-
name: voiceName,
|
|
29
|
-
lang: extractLanguage(voiceName),
|
|
30
|
-
current: voiceName === currentVoice
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return voices.sort((a, b) => a.name.localeCompare(b.name));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Get macOS voices using say -v ?
|
|
40
|
-
*/
|
|
41
|
-
function getMacOSVoices(currentVoice) {
|
|
42
|
-
const voices = [];
|
|
43
|
-
|
|
44
|
-
if (os.platform() !== 'darwin') {
|
|
45
|
-
return voices;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const output = execFileSync('say', ['-v', '?'], { encoding: 'utf8' }); // NOSONAR - Safe: checking macOS say voices from system PATH
|
|
50
|
-
const lines = output.split('\n');
|
|
51
|
-
|
|
52
|
-
for (const line of lines) {
|
|
53
|
-
if (!line.trim()) continue;
|
|
54
|
-
|
|
55
|
-
const parts = line.trim().split(/\s+/);
|
|
56
|
-
if (parts.length >= 2) {
|
|
57
|
-
const voiceName = parts[0];
|
|
58
|
-
const lang = parts[1];
|
|
59
|
-
|
|
60
|
-
voices.push({
|
|
61
|
-
name: voiceName,
|
|
62
|
-
lang,
|
|
63
|
-
current: voiceName === currentVoice
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
// say command failed
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return voices;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Extract language code from voice name
|
|
76
|
-
*/
|
|
77
|
-
function extractLanguage(voiceName) {
|
|
78
|
-
const match = voiceName.match(/^([a-z]{2}_[A-Z]{2})/);
|
|
79
|
-
return match ? match[1] : '';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Main function
|
|
84
|
-
*/
|
|
85
|
-
function main() {
|
|
86
|
-
const args = process.argv.slice(2);
|
|
87
|
-
|
|
88
|
-
// Parse arguments
|
|
89
|
-
const provider = args[0] || 'piper';
|
|
90
|
-
const currentVoice = args[1] || '';
|
|
91
|
-
const voiceDir = args[2] || '';
|
|
92
|
-
|
|
93
|
-
let voices = [];
|
|
94
|
-
let providerName = 'Piper TTS';
|
|
95
|
-
|
|
96
|
-
if (provider === 'piper') {
|
|
97
|
-
voices = getPiperVoices(voiceDir, currentVoice);
|
|
98
|
-
providerName = 'Piper TTS';
|
|
99
|
-
} else if (provider === 'macos') {
|
|
100
|
-
voices = getMacOSVoices(currentVoice);
|
|
101
|
-
providerName = 'macOS TTS';
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Display with boxen
|
|
105
|
-
const output = formatVoicesList(voices, {
|
|
106
|
-
provider: providerName,
|
|
107
|
-
columns: 2,
|
|
108
|
-
showUsage: true
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
console.log(output);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Voice List Display - Beautiful multi-column voice listing
|
|
4
|
+
* Called by voice-manager.sh to display voices with boxen formatting
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { formatVoicesList } from '../utils/list-formatter.js';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { execFileSync } from 'child_process';
|
|
11
|
+
import os from 'os';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get Piper voices from voice directory
|
|
15
|
+
*/
|
|
16
|
+
function getPiperVoices(voiceDir, currentVoice) {
|
|
17
|
+
const voices = [];
|
|
18
|
+
|
|
19
|
+
if (!fs.existsSync(voiceDir)) {
|
|
20
|
+
return voices;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const files = fs.readdirSync(voiceDir);
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
if (file.endsWith('.onnx')) {
|
|
26
|
+
const voiceName = path.basename(file, '.onnx');
|
|
27
|
+
voices.push({
|
|
28
|
+
name: voiceName,
|
|
29
|
+
lang: extractLanguage(voiceName),
|
|
30
|
+
current: voiceName === currentVoice
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return voices.sort((a, b) => a.name.localeCompare(b.name));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get macOS voices using say -v ?
|
|
40
|
+
*/
|
|
41
|
+
function getMacOSVoices(currentVoice) {
|
|
42
|
+
const voices = [];
|
|
43
|
+
|
|
44
|
+
if (os.platform() !== 'darwin') {
|
|
45
|
+
return voices;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const output = execFileSync('say', ['-v', '?'], { encoding: 'utf8' }); // NOSONAR - Safe: checking macOS say voices from system PATH
|
|
50
|
+
const lines = output.split('\n');
|
|
51
|
+
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
if (!line.trim()) continue;
|
|
54
|
+
|
|
55
|
+
const parts = line.trim().split(/\s+/);
|
|
56
|
+
if (parts.length >= 2) {
|
|
57
|
+
const voiceName = parts[0];
|
|
58
|
+
const lang = parts[1];
|
|
59
|
+
|
|
60
|
+
voices.push({
|
|
61
|
+
name: voiceName,
|
|
62
|
+
lang,
|
|
63
|
+
current: voiceName === currentVoice
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// say command failed
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return voices;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extract language code from voice name
|
|
76
|
+
*/
|
|
77
|
+
function extractLanguage(voiceName) {
|
|
78
|
+
const match = voiceName.match(/^([a-z]{2}_[A-Z]{2})/);
|
|
79
|
+
return match ? match[1] : '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Main function
|
|
84
|
+
*/
|
|
85
|
+
function main() {
|
|
86
|
+
const args = process.argv.slice(2);
|
|
87
|
+
|
|
88
|
+
// Parse arguments
|
|
89
|
+
const provider = args[0] || 'piper';
|
|
90
|
+
const currentVoice = args[1] || '';
|
|
91
|
+
const voiceDir = args[2] || '';
|
|
92
|
+
|
|
93
|
+
let voices = [];
|
|
94
|
+
let providerName = 'Piper TTS';
|
|
95
|
+
|
|
96
|
+
if (provider === 'piper') {
|
|
97
|
+
voices = getPiperVoices(voiceDir, currentVoice);
|
|
98
|
+
providerName = 'Piper TTS';
|
|
99
|
+
} else if (provider === 'macos') {
|
|
100
|
+
voices = getMacOSVoices(currentVoice);
|
|
101
|
+
providerName = 'macOS TTS';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Display with boxen
|
|
105
|
+
const output = formatVoicesList(voices, {
|
|
106
|
+
provider: providerName,
|
|
107
|
+
columns: 2,
|
|
108
|
+
showUsage: true
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
console.log(output);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
main();
|