Default Nub usage needs no shims: nub install, nub add, and nub run work on Nub's own engine, and the pin tells them which package manager's lockfile and config to follow. The shims run the exact pinned release for a bare pnpm, npm, or yarn command, whether you type it or a tool you don't control invokes it.

$ nub pm shim
nub pm shim: 6 entries in ~/.local/share/nub/shims (6 created)
PATH: added ~/.local/share/nub/shims to PATH (~/.zshrc) — restart your shell
​
$ which pnpm
~/.local/share/nub/shims/pnpm
​
$ pnpm --version
pnpm@9.5.0 (via nub shim)
Installing... (4 MB)
Installed in 0.8s
9.5.0

Each shim is a hardlink to the Nub binary, so no Node process starts in front of the package manager. On a cold cache the first command provisions the pinned version, then starts its launcher under the project's Node. The dim (via nub shim) line names the manager and version, and goes to stderr so piped stdout stays clean.

pnpm 12 ships as a native binary, and the shim runs it the way Corepack does: pnpm's own launcher downloads the binary on first use and keeps it beside the cached package, so later runs need no network. From pnpm 12.11.0 that download follows the registry and credentials in .npmrc. Earlier 12.x releases ignore .npmrc, so point them at a private mirror with COREPACK_NPM_REGISTRY, and COREPACK_NPM_TOKEN if it needs auth.

The shims live in the first of these directories:

  • $XDG_DATA_HOME/nub/shims — when that variable is set
  • %LOCALAPPDATA%\nub\shims — Windows
  • ~/.local/share/nub/shims

The shims do not choose the manager; the packageManager pin in package.json does. Set it with nub pm use, which never touches your PATH.

Strict by default

In a pinned project, a shim refuses to run a different package manager, which would write a competing lockfile and node_modules. The error names the command to run instead:

$ yarn add react   # ❌ wrong package manager for this project
nub: the nub package-manager shims on your PATH (installed via
     `nub pm shim`) intercepted this.
This project pins pnpm (via package.json#packageManager) — refusing to
run yarn.
A different package manager here would write a competing lockfile
and node_modules.

  run instead:  pnpm add react
  to bypass:    invoke the system yarn by absolute path,
                or remove the shims: nub pm unshim

Some commands fall through to the system tool whatever the pin, so npm create vite works in a pnpm project:

  • The npx and pnpx commands, and the init, create, dlx, and exec verbs of every manager
  • Commands that never touch the project: global installs (npm install -g) and registry and account commands such as npm login, npm whoami, and npm view
  • A package manager that a lifecycle script spawns during an install

npm publish still refuses, because it packs the project.

Missing system manager

An unpinned command falls through to the system manager. When there is none, as on a fresh machine or in a minimal container, the shim runs a default instead: the version family the committed lockfile implies, or the registry latest without one. It names the choice on stderr and writes no pin.

A resolved latest is remembered for 24 hours, and the last resolved version is reused when the registry is unreachable.

Running npm installs on Nub's engine

One opt-in routes npm's two install verbs to Nub's engine instead of the pinned npm:

nub pm shim --route-installs      # turn routing on
nub pm shim --no-route-installs   # turn it off, keep the shims

With routing on, a bare npm ci in a project with a package-lock.json runs nub ci, and a bare npm install runs nub install, from the same lockfile:

$ npm ci
npm ci → nub ci (via nub shim)

Every other npm command runs the real npm with the argv as typed. For the two install verbs, the input decides:

npm inputRouted install
--ignore-scriptsTranslated
--omit=dev, --production, NODE_ENV=productionTranslated
--omit=optionalTranslated
--includeTranslated; wins over --omit in either order, as in npm
--no-audit, --no-fund, --prefer-offline, log-level and progress flagsAccepted and ignored
--legacy-peer-deps, --workspace, --prefix, --no-package-lockNot routed; runs the real npm
A named package, a global installNot routed; runs the real npm
An npm install spawned by a lifecycle script during an installNot routed; runs the real npm

A routed install runs every lifecycle script, as npm does, under the node on PATH. It consults no Node pin and provisions nothing, and it hands the scripts NODE_ENV=production exactly when dev dependencies are omitted. An ignore-scripts=true in the project or user .npmrc, or npm_config_ignore_scripts in the environment, is honored, and --ignore-scripts=false on the command line outranks it.

Removing the shims

The nub pm unshim command deletes the shim directory and the PATH block, restoring the previous pnpm. It also removes install routing.

$ nub pm unshim
nub pm unshim: removed /Users/you/.local/share/nub/shims
  PATH: removed the shims block from /Users/you/.zshrc