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
743 lines (666 loc) • 22.3 kB
text/typescript
/**
* V3 CLI Status Command
* System status display for Claude Flow
*/
import type { Command, CommandContext, CommandResult } from '../types.js';
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: { user: number; system: number } | null = null;
let lastCpuTime = Date.now();
// Get real process CPU usage percentage
function getProcessCpuUsage(): number {
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(): number {
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: string): boolean {
const configPath = path.join(cwd, '.claude-flow', 'config.yaml');
return fs.existsSync(configPath);
}
// Format uptime
function formatUptime(ms: number): string {
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: number): string {
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(): Promise<{
initialized: boolean;
running: boolean;
swarm: {
id: string | null;
topology: string;
agents: { total: number; active: number; idle: number };
health: string;
uptime: number;
};
mcp: {
running: boolean;
port: number | null;
transport: string;
};
memory: {
entries: number;
size: string;
backend: string;
performance: { searchTime: number; cacheHitRate: number };
};
tasks: {
total: number;
pending: number;
running: number;
completed: number;
failed: number;
};
performance: {
cpuUsage: number;
memoryUsage: number;
flashAttention: string;
searchSpeed: string;
};
}> {
try {
// Get swarm status
const swarmStatus = await callMCPTool<{
swarmId: string;
topology: string;
agents: { total: number; active: number; idle: number; terminated: number };
health: string;
uptime: number;
}>('swarm_status', { includeMetrics: true });
// Get MCP status
let mcpStatus = { running: false, port: null as number | null, transport: 'stdio' };
try {
const mcp = await callMCPTool<{
running: boolean;
port: number;
transport: string;
}>('mcp_status', {});
mcpStatus = mcp;
} catch {
// MCP not running
}
// Get memory status
const memoryStatus = await callMCPTool<{
entries: number;
size: number;
backend: string;
performance: { avgSearchTime: number; cacheHitRate: number };
}>('memory_stats', {});
// Get task status
const taskStatus = await callMCPTool<{
total: number;
pending: number;
running: number;
completed: number;
failed: number;
}>('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: 'not measured',
searchSpeed: 'not measured'
}
};
} 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: Awaited<ReturnType<typeof getSystemStatus>>): void {
output.writeln();
// Header with overall status
const statusIcon = status.running
? output.success('[RUNNING]')
: output.warning('[STOPPED]');
output.writeln(`${output.bold('RuFlo 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) {
if (status.mcp.transport === 'stdio') {
output.printInfo(' Running (stdio mode)');
} else {
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: string): string {
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: CommandContext): Promise<CommandResult> => {
const watch = ctx.flags.watch as boolean;
const interval = (ctx.flags.interval as number) || DEFAULT_WATCH_INTERVAL / 1000;
const healthCheck = ctx.flags['health-check'] as boolean;
const cwd = ctx.cwd;
// Check initialization
if (!isInitialized(cwd)) {
output.printError('RuFlo is not initialized in this directory');
output.printInfo('Run "ruflo 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: Awaited<ReturnType<typeof getSystemStatus>>
): Promise<CommandResult> {
output.writeln();
output.writeln(output.bold('Health Check'));
output.writeln();
const checks: Array<{ name: string; status: 'pass' | 'fail' | 'warn'; message: string }> = [];
// 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
? (status.mcp.transport === 'stdio' ? 'Running (stdio mode)' : `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: number): Promise<CommandResult> {
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: Command = {
name: 'agents',
description: 'Show detailed agent status',
action: async (ctx: CommandContext): Promise<CommandResult> => {
try {
const result = await callMCPTool<{
agents: Array<{
id: string;
type: string;
status: string;
task?: string;
uptime: number;
metrics: { tasksCompleted: number; successRate: number };
}>;
}>('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: Command = {
name: 'tasks',
description: 'Show detailed task status',
action: async (ctx: CommandContext): Promise<CommandResult> => {
try {
const result = await callMCPTool<{
tasks: Array<{
id: string;
type: string;
status: string;
priority: string;
agent?: string;
progress: number;
createdAt: string;
}>;
}>('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: Command = {
name: 'memory',
description: 'Show detailed memory status',
action: async (ctx: CommandContext): Promise<CommandResult> => {
try {
const result = await callMCPTool<{
backend: string;
entries: number;
size: number;
namespaces: Array<{ name: string; entries: number }>;
performance: {
avgSearchTime: number;
avgWriteTime: number;
cacheHitRate: number;
hnswEnabled: boolean;
};
v3Gains: {
searchImprovement: string;
memoryReduction: string;
};
}>('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: Command = {
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;