@folpe/loom 0.2.0 → 0.3.0

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 (45) hide show
  1. package/README.md +82 -16
  2. package/data/agents/backend/AGENT.md +12 -0
  3. package/data/agents/database/AGENT.md +3 -0
  4. package/data/agents/frontend/AGENT.md +10 -0
  5. package/data/agents/marketing/AGENT.md +3 -0
  6. package/data/agents/orchestrator/AGENT.md +1 -19
  7. package/data/agents/security/AGENT.md +3 -0
  8. package/data/agents/tests/AGENT.md +2 -0
  9. package/data/agents/ux-ui/AGENT.md +5 -0
  10. package/data/presets/api-backend.yaml +0 -3
  11. package/data/presets/chrome-extension.yaml +0 -3
  12. package/data/presets/cli-tool.yaml +0 -3
  13. package/data/presets/e-commerce.yaml +0 -3
  14. package/data/presets/expo-mobile.yaml +0 -3
  15. package/data/presets/fullstack-auth.yaml +0 -3
  16. package/data/presets/landing-page.yaml +0 -3
  17. package/data/presets/mvp-lean.yaml +0 -3
  18. package/data/presets/saas-default.yaml +3 -4
  19. package/data/presets/saas-full.yaml +71 -0
  20. package/data/skills/api-design/SKILL.md +43 -2
  21. package/data/skills/auth-rbac/SKILL.md +179 -0
  22. package/data/skills/better-auth-patterns/SKILL.md +212 -0
  23. package/data/skills/chrome-extension-patterns/SKILL.md +13 -6
  24. package/data/skills/cli-development/SKILL.md +11 -3
  25. package/data/skills/drizzle-patterns/SKILL.md +166 -0
  26. package/data/skills/env-validation/SKILL.md +142 -0
  27. package/data/skills/form-validation/SKILL.md +169 -0
  28. package/data/skills/hero-copywriting/SKILL.md +12 -4
  29. package/data/skills/i18n-patterns/SKILL.md +176 -0
  30. package/data/skills/layered-architecture/SKILL.md +131 -0
  31. package/data/skills/nextjs-conventions/SKILL.md +46 -7
  32. package/data/skills/react-native-patterns/SKILL.md +10 -8
  33. package/data/skills/react-query-patterns/SKILL.md +193 -0
  34. package/data/skills/resend-email/SKILL.md +181 -0
  35. package/data/skills/seo-optimization/SKILL.md +10 -2
  36. package/data/skills/server-actions-patterns/SKILL.md +156 -0
  37. package/data/skills/shadcn-ui/SKILL.md +46 -12
  38. package/data/skills/stripe-integration/SKILL.md +11 -3
  39. package/data/skills/supabase-patterns/SKILL.md +23 -6
  40. package/data/skills/table-pagination/SKILL.md +224 -0
  41. package/data/skills/tailwind-patterns/SKILL.md +12 -2
  42. package/data/skills/testing-patterns/SKILL.md +203 -0
  43. package/data/skills/ui-ux-guidelines/SKILL.md +10 -5
  44. package/dist/index.js +254 -100
  45. package/package.json +2 -1
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: form-validation
3
+ description: "Zod dual validation patterns for client and server with react-hook-form and ShadCN Form. Use when building forms, implementing validation, or creating input schemas with i18n error messages."
4
+ ---
5
+
6
+ # Form Validation Patterns
7
+
8
+ ## Critical Rules
9
+
10
+ - **One Zod schema, two validations** — share between client and server.
11
+ - **Schema files in `src/schemas/`** — never inline schemas in components.
12
+ - **Use ShadCN Form components** — `FormField`, `FormMessage` for consistent UX.
13
+ - **Always show inline errors** — next to the field, not in toasts.
14
+ - **Disable submit while pending** — prevent double submissions.
15
+ - **Default values required** — every field needs a `defaultValues` entry in useForm.
16
+
17
+ ## Dual Validation Strategy
18
+
19
+ Every form must validate on **both client and server**:
20
+ - **Client**: instant feedback, UX quality.
21
+ - **Server**: security, data integrity — never trust client validation alone.
22
+
23
+ Use a single Zod schema shared between client and server.
24
+
25
+ ## Shared Schema Definition
26
+
27
+ ```ts
28
+ // src/schemas/user.schema.ts
29
+ import { z } from "zod";
30
+
31
+ export const createUserSchema = z.object({
32
+ name: z.string().min(2, "Name must be at least 2 characters").max(100),
33
+ email: z.string().email("Invalid email address"),
34
+ role: z.enum(["USER", "ADMIN"]).default("USER"),
35
+ bio: z.string().max(500).optional(),
36
+ });
37
+
38
+ export type CreateUserInput = z.infer<typeof createUserSchema>;
39
+ ```
40
+
41
+ ## Client-Side Validation with react-hook-form
42
+
43
+ ```tsx
44
+ "use client";
45
+ import { useForm } from "react-hook-form";
46
+ import { zodResolver } from "@hookform/resolvers/zod";
47
+ import { createUserSchema, type CreateUserInput } from "@/schemas/user.schema";
48
+ import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
49
+ import { Input } from "@/components/ui/input";
50
+ import { Button } from "@/components/ui/button";
51
+
52
+ export function CreateUserForm() {
53
+ const form = useForm<CreateUserInput>({
54
+ resolver: zodResolver(createUserSchema),
55
+ defaultValues: { name: "", email: "", role: "USER" },
56
+ });
57
+
58
+ return (
59
+ <Form {...form}>
60
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
61
+ <FormField
62
+ control={form.control}
63
+ name="name"
64
+ render={({ field }) => (
65
+ <FormItem>
66
+ <FormLabel>Name</FormLabel>
67
+ <FormControl><Input {...field} /></FormControl>
68
+ <FormMessage />
69
+ </FormItem>
70
+ )}
71
+ />
72
+ {/* More fields... */}
73
+ <Button type="submit" disabled={form.formState.isSubmitting}>
74
+ Create
75
+ </Button>
76
+ </form>
77
+ </Form>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ## Server-Side Validation
83
+
84
+ ```ts
85
+ // src/actions/user.actions.ts
86
+ "use server";
87
+ import { createUserSchema } from "@/schemas/user.schema";
88
+
89
+ export async function createUser(input: unknown) {
90
+ const parsed = createUserSchema.safeParse(input);
91
+ if (!parsed.success) {
92
+ return { success: false, error: parsed.error.errors[0].message };
93
+ }
94
+ // proceed with validated data...
95
+ }
96
+ ```
97
+
98
+ ## I18n Error Messages with Zod
99
+
100
+ For internationalized error messages, use a Zod error map:
101
+
102
+ ```ts
103
+ // src/lib/zod-i18n.ts
104
+ import { z } from "zod";
105
+ import { getTranslations } from "next-intl/server";
106
+
107
+ export async function createI18nSchema() {
108
+ const t = await getTranslations("validation");
109
+
110
+ return {
111
+ createUser: z.object({
112
+ name: z.string().min(2, t("name.min", { min: 2 })),
113
+ email: z.string().email(t("email.invalid")),
114
+ }),
115
+ };
116
+ }
117
+ ```
118
+
119
+ Or use a custom error map:
120
+
121
+ ```ts
122
+ // src/lib/zod-error-map.ts
123
+ import { type ZodErrorMap, ZodIssueCode } from "zod";
124
+
125
+ export function createZodErrorMap(t: (key: string, params?: Record<string, unknown>) => string): ZodErrorMap {
126
+ return (issue, ctx) => {
127
+ switch (issue.code) {
128
+ case ZodIssueCode.too_small:
129
+ return { message: t("too_small", { minimum: issue.minimum }) };
130
+ case ZodIssueCode.too_big:
131
+ return { message: t("too_big", { maximum: issue.maximum }) };
132
+ case ZodIssueCode.invalid_string:
133
+ if (issue.validation === "email") return { message: t("invalid_email") };
134
+ break;
135
+ }
136
+ return { message: ctx.defaultError };
137
+ };
138
+ }
139
+ ```
140
+
141
+ ## Multi-Card Form Layout
142
+
143
+ For complex forms, split into logical sections using Cards:
144
+
145
+ ```tsx
146
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
147
+ <Card>
148
+ <CardHeader>
149
+ <CardTitle>Personal Information</CardTitle>
150
+ </CardHeader>
151
+ <CardContent className="grid gap-4 md:grid-cols-2">
152
+ {/* Name, Email fields */}
153
+ </CardContent>
154
+ </Card>
155
+
156
+ <Card>
157
+ <CardHeader>
158
+ <CardTitle>Preferences</CardTitle>
159
+ </CardHeader>
160
+ <CardContent className="space-y-4">
161
+ {/* Role, Bio fields */}
162
+ </CardContent>
163
+ </Card>
164
+
165
+ <div className="flex justify-end">
166
+ <Button type="submit">Create User</Button>
167
+ </div>
168
+ </form>
169
+ ```
@@ -1,14 +1,22 @@
1
1
  ---
2
2
  name: hero-copywriting
3
- description: "Guidelines for writing high-converting hero sections. Use when creating landing pages, marketing pages, or any page with a hero section."
4
- allowed-tools: "Read, Write, Edit"
3
+ description: "Guidelines for writing high-converting hero sections with headlines, CTAs, and social proof. Use when creating landing pages, writing marketing copy, designing hero sections, or building any page that needs a compelling above-the-fold area."
5
4
  ---
6
5
 
7
6
  # Hero Section Copywriting
8
7
 
8
+ ## Critical Rules
9
+
10
+ - **Lead with the outcome, not the feature** — what does the user GET?
11
+ - **Headlines must be 8-12 words maximum** — short, punchy, outcome-focused.
12
+ - **CTA must use action verbs** — "Start", "Get", "Try", "Launch", "Build".
13
+ - **Always include social proof** — logos, metrics, ratings, or testimonials.
14
+ - **Be specific over generic** — "Save 5 hours a week" beats "Save time".
15
+ - **Speak to the reader directly** — use "you" and "your".
16
+
9
17
  ## Headline Rules
10
18
 
11
- - **Lead with the outcome**, not the feature. What does the user GET?
19
+ - Lead with the outcome, not the feature. What does the user GET?
12
20
  - Bad: "AI-powered project management"
13
21
  - Good: "Ship projects 3x faster with your AI co-pilot"
14
22
  - Keep headlines to **8-12 words maximum**.
@@ -41,7 +49,7 @@ allowed-tools: "Read, Write, Edit"
41
49
 
42
50
  ## Visual Guidelines
43
51
 
44
- - Hero should occupy the full viewport height on desktop (`min-h-screen` or `min-h-[80vh]`).
52
+ - Hero should occupy the full viewport height on desktop (`min-h-dvh` or `min-h-[80vh]`).
45
53
  - Text should be left-aligned or center-aligned. Never justify.
46
54
  - Maximum content width: `max-w-2xl` for centered, `max-w-xl` for left-aligned.
47
55
  - Generous whitespace. Don't crowd the hero.
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: i18n-patterns
3
+ description: "Internationalization with next-intl for RSC, Server Actions, and Zod messages. Use when adding multi-language support, translating user-facing strings, or implementing locale-aware validation."
4
+ ---
5
+
6
+ # I18n Patterns
7
+
8
+ ## Critical Rules
9
+
10
+ - **All user-facing strings must be translated** — no hardcoded text in components.
11
+ - **Use namespaces** — group translations by feature/page.
12
+ - **Server-side `getTranslations`** — use in RSC and Server Actions.
13
+ - **Client-side `useTranslations`** — use in Client Components only.
14
+ - **Validation messages are translated** — use i18n error map with Zod.
15
+ - **ICU message syntax** for plurals and interpolation: `"{count, plural, one {# item} other {# items}}"`.
16
+
17
+ ## Setup with next-intl
18
+
19
+ ### Configuration
20
+
21
+ ```ts
22
+ // src/i18n/config.ts
23
+ export const locales = ["en", "fr", "de"] as const;
24
+ export type Locale = (typeof locales)[number];
25
+ export const defaultLocale: Locale = "en";
26
+ ```
27
+
28
+ ### Message Files
29
+
30
+ ```
31
+ messages/
32
+ en.json
33
+ fr.json
34
+ de.json
35
+ ```
36
+
37
+ Structure messages by namespace:
38
+
39
+ ```json
40
+ {
41
+ "common": {
42
+ "save": "Save",
43
+ "cancel": "Cancel",
44
+ "delete": "Delete",
45
+ "loading": "Loading..."
46
+ },
47
+ "auth": {
48
+ "login": "Sign in",
49
+ "logout": "Sign out",
50
+ "register": "Create account"
51
+ },
52
+ "users": {
53
+ "title": "Users",
54
+ "create": "Create user",
55
+ "name": "Name",
56
+ "email": "Email"
57
+ },
58
+ "validation": {
59
+ "required": "This field is required",
60
+ "email.invalid": "Please enter a valid email",
61
+ "too_small": "Must be at least {minimum} characters",
62
+ "too_big": "Must be at most {maximum} characters"
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Server Components (RSC)
68
+
69
+ ```tsx
70
+ import { getTranslations } from "next-intl/server";
71
+
72
+ export default async function UsersPage() {
73
+ const t = await getTranslations("users");
74
+
75
+ return (
76
+ <div>
77
+ <h1>{t("title")}</h1>
78
+ <Button>{t("create")}</Button>
79
+ </div>
80
+ );
81
+ }
82
+ ```
83
+
84
+ ## Client Components
85
+
86
+ ```tsx
87
+ "use client";
88
+ import { useTranslations } from "next-intl";
89
+
90
+ export function UserForm() {
91
+ const t = useTranslations("users");
92
+
93
+ return <label>{t("name")}</label>;
94
+ }
95
+ ```
96
+
97
+ ## Server Actions with I18n
98
+
99
+ Pass locale context to server actions for localized error messages:
100
+
101
+ ```ts
102
+ // src/actions/user.actions.ts
103
+ "use server";
104
+ import { getLocale, getTranslations } from "next-intl/server";
105
+
106
+ export async function createUser(input: unknown) {
107
+ const t = await getTranslations("validation");
108
+ const locale = await getLocale();
109
+
110
+ const schema = z.object({
111
+ name: z.string().min(2, t("too_small", { minimum: 2 })),
112
+ email: z.string().email(t("email.invalid")),
113
+ });
114
+
115
+ const parsed = schema.safeParse(input);
116
+ if (!parsed.success) {
117
+ return { success: false, error: parsed.error.errors[0].message };
118
+ }
119
+ // ...
120
+ }
121
+ ```
122
+
123
+ ## Zod Validation with I18n
124
+
125
+ ### Custom Error Map
126
+
127
+ ```ts
128
+ // src/lib/zod-i18n.ts
129
+ import { z } from "zod";
130
+
131
+ export function createI18nErrorMap(
132
+ t: (key: string, params?: Record<string, unknown>) => string
133
+ ): z.ZodErrorMap {
134
+ return (issue, ctx) => {
135
+ switch (issue.code) {
136
+ case z.ZodIssueCode.too_small:
137
+ if (issue.type === "string") {
138
+ return { message: t("too_small", { minimum: issue.minimum }) };
139
+ }
140
+ break;
141
+ case z.ZodIssueCode.too_big:
142
+ if (issue.type === "string") {
143
+ return { message: t("too_big", { maximum: issue.maximum }) };
144
+ }
145
+ break;
146
+ case z.ZodIssueCode.invalid_string:
147
+ if (issue.validation === "email") {
148
+ return { message: t("email.invalid") };
149
+ }
150
+ break;
151
+ case z.ZodIssueCode.invalid_type:
152
+ if (issue.received === "undefined") {
153
+ return { message: t("required") };
154
+ }
155
+ break;
156
+ }
157
+ return { message: ctx.defaultError };
158
+ };
159
+ }
160
+ ```
161
+
162
+ ## Date and Number Formatting
163
+
164
+ ```tsx
165
+ import { useFormatter } from "next-intl";
166
+
167
+ function PriceDisplay({ amount }: { amount: number }) {
168
+ const format = useFormatter();
169
+ return <span>{format.number(amount, { style: "currency", currency: "EUR" })}</span>;
170
+ }
171
+
172
+ function DateDisplay({ date }: { date: Date }) {
173
+ const format = useFormatter();
174
+ return <span>{format.dateTime(date, { dateStyle: "medium" })}</span>;
175
+ }
176
+ ```
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: layered-architecture
3
+ description: "Enforces strict layered architecture: Presentation → Facade → Service → DAL → Persistence. Use when structuring a Next.js application, creating new features with separation of concerns, or refactoring code into layers."
4
+ ---
5
+
6
+ # Layered Architecture
7
+
8
+ Strict separation of concerns across five layers. Each layer only calls the layer directly below it.
9
+
10
+ ## Critical Rules
11
+
12
+ - **Never skip layers** — presentation must not call services directly.
13
+ - **No business logic in facades** — they coordinate, not decide.
14
+ - **No auth checks in DAL** — DAL is purely data access.
15
+ - **No database calls in services** — services call DAL.
16
+ - **Functional style** — pure functions, no classes, prefer composition over inheritance.
17
+ - **Top-down design** — keep functions short, extract when >100 lines.
18
+
19
+ ## Layer Overview
20
+
21
+ ```
22
+ Presentation (RSC / Client Components)
23
+
24
+ Facade (entry point for business logic)
25
+
26
+ Service (business rules, authorization)
27
+
28
+ DAL — Data Access Layer (queries, data shaping)
29
+
30
+ Persistence (Drizzle ORM / database)
31
+ ```
32
+
33
+ ## Presentation Layer
34
+
35
+ - React Server Components and Client Components live here.
36
+ - Components call **facades only** — never services or DAL directly.
37
+ - RSC by default. Only add `"use client"` when strictly needed (hooks, events, browser APIs).
38
+ - Top-down design: if a component exceeds ~100 lines, extract sub-functions/sub-components.
39
+ - Functional over OOP — pure functions, composition, immutability.
40
+
41
+ ```tsx
42
+ // app/(app)/users/page.tsx — Presentation
43
+ import { getUserList } from "@/facades/user.facade";
44
+
45
+ export default async function UsersPage() {
46
+ const users = await getUserList();
47
+ return <UserTable users={users} />;
48
+ }
49
+ ```
50
+
51
+ ## Facade Layer
52
+
53
+ - Entry points for business logic. One facade per domain entity.
54
+ - Located in `src/facades/` — files named `{entity}.facade.ts`.
55
+ - Facades orchestrate service calls and transform data for the presentation.
56
+ - Facades handle auth context extraction and pass it down.
57
+ - Never contain business logic — only coordination.
58
+
59
+ ```ts
60
+ // src/facades/user.facade.ts
61
+ import { getCurrentUser } from "@/lib/auth";
62
+ import { listUsers, createUser } from "@/services/user.service";
63
+
64
+ export async function getUserList() {
65
+ const currentUser = await getCurrentUser();
66
+ return listUsers(currentUser);
67
+ }
68
+ ```
69
+
70
+ ## Service Layer
71
+
72
+ - Contains all business rules and authorization checks.
73
+ - Located in `src/services/` — files named `{entity}.service.ts`.
74
+ - Services receive the auth context as parameter — never fetch it themselves.
75
+ - CASL authorization checks happen here.
76
+ - Services call DAL functions for data access.
77
+
78
+ ```ts
79
+ // src/services/user.service.ts
80
+ import { ForbiddenError } from "@casl/ability";
81
+ import { defineAbilityFor } from "@/lib/casl";
82
+ import { findAllUsers } from "@/dal/user.dal";
83
+
84
+ export async function listUsers(currentUser: AuthUser) {
85
+ const ability = defineAbilityFor(currentUser);
86
+ ForbiddenError.from(ability).throwUnlessCan("read", "User");
87
+ return findAllUsers();
88
+ }
89
+ ```
90
+
91
+ ## Data Access Layer (DAL)
92
+
93
+ - Pure data access — no business logic, no authorization.
94
+ - Located in `src/dal/` — files named `{entity}.dal.ts`.
95
+ - DAL functions shape data: select specific columns, join relations, paginate.
96
+ - Always return typed results — never raw query results.
97
+
98
+ ```ts
99
+ // src/dal/user.dal.ts
100
+ import { db } from "@/lib/db";
101
+ import { users } from "@/schema";
102
+
103
+ export async function findAllUsers() {
104
+ return db.select({
105
+ id: users.id,
106
+ name: users.name,
107
+ email: users.email,
108
+ role: users.role,
109
+ }).from(users);
110
+ }
111
+ ```
112
+
113
+ ## Persistence Layer
114
+
115
+ - Drizzle ORM schema definitions and database client.
116
+ - Located in `src/schema/` for table definitions, `src/lib/db.ts` for the client.
117
+ - Schema-only — no query logic here.
118
+
119
+ ## File Naming Convention
120
+
121
+ ```
122
+ src/
123
+ facades/ # {entity}.facade.ts
124
+ services/ # {entity}.service.ts
125
+ dal/ # {entity}.dal.ts
126
+ schema/ # {entity}.ts (Drizzle table defs)
127
+ lib/db.ts # Database client
128
+ ```
129
+
130
+ - All new files use **kebab-case**.
131
+ - One file per entity per layer — avoid catch-all files.
@@ -1,11 +1,18 @@
1
1
  ---
2
2
  name: nextjs-conventions
3
- description: "Enforces Next.js 15+ / React 19 / TypeScript conventions and best practices. Use when working on any Next.js project to ensure consistent patterns."
4
- allowed-tools: "Read, Write, Edit, Glob, Grep"
3
+ description: "Next.js 15+ / React 19 / TypeScript conventions for App Router, RSC, route groups, and file naming. Use when creating pages, components, layouts, or structuring a Next.js application."
5
4
  ---
6
5
 
7
6
  # Next.js Conventions
8
7
 
8
+ ## Critical Rules
9
+
10
+ - **RSC by default** — only add `"use client"` when strictly needed.
11
+ - **Functional over OOP** — pure functions, composition, immutability. Never class components.
12
+ - **kebab-case for all new files** — `user-profile.tsx`, `format-date.ts`.
13
+ - **Top-down design** — extract sub-functions/sub-components when >100 lines.
14
+ - **Never use `any`** — use `unknown` if the type is truly unknown.
15
+
9
16
  ## App Router
10
17
 
11
18
  - Use the App Router (`src/app/`) exclusively. Never use the Pages Router.
@@ -13,14 +20,39 @@ allowed-tools: "Read, Write, Edit, Glob, Grep"
13
20
  - Use `loading.tsx` for Suspense boundaries and `error.tsx` for error boundaries.
14
21
  - Use `not-found.tsx` for 404 pages at the appropriate route level.
15
22
 
23
+ ## Route Groups
24
+
25
+ Organize routes by access level using route groups:
26
+
27
+ ```
28
+ src/app/
29
+ (public)/ # No auth required — landing, login, register
30
+ login/
31
+ register/
32
+ (auth)/ # Auth required, any role — onboarding
33
+ onboarding/
34
+ (app)/ # Auth required, active user — main app
35
+ dashboard/
36
+ settings/
37
+ admin/ # Admin only — user management, app settings
38
+ users/
39
+ settings/
40
+ ```
41
+
42
+ - `(public)` routes are accessible without authentication.
43
+ - `(auth)` routes require a session but no specific role.
44
+ - `(app)` routes require an active, verified user.
45
+ - `admin` routes require admin role — not a route group so the URL reflects `/admin/`.
46
+
16
47
  ## Server vs Client Components
17
48
 
18
- - **Default to Server Components**. Only add `"use client"` when you need:
49
+ - **RSC by default**. Only add `"use client"` when strictly needed:
19
50
  - Event handlers (`onClick`, `onChange`, etc.)
20
51
  - React hooks (`useState`, `useEffect`, `useRef`, etc.)
21
52
  - Browser-only APIs (`window`, `localStorage`, etc.)
22
53
  - Never import server-only modules in client components.
23
54
  - Pass server data to client components via props, not by importing server functions.
55
+ - Wrap async data in `<Suspense>` boundaries for streaming and progressive rendering.
24
56
 
25
57
  ## Data Fetching
26
58
 
@@ -31,10 +63,10 @@ allowed-tools: "Read, Write, Edit, Glob, Grep"
31
63
 
32
64
  ## File Naming
33
65
 
34
- - Components: `PascalCase.tsx` (e.g., `UserProfile.tsx`)
35
- - Utilities: `kebab-case.ts` (e.g., `format-date.ts`)
36
- - Types: define in `src/types/` with `.ts` extension
37
- - Server Actions: `src/actions/{entity}.actions.ts`
66
+ - **All new files: `kebab-case`** (e.g., `user-profile.tsx`, `format-date.ts`).
67
+ - Types: define in `src/types/` with `.ts` extension.
68
+ - Server Actions: `src/actions/{entity}.actions.ts`.
69
+ - Schemas: `src/schemas/{entity}.schema.ts`.
38
70
 
39
71
  ## TypeScript
40
72
 
@@ -62,3 +94,10 @@ allowed-tools: "Read, Write, Edit, Glob, Grep"
62
94
  - Use `next/link` for internal navigation. Never use `<a>` for internal links.
63
95
  - Use `next/font` for font loading.
64
96
  - Lazy load heavy client components with `dynamic()` from `next/dynamic`.
97
+
98
+ ## Code Design
99
+
100
+ - **Functional over OOP** — pure functions, composition, immutability. Never use class components.
101
+ - **Top-down design** — if a function or component exceeds ~100 lines, extract sub-functions or sub-components.
102
+ - Prefer named exports over default exports (except for page/layout components).
103
+ - Group imports: React/Next.js first, then external libs, then internal modules.
@@ -1,11 +1,19 @@
1
1
  ---
2
2
  name: react-native-patterns
3
- description: "React Native and Expo best practices for navigation, styling, performance, and native features. Use when building mobile apps with Expo. Inspired by callstackincubator/agent-skills and expo/skills."
4
- allowed-tools: "Read, Write, Edit, Glob, Grep"
3
+ description: "React Native and Expo best practices for navigation, styling, performance, and native features. Use when building mobile apps with Expo, implementing React Native navigation, styling with NativeWind, optimizing mobile performance, or adding push notifications."
5
4
  ---
6
5
 
7
6
  # React Native & Expo Patterns
8
7
 
8
+ ## Critical Rules
9
+
10
+ - **Always try Expo Go first** — only use `npx expo run:ios/android` when custom native modules are required.
11
+ - **Functional components only** — never class components.
12
+ - **Use `expo-image` instead of React Native's `Image`** — better caching and performance.
13
+ - **Use `react-native-safe-area-context`** — never React Native's built-in `SafeAreaView`.
14
+ - **Use `FlashList` instead of `FlatList`** for large lists — significantly better performance.
15
+ - **Never block the JS thread** — target 60fps, offload heavy computation to native.
16
+
9
17
  ## Expo Router
10
18
 
11
19
  - Use file-based routing in the `app/` directory exclusively.
@@ -23,12 +31,6 @@ allowed-tools: "Read, Write, Edit, Glob, Grep"
23
31
  - Prefer `useWindowDimensions` over `Dimensions.get()` for responsive layouts.
24
32
  - Use flexbox exclusively for layout — avoid absolute positioning except for overlays.
25
33
 
26
- ## Running the App
27
-
28
- - **Always try Expo Go first** before creating custom builds (`npx expo start`).
29
- - Only use `npx expo run:ios/android` when custom native modules are required.
30
- - Expo Go supports: all `expo-*` packages, Expo Router, Reanimated, Gesture Handler.
31
-
32
34
  ## Components
33
35
 
34
36
  - Use functional components only — never class components.