"use strict";
/**
 * agent-dir-scanner.ts
 *
 * Scan a directory for AI agent source files and classify them by provider
 * and framework. Pure function — no disk writes, only reads + directory walk.
 *
 * F-NEW-PRODUCT-AGENT-DIR-WRAPPER slice 1
 * @connexum/ai-governance
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.scanAgentDirectory = scanAgentDirectory;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const agent_signatures_1 = require("./agent-signatures");
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const SKIPPED_DIRS = new Set([
    'node_modules',
    '__pycache__',
    '.git',
    'dist',
    'build',
    '.venv',
    'venv',
]);
const PYTHON_EXTS = new Set(['.py']);
const TS_EXTS = new Set(['.ts', '.tsx', '.js', '.mjs', '.jsx']);
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function languageOf(ext) {
    if (ext === '.py')
        return 'python';
    if (ext === '.ts' || ext === '.tsx')
        return 'typescript';
    if (ext === '.js' || ext === '.mjs' || ext === '.jsx')
        return 'javascript';
    return null;
}
/**
 * Strip triple-quoted Python blocks (both """ and ''') from a line array so
 * that signature matches inside docstrings are not counted. Returns a copy of
 * lines where the interior of any triple-quoted block is replaced with blank
 * strings. Lines that START a triple-quote block and immediately END it on the
 * same line (e.g. """value""") are also neutralised.
 */
function stripPythonTripleQuotes(lines) {
    const result = [];
    let inTriple = false;
    let tripleChar = '';
    for (const line of lines) {
        if (!inTriple) {
            // Look for the start of a triple-quote block
            const dq = line.indexOf('"""');
            const sq = line.indexOf("'''");
            const startIdx = dq === -1 && sq === -1
                ? -1
                : dq === -1 ? sq : sq === -1 ? dq : Math.min(dq, sq);
            if (startIdx !== -1) {
                const delim = line[startIdx] === '"' ? '"""' : "'''";
                const afterStart = line.indexOf(delim, startIdx + 3);
                if (afterStart !== -1) {
                    // Opened and closed on the same line — blank the interior
                    result.push(line.slice(0, startIdx));
                }
                else {
                    // Started but not closed on this line — blank rest of line and enter block
                    result.push(line.slice(0, startIdx));
                    inTriple = true;
                    tripleChar = delim;
                }
            }
            else {
                result.push(line);
            }
        }
        else {
            // Inside a triple-quoted block — push blank until closing delimiter
            const closeIdx = line.indexOf(tripleChar);
            if (closeIdx !== -1) {
                result.push(''); // blank; closing delimiter line consumed
                inTriple = false;
                tripleChar = '';
            }
            else {
                result.push('');
            }
        }
    }
    return result;
}
/**
 * Strip single-line comments before regex matching.
 * Python: # ...
 * JS/TS: // ...
 */
function stripLineComment(line, lang) {
    if (lang === 'python') {
        const idx = line.indexOf('#');
        if (idx !== -1)
            return line.slice(0, idx);
    }
    else {
        const idx = line.indexOf('//');
        if (idx !== -1)
            return line.slice(0, idx);
    }
    return line;
}
function stableId(filePath, pattern) {
    const hash = crypto.createHash('sha256').update(filePath + '|' + pattern).digest('hex');
    return 'auto-' + hash.slice(0, 12);
}
function analyseFile(absolutePath, relPath, lang) {
    let raw;
    try {
        raw = fs.readFileSync(absolutePath, 'utf-8');
    }
    catch {
        return null;
    }
    const rawLines = raw.split('\n');
    // For Python: strip triple-quoted blocks first to avoid docstring false-positives
    const lines = lang === 'python' ? stripPythonTripleQuotes(rawLines) : rawLines;
    const lineRanges = [];
    let provider = null;
    let framework = null;
    let firstPattern = '';
    const providerSigs = lang === 'python' ? agent_signatures_1.PYTHON_SIGNATURES : agent_signatures_1.TYPESCRIPT_SIGNATURES;
    const frameworkSigs = lang === 'python'
        ? agent_signatures_1.PYTHON_FRAMEWORK_SIGNATURES
        : agent_signatures_1.TYPESCRIPT_FRAMEWORK_SIGNATURES;
    for (let i = 0; i < lines.length; i++) {
        const stripped = stripLineComment(lines[i], lang);
        // Check framework signatures first (they may also carry provider info)
        for (const sig of frameworkSigs) {
            if (sig.regex.test(stripped)) {
                if (!framework) {
                    framework = sig.framework;
                    if (sig.provider && !provider)
                        provider = sig.provider;
                    if (!firstPattern)
                        firstPattern = sig.regex.source;
                }
                lineRanges.push({ start: i + 1, end: i + 1, match: rawLines[i].trim() });
                break;
            }
        }
        // Check provider signatures
        for (const sig of providerSigs) {
            if (sig.regex.test(stripped)) {
                if (!provider) {
                    provider = sig.provider;
                    if (!firstPattern)
                        firstPattern = sig.regex.source;
                }
                lineRanges.push({ start: i + 1, end: i + 1, match: rawLines[i].trim() });
                break;
            }
        }
    }
    if (lineRanges.length === 0)
        return null;
    // Deduplicate line ranges (a line may match both framework and provider sig)
    const seen = new Set();
    const dedupedRanges = [];
    for (const lr of lineRanges) {
        if (!seen.has(lr.start)) {
            seen.add(lr.start);
            dedupedRanges.push(lr);
        }
    }
    return { provider, framework, lineRanges: dedupedRanges, kind: 'import', firstPattern };
}
// ---------------------------------------------------------------------------
// Directory walker
// ---------------------------------------------------------------------------
function walkDir(dirPath, baseDir, results) {
    let entries;
    try {
        entries = fs.readdirSync(dirPath, { withFileTypes: true });
    }
    catch {
        return;
    }
    for (const entry of entries) {
        if (entry.isDirectory()) {
            if (SKIPPED_DIRS.has(entry.name))
                continue;
            walkDir(path.join(dirPath, entry.name), baseDir, results);
        }
        else if (entry.isFile()) {
            const ext = path.extname(entry.name).toLowerCase();
            if (!PYTHON_EXTS.has(ext) && !TS_EXTS.has(ext))
                continue;
            const lang = languageOf(ext);
            if (!lang)
                continue;
            const absPath = path.join(dirPath, entry.name);
            const relPath = path.relative(baseDir, absPath);
            const match = analyseFile(absPath, relPath, lang);
            if (!match)
                continue;
            const agentId = stableId(relPath, match.firstPattern);
            results.push({
                agentId,
                filePath: relPath,
                language: lang,
                provider: match.provider,
                framework: match.framework,
                lineRanges: match.lineRanges,
                wrapMode: 'shim',
                packs: [],
            });
        }
    }
}
// ---------------------------------------------------------------------------
// Public entry point
// ---------------------------------------------------------------------------
/**
 * Recursively scan `dirPath` for Python / TS / JS files that contain AI
 * provider or framework imports. Returns one DetectedAgent per file that
 * matches at least one signature.
 *
 * Pure function: no disk writes, no async I/O.
 */
function scanAgentDirectory(dirPath) {
    const results = [];
    walkDir(dirPath, dirPath, results);
    return results;
}
//# sourceMappingURL=agent-dir-scanner.js.map