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
83 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": "python3 /path/to/claude-code/examples/hooks/bash_command_validator_example.py"
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
36_VALIDATION_RULES = [
37 (
38 r"^grep\b(?!.*\|)",
39 "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
40 ),
41 (
42 r"^find\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 # Exit code 1 shows stderr to the user but not to Claude
62 sys.exit(1)
63
64 tool_name = input_data.get("tool_name", "")
65 if tool_name != "Bash":
66 sys.exit(0)
67
68 tool_input = input_data.get("tool_input", {})
69 command = tool_input.get("command", "")
70
71 if not command:
72 sys.exit(0)
73
74 issues = _validate_command(command)
75 if issues:
76 for message in issues:
77 print(f"• {message}", file=sys.stderr)
78 # Exit code 2 blocks tool call and shows stderr to Claude
79 sys.exit(2)
80
81
82if __name__ == "__main__":
83 main()