UNPKG

@ooples/token-optimizer-mcp

Version:

Intelligent context window optimization for Claude Code - store content externally via caching and compression, freeing up your context window for what matters

472 lines • 17.7 kB
/** * Smart Processes Tool - 60% Token Reduction * * Monitors running processes with: * - Process filtering (hide noise) * - Resource usage tracking * - Smart aggregation * - Anomaly detection */ import { exec } from 'child_process'; import { promisify } from 'util'; import { CacheEngine } from '../../core/cache-engine.js'; import { TokenCounter } from '../../core/token-counter.js'; import { MetricsCollector } from '../../core/metrics.js'; import { join } from 'path'; import { homedir, cpus, totalmem } from 'os'; const execAsync = promisify(exec); export class SmartProcesses { cache; cacheNamespace = 'smart_processes'; platform; constructor(cache, _tokenCounter, _metrics, _projectRoot) { this.cache = cache; this.platform = process.platform; } /** * Get process information with smart filtering and caching */ async run(options = {}) { const { filter, cpuThreshold = 10, memoryThreshold = 100, includeSystem = false, limit = 20, useCache = true, maxCacheAge = 60, } = options; // Get current snapshot const snapshot = await this.captureSnapshot(); // Get previous snapshot for comparison (if cache enabled) let previousSnapshot = null; if (useCache) { previousSnapshot = this.getCachedSnapshot(maxCacheAge); } // Cache current snapshot this.cacheSnapshot(snapshot); // Filter processes const filtered = this.filterProcesses(snapshot.processes, { filter, cpuThreshold, memoryThreshold, includeSystem, }); // Analyze and transform return this.transformOutput(snapshot, filtered, previousSnapshot, limit); } /** * Capture current process snapshot */ async captureSnapshot() { const processes = await this.getProcessList(); const totalCpu = processes.reduce((sum, p) => sum + p.cpu, 0); const totalMemory = processes.reduce((sum, p) => sum + p.memory, 0); return { timestamp: Date.now(), processes, totalCpu, totalMemory, systemInfo: { platform: this.platform, cpuCount: cpus().length, totalMemoryMB: Math.round(totalmem() / 1024 / 1024), }, }; } /** * Get list of running processes */ async getProcessList() { if (this.platform === 'win32') { return this.getWindowsProcesses(); } else { return this.getUnixProcesses(); } } /** * Get processes on Windows */ async getWindowsProcesses() { try { const { stdout } = await execAsync('wmic process get ProcessId,Name,UserModeTime,WorkingSetSize,CommandLine /format:csv', { maxBuffer: 10 * 1024 * 1024 }); const processes = []; const lines = stdout.split('\n').slice(1); // Skip header for (const line of lines) { if (!line.trim()) continue; const parts = line.split(','); if (parts.length < 5) continue; const pid = parseInt(parts[3], 10); const name = parts[1]; const memory = parseInt(parts[4], 10) / 1024 / 1024; // Convert to MB const cpu = parseInt(parts[2], 10) / 10000; // Rough approximation if (isNaN(pid) || isNaN(memory)) continue; processes.push({ pid, name: name || 'Unknown', cpu: isNaN(cpu) ? 0 : cpu, memory, command: parts[0] || name, user: 'current', // Windows WMIC doesn't easily provide user }); } return processes; } catch (err) { console.error('Error getting Windows processes:', err); return []; } } /** * Get processes on Unix/Linux/macOS */ async getUnixProcesses() { try { const { stdout } = await execAsync('ps aux --no-headers', { maxBuffer: 10 * 1024 * 1024, }); const processes = []; const lines = stdout.split('\n'); for (const line of lines) { if (!line.trim()) continue; const parts = line.trim().split(/\s+/); if (parts.length < 11) continue; const user = parts[0]; const pid = parseInt(parts[1], 10); const cpu = parseFloat(parts[2]); const memory = parseFloat(parts[3]); const command = parts.slice(10).join(' '); const name = parts[10].split('/').pop() || 'Unknown'; if (isNaN(pid) || isNaN(cpu) || isNaN(memory)) continue; // Convert memory % to MB const totalMemoryMB = totalmem() / 1024 / 1024; const memoryMB = (memory / 100) * totalMemoryMB; processes.push({ pid, name, cpu, memory: memoryMB, command, user, }); } return processes; } catch (err) { console.error('Error getting Unix processes:', err); return []; } } /** * Filter processes based on criteria */ filterProcesses(processes, options) { let filtered = processes; // Filter by name pattern if (options.filter) { const pattern = new RegExp(options.filter, 'i'); filtered = filtered.filter((p) => pattern.test(p.name) || pattern.test(p.command)); } // Filter by CPU threshold filtered = filtered.filter((p) => p.cpu >= options.cpuThreshold); // Filter by memory threshold filtered = filtered.filter((p) => p.memory >= options.memoryThreshold); // Filter out system processes (if not included) if (!options.includeSystem) { filtered = filtered.filter((p) => !p.name.startsWith('System') && !p.name.startsWith('kernel') && p.user !== 'root' && p.user !== 'SYSTEM'); } return filtered; } /** * Get cached snapshot */ getCachedSnapshot(maxAge) { const key = `${this.cacheNamespace}:snapshot`; const cached = this.cache.get(key); if (!cached) { return null; } try { const snapshot = JSON.parse(cached); const age = (Date.now() - snapshot.timestamp) / 1000; if (age <= maxAge) { return snapshot; } } catch (err) { return null; } return null; } /** * Cache snapshot */ cacheSnapshot(snapshot) { const key = `${this.cacheNamespace}:snapshot`; const dataToCache = JSON.stringify(snapshot); const originalSize = this.estimateOriginalOutputSize(snapshot); const compactSize = dataToCache.length; this.cache.set(key, dataToCache, originalSize, compactSize); } /** * Detect anomalies in process list */ detectAnomalies(processes, previous) { const anomalies = []; // Detect CPU spikes (> 80%) const highCpuProcesses = processes.filter((p) => p.cpu > 80); if (highCpuProcesses.length > 0) { anomalies.push({ type: 'cpu_spike', severity: 'high', message: `${highCpuProcesses.length} process(es) consuming >80% CPU`, processes: highCpuProcesses.map((p) => ({ pid: p.pid, name: p.name })), }); } // Detect memory leaks (compare with previous if available) if (previous) { for (const current of processes) { const prev = previous.find((p) => p.pid === current.pid); if (prev && current.memory > prev.memory * 2) { anomalies.push({ type: 'memory_leak', severity: 'medium', message: `Memory usage doubled for ${current.name}`, processes: [{ pid: current.pid, name: current.name }], }); } } } // Detect duplicate processes const nameCounts = new Map(); for (const p of processes) { nameCounts.set(p.name, (nameCounts.get(p.name) || 0) + 1); } for (const [name, count] of nameCounts.entries()) { if (count > 5) { const duplicates = processes.filter((p) => p.name === name); anomalies.push({ type: 'duplicate', severity: 'low', message: `${count} instances of ${name} running`, processes: duplicates.map((p) => ({ pid: p.pid, name: p.name })), }); } } return anomalies; } /** * Calculate resource usage trends */ calculateTrends(current, previous) { if (!previous) { return undefined; } return { cpuDelta: current.totalCpu - previous.totalCpu, memoryDelta: current.totalMemory - previous.totalMemory, processCountDelta: current.processes.length - previous.processes.length, }; } /** * Transform process data to smart output */ transformOutput(snapshot, filtered, previous, limit) { // Sort by CPU and memory const byCpu = [...filtered].sort((a, b) => b.cpu - a.cpu).slice(0, limit); const byMemory = [...filtered] .sort((a, b) => b.memory - a.memory) .slice(0, limit); // Detect anomalies const anomalies = this.detectAnomalies(filtered, previous ? previous.processes : null); // Calculate trends const trends = this.calculateTrends(snapshot, previous); // Count high resource processes const highCpuCount = filtered.filter((p) => p.cpu > 50).length; const highMemoryCount = filtered.filter((p) => p.memory > 500).length; const originalSize = this.estimateOriginalOutputSize(snapshot); const compactSize = this.estimateCompactSize(filtered, anomalies); return { summary: { totalProcesses: snapshot.processes.length, filteredCount: filtered.length, highCpuCount, highMemoryCount, timestamp: snapshot.timestamp, }, topProcesses: { byCpu: byCpu.map((p) => ({ pid: p.pid, name: p.name, cpu: p.cpu, memory: p.memory, })), byMemory: byMemory.map((p) => ({ pid: p.pid, name: p.name, cpu: p.cpu, memory: p.memory, })), }, anomalies, trends, metrics: { originalTokens: Math.ceil(originalSize / 4), compactedTokens: Math.ceil(compactSize / 4), reductionPercentage: Math.round(((originalSize - compactSize) / originalSize) * 100), }, }; } /** * Estimate original output size (full process list) */ estimateOriginalOutputSize(snapshot) { // Each process is ~200 chars in full ps/tasklist output return snapshot.processes.length * 200 + 500; } /** * Estimate compact output size */ estimateCompactSize(filtered, anomalies) { const summary = { totalProcesses: filtered.length, highCpu: filtered.filter((p) => p.cpu > 50).length, highMemory: filtered.filter((p) => p.memory > 500).length, }; // Only top 10 processes + anomalies const topProcesses = filtered.slice(0, 10).map((p) => ({ name: p.name, cpu: p.cpu, memory: p.memory, })); return JSON.stringify({ summary, topProcesses, anomalies }).length; } /** * Close cache connection */ close() { this.cache.close(); } } /** * Factory function for creating SmartProcesses with shared resources (for benchmarks) */ export function getSmartProcessesTool(cache, tokenCounter, metrics, projectRoot) { return new SmartProcesses(cache, tokenCounter, metrics, projectRoot); } /** * CLI-friendly function for running smart processes */ export async function runSmartProcesses(options = {}) { const cache = new CacheEngine(join(homedir(), '.token-optimizer-cache', 'cache.db')); const tokenCounter = new TokenCounter(); const metrics = new MetricsCollector(); const smartProcesses = new SmartProcesses(cache, tokenCounter, metrics, options.projectRoot); try { const result = await smartProcesses.run(options); let output = `\nāš™ļø Smart Process Monitor\n`; output += `${'='.repeat(50)}\n\n`; // Summary output += `Summary:\n`; output += ` Total Processes: ${result.summary.totalProcesses}\n`; output += ` Filtered: ${result.summary.filteredCount}\n`; output += ` High CPU (>50%): ${result.summary.highCpuCount}\n`; output += ` High Memory (>500MB): ${result.summary.highMemoryCount}\n`; output += ` Timestamp: ${new Date(result.summary.timestamp).toLocaleTimeString()}\n\n`; // Anomalies if (result.anomalies.length > 0) { output += `🚨 Anomalies Detected:\n`; for (const anomaly of result.anomalies) { const severityIcon = anomaly.severity === 'high' ? 'šŸ”“' : anomaly.severity === 'medium' ? '🟔' : '🟢'; output += ` ${severityIcon} [${anomaly.type}] ${anomaly.message}\n`; for (const proc of anomaly.processes.slice(0, 3)) { output += ` - PID ${proc.pid}: ${proc.name}\n`; } if (anomaly.processes.length > 3) { output += ` ... and ${anomaly.processes.length - 3} more\n`; } } output += '\n'; } // Top processes by CPU if (result.topProcesses.byCpu.length > 0) { output += `Top CPU Usage:\n`; for (const proc of result.topProcesses.byCpu.slice(0, 5)) { output += ` ${proc.cpu.toFixed(1)}% | ${proc.memory.toFixed(0)}MB | ${proc.name} (PID ${proc.pid})\n`; } output += '\n'; } // Top processes by memory if (result.topProcesses.byMemory.length > 0) { output += `Top Memory Usage:\n`; for (const proc of result.topProcesses.byMemory.slice(0, 5)) { output += ` ${proc.memory.toFixed(0)}MB | ${proc.cpu.toFixed(1)}% | ${proc.name} (PID ${proc.pid})\n`; } output += '\n'; } // Trends if (result.trends) { output += `Resource Trends:\n`; output += ` CPU: ${result.trends.cpuDelta > 0 ? '+' : ''}${result.trends.cpuDelta.toFixed(1)}%\n`; output += ` Memory: ${result.trends.memoryDelta > 0 ? '+' : ''}${result.trends.memoryDelta.toFixed(0)}MB\n`; output += ` Process Count: ${result.trends.processCountDelta > 0 ? '+' : ''}${result.trends.processCountDelta}\n\n`; } // Metrics output += `Token Reduction:\n`; output += ` Original: ${result.metrics.originalTokens} tokens\n`; output += ` Compacted: ${result.metrics.compactedTokens} tokens\n`; output += ` Reduction: ${result.metrics.reductionPercentage}%\n`; return output; } finally { smartProcesses.close(); } } // MCP Tool definition export const SMART_PROCESSES_TOOL_DEFINITION = { name: 'smart_processes', description: 'Monitor and analyze system processes with anomaly detection and resource tracking', inputSchema: { type: 'object', properties: { filter: { type: 'string', description: 'Filter processes by name pattern', }, cpuThreshold: { type: 'number', description: 'Show only high CPU usage processes (> threshold %)', }, memoryThreshold: { type: 'number', description: 'Show only high memory usage processes (> threshold MB)', }, includeSystem: { type: 'boolean', description: 'Include system processes', default: false, }, limit: { type: 'number', description: 'Maximum number of processes to show', default: 20, }, projectRoot: { type: 'string', description: 'Project root directory', }, compareWithPrevious: { type: 'boolean', description: 'Compare with previous snapshot', default: true, }, }, }, }; //# sourceMappingURL=smart-processes.js.map