agentvibes 5.9.0 → 5.11.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 (70) hide show
  1. package/.agentvibes/config.json +17 -12
  2. package/.agentvibes/install-manifest.json +98 -86
  3. package/.claude/audio/ui/CREDITS.txt +16 -0
  4. package/.claude/audio/ui/bling-success.wav +0 -0
  5. package/.claude/commands/agent-vibes/receiver.md +64 -0
  6. package/.claude/config/audio-effects.cfg +3 -3
  7. package/.claude/config/personality.txt +1 -0
  8. package/.claude/config/reverb-level.txt +1 -1
  9. package/.claude/github-star-reminder.txt +1 -1
  10. package/.claude/hooks/audio-processor.sh +57 -18
  11. package/.claude/hooks/kokoro-installer.sh +117 -0
  12. package/.claude/hooks/kokoro-server.py +219 -0
  13. package/.claude/hooks/kokoro-tts.py +141 -0
  14. package/.claude/hooks/piper-download-voices.sh +39 -16
  15. package/.claude/hooks/piper-installer.sh +11 -6
  16. package/.claude/hooks/piper-voice-manager.sh +6 -6
  17. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +3 -1
  18. package/.claude/hooks/play-tts-elevenlabs.sh +360 -0
  19. package/.claude/hooks/play-tts-kokoro.sh +127 -0
  20. package/.claude/hooks/play-tts-piper.sh +20 -13
  21. package/.claude/hooks/play-tts-ssh-remote.sh +9 -2
  22. package/.claude/hooks/play-tts-termux-ssh.sh +3 -1
  23. package/.claude/hooks/play-tts.sh +78 -2
  24. package/.claude/hooks/provider-commands.sh +71 -22
  25. package/.claude/hooks/provider-manager.sh +15 -7
  26. package/.claude/hooks/voice-manager.sh +6 -0
  27. package/.claude/hooks-windows/kokoro-server.py +219 -0
  28. package/.claude/hooks-windows/kokoro-tts.py +107 -0
  29. package/.claude/hooks-windows/play-tts-kokoro.ps1 +191 -0
  30. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +22 -16
  31. package/.claude/hooks-windows/play-tts.ps1 +29 -1
  32. package/README.md +12 -86
  33. package/RELEASE_NOTES.md +60 -0
  34. package/mcp-server/server.py +17 -7
  35. package/package.json +4 -3
  36. package/src/commands/install-mcp.js +730 -476
  37. package/src/console/app.js +28 -12
  38. package/src/console/audio-env.js +85 -1
  39. package/src/console/navigation.js +4 -0
  40. package/src/console/tabs/agents-tab.js +18 -12
  41. package/src/console/tabs/music-tab.js +7 -25
  42. package/src/console/tabs/receiver-tab.js +13 -13
  43. package/src/console/tabs/settings-tab.js +2 -2
  44. package/src/console/tabs/setup-tab.js +1708 -124
  45. package/src/console/tabs/voices-tab.js +76 -92
  46. package/src/console/widgets/format-utils.js +14 -2
  47. package/src/console/widgets/help-bar.js +55 -0
  48. package/src/console/widgets/personality-picker.js +2 -2
  49. package/src/console/widgets/reverb-picker.js +429 -41
  50. package/src/console/widgets/track-picker.js +60 -51
  51. package/src/i18n/de.js +204 -203
  52. package/src/i18n/en.js +1 -0
  53. package/src/i18n/es.js +204 -203
  54. package/src/i18n/fr.js +204 -203
  55. package/src/i18n/hi.js +204 -203
  56. package/src/i18n/ja.js +204 -203
  57. package/src/i18n/ko.js +204 -203
  58. package/src/i18n/pt.js +204 -203
  59. package/src/i18n/strings.js +54 -54
  60. package/src/i18n/zh-CN.js +204 -203
  61. package/src/installer.js +127 -34
  62. package/src/services/provider-service.js +178 -143
  63. package/src/services/provider-voice-catalog.js +126 -0
  64. package/src/services/tts-engine-service.js +53 -4
  65. package/src/utils/audio-duration-validator.js +341 -298
  66. package/src/utils/list-formatter.js +200 -194
  67. package/src/utils/platform-resolver.js +369 -0
  68. package/src/utils/preview-list-prompt.js +8 -0
  69. package/src/utils/provider-validator.js +79 -9
  70. package/templates/agentvibes-receiver.sh +6 -2
@@ -1,194 +1,200 @@
1
- #!/usr/bin/env node
2
- /**
3
- * AgentVibes List Formatter
4
- *
5
- * Beautiful multi-column boxen displays for voices and personalities
6
- * Inspired by BMAD-METHOD's installer UX
7
- */
8
-
9
- import chalk from 'chalk';
10
- import boxen from 'boxen';
11
-
12
- /**
13
- * Format items into multi-column layout
14
- * @param {Array} items - Array of strings or objects with {name, description, current}
15
- * @param {Object} options - Formatting options
16
- * @returns {string} Formatted multi-column text
17
- */
18
- export function formatColumns(items, options = {}) {
19
- const {
20
- columns = 2,
21
- columnWidth = 35,
22
- highlightChar = '▶',
23
- indent = ' '
24
- } = options;
25
-
26
- const rows = [];
27
- const itemsPerRow = columns;
28
-
29
- for (let i = 0; i < items.length; i += itemsPerRow) {
30
- const rowItems = items.slice(i, i + itemsPerRow);
31
- const row = rowItems.map((item, idx) => {
32
- const isObject = typeof item === 'object';
33
- const name = isObject ? item.name : item;
34
- const desc = isObject ? item.description : '';
35
- const isCurrent = isObject ? item.current : false;
36
-
37
- // Format item
38
- let formatted = isCurrent ? `${highlightChar} ${name}` : ` ${name}`;
39
-
40
- if (desc) {
41
- formatted = chalk.cyan(formatted) + chalk.gray(` ${desc}`);
42
- } else if (isCurrent) {
43
- formatted = chalk.cyan(formatted);
44
- }
45
-
46
- // Pad to column width (accounting for ANSI codes)
47
- const plainLength = (isCurrent ? `${highlightChar} ${name}` : ` ${name}`).length + (desc ? ` ${desc}`.length : 0);
48
- const padding = Math.max(0, columnWidth - plainLength);
49
-
50
- return formatted + ' '.repeat(padding);
51
- });
52
-
53
- rows.push(indent + row.join(' '));
54
- }
55
-
56
- return rows.join('\n');
57
- }
58
-
59
- /**
60
- * Format voices list with boxen
61
- * @param {Array} voices - Array of voice objects {name, lang, current}
62
- * @param {Object} options - Display options
63
- * @returns {string} Formatted boxen output
64
- */
65
- export function formatVoicesList(voices, options = {}) {
66
- const {
67
- provider = 'Piper TTS',
68
- title = '🎤 Available Voices',
69
- columns = 2,
70
- showUsage = true
71
- } = options;
72
-
73
- if (voices.length === 0) {
74
- const content = chalk.yellow('No voices found') + '\n\n' +
75
- chalk.gray('Download voices with:\n') +
76
- chalk.cyan(' /agent-vibes:provider download <voice-name>');
77
-
78
- return boxen(content, {
79
- padding: 1,
80
- margin: 1,
81
- borderStyle: 'round',
82
- borderColor: 'yellow',
83
- title: chalk.bold(title),
84
- titleAlignment: 'center'
85
- });
86
- }
87
-
88
- // Format voices
89
- const formattedItems = voices.map(v => ({
90
- name: v.name,
91
- description: v.lang || '',
92
- current: v.current || false
93
- }));
94
-
95
- let content = chalk.bold(`${provider}\n\n`);
96
- content += formatColumns(formattedItems, { columns });
97
-
98
- if (showUsage) {
99
- content += '\n\n' + chalk.gray('─'.repeat(60)) + '\n';
100
- content += chalk.dim('Switch voice: ') + chalk.cyan('/agent-vibes:switch <voice-name>') + '\n';
101
- content += chalk.dim('Preview voice: ') + chalk.cyan('/agent-vibes:preview <voice-name>');
102
- }
103
-
104
- return boxen(content, {
105
- padding: 1,
106
- margin: 1,
107
- borderStyle: 'round',
108
- borderColor: 'cyan',
109
- title: chalk.bold(title),
110
- titleAlignment: 'center'
111
- });
112
- }
113
-
114
- /**
115
- * Format personalities list with boxen
116
- * @param {Array} personalities - Array of personality objects {name, description, current}
117
- * @param {Object} options - Display options
118
- * @returns {string} Formatted boxen output
119
- */
120
- export function formatPersonalitiesList(personalities, options = {}) {
121
- const {
122
- title = '🎭 Available Personalities',
123
- columns = 2,
124
- showUsage = true
125
- } = options;
126
-
127
- if (personalities.length === 0) {
128
- const content = chalk.yellow('No personalities found') + '\n\n' +
129
- chalk.gray('Add a personality with:\n') +
130
- chalk.cyan(' /agent-vibes:personality add <name>');
131
-
132
- return boxen(content, {
133
- padding: 1,
134
- margin: 1,
135
- borderStyle: 'round',
136
- borderColor: 'yellow',
137
- title: chalk.bold(title),
138
- titleAlignment: 'center'
139
- });
140
- }
141
-
142
- let content = formatColumns(personalities, { columns, columnWidth: 40 });
143
-
144
- if (showUsage) {
145
- content += '\n\n' + chalk.gray('─'.repeat(60)) + '\n';
146
- content += chalk.dim('Set personality: ') + chalk.cyan('/agent-vibes:personality <name>') + '\n';
147
- content += chalk.dim('Add personality: ') + chalk.cyan('/agent-vibes:personality add <name>') + '\n';
148
- content += chalk.dim('Edit personality: ') + chalk.cyan('/agent-vibes:personality edit <name>');
149
- }
150
-
151
- return boxen(content, {
152
- padding: 1,
153
- margin: 1,
154
- borderStyle: 'round',
155
- borderColor: 'magenta',
156
- title: chalk.bold(title),
157
- titleAlignment: 'center'
158
- });
159
- }
160
-
161
- /**
162
- * Format generic list with boxen
163
- * @param {Array} items - Array of items (strings or objects)
164
- * @param {Object} options - Display options
165
- * @returns {string} Formatted boxen output
166
- */
167
- export function formatList(items, options = {}) {
168
- const {
169
- title = 'Items',
170
- icon = '📋',
171
- columns = 2,
172
- borderColor = 'blue',
173
- showCount = true
174
- } = options;
175
-
176
- const titleText = icon ? `${icon} ${title}` : title;
177
- const countText = showCount ? chalk.gray(` (${items.length})`) : '';
178
-
179
- let content = '';
180
- if (showCount) {
181
- content = chalk.bold(`Total: ${items.length} items\n\n`);
182
- }
183
-
184
- content += formatColumns(items, { columns });
185
-
186
- return boxen(content, {
187
- padding: 1,
188
- margin: 1,
189
- borderStyle: 'round',
190
- borderColor,
191
- title: chalk.bold(titleText) + countText,
192
- titleAlignment: 'center'
193
- });
194
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentVibes List Formatter
4
+ *
5
+ * Beautiful multi-column boxen displays for voices and personalities
6
+ * Inspired by BMAD-METHOD's installer UX
7
+ */
8
+
9
+ import chalk from 'chalk';
10
+ import boxen from 'boxen';
11
+
12
+ /**
13
+ * Format items into multi-column layout
14
+ * @param {Array} items - Array of strings or objects with {name, description, current}
15
+ * @param {Object} options - Formatting options
16
+ * @returns {string} Formatted multi-column text
17
+ */
18
+ export function formatColumns(items, options = {}) {
19
+ const {
20
+ columns = 2,
21
+ columnWidth = 35,
22
+ highlightChar = '▶',
23
+ indent = ' '
24
+ } = options;
25
+
26
+ const rows = [];
27
+ const itemsPerRow = columns;
28
+
29
+ for (let i = 0; i < items.length; i += itemsPerRow) {
30
+ const rowItems = items.slice(i, i + itemsPerRow);
31
+ const row = rowItems.map((item, idx) => {
32
+ const isObject = typeof item === 'object';
33
+ const name = isObject ? item.name : item;
34
+ const desc = isObject ? item.description : '';
35
+ const isCurrent = isObject ? item.current : false;
36
+
37
+ // Format item
38
+ let formatted = isCurrent ? `${highlightChar} ${name}` : ` ${name}`;
39
+
40
+ if (desc) {
41
+ formatted = chalk.cyan(formatted) + chalk.gray(` ${desc}`);
42
+ } else if (isCurrent) {
43
+ formatted = chalk.cyan(formatted);
44
+ }
45
+
46
+ // Pad to column width (accounting for ANSI codes)
47
+ const plainLength = (isCurrent ? `${highlightChar} ${name}` : ` ${name}`).length + (desc ? ` ${desc}`.length : 0);
48
+ const padding = Math.max(0, columnWidth - plainLength);
49
+
50
+ return formatted + ' '.repeat(padding);
51
+ });
52
+
53
+ rows.push(indent + row.join(' '));
54
+ }
55
+
56
+ return rows.join('\n');
57
+ }
58
+
59
+ /**
60
+ * Format voices list with boxen
61
+ * @param {Array} voices - Array of voice objects {name, lang, current}
62
+ * @param {Object} options - Display options
63
+ * @returns {string} Formatted boxen output
64
+ */
65
+ export function formatVoicesList(voices, options = {}) {
66
+ const {
67
+ provider = 'Piper TTS',
68
+ title = '🎤 Available Voices',
69
+ columns = 2,
70
+ showUsage = true
71
+ } = options;
72
+
73
+ if (voices.length === 0) {
74
+ const content =
75
+ chalk.yellow('No voices downloaded yet') + '\n\n' +
76
+ chalk.white('AgentVibes includes 900+ free voices.\n') +
77
+ chalk.white('Get started with a default voice:\n\n') +
78
+ chalk.cyan(' /agent-vibes:add\n\n') +
79
+ chalk.gray('Or browse the full voice library:\n') +
80
+ chalk.cyan(' /agent-vibes:list\n\n') +
81
+ chalk.dim('Piper installed but voices missing? Run:\n') +
82
+ chalk.dim(' /agent-vibes:provider download en_US-lessac-medium');
83
+
84
+ return boxen(content, {
85
+ padding: 1,
86
+ margin: 1,
87
+ borderStyle: 'round',
88
+ borderColor: 'yellow',
89
+ title: chalk.bold(title),
90
+ titleAlignment: 'center'
91
+ });
92
+ }
93
+
94
+ // Format voices
95
+ const formattedItems = voices.map(v => ({
96
+ name: v.name,
97
+ description: v.lang || '',
98
+ current: v.current || false
99
+ }));
100
+
101
+ let content = chalk.bold(`${provider}\n\n`);
102
+ content += formatColumns(formattedItems, { columns });
103
+
104
+ if (showUsage) {
105
+ content += '\n\n' + chalk.gray('─'.repeat(60)) + '\n';
106
+ content += chalk.dim('Switch voice: ') + chalk.cyan('/agent-vibes:switch <voice-name>') + '\n';
107
+ content += chalk.dim('Preview voice: ') + chalk.cyan('/agent-vibes:preview <voice-name>');
108
+ }
109
+
110
+ return boxen(content, {
111
+ padding: 1,
112
+ margin: 1,
113
+ borderStyle: 'round',
114
+ borderColor: 'cyan',
115
+ title: chalk.bold(title),
116
+ titleAlignment: 'center'
117
+ });
118
+ }
119
+
120
+ /**
121
+ * Format personalities list with boxen
122
+ * @param {Array} personalities - Array of personality objects {name, description, current}
123
+ * @param {Object} options - Display options
124
+ * @returns {string} Formatted boxen output
125
+ */
126
+ export function formatPersonalitiesList(personalities, options = {}) {
127
+ const {
128
+ title = '🎭 Available Personalities',
129
+ columns = 2,
130
+ showUsage = true
131
+ } = options;
132
+
133
+ if (personalities.length === 0) {
134
+ const content = chalk.yellow('No personalities found') + '\n\n' +
135
+ chalk.gray('Add a personality with:\n') +
136
+ chalk.cyan(' /agent-vibes:personality add <name>');
137
+
138
+ return boxen(content, {
139
+ padding: 1,
140
+ margin: 1,
141
+ borderStyle: 'round',
142
+ borderColor: 'yellow',
143
+ title: chalk.bold(title),
144
+ titleAlignment: 'center'
145
+ });
146
+ }
147
+
148
+ let content = formatColumns(personalities, { columns, columnWidth: 40 });
149
+
150
+ if (showUsage) {
151
+ content += '\n\n' + chalk.gray('─'.repeat(60)) + '\n';
152
+ content += chalk.dim('Set personality: ') + chalk.cyan('/agent-vibes:personality <name>') + '\n';
153
+ content += chalk.dim('Add personality: ') + chalk.cyan('/agent-vibes:personality add <name>') + '\n';
154
+ content += chalk.dim('Edit personality: ') + chalk.cyan('/agent-vibes:personality edit <name>');
155
+ }
156
+
157
+ return boxen(content, {
158
+ padding: 1,
159
+ margin: 1,
160
+ borderStyle: 'round',
161
+ borderColor: 'magenta',
162
+ title: chalk.bold(title),
163
+ titleAlignment: 'center'
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Format generic list with boxen
169
+ * @param {Array} items - Array of items (strings or objects)
170
+ * @param {Object} options - Display options
171
+ * @returns {string} Formatted boxen output
172
+ */
173
+ export function formatList(items, options = {}) {
174
+ const {
175
+ title = 'Items',
176
+ icon = '📋',
177
+ columns = 2,
178
+ borderColor = 'blue',
179
+ showCount = true
180
+ } = options;
181
+
182
+ const titleText = icon ? `${icon} ${title}` : title;
183
+ const countText = showCount ? chalk.gray(` (${items.length})`) : '';
184
+
185
+ let content = '';
186
+ if (showCount) {
187
+ content = chalk.bold(`Total: ${items.length} items\n\n`);
188
+ }
189
+
190
+ content += formatColumns(items, { columns });
191
+
192
+ return boxen(content, {
193
+ padding: 1,
194
+ margin: 1,
195
+ borderStyle: 'round',
196
+ borderColor,
197
+ title: chalk.bold(titleText) + countText,
198
+ titleAlignment: 'center'
199
+ });
200
+ }