UNPKG

accs-cli

Version:

ACCS CLI — Full-featured developer tool for scaffolding, running, building, and managing multi-language projects

364 lines (309 loc) 9.85 kB
/** * Doctor command - System health check */ import chalk from 'chalk'; import boxen from 'boxen'; import { logger } from '../utils/logger.js'; import { SystemCheck } from '../utils/system-check.js'; import { configManager } from '../config/config-manager.js'; import { FileUtils } from '../utils/file-utils.js'; import path from 'path'; import semver from 'semver'; import os from 'os'; export function doctorCommand(program) { program .command('doctor') .option('-f, --fix', 'Attempt to fix common issues') .option('-v, --verbose', 'Show detailed information') .description('Check system requirements and configuration') .action(async (options) => { try { await runDoctor(options); } catch (error) { logger.error('Doctor check failed:', error.message); process.exit(1); } }); } async function runDoctor(options) { console.log(boxen(chalk.blue.bold('🏥 ACCS System Doctor'), { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'blue' })); const issues = []; // System requirements check const systemChecks = await SystemCheck.runFullCheck(); const systemIssues = analyzeSystemChecks(systemChecks, options); issues.push(...systemIssues); // Configuration check const configIssues = await checkConfiguration(options); issues.push(...configIssues); // Project structure check const projectIssues = await checkProjectStructure(options); issues.push(...projectIssues); // Environment check const envIssues = await checkEnvironment(options); issues.push(...envIssues); // Display results logger.separator(); displaySummary(issues, options); // Attempt fixes if requested if (options.fix && issues.length > 0) { await attemptFixes(issues, options); } // Exit with appropriate code const criticalIssues = issues.filter(i => i.severity === 'error'); if (criticalIssues.length > 0) { process.exit(1); } } function analyzeSystemChecks(checks) { SystemCheck.displayResults(checks); const issues = []; Object.entries(checks).forEach(([tool, result]) => { if (!result.installed) { issues.push({ type: 'system', severity: tool === 'node' ? 'error' : 'warning', title: `${tool} is not installed`, description: `${tool} is required for certain features to work`, fix: `Install ${tool} from the official website`, tool }); } else if (!result.valid) { issues.push({ type: 'system', severity: 'warning', title: `${tool} version may be incompatible`, description: `Version ${result.version} may not work correctly`, fix: `Consider updating ${tool}`, tool }); } }); return issues; } async function checkConfiguration(options) { logger.section('Configuration Check'); const issues = []; try { configManager.validate(); logger.success('Configuration is valid'); if (options.verbose) { logger.info(`Config file: ${chalk.cyan(configManager.getConfigPath())}`); const config = configManager.getAll(); Object.entries(config).forEach(([key, value]) => { logger.info(` ${key}: ${chalk.yellow(JSON.stringify(value))}`); }); } } catch (error) { issues.push({ type: 'config', severity: 'error', title: 'Invalid configuration', description: error.message, fix: 'Run: accs config --reset' }); logger.error('Configuration is invalid:', error.message); } return issues; } async function checkProjectStructure(options) { logger.section('Project Structure Check'); const issues = []; const projectRoot = FileUtils.getProjectRoot(); // Check for package.json const packageJsonPath = path.join(projectRoot, 'package.json'); if (FileUtils.exists(packageJsonPath)) { try { const packageJson = await FileUtils.readJson(packageJsonPath); logger.success('package.json found and valid'); if (options.verbose) { logger.info(`Name: ${packageJson.name || 'unnamed'}`); logger.info(`Version: ${packageJson.version || 'unversioned'}`); } // Check for accs scripts if (packageJson.scripts) { const accsScripts = Object.entries(packageJson.scripts) .filter(([, script]) => script.includes('accs')); if (accsScripts.length > 0) { logger.success(`Found ${accsScripts.length} ACCS script(s)`); } } } catch (error) { issues.push({ type: 'project', severity: 'warning', title: 'Invalid package.json', description: error.message, fix: 'Fix JSON syntax errors' }); } } else { issues.push({ type: 'project', severity: 'info', title: 'No package.json found', description: 'This might not be a Node.js project', fix: 'Run: npm init or accs init' }); } // Check for common directories const commonDirs = ['src', 'dist', 'public', 'assets']; const existingDirs = commonDirs.filter(dir => FileUtils.exists(path.join(projectRoot, dir)) ); if (existingDirs.length > 0) { logger.success(`Found directories: ${existingDirs.join(', ')}`); } else { logger.info('No common project directories found'); } return issues; } async function checkEnvironment() { logger.section('Environment Check'); const issues = []; // Check Node.js version const nodeVersion = process.version; const requiredNode = '18.0.0'; if (semver.gte(nodeVersion, requiredNode)) { logger.success(`Node.js ${nodeVersion} (✓ Compatible)`); } else { issues.push({ type: 'environment', severity: 'error', title: 'Node.js version too old', description: `Current: ${nodeVersion}, Required: ${requiredNode}+`, fix: 'Update Node.js to the latest LTS version' }); } // Check memory const memoryUsage = process.memoryUsage(); const totalMemoryMB = Math.round(memoryUsage.rss / 1024 / 1024); logger.info(`Memory usage: ${totalMemoryMB} MB`); if (totalMemoryMB > 500) { issues.push({ type: 'environment', severity: 'warning', title: 'High memory usage', description: `Current usage: ${totalMemoryMB} MB`, fix: 'Consider restarting or checking for memory leaks' }); } // Check permissions const tempPath = path.join(os.tmpdir(), 'accs-test-' + Date.now()); try { await FileUtils.createDir(tempPath); await FileUtils.remove(tempPath); logger.success('File system permissions OK'); } catch (error) { issues.push({ type: 'environment', severity: 'error', title: 'File system permission issues', description: error.message, fix: 'Check file/directory permissions' }); } return issues; } function displaySummary(issues, options) { logger.section('Summary'); if (issues.length === 0) { const message = chalk.green('✨ All checks passed! Your system is ready to use ACCS.'); console.log(boxen(message, { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'green' })); return; } // Group issues by severity const errors = issues.filter(i => i.severity === 'error'); const warnings = issues.filter(i => i.severity === 'warning'); const info = issues.filter(i => i.severity === 'info'); if (errors.length > 0) { logger.error(`${errors.length} critical issue(s) found:`); errors.forEach(issue => displayIssue(issue, options)); } if (warnings.length > 0) { logger.warn(`${warnings.length} warning(s) found:`); warnings.forEach(issue => displayIssue(issue, options)); } if (info.length > 0 && options.verbose) { logger.info(`${info.length} informational item(s):`); info.forEach(issue => displayIssue(issue, options)); } // Show suggestions logger.separator(); if (errors.length > 0) { logger.error('Please fix critical issues before using ACCS'); } else if (warnings.length > 0) { logger.warn('Consider fixing warnings for better experience'); } logger.info(`Run ${chalk.cyan('accs doctor --fix')} to attempt automatic fixes`); } function displayIssue(issue, options) { const icon = { error: '✖', warning: '⚠', info: 'ℹ' }[issue.severity]; const color = { error: 'red', warning: 'yellow', info: 'blue' }[issue.severity]; console.log(` ${chalk[color](icon)} ${issue.title}`); if (options.verbose) { console.log(` ${chalk.gray(issue.description)}`); if (issue.fix) { console.log(` ${chalk.cyan('Fix:')} ${issue.fix}`); } } } async function attemptFixes(issues, options) { logger.section('Attempting Fixes'); let fixedCount = 0; for (const issue of issues) { try { const fixed = await attemptFix(issue, options); if (fixed) { logger.success(`Fixed: ${issue.title}`); fixedCount++; } } catch (error) { logger.error(`Failed to fix "${issue.title}": ${error.message}`); } } logger.separator(); if (fixedCount > 0) { logger.success(`Fixed ${fixedCount} issue(s)`); logger.info('Run doctor again to verify fixes'); } else { logger.warn('No issues could be automatically fixed'); logger.info('Please address the issues manually'); } } async function attemptFix(issue) { switch (issue.type) { case 'config': if (issue.title.includes('Invalid configuration')) { configManager.reset(); return true; } break; case 'project': if (issue.title.includes('No package.json')) { // Could create a basic package.json, but this might not be desired return false; } break; default: return false; } return false; }