@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.
- package/package.json +2 -2
- package/template/.cursor/rules/composition-patterns.mdc +186 -0
- package/template/.cursor/rules/data-access.mdc +29 -0
- package/template/.cursor/rules/project-structure.mdc +34 -0
- package/template/.cursor/rules/security.mdc +25 -0
- package/template/.cursor/rules/testing.mdc +24 -0
- package/template/.cursor/rules/ui-conventions.mdc +29 -0
- package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
- package/template/.cursor/skills/add-audit-log/SKILL.md +373 -0
- package/template/.cursor/skills/add-blog/SKILL.md +447 -0
- package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
- package/template/.cursor/skills/add-component/SKILL.md +158 -0
- package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
- package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
- package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
- package/template/.cursor/skills/add-feature/SKILL.md +174 -0
- package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
- package/template/.cursor/skills/add-page/SKILL.md +151 -0
- package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
- package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
- package/template/.cursor/skills/add-role/SKILL.md +156 -0
- package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
- package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
- package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
- package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
- package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
- package/template/.cursor/skills/build-form/SKILL.md +231 -0
- package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
- package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
- package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
- package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
- package/template/.cursor/skills/configure-email/SKILL.md +170 -0
- package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
- package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
- package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
- package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
- package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
- package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
- package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
- package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
- package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
- package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
- package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
- package/template/.cursor/skills/configure-search/SKILL.md +581 -0
- package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
- package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
- package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
- package/template/.cursor/skills/create-seed/SKILL.md +191 -0
- package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
- package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
- package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
- package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
- package/template/.cursor/skills/setup-project/SKILL.md +104 -0
- package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
- package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
- package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
- package/template/AGENTS.md +104 -0
- package/template/ARCHITECTURE.md +102 -0
- package/template/docs/QUALITY_SCORE.md +20 -0
- package/template/docs/design-docs/conversation-as-system-record.md +70 -0
- package/template/docs/design-docs/core-beliefs.md +43 -0
- package/template/docs/design-docs/index.md +8 -0
- package/template/docs/exec-plans/active/.gitkeep +0 -0
- package/template/docs/exec-plans/completed/.gitkeep +0 -0
- package/template/docs/exec-plans/tech-debt.md +7 -0
- package/template/docs/generated/.gitkeep +0 -0
- package/template/docs/product-specs/index.md +7 -0
- package/template/docs/references/index.md +18 -0
- package/template/e2e/api.spec.ts +20 -0
- package/template/e2e/auth.spec.ts +24 -0
- package/template/e2e/public.spec.ts +25 -0
- package/template/eslint.config.mjs +24 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.ts +45 -0
- package/template/package.json +80 -0
- package/template/playwright.config.ts +31 -0
- package/template/postcss.config.mjs +8 -0
- package/template/prisma/generated/prisma/browser.ts +49 -0
- package/template/prisma/generated/prisma/client.ts +73 -0
- package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
- package/template/prisma/generated/prisma/enums.ts +15 -0
- package/template/prisma/generated/prisma/internal/class.ts +254 -0
- package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
- package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
- package/template/prisma/generated/prisma/models/Account.ts +1543 -0
- package/template/prisma/generated/prisma/models/File.ts +1529 -0
- package/template/prisma/generated/prisma/models/Session.ts +1415 -0
- package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
- package/template/prisma/generated/prisma/models/User.ts +2235 -0
- package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
- package/template/prisma/generated/prisma/models.ts +17 -0
- package/template/prisma/schema/auth.prisma +69 -0
- package/template/prisma/schema/base.prisma +8 -0
- package/template/prisma/schema/file.prisma +15 -0
- package/template/prisma/schema/subscription.prisma +17 -0
- package/template/prisma.config.ts +13 -0
- package/template/scripts/check-architecture.ts +221 -0
- package/template/scripts/check-doc-freshness.ts +242 -0
- package/template/scripts/ensure-db.mjs +291 -0
- package/template/scripts/generate-docs.ts +143 -0
- package/template/scripts/generate-env-example.ts +89 -0
- package/template/scripts/seed.ts +56 -0
- package/template/scripts/update-quality-score.ts +263 -0
- package/template/src/__tests__/architecture.test.ts +114 -0
- package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
- package/template/src/app/(auth)/layout.tsx +11 -0
- package/template/src/app/(auth)/register/page.tsx +162 -0
- package/template/src/app/(auth)/reset-password/page.tsx +109 -0
- package/template/src/app/(auth)/sign-in/page.tsx +122 -0
- package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
- package/template/src/app/(auth)/verify/page.tsx +56 -0
- package/template/src/app/(protected)/admin/page.tsx +108 -0
- package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
- package/template/src/app/(protected)/dashboard/page.tsx +22 -0
- package/template/src/app/(protected)/layout.tsx +262 -0
- package/template/src/app/(protected)/settings/page.tsx +370 -0
- package/template/src/app/api/auth/forgot/route.ts +63 -0
- package/template/src/app/api/auth/login/route.ts +121 -0
- package/template/src/app/api/auth/logout/route.ts +19 -0
- package/template/src/app/api/auth/me/route.ts +30 -0
- package/template/src/app/api/auth/reset/route.ts +45 -0
- package/template/src/app/api/auth/signup/route.ts +85 -0
- package/template/src/app/api/auth/verify/route.ts +46 -0
- package/template/src/app/api/csrf/route.ts +12 -0
- package/template/src/app/api/health/route.ts +10 -0
- package/template/src/app/api/protected/admin/users/route.ts +24 -0
- package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
- package/template/src/app/api/protected/billing/portal/route.ts +39 -0
- package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
- package/template/src/app/api/protected/files/upload/route.ts +64 -0
- package/template/src/app/api/protected/user/password/route.ts +63 -0
- package/template/src/app/api/protected/user/profile/route.ts +35 -0
- package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
- package/template/src/app/api/protected/user/sessions/route.ts +22 -0
- package/template/src/app/api/readiness/route.ts +15 -0
- package/template/src/app/api/webhooks/stripe/route.ts +166 -0
- package/template/src/app/error.tsx +33 -0
- package/template/src/app/layout.tsx +29 -0
- package/template/src/app/not-found.tsx +20 -0
- package/template/src/app/page.tsx +136 -0
- package/template/src/app/privacy/page.tsx +178 -0
- package/template/src/app/providers.tsx +8 -0
- package/template/src/app/terms/page.tsx +139 -0
- package/template/src/config/app.config.ts +70 -0
- package/template/src/config/routes.ts +17 -0
- package/template/src/features/admin/index.ts +11 -0
- package/template/src/features/admin/permissions.ts +64 -0
- package/template/src/features/auth/context/AuthContext.tsx +96 -0
- package/template/src/features/auth/context/index.ts +2 -0
- package/template/src/features/auth/index.ts +3 -0
- package/template/src/features/auth/server/consent.ts +66 -0
- package/template/src/features/auth/server/session-revocation.ts +20 -0
- package/template/src/features/auth/server/sessions.ts +66 -0
- package/template/src/features/auth/server/user.ts +166 -0
- package/template/src/features/auth/types.ts +19 -0
- package/template/src/features/auth/validators.ts +29 -0
- package/template/src/features/billing/server/index.ts +66 -0
- package/template/src/features/billing/types.ts +43 -0
- package/template/src/features/uploads/server/index.ts +49 -0
- package/template/src/features/uploads/types.ts +26 -0
- package/template/src/lib/core/email/templates/base-layout.ts +122 -0
- package/template/src/lib/core/email/templates/index.ts +4 -0
- package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
- package/template/src/lib/core/email/templates/verification-email.ts +41 -0
- package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
- package/template/src/lib/mars.ts +56 -0
- package/template/src/lib/prisma.ts +19 -0
- package/template/src/proxy.ts +92 -0
- package/template/src/styles/brand.css +17 -0
- package/template/src/styles/globals.css +6 -0
- package/template/tsconfig.json +59 -0
- package/template/vitest.config.ts +41 -0
- 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 |
|