mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-11-02 07:12:34 +00:00
Merge pull request #1057 from fabro-sh/codex/hook-documentation
Fix hook documentation configuration examples
This commit is contained in:
commit
9df720aa24
2 changed files with 73 additions and 53 deletions
|
|
@ -5,31 +5,43 @@ description: "Run custom logic in response to workflow lifecycle events"
|
|||
|
||||
Hooks let you run custom logic at key points during a workflow — before a stage starts, after a run completes, when a sandbox is ready, and more. Use them for validation, notifications, guardrails, and orchestration without modifying the workflow graph itself.
|
||||
|
||||
The examples below are configuration fragments for `workflow.toml`. Add them to a workflow configuration using the current [run configuration schema](/execution/run-configuration).
|
||||
|
||||
## Hook types
|
||||
|
||||
Fabro supports four hook types, from simple shell commands to full agent sessions:
|
||||
|
||||
### Command
|
||||
|
||||
Run a shell command via `sh -c`. The simplest and most common hook type.
|
||||
Use `script` for a shell command string. This is the simplest and most common hook type.
|
||||
|
||||
```toml title="run.toml"
|
||||
[[hooks]]
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
event = "stage_start"
|
||||
command = "./scripts/pre-check.sh"
|
||||
script = "./scripts/pre-check.sh"
|
||||
```
|
||||
|
||||
Alternatively, `command` accepts an array of strings:
|
||||
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
event = "post_tool_use"
|
||||
command = ["cargo", "fmt"]
|
||||
matcher = "write_file|edit_file|apply_patch"
|
||||
```
|
||||
|
||||
Use either `script` or `command` for a command hook.
|
||||
|
||||
### HTTP
|
||||
|
||||
POST the event context as JSON to an HTTP endpoint. Useful for webhooks, external APIs, and notification services.
|
||||
|
||||
```toml title="run.toml"
|
||||
[[hooks]]
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
event = "run_complete"
|
||||
type = "http"
|
||||
url = "https://hooks.example.com/done"
|
||||
|
||||
[hooks.headers]
|
||||
[run.hooks.headers]
|
||||
X-Deployment-Environment = "{{ vars.DEPLOY_ENV }}"
|
||||
```
|
||||
|
||||
|
|
@ -45,10 +57,9 @@ X-Deployment-Environment = "{{ vars.DEPLOY_ENV }}"
|
|||
|
||||
A single-turn LLM call that evaluates the event context and returns an `ok`/`block` decision. The model responds with structured JSON.
|
||||
|
||||
```toml title="run.toml"
|
||||
[[hooks]]
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
event = "stage_start"
|
||||
type = "prompt"
|
||||
prompt = "Should this stage proceed given the current context? Check for any red flags."
|
||||
model = "haiku"
|
||||
blocking = true
|
||||
|
|
@ -63,10 +74,10 @@ blocking = true
|
|||
|
||||
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.
|
||||
|
||||
```toml title="run.toml"
|
||||
[[hooks]]
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
event = "run_complete"
|
||||
type = "agent"
|
||||
agent = "enabled"
|
||||
prompt = "Verify that all tests pass and the code compiles. Run the test suite."
|
||||
model = "sonnet"
|
||||
max_tool_rounds = 10
|
||||
|
|
@ -75,10 +86,13 @@ blocking = true
|
|||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `agent` | Set to `"enabled"` to select an agent hook. |
|
||||
| `prompt` | Task instructions for the agent. |
|
||||
| `model` | Model alias or ID. Defaults to `haiku`. |
|
||||
| `max_tool_rounds` | Maximum tool call rounds before the agent gives up. Default: `50`. |
|
||||
|
||||
The `agent = "enabled"` marker selects an agent hook; `prompt` supplies its instructions. Without that marker, `prompt` selects a single-turn prompt hook.
|
||||
|
||||
## Lifecycle events
|
||||
|
||||
Each hook fires on a specific lifecycle event:
|
||||
|
|
@ -104,34 +118,41 @@ Each hook fires on a specific lifecycle event:
|
|||
|
||||
## Configuration
|
||||
|
||||
Hooks are defined as `[[hooks]]` entries in any of these TOML config files:
|
||||
Hooks are defined as `[[run.hooks]]` entries in any of these TOML config files:
|
||||
|
||||
- **`.fabro/project.toml`** — project-level hooks, apply to all workflows in the project
|
||||
- **`workflow.toml`** — per-workflow hooks
|
||||
- **`~/.fabro/settings.toml`** — global defaults for all runs
|
||||
|
||||
Each entry selects one transport: `script` or `command`, `url`, `prompt`, or `agent = "enabled"` together with `prompt`. Do not add a `type` field; the transport fields select the hook type.
|
||||
|
||||
See [Merging hook configs](#merging-hook-configs) for how these layers combine.
|
||||
|
||||
```toml title="run.toml"
|
||||
[[hooks]]
|
||||
name = "pre-check"
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
id = "pre-check"
|
||||
name = "Pre-check script"
|
||||
event = "stage_start"
|
||||
command = "./scripts/pre-check.sh"
|
||||
script = "./scripts/pre-check.sh"
|
||||
matcher = "agent"
|
||||
blocking = true
|
||||
timeout_ms = 30000
|
||||
timeout = "30s"
|
||||
sandbox = false
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `name` | Optional display name. Auto-generated from event and type if omitted. |
|
||||
| `id` | Optional merge identity. Hooks with the same `id` replace each other across layers. |
|
||||
| `name` | Optional display name; does not control merging. If omitted, the hook uses `id` or an auto-generated label. |
|
||||
| `event` | The lifecycle event to listen for (required). |
|
||||
| `command` | Shell command shorthand — implies `type = "command"`. |
|
||||
| `type` | Explicit hook type: `"command"`, `"http"`, `"prompt"`, or `"agent"`. |
|
||||
| `script` | Shell command string. |
|
||||
| `command` | Command and arguments as an array of strings, such as `["cargo", "fmt"]`; an alternative to `script`. |
|
||||
| `url` | Selects an HTTP hook. |
|
||||
| `prompt` | Instructions for a prompt hook, or for an agent hook when `agent` is set. |
|
||||
| `agent` | Set to `"enabled"` to select an agent hook. |
|
||||
| `matcher` | Regex pattern to filter which stages trigger this hook. |
|
||||
| `blocking` | Whether the hook must complete before execution continues. Defaults vary by event. |
|
||||
| `timeout_ms` | Hook timeout in milliseconds. Default: `60000` (60s) for most types, `30000` (30s) for prompt hooks. |
|
||||
| `timeout` | Duration string with a unit, such as `"500ms"`, `"30s"`, or `"2m"`. Default: `"30s"` for prompt hooks, `"60s"` for other types. |
|
||||
| `sandbox` | Run inside the sandbox (`true`, default) or on the host (`false`). |
|
||||
|
||||
## Blocking vs. non-blocking
|
||||
|
|
@ -211,34 +232,33 @@ For tool events, the matcher is tested against the tool's internal name (e.g., `
|
|||
|
||||
This example auto-formats Rust code whenever the agent writes or edits a file:
|
||||
|
||||
```toml
|
||||
[[hooks]]
|
||||
```toml title="workflow.toml"
|
||||
[[run.hooks]]
|
||||
name = "cargo-fmt"
|
||||
event = "post_tool_use"
|
||||
command = "cargo fmt"
|
||||
script = "cargo fmt"
|
||||
matcher = "write_file|edit_file|apply_patch"
|
||||
blocking = true
|
||||
```
|
||||
|
||||
More examples:
|
||||
|
||||
```toml
|
||||
```toml title="workflow.toml"
|
||||
# Only fire for agent handler nodes
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
event = "stage_start"
|
||||
command = "./scripts/agent-guard.sh"
|
||||
script = "./scripts/agent-guard.sh"
|
||||
matcher = "^agent$"
|
||||
|
||||
# Fire for any node with "test" in its ID
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
event = "stage_complete"
|
||||
command = "./scripts/report-test.sh"
|
||||
script = "./scripts/report-test.sh"
|
||||
matcher = "test"
|
||||
|
||||
# Gate shell commands with a guardrail
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
event = "pre_tool_use"
|
||||
type = "prompt"
|
||||
prompt = "Is this shell command safe to run?"
|
||||
matcher = "^shell$"
|
||||
model = "haiku"
|
||||
|
|
@ -327,7 +347,7 @@ Each hook type has a default timeout:
|
|||
| Prompt | 30 seconds |
|
||||
| Agent | 60 seconds |
|
||||
|
||||
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`.
|
||||
Override with `timeout` on any hook definition, using a duration string such as `timeout = "30s"` or `timeout = "2m"`. Supported units are `ms`, `s`, `m`, `h`, and `d`. Prompt and agent hooks **fail open** on timeout — execution proceeds as if the hook returned `ok: true`.
|
||||
|
||||
## Fail-open behavior
|
||||
|
||||
|
|
@ -343,61 +363,61 @@ Command hooks do **not** fail open. A non-zero exit code (other than 0 or 2) pro
|
|||
|
||||
## Merging hook configs
|
||||
|
||||
Hooks from multiple config files are merged in this order (later layers win on name collisions):
|
||||
Hooks from multiple config files are merged in this order (later layers replace hooks with the same `id`):
|
||||
|
||||
1. **`~/.fabro/settings.toml`** — global defaults
|
||||
2. **`.fabro/project.toml`** — project-level overrides
|
||||
3. **`workflow.toml`** — per-workflow overrides
|
||||
|
||||
This lets you define global hooks at the server level, project-wide hooks in `.fabro/project.toml`, and override or extend them per workflow.
|
||||
Hooks with matching `id` values replace in place. Hooks without an `id` append after inherited hooks, even when their `name` matches. Use `id` when a workflow should override an inherited hook.
|
||||
|
||||
This lets you define global hooks at the server level, project-wide hooks in `.fabro/project.toml`, and override or extend them per workflow. See [configuration precedence](/execution/run-configuration#precedence) for the complete layer order.
|
||||
|
||||
## Full example
|
||||
|
||||
```toml title="run.toml"
|
||||
```toml title="workflow.toml"
|
||||
# Validate environment before the run starts
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
name = "env-check"
|
||||
event = "run_start"
|
||||
command = "./scripts/check-env.sh"
|
||||
script = "./scripts/check-env.sh"
|
||||
blocking = true
|
||||
sandbox = false
|
||||
|
||||
# Notify Slack when a stage completes
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
name = "slack-notify"
|
||||
event = "stage_complete"
|
||||
type = "http"
|
||||
url = "https://hooks.slack.com/workflows/T00/B00/abc123"
|
||||
tls = "verify"
|
||||
blocking = false
|
||||
|
||||
# LLM guardrail before agent stages
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
name = "safety-check"
|
||||
event = "stage_start"
|
||||
type = "prompt"
|
||||
prompt = "Review the event context. Is there any reason this stage should not proceed?"
|
||||
model = "haiku"
|
||||
matcher = "^agent$"
|
||||
blocking = true
|
||||
timeout_ms = 15000
|
||||
timeout = "15s"
|
||||
|
||||
# Post-run verification agent
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
name = "verify"
|
||||
event = "run_complete"
|
||||
type = "agent"
|
||||
agent = "enabled"
|
||||
prompt = "Run the test suite and verify all tests pass. Report any failures."
|
||||
model = "sonnet"
|
||||
max_tool_rounds = 20
|
||||
blocking = true
|
||||
timeout_ms = 120000
|
||||
timeout = "2m"
|
||||
|
||||
# Auto-format Rust files after writes
|
||||
[[hooks]]
|
||||
[[run.hooks]]
|
||||
name = "cargo-fmt"
|
||||
event = "post_tool_use"
|
||||
command = "cargo fmt"
|
||||
script = "cargo fmt"
|
||||
matcher = "write_file|edit_file|apply_patch"
|
||||
blocking = true
|
||||
```
|
||||
|
|
|
|||
|
|
@ -582,11 +582,11 @@ sandbox = false
|
|||
| `id` | Optional merge identity. Hooks with the same `id` replace each other across layers. |
|
||||
| `name` | Optional display name for the hook. |
|
||||
| `event` | Lifecycle event: `run_start`, `run_complete`, `stage_start`, `stage_complete`, etc. |
|
||||
| `script` | Shell-evaluated command (equivalent to the old `type = "command"` shorthand). |
|
||||
| `command` | Argv-style command (alternative to `script`). |
|
||||
| `script` | Shell command string. |
|
||||
| `command` | Command and arguments as an array of strings, such as `["cargo", "fmt"]`; an alternative to `script`. |
|
||||
| `matcher` | Regex matched against node ID or handler type. Limits which stages trigger this hook. |
|
||||
| `blocking` | Whether the hook must complete before execution continues. Defaults vary by event. |
|
||||
| `timeout` | Human-readable hook timeout (e.g. `"30s"`, `"1m"`). Default: `"60s"`. |
|
||||
| `timeout` | Human-readable hook timeout (e.g. `"30s"`, `"1m"`). Default: `"30s"` for prompt hooks, `"60s"` for other types. |
|
||||
| `sandbox` | Run inside the sandbox (`true`, default) or on the host (`false`). |
|
||||
|
||||
Hook merge semantics: hooks with matching `id` values replace in place. Hooks without an `id` from a higher-precedence layer append after the fully merged inherited hook list.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue