#367·ni

refactor(config): `getConfig` repeats package manager detection the runner already does

Author: unrevised6419Created Sep 16, 2026Updated Sep 16, 2026

Description

getConfig runs package manager detection and overwrites defaultAgent with the result:

https://github.com/antfu-collective/ni/blob/2d8c88e9f0d302da75eda36e1a158141714a351e/src/config.ts#L121-L123

typescript
const agent = await detect({ programmatic: true })
if (agent)
  config.defaultAgent = agent

This dates back to #50, when getConfig was the only place that read the packageManager field. Detection has since moved to detect() in the runner, but this copy stayed behind.

Why it is redundant

getDefaultAgent is only called after the runner's own detect() has already come back empty:

typescript
(await detect({ ...options, cwd })) || (await getDefaultAgent(options.programmatic))

A second detection in the same directory finds nothing either, so in the CLI this branch never changes the result. It matches the documented behaviour of defaultAgent ("fallback when no lock found"), but the fallback is already implemented by the runner.

What it costs

  • An extra detection pass: every command that falls back to defaultAgent checks lock files and package.json a second time.
  • A misleading API: getConfig().defaultAgent and getDefaultAgent() return the detected agent rather than the configured fallback.
  • A source of bugs: the call has no cwd, so with -C <dir> it detects against process.cwd() instead of the target directory. In test/programmatic, the packager/unknown and lockfile/unknown snapshots record this repository's own pnpm for fixtures that are meant to be undetectable. #363 passes cwd through, which fixes the leak but keeps the redundant call.

Proposal

Remove the detection from getConfig, so defaultAgent is only what .nirc or NI_DEFAULT_AGENT say. The CLI behaves the same. Code that calls getConfig().defaultAgent or getDefaultAgent() directly would get the configured value instead of the detected one, so this might warrant a note in the release.

Happy to open a PR once #363 lands, since it touches the same function.