Hook types
Fabro supports four hook types, from simple shell commands to full agent sessions:Command
Run a shell command viash -c. The simplest and most common hook type.
run.toml
HTTP
POST the event context as JSON to an HTTP endpoint. Useful for webhooks, external APIs, and notification services.run.toml
Prompt
A single-turn LLM call that evaluates the event context and returns anok/block decision. The model responds with structured JSON.
run.toml
Agent
A multi-turn agent session with full tool access (shell, file read/write, grep, glob). The agent can investigate the workspace before making a decision.run.toml
Lifecycle events
Each hook fires on a specific lifecycle event:Configuration
Hooks are defined as[[hooks]] entries in any of these TOML config files:
.fabro/project.toml— project-level hooks, apply to all workflows in the projectworkflow.toml— per-workflow hooks~/.fabro/settings.toml— global defaults for all runs
run.toml
Blocking vs. non-blocking
Blocking hooks can affect workflow execution. Non-blocking hooks run for side effects only — their decisions are ignored. Blocking by default:run_start, stage_start, edge_selected, pre_tool_use, sandbox_ready. These events represent decision points where a hook can prevent or redirect execution.
Non-blocking by default: All other events. Override with blocking = true if needed.
When multiple blocking hooks match the same event, they run sequentially. If any hook returns a block decision, execution short-circuits — remaining hooks are skipped.
Hook decisions
Blocking hooks return a decision that controls what happens next:
When multiple blocking hooks run, decisions are merged with this precedence: Block > Skip/Override > Proceed.
Command hook decisions
Command hooks communicate decisions via exit code and stdout:
To return an explicit decision from a command hook, print JSON to stdout:
Prompt and agent hook decisions
Prompt and agent hooks return a JSON response:Matchers
Thematcher field is a regex that filters when a hook fires. Omit matcher to match all occurrences of the event. Each event type matches against different context fields:
The matcher is a regex, so
write_file|edit_file matches either tool and ^agent$ matches exactly the handler type agent. When an event has multiple matchable fields (e.g., tool events match against both tool_name and node_id), the hook fires if any field matches the regex.
For tool events, the matcher is tested against the tool’s internal name (e.g., shell, write_file, edit_file). See Tools for the full list. To match all file-writing tools across providers, use write_file|edit_file|apply_patch.
Examples
This example auto-formats Rust code whenever the agent writes or edits a file:Execution environment
Sandbox vs. host
By default, command hooks run inside the sandbox (sandbox = true). This means they execute in the same environment as the agent’s tools — same filesystem, same installed packages.
Set sandbox = false to run on the host machine. This is useful for hooks that need access to host-only resources (CI systems, local credentials, notification tools).
HTTP, prompt, and agent hooks ignore this setting — HTTP calls are always made from the host, and prompt/agent hooks use the LLM API directly.
Environment variables
Command hooks receive these environment variables:Hook context
The full event context is available as a JSON payload. For command hooks running in the sandbox, it is written to a temp file (path inFABRO_HOOK_CONTEXT). For command hooks running on the host (sandbox = false), it is piped to stdin. For HTTP hooks, it is the POST body.
Example hook context JSON
Example hook context JSON
edge_from/edge_to/edge_label are only set for edge_selected, failure_reason for failure events, attempt/max_attempts for stage_retrying, etc. Null fields are omitted from the serialized JSON.
For tool-level events (pre_tool_use, post_tool_use, post_tool_use_failure), the context includes additional fields:
Example pre_tool_use context JSON
Example pre_tool_use context JSON
Timeouts
Each hook type has a default timeout:
Override with
timeout_ms on any hook definition. Prompt and agent hooks fail open on timeout — execution proceeds as if the hook returned ok: true.
Fail-open behavior
Hooks are designed to be safe by default. Several failure modes result in the hook proceeding rather than blocking:- Prompt/agent LLM call fails — proceeds
- Prompt/agent hook times out — proceeds
- Prompt hook returns unparseable JSON — proceeds
- HTTP hook returns non-2xx — proceeds
- HTTP hook connection fails — proceeds
block decision.
Merging hook configs
Hooks from multiple config files are merged in this order (later layers win on name collisions):~/.fabro/settings.toml— global defaults.fabro/project.toml— project-level overridesworkflow.toml— per-workflow overrides
.fabro/project.toml, and override or extend them per workflow.
Full example
run.toml