@mars-stack/cli 0.2.0 → 1.0.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 (175) hide show
  1. package/dist/index.js +137 -12
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -3
  4. package/template/.cursor/rules/composition-patterns.mdc +186 -0
  5. package/template/.cursor/rules/data-access.mdc +29 -0
  6. package/template/.cursor/rules/project-structure.mdc +34 -0
  7. package/template/.cursor/rules/security.mdc +25 -0
  8. package/template/.cursor/rules/testing.mdc +24 -0
  9. package/template/.cursor/rules/ui-conventions.mdc +29 -0
  10. package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
  11. package/template/.cursor/skills/add-audit-log/SKILL.md +375 -0
  12. package/template/.cursor/skills/add-blog/SKILL.md +447 -0
  13. package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
  14. package/template/.cursor/skills/add-component/SKILL.md +158 -0
  15. package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
  16. package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
  17. package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
  18. package/template/.cursor/skills/add-feature/SKILL.md +174 -0
  19. package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
  20. package/template/.cursor/skills/add-page/SKILL.md +151 -0
  21. package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
  22. package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
  23. package/template/.cursor/skills/add-role/SKILL.md +156 -0
  24. package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
  25. package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
  26. package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
  27. package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
  28. package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
  29. package/template/.cursor/skills/build-form/SKILL.md +231 -0
  30. package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
  31. package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
  32. package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
  33. package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
  34. package/template/.cursor/skills/configure-email/SKILL.md +170 -0
  35. package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
  36. package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
  37. package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
  38. package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
  39. package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
  40. package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
  41. package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
  42. package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
  43. package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
  44. package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
  45. package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
  46. package/template/.cursor/skills/configure-search/SKILL.md +581 -0
  47. package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
  48. package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
  49. package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
  50. package/template/.cursor/skills/create-seed/SKILL.md +191 -0
  51. package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
  52. package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
  53. package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
  54. package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
  55. package/template/.cursor/skills/setup-project/SKILL.md +104 -0
  56. package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
  57. package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
  58. package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
  59. package/template/AGENTS.md +104 -0
  60. package/template/ARCHITECTURE.md +102 -0
  61. package/template/docs/QUALITY_SCORE.md +20 -0
  62. package/template/docs/design-docs/conversation-as-system-record.md +70 -0
  63. package/template/docs/design-docs/core-beliefs.md +43 -0
  64. package/template/docs/design-docs/index.md +8 -0
  65. package/template/docs/exec-plans/active/.gitkeep +0 -0
  66. package/template/docs/exec-plans/completed/.gitkeep +0 -0
  67. package/template/docs/exec-plans/tech-debt.md +7 -0
  68. package/template/docs/generated/.gitkeep +0 -0
  69. package/template/docs/product-specs/index.md +7 -0
  70. package/template/docs/references/index.md +18 -0
  71. package/template/e2e/api.spec.ts +20 -0
  72. package/template/e2e/auth.spec.ts +24 -0
  73. package/template/e2e/public.spec.ts +25 -0
  74. package/template/eslint.config.mjs +24 -0
  75. package/template/next-env.d.ts +6 -0
  76. package/template/next.config.ts +45 -0
  77. package/template/package.json +80 -0
  78. package/template/playwright.config.ts +31 -0
  79. package/template/postcss.config.mjs +8 -0
  80. package/template/prisma/generated/prisma/browser.ts +49 -0
  81. package/template/prisma/generated/prisma/client.ts +73 -0
  82. package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
  83. package/template/prisma/generated/prisma/enums.ts +15 -0
  84. package/template/prisma/generated/prisma/internal/class.ts +254 -0
  85. package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
  86. package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
  87. package/template/prisma/generated/prisma/models/Account.ts +1543 -0
  88. package/template/prisma/generated/prisma/models/File.ts +1529 -0
  89. package/template/prisma/generated/prisma/models/Session.ts +1415 -0
  90. package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
  91. package/template/prisma/generated/prisma/models/User.ts +2235 -0
  92. package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
  93. package/template/prisma/generated/prisma/models.ts +17 -0
  94. package/template/prisma/schema/auth.prisma +69 -0
  95. package/template/prisma/schema/base.prisma +8 -0
  96. package/template/prisma/schema/file.prisma +15 -0
  97. package/template/prisma/schema/subscription.prisma +17 -0
  98. package/template/prisma.config.ts +13 -0
  99. package/template/scripts/check-architecture.ts +221 -0
  100. package/template/scripts/check-doc-freshness.ts +242 -0
  101. package/template/scripts/ensure-db.mjs +291 -0
  102. package/template/scripts/generate-docs.ts +143 -0
  103. package/template/scripts/generate-env-example.ts +89 -0
  104. package/template/scripts/seed.ts +56 -0
  105. package/template/scripts/update-quality-score.ts +263 -0
  106. package/template/src/__tests__/architecture.test.ts +114 -0
  107. package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
  108. package/template/src/app/(auth)/layout.tsx +11 -0
  109. package/template/src/app/(auth)/register/page.tsx +162 -0
  110. package/template/src/app/(auth)/reset-password/page.tsx +109 -0
  111. package/template/src/app/(auth)/sign-in/page.tsx +122 -0
  112. package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
  113. package/template/src/app/(auth)/verify/page.tsx +56 -0
  114. package/template/src/app/(protected)/admin/page.tsx +108 -0
  115. package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
  116. package/template/src/app/(protected)/dashboard/page.tsx +22 -0
  117. package/template/src/app/(protected)/layout.tsx +262 -0
  118. package/template/src/app/(protected)/settings/page.tsx +370 -0
  119. package/template/src/app/api/auth/forgot/route.ts +63 -0
  120. package/template/src/app/api/auth/login/route.ts +121 -0
  121. package/template/src/app/api/auth/logout/route.ts +19 -0
  122. package/template/src/app/api/auth/me/route.ts +30 -0
  123. package/template/src/app/api/auth/reset/route.ts +45 -0
  124. package/template/src/app/api/auth/signup/route.ts +85 -0
  125. package/template/src/app/api/auth/verify/route.ts +46 -0
  126. package/template/src/app/api/csrf/route.ts +12 -0
  127. package/template/src/app/api/health/route.ts +10 -0
  128. package/template/src/app/api/protected/admin/users/route.ts +24 -0
  129. package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
  130. package/template/src/app/api/protected/billing/portal/route.ts +39 -0
  131. package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
  132. package/template/src/app/api/protected/files/upload/route.ts +64 -0
  133. package/template/src/app/api/protected/user/password/route.ts +63 -0
  134. package/template/src/app/api/protected/user/profile/route.ts +35 -0
  135. package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
  136. package/template/src/app/api/protected/user/sessions/route.ts +22 -0
  137. package/template/src/app/api/readiness/route.ts +15 -0
  138. package/template/src/app/api/webhooks/stripe/route.ts +166 -0
  139. package/template/src/app/error.tsx +33 -0
  140. package/template/src/app/layout.tsx +29 -0
  141. package/template/src/app/not-found.tsx +20 -0
  142. package/template/src/app/page.tsx +136 -0
  143. package/template/src/app/privacy/page.tsx +178 -0
  144. package/template/src/app/providers.tsx +8 -0
  145. package/template/src/app/terms/page.tsx +139 -0
  146. package/template/src/config/app.config.ts +70 -0
  147. package/template/src/config/routes.ts +17 -0
  148. package/template/src/features/admin/index.ts +11 -0
  149. package/template/src/features/admin/permissions.ts +64 -0
  150. package/template/src/features/auth/context/AuthContext.tsx +96 -0
  151. package/template/src/features/auth/context/index.ts +2 -0
  152. package/template/src/features/auth/index.ts +3 -0
  153. package/template/src/features/auth/server/consent.ts +66 -0
  154. package/template/src/features/auth/server/session-revocation.ts +20 -0
  155. package/template/src/features/auth/server/sessions.ts +66 -0
  156. package/template/src/features/auth/server/user.ts +166 -0
  157. package/template/src/features/auth/types.ts +19 -0
  158. package/template/src/features/auth/validators.ts +29 -0
  159. package/template/src/features/billing/server/index.ts +66 -0
  160. package/template/src/features/billing/types.ts +43 -0
  161. package/template/src/features/uploads/server/index.ts +49 -0
  162. package/template/src/features/uploads/types.ts +26 -0
  163. package/template/src/lib/core/email/templates/base-layout.ts +122 -0
  164. package/template/src/lib/core/email/templates/index.ts +4 -0
  165. package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
  166. package/template/src/lib/core/email/templates/verification-email.ts +41 -0
  167. package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
  168. package/template/src/lib/mars.ts +56 -0
  169. package/template/src/lib/prisma.ts +19 -0
  170. package/template/src/proxy.ts +92 -0
  171. package/template/src/styles/brand.css +15 -0
  172. package/template/src/styles/globals.css +7 -0
  173. package/template/tsconfig.json +59 -0
  174. package/template/vitest.config.ts +41 -0
  175. package/template/vitest.setup.ts +24 -0
@@ -0,0 +1,138 @@
1
+ # Skill: Work with the Design Token System
2
+
3
+ Understand and extend the MARS three-layer design token architecture for theming and UI consistency.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks about theming, changing colours, adding dark mode support, creating new design tokens, or understanding the styling system.
8
+
9
+ ## Architecture Overview
10
+
11
+ ```
12
+ Layer 1: Primitive Palette (@mars-stack/ui/styles/primitives.css)
13
+ ↓ referenced by
14
+ Layer 2: Semantic Tokens (@mars-stack/ui/styles/tokens.css)
15
+ ↓ registered as
16
+ Layer 3: Tailwind Theme (@mars-stack/ui/styles/theme.css via @theme)
17
+ ↓ used in
18
+ Components (via Tailwind utility classes)
19
+ ```
20
+
21
+ ### Layer 1: Primitive Palette (`@mars-stack/ui/styles/primitives.css`)
22
+
23
+ Raw colour scales generated from the primary colour. These are building blocks, never used directly in components.
24
+
25
+ ```css
26
+ :root {
27
+ --brand-50: oklch(0.97 0.01 250);
28
+ --brand-100: oklch(0.93 0.03 250);
29
+ /* ... full 50-950 scale */
30
+ --gray-50: oklch(0.985 0 0);
31
+ --gray-100: oklch(0.97 0 0);
32
+ /* ... */
33
+ }
34
+ ```
35
+
36
+ ### Layer 2: Semantic Tokens (`@mars-stack/ui/styles/tokens.css`)
37
+
38
+ What colours *mean*. These swap between light and dark themes.
39
+
40
+ ```css
41
+ :root {
42
+ --surface-card: white;
43
+ --text-primary: var(--gray-900);
44
+ --border-input: var(--gray-300);
45
+ --brand-primary: var(--brand-600);
46
+ }
47
+
48
+ .dark {
49
+ --surface-card: var(--gray-900);
50
+ --text-primary: var(--gray-100);
51
+ --border-input: var(--gray-700);
52
+ --brand-primary: var(--brand-500);
53
+ }
54
+ ```
55
+
56
+ ### Layer 3: Tailwind Theme (`@mars-stack/ui/styles/theme.css`)
57
+
58
+ Registers semantic tokens as Tailwind utilities via `@theme`:
59
+
60
+ ```css
61
+ @theme {
62
+ --color-surface-background: var(--surface-background);
63
+ --color-surface-card: var(--surface-card);
64
+ --color-text-primary: var(--text-primary);
65
+ --color-border-default: var(--border-default);
66
+ --color-brand-primary: var(--brand-primary);
67
+ /* ... */
68
+ }
69
+ ```
70
+
71
+ This enables `bg-surface-card`, `text-text-primary`, `border-border-default`, etc. in components.
72
+
73
+ ## Token Categories
74
+
75
+ | Category | Prefix | Examples |
76
+ |----------|--------|---------|
77
+ | Surfaces | `surface-` | `bg-surface-background`, `bg-surface-card`, `bg-surface-input` |
78
+ | Text | `text-` | `text-text-primary`, `text-text-secondary`, `text-text-muted` |
79
+ | Borders | `border-` | `border-border-default`, `border-border-input`, `border-border-focus` |
80
+ | Brand | `brand-` | `bg-brand-primary`, `hover:bg-brand-primary-hover` |
81
+ | Feedback | varies | `bg-success-muted`, `text-text-error`, `border-border-success` |
82
+ | Interactive | varies | `bg-ghost-hover`, `focus:ring-ring-focus`, `bg-danger-bg` |
83
+ | Navigation | `nav-` | `text-nav-item`, `bg-nav-item-active-bg` |
84
+ | Table | `table-` | `bg-table-header-bg`, `hover:bg-table-row-hover` |
85
+ | Avatar | `avatar-` | `bg-avatar-bg`, `text-avatar-text` |
86
+
87
+ ## How to Change the Theme
88
+
89
+ ### Change Primary Colour
90
+
91
+ 1. Edit `@mars-stack/ui/styles/primitives.css` -- update the `--brand-*` scale to your new colour's oklch values.
92
+ 2. Everything else (semantic tokens, components) updates automatically.
93
+
94
+ ### Add a New Semantic Token
95
+
96
+ 1. Define the token in `@mars-stack/ui/styles/tokens.css` for both `:root` and `.dark`:
97
+ ```css
98
+ :root { --sidebar-accent: var(--brand-100); }
99
+ .dark { --sidebar-accent: var(--brand-900); }
100
+ ```
101
+
102
+ 2. Register it in `@mars-stack/ui/styles/theme.css`:
103
+ ```css
104
+ @theme {
105
+ --color-sidebar-accent: var(--sidebar-accent);
106
+ }
107
+ ```
108
+
109
+ 3. Use in components: `bg-sidebar-accent`.
110
+
111
+ ### Add a New Colour Scale
112
+
113
+ 1. Add the full 50-950 scale in `@mars-stack/ui/styles/primitives.css`.
114
+ 2. Add semantic tokens that reference it in `@mars-stack/ui/styles/tokens.css`.
115
+ 3. Register the semantic tokens in `@mars-stack/ui/styles/theme.css`.
116
+
117
+ ## Rules
118
+
119
+ - **Never use raw Tailwind colours** (`text-gray-900`, `bg-blue-500`) in components. Always use semantic tokens.
120
+ - **Always define dark mode counterparts** when adding new semantic tokens.
121
+ - **Components get their colours from Layer 3** (Tailwind utilities backed by semantic tokens). They never reference Layer 1 directly.
122
+ - The `app.config.ts` `theme.primaryColor` field is used by the CLI during scaffolding to generate the correct primitive palette. At runtime, the CSS tokens are the source of truth.
123
+
124
+ ## Breakpoints
125
+
126
+ Responsive breakpoints are defined in `@mars-stack/ui/styles/breakpoints.css` and registered in theme.css:
127
+
128
+ ```css
129
+ @theme {
130
+ --breakpoint-sm: 640px;
131
+ --breakpoint-md: 768px;
132
+ --breakpoint-lg: 1024px;
133
+ --breakpoint-xl: 1280px;
134
+ --breakpoint-2xl: 1536px;
135
+ }
136
+ ```
137
+
138
+ Use as standard Tailwind responsive prefixes: `md:grid-cols-2`, `lg:px-8`.
@@ -0,0 +1,119 @@
1
+ # Skill: Capture Conversation Context
2
+
3
+ Feed decisions, discoveries, and context from the current agent conversation back into the repository so future agents can access them.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when:
8
+ - A significant architectural decision was made during conversation
9
+ - A multi-step plan was executed and should be recorded
10
+ - A bug was investigated and the root cause should be documented
11
+ - A refactoring rationale needs to be preserved
12
+ - The conversation is ending and produced valuable context
13
+
14
+ ## Prerequisites
15
+
16
+ - Read `docs/design-docs/conversation-as-system-record.md` for the full design doc.
17
+ - Read `docs/exec-plans/` to see existing plans.
18
+ - Read `docs/QUALITY_SCORE.md` for current quality grades.
19
+
20
+ ## Step 1: Identify What Was Produced
21
+
22
+ Review the conversation and categorise the output:
23
+
24
+ | What happened | Artifact to create/update |
25
+ |---|---|
26
+ | Planned work and executed it | `docs/exec-plans/completed/<name>.md` |
27
+ | Planned work (not yet executed) | `docs/exec-plans/active/<name>.md` |
28
+ | Made an architecture decision | `docs/design-docs/<name>.md` |
29
+ | Fixed a bug with interesting root cause | `docs/exec-plans/completed/<bug-name>.md` |
30
+ | Discovered tech debt | `docs/exec-plans/tech-debt.md` |
31
+ | Changed quality grades | `docs/QUALITY_SCORE.md` |
32
+ | Updated core beliefs | `docs/design-docs/core-beliefs.md` |
33
+
34
+ ## Step 2: Write the Artifact
35
+
36
+ ### For completed execution plans
37
+
38
+ Follow the `create-execution-plan` skill format but with status `Completed`. Include:
39
+
40
+ ```markdown
41
+ # Execution Plan: <Title>
42
+
43
+ **Status:** Completed
44
+ **Created:** <date>
45
+ **Completed:** <date>
46
+ **Origin:** <Brief description of what prompted this work>
47
+
48
+ ## Context
49
+ <Why this work was needed>
50
+
51
+ ## What Was Done
52
+ <Summary of changes, organised by phase or area>
53
+
54
+ ## Key Decisions
55
+ <Decisions made during execution, with rationale>
56
+
57
+ ## Discoveries
58
+ <Anything unexpected that was found — framework quirks, bugs, constraints>
59
+ ```
60
+
61
+ ### For design docs
62
+
63
+ ```markdown
64
+ # Design Doc: <Title>
65
+
66
+ **Status:** Accepted / Proposed / Superseded
67
+ **Created:** <date>
68
+
69
+ ## Problem
70
+ <What problem does this solve?>
71
+
72
+ ## Decision
73
+ <What was decided?>
74
+
75
+ ## Alternatives Considered
76
+ <What else was evaluated and why it was rejected?>
77
+
78
+ ## Consequences
79
+ <What are the implications of this decision?>
80
+ ```
81
+
82
+ ### For tech debt updates
83
+
84
+ Append to the appropriate section of `docs/exec-plans/tech-debt.md`:
85
+
86
+ ```markdown
87
+ | <Item> | <Location> | <Impact> | <Tracking> |
88
+ ```
89
+
90
+ ## Step 3: Update Cross-References
91
+
92
+ 1. If a new design doc was created, add it to `docs/design-docs/index.md`.
93
+ 2. If quality scores changed, update `docs/QUALITY_SCORE.md`.
94
+ 3. If an active plan was completed, move it from `active/` to `completed/`.
95
+ 4. If core beliefs were affected, update `docs/design-docs/core-beliefs.md`.
96
+
97
+ ## Step 4: Verify
98
+
99
+ - [ ] Artifact is in the correct directory with correct status
100
+ - [ ] Key decisions include rationale (not just what, but why)
101
+ - [ ] Discoveries document the unexpected (things code can't convey)
102
+ - [ ] Cross-references are updated
103
+ - [ ] No conversation-specific noise (corrections, false starts, tone)
104
+
105
+ ## Anti-Patterns
106
+
107
+ - **Don't dump the transcript.** Extract decisions and context, not the conversation.
108
+ - **Don't document the obvious.** If the code diff tells the story, the artifact should explain only what the diff cannot.
109
+ - **Don't create empty placeholders.** If the conversation didn't produce meaningful context, don't create an artifact.
110
+ - **Don't duplicate what's in the code.** The artifact explains *why*, not *what*.
111
+
112
+ ## Checklist
113
+
114
+ - [ ] Identified conversation output type (plan, decision, discovery, debt)
115
+ - [ ] Created or updated the appropriate artifact
116
+ - [ ] Included rationale for key decisions
117
+ - [ ] Documented unexpected discoveries
118
+ - [ ] Updated cross-references (index files, quality scores)
119
+ - [ ] No transcript noise in the artifact
@@ -0,0 +1,322 @@
1
+ # Skill: Setup Billing (Meta-Skill)
2
+
3
+ Orchestrate the complete billing and subscription system: Stripe integration, webhook handling, subscription UI, customer portal, and documentation updates.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks to:
8
+ - Set up billing or subscriptions
9
+ - Add a paywall or premium features
10
+ - Integrate Stripe end-to-end
11
+ - Add pricing page with checkout
12
+
13
+ This meta-skill chains sub-skills in the correct order. For just the Stripe SDK setup, use `configure-payments` instead.
14
+
15
+ ## Prerequisites
16
+
17
+ - A Stripe account at [stripe.com](https://stripe.com)
18
+ - Read `src/config/app.config.ts` to check if billing is already flagged
19
+ - Read `prisma/schema/` to check for existing billing models
20
+
21
+ ## Execution Order
22
+
23
+ ```
24
+ ┌─────────────────────────────────────────────────────┐
25
+ │ Phase 0: Plan │
26
+ │ create-execution-plan │
27
+ ├─────────────────────────────────────────────────────┤
28
+ │ Phase 1: Payment Provider │
29
+ │ configure-payments (Stripe SDK, env vars, client) │
30
+ ├─────────────────────────────────────────────────────┤
31
+ │ Phase 2: Data Layer │
32
+ │ add-prisma-model (Subscription, WebhookEvent) │
33
+ ├─────────────────────────────────────────────────────┤
34
+ │ Phase 3: Webhook │
35
+ │ add-webhook (Stripe webhook handler) │
36
+ ├─────────────────────────────────────────────────────┤
37
+ │ Phase 4: Feature Module │
38
+ │ add-feature (billing service, checkout, portal) │
39
+ ├─────────────────────────────────────────────────────┤
40
+ │ Phase 5: API Routes │
41
+ │ add-api-route (checkout, portal, subscription) │
42
+ ├─────────────────────────────────────────────────────┤
43
+ │ Phase 6: UI │
44
+ │ add-page (pricing, billing dashboard) │
45
+ │ ↳ If pricing page needed: build-landing-page │
46
+ ├─────────────────────────────────────────────────────┤
47
+ │ Phase 7: Testing & Docs │
48
+ │ test-api-route → update-architecture-docs │
49
+ └─────────────────────────────────────────────────────┘
50
+ ```
51
+
52
+ ## Phase 0: Create Execution Plan
53
+
54
+ **Skill:** `create-execution-plan`
55
+
56
+ Create `docs/exec-plans/active/setup-billing.md` with all tasks listed below.
57
+
58
+ ## Phase 1: Payment Provider Setup
59
+
60
+ **Skill:** `configure-payments`
61
+
62
+ 1. Install Stripe SDK: `yarn add stripe`
63
+ 2. Set environment variables:
64
+ - `STRIPE_SECRET_KEY`
65
+ - `STRIPE_WEBHOOK_SECRET`
66
+ - `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`
67
+ 3. Create the lazy Stripe client at `src/features/billing/server/stripe.ts`
68
+ 4. Enable the billing feature flag:
69
+
70
+ ```typescript
71
+ // src/config/app.config.ts
72
+ features: {
73
+ billing: true,
74
+ }
75
+ ```
76
+
77
+ **Decision: Payment model?**
78
+
79
+ | Model | Use when | Stripe mode |
80
+ |-------|----------|-------------|
81
+ | Subscription | Monthly/annual recurring billing | `mode: 'subscription'` |
82
+ | One-time | Single purchases, credits | `mode: 'payment'` |
83
+ | Usage-based | Pay per API call, storage, etc. | `mode: 'subscription'` + metered billing |
84
+
85
+ ## Phase 2: Data Layer
86
+
87
+ **Skill:** `add-prisma-model`
88
+
89
+ ### Subscription model
90
+
91
+ ```prisma
92
+ // prisma/schema/billing.prisma
93
+ model Subscription {
94
+ id String @id @default(cuid())
95
+ userId String @unique
96
+ stripeCustomerId String @unique
97
+ stripeSubscriptionId String? @unique
98
+ stripePriceId String?
99
+ status String @default("inactive")
100
+ currentPeriodEnd DateTime?
101
+ cancelAtPeriodEnd Boolean @default(false)
102
+ createdAt DateTime @default(now())
103
+ updatedAt DateTime @updatedAt
104
+
105
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
106
+
107
+ @@index([stripeCustomerId])
108
+ @@index([status])
109
+ }
110
+ ```
111
+
112
+ ### Webhook event model (for idempotency)
113
+
114
+ ```prisma
115
+ model WebhookEvent {
116
+ id String @id @default(cuid())
117
+ externalId String @unique
118
+ type String
119
+ processedAt DateTime @default(now())
120
+
121
+ @@index([externalId])
122
+ }
123
+ ```
124
+
125
+ Run `yarn db:push`.
126
+
127
+ ## Phase 3: Webhook Handler
128
+
129
+ **Skill:** `add-webhook`
130
+
131
+ Create `src/app/api/webhooks/stripe/route.ts`:
132
+
133
+ 1. Verify Stripe signature using `stripe.webhooks.constructEvent`
134
+ 2. Check idempotency (has this event been processed?)
135
+ 3. Handle key events:
136
+
137
+ | Event | Handler | Action |
138
+ |-------|---------|--------|
139
+ | `checkout.session.completed` | `handleCheckoutCompleted` | Create/update subscription, set status to active |
140
+ | `customer.subscription.updated` | `handleSubscriptionUpdated` | Sync status, price, period end, cancel flag |
141
+ | `customer.subscription.deleted` | `handleSubscriptionDeleted` | Set status to canceled |
142
+ | `invoice.payment_failed` | `handlePaymentFailed` | Notify user, set status to past_due |
143
+
144
+ 4. Exclude webhook route from CSRF in middleware
145
+ 5. Return 200 quickly — do heavy processing inline (Vercel has 10s limit)
146
+
147
+ **If payment model is one-time instead of subscription:**
148
+
149
+ Handle `checkout.session.completed` to record the purchase. Skip subscription update/delete events.
150
+
151
+ **If payment model is usage-based:**
152
+
153
+ Additionally handle `invoice.created` to add usage line items before the invoice is finalized.
154
+
155
+ ## Phase 4: Feature Module
156
+
157
+ **Skill:** `add-feature`
158
+
159
+ Create `src/features/billing/`:
160
+
161
+ ```
162
+ src/features/billing/
163
+ ├── components/
164
+ │ ├── PricingCard.tsx # Individual plan card
165
+ │ ├── PricingTable.tsx # All plans side by side
166
+ │ ├── SubscriptionStatus.tsx # Current plan display
167
+ │ └── index.ts
168
+ ├── server/
169
+ │ ├── stripe.ts # Lazy Stripe client (from Phase 1)
170
+ │ ├── index.ts # Subscription queries + helpers
171
+ │ └── checkout.ts # Checkout session creation
172
+ ├── validation/
173
+ │ └── schemas.ts # Zod schemas for billing inputs
174
+ └── types.ts # Billing TypeScript types
175
+ ```
176
+
177
+ Key server functions:
178
+
179
+ ```typescript
180
+ // src/features/billing/server/index.ts
181
+ export { getStripe } from './stripe';
182
+
183
+ export async function getUserSubscription(userId: string) { ... }
184
+ export async function isSubscribed(userId: string): Promise<boolean> { ... }
185
+ export async function getSubscriptionStatus(userId: string) { ... }
186
+ ```
187
+
188
+ ## Phase 5: API Routes
189
+
190
+ **Skill:** `add-api-route`
191
+
192
+ | Route | Method | Auth | Purpose |
193
+ |-------|--------|------|---------|
194
+ | `/api/protected/billing/checkout` | POST | `withAuthNoParams` | Create Stripe Checkout session |
195
+ | `/api/protected/billing/portal` | POST | `withAuthNoParams` | Create Stripe Customer Portal session |
196
+ | `/api/protected/billing/subscription` | GET | `withAuthNoParams` | Get current subscription status |
197
+
198
+ ### Checkout route
199
+
200
+ Accepts `{ priceId: string }`. Creates or retrieves Stripe customer, then creates a Checkout Session with success/cancel URLs.
201
+
202
+ ### Portal route
203
+
204
+ Creates a Billing Portal session for subscription management. Requires an existing Stripe customer.
205
+
206
+ ### Subscription route
207
+
208
+ Returns the current subscription status, plan, and renewal date for display in the UI.
209
+
210
+ ## Phase 6: UI
211
+
212
+ **Skill:** `add-page`
213
+
214
+ ### Pricing page (public)
215
+
216
+ Create `src/app/(public)/pricing/page.tsx` or include in the landing page:
217
+
218
+ ```tsx
219
+ // Uses PricingTable component
220
+ // Shows plans with features and CTAs
221
+ // Unauthenticated users → redirect to signup with plan param
222
+ // Authenticated users → redirect to checkout
223
+ ```
224
+
225
+ ### Billing dashboard (protected)
226
+
227
+ Create `src/app/(protected)/billing/page.tsx`:
228
+
229
+ ```tsx
230
+ // Shows current subscription status
231
+ // "Manage Subscription" → opens Stripe Customer Portal
232
+ // "Upgrade" → creates checkout session
233
+ // Shows billing history (if available)
234
+ ```
235
+
236
+ **Conditional: If a pricing page is needed on the landing page:**
237
+
238
+ **Skill:** `build-landing-page` — Include a pricing section with plan comparison.
239
+
240
+ ### Subscription gate component
241
+
242
+ ```tsx
243
+ // src/features/billing/components/SubscriptionGate.tsx
244
+ 'use client';
245
+
246
+ export function SubscriptionGate({
247
+ children,
248
+ fallback,
249
+ }: {
250
+ children: React.ReactNode;
251
+ fallback?: React.ReactNode;
252
+ }) {
253
+ // Check subscription status
254
+ // If subscribed, render children
255
+ // If not, render fallback (upgrade prompt)
256
+ }
257
+ ```
258
+
259
+ ## Phase 7: Testing & Documentation
260
+
261
+ **Skill:** `test-api-route` + `update-architecture-docs`
262
+
263
+ ### Tests
264
+
265
+ 1. Unit test checkout route (mock Stripe, verify session creation)
266
+ 2. Unit test portal route (mock Stripe, verify portal session)
267
+ 3. Unit test webhook handler (mock signature verification, verify DB updates)
268
+ 4. Unit test `isSubscribed` helper
269
+ 5. E2E test: full checkout flow (requires Stripe test mode)
270
+
271
+ ### Webhook local testing
272
+
273
+ ```bash
274
+ stripe listen --forward-to localhost:3000/api/webhooks/stripe
275
+ ```
276
+
277
+ ### Documentation updates
278
+
279
+ 1. Update `docs/QUALITY_SCORE.md`:
280
+
281
+ ```markdown
282
+ | Billing | B | Stripe checkout, portal, webhook, subscription gate |
283
+ ```
284
+
285
+ 2. Update `ARCHITECTURE.md` if this is the first payment integration
286
+ 3. Mark execution plan as complete
287
+
288
+ ## Environment Variables Summary
289
+
290
+ ```bash
291
+ # Required
292
+ STRIPE_SECRET_KEY=sk_test_...
293
+ STRIPE_WEBHOOK_SECRET=whsec_...
294
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
295
+
296
+ # Optional
297
+ STRIPE_PRICE_ID_MONTHLY=price_...
298
+ STRIPE_PRICE_ID_YEARLY=price_...
299
+ ```
300
+
301
+ ## Checklist
302
+
303
+ - [ ] Execution plan created
304
+ - [ ] Stripe SDK installed
305
+ - [ ] Environment variables set
306
+ - [ ] Lazy Stripe client created
307
+ - [ ] Subscription + WebhookEvent Prisma models
308
+ - [ ] Webhook handler with signature verification and idempotency
309
+ - [ ] Webhook excluded from CSRF in middleware
310
+ - [ ] Billing feature module with server/components/validation
311
+ - [ ] Checkout API route
312
+ - [ ] Portal API route
313
+ - [ ] Subscription status API route
314
+ - [ ] Pricing page (public or landing page section)
315
+ - [ ] Billing dashboard page (protected)
316
+ - [ ] SubscriptionGate component for premium features
317
+ - [ ] `isSubscribed` helper for server-side gating
318
+ - [ ] Feature flag `appConfig.features.billing` set to `true`
319
+ - [ ] Unit tests for API routes and webhook
320
+ - [ ] Local webhook testing verified
321
+ - [ ] QUALITY_SCORE.md updated
322
+ - [ ] Execution plan completed
@@ -0,0 +1,104 @@
1
+ # Skill: Set Up a MARS Project
2
+
3
+ Guide for setting up a new MARS project from scratch or getting an existing one running locally.
4
+
5
+ ## When to Use
6
+
7
+ Use this skill when the user asks how to get started, set up their project, run locally, or troubleshoot a fresh install.
8
+
9
+ ## New Project (via CLI)
10
+
11
+ ```bash
12
+ npx @mars-stack/cli create
13
+ ```
14
+
15
+ The CLI will prompt for project name, features, services, and theme, then scaffold a ready-to-run project.
16
+
17
+ ## Existing Project Setup
18
+
19
+ ### 1. Install Dependencies
20
+
21
+ ```bash
22
+ yarn install
23
+ ```
24
+
25
+ ### 2. Environment Variables
26
+
27
+ Copy the example and fill in required values:
28
+
29
+ ```bash
30
+ cp .env.example .env
31
+ ```
32
+
33
+ **Required variables:**
34
+ - `DATABASE_URL` -- Postgres connection string
35
+ - `JWT_SECRET` -- Minimum 32 characters (generate with `openssl rand -hex 32`)
36
+
37
+ **Optional (based on features):**
38
+ - `SENDGRID_API_KEY` / `SENDGRID_FROM_EMAIL` -- if email provider is SendGrid
39
+ - `STRIPE_SECRET_KEY` / `STRIPE_WEBHOOK_SECRET` -- if payments enabled
40
+ - `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` -- for production rate limiting
41
+
42
+ ### 3. Database
43
+
44
+ **Option A: Local Docker (recommended for development)**
45
+
46
+ ```bash
47
+ yarn dev:services # Starts Postgres + Redis via Docker Compose
48
+ yarn db:push # Push schema to database
49
+ ```
50
+
51
+ **Option B: Cloud database (Neon, Supabase, etc.)**
52
+
53
+ Set `DATABASE_URL` in `.env` to your cloud connection string. For Neon:
54
+ ```
55
+ DATABASE_URL="postgresql://user:pass@ep-xxx.region.aws.neon.tech/dbname?sslmode=require"
56
+ ```
57
+
58
+ Then: `yarn db:push`
59
+
60
+ ### 4. Start Development
61
+
62
+ ```bash
63
+ yarn dev # Starts Next.js with Turbopack
64
+ ```
65
+
66
+ The app will be available at `http://localhost:3000`.
67
+
68
+ ## Health Check
69
+
70
+ Run the built-in doctor to verify your setup:
71
+
72
+ ```bash
73
+ npx @mars-stack/cli doctor
74
+ ```
75
+
76
+ This checks: Node.js version, Yarn, Docker, Git, `.env` file, Prisma schema, database connection, and JWT_SECRET.
77
+
78
+ ## Common Issues
79
+
80
+ ### "Cannot connect to the database"
81
+ - Is Docker running? `docker ps`
82
+ - Start services: `yarn dev:services`
83
+ - Check `DATABASE_URL` in `.env`
84
+
85
+ ### "Database tables are missing"
86
+ - Run `yarn db:push` to create tables from the Prisma schema
87
+
88
+ ### "JWT_SECRET is required"
89
+ - Generate one: `openssl rand -hex 32`
90
+ - Add to `.env`: `JWT_SECRET="<generated-value>"`
91
+
92
+ ### Build fails with environment errors
93
+ - The env validation is lazy and skips during build. Ensure `.env` exists for runtime.
94
+ - `APP_URL` is optional (falls back to `localhost:3000` in dev).
95
+
96
+ ## Key Files
97
+
98
+ | File | Purpose |
99
+ |------|---------|
100
+ | `src/config/app.config.ts` | App identity, features, services, theme |
101
+ | `.env` | Environment secrets (never commit) |
102
+ | `docker-compose.yml` | Local Postgres + Redis |
103
+ | `prisma/schema/` | Database models (multi-file) |
104
+ | `src/middleware.ts` | Auth redirects, CSRF, coming-soon mode |