Claude Code Kit

Hooks

Hooks are shell scripts that run automatically at specific points in Claude Code's workflow. Unlike CLAUDE.md instructions (which are advisory), hooks are **deterministic** — they always execute.


Included Hooks

PreToolUse (runs BEFORE a tool executes)

HookFileWhat it does
protect-files.claude/hooks/protect-files.shBlocks edits to .env, credentials, private keys, lock files
branch-protect.claude/hooks/branch-protect.shBlocks direct push to main/master and force pushes
block-dangerous-commands.claude/hooks/block-dangerous-commands.shBlocks rm -rf /, git reset --hard, DROP TABLE, etc.
conventional-commit.claude/hooks/conventional-commit.shEnforces conventional commit message format

PostToolUse (runs AFTER a tool executes)

HookFileWhat it does
secret-scan.claude/hooks/secret-scan.shScans for API keys, tokens, passwords, private keys in edited files
unicode-scan.claude/hooks/unicode-scan.shDetects invisible Unicode characters (Glassworm attack vector, zero-width chars, variation selectors)
loop-detect.claude/hooks/loop-detect.shDetects edit loops — warns at 4 edits, blocks at 6 edits to the same file

Stop (runs when Claude finishes)

HookFileWhat it does
task-complete-notify.claude/hooks/task-complete-notify.shDesktop notification + sound on macOS/Linux

Optional (installed but not enabled by default)

These hooks are included in the kit but not enabled in settings.json. They can be slow or conflict with project-specific configs.

HookFileEventWhat it does
auto-lint.claude/hooks/auto-lint.shPostToolUseRuns linter after file edits (eslint, ruff, gofmt, clippy, rubocop)
auto-format.claude/hooks/auto-format.shPostToolUseRuns formatter after file edits (prettier, black, gofmt, rustfmt)
skill-compliance.claude/hooks/skill-compliance.shPostToolUseChecks edited files against active skills and surfaces relevant checklists
skill-extract-reminder.claude/hooks/skill-extract-reminder.shUserPromptSubmitReminds to extract reusable skills from session discoveries

How It Works

Hooks are configured in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|NotebookEdit",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-files.sh" }
        ]
      }
    ]
  }
}
  • matcher: which tools trigger the hook (regex pattern)
  • exit 0: allow the action
  • exit 2: block the action (PreToolUse only)

The hook receives tool input as JSON via stdin.


Hook Profiles

The installer supports three profiles (--profile minimal|standard|strict). Each profile enables a different set of hooks:

Hookminimalstandardstrict
protect-files
branch-protect
block-dangerous-commands
conventional-commit
secret-scan
unicode-scan
loop-detect
task-complete-notify
auto-lint
auto-format
skill-compliance
skill-extract-reminder

The repository's .claude/settings.json represents the standard profile. The strict profile is generated by install.sh at install time.


Enabling / Disabling Hooks

Disable a specific hook

Remove or comment out its entry in .claude/settings.json.

Enable optional hooks

To enable auto-lint and auto-format, add to the PostToolUse section in .claude/settings.json:

{
  "matcher": "Edit|Write",
  "hooks": [
    { "type": "command", "command": ".claude/hooks/auto-lint.sh" },
    { "type": "command", "command": ".claude/hooks/auto-format.sh" }
  ]
}

To enable skill-compliance, add to the PostToolUse section in .claude/settings.json:

{
  "matcher": "Edit|Write",
  "hooks": [
    { "type": "command", "command": ".claude/hooks/skill-compliance.sh" }
  ]
}

To enable skill-extract-reminder, add a UserPromptSubmit section:

"UserPromptSubmit": [
  {
    "hooks": [
      { "type": "command", "command": ".claude/hooks/skill-extract-reminder.sh" }
    ]
  }
]

Make secret-scan block instead of warn

Edit secret-scan.sh and change the final exit 0 to exit 2.


Writing Your Own Hooks

Template

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

INPUT=$(cat)

TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | cut -d'"' -f4)
# Extract other fields as needed from the JSON input

# Your logic here

exit 0  # allow (or exit 2 to block in PreToolUse)

Tips

  • Keep hooks fast — they run on every tool call
  • Use exit 0 for pass, exit 2 for block
  • Output to stdout is shown to Claude as feedback
  • Test hooks manually: echo '{"tool_name":"Edit","tool_input":{"file_path":".env"}}' | .claude/hooks/your-hook.sh