@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
716 lines ⢠30.2 kB
JavaScript
/**
* 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