@mars-stack/core 2.0.1 → 3.0.1
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/cursor/rules/mars-data-access.mdc +6 -3
- package/cursor/rules/mars-testing.mdc +2 -2
- package/cursor/rules/mars-ui-conventions.mdc +4 -4
- package/cursor/skills/mars-add-api-route/SKILL.md +3 -1
- package/cursor/skills/mars-add-component/SKILL.md +1 -1
- package/cursor/skills/mars-add-feature/SKILL.md +1 -1
- package/cursor/skills/mars-add-page/SKILL.md +2 -4
- package/cursor/skills/mars-add-server-action/SKILL.md +1 -1
- package/cursor/skills/mars-add-webhook/SKILL.md +1 -1
- package/cursor/skills/mars-build-complete-feature/SKILL.md +3 -4
- package/cursor/skills/mars-build-data-table/SKILL.md +2 -3
- package/cursor/skills/mars-build-form/SKILL.md +3 -1
- package/cursor/skills/mars-configure-email/SKILL.md +3 -3
- package/cursor/skills/mars-configure-oauth/SKILL.md +1 -1
- package/cursor/skills/mars-configure-payments/SKILL.md +2 -2
- package/cursor/skills/mars-configure-storage/SKILL.md +2 -2
- package/cursor/skills/mars-setup-teams/SKILL.md +2 -8
- package/cursor/skills/mars-test-api-route/SKILL.md +1 -1
- package/cursor/skills/mars-update-architecture-docs/SKILL.md +15 -105
- package/package.json +1 -1
- package/scripts/postinstall.mjs +52 -11
|
@@ -4,13 +4,16 @@ globs: **/server/**
|
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## Prisma
|
|
7
|
+
## Prisma (v7)
|
|
8
8
|
|
|
9
|
-
- Import the
|
|
9
|
+
- Import the database client: `import { prisma } from '@/lib/prisma'`.
|
|
10
|
+
- Import Prisma types (models, enums, namespaces): `import type { User } from '@db'` or `import { Prisma } from '@db'`. The `@db` alias maps to `prisma/generated/prisma/client`.
|
|
11
|
+
- The `datasource` URL lives in `prisma.config.ts`, **not** in the schema file.
|
|
12
|
+
- `PrismaClient` requires a driver adapter: `new PrismaClient({ adapter })` with `@prisma/adapter-pg`.
|
|
13
|
+
- After changing the schema: `yarn db:generate` then `yarn db:push` (dev) or `yarn db:migrate` (with migration history). `prisma generate` no longer auto-runs.
|
|
10
14
|
- User-scoped queries MUST include `userId` from the authenticated session in the `where` clause, never from request parameters.
|
|
11
15
|
- Use `$transaction` for multi-step writes that must be atomic.
|
|
12
16
|
- New models go in `prisma/schema/` as separate `.prisma` files (multi-file schema).
|
|
13
|
-
- After changing the schema: `yarn db:push` (dev) or `yarn db:migrate dev` (with migration history).
|
|
14
17
|
|
|
15
18
|
## Error Handling
|
|
16
19
|
|
|
@@ -12,8 +12,8 @@ alwaysApply: false
|
|
|
12
12
|
|
|
13
13
|
## Mocking
|
|
14
14
|
|
|
15
|
-
- Use `@mars-stack/core/test-utils
|
|
16
|
-
- Use `@mars-stack/core/test-utils
|
|
15
|
+
- Use `@mars-stack/core/test-utils` to mock authenticated sessions in API route tests.
|
|
16
|
+
- Use `@mars-stack/core/test-utils` for consistent test data.
|
|
17
17
|
- Mock Prisma with `vi.mock('@/lib/prisma')` and provide typed mocks for each model method.
|
|
18
18
|
|
|
19
19
|
## Patterns
|
|
@@ -17,13 +17,13 @@ MARS uses a three-layer CSS token system. Never use raw Tailwind colour classes
|
|
|
17
17
|
|
|
18
18
|
## Component Architecture
|
|
19
19
|
|
|
20
|
-
- **Primitives** (`@mars-stack/ui`): Single-responsibility, fully styled via tokens, accept `className` for overrides. Examples: `Button`, `Input`, `Badge`, `Avatar`, `Spinner`.
|
|
21
|
-
- **Patterns** (`@mars-stack/ui`): Compose primitives, no business logic. Examples: `Card`, `Modal`, `Toast`, `FormField`, `EmptyState`.
|
|
20
|
+
- **Primitives** (`@mars-stack/ui` (primitives)): Single-responsibility, fully styled via tokens, accept `className` for overrides. Examples: `Button`, `Input`, `Badge`, `Avatar`, `Spinner`.
|
|
21
|
+
- **Patterns** (`@mars-stack/ui` (patterns)): Compose primitives, no business logic. Examples: `Card`, `Modal`, `Toast`, `FormField`, `EmptyState`.
|
|
22
22
|
- **Feature components** (`src/features/<name>/components/`): Contain business logic, compose primitives and patterns.
|
|
23
23
|
|
|
24
24
|
## Rules
|
|
25
25
|
|
|
26
|
-
- Always import components from `@mars-stack/ui`
|
|
26
|
+
- Always import components from barrel exports: `@mars-stack/ui` or `@mars-stack/ui`.
|
|
27
27
|
- Use `clsx` for conditional class composition.
|
|
28
|
-
- New
|
|
28
|
+
- New primitives go in `@mars-stack/ui` (primitives) with a barrel re-export.
|
|
29
29
|
- Components MUST support dark mode via the token system (tokens swap automatically with `.dark` class).
|
|
@@ -44,7 +44,9 @@ export const GET = withAuthNoParams(async (request: AuthenticatedRequest) => {
|
|
|
44
44
|
|
|
45
45
|
```typescript
|
|
46
46
|
import { handleApiError } from '@/lib/mars';
|
|
47
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
checkRateLimit, getClientIP, RATE_LIMITS, rateLimitResponse,
|
|
49
|
+
} from '@mars-stack/core/rate-limit';
|
|
48
50
|
import { NextResponse } from 'next/server';
|
|
49
51
|
|
|
50
52
|
export async function POST(request: Request) {
|
|
@@ -144,7 +144,7 @@ When adding overlay buttons inside an input (e.g. password visibility toggle):
|
|
|
144
144
|
|
|
145
145
|
## After Creating
|
|
146
146
|
|
|
147
|
-
1. Add the export to the
|
|
147
|
+
1. Add the export to the `@mars-stack/ui` package barrel file.
|
|
148
148
|
2. Export both the component and its props type.
|
|
149
149
|
|
|
150
150
|
## Checklist
|
|
@@ -143,9 +143,9 @@ If the feature should be toggleable, add it to `appConfig.features` in `src/conf
|
|
|
143
143
|
Create `route.test.ts` next to each API route. Mock Prisma and auth:
|
|
144
144
|
|
|
145
145
|
```typescript
|
|
146
|
+
import { mockAuth } from '@mars-stack/core/test-utils';
|
|
146
147
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
147
148
|
import { GET, POST } from './route';
|
|
148
|
-
import { mockAuth } from '@mars-stack/core/test-utils';
|
|
149
149
|
|
|
150
150
|
vi.mock('@/lib/prisma', () => ({
|
|
151
151
|
prisma: {
|
|
@@ -19,8 +19,7 @@ The middleware at `src/middleware.ts` handles auth redirects automatically based
|
|
|
19
19
|
## Protected Page Template
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
|
-
import { Card, CardHeader, CardBody } from '@mars-stack/ui';
|
|
23
|
-
import { H1, Paragraph } from '@mars-stack/ui';
|
|
22
|
+
import { Card, CardHeader, CardBody, H1, Paragraph } from '@mars-stack/ui';
|
|
24
23
|
|
|
25
24
|
export default function WidgetsPage() {
|
|
26
25
|
return (
|
|
@@ -69,9 +68,8 @@ For pages that need client-side data, create a client component in the feature m
|
|
|
69
68
|
// src/features/widgets/components/WidgetList.tsx
|
|
70
69
|
'use client';
|
|
71
70
|
|
|
71
|
+
import { Card, CardBody, Spinner, Badge, EmptyState } from '@mars-stack/ui';
|
|
72
72
|
import { useEffect, useState } from 'react';
|
|
73
|
-
import { Card, CardBody, EmptyState } from '@mars-stack/ui';
|
|
74
|
-
import { Spinner, Badge } from '@mars-stack/ui';
|
|
75
73
|
import type { Widget } from '@/features/widgets/types';
|
|
76
74
|
|
|
77
75
|
export function WidgetList() {
|
|
@@ -117,9 +117,9 @@ export async function createItem(formData: FormData) {
|
|
|
117
117
|
```tsx
|
|
118
118
|
'use client';
|
|
119
119
|
|
|
120
|
+
import { Button, Input } from '@mars-stack/ui';
|
|
120
121
|
import { useActionState } from 'react';
|
|
121
122
|
import { updateProfile } from '@/features/settings/server/actions';
|
|
122
|
-
import { Button, Input } from '@mars-stack/ui';
|
|
123
123
|
|
|
124
124
|
export function ProfileForm({ currentName }: { currentName: string }) {
|
|
125
125
|
const [state, formAction, isPending] = useActionState(updateProfile, {
|
|
@@ -172,7 +172,7 @@ async function processEvent(eventId: string, handler: () => Promise<void>) {
|
|
|
172
172
|
|
|
173
173
|
## Environment Variables
|
|
174
174
|
|
|
175
|
-
Add webhook secrets to
|
|
175
|
+
Add webhook secrets to `src/core/env/index.ts` in `buildEnvSchema()`:
|
|
176
176
|
|
|
177
177
|
```typescript
|
|
178
178
|
base.STRIPE_WEBHOOK_SECRET = z.string().min(1).optional();
|
|
@@ -171,10 +171,9 @@ Before writing any code, create an execution plan:
|
|
|
171
171
|
**Skill:** `update-architecture-docs`
|
|
172
172
|
|
|
173
173
|
1. Update `docs/QUALITY_SCORE.md` with the new feature grade
|
|
174
|
-
2. Update `
|
|
175
|
-
3.
|
|
176
|
-
4.
|
|
177
|
-
5. Move the plan from `active/` to `completed/`
|
|
174
|
+
2. Update `AGENTS.md` if the feature introduces new patterns
|
|
175
|
+
3. Mark the execution plan as complete
|
|
176
|
+
4. Move the plan from `active/` to `completed/`
|
|
178
177
|
|
|
179
178
|
## Feature Flag Integration
|
|
180
179
|
|
|
@@ -15,11 +15,10 @@ MARS provides `Table` primitives (`Table`, `TableHead`, `TableBody`, `TableRow`,
|
|
|
15
15
|
```tsx
|
|
16
16
|
'use client';
|
|
17
17
|
|
|
18
|
-
import { useEffect, useState, useCallback } from 'react';
|
|
19
18
|
import {
|
|
20
|
-
Table, TableHead, TableBody, TableRow, TableHeaderCell, TableCell,
|
|
21
|
-
Badge, Button, Spinner, Card, CardHeader, CardBody, EmptyState,
|
|
19
|
+
Table, TableHead, TableBody, TableRow, TableHeaderCell, TableCell, Badge, Button, Spinner, Card, CardHeader, CardBody, EmptyState,
|
|
22
20
|
} from '@mars-stack/ui';
|
|
21
|
+
import { useEffect, useState, useCallback } from 'react';
|
|
23
22
|
|
|
24
23
|
interface Item {
|
|
25
24
|
id: string;
|
|
@@ -19,8 +19,10 @@ MARS forms use:
|
|
|
19
19
|
```tsx
|
|
20
20
|
'use client';
|
|
21
21
|
|
|
22
|
+
import {
|
|
23
|
+
Button, Input, Select, Textarea, Checkbox, Card, CardHeader, CardBody, CardFooter, H2,
|
|
24
|
+
} from '@mars-stack/ui';
|
|
22
25
|
import { useState, type FormEvent } from 'react';
|
|
23
|
-
import { Button, Input, Select, Textarea, Checkbox, Card, CardHeader, CardBody, CardFooter, H2 } from '@mars-stack/ui';
|
|
24
26
|
import { z } from 'zod';
|
|
25
27
|
|
|
26
28
|
const formSchema = z.object({
|
|
@@ -8,7 +8,7 @@ Use this skill when the user asks to configure email, set up SendGrid, add Resen
|
|
|
8
8
|
|
|
9
9
|
## Architecture
|
|
10
10
|
|
|
11
|
-
MARS uses a pluggable email service via `@/lib/mars
|
|
11
|
+
MARS uses a pluggable email service re-exported via `@/lib/mars`. The active provider is set in `appConfig.services.email.provider`.
|
|
12
12
|
|
|
13
13
|
Available providers:
|
|
14
14
|
- `console` -- logs emails to terminal (default, no setup needed)
|
|
@@ -42,7 +42,7 @@ RESEND_FROM_EMAIL="noreply@yourdomain.com"
|
|
|
42
42
|
|
|
43
43
|
### Step 3: Update Env Validation
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
In `src/core/env/index.ts`, the schema already conditionally validates email vars:
|
|
46
46
|
|
|
47
47
|
```typescript
|
|
48
48
|
if (appConfig.services.email.provider !== 'console') {
|
|
@@ -61,7 +61,7 @@ yarn add resend
|
|
|
61
61
|
|
|
62
62
|
### Step 2: Add the Provider
|
|
63
63
|
|
|
64
|
-
In the email service module (
|
|
64
|
+
In the email service module (wired through `@/lib/mars`):
|
|
65
65
|
|
|
66
66
|
```typescript
|
|
67
67
|
async function sendWithResend(params: SendEmailParams): Promise<void> {
|
|
@@ -31,7 +31,7 @@ GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
|
|
|
31
31
|
GOOGLE_CLIENT_SECRET="your-client-secret"
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Update
|
|
34
|
+
Update `src/core/env/index.ts` to validate these:
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
37
|
if (appConfig.features.googleOAuth) {
|
|
@@ -81,8 +81,8 @@ model Subscription {
|
|
|
81
81
|
```typescript
|
|
82
82
|
// src/app/api/protected/billing/checkout/route.ts
|
|
83
83
|
import { handleApiError, withAuthNoParams, type AuthenticatedRequest } from '@/lib/mars';
|
|
84
|
-
import { getStripe } from '@/features/billing/server';
|
|
85
84
|
import { prisma } from '@/lib/prisma';
|
|
85
|
+
import { getStripe } from '@/features/billing/server';
|
|
86
86
|
import { NextResponse } from 'next/server';
|
|
87
87
|
import { z } from 'zod';
|
|
88
88
|
|
|
@@ -133,8 +133,8 @@ export const POST = withAuthNoParams(async (request: AuthenticatedRequest) => {
|
|
|
133
133
|
```typescript
|
|
134
134
|
// src/app/api/protected/billing/portal/route.ts
|
|
135
135
|
import { handleApiError, withAuthNoParams, type AuthenticatedRequest } from '@/lib/mars';
|
|
136
|
-
import { getStripe } from '@/features/billing/server';
|
|
137
136
|
import { prisma } from '@/lib/prisma';
|
|
137
|
+
import { getStripe } from '@/features/billing/server';
|
|
138
138
|
import { NextResponse } from 'next/server';
|
|
139
139
|
|
|
140
140
|
export const POST = withAuthNoParams(async (request: AuthenticatedRequest) => {
|
|
@@ -181,8 +181,8 @@ export const POST = withAuthNoParams(async (request: AuthenticatedRequest) => {
|
|
|
181
181
|
```tsx
|
|
182
182
|
'use client';
|
|
183
183
|
|
|
184
|
-
import { useState, useRef } from 'react';
|
|
185
184
|
import { Button, Spinner } from '@mars-stack/ui';
|
|
185
|
+
import { useState, useRef } from 'react';
|
|
186
186
|
|
|
187
187
|
interface FileUploadProps {
|
|
188
188
|
onUpload: (result: { url: string; pathname: string }) => void;
|
|
@@ -265,7 +265,7 @@ export function FileUpload({ onUpload, accept = 'image/*', maxSize = 10 }: FileU
|
|
|
265
265
|
|
|
266
266
|
- [ ] Storage SDK installed
|
|
267
267
|
- [ ] Environment variables set
|
|
268
|
-
- [ ] Storage service created (
|
|
268
|
+
- [ ] Storage service created (`src/core/storage/index.ts`)
|
|
269
269
|
- [ ] Upload API route with size and type validation
|
|
270
270
|
- [ ] Upload paths scoped by userId
|
|
271
271
|
- [ ] Client upload component
|
|
@@ -627,16 +627,11 @@ If certain routes should only be accessible to team members, add team verificati
|
|
|
627
627
|
| Teams | B | Team CRUD, invitations, role hierarchy, org switching |
|
|
628
628
|
```
|
|
629
629
|
|
|
630
|
-
2. Update `
|
|
631
|
-
- Add teams to the data model section
|
|
632
|
-
- Document the multi-tenancy pattern
|
|
633
|
-
- Add the role hierarchy diagram
|
|
634
|
-
|
|
635
|
-
3. Update `template/AGENTS.md`:
|
|
630
|
+
2. Update `AGENTS.md`:
|
|
636
631
|
- Add teams feature to directory listing
|
|
637
632
|
- Document team-scoped query pattern
|
|
638
633
|
|
|
639
|
-
|
|
634
|
+
3. Mark execution plan as complete
|
|
640
635
|
|
|
641
636
|
## Data Scoping Decision
|
|
642
637
|
|
|
@@ -683,6 +678,5 @@ model Project {
|
|
|
683
678
|
- [ ] Invitation emails (if email configured)
|
|
684
679
|
- [ ] Unit tests for all API routes
|
|
685
680
|
- [ ] E2E test for invitation flow
|
|
686
|
-
- [ ] ARCHITECTURE.md updated with tenancy pattern
|
|
687
681
|
- [ ] QUALITY_SCORE.md updated
|
|
688
682
|
- [ ] Execution plan completed
|
|
@@ -18,9 +18,9 @@ MARS API route tests:
|
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
20
|
// src/app/api/protected/projects/route.test.ts
|
|
21
|
+
import { mockAuth, createTestRequest, createTestRequestWithBody } from '@mars-stack/core/test-utils';
|
|
21
22
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
22
23
|
import { GET, POST } from './route';
|
|
23
|
-
import { mockAuth, createTestRequest, createTestRequestWithBody } from '@mars-stack/core/test-utils';
|
|
24
24
|
|
|
25
25
|
// Mock database
|
|
26
26
|
const mockFindMany = vi.fn();
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
# Skill: Update Architecture Docs
|
|
2
2
|
|
|
3
|
-
Keep the
|
|
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
4
|
|
|
5
5
|
## When to Use
|
|
6
6
|
|
|
7
7
|
Use this skill when:
|
|
8
|
-
- You have just added a new feature, service, or
|
|
8
|
+
- You have just added a new feature, service, or module
|
|
9
9
|
- You have changed the database schema
|
|
10
|
-
- You have added or removed a skill or rule
|
|
11
10
|
- You have completed an execution plan
|
|
12
11
|
- The user asks to update docs, sync docs, or refresh documentation
|
|
13
12
|
- Another skill's checklist includes "update docs"
|
|
14
13
|
|
|
15
14
|
## Prerequisites
|
|
16
15
|
|
|
17
|
-
- Read `ARCHITECTURE.md` to understand the current documented state.
|
|
18
16
|
- Read `AGENTS.md` for the project-level agent guide.
|
|
19
17
|
- Read `docs/QUALITY_SCORE.md` for current quality grades.
|
|
20
18
|
|
|
@@ -23,49 +21,21 @@ Use this skill when:
|
|
|
23
21
|
| Document | Location | Purpose | Update frequency |
|
|
24
22
|
|----------|----------|---------|------------------|
|
|
25
23
|
| `AGENTS.md` | Repo root | First file any agent reads. Overview + pointers | On structure changes |
|
|
26
|
-
| `ARCHITECTURE.md` | Repo root | Detailed technical architecture | On arch changes |
|
|
27
24
|
| `docs/QUALITY_SCORE.md` | Docs | Quality grades by domain | After every improvement |
|
|
28
|
-
| `docs/
|
|
29
|
-
| `docs/generated/skill-inventory.md` | Docs | Auto-generated skill list | Regenerate on skill changes |
|
|
30
|
-
| `template/AGENTS.md` | Template | Consumer-facing project guide | On template structure changes |
|
|
31
|
-
| `packages/core/cursor/manifest.json` | Core | Skill index with triggers | On skill add/remove |
|
|
25
|
+
| `docs/design-docs/` | Docs | Architecture decisions and design documents | On architectural changes |
|
|
32
26
|
|
|
33
27
|
## When to Update Each Document
|
|
34
28
|
|
|
35
|
-
###
|
|
29
|
+
### AGENTS.md
|
|
36
30
|
|
|
37
31
|
Update when:
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- A new export path is added to a package
|
|
41
|
-
- The security model changes (new auth pattern, new middleware)
|
|
42
|
-
- The skill/rule system changes
|
|
43
|
-
|
|
44
|
-
What to update:
|
|
45
|
-
1. Package layering diagram (if dependency graph changed)
|
|
46
|
-
2. Package export table (if new exports added)
|
|
47
|
-
3. Build pipeline section (if build process changed)
|
|
48
|
-
4. Security architecture section (if auth patterns changed)
|
|
49
|
-
5. Skills and rules section (if skill system changed)
|
|
50
|
-
|
|
51
|
-
### AGENTS.md (Root)
|
|
52
|
-
|
|
53
|
-
Update when:
|
|
54
|
-
- A new top-level directory is added
|
|
55
|
-
- A new CLI command is added
|
|
56
|
-
- The "How to Run" commands change
|
|
57
|
-
- A new "How to" section is needed (e.g., "How to Add a Plugin")
|
|
58
|
-
|
|
59
|
-
Keep it concise — AGENTS.md should fit in a single context window.
|
|
60
|
-
|
|
61
|
-
### template/AGENTS.md
|
|
62
|
-
|
|
63
|
-
Update when:
|
|
64
|
-
- The template directory structure changes
|
|
65
|
-
- New route groups are added
|
|
66
|
-
- New feature modules are added to the template
|
|
32
|
+
- The directory structure changes (new route groups, new features)
|
|
33
|
+
- New feature modules are added
|
|
67
34
|
- The config structure changes
|
|
68
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.
|
|
69
39
|
|
|
70
40
|
### docs/QUALITY_SCORE.md
|
|
71
41
|
|
|
@@ -78,31 +48,11 @@ How to update:
|
|
|
78
48
|
1. Find the relevant row in the appropriate table
|
|
79
49
|
2. Update the grade (A/B/C/D/F)
|
|
80
50
|
3. Update the notes to reflect current state
|
|
81
|
-
4. Add date reference if significant change
|
|
82
51
|
|
|
83
52
|
```markdown
|
|
84
53
|
| Dashboard | B | Basic layout with stat cards. Missing: chart widgets, date range filter |
|
|
85
54
|
```
|
|
86
55
|
|
|
87
|
-
### Generated Docs
|
|
88
|
-
|
|
89
|
-
Regenerate `docs/generated/package-map.md` when:
|
|
90
|
-
- A package's exports change
|
|
91
|
-
- A new package is added
|
|
92
|
-
- A package is removed
|
|
93
|
-
|
|
94
|
-
Regenerate `docs/generated/skill-inventory.md` when:
|
|
95
|
-
- A skill is added, removed, or renamed
|
|
96
|
-
- Skill triggers or dependencies change
|
|
97
|
-
|
|
98
|
-
### manifest.json
|
|
99
|
-
|
|
100
|
-
Update when:
|
|
101
|
-
- A new skill is added (add entry with triggers, dependencies, capabilities)
|
|
102
|
-
- A skill is removed (remove entry)
|
|
103
|
-
- Skill triggers change (update triggers array)
|
|
104
|
-
- Skill dependencies change (update dependencies array)
|
|
105
|
-
|
|
106
56
|
## Step-by-Step: After Adding a Feature
|
|
107
57
|
|
|
108
58
|
1. **QUALITY_SCORE.md** — Update the grade for the relevant domain:
|
|
@@ -111,61 +61,24 @@ Update when:
|
|
|
111
61
|
| Billing | B | Stripe checkout, portal, webhook. Missing: usage-based billing |
|
|
112
62
|
```
|
|
113
63
|
|
|
114
|
-
2. **
|
|
115
|
-
|
|
116
|
-
```markdown
|
|
117
|
-
### Billing Architecture
|
|
118
|
-
|
|
119
|
-
The billing system uses Stripe as the payment provider...
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
3. **template/AGENTS.md** — If the feature adds new directories or conventions:
|
|
64
|
+
2. **AGENTS.md** — If the feature adds new directories or conventions:
|
|
123
65
|
|
|
124
66
|
```markdown
|
|
125
67
|
├── features/
|
|
126
68
|
│ └── billing/ # Subscription management
|
|
127
69
|
```
|
|
128
70
|
|
|
129
|
-
|
|
71
|
+
3. **Execution plan** — Mark tasks complete, update verification:
|
|
130
72
|
|
|
131
73
|
```markdown
|
|
132
74
|
- [x] **3.1 Billing page** — Done. `src/app/(protected)/billing/page.tsx`
|
|
133
75
|
```
|
|
134
76
|
|
|
135
|
-
## Step-by-Step: After Adding a Skill
|
|
136
|
-
|
|
137
|
-
1. **manifest.json** — Add the skill entry:
|
|
138
|
-
|
|
139
|
-
```json
|
|
140
|
-
"new-skill-name": {
|
|
141
|
-
"file": "skills/mars-new-skill-name/SKILL.md",
|
|
142
|
-
"triggers": ["trigger phrase 1", "trigger phrase 2"],
|
|
143
|
-
"dependencies": ["dependency-skill"],
|
|
144
|
-
"capabilities": ["file-edit", "terminal"]
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
2. **skill-inventory.md** — Regenerate or manually add:
|
|
149
|
-
|
|
150
|
-
```markdown
|
|
151
|
-
| new-skill-name | Add a new thing | file-edit, terminal |
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
3. **AGENTS.md** — If the skill represents a new "How to" workflow:
|
|
155
|
-
|
|
156
|
-
```markdown
|
|
157
|
-
## How to Add a New Thing
|
|
158
|
-
|
|
159
|
-
1. Create the skill file
|
|
160
|
-
2. Add to manifest
|
|
161
|
-
3. ...
|
|
162
|
-
```
|
|
163
|
-
|
|
164
77
|
## Step-by-Step: After a Schema Change
|
|
165
78
|
|
|
166
|
-
1. **
|
|
167
|
-
2. **
|
|
168
|
-
3. **
|
|
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
|
|
169
82
|
|
|
170
83
|
## Validation
|
|
171
84
|
|
|
@@ -179,11 +92,8 @@ After updating docs, verify:
|
|
|
179
92
|
## Checklist
|
|
180
93
|
|
|
181
94
|
- [ ] Identified which documents need updating
|
|
182
|
-
- [ ] ARCHITECTURE.md updated (if architectural change)
|
|
183
95
|
- [ ] AGENTS.md updated (if structure or workflow change)
|
|
184
|
-
- [ ] template/AGENTS.md updated (if template structure change)
|
|
185
96
|
- [ ] QUALITY_SCORE.md grades updated
|
|
186
|
-
- [ ]
|
|
187
|
-
- [ ] Generated docs regenerated (if package or skill change)
|
|
97
|
+
- [ ] Design docs updated (if architectural decision)
|
|
188
98
|
- [ ] No broken internal links
|
|
189
99
|
- [ ] Execution plan marked as complete (if applicable)
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -45,6 +45,20 @@ function detectIdeEnvironment() {
|
|
|
45
45
|
const ide = detectIdeEnvironment();
|
|
46
46
|
|
|
47
47
|
// Cursor: copy rules + skills into .cursor/ (primary target)
|
|
48
|
+
const cursorDir = path.join(projectRoot, '.cursor');
|
|
49
|
+
const managedListPath = path.join(cursorDir, '.mars-managed');
|
|
50
|
+
|
|
51
|
+
let previouslyManaged = [];
|
|
52
|
+
if (fs.existsSync(managedListPath)) {
|
|
53
|
+
try {
|
|
54
|
+
previouslyManaged = JSON.parse(fs.readFileSync(managedListPath, 'utf-8'));
|
|
55
|
+
} catch {
|
|
56
|
+
previouslyManaged = [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const currentlyManaged = [];
|
|
61
|
+
|
|
48
62
|
const copies = [
|
|
49
63
|
{ src: 'rules', dest: '.cursor/rules' },
|
|
50
64
|
{ src: 'skills', dest: '.cursor/skills' },
|
|
@@ -59,21 +73,48 @@ for (const { src, dest } of copies) {
|
|
|
59
73
|
|
|
60
74
|
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
61
75
|
for (const entry of entries) {
|
|
62
|
-
if (entry.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
if (!entry.name.startsWith('mars-')) continue;
|
|
77
|
+
|
|
78
|
+
const destPath = path.join(destDir, entry.name);
|
|
79
|
+
const managedKey = `${dest}/${entry.name}`;
|
|
80
|
+
currentlyManaged.push(managedKey);
|
|
81
|
+
|
|
82
|
+
if (entry.isFile()) {
|
|
83
|
+
fs.copyFileSync(path.join(srcDir, entry.name), destPath);
|
|
84
|
+
} else if (entry.isDirectory()) {
|
|
85
|
+
fs.cpSync(path.join(srcDir, entry.name), destPath, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Remove unprefixed duplicate from old naming scheme (e.g. security.mdc → removed)
|
|
89
|
+
const unprefixedName = entry.name.replace(/^mars-/, '');
|
|
90
|
+
const unprefixedPath = path.join(destDir, unprefixedName);
|
|
91
|
+
if (fs.existsSync(unprefixedPath)) {
|
|
92
|
+
fs.rmSync(unprefixedPath, { recursive: true, force: true });
|
|
73
93
|
}
|
|
74
94
|
}
|
|
75
95
|
}
|
|
76
96
|
|
|
97
|
+
// Remove stale mars-* entries from previous versions that are no longer shipped
|
|
98
|
+
const currentSet = new Set(currentlyManaged);
|
|
99
|
+
for (const oldEntry of previouslyManaged) {
|
|
100
|
+
if (!currentSet.has(oldEntry)) {
|
|
101
|
+
const stalePath = path.join(projectRoot, oldEntry);
|
|
102
|
+
if (fs.existsSync(stalePath)) {
|
|
103
|
+
fs.rmSync(stalePath, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Write the managed list for future cleanup
|
|
109
|
+
fs.mkdirSync(cursorDir, { recursive: true });
|
|
110
|
+
fs.writeFileSync(managedListPath, JSON.stringify(currentlyManaged, null, 2));
|
|
111
|
+
|
|
112
|
+
// Copy manifest.json so it's available in scaffolded projects
|
|
113
|
+
const manifestSrc = path.join(packageDir, '..', 'cursor', 'manifest.json');
|
|
114
|
+
if (fs.existsSync(manifestSrc)) {
|
|
115
|
+
fs.copyFileSync(manifestSrc, path.join(cursorDir, 'manifest.json'));
|
|
116
|
+
}
|
|
117
|
+
|
|
77
118
|
// Generate IDE-specific adapter files for non-Cursor environments
|
|
78
119
|
if (ide !== 'cursor') {
|
|
79
120
|
const manifestPath = path.join(packageDir, '..', 'cursor', 'manifest.json');
|