antfu-collective/ni: one CLI that picks the right package manager for you
đź’ˇ Use the right package manager
At a glance
- What is it?
- ni is a small set of binaries that read the lockfile and packageManager field in your project and translate one command into npm, yarn, pnpm, bun, deno, nub or aube syntax. It is for people who move between repositories all day and keep typing the wrong install command.
- Who is it for?
- Adopt ni if your work spans several repositories with different lockfiles and you want one command vocabulary for install, run, upgrade and uninstall. Skip it if you only ever touch one package manager, or if you need deno to support production-only installs, since the README marks that combination as not supported.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 14 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem ni solves: one command vocabulary across seven package managers
Every package manager has its own verb for the same operation. Adding a dev dependency is `npm i -D`, `yarn add -D`, `pnpm add -D` and `bun add -d`; the README lists the exact mappings for each. Running a script is `npm run`, `yarn run`, `pnpm run`, `bun run` and `deno task`. Installing from a lockfile without touching it is `npm ci`, `yarn install --frozen-lockfile`, `pnpm install --frozen-lockfile` or `bun install --frozen-lockfile`. None of that is hard to learn. The cost is in the switching: you clone a repo, type the command you used in the last project, and get an error or, worse, a second lockfile.
ni targets that moment. It is a global CLI whose commands are short two-letter verbs: `ni` to install, `nr` to run, `nlx` to download and execute, `nup` to upgrade, `nun` to uninstall, `nci` for a clean install, `nd` to dedupe, and `na` to print the detected agent. The audience is anyone who works across repositories maintained by different teams, plus contributors who open pull requests in projects they do not own and do not want to guess the tooling. The README's own framing is blunt about the failure it prevents: running `npm i` in a yarn project.
How detection works and what each binary does
The package depends on `package-manager-detector`, and that dependency is the core of the mechanism. When you run a command, ni inspects the current working directory to determine which agent the project uses, then builds the equivalent native command and executes it through `tinyexec`. The README shows the resulting command for each agent as a comment under every example, which makes the translation table readable without reading the source.
The eight binaries are declared separately in `package.json` under `bin`: `ni`, `nci`, `nr`, `nup`, `nd`, `nlx`, `na` and `nun`. Each maps to its own file under `bin/`, and the package also exposes matching subpath exports such as `./nr` and `./nci` from `dist/`. That means the tools are usable as a CLI and importable as modules, though the README documents only the CLI surface.
Two details show how far the abstraction goes. `ni -g eslint` installs globally and the README notes it uses the default agent regardless of the current working directory, because a global install has no project to detect. And `nlx --local vitest` prefers an already-installed local binary over downloading one, which the README says is equivalent to `pnpm exec` or `yarn exec`; the flag matters mainly for pnpm, yarn and nub, since npm, bun and aube already resolve local binaries first. Interactive paths exist too: bare `nr` opens a script picker, `nr -p` picks a package and then a script, and bare `nun` multi-selects dependencies to remove. The interactive selection is backed by `fzf` and an inlined `@posva/prompts`, both visible in the dependency list.
Installing ni and running your first command
The README gives a single global install line. The package requires Node 20.19.0 or newer according to the `engines` field in `package.json`, so check that first.
npm i -g @antfu/niAfter that, the fastest way to see what ni would do is to run it in a project you already have. In a directory with a `pnpm-lock.yaml`, `ni` resolves to `pnpm install`; in one with `yarn.lock`, it resolves to `yarn install`. The README does not document a dry-run flag, so the observable behaviour is the install itself. To confirm which agent was detected without installing anything, use the alias command:
naThe README describes `na` as the agent alias, and the example output is the bare agent name, for example `npm`. Adding a dependency is the same shape across managers:
ni viteIn an npm project that becomes `npm i vite`; in a pnpm project, `pnpm add vite`; in a bun project, `bun add vite`. Dev dependencies take the flag you already know:
ni @types/node -DThe README lists the per-agent result, including `bun add -d @types/node` with a lowercase `d`, which ni handles for you. For CI-style installs that must not modify the lockfile, `nci` maps to `npm ci`, `yarn install --frozen-lockfile`, `pnpm install --frozen-lockfile`, `bun install --frozen-lockfile` or `deno cache --reload`. Running a script works the same way, and arguments pass through:
nr dev --port=3000That becomes `npm run dev -- --port=3000` under npm and `pnpm run dev --port=3000` under pnpm. The README also documents `nr -` to rerun the last command, which is useful when you are iterating on the same script.
Catalog mode: the feature most likely to surprise you
Since v29.0.0, ni changes behaviour inside workspaces that use catalogs. If a pnpm workspace declares catalogs in `pnpm-workspace.yaml`, a Yarn Berry workspace declares them in `.yarnrc.yml`, or a Bun workspace declares them in `package.json`, ni enters catalog mode automatically. Instead of writing a pinned version into `package.json`, it writes a `catalog:` reference and updates the workspace catalog.
The README walks through the pnpm case. Given a `prod` catalog containing `react: ^18.3.0`, running `ni react` detects the entry, writes `"react": "catalog:prod"` into `package.json` and runs `pnpm install`. For a package not in any catalog, ni prompts you to choose a catalog or skip, fetches the latest version, updates `pnpm-workspace.yaml` and then writes the reference. Bun workspaces store catalogs in the root `package.json`, either nested under `workspaces` or at the top level, and the flow is otherwise the same.
Two configuration keys control this. Setting `catalog=existing` in `~/.nirc` or the `NI_CATALOG=existing` environment variable restricts ni to reusing catalog entries that already exist; packages outside every catalog are installed with a pinned version and catalogs are left untouched. Setting `catalog=false` disables catalog mode entirely. There is also a workspace flag: `ni react -w` writes the catalog reference to the workspace root `package.json`.
The surprising part is the default. In a catalog workspace, the first `ni <package>` for something not yet catalogued will offer to modify your workspace configuration. That is a deliberate design choice, and it is consistent with how catalogs are meant to be used, but it means a command that looks like a plain install can produce a diff in `pnpm-workspace.yaml`. Teams that want installs to stay local should set `catalog=existing` before rolling ni out. The README also notes that when only a default catalog exists, new packages are added without prompting, and that when only named catalogs exist, the default catalog is never offered.
Where ni stops being the right tool
The abstraction is a translation layer, so it can only be as complete as the mapping table. The README itself marks one gap: `ni -P` for production-only installs is not supported under deno. That is a real limitation, not a documentation oversight, and it shows the pattern to expect. When an agent has no equivalent operation, ni cannot invent one.
Interactive upgrade is another uneven case. `nup -i` is explicitly not available for npm, while yarn, pnpm, bun, deno, nub and aube all have an interactive form. If your workflow depends on interactive upgrades and you are on npm, ni will not give you one.
There is also a category of work where ni adds a layer without removing any. If you maintain a single repository on a single package manager, the translation table is dead code for you; learning `pnpm add -D` once costs less than adding a global tool and remembering its flag conventions, which differ from the underlying managers in places. The `-D` versus `-d` difference between npm and bun is handled for you, but you still have to know that `ni -P` and `ni --frozen` exist and what they map to. Finally, ni decides based on the project directory. Running it somewhere without a lockfile or a `packageManager` field puts you at the mercy of detection, and the README does not document a fallback order for that case.
How ni compares with Corepack and plain package-manager scripts
Corepack, which ships with Node, solves an adjacent problem: it makes sure the package manager version declared in `packageManager` is the one that runs. Its scope is version pinning and shimming, not command translation. If your repository already declares `"packageManager": "[email protected]"`, Corepack will run pnpm for you, but you still type `pnpm add -D`. ni does the opposite: it assumes you have the managers available and rewrites the verb. The two are complementary, and the related searches around Corepack disabling and versions suggest people run into its constraints separately.
A second alternative is a thin set of npm scripts or a Makefile that wraps whichever manager the project uses. That keeps the mapping inside the repository, which is good for contributors and bad for you when you move to the next repository, because every project reinvents it. ni's advantage is that the mapping lives in one global install and covers eight agents, including deno and the newer nub and aube entries the README lists.
A third comparison is `npx` itself. `nlx` maps to `npx`, `yarn dlx`, `pnpm dlx`, `bunx`, `deno run npm:vitest`, `nubx` and `aube dlx` depending on the project. If you only ever use npx, the value is small. If you regularly hit a pnpm repository where `npx` behaves differently from `pnpm dlx`, the single `nlx` verb removes a decision.
Maintenance, licence and upgrade cost
The repository is not archived, and the last push was on 2026-09-16, the same day as the v30.6.0 release. The two prior releases, v30.5.0 and v30.4.0, are dated 2026-08-06. That is a steady release cadence over the visible window, and the version number is high enough that the project has been through many breaking-change cycles.
The licence is MIT, declared in `package.json` and present as a `LICENSE` file at the repository root. MIT is permissive: it allows use, modification and redistribution with the copyright notice and permission notice retained, and it provides the software without warranty. That is the extent of what the repository states; questions about your organisation's policy on bundled dependencies are for your own review.
The upgrade cost is worth naming. ni is a global tool, so its version is not pinned by the projects you work in. A new major can change detection behaviour or catalog handling, and you will feel it in every repository at once. The runtime dependency list is short (`fzf`, `package-manager-detector`, `tinyexec`, `tinyglobby`) with several packages inlined at build time, which limits how much supply-chain surface a global install pulls in, but it does not pin anything for you. The `engines` field requires Node 20.19.0 or newer, so an upgrade of ni can force a Node upgrade on older machines. Pinning the global install to a specific version is possible with your global manager of choice, though the README does not document a recommended approach.
Editorial conclusion
Adopt ni if your work spans several repositories with different lockfiles and you want one command vocabulary for install, run, upgrade and uninstall. Skip it if you only ever touch one package manager, or if you need deno to support production-only installs, since the README marks that combination as not supported. Before rolling it out, check the Node version requirement of 20.19.0 or newer and confirm how your team wants catalog mode configured, because the default behaviour writes catalog references into package.json in pnpm, Yarn Berry and Bun workspaces.
Frequently asked questions
How do I install @antfu/ni?
The README gives one command: `npm i -g @antfu/ni`. The package requires Node 20.19.0 or newer according to its engines field.
How does ni know which package manager to use?
It detects the agent from the project directory using the package-manager-detector dependency, then translates your command into that agent's native syntax. The README shows the resolved command under each example, such as `pnpm add vite` for `ni vite` in a pnpm project.
What is catalog mode in ni?
Since v29.0.0, ni enters catalog mode automatically in pnpm, Yarn Berry and Bun workspaces that configure catalogs. It writes `catalog:` references into package.json and updates the workspace catalog instead of pinning a version. You can set `catalog=existing` or `catalog=false` in `~/.nirc`, or use the `NI_CATALOG` environment variable, to change this.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/antfu-collective-ni)