Default Nub usage needs no shims: nub install, nub add, and nub run already provision and run the pinned package manager. The shims are for when you'd rather type the manager directly — bare pnpm install, npm ci, yarn add — and still have it go through Nub. One command turns that on.

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

After nub pm shim, bare pnpm resolves to a shim in ~/.nub/shims — a hardlink to the Nub binary. On a cold cache, pnpm --version (or any pinned-PM command) provisions the pinned pnpm, then dispatches to it in-process under the project's Node. The dim pnpm@9.5.0 (via nub shim) line names the manager and version it ran; it prints on stderr on every dispatch, so it never pollutes piped stdout. To remove the shims, nub pm unshim deletes the shim directory and strips the PATH block, restoring the previous pnpm.

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

Shims vs the pin

Two separate steps. The pin lives in package.json: nub pm use pnpm writes the packageManager field (and a devEngines.packageManager range) and aligns the lockfile, setting which manager the project uses. It does not touch your PATH or install any shim.

With a pin set, nub install already runs the right pnpm. The shims are the separate opt-in that makes the bare pnpm command — typed at a shell, or invoked by a tool you don't control — route through Nub too. Set the pin with nub pm use; install the shims once with nub pm shim if you want bare commands intercepted.

Per-invocation overhead

A shim sits in front of every command, so what matters is the time it adds. A corepack-style shim is a Node script (#!/usr/bin/env node), so invoking it boots the Node interpreter to read your pin and load the manager before any install work begins. The Nub shim is a hardlink to the native binary: the process that resolves the pin and dispatches is the one already running — no extra interpreter in front of the package manager.

Strict by default

In a pinned project, a shim refuses to run a different package manager — a competing lockfile and node_modules are exactly what you don't want. Bare yarn add react in a pnpm-pinned project exits nonzero and names what to run instead:

$ yarn add react
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.   # ❌ wrong package manager for this project
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

Runner commands stay transparent. The npx and pnpx tools and the init / create / dlx / exec verbs fall through to the system tool whatever the pin, so npm create vite works in a pnpm project. A package manager spawned by a running install — a lifecycle script shelling out to a different one — also falls through, so an install you never typed directly isn't broken.