/**
 * Cursor MCP injector.
 *
 * `cursor-agent` auto-loads MCP servers declared in `~/.cursor/mcp.json`
 * (global) or `.cursor/mcp.json` (per-workspace). The shape matches the
 * Claude / Gemini / generic MCP-host convention:
 *
 *   {
 *     "mcpServers": {
 *       "<name>": { "command": "...", "args": [...], "env": {...} }
 *     }
 *   }
 *
 * On adapter init we read-modify-write the global file so the `imhub`
 * entry always points at the live approval-bus socket (which rotates
 * every agim restart — see core/approval-bus.ts). Other entries the
 * operator already had stay untouched.
 *
 * Per-workspace .cursor/mcp.json is left untouched on purpose — that
 * file belongs to the project the operator opened cursor in, not agim.
 */
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { dirname, join } from 'node:path';
import { logger as rootLogger } from '../../../core/logger.js';
const log = rootLogger.child({ component: 'agent.cursor.mcp' });
/** Resolves to `${HOME}/.cursor/mcp.json`. Exported so tests can point
 *  at a tmp dir. */
export function defaultMcpConfigPath() {
    return join(homedir(), '.cursor', 'mcp.json');
}
/**
 * Ensure `~/.cursor/mcp.json` has the imhub MCP entry pointing at the
 * current approval-bus socket. Idempotent: safe to call on every
 * adapter init or before every spawn. Preserves any other keys /
 * mcpServers entries the operator already configured.
 */
export async function ensureImhubMcp(spec, opts = {}) {
    const filePath = opts.path ?? defaultMcpConfigPath();
    let current = {};
    try {
        const raw = await readFile(filePath, 'utf8');
        if (raw.trim()) {
            try {
                current = JSON.parse(raw);
            }
            catch {
                log.warn({ event: 'cursor.mcp.parse_failed', filePath }, 'replacing unparseable mcp.json');
                current = {};
            }
        }
    }
    catch (err) {
        const e = err;
        if (e.code !== 'ENOENT')
            throw err;
    }
    if (!current.mcpServers || typeof current.mcpServers !== 'object') {
        current.mcpServers = {};
    }
    const existing = current.mcpServers.imhub;
    if (existing
        && existing.command === spec.command
        && JSON.stringify(existing.args ?? []) === JSON.stringify(spec.args ?? [])
        && JSON.stringify(existing.env ?? {}) === JSON.stringify(spec.env ?? {})) {
        return;
    }
    current.mcpServers.imhub = spec;
    try {
        await mkdir(dirname(filePath), { recursive: true });
        await writeFile(filePath, JSON.stringify(current, null, 2), 'utf8');
        log.info({ event: 'cursor.mcp.config_written', filePath }, 'wrote imhub MCP entry to cursor global config');
    }
    catch (err) {
        log.warn({ event: 'cursor.mcp.write_failed', filePath, err: String(err) }, 'could not write cursor mcp.json — cursor will run without imhub MCP');
        throw err;
    }
}
/** Remove the imhub MCP entry. Used by tests / uninstall flows. */
export async function removeImhubMcp(opts = {}) {
    const filePath = opts.path ?? defaultMcpConfigPath();
    let current = {};
    try {
        const raw = await readFile(filePath, 'utf8');
        if (raw.trim())
            current = JSON.parse(raw);
    }
    catch (err) {
        const e = err;
        if (e.code === 'ENOENT')
            return;
        throw err;
    }
    if (current.mcpServers && current.mcpServers.imhub) {
        delete current.mcpServers.imhub;
        await writeFile(filePath, JSON.stringify(current, null, 2), 'utf8');
    }
}
//# sourceMappingURL=ensure-mcp-config.js.map