Project Generation
Project Generation
This guide explains how to use AI (Cursor) to generate a complete application from your idea using the foundation's architecture.
When to Use
Tell Cursor (or any AI tool) something like:
- "I want to create a finance tracker"
- "Build a task management app"
- "Add a blog feature"
The AI should follow this structured process to generate code that respects the foundation's architecture.
Step 1: Define Your App
App Type
Decide whether your app is:
| Type | Description | Multi-tenant UI |
|---|---|---|
| Personal | Single user, one workspace | Hidden (WorkspaceSwitcher removed) |
| Workspace | Multiple users, multiple workspaces | Visible |
Both types use the same code under the hood — only the UI differs.
Entities
For each entity, define:
- Fields and their types
- Which fields are required
- Relations to other entities
Supported field types:
| Type | Maps to |
|---|---|
text | text() — short text |
longtext | text() — long text |
number | integer() |
decimal | decimal(precision, scale) |
boolean | boolean() |
date | timestamp() |
datetime | timestamp({ withTimezone: true }) |
enum | text() (validate in Zod) |
uuid (relation) | uuid().references() |
Step 2: Create a PRD File
Create project.prd.json in the project root. This file serves as the single source of truth for code generation:
{
"version": "1.0",
"app": {
"name": "Personal Finance Tracker",
"description": "Track income, expenses, and budgets",
"type": "personal"
},
"entities": [
{
"name": "transaction",
"label": "Transaction",
"pluralLabel": "Transactions",
"icon": "DollarSign",
"fields": [
{ "name": "amount", "type": "decimal", "required": true },
{ "name": "description", "type": "text", "required": true },
{ "name": "date", "type": "date", "required": true },
{
"name": "type",
"type": "enum",
"values": ["income", "expense"],
"required": true
},
{
"name": "categoryId",
"type": "uuid",
"relation": { "entity": "category", "type": "many-to-one" },
"required": true
}
]
},
{
"name": "category",
"label": "Category",
"pluralLabel": "Categories",
"icon": "Tag",
"fields": [
{ "name": "name", "type": "text", "required": true },
{ "name": "color", "type": "text", "required": false }
]
}
],
"features": {
"charts": true,
"export": false
}
}Step 3: Generate Code
For each entity in the PRD, generate files in this order:
- Database schema →
lib/db/schema/{entity}.ts - Migration →
npm run db:generate+ add RLS policies - Queries →
lib/db/queries/{entity}.ts - Validation →
lib/validation/{entity}Schemas.ts - API routes →
app/api/{entity}/route.ts+[id]/route.ts - Client API →
lib/api/client/{entity}.ts - React Query hooks →
hooks/use{Entity}.ts - UI components →
components/{entity}/ - Dialogs →
components/dialogs/{Entity}Dialog.tsx - Page →
app/(admin)/{entity}/page.tsx - Navigation → add to
app-sidebar.tsx - Site config → update
siteConfig.ts
See the Creating Features guide for detailed code examples at each step.
Step 4: Remove the Example Entity
If you're replacing the habits example with your own entities:
- Delete all
habits-related files (schema, queries, API, hooks, components) - Remove from navigation
- Delete habits migration (keep core migrations
0002_rls.sqland0003_user_tenant_trigger.sql)
Step 5: Environment Setup
You'll need these Supabase credentials in .env.local:
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
DATABASE_URL=Step 6: Test
npm run devOpen http://localhost:3000 and verify everything works.
Important Rules
- Always filter by tenantId in queries
- Always add RLS policies to migrations
- Always include tenantId in React Query keys
- Follow naming conventions — camelCase for JavaScript, snake_case for database
- Use existing patterns from the
habitsexample - For Personal Apps — hide
WorkspaceSwitcherbut keep thetenantIdarchitecture