feat: multi-tab chat + repo integrations + V tono consistente + Clerk webhook - #72
feat: multi-tab chat + repo integrations + V tono consistente + Clerk webhook#72turbillon50 wants to merge 3 commits into
Conversation
…nsistente + Clerk webhook 4 cambios pedidos por Luis, todos en este push: ## 1. V personalidad consistente cross-scope (Fase 2) Agregada instrucción ABSOLUTA en buildSystemPrompt explicando que el scope (general vs proyecto) cambia QUÉ contexto carga, NUNCA CÓMO habla. Luis es el operador único: cercano, directo, sin formalidades. Si V se pone profesional en un chat y casual en otro, es BUG. lib/forge/system-prompt.ts:202-216 ## 2. /app/repovision: repos + integraciones (Fase 3) Nuevo endpoint GET /api/repos que llama listAllUserRepos() y cross-referencea con la tabla projects para enriquecer cada repo con: - tracked (¿está en V?) - projectCategory / projectStatus - vercel_url + dominio - last_audit_at UI completamente rehecha (repovision/page.tsx): - 4 stats (total / tracked / con deploy / archivados) - Filtros (activos / tracked / untracked / con deploy / archivados) - Búsqueda por nombre, lenguaje, descripción - Card por repo con badges de integración (V, Vercel, Dominio, Archivo) - Privacidad visible (lock/unlock) - "Preguntar a V" link directo al chat con el contexto del repo ## 3. Multi-tab chat (Fase 4) Reemplazado el dropdown de scope con TABS horizontales: - Cada tab = un scope (general o proyecto), persiste en localStorage - "+" abre dropdown para añadir otro proyecto como tab adicional - "X" en cada tab para cerrar (excepto el último general) - Switch entre tabs es instantáneo, cada tab mantiene su sesión propia (ya existía session_id per scope en el backend) - Reset button (antes "Plus") inicia nueva sesión en el scope activo - Mobile: scroll horizontal en la barra de tabs si hay muchos - Persistencia: vforge_chat_open_scopes en localStorage ## 4. Clerk webhook para auth real (Fase 1B) Nuevo endpoint POST /api/auth/webhook que recibe user.created / user.updated / user.deleted de Clerk, verifica firma con svix, y sincroniza la fila en users. Sin esto los signups de Clerk no aterrizan en nuestra DB. Config requerida en Vercel: - CLERK_WEBHOOK_SECRET (de Clerk dashboard → Webhooks) - Endpoint: https://<dominio>/api/auth/webhook - Eventos: user.created, user.updated, user.deleted Soft-delete en user.deleted para no romper foreign keys de audit_events / conversations. ## Bonus - SW bump v7-multichat para invalidar cache vieja - Dependencia nueva: svix ^1.94 (verificación de firma webhook)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fd7c7859af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| * No requiere auth porque la sesión es single-user (operator_luis). | ||
| * Cuando entre Clerk multi-tenant, esto pasará a leer el token de | ||
| * GitHub del usuario en sesión. | ||
| */ | ||
| export async function GET() { |
There was a problem hiding this comment.
Require operator auth for repository inventory endpoint
Protect this route with requireOperatorAuth before returning data, because it currently exposes the operator’s GitHub repository list (including private repo metadata) plus internal integration details (tracked, vercel_url, domain, etc.) to any unauthenticated caller. I checked the current middleware (middleware.ts) and it is pass-through (no auth.protect()), so this endpoint is publicly reachable in deployed environments unless guarded at the route level.
Useful? React with 👍 / 👎.
|
|
||
| <div className="mt-3 flex items-center justify-between gap-2 border-t border-app pt-3"> | ||
| <Link | ||
| href={`/app/chat?project=${i.projectId ?? ""}&q=${encodeURIComponent(`cuéntame de ${r.name}`)}`} |
There was a problem hiding this comment.
Wire chat deep-link params into chat initialization
Make the chat page consume the project/q query params, because this new link advertises “Preguntar a V” with repo context but currently just navigates to /app/chat without applying those parameters. As implemented, users clicking from RepoVision do not switch to the repo scope or get the suggested prompt prefilled, so the CTA behavior is broken relative to the intended flow.
Useful? React with 👍 / 👎.
…k params Codex catches: ## P1 (security): /api/repos exponía repos privados sin auth El endpoint devolvía la lista completa de repos del operador (incluyendo privados) + metadata de integraciones (vercel_url, dominios, tracked) a CUALQUIER caller sin auth. Middleware es pass-through (auth.protect sigue desactivado pendiente M11). Fix: - /api/repos ahora requiere requireOperatorAuth (Bearer token) igual que /api/vault/* - /app/repovision agrega flujo de Unlock idéntico al de /app/secrets: lee token de localStorage, si no existe muestra pantalla bloqueada con botón "Unlock" → window.prompt → setItem → loadRepos. - Mensajes de error en español según status code (401/403/503). ## P2 (UX): deep-link de chat no se aplicaba El link "Preguntar a V" en RepoVision pasaba ?project=ID&q=TEXT pero ChatExperience no consumía esos params, así que el CTA navegaba pero no cambiaba de scope ni pre-llenaba el input. Fix: - useSearchParams() en ChatExperience - En el mount-only useEffect: si hay ?project=ID, lo usamos como scope inicial (prioridad sobre el saved en localStorage). Si hay ?q=TEXTO, pre-llena el input. Una sola vez al cargar — si Luis navega después no se re-pisa.
Next 15 requiere envolver useSearchParams() en <Suspense> o el build estático falla con "missing-suspense-with-csr-bailout". El deep-link handler que agregué para ?project=X&q=Y necesitaba esto. Fallback null porque ChatExperience ya tiene loading states propios para los mensajes y proyectos.
Resumen (4 fases en un PR)
Fase 2 — V personalidad consistente cross-scope
Instrucción ABSOLUTA en el system prompt: el scope (general vs proyecto) cambia QUÉ contexto carga, NUNCA CÓMO habla. Luis es el operador único — cercano, directo, sin formalidades.
Fase 3 — /app/repovision: tus repos + integraciones
Nuevo endpoint
GET /api/reposque cross-referencea repos de GitHub con la tablaprojectsy devuelve cada repo enriquecido con:tracked,vercel_url,dominio,last_audit_at, etc.UI rehecha en
/app/repovision:Fase 4 — Multi-tab chat
Reemplazado el dropdown de scope con TABS horizontales:
vforge_chat_open_scopessession_idpropioFase 1B — Clerk webhook
Nuevo
POST /api/auth/webhook(verifica firma con svix) que sincronizauser.created/user.updated/user.deleteda nuestra tablausers. Soft-delete preserva FKs.Config requerida en Vercel:
CLERK_WEBHOOK_SECRET= signing secret de Clerk Dashboard → Webhookshttps://<dominio>/api/auth/webhookuser.created,user.updated,user.deletedCambios técnicos
svix ^1.94(firma webhooks)Lo que NO toqué (intencional)
VFORGE_OPERATOR_TOKENyVFORGE_MASTER_PEPPERen VercelgetOperatorSecret("GITHUB_TOKEN")pasen a leer per-userhttps://claude.ai/code/session_01ViBLo5sQLZ9ZvSKZyqcPHm
Generated by Claude Code