Created
October 14, 2025 13:09
-
-
Save Pradeek/4ac4295af3c4fce547ee18c603db0b44 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment