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

@piotar/pkg-sync

v2.4.0

Published

A utility for synchronization multiple packages, without any symlinks and any changes in projects(package.json, etc.).

Readme

pkg-sync

GitHub package.json version npm (scoped) node-current (scoped) NPM

Develop a local package and see it live in another project — without symlinks and without touching any package.json.

pkg-sync watches a package's build output and copies it straight into the consuming project's node_modules, so the project sees it exactly as if it were installed from the registry.

Why not npm link?

npm link creates a symlink, and symlinks break in many real-world setups: bundlers resolving a dependency twice (duplicate React, hooks errors), peerDependencies resolved from the wrong tree, tools that don't follow symlinks, and Windows quirks. Publishing to a registry on every change is too slow for tight feedback loops.

pkg-sync avoids all of that by copying real files into node_modules/<package>. Nothing in the consuming project changes — no symlink, no edited package.json, no lockfile churn — so the dependency behaves like a normal install.

How it works

  • You register a package once by its path. Its name and location are stored in a global registry at ~/.pkg-sync/data.json.
  • On sync, pkg-sync reads the target project's package.json, resolves its dependency tree up to a configurable depth (default 2), and intersects it with the registered packages. Every match is mirrored from its source into the project's node_modules.
  • Only relevant files are copied: by default the dist, lib, build and src directories, minus common noise (VCS folders, lockfiles, editor files). A package can override which directories are watched.
  • With watching enabled (the default), changes in a source package are debounced and re-copied automatically, giving a live local feedback loop.

Installation

npm install -g @piotar/pkg-sync

Requires Node.js >= 22.

Usage

We have a main project (App) and 2 dependencies (Ui and Store) in different locations (this is not a monorepo):

~
├── projects
│   └── App
│       ├── node_modules
│       ├── package.json
│       └── etc...
└── external
    ├── Ui
    │   ├── node_modules
    │   ├── package.json
    │   └── etc...
    └── Store
        ├── node_modules
        ├── package.json
        └── etc...
  1. Register each dependency — from ~/external/Ui and ~/external/Store run pkg-sync add . (. resolves the name from the closest package.json).
  2. From ~/projects/App run pkg-sync sync . — this copies the dependency files into App/node_modules and starts watching for changes.

That's it. Step 1 is only needed the first time; afterwards a single pkg-sync sync . is enough.

Configuration

Settings live in the same global registry and are managed with pkg-sync config:

  • pkg-sync config get — print all settings.
  • pkg-sync config set depth 3 — how deep the dependency tree is searched during sync/validate (values are parsed as JSON).
  • pkg-sync config restore — reset to defaults.

To watch non-default directories for a specific package, pass them when registering: pkg-sync add . -d dist,types.

Colored output honors the NO_COLOR environment variable and is disabled automatically when output is not a TTY.

Using from automation / AI agents

Every command accepts --json and prints a single JSON object on stdout:

pkg-sync list --json
pkg-sync validate /path/to/app --json      # { "packages": ["my-lib"] }
pkg-sync sync /path/to/app --no-watch --json   # { "synced": ["my-lib"], "watch": false }
pkg-sync status --json                          # { "targets": [{ "path": "/app", "packages": ["my-lib"], "syncedAt": 0, "stale": false }] }
pkg-sync unsync /path/to/app --json             # { "unsynced": ["my-lib"], "reinstalled": true }

The output contract:

  • stdout carries data only — human text, or one JSON line under --json. Diagnostics, progress and the update notice go to stderr, so pkg-sync list --json is always parseable.
  • Exit codes: 0 on success, 1 on error. Under --json, errors are emitted as {"error": "<message>"} on stderr.
  • Stay non-interactive: always pass --no-watch (the watcher never returns) and avoid -i/--interactive (it needs a TTY) — pass package names/paths explicitly instead.

A SKILL.md (agentskills.io format) ships with the package so the tool can be taught to AI agents and installed via a skill manager.

Claude Code plugin

The package doubles as a Claude Code plugin — a .claude-plugin/plugin.json manifest exposes the bundled SKILL.md as a pkg-sync skill. Load it straight from node_modules (no separate install):

claude --plugin-dir node_modules/@piotar/pkg-sync     # per-session

To make the skill available globally instead, symlink the installed package into your skills directory — it then auto-loads in every session:

ln -s "$(npm root -g)/@piotar/pkg-sync" ~/.claude/skills/pkg-sync

Either way Claude gains a pkg-sync skill that knows when and how to mirror your local package into another project.

Commands

Run pkg-sync <command> --help for the full, up-to-date options of any command.

add [path]

Register a package so it can be synced. path defaults to the closest package.json.

  • -n, --name <name> — override the package name (instead of the one in package.json)
  • -f, --force — overwrite an existing registration
  • -d, --dir <dirs> — comma-separated directories to watch, overriding the defaults

remove [packages...] (alias rm)

Unregister packages. Use . for the name in the closest package.json.

  • -i, --interactive — pick packages to remove from a list
  • -a, --all — remove every registered package

list (alias ls)

Show the data file path, the default watch directories, and every registered package.

validate [path]

Preview which registered packages would be synced for a project, without copying anything.

  • -d, --depth <n> — dependency search depth (default 2)

sync [path] [packages...]

Copy registered dependencies into a project's node_modules and watch for changes. Pass package names to limit the sync to a subset.

  • --no-watch — copy once and exit instead of watching
  • -i, --interactive — pick packages to sync from a list
  • -d, --depth <n> — dependency search depth (default 2)

unsync [path] (alias restore)

Remove previously synced files from a project and restore the published versions. Operates on the syncs recorded for the project (see status); path defaults to the closest package.json.

  • --no-reinstall — only delete the synced files, skip the package-manager reinstall
  • -i, --interactive — pick which synced packages to unsync from a list

The package manager is detected from the project's lockfile (bun, pnpm, yarn, else npm).

status [path]

Show which packages are currently synced into which projects. Without path, every recorded target is listed; a target is flagged stale when its node_modules no longer exists.

config <set|get|restore>

Manage stored settings.

  • config set <key> <value> — set a value, parsed as JSON (e.g. config set depth 3)
  • config get [key] — print one value, or all settings when no key is given
  • config restore — reset the config to defaults

update-check

Check npm for a newer version of pkg-sync.