cd /news/ai-agents/show-hn-guardrails-for-claude-code-b… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-141621] src=github.com β†— pub= topic=ai-agents verified=true sentiment=Β· neutral

Show HN: Guardrails for Claude Code: blocks rm -RF, reports what loaded

Developer ahmed-alstaty released a Claude Code plugin called Guardrails that blocks destructive shell commands such as recursive removal of /, ~, $HOME, ., .., *, the project directory, or anything outside it, and reports at session start which instruction files, rules, skills, hooks and plugins Claude actually loaded. The plugin enforces its rules through dependency-free python3 (3.8 or newer) scripts using PreToolUse and SessionStart hooks, tokenizes every Bash command to judge each simple command separately, and also denies edits to secrets paths including .env, *.pem, *.key, id_rsa*, credentials.json and .aws/credentials. It installs via the repository's own marketplace, alstaty, with the commands /plugin marketplace add ahmed-alstaty/guardrails-plugin and /plugin install guardrails@alstaty.

read9 min views2 publishedSep 29, 2026
Show HN: Guardrails for Claude Code: blocks rm -RF, reports what loaded
Image: Michielbdejong (auto-discovered)

A Claude Code plugin that puts hard limits around what Claude can do in your project, tells you what Claude actually loads at session start, and lints your CLAUDE.md files against Anthropic's own guidance.

Three features, all enforced with plain python3 scripts and no dependencies:

  1. Enforcement hooks (PreToolUse ): destructive shell commands and edits to secrets or protected paths are denied before the tool runs. Deployment and infrastructure files require your approval.
  2. Session start report (SessionStart ): a compactOK / WARN / SKIPPED checklist of instruction files, rules, skills, hooks and plugins, with warnings for the mistakes the docs warn about.
  3. Skills :/guardrails:guardrails-lint ,/guardrails:guardrails-status and/guardrails:guardrails-test .

The repository is its own marketplace (alstaty), so it takes two commands in a Claude Code session:

/plugin marketplace add ahmed-alstaty/guardrails-plugin
/plugin install guardrails@alstaty

Or from your shell:

claude plugin marketplace add ahmed-alstaty/guardrails-plugin
claude plugin install guardrails@alstaty            # add --scope project to enable it for the whole repo

From a local checkout, replace the GitHub shorthand with the path: claude plugin marketplace add ./guardrails-plugin. To try it for one session without installing: claude --plugin-dir ./guardrails-plugin.

Requirements: Claude Code with plugin support and python3 (3.8 or newer) on PATH. No pip packages.

Every Bash command is tokenized (quotes, escapes, &&, ||, ;, |, $( ), backticks, bash -c, eval, sudo/ env/ xargs wrappers) and each simple command is judged on its own, so true && rm -rf / is caught and rm build/old.log is not.

Category Denied Asks for approval Allowed
rm /shred /unlink recursive removal of / ,~ ,$HOME ,. ,.. ,* , the project directory, anything outside it, an unexpanded$VAR/ ,--no-preserve-root , protected or secrets paths non-recursive rm of a file outside the project;xargs rm -r ;find with-delete and no filter files and directories inside the project ( rm -rf build ,rm -rf node_modules )
git push --force /-f /+ref /--mirror tomain ormaster (current branch is detected when no refspec is given); pushing a deletion ofmain /master ;reset --hard ;clean -fd /-fx ;branch -D ;checkout -- . ,checkout . ,restore . ;filter-branch ,filter-repo push --force with an unknown branch;--force-with-lease to main;checkout -- <file> ,restore <file> ;clean -f ;stash drop/clear ;reflog expire ;gc --prune everything else, including push -f to a feature branch
Databases DROP DATABASE ,DROP SCHEMA ,dropdb ,dropDatabase() ,FLUSHALL /FLUSHDB ,db:drop ,migrate reset (inline,-c /-e , or heredoc, when a database client or migration tool is in the command) DROP TABLE ,TRUNCATE ,DELETE FROM withoutWHERE
Permissions chmod -R 777 ,chmod/chown -R on root or outside the project
Remote code curl ... | sh ,wget ... | sudo bash ,bash <(curl ...) ,sh -c "$(curl ...)" curl ... | jq
Disks and system mkfs* ,wipefs ,fdisk ,dd of=/dev/... ,> /dev/sd* , fork bombs,shutdown /reboot ,kill -1 ,crontab -r ,mv x /dev/null sudo su ,sudo -i
Shell writes redirection ( > ,>> ,&> ,tee ,cp /mv destination,sed -i ,truncate ) into a secrets file, a protected path, or.git/ writes outside the project or into an ask path

File tools (Edit, Write, MultiEdit, NotebookEdit) are judged by path:

Verdict Paths
Denied secrets: .env ,.env.* ,*.pem ,*.key ,*.p12 ,*.pfx ,*.jks ,id_rsa* ,id_ed25519* ,credentials.json ,service-account*.json ,secrets/** ,.netrc ,.htpasswd ,.aws/credentials ,.npmrc ,.pypirc ; protected:.git/** ; anything outside the project directory (the session scratchpad is exempt)
Asks Dockerfile* ,docker-compose*.yml ,compose*.yml ,.github/workflows/** ,.gitlab-ci.yml ,terraform/** ,*.tf ,k8s/** ,kubernetes/** ,helm/** ;migrations/** only when an edit deletes content or aWrite overwrites an existing migration
Allowed .env.example ,.env.sample ,.env.template ,*.example ,*.sample ,*.template ,*.pub , and everything else inside the project

Reading is never blocked; cat .env goes through so Claude can still understand configuration.

A block is never silent. The hook returns the documented permissionDecision: "deny" with a permissionDecisionReason that Claude sees, plus a systemMessage for you, and echoes the reason to stderr. Every reason ends with how to proceed:

Guardrails blocked: git push --force to main rewrites shared history. To allow once, run the command yourself in your terminal. To allow always, add it to .claude/guardrails.json allow_commands.

If the hook itself fails (unreadable input, internal error) it returns ask, not allow.

Create .claude/guardrails.json in the project (committed, applies to everyone) or ~/.claude/guardrails.json (just you). Both extend the defaults; lists are appended, nothing is replaced.

{
  "allow_commands": ["rm -rf /tmp/scratch*", "git push --force origin main"],
  "block_commands": ["npm publish*", "terraform apply*"],
  "protected_paths": ["package-lock.json", "docs/adr/**"],
  "ask_paths": ["config/production.yml"],
  "secrets_globs": ["*.token", "config/keys/**"],
  "allow_paths": ["Dockerfile", ".env.ci"]
}
  • allow_commands andblock_commands are shell-style globs matched against the whole command and against each simple command inside it.block_commands wins overallow_commands ;allow_commands wins over the built-in rules.
  • protected_paths are always denied,ask_paths prompt you,secrets_globs are denied, andallow_paths is checked first and exempts a path from all of them.
  • A pattern without / matches a file name anywhere (*.pem ); a pattern with/ matches the path relative to the project root (secrets/** ); an absolute or~/ pattern matches outside the project.

The session report shows which config files were loaded and warns about invalid JSON or unknown keys. A broken config never disables enforcement; the defaults keep applying.

At startup, resume and /clear the plugin prints this checklist in your terminal (as a system message) and hands Claude a short summary, so Claude knows the guardrails are on and will report a block instead of trying to route around it. Run /guardrails:guardrails-status to see the full report again at any time:

GUARDRAILS session report  (cwd: /work/app)

Instruction files (load at launch)
  WARN    [project] CLAUDE.md (243 lines) - over 200 lines (243); @import missing: docs/setup.md
  OK      [user] ~/.claude/CLAUDE.md (31 lines)
  SKIPPED AGENTS.md present but NOT read: a CLAUDE.md/CLAUDE.local.md exists at cwd or above ...

Rules (.claude/rules)
  OK      [project] .claude/rules/api.md (12 lines) paths: src/api/**/*.ts (on demand)

Nested instruction files (load when Claude reads files there)
  OK      packages/web/CLAUDE.md (18 lines) on demand

Skills
  WARN    [project] /deploy (.claude/skills/deploy/SKILL.md): no description, Claude cannot decide when to use it

Hooks
  OK      .claude/settings.json: PostToolUse(1)
  SKIPPED .claude/settings.local.json: not present
  OK      plugin guardrails: PreToolUse, SessionStart (this plugin)

Plugins
  OK      guardrails@alstaty v1.0.1 (enabled in user settings)

Guardrails
  OK      config: .claude/guardrails.json (allow_commands 2, block_commands 1, ...)

Summary: 7 OK, 2 WARN, 2 SKIPPED. Run /guardrails:guardrails-lint to fix instruction files.

Warnings cover: instruction files over 200 lines or over 4 MiB (Claude Code skips those), @imports that point at missing files, the same subject under "always" in one file and "never" in another, repeated IMPORTANT/ ALWAYS/ NEVER, CLAUDE.local.md not in .gitignore, skills without a name or description, SKILL.md files that are a summary with no steps and no pointer to steps, AGENTS.md present but not read (or present without a CLAUDE.md), invalid settings JSON, and plugins enabled in settings but not installed.

Two channels, two costs. The full report is a system message: it is shown to you and never enters the model, so it is free. What Claude receives is a few lines (the OK / WARN / SKIPPED counts, up to four warnings, and the instruction not to route around a block), about 150 to 300 tokens once per session start, cached on every later turn. Each denied or asked tool call adds one sentence, about 40 tokens. Running /guardrails:guardrails-status or /guardrails:guardrails-lint puts that output into the conversation like any other tool result, so use them when you want them, not on every session.

Command What it does
/guardrails:guardrails-lint [files] Lints every instruction file that loads (or the files you name) against the documented rules: line count vs 200, procedures that should be skills, path-by-path directory descriptions Claude can derive from code, repeated emphasis words, vague rules, broken @paths , duplicated rules across nested files, contradictions. Writes a suggested trimmed copy of each file and aSKILL.md draft for each procedure into a scratch directory. It changes nothing until you pick what to apply.
/guardrails:guardrails-status Re-runs the session report on demand.
/guardrails:guardrails-test ["cmd" ...] [--path FILE] Dry-runs the blocklist against a built-in sample set, or the commands and paths you pass, and prints ALLOW /ASK /DENY with the reason. Nothing is executed.

All three are user-invoked only (disable-model-invocation: true), so they cost no context until you call them.

tests/run_tests.sh

The script copies a fixture project to a temporary directory, feeds sample hook JSON to the real hook scripts and checks the decisions: destructive rm blocked, safe rm allowed, .env edits blocked, force push to main blocked, normal git push allowed, Dockerfile edits ask, config overrides, fail-safe on garbage input, the session report's warnings, the linter's findings, and the manifests. It exits 0 when everything passes.

To check the manifests with the CLI: claude plugin validate . (marketplace) and claude plugin validate .claude-plugin/plugin.json --strict.

  • This is enforcement for Claude Code only. The hooks run when Claude Code calls a tool. They do nothing for commands you type yourself, for other agents, or for scripts Claude writes and you run later.
  • It is not a sandbox. A hook sees a command string, not what the command does. A build script that deletes files, amake clean target,docker compose down -v , or a program invoked through a name the parser does not recognise all pass. Use Claude Code's sandbox and permission modes for isolation; use this plugin for guardrails on the common mistakes.
  • It does not replace backups or branch protection. Force pushes tomain are blocked here, but the remote should refuse them too. Commit often.
  • Heuristics have edges. The tokenizer handles the common shell constructs, not every one. Variables are not expanded (a command built from$VAR is judged on its text, andrm -rf $VAR/ is denied for that reason). Branch detection for a baregit push -f runsgit symbolic-ref in the project; if that fails the hook asks instead of guessing. The lint and contradiction checks are pattern-based and will produce some false positives; the skill tells Claude to verify each one before reporting it.
  • python3 must be on PATH for the hook processes. If it is missing, Claude Code reports a hook error and the tool call proceeds; the session report will not appear, which is your signal.
  • Windows : paths are handled with forward slashes and the scripts avoid POSIX-only calls, but the shell rules target bash-style commands. PowerShell commands are not parsed.
guardrails-plugin/
β”œβ”€β”€ .claude-plugin/
β”‚   β”œβ”€β”€ plugin.json           plugin manifest
β”‚   └── marketplace.json      marketplace "alstaty" listing this plugin at "."
β”œβ”€β”€ hooks/hooks.json          PreToolUse (Bash; Edit|Write|MultiEdit|NotebookEdit) and SessionStart
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ guardrails_common.py  config , path classification, hook output
β”‚   β”œβ”€β”€ shell_guard.py        tokenizer and command rules
β”‚   β”œβ”€β”€ pretooluse.py         PreToolUse entry point
β”‚   β”œβ”€β”€ instruction_scan.py   instruction-file discovery and heuristics
β”‚   β”œβ”€β”€ session_report.py     SessionStart entry point (also --text)
β”‚   β”œβ”€β”€ lint_claude_md.py     linter used by guardrails-lint
β”‚   └── dry_run.py            used by guardrails-test
β”œβ”€β”€ skills/
β”‚   β”œβ”€β”€ guardrails-lint/      SKILL.md + reference.md
β”‚   β”œβ”€β”€ guardrails-status/SKILL.md
β”‚   └── guardrails-test/SKILL.md
└── tests/run_tests.sh        hook tests with a fixture project

MIT. Copyright (c) 2026 Ahmad Alstaty.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/show-hn-guardrails-f…] indexed:0 read:9min 2026-09-29 Β· β€”