UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

386 lines 14.2 kB
/** * Context Analyzer - Intelligent detection of current development context * Enables context-aware command routing and smart defaults */ import { extname } from 'path'; import { spawn } from 'child_process'; export class MCPContext { session_id; project_path; current_files = []; recent_activity = []; time_of_day = 'unknown'; last_activity; project_state = 'unknown'; user_patterns = {}; is_debugging = false; is_learning = false; claude_context = {}; constructor(data = {}) { Object.assign(this, data); } toMCPContext() { return { session_id: this.session_id, project_path: this.project_path, current_files: this.current_files, recent_activity: this.recent_activity, time_of_day: this.time_of_day, last_activity: this.last_activity, project_state: this.project_state, user_patterns: this.user_patterns, is_debugging: this.is_debugging, is_learning: this.is_learning, claude_context: this.claude_context }; } } export class ContextAnalyzer { cacheTimeout = 30000; // 30 seconds contextCache = null; /** * Detect current development context with caching */ async detectContext() { // Check cache if (this.contextCache && (Date.now() - this.contextCache.timestamp) < this.cacheTimeout) { return this.contextCache.context; } const context = new MCPContext(); // Detect basic context await this.detectBasicContext(context); // Detect time context this.detectTimeContext(context); // Detect project state await this.detectProjectState(context); // Detect session type await this.detectSessionType(context); // Detect recent activity await this.detectRecentActivity(context); // Cache the result this.contextCache = { context, timestamp: Date.now() }; return context; } async detectBasicContext(context) { try { // Generate session ID context.session_id = `mira-session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; // Detect project path context.project_path = process.cwd(); // Detect current files from git status const currentFiles = await this.getCurrentFiles(); context.current_files = currentFiles; } catch (error) { // Silent failure, use defaults } } detectTimeContext(context) { const now = new Date(); const hour = now.getHours(); if (hour >= 5 && hour < 12) { context.time_of_day = 'morning'; } else if (hour >= 12 && hour < 17) { context.time_of_day = 'afternoon'; } else if (hour >= 17 && hour < 22) { context.time_of_day = 'evening'; } else { context.time_of_day = 'night'; } } async detectProjectState(context) { try { const fileTypes = context.current_files.map(file => extname(file).toLowerCase()); // Analyze file types to determine project state if (this.hasTestFiles(fileTypes)) { context.project_state = 'testing'; } else if (this.hasDocumentationFiles(fileTypes)) { context.project_state = 'documentation'; } else if (this.hasSourceFiles(fileTypes)) { context.project_state = 'active_development'; } else if (this.hasConfigFiles(fileTypes)) { context.project_state = 'configuration'; } else { context.project_state = 'maintenance'; } } catch (error) { context.project_state = 'unknown'; } } async detectSessionType(context) { try { // Get recent git commits for context clues const recentCommits = await this.getRecentCommits(); const commitMessages = recentCommits.join(' ').toLowerCase(); // Detect debugging session const debugKeywords = ['debug', 'fix', 'bug', 'error', 'issue', 'crash', 'exception']; context.is_debugging = debugKeywords.some(keyword => commitMessages.includes(keyword)); // Detect learning session const learningKeywords = ['learn', 'understand', 'explore', 'research', 'study', 'experiment']; context.is_learning = learningKeywords.some(keyword => commitMessages.includes(keyword)); // Also check current files for context const currentFileContent = context.current_files.join(' ').toLowerCase(); if (!context.is_debugging) { context.is_debugging = debugKeywords.some(keyword => currentFileContent.includes(keyword)); } if (!context.is_learning) { context.is_learning = learningKeywords.some(keyword => currentFileContent.includes(keyword)); } } catch (error) { // Silent failure, keep defaults } } async detectRecentActivity(context) { try { // Get recent git activity const recentCommits = await this.getRecentCommits(5); const recentBranches = await this.getRecentBranches(); context.recent_activity = [ ...recentCommits.map(commit => `commit: ${commit}`), ...recentBranches.map(branch => `branch: ${branch}`) ]; // Get last activity timestamp const lastCommitTime = await this.getLastCommitTime(); if (lastCommitTime) { context.last_activity = lastCommitTime; } } catch (error) { // Silent failure } } // Helper methods for file type detection hasTestFiles(fileTypes) { const testExtensions = ['.test.ts', '.test.js', '.spec.ts', '.spec.js', '.test.py', '.spec.py']; return this.contextCache?.context.current_files.some(file => testExtensions.some(ext => file.toLowerCase().includes(ext))) || false; } hasDocumentationFiles(fileTypes) { const docExtensions = ['.md', '.rst', '.txt', '.doc', '.docx']; return fileTypes.some(ext => docExtensions.includes(ext)); } hasSourceFiles(fileTypes) { const sourceExtensions = ['.ts', '.js', '.py', '.go', '.rs', '.java', '.cpp', '.c', '.cs']; return fileTypes.some(ext => sourceExtensions.includes(ext)); } hasConfigFiles(fileTypes) { const configExtensions = ['.json', '.yaml', '.yml', '.toml', '.ini', '.conf']; const configFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml']; return fileTypes.some(ext => configExtensions.includes(ext)) || (this.contextCache?.context.current_files.some(file => configFiles.some(cfg => file.toLowerCase().includes(cfg))) || false); } // Git integration methods async getCurrentFiles() { return new Promise((resolve) => { const git = spawn('git', ['status', '--porcelain'], { cwd: process.cwd() }); let output = ''; let resolved = false; // Add timeout to prevent hanging const timeout = setTimeout(() => { if (!resolved) { resolved = true; git.kill('SIGTERM'); resolve([]); } }, 5000); // 5 second timeout git.stdout.on('data', (data) => { output += data.toString(); }); git.on('close', (code) => { if (!resolved) { resolved = true; clearTimeout(timeout); if (code === 0) { const files = output .split('\n') .filter(line => line.trim()) .map(line => line.substring(3)) // Remove git status prefix .filter(file => file.trim()); resolve(files); } else { resolve([]); } } }); git.on('error', () => { if (!resolved) { resolved = true; clearTimeout(timeout); resolve([]); } }); }); } async getRecentCommits(limit = 10) { return new Promise((resolve) => { const git = spawn('git', ['log', '--oneline', `-${limit}`], { cwd: process.cwd() }); let output = ''; let resolved = false; // Add timeout to prevent hanging const timeout = setTimeout(() => { if (!resolved) { resolved = true; git.kill('SIGTERM'); resolve([]); } }, 5000); // 5 second timeout git.stdout.on('data', (data) => { output += data.toString(); }); git.on('close', (code) => { if (!resolved) { resolved = true; clearTimeout(timeout); if (code === 0) { const commits = output .split('\n') .filter(line => line.trim()) .map(line => line.substring(8)) // Remove commit hash .filter(msg => msg.trim()); resolve(commits); } else { resolve([]); } } }); git.on('error', () => { if (!resolved) { resolved = true; clearTimeout(timeout); resolve([]); } }); }); } async getRecentBranches() { return new Promise((resolve) => { const git = spawn('git', ['branch', '-a', '--sort=-committerdate'], { cwd: process.cwd() }); let output = ''; let resolved = false; // Add timeout to prevent hanging - this command can be slow with many remote branches const timeout = setTimeout(() => { if (!resolved) { resolved = true; git.kill('SIGTERM'); resolve([]); } }, 3000); // 3 second timeout for branch listing git.stdout.on('data', (data) => { output += data.toString(); }); git.on('close', (code) => { if (!resolved) { resolved = true; clearTimeout(timeout); if (code === 0) { const branches = output .split('\n') .filter(line => line.trim()) .map(line => line.replace(/^\*?\s*/, '').trim()) // Remove * and whitespace .filter(branch => branch && !branch.startsWith('remotes/')) .slice(0, 5); // Top 5 recent branches resolve(branches); } else { resolve([]); } } }); git.on('error', () => { if (!resolved) { resolved = true; clearTimeout(timeout); resolve([]); } }); }); } async getLastCommitTime() { return new Promise((resolve) => { const git = spawn('git', ['log', '-1', '--format=%cI'], { cwd: process.cwd() }); let output = ''; let resolved = false; // Add timeout to prevent hanging const timeout = setTimeout(() => { if (!resolved) { resolved = true; git.kill('SIGTERM'); resolve(null); } }, 5000); // 5 second timeout git.stdout.on('data', (data) => { output += data.toString(); }); git.on('close', (code) => { if (!resolved) { resolved = true; clearTimeout(timeout); if (code === 0 && output.trim()) { resolve(output.trim()); } else { resolve(null); } } }); git.on('error', () => { if (!resolved) { resolved = true; clearTimeout(timeout); resolve(null); } }); }); } /** * Clear the context cache to force refresh */ clearCache() { this.contextCache = null; } /** * Update user patterns based on usage */ updateUserPatterns(patterns) { if (this.contextCache) { this.contextCache.context.user_patterns = { ...this.contextCache.context.user_patterns, ...patterns }; } } } // Global instance for easy access const globalContextAnalyzer = new ContextAnalyzer(); /** * Convenience function to detect current context */ export async function detectCurrentContext() { return globalContextAnalyzer.detectContext(); } /** * Clear global context cache */ export function clearContextCache() { globalContextAnalyzer.clearCache(); } /** * Update global user patterns */ export function updateGlobalUserPatterns(patterns) { globalContextAnalyzer.updateUserPatterns(patterns); } //# sourceMappingURL=context-analyzer.js.map