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
.shhook scripts - Making them executable
- Editing
.claude/settings.local.json
Just run: claude-hooks add <hook-name>
Installation
Recommended (with uv):
uv tool install claude-hooks-template
Or with pip:
pip install claude-hooks-template
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>:
- Creates hook script:
.claude/hooks/<hook-name>.shwith basic template - Makes it executable:
chmod +xautomatically applied - Updates settings: Adds hook configuration to
.claude/settings.local.json - 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/
Releases
This project uses automated releases via GitHub Actions. When changes are merged to main, conventional commits determine version bumps:
fix:→ patch version (0.1.0 → 0.1.1)feat:→ minor version (0.1.0 → 0.2.0)BREAKING CHANGEorfeat!:/fix!:→ major version (0.1.0 → 1.0.0)
Manual releases can also be triggered:
# Using GitHub CLI
gh api repos/stevennevins/claude-hooks/dispatches \
-f event_type=release
# Or use GitHub UI: Actions → Dispatch Release → Run workflow
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
Metadata
Release files for claude-hooks-template 1.6.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claude_hooks_template-1.6.4.tar.gz | 43.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claude_hooks_template-1.6.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.1 kB
Release files / claude_hooks_template-1.6.4.tar.gz
| Download URL | claude_hooks_template-1.6.4.tar.gz |
|---|---|
| Size | 43.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2ad05b7eda43179d0d2a29f48e0b4484b586e2afba033c3951e248948318a19
|
|
BLAKE2b-256 checksum How to use checksums |
c4dcae9a623f3664942491f50a9c7a84440b753f2524273a60fd9d3dc2ca5885
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 13, 2025.
Transparency logRelease files / claude_hooks_template-1.6.4-py3-none-any.whl
| Download URL | claude_hooks_template-1.6.4-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d05c8e25621f54bb1927487409201b1b0e5438f28d8b9248c83c89a7cef352c
|
|
BLAKE2b-256 checksum How to use checksums |
289296ff0486d1628d953c513da47e77f4c67626c2966ef6c6c598951025c30b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 13, 2025.
Transparency log