agentvibes 5.5.0 → 5.6.1

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 (100) hide show
  1. package/.agentvibes/config.json +3 -30
  2. package/.claude/config/background-music-enabled.txt +1 -1
  3. package/.claude/config/background-music-position.txt +6 -6
  4. package/.claude/github-star-reminder.txt +1 -1
  5. package/.claude/hooks/play-tts-ssh-remote.sh +119 -42
  6. package/.claude/hooks/play-tts-windows-receiver.sh +31 -0
  7. package/.claude/hooks/stop.sh +2 -27
  8. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -108
  9. package/.claude/hooks-windows/play-tts.ps1 +23 -7
  10. package/.claude/piper-voices-dir.txt +1 -1
  11. package/.clawdbot/skill/README.md +326 -0
  12. package/.mcp.json +17 -27
  13. package/README.md +73 -82
  14. package/RELEASE_NOTES.md +61 -0
  15. package/bin/agent-vibes +39 -39
  16. package/package.json +1 -1
  17. package/src/bmad-detector.js +71 -71
  18. package/src/cli/list-personalities.js +110 -110
  19. package/src/cli/list-voices.js +114 -114
  20. package/src/commands/bmad-voices.js +394 -394
  21. package/src/commands/install-mcp.js +476 -476
  22. package/src/console/brand-colors.js +13 -13
  23. package/src/console/constants/personalities.js +44 -44
  24. package/src/console/modals/modal-overlay.js +247 -247
  25. package/src/console/navigation.js +5 -1
  26. package/src/console/tabs/agents-tab.js +5 -5
  27. package/src/console/tabs/help-tab.js +314 -314
  28. package/src/console/tabs/readme-tab.js +272 -272
  29. package/src/console/tabs/setup-tab.js +32 -17
  30. package/src/console/tabs/voices-tab.js +2 -2
  31. package/src/console/widgets/destroy-list.js +25 -25
  32. package/src/console/widgets/notice.js +55 -55
  33. package/src/console/widgets/personality-picker.js +213 -213
  34. package/src/console/widgets/reverb-picker.js +97 -97
  35. package/src/console/widgets/track-picker.js +1 -1
  36. package/src/i18n/de.js +202 -202
  37. package/src/i18n/es.js +202 -202
  38. package/src/i18n/fr.js +202 -202
  39. package/src/i18n/hi.js +202 -202
  40. package/src/i18n/ja.js +202 -202
  41. package/src/i18n/ko.js +202 -202
  42. package/src/i18n/pt.js +202 -202
  43. package/src/i18n/strings.js +54 -54
  44. package/src/i18n/zh-CN.js +202 -202
  45. package/src/installer/language-screen.js +31 -31
  46. package/src/installer/music-file-input.js +304 -304
  47. package/src/services/agent-voice-store.js +420 -423
  48. package/src/services/config-service.js +264 -264
  49. package/src/services/language-service.js +47 -47
  50. package/src/services/llm-provider-service.js +11 -4
  51. package/src/services/navigation-service.js +34 -10
  52. package/src/services/provider-service.js +143 -143
  53. package/src/utils/audio-duration-validator.js +298 -298
  54. package/src/utils/audio-format-validator.js +277 -277
  55. package/src/utils/dependency-checker.js +469 -469
  56. package/src/utils/file-ownership-verifier.js +358 -358
  57. package/src/utils/list-formatter.js +194 -194
  58. package/src/utils/music-file-validator.js +285 -285
  59. package/src/utils/preview-list-prompt.js +136 -136
  60. package/src/utils/secure-music-storage.js +412 -412
  61. package/.agentvibes/LITE-MODE.md +0 -236
  62. package/.agentvibes/README.md +0 -136
  63. package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +0 -141
  64. package/.agentvibes/backups/agents/analyst_20260204_144958.md +0 -78
  65. package/.agentvibes/backups/agents/architect_20260204_144958.md +0 -72
  66. package/.agentvibes/backups/agents/dev_20260204_144958.md +0 -74
  67. package/.agentvibes/backups/agents/pm_20260204_144958.md +0 -72
  68. package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +0 -64
  69. package/.agentvibes/backups/agents/sm_20260204_144958.md +0 -87
  70. package/.agentvibes/backups/agents/tea_20260204_144958.md +0 -79
  71. package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +0 -82
  72. package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +0 -80
  73. package/.agentvibes/config/README-personality-defaults.md +0 -162
  74. package/.agentvibes/config/agentvibes.json +0 -1
  75. package/.agentvibes/config/mode.txt +0 -1
  76. package/.agentvibes/config/personality-voice-defaults.default.json +0 -21
  77. package/.agentvibes/config/save-audio.txt +0 -1
  78. package/.agentvibes/config/voice-metadata.json +0 -160
  79. package/.agentvibes/hooks/help.sh +0 -191
  80. package/.agentvibes/hooks/post-tool-use-lite.sh +0 -111
  81. package/.agentvibes/hooks/save-audio-manager.sh +0 -162
  82. package/.agentvibes/hooks/session-start-full-optimized.sh +0 -102
  83. package/.agentvibes/hooks/session-start-full.sh +0 -142
  84. package/.agentvibes/hooks/session-start-lite-v2.sh +0 -34
  85. package/.agentvibes/hooks/session-start-lite.sh +0 -29
  86. package/.agentvibes/hooks/stop-lite.sh +0 -115
  87. package/.agentvibes/hooks/switch-mode.sh +0 -215
  88. package/.agentvibes/output-styles/audio-summary.md +0 -30
  89. package/.claude/audio/voice-samples/piper/alan.wav +0 -0
  90. package/.claude/audio/voice-samples/piper/amy.wav +0 -0
  91. package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
  92. package/.claude/audio/voice-samples/piper/joe.wav +0 -0
  93. package/.claude/audio/voice-samples/piper/john.wav +0 -0
  94. package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
  95. package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
  96. package/.claude/audio/voice-samples/piper/linda.wav +0 -0
  97. package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
  98. package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
  99. package/.claude/hooks/post-response.sh +0 -41
  100. package/bin/ensure-soprano-running.sh +0 -43
@@ -17,7 +17,8 @@ export class NavigationService {
17
17
  this._activeTab = TAB_ORDER.includes(initialTab) ? initialTab : 'settings';
18
18
  this._switchCallbacks = [];
19
19
  this._focusStack = [];
20
- this._modalOpen = false;
20
+ this._modalDepth = 0;
21
+ this._modalCloseCallbacks = [];
21
22
  }
22
23
 
23
24
  // ---------------------------------------------------------------------------
@@ -79,25 +80,48 @@ export class NavigationService {
79
80
  }
80
81
 
81
82
  // ---------------------------------------------------------------------------
82
- // Modal state (story 6.4 will expand this)
83
+ // Modal state
83
84
 
84
- /** Returns true if a modal is currently open */
85
+ /** Returns true if one or more modals are currently open */
85
86
  isModalOpen() {
86
- return this._modalOpen;
87
+ return this._modalDepth > 0;
87
88
  }
88
89
 
89
90
  /**
90
- * Open a modal. Sets modal-open state and calls the factory fn if provided.
91
- * @param {Function|null} fn - Optional factory/callback invoked immediately
91
+ * Open a modal. Increments depth counter and registers an optional close
92
+ * callback so nav hotkeys can force-close all open modals.
93
+ * @param {Function|null} fn - Optional factory/callback invoked immediately
94
+ * @param {Function|null} closeFn - Optional callback to close this modal's UI
92
95
  */
93
- openModal(fn) {
94
- this._modalOpen = true;
96
+ openModal(fn, closeFn) {
97
+ this._modalDepth++;
98
+ this._modalCloseCallbacks.push(closeFn ?? null);
95
99
  fn?.();
96
100
  }
97
101
 
98
- /** Close the current modal, restoring modal-closed state */
102
+ /**
103
+ * Close the topmost modal, decrementing the depth counter.
104
+ * Safe to call when depth is already 0.
105
+ */
99
106
  closeModal() {
100
- this._modalOpen = false;
107
+ if (this._modalDepth > 0) this._modalDepth--;
108
+ if (this._modalCloseCallbacks.length > 0) this._modalCloseCallbacks.pop();
109
+ }
110
+
111
+ /**
112
+ * Force-close all open modals by calling their registered close callbacks
113
+ * from innermost to outermost, then reset depth to 0.
114
+ * Used by nav hotkeys (S/V/M/…) to dismiss modals before switching tabs.
115
+ */
116
+ forceCloseAll() {
117
+ const callbacks = [...this._modalCloseCallbacks].reverse();
118
+ this._modalDepth = 0;
119
+ this._modalCloseCallbacks = [];
120
+ for (const cb of callbacks) {
121
+ if (typeof cb === 'function') {
122
+ try { cb(); } catch {}
123
+ }
124
+ }
101
125
  }
102
126
 
103
127
 
@@ -1,143 +1,143 @@
1
- /**
2
- * AgentVibes Provider Service
3
- * Story 7.1: Provider & Voice Settings Group
4
- *
5
- * Detects installed TTS providers, reads/writes active provider and voice
6
- * through ConfigService. Gracefully degrades when detection fails.
7
- */
8
-
9
- import { execFileSync } from 'node:child_process';
10
- import fs from 'node:fs';
11
- import path from 'node:path';
12
- import os from 'node:os';
13
-
14
- export class ProviderService {
15
- /**
16
- * @param {import('./config-service.js').ConfigService} configService
17
- */
18
- constructor(configService) {
19
- this._config = configService;
20
- this._installedProviders = null; // cached after first detection
21
- }
22
-
23
- // ---------------------------------------------------------------------------
24
- // Provider
25
-
26
- /**
27
- * Returns the currently active TTS provider from config.
28
- * Defaults to 'piper' if not configured.
29
- * @returns {string}
30
- */
31
- getActiveProvider() {
32
- return this._config.getConfig().provider ?? 'piper';
33
- }
34
-
35
- /**
36
- * Sets the active TTS provider in config AND syncs to .claude/tts-provider.txt
37
- * so the shell hooks (play-tts.sh → provider-manager.sh) pick up the change.
38
- * @param {string} provider
39
- */
40
- setActiveProvider(provider) {
41
- this._config.set('provider', provider);
42
- this._config.setGlobal('provider', provider);
43
- this._syncProviderFile(provider);
44
- }
45
-
46
- /**
47
- * Write provider to .claude/tts-provider.txt so shell hooks stay in sync.
48
- * Writes to projectRoot/.claude/tts-provider.txt if .claude/ exists there,
49
- * otherwise falls back to ~/.claude/tts-provider.txt.
50
- * @param {string} provider
51
- */
52
- _syncProviderFile(provider) {
53
- try {
54
- const projectClaudeDir = path.resolve(this._config.getProjectRoot(), '.claude');
55
- const targetDir = fs.existsSync(projectClaudeDir)
56
- ? projectClaudeDir
57
- : path.resolve(os.homedir(), '.claude');
58
- const targetFile = path.resolve(targetDir, 'tts-provider.txt');
59
- // Verify resolved path stays within targetDir (path traversal guard)
60
- if (!targetFile.startsWith(targetDir + path.sep) && targetFile !== targetDir) return;
61
- fs.mkdirSync(targetDir, { recursive: true });
62
- fs.writeFileSync(targetFile, provider, 'utf8');
63
- } catch {
64
- // Non-fatal — config.json is the authoritative source
65
- }
66
- }
67
-
68
- /**
69
- * Returns an array of installed/available TTS providers.
70
- * Detection uses `which` binary check. Always returns at least ['piper']
71
- * as graceful degradation (piper is the primary supported provider).
72
- * @returns {string[]}
73
- */
74
- getInstalledProviders() {
75
- if (this._installedProviders) return this._installedProviders;
76
-
77
- const providers = [];
78
-
79
- if (this._isAvailable('piper')) providers.push('piper');
80
- if (this._isAvailable('soprano')) providers.push('soprano');
81
-
82
- // macOS Say (darwin only)
83
- if (process.platform === 'darwin' && this._isAvailable('say')) {
84
- providers.push('macos');
85
- }
86
-
87
- // Graceful degradation: always return at least piper
88
- if (providers.length === 0) providers.push('piper');
89
-
90
- this._installedProviders = providers;
91
- return providers;
92
- }
93
-
94
- // ---------------------------------------------------------------------------
95
- // Voice
96
-
97
- /**
98
- * Returns the currently active voice ID from config.
99
- * Falls back to first installed voice if not configured.
100
- * @returns {string|null}
101
- */
102
- getActiveVoiceId() {
103
- const voice = this._config.getConfig().voice;
104
- if (voice) return voice;
105
- // Detect first installed voice instead of hardcoding a default that may not exist
106
- const voicesDir = path.join(os.homedir(), '.claude', 'piper-voices');
107
- try {
108
- const models = fs.readdirSync(voicesDir).filter(f => f.endsWith('.onnx'));
109
- if (models.length > 0) return models[0].replace(/\.onnx$/, '');
110
- } catch { /* dir may not exist */ }
111
- return null;
112
- }
113
-
114
- /**
115
- * Sets the active voice ID in config.
116
- * Writes to both project (if exists) and global config for portability.
117
- * @param {string} voiceId
118
- */
119
- setActiveVoice(voiceId) {
120
- this._config.set('voice', voiceId);
121
- this._config.setGlobal('voice', voiceId);
122
- }
123
-
124
- // ---------------------------------------------------------------------------
125
- // Private
126
-
127
- /**
128
- * Check if a binary is available in PATH using `which`.
129
- * Binary names are all hardcoded (not user input) — safe from injection.
130
- * @param {string} binary - hardcoded binary name ('piper', 'soprano', 'say')
131
- * @returns {boolean}
132
- */
133
- _isAvailable(binary) {
134
- try {
135
- execFileSync('which', [binary], { stdio: 'ignore', timeout: 2000 });
136
- return true;
137
- } catch {
138
- return false;
139
- }
140
- }
141
- }
142
-
143
- export default ProviderService;
1
+ /**
2
+ * AgentVibes Provider Service
3
+ * Story 7.1: Provider & Voice Settings Group
4
+ *
5
+ * Detects installed TTS providers, reads/writes active provider and voice
6
+ * through ConfigService. Gracefully degrades when detection fails.
7
+ */
8
+
9
+ import { execFileSync } from 'node:child_process';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import os from 'node:os';
13
+
14
+ export class ProviderService {
15
+ /**
16
+ * @param {import('./config-service.js').ConfigService} configService
17
+ */
18
+ constructor(configService) {
19
+ this._config = configService;
20
+ this._installedProviders = null; // cached after first detection
21
+ }
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Provider
25
+
26
+ /**
27
+ * Returns the currently active TTS provider from config.
28
+ * Defaults to 'piper' if not configured.
29
+ * @returns {string}
30
+ */
31
+ getActiveProvider() {
32
+ return this._config.getConfig().provider ?? 'piper';
33
+ }
34
+
35
+ /**
36
+ * Sets the active TTS provider in config AND syncs to .claude/tts-provider.txt
37
+ * so the shell hooks (play-tts.sh → provider-manager.sh) pick up the change.
38
+ * @param {string} provider
39
+ */
40
+ setActiveProvider(provider) {
41
+ this._config.set('provider', provider);
42
+ this._config.setGlobal('provider', provider);
43
+ this._syncProviderFile(provider);
44
+ }
45
+
46
+ /**
47
+ * Write provider to .claude/tts-provider.txt so shell hooks stay in sync.
48
+ * Writes to projectRoot/.claude/tts-provider.txt if .claude/ exists there,
49
+ * otherwise falls back to ~/.claude/tts-provider.txt.
50
+ * @param {string} provider
51
+ */
52
+ _syncProviderFile(provider) {
53
+ try {
54
+ const projectClaudeDir = path.resolve(this._config.getProjectRoot(), '.claude');
55
+ const targetDir = fs.existsSync(projectClaudeDir)
56
+ ? projectClaudeDir
57
+ : path.resolve(os.homedir(), '.claude');
58
+ const targetFile = path.resolve(targetDir, 'tts-provider.txt');
59
+ // Verify resolved path stays within targetDir (path traversal guard)
60
+ if (!targetFile.startsWith(targetDir + path.sep) && targetFile !== targetDir) return;
61
+ fs.mkdirSync(targetDir, { recursive: true });
62
+ fs.writeFileSync(targetFile, provider, 'utf8');
63
+ } catch {
64
+ // Non-fatal — config.json is the authoritative source
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Returns an array of installed/available TTS providers.
70
+ * Detection uses `which` binary check. Always returns at least ['piper']
71
+ * as graceful degradation (piper is the primary supported provider).
72
+ * @returns {string[]}
73
+ */
74
+ getInstalledProviders() {
75
+ if (this._installedProviders) return this._installedProviders;
76
+
77
+ const providers = [];
78
+
79
+ if (this._isAvailable('piper')) providers.push('piper');
80
+ if (this._isAvailable('soprano')) providers.push('soprano');
81
+
82
+ // macOS Say (darwin only)
83
+ if (process.platform === 'darwin' && this._isAvailable('say')) {
84
+ providers.push('macos');
85
+ }
86
+
87
+ // Graceful degradation: always return at least piper
88
+ if (providers.length === 0) providers.push('piper');
89
+
90
+ this._installedProviders = providers;
91
+ return providers;
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Voice
96
+
97
+ /**
98
+ * Returns the currently active voice ID from config.
99
+ * Falls back to first installed voice if not configured.
100
+ * @returns {string|null}
101
+ */
102
+ getActiveVoiceId() {
103
+ const voice = this._config.getConfig().voice;
104
+ if (voice) return voice;
105
+ // Detect first installed voice instead of hardcoding a default that may not exist
106
+ const voicesDir = path.join(os.homedir(), '.claude', 'piper-voices');
107
+ try {
108
+ const models = fs.readdirSync(voicesDir).filter(f => f.endsWith('.onnx'));
109
+ if (models.length > 0) return models[0].replace(/\.onnx$/, '');
110
+ } catch { /* dir may not exist */ }
111
+ return null;
112
+ }
113
+
114
+ /**
115
+ * Sets the active voice ID in config.
116
+ * Writes to both project (if exists) and global config for portability.
117
+ * @param {string} voiceId
118
+ */
119
+ setActiveVoice(voiceId) {
120
+ this._config.set('voice', voiceId);
121
+ this._config.setGlobal('voice', voiceId);
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Private
126
+
127
+ /**
128
+ * Check if a binary is available in PATH using `which`.
129
+ * Binary names are all hardcoded (not user input) — safe from injection.
130
+ * @param {string} binary - hardcoded binary name ('piper', 'soprano', 'say')
131
+ * @returns {boolean}
132
+ */
133
+ _isAvailable(binary) {
134
+ try {
135
+ execFileSync('which', [binary], { stdio: 'ignore', timeout: 2000 });
136
+ return true;
137
+ } catch {
138
+ return false;
139
+ }
140
+ }
141
+ }
142
+
143
+ export default ProviderService;