/** * Enhanced Intent Analyzer * Analyzes user input to determine intent and select appropriate tools * Inspired by ReAct pattern and agent intent analysis */ class IntentAnalyzer { constructor(config = {}) { this.tools = config.tools || []; this.history = config.history || []; this.patterns = this.loadPatterns(); this.context = { previousCommands: [], currentDirectory: process.cwd(), preferences: {} }; } /** * Load command patterns for intent detection */ loadPatterns() { return { // Shell command patterns shell: [ /^(ls|ll|la|dir)\b/, /^(cd|pwd)\b/, /^(cat|less|more|head|tail)\b/, /^(echo|printf)\b/, /^(grep|rg|ag|ack)\b/, /^(find|locate)\b/, /^(npm|yarn|pnpm|pip|pip3|cargo|go)\b/, /^(git|gh)\b/, /^(curl|wget)\b/, /^(ssh|scp|rsync)\b/, /^(docker|podman)\b/, /^(node|python|python3|ruby|bash|sh|zsh)\s/, /^(make|cmake|ninja)\b/, /^(test|npm test|pytest)\b/, /^(build|npm build|webpack|vite)\b/ ], // File operation patterns file: [ /^(read|open|view|show)\s+(?:file\s+)?['"]?[\w\-./]/, /^(write|create|save)\s+(?:file\s+)?['"]?[\w\-./]/, /^(delete|remove|rm)\s+(?:file\s+)?['"]?[\w\-./]/, /^(copy|cp|move|mv)\s+(?:file\s+)?['"]?[\w\-./]/, /^(list|ls|dir)\s+(?:files?\s+)?(?:in\s+)?['"]?[\w\-./]/, /\.(txt|md|js|ts|py|html|css|json|yaml|yml|xml)$/, /^edit\s+['"]?[\w\-./]/ ], // Code execution patterns code: [ /^run\s+(?:code|script|python|node)\b/, /^execute\s+(?:code|python|javascript)\b/, /^eval\b/, /^(python|python3|node)\s+-c/, /^(python|python3|node)\s+\S+\.py$/ ], // Web search patterns web: [ /^(search|google|bing)\b/, /^(lookup|find)\s+(?:on\s+(?:web|google|internet))/, /^what\s+is\b/, /^how\s+to\b/, /^explain\b/ ] }; } /** * Register available tools */ setTools(tools) { this.tools = tools; return this; } /** * Update context */ updateContext(updates) { Object.assign(this.context, updates); return this; } /** * Analyze input and determine intent * * @param {string} input - User input * @returns {IntentResult} Analysis result */ analyze(input) { const trimmed = input.trim(); // Check for empty input if (!trimmed) { return { intent: 'unknown', confidence: 0, tool: null, parameters: {}, reasoning: 'Empty input' }; } // Analyze patterns const patternResult = this.analyzePatterns(trimmed); if (patternResult.confidence > 0.7) { return patternResult; } // Analyze keywords const keywordResult = this.analyzeKeywords(trimmed); if (keywordResult.confidence > 0.5) { return keywordResult; } // Use context/history const contextResult = this.analyzeContext(trimmed); if (contextResult.confidence > 0.4) { return contextResult; } // Default to shell command return { intent: 'shell', confidence: 0.3, tool: 'shell', parameters: { command: trimmed }, reasoning: 'Default to shell execution' }; } /** * Analyze based on known patterns */ analyzePatterns(input) { const lower = input.toLowerCase(); for (const [intent, patterns] of Object.entries(this.patterns)) { for (const pattern of patterns) { if (pattern.test(input)) { return this.buildIntentResult(intent, input, 0.9, 'Pattern match'); } } } return { confidence: 0 }; } /** * Analyze based on keywords */ analyzeKeywords(input) { const keywords = { shell: ['execute', 'run', 'command', 'terminal', 'shell', 'bash'], file: ['file', 'folder', 'directory', 'read', 'write', 'create'], code: ['code', 'script', 'function', 'class'], web: ['search', 'find', 'google', 'lookup', 'internet', 'web'] }; const lower = input.toLowerCase(); let bestMatch = { intent: null, score: 0 }; for (const [intent, kwList] of Object.entries(keywords)) { const score = kwList.reduce((acc, kw) => { return acc + (lower.includes(kw) ? 1 : 0); }, 0); if (score > bestMatch.score) { bestMatch = { intent, score }; } } if (bestMatch.score > 0) { const confidence = Math.min(0.6, bestMatch.score * 0.2); return this.buildIntentResult(bestMatch.intent, input, confidence, 'Keyword match'); } return { confidence: 0 }; } /** * Analyze based on context and history */ analyzeContext(input) { // Check if this is a continuation const lastCommand = this.context.previousCommands[ this.context.previousCommands.length - 1 ]; if (lastCommand) { // Continuation of previous command if (input.startsWith('&&') || input.startsWith('||') || input.startsWith('|')) { return { intent: 'shell', confidence: 0.8, tool: 'shell', parameters: { command: `${lastCommand.command} ${input}` }, reasoning: 'Command continuation' }; } // Repeat previous command if (input === '!!' || input === 'again') { return { intent: lastCommand.intent, confidence: 0.7, tool: lastCommand.tool, parameters: lastCommand.parameters, reasoning: 'Repeat previous command' }; } // Reference to previous output if (input.includes('previous') || input.includes('last')) { return { intent: lastCommand.intent, confidence: 0.6, tool: lastCommand.tool, parameters: lastCommand.parameters, reasoning: 'Reference to previous command' }; } } return { confidence: 0 }; } /** * Build intent result based on detected intent */ buildIntentResult(intent, input, confidence, reasoning) { const toolMap = { shell: 'shell', file: 'file', code: 'shell', // Code execution uses shell web: 'web_search' // Hypothetical web tool }; const parameters = this.extractParameters(intent, input); return { intent, confidence, tool: toolMap[intent] || 'shell', parameters, reasoning }; } /** * Extract parameters based on intent */ extractParameters(intent, input) { switch (intent) { case 'shell': return { command: input }; case 'file': return this.extractFileParameters(input); case 'code': return { command: input }; case 'web': return { query: input.replace(/^(search|google|bing)\s+/i, '') }; default: return { command: input }; } } /** * Extract file operation parameters */ extractFileParameters(input) { const lower = input.toLowerCase(); // Detect operation let operation = 'read'; if (lower.startsWith('write') || lower.startsWith('create') || lower.startsWith('save')) { operation = 'write'; } else if (lower.startsWith('delete') || lower.startsWith('remove')) { operation = 'delete'; } else if (lower.startsWith('copy') || lower.startsWith('cp')) { operation = 'copy'; } else if (lower.startsWith('move') || lower.startsWith('mv')) { operation = 'move'; } else if (lower.startsWith('list') || lower.startsWith('ls')) { operation = 'list'; } // Extract path const pathMatch = input.match(/['"]?([\w\-./\\]+)['"]?/); const path = pathMatch ? pathMatch[1] : ''; return { operation, path }; } /** * Get suggestions based on context */ getSuggestions(input) { const suggestions = []; // Command history suggestions if (input.length > 0) { const matching = this.context.previousCommands .filter(cmd => cmd.command && cmd.command.startsWith(input)) .slice(0, 5) .map(cmd => cmd.command); suggestions.push(...matching); } // Common commands if (!input) { suggestions.push( 'ls -la', 'pwd', 'git status', 'npm install', 'npm test' ); } return suggestions; } /** * Learn from executed commands */ learn(command, result) { this.context.previousCommands.push({ command, result, timestamp: Date.now() }); // Keep only last 100 commands if (this.context.previousCommands.length > 100) { this.context.previousCommands.shift(); } return this; } } /** * Intent Result Structure */ class IntentResult { constructor(result) { this.intent = result.intent || 'unknown'; this.confidence = result.confidence || 0; this.tool = result.tool || null; this.parameters = result.parameters || {}; this.reasoning = result.reasoning || ''; } isValid() { return this.confidence > 0.3 && this.tool; } toJSON() { return { intent: this.intent, confidence: this.confidence, tool: this.tool, parameters: this.parameters, reasoning: this.reasoning }; } } module.exports = { IntentAnalyzer, IntentResult };