agentvibes 2.0.5 → 2.0.7

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 (104) hide show
  1. package/.claude/commands/agent-vibes/preview.md +5 -4
  2. package/.claude/hooks/language-manager.sh +50 -48
  3. package/.claude/hooks/piper-voice-manager.sh +37 -4
  4. package/.claude/hooks/play-tts-piper.sh +1 -1
  5. package/.claude/hooks/play-tts.sh +29 -127
  6. package/.claude/hooks/provider-commands.sh +41 -2
  7. package/.claude/piper-voices-dir.txt +1 -0
  8. package/README.md +2 -2
  9. package/RELEASE_NOTES.md +202 -0
  10. package/package.json +1 -1
  11. package/src/installer.js +367 -170
  12. package/agentvibes.org/.claude/commands/agent-vibes/add.md +0 -21
  13. package/agentvibes.org/.claude/commands/agent-vibes/agent-vibes.md +0 -68
  14. package/agentvibes.org/.claude/commands/agent-vibes/commands.json +0 -53
  15. package/agentvibes.org/.claude/commands/agent-vibes/get.md +0 -9
  16. package/agentvibes.org/.claude/commands/agent-vibes/list.md +0 -13
  17. package/agentvibes.org/.claude/commands/agent-vibes/personality.md +0 -79
  18. package/agentvibes.org/.claude/commands/agent-vibes/preview.md +0 -16
  19. package/agentvibes.org/.claude/commands/agent-vibes/provider.md +0 -54
  20. package/agentvibes.org/.claude/commands/agent-vibes/replay.md +0 -19
  21. package/agentvibes.org/.claude/commands/agent-vibes/sample.md +0 -12
  22. package/agentvibes.org/.claude/commands/agent-vibes/sentiment.md +0 -52
  23. package/agentvibes.org/.claude/commands/agent-vibes/set-language.md +0 -47
  24. package/agentvibes.org/.claude/commands/agent-vibes/set-pretext.md +0 -65
  25. package/agentvibes.org/.claude/commands/agent-vibes/switch.md +0 -53
  26. package/agentvibes.org/.claude/commands/agent-vibes/update.md +0 -20
  27. package/agentvibes.org/.claude/commands/agent-vibes/version.md +0 -10
  28. package/agentvibes.org/.claude/commands/agent-vibes/whoami.md +0 -7
  29. package/agentvibes.org/.claude/journal/2025-10-07.html +0 -373
  30. package/agentvibes.org/.claude/journal/index.html +0 -91
  31. package/agentvibes.org/.mcp-minimal.json +0 -60
  32. package/agentvibes.org/CHANGELOG.md +0 -56
  33. package/agentvibes.org/README.md +0 -93
  34. package/agentvibes.org/app/(auth)/layout.tsx +0 -15
  35. package/agentvibes.org/app/(auth)/reset-password/page.tsx +0 -45
  36. package/agentvibes.org/app/(auth)/signin/page.tsx +0 -82
  37. package/agentvibes.org/app/(auth)/signup/page.tsx +0 -104
  38. package/agentvibes.org/app/(default)/layout.tsx +0 -31
  39. package/agentvibes.org/app/(default)/page.tsx +0 -20
  40. package/agentvibes.org/app/api/hello/route.ts +0 -3
  41. package/agentvibes.org/app/css/additional-styles/theme.css +0 -82
  42. package/agentvibes.org/app/css/additional-styles/utility-patterns.css +0 -55
  43. package/agentvibes.org/app/css/style.css +0 -100
  44. package/agentvibes.org/app/layout.tsx +0 -63
  45. package/agentvibes.org/components/cta.tsx +0 -58
  46. package/agentvibes.org/components/features.tsx +0 -256
  47. package/agentvibes.org/components/hero-home.tsx +0 -133
  48. package/agentvibes.org/components/modal-video.tsx +0 -137
  49. package/agentvibes.org/components/page-illustration.tsx +0 -55
  50. package/agentvibes.org/components/spotlight.tsx +0 -77
  51. package/agentvibes.org/components/testimonials.tsx +0 -282
  52. package/agentvibes.org/components/ui/footer.tsx +0 -82
  53. package/agentvibes.org/components/ui/header.tsx +0 -53
  54. package/agentvibes.org/components/ui/logo.tsx +0 -10
  55. package/agentvibes.org/components/workflows.tsx +0 -176
  56. package/agentvibes.org/next.config.js +0 -4
  57. package/agentvibes.org/package-lock.json +0 -1974
  58. package/agentvibes.org/package.json +0 -30
  59. package/agentvibes.org/pnpm-lock.yaml +0 -1141
  60. package/agentvibes.org/postcss.config.js +0 -5
  61. package/agentvibes.org/public/audio/02-sarcastic.mp3 +0 -0
  62. package/agentvibes.org/public/audio/03-angry.mp3 +0 -0
  63. package/agentvibes.org/public/audio/04-grandpa.mp3 +0 -0
  64. package/agentvibes.org/public/audio/05-sarcastic-example2.mp3 +0 -0
  65. package/agentvibes.org/public/audio/french-rachel.mp3 +0 -0
  66. package/agentvibes.org/public/audio/spanish-antoni.mp3 +0 -0
  67. package/agentvibes.org/public/favicon.ico +0 -0
  68. package/agentvibes.org/public/fonts/nacelle-italic.woff2 +0 -0
  69. package/agentvibes.org/public/fonts/nacelle-regular.woff2 +0 -0
  70. package/agentvibes.org/public/fonts/nacelle-semibold.woff2 +0 -0
  71. package/agentvibes.org/public/fonts/nacelle-semibolditalic.woff2 +0 -0
  72. package/agentvibes.org/public/images/blurred-shape-gray.svg +0 -1
  73. package/agentvibes.org/public/images/blurred-shape.svg +0 -1
  74. package/agentvibes.org/public/images/client-logo-01.svg +0 -1
  75. package/agentvibes.org/public/images/client-logo-02.svg +0 -1
  76. package/agentvibes.org/public/images/client-logo-03.svg +0 -1
  77. package/agentvibes.org/public/images/client-logo-04.svg +0 -1
  78. package/agentvibes.org/public/images/client-logo-05.svg +0 -1
  79. package/agentvibes.org/public/images/client-logo-06.svg +0 -1
  80. package/agentvibes.org/public/images/client-logo-07.svg +0 -1
  81. package/agentvibes.org/public/images/client-logo-08.svg +0 -1
  82. package/agentvibes.org/public/images/client-logo-09.svg +0 -1
  83. package/agentvibes.org/public/images/features.png +0 -0
  84. package/agentvibes.org/public/images/footer-illustration.svg +0 -1
  85. package/agentvibes.org/public/images/hero-image-01.jpg +0 -0
  86. package/agentvibes.org/public/images/logo.svg +0 -1
  87. package/agentvibes.org/public/images/page-illustration.svg +0 -1
  88. package/agentvibes.org/public/images/secondary-illustration.svg +0 -1
  89. package/agentvibes.org/public/images/testimonial-01.jpg +0 -0
  90. package/agentvibes.org/public/images/testimonial-02.jpg +0 -0
  91. package/agentvibes.org/public/images/testimonial-03.jpg +0 -0
  92. package/agentvibes.org/public/images/testimonial-04.jpg +0 -0
  93. package/agentvibes.org/public/images/testimonial-05.jpg +0 -0
  94. package/agentvibes.org/public/images/testimonial-06.jpg +0 -0
  95. package/agentvibes.org/public/images/testimonial-07.jpg +0 -0
  96. package/agentvibes.org/public/images/testimonial-08.jpg +0 -0
  97. package/agentvibes.org/public/images/testimonial-09.jpg +0 -0
  98. package/agentvibes.org/public/images/workflow-01.png +0 -0
  99. package/agentvibes.org/public/images/workflow-02.png +0 -0
  100. package/agentvibes.org/public/images/workflow-03.png +0 -0
  101. package/agentvibes.org/public/videos/video.mp4 +0 -0
  102. package/agentvibes.org/tsconfig.json +0 -28
  103. package/agentvibes.org/utils/useMasonry.tsx +0 -67
  104. package/agentvibes.org/utils/useMousePosition.tsx +0 -27
package/src/installer.js CHANGED
@@ -68,11 +68,9 @@ async function install(options = {}) {
68
68
  // When running via npx, process.cwd() returns the npm cache directory
69
69
  // Use INIT_CWD (set by npm/npx) to get the actual user's working directory
70
70
  const currentDir = process.env.INIT_CWD || process.cwd();
71
- const targetDir = options.directory || currentDir;
72
71
 
73
72
  console.log(chalk.cyan('\n📍 Installation Details:'));
74
- console.log(chalk.gray(` Current directory: ${currentDir}`));
75
- console.log(chalk.gray(` Install location: ${targetDir}/.claude/ (project-local)`));
73
+ console.log(chalk.gray(` Install location: ${currentDir}/.claude/`));
76
74
  console.log(chalk.gray(` Package version: ${VERSION}`));
77
75
 
78
76
  // Show latest release notes from git log
@@ -96,6 +94,224 @@ async function install(options = {}) {
96
94
  // Git not available or not a git repo - skip release notes
97
95
  }
98
96
 
97
+ // Provider selection prompt
98
+ let selectedProvider = 'piper';
99
+ let elevenLabsKey = process.env.ELEVENLABS_API_KEY;
100
+ let piperVoicesPath = null;
101
+
102
+ if (!options.yes) {
103
+ console.log(chalk.cyan('🎭 Choose Your TTS Provider:\n'));
104
+
105
+ const { provider } = await inquirer.prompt([
106
+ {
107
+ type: 'list',
108
+ name: 'provider',
109
+ message: 'Which TTS provider would you like to use?',
110
+ choices: [
111
+ {
112
+ name: chalk.green('🆓 Piper TTS (Free, Offline)') + chalk.gray(' - 50+ neural voices, no API key needed'),
113
+ value: 'piper',
114
+ },
115
+ {
116
+ name: chalk.cyan('🎤 ElevenLabs (Premium)') + chalk.gray(' - 150+ AI voices, requires API key'),
117
+ value: 'elevenlabs',
118
+ },
119
+ ],
120
+ default: 'piper',
121
+ },
122
+ ]);
123
+
124
+ selectedProvider = provider;
125
+
126
+ // If Piper selected, ask for voice storage location
127
+ if (selectedProvider === 'piper') {
128
+ const homeDir = process.env.HOME || process.env.USERPROFILE;
129
+ const defaultPiperPath = path.join(homeDir, '.claude', 'piper-voices');
130
+
131
+ console.log(chalk.cyan('\n📁 Piper Voice Storage Location:\n'));
132
+ console.log(chalk.gray(' Piper voice models are ~25MB each. They can be stored globally'));
133
+ console.log(chalk.gray(' to be shared across all your projects, or locally per project.\n'));
134
+
135
+ const { piperPath } = await inquirer.prompt([
136
+ {
137
+ type: 'input',
138
+ name: 'piperPath',
139
+ message: 'Where should Piper voice models be downloaded?',
140
+ default: defaultPiperPath,
141
+ validate: (input) => {
142
+ if (!input || input.trim() === '') {
143
+ return 'Please provide a valid path';
144
+ }
145
+ return true;
146
+ },
147
+ },
148
+ ]);
149
+
150
+ piperVoicesPath = piperPath;
151
+ console.log(chalk.green(`✓ Piper voices will be stored in: ${piperVoicesPath}`));
152
+ }
153
+
154
+ // If ElevenLabs selected, handle API key
155
+ if (selectedProvider === 'elevenlabs') {
156
+ if (elevenLabsKey) {
157
+ console.log(chalk.green(`\n✓ ElevenLabs API key detected from environment`));
158
+ console.log(chalk.gray(` Key: ${elevenLabsKey.substring(0, 10)}...`));
159
+
160
+ const { useExisting } = await inquirer.prompt([
161
+ {
162
+ type: 'confirm',
163
+ name: 'useExisting',
164
+ message: 'Use this existing API key?',
165
+ default: true,
166
+ },
167
+ ]);
168
+
169
+ if (!useExisting) {
170
+ elevenLabsKey = null;
171
+ }
172
+ }
173
+
174
+ if (!elevenLabsKey) {
175
+ console.log(chalk.yellow('\n⚠️ ElevenLabs API Key Required'));
176
+ console.log(chalk.gray(' Get your free API key at: https://elevenlabs.io'));
177
+ console.log(chalk.gray(' Free tier: 10,000 characters/month\n'));
178
+
179
+ const { setupMethod } = await inquirer.prompt([
180
+ {
181
+ type: 'list',
182
+ name: 'setupMethod',
183
+ message: 'How would you like to set up your API key?',
184
+ choices: [
185
+ {
186
+ name: 'Add to shell config (recommended)',
187
+ value: 'shell',
188
+ },
189
+ {
190
+ name: 'Enter manually (I\'ll set it up myself later)',
191
+ value: 'manual',
192
+ },
193
+ {
194
+ name: 'Skip (use Piper TTS instead)',
195
+ value: 'skip',
196
+ },
197
+ ],
198
+ },
199
+ ]);
200
+
201
+ if (setupMethod === 'skip') {
202
+ console.log(chalk.yellow('\n→ Switching to Piper TTS (free option)\n'));
203
+ selectedProvider = 'piper';
204
+ } else if (setupMethod === 'manual') {
205
+ const { apiKey } = await inquirer.prompt([
206
+ {
207
+ type: 'input',
208
+ name: 'apiKey',
209
+ message: 'Enter your ElevenLabs API key:',
210
+ validate: (input) => input.length > 0 || 'API key cannot be empty',
211
+ },
212
+ ]);
213
+ elevenLabsKey = apiKey;
214
+ console.log(chalk.yellow('\n⚠️ Remember to add this to your environment variables later:'));
215
+ console.log(chalk.gray(` export ELEVENLABS_API_KEY="${apiKey}"\n`));
216
+ } else if (setupMethod === 'shell') {
217
+ const { apiKey } = await inquirer.prompt([
218
+ {
219
+ type: 'input',
220
+ name: 'apiKey',
221
+ message: 'Enter your ElevenLabs API key:',
222
+ validate: (input) => input.length > 0 || 'API key cannot be empty',
223
+ },
224
+ ]);
225
+ elevenLabsKey = apiKey;
226
+
227
+ // Detect shell
228
+ const shell = process.env.SHELL || '';
229
+ let shellConfig = '';
230
+ let shellName = '';
231
+
232
+ if (shell.includes('zsh')) {
233
+ shellConfig = path.join(process.env.HOME, '.zshrc');
234
+ shellName = 'zsh';
235
+ } else if (shell.includes('bash')) {
236
+ shellConfig = path.join(process.env.HOME, '.bashrc');
237
+ shellName = 'bash';
238
+ } else {
239
+ console.log(chalk.yellow('\n⚠️ Could not detect shell type'));
240
+ console.log(chalk.gray(' Please add manually to your shell config:'));
241
+ console.log(chalk.gray(` export ELEVENLABS_API_KEY="${apiKey}"\n`));
242
+ }
243
+
244
+ if (shellConfig && shellName) {
245
+ console.log(chalk.cyan(`\n🐚 Detected shell: ${shellName}`));
246
+ console.log(chalk.gray(` Config file: ${shellConfig}`));
247
+
248
+ const { confirmShell } = await inquirer.prompt([
249
+ {
250
+ type: 'confirm',
251
+ name: 'confirmShell',
252
+ message: `Add ELEVENLABS_API_KEY to ${shellConfig}?`,
253
+ default: true,
254
+ },
255
+ ]);
256
+
257
+ if (confirmShell) {
258
+ try {
259
+ const configContent = `\n# ElevenLabs API Key for AgentVibes\nexport ELEVENLABS_API_KEY="${apiKey}"\n`;
260
+ await fs.appendFile(shellConfig, configContent);
261
+ console.log(chalk.green(`\n✓ API key added to ${shellConfig}`));
262
+ console.log(chalk.yellow(' Run this to use immediately: ') + chalk.cyan(`source ${shellConfig}`));
263
+ } catch (error) {
264
+ console.log(chalk.red(`\n✗ Failed to write to ${shellConfig}`));
265
+ console.log(chalk.gray(' Please add manually:'));
266
+ console.log(chalk.gray(` export ELEVENLABS_API_KEY="${apiKey}"\n`));
267
+ }
268
+ }
269
+ }
270
+ }
271
+ }
272
+ }
273
+
274
+ console.log(''); // Spacing
275
+ } else {
276
+ console.log(chalk.green('✓ Auto-confirmed (--yes flag)'));
277
+ // Auto-detect provider based on API key
278
+ if (elevenLabsKey) {
279
+ selectedProvider = 'elevenlabs';
280
+ console.log(chalk.green('✓ Using ElevenLabs (API key detected)\n'));
281
+ } else {
282
+ selectedProvider = 'piper';
283
+ console.log(chalk.green('✓ Using Piper TTS (free option)\n'));
284
+ }
285
+ }
286
+
287
+ // Use current directory for installation (where installer was run)
288
+ const targetDir = options.directory || currentDir;
289
+
290
+ // Explain why installing in .claude/ and confirm
291
+ if (!options.yes) {
292
+ console.log(chalk.cyan('\n📂 Installation Location:\n'));
293
+ console.log(chalk.white(' AgentVibes will be installed in:'));
294
+ console.log(chalk.yellow(` ${targetDir}/.claude/\n`));
295
+ console.log(chalk.gray(' Why .claude/?'));
296
+ console.log(chalk.gray(' • Claude Code automatically discovers tools in .claude/ directories'));
297
+ console.log(chalk.gray(' • This makes slash commands and TTS features immediately available'));
298
+ console.log(chalk.gray(' • Project-specific installation keeps your setup isolated\n'));
299
+
300
+ const { confirmLocation } = await inquirer.prompt([
301
+ {
302
+ type: 'confirm',
303
+ name: 'confirmLocation',
304
+ message: `Install AgentVibes in ${targetDir}/.claude/ ?`,
305
+ default: true,
306
+ },
307
+ ]);
308
+
309
+ if (!confirmLocation) {
310
+ console.log(chalk.red('\n❌ Installation cancelled.\n'));
311
+ process.exit(0);
312
+ }
313
+ }
314
+
99
315
  console.log(chalk.cyan('\n📦 What will be installed:'));
100
316
  console.log(chalk.gray(` • 16 slash commands → ${targetDir}/.claude/commands/agent-vibes/`));
101
317
  console.log(chalk.gray(` • Multi-provider TTS system (ElevenLabs + Piper TTS) → ${targetDir}/.claude/hooks/`));
@@ -106,13 +322,13 @@ async function install(options = {}) {
106
322
  console.log(chalk.gray(` • 30+ language support with native voices`));
107
323
  console.log(chalk.gray(` • BMAD integration for multi-agent sessions\n`));
108
324
 
109
- // Confirmation prompt (unless --yes flag is used)
325
+ // Final confirmation prompt (unless --yes flag is used)
110
326
  if (!options.yes) {
111
327
  const { confirm } = await inquirer.prompt([
112
328
  {
113
329
  type: 'confirm',
114
330
  name: 'confirm',
115
- message: chalk.yellow(`Install AgentVibes in ${targetDir}/.claude/ ?`),
331
+ message: chalk.yellow(`Proceed with installation using ${selectedProvider === 'elevenlabs' ? 'ElevenLabs' : 'Piper TTS'}?`),
116
332
  default: true,
117
333
  },
118
334
  ]);
@@ -121,8 +337,6 @@ async function install(options = {}) {
121
337
  console.log(chalk.red('\n❌ Installation cancelled.\n'));
122
338
  process.exit(0);
123
339
  }
124
- } else {
125
- console.log(chalk.green('✓ Auto-confirmed (--yes flag)\n'));
126
340
  }
127
341
 
128
342
  console.log(''); // Add spacing
@@ -258,24 +472,19 @@ async function install(options = {}) {
258
472
  }
259
473
  spinner.succeed(chalk.green('Installed output styles!\n'));
260
474
 
261
- // Check for API key
262
- spinner.start('Checking ElevenLabs API key...');
263
- const apiKey = process.env.ELEVENLABS_API_KEY;
475
+ // Save provider selection
476
+ spinner.start('Saving provider configuration...');
477
+ const providerConfigPath = path.join(claudeDir, 'tts-provider.txt');
478
+ await fs.writeFile(providerConfigPath, selectedProvider);
264
479
 
265
- if (!apiKey) {
266
- spinner.warn(chalk.yellow('ElevenLabs API key not found!'));
267
- console.log(chalk.yellow('\n⚠️ To use AgentVibes, you need an ElevenLabs API key:\n'));
268
- console.log(chalk.white(' 1. Go to https://elevenlabs.io/'));
269
- console.log(chalk.white(' 2. Sign up or log in (free tier available)'));
270
- console.log(chalk.white(' 3. Copy your API key from the profile section'));
271
- console.log(chalk.white(' 4. Set it in your shell profile:\n'));
272
- console.log(chalk.cyan(' export ELEVENLABS_API_KEY="your-key-here"'));
273
- console.log(chalk.gray('\n Add this to ~/.bashrc or ~/.zshrc to make it permanent\n'));
274
- } else {
275
- spinner.succeed(chalk.green('ElevenLabs API key found!'));
276
- console.log(chalk.gray(` Key: ${apiKey.substring(0, 10)}...\n`));
480
+ // Save Piper voices directory if Piper is selected
481
+ if (selectedProvider === 'piper' && piperVoicesPath) {
482
+ const piperConfigPath = path.join(claudeDir, 'piper-voices-dir.txt');
483
+ await fs.writeFile(piperConfigPath, piperVoicesPath);
277
484
  }
278
485
 
486
+ spinner.succeed(chalk.green(`Provider set to: ${selectedProvider === 'elevenlabs' ? 'ElevenLabs' : 'Piper TTS'}\n`));
487
+
279
488
  // List what was installed
280
489
  console.log(chalk.cyan('📦 Installation Summary:'));
281
490
  console.log(chalk.white(` • ${commandFiles.length} slash commands installed`));
@@ -283,7 +492,21 @@ async function install(options = {}) {
283
492
  console.log(chalk.white(` • ${personalityFiles.length} personality templates installed`));
284
493
  console.log(chalk.white(` • ${outputStyleFiles.length} output styles installed`));
285
494
  console.log(chalk.white(` • Voice manager ready`));
286
- console.log(chalk.white(` • 22 unique ElevenLabs voices available\n`));
495
+
496
+ if (selectedProvider === 'elevenlabs') {
497
+ console.log(chalk.white(` • 27+ ElevenLabs AI voices available`));
498
+ console.log(chalk.white(` • 30+ languages supported`));
499
+ if (elevenLabsKey) {
500
+ console.log(chalk.green(` • ElevenLabs API key configured ✓`));
501
+ } else {
502
+ console.log(chalk.yellow(` • ElevenLabs API key: Set manually later`));
503
+ }
504
+ } else {
505
+ console.log(chalk.white(` • 50+ Piper neural voices available (free!)`));
506
+ console.log(chalk.white(` • 18 languages supported`));
507
+ console.log(chalk.green(` • No API key needed ✓`));
508
+ }
509
+ console.log('');
287
510
 
288
511
  // Show recent changes from git log or RELEASE_NOTES.md
289
512
  try {
@@ -504,84 +727,10 @@ program
504
727
  );
505
728
 
506
729
  console.log(chalk.cyan('📍 Update Details:'));
507
- console.log(chalk.white(` Current directory: ${currentDir}`));
508
- console.log(chalk.white(` Update location: ${targetDir}/.claude/ (project-local)`));
509
- console.log(chalk.white(` Package version: ${version}\n`));
510
-
511
- // Check if already installed
512
- const commandsDir = path.join(targetDir, '.claude', 'commands', 'agent-vibes');
513
- let isInstalled = false;
514
- try {
515
- await fs.access(commandsDir);
516
- isInstalled = true;
517
- } catch {}
518
-
519
- if (!isInstalled) {
520
- console.log(chalk.red('❌ AgentVibes is not installed in this directory.'));
521
- console.log(chalk.gray(' Run: node src/installer.js install\n'));
522
- process.exit(1);
523
- }
524
-
525
- // Show latest release notes from RELEASE_NOTES.md
526
- try {
527
- const releaseNotesPath = path.join(__dirname, '..', 'RELEASE_NOTES.md');
528
- const releaseNotes = await fs.readFile(releaseNotesPath, 'utf8');
529
-
530
- // Extract latest release summary
531
- const lines = releaseNotes.split('\n');
532
-
533
- // Find the first release version header
534
- const versionIndex = lines.findIndex(line => line.match(/^## 📦 v\d+\.\d+\.\d+/));
535
-
536
- if (versionIndex >= 0) {
537
- // Extract version
538
- const versionMatch = lines[versionIndex].match(/v(\d+\.\d+\.\d+)/);
539
- const version = versionMatch ? versionMatch[1] : 'unknown';
540
-
541
- // Find the AI Summary section
542
- const summaryIndex = lines.findIndex((line, idx) =>
543
- idx > versionIndex && line.includes('### 🤖 AI Summary')
544
- );
545
-
546
- if (summaryIndex >= 0) {
547
- console.log(chalk.cyan(`📰 Latest Release (v${version}):\n`));
548
-
549
- // Extract summary text (lines between AI Summary and next ###)
550
- let summaryText = '';
551
- for (let i = summaryIndex + 1; i < lines.length; i++) {
552
- const line = lines[i];
553
- if (line.startsWith('###') || line.startsWith('##')) break;
554
- if (line.trim()) {
555
- summaryText += line.trim() + ' ';
556
- }
557
- }
558
-
559
- // Wrap text at ~80 chars for better readability
560
- const words = summaryText.split(' ');
561
- let currentLine = '';
562
- const wrappedLines = [];
563
-
564
- words.forEach(word => {
565
- if ((currentLine + word).length > 80) {
566
- wrappedLines.push(currentLine.trim());
567
- currentLine = word + ' ';
568
- } else {
569
- currentLine += word + ' ';
570
- }
571
- });
572
- if (currentLine.trim()) wrappedLines.push(currentLine.trim());
730
+ console.log(chalk.gray(` Update location: ${targetDir}/.claude/`));
731
+ console.log(chalk.gray(` Package version: ${version}`));
573
732
 
574
- wrappedLines.forEach(line => {
575
- console.log(chalk.white(` ${line}`));
576
- });
577
- console.log();
578
- }
579
- }
580
- } catch {
581
- // Release notes not available - no problem
582
- }
583
-
584
- // Show latest commit messages
733
+ // Show latest release notes from git log
585
734
  try {
586
735
  const { execSync } = await import('node:child_process');
587
736
  const gitLog = execSync(
@@ -590,55 +739,33 @@ program
590
739
  ).trim();
591
740
 
592
741
  if (gitLog) {
593
- console.log(chalk.cyan('📝 Latest Commit Messages:\n'));
742
+ console.log(chalk.cyan('\n📰 Latest Release Notes:'));
594
743
  const commits = gitLog.split('\n');
595
744
  commits.forEach(commit => {
596
745
  const [hash, ...messageParts] = commit.split(' ');
597
746
  const message = messageParts.join(' ');
598
747
  console.log(chalk.gray(` ${hash}`) + ' ' + chalk.white(message));
599
748
  });
600
- console.log();
601
749
  }
602
750
  } catch (error) {
603
- // Git not available - try RELEASE_NOTES.md fallback
604
- try {
605
- const releaseNotesPath = path.join(__dirname, '..', 'RELEASE_NOTES.md');
606
- const releaseNotes = await fs.readFile(releaseNotesPath, 'utf8');
607
-
608
- // Extract commits from "Recent Commits" section
609
- const lines = releaseNotes.split('\n');
610
- const commitsIndex = lines.findIndex(line => line.includes('## 📝 Recent Commits'));
611
-
612
- if (commitsIndex >= 0) {
613
- console.log(chalk.cyan('📝 Latest Commit Messages:\n'));
614
-
615
- // Find the code block with commits (between ``` markers)
616
- let inCodeBlock = false;
617
- for (let i = commitsIndex + 1; i < lines.length; i++) {
618
- const line = lines[i];
751
+ // Git not available or not a git repo - skip release notes
752
+ }
619
753
 
620
- if (line.trim() === '```') {
621
- if (inCodeBlock) break; // End of code block
622
- inCodeBlock = true;
623
- continue;
624
- }
754
+ // Check if already installed
755
+ const commandsDir = path.join(targetDir, '.claude', 'commands', 'agent-vibes');
756
+ let isInstalled = false;
757
+ try {
758
+ await fs.access(commandsDir);
759
+ isInstalled = true;
760
+ } catch {}
625
761
 
626
- if (inCodeBlock && line.trim()) {
627
- // Parse commit line: "hash message"
628
- const match = line.match(/^([a-f0-9]+)\s+(.+)$/);
629
- if (match) {
630
- const [, hash, message] = match;
631
- console.log(chalk.gray(` ${hash}`) + ' ' + chalk.white(message));
632
- }
633
- }
634
- }
635
- console.log();
636
- }
637
- } catch {
638
- // No release notes available
639
- }
762
+ if (!isInstalled) {
763
+ console.log(chalk.red('\n❌ AgentVibes is not installed in this directory.'));
764
+ console.log(chalk.gray(' Run: npx agentvibes install\n'));
765
+ process.exit(1);
640
766
  }
641
767
 
768
+
642
769
  console.log(chalk.cyan('📦 What will be updated:'));
643
770
  console.log(chalk.gray(' • Slash commands (keep your customizations)'));
644
771
  console.log(chalk.gray(' • TTS scripts'));
@@ -688,11 +815,20 @@ program
688
815
  spinner.text = 'Updating TTS scripts...';
689
816
  const srcHooksDir = path.join(__dirname, '..', '.claude', 'hooks');
690
817
  const allHookFiles = await fs.readdir(srcHooksDir);
691
- // Only copy AgentVibes-related scripts, exclude project-specific files
692
- const hookFiles = allHookFiles.filter(file =>
693
- !file.includes('prepare-release') &&
694
- !file.startsWith('.')
695
- );
818
+
819
+ // Filter to only include files (not directories) and exclude project-specific files
820
+ const hookFiles = [];
821
+ for (const file of allHookFiles) {
822
+ const srcPath = path.join(srcHooksDir, file);
823
+ const stat = await fs.stat(srcPath);
824
+
825
+ if (stat.isFile() &&
826
+ file.endsWith('.sh') &&
827
+ !file.includes('prepare-release') &&
828
+ !file.startsWith('.')) {
829
+ hookFiles.push(file);
830
+ }
831
+ }
696
832
 
697
833
  for (const file of hookFiles) {
698
834
  const srcPath = path.join(srcHooksDir, file);
@@ -705,12 +841,20 @@ program
705
841
  // Update personalities (only add new ones, don't overwrite existing)
706
842
  spinner.text = 'Updating personality templates...';
707
843
  const srcPersonalitiesDir = path.join(__dirname, '..', '.claude', 'personalities');
708
- const srcPersonalityFiles = await fs.readdir(srcPersonalitiesDir);
844
+ const allPersonalityFiles = await fs.readdir(srcPersonalitiesDir);
709
845
  let newPersonalities = 0;
710
846
  let updatedPersonalities = 0;
711
847
 
712
- for (const file of srcPersonalityFiles) {
848
+ // Filter to only .md files, skip directories
849
+ for (const file of allPersonalityFiles) {
713
850
  const srcPath = path.join(srcPersonalitiesDir, file);
851
+ const stat = await fs.stat(srcPath);
852
+
853
+ // Only copy .md files, skip directories
854
+ if (!stat.isFile() || !file.endsWith('.md')) {
855
+ continue;
856
+ }
857
+
714
858
  const destPath = path.join(personalitiesDir, file);
715
859
 
716
860
  try {
@@ -743,44 +887,53 @@ program
743
887
  console.log(chalk.cyan('📦 Update Summary:'));
744
888
  console.log(chalk.white(` • ${commandFiles.length} commands updated`));
745
889
  console.log(chalk.white(` • ${hookFiles.length} TTS scripts updated`));
746
- console.log(chalk.white(` • ${srcPersonalityFiles.length} personality templates (${newPersonalities} new, ${updatedPersonalities} updated)`));
890
+ console.log(chalk.white(` • ${newPersonalities + updatedPersonalities} personality templates (${newPersonalities} new, ${updatedPersonalities} updated)`));
747
891
  console.log(chalk.white(` • ${outputStyleFiles.length} output styles updated\n`));
748
892
 
749
- // Show latest release notes from RELEASE_NOTES.md
893
+ // Show latest release notes from RELEASE_NOTES_V2.md (v2.0+) or RELEASE_NOTES.md (legacy)
750
894
  try {
751
- const releaseNotesPath = path.join(__dirname, '..', 'RELEASE_NOTES.md');
752
- const releaseNotes = await fs.readFile(releaseNotesPath, 'utf8');
753
-
754
- // Extract latest release summary
755
- const lines = releaseNotes.split('\n');
895
+ // Try v2.0 format first
896
+ let releaseNotesPath = path.join(__dirname, '..', 'RELEASE_NOTES_V2.md');
897
+ let releaseNotes;
898
+ let isV2Format = true;
756
899
 
757
- // Find the first release version header
758
- const versionIndex = lines.findIndex(line => line.match(/^## 📦 v\d+\.\d+\.\d+/));
900
+ try {
901
+ releaseNotes = await fs.readFile(releaseNotesPath, 'utf8');
902
+ } catch {
903
+ // Fallback to legacy format
904
+ releaseNotesPath = path.join(__dirname, '..', 'RELEASE_NOTES.md');
905
+ releaseNotes = await fs.readFile(releaseNotesPath, 'utf8');
906
+ isV2Format = false;
907
+ }
759
908
 
760
- if (versionIndex >= 0) {
761
- // Extract version
762
- const versionMatch = lines[versionIndex].match(/v(\d+\.\d+\.\d+)/);
763
- const version = versionMatch ? versionMatch[1] : 'unknown';
909
+ const lines = releaseNotes.split('\n');
764
910
 
765
- // Find the AI Summary section
766
- const summaryIndex = lines.findIndex((line, idx) =>
767
- idx > versionIndex && line.includes('### 🤖 AI Summary')
768
- );
911
+ if (isV2Format) {
912
+ // v2.0 format - extract summary from top of file
913
+ const packageVersion = packageJson.version;
914
+ console.log(chalk.cyan(`📰 Latest Release (v${packageVersion}):\n`));
769
915
 
770
- if (summaryIndex >= 0) {
771
- console.log(chalk.cyan(`📰 Latest Release (v${version}):\n`));
916
+ // Find content after "## 🚀 Major Features" line
917
+ let summaryText = '';
918
+ let foundMajorFeatures = false;
772
919
 
773
- // Extract summary text (lines between AI Summary and next ###)
774
- let summaryText = '';
775
- for (let i = summaryIndex + 1; i < lines.length; i++) {
776
- const line = lines[i];
777
- if (line.startsWith('###') || line.startsWith('##')) break;
778
- if (line.trim()) {
920
+ for (const line of lines) {
921
+ if (line.includes('## 🚀 Major Features')) {
922
+ foundMajorFeatures = true;
923
+ continue;
924
+ }
925
+ if (foundMajorFeatures) {
926
+ // Stop at next major heading or after 200 chars
927
+ if (line.startsWith('##') && !line.includes('Major Features')) break;
928
+ if (summaryText.length > 200) break;
929
+ if (line.trim() && !line.startsWith('#') && !line.startsWith('**Release Date')) {
779
930
  summaryText += line.trim() + ' ';
780
931
  }
781
932
  }
933
+ }
782
934
 
783
- // Wrap text at ~80 chars for better readability
935
+ // Wrap text at ~80 chars
936
+ if (summaryText) {
784
937
  const words = summaryText.split(' ');
785
938
  let currentLine = '';
786
939
  const wrappedLines = [];
@@ -800,6 +953,50 @@ program
800
953
  });
801
954
  console.log();
802
955
  }
956
+ } else {
957
+ // Legacy format (v1.x)
958
+ const versionIndex = lines.findIndex(line => line.match(/^## 📦 v\d+\.\d+\.\d+/));
959
+
960
+ if (versionIndex >= 0) {
961
+ const versionMatch = lines[versionIndex].match(/v(\d+\.\d+\.\d+)/);
962
+ const version = versionMatch ? versionMatch[1] : 'unknown';
963
+
964
+ const summaryIndex = lines.findIndex((line, idx) =>
965
+ idx > versionIndex && line.includes('### 🤖 AI Summary')
966
+ );
967
+
968
+ if (summaryIndex >= 0) {
969
+ console.log(chalk.cyan(`📰 Latest Release (v${version}):\n`));
970
+
971
+ let summaryText = '';
972
+ for (let i = summaryIndex + 1; i < lines.length; i++) {
973
+ const line = lines[i];
974
+ if (line.startsWith('###') || line.startsWith('##')) break;
975
+ if (line.trim()) {
976
+ summaryText += line.trim() + ' ';
977
+ }
978
+ }
979
+
980
+ const words = summaryText.split(' ');
981
+ let currentLine = '';
982
+ const wrappedLines = [];
983
+
984
+ words.forEach(word => {
985
+ if ((currentLine + word).length > 80) {
986
+ wrappedLines.push(currentLine.trim());
987
+ currentLine = word + ' ';
988
+ } else {
989
+ currentLine += word + ' ';
990
+ }
991
+ });
992
+ if (currentLine.trim()) wrappedLines.push(currentLine.trim());
993
+
994
+ wrappedLines.forEach(line => {
995
+ console.log(chalk.white(` ${line}`));
996
+ });
997
+ console.log();
998
+ }
999
+ }
803
1000
  }
804
1001
  } catch {
805
1002
  // Release notes not available - no problem
@@ -1,21 +0,0 @@
1
- ---
2
- description: Add a new custom ElevenLabs TTS voice
3
- argument-hint: <voice_name> <voice_id>
4
- ---
5
-
6
- Add a new custom ElevenLabs TTS voice to your voice library.
7
-
8
- Usage:
9
- - `/agent-vibes:add "My Custom Voice" abc123xyz456789`
10
- - `/agent-vibes:add Narrator KTPVrSVAEUSJRClDzBw7`
11
-
12
- The voice ID should be a 15-30 character alphanumeric string from your ElevenLabs account.
13
-
14
- To find your voice IDs:
15
- 1. Go to https://elevenlabs.io/app/voice-library
16
- 2. Click on a voice
17
- 3. Copy the voice ID from the URL or settings
18
-
19
- After adding, you can switch to it with `/agent-vibes:switch "Voice Name"`
20
-
21
- !bash .claude/hooks/voice-manager.sh add $ARGUMENTS