Implements all 4 stages from SENTRY_AS_PRODUCT.md:
Stage 1 — Auto-provision per-project Sentry:
- New module lib/integrations/sentry.ts with idempotent
ensureSentryProject(): creates Sentry project under shared
vibnai org, fetches DSN, persists to fs_projects.data.sentry.
- Wired into POST /api/projects/create (provision early so DSN is
ready before first deploy) and into applyEnvsAndDeploy in MCP
(lazy retry + env var injection on every apps.create).
- applySentryEnvToCoolifyApp upserts NEXT_PUBLIC_SENTRY_DSN +
SENTRY_AUTH_TOKEN onto the Coolify app, so the very first build
inlines the DSN into the client bundle and uploads source maps.
Stage 2 — Bake into scaffolds:
- New module lib/scaffold/sentry-snippets.ts exposes canonical
Next.js + Vite+React snippets the AI copies verbatim (keeps
outputs deterministic across chats).
- AI system prompt updated: explicit instructions to wire Sentry
on every new app, env vars are guaranteed available, project
Sentry slug comes from projects_get.
- projects.get MCP response now includes `sentry: {slug, dsn,
provisionedAt}` so the AI can substitute the slug into
withSentryConfig({ project: <slug> }).
Stage 3 — Expose error feed to the AI:
- Three new MCP tools registered:
project_recent_errors — list unresolved issues
project_error_detail — stack trace + breadcrumbs + replay url
project_error_resolve — mark resolved after a verified fix
- Tenant-safe: each tool re-checks projectId belongs to caller's
workspace before talking to Sentry.
Stage 4 — Auto-surface at chat-turn start:
- chat/route.ts pulls listRecentSentryIssues for the active
project (last 6h, count ≥ 2 to skip noise) and appends a
[PROJECT HEALTH] block to the system prompt. AI decides
whether to surface a one-liner; if user's message is about a
broken thing, AI prefers Sentry stack trace over guessing.
End state: a Vibn user's deployed app crashes for a real user →
Sentry captures with source-mapped stack trace + Session Replay →
next AI chat turn the AI knows about it and can offer a fix
without the user pasting the error.
Co-authored-by: Cursor <cursoragent@cursor.com>
User can now click "Connect GitHub" inside the Import-existing-code
flow, sign in via GitHub, and pick a repo from a searchable list of
their own + collaborator + org repos. Both public and private repos
work — the encrypted access token on the user's account is auto-
attached when the create endpoint runs the agent-runner mirror.
OAuth flow:
- GET /api/integrations/github/connect — generates state, sets
a 10-min httpOnly cookie, 302s to GitHub authorize.
- GET /api/integrations/github/callback — verifies state,
exchanges code for token, fetches /user, encrypts the
token with secret-box (AES-256-GCM, VIBN_SECRETS_KEY) and
persists it on fs_users.data.integrations.github.
Bounces back to ?gh_connected=login or ?gh_error=msg.
- GET /api/integrations/github/repos — server-side fetches
the connected user's repos (per_page=100, sort=pushed,
affiliation=owner+collaborator+org_member). Returns the
GitHub login + a stripped repo summary; never the token.
- POST /api/integrations/github/disconnect — drops the integration
from fs_users (does NOT revoke on github.com).
Scopes requested: repo, read:user.
Token storage:
- Encrypted at rest with secret-box (lib/auth/secret-box.ts) using
VIBN_SECRETS_KEY. Tokens never leave the server.
- One token per fs_users row, keyed by email.
ImportSetup UI:
- On mount, fires /repos to detect connection state.
- If connected: shows a connected-as-@login chip with disconnect
link, a search-as-you-type repo picker (max 220px scroll, badges
for Private / language), and a "paste a different URL instead"
escape hatch.
- If not connected: shows a Connect GitHub card with a public-URL
fallback inline.
- On return from OAuth (?gh_connected=… or ?gh_error=…), surfaces
a toast and silently refreshes the repo list.
- Selected repo carries default_branch + repo id into the create
payload so we can store them on the project for later UI hints.
/api/projects/create:
- When a githubRepoUrl is mirrored, falls back to the user's
OAuth-linked token if no PAT is explicitly passed. Means the
flow "just works" for private repos once GitHub is connected.
Required env (already set in production):
- GITHUB_CLIENT_ID
- GITHUB_CLIENT_SECRET
Made-with: Cursor