Fast package manager routing for npm, yarn, pnpm, bun, and deno.
hni is inspired by Antfu's ni, but packaged as a single multicall binary with extra shell setup for a node shim.
hni is still beta software and may have bugs.
One install gives you:
hnini,nr,nlx,nu,nun,nci,na,np,nsnodeshim viahni init <shell>(shell plugin only)
npm install -g @happytoolin/hni
hni --versionThis installs hni and the ni-family aliases (ni, nr, nlx, nu, nun, nci, na, np, ns) onto your global npm bin path.
The node shim is only enabled through hni init <shell>.
Under the hood, npm resolves a platform-specific optional dependency package that contains the native hni binary.
brew tap happytoolin/happytap
brew install hni
hni --versionTODO: https://happytoolin.com/hni/install.sh is not live yet. Use the raw GitHub script for now:
curl -fsSL https://raw.githubusercontent.com/happytoolin/hni/main/install.sh | bashOptional environment variables:
HNI_VERSION- install a specific version, for examplev0.0.2HNI_INSTALL_DIR- install somewhere other than~/.local/binHNI_NODE=off- disable thenodeshim for the current environment
TODO: https://happytoolin.com/hni/install.ps1 is not live yet. Use the raw GitHub script for now:
irm https://raw.githubusercontent.com/happytoolin/hni/main/install.ps1 | iexOptional parameters:
-Version latest-InstallDir "$env:LOCALAPPDATA\hni\bin"
Install hni:
deno install -gA -n hni jsr:@happytoolin/hni/hni
hni --versionInstall alias commands (example):
deno install -gA -n ni jsr:@happytoolin/hni/ni
deno install -gA -n nr jsr:@happytoolin/hni/nrInstall dependencies or add new ones.
ni
ni vite
ni -D vitest
ni -g eslint
ni --frozen
ni --frozen-if-present
ni --interactiveRun package scripts.
nr
nr dev
nr build
nr test -- --watch
nr --if-present lint
nr --repeat-lastExecute binaries without adding them permanently to your project.
nlx vitest
nlx eslint .
nlx create-vite@latestUpgrade dependencies.
nu
nu react react-dom
nu --interactiveRemove dependencies.
nun lodash
nun react react-dom
nun --multi-select
nun -g typescriptRun a clean install. If a lockfile exists, hni uses the package-manager-specific frozen install command.
nciPrint or forward directly to the detected package manager.
na --version
na config get registryRun shell commands in parallel or sequentially.
np "pnpm dev" "pnpm test"
ns "pnpm lint" "pnpm test"hni can also act as a package-manager-aware node shim.
Enable it by adding hni init <shell> to your shell config first.
node install vite
node run dev
node exec vitest
node ci
node p "echo one" "echo two"Regular Node.js usage still passes through:
node script.js
node -v
node -- --trace-warningshni help ni
hni completion zsh
hni init bash
hni doctorIf you want node-shim behavior, add the init line at the end of your shell config file, after anything that manages Node or rewrites PATH, such as nvm, mise, asdf, fnm, or volta.
Do not append the hni directory to the end of PATH. Put the init line at the end of the shell config file and let it prepend the correct path for you.
Add to ~/.zshrc:
eval "$(hni init zsh)"Add to ~/.bashrc:
eval "$(hni init bash)"Add to ~/.config/fish/config.fish:
hni init fish | sourceAdd to $PROFILE:
Invoke-Expression (& hni init powershell)Generate a stable init file, then source it from the end of ~/.config/nushell/config.nu:
hni init nushell | save --force ~/.config/nushell/hni.nu
source ~/.config/nushell/hni.nuThese work across hni and the multicall aliases:
? --dry-run --print-command
--explain
-C <dir>
-v --version
-h --helpUse -- to forward flags to the underlying package manager or script:
hni ni -- --help
nr test -- --watchConfig file:
~/.hnirc
Supported keys:
defaultAgent=prompt
globalAgent=npm
runAgent=node
useSfw=falseEnvironment overrides:
HNI_CONFIG_FILEHNI_DEFAULT_AGENTHNI_GLOBAL_AGENTHNI_USE_SFWHNI_AUTO_INSTALL
hni detects the package manager from:
packageManagerinpackage.json- lockfiles such as
pnpm-lock.yaml,yarn.lock,package-lock.json,bun.lockb, ordeno.lock - config defaults if detection is unavailable
Then it maps the command family to the right native command:
ni-> install or addnr-> run or tasknlx->npx/pnpm dlx/yarn dlx/bun xnu-> update / upgradenci-> frozen install when lockfiles exist
PowerShell ships with a built-in ni alias for New-Item.
If that conflicts with hni, remove or override it in your profile before loading hni:
Remove-Item Alias:ni -ErrorAction SilentlyContinue
Invoke-Expression (& hni init powershell)ni vite --debug-resolved
nr dev --explain
hni doctorThe active benchmark suite lives in benchmark/.
If you use just, the common local commands are wrapped in justfile:
just build-release
just test
just test-native
just ci
just benchRun all benchmark tracks with:
./benchmark/run.sh
just benchRun individual tracks with:
just bench-compare
just bench-native
just bench-runtime
just bench-direct
./benchmark/run.sh --track=compare
./benchmark/run.sh --track=native
./benchmark/run.sh --track=runtime
./benchmark/run.sh --track=directGenerate flamegraphs with:
./benchmark/profile.shTracked benchmark docs:
- current snapshot:
benchmark/LATEST.md - lightweight history:
benchmark/HISTORY.md - native compatibility:
docs/native-compat.md
Same-repo pull requests also run the benchmark workflow on GitHub Actions and update a sticky PR comment with the latest summary.
The current tracked snapshot is from March 22, 2026 and was generated with 50 warmups and 500 measured runs per case.
If you only want the headline, it is this: hni --native is meaningfully faster than normal package-manager usage on npm, pnpm, and yarn, still ahead on bun, and much less dramatic on deno.
The clearest story is the direct track, which compares what people normally type against hni --native. In the current snapshot, hni averages 4.90x faster overall there.
A few representative wins:
| Case | Direct | hni native |
Relative |
|---|---|---|---|
task noop (npm) |
207.83 ms | 36.63 ms | 5.67x |
task noop (pnpm) |
418.28 ms | 32.26 ms | 12.97x |
task noop (yarn) |
279.87 ms | 32.17 ms | 8.70x |
exec hello --flag (npm) |
249.48 ms | 10.25 ms | 24.35x |
exec hello --flag (pnpm) |
311.16 ms | 7.30 ms | 42.65x |
exec hello --flag (yarn) |
104.92 ms | 7.48 ms | 14.02x |
exec hello --flag (bun) |
15.07 ms | 7.46 ms | 2.02x |
The internal native track tells the same story from another angle: native mode averages 4.38x faster than delegated mode inside hni. The local-bin case is especially strong in the current snapshot: nlx hello --flag (npm local bin) lands at 7.75 ms natively versus 334.43 ms in delegated mode.
The Antfu comparison is smaller and more lightweight, but still points the same way. On that track, hni averages 1.66x faster overall, with ni --version coming in at 129.05 ms for hni versus 334.60 ms for Antfu's ni.
The main caveat is still Deno. In the direct task-style cases, hni is basically at parity there rather than dramatically ahead. In the separate runtime-style comparison, hni beats bun on both cases, while deno still wins the hooked-task case.
All timing uses hyperfine against the release binary. The suite looks at four angles:
direct: normal package-manager usage versushni --nativenative: delegated mode versus native mode insidehnicompare: a small CLI-focused comparison against Antfu'sniruntime: a side-by-side look athni, bun, and deno on a couple of task-style cases
The repo keeps the curated snapshot files rather than every intermediate result. For the full current matrix, use benchmark/LATEST.md.
