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
JavaScript
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