@every-env/cli
Version:
Multi-agent orchestrator for AI-powered development workflows
142 lines • 5.36 kB
JavaScript
import pLimit from 'p-limit';
import { EventEmitter } from 'events';
import { ClaudeAgent } from '../agents/claude-agent.js';
import { GenericAgent } from '../agents/generic-agent.js';
import { LiquidEngine } from '../templates/liquid-engine.js';
import { logger } from '../utils/logger.js';
export class AgentManager extends EventEmitter {
maxConcurrent;
limiter;
results = [];
runningAgents = new Map();
liquidEngine = new LiquidEngine();
constructor(maxConcurrent = 5) {
super();
this.maxConcurrent = maxConcurrent;
this.limiter = pLimit(maxConcurrent);
}
async runBatch(tasks) {
logger.info(`Running ${tasks.length} agents (max ${this.maxConcurrent} concurrent)`);
logger.debug('Tasks to run:', tasks.map(t => ({
agentId: t.agent.id,
pattern: t.pattern.name,
outputPath: t.outputPath
})));
this.emit('batch:start', { total: tasks.length });
const promises = tasks.map(task => this.limiter(() => this.runAgent(task)));
const results = await Promise.allSettled(promises);
this.emit('batch:complete', {
total: tasks.length,
successful: results.filter(r => r.status === 'fulfilled').length,
failed: results.filter(r => r.status === 'rejected').length,
});
return this.results;
}
async runAgent(task) {
const { agent, variables, outputPath } = task;
logger.debug(`Starting agent ${agent.id}`, {
pattern: task.pattern.name,
outputPath,
variableKeys: Object.keys(variables)
});
this.emit('agent:start', { agentId: agent.id, outputPath });
try {
// Create agent instance
logger.debug(`Creating agent instance for ${agent.id}`, {
command: agent.command,
promptMode: agent.promptMode
});
const agentInstance = this.createAgent(agent);
this.runningAgents.set(agent.id, agentInstance);
// Subscribe to agent events
agentInstance.on('stdout', (data) => {
this.emit('agent:output', {
agentId: agent.id,
type: 'stdout',
data
});
});
agentInstance.on('stderr', (data) => {
this.emit('agent:output', {
agentId: agent.id,
type: 'stderr',
data
});
});
// Load and process prompt
logger.debug(`Loading prompt for ${agent.id}`);
const prompt = await this.loadPrompt(agent.promptFile, variables);
// Run the agent
logger.debug(`Running agent ${agent.id}`);
const result = await agentInstance.run(prompt, outputPath);
logger.debug(`Agent ${agent.id} completed`, {
status: result.status,
duration: result.duration
});
this.results.push(result);
this.runningAgents.delete(agent.id);
this.emit('agent:complete', {
agentId: agent.id,
status: result.status,
duration: result.duration,
});
return result;
}
catch (error) {
const errorResult = {
agentId: agent.id,
status: 'failed',
duration: 0,
stdout: [],
stderr: [],
error: error instanceof Error ? error.message : 'Unknown error',
};
this.results.push(errorResult);
this.runningAgents.delete(agent.id);
this.emit('agent:failed', {
agentId: agent.id,
error: errorResult.error,
});
return errorResult;
}
}
createAgent(config) {
// Factory pattern for different agent types
const options = {
workingDir: config.workingDir,
env: config.env,
stdio: config.stdio,
timeout: config.timeout,
};
logger.debug(`Creating agent with command: ${config.command}`, {
agentId: config.id,
options
});
switch (config.command) {
case 'claude':
return new ClaudeAgent(config, options);
default:
// For other tools, use generic agent
return new GenericAgent(config, options);
}
}
async loadPrompt(promptFile, variables) {
logger.debug(`Loading prompt template: ${promptFile}`);
logger.debug(`Template variables:`, variables);
const rendered = await this.liquidEngine.renderFile(promptFile, variables);
logger.debug(`Rendered prompt length: ${rendered.length} characters`);
return rendered;
}
async stopAll() {
const stopPromises = Array.from(this.runningAgents.entries()).map(async ([agentId, agent]) => {
logger.info(`Stopping agent: ${agentId}`);
await agent.stop();
});
await Promise.all(stopPromises);
this.runningAgents.clear();
}
getResults() {
return [...this.results];
}
}
//# sourceMappingURL=agent-manager.js.map