@mars-stack/cli 0.2.0 → 0.2.2

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 (173) hide show
  1. package/package.json +2 -2
  2. package/template/.cursor/rules/composition-patterns.mdc +186 -0
  3. package/template/.cursor/rules/data-access.mdc +29 -0
  4. package/template/.cursor/rules/project-structure.mdc +34 -0
  5. package/template/.cursor/rules/security.mdc +25 -0
  6. package/template/.cursor/rules/testing.mdc +24 -0
  7. package/template/.cursor/rules/ui-conventions.mdc +29 -0
  8. package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
  9. package/template/.cursor/skills/add-audit-log/SKILL.md +373 -0
  10. package/template/.cursor/skills/add-blog/SKILL.md +447 -0
  11. package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
  12. package/template/.cursor/skills/add-component/SKILL.md +158 -0
  13. package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
  14. package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
  15. package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
  16. package/template/.cursor/skills/add-feature/SKILL.md +174 -0
  17. package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
  18. package/template/.cursor/skills/add-page/SKILL.md +151 -0
  19. package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
  20. package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
  21. package/template/.cursor/skills/add-role/SKILL.md +156 -0
  22. package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
  23. package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
  24. package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
  25. package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
  26. package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
  27. package/template/.cursor/skills/build-form/SKILL.md +231 -0
  28. package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
  29. package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
  30. package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
  31. package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
  32. package/template/.cursor/skills/configure-email/SKILL.md +170 -0
  33. package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
  34. package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
  35. package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
  36. package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
  37. package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
  38. package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
  39. package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
  40. package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
  41. package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
  42. package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
  43. package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
  44. package/template/.cursor/skills/configure-search/SKILL.md +581 -0
  45. package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
  46. package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
  47. package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
  48. package/template/.cursor/skills/create-seed/SKILL.md +191 -0
  49. package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
  50. package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
  51. package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
  52. package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
  53. package/template/.cursor/skills/setup-project/SKILL.md +104 -0
  54. package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
  55. package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
  56. package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
  57. package/template/AGENTS.md +104 -0
  58. package/template/ARCHITECTURE.md +102 -0
  59. package/template/docs/QUALITY_SCORE.md +20 -0
  60. package/template/docs/design-docs/conversation-as-system-record.md +70 -0
  61. package/template/docs/design-docs/core-beliefs.md +43 -0
  62. package/template/docs/design-docs/index.md +8 -0
  63. package/template/docs/exec-plans/active/.gitkeep +0 -0
  64. package/template/docs/exec-plans/completed/.gitkeep +0 -0
  65. package/template/docs/exec-plans/tech-debt.md +7 -0
  66. package/template/docs/generated/.gitkeep +0 -0
  67. package/template/docs/product-specs/index.md +7 -0
  68. package/template/docs/references/index.md +18 -0
  69. package/template/e2e/api.spec.ts +20 -0
  70. package/template/e2e/auth.spec.ts +24 -0
  71. package/template/e2e/public.spec.ts +25 -0
  72. package/template/eslint.config.mjs +24 -0
  73. package/template/next-env.d.ts +6 -0
  74. package/template/next.config.ts +45 -0
  75. package/template/package.json +80 -0
  76. package/template/playwright.config.ts +31 -0
  77. package/template/postcss.config.mjs +8 -0
  78. package/template/prisma/generated/prisma/browser.ts +49 -0
  79. package/template/prisma/generated/prisma/client.ts +73 -0
  80. package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
  81. package/template/prisma/generated/prisma/enums.ts +15 -0
  82. package/template/prisma/generated/prisma/internal/class.ts +254 -0
  83. package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
  84. package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
  85. package/template/prisma/generated/prisma/models/Account.ts +1543 -0
  86. package/template/prisma/generated/prisma/models/File.ts +1529 -0
  87. package/template/prisma/generated/prisma/models/Session.ts +1415 -0
  88. package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
  89. package/template/prisma/generated/prisma/models/User.ts +2235 -0
  90. package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
  91. package/template/prisma/generated/prisma/models.ts +17 -0
  92. package/template/prisma/schema/auth.prisma +69 -0
  93. package/template/prisma/schema/base.prisma +8 -0
  94. package/template/prisma/schema/file.prisma +15 -0
  95. package/template/prisma/schema/subscription.prisma +17 -0
  96. package/template/prisma.config.ts +13 -0
  97. package/template/scripts/check-architecture.ts +221 -0
  98. package/template/scripts/check-doc-freshness.ts +242 -0
  99. package/template/scripts/ensure-db.mjs +291 -0
  100. package/template/scripts/generate-docs.ts +143 -0
  101. package/template/scripts/generate-env-example.ts +89 -0
  102. package/template/scripts/seed.ts +56 -0
  103. package/template/scripts/update-quality-score.ts +263 -0
  104. package/template/src/__tests__/architecture.test.ts +114 -0
  105. package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
  106. package/template/src/app/(auth)/layout.tsx +11 -0
  107. package/template/src/app/(auth)/register/page.tsx +162 -0
  108. package/template/src/app/(auth)/reset-password/page.tsx +109 -0
  109. package/template/src/app/(auth)/sign-in/page.tsx +122 -0
  110. package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
  111. package/template/src/app/(auth)/verify/page.tsx +56 -0
  112. package/template/src/app/(protected)/admin/page.tsx +108 -0
  113. package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
  114. package/template/src/app/(protected)/dashboard/page.tsx +22 -0
  115. package/template/src/app/(protected)/layout.tsx +262 -0
  116. package/template/src/app/(protected)/settings/page.tsx +370 -0
  117. package/template/src/app/api/auth/forgot/route.ts +63 -0
  118. package/template/src/app/api/auth/login/route.ts +121 -0
  119. package/template/src/app/api/auth/logout/route.ts +19 -0
  120. package/template/src/app/api/auth/me/route.ts +30 -0
  121. package/template/src/app/api/auth/reset/route.ts +45 -0
  122. package/template/src/app/api/auth/signup/route.ts +85 -0
  123. package/template/src/app/api/auth/verify/route.ts +46 -0
  124. package/template/src/app/api/csrf/route.ts +12 -0
  125. package/template/src/app/api/health/route.ts +10 -0
  126. package/template/src/app/api/protected/admin/users/route.ts +24 -0
  127. package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
  128. package/template/src/app/api/protected/billing/portal/route.ts +39 -0
  129. package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
  130. package/template/src/app/api/protected/files/upload/route.ts +64 -0
  131. package/template/src/app/api/protected/user/password/route.ts +63 -0
  132. package/template/src/app/api/protected/user/profile/route.ts +35 -0
  133. package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
  134. package/template/src/app/api/protected/user/sessions/route.ts +22 -0
  135. package/template/src/app/api/readiness/route.ts +15 -0
  136. package/template/src/app/api/webhooks/stripe/route.ts +166 -0
  137. package/template/src/app/error.tsx +33 -0
  138. package/template/src/app/layout.tsx +29 -0
  139. package/template/src/app/not-found.tsx +20 -0
  140. package/template/src/app/page.tsx +136 -0
  141. package/template/src/app/privacy/page.tsx +178 -0
  142. package/template/src/app/providers.tsx +8 -0
  143. package/template/src/app/terms/page.tsx +139 -0
  144. package/template/src/config/app.config.ts +70 -0
  145. package/template/src/config/routes.ts +17 -0
  146. package/template/src/features/admin/index.ts +11 -0
  147. package/template/src/features/admin/permissions.ts +64 -0
  148. package/template/src/features/auth/context/AuthContext.tsx +96 -0
  149. package/template/src/features/auth/context/index.ts +2 -0
  150. package/template/src/features/auth/index.ts +3 -0
  151. package/template/src/features/auth/server/consent.ts +66 -0
  152. package/template/src/features/auth/server/session-revocation.ts +20 -0
  153. package/template/src/features/auth/server/sessions.ts +66 -0
  154. package/template/src/features/auth/server/user.ts +166 -0
  155. package/template/src/features/auth/types.ts +19 -0
  156. package/template/src/features/auth/validators.ts +29 -0
  157. package/template/src/features/billing/server/index.ts +66 -0
  158. package/template/src/features/billing/types.ts +43 -0
  159. package/template/src/features/uploads/server/index.ts +49 -0
  160. package/template/src/features/uploads/types.ts +26 -0
  161. package/template/src/lib/core/email/templates/base-layout.ts +122 -0
  162. package/template/src/lib/core/email/templates/index.ts +4 -0
  163. package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
  164. package/template/src/lib/core/email/templates/verification-email.ts +41 -0
  165. package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
  166. package/template/src/lib/mars.ts +56 -0
  167. package/template/src/lib/prisma.ts +19 -0
  168. package/template/src/proxy.ts +92 -0
  169. package/template/src/styles/brand.css +17 -0
  170. package/template/src/styles/globals.css +6 -0
  171. package/template/tsconfig.json +59 -0
  172. package/template/vitest.config.ts +41 -0
  173. package/template/vitest.setup.ts +24 -0
@@ -0,0 +1,219 @@
1
+ # Skill: Test an API Route
2
+
3
+ Write unit tests for a MARS API route using Vitest, mock-auth, and Prisma mocking.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks to test an API endpoint, write backend tests, or add test coverage for a route handler.
8
+
9
+ ## Architecture
10
+
11
+ MARS API route tests:
12
+ - Import and call the exported handler function directly (no HTTP server needed)
13
+ - Mock Prisma, auth session, and external services
14
+ - Assert response status and body
15
+ - Test all paths: success, validation errors, auth errors, database errors
16
+
17
+ ## Template: Full API Route Test
18
+
19
+ ```typescript
20
+ // src/app/api/protected/projects/route.test.ts
21
+ import { mockAuth, createTestRequest, createTestRequestWithBody } from '@mars-stack/core/test-utils';
22
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
23
+ import { GET, POST } from './route';
24
+
25
+ // Mock database
26
+ const mockFindMany = vi.fn();
27
+ const mockCreate = vi.fn();
28
+
29
+ vi.mock('@/lib/prisma', () => ({
30
+ prisma: {
31
+ project: {
32
+ findMany: (...args: unknown[]) => mockFindMany(...args),
33
+ create: (...args: unknown[]) => mockCreate(...args),
34
+ },
35
+ },
36
+ isDatabaseError: vi.fn(() => false),
37
+ formatDatabaseError: vi.fn(),
38
+ }));
39
+
40
+ // Mock auth -- return a valid session
41
+ vi.mock('@/lib/mars', () => ({
42
+ verifySessionForAPI: vi.fn(() => Promise.resolve(mockAuth)),
43
+ }));
44
+
45
+ describe('GET /api/protected/projects', () => {
46
+ beforeEach(() => {
47
+ vi.clearAllMocks();
48
+ });
49
+
50
+ it('returns projects for the authenticated user', async () => {
51
+ const projects = [
52
+ { id: '1', name: 'Project A', userId: mockAuth.userId },
53
+ { id: '2', name: 'Project B', userId: mockAuth.userId },
54
+ ];
55
+ mockFindMany.mockResolvedValue(projects);
56
+
57
+ const request = createTestRequest('GET', '/api/protected/projects');
58
+ const response = await GET(request);
59
+ const body = await response.json();
60
+
61
+ expect(response.status).toBe(200);
62
+ expect(body).toHaveLength(2);
63
+ expect(mockFindMany).toHaveBeenCalledWith(
64
+ expect.objectContaining({
65
+ where: { userId: mockAuth.userId },
66
+ }),
67
+ );
68
+ });
69
+
70
+ it('returns 401 when not authenticated', async () => {
71
+ const { verifySessionForAPI } = await import('@/lib/mars');
72
+ vi.mocked(verifySessionForAPI).mockResolvedValueOnce(null);
73
+
74
+ const request = createTestRequest('GET', '/api/protected/projects');
75
+ const response = await GET(request);
76
+
77
+ expect(response.status).toBe(401);
78
+ });
79
+ });
80
+
81
+ describe('POST /api/protected/projects', () => {
82
+ beforeEach(() => {
83
+ vi.clearAllMocks();
84
+ });
85
+
86
+ it('creates a project with valid data', async () => {
87
+ const newProject = { id: '3', name: 'New Project', userId: mockAuth.userId };
88
+ mockCreate.mockResolvedValue(newProject);
89
+
90
+ const request = createTestRequestWithBody('POST', '/api/protected/projects', {
91
+ name: 'New Project',
92
+ });
93
+ const response = await POST(request);
94
+ const body = await response.json();
95
+
96
+ expect(response.status).toBe(201);
97
+ expect(body.name).toBe('New Project');
98
+ expect(mockCreate).toHaveBeenCalledWith(
99
+ expect.objectContaining({
100
+ data: expect.objectContaining({
101
+ name: 'New Project',
102
+ userId: mockAuth.userId,
103
+ }),
104
+ }),
105
+ );
106
+ });
107
+
108
+ it('returns 400 for invalid input', async () => {
109
+ const request = createTestRequestWithBody('POST', '/api/protected/projects', {
110
+ name: '', // Empty name should fail validation
111
+ });
112
+ const response = await POST(request);
113
+
114
+ expect(response.status).toBe(400);
115
+ });
116
+
117
+ it('returns 503 when database is unavailable', async () => {
118
+ const dbError = new Error("Can't reach database server");
119
+ Object.defineProperty(dbError, 'name', { value: 'PrismaClientInitializationError' });
120
+ mockCreate.mockRejectedValue(dbError);
121
+
122
+ const { isDatabaseError } = await import('@/lib/prisma');
123
+ vi.mocked(isDatabaseError).mockReturnValueOnce(true);
124
+
125
+ const request = createTestRequestWithBody('POST', '/api/protected/projects', {
126
+ name: 'Test Project',
127
+ });
128
+ const response = await POST(request);
129
+
130
+ expect(response.status).toBe(503);
131
+ });
132
+ });
133
+ ```
134
+
135
+ ## Mock Auth Utilities
136
+
137
+ MARS provides mock auth utilities via `@mars-stack/core/test-utils`:
138
+
139
+ ```typescript
140
+ import { mockAuth, mockAuthAsAdmin, mockAuthAsUser } from '@mars-stack/core/test-utils';
141
+ import { createTestRequest, createTestRequestWithBody } from '@mars-stack/core/test-utils';
142
+
143
+ // mockAuth — default mock session object
144
+ // mockAuthAsAdmin / mockAuthAsUser — role-specific variants
145
+ // createTestRequest(method, path) — build a NextRequest without a body
146
+ // createTestRequestWithBody(method, path, body) — build a NextRequest with JSON body
147
+ ```
148
+
149
+ ## Testing Patterns
150
+
151
+ ### Test Auth Required
152
+
153
+ ```typescript
154
+ it('returns 401 when not authenticated', async () => {
155
+ const { verifySessionForAPI } = await import('@/lib/mars');
156
+ vi.mocked(verifySessionForAPI).mockResolvedValueOnce(null);
157
+ // ...assert 401
158
+ });
159
+ ```
160
+
161
+ ### Test Role Requirement
162
+
163
+ ```typescript
164
+ it('returns 403 for non-admin users', async () => {
165
+ const { verifySessionForAPI } = await import('@/lib/mars');
166
+ vi.mocked(verifySessionForAPI).mockResolvedValueOnce({ ...mockAuth, role: 'user' });
167
+
168
+ // For withRole, also mock the DB role check
169
+ mockFindUnique.mockResolvedValueOnce({ role: 'user' });
170
+
171
+ // ...assert 403
172
+ });
173
+ ```
174
+
175
+ ### Test Validation Errors
176
+
177
+ ```typescript
178
+ it('returns 400 with validation errors', async () => {
179
+ const request = createTestRequestWithBody('POST', '/api/protected/resource', {
180
+ email: 'not-an-email',
181
+ });
182
+ const response = await handler(request);
183
+ const body = await response.json();
184
+
185
+ expect(response.status).toBe(400);
186
+ expect(body.error).toBeDefined();
187
+ });
188
+ ```
189
+
190
+ ### Test Database Errors
191
+
192
+ ```typescript
193
+ it('returns 503 when database is down', async () => {
194
+ mockFindMany.mockRejectedValueOnce(new Error("Can't reach database"));
195
+ // ... assert 503
196
+ });
197
+ ```
198
+
199
+ ## Running Tests
200
+
201
+ ```bash
202
+ yarn test # Run all tests
203
+ yarn test src/app/api/ # Run only API tests
204
+ yarn test:watch # Watch mode
205
+ yarn test:coverage # With coverage report
206
+ ```
207
+
208
+ ## Checklist
209
+
210
+ - [ ] Test file created next to route (`route.test.ts`)
211
+ - [ ] Prisma mocked with `vi.mock('@/lib/prisma')`
212
+ - [ ] Auth session mocked with `vi.mock('@/lib/mars')`
213
+ - [ ] `vi.clearAllMocks()` in `beforeEach`
214
+ - [ ] Success path tested
215
+ - [ ] 401 unauthorized tested
216
+ - [ ] 400 validation error tested
217
+ - [ ] 403 forbidden tested (if role-gated)
218
+ - [ ] 503 database error tested
219
+ - [ ] Response status AND body assertions
@@ -0,0 +1,99 @@
1
+ # Skill: Update Architecture Docs
2
+
3
+ Keep the project documentation in sync with code changes. Covers when and how to update AGENTS.md, QUALITY_SCORE.md, and related docs.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when:
8
+ - You have just added a new feature, service, or module
9
+ - You have changed the database schema
10
+ - You have completed an execution plan
11
+ - The user asks to update docs, sync docs, or refresh documentation
12
+ - Another skill's checklist includes "update docs"
13
+
14
+ ## Prerequisites
15
+
16
+ - Read `AGENTS.md` for the project-level agent guide.
17
+ - Read `docs/QUALITY_SCORE.md` for current quality grades.
18
+
19
+ ## Document Index
20
+
21
+ | Document | Location | Purpose | Update frequency |
22
+ |----------|----------|---------|------------------|
23
+ | `AGENTS.md` | Repo root | First file any agent reads. Overview + pointers | On structure changes |
24
+ | `docs/QUALITY_SCORE.md` | Docs | Quality grades by domain | After every improvement |
25
+ | `docs/design-docs/` | Docs | Architecture decisions and design documents | On architectural changes |
26
+
27
+ ## When to Update Each Document
28
+
29
+ ### AGENTS.md
30
+
31
+ Update when:
32
+ - The directory structure changes (new route groups, new features)
33
+ - New feature modules are added
34
+ - The config structure changes
35
+ - New "How to Run" commands are added
36
+ - Key constraints change
37
+
38
+ Keep it concise — AGENTS.md should fit in a single context window.
39
+
40
+ ### docs/QUALITY_SCORE.md
41
+
42
+ Update when:
43
+ - A quality grade changes (feature implemented, bug fixed, test added)
44
+ - A new domain is added to the grading table
45
+ - An execution plan is completed
46
+
47
+ How to update:
48
+ 1. Find the relevant row in the appropriate table
49
+ 2. Update the grade (A/B/C/D/F)
50
+ 3. Update the notes to reflect current state
51
+
52
+ ```markdown
53
+ | Dashboard | B | Basic layout with stat cards. Missing: chart widgets, date range filter |
54
+ ```
55
+
56
+ ## Step-by-Step: After Adding a Feature
57
+
58
+ 1. **QUALITY_SCORE.md** — Update the grade for the relevant domain:
59
+
60
+ ```markdown
61
+ | Billing | B | Stripe checkout, portal, webhook. Missing: usage-based billing |
62
+ ```
63
+
64
+ 2. **AGENTS.md** — If the feature adds new directories or conventions:
65
+
66
+ ```markdown
67
+ ├── features/
68
+ │ └── billing/ # Subscription management
69
+ ```
70
+
71
+ 3. **Execution plan** — Mark tasks complete, update verification:
72
+
73
+ ```markdown
74
+ - [x] **3.1 Billing page** — Done. `src/app/(protected)/billing/page.tsx`
75
+ ```
76
+
77
+ ## Step-by-Step: After a Schema Change
78
+
79
+ 1. **AGENTS.md** — Update the schema file listing if new schema files added
80
+ 2. **QUALITY_SCORE.md** — Update database-related grades
81
+ 3. **Design docs** — Add a design doc if the schema change represents a significant architectural decision
82
+
83
+ ## Validation
84
+
85
+ After updating docs, verify:
86
+
87
+ 1. **No broken links** — Check that referenced files exist
88
+ 2. **Consistent terminology** — Use the same names as the code
89
+ 3. **Up-to-date grades** — Quality scores reflect actual state
90
+ 4. **AGENTS.md fits in context** — Keep it under ~200 lines
91
+
92
+ ## Checklist
93
+
94
+ - [ ] Identified which documents need updating
95
+ - [ ] AGENTS.md updated (if structure or workflow change)
96
+ - [ ] QUALITY_SCORE.md grades updated
97
+ - [ ] Design docs updated (if architectural decision)
98
+ - [ ] No broken internal links
99
+ - [ ] Execution plan marked as complete (if applicable)
@@ -0,0 +1,104 @@
1
+ # Project Agent Guide
2
+
3
+ > First file any agent reads. Fits in a single context window by design.
4
+ > This file is generated by Mars and customized per project.
5
+
6
+ ## Overview
7
+
8
+ This is a Next.js SaaS application generated by [Mars](https://github.com/mars-js/mars). It includes authentication, role-based access control, design tokens, structured logging, and rate limiting out of the box.
9
+
10
+ **Tech stack:** Next.js 16 (App Router), TypeScript, Prisma (PostgreSQL), Tailwind CSS v4, Zod, Vitest, Playwright.
11
+
12
+ ## Architecture (Layer Model)
13
+
14
+ ```
15
+ Presentation src/app/ Pages, layouts, API routes (may import from any layer)
16
+
17
+ Features src/features/ Feature modules with components/, server/, hooks/
18
+
19
+ Library src/lib/ Wiring layer: mars.ts (core re-exports), prisma.ts
20
+
21
+ Packages @mars-stack/core Auth, email, rate limiting, env, logging, SEO
22
+ @mars-stack/ui Primitives, patterns, hooks, design tokens
23
+
24
+ Database prisma/schema/ Prisma schema files (base.prisma, auth.prisma, ...)
25
+ ```
26
+
27
+ **Dependency direction is strictly top-down.** Features never import from other features. Shared logic goes in `src/lib/`.
28
+
29
+ ## Directory Structure
30
+
31
+ ```
32
+ src/
33
+ ├── app/ Next.js App Router
34
+ │ ├── (auth)/ Auth pages (sign-in, register, forgot/reset password, verify)
35
+ │ ├── (protected)/ Authenticated pages (dashboard, settings)
36
+ │ ├── api/ API routes
37
+ │ │ ├── auth/ Public auth endpoints (login, signup, forgot, reset, verify)
38
+ │ │ ├── protected/ Authenticated API routes (must use withAuth/withRole/withOwnership)
39
+ │ │ └── csrf/ CSRF token endpoint
40
+ │ └── layout.tsx Root layout with providers
41
+ ├── config/
42
+ │ ├── app.config.ts Single source of truth: identity, features, services, theme
43
+ │ └── routes.ts Route constants
44
+ ├── features/
45
+ │ └── auth/ Auth feature module
46
+ │ ├── context/ AuthContext provider
47
+ │ ├── server/ Server-only auth logic (user queries, session, consent)
48
+ │ └── validators.ts Zod validation schemas
49
+ ├── lib/
50
+ │ ├── mars.ts Re-exports from @mars-stack/core (logger, auth middleware, email, etc.)
51
+ │ └── prisma.ts Prisma client singleton
52
+ ├── styles/
53
+ │ ├── globals.css CSS entry point
54
+ │ └── brand.css Brand token overrides
55
+ └── proxy.ts Route protection, CSRF validation, auth redirects (Next.js 16 proxy)
56
+ ```
57
+
58
+ ## Key Constraints
59
+
60
+ 1. **Protected API routes must use auth middleware** — `withAuth`, `withRole`, or `withOwnership` from `@/lib/mars`.
61
+ 2. **Never import across features** — `src/features/billing/` cannot import from `src/features/auth/`. Shared logic goes in `src/lib/`.
62
+ 3. **Use design tokens, not raw colors** — No `bg-blue-500`. Use semantic tokens from `@mars-stack/ui/styles`.
63
+ 4. **Server modules must import `"server-only"`** — Any file under `*/server/` needs `import 'server-only'` as its first import.
64
+ 5. **Parse data at the boundary** — Zod validation on every API route input.
65
+ 6. **User-scoped queries use session userId** — Never accept userId from request params for authorization.
66
+ 7. **Constant-time comparison for secrets** — Use `constantTimeEqual` from `@mars-stack/core`, never `===`.
67
+
68
+ ## Config
69
+
70
+ `src/config/app.config.ts` is the single source of truth for:
71
+ - App identity (name, URL, support email)
72
+ - Feature flags (auth, admin, billing, notifications, etc.)
73
+ - Service providers (email, storage, database, payments, etc.)
74
+ - Theme (primary/secondary color, font, design direction)
75
+
76
+ ## How to Run
77
+
78
+ ```bash
79
+ yarn dev # Start Next.js dev server
80
+ yarn build # Production build
81
+ yarn test # Run Vitest unit tests
82
+ yarn test:e2e # Run Playwright e2e tests
83
+ yarn lint # ESLint
84
+ yarn db:push # Push Prisma schema to database
85
+ yarn db:generate # Generate Prisma client
86
+ yarn db:seed # Seed database
87
+ yarn db:studio # Open Prisma Studio
88
+ ```
89
+
90
+ ## Working Discipline
91
+
92
+ 1. **Commit after every step.** When executing a plan, commit after each completed task — not at the end. Version control is the progress log. Each commit message should reference the plan and step (e.g., `feat(billing): add Subscription model (billing-plan step 1.1)`).
93
+ 2. **Document every executed plan.** All plans — whether they started in a chat, a Cursor plan file, or someone's head — must end up in `docs/exec-plans/completed/` with decisions, discoveries, and outcomes recorded. Use the `capture-conversation-context` skill.
94
+ 3. **Feed conversations back.** Decisions, discoveries, and context that live only in chat threads are invisible to future agents. See [docs/design-docs/conversation-as-system-record.md](docs/design-docs/conversation-as-system-record.md).
95
+
96
+ ## Pointers
97
+
98
+ - **Architecture details:** [docs/design-docs/](docs/design-docs/)
99
+ - **Core beliefs:** [docs/design-docs/core-beliefs.md](docs/design-docs/core-beliefs.md)
100
+ - **Conversation feedback:** [docs/design-docs/conversation-as-system-record.md](docs/design-docs/conversation-as-system-record.md)
101
+ - **Execution plans:** [docs/exec-plans/](docs/exec-plans/)
102
+ - **Quality scores:** [docs/QUALITY_SCORE.md](docs/QUALITY_SCORE.md)
103
+ - **Skills:** [.cursor/skills/](.cursor/skills/)
104
+ - **Rules:** [.cursor/rules/](.cursor/rules/)
@@ -0,0 +1,102 @@
1
+ # Application Architecture
2
+
3
+ ## Layer Model
4
+
5
+ ```mermaid
6
+ flowchart TD
7
+ Presentation["Presentation Layer<br/>src/app/ — Pages, layouts, API routes"]
8
+ Features["Feature Layer<br/>src/features/ — Domain modules"]
9
+ Library["Library Layer<br/>src/lib/ — Wiring (mars.ts, prisma.ts)"]
10
+ Packages["Package Layer<br/>@mars-stack/core, @mars-stack/ui"]
11
+ Database["Database Layer<br/>prisma/schema/ — Prisma models"]
12
+
13
+ Presentation --> Features
14
+ Presentation --> Library
15
+ Presentation --> Packages
16
+ Features --> Library
17
+ Features --> Packages
18
+ Library --> Packages
19
+ Packages --> Database
20
+ ```
21
+
22
+ **Dependency direction is strictly top-down.** A lower layer never imports from a higher layer. Features never import from other features.
23
+
24
+ ## Cross-Cutting Concerns
25
+
26
+ All cross-cutting infrastructure is wired through `configureMars()` in `src/lib/mars.ts`:
27
+
28
+ | Concern | Mechanism | Import |
29
+ |---------|-----------|--------|
30
+ | Authentication | JWE sessions (HKDF + A256GCM) | `@/lib/mars` → `withAuth`, `withRole`, `withOwnership` |
31
+ | CSRF Protection | HMAC tokens with session fingerprint binding | `@/lib/mars` → `generateCSRFToken`, `validateCSRFRequest` |
32
+ | Logging | Pino structured logging (domain-scoped loggers) | `@/lib/mars` → `logger`, `authLogger`, `apiLogger` |
33
+ | Error Handling | Typed API errors with consistent response format | `@/lib/mars` → `handleApiError` |
34
+ | Rate Limiting | Upstash Redis with in-memory fallback | `@mars-stack/core/rate-limit` |
35
+ | Email | Provider interface (SendGrid, Resend, Console) | `@/lib/mars` → `sendEmail` |
36
+ | SEO | JSON-LD structured data builders | `@/lib/mars` → `buildOrganizationJsonLd`, etc. |
37
+ | Env Validation | Zod-based lazy proxy with build-time relaxation | `@/lib/mars` → `env`, `validateJWTSecret` |
38
+
39
+ ## Database Schema
40
+
41
+ Prisma schema files live in `prisma/schema/`:
42
+
43
+ - `base.prisma` — Datasource config, generator config
44
+ - `auth.prisma` — User, Session, VerificationToken models
45
+
46
+ **Naming conventions:** Models are PascalCase singular (`User`, not `Users`). Fields are camelCase. Relations use the model name as the field name.
47
+
48
+ **Entity relationships:**
49
+ ```
50
+ User 1──* Session (active sessions per user)
51
+ User 1──* VerificationToken (email verification / password reset)
52
+ ```
53
+
54
+ ## API Route Conventions
55
+
56
+ **Public auth routes** (`src/app/api/auth/`):
57
+ - Rate limited via `checkRateLimit`
58
+ - Validate input with Zod schemas
59
+ - Return consistent JSON responses via `handleApiError`
60
+
61
+ **Protected routes** (`src/app/api/protected/`):
62
+ - Wrapped with `withAuth`, `withRole`, or `withOwnership`
63
+ - UserId comes from the verified session, never from request params
64
+ - CSRF validation handled by middleware for state-changing methods
65
+
66
+ **Handler pattern:**
67
+ ```typescript
68
+ import { withAuth } from '@/lib/mars';
69
+
70
+ export const GET = withAuth(async (request, context, session) => {
71
+ // session.userId is verified
72
+ // Use prisma with userId in where clause
73
+ });
74
+ ```
75
+
76
+ ## Client-Side Patterns
77
+
78
+ - **AuthContext:** Provides `user`, `isLoading`, `logout` via React Context. Wraps the app in `src/app/providers.tsx`.
79
+ - **Forms:** `useZodForm` hook from `@mars-stack/ui/hooks` for type-safe form state with Zod validation.
80
+ - **Server vs Client Components:** Pages are server components by default. Interactive components use `"use client"` directive. Auth state comes from AuthContext (client) or session verification (server).
81
+
82
+ ## Design Token System
83
+
84
+ Three-layer OKLCH system:
85
+
86
+ 1. **Primitives** (`@mars-stack/ui/styles/primitives.css`): Raw OKLCH color scales — `--brand-50` through `--brand-950`, plus neutral, feedback colors
87
+ 2. **Semantic Tokens** (`@mars-stack/ui/styles/tokens.css`): Intent-mapped tokens — `--surface-primary`, `--text-primary`, `--border-default`, etc. Includes `.dark` overrides.
88
+ 3. **Theme** (`@mars-stack/ui/styles/theme.css`): Tailwind CSS v4 `@theme` block wiring semantic tokens to utility classes
89
+
90
+ **Override path:** Projects customize via `src/styles/brand.css` which overrides primitive values. The semantic layer automatically adapts.
91
+
92
+ ## Proxy (Middleware)
93
+
94
+ `src/proxy.ts` runs on every matched request (Next.js 16 uses `proxy.ts` instead of `middleware.ts`):
95
+
96
+ 1. Coming-soon mode redirect
97
+ 2. JWT_SECRET validation
98
+ 3. Session decryption (non-blocking — null session = unauthenticated)
99
+ 4. CSRF validation for state-changing API requests
100
+ 5. Auth route redirect (authenticated users → dashboard)
101
+ 6. Protected route redirect (unauthenticated users → sign-in with callback URL)
102
+ 7. Admin route access control (role check from session)
@@ -0,0 +1,20 @@
1
+ # Quality Score
2
+
3
+ Grades by domain. Updated as features are built and tested.
4
+
5
+ **Grading scale:**
6
+ - **A** — Complete, tested, documented, meets all quality bars
7
+ - **B** — Functional with minor gaps
8
+ - **C** — Partially implemented, needs significant work
9
+ - **D** — Scaffolded but not implemented
10
+ - **F** — Missing entirely
11
+
12
+ | Domain | Grade | Notes |
13
+ |--------|-------|-------|
14
+ | Auth API | A | Rate limited, CSRF protected, tested |
15
+ | Auth pages | B+ | All flows present. Terms/privacy links need real pages |
16
+ | Middleware | B | Route protection, CSRF validation, auth redirects |
17
+ | Dashboard | D | Placeholder — needs real content |
18
+ | Settings | D | Read-only — needs forms for profile, password, sessions |
19
+ | Navigation | D | Needs responsive nav shell |
20
+ | Accessibility | N/A | Audit pending |
@@ -0,0 +1,70 @@
1
+ # Design Doc: Conversation as System Record
2
+
3
+ **Status:** Accepted
4
+ **Created:** 2026-03-11
5
+
6
+ ## Problem
7
+
8
+ Agent conversations produce valuable context — architectural decisions, trade-off analysis, debugging insights, root cause analysis — that evaporates when the chat session ends. Future agents (and humans) working on the same codebase have no access to why decisions were made, what alternatives were considered, or what pitfalls were discovered.
9
+
10
+ This is the same problem the Harness Engineering team identified: *"Knowledge that lives in Google Docs, chat threads, or people's heads is not accessible to the system."* The solution for conversations is the same as for design docs and plans — feed them back into the repository.
11
+
12
+ ## Principle
13
+
14
+ **Every significant agent conversation must produce a persistent artifact in the repository.** The artifact type depends on the conversation:
15
+
16
+ | Conversation Type | Artifact Location | Format |
17
+ |---|---|---|
18
+ | Planning / roadmap | `docs/exec-plans/active/` → `completed/` | Execution plan markdown |
19
+ | Architecture decision | `docs/design-docs/` | Design doc markdown |
20
+ | Bug investigation | `docs/exec-plans/active/` | Bug fix plan with root cause |
21
+ | Feature implementation | `docs/exec-plans/completed/` | Completed plan with decisions |
22
+ | Refactoring rationale | `docs/design-docs/` | Design doc explaining why |
23
+ | Quality/debt discovery | `docs/QUALITY_SCORE.md` + `docs/exec-plans/tech-debt.md` | Grade updates + debt items |
24
+
25
+ ## What to Capture
26
+
27
+ Not every message needs to be saved. Capture the **decisions and their context**, not the transcript:
28
+
29
+ 1. **What was decided** — the actual choice made
30
+ 2. **What alternatives were considered** — and why they were rejected
31
+ 3. **What was discovered** — bugs found, constraints identified, assumptions invalidated
32
+ 4. **What changed** — files modified, architecture updated, quality scores adjusted
33
+ 5. **What remains** — open questions, known limitations, follow-up work
34
+
35
+ ## What NOT to Capture
36
+
37
+ - The conversational back-and-forth (tone, corrections, false starts)
38
+ - Implementation details already visible in the code diff
39
+ - Obvious choices that don't need justification
40
+
41
+ ## Process
42
+
43
+ ### During the conversation
44
+
45
+ 1. When a significant decision is made, note it. Don't wait until the end.
46
+ 2. When an unexpected constraint is discovered (e.g., "Next.js 16 uses proxy.ts, not middleware.ts"), document it immediately in the relevant artifact.
47
+ 3. When a plan is being executed, update the plan file as tasks complete.
48
+
49
+ ### At the end of the conversation
50
+
51
+ 1. If the conversation produced a plan, ensure it's in `docs/exec-plans/` with correct status.
52
+ 2. If architectural decisions were made, ensure they're in `docs/design-docs/` or in `ARCHITECTURE.md`.
53
+ 3. If quality changed, update `docs/QUALITY_SCORE.md`.
54
+ 4. If tech debt was discovered, update `docs/exec-plans/tech-debt.md`.
55
+ 5. If core beliefs were validated or updated, update `docs/design-docs/core-beliefs.md`.
56
+
57
+ ### For future agents
58
+
59
+ The `AGENTS.md` file points to `docs/` as the knowledge base. Future agents should:
60
+
61
+ 1. Read `docs/exec-plans/completed/` to understand how the system was built and why
62
+ 2. Read `docs/design-docs/` to understand architectural decisions
63
+ 3. Read `docs/QUALITY_SCORE.md` to understand current state and priorities
64
+ 4. Read `docs/exec-plans/tech-debt.md` to understand known limitations
65
+
66
+ ## Origin
67
+
68
+ This design doc was created after executing the Mars Product Roadmap — a plan that served as the complete blueprint for building the Mars system. The plan itself was originally stored in a Cursor-specific file (`.cursor/plans/`), invisible to all future agents. Moving it to `docs/exec-plans/completed/mars-product-roadmap.md` was the first application of this principle.
69
+
70
+ The Mars Product Roadmap is the most important completed execution plan in the repository. It documents every phase of the system's construction, every cross-cutting concern, every key decision made during execution, and the influencing principles from the Harness Engineering article on agent-first development.
@@ -0,0 +1,43 @@
1
+ # Core Beliefs
2
+
3
+ Architectural opinions that govern this project. Enforced mechanically where possible via linters and structural tests.
4
+
5
+ ## Data Validation
6
+
7
+ - Parse data shapes at the boundary — Zod validation on every API route input.
8
+ - Fail fast with clear, structured error messages.
9
+
10
+ ## Security
11
+
12
+ - Server-only modules must import `"server-only"` to prevent client-side bundling of secrets.
13
+ - User-scoped queries must include userId from the session, never from request parameters.
14
+ - Constant-time comparison (`constantTimeEqual`) for all secret/token comparisons.
15
+ - Derive keys with domain separators, never reuse JWT_SECRET directly.
16
+
17
+ ## Architecture
18
+
19
+ - No cross-feature imports. Shared logic goes in `src/lib/`.
20
+ - `app.config.ts` is the single source of truth for feature flags and service providers.
21
+ - Composition over inheritance — `configureMars()` wires services via function composition.
22
+
23
+ ## Technology
24
+
25
+ - Prefer boring, well-documented technology: Next.js, Prisma, Tailwind, Zod.
26
+ - Official SDKs over wrapper libraries for third-party integrations.
27
+
28
+ ## Testing
29
+
30
+ - Every server function needs a corresponding test.
31
+ - Structural tests validate architecture invariants (dependency direction, auth coverage, design tokens).
32
+
33
+ ## Accessibility
34
+
35
+ - All UI components must meet WCAG 2.1 AA (keyboard navigation, focus management, ARIA, contrast).
36
+
37
+ ## Agent-First Development
38
+
39
+ - The repository is the system of record. All knowledge lives in code, docs, skills, and rules.
40
+ - Every agent conversation must feed decisions and context back into the repository as persistent artifacts (execution plans, design docs, quality score updates). See `docs/design-docs/conversation-as-system-record.md`.
41
+ - All executed plans are documented in `docs/exec-plans/completed/`. Plans are references for future agents — they record what was built, what decisions were made, and what was discovered.
42
+ - Commit after every step in a plan. Version control is the progress log. Do not batch all changes into one commit at the end.
43
+ - When documentation falls short, promote the rule into a linter with an agent-legible error message.
@@ -0,0 +1,8 @@
1
+ # Design Documents
2
+
3
+ Catalog of architectural decisions and design rationale for this project.
4
+
5
+ | Document | Status | Summary |
6
+ |----------|--------|---------|
7
+ | [core-beliefs.md](core-beliefs.md) | Active | Architectural operating principles |
8
+ | [conversation-as-system-record.md](conversation-as-system-record.md) | Accepted | Every conversation feeds back into the repo |
File without changes
File without changes