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 / plugins / hookify / hooks / pretooluse.py
76 lines · 2.3 KBRaw
1#!/usr/bin/env python3
2"""PreToolUse hook executor for hookify plugin.
3
4This script is called by Claude Code before any tool executes.
5It reads .claude/hookify.*.local.md files and evaluates rules.
6"""
7
8import os
9import sys
10import json
11
12# CRITICAL: Add plugin root to Python path for imports
13# We need to add the parent of the plugin directory so Python can find "hookify" package
14PLUGIN_ROOT = os.environ.get('CLAUDE_PLUGIN_ROOT')
15if PLUGIN_ROOT:
16 # Add the parent directory of the plugin
17 parent_dir = os.path.dirname(PLUGIN_ROOT)
18 if parent_dir not in sys.path:
19 sys.path.insert(0, parent_dir)
20
21 # Also add PLUGIN_ROOT itself in case we have other scripts
22 if PLUGIN_ROOT not in sys.path:
23 sys.path.insert(0, PLUGIN_ROOT)
24
25try:
26 from hookify.core.config_loader import load_rules
27 from hookify.core.rule_engine import RuleEngine
28except ImportError as e:
29 # If imports fail, allow operation and log error
30 error_msg = {"systemMessage": f"Hookify import error: {e}"}
31 print(json.dumps(error_msg), file=sys.stdout)
32 sys.exit(0)
33
34
35def main():
36 """Main entry point for PreToolUse hook."""
37 try:
38 # Read input from stdin
39 input_data = json.load(sys.stdin)
40
41 # Determine event type for filtering
42 # For PreToolUse, we use tool_name to determine "bash" vs "file" event
43 tool_name = input_data.get('tool_name', '')
44
45 event = None
46 if tool_name == 'Bash':
47 event = 'bash'
48 elif tool_name in ['Edit', 'Write', 'MultiEdit', 'Read']:
49 # Include Read tool in file events to check symlink bypass
50 # Security fix for CVE-2025-59829
51 event = 'file'
52
53 # Load rules
54 rules = load_rules(event=event)
55
56 # Evaluate rules
57 engine = RuleEngine()
58 result = engine.evaluate_rules(rules, input_data)
59
60 # Always output JSON (even if empty)
61 print(json.dumps(result), file=sys.stdout)
62
63 except Exception as e:
64 # On any error, allow the operation and log
65 error_output = {
66 "systemMessage": f"Hookify error: {str(e)}"
67 }
68 print(json.dumps(error_output), file=sys.stdout)
69
70 finally:
71 # ALWAYS exit 0 - never block operations due to hook errors
72 sys.exit(0)
73
74
75if __name__ == '__main__':
76 main()