erosolar-cli
Version:
Unified AI agent framework for the command line - Multi-provider support with schema-driven tools, code intelligence, and transparent reasoning
176 lines (150 loc) • 5.93 kB
JavaScript
import { readFileSync, existsSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
// Use Node.js built-in color support for better cross-platform compatibility
const hasColors = process.stdout.isTTY && process.stdout.hasColors?.();
const colors = {
reset: hasColors ? '\x1b[0m' : '',
red: hasColors ? '\x1b[31m' : '',
green: hasColors ? '\x1b[32m' : '',
yellow: hasColors ? '\x1b[33m' : '',
blue: hasColors ? '\x1b[34m' : '',
magenta: hasColors ? '\x1b[35m' : '',
cyan: hasColors ? '\x1b[36m' : ''
};
function logSuccess(message) {
console.log(`${colors.green}✅${colors.reset} ${message}`);
}
function logError(message) {
console.log(`${colors.red}❌${colors.reset} ${message}`);
}
function logWarning(message) {
console.log(`${colors.yellow}⚠️${colors.reset} ${message}`);
}
function logInfo(message) {
console.log(`${colors.blue}ℹ️${colors.reset} ${message}`);
}
function checkFileExists(path, description = path) {
if (existsSync(path)) {
logSuccess(`${description} - Exists`);
return true;
} else {
logError(`${description} - Missing`);
return false;
}
}
function checkJsonFile(path, description = path) {
if (!checkFileExists(path, description)) return false;
try {
const content = JSON.parse(readFileSync(path, 'utf8'));
logSuccess(`${description} - Valid JSON`);
return content;
} catch (error) {
logError(`${description} - Invalid JSON: ${error.message}`);
return null;
}
}
console.log(`${colors.cyan}🧪 Erosolar CLI Comprehensive Health Check${colors.reset}\n`);
let hasErrors = false;
let hasWarnings = false;
// Check Node version using proper semver comparison
console.log(`${colors.magenta}📦 Node.js Environment${colors.reset}`);
const nodeVersion = process.version;
const [major] = process.versions.node.split('.').map(Number);
console.log(` Version: ${nodeVersion}`);
if (major >= 20) {
logSuccess(`Node.js version meets requirement (>=20.0.0)`);
} else {
logError(`Node.js 20.0.0 or newer is required`);
hasErrors = true;
}
// Check package.json configuration
console.log(`\n${colors.magenta}📋 Package Configuration${colors.reset}`);
const packageJson = checkJsonFile('package.json', 'Package.json');
if (packageJson) {
const requiredFields = ['name', 'version', 'description', 'main', 'bin'];
const missingFields = requiredFields.filter(field => !packageJson[field]);
if (missingFields.length === 0) {
logSuccess('Package.json configuration valid');
} else {
logError(`Missing required fields: ${missingFields.join(', ')}`);
hasErrors = true;
}
// Check erosolar-specific configuration
if (packageJson.erosolar?.rulebookSchema) {
logSuccess('Erosolar rulebook schema configured');
} else {
logWarning('Erosolar rulebook schema not configured in package.json');
hasWarnings = true;
}
}
// Check TypeScript compilation
console.log(`\n${colors.magenta}🔧 TypeScript & Build${colors.reset}`);
if (checkFileExists('dist', 'Build directory')) {
// Check main binary
if (checkFileExists('dist/bin/erosolar.js', 'Main binary')) {
// Check bin file permissions
const binCheck = spawnSync('ls', ['-la', 'dist/bin/erosolar.js'], { encoding: 'utf8' });
if (binCheck.status === 0 && binCheck.stdout.includes('-rwx')) {
logSuccess('Binary is executable');
} else {
logWarning('Binary is not executable');
hasWarnings = true;
}
}
} else {
logWarning('Build directory does not exist - run \'npm run build\'');
hasWarnings = true;
}
// Check agent rulebooks
console.log(`\n${colors.magenta}📚 Agent Rulebooks${colors.reset}`);
const agentFiles = [
{ path: 'agents/erosolar-code.rules.json', name: 'Erosolar Code rulebook' },
{ path: 'agents/general.rules.json', name: 'General rulebook' }
];
agentFiles.forEach(({ path, name }) => {
const content = checkJsonFile(path, name);
if (content) {
const requiredFields = ['profile', 'version', 'label', 'globalPrinciples', 'phases'];
const missingFields = requiredFields.filter(field => !content[field]);
if (missingFields.length === 0) {
logSuccess(`${name} - Valid structure`);
} else {
logError(`${name} - Missing required fields: ${missingFields.join(', ')}`);
hasErrors = true;
}
}
});
// Check rulebook schema
console.log(`\n${colors.magenta}📄 Schema Validation${colors.reset}`);
checkFileExists('src/contracts/schemas/agent-rules.schema.json', 'Agent rules schema');
// Check core dependencies
console.log(`\n${colors.magenta}📦 Core Dependencies${colors.reset}`);
const coreFiles = [
'src/core/agent.ts',
'src/core/agentRulebook.ts',
'src/core/toolRuntime.ts',
'src/providers/providerFactory.ts',
'src/capabilities/index.ts'
];
coreFiles.forEach(file => checkFileExists(file, file));
// Summary
console.log(`\n${colors.cyan}📊 Health Check Summary${colors.reset}`);
if (hasErrors) {
console.log(`${colors.red}❌ Health check failed with errors${colors.reset}`);
console.log(`${colors.yellow}💡 Run 'npm run build' to rebuild the project${colors.reset}`);
console.log(`${colors.yellow}💡 Run 'npm test' to verify functionality${colors.reset}`);
process.exit(1);
} else if (hasWarnings) {
console.log(`${colors.yellow}⚠️ Health check completed with warnings${colors.reset}`);
console.log(`${colors.yellow}💡 Review warnings above and address as needed${colors.reset}`);
process.exit(0);
} else {
console.log(`${colors.green}🎉 Health check passed successfully!${colors.reset}`);
console.log(`${colors.green}✅ The CLI is ready for use${colors.reset}`);
console.log(`\n${colors.cyan}💡 Next steps:${colors.reset}`);
console.log(' Run \'erosolar\' to start the CLI');
console.log(' Run \'npm test\' for full test suite');
console.log(' Run \'npm run build\' to rebuild if needed');
process.exit(0);
}