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

819 lines 31.8 kB
/** * V3 CLI Agent Command * Agent management commands for spawning, listing, and controlling agents */ import { output } from '../output.js'; import { select, confirm, input } from '../prompt.js'; import { callMCPTool, MCPClientError } from '../mcp-client.js'; // Available agent types with descriptions const AGENT_TYPES = [ { value: 'coder', label: 'Coder', hint: 'Code development with neural patterns' }, { value: 'researcher', label: 'Researcher', hint: 'Research with web access and data analysis' }, { value: 'tester', label: 'Tester', hint: 'Comprehensive testing with automation' }, { value: 'reviewer', label: 'Reviewer', hint: 'Code review with security and quality checks' }, { value: 'architect', label: 'Architect', hint: 'System design with enterprise patterns' }, { value: 'coordinator', label: 'Coordinator', hint: 'Multi-agent orchestration and workflow' }, { value: 'analyst', label: 'Analyst', hint: 'Performance analysis and optimization' }, { value: 'optimizer', label: 'Optimizer', hint: 'Performance optimization and bottleneck analysis' }, { value: 'security-architect', label: 'Security Architect', hint: 'Security architecture and threat modeling' }, { value: 'security-auditor', label: 'Security Auditor', hint: 'CVE remediation and security testing' }, { value: 'memory-specialist', label: 'Memory Specialist', hint: 'AgentDB unification (150x-12,500x faster)' }, { value: 'swarm-specialist', label: 'Swarm Specialist', hint: 'Unified coordination engine' }, { value: 'performance-engineer', label: 'Performance Engineer', hint: '2.49x-7.47x optimization targets' }, { value: 'core-architect', label: 'Core Architect', hint: 'Domain-driven design restructure' }, { value: 'test-architect', label: 'Test Architect', hint: 'TDD London School methodology' } ]; // Agent spawn subcommand const spawnCommand = { name: 'spawn', description: 'Spawn a new agent', options: [ { name: 'type', short: 't', description: 'Agent type to spawn', type: 'string', choices: AGENT_TYPES.map(a => a.value) }, { name: 'name', short: 'n', description: 'Agent name/identifier', type: 'string' }, { name: 'provider', short: 'p', description: 'Provider to use (anthropic, openrouter, ollama)', type: 'string', default: 'anthropic' }, { name: 'model', short: 'm', description: 'Model to use', type: 'string' }, { name: 'task', description: 'Initial task for the agent', type: 'string' }, { name: 'timeout', description: 'Agent timeout in seconds', type: 'number', default: 300 }, { name: 'auto-tools', description: 'Enable automatic tool usage', type: 'boolean', default: true } ], examples: [ { command: 'claude-flow agent spawn --type coder --name bot-1', description: 'Spawn a coder agent' }, { command: 'claude-flow agent spawn -t researcher --task "Research React 19"', description: 'Spawn researcher with task' } ], action: async (ctx) => { let agentType = ctx.flags.type; let agentName = ctx.flags.name; // Interactive mode if type not specified if (!agentType && ctx.interactive) { agentType = await select({ message: 'Select agent type:', options: AGENT_TYPES }); } if (!agentType) { output.printError('Agent type is required. Use --type or -t flag.'); return { success: false, exitCode: 1 }; } // Generate name if not provided if (!agentName) { agentName = `${agentType}-${Date.now().toString(36)}`; } output.printInfo(`Spawning ${agentType} agent: ${output.highlight(agentName)}`); try { // Call MCP tool to spawn agent const result = await callMCPTool('agent_spawn', { agentType, id: agentName, config: { provider: ctx.flags.provider || 'anthropic', model: ctx.flags.model, task: ctx.flags.task, timeout: ctx.flags.timeout, autoTools: ctx.flags.autoTools, }, priority: 'normal', metadata: { name: agentName, capabilities: getAgentCapabilities(agentType), }, }); output.writeln(); output.printTable({ columns: [ { key: 'property', header: 'Property', width: 15 }, { key: 'value', header: 'Value', width: 40 } ], data: [ { property: 'ID', value: result.agentId }, { property: 'Type', value: result.agentType }, { property: 'Name', value: agentName }, { property: 'Status', value: result.status }, { property: 'Created', value: result.createdAt }, { property: 'Capabilities', value: getAgentCapabilities(agentType).join(', ') } ] }); output.writeln(); output.printSuccess(`Agent ${agentName} spawned successfully`); if (ctx.flags.format === 'json') { output.printJson(result); } return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Failed to spawn agent: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Agent list subcommand const listCommand = { name: 'list', aliases: ['ls'], description: 'List all active agents', options: [ { name: 'all', short: 'a', description: 'Include inactive agents', type: 'boolean', default: false }, { name: 'type', short: 't', description: 'Filter by agent type', type: 'string' }, { name: 'status', short: 's', description: 'Filter by status', type: 'string' } ], action: async (ctx) => { try { // Call MCP tool to list agents const result = await callMCPTool('agent_list', { status: ctx.flags.all ? 'all' : ctx.flags.status || undefined, agentType: ctx.flags.type || undefined, limit: 100, }); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); output.writeln(output.bold('Active Agents')); output.writeln(); if (result.agents.length === 0) { output.printInfo('No agents found matching criteria'); return { success: true, data: result }; } // Format for display const displayAgents = result.agents.map(agent => ({ id: agent.id, type: agent.agentType, status: agent.status, created: new Date(agent.createdAt).toLocaleTimeString(), lastActivity: agent.lastActivityAt ? new Date(agent.lastActivityAt).toLocaleTimeString() : 'N/A', })); output.printTable({ columns: [ { key: 'id', header: 'ID', width: 20 }, { key: 'type', header: 'Type', width: 15 }, { key: 'status', header: 'Status', width: 12, format: formatStatus }, { key: 'created', header: 'Created', width: 12 }, { key: 'lastActivity', header: 'Last Activity', width: 12 } ], data: displayAgents }); output.writeln(); output.printInfo(`Total: ${result.total} agents`); return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Failed to list agents: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Agent status subcommand const statusCommand = { name: 'status', description: 'Show detailed status of an agent', options: [ { name: 'id', description: 'Agent ID', type: 'string' } ], action: async (ctx) => { let agentId = ctx.args[0] || ctx.flags.id; if (!agentId && ctx.interactive) { agentId = await input({ message: 'Enter agent ID:', validate: (v) => v.length > 0 || 'Agent ID is required' }); } if (!agentId) { output.printError('Agent ID is required'); return { success: false, exitCode: 1 }; } try { // Call MCP tool to get agent status const status = await callMCPTool('agent_status', { agentId, includeMetrics: true, includeHistory: false, }); if (ctx.flags.format === 'json') { output.printJson(status); return { success: true, data: status }; } output.writeln(); output.printBox([ `Type: ${status.agentType}`, `Status: ${formatStatus(status.status)}`, `Created: ${new Date(status.createdAt).toLocaleString()}`, `Last Activity: ${status.lastActivityAt ? new Date(status.lastActivityAt).toLocaleString() : 'N/A'}` ].join('\n'), `Agent: ${status.id}`); if (status.metrics) { output.writeln(); output.writeln(output.bold('Metrics')); const avgExecTime = status.metrics.averageExecutionTime ?? 0; const uptime = status.metrics.uptime ?? 0; output.printTable({ columns: [ { key: 'metric', header: 'Metric', width: 25 }, { key: 'value', header: 'Value', width: 15, align: 'right' } ], data: [ { metric: 'Tasks Completed', value: status.metrics.tasksCompleted ?? 0 }, { metric: 'Tasks In Progress', value: status.metrics.tasksInProgress ?? 0 }, { metric: 'Tasks Failed', value: status.metrics.tasksFailed ?? 0 }, { metric: 'Avg Execution Time', value: `${avgExecTime.toFixed(2)}ms` }, { metric: 'Uptime', value: `${(uptime / 1000 / 60).toFixed(1)}m` } ] }); } return { success: true, data: status }; } 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 }; } } }; // Agent stop subcommand const stopCommand = { name: 'stop', aliases: ['kill'], description: 'Stop a running agent', options: [ { name: 'force', short: 'f', description: 'Force stop without graceful shutdown', type: 'boolean', default: false }, { name: 'timeout', description: 'Graceful shutdown timeout in seconds', type: 'number', default: 30 } ], action: async (ctx) => { const agentId = ctx.args[0]; if (!agentId) { output.printError('Agent ID is required'); return { success: false, exitCode: 1 }; } const force = ctx.flags.force; if (!force && ctx.interactive) { const confirmed = await confirm({ message: `Are you sure you want to stop agent ${agentId}?`, default: false }); if (!confirmed) { output.printInfo('Operation cancelled'); return { success: true }; } } output.printInfo(`Stopping agent ${agentId}...`); try { // Call MCP tool to terminate agent const result = await callMCPTool('agent_terminate', { agentId, graceful: !force, reason: 'Stopped by user via CLI', }); if (!force) { output.writeln(output.dim(' Completing current task...')); output.writeln(output.dim(' Saving state...')); output.writeln(output.dim(' Releasing resources...')); } output.printSuccess(`Agent ${agentId} stopped successfully`); if (ctx.flags.format === 'json') { output.printJson(result); } return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Failed to stop agent: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Agent metrics subcommand const metricsCommand = { name: 'metrics', description: 'Show agent performance metrics', options: [ { name: 'period', short: 'p', description: 'Time period (1h, 24h, 7d, 30d)', type: 'string', default: '24h' } ], action: async (ctx) => { const agentId = ctx.args[0]; const period = ctx.flags.period; // Default metrics (updated by MCP agent/metrics when available) const metrics = { period, summary: { totalAgents: 4, activeAgents: 3, tasksCompleted: 127, avgSuccessRate: '96.2%', totalTokens: 1234567, avgResponseTime: '1.45s' }, byType: [ { type: 'coder', count: 2, tasks: 45, successRate: '97%' }, { type: 'researcher', count: 1, tasks: 32, successRate: '95%' }, { type: 'tester', count: 1, tasks: 50, successRate: '98%' } ], performance: { flashAttention: '2.8x speedup', memoryReduction: '52%', searchImprovement: '150x faster' } }; if (ctx.flags.format === 'json') { output.printJson(metrics); return { success: true, data: metrics }; } output.writeln(); output.writeln(output.bold(`Agent Metrics (${period})`)); output.writeln(); output.printTable({ columns: [ { key: 'metric', header: 'Metric', width: 20 }, { key: 'value', header: 'Value', width: 15, align: 'right' } ], data: [ { metric: 'Total Agents', value: metrics.summary.totalAgents }, { metric: 'Active Agents', value: metrics.summary.activeAgents }, { metric: 'Tasks Completed', value: metrics.summary.tasksCompleted }, { metric: 'Success Rate', value: metrics.summary.avgSuccessRate }, { metric: 'Total Tokens', value: metrics.summary.totalTokens.toLocaleString() }, { metric: 'Avg Response Time', value: metrics.summary.avgResponseTime } ] }); output.writeln(); output.writeln(output.bold('By Agent Type')); output.printTable({ columns: [ { key: 'type', header: 'Type', width: 12 }, { key: 'count', header: 'Count', width: 8, align: 'right' }, { key: 'tasks', header: 'Tasks', width: 8, align: 'right' }, { key: 'successRate', header: 'Success', width: 10, align: 'right' } ], data: metrics.byType }); output.writeln(); output.writeln(output.bold('V3 Performance Gains')); output.printList([ `Flash Attention: ${output.success(metrics.performance.flashAttention)}`, `Memory Reduction: ${output.success(metrics.performance.memoryReduction)}`, `Search: ${output.success(metrics.performance.searchImprovement)}` ]); return { success: true, data: metrics }; } }; // Agent pool subcommand const poolCommand = { name: 'pool', description: 'Manage agent pool for scaling', options: [ { name: 'size', short: 's', description: 'Pool size', type: 'number' }, { name: 'min', description: 'Minimum pool size', type: 'number', default: 1 }, { name: 'max', description: 'Maximum pool size', type: 'number', default: 10 }, { name: 'auto-scale', short: 'a', description: 'Enable auto-scaling', type: 'boolean', default: true } ], examples: [ { command: 'claude-flow agent pool --size 5', description: 'Set pool size' }, { command: 'claude-flow agent pool --min 2 --max 15', description: 'Configure auto-scaling' } ], action: async (ctx) => { try { const result = await callMCPTool('agent_pool', { size: ctx.flags.size, min: ctx.flags.min, max: ctx.flags.max, autoScale: ctx.flags.autoScale ?? true, }); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); const utilization = result.utilization ?? 0; output.printBox([ `Pool ID: ${result.poolId ?? 'default'}`, `Current Size: ${result.currentSize ?? 0}`, `Min/Max: ${result.minSize ?? 0}/${result.maxSize ?? 100}`, `Auto-Scale: ${result.autoScale ? 'Yes' : 'No'}`, `Utilization: ${(utilization * 100).toFixed(1)}%` ].join('\n'), 'Agent Pool'); const agents = result.agents ?? []; if (agents.length > 0) { output.writeln(); output.writeln(output.bold('Pool Agents')); output.printTable({ columns: [ { key: 'id', header: 'ID', width: 20 }, { key: 'type', header: 'Type', width: 15 }, { key: 'status', header: 'Status', width: 12, format: formatStatus } ], data: agents }); } return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Pool error: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Agent health subcommand const healthCommand = { name: 'health', description: 'Show agent health and metrics', options: [ { name: 'id', short: 'i', description: 'Agent ID (all if not specified)', type: 'string' }, { name: 'detailed', short: 'd', description: 'Show detailed health metrics', type: 'boolean', default: false }, { name: 'watch', short: 'w', description: 'Watch mode (refresh every 5s)', type: 'boolean', default: false } ], examples: [ { command: 'claude-flow agent health', description: 'Show all agents health' }, { command: 'claude-flow agent health -i agent-001 -d', description: 'Detailed health for specific agent' } ], action: async (ctx) => { const agentId = ctx.args[0] || ctx.flags.id; const detailed = ctx.flags.detailed; try { const result = await callMCPTool('agent_health', { agentId, detailed, }); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); output.writeln(output.bold('Agent Health')); output.writeln(); // Overall summary with null checks const overall = result.overall ?? { healthy: 0, degraded: 0, unhealthy: 0, avgCpu: 0, avgMemory: 0 }; const avgCpu = overall.avgCpu ?? 0; const avgMemory = overall.avgMemory ?? 0; output.printBox([ `Healthy: ${output.success(String(overall.healthy ?? 0))}`, `Degraded: ${output.warning(String(overall.degraded ?? 0))}`, `Unhealthy: ${output.error(String(overall.unhealthy ?? 0))}`, `Avg CPU: ${avgCpu.toFixed(1)}%`, `Avg Memory: ${(avgMemory * 100).toFixed(1)}%` ].join(' | '), 'Overall Status'); const healthAgents = result.agents ?? []; output.writeln(); output.printTable({ columns: [ { key: 'id', header: 'Agent ID', width: 18 }, { key: 'type', header: 'Type', width: 12 }, { key: 'health', header: 'Health', width: 10, format: formatHealthStatus }, { key: 'cpu', header: 'CPU %', width: 8, align: 'right', format: (v) => `${Number(v ?? 0).toFixed(1)}%` }, { key: 'memory', header: 'Memory', width: 10, align: 'right', format: (v) => { const mem = v; if (!mem) return '0%'; return `${(mem.used / mem.limit * 100).toFixed(0)}%`; } }, { key: 'tasks', header: 'Tasks', width: 12, align: 'right', format: (v) => { const t = v; if (!t) return '0/0'; return `${t.active ?? 0}/${t.completed ?? 0}`; } } ], data: healthAgents }); if (detailed && healthAgents.length > 0) { output.writeln(); output.writeln(output.bold('Detailed Metrics')); for (const agent of healthAgents) { output.writeln(); output.writeln(output.highlight(agent.id)); const uptime = agent.uptime ?? 0; const latency = agent.latency ?? { avg: 0, p99: 0 }; const tasks = agent.tasks ?? { completed: 0, failed: 0, queued: 0 }; const errors = agent.errors ?? { count: 0 }; output.printList([ `Uptime: ${(uptime / 1000 / 60).toFixed(1)} min`, `Latency: avg ${(latency.avg ?? 0).toFixed(1)}ms, p99 ${(latency.p99 ?? 0).toFixed(1)}ms`, `Tasks: ${tasks.completed ?? 0} completed, ${tasks.failed ?? 0} failed, ${tasks.queued ?? 0} queued`, `Errors: ${errors.count ?? 0}${errors.lastError ? ` (${errors.lastError})` : ''}` ]); } } return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Health check error: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; // Agent logs subcommand const logsCommand = { name: 'logs', description: 'Show agent activity logs', options: [ { name: 'id', short: 'i', description: 'Agent ID', type: 'string' }, { name: 'tail', short: 'n', description: 'Number of recent entries', type: 'number', default: 50 }, { name: 'level', short: 'l', description: 'Minimum log level', type: 'string', choices: ['debug', 'info', 'warn', 'error'], default: 'info' }, { name: 'follow', short: 'f', description: 'Follow log output', type: 'boolean', default: false }, { name: 'since', description: 'Show logs since (e.g., "1h", "30m")', type: 'string' } ], examples: [ { command: 'claude-flow agent logs -i agent-001', description: 'Show agent logs' }, { command: 'claude-flow agent logs -i agent-001 -f', description: 'Follow agent logs' }, { command: 'claude-flow agent logs -l error --since 1h', description: 'Show errors from last hour' } ], action: async (ctx) => { const agentId = ctx.args[0] || ctx.flags.id; const tail = ctx.flags.tail; const level = ctx.flags.level; if (!agentId) { output.printError('Agent ID is required. Use --id or -i'); return { success: false, exitCode: 1 }; } try { const result = await callMCPTool('agent_logs', { agentId, tail, level, since: ctx.flags.since, }); if (ctx.flags.format === 'json') { output.printJson(result); return { success: true, data: result }; } output.writeln(); output.writeln(output.bold(`Logs for ${agentId}`)); output.writeln(output.dim(`Showing ${result.entries.length} of ${result.total} entries`)); output.writeln(); for (const entry of result.entries) { const time = new Date(entry.timestamp).toLocaleTimeString(); const levelStr = formatLogLevel(entry.level); output.writeln(`${output.dim(time)} ${levelStr} ${entry.message}`); if (entry.context && Object.keys(entry.context).length > 0) { output.writeln(output.dim(` ${JSON.stringify(entry.context)}`)); } } return { success: true, data: result }; } catch (error) { if (error instanceof MCPClientError) { output.printError(`Logs error: ${error.message}`); } else { output.printError(`Unexpected error: ${String(error)}`); } return { success: false, exitCode: 1 }; } } }; function formatHealthStatus(health) { const h = String(health); switch (h) { case 'healthy': return output.success(h); case 'degraded': return output.warning(h); case 'unhealthy': return output.error(h); default: return h; } } function formatLogLevel(level) { switch (level) { case 'debug': return output.dim('[DEBUG]'); case 'info': return '[INFO] '; case 'warn': return output.warning('[WARN] '); case 'error': return output.error('[ERROR]'); default: return `[${level.toUpperCase()}]`; } } // Main agent command export const agentCommand = { name: 'agent', description: 'Agent management commands', subcommands: [spawnCommand, listCommand, statusCommand, stopCommand, metricsCommand, poolCommand, healthCommand, logsCommand], options: [], examples: [ { command: 'claude-flow agent spawn -t coder', description: 'Spawn a coder agent' }, { command: 'claude-flow agent list', description: 'List all agents' }, { command: 'claude-flow agent status agent-001', description: 'Show agent status' } ], action: async (ctx) => { // Show help if no subcommand output.writeln(); output.writeln(output.bold('Agent Management Commands')); output.writeln(); output.writeln('Usage: claude-flow agent <subcommand> [options]'); output.writeln(); output.writeln('Subcommands:'); output.printList([ `${output.highlight('spawn')} - Spawn a new agent`, `${output.highlight('list')} - List all active agents`, `${output.highlight('status')} - Show detailed agent status`, `${output.highlight('stop')} - Stop a running agent`, `${output.highlight('metrics')} - Show agent metrics` ]); output.writeln(); output.writeln('Run "claude-flow agent <subcommand> --help" for subcommand help'); return { success: true }; } }; // Helper functions function getAgentCapabilities(type) { const capabilities = { coder: ['code-generation', 'refactoring', 'debugging', 'testing'], researcher: ['web-search', 'data-analysis', 'summarization', 'citation'], tester: ['unit-testing', 'integration-testing', 'coverage-analysis', 'automation'], reviewer: ['code-review', 'security-audit', 'quality-check', 'documentation'], architect: ['system-design', 'pattern-analysis', 'scalability', 'documentation'], coordinator: ['task-orchestration', 'agent-management', 'workflow-control'], 'security-architect': ['threat-modeling', 'security-patterns', 'compliance', 'audit'], 'memory-specialist': ['vector-search', 'agentdb', 'caching', 'optimization'], 'performance-engineer': ['benchmarking', 'profiling', 'optimization', 'monitoring'] }; return capabilities[type] || ['general']; } function formatStatus(status) { const statusStr = String(status); switch (statusStr) { case 'active': return output.success(statusStr); case 'idle': return output.warning(statusStr); case 'inactive': case 'stopped': return output.dim(statusStr); case 'error': return output.error(statusStr); default: return statusStr; } } export default agentCommand; //# sourceMappingURL=agent.js.map