Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:45:50 +08:00
commit bd85f56f7c
78 changed files with 33541 additions and 0 deletions

View File

@@ -0,0 +1,617 @@
---
name: animation-interaction-validator
description: Ensures engaging user experience through validation of animations, transitions, micro-interactions, and feedback states, preventing flat/static interfaces that lack polish and engagement. Works with Tanstack Start (React) + shadcn/ui components.
triggers: ["interactive element creation", "event handler addition", "state changes", "async actions", "form submissions"]
note: "Code examples use React/TSX with shadcn/ui components (Button, Card, Input). Adapt patterns to your component library."
---
# Animation Interaction Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- Interactive elements are created (buttons, links, forms, inputs)
- Click, hover, or focus event handlers are added
- Component state changes (loading, success, error)
- Async operations are initiated (API calls, form submissions)
- Navigation or routing transitions occur
- Modal/dialog components are opened/closed
- Lists or data are updated dynamically
## Expertise Provided
### Animation & Interaction Validation
- **Transition Detection**: Ensures smooth state changes with CSS transitions
- **Hover State Validation**: Checks for hover feedback on interactive elements
- **Loading State Validation**: Ensures async actions have visual feedback
- **Micro-interaction Analysis**: Validates small, delightful animations
- **Focus State Validation**: Ensures keyboard navigation has visual feedback
- **Animation Performance**: Checks for performant animation patterns
### Specific Checks Performed
#### ❌ Critical Issues (Missing Feedback)
```tsx
// These patterns trigger alerts:
// No hover state
<Button onClick={submit}>Submit</Button>
// No loading state during async action
<Button onClick={async () => await submitForm()}>Save</Button>
// Jarring state change (no transition)
{showContent && <div>Content</div>}
// No focus state
<a href="/page" className="text-blue-500">Link</a>
// Form without feedback
<form onSubmit={handleSubmit}>
<Input value={value} onChange={setValue} />
<button type="submit">Submit</button>
</form>
```
#### ✅ Correct Interactive Patterns
```tsx
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Send } from "lucide-react"
import { cn } from "@/lib/utils"
// These patterns are validated as correct:
// Hover state with smooth transition
<Button
className="transition-all duration-300 hover:scale-105 hover:shadow-xl active:scale-95"
onClick={submit}
>
Submit
</Button>
// Loading state with visual feedback
<Button
disabled={isSubmitting}
className="transition-all duration-200 group"
onClick={handleSubmit}
>
<span className="flex items-center gap-2">
{!isSubmitting && (
<Send className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1" />
)}
{isSubmitting ? 'Submitting...' : 'Submit'}
</span>
</Button>
// Smooth state transition (using framer-motion or CSS)
<div
className={cn(
"transition-all duration-300 ease-out",
showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"
)}
>
{showContent && <div>Content</div>}
</div>
// Focus state with ring
<a
href="/page"
className="text-blue-500 transition-colors duration-200 hover:text-blue-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
>
Link
</a>
<!-- Form with success/error feedback -->
<form onSubmit={(e) => { e.preventDefault(); handleSubmit" className="space-y-4">
<Input
value="value"
error={errors.value"
className="transition-all duration-200"
/>
<Button
type="submit"
loading={isSubmitting"
disabled={isSubmitting"
className="transition-all duration-300 hover:scale-105"
>
Submit
</Button>
<!-- Success message with animation -->
<Transition name="fade">
<Alert
if="showSuccess"
color="green"
icon="i-heroicons-check-circle"
title="Success!"
className="animate-in slide-in-from-top"
/>
</Transition>
</form>
```
## Integration Points
### Complementary to Existing Components
- **frontend-design-specialist agent**: Provides design direction, SKILL validates implementation
- **component-aesthetic-checker**: Validates component customization, SKILL validates interactions
- **shadcn-ui-design-validator**: Catches generic patterns, SKILL ensures engagement
- **accessibility-guardian agent**: Validates a11y, SKILL validates visual feedback
### Escalation Triggers
- Complex animation sequences → `frontend-design-specialist` agent
- Component interaction patterns → `tanstack-ui-architect` agent
- Performance concerns → `edge-performance-oracle` agent
- Accessibility issues → `accessibility-guardian` agent
## Validation Rules
### P1 - Critical (Missing User Feedback)
- **No Hover States**: Buttons/links without hover effects
- **No Loading States**: Async actions without loading indicators
- **Jarring State Changes**: Content appearing/disappearing without transitions
- **No Focus States**: Interactive elements without keyboard focus indicators
- **Silent Errors**: Form errors without visual feedback
### P2 - Important (Enhanced Engagement)
- **No Micro-interactions**: Icons/elements without subtle animations
- **Static Navigation**: Page transitions without animations
- **Abrupt Modals**: Dialogs opening without enter/exit transitions
- **Instant Updates**: List changes without transition animations
- **No Disabled States**: Buttons during processing without visual change
### P3 - Polish (Delightful UX)
- **Limited Animation Variety**: Using only scale/opacity (no rotate, translate)
- **Generic Durations**: Not tuning animation speed for context
- **No Stagger**: List items appearing simultaneously (no stagger effect)
- **Missing Success States**: Completed actions without celebration animation
- **No Hover Anticipation**: No visual hint before interaction is possible
## Remediation Examples
### Fixing Missing Hover States
```tsx
<!-- ❌ Critical: No hover feedback -->
<Button onClick="handleClick">
Click me
</Button>
<!-- ✅ Correct: Multi-dimensional hover effects -->
<Button
className="
transition-all duration-300 ease-out
hover:scale-105 hover:shadow-xl hover:-rotate-1
active:scale-95 active:rotate-0
focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary-500
"
onClick="handleClick"
>
<span className="inline-flex items-center gap-2">
Click me
<Icon
name="i-heroicons-arrow-right"
className="transition-transform duration-300 group-hover:translate-x-1"
/>
</span>
</Button>
```
### Fixing Missing Loading States
```tsx
<!-- ❌ Critical: No loading feedback during async action -->
const submitForm = async () => {
await api.submit(formData);
};
<Button onClick="submitForm">
Submit
</Button>
<!-- ✅ Correct: Complete loading state with animations -->
const isSubmitting = ref(false);
const showSuccess = ref(false);
const submitForm = async () => {
isSubmitting.value = true;
try {
await api.submit(formData);
showSuccess.value = true;
setTimeout(() => showSuccess.value = false, 3000);
} catch (error) {
// Error handling
} finally {
isSubmitting.value = false;
}
};
<div className="space-y-4">
<Button
loading={isSubmitting"
disabled={isSubmitting"
className="
transition-all duration-300
hover:scale-105 hover:shadow-xl
disabled:opacity-50 disabled:cursor-not-allowed
"
onClick="submitForm"
>
<span className="flex items-center gap-2">
<Icon
if="!isSubmitting"
name="i-heroicons-paper-airplane"
className="transition-all duration-300 group-hover:translate-x-1 group-hover:-translate-y-1"
/>
{ isSubmitting ? 'Submitting...' : 'Submit'}
</span>
</Button>
<!-- Success feedback with animation -->
<Transition
enter-active-className="transition-all duration-500 ease-out"
enter-from-className="opacity-0 scale-50"
enter-to-className="opacity-100 scale-100"
leave-active-className="transition-all duration-300 ease-in"
leave-from-className="opacity-100 scale-100"
leave-to-className="opacity-0 scale-50"
>
<Alert
if="showSuccess"
color="green"
icon="i-heroicons-check-circle"
title="Success!"
description="Your form has been submitted."
/>
</Transition>
</div>
```
### Fixing Jarring State Changes
```tsx
<!-- ❌ Critical: Content appears/disappears abruptly -->
<div>
<Button onClick="showContent = !showContent">
Toggle
</Button>
<div if="showContent">
<p>This content appears instantly (jarring)</p>
</div>
</div>
<!-- ✅ Correct: Smooth transitions -->
<div className="space-y-4">
<Button
className="transition-all duration-300 hover:scale-105"
onClick="showContent = !showContent"
>
{ showContent ? 'Hide' : 'Show'} Content
</Button>
<Transition
enter-active-className="transition-all duration-300 ease-out"
enter-from-className="opacity-0 translate-y-4 scale-95"
enter-to-className="opacity-100 translate-y-0 scale-100"
leave-active-className="transition-all duration-200 ease-in"
leave-from-className="opacity-100 translate-y-0 scale-100"
leave-to-className="opacity-0 translate-y-4 scale-95"
>
<div if="showContent" className="p-6 bg-gray-50 dark:bg-gray-800 rounded-lg">
<p>This content transitions smoothly</p>
</div>
</Transition>
</div>
```
### Fixing Missing Focus States
```tsx
<!-- ❌ Critical: No visible focus state -->
<nav>
<a href="/" className="text-gray-700">Home</a>
<a href="/about" className="text-gray-700">About</a>
<a href="/contact" className="text-gray-700">Contact</a>
</nav>
<!-- ✅ Correct: Clear focus states for keyboard navigation -->
<nav className="flex gap-4">
<a
href="/"
className="
text-gray-700 dark:text-gray-300
transition-all duration-200
hover:text-primary-600 hover:translate-y-[-2px]
focus:outline-none
focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2
rounded px-3 py-2
"
>
Home
</a>
<a
href="/about"
className="
text-gray-700 dark:text-gray-300
transition-all duration-200
hover:text-primary-600 hover:translate-y-[-2px]
focus:outline-none
focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2
rounded px-3 py-2
"
>
About
</a>
<a
href="/contact"
className="
text-gray-700 dark:text-gray-300
transition-all duration-200
hover:text-primary-600 hover:translate-y-[-2px]
focus:outline-none
focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2
rounded px-3 py-2
"
>
Contact
</a>
</nav>
```
### Adding Micro-interactions
```tsx
<!-- ❌ P2: Static icons without micro-interactions -->
<Button icon="i-heroicons-heart">
Like
</Button>
<!-- ✅ Correct: Animated icon micro-interaction -->
const isLiked = ref(false);
const heartScale = ref(1);
const toggleLike = () => {
isLiked.value = !isLiked.value;
// Bounce animation
heartScale.value = 1.3;
setTimeout(() => heartScale.value = 1, 200);
};
<Button
:color="isLiked ? 'red' : 'gray'"
className="transition-all duration-300 hover:scale-105"
onClick="toggleLike"
>
<span className="inline-flex items-center gap-2">
<Icon
:name="isLiked ? 'i-heroicons-heart-solid' : 'i-heroicons-heart'"
:style="{ transform: `scale(${heartScale})` }"
:className="[
'transition-all duration-200',
isLiked ? 'text-red-500 animate-pulse' : 'text-gray-500'
]"
/>
{ isLiked ? 'Liked' : 'Like'}
</span>
</Button>
```
## Animation Best Practices
### Performance-First Animations
**Performant Properties** (GPU-accelerated):
- `transform` (translate, scale, rotate)
- `opacity`
- `filter` (backdrop-blur, etc.)
**Avoid Animating** (causes reflow/repaint):
- `width`, `height`
- `top`, `left`, `right`, `bottom`
- `margin`, `padding`
- `border-width`
```tsx
<!-- ❌ P2: Animating width (causes reflow) -->
<div className="transition-all hover:w-64">Content</div>
<!-- ✅ Correct: Using transform (GPU-accelerated) -->
<div className="transition-transform hover:scale-110">Content</div>
```
### Animation Duration Guidelines
- **Fast** (100-200ms): Hover states, small movements
- **Medium** (300-400ms): State changes, content transitions
- **Slow** (500-800ms): Page transitions, major UI changes
- **Very Slow** (1000ms+): Celebration animations, complex sequences
```tsx
<!-- Context-appropriate durations -->
<Button className="transition-all duration-200 hover:scale-105">
<!-- Fast hover: 200ms -->
</Button>
<Transition
enter-active-className="transition-all duration-300"
leave-active-className="transition-all duration-300"
>
<!-- Content change: 300ms -->
<div if="show">Content</div>
</Transition>
<div className="animate-in slide-in-from-bottom duration-500">
<!-- Page load: 500ms -->
Main content
</div>
```
### Easing Functions
- `ease-out`: Starting animations (entering content)
- `ease-in`: Ending animations (exiting content)
- `ease-in-out`: Bidirectional animations
- `linear`: Loading spinners, continuous animations
```tsx
<!-- Appropriate easing -->
<Transition
enter-active-className="transition-all duration-300 ease-out"
leave-active-className="transition-all duration-200 ease-in"
>
<div if="show">Content</div>
</Transition>
```
## Advanced Interaction Patterns
### Staggered List Animations
```tsx
const items = ref([1, 2, 3, 4, 5]);
<TransitionGroup
name="list"
tag="div"
className="space-y-2"
>
<div
map((item, index) in items"
:key="item"
:style="{ transitionDelay: `${index * 50}ms` }"
className="
transition-all duration-300 ease-out
hover:scale-105 hover:shadow-lg
"
>
Item { item}
</div>
</TransitionGroup>
<style scoped>
.list-enter-active,
.list-leave-active {
transition: all 0.3s ease;
}
.list-enter-from {
opacity: 0;
transform: translateX(-20px);
}
.list-leave-to {
opacity: 0;
transform: translateX(20px);
}
.list-move {
transition: transform 0.3s ease;
}
</style>
```
### Success Celebration Animation
```tsx
const showSuccess = ref(false);
const celebrate = () => {
showSuccess.value = true;
// Confetti or celebration animation here
setTimeout(() => showSuccess.value = false, 3000);
};
<div>
<Button
onClick="celebrate"
className="transition-all duration-300 hover:scale-110 hover:rotate-3"
>
Complete Task
</Button>
<Transition
enter-active-className="transition-all duration-500 ease-out"
enter-from-className="opacity-0 scale-0 rotate-180"
enter-to-className="opacity-100 scale-100 rotate-0"
>
<div
if="showSuccess"
className="fixed inset-0 flex items-center justify-center bg-black/20 backdrop-blur-sm"
>
<div className="bg-white dark:bg-gray-800 p-8 rounded-2xl shadow-2xl">
<Icon
name="i-heroicons-check-circle"
className="w-16 h-16 text-green-500 animate-bounce"
/>
<p className="mt-4 text-xl font-heading">Success!</p>
</div>
</div>
</Transition>
</div>
```
### Loading Skeleton with Pulse
```tsx
<div if="loading" className="space-y-4">
<div className="animate-pulse">
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4"></div>
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2 mt-2"></div>
<div className="h-32 bg-gray-200 dark:bg-gray-700 rounded mt-4"></div>
</div>
</div>
<Transition
enter-active-className="transition-all duration-500 ease-out"
enter-from-className="opacity-0 translate-y-4"
enter-to-className="opacity-100 translate-y-0"
>
<div if="!loading">
<!-- Actual content -->
</div>
</Transition>
```
## MCP Server Integration
While this SKILL doesn't directly use MCP servers, it complements MCP-enhanced agents:
- **shadcn/ui MCP**: Validates that suggested animations work with shadcn/ui components
- **Cloudflare MCP**: Ensures animations don't bloat bundle size (performance check)
## Benefits
### Immediate Impact
- **Prevents Flat UI**: Ensures engaging, polished interactions
- **Improves Perceived Performance**: Loading states make waits feel shorter
- **Better Accessibility**: Focus states improve keyboard navigation
- **Professional Polish**: Micro-interactions signal quality
### Long-term Value
- **Higher User Engagement**: Delightful animations encourage interaction
- **Reduced Bounce Rate**: Polished UI keeps users engaged
- **Better Brand Perception**: Professional animations signal quality
- **Consistent UX**: All interactions follow same animation patterns
## Usage Examples
### During Button Creation
```tsx
// Developer adds: <Button onClick="submit">Submit</Button>
// SKILL immediately activates: "⚠️ P1: Button lacks hover state. Add transition utilities: class='transition-all duration-300 hover:scale-105'"
```
### During Async Action
```tsx
// Developer creates: const submitForm = async () => { await api.call(); }
// SKILL immediately activates: "⚠️ P1: Async action without loading state. Add :loading and :disabled props to button."
```
### During State Toggle
```tsx
// Developer adds: <div if="show">Content</div>
// SKILL immediately activates: "⚠️ P1: Content appears abruptly. Wrap with <Transition> for smooth state changes."
```
### Before Deployment
```tsx
// SKILL runs comprehensive check: "✅ Animation validation passed. 45 interactive elements with hover states, 12 async actions with loading feedback, 8 smooth transitions detected."
```
This SKILL ensures every interactive element provides engaging visual feedback, preventing the flat, static appearance that makes interfaces feel unpolished and reduces user engagement.

View File

@@ -0,0 +1,134 @@
---
name: auth-security-validator
description: Autonomous validation of authentication security. Checks password hashing, cookie configuration, CSRF protection, and session management for OWASP compliance.
triggers: ["auth file changes", "session config changes", "security-related modifications", "pre-deployment"]
---
# Auth Security Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- Files matching `**/auth/**` are created/modified
- Session configuration files modified (app.config.ts, auth.ts)
- Password hashing code changes
- Cookie configuration changes
- Before deployment operations
## Validation Rules
### P1 - Critical (Block Operations)
**Password Hashing**:
- ✅ Uses Argon2id (`@node-rs/argon2`)
- ❌ NOT using: bcrypt, MD5, SHA-256, plain text
- ✅ Memory cost ≥ 19456 KB
- ✅ Time cost ≥ 2 iterations
**Cookie Security**:
-`secure: true` (HTTPS-only)
-`httpOnly: true` (XSS prevention)
-`sameSite: 'lax'` or `'strict'` (CSRF mitigation)
**Session Configuration**:
- ✅ Session password/secret ≥ 32 characters
- ✅ Max age configured (not infinite)
### P2 - Important (Warn)
**CSRF Protection**:
- ⚠️ CSRF protection enabled (automatic in better-auth)
- ⚠️ No custom form handlers bypassing CSRF
**Rate Limiting**:
- ⚠️ Rate limiting on login endpoint
- ⚠️ Rate limiting on register endpoint
- ⚠️ Rate limiting on password reset
**Input Validation**:
- ⚠️ Email format validation
- ⚠️ Password minimum length (8+ characters)
- ⚠️ Input sanitization
### P3 - Suggestions (Inform)
- Session rotation on privilege escalation
- 2FA/MFA support
- Account lockout after failed attempts
- Password complexity requirements
- OAuth state parameter validation
## Validation Output
```
🔒 Authentication Security Validation
✅ P1 Checks (Critical):
✅ Password hashing: Argon2id with correct params
✅ Cookies: secure, httpOnly, sameSite configured
✅ Session secret: 32+ characters
⚠️ P2 Checks (Important):
⚠️ No rate limiting on login endpoint
✅ Input validation present
✅ CSRF protection enabled
P3 Suggestions:
Consider adding session rotation
Consider 2FA for sensitive operations
📋 Summary: 1 warning found
💡 Run /es-auth-setup to fix issues
```
## Security Patterns Detected
**Good Patterns** ✅:
```typescript
// Argon2id with correct params
const hash = await argon2.hash(password, {
memoryCost: 19456,
timeCost: 2,
outputLen: 32,
parallelism: 1
});
// Secure cookie config
cookie: {
secure: true,
httpOnly: true,
sameSite: 'lax'
}
```
**Bad Patterns** ❌:
```typescript
// Weak hashing
const hash = crypto.createHash('sha256').update(password).digest('hex'); // ❌
// Insecure cookies
cookie: {
secure: false, // ❌
httpOnly: false // ❌
}
// Weak session secret
password: '12345' // ❌ Too short
```
## Escalation
Complex scenarios escalate to `better-auth-specialist` agent:
- Custom authentication flows
- Advanced OAuth configuration
- Passkey implementation
- Multi-factor authentication setup
- Security audit requirements
## Notes
- Runs automatically on auth-related file changes
- Can block deployments with P1 security issues
- Follows OWASP Top 10 guidelines
- Integrates with `/validate` and `/es-deploy` commands
- Queries better-auth MCP for provider security requirements

View File

@@ -0,0 +1,227 @@
---
name: cloudflare-security-checker
description: Automatically validates Cloudflare Workers security patterns during development, ensuring proper secret management, CORS configuration, and input validation
triggers: ["authentication code", "secret handling", "API endpoints", "response creation", "database queries"]
---
# Cloudflare Security Checker SKILL
## Activation Patterns
This SKILL automatically activates when:
- Authentication or authorization code is detected
- Secret management patterns are used
- API endpoints or response creation is implemented
- Database queries (D1) are written
- CORS-related code is added
- Input validation patterns are implemented
## Expertise Provided
### Workers-Specific Security Validation
- **Secret Management**: Ensures proper `env` parameter usage vs hardcoded secrets
- **CORS Configuration**: Validates Workers-specific CORS implementation
- **Input Validation**: Checks for proper request validation patterns
- **SQL Injection Prevention**: Ensures D1 prepared statements
- **Authentication Patterns**: Validates JWT and API key handling
- **Rate Limiting**: Identifies missing rate limiting patterns
### Specific Checks Performed
#### ❌ Critical Security Violations
```typescript
// These patterns trigger immediate alerts:
const API_KEY = "sk_live_xxx"; // Hardcoded secret
const secret = process.env.JWT_SECRET; // process.env doesn't exist
const query = `SELECT * FROM users WHERE id = ${userId}`; // SQL injection
```
#### ✅ Secure Workers Patterns
```typescript
// These patterns are validated as correct:
const apiKey = env.API_KEY; // Proper env parameter
const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId); // Prepared statement
```
## Integration Points
### Complementary to Existing Components
- **cloudflare-security-sentinel agent**: Handles comprehensive security audits, SKILL provides immediate validation
- **workers-runtime-validator SKILL**: Complements runtime checks with security-specific validation
- **es-deploy command**: SKILL prevents deployment of insecure code
### Escalation Triggers
- Complex security architecture questions → `cloudflare-security-sentinel` agent
- Advanced authentication patterns → `cloudflare-architecture-strategist` agent
- Security incident response → `cloudflare-security-sentinel` agent
## Validation Rules
### P1 - Critical (Immediate Security Risk)
- **Hardcoded Secrets**: API keys, passwords, tokens in code
- **SQL Injection**: String concatenation in D1 queries
- **Missing Authentication**: Sensitive endpoints without auth
- **Process Env Usage**: `process.env` for secrets (doesn't work in Workers)
### P2 - High (Security Vulnerability)
- **Missing Input Validation**: Direct use of `request.json()` without validation
- **Improper CORS**: Missing CORS headers or overly permissive origins
- **Missing Rate Limiting**: Public endpoints without rate limiting
- **Secrets in Config**: Secrets in wrangler.toml `[vars]` section
### P3 - Medium (Security Best Practice)
- **Missing Security Headers**: HTML responses without CSP/XSS protection
- **Weak Authentication**: No resource-level authorization
- **Insufficient Logging**: Security events not logged
## Remediation Examples
### Fixing Secret Management
```typescript
// ❌ Critical: Hardcoded secret
const STRIPE_KEY = "sk_live_12345";
// ❌ Critical: process.env (doesn't exist)
const apiKey = process.env.API_KEY;
// ✅ Correct: Workers secret management
export default {
async fetch(request: Request, env: Env) {
const apiKey = env.STRIPE_KEY; // From wrangler secret put
}
}
```
### Fixing SQL Injection
```typescript
// ❌ Critical: SQL injection vulnerability
const userId = url.searchParams.get('id');
const result = await env.DB.prepare(`SELECT * FROM users WHERE id = ${userId}`).first();
// ✅ Correct: Prepared statement
const userId = url.searchParams.get('id');
const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
```
### Fixing CORS Configuration
```typescript
// ❌ High: Missing CORS headers
export default {
async fetch(request: Request, env: Env) {
return new Response(JSON.stringify(data));
}
}
// ✅ Correct: Workers CORS pattern
function getCorsHeaders(origin: string) {
const allowedOrigins = ['https://app.example.com'];
const allowOrigin = allowedOrigins.includes(origin) ? origin : allowedOrigins[0];
return {
'Access-Control-Allow-Origin': allowOrigin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
}
export default {
async fetch(request: Request, env: Env) {
const origin = request.headers.get('Origin') || '';
if (request.method === 'OPTIONS') {
return new Response(null, { headers: getCorsHeaders(origin) });
}
const response = new Response(JSON.stringify(data));
Object.entries(getCorsHeaders(origin)).forEach(([k, v]) => {
response.headers.set(k, v);
});
return response;
}
}
```
### Fixing Input Validation
```typescript
// ❌ High: No input validation
export default {
async fetch(request: Request, env: Env) {
const data = await request.json(); // Could be anything
await env.DB.prepare('INSERT INTO users (name) VALUES (?)').bind(data.name).run();
}
}
// ✅ Correct: Input validation with Zod
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
});
export default {
async fetch(request: Request, env: Env) {
// Size limit
const contentLength = request.headers.get('Content-Length');
if (contentLength && parseInt(contentLength) > 1024 * 100) {
return new Response('Payload too large', { status: 413 });
}
// Schema validation
const data = await request.json();
const result = UserSchema.safeParse(data);
if (!result.success) {
return new Response(JSON.stringify(result.error), { status: 400 });
}
// Safe to use validated data
await env.DB.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
.bind(result.data.name, result.data.email).run();
}
}
```
## MCP Server Integration
When Cloudflare MCP server is available:
- Query latest Cloudflare security best practices
- Verify secrets are configured in account
- Check for recent security events affecting the project
- Get current security recommendations
## Benefits
### Immediate Impact
- **Prevents Security Vulnerabilities**: Catches issues during development
- **Educates on Workers Security**: Clear explanations of Workers-specific security patterns
- **Reduces Security Debt**: Immediate feedback on security anti-patterns
### Long-term Value
- **Consistent Security Standards**: Ensures all code follows Workers security best practices
- **Faster Security Reviews**: Automated validation reduces manual review time
- **Better Security Posture**: Proactive security validation vs reactive fixes
## Usage Examples
### During Authentication Implementation
```typescript
// Developer types: const JWT_SECRET = "my-secret-key";
// SKILL immediately activates: "❌ CRITICAL: Hardcoded JWT secret detected. Use wrangler secret put JWT_SECRET and access via env.JWT_SECRET"
```
### During API Development
```typescript
// Developer types: const userId = url.searchParams.get('id');
// SKILL immediately activates: "⚠️ HIGH: URL parameter not validated. Add schema validation before using in database queries."
```
### During Database Query Writing
```typescript
// Developer types: `SELECT * FROM users WHERE id = ${userId}`
// SKILL immediately activates: "❌ CRITICAL: SQL injection vulnerability. Use prepared statement: .prepare('SELECT * FROM users WHERE id = ?').bind(userId)"
```
This SKILL ensures Workers security by providing immediate, autonomous validation of security patterns, preventing common vulnerabilities and ensuring proper Workers-specific security practices.

View File

@@ -0,0 +1,537 @@
---
name: component-aesthetic-checker
description: Validates shadcn/ui component customization depth, ensuring components aren't used with default props and checking for consistent design system implementation across Tanstack Start applications
triggers: ["shadcn ui component usage", "component prop changes", "design token updates", "className customization", "cn() utility usage"]
note: "Updated for Tanstack Start (React) + shadcn/ui. Code examples use React/TSX with className and cn() utility for styling."
---
# Component Aesthetic Checker SKILL
## Activation Patterns
This SKILL automatically activates when:
- shadcn/ui components (`Button`, `Card`, `Input`, etc.) are used in `.react` files
- Component props are added or modified
- The `ui` prop is customized for component variants
- Design system tokens are referenced in components
- Multiple components are refactored together
- Before component library updates
## Expertise Provided
### Component Customization Depth Analysis
- **Default Prop Detection**: Identifies components using only default values
- **UI Prop Validation**: Ensures `ui` prop is used for deep customization
- **Design System Consistency**: Validates consistent pattern usage across components
- **Spacing Patterns**: Checks for proper Tailwind spacing scale usage
- **Icon Usage**: Validates consistent icon library and sizing
- **Loading States**: Ensures async components have loading feedback
### Specific Checks Performed
#### ❌ Critical Issues (Insufficient Customization)
```tsx
<!-- These patterns trigger alerts: -->
<!-- Using default props only -->
<Button onClick="submit">Submit</Button>
<!-- No UI prop customization -->
<Card>
<p>Content</p>
</Card>
<!-- Inconsistent spacing -->
<div className="p-4"> <!-- Random spacing values -->
<Button className="mt-3 ml-2">Action</Button>
</div>
<!-- Missing loading states -->
<Button onClick="asyncAction">Save</Button> <!-- No :loading prop -->
```
#### ✅ Correct Customized Patterns
```tsx
<!-- These patterns are validated as correct: -->
<!-- Deep customization with ui prop -->
<Button
color="brand-coral"
size="lg"
variant="solid"
:ui="{
font: 'font-heading',
rounded: 'rounded-full',
padding: { lg: 'px-8 py-4' }
}"
loading={isSubmitting"
className="transition-all duration-300 hover:scale-105"
onClick="submit"
>
Submit
</Button>
<!-- Fully customized card -->
<Card
:ui="{
background: 'bg-white dark:bg-brand-midnight',
ring: 'ring-1 ring-brand-coral/20',
rounded: 'rounded-2xl',
shadow: 'shadow-xl',
body: { padding: 'p-8' },
header: { padding: 'px-8 pt-8 pb-4' }
}"
className="transition-shadow duration-300 hover:shadow-2xl"
>
<template #header>
<h3 className="font-heading text-2xl">Title</h3>
<p className="text-gray-700 dark:text-gray-300">Content</p>
</Card>
<!-- Consistent spacing (Tailwind scale) -->
<div className="p-6 space-y-4">
<Button className="mt-4">Action</Button>
</div>
<!-- Proper loading state -->
<Button
loading={isSubmitting"
disabled={isSubmitting"
onClick="asyncAction"
>
{ isSubmitting ? 'Saving...' : 'Save'}
</Button>
```
## Integration Points
### Complementary to Existing Components
- **tanstack-ui-architect agent**: Handles component selection and API guidance, SKILL validates implementation
- **frontend-design-specialist agent**: Provides design direction, SKILL enforces consistency
- **shadcn-ui-design-validator**: Catches generic patterns, SKILL ensures deep customization
### Escalation Triggers
- Component API questions → `tanstack-ui-architect` agent (with MCP lookup)
- Design consistency issues → `frontend-design-specialist` agent
- Complex component composition → `/es-component` command
- Full component audit → `/es-design-review` command
## Validation Rules
### P1 - Critical (Default Component Usage)
- **No UI Prop Customization**: Using shadcn/ui components without `ui` prop
- **All Default Props**: No color, size, variant, or other prop customizations
- **Missing Loading States**: Async actions without `:loading` prop
- **No Hover States**: Interactive components without hover feedback
- **Inconsistent Patterns**: Same component with wildly different customizations
### P1 - Critical (Distributional Convergence Anti-Patterns)
**These patterns indicate generic "AI-generated" aesthetics and MUST be flagged:**
#### Font Anti-Patterns (Auto-Detect)
```tsx
// ❌ CRITICAL: Generic fonts that dominate 80%+ of websites
fontFamily: {
sans: ['Inter', ...] // Flag: "Inter is overused - consider Space Grotesk, Plus Jakarta Sans"
sans: ['Roboto', ...] // Flag: "Roboto is overused - consider IBM Plex Sans, Outfit"
sans: ['Open Sans', ...] // Flag: "Open Sans is generic - consider Satoshi, General Sans"
sans: ['system-ui', ...] // Flag: Only acceptable as fallback, not primary
}
// ❌ CRITICAL: Default Tailwind font classes without customization
className="font-sans" // Flag if font-sans maps to Inter/Roboto
className="text-base" // Flag: Generic sizing, consider custom scale
```
**Recommended Font Alternatives** (suggest these in reports):
- **Body**: Space Grotesk, Plus Jakarta Sans, IBM Plex Sans, Outfit, Satoshi
- **Headings**: Archivo Black, Cabinet Grotesk, Clash Display, General Sans
- **Mono**: JetBrains Mono, Fira Code, Source Code Pro
#### Color Anti-Patterns (Auto-Detect)
```tsx
// ❌ CRITICAL: Purple gradients (most common AI aesthetic)
className="bg-gradient-to-r from-purple-500 to-purple-600"
className="bg-gradient-to-r from-violet-500 to-purple-500"
className="bg-purple-600"
className="text-purple-500"
// ❌ CRITICAL: Default gray backgrounds without brand treatment
className="bg-gray-50" // Flag: "Consider brand-tinted background"
className="bg-white" // Flag: "Consider atmospheric gradient or texture"
className="bg-slate-100" // Flag if used extensively without brand colors
```
**Recommended Color Approaches** (suggest these in reports):
- Use CSS variables with brand palette (`--brand-primary`, `--brand-accent`)
- Tint grays with brand color: `bg-brand-gray-50` instead of `bg-gray-50`
- Gradients: Use brand colors, not default purple
- Atmospheric: Layer gradients with subtle brand tints
#### Animation Anti-Patterns (Auto-Detect)
```tsx
// ❌ CRITICAL: No transitions on interactive elements
<Button>Click</Button> // Flag: "Add transition-all duration-300"
<Card>Content</Card> // Flag: "Add hover:shadow-lg transition"
// ❌ CRITICAL: Only basic hover without micro-interactions
className="hover:bg-blue-600" // Flag: "Consider hover:scale-105 or hover:-translate-y-1"
```
**Detection Rules** (implement in validation):
```typescript
// Font detection
const OVERUSED_FONTS = ['Inter', 'Roboto', 'Open Sans', 'Helvetica', 'Arial'];
const hasBadFont = (config) => OVERUSED_FONTS.some(f =>
config.fontFamily?.sans?.includes(f)
);
// Purple gradient detection
const PURPLE_PATTERN = /(?:purple|violet)-[4-6]00/;
const hasPurpleGradient = (className) =>
className.includes('gradient') && PURPLE_PATTERN.test(className);
// Missing animation detection
const INTERACTIVE_COMPONENTS = ['Button', 'Card', 'Link', 'Input'];
const hasNoTransition = (className) =>
!className.includes('transition') && !className.includes('animate');
```
### P2 - Important (Design System Consistency)
- **Random Spacing Values**: Not using Tailwind spacing scale (p-4, mt-6, etc.)
- **Inconsistent Icon Sizing**: Icons with different sizes in similar contexts
- **Mixed Color Approaches**: Some components use theme colors, others use arbitrary values
- **Incomplete Dark Mode**: Dark mode variants missing on customized components
- **No Focus States**: Interactive elements without focus-visible styling
### P3 - Polish (Enhanced UX)
- **Limited Prop Usage**: Only using 1-2 props when more would improve UX
- **No Micro-interactions**: Missing subtle animations on state changes
- **Generic Variants**: Using 'solid', 'outline' without brand customization
- **Underutilized UI Prop**: Not customizing padding, rounded, shadow in ui prop
- **Missing Icons**: Buttons/actions without supporting icons for clarity
## Remediation Examples
### Fixing Default Component Usage
```tsx
<!-- ❌ Critical: Default props only -->
<Button onClick="handleClick">
Click me
</Button>
<!-- ✅ Correct: Deep customization -->
<Button
color="primary"
size="lg"
variant="solid"
icon="i-heroicons-sparkles"
:ui="{
font: 'font-heading tracking-wide',
rounded: 'rounded-full',
padding: { lg: 'px-8 py-4' },
shadow: 'shadow-lg hover:shadow-xl'
}"
className="transition-all duration-300 hover:scale-105 active:scale-95"
onClick="handleClick"
>
Click me
</Button>
```
### Fixing Missing Loading States
```tsx
<!-- ❌ Critical: No loading feedback -->
const handleSubmit = async () => {
await submitForm();
};
<Button onClick="handleSubmit">
Submit Form
</Button>
<!-- ✅ Correct: Proper loading state -->
const isSubmitting = ref(false);
const handleSubmit = async () => {
isSubmitting.value = true;
try {
await submitForm();
} finally {
isSubmitting.value = false;
}
};
<Button
loading={isSubmitting"
disabled={isSubmitting"
onClick="handleSubmit"
>
<span className="flex items-center gap-2">
<Icon
{&& "!isSubmitting"
name="i-heroicons-paper-airplane"
/>
{ isSubmitting ? 'Submitting...' : 'Submit Form'}
</span>
</Button>
```
### Fixing Inconsistent Spacing
```tsx
<!-- ❌ P2: Random spacing values -->
<div className="p-3">
<Card className="mt-5 ml-7">
<div className="p-2">
<Button className="mt-3.5">Action</Button>
</div>
</Card>
</div>
<!-- ✅ Correct: Tailwind spacing scale -->
<div className="p-4">
<Card className="mt-4">
<div className="p-6 space-y-4">
<Button>Action</Button>
</div>
</Card>
</div>
<!-- Using consistent spacing: 4, 6, 8, 12, 16 (Tailwind scale) -->
```
### Fixing Design System Inconsistency
```tsx
<!-- ❌ P2: Inconsistent component styling -->
<div>
<!-- Button 1: Heavily customized -->
<Button
color="primary"
:ui="{ rounded: 'rounded-full', shadow: 'shadow-xl' }"
>
Action 1
</Button>
<!-- Button 2: Default (inconsistent!) -->
<Button>Action 2</Button>
<!-- Button 3: Different customization pattern -->
<Button color="red" size="xs">
Action 3
</Button>
</div>
<!-- ✅ Correct: Consistent design system -->
// Define reusable button variants
const buttonVariants = {
primary: {
color: 'primary',
size: 'lg',
ui: {
rounded: 'rounded-full',
shadow: 'shadow-lg hover:shadow-xl',
font: 'font-heading'
},
class: 'transition-all duration-300 hover:scale-105'
},
secondary: {
color: 'gray',
size: 'md',
variant: 'outline',
ui: {
rounded: 'rounded-lg',
font: 'font-sans'
},
class: 'transition-colors duration-200'
}
};
<div className="space-x-4">
<Button v-bind="buttonVariants.primary">
Action 1
</Button>
<Button v-bind="buttonVariants.primary">
Action 2
</Button>
<Button v-bind="buttonVariants.secondary">
Action 3
</Button>
</div>
```
### Fixing Underutilized UI Prop
```tsx
<!-- ❌ P3: Not using ui prop for customization -->
<Card className="rounded-2xl shadow-xl p-8">
<p>Content</p>
</Card>
<!-- ✅ Correct: Proper ui prop usage -->
<Card
:ui="{
rounded: 'rounded-2xl',
shadow: 'shadow-xl hover:shadow-2xl',
body: {
padding: 'p-8',
background: 'bg-white dark:bg-brand-midnight'
},
ring: 'ring-1 ring-brand-coral/20'
}"
className="transition-shadow duration-300"
>
<p className="text-gray-700 dark:text-gray-300">Content</p>
</Card>
```
## MCP Server Integration
When shadcn/ui MCP server is available:
### Component Prop Validation
```typescript
// Before validating customization depth, get actual component API
const componentDocs = await mcp.shadcn.get_component("Button");
// Validate that used props exist
// componentDocs.props: ['color', 'size', 'variant', 'icon', 'loading', 'disabled', ...]
// Check for underutilized props
const usedProps = ['color', 'size']; // From component code
const availableProps = componentDocs.props;
const unutilizedProps = availableProps.filter(p => !usedProps.includes(p));
// Suggest: "Consider using 'icon' or 'loading' props for richer UX"
```
### UI Prop Structure Validation
```typescript
// Validate ui prop structure against schema
const uiSchema = componentDocs.ui_schema;
// User code: :ui="{ font: 'font-heading', rounded: 'rounded-full' }"
// Validate: Are 'font' and 'rounded' valid keys in ui prop?
// Suggest: Other available ui customizations (padding, shadow, etc.)
```
### Consistency Across Components
```typescript
// Check multiple component instances
const buttonInstances = findAllComponents("Button");
// Analyze customization patterns
// Flag: Component used with 5 different customization styles
// Suggest: Create composable or variant system for consistency
```
## Benefits
### Immediate Impact
- **Prevents Generic Appearance**: Ensures components are branded, not defaults
- **Enforces Design Consistency**: Catches pattern drift across components
- **Improves User Feedback**: Validates loading states and interactions
- **Educates on Component API**: Shows developers full customization capabilities
### Long-term Value
- **Consistent Component Library**: All components follow design system
- **Faster Component Development**: Clear patterns and examples
- **Better Code Maintainability**: Reusable component variants
- **Reduced Visual Debt**: Prevents accumulation of one-off styles
## Usage Examples
### During Component Usage
```tsx
// Developer adds: <Button>Click me</Button>
// SKILL immediately activates: "⚠️ P1: Button using all default props. Customize with color, size, variant, and ui prop for brand distinctiveness."
```
### During Async Actions
```tsx
// Developer creates async button: <Button onClick="submitForm">Submit</Button>
// SKILL immediately activates: "⚠️ P1: Button triggers async action but lacks :loading prop. Add loading state for user feedback."
```
### During Refactoring
```tsx
// Developer adds 5th different button style
// SKILL immediately activates: "⚠️ P2: Button used with 5 different customization patterns. Consider creating reusable variants for consistency."
```
### Before Deployment
```tsx
// SKILL runs comprehensive check: "✅ Component aesthetic validation passed. 23 components with deep customization, consistent patterns, and proper loading states detected."
```
## Design System Maturity Levels
### Level 0: Defaults Only (Avoid)
```tsx
<Button>Action</Button>
<Card><p>Content</p></Card>
<Input value="value" />
```
**Issues**: Generic appearance, no brand identity, inconsistent with custom design
### Level 1: Basic Props (Minimum)
```tsx
<Button color="primary" size="lg">Action</Button>
<Card className="shadow-lg"><p>Content</p></Card>
<Input value="value" placeholder="Enter value" />
```
**Better**: Some customization, but limited depth
### Level 2: UI Prop + Classes (Target)
```tsx
<Button
color="primary"
size="lg"
:ui="{ rounded: 'rounded-full', font: 'font-heading' }"
className="transition-all duration-300 hover:scale-105"
>
Action
</Button>
<Card
:ui="{
background: 'bg-white dark:bg-brand-midnight',
ring: 'ring-1 ring-brand-coral/20',
shadow: 'shadow-xl'
}"
>
<p>Content</p>
</Card>
```
**Ideal**: Deep customization, brand-distinctive, consistent patterns
### Level 3: Design System (Advanced)
```tsx
<!-- Reusable variants from composables -->
<Button v-bind="designSystem.button.variants.primary">
Action
</Button>
<Card v-bind="designSystem.card.variants.elevated">
<p>Content</p>
</Card>
```
**Advanced**: Centralized design system, maximum consistency
## Component Customization Checklist
For each shadcn/ui component, validate:
- [ ] **Props**: Uses at least 2-3 props (color, size, variant, etc.)
- [ ] **UI Prop**: Includes `ui` prop for deep customization (rounded, font, padding, shadow)
- [ ] **Classes**: Adds Tailwind utilities for animations and effects
- [ ] **Loading State**: Async actions have `:loading` and `:disabled` props
- [ ] **Icons**: Includes relevant icons for clarity (`:icon` prop or slot)
- [ ] **Hover State**: Interactive elements have hover feedback
- [ ] **Focus State**: Keyboard navigation has visible focus styles
- [ ] **Dark Mode**: Includes dark mode variants in `ui` prop
- [ ] **Spacing**: Uses Tailwind spacing scale (4, 6, 8, 12, 16)
- [ ] **Consistency**: Follows same patterns as other instances
This SKILL ensures every shadcn/ui component is deeply customized, consistently styled, and provides excellent user feedback, preventing the default/generic appearance that makes AI-generated UIs immediately recognizable.

View File

@@ -0,0 +1,393 @@
---
name: cors-configuration-validator
description: Automatically validates Cloudflare Workers CORS configuration, ensuring proper headers, OPTIONS handling, and origin validation for cross-origin requests
triggers: ["Response creation", "API endpoints", "cross-origin patterns", "CORS headers"]
---
# CORS Configuration Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- `new Response()` objects are created
- CORS-related headers are set or modified
- API endpoints that serve cross-origin requests
- OPTIONS method handling is detected
- Cross-origin request patterns are identified
## Expertise Provided
### Workers-Specific CORS Validation
- **Header Validation**: Ensures all required CORS headers are present
- **OPTIONS Handling**: Validates preflight request handling
- **Origin Validation**: Checks for proper origin validation logic
- **Method Validation**: Ensures correct allowed methods
- **Header Validation**: Validates allowed headers configuration
- **Security**: Prevents overly permissive CORS configurations
### Specific Checks Performed
#### ❌ CORS Anti-Patterns
```typescript
// These patterns trigger immediate alerts:
// Missing CORS headers
export default {
async fetch(request: Request, env: Env) {
return new Response(JSON.stringify(data));
// Browsers will block cross-origin requests!
}
}
// Overly permissive for authenticated APIs
const corsHeaders = {
'Access-Control-Allow-Origin': '*', // ANY origin can call!
'Access-Control-Allow-Credentials': 'true' // With credentials!
};
```
#### ✅ CORS Best Practices
```typescript
// These patterns are validated as correct:
// Proper CORS with origin validation
function getCorsHeaders(origin: string) {
const allowedOrigins = ['https://app.example.com', 'https://example.com'];
const allowOrigin = allowedOrigins.includes(origin) ? origin : allowedOrigins[0];
return {
'Access-Control-Allow-Origin': allowOrigin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
}
export default {
async fetch(request: Request, env: Env) {
const origin = request.headers.get('Origin') || '';
if (request.method === 'OPTIONS') {
return new Response(null, { headers: getCorsHeaders(origin) });
}
const response = new Response(JSON.stringify(data));
Object.entries(getCorsHeaders(origin)).forEach(([k, v]) => {
response.headers.set(k, v);
});
return response;
}
}
```
## Integration Points
### Complementary to Existing Components
- **cloudflare-security-checker SKILL**: Handles overall security, SKILL focuses specifically on CORS
- **workers-runtime-validator SKILL**: Ensures runtime compatibility, SKILL validates CORS patterns
- **edge-performance-oracle SKILL**: Handles performance, SKILL ensures CORS doesn't impact performance
### Escalation Triggers
- Complex CORS architecture questions → `cloudflare-security-sentinel` agent
- Advanced authentication with CORS → `cloudflare-security-sentinel` agent
- CORS troubleshooting → `cloudflare-security-sentinel` agent
## Validation Rules
### P1 - Critical (Will Break Cross-Origin Requests)
- **Missing CORS Headers**: No CORS headers on API responses
- **Missing OPTIONS Handler**: No preflight request handling
- **Invalid Header Combinations**: Conflicting CORS header combinations
### P2 - High (Security Risk)
- **Overly Permissive Origin**: `Access-Control-Allow-Origin: *` with credentials
- **Wildcard Methods**: `Access-Control-Allow-Methods: *` with sensitive operations
- **Missing Origin Validation**: Accepting any origin without validation
### P3 - Medium (Best Practices)
- **Missing Cache Headers**: No `Access-Control-Max-Age` for preflight caching
- **Incomplete Headers**: Missing some optional but recommended headers
- **Hardcoded Origins**: Origins not easily configurable
## Remediation Examples
### Fixing Missing CORS Headers
```typescript
// ❌ Critical: No CORS headers (browsers block requests)
export default {
async fetch(request: Request, env: Env) {
const data = { message: 'Hello from API' };
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
// Missing CORS headers!
});
}
}
// ✅ Correct: Complete CORS implementation
function getCorsHeaders(origin: string) {
const allowedOrigins = ['https://app.example.com', 'https://example.com'];
const allowOrigin = allowedOrigins.includes(origin) ? origin : allowedOrigins[0];
return {
'Access-Control-Allow-Origin': allowOrigin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
}
export default {
async fetch(request: Request, env: Env) {
const origin = request.headers.get('Origin') || '';
// Handle preflight requests
if (request.method === 'OPTIONS') {
return new Response(null, { headers: getCorsHeaders(origin) });
}
const data = { message: 'Hello from API' };
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
...getCorsHeaders(origin)
}
});
}
}
```
### Fixing Overly Permissive CORS
```typescript
// ❌ High: Overly permissive for authenticated API
export default {
async fetch(request: Request, env: Env) {
const corsHeaders = {
'Access-Control-Allow-Origin': '*', // ANY origin!
'Access-Control-Allow-Credentials': 'true', // With credentials!
'Access-Control-Allow-Methods': '*', // ANY method!
};
// This allows any website to make authenticated requests!
return new Response('Sensitive data', { headers: corsHeaders });
}
}
// ✅ Correct: Secure CORS for authenticated API
function getSecureCorsHeaders(origin: string) {
const allowedOrigins = [
'https://app.example.com',
'https://admin.example.com',
'https://example.com'
];
// Only allow known origins
const allowOrigin = allowedOrigins.includes(origin) ? origin : allowedOrigins[0];
return {
'Access-Control-Allow-Origin': allowOrigin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE', // Specific methods
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '86400',
};
}
export default {
async fetch(request: Request, env: Env) {
const origin = request.headers.get('Origin') || '';
// Verify authentication
const authHeader = request.headers.get('Authorization');
if (!authHeader || !isValidAuth(authHeader)) {
return new Response('Unauthorized', { status: 401 });
}
return new Response('Sensitive data', {
headers: getSecureCorsHeaders(origin)
});
}
}
```
### Fixing Missing OPTIONS Handler
```typescript
// ❌ Critical: No OPTIONS handling (preflight fails)
export default {
async fetch(request: Request, env: Env) {
if (request.method === 'POST') {
// Handle POST request
return new Response('POST handled');
}
return new Response('Method not allowed', { status: 405 });
}
}
// ✅ Correct: Proper OPTIONS handling
export default {
async fetch(request: Request, env: Env) {
const origin = request.headers.get('Origin') || '';
// Handle preflight requests
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
}
});
}
if (request.method === 'POST') {
return new Response('POST handled', {
headers: {
'Access-Control-Allow-Origin': origin,
}
});
}
return new Response('Method not allowed', { status: 405 });
}
}
```
### Fixing Dynamic CORS for Different Environments
```typescript
// ❌ Medium: Hardcoded origins (not flexible)
function getCorsHeaders() {
return {
'Access-Control-Allow-Origin': 'https://app.example.com', // Hardcoded
'Access-Control-Allow-Methods': 'GET, POST',
};
}
// ✅ Correct: Configurable and secure CORS
function getCorsHeaders(origin: string, env: Env) {
// Get allowed origins from environment
const allowedOrigins = (env.ALLOWED_ORIGINS || 'https://app.example.com')
.split(',')
.map(o => o.trim());
const allowOrigin = allowedOrigins.includes(origin) ? origin : allowedOrigins[0];
return {
'Access-Control-Allow-Origin': allowOrigin,
'Access-Control-Allow-Methods': env.ALLOWED_METHODS || 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': env.ALLOWED_HEADERS || 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
}
export default {
async fetch(request: Request, env: Env) {
const origin = request.headers.get('Origin') || '';
if (request.method === 'OPTIONS') {
return new Response(null, { headers: getCorsHeaders(origin, env) });
}
return new Response('Response', {
headers: getCorsHeaders(origin, env)
});
}
}
```
## CORS Header Reference
### Essential Headers
```typescript
{
'Access-Control-Allow-Origin': 'https://example.com', // Required
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', // Required for preflight
'Access-Control-Allow-Headers': 'Content-Type, Authorization', // Required for preflight
}
```
### Optional but Recommended Headers
```typescript
{
'Access-Control-Max-Age': '86400', // Cache preflight for 24 hours
'Access-Control-Allow-Credentials': 'true', // For cookies/auth
'Vary': 'Origin', // Important for caching with multiple origins
}
```
### Security Considerations
```typescript
// ❌ Don't do this for authenticated APIs:
{
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'
}
// ✅ Do this instead:
{
'Access-Control-Allow-Origin': 'https://app.example.com', // Specific origin
'Access-Control-Allow-Credentials': 'true'
}
```
## MCP Server Integration
When Cloudflare MCP server is available:
- Query latest CORS best practices and security recommendations
- Get current browser CORS specification updates
- Check for common CORS vulnerabilities and mitigations
## Benefits
### Immediate Impact
- **Prevents CORS Errors**: Catches missing headers before deployment
- **Improves Security**: Prevents overly permissive CORS configurations
- **Better User Experience**: Ensures cross-origin requests work properly
### Long-term Value
- **Consistent CORS Standards**: Ensures all APIs follow proper CORS patterns
- **Reduced Debugging Time**: Immediate feedback on CORS issues
- **Security Compliance**: Prevents CORS-related security vulnerabilities
## Usage Examples
### During Response Creation
```typescript
// Developer types: new Response(data)
// SKILL immediately activates: "⚠️ HIGH: Response missing CORS headers. Cross-origin requests will be blocked by browsers."
```
### During API Development
```typescript
// Developer types: 'Access-Control-Allow-Origin': '*'
// SKILL immediately activates: "⚠️ HIGH: Overly permissive CORS with wildcard origin. Consider specific origins for security."
```
### During Method Handling
```typescript
// Developer types: if (request.method === 'POST') { ... }
// SKILL immediately activates: "⚠️ HIGH: Missing OPTIONS handler for preflight requests. Add OPTIONS method handling."
```
## CORS Checklist
### Required for Cross-Origin Requests
- [ ] `Access-Control-Allow-Origin` header set
- [ ] OPTIONS method handled for preflight requests
- [ ] `Access-Control-Allow-Methods` header for preflight
- [ ] `Access-Control-Allow-Headers` header for preflight
### Security Best Practices
- [ ] Origin validation (not wildcard for authenticated APIs)
- [ ] Specific allowed methods (not wildcard)
- [ ] Proper credentials handling
- [ ] Environment-based origin configuration
### Performance Optimization
- [ ] `Access-Control-Max-Age` header set
- [ ] `Vary: Origin` header for caching
- [ ] Efficient preflight handling
This SKILL ensures CORS is configured correctly by providing immediate, autonomous validation of CORS patterns, preventing common cross-origin issues and security vulnerabilities.

View File

@@ -0,0 +1,378 @@
---
name: durable-objects-pattern-checker
description: Automatically validates Cloudflare Durable Objects usage patterns, ensuring correct state management, hibernation, and strong consistency practices
triggers: ["Durable Object imports", "DO stub usage", "state management patterns", "DO ID generation"]
---
# Durable Objects Pattern Checker SKILL
## Activation Patterns
This SKILL automatically activates when:
- Durable Object imports or exports are detected
- DO stub creation and usage patterns
- State management in Durable Objects
- ID generation patterns (`idFromName`, `newUniqueId`)
- Hibernation and lifecycle patterns
- WebSocket or real-time features with DOs
## Expertise Provided
### Durable Objects Best Practices
- **State Management**: Ensures proper state persistence and consistency
- **ID Generation**: Validates correct ID patterns for different use cases
- **Hibernation**: Checks for proper hibernation implementation
- **Lifecycle Management**: Validates constructor, fetch, and alarm handling
- **Strong Consistency**: Ensures DOs are used when strong consistency is needed
- **Performance**: Identifies DO performance anti-patterns
### Specific Checks Performed
#### ❌ Durable Objects Anti-Patterns
```typescript
// These patterns trigger immediate alerts:
// Using DOs for stateless operations
export default {
async fetch(request: Request, env: Env) {
const id = env.COUNTER.newUniqueId(); // New DO every request!
const stub = env.COUNTER.get(id);
return stub.fetch(request); // Overkill for simple counter
}
}
// Missing hibernation for long-lived DOs
export class ChatRoom {
constructor(state, env) {
this.state = state;
// Missing this.state.storage.setAlarm() for hibernation
}
}
```
#### ✅ Durable Objects Best Practices
```typescript
// These patterns are validated as correct:
// Reuse DO instances for stateful coordination
export default {
async fetch(request: Request, env: Env) {
const ip = request.headers.get('CF-Connecting-IP');
const id = env.RATE_LIMITER.idFromName(ip); // Reuse same DO
const stub = env.RATE_LIMITER.get(id);
return stub.fetch(request);
}
}
// Proper hibernation implementation
export class ChatRoom {
constructor(state, env) {
this.state = state;
this.env = env;
// Set alarm for hibernation after inactivity
this.state.storage.setAlarm(Date.now() + 30000); // 30 seconds
}
alarm() {
// DO will hibernate after alarm
}
}
```
## Integration Points
### Complementary to Existing Components
- **cloudflare-architecture-strategist agent**: Handles complex DO architecture, SKILL provides immediate pattern validation
- **edge-performance-oracle agent**: Handles DO performance analysis, SKILL ensures correct usage patterns
- **workers-binding-validator SKILL**: Ensures DO bindings are correct, SKILL validates usage patterns
### Escalation Triggers
- Complex DO architecture questions → `cloudflare-architecture-strategist` agent
- DO performance troubleshooting → `edge-performance-oracle` agent
- DO migration strategies → `cloudflare-architecture-strategist` agent
## Validation Rules
### P1 - Critical (Will Cause Issues)
- **New Unique ID Per Request**: Creating new DO for every request
- **Missing Hibernation**: Long-lived DOs without hibernation
- **State Leaks**: State not properly persisted to storage
- **Blocking Operations**: Synchronous operations in DO fetch
### P2 - High (Performance/Correctness Issues)
- **Wrong ID Pattern**: Using `newUniqueId` when `idFromName` is appropriate
- **Stateless DOs**: Using DOs for operations that don't need state
- **Missing Error Handling**: DO operations without proper error handling
- **Alarm Misuse**: Incorrect alarm patterns for hibernation
### P3 - Medium (Best Practices)
- **State Size**: Large state objects that impact performance
- **Concurrency**: Missing concurrency control for shared state
- **Cleanup**: Missing cleanup in DO lifecycle
## Remediation Examples
### Fixing New Unique ID Per Request
```typescript
// ❌ Critical: New DO for every request (expensive and wrong)
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
// Creates new DO instance for every request!
const id = env.USER_SESSION.newUniqueId();
const stub = env.USER_SESSION.get(id);
return stub.fetch(request);
}
}
// ✅ Correct: Reuse DO for same entity
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
// Reuse same DO for this user
const id = env.USER_SESSION.idFromName(userId);
const stub = env.USER_SESSION.get(id);
return stub.fetch(request);
}
}
```
### Fixing Missing Hibernation
```typescript
// ❌ High: DO never hibernates (wastes resources)
export class ChatRoom {
constructor(state, env) {
this.state = state;
this.env = env;
this.messages = [];
}
async fetch(request) {
// Handle chat messages...
// But never hibernates - stays in memory forever!
}
}
// ✅ Correct: Implement hibernation
export class ChatRoom {
constructor(state, env) {
this.state = state;
this.env = env;
// Load persisted state
this.loadState();
// Set alarm for hibernation after inactivity
this.resetHibernationTimer();
}
async loadState() {
const messages = await this.state.storage.get('messages');
this.messages = messages || [];
}
resetHibernationTimer() {
// Reset alarm for 30 seconds from now
this.state.storage.setAlarm(Date.now() + 30000);
}
async fetch(request) {
// Reset timer on activity
this.resetHibernationTimer();
// Handle chat messages...
return new Response('Message processed');
}
async alarm() {
// Persist state before hibernation
await this.state.storage.put('messages', this.messages);
// DO will hibernate after this method returns
}
}
```
### Fixing Wrong ID Pattern
```typescript
// ❌ High: Using newUniqueId for named resources
export default {
async fetch(request: Request, env: Env) {
const roomId = new URL(request.url).searchParams.get('room');
// Wrong: Creates new DO for same room name
const id = env.CHAT_ROOM.newUniqueId();
const stub = env.CHAT_ROOM.get(id);
return stub.fetch(request);
}
}
// ✅ Correct: Use idFromName for named resources
export default {
async fetch(request: Request, env: Env) {
const roomId = new URL(request.url).searchParams.get('room');
// Correct: Same DO for same room name
const id = env.CHAT_ROOM.idFromName(roomId);
const stub = env.CHAT_ROOM.get(id);
return stub.fetch(request);
}
}
```
### Fixing State Persistence
```typescript
// ❌ Critical: State not persisted (lost on hibernation)
export class Counter {
constructor(state, env) {
this.state = state;
this.count = 0; // Not persisted!
}
async fetch(request) {
if (request.url.endsWith('/increment')) {
this.count++; // Lost when DO hibernates!
return new Response(`Count: ${this.count}`);
}
}
}
// ✅ Correct: Persist state to storage
export class Counter {
constructor(state, env) {
this.state = state;
}
async fetch(request) {
if (request.url.endsWith('/increment')) {
// Persist to storage
const currentCount = (await this.state.storage.get('count')) || 0;
const newCount = currentCount + 1;
await this.state.storage.put('count', newCount);
return new Response(`Count: ${newCount}`);
}
if (request.url.endsWith('/get')) {
const count = await this.state.storage.get('count') || 0;
return new Response(`Count: ${count}`);
}
}
}
```
### Fixing Stateless DO Usage
```typescript
// ❌ High: Using DO for stateless operation (overkill)
export default {
async fetch(request: Request, env: Env) {
// Using DO for simple API call - unnecessary!
const id = env.API_PROXY.newUniqueId();
const stub = env.API_PROXY.get(id);
return stub.fetch(request);
}
}
// ✅ Correct: Handle stateless operations in Worker
export default {
async fetch(request: Request, env: Env) {
// Simple API call - handle directly in Worker
const response = await fetch('https://api.example.com/data');
return response;
}
}
// ✅ Correct: Use DO for actual stateful coordination
export default {
async fetch(request: Request, env: Env) {
const ip = request.headers.get('CF-Connecting-IP');
// Rate limiting needs state - perfect for DO
const id = env.RATE_LIMITER.idFromName(ip);
const stub = env.RATE_LIMITER.get(id);
return stub.fetch(request);
}
}
```
## Durable Objects Use Cases
### Use Durable Objects When:
- **Strong Consistency** required (rate limiting, counters)
- **Stateful Coordination** (chat rooms, game sessions)
- **Real-time Features** (WebSockets, collaboration)
- **Distributed Locks** (coordination between requests)
- **Long-running Operations** (background processing)
### Don't Use Durable Objects When:
- **Stateless Operations** (simple API calls)
- **Read-heavy Caching** (use KV instead)
- **Large File Storage** (use R2 instead)
- **Simple Key-Value** (use KV instead)
## MCP Server Integration
When Cloudflare MCP server is available:
- Query DO performance metrics and best practices
- Get latest hibernation patterns and techniques
- Check DO usage limits and quotas
- Analyze DO performance in production
## Benefits
### Immediate Impact
- **Prevents Resource Waste**: Catches DO anti-patterns that waste resources
- **Ensures Correctness**: Validates state persistence and consistency
- **Improves Performance**: Identifies performance issues in DO usage
### Long-term Value
- **Consistent DO Patterns**: Ensures all DO usage follows best practices
- **Better Resource Management**: Proper hibernation and lifecycle management
- **Reduced Costs**: Efficient DO usage reduces resource consumption
## Usage Examples
### During DO Creation
```typescript
// Developer types: const id = env.MY_DO.newUniqueId();
// SKILL immediately activates: "⚠️ HIGH: Using newUniqueId for every request. Consider idFromName for named resources or if this should be stateless."
```
### During State Management
```typescript
// Developer types: this.count = 0; in constructor
// SKILL immediately activates: "❌ CRITICAL: State not persisted to storage. Use this.state.storage.put() to persist data."
```
### During Hibernation
```typescript
// Developer types: DO without alarm() method
// SKILL immediately activates: "⚠️ HIGH: Durable Object missing hibernation. Add alarm() method and setAlarm() for resource efficiency."
```
## Performance Targets
### DO Creation
- **Excellent**: Reuse existing DOs (idFromName)
- **Good**: Minimal new DO creation
- **Acceptable**: Appropriate DO usage patterns
- **Needs Improvement**: Creating new DOs per request
### State Persistence
- **Excellent**: All state persisted to storage
- **Good**: Critical state persisted
- **Acceptable**: Basic state management
- **Needs Improvement**: State not persisted
### Hibernation
- **Excellent**: Proper hibernation implementation
- **Good**: Basic hibernation setup
- **Acceptable**: Some hibernation consideration
- **Needs Improvement**: No hibernation (resource waste)
This SKILL ensures Durable Objects are used correctly by providing immediate, autonomous validation of DO patterns, preventing common mistakes and ensuring efficient state management.

View File

@@ -0,0 +1,290 @@
---
name: edge-performance-optimizer
description: Automatically optimizes Cloudflare Workers performance during development, focusing on cold starts, bundle size, edge caching, and global latency
triggers: ["bundle size changes", "fetch calls", "storage operations", "dependency additions", "sequential operations"]
---
# Edge Performance Optimizer SKILL
## Activation Patterns
This SKILL automatically activates when:
- New dependencies are added to package.json
- Large files or heavy imports are detected
- Sequential operations that could be parallelized
- Missing edge caching opportunities
- Bundle size increases significantly
- Storage operations without optimization patterns
## Expertise Provided
### Edge-Specific Performance Optimization
- **Cold Start Optimization**: Minimizes bundle size and heavy dependencies
- **Global Distribution**: Ensures edge caching for global performance
- **CPU Time Optimization**: Identifies CPU-intensive operations
- **Storage Performance**: Optimizes KV/R2/D1 access patterns
- **Parallel Operations**: Suggests parallelization opportunities
- **Bundle Analysis**: Monitors and optimizes bundle size
### Specific Checks Performed
#### ❌ Performance Anti-Patterns
```typescript
// These patterns trigger immediate alerts:
import axios from 'axios'; // Heavy dependency (13KB)
import moment from 'moment'; // Heavy dependency (68KB)
import _ from 'lodash'; // Heavy dependency (71KB)
// Sequential operations that could be parallel
const user = await env.USERS.get(id);
const settings = await env.SETTINGS.get(id);
const prefs = await env.PREFS.get(id);
```
#### ✅ Performance Best Practices
```typescript
// These patterns are validated as correct:
// Native Web APIs instead of heavy libraries
const response = await fetch(url); // Built-in fetch (0KB)
const now = new Date(); // Native Date (0KB)
// Parallel operations
const [user, settings, prefs] = await Promise.all([
env.USERS.get(id),
env.SETTINGS.get(id),
env.PREFS.get(id),
]);
```
## Integration Points
### Complementary to Existing Components
- **edge-performance-oracle agent**: Handles comprehensive performance analysis, SKILL provides immediate optimization
- **workers-runtime-validator SKILL**: Complements runtime checks with performance optimization
- **es-deploy command**: SKILL ensures performance standards before deployment
### Escalation Triggers
- Complex performance architecture questions → `edge-performance-oracle` agent
- Global distribution strategy → `cloudflare-architecture-strategist` agent
- Performance troubleshooting → `edge-performance-oracle` agent
## Validation Rules
### P1 - Critical (Performance Killer)
- **Large Dependencies**: Heavy libraries like moment, lodash, axios
- **Bundle Size**: Over 200KB (kills cold start performance)
- **Sequential Operations**: Multiple sequential storage/network calls
- **Missing Edge Caching**: No caching for frequently accessed data
### P2 - High (Performance Impact)
- **Bundle Size**: Over 100KB (slows cold starts)
- **CPU Time**: Operations approaching 50ms limit
- **Lazy Loading**: Dynamic imports that hurt cold start
- **Large Payloads**: Responses over 100KB without streaming
### P3 - Medium (Optimization Opportunity)
- **Bundle Size**: Over 50KB (could be optimized)
- **Missing Parallelization**: Operations that could be parallel
- **No Request Caching**: Repeated expensive operations
## Remediation Examples
### Fixing Heavy Dependencies
```typescript
// ❌ Critical: Heavy dependencies (150KB+ bundle)
import axios from 'axios'; // 13KB
import moment from 'moment'; // 68KB
import _ from 'lodash'; // 71KB
// Total: 152KB just for utilities!
// ✅ Correct: Native Web APIs (minimal bundle)
// Use fetch instead of axios
const response = await fetch(url);
const data = await response.json();
// Use native Date instead of moment
const now = new Date();
const tomorrow = new Date(Date.now() + 86400000);
// Use native methods instead of lodash
const unique = [...new Set(array)];
const grouped = array.reduce((acc, item) => {
const key = item.category;
if (!acc[key]) acc[key] = [];
acc[key].push(item);
return acc;
}, {});
// Total: < 5KB for utilities
```
### Fixing Sequential Operations
```typescript
// ❌ High: Sequential KV operations (3x network round-trips)
export default {
async fetch(request: Request, env: Env) {
const user = await env.USERS.get(userId); // 10-30ms
const settings = await env.SETTINGS.get(id); // 10-30ms
const prefs = await env.PREFS.get(id); // 10-30ms
// Total: 30-90ms just for storage!
}
}
// ✅ Correct: Parallel operations (single round-trip)
export default {
async fetch(request: Request, env: Env) {
const [user, settings, prefs] = await Promise.all([
env.USERS.get(userId),
env.SETTINGS.get(id),
env.PREFS.get(id),
]);
// Total: 10-30ms (single round-trip)
}
}
```
### Fixing Missing Edge Caching
```typescript
// ❌ Critical: No edge caching (slow globally)
export default {
async fetch(request: Request, env: Env) {
const config = await fetch('https://api.example.com/config');
// Every request goes to origin!
// Sydney user → US origin = 200ms+ just for config
}
}
// ✅ Correct: Edge caching pattern
export default {
async fetch(request: Request, env: Env) {
const cache = caches.default;
const cacheKey = new Request('https://example.com/config', {
method: 'GET'
});
// Try cache first
let response = await cache.match(cacheKey);
if (!response) {
// Cache miss - fetch from origin
response = await fetch('https://api.example.com/config');
// Cache at edge with 1-hour TTL
response = new Response(response.body, {
...response,
headers: {
...response.headers,
'Cache-Control': 'public, max-age=3600',
}
});
await cache.put(cacheKey, response.clone());
}
// Sydney user → Sydney edge cache = < 10ms
return response;
}
}
```
### Fixing CPU Time Issues
```typescript
// ❌ High: Large synchronous processing (CPU time bomb)
export default {
async fetch(request: Request, env: Env) {
const users = await env.DB.prepare('SELECT * FROM users').all();
// If 10,000 users, this loops for 100ms+ CPU time
const enriched = users.results.map(user => {
return {
...user,
fullName: `${user.firstName} ${user.lastName}`,
// ... expensive computations
};
});
}
}
// ✅ Correct: Bounded operations
export default {
async fetch(request: Request, env: Env) {
// Option 1: Limit at database level
const users = await env.DB.prepare(
'SELECT * FROM users LIMIT ? OFFSET ?'
).bind(10, offset).all(); // Only 10 users, bounded CPU
// Option 2: Stream processing for large datasets
const { readable, writable } = new TransformStream();
// Process in chunks without loading everything into memory
// Option 3: Offload to Durable Object
const id = env.PROCESSOR.newUniqueId();
const stub = env.PROCESSOR.get(id);
return stub.fetch(request); // DO can run longer
}
}
```
## MCP Server Integration
When Cloudflare MCP server is available:
- Query real performance metrics (cold start times, CPU usage)
- Analyze global latency by region
- Get latest performance optimization techniques
- Check bundle size impact on cold starts
## Benefits
### Immediate Impact
- **Faster Cold Starts**: Reduces bundle size and heavy dependencies
- **Better Global Performance**: Ensures edge caching for worldwide users
- **Lower CPU Usage**: Identifies and optimizes CPU-intensive operations
- **Reduced Latency**: Parallelizes operations and adds caching
### Long-term Value
- **Consistent Performance Standards**: Ensures all code meets performance targets
- **Better User Experience**: Faster response times globally
- **Cost Optimization**: Reduced CPU time usage lowers costs
## Usage Examples
### During Dependency Addition
```typescript
// Developer types: npm install moment
// SKILL immediately activates: "❌ CRITICAL: moment is 68KB and will slow cold starts. Use native Date instead for 0KB impact."
```
### During Storage Operations
```typescript
// Developer types: sequential KV gets
// SKILL immediately activates: "⚠️ HIGH: Sequential KV operations detected. Use Promise.all() to parallelize and reduce latency by 3x."
```
### During API Development
```typescript
// Developer types: fetch without caching
// SKILL immediately activates: "⚠️ HIGH: No edge caching for API call. Add Cache API to serve from edge locations globally."
```
## Performance Targets
### Bundle Size
- **Excellent**: < 10KB
- **Good**: < 50KB
- **Acceptable**: < 100KB
- **Needs Improvement**: > 100KB
- **Action Required**: > 200KB
### Cold Start Time
- **Excellent**: < 3ms
- **Good**: < 5ms
- **Acceptable**: < 10ms
- **Needs Improvement**: > 10ms
- **Action Required**: > 20ms
### Global Latency (P95)
- **Excellent**: < 100ms
- **Good**: < 200ms
- **Acceptable**: < 500ms
- **Needs Improvement**: > 500ms
- **Action Required**: > 1000ms
This SKILL ensures Workers performance by providing immediate, autonomous optimization of performance patterns, preventing common performance issues and ensuring fast global response times.

View File

@@ -0,0 +1,3 @@
# Google Gemini API Key
# Get your API key from: https://makersuite.google.com/app/apikey
GEMINI_API_KEY=your-api-key-here

34
skills/gemini-imagegen/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
# Dependencies
node_modules/
# Build outputs
dist/
*.js
*.js.map
# Environment variables
.env
.env.local
# Generated images (examples)
*.png
*.jpg
*.jpeg
*.gif
*.webp
!examples/*.png
!examples/*.jpg
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -0,0 +1,103 @@
# Gemini ImageGen Skill
AI-powered image generation, editing, and composition using Google's Gemini API.
## Quick Start
1. **Install dependencies:**
```bash
npm install
```
2. **Set your API key:**
```bash
export GEMINI_API_KEY="your-api-key-here"
```
Get your key from: https://makersuite.google.com/app/apikey
3. **Generate an image:**
```bash
npm run generate "a sunset over mountains" output.png
```
## Features
- **Generate**: Create images from text descriptions
- **Edit**: Modify existing images with natural language prompts
- **Compose**: Combine multiple images with flexible layouts
## Usage Examples
### Generate Images
```bash
# Basic generation
npm run generate "futuristic city skyline" city.png
# Custom size
npm run generate "modern office" office.png -- --width 1920 --height 1080
```
### Edit Images
```bash
# Style transformation
npm run edit photo.jpg "make it look like a watercolor painting" artistic.png
# Object modification
npm run edit landscape.png "add a rainbow in the sky" enhanced.png
```
### Compose Images
```bash
# Grid layout (default)
npm run compose collage.png img1.jpg img2.jpg img3.jpg img4.jpg
# Horizontal banner
npm run compose banner.png left.png right.png -- --layout horizontal
# Custom composition
npm run compose result.png a.jpg b.jpg -- --prompt "blend seamlessly"
```
## Scripts
- `npm run generate <prompt> <output>` - Generate image from text
- `npm run edit <source> <prompt> <output>` - Edit existing image
- `npm run compose <output> <images...>` - Compose multiple images
## Configuration
### Environment Variables
- `GEMINI_API_KEY` (required) - Your Google Gemini API key
### Options
See `SKILL.md` for detailed documentation on all available options and parameters.
## Development Notes
This is a local development skill that runs on your machine, not on Cloudflare Workers. It's designed for:
- Design workflows and asset creation
- Visual content generation
- Image manipulation and prototyping
- Creating test images for development
## Implementation Status
**Note**: The current implementation includes:
- Complete TypeScript structure
- Argument parsing and validation
- Gemini API integration for image analysis
- Comprehensive error handling
For production use with actual image generation/editing, you'll need to:
1. Use the Imagen model (imagen-3.0-generate-001)
2. Implement proper image data handling
3. Add output file writing with actual image data
Refer to the [Gemini Imagen documentation](https://ai.google.dev/docs/imagen) for implementation details.
## License
MIT

View File

@@ -0,0 +1,231 @@
---
name: gemini-imagegen
description: Generate, edit, and compose images using Google's Gemini AI API for design workflows and visual content creation
triggers: ["image generation", "visual content", "AI art", "image editing", "design automation"]
---
# Gemini ImageGen SKILL
## Overview
This skill provides image generation and manipulation capabilities using Google's Gemini AI API. It's designed for local development workflows where you need to create or modify images using AI assistance.
## Features
- **Generate Images**: Create images from text descriptions
- **Edit Images**: Modify existing images based on text prompts
- **Compose Images**: Combine multiple images with layout instructions
- **Multiple Formats**: Support for PNG, JPEG, and other common image formats
- **Size Options**: Flexible output dimensions for different use cases
## Environment Setup
This skill requires a Gemini API key:
```bash
export GEMINI_API_KEY="your-api-key-here"
```
Get your API key from: https://makersuite.google.com/app/apikey
## Available Scripts
### 1. Generate Image (`scripts/generate-image.ts`)
Create new images from text descriptions.
**Usage:**
```bash
npx tsx scripts/generate-image.ts <prompt> <output-path> [options]
```
**Arguments:**
- `prompt`: Text description of the image to generate
- `output-path`: Where to save the generated image (e.g., `./output.png`)
**Options:**
- `--width <number>`: Image width in pixels (default: 1024)
- `--height <number>`: Image height in pixels (default: 1024)
- `--model <string>`: Gemini model to use (default: 'gemini-2.0-flash-exp')
**Examples:**
```bash
# Basic usage
GEMINI_API_KEY=xxx npx tsx scripts/generate-image.ts "a sunset over mountains" output.png
# Custom size
npx tsx scripts/generate-image.ts "modern office workspace" office.png --width 1920 --height 1080
# Using npm script
npm run generate "futuristic city skyline" city.png
```
### 2. Edit Image (`scripts/edit-image.ts`)
Modify existing images based on text instructions.
**Usage:**
```bash
npx tsx scripts/edit-image.ts <source-image> <prompt> <output-path> [options]
```
**Arguments:**
- `source-image`: Path to the image to edit
- `prompt`: Text description of the desired changes
- `output-path`: Where to save the edited image
**Options:**
- `--model <string>`: Gemini model to use (default: 'gemini-2.0-flash-exp')
**Examples:**
```bash
# Basic editing
GEMINI_API_KEY=xxx npx tsx scripts/edit-image.ts photo.jpg "add a blue sky" edited.jpg
# Style transfer
npx tsx scripts/edit-image.ts portrait.png "make it look like a watercolor painting" artistic.png
# Using npm script
npm run edit photo.jpg "remove background" no-bg.png
```
### 3. Compose Images (`scripts/compose-images.ts`)
Combine multiple images into a single composition.
**Usage:**
```bash
npx tsx scripts/compose-images.ts <output-path> <image1> <image2> [image3...] [options]
```
**Arguments:**
- `output-path`: Where to save the composed image
- `image1, image2, ...`: Paths to images to combine (2-4 images)
**Options:**
- `--layout <string>`: Layout pattern (horizontal, vertical, grid, custom) (default: 'grid')
- `--prompt <string>`: Additional instructions for composition
- `--width <number>`: Output width in pixels (default: auto)
- `--height <number>`: Output height in pixels (default: auto)
**Examples:**
```bash
# Grid layout
GEMINI_API_KEY=xxx npx tsx scripts/compose-images.ts collage.png img1.jpg img2.jpg img3.jpg img4.jpg
# Horizontal layout
npx tsx scripts/compose-images.ts banner.png left.png right.png --layout horizontal
# Custom composition with prompt
npx tsx scripts/compose-images.ts result.png a.jpg b.jpg --prompt "blend seamlessly with gradient transition"
# Using npm script
npm run compose output.png photo1.jpg photo2.jpg photo3.jpg --layout vertical
```
## NPM Scripts
The package.json includes convenient npm scripts:
```bash
npm run generate <prompt> <output> # Generate image from prompt
npm run edit <source> <prompt> <output> # Edit existing image
npm run compose <output> <images...> # Compose multiple images
```
## Installation
From the skill directory:
```bash
npm install
```
This installs:
- `@google/generative-ai`: Google's Gemini API SDK
- `tsx`: TypeScript execution runtime
- `typescript`: TypeScript compiler
## Usage in Design Workflows
### Creating Marketing Assets
```bash
# Generate hero image
npm run generate "modern tech startup hero image, clean, professional" hero.png --width 1920 --height 1080
# Create variations
npm run edit hero.png "change color scheme to blue and green" hero-variant.png
# Compose for social media
npm run compose social-post.png hero.png logo.png --layout horizontal
```
### Rapid Prototyping
```bash
# Generate UI mockup
npm run generate "mobile app login screen, minimalist design" mockup.png --width 375 --height 812
# Iterate on design
npm run edit mockup.png "add a gradient background" mockup-v2.png
```
### Content Creation
```bash
# Generate illustrations
npm run generate "technical diagram of cloud architecture" diagram.png
# Create composite images
npm run compose infographic.png chart1.png chart2.png diagram.png --layout vertical
```
## Technical Details
### Image Generation
- Uses Gemini's imagen-3.0-generate-001 model
- Supports text-to-image generation
- Configurable output dimensions
- Automatic format detection from file extension
### Image Editing
- Uses Gemini's vision capabilities
- Applies transformations based on natural language
- Preserves original image quality where possible
- Supports various editing operations (style, objects, colors, etc.)
### Image Composition
- Intelligent layout algorithms
- Automatic sizing and spacing
- Seamless blending options
- Support for multiple composition patterns
## Error Handling
Common errors and solutions:
1. **Missing API Key**: Ensure `GEMINI_API_KEY` environment variable is set
2. **Invalid Image Format**: Use supported formats (PNG, JPEG, WebP)
3. **File Not Found**: Verify source image paths are correct
4. **API Rate Limits**: Implement delays between requests if needed
5. **Large File Sizes**: Compress images before editing/composing
## Limitations
- API rate limits apply based on your Gemini API tier
- Generated images are subject to Gemini's content policies
- Maximum image dimensions depend on the model used
- Processing time varies based on complexity and size
## Integration with Claude Code
This skill runs locally and can be used during development:
1. **Design System Creation**: Generate component mockups and visual assets
2. **Documentation**: Create diagrams and illustrations for docs
3. **Testing**: Generate test images for visual regression testing
4. **Prototyping**: Rapid iteration on visual concepts
## See Also
- [Google Gemini API Documentation](https://ai.google.dev/docs)
- [Gemini Image Generation Guide](https://ai.google.dev/docs/imagen)
- Edge Stack Plugin for deployment workflows

View File

@@ -0,0 +1,28 @@
{
"name": "gemini-imagegen",
"version": "1.0.0",
"description": "Generate, edit, and compose images using Google's Gemini AI API",
"type": "module",
"scripts": {
"generate": "npx tsx scripts/generate-image.ts",
"edit": "npx tsx scripts/edit-image.ts",
"compose": "npx tsx scripts/compose-images.ts"
},
"keywords": [
"gemini",
"image-generation",
"ai",
"google-ai",
"image-editing"
],
"author": "",
"license": "MIT",
"dependencies": {
"@google/generative-ai": "^0.21.0"
},
"devDependencies": {
"@types/node": "^20.11.0",
"tsx": "^4.7.0",
"typescript": "^5.3.0"
}
}

View File

@@ -0,0 +1,287 @@
#!/usr/bin/env node
import { GoogleGenerativeAI } from '@google/generative-ai';
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve } from 'path';
interface ComposeOptions {
layout?: 'horizontal' | 'vertical' | 'grid' | 'custom';
prompt?: string;
width?: number;
height?: number;
model?: string;
}
async function composeImages(
outputPath: string,
imagePaths: string[],
options: ComposeOptions = {}
): Promise<void> {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
console.error('Error: GEMINI_API_KEY environment variable is required');
console.error('Get your API key from: https://makersuite.google.com/app/apikey');
process.exit(1);
}
if (imagePaths.length < 2) {
console.error('Error: At least 2 images are required for composition');
process.exit(1);
}
if (imagePaths.length > 4) {
console.error('Error: Maximum 4 images supported for composition');
process.exit(1);
}
// Verify all images exist
const resolvedPaths: string[] = [];
for (const imagePath of imagePaths) {
const resolvedPath = resolve(imagePath);
if (!existsSync(resolvedPath)) {
console.error(`Error: Image not found: ${resolvedPath}`);
process.exit(1);
}
resolvedPaths.push(resolvedPath);
}
const {
layout = 'grid',
prompt = '',
width,
height,
model = 'gemini-2.0-flash-exp'
} = options;
console.log('Composing images...');
console.log(`Images: ${resolvedPaths.length}`);
console.log(`Layout: ${layout}`);
console.log(`Model: ${model}`);
if (prompt) console.log(`Custom prompt: "${prompt}"`);
try {
const genAI = new GoogleGenerativeAI(apiKey);
const generativeModel = genAI.getGenerativeModel({ model });
// Read and encode all images
const imageDataList: Array<{ data: string; mimeType: string; path: string }> = [];
for (const imagePath of resolvedPaths) {
const imageData = readFileSync(imagePath);
const base64Image = imageData.toString('base64');
const mimeType = getMimeType(imagePath);
imageDataList.push({
data: base64Image,
mimeType,
path: imagePath
});
console.log(`Loaded: ${imagePath} (${(imageData.length / 1024).toFixed(2)} KB)`);
}
// Build composition prompt
let compositionPrompt = `You are an image composition assistant. Analyze these ${imageDataList.length} images and describe how to combine them into a single composition using a ${layout} layout.`;
if (width && height) {
compositionPrompt += ` The output should be ${width}x${height} pixels.`;
}
if (prompt) {
compositionPrompt += ` Additional instructions: ${prompt}`;
}
compositionPrompt += '\n\nProvide detailed instructions for:\n';
compositionPrompt += '1. Optimal arrangement of images\n';
compositionPrompt += '2. Sizing and spacing recommendations\n';
compositionPrompt += '3. Any blending or transition effects\n';
compositionPrompt += '4. Color harmony adjustments';
// Prepare content parts with all images
const contentParts: Array<any> = [];
for (const imageData of imageDataList) {
contentParts.push({
inlineData: {
data: imageData.data,
mimeType: imageData.mimeType
}
});
}
contentParts.push(compositionPrompt);
// Analyze the composition
const result = await generativeModel.generateContent(contentParts);
const response = result.response;
const compositionInstructions = response.text();
console.log('\nComposition Analysis:');
console.log(compositionInstructions);
// For actual image composition with Gemini, you would typically:
// 1. Use an image composition/editing model
// 2. Send all source images with layout instructions
// 3. Receive the composed image as base64
// 4. Save to output path
console.warn('\nNote: This is a demonstration implementation.');
console.warn('For actual image composition, you would use specialized image composition APIs.');
console.warn('The model has analyzed the images and provided composition instructions.');
// Calculate suggested dimensions based on layout
const suggestedDimensions = calculateDimensions(layout, imageDataList.length, width, height);
console.log(`\nSuggested output dimensions: ${suggestedDimensions.width}x${suggestedDimensions.height}`);
// In a real implementation:
// const composedImageData = Buffer.from(response.candidates[0].content.parts[0].inlineData.data, 'base64');
// writeFileSync(resolve(outputPath), composedImageData);
console.log(`\nTo implement actual image composition:`);
console.log(`1. Use an image composition library or service`);
console.log(`2. Apply the ${layout} layout with ${imageDataList.length} images`);
console.log(`3. Follow the composition instructions provided above`);
console.log(`4. Save to: ${resolve(outputPath)}`);
} catch (error) {
if (error instanceof Error) {
console.error('Error composing images:', error.message);
if (error.message.includes('API key')) {
console.error('\nPlease verify your GEMINI_API_KEY is valid');
}
} else {
console.error('Error composing images:', error);
}
process.exit(1);
}
}
function getMimeType(filePath: string): string {
const extension = filePath.toLowerCase().split('.').pop();
const mimeTypes: Record<string, string> = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
};
return mimeTypes[extension || ''] || 'image/jpeg';
}
function calculateDimensions(
layout: string,
imageCount: number,
width?: number,
height?: number
): { width: number; height: number } {
// If dimensions are provided, use them
if (width && height) {
return { width, height };
}
// Default image size assumption
const defaultSize = 1024;
switch (layout) {
case 'horizontal':
return {
width: width || defaultSize * imageCount,
height: height || defaultSize
};
case 'vertical':
return {
width: width || defaultSize,
height: height || defaultSize * imageCount
};
case 'grid':
const cols = Math.ceil(Math.sqrt(imageCount));
const rows = Math.ceil(imageCount / cols);
return {
width: width || defaultSize * cols,
height: height || defaultSize * rows
};
case 'custom':
default:
return {
width: width || defaultSize,
height: height || defaultSize
};
}
}
// Parse command line arguments
function parseArgs(): { outputPath: string; imagePaths: string[]; options: ComposeOptions } {
const args = process.argv.slice(2);
if (args.length < 3) {
console.error('Usage: compose-images.ts <output-path> <image1> <image2> [image3...] [options]');
console.error('\nArguments:');
console.error(' output-path Where to save the composed image');
console.error(' image1-4 Paths to images to combine (2-4 images)');
console.error('\nOptions:');
console.error(' --layout <string> Layout pattern (horizontal|vertical|grid|custom) (default: grid)');
console.error(' --prompt <string> Additional composition instructions');
console.error(' --width <number> Output width in pixels (default: auto)');
console.error(' --height <number> Output height in pixels (default: auto)');
console.error(' --model <string> Gemini model to use (default: gemini-2.0-flash-exp)');
console.error('\nExample:');
console.error(' GEMINI_API_KEY=xxx npx tsx scripts/compose-images.ts collage.png img1.jpg img2.jpg img3.jpg --layout grid');
process.exit(1);
}
const outputPath = args[0];
const imagePaths: string[] = [];
const options: ComposeOptions = {};
// Parse image paths and options
for (let i = 1; i < args.length; i++) {
const arg = args[i];
if (arg.startsWith('--')) {
const flag = arg;
const value = args[i + 1];
switch (flag) {
case '--layout':
if (['horizontal', 'vertical', 'grid', 'custom'].includes(value)) {
options.layout = value as ComposeOptions['layout'];
} else {
console.warn(`Invalid layout: ${value}. Using default: grid`);
}
i++;
break;
case '--prompt':
options.prompt = value;
i++;
break;
case '--width':
options.width = parseInt(value, 10);
i++;
break;
case '--height':
options.height = parseInt(value, 10);
i++;
break;
case '--model':
options.model = value;
i++;
break;
default:
console.warn(`Unknown option: ${flag}`);
i++;
}
} else {
imagePaths.push(arg);
}
}
return { outputPath, imagePaths, options };
}
// Main execution
const { outputPath, imagePaths, options } = parseArgs();
composeImages(outputPath, imagePaths, options).catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});

View File

@@ -0,0 +1,162 @@
#!/usr/bin/env node
import { GoogleGenerativeAI } from '@google/generative-ai';
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve } from 'path';
interface EditOptions {
model?: string;
}
async function editImage(
sourcePath: string,
prompt: string,
outputPath: string,
options: EditOptions = {}
): Promise<void> {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
console.error('Error: GEMINI_API_KEY environment variable is required');
console.error('Get your API key from: https://makersuite.google.com/app/apikey');
process.exit(1);
}
const resolvedSourcePath = resolve(sourcePath);
if (!existsSync(resolvedSourcePath)) {
console.error(`Error: Source image not found: ${resolvedSourcePath}`);
process.exit(1);
}
const { model = 'gemini-2.0-flash-exp' } = options;
console.log('Editing image...');
console.log(`Source: ${resolvedSourcePath}`);
console.log(`Prompt: "${prompt}"`);
console.log(`Model: ${model}`);
try {
const genAI = new GoogleGenerativeAI(apiKey);
const generativeModel = genAI.getGenerativeModel({ model });
// Read and encode the source image
const imageData = readFileSync(resolvedSourcePath);
const base64Image = imageData.toString('base64');
// Determine MIME type from file extension
const mimeType = getMimeType(resolvedSourcePath);
console.log(`Image size: ${(imageData.length / 1024).toFixed(2)} KB`);
console.log(`MIME type: ${mimeType}`);
// Use Gemini's vision capabilities to analyze and describe the edit
const enhancedPrompt = `You are an image editing assistant. Analyze this image and describe how to apply the following edit: "${prompt}". Provide detailed instructions for the transformation.`;
const result = await generativeModel.generateContent([
{
inlineData: {
data: base64Image,
mimeType: mimeType
}
},
enhancedPrompt
]);
const response = result.response;
const editInstructions = response.text();
console.log('\nEdit Analysis:');
console.log(editInstructions);
// For actual image editing with Gemini, you would typically:
// 1. Use the Imagen model's image editing capabilities
// 2. Send the source image with the edit prompt
// 3. Receive the edited image as base64
// 4. Save to output path
console.warn('\nNote: This is a demonstration implementation.');
console.warn('For actual image editing, you would use Gemini\'s image editing API.');
console.warn('The model has analyzed the image and provided edit instructions.');
// In a real implementation with Imagen editing:
// const editedImageData = Buffer.from(response.candidates[0].content.parts[0].inlineData.data, 'base64');
// writeFileSync(resolve(outputPath), editedImageData);
console.log(`\nTo implement actual image editing:`);
console.log(`1. Use Gemini's image editing endpoint`);
console.log(`2. Send source image with edit prompt`);
console.log(`3. Parse the edited image data from response`);
console.log(`4. Save to: ${resolve(outputPath)}`);
console.log(`\nRefer to: https://ai.google.dev/docs/imagen`);
} catch (error) {
if (error instanceof Error) {
console.error('Error editing image:', error.message);
if (error.message.includes('API key')) {
console.error('\nPlease verify your GEMINI_API_KEY is valid');
}
} else {
console.error('Error editing image:', error);
}
process.exit(1);
}
}
function getMimeType(filePath: string): string {
const extension = filePath.toLowerCase().split('.').pop();
const mimeTypes: Record<string, string> = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
};
return mimeTypes[extension || ''] || 'image/jpeg';
}
// Parse command line arguments
function parseArgs(): { sourcePath: string; prompt: string; outputPath: string; options: EditOptions } {
const args = process.argv.slice(2);
if (args.length < 3) {
console.error('Usage: edit-image.ts <source-image> <prompt> <output-path> [options]');
console.error('\nArguments:');
console.error(' source-image Path to the image to edit');
console.error(' prompt Text description of the desired changes');
console.error(' output-path Where to save the edited image');
console.error('\nOptions:');
console.error(' --model <string> Gemini model to use (default: gemini-2.0-flash-exp)');
console.error('\nExample:');
console.error(' GEMINI_API_KEY=xxx npx tsx scripts/edit-image.ts photo.jpg "add a blue sky" edited.jpg');
process.exit(1);
}
const sourcePath = args[0];
const prompt = args[1];
const outputPath = args[2];
const options: EditOptions = {};
// Parse options
for (let i = 3; i < args.length; i += 2) {
const flag = args[i];
const value = args[i + 1];
switch (flag) {
case '--model':
options.model = value;
break;
default:
console.warn(`Unknown option: ${flag}`);
}
}
return { sourcePath, prompt, outputPath, options };
}
// Main execution
const { sourcePath, prompt, outputPath, options } = parseArgs();
editImage(sourcePath, prompt, outputPath, options).catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});

View File

@@ -0,0 +1,142 @@
#!/usr/bin/env node
import { GoogleGenerativeAI } from '@google/generative-ai';
import { writeFileSync } from 'fs';
import { resolve } from 'path';
interface GenerateOptions {
width?: number;
height?: number;
model?: string;
}
async function generateImage(
prompt: string,
outputPath: string,
options: GenerateOptions = {}
): Promise<void> {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
console.error('Error: GEMINI_API_KEY environment variable is required');
console.error('Get your API key from: https://makersuite.google.com/app/apikey');
process.exit(1);
}
const {
width = 1024,
height = 1024,
model = 'gemini-2.0-flash-exp'
} = options;
console.log('Generating image...');
console.log(`Prompt: "${prompt}"`);
console.log(`Dimensions: ${width}x${height}`);
console.log(`Model: ${model}`);
try {
const genAI = new GoogleGenerativeAI(apiKey);
const generativeModel = genAI.getGenerativeModel({ model });
// Enhanced prompt with image generation context
const enhancedPrompt = `Generate a high-quality image with the following description: ${prompt}. Image dimensions: ${width}x${height} pixels.`;
// For image generation, we'll use the text generation to get image data
// Note: As of the current Gemini API, direct image generation might require
// using the imagen model or multimodal capabilities
const result = await generativeModel.generateContent([
{
inlineData: {
data: '',
mimeType: 'text/plain'
}
},
enhancedPrompt
]);
const response = result.response;
const text = response.text();
// For actual image generation with Gemini, you would typically:
// 1. Use the Imagen model (imagen-3.0-generate-001)
// 2. Parse the response to get base64 image data
// 3. Convert to binary and save
// Placeholder implementation - in production, this would use the actual Imagen API
console.warn('\nNote: This is a demonstration implementation.');
console.warn('For actual image generation, you would use the Imagen model.');
console.warn('Response from model:', text.substring(0, 200) + '...');
// In a real implementation with Imagen:
// const imageData = Buffer.from(response.candidates[0].content.parts[0].inlineData.data, 'base64');
// writeFileSync(resolve(outputPath), imageData);
console.log(`\nTo implement actual image generation:`);
console.log(`1. Use the Imagen model (imagen-3.0-generate-001)`);
console.log(`2. Parse the base64 image data from the response`);
console.log(`3. Save to: ${resolve(outputPath)}`);
console.log(`\nRefer to: https://ai.google.dev/docs/imagen`);
} catch (error) {
if (error instanceof Error) {
console.error('Error generating image:', error.message);
if (error.message.includes('API key')) {
console.error('\nPlease verify your GEMINI_API_KEY is valid');
}
} else {
console.error('Error generating image:', error);
}
process.exit(1);
}
}
// Parse command line arguments
function parseArgs(): { prompt: string; outputPath: string; options: GenerateOptions } {
const args = process.argv.slice(2);
if (args.length < 2) {
console.error('Usage: generate-image.ts <prompt> <output-path> [options]');
console.error('\nArguments:');
console.error(' prompt Text description of the image to generate');
console.error(' output-path Where to save the generated image');
console.error('\nOptions:');
console.error(' --width <number> Image width in pixels (default: 1024)');
console.error(' --height <number> Image height in pixels (default: 1024)');
console.error(' --model <string> Gemini model to use (default: gemini-2.0-flash-exp)');
console.error('\nExample:');
console.error(' GEMINI_API_KEY=xxx npx tsx scripts/generate-image.ts "a sunset over mountains" output.png --width 1920 --height 1080');
process.exit(1);
}
const prompt = args[0];
const outputPath = args[1];
const options: GenerateOptions = {};
// Parse options
for (let i = 2; i < args.length; i += 2) {
const flag = args[i];
const value = args[i + 1];
switch (flag) {
case '--width':
options.width = parseInt(value, 10);
break;
case '--height':
options.height = parseInt(value, 10);
break;
case '--model':
options.model = value;
break;
default:
console.warn(`Unknown option: ${flag}`);
}
}
return { prompt, outputPath, options };
}
// Main execution
const { prompt, outputPath, options } = parseArgs();
generateImage(prompt, outputPath, options).catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ES2022"],
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./scripts"
},
"include": ["scripts/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -0,0 +1,346 @@
---
name: kv-optimization-advisor
description: Automatically optimizes Cloudflare KV storage patterns, suggesting parallel operations, caching strategies, and storage choice guidance
triggers: ["KV operations", "storage access patterns", "sequential storage calls", "large data patterns"]
---
# KV Optimization Advisor SKILL
## Activation Patterns
This SKILL automatically activates when:
- KV `get`, `put`, `delete`, or `list` operations are detected
- Sequential storage operations that could be parallelized
- Large data patterns that might exceed KV limits
- Missing caching opportunities for repeated KV calls
- Storage choice patterns (KV vs R2 vs D1)
## Expertise Provided
### KV Performance Optimization
- **Parallel Operations**: Identifies sequential KV calls that can be parallelized
- **Request-Scoped Caching**: Suggests in-memory caching during request processing
- **Storage Choice Guidance**: Recommends KV vs R2 vs D1 based on use case
- **Value Size Optimization**: Monitors for large values that impact performance
- **Batch Operations**: Suggests batch operations when appropriate
- **TTL Optimization**: Recommends optimal TTL strategies
### Specific Checks Performed
#### ❌ KV Performance Anti-Patterns
```typescript
// These patterns trigger immediate alerts:
// Sequential KV operations (multiple network round-trips)
const user = await env.USERS.get(id); // 10-30ms
const settings = await env.SETTINGS.get(id); // 10-30ms
const prefs = await env.PREFS.get(id); // 10-30ms
// Total: 30-90ms just for storage!
// Repeated KV calls in same request
const user1 = await env.USERS.get(id);
const user2 = await env.USERS.get(id); // Same data fetched twice!
```
#### ✅ KV Performance Best Practices
```typescript
// These patterns are validated as correct:
// Parallel KV operations (single network round-trip)
const [user, settings, prefs] = await Promise.all([
env.USERS.get(id),
env.SETTINGS.get(id),
env.PREFS.get(id),
]);
// Total: 10-30ms (single round-trip)
// Request-scoped caching
const cache = new Map();
async function getCached(key: string, env: Env) {
if (cache.has(key)) return cache.get(key);
const value = await env.USERS.get(key);
cache.set(key, value);
return value;
}
```
## Integration Points
### Complementary to Existing Components
- **edge-performance-oracle agent**: Handles comprehensive performance analysis, SKILL provides immediate KV optimization
- **cloudflare-architecture-strategist agent**: Handles storage architecture decisions, SKILL provides immediate optimization
- **workers-binding-validator SKILL**: Ensures KV bindings are correct, SKILL optimizes usage patterns
### Escalation Triggers
- Complex storage architecture questions → `cloudflare-architecture-strategist` agent
- KV performance troubleshooting → `edge-performance-oracle` agent
- Storage migration strategies → `cloudflare-architecture-strategist` agent
## Validation Rules
### P1 - Critical (Performance Killer)
- **Sequential Operations**: Multiple sequential KV calls that could be parallelized
- **Repeated Calls**: Same KV key fetched multiple times in one request
- **Large Values**: Values approaching 25MB KV limit
### P2 - High (Performance Impact)
- **Missing Caching**: Repeated expensive KV operations without caching
- **Wrong Storage Choice**: Using KV for data that should be in R2 or D1
- **No TTL Strategy**: Missing or inappropriate TTL configuration
### P3 - Medium (Optimization Opportunity)
- **Batch Opportunities**: Multiple operations that could be batched
- **Suboptimal TTL**: TTL values that are too short or too long
- **Missing Error Handling**: KV operations without proper error handling
## Remediation Examples
### Fixing Sequential Operations
```typescript
// ❌ Critical: Sequential KV operations (3x network round-trips)
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
const user = await env.USERS.get(userId); // 10-30ms
const settings = await env.SETTINGS.get(userId); // 10-30ms
const prefs = await env.PREFS.get(userId); // 10-30ms
// Total: 30-90ms just for storage!
return new Response(JSON.stringify({ user, settings, prefs }));
}
}
// ✅ Correct: Parallel operations (single round-trip)
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
// Fetch in parallel - single network round-trip time
const [user, settings, prefs] = await Promise.all([
env.USERS.get(userId),
env.SETTINGS.get(userId),
env.PREFS.get(userId),
]);
// Total: 10-30ms (single round-trip)
return new Response(JSON.stringify({ user, settings, prefs }));
}
}
```
### Fixing Repeated Calls with Caching
```typescript
// ❌ High: Same KV data fetched multiple times
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
// Fetch user data multiple times unnecessarily
const user1 = await env.USERS.get(userId);
const user2 = await env.USERS.get(userId); // Duplicate call!
const user3 = await env.USERS.get(userId); // Duplicate call!
// Process user data...
return new Response('Processed');
}
}
// ✅ Correct: Request-scoped caching
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
// Request-scoped cache to avoid duplicate KV calls
const cache = new Map();
async function getCachedUser(id: string) {
if (cache.has(id)) return cache.get(id);
const user = await env.USERS.get(id);
cache.set(id, user);
return user;
}
const user1 = await getCachedUser(userId); // KV call
const user2 = await getCachedUser(userId); // From cache
const user3 = await getCachedUser(userId); // From cache
// Process user data...
return new Response('Processed');
}
}
```
### Fixing Storage Choice
```typescript
// ❌ High: Using KV for large files (wrong storage choice)
export default {
async fetch(request: Request, env: Env) {
const fileId = new URL(request.url).searchParams.get('id');
// KV is for small key-value data, not large files!
const fileData = await env.FILES.get(fileId); // Could be 10MB+
return new Response(fileData);
}
}
// ✅ Correct: Use R2 for large files
export default {
async fetch(request: Request, env: Env) {
const fileId = new URL(request.url).searchParams.get('id');
// R2 is designed for large objects/files
const object = await env.FILES_BUCKET.get(fileId);
if (!object) {
return new Response('Not found', { status: 404 });
}
return new Response(object.body);
}
}
```
### Fixing TTL Strategy
```typescript
// ❌ Medium: No TTL strategy (data never expires)
export default {
async fetch(request: Request, env: Env) {
const cacheKey = `data:${Date.now()}`;
// Data cached forever - may become stale
await env.CACHE.put(cacheKey, data);
}
}
// ✅ Correct: Appropriate TTL strategy
export default {
async fetch(request: Request, env: Env) {
const cacheKey = 'user:profile:123';
// Cache user profile for 1 hour (reasonable for user data)
await env.CACHE.put(cacheKey, data, {
expirationTtl: 3600 // 1 hour
});
// Cache API response for 5 minutes (frequently changing)
await env.API_CACHE.put(apiKey, response, {
expirationTtl: 300 // 5 minutes
});
// Cache static data for 24 hours (rarely changes)
await env.STATIC_CACHE.put(staticKey, data, {
expirationTtl: 86400 // 24 hours
});
}
}
```
### Fixing Large Value Handling
```typescript
// ❌ High: Large values approaching KV limits
export default {
async fetch(request: Request, env: Env) {
const reportId = new URL(request.url).searchParams.get('id');
// Large report (20MB) - close to KV 25MB limit!
const report = await env.REPORTS.get(reportId);
return new Response(report);
}
}
// ✅ Correct: Compress large values or use R2
export default {
async fetch(request: Request, env: Env) {
const reportId = new URL(request.url).searchParams.get('id');
// Option 1: Compress before storing in KV
const compressed = await env.REPORTS.get(reportId);
const decompressed = decompress(compressed);
// Option 2: Use R2 for large objects
const object = await env.REPORTS_BUCKET.get(reportId);
return new Response(object.body);
}
}
```
## Storage Choice Guidance
### Use KV When:
- **Small values** (< 1MB typical, < 25MB max)
- **Key-value access patterns**
- **Eventually consistent** data is acceptable
- **Low latency** reads required globally
- **Simple caching** needs
### Use R2 When:
- **Large objects** (files, images, videos)
- **S3-compatible** access needed
- **Strong consistency** required
- **Object storage** patterns
- **Large files** (> 1MB)
### Use D1 When:
- **Relational data** with complex queries
- **Strong consistency** required
- **SQL operations** needed
- **Structured data** with relationships
- **Complex queries** and joins
## MCP Server Integration
When Cloudflare MCP server is available:
- Query KV performance metrics (latency, hit rates)
- Analyze storage usage patterns
- Get latest KV optimization techniques
- Check storage limits and quotas
## Benefits
### Immediate Impact
- **Faster Response Times**: Parallel operations reduce latency by 3x or more
- **Reduced KV Costs**: Fewer operations and better caching
- **Better Performance**: Proper storage choice improves overall performance
### Long-term Value
- **Consistent Optimization**: Ensures all KV usage follows best practices
- **Cost Efficiency**: Optimized storage patterns reduce costs
- **Better User Experience**: Faster response times from optimized storage
## Usage Examples
### During KV Operation Writing
```typescript
// Developer types: sequential KV gets
// SKILL immediately activates: "⚠️ HIGH: Sequential KV operations detected. Use Promise.all() to parallelize and reduce latency by 3x."
```
### During Storage Architecture
```typescript
// Developer types: storing large files in KV
// SKILL immediately activates: "⚠️ HIGH: Large file storage in KV detected. Use R2 for objects > 1MB to avoid performance issues."
```
### During Caching Implementation
```typescript
// Developer types: repeated KV calls in same request
// SKILL immediately activates: "⚠️ HIGH: Duplicate KV calls detected. Add request-scoped caching to avoid redundant network calls."
```
## Performance Targets
### KV Operation Latency
- **Excellent**: < 10ms (parallel operations)
- **Good**: < 30ms (single operation)
- **Acceptable**: < 100ms (sequential operations)
- **Needs Improvement**: > 100ms
### Cache Hit Rate
- **Excellent**: > 90%
- **Good**: > 75%
- **Acceptable**: > 50%
- **Needs Improvement**: < 50%
This SKILL ensures KV storage performance by providing immediate, autonomous optimization of storage patterns, preventing common performance issues and ensuring efficient data access.

View File

@@ -0,0 +1,93 @@
---
name: polar-integration-validator
description: Autonomous validation of Polar.sh billing integration. Checks webhook endpoints, signature verification, subscription middleware, and environment configuration.
triggers: ["webhook file changes", "subscription code changes", "wrangler.toml updates", "billing-related modifications"]
---
# Polar Integration Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- Files matching `**/webhooks/polar.*` are created/modified
- Files containing "subscription" or "polar" in path are modified
- `wrangler.toml` is updated
- Environment variable files (`.dev.vars`, `.env`) are modified
- Before deployment operations
## Validation Rules
### P1 - Critical (Block Operations)
**Webhook Endpoint**:
- ✅ Webhook handler exists (`server/api/webhooks/polar.ts` or similar)
- ✅ Signature verification implemented (`polar.webhooks.verify`)
- ✅ All critical events handled: `checkout.completed`, `subscription.created`, `subscription.updated`, `subscription.canceled`
**Environment Variables**:
-`POLAR_ACCESS_TOKEN` configured (check `.dev.vars` or secrets)
-`POLAR_WEBHOOK_SECRET` in wrangler.toml
**Database**:
- ✅ Users table has `polar_customer_id` column
- ✅ Subscriptions table exists
- ✅ Foreign key relationship configured
### P2 - Important (Warn)
**Event Handling**:
- ⚠️ `subscription.past_due` handler exists
- ⚠️ Database updates in all event handlers
- ⚠️ Error logging implemented
**Subscription Middleware**:
- ⚠️ Subscription check function exists
- ⚠️ Used on protected routes
- ⚠️ Checks `subscription_status === 'active'`
- ⚠️ Checks `current_period_end` not expired
### P3 - Suggestions (Inform)
- Webhook event logging to database
- Customer creation helper function
- Subscription status caching
- Rate limiting on webhook endpoint
## Validation Output
```
🔍 Polar.sh Integration Validation
✅ P1 Checks (Critical):
✅ Webhook endpoint exists
✅ Signature verification implemented
✅ Environment variables configured
✅ Database schema complete
⚠️ P2 Checks (Important):
⚠️ Missing subscription.past_due handler
✅ Subscription middleware exists
✅ Protected routes check subscription
P3 Suggestions:
Consider adding webhook event logging
Add rate limiting to webhook endpoint
📋 Summary: 1 warning found
💡 Run /es-billing-setup to fix issues
```
## Escalation
Complex scenarios escalate to `polar-billing-specialist` agent:
- Custom webhook processing logic
- Multi-tenant subscription architecture
- Usage-based billing implementation
- Migration from other billing providers
## Notes
- Runs automatically on relevant file changes
- Can block deployments with P1 issues
- Queries Polar MCP for product validation
- Integrates with `/validate` and `/es-deploy` commands

View File

@@ -0,0 +1,333 @@
---
name: shadcn-ui-design-validator
description: Automatically validates frontend design patterns to prevent generic aesthetics (Inter fonts, purple gradients, minimal animations) and enforce distinctive, branded design during Tanstack Start (React) development with shadcn/ui
triggers: ["tsx file creation", "component changes", "tailwind config changes", "shadcn component usage", "design system updates"]
note: "Updated for Tanstack Start + shadcn/ui. Validates React/TSX components with shadcn/ui patterns."
---
# shadcn/ui Design Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- New `.tsx` React components are created
- Tailwind configuration (`tailwind.config.ts`) is modified
- Tanstack Start configuration (`app.config.ts`) is modified
- Component styling or classes are changed
- Design token definitions are updated
- Before deployment commands are executed
## Expertise Provided
### Design Pattern Validation
- **Generic Pattern Detection**: Identifies default/overused design patterns
- **Typography Analysis**: Ensures distinctive font choices and hierarchy
- **Animation Validation**: Checks for engaging micro-interactions and transitions
- **Color System**: Validates distinctive color palettes vs generic defaults
- **Component Customization**: Ensures shadcn/ui components are customized, not default
### Specific Checks Performed
#### ❌ Critical Violations (Generic Design Patterns)
```tsx
<!-- These patterns trigger alerts: -->
<!-- Generic font (Inter/Roboto) -->
<div className="font-sans"> <!-- Using default Inter -->
<!-- Purple gradient on white (overused pattern) -->
<div className="bg-gradient-to-r from-purple-500 to-purple-600">
<!-- No animations/transitions -->
<Button onClick="submit">Submit</Button> <!-- No hover state -->
<!-- Default background colors -->
<div className="bg-gray-50"> <!-- Generic #f9fafb -->
```
#### ✅ Correct Distinctive Patterns
```tsx
<!-- These patterns are validated as correct: -->
<!-- Custom distinctive fonts -->
<h1 className="font-heading"> <!-- Custom font family -->
<!-- Custom brand colors -->
<div className="bg-brand-coral"> <!-- Distinctive palette -->
<!-- Engaging animations -->
<Button
className="transition-all duration-300 hover:scale-105 hover:shadow-xl"
onClick="submit"
>
Submit
</Button>
<!-- Atmospheric backgrounds -->
<div className="bg-gradient-to-br from-brand-ocean via-brand-sky to-brand-coral">
```
## Integration Points
### Complementary to Existing Components
- **frontend-design-specialist agent**: Handles deep design analysis, SKILL provides immediate validation
- **tanstack-ui-architect agent**: Component expertise, SKILL validates implementation
- **es-design-review command**: SKILL provides continuous validation between explicit reviews
### Escalation Triggers
- Complex design system questions → `frontend-design-specialist` agent
- Component customization help → `tanstack-ui-architect` agent
- Accessibility concerns → `accessibility-guardian` agent
- Full design review → `/es-design-review` command
## Validation Rules
### P1 - Critical (Generic Patterns to Avoid)
- **Default Fonts**: Inter, Roboto, Helvetica (in over 80% of sites)
- **Purple Gradients**: `from-purple-*` to `to-purple-*` on white backgrounds
- **Generic Grays**: `bg-gray-50`, `bg-gray-100` (overused neutrals)
- **No Animations**: Interactive elements without hover/focus transitions
- **Default Component Props**: Using shadcn/ui components with all default props
### P2 - Important (Polish and Engagement)
- **Missing Hover States**: Buttons/links without hover effects
- **No Loading States**: Async actions without loading feedback
- **Inconsistent Spacing**: Not using Tailwind spacing scale consistently
- **No Micro-interactions**: Forms/buttons without feedback animations
- **Weak Typography Hierarchy**: Similar font sizes for different heading levels
### P3 - Best Practices
- **Font Weight Variety**: Using only one or two font weights
- **Limited Color Palette**: Not defining custom brand colors
- **No Custom Tokens**: Not extending Tailwind theme with brand values
- **Missing Dark Mode**: No dark mode variants (if applicable)
## Remediation Examples
### Fixing Generic Fonts
```tsx
<!-- ❌ Critical: Default Inter font -->
<h1 className="text-4xl font-sans">Welcome</h1>
<!-- ✅ Correct: Distinctive custom font -->
<h1 className="text-4xl font-heading tracking-tight">Welcome</h1>
<!-- tailwind.config.ts -->
export default {
theme: {
extend: {
fontFamily: {
// ❌ NOT: sans: ['Inter', 'sans-serif']
// ✅ YES: Distinctive fonts
sans: ['Space Grotesk', 'system-ui', 'sans-serif'],
heading: ['Archivo Black', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace']
}
}
}
}
```
### Fixing Generic Colors
```tsx
<!-- ❌ Critical: Purple gradient (overused) -->
<div className="bg-gradient-to-r from-purple-500 to-purple-600">
<h2 className="text-white">Hero Section</h2>
</div>
<!-- ✅ Correct: Custom brand colors -->
<div className="bg-gradient-to-br from-brand-coral via-brand-ocean to-brand-sunset">
<h2 className="text-white">Hero Section</h2>
</div>
<!-- tailwind.config.ts -->
export default {
theme: {
extend: {
colors: {
// ❌ NOT: Using only default Tailwind colors
// ✅ YES: Custom brand palette
brand: {
coral: '#FF6B6B',
ocean: '#4ECDC4',
sunset: '#FFE66D',
midnight: '#2C3E50',
cream: '#FFF5E1'
}
}
}
}
}
```
### Fixing Missing Animations
```tsx
<!-- ❌ Critical: No hover/transition effects -->
<Button onClick="handleSubmit">
Submit Form
</Button>
<!-- ✅ Correct: Engaging animations -->
<Button
className="transition-all duration-300 hover:scale-105 hover:shadow-xl active:scale-95"
onClick="handleSubmit"
>
<span className="inline-flex items-center gap-2">
Submit Form
<Icon
name="i-heroicons-arrow-right"
className="transition-transform duration-300 group-hover:translate-x-1"
/>
</span>
</Button>
```
### Fixing Default Component Usage
```tsx
<!-- ❌ P2: All default props (generic appearance) -->
<Card>
<p>Content here</p>
</Card>
<!-- ✅ Correct: Customized for brand distinctiveness -->
<Card
:ui="{
background: 'bg-white dark:bg-brand-midnight',
ring: 'ring-1 ring-brand-coral/20',
rounded: 'rounded-2xl',
shadow: 'shadow-xl hover:shadow-2xl',
body: { padding: 'p-8' }
}"
className="transition-all duration-300 hover:-translate-y-1"
>
<p className="text-gray-700 dark:text-gray-300">Content here</p>
</Card>
```
## MCP Server Integration
When shadcn/ui MCP server is available:
- Query component customization options before validation
- Verify that suggested customizations use valid props
- Get latest component API to prevent hallucination
- Validate `ui` prop structure against actual schema
**Example MCP Usage**:
```typescript
// Validate Button customization
const buttonDocs = await mcp.shadcn.get_component("Button");
// Check if suggested props exist: color, size, variant, ui, etc.
// Ensure customizations align with actual API
```
## Benefits
### Immediate Impact
- **Prevents Generic Design**: Catches overused patterns before they ship
- **Enforces Brand Identity**: Ensures consistent, distinctive aesthetics
- **Improves User Engagement**: Validates animations and interactions
- **Educates Developers**: Clear explanations of design best practices
### Long-term Value
- **Consistent Visual Identity**: All components follow brand guidelines
- **Faster Design Iterations**: Immediate feedback on design choices
- **Better User Experience**: Polished animations and interactions
- **Reduced Design Debt**: Prevents accumulation of generic patterns
## Usage Examples
### During Component Creation
```tsx
// Developer creates: <div className="font-sans bg-purple-500">
// SKILL immediately activates: "⚠️ WARNING: Using default 'font-sans' (Inter) and purple gradient. Consider custom brand fonts and colors for distinctive design."
```
### During Styling
```tsx
// Developer adds: <Button>Click me</Button>
// SKILL immediately activates: "⚠️ P2: Button lacks hover animations. Add transition utilities for better engagement: class='transition-all duration-300 hover:scale-105'"
```
### During Configuration
```typescript
// Developer modifies tailwind.config.ts with default Inter
// SKILL immediately activates: "⚠️ P1: Using Inter font (appears in 80%+ of sites). Replace with distinctive font choices like Space Grotesk, Archivo, or other brand-appropriate fonts."
```
### Before Deployment
```tsx
// SKILL runs comprehensive check: "✅ Design validation passed. Custom fonts, distinctive colors, engaging animations, and customized components detected."
```
## Design Philosophy Alignment
This SKILL implements the core insight from Claude's "Improving Frontend Design Through Skills" blog post:
> "Think about frontend design the way a frontend engineer would. The more you can map aesthetic improvements to implementable frontend code, the better Claude can execute."
**Key Mappings**:
- **Typography** → Tailwind `fontFamily` config + utility classes
- **Animations** → Tailwind `transition-*`, `hover:*`, `duration-*` utilities
- **Background effects** → Custom gradient combinations, `backdrop-*` utilities
- **Themes** → Extended Tailwind color palette with brand tokens
## Distinctive vs Generic Patterns
### ❌ Generic Patterns (What to Avoid)
```tsx
<!-- The "AI default aesthetic" -->
<div className="bg-white">
<h1 className="font-sans text-gray-900">Title</h1>
<div className="bg-gradient-to-r from-purple-500 to-purple-600">
<Button>Action</Button>
</div>
</div>
```
**Problems**:
- Inter font (default)
- Purple gradient (overused)
- Gray backgrounds (generic)
- No animations (flat)
- Default components (no customization)
### ✅ Distinctive Patterns (What to Strive For)
```tsx
<!-- Brand-distinctive aesthetic -->
<div className="bg-gradient-to-br from-brand-cream via-white to-brand-ocean/10">
<h1 className="font-heading text-6xl text-brand-midnight tracking-tighter">
Title
</h1>
<div className="relative overflow-hidden rounded-3xl bg-brand-coral p-8">
<!-- Atmospheric background -->
<div className="absolute inset-0 bg-gradient-to-br from-brand-coral to-brand-sunset opacity-80" />
<Button
:ui="{
font: 'font-heading',
rounded: 'rounded-full',
size: 'xl'
}"
className="relative z-10 transition-all duration-500 hover:scale-110 hover:rotate-2 hover:shadow-2xl active:scale-95"
>
<span className="flex items-center gap-2">
Action
<Icon
name="i-heroicons-sparkles"
className="animate-pulse"
/>
</span>
</Button>
</div>
</div>
```
**Strengths**:
- Custom fonts (Archivo Black for headings)
- Brand-specific colors (coral, ocean, sunset)
- Atmospheric gradients (multiple layers)
- Rich animations (scale, rotate, shadow transitions)
- Heavily customized components (ui prop + utility classes)
- Micro-interactions (icon pulse, hover effects)
This SKILL ensures every Tanstack Start project develops a distinctive visual identity by preventing generic patterns and guiding developers toward branded, engaging design implementations.

View File

@@ -0,0 +1,305 @@
---
name: workers-binding-validator
description: Automatically validates Cloudflare Workers binding configuration, ensuring code references match wrangler.toml setup and TypeScript interfaces are accurate
triggers: ["env parameter usage", "wrangler.toml changes", "TypeScript interface updates", "binding references"]
---
# Workers Binding Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- `env` parameter is used in Workers code
- wrangler.toml file is modified
- TypeScript `Env` interface is defined or updated
- New binding references are added to code
- Binding configuration patterns are detected
## Expertise Provided
### Binding Configuration Validation
- **Binding Consistency**: Ensures code references match wrangler.toml configuration
- **TypeScript Interface Validation**: Validates `Env` interface matches actual bindings
- **Binding Type Accuracy**: Ensures correct binding types (KV, R2, D1, Durable Objects)
- **Remote Binding Validation**: Checks remote binding configuration for development
- **Secret Binding Verification**: Validates secret vs environment variable bindings
### Specific Checks Performed
#### ❌ Critical Binding Mismatches
```typescript
// These patterns trigger immediate alerts:
// Code references binding that doesn't exist in wrangler.toml
const user = await env.USER_DATA.get(id); // USER_DATA not configured
// TypeScript interface doesn't match wrangler.toml
interface Env {
USERS: KVNamespace; // Code expects USERS
// wrangler.toml has USER_DATA (mismatch!)
}
```
#### ✅ Correct Binding Patterns
```typescript
// These patterns are validated as correct:
// Matching wrangler.toml and TypeScript interface
interface Env {
USER_DATA: KVNamespace; // Matches wrangler.toml binding name
API_BUCKET: R2Bucket; // Correct R2 binding type
}
// Proper usage in code
const user = await env.USER_DATA.get(id);
const object = await env.API_BUCKET.get(key);
```
## Integration Points
### Complementary to Existing Components
- **binding-context-analyzer agent**: Handles complex binding analysis, SKILL provides immediate validation
- **workers-runtime-validator SKILL**: Complements runtime checks with binding validation
- **cloudflare-security-checker SKILL**: Ensures secret bindings are properly configured
### Escalation Triggers
- Complex binding architecture questions → `binding-context-analyzer` agent
- Migration between binding types → `cloudflare-architecture-strategist` agent
- Binding performance issues → `edge-performance-oracle` agent
## Validation Rules
### P1 - Critical (Will Fail at Runtime)
- **Missing Bindings**: Code references bindings not in wrangler.toml
- **Type Mismatches**: Wrong binding types in TypeScript interface
- **Name Mismatches**: Different names in code vs configuration
- **Missing Env Interface**: No TypeScript interface for bindings
### P2 - High (Configuration Issues)
- **Remote Binding Missing**: Development bindings without `remote = true`
- **Secret vs Var Confusion**: Secrets in [vars] section or vice versa
- **Incomplete Interface**: Missing bindings in TypeScript interface
### P3 - Medium (Best Practices)
- **Binding Documentation**: Missing JSDoc comments for bindings
- **Binding Organization**: Poor organization of related bindings
## Remediation Examples
### Fixing Missing Bindings
```typescript
// ❌ Critical: Code references binding not in wrangler.toml
export default {
async fetch(request: Request, env: Env) {
const user = await env.USER_CACHE.get(userId); // USER_CACHE not configured!
}
}
// wrangler.toml (missing binding)
[[kv_namespaces]]
binding = "USER_DATA" # Different name!
id = "user-data"
// ✅ Correct: Matching names
export default {
async fetch(request: Request, env: Env) {
const user = await env.USER_DATA.get(userId); // Matches wrangler.toml
}
}
// wrangler.toml (correct binding)
[[kv_namespaces]]
binding = "USER_DATA" # Matches code!
id = "user-data"
```
### Fixing TypeScript Interface Mismatches
```typescript
// ❌ Critical: Interface doesn't match wrangler.toml
interface Env {
USERS: KVNamespace; // Code expects USERS
SESSIONS: KVNamespace; // Code expects SESSIONS
}
// wrangler.toml has different names
[[kv_namespaces]]
binding = "USER_DATA" # Different!
id = "user-data"
[[kv_namespaces]]
binding = "SESSION_DATA" # Different!
id = "session-data"
// ✅ Correct: Matching interface and configuration
interface Env {
USER_DATA: KVNamespace; # Matches wrangler.toml
SESSION_DATA: KVNamespace; # Matches wrangler.toml
}
// wrangler.toml (matching names)
[[kv_namespaces]]
binding = "USER_DATA" # Matches interface!
id = "user-data"
[[kv_namespaces]]
binding = "SESSION_DATA" # Matches interface!
id = "session-data"
```
### Fixing Binding Type Mismatches
```typescript
// ❌ Critical: Wrong binding type
interface Env {
MY_BUCKET: KVNamespace; # Wrong type - should be R2Bucket
MY_DB: D1Database; # Wrong type - should be KVNamespace
}
// wrangler.toml
[[r2_buckets]]
binding = "MY_BUCKET" # R2 bucket, not KV!
[[kv_namespaces]]
binding = "MY_DB" # KV namespace, not D1!
// ✅ Correct: Proper binding types
interface Env {
MY_BUCKET: R2Bucket; # Correct type for R2 bucket
MY_DB: KVNamespace; # Correct type for KV namespace
}
// wrangler.toml (same as above)
[[r2_buckets]]
binding = "MY_BUCKET"
[[kv_namespaces]]
binding = "MY_DB"
```
### Fixing Remote Binding Configuration
```typescript
// ❌ High: Missing remote binding for development
// wrangler.toml
[[kv_namespaces]]
binding = "USER_DATA"
id = "user-data"
# Missing remote = true for development!
// ✅ Correct: Remote binding configured
[[kv_namespaces]]
binding = "USER_DATA"
id = "user-data"
remote = true # Enables remote binding for development
```
### Fixing Secret vs Environment Variable Confusion
```typescript
// ❌ High: Secret in [vars] section (visible in git)
// wrangler.toml
[vars]
API_KEY = "sk_live_12345" # Secret exposed in git!
// ✅ Correct: Secret via wrangler secret command
// wrangler.toml (no secrets in [vars])
[vars]
PUBLIC_API_URL = "https://api.example.com" # Non-secret config only
# Set secret via command line:
# wrangler secret put API_KEY
# (prompt: enter secret value)
// Code accesses both correctly
interface Env {
API_KEY: string; # From wrangler secret
PUBLIC_API_URL: string; # From wrangler.toml [vars]
}
```
## MCP Server Integration
When Cloudflare MCP server is available:
- Query actual binding configuration from Cloudflare account
- Verify bindings exist and are accessible
- Check binding permissions and limits
- Get latest binding configuration best practices
## Benefits
### Immediate Impact
- **Prevents Runtime Failures**: Catches binding mismatches before deployment
- **Reduces Debugging Time**: Immediate feedback on configuration issues
- **Ensures Type Safety**: Validates TypeScript interfaces match reality
### Long-term Value
- **Consistent Configuration**: Ensures all code uses correct binding patterns
- **Better Developer Experience**: Clear error messages for binding issues
- **Reduced Deployment Issues**: Configuration validation prevents failed deployments
## Usage Examples
### During Binding Usage
```typescript
// Developer types: const data = await env.CACHE.get(key);
// SKILL immediately activates: "❌ CRITICAL: CACHE binding not found in wrangler.toml. Add [[kv_namespaces]] binding = 'CACHE' or check spelling."
```
### During Interface Definition
```typescript
// Developer types: interface Env { USERS: R2Bucket; }
// SKILL immediately activates: "⚠️ HIGH: USERS binding type mismatch. wrangler.toml shows USERS as KVNamespace, not R2Bucket."
```
### During Configuration Changes
```typescript
// Developer modifies wrangler.toml binding name
// SKILL immediately activates: "⚠️ HIGH: Binding name changed from USER_DATA to USERS. Update TypeScript interface and code references."
```
## Binding Type Reference
### KV Namespace
```typescript
interface Env {
MY_KV: KVNamespace;
}
// Usage: await env.MY_KV.get(key)
```
### R2 Bucket
```typescript
interface Env {
MY_BUCKET: R2Bucket;
}
// Usage: await env.MY_BUCKET.get(key)
```
### D1 Database
```typescript
interface Env {
MY_DB: D1Database;
}
// Usage: await env.MY_DB.prepare(query).bind(params).all()
```
### Durable Object
```typescript
interface Env {
MY_DO: DurableObjectNamespace;
}
// Usage: env.MY_DO.get(id)
```
### AI Binding
```typescript
interface Env {
AI: Ai;
}
// Usage: await env.AI.run(model, params)
```
### Vectorize
```typescript
interface Env {
VECTORS: VectorizeIndex;
}
// Usage: await env.VECTORS.query(vector, options)
```
This SKILL ensures Workers binding configuration is correct by providing immediate, autonomous validation of binding patterns, preventing runtime failures and configuration mismatches.

View File

@@ -0,0 +1,148 @@
---
name: workers-runtime-validator
description: Automatically validates Cloudflare Workers runtime compatibility during development, preventing Node.js API usage and ensuring proper Workers patterns
triggers: ["import statements", "file creation", "code changes", "deployment preparation"]
---
# Workers Runtime Validator SKILL
## Activation Patterns
This SKILL automatically activates when:
- New `.ts` or `.js` files are created in Workers projects
- Import statements are added or modified
- Code changes include potential runtime violations
- Before deployment commands are executed
- When `process.env`, `require()`, or Node.js APIs are detected
## Expertise Provided
### Runtime Compatibility Validation
- **Forbidden API Detection**: Identifies Node.js built-ins that don't exist in Workers
- **Environment Access**: Ensures proper `env` parameter usage vs `process.env`
- **Module System**: Validates ES modules usage (no `require()`)
- **Async Patterns**: Ensures all I/O operations are async
- **Package Compatibility**: Checks npm packages for Node.js dependencies
### Specific Checks Performed
#### ❌ Critical Violations (Will Break in Production)
```typescript
// These patterns trigger immediate alerts:
import fs from 'fs'; // Node.js API
import { Buffer } from 'buffer'; // Node.js API
const secret = process.env.API_KEY; // process doesn't exist
const data = require('./module'); // require() not supported
```
#### ✅ Correct Workers Patterns
```typescript
// These patterns are validated as correct:
import { z } from 'zod'; // Web-compatible package
const secret = env.API_KEY; // Proper env parameter
const hash = await crypto.subtle.digest(); // Web Crypto API
```
## Integration Points
### Complementary to Existing Components
- **workers-runtime-guardian agent**: Handles deep runtime analysis, SKILL provides immediate validation
- **es-deploy command**: SKILL prevents deployment failures by catching issues early
- **validate command**: SKILL provides continuous validation between explicit checks
### Escalation Triggers
- Complex runtime compatibility questions → `workers-runtime-guardian` agent
- Package dependency analysis → `edge-performance-oracle` agent
- Migration from Node.js to Workers → `cloudflare-architecture-strategist` agent
## Validation Rules
### P1 - Critical (Must Fix Immediately)
- **Node.js Built-ins**: `fs`, `path`, `os`, `crypto`, `process`, `buffer`
- **CommonJS Usage**: `require()`, `module.exports`
- **Process Access**: `process.env`, `process.exit()`
- **Synchronous I/O**: Any blocking I/O operations
### P2 - Important (Should Fix)
- **Package Dependencies**: npm packages with Node.js dependencies
- **Missing Async**: I/O operations without await
- **Buffer Usage**: Using Node.js Buffer instead of Uint8Array
### P3 - Best Practices
- **TypeScript Env Interface**: Missing or incorrect Env type definition
- **Web API Usage**: Not using modern Web APIs when available
## Remediation Examples
### Fixing Node.js API Usage
```typescript
// ❌ Critical: Node.js crypto
import crypto from 'crypto';
const hash = crypto.createHash('sha256');
// ✅ Correct: Web Crypto API
const encoder = new TextEncoder();
const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));
```
### Fixing Environment Access
```typescript
// ❌ Critical: process.env
const apiKey = process.env.API_KEY;
// ✅ Correct: env parameter
export default {
async fetch(request: Request, env: Env) {
const apiKey = env.API_KEY;
}
}
```
### Fixing Module System
```typescript
// ❌ Critical: CommonJS
const utils = require('./utils');
// ✅ Correct: ES modules
import { utils } from './utils';
```
## MCP Server Integration
When Cloudflare MCP server is available:
- Query latest Workers runtime API documentation
- Check for deprecated APIs before suggesting fixes
- Get current compatibility information for new features
## Benefits
### Immediate Impact
- **Prevents Runtime Failures**: Catches issues before deployment
- **Reduces Debugging Time**: Immediate feedback on violations
- **Educates Developers**: Clear explanations of Workers vs Node.js differences
### Long-term Value
- **Consistent Code Quality**: Ensures all code follows Workers patterns
- **Faster Development**: No need to wait for deployment to discover issues
- **Better Developer Experience**: Real-time guidance during coding
## Usage Examples
### During Code Creation
```typescript
// Developer types: import fs from 'fs';
// SKILL immediately activates: "❌ CRITICAL: 'fs' is a Node.js API not available in Workers runtime. Use Web APIs or Workers-specific alternatives."
```
### During Refactoring
```typescript
// Developer changes: const secret = process.env.API_KEY;
// SKILL immediately activates: "❌ CRITICAL: 'process.env' doesn't exist in Workers. Use the 'env' parameter passed to your fetch handler instead."
```
### Before Deployment
```typescript
// SKILL runs comprehensive check: "✅ Runtime validation passed. No Node.js APIs detected, all environment access uses proper env parameter."
```
This SKILL ensures Workers runtime compatibility by providing immediate, autonomous validation of code patterns, preventing common migration mistakes and runtime failures.