claude-flow 1.0.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +612 -0
  3. package/bin/claude-flow +0 -0
  4. package/bin/claude-flow-simple +0 -0
  5. package/bin/claude-flow-typecheck +0 -0
  6. package/deno.json +84 -0
  7. package/package.json +45 -0
  8. package/scripts/check-links.ts +274 -0
  9. package/scripts/check-performance-regression.ts +168 -0
  10. package/scripts/claude-sparc.sh +562 -0
  11. package/scripts/coverage-report.ts +692 -0
  12. package/scripts/demo-task-system.ts +224 -0
  13. package/scripts/install.js +72 -0
  14. package/scripts/test-batch-tasks.ts +29 -0
  15. package/scripts/test-coordination-features.ts +238 -0
  16. package/scripts/test-mcp.ts +251 -0
  17. package/scripts/test-runner.ts +571 -0
  18. package/scripts/validate-examples.ts +288 -0
  19. package/src/cli/cli-core.ts +273 -0
  20. package/src/cli/commands/agent.ts +83 -0
  21. package/src/cli/commands/config.ts +442 -0
  22. package/src/cli/commands/help.ts +765 -0
  23. package/src/cli/commands/index.ts +963 -0
  24. package/src/cli/commands/mcp.ts +191 -0
  25. package/src/cli/commands/memory.ts +74 -0
  26. package/src/cli/commands/monitor.ts +403 -0
  27. package/src/cli/commands/session.ts +595 -0
  28. package/src/cli/commands/start.ts +156 -0
  29. package/src/cli/commands/status.ts +345 -0
  30. package/src/cli/commands/task.ts +79 -0
  31. package/src/cli/commands/workflow.ts +763 -0
  32. package/src/cli/completion.ts +553 -0
  33. package/src/cli/formatter.ts +310 -0
  34. package/src/cli/index.ts +211 -0
  35. package/src/cli/main.ts +23 -0
  36. package/src/cli/repl.ts +1050 -0
  37. package/src/cli/simple-cli.js +211 -0
  38. package/src/cli/simple-cli.ts +211 -0
  39. package/src/coordination/README.md +400 -0
  40. package/src/coordination/advanced-scheduler.ts +487 -0
  41. package/src/coordination/circuit-breaker.ts +366 -0
  42. package/src/coordination/conflict-resolution.ts +490 -0
  43. package/src/coordination/dependency-graph.ts +475 -0
  44. package/src/coordination/index.ts +63 -0
  45. package/src/coordination/manager.ts +460 -0
  46. package/src/coordination/messaging.ts +290 -0
  47. package/src/coordination/metrics.ts +585 -0
  48. package/src/coordination/resources.ts +322 -0
  49. package/src/coordination/scheduler.ts +390 -0
  50. package/src/coordination/work-stealing.ts +224 -0
  51. package/src/core/config.ts +627 -0
  52. package/src/core/event-bus.ts +186 -0
  53. package/src/core/json-persistence.ts +183 -0
  54. package/src/core/logger.ts +262 -0
  55. package/src/core/orchestrator-fixed.ts +312 -0
  56. package/src/core/orchestrator.ts +1234 -0
  57. package/src/core/persistence.ts +276 -0
  58. package/src/mcp/auth.ts +438 -0
  59. package/src/mcp/claude-flow-tools.ts +1280 -0
  60. package/src/mcp/load-balancer.ts +510 -0
  61. package/src/mcp/router.ts +240 -0
  62. package/src/mcp/server.ts +548 -0
  63. package/src/mcp/session-manager.ts +418 -0
  64. package/src/mcp/tools.ts +180 -0
  65. package/src/mcp/transports/base.ts +21 -0
  66. package/src/mcp/transports/http.ts +457 -0
  67. package/src/mcp/transports/stdio.ts +254 -0
  68. package/src/memory/backends/base.ts +22 -0
  69. package/src/memory/backends/markdown.ts +283 -0
  70. package/src/memory/backends/sqlite.ts +329 -0
  71. package/src/memory/cache.ts +238 -0
  72. package/src/memory/indexer.ts +238 -0
  73. package/src/memory/manager.ts +572 -0
  74. package/src/terminal/adapters/base.ts +29 -0
  75. package/src/terminal/adapters/native.ts +504 -0
  76. package/src/terminal/adapters/vscode.ts +340 -0
  77. package/src/terminal/manager.ts +308 -0
  78. package/src/terminal/pool.ts +271 -0
  79. package/src/terminal/session.ts +250 -0
  80. package/src/terminal/vscode-bridge.ts +242 -0
  81. package/src/utils/errors.ts +231 -0
  82. package/src/utils/helpers.ts +476 -0
  83. package/src/utils/types.ts +493 -0
@@ -0,0 +1,627 @@
1
+ /**
2
+ * Configuration management for Claude-Flow
3
+ */
4
+
5
+ import { Config } from '../utils/types.ts';
6
+ import { deepMerge, safeParseJSON } from '../utils/helpers.ts';
7
+ import { ConfigError, ValidationError } from '../utils/errors.ts';
8
+
9
+ /**
10
+ * Default configuration values
11
+ */
12
+ const DEFAULT_CONFIG: Config = {
13
+ orchestrator: {
14
+ maxConcurrentAgents: 10,
15
+ taskQueueSize: 100,
16
+ healthCheckInterval: 30000, // 30 seconds
17
+ shutdownTimeout: 30000, // 30 seconds
18
+ },
19
+ terminal: {
20
+ type: 'auto',
21
+ poolSize: 5,
22
+ recycleAfter: 10, // recycle after 10 uses
23
+ healthCheckInterval: 60000, // 1 minute
24
+ commandTimeout: 300000, // 5 minutes
25
+ },
26
+ memory: {
27
+ backend: 'hybrid',
28
+ cacheSizeMB: 100,
29
+ syncInterval: 5000, // 5 seconds
30
+ conflictResolution: 'crdt',
31
+ retentionDays: 30,
32
+ },
33
+ coordination: {
34
+ maxRetries: 3,
35
+ retryDelay: 1000, // 1 second
36
+ deadlockDetection: true,
37
+ resourceTimeout: 60000, // 1 minute
38
+ messageTimeout: 30000, // 30 seconds
39
+ },
40
+ mcp: {
41
+ transport: 'stdio',
42
+ port: 3000,
43
+ tlsEnabled: false,
44
+ },
45
+ logging: {
46
+ level: 'info',
47
+ format: 'json',
48
+ destination: 'console',
49
+ },
50
+ };
51
+
52
+ /**
53
+ * Configuration manager
54
+ */
55
+ export class ConfigManager {
56
+ private static instance: ConfigManager;
57
+ private config: Config;
58
+ private configPath?: string;
59
+ private profiles: Map<string, Partial<Config>> = new Map();
60
+ private currentProfile?: string;
61
+ private userConfigDir: string;
62
+
63
+ private constructor() {
64
+ this.config = deepClone(DEFAULT_CONFIG);
65
+ this.userConfigDir = this.getUserConfigDir();
66
+ }
67
+
68
+ /**
69
+ * Gets the singleton instance
70
+ */
71
+ static getInstance(): ConfigManager {
72
+ if (!ConfigManager.instance) {
73
+ ConfigManager.instance = new ConfigManager();
74
+ }
75
+ return ConfigManager.instance;
76
+ }
77
+
78
+ /**
79
+ * Loads configuration from various sources
80
+ */
81
+ async load(configPath?: string): Promise<Config> {
82
+ if (configPath !== undefined) {
83
+ this.configPath = configPath;
84
+ }
85
+
86
+ // Start with defaults
87
+ let config = deepClone(DEFAULT_CONFIG);
88
+
89
+ // Load from file if specified
90
+ if (configPath) {
91
+ const fileConfig = await this.loadFromFile(configPath);
92
+ config = deepMergeConfig(config, fileConfig);
93
+ }
94
+
95
+ // Load from environment variables
96
+ const envConfig = this.loadFromEnv();
97
+ config = deepMergeConfig(config, envConfig);
98
+
99
+ // Validate the final configuration
100
+ this.validate(config);
101
+
102
+ this.config = config;
103
+ return config;
104
+ }
105
+
106
+ /**
107
+ * Gets the current configuration
108
+ */
109
+ get(): Config {
110
+ return deepClone(this.config);
111
+ }
112
+
113
+ /**
114
+ * Updates configuration values
115
+ */
116
+ update(updates: Partial<Config>): Config {
117
+ this.config = deepMergeConfig(this.config, updates);
118
+ this.validate(this.config);
119
+ return this.get();
120
+ }
121
+
122
+ /**
123
+ * Loads default configuration
124
+ */
125
+ loadDefault(): void {
126
+ this.config = deepClone(DEFAULT_CONFIG);
127
+ }
128
+
129
+ /**
130
+ * Saves configuration to file
131
+ */
132
+ async save(path?: string): Promise<void> {
133
+ const savePath = path || this.configPath;
134
+ if (!savePath) {
135
+ throw new ConfigError('No configuration file path specified');
136
+ }
137
+
138
+ const content = JSON.stringify(this.config, null, 2);
139
+ await Deno.writeTextFile(savePath, content);
140
+ }
141
+
142
+ /**
143
+ * Gets user configuration directory
144
+ */
145
+ private getUserConfigDir(): string {
146
+ const home = Deno.env.get('HOME') || Deno.env.get('USERPROFILE') || '/tmp';
147
+ return `${home}/.claude-flow`;
148
+ }
149
+
150
+ /**
151
+ * Creates user config directory if it doesn't exist
152
+ */
153
+ private async ensureUserConfigDir(): Promise<void> {
154
+ try {
155
+ await Deno.mkdir(this.userConfigDir, { recursive: true });
156
+ } catch (error) {
157
+ if (!(error instanceof Deno.errors.AlreadyExists)) {
158
+ throw new ConfigError(`Failed to create config directory: ${(error as Error).message}`);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Loads all profiles from the profiles directory
165
+ */
166
+ async loadProfiles(): Promise<void> {
167
+ const profilesDir = `${this.userConfigDir}/profiles`;
168
+
169
+ try {
170
+ for await (const entry of Deno.readDir(profilesDir)) {
171
+ if (entry.isFile && entry.name.endsWith('.json')) {
172
+ const profileName = entry.name.replace('.json', '');
173
+ const profilePath = `${profilesDir}/${entry.name}`;
174
+
175
+ try {
176
+ const content = await Deno.readTextFile(profilePath);
177
+ const profileConfig = safeParseJSON<Partial<Config>>(content);
178
+
179
+ if (profileConfig) {
180
+ this.profiles.set(profileName, profileConfig);
181
+ }
182
+ } catch (error) {
183
+ console.warn(`Failed to load profile ${profileName}: ${(error as Error).message}`);
184
+ }
185
+ }
186
+ }
187
+ } catch (error) {
188
+ // Profiles directory doesn't exist - this is okay
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Applies a named profile
194
+ */
195
+ async applyProfile(profileName: string): Promise<void> {
196
+ await this.loadProfiles();
197
+
198
+ const profile = this.profiles.get(profileName);
199
+ if (!profile) {
200
+ throw new ConfigError(`Profile '${profileName}' not found`);
201
+ }
202
+
203
+ this.config = deepMergeConfig(this.config, profile);
204
+ this.currentProfile = profileName;
205
+ this.validate(this.config);
206
+ }
207
+
208
+ /**
209
+ * Saves current configuration as a profile
210
+ */
211
+ async saveProfile(profileName: string, config?: Partial<Config>): Promise<void> {
212
+ await this.ensureUserConfigDir();
213
+
214
+ const profilesDir = `${this.userConfigDir}/profiles`;
215
+ await Deno.mkdir(profilesDir, { recursive: true });
216
+
217
+ const profileConfig = config || this.config;
218
+ const profilePath = `${profilesDir}/${profileName}.json`;
219
+
220
+ const content = JSON.stringify(profileConfig, null, 2);
221
+ await Deno.writeTextFile(profilePath, content);
222
+
223
+ this.profiles.set(profileName, profileConfig);
224
+ }
225
+
226
+ /**
227
+ * Deletes a profile
228
+ */
229
+ async deleteProfile(profileName: string): Promise<void> {
230
+ const profilePath = `${this.userConfigDir}/profiles/${profileName}.json`;
231
+
232
+ try {
233
+ await Deno.remove(profilePath);
234
+ this.profiles.delete(profileName);
235
+ } catch (error) {
236
+ if (error instanceof Deno.errors.NotFound) {
237
+ throw new ConfigError(`Profile '${profileName}' not found`);
238
+ }
239
+ throw new ConfigError(`Failed to delete profile: ${(error as Error).message}`);
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Lists all available profiles
245
+ */
246
+ async listProfiles(): Promise<string[]> {
247
+ await this.loadProfiles();
248
+ return Array.from(this.profiles.keys());
249
+ }
250
+
251
+ /**
252
+ * Gets a specific profile configuration
253
+ */
254
+ async getProfile(profileName: string): Promise<Partial<Config> | undefined> {
255
+ await this.loadProfiles();
256
+ return this.profiles.get(profileName);
257
+ }
258
+
259
+ /**
260
+ * Gets the current active profile name
261
+ */
262
+ getCurrentProfile(): string | undefined {
263
+ return this.currentProfile;
264
+ }
265
+
266
+ /**
267
+ * Sets a configuration value by path
268
+ */
269
+ set(path: string, value: any): void {
270
+ const keys = path.split('.');
271
+ let current: any = this.config;
272
+
273
+ for (let i = 0; i < keys.length - 1; i++) {
274
+ const key = keys[i];
275
+ if (!(key in current)) {
276
+ current[key] = {};
277
+ }
278
+ current = current[key];
279
+ }
280
+
281
+ current[keys[keys.length - 1]] = value;
282
+ this.validate(this.config);
283
+ }
284
+
285
+ /**
286
+ * Gets a configuration value by path
287
+ */
288
+ getValue(path: string): any {
289
+ const keys = path.split('.');
290
+ let current: any = this.config;
291
+
292
+ for (const key of keys) {
293
+ if (current && typeof current === 'object' && key in current) {
294
+ current = current[key];
295
+ } else {
296
+ return undefined;
297
+ }
298
+ }
299
+
300
+ return current;
301
+ }
302
+
303
+ /**
304
+ * Resets configuration to defaults
305
+ */
306
+ reset(): void {
307
+ this.config = deepClone(DEFAULT_CONFIG);
308
+ delete this.currentProfile;
309
+ }
310
+
311
+ /**
312
+ * Gets configuration schema for validation
313
+ */
314
+ getSchema(): any {
315
+ return {
316
+ orchestrator: {
317
+ maxConcurrentAgents: { type: 'number', min: 1, max: 100 },
318
+ taskQueueSize: { type: 'number', min: 1, max: 10000 },
319
+ healthCheckInterval: { type: 'number', min: 1000, max: 300000 },
320
+ shutdownTimeout: { type: 'number', min: 1000, max: 300000 },
321
+ },
322
+ terminal: {
323
+ type: { type: 'string', values: ['auto', 'vscode', 'native'] },
324
+ poolSize: { type: 'number', min: 1, max: 50 },
325
+ recycleAfter: { type: 'number', min: 1, max: 1000 },
326
+ healthCheckInterval: { type: 'number', min: 1000, max: 3600000 },
327
+ commandTimeout: { type: 'number', min: 1000, max: 3600000 },
328
+ },
329
+ memory: {
330
+ backend: { type: 'string', values: ['sqlite', 'markdown', 'hybrid'] },
331
+ cacheSizeMB: { type: 'number', min: 1, max: 10000 },
332
+ syncInterval: { type: 'number', min: 1000, max: 300000 },
333
+ conflictResolution: { type: 'string', values: ['crdt', 'timestamp', 'manual'] },
334
+ retentionDays: { type: 'number', min: 1, max: 3650 },
335
+ },
336
+ coordination: {
337
+ maxRetries: { type: 'number', min: 0, max: 100 },
338
+ retryDelay: { type: 'number', min: 100, max: 60000 },
339
+ deadlockDetection: { type: 'boolean' },
340
+ resourceTimeout: { type: 'number', min: 1000, max: 3600000 },
341
+ messageTimeout: { type: 'number', min: 1000, max: 300000 },
342
+ },
343
+ mcp: {
344
+ transport: { type: 'string', values: ['stdio', 'http', 'websocket'] },
345
+ port: { type: 'number', min: 1, max: 65535 },
346
+ tlsEnabled: { type: 'boolean' },
347
+ },
348
+ logging: {
349
+ level: { type: 'string', values: ['debug', 'info', 'warn', 'error'] },
350
+ format: { type: 'string', values: ['json', 'text'] },
351
+ destination: { type: 'string', values: ['console', 'file'] },
352
+ },
353
+ };
354
+ }
355
+
356
+ /**
357
+ * Validates a value against schema
358
+ */
359
+ private validateValue(value: any, schema: any, path: string): void {
360
+ if (schema.type === 'number') {
361
+ if (typeof value !== 'number' || isNaN(value)) {
362
+ throw new ValidationError(`${path}: must be a number`);
363
+ }
364
+ if (schema.min !== undefined && value < schema.min) {
365
+ throw new ValidationError(`${path}: must be at least ${schema.min}`);
366
+ }
367
+ if (schema.max !== undefined && value > schema.max) {
368
+ throw new ValidationError(`${path}: must be at most ${schema.max}`);
369
+ }
370
+ } else if (schema.type === 'string') {
371
+ if (typeof value !== 'string') {
372
+ throw new ValidationError(`${path}: must be a string`);
373
+ }
374
+ if (schema.values && !schema.values.includes(value)) {
375
+ throw new ValidationError(`${path}: must be one of [${schema.values.join(', ')}]`);
376
+ }
377
+ } else if (schema.type === 'boolean') {
378
+ if (typeof value !== 'boolean') {
379
+ throw new ValidationError(`${path}: must be a boolean`);
380
+ }
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Gets configuration diff between current and default
386
+ */
387
+ getDiff(): any {
388
+ const defaultConfig = DEFAULT_CONFIG;
389
+ const diff: any = {};
390
+
391
+ const findDifferences = (current: any, defaults: any, path: string = '') => {
392
+ for (const key in current) {
393
+ const currentValue = current[key];
394
+ const defaultValue = defaults[key];
395
+ const fullPath = path ? `${path}.${key}` : key;
396
+
397
+ if (typeof currentValue === 'object' && currentValue !== null && !Array.isArray(currentValue)) {
398
+ if (typeof defaultValue === 'object' && defaultValue !== null) {
399
+ const nestedDiff = {};
400
+ findDifferences(currentValue, defaultValue, fullPath);
401
+ if (Object.keys(nestedDiff).length > 0) {
402
+ if (!path) {
403
+ diff[key] = nestedDiff;
404
+ }
405
+ }
406
+ }
407
+ } else if (currentValue !== defaultValue) {
408
+ const pathParts = fullPath.split('.');
409
+ let target = diff;
410
+ for (let i = 0; i < pathParts.length - 1; i++) {
411
+ if (!target[pathParts[i]]) {
412
+ target[pathParts[i]] = {};
413
+ }
414
+ target = target[pathParts[i]];
415
+ }
416
+ target[pathParts[pathParts.length - 1]] = currentValue;
417
+ }
418
+ }
419
+ };
420
+
421
+ findDifferences(this.config, defaultConfig);
422
+ return diff;
423
+ }
424
+
425
+ /**
426
+ * Exports configuration with metadata
427
+ */
428
+ export(): any {
429
+ return {
430
+ version: '1.0.0',
431
+ exported: new Date().toISOString(),
432
+ profile: this.currentProfile,
433
+ config: this.config,
434
+ diff: this.getDiff(),
435
+ };
436
+ }
437
+
438
+ /**
439
+ * Imports configuration from export
440
+ */
441
+ import(data: any): void {
442
+ if (!data.config) {
443
+ throw new ConfigError('Invalid configuration export format');
444
+ }
445
+
446
+ this.validate(data.config);
447
+ this.config = data.config;
448
+ this.currentProfile = data.profile;
449
+ }
450
+
451
+ /**
452
+ * Loads configuration from file
453
+ */
454
+ private async loadFromFile(path: string): Promise<Partial<Config>> {
455
+ try {
456
+ const content = await Deno.readTextFile(path);
457
+ const config = safeParseJSON<Partial<Config>>(content);
458
+
459
+ if (!config) {
460
+ throw new ConfigError(`Invalid JSON in configuration file: ${path}`);
461
+ }
462
+
463
+ return config;
464
+ } catch (error) {
465
+ if (error instanceof Deno.errors.NotFound) {
466
+ // File doesn't exist, use defaults
467
+ return {};
468
+ }
469
+ throw new ConfigError(`Failed to load configuration from ${path}: ${(error as Error).message}`);
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Loads configuration from environment variables
475
+ */
476
+ private loadFromEnv(): Partial<Config> {
477
+ const config: Partial<Config> = {};
478
+
479
+ // Orchestrator settings
480
+ const maxAgents = Deno.env.get('CLAUDE_FLOW_MAX_AGENTS');
481
+ if (maxAgents) {
482
+ if (!config.orchestrator) {
483
+ config.orchestrator = {} as any;
484
+ }
485
+ config.orchestrator = {
486
+ ...DEFAULT_CONFIG.orchestrator,
487
+ ...config.orchestrator,
488
+ maxConcurrentAgents: parseInt(maxAgents, 10),
489
+ };
490
+ }
491
+
492
+ // Terminal settings
493
+ const terminalType = Deno.env.get('CLAUDE_FLOW_TERMINAL_TYPE');
494
+ if (terminalType === 'vscode' || terminalType === 'native' || terminalType === 'auto') {
495
+ config.terminal = {
496
+ ...DEFAULT_CONFIG.terminal,
497
+ ...config.terminal,
498
+ type: terminalType,
499
+ };
500
+ }
501
+
502
+ // Memory settings
503
+ const memoryBackend = Deno.env.get('CLAUDE_FLOW_MEMORY_BACKEND');
504
+ if (memoryBackend === 'sqlite' || memoryBackend === 'markdown' || memoryBackend === 'hybrid') {
505
+ config.memory = {
506
+ ...DEFAULT_CONFIG.memory,
507
+ ...config.memory,
508
+ backend: memoryBackend,
509
+ };
510
+ }
511
+
512
+ // MCP settings
513
+ const mcpTransport = Deno.env.get('CLAUDE_FLOW_MCP_TRANSPORT');
514
+ if (mcpTransport === 'stdio' || mcpTransport === 'http' || mcpTransport === 'websocket') {
515
+ config.mcp = {
516
+ ...DEFAULT_CONFIG.mcp,
517
+ ...config.mcp,
518
+ transport: mcpTransport,
519
+ };
520
+ }
521
+
522
+ const mcpPort = Deno.env.get('CLAUDE_FLOW_MCP_PORT');
523
+ if (mcpPort) {
524
+ config.mcp = {
525
+ ...DEFAULT_CONFIG.mcp,
526
+ ...config.mcp,
527
+ port: parseInt(mcpPort, 10),
528
+ };
529
+ }
530
+
531
+ // Logging settings
532
+ const logLevel = Deno.env.get('CLAUDE_FLOW_LOG_LEVEL');
533
+ if (logLevel === 'debug' || logLevel === 'info' || logLevel === 'warn' || logLevel === 'error') {
534
+ config.logging = {
535
+ ...DEFAULT_CONFIG.logging,
536
+ ...config.logging,
537
+ level: logLevel,
538
+ };
539
+ }
540
+
541
+ return config;
542
+ }
543
+
544
+ /**
545
+ * Validates configuration
546
+ */
547
+ private validate(config: Config): void {
548
+ // Orchestrator validation
549
+ if (config.orchestrator.maxConcurrentAgents < 1) {
550
+ throw new ValidationError('maxConcurrentAgents must be at least 1');
551
+ }
552
+ if (config.orchestrator.taskQueueSize < 1) {
553
+ throw new ValidationError('taskQueueSize must be at least 1');
554
+ }
555
+
556
+ // Terminal validation
557
+ if (config.terminal.poolSize < 1) {
558
+ throw new ValidationError('terminal poolSize must be at least 1');
559
+ }
560
+ if (config.terminal.recycleAfter < 1) {
561
+ throw new ValidationError('terminal recycleAfter must be at least 1');
562
+ }
563
+
564
+ // Memory validation
565
+ if (config.memory.cacheSizeMB < 1) {
566
+ throw new ValidationError('memory cacheSizeMB must be at least 1');
567
+ }
568
+ if (config.memory.retentionDays < 1) {
569
+ throw new ValidationError('memory retentionDays must be at least 1');
570
+ }
571
+
572
+ // Coordination validation
573
+ if (config.coordination.maxRetries < 0) {
574
+ throw new ValidationError('coordination maxRetries cannot be negative');
575
+ }
576
+
577
+ // MCP validation
578
+ if (config.mcp.transport === 'http' || config.mcp.transport === 'websocket') {
579
+ if (!config.mcp.port || config.mcp.port < 1 || config.mcp.port > 65535) {
580
+ throw new ValidationError('Invalid MCP port number');
581
+ }
582
+ }
583
+ }
584
+ }
585
+
586
+ // Export singleton instance
587
+ export const configManager = ConfigManager.getInstance();
588
+
589
+ // Helper function to load configuration
590
+ export async function loadConfig(path?: string): Promise<Config> {
591
+ return await configManager.load(path);
592
+ }
593
+
594
+ function deepClone<T>(obj: T): T {
595
+ return JSON.parse(JSON.stringify(obj));
596
+ }
597
+
598
+ // Custom deepMerge for Config type
599
+ function deepMergeConfig(target: Config, ...sources: Partial<Config>[]): Config {
600
+ const result = deepClone(target);
601
+
602
+ for (const source of sources) {
603
+ if (!source) continue;
604
+
605
+ // Merge each section
606
+ if (source.orchestrator) {
607
+ result.orchestrator = { ...result.orchestrator, ...source.orchestrator };
608
+ }
609
+ if (source.terminal) {
610
+ result.terminal = { ...result.terminal, ...source.terminal };
611
+ }
612
+ if (source.memory) {
613
+ result.memory = { ...result.memory, ...source.memory };
614
+ }
615
+ if (source.coordination) {
616
+ result.coordination = { ...result.coordination, ...source.coordination };
617
+ }
618
+ if (source.mcp) {
619
+ result.mcp = { ...result.mcp, ...source.mcp };
620
+ }
621
+ if (source.logging) {
622
+ result.logging = { ...result.logging, ...source.logging };
623
+ }
624
+ }
625
+
626
+ return result;
627
+ }