How to Debug Why a Command Is Being Blocked or Allowed by dcg
Use dcg explain "<command>" to see the exact rule ID and denial reason, or trace the evaluation pipeline through eight specific stages from hook input to final JSON response.
The dcg (destructive_command_guard) crate from Dicklesworthstone/destructive_command_guard intercepts shell commands from AI coding agents and evaluates them against a curated set of safety patterns. When a command behaves unexpectedly—either blocking legitimate operations or allowing risky ones—you can debug the decision by stepping through the evaluation pipeline defined in the Rust source.
Understanding the dcg Evaluation Pipeline
Every command passes through a deterministic eight-stage pipeline implemented across src/hook.rs and supporting modules:
- Hook Input Parsing –
hook::read_hook_inputdeserializes stdin JSON intoHookInputwith a configurable byte limit. - Protocol Detection –
hook::detect_protocolinspects fields likeevent,tool_name,hookEventName, andturn_idto identify the client (Claude, Copilot, Gemini, etc.). - Command Extraction –
hook::extract_command_with_protocolpulls the shell string fromtoolCall.args.CommandLine,tool_input.command, ortool_args.commanddepending on the protocol. - Fast-Reject Filter –
quick_reject::is_quick_rejectperforms substring checks to allow > 99 % of benign commands without regex overhead. - Normalization –
normalize::normalize_commandstrips absolute paths (e.g.,/usr/bin/git→git) and expands aliases for stable matching. - Pattern Evaluation –
evaluator::evaluatechecks againstSAFE_PATTERNS(whitelist) andDESTRUCTIVE_PATTERNS(blacklist), returning aMatchResultwithpack_id,pattern_name, andMatchSpan. - Denial Formatting –
hook::format_denial_messagebuilds the human-readable explanation and rule ID (e.g.,core.git:reset-hard). - Response Serialization –
hook::write_denial_toemits the JSON payload (permissionDecision: "deny") to stdout and a colorized warning to stderr viaprint_colorful_warning_to.
When a command is allowed, the process exits silently with code 0 and no output. When blocked, stage 8 produces a structured JSON response containing ruleId, severity, confidence, and remediation fields.
Debugging a Blocked Command Step by Step
If dcg blocks a command you expect to be safe, trace through these verification points in the source code.
Inspect the Raw Hook Input
Start by verifying what dcg actually received. The hook::read_hook_input function in src/hook.rs reads up to a configurable byte limit from stdin and parses the JSON.
# Simulate hook input for debugging
echo '{"toolName":"Bash","toolInput":{"command":"git reset --hard HEAD"}}' | dcg explain "git reset --hard HEAD"
If the JSON structure differs from what the extractor expects, extract_command_with_protocol will fail to locate the command string.
Verify Protocol Detection
Check which HookProtocol variant hook::detect_protocol selected. The function examines field presence to distinguish between Claude, Gemini, Copilot, Codex, Hermes, Grok, and Antigravity protocols. An incorrect detection causes extract_command_with_protocol to look in the wrong JSON path (e.g., toolCall vs. tool_input).
Check Command Extraction
Confirm which extractor succeeded. The hook::extract_command_with_protocol function delegates to:
extract_command_from_tool_call(Copilot/Codex)extract_command_from_tool_input(Claude)extract_command_from_tool_args(Legacy formats)
If the command appears in the JSON but extraction returns None, the protocol detection likely selected the wrong variant.
Review the Quick-Reject Filter
Before regex evaluation, src/quick_reject.rs runs is_quick_reject to filter obviously safe commands. If your command contains suspicious substrings but should be allowed, it may be passing this filter when it should not, or vice versa. This stage has no logging by default, so you must test manually:
use dcg::quick_reject::is_quick_reject;
let cmd = "git status";
if is_quick_reject(cmd) {
println!("Command bypassed regex evaluation");
}
Examine Normalization
The normalize::normalize_command function in src/normalize.rs strips binary paths and canonicalizes aliases. If your pattern expects git checkout but the input is /usr/local/bin/git checkout, normalization ensures matching still succeeds. Debug the normalized form to verify pattern alignment.
Analyze Pattern Matching Results
The core logic resides in src/evaluator.rs. The evaluate function returns a HookResult::Deny containing:
pack: The pattern pack ID (e.g.,core.git)pattern_name: The specific rule (e.g.,reset-hard)severity:Critical,High,Medium, orLowconfidence: Float between 0.0 and 1.0
If evaluate returns HookResult::Allow, the command matched a SAFE_PATTERNS entry or triggered no DESTRUCTIVE_PATTERNS hits.
Inspect the Denial Output
Finally, hook::write_denial_to constructs the final payload. For blocked commands, it prints a colorized warning to stderr showing the rule ID and a "Tip: dcg explain ..." hint, while stdout receives the JSON for the AI client. Check src/output/denial.rs for the DenialBox rendering logic if the terminal output appears malformed.
Using the dcg explain CLI Tool
The fastest way to debug is the built-in explain command, which runs stages 1‑7 and prints the denial message without requiring a full IDE hook setup:
$ dcg explain "rm -rf /"
BLOCKED by dcg
Tip: dcg explain "rm -rf /"
Reason: rm -rf / destroys system directories
Explanation: Recursive deletion of root filesystem.
Rule: core.filesystem:rm-rf-root
Command: rm -rf /
If this operation is truly needed, ask the user for explicit permission and have them run the command manually.
The Rule field (e.g., core.git:reset-hard) corresponds to the pack_id and pattern_name returned by the evaluator.
Resolving False Positives with Allowlists
If a block is incorrect, add the rule ID to your project allowlist using the CLI:
$ dcg allowlist add core.git:reset-hard --project
Allowlist entry added for rule core.git:reset-hard (project scope)
This modifies the allowlist storage managed by src/allowlist.rs. You can also report false positives via the link printed in the warning footer, which points to the GitHub issue template.
Summary
- Trace the pipeline: Input → Protocol → Extraction → Quick-Reject → Normalization → Evaluation → Output.
- Use
dcg explain: Instantly reveals whichDESTRUCTIVE_PATTERNSentry triggered the block and displays theruleId. - Check
src/evaluator.rs: Theevaluatefunction returnsMatchResultwithpack_idandpattern_namefor precise identification. - Normalize first: Commands are stripped of paths in
src/normalize.rsbefore pattern matching. - Allowlist locally: Use
dcg allowlist add <ruleId> --projectto override specific rules without modifying source packs.
Frequently Asked Questions
How do I see exactly which rule blocked my command?
Run dcg explain "<command>" in your terminal. The output includes a Rule field formatted as pack_id:pattern_name (e.g., core.git:reset-hard), which corresponds to the DESTRUCTIVE_PATTERNS definition in src/packs/mod.rs.
Why was my safe command allowed immediately without evaluation?
The command likely triggered the fast-reject filter in src/quick_reject.rs. The is_quick_reject function uses simple substring checks to allow benign commands (like git status) without invoking the regex engine, improving performance for the common case.
Can I debug the hook pipeline without installing the IDE extension?
Yes. Pipe JSON directly into dcg via stdin to simulate the hook environment. The hook::read_hook_input function reads from stdin, so you can test protocol detection and extraction locally without triggering the actual IDE integration.
Where are the destructive patterns defined?
Destructive patterns are defined in src/packs/mod.rs using the destructive! macro, while safe patterns use the SAFE_PATTERNS macro. Each pattern belongs to a pack (e.g., core.git, core.filesystem) and includes metadata for severity, confidence, and remediation suggestions.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →