nextjs-hackathon-stack 0.1.40 → 0.1.41
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 +3 -63
- package/package.json +1 -1
- package/template/.claude/agents/backend.md +54 -0
- package/template/.claude/agents/business-analyst.md +195 -0
- package/template/.claude/agents/code-reviewer.md +76 -0
- package/template/.claude/agents/frontend.md +85 -0
- package/template/.claude/agents/security-researcher.md +54 -0
- package/template/.claude/agents/technical-lead.md +92 -0
- package/template/.claude/agents/test-qa.md +85 -0
- package/template/.claude/rules/architecture.mdc +48 -0
- package/template/.claude/rules/coding-standards.mdc +120 -0
- package/template/.claude/rules/components.mdc +49 -0
- package/template/.claude/rules/data-fetching.mdc +115 -0
- package/template/.claude/rules/forms.mdc +100 -0
- package/template/.claude/rules/general.mdc +54 -0
- package/template/.claude/rules/migrations.mdc +11 -0
- package/template/.claude/rules/nextjs.mdc +71 -0
- package/template/.claude/rules/security.mdc +108 -0
- package/template/.claude/rules/supabase.mdc +70 -0
- package/template/.claude/rules/testing.mdc +136 -0
- package/template/.claude/settings.json +16 -0
- package/template/.claude/skills/build-feature/SKILL.md +198 -0
- package/template/.claude/skills/build-feature/references/server-action-test-template.md +103 -0
- package/template/.claude/skills/create-api-route/SKILL.md +62 -0
- package/template/.claude/skills/discover-feature/SKILL.md +200 -0
- package/template/.claude/skills/memory/SKILL.md +208 -0
- package/template/.claude/skills/review-branch/SKILL.md +43 -0
- package/template/.claude/skills/review-branch/references/review-checklist.md +36 -0
- package/template/.claude/skills/security-audit/SKILL.md +40 -0
- package/template/.claude/skills/security-audit/references/audit-steps.md +41 -0
- package/template/.claude/skills/supabase/SKILL.md +105 -0
- package/template/.claude/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/template/.claude/skills/supabase/references/skill-feedback.md +17 -0
- package/template/.claude/skills/supabase-postgres-best-practices/SKILL.md +65 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp__contributing.md +170 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp__sections.md +39 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp__template.md +34 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_advanced-full-text-search.md +55 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_advanced-jsonb-indexing.md +49 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_conn-idle-timeout.md +46 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_conn-limits.md +44 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_conn-pooling.md +41 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_conn-prepared-statements.md +46 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_data-batch-inserts.md +54 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_data-n-plus-one.md +53 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_data-pagination.md +50 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_data-upsert.md +50 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_lock-advisory.md +56 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_lock-deadlock-prevention.md +68 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_lock-short-transactions.md +50 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_lock-skip-locked.md +54 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_monitor-explain-analyze.md +45 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_monitor-pg-stat-statements.md +55 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_monitor-vacuum-analyze.md +55 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_query-composite-indexes.md +44 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_query-covering-indexes.md +40 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_query-index-types.md +48 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_query-missing-indexes.md +43 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_query-partial-indexes.md +45 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_schema-constraints.md +80 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_schema-data-types.md +46 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_schema-foreign-key-indexes.md +59 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_schema-lowercase-identifiers.md +55 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_schema-partitioning.md +55 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_schema-primary-keys.md +61 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_security-privileges.md +54 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_security-rls-basics.md +50 -0
- package/template/.claude/skills/supabase-postgres-best-practices/references/pgbp_security-rls-performance.md +57 -0
- package/template/.cursor/agents/business-analyst.md +197 -0
- package/template/.cursor/agents/technical-lead.md +3 -3
- package/template/.cursor/mcp.json +6 -2
- package/template/.cursor/skills/build-feature/SKILL.md +20 -21
- package/template/.cursor/skills/discover-feature/SKILL.md +118 -29
- package/template/.cursor/skills/supabase/SKILL.md +104 -0
- package/template/.cursor/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/template/.cursor/skills/supabase/references/skill-feedback.md +17 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp__contributing.md +170 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp__sections.md +39 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp__template.md +34 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_advanced-full-text-search.md +55 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_advanced-jsonb-indexing.md +49 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_conn-idle-timeout.md +46 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_conn-limits.md +44 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_conn-pooling.md +41 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_conn-prepared-statements.md +46 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_data-batch-inserts.md +54 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_data-n-plus-one.md +53 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_data-pagination.md +50 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_data-upsert.md +50 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_lock-advisory.md +56 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_lock-deadlock-prevention.md +68 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_lock-short-transactions.md +50 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_lock-skip-locked.md +54 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_monitor-explain-analyze.md +45 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_monitor-pg-stat-statements.md +55 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_monitor-vacuum-analyze.md +55 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_query-composite-indexes.md +44 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_query-covering-indexes.md +40 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_query-index-types.md +48 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_query-missing-indexes.md +43 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_query-partial-indexes.md +45 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_schema-constraints.md +80 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_schema-data-types.md +46 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_schema-foreign-key-indexes.md +59 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_schema-lowercase-identifiers.md +55 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_schema-partitioning.md +55 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_schema-primary-keys.md +61 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_security-privileges.md +54 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_security-rls-basics.md +50 -0
- package/template/.cursor/skills/supabase-postgres-best-practices/references/pgbp_security-rls-performance.md +57 -0
- package/template/.mcp.json +16 -0
- package/template/.opencode/agents/backend.md +72 -0
- package/template/.opencode/agents/business-analyst.md +153 -0
- package/template/.opencode/agents/code-reviewer.md +80 -0
- package/template/.opencode/agents/frontend.md +84 -0
- package/template/.opencode/agents/security-researcher.md +58 -0
- package/template/.opencode/agents/technical-lead.md +131 -0
- package/template/.opencode/agents/test-qa.md +103 -0
- package/template/.opencode/memory/architecture-snapshot.md +127 -0
- package/template/.opencode/skills/build-feature/SKILL.md +208 -0
- package/template/.opencode/skills/create-api-route/SKILL.md +63 -0
- package/template/.opencode/skills/discover-feature/SKILL.md +194 -0
- package/template/.opencode/skills/memory/SKILL.md +199 -0
- package/template/.opencode/skills/review-branch/SKILL.md +43 -0
- package/template/.opencode/skills/security-audit/SKILL.md +40 -0
- package/template/.opencode/skills/supabase/SKILL.md +105 -0
- package/template/.opencode/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/template/.opencode/skills/supabase/references/skill-feedback.md +17 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/SKILL.md +65 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp__contributing.md +170 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp__sections.md +39 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp__template.md +34 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_advanced-full-text-search.md +55 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_advanced-jsonb-indexing.md +49 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_conn-idle-timeout.md +46 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_conn-limits.md +44 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_conn-pooling.md +41 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_conn-prepared-statements.md +46 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_data-batch-inserts.md +54 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_data-n-plus-one.md +53 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_data-pagination.md +50 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_data-upsert.md +50 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_lock-advisory.md +56 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_lock-deadlock-prevention.md +68 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_lock-short-transactions.md +50 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_lock-skip-locked.md +54 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_monitor-explain-analyze.md +45 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_monitor-pg-stat-statements.md +55 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_monitor-vacuum-analyze.md +55 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_query-composite-indexes.md +44 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_query-covering-indexes.md +40 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_query-index-types.md +48 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_query-missing-indexes.md +43 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_query-partial-indexes.md +45 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_schema-constraints.md +80 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_schema-data-types.md +46 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_schema-foreign-key-indexes.md +59 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_schema-lowercase-identifiers.md +55 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_schema-partitioning.md +55 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_schema-primary-keys.md +61 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_security-privileges.md +54 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_security-rls-basics.md +50 -0
- package/template/.opencode/skills/supabase-postgres-best-practices/references/pgbp_security-rls-performance.md +57 -0
- package/template/.requirements/README.md +1 -1
- package/template/AGENTS.md +1 -1
- package/template/CLAUDE.md +1 -1
- package/template/README.md +15 -2
- package/template/_gitignore +3 -0
- package/template/docker-compose.yml +26 -0
- package/template/ia-flow.md +341 -0
- package/template/opencode.json +23 -0
- package/template/.cursor/agents/business-intelligence.md +0 -83
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-qa
|
|
3
|
+
description: Testing specialist. Use when writing tests, enforcing TDD workflow, fixing coverage gaps, or debugging test failures. Always write tests BEFORE implementation. Triggers: writing tests, TDD workflow, coverage gaps, test failures, mock setup.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test & QA Agent
|
|
7
|
+
|
|
8
|
+
## First Step — Load Context via MCP Memory
|
|
9
|
+
|
|
10
|
+
1. Read `package.json` to get the project name (`<project-name>`)
|
|
11
|
+
2. Call `search_memory` with `tags: ["project:<project-name>", "domain:patterns"]` and `query: "test mock supabase"` — canonical test references and mock setup (`makeChain`, `makeSupabaseMock`)
|
|
12
|
+
3. **Fallback**: if the memory service is unavailable or returns no results, read `.cursor/memory/architecture-snapshot.md` directly
|
|
13
|
+
|
|
14
|
+
Import `makeChain` and `makeSupabaseMock` from `@/shared/test-utils/supabase-mock` — never recreate the mock chain from scratch.
|
|
15
|
+
|
|
16
|
+
## TDD Workflow
|
|
17
|
+
|
|
18
|
+
1. **RED** — write failing test that describes the desired behavior
|
|
19
|
+
2. **GREEN** — write minimum code to make test pass
|
|
20
|
+
3. **REFACTOR** — clean up while keeping tests green
|
|
21
|
+
4. **VERIFY** — run `pnpm test:coverage` and confirm 100%
|
|
22
|
+
|
|
23
|
+
## Verification Gates (mandatory — show output, do not just claim)
|
|
24
|
+
|
|
25
|
+
Each phase requires pasting the actual terminal output before proceeding:
|
|
26
|
+
|
|
27
|
+
| Gate | Command | Expected output |
|
|
28
|
+
|------|---------|-----------------|
|
|
29
|
+
| After RED | `pnpm test:unit` | All new tests **FAIL** (red) |
|
|
30
|
+
| After GREEN | `pnpm test:unit` | All tests **PASS** (green) |
|
|
31
|
+
| After VERIFY | `pnpm test:coverage` | ≥95% statements/functions/lines, ≥90% branches |
|
|
32
|
+
|
|
33
|
+
**Never say "tests pass" or "tests fail" without showing the actual output.**
|
|
34
|
+
**Do NOT write any implementation code until the RED gate output confirms failures.**
|
|
35
|
+
|
|
36
|
+
## Key Rules (auto-loaded by file context)
|
|
37
|
+
- Testing standards, AAA pattern, coverage: `testing.mdc`
|
|
38
|
+
- TDD iron rules: `coding-standards.mdc`
|
|
39
|
+
|
|
40
|
+
## Coverage Requirement
|
|
41
|
+
```
|
|
42
|
+
branches: 90%
|
|
43
|
+
functions: 95%
|
|
44
|
+
lines: 95%
|
|
45
|
+
statements: 95%
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use `/* v8 ignore next */` for genuinely unreachable defensive branches only.
|
|
49
|
+
|
|
50
|
+
After every change:
|
|
51
|
+
1. Run `pnpm test:coverage`
|
|
52
|
+
2. If below thresholds, add missing tests
|
|
53
|
+
3. Never mark work complete without meeting thresholds
|
|
54
|
+
|
|
55
|
+
## Supabase Mock Pattern
|
|
56
|
+
|
|
57
|
+
Always use the shared helpers — never inline mock chain construction:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { makeChain, makeSupabaseMock } from "@/shared/test-utils/supabase-mock";
|
|
61
|
+
|
|
62
|
+
// Unauthenticated
|
|
63
|
+
const { mockClient } = makeSupabaseMock({ user: null });
|
|
64
|
+
|
|
65
|
+
// Authenticated + DB success
|
|
66
|
+
const { mockClient, mockFrom } = makeSupabaseMock({ user: { id: "user-1" } });
|
|
67
|
+
mockFrom.mockReturnValue(makeChain({ data: [...], error: null }));
|
|
68
|
+
|
|
69
|
+
// DB error
|
|
70
|
+
mockFrom.mockReturnValue(makeChain({ error: { message: "db error" } }));
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Test Plan Compliance
|
|
74
|
+
|
|
75
|
+
When the Technical Lead provides a test plan, follow it:
|
|
76
|
+
- Use the specified test files, `describe` block names, and mock setup
|
|
77
|
+
- Flag missing edge cases to the TL **before** adding unplanned tests — do not expand scope unilaterally
|
|
78
|
+
- If the plan is ambiguous or incomplete, ask for clarification rather than guessing
|
|
79
|
+
|
|
80
|
+
For the full Server Action test template, see `build-feature` skill references (`references/server-action-test-template.md`).
|
|
81
|
+
|
|
82
|
+
## Guardrails
|
|
83
|
+
- Never write implementation without a failing test first
|
|
84
|
+
- Never mock internal project code — only external deps
|
|
85
|
+
- Verify all edge cases: empty states, errors, loading states
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Feature-based architecture rules. Applies to all source files.
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: ["src/**"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Architecture Rules
|
|
8
|
+
|
|
9
|
+
## Feature-Based Structure
|
|
10
|
+
```
|
|
11
|
+
features/
|
|
12
|
+
└── <feature-name>/
|
|
13
|
+
├── components/ # React components
|
|
14
|
+
├── actions/ # Server Actions (*.action.ts)
|
|
15
|
+
├── queries/ # Read queries — *.queries.ts (repository layer)
|
|
16
|
+
├── hooks/ # React hooks (use-*.ts)
|
|
17
|
+
├── api/ # Route handlers (route.ts)
|
|
18
|
+
├── lib/ # Feature-specific utilities, schemas, and types
|
|
19
|
+
└── __tests__/ # Colocated tests
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Dependency Direction
|
|
23
|
+
```
|
|
24
|
+
features/* → shared/* ✅
|
|
25
|
+
shared/* → features/* ❌ (circular dependency)
|
|
26
|
+
features/a → features/b ❌ (cross-feature import)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If two features share logic, move it to `shared/`.
|
|
30
|
+
|
|
31
|
+
## Layer Rules
|
|
32
|
+
- `app/` — routing, layouts, RSC data fetching
|
|
33
|
+
- `features/` — feature business logic, UI, actions
|
|
34
|
+
- `shared/lib/` — infrastructure (supabase, ai, db)
|
|
35
|
+
- `shared/components/` — design system components
|
|
36
|
+
|
|
37
|
+
## Error Boundaries
|
|
38
|
+
- Place at route level (`app/(protected)/layout.tsx`)
|
|
39
|
+
- Never let unhandled errors propagate to root
|
|
40
|
+
|
|
41
|
+
## Server Actions
|
|
42
|
+
- File naming: `name.action.ts`
|
|
43
|
+
- Always `"use server"` at top of file
|
|
44
|
+
- Validate input with Zod before any business logic
|
|
45
|
+
- Return typed result objects, use `redirect()` for navigation
|
|
46
|
+
- Actions that mutate data must return `ActionResult` from `@/shared/lib/action-result` — never return `void`
|
|
47
|
+
- The calling component is responsible for showing `toast.success()` / `toast.error()` based on the result
|
|
48
|
+
- NEVER import or call client-only APIs inside server actions (`toast` from sonner, React hooks, `window`, `document`). Server actions run on the server where these APIs do not exist and will throw at runtime.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Code quality standards. Applies to TypeScript/TSX source files.
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: ["src/**/*.ts", "src/**/*.tsx"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Coding Standards
|
|
8
|
+
|
|
9
|
+
## Absolute Rules
|
|
10
|
+
- **Zero `any`** — use `unknown` + type guards or proper generics. `@typescript-eslint/no-explicit-any: error`
|
|
11
|
+
- **Zero comments in production code** — code must be self-documenting through naming. Test AAA labels (`// Arrange`, `// Act`, `// Assert`) are required in tests.
|
|
12
|
+
- **Zero magic numbers/strings** — use typed constants
|
|
13
|
+
- **No `console.log`** in production code — `no-console: "error"`
|
|
14
|
+
- **No `!` non-null assertions** — use proper null checks
|
|
15
|
+
- **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.
|
|
16
|
+
|
|
17
|
+
## AI Agent Lint Pre-Flight
|
|
18
|
+
|
|
19
|
+
Before writing any code, internalize these rules. The project runs `eslint --max-warnings 0` — any violation blocks commits. These are the patterns that most commonly trip AI code generation:
|
|
20
|
+
|
|
21
|
+
- **`no-console: "error"`** — never use `console.*` in production code. Use proper error handling (`throw`) or `ActionResult` error returns.
|
|
22
|
+
- **`strict-boolean-expressions`** — never write `if (value)` for non-boolean types. Use explicit checks:
|
|
23
|
+
- `string`: `if (str !== "")`
|
|
24
|
+
- `number`: `if (n !== 0)`
|
|
25
|
+
- `T | null | undefined`: `if (value !== null && value !== undefined)`
|
|
26
|
+
- `T[]`: `if (array.length > 0)`
|
|
27
|
+
- JSX: `{condition !== null && condition !== undefined && <Component />}`
|
|
28
|
+
- **`naming-convention`** — `camelCase` for variables/functions, `PascalCase` for types/interfaces/React components, `UPPER_CASE` allowed for constants. Never use `snake_case` except in `property` selectors (DB columns, JSON keys).
|
|
29
|
+
- **`no-floating-promises`** — always `await` or prefix with `void` for fire-and-forget.
|
|
30
|
+
- **`restrict-template-expressions`** — never interpolate non-string/number values directly: `` `${someObject}` `` → `` `${someObject.toString()}` ``.
|
|
31
|
+
- **`no-unnecessary-condition`** — never check conditions that the type system proves always truthy/falsy.
|
|
32
|
+
- **`no-unsafe-*`** — never pass `any`-typed values to typed parameters. Narrow with Zod or type guards first.
|
|
33
|
+
|
|
34
|
+
> **Rule of thumb**: If unsure whether a pattern is valid, read `eslint.config.ts` before writing.
|
|
35
|
+
|
|
36
|
+
## TDD
|
|
37
|
+
|
|
38
|
+
Follow the RED → GREEN → REFACTOR cycle. See `testing.mdc` for the full rules and iron rules. Summary: never write implementation without a prior failing test.
|
|
39
|
+
|
|
40
|
+
## SOLID Principles
|
|
41
|
+
- **S**ingle Responsibility: one reason to change per module
|
|
42
|
+
- **O**pen/Closed: extend behavior without modifying existing code
|
|
43
|
+
- **L**iskov Substitution: subtypes must be substitutable for base types
|
|
44
|
+
- **I**nterface Segregation: prefer small, focused interfaces
|
|
45
|
+
- **D**ependency Inversion: depend on abstractions, not concretions
|
|
46
|
+
|
|
47
|
+
## KISS & DRY
|
|
48
|
+
- Simplest solution that works — no over-engineering
|
|
49
|
+
- Three similar lines is better than a premature abstraction
|
|
50
|
+
- Extract abstraction only when you have 3+ real use cases
|
|
51
|
+
|
|
52
|
+
## Size Limits
|
|
53
|
+
- Functions: max 20 lines
|
|
54
|
+
- Files: max 200 lines
|
|
55
|
+
- If exceeded, split by responsibility
|
|
56
|
+
|
|
57
|
+
## Error Handling
|
|
58
|
+
- Use `Result` types for expected failures
|
|
59
|
+
- Never swallow errors silently
|
|
60
|
+
- Always handle Promise rejections
|
|
61
|
+
|
|
62
|
+
## Types
|
|
63
|
+
- Prefer `interface` for object shapes (enforced by ESLint `consistent-type-definitions`). Use `type` for unions, intersections, and utility types.
|
|
64
|
+
- Use `satisfies` for type-checked literals
|
|
65
|
+
- Use `as const` for immutable literals
|
|
66
|
+
- **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.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// ✅
|
|
70
|
+
const schema = z.array(supportItemSchema);
|
|
71
|
+
const items = schema.parse(data);
|
|
72
|
+
|
|
73
|
+
// ❌
|
|
74
|
+
const items = data as SupportListItem[];
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Language Rules
|
|
78
|
+
|
|
79
|
+
- **Code-level text must be in English**: test `it()` descriptions, `describe()` block names, variable names, code comments, developer-facing `Error` constructor messages (never shown to users), `console` output
|
|
80
|
+
- **User-facing text stays in the target language**: UI labels, placeholders, toast messages, form validation messages shown on screen, `ActionResult` error/success strings that end up displayed in the UI
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// ✅ Code-level: English
|
|
84
|
+
it("returns error when candidate is not found", async () => { ... });
|
|
85
|
+
throw new Error("Unexpected DB response shape"); // developer-facing, never shown in UI
|
|
86
|
+
|
|
87
|
+
// ✅ User-facing: target language (Spanish)
|
|
88
|
+
return { success: false, error: "Candidato no encontrado" }; // shown in toast
|
|
89
|
+
toast.error("No se pudo guardar el cambio");
|
|
90
|
+
|
|
91
|
+
// ❌ Wrong: code-level in wrong language
|
|
92
|
+
it("retorna error si el candidato no existe", async () => { ... });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Coverage Exclusions (configure upfront, not reactively)
|
|
96
|
+
|
|
97
|
+
Before writing tests, add exclusions to `vitest.config.ts` for:
|
|
98
|
+
- Drizzle schema definition files (`src/shared/db/*.schema.ts`)
|
|
99
|
+
- Pure type files (files with only `type`/`interface` exports, no runtime logic)
|
|
100
|
+
- Third-party UI wrapper components that rely on portals or browser APIs not supported in jsdom (e.g., `sonner.tsx`, toast providers)
|
|
101
|
+
|
|
102
|
+
Configure these exclusions **before writing tests** to avoid reactive coverage fixes mid-implementation.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// vitest.config.ts
|
|
106
|
+
coverage: {
|
|
107
|
+
exclude: [
|
|
108
|
+
"src/shared/db/*.schema.ts",
|
|
109
|
+
"src/shared/components/ui/sonner.tsx",
|
|
110
|
+
// add other portal/type-only files here
|
|
111
|
+
],
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Pre-commit Quality Gates
|
|
116
|
+
Every commit must pass two sequential gates:
|
|
117
|
+
1. **lint-staged** — runs `eslint --max-warnings 0` on staged `.ts/.tsx` files. Any lint warning or error blocks the commit immediately (before the slower test suite runs).
|
|
118
|
+
2. **Full test suite** — runs `vitest run --coverage` with 95% statement/function/line and 90% branch coverage required.
|
|
119
|
+
|
|
120
|
+
Code that fails either gate cannot be committed.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: shadcn/ui and Tailwind v4 component standards.
|
|
3
|
+
globs: ["src/**/components/**"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Component Standards
|
|
8
|
+
|
|
9
|
+
## shadcn/ui
|
|
10
|
+
- Use shadcn/ui components from `@/shared/components/ui/`
|
|
11
|
+
- Never build custom UI primitives when shadcn has one
|
|
12
|
+
- Extend with `className` prop via `cn()` utility
|
|
13
|
+
- New York style, CSS variables for theming
|
|
14
|
+
|
|
15
|
+
## shadcn/ui (MCP + CLI)
|
|
16
|
+
- **MCP tools** (preferred): `shadcn_search`, `shadcn_docs`, `shadcn_install`, `shadcn_diff`
|
|
17
|
+
- Before writing component code, use `shadcn_docs <component>` to get up-to-date API and usage patterns
|
|
18
|
+
- Use `shadcn_search <query>` to discover available components
|
|
19
|
+
- Install components with `shadcn_install` or `shadcn add <component>` — never copy-paste component source manually
|
|
20
|
+
- Use `shadcn_diff <component>` to check for upstream changes to already-installed components
|
|
21
|
+
- The shadcn skill (`pnpm dlx skills add shadcn/ui`) is installed during scaffolding — provides project context automatically
|
|
22
|
+
|
|
23
|
+
## Tailwind v4
|
|
24
|
+
- CSS-native config via `@theme {}` in CSS
|
|
25
|
+
- No `tailwind.config.js` — all config in `tailwind.css`
|
|
26
|
+
- No inline styles, no CSS modules
|
|
27
|
+
- Use Tailwind utility classes only
|
|
28
|
+
|
|
29
|
+
## Design Tokens (Required)
|
|
30
|
+
- Always use semantic token classes (`bg-primary`, `text-destructive`, `text-muted-foreground`, `border-border`, etc.)
|
|
31
|
+
- Never use raw Tailwind palette colors (`bg-blue-500`, `text-gray-700`, `text-red-500`, etc.)
|
|
32
|
+
- All colors must reference CSS variables defined in `@theme {}` in `tailwind.css`
|
|
33
|
+
- Available semantic colors: primary, secondary, accent, destructive, success, warning, info, muted, background, foreground, border, input, ring
|
|
34
|
+
- If a needed color does not exist as a token, add it to `@theme {}` in `tailwind.css` first
|
|
35
|
+
|
|
36
|
+
## Accessibility (Required)
|
|
37
|
+
- Semantic HTML elements (`<button>`, `<nav>`, `<main>`, etc.)
|
|
38
|
+
- ARIA labels for interactive elements without visible text
|
|
39
|
+
- Keyboard navigation support
|
|
40
|
+
- Color contrast ratios (WCAG AA minimum)
|
|
41
|
+
- `role="alert"` for error messages
|
|
42
|
+
|
|
43
|
+
## Server vs Client Components
|
|
44
|
+
- Default to Server Components (no `"use client"`)
|
|
45
|
+
- Add `"use client"` only when you need:
|
|
46
|
+
- Event handlers (`onClick`, `onChange`, etc.)
|
|
47
|
+
- Hooks (`useState`, `useEffect`, etc.)
|
|
48
|
+
- Browser APIs
|
|
49
|
+
- Keep Client Components small — push them to leaf nodes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: TanStack Query vs RSC data fetching boundaries. Applied when working on components, pages, queries, hooks, or actions.
|
|
3
|
+
globs: ["src/**/components/**", "src/**/queries/**", "src/**/hooks/**", "src/**/actions/**", "src/app/**"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Data Fetching Rules (Risk 1: TanStack Query + RSC Boundary)
|
|
8
|
+
|
|
9
|
+
## Hard Boundary
|
|
10
|
+
|
|
11
|
+
| Use Case | Where |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Initial page data | RSC `async` Server Component |
|
|
14
|
+
| Background refresh / polling | TanStack Query |
|
|
15
|
+
| User-triggered client reads | TanStack Query |
|
|
16
|
+
| All mutations | Server Actions ONLY |
|
|
17
|
+
|
|
18
|
+
**Never use TanStack Query mutations. Never use `useMutation`.**
|
|
19
|
+
|
|
20
|
+
## WRONG ❌
|
|
21
|
+
```typescript
|
|
22
|
+
// ❌ Fetching initial data in a Client Component with TanStack Query
|
|
23
|
+
"use client";
|
|
24
|
+
export function UserProfile({ userId }: { userId: string }) {
|
|
25
|
+
const { data } = useQuery({
|
|
26
|
+
queryKey: ["user", userId],
|
|
27
|
+
queryFn: () => fetchUser(userId), // Initial data should come from RSC
|
|
28
|
+
});
|
|
29
|
+
return <div>{data?.name}</div>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ❌ Mutation with TanStack Query
|
|
33
|
+
const mutation = useMutation({
|
|
34
|
+
mutationFn: (data) => updateUser(data), // Use Server Action instead
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## CORRECT ✅
|
|
39
|
+
```typescript
|
|
40
|
+
// ✅ Initial data from RSC via feature's queries/ module
|
|
41
|
+
import { getUserById } from "@/features/users/queries/users.queries";
|
|
42
|
+
|
|
43
|
+
export default async function UserPage({ params }: { params: { id: string } }) {
|
|
44
|
+
const supabase = await createClient();
|
|
45
|
+
const { data: user } = await getUserById(supabase, params.id); // via repository
|
|
46
|
+
return <UserProfile initialUser={user} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ✅ TanStack Query for background refresh only
|
|
50
|
+
// queryFn must call a function from the feature's queries/ module — never inline supabase.from() here
|
|
51
|
+
"use client";
|
|
52
|
+
export function UserProfile({ initialUser }: { initialUser: User }) {
|
|
53
|
+
const { data: user } = useQuery({
|
|
54
|
+
queryKey: ["user", initialUser.id],
|
|
55
|
+
queryFn: () => fetchUser(initialUser.id), // fetchUser lives in features/users/queries/
|
|
56
|
+
initialData: initialUser, // Seeded from RSC
|
|
57
|
+
staleTime: 5 * 60 * 1000,
|
|
58
|
+
});
|
|
59
|
+
return <div>{user.name}</div>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ✅ Mutation via Server Action
|
|
63
|
+
async function updateUserAction(formData: FormData) {
|
|
64
|
+
"use server";
|
|
65
|
+
await db.update(users).set({ name: formData.get("name") });
|
|
66
|
+
revalidatePath("/profile");
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Error Handling
|
|
71
|
+
|
|
72
|
+
TanStack Query errors must be handled via the `error` property — never swallow them silently.
|
|
73
|
+
|
|
74
|
+
- **Data queries** (background reads): show an inline error state in the component
|
|
75
|
+
- **User-triggered actions**: show a toast via `sonner` (see `forms.mdc` Pattern A)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
"use client";
|
|
79
|
+
export function UserProfile({ initialUser }: { initialUser: User }) {
|
|
80
|
+
const { data: user, error } = useQuery({
|
|
81
|
+
queryKey: ["user", initialUser.id],
|
|
82
|
+
queryFn: () => fetchUser(initialUser.id),
|
|
83
|
+
initialData: initialUser,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (error) return <p role="alert">Failed to load user data.</p>;
|
|
87
|
+
return <div>{user.name}</div>;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Cache Invalidation After Server Actions
|
|
92
|
+
|
|
93
|
+
After a Server Action mutates data, invalidate the relevant TanStack Query cache so the UI reflects the new state.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// In the calling component (Client Component)
|
|
97
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
98
|
+
import { toast } from "sonner";
|
|
99
|
+
|
|
100
|
+
const queryClient = useQueryClient();
|
|
101
|
+
|
|
102
|
+
startTransition(async () => {
|
|
103
|
+
const result = await updateUserAction(formData);
|
|
104
|
+
if (result.status === "success") {
|
|
105
|
+
// Invalidate cache so background queries re-fetch with fresh data
|
|
106
|
+
await queryClient.invalidateQueries({ queryKey: ["user", userId] });
|
|
107
|
+
toast.success(result.message);
|
|
108
|
+
} else {
|
|
109
|
+
toast.error(result.message);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> **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.
|
|
115
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: React Hook Form + Server Actions pattern. Client-side validation via zodResolver + server-side revalidation in the action.
|
|
3
|
+
globs: ["src/**/*form*", "src/**/*.action.ts"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Forms Pattern (Risk 4: RHF + Server Actions)
|
|
8
|
+
|
|
9
|
+
## The One Pattern
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Client: RHF + zodResolver → client validation (UX)
|
|
13
|
+
↓ valid
|
|
14
|
+
Server Action → zod validate again (security) → business logic
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Zod validates TWICE:
|
|
18
|
+
1. Client-side: immediate UX feedback via RHF
|
|
19
|
+
2. Server-side: never trust the client
|
|
20
|
+
|
|
21
|
+
## Template
|
|
22
|
+
```typescript
|
|
23
|
+
// form.tsx (client)
|
|
24
|
+
"use client";
|
|
25
|
+
const schema = z.object({ email: z.string().email() });
|
|
26
|
+
type FormValues = z.infer<typeof schema>;
|
|
27
|
+
|
|
28
|
+
export function MyForm() {
|
|
29
|
+
const [state, formAction, isPending] = useActionState(myAction, { status: "idle" });
|
|
30
|
+
const { register, handleSubmit, formState: { errors } } = useForm<FormValues>({
|
|
31
|
+
resolver: zodResolver(schema),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const onSubmit = handleSubmit((data) => {
|
|
35
|
+
const fd = new FormData();
|
|
36
|
+
Object.entries(data).forEach(([k, v]) => fd.set(k, String(v)));
|
|
37
|
+
formAction(fd);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<form onSubmit={onSubmit}>
|
|
42
|
+
{state.status === "error" && <p role="alert">{state.message}</p>}
|
|
43
|
+
<input {...register("email")} />
|
|
44
|
+
{errors.email && <p>{errors.email.message}</p>}
|
|
45
|
+
<button disabled={isPending}>Submit</button>
|
|
46
|
+
</form>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// action.ts (server)
|
|
51
|
+
"use server";
|
|
52
|
+
const schema = z.object({ email: z.string().email() });
|
|
53
|
+
|
|
54
|
+
export async function myAction(_prev: State, formData: FormData): Promise<State> {
|
|
55
|
+
const parsed = schema.safeParse({ email: formData.get("email") });
|
|
56
|
+
if (!parsed.success) return { status: "error", message: parsed.error.issues[0]?.message ?? "Invalid" };
|
|
57
|
+
// business logic...
|
|
58
|
+
redirect("/success");
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Shared Schema
|
|
63
|
+
|
|
64
|
+
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.
|
|
65
|
+
|
|
66
|
+
## Toast Feedback
|
|
67
|
+
|
|
68
|
+
> **⚠️ Toast calls belong in client components ONLY.** Never import `sonner` or call `toast()` inside a `"use server"` file. The server action returns `ActionResult`; the component reads the result and shows the toast.
|
|
69
|
+
|
|
70
|
+
Every action that affects the database MUST show a toast to the user. No mutation should complete silently.
|
|
71
|
+
|
|
72
|
+
- Use `toast.success(message)` for successful operations
|
|
73
|
+
- Use `toast.error(message)` for failed operations
|
|
74
|
+
- Import `toast` from `sonner`
|
|
75
|
+
- Server actions that mutate data must return `ActionResult` from `@/shared/lib/action-result` (not `void`)
|
|
76
|
+
- Exception: if the action redirects to another page on success, no success toast is needed (the user sees the new page)
|
|
77
|
+
|
|
78
|
+
### Pattern A — Direct action call (button onClick, non-`useActionState` forms)
|
|
79
|
+
```typescript
|
|
80
|
+
import { toast } from "sonner";
|
|
81
|
+
import type { ActionResult } from "@/shared/lib/action-result";
|
|
82
|
+
|
|
83
|
+
startTransition(async () => {
|
|
84
|
+
const result = await myAction(id);
|
|
85
|
+
if (result.status === "success") toast.success(result.message);
|
|
86
|
+
else toast.error(result.message);
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Pattern B — `useActionState` forms
|
|
91
|
+
```typescript
|
|
92
|
+
import { useEffect } from "react";
|
|
93
|
+
import { toast } from "sonner";
|
|
94
|
+
|
|
95
|
+
const [state, formAction] = useActionState(myAction, { status: "idle" });
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (state.status === "error") toast.error(state.message);
|
|
99
|
+
}, [state]);
|
|
100
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Stack overview and project conventions. Always loaded — defines the technology stack, project structure, and naming conventions for this project.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stack Overview
|
|
7
|
+
|
|
8
|
+
## Technology Stack
|
|
9
|
+
- **Framework**: Next.js 16.2 (App Router)
|
|
10
|
+
- **Database**: Supabase (PostgreSQL) with Drizzle ORM for schema/migrations
|
|
11
|
+
- **Auth**: Supabase Auth via `@supabase/ssr`
|
|
12
|
+
- **ORM**: Drizzle + `drizzle-zod` (schema + migrations ONLY — no runtime queries)
|
|
13
|
+
- **DB Runtime Queries**: `supabase-js` client (RLS active)
|
|
14
|
+
- **Client State**: TanStack Query v5
|
|
15
|
+
- **Validation**: Zod (auto-generated via `drizzle-zod`)
|
|
16
|
+
- **Forms**: React Hook Form + Zod resolver
|
|
17
|
+
- **UI**: shadcn/ui + Tailwind CSS v4 (shadcn skill installed for AI context)
|
|
18
|
+
- **AI**: Vercel AI SDK + Google Gemini 2.0 Flash via AI Gateway
|
|
19
|
+
- **Testing**: Vitest + React Testing Library + Playwright
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
├── app/ # Next.js App Router pages
|
|
25
|
+
├── features/ # Feature modules (auth, chat)
|
|
26
|
+
│ └── <feature>/
|
|
27
|
+
│ ├── components/
|
|
28
|
+
│ ├── actions/
|
|
29
|
+
│ ├── queries/
|
|
30
|
+
│ ├── hooks/
|
|
31
|
+
│ ├── api/
|
|
32
|
+
│ ├── lib/
|
|
33
|
+
│ └── __tests__/
|
|
34
|
+
├── shared/ # Shared across features
|
|
35
|
+
│ ├── components/ui/ # shadcn/ui components
|
|
36
|
+
│ ├── lib/ # supabase, ai, utilities
|
|
37
|
+
│ └── db/ # Drizzle schema + migrations
|
|
38
|
+
└── e2e/ # Playwright tests
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Naming Conventions
|
|
42
|
+
- Files: `kebab-case.ts`
|
|
43
|
+
- Components: `PascalCase`
|
|
44
|
+
- Hooks: `useCamelCase`
|
|
45
|
+
- Server Actions: `kebab-case.action.ts`
|
|
46
|
+
- API Routes: `route.ts` (in feature `api/` folder)
|
|
47
|
+
- Tests: `*.test.ts` or `*.test.tsx`
|
|
48
|
+
- E2e: `*.spec.ts`
|
|
49
|
+
|
|
50
|
+
## Import Ordering
|
|
51
|
+
1. Node built-ins
|
|
52
|
+
2. External packages
|
|
53
|
+
3. Internal (`@/`)
|
|
54
|
+
4. Relative (`./`, `../`)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Migration files are auto-generated by Drizzle CLI. NEVER create, edit, or delete them directly.
|
|
3
|
+
globs: ["src/shared/db/migrations/**"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Migration Files — Generated Artifacts
|
|
8
|
+
|
|
9
|
+
Files in `src/shared/db/migrations/` are **auto-generated**. See `supabase.mdc` for the full migration workflow and CLI commands.
|
|
10
|
+
|
|
11
|
+
**Never create, edit, or delete migration files by hand.**
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Next.js 16.2 App Router patterns.
|
|
3
|
+
globs: ["src/app/**"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Next.js 16.2 Rules
|
|
8
|
+
|
|
9
|
+
## App Router Conventions
|
|
10
|
+
- `page.tsx` — public route page component
|
|
11
|
+
- `layout.tsx` — shared layout (wraps child pages)
|
|
12
|
+
- `loading.tsx` — Suspense boundary UI
|
|
13
|
+
- `error.tsx` — Error boundary UI
|
|
14
|
+
- `route.ts` — API route handler
|
|
15
|
+
- `proxy.ts` — Request proxy (project root, Node.js runtime). Intercepts and rewrites requests before they reach route handlers (e.g., session validation, redirects).
|
|
16
|
+
|
|
17
|
+
## Route Groups
|
|
18
|
+
- `(auth)` — unauthenticated routes
|
|
19
|
+
- `(protected)` — authenticated routes with RSC auth check
|
|
20
|
+
|
|
21
|
+
## Data Fetching in RSC
|
|
22
|
+
```typescript
|
|
23
|
+
// ✅ Async Server Component — fetch directly
|
|
24
|
+
export default async function Page() {
|
|
25
|
+
const data = await fetchData(); // No useState, no useEffect
|
|
26
|
+
return <ClientComponent initialData={data} />;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Route Handler Patterns
|
|
31
|
+
```typescript
|
|
32
|
+
// app/api/*/route.ts
|
|
33
|
+
export async function POST(request: Request) {
|
|
34
|
+
const body = await request.json() as unknown;
|
|
35
|
+
const parsed = schema.safeParse(body);
|
|
36
|
+
if (!parsed.success) return Response.json({ error: "Invalid input" }, { status: 400 });
|
|
37
|
+
// ...
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Metadata
|
|
42
|
+
```typescript
|
|
43
|
+
export const metadata: Metadata = {
|
|
44
|
+
title: "Page Title",
|
|
45
|
+
description: "Page description",
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Browser Log Forwarding
|
|
50
|
+
|
|
51
|
+
Next.js 16.2 can forward browser logs to the terminal. Configure in `next.config.ts`:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const nextConfig: NextConfig = {
|
|
55
|
+
logging: {
|
|
56
|
+
browserToTerminal: 'error', // 'error' (default) | 'warn' | true (all) | false (disabled)
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `'error'` — forwards browser errors only (default)
|
|
62
|
+
- `'warn'` — forwards errors and warnings
|
|
63
|
+
- `true` — forwards all console output
|
|
64
|
+
- `false` — disabled
|
|
65
|
+
|
|
66
|
+
## Dev Server Lock File
|
|
67
|
+
|
|
68
|
+
Next.js 16.2 writes `.next/dev/lock` when `next dev` starts. The file contains the PID, port, and URL of the running dev server.
|
|
69
|
+
|
|
70
|
+
- Agents and scripts must check `.next/dev/lock` before starting `next dev` or `next build` to avoid duplicate processes.
|
|
71
|
+
- If the lock file exists and the PID is still alive, attach to the existing server instead of starting a new one.
|