// ============================================================================= // Generated by adapters/opencode/adapter.sh — do not edit. // mbifc-hooks.js — opencode plugin that dispatches events to bash hook scripts // ============================================================================= // This file is generated at build time. It contains a vendored bash executor // (from adapters/opencode/templates/bash-executor.js) and a hook registry // synthesised from the source hooks/*.hook.yaml files. // ============================================================================= // ── bash-executor.js (vendored) ───────────────────────────────────────────── __BASH_EXECUTOR__ // ── Hook registry (generated) ─────────────────────────────────────────────── const HOOKS = __HOOK_REGISTRY__; // ── Helpers ───────────────────────────────────────────────────────────────── /** * Build the neutral JSON payload from an opencode event context. * The opencode event shapes vary per event; we extract the common fields and * leave everything else under `args`. */ function buildPayload(eventName, input) { const tool = (input && (input.tool || input.toolName)) || ""; const args = (input && (input.args || input.toolInput || input.input)) || (input && input.title !== undefined ? { title: input.title, message: input.message } : {}) || {}; const neutralEvent = (() => { switch (eventName) { case "tool.execute.before": return "before-tool-use"; case "tool.execute.after": return "after-tool-use"; case "session.idle": return "on-notification"; case "session.created": return "on-session-start"; case "tui.prompt.append": return "on-prompt-submit"; default: return eventName; } })(); return { event: neutralEvent, tool, args, session_id: (input && input.sessionId) || "", cwd: (input && input.cwd) || process.cwd(), framework: "opencode", platform_dir: ".opencode", dispatcher_name: "AGENTS.md", }; } /** * Check whether a hook's match-tool filter (if any) matches the current tool. */ function matchesTool(hook, tool) { const m = hook.matchTool; if (!m || m.length === 0) return true; return m.includes((tool || "").toLowerCase()); } // ── Plugin entry point ────────────────────────────────────────────────────── module.exports = async function mbifcHooksPlugin({ app, client }) { // Group hooks by opencode event name const byEvent = {}; for (const h of HOOKS) { for (const trig of h.triggers) { (byEvent[trig.event] ||= []).push({ ...h, matchTool: trig.matchTool }); } } // Register one handler per opencode event; dispatch to matching hooks. const handlers = {}; for (const [event, hooks] of Object.entries(byEvent)) { handlers[event] = async (input, output) => { const payload = buildPayload(event, { ...input, ...output }); for (const h of hooks) { if (!matchesTool(h, payload.tool)) continue; const path = require("node:path"); const scriptAbs = path.isAbsolute(h.script) ? h.script : path.join(__dirname, h.script); const { exitCode, stdout, stderr } = await runBashHook(scriptAbs, payload); if (exitCode === 2) { // Documented opencode block mechanism for tool.execute.before const reason = stdout.trim() || stderr.trim() || "exit 2"; throw new Error(`[${h.name}] blocked: ${reason}`); } } }; } return handlers; };