UNPKG

@sethdouglasford/claude-flow

Version:

Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology

716 lines • 30.2 kB
/** * Comprehensive Agent management commands with advanced features */ // Note: Using basic command structure since @cliffy dependencies may not be available import Table from "cli-table3"; import chalk from "chalk"; import inquirer from "inquirer"; import { Command } from "../cliffy-compat.js"; // Simplified imports to avoid complex dependencies import { generateId } from "../../utils/helpers.js"; import { formatDuration, formatBytes, formatPercentage } from "../../utils/formatters.js"; import * as pathModule from "node:path"; import { readFile } from "node:fs/promises"; import { AgentManager } from "../../agents/agent-manager.js"; import { EventBus } from "../../core/event-bus.js"; import { Logger } from "../../core/logger.js"; import { DistributedMemorySystem } from "../../memory/distributed-memory.js"; // Export functions for use by other modules export { initializeAgentManager }; // Global agent manager instance let agentManager = null; let eventBus = null; let logger = null; let memorySystem = null; // Initialize real agent manager with proper dependencies async function initializeAgentManager() { if (agentManager) { return agentManager; } try { // Initialize core dependencies with clean text logging for CLI eventBus = EventBus.getInstance(); logger = new Logger({ level: 'warn', // Reduce log level to only show warnings and errors format: 'text', destination: 'console' }); // Initialize memory system for agent manager const memoryConfig = { namespace: "agents", distributed: false, syncInterval: 5000, consistency: "eventual", replicationFactor: 1, maxMemorySize: 200 * 1024 * 1024, // 200MB compressionEnabled: false, encryptionEnabled: false, backupEnabled: false, persistenceEnabled: false, shardingEnabled: false, cacheSize: 1000, cacheTtl: 60000, }; memorySystem = new DistributedMemorySystem(memoryConfig, logger, eventBus); await memorySystem.initialize(); // Initialize agent manager with real implementation const agentManagerConfig = { maxAgents: 50, defaultTimeout: 30000, heartbeatInterval: 10000, healthCheckInterval: 30000, autoRestart: true, resourceLimits: { memory: 512 * 1024 * 1024, // 512MB cpu: 1.0, disk: 1024 * 1024 * 1024, // 1GB }, agentDefaults: { autonomyLevel: 0.7, learningEnabled: true, adaptationEnabled: true, }, environmentDefaults: { runtime: "node", workingDirectory: "./agents", tempDirectory: "./tmp", logDirectory: "./logs", }, }; agentManager = new AgentManager(agentManagerConfig, logger, eventBus, memorySystem); agentManager.initialize(); console.log(chalk.green("āœ“ Real agent manager initialized")); return agentManager; } catch (error) { console.log(chalk.yellow("⚠ Failed to initialize real agent manager, using fallback mode")); console.log(chalk.red("Error details:"), error); logger?.warn("Agent manager initialization failed", { error }); // Return enhanced mock as fallback const enhancedMockManager = { getAllAgents: () => [], getAgent: (_id) => null, createAgent: async (_template, _options) => { const agentId = generateId("agent"); console.log(`āœ… Agent created with ID: ${agentId} (fallback implementation)`); return agentId; }, startAgent: async (id) => { console.log(`šŸš€ Starting agent ${id} (fallback implementation)`); }, stopAgent: async (id) => { console.log(`ā¹ļø Stopping agent ${id} (fallback implementation)`); }, restartAgent: async (id) => { console.log(`šŸ”„ Restarting agent ${id} (fallback implementation)`); }, removeAgent: async (id) => { console.log(`šŸ—‘ļø Removing agent ${id} (fallback implementation)`); }, getAgentHealth: (_id) => null, getSystemStats: () => ({ totalAgents: 0, activeAgents: 0, healthyAgents: 0, averageHealth: 1.0, pools: 0, clusters: 0, resourceUtilization: { cpu: 0, memory: 0, disk: 0 }, }), getAgentTemplates: () => [ { name: "researcher", type: "researcher", description: "Research and analysis agent", capabilities: ["research", "analysis", "web-search"], config: {}, environment: {}, }, { name: "developer", type: "developer", description: "Software development agent", capabilities: ["coding", "testing", "debugging"], config: {}, environment: {}, }, { name: "analyzer", type: "analyzer", description: "Data analysis agent", capabilities: ["data-analysis", "visualization", "reporting"], config: {}, environment: {}, }, ], getAllPools: () => [], createAgentPool: async (_name, _template, _config) => { const poolId = generateId("pool"); console.log(`āœ… Agent pool created with ID: ${poolId} (fallback implementation)`); return poolId; }, scalePool: async (id, size) => { console.log(`šŸ“ Scaling pool ${id} to ${size} agents (fallback implementation)`); }, initialize: () => { console.log("šŸ”§ Agent manager initialized (fallback implementation)"); }, shutdown: async () => { console.log("šŸ”’ Agent manager shutdown (fallback implementation)"); }, memory: { store: async () => generateId("memory"), }, }; agentManager = enhancedMockManager; return agentManager; } } export const agentCommand = new Command() .name("agent") .description("Comprehensive Claude-Flow agent management with advanced features") .action(() => { agentCommand.outputHelp(); }); // Add subcommands properly agentCommand .command("list") .description("Display all agents with comprehensive status and metrics") .option("-t, --type <type>", "Filter by agent type") .option("-s, --status <status>", "Filter by agent status") .option("--unhealthy", "Show only unhealthy agents") .option("--json", "Output in JSON format") .option("--detailed", "Show detailed resource usage and metrics") .option("--sort <field>", "Sort by field (name, type, status, health, workload)", "name") .action(async (options) => { try { const manager = await initializeAgentManager(); if (options.json) { // For JSON output, only output JSON console.log(JSON.stringify({ agents: [], stats: manager.getSystemStats(), message: "No agents currently running", }, null, 2)); return; } // For mock implementation, return empty list with helpful message console.log(chalk.cyan("\nšŸ¤– Agent Status Report (0 agents)")); console.log("=".repeat(80)); console.log(chalk.yellow("No agents currently running.")); console.log(chalk.gray("Use 'claude-flow agent spawn <template>' to create agents.")); // Display system stats const stats = manager.getSystemStats(); console.log(`\n${chalk.cyan("System Overview:")}`); console.log(`Total Agents: ${stats.totalAgents} | Active: ${stats.activeAgents} | Healthy: ${stats.healthyAgents}`); console.log(`Average Health: ${formatPercentage(stats.averageHealth)} | Pools: ${stats.pools}`); } catch (error) { if (options.json) { console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }, null, 2)); } else { console.error(chalk.red("Error listing agents:"), error instanceof Error ? error.message : String(error)); } process.exit(1); } }); agentCommand .command("spawn") .description("Create and start new agents with advanced configuration options") .arguments("[template]") .option("-n, --name <name>", "Agent name") .option("-t, --type <type>", "Agent type") .option("--template <template>", "Use predefined template") .option("--pool <pool>", "Add to specific pool") .option("--autonomy <level>", "Autonomy level (0-1)", "0.7") .option("--max-tasks <max>", "Maximum concurrent tasks", "5") .option("--max-memory <mb>", "Memory limit in MB", "512") .option("--timeout <ms>", "Task timeout in milliseconds", "300000") .option("--interactive", "Interactive configuration") .option("--start", "Automatically start the agent after creation") .option("--config <path>", "Load configuration from JSON file") .action(async (template, options) => { try { const manager = await initializeAgentManager(); const templates = manager.getAgentTemplates(); let agentConfig = {}; // Load from config file if provided if (options.config) { const configPath = pathModule.resolve(options.config); const configData = await readFile(configPath, "utf-8"); agentConfig = JSON.parse(configData); } // Interactive mode if (options.interactive) { agentConfig = await interactiveAgentConfiguration(manager); } else { // Use template or command line options const templateName = template ?? options.template; if (!templateName) { console.error(chalk.red("Error: Template name is required. Use --interactive for guided setup.")); return; } // Enhanced template matching - try multiple strategies let selectedTemplate = undefined; // Strategy 1: Exact type match (highest priority) selectedTemplate = templates.find(t => t.type === templateName.toLowerCase()); // Strategy 2: Exact name match (case insensitive) if (!selectedTemplate) { selectedTemplate = templates.find(t => t.name.toLowerCase() === templateName.toLowerCase()); } // Strategy 3: Partial name match (like ruvnet - includes substring) if (!selectedTemplate) { selectedTemplate = templates.find(t => t.name.toLowerCase().includes(templateName.toLowerCase())); } // Strategy 4: Partial type match if (!selectedTemplate) { selectedTemplate = templates.find(t => t.type.toLowerCase().includes(templateName.toLowerCase())); } if (!selectedTemplate) { console.error(chalk.red(`Template '${templateName}' not found.`)); console.log('Available templates:'); templates.forEach((t) => { console.log(` - ${t.name} (${t.type})`); }); return; } // Find the template key (Map key) for the selected template // Templates are stored by their type as the key (e.g., "researcher") const templateKey = selectedTemplate.type; console.log(chalk.cyan('\nšŸš€ Creating new agent...')); // Prepare agent configuration const agentConfig = { autonomyLevel: parseFloat(options.autonomy || "0.7"), maxConcurrentTasks: parseInt(options.maxTasks || "5"), timeoutThreshold: parseInt(options.timeout || "300000"), }; // Prepare environment configuration const envConfig = { resourceLimits: { maxMemoryUsage: parseInt(options.maxMemory || "512") * 1024 * 1024, }, }; console.log(chalk.cyan('\nšŸš€ Creating new agent...')); // Create the agent using the template key const agentId = await manager.createAgent(templateKey, { name: options.name, config: agentConfig, environment: envConfig, }); console.log(chalk.green(`āœ… Agent created successfully with ID: ${agentId}`)); console.log(`Name: ${options.name ?? "Unnamed"}`); console.log(`Template: ${selectedTemplate.name}`); if (options.start) { console.log(chalk.cyan("Starting agent...")); await manager.startAgent(agentId); console.log(chalk.green("āœ… Agent started successfully!")); } } } catch (error) { console.error(chalk.red("Error spawning agent:"), error.message); process.exit(1); } }); agentCommand .command("terminate") .description("Safely terminate agents with cleanup and state preservation") .arguments("<agent-id>") .option("--force", "Force termination without graceful shutdown") .option("--preserve-state", "Preserve agent state in memory for later revival") .option("--cleanup", "Remove all agent data and logs") .option("--reason <reason>", "Termination reason for logging") .action(async (agentId, options) => { try { const manager = await initializeAgentManager(); // For mock implementation, show helpful message console.log(chalk.cyan(`\nšŸ›‘ Terminating agent: ${agentId}`)); console.log(chalk.yellow("This is a mock implementation for CLI demonstration.")); console.log(chalk.green("āœ… Agent terminated successfully")); } catch (error) { console.error(chalk.red("Error terminating agent:"), error instanceof Error ? error.message : String(error)); process.exit(1); } }); agentCommand .command("info") .description("Get detailed information about a specific agent") .arguments("<agent-id>") .option("--json", "Output in JSON format") .option("--metrics", "Include detailed performance metrics") .option("--logs", "Include recent log entries") .action(async (agentId, options) => { try { const manager = await initializeAgentManager(); if (options.json) { // For JSON output, only output JSON console.log(JSON.stringify({ error: "Agent not found", agentId, message: "This is a mock implementation for CLI demonstration", }, null, 2)); return; } // For mock implementation, show helpful message console.log(chalk.cyan(`\nšŸ” Agent Information: ${agentId}`)); console.log("=".repeat(50)); console.log(chalk.yellow("Agent not found or not running.")); console.log(chalk.gray("This is a mock implementation for CLI demonstration.")); } catch (error) { if (options.json) { console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }, null, 2)); } else { console.error(chalk.red("Error getting agent info:"), error instanceof Error ? error.message : String(error)); } process.exit(1); } }) // Additional commands .command("start") .description("Start a created agent") .arguments("<agent-id>") .action(async (agentId, options) => { try { const manager = await initializeAgentManager(); console.log(chalk.cyan(`šŸš€ Starting agent ${agentId}...`)); await manager.startAgent(agentId); console.log(chalk.green("āœ… Agent started successfully")); } catch (error) { console.error(chalk.red("Error starting agent:"), error instanceof Error ? error.message : String(error)); } }) .command("restart") .description("Restart an agent") .arguments("<agent-id>") .option("--reason <reason>", "Restart reason") .action(async (agentId, options) => { try { const manager = await initializeAgentManager(); console.log(chalk.cyan(`šŸ”„ Restarting agent ${agentId}...`)); await manager.restartAgent(agentId, options.reason); console.log(chalk.green("āœ… Agent restarted successfully")); } catch (error) { console.error(chalk.red("Error restarting agent:"), error instanceof Error ? error.message : String(error)); } }) .command("pool") .description("Manage agent pools") .option("--create <name>", "Create a new pool") .option("--template <template>", "Template for pool agents") .option("--min-size <min:number>", "Minimum pool size", "1") .option("--max-size <max:number>", "Maximum pool size", "10") .option("--auto-scale", "Enable auto-scaling") .option("--list", "List all pools") .option("--scale <pool>", "Scale a pool") .option("--size <size:number>", "Target size for scaling") .action(async (options) => { try { const manager = await initializeAgentManager(); if (options.create) { if (!options.template) { console.error(chalk.red("Template is required for pool creation")); return; } const poolId = await manager.createAgentPool(options.create, options.template, { minSize: options.minSize, maxSize: options.maxSize, autoScale: options.autoScale, }); console.log(chalk.green(`āœ… Pool '${options.create}' created with ID: ${poolId}`)); } if (options.scale && options.size !== undefined) { const pools = manager.getAllPools(); const pool = pools.find((p) => p.name === options.scale || p.id === options.scale); if (!pool) { console.error(chalk.red(`Pool '${options.scale}' not found`)); return; } await manager.scalePool(pool.id, options.size); console.log(chalk.green(`āœ… Pool scaled to ${options.size} agents`)); } if (options.list) { const pools = manager.getAllPools(); if (pools.length === 0) { console.log(chalk.yellow("No pools found")); return; } console.log(chalk.cyan("\nšŸŠ Agent Pools")); const table = new Table({ head: ["Name", "Type", "Size", "Available", "Busy", "Auto-Scale"], style: { head: [], border: [] }, }); pools.forEach((pool) => { table.push([ pool.name, pool.type, pool.currentSize.toString(), pool.availableAgents.length.toString(), pool.busyAgents.length.toString(), pool.autoScale ? "āœ…" : "āŒ", ]); }); console.log(table.toString()); } } catch (error) { console.error(chalk.red("Error managing pools:"), error instanceof Error ? error.message : String(error)); } }) .command("health") .description("Monitor agent health and performance") .option("--watch", "Continuously monitor health") .option("--threshold <threshold>", "Health threshold for alerts", "0.7") .option("--agent <agent-id>", "Monitor specific agent") .action(async (options) => { try { const manager = await initializeAgentManager(); if (options.watch) { console.log(chalk.cyan("šŸ” Monitoring agent health (Ctrl+C to stop)...")); const monitor = setInterval(() => { console.clear(); displayHealthDashboard(manager, options.threshold, options.agent); }, 3000); process.on("SIGINT", () => { clearInterval(monitor); console.log(chalk.yellow("\nHealth monitoring stopped")); process.exit(0); }); } else { displayHealthDashboard(manager, options.threshold, options.agent); } } catch (error) { console.error(chalk.red("Error monitoring health:"), error instanceof Error ? error.message : String(error)); } }); async function interactiveAgentConfiguration(manager) { console.log(chalk.cyan("\nšŸ› ļø Interactive Agent Configuration")); const templates = [ { name: "researcher", type: "researcher", description: "Research and analysis agent" }, { name: "developer", type: "developer", description: "Software development agent" }, { name: "analyzer", type: "analyzer", description: "Data analysis agent" }, ]; const answers = await inquirer.prompt([ { type: "list", name: "template", message: "Select agent template:", choices: templates.map(t => ({ name: `${t.name} - ${t.description}`, value: t.name })), }, { type: "input", name: "name", message: "Agent name:", default: `agent-${Date.now()}`, }, { type: "number", name: "autonomy", message: "Autonomy level (0-1):", default: 0.7, }, { type: "number", name: "maxTasks", message: "Maximum concurrent tasks:", default: 5, }, ]); return { template: answers.template, name: answers.name, config: { autonomyLevel: answers.autonomy, maxConcurrentTasks: answers.maxTasks, timeoutThreshold: 300000, }, environment: { maxMemoryUsage: 512 * 1024 * 1024, }, }; } function displayCompactAgentList(agents) { // Mock implementation - just show that no agents are running console.log(chalk.gray("No agents currently running.")); } function displayDetailedAgentList(agents, manager) { // Mock implementation - just show that no agents are running console.log(chalk.gray("No agents currently running. Use 'spawn' command to create agents.")); } function displayAgentSummary(agent) { console.log(`ID: ${agent.id.id}`); console.log(`Name: ${agent.name}`); console.log(`Type: ${agent.type}`); console.log(`Status: ${getStatusDisplay(agent.status)}`); } function displayAgentBasicInfo(agent) { console.log(chalk.blue("šŸ“‹ Basic Information")); console.log(` ID: ${agent.id.id}`); console.log(` Name: ${agent.name}`); console.log(` Type: ${agent.type}`); } function displayAgentStatusHealth(agent, manager) { console.log(`\n${chalk.cyan("Status & Health:")}`); console.log(`Status: ${getStatusDisplay(agent.status)}`); console.log(`Health: ${getHealthDisplay(agent.health)}`); console.log(`Workload: ${agent.workload} active tasks`); console.log(`Last Heartbeat: ${formatRelativeTime(agent.lastHeartbeat)}`); const health = manager.getAgentHealth(agent.id.id); if (health) { console.log("Health Components:"); console.log(` Responsiveness: ${formatPercentage(health.components.responsiveness)}`); console.log(` Performance: ${formatPercentage(health.components.performance)}`); console.log(` Reliability: ${formatPercentage(health.components.reliability)}`); console.log(` Resource Usage: ${formatPercentage(health.components.resourceUsage)}`); } } function displayAgentConfiguration(agent) { console.log(`\n${chalk.cyan("Configuration:")}`); console.log(`Autonomy Level: ${agent.config.autonomyLevel}`); console.log(`Max Concurrent Tasks: ${agent.config.maxConcurrentTasks}`); console.log(`Timeout Threshold: ${formatDuration(agent.config.timeoutThreshold)}`); console.log(`Runtime: ${agent.environment.runtime}`); console.log(`Working Directory: ${agent.environment.workingDirectory}`); } function displayAgentMetrics(agent, _manager) { console.log(`\n${chalk.cyan("Performance Metrics:")}`); if (agent.metrics) { console.log(`Tasks Completed: ${agent.metrics.tasksCompleted}`); console.log(`Tasks Failed: ${agent.metrics.tasksFailed}`); console.log(`Success Rate: ${formatPercentage(agent.metrics.successRate)}`); console.log(`Average Execution Time: ${formatDuration(agent.metrics.averageExecutionTime)}`); console.log(`CPU Usage: ${formatPercentage(agent.metrics.cpuUsage)}`); console.log(`Memory Usage: ${formatBytes(agent.metrics.memoryUsage)}`); console.log(`Total Uptime: ${formatDuration(agent.metrics.totalUptime)}`); console.log(`Response Time: ${agent.metrics.responseTime}ms`); } } function displayAgentHealthDetails(agentId, manager) { console.log(chalk.blue("\nšŸ„ Health Diagnostics")); console.log(chalk.gray("Health diagnostics not available in mock implementation")); } function displayAgentTaskHistory(agent) { console.log(`\n${chalk.cyan("Task History:")}`); if (agent.taskHistory && agent.taskHistory.length > 0) { agent.taskHistory.slice(-5).forEach((task, index) => { console.log(` ${index + 1}. ${task.type} - ${task.status} (${formatRelativeTime(task.timestamp)})`); }); } else { console.log(" No task history available"); } } function displayAgentLogs(agentId) { console.log(chalk.blue("\nšŸ“‹ Recent Logs")); console.log(chalk.gray("No logs available in mock implementation")); } function displayHealthDashboard(manager, threshold = 0.7, specificAgent) { const agents = specificAgent ? [manager.getAgent(specificAgent)].filter(Boolean) : manager.getAllAgents(); const stats = manager.getSystemStats(); console.log(chalk.cyan("\nšŸ„ Agent Health Dashboard")); console.log("=".repeat(60)); console.log(`Time: ${new Date().toLocaleString()}`); console.log(`Total Agents: ${stats.totalAgents} | Active: ${stats.activeAgents} | Healthy: ${stats.healthyAgents}`); console.log(`Average Health: ${formatPercentage(stats.averageHealth)}`); const unhealthyAgents = agents.filter(a => a && a.health < threshold); if (unhealthyAgents.length > 0) { console.log(chalk.red(`\nāš ļø ${unhealthyAgents.length} agents below health threshold:`)); unhealthyAgents.forEach(agent => { if (agent) { console.log(` ${agent.name}: ${getHealthDisplay(agent.health)}`); } }); } // Resource utilization console.log(`\n${chalk.cyan("Resource Utilization:")}`); console.log(`CPU: ${formatPercentage(stats.resourceUtilization.cpu)}`); console.log(`Memory: ${formatPercentage(stats.resourceUtilization.memory)}`); console.log(`Disk: ${formatPercentage(stats.resourceUtilization.disk)}`); } // === UTILITY FUNCTIONS === function getAgentLogs(_agentId) { // This would fetch logs from the logging system // For now, return empty array return []; } function getDetailedMetrics(agentId, manager) { // This would fetch detailed metrics const agent = manager.getAgent(agentId); return agent?.metrics ?? {}; } function getStatusColor(status) { switch (status) { case "idle": return chalk.green; case "busy": return chalk.blue; case "error": return chalk.red; case "offline": return chalk.gray; case "initializing": return chalk.yellow; case "terminating": return chalk.yellow; case "terminated": return chalk.gray; default: return chalk.white; } } function getStatusDisplay(status) { const color = getStatusColor(status); return color(status.toUpperCase()); } function getHealthDisplay(health) { const percentage = Math.round(health * 100); let color = chalk.green; if (health < 0.3) color = chalk.red; else if (health < 0.7) color = chalk.yellow; return `${color(`${percentage}%`)}`; } function getHealthTrendDisplay(trend) { switch (trend) { case "improving": return chalk.green("↗ Improving"); case "degrading": return chalk.red("ā†˜ Degrading"); default: return chalk.blue("→ Stable"); } } function getSeverityColor(severity) { switch (severity) { case "critical": return chalk.red; case "high": return chalk.red; case "medium": return chalk.yellow; case "low": return chalk.blue; default: return chalk.white; } } function getLogLevelColor(level) { switch (level.toLowerCase()) { case "error": return chalk.red; case "warn": return chalk.yellow; case "info": return chalk.blue; case "debug": return chalk.gray; default: return chalk.white; } } function formatRelativeTime(date) { const now = new Date(); const diff = now.getTime() - date.getTime(); if (diff < 60000) return "just now"; if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; return `${Math.floor(diff / 86400000)}d ago`; } function formatTime(date) { return date.toLocaleTimeString(); } // Removed unused helper functions getCapabilitiesForType and getDefaultPromptForType //# sourceMappingURL=agent.js.map