Skip to main content

Claude PyHooks

A lightweight framework for implementing Claude Code hooks in Python.


Overview

Claude PyHooks makes it easy to build and register custom hooks for Claude Code.
The following hook types are supported:

Hook type Typical use cases
PreToolUse Validate or block tool calls (e.g. dangerous shell commands)
PostToolUse Post-processing after tool execution (logging, backups, notifications, …)
Notification Handle general notifications emitted by Claude Code
Stop Run cleanup logic when a session ends
SubagentStop Run cleanup logic when a sub-agent terminates

Installation

pip install claude-pyhooks
# or, if you use uv
uv add claude-pyhooks

Development version (recommended with uv)

git clone <this-repository>
cd claude-pyhooks
uv sync
uv pip install -e .

Quick start

PreToolUse — block a dangerous command

#!/usr/bin/env python3
from claude_pyhooks import (
    PreToolUseInput,
    PreToolUseOutput,
    BashInput,
)

def main() -> None:
    hook_input = PreToolUseInput.from_stdin()

    if hook_input.tool_name == "Bash":
        assert isinstance(hook_input.tool_input, BashInput)
        command = (hook_input.tool_input.command or "").strip()

        if "rm -rf /" in command:
            PreToolUseOutput.block(
                f"Dangerous command blocked: {command}"
            ).execute()

if __name__ == "__main__":
    main()

PostToolUse — log every Bash run

#!/usr/bin/env python3
from claude_pyhooks import (
    PreToolUseInput,
    PreToolUseOutput,
    BashInput,
)

def main() -> None:
    hook_input = PreToolUseInput.from_stdin()

    if hook_input.tool_name == "Bash":
        assert isinstance(hook_input.tool_input, BashInput)
        command = (hook_input.tool_input.command or "").strip()

        if "rm -rf /" in command:
            PreToolUseOutput.block(
                f"Dangerous command blocked: {command}"
            ).execute()

if __name__ == "__main__":
    main()

Notification — play a beep

#!/usr/bin/env python3
from pathlib import Path
from claude_pyhooks import (
    PostToolUseInput,
    BashInput,
    BashResponse,
)

log_file = Path(__file__).with_name("bash_command_log.txt")

def main() -> None:
    hook_input = PostToolUseInput.from_stdin()

    if hook_input.tool_name == "Bash":
        assert isinstance(hook_input.tool_input, BashInput)
        assert isinstance(hook_input.tool_response, BashResponse)

        command = hook_input.tool_input.command
        success = not hook_input.tool_response.stderr
        status = "success" if success else "failure"

        with log_file.open("a") as f:
            f.write(f"{status}: {command}\n")

if __name__ == "__main__":
    main()

Registering hooks with Claude Code

Add the hooks to ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "uv run /path/to/pre_hook.py"
          }
        ]
      }
    ]
  }
}

API highlights

Input classes

Class Description
PreToolUseInput Data passed before a tool runs
PostToolUseInput Data passed after a tool runs
NotificationInput Generic notification payload
StopInput Session-end payload
SubagentStopInput Sub-agent end payload

tool_input and tool_response are automatically parsed into strongly-typed objects such as BashInput or BashResponse.

Output classes

Class Description
PreToolUseOutput Approve or block execution (approve(), block())
PostToolUseOutput Return post-processing commands
StopOutput Commands on session end
SubagentStopOutput Commands on sub-agent end

Calling .execute() dispatches the command queue.
If the output blocks execution, the process exits with code 2 automatically.

Beep utilities

Pre-defined constants:

  • DEFAULT_BEEP
  • SUCCESS_BEEP
  • ERROR_BEEP
  • WARNING_BEEP

Each constant is an instance of BeepCommand and can be part of a BeepSequence.


Examples

See the examples/ directory for practical scripts:

File What it does
pre_bash.py Blocks dangerous Bash commands
post_bash.py Logs Bash command results
notification_beep.py Plays beeps based on notification

License

MIT License

Metadata

Release files for claude-pyhooks 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for claude-pyhooks 0.1.1
File Size Uploaded
claude_pyhooks-0.1.1.tar.gz 26.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for claude-pyhooks 0.1.1
File Interpreter ABI Platform
claude_pyhooks-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 43.2 kB

Release files / claude_pyhooks-0.1.1.tar.gz

Download URL claude_pyhooks-0.1.1.tar.gz
Size 26.1 kB
Tags Source
SHA-256 checksum
How to use checksums
66d0fbd08aa2e8869fef7b8de76fe23d5d66149a1ed7c5c957b0689b0247efd5
BLAKE2b-256 checksum
How to use checksums
0243a6862841dbcdce270ff5347a7f8e23f6a16f7271c90aff3a1aa21850c002
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.3

Release files / claude_pyhooks-0.1.1-py3-none-any.whl

Download URL claude_pyhooks-0.1.1-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd6f4bd8a1856d3cc5148cc0aac1102efac3e4e6e05b1fd84b1709f9d65784c9
BLAKE2b-256 checksum
How to use checksums
92b47bb0bbd305e7cc93741e1a7001a6f55234cf1b26e4cefa20b2577849ada5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page