| name | mise |
| description | Dev-tool and runtime version management with mise — the modern replacement for nvm, pyenv, goenv, rbenv, and asdf. Use when setting up a new repo, pinning Node/Python/Go/Rust/Bun versions, configuring per-project tool versions via .mise.toml or .tool-versions, wiring direnv-style env loading, debugging "wrong version active" errors, or migrating off legacy version managers. Covers mise.toml schema, env hooks, task definitions, and integration with just and CI. |
Mise: Modern Dev Tool Management
Mise is a polyglot version manager that replaces nvm, pyenv, rbenv, and most Homebrew CLI tools with a single, fast, declarative system.
Related skills:
- just-pro - Build system setup (includes mise integration patterns)
- cli-tools - Power CLI tools (many installable via mise)
Why Mise?
| Problem | Old Way | Mise Way |
|---|
| Node versions | nvm, fnm, volta | mise use node@22 |
| Python versions | pyenv, conda | mise use python@3.12 |
| Go versions | goenv, manual | mise use go@1.25 |
| CLI tools | Homebrew | mise use jq ripgrep bat |
| Per-project versions | .nvmrc + .python-version + ... | Single .mise.toml |
Benefits:
- 1000+ tools available (
mise registry | wc -l)
- Parallel installs, prebuilt binaries
- Works on macOS and Linux
- Declarative config in repo = reproducible environments
Quick Start
Install Mise
curl https://mise.run | sh
Shell Setup
Add to your shell rc file. If using both mise and direnv (recommended), load mise first:
eval "$(mise activate zsh)"
eval "$(direnv hook zsh)"
mise activate fish | source
direnv hook fish | source
For faster startup, use shims instead of (or with) activation:
export PATH="$HOME/.local/share/mise/shims:$PATH"
eval "$(mise activate zsh)"
eval "$(direnv hook zsh)"
fish_add_path -p ~/.local/share/mise/shims
mise activate fish | source
direnv hook fish | source
Install Tools
mise use node@22 python@3.12 go@latest
mise use -g jq ripgrep bat
Project Setup
New Repo
mise use node@22 go@1.25
git add .mise.toml
Existing Repo (first clone)
mise trust
mise install
Example .mise.toml
[tools]
node = "22"
go = "1.25"
python = "3.12"
just = "latest"
sqlc = "latest"
Configuration Hierarchy
Mise merges configs from multiple levels:
~/.config/mise/config.toml # Global defaults
└── ~/projects/.mise.toml # Workspace defaults
└── ~/projects/foo/.mise.toml # Project-specific
More specific configs override less specific ones.
Global Config (~/.config/mise/config.toml)
Your daily-driver tools:
[tools]
node = "lts"
python = "3.12"
go = "latest"
jq = "latest"
yq = "latest"
ripgrep = "latest"
fd = "latest"
bat = "latest"
eza = "latest"
delta = "latest"
fzf = "latest"
gh = "latest"
lazygit = "latest"
just = "latest"
direnv = "latest"
starship = "latest"
[settings]
auto_install = true
Direnv Integration
Direnv handles per-directory environment variables. Combined with mise:
- Mise → tool versions (node, go, python)
- Direnv → environment variables (DATABASE_URL, API keys)
Best Practice: Keep a single source of truth:
.mise.toml → tool versions only (node, go, python)
.envrc → environment variables (DATABASE_URL, API_KEY, etc.)
Don't use [env] section in .mise.toml - it creates confusion about where vars come from.
Setup
-
Install direnv via mise:
mise use -g direnv
-
Add direnv hook to shell rc:
eval "$(direnv hook zsh)"
direnv hook fish | source
-
Create .envrc in your project:
if command -v mise &> /dev/null; then
eval "$(mise hook-env -s bash)"
fi
export DATABASE_URL="postgres://localhost/myapp"
export LOG_LEVEL="debug"
-
Allow the envrc:
direnv allow
Tip: Use .envrc.example (committed) + .envrc (gitignored with secrets).
Just Integration
just and mise complement each other:
- mise → pins tool versions
- just → runs commands using those tools
See the just-pro skill for full patterns. Quick summary:
Shell Override (recommended for teams)
# Every recipe runs through mise automatically
set shell := ["mise", "exec", "--", "bash", "-c"]
build:
npm run build
test:
go test ./...
Graceful Degradation (for open source)
_exec cmd:
#!/usr/bin/env bash
if command -v mise &>/dev/null; then
mise exec -- {{cmd}}
else
{{cmd}}
fi
build: (_exec "npm run build")
Setup Recipe
setup:
#!/usr/bin/env bash
mise trust && mise install
# Create .envrc from example if missing
if [[ ! -f .envrc ]] && [[ -f .envrc.example ]]; then
cp .envrc.example .envrc
echo "Created .envrc from example - edit with your values"
direnv allow
fi
echo "Toolchain ready"
Mise vs Devcontainer
| Aspect | Mise + Direnv | Devcontainer |
|---|
| Isolation | Shared host filesystem | Full container isolation |
| Speed | Native performance | Container overhead |
| Setup time | Seconds (mise install) | Minutes (image build) |
| Works offline | After first install | After first build |
| IDE support | Any editor, native | VS Code / JetBrains |
| Team adoption | Low friction | Requires Docker knowledge |
| CI parity | Good (mise in CI) | Excellent (same container) |
Recommendation: Use mise for fast local dev. Add devcontainer for hermetic reproducibility if needed. They're not mutually exclusive.
Troubleshooting
Tools not in PATH
mise doctor
Ensure mise activates after other PATH modifications in shell rc.
Shims vs Activate
- Shims (
~/.local/share/mise/shims): Wrapper scripts, always work
- Activate: Dynamic PATH modification, faster for frequent version switching
Use both for reliability:
export PATH="$HOME/.local/share/mise/shims:$PATH"
eval "$(mise activate zsh)"
Direnv + Mise
Use mise hook-env -s bash in .envrc, not mise activate:
eval "$(mise hook-env -s bash)"
eval "$(mise activate bash)"
Quick Reference
mise use node@22
mise use -g ripgrep
mise ls
mise ls-remote node
mise outdated
mise install
mise trust
mise prune
mise reshim
mise self-update
Further Reading