Created
October 14, 2025 13:09
-
-
Save Pradeek/4ac4295af3c4fce547ee18c603db0b44 to your computer and use it in GitHub Desktop.
Revisions
-
Pradeek created this gist
Oct 14, 2025 .There are no files selected for viewing
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 charactersOriginal 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