agentvibes 5.6.7 → 5.6.9

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 (142) 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/config/README-personality-defaults.md +162 -0
  14. package/.agentvibes/config/agentvibes.json +1 -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 +38 -1
  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/audio/voice-samples/piper/alan.wav +0 -0
  31. package/.claude/audio/voice-samples/piper/amy.wav +0 -0
  32. package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
  33. package/.claude/audio/voice-samples/piper/joe.wav +0 -0
  34. package/.claude/audio/voice-samples/piper/john.wav +0 -0
  35. package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
  36. package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
  37. package/.claude/audio/voice-samples/piper/linda.wav +0 -0
  38. package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
  39. package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
  40. package/.claude/commands/agent-vibes/provider.md +0 -0
  41. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  42. package/.claude/commands/agent-vibes-rdp.md +24 -24
  43. package/.claude/config/audio-effects.cfg +6 -1
  44. package/.claude/config/background-music-position.txt +8 -6
  45. package/.claude/config/reverb-level.txt +0 -0
  46. package/.claude/docs/TERMUX_SETUP.md +408 -408
  47. package/.claude/github-star-reminder.txt +1 -1
  48. package/.claude/hooks/audio-cache-utils.sh +0 -0
  49. package/.claude/hooks/audio-processor.sh +0 -0
  50. package/.claude/hooks/background-music-manager.sh +0 -0
  51. package/.claude/hooks/bmad-party-manager.sh +225 -0
  52. package/.claude/hooks/bmad-party-speak.sh +0 -0
  53. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  54. package/.claude/hooks/bmad-speak.sh +0 -0
  55. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  56. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  57. package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
  58. package/.claude/hooks/clawdbot-receiver.sh +0 -0
  59. package/.claude/hooks/clean-audio-cache.sh +0 -0
  60. package/.claude/hooks/cleanup-cache.sh +0 -0
  61. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  62. package/.claude/hooks/download-extra-voices.sh +0 -0
  63. package/.claude/hooks/effects-manager.sh +0 -0
  64. package/.claude/hooks/github-star-reminder.sh +0 -0
  65. package/.claude/hooks/language-manager.sh +0 -0
  66. package/.claude/hooks/learn-manager.sh +0 -0
  67. package/.claude/hooks/macos-voice-manager.sh +0 -0
  68. package/.claude/hooks/migrate-background-music.sh +0 -0
  69. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  70. package/.claude/hooks/optimize-background-music.sh +0 -0
  71. package/.claude/hooks/path-resolver.sh +0 -0
  72. package/.claude/hooks/personality-manager.sh +0 -0
  73. package/.claude/hooks/piper-download-voices.sh +0 -0
  74. package/.claude/hooks/piper-installer.sh +0 -0
  75. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  76. package/.claude/hooks/piper-voice-manager.sh +0 -0
  77. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
  78. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  79. package/.claude/hooks/play-tts-macos.sh +0 -0
  80. package/.claude/hooks/play-tts-piper.sh +40 -2
  81. package/.claude/hooks/play-tts-soprano.sh +0 -0
  82. package/.claude/hooks/play-tts-ssh-remote.sh +0 -0
  83. package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
  84. package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
  85. package/.claude/hooks/play-tts.sh +13 -0
  86. package/.claude/hooks/post-response.sh +41 -0
  87. package/.claude/hooks/prepare-release.sh +0 -0
  88. package/.claude/hooks/provider-commands.sh +0 -0
  89. package/.claude/hooks/provider-manager.sh +0 -0
  90. package/.claude/hooks/replay-target-audio.sh +0 -0
  91. package/.claude/hooks/sentiment-manager.sh +0 -0
  92. package/.claude/hooks/session-start-tts.sh +48 -13
  93. package/.claude/hooks/soprano-gradio-synth.py +0 -0
  94. package/.claude/hooks/speed-manager.sh +0 -0
  95. package/.claude/hooks/stop-tts.sh +0 -0
  96. package/.claude/hooks/stop.sh +63 -0
  97. package/.claude/hooks/termux-installer.sh +0 -0
  98. package/.claude/hooks/translate-manager.sh +0 -0
  99. package/.claude/hooks/translator.py +0 -0
  100. package/.claude/hooks/tts-queue-worker.sh +0 -0
  101. package/.claude/hooks/tts-queue.sh +0 -0
  102. package/.claude/hooks/verbosity-manager.sh +0 -0
  103. package/.claude/hooks/voice-manager.sh +0 -0
  104. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  105. package/.claude/hooks-windows/play-tts-piper.ps1 +26 -1
  106. package/.claude/hooks-windows/play-tts.ps1 +25 -1
  107. package/.claude/hooks-windows/session-start-tts.ps1 +28 -9
  108. package/.claude/piper-voices-dir.txt +1 -0
  109. package/.claude/settings.json +2 -2
  110. package/.clawdbot/README.md +105 -105
  111. package/.mcp.json +32 -3
  112. package/CLAUDE.md +9 -0
  113. package/README.md +21 -3
  114. package/RELEASE_NOTES.md +61 -0
  115. package/WINDOWS-SETUP.md +208 -208
  116. package/bin/agent-vibes +0 -0
  117. package/bin/agentvibes-voice-browser.js +59 -4
  118. package/bin/agentvibes.js +0 -0
  119. package/bin/ensure-soprano-running.sh +43 -0
  120. package/bin/mcp-server.js +121 -121
  121. package/bin/mcp-server.sh +0 -0
  122. package/bin/test-bmad-pr +78 -78
  123. package/mcp-server/QUICK_START.md +203 -203
  124. package/mcp-server/README.md +345 -345
  125. package/mcp-server/WINDOWS_SETUP.md +260 -260
  126. package/mcp-server/docs/troubleshooting-audio.md +313 -313
  127. package/mcp-server/examples/claude_desktop_config.json +11 -11
  128. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  129. package/mcp-server/examples/custom_instructions.md +169 -169
  130. package/mcp-server/install-deps.js +177 -130
  131. package/mcp-server/server.py +1797 -1787
  132. package/mcp-server/test_server.py +0 -0
  133. package/package.json +1 -1
  134. package/src/console/app.js +6 -0
  135. package/src/console/tabs/music-tab.js +18 -2
  136. package/src/console/widgets/format-utils.js +11 -2
  137. package/src/installer.js +38 -37
  138. package/src/services/llm-provider-service.js +28 -9
  139. package/src/utils/voice-names.js +2 -0
  140. package/templates/agentvibes-receiver.sh +0 -0
  141. package/templates/audio/welcome-music.mp3 +0 -0
  142. 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.6.7",
4
+ "version": "5.6.9",
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": [
@@ -30,6 +30,7 @@ import { createReceiverTab } from './tabs/receiver-tab.js';
30
30
  import { createAgentsTab } from './tabs/agents-tab.js';
31
31
  import { ConfigService } from '../services/config-service.js';
32
32
  import { ProviderService } from '../services/provider-service.js';
33
+ import { seedAllLlmDefaultsSync } from '../services/llm-provider-service.js';
33
34
 
34
35
  const _dir = path.dirname(fileURLToPath(import.meta.url));
35
36
  const _pkg = JSON.parse(readFileSync(path.join(_dir, '../../package.json'), 'utf8'));
@@ -893,6 +894,11 @@ export class AgentVibesConsole {
893
894
  * @returns {Promise<AgentVibesConsole>}
894
895
  */
895
896
  export async function launchConsole(opts = {}) {
897
+ // Seed per-LLM piper defaults into the project config on every TUI launch.
898
+ // This guarantees play-tts.ps1 always finds a piper engine row for each LLM
899
+ // and never silently falls back to the global windows-sapi provider.
900
+ seedAllLlmDefaultsSync(process.cwd());
901
+
896
902
  const app = new AgentVibesConsole(opts);
897
903
  await app.init();
898
904
  return app;
@@ -225,6 +225,19 @@ function _setMusic(configService, update) {
225
225
  configService.set('backgroundMusic', { ...current, ...update });
226
226
  }
227
227
 
228
+ /**
229
+ * Sync the background-music-enabled.txt flag file so bash hooks see the change.
230
+ * The TUI stores enabled state in config.json, but audio-processor.sh and
231
+ * play-tts-piper.sh read from this .txt file — they must stay in sync.
232
+ * @param {boolean} enabled
233
+ */
234
+ export function applyEnabledToFile(enabled) {
235
+ const enabledFile = path.join(process.cwd(), '.claude', 'config', 'background-music-enabled.txt');
236
+ try {
237
+ fs.writeFileSync(enabledFile, enabled ? 'true' : 'false', 'utf-8');
238
+ } catch { /* non-fatal */ }
239
+ }
240
+
228
241
  /**
229
242
  * Patch the 'default' entry in audio-effects.cfg to use the given track.
230
243
  * play-tts-piper.sh reads the track from audio-effects.cfg (not from config.json),
@@ -431,7 +444,9 @@ export function createMusicTab(screen, services) {
431
444
 
432
445
  const toggleBtn = _createBtn(_tl('musicToggleBtn'), () => {
433
446
  const { enabled } = _getMusic(configService);
434
- _setMusic(configService, { enabled: !enabled });
447
+ const next = !enabled;
448
+ _setMusic(configService, { enabled: next });
449
+ applyEnabledToFile(next); // sync bash hooks (audio-processor.sh reads this file)
435
450
  refreshDisplay();
436
451
  });
437
452
  toggleBtn.bottom = 4;
@@ -772,8 +787,9 @@ export function createMusicTab(screen, services) {
772
787
  }
773
788
 
774
789
  function _saveLocally() {
775
- _setMusic(configService, { track: trackId });
790
+ _setMusic(configService, { track: trackId, enabled: true });
776
791
  applyTrackToAudioEffects(trackId);
792
+ applyEnabledToFile(true); // saving a track implies enabling music
777
793
  refreshDisplay();
778
794
  _showTrackChangedNotice(displayName);
779
795
  }
@@ -5,6 +5,8 @@
5
5
  * circular imports between widgets and tabs.
6
6
  */
7
7
 
8
+ import { uniquifyVoiceName } from '../../utils/voice-names.js';
9
+
8
10
  const TRACK_NAMES = Object.freeze({
9
11
  'agentvibes_soft_flamenco_loop.mp3': '🎻 Soft Flamenco',
10
12
  'agent_vibes_bachata_v1_loop.mp3': '🎺 Bachata',
@@ -60,8 +62,15 @@ export function formatVoiceName(voice) {
60
62
 
61
63
  let name;
62
64
  if (voice.includes('::')) {
63
- // 16Speakers::Rose_Ibex extract after '::'
64
- name = voice.split('::')[1];
65
+ const speakerPart = voice.split('::')[1];
66
+ if (speakerPart.includes('_')) {
67
+ // 16Speakers format (Rose_Ibex) — already a complete name, just normalise display
68
+ name = speakerPart.replace(/_/g, ' ');
69
+ } else {
70
+ // LibriTTS / single-word names: append deterministic surname
71
+ // "Mary" → "Mary Bell", "Mary-7" → "Mary Hayes"
72
+ name = uniquifyVoiceName(speakerPart);
73
+ }
65
74
  } else {
66
75
  const parts = voice.split('-');
67
76
  const QUALITIES = new Set(['high', 'medium', 'low']);
package/src/installer.js CHANGED
@@ -4054,17 +4054,18 @@ async function configureSessionStartHook(targetDir, spinner) {
4054
4054
  existingSettings.hooks = {};
4055
4055
  }
4056
4056
 
4057
- if (!existingSettings.hooks.SessionStart) {
4058
- if (isNativeWindows()) {
4059
- existingSettings.hooks.SessionStart = [{
4060
- hooks: [{
4061
- type: 'command',
4062
- command: 'powershell -NoProfile -ExecutionPolicy Bypass -File "$CLAUDE_PROJECT_DIR\\.claude\\hooks-windows\\session-start-tts.ps1"'
4063
- }]
4064
- }];
4065
- } else {
4066
- existingSettings.hooks.SessionStart = templateSettings.hooks.SessionStart;
4067
- }
4057
+ // Windows uses SessionStart; Linux/macOS/WSL uses UserPromptSubmit.
4058
+ // Both point to the global $HOME path so updates to the hook script
4059
+ // take effect immediately without needing per-project reinstalls.
4060
+ const hookKey = isNativeWindows() ? 'SessionStart' : 'UserPromptSubmit';
4061
+ const hookCommand = isNativeWindows()
4062
+ ? 'powershell -NoProfile -ExecutionPolicy Bypass -File "$HOME\\.claude\\hooks-windows\\session-start-tts.ps1"'
4063
+ : 'bash "$HOME/.claude/hooks/session-start-tts.sh"';
4064
+
4065
+ if (!existingSettings.hooks[hookKey]) {
4066
+ existingSettings.hooks[hookKey] = [{
4067
+ hooks: [{ type: 'command', command: hookCommand }]
4068
+ }];
4068
4069
 
4069
4070
  if (!existingSettings.$schema) {
4070
4071
  existingSettings.$schema = templateSettings.$schema;
@@ -5045,8 +5046,8 @@ async function updateCommandFiles(targetDir, spinner) {
5045
5046
  * These hooks contain bug fixes (e.g. markdown stripping) that must propagate
5046
5047
  * on every `npx agentvibes update` regardless of target directory.
5047
5048
  */
5048
- const CRITICAL_HOOKS = ['stop-tts.sh', 'stop.sh', 'play-tts.sh', 'session-start-tts.sh', 'bmad-party-speak.sh'];
5049
- const CRITICAL_HOOKS_WINDOWS = ['play-tts.ps1', 'session-start-tts.ps1', 'bmad-speak.ps1', 'bmad-party-speak.ps1'];
5049
+ const CRITICAL_HOOKS = ['stop-tts.sh', 'stop.sh', 'play-tts.sh', 'play-tts-piper.sh', 'audio-processor.sh', 'session-start-tts.sh', 'bmad-party-speak.sh'];
5050
+ const CRITICAL_HOOKS_WINDOWS = ['play-tts.ps1', 'play-tts-piper.ps1', 'audio-processor.ps1', 'session-start-tts.ps1', 'bmad-speak.ps1', 'bmad-party-speak.ps1'];
5050
5051
 
5051
5052
  /**
5052
5053
  * Update critical hooks in the global ~/.claude/hooks/ directory if it exists.
@@ -5056,45 +5057,38 @@ const CRITICAL_HOOKS_WINDOWS = ['play-tts.ps1', 'session-start-tts.ps1', 'bmad-s
5056
5057
  * @returns {Promise<number>} Number of hooks updated
5057
5058
  */
5058
5059
  async function updateGlobalHooks(srcHooksDir, homeDirOverride) {
5059
- const globalHooksDir = path.join(homeDirOverride || os.homedir(), '.claude', 'hooks');
5060
+ const homeDir = homeDirOverride || os.homedir();
5061
+ const globalHooksDir = path.join(homeDir, '.claude', 'hooks');
5060
5062
  let updated = 0;
5061
- try {
5062
- await fs.access(globalHooksDir);
5063
- } catch {
5064
- return 0; // global hooks dir not present — nothing to do
5065
- }
5063
+
5064
+ // Always ensure the global hooks dir exists so registered $HOME hooks resolve.
5065
+ await fs.mkdir(globalHooksDir, { recursive: true });
5066
5066
 
5067
5067
  for (const hook of CRITICAL_HOOKS) {
5068
5068
  const destPath = path.join(globalHooksDir, hook);
5069
5069
  const srcPath = path.join(srcHooksDir, hook);
5070
5070
  try {
5071
- await fs.access(destPath); // only update if already installed
5072
5071
  await fs.copyFile(srcPath, destPath);
5073
5072
  await fs.chmod(destPath, 0o750);
5074
5073
  updated++;
5075
5074
  } catch {
5076
- // file not in global dir or src missing — skip silently
5075
+ // src missing — skip silently
5077
5076
  }
5078
5077
  }
5079
5078
 
5080
- // Also update Windows global hooks-windows dir if present
5081
- const globalHooksWindowsDir = path.join(homeDirOverride || os.homedir(), '.claude', 'hooks-windows');
5079
+ // Also ensure Windows global hooks-windows dir and scripts are up to date.
5080
+ const globalHooksWindowsDir = path.join(homeDir, '.claude', 'hooks-windows');
5082
5081
  const srcHooksWindowsDir = path.join(path.dirname(srcHooksDir), 'hooks-windows');
5083
- try {
5084
- await fs.access(globalHooksWindowsDir);
5085
- for (const hook of CRITICAL_HOOKS_WINDOWS) {
5086
- const destPath = path.join(globalHooksWindowsDir, hook);
5087
- const srcPath = path.join(srcHooksWindowsDir, hook);
5088
- try {
5089
- await fs.access(destPath); // only update if already installed
5090
- await fs.copyFile(srcPath, destPath);
5091
- updated++;
5092
- } catch {
5093
- // file not in global dir or src missing — skip silently
5094
- }
5082
+ await fs.mkdir(globalHooksWindowsDir, { recursive: true });
5083
+ for (const hook of CRITICAL_HOOKS_WINDOWS) {
5084
+ const destPath = path.join(globalHooksWindowsDir, hook);
5085
+ const srcPath = path.join(srcHooksWindowsDir, hook);
5086
+ try {
5087
+ await fs.copyFile(srcPath, destPath);
5088
+ updated++;
5089
+ } catch {
5090
+ // src missing — skip silently
5095
5091
  }
5096
- } catch {
5097
- // hooks-windows dir not present — nothing to do
5098
5092
  }
5099
5093
 
5100
5094
  return updated;
@@ -5337,6 +5331,13 @@ async function install(options = {}) {
5337
5331
  await copyBackgroundMusicFiles(targetDir, silentSpinner);
5338
5332
  await copyConfigFiles(targetDir, silentSpinner);
5339
5333
  await copyCodexFiles(targetDir, silentSpinner);
5334
+
5335
+ // Populate global ~/.claude/hooks[/-windows]/ so $HOME hook paths resolve
5336
+ // on first install (not just on update).
5337
+ const hooksSubdirInstall = isNativeWindows() ? 'hooks-windows' : 'hooks';
5338
+ const srcHooksDirInstall = path.join(__dirname, '..', '.claude', hooksSubdirInstall);
5339
+ await updateGlobalHooks(srcHooksDirInstall);
5340
+
5340
5341
  await configureSessionStartHook(targetDir, silentSpinner);
5341
5342
  await configurePartyModeHook(targetDir, silentSpinner);
5342
5343
  await installPluginManifest(targetDir, silentSpinner);
@@ -92,8 +92,17 @@ const DEFAULT_LLM_CONFIGS = {
92
92
  };
93
93
 
94
94
  function ensureDefaultLlmConfigSync(llmKey, targetDir) {
95
- const existing = loadLlmConfigSync(llmKey, targetDir);
96
- if (existing.sourcePath) return;
95
+ // Check only the project dir — a global config row must not prevent seeding
96
+ // per-project defaults (otherwise new installs silently inherit a different
97
+ // project's voice/pretext and the per-LLM piper engine is never written,
98
+ // causing play-tts.ps1 to fall through to the global SAPI provider).
99
+ const resolvedDir = targetDir || process.env.INIT_CWD || process.cwd();
100
+ const projectCfgPath = path.join(resolvedDir, '.claude', 'config', 'audio-effects.cfg');
101
+ const cfgKey = `llm:${llmKey}`;
102
+ try {
103
+ const content = fsSync.readFileSync(projectCfgPath, 'utf8');
104
+ if (content.split('\n').some(line => line.startsWith(cfgKey + '|'))) return;
105
+ } catch { /* file not found — continue to seed */ }
97
106
 
98
107
  const defaults = DEFAULT_LLM_CONFIGS[llmKey];
99
108
  if (!defaults) return;
@@ -101,6 +110,17 @@ function ensureDefaultLlmConfigSync(llmKey, targetDir) {
101
110
  saveLlmConfigSync(llmKey, defaults, targetDir);
102
111
  }
103
112
 
113
+ /**
114
+ * Seed piper defaults for every LLM in DEFAULT_LLM_CONFIGS into the project dir.
115
+ * Called at install time so play-tts.ps1 always finds a per-LLM piper row in the
116
+ * project config and never silently falls back to the global windows-sapi provider.
117
+ */
118
+ export function seedAllLlmDefaultsSync(targetDir) {
119
+ for (const llmKey of Object.keys(DEFAULT_LLM_CONFIGS)) {
120
+ ensureDefaultLlmConfigSync(llmKey, targetDir);
121
+ }
122
+ }
123
+
104
124
  // ── Provider install-checks ─────────────────────────────────────────────────
105
125
 
106
126
  export async function checkClaudeInstalled(targetDir) {
@@ -902,13 +922,12 @@ export function saveLlmConfigSync(llmKey, config, targetDir) {
902
922
  const sanitize = (v) => (v || '').replace(/[\|\n\r\x00]/g, '');
903
923
  const cfgLine = `${cfgKey}|${sanitize(config.effects)}|${sanitize(config.bgTrack)}|${sanitize(config.bgVolume)}|${sanitize(config.voice)}|${sanitize(config.pretext)}|${sanitize(config.ttsEngine)}`;
904
924
  const resolvedTargetDir = targetDir || process.env.INIT_CWD || process.cwd();
905
- // When targetDir is explicitly passed, write there directly (do not fall back to global).
906
- // resolveCfgPath falls back to ~/.claude when the local file doesn't yet exist,
907
- // which would silently redirect writes away from the intended directory.
908
- const cfgPath = config.sourcePath ||
909
- (targetDir
910
- ? path.join(resolvedTargetDir, '.claude', 'config', 'audio-effects.cfg')
911
- : resolveCfgPath(resolvedTargetDir));
925
+ // When targetDir is explicitly passed, always write to the project dir never follow
926
+ // config.sourcePath back to the global ~/.claude/config (it may have been loaded from there
927
+ // as a "default seed" for a new project, and writing back would pollute other projects).
928
+ const cfgPath = targetDir
929
+ ? path.join(resolvedTargetDir, '.claude', 'config', 'audio-effects.cfg')
930
+ : (config.sourcePath || resolveCfgPath(resolvedTargetDir));
912
931
 
913
932
  try {
914
933
  let content = '';
@@ -35,6 +35,8 @@ export function uniquifyVoiceName(rawName) {
35
35
  const idx = (n - 1) % SURNAME_POOL.length;
36
36
  return `${base} ${SURNAME_POOL[idx]}`;
37
37
  }
38
+ // n=1 (or 0): strip the suffix — "Mary-1" → "Mary Bell" is cleaner than "Mary-1 Bell"
39
+ return `${base} ${SURNAME_POOL[0]}`;
38
40
  }
39
41
  if (/\s/.test(rawName)) return rawName;
40
42
  return `${rawName} ${SURNAME_POOL[0]}`;
File without changes
File without changes
@@ -1 +0,0 @@
1
- play-tts-agentvibes-receiver-for-voiceless-connections.sh