Skip to main content

Module dialect

Module dialect 

Source
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§

HookOutcome
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.