Skip to content
julien-dev-hook-creator logo

Hook Creator

julien-dev-hook-creator

Guide for creating Claude Code hooks - shell commands that execute at specific lifecycle events (SessionStart, SessionEnd, PreToolUse, PostToolUse, etc.). Use when users want to automate actions, add validation, logging, or integrate external tools into Claude Code workflows.

SKILL.md

Full skill instructions

Hook Creator

This skill guides the creation of Claude Code hooks - deterministic shell commands or LLM prompts that execute at specific points in Claude's lifecycle.

What Are Hooks?

Observability

First: At the start of execution, display:

🔧 Skill "julien-dev-hook-creator" activated

Hooks provide deterministic control over Claude's behavior. Unlike skills (which Claude chooses to use), hooks always execute at their designated lifecycle event.

┌─────────────────────────────────────────────────────────────────┐
│                    HOOKS vs SKILLS                              │
├─────────────────────────────────────────────────────────────────┤
│  HOOKS: Deterministic, always run at lifecycle events           │
│  SKILLS: Model-invoked, Claude decides when to use              │
└─────────────────────────────────────────────────────────────────┘

Available Hook Events

EventWhen It RunsCommon Use Cases
SessionStartSession begins/​resumesLoad context, sync data, set env vars
SessionEndSession endsCleanup, save state, push changes
PreToolUseBefore tool executionValidate, block, modify tool input
PostToolUseAfter tool completesFormat output, log, trigger actions
PermissionRequestPermission dialog shownAuto-approve or deny permissions
UserPromptSubmitUser submits promptAdd context, validate requests
NotificationClaude sends notificationCustom alerts
StopClaude finishes respondingDecide if Claude should continue
SubagentStopSubagent completesEvaluate task completion

Hook Configuration

Hooks are configured in ~/​.claude/​settings.json (global) or .claude/​settings.json (project).

Basic Structure

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

Configuration Fields

FieldRequiredDescription
matcherFor tool eventsPattern to match tool names (regex supported)
typeYes"command" (shell) or "prompt" (LLM)
commandFor type:commandShell command to execute
promptFor type:promptLLM prompt for evaluation
timeoutNoSeconds before timeout (default: 60, max: 300)

Matcher Patterns

"matcher": "Write"           // Exact match
"matcher": "Edit|Write"      // OR pattern (regex)
"matcher": "Notebook.*"      // Wildcard pattern
"matcher": "*"               // All tools (or omit matcher)

Hook Input (stdin)

Hooks receive JSON via stdin with context about the event:

{
  "session_id": "abc123",
  "transcript_path": "/​path/​to/​transcript.jsonl",
  "cwd": "/​current/​working/​directory",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/​path/​to/​file.txt",
    "content": "file content"
  }
}

Hook Output (Exit Codes)

Exit CodeBehavior
0Success - continue normally
2Block - stderr fed to Claude, action blocked
OtherNon-blocking error (shown in verbose mode)

Advanced JSON Output (exit 0)

{
  "continue": true,
  "stopReason": "message if continue=false",
  "suppressOutput": true,
  "systemMessage": "warning shown to user"
}

PreToolUse Decision Control

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "Reason here",
    "updatedInput": {
      "field": "modified value"
    }
  }
}

Creating a Hook - Step by Step

Step 1: Identify the Use Case

Ask:

  • When should this run? (which event)
  • What should it do? (validate, log, transform, block)
  • Scope: Global (~/​.claude/​settings.json) or project (.claude/​settings.json)?

Step 2: Write the Script

Create script in ~/​.claude/​scripts/ or .claude/​scripts/:

#!/​bin/​bash
# ~/​.claude/​scripts/​my-hook.sh

# Read input from stdin
INPUT=$(cat)

# Parse with jq
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Your logic here
if [[ "$FILE_PATH" == *".env"* ]]; then
    echo "Blocked: Cannot modify .env files" >&2
    exit 2  # Block the action
fi

exit 0  # Allow the action

Important: Make executable with chmod +x

Step 3: Configure the Hook

Add to settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/​.claude/​scripts/​my-hook.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Step 4: Test

# Test script directly
echo '{"tool_name":"Write","tool_input":{"file_path":"/​test/​.env"}}' | bash ~/​.claude/​scripts/​my-hook.sh
echo "Exit code: $?"

Real-World Example: Terminal Title Restoration

Problem: happy.cmd and claude.cmd contain title %COMSPEC% which overwrites terminal title to "C:\WINDOWS\system32\cmd.exe"

Solution: SessionStart hook that restores the title after launch

Script: ~/​.claude/​scripts/​restore-terminal-title-on-start.ps1

# Restore terminal title on Claude Code SessionStart
# This runs AFTER Claude has potentially overwritten the title
try {
    # Get current directory name
    $dirName = if ($PWD.Path -eq $HOME) {
        "~"
    } else {
        Split-Path $PWD -Leaf
    }

    # Restore title using multiple methods for maximum compatibility

    # Method 1: PowerShell native
    $Host.UI.RawUI.WindowTitle = $dirName

    # Method 2: ANSI escape sequence (more reliable with Windows Terminal)
    Write-Host "$([char]27)]0;$dirName$([char]7)" -NoNewline

    # Exit with success
    exit 0
} catch {
    # Silent fail - don't break Claude startup
    exit 0
}

Configuration: ~/​.claude/​settings.json (NOT repo .claude/​settings.json)

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node \"%USERPROFILE%\\.claude\\scripts\\session-start-banner.js\"",
            "timeout": 5
          },
          {
            "type": "command",
            "command": "powershell.exe -NoProfile -File \"%USERPROFILE%\\.claude\\scripts\\restore-terminal-title-on-start.ps1\"",
            "timeout": 2
          }
        ]
      }
    ]
  }
}

Timeline:

  1. happy.cmd executes → title becomes "C:\WINDOWS\system32\cmd.exe"
  2. Happy/​Claude starts
  3. SessionStart hook: session-start-banner.js displays banner
  4. SessionStart hook: restore-terminal-title-on-start.ps1 fixes the title

Result: Title restored to directory name despite npm CLI wrapper interference

Lesson: Hooks can fix issues caused by external tools (npm wrappers, shell scripts)!

Hook Languages: JavaScript vs Python vs PowerShell

JavaScript Hooks (Fastest Startup)

Pros:

  • Node.js already loaded by Claude Code
  • No interpreter startup cost
  • Faster execution (~50-200ms faster than Python)
  • Great async support

Cons:

  • Limited system integration compared to PowerShell
  • JSON parsing requires external library or built-in JSON

Examples:

  • session-start-banner.js - Fast banner display
  • track-skill-invocation.js - Performance-critical tracking
  • fast-skill-router.js - Routing must be instant

When to use: Performance-critical hooks (SessionStart, UserPromptSubmit)

Python Hooks (Rich Ecosystem)

Pros:

  • Rich libraries (json, pathlib, subprocess)
  • Better for complex data processing
  • Easier multiline string handling
  • Great for ML/​data tasks

Cons:

  • Python interpreter startup cost (~100-300ms)
  • May not be installed on all systems

Examples:

  • session-end-delete-reserved.py - Complex file operations
  • save-session-for-memory.py - Data processing
  • cleanup-null-files.py - File system traversal

When to use: Complex logic, data processing, non-time-critical tasks

PowerShell Hooks (Windows Native)

Pros:

  • Native Windows API access
  • Can modify environment directly
  • Better integration with Windows Terminal
  • Access to .NET framework

Cons:

  • Windows-only
  • Slower than JavaScript (~50-150ms startup)
  • CRLF line ending issues

Examples:

  • restore-terminal-title-on-start.ps1 - Terminal manipulation
  • cleanup-null-files.ps1 - Windows file operations
  • set-terminal-title.ps1 - Environment modification

When to use: Windows-specific tasks, terminal manipulation, .NET integration

Choosing the Right Language

Need speed? → JavaScript
Need Python libraries? → Python
Need Windows integration? → PowerShell
Need to modify terminal? → PowerShell
Need to call .NET APIs? → PowerShell
Need async operations? → JavaScript

Common Hook Patterns

1. File Protection (PreToolUse)

#!/​bin/​bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

PROTECTED=(".env" "package-lock.json" ".git/" "credentials")
for pattern in "${PROTECTED[@]}"; do
    if [[ "$FILE_PATH" == *"$pattern"* ]]; then
        echo "Protected file: $pattern" >&2
        exit 2
    fi
done
exit 0

2. Auto-Format on Save (PostToolUse)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(cat | jq -r '.tool_input.file_path') && npx prettier --write \"$FILE\" 2>/​dev/​null || true"
          }
        ]
      }
    ]
  }
}

3. Command Logging (PostToolUse)

#!/​bin/​bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
DESC=$(echo "$INPUT" | jq -r '.tool_input.description // "No description"')
echo "$(date +%Y-%m-%d_%H:%M:%S) | $CMD | $DESC" >> ~/​.claude/​logs/​bash-commands.log
exit 0

4. Session Sync (SessionStart/​SessionEnd)

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [{
          "type": "command",
          "command": "bash ~/​.claude/​scripts/​sync-marketplace.sh",
          "timeout": 30
        }]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [{
          "type": "command",
          "command": "bash ~/​.claude/​scripts/​push-marketplace.sh",
          "timeout": 30
        }]
      }
    ]
  }
}

5. Add Context to Prompts (UserPromptSubmit)

#!/​bin/​bash
# stdout is added as context to the prompt
echo "Current git branch: $(git branch --show-current 2>/​dev/​null || echo 'not a git repo')"
echo "Node version: $(node -v 2>/​dev/​null || echo 'not installed')"
exit 0

6. LLM-based Stop Decision (Stop)

{
  "hooks": {
    "Stop": [
      {
        "hooks": [{
          "type": "prompt",
          "prompt": "Review if all tasks are complete. Check: 1) All todos marked done 2) Tests passing 3) No pending questions. Respond with decision: approve (stop) or block (continue).",
          "timeout": 30
        }]
      }
    ]
  }
}

Best Practices

Do's

  • ✅ Always quote shell variables: "$VAR" not $VAR
  • ✅ Use absolute paths for scripts
  • ✅ Handle errors gracefully (exit 0 if non-critical)
  • ✅ Set appropriate timeouts
  • ✅ Test scripts independently before configuring
  • ✅ Use tr -d '\r' for Windows CRLF compatibility

Don'ts

  • ❌ Don't block critical operations without good reason
  • ❌ Don't use long timeouts (blocks Claude)
  • ❌ Don't trust input blindly - validate paths
  • ❌ Don't expose secrets in logs
  • ❌ Don't use interactive commands (no stdin available)

Debugging Hooks

# Run with debug output
bash -x ~/​.claude/​scripts/​my-hook.sh

# Test with sample input
echo '{"tool_name":"Write","tool_input":{"file_path":"/​test/​file.txt"}}' | bash ~/​.claude/​scripts/​my-hook.sh

# Check hook errors in Claude Code
# Look for "hook error" messages in the UI

For detailed troubleshooting of common errors (timeout, CRLF, jq not found, etc.), see references/​troubleshooting.md.

Environment Variables

Available in hooks:

  • CLAUDE_PROJECT_DIR - Current project directory
  • CLAUDE_CODE_REMOTE - Remote mode indicator
  • CLAUDE_ENV_FILE - (SessionStart only) File path for persisting env vars

File Locations - CRITICAL INFORMATION

LocationScopeUsage
~/​.claude/​settings.jsonGlobal (REAL FILE)File USED by Claude Code
.claude/​settings.jsonProject (versioning)Committed to repo, NOT used directly
.claude/​settings.local.jsonLocal overridesNot committed
~/​.claude/​scripts/Global scriptsUsed by hooks
.claude/​scripts/Project scriptsVersioned with repo

⚠️ CRITICAL WARNING

Claude Code uses ~/​.claude/​settings.json (home directory) NOT the repo .claude/​settings.json

These files are DIFFERENT and must be synchronized manually!

Best Practice:

  1. Modify ~/​.claude/​settings.json first (real file)
  2. Copy changes to .claude/​settings.json (for versioning)
  3. Commit repo version for documentation

Never assume the repo version is active!

Verification:

# Check what Claude Code actually uses
cat ~/​.claude/​settings.json | grep -A 5 "SessionStart"

# Compare with repo version
diff ~/​.claude/​settings.json .claude/​settings.json

Quick Reference

Event Flow:
SessionStart → UserPromptSubmit → PreToolUse → [Tool] → PostToolUse → Stop → SessionEnd

Exit Codes:
0 = Success (continue)
2 = Block (stop action, feed stderr to Claude)
* = Non-blocking error

Matcher:
"Write"        = exact match
"Edit|Write"   = OR
"Notebook.*"   = regex
"*" or omit    = all tools

🔗 Skill Chaining

Skills Required Before

  • Aucun (skill autonome)
  • Optionnel: Connaissance de base de bash/​shell scripting

Input Expected

  • Use case description: Quel événement déclencher, quelle action effectuer
  • Scope decision: Global (~/​.claude/​settings.json) ou project (.claude/​settings.json)
  • Prerequisites: jq installé pour parsing JSON

Output Produced

  • Format:
    • Script bash dans ~/​.claude/​scripts/ ou .claude/​scripts/
    • Configuration JSON dans settings.json
  • Side effects:
    • Création/​modification de fichiers scripts
    • Modification de settings.json
    • Hooks actifs au prochain événement
  • Duration: 2-5 minutes pour un hook simple

Compatible Skills After

Recommandés:

  • sync-personal-skills: Si le hook modifie des fichiers du marketplace
  • skill-creator: Si création d'un skill qui intègre des hooks

Optionnels:

  • Git workflow: Committer les scripts et settings

Called By

  • Direct user invocation: "Crée un hook pour...", "Je veux automatiser..."
  • Part of skill/​workflow development

Tools Used

  • Read (lecture settings.json existant)
  • Write (création scripts bash)
  • Edit (modification settings.json)
  • Bash (test du hook, chmod +x)

Visual Workflow

User: "Je veux protéger les fichiers .env"
    ↓
hook-creator (this skill)
    ├─► Step 1: Identify event (PreToolUse)
    ├─► Step 2: Write script (protect-files.sh)
    ├─► Step 3: chmod +x script
    ├─► Step 4: Configure settings.json
    └─► Step 5: Test with sample input
    ↓
Hook active ✅
    ↓
[Next: Test in real session]

Usage Example

Scenario: Créer un hook de logging des commandes bash

Input: "Log toutes les commandes bash exécutées"

Process:

  1. Event identifié: PostToolUse avec matcher Bash
  2. Script créé: ~/​.claude/​scripts/​log-bash.sh
  3. Settings.json mis à jour avec hook config
  4. Test avec sample JSON input

Result:

  • Script logging actif
  • Commandes loguées dans ~/​.claude/​logs/​bash-commands.log

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View