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.0.tar.gz (26.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_pyhooks-0.1.0-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: claude_pyhooks-0.1.0.tar.gz
  • Upload date:
  • Size: 26.2 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.0.tar.gz
Algorithm Hash digest
SHA256 36b22f516057dcd29cb23bd4d689a6cb9cd7b1d17a6e99223f8b278b83637f06
MD5 f3ec3f146c655d0da5d65aecac039a73
BLAKE2b-256 e9cf0acf08655f42d91b25d42efadda6baa06115cecf50b8f7e01d79d4dba9a1

See more details on using hashes here.

File details

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

File metadata

  • Download URL: claude_pyhooks-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ffd2408b4261b92323dde36b3650e145a3278ccf03d8ca8b2de2cd4223522eff
MD5 9ed3fce1ef2cccd757fe81539833f4b2
BLAKE2b-256 8d642d15ea67670522ea2512326699d4bf35717f34675f5e6d179f5847733868

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