# Turborepo Monorepo Per-Project Migration Plan ## Why We Are Making This Change The core thesis of this platform is that **one AI controls everything in one project**. For that to work, the AI needs a complete mental model of the project — all apps, all shared code, all dependencies — in a single coherent context. The current architecture creates a single Gitea repo per project with no enforced internal structure. The AI has no reliable way to know where apps live, what shares code with what, or how to trigger a targeted build for one part of the project. By adopting **Turborepo monorepo per project**, every project repo gets a standardised structure containing all of its apps (`product`, `website`, `admin`, `storybook`) and shared packages (`ui`, `tokens`, `types`, `config`). The AI operates across the entire project simultaneously. Build orchestration, deployment, and shared code all become coherent automatically. **The structure every user project repo will have:** ``` {project-slug}/ ← one Gitea repo per project apps/ product/ ← core user-facing app (Next.js) website/ ← marketing / landing site (Next.js) admin/ ← internal admin tool (Next.js) storybook/ ← component browser and design system packages/ ui/ ← shared React component library tokens/ ← design tokens (colors, spacing, typography) types/ ← shared TypeScript types config/ ← shared eslint, tsconfig turbo.json package.json ← pnpm workspace root .gitignore README.md ``` Turborepo is MIT-licensed, runs anywhere, and costs nothing. No Vercel dependency. --- ## Infrastructure Context Everything runs on a single GCP VM (`34.19.250.135`, Montreal) via Docker + Traefik: | Service | URL | Repo | |---|---|---| | Platform frontend | `vibnai.com` | `git.vibnai.com/mark/vibn-frontend` | | Gitea | `git.vibnai.com` | — | | Coolify | `coolify.vibnai.com` | — | | PostgreSQL | internal | — | **All platform logic lives in `vibn-frontend`** (Next.js). There is no separate control plane service. The backend is Next.js API routes in `app/api/`. Storage is PostgreSQL via raw SQL queries (no ORM layer in use for project data). **Integrations that already exist and should not be replaced:** - `lib/gitea.ts` — full Gitea API client (create repo, webhooks, signature verification) - `lib/coolify.ts` — full Coolify API client (projects, databases, applications, deployments) - `app/api/projects/create/route.ts` — project creation flow (creates Gitea repo) - `app/api/webhooks/gitea/route.ts` — receives Gitea push/PR events - `app/api/webhooks/coolify/route.ts` — receives Coolify deployment events - `app/api/ai/chat/route.ts` — AI chat with Gemini - `lib/auth/authOptions.ts` — NextAuth v4 with Prisma adapter --- ## Scope of Changes ### 1. Scaffold Templates **What:** A set of template files written into the user's Gitea repo when a project is created, giving every project the standard Turborepo monorepo structure. **Where:** `vibn-frontend/lib/scaffold/turborepo/` **Files to create:** - `turbo.json` — pipeline: `build`, `dev`, `lint`, `type-check`, `test` - `package.json` — pnpm workspace root pointing to `apps/*` and `packages/*` - `.gitignore` - `README.md` — project-specific (name injected at scaffold time) - `apps/product/` — Next.js 15, references shared `ui`, `tokens`, `types` - `apps/website/` — Next.js 15 - `apps/admin/` — Next.js 15 - `apps/storybook/` — Storybook 8 - `packages/ui/` — Button, Card, Input, Badge components using CSS token vars - `packages/tokens/` — design tokens as TS + CSS custom properties - `packages/types/` — shared `User`, `ApiResponse`, `PaginatedResponse` types - `packages/config/` — `tsconfig.base.json` and `eslint.config.js` **Status:** Templates were written and are ready. Need to be moved to `vibn-frontend/lib/scaffold/turborepo/`. --- ### 2. Project Creation Route — Add Scaffold Push **What:** The existing `app/api/projects/create/route.ts` already creates a Gitea repo. It needs one additional step: push the Turborepo scaffold as the initial commit. **File to update:** `vibn-frontend/app/api/projects/create/route.ts` **Current flow:** 1. Create Gitea repo (`auto_init: true` — creates empty repo with README) 2. Register webhook 3. Save project record to PostgreSQL **New step to add after repo creation:** - Read scaffold template files from `lib/scaffold/turborepo/` - Replace `{{project-slug}}` and `{{project-name}}` placeholders - Push each file to the Gitea repo via the contents API - This replaces the default empty `auto_init` commit **Note:** Change `auto_init: true` to `auto_init: false` since we are pushing the scaffold ourselves. --- ### 3. Project Data Model — Add App Tracking **What:** The `fs_projects` table stores project data as a JSONB `data` column. The `data` object needs two new fields to track the monorepo apps and their Coolify services. **Fields to add to the project `data` JSONB:** ```typescript apps: Array<{ name: string; // "product" | "website" | "admin" | "storybook" path: string; // "apps/product" coolifyServiceUuid?: string; domain?: string; }> turboVersion: string; // e.g. "2.3.3" ``` No schema migration needed — it's JSONB, just include these fields when inserting/updating. --- ### 4. Coolify — Per-App Service Provisioning **What:** When a project is created, each app in the monorepo gets its own Coolify service with the correct Turbo build filter. This extends the existing `lib/coolify.ts`. **File to update:** `vibn-frontend/lib/coolify.ts` **Add function:** ```typescript createMonorepoAppService(opts: { projectUuid: string; appName: string; // e.g. "product" gitRepo: string; // the project's Gitea clone URL domain: string; // e.g. "product-taskmaster.vibnai.com" }): Promise ``` Build command: `pnpm install && turbo run build --filter={appName}` **Wire into project creation:** After Gitea repo is created and scaffold is pushed, create one Coolify service per app and store the `coolifyServiceUuid` in the project's `apps` array. --- ### 5. Deploy API Route **What:** A new API route that triggers a Coolify deployment for a specific app within a project. **File to create:** `vibn-frontend/app/api/projects/[projectId]/deploy/route.ts` ``` POST /api/projects/{projectId}/deploy Body: { app_name: "product" | "website" | "admin" | "storybook" } ``` Flow: 1. Load project from PostgreSQL 2. Find the app's `coolifyServiceUuid` 3. Call `deployApplication(uuid)` from `lib/coolify.ts` 4. Return deployment UUID --- ### 6. AI Chat — Project Context Injection **What:** The existing `app/api/ai/chat/route.ts` handles Gemini chat. It needs to inject monorepo structure context when a `projectId` is present in the request. **File to update:** `vibn-frontend/app/api/ai/chat/route.ts` **Add to chat request handling:** - Accept optional `projectId` - When present, load the project from PostgreSQL - Inject into the system prompt: - Project name, slug, repo URL - List of apps and their domains - Shared packages available - Turbo version and build command pattern - Add two new Gemini tools: - `deploy_app` — triggers `POST /api/projects/{projectId}/deploy` - `scaffold_app` — adds a new app folder to the monorepo via Gitea contents API --- ## Implementation Order | Step | Task | File | Depends On | |------|------|------|-----------| | 1 | Move scaffold templates into `vibn-frontend/lib/scaffold/` | `lib/scaffold/turborepo/**` | — | | 2 | Update project creation to push scaffold | `app/api/projects/create/route.ts` | Step 1 | | 3 | Add app tracking fields to project data | `app/api/projects/create/route.ts` | Step 2 | | 4 | Add `createMonorepoAppService` to Coolify lib | `lib/coolify.ts` | — | | 5 | Wire Coolify per-app provisioning into project creation | `app/api/projects/create/route.ts` | Steps 3, 4 | | 6 | Add deploy route | `app/api/projects/[projectId]/deploy/route.ts` | Step 4 | | 7 | Inject monorepo context into AI chat | `app/api/ai/chat/route.ts` | Step 3 | --- ## What Does Not Change - Gitea as source control — same, one repo per project (already the case) - Coolify as deployment host — same, extended with per-app services - NextAuth for auth — unchanged - PostgreSQL + JSONB for project storage — unchanged - `lib/gitea.ts` and `lib/coolify.ts` — extended, not replaced - No Vercel dependency anywhere