// llm-detector/detectors.ts — Provider, framework, vectorstore, RAG, and tool-use detection
import { join } from 'node:path';
import { glob } from 'glob';
import { LLM_PROVIDER_NPM, LLM_PROVIDER_ENV, LLM_PROVIDER_PYTHON, LLM_FRAMEWORK_NPM, VECTORSTORE_NPM, VECTORSTORE_PYTHON, RAG_CODE_PATTERNS, TOOL_USE_PATTERNS, FINE_TUNING_FILES, FINE_TUNING_PACKAGES_PYTHON, FINE_TUNING_PACKAGES_NPM, } from './provider-maps.js';
import { readFileOrNull, readPackageJson } from './file-readers.js';
/** Detect LLM provider from npm package.json */
export function detectProviderFromNpm(allDeps) {
    for (const [provider, packages] of Object.entries(LLM_PROVIDER_NPM)) {
        if (packages.some((p) => p in allDeps)) {
            return provider;
        }
    }
    return null;
}
/** Detect LLM provider from Python deps string */
export function detectProviderFromPython(pythonDeps) {
    for (const [provider, packages] of Object.entries(LLM_PROVIDER_PYTHON)) {
        if (packages.some((p) => pythonDeps.toLowerCase().includes(p.toLowerCase()))) {
            return provider;
        }
    }
    /* v8 ignore next 1 */
    return null;
}
/** Detect LLM provider from env files */
export async function detectProviderFromEnv(projectPath) {
    const envFiles = ['.env', '.env.example', '.env.local', '.env.production'];
    for (const envFile of envFiles) {
        const content = await readFileOrNull(join(projectPath, envFile));
        if (!content) {
            continue;
        }
        for (const [provider, envVars] of Object.entries(LLM_PROVIDER_ENV)) {
            if (envVars.some((v) => content.includes(v))) {
                return provider;
            }
        }
    }
    return null;
}
/** Detect LLM framework from npm deps */
export function detectFrameworkFromNpm(allDeps) {
    for (const [framework, packages] of Object.entries(LLM_FRAMEWORK_NPM)) {
        if (packages.some((p) => p in allDeps)) {
            return framework;
        }
    }
    return 'none';
}
/** Detect Python LLM framework */
export function detectFrameworkFromPython(pythonDeps) {
    const lower = pythonDeps.toLowerCase();
    if (lower.includes('langchain')) {
        return 'langchain';
    }
    if (lower.includes('llama-index') ||
        lower.includes('llama_index') ||
        lower.includes('llamaindex')) {
        return 'llamaindex';
    }
    return 'none';
}
/** Detect vector store from npm deps */
export function detectVectorStoreFromNpm(allDeps) {
    for (const [store, packages] of Object.entries(VECTORSTORE_NPM)) {
        if (packages.some((p) => p in allDeps)) {
            return store;
        }
    }
    return null;
}
/** Detect vector store from Python deps */
export function detectVectorStoreFromPython(pythonDeps) {
    const lower = pythonDeps.toLowerCase();
    for (const [store, packages] of Object.entries(VECTORSTORE_PYTHON)) {
        if (packages.some((p) => lower.includes(p.toLowerCase()))) {
            return store;
        }
    }
    return null;
}
/** Scan source files for RAG patterns */
export async function detectRAGFromSource(projectPath) {
    try {
        const files = await glob('**/*.{ts,js,py,go}', {
            cwd: projectPath,
            nodir: true,
            maxDepth: 4,
            ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**', 'vendor/**'],
        });
        for (const f of files.slice(0, 40)) {
            const content = await readFileOrNull(join(projectPath, f));
            if (content && RAG_CODE_PATTERNS.some((p) => content.includes(p))) {
                return true;
            }
        }
    }
    catch {
        // ignore
    }
    return false;
}
/** Scan source files for tool use patterns */
export async function detectToolUseFromSource(projectPath) {
    // Check .mcp.json files first
    const mcpJson = await readFileOrNull(join(projectPath, '.mcp.json'));
    if (mcpJson) {
        return true;
    }
    try {
        const files = await glob('**/*.{ts,js,py,go,json}', {
            cwd: projectPath,
            nodir: true,
            maxDepth: 4,
            ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**', 'vendor/**'],
        });
        for (const f of files.slice(0, 40)) {
            const content = await readFileOrNull(join(projectPath, f));
            /* v8 ignore next 2 */
            if (!content) {
                continue;
            }
            const hasLlmContext = content.includes('"model":') ||
                content.includes('"messages":') ||
                content.includes('chat.completions') ||
                content.includes('messages.create');
            if (hasLlmContext && TOOL_USE_PATTERNS.some((p) => content.includes(p))) {
                return true;
            }
        }
    }
    catch {
        // ignore
    }
    return false;
}
/** Check for MCP SDK dependency */
export function hasMcpSdk(allDeps) {
    return '@modelcontextprotocol/sdk' in allDeps;
}
/** Detect fine-tuning indicators */
export async function detectFineTuning(projectPath, pythonDeps) {
    for (const f of FINE_TUNING_FILES) {
        const content = await readFileOrNull(join(projectPath, f));
        if (content !== null) {
            return true;
        }
    }
    if (pythonDeps && FINE_TUNING_PACKAGES_PYTHON.some((p) => pythonDeps.toLowerCase().includes(p))) {
        return true;
    }
    /* v8 ignore next 3 */
    if (FINE_TUNING_PACKAGES_NPM.length > 0) {
        return false;
    }
    return false;
}
/** Detect own embeddings (not just calling an API) */
export async function detectEmbeddingsOwn(projectPath, pythonDeps) {
    if (pythonDeps &&
        (pythonDeps.toLowerCase().includes('sentence-transformers') ||
            pythonDeps.toLowerCase().includes('transformers'))) {
        return true;
    }
    const pkg = await readPackageJson(projectPath);
    if (pkg) {
        const all = { ...pkg.deps, ...pkg.devDeps };
        if ('@xenova/transformers' in all || 'onnxruntime-node' in all) {
            return true;
        }
    }
    return false;
}
//# sourceMappingURL=detectors.js.map