详细分析 ▾
运行时依赖
版本
stack-scaffold 0.1.2 - Added a CHANGELOG.md file to the project. - Updated SKILL.md with no functional or protocol changes. - Updated claw.json (details not shown).
安装命令 点击复制
技能文档
You are an expert full-stack developer. When the user asks to create a new project, scaffold the complete structure following the conventions below. Always confirm the project name and target directory with the user before creating files. This skill only creates new files in empty or new directories — it never reads or modifies existing .env, .env.local, or credential files.
Planning Protocol (MANDATORY — execute 之前 任何 action)
Before writing a single file or running any command, you MUST complete this planning phase:
- Understand 请求. Restate 什么 用户 wants 在...中 own words. Identify 任何 ambiguities. 如果 请求 vague (e.g., "创建 project"), ask one round 的 clarifying questions (project name, purpose, 任何 specific requirements).
- Survey environment. Check current directory structure 和 installed tools. Run
ls和节点 -v到 confirm target directory 空 或 做 不 exist 尚未. 做 不 读取, 打开, 或 inspect 任何.env,.env.local, 或 credential files. skill 仅 creates 新的 projects — 如果 directory 已经 contains project, ask 用户 到 confirm 之前 proceeding.
- Build execution plan. 写入 out numbered 列表 的 steps 您 将 take, 包括 file paths 您 将 创建 或 修改, commands 您 将 run, 和 expected outcome 的 每个 step. Present plan 到 yourself (在...中 reasoning) 之前 executing.
- Identify risks. Note 任何 step could 失败 或 cause data loss (overwriting files, dropping tables, force-pushing). 对于 每个 risk, define mitigation (备份, dry-run, confirmation).
- Execute sequentially. 关注 plan step 由 step. 之后 每个 step, 验证 succeeded 之前 moving 到 下一个. 如果 step fails, diagnose issue, 更新 plan, 和 continue.
- Summarize. 之后 completing 所有 steps, provide concise summary 的 什么 是 created, 什么 是 modified, 和 任何 manual steps 用户 仍然 needs 到 take.
Do NOT skip this protocol. Rushing to execute without planning leads to errors, broken state, and wasted time.
Project Initialization
- Run
npx 创建-下一个-app@latest到 创建 下一个.js project 带有 App Router.--typescript --tailwind --eslint --app --src-dir --导入-alias "@/" cd进入 project directory.- Ensure
.gitignoreexists 和 includes 在 minimum:.env,.env.local,.env.local,node_modules/,.下一个/.创建-下一个-app模板 已经 includes these, 但是 验证 之前 任何 commit. - Initialize git:
git init && git 添加 - && git commit -m "chore: initial 下一个.js scaffold".
Dependencies
Install the following in a single command:
npm install @supabase/supabase-js @supabase/ssr firebase firebase-admin zod zustand next-themes
npm install -D @types/node vitest @vitejs/plugin-react playwright @playwright/test prettier eslint-config-prettier
Directory Structure
Create this structure inside src/:
src/
├── app/
│ ├── (auth)/
│ │ ├── login/page.tsx
│ │ └── signup/page.tsx
│ ├── (dashboard)/
│ │ └── page.tsx
│ ├── api/
│ │ └── health/route.ts
│ ├── layout.tsx
│ ├── page.tsx
│ └── globals.css
├── components/
│ ├── ui/ # Reusable UI primitives
│ └── shared/ # Shared composite components
├── lib/
│ ├── supabase/
│ │ ├── client.ts # Browser Supabase client
│ │ ├── server.ts # Server Supabase client (cookies-based)
│ │ ├── middleware.ts # Auth refresh middleware helper
│ │ └── types.ts # Generated DB types (placeholder)
│ ├── firebase/
│ │ ├── client.ts # Firebase client SDK init
│ │ └── admin.ts # Firebase Admin SDK init (server-only)
│ └── utils.ts
├── hooks/
│ └── use-auth.ts # Auth state hook
├── stores/
│ └── user-store.ts # Zustand user store
├── types/
│ └── index.ts
└── middleware.ts # Next.js middleware for auth
File Contents
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 {
// Called from Server Component — ignore
}
},
},
}
);
}
src/lib/firebase/client.ts
import { initializeApp, getApps } from "firebase/app";
import { getAuth } from "firebase/auth";const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
export const auth = getAuth(app);
src/lib/firebase/管理员.ts
import { initializeApp, getApps, cert } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";if (getApps().length === 0) {
initializeApp({
credential: cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n"),
}),
});
}
export const adminAuth = getAuth();
src/中间件.ts
import { type NextRequest, NextResponse } from "next/server";
import { createServerClient } from "@supabase/ssr";export async function middleware(request: NextRequest) {
let response = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value }) =>
request.cookies.set(name, value)
);
response = NextResponse.next({ request });
cookiesToSet.forEach(({ name, value, options }) =>
response.cookies.set(name, value, options)
);
},
},
}
);
await supabase.auth.getUser();
return response;
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|.\\.(?:svg|png|jpg|jpeg|gif|webp)$).)"],
};
src/app/api/health/路由.ts
import { NextResponse } from "next/server";export async function GET() {
return NextResponse.json({ status: "ok", timestamp: new Date().toISOString() });
}
.env.示例
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=# Firebase Client
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
# Firebase Admin (server-only)
FIREBASE_PROJECT_ID=
FIREBASE_CLIENT_EMAIL=
FIREBASE_PRIVATE_KEY=
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
vercel.json
{
"framework": "nextjs",
"regions": ["gru1"],
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "no-store" }
]
}
]
}
vitest.配置.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
globals: true,
setupFiles: ["./src/tests/setup.ts"],
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
Post-Scaffold Steps
- 复制
.env.示例到.env.local和 remind 用户 到 fill 在...中 values. - 创建 initial Supabase migration file 在
supabase/migrations/00000000000000_init.sql带有profiles表:
create table public.profiles (
id uuid references auth.users on delete cascade primary key,
email text not null,
full_name text,
avatar_url text,
created_at timestamptz default now() not null,
updated_at timestamptz default now() not null
);alter table public.profiles enable row level security;
create policy "Users can view own profile" on public.profiles
for select using (auth.uid() = id);
create policy "Users can update own profile" on public.profiles
for update using (auth.uid() = id);
- 添加 scripts 到
包.json:
{
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
"format": "prettier --write .",
"test": "vitest",
"test:e2e": "playwright test",
"types:supabase": "npx supabase gen types typescript --local > src/lib/supabase/types.ts"
}
}
- Commit:
git 添加 - && git commit -m "chore: 添加 满 stack scaffold 带有 Supabase, Firebase Auth, Vercel 配置".
- 打印 summary 的 什么 是 created 和 什么 用户 needs 到 configure manually (env vars, Supabase project, Firebase project, Vercel project 链接, Cloudflare DNS).
下一个 Steps — Recommended Skill Sequence
After scaffolding is complete, run the following skills in order:
firebase-auth-setup— Configures auth providers (Google, Apple, email/密码), creates auth 钩子, auth provider 组件, server-side 令牌 verification, 和 Firebase-Supabase 用户 同步 路由. builds 在...上 Firebase client/管理员 SDK files created 由 scaffold.shadcn-主题-默认— Applies 默认 shadcn/ui Neutral 主题 带有 OKLCH CSS variables 和 dark mode support.cloudflare-guard— Sets up DNS, SSL, rate limiting, 和 caching 对于 domain 在...上 Cloudflare.deploy-pilot— Runs pre-deploy checklist 和 pushes initial deployment 到 Vercel.
The scaffold creates placeholder files for Firebase (src/lib/firebase/client.ts, src/lib/firebase/admin.ts) and a basic auth middleware (src/middleware.ts). The firebase-auth-setup skill extends these with full auth flows, user sync, and custom claims.
免费技能或插件可能存在安全风险,如需更匹配、更安全的方案,建议联系付费定制