/**
 * V3 CLI Doctor Command
 * System diagnostics, dependency checks, config validation
 *
 * Created with ruv.io
 */
import { output } from '../output.js';
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
import { join } from 'path';
import { execSync, exec } from 'child_process';
import { promisify } from 'util';
// Promisified exec with proper shell and env inheritance for cross-platform support
const execAsync = promisify(exec);
/**
 * Execute command asynchronously with proper environment inheritance
 * Critical for Windows where PATH may not be inherited properly
 */
async function runCommand(command, timeoutMs = 5000) {
    const { stdout } = await execAsync(command, {
        encoding: 'utf8',
        timeout: timeoutMs,
        shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/sh', // Use proper shell per platform
        env: { ...process.env }, // Explicitly inherit full environment
        windowsHide: true, // Hide window on Windows
    });
    return stdout.trim();
}
// Check Node.js version
async function checkNodeVersion() {
    const requiredMajor = 20;
    const version = process.version;
    const major = parseInt(version.slice(1).split('.')[0], 10);
    if (major >= requiredMajor) {
        return { name: 'Node.js Version', status: 'pass', message: `${version} (>= ${requiredMajor} required)` };
    }
    else if (major >= 18) {
        return { name: 'Node.js Version', status: 'warn', message: `${version} (>= ${requiredMajor} recommended)`, fix: 'nvm install 20 && nvm use 20' };
    }
    else {
        return { name: 'Node.js Version', status: 'fail', message: `${version} (>= ${requiredMajor} required)`, fix: 'nvm install 20 && nvm use 20' };
    }
}
// Check npm version (async with proper env inheritance)
async function checkNpmVersion() {
    try {
        const version = await runCommand('npm --version');
        const major = parseInt(version.split('.')[0], 10);
        if (major >= 9) {
            return { name: 'npm Version', status: 'pass', message: `v${version}` };
        }
        else {
            return { name: 'npm Version', status: 'warn', message: `v${version} (>= 9 recommended)`, fix: 'npm install -g npm@latest' };
        }
    }
    catch {
        return { name: 'npm Version', status: 'fail', message: 'npm not found', fix: 'Install Node.js from https://nodejs.org' };
    }
}
// Check config file
async function checkConfigFile() {
    const configPaths = [
        '.claude-flow/config.json',
        'claude-flow.config.json',
        '.claude-flow.json'
    ];
    for (const configPath of configPaths) {
        if (existsSync(configPath)) {
            try {
                const content = readFileSync(configPath, 'utf8');
                JSON.parse(content);
                return { name: 'Config File', status: 'pass', message: `Found: ${configPath}` };
            }
            catch (e) {
                return { name: 'Config File', status: 'fail', message: `Invalid JSON: ${configPath}`, fix: 'Fix JSON syntax in config file' };
            }
        }
    }
    return { name: 'Config File', status: 'warn', message: 'No config file (using defaults)', fix: 'claude-flow config init' };
}
// Check daemon status
async function checkDaemonStatus() {
    try {
        const pidFile = '.claude-flow/daemon.pid';
        if (existsSync(pidFile)) {
            const pid = readFileSync(pidFile, 'utf8').trim();
            try {
                process.kill(parseInt(pid, 10), 0); // Check if process exists
                return { name: 'Daemon Status', status: 'pass', message: `Running (PID: ${pid})` };
            }
            catch {
                return { name: 'Daemon Status', status: 'warn', message: 'Stale PID file', fix: 'rm .claude-flow/daemon.pid && claude-flow daemon start' };
            }
        }
        return { name: 'Daemon Status', status: 'warn', message: 'Not running', fix: 'claude-flow daemon start' };
    }
    catch {
        return { name: 'Daemon Status', status: 'warn', message: 'Unable to check', fix: 'claude-flow daemon status' };
    }
}
// Check memory database
async function checkMemoryDatabase() {
    const dbPaths = [
        '.claude-flow/memory.db',
        '.swarm/memory.db',
        'data/memory.db'
    ];
    for (const dbPath of dbPaths) {
        if (existsSync(dbPath)) {
            try {
                const stats = statSync(dbPath);
                const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
                return { name: 'Memory Database', status: 'pass', message: `${dbPath} (${sizeMB} MB)` };
            }
            catch {
                return { name: 'Memory Database', status: 'warn', message: `${dbPath} (unable to stat)` };
            }
        }
    }
    return { name: 'Memory Database', status: 'warn', message: 'Not initialized', fix: 'claude-flow memory configure --backend hybrid' };
}
// Check API keys
async function checkApiKeys() {
    const keys = ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY'];
    const found = [];
    for (const key of keys) {
        if (process.env[key]) {
            found.push(key);
        }
    }
    if (found.includes('ANTHROPIC_API_KEY') || found.includes('CLAUDE_API_KEY')) {
        return { name: 'API Keys', status: 'pass', message: `Found: ${found.join(', ')}` };
    }
    else if (found.length > 0) {
        return { name: 'API Keys', status: 'warn', message: `Found: ${found.join(', ')} (no Claude key)`, fix: 'export ANTHROPIC_API_KEY=your_key' };
    }
    else {
        return { name: 'API Keys', status: 'warn', message: 'No API keys found', fix: 'export ANTHROPIC_API_KEY=your_key' };
    }
}
// Check git (async with proper env inheritance)
async function checkGit() {
    try {
        const version = await runCommand('git --version');
        return { name: 'Git', status: 'pass', message: version.replace('git version ', 'v') };
    }
    catch {
        return { name: 'Git', status: 'warn', message: 'Not installed', fix: 'Install git from https://git-scm.com' };
    }
}
// Check if in git repo (async with proper env inheritance)
async function checkGitRepo() {
    try {
        await runCommand('git rev-parse --git-dir');
        return { name: 'Git Repository', status: 'pass', message: 'In a git repository' };
    }
    catch {
        return { name: 'Git Repository', status: 'warn', message: 'Not a git repository', fix: 'git init' };
    }
}
// Check MCP servers
async function checkMcpServers() {
    const mcpConfigPaths = [
        join(process.env.HOME || '', '.claude/claude_desktop_config.json'),
        join(process.env.HOME || '', '.config/claude/mcp.json'),
        '.mcp.json'
    ];
    for (const configPath of mcpConfigPaths) {
        if (existsSync(configPath)) {
            try {
                const content = JSON.parse(readFileSync(configPath, 'utf8'));
                const servers = content.mcpServers || content.servers || {};
                const count = Object.keys(servers).length;
                const hasClaudeFlow = 'claude-flow' in servers || 'claude-flow_alpha' in servers;
                if (hasClaudeFlow) {
                    return { name: 'MCP Servers', status: 'pass', message: `${count} servers (claude-flow configured)` };
                }
                else {
                    return { name: 'MCP Servers', status: 'warn', message: `${count} servers (claude-flow not found)`, fix: 'claude mcp add claude-flow npx @claude-flow/cli@v3alpha mcp start' };
                }
            }
            catch {
                // continue to next path
            }
        }
    }
    return { name: 'MCP Servers', status: 'warn', message: 'No MCP config found', fix: 'claude mcp add claude-flow npx @claude-flow/cli@v3alpha mcp start' };
}
// Check disk space (async with proper env inheritance)
async function checkDiskSpace() {
    try {
        if (process.platform === 'win32') {
            return { name: 'Disk Space', status: 'pass', message: 'Check skipped on Windows' };
        }
        const output_str = await runCommand('df -h . | tail -1');
        const parts = output_str.split(/\s+/);
        const available = parts[3];
        const usePercent = parseInt(parts[4]?.replace('%', '') || '0', 10);
        if (usePercent > 90) {
            return { name: 'Disk Space', status: 'fail', message: `${available} available (${usePercent}% used)`, fix: 'Free up disk space' };
        }
        else if (usePercent > 80) {
            return { name: 'Disk Space', status: 'warn', message: `${available} available (${usePercent}% used)` };
        }
        return { name: 'Disk Space', status: 'pass', message: `${available} available` };
    }
    catch {
        return { name: 'Disk Space', status: 'warn', message: 'Unable to check' };
    }
}
// Check TypeScript/build (async with proper env inheritance)
async function checkBuildTools() {
    try {
        const tscVersion = await runCommand('npx tsc --version', 10000); // tsc can be slow
        if (!tscVersion || tscVersion.includes('not found')) {
            return { name: 'TypeScript', status: 'warn', message: 'Not installed locally', fix: 'npm install -D typescript' };
        }
        return { name: 'TypeScript', status: 'pass', message: tscVersion.replace('Version ', 'v') };
    }
    catch {
        return { name: 'TypeScript', status: 'warn', message: 'Not installed locally', fix: 'npm install -D typescript' };
    }
}
// Check for stale npx cache (version freshness)
async function checkVersionFreshness() {
    try {
        // Get current CLI version from package.json
        let currentVersion = '0.0.0';
        try {
            // Get directory of current module and walk up to find package.json
            const moduleUrl = new URL(import.meta.url);
            const modulePath = moduleUrl.protocol === 'file:' ? moduleUrl.pathname : import.meta.url;
            const moduleDir = modulePath.substring(0, modulePath.lastIndexOf('/'));
            // Look for package.json in multiple locations
            const possiblePaths = [
                join(moduleDir, '..', 'package.json'), // ../package.json from dist/commands
                join(moduleDir, '..', '..', 'package.json'), // ../../package.json from dist/commands
                join(process.cwd(), 'package.json'), // Current working directory
                join(process.cwd(), 'node_modules/@claude-flow/cli/package.json')
            ];
            // Also search in npx cache directories (Linux/macOS)
            const homeDir = process.env.HOME || process.env.USERPROFILE || '';
            if (homeDir) {
                // Check common npx cache locations
                try {
                    const npxCacheDir = join(homeDir, '.npm', '_npx');
                    if (existsSync(npxCacheDir)) {
                        const cacheEntries = readdirSync(npxCacheDir);
                        for (const entry of cacheEntries) {
                            const npxPkgPath = join(npxCacheDir, entry, 'node_modules', '@claude-flow', 'cli', 'package.json');
                            if (existsSync(npxPkgPath)) {
                                possiblePaths.push(npxPkgPath);
                            }
                        }
                    }
                }
                catch {
                    // Ignore errors scanning npx cache
                }
            }
            for (const pkgPath of possiblePaths) {
                try {
                    if (existsSync(pkgPath)) {
                        const packageJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
                        // Make sure it's the right package
                        if (packageJson.name === '@claude-flow/cli' && packageJson.version) {
                            currentVersion = packageJson.version;
                            break;
                        }
                    }
                }
                catch {
                    continue;
                }
            }
        }
        catch {
            // Fall back to a default
            currentVersion = '0.0.0';
        }
        // Check if running via npx (look for _npx in process path or argv)
        const isNpx = process.argv[1]?.includes('_npx') ||
            process.env.npm_execpath?.includes('npx') ||
            process.cwd().includes('_npx');
        // Query npm for latest version (using alpha tag since that's what we publish to)
        let latestVersion = currentVersion;
        try {
            const npmInfo = await runCommand('npm view @claude-flow/cli@alpha version 2>/dev/null', 5000);
            latestVersion = npmInfo.trim();
        }
        catch {
            // Can't reach npm registry - skip check
            return {
                name: 'Version Freshness',
                status: 'warn',
                message: `v${currentVersion} (cannot check registry)`
            };
        }
        // Parse version numbers for comparison (handle prerelease like 3.0.0-alpha.84)
        const parseVersion = (v) => {
            const match = v.match(/^(\d+)\.(\d+)\.(\d+)(?:-[a-zA-Z]+\.(\d+))?/);
            if (!match)
                return { major: 0, minor: 0, patch: 0, prerelease: 0 };
            return {
                major: parseInt(match[1], 10) || 0,
                minor: parseInt(match[2], 10) || 0,
                patch: parseInt(match[3], 10) || 0,
                prerelease: parseInt(match[4], 10) || 0
            };
        };
        const current = parseVersion(currentVersion);
        const latest = parseVersion(latestVersion);
        // Compare versions (including prerelease number)
        const isOutdated = (latest.major > current.major ||
            (latest.major === current.major && latest.minor > current.minor) ||
            (latest.major === current.major && latest.minor === current.minor && latest.patch > current.patch) ||
            (latest.major === current.major && latest.minor === current.minor && latest.patch === current.patch && latest.prerelease > current.prerelease));
        if (isOutdated) {
            const fix = isNpx
                ? 'rm -rf ~/.npm/_npx/* && npx -y @claude-flow/cli@latest'
                : 'npm update @claude-flow/cli';
            return {
                name: 'Version Freshness',
                status: 'warn',
                message: `v${currentVersion} (latest: v${latestVersion})${isNpx ? ' [npx cache stale]' : ''}`,
                fix
            };
        }
        return {
            name: 'Version Freshness',
            status: 'pass',
            message: `v${currentVersion} (up to date)`
        };
    }
    catch (error) {
        return {
            name: 'Version Freshness',
            status: 'warn',
            message: 'Unable to check version freshness'
        };
    }
}
// Check Claude Code CLI (async with proper env inheritance)
async function checkClaudeCode() {
    try {
        const version = await runCommand('claude --version');
        // Parse version from output like "claude 1.0.0" or "Claude Code v1.0.0"
        const versionMatch = version.match(/v?(\d+\.\d+\.\d+)/);
        const versionStr = versionMatch ? `v${versionMatch[1]}` : version;
        return { name: 'Claude Code CLI', status: 'pass', message: versionStr };
    }
    catch {
        return {
            name: 'Claude Code CLI',
            status: 'warn',
            message: 'Not installed',
            fix: 'npm install -g @anthropic-ai/claude-code'
        };
    }
}
// Install Claude Code CLI
async function installClaudeCode() {
    try {
        output.writeln();
        output.writeln(output.bold('Installing Claude Code CLI...'));
        execSync('npm install -g @anthropic-ai/claude-code', {
            encoding: 'utf8',
            stdio: 'inherit'
        });
        output.writeln(output.success('Claude Code CLI installed successfully!'));
        return true;
    }
    catch (error) {
        output.writeln(output.error('Failed to install Claude Code CLI'));
        if (error instanceof Error) {
            output.writeln(output.dim(error.message));
        }
        return false;
    }
}
// Check agentic-flow v3 integration (filesystem-based to avoid slow WASM/DB init)
async function checkAgenticFlow() {
    try {
        // Walk common node_modules paths to find agentic-flow/package.json
        const candidates = [
            join(process.cwd(), 'node_modules', 'agentic-flow', 'package.json'),
            join(process.cwd(), '..', 'node_modules', 'agentic-flow', 'package.json'),
        ];
        let pkgJsonPath = null;
        for (const p of candidates) {
            if (existsSync(p)) {
                pkgJsonPath = p;
                break;
            }
        }
        if (!pkgJsonPath) {
            return {
                name: 'agentic-flow',
                status: 'warn',
                message: 'Not installed (optional — embeddings/routing will use fallbacks)',
                fix: 'npm install agentic-flow@latest'
            };
        }
        const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
        const version = pkg.version || 'unknown';
        const exports = pkg.exports || {};
        const features = [
            exports['./reasoningbank'] ? 'ReasoningBank' : null,
            exports['./router'] ? 'Router' : null,
            exports['./transport/quic'] ? 'QUIC' : null,
        ].filter(Boolean);
        return {
            name: 'agentic-flow',
            status: 'pass',
            message: `v${version} (${features.join(', ')})`
        };
    }
    catch {
        return { name: 'agentic-flow', status: 'warn', message: 'Check failed' };
    }
}
// Format health check result
function formatCheck(check) {
    const icon = check.status === 'pass' ? output.success('✓') :
        check.status === 'warn' ? output.warning('⚠') :
            output.error('✗');
    return `${icon} ${check.name}: ${check.message}`;
}
// Main doctor command
export const doctorCommand = {
    name: 'doctor',
    description: 'System diagnostics and health checks',
    options: [
        {
            name: 'fix',
            short: 'f',
            description: 'Show fix commands for issues',
            type: 'boolean',
            default: false
        },
        {
            name: 'install',
            short: 'i',
            description: 'Auto-install missing dependencies (Claude Code CLI)',
            type: 'boolean',
            default: false
        },
        {
            name: 'component',
            short: 'c',
            description: 'Check specific component (version, node, npm, config, daemon, memory, api, git, mcp, claude, disk, typescript)',
            type: 'string'
        },
        {
            name: 'verbose',
            short: 'v',
            description: 'Verbose output',
            type: 'boolean',
            default: false
        }
    ],
    examples: [
        { command: 'claude-flow doctor', description: 'Run full health check' },
        { command: 'claude-flow doctor --fix', description: 'Show fixes for issues' },
        { command: 'claude-flow doctor --install', description: 'Auto-install missing dependencies' },
        { command: 'claude-flow doctor -c version', description: 'Check for stale npx cache' },
        { command: 'claude-flow doctor -c claude', description: 'Check Claude Code CLI only' }
    ],
    action: async (ctx) => {
        const showFix = ctx.flags.fix;
        const autoInstall = ctx.flags.install;
        const component = ctx.flags.component;
        const verbose = ctx.flags.verbose;
        output.writeln();
        output.writeln(output.bold('Claude Flow Doctor'));
        output.writeln(output.dim('System diagnostics and health check'));
        output.writeln(output.dim('─'.repeat(50)));
        output.writeln();
        const allChecks = [
            checkVersionFreshness,
            checkNodeVersion,
            checkNpmVersion,
            checkClaudeCode,
            checkGit,
            checkGitRepo,
            checkConfigFile,
            checkDaemonStatus,
            checkMemoryDatabase,
            checkApiKeys,
            checkMcpServers,
            checkDiskSpace,
            checkBuildTools,
            checkAgenticFlow
        ];
        const componentMap = {
            'version': checkVersionFreshness,
            'freshness': checkVersionFreshness,
            'node': checkNodeVersion,
            'npm': checkNpmVersion,
            'claude': checkClaudeCode,
            'config': checkConfigFile,
            'daemon': checkDaemonStatus,
            'memory': checkMemoryDatabase,
            'api': checkApiKeys,
            'git': checkGit,
            'mcp': checkMcpServers,
            'disk': checkDiskSpace,
            'typescript': checkBuildTools,
            'agentic-flow': checkAgenticFlow
        };
        let checksToRun = allChecks;
        if (component && componentMap[component]) {
            checksToRun = [componentMap[component]];
        }
        const results = [];
        const fixes = [];
        // OPTIMIZATION: Run all checks in parallel for 3-5x faster execution
        const spinner = output.createSpinner({ text: 'Running health checks in parallel...', spinner: 'dots' });
        spinner.start();
        try {
            // Execute all checks concurrently
            const checkResults = await Promise.allSettled(checksToRun.map(check => check()));
            spinner.stop();
            // Process results in order
            for (const settledResult of checkResults) {
                if (settledResult.status === 'fulfilled') {
                    const result = settledResult.value;
                    results.push(result);
                    output.writeln(formatCheck(result));
                    if (result.fix && (result.status === 'fail' || result.status === 'warn')) {
                        fixes.push(`${result.name}: ${result.fix}`);
                    }
                }
                else {
                    const errorResult = {
                        name: 'Check',
                        status: 'fail',
                        message: settledResult.reason?.message || 'Unknown error'
                    };
                    results.push(errorResult);
                    output.writeln(formatCheck(errorResult));
                }
            }
        }
        catch (error) {
            spinner.stop();
            output.writeln(output.error('Failed to run health checks'));
        }
        // Auto-install missing dependencies if requested
        if (autoInstall) {
            const claudeCodeResult = results.find(r => r.name === 'Claude Code CLI');
            if (claudeCodeResult && claudeCodeResult.status !== 'pass') {
                const installed = await installClaudeCode();
                if (installed) {
                    // Re-check Claude Code after installation
                    const newCheck = await checkClaudeCode();
                    const idx = results.findIndex(r => r.name === 'Claude Code CLI');
                    if (idx !== -1) {
                        results[idx] = newCheck;
                        // Update fixes list
                        const fixIdx = fixes.findIndex(f => f.startsWith('Claude Code CLI:'));
                        if (fixIdx !== -1 && newCheck.status === 'pass') {
                            fixes.splice(fixIdx, 1);
                        }
                    }
                    output.writeln(formatCheck(newCheck));
                }
            }
        }
        // Summary
        const passed = results.filter(r => r.status === 'pass').length;
        const warnings = results.filter(r => r.status === 'warn').length;
        const failed = results.filter(r => r.status === 'fail').length;
        output.writeln();
        output.writeln(output.dim('─'.repeat(50)));
        output.writeln();
        const summaryParts = [
            output.success(`${passed} passed`),
            warnings > 0 ? output.warning(`${warnings} warnings`) : null,
            failed > 0 ? output.error(`${failed} failed`) : null
        ].filter(Boolean);
        output.writeln(`Summary: ${summaryParts.join(', ')}`);
        // Show fixes
        if (showFix && fixes.length > 0) {
            output.writeln();
            output.writeln(output.bold('Suggested Fixes:'));
            output.writeln();
            for (const fix of fixes) {
                output.writeln(output.dim(`  ${fix}`));
            }
        }
        else if (fixes.length > 0 && !showFix) {
            output.writeln();
            output.writeln(output.dim(`Run with --fix to see ${fixes.length} suggested fix${fixes.length > 1 ? 'es' : ''}`));
        }
        // Overall result
        if (failed > 0) {
            output.writeln();
            output.writeln(output.error('Some checks failed. Please address the issues above.'));
            return { success: false, exitCode: 1, data: { passed, warnings, failed, results } };
        }
        else if (warnings > 0) {
            output.writeln();
            output.writeln(output.warning('All checks passed with some warnings.'));
            return { success: true, data: { passed, warnings, failed, results } };
        }
        else {
            output.writeln();
            output.writeln(output.success('All checks passed! System is healthy.'));
            return { success: true, data: { passed, warnings, failed, results } };
        }
    }
};
export default doctorCommand;
//# sourceMappingURL=doctor.js.map