UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

584 lines 21.9 kB
/** * V3 CLI Status Command * System status display for Claude Flow */ import { output } from '../output.js'; import { callMCPTool, MCPClientError } from '../mcp-client.js'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; // Status refresh interval (ms) const DEFAULT_WATCH_INTERVAL = 2000; // Track CPU usage over time let lastCpuUsage = null; let lastCpuTime = Date.now(); // Get real process CPU usage percentage function getProcessCpuUsage() { const cpuUsage = process.cpuUsage(lastCpuUsage ? { user: lastCpuUsage.user, system: lastCpuUsage.system } : undefined); const now = Date.now(); const elapsed = now - lastCpuTime; // Calculate percentage (cpuUsage is in microseconds) const totalCpu = (cpuUsage.user + cpuUsage.system) / 1000; // Convert to ms const percentage = elapsed > 0 ? (totalCpu / elapsed) * 100 : 0; // Update for next call lastCpuUsage = cpuUsage; lastCpuTime = now; return Math.min(100, Math.max(0, percentage)); } // Get real process memory usage percentage function getProcessMemoryUsage() { const memoryUsage = process.memoryUsage(); const totalMemory = os.totalmem(); const usedMemory = memoryUsage.heapUsed + memoryUsage.external; return (usedMemory / totalMemory) * 100; } // Check if project is initialized function isInitialized(cwd) { const configPath = path.join(cwd, '.claude-flow', 'config.yaml'); return fs.existsSync(configPath); } // Format uptime function formatUptime(ms) { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) { return `${days}d ${hours % 24}h ${minutes % 60}m`; } else if (hours > 0) { return `${hours}h ${minutes % 60}m ${seconds % 60}s`; } else if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } else { return `${seconds}s`; } } // Format bytes function formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`; } // Get system status data async function getSystemStatus() { try { // Get swarm status const swarmStatus = await callMCPTool('swarm_status', { includeMetrics: true }); // Get MCP status let mcpStatus = { running: false, port: null, transport: 'stdio' }; try { const mcp = await callMCPTool('mcp_status', {}); mcpStatus = mcp; } catch { // MCP not running } // Get memory status const memoryStatus = await callMCPTool('memory_stats', {}); // Get task status const taskStatus = await callMCPTool('task_summary', {}); return { initialized: true, running: true, swarm: { id: swarmStatus.swarmId, topology: swarmStatus.topology, agents: { total: swarmStatus.agents.total, active: swarmStatus.agents.active, idle: swarmStatus.agents.idle }, health: swarmStatus.health, uptime: swarmStatus.uptime }, mcp: mcpStatus, memory: { entries: memoryStatus.entries, size: formatBytes(memoryStatus.size), backend: memoryStatus.backend, performance: { searchTime: memoryStatus.performance.avgSearchTime, cacheHitRate: memoryStatus.performance.cacheHitRate } }, tasks: taskStatus, performance: { cpuUsage: getProcessCpuUsage(), memoryUsage: getProcessMemoryUsage(), flashAttention: '2.8x speedup', searchSpeed: '150x faster' } }; } catch (error) { // System not running return { initialized: true, running: false, swarm: { id: null, topology: 'none', agents: { total: 0, active: 0, idle: 0 }, health: 'stopped', uptime: 0 }, mcp: { running: false, port: null, transport: 'stdio' }, memory: { entries: 0, size: '0 B', backend: 'none', performance: { searchTime: 0, cacheHitRate: 0 } }, tasks: { total: 0, pending: 0, running: 0, completed: 0, failed: 0 }, performance: { cpuUsage: 0, memoryUsage: 0, flashAttention: 'N/A', searchSpeed: 'N/A' } }; } } // Display status in text format function displayStatus(status) { output.writeln(); // Header with overall status const statusIcon = status.running ? output.success('[RUNNING]') : output.warning('[STOPPED]'); output.writeln(`${output.bold('Claude Flow V3')} ${statusIcon}`); output.writeln(); // Swarm section output.writeln(output.bold('Swarm')); if (status.running) { output.printTable({ columns: [ { key: 'property', header: 'Property', width: 15 }, { key: 'value', header: 'Value', width: 30 } ], data: [ { property: 'ID', value: status.swarm.id }, { property: 'Topology', value: status.swarm.topology }, { property: 'Health', value: formatHealth(status.swarm.health) }, { property: 'Uptime', value: formatUptime(status.swarm.uptime) } ] }); } else { output.printInfo(' Swarm not running'); } output.writeln(); // Agents section output.writeln(output.bold('Agents')); output.printTable({ columns: [ { key: 'status', header: 'Status', width: 12 }, { key: 'count', header: 'Count', width: 10, align: 'right' } ], data: [ { status: 'Active', count: status.swarm.agents.active }, { status: 'Idle', count: status.swarm.agents.idle }, { status: output.bold('Total'), count: status.swarm.agents.total } ] }); output.writeln(); // Tasks section output.writeln(output.bold('Tasks')); output.printTable({ columns: [ { key: 'status', header: 'Status', width: 12 }, { key: 'count', header: 'Count', width: 10, align: 'right' } ], data: [ { status: 'Pending', count: status.tasks.pending }, { status: 'Running', count: status.tasks.running }, { status: 'Completed', count: status.tasks.completed }, { status: 'Failed', count: status.tasks.failed }, { status: output.bold('Total'), count: status.tasks.total } ] }); output.writeln(); // Memory section output.writeln(output.bold('Memory')); output.printTable({ columns: [ { key: 'property', header: 'Property', width: 18 }, { key: 'value', header: 'Value', width: 20, align: 'right' } ], data: [ { property: 'Backend', value: status.memory.backend }, { property: 'Entries', value: status.memory.entries }, { property: 'Size', value: status.memory.size }, { property: 'Search Time', value: `${status.memory.performance.searchTime.toFixed(2)}ms` }, { property: 'Cache Hit Rate', value: `${(status.memory.performance.cacheHitRate * 100).toFixed(1)}%` } ] }); output.writeln(); // MCP section output.writeln(output.bold('MCP Server')); if (status.mcp.running) { output.printInfo(` Running on port ${status.mcp.port} (${status.mcp.transport})`); } else { output.printInfo(' Not running'); } output.writeln(); // Performance section if (status.running) { output.writeln(output.bold('V3 Performance Gains')); output.printList([ `Flash Attention: ${output.success(status.performance.flashAttention)}`, `Vector Search: ${output.success(status.performance.searchSpeed)}`, `CPU Usage: ${status.performance.cpuUsage.toFixed(1)}%`, `Memory Usage: ${status.performance.memoryUsage.toFixed(1)}%` ]); } } // Format health status with color function formatHealth(health) { switch (health) { case 'healthy': return output.success(health); case 'degraded': return output.warning(health); case 'unhealthy': case 'stopped': return output.error(health); default: return health; } } // Main status action const statusAction = async (ctx) => { const watch = ctx.flags.watch; const interval = ctx.flags.interval || DEFAULT_WATCH_INTERVAL / 1000; const healthCheck = ctx.flags['health-check']; const cwd = ctx.cwd; // Check initialization if (!isInitialized(cwd)) { output.printError('Claude Flow is not initialized in this directory'); output.printInfo('Run "claude-flow init" to initialize'); return { success: false, exitCode: 1 }; } // Get status const status = await getSystemStatus(); // Health check mode if (healthCheck) { return performHealthCheck(status); } // JSON output if (ctx.flags.format === 'json') { output.printJson(status); return { success: true, data: status }; } // Watch mode if (watch) { return watchStatus(interval); } // Single status display displayStatus(status); return { success: true, data: status }; }; // Perform health checks async function performHealthCheck(status) { output.writeln(); output.writeln(output.bold('Health Check')); output.writeln(); const checks = []; // Check if system is running checks.push({ name: 'System Running', status: status.running ? 'pass' : 'fail', message: status.running ? 'System is running' : 'System is not running' }); // Check swarm health if (status.running) { checks.push({ name: 'Swarm Health', status: status.swarm.health === 'healthy' ? 'pass' : status.swarm.health === 'degraded' ? 'warn' : 'fail', message: `Swarm is ${status.swarm.health}` }); // Check agent count checks.push({ name: 'Agents Available', status: status.swarm.agents.active > 0 ? 'pass' : status.swarm.agents.idle > 0 ? 'warn' : 'fail', message: `${status.swarm.agents.active} active, ${status.swarm.agents.idle} idle` }); // Check MCP checks.push({ name: 'MCP Server', status: status.mcp.running ? 'pass' : 'warn', message: status.mcp.running ? `Running on port ${status.mcp.port}` : 'Not running' }); // Check memory backend checks.push({ name: 'Memory Backend', status: status.memory.backend !== 'none' ? 'pass' : 'fail', message: `Using ${status.memory.backend} backend` }); // Check for failed tasks const failRate = status.tasks.total > 0 ? status.tasks.failed / status.tasks.total : 0; checks.push({ name: 'Task Success Rate', status: failRate < 0.05 ? 'pass' : failRate < 0.2 ? 'warn' : 'fail', message: `${((1 - failRate) * 100).toFixed(1)}% success rate` }); } // Display results for (const check of checks) { const icon = check.status === 'pass' ? output.success('[PASS]') : check.status === 'warn' ? output.warning('[WARN]') : output.error('[FAIL]'); output.writeln(`${icon} ${check.name}: ${check.message}`); } output.writeln(); const passed = checks.filter(c => c.status === 'pass').length; const warned = checks.filter(c => c.status === 'warn').length; const failed = checks.filter(c => c.status === 'fail').length; if (failed === 0) { output.printSuccess(`All checks passed (${passed} passed, ${warned} warnings)`); } else { output.printError(`Health check failed (${passed} passed, ${warned} warnings, ${failed} failed)`); } return { success: failed === 0, exitCode: failed > 0 ? 1 : 0, data: { checks, summary: { passed, warned, failed } } }; } // Watch mode - continuous status updates async function watchStatus(intervalSeconds) { output.writeln(); output.writeln(output.bold('Watch Mode')); output.writeln(output.dim(`Refreshing every ${intervalSeconds}s. Press Ctrl+C to exit.`)); output.writeln(); const refresh = async () => { // Clear screen process.stdout.write('\x1b[2J\x1b[H'); output.writeln(output.dim(`Last updated: ${new Date().toLocaleTimeString()}`)); output.writeln(); const status = await getSystemStatus(); displayStatus(status); }; // Initial display await refresh(); // Set up interval const intervalId = setInterval(refresh, intervalSeconds * 1000); // Handle exit return new Promise((resolve) => { process.on('SIGINT', () => { clearInterval(intervalId); output.writeln(); output.printInfo('Watch mode stopped'); resolve({ success: true }); }); }); } // Agents subcommand const agentsCommand = { name: 'agents', description: 'Show detailed agent status', action: async (ctx) => { try { const result = await callMCPTool('agent_list', { includeMetrics: true, status: 'all' }); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); output.writeln(output.bold('Agent Status')); output.writeln(); if (result.agents.length === 0) { output.printInfo('No agents running'); return { success: true, data: result }; } output.printTable({ columns: [ { key: 'id', header: 'ID', width: 20 }, { key: 'type', header: 'Type', width: 12 }, { key: 'status', header: 'Status', width: 10 }, { key: 'task', header: 'Current Task', width: 25 }, { key: 'uptime', header: 'Uptime', width: 12 }, { key: 'success', header: 'Success', width: 8 } ], data: result.agents.map(a => ({ id: a.id, type: a.type, status: formatHealth(a.status), task: a.task || '-', uptime: formatUptime(a.uptime), success: `${(a.metrics.successRate * 100).toFixed(0)}%` })) }); return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Failed to get agent status: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Tasks subcommand const tasksCommand = { name: 'tasks', description: 'Show detailed task status', action: async (ctx) => { try { const result = await callMCPTool('task_list', { status: 'all', limit: 50 }); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); output.writeln(output.bold('Task Status')); output.writeln(); if (result.tasks.length === 0) { output.printInfo('No tasks'); return { success: true, data: result }; } output.printTable({ columns: [ { key: 'id', header: 'ID', width: 15 }, { key: 'type', header: 'Type', width: 15 }, { key: 'status', header: 'Status', width: 12 }, { key: 'priority', header: 'Priority', width: 10 }, { key: 'agent', header: 'Agent', width: 15 }, { key: 'progress', header: 'Progress', width: 10 } ], data: result.tasks.map(t => ({ id: t.id, type: t.type, status: formatHealth(t.status), priority: t.priority, agent: t.agent || '-', progress: `${t.progress}%` })) }); return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Failed to get task status: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Memory subcommand const memoryCommand = { name: 'memory', description: 'Show detailed memory status', action: async (ctx) => { try { const result = await callMCPTool('memory_detailed-stats', {}); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); output.writeln(output.bold('Memory Status')); output.writeln(); output.printTable({ columns: [ { key: 'property', header: 'Property', width: 20 }, { key: 'value', header: 'Value', width: 25 } ], data: [ { property: 'Backend', value: result.backend }, { property: 'Total Entries', value: result.entries.toLocaleString() }, { property: 'Storage Size', value: formatBytes(result.size) }, { property: 'HNSW Index', value: result.performance.hnswEnabled ? 'Enabled' : 'Disabled' } ] }); output.writeln(); output.writeln(output.bold('Performance')); output.printTable({ columns: [ { key: 'metric', header: 'Metric', width: 20 }, { key: 'value', header: 'Value', width: 20, align: 'right' } ], data: [ { metric: 'Avg Search Time', value: `${result.performance.avgSearchTime.toFixed(2)}ms` }, { metric: 'Avg Write Time', value: `${result.performance.avgWriteTime.toFixed(2)}ms` }, { metric: 'Cache Hit Rate', value: `${(result.performance.cacheHitRate * 100).toFixed(1)}%` } ] }); output.writeln(); output.writeln(output.bold('V3 Performance Gains')); output.printList([ `Search Speed: ${output.success(result.v3Gains.searchImprovement)}`, `Memory Usage: ${output.success(result.v3Gains.memoryReduction)}` ]); return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Failed to get memory status: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Main status command export const statusCommand = { name: 'status', description: 'Show system status', subcommands: [agentsCommand, tasksCommand, memoryCommand], options: [ { name: 'watch', short: 'w', description: 'Watch mode - continuously update status', type: 'boolean', default: false }, { name: 'interval', short: 'i', description: 'Watch mode update interval in seconds', type: 'number', default: 2 }, { name: 'health-check', description: 'Perform health checks and exit', type: 'boolean', default: false } ], examples: [ { command: 'claude-flow status', description: 'Show current system status' }, { command: 'claude-flow status --watch', description: 'Watch mode with live updates' }, { command: 'claude-flow status --watch -i 5', description: 'Watch mode updating every 5 seconds' }, { command: 'claude-flow status --health-check', description: 'Run health checks' }, { command: 'claude-flow status --json', description: 'Output status as JSON' }, { command: 'claude-flow status agents', description: 'Show detailed agent status' }, { command: 'claude-flow status tasks', description: 'Show detailed task status' }, { command: 'claude-flow status memory', description: 'Show detailed memory status' } ], action: statusAction }; export default statusCommand; //# sourceMappingURL=status.js.map