"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.opencodeOrchestrator = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
const shared_js_1 = require("./shared.js");
const OPENCODE_USER_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), '.config', 'opencode', 'opencode.json');
/**
 * Safe-bash allowlist for OpenCode reviewers. Read-only ops that
 * reviewers legitimately need to inspect a diff (git diff, ls, cat,
 * grep, find) — these shouldn't prompt every spawn. Destructive ops
 * (rm, git push, git reset) fall through to `*: ask` which preserves
 * the safety gate.
 *
 * Layer 1 of the three-layer permission model: prevent the dialog from
 * appearing in the first place. Layer 2 (shim.recoverKeys) catches
 * anything this misses.
 */
const OPENCODE_SAFE_BASH = {
    'git diff': 'allow',
    'git diff *': 'allow',
    'git status': 'allow',
    'git log': 'allow',
    'git log *': 'allow',
    'git show *': 'allow',
    'git blame *': 'allow',
    cat: 'allow',
    'cat *': 'allow',
    ls: 'allow',
    'ls *': 'allow',
    'find *': 'allow',
    'rg *': 'allow',
    'grep *': 'allow',
    'head *': 'allow',
    'tail *': 'allow',
    'wc *': 'allow',
    pwd: 'allow',
    'echo *': 'allow',
    '*': 'ask',
};
function readOpencodeConfig() {
    if (!node_fs_1.default.existsSync(OPENCODE_USER_CONFIG_PATH)) {
        return { config: {}, existed: false };
    }
    try {
        return {
            config: JSON.parse(node_fs_1.default.readFileSync(OPENCODE_USER_CONFIG_PATH, 'utf-8')),
            existed: true,
        };
    }
    catch {
        return { config: {}, existed: true };
    }
}
function getOpencodeStatus() {
    const detected = node_fs_1.default.existsSync(node_path_1.default.dirname(OPENCODE_USER_CONFIG_PATH));
    const { config } = readOpencodeConfig();
    const connected = detected &&
        Boolean(config.mcp && config.mcp.chorus);
    return {
        name: 'opencode',
        label: 'OpenCode',
        connected,
        approvedTools: connected ? 1 : 0,
        totalTools: 1,
        note: 'Registers Chorus as a local MCP server in ~/.config/opencode/opencode.json with `enabled: true` so the agent can call its tools.',
        supported: detected,
        firstCallBehavior: 'auto',
    };
}
/**
 * Register Chorus with OpenCode. `opencode mcp add` is interactive (no
 * flags for non-tty use), so we patch the user-scope config directly.
 * Idempotent.
 */
async function connectOpencode(opts) {
    const { config } = readOpencodeConfig();
    const mcp = (config.mcp ?? {});
    const existing = mcp.chorus;
    const existingBinMatches = existing &&
        Array.isArray(existing.command) &&
        existing.command.includes(opts.binPath);
    // Bash pre-approval is independent of the MCP block — pre-approve
    // even if the MCP block is already correct, in case the user's
    // existing config doesn't have it. Idempotent merge: never overwrite
    // a user's explicit setting, only fill gaps.
    const existingPermission = (config.permission ?? {});
    const existingBash = (existingPermission.bash ?? {});
    const mergedBash = {
        ...OPENCODE_SAFE_BASH,
    };
    // User's settings win over our defaults — preserves explicit deny rules.
    for (const [pattern, mode] of Object.entries(existingBash)) {
        mergedBash[pattern] = mode;
    }
    const bashChanged = JSON.stringify(existingBash) !== JSON.stringify(mergedBash);
    if (existingBinMatches && !bashChanged) {
        return {
            added: [],
            alreadyPresent: ['mcp.chorus', 'permission.bash'],
            configPath: OPENCODE_USER_CONFIG_PATH,
            slashCommand: 'skipped',
            slashCommandPath: '',
        };
    }
    const daemonUrl = opts.daemonUrl ?? shared_js_1.DEFAULT_DAEMON_URL;
    mcp.chorus = {
        type: 'local',
        command: ['node', opts.binPath, 'mcp'],
        environment: { CHORUS_DAEMON_URL: daemonUrl },
        enabled: true,
    };
    const next = {
        $schema: 'https://opencode.ai/config.json',
        ...config,
        mcp,
        permission: {
            ...existingPermission,
            bash: mergedBash,
        },
    };
    node_fs_1.default.mkdirSync(node_path_1.default.dirname(OPENCODE_USER_CONFIG_PATH), { recursive: true });
    node_fs_1.default.writeFileSync(OPENCODE_USER_CONFIG_PATH, JSON.stringify(next, null, 2) + '\n', 'utf-8');
    const added = [];
    if (!existingBinMatches)
        added.push('mcp.chorus');
    if (bashChanged)
        added.push('permission.bash');
    return {
        added,
        alreadyPresent: [],
        configPath: OPENCODE_USER_CONFIG_PATH,
        slashCommand: 'skipped',
        slashCommandPath: '',
    };
}
exports.opencodeOrchestrator = {
    name: 'opencode',
    label: 'OpenCode',
    getStatus: getOpencodeStatus,
    detect: () => node_fs_1.default.existsSync(node_path_1.default.dirname(OPENCODE_USER_CONFIG_PATH)) ||
        node_fs_1.default.existsSync(node_path_1.default.join(node_os_1.default.homedir(), '.opencode')),
    connect: async (opts) => {
        const full = await connectOpencode(opts);
        return { registered: full.added.length > 0, toolsAdded: 0, full };
    },
};
//# sourceMappingURL=opencode.js.map