Skip to main content

CLI tool for configuring Claude Code hooks in projects

Project description

claude-hooks

Simple CLI tool for configuring Claude Code hooks in your projects.

What This Does

claude-hooks helps you quickly add hooks to your Claude Code project. Instead of manually:

  • Creating .sh hook scripts
  • Making them executable
  • Editing .claude/settings.local.json

Just run: claude-hooks add <hook-name>

Installation

pip install claude-hooks

Or with uv:

uv pip install claude-hooks

Usage

Navigate to your Claude Code project directory (must have .claude/ directory) and run:

# Install all hooks at once
claude-hooks install

# Or add hooks one at a time:

# Add hook for Edit tool
claude-hooks add on_edit

# Add hook for TodoWrite tool
claude-hooks add on_todo_complete

# Add hook for Stop event
claude-hooks add on_stop

# Add hook for SessionStart event
claude-hooks add on_start

# Add hook for Bash tool
claude-hooks add on_bash

# Add hook for git commits (filter in script)
claude-hooks add on_git_commit

# Add hook for AskUserQuestion tool
claude-hooks add on_ask_user

# Add hook for Write tool
claude-hooks add on_write

# Add hook for MultiEdit tool
claude-hooks add on_multiedit

# Add hook for Task tool (subagent completion)
claude-hooks add on_task_complete

# Add hook for Read tool
claude-hooks add on_read

# Add hook for user prompt submission
claude-hooks add on_user_prompt

# Add hook for plan mode exit
claude-hooks add on_plan

# Add hook for pre-compaction (runs before context compaction)
claude-hooks add on_precompact

# Add hook for periodic snapshots
claude-hooks add on_snapshot

What It Does

When you run claude-hooks add <hook-name>:

  1. Creates hook script: .claude/hooks/<hook-name>.sh with basic template
  2. Makes it executable: chmod +x automatically applied
  3. Updates settings: Adds hook configuration to .claude/settings.local.json
  4. Shows summary: Displays hook event and matcher info

Example

$ claude-hooks add on_edit
Created hook script: .claude/hooks/on_edit.sh
Added hook to settings.local.json
  Event: PostToolUse
  Matcher: Edit

Edit .claude/hooks/on_edit.sh to customize hook behavior

Available Hooks

Hook Name Claude Event Matcher Description
on_edit PostToolUse Edit Triggers after Edit tool completes
on_todo_complete PostToolUse TodoWrite Triggers after TodoWrite tool completes
on_stop Stop (none) Triggers when agent stops responding
on_start SessionStart (none) Triggers when session starts
on_bash PreToolUse Bash Triggers before Bash tool executes
on_git_commit PreToolUse Bash Triggers before Bash tool executes (filter for git commit in script)
on_ask_user PreToolUse AskUserQuestion Triggers before AskUserQuestion tool executes
on_write PostToolUse Write Triggers after Write tool completes
on_multiedit PostToolUse MultiEdit Triggers after MultiEdit tool completes
on_task_complete PostToolUse Task Triggers after Task tool completes
on_read PreToolUse Read Triggers before Read tool executes
on_user_prompt UserPromptSubmit (none) Triggers when user submits a prompt
on_plan PreToolUse ExitPlanMode Triggers before ExitPlanMode tool executes
on_precompact PreCompact (none) Triggers before context compaction begins
on_snapshot PreToolUse (none) Triggers periodically to save conversation snapshots

Hook Script Templates

Each generated hook script includes comprehensive documentation:

  • Input JSON Schema: All fields hook receives from Claude Code
  • Output JSON Schema: Required/optional fields hook must return
  • Exit Code Documentation: What each exit code does
  • Practical Examples: Real jq parsing examples you can copy-paste

Example: PreToolUse Hook (on_plan, on_read, on_bash)

#!/bin/bash
# on_plan - Triggers before ExitPlanMode tool executes
#
# INPUT JSON SCHEMA:
# {
#   "session_id": "string",
#   "transcript_path": "string",
#   "cwd": "string",
#   "permission_mode": "default|plan|acceptEdits|bypassPermissions",
#   "hook_event_name": "PreToolUse",
#   "tool_name": "string (e.g., 'Read', 'Edit', 'Bash')",
#   "tool_input": {} (tool-specific parameters)
# }
#
# OUTPUT JSON SCHEMA:
# {
#   "hookSpecificOutput": {
#     "hookEventName": "PreToolUse",
#     "permissionDecision": "allow|deny|ask",
#     "permissionDecisionReason": "string (optional)",
#     "updatedInput": {} (optional - modify tool parameters)
#   },
#   "continue": true|false (optional, default: true),
#   "stopReason": "string (optional)",
#   "suppressOutput": true|false (optional, default: false),
#   "systemMessage": "string (optional)"
# }
#
# EXIT CODES:
#   0 - Normal success (allow tool execution)
#   2 - Block tool execution and show stderr to Claude
#   Other - Error condition
#
# EXAMPLES:
#
#   # Allow tool execution:
#   echo '{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}}'
#
#   # Block tool execution:
#   echo '{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Restricted file"}}'
#
#   # Parse specific fields:
#   TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
#   FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

INPUT=$(cat)

# Default: allow tool execution
echo '{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}}'

exit 0

Example: PostToolUse Hook (on_edit, on_write, on_todo_complete)

#!/bin/bash
# on_edit - Triggers after Edit tool completes
#
# INPUT JSON SCHEMA:
# {
#   "session_id": "string",
#   "transcript_path": "string",
#   "cwd": "string",
#   "permission_mode": "default|plan|acceptEdits|bypassPermissions",
#   "hook_event_name": "PostToolUse",
#   "tool_name": "string (e.g., 'Edit', 'Write', 'TodoWrite')",
#   "tool_input": {} (tool-specific parameters that were used),
#   "tool_response": {} (tool-specific response/result)
# }
#
# OUTPUT JSON SCHEMA:
# {
#   "decision": "block|undefined (optional)",
#   "reason": "string (required if decision is 'block')",
#   "hookSpecificOutput": {
#     "hookEventName": "PostToolUse",
#     "additionalContext": "string (optional - added to Claude's context)"
#   },
#   "continue": true|false (optional, default: true),
#   "stopReason": "string (optional)",
#   "suppressOutput": true|false (optional, default: false),
#   "systemMessage": "string (optional)"
# }
#
# EXIT CODES:
#   0 - Normal success
#   2 - Show stderr to Claude for processing
#   Other - Error condition
#
# NOTE: Tool has already executed when this hook runs!
#
# EXAMPLES:
#
#   # Add context about what was changed:
#   FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
#   echo '{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Modified: '"$FILE_PATH"'"}}'

INPUT=$(cat)

# Default: do nothing
echo '{}'

exit 0

After generating a hook, edit the .sh file to add your custom logic. All the documentation you need is in the generated file!

Settings Format

Hooks are added to .claude/settings.local.json in this format:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/on_edit.sh"
          }
        ]
      }
    ]
  }
}

Requirements

  • Python 3.10+
  • Claude Code project with .claude/ directory
  • jq (optional, for parsing JSON in hook scripts)

Development

Setup

# Clone repository
git clone https://github.com/stevennevins/claude-hooks.git
cd claude-hooks

# Install dependencies
uv sync --group dev

Running Tests

# All tests
uv run pytest

# Unit tests only
uv run pytest tests/unit/ -v

# With coverage
uv run pytest --cov=claude_hooks

Code Quality

# Format code
uv run ruff format claude_hooks/ tests/

# Lint code
uv run ruff check claude_hooks/ tests/

# Type check
uv run pyright claude_hooks/

Why This Tool?

Grug tired of manual hook setup. Make simple tool that:

  • Create hook script fast
  • Update settings automatically
  • Work every time same way
  • No remember JSON structure

Simple better than complex.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_hooks_template-1.4.1.tar.gz (41.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_hooks_template-1.4.1-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file claude_hooks_template-1.4.1.tar.gz.

File metadata

  • Download URL: claude_hooks_template-1.4.1.tar.gz
  • Upload date:
  • Size: 41.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for claude_hooks_template-1.4.1.tar.gz
Algorithm Hash digest
SHA256 45cf31046b6d46dfb79c1db0a36b56b02be673bb4fc7e0c592f4cf7d7cf38209
MD5 2f6dffb372cfaba1e73c54bfb512e0ad
BLAKE2b-256 e5c39bf4d9f8b3735191af6f165e9c1f5481358a2d536529fe4540ffbc1cfd30

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_hooks_template-1.4.1.tar.gz:

Publisher: release.yml on stevennevins/claude-hooks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_hooks_template-1.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_hooks_template-1.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4c02977aabdf10cac8e13904635f8b8aafa49a32840f716d20ab7955f6782fc9
MD5 356aa198c49df0de4640471946c55bd5
BLAKE2b-256 792ddfb36b9a1855313f5accf80de960dfda64159bd7017dd266a9d9ba4fbe0f

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_hooks_template-1.4.1-py3-none-any.whl:

Publisher: release.yml on stevennevins/claude-hooks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page