/**
 * CodexLens Tool - Bridge between CCW and CodexLens Python package
 * Provides code indexing and semantic search via spawned Python process
 *
 * Features:
 * - Automatic venv bootstrap at ~/.codexlens/venv
 * - JSON protocol communication
 * - Symbol extraction and semantic search
 * - FTS5 full-text search
 */
import { z } from 'zod';
import { spawn, execSync } from 'child_process';
import { existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';
import { fileURLToPath } from 'url';
import { getSystemPython } from '../utils/python-utils.js';
import { EXEC_TIMEOUTS } from '../utils/exec-constants.js';
import { ensureUvInstalled, isUvAvailable, createCodexLensUvManager, } from '../utils/uv-manager.js';
import { getCodexLensDataDir, getCodexLensVenvDir, getCodexLensPython, getCodexLensPip, } from '../utils/codexlens-path.js';
// Get directory of this module
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
 * Check if a path is inside node_modules (unstable for editable installs)
 * Paths inside node_modules will change when npm reinstalls packages,
 * breaking editable (-e) pip installs that reference them.
 */
function isInsideNodeModules(pathToCheck) {
    const normalizedPath = pathToCheck.replace(/\\/g, '/').toLowerCase();
    return normalizedPath.includes('/node_modules/');
}
/**
 * Check if we're running in a development environment (not from node_modules)
 * Also detects Yarn PnP (Plug'n'Play) which doesn't use node_modules.
 */
function isDevEnvironment() {
    // Yarn PnP detection: if pnp version exists, it's a managed production environment
    if (process.versions.pnp) {
        return false;
    }
    return !isInsideNodeModules(__dirname);
}
/**
 * Find valid local package path for development installs.
 * Returns null if running from node_modules (should use PyPI instead).
 *
 * IMPORTANT: When running from node_modules, local paths are unstable
 * because npm reinstall will delete and recreate the node_modules directory,
 * breaking any editable (-e) pip installs that reference them.
 */
function findLocalPackagePath(packageName) {
    // Always try to find local paths first, even when running from node_modules.
    // codex-lens is a local development package not published to PyPI,
    // so we must find it locally regardless of execution context.
    const possiblePaths = [
        join(process.cwd(), packageName),
        join(__dirname, '..', '..', '..', packageName), // ccw/src/tools -> project root
        join(homedir(), packageName),
    ];
    // Also check common workspace locations
    const cwd = process.cwd();
    const cwdParent = dirname(cwd);
    if (cwdParent !== cwd) {
        possiblePaths.push(join(cwdParent, packageName));
    }
    for (const localPath of possiblePaths) {
        // Skip paths inside node_modules
        if (isInsideNodeModules(localPath)) {
            continue;
        }
        if (existsSync(join(localPath, 'pyproject.toml'))) {
            console.log(`[CodexLens] Found local ${packageName} at: ${localPath}`);
            return localPath;
        }
    }
    if (!isDevEnvironment()) {
        console.log(`[CodexLens] Running from node_modules - will try PyPI for ${packageName}`);
    }
    return null;
}
/**
 * Find valid local codex-lens package path for development installs.
 */
function findLocalCodexLensPath() {
    return findLocalPackagePath('codex-lens');
}
/**
 * Find valid local ccw-litellm package path for development installs.
 */
function findLocalCcwLitellmPath() {
    return findLocalPackagePath('ccw-litellm');
}
// Bootstrap status cache
let bootstrapChecked = false;
let bootstrapReady = false;
let venvStatusCache = null;
const VENV_STATUS_TTL = 5 * 60 * 1000; // 5 minutes TTL
let semanticStatusCache = null;
const SEMANTIC_STATUS_TTL = 5 * 60 * 1000; // 5 minutes TTL
// Track running indexing process for cancellation
let currentIndexingProcess = null;
let currentIndexingAborted = false;
// Define Zod schema for validation
const ParamsSchema = z.object({
    action: z.enum([
        'init',
        'search',
        'search_files',
        'status',
        'symbol',
        'check',
        'update',
        'bootstrap',
    ]),
    path: z.string().optional(),
    query: z.string().optional(),
    mode: z.enum(['auto', 'text', 'semantic', 'exact', 'fuzzy', 'hybrid', 'vector', 'pure-vector']).default('auto'),
    format: z.enum(['json', 'text', 'pretty']).default('json'),
    languages: z.array(z.string()).optional(),
    limit: z.number().default(20),
    enrich: z.boolean().default(false),
    // Additional fields for internal functions
    file: z.string().optional(),
    key: z.string().optional(),
    value: z.string().optional(),
    newPath: z.string().optional(),
    all: z.boolean().optional(),
});
/**
 * Clear venv status cache (call after install/uninstall operations)
 */
function clearVenvStatusCache() {
    venvStatusCache = null;
}
// Python detection functions imported from ../utils/python-utils.js
/**
 * Check if CodexLens venv exists and has required packages
 * @param force - Force refresh cache (default: false)
 * @returns Ready status
 */
async function checkVenvStatus(force = false) {
    const funcStart = Date.now();
    console.log('[PERF][CodexLens] checkVenvStatus START');
    // Use cached result if available and not expired
    if (!force && venvStatusCache && (Date.now() - venvStatusCache.timestamp < VENV_STATUS_TTL)) {
        console.log(`[PERF][CodexLens] checkVenvStatus CACHE HIT: ${Date.now() - funcStart}ms`);
        return venvStatusCache.status;
    }
    // Check venv exists
    if (!existsSync(getCodexLensVenvDir())) {
        const result = { ready: false, error: 'Venv not found' };
        venvStatusCache = { status: result, timestamp: Date.now() };
        console.log(`[PERF][CodexLens] checkVenvStatus (no venv): ${Date.now() - funcStart}ms`);
        return result;
    }
    // Check python executable exists
    if (!existsSync(getCodexLensPython())) {
        const result = { ready: false, error: 'Python executable not found in venv' };
        venvStatusCache = { status: result, timestamp: Date.now() };
        console.log(`[PERF][CodexLens] checkVenvStatus (no python): ${Date.now() - funcStart}ms`);
        return result;
    }
    // Check codexlens and core dependencies are importable
    const spawnStart = Date.now();
    console.log('[PERF][CodexLens] checkVenvStatus spawning Python...');
    return new Promise((resolve) => {
        const child = spawn(getCodexLensPython(), ['-c', 'import codexlens; import watchdog; print(codexlens.__version__)'], {
            stdio: ['ignore', 'pipe', 'pipe'],
            timeout: 10000,
        });
        let stdout = '';
        let stderr = '';
        child.stdout.on('data', (data) => {
            stdout += data.toString();
        });
        child.stderr.on('data', (data) => {
            stderr += data.toString();
        });
        child.on('close', (code) => {
            let result;
            if (code === 0) {
                result = { ready: true, version: stdout.trim() };
            }
            else {
                result = { ready: false, error: `CodexLens not installed: ${stderr}` };
            }
            // Cache the result
            venvStatusCache = { status: result, timestamp: Date.now() };
            console.log(`[PERF][CodexLens] checkVenvStatus Python spawn: ${Date.now() - spawnStart}ms | TOTAL: ${Date.now() - funcStart}ms | ready: ${result.ready}`);
            resolve(result);
        });
        child.on('error', (err) => {
            const result = { ready: false, error: `Failed to check venv: ${err.message}` };
            venvStatusCache = { status: result, timestamp: Date.now() };
            console.log(`[PERF][CodexLens] checkVenvStatus ERROR: ${Date.now() - funcStart}ms`);
            resolve(result);
        });
    });
}
/**
 * Clear semantic status cache (call after install/uninstall operations)
 */
function clearSemanticStatusCache() {
    semanticStatusCache = null;
}
/**
 * Check if semantic search dependencies are installed
 * @param force - Force refresh cache (default: false)
 * @returns Semantic status
 */
async function checkSemanticStatus(force = false) {
    const funcStart = Date.now();
    console.log('[PERF][CodexLens] checkSemanticStatus START');
    // Use cached result if available and not expired
    if (!force && semanticStatusCache && (Date.now() - semanticStatusCache.timestamp < SEMANTIC_STATUS_TTL)) {
        console.log(`[PERF][CodexLens] checkSemanticStatus CACHE HIT: ${Date.now() - funcStart}ms`);
        return semanticStatusCache.status;
    }
    // First check if CodexLens is installed
    const venvStatus = await checkVenvStatus();
    if (!venvStatus.ready) {
        const result = { available: false, error: 'CodexLens not installed' };
        semanticStatusCache = { status: result, timestamp: Date.now() };
        console.log(`[PERF][CodexLens] checkSemanticStatus (no venv): ${Date.now() - funcStart}ms`);
        return result;
    }
    // Check semantic module availability and accelerator info
    const spawnStart = Date.now();
    console.log('[PERF][CodexLens] checkSemanticStatus spawning Python...');
    return new Promise((resolve) => {
        const checkCode = `
import sys
import json
try:
    import codexlens.semantic as semantic
    SEMANTIC_AVAILABLE = bool(getattr(semantic, "SEMANTIC_AVAILABLE", False))
    SEMANTIC_BACKEND = getattr(semantic, "SEMANTIC_BACKEND", None)
    LITELLM_AVAILABLE = bool(getattr(semantic, "LITELLM_AVAILABLE", False))
    result = {
        "available": SEMANTIC_AVAILABLE,
        "backend": SEMANTIC_BACKEND if SEMANTIC_AVAILABLE else None,
        "litellm_available": LITELLM_AVAILABLE,
    }

    # Get ONNX providers for accelerator info
    try:
        import onnxruntime
        providers = onnxruntime.get_available_providers()
        result["providers"] = providers

        # Determine accelerator type
        if "CUDAExecutionProvider" in providers or "TensorrtExecutionProvider" in providers:
            result["accelerator"] = "CUDA"
        elif "DmlExecutionProvider" in providers:
            result["accelerator"] = "DirectML"
        elif "CoreMLExecutionProvider" in providers:
            result["accelerator"] = "CoreML"
        elif "ROCMExecutionProvider" in providers:
            result["accelerator"] = "ROCm"
        else:
            result["accelerator"] = "CPU"
    except:
        result["providers"] = []
        result["accelerator"] = "CPU"

    print(json.dumps(result))
except Exception as e:
    print(json.dumps({"available": False, "error": str(e)}))
`;
        const child = spawn(getCodexLensPython(), ['-c', checkCode], {
            stdio: ['ignore', 'pipe', 'pipe'],
            timeout: 15000,
        });
        let stdout = '';
        let stderr = '';
        child.stdout.on('data', (data) => {
            stdout += data.toString();
        });
        child.stderr.on('data', (data) => {
            stderr += data.toString();
        });
        child.on('close', (code) => {
            const output = stdout.trim();
            try {
                const result = JSON.parse(output);
                console.log(`[PERF][CodexLens] checkSemanticStatus Python spawn: ${Date.now() - spawnStart}ms | TOTAL: ${Date.now() - funcStart}ms | available: ${result.available}`);
                const status = {
                    available: result.available || false,
                    backend: result.backend,
                    accelerator: result.accelerator || 'CPU',
                    providers: result.providers || [],
                    litellmAvailable: result.litellm_available || false,
                    error: result.error
                };
                // Cache the result
                semanticStatusCache = { status, timestamp: Date.now() };
                resolve(status);
            }
            catch {
                console.log(`[PERF][CodexLens] checkSemanticStatus PARSE ERROR: ${Date.now() - funcStart}ms`);
                const errorStatus = { available: false, error: output || stderr || 'Unknown error' };
                semanticStatusCache = { status: errorStatus, timestamp: Date.now() };
                resolve(errorStatus);
            }
        });
        child.on('error', (err) => {
            console.log(`[PERF][CodexLens] checkSemanticStatus ERROR: ${Date.now() - funcStart}ms`);
            const errorStatus = { available: false, error: `Check failed: ${err.message}` };
            semanticStatusCache = { status: errorStatus, timestamp: Date.now() };
            resolve(errorStatus);
        });
    });
}
/**
 * Ensure LiteLLM embedder dependencies are available in the CodexLens venv.
 * Installs ccw-litellm into the venv if needed.
 */
async function ensureLiteLLMEmbedderReady() {
    // Ensure CodexLens venv exists and CodexLens is installed.
    const readyStatus = await ensureReady();
    if (!readyStatus.ready) {
        return { success: false, error: readyStatus.error || 'CodexLens not ready' };
    }
    // Check if ccw_litellm can be imported
    const importStatus = await new Promise((resolve) => {
        const child = spawn(getCodexLensPython(), ['-c', 'import ccw_litellm; print("OK")'], {
            stdio: ['ignore', 'pipe', 'pipe'],
            timeout: 15000,
        });
        let stderr = '';
        child.stderr.on('data', (data) => {
            stderr += data.toString();
        });
        child.on('close', (code) => {
            resolve({ ok: code === 0, error: stderr.trim() || undefined });
        });
        child.on('error', (err) => {
            resolve({ ok: false, error: err.message });
        });
    });
    if (importStatus.ok) {
        return { success: true };
    }
    console.log('[CodexLens] Installing ccw-litellm for LiteLLM embedding backend...');
    // Find local ccw-litellm package path (only in development, not from node_modules)
    const localPath = findLocalCcwLitellmPath();
    // Priority: Use UV if available (faster, better dependency resolution)
    if (await isUvAvailable()) {
        console.log('[CodexLens] Using UV for ccw-litellm installation...');
        try {
            const uv = createCodexLensUvManager();
            // Ensure venv exists
            if (!uv.isVenvValid()) {
                const venvResult = await uv.createVenv();
                if (!venvResult.success) {
                    console.log('[CodexLens] UV venv creation failed, falling back to pip:', venvResult.error);
                    // Fall through to pip fallback
                }
            }
            if (uv.isVenvValid()) {
                let uvResult;
                if (localPath) {
                    console.log(`[CodexLens] Installing ccw-litellm from local path with UV: ${localPath}`);
                    uvResult = await uv.installFromProject(localPath);
                }
                else {
                    console.log('[CodexLens] Installing ccw-litellm from PyPI with UV...');
                    uvResult = await uv.install(['ccw-litellm']);
                }
                if (uvResult.success) {
                    return { success: true };
                }
                console.log('[CodexLens] UV install failed, falling back to pip:', uvResult.error);
            }
        }
        catch (uvErr) {
            console.log('[CodexLens] UV error, falling back to pip:', uvErr.message);
        }
    }
    // Fallback: Use pip for installation
    const pipPath = getCodexLensPip();
    try {
        if (localPath) {
            console.log(`[CodexLens] Installing ccw-litellm from local path with pip: ${localPath}`);
            execSync(`"${pipPath}" install -e "${localPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
        }
        else {
            console.log('[CodexLens] Installing ccw-litellm from PyPI with pip...');
            execSync(`"${pipPath}" install ccw-litellm`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
        }
        return { success: true };
    }
    catch (err) {
        return { success: false, error: `Failed to install ccw-litellm: ${err.message}` };
    }
}
/**
 * Mapping from GPU mode to codexlens extras for UV installation
 */
const GPU_MODE_EXTRAS = {
    cpu: ['semantic'],
    cuda: ['semantic-gpu'],
    directml: ['semantic-directml'],
};
/**
 * Check Python environment in venv for DirectML compatibility
 * DirectML requires: 64-bit Python, version 3.8-3.12
 */
async function checkPythonEnvForDirectML() {
    const pythonPath = getCodexLensPython();
    if (!existsSync(pythonPath)) {
        return { version: '', majorMinor: '', architecture: 0, compatible: false, error: 'Python not found in venv' };
    }
    try {
        // Get Python version and architecture in one call
        // Use % formatting instead of f-string to avoid Windows shell escaping issues with curly braces
        const checkScript = `import sys, struct; print('%d.%d.%d|%d' % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro, struct.calcsize('P') * 8))`;
        const result = execSync(`"${pythonPath}" -c "${checkScript}"`, { encoding: 'utf-8', timeout: 10000 }).trim();
        const [version, archStr] = result.split('|');
        const architecture = parseInt(archStr, 10);
        const [major, minor] = version.split('.').map(Number);
        const majorMinor = `${major}.${minor}`;
        // DirectML wheels available for Python 3.8-3.12, 64-bit only
        const versionCompatible = major === 3 && minor >= 8 && minor <= 12;
        const archCompatible = architecture === 64;
        const compatible = versionCompatible && archCompatible;
        let error;
        if (!archCompatible) {
            error = `Python is ${architecture}-bit. onnxruntime-directml requires 64-bit Python. Please reinstall Python as 64-bit.`;
        }
        else if (!versionCompatible) {
            error = `Python ${majorMinor} is not supported. onnxruntime-directml requires Python 3.8-3.12.`;
        }
        return { version, majorMinor, architecture, compatible, error };
    }
    catch (e) {
        return { version: '', majorMinor: '', architecture: 0, compatible: false, error: `Failed to check Python: ${e.message}` };
    }
}
/**
 * Detect available GPU acceleration
 * @returns Detected GPU mode and info
 */
async function detectGpuSupport() {
    const available = ['cpu'];
    let detectedInfo = 'CPU only';
    // Check for NVIDIA GPU (CUDA)
    try {
        if (process.platform === 'win32') {
            execSync('nvidia-smi', { stdio: 'pipe', timeout: EXEC_TIMEOUTS.SYSTEM_INFO });
            available.push('cuda');
            detectedInfo = 'NVIDIA GPU detected (CUDA available)';
        }
        else {
            execSync('which nvidia-smi', { stdio: 'pipe', timeout: EXEC_TIMEOUTS.SYSTEM_INFO });
            available.push('cuda');
            detectedInfo = 'NVIDIA GPU detected (CUDA available)';
        }
    }
    catch {
        // NVIDIA not available
    }
    // On Windows, DirectML requires 64-bit Python 3.8-3.12
    let pythonEnv;
    if (process.platform === 'win32') {
        pythonEnv = await checkPythonEnvForDirectML();
        if (pythonEnv.compatible) {
            available.push('directml');
            if (available.includes('cuda')) {
                detectedInfo = 'NVIDIA GPU detected (CUDA & DirectML available)';
            }
            else {
                detectedInfo = 'DirectML available (Windows GPU acceleration)';
            }
        }
        else if (pythonEnv.error) {
            // DirectML not available due to Python environment
            console.log(`[CodexLens] DirectML unavailable: ${pythonEnv.error}`);
        }
    }
    // Recommend best available mode
    let recommendedMode = 'cpu';
    if (process.platform === 'win32' && available.includes('directml')) {
        recommendedMode = 'directml'; // DirectML is easier on Windows (no CUDA toolkit needed)
    }
    else if (available.includes('cuda')) {
        recommendedMode = 'cuda';
    }
    return { mode: recommendedMode, available, info: detectedInfo, pythonEnv };
}
/**
 * Bootstrap CodexLens venv using UV (fast package manager)
 * @param gpuMode - GPU acceleration mode for semantic search
 * @returns Bootstrap result
 */
async function bootstrapWithUv(gpuMode = 'cpu') {
    console.log('[CodexLens] Bootstrapping with UV package manager...');
    // Ensure UV is installed
    const uvInstalled = await ensureUvInstalled();
    if (!uvInstalled) {
        return { success: false, error: 'Failed to install UV package manager' };
    }
    // Create UV manager for CodexLens
    const uv = createCodexLensUvManager();
    // Create venv if not exists
    if (!uv.isVenvValid()) {
        console.log('[CodexLens] Creating virtual environment with UV...');
        const createResult = await uv.createVenv();
        if (!createResult.success) {
            return { success: false, error: `Failed to create venv: ${createResult.error}` };
        }
    }
    // Find local codex-lens package (only in development, not from node_modules)
    const codexLensPath = findLocalCodexLensPath();
    // Determine extras based on GPU mode
    const extras = GPU_MODE_EXTRAS[gpuMode];
    if (!codexLensPath) {
        // codex-lens is a local-only package, not published to PyPI
        const errorMsg = `Cannot find codex-lens directory for local installation.\n\n` +
            `codex-lens is a local development package (not published to PyPI) and must be installed from local files.\n\n` +
            `To fix this:\n` +
            `1. Ensure the 'codex-lens' directory exists in your project root\n` +
            `   Expected location: D:\\Claude_dms3\\codex-lens\n` +
            `2. Verify pyproject.toml exists: D:\\Claude_dms3\\codex-lens\\pyproject.toml\n` +
            `3. Run ccw from the correct working directory (e.g., D:\\Claude_dms3)\n` +
            `4. Or manually install: cd D:\\Claude_dms3\\codex-lens && pip install -e .[${extras.join(',')}]`;
        return { success: false, error: errorMsg };
    }
    console.log(`[CodexLens] Installing from local path with UV: ${codexLensPath}`);
    console.log(`[CodexLens] Extras: ${extras.join(', ')}`);
    const installResult = await uv.installFromProject(codexLensPath, extras);
    if (!installResult.success) {
        return { success: false, error: `Failed to install codex-lens: ${installResult.error}` };
    }
    // Clear cache after successful installation
    clearVenvStatusCache();
    clearSemanticStatusCache();
    console.log(`[CodexLens] Bootstrap with UV complete (${gpuMode} mode)`);
    return { success: true, message: `Installed with UV (${gpuMode} mode)` };
}
/**
 * Install semantic search dependencies using UV (fast package manager)
 * UV automatically handles ONNX Runtime conflicts
 * @param gpuMode - GPU acceleration mode: 'cpu', 'cuda', or 'directml'
 * @returns Bootstrap result
 */
async function installSemanticWithUv(gpuMode = 'cpu') {
    console.log('[CodexLens] Installing semantic dependencies with UV...');
    // First check if CodexLens is installed
    const venvStatus = await checkVenvStatus();
    if (!venvStatus.ready) {
        return { success: false, error: 'CodexLens not installed. Install CodexLens first.' };
    }
    // Check Python environment compatibility for DirectML
    if (gpuMode === 'directml') {
        const pythonEnv = await checkPythonEnvForDirectML();
        if (!pythonEnv.compatible) {
            const errorDetails = pythonEnv.error || 'Unknown compatibility issue';
            return {
                success: false,
                error: `DirectML installation failed: ${errorDetails}\n\nTo fix this:\n1. Uninstall current Python\n2. Install 64-bit Python 3.10, 3.11, or 3.12 from python.org\n3. Delete ~/.codexlens/venv folder\n4. Reinstall CodexLens`,
            };
        }
        console.log(`[CodexLens] Python ${pythonEnv.version} (${pythonEnv.architecture}-bit) - DirectML compatible`);
    }
    // Create UV manager
    const uv = createCodexLensUvManager();
    // Find local codex-lens package (only in development, not from node_modules)
    const codexLensPath = findLocalCodexLensPath();
    // Determine extras based on GPU mode
    const extras = GPU_MODE_EXTRAS[gpuMode];
    const modeDescription = gpuMode === 'cuda'
        ? 'NVIDIA CUDA GPU acceleration'
        : gpuMode === 'directml'
            ? 'Windows DirectML GPU acceleration'
            : 'CPU (ONNX Runtime)';
    console.log(`[CodexLens] Mode: ${modeDescription}`);
    console.log(`[CodexLens] Extras: ${extras.join(', ')}`);
    // Install with extras - UV handles dependency conflicts automatically
    if (!codexLensPath) {
        // codex-lens is a local-only package, not published to PyPI
        const errorMsg = `Cannot find codex-lens directory for local installation.\n\n` +
            `codex-lens is a local development package (not published to PyPI) and must be installed from local files.\n\n` +
            `To fix this:\n` +
            `1. Ensure the 'codex-lens' directory exists in your project root\n` +
            `2. Verify pyproject.toml exists in codex-lens directory\n` +
            `3. Run ccw from the correct working directory\n` +
            `4. Or manually install: cd codex-lens && pip install -e .[${extras.join(',')}]`;
        return { success: false, error: errorMsg };
    }
    console.log(`[CodexLens] Reinstalling from local path with semantic extras...`);
    const installResult = await uv.installFromProject(codexLensPath, extras);
    if (!installResult.success) {
        return { success: false, error: `Installation failed: ${installResult.error}` };
    }
    console.log(`[CodexLens] Semantic dependencies installed successfully (${gpuMode} mode)`);
    return { success: true, message: `Installed with ${modeDescription}` };
}
/**
 * Install semantic search dependencies with optional GPU acceleration
 * @param gpuMode - GPU acceleration mode: 'cpu', 'cuda', or 'directml'
 * @returns Bootstrap result
 */
async function installSemantic(gpuMode = 'cpu') {
    // Prefer UV if available
    if (await isUvAvailable()) {
        console.log('[CodexLens] Using UV for semantic installation...');
        return installSemanticWithUv(gpuMode);
    }
    // Fall back to pip logic...
    // First ensure CodexLens is installed
    const venvStatus = await checkVenvStatus();
    if (!venvStatus.ready) {
        return { success: false, error: 'CodexLens not installed. Install CodexLens first.' };
    }
    // Check Python environment compatibility for DirectML
    if (gpuMode === 'directml') {
        const pythonEnv = await checkPythonEnvForDirectML();
        if (!pythonEnv.compatible) {
            const errorDetails = pythonEnv.error || 'Unknown compatibility issue';
            return {
                success: false,
                error: `DirectML installation failed: ${errorDetails}\n\nTo fix this:\n1. Uninstall current Python\n2. Install 64-bit Python 3.10, 3.11, or 3.12 from python.org\n3. Delete ~/.codexlens/venv folder\n4. Reinstall CodexLens`
            };
        }
        console.log(`[CodexLens] Python ${pythonEnv.version} (${pythonEnv.architecture}-bit) - DirectML compatible`);
    }
    const pipPath = getCodexLensPip();
    // IMPORTANT: Uninstall all onnxruntime variants first to prevent conflicts
    // Having multiple onnxruntime packages causes provider detection issues
    const onnxVariants = ['onnxruntime', 'onnxruntime-gpu', 'onnxruntime-directml'];
    console.log(`[CodexLens] Cleaning up existing ONNX Runtime packages...`);
    for (const pkg of onnxVariants) {
        try {
            execSync(`"${pipPath}" uninstall ${pkg} -y`, { stdio: 'pipe', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
            console.log(`[CodexLens] Removed ${pkg}`);
        }
        catch {
            // Package not installed, ignore
        }
    }
    // Build package list based on GPU mode
    const packages = ['numpy>=1.24', 'fastembed>=0.5', 'hnswlib>=0.8.0'];
    let modeDescription = 'CPU (ONNX Runtime)';
    let onnxPackage = 'onnxruntime>=1.18.0'; // Default CPU
    if (gpuMode === 'cuda') {
        onnxPackage = 'onnxruntime-gpu>=1.18.0';
        modeDescription = 'NVIDIA CUDA GPU acceleration';
    }
    else if (gpuMode === 'directml') {
        onnxPackage = 'onnxruntime-directml>=1.18.0';
        modeDescription = 'Windows DirectML GPU acceleration';
    }
    return new Promise((resolve) => {
        console.log(`[CodexLens] Installing semantic search dependencies...`);
        console.log(`[CodexLens] Mode: ${modeDescription}`);
        console.log(`[CodexLens] ONNX Runtime: ${onnxPackage}`);
        console.log(`[CodexLens] Packages: ${packages.join(', ')}`);
        // Install ONNX Runtime first with force-reinstall to ensure clean state
        const installOnnx = spawn(pipPath, ['install', '--force-reinstall', onnxPackage], {
            stdio: ['ignore', 'pipe', 'pipe'],
            timeout: 600000, // 10 minutes for GPU packages
        });
        let onnxStdout = '';
        let onnxStderr = '';
        installOnnx.stdout.on('data', (data) => {
            onnxStdout += data.toString();
            const line = data.toString().trim();
            if (line.includes('Downloading') || line.includes('Installing')) {
                console.log(`[CodexLens] ${line}`);
            }
        });
        installOnnx.stderr.on('data', (data) => {
            onnxStderr += data.toString();
        });
        installOnnx.on('close', (onnxCode) => {
            if (onnxCode !== 0) {
                resolve({ success: false, error: `Failed to install ${onnxPackage}: ${onnxStderr || onnxStdout}` });
                return;
            }
            console.log(`[CodexLens] ${onnxPackage} installed successfully`);
            // Now install remaining packages
            const child = spawn(pipPath, ['install', ...packages], {
                stdio: ['ignore', 'pipe', 'pipe'],
                timeout: 600000,
            });
            let stdout = '';
            let stderr = '';
            child.stdout.on('data', (data) => {
                stdout += data.toString();
                const line = data.toString().trim();
                if (line.includes('Downloading') || line.includes('Installing') || line.includes('Collecting')) {
                    console.log(`[CodexLens] ${line}`);
                }
            });
            child.stderr.on('data', (data) => {
                stderr += data.toString();
            });
            child.on('close', (code) => {
                if (code === 0) {
                    // IMPORTANT: fastembed installs onnxruntime (CPU) as dependency, which conflicts
                    // with onnxruntime-directml/gpu. Reinstall the GPU version to ensure it takes precedence.
                    if (gpuMode !== 'cpu') {
                        try {
                            console.log(`[CodexLens] Reinstalling ${onnxPackage} to ensure GPU provider works...`);
                            execSync(`"${pipPath}" install --force-reinstall ${onnxPackage}`, { stdio: 'pipe', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
                            console.log(`[CodexLens] ${onnxPackage} reinstalled successfully`);
                        }
                        catch (e) {
                            console.warn(`[CodexLens] Warning: Failed to reinstall ${onnxPackage}: ${e.message}`);
                        }
                    }
                    console.log(`[CodexLens] Semantic dependencies installed successfully (${gpuMode} mode)`);
                    resolve({ success: true, message: `Installed with ${modeDescription}` });
                }
                else {
                    resolve({ success: false, error: `Installation failed: ${stderr || stdout}` });
                }
            });
            child.on('error', (err) => {
                resolve({ success: false, error: `Failed to run pip: ${err.message}` });
            });
        });
        installOnnx.on('error', (err) => {
            resolve({ success: false, error: `Failed to install ONNX Runtime: ${err.message}` });
        });
    });
}
/**
 * Bootstrap CodexLens venv with required packages
 * @returns Bootstrap result
 */
async function bootstrapVenv() {
    // Prefer UV if available (faster package resolution and installation)
    if (await isUvAvailable()) {
        console.log('[CodexLens] Using UV for bootstrap...');
        return bootstrapWithUv();
    }
    // Fall back to pip logic...
    // Ensure data directory exists
    const dataDir = getCodexLensDataDir();
    const venvDir = getCodexLensVenvDir();
    if (!existsSync(dataDir)) {
        mkdirSync(dataDir, { recursive: true });
    }
    // Create venv if not exists
    if (!existsSync(venvDir)) {
        try {
            console.log('[CodexLens] Creating virtual environment...');
            const pythonCmd = getSystemPython();
            execSync(`${pythonCmd} -m venv "${venvDir}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PROCESS_SPAWN });
        }
        catch (err) {
            return { success: false, error: `Failed to create venv: ${err.message}` };
        }
    }
    // Install codex-lens
    try {
        console.log('[CodexLens] Installing codex-lens package...');
        const pipPath = getCodexLensPip();
        // Try local path - codex-lens is local-only, not published to PyPI
        const codexLensPath = findLocalCodexLensPath();
        if (!codexLensPath) {
            // codex-lens is a local-only package, not published to PyPI
            const errorMsg = `Cannot find codex-lens directory for local installation.\n\n` +
                `codex-lens is a local development package (not published to PyPI) and must be installed from local files.\n\n` +
                `To fix this:\n` +
                `1. Ensure the 'codex-lens' directory exists in your project root\n` +
                `2. Verify pyproject.toml exists in codex-lens directory\n` +
                `3. Run ccw from the correct working directory\n` +
                `4. Or manually install: cd codex-lens && pip install -e .`;
            throw new Error(errorMsg);
        }
        console.log(`[CodexLens] Installing from local path: ${codexLensPath}`);
        execSync(`"${pipPath}" install -e "${codexLensPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
        // Clear cache after successful installation
        clearVenvStatusCache();
        clearSemanticStatusCache();
        return { success: true };
    }
    catch (err) {
        const errorMsg = `Failed to install codex-lens: ${err.message}\n\n` +
            `codex-lens is a local development package. To fix this:\n` +
            `1. Ensure the 'codex-lens' directory exists in your project root\n` +
            `2. Run the installation from the correct working directory\n` +
            `3. Or manually install: cd codex-lens && pip install -e .`;
        return { success: false, error: errorMsg };
    }
}
/**
 * Ensure CodexLens is ready to use
 * @returns Ready status
 */
async function ensureReady() {
    // Use cached result if already checked
    if (bootstrapChecked && bootstrapReady) {
        return { ready: true };
    }
    // Check current status
    const status = await checkVenvStatus();
    if (status.ready) {
        bootstrapChecked = true;
        bootstrapReady = true;
        return { ready: true, version: status.version };
    }
    // Attempt bootstrap
    const bootstrap = await bootstrapVenv();
    if (!bootstrap.success) {
        return { ready: false, error: bootstrap.error };
    }
    // Verify after bootstrap
    const recheck = await checkVenvStatus();
    bootstrapChecked = true;
    bootstrapReady = recheck.ready;
    return recheck;
}
/**
 * Parse progress info from CodexLens output
 * @param line - Output line to parse
 * @returns Progress info or null
 */
function parseProgressLine(line) {
    // Parse file processing progress: "Processing file X/Y: path"
    const fileMatch = line.match(/Processing file (\d+)\/(\d+):\s*(.+)/i);
    if (fileMatch) {
        const current = parseInt(fileMatch[1], 10);
        const total = parseInt(fileMatch[2], 10);
        return {
            stage: 'indexing',
            message: `Processing ${fileMatch[3]}`,
            percent: Math.round((current / total) * 80) + 10, // 10-90%
            filesProcessed: current,
            totalFiles: total,
        };
    }
    // Parse stage messages
    if (line.includes('Discovering files')) {
        return { stage: 'discover', message: 'Discovering files...', percent: 5 };
    }
    if (line.includes('Building index')) {
        return { stage: 'build', message: 'Building index...', percent: 10 };
    }
    if (line.includes('Extracting symbols')) {
        return { stage: 'symbols', message: 'Extracting symbols...', percent: 50 };
    }
    if (line.includes('Generating embeddings') || line.includes('Creating embeddings')) {
        return { stage: 'embeddings', message: 'Generating embeddings...', percent: 70 };
    }
    // Note: "Finalizing index" and "Building ANN" are handled separately below
    // Only match generic "Complete" here (not "Finalizing" which has specific handlers)
    // Parse indexed count: "Indexed X files" - FTS complete, but embeddings may follow
    const indexedMatch = line.match(/Indexed (\d+) files/i);
    if (indexedMatch) {
        return {
            stage: 'fts_complete', // Not 'complete' - embeddings generation may still be pending
            message: `Indexed ${indexedMatch[1]} files, generating embeddings...`,
            percent: 60, // FTS done, embeddings starting
            filesProcessed: parseInt(indexedMatch[1], 10),
        };
    }
    // Parse embedding batch progress: "Batch X: N files, M chunks"
    const batchMatch = line.match(/Batch (\d+):\s*(\d+) files,\s*(\d+) chunks/i);
    if (batchMatch) {
        return {
            stage: 'embeddings',
            message: `Embedding batch ${batchMatch[1]}: ${batchMatch[3]} chunks`,
            percent: 70, // Stay at 70% during embedding batches
        };
    }
    // Parse embedding progress with file count
    const embedProgressMatch = line.match(/Processing (\d+) files/i);
    if (embedProgressMatch && line.toLowerCase().includes('embed')) {
        return {
            stage: 'embeddings',
            message: `Processing ${embedProgressMatch[1]} files for embeddings`,
            percent: 75,
        };
    }
    // Parse finalizing ANN index
    if (line.includes('Finalizing index') || line.includes('Building ANN')) {
        return { stage: 'finalizing', message: 'Finalizing vector index...', percent: 90 };
    }
    // Parse embeddings complete message
    const embedCompleteMatch = line.match(/Embeddings complete:\s*(\d+)\s*chunks/i);
    if (embedCompleteMatch) {
        return {
            stage: 'embeddings_complete',
            message: `Embeddings complete: ${embedCompleteMatch[1]} chunks`,
            percent: 95,
        };
    }
    // Parse generic completion (but not "Embeddings complete" which is handled above)
    if (line.includes('Complete') && !line.toLowerCase().includes('embeddings complete')) {
        return { stage: 'complete', message: 'Complete', percent: 98 };
    }
    return null;
}
/**
 * Execute CodexLens CLI command with real-time progress updates
 * @param args - CLI arguments
 * @param options - Execution options
 * @returns Execution result
 */
async function executeCodexLens(args, options = {}) {
    const { timeout = 300000, cwd = process.cwd(), onProgress } = options; // Default 5 min
    // Ensure ready
    const readyStatus = await ensureReady();
    if (!readyStatus.ready) {
        return { success: false, error: readyStatus.error };
    }
    return new Promise((resolve) => {
        // SECURITY: Use spawn without shell to prevent command injection
        // Pass arguments directly - no manual quoting needed
        // spawn's cwd option handles drive changes correctly on Windows
        const spawnArgs = ['-m', 'codexlens', ...args];
        const child = spawn(getCodexLensPython(), spawnArgs, {
            cwd,
            shell: false, // CRITICAL: Prevent command injection
            timeout,
            // Ensure proper encoding on Windows
            env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
        });
        // Track indexing process for cancellation (only for init commands)
        const isIndexingCommand = args.includes('init');
        if (isIndexingCommand) {
            currentIndexingProcess = child;
            currentIndexingAborted = false;
        }
        let stdout = '';
        let stderr = '';
        let stdoutLineBuffer = '';
        let stderrLineBuffer = '';
        let timeoutHandle = null;
        let resolved = false;
        // Helper to safely resolve only once
        const safeResolve = (result) => {
            if (resolved)
                return;
            resolved = true;
            if (timeoutHandle) {
                clearTimeout(timeoutHandle);
                timeoutHandle = null;
            }
            // Clear indexing process tracking
            if (isIndexingCommand) {
                currentIndexingProcess = null;
            }
            resolve(result);
        };
        // Set up timeout handler
        if (timeout > 0) {
            timeoutHandle = setTimeout(() => {
                if (!resolved) {
                    child.kill('SIGTERM');
                    // Give it a moment to die gracefully, then force kill
                    setTimeout(() => {
                        if (!resolved) {
                            child.kill('SIGKILL');
                        }
                    }, 5000);
                    safeResolve({ success: false, error: 'Command timed out' });
                }
            }, timeout);
        }
        // Process stdout line by line for real-time progress
        child.stdout?.on('data', (data) => {
            const chunk = data.toString();
            stdoutLineBuffer += chunk;
            stdout += chunk;
            // Process complete lines
            const lines = stdoutLineBuffer.split('\n');
            stdoutLineBuffer = lines.pop() || ''; // Keep incomplete line in buffer
            for (const line of lines) {
                const trimmedLine = line.trim();
                if (trimmedLine && onProgress) {
                    const progress = parseProgressLine(trimmedLine);
                    if (progress) {
                        onProgress(progress);
                    }
                }
            }
        });
        // Collect stderr
        child.stderr?.on('data', (data) => {
            const chunk = data.toString();
            stderrLineBuffer += chunk;
            stderr += chunk;
            // Also check stderr for progress (some tools output progress to stderr)
            const lines = stderrLineBuffer.split('\n');
            stderrLineBuffer = lines.pop() || '';
            for (const line of lines) {
                const trimmedLine = line.trim();
                if (trimmedLine && onProgress) {
                    const progress = parseProgressLine(trimmedLine);
                    if (progress) {
                        onProgress(progress);
                    }
                }
            }
        });
        // Handle process errors (spawn failure)
        child.on('error', (err) => {
            safeResolve({ success: false, error: `Failed to start process: ${err.message}` });
        });
        // Handle process completion
        child.on('close', (code) => {
            // Process any remaining buffered content
            if (stdoutLineBuffer.trim() && onProgress) {
                const progress = parseProgressLine(stdoutLineBuffer.trim());
                if (progress) {
                    onProgress(progress);
                }
            }
            if (code === 0) {
                safeResolve({ success: true, output: stdout.trim() });
            }
            else {
                safeResolve({ success: false, error: stderr.trim() || `Process exited with code ${code}` });
            }
        });
    });
}
/**
 * Initialize CodexLens index for a directory
 * @param params - Parameters
 * @returns Execution result
 */
async function initIndex(params) {
    const { path = '.', languages } = params;
    // Use 'index init' subcommand (new CLI structure)
    const args = ['index', 'init', path];
    if (languages && languages.length > 0) {
        args.push('--language', languages.join(','));
    }
    return executeCodexLens(args, { cwd: path });
}
/**
 * Search code using CodexLens
 * @param params - Search parameters
 * @returns Execution result
 */
async function searchCode(params) {
    const { query, path = '.', limit = 20, mode = 'auto', enrich = false } = params;
    if (!query) {
        return { success: false, error: 'Query is required for search action' };
    }
    // Map MCP mode names to CLI mode names
    const modeMap = {
        'text': 'exact',
        'semantic': 'pure-vector',
        'auto': 'auto',
        'exact': 'exact',
        'fuzzy': 'fuzzy',
        'hybrid': 'hybrid',
        'vector': 'vector',
        'pure-vector': 'pure-vector',
    };
    const cliMode = modeMap[mode] || 'auto';
    const args = ['search', query, '--limit', limit.toString(), '--mode', cliMode, '--json'];
    if (enrich) {
        args.push('--enrich');
    }
    const result = await executeCodexLens(args, { cwd: path });
    if (result.success && result.output) {
        try {
            result.results = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Search code and return only file paths
 * @param params - Search parameters
 * @returns Execution result
 */
async function searchFiles(params) {
    const { query, path = '.', limit = 20, mode = 'auto', enrich = false } = params;
    if (!query) {
        return { success: false, error: 'Query is required for search_files action' };
    }
    // Map MCP mode names to CLI mode names
    const modeMap = {
        'text': 'exact',
        'semantic': 'pure-vector',
        'auto': 'auto',
        'exact': 'exact',
        'fuzzy': 'fuzzy',
        'hybrid': 'hybrid',
        'vector': 'vector',
        'pure-vector': 'pure-vector',
    };
    const cliMode = modeMap[mode] || 'auto';
    const args = ['search', query, '--files-only', '--limit', limit.toString(), '--mode', cliMode, '--json'];
    if (enrich) {
        args.push('--enrich');
    }
    const result = await executeCodexLens(args, { cwd: path });
    if (result.success && result.output) {
        try {
            result.files = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Extract symbols from a file
 * @param params - Parameters
 * @returns Execution result
 */
async function extractSymbols(params) {
    const { file } = params;
    if (!file) {
        return { success: false, error: 'File is required for symbol action' };
    }
    const args = ['symbol', file, '--json'];
    const result = await executeCodexLens(args);
    if (result.success && result.output) {
        try {
            result.symbols = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Get index status
 * @param params - Parameters
 * @returns Execution result
 */
async function getStatus(params) {
    const { path = '.' } = params;
    const args = ['status', '--json'];
    const result = await executeCodexLens(args, { cwd: path });
    if (result.success && result.output) {
        try {
            result.status = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Show configuration
 * @param params - Parameters
 * @returns Execution result
 */
async function configShow() {
    const args = ['config', 'show', '--json'];
    const result = await executeCodexLens(args);
    if (result.success && result.output) {
        try {
            result.config = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Set configuration value
 * @param params - Parameters
 * @returns Execution result
 */
async function configSet(params) {
    const { key, value } = params;
    if (!key) {
        return { success: false, error: 'key is required for config_set action' };
    }
    if (!value) {
        return { success: false, error: 'value is required for config_set action' };
    }
    const args = ['config', 'set', key, value, '--json'];
    const result = await executeCodexLens(args);
    if (result.success && result.output) {
        try {
            result.config = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Migrate indexes to new location
 * @param params - Parameters
 * @returns Execution result
 */
async function configMigrate(params) {
    const { newPath } = params;
    if (!newPath) {
        return { success: false, error: 'newPath is required for config_migrate action' };
    }
    const args = ['config', 'migrate', newPath, '--json'];
    const result = await executeCodexLens(args, { timeout: 300000 }); // 5 min for migration
    if (result.success && result.output) {
        try {
            result.config = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
/**
 * Clean indexes
 * @param params - Parameters
 * @returns Execution result
 */
async function cleanIndexes(params) {
    const { path, all } = params;
    const args = ['clean'];
    if (all) {
        args.push('--all');
    }
    else if (path) {
        args.push(path);
    }
    args.push('--json');
    const result = await executeCodexLens(args);
    if (result.success && result.output) {
        try {
            result.cleanResult = JSON.parse(result.output);
            delete result.output;
        }
        catch {
            // Keep raw output if JSON parse fails
        }
    }
    return result;
}
// Tool schema for MCP
export const schema = {
    name: 'codex_lens',
    description: `CodexLens - Code indexing and semantic search.

Usage:
  codex_lens(action="init", path=".")           # Index directory (auto-generates embeddings if available)
  codex_lens(action="search", query="func")     # Search code (auto: hybrid if embeddings exist, else exact)
  codex_lens(action="search", query="func", mode="hybrid")  # Force hybrid search
  codex_lens(action="search_files", query="x")  # Search, return paths only

Graph Enrichment:
  codex_lens(action="search", query="func", enrich=true)  # Enrich results with code relationships

Search Modes:
  - auto: Auto-detect (hybrid if embeddings exist, exact otherwise) [default]
  - exact/text: Exact FTS for code identifiers
  - hybrid: Exact + Fuzzy + Vector fusion (best results, requires embeddings)
  - fuzzy: Typo-tolerant search
  - vector: Semantic + keyword
  - pure-vector/semantic: Pure semantic search

Note: For advanced operations (config, status, clean), use CLI directly: codexlens --help`,
    inputSchema: {
        type: 'object',
        properties: {
            action: {
                type: 'string',
                enum: [
                    'init',
                    'search',
                    'search_files',
                    'status',
                    'symbol',
                    'check',
                    'update',
                    'bootstrap',
                ],
                description: 'Action to perform: init/update (index directory), search (search code), search_files (search files only), status (index status), symbol (extract symbols), check (check if ready), bootstrap (setup venv)',
            },
            path: {
                type: 'string',
                description: 'Target directory path (for init, search, search_files). Defaults to current directory.',
            },
            query: {
                type: 'string',
                description: 'Search query (required for search and search_files actions)',
            },
            mode: {
                type: 'string',
                enum: ['auto', 'text', 'semantic', 'exact', 'fuzzy', 'hybrid', 'vector', 'pure-vector'],
                description: 'Search mode: auto (default, hybrid if embeddings exist), text/exact (FTS), hybrid (best), fuzzy, vector, semantic/pure-vector',
                default: 'auto',
            },
            format: {
                type: 'string',
                enum: ['json', 'text', 'pretty'],
                description: 'Output format: json (default), text, pretty',
                default: 'json',
            },
            languages: {
                type: 'array',
                items: { type: 'string' },
                description: 'Languages to index (for init action). Example: ["javascript", "typescript", "python"]',
            },
            limit: {
                type: 'number',
                description: 'Maximum number of search results (for search and search_files actions)',
                default: 20,
            },
            enrich: {
                type: 'boolean',
                description: 'Enrich search results with code graph relationships (calls, imports)',
                default: false,
            },
        },
        required: ['action'],
    },
};
// Handler function
export async function handler(params) {
    const parsed = ParamsSchema.safeParse(params);
    if (!parsed.success) {
        return { success: false, error: `Invalid params: ${parsed.error.message}` };
    }
    const { action } = parsed.data;
    try {
        let result;
        switch (action) {
            case 'init':
                result = await initIndex(parsed.data);
                break;
            case 'search':
                result = await searchCode(parsed.data);
                break;
            case 'search_files':
                result = await searchFiles(parsed.data);
                break;
            case 'status':
                result = await getStatus(parsed.data);
                break;
            case 'symbol':
                result = await extractSymbols(parsed.data);
                break;
            case 'check':
                const checkStatus = await ensureReady();
                result = {
                    success: checkStatus.ready,
                    ready: checkStatus.ready,
                    version: checkStatus.version,
                    error: checkStatus.error,
                };
                break;
            case 'update':
                // Update is an alias for init (incremental update)
                result = await initIndex(parsed.data);
                break;
            case 'bootstrap':
                const bootstrapResult = await bootstrapVenv();
                result = {
                    success: bootstrapResult.success,
                    message: bootstrapResult.message,
                    error: bootstrapResult.error,
                };
                break;
            default:
                throw new Error(`Unknown action: ${action}. Valid actions: init, search, search_files, status, symbol, check, update, bootstrap`);
        }
        return result.success ? { success: true, result } : { success: false, error: result.error };
    }
    catch (error) {
        return { success: false, error: error.message };
    }
}
/**
 * Uninstall CodexLens by removing the venv directory
 * @returns Uninstall result
 */
async function uninstallCodexLens() {
    try {
        // Check if venv exists
        if (!existsSync(getCodexLensVenvDir())) {
            return { success: false, error: 'CodexLens not installed (venv not found)' };
        }
        console.log('[CodexLens] Uninstalling CodexLens...');
        // On Windows, kill any Python processes that might be holding locks on .db files
        if (process.platform === 'win32') {
            console.log('[CodexLens] Killing any CodexLens Python processes...');
            const { execSync } = await import('child_process');
            try {
                // Kill any python processes from our venv that might be holding file locks
                execSync(`taskkill /F /IM python.exe /FI "MODULES eq sqlite3" 2>nul`, { stdio: 'ignore', timeout: EXEC_TIMEOUTS.SYSTEM_INFO });
            }
            catch {
                // Ignore errors - no processes to kill
            }
            // Small delay to allow file handles to be released
            await new Promise(resolve => setTimeout(resolve, 500));
        }
        const dataDir = getCodexLensDataDir();
        console.log(`[CodexLens] Removing directory: ${dataDir}`);
        // Remove the entire .codexlens directory with retry logic for locked files
        const fs = await import('fs');
        const path = await import('path');
        // Helper function to remove directory with retries (Windows EBUSY workaround)
        const removeWithRetry = async (dirPath, maxRetries = 3, delay = 1000) => {
            for (let attempt = 1; attempt <= maxRetries; attempt++) {
                try {
                    fs.rmSync(dirPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 500 });
                    return;
                }
                catch (err) {
                    if (err.code === 'EBUSY' || err.code === 'EPERM' || err.code === 'ENOTEMPTY') {
                        console.log(`[CodexLens] Retry ${attempt}/${maxRetries} - file locked, waiting...`);
                        if (attempt < maxRetries) {
                            // On Windows, try to forcefully release file handles
                            if (process.platform === 'win32' && err.path) {
                                try {
                                    const { execSync } = await import('child_process');
                                    // Try to close handles on the specific file
                                    execSync(`handle -c ${err.path} -y 2>nul`, { stdio: 'ignore', timeout: EXEC_TIMEOUTS.SYSTEM_INFO });
                                }
                                catch {
                                    // handle.exe may not be installed, ignore
                                }
                            }
                            await new Promise(resolve => setTimeout(resolve, delay));
                            continue;
                        }
                    }
                    throw err;
                }
            }
        };
        await removeWithRetry(dataDir);
        // Reset bootstrap cache
        bootstrapChecked = false;
        bootstrapReady = false;
        clearVenvStatusCache();
        clearSemanticStatusCache();
        console.log('[CodexLens] CodexLens uninstalled successfully');
        return { success: true, message: 'CodexLens uninstalled successfully' };
    }
    catch (err) {
        const errorMsg = err.message;
        // Provide helpful message for Windows users with locked files
        if (errorMsg.includes('EBUSY') || errorMsg.includes('resource busy')) {
            return {
                success: false,
                error: `Failed to uninstall CodexLens: Files are locked. Please close any applications using CodexLens indexes (e.g., Claude Code, VS Code) and try again. Details: ${errorMsg}`
            };
        }
        return { success: false, error: `Failed to uninstall CodexLens: ${errorMsg}` };
    }
}
/**
 * Cancel the currently running indexing process
 * @returns Result indicating if cancellation was successful
 */
function cancelIndexing() {
    if (!currentIndexingProcess) {
        return { success: false, error: 'No indexing process is currently running' };
    }
    if (currentIndexingAborted) {
        return { success: false, error: 'Indexing process is already being cancelled' };
    }
    try {
        currentIndexingAborted = true;
        // Send SIGTERM first for graceful shutdown
        if (process.platform === 'win32') {
            // On Windows, use taskkill to kill the process tree
            const { execSync } = require('child_process');
            try {
                execSync(`taskkill /pid ${currentIndexingProcess.pid} /T /F`, { stdio: 'ignore', timeout: EXEC_TIMEOUTS.SYSTEM_INFO });
            }
            catch {
                // Process may have already exited
            }
        }
        else {
            // On Unix, send SIGTERM
            currentIndexingProcess.kill('SIGTERM');
            // Force kill after 3 seconds if still running
            setTimeout(() => {
                if (currentIndexingProcess) {
                    currentIndexingProcess.kill('SIGKILL');
                }
            }, 3000);
        }
        console.log('[CodexLens] Indexing process cancelled');
        return { success: true, message: 'Indexing cancelled successfully' };
    }
    catch (err) {
        return { success: false, error: `Failed to cancel indexing: ${err.message}` };
    }
}
/**
 * Check if an indexing process is currently running
 * @returns True if indexing is in progress
 */
function isIndexingInProgress() {
    return currentIndexingProcess !== null && !currentIndexingAborted;
}
// Export for direct usage
export { ensureReady, executeCodexLens, checkVenvStatus, bootstrapVenv, checkSemanticStatus, ensureLiteLLMEmbedderReady, installSemantic, detectGpuSupport, uninstallCodexLens, cancelIndexing, isIndexingInProgress, 
// UV-based installation functions
bootstrapWithUv, installSemanticWithUv, };
// Export Python path for direct spawn usage (e.g., watcher)
export function getVenvPythonPath() {
    return getCodexLensPython();
}
// Backward-compatible export for tests
export const codexLensTool = {
    name: schema.name,
    description: schema.description,
    parameters: schema.inputSchema,
    execute: async (params) => {
        const result = await handler(params);
        // Return the result directly - tests expect {success: boolean, ...} format
        return result.success ? result.result : { success: false, error: result.error };
    }
};
//# sourceMappingURL=codex-lens.js.map