CLI and Node.js library for the SkillsMP REST API — search
the SkillsMP skill index (1.6M+ SKILL.md files pulled from GitHub) from your
terminal, with on-disk caching and optional API key auth.
Zero runtime dependencies. Requires Node.js 20+.
npm install -g skillsmpor run it without installing:
npx skillsmp search "seo"1. stars is the star count of the GitHub repo that contains the skill, not
of the skill itself. Measured against the live API: a result reported
stars: 240467; the containing repo (affaan-m/ECC) actually has 240743
stars — they match. A skill sitting inside a mega-repo inherits that repo's
popularity even if it's auto-translated boilerplate, while a careful,
standalone skill in its own small repo shows single digits. Sorting by stars
surfaces "lives in a famous repo," not "is a good skill." (Example: searching
backlink sorted by stars returned three of its first four results from one
28562-star note-taking app's repo, about that app's internal wiki-style
backlinks — nothing to do with SEO backlinks.) Every star count this CLI
prints is labelled repo★ for this reason.
2. pagination.total is unreliable. It ships with totalIsExact: false
and has been observed drastically undercounting — searching SEO across the
1.6M-record index reported total: 5. This CLI paginates on hasNext only,
never on a page count computed from total.
--page <n> page number (default 1)
--limit <n> results per page, max 100 (default 20)
--sort <stars|recent> sort order (default stars — see the stars caveat above)
--category <slug> filter by category slug, e.g. data-ai, devops
--occupation <slug> filter by SOC occupation slug, e.g. software-developers
--lang <code> filter by content language: en, zh, ja, ...; mul = mixed, und = undetermined
--pages <n> walk up to n pages, stopping when the API's hasNext is false
--json machine-readable output
--no-cache bypass the cache read (the fresh result is still cached)
--api-key <key> overrides SKILLSMP_API_KEY and the saved key for this call
Human output shows, per result: name, author, star count (labelled as the containing repo's stars), content language, last-updated date, a truncated description, and the GitHub URL. A trailing status line reports whether the result came from cache (and its age), current quota, and how many pages were fetched.
--json output carries the same cache/quota information in fields rather
than mixing it into stdout, so it stays machine-parseable.
The API has no OAuth flow, so this is a manual key entry:
- Prints
https://skillsmp.com/docs/apiand tries to open it in your default browser (open/xdg-open/start). If that fails, the printed URL is the fallback — nothing is broken. - Prompts for your key without echoing it to the terminal. For scripting,
pipe it in instead:
echo "$KEY" | skillsmp login --stdin. - Validates the key with one real API call before saving anything. If it's rejected, nothing is written.
- Saves it to the config file (see below) with file mode
0600.
Deletes the stored key and prints the config file path it removed it from.
Reports whether a key is configured (and its source: flag / env / file), the last 4 characters only (never more), and current quota read from a live request's rate-limit headers.
stats— entry count, total size on disk, oldest/newest entry timestamps.clear— deletes all cached entries.ttl— with no argument, prints the current effective TTL and its source; with an argument (ttl 24h,ttl 30m,ttl 604800), persists a new TTL into the config file.
Prints the fully resolved configuration (API key masked, cache TTL, base
URL, config/cache directories) and which source supplied each value —
flag, env, file, default, or computed for paths — so an unexpected
value can be traced.
--api-keyflagSKILLSMP_API_KEYenvironment variable- the config file
- none — anonymous access
The API works fine with no key: anonymous is 50 requests/day and 10/minute. An API key raises that to 500/day and 30/minute. The key is never written into the repository, cache files, logs, or error messages.
| What | Location | Override |
|---|---|---|
| Config file | $XDG_CONFIG_HOME/skillsmp/config.json (default ~/.config/skillsmp/config.json) |
XDG_CONFIG_HOME |
| Cache directory | $XDG_CACHE_HOME/skillsmp/ (default ~/.cache/skillsmp/) |
XDG_CACHE_HOME |
The config file holds apiKey (mode 0600) and cacheTtl (seconds).
Every successful search response is cached to disk, keyed by a SHA-256 hash
of the normalised request parameters (q, page, limit, sortBy,
category, occupation, language) — the API key is deliberately
excluded from the cache key, since the same query returns the same public
data no matter who asked; this also means switching between anonymous and
authenticated access reuses the same cache entries instead of doubling disk
usage.
- Default TTL: 7 days. Configurable via
skillsmp cache ttl <duration>, theSKILLSMP_CACHE_TTLenv var, or acacheTtlfield in the config file. Durations accept30m,24h,7d, or a plain number of seconds. --no-cacheskips the cache read but still writes the fresh result, so the next normal call benefits from it.- Entries store the fetch timestamp, so expiry is evaluated at read time — changing the TTL takes effect on existing entries immediately, no rewrite needed.
- Error responses are never cached. Caching a
DAILY_QUOTA_EXCEEDEDfor a week would make the CLI insist the quota is exhausted long after it actually reset.
import { searchOnePage, searchPages, resolveConfig } from "skillsmp";
const config = resolveConfig({});
const { data, cache } = await searchOnePage({
q: "seo",
apiKey: config.apiKey.value,
cacheTtlSeconds: config.cacheTtl.value,
});
console.log(data.skills, cache.hit);See index.js for the full exported surface: searchSkills (raw API call),
searchOnePage / searchPages (cache-aware, hasNext-driven pagination),
resolveConfig, cache helpers, duration parsing, and explainError.
0— success1— usage error or API error (invalid key, bad occupation/language slug, etc.)2— daily quota exceeded
API error codes are translated into actionable messages rather than printed raw:
| Code | CLI behavior |
|---|---|
INVALID_API_KEY |
Suggests skillsmp logout + skillsmp login, or unsetting SKILLSMP_API_KEY |
MISSING_QUERY |
Usage hint with an example |
INVALID_OCCUPATION / INVALID_LANGUAGE |
Surfaces the API's message about the bad slug |
DAILY_QUOTA_EXCEEDED |
States the 50/day anonymous vs 500/day keyed limits and points at skillsmp login; exit code 2 |
INTERNAL_ERROR |
Suggests retrying |
Network failures, timeouts, and malformed JSON responses are also caught and reported as plain error messages — never a stack trace.
npm testRuns the built-in node:test suite. All network calls are stubbed (a fake
fetch implementation is injected); the suite makes no real requests.
MIT