Back to Mise

Shims

docs/dev-tools/shims.md

2026.9.216.6 KB
Original Source

Shims

There are several ways to load the mise context (dev tools, environment variables) into your shell:

This page explains the differences between these methods and how to use them. In particular, it will help you decide whether to use shims or mise activate in your shell.

Overview of the mise activation methods {#overview}

PATH activation {#path-activation}

mise's "PATH" activation method updates environment variables every time the prompt is displayed. In particular, it updates the PATH environment variable, which your shell uses to search for the programs it can run.

::: info For Bash, add eval "$(mise activate bash)" to ~/.bashrc. Run an echo ... >> ~/.bashrc setup command in your terminal only once; do not put that append command in the startup file itself. :::

For example, by default, your PATH variable might look like this:

sh
echo "$PATH"
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

With mise activate, mise automatically adds the required tools to PATH.

sh
PATH="$HOME/.local/share/mise/installs/python/3.14.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

In this example, the python bin directory was added at the beginning of PATH, making it available in the current shell session. When a fuzzy version like python = "3.14" or node = "26" is active, this path may use the requested-version symlink, such as ~/.local/share/mise/installs/python/3.14/bin, instead of the fully resolved patch version.

Use shims when a program needs a stable path to a tool, such as an IDE configured with a Python executable. For scripts, mise exec -- <command> loads both tools and environment variables explicitly.

Shims {#mise-activate-shims}

::: warning mise activate --shims does not support all the features of mise activate.

See shims vs path for more information. :::

When using shims, mise places small executables (shims) in a directory that is included in your PATH. You can think of shims as symlinks to the mise binary that intercept commands and load the appropriate context.

sh
ls -l ~/.local/share/mise/shims/node
# [...] ~/.local/share/mise/shims/node -> ~/.local/bin/mise

By default, the shim directory is located at ~/.local/share/mise/shims (on Windows: %LOCALAPPDATA%\mise\shims). When you install a tool (for example, node), mise adds an entry to the shims directory for every binary the tool provides (for example, ~/.local/share/mise/shims/node).

sh
mise use node@24 npm:prettier@3

~/.local/share/mise/shims/node --version
~/.local/share/mise/shims/prettier --version

These commands use the versions selected for the current directory. The paths above assume the default Unix data directory. To find shims by command name, add their directory to the existing PATH:

sh
export PATH="$HOME/.local/share/mise/shims:$PATH"

Child processes inherit this PATH. Independently launched applications, CI jobs, and other shells need their own environment setup; editing one shell's profile does not configure every process on the machine.

Lazy tools

Set lazy = true on a tool when it should be installed the first time one of its commands is invoked instead of by a bare mise install:

toml
[tools]
node = { version = "24", lazy = true }

For registry shorthands, mise creates bootstrap shims from the registry's bins metadata. Explicit backends and tools that are not in the registry must declare their command names with lazy_bins:

toml
[tools]
"github:example/acme" = { version = "1.2.3", lazy = true, lazy_bins = ["acme", "acmectl"] }

Run mise reshim after editing a lazy declaration directly. Commands such as mise use that update tool configuration rebuild the shim farm automatically. Invoking a lazy shim installs only its configured provider and then executes it. This is independent of not_found_auto_install; an explicit project tool selection is never bypassed by a lower-precedence lazy declaration.

A bare mise install skips missing lazy tools. Pass --include-lazy to install all configured tools, including lazy declarations, or name one explicitly, such as mise install node, to install only that lazy tool immediately. Once installed, normal mise activate places the real tool path ahead of the shim farms, so later calls have no shim dispatch overhead. mise activate --shims remains project-aware and dispatches every call through mise by design.

Tasks and mise x work the same way. mise run does not preinstall lazy tools. Instead, whenever the toolset has a lazy declaration, the environment mise builds for a task (and for mise x and mise env) places the shim farms behind the tool paths if they are not already on PATH, and any missing bootstrap shims are created first. The task installs the tool the first time it runs one of its commands. mise x -- <command> installs the provider of a lazy command directly.

::: tip mise activate --shims is a shorthand for adding the shims directory to PATH. :::

How to add mise shims to PATH

Use mise activate --shims when mise itself is already on PATH. Add the following lines to the indicated files, preserving existing setup. Bash and Zsh profiles run for login shells; they are not startup files for arbitrary non-interactive scripts.

::: code-group

sh
# Use ~/.profile instead if that is your existing login startup file.
eval "$(mise activate bash --shims)"
sh
eval "$(mise activate bash)"
sh
eval "$(mise activate zsh --shims)"
sh
eval "$(mise activate zsh)"
fish
if status is-interactive
    mise activate fish | source
else
    mise activate fish --shims | source
end

:::

Bash login shells read the first available file among ~/.bash_profile, ~/.bash_login, and ~/.profile. They read ~/.bashrc only if the profile sources it. Check your existing startup files before adding a new profile that would hide the old one. Zsh reads ~/.zprofile for login shells and ~/.zshrc for interactive shells.

For a script or CI command, prefer mise exec -- <command>. A script launched from an already configured shell inherits its PATH, but a scheduler or IDE may not inherit that shell's environment. See IDE integration and Windows setup for those environments.

::: info It's fine to call mise activate --shims in your shell profile file and then later call mise activate in an interactive session. PATH activation keeps the user and existing system shim farms behind real tool paths when the effective toolset contains a lazy declaration or not_found_auto_install is enabled. Without either, full activation removes the shim farms as before. This makes lazy bootstrap commands available without adding dispatch overhead after installation. not_found_auto_install still controls general missing-tool installation, but does not disable an explicit lazy = true declaration.

:::

To explicitly keep tool shims out of full shell activation, including when auto-install or lazy tools are enabled, run mise settings set activate_shims false and restart your shell. See activate_shims for the tradeoffs. Shims serve several purposes: installing missing configured versions, bootstrapping lazy tools, and dispatching configured command wrappers. Wrappers such as cargo through mr-boxington use their own command-wrappers/bin directory, which remains active with this setting disabled. Explicit mise activate --shims also continues to work.

::: info When a shim cannot resolve a mise-managed tool (for example, a version pinned in mise.toml that hasn't been installed and not_found_auto_install is disabled), it falls back to the first same-named executable found elsewhere on PATH rather than erroring. This is convenient for tools you also want available outside of mise, but for a tool the OS also ships (python3 on Debian/Ubuntu, for example) it means the shim can silently run a completely different, unrelated binary instead of failing loudly.

Set not_found_system_fallback to false, alongside not_found_auto_install = false, if you'd rather an unresolvable shim fail outright. :::

  • You can also decide to use only shims if you prefer, though this comes with some limitations.
  • An alternative to mise activate --shims is to use export PATH="$HOME/.local/share/mise/shims:$PATH". This can be helpful if mise is not yet available at that point.

mise reshim

To force mise to update the contents of the shims directory, run mise reshim.

Use mise reshim --system for the system shim farm. If shims_dir and system_shims_dir resolve to the same physical path, either command reconciles one combined farm containing both scopes.

mise rebuilds shims when it installs, updates, or removes a tool. If another package manager adds executables inside an existing installation, run mise reshim. The Node.js core plugin can do this after npm install -g through its node.npm_shim wrapper; this is not a general hook for every package manager.

mise reshim only creates and removes shims. Some users treat it as a "fix it" button, but it is only necessary when ~/.local/share/mise/shims doesn't contain something it should.

For mise reshim, the configured shim directory may be a shared executable directory such as ~/.local/bin or /usr/local/bin: reshim only replaces or removes entries it recognizes as mise shims, and leaves a same-named unmanaged file in place. Other mise features still identify shim directories as whole PATH entries, however, so a shared directory is not yet supported with mise activate, hook-env, or internal dependency lookups. Use a dedicated shims_dir if you use those features.

Command wrappers

Use [wrappers] when a command should always pass through another program while keeping its ordinary name. For example, this routes every cargo invocation through Mr Boxington:

toml
[tools]
mr-boxington = "1.4.1"

[wrappers.cargo]
command = "mbx"
env = { MBX_CARGO_SHIM_MODE = "1" }

Run mise reshim after adding or removing a wrapper. The wrapper is available with both mise activate and mise activate --shims, and takes precedence over an executable with the same name. When it delegates, mise removes its dispatch directories from PATH, so mbx resolves Cargo from mise-managed Rust when configured and otherwise falls through to rustup or the system installation.

A short form is available when no arguments or environment variables are needed:

toml
[wrappers]
terraform = "tofu"

The detailed form can insert arguments before those supplied by the user:

toml
[wrappers.python]
command = "uv"
args = ["run", "python"]

Shims vs PATH {#shims-vs-path}

The following features are affected when shims are used instead of PATH activation:

  • Env vars defined in mise are only available to mise tools
  • Most hooks won't trigger
  • The Unix which command points to the shim, obscuring the real executable

In general, PATH activation (mise activate) is recommended over shims for interactive situations.

With activate, every time the prompt is displayed, mise determines what PATH and other env vars should be and exports them. This is why it doesn't work well for non-interactive situations like scripts: the prompt is never displayed, so you have to call mise hook-env manually to get mise to update the env vars (though there are exceptions; see hook on cd).

Env vars and shims

A downside of shims is that environment variables are only loaded when a shim is called. This means that if you set an environment variable in mise.toml, it is only applied when a shim is called.

The following example only works under mise activate:

sh
$ mise set NODE_ENV=production
$ echo $NODE_ENV
production

But this works with either:

sh
$ mise set NODE_ENV=production
$ node -p process.env.NODE_ENV
production

You can also use mise x|exec and mise r|run to load the environment even if you don't need any mise tools:

sh
$ mise set NODE_ENV=production
$ mise x -- bash -c "echo \$NODE_ENV"
production
$ mise r some_task_that_uses_NODE_ENV
production

::: tip In general, tasks are a good way to ensure that the mise environment is always loaded. :::

Hooks and shims

The hooks cd, enter, and leave only trigger with mise activate. The separate watch_files configuration also requires mise activate. However, preinstall and postinstall still work with shims because they don't require shell integration.

which

Many users find which valuable. Shims effectively "break" which, causing it to show the location of the shim. A workaround is mise which, which shows the actual location. Some users prefer the "cleanliness" of running which node and getting back a real path with a version number in it, e.g.:

sh
$ which node
~/.local/share/mise/installs/node/24/bin/node

Performance

PATH activation does its work at prompts and supported directory-change hooks. Shims resolve the environment when a command is invoked. Which costs less depends on how you run commands.

For example, a script that repeatedly calls a shim resolves the environment on each call:

bash
for i in {1..500}; do
    node script.js
done

Run the enclosing script with mise exec -- bash benchmark.sh to prepare the environment once. Child processes then inherit the real tool directories ahead of the shim directory. Similarly, a process launched by a shim passes the resolved environment to its children.

See slow shell prompts to diagnose activation overhead. Hook behavior and parent-shell environment updates also differ, so choose an activation method based on those requirements as well as performance.

Neither shims nor PATH {#neither-shims-nor-path}

mise exec, mise run, and mise en load tools and environment variables explicitly:

sh
mise exec -- node --version
mise run build

The second command requires a task named build. This approach works for CI, scripts, and projects where you do not want to change shell startup files. It requires mise on PATH, but no shell activation or shim directory.

Hook on cd {#hook-on-cd}

For some shells (bash, zsh, fish, xonsh), mise hooks into the cd command, while in others, it only runs when the prompt is displayed. This relies on chpwd in zsh, a chpwd emulation (wrapping cd/pushd/popd) plus PROMPT_COMMAND in bash, fish_prompt in fish, and on_chdir in xonsh.

Directory-change hooks let those shells apply the new project environment before the next command, even if no prompt has appeared yet.

::: details Running several commands in a single line

If you run a set of commands in a single line like the following:

sh
cd ~
cd ~/src/proj1 && node -v && cd ~/src/proj2 && node -v

With mise activate in a shell without a cd hook, this uses the tools from ~, not from ~/src/proj1 or ~/src/proj2, even after the directory changes.

This is because in these shells mise runs just before your prompt is displayed, whereas in others it hooks into cd. Shims will always work with the inline example above.

:::

Using mise in rc files

rc files like .zshrc are unusual: they are scripts, but they run only for interactive sessions. If you need to access tools provided by mise inside an rc file, you have two options:

::: code-group

sh
eval "$(mise activate zsh)"
eval "$(mise hook-env -s zsh)"
node some_script.js
sh
eval "$(mise activate zsh --shims)" # should be first
eval "$(mise activate zsh)"
node some_script.js

:::