How dcg Handles Different AI Agent Protocols: Claude, Codex, and Gemini

dcg inspects a JSON envelope from STDIN, detects the specific AI agent through field heuristics, and returns a protocol-shaped denial so Claude Code, Codex, Gemini, and other supported agents can each interpret the guard decision correctly.

Destructive Command Guard (dcg) from the Dicklesworthstone/destructive_command_guard repository acts as a PreToolUse hook that intercepts tool calls before they reach the shell. Because each AI back-end sends a slightly different JSON payload over standard input, dcg must identify the originating protocol before evaluating or blocking a command. This protocol-aware mediation is implemented almost entirely in src/hook.rs.

Parsing the Inbound JSON Envelope

All supported agents stream a JSON payload to dcg over STDIN. The HookInput struct defined at lines 19-74 of src/hook.rs models every field used by the known back-ends, including optional identifiers like toolCall, event, tool_name, hook_event_name, turn_id, and environment variables. By deserializing into this unified struct, dcg avoids maintaining separate parsers for each agent.

Protocol Detection Heuristics in detect_protocol

The function detect_protocol (lines 85-167) disambiguates the sender by checking fields in a specific order. It returns a variant of the HookProtocol enum (lines 24-41) that downstream code uses to decide how to extract the command and format the denial.

  • Antigravity — The presence of a toolCall object containing nested name/args immediately flags the Antigravity protocol.

  • Hermes — If hook_event_name equals "pre_tool_call" or tool_name equals "terminal", the payload is treated as Hermes.

  • Grok — The payload matches Grok when hookEventName equals "pre_tool_use" or toolName equals "run_terminal_cmd".

  • Copilot — A distinct event field with value "pre-tool-use" or the presence of tool_args triggers Copilot detection.

  • Codex — Codex is identified by shell-tool names such as bash, powershell, pwsh, or launch-process combined with a non-empty turn_id; a PowerShell tool name alone also forces Codex detection.

  • Claude-compatible — When none of the prior checks match, or when the CLAUDE_CODE environment variable is set, dcg falls back to the Claude-compatible protocol.

  • Gemini — The Gemini protocol is recognized when tool_name equals "run_shell_command" and hook_event_name equals "BeforeTool", or when any combination of Gemini-specific envelope fields appears.

Extracting the Shell Command by Protocol

Once the protocol is known, extract_command_with_protocol (lines 73-104) pulls the actual command string out of the JSON.

  • Antigravity → toolCall.args.CommandLine
  • Standard payloads → tool_input.command or tool_args.command
  • Both JSON-object and JSON-string forms are accepted.

Emitting Protocol-Specific Denial Responses

Denials are authored by write_denial_to (lines 135-190). Depending on the HookProtocol variant, dcg serializes a different output struct to STDOUT so the originating agent can parse the result.

  • ClaudeCompatible — Emits HookOutput wrapped in hookSpecificOutput with full ergonomic metadata: ruleId, packId, severity, remediation, and allowOnce* fields.

  • Codex — Emits a minimal HookOutput containing only hook_event_name, permission_decision, and permission_decision_reason. Extra ergonomic fields are stripped because Codex’s strict parser discards unknown keys.

  • Copilot — Returns CopilotHookOutput with top-level permissionDecision and permissionDecisionReason.

  • Gemini — Returns GeminiHookOutput containing decision, reason, optional systemMessage, plus ergonomics.

  • Hermes — Returns HermesHookOutput supplying both decision/reason and action/message, since Hermes accepts either format, along with ergonomic extensions.

For every protocol except Codex, dcg injects consistent metadata fields—ruleId, packId, severity, confidence, and remediation—so downstream tooling receives uniform telemetry without breaking any agent parser.

Human-visible messages are built by format_denial_message (lines 80-112) and printed via print_colorful_warning (lines 64-78). For agents that consume JSON payloads, the structured response goes to stdout while the colorful warning goes to stderr.

Fail-Open Safety Behavior

If the input fails to parse or does not represent a supported shell tool, dcg returns an Allow decision. This fail-open design prevents non-zero exits from breaking agent workflows such as Grok, which require hooks to exit cleanly even on unrecognized input.

Runnable Examples for Claude, Codex, Gemini, and More

Claude Code Default Invocation

echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' \
 | dcg

This produces the full HookOutput JSON on stdout—including ruleId "core.git:reset-hard"—and prints a colored warning to stderr.

Gemini BeforeTool Hook

echo '{
  "toolName":"run_shell_command",
  "hookEventName":"BeforeTool",
  "sessionId":"abc",
  "toolInput":{"command":"rm -rf /"}
}' | dcg

dcg emits a GeminiHookOutput JSON object with decision and reason fields, plus ergonomic metadata.

Codex Minimal Response

echo '{"tool_name":"Bash","tool_input":{"command":"git push -f"},"turnId":"12345"}' \
 | dcg

Because Codex cannot tolerate unknown fields, the output is a stripped-down HookOutput containing only hook_event_name, permission_decision, and permission_decision_reason.

Copilot Top-Level Denial

echo '{"event":"pre-tool-use","tool_args":{"command":"kubectl delete namespace prod"}}' \
 | dcg

This returns CopilotHookOutput with permissionDecision and permissionDecisionReason.

Antigravity CLI via Hermes Mapping

echo '{
  "toolCall":{"name":"run_command","args":{"CommandLine":"docker system prune"}}
}' | dcg

Antigravity is detected through its toolCall shape, and dcg emits Hermes-compatible JSON because Antigravity maps to the Hermes protocol internally.

Summary

  • src/hook.rs houses the unified HookInput struct and detect_protocol logic that identifies Claude, Codex, Gemini, Hermes, Grok, Copilot, and Antigravity envelopes.
  • Protocol detection relies on ordered checks of fields like toolCall, event, tool_name, turn_id, and environment variables.
  • extract_command_with_protocol and write_denial_to ensure the correct command is pulled and the correct JSON shape is returned.
  • Codex receives a minimal payload, while Claude-compatible, Gemini, Hermes, and others receive ergonomic metadata fields for richer telemetry.
  • A fail-open policy guarantees that parsing errors never block legitimate agent operations.

Frequently Asked Questions

Which file in dcg handles protocol detection?

The core logic resides in src/hook.rs, specifically the detect_protocol function at lines 85-167. It inspects incoming JSON fields and environment variables to return a HookProtocol enum variant that drives the rest of the pipeline.

Why does Codex receive a different denial format than Claude or Gemini?

Codex uses a strict JSON parser that rejects unknown fields. To avoid breaking the agent, dcg strips ergonomic metadata—such as ruleId and severity—and returns a minimal HookOutput containing only hook_event_name, permission_decision, and permission_decision_reason.

How does dcg avoid breaking an agent when the input is not a shell command?

If the payload fails to parse or is not a recognized shell-tool candidate, dcg applies a fail-open strategy and returns an Allow result. This ensures that unsupported or malformed hooks never cause a non-zero exit that would disrupt the agent's flow.

Can dcg distinguish between Gemini and Hermes when fields look similar?

Yes. detect_protocol checks for Gemini-specific combinations such as tool_name == "run_shell_command" paired with hook_event_name == "BeforeTool", while Hermes is identified by hook_event_name == "pre_tool_call" or tool_name == "terminal". The ordered inspection guarantees that the first definitive match wins.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →