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.

1,086 lines (1,085 loc) 47.6 kB
import { IntentRecognitionEngine } from './intent-recognizer.js'; import logger from '../../../logger.js'; export class CommandGateway { static instance; intentRecognizer; config; commandHistory = new Map(); contextCache = new Map(); sessionMetrics = new Map(); intentSuccessMetrics = new Map(); constructor() { this.intentRecognizer = IntentRecognitionEngine.getInstance(); this.config = { maxProcessingTime: 10000, trackHistory: true, maxHistoryEntries: 50, enableContextAware: true, autoExecuteThreshold: 0.8 }; } static getInstance() { if (!CommandGateway.instance) { CommandGateway.instance = new CommandGateway(); } return CommandGateway.instance; } async processCommand(input, context = {}) { const startTime = Date.now(); const sessionId = context.sessionId || 'default'; try { logger.info({ sessionId, input: input.substring(0, 100) }, 'Processing natural language command'); const commandContext = this.getOrCreateContext(sessionId, context); const recognitionResult = await this.intentRecognizer.recognizeIntent(input, this.buildRecognitionContext(commandContext)); if (!recognitionResult) { return this.createFailureResult(input, 'Unable to understand the command. Please try rephrasing or use a more specific request.', ['Try: "Create a project called MyApp"', 'Try: "List all tasks"', 'Try: "Run task 123"'], startTime); } const recognizedIntent = { intent: recognitionResult.intent, confidence: recognitionResult.confidence, confidenceLevel: recognitionResult.confidenceLevel, entities: this.convertEntitiesToArray(recognitionResult.entities), originalInput: input, processedInput: input.toLowerCase().trim(), alternatives: recognitionResult.alternatives.map(alt => ({ intent: alt.intent, confidence: alt.confidence })), metadata: { processingTime: recognitionResult.processingTime, method: recognitionResult.strategy === 'pattern' ? 'pattern' : recognitionResult.strategy === 'llm' ? 'llm' : 'hybrid', timestamp: recognitionResult.metadata.timestamp } }; logger.info({ intent: recognizedIntent.intent, confidence: recognizedIntent.confidence, strategy: recognitionResult.strategy, rawEntities: recognitionResult.entities, convertedEntities: recognizedIntent.entities, originalInput: input }, 'Intent recognition and entity extraction debug'); if (this.config.trackHistory) { this.updateCommandHistory(sessionId, recognizedIntent); } const validation = await this.validateCommand(recognizedIntent, commandContext); if (!validation.isValid) { return this.createValidationErrorResult(recognizedIntent, validation, startTime); } const requiresConfirmation = this.shouldRequireConfirmation(recognizedIntent, validation); const toolParams = await this.mapIntentToToolParams(recognizedIntent, validation.normalizedParams); logger.info({ intent: recognizedIntent.intent, entities: recognizedIntent.entities, normalizedParams: validation.normalizedParams, toolParams, originalInput: input }, 'CommandGateway parameter extraction debug'); const processingTime = Date.now() - startTime; this.trackIntentSuccess(recognizedIntent.intent, true, input); return { success: true, intent: recognizedIntent, toolParams, validationErrors: [], suggestions: validation.suggestions, metadata: { processingTime, confidence: recognizedIntent.confidence, requiresConfirmation, ambiguousInput: recognizedIntent.confidence < 0.7 } }; } catch (error) { logger.error({ err: error, sessionId, input }, 'Command processing failed'); this.trackIntentSuccess('unknown', false, input, error instanceof Error ? error.message : 'Unknown error'); return this.createFailureResult(input, `Command processing failed: ${error instanceof Error ? error.message : 'Unknown error'}`, ['Please try again with a simpler command', 'Check your input for typos'], startTime); } } getOrCreateContext(sessionId, partialContext) { let context = this.contextCache.get(sessionId); if (!context) { context = { sessionId, userId: partialContext.userId, currentProject: partialContext.currentProject, currentTask: partialContext.currentTask, conversationHistory: [], userPreferences: {} }; this.contextCache.set(sessionId, context); } if (partialContext.currentProject) { context.currentProject = partialContext.currentProject; } if (partialContext.currentTask) { context.currentTask = partialContext.currentTask; } if (partialContext.userPreferences) { Object.assign(context.userPreferences, partialContext.userPreferences); } return context; } buildRecognitionContext(context) { return { currentProject: context.currentProject, currentTask: context.currentTask, recentIntents: context.conversationHistory.slice(-5).map(h => h.intent), userPreferences: context.userPreferences, sessionId: context.sessionId }; } updateCommandHistory(sessionId, intent) { let history = this.commandHistory.get(sessionId) || []; history.push(intent); if (history.length > this.config.maxHistoryEntries) { history = history.slice(-this.config.maxHistoryEntries); } this.commandHistory.set(sessionId, history); const context = this.contextCache.get(sessionId); if (context) { context.conversationHistory = history; } } async validateCommand(intent, context) { const errors = []; const warnings = []; const suggestions = []; const normalizedParams = {}; for (const entity of intent.entities) { const paramName = this.mapEntityTypeToParamName(entity.type); normalizedParams[paramName] = entity.value; } switch (intent.intent) { case 'create_project': return this.validateCreateProject(intent, normalizedParams, errors, warnings, suggestions); case 'create_task': return this.validateCreateTask(intent, context, normalizedParams, errors, warnings, suggestions); case 'list_projects': case 'list_tasks': return this.validateListCommand(intent, normalizedParams, errors, warnings, suggestions); case 'run_task': return this.validateRunTask(intent, context, normalizedParams, errors, warnings, suggestions); case 'check_status': return this.validateStatusCheck(intent, context, normalizedParams, errors, warnings, suggestions); case 'decompose_task': return this.validateDecomposeTask(intent, context, normalizedParams, errors, warnings, suggestions); case 'decompose_epic': return this.validateDecomposeEpic(intent, context, normalizedParams, errors, warnings, suggestions); case 'decompose_project': return this.validateDecomposeProject(intent, context, normalizedParams, errors, warnings, suggestions); case 'search_files': return this.validateSearchFiles(intent, context, normalizedParams, errors, warnings, suggestions); case 'search_content': return this.validateSearchContent(intent, context, normalizedParams, errors, warnings, suggestions); case 'parse_prd': case 'parse_tasks': case 'import_artifact': return this.validateArtifactOperation(intent, context, normalizedParams, errors, warnings, suggestions); case 'update_project': return this.validateUpdateProject(intent, context, normalizedParams, errors, warnings, suggestions); case 'assign_task': return this.validateAssignTask(intent, context, normalizedParams, errors, warnings, suggestions); default: errors.push(`Unsupported intent: ${intent.intent}`); suggestions.push('Try using a supported command like create, list, run, status, or decompose'); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams }; } validateCreateProject(intent, params, errors, warnings, suggestions) { if (!params.projectName) { errors.push('Project name is required'); suggestions.push('Try: "Create a project called MyApp"'); } else { params.projectName = String(params.projectName).trim(); if (!/^[a-zA-Z0-9\-_\s]+$/.test(String(params.projectName))) { warnings.push('Project name contains special characters that may cause issues'); suggestions.push('Consider using only letters, numbers, hyphens, and underscores'); } } if (!params.description) { params.description = `Project: ${params.projectName}`; warnings.push('No description provided, using default'); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateCreateTask(intent, context, params, errors, warnings, suggestions) { const hasTaskTitle = params.taskTitle || params.taskName || (params.words && Array.isArray(params.words) && params.words.length > 0); if (!hasTaskTitle) { errors.push('Task title is required'); suggestions.push('Try: "Create a task for implementing authentication"'); } else { if (params.words && Array.isArray(params.words) && params.words.length > 0) { params.taskTitle = params.words.join(' '); warnings.push('Task title extracted from input'); } } if (!params.projectName && context.currentProject) { params.projectName = context.currentProject; warnings.push(`Using current project: ${context.currentProject}`); } else if (!params.projectName) { errors.push('Project name is required when no current project is set'); suggestions.push('Specify a project or set a current project first'); } if (params.priority) { const validPriorities = ['low', 'medium', 'high', 'critical']; if (!validPriorities.includes(String(params.priority).toLowerCase())) { warnings.push('Invalid priority, using medium as default'); params.priority = 'medium'; } else { params.priority = String(params.priority).toLowerCase(); } } else { params.priority = 'medium'; } if (params.type) { const validTypes = ['development', 'testing', 'documentation', 'research', 'bug', 'feature']; if (!validTypes.includes(String(params.type).toLowerCase())) { warnings.push('Invalid task type, using development as default'); params.type = 'development'; } else { params.type = String(params.type).toLowerCase(); } } else { params.type = 'development'; } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateListCommand(intent, params, errors, warnings, suggestions) { if (params.status) { const validStatuses = ['pending', 'in_progress', 'completed', 'blocked', 'cancelled']; if (!validStatuses.includes(String(params.status).toLowerCase())) { warnings.push('Invalid status filter, showing all items'); delete params.status; } else { params.status = String(params.status).toLowerCase(); } } if (params.timeframe) { const validTimeframes = ['today', 'tomorrow', 'this week', 'next week', 'this month']; const timeframeStr = String(params.timeframe).toLowerCase(); if (!validTimeframes.includes(timeframeStr) && !/^\d{4}-\d{2}-\d{2}$/.test(timeframeStr)) { warnings.push('Invalid timeframe filter, showing all items'); delete params.timeframe; } } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateRunTask(intent, context, params, errors, warnings, suggestions) { const hasTaskId = params.taskId || params.taskTitle || (params.numbers && Array.isArray(params.numbers) && params.numbers.length > 0); if (!hasTaskId) { errors.push('Task ID or task title is required'); suggestions.push('Try: "Run task 123" or "Run the authentication task"'); } else { if (params.numbers && Array.isArray(params.numbers) && params.numbers.length > 0) { params.taskId = `task-${params.numbers[0]}`; warnings.push('Task ID extracted from number'); } if (params.taskTitle && !params.taskId) { warnings.push('Task title provided, will attempt to resolve to task ID'); } } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateStatusCheck(intent, context, params, errors, warnings, suggestions) { if (!params.projectName && !params.taskId && context.currentProject) { params.projectName = context.currentProject; warnings.push(`Checking status of current project: ${context.currentProject}`); } if (!params.projectName && !params.taskId) { warnings.push('No specific target, showing general status'); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateDecomposeTask(intent, context, params, errors, warnings, suggestions) { const hasTaskId = params.taskId || params.taskTitle; if (!hasTaskId) { errors.push('Task ID or task title is required for decomposition'); suggestions.push('Try: "Decompose task T001" or "Break down the authentication task"'); } else { if (params.taskTitle && !params.taskId) { warnings.push('Task title provided, will attempt to resolve to task ID'); } } if (params.decompositionScope) { const validScopes = ['development tasks', 'implementation steps', 'technical tasks', 'all aspects']; const scope = String(params.decompositionScope).toLowerCase(); if (!validScopes.some(validScope => scope.includes(validScope))) { warnings.push('Decomposition scope may be too broad or unclear'); suggestions.push('Consider specifying: development tasks, implementation steps, or technical tasks'); } } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateDecomposeEpic(intent, context, params, errors, warnings, suggestions) { const hasEpicId = params.epicId || params.epicTitle; if (!hasEpicId) { errors.push('Epic ID or epic title is required for decomposition'); suggestions.push('Try: "Decompose epic E001" or "Break down the authentication epic"'); } else { if (params.epicTitle && !params.epicId) { warnings.push('Epic title provided, will attempt to resolve to epic ID'); } } if (params.decompositionScope) { const validScopes = ['development tasks', 'implementation steps', 'technical tasks', 'all aspects']; const scope = String(params.decompositionScope).toLowerCase(); if (!validScopes.some(validScope => scope.includes(validScope))) { warnings.push('Decomposition scope may be too broad or unclear'); suggestions.push('Consider specifying: development tasks, implementation steps, or technical tasks'); } } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateDecomposeProject(intent, context, params, errors, warnings, suggestions) { if (!params.projectName) { errors.push('Project name or ID is required for decomposition'); suggestions.push('Try: "Decompose project MyApp" or "Break down project PID-001"'); } else { params.projectName = String(params.projectName).trim(); } if (params.decompositionScope) { const validScopes = ['development tasks', 'implementation phases', 'technical components', 'all aspects']; const scope = String(params.decompositionScope).toLowerCase(); if (!validScopes.some(validScope => scope.includes(validScope))) { warnings.push('Decomposition scope may be too broad or unclear'); suggestions.push('Consider specifying: development tasks, implementation phases, or technical components'); } } if (params.decompositionDetails) { const details = String(params.decompositionDetails); if (details.length > 1000) { warnings.push('Decomposition details are very long, may affect processing performance'); } } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateSearchFiles(intent, context, params, errors, warnings, suggestions) { if (!params.searchPattern && !params.fileName) { errors.push('Search pattern or file name is required'); suggestions.push('Try: "Search for *.js files" or "Find config files"'); } else { if (params.searchPattern) { params.searchPattern = String(params.searchPattern).trim(); if (String(params.searchPattern).length < 2) { warnings.push('Search pattern is very short, may return too many results'); } } if (params.fileName) { params.fileName = String(params.fileName).trim(); } } if (params.fileExtensions) { const extensions = Array.isArray(params.fileExtensions) ? params.fileExtensions : [params.fileExtensions]; params.fileExtensions = extensions.map(ext => String(ext).startsWith('.') ? String(ext) : `.${ext}`); } if (!params.searchDirectory) { params.searchDirectory = context.currentProject || '.'; } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateSearchContent(intent, context, params, errors, warnings, suggestions) { if (!params.searchQuery) { errors.push('Search query is required'); suggestions.push('Try: "Search content for authentication" or "Find code containing API"'); } else { params.searchQuery = String(params.searchQuery).trim(); if (String(params.searchQuery).length < 3) { warnings.push('Search query is very short, may return too many results'); suggestions.push('Consider using a more specific search term'); } } if (params.useRegex && params.regexPattern) { try { new RegExp(String(params.regexPattern)); params.regexPattern = String(params.regexPattern); } catch { errors.push('Invalid regular expression pattern'); suggestions.push('Check your regex syntax or use simple text search'); } } if (params.caseSensitive !== undefined) { params.caseSensitive = Boolean(params.caseSensitive); } else { params.caseSensitive = false; } if (!params.searchDirectory) { params.searchDirectory = context.currentProject || '.'; } if (params.fileExtensions) { const extensions = Array.isArray(params.fileExtensions) ? params.fileExtensions : [params.fileExtensions]; params.fileExtensions = extensions.map(ext => String(ext).startsWith('.') ? String(ext) : `.${ext}`); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateArtifactOperation(intent, context, params, errors, warnings, suggestions) { if (!params.projectName) { errors.push('Project name is required for artifact operations'); suggestions.push('Try: "Parse PRD for project MyApp" or "Import tasks for PID-001"'); } else { params.projectName = String(params.projectName).trim(); } if (!params.filePath && !params.artifactContent && !params.artifactData) { if (intent.intent === 'parse_prd') { errors.push('PRD file path or content is required'); suggestions.push('Try: "Parse PRD from requirements.md" or provide PRD content'); } else if (intent.intent === 'parse_tasks') { errors.push('Task list file path or content is required'); suggestions.push('Try: "Parse tasks from todo.md" or provide task list content'); } else if (intent.intent === 'import_artifact') { errors.push('Artifact file path or content is required'); suggestions.push('Try: "Import artifact from data.json" or provide artifact content'); } } else { if (params.filePath) { params.filePath = String(params.filePath).trim(); if (!String(params.filePath).includes('.')) { warnings.push('File path may be missing extension'); } const filePath = String(params.filePath).toLowerCase(); if (intent.intent === 'parse_prd') { if (!filePath.endsWith('.md') && !filePath.endsWith('.txt') && !filePath.endsWith('.doc')) { warnings.push('PRD files are typically .md, .txt, or .doc files'); } } else if (intent.intent === 'parse_tasks') { if (!filePath.endsWith('.md') && !filePath.endsWith('.txt') && !filePath.endsWith('.json')) { warnings.push('Task list files are typically .md, .txt, or .json files'); } } } if (params.artifactContent) { params.artifactContent = String(params.artifactContent).trim(); if (String(params.artifactContent).length < 10) { warnings.push('Artifact content seems very short'); } } } if (params.artifactType) { const validTypes = ['prd', 'requirements', 'tasks', 'todo', 'specifications', 'documentation']; const artifactType = String(params.artifactType).toLowerCase(); if (!validTypes.includes(artifactType)) { warnings.push(`Artifact type '${artifactType}' may not be recognized`); suggestions.push(`Consider using: ${validTypes.join(', ')}`); } params.artifactType = artifactType; } else { if (intent.intent === 'parse_prd') { params.artifactType = 'prd'; } else if (intent.intent === 'parse_tasks') { params.artifactType = 'tasks'; } } if (params.parseOptions) { const options = params.parseOptions; if (options.extractTasks !== undefined) { options.extractTasks = Boolean(options.extractTasks); } if (options.extractRequirements !== undefined) { options.extractRequirements = Boolean(options.extractRequirements); } if (options.preserveStructure !== undefined) { options.preserveStructure = Boolean(options.preserveStructure); } params.parseOptions = options; } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateUpdateProject(intent, context, params, errors, warnings, suggestions) { if (!params.projectName) { errors.push('Project name is required for update operation'); suggestions.push('Try: "Update project MyApp status to completed"'); } else { params.projectName = String(params.projectName).trim(); } const updateableFields = ['status', 'priority', 'description', 'assignee', 'deadline']; const hasUpdates = updateableFields.some(field => params[field] !== undefined); if (!hasUpdates) { warnings.push('No specific updates provided, will prompt for changes'); suggestions.push('Try specifying what to update: status, priority, description, assignee, or deadline'); } if (params.status) { const validStatuses = ['pending', 'in_progress', 'completed', 'blocked', 'cancelled']; const status = String(params.status).toLowerCase().replace(/\s+/g, '_'); if (!validStatuses.includes(status)) { errors.push(`Invalid status: ${params.status}`); suggestions.push(`Valid statuses: ${validStatuses.join(', ')}`); } else { params.status = status; } } if (params.priority) { const validPriorities = ['low', 'medium', 'high', 'critical']; const priority = String(params.priority).toLowerCase(); if (!validPriorities.includes(priority)) { warnings.push(`Priority '${params.priority}' may not be recognized`); suggestions.push(`Valid priorities: ${validPriorities.join(', ')}`); } else { params.priority = priority; } } if (params.assignee) { params.assignee = String(params.assignee).trim(); if (String(params.assignee).length < 2) { warnings.push('Assignee name seems very short'); } } if (params.description) { params.description = String(params.description).trim(); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } validateAssignTask(intent, context, params, errors, warnings, suggestions) { if (!params.taskId && !params.taskTitle) { errors.push('Task ID or title is required for assignment'); suggestions.push('Try: "Assign task T-001 to John" or "Assign authentication task to Sarah"'); } else { if (params.taskId) { params.taskId = String(params.taskId).trim(); } if (params.taskTitle) { params.taskTitle = String(params.taskTitle).trim(); warnings.push('Task title provided, will attempt to resolve to task ID'); } } if (!params.assignee) { errors.push('Assignee is required for task assignment'); suggestions.push('Try: "Assign task to John" or "Assign to team-frontend"'); } else { params.assignee = String(params.assignee).trim(); if (String(params.assignee).length < 2) { errors.push('Assignee name is too short'); } const assignee = String(params.assignee).toLowerCase(); if (assignee.includes('team-') || assignee.includes('group-')) { warnings.push('Assigning to a team/group - ensure the team exists'); } } if (params.force !== undefined) { params.force = Boolean(params.force); } else { params.force = false; } if (context.currentTask && params.taskId === context.currentTask) { warnings.push('Task may already be assigned, use force option if needed'); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } shouldRequireConfirmation(intent, validation) { if (intent.confidence < this.config.autoExecuteThreshold) { return true; } if (validation.warnings.length > 0) { return true; } const destructiveIntents = ['delete_project', 'delete_task', 'archive_project']; if (destructiveIntents.includes(intent.intent)) { return true; } return false; } async mapIntentToToolParams(intent, normalizedParams) { const toolParams = {}; switch (intent.intent) { case 'create_project': toolParams.command = 'create'; toolParams.projectName = normalizedParams.projectName; toolParams.description = normalizedParams.description; toolParams.options = { priority: normalizedParams.priority || 'medium', type: normalizedParams.type || 'development' }; break; case 'create_task': toolParams.command = 'create'; toolParams.projectName = normalizedParams.projectName; toolParams.description = normalizedParams.taskTitle; toolParams.options = { priority: normalizedParams.priority || 'medium', type: normalizedParams.type || 'development', assignee: normalizedParams.assignee }; break; case 'list_projects': toolParams.command = 'list'; toolParams.options = { type: 'projects', status: normalizedParams.status, timeframe: normalizedParams.timeframe }; break; case 'list_tasks': toolParams.command = 'list'; toolParams.options = { type: 'tasks', status: normalizedParams.status, timeframe: normalizedParams.timeframe, assignee: normalizedParams.assignee, project: normalizedParams.projectName }; break; case 'run_task': toolParams.command = 'run'; toolParams.taskId = normalizedParams.taskId || normalizedParams.taskTitle; toolParams.options = { force: normalizedParams.force || false }; break; case 'check_status': toolParams.command = 'status'; toolParams.projectName = normalizedParams.projectName; toolParams.taskId = normalizedParams.taskId; toolParams.options = { detailed: true }; break; case 'decompose_task': toolParams.command = 'decompose'; toolParams.taskId = normalizedParams.taskId || normalizedParams.taskTitle; toolParams.description = normalizedParams.description; toolParams.options = { scope: normalizedParams.decompositionScope, details: normalizedParams.decompositionDetails, force: normalizedParams.force || false }; break; case 'decompose_epic': toolParams.command = 'decompose'; toolParams.epicId = normalizedParams.epicId || normalizedParams.epicTitle; toolParams.description = normalizedParams.description; toolParams.options = { scope: normalizedParams.decompositionScope, details: normalizedParams.decompositionDetails, force: normalizedParams.force || false }; break; case 'decompose_project': toolParams.command = 'decompose'; toolParams.projectName = normalizedParams.projectName; toolParams.description = normalizedParams.description; toolParams.options = { scope: normalizedParams.decompositionScope, details: normalizedParams.decompositionDetails, force: normalizedParams.force || false }; break; case 'open_project': toolParams.command = 'open'; toolParams.projectName = normalizedParams.projectName; toolParams.options = {}; break; case 'update_project': toolParams.command = 'update'; toolParams.projectName = normalizedParams.projectName; toolParams.updates = normalizedParams.updates || {}; toolParams.options = {}; break; case 'search_files': toolParams.command = 'search'; toolParams.searchType = 'files'; toolParams.searchPattern = normalizedParams.searchPattern || normalizedParams.fileName; toolParams.options = { directory: normalizedParams.searchDirectory, extensions: normalizedParams.fileExtensions, recursive: true }; break; case 'search_content': toolParams.command = 'search'; toolParams.searchType = 'content'; toolParams.searchQuery = normalizedParams.searchQuery; toolParams.options = { directory: normalizedParams.searchDirectory, extensions: normalizedParams.fileExtensions, useRegex: normalizedParams.useRegex || false, caseSensitive: normalizedParams.caseSensitive || false, regexPattern: normalizedParams.regexPattern }; break; case 'parse_prd': toolParams.command = 'parse'; toolParams.artifactType = 'prd'; toolParams.projectName = normalizedParams.projectName; toolParams.filePath = normalizedParams.filePath; toolParams.artifactContent = normalizedParams.artifactContent; toolParams.options = normalizedParams.parseOptions || {}; break; case 'parse_tasks': toolParams.command = 'parse'; toolParams.artifactType = 'tasks'; toolParams.projectName = normalizedParams.projectName; toolParams.filePath = normalizedParams.filePath; toolParams.artifactContent = normalizedParams.artifactContent; toolParams.options = normalizedParams.parseOptions || {}; break; case 'import_artifact': toolParams.command = 'import'; toolParams.artifactType = normalizedParams.artifactType || 'generic'; toolParams.projectName = normalizedParams.projectName; toolParams.filePath = normalizedParams.filePath; toolParams.artifactContent = normalizedParams.artifactContent; toolParams.options = normalizedParams.parseOptions || {}; break; case 'assign_task': toolParams.command = 'assign'; toolParams.taskId = normalizedParams.taskId || normalizedParams.taskTitle; toolParams.assignee = normalizedParams.assignee; toolParams.options = { force: normalizedParams.force || false }; break; case 'refine_task': toolParams.command = 'refine'; toolParams.taskId = normalizedParams.taskId || normalizedParams.taskTitle; toolParams.refinements = normalizedParams.description || normalizedParams.refinements; toolParams.options = { scope: normalizedParams.refinementScope }; break; case 'get_help': toolParams.command = 'help'; toolParams.topic = normalizedParams.helpTopic || normalizedParams.topic; toolParams.options = { detailed: true }; break; case 'unrecognized_intent': case 'clarification_needed': case 'unknown': toolParams.command = 'fallback'; toolParams.originalInput = normalizedParams.originalInput; toolParams.suggestions = normalizedParams.suggestions || []; toolParams.options = { intent: intent.intent, confidence: intent.confidence }; break; default: throw new Error(`Unsupported intent for tool mapping: ${intent.intent}`); } return toolParams; } createFailureResult(input, message, suggestions, startTime) { return { success: false, intent: { intent: 'unknown', confidence: 0, confidenceLevel: 'very_low', entities: [], originalInput: input, processedInput: input.toLowerCase().trim(), alternatives: [], metadata: { processingTime: Date.now() - startTime, method: 'pattern', timestamp: new Date() } }, toolParams: {}, validationErrors: [message], suggestions, metadata: { processingTime: Date.now() - startTime, confidence: 0, requiresConfirmation: false, ambiguousInput: true } }; } createValidationErrorResult(intent, validation, startTime) { return { success: false, intent, toolParams: {}, validationErrors: validation.errors, suggestions: validation.suggestions, metadata: { processingTime: Date.now() - startTime, confidence: intent.confidence, requiresConfirmation: false, ambiguousInput: intent.confidence < 0.7 } }; } mapEntityTypeToParamName(entityType) { const mapping = { 'project_name': 'projectName', 'task_name': 'taskName', 'task_title': 'taskTitle', 'task_id': 'taskId', 'description': 'description', 'priority': 'priority', 'type': 'type', 'status': 'status', 'assignee': 'assignee', 'timeframe': 'timeframe', 'features': 'features', 'decomposition_scope': 'decompositionScope', 'decomposition_details': 'decompositionDetails' }; return mapping[entityType] || entityType; } convertEntitiesToArray(entities) { if (Array.isArray(entities)) { return entities.map(entity => ({ type: entity.type || 'unknown', value: String(entity.value || ''), confidence: entity.confidence || 1.0 })); } const entityArray = []; for (const [type, value] of Object.entries(entities)) { if (value !== undefined && value !== null) { entityArray.push({ type, value: String(value), confidence: 1.0 }); } } return entityArray; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; logger.info({ config: this.config }, 'Command Gateway configuration updated'); } getConfig() { return { ...this.config }; } clearHistory(sessionId) { this.commandHistory.delete(sessionId); this.contextCache.delete(sessionId); logger.info({ sessionId }, 'Command history cleared'); } getHistory(sessionId) { return this.commandHistory.get(sessionId) || []; } getStatistics() { const totalSessions = this.commandHistory.size; let totalCommands = 0; let successfulCommands = 0; for (const history of this.commandHistory.values()) { totalCommands += history.length; successfulCommands += history.filter(h => h.confidence >= 0.7).length; } return { totalSessions, totalCommands, averageProcessingTime: this.calculateAverageProcessingTime(), successRate: totalCommands > 0 ? successfulCommands / totalCommands : 0 }; } calculateAverageProcessingTime() { const allSessions = Array.from(this.sessionMetrics.values()); if (allSessions.length === 0) { return 0; } const totalProcessingTime = allSessions.reduce((sum, session) => { return sum + session.commands.reduce((cmdSum, cmd) => cmdSum + cmd.processingTime, 0); }, 0); const totalCommands = allSessions.reduce((sum, session) => sum + session.commands.length, 0); return totalCommands > 0 ? totalProcessingTime / totalCommands : 0; } trackIntentSuccess(intent, success, input, error) { const metrics = this.intentSuccessMetrics.get(intent) || { total: 0, successful: 0, failed: 0, lastUpdated: new Date(), recentFailures: [] }; metrics.total++; metrics.lastUpdated = new Date(); if (success) { metrics.successful++; } else { metrics.failed++; metrics.recentFailures.push({ input: input.substring(0, 100), error: error || 'Unknown error', timestamp: new Date() }); if (metrics.recentFailures.length > 10) { metrics.recentFailures = metrics.recentFailures.slice(-10); } } this.intentSuccessMetrics.set(intent, metrics); const failureRate = metrics.failed / metrics.total; if (metrics.total >= 5 && failureRate > 0.5) { logger.warn({ intent, total: metrics.total, successful: metrics.successful, failed: metrics.failed, failureRate: Math.round(failureRate * 100), recentFailures: metrics.recentFailures.slice(-3) }, 'High intent recognition failure rate detected'); } } getIntentSuccessRates() { const stats = {}; for (const [intent, metrics] of this.intentSuccessMetrics.entries()) { const successRate = metrics.total > 0 ? metrics.successful / metrics.total : 0; const failureRate = metrics.total > 0 ? metrics.failed / metrics.total : 0; stats[intent] = { intent, total: metrics.total, successRate: Math.round(successRate * 100) / 100, failureRate: Math.round(failureRate * 100) / 100, lastUpdated: metrics.lastUpdated }; if (failureRate > 0.3 && metrics.recentFailures.length > 0) { stats[intent].recentFailures = metrics.recentFailures.slice(-5); } } return stats; } getSystemHealthMetrics() { let totalCommands = 0; let totalSuccessful = 0; const problematicIntents = []; let lastUpdated = new Date(0); for (const [intent, metrics] of this.intentSuccessMetrics.entries()) { totalCommands += metrics.total; totalSuccessful += metrics.successful; if (metrics.lastUpdated > lastUpdated) { lastUpdated = metrics.lastUpdated; } const failureRate = metrics.total > 0 ? metrics.failed / metrics.total : 0; if (metrics.total >= 3 && failureRate > 0.3) { problematicIntents.push(intent); } } const overallSuccessRate = totalCommands > 0 ? totalSuccessful / totalCommands : 0; const intentCoverage = this.intentSuccessMetrics.size; return { totalCommands, overallSuccessRate: Math.round(overallSuccessRate * 100) / 100, intentCoverage, problematicIntents, lastUpdated }; } }