@oliverpople/agency-x
Version:
🚀 **Transform feature requests into production-ready code in seconds**
156 lines (126 loc) • 5.07 kB
text/typescript
import * as dotenv from 'dotenv';
dotenv.config();
import { runOrchestrator } from '../orchestrator/orchestratorAgent';
import { speak } from '../utils/voice';
import { AGENT_DEFINITIONS } from '../configs/agentConfigs';
import { setDefaultProvider } from '../llm/llmRouter';
import chalk from 'chalk';
const args = process.argv.slice(2);
const getArgValue = (argName: string) => {
const argIndex = args.indexOf(argName);
return argIndex !== -1 && args[argIndex + 1] ? args[argIndex + 1] : null;
};
const showHelp = () => {
console.log(chalk.dim('agency-x') + chalk.bold(' - AI Orchestration System\n'));
console.log('Usage:');
console.log(chalk.cyan(' agency-x') + ' --feature ' + chalk.yellow('"description"'));
console.log(chalk.cyan(' agency-x') + ' --resume ' + chalk.yellow('SPEC-ID'));
console.log('');
console.log('Options:');
console.log(' --feature What to build');
console.log(' --resume Continue from session ID');
console.log(' --voice Enable audio narration');
console.log(' --concurrent Max parallel agents (default: 5)');
console.log(' --verbose Show detailed logs');
console.log(' --llm LLM provider (claude or openai, default: claude)');
console.log(' --help Show this help');
};
const feature = getArgValue('--feature');
const resume = getArgValue('--resume');
const concurrent = parseInt(getArgValue('--concurrent') || '5', 10);
const llmProvider = getArgValue('--llm') || 'claude';
const voice = args.includes('--voice');
const verbose = args.includes('--verbose');
const help = args.includes('--help');
// Validate LLM provider
if (!['claude', 'openai'].includes(llmProvider)) {
console.error(chalk.red('Invalid LLM provider. Use: claude or openai'));
process.exit(1);
}
// Set the LLM provider
setDefaultProvider(llmProvider as 'claude' | 'openai');
if (help) {
showHelp();
process.exit(0);
}
if (!feature && !resume) {
console.error(chalk.red('Missing required argument'));
console.log('Try: ' + chalk.cyan('agency-x --help'));
process.exit(1);
}
if (verbose) {
process.env.DEBUG = 'true';
}
let startTime = Date.now();
let isRunning = true;
const cleanup = () => {
isRunning = false;
process.exit(0);
};
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
// Simple progress indicator
const showProgress = (completed: number, total: number, phase?: string) => {
const percent = ((completed / total) * 100).toFixed(0);
const bar = 'â–ˆ'.repeat(Math.floor(completed / total * 20)) + 'â–‘'.repeat(20 - Math.floor(completed / total * 20));
const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);
process.stdout.write('\r\x1b[K'); // Clear line
process.stdout.write(`${chalk.blue(bar)} ${percent}% (${completed}/${total}) ${elapsed}s`);
if (phase) process.stdout.write(chalk.gray(` • ${phase}`));
};
async function main() {
try {
// Minimal startup message
if (resume) {
console.log(chalk.dim('Resuming'), chalk.cyan(resume));
} else {
console.log(chalk.dim('Building'), chalk.cyan(feature));
}
// Show LLM provider being used
const providerEmoji = llmProvider === 'openai' ? '🤖' : '🧠';
const providerName = llmProvider === 'openai' ? 'OpenAI GPT' : 'Anthropic Claude';
console.log(chalk.dim(`${providerEmoji} Using ${providerName}`));
if (concurrent !== 5 || voice) {
const opts = [];
if (concurrent !== 5) opts.push(`${concurrent} concurrent`);
if (voice) opts.push('voice enabled');
console.log(chalk.dim(`(${opts.join(', ')})\n`));
}
// Add progress tracking callback
let progressInterval: NodeJS.Timeout | null = null;
let lastStatus = { completed: 0, failed: 0, running: 0 };
if (!verbose) {
progressInterval = setInterval(() => {
// This will be updated by orchestrator callback
showProgress(lastStatus.completed + lastStatus.failed, AGENT_DEFINITIONS.length);
}, 500);
}
const result = await runOrchestrator({
feature: feature || '',
voice,
resume: resume || undefined,
maxConcurrent: concurrent,
onStop: voice ? (log) => speak(log.join('\n')) : undefined,
onProgress: (status) => {
lastStatus = status;
}
});
if (progressInterval) {
clearInterval(progressInterval);
process.stdout.write('\r\x1b[K'); // Clear progress line
}
// Final summary
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
console.log(chalk.green('✓') + ' Complete in ' + chalk.cyan(duration + 's'));
console.log(chalk.dim('Saved: ') + chalk.cyan('./sessions/' + result.specId + '.json'));
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.log('\n' + chalk.red('✗') + ' Failed: ' + errorMessage);
if (verbose && error instanceof Error && error.stack) {
console.log(chalk.dim('\n' + error.stack));
}
process.exit(1);
}
}
main();