Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/swt-labs/vibe-better-with-claude-code-vbw/llms.txt

Use this file to discover all available pages before exploring further.

LLMs with Bash access will occasionally run destructive database commands during verification or debugging — migrate:fresh, db:drop, TRUNCATE TABLE — wiping development data without warning. VBW prevents this with a PreToolUse hook that intercepts every Bash call before it reaches the shell.

How it works

bash-guard.sh fires on the Bash tool, not on a specific agent. Every Bash command from every agent passes through the same gate. The hook pattern-matches against a blocklist and blocks matches with exit code 2 (fail-closed). The command never executes.
Agent wants to run: php artisan migrate:fresh --seed
                              |
                    +─────────v──────────+
                    │  Claude Code sees  │
                    │  Bash tool call    │
                    +─────────┬──────────+
                              |
                    +─────────v──────────+
                    │  PreToolUse fires  │
                    │  bash-guard.sh     │
                    +─────────┬──────────+
                              |
                 +────────────v────────────+
                 │ VBW_ALLOW_DESTRUCTIVE=1?│
                 +──┬───────────────────┬──+
                   yes                  no
                    |          +────────v────────+
                 exit 0       │  Pattern match   │
                 (allow)      │  against blocklist│
                              +──┬───────────┬──+
                              match       no match
                               |              |
                            exit 2         exit 0
                            (BLOCK)        (allow)
                               |
                    +──────────v───────────+
                    │ Agent sees:          │
                    │ "Blocked: destructive│
                    │  command detected"   │
                    +──────────────────────+
The agent receives an error message explaining the block and adapts — typically falling back to read-only queries or the test suite.

Three defense layers

LayerTypeWhen it firesReliability
bash-guard.shPreToolUse hookBefore every Bash callDeterministic (regex match)
Agent prompt rulesBehavioral guidanceWhen agent reads its instructionsProbabilistic (model compliance)
forbidden_commands contractPostToolUse hard gateAfter Bash executionDeterministic but reactive
Layer 1 is the fix. It blocks destructive commands before they execute, regardless of what the model decides to do. Layers 2 and 3 reduce noise and provide an audit trail.

Agents covered

All five agents with Bash access are filtered equally:
  • Dev — primary implementation agent
  • QA — verification and testing
  • Lead — orchestration and coordination
  • Debugger — investigation and diagnosis
  • Docs — documentation tasks
Adding a new agent type does not create a gap — the hook matches on the Bash tool, not on agent identity.

Patterns blocked

40+ patterns across every major ecosystem:
CategoryBlocked patterns
PHP / Laravelartisan migrate:fresh, artisan migrate:reset, artisan migrate:refresh, artisan db:wipe, artisan db:seed --force
Ruby / Railsrails db:drop, rails db:reset, rake db:schema:load, bundle exec rake db:drop
Python / Djangomanage.py flush, manage.py sqlflush, django-admin flush
Prismaprisma migrate reset, prisma db push --force-reset, npx prisma migrate reset
Knexknex migrate:rollback --all
Sequelizesequelize db:drop, npx sequelize-cli db:drop
TypeORMtypeorm schema:drop
Drizzledrizzle-kit push --force
Gomigrate ... drop (via golang-migrate)
Rust / Dieseldiesel database reset, diesel migration revert --all
Rust / SQLxsqlx database drop
Elixir / Ectomix ecto.drop, mix ecto.reset, mix ecto.rollback --all
Raw SQLDROP DATABASE, DROP TABLE, TRUNCATE via mysql, psql, sqlite3, mongosh
MongoDBdropDatabase(), .drop() via mongosh
Redisredis-cli FLUSHALL, redis-cli FLUSHDB
Docker volumesdocker-compose down -v, docker compose down -v, docker volume rm, docker volume prune, docker system prune --volumes
Filesystemrm *.sqlite3, rm *.db, rm -rf /var/lib/mysql (and postgresql, mongodb)
Safe commands pass through unblocked: php artisan migrate (forward migration), rails db:migrate, prisma migrate dev, docker-compose down (without -v), and all read-only queries.

Override options

When you legitimately need to run a destructive command, you have three options: Environment variable — Set VBW_ALLOW_DESTRUCTIVE=1 before starting your session. The guard checks this first and exits immediately with zero overhead.
VBW_ALLOW_DESTRUCTIVE=1 claude
Config toggle — Disable the guard entirely for a project:
/vbw:config bash_guard false
Or edit .vbw-planning/config.json directly:
{
  "bash_guard": false
}
Run it yourself — The hook only fires inside Claude Code. Open a separate terminal and run the command directly. The guard protects against agents doing it unsupervised, not against you.
Setting bash_guard: false removes protection for all five agents — Dev, QA, Lead, Debugger, and Docs. Only disable it if you intentionally need agents to run destructive commands in this project.

Extending the blocklist

Add project-specific patterns to .vbw-planning/destructive-commands.local.txt:
echo "scripts/nuke-dev-data\.sh" >> .vbw-planning/destructive-commands.local.txt
One regex per line, same format as the default config/destructive-commands.txt. Local patterns supplement the defaults — they don’t replace them.
# Block a custom reset script
scripts/nuke-dev-data\.sh

# Block a project-specific ORM command
myorm\s+schema:destroy

Event logging

Every blocked command is logged to .vbw-planning/.event-log.jsonl with:
  • Command preview (truncated to 40 characters)
  • Matched pattern
  • Agent name (VBW_ACTIVE_AGENT)
  • Timestamp
Use this log to audit what agents attempted during a session.

Design notes

Fail-closed. If jq is missing, input is unparseable, or anything unexpected happens, the guard blocks the command (exit 2). It never fails open. ~50ms overhead. One jq parse + one grep per Bash call. The 5-second hook timeout in hooks.json provides a safety ceiling.

Build docs developers (and LLMs) love