claude-code-subagents-orchestrator
Version:
Claude Code Sub-agents Orchestrator - A powerful MCP server for orchestrating multiple AI sub-agents for complex task execution in Claude Code
232 lines (202 loc) • 6.39 kB
JavaScript
/**
* Universal Launcher for Claude Code Subagents Orchestrator
*
* This script handles all installation scenarios:
* - Global npm installation
* - Local npm installation
* - Development environment
* - Direct execution
*
* It automatically detects the correct entry point and execution method.
*/
import { fileURLToPath } from 'url';
import { dirname, join, resolve } from 'path';
import { spawn } from 'child_process';
import { existsSync, readFileSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Find the project root by looking for package.json
*/
function findProjectRoot(startPath) {
let currentPath = startPath;
while (currentPath !== '/') {
if (existsSync(join(currentPath, 'package.json'))) {
return currentPath;
}
currentPath = dirname(currentPath);
}
return null;
}
/**
* Detect if running in development mode
*/
function isDevMode() {
// Check if we're in a development environment
const projectRoot = findProjectRoot(__dirname);
if (!projectRoot) return false;
// Check if src directory exists (development indicator)
return existsSync(join(projectRoot, 'src'));
}
/**
* Find the correct entry point based on installation type
*/
function findEntryPoint() {
const projectRoot = findProjectRoot(__dirname);
// Possible entry points in order of preference
const possiblePaths = [
// MCP server entry point (highest priority for minimal package)
join(__dirname, '..', 'server.js'),
join(projectRoot || '', 'server.js'),
// Production builds
join(__dirname, '..', 'dist', 'index.js'), // npm global install
join(__dirname, '..', 'dist', 'server.js'), // alternative entry
join(projectRoot || '', 'dist', 'index.js'), // local install
join(projectRoot || '', 'dist', 'server.js'), // alternative local
// Development paths
join(__dirname, '..', 'src', 'index.ts'), // development mode
join(__dirname, '..', 'src', 'server.ts'), // alternative dev
join(projectRoot || '', 'src', 'index.ts'), // local dev
join(projectRoot || '', 'src', 'server.ts'), // alternative local dev
// Fallback to current directory
join(process.cwd(), 'dist', 'index.js'),
join(process.cwd(), 'src', 'index.ts'),
].filter(Boolean); // Remove any null paths
// Find first existing path
for (const path of possiblePaths) {
if (existsSync(path)) {
return path;
}
}
return null;
}
/**
* Check if tsx is available for TypeScript execution
*/
function hasTsx() {
try {
const { execSync } = require('child_process');
execSync('tsx --version', { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
/**
* Main execution
*/
function main() {
console.log('Starting Claude Code Subagents Orchestrator...\n');
const entryPoint = findEntryPoint();
if (!entryPoint) {
console.error('ERROR: Could not find claude-orchestrator entry point');
console.error('\nSearched in:');
console.error(' - Global npm installation');
console.error(' - Local npm installation');
console.error(' - Development environment');
console.error(' - Current directory');
console.error('\nPlease ensure the package is properly installed.');
console.error('\nTry running: npm install -g claude-code-subagents-orchestrator');
process.exit(1);
}
// Determine execution method
const isTypeScript = entryPoint.endsWith('.ts');
let executable = 'node';
let args = [];
if (isTypeScript) {
if (hasTsx()) {
executable = 'tsx';
args = [entryPoint];
} else {
// Fallback to node with experimental loader
args = [
'--loader', 'tsx',
'--no-warnings',
entryPoint
];
}
} else {
// For JavaScript files, check if ES modules
try {
const projectRoot = findProjectRoot(dirname(entryPoint));
if (projectRoot) {
const packageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
if (packageJson.type === 'module') {
// ES modules need experimental specifier resolution
args = [
'--experimental-specifier-resolution=node',
'--no-warnings',
entryPoint
];
} else {
args = [entryPoint];
}
} else {
args = [entryPoint];
}
} catch {
args = [entryPoint];
}
}
// Add user arguments
args.push(...process.argv.slice(2));
// Debug output in verbose mode
if (process.argv.includes('--debug') || process.argv.includes('-v')) {
console.log(`Entry point: ${entryPoint}`);
console.log(`Executable: ${executable}`);
console.log(`Arguments: ${args.join(' ')}\n`);
}
// Spawn the process
const child = spawn(executable, args, {
stdio: 'inherit',
env: {
...process.env,
CLAUDE_ORCHESTRATOR_LAUNCHER: 'true',
CLAUDE_ORCHESTRATOR_ROOT: findProjectRoot(__dirname) || __dirname,
},
shell: false
});
// Handle child process events
child.on('error', (error) => {
console.error('\nERROR: Failed to start orchestrator:', error.message);
if (error.code === 'ENOENT') {
console.error(`\nThe executable '${executable}' was not found.`);
if (isTypeScript && !hasTsx()) {
console.error('\nFor TypeScript execution, please install tsx:');
console.error(' npm install -g tsx');
}
}
process.exit(1);
});
child.on('exit', (code, signal) => {
if (signal) {
console.log(`\nOrchestrator terminated by signal: ${signal}`);
process.exit(1);
} else if (code !== null && code !== 0) {
process.exit(code);
} else {
process.exit(0);
}
});
// Forward signals to child process
['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach(signal => {
process.on(signal, () => {
if (!child.killed) {
child.kill(signal);
}
});
});
}
// Error handling
process.on('uncaughtException', (error) => {
console.error('\nFATAL ERROR:', error.message);
console.error(error.stack);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('\nUnhandled Promise Rejection:', reason);
process.exit(1);
});
// Run the launcher
main();