# Generic pre-tool-use hook

> How any coding agent with a pre-tool-use hook can call agentjail-hook to enforce policy before a tool call runs.

agentjail is agent-agnostic at the protocol level. `agentjail-hook` reads a
Claude Code-style `PreToolUse` JSON event from stdin and writes a decision to
stdout. Any agent that can run a pre-tool-use command and feed that JSON shape
can in principle use it — but **the only turnkey installer today is Claude Code**
(macOS only). For other agents, this page describes the hook protocol so you can
wire it manually once your agent supports it.

## The protocol

`agentjail-hook` is a stdin/stdout process:

- **Input (stdin):** a JSON object with at minimum:

  ```json
  {
    "hook_event_name": "PreToolUse",
    "tool_name": "Bash",
    "tool_input": { "command": "rm -rf /" }
  }
  ```

  `tool_name` is the tool schema name as your agent knows it (e.g. `Bash`,
  `Write`, `Read`). `tool_input` is the tool's input as a JSON object; for
  shell tools it is typically `{"command": "..."}`.

- **Output (stdout):** a JSON object:

  ```json
  {
    "hookSpecificOutput": {
      "hookEventName": "PreToolUse",
      "permissionDecision": "allow",
      "permissionDecisionReason": "..."
    }
  }
  ```

  `permissionDecision` is `"allow"`, `"ask"`, or `"deny"`.

- **Exit codes:**
  - **Exit 0** — allow or ask. The agent proceeds normally.
  - **Exit 2** — deny. The agent should stop the call and surface the reason.

The evaluation is local and offline: no network round-trip, no model in the
loop. The same input always produces the same verdict in milliseconds.

## Hook lifecycle

The sequence at every tool call:

```text
agent decides to call a tool
        |
        v
pre-tool-use hook fires
        |
        v
agentjail-hook reads JSON from stdin
        |
   exit 0?
   /      \
 yes       no (exit 2)
  |         |
  v         v
tool      blocked:
runs      agent receives
          structured reason,
          explains itself
```

The agent never reaches the tool on a denial. The block happens before any
command is handed to the shell, filesystem, or network. See
[How it works](/docs/concepts/how-it-works) for the full lifecycle.

## Testing a rule before you wire it up

You can test `agentjail-hook` directly from your terminal without a running
agent. The daemon must be running (`agentjail status`). Pipe a synthetic event
and inspect the output:

```sh
# should deny (targets sensitive path)
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"cat ~/.ssh/id_rsa"}}' \
  | agentjail-hook

# should allow (safe read)
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls ./src"}}' \
  | agentjail-hook
```

Check the exit code (`$?`) and the printed JSON to confirm your rules behave as
expected before wiring them in.

## Next steps

- [Claude Code integration](/docs/integrations/claude-code): the only turnkey
  integration today — wiring agentjail into Claude Code's PreToolUse hook.
- [Cursor integration](/docs/integrations/cursor): planned, not yet available.
- [Codex integration](/docs/integrations/codex): planned, not yet available.
- [How it works](/docs/concepts/how-it-works): the full evaluation lifecycle.
