Skip to content

Instantly share code, notes, and snippets.

@Pradeek
Created October 14, 2025 13:09
Show Gist options
  • Select an option

  • Save Pradeek/4ac4295af3c4fce547ee18c603db0b44 to your computer and use it in GitHub Desktop.

Select an option

Save Pradeek/4ac4295af3c4fce547ee18c603db0b44 to your computer and use it in GitHub Desktop.

Revisions

  1. Pradeek created this gist Oct 14, 2025.
    58 changes: 58 additions & 0 deletions enforce-uv-python.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    #!/usr/bin/env python3
    """
    PreToolUse hook that denies bare 'python' commands in uv projects.
    Enforces using 'uv run python' for proper virtual environment isolation.
    Only applies when project has uv indicators (pyproject.toml with uv.lock or .venv managed by uv).
    Put this under ~/.claude/hooks/
    """
    import json
    import sys
    import os
    import re
    from datetime import datetime
    from pathlib import Path

    # Read input
    input_data = json.load(sys.stdin)

    # Extract tool information
    tool_name = input_data.get('tool_name', '')
    tool_input = input_data.get('tool_input', {})

    # Check if it's a Bash tool
    if tool_name == 'Bash':
    command = tool_input.get('command', '').strip()
    cwd = input_data.get('cwd', '')

    # Check if command starts with 'python' (but not 'python3' or specific paths)
    python_pattern = r'^python(?:\s|$)'
    uses_bare_python = bool(re.match(python_pattern, command))

    if uses_bare_python:
    # Check if this is a uv project by looking for indicators
    def is_uv_project(directory):
    path = Path(directory)

    # Walk up the directory tree looking for project root
    for current_path in [path] + list(path.parents):
    # Check for pyproject.toml + uv.lock
    pyproject_path = current_path / "pyproject.toml"
    uv_lock_path = current_path / "uv.lock"

    if pyproject_path.exists() and uv_lock_path.exists():
    return True

    # Check for .venv with pyvenv.cfg mentioning uv
    venv_path = current_path / ".venv" / "pyvenv.cfg"
    if venv_path.exists():
    try:
    with open(venv_path) as f:
    content = f.read()
    if 'uv' in content.lower():
    return True
    except:
    pass

    # Stop at git root or filesystem root
    if (current_path / ".git").exists() or current_path == current_path.parent:
    break