refactor: generic skills build system with auto-discovery (#8)

* refactor: generic skills build system with auto-discovery

- Rename postgres-best-practices-build → skills-build
- Add auto-discovery: scans skills/ for subdirectories with metadata.json
- Build/validate all skills or specific skill with -- argument
- Update root AGENTS.md and CONTRIBUTING.md with new structure
- No configuration needed to add new skills

Usage:
  npm run build                    # Build all skills
  npm run build -- skill-name      # Build specific skill
  npm run validate                 # Validate all skills

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix ci

* more generic impact levels

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Pedro Rodrigues
2026-01-23 15:56:11 +00:00
committed by GitHub
parent 4b49188895
commit bbde7ff5f8
15 changed files with 955 additions and 907 deletions

View File

@@ -1,4 +1,4 @@
name: Postgres Best Practices CI name: Skills CI
on: on:
push: push:
@@ -23,7 +23,7 @@ jobs:
run: npm run ci:check run: npm run ci:check
validate-and-build: validate-and-build:
name: Validate and Build name: Validate and Build Skills
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -33,22 +33,22 @@ jobs:
node-version: '20' node-version: '20'
- name: Install dependencies - name: Install dependencies
working-directory: packages/postgres-best-practices-build working-directory: packages/skills-build
run: npm install run: npm install
- name: Validate rule files - name: Validate all skills
working-directory: packages/postgres-best-practices-build working-directory: packages/skills-build
run: npm run validate run: npm run validate
- name: Build AGENTS.md - name: Build all skills
working-directory: packages/postgres-best-practices-build working-directory: packages/skills-build
run: npm run build run: npm run build
- name: Check for uncommitted changes - name: Check for uncommitted changes
run: | run: |
if [[ -n $(git status --porcelain skills/postgres-best-practices/AGENTS.md) ]]; then if [[ -n $(git status --porcelain 'skills/*/AGENTS.md') ]]; then
echo "Error: AGENTS.md is not up to date" echo "Error: AGENTS.md files are not up to date"
echo "Run 'npm run build' and commit the changes" echo "Run 'npm run build' and commit the changes"
git diff skills/postgres-best-practices/AGENTS.md git diff skills/*/AGENTS.md
exit 1 exit 1
fi fi

151
AGENTS.md
View File

@@ -1,120 +1,71 @@
# AGENTS.md # AGENTS.md
This file provides guidance to AI coding agents (Claude Code, Cursor, Copilot, Guidance for AI coding agents working with this repository.
etc.) when working with code in this repository.
## Repository Overview ## Repository Structure
A collection of Postgres best practices skills for AI coding agents, maintained
by Supabase. Skills are packaged instructions that extend agent capabilities for
database optimization.
## Creating a New Rule
### Directory Structure
``` ```
skills/ skills/
postgres-best-practices/ {skill-name}/
SKILL.md # Required: skill definition metadata.json # Required: skill metadata
AGENTS.md # Generated: compiled rules AGENTS.md # Generated: compiled rules
metadata.json # Required: version and metadata
rules/ rules/
_template.md # Template for new rules _sections.md # Required: section definitions
_sections.md # Section definitions {prefix}-{name}.md # Rule files
_contributing.md # Writing guidelines
{prefix}-{name}.md # Individual rule files
packages/ packages/
postgres-best-practices-build/ skills-build/ # Generic build system for all skills
src/ # Build system source
package.json # NPM scripts
``` ```
### Naming Conventions ## Commands
- **Rule files**: `{prefix}-{kebab-case-name}.md` (e.g.,
`query-missing-indexes.md`)
- **Prefixes determine section**: `query-`, `conn-`, `schema-`, `lock-`,
`security-`, `data-`, `monitor-`, `advanced-`
- **Special files**: Prefixed with `_` (e.g., `_template.md`, `_sections.md`)
### Rule File Format
````markdown
---
title: Clear, Action-Oriented Title
impact: CRITICAL|HIGH|MEDIUM-HIGH|MEDIUM|LOW-MEDIUM|LOW
impactDescription: Quantified benefit (e.g., "10-100x faster")
tags: relevant, keywords
---
## [Title]
[1-2 sentence explanation]
**Incorrect (description):**
```sql
-- Comment explaining what's wrong
[Bad SQL example]
```
**Correct (description):**
```sql
-- Comment explaining why this is better
[Good SQL example]
```
````
### Best Practices for Context Efficiency
Skills are loaded on-demand. To minimize context usage:
- **Keep rules focused** - One optimization pattern per rule
- **Write specific titles** - Helps the agent know exactly when to apply
- **Use progressive disclosure** - Reference supporting files when needed
- **Self-contained examples** - Complete, runnable SQL
- **Quantify impact** - Include specific metrics (10x faster, 50% smaller)
### Build System
After creating or updating rules:
```bash ```bash
cd packages/postgres-best-practices-build npm run build # Build all skills
npm install npm run build -- {skill-name} # Build specific skill
npm run validate # Check rule format npm run validate # Validate all skills
npm run build # Generate AGENTS.md npm run validate -- {skill-name} # Validate specific skill
``` ```
**Automatic Section Ordering**: The build system automatically reads section order ## Creating a New Skill
from `_sections.md`. To reorder sections (e.g., promoting Security from MEDIUM-HIGH
to CRITICAL priority), simply edit the section numbers in `_sections.md` and
rebuild. The section mapping is generated dynamically, so manual updates to
`config.ts` are no longer needed.
### Impact Levels 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`
5. Run `npm run build`
| Level | Improvement | Examples | ## Rule File Format
| ----------- | ----------- | -------------------------------------- |
| CRITICAL | 10-100x | Missing indexes, connection exhaustion |
| HIGH | 5-20x | Wrong index types, poor partitioning |
| MEDIUM-HIGH | 2-5x | N+1 queries, RLS optimization |
| MEDIUM | 1.5-3x | Redundant indexes, stale statistics |
| LOW-MEDIUM | 1.2-2x | VACUUM tuning, config tweaks |
| LOW | Incremental | Advanced patterns, edge cases |
### File Prefix to Section Mapping ```markdown
---
title: Action-Oriented Title
impact: CRITICAL|HIGH|MEDIUM-HIGH|MEDIUM|LOW-MEDIUM|LOW
impactDescription: Quantified benefit
tags: keywords
---
| Prefix | Section | Priority | ## Title
| ----------- | ------------------------ | --------------- |
| `query-` | Query Performance | 1 (CRITICAL) | 1-2 sentence explanation.
| `conn-` | Connection Management | 2 (CRITICAL) |
| `security-` | Security & RLS | 3 (CRITICAL) | **Incorrect:**
| `schema-` | Schema Design | 4 (HIGH) | \`\`\`sql
| `lock-` | Concurrency & Locking | 5 (MEDIUM-HIGH) | -- bad example
| `data-` | Data Access Patterns | 6 (MEDIUM) | \`\`\`
| `monitor-` | Monitoring & Diagnostics | 7 (LOW-MEDIUM) |
| `advanced-` | Advanced Features | 8 (LOW) | **Correct:**
\`\`\`sql
-- good example
\`\`\`
```
## Impact Levels
| Level | Improvement | Use For |
| ----------- | ----------------------------- | ---------------------------------------------------------- |
| CRITICAL | 10-100x or prevents failure | Security vulnerabilities, data loss, breaking changes |
| HIGH | 5-20x or major quality gain | Architecture decisions, core functionality, scalability |
| MEDIUM-HIGH | 2-5x or significant benefit | Design patterns, common anti-patterns, reliability |
| MEDIUM | 1.5-3x or noticeable gain | Optimization, best practices, maintainability |
| LOW-MEDIUM | 1.2-2x or minor benefit | Configuration, tooling, code organization |
| LOW | Incremental or edge cases | Advanced techniques, rare scenarios, polish |

View File

@@ -2,10 +2,9 @@
Thank you for contributing to Supabase Agent Skills! Here's how to get started: Thank you for contributing to Supabase Agent Skills! Here's how to get started:
[1. Getting Started](#getting-started) [2. Issues](#issues) [1. Getting Started](#getting-started) | [2. Issues](#issues) |
[3. Pull Requests](#pull-requests) [3. Pull Requests](#pull-requests) | [4. Contributing New Rules](#contributing-new-rules) |
[4. Contributing New Rules](#contributing-new-rules) [5. Creating a New Skill](#creating-a-new-skill)
[5. Extending Existing Skills](#extending-existing-skills)
## Getting Started ## Getting Started
@@ -49,6 +48,68 @@ npm run build # Generate AGENTS.md from rules
Both commands must complete successfully. Both commands must complete successfully.
## Contributing New Rules
To add a rule to an existing skill:
1. Navigate to `skills/{skill-name}/rules/`
2. Copy `_template.md` to `{prefix}-{your-rule-name}.md`
3. Fill in the frontmatter (title, impact, tags)
4. Write explanation and examples (Incorrect/Correct)
5. Run validation and build:
```bash
npm run validate
npm run build
```
## Creating a New Skill
To create an entirely new skill:
### 1. Create the directory structure
```bash
mkdir -p skills/my-skill/rules
```
### 2. Create metadata.json
```json
{
"version": "1.0.0",
"organization": "Your Org",
"date": "January 2026",
"abstract": "Brief description of this skill."
}
```
### 3. Create rules/_sections.md
```markdown
## 1. First Category (first)
**Impact:** HIGH
**Description:** What this category covers.
## 2. Second Category (second)
**Impact:** MEDIUM
**Description:** What this category covers.
```
### 4. Create rule files
Name files as `{prefix}-{rule-name}.md` where prefix matches a section.
Example: `first-example-rule.md` for section "First Category"
### 5. Build
```bash
npm run build
```
The build system auto-discovers skills. No configuration needed.
## Questions or Feedback? ## Questions or Feedback?
- Open an Issue for bugs or suggestions - Open an Issue for bugs or suggestions

View File

@@ -5,8 +5,8 @@
"license": "MIT", "license": "MIT",
"description": "Official Supabase agent skills", "description": "Official Supabase agent skills",
"scripts": { "scripts": {
"build": "npm --prefix packages/postgres-best-practices-build run build", "build": "npm --prefix packages/skills-build run build",
"validate": "npm --prefix packages/postgres-best-practices-build run validate", "validate": "npm --prefix packages/skills-build run validate",
"format": "biome format --write .", "format": "biome format --write .",
"format:check": "biome format .", "format:check": "biome format .",
"lint": "biome lint --write .", "lint": "biome lint --write .",

View File

@@ -1,590 +0,0 @@
{
"name": "postgresql-best-practices-build",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "postgresql-best-practices-build",
"version": "1.0.0",
"devDependencies": {
"@types/node": "^20.10.0",
"tsx": "^4.7.0",
"typescript": "^5.3.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
"integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
"integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
"integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
"integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
"integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
"integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
"integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
"integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
"integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
"integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
"integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
"integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
"integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
"integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
"integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
"integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
"integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
"integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
"integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
"integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
"integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
"integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
"integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
"integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
"integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
"integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@types/node": {
"version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/esbuild": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
"integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.2",
"@esbuild/android-arm": "0.27.2",
"@esbuild/android-arm64": "0.27.2",
"@esbuild/android-x64": "0.27.2",
"@esbuild/darwin-arm64": "0.27.2",
"@esbuild/darwin-x64": "0.27.2",
"@esbuild/freebsd-arm64": "0.27.2",
"@esbuild/freebsd-x64": "0.27.2",
"@esbuild/linux-arm": "0.27.2",
"@esbuild/linux-arm64": "0.27.2",
"@esbuild/linux-ia32": "0.27.2",
"@esbuild/linux-loong64": "0.27.2",
"@esbuild/linux-mips64el": "0.27.2",
"@esbuild/linux-ppc64": "0.27.2",
"@esbuild/linux-riscv64": "0.27.2",
"@esbuild/linux-s390x": "0.27.2",
"@esbuild/linux-x64": "0.27.2",
"@esbuild/netbsd-arm64": "0.27.2",
"@esbuild/netbsd-x64": "0.27.2",
"@esbuild/openbsd-arm64": "0.27.2",
"@esbuild/openbsd-x64": "0.27.2",
"@esbuild/openharmony-arm64": "0.27.2",
"@esbuild/sunos-x64": "0.27.2",
"@esbuild/win32-arm64": "0.27.2",
"@esbuild/win32-ia32": "0.27.2",
"@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-tsconfig": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/tsx": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -1,47 +0,0 @@
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Build package directory
export const BUILD_DIR = join(__dirname, "..");
// Skill directory (relative to build package)
export const SKILL_DIR = join(
BUILD_DIR,
"../../skills/postgres-best-practices",
);
// Rules directory
export const RULES_DIR = join(SKILL_DIR, "rules");
// Output files
export const AGENTS_OUTPUT = join(SKILL_DIR, "AGENTS.md");
export const METADATA_FILE = join(SKILL_DIR, "metadata.json");
// Section prefix to number mapping (DEPRECATED)
// This is kept as a fallback, but the build system now generates
// the section map dynamically from _sections.md.
// To reorder sections, simply change the order in _sections.md.
export const SECTION_MAP: Record<string, number> = {
query: 1,
conn: 2,
connection: 2,
security: 3,
schema: 4,
lock: 5,
data: 6,
monitor: 7,
advanced: 8,
};
// Valid impact levels in priority order
export const IMPACT_LEVELS = [
"CRITICAL",
"HIGH",
"MEDIUM-HIGH",
"MEDIUM",
"LOW-MEDIUM",
"LOW",
] as const;

591
packages/skills-build/package-lock.json generated Normal file
View File

@@ -0,0 +1,591 @@
{
"name": "skills-build",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "skills-build",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^20.10.0",
"tsx": "^4.7.0",
"typescript": "^5.3.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
"integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
"integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
"integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
"integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
"integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
"integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
"integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
"integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
"integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
"integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
"integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
"integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
"integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
"integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
"integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
"integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
"integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
"integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
"integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
"integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
"integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
"integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
"integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
"integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
"integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
"integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@types/node": {
"version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/esbuild": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
"integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.2",
"@esbuild/android-arm": "0.27.2",
"@esbuild/android-arm64": "0.27.2",
"@esbuild/android-x64": "0.27.2",
"@esbuild/darwin-arm64": "0.27.2",
"@esbuild/darwin-x64": "0.27.2",
"@esbuild/freebsd-arm64": "0.27.2",
"@esbuild/freebsd-x64": "0.27.2",
"@esbuild/linux-arm": "0.27.2",
"@esbuild/linux-arm64": "0.27.2",
"@esbuild/linux-ia32": "0.27.2",
"@esbuild/linux-loong64": "0.27.2",
"@esbuild/linux-mips64el": "0.27.2",
"@esbuild/linux-ppc64": "0.27.2",
"@esbuild/linux-riscv64": "0.27.2",
"@esbuild/linux-s390x": "0.27.2",
"@esbuild/linux-x64": "0.27.2",
"@esbuild/netbsd-arm64": "0.27.2",
"@esbuild/netbsd-x64": "0.27.2",
"@esbuild/openbsd-arm64": "0.27.2",
"@esbuild/openbsd-x64": "0.27.2",
"@esbuild/openharmony-arm64": "0.27.2",
"@esbuild/sunos-x64": "0.27.2",
"@esbuild/win32-arm64": "0.27.2",
"@esbuild/win32-ia32": "0.27.2",
"@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-tsconfig": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/tsx": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -1,10 +1,10 @@
{ {
"name": "postgres-best-practices-build", "name": "skills-build",
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"author": "Supabase", "author": "Supabase",
"license": "MIT", "license": "MIT",
"description": "Build system for Supabase agent skills", "description": "Generic build system for Supabase agent skills",
"scripts": { "scripts": {
"build": "tsx src/build.ts", "build": "tsx src/build.ts",
"validate": "tsx src/validate.ts", "validate": "tsx src/validate.ts",

View File

@@ -1,6 +1,11 @@
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import { basename, join } from "node:path"; import { basename, join } from "node:path";
import { AGENTS_OUTPUT, METADATA_FILE, RULES_DIR } from "./config.js"; import {
discoverSkills,
getSkillPaths,
type SkillPaths,
validateSkillExists,
} from "./config.js";
import { parseRuleFile } from "./parser.js"; import { parseRuleFile } from "./parser.js";
import type { Metadata, Rule, Section } from "./types.js"; import type { Metadata, Rule, Section } from "./types.js";
import { validateRuleFile } from "./validate.js"; import { validateRuleFile } from "./validate.js";
@@ -8,11 +13,11 @@ import { validateRuleFile } from "./validate.js";
/** /**
* Parse section definitions from _sections.md * Parse section definitions from _sections.md
*/ */
function parseSections(): Section[] { function parseSections(rulesDir: string): Section[] {
const sectionsFile = join(RULES_DIR, "_sections.md"); const sectionsFile = join(rulesDir, "_sections.md");
if (!existsSync(sectionsFile)) { if (!existsSync(sectionsFile)) {
console.warn("Warning: _sections.md not found, using default sections"); console.warn("Warning: _sections.md not found, using empty sections");
return getDefaultSections(); return [];
} }
const content = readFileSync(sectionsFile, "utf-8"); const content = readFileSync(sectionsFile, "utf-8");
@@ -36,78 +41,14 @@ function parseSections(): Section[] {
}); });
} }
return sections.length > 0 ? sections : getDefaultSections(); return sections;
}
/**
* Default sections if _sections.md is missing or unparseable
*/
function getDefaultSections(): Section[] {
return [
{
number: 1,
title: "Query Performance",
prefix: "query",
impact: "CRITICAL",
description: "Slow queries, missing indexes, inefficient plans",
},
{
number: 2,
title: "Connection Management",
prefix: "conn",
impact: "CRITICAL",
description: "Pooling, limits, serverless strategies",
},
{
number: 3,
title: "Security & RLS",
prefix: "security",
impact: "CRITICAL",
description: "Row-Level Security, privileges, auth patterns",
},
{
number: 4,
title: "Schema Design",
prefix: "schema",
impact: "HIGH",
description: "Table design, indexes, partitioning, data types",
},
{
number: 5,
title: "Concurrency & Locking",
prefix: "lock",
impact: "MEDIUM-HIGH",
description: "Transactions, isolation, deadlocks",
},
{
number: 6,
title: "Data Access Patterns",
prefix: "data",
impact: "MEDIUM",
description: "N+1 queries, batch operations, pagination",
},
{
number: 7,
title: "Monitoring & Diagnostics",
prefix: "monitor",
impact: "LOW-MEDIUM",
description: "pg_stat_statements, EXPLAIN, metrics",
},
{
number: 8,
title: "Advanced Features",
prefix: "advanced",
impact: "LOW",
description: "Full-text search, JSONB, extensions",
},
];
} }
/** /**
* Load metadata from metadata.json * Load metadata from metadata.json
*/ */
function loadMetadata(): Metadata { function loadMetadata(metadataFile: string, skillName: string): Metadata {
if (!existsSync(METADATA_FILE)) { if (!existsSync(metadataFile)) {
return { return {
version: "1.0.0", version: "1.0.0",
organization: "Supabase", organization: "Supabase",
@@ -115,12 +56,12 @@ function loadMetadata(): Metadata {
month: "long", month: "long",
year: "numeric", year: "numeric",
}), }),
abstract: "Postgres performance optimization guide for developers.", abstract: `${skillName} guide for developers.`,
references: [], references: [],
}; };
} }
return JSON.parse(readFileSync(METADATA_FILE, "utf-8")); return JSON.parse(readFileSync(metadataFile, "utf-8"));
} }
/** /**
@@ -133,6 +74,16 @@ function toAnchor(text: string): string {
.replace(/\s+/g, "-"); .replace(/\s+/g, "-");
} }
/**
* Convert skill name to title (e.g., "postgres-best-practices" -> "Postgres Best Practices")
*/
function skillNameToTitle(skillName: string): string {
return skillName
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
/** /**
* Generate SECTION_MAP from parsed sections * Generate SECTION_MAP from parsed sections
*/ */
@@ -147,23 +98,34 @@ export function generateSectionMap(
} }
/** /**
* Build AGENTS.md from all rule files * Build AGENTS.md for a specific skill
*/ */
function buildAgents(): void { function buildSkill(paths: SkillPaths): void {
console.log("Building AGENTS.md...\n"); console.log(`[${paths.name}] Building AGENTS.md...`);
// Load metadata and sections // Load metadata and sections
const metadata = loadMetadata(); const metadata = loadMetadata(paths.metadataFile, paths.name);
const sections = parseSections(); const sections = parseSections(paths.rulesDir);
const sectionMap = generateSectionMap(sections); 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.`);
writeFileSync(
paths.agentsOutput,
`# ${skillTitle}\n\nNo rules defined yet.\n`,
);
return;
}
// Get all rule files // Get all rule files
const ruleFiles = readdirSync(RULES_DIR) const ruleFiles = readdirSync(paths.rulesDir)
.filter((f) => f.endsWith(".md") && !f.startsWith("_")) .filter((f) => f.endsWith(".md") && !f.startsWith("_"))
.map((f) => join(RULES_DIR, f)); .map((f) => join(paths.rulesDir, f));
if (ruleFiles.length === 0) { if (ruleFiles.length === 0) {
console.log("No rule files found. Generating empty AGENTS.md template."); console.log(` No rule files found. Generating empty AGENTS.md.`);
} }
// Parse and validate all rules // Parse and validate all rules
@@ -206,7 +168,7 @@ function buildAgents(): void {
const output: string[] = []; const output: string[] = [];
// Header // Header
output.push("# Postgres Best Practices\n"); output.push(`# ${skillTitle}\n`);
output.push(`**Version ${metadata.version}**`); output.push(`**Version ${metadata.version}**`);
output.push(`${metadata.organization}`); output.push(`${metadata.organization}`);
output.push(`${metadata.date}\n`); output.push(`${metadata.date}\n`);
@@ -307,8 +269,8 @@ function buildAgents(): void {
} }
// Write output // Write output
writeFileSync(AGENTS_OUTPUT, output.join("\n")); writeFileSync(paths.agentsOutput, output.join("\n"));
console.log(`Generated: ${AGENTS_OUTPUT}`); console.log(` Generated: ${paths.agentsOutput}`);
console.log(` Total rules: ${rules.length}`); console.log(` Total rules: ${rules.length}`);
} }
@@ -318,7 +280,35 @@ const isMainModule =
process.argv[1]?.endsWith("build.js"); process.argv[1]?.endsWith("build.js");
if (isMainModule) { if (isMainModule) {
buildAgents(); const targetSkill = process.argv[2];
if (targetSkill) {
// Build specific skill
if (!validateSkillExists(targetSkill)) {
console.error(`Error: Skill "${targetSkill}" not found in skills/`);
const available = discoverSkills();
if (available.length > 0) {
console.error(`Available skills: ${available.join(", ")}`);
}
process.exit(1);
}
buildSkill(getSkillPaths(targetSkill));
} else {
// Build all skills
const skills = discoverSkills();
if (skills.length === 0) {
console.log("No skills found in skills/ directory.");
process.exit(0);
} }
export { buildAgents, parseSections }; console.log(`Found ${skills.length} skill(s): ${skills.join(", ")}\n`);
for (const skill of skills) {
buildSkill(getSkillPaths(skill));
console.log("");
}
}
console.log("✅ Done!");
}
export { buildSkill, parseSections };

View File

@@ -0,0 +1,59 @@
import { existsSync, readdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Build package directory
export const BUILD_DIR = join(__dirname, "..");
// Skills root directory
export const SKILLS_ROOT = join(BUILD_DIR, "../../skills");
// Skill paths interface
export interface SkillPaths {
name: string;
skillDir: string;
rulesDir: string;
agentsOutput: string;
metadataFile: string;
}
// Discover all valid skills (directories with metadata.json)
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")))
.map((d) => d.name);
}
// Get paths for a specific skill
export function getSkillPaths(skillName: string): SkillPaths {
const skillDir = join(SKILLS_ROOT, skillName);
return {
name: skillName,
skillDir,
rulesDir: join(skillDir, "rules"),
agentsOutput: join(skillDir, "AGENTS.md"),
metadataFile: join(skillDir, "metadata.json"),
};
}
// Validate skill exists
export function validateSkillExists(skillName: string): boolean {
const paths = getSkillPaths(skillName);
return existsSync(paths.metadataFile);
}
// Valid impact levels in priority order
export const IMPACT_LEVELS = [
"CRITICAL",
"HIGH",
"MEDIUM-HIGH",
"MEDIUM",
"LOW-MEDIUM",
"LOW",
] as const;

View File

@@ -1,7 +1,13 @@
import { readdirSync } from "node:fs"; import { existsSync, readdirSync } from "node:fs";
import { basename, join } from "node:path"; import { basename, join } from "node:path";
import { generateSectionMap, parseSections } from "./build.js"; import { generateSectionMap, parseSections } from "./build.js";
import { IMPACT_LEVELS, RULES_DIR } from "./config.js"; import {
discoverSkills,
getSkillPaths,
IMPACT_LEVELS,
type SkillPaths,
validateSkillExists,
} from "./config.js";
import { parseRuleFile } from "./parser.js"; import { parseRuleFile } from "./parser.js";
import type { ValidationResult } from "./types.js"; import type { ValidationResult } from "./types.js";
@@ -38,14 +44,17 @@ function isGoodExample(label: string): boolean {
export function validateRuleFile( export function validateRuleFile(
filePath: string, filePath: string,
sectionMap?: Record<string, number>, sectionMap?: Record<string, number>,
rulesDir?: string,
): ValidationResult { ): ValidationResult {
const errors: string[] = []; const errors: string[] = [];
const warnings: string[] = []; const warnings: string[] = [];
// Generate section map if not provided // Generate section map if not provided
if (!sectionMap) { if (!sectionMap && rulesDir) {
const sections = parseSections(); const sections = parseSections(rulesDir);
sectionMap = generateSectionMap(sections); sectionMap = generateSectionMap(sections);
} else if (!sectionMap) {
sectionMap = {};
} }
const result = parseRuleFile(filePath, sectionMap); const result = parseRuleFile(filePath, sectionMap);
@@ -129,62 +138,45 @@ export function validateRuleFile(
} }
/** /**
* Validate all rule files in the rules directory * Validate all rule files for a skill
*/ */
export function validateAllRules(): { function validateSkill(paths: SkillPaths): boolean {
totalFiles: number; console.log(`[${paths.name}] Validating...`);
validFiles: number;
invalidFiles: number; // Check if rules directory exists
results: Map<string, ValidationResult>; if (!existsSync(paths.rulesDir)) {
} { console.log(` No rules directory found.`);
const results = new Map<string, ValidationResult>(); return true;
let validFiles = 0; }
let invalidFiles = 0;
// Get section map
const sections = parseSections(paths.rulesDir);
const sectionMap = generateSectionMap(sections);
// Get all markdown files (excluding _ prefixed files) // Get all markdown files (excluding _ prefixed files)
const files = readdirSync(RULES_DIR) const files = readdirSync(paths.rulesDir)
.filter((f) => f.endsWith(".md") && !f.startsWith("_")) .filter((f) => f.endsWith(".md") && !f.startsWith("_"))
.map((f) => join(RULES_DIR, f)); .map((f) => join(paths.rulesDir, f));
if (files.length === 0) {
console.log(` No rule files found.`);
return true;
}
let validFiles = 0;
let invalidFiles = 0;
let hasErrors = false;
for (const file of files) { for (const file of files) {
const result = validateRuleFile(file); const result = validateRuleFile(file, sectionMap, paths.rulesDir);
results.set(basename(file), result); const filename = basename(file);
if (result.valid) { if (result.valid) {
validFiles++; validFiles++;
} else { } else {
invalidFiles++; invalidFiles++;
} }
}
return {
totalFiles: files.length,
validFiles,
invalidFiles,
results,
};
}
// Run validation when executed directly
const isMainModule =
process.argv[1]?.endsWith("validate.ts") ||
process.argv[1]?.endsWith("validate.js");
if (isMainModule) {
console.log("Validating Postgres best practices rules...\n");
const { totalFiles, validFiles, invalidFiles, results } = validateAllRules();
if (totalFiles === 0) {
console.log("No rule files found (this is expected for initial setup).");
console.log("Create rule files in: skills/postgres-best-practices/rules/");
console.log("Use the _template.md as a starting point.\n");
process.exit(0);
}
let hasErrors = false;
for (const [filename, result] of results) {
if (!result.valid || result.warnings.length > 0) { if (!result.valid || result.warnings.length > 0) {
console.log(`\n ${filename}:`); console.log(`\n ${filename}:`);
@@ -199,16 +191,58 @@ if (isMainModule) {
} }
} }
console.log(`\n${"=".repeat(50)}`);
console.log( console.log(
`Total: ${totalFiles} files | Valid: ${validFiles} | Invalid: ${invalidFiles}`, `\n Total: ${files.length} | Valid: ${validFiles} | Invalid: ${invalidFiles}`,
); );
if (hasErrors) { return !hasErrors;
console.log("\nValidation failed. Please fix the errors above."); }
// Run validation when executed directly
const isMainModule =
process.argv[1]?.endsWith("validate.ts") ||
process.argv[1]?.endsWith("validate.js");
if (isMainModule) {
const targetSkill = process.argv[2];
if (targetSkill) {
// Validate specific skill
if (!validateSkillExists(targetSkill)) {
console.error(`Error: Skill "${targetSkill}" not found in skills/`);
const available = discoverSkills();
if (available.length > 0) {
console.error(`Available skills: ${available.join(", ")}`);
}
process.exit(1); process.exit(1);
}
const valid = validateSkill(getSkillPaths(targetSkill));
console.log(valid ? "\n✅ Validation passed!" : "\n❌ Validation failed.");
process.exit(valid ? 0 : 1);
} else { } else {
console.log("\nValidation passed!"); // Validate all skills
const skills = discoverSkills();
if (skills.length === 0) {
console.log("No skills found in skills/ directory.");
process.exit(0); process.exit(0);
} }
console.log(`Found ${skills.length} skill(s): ${skills.join(", ")}\n`);
let allValid = true;
for (const skill of skills) {
if (!validateSkill(getSkillPaths(skill))) {
allValid = false;
} }
console.log("");
}
console.log(
allValid ? "✅ All validations passed!" : "❌ Some validations failed.",
);
process.exit(allValid ? 0 : 1);
}
}
export { validateSkill };

View File

@@ -6,8 +6,7 @@ AI agents and LLMs.
## Quick Start ## Quick Start
```bash ```bash
# Install dependencies # From repository root
cd packages/postgres-best-practices-build
npm install npm install
# Validate existing rules # Validate existing rules
@@ -58,8 +57,8 @@ skills/postgres-best-practices/
├── _contributing.md # Writing guidelines ├── _contributing.md # Writing guidelines
└── *.md # Individual rules └── *.md # Individual rules
packages/postgres-best-practices-build/ packages/skills-build/
├── src/ # Build system source ├── src/ # Generic build system source
├── package.json # NPM scripts ├── package.json # NPM scripts
└── test-cases.json # [GENERATED] Test artifacts └── test-cases.json # [GENERATED] Test artifacts
``` ```