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-syncer

v0.9.0

Published

Vendor Claude Code skills and subagents from any catalog repo into your repo, with a lockfile and content hashing. Run via npx.

Readme

skills-syncer

CI

Vendor Claude Code skills and the subagents they use from one catalog into many repos — real file copies, recorded by a lockfile. One source of truth instead of a copy you hand-edit everywhere.

Unlike a git submodule or a symlink, each repo gets a real copy committed into .claude/, so teammates, CI, and sandboxes never need this tool at runtime — only the person adding or updating a skill runs the sync.

  • Zero runtime dependencies (Node built-ins only).
  • No build step — run it straight from npm with npx.
  • git is needed only for github: sources; local catalogs need nothing.

Quick start

Run from the repo you want to set up. npx fetches the tool; the catalog comes from --from:

npx skills-syncer --from github:acme/our-skills --skill fsd-rules react-rules
✓ synced 2 skills · 1 agent · AGENTS.md → my-repo  (github:acme/our-skills)

Then commit what it wrote: .claude/skills/, .claude/agents/ (if any), AGENTS.md, skills-syncer.json, and skills-syncer-lock.json.

Usage scenarios

1. Set up a brand-new repo

Pick the skills you want; their required agents come along automatically.

npx skills-syncer --from github:acme/our-skills --skill fsd-rules react-rules

2. Install everything

npx skills-syncer --from github:acme/our-skills --skill '*' --agent '*'

A '*' selection is stored literally in skills-syncer.json, so a later bare re-sync (scenario 4) picks up skills added to the catalog since.

3. Add an agent on its own

Agents are a first-class catalog — install one directly even if no selected skill requires it.

npx skills-syncer --from github:acme/our-skills --skill run-maintain --agent worker

4. Re-sync later to pull catalog updates

With no flags, the source and selection are read from skills-syncer.json. Run this after the catalog changes to refresh this repo's copies:

npx skills-syncer

Commit the diff. Because it is idempotent, re-running with no changes is a no-op.

5. Narrow or change the selection

Pass a new --skill/--agent set. Items dropped from the selection are removed from .claude/ (the run prints removed skills: …); repo-authored skills and agents are never touched.

# was fsd-rules + react-rules; now just fsd-rules — react-rules is removed
npx skills-syncer --from github:acme/our-skills --skill fsd-rules

6. Develop against a local catalog

Point --from at a checkout or any folder. Useful while authoring skills before pushing them.

npx skills-syncer --from ../our-skills --skill '*'
npx skills-syncer --from /abs/path/to/catalog --skill fsd-rules

7. Pin to a branch or tag

Append #ref to a github: source to clone a specific branch or tag.

npx skills-syncer --from github:acme/our-skills#v2 --skill '*'

8. Roll a catalog change out to many repos at once

The tool is pull-only: a change reaches a repo only when the sync runs there. --all re-syncs every immediate subfolder that has a skills-syncer.json — each from its own recorded source and selection — so one command updates a whole folder of repos. Run it from the folder that holds them (or pass --root):

cd ~/code/myorg          # a folder of sibling repos
npx skills-syncer --all              # re-sync each repo from its own source
npx skills-syncer --all --dry-run    # preview every repo, write nothing
npx skills-syncer --all --root ~/code/myorg   # scan a specific folder

It walks one level deep (worktrees and nested repos are not reached) and reports how many repos synced, were skipped (no skills-syncer.json), or failed. Repos that share a source are grouped, so a github: catalog is fetched once, not once per repo, and a repo whose source fails doesn't stop the rest.

The output is one aligned line per repo — the source is printed once per group, not on every line — with a summary at the end (colours show on a terminal, plain when piped or under NO_COLOR):

skills-syncer · syncing 4 repo(s)  (1 skipped — no skills-syncer.json)
  from github:acme/our-skills
    ✓ api-core     2 skills · AGENTS.md
    ✓ ui-kit      22 skills · 6 agents · AGENTS.md
    ✓ web-app     36 skills · 6 agents · AGENTS.md
    ✓ checkout     1 skill · AGENTS.md  −3 removed
  from github:acme/legacy-skills
    ✗ old-portal  could not clone https://github.com/acme/legacy-skills.git

done with errors · synced 4 repo(s), skipped 1 (no skills-syncer.json), failed: old-portal

A removed count (−3 removed) appears when a re-sync drops items no longer in the repo's selection.

Every sync is incremental: an item already matching the catalog is left untouched, so a re-sync with nothing to do is a true no-op (no file churn).

9. Preview a sync without writing

--dry-run (or -n) computes the full plan — what would be installed, overwritten, or removed — and writes nothing. Re-run without it to apply.

npx skills-syncer --from github:acme/our-skills --skill '*' --dry-run

Flags

| Flag | Meaning | | --- | --- | | --from <src> | catalog source: github:owner/repo[#ref] or a local path | | --skill <names…> | skills to install ('*' = all in the catalog) | | --agent <names…> | agents to install directly ('*' = all); a selected skill's required agents come automatically | | --all | re-sync every immediate subfolder that has a skills-syncer.json | | --root <dir> | with --all, the folder to scan (default: current dir) | | --no-claude-link | write no CLAUDE.md; remove one this tool made. Recorded in skills-syncer.json; applies to --all too | | --claude-link | CLAUDE.md is a symlink to AGENTS.md (the default) | | --claude-import | CLAUDE.md is a real file holding @AGENTS.md, the Claude Code import | | --no-hooks | vendor no hooks; remove ones this tool installed. Recorded in skills-syncer.json; applies to --all too | | --hooks | vendor the catalog's hooks (the default) | | --dry-run, -n | show what would change; write nothing | | --help, -h | show usage | | --version, -v | print the version |

With no flags, the source and selection are read from skills-syncer.json.

The source catalog

A source is just a directory — a github:owner/repo[#ref] (shallow-cloned) or a local path — laid out like this:

<catalog>/
  skills/<name>/SKILL.md ...     # or .claude/skills/<name>/  (auto-detected)
  agents/<role>.md               # or .claude/agents/<role>.md
  hooks/<file>                   # or .claude/hooks/  (optional) hook scripts
  settings.json                  # or .claude/settings.json  (optional) hook wiring
  skill-agents.json              # optional: { "<skill>": ["<agent>", ...] }
  AGENTS.md                      # optional: shared instructions block
  • Hooks are not part of the selection. A skill is a document an agent loads; a hook is repo-wide wiring that runs whether an agent asks for it or not. So every repo takes all of the catalog's hooks, or none of them with --no-hooks. See Hooks below.
  • Skills and agents are two catalogs. An agent installs when it is named with --agent, or required by a selected skill via skill-agents.json. So selecting an orchestrator skill never leaves it without its agents.
  • AGENTS.md is merged into the top of the target repo's AGENTS.md inside fenced markers; repo-specific notes below the block are preserved across re-syncs. Alongside it the sync points CLAUDE.md at AGENTS.md so Claude Code reads the same instructions — a symlink by default, an @AGENTS.md import with --claude-import, or nothing with --no-claude-link. Either way the target is a sibling in the same repo, so it still rides with git into worktrees and sandboxes. A repo-authored real CLAUDE.md is left untouched — see below.

Choosing the CLAUDE.md shape

Three shapes, one flag each. All three keep a single copy of the text in AGENTS.md.

| Flag | What lands at CLAUDE.md | | --- | --- | | (default) / --claude-link | a symlink to AGENTS.md | | --claude-import | a real file holding @AGENTS.md — the Claude Code import | | --no-claude-link | nothing |

npx skills-syncer --claude-import             # this repo
npx skills-syncer --all --claude-import       # every repo under the folder

Use --claude-import where a symlink does not survive: Windows without developer mode, an export or archive step that dereferences links, a copy that flattens them. The file is one line, so git treats it like any other text file:

@AGENTS.md

Switching is free — a later --claude-link turns the file back into a symlink, and --no-claude-link removes it. Only what this tool wrote is replaced or removed: a CLAUDE.md the repo authored is left alone, with a warning. (A symlink named CLAUDE.md counts as this tool's, so a stale one is repointed.)

A non-default choice is recorded in skills-syncer.json ("claudeLink": false or "claudeLink": "import"), so plain re-syncs — including --all, which reads each repo's own config — keep honouring it with no flag to repeat. Unlike --from/--skill/--agent, these flags are not ignored by --all: they override every repo's recorded value, so a fleet flips in one run. Passing two of them together is an error.

Bundled catalog (ship the tool with your catalog)

A catalog repo can bundle skills-syncer as its own bin, so consumers run it straight from the catalog with no --from:

// package.json in your catalog repo
{ "bin": { "your-catalog": "bin/skills-syncer.mjs" } }
npx github:acme/our-skills --skill '*'   # the catalog is its own source

When no --from is given and there is no skills-syncer.json, the tool falls back to its own package root if that carries a catalog (skills/, .claude/skills/, …). The lock records the catalog's package name as the source, and skills-syncer.json keeps only the selection (a bare re-sync resolves the bundled catalog again).

What it writes into your repo

| File | Role | | --- | --- | | .claude/skills/<name>/ | each selected skill folder (real copy) | | .claude/agents/<role>.md | each selected/required agent (registered subagent) | | .claude/hooks/<file> | every hook script in the catalog (real copy) | | .claude/settings.json | the catalog's hooks block, merged in; every other key is left alone | | AGENTS.md | shared block merged in, repo notes kept below | | CLAUDE.md | symlink → AGENTS.md, or @AGENTS.md in a real file with --claude-import, or nothing with --no-claude-link (a repo-authored CLAUDE.md is never touched) | | skills-syncer.json | your choice: source + selection (hand-editable, committed) | | skills-syncer-lock.json | generated manifest: per-item content hash |

A re-sync replaces only what the lock installed and removes what you dropped from the selection — it never touches a repo-authored skill or agent.

Both JSON files are written the way prettier and biome print them: two-space indent, one key per line, and a short array kept on one line while it fits 80 columns. A repo formatter therefore has nothing to fix, and a re-sync has no formatting to undo. Should a formatter still differ (a repo that indents with tabs, say), it wins: the tool rewrites a file only when the data changes, never to restyle it.

skills-syncer.json is hand-editable — change the from, skills, or agents fields and run a bare npx skills-syncer instead of retyping flags:

{
  "from": "github:acme/our-skills",
  "skills": ["fsd-rules", "react-rules"],
  "agents": ["worker"],
  "claudeLink": "import"
}

claudeLink is optional. It appears only when the repo picked something other than the default symlink: "import" for the @AGENTS.md file, or false for no CLAUDE.md at all.

Hooks

A skill is a document an agent chooses to load. A hook is different. It runs on an event, whether an agent asks for it or not, and it only runs when .claude/settings.json points at it. So the catalog ships both halves:

<catalog>/
  hooks/language-check.mjs     # the script
  settings.json                # { "hooks": { "Stop": [ ... ] } }

The sync copies every file under hooks/ into the repo, and merges only the hooks block of settings.json. Every other key in the repo settings is left alone. Use $CLAUDE_PROJECT_DIR in a command so the path works in any repo:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/language-check.mjs\"" }
        ]
      }
    ]
  }
}

The lock records each hook group the tool installed. A later sync takes those exact groups back out before it writes the new ones. So:

  • A hook you add to the repo by hand is never removed.
  • A hook dropped from the catalog is dropped from the repo.
  • A changed command replaces the old one instead of piling up next to it.
  • A hook file the repo authored is never overwritten. The tool warns and skips it.

A repo opts out with --no-hooks, which is recorded as "hooks": false in skills-syncer.json. Opting out also removes the hooks the tool installed earlier, and deletes .claude/settings.json when nothing else is left in it.

Requirements

  • Node ≥ 18
  • git on PATH (only for github: sources; local paths need nothing)

License

MIT