"use strict";
/**
 * Agent Discovery (W2.1 of F-NEW-PRODUCT-AGENT-VISIBILITY-CLUSTER-2026-05-12).
 *
 * Customer-side scan that walks a project directory and produces an
 * AgentManifest[] for downstream auto-pack-generation (W2.3).
 *
 * Runs entirely on the customer's machine (Invariant 10 — push-only;
 * we never reach into customer infrastructure). The discovery produces a
 * signed manifest that the customer pushes to us; we never read their
 * filesystem.
 *
 * Detects:
 *   - Claude Code `agent.md` agent definitions
 *   - OpenAI Agents SDK `new Agent({...})` instantiations (JS/TS)
 *   - LangChain `Tool` / `DynamicTool` registrations
 *   - MCP server entries from `mcp.json` / `.mcp/config.json`
 *
 * Each detection produces an AgentManifest the OWASP scanner + auto-pack
 * generator can consume directly.
 *
 * @connexum/ai-governance  F-NEW-PRODUCT-AGENT-DISCOVERY-CLI
 */
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.discoverAgents = discoverAgents;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const DEFAULT_SKIP = new Set([
    'node_modules',
    '.git',
    'dist',
    'build',
    '.next',
    '.venv',
    'venv',
    '__pycache__',
    '.cache',
    '.tox',
]);
const DEFAULT_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py', '.md', '.json']);
function discoverAgents(rootDir, options = {}) {
    const skip = new Set(options.skipDirs ?? Array.from(DEFAULT_SKIP));
    const exts = new Set(options.fileExtensions ?? Array.from(DEFAULT_EXTS));
    const maxDepth = options.maxDepth ?? 8;
    if (!fs.existsSync(rootDir)) {
        throw new Error(`discoverAgents: root directory does not exist: ${rootDir}`);
    }
    const stat = fs.statSync(rootDir);
    if (!stat.isDirectory()) {
        throw new Error(`discoverAgents: not a directory: ${rootDir}`);
    }
    const results = [];
    walk(rootDir, 0, maxDepth, skip, exts, results);
    return dedupeManifests(results);
}
function walk(dir, depth, maxDepth, skip, exts, out) {
    if (depth > maxDepth)
        return;
    let entries;
    try {
        entries = fs.readdirSync(dir, { withFileTypes: true });
    }
    catch {
        return; // unreadable directory — skip silently per Invariant 10 (never throw on customer fs)
    }
    for (const entry of entries) {
        if (entry.isDirectory()) {
            if (skip.has(entry.name))
                continue;
            walk(path.join(dir, entry.name), depth + 1, maxDepth, skip, exts, out);
            continue;
        }
        const ext = path.extname(entry.name).toLowerCase();
        if (!exts.has(ext))
            continue;
        const fullPath = path.join(dir, entry.name);
        let content;
        try {
            content = fs.readFileSync(fullPath, 'utf8');
        }
        catch {
            continue;
        }
        if (entry.name === 'agent.md' || (entry.name.endsWith('.md') && /^---\s*$/m.test(content) && /\b(tools|name):/m.test(content))) {
            const manifest = parseClaudeCodeAgentMd(fullPath, content);
            if (manifest)
                out.push(manifest);
        }
        if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
            for (const m of parseOpenaiAgentsSdk(fullPath, content))
                out.push(m);
            for (const m of parseLangChainTools(fullPath, content))
                out.push(m);
        }
        if (entry.name === 'mcp.json' || entry.name === '.mcp.json' || /mcp[._-]config\.json$/.test(entry.name)) {
            for (const m of parseMcpConfig(fullPath, content))
                out.push(m);
        }
    }
}
// ---------------------------------------------------------------------------
// Parsers
// ---------------------------------------------------------------------------
function parseClaudeCodeAgentMd(file, content) {
    // Claude Code agent.md uses frontmatter with `name:` and `tools:` keys.
    // Body is optional; frontmatter-only files are valid.
    const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*(?:\n([\s\S]*))?$/);
    if (!fmMatch)
        return null;
    const frontmatter = fmMatch[1];
    const body = fmMatch[2] ?? '';
    const name = matchKey(frontmatter, 'name') ?? path.basename(file, '.md');
    const toolsLine = matchKey(frontmatter, 'tools');
    const tools = toolsLine
        ? toolsLine
            .split(',')
            .map((t) => t.trim())
            .filter((t) => t.length > 0)
        : [];
    return {
        agentId: `claude-code:${name}`,
        name,
        tools,
        systemPrompt: body.slice(0, 2000),
        llmProvider: 'anthropic/claude-code',
        hooks: { preToolUse: false, postToolUse: false, postResponse: false },
    };
}
function matchKey(frontmatter, key) {
    const re = new RegExp(`^${key}\\s*:\\s*(.+)$`, 'm');
    const m = frontmatter.match(re);
    return m ? m[1].trim() : undefined;
}
function parseOpenaiAgentsSdk(file, content) {
    // Detect: new Agent({ name: '...', tools: [...] })
    const re = /new\s+Agent\s*\(\s*\{([\s\S]*?)\}\s*\)/g;
    const out = [];
    let m;
    while ((m = re.exec(content)) !== null) {
        const block = m[1];
        const nameMatch = block.match(/name\s*:\s*['"`]([^'"`]+)['"`]/);
        const toolsMatch = block.match(/tools\s*:\s*\[([\s\S]*?)\]/);
        const name = nameMatch ? nameMatch[1] : path.basename(file, path.extname(file));
        const tools = [];
        if (toolsMatch) {
            const toolList = toolsMatch[1];
            const idRe = /([A-Za-z_][A-Za-z0-9_]*)/g;
            let tm;
            while ((tm = idRe.exec(toolList)) !== null)
                tools.push(tm[1]);
        }
        out.push({
            agentId: `openai-agents:${name}`,
            name,
            tools,
            llmProvider: 'openai/agents-sdk',
            hooks: { preToolUse: false, postToolUse: false, postResponse: false },
        });
    }
    return out;
}
function parseLangChainTools(file, content) {
    // Detect: new DynamicTool({ name: '...' }) or new Tool({ name: '...' })
    const toolRe = /new\s+(?:Dynamic)?Tool\s*\(\s*\{[\s\S]*?name\s*:\s*['"`]([^'"`]+)['"`]/g;
    const tools = [];
    let m;
    while ((m = toolRe.exec(content)) !== null)
        tools.push(m[1]);
    if (tools.length === 0)
        return [];
    return [
        {
            agentId: `langchain:${path.basename(file, path.extname(file))}`,
            name: `LangChain agent in ${path.basename(file)}`,
            tools,
            llmProvider: 'langchain',
            hooks: { preToolUse: false, postToolUse: false, postResponse: false },
        },
    ];
}
function parseMcpConfig(file, content) {
    try {
        const parsed = JSON.parse(content);
        if (!parsed.mcpServers)
            return [];
        const servers = Object.entries(parsed.mcpServers).map(([name, def]) => {
            return def.url ?? `stdio:${def.command ?? name}`;
        });
        if (servers.length === 0)
            return [];
        return [
            {
                agentId: `mcp-config:${path.basename(path.dirname(file))}`,
                name: `MCP-using agent in ${path.dirname(file)}`,
                tools: Object.keys(parsed.mcpServers).map((n) => `mcp__${n}`),
                mcpServers: servers,
                hooks: { preToolUse: false, postToolUse: false, postResponse: false },
            },
        ];
    }
    catch {
        return [];
    }
}
function dedupeManifests(manifests) {
    const seen = new Set();
    const out = [];
    for (const m of manifests) {
        if (seen.has(m.agentId))
            continue;
        seen.add(m.agentId);
        out.push(m);
    }
    return out;
}
//# sourceMappingURL=agent-discovery.js.map