chore: comply with agent skills open stardard (#14)

This commit is contained in:
Pedro Rodrigues
2026-01-26 15:22:35 +00:00
committed by GitHub
parent 1712854a51
commit f421451c79
44 changed files with 311 additions and 116 deletions

View File

@@ -2,16 +2,18 @@
Guidance for AI coding agents working with this repository.
> **Note:** `CLAUDE.md` is a symlink to this file.
## Repository Structure
```
skills/
{skill-name}/
metadata.json # Required: skill metadata
AGENTS.md # Generated: compiled rules
rules/
SKILL.md # Required: skill manifest (Agent Skills spec)
AGENTS.md # Generated: compiled references
references/
_sections.md # Required: section definitions
{prefix}-{name}.md # Rule files
{prefix}-{name}.md # Reference files
packages/
skills-build/ # Generic build system for all skills
@@ -31,13 +33,38 @@ npm run check # Format and lint (auto-fix)
## Creating a New Skill
1. Create directory: `mkdir -p skills/{name}/rules`
2. Add `metadata.json` with version, organization, abstract
3. Add `rules/_sections.md` defining sections
4. Add rule files: `{prefix}-{rule-name}.md`
Skills follow the [Agent Skills Open Standard](https://agentskills.io/).
1. Create directory: `mkdir -p skills/{skill-name}/references`
2. Create `SKILL.md` with required frontmatter:
```yaml
---
name: skill-name
description: What this skill does and when to use it.
license: MIT
metadata:
author: your-org
version: "1.0.0"
---
```
3. Add `references/_sections.md` defining sections
4. Add reference files: `{prefix}-{reference-name}.md`
5. Run `npm run build`
## Rule File Format
## SKILL.md Format (Agent Skills Spec)
```yaml
---
name: skill-name # Required: must match directory name
description: Description of skill. # Required: what it does and when to use
license: MIT # Optional: license
metadata: # Optional: arbitrary key-value pairs
author: your-org
version: "1.0.0"
---
```
## Reference File Format
```markdown
---

View File

@@ -3,7 +3,7 @@
Thank you for contributing to Supabase Agent Skills! Here's how to get started:
[1. Getting Started](#getting-started) | [2. Issues](#issues) |
[3. Pull Requests](#pull-requests) | [4. Contributing New Rules](#contributing-new-rules) |
[3. Pull Requests](#pull-requests) | [4. Contributing New References](#contributing-new-references) |
[5. Creating a New Skill](#creating-a-new-skill)
## Getting Started
@@ -14,15 +14,15 @@ before contributing.
## Issues
If you find a typo, have a suggestion for a new skill/rule, or want to improve
existing skills/rules, please create an Issue.
If you find a typo, have a suggestion for a new skill/reference, or want to improve
existing skills/references, please create an Issue.
- Please search
[existing Issues](https://github.com/supabase/agent-skills/issues) before
creating a new one.
- Please include a clear description of the problem or suggestion.
- Tag your issue appropriately (e.g., `bug`, `question`, `enhancement`,
`new-rule`, `new-skill`, `documentation`).
`new-reference`, `new-skill`, `documentation`).
## Pull Requests
@@ -42,18 +42,18 @@ We actively welcome your Pull Requests! Here's what to keep in mind:
Before submitting your PR, please run these checks:
```bash
npm run validate # Check rule format and structure
npm run build # Generate AGENTS.md from rules
npm run validate # Check reference format and structure
npm run build # Generate AGENTS.md from references
```
Both commands must complete successfully.
## Contributing New Rules
## Contributing New References
To add a rule to an existing skill:
To add a reference to an existing skill:
1. Navigate to `skills/{skill-name}/rules/`
2. Copy `_template.md` to `{prefix}-{your-rule-name}.md`
1. Navigate to `skills/{skill-name}/references/`
2. Copy `_template.md` to `{prefix}-{your-reference-name}.md`
3. Fill in the frontmatter (title, impact, tags)
4. Write explanation and examples (Incorrect/Correct)
5. Run validation and build:
@@ -65,26 +65,39 @@ npm run build
## Creating a New Skill
To create an entirely new skill:
Skills follow the [Agent Skills Open Standard](https://agentskills.io/).
### 1. Create the directory structure
```bash
mkdir -p skills/my-skill/rules
mkdir -p skills/my-skill/references
```
### 2. Create metadata.json
### 2. Create SKILL.md
```json
{
"version": "1.0.0",
"organization": "Your Org",
"date": "January 2026",
"abstract": "Brief description of this skill."
}
```yaml
---
name: my-skill
description: Brief description of what this skill does and when to use it.
license: MIT
metadata:
author: your-org
version: "1.0.0"
organization: Your Org
date: January 2026
abstract: Detailed description of this skill for the compiled AGENTS.md.
---
# My Skill
Instructions for agents using this skill.
## References
- https://example.com/docs
```
### 3. Create rules/_sections.md
### 3. Create references/_sections.md
```markdown
## 1. First Category (first)
@@ -96,11 +109,11 @@ mkdir -p skills/my-skill/rules
**Description:** What this category covers.
```
### 4. Create rule files
### 4. Create reference files
Name files as `{prefix}-{rule-name}.md` where prefix matches a section.
Name files as `{prefix}-{reference-name}.md` where prefix matches a section.
Example: `first-example-rule.md` for section "First Category"
Example: `first-example-reference.md` for section "First Category"
### 5. Build
@@ -108,7 +121,7 @@ Example: `first-example-rule.md` for section "First Category"
npm run build
```
The build system auto-discovers skills. No configuration needed.
The build system auto-discovers skills by looking for `SKILL.md` files.
## Questions or Feedback?

View File

@@ -29,9 +29,9 @@ You can also install the skills in this repo as Claude Code plugins
## Available Skills
<details>
<summary><strong>postgres-best-practices</strong></summary>
<summary><strong>supabase-postgres-best-practices</strong></summary>
Postgres performance optimization guidelines from Supabase. Contains rules
Postgres performance optimization guidelines from Supabase. Contains references
across 8 categories, prioritized by impact.
**Use when:**
@@ -76,12 +76,11 @@ Help me add proper indexes to this table
## Skill Structure
Each skill contains:
Each skill follows the [Agent Skills Open Standard](https://agentskills.io/):
- `SKILL.md` - Instructions for the agent
- `AGENTS.md` - Compiled rules document (generated)
- `rules/` - Individual rule files
- `metadata.json` - Version and metadata
- `SKILL.md` - Required skill manifest with frontmatter (name, description, metadata)
- `AGENTS.md` - Compiled references document (generated)
- `references/` - Individual reference files
## License

View File

@@ -45,10 +45,117 @@ function parseSections(rulesDir: string): Section[] {
}
/**
* Load metadata from metadata.json
* Parse SKILL.md frontmatter to extract metadata
*/
function loadMetadata(metadataFile: string, skillName: string): Metadata {
if (!existsSync(metadataFile)) {
function parseSkillFrontmatter(content: string): Record<string, unknown> {
if (!content.startsWith("---")) {
return {};
}
const endIndex = content.indexOf("---", 3);
if (endIndex === -1) {
return {};
}
const frontmatterContent = content.slice(3, endIndex).trim();
const result: Record<string, unknown> = {};
let currentKey = "";
let inMetadata = false;
const metadataObj: Record<string, string> = {};
for (const line of frontmatterContent.split("\n")) {
// Check for metadata block start
if (line.trim() === "metadata:") {
inMetadata = true;
continue;
}
// Handle metadata nested values
if (inMetadata && line.startsWith(" ")) {
const colonIndex = line.indexOf(":");
if (colonIndex !== -1) {
const key = line.slice(0, colonIndex).trim();
let value = line.slice(colonIndex + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
metadataObj[key] = value;
}
continue;
}
// End metadata block when we hit a non-indented line
if (inMetadata && !line.startsWith(" ") && line.trim()) {
inMetadata = false;
result.metadata = metadataObj;
}
// Handle top-level key-value
const colonIndex = line.indexOf(":");
if (colonIndex === -1) continue;
currentKey = line.slice(0, colonIndex).trim();
let value = line.slice(colonIndex + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
if (value) {
result[currentKey] = value;
}
}
// Ensure metadata is captured if file ends in metadata block
if (inMetadata && Object.keys(metadataObj).length > 0) {
result.metadata = metadataObj;
}
return result;
}
/**
* Extract references from SKILL.md body
*/
function extractReferencesFromBody(content: string): string[] {
const references: string[] = [];
const lines = content.split("\n");
let inReferencesSection = false;
for (const line of lines) {
if (line.match(/^##\s+References/i)) {
inReferencesSection = true;
continue;
}
if (inReferencesSection) {
// Stop at next heading
if (line.startsWith("## ")) {
break;
}
// Match list items with URLs
const urlMatch = line.match(/^-\s*(https?:\/\/[^\s]+)/);
if (urlMatch) {
references.push(urlMatch[1]);
}
}
}
return references;
}
/**
* Load metadata from SKILL.md frontmatter (Agent Skills spec compliant)
*/
function loadMetadata(skillFile: string, skillName: string): Metadata {
if (!existsSync(skillFile)) {
return {
version: "1.0.0",
organization: "Supabase",
@@ -61,7 +168,25 @@ function loadMetadata(metadataFile: string, skillName: string): Metadata {
};
}
return JSON.parse(readFileSync(metadataFile, "utf-8"));
const content = readFileSync(skillFile, "utf-8");
const frontmatter = parseSkillFrontmatter(content);
const metadata = (frontmatter.metadata as Record<string, string>) || {};
return {
version: metadata.version || "1.0.0",
organization: metadata.organization || "Supabase",
date:
metadata.date ||
new Date().toLocaleDateString("en-US", {
month: "long",
year: "numeric",
}),
abstract:
metadata.abstract ||
(frontmatter.description as string) ||
`${skillName} guide for developers.`,
references: extractReferencesFromBody(content),
};
}
/**
@@ -104,14 +229,14 @@ function buildSkill(paths: SkillPaths): void {
console.log(`[${paths.name}] Building AGENTS.md...`);
// Load metadata and sections
const metadata = loadMetadata(paths.metadataFile, paths.name);
const sections = parseSections(paths.rulesDir);
const metadata = loadMetadata(paths.skillFile, paths.name);
const sections = parseSections(paths.referencesDir);
const sectionMap = generateSectionMap(sections);
const skillTitle = skillNameToTitle(paths.name);
// Check if rules directory exists
if (!existsSync(paths.rulesDir)) {
console.log(` No rules directory found. Generating empty AGENTS.md.`);
// Check if references directory exists
if (!existsSync(paths.referencesDir)) {
console.log(` No references directory found. Generating empty AGENTS.md.`);
writeFileSync(
paths.agentsOutput,
`# ${skillTitle}\n\nNo rules defined yet.\n`,
@@ -119,19 +244,19 @@ function buildSkill(paths: SkillPaths): void {
return;
}
// Get all rule files
const ruleFiles = readdirSync(paths.rulesDir)
// Get all reference files
const referenceFiles = readdirSync(paths.referencesDir)
.filter((f) => f.endsWith(".md") && !f.startsWith("_"))
.map((f) => join(paths.rulesDir, f));
.map((f) => join(paths.referencesDir, f));
if (ruleFiles.length === 0) {
console.log(` No rule files found. Generating empty AGENTS.md.`);
if (referenceFiles.length === 0) {
console.log(` No reference files found. Generating empty AGENTS.md.`);
}
// Parse and validate all rules
const rules: Rule[] = [];
for (const file of ruleFiles) {
for (const file of referenceFiles) {
const validation = validateRuleFile(file, sectionMap);
if (!validation.valid) {
console.error(` Skipping invalid file ${basename(file)}:`);

View File

@@ -15,18 +15,18 @@ export const SKILLS_ROOT = join(BUILD_DIR, "../../skills");
export interface SkillPaths {
name: string;
skillDir: string;
rulesDir: string;
referencesDir: string;
agentsOutput: string;
metadataFile: string;
skillFile: string;
}
// Discover all valid skills (directories with metadata.json)
// Discover all valid skills (directories with SKILL.md per Agent Skills spec)
export function discoverSkills(): string[] {
if (!existsSync(SKILLS_ROOT)) return [];
return readdirSync(SKILLS_ROOT, { withFileTypes: true })
.filter((d) => d.isDirectory())
.filter((d) => existsSync(join(SKILLS_ROOT, d.name, "metadata.json")))
.filter((d) => existsSync(join(SKILLS_ROOT, d.name, "SKILL.md")))
.map((d) => d.name);
}
@@ -36,16 +36,16 @@ export function getSkillPaths(skillName: string): SkillPaths {
return {
name: skillName,
skillDir,
rulesDir: join(skillDir, "rules"),
referencesDir: join(skillDir, "references"),
agentsOutput: join(skillDir, "AGENTS.md"),
metadataFile: join(skillDir, "metadata.json"),
skillFile: join(skillDir, "SKILL.md"),
};
}
// Validate skill exists
export function validateSkillExists(skillName: string): boolean {
const paths = getSkillPaths(skillName);
return existsSync(paths.metadataFile);
return existsSync(paths.skillFile);
}
// Valid impact levels in priority order

View File

@@ -44,14 +44,14 @@ function isGoodExample(label: string): boolean {
export function validateRuleFile(
filePath: string,
sectionMap?: Record<string, number>,
rulesDir?: string,
referencesDir?: string,
): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
// Generate section map if not provided
if (!sectionMap && rulesDir) {
const sections = parseSections(rulesDir);
if (!sectionMap && referencesDir) {
const sections = parseSections(referencesDir);
sectionMap = generateSectionMap(sections);
} else if (!sectionMap) {
sectionMap = {};
@@ -138,25 +138,25 @@ export function validateRuleFile(
}
/**
* Validate all rule files for a skill
* Validate all reference files for a skill
*/
function validateSkill(paths: SkillPaths): boolean {
console.log(`[${paths.name}] Validating...`);
// Check if rules directory exists
if (!existsSync(paths.rulesDir)) {
console.log(` No rules directory found.`);
// Check if references directory exists
if (!existsSync(paths.referencesDir)) {
console.log(` No references directory found.`);
return true;
}
// Get section map
const sections = parseSections(paths.rulesDir);
const sections = parseSections(paths.referencesDir);
const sectionMap = generateSectionMap(sections);
// Get all markdown files (excluding _ prefixed files)
const files = readdirSync(paths.rulesDir)
const files = readdirSync(paths.referencesDir)
.filter((f) => f.endsWith(".md") && !f.startsWith("_"))
.map((f) => join(paths.rulesDir, f));
.map((f) => join(paths.referencesDir, f));
if (files.length === 0) {
console.log(` No rule files found.`);
@@ -168,7 +168,7 @@ function validateSkill(paths: SkillPaths): boolean {
let hasErrors = false;
for (const file of files) {
const result = validateRuleFile(file, sectionMap, paths.rulesDir);
const result = validateRuleFile(file, sectionMap, paths.referencesDir);
const filename = basename(file);
if (result.valid) {

View File

@@ -1,13 +0,0 @@
{
"version": "1.0.0",
"organization": "Supabase",
"date": "January 2026",
"abstract": "Comprehensive Postgres performance optimization guide for developers using Supabase and Postgres. Contains performance rules across 8 categories, prioritized by impact from critical (query performance, connection management) to incremental (advanced features). Each rule includes detailed explanations, incorrect vs. correct SQL examples, query plan analysis, and specific performance metrics to guide automated optimization and code generation.",
"references": [
"https://www.postgresql.org/docs/current/",
"https://supabase.com/docs",
"https://wiki.postgresql.org/wiki/Performance_Optimization",
"https://supabase.com/docs/guides/database/overview",
"https://supabase.com/docs/guides/auth/row-level-security"
]
}

View File

@@ -1,6 +1,6 @@
# Postgres Best Practices
# Supabase Postgres Best Practices
**Version 1.0.0**
**Version 1.1.0**
Supabase
January 2026

View File

@@ -1,7 +1,7 @@
# Postgres Best Practices - Contributor Guide
# Supabase Postgres Best Practices - Contributor Guide
This repository contains Postgres performance optimization rules optimized for
AI agents and LLMs.
This skill contains Postgres performance optimization references optimized for
AI agents and LLMs. It follows the [Agent Skills Open Standard](https://agentskills.io/).
## Quick Start
@@ -9,14 +9,14 @@ AI agents and LLMs.
# From repository root
npm install
# Validate existing rules
# Validate existing references
npm run validate
# Build AGENTS.md
npm run build
```
## Creating a New Rule
## Creating a New Reference
1. **Choose a section prefix** based on the category:
- `query-` Query Performance (CRITICAL)
@@ -30,7 +30,7 @@ npm run build
2. **Copy the template**:
```bash
cp rules/_template.md rules/query-your-rule-name.md
cp references/_template.md references/query-your-reference-name.md
```
3. **Fill in the content** following the template structure
@@ -43,29 +43,27 @@ npm run build
5. **Review** the generated `AGENTS.md`
## Repository Structure
## Skill Structure
```
skills/postgres-best-practices/
├── SKILL.md # Agent-facing skill manifest
├── AGENTS.md # [GENERATED] Compiled rules document
skills/supabase-postgres-best-practices/
├── SKILL.md # Agent-facing skill manifest (Agent Skills spec)
├── AGENTS.md # [GENERATED] Compiled references document
├── README.md # This file
── metadata.json # Version and metadata
└── rules/
├── _template.md # Rule template
── references/
├── _template.md # Reference template
├── _sections.md # Section definitions
├── _contributing.md # Writing guidelines
└── *.md # Individual rules
└── *.md # Individual references
packages/skills-build/
├── src/ # Generic build system source
── package.json # NPM scripts
└── test-cases.json # [GENERATED] Test artifacts
── package.json # NPM scripts
```
## Rule File Structure
## Reference File Structure
See `rules/_template.md` for the complete template. Key elements:
See `references/_template.md` for the complete template. Key elements:
````markdown
---
@@ -97,7 +95,7 @@ tags: relevant, keywords
```
## Writing Guidelines
See `rules/_contributing.md` for detailed guidelines. Key principles:
See `references/_contributing.md` for detailed guidelines. Key principles:
1. **Show concrete transformations** - "Change X to Y", not abstract advice
2. **Error-first structure** - Show the problem before the solution

View File

@@ -4,7 +4,10 @@ description: Postgres performance optimization and best practices from Supabase.
license: MIT
metadata:
author: supabase
version: "1.0.0"
version: "1.1.0"
organization: Supabase
date: January 2026
abstract: Comprehensive Postgres performance optimization guide for developers using Supabase and Postgres. Contains performance rules across 8 categories, prioritized by impact from critical (query performance, connection management) to incremental (advanced features). Each rule includes detailed explanations, incorrect vs. correct SQL examples, query plan analysis, and specific performance metrics to guide automated optimization and code generation.
---
# Supabase Postgres Best Practices
@@ -39,9 +42,9 @@ Reference these guidelines when:
Read individual rule files for detailed explanations and SQL examples:
```
rules/query-missing-indexes.md
rules/schema-partial-indexes.md
rules/_sections.md
references/query-missing-indexes.md
references/schema-partial-indexes.md
references/_sections.md
```
Each rule file contains:
@@ -55,3 +58,11 @@ Each rule file contains:
## Full Compiled Document
For the complete guide with all rules expanded: `AGENTS.md`
## References
- https://www.postgresql.org/docs/current/
- https://supabase.com/docs
- https://wiki.postgresql.org/wiki/Performance_Optimization
- https://supabase.com/docs/guides/database/overview
- https://supabase.com/docs/guides/auth/row-level-security

View File

@@ -1,7 +1,7 @@
# Writing Guidelines for Postgres Rules
# Writing Guidelines for Postgres References
This document provides guidelines for creating effective Postgres best
practice rules that work well with AI agents and LLMs.
practice references that work well with AI agents and LLMs.
## Key Principles
@@ -89,7 +89,7 @@ CREATE INDEX CONCURRENTLY USERS_EMAIL_IDX ON USERS(EMAIL) WHERE DELETED_AT IS NU
**Default: SQL Only**
Most rules should focus on pure SQL patterns. This keeps examples portable.
Most references should focus on pure SQL patterns. This keeps examples portable.
**Include Application Code When:**
@@ -155,7 +155,7 @@ Reference:
## Review Checklist
Before submitting a rule:
Before submitting a reference:
- [ ] Title is clear and action-oriented
- [ ] Impact level matches the performance gain

View File

@@ -0,0 +1,35 @@
# Reference Sections
Defines the organization of reference documentation. Each section groups related content.
## 1. Getting Started (getting-started)
**Description:** Project setup, connection strings, initial configuration, and first steps with Supabase.
## 2. Auth (auth)
**Description:** Authentication, authorization, Row Level Security, social login, and session management.
## 3. Database (database)
**Description:** Postgres database, migrations, queries, RLS policies, and data modeling.
## 4. Storage (storage)
**Description:** File uploads, buckets, access control, and media handling.
## 5. Edge Functions (functions)
**Description:** Serverless functions, Deno runtime, webhooks, and background tasks.
## 6. Realtime (realtime)
**Description:** Real-time subscriptions, presence, broadcast, and live updates.
## 7. Client Libraries (client)
**Description:** supabase-js SDK, client configuration, and framework integrations.
## 8. CLI & Tools (cli)
**Description:** Supabase CLI, local development, migrations, CI/CD, and MCP integration.