npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@skill-router/cli

v0.4.31

Published

SKILLROUTER CLI (sr) — search engine for agent skills: sr resolve ranks screened third-party skills and SkillRouter's own website tools; sr ensure installs the pick, hash-checked. Free for individuals. Pair with `sr connect`, search with `sr resolve`, ins

Readme

@skill-router/cli

sr — the SKILLROUTER command line. SkillRouter is a search engine for agent skills: sr resolve ranks crawled third-party skills together with SkillRouter's own website tools for a task, and sr ensure verifies and installs the pick. Search and install are free for individuals; a browser is one ordinary candidate, never the only path. Third-party skills run under their own manual.

The pivot is being implemented in the integration queue. Check the installed CLI and deployed backend before relying on a new feature. The site-adapter examples below describe one existing capability, not the platform's full scope; browser use is not a prerequisite for every task.

npm i -g @skill-router/cli

sr connect                 # pair this machine — approve in your browser, key auto-saved
sr catalog                 # skills + contracts
sr account                 # account and membership status
sr ensure boss-web-cli     # install one authorized execution component on demand
sr xiaohongshu search "agent tools"   # call a listed skill directly (account key, free for individuals)
sr update                  # diff-only CLI/MCP/runtime + skills/sitedata update; never waits for site login
  • Credentials live in ~/.skillrouter/credentials.json, shared with @skill-router/mcp — log in once, every surface works.
  • Manuals are agent-readable; there is nothing for you to learn. Point your agent at https://skillrouter.org/SKILL.md.
  • Self-hosted/dev: SKILLROUTER_BASE=http://localhost:9000/api/sr sr catalog.

Not every task has a suitable skill, and not every skill is installable: installation follows the licence, pack availability and local script authorization, and sr resolve can legitimately return rank.no_match: true. Say so rather than promising a hit.

On-demand adapters

sr <site> <command> first checks the local runtime. If the exact site and command are in the public index but its execution component is missing, the CLI downloads the bundle, verifies SHA-256 plus the ZIP/manifest structure, installs it once under a bundle lock, and then continues the original argv. Already installed components run locally without a catalog request, including offline and after a supporter membership lapses.

sr ensure needs no sr connect: a machine without an account installs anonymously through the same endpoint (an account key is sent when one exists). sr connect is only required for sr community submit / claim and for sr account / sr run. When a key is rejected (401/409) the CLI drops it and retries once anonymously; the failure code stays connect_required only if that retry fails too. An old backend that still gates a package answers 402/403, which surface as payment_required / not_available — never as a silent success.

sr ensure boss-web-cli --check --json  # authorize/validate only; no install
sr ensure boss-web-cli --json          # idempotent first install
SR_AUTO_ENSURE=0 sr boss search ...    # rollback switch: keep legacy missing-plugin behavior

Third-party skills

sr resolve can list "kind": "external" candidates: public GitHub skills the crawler screened and mirrors. They are third-party — SkillRouter does not maintain or run them. Install one by id, which fetches the mirror pack, recomputes the content hash locally, and keeps it in SkillRouter's cache (~/.skillrouter/external/<name>), not in your host's skill directories, so it does not load into every turn: the agent reads the returned skill_md when it needs it. Skills installed before this change stay bridged as they were. A pack that bundles scripts is refused until you pass --allow-scripts explicitly. Nothing from a pack is ever executed or imported. Each pack carries the skill's original license files; a skill that cannot be mirrored (for example over the size caps) stays link-only, with source_url as the place to read it.

sr ensure gh/owner/repo --json           # fetch, verify the content hash, cache; prints skill_md
sr ensure gh/owner/repo --check --json   # local readiness only; writes nothing
sr ensure gh/owner/repo --refresh        # refetch and upgrade; never overwrites local edits
sr ensure gh/owner/repo --allow-scripts  # required once, only after the user agrees
sr external list                         # installed third-party skills, per-turn cost, stale (--json)
sr external remove <name>                # remove only entries whose ownership verifies

Unlike sr ensure <skill>, a third-party install needs no Chrome and no execution runtime: a documentation-only pack installs on a bare machine. --check writes nothing, sends no receipt and executes nothing. A locally modified install reports blocked / local_modified instead of being overwritten, and an interrupted install is rolled back on the next run. Every install also appends the usual ensure journal line; the optional install receipt is skipped under SR_EXTERNAL_NO_REPORT=1 (or when local outcome reporting is off) and can never change the exit code.

execution.bundle and execution.runtime_plugin come from the catalog contract. The CLI never derives a download from a prompt, manual URL, or site-name suffix. sr setup --bundle <path> remains available for offline/development bundles; pre-downloading every adapter is no longer onboarding. Every sr ensure call also appends one line — skill, target, state, code, changed, source — to the local journal ~/.skillrouter/journal/ensure.jsonl; it never leaves the machine.

Submitting a skill repository

SkillRouter indexes public GitHub repositories: a folder with a SKILL.md anywhere in the repo becomes a candidate skill. Hand the CLI the repository address; the crawler picks it up on the next round. Submitting an address uploads nothing from this machine (to share local-only skills, see below).

sr connect                                        # submissions need your account key
sr community submit https://github.com/owner/repo # or owner/repo; queued for the next crawl
sr community submissions                          # what you submitted (--withdraw <repo> takes one back)
sr author claim owner/repo                        # write .skillrouter-claim at the repo root, then:
sr author verify owner/repo                       # commit the file first; verify reads it from GitHub
sr author stats owner/repo                        # per-skill listing state, unlisted reason, 30-day shown/top1/installs

Sharing skills that live only on this machine

sr community share                   # preview: classifies every local skill, uploads nothing
sr community share --accept-terms    # once per terms version, after reading them
sr community share --send <name...>  # or --send --all; --as <handle> sets your author name once
sr community share --list            # your uploads; --withdraw <name> takes one back

The preview scans each host's skills folder, ~/.agents/skills and the current project's .claude/skills / .agents/skills. Skills installed by SkillRouter are skipped; skills whose content is already in the catalog are skipped (only content hashes are sent for this check, and only with a key); skills with a public GitHub origin are submitted as repository addresses instead of uploaded. A skill matching a secret pattern (API key, token, password assignment, private key, credentials in a URL) is never uploaded, and the output names the rule and line, not the value; the patterns cannot catch every secret, so the preview is yours to check. Home paths, emails and internal hosts are flagged for you to decide. Symlinks inside a skill are not followed; .git, node_modules, .env*, binaries and files over 256 KiB are left out; a skill over 2 MiB or 200 files is refused. Upload requests carry skill-relative paths and file contents only — no absolute paths, host names or user names. Uploaded skills are kept in a SkillRouter-held hosting repository under <handle>/<name>/, then indexed and installed through SkillRouter like any other catalog skill; skills without a licence file are shared under CC BY 4.0.

sr community submit <draft-dir> (the old adapter-package flow) is refused with adapter_submissions_closed and exit code 2 — it reads nothing, packs nothing, and makes no request.

Skill discovery across agent harnesses

sr snapshot reads the skills already present on this machine, and sr integrate wires SkillRouter into Codex and Claude Code. SkillRouter no longer injects anything per turn: a SessionStart warmup primes the local index, and the skillrouter-router Skill finds the right skill for any task on demand with sr resolve (plus sr ensure for the one candidate it picks). Website work in the user's own Chrome is one candidate among others: the sr-browser Skill, ranked like everything else. Run sr resolve before any specialised task, even when an installed skill seems to fit; the agent then compares its candidates with the installed skills in its own context and picks the better one — installed skill names, descriptions and paths are never uploaded.

sr snapshot                # JSON: skills found, name collisions, invalid ones, catalog matches
sr snapshot --format hook  # compact projection injected at session start
sr resolve "<need>" --json                        # candidates for a task; site is not required
sr resolve "<need>" --site <domain>,<brand> --json  # optional filter for an explicitly named site
sr integrate --dry-run     # show exactly what would be written — nothing is touched
sr integrate               # router-only; asks for confirmation first
sr integrate verify        # health check; non-zero exit if something is broken
sr integrate remove        # undo — removes only what SkillRouter added

sr integrate adds only marked SkillRouter entries and preserves every existing setting:

  1. Claude Code hook in ~/.claude/settings.json: SessionStart warms the local index. sr integrate also migrates away any UserPromptSubmit/Stop fragment written by cli ≤ 0.4.22 (third-party entries in the same arrays are left byte-for-byte intact).

  2. Codex hooks in ~/.codex/hooks.json: no SkillRouter hooks are needed; the same migration removes old per-turn fragments and deletes the keys it empties.

  3. Small infrastructure Skills linked into ~/.agents/skills and every detected host's native skill root: skillrouter-router (find and run a candidate), sr-browser (drive a website in the user's own Chrome; its browser, login, write, workspace and failure-handoff rules live in its own SKILL.md) and community-submit. The count follows the shipped manuals, not a fixed number. ~/.agents/skills is written when a host that reads that shared directory is detected (Codex, Cursor, Gemini CLI, …), or when no host is detected at all. A claude-only machine therefore no longer gets ~/.agents/skills — that directory only existed for hosts that read it.

    Upgrading from a build without the timeout field on SessionStart: sr integrate --dry-run will report Claude's SessionStart as update, because sr integrate rewrites our own fragment in place to add the current timeout (entries marked _skillrouter; foreign entries are never touched). sr integrate verify stays green throughout — the recorded hash is refreshed when the fragment is written, so a drift line only appears if someone edits our entry by hand.

Run sr integrate --list-hosts to see which hosts this machine detects. Hosts are only written when detected, and a host with a hook surface is verified after every write:

| id | tier | writes | notes | |---|---|---|---| | claude | hook | ~/.claude/settings.json | SessionStart only; Claude Code asks you to approve externally added hooks once | | codex | hook | ~/.codex/hooks.json | no SkillRouter hooks; the router skill reaches Codex through ~/.agents/skills, and only old fragments are cleaned up | | gemini | hook | ~/.gemini/settings.json | SessionStart only, timeout in milliseconds. Only ~/.gemini/settings.json or gemini on PATH counts — a bare ~/.gemini/ (Antigravity) is ignored | | qwen | hook | ~/.qwen/settings.json | SessionStart only | | cursor | hook | ~/.cursor/hooks.json | sessionStart writes {additional_context}; beforeSubmitPrompt cannot inject and is not used | | copilot, kiro, factory | skills | each host's skills root | router Skill linked next to the host's own skills (Kiro does not read ~/.agents/skills) | | opencode, amp, zed, warp, roo, junie, goose, kimi, trae, openclaw, hermes, cline | skills | ~/.agents/skills | these read the shared ~/.agents/skills directory; no per-host file is written this batch |

Use sr integrate --host <id,…> to limit a run to specific hosts (for example --host gemini). sr integrate --dry-run shows the exact plan without writing.

Integration is router-only. Product skills stay in the local SkillRouter index and their execution component installs on first use, so the native skill roots do not grow with the catalog. Machines integrated by an older CLI that still carry per-product native stubs are migrated by sr integrate or sr update: only stubs SkillRouter wrote and never modified are removed; edited or foreign entries are left in place and reported.

Settings files are backed up to ~/.skillrouter/backup/. sr integrate remove removes only entries and links whose identity/content still matches what SkillRouter installed.

Scanning only ever reads the name and description from each SKILL.md. Skill bodies are never read at startup and skill scripts are never executed.

What leaves your machine

One anonymous catalog request is cached for 15 minutes. Public bundle updates work the same with or without a saved key. Matching stays on your machine: project paths, source code, local skill names, and user prompts are never sent to SkillRouter. For manual sr snapshot diagnostics, pass --no-available to skip that fetch entirely; local skills keep working with no network at all. If SkillRouter is unreachable, the last successful catalog is reused and the snapshot is marked incomplete — your local skills are never affected.

sr resolve lists every matching candidate — nothing is withheld — and owned is installed, community or available. --all is kept as a compatibility no-op (the default list is already the full public list). sr ensure is the final gate: it verifies the runtime, the local readiness and the install. Nothing is ever bought automatically. Every sr resolve call also appends one line — need, site, access, k, coverage, next, excluded (always null since the authorization gate was removed), plus skill/site/command/score/owned for the first ten candidates — to the local journal ~/.skillrouter/journal/resolve.jsonl; it never leaves the machine.

Uncovered sites and community feedback

sr resolve may return no candidate that can do the task, and rank.no_match: true says the ranking model found nothing applicable. The sr-browser Skill is not a special fallback channel: it is one candidate among the others, ranked with them, so a website task can also come up empty. When that happens the Router says so in one line and the agent finishes with the tools and skills it already has (a browser skill or browser tool included), re-searches with a changed need, or records the gap. SkillRouter never installs or remotely controls a browser. Writes still require confirmation, and login, CAPTCHA, risk control, payments, credentials, and other red-zone workflows are never explored.

When sr resolve returns no candidate at all, the CLI records that miss by itself (one bounded, best-effort POST of the need text; it never changes the exit code or the output). There is no separate report command any more.

For the closing line the agent uses sr feedback done: it appends one line to the local journal ~/.skillrouter/journal/feedback.jsonl — the skill or skill/command, the outcome (ok, wrong, nofit, blocked, fallback) and the user's own sentence verbatim via --said — and never uploads it. It prints { ok: true, journal: <path|null> } and exits 0 even when the local write fails (SR_JOURNAL=off reports journal: null).

sr feedback status
sr feedback off
sr feedback on
sr feedback done <skill>[/<command>] <ok|wrong|nofit|blocked|fallback> --said "<user's words>"  # local journal only

Feedback is independent from the default-off sr signal health ping. A supporting integrated client shows one single-line disclosure once; there is no per-task prompt. Public explanation: https://skillrouter.org/us/docs.

Anonymous outcome events (on by default, one switch off)

When a cloud sr resolve returns a rid, the CLI turns the results a user actually sees into anonymous outcome events and queues them on this machine:

  • pick — sr ensure really succeeded (installed / ready_local) for a candidate of a resolve from the last 60 minutes; --check and a side install that matched no candidate never count;
  • ok / wrong / nofit / blocked / fallback — an explicit sr feedback done conclusion (nofit lands on that resolve's first candidate);
  • run_ok / run_fail / blocked — new lines of the local runs.jsonl mapped back to their canonical skill/command id (auth/arg/env/timeout are blocked, never a quality vote; empty concludes nothing; verify/audit runs and unmappable sites are skipped);
  • requery — a re-search within 10 minutes of the previous one, in the same identifiable local session, when that session's candidate had been picked and no feedback done conclusion followed.

An event is exactly { id, ev, day, rid?, pos? } (plus the opt-in q below). id is a canonical public id only — a bare skill id, an official skill/command, an external gh/owner/repo[/path], or builtin/sr-browser — taken from the public index or from a resolve response; arbitrary text, URLs and private skill names never become telemetry ids. day is a UTC date within the last seven days, pos is 1–10, and rid is a 16-hex local correlation id (the server only records that a rid was present). Your request sentence, --said, tasks, argv, arguments, results, URLs, paths, account ids and API keys have no representation in the payload, the queue or the request.

Optional q (off by default). Only if you set SR_OUTCOME_QUERY=1 (exactly 1; unset, empty, 0 or any other value keeps it off), the ok, run_ok and wrong events that belong to a recent resolve also carry a sixth key q: your need sentence in normalized form (NFKC, control characters removed, lower-cased, whitespace collapsed, at most 128 characters) — the same normalization the server uses for missed searches. It is used to improve search (which wording finds which skill). No other event ever carries q. Locally, resolve keeps only that normalized need_norm next to the rid (never the raw sentence); it is sent only while SR_OUTCOME_QUERY=1, both when the event is queued and when it is uploaded. Deploy the backend before a CLI that sends q: an older server drops an event that carries q.

Delivery is best effort and never changes a command's output or exit code. The queue is ~/.skillrouter/outcome/queue.jsonl (at most 500 rows, 7 UTC days, ≤512 bytes per row, written under a lock so concurrent sr processes cannot corrupt it); a batch is packed to the whole request body's 8 KiB and 50-event limits rather than blindly taking 50 rows, and only a 204 removes it. Anything else is kept and retried with bounded backoff — local state is never lost to a failed send, and an upload never blocks the command (it runs in a detached worker that receives no unrelated environment variables).

sr feedback outcome status      # is it on, what is queued, where it goes
sr feedback outcome off         # stop: clears pending, aborts an in-flight upload
sr feedback outcome on

SR_OUTCOME=off, SR_JOURNAL=off and SR_EXTERNAL_NO_REPORT=1 each disable outcome events too, and sr feedback off turns the whole client-reporting chain (outcome events) off in one step. Turning reporting off clears the pending queue and an upload already in flight can never write it back. The server side aggregates separately: turning the switch off stops future sends, it does not erase what an earlier run already delivered. Installed/failed receipts remain client self-reports, download is a server-verified download, and shown/top1 come from the final rank response — none of them is independent proof that a user saw or ran anything.

sr diagnose

Reads the four local journals (resolve, ensure, runs, feedback, rotation files included), stitches them into one chain — listed → picked → ran → acknowledged — and prints a five-section report on this machine only:

sr diagnose [--since <days>=30] [--window <minutes>=30] [--json]
  --export <file>        counts-only JSON export (0600)
  --with-said            also export the feedback quotes — the user's own words
  --merge <file>...      corroborate several exports; >= --k distinct accounts make a suspect
  --k <n>=2              distinct non-empty accounts a (site, cmd) failure needs
  --proposals <file>     default ~/.skillrouter/patrol/status-proposals.json (whole-file, 0600)
  --requests <file>      default ~/.skillrouter/patrol/requests.jsonl (append, 0600)
  1. 列了没挑 — resolves that had candidates but no ensure/runs within the window, plus the nofit count and the top needs. need is user text and is shown only here.
  2. 挑了没跑成 — runs by (site, cmd, outcome), outcome ≠ ok only, with the objective suspect set {selector_miss, shape_mismatch, crash} marked.
  3. agent 主观 — feedback by (skill, command, outcome); a lead, never a weight.
  4. 混淆对 — where the top-1 candidate was not the skill that was actually ensured/run.
  5. 覆盖缺口 — coverage: none / next: search_then_site by site (old journals may still carry the retired withheld-candidate reason, which diagnose keeps grouping), plus feedback fallback by skill.

An ensure/runs/feedback line belongs to the nearest preceding resolve inside the window, so nothing is counted twice. Sites that no resolve candidate maps are grouped as site:<site>; a skill name is never guessed.

--export writes counts only: runs, feedback, confusions, unrouted and an account that is sha256("sr-diagnose:" + <api key>) truncated to 16 hex (or null without a key). It contains no need, no quotes, no per-line timestamps, no pid and no paths. --with-said is the only way quotes enter the file (each ≤ 200 characters) and it must be asked for explicitly; it prints one reminder line — on stdout normally, and on stderr when --json is used, so the JSON report on stdout stays machine-parseable. Nothing here touches the network, and diagnose never writes the journal, the index or sitedata.

The export and the proposal file are replaced atomically (same-directory temp file + rename), so a failed write leaves the previous file byte-identical instead of truncating it; a refused or failed target never turns into a partial file, and the --requests append says so when it cannot promise that. Only a directory diagnose itself creates is tightened to 0700 and the files are 0600: a directory you already had (a shared or synced one you pointed --export at) keeps its mode.

--merge corroborates exports collected from several machines: a (site, cmd) whose objective failure appears under at least --k distinct non-null accounts becomes a suspect proposal. One account counted twice, account: null, and the empty outcome never do. Proposals overwrite the proposals file; each new proposal is also appended to the patrol request file as {ts, skill, site, cmd, reason:"suspect", accounts, source:"diagnose"} — the same (site, cmd) within 7 days is not repeated. The request file is a local hand-off for a maintainer; the automated patrol sweep that used to consume it was retired on 2026-09-22.

sr manual <skill>

Looks the skill up in the catalog (by its skill id, legacy handle, or title). The CLI then requests the manual endpoint directly — with no account too; a saved key is sent when one exists. On success the full guide lands in the private ordinary cache ~/.skillrouter/manuals/<skill>/{SKILL.md,meta.json}. The CLI verifies the endpoint SHA-256 before an atomic write; this directory is never added to native skill roots. The Router reads it only after a request selects that product. Old ~/.skillrouter/skills/<skill>/SKILL.md files remain a read-only fallback and are never overwritten by sr manual. When the server still answers 401/403 (an older backend), the response falls back to the public product-page URL instead of the full text. Unreleased skills (status: coming) are not downloadable. Pass --print to emit the verified Markdown body after caching it; the default JSON response returns the readable body and metadata paths.

Compatibility notes

The webnav runner keeps two legacy output columns, charged_points and free_trial, for schema compatibility. They are always 0 / false; use member: true as the access signal.