UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

340 lines (339 loc) 13.8 kB
import { findBestSemanticMatch } from '../../services/routing/semanticMatcher.js'; import { readFile } from 'fs/promises'; import { getProjectRoot } from '../../tools/code-map-generator/utils/pathUtils.enhanced.js'; import path from 'path'; import logger from '../../logger.js'; export class UnifiedIntentRegistry { static instance; toolConfigs = {}; intentPatterns = new Map(); keywordMappings = new Map(); initialized = false; constructor() { } static getInstance() { if (!UnifiedIntentRegistry.instance) { UnifiedIntentRegistry.instance = new UnifiedIntentRegistry(); } return UnifiedIntentRegistry.instance; } async initialize() { if (this.initialized) return; try { const projectRoot = getProjectRoot(); const configPath = path.join(projectRoot, 'mcp-config.json'); const configContent = await readFile(configPath, 'utf-8'); const config = JSON.parse(configContent); this.toolConfigs = config.tools; await this.buildIntentPatterns(); await this.buildKeywordMappings(); this.initialized = true; logger.info({ toolCount: Object.keys(this.toolConfigs).length, patternCount: this.intentPatterns.size }, 'UnifiedIntentRegistry initialized with existing tool configurations'); } catch (error) { logger.error({ err: error }, 'Failed to initialize UnifiedIntentRegistry'); throw new Error(`Intent registry initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async recognizeIntentWithToolSelection(input, context, _config) { await this.initialize(); const toolCandidates = []; try { const semanticMatch = await this.performSemanticMatching(input); if (semanticMatch) { toolCandidates.push(...semanticMatch); } const patternMatches = await this.performPatternMatching(input); toolCandidates.push(...patternMatches); const keywordMatches = await this.performKeywordMatching(input); toolCandidates.push(...keywordMatches); this.applyContextAwareBoosting(toolCandidates, context); const rankedCandidates = this.rankAndDeduplicateCandidates(toolCandidates); if (rankedCandidates.length === 0) { return null; } const bestCandidate = rankedCandidates[0]; const intent = await this.createRecognizedIntent(input, bestCandidate, context); return { intent, toolCandidates: rankedCandidates }; } catch (error) { logger.error({ err: error, input }, 'Intent recognition with tool selection failed'); return null; } } async performSemanticMatching(input) { try { const semanticMatch = await findBestSemanticMatch(input); if (semanticMatch && semanticMatch.confidence >= 0.7) { return [{ tool: semanticMatch.toolName, confidence: semanticMatch.confidence, reason: `Semantic match: ${semanticMatch.matchedPattern || 'High similarity'}`, matchType: 'semantic' }]; } return []; } catch (error) { logger.debug({ err: error }, 'Semantic matching failed, continuing with other methods'); return []; } } async performPatternMatching(input) { const matches = []; const normalizedInput = input.toLowerCase().trim(); for (const [toolName, patterns] of this.intentPatterns.entries()) { for (const pattern of patterns) { const match = pattern.exec(normalizedInput); if (match) { const confidence = this.calculatePatternConfidence(match, pattern, normalizedInput); matches.push({ tool: toolName, confidence, reason: `Pattern match: ${pattern.source}`, matchType: 'pattern' }); break; } } } return matches; } async performKeywordMatching(input) { const matches = []; const normalizedInput = input.toLowerCase().trim(); const inputWords = normalizedInput.split(/\s+/); for (const [toolName, keywords] of this.keywordMappings.entries()) { let keywordScore = 0; let matchingKeywords = 0; for (const keyword of keywords) { const keywordWords = keyword.toLowerCase().split(/\s+/); if (normalizedInput.includes(keyword.toLowerCase())) { keywordScore += 1.0; matchingKeywords++; } else { const partialMatches = keywordWords.filter(word => inputWords.some(inputWord => inputWord.includes(word) || word.includes(inputWord))); if (partialMatches.length > 0) { keywordScore += (partialMatches.length / keywordWords.length) * 0.6; matchingKeywords++; } } } if (keywordScore > 0.3) { const confidence = Math.min(keywordScore / keywords.length, 0.9); matches.push({ tool: toolName, confidence, reason: `Keyword match: ${matchingKeywords} keywords matched`, matchType: 'keyword' }); } } return matches; } applyContextAwareBoosting(candidates, context) { for (const candidate of candidates) { const preference = context.preferredTools[candidate.tool] || 0; candidate.confidence += (preference * 0.1); const recentSuccess = context.toolHistory .slice(-5) .find(h => h.tool === candidate.tool && h.success); if (recentSuccess) { candidate.confidence += 0.05; } if (context.activeWorkflow && this.isToolRelevantToWorkflow(candidate.tool, context.activeWorkflow)) { candidate.confidence += 0.1; } candidate.confidence = Math.min(candidate.confidence, 1.0); } } rankAndDeduplicateCandidates(candidates) { const toolMap = new Map(); for (const candidate of candidates) { const existing = toolMap.get(candidate.tool); if (!existing || candidate.confidence > existing.confidence) { toolMap.set(candidate.tool, candidate); } } return Array.from(toolMap.values()) .sort((a, b) => b.confidence - a.confidence) .slice(0, 5); } async createRecognizedIntent(input, candidate, context) { const entities = await this.extractEntitiesForTool(candidate.tool, input, context); return { intent: this.mapToolToIntent(candidate.tool), confidence: candidate.confidence, confidenceLevel: this.mapConfidenceLevel(candidate.confidence), entities, originalInput: input, processedInput: input.toLowerCase().trim(), alternatives: [], metadata: { processingTime: 0, method: this.mapMethodType(candidate.matchType), timestamp: new Date() } }; } async buildIntentPatterns() { for (const [toolName, config] of Object.entries(this.toolConfigs)) { const patterns = []; for (const pattern of config.input_patterns) { try { const regexPattern = pattern .replace(/\{[^}]+\}/g, '([\\w\\s\\-_]+)') .replace(/\s+/g, '\\s+') .replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); patterns.push(new RegExp(regexPattern, 'i')); } catch (error) { logger.debug({ err: error, pattern }, `Invalid pattern for ${toolName}`); } } this.intentPatterns.set(toolName, patterns); } } async buildKeywordMappings() { for (const [toolName, config] of Object.entries(this.toolConfigs)) { const keywords = []; keywords.push(...config.use_cases); const descWords = config.description .toLowerCase() .split(/[^\w]+/) .filter(word => word.length > 3); keywords.push(...descWords.slice(0, 10)); for (const pattern of config.input_patterns) { const patternWords = pattern .replace(/\{[^}]+\}/g, '') .split(/[^\w]+/) .filter(word => word.length > 2); keywords.push(...patternWords); } this.keywordMappings.set(toolName, [...new Set(keywords)]); } } calculatePatternConfidence(match, pattern, input) { let confidence = 0.8; if (match[0].length > input.length * 0.5) { confidence += 0.1; } if (match.length > 1) { confidence += 0.05; } return Math.min(confidence, 0.95); } isToolRelevantToWorkflow(toolName, workflowName) { const workflowRelevance = { 'full-stack-development': ['fullstack-starter-kit-generator', 'rules-generator', 'prd-generator'], 'research-and-plan': ['research-manager', 'prd-generator', 'user-stories-generator'], 'code-analysis': ['map-codebase', 'curate-context', 'rules-generator'], 'project-setup': ['fullstack-starter-kit-generator', 'task-list-generator', 'vibe-task-manager'] }; return workflowRelevance[workflowName]?.includes(toolName) || false; } mapToolToIntent(toolName) { const intentMappings = { 'research-manager': 'unknown', 'prd-generator': 'parse_prd', 'user-stories-generator': 'parse_tasks', 'task-list-generator': 'parse_tasks', 'fullstack-starter-kit-generator': 'create_project', 'rules-generator': 'create_project', 'map-codebase': 'search_files', 'curate-context': 'search_content', 'run-workflow': 'run_task', 'vibe-task-manager': 'create_project', 'get-job-result': 'check_status', 'register-agent': 'unknown', 'get-agent-tasks': 'unknown', 'submit-task-response': 'unknown', 'process-request': 'unknown' }; return intentMappings[toolName] || 'unknown'; } mapConfidenceLevel(confidence) { if (confidence >= 0.9) return 'very_high'; if (confidence >= 0.8) return 'high'; if (confidence >= 0.6) return 'medium'; if (confidence >= 0.4) return 'low'; return 'very_low'; } mapMethodType(matchType) { switch (matchType) { case 'pattern': case 'keyword': return 'pattern'; case 'semantic': return 'llm'; case 'fallback': return 'hybrid'; default: return 'hybrid'; } } async extractEntitiesForTool(toolName, input, _context) { const entities = []; const patterns = { names: /(?:for|called|named|project|feature)\s+"([^"]+)"|(?:for|called|named|project|feature)\s+([^\s]+)/gi, files: /([^\s]+\.(js|ts|py|md|json|html|css|txt))/gi, numbers: /\b(\d+)\b/g, tech: /\b(react|angular|vue|node|python|java|typescript|javascript|docker|kubernetes)\b/gi }; let match; while ((match = patterns.names.exec(input)) !== null) { entities.push({ type: this.getEntityTypeForTool(toolName, 'name'), value: match[1] || match[2], confidence: 0.9 }); } patterns.files.lastIndex = 0; while ((match = patterns.files.exec(input)) !== null) { entities.push({ type: 'file_path', value: match[1], confidence: 0.8 }); } patterns.numbers.lastIndex = 0; while ((match = patterns.numbers.exec(input)) !== null) { entities.push({ type: 'number', value: match[1], confidence: 0.7 }); } patterns.tech.lastIndex = 0; while ((match = patterns.tech.exec(input)) !== null) { entities.push({ type: 'technology', value: match[1].toLowerCase(), confidence: 0.8 }); } return entities; } getEntityTypeForTool(toolName, baseType) { const toolEntityMappings = { 'prd-generator': { name: 'product_name' }, 'user-stories-generator': { name: 'feature_name' }, 'fullstack-starter-kit-generator': { name: 'project_name' }, 'research-manager': { name: 'topic' }, 'map-codebase': { name: 'project_name' } }; return toolEntityMappings[toolName]?.[baseType] || baseType; } }