Expand description
Per-CLI hook dialects.
Every supported agent CLI exposes a “run a command before the tool executes,
read a decision back” hook — but each speaks a slightly different protocol on
stdin and stdout. A Dialect knows how to (a) parse one CLI’s PreTool
payload into a normalized Shell command and (b) serialize the daemon’s
Verdict back into that CLI’s decision protocol.
The decision policy (what Allow/Deny/Hold means, and that a catastrophic
hold becomes a deny) lives in crate::hook and is identical for every
dialect — only the wire format differs here. This keeps the security spine in
one place and the per-CLI glue mechanical.
Protocols, as researched against each CLI’s docs:
- Claude Code / Qwen Code / Codex CLI:
{tool_name, tool_input.command}in;{hookSpecificOutput:{permissionDecision: allow|deny|ask, …}}out. - Gemini CLI:
{tool_name, tool_input.command}in;{decision: allow|deny}out (no native “ask” — an ambiguous hold is mapped to deny). - GitHub Copilot CLI:
{toolName, toolArgs.command}in (camelCase);{permissionDecision: allow|deny|ask, permissionDecisionReason}out. - Cursor CLI:
{command, cwd}in (beforeShellExecution);{permission: allow|deny|ask, userMessage, agentMessage}out. - OpenCode: no external-command hook — a bundled JS plugin bridges to us with
a simple
{command, cwd}in /{decision: allow|deny|ask, reason}out.
Structs§
- Hook
Outcome - What the adapter prints on stdout and exits with for one payload.
- Shell
- A normalized shell command extracted from a hook payload.
Enums§
- Dialect
- Which agent CLI’s hook protocol to speak.
- Parsed
- Result of parsing a hook payload.
- Resolved
- The resolved, dialect-independent decision the daemon’s verdict maps to.
Functions§
- resolve
- Map a daemon verdict to the dialect-independent decision.