From hypervibe
Adds a Neon Postgres database with Drizzle ORM to a T3 project. Provisions the database, configures ORM, pushes schema, and handles monorepo setup.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hypervibe:add-dbThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Detect the user's language from their messages and ALWAYS reply in that language (default: English). This applies to every user-facing message: questions, progress, confirmations, summaries, errors.
Adds a Neon Postgres database with Drizzle ORM to the current project. Can be called by /bootstrap or standalone on an existing project.
The deterministic core (provisioning, driver swap, schema push, env var push) is handled by scripts/setup-db.mjs. This SKILL takes care of the entry-side decisions (re-config detection, monorepo case, MCP availability) and the exit-side communication (CLAUDE.md update, summary).
This skill reads the Neon key from the vault, so first make sure the vault is unlocked (follow _ensure-vault): node "${CLAUDE_SKILL_DIR}/../../scripts/vault/vault.mjs" status then, if locked/expired, run node "${CLAUDE_SKILL_DIR}/../../scripts/vault/launch.mjs" unlock (window, once a day); if the vault does not exist yet, delegate to _add-keyring.
First of all, invoke _check-deps db to detect whether a real cloud DB is already wired up:
result=$(node "${CLAUDE_SKILL_DIR}/../../scripts/check-deps.mjs" db)
db_ok=$(echo "$result" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).db.ok)")
db_host=$(echo "$result" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).db.host || '')")
db_ok = true then re-configuration modeA real cloud DB is already wired up (host: $db_host). Do NOT run setup-db.mjs (it would create a new Neon project, polluting the free tier). Do NOT rewrite drizzle.config.ts or the schema. Show a menu:
🗄️ A database is already in place (host:
$db_host)What do you want to do?
- Just push the schema to the DB (if you changed the Drizzle schema but haven't pushed it yet) - equivalent to
pnpm db:push- Migrate to a new Neon DB (e.g. change region, start from a clean project) - ⚠️ destructive: all current data is lost. I'll guide you through creating the new project + switching the
DATABASE_URL- Reset the current schema (drop + recreate all tables) - ⚠️ destructive, the data in the current DB is lost
- Redo everything from scratch (useful only if your Drizzle config is broken - first remove
DATABASE_URLfrom the local.env)- Something else - tell me what you want
Wait for the answer.
Depending on the answer:
| Choice | Action |
|---|---|
| 1 (push schema only) | cd <WEB_DIR> && pnpm db:push (or pnpm drizzle-kit push). Show the result. Skip to the final summary. |
| 2 (migrate to a new DB) | Confirm with the user "do you confirm losing the current data?" then re-run setup-db.mjs --name <project-name> (it provisions a new Neon project and pushes the DATABASE_URL - it will overwrite the old one in .env and on Vercel). Mention that the old Neon project stays on the account (the user can delete it manually in dashboard.neon.tech if they want to free up a slot). |
| 3 (reset schema) | Confirm with the user then try cd <WEB_DIR> && npx drizzle-kit drop (depending on the Drizzle version). If not available, list the existing tables via psql or the Neon console, then DROP each one via SQL, then pnpm db:push. |
| 4 (redo everything) | Abort: ask the user to remove DATABASE_URL from the .env, then re-run /add-db. |
| 5 (something else) | Ask for details. Don't run the full flow by default. |
At the end, jump straight to the final summary (Step 8 below).
db_ok = false (not configured yet)Continue normally to Step 1.
Invoke the _detect-project-root internal skill to get PROJECT_NAME, WEB_DIR, IS_MONOREPO, and IS_NEXTJS.
IS_NEXTJS=no then abort. This skill requires a Next.js project.IS_MONOREPO=yes then do not run the script (it refuses --monorepo in v1). Go straight to Step 2 (manual monorepo mode).IS_MONOREPO=no then continue to Step 3 or Step 4 depending on the Neon access mode.Everything goes through the Neon REST API (console.neon.tech/api/v2) with the NEON.api_key from the vault. setup-db.mjs enforces region_id: "aws-eu-central-1" (Frankfurt, next to Vercel fra1).
Check that the Neon key is in the vault (_get-secret pattern):
VAULT="${CLAUDE_SKILL_DIR}/../../scripts/vault/vault.mjs"
node "$VAULT" get NEON api_key >/dev/null 2>&1; RC=$?
RC=0 then the key is present, go to Step 4 (REST provisioning via setup-db.mjs, which reads the key from the vault itself).RC=2/3 (vault locked/expired) then warn the user, node "${CLAUDE_SKILL_DIR}/../../scripts/vault/launch.mjs" unlock (blocking), retry.RC=4 (key missing) then have it created + stored in the vault:
To create your database, I need a Neon key (just once - I'll store it in your vault).
- Go to https://console.neon.tech/app/settings/api-keys then Create new API key then copy it.
- A window will open: paste it in (masked input).
node "${CLAUDE_SKILL_DIR}/../../scripts/vault/launch.mjs" add --name NEON --service Neon --fields "api_key:secret"
Then retry the get then Step 4.(Legacy: setup-db.mjs still accepts a NEON_API_KEY env var as a fallback during the migration, but the vault is the source of truth.)
The setup-db.mjs script does not yet handle the monorepo case (which requires creating a shared packages/db/ package, moving the schema/client/config). Proceed manually:
Create the shared packages/db package:
mkdir -p packages/db/src
Create packages/db/package.json:
{
"name": "@<project-name>/db",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts"
}
Install the driver in the package:
cd packages/db
pnpm add @neondatabase/serverless drizzle-orm
pnpm add -D drizzle-kit
Move the schema, the client, and the Drizzle config into packages/db/src/. All apps (apps/web, apps/worker) must import from @<project-name>/db.
Update apps/web (and other apps) to import the DB from the shared package instead of the local files.
Provision the Neon project manually via the REST API (the Neon key comes from the vault):
NEON_KEY=$(node "${CLAUDE_SKILL_DIR}/../../scripts/vault/vault.mjs" get NEON api_key)
curl -X POST "https://console.neon.tech/api/v2/projects" \
-H "Authorization: Bearer $NEON_KEY" \
-H "Content-Type: application/json" \
-d '{"project":{"name":"<project-name>"}}'
Get the pooled connection_uri from the response.
Push DATABASE_URL=<connection-uri> to the monorepo root via _push-env-vars.
cd packages/db && npx drizzle-kit push.
Jump straight to Step 5 (Update CLAUDE.md), passing IS_MONOREPO=yes to _update-claude-md.
We always provision via the REST API (setup-db.mjs, Step 4), which guarantees the Frankfurt region and reads the Neon key from the vault. The monorepo case (Step 2) stays manual but also uses the REST API.
Run the script from the WEB_DIR:
node "${CLAUDE_SKILL_DIR}/../../scripts/setup-db.mjs" \
--name "<PROJECT_NAME>" \
--web-dir "<WEB_DIR>"
The script chains 7 sub-steps: preflight, list projects (with a warning if quota), create Neon project, install driver, swap the Drizzle client to neon-http, push schema, push env vars.
The script displays in real time:
▸ <step> when it starts each sub-step✅ <result> at the end of each one⚠️ <warning> for non-blocking warnings (Neon quota near limit, name conflict){"success":true,"projectId":"...","host":"...","projectName":"..."}Let the output stream through live (no > /tmp/..., no capture). The user wants to see the progress.
Mark the step ✅, capture projectId and host from the final JSON, and go to Step 5.
If the banner contains warnings (e.g. NEON_QUOTA_NEAR_LIMIT, NEON_PROJECT_NAME_CONFLICT), mention them in the final summary but don't block - the project is provisioned.
❌ Failed at: <step>). The name maps 1:1 onto a function in the script - open setup-db.mjs and read the function to understand.preflight failed then usually a missing NEON_API_KEY or no Next.js / no Drizzle in the project then go back to Step 1.listProjects or createProject failed then a Neon API error. Often a quota exceeded (show the error message as-is to the user) or an expired API key.installDriver failed then a pnpm error (network, registry). Retry by hand: cd <WEB_DIR> && pnpm add @neondatabase/serverless.swapDriver failed then T3 may have moved src/server/db/index.ts. Patch the file manually, taking inspiration from the template in setup-db.mjs step swapDriver.pushSchema failed then the schema probably has a problem (table already exists with another prefix, migration conflict). Read the drizzle-kit error, fix the schema, and retry: cd <WEB_DIR> && DATABASE_URL='<connection-uri>' npx drizzle-kit push.pushEnvVars failed then the Neon project is provisioned + schema pushed, but the env var is not in .env/Vercel. Get the connection URI from the script state (visible in the logs) and invoke _push-env-vars DATABASE_URL=<uri> manually.Invoke _update-claude-md with:
stack: - **Database**: Neon PostgreSQLcommands:
- \pnpm db:push` - Push schema to Neon`- \pnpm db:studio` - Open Drizzle Studio`env-vars: - \DATABASE_URL` - Neon PostgreSQL connection string`conventions:
- Data: Optimistic UI - the interface updates reactively right away, the database syncs in the background. Never block the UI waiting for the server response.IS_MONOREPO=yes, also add: - DB: import from \@<PROJECT_NAME>/db`, never a relative cross-app path.`The helper is idempotent - re-running /add-db won't duplicate existing lines.
🚨 This step is mandatory. You absolutely must invoke add-backup-db here, even when /add-db is called from /bootstrap or another skill. Skipping this step = serious bug: the user loses their data if the DB crashes, without knowing it. An internal audit on 2026-05-16 showed that ~36% of bootstraps forgot this step before we made it explicit - it's the main source of potential data loss in Hypervibe.
The add-backup-db skill is idempotent (no-op if the project is already registered) - so there's no reason to skip "just to be safe".
Invoke skill: add-backup-db
With args: --quiet
Capture the status returned by add-backup-db (the caller, i.e. you, must decide what to put in the final summary based on this status):
| Returned status | What to do in the summary |
|---|---|
ok:created (worker created for the first time + project registered) | Add the line "✅ Automatic backups enabled" + details (see Step 8) |
ok:added (worker already existed, project added to the list) | Add the line "✅ Automatic backups enabled" + details |
ok:already-registered (project already in the list, no-op) | Add the line "✅ Automatic backups already active for this project" |
skipped:no-neon-key (Neon API key missing, quiet mode then couldn't prompt) | Add the line "⚠️ Automatic backups not enabled: your Neon API key is missing. Run /start to configure it, then /add-backup-db to enable backups on this project." |
skipped:cloudflare-missing (no CF token) | Add the line "⚠️ Automatic backups not enabled: Cloudflare is not configured on your machine. Run /start then /add-backup-db when you're ready." |
error:* | Add the line "⚠️ Automatic backups not enabled (technical error). You can retry with /add-backup-db." + do not block the overall summary |
NEVER block the /add-db flow on a backup-activation failure. The DB is in place, that's the main topic - the step must have run, but its result (success or failure) must not crash the skill.
🛑 Before moving to Step 8, programmatically verify that step 6 actually took effect. This is the safety net that catches cases where the add-backup-db invocation was forgotten, executed wrong, or silently failed.
# Get the Neon project ID of the current project (look it up via the Neon API by matching the project's DATABASE_URL)
PROJECT_ID="<neon-project-id-of-the-project>"
# Check that this project ID appears in the shared clock's registry (unified
# hypervibe-jobs worker), with a legacy fallback for machines not yet migrated
# from the old standalone db-backup worker.
if [ -f ~/.hypervibe-jobs/jobs.js ] && grep -q "\"$PROJECT_ID\"" ~/.hypervibe-jobs/jobs.js; then
echo "OK:backup-registered"
elif [ -f ~/.db-backup-worker/wrangler.toml ] && grep -q "\"$PROJECT_ID\"" ~/.db-backup-worker/wrangler.toml; then
echo "OK:backup-registered-legacy"
elif [ -f ~/.hypervibe-jobs/jobs.js ] || [ -f ~/.db-backup-worker/wrangler.toml ]; then
echo "FAIL:backup-not-registered"
else
echo "FAIL:worker-config-missing"
fi
Interpretation:
OK:backup-registered then all good, go to Step 8.OK:backup-registered-legacy then the backup runs on the old standalone worker: fine for now, add-backup-db proposes the migration to the unified worker next time it runs interactively.FAIL:backup-not-registered then step 6 failed to register the project. Re-invoke add-backup-db --quiet one more time. If it fails again, capture the returned status (Step 6 table above) and add the matching warning to the final summary.FAIL:worker-config-missing then either the project is the very first one on this machine (in which case add-backup-db should have provisioned the shared worker), or the step 6 status was skipped:cloudflare-missing or error:*. The warning in the summary is enough, don't retry in a loop.The goal: we accept that there are cases where the backup can't be enabled (no Neon key, no Cloudflare token), but we don't accept forgetting to enable it when all the ingredients are there.
Add Neon to the project's RGPD subprocessor registry:
node "${CLAUDE_SKILL_DIR}/../../scripts/update-privacy-policy.mjs" --add neon
The helper is idempotent. If the politique-de-confidentialite/page.tsx page exists (created by /bootstrap), it updates automatically from the registry. If the page doesn't exist (pre-bootstrap project), only the registry is created - /rgpd-audit can generate the page retroactively.
Tell the user:
<project-name> is provisioned and connected (host: <host>)DATABASE_URL is in the local .env and on Vercel (production / preview / development)pnpm db:push to push schema changes, pnpm db:studio to browse the dataIf any warnings were raised by the script (NEON_QUOTA_NEAR_LIMIT, NEON_PROJECT_NAME_CONFLICT, etc.), mention them here.
npx claudepluginhub flavien-ia/hypervibe-harness --plugin hypervibeAdds automated Neon database backups to the current project using a shared Cloudflare Worker. Handles registration, snapshot scheduling, and retention policy.
Provisions Neon PostgreSQL database and sets up Drizzle ORM with dependencies, credentials, schemas, and migrations for immediate database connectivity and querying.
Guides and best practices for Neon Serverless Postgres: setup, connections, branching, autoscaling, scale-to-zero, read replicas, connection pooling, Neon Auth, CLI, MCP server, REST API, TypeScript SDK, and Python SDK.