/saas — 全栈 SaaS 构建器
触发指令: /saas [创意] 或仅 /saas(将提示输入创意)
输出: 一个完整部署、生产就绪的 SaaS 应用,托管在 Vercel URL 上。包含工作认证、真实后端、实时数据和美观 UI。不是演示版,不是脚手架,而是真正的产品。
流水线概览
接收 → 研究(并行)→ 规划 → 澄清 → 构建 → 测试 → 审查 → 部署
每个阶段都作为下一阶段的关卡。没有捷径,不可跳过。
阶段 1 — 接收
如果用户运行了 /saas [创意],则从命令中提取创意。
如果用户仅运行了 /saas 而无参数,则询问:
SaaS 创意是什么?请用 1-3 句话描述。包括:
- 它的功能
- 目标用户
- 它解决的问题
存储为 IDEA。在继续之前,用一句话确认并回显。
阶段 2 — 并行研究
使用 Agent 工具同时启动两个研究线程。不要等待一个完成后再开始另一个。
线程 A — 市场情报
生成一个 general-purpose agent,提示如下:
针对此创意进行 SaaS 市场调研:[IDEA]
交付一份结构化报告,涵盖:
1. 现有玩家(5-8 个竞品)
- 名称、URL、定价层级、定位
- 他们的优势与不足
- 预估 ARR(如有)
2. 市场规模与需求
- 类别(CRM、项目管理、分析等)
- 市场规模估算
- 增长趋势(增长/平稳/下降)
- 需求证据(Reddit 帖子、ProductHunt 投票数、招聘信息)
3. 定价基准
- 免费增值 vs 纯付费 vs 试用
- 典型定价点($/用户/月、固定费用、按使用量计费)
- 在该类别中哪些模型胜出
4. 功能表
- 列出竞品间最常见的 10 个功能
- 标记哪些是入门级功能,哪些是差异化功能
5. 空白空间
- 竞品没有做什么?
- 用户在哪些地方抱怨?(查看 G2、Capterra、Reddit)
- 突破口机会是什么?
返回结构化 markdown。要具体,无废话。
线程 B — 技术模式研究
生成第二个 general-purpose agent:
针对此类 SaaS 研究技术实现模式:[IDEA]
交付:
1. 数据模型模式
- 此类应用通常需要哪些表/实体?
- 关键关系与约束
- 多租户方案(基于行的 org_id vs 独立 schema)
2. 认证模式
- 此类应用是否需要社交登录?魔法链接?SSO?
- 邀请制 vs 开放注册?
- 角色模式(典型为管理员/成员/查看者?)
3. 计费模式
- 按席位、按使用量还是固定费用?
- 该领域有效的免费层策略?
4. 集成需求
- 该类别的应用通常与哪些集成?
- Webhook、Zapier、API 密钥?
5. 技术风险
- 该类别中构建什么比较困难?
- 团队常犯什么错误?
- 性能/扩展性考虑
返回结构化 markdown。要具体。
等待两个线程都完成后再继续。
阶段 3 — 架构规划
使用两个研究的输出,生成一份结构化的构建计划。将其写入 .saas-workspace/[项目-slug]/PLAN.md。
计划必须包括:
3.1 产品定义
## 产品
名称:[从创意衍生,可更改]
标语:[一句话]
核心价值主张:[一句话]
目标用户:[具体画像]
## MVP 功能集(我们今天要构建的内容)
- [功能 1] — [为何纳入范围]
- [功能 2] — [为何纳入范围]
...
## 范围外(v1)
- [功能] — [推迟到 v2,因为...]
3.2 数据模型
设计完整的 Postgres schema。对于每个表:
- 表名、用途
- 所有列,包含类型、约束、默认值
- 外键关系
- 需要的 RLS 策略
- 索引
从以下必需表开始:
-- 始终包含:
profiles(扩展 auth.users)
organizations(如果是多租户)
organization_members(如果是多租户)
subscriptions(如果有计费)
然后添加产品的特定领域表。
3.3 路由地图
着陆页: /
认证: /login /signup /auth/callback
应用框架: /app(需要认证)
核心功能: /app/[功能](每个 MVP 功能一个)
设置: /app/settings
API 路由: /api/...(逐一列出)
3.4 技术栈确认
框架: Next.js 15(App Router, TypeScript)
数据库: Supabase(Postgres + Auth + RLS)
样式: Tailwind CSS v4 + shadcn/ui
邮件: Resend(事务性邮件)
部署: Vercel
支付: Stripe(如果需要计费)
3.5 构建顺序
列出准确的顺序:迁移 → 认证 → API 路由 → 应用框架 → 功能 → 着陆页 → 邮件模板 → 测试。
阶段 4 — 澄清问题
在编写一行代码之前,先找出缺口。使用 AskUserQuestion 来询问以下问题(每次调用最多分组为 4 个问题):
第一轮 — 产品范围:
- 确认名称(根据创意提供 3 个选项)
- 多租户(团队/组织)还是单用户账户?
- v1 是否需要支付/计费,还是在构建期间免费?
- 研究中是否有任何特定功能是必须拥有的 vs 锦上添花的?
第二轮 — 设计与部署(仅当第一轮的答案引发新问题时才问):
- 域名?(或暂时使用 Vercel 子域名)
- 是否有品牌颜色/偏好?(默认:Rawgrowth 深绿系统)
- Resend 的发件人邮箱地址?
在继续之前将答案整合到 PLAN.md 中。
阶段 5 — 构建
构建前准备
在编写任何代码前加载以下技能:
frontend-theme— 注入完整的 Rawgrowth 设计系统ui-ux-pro-max— 设计决策、调色板、排版- 调用 Magic MCP(
@21st-dev/magic)用于任何复杂组件
项目位置: 创建在 ~/saas-projects/[项目-slug]/
脚手架命令:
cd ~/saas-projects
npx create-next-app@latest [项目-slug] \
--typescript \
--tailwind \
--eslint \
--app \
--src-dir \
--import-alias "@/*" \
--no-git
cd [项目-slug]
安装依赖:
npm install @supabase/supabase-js @supabase/ssr \
resend \
@radix-ui/react-icons \
lucide-react \
class-variance-authority clsx tailwind-merge \
zod \
@hookform/resolvers react-hook-form
# shadcn/ui 初始化
npx shadcn@latest init --defaults
# 添加 shadcn 组件
npx shadcn@latest add button input label card \
dropdown-menu avatar badge separator toast \
dialog sheet tabs form
Tailwind v4 配置 — 将 frontend-theme 令牌应用到 src/app/globals.css。
5.1 数据库与迁移
为每次迁移使用 add-migration 技能。
迁移 1:核心 schema
-- 对所有面向用户的表启用 RLS
-- profiles:扩展 auth.users
CREATE TABLE profiles (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
email TEXT NOT NULL,
full_name TEXT,
avatar_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "用户可以查看自己的个人资料"
ON profiles FOR SELECT USING (auth.uid() = id);
CREATE POLICY "用户可以更新自己的个人资料"
ON profiles FOR UPDATE USING (auth.uid() = id);
-- 注册时自动创建个人资料
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, email, full_name, avatar_url)
VALUES (
NEW.id,
NEW.email,
NEW.raw_user_meta_data->>'full_name',
NEW.raw_user_meta_data->>'avatar_url'
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
迁移 2:组织(如果是多租户)
CREATE TABLE organizations (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
owner_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
plan TEXT DEFAULT 'free',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE organization_members (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
org_id UUID REFERENCES organizations(id) ON DELETE CASCADE NOT NULL,
user_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('owner', 'admin', 'member')),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(org_id, user_id)
);
ALTER TABLE organizations ENABLE ROW LEVEL SECURITY;
ALTER TABLE organization_members ENABLE ROW LEVEL SECURITY;
CREATE POLICY "成员可以查看自己的组织"
ON organizations FOR SELECT
USING (id IN (
SELECT org_id FROM organization_members WHERE user_id = auth.uid()
));
CREATE POLICY "成员可以查看组织成员资格"
ON organization_members FOR SELECT
USING (org_id IN (
SELECT org_id FROM organization_members WHERE user_id = auth.uid()
));
迁移 3+:领域表 — 根据 PLAN.md 中的数据模型创建产品特定表。对每个表应用 RLS。
5.2 Supabase 客户端设置
src/lib/supabase/client.ts:
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
src/lib/supabase/server.ts:
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll() },
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {}
},
},
}
)
}
src/middleware.ts — 保护所有 /app/* 路由,将未认证用户重定向到 /login。
5.3 认证页
构建 /login 和 /signup 作为完整页面(非模态框)。应用 frontend-theme:
- 深色背景(#060B08)
- 居中卡片,带边框(rgba(255,255,255,0.06))
- 绿色 CTA 按钮(#0CBF6A)
- 邮箱 + 密码字段(+ 社交登录,如果配置)
- “忘记密码”链接
- 无表情符号。干净、高级、极简。
src/app/(auth)/login/page.tsx — 使用 Supabase Auth 的登录表单
src/app/(auth)/signup/page.tsx — 带邮箱确认流程的注册表单
src/app/auth/callback/route.ts — OAuth/魔法链接回调处理程序
邮箱确认流程:
- 注册时 → Supabase 发送确认邮件(通过 Resend SMTP)
- 回调路由将 code 交换为 session
- 成功时重定向到
/app/dashboard
5.4 应用框架
src/app/(app)/layout.tsx — 经过认证的布局,包含:
- 侧边栏(在移动设备上可折叠)
- 顶部导航,带用户头像 + 下拉菜单(个人资料、设置、退出)
- 面包屑导航
- Toast 通知出口
侧边栏项目根据 PLAN.md 中的路由地图生成。
使用 Magic MCP 获取侧边栏组件:搜索 "dashboard sidebar dark" 并根据 frontend-theme 令牌进行调整。
5.5 核心功能页
对于 MVP 功能集中的每个功能(来自 PLAN.md):
- 先设计 — 查询 Magic MCP 获取相关组件模式
- 数据层 — 服务器组件直接从 Supabase 获取数据
- 变更操作 — 带 Zod 验证的服务器操作
- 错误状态 — 每个表单都有验证错误、加载状态、空状态
- 无
any类型 — 从 Supabase schema 生成 TypeScript 类型
数据获取服务器组件模式:
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
export default async function FeaturesPage() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
redirect('/login')
}
const { data: features } = await supabase
.from('features')
.select('*')
.eq('user_id', user.id)
return (
<div>
{/* 渲染功能 */}
</div>
)
}