feat: add Turborepo per-project monorepo scaffold and project API

- Add Turborepo scaffold templates (apps: product, website, admin, storybook; packages: ui, tokens, types, config)
- Add ProjectRecord and AppRecord types to control plane
- Add Gitea integration service (repo creation, scaffold push, webhooks)
- Add Coolify integration service (project + per-app service provisioning with turbo --filter)
- Add project routes: GET/POST /projects, GET /projects/:id/apps, POST /projects/:id/deploy
- Update chat route to inject project/monorepo context into AI requests
- Add deploy_app and scaffold_app tools to Gemini tool set
- Update deploy executor with monorepo-aware /execute/deploy endpoint
- Add TURBOREPO_MIGRATION_PLAN.md documenting rationale and scope

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-21 15:07:35 -08:00
parent 57b9ce2f1a
commit 2c3e7f9dfb
40 changed files with 1625 additions and 33 deletions

229
TURBOREPO_MIGRATION_PLAN.md Normal file
View File

@@ -0,0 +1,229 @@
# 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 separate Gitea repos per app (frontend repo, API repo, etc.), which fragments that context. The AI has to context-switch across repos, cross-repo dependencies are manual and brittle, and shared code has no clean home.
By adopting **Turborepo monorepo per project**, every project becomes a single repo containing all of its apps (`product`, `website`, `admin`) and shared packages (`ui`, `types`, `config`). The AI operates across the entire project simultaneously. Build orchestration, deployment, and shared code all become coherent automatically.
**The structure every project will have:**
```
{project-slug}/
apps/
product/ ← the core user-facing app
website/ ← marketing / landing site
admin/ ← internal admin tool
packages/
ui/ ← shared component library
types/ ← shared TypeScript types
config/ ← shared eslint, tsconfig
turbo.json
package.json ← workspace root (pnpm workspaces)
.gitignore
README.md
```
This is not a Vercel dependency. Turborepo is MIT-licensed, runs anywhere, and costs nothing. Remote caching is optional and can be self-hosted on Coolify.
---
## Scope of Changes
### 1. Project Scaffold Templates
**What:** Create a set of template files that get written into a new Gitea repo when a project is created.
**Files to create:** `platform/scripts/templates/turborepo/`
- `turbo.json` — pipeline config defining `build`, `dev`, `lint`, `test` tasks and their dependencies
- `package.json` — workspace root with pnpm workspaces pointing to `apps/*` and `packages/*`
- `.gitignore` — covering node_modules, dist, .turbo cache
- `apps/product/package.json` — Next.js app skeleton
- `apps/website/package.json` — Astro or Next.js skeleton
- `apps/admin/package.json` — Next.js app skeleton
- `packages/ui/package.json` — shared component library stub
- `packages/types/package.json` — shared types stub
- `packages/config/` — shared `tsconfig.json` and `eslint` base configs
**Notes:**
- Templates should be stack-agnostic at the shell level — the `turbo.json` pipeline is what matters, inner frameworks can vary
- Stack choices (Next.js vs Astro, etc.) can be parameterised later when we add a project creation wizard
---
### 2. Control Plane — Project Data Model Update
**What:** The current data model stores multiple Gitea repos per project. This changes to one repo per project.
**File:** `platform/backend/control-plane/src/types.ts`
**Changes:**
- Remove `repos: Array<{ gitea_repo, path }>` from `ProjectRecord` (or update it)
- Add `repo: string` — single Gitea repo URL for the project
- Add `apps: Array<{ name: string; path: string; coolify_service_uuid?: string }>` — tracks each app inside the monorepo and its Coolify service
- Add `turbo: { version: string }` — tracks which Turborepo version the project was scaffolded with
---
### 3. Control Plane — New Project Routes
**What:** Add project management endpoints to the control plane API.
**File to create:** `platform/backend/control-plane/src/routes/projects.ts`
**Endpoints:**
| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/projects` | Create project — scaffold Turborepo repo in Gitea, register in DB |
| `GET` | `/projects/:project_id` | Get project record |
| `GET` | `/projects/:project_id/apps` | List apps within the monorepo |
| `POST` | `/projects/:project_id/apps` | Add a new app to the monorepo |
| `POST` | `/projects/:project_id/deploy` | Trigger Turbo build + Coolify deploy for one or all apps |
**Project creation flow (`POST /projects`):**
1. Validate request (name, tenant_id, optional app selections)
2. Create Gitea repo via Gitea API
3. Scaffold Turborepo structure from templates, push initial commit
4. Register webhook: Gitea repo → control plane `/webhooks/gitea`
5. Create Coolify project
6. Create one Coolify service per app (with correct build filter)
7. Save project record to storage
8. Return project record with repo URL and app list
---
### 4. Control Plane — Storage Layer Updates
**What:** Add project storage operations alongside existing runs/tools storage.
**File to update:** `platform/backend/control-plane/src/storage/memory.ts`
**File to update:** `platform/backend/control-plane/src/storage/firestore.ts`
**File to update:** `platform/backend/control-plane/src/storage/index.ts`
**New operations to add:**
- `saveProject(project: ProjectRecord): Promise<void>`
- `getProject(projectId: string): Promise<ProjectRecord | null>`
- `listProjects(tenantId: string): Promise<ProjectRecord[]>`
- `updateProjectApp(projectId: string, app: AppRecord): Promise<void>`
---
### 5. Gitea Integration Service
**What:** New service to abstract all Gitea API calls. Currently there is no Gitea integration in the control plane.
**File to create:** `platform/backend/control-plane/src/gitea.ts`
**Responsibilities:**
- Create repo for a project
- Push initial scaffolded files (initial commit)
- Register webhooks
- Read file tree (so AI can understand the project structure)
- Read/write individual files (so AI can make edits)
**Config needed in `config.ts`:**
- `giteaUrl` — from `GITEA_URL` env var (e.g. `https://git.vibnai.com`)
- `giteaToken` — from `GITEA_TOKEN` env var (admin token for repo creation)
---
### 6. Coolify Integration Service
**What:** New service to abstract all Coolify API calls. Currently the deploy executor calls Coolify but there is no central integration.
**File to create:** `platform/backend/control-plane/src/coolify.ts`
**Responsibilities:**
- Create a Coolify project
- Create a Coolify application service linked to a Gitea repo
- Set the build command to `turbo run build --filter={app-name}`
- Set the publish directory per app
- Trigger a deployment
- Get deployment status
**Config needed in `config.ts`:**
- `coolifyUrl` — from `COOLIFY_URL` env var
- `coolifyToken` — from `COOLIFY_TOKEN` env var
---
### 7. Deploy Executor — Monorepo Awareness
**What:** The existing deploy executor (`platform/backend/executors/deploy`) currently deploys a single service. It needs to understand the monorepo structure and use `turbo run build --filter` to target the right app.
**File to update:** `platform/backend/executors/deploy/src/index.ts`
**Changes:**
- Accept `app_name` in the input payload (e.g. `"product"`, `"website"`, `"admin"`)
- Build command becomes `turbo run build --filter={app_name}` instead of `npm run build`
- Pass the root of the monorepo as the build context, not an app subdirectory
---
### 8. AI Context — Project-Aware Prompting
**What:** The Gemini chat integration currently has no awareness of which project the user is in. It needs project context so the AI can reason across the whole monorepo.
**File to update:** `platform/backend/control-plane/src/gemini.ts`
**File to update:** `platform/backend/control-plane/src/routes/chat.ts`
**Changes:**
- Add `project_id` to `ChatRequest`
- On chat requests with a `project_id`, fetch the project record and inject:
- Repo structure (app names, package names)
- Recent deployment status per app
- `turbo.json` pipeline config
- Add a new Gemini tool: `scaffold_app` — lets the AI add a new app to the user's monorepo
- Add a new Gemini tool: `deploy_app` — lets the AI trigger a Coolify deploy for a specific app by name
---
### 9. Theia Workspace — Single Repo Mode
**What:** The current Theia docker-compose opens a multi-root workspace across multiple repos. With one repo per project, this simplifies to a single workspace root.
**File to update:** `theia-docker-compose.yml` (and the Coolify service config for Theia)
**Changes:**
- Workspace path points to the cloned monorepo root
- Git remote is the project's single Gitea repo
- Theia extensions should be aware of the `turbo.json` to surface run targets in the UI (future)
---
### 10. Local Dev — Replace start-all.sh with Turbo
**What:** The current `platform/scripts/start-all.sh` manually starts each service with `&`. Once the platform itself is in a Turborepo, this can be replaced with `turbo run dev`.
**Note:** This is a nice-to-have follow-on. The priority is getting user project scaffolding right first. The platform's own internal structure can be migrated to Turborepo in a separate pass.
---
## Implementation Order
| Step | Task | Depends On |
|------|------|-----------|
| 1 | Create scaffold templates | Nothing |
| 2 | Add `ProjectRecord` type + storage ops | Step 1 |
| 3 | Build Gitea integration service | Step 2 |
| 4 | Build Coolify integration service | Step 2 |
| 5 | Add project routes to control plane | Steps 2, 3, 4 |
| 6 | Update deploy executor for monorepo | Step 5 |
| 7 | Update AI chat with project context | Step 5 |
| 8 | Update Theia workspace config | Step 5 |
| 9 | Migrate platform itself to Turborepo | All of the above |
---
## What Does Not Change
- Gitea as the source control host — same, just one repo per project instead of many
- Coolify as the deployment host — same, just configured with Turbo build filters
- Theia as the IDE — same, just opens one repo instead of multi-root
- The control plane API architecture (Fastify, in-memory/Firestore storage) — same, just extended
- Auth model — unchanged
- No Vercel dependency anywhere in this plan