v2026.4.27

jdx/misev2026.4.27Apr 29, 2026by mise-en-dev

AI Summary

Adds control over npm package manager arguments, improves `mise watch` to follow task dependencies, and fixes an infinite shim recursion bug on macOS.

Key Highlights

  • New `npm_args`, `pnpm_args`, and `bun_args` tool options for configuring package manager flags.
  • `mise watch` now follows the task graph and watches sources of dependencies.
  • Fixed infinite shim recursion on macOS caused by case-insensitive path comparisons.

New Features

  • NPM args control
  • Dependency-aware watch mode
  • macOS shim recursion fix

Full Release Notes

A focused release: more control over how npm-backed tools get installed, smarter `mise watch` that follows task dependencies, and a fix for a nasty macOS shim recursion that could lock up a shell during `mise up --bump`.

## Added

- **(backend)** New `npm_args`, `pnpm_args`, `bun_args`, and `aube_args` tool options on the npm backend ([#9109](https://github.com/jdx/mise/pull/9109)) by @risu729. Each one is forwarded to the matching package manager when it's the active `settings.npm.package_manager`, mirroring the `pipx` backend's style. The args are also recorded in the lockfile and at install time:

  ```toml
  [tools]
  "npm:npm" = { version = "latest", aube_args = "--reporter append-only" }
  "npm:tiny" = { version = "latest", pnpm_args = "--loglevel=warn" }
  ```

- **(env)** External vfox environment plugins now get `ctx.config_root` in their `MiseEnv` / `MisePath` hooks ([#9465](https://github.com/jdx/mise/pull/9465)) by @hisaac. This matches what built-in directives like `_.file` already see, so plugins (e.g. [`mise-xcode`](https://github.com/hisaac/mise-xcode)) can resolve user-supplied relative paths against the project root regardless of the shell's cwd. `watch_files` returned from a plugin are now also absolutized against `config_root` instead of `current_dir()`.

- **(task)** `mise watch` now follows the task graph and watches the sources of each chosen task's dependencies as well as its own ([#9437](https://github.com/jdx/mise/pull/9437)) by @43081j. Pass `--skip-deps` (or set `skip_deps`) to restore the previous "task sources only" behavior. Explicit `--glob` overrides still win.

- **(release)** `scripts/gen-aqua-changelog.sh` now diffs the previous tag's `registry.yaml` against the current one and emits `New Packages` / `Updated Packages` sections in the release PR, instead of dumping the aqua-registry release tags rolled into the release ([#9471](https://github.com/jdx/mise/pull/9471)) by @jdx. This restores the pre-[#9043](https://github.com/jdx/mise/pull/9043) behavior for the merged-registry world.

## Fixed

- **(backend)** When `_list_remote_versions` returned an empty list (invalid module path, throttling, etc.) the empty result was cached as if it were authoritative, poisoning both the on-disk cache file and the in-memory `OnceCell` for up to an hour ([#9444](https://github.com/jdx/mise/pull/9444)) by @c22. The cache is now cleared in both places when the list comes back empty, so the next call re-fetches.

- **(shims)** Fixed an infinite shim recursion on macOS reported in [#9462](https://github.com/jdx/mise/discussions/9462) where `mise up --bump` against npm packages would loop `mise -> npm shim -> mise -> npm shim -> ...` and sometimes crash the session ([#9468](https://github.com/jdx/mise/pull/9468)) by @jdx. The trigger was a case-mismatched `$HOME` in `PATH` (`/Users/Olfway/...` vs. `/Users/olfway/...`) — the shims-stripping in `Backend::dependency_env` compared byte-equal, so on case-insensitive APFS/HFS+ volumes it was a no-op and `npm` re-resolved to the mise shim. A new `file::paths_eq` does case-insensitive compares on macOS/Windows and byte-equal on Linux, and is now used everywhere mise asks "is this PATH entry the shims directory?" — including `path_env_without_shims`, `which_no_shims`, `PathEnv` partitioning, `cli::exec` program resolution, and the doctor's `shims_on_path` check (which had been silently reporting `no` for affected users).

- **(task)** Under `deny_env = true` on Linux, every env var was being stripped from the child process — including the `PATH` / `HOME` / `USER` / `SHELL` / `TERM` / `LANG` that `filter_env` and the docs say should pass through ([#9467](https://github.com/jdx/mise/pull/9467)) by @jdx, fixing [#9466](https://github.com/jdx/mise/discussions/9466). `apply_sandbox()` was calling `Command::env_clear()` *after* the task executor populated explicit envs via `.envs(filtered_env)`, wiping both. The Linux branch now snapshots the explicit envs before clearing and re-applies them; macOS already did this. A new `path_test` task in `e2e/sandbox/test_sandbox_task` guards against regressions.

## New Contributors

* @43081j made their first contribution in [#9437](https://github.com/jdx/mise/pull/9437)
* @hisaac made their first contribution in [#9465](https://github.com/jdx/mise/pull/9465)

**Full Changelog**: https://github.com/jdx/mise/compare/v2026.4.26...v2026.4.27