spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / examples / hooks / bash_command_validator_example.py
Thariq Shihiparproper hook file 3d2166e · on Jul 2, 2025 History
84 lines · 2.0 KBRaw
1#!/usr/bin/env python3
2"""
3Claude Code Hook: Bash Command Validator
4=========================================
5This hook runs as a PreToolUse hook for the Bash tool.
6It validates bash commands against a set of rules before execution.
7In this case it changes grep calls to using rg.
8
9Read more about hooks here: https://docs.anthropic.com/en/docs/claude-code/hooks
10
11Make sure to change your path to your actual script.
12
13{
14 "hooks": {
15 "PreToolUse": [
16 {
17 "matcher": "Bash",
18 "hooks": [
19 {
20 "type": "command",
21 "command": "python -m ./bash_command_validator_example"
22 }
23 ]
24 }
25 ]
26 }
27}
28
29"""
30
31import json
32import re
33import sys
34
35# Define validation rules as a list of (regex pattern, message) tuples
36VALIDATION_RULES = [
37 (
38 r"\bgrep\b(?!.*\|)",
39 "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
40 ),
41 (
42 r"\bfind\s+\S+\s+-name\b",
43 "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
44 ),
45]
46
47
48def validate_command(command: str) -> list[str]:
49 issues = []
50 for pattern, message in VALIDATION_RULES:
51 if re.search(pattern, command):
52 issues.append(message)
53 return issues
54
55
56def main():
57 try:
58 input_data = json.load(sys.stdin)
59 except json.JSONDecodeError as e:
60 print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
61 sys.exit(1)
62
63 tool_name = input_data.get("tool_name", "")
64 if tool_name != "Bash":
65 sys.exit(0)
66
67 tool_input = input_data.get("tool_input", {})
68 command = tool_input.get("command", "")
69
70 if not command:
71 sys.exit(0)
72
73 # Validate the command
74 issues = validate_command(command)
75
76 if issues:
77 for message in issues:
78 print(f"• {message}", file=sys.stderr)
79 # Exit code 2 blocks tool call and shows stderr to Claude
80 sys.exit(2)
81
82
83if __name__ == "__main__":
84 main()