| 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 # ALLOWED - refuses if remote has unseen commits
git push --force # BLOCKED - can overwrite others' work
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
# ~/.config/dcg/config.toml
[packs]
enabled = [
"database.postgresql",
"containers.docker",
"kubernetes", # Enables all kubernetes sub-packs
]
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/master/install.sh?$(date +%s)" | bash
# Easy mode: auto-update PATH
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/master/install.sh?$(date +%s)" | bash -s -- --easy-mode
# System-wide (requires sudo)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/master/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
PreToolUseformat - 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:
# Show version with build metadata
dcg --version
# Test a command
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 # Blocked
/usr/local/bin/git checkout -- . # Blocked
/bin/rm -rf /home/user # Blocked
Flag Ordering Variants
rm -rf /path # Combined flags
rm -fr /path # Reversed order
rm -r -f /path # Separate flags
rm --recursive --force /path # Long flags
All variants are handled.
Shell Variable Expansion
rm -rf $TMPDIR/build # Allowed (temp)
rm -rf ${TMPDIR}/build # Allowed
rm -rf "$TMPDIR/build" # Allowed
rm -rf "${TMPDIR:-/tmp}/build" # Allowed
Staged vs Worktree Restore
git restore --staged file.txt # Allowed (unstaging only)
git restore -S file.txt # Allowed (short flag)
git restore file.txt # BLOCKED (discards changes)
git restore --worktree file.txt # BLOCKED (explicit worktree)
git restore -S -W file.txt # BLOCKED (includes worktree)
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 --orgit reset --hard - Remote history destruction from force pushes
- Stash loss from
git stash drop/clear - Filesystem accidents from
rm -rfoutside 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.shcontents 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.jsonhas 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=1or 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 |