@folpe/loom 0.1.0 → 0.2.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.
- package/data/agents/orchestrator/AGENT.md +5 -1
- package/data/presets/api-backend.yaml +40 -0
- package/data/presets/chrome-extension.yaml +39 -0
- package/data/presets/cli-tool.yaml +34 -0
- package/data/presets/e-commerce.yaml +52 -0
- package/data/presets/expo-mobile.yaml +44 -0
- package/data/presets/fullstack-auth.yaml +48 -0
- package/data/presets/landing-page.yaml +41 -0
- package/data/presets/mvp-lean.yaml +38 -0
- package/data/presets/saas-default.yaml +4 -7
- package/data/skills/api-design/SKILL.md +108 -0
- package/data/skills/chrome-extension-patterns/SKILL.md +98 -0
- package/data/skills/cli-development/SKILL.md +139 -0
- package/data/skills/react-native-patterns/SKILL.md +85 -0
- package/data/skills/seo-optimization/SKILL.md +98 -0
- package/data/skills/shadcn-ui/SKILL.md +92 -0
- package/data/skills/stripe-integration/SKILL.md +88 -0
- package/data/skills/supabase-patterns/SKILL.md +93 -0
- package/data/skills/ui-ux-guidelines/SKILL.md +174 -0
- package/package.json +1 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-development
|
|
3
|
+
description: "CLI tool development patterns for Node.js with Commander.js, terminal UX, and npm distribution. Use when building command-line tools."
|
|
4
|
+
allowed-tools: "Read, Write, Edit, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# CLI Development Patterns
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/
|
|
13
|
+
index.ts # Entry point — program definition and parse()
|
|
14
|
+
commands/ # One file per command/subcommand
|
|
15
|
+
init.ts
|
|
16
|
+
build.ts
|
|
17
|
+
list.ts
|
|
18
|
+
lib/ # Shared utilities
|
|
19
|
+
config.ts # Config loading and validation
|
|
20
|
+
output.ts # Formatting and printing helpers
|
|
21
|
+
errors.ts # Custom error classes
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Commander.js Setup
|
|
25
|
+
|
|
26
|
+
- Define the program with metadata from `package.json`:
|
|
27
|
+
```ts
|
|
28
|
+
const program = new Command()
|
|
29
|
+
.name('mytool')
|
|
30
|
+
.description('What this tool does')
|
|
31
|
+
.version(version)
|
|
32
|
+
```
|
|
33
|
+
- One file per subcommand — register with `program.addCommand()`.
|
|
34
|
+
- Use `.argument()` for required positional args, `.option()` for flags:
|
|
35
|
+
```ts
|
|
36
|
+
program
|
|
37
|
+
.command('init')
|
|
38
|
+
.description('Initialize a new project')
|
|
39
|
+
.argument('<name>', 'project name')
|
|
40
|
+
.option('-t, --template <template>', 'template to use', 'default')
|
|
41
|
+
.action(async (name, options) => { /* ... */ })
|
|
42
|
+
```
|
|
43
|
+
- Always provide `--help` descriptions for all arguments and options.
|
|
44
|
+
|
|
45
|
+
## Exit Codes
|
|
46
|
+
|
|
47
|
+
- `0` — success
|
|
48
|
+
- `1` — general error (runtime failure, unhandled exception)
|
|
49
|
+
- `2` — usage error (invalid arguments, missing required flags)
|
|
50
|
+
- Exit explicitly: `process.exit(code)` after cleanup.
|
|
51
|
+
- Catch unhandled errors at the top level:
|
|
52
|
+
```ts
|
|
53
|
+
program.parseAsync().catch((error) => {
|
|
54
|
+
console.error(error.message)
|
|
55
|
+
process.exit(1)
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Output Conventions
|
|
60
|
+
|
|
61
|
+
- Use `stdout` for actual output (data, results, formatted tables).
|
|
62
|
+
- Use `stderr` for progress, logging, warnings, and errors.
|
|
63
|
+
- Support `--json` flag for machine-readable output on data commands.
|
|
64
|
+
- Support `--quiet` or `--silent` flag to suppress non-essential output.
|
|
65
|
+
- Use colors sparingly — respect `NO_COLOR` environment variable:
|
|
66
|
+
```ts
|
|
67
|
+
const useColor = !process.env.NO_COLOR && process.stdout.isTTY
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Terminal UX
|
|
71
|
+
|
|
72
|
+
- Show a spinner for long operations (use `ora` or `nanospinner`).
|
|
73
|
+
- Use progress bars for multi-step or percentage-based operations.
|
|
74
|
+
- Confirm destructive actions with a prompt (use `@inquirer/prompts`):
|
|
75
|
+
```ts
|
|
76
|
+
const confirmed = await confirm({ message: 'Delete all files?' })
|
|
77
|
+
if (!confirmed) process.exit(0)
|
|
78
|
+
```
|
|
79
|
+
- Use tables for structured data display (use `cli-table3` or `columnify`).
|
|
80
|
+
- Truncate long output with `--limit` option or pipe to `less`.
|
|
81
|
+
|
|
82
|
+
## Input
|
|
83
|
+
|
|
84
|
+
- Accept input from stdin for piping:
|
|
85
|
+
```ts
|
|
86
|
+
if (!process.stdin.isTTY) {
|
|
87
|
+
const input = await readStdin()
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
- Support both `--flag value` and `--flag=value` syntax (Commander.js handles this).
|
|
91
|
+
- Use environment variables as fallback for configuration: `MYTOOL_TOKEN`, `MYTOOL_CONFIG`.
|
|
92
|
+
|
|
93
|
+
## Error Messages
|
|
94
|
+
|
|
95
|
+
- Include what went wrong, why, and how to fix it:
|
|
96
|
+
```
|
|
97
|
+
Error: Config file not found at ./config.yaml
|
|
98
|
+
Run `mytool init` to create a default configuration.
|
|
99
|
+
```
|
|
100
|
+
- Use chalk/picocolors for error formatting:
|
|
101
|
+
- Red for errors
|
|
102
|
+
- Yellow for warnings
|
|
103
|
+
- Dim for secondary info
|
|
104
|
+
- Never show raw stack traces to users. Log them with `--verbose` flag.
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
107
|
+
|
|
108
|
+
- Support config file (`.mytoolrc`, `mytool.config.ts`, or field in `package.json`).
|
|
109
|
+
- Use `cosmiconfig` or manual lookup for config file discovery.
|
|
110
|
+
- Validate config with Zod schema — fail fast with clear error on invalid config.
|
|
111
|
+
- Allow CLI flags to override config file values.
|
|
112
|
+
|
|
113
|
+
## Bundling & Distribution
|
|
114
|
+
|
|
115
|
+
- Bundle with `tsup` for a single distributable file:
|
|
116
|
+
```ts
|
|
117
|
+
// tsup.config.ts
|
|
118
|
+
export default defineConfig({
|
|
119
|
+
entry: ['src/index.ts'],
|
|
120
|
+
format: ['esm'],
|
|
121
|
+
target: 'node20',
|
|
122
|
+
clean: true,
|
|
123
|
+
banner: { js: '#!/usr/bin/env node' },
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
- Set `"bin"` in `package.json` pointing to the built file.
|
|
127
|
+
- Set `"type": "module"` for ESM.
|
|
128
|
+
- Test the built binary locally before publishing: `node dist/index.js`.
|
|
129
|
+
|
|
130
|
+
## Testing
|
|
131
|
+
|
|
132
|
+
- Test commands by invoking them programmatically, not by spawning processes:
|
|
133
|
+
```ts
|
|
134
|
+
import { program } from '../src/index'
|
|
135
|
+
program.parse(['node', 'test', 'init', 'my-project'])
|
|
136
|
+
```
|
|
137
|
+
- Test output by capturing stdout/stderr.
|
|
138
|
+
- Test error cases: missing args, invalid flags, missing config.
|
|
139
|
+
- Test stdin piping with mock readable streams.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
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"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Native & Expo Patterns
|
|
8
|
+
|
|
9
|
+
## Expo Router
|
|
10
|
+
|
|
11
|
+
- Use file-based routing in the `app/` directory exclusively.
|
|
12
|
+
- Never co-locate components, types, or utilities in the `app/` directory — keep them in `src/`.
|
|
13
|
+
- Always ensure a route matches `/` (may be inside a group route).
|
|
14
|
+
- Use layout routes (`_layout.tsx`) for shared navigation UI (tabs, stacks, drawers).
|
|
15
|
+
- Remove old route files immediately when restructuring navigation.
|
|
16
|
+
- Use kebab-case for file names: `comment-card.tsx`.
|
|
17
|
+
|
|
18
|
+
## Styling with NativeWind
|
|
19
|
+
|
|
20
|
+
- Use NativeWind (Tailwind for React Native) as the primary styling method.
|
|
21
|
+
- Use `className` prop like web Tailwind — NativeWind compiles to StyleSheet.
|
|
22
|
+
- Use `cn()` helper for conditional classes.
|
|
23
|
+
- Prefer `useWindowDimensions` over `Dimensions.get()` for responsive layouts.
|
|
24
|
+
- Use flexbox exclusively for layout — avoid absolute positioning except for overlays.
|
|
25
|
+
|
|
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
|
+
## Components
|
|
33
|
+
|
|
34
|
+
- Use functional components only — never class components.
|
|
35
|
+
- Use `expo-image` instead of React Native's `Image` — better caching and performance.
|
|
36
|
+
- Use `expo-image` with `source="sf:name"` for SF Symbols icons — not `@expo/vector-icons`.
|
|
37
|
+
- Use `react-native-safe-area-context` — never React Native's built-in `SafeAreaView`.
|
|
38
|
+
- Use `<ScrollView contentInsetAdjustmentBehavior="automatic" />` instead of `<SafeAreaView>` for root scrolling.
|
|
39
|
+
- Use `process.env.EXPO_OS` instead of `Platform.OS`.
|
|
40
|
+
- Use `React.use` instead of `React.useContext`.
|
|
41
|
+
|
|
42
|
+
## Navigation
|
|
43
|
+
|
|
44
|
+
- Use Expo Router's typed routes for type-safe navigation.
|
|
45
|
+
- Handle deep linking and universal links from project start.
|
|
46
|
+
- Use native tabs (`NativeTabs`) for iOS tab navigation when possible.
|
|
47
|
+
- Preload frequently accessed screens for faster navigation.
|
|
48
|
+
|
|
49
|
+
## Performance
|
|
50
|
+
|
|
51
|
+
- **Profile before optimizing** — measure with React DevTools and Xcode Instruments.
|
|
52
|
+
- Replace `ScrollView` with `FlashList` (not `FlatList`) for large lists.
|
|
53
|
+
- Use React Compiler for automatic memoization when available.
|
|
54
|
+
- Avoid barrel exports — import directly from source files to reduce bundle size.
|
|
55
|
+
- Keep JS thread work minimal — offload to native with Turbo Modules when needed.
|
|
56
|
+
- Target 60fps — never block the JS thread with heavy computation.
|
|
57
|
+
- Use `useDeferredValue` for expensive computations during rendering.
|
|
58
|
+
|
|
59
|
+
## Animations
|
|
60
|
+
|
|
61
|
+
- Use `react-native-reanimated` for performant animations on the UI thread.
|
|
62
|
+
- Animate only `transform` and `opacity` — avoid animating layout properties.
|
|
63
|
+
- Use shared values and worklets for gesture-driven animations.
|
|
64
|
+
- Respect `prefers-reduced-motion` accessibility setting.
|
|
65
|
+
|
|
66
|
+
## Offline & Storage
|
|
67
|
+
|
|
68
|
+
- Use `@react-native-async-storage/async-storage` or `react-native-mmkv` for local key-value storage.
|
|
69
|
+
- Use `expo-sqlite` for structured offline data.
|
|
70
|
+
- Implement optimistic updates for network operations.
|
|
71
|
+
- Cache critical data locally for offline-first experience.
|
|
72
|
+
|
|
73
|
+
## Push Notifications
|
|
74
|
+
|
|
75
|
+
- Use `expo-notifications` for push notification setup.
|
|
76
|
+
- Request permission at a contextual moment — never on first launch.
|
|
77
|
+
- Handle notification responses (tap, dismiss) in the app root.
|
|
78
|
+
- Store push tokens in your backend database, linked to user profiles.
|
|
79
|
+
|
|
80
|
+
## App Distribution
|
|
81
|
+
|
|
82
|
+
- Use EAS Build for creating production builds.
|
|
83
|
+
- Use EAS Submit for app store submissions.
|
|
84
|
+
- Configure `app.json` / `app.config.ts` thoroughly: icons, splash, permissions, version.
|
|
85
|
+
- Test on both iOS and Android — never assume platform parity.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: seo-optimization
|
|
3
|
+
description: "SEO best practices for structured data, meta tags, Core Web Vitals, and indexing. Use when building landing pages, marketing sites, or public-facing content."
|
|
4
|
+
allowed-tools: "Read, Write, Edit, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SEO Optimization
|
|
8
|
+
|
|
9
|
+
## Meta Tags
|
|
10
|
+
|
|
11
|
+
- Every page must have unique `title` and `description` meta tags:
|
|
12
|
+
```ts
|
|
13
|
+
export const metadata: Metadata = {
|
|
14
|
+
title: 'Product Name — Clear Value Proposition',
|
|
15
|
+
description: 'Compelling 150-160 char description with primary keyword and call to action.',
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
- Title format: `Page Title — Brand Name` (50-60 characters).
|
|
19
|
+
- Description: 150-160 characters, include primary keyword, end with CTA or benefit.
|
|
20
|
+
- Use `generateMetadata()` for dynamic routes to generate unique meta per page.
|
|
21
|
+
|
|
22
|
+
## Open Graph & Social
|
|
23
|
+
|
|
24
|
+
- Include Open Graph tags for social sharing:
|
|
25
|
+
```ts
|
|
26
|
+
openGraph: {
|
|
27
|
+
title: 'Page Title',
|
|
28
|
+
description: 'Social description',
|
|
29
|
+
images: [{ url: '/og-image.png', width: 1200, height: 630 }],
|
|
30
|
+
type: 'website',
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
- Create a dedicated OG image for each important page (1200x630px).
|
|
34
|
+
- Add Twitter card meta: `twitter: { card: 'summary_large_image' }`.
|
|
35
|
+
|
|
36
|
+
## Structured Data (JSON-LD)
|
|
37
|
+
|
|
38
|
+
- Add JSON-LD structured data for rich search results:
|
|
39
|
+
```tsx
|
|
40
|
+
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({
|
|
41
|
+
'@context': 'https://schema.org',
|
|
42
|
+
'@type': 'Product',
|
|
43
|
+
name: product.name,
|
|
44
|
+
description: product.description,
|
|
45
|
+
offers: { '@type': 'Offer', price: product.price, priceCurrency: 'EUR' }
|
|
46
|
+
}) }} />
|
|
47
|
+
```
|
|
48
|
+
- Use appropriate schema types: `Organization`, `Product`, `Article`, `FAQPage`, `BreadcrumbList`.
|
|
49
|
+
- Validate with Google's Rich Results Test.
|
|
50
|
+
|
|
51
|
+
## Semantic HTML
|
|
52
|
+
|
|
53
|
+
- Use proper heading hierarchy: one `<h1>` per page, then `<h2>`, `<h3>`, etc.
|
|
54
|
+
- Use semantic elements: `<main>`, `<article>`, `<section>`, `<nav>`, `<aside>`, `<footer>`.
|
|
55
|
+
- Add `alt` text to all images — descriptive, not keyword-stuffed.
|
|
56
|
+
- Use `<a>` with descriptive anchor text — avoid "click here".
|
|
57
|
+
|
|
58
|
+
## Performance (Core Web Vitals)
|
|
59
|
+
|
|
60
|
+
- Target scores: LCP < 2.5s, INP < 200ms, CLS < 0.1.
|
|
61
|
+
- Optimize images with `next/image`: proper sizing, modern formats (WebP/AVIF), lazy loading.
|
|
62
|
+
- Minimize render-blocking resources: defer non-critical CSS/JS.
|
|
63
|
+
- Use `next/font` to prevent FOUT/FOIT.
|
|
64
|
+
- Preload critical assets: hero images, above-the-fold fonts.
|
|
65
|
+
|
|
66
|
+
## Technical SEO
|
|
67
|
+
|
|
68
|
+
- Generate `sitemap.xml` dynamically:
|
|
69
|
+
```ts
|
|
70
|
+
// src/app/sitemap.ts
|
|
71
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
72
|
+
return [
|
|
73
|
+
{ url: 'https://example.com', lastModified: new Date(), changeFrequency: 'weekly', priority: 1 },
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
- Create `robots.txt`:
|
|
78
|
+
```ts
|
|
79
|
+
// src/app/robots.ts
|
|
80
|
+
export default function robots(): MetadataRoute.Robots {
|
|
81
|
+
return { rules: { userAgent: '*', allow: '/' }, sitemap: 'https://example.com/sitemap.xml' }
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
- Use canonical URLs to prevent duplicate content issues.
|
|
85
|
+
- Implement proper redirects (301) for moved pages — never soft 404s.
|
|
86
|
+
|
|
87
|
+
## Content
|
|
88
|
+
|
|
89
|
+
- Place primary keyword in the first 100 words of page content.
|
|
90
|
+
- Use internal links between related pages to distribute page authority.
|
|
91
|
+
- Add breadcrumb navigation with `BreadcrumbList` schema.
|
|
92
|
+
- Ensure all pages are reachable within 3 clicks from the homepage.
|
|
93
|
+
|
|
94
|
+
## Internationalization
|
|
95
|
+
|
|
96
|
+
- Use `hreflang` tags for multi-language sites.
|
|
97
|
+
- Use `lang` attribute on `<html>` element.
|
|
98
|
+
- Create dedicated URLs per language: `/en/about`, `/fr/about`.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: shadcn-ui
|
|
3
|
+
description: "ShadCN UI component patterns, composition, and accessibility guidelines. Use when building interfaces with ShadCN components. Inspired by ibelick/ui-skills baseline-ui."
|
|
4
|
+
allowed-tools: "Read, Write, Edit, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ShadCN UI Patterns
|
|
8
|
+
|
|
9
|
+
## Installation & Setup
|
|
10
|
+
|
|
11
|
+
- Install components individually with `npx shadcn@latest add <component>` — never install all at once.
|
|
12
|
+
- Components are copied to `src/components/ui/` — they are yours to customize.
|
|
13
|
+
- Configure `components.json` for path aliases and styling preferences.
|
|
14
|
+
- Use `cn()` from `src/lib/utils` for class merging (clsx + tailwind-merge).
|
|
15
|
+
|
|
16
|
+
## Component Usage
|
|
17
|
+
|
|
18
|
+
- **Always use ShadCN primitives** before building custom components:
|
|
19
|
+
- `Button` for all clickable actions (with appropriate `variant` and `size`)
|
|
20
|
+
- `Card` for content containers
|
|
21
|
+
- `Dialog` for modal overlays
|
|
22
|
+
- `Sheet` for slide-out panels
|
|
23
|
+
- `DropdownMenu` for action menus
|
|
24
|
+
- `Select` for option picking
|
|
25
|
+
- `Table` for data display
|
|
26
|
+
- `Form` + `Input` + `Label` for forms
|
|
27
|
+
- `Badge` for tags and status indicators
|
|
28
|
+
- `Tabs` for content switching
|
|
29
|
+
- `Toast` / `Sonner` for notifications
|
|
30
|
+
- Never rebuild keyboard or focus behavior by hand — use the component primitives.
|
|
31
|
+
- Never mix primitive systems (Radix, Headless UI, React Aria) within the same surface.
|
|
32
|
+
|
|
33
|
+
## Forms
|
|
34
|
+
|
|
35
|
+
- Use `react-hook-form` + `zod` with ShadCN's `Form` component:
|
|
36
|
+
```tsx
|
|
37
|
+
const form = useForm<z.infer<typeof schema>>({
|
|
38
|
+
resolver: zodResolver(schema),
|
|
39
|
+
defaultValues: { name: '' },
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
- Stack form fields with `space-y-4`.
|
|
43
|
+
- Use `grid gap-4 md:grid-cols-2` for side-by-side fields on desktop.
|
|
44
|
+
- Show validation errors inline with `FormMessage`.
|
|
45
|
+
- Use `FormDescription` for helper text below fields.
|
|
46
|
+
|
|
47
|
+
## Data Tables
|
|
48
|
+
|
|
49
|
+
- Use ShadCN `DataTable` pattern with `@tanstack/react-table`:
|
|
50
|
+
- Define columns with type-safe `ColumnDef`.
|
|
51
|
+
- Support sorting, filtering, and pagination.
|
|
52
|
+
- Add row actions via `DropdownMenu`.
|
|
53
|
+
- Use `tabular-nums` on numeric columns for alignment.
|
|
54
|
+
- Add loading skeletons with ShadCN `Skeleton` for async data.
|
|
55
|
+
|
|
56
|
+
## Dialogs & Alerts
|
|
57
|
+
|
|
58
|
+
- Use `Dialog` for informational or form-based modals.
|
|
59
|
+
- Use `AlertDialog` for destructive or irreversible actions — never a plain `Dialog`.
|
|
60
|
+
- Keep dialog content focused — one primary action per dialog.
|
|
61
|
+
- Always provide a way to dismiss (close button, escape key, outside click).
|
|
62
|
+
|
|
63
|
+
## Theming
|
|
64
|
+
|
|
65
|
+
- Use CSS variables from the ShadCN theme system: `bg-background`, `text-foreground`, `bg-card`, `text-muted-foreground`, `bg-primary`, etc.
|
|
66
|
+
- Never hardcode hex colors — always use semantic tokens.
|
|
67
|
+
- Support dark mode via the `dark` class on `<html>` — CSS variables handle the switch.
|
|
68
|
+
- Limit accent color usage to one per view.
|
|
69
|
+
|
|
70
|
+
## Accessibility
|
|
71
|
+
|
|
72
|
+
- All icon-only buttons must have `aria-label`.
|
|
73
|
+
- Use `sr-only` class for screen-reader-only text.
|
|
74
|
+
- Ensure all interactive elements have visible focus states (ShadCN handles this by default).
|
|
75
|
+
- Use `role` and `aria-*` attributes when composing custom components.
|
|
76
|
+
- Never block paste in `input` or `textarea` elements.
|
|
77
|
+
|
|
78
|
+
## Animation
|
|
79
|
+
|
|
80
|
+
- Never add animation unless explicitly requested.
|
|
81
|
+
- Use `transition-colors` for hover/focus state changes.
|
|
82
|
+
- Keep interaction feedback under 200ms.
|
|
83
|
+
- Avoid animating layout properties (`width`, `height`, `margin`, `padding`) — use `transform` and `opacity` only.
|
|
84
|
+
- Respect `prefers-reduced-motion` media query.
|
|
85
|
+
|
|
86
|
+
## Anti-Patterns to Avoid
|
|
87
|
+
|
|
88
|
+
- Never use `h-screen` — use `h-dvh` for correct mobile viewport.
|
|
89
|
+
- Never use arbitrary `z-index` values — use a fixed scale.
|
|
90
|
+
- Never use gradients or glow effects unless explicitly requested.
|
|
91
|
+
- Never use custom easing curves unless explicitly requested.
|
|
92
|
+
- Empty states must have one clear next action.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stripe-integration
|
|
3
|
+
description: "Stripe payment integration patterns for checkout, subscriptions, and webhooks. Use when building e-commerce, SaaS billing, or any payment flow."
|
|
4
|
+
allowed-tools: "Read, Write, Edit, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Stripe Integration
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
- Install `stripe` (server) and `@stripe/stripe-js` + `@stripe/react-stripe-js` (client).
|
|
12
|
+
- Store keys in environment variables:
|
|
13
|
+
- `STRIPE_SECRET_KEY` — server-side only, never expose to client
|
|
14
|
+
- `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` — safe for client-side
|
|
15
|
+
- `STRIPE_WEBHOOK_SECRET` — for verifying webhook signatures
|
|
16
|
+
- Create a Stripe instance in `src/lib/stripe.ts`:
|
|
17
|
+
```ts
|
|
18
|
+
import Stripe from 'stripe'
|
|
19
|
+
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Checkout
|
|
23
|
+
|
|
24
|
+
- **Always use Stripe Checkout or Payment Elements** — never collect card details directly.
|
|
25
|
+
- Create a Checkout Session server-side, redirect client-side:
|
|
26
|
+
```ts
|
|
27
|
+
const session = await stripe.checkout.sessions.create({
|
|
28
|
+
mode: 'payment', // or 'subscription'
|
|
29
|
+
line_items: [{ price: priceId, quantity: 1 }],
|
|
30
|
+
success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
31
|
+
cancel_url: `${origin}/cart`,
|
|
32
|
+
metadata: { userId, orderId },
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
- Use `metadata` to link Stripe objects back to your database records.
|
|
36
|
+
- For subscriptions, use `mode: 'subscription'` with recurring prices.
|
|
37
|
+
|
|
38
|
+
## Webhooks
|
|
39
|
+
|
|
40
|
+
- Create a webhook endpoint at `/api/webhooks/stripe`:
|
|
41
|
+
```ts
|
|
42
|
+
const sig = request.headers.get('stripe-signature')!
|
|
43
|
+
const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
|
|
44
|
+
```
|
|
45
|
+
- Always verify webhook signatures. Never trust unverified payloads.
|
|
46
|
+
- Handle these critical events:
|
|
47
|
+
- `checkout.session.completed` — fulfill the order
|
|
48
|
+
- `invoice.payment_succeeded` — extend subscription
|
|
49
|
+
- `invoice.payment_failed` — notify user, retry logic
|
|
50
|
+
- `customer.subscription.deleted` — revoke access
|
|
51
|
+
- Make webhook handlers idempotent — the same event may arrive multiple times.
|
|
52
|
+
- Return 200 quickly. Process heavy logic asynchronously if needed.
|
|
53
|
+
|
|
54
|
+
## Products & Prices
|
|
55
|
+
|
|
56
|
+
- Define products and prices in Stripe Dashboard for simplicity.
|
|
57
|
+
- Use `price` IDs (not `product` IDs) when creating Checkout Sessions.
|
|
58
|
+
- For multiple pricing tiers, create one product with multiple prices (monthly/yearly).
|
|
59
|
+
- Sync product/price data to your database via webhooks or a scheduled job.
|
|
60
|
+
|
|
61
|
+
## Customer Portal
|
|
62
|
+
|
|
63
|
+
- Use Stripe Customer Portal for subscription management (upgrade, downgrade, cancel, update payment method).
|
|
64
|
+
- Create a portal session and redirect:
|
|
65
|
+
```ts
|
|
66
|
+
const portalSession = await stripe.billingPortal.sessions.create({
|
|
67
|
+
customer: customerId,
|
|
68
|
+
return_url: `${origin}/account`,
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Security
|
|
73
|
+
|
|
74
|
+
- Never log or store raw card numbers, CVV, or full card details.
|
|
75
|
+
- Use Stripe's PCI-compliant elements for all card input.
|
|
76
|
+
- Validate amounts and currencies server-side before creating charges.
|
|
77
|
+
- Use `idempotencyKey` for critical operations to prevent duplicate charges.
|
|
78
|
+
- Restrict Stripe API key permissions in production (read-only where possible).
|
|
79
|
+
|
|
80
|
+
## Testing
|
|
81
|
+
|
|
82
|
+
- Use Stripe test mode keys during development.
|
|
83
|
+
- Use test card numbers: `4242424242424242` (success), `4000000000000002` (decline).
|
|
84
|
+
- Use Stripe CLI to forward webhooks to localhost:
|
|
85
|
+
```bash
|
|
86
|
+
stripe listen --forward-to localhost:3000/api/webhooks/stripe
|
|
87
|
+
```
|
|
88
|
+
- Test all webhook event types, especially failure scenarios.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-patterns
|
|
3
|
+
description: "Supabase best practices for auth, database, RLS, and real-time. Use when working with Supabase in any project. Inspired by supabase/agent-skills."
|
|
4
|
+
allowed-tools: "Read, Write, Edit, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Supabase Patterns
|
|
8
|
+
|
|
9
|
+
## Authentication
|
|
10
|
+
|
|
11
|
+
- Use `@supabase/ssr` for Next.js server-side auth — never use `@supabase/auth-helpers-nextjs` (deprecated).
|
|
12
|
+
- Create two client helpers:
|
|
13
|
+
- `createClient()` in `src/lib/supabase/client.ts` for Client Components
|
|
14
|
+
- `createServerClient()` in `src/lib/supabase/server.ts` for Server Components / Actions
|
|
15
|
+
- Always validate session server-side before granting access. Never trust `supabase.auth.getUser()` from the client alone.
|
|
16
|
+
- Use middleware (`middleware.ts`) to refresh the session on every request:
|
|
17
|
+
```ts
|
|
18
|
+
const { data: { user } } = await supabase.auth.getUser()
|
|
19
|
+
if (!user && protectedRoutes.includes(request.nextUrl.pathname)) {
|
|
20
|
+
return NextResponse.redirect(new URL('/login', request.url))
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
- Support multiple auth strategies: email/password, OAuth (Google, GitHub), magic link.
|
|
24
|
+
- Store user metadata in a separate `profiles` table linked by `auth.users.id`.
|
|
25
|
+
|
|
26
|
+
## Row Level Security (RLS)
|
|
27
|
+
|
|
28
|
+
- **Enable RLS on every table** — no exceptions, even internal tables.
|
|
29
|
+
- Write policies that reference `auth.uid()` directly:
|
|
30
|
+
```sql
|
|
31
|
+
CREATE POLICY "Users read own data"
|
|
32
|
+
ON profiles FOR SELECT
|
|
33
|
+
USING (auth.uid() = user_id);
|
|
34
|
+
```
|
|
35
|
+
- Avoid `security definer` functions unless absolutely necessary. Prefer RLS policies.
|
|
36
|
+
- Test policies explicitly: try accessing data as different users/roles.
|
|
37
|
+
- Use `auth.jwt() ->> 'role'` for role-based access if implementing RBAC.
|
|
38
|
+
|
|
39
|
+
## Database Schema
|
|
40
|
+
|
|
41
|
+
- Use `uuid` for primary keys, generated by `gen_random_uuid()`.
|
|
42
|
+
- Add `created_at` and `updated_at` timestamps to every table:
|
|
43
|
+
```sql
|
|
44
|
+
created_at timestamptz DEFAULT now() NOT NULL,
|
|
45
|
+
updated_at timestamptz DEFAULT now() NOT NULL
|
|
46
|
+
```
|
|
47
|
+
- Create an `updated_at` trigger using `moddatetime` extension.
|
|
48
|
+
- Use foreign key constraints for referential integrity.
|
|
49
|
+
- Add indexes on columns used in WHERE clauses and JOIN conditions.
|
|
50
|
+
|
|
51
|
+
## Query Patterns
|
|
52
|
+
|
|
53
|
+
- Use the Supabase client query builder for simple CRUD:
|
|
54
|
+
```ts
|
|
55
|
+
const { data, error } = await supabase
|
|
56
|
+
.from('posts')
|
|
57
|
+
.select('*, author:profiles(name, avatar_url)')
|
|
58
|
+
.eq('published', true)
|
|
59
|
+
.order('created_at', { ascending: false })
|
|
60
|
+
.limit(20)
|
|
61
|
+
```
|
|
62
|
+
- Handle errors explicitly — never ignore `error`:
|
|
63
|
+
```ts
|
|
64
|
+
if (error) throw new Error(error.message)
|
|
65
|
+
```
|
|
66
|
+
- Use `.single()` when expecting exactly one row. Use `.maybeSingle()` when the row may not exist.
|
|
67
|
+
- Use server-side Supabase client for mutations in Server Actions.
|
|
68
|
+
|
|
69
|
+
## Real-time
|
|
70
|
+
|
|
71
|
+
- Use Supabase Realtime only for features that genuinely need live updates (chat, notifications, collaboration).
|
|
72
|
+
- Subscribe in Client Components and clean up on unmount:
|
|
73
|
+
```ts
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const channel = supabase.channel('room').on('postgres_changes', { event: '*', schema: 'public', table: 'messages' }, handler).subscribe()
|
|
76
|
+
return () => { supabase.removeChannel(channel) }
|
|
77
|
+
}, [])
|
|
78
|
+
```
|
|
79
|
+
- Prefer polling or revalidation for data that changes infrequently.
|
|
80
|
+
|
|
81
|
+
## Storage
|
|
82
|
+
|
|
83
|
+
- Use Supabase Storage for file uploads (images, documents).
|
|
84
|
+
- Create separate buckets for public vs private files.
|
|
85
|
+
- Set RLS policies on storage buckets.
|
|
86
|
+
- Generate signed URLs for private files with short expiration times.
|
|
87
|
+
|
|
88
|
+
## Performance
|
|
89
|
+
|
|
90
|
+
- Use connection pooling (Supavisor) in production — never connect directly at scale.
|
|
91
|
+
- Add `LIMIT` to all queries. Never fetch unbounded result sets.
|
|
92
|
+
- Use `select('column1, column2')` instead of `select('*')` for large tables.
|
|
93
|
+
- Create partial indexes for frequently filtered subsets of data.
|