Pradeep Elankumaran
PRADEEP.md building jo · sf
←

safe-skill-search: Quality-Filtered Local Search for Agent Skills

In my previous post, I analyzed 4,784 AI agent skills across 5 registries. An agent needs to navigate this space quickly and safely.

safe-skill-search is a Rust CLI that searches 4,700+ skills locally in under 100ms with quality filtering. By default, it only shows skills with a quality score ≥ 80.

Skill discovery is part of the agent loop: plan → find capability → act. If it takes seconds, the loop stalls. Under 100ms keeps things responsive.

A web API can’t reliably hit that bar:

Issue Impact
Network + TLS 50-200ms baseline
Cold starts Serverless adds 200-800ms
Rate limits Queuing under load
Tail latency p95 > p50, agents feel the worst case

Other considerations:

  • Offline/CI environments: Agents run in places with no internet
  • Privacy: Search queries contain project context
  • Determinism: Local indexes don’t go down

The stack: git → SQLite → Tantivy

Three layers, each with a reason to exist:

GitHub repos (registries)
        │
        │  git clone --depth 1
        ▼
   repos/  ──────▶ parse SKILL.md frontmatter + content
        │                         │
        │                         ▼
        │                  SQLite skills.db
        │                  (metadata + full SKILL.md)
        │                         │
        │                         ▼
        └──────────────▶ Tantivy index/
                        (BM25 full-text search)
                                  │
                                  ▼
                          query → ranked results

Git = distribution

The registries are already GitHub repos. Clone them locally with shallow fetch:

git clone --depth 1 https://github.com/openclaw/skills.git

Git becomes the package index transport. No custom sync protocol needed.

SQLite = metadata cache

Store structured fields: slug, name, description, registry, trust level, stars, full SKILL.md content. Makes it trivial to build other views (skill-search top, skill-search show).

CREATE TABLE skills (
    slug TEXT NOT NULL,
    name TEXT NOT NULL,
    registry TEXT NOT NULL,
    description TEXT DEFAULT '',
    skill_md TEXT DEFAULT '',
    github_url TEXT NOT NULL,
    stars INTEGER DEFAULT 0,
    trusted INTEGER DEFAULT 0,
    UNIQUE(registry, slug)
);

Tantivy = search engine

Tantivy is a Rust search library with BM25 ranking. The index combines name, description, and full SKILL.md content for recall:

let content = format!("{} {} {}", skill.name, skill.description, skill.skill_md);
doc.add_text(self.content_field, &content);

Query parsing supports multi-field search with optional registry filtering:

let query_parser = QueryParser::for_index(
    &self.index,
    vec![self.name_field, self.description_field, self.content_field],
);

Result: sub-100ms queries across 4,700+ skills.

The skills analysis showed only ~31% of skills score 90+. Many have placeholder content, broken scripts, or security concerns.

safe-skill-search embeds the quality scores directly into the binary:

pub struct QualityScores {
    scores: HashMap<String, QualityEntry>,
}

impl QualityScores {
    pub fn load() -> Self {
        let json_data = include_str!("../skills.json");  // frozen at build time
        // ...
    }
}

Search results are filtered before display:

.filter(|r| r["quality_score"].as_i64().unwrap_or(0) >= min_score)

The default --min-score 80 means you only see skills that passed quality review. Override with --min-score 0 to see everything.

Output now shows quality scores inline:

$ safe-skill-search search "browser"
1. [✓] puppeteer (anthropic) [Q:95] - Browser automation with Puppeteer
2. [⚠] smooth-browser ★1 (clawdhub) [Q:95] - Browser automation with error handling
3. [⚠] browserwing (clawdhub) [Q:80] - Browser automation toolkit

End-to-end flow

1. Install

curl -fsSL https://github.com/jo-inc/safe-skill-search/releases/latest/download/install.sh | bash

The script detects your platform and downloads the right release. No Rust toolchain required.

2. First run: auto-sync

On first launch, safe-skill-search detects an empty database and syncs automatically:

$ safe-skill-search search "trello"
First launch detected, syncing skills...
Syncing registry: clawdhub
Syncing registry: anthropic
Syncing registry: openai
Indexing 4784 skills

This happens once. Takes ~10 seconds to clone 5 registries and build the index.

3. Search (with quality filtering)

$ safe-skill-search search "browser automation"
1. [✓] puppeteer (anthropic) [Q:95] - Browser automation with Puppeteer
   https://github.com/anthropics/skills/tree/main/skills/puppeteer

2. [⚠] smooth-browser ★1 (clawdhub) [Q:95] - Browser automation with error handling
   https://github.com/openclaw/skills/tree/main/skills/antoniocirclemind/smooth-browser

[✓] = trusted (anthropic, openai curated). [⚠] = community/experimental. [Q:95] = quality score.

Low-quality skills are filtered out by default. Show everything with --min-score 0.

For programmatic use:

$ safe-skill-search search "pdf" --json --limit 3
[
  {
    "slug": "pdf",
    "name": "pdf",
    "registry": "anthropic",
    "description": "Comprehensive PDF manipulation toolkit...",
    "github_url": "https://github.com/anthropics/skills/tree/main/skills/pdf",
    "stars": 0,
    "trusted": true,
    "search_score": 25.3,
    "quality_score": 95
  }
]

4. Get install URL

$ safe-skill-search url trello
https://github.com/openclaw/skills/tree/main/skills/steipete/trello

Bootstrapping your agent’s toolkit

safe-skill-search is a CLI tool that helps you discover and vet skills before installing them.

The workflow:

safe-skill-search search "calendar"      # Find (quality filtered)
safe-skill-search show google-calendar   # Inspect (shows quality score)
safe-skill-search url google-calendar    # Get install URL
# → install in your agent

With 4,700+ skills, discovery with filtering is necessary infrastructure.

Data storage

Everything lives in ~/.local/share/skill-search/:

Path Contents Size
skills.db SQLite with all skill metadata ~2MB
index/ Tantivy search index ~5MB
repos/ Cloned git repositories ~100MB

Update with skill-search sync. Force refresh with skill-search sync --force.

Why BM25 instead of embeddings?

Approach Pros Cons
BM25 Zero dependencies, instant indexing, predictable Keyword-based, no semantic understanding
Vectors Semantic matching, handles synonyms Requires embedding model, larger index, slower indexing

For skill search specifically:

  1. Skills have good keywords. Names like trello, pdf, browser-automation are already descriptive. You’re usually searching for what you know you want.

  2. SKILL.md content is dense. The full markdown includes examples, commands, trigger phrases. BM25 over this content catches most queries.

  3. Embedding adds latency at index time. Generating embeddings for 4,700 skills means either shipping a model or calling an API. Both add friction to the “just works” goal.

  4. No model dependency. The binary is self-contained. No Python, no ONNX runtime, no API keys.

Vector search makes sense when:

  • Queries are vague (“something that helps with meetings”)
  • You’re building a recommendation system
  • Semantic similarity matters more than keyword match

BM25 covers most cases. Hybrid search (BM25 + vectors) is an option if recall becomes a problem.

What’s next

  • Quality scoring in CLI output (filter before installing) ✓ Done
  • Field boosts for better ranking (name > description > content)
  • Maybe: prebuilt index distribution for faster first-run
  • Periodic refresh of embedded quality scores

Local-first with quality filtering is the core constraint.