Skip to main content

Overview

Auto-Skill integrates with Skills.sh, a community registry of 27,000+ coding agent skills. The External Skill Loader enables runtime skill discovery, loading, and execution from multiple sources:
  • Skills.sh API: 27,000+ community skills
  • GitHub registries: Direct repository integration
  • .well-known endpoints: RFC 8615 skill discovery

Architecture

Skill Sources

The loader supports pluggable skill source providers:
export interface SkillSource {
  name: string;
  enabled: boolean;
  search(query: string, limit: number): Promise<SkillSearchResult[]>;
}

ExternalSkill Interface

export interface ExternalSkill {
  id: string;
  title: string;
  description: string | null;
  source: string; // owner/repo
  installCount: number;
  relevanceScore: number;
  content: string | null; // Full SKILL.md content
  rawUrl: string | null;
  skillsShUrl: string | null;
  githubUrl: string | null;
  version?: string;
  allowedTools?: string[];
  tags?: string[];
}

Usage

Search for skills across all sources:
import { createExternalSkillLoader } from "auto-skill";

const loader = createExternalSkillLoader();
await loader.start();

const response = await loader.search("nextjs", {
  limit: 10,
  includeContent: false, // Don't fetch full content yet
});

console.log(`Found ${response.count} skills:`);

for (const skill of response.skills) {
  console.log(`- ${skill.title}`);
  console.log(`  Source: ${skill.source}`);
  console.log(`  Installs: ${skill.installCount}`);
  console.log(`  URL: ${skill.skillsShUrl}`);
}

await loader.stop();

2. GitHub Integration

The loader automatically fetches skills from GitHub:
1

Detect Default Branch

The loader detects the repository’s default branch (main/master)
2

Find SKILL.md

Searches for SKILL.md in standard locations:
  • skills/{skillId}/SKILL.md
  • {skillId}/SKILL.md
  • SKILL.md
3

Fetch Content

Downloads the raw SKILL.md content via GitHub’s raw.githubusercontent.com
class GitHubClient {
  async fetchSkill(source: string, skillId: string): Promise<GitHubFetchResult> {
    const [owner, repo] = source.split("/");

    // 1. Get default branch
    const branch = await this.getDefaultBranch(owner, repo);

    // 2. Find SKILL.md path
    const skillPath = await this.findSkillPath(owner, repo, branch, skillId);
    if (!skillPath) {
      return { success: false, error: "SKILL.md not found" };
    }

    // 3. Fetch raw content
    const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${skillPath}`;
    const response = await fetch(rawUrl);
    const content = await response.text();

    return { success: true, content, rawUrl };
  }
}

3. Caching

The loader uses an in-memory cache to reduce API calls:
class InMemoryCache {
  private cache = new Map<string, CacheEntry<unknown>>();
  private defaultTtl: number = 86400; // 24 hours

  async get<T>(key: string): Promise<T | null> {
    const entry = this.cache.get(key);
    if (!entry || entry.expiresAt < Date.now()) {
      return null;
    }
    return entry.value;
  }

  async set<T>(key: string, value: T, ttl?: number): Promise<void> {
    const ttlMs = (ttl ?? this.defaultTtl) * 1000;
    this.cache.set(key, {
      value,
      expiresAt: Date.now() + ttlMs,
    });
  }
}
Cache entries expire after 24 hours by default. Configure with cacheTtl option.

Skills.sh Integration

The Skills.sh source provider searches the community registry:
class SkillsShSource implements SkillSource {
  name = "skills.sh";
  enabled = true;
  private baseUrl = "https://skills.sh";

  async search(query: string, limit: number): Promise<SkillSearchResult[]> {
    const url = new URL(`${this.baseUrl}/api/search`);
    url.searchParams.set("q", query);
    url.searchParams.set("limit", String(limit));

    const response = await fetch(url.toString(), {
      headers: {
        "Accept": "application/json",
        "User-Agent": "auto-skill/5.0.0",
      },
    });

    const data = await response.json();
    return data.results.map((item) => ({
      id: item.id,
      name: item.name,
      source: `${item.owner}/${item.repo}`,
      sourceRegistry: "skills.sh",
      installs: item.install_count ?? 0,
    }));
  }
}

Relevance Ranking

Skills are ranked by relevance to improve search results:
class RelevanceRanker {
  rank(skills: ExternalSkill[], query: string): ExternalSkill[] {
    // Multi-signal ranking:
    // 1. Query match score
    // 2. Install count (popularity)
    // 3. Content relevance (when available)
    
    // For now: sort by install count
    return skills.sort((a, b) => b.installCount - a.installCount);
  }
}

API Reference

createExternalSkillLoader(options?)

Factory function to create an ExternalSkillLoader instance.
options.githubToken
string
GitHub personal access token for increased rate limits (60 → 5,000 requests/hour)
options.cacheTtl
number
default:"86400"
Cache TTL in seconds (default: 24 hours)

ExternalSkillLoader Methods

start
() => Promise<void>
Start the loader and initialize the cache cleanup interval.
stop
() => Promise<void>
Stop the loader and clear cache cleanup interval.
Search for skills across all enabled sources.Options:
  • limit?: number - Maximum results (default: 10)
  • includeContent?: boolean - Fetch full SKILL.md content (default: false)
Returns:
interface SkillSearchResponse {
  query: string;
  count: number;
  skills: ExternalSkill[];
}
getCacheStats
() => Promise<CacheStats>
Get cache statistics.Returns:
interface CacheStats {
  size: number;
  hits: number;
  misses: number;
}

Example: Manual Skill Loading

import {
  createExternalSkillLoader,
  createProactiveDiscovery,
  createSkillRecommendationEngine,
} from "auto-skill";

const loader = createExternalSkillLoader();
const discovery = createProactiveDiscovery(loader);
const engine = createSkillRecommendationEngine(loader, discovery);

await loader.start();

// Search for a specific skill
const skills = await engine.searchSkills("vercel deployment", 5);

if (skills.length > 0) {
  // Load the first skill with full content
  const firstSkill = skills[0];
  const fullSkill = await engine.loadExternalSkill(
    firstSkill.source,
    firstSkill.id
  );

  if (fullSkill?.content) {
    console.log("✅ Skill loaded successfully!");
    console.log(`Content length: ${fullSkill.content.length} chars`);
  }
}

await loader.stop();

Best Practices

When displaying search results, set includeContent: false to avoid fetching full SKILL.md files. Only fetch content when the user selects a specific skill.
Set GITHUB_TOKEN environment variable or pass githubToken option to increase rate limits from 60 to 5,000 requests/hour.
Call loader.start() before searching and loader.stop() when done to properly manage cache cleanup.

See Also

Proactive Discovery

Context-aware skill recommendations

Skill Graduation

Promote external skills to local

Build docs developers (and LLMs) love