agentvibes 5.2.1 → 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.
Files changed (222) hide show
  1. package/.agentvibes/LITE-MODE.md +236 -0
  2. package/.agentvibes/README.md +136 -0
  3. package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +141 -0
  4. package/.agentvibes/backups/agents/analyst_20260204_144958.md +78 -0
  5. package/.agentvibes/backups/agents/architect_20260204_144958.md +72 -0
  6. package/.agentvibes/backups/agents/dev_20260204_144958.md +74 -0
  7. package/.agentvibes/backups/agents/pm_20260204_144958.md +72 -0
  8. package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +64 -0
  9. package/.agentvibes/backups/agents/sm_20260204_144958.md +87 -0
  10. package/.agentvibes/backups/agents/tea_20260204_144958.md +79 -0
  11. package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +82 -0
  12. package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +80 -0
  13. package/.agentvibes/bmad/bmad-voices.md +69 -69
  14. package/.agentvibes/config/README-personality-defaults.md +162 -0
  15. package/.agentvibes/config/mode.txt +1 -0
  16. package/.agentvibes/config/personality-voice-defaults.default.json +21 -0
  17. package/.agentvibes/config/save-audio.txt +1 -0
  18. package/.agentvibes/config/voice-metadata.json +160 -0
  19. package/.agentvibes/config.json +24 -15
  20. package/.agentvibes/hooks/help.sh +191 -0
  21. package/.agentvibes/hooks/post-tool-use-lite.sh +111 -0
  22. package/.agentvibes/hooks/save-audio-manager.sh +162 -0
  23. package/.agentvibes/hooks/session-start-full-optimized.sh +102 -0
  24. package/.agentvibes/hooks/session-start-full.sh +142 -0
  25. package/.agentvibes/hooks/session-start-lite-v2.sh +34 -0
  26. package/.agentvibes/hooks/session-start-lite.sh +29 -0
  27. package/.agentvibes/hooks/stop-lite.sh +115 -0
  28. package/.agentvibes/hooks/switch-mode.sh +215 -0
  29. package/.agentvibes/output-styles/audio-summary.md +30 -0
  30. package/.claude/activation-instructions +54 -54
  31. package/.claude/audio/voice-samples/piper/alan.wav +0 -0
  32. package/.claude/audio/voice-samples/piper/amy.wav +0 -0
  33. package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
  34. package/.claude/audio/voice-samples/piper/joe.wav +0 -0
  35. package/.claude/audio/voice-samples/piper/john.wav +0 -0
  36. package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
  37. package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
  38. package/.claude/audio/voice-samples/piper/linda.wav +0 -0
  39. package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
  40. package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
  41. package/.claude/commands/agent-vibes/add.md +21 -21
  42. package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
  43. package/.claude/commands/agent-vibes/agent.md +79 -79
  44. package/.claude/commands/agent-vibes/background-music.md +111 -111
  45. package/.claude/commands/agent-vibes/bmad.md +198 -198
  46. package/.claude/commands/agent-vibes/clean.md +18 -18
  47. package/.claude/commands/agent-vibes/cleanup.md +18 -18
  48. package/.claude/commands/agent-vibes/commands.json +145 -145
  49. package/.claude/commands/agent-vibes/effects.md +97 -97
  50. package/.claude/commands/agent-vibes/get.md +9 -9
  51. package/.claude/commands/agent-vibes/hide.md +91 -91
  52. package/.claude/commands/agent-vibes/language.md +23 -23
  53. package/.claude/commands/agent-vibes/learn.md +67 -67
  54. package/.claude/commands/agent-vibes/list.md +13 -13
  55. package/.claude/commands/agent-vibes/mute.md +37 -37
  56. package/.claude/commands/agent-vibes/preview.md +17 -17
  57. package/.claude/commands/agent-vibes/provider.md +68 -68
  58. package/.claude/commands/agent-vibes/replay-target.md +14 -14
  59. package/.claude/commands/agent-vibes/sample.md +12 -12
  60. package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
  61. package/.claude/commands/agent-vibes/set-pretext.md +65 -65
  62. package/.claude/commands/agent-vibes/set-speed.md +41 -41
  63. package/.claude/commands/agent-vibes/show.md +84 -84
  64. package/.claude/commands/agent-vibes/switch.md +87 -87
  65. package/.claude/commands/agent-vibes/target-voice.md +26 -26
  66. package/.claude/commands/agent-vibes/target.md +30 -30
  67. package/.claude/commands/agent-vibes/translate.md +68 -68
  68. package/.claude/commands/agent-vibes/unmute.md +45 -45
  69. package/.claude/commands/agent-vibes/whoami.md +7 -7
  70. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  71. package/.claude/commands/agent-vibes-rdp.md +24 -24
  72. package/.claude/config/audio-effects.cfg +4 -11
  73. package/.claude/config/audio-effects.cfg.sample +52 -52
  74. package/.claude/config/background-music-position.txt +27 -0
  75. package/.claude/config/background-music-volume.txt +1 -1
  76. package/.claude/config/background-music.cfg +1 -0
  77. package/.claude/config/background-music.txt +1 -0
  78. package/.claude/config/tts-speech-rate.txt +1 -4
  79. package/.claude/config/tts-verbosity.txt +1 -0
  80. package/.claude/docs/TERMUX_SETUP.md +408 -408
  81. package/.claude/github-star-reminder.txt +1 -1
  82. package/.claude/hooks/README-TTS-QUEUE.md +135 -135
  83. package/.claude/hooks/audio-cache-utils.sh +0 -0
  84. package/.claude/hooks/audio-processor.sh +60 -14
  85. package/.claude/hooks/background-music-manager.sh +0 -0
  86. package/.claude/hooks/bmad-party-manager.sh +225 -0
  87. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  88. package/.claude/hooks/bmad-speak.sh +6 -13
  89. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  90. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  91. package/.claude/hooks/clawdbot-receiver-SECURE.sh +25 -23
  92. package/.claude/hooks/clawdbot-receiver.sh +4 -28
  93. package/.claude/hooks/clean-audio-cache.sh +0 -0
  94. package/.claude/hooks/cleanup-cache.sh +0 -0
  95. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  96. package/.claude/hooks/download-extra-voices.sh +0 -0
  97. package/.claude/hooks/effects-manager.sh +0 -0
  98. package/.claude/hooks/github-star-reminder.sh +0 -0
  99. package/.claude/hooks/language-manager.sh +0 -0
  100. package/.claude/hooks/learn-manager.sh +0 -0
  101. package/.claude/hooks/macos-voice-manager.sh +0 -0
  102. package/.claude/hooks/migrate-background-music.sh +0 -0
  103. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  104. package/.claude/hooks/optimize-background-music.sh +0 -0
  105. package/.claude/hooks/personality-manager.sh +0 -0
  106. package/.claude/hooks/piper-download-voices.sh +0 -0
  107. package/.claude/hooks/piper-installer.sh +1 -1
  108. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  109. package/.claude/hooks/piper-voice-manager.sh +0 -0
  110. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  111. package/.claude/hooks/play-tts-macos.sh +6 -12
  112. package/.claude/hooks/play-tts-piper.sh +50 -79
  113. package/.claude/hooks/play-tts-soprano.sh +9 -43
  114. package/.claude/hooks/play-tts-ssh-remote.sh +42 -120
  115. package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
  116. package/.claude/hooks/play-tts.sh +48 -37
  117. package/.claude/hooks/post-response.sh +41 -0
  118. package/.claude/hooks/prepare-release.sh +0 -0
  119. package/.claude/hooks/provider-commands.sh +0 -0
  120. package/.claude/hooks/provider-manager.sh +0 -0
  121. package/.claude/hooks/replay-target-audio.sh +0 -0
  122. package/.claude/hooks/requirements.txt +6 -6
  123. package/.claude/hooks/sentiment-manager.sh +0 -0
  124. package/.claude/hooks/session-start-tts.sh +56 -39
  125. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  126. package/.claude/hooks/speed-manager.sh +0 -0
  127. package/.claude/hooks/stop.sh +63 -0
  128. package/.claude/hooks/termux-installer.sh +0 -0
  129. package/.claude/hooks/translate-manager.sh +0 -0
  130. package/.claude/hooks/translator.py +237 -237
  131. package/.claude/hooks/tts-queue-worker.sh +0 -0
  132. package/.claude/hooks/tts-queue.sh +0 -0
  133. package/.claude/hooks/verbosity-manager.sh +0 -0
  134. package/.claude/hooks/voice-manager.sh +26 -4
  135. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  136. package/.claude/hooks-windows/bmad-party-speak.ps1 +278 -274
  137. package/.claude/hooks-windows/bmad-speak.ps1 +264 -264
  138. package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -53
  139. package/.claude/hooks-windows/effects-manager.ps1 +294 -294
  140. package/.claude/hooks-windows/language-manager.ps1 +193 -193
  141. package/.claude/hooks-windows/learn-manager.ps1 +241 -241
  142. package/.claude/hooks-windows/personality-manager.ps1 +266 -266
  143. package/.claude/hooks-windows/play-tts-soprano.ps1 +5 -5
  144. package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -138
  145. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +164 -0
  146. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -0
  147. package/.claude/hooks-windows/play-tts.ps1 +104 -481
  148. package/.claude/hooks-windows/provider-manager.ps1 +158 -192
  149. package/.claude/hooks-windows/session-start-tts.ps1 +55 -46
  150. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  151. package/.claude/hooks-windows/speed-manager.ps1 +166 -166
  152. package/.claude/hooks-windows/voice-manager-windows.ps1 +176 -260
  153. package/.claude/output-styles/agent-vibes.md +202 -202
  154. package/.claude/personalities/angry.md +14 -14
  155. package/.claude/personalities/annoying.md +14 -14
  156. package/.claude/personalities/crass.md +14 -14
  157. package/.claude/personalities/dramatic.md +14 -14
  158. package/.claude/personalities/dry-humor.md +50 -50
  159. package/.claude/personalities/flirty.md +20 -20
  160. package/.claude/personalities/funny.md +14 -14
  161. package/.claude/personalities/grandpa.md +32 -32
  162. package/.claude/personalities/millennial.md +14 -14
  163. package/.claude/personalities/moody.md +14 -14
  164. package/.claude/personalities/normal.md +16 -16
  165. package/.claude/personalities/pirate.md +14 -14
  166. package/.claude/personalities/poetic.md +14 -14
  167. package/.claude/personalities/professional.md +14 -14
  168. package/.claude/personalities/rapper.md +55 -55
  169. package/.claude/personalities/robot.md +14 -14
  170. package/.claude/personalities/sarcastic.md +38 -38
  171. package/.claude/personalities/sassy.md +14 -14
  172. package/.claude/personalities/surfer-dude.md +14 -14
  173. package/.claude/personalities/zen.md +14 -14
  174. package/.claude/piper-voices-dir.txt +1 -0
  175. package/.claude/settings.json +25 -15
  176. package/.claude/verbosity.txt +1 -1
  177. package/.clawdbot/README.md +105 -105
  178. package/.clawdbot/skill/SKILL.md +149 -145
  179. package/.mcp.json +30 -11
  180. package/CLAUDE.md +170 -215
  181. package/README.md +206 -515
  182. package/RELEASE_NOTES.md +1132 -1884
  183. package/WINDOWS-SETUP.md +208 -208
  184. package/bin/agent-vibes +0 -0
  185. package/bin/agentvibes-voice-browser.js +64 -1289
  186. package/bin/agentvibes.js +0 -0
  187. package/bin/ensure-soprano-running.sh +43 -0
  188. package/bin/mcp-server.js +121 -121
  189. package/bin/mcp-server.sh +0 -0
  190. package/bin/test-bmad-pr +78 -78
  191. package/mcp-server/QUICK_START.md +203 -203
  192. package/mcp-server/README.md +345 -345
  193. package/mcp-server/WINDOWS_SETUP.md +260 -260
  194. package/mcp-server/docs/troubleshooting-audio.md +313 -313
  195. package/mcp-server/examples/claude_desktop_config.json +11 -11
  196. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  197. package/mcp-server/examples/custom_instructions.md +169 -169
  198. package/mcp-server/install-deps.js +130 -130
  199. package/mcp-server/pyproject.toml +52 -52
  200. package/mcp-server/requirements.txt +2 -2
  201. package/mcp-server/server.py +1451 -1578
  202. package/mcp-server/test_server.py +395 -395
  203. package/package.json +1 -3
  204. package/setup-windows.ps1 +815 -815
  205. package/src/console/tabs/setup-tab.js +9 -6
  206. package/src/console/tabs/voices-tab.js +9 -3
  207. package/src/installer.js +42 -5
  208. package/src/services/llm-provider-service.js +13 -0
  209. package/templates/agentvibes-receiver.sh +158 -483
  210. package/templates/audio/welcome-music.mp3 +0 -0
  211. package/.agentvibes/bmad-voice-map.json +0 -104
  212. package/.agentvibes/copilot-sessions.log +0 -4
  213. package/.claude/config/audio-effects-bmad.cfg +0 -50
  214. package/.claude/config/background-music-enabled.txt +0 -1
  215. package/.claude/config/intro-text.txt +0 -1
  216. package/.claude/config/personality.txt +0 -1
  217. package/.claude/config/piper-speech-rate.txt +0 -4
  218. package/.claude/config/piper-target-speech-rate.txt +0 -1
  219. package/.claude/config/reverb-level.txt +0 -1
  220. package/.claude/config/tts-target-speech-rate.txt +0 -1
  221. package/voice-assignments.json +0 -8245
  222. /package/{.claude → .agentvibes}/config/agentvibes.json +0 -0
@@ -1278,29 +1278,32 @@ export function createSetupTab(screen, services) {
1278
1278
  const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
1279
1279
 
1280
1280
  // Route through remote provider if active
1281
+ // Search order: CLAUDE_PROJECT_DIR → cwd → package root → home
1281
1282
  const _remoteProviders = ['ssh-remote', 'agentvibes-receiver'];
1282
1283
  let _activeProvider = '';
1283
1284
  try {
1284
- const _projectRoot = path.resolve(__dirname, '..', '..');
1285
+ const _pkgRoot = path.resolve(__dirname, '..', '..');
1285
1286
  const _provPaths = [
1286
- path.join(_projectRoot, '.claude', 'tts-provider.txt'),
1287
+ process.env.CLAUDE_PROJECT_DIR && path.join(process.env.CLAUDE_PROJECT_DIR, '.claude', 'tts-provider.txt'),
1288
+ path.join(process.cwd(), '.claude', 'tts-provider.txt'),
1289
+ path.join(_pkgRoot, '.claude', 'tts-provider.txt'),
1287
1290
  path.join(os.homedir(), '.claude', 'tts-provider.txt'),
1288
- ];
1291
+ ].filter(Boolean);
1289
1292
  for (const p of _provPaths) {
1290
1293
  if (fs.existsSync(p)) { _activeProvider = fs.readFileSync(p, 'utf8').trim(); break; }
1291
1294
  }
1292
1295
  } catch {}
1293
1296
 
1294
1297
  if (_remoteProviders.includes(_activeProvider)) {
1295
- const _projectRoot = path.resolve(__dirname, '..', '..');
1298
+ const _hooksBase = process.env.CLAUDE_PROJECT_DIR || process.cwd();
1296
1299
  let rProc;
1297
1300
  if (_isWin) {
1298
- const _playTts = path.join(_projectRoot, '.claude', 'hooks-windows', 'play-tts.ps1');
1301
+ const _playTts = path.join(_hooksBase, '.claude', 'hooks-windows', 'play-tts.ps1');
1299
1302
  rProc = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', _playTts, phrase, voiceId], {
1300
1303
  stdio: 'ignore', detached: false, windowsHide: true, env: _spawnEnv,
1301
1304
  });
1302
1305
  } else {
1303
- const _playTts = path.join(_projectRoot, '.claude', 'hooks', 'play-tts.sh');
1306
+ const _playTts = path.join(_hooksBase, '.claude', 'hooks', 'play-tts.sh');
1304
1307
  rProc = spawn('bash', [_playTts, phrase, voiceId], {
1305
1308
  stdio: 'ignore', detached: true, env: _spawnEnv,
1306
1309
  });
@@ -868,14 +868,17 @@ export function createVoicesTab(screen, services) {
868
868
  _playingVoiceId = null;
869
869
 
870
870
  // Check if we should route through remote provider (ssh-remote / agentvibes-receiver)
871
+ // Search order: CLAUDE_PROJECT_DIR (actual project) → cwd → package root → home
871
872
  const projectRoot = path.resolve(__dirname, '..', '..');
872
873
  const remoteProviders = ['ssh-remote', 'agentvibes-receiver'];
873
874
  let activeProvider = '';
874
875
  try {
875
876
  const providerPaths = [
877
+ process.env.CLAUDE_PROJECT_DIR && path.join(process.env.CLAUDE_PROJECT_DIR, '.claude', 'tts-provider.txt'),
878
+ path.join(process.cwd(), '.claude', 'tts-provider.txt'),
876
879
  path.join(projectRoot, '.claude', 'tts-provider.txt'),
877
880
  path.join(os.homedir(), '.claude', 'tts-provider.txt'),
878
- ];
881
+ ].filter(Boolean);
879
882
  for (const p of providerPaths) {
880
883
  if (fs.existsSync(p)) { activeProvider = fs.readFileSync(p, 'utf8').trim(); break; }
881
884
  }
@@ -884,14 +887,17 @@ export function createVoicesTab(screen, services) {
884
887
  if (remoteProviders.includes(activeProvider)) {
885
888
  const isWindows = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
886
889
  const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
890
+ // Resolve play-tts from the actual project (CLAUDE_PROJECT_DIR / cwd),
891
+ // not the npm package root — hooks live in the user's project dir.
892
+ const hooksBase = process.env.CLAUDE_PROJECT_DIR || process.cwd();
887
893
  let proc;
888
894
  if (isWindows) {
889
- const playTts = path.join(projectRoot, '.claude', 'hooks-windows', 'play-tts.ps1');
895
+ const playTts = path.join(hooksBase, '.claude', 'hooks-windows', 'play-tts.ps1');
890
896
  proc = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', playTts, phrase, voiceId], {
891
897
  stdio: 'ignore', detached: false, windowsHide: true, env: _spawnEnv,
892
898
  });
893
899
  } else {
894
- const playTts = path.join(projectRoot, '.claude', 'hooks', 'play-tts.sh');
900
+ const playTts = path.join(hooksBase, '.claude', 'hooks', 'play-tts.sh');
895
901
  proc = spawn('bash', [playTts, phrase, voiceId], {
896
902
  stdio: 'ignore', detached: true, env: _spawnEnv,
897
903
  });
package/src/installer.js CHANGED
@@ -132,6 +132,36 @@ function isNativeWindows() {
132
132
  return process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
133
133
  }
134
134
 
135
+ /**
136
+ * Wrap an ora spinner with safe fallbacks for all methods.
137
+ * Ensures compatibility across platforms (Windows, WSL, macOS, Linux) and ora versions.
138
+ * @param {Object} spinner - An ora spinner instance (or any spinner-like object)
139
+ * @returns {Object} A spinner proxy with guaranteed methods
140
+ */
141
+ function createRobustSpinner(spinner) {
142
+ const safe = (method, fallback) => (...args) => {
143
+ if (typeof spinner[method] === 'function') {
144
+ try { spinner[method](...args); } catch { fallback?.(...args); }
145
+ } else {
146
+ fallback?.(...args);
147
+ }
148
+ return proxy;
149
+ };
150
+ const proxy = {
151
+ start: safe('start'),
152
+ stop: safe('stop'),
153
+ succeed: safe('succeed', (t) => { if (t) process.stdout.write(`✓ ${t}\n`); }),
154
+ fail: safe('fail', (t) => { if (t) process.stderr.write(`✗ ${t}\n`); }),
155
+ warn: safe('warn', (t) => { if (t) process.stdout.write(`⚠ ${t}\n`); }),
156
+ info: safe('info', (t) => { if (t) process.stdout.write(`ℹ ${t}\n`); }),
157
+ stopAndPersist: safe('stopAndPersist'),
158
+ get text() { return spinner.text ?? ''; },
159
+ set text(t) { try { spinner.text = t; } catch { /* non-TTY */ } },
160
+ get isSpinning(){ return spinner.isSpinning ?? false; },
161
+ };
162
+ return proxy;
163
+ }
164
+
135
165
  /**
136
166
  * Get the Piper provider name (always 'piper' on all platforms)
137
167
  * @returns {string} The piper provider identifier
@@ -4215,7 +4245,7 @@ async function checkAndInstallPiperWindows(targetDir, options) {
4215
4245
  }
4216
4246
  const piperDir = path.join(localAppData, 'Programs', 'Piper');
4217
4247
  const piperExe = path.join(piperDir, 'piper.exe');
4218
- const spinner = ora();
4248
+ const spinner = createRobustSpinner(ora());
4219
4249
 
4220
4250
  if (fsSync.existsSync(piperExe)) {
4221
4251
  console.log(chalk.green('✓ Piper TTS is already installed at ' + piperDir + '\n'));
@@ -4844,7 +4874,14 @@ async function updatePersonalityFiles(targetDir, srcPersonalitiesDir) {
4844
4874
  * @returns {Object} Mock spinner object
4845
4875
  */
4846
4876
  function createSilentSpinner() {
4847
- const s = { start: () => s, succeed: () => s, info: () => s, fail: () => s, warn: () => s, stop: () => s };
4877
+ const s = {
4878
+ start: () => s, succeed: () => s, info: () => s,
4879
+ fail: () => s, warn: () => s, stop: () => s,
4880
+ stopAndPersist: () => s,
4881
+ get text() { return ''; },
4882
+ set text(_) {},
4883
+ get isSpinning() { return false; },
4884
+ };
4848
4885
  return s;
4849
4886
  }
4850
4887
 
@@ -5025,7 +5062,7 @@ function displayUpdateSummary(results) {
5025
5062
  * @param {Object} options - Update options
5026
5063
  */
5027
5064
  async function updateAgentVibes(targetDir, options) {
5028
- const spinner = ora('Updating AgentVibes...').start();
5065
+ const spinner = createRobustSpinner(ora('Updating AgentVibes...').start());
5029
5066
 
5030
5067
  try {
5031
5068
  // Perform all update operations
@@ -5145,7 +5182,7 @@ async function install(options = {}) {
5145
5182
  const silentSpinner = createSilentSpinner();
5146
5183
 
5147
5184
  console.log('');
5148
- const spinner = ora('Installing AgentVibes...').start();
5185
+ const spinner = createRobustSpinner(ora('Installing AgentVibes...').start());
5149
5186
 
5150
5187
  try {
5151
5188
  // Create .claude directory structure
@@ -5599,7 +5636,7 @@ program
5599
5636
  console.log(chalk.gray('✓ Auto-confirmed (--yes flag)\n'));
5600
5637
  }
5601
5638
 
5602
- const spinner = ora('Uninstalling AgentVibes...').start();
5639
+ const spinner = createRobustSpinner(ora('Uninstalling AgentVibes...').start());
5603
5640
 
5604
5641
  try {
5605
5642
  let removedCount = 0;
@@ -209,6 +209,19 @@ export async function installClaudeMcp(targetDir) {
209
209
  await installer.copyBackgroundMusicFiles(targetDir, silentSpinner);
210
210
  ensureDefaultLlmConfigSync('claude-code', targetDir);
211
211
 
212
+ // Explicitly write tts-provider.txt so `get_active_provider()` in
213
+ // provider-manager.sh doesn't silently fall back to "piper". Without
214
+ // this, headless servers with no audio device hit a confusing failure
215
+ // mode where TTS tries to synth locally and fails silently. Users
216
+ // can still change the provider via the Setup TUI or slash command.
217
+ const ttsProviderPath = path.join(targetDir, '.claude', 'tts-provider.txt');
218
+ try {
219
+ await fs.access(ttsProviderPath);
220
+ // Already exists — user has explicitly set a provider, don't clobber
221
+ } catch {
222
+ await fs.writeFile(ttsProviderPath, 'piper\n');
223
+ }
224
+
212
225
  return { success: true, mcpCreated, mcpError };
213
226
  } catch (err) {
214
227
  return { success: false, error: err.message, mcpError };