trax/.claude/hooks/type-check.py

81 lines
2.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Type-checking hook for Claude Code
Runs mypy/ruff on modified Python files
"""
import sys
import json
import subprocess
from pathlib import Path
def main():
try:
# Read input from Claude Code
input_data = json.loads(sys.stdin.read())
# Check if this is a file modification
tool = input_data.get('tool', '')
if tool not in ['edit', 'multi_edit', 'write']:
return 0
# Get the file path
result = input_data.get('result', {})
file_path = result.get('file_path', '')
if not file_path:
return 0
# Check if it's a Python file
if not file_path.endswith('.py'):
return 0
# Convert to Path object
file_path = Path(file_path)
if not file_path.exists():
return 0
# Run type checking with mypy
mypy_result = subprocess.run(
['uv', 'run', 'mypy', str(file_path)],
capture_output=True,
text=True,
cwd='/Users/enias/projects/my-ai-projects/apps/trax'
)
# Run linting with ruff
ruff_result = subprocess.run(
['uv', 'run', 'ruff', 'check', str(file_path)],
capture_output=True,
text=True,
cwd='/Users/enias/projects/my-ai-projects/apps/trax'
)
# Collect errors
errors = []
if mypy_result.returncode != 0:
errors.append(f"Type errors in {file_path}:\n{mypy_result.stdout}")
if ruff_result.returncode != 0:
errors.append(f"Linting errors in {file_path}:\n{ruff_result.stdout}")
# Report errors if any
if errors:
error_msg = "\n\n".join(errors)
print(f"❌ Quality Check Failed:\n\n{error_msg}", file=sys.stderr)
# Exit code 2 = blocking error (Claude Code must fix)
# Exit code 1 = warning (Claude Code is informed but continues)
return 2
# Success message
print(f"✅ Quality checks passed for {file_path}")
return 0
except Exception as e:
# Don't break Claude Code if hook fails
print(f"Hook error (non-blocking): {e}", file=sys.stderr)
return 0
if __name__ == "__main__":
sys.exit(main())