@sylphx/flow 1.0.1 → 1.0.3

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 (229) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +10 -9
  3. package/src/commands/codebase-command.ts +168 -0
  4. package/src/commands/flow-command.ts +1137 -0
  5. package/src/commands/flow-orchestrator.ts +296 -0
  6. package/src/commands/hook-command.ts +444 -0
  7. package/src/commands/init-command.ts +92 -0
  8. package/src/commands/init-core.ts +322 -0
  9. package/src/commands/knowledge-command.ts +161 -0
  10. package/src/commands/run-command.ts +120 -0
  11. package/src/components/benchmark-monitor.tsx +331 -0
  12. package/src/components/reindex-progress.tsx +261 -0
  13. package/src/composables/functional/index.ts +14 -0
  14. package/src/composables/functional/useEnvironment.ts +171 -0
  15. package/src/composables/functional/useFileSystem.ts +139 -0
  16. package/src/composables/index.ts +5 -0
  17. package/src/composables/useEnv.ts +13 -0
  18. package/src/composables/useRuntimeConfig.ts +27 -0
  19. package/src/composables/useTargetConfig.ts +45 -0
  20. package/src/config/ai-config.ts +376 -0
  21. package/src/config/constants.ts +35 -0
  22. package/src/config/index.ts +27 -0
  23. package/src/config/rules.ts +43 -0
  24. package/src/config/servers.ts +371 -0
  25. package/src/config/targets.ts +126 -0
  26. package/src/core/agent-loader.ts +141 -0
  27. package/src/core/agent-manager.ts +174 -0
  28. package/src/core/ai-sdk.ts +603 -0
  29. package/src/core/app-factory.ts +381 -0
  30. package/src/core/builtin-agents.ts +9 -0
  31. package/src/core/command-system.ts +550 -0
  32. package/src/core/config-system.ts +550 -0
  33. package/src/core/connection-pool.ts +390 -0
  34. package/src/core/di-container.ts +155 -0
  35. package/src/core/error-handling.ts +519 -0
  36. package/src/core/formatting/bytes.test.ts +115 -0
  37. package/src/core/formatting/bytes.ts +64 -0
  38. package/src/core/functional/async.ts +313 -0
  39. package/src/core/functional/either.ts +109 -0
  40. package/src/core/functional/error-handler.ts +135 -0
  41. package/src/core/functional/error-types.ts +311 -0
  42. package/src/core/functional/index.ts +19 -0
  43. package/src/core/functional/option.ts +142 -0
  44. package/src/core/functional/pipe.ts +189 -0
  45. package/src/core/functional/result.ts +204 -0
  46. package/src/core/functional/validation.ts +138 -0
  47. package/src/core/headless-display.ts +96 -0
  48. package/src/core/index.ts +6 -0
  49. package/src/core/installers/file-installer.ts +303 -0
  50. package/src/core/installers/mcp-installer.ts +213 -0
  51. package/src/core/interfaces/index.ts +22 -0
  52. package/src/core/interfaces/repository.interface.ts +91 -0
  53. package/src/core/interfaces/service.interface.ts +133 -0
  54. package/src/core/interfaces.ts +129 -0
  55. package/src/core/loop-controller.ts +200 -0
  56. package/src/core/result.ts +351 -0
  57. package/src/core/rule-loader.ts +147 -0
  58. package/src/core/rule-manager.ts +240 -0
  59. package/src/core/service-config.ts +252 -0
  60. package/src/core/session-service.ts +121 -0
  61. package/src/core/state-detector.ts +389 -0
  62. package/src/core/storage-factory.ts +115 -0
  63. package/src/core/stream-handler.ts +288 -0
  64. package/src/core/target-manager.ts +161 -0
  65. package/src/core/type-utils.ts +427 -0
  66. package/src/core/unified-storage.ts +456 -0
  67. package/src/core/upgrade-manager.ts +300 -0
  68. package/src/core/validation/limit.test.ts +155 -0
  69. package/src/core/validation/limit.ts +46 -0
  70. package/src/core/validation/query.test.ts +44 -0
  71. package/src/core/validation/query.ts +20 -0
  72. package/src/db/auto-migrate.ts +322 -0
  73. package/src/db/base-database-client.ts +144 -0
  74. package/src/db/cache-db.ts +218 -0
  75. package/src/db/cache-schema.ts +75 -0
  76. package/src/db/database.ts +70 -0
  77. package/src/db/index.ts +252 -0
  78. package/src/db/memory-db.ts +153 -0
  79. package/src/db/memory-schema.ts +29 -0
  80. package/src/db/schema.ts +289 -0
  81. package/src/db/session-repository.ts +733 -0
  82. package/src/domains/codebase/index.ts +5 -0
  83. package/src/domains/codebase/tools.ts +139 -0
  84. package/src/domains/index.ts +8 -0
  85. package/src/domains/knowledge/index.ts +10 -0
  86. package/src/domains/knowledge/resources.ts +537 -0
  87. package/src/domains/knowledge/tools.ts +174 -0
  88. package/src/domains/utilities/index.ts +6 -0
  89. package/src/domains/utilities/time/index.ts +5 -0
  90. package/src/domains/utilities/time/tools.ts +291 -0
  91. package/src/index.ts +211 -0
  92. package/src/services/agent-service.ts +273 -0
  93. package/src/services/claude-config-service.ts +252 -0
  94. package/src/services/config-service.ts +258 -0
  95. package/src/services/evaluation-service.ts +271 -0
  96. package/src/services/functional/evaluation-logic.ts +296 -0
  97. package/src/services/functional/file-processor.ts +273 -0
  98. package/src/services/functional/index.ts +12 -0
  99. package/src/services/index.ts +13 -0
  100. package/src/services/mcp-service.ts +432 -0
  101. package/src/services/memory.service.ts +476 -0
  102. package/src/services/search/base-indexer.ts +156 -0
  103. package/src/services/search/codebase-indexer-types.ts +38 -0
  104. package/src/services/search/codebase-indexer.ts +647 -0
  105. package/src/services/search/embeddings-provider.ts +455 -0
  106. package/src/services/search/embeddings.ts +316 -0
  107. package/src/services/search/functional-indexer.ts +323 -0
  108. package/src/services/search/index.ts +27 -0
  109. package/src/services/search/indexer.ts +380 -0
  110. package/src/services/search/knowledge-indexer.ts +422 -0
  111. package/src/services/search/semantic-search.ts +244 -0
  112. package/src/services/search/tfidf.ts +559 -0
  113. package/src/services/search/unified-search-service.ts +888 -0
  114. package/src/services/smart-config-service.ts +385 -0
  115. package/src/services/storage/cache-storage.ts +487 -0
  116. package/src/services/storage/drizzle-storage.ts +581 -0
  117. package/src/services/storage/index.ts +15 -0
  118. package/src/services/storage/lancedb-vector-storage.ts +494 -0
  119. package/src/services/storage/memory-storage.ts +268 -0
  120. package/src/services/storage/separated-storage.ts +467 -0
  121. package/src/services/storage/vector-storage.ts +13 -0
  122. package/src/shared/agents/index.ts +63 -0
  123. package/src/shared/files/index.ts +99 -0
  124. package/src/shared/index.ts +32 -0
  125. package/src/shared/logging/index.ts +24 -0
  126. package/src/shared/processing/index.ts +153 -0
  127. package/src/shared/types/index.ts +25 -0
  128. package/src/targets/claude-code.ts +574 -0
  129. package/src/targets/functional/claude-code-logic.ts +185 -0
  130. package/src/targets/functional/index.ts +6 -0
  131. package/src/targets/opencode.ts +529 -0
  132. package/src/types/agent.types.ts +32 -0
  133. package/src/types/api/batch.ts +108 -0
  134. package/src/types/api/errors.ts +118 -0
  135. package/src/types/api/index.ts +55 -0
  136. package/src/types/api/requests.ts +76 -0
  137. package/src/types/api/responses.ts +180 -0
  138. package/src/types/api/websockets.ts +85 -0
  139. package/src/types/api.types.ts +9 -0
  140. package/src/types/benchmark.ts +49 -0
  141. package/src/types/cli.types.ts +87 -0
  142. package/src/types/common.types.ts +35 -0
  143. package/src/types/database.types.ts +510 -0
  144. package/src/types/mcp-config.types.ts +448 -0
  145. package/src/types/mcp.types.ts +69 -0
  146. package/src/types/memory-types.ts +63 -0
  147. package/src/types/provider.types.ts +28 -0
  148. package/src/types/rule.types.ts +24 -0
  149. package/src/types/session.types.ts +214 -0
  150. package/src/types/target-config.types.ts +295 -0
  151. package/src/types/target.types.ts +140 -0
  152. package/src/types/todo.types.ts +25 -0
  153. package/src/types.ts +40 -0
  154. package/src/utils/advanced-tokenizer.ts +191 -0
  155. package/src/utils/agent-enhancer.ts +114 -0
  156. package/src/utils/ai-model-fetcher.ts +19 -0
  157. package/src/utils/async-file-operations.ts +516 -0
  158. package/src/utils/audio-player.ts +345 -0
  159. package/src/utils/cli-output.ts +266 -0
  160. package/src/utils/codebase-helpers.ts +211 -0
  161. package/src/utils/console-ui.ts +79 -0
  162. package/src/utils/database-errors.ts +140 -0
  163. package/src/utils/debug-logger.ts +49 -0
  164. package/src/utils/error-handler.ts +53 -0
  165. package/src/utils/file-operations.ts +310 -0
  166. package/src/utils/file-scanner.ts +259 -0
  167. package/src/utils/functional/array.ts +355 -0
  168. package/src/utils/functional/index.ts +15 -0
  169. package/src/utils/functional/object.ts +279 -0
  170. package/src/utils/functional/string.ts +281 -0
  171. package/src/utils/functional.ts +543 -0
  172. package/src/utils/help.ts +20 -0
  173. package/src/utils/immutable-cache.ts +106 -0
  174. package/src/utils/index.ts +78 -0
  175. package/src/utils/jsonc.ts +158 -0
  176. package/src/utils/logger.ts +396 -0
  177. package/src/utils/mcp-config.ts +249 -0
  178. package/src/utils/memory-tui.ts +414 -0
  179. package/src/utils/models-dev.ts +91 -0
  180. package/src/utils/notifications.ts +169 -0
  181. package/src/utils/object-utils.ts +51 -0
  182. package/src/utils/parallel-operations.ts +487 -0
  183. package/src/utils/paths.ts +143 -0
  184. package/src/utils/process-manager.ts +155 -0
  185. package/src/utils/prompts.ts +120 -0
  186. package/src/utils/search-tool-builder.ts +214 -0
  187. package/src/utils/secret-utils.ts +179 -0
  188. package/src/utils/security.ts +537 -0
  189. package/src/utils/session-manager.ts +168 -0
  190. package/src/utils/session-title.ts +87 -0
  191. package/src/utils/settings.ts +182 -0
  192. package/src/utils/simplified-errors.ts +410 -0
  193. package/src/utils/sync-utils.ts +159 -0
  194. package/src/utils/target-config.ts +570 -0
  195. package/src/utils/target-utils.ts +394 -0
  196. package/src/utils/template-engine.ts +94 -0
  197. package/src/utils/test-audio.ts +71 -0
  198. package/src/utils/todo-context.ts +46 -0
  199. package/src/utils/token-counter.ts +288 -0
  200. package/dist/index.d.ts +0 -10
  201. package/dist/index.js +0 -59554
  202. package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
  203. package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
  204. package/dist/shared/chunk-25dwp0dp.js +0 -89
  205. package/dist/shared/chunk-3pjb6063.js +0 -208
  206. package/dist/shared/chunk-4d6ydpw7.js +0 -2854
  207. package/dist/shared/chunk-4wjcadjk.js +0 -225
  208. package/dist/shared/chunk-5j4w74t6.js +0 -30
  209. package/dist/shared/chunk-5j8m3dh3.js +0 -58
  210. package/dist/shared/chunk-5thh3qem.js +0 -91
  211. package/dist/shared/chunk-6g9xy73m.js +0 -252
  212. package/dist/shared/chunk-7eq34c42.js +0 -23
  213. package/dist/shared/chunk-c2gwgx3r.js +0 -115
  214. package/dist/shared/chunk-cjd3mk4c.js +0 -1320
  215. package/dist/shared/chunk-g5cv6703.js +0 -368
  216. package/dist/shared/chunk-hpkhykhq.js +0 -574
  217. package/dist/shared/chunk-m2322pdk.js +0 -122
  218. package/dist/shared/chunk-nd5fdvaq.js +0 -26
  219. package/dist/shared/chunk-pgd3m6zf.js +0 -108
  220. package/dist/shared/chunk-qk8n91hw.js +0 -494
  221. package/dist/shared/chunk-rkkn8szp.js +0 -16855
  222. package/dist/shared/chunk-t16rfxh0.js +0 -61
  223. package/dist/shared/chunk-t4fbfa5v.js +0 -19
  224. package/dist/shared/chunk-t77h86w6.js +0 -276
  225. package/dist/shared/chunk-v0ez4aef.js +0 -71
  226. package/dist/shared/chunk-v29j2r3s.js +0 -32051
  227. package/dist/shared/chunk-vfbc6ew5.js +0 -765
  228. package/dist/shared/chunk-vmeqwm1c.js +0 -204
  229. package/dist/shared/chunk-x66eh37x.js +0 -137
@@ -0,0 +1,345 @@
1
+ /**
2
+ * Cross-Platform Audio Player
3
+ * Detects and uses available audio players across different platforms
4
+ *
5
+ * Supported players (in priority order):
6
+ * - macOS: afplay (built-in)
7
+ * - Linux: mpg123, mpg321, play (sox), aplay, mplayer, cvlc
8
+ * - Windows: powershell (built-in), cmdmp3
9
+ * - Universal: mplayer, omxplayer (Raspberry Pi)
10
+ */
11
+
12
+ import { spawn } from 'child_process';
13
+ import { existsSync } from 'fs';
14
+ import { join } from 'path';
15
+
16
+ /**
17
+ * Audio player configuration
18
+ */
19
+ interface AudioPlayer {
20
+ name: string;
21
+ command: string;
22
+ args: (filePath: string) => string[];
23
+ platforms: NodeJS.Platform[];
24
+ checkAvailability?: () => Promise<boolean>;
25
+ }
26
+
27
+ /**
28
+ * Available audio players in priority order
29
+ */
30
+ const AUDIO_PLAYERS: AudioPlayer[] = [
31
+ // macOS built-in
32
+ {
33
+ name: 'afplay',
34
+ command: 'afplay',
35
+ args: (file) => [file],
36
+ platforms: ['darwin'],
37
+ },
38
+
39
+ // Linux players (in priority order)
40
+ {
41
+ name: 'mpg123',
42
+ command: 'mpg123',
43
+ args: (file) => ['-q', file], // quiet mode
44
+ platforms: ['linux', 'freebsd', 'openbsd'],
45
+ },
46
+ {
47
+ name: 'mpg321',
48
+ command: 'mpg321',
49
+ args: (file) => ['-q', file], // quiet mode
50
+ platforms: ['linux', 'freebsd', 'openbsd'],
51
+ },
52
+ {
53
+ name: 'play',
54
+ command: 'play',
55
+ args: (file) => ['-q', file], // sox play command
56
+ platforms: ['linux', 'freebsd', 'openbsd', 'darwin'],
57
+ },
58
+ {
59
+ name: 'aplay',
60
+ command: 'aplay',
61
+ args: (file) => ['-q', file], // ALSA player
62
+ platforms: ['linux'],
63
+ },
64
+ {
65
+ name: 'mplayer',
66
+ command: 'mplayer',
67
+ args: (file) => ['-really-quiet', file],
68
+ platforms: ['linux', 'freebsd', 'openbsd', 'darwin', 'win32'],
69
+ },
70
+ {
71
+ name: 'cvlc',
72
+ command: 'cvlc',
73
+ args: (file) => ['--play-and-exit', '--quiet', file], // VLC command-line
74
+ platforms: ['linux', 'freebsd', 'openbsd', 'darwin', 'win32'],
75
+ },
76
+ {
77
+ name: 'omxplayer',
78
+ command: 'omxplayer',
79
+ args: (file) => ['-o', 'local', file], // Raspberry Pi
80
+ platforms: ['linux'],
81
+ },
82
+
83
+ // Windows players
84
+ {
85
+ name: 'powershell',
86
+ command: 'powershell',
87
+ args: (file) => [
88
+ '-NoProfile',
89
+ '-NonInteractive',
90
+ '-Command',
91
+ `(New-Object Media.SoundPlayer "${file}").PlaySync()`
92
+ ],
93
+ platforms: ['win32'],
94
+ // PowerShell Media.SoundPlayer only supports WAV files
95
+ checkAvailability: async () => {
96
+ // Check if we're on Windows and file is WAV
97
+ return process.platform === 'win32';
98
+ }
99
+ },
100
+ {
101
+ name: 'cmdmp3',
102
+ command: 'cmdmp3',
103
+ args: (file) => [file],
104
+ platforms: ['win32'],
105
+ },
106
+ ];
107
+
108
+ /**
109
+ * Cached available player to avoid repeated checks
110
+ */
111
+ let cachedPlayer: AudioPlayer | null = null;
112
+ let cacheChecked = false;
113
+
114
+ /**
115
+ * Check if a command is available in PATH
116
+ */
117
+ async function isCommandAvailable(command: string): Promise<boolean> {
118
+ return new Promise((resolve) => {
119
+ // Try to execute the command with --version or --help
120
+ const proc = spawn(command, ['--version'], {
121
+ stdio: 'ignore',
122
+ shell: true
123
+ });
124
+
125
+ proc.on('error', () => resolve(false));
126
+ proc.on('exit', (code) => {
127
+ // Some commands return non-zero for --version, so just check if they exist
128
+ resolve(true);
129
+ });
130
+
131
+ // Timeout after 1 second
132
+ setTimeout(() => {
133
+ proc.kill();
134
+ resolve(false);
135
+ }, 1000);
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Detect the first available audio player for current platform
141
+ */
142
+ export async function detectAudioPlayer(): Promise<AudioPlayer | null> {
143
+ if (cacheChecked && cachedPlayer) {
144
+ return cachedPlayer;
145
+ }
146
+
147
+ const currentPlatform = process.platform;
148
+
149
+ // Filter players for current platform
150
+ const compatiblePlayers = AUDIO_PLAYERS.filter(
151
+ (player) => player.platforms.includes(currentPlatform)
152
+ );
153
+
154
+ // Check each player in priority order
155
+ for (const player of compatiblePlayers) {
156
+ // Custom availability check if provided
157
+ if (player.checkAvailability) {
158
+ const isAvailable = await player.checkAvailability();
159
+ if (!isAvailable) continue;
160
+ }
161
+
162
+ // Check if command is available
163
+ const isAvailable = await isCommandAvailable(player.command);
164
+ if (isAvailable) {
165
+ cachedPlayer = player;
166
+ cacheChecked = true;
167
+ return player;
168
+ }
169
+ }
170
+
171
+ cacheChecked = true;
172
+ return null;
173
+ }
174
+
175
+ /**
176
+ * Play audio file using the best available player
177
+ * @param filePath Absolute path to audio file
178
+ * @param options Playback options
179
+ * @returns Promise that resolves when playback completes or rejects on error
180
+ */
181
+ export async function playSound(
182
+ filePath: string,
183
+ options?: {
184
+ volume?: number; // 0-100 (not supported by all players)
185
+ background?: boolean; // Play in background (default: true)
186
+ timeout?: number; // Max playback duration in ms (default: 5000)
187
+ }
188
+ ): Promise<void> {
189
+ const {
190
+ background = true,
191
+ timeout = 5000
192
+ } = options || {};
193
+
194
+ // Check if file exists
195
+ if (!existsSync(filePath)) {
196
+ throw new Error(`Audio file not found: ${filePath}`);
197
+ }
198
+
199
+ // Detect available player
200
+ const player = await detectAudioPlayer();
201
+ if (!player) {
202
+ throw new Error('No audio player available on this system');
203
+ }
204
+
205
+ return new Promise((resolve, reject) => {
206
+ const args = player.args(filePath);
207
+ const proc = spawn(player.command, args, {
208
+ stdio: 'ignore',
209
+ detached: background,
210
+ });
211
+
212
+ // If background mode, unref and resolve immediately
213
+ if (background) {
214
+ proc.unref();
215
+ resolve();
216
+ return;
217
+ }
218
+
219
+ // Otherwise wait for completion
220
+ let timeoutId: NodeJS.Timeout | null = null;
221
+
222
+ proc.on('error', (error) => {
223
+ if (timeoutId) clearTimeout(timeoutId);
224
+ reject(new Error(`Audio player error: ${error.message}`));
225
+ });
226
+
227
+ proc.on('exit', (code) => {
228
+ if (timeoutId) clearTimeout(timeoutId);
229
+ if (code === 0) {
230
+ resolve();
231
+ } else {
232
+ reject(new Error(`Audio player exited with code ${code}`));
233
+ }
234
+ });
235
+
236
+ // Timeout protection
237
+ if (timeout > 0) {
238
+ timeoutId = setTimeout(() => {
239
+ proc.kill();
240
+ reject(new Error(`Audio playback timeout after ${timeout}ms`));
241
+ }, timeout);
242
+ }
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Get information about the detected audio player
248
+ */
249
+ export async function getAudioPlayerInfo(): Promise<{
250
+ available: boolean;
251
+ player: string | null;
252
+ platform: string;
253
+ }> {
254
+ const player = await detectAudioPlayer();
255
+
256
+ return {
257
+ available: player !== null,
258
+ player: player?.name || null,
259
+ platform: process.platform
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Built-in system sounds for different platforms
265
+ */
266
+ export const SYSTEM_SOUNDS = {
267
+ // macOS system sounds
268
+ darwin: {
269
+ glass: '/System/Library/Sounds/Glass.aiff',
270
+ hero: '/System/Library/Sounds/Hero.aiff',
271
+ pop: '/System/Library/Sounds/Pop.aiff',
272
+ ping: '/System/Library/Sounds/Ping.aiff',
273
+ purr: '/System/Library/Sounds/Purr.aiff',
274
+ submarine: '/System/Library/Sounds/Submarine.aiff',
275
+ blow: '/System/Library/Sounds/Blow.aiff',
276
+ bottle: '/System/Library/Sounds/Bottle.aiff',
277
+ frog: '/System/Library/Sounds/Frog.aiff',
278
+ funk: '/System/Library/Sounds/Funk.aiff',
279
+ morse: '/System/Library/Sounds/Morse.aiff',
280
+ },
281
+ // Linux typical locations (may vary)
282
+ linux: {
283
+ complete: '/usr/share/sounds/freedesktop/stereo/complete.oga',
284
+ message: '/usr/share/sounds/freedesktop/stereo/message.oga',
285
+ bell: '/usr/share/sounds/freedesktop/stereo/bell.oga',
286
+ dialog: '/usr/share/sounds/freedesktop/stereo/dialog-information.oga',
287
+ },
288
+ // Windows - would need .wav files
289
+ win32: {
290
+ // Windows Media directory sounds
291
+ notify: 'C:\\Windows\\Media\\Windows Notify.wav',
292
+ ding: 'C:\\Windows\\Media\\Windows Ding.wav',
293
+ chord: 'C:\\Windows\\Media\\chord.wav',
294
+ }
295
+ } as const;
296
+
297
+ /**
298
+ * Get default system sound for current platform
299
+ */
300
+ export function getDefaultSystemSound(): string | null {
301
+ const platform = process.platform;
302
+
303
+ if (platform === 'darwin') {
304
+ return SYSTEM_SOUNDS.darwin.glass;
305
+ } else if (platform === 'linux') {
306
+ // Check which sound exists
307
+ const sounds = Object.values(SYSTEM_SOUNDS.linux);
308
+ for (const sound of sounds) {
309
+ if (existsSync(sound)) {
310
+ return sound;
311
+ }
312
+ }
313
+ } else if (platform === 'win32') {
314
+ // Check which sound exists
315
+ const sounds = Object.values(SYSTEM_SOUNDS.win32);
316
+ for (const sound of sounds) {
317
+ if (existsSync(sound)) {
318
+ return sound;
319
+ }
320
+ }
321
+ }
322
+
323
+ return null;
324
+ }
325
+
326
+ /**
327
+ * Play system notification sound
328
+ */
329
+ export async function playNotificationSound(): Promise<void> {
330
+ const soundPath = getDefaultSystemSound();
331
+
332
+ if (!soundPath) {
333
+ // No system sound available, just return silently
334
+ return;
335
+ }
336
+
337
+ try {
338
+ await playSound(soundPath, { background: true, timeout: 3000 });
339
+ } catch (error) {
340
+ // Fail silently - don't crash on sound playback errors
341
+ if (process.env.DEBUG) {
342
+ console.error('[Audio] Failed to play notification sound:', error);
343
+ }
344
+ }
345
+ }
@@ -0,0 +1,266 @@
1
+ /**
2
+ * CLI Output Utilities
3
+ * Provides structured output for CLI commands with proper logging separation
4
+ */
5
+
6
+ import { logger } from './logger.js';
7
+
8
+ /**
9
+ * CLI output levels
10
+ */
11
+ export type CLIOutputLevel = 'info' | 'success' | 'warning' | 'error';
12
+
13
+ /**
14
+ * CLI output interface
15
+ */
16
+ export interface ICLIOutput {
17
+ /**
18
+ * Print user-facing message
19
+ */
20
+ print(message: string, level?: CLIOutputLevel): void;
21
+
22
+ /**
23
+ * Print success message
24
+ */
25
+ success(message: string): void;
26
+
27
+ /**
28
+ * Print warning message
29
+ */
30
+ warning(message: string): void;
31
+
32
+ /**
33
+ * Print error message
34
+ */
35
+ error(message: string): void;
36
+
37
+ /**
38
+ * Print info message
39
+ */
40
+ info(message: string): void;
41
+
42
+ /**
43
+ * Print formatted data
44
+ */
45
+ table(data: Record<string, unknown>[]): void;
46
+
47
+ /**
48
+ * Print formatted list
49
+ */
50
+ list(items: string[], options?: { numbered?: boolean; bullet?: string }): void;
51
+ }
52
+
53
+ /**
54
+ * CLI output implementation
55
+ */
56
+ export class CLIOutput implements ICLIOutput {
57
+ private colors = {
58
+ reset: '\x1b[0m',
59
+ bright: '\x1b[1m',
60
+ dim: '\x1b[2m',
61
+ red: '\x1b[31m',
62
+ green: '\x1b[32m',
63
+ yellow: '\x1b[33m',
64
+ blue: '\x1b[34m',
65
+ magenta: '\x1b[35m',
66
+ cyan: '\x1b[36m',
67
+ white: '\x1b[37m',
68
+ };
69
+
70
+ private icons = {
71
+ info: 'ℹ️',
72
+ success: '✅',
73
+ warning: '⚠️',
74
+ error: '❌',
75
+ };
76
+
77
+ print(message: string, level: CLIOutputLevel = 'info'): void {
78
+ // Log internally for debugging
79
+ logger.debug('CLI output', { message, level });
80
+
81
+ // Output to user
82
+ switch (level) {
83
+ case 'success':
84
+ this.success(message);
85
+ break;
86
+ case 'warning':
87
+ this.warning(message);
88
+ break;
89
+ case 'error':
90
+ this.error(message);
91
+ break;
92
+ default:
93
+ this.info(message);
94
+ break;
95
+ }
96
+ }
97
+
98
+ success(message: string): void {
99
+ console.log(`${this.icons.success} ${this.colors.green}${message}${this.colors.reset}`);
100
+ logger.info('CLI success output', { message });
101
+ }
102
+
103
+ warning(message: string): void {
104
+ console.log(`${this.icons.warning} ${this.colors.yellow}${message}${this.colors.reset}`);
105
+ logger.warn('CLI warning output', { message });
106
+ }
107
+
108
+ error(message: string): void {
109
+ console.error(`${this.icons.error} ${this.colors.red}${message}${this.colors.reset}`);
110
+ logger.error('CLI error output', { message });
111
+ }
112
+
113
+ info(message: string): void {
114
+ console.log(`${this.icons.info} ${message}`);
115
+ logger.info('CLI info output', { message });
116
+ }
117
+
118
+ table(data: Record<string, unknown>[]): void {
119
+ if (data.length === 0) {
120
+ this.info('No data to display');
121
+ return;
122
+ }
123
+
124
+ // Simple table formatting
125
+ const headers = Object.keys(data[0]);
126
+ const columnWidths = headers.map((header) =>
127
+ Math.max(header.length, ...data.map((row) => String(row[header] || '').length))
128
+ );
129
+
130
+ // Print header
131
+ const headerRow = headers.map((header, i) => header.padEnd(columnWidths[i])).join(' | ');
132
+ console.log(this.colors.bright + headerRow + this.colors.reset);
133
+ console.log('-'.repeat(headerRow.length));
134
+
135
+ // Print rows
136
+ data.forEach((row) => {
137
+ const rowStr = headers
138
+ .map((header, i) => String(row[header] || '').padEnd(columnWidths[i]))
139
+ .join(' | ');
140
+ console.log(rowStr);
141
+ });
142
+
143
+ logger.debug('CLI table output', {
144
+ headers,
145
+ rowCount: data.length,
146
+ columnWidths,
147
+ });
148
+ }
149
+
150
+ list(items: string[], options: { numbered?: boolean; bullet?: string } = {}): void {
151
+ const { numbered = false, bullet = '•' } = options;
152
+
153
+ if (items.length === 0) {
154
+ this.info('No items to display');
155
+ return;
156
+ }
157
+
158
+ items.forEach((item, index) => {
159
+ const prefix = numbered ? `${index + 1}.` : bullet;
160
+ console.log(`${prefix} ${item}`);
161
+ });
162
+
163
+ logger.debug('CLI list output', {
164
+ itemCount: items.length,
165
+ numbered,
166
+ bullet,
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Print formatted memory entry
172
+ */
173
+ memoryEntry(
174
+ entry: {
175
+ namespace: string;
176
+ key: string;
177
+ value: unknown;
178
+ updated_at: string;
179
+ },
180
+ index?: number
181
+ ): void {
182
+ const prefix = index !== undefined ? `${index + 1}.` : '•';
183
+ const safeValue = entry.value || '';
184
+ const value =
185
+ typeof safeValue === 'string'
186
+ ? safeValue.substring(0, 50) + (safeValue.length > 50 ? '...' : '')
187
+ : `${JSON.stringify(safeValue).substring(0, 50)}...`;
188
+
189
+ console.log(`${prefix} ${entry.namespace}:${entry.key}`);
190
+ console.log(` Value: ${value}`);
191
+ console.log(` Updated: ${entry.updated_at}`);
192
+ console.log('');
193
+
194
+ logger.debug('CLI memory entry output', {
195
+ namespace: entry.namespace,
196
+ key: entry.key,
197
+ valueLength: String(entry.value || '').length,
198
+ });
199
+ }
200
+
201
+ /**
202
+ * Print search results summary
203
+ */
204
+ searchSummary(query: string, results: number, namespace?: string): void {
205
+ console.log(`${this.colors.cyan}🔍 Search results for pattern: ${query}${this.colors.reset}`);
206
+
207
+ if (namespace) {
208
+ console.log(`${this.colors.dim}Namespace: ${namespace}${this.colors.reset}`);
209
+ }
210
+
211
+ console.log(`${this.colors.bright}Found: ${results} results${this.colors.reset}\n`);
212
+
213
+ logger.info('CLI search summary', { query, results, namespace });
214
+ }
215
+
216
+ /**
217
+ * Print list summary
218
+ */
219
+ listSummary(namespace: string, count: number, total?: number): void {
220
+ if (namespace && namespace !== 'all') {
221
+ console.log(
222
+ `${this.colors.cyan}📋 Memory entries in namespace: ${namespace}${this.colors.reset}`
223
+ );
224
+ console.log(`${this.colors.bright}Total: ${count} entries${this.colors.reset}\n`);
225
+ } else if (total) {
226
+ console.log(
227
+ `${this.colors.cyan}📋 All memory entries (showing first ${count})${this.colors.reset}`
228
+ );
229
+ console.log(`${this.colors.bright}Total: ${total} entries${this.colors.reset}\n`);
230
+ }
231
+
232
+ logger.info('CLI list summary', { namespace, count, total });
233
+ }
234
+
235
+ /**
236
+ * Print empty state message
237
+ */
238
+ emptyState(type: 'entries' | 'results' | 'data', context?: string): void {
239
+ const messages = {
240
+ entries: 'No entries found',
241
+ results: 'No matching entries found',
242
+ data: 'No data to display',
243
+ };
244
+
245
+ let message = messages[type];
246
+ if (context) {
247
+ message += ` in ${context}`;
248
+ }
249
+ message += '.';
250
+
251
+ this.info(message);
252
+ logger.debug('CLI empty state', { type, context, message });
253
+ }
254
+ }
255
+
256
+ // Global CLI output instance
257
+ export const cli = new CLIOutput();
258
+
259
+ /**
260
+ * Convenience functions
261
+ */
262
+ export const print = (message: string, level?: CLIOutputLevel) => cli.print(message, level);
263
+ export const success = (message: string) => cli.success(message);
264
+ export const warning = (message: string) => cli.warning(message);
265
+ export const error = (message: string) => cli.error(message);
266
+ export const info = (message: string) => cli.info(message);