import { z } from 'zod';
import { writeStream } from '../utils.js';
import { TypeScriptCheckerModeSchema } from '../code/types.js';
// From the leaf module rather than `../code/entity/index.js`: the barrel
// re-exports the session, which statically pulls in the whole code layer, and
// this command's registration must stay cheap enough to sit on every startup.
import { DEFAULT_ENTITY_BOUNDS } from '../code/entity/types.js';
/**
 * `occ mcp` — CLI registration for the MCP stdio server.
 *
 * This module deliberately imports nothing from `./server.js`. The MCP SDK is
 * the heaviest dependency OCC has, and it is only ever needed by this one
 * command; a static import here would put it on the critical path of every
 * `occ --help`, every `occ code map`, and every programmatic facade load. The
 * dynamic import inside the action keeps it out of the startup graph, which
 * `test/lazy-imports.test.ts` asserts rather than trusts.
 */
const positiveInt = z.coerce.number().int().positive();
const McpOptionsSchema = z.object({
    root: z.string().optional(),
    cacheDir: z.string().optional(),
    excludeDir: z.array(z.string()).optional(),
    testsDir: z.string().optional(),
    formats: z.boolean().optional(),
    tokenizer: z.string().optional(),
    typescriptChecker: TypeScriptCheckerModeSchema.optional(),
    refresh: z.boolean().optional(),
    maxCommits: positiveInt.optional(),
    maxEntitiesTotal: positiveInt.optional(),
    maxLiveEntities: positiveInt.optional(),
    maxTotalBlobBytes: positiveInt.optional(),
    maxLineageEvents: positiveInt.optional(),
});
/**
 * Bounds, or nothing.
 *
 * Every axis defaults to `DEFAULT_ENTITY_BOUNDS`, so a partial override is a
 * merge rather than a replacement — a caller raising the commit window should
 * not have to restate the blob budget. Returning `undefined` when nothing was
 * given keeps the default object identity, which the cache fingerprints.
 */
function boundsFrom(options) {
    const overrides = {
        maxCommits: options.maxCommits,
        maxEntitiesTotal: options.maxEntitiesTotal,
        maxLiveEntities: options.maxLiveEntities,
        maxTotalBlobBytes: options.maxTotalBlobBytes,
        maxLineageEvents: options.maxLineageEvents,
    };
    const given = Object.entries(overrides).filter(([, value]) => value !== undefined);
    if (given.length === 0)
        return undefined;
    return { ...DEFAULT_ENTITY_BOUNDS, ...Object.fromEntries(given) };
}
function collect(value, previous = []) {
    return [...previous, value];
}
function splitList(value) {
    if (!value)
        return undefined;
    const parts = value
        .split(',')
        .map(entry => entry.trim())
        .filter(Boolean);
    return parts.length > 0 ? parts : undefined;
}
export function registerMcpCommand(program) {
    const cmd = program
        .command('mcp')
        .description('Run the entity-intelligence MCP server over stdio (no daemon, no network)');
    cmd.option('--root <dir>', 'repository this server answers about', process.cwd());
    cmd.option('--cache-dir <path>', 'where the lineage cache lives (defaults outside the repository)');
    cmd.option('--exclude-dir <dir>', 'directory to exclude (repeatable)', collect);
    cmd.option('--tests-dir <dirs>', 'extra directories to treat as tests (comma-separated)');
    cmd.option('--no-formats', 'exclude data-format entities (JSON, YAML, TOML, CSV, Markdown)');
    cmd.option('--tokenizer <name>', 'token counter for context budgets');
    cmd.option('--typescript-checker <mode>', 'TypeScript resolver: preferred, required, or disabled', 'preferred');
    cmd.option('--no-refresh', 'do not re-check freshness before each tool call');
    cmd.option('--max-commits <n>', `commit window to index (default ${DEFAULT_ENTITY_BOUNDS.maxCommits})`);
    cmd.option('--max-entities-total <n>', `entity ceiling (default ${DEFAULT_ENTITY_BOUNDS.maxEntitiesTotal})`);
    cmd.option('--max-live-entities <n>', `live entity ceiling (default ${DEFAULT_ENTITY_BOUNDS.maxLiveEntities})`);
    cmd.option('--max-total-blob-bytes <n>', `blob read budget (default ${DEFAULT_ENTITY_BOUNDS.maxTotalBlobBytes})`);
    cmd.option('--max-lineage-events <n>', `event ceiling (default ${DEFAULT_ENTITY_BOUNDS.maxLineageEvents})`);
    cmd.action(async () => {
        try {
            // Inside the try, not above it. Outside it a rejected option value
            // escapes the action as an unhandled rejection and Node prints the
            // ZodError stack; the handler below is what turns it into a diagnostic a
            // reader can act on. (stdout is safe either way on this path — the
            // transport does not exist yet — so what is at stake is legibility, not
            // protocol corruption.)
            const options = McpOptionsSchema.parse(cmd.opts());
            const { runMcpServer } = await import('./server.js');
            await runMcpServer({
                repoRoot: options.root ?? process.cwd(),
                cacheDir: options.cacheDir,
                excludeDir: options.excludeDir,
                testsDir: splitList(options.testsDir),
                formats: options.formats !== false,
                tokenizer: options.tokenizer,
                typescriptChecker: options.typescriptChecker,
                refresh: options.refresh !== false,
                bounds: boundsFrom(options),
            });
        }
        catch (error) {
            // stdout is the transport's; a failure report belongs on stderr even
            // though the transport is, by this point, already gone.
            await writeStream(process.stderr, `Error: ${error.message}\n`);
            process.exitCode = 1;
        }
    });
}
//# sourceMappingURL=command.js.map