Create Claude Code hooks for static analysis and code quality. Provides pre-built linting, formatting, and type-checking hooks. Use when setting up project quality gates, enforcing code standards, or adding pre-commit style checks.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
references/events.mdreferences/static-checks.mdscripts/add_hook.pyCreate hooks that enforce code quality through static analysis.
Add a static check hook:
python3 scripts/add_hook.py <hook-name> --type <lint|format|typecheck|custom>
Options:
--type, -t: Hook template type (default: custom)--path, -p: Output directory (default: .claude/hooks)--event, -e: Hook event (default: PostToolUse).claude/
├── hooks/
│ ├── eslint_check.py # Linting hook
│ ├── prettier_check.py # Formatting hook
│ └── typecheck.py # Type checking hook
└── settings.json # Hook configuration
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/eslint_check.py",
"timeout": 30
}]
}
]
}
}
#!/usr/bin/env python3
import json, subprocess, sys
data = json.load(sys.stdin)
file_path = data.get('tool_input', {}).get('file_path', '')
if not file_path.endswith(('.js', '.ts', '.tsx')):
sys.exit(0)
result = subprocess.run(
['npx', 'eslint', '--format', 'compact', file_path],
capture_output=True, text=True
)
if result.returncode != 0:
print(f"ESLint errors:\n{result.stdout}", file=sys.stderr)
sys.exit(2) # Block the action
#!/usr/bin/env python3
import json, subprocess, sys
data = json.load(sys.stdin)
file_path = data.get('tool_input', {}).get('file_path', '')
result = subprocess.run(
['npx', 'prettier', '--check', file_path],
capture_output=True, text=True
)
if result.returncode != 0:
# Auto-fix instead of blocking
subprocess.run(['npx', 'prettier', '--write', file_path])
print(f"Auto-formatted: {file_path}")
#!/usr/bin/env python3
import json, subprocess, sys
data = json.load(sys.stdin)
file_path = data.get('tool_input', {}).get('file_path', '')
if not file_path.endswith(('.ts', '.tsx')):
sys.exit(0)
result = subprocess.run(
['npx', 'tsc', '--noEmit', '--pretty', 'false'],
capture_output=True, text=True
)
if result.returncode != 0:
# Filter errors for this file only
errors = [l for l in result.stdout.split('\n') if file_path in l]
if errors:
print('\n'.join(errors[:5]), file=sys.stderr)
sys.exit(2)
| Event | Use Case | Blocking |
|---|---|---|
PreToolUse | Validate before write | Yes |
PostToolUse | Check after write | Yes |
UserPromptSubmit | Add linting context | Yes |
| Code | Meaning | Effect |
|---|---|---|
| 0 | Success | Continue, stdout shown in verbose |
| 2 | Block | Action blocked, stderr fed to Claude |
| Other | Warning | Continue, stderr shown in verbose |