skillsmp
v0.1.0
Published
CLI and Node.js library for the SkillsMP REST API (https://skillsmp.com) — search the SkillsMP skill index from your terminal, with disk caching and optional API key auth.
Downloads
231
Maintainers
Readme
skillsmp
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+.
Install
npm install -g skillsmpor run it without installing:
npx skillsmp search "seo"Two things about the API you should know before trusting stars
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.
Commands
skillsmp search <query> [options]
--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 callHuman 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.
skillsmp login [--stdin]
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.
skillsmp logout
Deletes the stored key and prints the config file path it removed it from.
skillsmp whoami
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.
skillsmp cache stats|clear|ttl [<duration>]
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.
skillsmp config [--json]
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.
Key resolution order (highest wins)
--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.
Config and cache locations
| 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).
Caching
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.
Programmatic use
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.
Exit codes
0— success1— usage error or API error (invalid key, bad occupation/language slug, etc.)2— daily quota exceeded
Errors
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.
Testing
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.
License
MIT
