xiaohongshu
xiaohongshu
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.
Full skill instructions
This skill guides the creation of Claude Code hooks - deterministic shell commands or LLM prompts that execute at specific points in Claude's lifecycle.
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 │
└─────────────────────────────────────────────────────────────────┘
| Event | When It Runs | Common Use Cases |
|---|---|---|
SessionStart | Session begins/resumes | Load context, sync data, set env vars |
SessionEnd | Session ends | Cleanup, save state, push changes |
PreToolUse | Before tool execution | Validate, block, modify tool input |
PostToolUse | After tool completes | Format output, log, trigger actions |
PermissionRequest | Permission dialog shown | Auto-approve or deny permissions |
UserPromptSubmit | User submits prompt | Add context, validate requests |
Notification | Claude sends notification | Custom alerts |
Stop | Claude finishes responding | Decide if Claude should continue |
SubagentStop | Subagent completes | Evaluate task completion |
Hooks are configured in ~/.claude/settings.json (global) or .claude/settings.json (project).
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 60
}
]
}
]
}
}
| Field | Required | Description |
|---|---|---|
matcher | For tool events | Pattern to match tool names (regex supported) |
type | Yes | "command" (shell) or "prompt" (LLM) |
command | For type:command | Shell command to execute |
prompt | For type:prompt | LLM prompt for evaluation |
timeout | No | Seconds before timeout (default: 60, max: 300) |
"matcher": "Write" // Exact match
"matcher": "Edit|Write" // OR pattern (regex)
"matcher": "Notebook.*" // Wildcard pattern
"matcher": "*" // All tools (or omit matcher)
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"
}
}
| Exit Code | Behavior |
|---|---|
0 | Success - continue normally |
2 | Block - stderr fed to Claude, action blocked |
| Other | Non-blocking error (shown in verbose mode) |
{
"continue": true,
"stopReason": "message if continue=false",
"suppressOutput": true,
"systemMessage": "warning shown to user"
}
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "Reason here",
"updatedInput": {
"field": "modified value"
}
}
}
Ask:
~/.claude/settings.json) or project (.claude/settings.json)?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
Add to settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/scripts/my-hook.sh",
"timeout": 10
}
]
}
]
}
}
# Test script directly
echo '{"tool_name":"Write","tool_input":{"file_path":"/test/.env"}}' | bash ~/.claude/scripts/my-hook.sh
echo "Exit code: $?"
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:
happy.cmd executes → title becomes "C:\WINDOWS\system32\cmd.exe"session-start-banner.js displays bannerrestore-terminal-title-on-start.ps1 fixes the titleResult: Title restored to directory name despite npm CLI wrapper interference
Lesson: Hooks can fix issues caused by external tools (npm wrappers, shell scripts)!
Pros:
Cons:
Examples:
session-start-banner.js - Fast banner displaytrack-skill-invocation.js - Performance-critical trackingfast-skill-router.js - Routing must be instantWhen to use: Performance-critical hooks (SessionStart, UserPromptSubmit)
Pros:
Cons:
Examples:
session-end-delete-reserved.py - Complex file operationssave-session-for-memory.py - Data processingcleanup-null-files.py - File system traversalWhen to use: Complex logic, data processing, non-time-critical tasks
Pros:
Cons:
Examples:
restore-terminal-title-on-start.ps1 - Terminal manipulationcleanup-null-files.ps1 - Windows file operationsset-terminal-title.ps1 - Environment modificationWhen to use: Windows-specific tasks, terminal manipulation, .NET integration
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
#!/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
{
"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"
}
]
}
]
}
}
#!/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
{
"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
}]
}
]
}
}
#!/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
{
"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
}]
}
]
}
}
"$VAR" not $VARtr -d '\r' for Windows CRLF compatibility# 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.
Available in hooks:
CLAUDE_PROJECT_DIR - Current project directoryCLAUDE_CODE_REMOTE - Remote mode indicatorCLAUDE_ENV_FILE - (SessionStart only) File path for persisting env vars| Location | Scope | Usage |
|---|---|---|
~/.claude/settings.json | Global (REAL FILE) | File USED by Claude Code |
.claude/settings.json | Project (versioning) | Committed to repo, NOT used directly |
.claude/settings.local.json | Local overrides | Not committed |
~/.claude/scripts/ | Global scripts | Used by hooks |
.claude/scripts/ | Project scripts | Versioned with repo |
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:
~/.claude/settings.json first (real file).claude/settings.json (for versioning)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
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
~/.claude/settings.json) ou project (.claude/settings.json)jq installé pour parsing JSON~/.claude/scripts/ ou .claude/scripts/settings.jsonRecommandés:
Optionnels:
Read (lecture settings.json existant)Write (création scripts bash)Edit (modification settings.json)Bash (test du hook, chmod +x)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]
Scenario: Créer un hook de logging des commandes bash
Input: "Log toutes les commandes bash exécutées"
Process:
PostToolUse avec matcher Bash~/.claude/scripts/log-bash.shResult:
~/.claude/logs/bash-commands.logxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
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...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
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...
Display project status, active tracks, and next actions
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.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience