buildcrew 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -258,19 +258,6 @@ model: opus # or sonnet, haiku
258
258
  └── Debug: investigator
259
259
  ```
260
260
 
261
- ## Compared to gstack
262
-
263
- | | buildcrew | gstack |
264
- |---|-----------|--------|
265
- | Install | `npx buildcrew` | `git clone` + Bun + `./setup` |
266
- | Orchestration | Auto (constitution routes) | Manual (`/qa`, `/review`, ...) |
267
- | Harness | `init` + `add` + custom .md | Manual CLAUDE.md |
268
- | Dependencies | None | Bun, Playwright binary (~58MB) |
269
- | Browser testing | Playwright MCP | Custom Playwright daemon |
270
- | Pipeline docs | Auto-generated chain (01→07) | Per-skill output |
271
- | Agents | 11 (5 opus, 6 sonnet) | 34 |
272
- | Customization | Edit .md directly, never overwritten | May overwrite on update |
273
-
274
261
  ## License
275
262
 
276
263
  MIT
package/bin/setup.js CHANGED
@@ -3,14 +3,13 @@
3
3
  import { readdir, copyFile, mkdir, readFile, writeFile, access } from "fs/promises";
4
4
  import { join, dirname } from "path";
5
5
  import { fileURLToPath } from "url";
6
- import { createInterface } from "readline";
7
6
 
8
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
8
  const AGENTS_SRC = join(__dirname, "..", "agents");
10
9
  const TEMPLATES_SRC = join(__dirname, "..", "templates");
11
10
  const TARGET_DIR = join(process.cwd(), ".claude", "agents");
12
11
  const HARNESS_DIR = join(process.cwd(), ".claude", "harness");
13
- const VERSION = "1.0.0";
12
+ const VERSION = "1.1.0";
14
13
 
15
14
  const RESET = "\x1b[0m";
16
15
  const BOLD = "\x1b[1m";
@@ -41,246 +40,330 @@ const TEMPLATES = {
41
40
  "env-vars": { file: "env-vars.md", desc: "Environment variables & secrets guide", category: "system" },
42
41
  };
43
42
 
44
- // ─── Interactive prompt ───
45
-
46
- function createPrompt() {
47
- const rl = createInterface({ input: process.stdin, output: process.stdout });
48
- const ask = (q) => new Promise(resolve => rl.question(q, resolve));
49
- const close = () => rl.close();
50
- return { ask, close };
51
- }
52
-
53
- // ─── Auto-detect project info ───
43
+ // ─── Deep auto-detect ───
54
44
 
55
45
  async function detectProject() {
56
- const info = { name: "", stack: [], framework: "", hasTS: false, hasTailwind: false, hasI18n: false, hasAuth: false, hasDB: false };
46
+ const info = {
47
+ name: "", description: "", stack: [], framework: "",
48
+ hasTS: false, hasTailwind: false, hasI18n: false, hasAuth: false, hasDB: false,
49
+ hasPayments: false, hasAI: false, deploy: "", dbName: "", authName: "", paymentName: "",
50
+ components: [], apiRoutes: [], locales: [],
51
+ };
52
+
53
+ // package.json
57
54
  try {
58
55
  const pkg = JSON.parse(await readFile(join(process.cwd(), "package.json"), "utf8"));
59
56
  info.name = pkg.name || "";
57
+ info.description = pkg.description || "";
60
58
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
59
+
60
+ // Framework
61
61
  if (allDeps["next"]) { info.framework = "Next.js"; info.stack.push("Next.js"); }
62
62
  else if (allDeps["nuxt"]) { info.framework = "Nuxt"; info.stack.push("Nuxt"); }
63
63
  else if (allDeps["react"]) { info.framework = "React"; info.stack.push("React"); }
64
64
  else if (allDeps["vue"]) { info.framework = "Vue"; info.stack.push("Vue"); }
65
65
  else if (allDeps["svelte"] || allDeps["@sveltejs/kit"]) { info.framework = "SvelteKit"; info.stack.push("SvelteKit"); }
66
66
  else if (allDeps["express"]) { info.framework = "Express"; info.stack.push("Express"); }
67
+
68
+ // Core
67
69
  if (allDeps["typescript"]) { info.hasTS = true; info.stack.push("TypeScript"); }
68
70
  if (allDeps["tailwindcss"]) { info.hasTailwind = true; info.stack.push("TailwindCSS"); }
69
- if (allDeps["next-intl"] || allDeps["i18next"] || allDeps["react-intl"]) { info.hasI18n = true; info.stack.push("i18n"); }
70
- if (allDeps["@supabase/supabase-js"]) { info.hasDB = true; info.stack.push("Supabase"); }
71
- else if (allDeps["prisma"] || allDeps["@prisma/client"]) { info.hasDB = true; info.stack.push("Prisma"); }
72
- else if (allDeps["drizzle-orm"]) { info.hasDB = true; info.stack.push("Drizzle"); }
73
- if (allDeps["next-auth"] || allDeps["@auth/core"] || allDeps["@supabase/auth-helpers-nextjs"]) { info.hasAuth = true; info.stack.push("Auth"); }
74
- if (allDeps["stripe"] || allDeps["@paddle/paddle-js"]) { info.stack.push("Payments"); }
71
+ if (allDeps["framer-motion"]) info.stack.push("Framer Motion");
72
+
73
+ // i18n
74
+ if (allDeps["next-intl"] || allDeps["i18next"] || allDeps["react-intl"] || allDeps["next-i18next"]) {
75
+ info.hasI18n = true; info.stack.push("i18n");
76
+ }
77
+
78
+ // DB
79
+ if (allDeps["@supabase/supabase-js"]) { info.hasDB = true; info.dbName = "Supabase"; info.stack.push("Supabase"); }
80
+ else if (allDeps["prisma"] || allDeps["@prisma/client"]) { info.hasDB = true; info.dbName = "Prisma"; info.stack.push("Prisma"); }
81
+ else if (allDeps["drizzle-orm"]) { info.hasDB = true; info.dbName = "Drizzle"; info.stack.push("Drizzle"); }
82
+ else if (allDeps["mongoose"]) { info.hasDB = true; info.dbName = "MongoDB"; info.stack.push("MongoDB"); }
83
+
84
+ // Auth
85
+ if (allDeps["next-auth"] || allDeps["@auth/core"]) { info.hasAuth = true; info.authName = "NextAuth"; info.stack.push("NextAuth"); }
86
+ else if (allDeps["@supabase/auth-helpers-nextjs"] || allDeps["@supabase/ssr"]) { info.hasAuth = true; info.authName = "Supabase Auth"; info.stack.push("Supabase Auth"); }
87
+ else if (allDeps["firebase"]) { info.hasAuth = true; info.authName = "Firebase Auth"; info.stack.push("Firebase"); }
88
+
89
+ // Payments
90
+ if (allDeps["stripe"]) { info.hasPayments = true; info.paymentName = "Stripe"; info.stack.push("Stripe"); }
91
+ else if (allDeps["@paddle/paddle-js"]) { info.hasPayments = true; info.paymentName = "Paddle"; info.stack.push("Paddle"); }
92
+ else if (allDeps["@tosspayments/payment-sdk"]) { info.hasPayments = true; info.paymentName = "Toss Payments"; info.stack.push("Toss Payments"); }
93
+
94
+ // AI
95
+ if (allDeps["openai"]) { info.hasAI = true; info.stack.push("OpenAI"); }
96
+ else if (allDeps["@anthropic-ai/sdk"]) { info.hasAI = true; info.stack.push("Anthropic"); }
97
+ else if (allDeps["@google/generative-ai"]) { info.hasAI = true; info.stack.push("Google AI"); }
75
98
  } catch {}
76
- return info;
77
- }
78
99
 
79
- // ─── Add: copy template to harness ───
100
+ // Detect deploy from vercel.json / netlify.toml / fly.toml
101
+ if (await exists(join(process.cwd(), "vercel.json")) || await exists(join(process.cwd(), ".vercel"))) info.deploy = "Vercel";
102
+ else if (await exists(join(process.cwd(), "netlify.toml"))) info.deploy = "Netlify";
103
+ else if (await exists(join(process.cwd(), "fly.toml"))) info.deploy = "Fly.io";
104
+ else if (await exists(join(process.cwd(), "Dockerfile"))) info.deploy = "Docker";
80
105
 
81
- async function runAdd(type, force) {
82
- if (!type) {
83
- log(`\n ${BOLD}Available harness templates:${RESET}\n`);
84
- const categories = { core: "Core (auto-created by init)", data: "Data", system: "System", design: "Design", domain: "Domain" };
85
- for (const [cat, label] of Object.entries(categories)) {
86
- const items = Object.entries(TEMPLATES).filter(([, v]) => v.category === cat);
87
- if (items.length === 0) continue;
88
- log(` ${CYAN}${label}${RESET}`);
89
- for (const [name, info] of items) {
90
- const installed = await exists(join(HARNESS_DIR, `${name}.md`));
91
- const status = installed ? `${GREEN}installed${RESET}` : `${DIM}not installed${RESET}`;
92
- log(` ${BOLD}${name.padEnd(16)}${RESET} ${status} ${DIM}${info.desc}${RESET}`);
93
- }
94
- log("");
106
+ // Scan components
107
+ try {
108
+ const compsDir = join(process.cwd(), "src", "components");
109
+ if (await exists(compsDir)) {
110
+ const files = await readdir(compsDir);
111
+ info.components = files.filter(f => f.endsWith(".tsx") || f.endsWith(".vue") || f.endsWith(".svelte")).map(f => f.replace(/\.\w+$/, ""));
95
112
  }
96
- log(` ${BOLD}Usage:${RESET} npx buildcrew add ${CYAN}<template>${RESET}`);
97
- log(` ${BOLD}Example:${RESET} npx buildcrew add erd\n`);
98
- return;
99
- }
100
-
101
- const template = TEMPLATES[type];
102
- if (!template) {
103
- log(`\n ${RED}Unknown template: ${type}${RESET}`);
104
- log(` ${DIM}Run ${BOLD}npx buildcrew add${RESET}${DIM} to see available templates.${RESET}\n`);
105
- return;
106
- }
113
+ } catch {}
107
114
 
108
- if (!template.file) {
109
- log(`\n ${YELLOW}${type}.md is auto-generated by ${BOLD}npx buildcrew init${RESET}`);
110
- log(` ${DIM}Run init to create it, or create .claude/harness/${type}.md manually.${RESET}\n`);
111
- return;
112
- }
115
+ // Scan API routes
116
+ try {
117
+ const apiDir = join(process.cwd(), "src", "app", "api");
118
+ if (await exists(apiDir)) {
119
+ const scanDir = async (dir, prefix = "") => {
120
+ const entries = await readdir(dir, { withFileTypes: true });
121
+ const routes = [];
122
+ for (const e of entries) {
123
+ if (e.isDirectory()) routes.push(...await scanDir(join(dir, e.name), `${prefix}/${e.name}`));
124
+ else if (e.name === "route.ts" || e.name === "route.js") routes.push(prefix || "/");
125
+ }
126
+ return routes;
127
+ };
128
+ info.apiRoutes = await scanDir(apiDir);
129
+ }
130
+ } catch {}
113
131
 
114
- const target = join(HARNESS_DIR, `${type}.md`);
115
- if ((await exists(target)) && !force) {
116
- log(`\n ${YELLOW}${type}.md already exists.${RESET} Use ${BOLD}--force${RESET} to overwrite.\n`);
117
- return;
118
- }
132
+ // Scan i18n locales
133
+ try {
134
+ for (const localeDir of ["src/i18n/dictionaries", "src/locales", "locales", "messages", "public/locales"]) {
135
+ const dir = join(process.cwd(), localeDir);
136
+ if (await exists(dir)) {
137
+ const files = await readdir(dir);
138
+ info.locales = files.filter(f => f.endsWith(".json")).map(f => f.replace(".json", ""));
139
+ break;
140
+ }
141
+ }
142
+ } catch {}
119
143
 
120
- await mkdir(HARNESS_DIR, { recursive: true });
121
- await copyFile(join(TEMPLATES_SRC, template.file), target);
122
- log(`\n ${GREEN} + ${RESET} .claude/harness/${type}.md`);
123
- log(` ${DIM}Edit this file to fill in your project details.${RESET}`);
124
- log(` ${DIM}All agents will read it automatically.${RESET}\n`);
144
+ return info;
125
145
  }
126
146
 
127
- // ─── Init: Harness Engineering ───
147
+ // ─── Init: Zero-question harness generation ───
128
148
 
129
149
  async function runInit(force) {
130
- log(`\n ${BOLD}buildcrew init${RESET} — Harness Engineering Setup\n`);
131
- log(` ${DIM}Setting up project context and team rules.${RESET}`);
132
- log(` ${DIM}All 11 agents will read these before every task.${RESET}\n`);
150
+ log(`\n ${BOLD}buildcrew init${RESET} v${VERSION}\n`);
133
151
 
134
152
  if ((await exists(join(HARNESS_DIR, "project.md"))) && !force) {
135
153
  log(` ${YELLOW}Harness already exists at .claude/harness/${RESET}`);
136
- log(` ${DIM}Use ${BOLD}npx buildcrew init --force${RESET}${DIM} to overwrite.${RESET}\n`);
154
+ log(` ${DIM}Use ${BOLD}--force${RESET}${DIM} to regenerate. Or just edit the files directly.${RESET}\n`);
137
155
  return;
138
156
  }
139
157
 
140
- const detected = await detectProject();
141
- const { ask, close } = createPrompt();
158
+ log(` ${DIM}Scanning project...${RESET}\n`);
159
+ const d = await detectProject();
142
160
 
143
- try {
144
- log(` ${CYAN}${BOLD}[1/3] Project Context${RESET}\n`);
145
- const name = (await ask(` ${BOLD}Project name${RESET} ${DIM}(${detected.name || "none"})${RESET}: `)) || detected.name || "my-project";
146
- const description = await ask(` ${BOLD}What does this project do?${RESET} (1 sentence): `);
147
- const stackAuto = detected.stack.length > 0 ? detected.stack.join(", ") : "not detected";
148
- const stackExtra = await ask(` ${BOLD}Tech stack${RESET} ${DIM}(detected: ${stackAuto})${RESET}\n ${DIM}Add anything missing (comma-separated, or Enter):${RESET} `);
149
- const deployTarget = await ask(` ${BOLD}Deploy target${RESET} ${DIM}(Vercel, AWS, etc.)${RESET}: `);
150
- const prodUrl = await ask(` ${BOLD}Production URL${RESET} ${DIM}(or Enter to skip)${RESET}: `);
151
-
152
- log(`\n ${CYAN}${BOLD}[2/3] Team Rules${RESET}\n`);
153
- const conventions = await ask(` ${BOLD}Coding conventions${RESET}\n ${DIM}(e.g., "functional components only, no default exports")${RESET}\n : `);
154
- const priorities = await ask(` ${BOLD}What to prioritize${RESET}\n ${DIM}(e.g., "UX over performance, security first")${RESET}\n : `);
155
- const avoid = await ask(` ${BOLD}What to avoid${RESET}\n ${DIM}(e.g., "no class components, no any types")${RESET}\n : `);
156
- const quality = await ask(` ${BOLD}Quality standards${RESET}\n ${DIM}(e.g., "all code must pass tsc, no console.log")${RESET}\n : `);
157
- const reviewRules = await ask(` ${BOLD}Review rules${RESET}\n ${DIM}(e.g., "always check auth on API routes, mobile-first")${RESET}\n : `);
158
-
159
- log(`\n ${CYAN}${BOLD}[3/3] Domain Knowledge${RESET}\n`);
160
- const domain = await ask(` ${BOLD}Domain${RESET} ${DIM}(e.g., "e-commerce", "fintech")${RESET}: `);
161
- const userTypes = await ask(` ${BOLD}User types${RESET} ${DIM}(e.g., "free, premium, admin")${RESET}: `);
162
- const keyTerms = await ask(` ${BOLD}Key terms${RESET}\n ${DIM}(e.g., "reading=tarot session, spread=card layout")${RESET}\n : `);
163
- const businessRules = await ask(` ${BOLD}Business rules${RESET}\n ${DIM}(e.g., "free=3 reads/day, premium=unlimited")${RESET}\n : `);
164
-
165
- close();
166
-
167
- await mkdir(HARNESS_DIR, { recursive: true });
168
- const fullStack = [...detected.stack, ...(stackExtra ? stackExtra.split(",").map(s => s.trim()) : [])].filter(Boolean);
169
-
170
- const projectMd = `# Project: ${name}
161
+ await mkdir(HARNESS_DIR, { recursive: true });
162
+
163
+ // ─── project.md ───
164
+ const projectMd = `# Project: ${d.name || "my-project"}
171
165
 
172
166
  ## Overview
173
- ${description || "[Edit: describe your project]"}
167
+ ${d.description || "<!-- Describe what this project does in 1-2 sentences -->"}
174
168
 
175
169
  ## Tech Stack
176
- ${fullStack.map(s => `- ${s}`).join("\n") || "- [Edit: add your tech stack]"}
170
+ ${d.stack.map(s => `- ${s}`).join("\n") || "<!-- Add your tech stack -->"}
177
171
 
178
172
  ## Framework
179
- ${detected.framework || "[Not detected]"}
173
+ ${d.framework || "<!-- Not detected -->"}
180
174
 
181
175
  ## Infrastructure
182
- - **Deploy**: ${deployTarget || "[Edit]"}
183
- - **Production URL**: ${prodUrl || "[Edit]"}
184
- - **TypeScript**: ${detected.hasTS ? "Yes" : "No"}
185
- - **CSS**: ${detected.hasTailwind ? "TailwindCSS" : "[Edit]"}
186
- - **i18n**: ${detected.hasI18n ? "Yes" : "No"}
187
- - **Auth**: ${detected.hasAuth ? "Yes" : "No"}
188
- - **Database**: ${detected.hasDB ? "Yes" : "No"}
176
+ - **Deploy**: ${d.deploy || "<!-- Vercel / AWS / Netlify / etc. -->"}
177
+ - **Production URL**: <!-- https://your-app.com -->
178
+ - **TypeScript**: ${d.hasTS ? "Yes" : "No"}
179
+ - **CSS**: ${d.hasTailwind ? "TailwindCSS" : "<!-- Your CSS solution -->"}
180
+ - **i18n**: ${d.hasI18n ? `Yes (${d.locales.join(", ")})` : "No"}
181
+ - **Auth**: ${d.hasAuth ? d.authName : "No"}
182
+ - **Database**: ${d.hasDB ? d.dbName : "No"}
183
+ - **Payments**: ${d.hasPayments ? d.paymentName : "No"}
184
+ - **AI**: ${d.hasAI ? d.stack.find(s => ["OpenAI","Anthropic","Google AI"].includes(s)) || "Yes" : "No"}
185
+
186
+ ## Key Components
187
+ ${d.components.length > 0 ? d.components.map(c => `- ${c}`).join("\n") : "<!-- List your main components -->"}
188
+
189
+ ## API Routes
190
+ ${d.apiRoutes.length > 0 ? d.apiRoutes.map(r => `- \`/api${r}\``).join("\n") : "<!-- List your API endpoints -->"}
189
191
 
190
192
  ## Domain
191
- - **Industry**: ${domain || "[Edit]"}
192
- - **User types**: ${userTypes || "[Edit]"}
193
+ - **Industry**: <!-- e.g., e-commerce, fintech, entertainment -->
194
+ - **User types**: <!-- e.g., free users, premium users, admins -->
193
195
 
194
196
  ## Key Domain Terms
195
- ${keyTerms ? keyTerms.split(",").map(t => `- ${t.trim()}`).join("\n") : "- [Edit: add domain-specific terms]"}
197
+ <!-- Define project-specific terms so agents use consistent language -->
198
+ <!-- - term = definition -->
196
199
 
197
200
  ## Business Rules
198
- ${businessRules ? businessRules.split(",").map(r => `- ${r.trim()}`).join("\n") : "- [Edit: add business rules]"}
201
+ <!-- Rules that affect feature development -->
202
+ <!-- - Free users: 3 actions per day -->
203
+ <!-- - Premium: unlimited access -->
199
204
  `;
200
205
 
201
- const rulesMd = `# Team Rules
206
+ // ─── rules.md ───
207
+ const rulesMd = `# Team Rules
202
208
 
203
- These rules apply to ALL agents. Every agent reads this before starting work.
209
+ All agents read this before every task. Edit freely.
204
210
 
205
211
  ## Coding Conventions
206
- ${conventions ? conventions.split(",").map(c => `- ${c.trim()}`).join("\n") : "- Follow existing codebase patterns"}
212
+ ${d.framework === "Next.js" ? `- Use App Router patterns
213
+ - Server components by default, "use client" only when needed
214
+ - Functional components only` : "<!-- Add your coding conventions -->"}
215
+ ${d.hasTS ? "- Strict TypeScript — no \`any\` types" : ""}
216
+ ${d.hasTailwind ? "- Use TailwindCSS utility classes, avoid custom CSS" : ""}
217
+ - No \`console.log\` in production code
218
+ - No default exports (except pages/layouts)
207
219
 
208
220
  ## Priorities
209
- ${priorities ? priorities.split(",").map(p => `- ${p.trim()}`).join("\n") : "- [Edit: set your priorities]"}
221
+ - UX over premature optimization
222
+ - Mobile-first responsive design
223
+ ${d.hasI18n ? `- i18n: all user-facing text must be in locale files (${d.locales.join(", ")})` : ""}
224
+ ${d.hasPayments ? "- Payment security: amounts validated server-side only" : ""}
210
225
 
211
226
  ## What to Avoid
212
- ${avoid ? avoid.split(",").map(a => `- ${a.trim()}`).join("\n") : "- [Edit: list things to avoid]"}
227
+ - Class components
228
+ - \`any\` type assertions
229
+ - Inline styles (use Tailwind or CSS modules)
230
+ - Hardcoded strings in UI (use i18n)
231
+ - Direct DB access from client components
213
232
 
214
233
  ## Quality Standards
215
- ${quality ? quality.split(",").map(q => `- ${q.trim()}`).join("\n") : "- All code must pass type checker and linter\n- No debug logs in production"}
234
+ ${d.hasTS ? "- \`npx tsc --noEmit\` must pass" : ""}
235
+ - \`npm run lint\` must pass
236
+ - \`npm run build\` must pass
237
+ ${d.hasI18n && d.locales.length > 0 ? `- All ${d.locales.length} locale files must be in sync` : ""}
238
+ - No unused imports or variables
216
239
 
217
240
  ## Review Standards
218
- ${reviewRules ? reviewRules.split(",").map(r => `- ${r.trim()}`).join("\n") : "- [Edit: add review rules]"}
241
+ ${d.hasAuth ? "- All API routes must check authentication" : ""}
242
+ ${d.hasPayments ? "- Payment mutations must be server-side and atomic" : ""}
243
+ ${d.hasAI ? "- AI-generated content treated as untrusted (sanitize before render)" : ""}
244
+ - Check for XSS on any user input rendering
245
+ - Verify responsive layout at 375px, 768px, 1440px
219
246
  `;
220
247
 
221
- await writeFile(join(HARNESS_DIR, "project.md"), projectMd);
222
- await writeFile(join(HARNESS_DIR, "rules.md"), rulesMd);
223
-
224
- log(`\n ${GREEN}${BOLD}Harness created!${RESET}\n`);
225
- log(` ${GREEN} + ${RESET} .claude/harness/project.md`);
226
- log(` ${GREEN} + ${RESET} .claude/harness/rules.md`);
227
-
228
- // Suggest additional templates
229
- log(`\n ${BOLD}Add more context with templates:${RESET}\n`);
230
- const suggestions = [
231
- ["erd", "Database schema & relationships"],
232
- ["architecture", "System architecture overview"],
233
- ["api-spec", "API endpoints & contracts"],
234
- ["design-system", "Colors, typography, components"],
235
- ["glossary", "Domain terms & user roles"],
236
- ["user-flow", "User journeys & page map"],
237
- ["env-vars", "Environment variables guide"],
238
- ];
239
- for (const [name, desc] of suggestions) {
240
- log(` ${DIM}npx buildcrew add ${CYAN}${name.padEnd(15)}${RESET} ${DIM}${desc}${RESET}`);
248
+ await writeFile(join(HARNESS_DIR, "project.md"), projectMd);
249
+ await writeFile(join(HARNESS_DIR, "rules.md"), rulesMd);
250
+
251
+ // Auto-add relevant templates
252
+ let templateCount = 0;
253
+ const autoTemplates = [];
254
+ if (d.hasDB) autoTemplates.push("erd");
255
+ if (d.apiRoutes.length > 0) autoTemplates.push("api-spec");
256
+ if (d.hasTailwind) autoTemplates.push("design-system");
257
+ if (d.hasI18n || d.components.length > 5) autoTemplates.push("user-flow");
258
+ autoTemplates.push("architecture");
259
+
260
+ for (const name of autoTemplates) {
261
+ const t = TEMPLATES[name];
262
+ if (t && t.file) {
263
+ await copyFile(join(TEMPLATES_SRC, t.file), join(HARNESS_DIR, `${name}.md`));
264
+ templateCount++;
241
265
  }
266
+ }
242
267
 
243
- log(`\n ${DIM}Or create any .md file in .claude/harness/ — agents read them all.${RESET}\n`);
268
+ // Print results
269
+ log(` ${GREEN}${BOLD}Harness generated!${RESET} (${2 + templateCount} files)\n`);
270
+ log(` ${GREEN} + ${RESET} project.md ${DIM}auto-detected from package.json${RESET}`);
271
+ log(` ${GREEN} + ${RESET} rules.md ${DIM}smart defaults for ${d.framework || "your stack"}${RESET}`);
272
+ for (const name of autoTemplates) {
273
+ log(` ${GREEN} + ${RESET} ${(name + ".md").padEnd(17)}${DIM}${TEMPLATES[name].desc}${RESET}`);
274
+ }
244
275
 
245
- if (!(await exists(TARGET_DIR))) {
246
- log(` ${YELLOW}Agents not installed yet.${RESET} Run ${BOLD}npx buildcrew${RESET} first.\n`);
276
+ // Show what was detected
277
+ log(`\n ${BOLD}Detected:${RESET}`);
278
+ if (d.stack.length > 0) log(` ${CYAN}Stack${RESET} ${d.stack.join(", ")}`);
279
+ if (d.deploy) log(` ${CYAN}Deploy${RESET} ${d.deploy}`);
280
+ if (d.components.length > 0) log(` ${CYAN}Components${RESET} ${d.components.length} found`);
281
+ if (d.apiRoutes.length > 0) log(` ${CYAN}API Routes${RESET} ${d.apiRoutes.length} found`);
282
+ if (d.locales.length > 0) log(` ${CYAN}Locales${RESET} ${d.locales.join(", ")}`);
283
+
284
+ // Available extras
285
+ const remaining = Object.entries(TEMPLATES).filter(([name, t]) => t.file && !autoTemplates.includes(name));
286
+ if (remaining.length > 0) {
287
+ log(`\n ${BOLD}Add more:${RESET}`);
288
+ for (const [name, t] of remaining) {
289
+ log(` ${DIM}npx buildcrew add ${CYAN}${name}${RESET}`);
247
290
  }
291
+ }
292
+
293
+ log(`\n ${BOLD}Next step:${RESET} Edit ${CYAN}.claude/harness/*.md${RESET} to fill in project-specific details.`);
294
+ log(` ${DIM}Look for <!-- comments --> — those are the parts to customize.${RESET}\n`);
295
+ }
296
+
297
+ // ─── Add: copy template ───
248
298
 
249
- } catch (err) {
250
- close();
251
- throw err;
299
+ async function runAdd(type, force) {
300
+ if (!type) {
301
+ log(`\n ${BOLD}Available harness templates:${RESET}\n`);
302
+ const categories = { core: "Core (auto-created by init)", data: "Data", system: "System", design: "Design", domain: "Domain" };
303
+ for (const [cat, label] of Object.entries(categories)) {
304
+ const items = Object.entries(TEMPLATES).filter(([, v]) => v.category === cat);
305
+ if (items.length === 0) continue;
306
+ log(` ${CYAN}${label}${RESET}`);
307
+ for (const [name, info] of items) {
308
+ const installed = await exists(join(HARNESS_DIR, `${name}.md`));
309
+ const status = installed ? `${GREEN}installed${RESET}` : `${DIM}not installed${RESET}`;
310
+ log(` ${BOLD}${name.padEnd(16)}${RESET} ${status} ${DIM}${info.desc}${RESET}`);
311
+ }
312
+ log("");
313
+ }
314
+ log(` ${BOLD}Usage:${RESET} npx buildcrew add ${CYAN}<template>${RESET}\n`);
315
+ return;
316
+ }
317
+
318
+ const template = TEMPLATES[type];
319
+ if (!template) {
320
+ log(`\n ${RED}Unknown template: ${type}${RESET}`);
321
+ log(` ${DIM}Run ${BOLD}npx buildcrew add${RESET}${DIM} to see available templates.${RESET}\n`);
322
+ return;
252
323
  }
324
+
325
+ if (!template.file) {
326
+ log(`\n ${YELLOW}${type}.md is auto-generated by ${BOLD}npx buildcrew init${RESET}\n`);
327
+ return;
328
+ }
329
+
330
+ const target = join(HARNESS_DIR, `${type}.md`);
331
+ if ((await exists(target)) && !force) {
332
+ log(`\n ${YELLOW}${type}.md already exists.${RESET} Use ${BOLD}--force${RESET} to overwrite.\n`);
333
+ return;
334
+ }
335
+
336
+ await mkdir(HARNESS_DIR, { recursive: true });
337
+ await copyFile(join(TEMPLATES_SRC, template.file), target);
338
+ log(`\n ${GREEN} + ${RESET} .claude/harness/${type}.md`);
339
+ log(` ${DIM}Edit to fill in your project details. Agents read it automatically.${RESET}\n`);
253
340
  }
254
341
 
255
342
  // ─── Harness status ───
256
343
 
257
344
  async function runHarnessStatus() {
258
- log(`\n ${BOLD}Harness files${RESET} ${DIM}(.claude/harness/)${RESET}\n`);
345
+ log(`\n ${BOLD}Harness${RESET} ${DIM}(.claude/harness/)${RESET}\n`);
259
346
 
260
347
  if (!(await exists(HARNESS_DIR))) {
261
- log(` ${YELLOW}No harness directory found.${RESET}`);
262
- log(` ${DIM}Run ${BOLD}npx buildcrew init${RESET}${DIM} to get started.${RESET}\n`);
348
+ log(` ${YELLOW}No harness found.${RESET} Run ${BOLD}npx buildcrew init${RESET}\n`);
263
349
  return;
264
350
  }
265
351
 
266
352
  const files = (await readdir(HARNESS_DIR)).filter(f => f.endsWith(".md")).sort();
267
- if (files.length === 0) {
268
- log(` ${YELLOW}Harness directory is empty.${RESET}\n`);
269
- return;
270
- }
353
+ if (files.length === 0) { log(` ${YELLOW}Empty.${RESET}\n`); return; }
271
354
 
272
355
  for (const file of files) {
273
356
  const name = file.replace(".md", "");
274
- const isTemplate = TEMPLATES[name];
275
357
  const content = await readFile(join(HARNESS_DIR, file), "utf8");
276
358
  const lines = content.split("\n").length;
277
- const hasEdits = !content.includes("[Edit");
278
- const status = hasEdits ? `${GREEN}configured${RESET}` : `${YELLOW}needs editing${RESET}`;
279
- const desc = isTemplate ? `${DIM}${isTemplate.desc}${RESET}` : `${DIM}(custom)${RESET}`;
280
- log(` ${BOLD}${name.padEnd(16)}${RESET} ${status} ${lines} lines ${desc}`);
359
+ const hasComments = content.includes("<!--");
360
+ const status = hasComments ? `${YELLOW}needs editing${RESET}` : `${GREEN}configured${RESET}`;
361
+ const t = TEMPLATES[name];
362
+ const desc = t ? `${DIM}${t.desc}${RESET}` : `${DIM}(custom)${RESET}`;
363
+ log(` ${BOLD}${name.padEnd(16)}${RESET} ${status} ${DIM}${lines} lines${RESET} ${desc}`);
281
364
  }
282
365
 
283
- log(`\n ${DIM}Agents read ALL .md files here. Add any file you want.${RESET}\n`);
366
+ log(`\n ${DIM}Edit files to replace <!-- comments --> with your content.${RESET}\n`);
284
367
  }
285
368
 
286
369
  // ─── Install agents ───
@@ -288,78 +371,48 @@ async function runHarnessStatus() {
288
371
  async function runInstall(force) {
289
372
  const files = (await readdir(AGENTS_SRC)).filter(f => f.endsWith(".md"));
290
373
  log(`\n ${BOLD}buildcrew${RESET} v${VERSION}\n ${DIM}11 AI agents for Claude Code${RESET}\n`);
291
- log(`${DIM} Installing to ${TARGET_DIR}${RESET}\n`);
292
374
 
293
375
  await mkdir(TARGET_DIR, { recursive: true });
294
376
  let installed = 0, skipped = 0;
295
377
 
296
378
  for (const file of files) {
297
379
  const target = join(TARGET_DIR, file);
298
- if ((await exists(target)) && !force) {
299
- log(` ${YELLOW}skip${RESET} ${file} ${DIM}(exists, use --force)${RESET}`);
300
- skipped++;
301
- continue;
302
- }
380
+ if ((await exists(target)) && !force) { skipped++; continue; }
303
381
  await copyFile(join(AGENTS_SRC, file), target);
304
- log(` ${GREEN} + ${RESET} ${file}`);
382
+ log(` ${GREEN} + ${RESET} ${file}`);
305
383
  installed++;
306
384
  }
307
385
 
308
386
  log("");
309
- if (installed > 0) {
310
- log(` ${GREEN}${BOLD}Done!${RESET} ${installed} agents installed.${skipped > 0 ? ` ${skipped} skipped.` : ""}\n`);
311
- } else {
312
- log(` ${YELLOW}All agents already exist.${RESET} Use ${BOLD}--force${RESET} to overwrite.\n`);
313
- }
387
+ if (installed > 0) log(` ${GREEN}${BOLD}Done!${RESET} ${installed} agents installed.${skipped > 0 ? ` ${skipped} skipped.` : ""}\n`);
388
+ else log(` ${YELLOW}All agents already exist.${RESET} Use ${BOLD}--force${RESET} to overwrite.\n`);
314
389
 
315
- const hasHarness = await exists(join(HARNESS_DIR, "project.md"));
316
- if (!hasHarness) {
317
- log(` ${CYAN}Next:${RESET} Run ${BOLD}npx buildcrew init${RESET} to set up your project harness.\n`);
318
- }
319
-
320
- let hasPlaywright = false;
321
- try {
322
- const settingsPath = join(process.env.HOME, ".claude", "settings.json");
323
- if (await exists(settingsPath)) {
324
- hasPlaywright = (await readFile(settingsPath, "utf8")).includes("playwright");
325
- }
326
- } catch {}
327
- if (!hasPlaywright) {
328
- log(` ${YELLOW}Optional:${RESET} Playwright MCP not detected.`);
329
- log(` ${DIM}Setup: claude mcp add playwright -- npx @anthropic-ai/mcp-server-playwright${RESET}\n`);
390
+ if (!(await exists(join(HARNESS_DIR, "project.md")))) {
391
+ log(` ${CYAN}Next:${RESET} ${BOLD}npx buildcrew init${RESET} — auto-generates project harness from your codebase.\n`);
330
392
  }
331
393
 
332
- log(` ${BOLD}Quick start:${RESET} @constitution [your request]\n`);
394
+ log(` ${BOLD}Start:${RESET} @constitution [your request]\n`);
333
395
  }
334
396
 
335
- // ─── List agents ───
397
+ // ─── List / Uninstall ───
336
398
 
337
399
  async function runList() {
338
400
  const files = (await readdir(AGENTS_SRC)).filter(f => f.endsWith(".md"));
339
401
  log(`\n ${BOLD}buildcrew${RESET} v${VERSION} — 11 agents\n`);
340
402
  for (const file of files) {
341
403
  const content = await readFile(join(AGENTS_SRC, file), "utf8");
342
- const nameMatch = content.match(/^name:\s*(.+)$/m);
343
- const descMatch = content.match(/^description:\s*(.+)$/m);
344
- const modelMatch = content.match(/^model:\s*(.+)$/m);
345
- const name = nameMatch ? nameMatch[1] : file.replace(".md", "");
346
- const desc = descMatch ? descMatch[1] : "";
347
- const model = modelMatch ? modelMatch[1] : "sonnet";
404
+ const name = (content.match(/^name:\s*(.+)$/m) || [])[1] || file.replace(".md", "");
405
+ const desc = (content.match(/^description:\s*(.+)$/m) || [])[1] || "";
406
+ const model = (content.match(/^model:\s*(.+)$/m) || [])[1] || "sonnet";
348
407
  const modelTag = model === "opus" ? `${MAGENTA}opus${RESET}` : `${DIM}sonnet${RESET}`;
349
- const color = name === "constitution" ? BOLD : "";
350
- log(` ${color}${name.padEnd(20)}${RESET} ${modelTag} ${DIM}${desc.slice(0, 55)}${RESET}`);
408
+ log(` ${name === "constitution" ? BOLD : ""}${name.padEnd(20)}${RESET} ${modelTag} ${DIM}${desc.slice(0, 55)}${RESET}`);
351
409
  }
352
410
  log("");
353
411
  }
354
412
 
355
- // ─── Uninstall ───
356
-
357
413
  async function runUninstall() {
358
414
  const files = (await readdir(AGENTS_SRC)).filter(f => f.endsWith(".md"));
359
- if (!(await exists(TARGET_DIR))) {
360
- log(`${YELLOW}No .claude/agents/ directory found.${RESET}`);
361
- return;
362
- }
415
+ if (!(await exists(TARGET_DIR))) { log(`${YELLOW}No agents found.${RESET}`); return; }
363
416
  let removed = 0;
364
417
  for (const file of files) {
365
418
  const target = join(TARGET_DIR, file);
@@ -376,38 +429,30 @@ async function main() {
376
429
  const subcommand = args[1];
377
430
  const force = args.includes("--force") || args.includes("-f");
378
431
 
379
- if (args.includes("--version") || args.includes("-v")) {
380
- log(`buildcrew v${VERSION}`);
381
- return;
382
- }
432
+ if (args.includes("--version") || args.includes("-v")) { log(`buildcrew v${VERSION}`); return; }
383
433
 
384
434
  if (args.includes("--help") || args.includes("-h")) {
385
435
  log(`
386
436
  ${BOLD}buildcrew${RESET} v${VERSION} — 11 AI agents for Claude Code
387
437
 
388
438
  ${BOLD}Commands:${RESET}
389
- npx buildcrew Install agents
390
- npx buildcrew init Set up project harness (interactive)
391
- npx buildcrew add List available harness templates
392
- npx buildcrew add <template> Add a harness template
393
- npx buildcrew harness Show harness status
439
+ npx buildcrew Install agents
440
+ npx buildcrew init Auto-generate project harness (zero questions)
441
+ npx buildcrew add List harness templates
442
+ npx buildcrew add <name> Add a harness template
443
+ npx buildcrew harness Show harness file status
394
444
 
395
445
  ${BOLD}Options:${RESET}
396
- --force, -f Overwrite existing files
397
- --list, -l List all agents
398
- --uninstall Remove installed agents
399
- --version, -v Show version
400
-
401
- ${BOLD}Setup (recommended):${RESET}
402
- ${GREEN}1.${RESET} npx buildcrew ${DIM}Install agent files${RESET}
403
- ${GREEN}2.${RESET} npx buildcrew init ${DIM}Project context + team rules${RESET}
404
- ${GREEN}3.${RESET} npx buildcrew add erd ${DIM}Add more context (optional)${RESET}
405
- ${GREEN}4.${RESET} @constitution [task] ${DIM}Start working${RESET}
406
-
407
- ${BOLD}Harness templates:${RESET}
408
- erd, architecture, api-spec, design-system,
409
- glossary, user-flow, env-vars
410
- ${DIM}Or create any .md file in .claude/harness/${RESET}
446
+ --force, -f Overwrite existing files
447
+ --list, -l List all agents
448
+ --uninstall Remove agents
449
+ --version Show version
450
+
451
+ ${BOLD}Setup:${RESET}
452
+ ${GREEN}1.${RESET} npx buildcrew ${DIM}Install agents${RESET}
453
+ ${GREEN}2.${RESET} npx buildcrew init ${DIM}Auto-generate harness from codebase${RESET}
454
+ ${GREEN}3.${RESET} Edit .claude/harness/ ${DIM}Customize (replace <!-- comments -->)${RESET}
455
+ ${GREEN}4.${RESET} @constitution [task] ${DIM}Start working${RESET}
411
456
 
412
457
  ${BOLD}More info:${RESET} https://github.com/z1nun/buildcrew
413
458
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildcrew",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "11 AI agents for Claude Code — auto-orchestrated dev team with 9 operating modes",
5
5
  "author": "z1nun",
6
6
  "license": "MIT",
@@ -11,6 +11,7 @@
11
11
  "files": [
12
12
  "bin/",
13
13
  "agents/",
14
+ "templates/",
14
15
  "README.md",
15
16
  "LICENSE"
16
17
  ],
@@ -0,0 +1,52 @@
1
+ # API Specification
2
+
3
+ ## Base URL
4
+ - **Development**: `http://localhost:3000/api`
5
+ - **Production**: `https://[your-domain]/api`
6
+
7
+ ## Authentication
8
+ - **Method**: [Bearer token / Cookie / API key]
9
+ - **Header**: `Authorization: Bearer <token>`
10
+
11
+ ## Endpoints
12
+
13
+ ### [Resource Name]
14
+
15
+ #### `GET /api/[resource]`
16
+ - **Auth**: Required / Public
17
+ - **Description**: [what it does]
18
+ - **Query Params**: `?page=1&limit=20`
19
+ - **Response 200**:
20
+ ```json
21
+ {
22
+ "data": [],
23
+ "total": 0
24
+ }
25
+ ```
26
+ - **Response 401**: Unauthorized
27
+
28
+ #### `POST /api/[resource]`
29
+ - **Auth**: Required
30
+ - **Body**:
31
+ ```json
32
+ {
33
+ "field": "value"
34
+ }
35
+ ```
36
+ - **Response 201**: Created
37
+ - **Response 400**: Validation error
38
+
39
+ ## Error Format
40
+ ```json
41
+ {
42
+ "error": "Error message",
43
+ "code": "ERROR_CODE"
44
+ }
45
+ ```
46
+
47
+ ## Rate Limits
48
+ - [e.g., "100 requests per minute per user"]
49
+ - [e.g., "AI endpoints: 10 requests per minute"]
50
+
51
+ ---
52
+ *Agents use this to implement correct API calls, validate endpoints during review, and test during QA.*
@@ -0,0 +1,49 @@
1
+ # Architecture
2
+
3
+ ## System Overview
4
+
5
+ ```
6
+ [Client] → [Frontend] → [API Layer] → [Database]
7
+ ↓ ↓
8
+ [Auth] [External APIs]
9
+ ```
10
+
11
+ ## Frontend
12
+ - **Framework**: [Next.js / React / Vue / etc.]
13
+ - **Routing**: [App Router / Pages Router / file-based]
14
+ - **State Management**: [React Context / Zustand / Redux / etc.]
15
+ - **Styling**: [TailwindCSS / CSS Modules / styled-components]
16
+
17
+ ## Backend / API
18
+ - **Type**: [Next.js API Routes / Express / serverless]
19
+ - **Authentication**: [Supabase Auth / NextAuth / custom]
20
+ - **Database**: [Supabase / PostgreSQL / MongoDB]
21
+ - **ORM**: [Prisma / Drizzle / Supabase client]
22
+
23
+ ## External Services
24
+ | Service | Purpose | Auth Method |
25
+ |---------|---------|-------------|
26
+ | [e.g., OpenAI] | [AI generation] | [API key] |
27
+ | [e.g., Stripe] | [Payments] | [API key + webhook] |
28
+
29
+ ## Directory Structure
30
+ ```
31
+ src/
32
+ ├── app/ [pages and API routes]
33
+ ├── components/ [React components]
34
+ ├── lib/ [utilities and helpers]
35
+ └── [other dirs]
36
+ ```
37
+
38
+ ## Key Patterns
39
+ - [e.g., "Server components by default, 'use client' only when needed"]
40
+ - [e.g., "All DB access through lib/supabase/ helpers"]
41
+ - [e.g., "API routes validate input with zod"]
42
+
43
+ ## Deploy
44
+ - **Platform**: [Vercel / AWS / etc.]
45
+ - **CI/CD**: [GitHub Actions / Vercel auto-deploy]
46
+ - **Environments**: [dev, staging, prod]
47
+
48
+ ---
49
+ *Agents use this to understand system boundaries, make architecture-consistent decisions, and avoid breaking existing patterns.*
@@ -0,0 +1,66 @@
1
+ # Design System
2
+
3
+ ## Colors
4
+
5
+ ### Brand
6
+ | Token | Value | Usage |
7
+ |-------|-------|-------|
8
+ | `primary` | [#hex] | CTAs, links, active states |
9
+ | `secondary` | [#hex] | Secondary buttons, borders |
10
+ | `accent` | [#hex] | Highlights, badges |
11
+
12
+ ### Semantic
13
+ | Token | Value | Usage |
14
+ |-------|-------|-------|
15
+ | `success` | [#hex] | Success messages, confirmations |
16
+ | `error` | [#hex] | Error messages, destructive actions |
17
+ | `warning` | [#hex] | Warnings, caution states |
18
+ | `info` | [#hex] | Informational messages |
19
+
20
+ ### Neutral
21
+ | Token | Value | Usage |
22
+ |-------|-------|-------|
23
+ | `bg` | [#hex] | Page background |
24
+ | `surface` | [#hex] | Card/modal backgrounds |
25
+ | `border` | [#hex] | Borders, dividers |
26
+ | `text` | [#hex] | Primary text |
27
+ | `text-muted` | [#hex] | Secondary text, labels |
28
+
29
+ ## Typography
30
+
31
+ | Level | Size | Weight | Line Height | Usage |
32
+ |-------|------|--------|-------------|-------|
33
+ | H1 | [rem] | [weight] | [lh] | Page titles |
34
+ | H2 | [rem] | [weight] | [lh] | Section titles |
35
+ | H3 | [rem] | [weight] | [lh] | Card titles |
36
+ | Body | [rem] | [weight] | [lh] | Default text |
37
+ | Small | [rem] | [weight] | [lh] | Captions, labels |
38
+
39
+ ## Spacing
40
+ - **Base unit**: [4px / 8px]
41
+ - **Scale**: 4, 8, 12, 16, 24, 32, 48, 64
42
+
43
+ ## Border Radius
44
+ - **Small**: [4px] — inputs, small cards
45
+ - **Medium**: [8px] — cards, modals
46
+ - **Large**: [16px] — large containers
47
+ - **Full**: [9999px] — pills, avatars
48
+
49
+ ## Shadows
50
+ - **Small**: [shadow] — hover states
51
+ - **Medium**: [shadow] — cards, dropdowns
52
+ - **Large**: [shadow] — modals, popovers
53
+
54
+ ## Components
55
+ | Component | Variants | Location |
56
+ |-----------|----------|----------|
57
+ | Button | primary, secondary, ghost, destructive | `src/components/Button` |
58
+ | [Component] | [variants] | [path] |
59
+
60
+ ## Animation
61
+ - **Duration**: fast (150ms), normal (300ms), slow (500ms)
62
+ - **Easing**: ease-out for enter, ease-in for exit
63
+ - **Library**: [Framer Motion / CSS transitions / etc.]
64
+
65
+ ---
66
+ *Agents use this to create visually consistent components, pick correct tokens, and follow established patterns.*
@@ -0,0 +1,36 @@
1
+ # Environment Variables
2
+
3
+ ## Required (app won't work without these)
4
+
5
+ | Variable | Description | Where to Get |
6
+ |----------|-------------|-------------|
7
+ | `DATABASE_URL` | [Database connection string] | [Supabase dashboard / provider] |
8
+ | `NEXT_PUBLIC_SUPABASE_URL` | [Supabase project URL] | [Supabase dashboard] |
9
+ | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | [Supabase public key] | [Supabase dashboard] |
10
+ | | | |
11
+
12
+ ## Optional (features degrade gracefully)
13
+
14
+ | Variable | Description | Feature Affected |
15
+ |----------|-------------|-----------------|
16
+ | `OPENAI_API_KEY` | [AI API access] | [AI features disabled] |
17
+ | `STRIPE_SECRET_KEY` | [Payment processing] | [Payments disabled] |
18
+ | | | |
19
+
20
+ ## Server-Only (never expose to client)
21
+
22
+ | Variable | Why Server-Only |
23
+ |----------|----------------|
24
+ | `SUPABASE_SERVICE_ROLE_KEY` | Bypasses RLS |
25
+ | `[API]_SECRET_KEY` | Payment/API secrets |
26
+
27
+ ## Environments
28
+
29
+ | Env | .env file | Notes |
30
+ |-----|-----------|-------|
31
+ | Local | `.env.local` | Never committed |
32
+ | Preview | Vercel env vars | Auto from branch |
33
+ | Production | Vercel env vars | Manual setup |
34
+
35
+ ---
36
+ *Agents use this to avoid exposing secrets, configure features correctly, and debug environment issues.*
@@ -0,0 +1,32 @@
1
+ # ERD (Entity Relationship Diagram)
2
+
3
+ ## Tables
4
+
5
+ ### users
6
+ | Column | Type | Constraints | Description |
7
+ |--------|------|-------------|-------------|
8
+ | id | uuid | PK | |
9
+ | email | text | unique, not null | |
10
+ | created_at | timestamp | default now() | |
11
+
12
+ ### [table_name]
13
+ | Column | Type | Constraints | Description |
14
+ |--------|------|-------------|-------------|
15
+
16
+ ## Relationships
17
+
18
+ ```
19
+ users 1──* orders (one user has many orders)
20
+ orders *──1 products (many orders reference one product)
21
+ ```
22
+
23
+ ## Indexes
24
+ - `users.email` — unique index for login lookup
25
+ - [add your indexes]
26
+
27
+ ## RLS Policies (if using Supabase)
28
+ - `users`: users can only read/update their own row
29
+ - [add your policies]
30
+
31
+ ---
32
+ *Agents use this to understand your data model when planning features, writing queries, and reviewing security.*
@@ -0,0 +1,27 @@
1
+ # Glossary
2
+
3
+ Domain terms and their definitions. All agents use consistent terminology.
4
+
5
+ | Term | Definition | In Code As |
6
+ |------|-----------|------------|
7
+ | [e.g., reading] | [e.g., A tarot card reading session] | `Reading` type |
8
+ | [e.g., spread] | [e.g., The layout pattern for cards] | `SpreadType` enum |
9
+ | [e.g., interpretation] | [e.g., AI-generated meaning of cards] | `Interpretation` type |
10
+ | | | |
11
+ | | | |
12
+
13
+ ## User Roles
14
+ | Role | Permissions | In Code As |
15
+ |------|------------|------------|
16
+ | [e.g., free user] | [e.g., 3 readings per day] | `role: "free"` |
17
+ | [e.g., premium] | [e.g., unlimited readings + chat] | `role: "premium"` |
18
+ | [e.g., admin] | [e.g., all access + user management] | `role: "admin"` |
19
+
20
+ ## Status Flows
21
+ ```
22
+ [e.g., Order: draft → pending → paid → fulfilled → completed]
23
+ [e.g., User: anonymous → registered → verified → premium]
24
+ ```
25
+
26
+ ---
27
+ *Agents use this to write code with correct naming, understand business logic, and communicate clearly in docs.*
@@ -0,0 +1,40 @@
1
+ # User Flows
2
+
3
+ ## Core Flows
4
+
5
+ ### [Flow Name, e.g., "Purchase Flow"]
6
+ ```
7
+ [Entry Point] → [Step 1] → [Step 2] → [Step 3] → [Success]
8
+ ↓ ↓ ↓
9
+ [Error A] [Error B] [Error C]
10
+ ```
11
+
12
+ **Happy Path**:
13
+ 1. User [action]
14
+ 2. System [response]
15
+ 3. User [action]
16
+ 4. System [response] → Success
17
+
18
+ **Error Paths**:
19
+ - Step 1 fails: [what happens, where user goes]
20
+ - Step 2 fails: [what happens]
21
+ - Network error: [retry? fallback?]
22
+
23
+ **Edge Cases**:
24
+ - [e.g., user refreshes mid-flow]
25
+ - [e.g., session expires during checkout]
26
+ - [e.g., concurrent access from two devices]
27
+
28
+ ### [Next Flow...]
29
+
30
+ ## Page Map
31
+ ```
32
+ / → Home (landing)
33
+ /login → Auth
34
+ /dashboard → Main dashboard (auth required)
35
+ /dashboard/[id] → Detail page
36
+ /settings → User settings
37
+ ```
38
+
39
+ ---
40
+ *Agents use this to implement correct navigation, handle all error states, and test edge cases during QA.*