-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy pathconsts.py
More file actions
156 lines (121 loc) · 4.39 KB
/
consts.py
File metadata and controls
156 lines (121 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""Constants for AI guardrails hooks management.
Currently supports:
- Cursor
- Claude Code
"""
import platform
from copy import deepcopy
from enum import Enum
from pathlib import Path
from typing import NamedTuple
class AIIDEType(str, Enum):
"""Supported AI IDE types."""
CURSOR = 'cursor'
CLAUDE_CODE = 'claude-code'
class PolicyMode(str, Enum):
"""Policy enforcement mode for global mode and per-feature actions."""
BLOCK = 'block'
WARN = 'warn'
class InstallMode(str, Enum):
"""Installation mode for ai-guardrails install command."""
REPORT = 'report'
BLOCK = 'block'
class IDEConfig(NamedTuple):
"""Configuration for an AI IDE."""
name: str
hooks_dir: Path
repo_hooks_subdir: str # Subdirectory in repo for hooks (e.g., '.cursor')
hooks_file_name: str
hook_events: list[str] # List of supported hook event names for this IDE
def _get_cursor_hooks_dir() -> Path:
"""Get Cursor hooks directory based on platform."""
if platform.system() == 'Darwin':
return Path.home() / '.cursor'
if platform.system() == 'Windows':
return Path.home() / 'AppData' / 'Roaming' / 'Cursor'
# Linux
return Path.home() / '.config' / 'Cursor'
def _get_claude_code_hooks_dir() -> Path:
"""Get Claude Code hooks directory.
Claude Code uses ~/.claude on all platforms.
"""
return Path.home() / '.claude'
# IDE-specific configurations
IDE_CONFIGS: dict[AIIDEType, IDEConfig] = {
AIIDEType.CURSOR: IDEConfig(
name='Cursor',
hooks_dir=_get_cursor_hooks_dir(),
repo_hooks_subdir='.cursor',
hooks_file_name='hooks.json',
hook_events=['beforeSubmitPrompt', 'beforeReadFile', 'beforeMCPExecution'],
),
AIIDEType.CLAUDE_CODE: IDEConfig(
name='Claude Code',
hooks_dir=_get_claude_code_hooks_dir(),
repo_hooks_subdir='.claude',
hooks_file_name='settings.json',
hook_events=['UserPromptSubmit', 'PreToolUse:Read', 'PreToolUse:mcp'],
),
}
# Default IDE
DEFAULT_IDE = AIIDEType.CURSOR
# Command used in hooks
CYCODE_SCAN_PROMPT_COMMAND = 'cycode ai-guardrails scan'
CYCODE_SESSION_START_COMMAND = 'cycode ai-guardrails session-start'
def _get_cursor_hooks_config(async_mode: bool = False) -> dict:
"""Get Cursor-specific hooks configuration."""
config = IDE_CONFIGS[AIIDEType.CURSOR]
command = f'{CYCODE_SCAN_PROMPT_COMMAND} &' if async_mode else CYCODE_SCAN_PROMPT_COMMAND
hooks = {event: [{'command': command}] for event in config.hook_events}
hooks['sessionStart'] = [{'command': f'{CYCODE_SESSION_START_COMMAND} --ide cursor'}]
return {
'version': 1,
'hooks': hooks,
}
def _get_claude_code_hooks_config(async_mode: bool = False) -> dict:
"""Get Claude Code-specific hooks configuration.
Claude Code uses a different hook format with nested structure:
- hooks are arrays of objects with 'hooks' containing command arrays
- PreToolUse uses 'matcher' field to specify which tools to intercept
"""
command = f'{CYCODE_SCAN_PROMPT_COMMAND} --ide claude-code'
hook_entry = {'type': 'command', 'command': command}
if async_mode:
hook_entry['async'] = True
hook_entry['timeout'] = 20
return {
'hooks': {
'SessionStart': [
{
'matcher': 'startup',
'hooks': [{'type': 'command', 'command': f'{CYCODE_SESSION_START_COMMAND} --ide claude-code'}],
}
],
'UserPromptSubmit': [
{
'hooks': [deepcopy(hook_entry)],
}
],
'PreToolUse': [
{
'matcher': 'Read',
'hooks': [deepcopy(hook_entry)],
},
{
'matcher': 'mcp__.*',
'hooks': [deepcopy(hook_entry)],
},
],
},
}
def get_hooks_config(ide: AIIDEType, async_mode: bool = False) -> dict:
"""Get the hooks configuration for a specific IDE.
Args:
ide: The AI IDE type
async_mode: If True, hooks run asynchronously (non-blocking)
Returns:
Dict with hooks configuration for the specified IDE
"""
if ide == AIIDEType.CLAUDE_CODE:
return _get_claude_code_hooks_config(async_mode=async_mode)
return _get_cursor_hooks_config(async_mode=async_mode)