Awesome-jetbrains-claude claude-code-hooks

Create custom Claude Code hooks — bash scripts (.sh) that execute automatically in response to Claude Code events (file edits, tool use, session lifecycle). Use when the user asks to create a hook, automation script for Claude Code, pre/post tool use check, or guard for Claude Code actions. Covers writing .sh files, registering hooks in settings.local.json, proper stdin/stdout/exit code handling, and all event types (PreToolUse, PostToolUse, SessionStart, Stop, etc.). Also use when debugging or fixing existing hooks.

install
source · Clone the upstream repo
git clone https://github.com/IliyaBrook/awesome-jetbrains-claude
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/IliyaBrook/awesome-jetbrains-claude "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/claude-code-hooks" ~/.claude/skills/iliyabrook-awesome-jetbrains-claude-claude-code-hooks && rm -rf "$T"
manifest: .claude/skills/claude-code-hooks/SKILL.md
source content

Claude Code Hooks

Create and register bash hook scripts for Claude Code automation.

Workflow

1. Clarify Requirements

Determine:

  • What event? PreToolUse (validate/block before action), PostToolUse (check after action), SessionStart, Stop, etc.
  • Which tools? Edit, Write, Bash, etc. — this becomes the
    matcher
    regex
  • What logic? What the script should check or enforce
  • What feedback? Block the action, warn Claude, or just log

2. Write the .sh Script

Place in

.claude/hooks/<name>.sh
. Follow the mandatory skeleton — every hook MUST include these elements in order:

#!/usr/bin/env bash
set -uo pipefail

# 1. Terminal guard (MANDATORY — prevents hang on manual run)
if [[ -t 0 ]]; then
    exit 0
fi

# 2. Read stdin ONCE (stdin is consumed on first read)
input=$(cat)

# 3. Extract fields with // empty (NEVER omit — prevents "null" strings)
tool_name=$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)
file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty' 2>/dev/null)

# 4. Early exits (silent exit 0 = Claude ignores it)
[ -z "$file_path" ] && exit 0

# 5. Make path absolute
[[ "$file_path" = /* ]] || file_path="$(pwd)/$file_path"

# 6. Main logic + output

3. Output Correctly

To send feedback to Claude (the ONLY correct way):

# Via jq (safe — handles special chars)
printf '%s\n' "$reason" | jq -Rs '{"decision": "block", "reason": .}'
exit 0

# Direct JSON (only for static messages)
printf '%s' '{"decision": "block", "reason": "Error message"}'
exit 0

For info messages (Claude sees as a note):

echo "Check passed"
exit 0

NEVER do this:

echo "Error" >&2; exit 1   # Claude sees "hook error", NOT your message

4. Register in settings.local.json

Add to

.claude/settings.local.json
(create if absent):

{
  "hooks": {
    "<EventType>": [
      {
        "matcher": "<tool_regex>",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/<name>.sh\"",
            "timeout": 30,
            "statusMessage": "Running check..."
          }
        ]
      }
    ]
  }
}

Always quote the path with escaped double quotes — spaces in path break execution.

5. Set Executable Permission

chmod +x .claude/hooks/<name>.sh

Critical Rules

  1. set -uo pipefail
    — never
    set -e
    (too aggressive for hooks)
  2. [[ -t 0 ]] && exit 0
    — MUST be first check, prevents hang
  3. input=$(cat)
    once
    — stdin is consumed on first read, cannot re-read
  4. // empty
    in every jq call
    — without it, missing fields return literal
    "null"
  5. printf '%s'
    not
    echo
    — echo mishandles special chars in JSON
  6. JSON stdout + exit 0 — the ONLY way to send feedback to Claude
  7. exit 1 = hook failure — Claude ignores your message, logs it as error
  8. exit 2 = block — only works in PreToolUse

Reference

For complete documentation on event types, matchers, input JSON structure, all exit codes, and a real-world ts-diagnostics.sh example, read references/hooks-guide.md.