UNPKG

@mrtkrcm/acp-claude-code

Version:

ACP (Agent Client Protocol) bridge for Claude Code

299 lines 12.4 kB
import { existsSync } from 'node:fs'; import { access, constants } from 'node:fs/promises'; import { spawn } from 'node:child_process'; import { resolve } from 'node:path'; export class DiagnosticSystem { static detectPlatformCapabilities() { return { platform: process.platform, hasTTY: process.stdout.isTTY || false, terminal: process.env.TERM, isWSL: !!process.env.WSL_DISTRO_NAME, nodeVersion: process.version, arch: process.arch, }; } static async findClaudeExecutable(pathOverride) { const candidates = [ pathOverride, process.env.ACP_PATH_TO_CLAUDE_CODE_EXECUTABLE, '/usr/local/bin/claude', '/opt/homebrew/bin/claude', '~/.local/bin/claude', ].filter(Boolean); for (const candidate of candidates) { try { // Resolve ~ to home directory const resolvedPath = candidate.startsWith('~') ? resolve(process.env.HOME || '/', candidate.slice(2)) : candidate; await access(resolvedPath, constants.F_OK | constants.X_OK); return resolvedPath; } catch { continue; } } // Try 'claude' in PATH return new Promise((resolve) => { const whichCmd = process.platform === 'win32' ? 'where' : 'which'; const child = spawn(whichCmd, ['claude'], { stdio: 'pipe', timeout: 3000 }); let output = ''; child.stdout?.on('data', (data) => output += data.toString()); child.on('close', (code) => { resolve(code === 0 && output.trim() ? output.trim().split('\n')[0] : null); }); child.on('error', () => resolve(null)); }); } static async getClaudeVersion(executablePath) { return new Promise((resolve) => { const child = spawn(executablePath, ['--version'], { stdio: 'pipe', timeout: 3000 }); let output = ''; child.stdout?.on('data', (data) => output += data.toString()); child.on('close', (code) => { if (code === 0) { const match = output.match(/(\d+\.\d+\.\d+)/); resolve(match ? match[1] : output.trim()); } else { resolve(null); } }); child.on('error', () => resolve(null)); }); } static async checkClaudeAuthentication(_executablePath) { // Check if config file exists const configPath = resolve(process.env.HOME || '/', '.claude', 'config.json'); return existsSync(configPath); } static async generateReport() { const platform = this.detectPlatformCapabilities(); const issues = []; // Configuration analysis const permissionMode = process.env.ACP_PERMISSION_MODE || 'default'; const pathOverride = process.env.ACP_PATH_TO_CLAUDE_CODE_EXECUTABLE; const debugMode = process.env.ACP_DEBUG === 'true'; // Platform compatibility checks if (!platform.hasTTY) { issues.push({ level: 'warning', category: 'platform', code: 'NO_TTY', message: 'Running in non-TTY environment', solution: 'Some Claude Code features may be limited. Consider using acceptEdits permission mode.' }); } if (platform.platform === 'win32') { issues.push({ level: 'warning', category: 'platform', code: 'WINDOWS_PLATFORM', message: 'Windows platform detected', solution: 'Consider using WSL or PowerShell instead of Git Bash for better compatibility.' }); } // Node.js version check const nodeVersionMatch = platform.nodeVersion.match(/^v(\d+)/); if (nodeVersionMatch) { const majorVersion = parseInt(nodeVersionMatch[1], 10); if (majorVersion < 18) { issues.push({ level: 'error', category: 'platform', code: 'NODE_VERSION_OLD', message: `Node.js ${platform.nodeVersion} is too old (minimum: 18.0.0)`, solution: 'Upgrade to Node.js 18 or later' }); } } // Claude Code analysis const claudePath = await this.findClaudeExecutable(pathOverride); const claudeAvailable = !!claudePath; let claudeVersion; let claudeAuthenticated; if (claudeAvailable && claudePath) { claudeVersion = await this.getClaudeVersion(claudePath) || undefined; claudeAuthenticated = await this.checkClaudeAuthentication(claudePath); if (!claudeVersion) { issues.push({ level: 'warning', category: 'claude', code: 'VERSION_CHECK_FAILED', message: 'Could not determine Claude Code version', solution: 'Verify Claude Code installation is working properly.' }); } if (!claudeAuthenticated) { issues.push({ level: 'error', category: 'claude', code: 'NOT_AUTHENTICATED', message: 'Claude Code is not authenticated', solution: 'Run "claude setup-token" to authenticate.' }); } } else { issues.push({ level: 'error', category: 'claude', code: 'EXECUTABLE_NOT_FOUND', message: 'Claude Code executable not found', solution: pathOverride ? `Check if the path ${pathOverride} exists and is executable.` : 'Install Claude Code or set ACP_PATH_TO_CLAUDE_CODE_EXECUTABLE environment variable.' }); } // Configuration recommendations if (!platform.hasTTY && permissionMode === 'default') { issues.push({ level: 'info', category: 'configuration', code: 'PERMISSION_MODE_SUGGESTION', message: 'Consider using acceptEdits permission mode for non-TTY environments', solution: 'Set ACP_PERMISSION_MODE=acceptEdits environment variable.' }); } // Enhanced path validation if (pathOverride) { if (!existsSync(pathOverride)) { issues.push({ level: 'error', category: 'configuration', code: 'CUSTOM_PATH_NOT_FOUND', message: `Custom Claude Code path does not exist: ${pathOverride}`, solution: 'Verify the ACP_PATH_TO_CLAUDE_CODE_EXECUTABLE path is correct and accessible.' }); } else { // Check if path is executable try { await access(pathOverride, constants.X_OK); } catch { issues.push({ level: 'error', category: 'configuration', code: 'CUSTOM_PATH_NOT_EXECUTABLE', message: `Custom Claude Code path is not executable: ${pathOverride}`, solution: 'Make the file executable with: chmod +x <path>' }); } } } // Simple compatibility scoring const errorCount = issues.filter(i => i.level === 'error').length; const warningCount = issues.filter(i => i.level === 'warning').length; let score = 100 - errorCount * 25 - warningCount * 5; if (claudeAvailable && claudeAuthenticated) score += 10; score = Math.max(0, Math.min(100, score)); const compatible = errorCount === 0; return { platform, claudeCode: { available: claudeAvailable, path: claudePath || undefined, version: claudeVersion, authenticated: claudeAuthenticated, }, configuration: { permissionMode, pathOverride, debugMode, }, issues, compatible, score, }; } static formatReport(report) { const { platform, claudeCode, configuration, issues, compatible, score } = report; let output = 'ACP-Claude-Code Diagnostic Report\n'; output += '='.repeat(50) + '\n\n'; // Overall status const statusSymbol = compatible ? '[OK]' : '[ERROR]'; output += `${statusSymbol} Overall Status: ${compatible ? 'COMPATIBLE' : 'ISSUES FOUND'}\n`; output += `Compatibility Score: ${score}/100\n\n`; // Platform info output += 'Platform Information:\n'; output += ` Platform: ${platform.platform} (${platform.arch})\n`; output += ` Node.js: ${platform.nodeVersion}\n`; output += ` TTY Support: ${platform.hasTTY ? 'Yes' : 'No'}\n`; output += ` Terminal: ${platform.terminal || 'Unknown'}\n`; if (platform.isWSL) output += ` WSL Environment: Yes\n`; output += '\n'; // Claude Code status output += '🤖 Claude Code Status:\n'; output += ` Available: ${claudeCode.available ? 'Yes' : 'No'}\n`; if (claudeCode.path) output += ` Path: ${claudeCode.path}\n`; if (claudeCode.version) output += ` Version: ${claudeCode.version}\n`; if (claudeCode.authenticated !== undefined) { output += ` Authenticated: ${claudeCode.authenticated ? 'Yes' : 'No'}\n`; } output += '\n'; // Configuration output += 'Configuration:\n'; output += ` Permission Mode: ${configuration.permissionMode}\n`; output += ` Debug Mode: ${configuration.debugMode ? 'Enabled' : 'Disabled'}\n`; if (configuration.pathOverride) { output += ` Custom Path: ${configuration.pathOverride}\n`; } output += '\n'; // Issues if (issues.length > 0) { output += 'Issues Found:\n\n'; const errors = issues.filter(i => i.level === 'error'); const warnings = issues.filter(i => i.level === 'warning'); const infos = issues.filter(i => i.level === 'info'); if (errors.length > 0) { output += 'ERRORS (Must Fix):\n'; errors.forEach((issue, i) => { output += ` ${i + 1}. ${issue.message}\n`; if (issue.solution) output += ` → ${issue.solution}\n`; }); output += '\n'; } if (warnings.length > 0) { output += 'WARNINGS (Should Fix):\n'; warnings.forEach((issue, i) => { output += ` ${i + 1}. ${issue.message}\n`; if (issue.solution) output += ` → ${issue.solution}\n`; }); output += '\n'; } if (infos.length > 0) { output += 'SUGGESTIONS (Optional):\n'; infos.forEach((issue, i) => { output += ` ${i + 1}. ${issue.message}\n`; if (issue.solution) output += ` → ${issue.solution}\n`; }); output += '\n'; } } else { output += 'No issues found! Everything looks good.\n\n'; } output += 'Run this diagnostic with: ACP_DEBUG=true acp-claude-code --diagnose\n'; return output; } /** * Get basic system metrics */ static getSystemMetrics() { return { memory: process.memoryUsage(), uptime: process.uptime(), platform: process.platform, }; } } //# sourceMappingURL=diagnostics.js.map