pretooluse.py 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. #!/usr/bin/env python3
  2. """PreToolUse hook executor for hookify plugin.
  3. This script is called by Claude Code before any tool executes.
  4. It reads .claude/hookify.*.local.md files and evaluates rules.
  5. """
  6. import os
  7. import sys
  8. import json
  9. # Add plugin root to Python path for imports
  10. PLUGIN_ROOT = os.environ.get('CLAUDE_PLUGIN_ROOT')
  11. if PLUGIN_ROOT and PLUGIN_ROOT not in sys.path:
  12. sys.path.insert(0, PLUGIN_ROOT)
  13. try:
  14. from core.config_loader import load_rules
  15. from core.rule_engine import RuleEngine
  16. except ImportError as e:
  17. # If imports fail, allow operation and log error
  18. error_msg = {"systemMessage": f"Hookify import error: {e}"}
  19. print(json.dumps(error_msg), file=sys.stdout)
  20. sys.exit(0)
  21. def main():
  22. """Main entry point for PreToolUse hook."""
  23. try:
  24. # Read input from stdin
  25. input_data = json.load(sys.stdin)
  26. # Determine event type for filtering
  27. # For PreToolUse, we use tool_name to determine "bash" vs "file" event
  28. tool_name = input_data.get('tool_name', '')
  29. event = None
  30. if tool_name == 'Bash':
  31. event = 'bash'
  32. elif tool_name in ['Edit', 'Write', 'MultiEdit']:
  33. event = 'file'
  34. # Load rules
  35. rules = load_rules(event=event)
  36. # Evaluate rules
  37. engine = RuleEngine()
  38. result = engine.evaluate_rules(rules, input_data)
  39. # Always output JSON (even if empty)
  40. print(json.dumps(result), file=sys.stdout)
  41. except Exception as e:
  42. # On any error, allow the operation and log
  43. error_output = {
  44. "systemMessage": f"Hookify error: {str(e)}"
  45. }
  46. print(json.dumps(error_output), file=sys.stdout)
  47. finally:
  48. # ALWAYS exit 0 - never block operations due to hook errors
  49. sys.exit(0)
  50. if __name__ == '__main__':
  51. main()