UNPKG

claude-agents-manager

Version:

Elite AI research and development platform with 60+ specialized agents, comprehensive research workflows, citation-backed reports, and advanced multi-agent coordination for Claude Code. Features deep research capabilities, concurrent execution, shared mem

208 lines (181 loc) 6.5 kB
import chalk from "chalk"; import ora from "ora"; import { writeFileSync, copyFileSync, existsSync, mkdirSync } from "fs"; import { join, dirname } from "path"; import { fileURLToPath } from "url"; import { getAgentsDir, getCommandsDir, ensureDirectories, ensureProjectDirectories, } from "../utils/paths.js"; import { selectAgents, confirmAction, selectInstallScope, selectHookOptions, } from "../utils/prompts.js"; import { addInstalledAgent, getInstalledAgents } from "../utils/config.js"; import { getAvailableAgents, getAgentDetails, formatAgentForInstall, } from "../utils/agents.js"; import { detectContextForge } from "../utils/contextForgeDetector.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export async function installCommand(options) { const spinner = ora(); try { // Detect context-forge project const contextForgeInfo = detectContextForge(); if (contextForgeInfo.hasContextForge) { console.log(chalk.cyan("🛠️ Context-Forge project detected!")); console.log( chalk.gray( " Sub-agents will be integrated with your existing setup.\n", ), ); } // Ensure directories exist ensureDirectories(); // Get available agents spinner.start("Loading available agents..."); const availableAgents = getAvailableAgents(); spinner.stop(); if (availableAgents.length === 0) { console.log(chalk.yellow("No agents available to install.")); return; } // Get already installed agents const installedAgents = getInstalledAgents(); const installedNames = Object.keys(installedAgents); // Filter out already installed agents const installableAgents = availableAgents.filter( (agent) => !installedNames.includes(agent.name), ); if (installableAgents.length === 0) { console.log(chalk.yellow("All available agents are already installed.")); console.log( chalk.gray('Use "claude-agents list" to see installed agents.'), ); return; } // Select agents to install let selectedAgents; if (options.all) { selectedAgents = installableAgents.map((a) => a.name); } else { selectedAgents = await selectAgents(installableAgents); } if (selectedAgents.length === 0) { console.log(chalk.yellow("No agents selected for installation.")); return; } // Select installation scope const scope = options.project ? "project" : await selectInstallScope(); const isProject = scope === "project"; if (isProject) { ensureProjectDirectories(); } const agentsDir = getAgentsDir(isProject); const commandsDir = getCommandsDir(isProject); // Confirm installation const confirmMessage = `Install ${selectedAgents.length} agent(s) to ${scope} directory?`; if (!(await confirmAction(confirmMessage))) { console.log(chalk.yellow("Installation cancelled.")); return; } // Install each selected agent console.log(""); for (const agentName of selectedAgents) { spinner.start(`Installing ${chalk.bold(agentName)}...`); try { const agentDetails = getAgentDetails(agentName); if (!agentDetails) { spinner.fail(`Failed to load agent ${agentName}`); continue; } // Write agent file const agentPath = join(agentsDir, `${agentName}.md`); const formattedContent = formatAgentForInstall(agentDetails); writeFileSync(agentPath, formattedContent); // Copy associated slash commands if they exist if (agentDetails.commands && agentDetails.commands.length > 0) { // If context-forge project, put commands in sub-agents folder to avoid conflicts const targetCommandsDir = contextForgeInfo.hasContextForge && isProject ? join(commandsDir, "agents") : commandsDir; // Ensure sub-agents command directory exists if ( contextForgeInfo.hasContextForge && isProject && !existsSync(targetCommandsDir) ) { mkdirSync(targetCommandsDir, { recursive: true }); } for (const command of agentDetails.commands) { const srcPath = join( __dirname, "..", "..", "commands", `${command}.md`, ); if (existsSync(srcPath)) { // Prefix command name if in context-forge project to avoid conflicts const commandName = contextForgeInfo.hasContextForge && isProject ? `agent-${command}.md` : `${command}.md`; const destPath = join(targetCommandsDir, commandName); copyFileSync(srcPath, destPath); } } } // Add to config addInstalledAgent(agentName, agentDetails, isProject); spinner.succeed(`Installed ${chalk.bold(agentName)}`); // Ask about hooks configuration if (agentDetails.hooks?.recommended || agentDetails.hooks?.optional) { const hooks = await selectHookOptions(); if (hooks && hooks.length > 0) { console.log( chalk.gray(` Configure hooks manually in your settings.json`), ); } } } catch (error) { spinner.fail(`Failed to install ${agentName}: ${error.message}`); } } console.log(""); console.log(chalk.green("✓ Installation complete!")); console.log( chalk.gray('Use "claude-agents list" to see your installed agents.'), ); console.log( chalk.gray( 'Agents are automatically enabled. Use "claude-agents disable <agent>" to disable.', ), ); if (contextForgeInfo.hasContextForge && isProject) { console.log(""); console.log(chalk.cyan("📝 Context-Forge Integration:")); console.log( chalk.gray( " • Agent commands are in .claude/commands/agents/ to avoid conflicts", ), ); console.log(chalk.gray(" • Agents can work with your existing PRPs")); console.log( chalk.gray(' • Use Task("agent-name: description") in Claude Code'), ); } } catch (error) { spinner.fail("Installation failed"); console.error(chalk.red("Error:"), error.message); process.exit(1); } }