UNPKG

mcp-subagents

Version:

Multi-Agent AI Orchestration via Model Context Protocol - Access specialized CLI AI agents (Aider, Qwen, Gemini, Goose, etc.) with intelligent fallback and configuration

185 lines 6.43 kB
import { exec as execCallback } from 'child_process'; import { promisify } from 'util'; const exec = promisify(execCallback); export class AgentDetector { configManager; constructor(configManager) { this.configManager = configManager; } async detectAllAgents() { const agentCheckers = { qwen: () => this.detectQwen(), gemini: () => this.detectGemini(), aider: () => this.detectAider(), goose: () => this.detectGoose(), codex: () => this.detectCodex(), opencode: () => this.detectOpenCode(), claude: () => this.detectClaude() }; const results = await Promise.allSettled(Object.entries(agentCheckers).map(async ([name, checker]) => { try { return await checker(); } catch { return { name: name, available: false, authStatus: 'needs_setup', authInstructions: `Install ${name} first`, inputMethod: 'flag' }; } })); return results .filter((r) => r.status === 'fulfilled') .map(r => r.value); } async detectQwen() { await this.checkCommandExists('qwen'); const hasApiKey = !!this.getEffectiveEnvVar('qwen', 'OPENAI_API_KEY'); const authStatus = hasApiKey ? 'ready' : 'needs_api_key'; return { name: 'qwen', available: true, version: 'available', authStatus, authInstructions: authStatus === 'needs_api_key' ? 'Set OPENAI_API_KEY environment variable' : 'Ready to use', inputMethod: 'flag', autoAcceptFlag: '-y', modelFlag: '-m' }; } async detectGemini() { await this.checkCommandExists('gemini'); return { name: 'gemini', available: true, version: 'available', authStatus: 'ready', authInstructions: 'Uses existing Google Cloud authentication', inputMethod: 'flag', autoAcceptFlag: '-y', modelFlag: '-m' }; } async detectAider() { await this.checkCommandExists('aider'); const hasOpenAI = !!this.getEffectiveEnvVar('aider', 'OPENAI_API_KEY'); const hasAnthropic = !!this.getEffectiveEnvVar('aider', 'ANTHROPIC_API_KEY'); const authStatus = (hasOpenAI || hasAnthropic) ? 'ready' : 'needs_api_key'; return { name: 'aider', available: true, version: 'available', authStatus, authInstructions: authStatus === 'needs_api_key' ? 'Set OPENAI_API_KEY or ANTHROPIC_API_KEY environment variable' : 'Ready to use', inputMethod: 'flag', autoAcceptFlag: '--yes-always', modelFlag: '--model' }; } async detectGoose() { await this.checkCommandExists('goose'); return { name: 'goose', available: true, version: 'available', authStatus: 'ready', authInstructions: 'Uses existing provider authentication', inputMethod: 'flag', autoAcceptFlag: '--quiet', modelFlag: '--model', requiresSubcommand: 'run' }; } async detectCodex() { await this.checkCommandExists('codex'); // Try to detect if codex is already authenticated let authStatus = 'needs_login'; let authInstructions = 'Run: codex login'; try { // Quick test to see if codex is authenticated await exec('codex --help', { timeout: 3000 }); // If help works without error, assume it's ready (or try a better test) authStatus = 'ready'; authInstructions = ''; } catch { // Keep default needs_login status } return { name: 'codex', available: true, version: 'available', authStatus, authInstructions, inputMethod: 'positional', autoAcceptFlag: '--full-auto', modelFlag: '-m', requiresSubcommand: 'exec' }; } async detectOpenCode() { await this.checkCommandExists('opencode'); // Try to detect if opencode is already authenticated let authStatus = 'needs_login'; let authInstructions = 'Run: opencode auth login'; try { // Quick test to see if opencode is authenticated await exec('opencode --help', { timeout: 3000 }); // If help works without error, assume it's ready (or try a better test) authStatus = 'ready'; authInstructions = ''; } catch { // Keep default needs_login status } return { name: 'opencode', available: true, version: 'available', authStatus, authInstructions, inputMethod: 'subcommand', modelFlag: '-m', requiresSubcommand: 'run' }; } async detectClaude() { await this.checkCommandExists('claude'); // Claude is built into Claude Code and auto-authenticated when running inside it return { name: 'claude', available: true, version: 'available', authStatus: 'ready', authInstructions: '', // No auth needed - already authenticated inputMethod: 'stdin' }; } async checkCommandExists(command) { try { await exec(`which ${command}`, { timeout: 2000 }); } catch { throw new Error(`Command '${command}' not found`); } } getEffectiveEnvVar(agentName, envVar) { // First check process.env const processValue = process.env[envVar]; if (processValue) return processValue; // Then check config env vars if (this.configManager) { const agentConfig = this.configManager.getAgentConfig(agentName); return agentConfig.env?.[envVar]; } return undefined; } } //# sourceMappingURL=detection.js.map