Skip to main content
Hooks let you observe, control, and extend the agent loop using custom scripts. Hooks are spawned processes that communicate over stdio using JSON in both directions. They run before or after defined stages of the agent loop and can observe, block, or modify behavior. With hooks, you can:
  • Run formatters after edits
  • Add analytics for events
  • Scan for PII or secrets
  • Gate risky operations (e.g., SQL writes)

Agent Support

Hooks work with Firebender Agent. The agent uses these hook events:
  • sessionStart / sessionEnd - Observe chat session lifecycle for analytics and batching
  • subagentStart / subagentStop - Observe Task-tool subagent lifecycle and add subagent-specific context
  • preToolUse / postToolUse - Generic tool use hooks (fires for all tools)
  • beforeShellExecution / afterShellExecution - Control shell commands
  • beforeMCPExecution / afterMCPExecution - Control MCP tool usage
  • beforeReadFile / afterFileEdit - Control file access and edits
  • preCompact - Observe context window compaction
  • stop - Handle agent completion

Quickstart

Create a hooks.json file. You can create it at the project level (<project>/.firebender/hooks.json) or in your home directory (~/.firebender/hooks.json). Project-level hooks apply only to that specific project, while home directory hooks apply globally.
For user-level hooks that apply globally, create ~/.firebender/hooks.json:
Create your hook script at ~/.firebender/hooks/format.sh:
Make it executable:
Restart your IDE. Your hook now runs after every file edit.

Hook Types

Hooks support command-based hooks.

Command-Based Hooks

Command hooks execute shell scripts that receive JSON input via stdin and return JSON output via stdout.
Exit code behavior:
  • Exit code 0 - Hook succeeded, use the JSON output
  • Exit code 2 - Block the action (equivalent to returning permission: "deny")
  • Other exit codes - Hook failed, action proceeds (fail-open by default)

Configuration

Configuration File

This example shows a user-level hooks file (~/.firebender/hooks.json). For project-level hooks, change paths like ./hooks/script.sh to .firebender/hooks/script.sh:

Global Configuration Options

Per-Script Configuration Options

Matcher Configuration

Matchers let you filter when a hook runs. Which field the matcher applies to depends on the hook:
beforeShellExecution: The matcher runs against the shell command string. Use it to run hooks only when the command matches a pattern (e.g. network calls, file deletions). The example above runs approve-network.sh only when the command contains curl, wget, or nc . Available matchers by hook:
  • preToolUse (and other tool hooks): Filter by tool type — Shell, Read, Write, Grep, Delete, MCP, Task, etc.
  • beforeShellExecution: Filter by the shell command text; the matcher is matched against the full command string.

Reference

Common Schema

Input (all hooks)

All hooks receive a base set of fields in addition to their hook-specific fields:

Hook Events

sessionStart

Called when a chat session becomes active. Useful for session analytics, per-session bookkeeping, and initializing any per-conversation state you want to aggregate later.

sessionEnd

Called when a chat session ends. Useful for batching or summarizing per-session activity such as skill usage, tool usage, or audit records.

subagentStart

Called when the Task tool spawns a subagent. Useful for subagent analytics, auditing by agent type, or injecting additional context into the subagent before it begins.

subagentStop

Called when a Task-tool subagent finishes. Useful for per-subagent analytics, summaries, or cleanup keyed by subagent type.

preToolUse

Called before any tool execution. This is a generic hook that fires for all tool types (Shell, Read, Write, MCP, Task, etc.). Use matchers to filter by specific tools.

postToolUse

Called after successful tool execution. Useful for auditing and analytics.

beforeShellExecution / beforeMCPExecution

Called before any shell command or MCP tool is executed. Return a permission decision. beforeMCPExecution uses fail-closed behavior. If the hook script fails to execute (crashes, times out, or returns invalid JSON), the MCP tool call will be blocked. This ensures MCP operations cannot bypass configured hooks.

afterShellExecution

Fires after a shell command executes; useful for auditing or collecting metrics from command output.

afterMCPExecution

Fires after an MCP tool executes; includes the tool’s input parameters and full JSON result.

afterFileEdit

Fires after the Agent edits a file; useful for formatters or accounting of agent-written code.

beforeReadFile

Called before Agent reads a file. Use for access control to block sensitive files from being sent to the model. This hook uses fail-closed behavior. If the hook script fails to execute (crashes, times out, or returns invalid JSON), the file read will be blocked. This provides security guarantees for sensitive file access.

preCompact

Called before context window compaction/summarization occurs. This is an observational hook that cannot block or modify the compaction behavior. Useful for logging when compaction happens or notifying users.

stop

Called when the agent loop ends. Can optionally auto-submit a follow-up user message to keep iterating.
The optional followup_message is a string. When provided and non-empty, Firebender will automatically submit it as the next user message. This enables loop-style flows (e.g., iterate until a goal is met). The loop_count field indicates how many times the stop hook has already triggered an automatic follow-up for this conversation (starts at 0). To prevent infinite loops, a maximum of 5 auto follow-ups is enforced.

Environment Variables

Hook scripts receive environment variables when executed:

Troubleshooting

How to confirm hooks are active

There is a Hooks tab in Firebender Settings to debug configured and executed hooks, as well as the Hooks output in ~/.firebender/hooks-logs to see errors.

If hooks are not working

  1. Restart your IDE to ensure the hooks service is running.
  2. Check that relative paths are correct for your hook source:
    • For project hooks, paths are relative to the project root (e.g., .firebender/hooks/script.sh)
    • For user hooks, paths are relative to ~/.firebender/ (e.g., ./hooks/script.sh or hooks/script.sh)

Exit code blocking

Exit code 2 from command hooks blocks the action (equivalent to returning decision: "deny").