or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdcli-interface.mdcli-options.mdconfiguration.mdcustom-commands.mdhooks.mdide-integration.mdindex.mdmcp-integration.mdsdk-integration.mdslash-commands.md
tile.json

hooks.mddocs/

Hook System

The Claude Code hook system provides an extensible event-driven architecture that allows custom validation, transformation, and control of tool executions. Hooks can intercept tool calls before execution to validate commands, modify inputs, or block dangerous operations.

Capabilities

Hook Configuration

Configure hooks through JSON configuration that defines when and how hooks should execute.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Session started in $CLAUDE_PROJECT_DIR'",
            "timeout": 5000
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Session ended at $(date)' >> ~/.claude/session.log"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command", 
            "command": "python3 /path/to/hook-script.py",
            "timeout": 3000
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'User prompt logged' >> ~/.claude/prompts.log"
          }
        ]
      }
    ],
    "PreCompact": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/backup-conversation.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Task completed successfully'"
          }
        ]
      }
    ],
    "SubagentStop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Subagent task completed'"
          }
        ]
      }
    ]
  }
}

Configuration Structure:

  • hooks: Root configuration object
  • Hook types: SessionStart, SessionEnd, PreToolUse, UserPromptSubmit, PreCompact, Stop, SubagentStop
  • matcher: Tool name to match (e.g., "Bash", "Read", "Write", "*" for all)
  • hooks: Array of hook actions to execute when matcher triggers
  • type: Hook action type (currently supports "command")
  • command: Shell command to execute as the hook
  • timeout: Optional timeout in milliseconds for hook execution

Hook Types

Claude Code supports multiple hook types for different lifecycle events.

SessionStart Hooks

Executed when a new Claude Code session begins.

{
  "SessionStart": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "git status",
          "timeout": 5000
        }
      ]
    }
  ]
}

Environment Variables Available:

  • CLAUDE_PROJECT_DIR: Current project directory
  • CLAUDE_SESSION_ID: Unique session identifier
  • CLAUDE_USER: Current user (if available)

SessionEnd Hooks

Executed when a Claude Code session terminates.

{
  "SessionEnd": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "echo 'Session ended at $(date)' >> ~/.claude/session.log"
        }
      ]
    }
  ]
}

PreToolUse Hooks

Executed before any tool is used (most common hook type).

{
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "python3 ~/.claude/hooks/bash-validator.py",
          "timeout": 3000
        }
      ]
    }
  ]
}

UserPromptSubmit Hooks

Executed when a user submits input to Claude.

{
  "UserPromptSubmit": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "echo 'User prompt: $CLAUDE_USER_PROMPT' >> ~/.claude/prompts.log"
        }
      ]
    }
  ]
}

Environment Variables Available:

  • CLAUDE_USER_PROMPT: The user's input text
  • CLAUDE_SESSION_ID: Current session identifier

PreCompact Hooks

Executed before conversation compaction occurs.

{
  "PreCompact": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "~/.claude/hooks/backup-conversation.sh"
        }
      ]
    }
  ]
}

Stop Hooks

Executed when a main task completes successfully.

{
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "echo 'Task completed successfully' | notify-send 'Claude Code'"
        }
      ]
    }
  ]
}

SubagentStop Hooks

Executed when a subagent task completes.

{
  "SubagentStop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "echo 'Subagent task completed'"
        }
      ]
    }
  ]
}

Hook Input Interface

Hook scripts receive JSON input via stdin containing information about the tool being executed.

{
  "tool_name": "Bash",
  "tool_input": {
    "command": "git status",
    "description": "Show working tree status"
  },
  "environment": {
    "CLAUDE_PROJECT_DIR": "/path/to/project",
    "CLAUDE_SESSION_ID": "session-123"
  }
}

Input Structure:

  • tool_name: Name of the tool being executed
  • tool_input: Object containing the tool's input parameters
    • For Bash tool: command (string), description (string), optional parameters
    • For Read tool: file_path (string), optional limit and offset
    • For Write tool: file_path (string), content (string)

Hook Output and Exit Codes

Hook scripts communicate results through exit codes and stderr output.

# Exit code meanings:
# 0: Success - Allow tool execution to proceed
# 1: Error - Show stderr to user but not to Claude, allow execution
# 2: Block - Show stderr to Claude and block tool execution

Exit Code Behaviors:

  • Exit 0: Hook validation passed, tool execution continues normally
  • Exit 1: Hook detected an issue but doesn't block execution, stderr shown to user only
  • Exit 2: Hook blocks tool execution, stderr message shown to Claude for consideration

Hook Script Implementation

Example hook script structure for bash command validation:

#!/usr/bin/env python3
import json
import sys
import re

def validate_command(command):
    """
    Validate bash command against security rules
    Returns list of validation issues
    """
    issues = []
    
    # Example validation rules
    if re.search(r"^rm\s+-rf\s+/", command):
        issues.append("Dangerous recursive delete of root paths blocked")
    
    if re.search(r"^grep\b(?!.*\|)", command):
        issues.append("Use 'rg' (ripgrep) instead of 'grep' for better performance")
        
    return issues

def main():
    try:
        input_data = json.load(sys.stdin)
    except json.JSONDecodeError as e:
        print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
        sys.exit(1)
    
    if input_data.get("tool_name") != "Bash":
        sys.exit(0)
        
    command = input_data.get("tool_input", {}).get("command", "")
    if not command:
        sys.exit(0)
        
    issues = validate_command(command)
    if issues:
        for message in issues:
            print(f"• {message}", file=sys.stderr)
        sys.exit(2)  # Block execution

if __name__ == "__main__":
    main()

Pre-Tool Use Hooks

The most common hook type that executes before any tool is used.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /path/to/bash-validator.py"
          }
        ]
      },
      {
        "matcher": "Write", 
        "hooks": [
          {
            "type": "command",
            "command": "/usr/local/bin/file-security-check"
          }
        ]
      }
    ]
  }
}

Common Use Cases:

  • Security Validation: Block dangerous commands or file operations
  • Command Translation: Suggest better alternatives (grep → rg, find → rg --files)
  • Policy Enforcement: Ensure compliance with team coding standards
  • Logging and Auditing: Track tool usage for compliance or debugging
  • Environment Checks: Validate prerequisites before command execution

Hook Development Patterns

Command Validation Hook:

# Pattern for validating bash commands
def validate_bash_command(command):
    validation_rules = [
        (r"^dangerous_pattern", "Reason why this is dangerous"),
        (r"^deprecated_command", "Use modern_alternative instead")
    ]
    
    issues = []
    for pattern, message in validation_rules:
        if re.search(pattern, command):
            issues.append(message)
    return issues

File Operation Hook:

# Pattern for validating file operations
def validate_file_operation(tool_input):
    file_path = tool_input.get("file_path", "")
    
    # Check for sensitive file patterns
    sensitive_patterns = [
        r"\.env$",
        r"\.key$", 
        r"\.pem$",
        r"password",
        r"secret"
    ]
    
    for pattern in sensitive_patterns:
        if re.search(pattern, file_path, re.IGNORECASE):
            return ["Sensitive file operation detected"]
    
    return []

Hook Configuration Management

Hooks are typically configured in user or project-specific configuration files:

  • Global Configuration: System-wide hook rules for all projects
  • Project Configuration: Project-specific hooks stored in .claude/ directory
  • User Configuration: Personal hooks that apply across projects

Configuration Loading Order:

  1. System-wide default hooks
  2. User-specific global hooks
  3. Project-specific hooks (highest priority)

Error Handling in Hooks

# Robust error handling pattern for hooks
def main():
    try:
        input_data = json.load(sys.stdin)
        result = process_hook(input_data)
        
        if result.should_block:
            for error in result.errors:
                print(f"• {error}", file=sys.stderr)
            sys.exit(2)
        elif result.warnings:
            for warning in result.warnings:
                print(f"Warning: {warning}", file=sys.stderr)
            sys.exit(1)
        else:
            sys.exit(0)
            
    except Exception as e:
        print(f"Hook execution error: {e}", file=sys.stderr)
        sys.exit(1)  # Allow execution on hook failure

Hook Testing and Debugging

# Test hook script directly
echo '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}' | python3 hook-script.py

# Check exit code
echo $?

# Test with various inputs
echo '{"tool_name": "Read", "tool_input": {"file_path": ".env"}}' | python3 hook-script.py