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
.shhook 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>:
- 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/
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45cf31046b6d46dfb79c1db0a36b56b02be673bb4fc7e0c592f4cf7d7cf38209
|
|
| MD5 |
2f6dffb372cfaba1e73c54bfb512e0ad
|
|
| BLAKE2b-256 |
e5c39bf4d9f8b3735191af6f165e9c1f5481358a2d536529fe4540ffbc1cfd30
|
Provenance
The following attestation bundles were made for claude_hooks_template-1.4.1.tar.gz:
Publisher:
release.yml on stevennevins/claude-hooks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_hooks_template-1.4.1.tar.gz -
Subject digest:
45cf31046b6d46dfb79c1db0a36b56b02be673bb4fc7e0c592f4cf7d7cf38209 - Sigstore transparency entry: 699785400
- Sigstore integration time:
-
Permalink:
stevennevins/claude-hooks@a106b70deafb81132003eefd9345122e17e52862 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stevennevins
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a106b70deafb81132003eefd9345122e17e52862 -
Trigger Event:
push
-
Statement type:
File details
Details for the file claude_hooks_template-1.4.1-py3-none-any.whl.
File metadata
- Download URL: claude_hooks_template-1.4.1-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c02977aabdf10cac8e13904635f8b8aafa49a32840f716d20ab7955f6782fc9
|
|
| MD5 |
356aa198c49df0de4640471946c55bd5
|
|
| BLAKE2b-256 |
792ddfb36b9a1855313f5accf80de960dfda64159bd7017dd266a9d9ba4fbe0f
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_hooks_template-1.4.1-py3-none-any.whl -
Subject digest:
4c02977aabdf10cac8e13904635f8b8aafa49a32840f716d20ab7955f6782fc9 - Sigstore transparency entry: 699785401
- Sigstore integration time:
-
Permalink:
stevennevins/claude-hooks@a106b70deafb81132003eefd9345122e17e52862 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stevennevins
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a106b70deafb81132003eefd9345122e17e52862 -
Trigger Event:
push
-
Statement type: