Configurable guardrails for Claude Code and Codex CLI - block commands, protect files, enforce workflows.
command-guard is a plugin for both Claude Code and Codex CLI. It provides configurable guardrails for blocking or warning about tool usage and ships with no default rules — all configuration comes from your project's .claude/command-guard.json (the same file is read under both runtimes). The override mechanism, safe patterns, and warning throttle all work identically.
claude plugin marketplace add golergka/command-guard
claude plugin install command-guard@golergka-command-guardFor local development/testing:
claude --plugin-dir /path/to/command-guardcodex plugin marketplace add golergka/command-guard
codex plugin add command-guard@golergka-command-guardCodex auto-discovers hooks.json at the plugin root, so the same guard fires for *exec_command tool calls. The plugin reads either CLAUDE_PROJECT_DIR or CODEX_PROJECT_DIR to find .claude/command-guard.json, so config lives in the same place either way.
This repository contains multiple independent plugins. Install only what you need:
| Plugin | Description | Install command |
|---|---|---|
command-guard |
Configurable rules to block or warn about tool usage | claude plugin install command-guard@golergka-command-guard |
commit-guard |
Prevents Claude from finishing with uncommitted changes | claude plugin install commit-guard@golergka-command-guard |
Create .claude/command-guard.json in your project root:
{
"rules": [
{
"match": "command",
"pattern": "git\\s+reset\\s+--hard",
"message": "git reset --hard destroys uncommitted changes. Use git stash first.",
"severity": "error"
},
{
"match": "file_path",
"pattern": "patches/.*\\.patch$",
"message": "Patch files should not be edited directly. Use pnpm patch workflow.",
"severity": "error"
},
{
"match": "tool_name",
"pattern": "mcp__betterstack__",
"message": "Consider using better-stack-logs agent for context isolation.",
"severity": "warning"
}
],
"safePatterns": ["git\\s+checkout\\s+-b\\s"]
}If no config file exists, all tool uses are allowed (empty rules = no blocking).
| Field | Required | Type | Description |
|---|---|---|---|
match |
Yes | "command" | "file_path" | "tool_name" |
What to match against |
pattern |
Yes | string (regex) | Pattern to match |
message |
Yes | string | Message shown when triggered |
severity |
Yes | "error" | "warning" |
error=block, warning=reminder |
case_sensitive |
No | boolean | Default: false |
Matches against Bash command content. Commands are:
- Split on chain operators (
&&,||,;,|) - Normalized (absolute paths like
/usr/bin/gitbecomegit) - Stripped of quoted strings to avoid false positives
Matches against file paths in Edit and Write tool calls.
Matches against the tool name. Useful for MCP tools like mcp__betterstack__telemetry_query.
Blocks the tool use entirely. The user sees a block message and the tool doesn't execute.
BLOCKED: git reset --hard destroys all uncommitted changes permanently. Use 'git stash' first.
Context: git reset --hard HEAD~1
To override, add a comment: # OVERRIDE: <reason>
Shows a reminder after the tool completes (PostToolUse). Doesn't block execution.
Warnings are throttled per (working directory, Claude session, rule) — by default, each rule surfaces on hit #1, #11, #21, … for a given session and folder. Intermediate hits are silently suppressed so a noisy rule doesn't flood the conversation. Errors are never throttled.
Override the interval with the top-level warningThrottle field in .claude/command-guard.json (default 10, set to 1 to disable throttling):
{
"warningThrottle": 10,
"rules": [ ... ]
}Counter state lives in ~/.claude/command-guard/throttle/<session_id>.json and is pruned automatically once the directory grows past ~50 sessions.
The safePatterns array contains regex patterns that bypass blocking rules. This is useful for allowing safe variants of otherwise dangerous commands:
{
"safePatterns": [
"git\\s+checkout\\s+-b\\s",
"git\\s+restore\\s+--staged",
"git\\s+push\\s+--force-with-lease"
]
}Any blocked command can be overridden by adding a comment with explicit reasoning:
git reset --hard # OVERRIDE: cleaning up failed rebase stateThe reason must be at least 5 characters. Override usage is logged to stderr.
{
"rules": [
{
"match": "command",
"pattern": "git\\s+reset\\s+--hard",
"message": "git reset --hard destroys all uncommitted changes. Use 'git stash' first.",
"severity": "error"
},
{
"match": "command",
"pattern": "git\\s+push\\s+(\\S+\\s+)*(-f|--force)(?!-with-lease)",
"message": "git push --force overwrites remote history. Use --force-with-lease for safer force push.",
"severity": "error"
}
],
"safePatterns": ["git\\s+push\\s+--force-with-lease"]
}{
"rules": [
{
"match": "command",
"pattern": "^prettier\\s+(?!--help)",
"message": "Use /automated-checks skill for formatting, linting, and type checking.",
"severity": "error"
},
{
"match": "command",
"pattern": "git\\s+commit\\s+",
"message": "Make sure you have used /git-commits skill for proper conventional commits.",
"severity": "warning"
}
]
}{
"rules": [
{
"match": "file_path",
"pattern": "(^|/)patches/.*\\.patch$",
"message": "Patch files are generated by pnpm. Use pnpm patch workflow instead.",
"severity": "error"
},
{
"match": "file_path",
"pattern": "supabase/migrations/",
"message": "Migration files should be generated, not written directly.",
"severity": "warning"
}
]
}{
"rules": [
{
"match": "tool_name",
"pattern": "mcp__betterstack__",
"message": "Consider using better-stack-logs agent for context isolation.",
"severity": "warning"
}
]
}The plugin includes a /command-guard skill for viewing and editing configuration. Use it to:
- View current configuration
- Add new rules
- Edit existing rules
- Add safe patterns
The plugin registers hooks for:
- PreToolUse (Bash, Edit, Write, MCP tools): Checks for
errorseverity rules and blocks if matched - PostToolUse (all tools): Checks for
warningseverity rules and shows reminders
Prevents Claude from finishing a session with uncommitted changes. Uses a Stop hook — when Claude tries to end the conversation, the hook checks git status and blocks if there are uncommitted changes.
- If the working tree is dirty, Claude is re-prompted to commit before finishing
- Claude can acknowledge the situation by including
UNCOMMITTED_OKin its message (e.g. when it only did research or the user told it not to commit) - Does nothing outside of git repositories
The repository includes .claude/settings.json which installs both plugins from the local directory for development:
{
"plugins": ["./", "./plugins/commit-guard/"]
}Tests use pytest and can be run without any Claude Code or LLM calls:
pytest tests/ -vThe tests cover:
- Unit tests for individual functions (rule matching, command parsing, etc.)
- Integration tests that run the script as a subprocess with sample configs
Test fixtures are in tests/fixtures/. The basic_rules.json file contains sample rules for testing.