Skip to Content
GuidesUse Hooks

Use Hooks

Run custom scripts at key points in the proto lifecycle — before tool calls, after edits, at session boundaries, and during agent team coordination.

Configure a hook

Hooks are defined in .proto/settings.json (project) or ~/.proto/settings.json (global):

{ "hooks": { "PreToolUse": [ { "matcher": "^bash$", "hooks": [ { "type": "command", "command": "./scripts/check-bash-safety.sh", "timeout": 10000 } ] } ] } }

Disable all hooks temporarily without deleting config:

{ "disableAllHooks": true }

Hook types

There are three hook types in the system, but only command is configurable via settings.json. The http and prompt types are available through the SDK only.

Settings-configurable

TypePurpose
commandRun a shell script. Event JSON on stdin, decisions on stdout

SDK-only

TypePurpose
httpPOST event JSON to a webhook URL. Available via hookCallbacks.
promptAsk an LLM to make a judgment call. Available via hookCallbacks.

Command hook

{ "type": "command", "command": "/path/to/script.sh", "timeout": 30000, "env": { "CUSTOM_VAR": "value" } }

Modifiers

async: true — run in the background; output and decisions are ignored.

if — fine-grained argument filter (fires only when the tool’s primary argument matches):

{ "type": "command", "if": "Bash(git *)", "command": "check-git-policy.sh" }

Syntax: ToolName(glob). Glob matches command for Bash, file_path for Edit/Write, pattern for Grep.

Events

Lifecycle

EventWhenCan block?
SessionStartSession begins or resumesNo
SessionEndSession terminatesNo
PreCompactBefore context compactionNo
UserPromptSubmitUser submits a promptYes (exit 2)
StopBefore model concludes responseYes (exit 2 or JSON)

Tool events

EventWhenCan block?
PreToolUseBefore tool executesYes
PostToolUseAfter tool succeedsLimited
PostToolUseFailureAfter tool failsLimited
PermissionRequestPermission dialog shownYes
NotificationBefore a notification is shownNo

Agent & team events

EventWhen
SubagentStartSubagent spawned
SubagentStopSubagent finishes
TeammateIdleBackground agent becomes idle
TaskCreatedTask added to shared list
TaskCompletedTask marked done

Input/output contract

Exit codes (command hooks)

CodeMeaningBehavior
0SuccessParse stdout as JSON for decisions
1Non-blocking errorContinue; stderr logged
2Blocking errorBlock the action; stderr fed to model

JSON output

{ "continue": true, "decision": "allow", "reason": "explanation" }

Common input fields (all events)

{ "session_id": "string", "transcript_path": "string", "cwd": "string", "hook_event_name": "string", "timestamp": "ISO 8601" }

Key event-specific fields

PreToolUse — input: tool_name, tool_input. Output: hookSpecificOutput.permissionDecision (allow|deny|ask) and optional permissionDecisionReason.

PostToolUse — input: tool_name, tool_input, tool_response. Output: decision (allow|block).

Stop — input: stop_hook_active, last_assistant_message. Output: decision (allow|block). Check stop_hook_active before continuing to avoid infinite loops.

SessionStart — input: source (startup|resume|clear|compact). Output: hookSpecificOutput.additionalContext injected into session context.

SessionEnd — input: reason. Enum values: clear, logout, prompt_input_exit, bypass_permissions_disabled, other.

PreCompact — input: trigger (manual|auto), custom_instructions. Trigger is manual when fired by /compress, auto when fired by the compaction threshold.

Notification — input: message, title (optional), notification_type. Enum values: permission_prompt, idle_prompt, auth_success, elicitation_dialog.

TeammateIdle — input: agent_id, agent_name, result_summary, success. Exit 2 to send feedback back to the agent.

Matcher patterns

Matchers are regex patterns on tool names (^bash$, read.*) or agent types (^Explore$). Empty string matches all.

For PreCompact, matchers filter on trigger (manual|auto). For Notification, matchers filter on notification_type.

Execution model

  • Hooks run in parallel by default. Set sequential: true on a hook definition object to force in-order execution.
  • When multiple hooks conflict, the most restrictive wins: deny > ask > allow.
  • Default timeout: 60 seconds. Max output: 1 MB.
  • Project hooks require trusted folder status.

SDK hook callbacks

Register hook callbacks directly in TypeScript instead of shell scripts:

import { query, type HookCallback } from '@protolabsai/sdk'; const securityGate: HookCallback = async (input) => { const data = input as { tool_name?: string; tool_input?: Record<string, unknown>; }; if (data.tool_name === 'Bash') { const cmd = String(data.tool_input?.command ?? ''); if (cmd.includes('rm -rf')) { return { shouldSkip: true, message: 'Blocked: destructive command' }; } } return {}; }; const conversation = query({ prompt: 'Refactor the auth module', options: { hookCallbacks: { PreToolUse: [securityGate] }, }, });

Callback return values

FieldEffect
shouldSkipSkip this tool call (PreToolUse only)
shouldInterruptStop the agent immediately
suppressOutputSuppress tool output from conversation
messageFeedback sent to the agent

See Contributing → Examples → SDK Hooks for more patterns.

Environment variables

Command hooks inherit process.env plus:

PROTO_PROJECT_DIR — project root GEMINI_PROJECT_DIR — same (compatibility alias) CLAUDE_PROJECT_DIR — same (compatibility alias)
Last updated on