2222
2323_CYCODE_COMMAND_MARKERS = ('cycode ai-guardrails' ,)
2424
25+ # Command-carrying fields of a flat hook entry. Copilot entries use per-OS
26+ # `bash`/`powershell` fields instead of `command`.
27+ _COMMAND_FIELDS = ('command' , 'bash' , 'powershell' )
28+
2529
2630def _is_cycode_command (command : str ) -> bool :
2731 return any (marker in command for marker in _CYCODE_COMMAND_MARKERS )
2832
2933
34+ def _has_cycode_command_field (entry : dict ) -> bool :
35+ return any (_is_cycode_command (entry .get (field , '' )) for field in _COMMAND_FIELDS )
36+
37+
3038def is_cycode_hook_entry (entry : dict ) -> bool :
3139 """True if any hook inside ``entry`` is owned by Cycode."""
32- command = entry .get ('command' , '' )
33- if _is_cycode_command (command ):
40+ if _has_cycode_command_field (entry ):
3441 return True
3542
3643 for hook in entry .get ('hooks' , []):
@@ -47,9 +54,9 @@ def _strip_cycode_from_entry(entry: dict) -> Optional[dict]:
4754 every nested hook was Cycode). Non-Cycode hooks co-located in the same
4855 entry are preserved.
4956 """
50- # Cursor format: the entry itself IS a single hook command.
51- if 'command' in entry and 'hooks' not in entry :
52- return None if _is_cycode_command (entry . get ( 'command' , '' ) ) else entry
57+ # Cursor/Copilot format: the entry itself IS a single hook command.
58+ if 'hooks' not in entry and any ( field in entry for field in _COMMAND_FIELDS ) :
59+ return None if _has_cycode_command_field (entry ) else entry
5360
5461 # Claude Code / Codex format: nested `hooks` list inside the entry.
5562 nested = entry .get ('hooks' )
0 commit comments