Hooks

  • Hooks are user-defined handlers that run at deterministic points in an agent’s lifecycle
  • Use cases
    • Observability
    • Guardrails
    • Automation like formatting/linting/type-checks/testing

Location of Hooks

  • Claude Code: .claude/settings.json
  • GitHub Copilot: .github/hooks/NAME.json

Hooks Structure

  • Not Universal
  • Copilot: https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-hooks
  • General Events
    • Session Start
    • User Prompt Submit
    • Pre-Tool Use
    • Post-Tool Use
    • Stop
    • Session End
    • Error Occurred
  • Use cases
    • Log Tool call: Pre-Tool Use
    • Run Linter after edit: Post-Tool Use
    • Block dangerous commands: Pre-Tool Use
    • Inject repo context: User Prompt Submit
    • Persist conversation state: Stop/Session End

Example: Hook to Log Tool Input/Output

  • .github/hooks
{
  "version": 1,
  "hooks": {
    "preToolUse": [
      {
        "type": "command",
        "bash": "./scripts/pre-tool-log-raw.sh",
        "powershell": "./scripts/pre-tool-log-raw.ps1",
        "cwd": ".github/hooks",
        "timeoutSec": 10
      }
    ],
    "postToolUse": [
      {
        "type": "command",
        "bash": "./scripts/post-tool-log-raw.sh",
        "powershell": "./scripts/post-tool-log-raw.ps1",
        "cwd": ".github/hooks",
        "timeoutSec": 10
      }
    ]
  }
}
  • pre-tool-log-raw.sh
#!/bin/bash
set -euo pipefail
 
INPUT="$(cat)"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HOOK_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
LOG_DIR="$HOOK_ROOT/logs"
RAW_LOG_FILE="$LOG_DIR/pre-tool-use-raw.jsonl"
 
mkdir -p "$LOG_DIR"
chmod 700 "$LOG_DIR"
 
# Log raw hook payload exactly as received on stdin.
printf '%s\n' "$INPUT" >> "$RAW_LOG_FILE"
 
exit 0
  • post-tool-log-raw.sh
#!/bin/bash
set -euo pipefail
 
INPUT="$(cat)"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HOOK_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
LOG_DIR="$HOOK_ROOT/logs"
RAW_LOG_FILE="$LOG_DIR/post-tool-use-raw.jsonl"
 
mkdir -p "$LOG_DIR"
chmod 700 "$LOG_DIR"
 
# Log raw hook payload exactly as received on stdin.
printf '%s\n' "$INPUT" >> "$RAW_LOG_FILE"
 
exit 0

Windows

  • pre-tool-log-raw.ps1
$ErrorActionPreference = "Stop"
 
$inputRaw = [Console]::In.ReadToEnd()
$hookRoot = Split-Path -Parent $PSScriptRoot
$logDir = Join-Path $hookRoot "logs"
$rawLogFile = Join-Path $logDir "pre-tool-use-raw.jsonl"
 
if (-not (Test-Path $logDir)) {
  New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
 
# Log raw hook payload exactly as received on stdin.
Add-Content -Path $rawLogFile -Value $inputRaw
 
exit 0
  • post-tool-log-raw.ps1
$ErrorActionPreference = "Stop"
 
$inputRaw = [Console]::In.ReadToEnd()
$hookRoot = Split-Path -Parent $PSScriptRoot
$logDir = Join-Path $hookRoot "logs"
$rawLogFile = Join-Path $logDir "post-tool-use-raw.jsonl"
 
if (-not (Test-Path $logDir)) {
  New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
 
# Log raw hook payload exactly as received on stdin.
Add-Content -Path $rawLogFile -Value $inputRaw
 
exit 0

Observations on Logs

  • All MCP related tools have naming syntax mcp_${server_name}_${tool_name}
    • For example
      • mcp_playwright_browser_tabs
      • mcp_playwright_browser_navigate
      • mcp_playwright_browser_click
    • For all Playwright MCP tools refer: MCP_Playwright
  • These are internal tools which Github Copilot (VSCode) has
    • todo
      • Manage and track items for Task planning
    • browser
      • Open and interact with integrated browser pages
    • web
      • Fetch information from website or GitHub
    • edit (Edit files)
    • read (Read files)
    • search (Search files)
    • execute (Execute code and applications)
    • vscode
      • Use VSCode features/extensions/API
      • Ask structured questions to clarify on agent run
    • agent
      • Delegate Tasks to other agents
  • Example internal tool names from logs
    • run_in_terminal
    • navigate_page
    • open_browser_page
    • type_in_page
    • click_element
    • read_page
    • read_file