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
JavaScript
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;
}
}