Skip to content

Why agent-hooks-bridge

The problem

Claude Code, Cursor, and Codex CLI all support hooks. Their stdin and stdout schemas don't agree. The same "before a Bash command runs" event has three different stdin shapes:

jsonc
{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "..."
  }
}
jsonc
{
  "hook_event_name": "beforeShellExecution",
  "command": "...",
  "cursor_version": "0.46.0"
}
jsonc
{
  "hook_event_name": "PreToolUse",
  "turn_id": "...",
  "tool_name": "apply_patch"
}

Cursor uses a camelCase event name and a flatter shape. Codex sends turn_id and uses apply_patch for file edits.

Response shapes differ similarly. Cursor's beforeSubmitPrompt accepts only continue and user_message; no context injection. Each host has its own permission-decision encoding.

To support all three, you currently write three scripts.

What this package does

You write one hook using a unified schema:

ts
import { defineHook, run } from "@pivanov/agent-hooks-bridge";

const hooks = defineHook({
  PreToolUse: (event) => {
    if (event.tool === "Bash" && /rm -rf/.test(String(event.tool_input.command ?? ""))) {
      return { decision: "deny", reason: "blocked by policy" };
    }
    return { decision: "allow" };
  },
});

await run(hooks);

At runtime the bridge:

  1. Reads stdin.
  2. Resolves the host: explicit option, then --host flag in argv, then stdin markers (cursor_version, turn_id, transcript_path).
  3. Parses the native payload into a unified event.
  4. Calls your handler.
  5. Serializes the response back into the host's native shape.
  6. Sets the exit code.

What it does not do

  • Validate input at runtime (no Zod, no schema library).
  • Ship a curated set of hook scripts.
  • Provide skills, agent definitions, rules, or security scanning.
  • Generate code at install time.

See Capability Matrix for the per-host field-by-field breakdown.

Not affiliated with Anthropic, Anysphere, or OpenAI. Supported by LogicStar AI.