Skip to content

Getting Started

Install

bash
bun add @pivanov/agent-hooks-bridge
bash
npm install @pivanov/agent-hooks-bridge
bash
yarn add @pivanov/agent-hooks-bridge
bash
pnpm add @pivanov/agent-hooks-bridge

The package is ESM-only. Requires Bun ≥1.0 or Node ≥22.

Quickstart with init

The fastest path is the init subcommand. It scaffolds a starter hook script (no-op, all five events wired) and runs install in one step:

bash
bunx @pivanov/agent-hooks-bridge init

This creates ./.hooks/format.ts, marks it executable, and writes .claude/settings.json, .cursor/hooks.json, .codex/hooks.toml pointing at it. Edit the script to add your logic, then re-run install only if you change the path or which events you want wired.

To verify the install:

bash
bunx @pivanov/agent-hooks-bridge doctor

The rest of this page walks through the same flow by hand if you'd rather see what init does.

Write a hook (manual)

Create .hooks/format.ts in your project:

ts
#!/usr/bin/env bun
// .hooks/format.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: "rm -rf is blocked by policy" };
    }
    return { decision: "allow" };
  },

  PostToolUse: async (event) => {
    if (event.tool !== "Edit" && event.tool !== "Write") {
      return { decision: "allow" };
    }
    const file = event.tool_input.file_path;
    if (typeof file !== "string" || !/\.(ts|tsx|js|jsx)$/.test(file)) {
      return { decision: "allow" };
    }
    await Bun.spawn(["biome", "check", "--write", file]).exited;
    return {
      decision: "allow",
      additional_context: `biome auto-fixed ${file}`,
    };
  },
});

await run(hooks);

Make it executable:

bash
chmod +x .hooks/format.ts

Wire it into all three hosts

bash
bunx @pivanov/agent-hooks-bridge install ./.hooks/format.ts

This generates / merges these files:

  • .claude/settings.json
  • .cursor/hooks.json
  • .codex/hooks.toml

Each pointing at your one script. Re-running the install is idempotent: only entries that point at your script are replaced; everything else is preserved.

Preview without writing:

bash
bunx @pivanov/agent-hooks-bridge install ./.hooks/format.ts --dry-run

To install only into the hosts you actually have installed, pass --hosts auto. To install into your user-level config so the hook fires across every project, pass --global. See Install reference for the full flag list.

What happens at runtime

When the agent fires a hook, the host spawns your script with --host <host> and pipes a JSON payload to stdin:

text
host → spawn ./.hooks/format.ts --host cursor
host → stdin: { "hook_event_name": "beforeShellExecution", ... }

run() reads stdin
  → resolves host from --host argv (falls back to stdin detection)
  → adapter.parse(raw) → unified TUnifiedEvent
  → your handler runs
  → adapter.serialize(response, event) → native stdout
  → exit (0 = proceed, 2 = deny, 1 = bridge error)

host ← stdout JSON
host ← exit code

Next steps

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