From 3773e5e479d263e6d4532e5a186ae18b48d7262e Mon Sep 17 00:00:00 2001 From: Ivan Willig Date: Tue, 2 Dec 2025 20:00:46 -0500 Subject: [PATCH] Add skill for using the SQLite database directly --- .gitattributes | 1 + clojure-skills.db | Bin 2072576 -> 2109440 bytes .../tooling/clojure_skills_database_schema.md | 924 ++++++++++++++++++ 3 files changed, 925 insertions(+) create mode 100644 .gitattributes create mode 100644 skills/tooling/clojure_skills_database_schema.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5628d16 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sqlite diff=sqlite3 diff --git a/clojure-skills.db b/clojure-skills.db index adce4bbd7f6f2fd6c43ca99d938ff1f1f5190e93..9eea824b7837b2e1208f3dde110fc380c6b3c957 100644 GIT binary patch delta 38333 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 YCpJ?o+7*qr;}2HUKUmRlwjzG?zf6+b!vFvP diff --git a/skills/tooling/clojure_skills_database_schema.md b/skills/tooling/clojure_skills_database_schema.md new file mode 100644 index 0000000..77ac94e --- /dev/null +++ b/skills/tooling/clojure_skills_database_schema.md @@ -0,0 +1,924 @@ +--- +name: clojure_skills_database_schema +title: Clojure Skills Database Schema and SQL Querying +description: | + Deep dive into the clojure-skills SQLite database schema for LLM agents. Learn how to + query the skills tables directly using SQL, understand the FTS5 full-text search system, + and leverage the database structure for advanced skill discovery. Focuses on SQL queries + only - no database modification, no Clojure code. Use when you need to understand the + database internals or write custom SQL queries for skill discovery. +--- + +# Clojure Skills Database Schema and SQL Querying + +## Quick Start + +The clojure-skills database is a SQLite database with FTS5 full-text search, storing 70+ Clojure skills with rich metadata. This guide shows you how to query it using SQL. + +```sql +-- View schema +.schema skills + +-- Simple query +SELECT name, category +FROM skills +WHERE category = 'libraries/database'; + +-- Full-text search +SELECT s.name, s.category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'validation' +LIMIT 10; + +-- Count skills by category +SELECT category, COUNT(*) as count +FROM skills +GROUP BY category +ORDER BY count DESC; +``` + +**Key benefits:** +- Direct SQL access for advanced queries +- FTS5 full-text search with ranking +- Rich metadata (tokens, size, timestamps) +- Category hierarchy for organization +- Fast lookups with proper indexing + +## Database Schema + +### Skills Table + +The `skills` table is the primary table storing all skill metadata and content: + +```sql +CREATE TABLE skills ( + -- Primary key + id INTEGER PRIMARY KEY AUTOINCREMENT, + + -- File identification + path TEXT NOT NULL UNIQUE, -- Full path: "skills/libraries/database/malli.md" + file_hash TEXT NOT NULL, -- SHA-256 hash for change detection + + -- Classification + category TEXT NOT NULL, -- Hierarchical: "libraries/database" + name TEXT NOT NULL, -- Unique identifier: "malli" + + -- Metadata (optional) + title TEXT, -- Human-readable title + description TEXT, -- Brief description (from YAML frontmatter) + + -- Content + content TEXT NOT NULL, -- Full markdown content (including frontmatter) + + -- Statistics + size_bytes INTEGER NOT NULL, -- File size in bytes + token_count INTEGER, -- Estimated token count (~chars/4) + + -- Timestamps + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +-- Indexes for fast lookups +CREATE INDEX idx_skills_category ON skills(category); +CREATE INDEX idx_skills_name ON skills(name); +CREATE INDEX idx_skills_hash ON skills(file_hash); +``` + +**Field Details:** + +- **id** - Auto-incrementing integer, used internally and in FTS5 rowid +- **path** - Absolute file path from project root (UNIQUE constraint) +- **category** - Hierarchical slash-separated category (e.g., "libraries/database") +- **name** - Skill identifier derived from filename without .md extension +- **title** - Optional, from YAML frontmatter "title" field +- **description** - Optional, from YAML frontmatter "description" field +- **content** - Full file content including YAML frontmatter +- **file_hash** - SHA-256 hex string for detecting file changes +- **size_bytes** - File size on disk +- **token_count** - Estimated tokens using simple char count / 4 +- **created_at** - ISO 8601 timestamp of first sync +- **updated_at** - ISO 8601 timestamp of last update + +### Skills FTS5 Table + +Full-text search table automatically synchronized with skills table: + +```sql +CREATE VIRTUAL TABLE skills_fts USING fts5( + path, + category, + name, + title, + description, + content, + content='skills', -- Linked to skills table + content_rowid='id' -- Maps to skills.id +); +``` + +**How FTS5 Works:** + +- **Virtual table** - Not a real table, query interface to full-text index +- **content='skills'** - Tells FTS5 to pull data from skills table +- **content_rowid='id'** - Maps FTS5 rowid to skills.id for joins +- **Automatic sync** - Changes to skills table automatically update FTS5 +- **Indexed fields** - All 6 fields are searchable and contribute to ranking + +**Key FTS5 features:** +- Fast full-text search (O(log n) lookups) +- Relevance ranking via BM25 algorithm +- Snippet extraction with context highlighting +- Boolean queries (AND, OR, NOT, phrase search) +- Prefix matching (term*) + +## Category Hierarchy + +Skills are organized in a hierarchical category structure using slash-separated paths: + +### Top-Level Categories + +``` +language/ - Core Clojure language concepts (3 skills) +clojure_mcp/ - MCP (Model Context Protocol) integration (1 skill) +http_servers/ - HTTP server implementations (3 skills) +libraries/ - Third-party libraries (50+ skills, nested) +testing/ - Test frameworks and tools (10 skills) +tooling/ - Development tools (18 skills) +style/ - Code style guidelines (13 skills) +llm_agent/ - LLM agent patterns (2 skills) +internal/ - Internal documentation (1 skill) +``` + +### Library Subcategories (libraries/*) + +The `libraries/` category has extensive subcategories: + +``` +libraries/async - Async programming (core.async, manifold, promesa) +libraries/caching - Caching solutions (core.cache) +libraries/cli - CLI tools (cli-matic) +libraries/component_system - Component frameworks (component) +libraries/configuration - Configuration management (environ, config) +libraries/data_formats - Parsers (JSON, YAML, XML, EDN) +libraries/data_structures - Data structure utilities (editscript) +libraries/data_validation - Schema validation (malli, spec, schema) +libraries/database - Database libraries (next.jdbc, honeysql, ragtime) +libraries/diagramming - Diagram generation (salt) +libraries/frontend - Frontend libraries (datastar) +libraries/functional - Functional utilities (cats, failjure, lentes, meander) +libraries/graphql - GraphQL (lacinia) +libraries/html - HTML generation (hiccup) +libraries/http_client - HTTP clients (clj-http-lite) +libraries/java_interop - Java interop (FFM) +libraries/logging - Logging (cambium, mulog) +libraries/metadata - Metadata tools (metazoa) +libraries/observability - Observability (clj-otel) +libraries/parsing - Parser generators (antlr) +libraries/rest_api - REST API frameworks (reitit, liberator, bidi) +libraries/security - Security (buddy) +libraries/terminal - Terminal UIs (bling) +``` + +### Querying by Category + +```sql +-- Top-level categories with counts +SELECT SUBSTR(category, 1, INSTR(category || '/', '/') - 1) as top_category, + COUNT(*) as count +FROM skills +GROUP BY top_category +ORDER BY count DESC; + +-- All skills in a specific category +SELECT name, title +FROM skills +WHERE category = 'libraries/database' +ORDER BY name; + +-- All library subcategories +SELECT DISTINCT category +FROM skills +WHERE category LIKE 'libraries/%' +ORDER BY category; + +-- Skills in category hierarchy (all database-related) +SELECT name, category +FROM skills +WHERE category LIKE 'libraries/database%' +ORDER BY category, name; +``` + +## Common SQL Queries + +### Basic Lookups + +```sql +-- Get skill by name (fastest lookup) +SELECT * FROM skills WHERE name = 'malli'; + +-- Get skill by path +SELECT * FROM skills WHERE path = 'skills/libraries/data_validation/malli.md'; + +-- Get skill by ID +SELECT * FROM skills WHERE id = 15; + +-- Check if skill exists +SELECT COUNT(*) FROM skills WHERE name = 'next_jdbc'; + +-- Get multiple skills by name +SELECT id, name, category, title, token_count +FROM skills +WHERE name IN ('malli', 'spec', 'schema') +ORDER BY name; +``` + +### Category Queries + +```sql +-- List all skills in a category +SELECT id, name, title, token_count +FROM skills +WHERE category = 'libraries/database' +ORDER BY name; + +-- Count skills per category +SELECT category, COUNT(*) as skill_count +FROM skills +GROUP BY category +ORDER BY skill_count DESC; + +-- Find categories with most skills +SELECT category, COUNT(*) as count +FROM skills +GROUP BY category +ORDER BY count DESC +LIMIT 10; + +-- List top-level categories only +SELECT DISTINCT + CASE + WHEN category LIKE '%/%' THEN SUBSTR(category, 1, INSTR(category, '/') - 1) + ELSE category + END as top_category +FROM skills +ORDER BY top_category; + +-- Skills in related categories (all data-related) +SELECT name, category +FROM skills +WHERE category LIKE 'libraries/data%' +ORDER BY category, name; +``` + +### Content Queries + +```sql +-- Skills without titles +SELECT name, category +FROM skills +WHERE title IS NULL OR title = ''; + +-- Skills without descriptions +SELECT name, category +FROM skills +WHERE description IS NULL OR description = ''; + +-- Large skills (> 5000 tokens) +SELECT name, category, token_count +FROM skills +WHERE token_count > 5000 +ORDER BY token_count DESC; + +-- Small skills (< 1000 tokens) +SELECT name, category, token_count +FROM skills +WHERE token_count < 1000 +ORDER BY token_count; + +-- Medium-sized skills (good for context windows) +SELECT name, category, token_count +FROM skills +WHERE token_count BETWEEN 2000 AND 5000 +ORDER BY token_count; +``` + +### Statistical Queries + +```sql +-- Database statistics +SELECT + COUNT(*) as total_skills, + COUNT(DISTINCT category) as total_categories, + SUM(size_bytes) as total_bytes, + SUM(token_count) as total_tokens, + AVG(token_count) as avg_tokens_per_skill, + MAX(token_count) as largest_skill_tokens, + MIN(token_count) as smallest_skill_tokens +FROM skills; + +-- Token count distribution +SELECT + CASE + WHEN token_count < 1000 THEN '<1K' + WHEN token_count < 2000 THEN '1K-2K' + WHEN token_count < 5000 THEN '2K-5K' + WHEN token_count < 10000 THEN '5K-10K' + ELSE '>10K' + END as token_range, + COUNT(*) as count +FROM skills +GROUP BY token_range +ORDER BY MIN(token_count); + +-- Skills by category with aggregate stats +SELECT + category, + COUNT(*) as skill_count, + SUM(token_count) as total_tokens, + AVG(token_count) as avg_tokens, + MAX(token_count) as max_tokens +FROM skills +GROUP BY category +ORDER BY total_tokens DESC; + +-- Top 10 largest skills +SELECT name, category, token_count, size_bytes +FROM skills +ORDER BY token_count DESC +LIMIT 10; + +-- Category size comparison +SELECT + CASE + WHEN category LIKE '%/%' THEN SUBSTR(category, 1, INSTR(category, '/') - 1) + ELSE category + END as top_category, + COUNT(*) as skills, + SUM(token_count) as total_tokens, + AVG(token_count) as avg_tokens +FROM skills +GROUP BY top_category +ORDER BY total_tokens DESC; +``` + +### Temporal Queries + +```sql +-- Recently added skills +SELECT name, category, created_at +FROM skills +ORDER BY created_at DESC +LIMIT 10; + +-- Recently updated skills +SELECT name, category, updated_at +FROM skills +ORDER BY updated_at DESC +LIMIT 10; + +-- Skills added after a date +SELECT name, category, created_at +FROM skills +WHERE created_at > '2024-11-01' +ORDER BY created_at DESC; + +-- Skills updated in last 7 days +SELECT name, category, updated_at +FROM skills +WHERE updated_at > datetime('now', '-7 days') +ORDER BY updated_at DESC; + +-- Skills updated in last 24 hours +SELECT name, category, updated_at +FROM skills +WHERE updated_at > datetime('now', '-1 day') +ORDER BY updated_at DESC; +``` + +## Full-Text Search with FTS5 + +### Basic FTS5 Queries + +```sql +-- Simple search (all fields) +SELECT s.id, s.name, s.category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'database' +ORDER BY rank +LIMIT 20; + +-- Search with snippets (shows matched context) +SELECT + s.name, + s.category, + snippet(skills_fts, 5, '[', ']', '...', 30) as snippet, + rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'validation' +ORDER BY rank +LIMIT 10; + +-- Get match highlights in content field +SELECT + s.name, + highlight(skills_fts, 5, '', '') as highlighted_content +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'honeysql' +LIMIT 1; + +-- Search with full skill details +SELECT + s.id, + s.name, + s.category, + s.title, + s.description, + s.token_count, + snippet(skills_fts, 5, '[', ']', '...', 50) as snippet, + rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'async' +ORDER BY rank +LIMIT 10; +``` + +**FTS5 Query Syntax:** + +```sql +-- AND (implicit with space) +WHERE skills_fts MATCH 'database migration' + +-- OR +WHERE skills_fts MATCH 'validation OR schema' + +-- NOT +WHERE skills_fts MATCH 'database NOT migration' + +-- Exact phrase +WHERE skills_fts MATCH '"next.jdbc"' + +-- Prefix search +WHERE skills_fts MATCH 'data*' + +-- Column-specific search +WHERE skills_fts MATCH 'name:malli' +WHERE skills_fts MATCH 'category:database' +WHERE skills_fts MATCH 'description:validation' +WHERE skills_fts MATCH 'content:honeysql' + +-- Complex boolean +WHERE skills_fts MATCH '(database OR async) AND NOT migration' +``` + +### FTS5 Ranking + +FTS5 uses BM25 algorithm for relevance ranking: + +```sql +-- Best matches first (lowest rank score = best match) +SELECT + s.name, + s.category, + rank, + bm25(skills_fts) as bm25_score +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'database' +ORDER BY rank +LIMIT 10; + +-- Boost specific columns (name more important than content) +-- Weights: path=10, category=5, name=3, title=3, description=3, content=1 +SELECT + s.name, + s.category, + bm25(skills_fts, 10.0, 5.0, 3.0, 3.0, 3.0, 1.0) as weighted_rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'http' +ORDER BY weighted_rank +LIMIT 10; +``` + +**BM25 parameters:** +- Lower scores = better matches +- Considers term frequency and document length +- Penalizes very common terms +- Rewards rare terms + +### Advanced FTS5 Features + +```sql +-- Count total matches +SELECT COUNT(*) +FROM skills_fts +WHERE skills_fts MATCH 'database'; + +-- Find near matches (within 5 words) +SELECT s.name, s.category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'NEAR(database query, 5)' +ORDER BY rank +LIMIT 10; + +-- Column filter with multiple terms +SELECT s.name, s.category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'category:libraries/database AND content:sql' +ORDER BY rank; + +-- Search in name or title only (faster than full content search) +SELECT s.name, s.category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH '{name title}: validation' +ORDER BY rank +LIMIT 10; +``` + +## Combining FTS5 with Category Filters + +```sql +-- Search within specific category +SELECT + s.name, + s.category, + snippet(skills_fts, 5, '[', ']', '...', 30) as snippet +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'query' + AND s.category = 'libraries/database' +ORDER BY rank +LIMIT 10; + +-- Search within category hierarchy +SELECT + s.name, + s.category, + rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'async' + AND s.category LIKE 'libraries/%' +ORDER BY rank; + +-- Multi-category search +SELECT + s.name, + s.category, + rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'test' + AND s.category IN ('testing', 'tooling') +ORDER BY rank; + +-- Search with token count filter (for context budgets) +SELECT + s.name, + s.category, + s.token_count, + snippet(skills_fts, 5, '[', ']', '...', 30) as snippet +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'database' + AND s.token_count < 3000 +ORDER BY rank +LIMIT 10; + +-- Search top-level categories only +SELECT + s.name, + s.category, + rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'clojure' + AND s.category NOT LIKE '%/%' +ORDER BY rank; +``` + +## Practical SQL Examples for LLM Agents + +### Example 1: Find Skills for User Question + +**User asks:** "How do I validate data in Clojure?" + +```sql +-- Step 1: Search for validation skills +SELECT + s.id, + s.name, + s.category, + s.title, + snippet(skills_fts, 5, '[', ']', '...', 50) as snippet, + rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'validation' +ORDER BY rank +LIMIT 5; + +-- Results: malli, spec, schema in libraries/data_validation + +-- Step 2: Get full content for top match +SELECT content +FROM skills +WHERE name = 'malli'; + +-- Step 3: Check token count to verify it fits in context +SELECT name, token_count +FROM skills +WHERE name = 'malli'; +``` + +### Example 2: Recommend Skills for Project + +**User asks:** "I need to build a REST API with database. What libraries should I use?" + +```sql +-- Step 1: Find HTTP server skills +SELECT s.name, s.category, s.title +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'http server' +ORDER BY rank +LIMIT 5; + +-- Step 2: Find REST API frameworks +SELECT name, title +FROM skills +WHERE category = 'libraries/rest_api' +ORDER BY name; + +-- Step 3: Find database skills +SELECT name, title +FROM skills +WHERE category = 'libraries/database' +ORDER BY name; + +-- Step 4: Get details for recommendation +SELECT name, title, description, token_count +FROM skills +WHERE name IN ('ring', 'reitit', 'next_jdbc', 'honeysql') +ORDER BY name; + +-- Step 5: Check total token budget +SELECT SUM(token_count) as total_tokens +FROM skills +WHERE name IN ('ring', 'reitit', 'next_jdbc', 'honeysql'); +``` + +### Example 3: Build Topic Context + +**User asks:** "Explain async programming in Clojure." + +```sql +-- Step 1: Find async skills in async category +SELECT name, category, title, token_count +FROM skills +WHERE category = 'libraries/async' +ORDER BY name; + +-- Step 2: Search for related concepts in other categories +SELECT DISTINCT s.name, s.category, rank +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'async OR concurrent OR promise OR channel' + AND s.category != 'libraries/async' +ORDER BY rank +LIMIT 5; + +-- Step 3: Get content for all async skills (check token budget first) +SELECT name, token_count +FROM skills +WHERE name IN ('core_async', 'manifold', 'promesa') +ORDER BY token_count; + +-- Step 4: Load content starting with smallest +SELECT name, content +FROM skills +WHERE name IN ('core_async', 'manifold', 'promesa') +ORDER BY token_count; +``` + +### Example 4: Check Skill Coverage + +**Before implementing:** "Do we have skills for these topics?" + +```sql +-- Check if topics are covered +SELECT + 'validation' as topic, + COUNT(*) as skill_count +FROM skills_fts +WHERE skills_fts MATCH 'validation' +UNION ALL +SELECT + 'testing' as topic, + COUNT(*) as skill_count +FROM skills_fts +WHERE skills_fts MATCH 'testing' +UNION ALL +SELECT + 'logging' as topic, + COUNT(*) as skill_count +FROM skills_fts +WHERE skills_fts MATCH 'logging'; + +-- Find skills matching each topic with details +SELECT 'validation' as topic, name, category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'validation' +UNION ALL +SELECT 'testing' as topic, name, category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'testing' +UNION ALL +SELECT 'logging' as topic, name, category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'logging' +ORDER BY topic, name; +``` + +### Example 5: Find Skills Suitable for Context Budget + +**User needs:** "Load database skills but I only have 5000 tokens available." + +```sql +-- Find database skills under token limit +SELECT name, category, token_count +FROM skills +WHERE category = 'libraries/database' + AND token_count < 5000 +ORDER BY token_count DESC; + +-- Find combination that maximizes usage under limit +SELECT + name, + token_count, + SUM(token_count) OVER (ORDER BY token_count) as cumulative_tokens +FROM skills +WHERE category = 'libraries/database' + AND token_count < 5000 +ORDER BY token_count; + +-- Alternative: Find smallest N skills that fit +SELECT name, token_count +FROM skills +WHERE category = 'libraries/database' +ORDER BY token_count +LIMIT 3; +``` + +## Performance Considerations + +### Index Usage + +```sql +-- Fast: Uses idx_skills_name index +SELECT * FROM skills WHERE name = 'malli'; + +-- Fast: Uses idx_skills_category index +SELECT * FROM skills WHERE category = 'libraries/database'; + +-- Fast: Full-text search uses FTS5 index +SELECT * FROM skills_fts WHERE skills_fts MATCH 'database'; + +-- Slow: Full table scan (no index on title) +SELECT * FROM skills WHERE title LIKE '%validation%'; + +-- Fast alternative: Use FTS5 +SELECT s.* FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'title:validation'; +``` + +### Query Optimization + +```sql +-- Good: Limit early +SELECT name, category +FROM skills +WHERE category = 'libraries/database' +LIMIT 10; + +-- Good: Use covering index +SELECT name, category -- Both indexed +FROM skills +WHERE category = 'testing'; + +-- Good: Avoid SELECT * +SELECT id, name, category -- Only needed columns +FROM skills +WHERE category LIKE 'libraries/%'; + +-- Good: Add LIMIT to FTS5 queries +SELECT s.name, s.category +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'library' +ORDER BY rank +LIMIT 20; + +-- Good: Use FTS5 instead of LIKE +SELECT s.* +FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'sql OR database'; +``` + +### Analyzing Queries + +```sql +-- Show query plan +EXPLAIN QUERY PLAN +SELECT name FROM skills WHERE category = 'testing'; + +-- Shows: +-- SEARCH TABLE skills USING INDEX idx_skills_category (category=?) + +-- Check if index is used +EXPLAIN QUERY PLAN +SELECT * FROM skills_fts WHERE skills_fts MATCH 'database'; + +-- Shows: +-- SCAN TABLE skills_fts VIRTUAL TABLE INDEX 0:M1 +``` + +## Common Use Cases Summary + +### Discovery Patterns + +```sql +-- 1. Find skill by exact name +SELECT * FROM skills WHERE name = 'malli'; + +-- 2. Browse category +SELECT name, title FROM skills WHERE category = 'libraries/database'; + +-- 3. Search by keyword +SELECT s.name, s.category FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'validation' +ORDER BY rank LIMIT 10; + +-- 4. Find related skills +SELECT s.name, s.category FROM skills_fts +JOIN skills s ON skills_fts.rowid = s.id +WHERE skills_fts MATCH 'async OR concurrent' +ORDER BY rank; + +-- 5. Check token budget +SELECT name, token_count FROM skills +WHERE name IN ('malli', 'spec', 'schema'); +``` + +## Summary + +The clojure-skills database provides a powerful SQL-based foundation for skill discovery: + +**Core tables:** +- `skills` - Main table with metadata and content +- `skills_fts` - FTS5 virtual table for full-text search + +**Key features:** +- Hierarchical category organization (slash-separated) +- Full-text search with BM25 relevance ranking +- Rich metadata (tokens, size, timestamps) +- Automatic FTS5 synchronization +- Efficient indexing for fast lookups + +**Query patterns:** +1. **Exact lookup** - `WHERE name = ?` (fastest, uses index) +2. **Category browse** - `WHERE category = ?` (fast, uses index) +3. **Full-text search** - `skills_fts MATCH ?` with JOIN (ranked results) +4. **Combined filters** - FTS5 + category + metadata conditions + +**For LLM agents:** +- Query directly with SQL for complex needs +- Use FTS5 for relevance-ranked topic search +- Filter by category to narrow scope +- Check token counts before loading content +- Build focused context from multiple skills +- Combine queries for multi-step discovery + +**Performance tips:** +- Use indexes (name, category, file_hash) +- Always add LIMIT clause to large result sets +- Select only needed columns (avoid SELECT *) +- Use FTS5 instead of LIKE for text search +- Analyze query plans with EXPLAIN QUERY PLAN + +This schema enables fast, flexible skill discovery through standard SQL queries.