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,058 lines (1,057 loc) • 48.7 kB
JavaScript
import logger from '../../../logger.js';
export class EntityExtractors {
static projectName(text, _match) {
const entities = {};
const projectPatterns = [
/\b(PID-[A-Z0-9-]+)\b/i,
/called\s+["']([^"']+)["']/i,
/project\s+["']([^"']+)["']/i,
/["']([^"']+)["']\s+project/i,
/for\s+["']([^"']+)["']/i,
/called\s+([A-Za-z0-9\-_]+(?:\s+[A-Za-z0-9\-_]+){0,4})(?=\s+[-–—]|\s+(?:project|task|file|document|prd|tasks?|list|into|with|for|using|through|via|detailed|comprehensive|development)|\s*$)/i,
/project\s+(?!for\s)([A-Za-z0-9\-_]+(?:\s+[A-Za-z0-9\-_]+){0,4})(?=\s+[-–—]|\s+(?:project|task|file|document|prd|tasks?|list|into|with|for|using|through|via|detailed|comprehensive|development)|\s*$)/i,
/for\s+(?:the\s+)?([A-Za-z0-9\-_]+(?:\s+[A-Za-z0-9\-_]+){0,4})(?=\s+[-–—]|\s+(?:project|task|file|document|prd|tasks?|list|into|with|for|using|through|via|detailed|comprehensive|development)|\s*$)/i,
/called\s+([A-Za-z0-9\-_]+)(?:\s|$)/i,
/project\s+(?!for\s)([A-Za-z0-9\-_]+)(?:\s|$)/i,
/for\s+(?:the\s+)?([A-Za-z0-9\-_]+)(?:\s|$)/i
];
const blacklistedPhrases = [
'should be set up',
'needs to be',
'must be',
'has to be',
'will be',
'would be',
'could be',
'can be',
'should be',
'is being',
'was being',
'has been',
'have been'
];
const allMatches = [];
projectPatterns.forEach((pattern, priority) => {
const projectMatch = text.match(pattern);
if (projectMatch) {
let projectName = projectMatch[1].trim();
projectName = projectName.replace(/\s+/g, ' ');
const hasDashSeparator = text.substring(projectMatch.index + projectMatch[0].length).match(/^\s*[-–—]/);
const projectIdWithDescMatch = projectName.match(/^([A-Z0-9]+(?:-[A-Z0-9]+)*)\s*[-–—]\s+/i);
if (projectIdWithDescMatch && projectIdWithDescMatch[1].includes('-')) {
projectName = projectIdWithDescMatch[1];
}
else {
projectName = projectName.replace(/\s*[-–—]\s*$/, '');
projectName = projectName.replace(/\s+(project|task|file|document|prd|tasks?|list|into|with|for|using|through|via|detailed|comprehensive|development)$/i, '');
}
const lowerName = projectName.toLowerCase();
const isBlacklisted = blacklistedPhrases.some(phrase => lowerName === phrase || lowerName.includes(phrase));
if (projectName.length > 0 && !isBlacklisted) {
allMatches.push({
name: projectName,
priority: priority,
position: projectMatch.index,
hasDashSeparator: !!hasDashSeparator,
pattern: pattern
});
}
}
});
if (allMatches.length > 0) {
allMatches.sort((a, b) => {
if (a.priority !== b.priority) {
return a.priority - b.priority;
}
if (a.hasDashSeparator !== b.hasDashSeparator) {
return b.hasDashSeparator ? 1 : -1;
}
return a.position - b.position;
});
const bestMatch = allMatches[0];
if (allMatches.length > 1) {
logger.debug({
selectedMatch: bestMatch.name,
allMatches: allMatches.map(m => ({
name: m.name,
priority: m.priority,
position: m.position,
hasDashSeparator: m.hasDashSeparator
})),
originalText: text
}, 'Multiple project name matches found, selected best match');
}
let projectName = bestMatch.name;
if (projectName.length > 50) {
const truncated = projectName.substring(0, 50);
const lastSpace = truncated.lastIndexOf(' ');
if (lastSpace > 30) {
projectName = truncated.substring(0, lastSpace).trim();
}
else {
projectName = truncated.trim();
}
logger.warn({
originalLength: bestMatch.name.length,
truncatedLength: projectName.length,
originalName: bestMatch.name,
truncatedName: projectName
}, 'Project name truncated to meet 50 character limit');
}
entities.projectName = projectName;
}
return entities;
}
static taskInfo(text, _match) {
const entities = {};
const titlePatterns = [
/task\s+["']([^"']+)["']/i,
/["']([^"']+)["']\s+task/i,
/for\s+["']([^"']+)["']/i,
/called\s+["']([^"']+)["']/i,
/to\s+["']([^"']+)["']/i,
/called\s+(\w+)/i,
/for\s+(\w+)/i,
/task\s+(\w+)/i,
/(\w+)\s+task/i
];
for (const pattern of titlePatterns) {
const titleMatch = text.match(pattern);
if (titleMatch) {
entities.taskTitle = titleMatch[1].trim();
break;
}
}
const priorityMatch = text.match(/\b(low|medium|high|critical)\s+priority\b/i) ||
text.match(/priority\s+(low|medium|high|critical)\b/i);
if (priorityMatch) {
entities.priority = priorityMatch[1].toLowerCase();
}
const typeMatch = text.match(/\b(development|testing|documentation|research|bug|feature)\b/i);
if (typeMatch) {
entities.type = typeMatch[1].toLowerCase();
}
return entities;
}
static epicInfo(text, _match) {
const entities = {};
const titlePatterns = [
/epic\s+["']([^"']+)["']/i,
/["']([^"']+)["']\s+epic/i,
/for\s+["']([^"']+)["']/i,
/called\s+["']([^"']+)["']/i,
/to\s+["']([^"']+)["']/i,
/called\s+(\w+)/i,
/for\s+(\w+)/i,
/epic\s+(\w+)/i,
/(\w+)\s+epic/i
];
for (const pattern of titlePatterns) {
const titleMatch = text.match(pattern);
if (titleMatch) {
entities.epicTitle = titleMatch[1].trim();
break;
}
}
const epicIdPatterns = [
/epic\s+([E]\d+)/i,
/([E]\d+)/i,
/epic\s+id\s+(\w+)/i,
/id\s+(\w+)/i
];
for (const pattern of epicIdPatterns) {
const idMatch = text.match(pattern);
if (idMatch) {
entities.epicId = idMatch[1].trim();
break;
}
}
const priorityMatch = text.match(/\b(low|medium|high|critical)\s+priority\b/i) ||
text.match(/priority\s+(low|medium|high|critical)\b/i);
if (priorityMatch) {
entities.priority = priorityMatch[1].toLowerCase();
}
return entities;
}
static statusInfo(text, _match) {
const entities = {};
const statusMatch = text.match(/\b(pending|in_progress|completed|blocked|cancelled)\b/i);
if (statusMatch) {
entities.status = statusMatch[1].toLowerCase();
}
const timePatterns = [
/\b(today|tomorrow|this\s+week|next\s+week|this\s+month)\b/i,
/\b(\d{1,2}\/\d{1,2}\/\d{4})\b/,
/\b(\d{4}-\d{2}-\d{2})\b/
];
for (const pattern of timePatterns) {
const timeMatch = text.match(pattern);
if (timeMatch) {
entities.timeframe = timeMatch[1];
break;
}
}
return entities;
}
static agentInfo(text, _match) {
const entities = {};
const agentPatterns = [
/agent\s+["']([^"']+)["']/i,
/to\s+["']([^"']+)["']/i,
/assign\s+to\s+(\w+)/i,
/give\s+(\w+)\s+the/i
];
for (const pattern of agentPatterns) {
const agentMatch = text.match(pattern);
if (agentMatch) {
entities.assignee = agentMatch[1].trim();
break;
}
}
return entities;
}
static descriptionInfo(text, _match) {
const entities = {};
const descriptionPatterns = [
/with\s+focus\s+on\s+(.+)/i,
/considering\s+(.+)/i,
/taking\s+into\s+account\s+(.+)/i,
/for\s+(.+)/i,
/description\s+["']([^"']+)["']/i,
/context\s+["']([^"']+)["']/i
];
for (const pattern of descriptionPatterns) {
const descMatch = text.match(pattern);
if (descMatch) {
entities.description = descMatch[1].trim();
break;
}
}
return entities;
}
static searchInfo(text, _match) {
const entities = {};
const searchPatterns = [
/(?:find|search\s+for|locate)\s+(.+?)\s+files?/i,
/(?:find|search\s+for|locate)\s+(.+)/i,
/files?\s+(?:named|called|matching)\s+(.+)/i,
/(.+?)\s+files?/i,
/"([^"]+)"/,
/'([^']+)'/
];
for (const pattern of searchPatterns) {
const searchMatch = text.match(pattern);
if (searchMatch) {
entities.searchPattern = searchMatch[1].trim();
break;
}
}
const extMatch = text.match(/\.(\w+)\s+files?|(\w+)\s+files?/);
if (extMatch) {
const ext = extMatch[1] || extMatch[2];
if (['js', 'ts', 'tsx', 'jsx', 'py', 'java', 'cpp', 'css', 'html'].includes(ext)) {
entities.extensions = [`.${ext}`];
}
}
return entities;
}
static contentInfo(text, _match) {
const entities = {};
const contentPatterns = [
/(?:find|search\s+for|locate)\s+(.+?)\s+(?:in\s+files?|in\s+code)/i,
/(?:find|search\s+for|locate)\s+"(.+?)"/i,
/(?:find|search\s+for|locate)\s+'(.+?)'/i,
/(?:find|search\s+for|locate)\s+(.+)/i,
/content\s+(?:containing|with)\s+(.+)/i
];
for (const pattern of contentPatterns) {
const contentMatch = text.match(pattern);
if (contentMatch) {
entities.searchQuery = contentMatch[1].trim();
entities.content = contentMatch[1].trim();
break;
}
}
if (text.toLowerCase().includes('case sensitive') || text.toLowerCase().includes('exact case')) {
entities.caseSensitive = true;
}
if (text.toLowerCase().includes('regex') || text.toLowerCase().includes('regular expression')) {
entities.useRegex = true;
}
return entities;
}
static artifactInfo(text, _match) {
const entities = {};
const artifactTypePatterns = [
/\b(prd|product\s+requirements?\s+document)\b/i,
/\b(task\s+list|tasks?)\b/i,
/\b(task\s+breakdown)\b/i,
/\b(artifact|document|file)\b/i
];
for (const pattern of artifactTypePatterns) {
const typeMatch = text.match(pattern);
if (typeMatch) {
let artifactType = typeMatch[1].toLowerCase();
if (artifactType.includes('prd') || artifactType.includes('product') || artifactType.includes('requirements')) {
artifactType = 'prd';
}
else if (artifactType.includes('task')) {
artifactType = 'tasks';
}
else if (artifactType.includes('artifact') || artifactType.includes('document') || artifactType.includes('file')) {
artifactType = 'artifact';
}
entities.artifactType = artifactType;
break;
}
}
const filePathPatterns = [
/from\s+["']([^"']+)["']/i,
/from\s+(\S+\.(?:md|txt|json|yaml|yml))/i,
/from\s+(\S+)/i,
/["']([^"']*\.(?:md|txt|json|yaml|yml))["']/i,
/(\S+\.(?:md|txt|json|yaml|yml))/i
];
for (const pattern of filePathPatterns) {
const pathMatch = text.match(pattern);
if (pathMatch) {
entities.filePath = pathMatch[1].trim();
break;
}
}
return entities;
}
static general(text, _match) {
const entities = {};
const tagMatches = text.match(/#(\w+)/g);
if (tagMatches) {
entities.tags = tagMatches.map(tag => tag.substring(1));
}
const numberMatches = text.match(/\b\d+\b/g);
if (numberMatches) {
entities.numbers = numberMatches.map(num => parseInt(num, 10));
}
return entities;
}
}
export class IntentPatternEngine {
patterns = new Map();
config;
constructor(config = {}) {
this.config = {
minConfidence: config.minConfidence ?? 0.3,
maxPatternsPerIntent: config.maxPatternsPerIntent ?? 10,
enableFuzzyMatching: config.enableFuzzyMatching ?? false,
fuzzyThreshold: config.fuzzyThreshold ?? 0.7
};
this.initializeDefaultPatterns();
}
initializeDefaultPatterns() {
this.addPattern('create_project', {
id: 'create_project_basic',
intent: 'create_project',
patterns: [
'create\\s+(?:a\\s+)?(?:new\\s+)?project',
'start\\s+(?:a\\s+)?(?:new\\s+)?project',
'set\\s+up\\s+(?:a\\s+)?(?:new\\s+)?project',
'initialize\\s+(?:a\\s+)?(?:new\\s+)?project',
'create\\s+(?:something\\s+)?(?:new\\s+)?(?:for\\s+the\\s+)?project',
'make\\s+(?:a\\s+)?(?:new\\s+)?project',
'build\\s+(?:a\\s+)?(?:new\\s+)?project',
'develop\\s+(?:a\\s+)?(?:new\\s+)?project',
'generate\\s+(?:a\\s+)?(?:new\\s+)?project',
'setup\\s+(?:a\\s+)?(?:new\\s+)?project',
'begin\\s+(?:a\\s+)?(?:new\\s+)?project',
'launch\\s+(?:a\\s+)?(?:new\\s+)?project',
'establish\\s+(?:a\\s+)?(?:new\\s+)?project',
'initiate\\s+(?:a\\s+)?(?:new\\s+)?project',
'(?:let\'s\\s+)?(?:create|start|build|make)\\s+(?:a\\s+)?(?:new\\s+)?project',
'i\\s+(?:want\\s+to\\s+|need\\s+to\\s+)?(?:create|start|build|make)\\s+(?:a\\s+)?(?:new\\s+)?project',
'can\\s+(?:you\\s+)?(?:create|start|build|make)\\s+(?:a\\s+)?(?:new\\s+)?project'
],
keywords: ['create', 'start', 'setup', 'initialize', 'project', 'new', 'make', 'build', 'develop', 'generate', 'launch'],
requiredEntities: [],
optionalEntities: ['projectName', 'description'],
priority: 10,
active: true,
examples: [
'Create a new project called "Web App"',
'Start a project for the mobile app',
'Set up a new project',
'Build a new project for streaming platform',
'Let\'s create a new project',
'I want to create a project',
'Can you make a new project?'
]
});
this.addPattern('open_project', {
id: 'open_project_basic',
intent: 'open_project',
patterns: [
'open\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'switch\\s+to\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'work\\s+on\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'continue\\s+(?:with\\s+)?(?:the\\s+)?(?:\\w+\\s+)?project',
'load\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'access\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'(?:let\'s\\s+)?(?:open|switch|work|continue)\\s+(?:with\\s+|on\\s+)?(?:the\\s+)?(?:\\w+\\s+)?project',
'i\\s+(?:want\\s+to\\s+|need\\s+to\\s+)?(?:open|work\\s+on|switch\\s+to)\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'can\\s+(?:you\\s+)?(?:open|load|switch\\s+to)\\s+(?:the\\s+)?(?:\\w+\\s+)?project'
],
keywords: ['open', 'switch', 'work', 'continue', 'load', 'access', 'project'],
requiredEntities: ['projectName'],
optionalEntities: [],
priority: 10,
active: true,
examples: [
'Open the web app project',
'Switch to mobile project',
'Work on the API project',
'Continue with project "E-commerce Platform"',
'Load the streaming project',
'I want to work on the mobile project',
'Can you open the backend project?'
]
});
this.addPattern('create_task', {
id: 'create_task_basic',
intent: 'create_task',
patterns: [
'create\\s+(?:a\\s+)?(?:new\\s+)?(?:high\\s+priority\\s+)?(?:development\\s+)?task',
'add\\s+(?:a\\s+)?(?:new\\s+)?task',
'make\\s+(?:a\\s+)?(?:new\\s+)?task',
'need\\s+(?:a\\s+)?(?:new\\s+)?task',
'create\\s+(?:a\\s+)?(?:high|medium|low|critical)\\s+priority\\s+(?:development|testing|documentation|research|bug|feature)\\s+task',
'create\\s+(?:a\\s+)?(?:development|testing|documentation|research|bug|feature)\\s+task'
],
keywords: ['create', 'add', 'make', 'need', 'task', 'new', 'priority', 'development', 'high'],
requiredEntities: [],
optionalEntities: ['taskTitle', 'priority', 'type', 'assignee'],
priority: 10,
active: true,
examples: [
'Create a task for implementing authentication',
'Add a new task to fix the login bug',
'Make a task for testing the API',
'Create a high priority development task for implementing authentication'
]
});
this.addPattern('list_projects', {
id: 'list_projects_basic',
intent: 'list_projects',
patterns: [
'list\\s+(?:all\\s+)?projects',
'show\\s+(?:me\\s+)?(?:all\\s+)?projects',
'display\\s+(?:all\\s+)?projects',
'what\\s+projects',
'show\\s+(?:me\\s+)?(?:all\\s+)?(?:completed|pending|in_progress|blocked|cancelled)\\s+projects',
'list\\s+(?:all\\s+)?(?:completed|pending|in_progress)\\s+projects',
'show\\s+(?:me\\s+)?(?:all\\s+)?(?:completed|pending|in_progress|blocked|cancelled)\\s+projects\\s+(?:from\\s+)?(?:today|tomorrow|this\\s+week|next\\s+week|this\\s+month)'
],
keywords: ['list', 'show', 'display', 'projects', 'all', 'completed', 'pending', 'week'],
requiredEntities: [],
optionalEntities: ['status', 'timeframe'],
priority: 10,
active: true,
examples: [
'List all projects',
'Show me the projects',
'What projects do we have?',
'Show me all completed projects from this week'
]
});
this.addPattern('list_tasks', {
id: 'list_tasks_basic',
intent: 'list_tasks',
patterns: [
'list\\s+(?:all\\s+)?tasks',
'show\\s+(?:me\\s+)?(?:all\\s+)?tasks',
'display\\s+(?:all\\s+)?tasks',
'what\\s+tasks',
'show\\s+(?:completed|pending|in_progress|blocked|cancelled)\\s+tasks',
'list\\s+(?:all\\s+)?(?:pending|completed|in_progress)\\s+tasks',
'show\\s+(?:completed|pending|in_progress|blocked|cancelled)\\s+tasks\\s+(?:from\\s+)?(?:today|tomorrow|this\\s+week|next\\s+week|this\\s+month)',
'list\\s+(?:all\\s+)?(?:pending|completed|in_progress)\\s+tasks\\s+assigned\\s+to\\s+me'
],
keywords: ['list', 'show', 'display', 'tasks', 'all', 'completed', 'pending', 'today', 'assigned'],
requiredEntities: [],
optionalEntities: ['status', 'priority', 'assignee', 'timeframe'],
priority: 10,
active: true,
examples: [
'List all tasks',
'Show me pending tasks',
'What tasks are assigned to me?',
'Show completed tasks from today',
'List all pending tasks assigned to me'
]
});
this.addPattern('check_status', {
id: 'check_status_basic',
intent: 'check_status',
patterns: [
'status\\s+of',
'check\\s+(?:the\\s+)?status',
'what(?:\'s|\\s+is)\\s+the\\s+status',
'how\\s+is\\s+.+\\s+(?:going|progressing)',
'show\\s+(?:project\\s+)?status',
'show\\s+(?:me\\s+)?(?:the\\s+)?(?:project\\s+)?status',
'what(?:\'s|\\s+is)\\s+the\\s+status\\s+of\\s+the\\s+.+\\s+(?:application\\s+)?project'
],
keywords: ['status', 'check', 'progress', 'how', 'going', 'show', 'project'],
requiredEntities: [],
optionalEntities: ['projectName', 'taskId'],
priority: 10,
active: true,
examples: [
'What\'s the status of the web project?',
'Check the status of task 123',
'How is the development going?',
'Show project status',
'What\'s the status of the web application project?'
]
});
this.addPattern('run_task', {
id: 'run_task_basic',
intent: 'run_task',
patterns: [
'run\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'execute\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'start\\s+(?:working\\s+on\\s+)?(?:the\\s+)?(?:\\w+\\s+)?task',
'begin\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'run\\s+(?:task\\s+)?\\d+',
'execute\\s+(?:task\\s+)?\\d+',
'run\\s+(?:the\\s+)?\\w+\\s+task',
'execute\\s+(?:the\\s+)?\\w+\\s+task'
],
keywords: ['run', 'execute', 'start', 'begin', 'task', 'authentication'],
requiredEntities: [],
optionalEntities: ['taskId', 'taskTitle'],
priority: 10,
active: true,
examples: [
'Run task 123',
'Execute the authentication task',
'Start working on the login feature',
'Run the authentication task'
]
});
this.addPattern('decompose_task', {
id: 'decompose_task_basic',
intent: 'decompose_task',
patterns: [
'decompose\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'break\\s+down\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'split\\s+(?:up\\s+)?(?:the\\s+)?(?:\\w+\\s+)?task',
'divide\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'breakdown\\s+(?:the\\s+)?(?:\\w+\\s+)?task',
'decompose\\s+task\\s+\\w+',
'break\\s+down\\s+task\\s+\\w+',
'split\\s+task\\s+\\w+'
],
keywords: ['decompose', 'break down', 'split', 'divide', 'breakdown', 'task'],
requiredEntities: [],
optionalEntities: ['taskId', 'description'],
priority: 10,
active: true,
examples: [
'Decompose task T001',
'Break down the authentication task',
'Split up this task',
'Decompose the login feature task'
]
});
this.addPattern('decompose_epic', {
id: 'decompose_epic_basic',
intent: 'decompose_epic',
patterns: [
'decompose\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'break\\s+down\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'split\\s+(?:up\\s+)?(?:the\\s+)?(?:\\w+\\s+)?epic',
'divide\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'breakdown\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'decompose\\s+epic\\s+\\w+',
'break\\s+down\\s+epic\\s+\\w+',
'analyze\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'plan\\s+(?:out\\s+)?(?:the\\s+)?(?:\\w+\\s+)?epic',
'organize\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'structure\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'outline\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'(?:can\\s+you\\s+)?(?:decompose|break\\s+down|analyze)\\s+(?:this\\s+|the\\s+)?epic',
'i\\s+(?:want\\s+to\\s+|need\\s+to\\s+)?(?:decompose|break\\s+down|analyze)\\s+(?:this\\s+|the\\s+)?epic',
'(?:let\'s\\s+)?(?:decompose|break\\s+down|analyze)\\s+(?:this\\s+|the\\s+)?epic',
'create\\s+tasks\\s+for\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'generate\\s+tasks\\s+for\\s+(?:the\\s+)?(?:\\w+\\s+)?epic',
'break\\s+epic\\s+into\\s+tasks',
'convert\\s+epic\\s+to\\s+tasks'
],
keywords: ['decompose', 'break down', 'split', 'divide', 'breakdown', 'epic', 'analyze', 'plan', 'organize', 'tasks'],
requiredEntities: [],
optionalEntities: ['epicId', 'epicName', 'description'],
priority: 10,
active: true,
examples: [
'Decompose epic E001',
'Break down the authentication epic',
'Split up this epic',
'Create tasks for the user management epic',
'Decompose the payment processing epic into tasks'
]
});
this.addPattern('decompose_project', {
id: 'decompose_project_basic',
intent: 'decompose_project',
patterns: [
'decompose\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'break\\s+down\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'split\\s+(?:up\\s+)?(?:the\\s+)?(?:\\w+\\s+)?project',
'divide\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'breakdown\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'decompose\\s+project\\s+\\w+',
'break\\s+down\\s+project\\s+\\w+',
'analyze\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'plan\\s+(?:out\\s+)?(?:the\\s+)?(?:\\w+\\s+)?project',
'organize\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'structure\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'outline\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'(?:can\\s+you\\s+)?(?:decompose|break\\s+down|analyze)\\s+(?:this\\s+|the\\s+)?project',
'i\\s+(?:want\\s+to\\s+|need\\s+to\\s+)?(?:decompose|break\\s+down|analyze)\\s+(?:this\\s+|the\\s+)?project',
'(?:let\'s\\s+)?(?:decompose|break\\s+down|analyze)\\s+(?:this\\s+|the\\s+)?project',
'create\\s+tasks\\s+for\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'generate\\s+tasks\\s+for\\s+(?:the\\s+)?(?:\\w+\\s+)?project'
],
keywords: ['decompose', 'break down', 'split', 'divide', 'breakdown', 'project', 'analyze', 'plan', 'organize', 'tasks'],
requiredEntities: [],
optionalEntities: ['projectId', 'projectName', 'description'],
priority: 10,
active: true,
examples: [
'Decompose project PID-WEBAPP-001',
'Break down the web app project',
'Analyze this project',
'Can you decompose the project?',
'I need to break down this project',
'Create tasks for the streaming project'
]
});
this.addPattern('search_files', {
id: 'search_files_basic',
intent: 'search_files',
patterns: [
'find\\s+(?:all\\s+)?(?:\\w+\\s+)?files?',
'search\\s+(?:for\\s+)?(?:all\\s+)?(?:\\w+\\s+)?files?',
'locate\\s+(?:all\\s+)?(?:\\w+\\s+)?files?',
'show\\s+(?:me\\s+)?(?:all\\s+)?(?:\\w+\\s+)?files?',
'list\\s+(?:all\\s+)?(?:\\w+\\s+)?files?',
'find\\s+files?\\s+(?:named|called|matching)\\s+\\w+',
'search\\s+files?\\s+(?:named|called|matching)\\s+\\w+'
],
keywords: ['find', 'search', 'locate', 'show', 'list', 'files', 'file'],
requiredEntities: [],
optionalEntities: ['searchPattern', 'fileName', 'extensions'],
priority: 10,
active: true,
examples: [
'Find auth files',
'Search for component files',
'Locate all .ts files',
'Show me test files'
]
});
this.addPattern('search_content', {
id: 'search_content_basic',
intent: 'search_content',
patterns: [
'find\\s+(?:all\\s+)?(?:instances\\s+of\\s+)?\\w+\\s+(?:in\\s+)?(?:files?|code)',
'search\\s+(?:for\\s+)?(?:all\\s+)?(?:instances\\s+of\\s+)?\\w+\\s+(?:in\\s+)?(?:files?|code)',
'locate\\s+(?:all\\s+)?(?:instances\\s+of\\s+)?\\w+\\s+(?:in\\s+)?(?:files?|code)',
'find\\s+content\\s+(?:containing|with)\\s+\\w+',
'search\\s+content\\s+(?:containing|with)\\s+\\w+',
'find\\s+"[^"]+"\\s+(?:in\\s+)?(?:files?|code)',
'search\\s+"[^"]+"\\s+(?:in\\s+)?(?:files?|code)'
],
keywords: ['find', 'search', 'locate', 'content', 'code', 'in files', 'instances'],
requiredEntities: [],
optionalEntities: ['searchQuery', 'content', 'extensions'],
priority: 10,
active: true,
examples: [
'Find useState in files',
'Search for authentication code',
'Locate all instances of "login"',
'Find content containing API'
]
});
this.addPattern('parse_prd', {
id: 'parse_prd_basic',
intent: 'parse_prd',
patterns: [
'parse\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'load\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'read\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'process\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'analyze\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'import\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'open\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)',
'parse\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)\\s+for\\s+(?:the\\s+)?(?:project\\s+)?\\w+',
'load\\s+(?:the\\s+)?(?:prd|product\\s+requirements?\\s+document)\\s+for\\s+(?:the\\s+)?(?:project\\s+)?\\w+',
'parse\\s+(?:prd|product\\s+requirements?\\s+document)\\s+for\\s+["\'](.*?)["\']',
'parse\\s+prd',
'load\\s+prd',
'read\\s+prd',
'process\\s+prd',
'analyze\\s+prd',
'import\\s+prd',
'open\\s+prd'
],
keywords: ['parse', 'load', 'read', 'process', 'analyze', 'import', 'open', 'prd', 'product', 'requirements', 'document'],
requiredEntities: [],
optionalEntities: ['projectName', 'filePath'],
priority: 10,
active: true,
examples: [
'Parse the PRD',
'Load PRD for my project',
'Read the product requirements document',
'Process PRD file',
'Analyze the PRD',
'Parse PRD for "E-commerce Platform"',
'Load the product requirements document for the web app'
]
});
this.addPattern('update_project', {
id: 'update_project_basic',
intent: 'update_project',
patterns: [
'update\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'modify\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'change\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'edit\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'configure\\s+(?:the\\s+)?(?:\\w+\\s+)?project',
'update\\s+project\\s+\\w+',
'modify\\s+project\\s+\\w+',
'change\\s+project\\s+\\w+',
'update\\s+(?:the\\s+)?(?:\\w+\\s+)?project\\s+(?:settings|configuration|config|properties)',
'modify\\s+(?:the\\s+)?(?:\\w+\\s+)?project\\s+(?:settings|configuration|config|properties)',
'change\\s+(?:the\\s+)?(?:\\w+\\s+)?project\\s+(?:settings|configuration|config|properties)',
'configure\\s+(?:the\\s+)?(?:\\w+\\s+)?project\\s+(?:settings|configuration|config|properties)',
'update\\s+(?:the\\s+)?(?:\\w+\\s+)?project\\s+(?:details|info|information)',
'edit\\s+(?:the\\s+)?(?:\\w+\\s+)?project\\s+(?:details|info|information|settings|configuration)'
],
keywords: ['update', 'modify', 'change', 'edit', 'configure', 'project', 'settings', 'configuration', 'config', 'properties', 'details'],
requiredEntities: [],
optionalEntities: ['projectName', 'property', 'value'],
priority: 10,
active: true,
examples: [
'Update project configuration',
'Modify the project settings',
'Change project properties',
'Edit the project details',
'Configure the project',
'Update project MyApp',
'Modify project settings for WebApp'
]
});
this.addPattern('parse_tasks', {
id: 'parse_tasks_basic',
intent: 'parse_tasks',
patterns: [
'parse\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'load\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'read\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'process\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'analyze\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'import\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'open\\s+(?:the\\s+)?(?:task\\s+list|tasks?)',
'parse\\s+(?:the\\s+)?(?:task\\s+list|tasks?)\\s+for\\s+(?:the\\s+)?(?:project\\s+)?\\w+',
'load\\s+(?:the\\s+)?(?:task\\s+list|tasks?)\\s+for\\s+(?:the\\s+)?(?:project\\s+)?\\w+',
'parse\\s+(?:task\\s+list|tasks?)\\s+for\\s+["\'](.*?)["\']',
'parse\\s+(?:the\\s+)?(?:task\\s+breakdown|task\\s+file)',
'load\\s+(?:the\\s+)?(?:task\\s+breakdown|task\\s+file)',
'read\\s+(?:the\\s+)?(?:task\\s+breakdown|task\\s+file)',
'process\\s+(?:the\\s+)?(?:task\\s+breakdown|task\\s+file)',
'analyze\\s+(?:the\\s+)?(?:task\\s+breakdown|task\\s+file)'
],
keywords: ['parse', 'load', 'read', 'process', 'analyze', 'import', 'open', 'task', 'tasks', 'list', 'breakdown', 'file'],
requiredEntities: [],
optionalEntities: ['projectName', 'filePath'],
priority: 10,
active: true,
examples: [
'Parse the task list',
'Load task list for project',
'Read the tasks file',
'Process task list',
'Analyze the task breakdown',
'Parse tasks for "Mobile App"',
'Load the task list for the web application'
]
});
this.addPattern('import_artifact', {
id: 'import_artifact_basic',
intent: 'import_artifact',
patterns: [
'import\\s+(?:prd|product\\s+requirements?\\s+document)\\s+from\\s+\\S+',
'import\\s+(?:task\\s+list|tasks?)\\s+from\\s+\\S+',
'import\\s+(?:artifact|document|file)\\s+from\\s+\\S+',
'load\\s+(?:prd|product\\s+requirements?\\s+document)\\s+from\\s+\\S+',
'load\\s+(?:task\\s+list|tasks?)\\s+from\\s+\\S+',
'load\\s+(?:artifact|document|file)\\s+from\\s+\\S+',
'import\\s+(?:prd|product\\s+requirements?\\s+document)\\s+from\\s+["\'](.*?)["\']',
'import\\s+(?:task\\s+list|tasks?)\\s+from\\s+["\'](.*?)["\']',
'import\\s+(?:artifact|document|file)\\s+from\\s+["\'](.*?)["\']',
'load\\s+(?:prd|product\\s+requirements?\\s+document)\\s+from\\s+["\'](.*?)["\']',
'load\\s+(?:task\\s+list|tasks?)\\s+from\\s+["\'](.*?)["\']',
'load\\s+(?:artifact|document|file)\\s+from\\s+["\'](.*?)["\']',
'import\\s+prd\\s+from\\s+\\S+',
'import\\s+tasks?\\s+from\\s+\\S+',
'load\\s+prd\\s+from\\s+\\S+',
'load\\s+tasks?\\s+from\\s+\\S+',
'import\\s+from\\s+\\S+',
'load\\s+from\\s+\\S+',
'load\\s+(?:prd|product\\s+requirements?\\s+document)\\s+file',
'load\\s+(?:task\\s+list|tasks?)\\s+file',
'import\\s+(?:prd|product\\s+requirements?\\s+document)\\s+file',
'import\\s+(?:task\\s+list|tasks?)\\s+file',
'load\\s+prd\\s+file',
'load\\s+tasks?\\s+file',
'import\\s+prd\\s+file',
'import\\s+tasks?\\s+file'
],
keywords: ['import', 'load', 'from', 'prd', 'product', 'requirements', 'document', 'task', 'tasks', 'list', 'artifact', 'file'],
requiredEntities: [],
optionalEntities: ['artifactType', 'filePath', 'projectName'],
priority: 10,
active: true,
examples: [
'Import PRD from file.md',
'Load task list from path/to/file.md',
'Import artifact from document.md',
'Load PRD file',
'Import tasks from file',
'Import PRD from "/path/to/requirements.md"',
'Load task list from "project-tasks.md"'
]
});
logger.info({ patternCount: this.getTotalPatternCount() }, 'Default patterns initialized');
}
addPattern(intent, pattern) {
if (!this.patterns.has(intent)) {
this.patterns.set(intent, []);
}
const intentPatterns = this.patterns.get(intent);
const existingPattern = intentPatterns.find(p => p.id === pattern.id);
if (existingPattern) {
logger.warn({ patternId: pattern.id, intent }, 'Pattern already exists, updating');
Object.assign(existingPattern, pattern);
}
else {
intentPatterns.push(pattern);
intentPatterns.sort((a, b) => b.priority - a.priority);
}
logger.debug({ patternId: pattern.id, intent, priority: pattern.priority }, 'Pattern added');
}
removePattern(intent, patternId) {
const intentPatterns = this.patterns.get(intent);
if (!intentPatterns) {
return false;
}
const index = intentPatterns.findIndex(p => p.id === patternId);
if (index === -1) {
return false;
}
intentPatterns.splice(index, 1);
logger.debug({ patternId, intent }, 'Pattern removed');
return true;
}
matchIntent(text) {
const startTime = Date.now();
const matches = [];
const normalizedText = text.toLowerCase().trim();
for (const [intent, intentPatterns] of Array.from(this.patterns.entries())) {
for (const pattern of intentPatterns) {
if (!pattern.active)
continue;
const match = this.matchPattern(normalizedText, pattern, text);
if (match) {
matches.push({
intent,
confidence: match.confidence,
confidenceLevel: this.getConfidenceLevel(match.confidence),
entities: match.entities,
pattern,
matchedText: match.matchedText,
processingTime: Date.now() - startTime
});
}
}
}
matches.sort((a, b) => b.confidence - a.confidence);
const filteredMatches = matches.filter(m => m.confidence >= this.config.minConfidence);
logger.debug({
inputLength: text.length,
totalMatches: matches.length,
filteredMatches: filteredMatches.length,
processingTime: Date.now() - startTime
}, 'Intent matching completed');
return filteredMatches;
}
matchPattern(text, pattern, originalText) {
let bestMatch = null;
let bestConfidence = 0;
for (const patternStr of pattern.patterns) {
try {
const regex = new RegExp(patternStr, 'i');
const match = text.match(regex);
if (match) {
const confidence = this.calculatePatternConfidence(text, match, pattern);
if (confidence > bestConfidence) {
bestMatch = match;
bestConfidence = confidence;
}
}
}
catch (error) {
logger.warn({ pattern: patternStr, error }, 'Invalid regex pattern');
}
}
if (!bestMatch || bestConfidence < this.config.minConfidence) {
return null;
}
const entities = this.extractEntities(originalText, bestMatch, pattern);
return {
confidence: bestConfidence,
entities,
matchedText: bestMatch[0]
};
}
calculatePatternConfidence(text, match, pattern) {
let confidence = 0.5;
const keywordMatches = pattern.keywords.filter(keyword => text.toLowerCase().includes(keyword.toLowerCase()));
confidence += (keywordMatches.length / pattern.keywords.length) * 0.3;
const matchRatio = match[0].length / text.length;
confidence += Math.min(matchRatio * 0.2, 0.2);
if (match.index === 0) {
confidence += 0.1;
}
return Math.min(Math.max(confidence, 0), 1);
}
extractEntities(originalText, match, pattern) {
const entities = {};
switch (pattern.intent) {
case 'create_project':
case 'open_project':
Object.assign(entities, EntityExtractors.projectName(originalText, match));
break;
case 'create_task':
case 'run_task':
Object.assign(entities, EntityExtractors.taskInfo(originalText, match));
break;
case 'list_projects':
Object.assign(entities, EntityExtractors.statusInfo(originalText, match));
break;
case 'list_tasks':
Object.assign(entities, EntityExtractors.statusInfo(originalText, match));
Object.assign(entities, EntityExtractors.agentInfo(originalText, match));
Object.assign(entities, EntityExtractors.projectName(originalText, match));
break;
case 'check_status':
Object.assign(entities, EntityExtractors.statusInfo(originalText, match));
Object.assign(entities, EntityExtractors.projectName(originalText, match));
break;
case 'assign_task':
Object.assign(entities, EntityExtractors.agentInfo(originalText, match));
Object.assign(entities, EntityExtractors.taskInfo(originalText, match));
break;
case 'decompose_task':
Object.assign(entities, EntityExtractors.taskInfo(originalText, match));
Object.assign(entities, EntityExtractors.descriptionInfo(originalText, match));
break;
case 'decompose_epic':
Object.assign(entities, EntityExtractors.epicInfo(originalText, match));
Object.assign(entities, EntityExtractors.descriptionInfo(originalText, match));
break;
case 'decompose_project':
Object.assign(entities, EntityExtractors.projectName(originalText, match));
Object.assign(entities, EntityExtractors.descriptionInfo(originalText, match));
break;
case 'search_files':
Object.assign(entities, EntityExtractors.searchInfo(originalText, match));
break;
case 'search_content':
Object.assign(entities, EntityExtractors.searchInfo(originalText, match));
Object.assign(entities, EntityExtractors.contentInfo(originalText, match));
break;
case 'parse_prd':
Object.assign(entities, EntityExtractors.projectName(originalText, match));
Object.assign(entities, EntityExtractors.artifactInfo(originalText, match));
break;
case 'parse_tasks':
Object.assign(entities, EntityExtractors.projectName(originalText, match));
Object.assign(entities, EntityExtractors.artifactInfo(originalText, match));
break;
case 'import_artifact':
Object.assign(entities, EntityExtractors.projectName(originalText, match));
Object.assign(entities, EntityExtractors.artifactInfo(originalText, match));
break;
case 'update_project':
Object.assign(entities, EntityExtractors.projectName(originalText, match));
Object.assign(entities, EntityExtractors.general(originalText, match));
break;
}
Object.assign(entities, EntityExtractors.general(originalText, match));
return entities;
}
getConfidenceLevel(confidence) {
if (confidence >= 0.9)
return 'very_high';
if (confidence >= 0.7)
return 'high';
if (confidence >= 0.5)
return 'medium';
if (confidence >= 0.3)
return 'low';
return 'very_low';
}
getTotalPatternCount() {
let total = 0;
for (const patterns of Array.from(this.patterns.values())) {
total += patterns.length;
}
return total;
}
getPatternsForIntent(intent) {
return this.patterns.get(intent) || [];
}
getSupportedIntents() {
return Array.from(this.patterns.keys());
}
updateConfig(config) {
this.config = { ...this.config, ...config };
logger.info({ config: this.config }, 'Pattern engine configuration updated');
}
getConfig() {
return { ...this.config };
}
clearPatterns() {
this.patterns.clear();
logger.info('All patterns cleared');
}
exportPatterns() {
const exported = {};
for (const [intent, patterns] of Array.from(this.patterns.entries())) {
exported[intent] = patterns;
}
return exported;
}
importPatterns(patterns) {
this.clearPatterns();
for (const [intent, intentPatterns] of Object.entries(patterns)) {
for (const pattern of intentPatterns) {
this.addPattern(intent, pattern);
}
}
logger.info({ intentCount: Object.keys(patterns).length }, 'Patterns imported');
}
}