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

@ray_of_goodness_/skillslink

v0.4.3

Published

Find, validate, publish and pull agent skills and Agent Plugins from a SkillsLink registry

Readme

@ray_of_goodness_/skillslink

Find, validate, publish and pull agent skills and Agent Plugins packages from the terminal. No runtime dependencies. Installs a skillslink command.

Install

npm i -g @ray_of_goodness_/skillslink
# or run it without installing
npx @ray_of_goodness_/skillslink scan

Working on the CLI itself? Link your checkout instead:

cd packages/cli && npm link

Commands

skillslink login [--registry <url>]        Store an API token in ~/.skillslink.json
skillslink logout                          Remove the stored token
skillslink whoami                          Show the current user and registry
skillslink scan [dirs...] [--all] [--depth <n>] [--json] [--push]
skillslink plugins [dirs...] [--depth <n>] [--json] [--push] [--fix]
skillslink validate [dir] [--json] [--fix] [--dry-run] [--yes]
skillslink push <dir> [--slug <slug>] [--version <v>] [--public]
skillslink sync [--prefix <p>] [--dry-run] [--public] [--yes]
skillslink pull <author/slug> [--dir <target>] [--version <v>]
skillslink install [<author/slug>...] [--all] [--force] [--dry-run] [--yes]
skillslink backups                         Snapshots taken before each install
skillslink restore [<id>] [--prune] [--dry-run] [--yes]
skillslink list                            Everything you published
skillslink delete <slug...> [--dry-run] [--yes]
skillslink config                          Show current config (token masked)

sync options
  --prefix <p>     Prefix every slug, not just the conflicting ones
  --dry-run        Show the plan without uploading
  --public         Publish everything publicly (private by default)
  --yes            Skip the confirmation prompt
  --skills-only    Skip configs and plugins
  --configs-only   Skip skills and plugins
  --plugins-only   Skip skills and configs
  --no-plugins     Skip plugins

install options
  --all            Install everything you published
  --dir <root>     Install under this root instead of the published source
  --force          Overwrite local files that differ
  --dry-run        Show the plan without writing
  --yes            Skip the confirmation prompt
  --no-backup      Skip the pre-install snapshot
  --skills-only    Skip configs and plugins (with --all)
  --configs-only   Skip skills and plugins (with --all)
  --plugins-only   Skip skills and configs (with --all)
  --no-plugins     Skip plugins (with --all)

plugin options
  --fix            Migrate a pre-standard package to Agent Plugins 1.0.0
  --dry-run        Show the migration without writing
  --yes            Skip the confirmation prompt
  --no-backup      Skip the pre-migration snapshot
  --push           Publish every valid plugin found
  --json           Machine-readable report

restore options
  --prune          Also delete files the install created
  --dry-run        Show what would be restored
  --yes            Skip the confirmation prompt

delete options
  --dry-run        Show what would be deleted
  --yes            Skip the confirmation prompt

Scanning

scan walks directories and reports every folder containing a SKILL.md, reading name and version from the frontmatter. It stops descending once it finds a SKILL.md, follows symlinks without looping, and skips node_modules, VCS folders, build output and Library.

Default roots:

~/.claude/skills        ~/.claude/plugins       ~/.config/claude/skills
~/.codex/skills         ./.claude/skills        ./skills        ./examples

Pass directories explicitly to override them, or --all to sweep the whole home directory (depth 8). --push publishes everything found; skills whose version already exists are reported as skipped rather than failing the run.

Agent Plugins

An Agent Plugins package is a directory with a plugin.json manifest. It ships skills under skills/, MCP servers in mcp.json and client-specific files under reverse-domain directories, and it travels as one unit.

skillslink validate ./my-plugin      # conformance report, exit code 1 when invalid
skillslink plugins                   # every plugin.json on the machine
skillslink push ./my-plugin          # publish the package
skillslink install bohdan/my-plugin  # back to ~/.claude/plugins/<manifest name>

validate implements Agent Plugins 1.0.0 rather than just its JSON Schemas, so it enforces what the schemas cannot express:

  • plugin.json is a closed document. Unknown top-level fields and a non-object extensions are reported and ignored; anything else invalid — a bad name, a foreign author field, an unsupported $schema — rejects the package.
  • name is 1–64 characters of a-z0-9.-, alphanumeric at both ends, with no -- or ...
  • mcp.json is a closed union per transport. A command must be one executable token — a bare name or a ./ path inside the package, never a shell string. cwd must be ./…, ${PLUGIN_ROOT}… or ${PLUGIN_DATA}… and may not escape what it is rooted in. Remote urls must be absolute HTTPS (HTTP only for loopback), with no user information and no fragment. env may not set PLUGIN_ROOT or PLUGIN_DATA — the loading client provides those.
  • Only immediate children of skills/ holding a SKILL.md are skills; deeper directories are never searched.
  • Failures stay at their own boundary: a broken mcp.json disables MCP, a single invalid server entry is skipped, and neither stops the rest of the package from loading.

push picks the kind from the directory — a plugin.json means a plugin, a bare SKILL.md means a skill — and the registry re-runs the same validation before storing anything.

Installed plugins land in ~/.claude/plugins/<manifest name>, one directory rooted at a single location. PLUGIN_ROOT and PLUGIN_DATA are the loading client's job, not the installer's.

Migrating a pre-standard package

Plugins written before the specification keep their manifest under .claude-plugin/, .codex-plugin/ and the like, with client-specific fields at the top level and MCP servers either inline or in a .mcp.json of their own. --fix turns one into a conformant package:

skillslink validate ./old-plugin --fix --dry-run   # what it would write, and why
skillslink validate ./old-plugin --fix             # asks, snapshots, then writes
skillslink plugins --fix --dry-run                 # every non-conformant package on the machine
skillslink plugins --fix                           # migrate them in one pass

What it does:

  • Writes a root plugin.json with the canonical $schema, keeping name, version, description, author, homepage, repository, license and keywords where they are legal.
  • Moves everything the closed schema has no room for — commands, hooks, displayName, interface, a stray author.github, a version that is a number — under extensions["com.anthropic.claude-code"] (or the namespace matching the vendor directory it came from). Nothing is dropped.
  • Normalises a name that breaks the rules: My Awful--Name!! becomes my-awful-name, and the run says so.
  • Collects MCP servers from the inline mcpServers, ./.mcp.json and .claude-plugin/.mcp.json — including the bare-map variant with no mcpServers wrapper — into a proper mcp.json: transport inferred from command or url, ${CLAUDE_PLUGIN_ROOT} renamed to ${PLUGIN_ROOT}, a root-anchored command rewritten as a ./ path, a bare cwd rewritten as ./…, and a PLUGIN_ROOT or PLUGIN_DATA entry dropped from env since the client supplies it.

What it refuses to guess is reported as manual and left for you: a command that is a shell string (node server.js --flag), a plain-HTTP endpoint on a non-loopback host. Those servers are skipped rather than written half-right.

The legacy manifest stays where it is. It is what the old client still reads, so a migrated package works with both. Every file is snapshotted before it is written, so skillslink restore <id> --prune undoes the whole migration.

Syncing everything

sync pushes your skills and your agent configs in one run.

skillslink sync --dry-run     # show the plan, upload nothing
skillslink sync               # asks for confirmation, then pushes

It collects agents, commands, hooks, prompts, rules, output-styles and workflows, plus the loose *.json / *.toml / *.yaml settings at the root of each agent directory:

~/.claude    ~/.codex    ~/.cursor    ~/.gemini    ~/.opencode

sync also picks up plugin packages from ~/.claude/plugins and its siblings, and a skill that lives inside one is not published separately — the package already carries it. Invalid packages are skipped; run skillslink validate on them to see why.

Left out of the config bundles on purpose: skills and plugins (they are published as skills and plugins instead), per-machine runtime state (projects, todos, history, logs, cache, shell-snapshots), and anything matching *.local.*, credentials or auth.

Secrets are redacted before upload. Values under key names like token, api_key, secret or password, every entry inside an env or headers map, and anything shaped like a known credential (sk-ant-…, ghp_…, xoxb-…, JWTs) are replaced with __REDACTED__. The run reports how many values it scrubbed. Re-enter them by hand after pulling.

Everything you push is private unless you pass --public.

Name conflicts

Two sources often use the same folder name — ~/.claude/agents and ~/.cursor/agents both want the slug agents. The colliding ones get their source as a prefix; unique names are left alone:

claude/agents    -> claude-agents
cursor/agents    -> cursor-agents
claude/commands  -> commands

Skills that ship with a plugin are namespaced by that plugin instead, so two plugins can each carry an access skill, and a slug never moves the day you write a skill of the same name:

plugins/…/telegram/skills/access  -> telegram-access
plugins/…/discord/skills/access   -> discord-access
~/.claude/skills/access           -> access

A plugin also keeps its skills in several places at once — a marketplace checkout plus one cache directory per version ever installed. Those are the same skill, so a scan reports the checkout (or the newest cached version) and ignores the rest instead of publishing access-2, access-3.

The plugin name comes from its .claude-plugin/plugin.json, not from the directory it happens to sit in — a checkout under marketplaces/marketingskills and its cache copy under cache/marketingskills/marketing-skills both resolve to marketing-skills, so uninstalling the checkout does not rename anything you already published. Without a manifest the directory name is used instead.

--prefix work applies on top of all that, giving work-claude-agents and work-commands. A slug already taken in the registry by a different source is prefixed the same way.

Re-running sync is cheap: an unchanged bundle is reported as same and stores nothing, while a changed one is published as the next patch version.

Setting up a second machine

install is the reverse of sync. On a fresh machine, log in and pull everything back down:

skillslink login
skillslink install --all --dry-run   # show where every bundle would land
skillslink install --all             # asks for confirmation, then writes

Each bundle goes back where it came from. Skills land in ~/.claude/skills/<slug>; configs return to the directory they were published from, so a codex-settings bundle goes to ~/.codex and a settings bundle to the root of ~/.claude rather than into a folder of that name. --dir <root> overrides the root for the whole run.

A single bundle works too, and needs no token when it is public:

skillslink install bohdan/code-review

Local files win. A file that already exists with different contents is reported as kept and left alone — --force overwrites it. Unchanged files are same and are not rewritten, so re-running install is a no-op. Bundle paths that would escape their target directory are refused outright.

Configs come back with __REDACTED__ where your secrets were; the run tells you how many files need them filled in again.

Backups and rollback

Every install snapshots each file it is about to write before writing it, into ~/.skillslink/backups/<id>/. The last 20 runs are kept.

skillslink backups                    # ids, newest first
skillslink restore                    # put the newest snapshot back
skillslink restore <id> --dry-run     # see exactly what would change
skillslink restore <id> --prune       # also delete the files that install created

Files that existed are restored from the snapshot. Files the install created are recorded too but left in place unless you pass --prune, which is what makes a rollback complete. --no-backup skips the snapshot for that run.

sync needs no backup — it only reads local files and uploads them.

Deleting

delete removes your own skills and configs from the registry, every version of them, for good. Slugs accept * and ?:

skillslink delete my-skill               # asks for confirmation
skillslink delete '*-2' '*-3' --dry-run  # preview a cleanup sweep
skillslink delete '*-2' --yes            # and run it

author/slug works too, as long as the author is you. Without a terminal to answer the prompt, --yes is required.

Configuration

| Source | Key | | --- | --- | | ~/.skillslink.json | registry, token | | Environment | SKILLSLINK_REGISTRY, SKILLSLINK_TOKEN | | Flags | --registry, --token |

Flags win over environment, environment wins over the config file. Default registry is https://skills-sync-web.vercel.app.

Bundle rules

A skill directory must contain a SKILL.md. Dotfiles, node_modules, .git, dist, .output and .nuxt are skipped. Text files only: max 200 files, 1 MB per file, 8 MB total. --slug and --version fall back to name and version in the SKILL.md frontmatter.

A plugin directory must contain a plugin.json. Its whole tree is bundled — dotfiles included, since client extension data lives there — minus version control and build output. Max 300 files, 1 MB per file, 8 MB total, and --slug and --version fall back to name and version in the manifest. Binary files cannot be published: bundles are stored as text, so a plugin must not ship a compiled executable.