@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
JavaScript
/**
* 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