Hooks
Hooks are shell commands that run at IsonForge lifecycle events. Use them to enforce policy, auto-format code, run linters, or block dangerous tool calls.
Configuration
Hooks live in ~/.isonforge/settings.json (user) or <cwd>/.isonforge/settings.json (project):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "./scripts/check-license.sh", "timeout": 10}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{"type": "command", "command": "pnpm format"}]
}
],
"UserPromptSubmit": [
{"hooks": [{"type": "command", "command": "./scripts/audit-log.sh"}]}
]
}
}
Lifecycle events
| Event | Fires | Can block? |
|---|---|---|
SessionStart |
Once when the session starts | No |
UserPromptSubmit |
Before each user prompt goes to the agent | Yes (exit 2) |
PreToolUse |
Before each tool call | Yes (exit 2 or permissionDecision: deny) |
PostToolUse |
After each tool call | No |
Stop |
When the session exits | No |
Matcher
matcher is a pipe-delimited regex matched against the tool name (for PreToolUse / PostToolUse). Common values:
Edit|Write- both file-write toolsBash- bash toolmcp__github__.*- any GitHub MCP tool- omitted - fires for every event in that category
Hook command
Each hook entry is {"type": "command", "command": "...", "timeout": 30}:
commandruns viash -c <command>.timeoutis seconds (default 30, configurable per-hook).- IsonForge pipes a JSON context to the hook's stdin.
The JSON context looks like:
{
"event": "PreToolUse",
"session_id": "abc12345",
"cwd": "/home/user/proj",
"tool_name": "edit_file",
"tool_input": {"path": "src/auth.py", "old_string": "...", "new_string": "..."}
}
Use jq to read it:
#!/usr/bin/env bash
PATH_ARG=$(jq -r '.tool_input.path' < /dev/stdin)
if [[ "$PATH_ARG" == "src/secrets.py" ]]; then
echo "Editing secrets.py blocked by policy" >&2
exit 2
fi
Exit codes
| Code | Effect |
|---|---|
0 |
Success. stdout (if any) is shown in the REPL. |
2 |
BLOCK. For PreToolUse, the tool call is cancelled. For UserPromptSubmit, the prompt is cancelled. |
| Other non-zero | Warning printed; execution continues. |
JSON output (PreToolUse only)
For more nuanced control, output JSON to stdout:
{
"hookSpecificOutput": {
"permissionDecision": "deny"
}
}
Or "allow" to skip the permission prompt entirely, or "ask" to force a prompt regardless of mode. This is independent of the exit-2 block - JSON is parsed before exit-code logic.
Environment
When a hook runs:
ISONFORGE_PROJECT_DIR= cwd at hook spawn time.- Standard env vars (
PATH,HOME, etc.) are inherited. - stdout is captured (max ~16 KB).
- Working directory = current project root.
Example: auto-format after edits
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "pnpm exec prettier --write $(jq -r .tool_input.path)"}
]
}
]
}
}
Example: block edits outside the repo
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "./scripts/check-path.sh"}
]
}
]
}
}
#!/usr/bin/env bash
# scripts/check-path.sh
P=$(jq -r '.tool_input.path' < /dev/stdin)
ROOT=$(git rev-parse --show-toplevel)
ABS=$(realpath "$P")
if [[ "$ABS" != "$ROOT"/* ]]; then
echo "Refusing to edit $P (outside repo)" >&2
exit 2
fi
Example: gate prompts via secret scanner
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{"type": "command", "command": "./scripts/secret-scan.sh"}
]
}
]
}
}
Detects accidental API keys / passwords in the user prompt and blocks submission.
Example: audit log on session end
{
"hooks": {
"Stop": [
{"hooks": [{"type": "command", "command": "./scripts/audit-log.sh"}]}
]
}
}
View configured hooks
/hooks
Lists every hook from settings.json with event, matcher, command preview.
Best practices
- Fast hooks only. They run in the request critical path. Long-running hooks slow down the REPL.
- Fail loud. If a hook decision matters, exit 2 with a clear stderr message. Silent failures defeat the point.
- Test outside IsonForge first. Hook commands run with stdin JSON - test with
echo '...' | ./your-hook.sh. - Keep hook scripts in the repo. Hooks invoked by relative path stay versioned with the project.