Hooks

Hooks let you run shell commands automatically at key points during agent execution. Use them to enforce policies, inject context, validate tool inputs, auto-approve permissions, or integrate with external systems. Hooks can observe what the agent is doing, modify its inputs, or block operations entirely.

Hooks panel in Agent Settings showing event types and the Add New Hook form

Hook events

Each hook is attached to an event that fires at a specific point in the agent lifecycle:

EventWhen it firesCan block
PreToolUseBefore a tool is executedYes
PostToolUseAfter a tool finishes executingNo
PermissionRequestWhen a permission dialog is about to be shownYes (auto-allow or auto-deny)
SessionStartWhen a new chat session is createdNo
UserPromptSubmitWhen the user submits a promptYes
StopWhen the agent’s turn endsNo (but can force continuation)
NotificationWhen a notification is sentNo
PreCompactBefore conversation summarization/compactionNo
SessionEndWhen a session ends (clear, logout, exit)No
SubagentStopWhen a sub-agent’s turn endsNo
SetupWhen the agent performs initial environment setupYes

Managing hooks

Open Agent Settings and select Hooks from the sidebar. The panel lists all event types as expandable sections with a badge showing the count of configured hooks (enabled / total).

To add a new hook:

  1. Click the + button next to the event you want to hook into.
  2. Choose the Storage Location:
    • User — stored globally in ~/.snowflake/cortex/settings.json.
    • Workspace — stored in <workspace>/.snowflake/cortex/settings.json.
  3. Configure using the Form view or switch to JSON for raw editing.
  4. Set the fields:
    • Tool Matcher — regex pattern to filter which tools trigger the hook (for tool events). Use * for all tools.
    • Command — the shell command to execute (for example, ./my-hook-script.sh).
    • Timeout — seconds to wait before timing out (default 60).
    • Status Message — custom message displayed as a spinner while the hook runs.
  5. Click Save.

Configuration file

Hooks are stored in the hooks key of your settings file:

ScopePath
User (global)~/.snowflake/cortex/settings.json
Workspace<workspace>/.snowflake/cortex/settings.json or <workspace>/.cortex/settings.json

Example configuration:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "./validate-tool-call.sh",
            "timeout": 60,
            "enabled": true,
            "statusMessage": "Validating..."
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Session started'",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Hook fields

FieldTypeDescription
typestring"command" — runs a shell command.
commandstringThe shell command to execute.
timeoutnumberSeconds to wait before timing out (default: 60).
enabledbooleanWhether the hook is active (default: true).
statusMessagestringMessage shown as a spinner while the hook runs.

Tool matcher

For tool-based events (PreToolUse, PostToolUse, PermissionRequest), the matcher field is a regex pattern tested against the tool name. Use "*" or leave empty to match all tools. Multiple matchers with different hooks can be configured for the same event.

Execution context

When a hook fires, the full execution context is piped to the command’s stdin as JSON. This includes information about the current session and the triggering event:

FieldAvailable onDescription
session_idAll eventsUnique session identifier
cwdAll eventsCurrent working directory
hook_event_nameAll eventsThe event being triggered
tool_nameTool eventsName of the tool being called
tool_inputTool eventsInput parameters (JSON object)
tool_responsePostToolUseThe tool’s response text
promptUserPromptSubmitThe user’s submitted prompt text

Hook output

Hook commands communicate back via stdout (JSON) and exit code:

Exit codes

Exit codeMeaning
0Success — proceed normally
2Block — prevent the operation. Stderr content becomes the block reason.
Other non-zeroError — logged but does not block

JSON output (stdout)

For more control, your hook can output JSON to stdout:

{
  "decision": "approve",
  "reason": "All checks passed",
  "additionalContext": "Extra context injected into the conversation",
  "hookSpecificOutput": {
    "permissionDecision": "allow",
    "updatedInput": { "command": "modified-command" }
  }
}
FieldDescription
decision"approve" or "block" — whether to proceed or block.
reasonExplanation (shown to agent if blocked).
additionalContextText injected into the conversation as context for the agent.
hookSpecificOutput.permissionDecisionFor PermissionRequest: "allow", "deny", or "ask".
hookSpecificOutput.updatedInputFor PreToolUse: modified tool input parameters.

If stdout is not valid JSON, it is treated as additionalContext text.

Blocking operations

PreToolUse, PermissionRequest, and UserPromptSubmit hooks can block operations. When a hook blocks:

  • The tool call (or prompt submission) is prevented.
  • The block reason is shown to the agent so it can adjust its approach.
  • The agent sees a message like: “[Hook] Tool execution blocked: <reason>”

To block from a hook, either:

  • Exit with code 2 and write the reason to stderr.
  • Output JSON with "decision": "block" and a "reason" field.

Examples

Block writes to production files

#!/bin/bash
# block-prod-writes.sh — PreToolUse hook for edit/write tools
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE" == */production/* ]]; then
  echo "Cannot modify files in the production directory" >&2
  exit 2
fi

Auto-approve read-only tools

#!/bin/bash
# auto-approve-reads.sh — PermissionRequest hook
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')

case "$TOOL" in
  read|grep|glob)
    echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
    ;;
  *)
    echo '{"hookSpecificOutput":{"permissionDecision":"ask"}}'
    ;;
esac

Inject project context on session start

#!/bin/bash
# session-context.sh — SessionStart hook
echo "This project uses Python 3.12, pytest for testing, and ruff for linting."

Configuration precedence

Hooks from multiple sources are merged. Workspace hooks fire before user hooks. Plugins and profiles can also contribute hooks (shown as read-only in the UI).