From 3d2166eec9b18ff18acccaedf2e04b3a3dfa6f0e Mon Sep 17 00:00:00 2001 From: Thariq Shihipar Date: Wed, 2 Jul 2025 09:32:33 -0700 Subject: [PATCH] proper hook file --- .../hooks/bash_command_validator_example.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/hooks/bash_command_validator_example.py diff --git a/examples/hooks/bash_command_validator_example.py b/examples/hooks/bash_command_validator_example.py new file mode 100644 index 00000000..daed91bf --- /dev/null +++ b/examples/hooks/bash_command_validator_example.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +Claude Code Hook: Bash Command Validator +========================================= +This hook runs as a PreToolUse hook for the Bash tool. +It validates bash commands against a set of rules before execution. +In this case it changes grep calls to using rg. + +Read more about hooks here: https://docs.anthropic.com/en/docs/claude-code/hooks + +Make sure to change your path to your actual script. + +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "python -m ./bash_command_validator_example" + } + ] + } + ] + } +} + +""" + +import json +import re +import sys + +# Define validation rules as a list of (regex pattern, message) tuples +VALIDATION_RULES = [ + ( + r"\bgrep\b(?!.*\|)", + "Use 'rg' (ripgrep) instead of 'grep' for better performance and features", + ), + ( + r"\bfind\s+\S+\s+-name\b", + "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance", + ), +] + + +def validate_command(command: str) -> list[str]: + issues = [] + for pattern, message in VALIDATION_RULES: + if re.search(pattern, command): + issues.append(message) + return issues + + +def main(): + try: + input_data = json.load(sys.stdin) + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON input: {e}", file=sys.stderr) + sys.exit(1) + + tool_name = input_data.get("tool_name", "") + if tool_name != "Bash": + sys.exit(0) + + tool_input = input_data.get("tool_input", {}) + command = tool_input.get("command", "") + + if not command: + sys.exit(0) + + # Validate the command + issues = validate_command(command) + + if issues: + for message in issues: + print(f"• {message}", file=sys.stderr) + # Exit code 2 blocks tool call and shows stderr to Claude + sys.exit(2) + + +if __name__ == "__main__": + main()