@girardmedia/bootspring 2.0.36 → 2.0.37

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.
@@ -0,0 +1,795 @@
1
+ /**
2
+ * Intelligent Agent Router
3
+ *
4
+ * Analyzes tasks and routes them to the most appropriate agent(s).
5
+ * Supports single-agent routing and multi-agent collaboration planning.
6
+ *
7
+ * @package bootspring
8
+ * @module intelligence/agent-router
9
+ */
10
+
11
+ const fs = require('fs').promises;
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Expertise domains and their indicators
16
+ */
17
+ const EXPERTISE_DOMAINS = {
18
+ database: {
19
+ name: 'Database & Data Modeling',
20
+ keywords: ['database', 'schema', 'model', 'migration', 'prisma', 'sql', 'table', 'relation', 'index', 'query'],
21
+ filePatterns: ['prisma/**', 'models/**', 'schema/**', 'migrations/**'],
22
+ agents: ['database-expert', 'prisma-expert', 'sql-expert']
23
+ },
24
+ security: {
25
+ name: 'Security & Authentication',
26
+ keywords: ['auth', 'security', 'password', 'token', 'jwt', 'session', 'permission', 'role', 'csrf', 'xss', 'encryption'],
27
+ filePatterns: ['**/auth/**', '**/security/**', 'middleware/**'],
28
+ agents: ['security-expert', 'auth-expert', 'crypto-expert']
29
+ },
30
+ frontend: {
31
+ name: 'Frontend & UI',
32
+ keywords: ['component', 'ui', 'react', 'vue', 'angular', 'css', 'style', 'responsive', 'animation', 'layout'],
33
+ filePatterns: ['components/**', 'pages/**', 'views/**', 'styles/**', '**/*.tsx', '**/*.jsx'],
34
+ agents: ['frontend-expert', 'react-expert', 'ui-expert', 'css-expert']
35
+ },
36
+ backend: {
37
+ name: 'Backend & API',
38
+ keywords: ['api', 'endpoint', 'route', 'controller', 'service', 'rest', 'graphql', 'server', 'middleware'],
39
+ filePatterns: ['api/**', 'routes/**', 'controllers/**', 'services/**'],
40
+ agents: ['backend-expert', 'api-expert', 'nodejs-expert']
41
+ },
42
+ testing: {
43
+ name: 'Testing & QA',
44
+ keywords: ['test', 'spec', 'mock', 'jest', 'vitest', 'cypress', 'coverage', 'e2e', 'unit', 'integration'],
45
+ filePatterns: ['**/*.test.*', '**/*.spec.*', '__tests__/**', 'tests/**'],
46
+ agents: ['testing-expert', 'qa-expert', 'e2e-expert']
47
+ },
48
+ devops: {
49
+ name: 'DevOps & Deployment',
50
+ keywords: ['deploy', 'docker', 'kubernetes', 'ci', 'cd', 'pipeline', 'aws', 'vercel', 'netlify', 'nginx'],
51
+ filePatterns: ['Dockerfile', 'docker-compose.*', '.github/**', 'deploy/**', 'infra/**'],
52
+ agents: ['devops-expert', 'docker-expert', 'aws-expert']
53
+ },
54
+ performance: {
55
+ name: 'Performance & Optimization',
56
+ keywords: ['performance', 'optimize', 'cache', 'lazy', 'bundle', 'speed', 'memory', 'profil', 'benchmark'],
57
+ filePatterns: [],
58
+ agents: ['performance-expert', 'optimization-expert']
59
+ },
60
+ architecture: {
61
+ name: 'Architecture & Design',
62
+ keywords: ['architecture', 'design', 'pattern', 'structure', 'refactor', 'module', 'layer', 'clean', 'solid'],
63
+ filePatterns: [],
64
+ agents: ['architect', 'design-expert', 'refactoring-expert']
65
+ },
66
+ payment: {
67
+ name: 'Payments & Commerce',
68
+ keywords: ['payment', 'stripe', 'checkout', 'subscription', 'billing', 'invoice', 'cart', 'order', 'commerce'],
69
+ filePatterns: ['**/payment/**', '**/checkout/**', '**/billing/**'],
70
+ agents: ['payment-expert', 'stripe-expert', 'ecommerce-expert']
71
+ },
72
+ realtime: {
73
+ name: 'Real-time & WebSockets',
74
+ keywords: ['websocket', 'socket', 'realtime', 'live', 'stream', 'push', 'notification', 'sse'],
75
+ filePatterns: ['**/socket/**', '**/realtime/**', '**/ws/**'],
76
+ agents: ['realtime-expert', 'websocket-expert']
77
+ }
78
+ };
79
+
80
+ /**
81
+ * Complexity assessment factors
82
+ */
83
+ const COMPLEXITY_FACTORS = {
84
+ scope: {
85
+ single_file: 1,
86
+ few_files: 2,
87
+ module: 3,
88
+ cross_module: 4,
89
+ system_wide: 5
90
+ },
91
+ expertise: {
92
+ single_domain: 1,
93
+ two_domains: 2,
94
+ multi_domain: 3,
95
+ specialized: 4
96
+ },
97
+ risk: {
98
+ low: 1,
99
+ medium: 2,
100
+ high: 3,
101
+ critical: 4
102
+ },
103
+ dependencies: {
104
+ none: 1,
105
+ few: 2,
106
+ many: 3,
107
+ complex: 4
108
+ }
109
+ };
110
+
111
+ /**
112
+ * Collaboration patterns
113
+ */
114
+ const COLLABORATION_PATTERNS = {
115
+ sequential: {
116
+ name: 'Sequential',
117
+ description: 'Agents work one after another, passing results forward',
118
+ bestFor: ['layered implementations', 'dependency chains', 'build pipelines']
119
+ },
120
+ parallel: {
121
+ name: 'Parallel',
122
+ description: 'Agents work simultaneously on independent parts',
123
+ bestFor: ['independent components', 'multi-module features', 'test suites']
124
+ },
125
+ iterative: {
126
+ name: 'Iterative',
127
+ description: 'Agents collaborate in review cycles until quality met',
128
+ bestFor: ['security reviews', 'performance optimization', 'refactoring']
129
+ },
130
+ hierarchical: {
131
+ name: 'Hierarchical',
132
+ description: 'Lead agent coordinates specialist agents',
133
+ bestFor: ['complex features', 'full-stack implementations', 'major refactors']
134
+ }
135
+ };
136
+
137
+ /**
138
+ * IntelligentAgentRouter class
139
+ */
140
+ class IntelligentAgentRouter {
141
+ /**
142
+ * @param {Object} options - Configuration options
143
+ */
144
+ constructor(options = {}) {
145
+ this.projectRoot = options.projectRoot || process.cwd();
146
+ this.agentsDir = options.agentsDir || path.join(this.projectRoot, 'agents');
147
+ this.availableAgents = [];
148
+ }
149
+
150
+ /**
151
+ * Initialize the router by loading available agents
152
+ */
153
+ async initialize() {
154
+ await this.loadAvailableAgents();
155
+ return this;
156
+ }
157
+
158
+ /**
159
+ * Load available agents from the agents directory
160
+ */
161
+ async loadAvailableAgents() {
162
+ try {
163
+ const files = await fs.readdir(this.agentsDir);
164
+ const agentFiles = files.filter(f => f.endsWith('.json'));
165
+
166
+ this.availableAgents = [];
167
+ for (const file of agentFiles) {
168
+ try {
169
+ const content = await fs.readFile(path.join(this.agentsDir, file), 'utf-8');
170
+ const agent = JSON.parse(content);
171
+ this.availableAgents.push({
172
+ id: path.basename(file, '.json'),
173
+ ...agent
174
+ });
175
+ } catch {
176
+ // Skip invalid agent files
177
+ }
178
+ }
179
+ } catch {
180
+ this.availableAgents = [];
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Route a task to appropriate agent(s)
186
+ * @param {string} task - Task description
187
+ * @param {Object} context - Additional context
188
+ */
189
+ async route(task, context = {}) {
190
+ // Analyze the task
191
+ const complexity = await this.assessComplexity(task, context);
192
+ const expertise = await this.identifyExpertise(task, context);
193
+
194
+ // Check if collaboration is needed
195
+ const needsCollaboration = this.needsMultipleAgents(task, expertise, complexity);
196
+
197
+ if (needsCollaboration) {
198
+ return this.planAgentCollaboration(task, expertise, complexity, context);
199
+ }
200
+
201
+ // Single agent routing
202
+ return this.routeToSingleAgent(task, expertise, complexity, context);
203
+ }
204
+
205
+ /**
206
+ * Assess task complexity
207
+ * @param {string} task - Task description
208
+ * @param {Object} context - Additional context
209
+ */
210
+ async assessComplexity(task, context = {}) {
211
+ const analysis = {
212
+ score: 0,
213
+ factors: {},
214
+ level: 'low'
215
+ };
216
+
217
+ const taskLower = task.toLowerCase();
218
+
219
+ // Assess scope
220
+ if (taskLower.includes('entire') || taskLower.includes('all') || taskLower.includes('system')) {
221
+ analysis.factors.scope = COMPLEXITY_FACTORS.scope.system_wide;
222
+ } else if (taskLower.includes('module') || taskLower.includes('feature')) {
223
+ analysis.factors.scope = COMPLEXITY_FACTORS.scope.cross_module;
224
+ } else if (taskLower.includes('component') || taskLower.includes('file')) {
225
+ analysis.factors.scope = COMPLEXITY_FACTORS.scope.few_files;
226
+ } else {
227
+ analysis.factors.scope = COMPLEXITY_FACTORS.scope.single_file;
228
+ }
229
+
230
+ // Assess expertise breadth
231
+ const expertiseAreas = this.detectExpertiseAreas(task);
232
+ if (expertiseAreas.length > 2) {
233
+ analysis.factors.expertise = COMPLEXITY_FACTORS.expertise.multi_domain;
234
+ } else if (expertiseAreas.length === 2) {
235
+ analysis.factors.expertise = COMPLEXITY_FACTORS.expertise.two_domains;
236
+ } else {
237
+ analysis.factors.expertise = COMPLEXITY_FACTORS.expertise.single_domain;
238
+ }
239
+
240
+ // Assess risk
241
+ const riskKeywords = ['security', 'payment', 'data', 'migration', 'production', 'delete', 'remove'];
242
+ const hasRiskKeywords = riskKeywords.some(kw => taskLower.includes(kw));
243
+ if (hasRiskKeywords) {
244
+ analysis.factors.risk = COMPLEXITY_FACTORS.risk.high;
245
+ } else if (taskLower.includes('refactor') || taskLower.includes('change')) {
246
+ analysis.factors.risk = COMPLEXITY_FACTORS.risk.medium;
247
+ } else {
248
+ analysis.factors.risk = COMPLEXITY_FACTORS.risk.low;
249
+ }
250
+
251
+ // Assess dependencies
252
+ const depKeywords = ['integrate', 'connect', 'sync', 'depend', 'require'];
253
+ if (depKeywords.some(kw => taskLower.includes(kw))) {
254
+ analysis.factors.dependencies = COMPLEXITY_FACTORS.dependencies.many;
255
+ } else {
256
+ analysis.factors.dependencies = COMPLEXITY_FACTORS.dependencies.few;
257
+ }
258
+
259
+ // Calculate total score
260
+ analysis.score = Object.values(analysis.factors).reduce((sum, val) => sum + val, 0);
261
+
262
+ // Determine level
263
+ if (analysis.score >= 12) {
264
+ analysis.level = 'very_high';
265
+ } else if (analysis.score >= 9) {
266
+ analysis.level = 'high';
267
+ } else if (analysis.score >= 6) {
268
+ analysis.level = 'medium';
269
+ } else {
270
+ analysis.level = 'low';
271
+ }
272
+
273
+ // Determine context needed
274
+ analysis.contextNeeded = analysis.level === 'very_high' ? 'deep' :
275
+ analysis.level === 'high' ? 'standard' : 'minimal';
276
+
277
+ return analysis;
278
+ }
279
+
280
+ /**
281
+ * Detect expertise areas from task
282
+ * @param {string} task - Task description
283
+ */
284
+ detectExpertiseAreas(task) {
285
+ const taskLower = task.toLowerCase();
286
+ const areas = [];
287
+
288
+ for (const [domain, config] of Object.entries(EXPERTISE_DOMAINS)) {
289
+ const matchCount = config.keywords.filter(kw => taskLower.includes(kw)).length;
290
+ if (matchCount > 0) {
291
+ areas.push({
292
+ domain,
293
+ name: config.name,
294
+ matchCount,
295
+ confidence: Math.min(1.0, matchCount / 3)
296
+ });
297
+ }
298
+ }
299
+
300
+ return areas.sort((a, b) => b.matchCount - a.matchCount);
301
+ }
302
+
303
+ /**
304
+ * Identify required expertise for a task
305
+ * @param {string} task - Task description
306
+ * @param {Object} context - Additional context
307
+ */
308
+ async identifyExpertise(task, context = {}) {
309
+ const areas = this.detectExpertiseAreas(task);
310
+ const affectedFiles = context.affectedFiles || [];
311
+
312
+ // Enhance with file pattern matching
313
+ for (const file of affectedFiles) {
314
+ for (const [domain, config] of Object.entries(EXPERTISE_DOMAINS)) {
315
+ for (const pattern of config.filePatterns) {
316
+ const regex = this.globToRegex(pattern);
317
+ if (regex.test(file)) {
318
+ const existing = areas.find(a => a.domain === domain);
319
+ if (existing) {
320
+ existing.confidence = Math.min(1.0, existing.confidence + 0.2);
321
+ } else {
322
+ areas.push({
323
+ domain,
324
+ name: config.name,
325
+ matchCount: 1,
326
+ confidence: 0.5
327
+ });
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ return {
335
+ primary: areas[0] || null,
336
+ secondary: areas.slice(1, 3),
337
+ all: areas,
338
+ isMultiDomain: areas.length > 1
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Convert glob pattern to regex
344
+ * @param {string} glob - Glob pattern
345
+ */
346
+ globToRegex(glob) {
347
+ const escaped = glob
348
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
349
+ .replace(/\*\*/g, '.*')
350
+ .replace(/\*/g, '[^/]*');
351
+ return new RegExp(escaped);
352
+ }
353
+
354
+ /**
355
+ * Check if task needs multiple agents
356
+ * @param {string} task - Task description
357
+ * @param {Object} expertise - Expertise analysis
358
+ * @param {Object} complexity - Complexity analysis
359
+ */
360
+ needsMultipleAgents(task, expertise, complexity) {
361
+ // Multi-domain tasks need collaboration
362
+ if (expertise.isMultiDomain && expertise.all.length >= 2) {
363
+ return true;
364
+ }
365
+
366
+ // High complexity tasks may need collaboration
367
+ if (complexity.level === 'very_high' || complexity.level === 'high') {
368
+ return true;
369
+ }
370
+
371
+ // Risk-heavy tasks benefit from review
372
+ if (complexity.factors.risk >= 3) {
373
+ return true;
374
+ }
375
+
376
+ // System-wide scope needs coordination
377
+ if (complexity.factors.scope >= 4) {
378
+ return true;
379
+ }
380
+
381
+ return false;
382
+ }
383
+
384
+ /**
385
+ * Route to a single agent
386
+ * @param {string} task - Task description
387
+ * @param {Object} expertise - Expertise analysis
388
+ * @param {Object} complexity - Complexity analysis
389
+ * @param {Object} context - Additional context
390
+ */
391
+ routeToSingleAgent(task, expertise, complexity, context) {
392
+ const primaryDomain = expertise.primary?.domain || 'backend';
393
+ const domainConfig = EXPERTISE_DOMAINS[primaryDomain];
394
+
395
+ // Find best matching available agent
396
+ const primaryAgent = this.findBestAgent(domainConfig.agents, expertise);
397
+
398
+ // Find fallback agents
399
+ const fallbackAgents = this.findFallbackAgents(domainConfig.agents, primaryAgent);
400
+
401
+ return {
402
+ mode: 'single',
403
+ primaryAgent,
404
+ fallbackAgents,
405
+ expertise: expertise.primary,
406
+ complexity,
407
+ contextLevel: complexity.contextNeeded,
408
+ suggestedApproach: this.planApproach(task, complexity),
409
+ estimatedEffort: this.estimateEffort(complexity)
410
+ };
411
+ }
412
+
413
+ /**
414
+ * Plan multi-agent collaboration
415
+ * @param {string} task - Task description
416
+ * @param {Object} expertise - Expertise analysis
417
+ * @param {Object} complexity - Complexity analysis
418
+ * @param {Object} context - Additional context
419
+ */
420
+ planAgentCollaboration(task, expertise, complexity, context) {
421
+ // Determine collaboration pattern
422
+ const pattern = this.selectCollaborationPattern(task, expertise, complexity);
423
+
424
+ // Build agent sequence
425
+ const agents = this.buildAgentSequence(expertise, pattern, context);
426
+
427
+ // Define handoff points
428
+ const handoffs = this.defineHandoffs(agents, pattern);
429
+
430
+ return {
431
+ mode: 'collaboration',
432
+ collaborationType: pattern.name.toLowerCase(),
433
+ pattern,
434
+ agents,
435
+ handoffPoints: handoffs,
436
+ coordinatorAgent: this.selectCoordinator(agents, complexity),
437
+ complexity,
438
+ suggestedApproach: this.planCollaborativeApproach(task, pattern, agents),
439
+ estimatedEffort: this.estimateCollaborativeEffort(agents, complexity)
440
+ };
441
+ }
442
+
443
+ /**
444
+ * Select best collaboration pattern
445
+ * @param {string} task - Task description
446
+ * @param {Object} expertise - Expertise analysis
447
+ * @param {Object} complexity - Complexity analysis
448
+ */
449
+ selectCollaborationPattern(task, expertise, complexity) {
450
+ const taskLower = task.toLowerCase();
451
+
452
+ // Security or review tasks -> iterative
453
+ if (taskLower.includes('review') || taskLower.includes('security') || taskLower.includes('audit')) {
454
+ return COLLABORATION_PATTERNS.iterative;
455
+ }
456
+
457
+ // Independent components -> parallel
458
+ if (taskLower.includes('component') || expertise.all.length >= 3) {
459
+ return COLLABORATION_PATTERNS.parallel;
460
+ }
461
+
462
+ // Complex full-stack -> hierarchical
463
+ if (complexity.level === 'very_high') {
464
+ return COLLABORATION_PATTERNS.hierarchical;
465
+ }
466
+
467
+ // Default to sequential
468
+ return COLLABORATION_PATTERNS.sequential;
469
+ }
470
+
471
+ /**
472
+ * Build agent sequence for collaboration
473
+ * @param {Object} expertise - Expertise analysis
474
+ * @param {Object} pattern - Collaboration pattern
475
+ * @param {Object} context - Additional context
476
+ */
477
+ buildAgentSequence(expertise, pattern, context) {
478
+ const agents = [];
479
+ let phase = 1;
480
+
481
+ // Add agents based on expertise areas
482
+ for (const area of expertise.all) {
483
+ const domainConfig = EXPERTISE_DOMAINS[area.domain];
484
+ const agent = this.findBestAgent(domainConfig.agents, expertise);
485
+
486
+ if (agent && !agents.find(a => a.agent === agent)) {
487
+ agents.push({
488
+ agent,
489
+ domain: area.domain,
490
+ phase: pattern.name === 'Parallel' ? 1 : phase++,
491
+ focus: `Handle ${area.name} aspects`,
492
+ confidence: area.confidence,
493
+ input: phase > 1 ? agents[agents.length - 1]?.output : null,
494
+ output: `${area.domain} implementation`
495
+ });
496
+ }
497
+ }
498
+
499
+ // Ensure at least 2 agents for collaboration
500
+ if (agents.length < 2 && expertise.primary) {
501
+ const reviewAgent = this.findReviewAgent(expertise.primary.domain);
502
+ if (reviewAgent) {
503
+ agents.push({
504
+ agent: reviewAgent,
505
+ domain: 'review',
506
+ phase: agents.length + 1,
507
+ focus: 'Review and validate implementation',
508
+ input: agents[agents.length - 1]?.output,
509
+ output: 'Validated implementation'
510
+ });
511
+ }
512
+ }
513
+
514
+ return agents;
515
+ }
516
+
517
+ /**
518
+ * Find best matching agent from candidates
519
+ * @param {Array} candidates - Candidate agent names
520
+ * @param {Object} expertise - Expertise analysis
521
+ */
522
+ findBestAgent(candidates, expertise) {
523
+ // Check against available agents
524
+ for (const candidate of candidates) {
525
+ const available = this.availableAgents.find(a =>
526
+ a.id === candidate ||
527
+ a.name?.toLowerCase().includes(candidate.replace('-expert', '')) ||
528
+ a.expertise?.includes(expertise.primary?.domain)
529
+ );
530
+ if (available) {
531
+ return available.id;
532
+ }
533
+ }
534
+
535
+ // Return first candidate as default
536
+ return candidates[0] || 'general-expert';
537
+ }
538
+
539
+ /**
540
+ * Find fallback agents
541
+ * @param {Array} candidates - Candidate agent names
542
+ * @param {string} primaryAgent - Primary agent already selected
543
+ */
544
+ findFallbackAgents(candidates, primaryAgent) {
545
+ return candidates
546
+ .filter(c => c !== primaryAgent)
547
+ .slice(0, 2);
548
+ }
549
+
550
+ /**
551
+ * Find a review agent for a domain
552
+ * @param {string} domain - Primary domain
553
+ */
554
+ findReviewAgent(domain) {
555
+ const reviewAgents = {
556
+ database: 'database-reviewer',
557
+ security: 'security-auditor',
558
+ frontend: 'ui-reviewer',
559
+ backend: 'code-reviewer',
560
+ default: 'code-reviewer'
561
+ };
562
+
563
+ return reviewAgents[domain] || reviewAgents.default;
564
+ }
565
+
566
+ /**
567
+ * Define handoff points between agents
568
+ * @param {Array} agents - Agent sequence
569
+ * @param {Object} pattern - Collaboration pattern
570
+ */
571
+ defineHandoffs(agents, pattern) {
572
+ const handoffs = [];
573
+
574
+ if (pattern.name === 'Sequential' || pattern.name === 'Hierarchical') {
575
+ for (let i = 0; i < agents.length - 1; i++) {
576
+ handoffs.push({
577
+ from: agents[i].agent,
578
+ to: agents[i + 1].agent,
579
+ artifact: agents[i].output,
580
+ validation: this.getHandoffValidation(agents[i], agents[i + 1])
581
+ });
582
+ }
583
+ } else if (pattern.name === 'Iterative') {
584
+ // Create review loop
585
+ if (agents.length >= 2) {
586
+ handoffs.push({
587
+ from: agents[0].agent,
588
+ to: agents[1].agent,
589
+ artifact: 'Implementation',
590
+ validation: 'Review and feedback'
591
+ });
592
+ handoffs.push({
593
+ from: agents[1].agent,
594
+ to: agents[0].agent,
595
+ artifact: 'Feedback',
596
+ validation: 'Address feedback',
597
+ isLoop: true
598
+ });
599
+ }
600
+ }
601
+
602
+ return handoffs;
603
+ }
604
+
605
+ /**
606
+ * Get validation requirements for a handoff
607
+ * @param {Object} fromAgent - Source agent
608
+ * @param {Object} toAgent - Target agent
609
+ */
610
+ getHandoffValidation(fromAgent, toAgent) {
611
+ const validations = {
612
+ 'database-review': 'Schema validation and migration check',
613
+ 'security-review': 'Security audit and vulnerability scan',
614
+ 'code-review': 'Code quality and pattern compliance',
615
+ 'test-review': 'Test coverage and passing status'
616
+ };
617
+
618
+ return validations[`${fromAgent.domain}-${toAgent.domain}`] ||
619
+ `Validate ${fromAgent.output}`;
620
+ }
621
+
622
+ /**
623
+ * Select coordinator for hierarchical collaboration
624
+ * @param {Array} agents - Agent sequence
625
+ * @param {Object} complexity - Complexity analysis
626
+ */
627
+ selectCoordinator(agents, complexity) {
628
+ // For very complex tasks, use architect
629
+ if (complexity.level === 'very_high') {
630
+ return 'architect';
631
+ }
632
+
633
+ // Otherwise, the first agent coordinates
634
+ return agents[0]?.agent || 'project-lead';
635
+ }
636
+
637
+ /**
638
+ * Plan approach for single agent
639
+ * @param {string} task - Task description
640
+ * @param {Object} complexity - Complexity analysis
641
+ */
642
+ planApproach(task, complexity) {
643
+ if (complexity.level === 'low') {
644
+ return {
645
+ steps: ['Implement directly', 'Test', 'Complete'],
646
+ reviewNeeded: false
647
+ };
648
+ }
649
+
650
+ return {
651
+ steps: [
652
+ 'Analyze existing code',
653
+ 'Plan implementation',
654
+ 'Implement changes',
655
+ 'Write/update tests',
656
+ 'Review and refine',
657
+ 'Complete'
658
+ ],
659
+ reviewNeeded: complexity.factors.risk >= 2
660
+ };
661
+ }
662
+
663
+ /**
664
+ * Plan collaborative approach
665
+ * @param {string} task - Task description
666
+ * @param {Object} pattern - Collaboration pattern
667
+ * @param {Array} agents - Agent sequence
668
+ */
669
+ planCollaborativeApproach(task, pattern, agents) {
670
+ return {
671
+ pattern: pattern.name,
672
+ steps: agents.map(a => ({
673
+ agent: a.agent,
674
+ phase: a.phase,
675
+ action: a.focus
676
+ })),
677
+ coordination: pattern.name === 'Hierarchical' ? 'Lead agent coordinates' : 'Self-coordinated',
678
+ reviewCycles: pattern.name === 'Iterative' ? 2 : 1
679
+ };
680
+ }
681
+
682
+ /**
683
+ * Estimate effort for single agent
684
+ * @param {Object} complexity - Complexity analysis
685
+ */
686
+ estimateEffort(complexity) {
687
+ const baseHours = {
688
+ low: 1,
689
+ medium: 3,
690
+ high: 8,
691
+ very_high: 16
692
+ };
693
+
694
+ const hours = baseHours[complexity.level] || 4;
695
+
696
+ return {
697
+ hours,
698
+ range: `${Math.round(hours * 0.7)}-${Math.round(hours * 1.5)} hours`,
699
+ confidence: complexity.level === 'low' ? 'high' : 'medium'
700
+ };
701
+ }
702
+
703
+ /**
704
+ * Estimate effort for collaboration
705
+ * @param {Array} agents - Agent sequence
706
+ * @param {Object} complexity - Complexity analysis
707
+ */
708
+ estimateCollaborativeEffort(agents, complexity) {
709
+ const singleEstimate = this.estimateEffort(complexity);
710
+ const agentCount = agents.length;
711
+
712
+ // Collaboration overhead
713
+ const overheadFactor = 1 + (agentCount - 1) * 0.3;
714
+
715
+ const hours = singleEstimate.hours * overheadFactor;
716
+
717
+ return {
718
+ hours: Math.round(hours),
719
+ range: `${Math.round(hours * 0.7)}-${Math.round(hours * 1.5)} hours`,
720
+ confidence: 'medium',
721
+ breakdown: agents.map(a => ({
722
+ agent: a.agent,
723
+ estimatedHours: Math.round(hours / agentCount)
724
+ }))
725
+ };
726
+ }
727
+
728
+ /**
729
+ * Get routing recommendation for a task
730
+ * @param {string} task - Task description
731
+ * @param {Object} context - Additional context
732
+ */
733
+ async getRecommendation(task, context = {}) {
734
+ const routing = await this.route(task, context);
735
+
736
+ return {
737
+ routing,
738
+ reasoning: this.explainRouting(routing),
739
+ alternatives: this.suggestAlternatives(routing)
740
+ };
741
+ }
742
+
743
+ /**
744
+ * Explain routing decision
745
+ * @param {Object} routing - Routing result
746
+ */
747
+ explainRouting(routing) {
748
+ const reasons = [];
749
+
750
+ if (routing.mode === 'single') {
751
+ reasons.push(`Task matches ${routing.expertise?.name || 'general'} expertise`);
752
+ reasons.push(`Complexity level: ${routing.complexity.level}`);
753
+ reasons.push(`Single agent sufficient for this task`);
754
+ } else {
755
+ reasons.push(`Multi-domain task requires collaboration`);
756
+ reasons.push(`${routing.pattern.name} pattern selected: ${routing.pattern.description}`);
757
+ reasons.push(`${routing.agents.length} agents involved`);
758
+ }
759
+
760
+ return reasons;
761
+ }
762
+
763
+ /**
764
+ * Suggest alternative routing options
765
+ * @param {Object} routing - Current routing result
766
+ */
767
+ suggestAlternatives(routing) {
768
+ const alternatives = [];
769
+
770
+ if (routing.mode === 'single' && routing.complexity.level !== 'low') {
771
+ alternatives.push({
772
+ mode: 'collaboration',
773
+ reason: 'Consider adding a reviewer for higher quality',
774
+ agents: [routing.primaryAgent, 'code-reviewer']
775
+ });
776
+ }
777
+
778
+ if (routing.mode === 'collaboration' && routing.agents.length > 2) {
779
+ alternatives.push({
780
+ mode: 'single',
781
+ reason: 'Could simplify by using primary expert only',
782
+ agent: routing.agents[0]?.agent
783
+ });
784
+ }
785
+
786
+ return alternatives;
787
+ }
788
+ }
789
+
790
+ module.exports = {
791
+ IntelligentAgentRouter,
792
+ EXPERTISE_DOMAINS,
793
+ COMPLEXITY_FACTORS,
794
+ COLLABORATION_PATTERNS
795
+ };