| name | dcg |
| description | Destructive Command Guard - High-performance Rust hook for Claude Code that blocks dangerous commands before execution. SIMD-accelerated, modular pack system, whitelist-first architecture. Essential safety layer for agent workflows. |
DCG — Destructive Command Guard
A high-performance Claude Code hook that intercepts and blocks destructive commands before they execute. Written in Rust with SIMD-accelerated filtering for sub-millisecond latency.
Why This Exists
AI coding agents are powerful but fallible. They can accidentally run destructive commands:
- "Let me clean up the build artifacts" →
rm -rf ./src (typo)
- "I'll reset to the last commit" →
git reset --hard (destroys uncommitted changes)
- "Let me fix the merge conflict" →
git checkout -- . (discards all modifications)
- "I'll clean up untracked files" →
git clean -fd (permanently deletes untracked files)
DCG intercepts dangerous commands before execution and blocks them with a clear explanation, giving you a chance to stash your changes first.
Critical Design Principles
1. Whitelist-First Architecture
Safe patterns are checked before destructive patterns. This ensures explicitly safe commands are never accidentally blocked:
git checkout -b feature → Matches SAFE "checkout-new-branch" → ALLOW
git checkout -- file.txt → No safe match, matches DESTRUCTIVE → DENY
2. Fail-Safe Defaults (Default-Allow)
Unrecognized commands are allowed by default. This ensures:
- The hook never breaks legitimate workflows
- Only known dangerous patterns are blocked
- New git commands work until explicitly categorized
3. Zero False Negatives Philosophy
The pattern set prioritizes never allowing dangerous commands over avoiding false positives. A few extra prompts for manual confirmation are acceptable; lost work is not.
What It Blocks
Git Commands That Destroy Uncommitted Work
| Command | Reason |
|---|
git reset --hard | Destroys uncommitted changes |
git reset --merge | Destroys uncommitted changes |
git checkout -- <file> | Discards file modifications |
git restore <file> (without --staged) | Discards uncommitted changes |
git clean -f | Permanently deletes untracked files |
Git Commands That Destroy Remote History
| Command | Reason |
|---|
git push --force / -f | Overwrites remote commits |
git branch -D | Force-deletes without merge check |
Git Commands That Destroy Stashed Work
| Command | Reason |
|---|
git stash drop | Permanently deletes a stash |
git stash clear | Permanently deletes all stashes |
Filesystem Commands
| Command | Reason |
|---|
rm -rf (outside /tmp, /var/tmp, $TMPDIR) | Recursive deletion is dangerous |
What It ALLOWS
Safe operations pass through silently:
Always Safe Git Operations
git status, git log, git diff, git add, git commit, git push, git pull, git fetch, git branch -d (safe delete with merge check), git stash, git stash pop, git stash list
Explicitly Safe Patterns
| Pattern | Why Safe |
|---|
git checkout -b <branch> | Creating new branches |
git checkout --orphan <branch> | Creating orphan branches |
git restore --staged <file> | Unstaging only, doesn't touch working tree |
git restore -S <file> | Short flag for staged |
git clean -n / --dry-run | Preview mode, no actual deletion |
rm -rf /tmp/* | Temp directories are ephemeral |
rm -rf $TMPDIR/* | Shell variable forms |
Safe Alternative: --force-with-lease
git push --force-with-lease
git push --force
Modular Pack System
DCG uses a modular "pack" system to organize patterns by category:
Core Packs (Always Enabled)
| Pack | Description |
|---|
core.git | Destructive git commands |
core.filesystem | Dangerous rm -rf outside temp |
Database Packs
| Pack | Description |
|---|
database.postgresql | DROP/TRUNCATE in PostgreSQL |
database.mysql | DROP/TRUNCATE in MySQL/MariaDB |
database.mongodb | dropDatabase, drop() |
database.redis | FLUSHALL/FLUSHDB |
database.sqlite | DROP in SQLite |
Container Packs
| Pack | Description |
|---|
containers.docker | docker system prune, docker rm -f |
containers.compose | docker-compose down --volumes |
containers.podman | podman system prune |
Kubernetes Packs
| Pack | Description |
|---|
kubernetes.kubectl | kubectl delete namespace |
kubernetes.helm | helm uninstall |
kubernetes.kustomize | kustomize delete patterns |
Cloud Provider Packs
| Pack | Description |
|---|
cloud.aws | Destructive AWS CLI commands |
cloud.gcp | Destructive gcloud commands |
cloud.azure | Destructive az commands |
Infrastructure Packs
| Pack | Description |
|---|
infrastructure.terraform | terraform destroy |
infrastructure.ansible | Dangerous ansible patterns |
infrastructure.pulumi | pulumi destroy |
System Packs
| Pack | Description |
|---|
system.disk | dd, mkfs, fdisk operations |
system.permissions | Dangerous chmod/chown patterns |
system.services | systemctl stop/disable patterns |
Other Packs
| Pack | Description |
|---|
strict_git | Extra paranoid git protections |
package_managers | npm unpublish, cargo yank |
Configuring Packs
[packs]
enabled = [
"database.postgresql",
"containers.docker",
"kubernetes",
]
Environment Variables
| Variable | Description |
|---|
DCG_PACKS="containers.docker,kubernetes" | Enable packs (comma-separated) |
DCG_DISABLE="kubernetes.helm" | Disable packs/sub-packs |
DCG_VERBOSE=1 | Verbose output |
DCG_COLOR=auto|always|never | Color mode |
DCG_BYPASS=1 | Bypass DCG entirely (escape hatch) |
Installation
Quick Install (Recommended)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/main/install.sh?$(date +%s)" | bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/main/install.sh?$(date +%s)" | bash -s -- --easy-mode
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/main/install.sh?$(date +%s)" | sudo bash -s -- --system
From Source (Requires Rust Nightly)
cargo +nightly install --git https://github.com/Dicklesworthstone/destructive_command_guard
Prebuilt Binaries
Available for: Linux x86_64, Linux ARM64, macOS Intel, macOS Apple Silicon, Windows
Claude Code Configuration
Add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "dcg"
}
]
}
]
}
}
Important: Restart Claude Code after adding the hook.
How It Works
Processing Pipeline
┌─────────────────────────────────────────────────────────────────┐
│ Claude Code │
│ Agent executes `rm -rf ./build` │
└─────────────────────┬───────────────────────────────────────────┘
│
▼ PreToolUse hook (stdin: JSON)
┌─────────────────────────────────────────────────────────────────┐
│ dcg │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Parse │───▶│ Normalize │───▶│ Quick Reject │ │
│ │ JSON │ │ Command │ │ Filter │ │
│ └──────────────┘ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ┌───────────────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Pattern Matching │ │
│ │ 1. Check SAFE_PATTERNS (whitelist) ──▶ Allow if match │ │
│ │ 2. Check DESTRUCTIVE_PATTERNS ──────▶ Deny if match │ │
│ │ 3. No match ────────────────────────▶ Allow (default) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────────┘
│
▼ stdout: JSON (deny) or empty (allow)
Stage 1: JSON Parsing
- Reads hook input from stdin
- Validates Claude Code's
PreToolUse format
- Non-Bash tools immediately allowed
Stage 2: Command Normalization
- Strips absolute paths:
/usr/bin/git status → git status
- Preserves argument paths
Stage 3: Quick Rejection Filter
- SIMD-accelerated substring search for "git" or "rm"
- Commands without these bypass regex entirely (99%+ of commands)
Stage 4: Pattern Matching
- Safe patterns checked first (short-circuit on match → allow)
- Destructive patterns checked second (match → deny)
- No match → default allow
Exit Codes
| Code | Meaning |
|---|
0 | Command is safe, proceed |
2 | Command is blocked, do not execute |
CLI Usage
Test commands manually:
dcg --version
echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' | dcg
Example Block Message
════════════════════════════════════════════════════════════════════════
BLOCKED dcg
────────────────────────────────────────────────────────────────────────
Reason: git reset --hard destroys uncommitted changes. Use 'git stash' first.
Command: git reset --hard HEAD~1
Tip: If you need to run this command, execute it manually in a terminal.
Consider using 'git stash' first to save your changes.
════════════════════════════════════════════════════════════════════════
Contextual Suggestions
| Command Type | Suggestion |
|---|
git reset, git checkout -- | "Consider using 'git stash' first" |
git clean | "Use 'git clean -n' first to preview" |
git push --force | "Consider using '--force-with-lease'" |
rm -rf | "Verify the path carefully before running manually" |
Edge Cases Handled
Path Normalization
/usr/bin/git reset --hard
/usr/local/bin/git checkout -- .
/bin/rm -rf /home/user
Flag Ordering Variants
rm -rf /path
rm -fr /path
rm -r -f /path
rm --recursive --force /path
All variants are handled.
Shell Variable Expansion
rm -rf $TMPDIR/build
rm -rf ${TMPDIR}/build
rm -rf "$TMPDIR/build"
rm -rf "${TMPDIR:-/tmp}/build"
Staged vs Worktree Restore
git restore --staged file.txt
git restore -S file.txt
git restore file.txt
git restore --worktree file.txt
git restore -S -W file.txt
Performance Optimizations
DCG is designed for zero perceived latency:
| Optimization | Technique |
|---|
| Lazy Static | Regex patterns compiled once via LazyLock |
| SIMD Quick Reject | memchr crate for CPU vector instructions |
| Early Exit | Safe match returns immediately |
| Zero-Copy JSON | serde_json operates on input buffer |
| Zero-Allocation | Cow<str> for path normalization |
| Release Profile | opt-level="z", LTO, single codegen unit |
Result: Sub-millisecond execution for typical commands.
Pattern Counts
| Type | Count |
|---|
| Safe patterns (whitelist) | 34 |
| Destructive patterns (blacklist) | 16 |
Security Considerations
What DCG Protects Against
- Accidental data loss from
git checkout -- or git reset --hard
- Remote history destruction from force pushes
- Stash loss from
git stash drop/clear
- Filesystem accidents from
rm -rf outside temp directories
What DCG Does NOT Protect Against
- Malicious actors (can bypass the hook)
- Non-Bash commands (Python/JavaScript file writes, API calls)
- Committed but unpushed work
- Commands inside scripts (
./deploy.sh contents not inspected)
Threat Model
DCG assumes the AI agent is well-intentioned but fallible. It catches honest mistakes, not adversarial attacks.
Troubleshooting
Hook not blocking commands
- Verify
~/.claude/settings.json has hook configuration
- Restart Claude Code
- Test manually:
echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' | dcg
Hook blocking safe commands
- Check if there's an edge case not covered
- File a GitHub issue
- Temporary bypass:
DCG_BYPASS=1 or run command manually
FAQ
Q: Why block git branch -D but allow git branch -d?
Lowercase -d only deletes branches fully merged. Uppercase -D force-deletes regardless of merge status, potentially losing commits.
Q: Why is git push --force-with-lease allowed?
Force-with-lease refuses to push if the remote has commits you haven't seen, preventing accidental overwrites.
Q: Why block all rm -rf outside temp directories?
Recursive forced deletion is extremely dangerous. A typo or wrong variable can delete critical files. Temp directories are designed to be ephemeral.
Q: What if I really need to run a blocked command?
DCG instructs the agent to ask for permission. Run the command manually in a separate terminal after making a conscious decision.
Integration with Flywheel
| Tool | Integration |
|---|
| Claude Code | Native PreToolUse hook |
| Agent Mail | Agents can report blocked commands to coordinator |
| BV | Flag tasks that repeatedly trigger DCG |
| CASS | Search DCG block patterns across sessions |
| RU | DCG protects agent-sweep from destructive commits |