neuro-cli 4.1.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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. package/package.json +83 -0
@@ -0,0 +1,137 @@
1
+ export interface VoiceConfig {
2
+ /** Whether voice I/O is enabled */
3
+ enabled: boolean;
4
+ /** TTS engine to use */
5
+ ttsEngine: 'system' | 'espeak' | 'say' | 'auto';
6
+ /** STT engine to use */
7
+ sttEngine: 'system' | 'whisper' | 'auto';
8
+ /** TTS voice name */
9
+ voiceName: string;
10
+ /** TTS speech rate (words per minute or 0.5-2.0) */
11
+ speechRate: number;
12
+ /** TTS volume (0-100 or 0.0-1.0) */
13
+ volume: number;
14
+ /** TTS pitch adjustment */
15
+ pitch: number;
16
+ /** Language for TTS/STT */
17
+ language: string;
18
+ /** Whether to auto-speak assistant responses */
19
+ autoSpeak: boolean;
20
+ /** Whether to listen for voice commands continuously */
21
+ continuousListening: boolean;
22
+ /** Voice command prefix (e.g. "hey neuro") */
23
+ wakeWord: string;
24
+ /** STT silence timeout in ms */
25
+ silenceTimeout: number;
26
+ }
27
+ export interface VoiceCommand {
28
+ command: string;
29
+ pattern: RegExp;
30
+ handler: (match: RegExpMatchArray) => void;
31
+ description: string;
32
+ }
33
+ export interface VoiceRecognitionResult {
34
+ text: string;
35
+ confidence: number;
36
+ isCommand: boolean;
37
+ commandName?: string;
38
+ }
39
+ export declare class VoiceIO {
40
+ private config;
41
+ private activeTTSProcess;
42
+ private isSpeaking;
43
+ private isListening;
44
+ private voiceCommands;
45
+ private ttsAvailable;
46
+ private sttAvailable;
47
+ private speechQueue;
48
+ private onTranscript;
49
+ constructor(config?: Partial<VoiceConfig>);
50
+ /**
51
+ * Check if voice I/O is enabled
52
+ */
53
+ isEnabled(): boolean;
54
+ /**
55
+ * Enable voice I/O
56
+ */
57
+ enable(): void;
58
+ /**
59
+ * Disable voice I/O
60
+ */
61
+ disable(): void;
62
+ /**
63
+ * Toggle voice I/O
64
+ */
65
+ toggle(): boolean;
66
+ /**
67
+ * Speak text using TTS
68
+ */
69
+ speak(text: string): Promise<boolean>;
70
+ /**
71
+ * Speak text asynchronously (queue it)
72
+ */
73
+ speakAsync(text: string): void;
74
+ /**
75
+ * Stop current TTS
76
+ */
77
+ stopSpeaking(): void;
78
+ /**
79
+ * Check if currently speaking
80
+ */
81
+ getIsSpeaking(): boolean;
82
+ /**
83
+ * Start listening for voice input
84
+ */
85
+ startListening(callback: (text: string) => void): Promise<boolean>;
86
+ /**
87
+ * Stop listening for voice input
88
+ */
89
+ stopListening(): void;
90
+ /**
91
+ * Check if currently listening
92
+ */
93
+ getIsListening(): boolean;
94
+ /**
95
+ * Process a voice transcript for commands
96
+ */
97
+ processVoiceInput(text: string): VoiceRecognitionResult;
98
+ /**
99
+ * Register a voice command
100
+ */
101
+ registerCommand(command: VoiceCommand): void;
102
+ /**
103
+ * List registered voice commands
104
+ */
105
+ listCommands(): VoiceCommand[];
106
+ /**
107
+ * Set TTS engine
108
+ */
109
+ setTTSEngine(engine: VoiceConfig['ttsEngine']): void;
110
+ /**
111
+ * Set STT engine
112
+ */
113
+ setSTTEngine(engine: VoiceConfig['sttEngine']): void;
114
+ /**
115
+ * Set voice parameters
116
+ */
117
+ setVoice(params: Partial<Pick<VoiceConfig, 'voiceName' | 'speechRate' | 'volume' | 'pitch' | 'language'>>): void;
118
+ /**
119
+ * List available TTS voices
120
+ */
121
+ listVoices(): string[];
122
+ /**
123
+ * Get config
124
+ */
125
+ getConfig(): VoiceConfig;
126
+ /**
127
+ * Print voice status
128
+ */
129
+ printStatus(): void;
130
+ private checkTTSAvailability;
131
+ private checkSTTAvailability;
132
+ private cleanTextForTTS;
133
+ private registerDefaultCommands;
134
+ private saveConfig;
135
+ private loadConfig;
136
+ }
137
+ //# sourceMappingURL=voice.d.ts.map
@@ -0,0 +1,538 @@
1
+ // ============================================================
2
+ // NeuroCLI - Voice I/O System
3
+ // Text-to-speech output, speech-to-text input,
4
+ // voice command recognition, /voice on/off
5
+ // Configurable voice settings
6
+ // ============================================================
7
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { homedir } from 'os';
10
+ import { execSync, spawn } from 'child_process';
11
+ import chalk from 'chalk';
12
+ // -----------------------------------------------------------
13
+ // Default config
14
+ // -----------------------------------------------------------
15
+ const VOICE_CONFIG_PATH = join(homedir(), '.neuro', 'voice-config.json');
16
+ function detectTTSEngine() {
17
+ // macOS has 'say' built in
18
+ if (process.platform === 'darwin')
19
+ return 'say';
20
+ // Check for espeak on Linux
21
+ try {
22
+ execSync('which espeak', { stdio: 'ignore' });
23
+ return 'espeak';
24
+ }
25
+ catch { /* not found */ }
26
+ try {
27
+ execSync('which espeak-ng', { stdio: 'ignore' });
28
+ return 'espeak';
29
+ }
30
+ catch { /* not found */ }
31
+ return 'system';
32
+ }
33
+ function detectSTTEngine() {
34
+ try {
35
+ execSync('which whisper', { stdio: 'ignore' });
36
+ return 'whisper';
37
+ }
38
+ catch { /* not found */ }
39
+ return 'system';
40
+ }
41
+ function defaultConfig() {
42
+ return {
43
+ enabled: false,
44
+ ttsEngine: 'auto',
45
+ sttEngine: 'auto',
46
+ voiceName: '',
47
+ speechRate: 1.0,
48
+ volume: 0.8,
49
+ pitch: 1.0,
50
+ language: 'en',
51
+ autoSpeak: false,
52
+ continuousListening: false,
53
+ wakeWord: 'hey neuro',
54
+ silenceTimeout: 3000,
55
+ };
56
+ }
57
+ // -----------------------------------------------------------
58
+ // VoiceIO
59
+ // -----------------------------------------------------------
60
+ export class VoiceIO {
61
+ config;
62
+ activeTTSProcess = null;
63
+ isSpeaking = false;
64
+ isListening = false;
65
+ voiceCommands = [];
66
+ ttsAvailable = false;
67
+ sttAvailable = false;
68
+ speechQueue = [];
69
+ onTranscript = null;
70
+ constructor(config) {
71
+ this.config = { ...defaultConfig(), ...config };
72
+ this.loadConfig();
73
+ // Auto-detect engines
74
+ if (this.config.ttsEngine === 'auto') {
75
+ this.config.ttsEngine = detectTTSEngine();
76
+ }
77
+ if (this.config.sttEngine === 'auto') {
78
+ this.config.sttEngine = detectSTTEngine();
79
+ }
80
+ // Check availability
81
+ this.checkTTSAvailability();
82
+ this.checkSTTAvailability();
83
+ // Register default voice commands
84
+ this.registerDefaultCommands();
85
+ }
86
+ // ----------------------------------------------------------
87
+ // Public API
88
+ // ----------------------------------------------------------
89
+ /**
90
+ * Check if voice I/O is enabled
91
+ */
92
+ isEnabled() {
93
+ return this.config.enabled;
94
+ }
95
+ /**
96
+ * Enable voice I/O
97
+ */
98
+ enable() {
99
+ if (!this.ttsAvailable && !this.sttAvailable) {
100
+ console.log(chalk.yellow('No TTS/STT engines found. Install espeak or use macOS for TTS, or whisper for STT.'));
101
+ console.log(chalk.gray('Voice I/O enabled in limited mode (will use system APIs if available).'));
102
+ }
103
+ this.config.enabled = true;
104
+ this.saveConfig();
105
+ console.log(chalk.green('Voice I/O enabled.'));
106
+ }
107
+ /**
108
+ * Disable voice I/O
109
+ */
110
+ disable() {
111
+ this.stopSpeaking();
112
+ this.stopListening();
113
+ this.config.enabled = false;
114
+ this.saveConfig();
115
+ console.log(chalk.gray('Voice I/O disabled.'));
116
+ }
117
+ /**
118
+ * Toggle voice I/O
119
+ */
120
+ toggle() {
121
+ if (this.config.enabled)
122
+ this.disable();
123
+ else
124
+ this.enable();
125
+ return this.config.enabled;
126
+ }
127
+ /**
128
+ * Speak text using TTS
129
+ */
130
+ async speak(text) {
131
+ if (!this.config.enabled || !this.ttsAvailable)
132
+ return false;
133
+ // Clean text for TTS (remove markdown, code blocks, etc.)
134
+ const cleanText = this.cleanTextForTTS(text);
135
+ if (!cleanText.trim())
136
+ return false;
137
+ return new Promise((resolve) => {
138
+ try {
139
+ let command;
140
+ let args;
141
+ switch (this.config.ttsEngine) {
142
+ case 'say': // macOS
143
+ command = 'say';
144
+ args = [];
145
+ if (this.config.voiceName)
146
+ args.push('-v', this.config.voiceName);
147
+ if (this.config.speechRate !== 1.0)
148
+ args.push('-r', String(Math.round(this.config.speechRate * 200)));
149
+ args.push(cleanText);
150
+ break;
151
+ case 'espeak':
152
+ command = 'espeak';
153
+ args = [];
154
+ if (this.config.voiceName)
155
+ args.push('-v', this.config.voiceName);
156
+ if (this.config.speechRate > 0)
157
+ args.push('-s', String(Math.round(this.config.speechRate * 175)));
158
+ if (this.config.pitch !== 1.0)
159
+ args.push('-p', String(Math.round(this.config.pitch * 50)));
160
+ if (this.config.volume < 1.0)
161
+ args.push('-a', String(Math.round(this.config.volume * 100)));
162
+ args.push(cleanText);
163
+ break;
164
+ default:
165
+ // Try system default
166
+ if (process.platform === 'darwin') {
167
+ command = 'say';
168
+ args = [cleanText];
169
+ }
170
+ else {
171
+ resolve(false);
172
+ return;
173
+ }
174
+ }
175
+ this.isSpeaking = true;
176
+ const proc = spawn(command, args, { stdio: 'ignore' });
177
+ this.activeTTSProcess = proc;
178
+ proc.on('close', () => {
179
+ this.isSpeaking = false;
180
+ this.activeTTSProcess = null;
181
+ // Process queue
182
+ if (this.speechQueue.length > 0) {
183
+ const next = this.speechQueue.shift();
184
+ this.speak(next).then(resolve);
185
+ }
186
+ else {
187
+ resolve(true);
188
+ }
189
+ });
190
+ proc.on('error', () => {
191
+ this.isSpeaking = false;
192
+ this.activeTTSProcess = null;
193
+ resolve(false);
194
+ });
195
+ }
196
+ catch {
197
+ resolve(false);
198
+ }
199
+ });
200
+ }
201
+ /**
202
+ * Speak text asynchronously (queue it)
203
+ */
204
+ speakAsync(text) {
205
+ if (!this.config.enabled)
206
+ return;
207
+ if (this.isSpeaking) {
208
+ this.speechQueue.push(text);
209
+ }
210
+ else {
211
+ this.speak(text).catch(() => { });
212
+ }
213
+ }
214
+ /**
215
+ * Stop current TTS
216
+ */
217
+ stopSpeaking() {
218
+ if (this.activeTTSProcess) {
219
+ try {
220
+ this.activeTTSProcess.kill();
221
+ }
222
+ catch { /* ignore */ }
223
+ this.activeTTSProcess = null;
224
+ }
225
+ this.isSpeaking = false;
226
+ this.speechQueue = [];
227
+ }
228
+ /**
229
+ * Check if currently speaking
230
+ */
231
+ getIsSpeaking() {
232
+ return this.isSpeaking;
233
+ }
234
+ /**
235
+ * Start listening for voice input
236
+ */
237
+ async startListening(callback) {
238
+ if (!this.config.enabled || !this.sttAvailable)
239
+ return false;
240
+ this.onTranscript = callback;
241
+ this.isListening = true;
242
+ console.log(chalk.cyan('Listening... (speak now)'));
243
+ try {
244
+ switch (this.config.sttEngine) {
245
+ case 'whisper': {
246
+ // Use whisper CLI to record and transcribe
247
+ const proc = spawn('whisper', [
248
+ '--model', 'base',
249
+ '--language', this.config.language,
250
+ '--output_format', 'txt',
251
+ '--output_dir', '-',
252
+ ], { stdio: ['pipe', 'pipe', 'pipe'] });
253
+ let transcript = '';
254
+ proc.stdout.on('data', (data) => {
255
+ transcript += data.toString();
256
+ });
257
+ proc.on('close', () => {
258
+ this.isListening = false;
259
+ if (transcript.trim() && this.onTranscript) {
260
+ this.onTranscript(transcript.trim());
261
+ }
262
+ });
263
+ return true;
264
+ }
265
+ default: {
266
+ // System API fallback - use arecord + whisper or similar
267
+ console.log(chalk.yellow('System STT not fully supported. Consider installing whisper CLI.'));
268
+ this.isListening = false;
269
+ return false;
270
+ }
271
+ }
272
+ }
273
+ catch {
274
+ this.isListening = false;
275
+ return false;
276
+ }
277
+ }
278
+ /**
279
+ * Stop listening for voice input
280
+ */
281
+ stopListening() {
282
+ this.isListening = false;
283
+ this.onTranscript = null;
284
+ }
285
+ /**
286
+ * Check if currently listening
287
+ */
288
+ getIsListening() {
289
+ return this.isListening;
290
+ }
291
+ /**
292
+ * Process a voice transcript for commands
293
+ */
294
+ processVoiceInput(text) {
295
+ const lower = text.toLowerCase().trim();
296
+ // Check for wake word
297
+ if (this.config.wakeWord && !lower.startsWith(this.config.wakeWord.toLowerCase())) {
298
+ return {
299
+ text,
300
+ confidence: 1.0,
301
+ isCommand: false,
302
+ };
303
+ }
304
+ // Remove wake word
305
+ const commandText = this.config.wakeWord
306
+ ? lower.replace(this.config.wakeWord.toLowerCase(), '').trim()
307
+ : lower;
308
+ // Check voice commands
309
+ for (const cmd of this.voiceCommands) {
310
+ const match = commandText.match(cmd.pattern);
311
+ if (match) {
312
+ cmd.handler(match);
313
+ return {
314
+ text,
315
+ confidence: 1.0,
316
+ isCommand: true,
317
+ commandName: cmd.command,
318
+ };
319
+ }
320
+ }
321
+ return {
322
+ text,
323
+ confidence: 1.0,
324
+ isCommand: false,
325
+ };
326
+ }
327
+ /**
328
+ * Register a voice command
329
+ */
330
+ registerCommand(command) {
331
+ this.voiceCommands.push(command);
332
+ }
333
+ /**
334
+ * List registered voice commands
335
+ */
336
+ listCommands() {
337
+ return [...this.voiceCommands];
338
+ }
339
+ /**
340
+ * Set TTS engine
341
+ */
342
+ setTTSEngine(engine) {
343
+ this.config.ttsEngine = engine;
344
+ this.checkTTSAvailability();
345
+ this.saveConfig();
346
+ }
347
+ /**
348
+ * Set STT engine
349
+ */
350
+ setSTTEngine(engine) {
351
+ this.config.sttEngine = engine;
352
+ this.checkSTTAvailability();
353
+ this.saveConfig();
354
+ }
355
+ /**
356
+ * Set voice parameters
357
+ */
358
+ setVoice(params) {
359
+ if (params.voiceName !== undefined)
360
+ this.config.voiceName = params.voiceName;
361
+ if (params.speechRate !== undefined)
362
+ this.config.speechRate = params.speechRate;
363
+ if (params.volume !== undefined)
364
+ this.config.volume = params.volume;
365
+ if (params.pitch !== undefined)
366
+ this.config.pitch = params.pitch;
367
+ if (params.language !== undefined)
368
+ this.config.language = params.language;
369
+ this.saveConfig();
370
+ }
371
+ /**
372
+ * List available TTS voices
373
+ */
374
+ listVoices() {
375
+ try {
376
+ switch (this.config.ttsEngine) {
377
+ case 'say': {
378
+ const output = execSync('say -v "?"', { encoding: 'utf-8' });
379
+ return output.split('\n').filter(l => l.trim()).map(l => l.split(/\s+/)[0]).filter(Boolean);
380
+ }
381
+ case 'espeak': {
382
+ const output = execSync('espeak --voices', { encoding: 'utf-8' });
383
+ return output.split('\n').slice(1).filter(l => l.trim()).map(l => {
384
+ const parts = l.trim().split(/\s+/);
385
+ return parts.length >= 4 ? parts[3] : '';
386
+ }).filter(Boolean);
387
+ }
388
+ default:
389
+ return [];
390
+ }
391
+ }
392
+ catch {
393
+ return [];
394
+ }
395
+ }
396
+ /**
397
+ * Get config
398
+ */
399
+ getConfig() {
400
+ return { ...this.config };
401
+ }
402
+ /**
403
+ * Print voice status
404
+ */
405
+ printStatus() {
406
+ console.log('');
407
+ console.log(chalk.bold('--- NeuroCLI Voice I/O ---'));
408
+ console.log(` Enabled: ${this.config.enabled ? chalk.green('yes') : chalk.gray('no')}`);
409
+ console.log(` TTS Engine: ${chalk.cyan(this.config.ttsEngine)} ${this.ttsAvailable ? chalk.green('(available)') : chalk.red('(unavailable)')}`);
410
+ console.log(` STT Engine: ${chalk.cyan(this.config.sttEngine)} ${this.sttAvailable ? chalk.green('(available)') : chalk.red('(unavailable)')}`);
411
+ console.log(` Voice: ${this.config.voiceName || chalk.gray('(default)')}`);
412
+ console.log(` Rate: ${this.config.speechRate}`);
413
+ console.log(` Language: ${this.config.language}`);
414
+ console.log(` Auto-speak: ${this.config.autoSpeak ? chalk.green('on') : chalk.gray('off')}`);
415
+ console.log(` Wake word: ${this.config.wakeWord || chalk.gray('(none)')}`);
416
+ console.log(` Voice commands: ${this.voiceCommands.length}`);
417
+ if (this.isSpeaking)
418
+ console.log(` ${chalk.yellow('Currently speaking...')}`);
419
+ if (this.isListening)
420
+ console.log(` ${chalk.cyan('Currently listening...')}`);
421
+ console.log('');
422
+ }
423
+ // ----------------------------------------------------------
424
+ // Private helpers
425
+ // ----------------------------------------------------------
426
+ checkTTSAvailability() {
427
+ try {
428
+ switch (this.config.ttsEngine) {
429
+ case 'say':
430
+ execSync('which say', { stdio: 'ignore' });
431
+ this.ttsAvailable = true;
432
+ break;
433
+ case 'espeak':
434
+ try {
435
+ execSync('which espeak', { stdio: 'ignore' });
436
+ }
437
+ catch {
438
+ execSync('which espeak-ng', { stdio: 'ignore' });
439
+ }
440
+ this.ttsAvailable = true;
441
+ break;
442
+ default:
443
+ this.ttsAvailable = process.platform === 'darwin';
444
+ }
445
+ }
446
+ catch {
447
+ this.ttsAvailable = false;
448
+ }
449
+ }
450
+ checkSTTAvailability() {
451
+ try {
452
+ if (this.config.sttEngine === 'whisper') {
453
+ execSync('which whisper', { stdio: 'ignore' });
454
+ this.sttAvailable = true;
455
+ }
456
+ else {
457
+ this.sttAvailable = false;
458
+ }
459
+ }
460
+ catch {
461
+ this.sttAvailable = false;
462
+ }
463
+ }
464
+ cleanTextForTTS(text) {
465
+ return text
466
+ // Remove code blocks
467
+ .replace(/```[\s\S]*?```/g, ' code block ')
468
+ // Remove inline code
469
+ .replace(/`[^`]+`/g, ' code ')
470
+ // Remove markdown formatting
471
+ .replace(/\*\*([^*]+)\*\*/g, '$1')
472
+ .replace(/\*([^*]+)\*/g, '$1')
473
+ .replace(/__([^_]+)__/g, '$1')
474
+ .replace(/_([^_]+)_/g, '$1')
475
+ .replace(/##?\s+/g, '')
476
+ // Remove URLs
477
+ .replace(/https?:\/\/\S+/g, ' link ')
478
+ // Remove ANSI escape codes
479
+ .replace(/\x1b\[[0-9;]*m/g, '')
480
+ // Collapse whitespace
481
+ .replace(/\s+/g, ' ')
482
+ .trim();
483
+ }
484
+ registerDefaultCommands() {
485
+ this.voiceCommands = [
486
+ {
487
+ command: 'exit',
488
+ pattern: /^(exit|quit|bye)/,
489
+ handler: () => console.log(chalk.yellow('Use /exit or Ctrl+C to quit.')),
490
+ description: 'Exit NeuroCLI',
491
+ },
492
+ {
493
+ command: 'model',
494
+ pattern: /^(switch model|change model|use model)\s+(.+)/,
495
+ handler: (match) => console.log(chalk.gray(`Model switch requested: ${match[2]}`)),
496
+ description: 'Switch model',
497
+ },
498
+ {
499
+ command: 'clear',
500
+ pattern: /^(clear screen|clear)/,
501
+ handler: () => console.clear(),
502
+ description: 'Clear terminal',
503
+ },
504
+ {
505
+ command: 'help',
506
+ pattern: /^(help|commands)/,
507
+ handler: () => console.log(chalk.gray('Voice commands: exit, model <name>, clear, help, stop')),
508
+ description: 'Show help',
509
+ },
510
+ {
511
+ command: 'stop',
512
+ pattern: /^(stop|shut up|quiet|silence)/,
513
+ handler: () => this.stopSpeaking(),
514
+ description: 'Stop speaking',
515
+ },
516
+ ];
517
+ }
518
+ saveConfig() {
519
+ try {
520
+ const dir = join(VOICE_CONFIG_PATH, '..');
521
+ if (!existsSync(dir))
522
+ mkdirSync(dir, { recursive: true });
523
+ writeFileSync(VOICE_CONFIG_PATH, JSON.stringify(this.config, null, 2), 'utf-8');
524
+ }
525
+ catch { /* Silently fail */ }
526
+ }
527
+ loadConfig() {
528
+ try {
529
+ if (existsSync(VOICE_CONFIG_PATH)) {
530
+ const raw = readFileSync(VOICE_CONFIG_PATH, 'utf-8');
531
+ const saved = JSON.parse(raw);
532
+ this.config = { ...this.config, ...saved };
533
+ }
534
+ }
535
+ catch { /* Silently fail */ }
536
+ }
537
+ }
538
+ //# sourceMappingURL=voice.js.map