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

skills-lock

v0.1.0

Published

The lock layer for agent skills — manifest in package.json, skills-lock.json with pinned commits and content hashes, reproducible installs.

Readme

skills-lock

The lock layer for agent skills. Skills are declared in package.json, pinned to exact commits and content hashes in skills-lock.json, and installed into agent directories (.claude/skills/, .cursor/skills/, …) with one command. Like a package manager's lockfile workflow, but the packages are SKILL.md directories and the registry is any git repo.

// package.json
{
  "skills": {
    "frontend-design": "anthropics/skills",
    "code-review": "acme/skills#v2.1.0",
    "deploy": { "source": "acme/skills#v2.1.0", "path": "ops/deploy" }
  },
  "skillsConfig": { "agents": ["claude-code", "cursor"] }
}
$ skills-lock install
+ frontend-design  anthropics/skills @ 575462609294  → claude-code, cursor
+ code-review      acme/skills#v2.1.0 @ 8f3c2a19bd04  → claude-code, cursor
+ deploy           acme/skills#v2.1.0 @ 8f3c2a19bd04  → claude-code, cursor
✓ 3 skill(s) installed, skills-lock.json written

Commit skills-lock.json. Teammates and CI run skills-lock install --frozen and get byte-identical skills, verified by content hash — or a hard failure.

Why

Skills today are distributed imperatively: npx skills add owner/repo fetches whatever is on main right now and vendors it into your repo. That leaves four gaps that every package ecosystem solved years ago:

  1. No manifest. There is no record of which skills a project depends on, so there is no "clone and restore" workflow.
  2. No version pinning. Fetching a branch head means upstream edits change your agents' behavior silently. A skill is a prompt injected into a privileged agent — silent drift is a supply-chain problem, not just a reproducibility annoyance.
  3. No lockfile / integrity. Even a pinned tag can be force-moved. Content hashes make tampering loud.
  4. No CI story. --frozen is what lets a pipeline assert "the skills in this build are exactly the ones the team reviewed."

package.json already sits in most repos and tolerates extra fields. It would suffice as the standard manifest — this tool is the existence proof.

Install

$ npm install -g skills-lock    # or: npx skills-lock ...

Requires Node ≥ 18 and git. Zero runtime dependencies.

Commands

| Command | What it does | |---|---| | skills-lock install | Resolve + install everything in "skills", write skills-lock.json. Already-locked skills are not re-resolved — the pin holds until you ask. | | skills-lock install --frozen | CI mode: no resolution, no lockfile writes. Fails if the lock is missing, out of sync with package.json, or any content hash mismatches. | | skills-lock add <source> [--skill name]… [--path dir] [--list] | Discover skills in a source, add them to package.json, install. --list previews without installing. | | skills-lock update [name…] | Re-resolve floating refs (e.g. #main) to their current commit and re-pin. The only command that moves a pin. | | skills-lock remove <name…> | Remove from manifest, lockfile, and all agent directories. | | skills-lock list | Declared skills with their pinned commits. |

Sources

owner/repo                  GitHub shorthand, repo default branch
owner/repo#v1.2.0           pinned to a tag (also: branch or full commit SHA)
github:owner/repo#ref       explicit prefix
https://host/repo.git#ref   any git remote (GitLab, internal mirrors, …)
file:../team-skills         local directory — no pinning, integrity only

A repo can host many skills (a catalog): skills-lock finds every SKILL.md and matches by frontmatter name, falling back to directory name. When a name is ambiguous, use the long form { "source": …, "path": … }.

The lockfile

{
  "lockfileVersion": 1,
  "skills": {
    "frontend-design": {
      "spec": "anthropics/skills",
      "resolved": "https://github.com/anthropics/skills.git",
      "ref": null,
      "commit": "57546260929473d4e0d1c1bb75297be2fdfa1949",
      "path": "skills/frontend-design",
      "integrity": "sha256-ZlxYVFXey8sCEWvJKK31FIuc0PZ7frSQjIwNZHwA9oo="
    }
  }
}

commit pins the source; integrity is a sha256 over the skill directory's file tree, so even a rewritten tag or a poisoned cache cannot slip modified instructions past --frozen.

Agents

Default target is claude-code (.claude/skills/). Configure more in package.json:

{
  "skillsConfig": {
    "agents": ["claude-code", "cursor", "my-agent"],
    "agentDirs": { "my-agent": ".my-agent/skills" }
  }
}

Built-in: claude-code, cursor, opencode, windsurf, github-copilot, amp, universal (.agents/skills).

CI

- run: npx skills-lock install --frozen

Set SKILLS_LOCK_CACHE to relocate the git checkout cache (default: .skills/cache/ in the project, auto-gitignored) — useful for sharing a cache directory across CI jobs.

Security

A skill is a prompt injected into a privileged agent, and the manifest that selects skills is often written by someone other than the person running the install — you clone a repo and run skills-lock install to restore its skills. So the threat model treats package.json and skills-lock.json as untrusted input, and running install against a hostile manifest must not execute code or touch anything outside the project.

What that means concretely:

  • Source transports are allowlisted. Only http(s)://, git://, ssh://, and explicit local paths are accepted. Git's ext:: transport (which runs an arbitrary command) and other schemes are refused — including via the git+ prefix, which does not bypass the check.
  • No git argument injection. Refs and URLs may not begin with -, and -- terminates option parsing before any user value reaches git. Commits read from the lockfile must be full 40-hex SHAs before they touch git.
  • No path escapes. Skill names are restricted to a safe directory charset; the install destination, entry.path, and skillsConfig.agentDirs overrides are all asserted to stay inside their expected directory before any recursive remove or copy.
  • No symlinks. A skill tree containing a symlink is refused, closing both the integrity-hash bypass (symlinks evade content hashing) and the arbitrary-file-read risk (a symlink copied into an agent dir pointing at, say, ~/.ssh/id_rsa).
  • Integrity is verified. install --frozen recomputes a sha256 tree hash of each skill and fails on any mismatch, so a rewritten tag or a poisoned cache cannot slip modified instructions past CI.

These guarantees are covered by adversarial regression tests in test/e2e.mjs (each asserts the absence of the bad effect — no executed payload, victim directories left intact — not merely that an error is shown).

Two residual notes: file: sources are inherently local-trust (they read a path you put in your own manifest), and ssh:// hostname hardening relies on your installed git (use git ≥ 2.30). Found a gap? Please open an issue.

How this relates to prior art

| | manifest | lockfile | pinning | integrity | new infra needed | |---|---|---|---|---|---| | npx skills add (skills.sh) | — | — | ref in URL, not recorded | — | none | | paks | per-skill frontmatter | — | --version at install | — | own registry | | pixi/conda (pavel.pink) | pixi.toml | pixi.lock | semver | conda hashes | conda packaging + pixi in every project | | skills-lock | package.json | skills-lock.json | tag/branch/SHA → commit | sha256 tree hash | none — any git repo is a registry |

The pixi approach is the most complete (conda's run_constraints can even couple a skill's version to the library it documents) but demands conda tooling in every consuming project. skills-lock deliberately targets the other end: zero new infrastructure over the way skills are already distributed — git repos of SKILL.md directories.

Status & direction

This is a working prototype making a standards argument: the manifest format for agent skills should be package.json, and installs should be lockfile-driven. The ideal endgame is not another tool — it's this workflow (manifest + lock + --frozen) absorbed into the ecosystem leader (vercel-labs/skills), whose agent-directory map already covers 70+ agents.

Not yet handled, by design or by youth:

  • Semver ranges (^1.2) — would resolve against git tags; pinned refs cover the core need first.
  • Global scope (~/.claude/skills) — project scope is where reproducibility matters most.
  • Skill dependencies — SKILL.md frontmatter has no standard field yet.
  • Symlink installs — copies are maximally compatible; symlinks into the cache would give a single source of truth.

License

MIT