Skip to content
cccskills
FreePublished on GitHub

hooks-reference

Use this skill when asked about "hooks", "PreToolUse", "PostToolUse", "SessionStart", "hook events", "validate tool use", "block commands", "add context on session start", or implementing event-driven automation.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md8.8 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Hooks Reference Skill

This skill provides comprehensive guidance for implementing Claude Code hooks - event handlers that automate validation, context loading, and workflow enforcement.

Hook Events Overview

EventWhen TriggeredUse Cases
PreToolUseBefore tool executesValidate, block, modify
PostToolUseAfter tool completesAudit, react, verify
PermissionRequestPermission dialog shownAuto-allow, auto-deny
StopClaude finishesForce continue, verify
SubagentStopSubagent finishesVerify task complete
SessionStartSession beginsLoad context, setup
SessionEndSession endsCleanup, save state
UserPromptSubmitPrompt submittedValidate, add context
PreCompactBefore compactionPreserve info
NotificationNotification sentAlert, log

hooks.json Structure

Basic structure:

{
  "description": "What these hooks do",
  "hooks": {
    "EventName": [
      {
        "matcher": "Pattern",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/handler.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Matcher Patterns

For tool events (PreToolUse, PostToolUse, PermissionRequest):

  • "Write" - Match exact tool name
  • "Write|Edit" - Match multiple tools (regex)
  • "Notebook.*" - Regex pattern
  • "*" or "" - Match all tools

For SessionStart:

  • "startup" - Initial startup
  • "resume" - From --resume, --continue, /resume
  • "clear" - From /clear
  • "compact" - From auto/manual compact

For Notification:

  • "permission_prompt" - Permission requests
  • "idle_prompt" - Claude waiting for input

Hook Types

Command Hooks (type: "command")

Execute a bash script:

{
  "type": "command",
  "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
  "timeout": 30
}

Prompt Hooks (type: "prompt")

LLM-based evaluation (Stop, SubagentStop only):

{
  "type": "prompt",
  "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete.",
  "timeout": 30
}

Exit Codes

Exit CodeMeaningBehavior
0SuccessAction proceeds, stdout to user (verbose)
2BlockAction blocked, stderr shown to Claude
OtherErrorNon-blocking, stderr to user (verbose)

Hook Input (stdin JSON)

Common fields:

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/working/directory",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse"
}

PreToolUse specific:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_use_id": "toolu_01ABC..."
}

SessionStart specific:

{
  "source": "startup"
}

Stop specific:

{
  "stop_hook_active": false
}

Advanced JSON Output

Return structured decisions via stdout (exit code 0):

PreToolUse Decision Control

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "Auto-approved documentation file",
    "updatedInput": {
      "field_to_modify": "new value"
    }
  }
}

Decision values: "allow", "deny", "ask"

Stop Decision Control

{
  "decision": "block",
  "reason": "Not all tasks complete - still need to run tests"
}

UserPromptSubmit Context

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Current time: 2024-01-15 10:30:00"
  }
}

SessionStart Context

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Project context loaded..."
  }
}

Example Hooks

1. Validate File Writes (PreToolUse)

hooks/hooks.json:

{
  "description": "Validate file write operations",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate-write.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

scripts/validate-write.sh:

#!/usr/bin/env bash

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')

# Block writes to sensitive files
if [[ "$FILE_PATH" == *.env* ]] || [[ "$FILE_PATH" == *secret* ]]; then
  echo "Cannot write to sensitive files: $FILE_PATH" >&2
  exit 2
fi

# Block writes outside project
if [[ ! "$FILE_PATH" == "$CLAUDE_PROJECT_DIR"* ]]; then
  echo "Cannot write outside project directory" >&2
  exit 2
fi

exit 0

2. Block Dangerous Commands (PreToolUse)

hooks/hooks.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate-bash.sh"
          }
        ]
      }
    ]
  }
}

scripts/validate-bash.sh:

#!/usr/bin/env bash

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Block destructive commands
DANGEROUS_PATTERNS=(
  "rm -rf /"
  "rm -rf ~"
  ":(){ :|:& };:"
  "> /dev/sda"
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if [[ "$COMMAND" == *"$pattern"* ]]; then
    echo "Blocked dangerous command pattern: $pattern" >&2
    exit 2
  fi
done

exit 0

3. Load Project Context (SessionStart)

hooks/hooks.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
          }
        ]
      }
    ]
  }
}

scripts/load-context.sh:

#!/usr/bin/env bash

CONTEXT=""

# Load CLAUDE.md if exists
if [ -f "$CLAUDE_PROJECT_DIR/CLAUDE.md" ]; then
  CONTEXT+="Project instructions from CLAUDE.md have been loaded.\n"
fi

# Add git status
if [ -d "$CLAUDE_PROJECT_DIR/.git" ]; then
  BRANCH=$(git -C "$CLAUDE_PROJECT_DIR" branch --show-current 2>/dev/null)
  CONTEXT+="Current git branch: $BRANCH\n"
fi

# Output as JSON for structured context
cat << EOF
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "$CONTEXT"
  }
}
EOF

exit 0

4. Verify Before Stop (Stop)

Using prompt-based hook:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop. Context: $ARGUMENTS\n\nCheck if:\n1. All requested tasks are complete\n2. No errors need addressing\n3. No tests need running\n\nRespond with: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}"
          }
        ]
      }
    ]
  }
}

5. Add Timestamp to Prompts (UserPromptSubmit)

hooks/hooks.json:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/add-timestamp.sh"
          }
        ]
      }
    ]
  }
}

scripts/add-timestamp.sh:

#!/usr/bin/env bash

# Plain text stdout is added as context
echo "Current time: $(date '+%Y-%m-%d %H:%M:%S %Z')"

exit 0

Environment Variables

Available in hooks:

  • ${CLAUDE_PLUGIN_ROOT} - Absolute path to plugin directory
  • $CLAUDE_PROJECT_DIR - Project root directory
  • $CLAUDE_ENV_FILE - (SessionStart only) File to persist env vars
  • $CLAUDE_CODE_REMOTE - "true" if running in web environment

Persisting Environment (SessionStart)

#!/usr/bin/env bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
  echo 'export API_URL=http://localhost:3000' >> "$CLAUDE_ENV_FILE"
fi

exit 0

Best Practices

  1. Always quote variables: Use "$VAR" not $VAR
  2. Validate input: Never trust stdin blindly
  3. Use portable paths: ${CLAUDE_PLUGIN_ROOT} for plugin files
  4. Set timeouts: Prevent hanging hooks
  5. Handle errors: Check for missing fields with // empty
  6. Keep hooks fast: Target <1 second execution
  7. Use jq for JSON: Safer than string parsing

Debugging

Enable debug mode:

claude --debug

Test hook manually:

echo '{"tool_name":"Write","tool_input":{"file_path":"test.txt"}}' | \
  ./scripts/validate-write.sh
echo $?  # Check exit code

References

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Complete methodology for preventing AI hallucinations. Use when accuracy is critical and you need Claude to verify before claiming, cite before asserting, and admit uncertainty instead of guessing.

The English summary is being prepared. Showing the original description.

a-ariff/ariff-claude-plugins14Updated Mar 29, 2026

Transform rough ideas into solid designs through structured questioning

The English summary is being prepared. Showing the original description.

a-ariff/ariff-claude-plugins14Updated Mar 29, 2026

Canvas API Connection Skill

The English summary is being prepared. Showing the original description.

a-ariff/ariff-claude-plugins14Updated Mar 29, 2026

Forces every code-related claim to include file:line citations. No citation means the claim must be verified or removed. Use when building documentation, writing code reviews, or any output that others will rely on.

The English summary is being prepared. Showing the original description.

a-ariff/ariff-claude-plugins14Updated Mar 29, 2026

Assign confidence scores (0-100) to every claim in a response. Helps users understand which parts are verified facts and which are educated guesses. Use when the user needs to know how much to trust each part of the answer.

The English summary is being prepared. Showing the original description.

a-ariff/ariff-claude-plugins14Updated Mar 29, 2026

Forces Claude to extract exact quotes from files before making claims about them. Prevents hallucinations by grounding every statement in actual text from the codebase. Use when working with large files or unfamiliar code.

The English summary is being prepared. Showing the original description.

a-ariff/ariff-claude-plugins14Updated Mar 29, 2026

See all skills by a-ariff

Report a problem with this skill