/saas — フルスタックSaaSビルダー
トリガー: /saas [アイデア] または /saas(アイデアを促されます)
出力: VercelのURLで完全にデプロイされた本番稼働可能なSaaS。動作する認証、実際のバックエンド、ライブデータ、美しいUI。デモでもスキャフォールドでもありません。本物の製品です。
パイプラインの概要
インテーク → リサーチ(並列) → 計画 → 明確化 → ビルド → テスト → レビュー → デプロイ
各フェーズが次のフェーズを制御します。近道はありません。スキップもありません。
フェーズ1 — インテーク
ユーザーが /saas [アイデア] を実行した場合、コマンドからアイデアを抽出します。
引数なしで /saas を実行した場合は、次のように尋ねます:
SaaSのアイデアは何ですか?1〜3文で説明してください。次の内容を含めてください:
- 何をするものか
- 誰のためのものか
- どのような問題を解決するか
IDEA として保存します。先に進む前に、1文で確認します。
フェーズ2 — 並列リサーチ
Agentツールを使用して、2つのリサーチトラックを同時に開始します。一方が終了するのを待ってから他方を開始しないでください。
トラックA — 市場調査
general-purpose エージェントを次のプロンプトで起動します:
このアイデアのSaaS市場を調査してください: [IDEA]
カバーする構造化レポートを提供してください:
1. 既存のプレイヤー(5〜8社の競合)
- 名前、URL、価格帯、ポジショニング
- 得意な点、不足している点
- 可能であれば推定ARR
2. 市場規模と需要
- カテゴリ(CRM、プロジェクト管理、分析など)
- 市場規模の推定
- 成長トレンド(成長/横ばい/衰退)
- 需要の証拠(Redditスレッド、ProductHuntのupvote、求人情報)
3. 価格ベンチマーク
- フリーミアム vs 有料のみ vs トライアル
- 典型的な価格帯($/ユーザー/月、定額、使用量ベース)
- このカテゴリで勝つモデル
4. 機能テーブル
- 競合全体で最も一般的な10の機能をリストアップ
- 必須機能と差別化要因をマーク
5. ホワイトスペース
- 競合は何をしていないか?
- ユーザーはどこで不満を言っているか?(G2、Capterra、Redditを確認)
- ウェッジの機会は何か?
構造化マークダウンで返してください。具体的に。無駄を省いて。
トラックB — 技術パターンリサーチ
2つ目の general-purpose エージェントを起動します:
このタイプのSaaSの技術的実装パターンを調査してください: [IDEA]
提供:
1. データモデルパターン
- このタイプのアプリには通常どのようなテーブル/エンティティが必要か?
- 主要な関係と制約
- マルチテナンシーアプローチ(行レベルのorg_id vs 個別スキーマ)
2. 認証パターン
- このタイプのアプリにはソーシャルログイン、マジックリンク、SSOが必要か?
- 招待制 vs オープンサインアップ?
- ロールパターン(管理者/メンバー/閲覧者が典型的?)
3. 課金パターン
- シート単位、使用量ベース、または定額?
- この分野で機能する無料ティア戦略?
4. 統合の必要性
- このカテゴリのアプリは通常何と統合するか?
- Webhook、Zapier、APIキー?
5. 技術的リスク
- このカテゴリで構築するのが難しいものは?
- チームが間違えやすいものは?
- パフォーマンス/スケールの考慮事項
構造化マークダウンで返してください。具体的に。
両方のトラックが完了するのを待ってから先に進みます。
フェーズ3 — アーキテクチャ計画
両方のリサーチ出力を使用して、構造化されたビルド計画を作成します。これを .saas-workspace/[project-slug]/PLAN.md に書き込みます。
計画には以下を含める必要があります:
3.1 製品定義
## 製品
名前: [アイデアから派生、変更可能]
タグライン: [1行]
コアバリュープロポジション: [1文]
ターゲットユーザー: [具体的なペルソナ]
## MVP機能セット(今日構築するもの)
- [機能1] — [スコープに入れる理由]
- [機能2] — [スコープに入れる理由]
...
## スコープ外(v1)
- [機能] — [v2に延期する理由...]
3.2 データモデル
完全なPostgresスキーマを設計します。すべてのテーブルについて:
- テーブル名、目的
- すべてのカラム(型、制約、デフォルト値)
- 外部キー関係
- 必要なRLSポリシー
- インデックス
以下の必須テーブルから始めます:
-- 常に含める:
profiles(auth.usersを拡張)
organizations(マルチテナントの場合)
organization_members(マルチテナントの場合)
subscriptions(課金がある場合)
次に、製品のドメイン固有テーブルを追加します。
3.3 ルートマップ
ランディングページ: /
認証: /login /signup /auth/callback
アプリシェル: /app(認証必須)
コア機能: /app/[feature](MVP機能ごとに1つ)
設定: /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 — 明確化の質問
コードを1行も書く前に、ギャップを表面化します。AskUserQuestion を使用して次の質問をします(1回の呼び出しで最大4つの質問にまとめる):
ラウンド1 — 製品スコープ:
- 名前を確認(アイデアから派生した3つのオプションを提示)
- マルチテナント(チーム/組織)か、シングルユーザーアカウントか?
- v1に決済/課金が必要か、それとも構築中は無料か?
- リサーチで見つかった特定の機能で、必須 vs あったら良いものは?
ラウンド2 — デザインとデプロイメント(ラウンド1の回答が新しい質問を生む場合のみ尋ねる):
- ドメイン?(または今はVercelサブドメインを使用)
- ブランドカラーや好みは?(デフォルト: Rawgrowthダークグリーンシステム)
- Resendのメール送信元アドレス?
先に進む前に、回答をPLAN.mdに組み込みます。
フェーズ5 — ビルド
ビルド前セットアップ
コードを書く前に、次のスキルをロードします:
frontend-theme— 完全なRawgrowthデザインシステムを注入ui-ux-pro-max— デザイン決定、パレット、タイポグラフィ- Magic MCP(
@21st-dev/magic)を呼び出して複雑なコンポーネントを処理
プロジェクトの場所: ~/saas-projects/[project-slug]/ に作成
スキャフォールドコマンド:
cd ~/saas-projects
npx create-next-app@latest [project-slug] \
--typescript \
--tailwind \
--eslint \
--app \
--src-dir \
--import-alias "@/*" \
--no-git
cd [project-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設定 — フロントエンドテーマトークンを src/app/globals.css に適用
5.1 データベースとマイグレーション
各マイグレーションに add-migration スキルを使用します。
マイグレーション1: コアスキーマ
-- すべてのユーザー向けテーブルで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 を完全なページ(モーダルではなく)として構築します。フロントエンドテーマを適用:
- ダーク背景(#060B08)
- 中央寄せのカード(ボーダー: rgba(255,255,255,0.06))
- 緑色のCTAボタン(#0CBF6A)
- メール + パスワードフィールド(+ 設定されている場合はソーシャルログイン)
- "パスワードをお忘れですか" リンク
- 絵文字なし。クリーンで高級感があり、ミニマル。
src/app/(auth)/login/page.tsx — Supabase認証を使用したログインフォーム
src/app/(auth)/signup/page.tsx — メール確認フローを含むサインアップフォーム
src/app/auth/callback/route.ts — OAuth/マジックリンクコールバックハンドラー
メール確認フロー:
- サインアップ時 → Supabaseが確認メールを送信(Resend SMTP経由)
- コールバックルートがコードをセッションと交換
- 成功時は
/app/dashboardにリダイレクト
5.4 アプリシェル
src/app/(app)/layout.tsx — 認証付きレイアウト:
- サイドバー(モバイルで折りたたみ可能)
- ユーザーアバターとドロップダウンメニュー(プロフィール、設定、サインアウト)付きのトップナビゲーション
- パンくずリスト
- トースト通知アウトレット
サイドバーアイテムはPLAN.mdのルートマップから派生。
Magic MCPを使用してサイドバーコンポーネントを取得: "ダッシュボードサイドバーダーク" を検索し、フロントエンドテーマトークンに適合させます。
5.5 コア機能ページ
MVP機能セットの各機能について(PLAN.mdから):
- まずデザイン — Magic MCPに関連するコンポーネントパターンを問い合わせ
- データレイヤー — サーバーコンポーネントがSupabaseから直接フェッチ
- ミューテーション — Zodバリデーション付きのServer Actions
- エラー状態 — すべてのフォームにバリデーションエラー、ローディング状態、空の状態を設定
any型なし — SupabaseスキーマからTypeScript型を生成
データフェッチ用サーバーコンポーネントのパターン:
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'