aide-cli
Version:
AIDE - The companion control system for Claude Code with intelligent task management
261 lines (221 loc) • 9.07 kB
JavaScript
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()
};