|
14 | 14 | JSON response shape that the IDE expects on stdout. |
15 | 15 | """ |
16 | 16 |
|
| 17 | +import platform |
17 | 18 | from abc import ABC, abstractmethod |
18 | 19 | from dataclasses import dataclass |
19 | 20 | from enum import Enum |
|
24 | 25 | from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType |
25 | 26 |
|
26 | 27 |
|
| 28 | +def shell_background_suffix(async_mode: bool) -> str: |
| 29 | + """`' &'` when backgrounding is requested and the platform's shell supports it. |
| 30 | +
|
| 31 | + Only valid for hooks whose runner is stdin-safe under backgrounding (zsh keeps |
| 32 | + a backgrounded command's stdin; verified for Cursor/Codex). bash/sh reattach it |
| 33 | + to /dev/null, silently emptying the payload — hooks that run under bash (e.g. |
| 34 | + Copilot's `bash` field) must add an explicit `<&0` redirect instead. |
| 35 | +
|
| 36 | + Windows gets no suffix: depending on the IDE, hooks may run under cmd (where a |
| 37 | + trailing `&` is a no-op separator) or Windows PowerShell (where it's a parse |
| 38 | + error that would fail the hook). Until the CLI can self-detach in report mode, |
| 39 | + Windows hooks run synchronously. |
| 40 | + """ |
| 41 | + if not async_mode or platform.system() == 'Windows': |
| 42 | + return '' |
| 43 | + return ' &' |
| 44 | + |
| 45 | + |
27 | 46 | class DecisionAction(str, Enum): |
28 | 47 | """Canonical decision action returned by event handlers.""" |
29 | 48 |
|
@@ -138,6 +157,15 @@ def matches_payload(self, raw_payload: dict) -> bool: |
138 | 157 | event (e.g. Cursor reading Claude Code hooks from ~/.claude/settings.json). |
139 | 158 | """ |
140 | 159 |
|
| 160 | + def is_synthetic_prompt(self, raw_payload: dict) -> bool: |
| 161 | + """Return True when a prompt event carries IDE/harness-generated content |
| 162 | + rather than text the user typed. |
| 163 | +
|
| 164 | + Synthetic prompts are skipped without scanning or telemetry. |
| 165 | + Default: False. Override for IDEs that inject synthetic user turns. |
| 166 | + """ |
| 167 | + return False |
| 168 | + |
141 | 169 | @abstractmethod |
142 | 170 | def parse_hook_payload(self, raw_payload: dict) -> AIHookPayload: |
143 | 171 | """Normalize a raw stdin payload into the canonical ``AIHookPayload``.""" |
|
0 commit comments