Skip to main content

A lightweight framework for implementing Claude Code hooks in Python.

Project description

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

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_pyhooks-0.1.1.tar.gz (26.1 kB view details)

Uploaded Source

Built Distribution

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

claude_pyhooks-0.1.1-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

Details for the file claude_pyhooks-0.1.1.tar.gz.

File metadata

  • Download URL: claude_pyhooks-0.1.1.tar.gz
  • Upload date:
  • Size: 26.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for claude_pyhooks-0.1.1.tar.gz
Algorithm Hash digest
SHA256 66d0fbd08aa2e8869fef7b8de76fe23d5d66149a1ed7c5c957b0689b0247efd5
MD5 641b61e7f3e373e9b796616935c4aa07
BLAKE2b-256 0243a6862841dbcdce270ff5347a7f8e23f6a16f7271c90aff3a1aa21850c002

See more details on using hashes here.

File details

Details for the file claude_pyhooks-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: claude_pyhooks-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 17.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for claude_pyhooks-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cd6f4bd8a1856d3cc5148cc0aac1102efac3e4e6e05b1fd84b1709f9d65784c9
MD5 083baa3110442d2078c59faf068773af
BLAKE2b-256 92b47bb0bbd305e7cc93741e1a7001a6f55234cf1b26e4cefa20b2577849ada5

See more details on using hashes here.

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