nextjs-hackathon-stack 0.1.29 → 0.1.31
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/dist/index.js +6 -3
- package/package.json +1 -1
- package/template/.cursor/agents/backend.md +10 -59
- package/template/.cursor/agents/business-intelligence.md +37 -15
- package/template/.cursor/agents/code-reviewer.md +2 -2
- package/template/.cursor/agents/frontend.md +6 -10
- package/template/.cursor/agents/security-researcher.md +8 -1
- package/template/.cursor/agents/technical-lead.md +47 -18
- package/template/.cursor/agents/test-qa.md +9 -49
- package/template/.cursor/rules/architecture.mdc +2 -0
- package/template/.cursor/rules/coding-standards.mdc +12 -1
- package/template/.cursor/rules/components.mdc +7 -0
- package/template/.cursor/rules/data-fetching.mdc +56 -5
- package/template/.cursor/rules/forms.mdc +4 -0
- package/template/.cursor/rules/general.mdc +5 -3
- package/template/.cursor/rules/nextjs.mdc +27 -3
- package/template/.cursor/rules/security.mdc +53 -3
- package/template/.cursor/rules/supabase.mdc +19 -3
- package/template/.cursor/rules/testing.mdc +13 -4
- package/template/.cursor/skills/create-feature/SKILL.md +8 -4
- package/template/.cursor/skills/create-feature/references/server-action-test-template.md +51 -0
- package/template/.cursor/skills/review-branch/SKILL.md +2 -22
- package/template/.cursor/skills/review-branch/references/review-checklist.md +36 -0
- package/template/.cursor/skills/security-audit/SKILL.md +8 -39
- package/template/.cursor/skills/security-audit/references/audit-steps.md +41 -0
- package/template/CLAUDE.md +27 -10
- package/template/eslint.config.ts +7 -1
- package/template/next.config.ts +2 -1
- package/template/src/app/(protected)/page.tsx +2 -4
- package/template/src/app/__tests__/auth-callback.test.ts +14 -0
- package/template/src/app/__tests__/protected-page.test.tsx +11 -13
- package/template/src/app/api/auth/callback/route.ts +2 -1
- package/template/src/e2e/login.spec.ts +4 -4
- package/template/src/features/todos/__tests__/todos.action.test.ts +78 -44
- package/template/src/features/todos/__tests__/todos.queries.test.ts +101 -0
- package/template/src/features/todos/actions/todos.action.ts +42 -28
- package/template/src/features/todos/queries/todos.queries.ts +16 -0
- package/template/src/shared/lib/supabase/middleware.ts +0 -1
- package/template/src/shared/lib/supabase/server.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ async function runCli(argProjectName, skipInstall) {
|
|
|
48
48
|
|
|
49
49
|
// src/scaffold.ts
|
|
50
50
|
import { execSync } from "child_process";
|
|
51
|
-
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync,
|
|
51
|
+
import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, readdirSync, symlinkSync, writeFileSync } from "fs";
|
|
52
52
|
import { join, dirname } from "path";
|
|
53
53
|
import { fileURLToPath } from "url";
|
|
54
54
|
import * as p3 from "@clack/prompts";
|
|
@@ -69,8 +69,11 @@ function copyDir(src, dest, vars) {
|
|
|
69
69
|
destEntry = "." + entry.slice(1);
|
|
70
70
|
}
|
|
71
71
|
const destPath = join(dest, destEntry);
|
|
72
|
-
const stat =
|
|
73
|
-
if (stat.
|
|
72
|
+
const stat = lstatSync(srcPath);
|
|
73
|
+
if (stat.isSymbolicLink()) {
|
|
74
|
+
const target = readlinkSync(srcPath);
|
|
75
|
+
symlinkSync(target, destPath);
|
|
76
|
+
} else if (stat.isDirectory()) {
|
|
74
77
|
copyDir(srcPath, destPath, vars);
|
|
75
78
|
} else if (entry.endsWith(".tmpl")) {
|
|
76
79
|
const content = readFileSync(srcPath, "utf-8");
|
package/package.json
CHANGED
|
@@ -13,69 +13,20 @@ readonly: false
|
|
|
13
13
|
- Configure Supabase RLS policies and auth flows
|
|
14
14
|
- Select appropriate runtime (Edge vs Node.js)
|
|
15
15
|
|
|
16
|
-
## Rules
|
|
16
|
+
## Key Rules (auto-loaded by file context)
|
|
17
|
+
- Drizzle + Supabase + repository pattern: `supabase.mdc`
|
|
18
|
+
- Migrations: `migrations.mdc` — never edit SQL directly
|
|
19
|
+
- Server Actions pattern: `architecture.mdc` (auth → Zod → scoped query → ActionResult)
|
|
20
|
+
- Security: `security.mdc` (env vars, RLS, input validation)
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
// ✅ Use Drizzle for schema definition
|
|
21
|
-
export const users = pgTable("users", {
|
|
22
|
-
id: uuid("id").primaryKey().defaultRandom(),
|
|
23
|
-
email: text("email").notNull().unique(),
|
|
24
|
-
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// ✅ Auto-generate Zod from Drizzle — never write Zod for DB types manually
|
|
28
|
-
export const insertUserSchema = createInsertSchema(users);
|
|
29
|
-
export const selectUserSchema = createSelectSchema(users);
|
|
30
|
-
|
|
31
|
-
// ❌ NEVER use Drizzle for runtime queries — bypasses RLS
|
|
32
|
-
const users = await db.select().from(usersTable); // WRONG
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Runtime Queries — supabase-js (RLS always active)
|
|
36
|
-
```typescript
|
|
37
|
-
// ✅ All runtime queries via supabase-js
|
|
38
|
-
const { data, error } = await supabase.from("users").select("*").eq("id", userId);
|
|
39
|
-
|
|
40
|
-
// ❌ NEVER use Drizzle for runtime queries
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Migration Workflow
|
|
44
|
-
**NEVER create or edit files in `src/shared/db/migrations/` directly.**
|
|
45
|
-
|
|
46
|
-
Always follow this workflow:
|
|
47
|
-
1. Edit schema: `src/shared/db/schema.ts`
|
|
48
|
-
2. Generate migration: `pnpm db:generate`
|
|
49
|
-
3. Review the generated SQL in `src/shared/db/migrations/`
|
|
50
|
-
4. Apply migration: `pnpm db:migrate`
|
|
51
|
-
5. Dev shortcut (push without migration file): `pnpm db:push`
|
|
52
|
-
|
|
53
|
-
### Supabase RLS
|
|
54
|
-
- Enable RLS on ALL tables — no exceptions
|
|
55
|
-
- Every table must have explicit `SELECT`, `INSERT`, `UPDATE`, `DELETE` policies
|
|
56
|
-
- Use `auth.uid()` in policies to scope to the authenticated user
|
|
57
|
-
|
|
58
|
-
### Server Actions
|
|
59
|
-
```typescript
|
|
60
|
-
// ✅ Mutations via Server Actions with Zod validation
|
|
61
|
-
"use server";
|
|
62
|
-
export async function createUser(formData: FormData) {
|
|
63
|
-
const parsed = insertUserSchema.safeParse(Object.fromEntries(formData));
|
|
64
|
-
if (!parsed.success) return { error: parsed.error.flatten() };
|
|
65
|
-
// ... supabase-js mutation
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ❌ Never expose DATABASE_URL to the client
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Environment Variables
|
|
72
|
-
- `NEXT_PUBLIC_SUPABASE_URL` + `NEXT_PUBLIC_SUPABASE_ANON_KEY` — client-safe
|
|
73
|
-
- `DATABASE_URL` — server-only (Drizzle migrations only), NEVER prefix with `NEXT_PUBLIC_`
|
|
74
|
-
|
|
75
|
-
### Runtime Selection
|
|
22
|
+
## Runtime Selection
|
|
76
23
|
- Default to **Node.js runtime** for Server Actions and API routes
|
|
77
24
|
- Use **Edge runtime** only when latency is critical and no Node.js APIs are needed
|
|
78
25
|
|
|
26
|
+
## Dev Server
|
|
27
|
+
- Before running `next dev`, check `.next/dev/lock`. If it exists and the PID is alive, an instance is already running — use that server's URL instead of starting a new one.
|
|
28
|
+
- Enable `logging.browserToTerminal` in `next.config.ts` for terminal-based debugging (recommended: `'error'` for production-like sessions, `true` during active development).
|
|
29
|
+
|
|
79
30
|
## Guardrails
|
|
80
31
|
- Never write migration SQL directly — always use `pnpm db:generate`
|
|
81
32
|
- Never expose `DATABASE_URL` to the browser
|
|
@@ -8,12 +8,27 @@ readonly: true
|
|
|
8
8
|
# Business Intelligence Agent
|
|
9
9
|
|
|
10
10
|
## Responsibilities
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- Discover requirements through user questions before defining anything
|
|
12
|
+
- Write functional issues describing user-visible behavior
|
|
13
|
+
- Map requirements to functional test cases
|
|
13
14
|
- Validate implementations match acceptance criteria
|
|
14
15
|
- Maintain `.requirements/` documentation
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
**Do NOT create technical tasks — that is the Technical Lead's job.**
|
|
18
|
+
|
|
19
|
+
## Discovery Process
|
|
20
|
+
|
|
21
|
+
Never assume requirements. Always ask first.
|
|
22
|
+
|
|
23
|
+
1. **Problem & audience** — "What problem does this solve? Who experiences it?"
|
|
24
|
+
2. **User flows** — "Walk me through the happy path. What happens on error?"
|
|
25
|
+
3. **Edge cases & constraints** — "What are the limits? What should NOT happen?"
|
|
26
|
+
4. **Confirm understanding** — Restate what you heard and ask for approval before writing anything
|
|
27
|
+
|
|
28
|
+
Only after the user confirms your understanding should you produce a functional issue.
|
|
29
|
+
|
|
30
|
+
## Functional Issue Format
|
|
31
|
+
|
|
17
32
|
```markdown
|
|
18
33
|
## Feature: [Feature Name]
|
|
19
34
|
|
|
@@ -21,18 +36,25 @@ readonly: true
|
|
|
21
36
|
As a [user type], I want [goal] so that [reason].
|
|
22
37
|
|
|
23
38
|
### Acceptance Criteria
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
### Test Cases
|
|
29
|
-
- [ ] TC1:
|
|
30
|
-
- [ ] TC2:
|
|
31
|
-
- [ ] TC3:
|
|
39
|
+
- [ ] AC1: When [user does X], they see [Y]
|
|
40
|
+
- [ ] AC2: When [error condition], user sees [message/state]
|
|
41
|
+
- [ ] AC3: [Edge case]: [expected outcome]
|
|
42
|
+
|
|
43
|
+
### Functional Test Cases
|
|
44
|
+
- [ ] TC1 (AC1): User does X → sees Y (happy path)
|
|
45
|
+
- [ ] TC2 (AC2): User triggers error → sees error message
|
|
46
|
+
- [ ] TC3 (AC3): Edge case behavior visible to user
|
|
32
47
|
```
|
|
33
48
|
|
|
49
|
+
**Rules for this format:**
|
|
50
|
+
- Acceptance criteria use plain functional language — no code, no implementation details
|
|
51
|
+
- Test cases describe what the **user sees**, not system internals
|
|
52
|
+
- Every acceptance criterion maps to at least one test case
|
|
53
|
+
- No mention of database tables, API calls, component names, or file paths
|
|
54
|
+
|
|
34
55
|
## Guardrails
|
|
35
|
-
- Always
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
56
|
+
- Always ask questions before writing — never assume
|
|
57
|
+
- Document all requirements in `.requirements/` directory
|
|
58
|
+
- Flag ambiguous requirements — ask rather than guess
|
|
59
|
+
- Functional issues only: user stories, acceptance criteria, visible behavior
|
|
60
|
+
- Hand off to Technical Lead when the functional issue is approved
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-reviewer
|
|
3
3
|
description: Code review specialist. Use when reviewing branch changes, PRs, or MRs. Supports GitHub (gh) and GitLab (glab).
|
|
4
|
-
model:
|
|
4
|
+
model: fast
|
|
5
5
|
readonly: true
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -26,7 +26,7 @@ glab --version 2>/dev/null && echo "GitLab"
|
|
|
26
26
|
|
|
27
27
|
### Code Quality
|
|
28
28
|
- [ ] Zero `any` types
|
|
29
|
-
- [ ] Zero comments
|
|
29
|
+
- [ ] Zero comments (excluding test AAA labels: `// Arrange`, `// Act`, `// Assert`)
|
|
30
30
|
- [ ] Functions ≤ 20 lines
|
|
31
31
|
- [ ] Files ≤ 200 lines
|
|
32
32
|
- [ ] No magic numbers/strings
|
|
@@ -13,16 +13,12 @@ readonly: false
|
|
|
13
13
|
- Write component tests with React Testing Library
|
|
14
14
|
|
|
15
15
|
## Rules
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
- ARIA labels
|
|
23
|
-
- Keyboard navigation
|
|
24
|
-
- `role="alert"` for errors
|
|
25
|
-
- `data-testid` for tests
|
|
16
|
+
Follow `components.mdc` for shadcn/ui, Tailwind v4, and accessibility standards.
|
|
17
|
+
|
|
18
|
+
## Component Discovery
|
|
19
|
+
- Before building UI, check if a shadcn/ui component exists: `shadcn search <query>` or `shadcn docs <component>`
|
|
20
|
+
- Install missing components with `shadcn add <component>` — never manually create files in `@/shared/components/ui/`
|
|
21
|
+
- The shadcn skill (`npx skills add shadcn/ui`) provides project context automatically via `components.json`
|
|
26
22
|
|
|
27
23
|
## Component Pattern
|
|
28
24
|
```tsx
|
|
@@ -11,7 +11,8 @@ readonly: true
|
|
|
11
11
|
|
|
12
12
|
### Authentication & Authorization
|
|
13
13
|
- [ ] Supabase RLS enabled on all tables
|
|
14
|
-
- [ ]
|
|
14
|
+
- [ ] `getUser()` called at the top of every Server Action and API route before data access
|
|
15
|
+
- [ ] All queries scoped to `user.id` — not relying on RLS alone
|
|
15
16
|
- [ ] Session validation in proxy
|
|
16
17
|
- [ ] No hardcoded credentials or API keys
|
|
17
18
|
|
|
@@ -20,6 +21,8 @@ readonly: true
|
|
|
20
21
|
- [ ] Zod validation in all Server Actions
|
|
21
22
|
- [ ] No SQL injection vectors (Drizzle parameterized)
|
|
22
23
|
- [ ] No XSS vectors (`dangerouslySetInnerHTML`)
|
|
24
|
+
- [ ] No `as` type assertions on data from DB or external APIs — use Zod `.parse()`
|
|
25
|
+
- [ ] Redirect targets validated: `next`/`redirect` params must start with `/` and not `//`
|
|
23
26
|
|
|
24
27
|
### Secrets Management
|
|
25
28
|
- [ ] No `NEXT_PUBLIC_` prefix on secret keys
|
|
@@ -30,6 +33,10 @@ readonly: true
|
|
|
30
33
|
- [ ] Run `pnpm audit` — report Critical/High findings
|
|
31
34
|
- [ ] Review new dependencies for malicious packages
|
|
32
35
|
|
|
36
|
+
### Rate Limiting
|
|
37
|
+
- [ ] Rate limiting applied to all AI routes (per `security.mdc`)
|
|
38
|
+
- [ ] Rate limiting applied to auth endpoints (per `security.mdc`)
|
|
39
|
+
|
|
33
40
|
### Headers & Cookies
|
|
34
41
|
- [ ] CSP header configured in `next.config.ts`
|
|
35
42
|
- [ ] HSTS enabled
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
name: technical-lead
|
|
3
3
|
description: Orchestrator and architecture owner. Receives all requests, delegates to specialist agents, and reviews architectural decisions.
|
|
4
4
|
model: inherit
|
|
5
|
+
# Note: `model: inherit` means this agent uses whatever model the user selected.
|
|
6
|
+
# For security-critical reviews, consider switching to a more capable model explicitly.
|
|
5
7
|
readonly: false
|
|
6
8
|
---
|
|
7
9
|
|
|
@@ -17,45 +19,72 @@ readonly: false
|
|
|
17
19
|
|
|
18
20
|
## Orchestration
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
**How to use this agent**: When starting a task, use @technical-lead to plan and break it down, then switch to the appropriate specialist agent. The @technical-lead does not automatically launch other agents — you direct which agent to use next based on the delegation table below.
|
|
23
|
+
|
|
24
|
+
Delegate to the appropriate specialist based on domain:
|
|
21
25
|
|
|
22
26
|
| Agent | Use when |
|
|
23
27
|
|---|---|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
28
|
+
| @frontend | UI components, pages, layouts, Tailwind, shadcn/ui, accessibility |
|
|
29
|
+
| @backend | Server Actions, API routes, Supabase RLS, Drizzle schema, migrations |
|
|
30
|
+
| @business-intelligence | Requirements definition, user stories, acceptance criteria |
|
|
31
|
+
| @test-qa | Writing tests, TDD workflow, coverage enforcement |
|
|
32
|
+
| @code-reviewer | Branch/PR review (readonly) |
|
|
33
|
+
| @security-researcher | Security audits, vulnerability scanning (readonly) |
|
|
30
34
|
|
|
31
35
|
## Delegation Rules
|
|
32
36
|
|
|
33
37
|
- **Single-domain** → delegate to the matching agent
|
|
34
|
-
- **Cross-domain** → launch multiple agents in parallel (e.g., a new feature needs
|
|
35
|
-
- Always delegate code review to
|
|
36
|
-
- Always delegate security audits to
|
|
37
|
-
- The technical-lead retains final say on all architectural decisions
|
|
38
|
+
- **Cross-domain** → launch multiple agents in parallel (e.g., a new feature needs @business-intelligence for requirements + @backend for API + @frontend for UI)
|
|
39
|
+
- Always delegate code review to @code-reviewer — do not duplicate its checklist here
|
|
40
|
+
- Always delegate security audits to @security-researcher
|
|
41
|
+
- The @technical-lead retains final say on all architectural decisions
|
|
38
42
|
|
|
39
43
|
## Workflow Sequences
|
|
40
44
|
|
|
41
45
|
### New feature
|
|
42
|
-
|
|
46
|
+
@business-intelligence (functional issue) → @technical-lead (task breakdown + test plan) → @test-qa (RED) → @backend/@frontend (GREEN) → @code-reviewer → @security-researcher
|
|
43
47
|
|
|
44
48
|
### Bug fix
|
|
45
|
-
|
|
49
|
+
@test-qa (reproduce with failing test) → @backend/@frontend (fix) → @code-reviewer
|
|
46
50
|
|
|
47
51
|
### Refactor
|
|
48
|
-
|
|
52
|
+
@technical-lead (plan) → @backend/@frontend (implement) → @test-qa (verify) → @code-reviewer
|
|
53
|
+
|
|
54
|
+
## Task Breakdown (New Feature)
|
|
55
|
+
|
|
56
|
+
When you receive a functional issue from @business-intelligence, do this before delegating:
|
|
57
|
+
|
|
58
|
+
1. **Read the functional issue** — understand acceptance criteria and user flows
|
|
59
|
+
2. **Split into technical tasks** — one task per agent, scoped to a single concern
|
|
60
|
+
3. **Plan the test structure upfront**:
|
|
61
|
+
- Which test files need to be created
|
|
62
|
+
- One `describe` block per acceptance criterion
|
|
63
|
+
- Which mocks are needed (Supabase, HTTP, etc.)
|
|
64
|
+
- Which edge cases map to which criteria
|
|
65
|
+
4. **Delegate in order**:
|
|
66
|
+
- @test-qa gets the test plan → writes failing tests (RED)
|
|
67
|
+
- @backend/@frontend implements to make tests pass (GREEN)
|
|
68
|
+
- @code-reviewer and @security-researcher review the result
|
|
69
|
+
|
|
70
|
+
### TDD efficiency guardrails
|
|
71
|
+
- Plan test structure before delegating to @test-qa — never send "write tests for this feature" without a plan
|
|
72
|
+
- Scope tests to acceptance criteria — no speculative tests for hypothetical future requirements
|
|
73
|
+
- One `describe` block per acceptance criterion
|
|
49
74
|
|
|
50
75
|
## Architectural Review Checklist
|
|
51
76
|
|
|
52
|
-
|
|
53
|
-
- [ ]
|
|
54
|
-
- [ ] Proper layer separation
|
|
55
|
-
- [ ] Edge runtime on all AI routes (Risk 3)
|
|
77
|
+
Review against project rules (`coding-standards.mdc`, `architecture.mdc`, `data-fetching.mdc`, `security.mdc`). Project-specific checks:
|
|
78
|
+
- [ ] Edge runtime on AI routes unless Node.js APIs are required (Risk 3)
|
|
56
79
|
- [ ] TanStack Query is NOT used for mutations (Risk 1)
|
|
57
80
|
- [ ] Every DB mutation shows toast feedback to the user (success or error via `sonner`)
|
|
58
81
|
|
|
82
|
+
## Debugging with next-browser
|
|
83
|
+
|
|
84
|
+
`@vercel/next-browser` — terminal access to a running Next.js app for PPR analysis, component tree inspection, network monitoring, and screenshots.
|
|
85
|
+
|
|
86
|
+
Install: `npx skills add vercel-labs/next-browser`
|
|
87
|
+
|
|
59
88
|
## Guardrails
|
|
60
89
|
- Reject any PR without test coverage
|
|
61
90
|
- Reject any `any` type usage
|
|
@@ -13,39 +13,9 @@ readonly: false
|
|
|
13
13
|
3. **REFACTOR** — clean up while keeping tests green
|
|
14
14
|
4. **VERIFY** — run `pnpm test:coverage` and confirm 100%
|
|
15
15
|
|
|
16
|
-
##
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
it("description of behavior", async () => {
|
|
22
|
-
// Arrange — set up mocks, render, initial state
|
|
23
|
-
mockFn.mockResolvedValue({ data: "value" });
|
|
24
|
-
render(<Component />);
|
|
25
|
-
|
|
26
|
-
// Act — single action being tested
|
|
27
|
-
await userEvent.click(screen.getByRole("button", { name: /submit/i }));
|
|
28
|
-
|
|
29
|
-
// Assert — verify the outcome
|
|
30
|
-
expect(screen.getByText(/success/i)).toBeInTheDocument();
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
- Add blank lines between sections when all three are non-trivial
|
|
35
|
-
- One logical assertion per test (multiple `expect` calls are fine if they test the same outcome)
|
|
36
|
-
- If there is no Act, you are testing the wrong thing — restructure
|
|
37
|
-
|
|
38
|
-
## Vitest Rules
|
|
39
|
-
- Mock only external dependencies (HTTP calls, Supabase, DB)
|
|
40
|
-
- Test behavior, not implementation details
|
|
41
|
-
- Use `data-testid` for component queries
|
|
42
|
-
- Never test private functions directly
|
|
43
|
-
- Always use AAA — reject any test that skips a section
|
|
44
|
-
|
|
45
|
-
## Playwright Rules
|
|
46
|
-
- Page Object Pattern for complex flows
|
|
47
|
-
- `data-testid` attributes for stable selectors
|
|
48
|
-
- Every user flow needs an e2e test
|
|
16
|
+
## Key Rules (auto-loaded by file context)
|
|
17
|
+
- Testing standards, AAA pattern, coverage: `testing.mdc`
|
|
18
|
+
- TDD iron rules: `coding-standards.mdc`
|
|
49
19
|
|
|
50
20
|
## Coverage Requirement
|
|
51
21
|
```
|
|
@@ -60,24 +30,14 @@ After every change:
|
|
|
60
30
|
2. If below 100%, add missing tests
|
|
61
31
|
3. Never mark work complete without full coverage
|
|
62
32
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
### NEVER modify tests to pass
|
|
66
|
-
- Tests are the contract. If code doesn't pass a test, fix the CODE.
|
|
67
|
-
- The only time to modify a test is during RED phase (writing new) or REFACTOR phase (restructure without weakening).
|
|
68
|
-
- Weakening a test = removing assertions, loosening matchers, adding `.skip`, broadening regex. ALL are forbidden.
|
|
69
|
-
- If you find yourself wanting to change a test to match the code, STOP. The code is wrong.
|
|
33
|
+
## Test Plan Compliance
|
|
70
34
|
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
- Reject completion if ANY uncovered branch exists
|
|
35
|
+
When the Technical Lead provides a test plan, follow it:
|
|
36
|
+
- Use the specified test files, `describe` block names, and mock setup
|
|
37
|
+
- Flag missing edge cases to the TL **before** adding unplanned tests — do not expand scope unilaterally
|
|
38
|
+
- If the plan is ambiguous or incomplete, ask for clarification rather than guessing
|
|
76
39
|
|
|
77
|
-
|
|
78
|
-
- Every `it()` / `test()` block must have `// Arrange`, `// Act`, `// Assert` comments
|
|
79
|
-
- For trivial render tests, use `// Arrange + Act` and `// Assert`
|
|
80
|
-
- For error-throwing tests, use `// Arrange` and `// Act + Assert`
|
|
40
|
+
For Server Action test templates, see `create-feature` skill references.
|
|
81
41
|
|
|
82
42
|
## Guardrails
|
|
83
43
|
- Never write implementation without a failing test first
|
|
@@ -11,8 +11,10 @@ features/
|
|
|
11
11
|
└── <feature-name>/
|
|
12
12
|
├── components/ # React components
|
|
13
13
|
├── actions/ # Server Actions (*.action.ts)
|
|
14
|
+
├── queries/ # Read queries — *.queries.ts (repository layer)
|
|
14
15
|
├── hooks/ # React hooks (use-*.ts)
|
|
15
16
|
├── api/ # Route handlers (route.ts)
|
|
17
|
+
├── lib/ # Feature-specific utilities, schemas, and types
|
|
16
18
|
└── __tests__/ # Colocated tests
|
|
17
19
|
```
|
|
18
20
|
|
|
@@ -7,10 +7,11 @@ alwaysApply: true
|
|
|
7
7
|
|
|
8
8
|
## Absolute Rules
|
|
9
9
|
- **Zero `any`** — use `unknown` + type guards or proper generics. `@typescript-eslint/no-explicit-any: error`
|
|
10
|
-
- **Zero comments** — code must be self-documenting through naming.
|
|
10
|
+
- **Zero comments in production code** — code must be self-documenting through naming. Test AAA labels (`// Arrange`, `// Act`, `// Assert`) are required in tests.
|
|
11
11
|
- **Zero magic numbers/strings** — use typed constants
|
|
12
12
|
- **No `console.log`** in production code — `no-console: warn`
|
|
13
13
|
- **No `!` non-null assertions** — use proper null checks
|
|
14
|
+
- **No `eslint-disable` inline comments** — fix the code instead. If a rule conflict is truly unavoidable (e.g. third-party API with no non-deprecated alternative), add a file-level override in `eslint.config.ts` with a `// TODO` comment explaining why.
|
|
14
15
|
|
|
15
16
|
## TDD (Test-Driven Development)
|
|
16
17
|
1. **RED**: Write a failing test first
|
|
@@ -51,6 +52,16 @@ Never write implementation before tests exist.
|
|
|
51
52
|
- Prefer `type` over `interface` for data shapes
|
|
52
53
|
- Use `satisfies` for type-checked literals
|
|
53
54
|
- Use `as const` for immutable literals
|
|
55
|
+
- **Never use `as` type assertions for data from external sources** (DB, API, user input). Use Zod `.parse()` or `.safeParse()` to validate and type runtime data. `as const` is acceptable for literals.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// ✅
|
|
59
|
+
const schema = z.array(supportItemSchema);
|
|
60
|
+
const items = schema.parse(data);
|
|
61
|
+
|
|
62
|
+
// ❌
|
|
63
|
+
const items = data as SupportListItem[];
|
|
64
|
+
```
|
|
54
65
|
|
|
55
66
|
## Pre-commit Quality Gates
|
|
56
67
|
Every commit must pass two sequential gates:
|
|
@@ -11,6 +11,13 @@ globs: ["src/**/components/**"]
|
|
|
11
11
|
- Extend with `className` prop via `cn()` utility
|
|
12
12
|
- New York style, CSS variables for theming
|
|
13
13
|
|
|
14
|
+
## shadcn CLI
|
|
15
|
+
- Install the shadcn skill for AI-aware context: `npx skills add shadcn/ui`
|
|
16
|
+
- Before writing component code, run `shadcn docs <component>` to get up-to-date API and usage patterns
|
|
17
|
+
- Use `shadcn search <query>` to discover available components
|
|
18
|
+
- Install components with `shadcn add <component>` — never copy-paste component source manually
|
|
19
|
+
- Use `shadcn diff` to check for upstream changes to already-installed components
|
|
20
|
+
|
|
14
21
|
## Tailwind v4
|
|
15
22
|
- CSS-native config via `@theme {}` in CSS
|
|
16
23
|
- No `tailwind.config.js` — all config in `tailwind.css`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: TanStack Query vs RSC data fetching
|
|
3
|
-
|
|
2
|
+
description: TanStack Query vs RSC data fetching boundaries. RISK 1. Applied when working on components, pages, queries, hooks, or actions.
|
|
3
|
+
globs: ["src/**/components/**", "src/**/queries/**", "src/**/hooks/**", "src/**/actions/**", "src/app/**"]
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Data Fetching Rules (Risk 1: TanStack Query + RSC Boundary)
|
|
@@ -36,18 +36,22 @@ const mutation = useMutation({
|
|
|
36
36
|
|
|
37
37
|
## CORRECT ✅
|
|
38
38
|
```typescript
|
|
39
|
-
// ✅ Initial data from RSC
|
|
39
|
+
// ✅ Initial data from RSC via feature's queries/ module
|
|
40
|
+
import { getUserById } from "@/features/users/queries/users.queries";
|
|
41
|
+
|
|
40
42
|
export default async function UserPage({ params }: { params: { id: string } }) {
|
|
41
|
-
const
|
|
43
|
+
const supabase = await createClient();
|
|
44
|
+
const { data: user } = await getUserById(supabase, params.id); // via repository
|
|
42
45
|
return <UserProfile initialUser={user} />;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
// ✅ TanStack Query for background refresh only
|
|
49
|
+
// queryFn must call a function from the feature's queries/ module — never inline supabase.from() here
|
|
46
50
|
"use client";
|
|
47
51
|
export function UserProfile({ initialUser }: { initialUser: User }) {
|
|
48
52
|
const { data: user } = useQuery({
|
|
49
53
|
queryKey: ["user", initialUser.id],
|
|
50
|
-
queryFn: () => fetchUser(initialUser.id),
|
|
54
|
+
queryFn: () => fetchUser(initialUser.id), // fetchUser lives in features/users/queries/
|
|
51
55
|
initialData: initialUser, // Seeded from RSC
|
|
52
56
|
staleTime: 5 * 60 * 1000,
|
|
53
57
|
});
|
|
@@ -61,3 +65,50 @@ async function updateUserAction(formData: FormData) {
|
|
|
61
65
|
revalidatePath("/profile");
|
|
62
66
|
}
|
|
63
67
|
```
|
|
68
|
+
|
|
69
|
+
## Error Handling
|
|
70
|
+
|
|
71
|
+
TanStack Query errors must be handled via the `error` property — never swallow them silently.
|
|
72
|
+
|
|
73
|
+
- **Data queries** (background reads): show an inline error state in the component
|
|
74
|
+
- **User-triggered actions**: show a toast via `sonner` (see `forms.mdc` Pattern A)
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
"use client";
|
|
78
|
+
export function UserProfile({ initialUser }: { initialUser: User }) {
|
|
79
|
+
const { data: user, error } = useQuery({
|
|
80
|
+
queryKey: ["user", initialUser.id],
|
|
81
|
+
queryFn: () => fetchUser(initialUser.id),
|
|
82
|
+
initialData: initialUser,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (error) return <p role="alert">Failed to load user data.</p>;
|
|
86
|
+
return <div>{user.name}</div>;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Cache Invalidation After Server Actions
|
|
91
|
+
|
|
92
|
+
After a Server Action mutates data, invalidate the relevant TanStack Query cache so the UI reflects the new state.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// In the calling component (Client Component)
|
|
96
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
97
|
+
import { toast } from "sonner";
|
|
98
|
+
|
|
99
|
+
const queryClient = useQueryClient();
|
|
100
|
+
|
|
101
|
+
startTransition(async () => {
|
|
102
|
+
const result = await updateUserAction(formData);
|
|
103
|
+
if (result.status === "success") {
|
|
104
|
+
// Invalidate cache so background queries re-fetch with fresh data
|
|
105
|
+
await queryClient.invalidateQueries({ queryKey: ["user", userId] });
|
|
106
|
+
toast.success(result.message);
|
|
107
|
+
} else {
|
|
108
|
+
toast.error(result.message);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> **Note on optimistic updates**: Full optimistic updates use `onMutate` + rollback on failure (`onError`). Do not implement this pattern until you have a concrete use case — `invalidateQueries` is simpler and sufficient for most scenarios.
|
|
114
|
+
|
|
@@ -58,6 +58,10 @@ export async function myAction(_prev: State, formData: FormData): Promise<State>
|
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
## Shared Schema
|
|
62
|
+
|
|
63
|
+
Share the Zod schema between client and server from a single source (e.g., `lib/schemas.ts` or the Drizzle-generated schema via `drizzle-zod`) to avoid drift. The template above defines `schema` twice as an illustration — in production, import it from a shared module.
|
|
64
|
+
|
|
61
65
|
## Toast Feedback
|
|
62
66
|
|
|
63
67
|
Every action that affects the database MUST show a toast to the user. No mutation should complete silently.
|
|
@@ -6,7 +6,7 @@ alwaysApply: true
|
|
|
6
6
|
# Stack Overview
|
|
7
7
|
|
|
8
8
|
## Technology Stack
|
|
9
|
-
- **Framework**: Next.js 16 (App Router)
|
|
9
|
+
- **Framework**: Next.js 16.2 (App Router)
|
|
10
10
|
- **Database**: Supabase (PostgreSQL) with Drizzle ORM for schema/migrations
|
|
11
11
|
- **Auth**: Supabase Auth via `@supabase/ssr`
|
|
12
12
|
- **ORM**: Drizzle + `drizzle-zod` (schema + migrations ONLY — no runtime queries)
|
|
@@ -14,7 +14,7 @@ alwaysApply: true
|
|
|
14
14
|
- **Client State**: TanStack Query v5
|
|
15
15
|
- **Validation**: Zod (auto-generated via `drizzle-zod`)
|
|
16
16
|
- **Forms**: React Hook Form + Zod resolver
|
|
17
|
-
- **UI**: shadcn/ui + Tailwind CSS v4
|
|
17
|
+
- **UI**: shadcn/ui + Tailwind CSS v4 (shadcn skill installed for AI context)
|
|
18
18
|
- **AI**: Vercel AI SDK + Google Gemini 2.0 Flash via AI Gateway
|
|
19
19
|
- **Testing**: Vitest + React Testing Library + Playwright
|
|
20
20
|
|
|
@@ -26,8 +26,10 @@ src/
|
|
|
26
26
|
│ └── <feature>/
|
|
27
27
|
│ ├── components/
|
|
28
28
|
│ ├── actions/
|
|
29
|
+
│ ├── queries/
|
|
29
30
|
│ ├── hooks/
|
|
30
31
|
│ ├── api/
|
|
32
|
+
│ ├── lib/
|
|
31
33
|
│ └── __tests__/
|
|
32
34
|
├── shared/ # Shared across features
|
|
33
35
|
│ ├── components/ui/ # shadcn/ui components
|
|
@@ -40,7 +42,7 @@ src/
|
|
|
40
42
|
- Files: `kebab-case.ts`
|
|
41
43
|
- Components: `PascalCase`
|
|
42
44
|
- Hooks: `useCamelCase`
|
|
43
|
-
- Server Actions: `
|
|
45
|
+
- Server Actions: `kebab-case.action.ts`
|
|
44
46
|
- API Routes: `route.ts` (in feature `api/` folder)
|
|
45
47
|
- Tests: `*.test.ts` or `*.test.tsx`
|
|
46
48
|
- E2e: `*.spec.ts`
|