UNPKG

aide-cli

Version:

AIDE - The companion control system for Claude Code with intelligent task management

261 lines (221 loc) 9.07 kB
const fs = require('fs-extra'); const path = require('path'); const os = require('os'); const { exec } = require('child_process'); const { promisify } = require('util'); const chalk = require('chalk'); const execAsync = promisify(exec); class AIDEVerifier { constructor() { this.homeDir = os.homedir(); this.claudeDir = path.join(this.homeDir, '.claude'); } async verify(customClaudeDir = null) { const claudeDir = customClaudeDir || this.claudeDir; const issues = []; const results = { success: false, claudeDir, python: null, scriptsCount: 0, wrapperExists: false, claudeMdValid: false, issues: [] }; try { // 1. Verificar directorio Claude if (!await fs.pathExists(claudeDir)) { issues.push(`Claude directory not found: ${claudeDir}`); } else { console.log(chalk.green(`✅ Claude directory: ${claudeDir}`)); } // 2. Verificar scripts Python const requiredScripts = [ 'aide_init.py', 'aide_track.py', 'aide_status.py', 'aide_adapt.py' ]; let scriptsFound = 0; for (const script of requiredScripts) { const scriptPath = path.join(claudeDir, script); if (await fs.pathExists(scriptPath)) { scriptsFound++; console.log(chalk.green(` ✅ ${script}`)); } else { issues.push(`Missing Python script: ${script}`); console.log(chalk.red(` ❌ ${script}`)); } } results.scriptsCount = scriptsFound; // 3. Verificar wrapper script (opcional en modo daemon) const isWindows = os.platform() === 'win32'; const wrapperName = isWindows ? 'aide-wrapper.bat' : 'aide-wrapper.sh'; const wrapperPath = path.join(claudeDir, wrapperName); if (await fs.pathExists(wrapperPath)) { results.wrapperExists = true; console.log(chalk.green(`✅ Wrapper script: ${wrapperName}`)); // Verificar permisos en Unix if (!isWindows) { const stats = await fs.stat(wrapperPath); const isExecutable = !!(stats.mode & parseInt('111', 8)); if (!isExecutable) { issues.push(`Wrapper script is not executable: ${wrapperPath}`); } } } else { // No es crítico si no hay wrapper en modo daemon console.log(chalk.gray(`ℹ️ Wrapper script not found (not required with daemon mode)`)); } // 4. Verificar Python try { const pythonResult = await this.verifyPython(wrapperPath); results.python = pythonResult; console.log(chalk.green(`✅ Python: ${pythonResult.command} (${pythonResult.version})`)); } catch (error) { issues.push(`Python verification failed: ${error.message}`); console.log(chalk.red(`❌ Python: ${error.message}`)); } // 5. Verificar CLAUDE.md const claudeMdPath = path.join(claudeDir, 'CLAUDE.md'); if (await fs.pathExists(claudeMdPath)) { const claudeMdValid = await this.verifyClaude(claudeMdPath); results.claudeMdValid = claudeMdValid; if (claudeMdValid) { console.log(chalk.green('✅ CLAUDE.md: AIDE integration found')); } else { issues.push('CLAUDE.md exists but AIDE integration not found'); console.log(chalk.yellow('⚠️ CLAUDE.md: No AIDE integration')); } } else { issues.push('CLAUDE.md not found'); console.log(chalk.red('❌ CLAUDE.md: Not found')); } // 6. Test de funcionalidad básica if (results.python) { try { // Probar con comandos globales primero const { stdout } = await execAsync('aide-status', { timeout: 5000 }); if (stdout.includes('AIDE') || stdout.includes('No project')) { console.log(chalk.green('✅ Basic functionality test passed (using global commands)')); } } catch (cmdError) { // Si falla, intentar con wrapper si existe if (results.wrapperExists) { try { await this.testBasicFunctionality(wrapperPath); console.log(chalk.green('✅ Basic functionality test passed (using wrapper)')); } catch (error) { issues.push(`Functionality test failed: ${error.message}`); console.log(chalk.red(`❌ Functionality test: ${error.message}`)); } } else { issues.push(`Global commands not working and no wrapper found`); console.log(chalk.red(`❌ Functionality test: Global commands failed`)); } } } results.issues = issues; results.success = issues.length === 0; return results; } catch (error) { results.issues = [`Verification error: ${error.message}`]; return results; } } async verifyPython(wrapperPath) { const isWindows = os.platform() === 'win32'; const testCommand = isWindows ? `"${wrapperPath}" status 2>nul || echo "wrapper test"` : `"${wrapperPath}" status 2>/dev/null || echo "wrapper test"`; try { const { stdout, stderr } = await execAsync(testCommand); // Si el wrapper funciona, extraer info de Python if (!stderr) { // Ejecutar comando directo para obtener versión de Python const pythonCommand = await this.extractPythonCommand(wrapperPath); const { stdout: versionOutput } = await execAsync(`${pythonCommand} --version`); return { command: pythonCommand, version: versionOutput.trim() }; } } catch (error) { // Fallback: intentar detectar Python directamente const candidates = ['python3', 'python', 'py']; for (const cmd of candidates) { try { const { stdout } = await execAsync(`${cmd} --version`); return { command: cmd, version: stdout.trim() }; } catch (e) { continue; } } throw new Error('No working Python found'); } } async extractPythonCommand(wrapperPath) { const content = await fs.readFile(wrapperPath, 'utf8'); // Buscar PREFERRED_PYTHON en el wrapper const match = content.match(/PREFERRED_PYTHON[=:]"?([^"\s]+)"?/); if (match) { return match[1]; } return 'python3'; // fallback } async verifyClaude(claudeMdPath) { const content = await fs.readFile(claudeMdPath, 'utf8'); // Verificar que contiene la integración AIDE const aideMarkers = [ 'AIDE Integration', 'AIDE Central Control System', 'Task Control Rules', 'aide-init' ]; return aideMarkers.every(marker => content.includes(marker)); } async testBasicFunctionality(wrapperPath) { const isWindows = os.platform() === 'win32'; // Test que el wrapper puede ejecutar el script de status const testCommand = isWindows ? `"${wrapperPath}" status` : `"${wrapperPath}" status`; const { stdout, stderr } = await execAsync(testCommand, { timeout: 10000 }); if (stderr && !stdout.includes('AIDE no inicializado')) { throw new Error(`Wrapper test failed: ${stderr}`); } // El output esperado debe incluir AIDE status o error de inicialización if (!stdout.includes('AIDE no inicializado') && !stdout.includes('AIDE Status') && !stdout.includes('AIDE-AI Status')) { throw new Error(`Unexpected wrapper output: ${stdout.substring(0, 100)}...`); } } async generateDiagnosticReport() { console.log(chalk.blue.bold('\n🔍 AIDE Diagnostic Report\n')); const result = await this.verify(); console.log(chalk.blue('📋 Summary:')); console.log(` Status: ${result.success ? chalk.green('✅ Healthy') : chalk.red('❌ Issues Found')}`); console.log(` Claude Dir: ${result.claudeDir}`); console.log(` Python: ${result.python ? `${result.python.command} (${result.python.version})` : 'Not detected'}`); console.log(` Scripts: ${result.scriptsCount}/4 installed`); console.log(` Wrapper: ${result.wrapperExists ? 'Present' : 'Missing'}`); console.log(` CLAUDE.md: ${result.claudeMdValid ? 'Integrated' : 'Not integrated'}`); if (result.issues.length > 0) { console.log(chalk.red.bold('\n⚠️ Issues Found:')); result.issues.forEach((issue, index) => { console.log(chalk.red(` ${index + 1}. ${issue}`)); }); console.log(chalk.yellow.bold('\n💡 Recommended Actions:')); console.log(chalk.yellow(' 1. Run: aide install --force')); console.log(chalk.yellow(' 2. Verify Claude Code is properly installed')); console.log(chalk.yellow(' 3. Check Python 3.7+ is available')); } return result; } } module.exports = { verify: (claudeDir) => new AIDEVerifier().verify(claudeDir), generateDiagnosticReport: () => new AIDEVerifier().generateDiagnosticReport() };