agentsclimarketplace

Vibe safe

Skill googlarz/vibe-safe

Use when a non-technical contributor (PM, designer, researcher) is about to write, commit, or merge AI-assisted code in a shared codebase and wants to catch the specific ways vibecoding goes wrong before they become incidents.From its SKILL.md

Install
npx -y skills add googlarz/vibe-safe

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

49.6 KB, ~12.4k tokens by cl100k_base, as published. Nobody here has run it

vibe-safe

Overview

Active safety session for non-technical contributors. Claude reads your actual git state and scans real files — you don't self-report anything. Every flag cites the line and file.

Core rule: "Claude said it's fine" is not a safety check. This skill is.

Auto-routing

You don't need to pick a mode. Just say vibe-safe and Claude will detect the right one:

git status shows no staged changes, you're describing a task → BEFORE mode
git diff --staged has content → COMMIT mode
git diff HEAD has unstaged changes → REVIEW mode
"did Claude change more than I asked?" → SCOPE mode
commits ahead of main, ready to open PR → PR mode
conflict markers in any file → CONFLICT mode
Claude just proposed something that feels big → ALARM mode
"what does this flag mean?" / need deeper explanation → EXPLAIN mode
developer reviewing a vibe-coded PR → REVIEWER mode
checking whether past commits contain mistakes → HISTORY mode
`.vibesafe` exists and repo has changed significantly → REFRESH mode

Or invoke directly: vibe-safe before / review / scope / commit / pr / conflict / alarm / history / explain / reviewer / refresh


Repo Configuration: .vibesafe

Two purposes: (1) custom danger/safe zones, (2) developer-defined contracts that vibe-safe enforces for every PM contribution.

# .vibesafe

# ── Zones ────────────────────────────────────────────
danger_zone: src/payments/
danger_zone: infrastructure/
safe_zone: src/components/marketing/
safe_zone: public/images/

# ── Developer contracts ──────────────────────────────
# Set these once. vibe-safe enforces them for every commit from this repo.

require_tests: true              # New source files must have test file changes
max_changed_files: 15            # Warn + SCOPE audit if commit exceeds this
require_migration_rollback: true # Migrations must include a down/downgrade/rollback
block_pattern: TODO              # Hard stop if TODO appears in added implementation lines
block_pattern: FIXME             # Same for FIXME (repeat the key for multiple patterns)
require_reviewer: @alice         # Added to every PR description generated by vibe-safe

BEFORE mode generates this file (zones only). Developers add contracts. Once committed, every contributor is held to the same standards automatically.


Mode: BEFORE

Before Claude writes anything. Run this before you describe your task.

Claude runs:

  • git branch --show-current — if main/master: STOP and fix immediately: Claude runs git checkout -b feature/[3-word-description-from-task]
  • git log --follow -- <your target files> — establishes who owns this area
  • File extension check against Danger Zones + .vibesafe custom zones
  • ls .git/hooks/pre-commit — checks whether the mechanical safety hook is installed

Output:

  1. Scoped prompt — limits what files Claude is allowed to touch. Use it verbatim.

  2. Hook installation — check for an existing pre-commit hook first:

    • No existing hook → copy ~/.claude/skills/vibe-safe/hooks/pre-commit to .git/hooks/pre-commit and chmod +x
    • Hook already exists → wrap it: create a new hook that runs the existing hook first, then vibe-safe. Do not overwrite. Announce what was wrapped: "Existing hook detected (code-review-graph / husky / custom) — vibe-safe will run after it."
    # Wrapper pattern when hook already exists:
    #!/bin/sh
    # vibe-safe wrapper — runs existing hook first
    sh .git/hooks/pre-commit.bak "$@"
    RESULT=$?
    [ "$RESULT" -ne 0 ] && exit "$RESULT"
    sh ~/.claude/skills/vibe-safe/hooks/pre-commit
    

    Back up the existing hook to .git/hooks/pre-commit.bak before wrapping.

  3. CI installation — the CI workflow file lives at .github/workflows/vibe-safe.yml, which is itself a Danger Zone path. Always treat it this way:

    • Do not silently copy it into the current branch
    • Always tell the contributor: "The CI workflow file needs a developer to review and merge it — .github/workflows/ is a danger zone. I'll prepare the file, but it should go in as a separate PR that a developer approves."
    • Offer two paths:
      • Path A (recommended): Stage just the CI files on the current branch and note in the PR description that a developer needs to review the .github/workflows/ change specifically
      • Path B: Create a separate chore/install-vibe-safe-ci branch for the CI file, so the contributor's feature work isn't blocked
    • Never suggest committing the CI workflow file the same way as .vibesafe or the hook — those are safe for the contributor to own; the workflow file is not

Post-generation step order — after .vibesafe is written and confirmed, present these in sequence, one at a time:

Next steps:

1. Commit .vibesafe now — this makes the rules live for everyone on the team.
   Stage + commit on this branch? Yes / Not yet

2. Install the pre-commit hook (runs locally on every git commit, no Claude needed).
   [shows whether existing hook was detected and whether it will wrap or install fresh]
   Install now? Yes / Skip

3. Install CI workflow (.github/workflows/vibe-safe.yml).
   ⚠️  This file is in your danger_zone: .github/workflows/ — a developer needs to
   review this before it merges. I'll prepare the file, but don't commit it yourself.
   Prepare file for developer review? Yes / Skip

Never present all three at once as a list to action simultaneously. Step 1 is safe for the contributor to own. Step 2 is local and safe. Step 3 always requires developer involvement — say so explicitly, every time, not just when .github/workflows/ is in the detected danger zones. 4. .vibesafe generation — if no .vibesafe exists, Claude asks:

  • "What parts of this codebase should a developer always be consulted on?"
  • "What areas are definitely yours to edit freely (copy, images, marketing)?" Then writes the file and shows you what to commit.

For developers setting up a shared repo: Claude reads the repo before asking anything, then proposes contract values with evidence:

Claude runs:
find . -name "*.test.*" -o -name "*_test.*" -o -name "*.spec.*" | head -20
→ Found 47 test files → proposing require_tests: true

git log --oneline -30 | xargs -I{} git diff --name-only {}^ {} | wc -l ÷ 30
→ Average commit touches 7 files → proposing max_changed_files: 12

find . -path "*/migrations/*" | head -10 → then checks each for def down / exports.down
→ 3 migration files, all have rollbacks → proposing require_migration_rollback: true

git grep -rn "TODO" --include="*.ts" --include="*.py" | wc -l
→ 2 TODOs in entire codebase → proposing block_pattern: TODO (low noise)

git log --format="%ae" -50 | sort | uniq -c | sort -rn | head -3
→ [email protected] reviewed 80% of commits → proposing require_reviewer: @alice

ls .env.example .env.sample 2>/dev/null
→ .env.example exists → env drift check will be active

Discovery runs silently, then Claude produces one document to paste into Slack or email. Discovered rules appear with confirmation prompts; open questions cover the rest:

Hi — setting up a safety tool before I commit code.
Can you confirm what I found and answer a few questions?

FOUND IN REPO:

✅ require_tests — found 47 test files
   Keep? Yes / No / Only for: ___

✅ max_changed_files: 12 — avg 7 files per commit in git history
   Keep 12? Yes / Lower to: ___

✅ require_migration_rollback — all 3 existing migrations have a down()
   Keep? Yes / No

✅ block_pattern: TODO — only 2 TODOs in entire codebase
   Keep? Yes / No / Add more: ___

✅ require_reviewer: @alice — top committer across src/
   Right person? Yes / Change to: ___

QUESTIONS:

1. Any folders I should never touch without you?
   (already flagging: migrations/, src/auth/ — anything else?)
   →

2. Areas that are definitely mine to edit freely?
   (e.g. marketing copy, images, public/)
   →

3. Anything else I should know before I start committing?
   →

PM pastes the answers back. Claude interprets the answers using the rules below, then shows a preview of .vibesafe before writing it.

Answer interpretation rules — apply these when processing the questionnaire responses before writing .vibesafe:

Rule 1 — Philosophy ≠ decision. Re-ask as a binary. If the PM answers a yes/no question with an explanation, philosophy, or constraint instead of a clear yes/no, do NOT interpret it and move on. Re-ask as a forced choice:

"Got it — so should I [enforce X as a hard rule / just flag X for a developer to review / skip X entirely]?" Examples of philosophy answers that need re-asking:

  • "migrations should be idempotent" → ask: "So enforce require_migration_rollback: true? Yes / No / Flag only"
  • "it's about coverage not test count" → see Rule 3
  • "changes should be scoped" → ask: "Should I keep max_changed_files at 12, or would a different number fit better? Keep 12 / Lower to: __ / Raise to: __"

Rule 2 — Rejection without replacement. Ask who/what instead. If the PM rejects a proposed value without providing a replacement, don't drop the rule entirely. Ask for the replacement:

  • "naaaah" to require_reviewer: @alice → ask: "Who should review PRs then? One person, or multiple? Name(s): ___"
  • "not that" to a danger zone → ask: "What should I use instead?"

Rule 3 — "Maybe" or vague approval. Confirm interpretation before writing. If the PM says "maybe", "probably", "I guess", or gives conditional approval, state your interpretation and ask for confirmation before writing:

"I'll treat that as yes and add admin.py to danger_zone — correct? Yes / No" Never silently interpret a maybe as a yes or a no.

Rule 4 — "No idea" on a technical component. Offer a safe default. If the PM doesn't know whether a component is load-bearing (Celery worker, middleware, DI path, background job), offer:

"No idea is fine — safest approach is to add it to danger_zone so it always gets a developer review. Do that? Yes / Skip" Unknown territory defaults to flagged, not ignored.

Rule 5 — Requested rule beyond vibe-safe's capability. Be honest, offer what's possible. If the PM asks for a rule vibe-safe can't mechanically enforce, say so and offer the closest thing:

  • "TODO only allowed behind a feature flag" → "I can't check for feature flags automatically — that requires code understanding, not grep. I can hard-block TODO everywhere in implementation files, which means Claude must not write TODOs at all. Your developer would verify the feature-flag rule in code review. Block TODO everywhere? Yes / No"
  • "tests must maintain coverage" → "Two separate things: I can enforce that every source change includes a test file in the PR (presence). I can't measure live coverage — that's your CI pipeline's job (--cov-fail-under=N, codecov, etc.). But if Claude lowers that threshold to make CI pass, I will catch that — same as I catch jest/codecov threshold drops. So: I enforce test presence in PRs, your CI enforces the coverage floor, and I guard against the floor being quietly lowered. Sound right? Yes / No"

Rule 6 — Jargon the PM doesn't recognize. Explain before proceeding. If a question uses technical terms and the PM responds with "what does that mean?" or equivalent confusion, explain it in one sentence before asking again:

"Claude model routing means your repo has configuration that controls which AI model is used for different tasks. Should changes to those config files require a developer's review? Yes / No / What files?" Never re-ask the jargon-heavy version.

Rule 7 — Preview before write. After processing all answers, before writing .vibesafe, show the PM what will be written:

Here's what I'll write to `.vibesafe`:

danger_zone: backend/alembic/
danger_zone: backend/app/routers/auth.py
...
require_tests: true
max_changed_files: 12
block_pattern: FIXME
require_reviewer: @team-lead

Anything to change before I create this file? Yes / Looks good

Only write after explicit confirmation. This is the last checkpoint before the config is locked.

Stack-aware questions — before sending the questionnaire, Claude runs:

ls package.json Gemfile requirements.txt Cargo.toml go.mod pom.xml 2>/dev/null

and adds one targeted section based on what it finds:

Stack detectedExtra questions added
package.json + React/Next.js"Do you have server-side API routes I shouldn't touch? Any SSR/ISR pages with special caching?"
package.json + Express/Node API"Any rate-limiting or auth middleware paths? Background job queues?"
Gemfile (Rails)"Any Mailers, Sidekiq workers, or background jobs? Admin panel at /admin?"
requirements.txt / pyproject.toml (Django/FastAPI)"Any Celery tasks, admin customizations, or Django signals?"
go.mod"Any gRPC service definitions or generated protobuf files I shouldn't edit?"
Cargo.toml"Any FFI bindings or unsafe blocks that need special review?"

Stack-specific danger zones are also auto-suggested (e.g., danger_zone: pages/api/ for Next.js, danger_zone: app/jobs/ for Rails).


Mode: REFRESH

Run when .vibesafe already exists but the repo has evolved and the rules may be stale.

Claude re-runs the same discovery as BEFORE mode (test file count, average commit size, migration patterns, TODOs, top committers) and diffs the findings against the existing .vibesafe:

Output — three sections:

UNCHANGED (still accurate):
  require_migration_rollback: true — 5 migration files, all have rollbacks ✅

DRIFTED (rule exists but evidence changed):
  max_changed_files: 12 → evidence now suggests 18 (avg commit grew from 7 → 11 files)
  Proposed update: max_changed_files: 18
  Keep current / Update / Remove?

NEW (found in repo, not yet in .vibesafe):
  block_pattern: FIXME — found 0 FIXMEs in codebase, low noise
  Add? Yes / No

MISSING (in .vibesafe, no longer evidence-backed):
  require_reviewer: @alice — [email protected] has only 3 commits in last 90 days (was top reviewer)
  New top committer: [email protected] (67% of recent commits)
  Update to: @bob? Yes / No / Keep @alice

Claude proposes only changes — if a rule is still accurate it stays untouched. PM sends the diff to the developer, gets sign-off, Claude applies the updates to .vibesafe and commits.


Mode: REVIEW

After Claude writes code, before you commit.

Claude runs:

  • git diff HEAD for unstaged changes
  • git grep -nE "sk-|pk_|ghp_|AKIA|api_key[[:space:]]*=|secret[[:space:]]*=|password[[:space:]]*=|Bearer |token[[:space:]]*=" across ALL tracked files — credentials live in files you didn't intend to change and won't appear in your diff

What gets flagged with evidence:

SignalExample flag
Credential patternsrc/api/client.ts:14 — const API_KEY = "sk-proj-abc123" ⛔ Permanent in git history even if deleted
File deletionsrc/utils/helper.ts deleted — Claude may be wrong that it's unused
Config file modifiedconfig/nginx.conf — controls production traffic for everyone
Migration file presentdb/migrations/*.sql — irreversible schema change
Auth-related filesrc/auth/session.ts — security surface, needs developer eyes
Scope creepFiles modified that weren't in your original target
Quality gate weakeningjest.config.ts: threshold 80 → 60 ⛔ Claude fixed the check, not the bug
Error suppressionsrc/api/client.ts:23 — // @ts-ignore ⛔ Type error hidden, not fixed
Linter suppressionsrc/Form.tsx:7 — // eslint-disable-next-line ⛔ Lint error hidden, not fixed
Test bypassauth.test.ts:45 — it.skip("validates token"...) ⛔ Failing test skipped, not fixed
Test deletiontests/payment.test.ts deleted ⛔ Entire test file removed to make suite pass
Debug outputsrc/Form.tsx:12 — console.log(userData) ⛔ Logs production data, may expose PII
Empty catch blocksrc/api/client.ts:67 — catch (e) {} ⛔ Errors silently swallowed
Lock file driftpackage-lock.json changed, package.json unchanged — undocumented dependency change
New dependencypackage.json: "lodash" added — check license, security, bundle size
Binary/large fileassets/video.mp4 staged ⛔ Bloats git history permanently
PII in diffseeds/users.ts:4 — email: "[email protected]" ⛔ Real data committed
Internal hostnameconfig/api.ts:2 — baseURL: "http://api.internal:8080" ⛔ Infrastructure exposed
Force push proposedClaude suggested git push --force → ALARM immediately
XSS sink addeddangerouslySetInnerHTML added — direct XSS attack surface
Code injectioneval( added — arbitrary code execution risk
SSL disabledverify=False / NODE_TLS_REJECT_UNAUTHORIZED=0 / rejectUnauthorized: false — Claude "fixed" a cert error by disabling verification
CORS wildcardorigin: '*' / Access-Control-Allow-Origin: * — API open to any domain
.gitignore entries removedPreviously ignored files (possibly secrets) now tracked and will be committed
Timing hacksetTimeout/sleep/time.sleep with hardcoded value — Claude papered over a race condition
TypeScript type erasure: any / as any added — Claude escaped the type system instead of fixing types
Debug mode in configDEBUG = True / debug: true in non-test config — debug mode left on
TODO/FIXME stubthrow new Error("TODO") or // TODO: implement in new code — Claude left a placeholder
Commented-out code// const user = await getUser(id) — working code disabled, Claude may have been unsure
Private key file stagedserver.pem, id_rsa, client.p12 staged ⛔ Binary credential — not caught by text grep
rm -rf in scriptrm -rf $DIR/* in committed shell script ⛔ Aggressive cleanup Claude wrote without knowing prod paths
String thrown, not Errorthrow "something failed" — loses stack trace, bugs become undiagnosable in production

Every flag ends with a plain-English explanation of the worst-case consequence and a specific action.


Mode: COMMIT

Before git commit. For fixable problems, Claude executes the fix — you don't need to know the git commands.

Five automated checks, each with remediation:

  1. Credential scangit grep -nE "sk-|pk_|ghp_|AKIA|api_key[[:space:]]*=|secret[[:space:]]*=|password[[:space:]]*=|Bearer |token[[:space:]]*=" on ALL tracked files

    • Staged file contains credential → Claude runs git restore --staged <file>, explains the key must be rotated even after removal
    • Untracked file contains credential → flag only (Claude cannot unstage what isn't staged)
  2. Danger Zone audit — staged files vs. default list + .vibesafe custom zones

    • Danger Zone file staged → Claude runs git restore --staged <file> and tells you what to ask a developer to apply instead
    • Quality gate file staged (jest.config.*, vitest.config.*, codecov.yml, .nycrc, pytest.ini, setup.cfg, pyproject.toml, .coveragerc, sonar-project.properties) → Claude reads the diff and checks whether any numeric threshold decreased. If a number dropped (coverage %, error limit, score floor), flag as quality gate weakening: "Claude may have fixed the failing check by lowering the bar, not by fixing the code."
    • --cov-fail-under drop in ANY changed file (including CI YAML, Makefile, tox.ini) → same flag. This catches coverage thresholds living in workflow files rather than pytest config.
  3. Deletion audit — any files being removed?

    • File deleted → flag: "Claude may be wrong that this is unused. Confirm with a developer before this commit."
  4. Branch checkgit branch --show-current

    • On main/master → Claude runs git checkout -b feature/[3-word-slug-from-diff], then re-runs all checks on the new branch
  5. Scope check — staged file list vs. your stated intent

    • Out-of-scope file → Claude runs git restore --staged <file> after your confirmation

Code health checks — Claude runs these on the staged diff (git diff --cached), scanning only added lines (grep "^+"):

  1. Suppression scan@ts-ignore, @ts-nocheck, eslint-disable, @ts-expect-error added

    • Found → flag with line; Claude does not auto-remove (may be intentional), but requires explanation before proceeding
  2. Test bypass scan.skip(, xit(, xdescribe(, x.test( added

    • Found → flag: "Claude bypassed a failing test instead of fixing it"
  3. Debug artifact scanconsole.log, console.error, console.warn, debugger added

    • Found in non-test file → flag: "Debug output left in production code"
  4. Empty catch scancatch\s*\(.*\)\s*\{\s*\} or catch\s*\{\s*\} added

    • Found → flag: "Errors silently swallowed — failures invisible in production"
  5. Lock file driftpackage-lock.json/yarn.lock/pnpm-lock.yaml staged without package.json, or vice versa

    • Found → flag: "Manual lock file edits are almost always wrong"
  6. Binary/large file scangit diff --cached --numstat for lines showing - - <filename> (binary)

    • Found → flag: "Binary files bloat git history permanently and cannot be removed cleanly"
  7. PII scan — added lines matching [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} (email) or \+?[0-9]{10,} (phone)

    • Found → flag: "Real personal data in committed code — check if this is test fixture or real user data"
  8. Internal hostname scan — added lines matching localhost:[0-9]+, 192\.168\., 10\.[0-9]+\., \.internal\b, \.corp\b

    • Found → flag: "Internal infrastructure URL committed — exposes network topology"
  9. New dependency checkgit diff --cached package.json shows new entry in dependencies or devDependencies

    • Found → surface: name, check if it's a known package, flag for license and security review
  10. Force push guard — if Claude ever proposes git push --force or git push -f at any point in the session: route immediately to ALARM mode

  11. XSS sink scandangerouslySetInnerHTML, innerHTML =, document.write( added

    • Found → flag: "Direct XSS attack surface — any user-controlled input reaching this is a vulnerability"
  12. Code injection scaneval( added (JS/Python/Ruby)

    • Found → flag: "Arbitrary code execution risk — almost never the right solution"
  13. SSL bypass scanverify=False, NODE_TLS_REJECT_UNAUTHORIZED, rejectUnauthorized: false, ssl_verify: false added

    • Found → flag: "Claude disabled certificate verification to fix a cert error — this silently removes all TLS protection"
  14. CORS wildcard scanorigin: '*', Access-Control-Allow-Origin: *, cors({ origin: true }) added

    • Found → flag: "API now accepts requests from any domain — check if this is intentional"
  15. .gitignore regression scangit diff --cached .gitignore | grep "^-" for removed entries

    • Lines removed → flag: "Previously ignored files are now tracked — if any are secrets, they'll be committed on the next add"
  16. Timing hack scansetTimeout(, sleep(, time.sleep(, asyncio.sleep( with a hardcoded numeric argument added

    • Found → flag: "Hardcoded delay — Claude may have papered over a race condition rather than fixing the root cause"
  17. Type erasure scan: any or as any added (TypeScript files only)

    • More than 2 occurrences in one diff → flag: "Claude may have escaped the type system to avoid fixing type errors"
  18. Debug mode scanDEBUG = True, debug: true, APP_ENV=development added in non-test config files

    • Found → flag: "Debug mode in a non-test file — check this isn't heading to production"
  19. TODO/FIXME stub scanthrow new Error("TODO"), raise NotImplementedError, // TODO, # FIXME in implementation files (not .md, not test files)

    • Found → flag: "Claude left a placeholder instead of implementing — this will fail at runtime"
  20. Commented-out code scan — added lines matching // <code-keyword> or # <code-keyword> where keyword is const, let, var, function, return, import, export, if, for, while, class, def, async

    • Found → flag: "Claude commented out working code — may indicate uncertainty about the change"
  21. Private key file scangit diff --cached --name-only for *.pem, *.key, *.pfx, *.p12, *.jks, id_rsa, id_ed25519, *.crt, *.cer

    • Found → STOP: "Binary credential file staged — not caught by text grep. Private keys in git history are compromised permanently."
  22. Destructive command scanrm -rf in staged .sh, Makefile, .github/workflows/, or any script file

    • Found → flag: "Claude wrote an aggressive delete command — verify the path is not production data before committing"
  23. String throw scanthrow " or throw ' (not throw new) in JavaScript/TypeScript files

    • Found → flag: "String thrown instead of Error object — stack trace is lost, this bug will be undiagnosable in production"
  24. .env.example drift — added lines matching process.env., os.environ[, getenv(, import.meta.env., ENV[ in non-test files

    • If .env.example (or .env.sample) not also modified → flag: "New environment variable referenced in code but not documented in .env.example — other developers won't know to set it and the app will silently fail"
  25. Migration rollback check — if any staged file is in migrations/, db/migrate/, or matches *_migration.*

    • Claude reads the file and checks for def down, def downgrade, exports.down, down(, rollback(, -- Down
    • Not found → flag: "Migration has no rollback — this schema change cannot be undone automatically"
    • Note: Django migrations auto-generate reversals — skip this check for files in */django/*/migrations/ or containing from django.db import migrations
    • If .vibesafe require_migration_rollback: true → hard stop instead of flag
  26. Test coverage checkgit diff --cached --name-only for new source files (.ts, .js, .tsx, .jsx, .py, .rb, .go, .rs) that are not test files and not config/markdown

    • Check if any test files (*.test.*, *_test.*, *_spec.*, files in tests/ or __tests__/) also changed
    • New functions/classes added (grep ^+.*function |^+.*class |^+.*def |^+.*=>) but zero test files staged → flag: "New code with no test changes — Claude may not have written tests for this"
    • If .vibesafe require_tests: true → hard stop instead of flag
    • Exempt: commits where all staged files are non-source (.md, .txt, .yaml, .json, images, .css, .html)
  27. Developer contract enforcement — read .vibesafe for contract keys:

    • max_changed_files: N — count staged source files; if exceeded: warn, surface SCOPE mode, list the unexpected files
    • block_pattern: WORD — grep added lines (excluding test files and .md) for each pattern; hard stop if found: "Developer contract: WORD not allowed in committed code"
    • require_tests, require_migration_rollback — enforced in checks 31 and 30 above
    • require_reviewer: @handle — stored; added to PR description in PR mode
  28. Insecure random in security fileMath.random() added in files named auth, token, session, jwt, oauth, login, credential, password

    • Found → STOP: "Math.random() is not cryptographically secure — use crypto.getRandomValues() or crypto.randomUUID() for auth/session tokens"
  29. SQL string interpolation — added lines matching "SELECT" + , f"SELECT, `SELECT ${, "INSERT" +, f"INSERT, "UPDATE" +, f"UPDATE

    • Found → STOP: "SQL built by string concatenation is vulnerable to injection — use parameterized queries or a query builder"
  30. Shell injection via subprocessshell: true (JS/TS) or shell=True (Python) added in subprocess/exec calls

    • Found → STOP: "shell=True passes the command through the shell — unsanitized user input reaching this is command injection"
  31. Missing await on async call — added lines matching = db., = await expected but await absent, or assignment from a known async pattern (.findOne(, .query(, .fetch(, .get(, .post() without await

    • Found → flag: "Result is likely a Promise object, not the actual value — add await or the call is silently a no-op"
  32. Test block with no assertionsit( / test( / describe( block added with no expect(, assert, should, toBe, toEqual, toHaveBeenCalled inside

    • Found → flag: "Test added with no assertions — it will always pass regardless of what the code does"
  33. gitleaks scan (if installed) — run gitleaks detect --source . --no-git on staged files

    • Not installed → silently skip (grep-based credential scan still runs)
    • Installed + finds credential → STOP: "gitleaks detected a high-entropy secret — rotate immediately, even if it looks like a placeholder"
  34. semgrep scan (if installed) — run semgrep --config=p/owasp-top-ten --quiet on staged files

    • Not installed → silently skip
    • Installed + finds match → STOP: "semgrep flagged an OWASP rule match — [rule id and file:line cited]"
  35. npm audit (if installed and package.json staged or present) — run npm audit --audit-level=high --json

    • Not installed → silently skip
    • Installed + high/critical CVE found → STOP: "npm audit found [N] high/critical vulnerabilities — review before committing"
  36. API key in client-side file — credential patterns (sk-*, pk_live_, AKIA*, api_key=) in files under src/, frontend/, public/, static/, client/, components/, pages/, app/

    • Found → STOP: "Credential in client-side file — visible to anyone who opens DevTools. Move to a server-side environment variable or backend proxy."
  37. Sensitive data in log statementsconsole.log(, logger.info(, print(, logging.debug( on lines that also reference password, secret, token, api_key, credential, auth

    • Found → flag: "Possible sensitive data in log output — scrub before production"
  38. Security header regressionhelmet import removed from a server file, or explicit insecure header config added (Content-Security-Policy: unsafe-eval, X-Frame-Options: ALLOW-FROM, Strict-Transport-Security: max-age=0)

    • Found → flag: "Security middleware removed or insecure header configured"
  39. New route without rate limitingapp.get(, app.post(, @app.route(, @router.get( added in a file with no rateLimit, throttle, slowapi, limiter, RateLimit reference anywhere in the file

    • Found → flag: "New endpoint has no rate limiting — add before exposing to traffic"

When all checks pass: Claude generates the commit message from the diff + your one-line description. You confirm or edit, then Claude runs git commit -m "..." for you.


Mode: PR

Before opening a pull request.

Claude reads git diff main...HEAD and asks one question: "Why are you making this change?"

Then generates a complete PR description:

  • What changed — derived from the diff, plain English
  • Why — your answer
  • What to test — derived from file types and risk level
  • Flagged uncertainties — unresolved review flags surface here
  • Who should review — from git log of changed files + require_reviewer from .vibesafe

Also checks: are you targeting the right base branch? Is your branch up to date?

PR safety artifact — appended to the PR description body so the reviewer sees what was verified:

---
🛡️ vibe-safe — checked [date]

| Check | Result |
|-------|--------|
| Credential scan | ✅ Clean |
| 36 commit checks | ✅ Passed |
| Scope audit | ✅ 4 files, all in scope |
| Test coverage | ⚠️ 2 new functions, no test changes (acknowledged) |
| .env.example drift | ✅ No new env vars |
| Migration rollback | ✅ N/A |
| Developer contracts | ✅ All team rules met |

[vibe-safe](https://github.com/googlarz/vibe-safe)

Any warning acknowledged during COMMIT shows as ⚠️ with "(acknowledged)" so the reviewer knows it was seen, not missed.


Mode: SCOPE

Run when you think Claude may have changed more than you asked for, or when max_changed_files fires.

Claude asks: "What was the original task in one sentence?"

Then runs git diff --name-only HEAD (or git diff --name-only main...HEAD if commits ahead) and evaluates each changed file:

Output:

  • IN SCOPE — directly related to the stated task
  • 🔍 LIKELY NEEDED — indirectly required (shared config, auto-updated lock files, framework boilerplate)
  • ⚠️ SUSPICIOUS — changed, but the connection to the stated task is unclear
  • 🚨 OUT OF SCOPE — clearly unrelated; Claude wandered

For each suspicious or out-of-scope file: Claude explains what changed in plain English and asks "keep it or remove it?" For removes, Claude runs git restore <file>.

.vibesafe max_changed_files sets the soft ceiling that triggers automatic SCOPE routing.


Mode: CONFLICT

When merge conflict markers appear.

Claude reads <<<<<<< / ======= / >>>>>>> and tells you:

  • What the current version does (plain English)
  • What your change does (plain English)
  • What specifically is lost if you accept either side

Never recommends "accept all theirs/ours." If the conflict is in a Danger Zone file: STOP — CALL A DEVELOPER.


Mode: ALARM

When Claude proposes something that feels big, irreversible, or outside scope.

Claude assesses three things:

  • Reversible? Can this be undone with a single git revert?
  • Shared? Does it affect infrastructure other teams depend on?
  • Expected review? Would a developer expect to approve this first?

Output: GO AHEAD / PAUSE AND CHECK / STOP — CALL A DEVELOPER

Auto-escalate to ALARM — no questions needed:

  • Claude proposes git push --force or git push -f for any reason
  • Claude proposes git commit --no-verify for any reason — this skips all 21 hook checks
  • Claude proposes running a direct database command (psql, rails db:, knex migrate)
  • Claude proposes modifying .github/workflows/ to skip a failing check

Mode: HISTORY

Scans recent git history for past mistakes that need active remediation — not just the current state.

Claude runs:

  • git log -S "sk-|pk_|ghp_|AKIA|api_key|secret|password" --oneline -50 — finds commits that added or removed credential patterns. A key "deleted" in commit B is still readable in commit A.
  • git log --grep="fix\|remove\|revert\|oops\|secret\|key\|credential\|token" --oneline -20 — finds commits whose messages hint at past cleanup attempts
  • git log --diff-filter=D --name-only --format="" -30 — finds files deleted in recent commits (possible sensitive data removal)

For each flagged commit, Claude distinguishes:

  • Active exposure — credential still present in HEAD (STOP: rotate immediately)
  • Historical exposure — removed in a later commit but still readable in history (requires key rotation + git filter-repo to scrub, plus force-push and re-clone for all team members)
  • Likely clean — no pattern found

Historical exposure output includes exact remediation steps: rotate the key, run git filter-repo, force-push, notify anyone who cloned.


Mode: EXPLAIN

When a vibe-safe flag fires and you don't understand what it means or what to do about it.

Invoke: vibe-safe explain <topic> — e.g., vibe-safe explain credential, vibe-safe explain ssl, vibe-safe explain ts-ignore, vibe-safe explain empty catch

Claude provides:

  • What it is — plain English, no jargon
  • Why it matters — concrete worst-case scenario in non-technical terms
  • What it looks like when it goes wrong — a real example
  • The proper fix — not just "don't do this" but what Claude should have done instead
  • One sentence for your developer — how to communicate the risk without needing to explain the full technical detail yourself

Mode: REVIEWER

For developers reviewing a PR opened by a non-technical contributor using Claude.

When to use: You're reviewing a PR where the author isn't a developer. You want to understand what they intended vs. what Claude actually changed — and what needs your attention — without reading the full diff yourself.

Run:

vibe-safe reviewer

What Claude does:

  1. Read the diffgit diff main...HEAD (full diff, not just staged)
  2. Infer intent — read commit messages, branch name, PR description if available. Ask "what was this person trying to accomplish?" If intent is ambiguous, ask the reviewer: "What did the contributor say they were changing?"
  3. Map intent vs. reality — classify every changed file:
    • IN SCOPE — directly implements the stated intent
    • 🔶 LIKELY NEEDED — supporting change that intent probably required (e.g., types update alongside component change)
    • ⚠️ SUSPICIOUS — changed but not obviously connected to intent — flag for explanation
    • OUT OF SCOPE — unrelated to stated intent, no plausible connection
  4. Run vibe-safe checks — mentally apply all COMMIT mode checks to the diff. Report any that fire.
  5. Identify test gaps — which changed source files have no test changes? Which have no test files at all in the repo?
  6. Generate questions — specific, answerable questions the developer should ask or verify before approving.

Output format:

## vibe-safe REVIEWER report

**Intent (inferred):** [what the contributor was trying to do]
**Branch:** [branch name]   **Files changed:** [N]   **Commits:** [N]

### File verdict
| File | Verdict | Reason |
|------|---------|--------|
| src/components/Hero.tsx | ✅ IN SCOPE | hero text updated |
| src/styles/global.css | 🔶 LIKELY NEEDED | font-size change adjacent to text edit |
| src/api/analytics.ts | ⚠️ SUSPICIOUS | tracking call added — not mentioned in intent |

### vibe-safe checks
[List any patterns that fired, same format as COMMIT mode. If none: "No safety flags."]

### Test coverage
- [file]: no test changes alongside source change
- [file]: no test file exists in repo — consider creating one

### Questions to ask / verify before approving
1. [specific question about a suspicious file or pattern]
2. [specific question about intent vs. implementation mismatch]
3. [specific question about test coverage if critical path]

### Verdict
**APPROVE** / **REQUEST CHANGES** / **DISCUSS WITH CONTRIBUTOR**

[1-2 sentence summary of what the reviewer should do next]

Rules for verdict:

  • Any hard-block pattern (credential, XSS, SSL bypass, SQL injection, etc.) → REQUEST CHANGES immediately, don't soft-pedal it
  • OUT OF SCOPE files with no explanation → DISCUSS WITH CONTRIBUTOR — Claude may have gone further than asked
  • Only IN SCOPE / LIKELY NEEDED files, no flags → APPROVE (with any test gap notes)
  • Suspicious files with plausible explanation → APPROVE with comment

What REVIEWER mode is NOT:

  • Not a substitute for understanding the business logic
  • Not a guarantee the code is correct — only that it doesn't have obvious safety failures and matches stated intent
  • Not for reviewing developer-written PRs — use a code review tool for that

What vibe-safe can't catch — and how to handle it

Three categories of risk that grep cannot cover. When they come up, say what the limitation is and give the concrete next step — don't just say "talk to a developer."


API response data exposure

Why vibe-safe can't catch it: Whether a response leaks sensitive fields depends on what the serializer/schema includes — and that requires understanding the data model, not just reading added lines.

What to do:

  1. Use allowlist serializers, not raw model returns. The pattern return await db.user.findFirst(...) directly in a route handler is the root cause. The fix is always a schema/serializer that explicitly names the fields to expose:

    • FastAPI: response_model=UserPublic on the route decorator
    • Django REST: a serializer_class that explicitly lists fields
    • Rails: render json: user.as_json(only: [:id, :name]) not render json: user
    • Prisma/Node: select: { id: true, name: true } not returning the full object
  2. Audit new route handlers before merge. When a new endpoint is added, the developer reviewing it should ask: "does the response go through a schema that explicitly allowlists fields, or is it returning a raw DB object?" This is what REVIEWER mode surfaces.

  3. Runtime scan before launch. Tools: OWASP ZAP (free), Burp Suite, or a simple curl against your staging API checking that sensitive fields (password_hash, internal_id, stripe_customer_id, ssn, dob) don't appear in responses. Run this as part of your staging checklist, not just pre-commit.

  4. Add a test that asserts response shape. The most durable protection is a test that explicitly asserts which fields appear in the response and fails if new ones are added without review.


Privacy policy

Why vibe-safe can't catch it: Whether you need a privacy policy depends on what data you collect, where your users are, and which regulations apply — none of which are in the code.

What to do:

  1. The trigger is collecting personal data, not shipping code. If any of these exist in your models or API payloads — email, name, phone, ip_address, location, user_agent, device_id, date_of_birth, behavioral tracking — you are collecting personal data and need a policy before you expose this to real users.

  2. Jurisdiction determines the requirement:

    • EU/EEA users → GDPR applies. You need a privacy policy, a lawful basis for processing, and data subject rights (access, deletion, portability).
    • California users → CCPA applies if you meet the thresholds (>$25M revenue, >50k users, or >50% revenue from selling data).
    • UK → UK GDPR (same substance as EU GDPR post-Brexit).
    • When in doubt: write the policy anyway. The cost of writing one is far less than the cost of not having one when you need it.
  3. Fast path for early-stage products: iubenda, Termly, or GetTerms.io generate compliant policies for ~$10/month. Use one of these. Do not write your own from scratch or copy one from another site.

  4. What a developer can do now: Before adding any new PII field to a model, answer: "what's the purpose, retention period, and who has access?" Document it. This becomes the basis for the privacy policy and also forces the right design conversation.


Data storage architecture

Why vibe-safe can't catch it: Where data ends up, who can access it, and whether it's encrypted at rest requires understanding infrastructure — not reading diffs.

What to do:

  1. Map it before you build it. Before adding a new data entity, answer four questions:

    • Where is it stored? (Postgres table, S3 bucket, Redis cache, CloudWatch log, third-party API)
    • Who can read it? (application service role, DBA, DevOps, third-party vendor)
    • How long is it kept? (forever, 90 days, deleted on account close)
    • Is it encrypted at rest? (database-level, field-level for PII, or not at all) If you can't answer these, the feature isn't ready to build.
  2. Logs are a data store. Whatever goes into console.log, logger.info, or your observability platform (Datadog, Sentry, CloudWatch) is stored, queryable, and often retained for months. Treat log destinations as data stores with the same access controls.

  3. Third-party SDKs are data stores. When you add Segment, Mixpanel, Intercom, Sentry, FullStory, or similar: you are sending user data to their infrastructure. Check what each SDK collects by default, whether it can be configured to exclude PII, and whether their DPA (Data Processing Agreement) covers your jurisdiction.

  4. The one question to ask before any new integration: "Does this send user data off our infrastructure, and if so, what data, to whom, and under what terms?" This question should be answered before the SDK is added to package.json, not after it's in production.


STOP — CALL A DEVELOPER (non-negotiable)

  • Any file in migrations/, schema/, db/ is in the diff
  • Any CI/CD config (.github/workflows/, Jenkinsfile, .circleci/)
  • Any environment config (.env, docker-compose.*, nginx.conf, *.conf)
  • Any auth-related file (auth, login, session, jwt, oauth, permissions)
  • Branch is main or master
  • Claude proposed running a database command
  • Credentials found anywhere in modified files

PAUSE AND CHECK

  • Diff is larger than you expected
  • Files outside your intended scope appear
  • A file you've never seen is being modified
  • Claude said "this should be safe" but you don't understand why

GO AHEAD

  • Only your intended files in the diff
  • No credential flags in any modified file
  • You're on a feature branch
  • A developer has seen the plan

Danger Zones

Files that need developer involvement regardless of change size:

CategoryPatterns
Environment.env, .env.*
Infrastructurenginx.conf, *.conf, Dockerfile, docker-compose.*
CI/CD.github/workflows/, Jenkinsfile, .circleci/, .gitlab-ci.yml
Databasemigrations/, schema/, *.sql
Authfiles named: auth, login, session, jwt, oauth, permission, role
Buildwebpack.config.*, vite.config.*, tsconfig.json
Quality gatesjest.config.*, vitest.config.*, codecov.yml, .nycrc, .eslintrc.*, .stylelintrc.*, sonar-project.properties
Dependenciespackage.json, Gemfile, requirements.txt (version changes)

Common Rationalizations — Red Flags

ThoughtWhat it actually means
"Claude said it was fine"Claude doesn't know your codebase's undocumented dependencies
"It's not in my diff"Credentials can exist in files you didn't intend to change
"It's just a small change"File type matters more than size — one line in nginx.conf hits production
"I'll fix it in the next commit"Committed credentials are compromised permanently, even if deleted
"I accepted 'theirs' everywhere"You may have silently reverted your own work
"The tests still pass"Tests don't cover what they don't test
"It's just a config tweak"Config files control production for everyone on the team
"I've done this before"Past safety was luck, not process
"Claude made the failing checks pass"Check what it changed to make them pass — lowering a threshold is not fixing a bug
"It's just a ts-ignore, it'll be fine"Suppressed errors accumulate into unfixable technical debt
"I'll remove the console.log later"Later doesn't happen; debug logs leak data in production
"The test was probably out of date anyway"Claude skipped it because it was failing, not because it was wrong
"It's just push --force on my branch"If anyone else pulled that branch, their work is gone
"verify=False is just for testing"It was in the diff — check if it's going to production
"The any type is fine for now"Type debt compounds; Claude added it to avoid fixing the real error
"The sleep just makes it more reliable"Timing hacks hide bugs and make tests slow and flaky
"I'll implement that part later"Claude shipped a placeholder — it will throw at runtime
"That code was probably dead anyway"Commented-out code means Claude wasn't sure — don't ship uncertainty
"The rm -rf is scoped to a temp dir"Verify that before it runs in CI against a production path
"throw is throw, same thing"String throws lose the stack trace — production bugs become invisible
"The hook keeps failing so I'll use --no-verify"Skipping the hook skips all checks — find out why it's failing instead
"It's fine, I checked git history and deleted it"Deleted ≠ gone — run HISTORY mode to confirm and get remediation steps
"Claude only changed 3 files, it's fine"File count isn't scope — run SCOPE mode to verify intent matches diff
"The migration is simple, no rollback needed"Every irreversible migration needs a down path — that's the whole point
"I'll add tests later"Later doesn't happen; require_tests exists so your developer doesn't have to say this in code review
"It uses the env var everywhere already"New references still need .env.example entries — the next dev spinning up won't know to set it

Mode: VERIFY

Invoke after any vibe-safe session to confirm the session is clean.

Claude runs:

  • git branch --show-current — confirms not on main/master
  • git diff main...HEAD --name-only — lists every file that will be in the PR
  • git grep -nE "sk-|pk_|ghp_|AKIA|api_key[[:space:]]*=|secret[[:space:]]*=|password[[:space:]]*=|Bearer |token[[:space:]]*=" — final credential sweep
  • git log --oneline -3 — confirms commit messages are descriptive

Output: CLEAN or remaining flags with specific file:line evidence.

What ships with it: 6 files

89.8 KB alongside SKILL.md, 2 of them executable

hooks/

Keep looking

Skills are one crate of 326,790. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.