Skip to main content

Claude Code Hooks

Integration with Claude Code's hook system for event capture.

Overview

Claude Code supports hooks - shell commands that execute in response to events. ClaudeBox uses hooks to capture session data when conversations end or reach milestones.

Hook Types

HookTriggerPurpose
stopSession endsCapture complete conversation
notificationMilestone reachedCapture progress updates

Installation

The hooks are installed automatically by the deploy script:

cd ~/repos-meetrhea/claudebox
./deploy.sh

This copies hook scripts to ~/.claude/hooks/:

~/.claude/hooks/
├── stop-inbox-hook.sh
└── notification-inbox-hook.sh

Hook Implementation

stop-inbox-hook.sh

Triggered when a Claude Code session ends:

#!/bin/bash
# Read JSON payload from stdin
read -r payload

# Extract fields
session_id=$(echo "$payload" | jq -r '.session_id')
transcript_path=$(echo "$payload" | jq -r '.transcript_path')
cwd=$(echo "$payload" | jq -r '.cwd')

# Generate event file
event_id=$(uuidgen)
timestamp=$(date -Iseconds)

# Write to inbox
cat > ~/.claude/inbox/claudebox/events/event_${event_id}.json <<EOF
{
"event_id": "$event_id",
"event_type": "stop",
"session_id": "$session_id",
"transcript_path": "$transcript_path",
"cwd": "$cwd",
"timestamp": "$timestamp"
}
EOF

# Always exit 0 to not block Claude Code
exit 0

notification-inbox-hook.sh

Triggered at conversation milestones:

#!/bin/bash
read -r payload

# Similar structure to stop hook
# Used for progress tracking during long sessions

Hook Payload

Claude Code sends JSON payloads to hooks via stdin:

{
"session_id": "abc-123-def-456",
"transcript_path": "/home/user/.claude/projects/myproject/session.jsonl",
"cwd": "/home/user/repos/myproject",
"summary": "Working on authentication feature",
"message_count": 42
}

Design Principles

Silent and Non-blocking

Hooks must:

  • Never output to stdout/stderr (would appear in Claude Code)
  • Always exit 0 (non-zero blocks Claude Code)
  • Execute quickly (async processing preferred)

All errors are logged to ~/.claude/inbox/claudebox/logs/hook-errors.log.

Idempotent Processing

The daemon handles duplicate events gracefully. If the same event is processed twice, it's detected and skipped.

Debugging Hooks

Check hook installation

ls -la ~/.claude/hooks/

Test hook manually

echo '{"session_id":"test","transcript_path":"/tmp/test.jsonl","cwd":"/tmp"}' | \
~/.claude/hooks/stop-inbox-hook.sh

Check for errors

cat ~/.claude/inbox/claudebox/logs/hook-errors.log

Verify events are created

ls ~/.claude/inbox/claudebox/events/

Custom Hooks

You can add custom hooks alongside ClaudeBox hooks. Claude Code executes all hooks in ~/.claude/hooks/ that match the pattern:

  • *-stop-hook.sh - Session end hooks
  • *-notification-hook.sh - Notification hooks

Example custom hook:

#!/bin/bash
# my-custom-stop-hook.sh
# Send notification when session ends

read -r payload
session_id=$(echo "$payload" | jq -r '.session_id')

# Send to your notification service
curl -s -X POST https://my-service.com/notify \
-d "{\"session\": \"$session_id\"}" \
> /dev/null 2>&1

exit 0

Transcript Format

The transcript_path points to a JSONL file with conversation records:

{"type":"system","content":"You are Claude...","timestamp":"2025-01-01T12:00:00Z"}
{"type":"user","content":"Help me debug this","timestamp":"2025-01-01T12:00:01Z"}
{"type":"assistant","content":"I'll help you...","timestamp":"2025-01-01T12:00:02Z"}
{"type":"tool_use","tool":"Read","input":{"path":"/src/main.py"},"timestamp":"2025-01-01T12:00:03Z"}
{"type":"tool_result","tool_id":"123","output":"...","timestamp":"2025-01-01T12:00:04Z"}
{"type":"summary","content":"Fixed authentication bug","timestamp":"2025-01-01T12:30:00Z"}

The daemon parses this format to extract:

  • Messages (user, assistant, system)
  • Tool calls and results
  • Summaries
  • File paths mentioned