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.
Files changed (39) hide show
  1. package/dist/index.js +6 -3
  2. package/package.json +1 -1
  3. package/template/.cursor/agents/backend.md +10 -59
  4. package/template/.cursor/agents/business-intelligence.md +37 -15
  5. package/template/.cursor/agents/code-reviewer.md +2 -2
  6. package/template/.cursor/agents/frontend.md +6 -10
  7. package/template/.cursor/agents/security-researcher.md +8 -1
  8. package/template/.cursor/agents/technical-lead.md +47 -18
  9. package/template/.cursor/agents/test-qa.md +9 -49
  10. package/template/.cursor/rules/architecture.mdc +2 -0
  11. package/template/.cursor/rules/coding-standards.mdc +12 -1
  12. package/template/.cursor/rules/components.mdc +7 -0
  13. package/template/.cursor/rules/data-fetching.mdc +56 -5
  14. package/template/.cursor/rules/forms.mdc +4 -0
  15. package/template/.cursor/rules/general.mdc +5 -3
  16. package/template/.cursor/rules/nextjs.mdc +27 -3
  17. package/template/.cursor/rules/security.mdc +53 -3
  18. package/template/.cursor/rules/supabase.mdc +19 -3
  19. package/template/.cursor/rules/testing.mdc +13 -4
  20. package/template/.cursor/skills/create-feature/SKILL.md +8 -4
  21. package/template/.cursor/skills/create-feature/references/server-action-test-template.md +51 -0
  22. package/template/.cursor/skills/review-branch/SKILL.md +2 -22
  23. package/template/.cursor/skills/review-branch/references/review-checklist.md +36 -0
  24. package/template/.cursor/skills/security-audit/SKILL.md +8 -39
  25. package/template/.cursor/skills/security-audit/references/audit-steps.md +41 -0
  26. package/template/CLAUDE.md +27 -10
  27. package/template/eslint.config.ts +7 -1
  28. package/template/next.config.ts +2 -1
  29. package/template/src/app/(protected)/page.tsx +2 -4
  30. package/template/src/app/__tests__/auth-callback.test.ts +14 -0
  31. package/template/src/app/__tests__/protected-page.test.tsx +11 -13
  32. package/template/src/app/api/auth/callback/route.ts +2 -1
  33. package/template/src/e2e/login.spec.ts +4 -4
  34. package/template/src/features/todos/__tests__/todos.action.test.ts +78 -44
  35. package/template/src/features/todos/__tests__/todos.queries.test.ts +101 -0
  36. package/template/src/features/todos/actions/todos.action.ts +42 -28
  37. package/template/src/features/todos/queries/todos.queries.ts +16 -0
  38. package/template/src/shared/lib/supabase/middleware.ts +0 -1
  39. 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, statSync, writeFileSync } from "fs";
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 = statSync(srcPath);
73
- if (stat.isDirectory()) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextjs-hackathon-stack",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Scaffold a full-stack Next.js hackathon starter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- ### Drizzle ORM — Schema Definition Only
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
- - Define requirements in Given/When/Then format
12
- - Map requirements to test cases
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
- ## Requirements Format
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
- **Given** [initial context]
25
- **When** [action taken]
26
- **Then** [expected outcome]
27
-
28
- ### Test Cases
29
- - [ ] TC1: [Happy path description]
30
- - [ ] TC2: [Error case description]
31
- - [ ] TC3: [Edge case description]
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 document in `.requirements/` directory
36
- - Every acceptance criterion must map to at least one test case
37
- - Validate implementation against all criteria before approving
38
- - Flag ambiguous requirements ask for clarification rather than assuming
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: inherit
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
- - Tailwind v4 CSS-native config (`@theme {}` in CSS) — no `tailwind.config.js`
17
- - No inline styles, no CSS modules
18
- - Always use shadcn/ui primitives over custom implementations
19
- - Default to Server Components add `"use client"` only when necessary
20
- - Accessibility is non-negotiable:
21
- - Semantic HTML
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
- - [ ] Auth check in every protected API route
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
- The technical-lead is the orchestrator. Every request passes through here first. Delegate to the appropriate specialist based on domain:
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
- | `frontend` | UI components, pages, layouts, Tailwind, shadcn/ui, accessibility |
25
- | `backend` | Server Actions, API routes, Supabase RLS, Drizzle schema, migrations |
26
- | `business-intelligence` | Requirements definition, user stories, acceptance criteria |
27
- | `test-qa` | Writing tests, TDD workflow, coverage enforcement |
28
- | `code-reviewer` | Branch/PR review (readonly) |
29
- | `security-researcher` | Security audits, vulnerability scanning (readonly) |
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 `business-intelligence` for requirements + `backend` for API + `frontend` for UI)
35
- - Always delegate code review to `code-reviewer` — do not duplicate its checklist here
36
- - Always delegate security audits to `security-researcher`
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
- `business-intelligence``test-qa` (RED) → `backend`/`frontend` (GREEN) → `code-reviewer``security-researcher`
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
- `test-qa` (reproduce with failing test) → `backend`/`frontend` (fix) → `code-reviewer`
49
+ @test-qa (reproduce with failing test) → @backend/@frontend (fix) → @code-reviewer
46
50
 
47
51
  ### Refactor
48
- `technical-lead` (plan) → `backend`/`frontend` (implement) → `test-qa` (verify) → `code-reviewer`
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
- - [ ] Zero `any` types
53
- - [ ] No circular imports (`features shared`, not reverse)
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
- ## AAA Pattern (Arrange-Act-Assert)
17
-
18
- Every test MUST use Arrange-Act-Assert. Enforce this on every file you touch.
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
- ## Strict Enforcement
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
- ### Branch + Edge Case Coverage
72
- - 100% branch coverage: every if/else, ternary, ??, ?., switch case, catch block
73
- - For every function, enumerate edge cases before writing tests:
74
- - null/undefined, empty values, boundaries, error responses, auth expired, concurrent calls
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
- ### AAA is enforced on every test
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. If you need a comment, rename the function
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 rules. RISK 1. Always applies.
3
- alwaysApply: true
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, passed as prop
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 user = await fetchUser(params.id); // RSC fetch
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: `camelCase.action.ts`
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`