Skill Format
Structure, frontmatter, and best practices for writing effective skills
Skills are markdown files (SKILL.md) with optional YAML frontmatter. The AI reads these instructions to understand how to work with a technology.
A well-structured skill helps the AI provide better, more accurate assistance. Think of it as writing documentation specifically designed for AI consumption.
Frontmatter
The frontmatter is YAML metadata at the top of your skill file, wrapped in ---:
--- name: Supabase Best Practices description: Database, auth, and realtime patterns for Supabase version: 2.1.0 author: supabase tags: [database, auth, postgres, realtime, backend] ---
Fields
| Field | Required | Description |
|---|---|---|
| name | Yes | Human-readable name for the skill |
| description | Yes | Brief description of what the skill teaches |
| version | Yes | Semantic version (e.g., 1.0.0) |
| author | No | Author or organization name |
| tags | No | Array of keywords for discovery |
Recommended Structure
While skills can be structured however you like, this structure has proven effective:
1. Overview
Start with a brief overview of what the technology does and when to use it.
## Overview Supabase is an open source Firebase alternative providing: - PostgreSQL database with instant REST and GraphQL APIs - Authentication with Row Level Security - Real-time subscriptions - Edge Functions for serverless compute - Storage for large files Use Supabase when you need a full backend with minimal setup.
2. Setup / Getting Started
How to initialize and configure the technology.
## Setup
### Installation
```bash
npm install @supabase/supabase-js
```
### Client Initialization
```typescript
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
```3. Best Practices
Core patterns and idioms the AI should follow.
## Best Practices
### Always Use TypeScript Types
Generate and use database types for type safety:
```typescript
// Generate types: npx supabase gen types typescript
import { Database } from './database.types'
const supabase = createClient<Database>(url, key)
// Now queries are fully typed
const { data } = await supabase
.from('users')
.select('id, email, created_at')
.single()
// data is typed as { id: string; email: string; created_at: string } | null
```
### Use Row Level Security (RLS)
Never rely on client-side authorization. Always use RLS policies:
```sql
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Users can only read their own posts
CREATE POLICY "Users can read own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
```4. Common Gotchas
Mistakes to avoid and why.
## Common Gotchas
### Don't Expose Service Role Key
The service role key bypasses RLS. Never use it in client code:
```typescript
// BAD - service key in browser
const supabase = createClient(url, process.env.SUPABASE_SERVICE_KEY!)
// GOOD - anon key in browser
const supabase = createClient(url, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!)
// Service key only in server-side code (API routes, server actions)
```
### Handle Auth State Changes
Don't assume auth state is static:
```typescript
// BAD - only checks once
const user = supabase.auth.getUser()
// GOOD - subscribes to changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_OUT') {
// Handle logout
}
})
```5. Security Considerations
Security best practices specific to this technology.
## Security Considerations
### Validate All Inputs
Even with RLS, validate data before insertion:
```typescript
// In an API route or server action
import { z } from 'zod'
const PostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1).max(10000),
})
export async function createPost(input: unknown) {
const validated = PostSchema.parse(input)
const { data, error } = await supabase
.from('posts')
.insert(validated)
.select()
.single()
}
```Writing Effective Skills
- Be specific and actionable
Tell the AI exactly what to do. "Always use RLS" is vague; showing the exact SQL policy syntax is actionable.
- Include complete, working code examples
Every code snippet should be copy-pasteable and functional. Include imports, types, and error handling.
- Explain the "why"
Don't just show patterns - explain why they're important. This helps the AI make better decisions in novel situations.
- Show good and bad examples
Contrast incorrect patterns with correct ones. Label them clearly so the AI knows which to avoid.
- Keep it focused
One skill per technology. A React skill shouldn't include generic TypeScript patterns - let users install both if needed.
- Cover edge cases
Document error handling, loading states, empty states, and other scenarios the AI might encounter.
- Use consistent formatting
Use clear headers, consistent code block languages, and organized sections. This helps both AI and humans parse the content.
Anti-patterns to Avoid
- Vague instructions
"Write clean code" or "Follow best practices" without specifics isn't helpful.
- Incomplete code snippets
Missing imports, undefined variables, or truncated examples confuse the AI.
- Outdated information
Deprecated APIs or old patterns teach the AI bad habits. Keep skills current.
- Too broad scope
A skill covering "full-stack development" is too broad. Focus on one technology.
- No context for when to apply
Always explain when a pattern applies. The AI needs to know context to make good decisions.
Complete Example
Here's a complete, minimal skill file you can use as a starting template:
---
name: My Technology Skill
description: Best practices for working with My Technology
version: 1.0.0
author: your-username
tags: [my-tech, backend, api]
---
# My Technology Skill
## Overview
My Technology is a tool for [describe what it does]. Use it when you need to [describe use cases].
Key features:
- Feature 1
- Feature 2
- Feature 3
## Setup
### Installation
```bash
npm install my-technology
```
### Basic Configuration
```typescript
import { MyTech } from 'my-technology'
const client = new MyTech({
apiKey: process.env.MY_TECH_API_KEY!,
})
```
## Best Practices
### Always Do This
Explain why this pattern is important.
```typescript
// Good example with full context
const result = await client.doSomething({
option: 'value',
})
```
### Use This Pattern For X
Explain when and why.
```typescript
// Complete, working example
```
## Common Gotchas
### Don't Do This
Explain why this is problematic.
```typescript
// BAD - explain the issue
const bad = wrongApproach()
// GOOD - explain the fix
const good = correctApproach()
```
## Security
### Protect Sensitive Data
```typescript
// Never expose API keys
// Use environment variables
// Validate all inputs
```