claude-hooks


Nameclaude-hooks JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
SummaryPython utilities for handling Claude Code hooks with a framework for creating event-driven hooks
upload_time2025-07-18 16:34:07
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseApache-2.0
keywords ai automation claude hooks
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # claude-hooks

## Why This Exists

A portable Python framework for writing Claude Code hooks that reduces boilerplate and provides essential infrastructure:

- **Portable Python Hooks**: Write hooks in Python with proper dependency management via `uv`
- **Automatic Logging**: Built-in rotating log files for all hook executions
- **Dependency Support**: Use `uv` inline dependencies in your hooks when needed
- **Isolated Environment**: All hooks run in their own virtual environment
- **Framework Structure**: Provides the scaffolding to focus on hook logic, not infrastructure

## Installation vs Creation

This package provides two main operations:

**`uvx claude-hooks init`** - **Installs** hook templates and creates/updates `settings.json`
- Creates template files for all hook types (or specific ones with arguments)
- Creates a new `settings.json` file or **merges** hook configurations into existing one
- Existing `settings.json` content is preserved - only adds missing hook entries
- Templates provide the framework structure and automatic logging setup

**`uvx claude-hooks create <filename>`** - **Creates** a single hook file
- Generates one hook file without touching `settings.json`
- Provided for flexibility init is expected to be most frequently used

```bash
# Install all hook templates + settings.json
uvx claude-hooks init

# Install specific hook templates + settings.json  
uvx claude-hooks init notification pre-tool-use

# Create a single hook file (no settings.json changes)
uvx claude-hooks create notification.py

# See all available hook types and options
uvx claude-hooks init --help
uvx claude-hooks create --help
```

## Folder Structure

When you run `init`, this framework creates a specific folder structure:

```
.claude/
├── hooks/                    # All hook files go here
│   ├── notification.py
│   ├── pre_tool_use.py
│   ├── post_tool_use.py
│   ├── stop.py
│   ├── subagent_stop.py
│   ├── pre_compact.py
│   └── logs/                 # Auto-created when hooks run
│       ├── notification.log
│       ├── pretooluse.log
│       └── ...
└── settings.json             # Claude Code configuration
```

**For global hooks in `~/.claude/`:**
- Hook files: `~/.claude/hooks/*.py` 
- Log files: `~/.claude/hooks/logs/*.log`
- Settings: `~/.claude/settings.json`

**For project hooks:**
- Hook files: `./.claude/hooks/*.py`
- Log files: `./.claude/hooks/logs/*.log`
- Settings: `./.claude/settings.json`

## Global vs Project Hooks

You can install hooks at different levels to serve different purposes:

### Global Hooks (~/.claude/)
Install hooks in your global Claude directory for system-wide functionality:

```bash
cd ~/.claude
uvx claude-hooks init notification stop
```

**Use global hooks for:**
- **Universal logging**: Log all Claude Code activity across all projects
- **General notifications**: System-wide alerts and monitoring
- **Security policies**: Apply consistent security rules everywhere
- **Personal preferences**: Your own workflow and safety checks

### Project Hooks (./project-root/)
Install hooks in specific project directories for project-specific functionality:

```bash
cd /path/to/your/project
uvx claude-hooks init pre-tool-use post-tool-use
```

**Use project hooks for:**
- **Project-specific linting**: Run project's linter/formatter on file edits
- **Code quality**: Enforce project coding standards and conventions
- **CI/CD integration**: Validate changes before they're made
- **Team policies**: Shared rules that apply to all team members

### Why This Matters

**Global hooks** provide consistent behavior across all your Claude Code usage with centralized logging and personal preferences.

**Project hooks** can be shared with your team, ensuring consistent project-specific behavior for all team members.

Claude Code will use hooks from both locations when available, with project hooks taking precedence for overlapping functionality.

## Usage

### Simple Exit Code Hooks

```python
from claude_hooks import run_hooks

def security_hook(event):
    # PreToolUse hook - supports approve, block, undefined
    if event.tool_name == "Bash":
        command = event.tool_input.get("command", "")
        if "rm -rf" in command:
            return event.block("Dangerous command blocked")
        elif command.startswith("ls"):
            return event.approve("Safe listing command")
    
    return event.undefined()  # Let Claude Code decide

if __name__ == "__main__":
    run_hooks(security_hook)
```

### Advanced JSON Output Hooks

```python
from claude_hooks import run_hooks

def advanced_hook(event):
    if event.tool_name == "Edit":
        file_path = event.tool_input.get("file_path", "")
        
        # Block with JSON output
        if ".env" in file_path:
            return event.block_json("Sensitive file access blocked")
        
        # Approve with suppressed transcript output
        if file_path.endswith(".log"):
            return event.approve_json("Log access approved", suppress_output=True)
        
        # Stop Claude entirely for critical files
        if "critical" in file_path:
            return event.stop_claude("Manual review required for critical files")
    
    return event.undefined_json()

if __name__ == "__main__":
    run_hooks(advanced_hook)
```

### Parallel Hook Execution

Run multiple hooks in parallel - any hook returning `BLOCK` immediately blocks the operation:

```python
from claude_hooks import run_hooks

def security_hook(event):
    if "dangerous" in event.tool_input.get("command", ""):
        return event.block("Security violation")
    return event.undefined()

def audit_hook(event):
    # Log all tool usage
    print(f"Tool used: {event.tool_name}")
    return event.undefined()

def compliance_hook(event):
    if event.tool_name in ["Edit", "Write"]:
        file_path = event.tool_input.get("file_path", "")
        if ".env" in file_path:
            return event.block("Compliance: No env file modifications")
    return event.undefined()

if __name__ == "__main__":
    # All hooks run in parallel - first block wins
    run_hooks(security_hook, audit_hook, compliance_hook)
```

### Serial vs Parallel Execution

**Serial Execution**: Functions call each other in sequence
```python
def first_hook(event):
    # First security check
    if "rm -rf" in event.tool_input.get("command", ""):
        return event.block("Dangerous command")
    
    # Pass to next hook
    return second_hook(event)

def second_hook(event):
    # Second check after first passes
    if "sudo" in event.tool_input.get("command", ""):
        return event.block("Elevated privileges")
    
    return event.undefined()

if __name__ == "__main__":
    run_hooks(first_hook)  # Only register the first - it calls the second
```

**Parallel Execution**: Register multiple functions to run simultaneously
```python
def security_hook(event):
    if "rm -rf" in event.tool_input.get("command", ""):
        return event.block("Security violation")
    return event.undefined()

def compliance_hook(event):
    if "sudo" in event.tool_input.get("command", ""):
        return event.block("Compliance violation")
    return event.undefined()

if __name__ == "__main__":
    # Both hooks run in parallel - first to block wins
    run_hooks(security_hook, compliance_hook)
```

### Hook-Specific Constraints

Each hook type supports different decision types:

```python
# PreToolUse - supports all decisions
def pre_tool_hook(event):
    return event.approve("reason")  # ✅ Allowed
    return event.block("reason")    # ✅ Allowed  
    return event.undefined()        # ✅ Allowed

# PostToolUse - only block and undefined
def post_tool_hook(event):
    return event.approve("reason")  # ❌ Raises NotImplementedError
    return event.block("reason")    # ✅ Allowed
    return event.undefined()        # ✅ Allowed

# Notification - only undefined behavior  
def notification_hook(event):
    print(f"Received: {event.message}")
    return None  # ✅ Allowed (treated as undefined)
    # or return event.undefined()
```

## Hook Types

| Hook Type | Supports | Description |
|-----------|----------|-------------|
| **PreToolUse** | approve, block, undefined | Called before tool execution, can block or approve tools |
| **PostToolUse** | block, undefined | Called after tool execution with response data |
| **Notification** | undefined only | Called for various Claude Code notifications |
| **Stop** | block, undefined | Called when Claude finishes, can prevent stopping |
| **SubagentStop** | block, undefined | Called when Claude subagent stops |
| **PreCompact** | undefined only | Called before conversation compaction |

## Output Modes

### Simple Exit Codes
- **Exit 0**: Success/approve (stdout shown to user in transcript)
- **Exit 2**: Block operation (stderr fed back to Claude)

### Advanced JSON Output
- **Decision Control**: `"approve"`, `"block"`, or undefined
- **Continue Control**: Stop Claude with `"continue": false`
- **Output Suppression**: Hide from transcript with `"suppressOutput": true`
- **Stop Reason**: Custom message when stopping Claude

Both upstream naming (`suppressOutput`, `stopReason`) and Python-style shortcuts (`suppress_output`, `stop_reason`) are supported.

## Upstream Compatibility

This framework is fully compatible with Claude Code's official hook specification:

- **Hook Input Format**: [https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input](https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input)
- **Hook Output Format**: [https://docs.anthropic.com/en/docs/claude-code/hooks#hook-output](https://docs.anthropic.com/en/docs/claude-code/hooks#hook-output)

## Testing Hooks

After creating or editing hook files, test them to catch runtime errors before they affect Claude Code sessions:

```bash
# From your hooks directory (where pyproject.toml is located)
cd ~/.claude/hooks  # or cd ./hooks for project hooks

# Test hook types with sample data
uv run claude-hooks test notification --message "Test notification" -v
uv run claude-hooks test pre-tool-use --tool "Bash" --command "ls -la" -v  
uv run claude-hooks test post-tool-use --tool "Read" --output "file contents" -v
uv run claude-hooks test stop --session-id "test-123" -v

# See all test options
uv run claude-hooks test --help
```

**Important**: Always run tests from the hooks directory after editing any hook files. Failed hooks show detailed error messages and stack traces to help debug issues.

## Convenience Features

This framework adds developer-friendly features on top of the upstream specification:

### Event Helper Classes
- **Type-safe access**: `event.tool_name`, `event.tool_input`, `event.tool_response`  
- **Convenience properties**: `event.session_id`, `event.transcript_path`
- **Validation**: Automatic validation of required fields per hook type

### Decision Helper Methods
- **Simple**: `event.block("reason")`, `event.approve("reason")`, `event.undefined()`
- **JSON**: `event.block_json()`, `event.approve_json()`, `event.undefined_json()`, `event.stop_claude()`
- **Global**: `block()`, `approve()`, `undefined()`, `block_json()`, etc.

### Automatic Logging
All hook functions get automatic logging with no imports required:

```python
def my_hook(event):
    event.logger.info("Hook executed successfully")
    event.logger.debug("Detailed debug information")  
    event.logger.warning("Something needs attention")
    event.logger.error("An error occurred")
    return event.undefined()
```

**Log Location**: `logs/{event_name}.log` (e.g., `notification.log`, `pretooluse.log`)  
**Log Format**: `timestamp [function_name] LEVEL: message`

**Control Log Level**: Set environment variable before starting Claude Code:
```bash
export CLAUDE_HOOKS_LOG_LEVEL=DEBUG  # DEBUG, INFO (default), WARNING, ERROR, CRITICAL
claude
```

## License

Apache-2.0

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "claude-hooks",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "ai, automation, claude, hooks",
    "author": null,
    "author_email": "Chris Sanders <sanders.chris@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/8f/2b/e5235af6c2de8e0f838ffa97b1dc8c77c98c589242e9ac83d8bd06d15294/claude_hooks-0.1.1.tar.gz",
    "platform": null,
    "description": "# claude-hooks\n\n## Why This Exists\n\nA portable Python framework for writing Claude Code hooks that reduces boilerplate and provides essential infrastructure:\n\n- **Portable Python Hooks**: Write hooks in Python with proper dependency management via `uv`\n- **Automatic Logging**: Built-in rotating log files for all hook executions\n- **Dependency Support**: Use `uv` inline dependencies in your hooks when needed\n- **Isolated Environment**: All hooks run in their own virtual environment\n- **Framework Structure**: Provides the scaffolding to focus on hook logic, not infrastructure\n\n## Installation vs Creation\n\nThis package provides two main operations:\n\n**`uvx claude-hooks init`** - **Installs** hook templates and creates/updates `settings.json`\n- Creates template files for all hook types (or specific ones with arguments)\n- Creates a new `settings.json` file or **merges** hook configurations into existing one\n- Existing `settings.json` content is preserved - only adds missing hook entries\n- Templates provide the framework structure and automatic logging setup\n\n**`uvx claude-hooks create <filename>`** - **Creates** a single hook file\n- Generates one hook file without touching `settings.json`\n- Provided for flexibility init is expected to be most frequently used\n\n```bash\n# Install all hook templates + settings.json\nuvx claude-hooks init\n\n# Install specific hook templates + settings.json  \nuvx claude-hooks init notification pre-tool-use\n\n# Create a single hook file (no settings.json changes)\nuvx claude-hooks create notification.py\n\n# See all available hook types and options\nuvx claude-hooks init --help\nuvx claude-hooks create --help\n```\n\n## Folder Structure\n\nWhen you run `init`, this framework creates a specific folder structure:\n\n```\n.claude/\n\u251c\u2500\u2500 hooks/                    # All hook files go here\n\u2502   \u251c\u2500\u2500 notification.py\n\u2502   \u251c\u2500\u2500 pre_tool_use.py\n\u2502   \u251c\u2500\u2500 post_tool_use.py\n\u2502   \u251c\u2500\u2500 stop.py\n\u2502   \u251c\u2500\u2500 subagent_stop.py\n\u2502   \u251c\u2500\u2500 pre_compact.py\n\u2502   \u2514\u2500\u2500 logs/                 # Auto-created when hooks run\n\u2502       \u251c\u2500\u2500 notification.log\n\u2502       \u251c\u2500\u2500 pretooluse.log\n\u2502       \u2514\u2500\u2500 ...\n\u2514\u2500\u2500 settings.json             # Claude Code configuration\n```\n\n**For global hooks in `~/.claude/`:**\n- Hook files: `~/.claude/hooks/*.py` \n- Log files: `~/.claude/hooks/logs/*.log`\n- Settings: `~/.claude/settings.json`\n\n**For project hooks:**\n- Hook files: `./.claude/hooks/*.py`\n- Log files: `./.claude/hooks/logs/*.log`\n- Settings: `./.claude/settings.json`\n\n## Global vs Project Hooks\n\nYou can install hooks at different levels to serve different purposes:\n\n### Global Hooks (~/.claude/)\nInstall hooks in your global Claude directory for system-wide functionality:\n\n```bash\ncd ~/.claude\nuvx claude-hooks init notification stop\n```\n\n**Use global hooks for:**\n- **Universal logging**: Log all Claude Code activity across all projects\n- **General notifications**: System-wide alerts and monitoring\n- **Security policies**: Apply consistent security rules everywhere\n- **Personal preferences**: Your own workflow and safety checks\n\n### Project Hooks (./project-root/)\nInstall hooks in specific project directories for project-specific functionality:\n\n```bash\ncd /path/to/your/project\nuvx claude-hooks init pre-tool-use post-tool-use\n```\n\n**Use project hooks for:**\n- **Project-specific linting**: Run project's linter/formatter on file edits\n- **Code quality**: Enforce project coding standards and conventions\n- **CI/CD integration**: Validate changes before they're made\n- **Team policies**: Shared rules that apply to all team members\n\n### Why This Matters\n\n**Global hooks** provide consistent behavior across all your Claude Code usage with centralized logging and personal preferences.\n\n**Project hooks** can be shared with your team, ensuring consistent project-specific behavior for all team members.\n\nClaude Code will use hooks from both locations when available, with project hooks taking precedence for overlapping functionality.\n\n## Usage\n\n### Simple Exit Code Hooks\n\n```python\nfrom claude_hooks import run_hooks\n\ndef security_hook(event):\n    # PreToolUse hook - supports approve, block, undefined\n    if event.tool_name == \"Bash\":\n        command = event.tool_input.get(\"command\", \"\")\n        if \"rm -rf\" in command:\n            return event.block(\"Dangerous command blocked\")\n        elif command.startswith(\"ls\"):\n            return event.approve(\"Safe listing command\")\n    \n    return event.undefined()  # Let Claude Code decide\n\nif __name__ == \"__main__\":\n    run_hooks(security_hook)\n```\n\n### Advanced JSON Output Hooks\n\n```python\nfrom claude_hooks import run_hooks\n\ndef advanced_hook(event):\n    if event.tool_name == \"Edit\":\n        file_path = event.tool_input.get(\"file_path\", \"\")\n        \n        # Block with JSON output\n        if \".env\" in file_path:\n            return event.block_json(\"Sensitive file access blocked\")\n        \n        # Approve with suppressed transcript output\n        if file_path.endswith(\".log\"):\n            return event.approve_json(\"Log access approved\", suppress_output=True)\n        \n        # Stop Claude entirely for critical files\n        if \"critical\" in file_path:\n            return event.stop_claude(\"Manual review required for critical files\")\n    \n    return event.undefined_json()\n\nif __name__ == \"__main__\":\n    run_hooks(advanced_hook)\n```\n\n### Parallel Hook Execution\n\nRun multiple hooks in parallel - any hook returning `BLOCK` immediately blocks the operation:\n\n```python\nfrom claude_hooks import run_hooks\n\ndef security_hook(event):\n    if \"dangerous\" in event.tool_input.get(\"command\", \"\"):\n        return event.block(\"Security violation\")\n    return event.undefined()\n\ndef audit_hook(event):\n    # Log all tool usage\n    print(f\"Tool used: {event.tool_name}\")\n    return event.undefined()\n\ndef compliance_hook(event):\n    if event.tool_name in [\"Edit\", \"Write\"]:\n        file_path = event.tool_input.get(\"file_path\", \"\")\n        if \".env\" in file_path:\n            return event.block(\"Compliance: No env file modifications\")\n    return event.undefined()\n\nif __name__ == \"__main__\":\n    # All hooks run in parallel - first block wins\n    run_hooks(security_hook, audit_hook, compliance_hook)\n```\n\n### Serial vs Parallel Execution\n\n**Serial Execution**: Functions call each other in sequence\n```python\ndef first_hook(event):\n    # First security check\n    if \"rm -rf\" in event.tool_input.get(\"command\", \"\"):\n        return event.block(\"Dangerous command\")\n    \n    # Pass to next hook\n    return second_hook(event)\n\ndef second_hook(event):\n    # Second check after first passes\n    if \"sudo\" in event.tool_input.get(\"command\", \"\"):\n        return event.block(\"Elevated privileges\")\n    \n    return event.undefined()\n\nif __name__ == \"__main__\":\n    run_hooks(first_hook)  # Only register the first - it calls the second\n```\n\n**Parallel Execution**: Register multiple functions to run simultaneously\n```python\ndef security_hook(event):\n    if \"rm -rf\" in event.tool_input.get(\"command\", \"\"):\n        return event.block(\"Security violation\")\n    return event.undefined()\n\ndef compliance_hook(event):\n    if \"sudo\" in event.tool_input.get(\"command\", \"\"):\n        return event.block(\"Compliance violation\")\n    return event.undefined()\n\nif __name__ == \"__main__\":\n    # Both hooks run in parallel - first to block wins\n    run_hooks(security_hook, compliance_hook)\n```\n\n### Hook-Specific Constraints\n\nEach hook type supports different decision types:\n\n```python\n# PreToolUse - supports all decisions\ndef pre_tool_hook(event):\n    return event.approve(\"reason\")  # \u2705 Allowed\n    return event.block(\"reason\")    # \u2705 Allowed  \n    return event.undefined()        # \u2705 Allowed\n\n# PostToolUse - only block and undefined\ndef post_tool_hook(event):\n    return event.approve(\"reason\")  # \u274c Raises NotImplementedError\n    return event.block(\"reason\")    # \u2705 Allowed\n    return event.undefined()        # \u2705 Allowed\n\n# Notification - only undefined behavior  \ndef notification_hook(event):\n    print(f\"Received: {event.message}\")\n    return None  # \u2705 Allowed (treated as undefined)\n    # or return event.undefined()\n```\n\n## Hook Types\n\n| Hook Type | Supports | Description |\n|-----------|----------|-------------|\n| **PreToolUse** | approve, block, undefined | Called before tool execution, can block or approve tools |\n| **PostToolUse** | block, undefined | Called after tool execution with response data |\n| **Notification** | undefined only | Called for various Claude Code notifications |\n| **Stop** | block, undefined | Called when Claude finishes, can prevent stopping |\n| **SubagentStop** | block, undefined | Called when Claude subagent stops |\n| **PreCompact** | undefined only | Called before conversation compaction |\n\n## Output Modes\n\n### Simple Exit Codes\n- **Exit 0**: Success/approve (stdout shown to user in transcript)\n- **Exit 2**: Block operation (stderr fed back to Claude)\n\n### Advanced JSON Output\n- **Decision Control**: `\"approve\"`, `\"block\"`, or undefined\n- **Continue Control**: Stop Claude with `\"continue\": false`\n- **Output Suppression**: Hide from transcript with `\"suppressOutput\": true`\n- **Stop Reason**: Custom message when stopping Claude\n\nBoth upstream naming (`suppressOutput`, `stopReason`) and Python-style shortcuts (`suppress_output`, `stop_reason`) are supported.\n\n## Upstream Compatibility\n\nThis framework is fully compatible with Claude Code's official hook specification:\n\n- **Hook Input Format**: [https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input](https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input)\n- **Hook Output Format**: [https://docs.anthropic.com/en/docs/claude-code/hooks#hook-output](https://docs.anthropic.com/en/docs/claude-code/hooks#hook-output)\n\n## Testing Hooks\n\nAfter creating or editing hook files, test them to catch runtime errors before they affect Claude Code sessions:\n\n```bash\n# From your hooks directory (where pyproject.toml is located)\ncd ~/.claude/hooks  # or cd ./hooks for project hooks\n\n# Test hook types with sample data\nuv run claude-hooks test notification --message \"Test notification\" -v\nuv run claude-hooks test pre-tool-use --tool \"Bash\" --command \"ls -la\" -v  \nuv run claude-hooks test post-tool-use --tool \"Read\" --output \"file contents\" -v\nuv run claude-hooks test stop --session-id \"test-123\" -v\n\n# See all test options\nuv run claude-hooks test --help\n```\n\n**Important**: Always run tests from the hooks directory after editing any hook files. Failed hooks show detailed error messages and stack traces to help debug issues.\n\n## Convenience Features\n\nThis framework adds developer-friendly features on top of the upstream specification:\n\n### Event Helper Classes\n- **Type-safe access**: `event.tool_name`, `event.tool_input`, `event.tool_response`  \n- **Convenience properties**: `event.session_id`, `event.transcript_path`\n- **Validation**: Automatic validation of required fields per hook type\n\n### Decision Helper Methods\n- **Simple**: `event.block(\"reason\")`, `event.approve(\"reason\")`, `event.undefined()`\n- **JSON**: `event.block_json()`, `event.approve_json()`, `event.undefined_json()`, `event.stop_claude()`\n- **Global**: `block()`, `approve()`, `undefined()`, `block_json()`, etc.\n\n### Automatic Logging\nAll hook functions get automatic logging with no imports required:\n\n```python\ndef my_hook(event):\n    event.logger.info(\"Hook executed successfully\")\n    event.logger.debug(\"Detailed debug information\")  \n    event.logger.warning(\"Something needs attention\")\n    event.logger.error(\"An error occurred\")\n    return event.undefined()\n```\n\n**Log Location**: `logs/{event_name}.log` (e.g., `notification.log`, `pretooluse.log`)  \n**Log Format**: `timestamp [function_name] LEVEL: message`\n\n**Control Log Level**: Set environment variable before starting Claude Code:\n```bash\nexport CLAUDE_HOOKS_LOG_LEVEL=DEBUG  # DEBUG, INFO (default), WARNING, ERROR, CRITICAL\nclaude\n```\n\n## License\n\nApache-2.0\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "Python utilities for handling Claude Code hooks with a framework for creating event-driven hooks",
    "version": "0.1.1",
    "project_urls": {
        "Homepage": "https://github.com/chrissanders/claude-hooks",
        "Issues": "https://github.com/chrissanders/claude-hooks/issues",
        "Repository": "https://github.com/chrissanders/claude-hooks"
    },
    "split_keywords": [
        "ai",
        " automation",
        " claude",
        " hooks"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "079edc2d04181a3b3df8bc713280687565ab1a74c35dd6aaf07b35b4e89336f7",
                "md5": "b8aff035e311bf029fa6a5fb92c1ae0a",
                "sha256": "06a6ffd2701829b354bf18534b4ac62ee796eb936f0250f4eb5ffaa7c3d1b8f9"
            },
            "downloads": -1,
            "filename": "claude_hooks-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b8aff035e311bf029fa6a5fb92c1ae0a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 22057,
            "upload_time": "2025-07-18T16:34:06",
            "upload_time_iso_8601": "2025-07-18T16:34:06.734682Z",
            "url": "https://files.pythonhosted.org/packages/07/9e/dc2d04181a3b3df8bc713280687565ab1a74c35dd6aaf07b35b4e89336f7/claude_hooks-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8f2be5235af6c2de8e0f838ffa97b1dc8c77c98c589242e9ac83d8bd06d15294",
                "md5": "fee48d1bc285fc5ab84d6cd4d8588364",
                "sha256": "620822fd92b6fd29c8bd25002884112cfaf0e11b4a560539e3e90c8c59e71e0c"
            },
            "downloads": -1,
            "filename": "claude_hooks-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "fee48d1bc285fc5ab84d6cd4d8588364",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 33538,
            "upload_time": "2025-07-18T16:34:07",
            "upload_time_iso_8601": "2025-07-18T16:34:07.627800Z",
            "url": "https://files.pythonhosted.org/packages/8f/2b/e5235af6c2de8e0f838ffa97b1dc8c77c98c589242e9ac83d8bd06d15294/claude_hooks-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-18 16:34:07",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "chrissanders",
    "github_project": "claude-hooks",
    "github_not_found": true,
    "lcname": "claude-hooks"
}
        
Elapsed time: 0.93649s