TL;DR
nypm is the UnJS universal package manager API — programmatic API to detect and run npm/yarn/pnpm/bun, auto-detects from lockfile, used by Nuxt CLI and other tools. ni is Anthony Fu's package manager runner — CLI that auto-detects your package manager and runs the right command (ni → npm install or pnpm install or yarn), the most popular. corepack is Node.js's built-in package manager manager — ships with Node.js, enforces package manager versions via packageManager field, transparent proxying. In 2026: nypm for programmatic package manager abstraction, ni for interactive CLI use, corepack for version enforcement.
Key Takeaways
- nypm: ~2M weekly downloads — UnJS, programmatic API, auto-detect, install/add/remove
- ni: ~500K weekly downloads — Anthony Fu, CLI runner, auto-detect, aliased commands
- corepack: ships with Node.js — version enforcement, transparent proxy,
packageManagerfield - All three solve "which package manager does this project use?"
- nypm is for tools/scripts; ni is for developers; corepack is for version pinning
- ni has the best DX for daily development workflows
nypm
nypm — universal package manager API:
Detect package manager
import { detectPackageManager } from "nypm"
// Auto-detect from lockfile:
const pm = await detectPackageManager("/path/to/project")
console.log(pm)
// → { name: "pnpm", version: "9.0.0", command: "pnpm" }
// or { name: "npm", version: "10.0.0", command: "npm" }
// or { name: "yarn", version: "4.0.0", command: "yarn" }
// or { name: "bun", version: "1.0.0", command: "bun" }
// Detection priority:
// 1. pnpm-lock.yaml → pnpm
// 2. yarn.lock → yarn
// 3. package-lock.json → npm
// 4. bun.lockb → bun
Install dependencies
import { installDependencies, addDependency, removeDependency } from "nypm"
// Install all dependencies (like `npm install`):
await installDependencies({ cwd: "/path/to/project" })
// → Runs the right command: npm install, pnpm install, yarn, or bun install
// Add a dependency:
await addDependency("express", { cwd: "/path/to/project" })
// → pnpm add express (or npm install express, etc.)
// Add as dev dependency:
await addDependency("vitest", {
cwd: "/path/to/project",
dev: true,
})
// → pnpm add -D vitest
// Remove a dependency:
await removeDependency("lodash", { cwd: "/path/to/project" })
// → pnpm remove lodash
Advanced options
import { installDependencies, addDependency } from "nypm"
// Force a specific package manager:
await installDependencies({
cwd: "/path/to/project",
packageManager: "pnpm",
})
// Silent mode:
await addDependency("react", {
cwd: "/path/to/project",
silent: true,
})
// With workspace support:
await addDependency("shared-utils", {
cwd: "/path/to/project",
workspace: true,
})
How Nuxt uses nypm
// Nuxt CLI uses nypm to install modules:
// When you run: nuxi module add @nuxt/image
// Internally:
import { addDependency } from "nypm"
async function installModule(name: string) {
// Auto-detects your package manager and runs the right command:
await addDependency(name, {
cwd: process.cwd(),
dev: true,
})
}
// Works regardless of whether the project uses npm, pnpm, yarn, or bun
ni
ni — CLI package manager runner:
Commands
# ni — install dependencies (like npm install):
ni
# → npm install / pnpm install / yarn / bun install
# ni <package> — add dependency (like npm install <pkg>):
ni express
# → npm install express / pnpm add express / yarn add express
# ni -D <package> — add dev dependency:
ni -D vitest
# → npm install -D vitest / pnpm add -D vitest
# nr — run script (like npm run):
nr dev
# → npm run dev / pnpm dev / yarn dev / bun run dev
# nr — interactive script selection:
nr
# → Shows list of scripts to choose from (fuzzy search)
# nun — uninstall (like npm uninstall):
nun lodash
# → npm uninstall lodash / pnpm remove lodash / yarn remove lodash
# nci — clean install (like npm ci):
nci
# → npm ci / pnpm install --frozen-lockfile / yarn install --frozen-lockfile
# nu — upgrade packages:
nu
# → npm update / pnpm update / yarn upgrade
Global install
# Install globally:
npm install -g @antfu/ni
# Or use with npx:
npx @antfu/ni
How detection works
ni detects your package manager by looking for lockfiles:
Priority:
1. bun.lockb → bun
2. pnpm-lock.yaml → pnpm
3. yarn.lock → yarn
4. package-lock.json → npm
Also checks:
- packageManager field in package.json
- Walks up directory tree to find lockfile
If no lockfile found:
→ Prompts you to choose a package manager
Configuration
# ~/.nirc — global config:
# Default package manager for new projects:
defaultAgent=pnpm
# Always use global agent for global installs:
globalAgent=npm
Real-world usage
# Clone any repo and just run:
git clone https://github.com/someone/project
cd project
ni # Installs with the right package manager
nr dev # Runs dev script with the right runner
nr build # Runs build
nr test # Runs tests
# No need to remember: is it npm/pnpm/yarn/bun?
corepack
corepack — Node.js built-in:
Enable corepack
# Corepack ships with Node.js but is disabled by default:
corepack enable
# Now pnpm and yarn are available as transparent proxies:
pnpm --version # → Downloads and runs the correct version
yarn --version # → Downloads and runs the correct version
Package manager field
// package.json — pin exact package manager version:
{
"name": "pkgpulse",
"packageManager": "pnpm@9.15.0"
}
# With corepack enabled:
pnpm install
# → Automatically uses pnpm@9.15.0
# → If wrong version installed, downloads the correct one
# If someone tries to use the wrong package manager:
npm install
# → Error: This project is configured to use pnpm
yarn install
# → Error: This project is configured to use pnpm
Version management
# Set package manager for project:
corepack use pnpm@9.15.0
# → Updates packageManager field in package.json
# Prepare (download for offline use):
corepack prepare pnpm@9.15.0 --activate
# Install specific version:
corepack install --global pnpm@9.15.0
CI/CD usage
# GitHub Actions:
name: Build
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: corepack enable
# Now pnpm/yarn uses the version from packageManager field
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm test
How it works
corepack acts as a transparent proxy:
1. You run: pnpm install
2. corepack intercepts the command
3. Reads packageManager from package.json: "pnpm@9.15.0"
4. Downloads pnpm@9.15.0 if not cached
5. Forwards the command to pnpm@9.15.0
Benefits:
✅ Everyone uses the exact same version
✅ No global install conflicts
✅ CI and local environments are identical
✅ Automatic version management
Supported package managers:
- pnpm
- yarn (v1, v2+, v4+)
- (npm is the default, not managed by corepack)
Feature Comparison
| Feature | nypm | ni | corepack |
|---|---|---|---|
| Purpose | Programmatic API | CLI runner | Version manager |
| Auto-detect PM | ✅ (lockfile) | ✅ (lockfile) | ✅ (packageManager field) |
| Install deps | ✅ | ✅ (ni) | N/A (proxies to PM) |
| Run scripts | ❌ | ✅ (nr) | N/A |
| Version pinning | ❌ | ❌ | ✅ |
| Enforce PM | ❌ | ❌ | ✅ |
| Interactive | ❌ | ✅ (script selection) | ❌ |
| Ships with Node | ❌ | ❌ | ✅ |
| npm support | ✅ | ✅ | ❌ (npm is default) |
| bun support | ✅ | ✅ | ❌ |
| Used by | Nuxt CLI, UnJS tools | Developers | CI/CD, teams |
| Weekly downloads | ~2M | ~500K | Ships with Node |
When to Use Each
Use nypm if:
- Building tools that need to install packages programmatically
- Need package manager detection in scripts/CLIs
- Building a framework CLI (like Nuxt, Astro)
- Want a universal API for npm/pnpm/yarn/bun
Use ni if:
- Want the fastest workflow for daily development
- Work across multiple projects with different package managers
- Want interactive script selection (
nr) - Tired of remembering
pnpm addvsyarn addvsnpm install
Use corepack if:
- Need to pin package manager versions for a team
- Want to enforce that everyone uses the same PM version
- Need consistent CI/CD environments
- Want zero-install package manager provisioning
How ni Detects Package Managers and Why Order Matters
The lockfile detection order that ni uses is not arbitrary — it reflects a prioritization decision that has practical consequences in polyglot monorepos and during migrations between package managers.
ni checks for lockfiles in this priority: bun.lockb first, then pnpm-lock.yaml, then yarn.lock, then package-lock.json. This means that if your repository has both a yarn.lock (left over from a previous package manager) and a pnpm-lock.yaml (the current one), ni will correctly use pnpm. The higher priority of Bun and pnpm over Yarn and npm reflects the assumption that if you've generated a newer-style lockfile, you've intentionally migrated.
The secondary detection mechanism — reading the packageManager field in package.json — runs after lockfile detection, but it can be configured as the primary signal by setting useLockFile=false in your ~/.nirc. This is useful in scenarios where the lockfile hasn't been committed yet but the packageManager field has been set, such as during initial project bootstrapping or in generated templates.
For projects where detection fails (no lockfile and no packageManager field), ni falls back to your defaultAgent configuration in ~/.nirc, defaulting to npm. Teams that work exclusively with pnpm should set defaultAgent=pnpm in their global config to avoid accidentally initializing new projects with npm. This global config is per-developer, not per-project, which means it cannot be enforced via the repository — corepack's packageManager field is the right mechanism for team-wide enforcement.
corepack's Role in CI/CD and Reproducible Builds
The value of corepack extends beyond developer convenience — it directly addresses one of the most common sources of CI/CD inconsistency: the package manager version mismatch.
Without corepack, your CI pipeline typically installs whatever version of pnpm or yarn is present in the actions/setup-node action or the base Docker image. These versions drift over time as the action is updated. A project that was originally built with pnpm 8.15 may silently use pnpm 9.x months later when the action updates, and lockfile format changes between major pnpm versions can cause --frozen-lockfile installs to fail or behave unexpectedly.
With corepack and a "packageManager": "pnpm@9.15.0" field in package.json, the CI pipeline always uses exactly pnpm 9.15.0 regardless of what the base image provides. corepack downloads and caches the specified version, and the cache can be warm on subsequent runs using actions/cache on ~/.node/corepack. This makes build reproducibility comparable to what you get from language runtimes that pin their own versions (.nvmrc, .node-version, .python-version).
One important practical consideration: corepack's transparent proxy means that running pnpm in a corepack-enabled environment actually runs corepack's shim, which then downloads the correct pnpm version if needed. This requires internet access on first use. In air-gapped CI environments, you need to pre-populate the corepack cache using corepack prepare pnpm@9.15.0 --activate in a network-accessible build step, or provide the corepack cache as an artifact. The corepack install --global pnpm@9.15.0 command is designed for this offline-preparation use case.
Combining All Three: A Complete Strategy for Monorepos
In practice, the three tools are complementary rather than competitive, and combining them addresses different layers of the package manager problem in a monorepo.
corepack handles version enforcement at the repository level. The "packageManager" field in the root package.json ensures every contributor — and every CI run — uses the same pnpm version. This should be the baseline for any serious monorepo.
ni handles daily developer ergonomics. Even with corepack enforcing pnpm, developers who work across multiple projects (some using npm, some pnpm, some bun) benefit from ni so they don't need to think about which command to run when switching context. nr dev, ni, and nun package work identically whether the project uses npm or pnpm.
nypm handles programmatic package management in scripts, code generators, and framework CLIs. When you write a scaffold tool that installs dependencies after generating a new project, using nypm instead of hard-coding pnpm add means your tool works correctly for users who chose a different package manager. The Nuxt module installer, create-t3-app, and similar tools benefit from this abstraction layer.
Migrating Between Package Managers with nypm and ni
When a team decides to switch package managers — from npm to pnpm, for example — ni and nypm smooth the transition in different ways. ni handles the daily workflow side: developers running ni and nr dev don't need to think about which package manager is active. As you add the pnpm-lock.yaml and remove package-lock.json, ni automatically detects the change and starts using pnpm commands. No developer needs to update their muscle memory or their shell aliases — the migration is transparent at the command level. nypm handles the programmatic side: any scripts in your repository that called npm install explicitly need updating, but scripts that used nypm's installDependencies() API continue working without modification because nypm detects the new lockfile and delegates to pnpm. The migration checklist is: update the packageManager field in package.json, run corepack use pnpm@<version> to set the version, delete the old lockfile, run pnpm install to generate the pnpm lockfile, and commit. CI pipelines using corepack automatically pick up the new package manager without any workflow file changes.
Security Considerations for Package Manager Version Pinning
Pinning package manager versions with corepack's packageManager field has a security dimension beyond reproducibility. Package manager vulnerabilities — though rare — have occurred, and teams running an unpinned package manager may inadvertently run a vulnerable version for weeks before noticing. With the packageManager field pinned and corepack enforcing it, security updates to pnpm or yarn require an explicit change to package.json, creating a visible audit trail and forcing teams to consciously accept the upgrade. The flip side is that teams must actively monitor package manager release notes and update the pinned version when security patches are released. A workflow that works well is to automate the packageManager field update using Dependabot or Renovate — both tools support corepack's packageManager field and can open pull requests when new versions of pnpm or yarn are released, giving teams automatic notification without requiring manual version monitoring.
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on nypm v0.3.x, @antfu/ni v0.21.x, and corepack (Node.js 22.x built-in).
Compare developer tooling and package management on PkgPulse →
See also: patch-package vs pnpm patch vs yarn patch and taze vs npm-check-updates vs npm-check, acorn vs @babel/parser vs espree.