Claude Code Hooks for uv Projects

November 11, 2025

Claude Code supports custom hooks that can guide the AI agent toward project-specific workflows. For Python projects using uv, these hooks ensure the AI consistently uses modern tooling patterns instead of falling back to traditional pip and python commands.

Note

This post builds on the interceptor approach for teaching AI agents about uv. While interceptors provide runtime feedback, Claude Code hooks offer deeper integration by preventing problematic commands before execution.

Understanding Claude Code Hooks

Claude Code supports three types of hooks that run at different stages of interaction:

  • PreToolUse: Executes before Claude runs commands, allowing intervention and command transformation
  • PostToolUse: Runs after tool execution to provide feedback or additional context
  • Notification: Monitors Claude’s message context for keywords and displays contextual reminders

These hooks are Python scripts that analyze and respond to Claude’s intended actions, enabling project-specific guidance without modifying Claude Code itself.

The uv Hook Configuration

Developer Glenn Matlin created a comprehensive hook configuration for uv projects that demonstrates how to guide Claude toward uv-first workflows.

Pre-Tool Use Hook

The pre-tool hook intercepts commands before execution, checking for Python-related operations that should use uv instead:

#!/usr/bin/env python3
import json
import sys

def main():
    # Read the tool use data from stdin
    data = json.load(sys.stdin)

    # Check if this is a Bash/Run command
    if data.get("tool") not in ["Bash", "Run"]:
        sys.exit(0)

    command = data.get("parameters", {}).get("command", "")

    # Detect problematic patterns
    patterns = {
        "python ": "uv run",
        "pip install": "uv add",
        "pytest": "uv run pytest",
        "ruff": "uv run ruff"
    }

    for old, new in patterns.items():
        if old in command:
            print(f"⚠️  Detected '{old}' in command.", file=sys.stderr)
            print(f"💡 In uv projects, use '{new}' instead.", file=sys.stderr)
            sys.exit(1)

if __name__ == "__main__":
    main()

This hook blocks commands containing traditional Python patterns and provides immediate feedback about the uv-equivalent command.

Notification Hook

The notification hook monitors Claude’s messages for Python-related keywords and provides contextual reminders:

#!/usr/bin/env python3
import json
import sys

def main():
    data = json.load(sys.stdin)
    message = data.get("content", "").lower()

    keywords = {
        "install": "Remember: Use 'uv add' for package installation",
        "run python": "Remember: Use 'uv run python' for scripts",
        "virtual environment": "Remember: uv handles environments automatically"
    }

    for keyword, reminder in keywords.items():
        if keyword in message:
            print(f"📝 {reminder}", file=sys.stderr)
            break

if __name__ == "__main__":
    main()

Unlike the pre-tool hook which blocks execution, the notification hook provides gentle reminders as Claude discusses Python operations.

Installation

Create Hook Directory

Claude Code looks for hooks in ~/.claude/hooks/. Create this directory:

mkdir -p ~/.claude/hooks

Add Hook Scripts

Download and save the hook scripts:

# Pre-tool use hook
curl -o ~/.claude/hooks/pre_tool_use_uv.py \
  https://gist.githubusercontent.com/glennmatlin/fadc41edc3bb9ff68ff9cfa5d6b8aca7/raw/pre_tool_use_uv.py

# Notification hook
curl -o ~/.claude/hooks/notification_uv.py \
  https://gist.githubusercontent.com/glennmatlin/fadc41edc3bb9ff68ff9cfa5d6b8aca7/raw/notification_uv.py

# Make them executable
chmod +x ~/.claude/hooks/pre_tool_use_uv.py
chmod +x ~/.claude/hooks/notification_uv.py

Configure Settings

Create or update ~/.claude/settings.json to register the hooks:

{
  "hooks": {
    "PreToolUse": [
      {
        "command": "~/.claude/hooks/pre_tool_use_uv.py",
        "tools": ["Bash", "Run"],
        "timeout": 10000
      }
    ],
    "Notification": [
      {
        "command": "~/.claude/hooks/notification_uv.py",
        "timeout": 5000
      }
    ]
  }
}

Restart Claude Code

Restart Claude Code for the hooks to take effect. The hooks will now monitor all Claude operations in your terminal session.

How It Works in Practice

When working on a uv project, the hooks provide multi-layered guidance:

Before execution, the pre-tool hook prevents traditional commands:

$ python script.py
⚠️  Detected 'python ' in command.
💡 In uv projects, use 'uv run' instead.

During conversation, the notification hook provides contextual reminders:

Claude: To install the package, I'll need to...
📝 Remember: Use 'uv add' for package installation

This approach creates a consistent learning environment where Claude adapts to uv workflows without manual correction on every interaction.

Comparison with Interceptors

Both hooks and interceptors guide AI agents toward uv, but they work differently:

AspectHooksInterceptors
ScopeClaude Code onlyAny AI agent or terminal session
IntegrationNative Claude Code featurePATH manipulation with direnv
Feedback timingBefore command executionDuring command execution
ConfigurationGlobal ~/.claude/ directoryPer-project .envrc files
ComplexityPython scripts with JSON parsingSimple shell scripts

Hooks provide deeper integration with Claude Code but require Claude-specific configuration. Interceptors work with any tool but require direnv and per-project setup.

Extending the Hooks

The hook scripts can be customized for project-specific patterns. For example, to add guidance about dependency groups:

# In pre_tool_use_uv.py, add to patterns:
patterns = {
    # ... existing patterns ...
    "pip install -r requirements-dev.txt": "uv sync --group dev"
}

Or to add reminders about tool execution:

# In notification_uv.py, add to keywords:
keywords = {
    # ... existing keywords ...
    "ruff check": "Consider: 'uv tool run ruff' for one-off usage"
}

These modifications allow tailoring the hooks to match team conventions and project structure.

Learn More

Last updated on

Please submit corrections and feedback...