22
33Currently supports:
44- Cursor
5-
6- To add a new IDE (e.g., Claude Code):
7- 1. Add new value to AIIDEType enum
8- 2. Create _get_<ide>_hooks_dir() function with platform-specific paths
9- 3. Add entry to IDE_CONFIGS dict with IDE-specific hook event names
10- 4. Unhide --ide option in commands (install, uninstall, status)
5+ - Claude Code
116"""
127
138import platform
@@ -20,6 +15,14 @@ class AIIDEType(str, Enum):
2015 """Supported AI IDE types."""
2116
2217 CURSOR = 'cursor'
18+ CLAUDE_CODE = 'claude-code'
19+
20+
21+ class PolicyMode (str , Enum ):
22+ """Policy enforcement mode for global mode and per-feature actions."""
23+
24+ BLOCK = 'block'
25+ WARN = 'warn'
2326
2427
2528class IDEConfig (NamedTuple ):
@@ -42,6 +45,14 @@ def _get_cursor_hooks_dir() -> Path:
4245 return Path .home () / '.config' / 'Cursor'
4346
4447
48+ def _get_claude_code_hooks_dir () -> Path :
49+ """Get Claude Code hooks directory.
50+
51+ Claude Code uses ~/.claude on all platforms.
52+ """
53+ return Path .home () / '.claude'
54+
55+
4556# IDE-specific configurations
4657IDE_CONFIGS : dict [AIIDEType , IDEConfig ] = {
4758 AIIDEType .CURSOR : IDEConfig (
@@ -51,6 +62,13 @@ def _get_cursor_hooks_dir() -> Path:
5162 hooks_file_name = 'hooks.json' ,
5263 hook_events = ['beforeSubmitPrompt' , 'beforeReadFile' , 'beforeMCPExecution' ],
5364 ),
65+ AIIDEType .CLAUDE_CODE : IDEConfig (
66+ name = 'Claude Code' ,
67+ hooks_dir = _get_claude_code_hooks_dir (),
68+ repo_hooks_subdir = '.claude' ,
69+ hooks_file_name = 'settings.json' ,
70+ hook_events = ['UserPromptSubmit' , 'PreToolUse:Read' , 'PreToolUse:mcp' ],
71+ ),
5472}
5573
5674# Default IDE
@@ -60,6 +78,47 @@ def _get_cursor_hooks_dir() -> Path:
6078CYCODE_SCAN_PROMPT_COMMAND = 'cycode ai-guardrails scan'
6179
6280
81+ def _get_cursor_hooks_config () -> dict :
82+ """Get Cursor-specific hooks configuration."""
83+ config = IDE_CONFIGS [AIIDEType .CURSOR ]
84+ hooks = {event : [{'command' : CYCODE_SCAN_PROMPT_COMMAND }] for event in config .hook_events }
85+
86+ return {
87+ 'version' : 1 ,
88+ 'hooks' : hooks ,
89+ }
90+
91+
92+ def _get_claude_code_hooks_config () -> dict :
93+ """Get Claude Code-specific hooks configuration.
94+
95+ Claude Code uses a different hook format with nested structure:
96+ - hooks are arrays of objects with 'hooks' containing command arrays
97+ - PreToolUse uses 'matcher' field to specify which tools to intercept
98+ """
99+ command = f'{ CYCODE_SCAN_PROMPT_COMMAND } --ide claude-code'
100+
101+ return {
102+ 'hooks' : {
103+ 'UserPromptSubmit' : [
104+ {
105+ 'hooks' : [{'type' : 'command' , 'command' : command }],
106+ }
107+ ],
108+ 'PreToolUse' : [
109+ {
110+ 'matcher' : 'Read' ,
111+ 'hooks' : [{'type' : 'command' , 'command' : command }],
112+ },
113+ {
114+ 'matcher' : 'mcp__.*' ,
115+ 'hooks' : [{'type' : 'command' , 'command' : command }],
116+ },
117+ ],
118+ },
119+ }
120+
121+
63122def get_hooks_config (ide : AIIDEType ) -> dict :
64123 """Get the hooks configuration for a specific IDE.
65124
@@ -69,10 +128,6 @@ def get_hooks_config(ide: AIIDEType) -> dict:
69128 Returns:
70129 Dict with hooks configuration for the specified IDE
71130 """
72- config = IDE_CONFIGS [ide ]
73- hooks = {event : [{'command' : CYCODE_SCAN_PROMPT_COMMAND }] for event in config .hook_events }
74-
75- return {
76- 'version' : 1 ,
77- 'hooks' : hooks ,
78- }
131+ if ide == AIIDEType .CLAUDE_CODE :
132+ return _get_claude_code_hooks_config ()
133+ return _get_cursor_hooks_config ()
0 commit comments