What Is mise's Approach to Shims? Dynamic Tool Resolution Explained

mise uses lightweight executable shims that intercept command calls and forward them to the core mise binary, enabling on-demand version resolution without modifying shell environment variables.

mise is a polyglot tool version manager (formerly rtx) that handles runtime installations for Node.js, Python, Ruby, and hundreds of other tools. Unlike traditional version managers that manipulate PATH entries directly, mise's approach to shims relies on proxy executables that delegate to the core binary for runtime tool selection. This architecture allows tools to resolve to the correct version dynamically based on your current directory's .mise.toml configuration.

How mise Shims Work Under the Hood

When you invoke a command like node, mise's shim system acts as a transparent intermediary. The shim itself is a minimal executable stored in a dedicated directory that passes all arguments to mise exec, which then determines the correct binary to run based on your active toolset.

Shim Generation and Storage

Shims are created automatically during tool installation. When you run mise install node, the core logic in [src/shims.rs](https://github.com/jdx/mise/blob/main/src/shims.rs) discovers every executable provided by the tool (e.g., node, npm, npx) and generates corresponding shim files. These files live in ~/.local/share/mise/shims on Unix systems or %LOCALAPPDATA%\mise\shims on Windows.

The [src/config/mod.rs](https://github.com/jdx/mise/blob/main/src/config/mod.rs) file contains the rebuild_shims_and_runtime_symlinks function, which triggers shim recreation whenever tools are added, removed, or upgraded. This ensures the shim directory stays synchronized with your installed toolset.

PATH Integration and Dynamic Resolution

To use shims, you add the shims directory to your PATH, typically via mise activate --shims. When a shim is invoked, it executes mise exec <tool> <original-args>, allowing mise to load the proper environment variables and select the correct version based on your current project configuration. Because the shim calls back into mise on every invocation, the actual binary can change at runtime without rewriting symlinks.

Preventing Recursive Execution

To avoid infinite loops where a shim might call itself, mise strips its own shims from the PATH before spawning subprocesses. The strip_shims_from_path logic in [src/tera.rs](https://github.com/jdx/mise/blob/main/src/tera.rs) removes shim entries from the environment, ensuring that spawned tools reference the actual binaries rather than re-entering the shim layer.

Practical Workflow: Installing and Using Shims

The following workflow demonstrates complete shim lifecycle management, from initial setup to maintenance:


# 1. Add shims to PATH (place in .bashrc or .zshrc for persistence)

eval "$(mise activate bash --shims)"

# 2. Install a tool - shims generate automatically

mise install node

# Creates: ~/.local/share/mise/shims/node

# Creates: ~/.local/share/mise/shims/npm

# 3. Use tools through shims - version resolves dynamically

node -v

# Actually executes: mise exec node -v

# 4. Rebuild shims after adding new executables

mise reshim

# Triggers rebuild_shims_and_runtime_symlinks to sync the directory

Limitations of the Shim Approach

Shims provide command discovery but do not replicate full mise functionality. According to the [docs/dev-tools/shims.md](https://github.com/jdx/mise/blob/main/docs/dev-tools/shims.md) documentation, shims do not support shell-specific features such as mise hook-env, watch_files, or arbitrary [env] entries defined in mise.toml. These features require mise activate without the --shims flag, which modifies the shell environment directly rather than using proxy executables.

Additionally, shims introduce a small latency overhead on each invocation because they must spawn the mise binary to resolve the correct tool version. For high-frequency scripting where microseconds matter, direct PATH activation may be preferable.

Summary

  • mise creates shims as lightweight wrappers in ~/.local/share/mise/shims that forward calls to the core binary.
  • Dynamic resolution occurs at runtime via mise exec, allowing version switching without symlink manipulation.
  • Recursion prevention is handled by strip_shims_from_path in src/tera.rs before spawning subprocesses.
  • Maintenance requires running mise reshim to synchronize the directory with your current toolset.
  • Trade-offs include lack of support for shell hooks and environment variables defined in configuration files.

Frequently Asked Questions

How do I activate shims in my shell?

Run mise activate --shims and follow the output to add the shims directory to your PATH. This places ~/.local/share/mise/shims (or the Windows equivalent) at the front of your PATH, causing the shell to find shim executables before system binaries.

Why does mise need to rebuild shims?

Shims must be rebuilt when new executables are added or removed from your tool installations. The mise reshim command triggers the rebuild_shims_and_runtime_symlinks function in src/config/mod.rs, ensuring that every binary provided by your installed tools has a corresponding shim file in the directory.

Can shims replace mise activate completely?

No. While shims handle command resolution, they cannot process shell-specific features like hook-env, watch_files, or [env] entries from your mise.toml. For full environment variable management and directory change hooks, you need standard mise activate without the --shims flag.

How does mise prevent infinite loops when using shims?

Before executing any external command, mise calls strip_shims_from_path (defined in src/tera.rs) to remove the shims directory from the PATH environment variable. This ensures that subprocesses reference the actual tool binaries directly rather than recursively invoking mise shims.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →