
Mise
- 14 installs
- 22 repo stars
- Updated February 19, 2026
- markpitt/claude-skills
Helps with ai & agent building tasks.
About
mise is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- mise
- AI & Agent Building
- AI-coding skill
Mise by the numbers
- 14 all-time installs (skills.sh)
- Ranked #11,275 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/markpitt/claude-skills --skill miseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 14 |
|---|---|
| repo stars | ★ 22 |
| Last updated | February 19, 2026 |
| Repository | markpitt/claude-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Mise Skill
Expert guidance on mise — the single tool that replaces asdf, nvm, pyenv, direnv, and make. Covers installation, mise.toml configuration, runtime management, task runner, environment variables, secrets, backends, Lua plugins, and team workflows.
Quick Reference Table
| Task | Load Resource | Key Concepts |
|---|---|---|
| Install mise or activate in shell | references/core-concepts.md | curl install, shell activation, mise doctor |
| Configure tools and env vars in mise.toml | references/core-concepts.md | [tools], [env], config hierarchy, mise.local.toml |
| List, install, upgrade, or pin tool versions | references/commands-reference.md | mise use, mise install, mise upgrade, mise prune |
| Understand backends (ubi, cargo, npm, aqua) | references/backends.md | Core, UBI, Cargo, npm, Pipx, Aqua, Lua plugins |
| Define and run project tasks | references/task-runner.md | [tasks], mise run, depends, parallel, monorepo |
| Manage environment variables and secrets | references/environment-secrets.md | _.path, _.file, templating, SOPS, fnox |
| Advanced: caching, hooks, Pitchfork daemons | references/advanced-features.md | msgpack cache, Lua hooks, Pitchfork, Usage completions |
| Migrate from asdf, nvm, or direnv | references/core-concepts.md | .tool-versions, .nvmrc, compatibility |
| Team workflows, CI/CD, lockfiles | references/advanced-features.md | mise.lock, monorepo tasks, CI integration |
Orchestration Protocol
Phase 1 — Classify the Task
Identify which category the user's request falls into:
- Setup — installing mise, activating in shell, configuring global tools
- Configuration — writing or editing mise.toml, pinning versions, managing hierarchy
- Tool management — installing, listing, upgrading, or switching runtime versions
- Backends — choosing the right backend for a tool (ubi, aqua, cargo, npm)
- Tasks — defining and running tasks, task dependencies, monorepo tasks
- Environment — setting env vars, PATH modifications, .env file loading
- Secrets — SOPS encryption, fnox remote secrets
- Advanced — caching internals, Lua plugins, Pitchfork daemons, shell hooks
- Migration — moving from asdf, nvm, pyenv, or direnv
Phase 2 — Load the Right Resource
Load the resource indicated in the Quick Reference Table. For tasks spanning multiple areas (e.g. "set up a new project with tools, tasks, and secrets"), load all relevant files. The files are lean enough to load together.
Phase 3 — Execute
Apply guidance from the loaded resource. Produce complete, commented mise.toml snippets where configuration is involved. Always suggest mise doctor as the first debugging step for any environment issue.
Common Task Workflows
Workflow 1: Bootstrap a New Project
1. Load references/core-concepts.md for config hierarchy 2. Create mise.toml in the project root with [tools] for required runtimes 3. Add [env] section for project-specific environment variables 4. Run mise install to install all declared tools 5. Commit mise.toml; add mise.local.toml to .gitignore for personal overrides
[tools]
node = "22"
python = "3.12"
[env]
NODE_ENV = "development"
_.path = ["./node_modules/.bin"]Workflow 2: Replace nvm / asdf
1. Load references/core-concepts.md 2. Mise auto-reads .nvmrc and .tool-versions — no conversion needed immediately 3. Activate mise in shell profile: eval "$(mise activate zsh)" 4. Uninstall nvm/asdf once comfortable; run mise doctor to verify 5. Migrate to mise.toml to unlock env vars and tasks
Workflow 3: Define Project Tasks
1. Load references/task-runner.md 2. Add [tasks.name] blocks to mise.toml 3. Set depends for prerequisite tasks 4. Run: mise run <task> or mise run --list to see all tasks
Workflow 4: Manage Secrets with SOPS
1. Load references/environment-secrets.md 2. Install sops: mise use --global sops 3. Encrypt secrets file with age key 4. Reference in mise.toml: _.file = { path = "secrets.enc.json", decrypt = true }
Workflow 5: Troubleshoot Environment Issues
1. Run mise doctor — surfaces shell integration problems, PATH issues, shim conflicts 2. Run mise env — shows exactly which env vars mise will set in the current directory 3. Run mise ls — lists active tool versions in the current directory 4. Load references/core-concepts.md for config hierarchy to diagnose overrides
Resource Summaries
| File | Contents | Lines |
|---|---|---|
| references/core-concepts.md | Installation, shell activation, mise.toml format, config hierarchy, migration from asdf/nvm | ~280 |
| references/commands-reference.md | Full command reference: use, install, upgrade, exec, ls, prune, doctor, env | ~240 |
| references/backends.md | Core, UBI, Cargo, npm, Pipx, Aqua backends; Lua plugin architecture; writing custom plugins | ~260 |
| references/task-runner.md | Task definitions, depends, parallel execution, file tasks, monorepo tasks, Usage completions | ~250 |
| references/environment-secrets.md | [env] reference, PATH management, Tera templating, SOPS integration, fnox remote secrets | ~240 |
| references/advanced-features.md | Caching architecture, Lua lifecycle hooks, Pitchfork daemon manager, shell hooks, team CI/CD | ~260 |
Best Practices
- Commit mise.toml — treat it as the project's source of truth for the toolchain; everyone on the team gets the same runtimes
- Use mise.local.toml for personal overrides — add it to .gitignore; never commit personal paths or secrets
- Prefer `mise use` over hand-editing —
mise use node@22writes the correct version and installs the tool atomically - Global tools = CLI utilities only — use
mise use --globalfor ripgrep, bat, gh; enforce local versions for runtimes to ensure reproducibility - Always run `mise doctor` first — it diagnoses 90% of "tool not found" / "wrong version" issues instantly
- Use lockfiles for CI strictness — enable mise.lock (experimental) to pin exact patch versions
- Don't mix mise + asdf shims — they conflict on PATH; remove asdf entirely when migrating
External References
- mise documentation — official docs
- mise GitHub — source and releases
- mise backends list — all available backends
- mise plugins registry — community plugins
- SOPS documentation — secrets encryption
- Pitchfork — daemon manager companion tool
- fnox — remote secrets companion tool
Mise Advanced Features
Covers caching architecture, shell hooks, Pitchfork daemon management, team workflows, CI/CD integration, and lockfiles.
---
Performance and Caching Architecture
Mise is built in Rust with a sub-millisecond shell prompt target. Understanding its cache model helps diagnose unexpected behaviour.
Cache Mechanics
| Cache Layer | What's Cached | Invalidation |
|---|---|---|
| Remote version lists | Available versions of each tool (e.g. all Node.js versions) | Daily (configurable) |
| Environment resolution | Resolved PATH and env vars for the current directory | File timestamp change on any mise.toml or lock file |
| Tool metadata | Plugin metadata and checksums | Per plugin update |
- Serialization: msgpack with zstd compression — significantly smaller and faster than JSON.
- Early exit: If no
mise.tomlhas changed since last entry, mise exits without recalculating the environment. This is why the shell prompt stays fast even in large projects. - Why remote secrets aren't built in: Fetching secrets from AWS/Vault would require a network call on every directory change, breaking the under-10ms prompt requirement. This is why fnox exists as a separate tool.
Cache Location
# Default cache dir
~/.cache/mise/
# Override
export MISE_CACHE_DIR=/tmp/mise-cacheClearing the Cache
# Clear all caches (rarely needed)
mise cache clear
# Clear only version list cache (if you need latest versions immediately)
mise cache clear --stale---
Shell Hooks
Mise supports hooks that run scripts at lifecycle events.
Tool Post-Install Hooks
Run a command after a specific tool is installed:
[tools]
# Enable corepack after Node.js installs
node = { version = "22", postinstall = "corepack enable" }
# Set up a Python virtualenv after Python installs
python = { version = "3.12", postinstall = "python -m venv .venv" }Directory Hooks (enter/leave)
Run scripts when entering or leaving a directory:
[hooks]
enter = "echo 'Entered project directory'"
leave = "echo 'Left project directory; cleaning up...'"More complex hooks using an array:
[hooks]
enter = [
"mise doctor --quiet",
"echo 'Project: {{ cwd | basename }}'"
]Git Hooks
Generate a Git pre-commit hook that runs a mise task:
mise generate git-pre-commit --task lintThis creates .git/hooks/pre-commit that runs mise run lint before each commit.
Available Hook Types
| Hook | Trigger |
|---|---|
enter | Directory entry |
leave | Directory exit |
postinstall | After a tool installs |
preinstall | Before a tool installs |
---
Pitchfork: Daemon Management
Pitchfork is a companion tool that automatically manages background processes (databases, dev servers, message queues) per directory.
Problem it Solves
Without Pitchfork, developers either:
- Manually run
docker-compose upin a separate terminal tab (and forget to stop it) - Write complex
mise run starttasks that don't clean up on exit
How Pitchfork Works
1. Tracks active shell sessions per project directory 2. When the first shell enters the directory: starts configured daemons 3. When the last shell exits: automatically stops all daemons 4. If you open 3 tabs in the same project, daemons persist until all 3 close
Configuration
# mise.toml
[tools]
pitchfork = "latest"
[tasks.start-dev]
# Pitchfork manages this as a daemon
run = "pitchfork start"# pitchfork.toml
[[daemons]]
name = "postgres"
command = "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=dev postgres:16"
health_check = { tcp = "localhost:5432", timeout = 30 }
[[daemons]]
name = "redis"
command = "redis-server"
health_check = { tcp = "localhost:6379" }Status
Pitchfork is experimental as of early 2026. It works well but the API may change.
---
Lockfiles
Mise lockfiles (mise.lock) pin exact tool versions including patch numbers. Without a lockfile, node = "22" could resolve to 22.11.0 today and 22.12.0 tomorrow.
Enabling Lockfiles
# mise.toml
[settings]
experimental = true
lockfile = trueWorkflow
# First install: creates mise.lock
mise install
# mise.lock is generated automatically:
# [node]
# version = "22.11.0"
# backend = "core"
# checksum = "sha256:abc123..."
# Team members: reproduce exact versions
mise install # uses mise.lock if presentLockfile Recommendations
| Scenario | Recommendation |
|---|---|
| Application (final artifact) | Commit mise.lock |
| Library | Don't commit mise.lock; let consumers resolve |
| CI pipeline | Always use mise.lock |
| Local dev, solo | Optional |
---
Team Workflows
Onboarding a New Developer
# 1. Clone the repo
git clone https://github.com/myorg/myproject && cd myproject
# 2. Install mise (if not already installed)
curl https://mise.run | sh && eval "$(mise activate zsh)"
# 3. Install all project tools in one command
mise install
# 4. (Optional) Install project tasks completion
eval "$(mise completion zsh)"
# 5. Verify
mise doctor
mise lsRecommended .gitignore Additions
# Mise personal overrides
mise.local.toml
.env.local
.env.personal
# SOPS plaintext (never commit unencrypted secrets)
secrets.json
.env.secretsCI/CD Integration
# GitHub Actions example
- name: Install mise
run: curl https://mise.run | sh
env:
MISE_INSTALL_PATH: /usr/local/bin/mise
- name: Install project tools
run: mise install
# Uses mise.lock if present for reproducible builds
- name: Run CI tasks
run: mise run ci# GitLab CI example
before_script:
- curl https://mise.run | MISE_INSTALL_PATH=/usr/local/bin sh
- mise install
test:
script:
- mise run testMonorepo Strategy
For large monorepos with many packages:
monorepo/
├── mise.toml # shared: common tools and env vars for all packages
├── mise.lock # committed: reproducible versions
├── apps/
│ ├── api/
│ │ └── mise.toml # API-specific tools only (e.g., dotnet version override)
│ └── frontend/
│ └── mise.toml # Frontend-specific tools (e.g., node, pnpm)
└── packages/
└── shared-ui/
└── mise.toml # Library toolsRoot mise.toml defines the common baseline. Sub-packages override only what's different.
Enable monorepo task execution at root:
# mise.toml (root)
[settings]
experimental = true# Run tests in all packages
mise //...:test
# Run build in a specific app
mise //apps/api:build
# Run lint in all apps
mise //apps/*:lint---
Settings Reference
Commonly used [settings] entries:
[settings]
# Enable experimental features (lockfiles, monorepo tasks, etc.)
experimental = true
# Enable lockfile
lockfile = true
# Auto-install tools declared in mise.toml on directory entry
# (default: false — prompts instead)
auto_install = true
# Disable legacy file reading (.nvmrc, .tool-versions)
legacy_version_file = false
# Concurrency for parallel tool installations
jobs = 4
# Verbose HTTP requests (for debugging network/download issues)
verbose = false---
Troubleshooting
| Problem | Diagnosis | Fix |
|---|---|---|
| Wrong version of a tool active | mise ls to check + mise env to see PATH | Check config hierarchy with mise config ls; ensure project mise.toml takes precedence |
| Tool not found after install | mise doctor | Shell not activated; add eval "$(mise activate zsh)" to shell profile |
| SOPS decryption fails on CI | Check SOPS key availability | Inject age private key or IAM role into CI environment |
| Slow shell prompt | MISE_VERBOSE=1 mise env to time resolution | Check for network-calling hooks; clear stale cache with mise cache clear |
| asdf shims conflicting | mise doctor reports PATH order issue | Remove asdf from shell profile; uninstall fully |
| mise.lock conflict on merge | Two branches installed different patch versions | Run mise install after merge to regenerate lock |
Mise Backends
Backends are the mechanisms mise uses to fetch and install tools. Choosing the right backend determines where a tool comes from, how it's installed, and how it's updated.
---
Backend Overview
| Backend | Prefix | Source | Best For |
|---|---|---|---|
| Core | (none) | Built-in Rust implementation | Node, Python, Go, Java, Ruby, Rust |
| UBI | ubi: | GitHub Releases (binary download) | Any tool with GitHub releases |
| Cargo | cargo: | crates.io | Rust CLI tools |
| npm | npm: | npm registry | Node.js CLI tools |
| Pipx | pipx: | PyPI | Python CLI tools |
| Aqua | aqua: | Aqua registry | Security-audited CLI tools |
| Lua plugins | (custom) | Custom Lua scripts | Custom/private tools |
| asdf plugins | asdf: | asdf plugin registry | Legacy compatibility |
---
Core Backends
Core backends are built directly into mise as Rust code. They are the fastest and most reliable option because they don't require external plugin scripts.
Supported Core Tools
| Tool | mise name | Notes |
|---|---|---|
| Node.js | node | Reads .nvmrc, supports lts, lts/iron aliases |
| Python | python | Reads .python-version |
| Go | go | |
| Java | java | Supports Temurin, Corretto, Zulu distributions |
| Ruby | ruby | |
| Rust | rust | Manages toolchains via rustup |
| .NET | dotnet | |
| Erlang | erlang | |
| Elixir | elixir | |
| Deno | deno | |
| Bun | bun |
[tools]
node = "22"
python = "3.12"
go = "1.23"
java = { version = "21", jdk_id = "temurin" }
dotnet = "8.0"---
UBI (Universal Binary Installer)
UBI downloads pre-compiled binaries from GitHub Releases. Use it for any tool that publishes release artifacts to GitHub.
Syntax
[tools]
# Basic: owner/repo (mise infers binary name from repo)
"ubi:cli/cli" = "latest" # GitHub CLI (gh)
"ubi:BurntSushi/ripgrep" = "latest" # ripgrep
"ubi:sharkdp/bat" = "latest" # bat
# With explicit binary name (when it differs from repo name)
"ubi:cli/cli[exe:gh]" = "latest"Command line
mise use ubi:BurntSushi/ripgrepWhen to use UBI
- The tool publishes GitHub release assets (
.tar.gz,.zip, platform-specific binaries) - You want to pin to exact GitHub release tags
- The tool isn't in Aqua registry or other backends
---
Cargo Backend
Installs Rust CLI tools from crates.io using cargo install. Requires Rust/Cargo to be available.
[tools]
"cargo:ripgrep" = "latest"
"cargo:tokei" = "latest"
"cargo:cargo-watch" = "latest"mise use cargo:cargo-watchNote: Cargo installs compile from source, which is slower than binary backends. Prefer UBI or Aqua if a pre-compiled binary is available.
---
npm Backend
Installs Node.js packages as global CLI tools via npm.
[tools]
"npm:typescript" = "latest"
"npm:@angular/cli" = "latest"
"npm:azure-functions-core-tools" = "4"
"npm:prettier" = "3"mise use npm:typescriptBest practice: For project-local tools, prefer committing them to package.json devDependencies. Use the npm backend for tools that need to be available globally across projects.
---
Pipx Backend
Installs Python applications in isolated virtualenvs via pipx. Requires Python to be available.
[tools]
"pipx:black" = "latest"
"pipx:ruff" = "latest"
"pipx:poetry" = "latest"mise use pipx:black---
Aqua Backend
Aqua is a registry-based CLI tool manager with security focus. It provides a curated list of tools with checksums and signature verification.
[tools]
"aqua:cli/cli" = "latest" # GitHub CLI
"aqua:hashicorp/terraform" = "latest"
"aqua:helm/helm" = "latest"
"aqua:kubernetes/kubectl" = "latest"mise use aqua:hashicorp/terraformAdvantages of Aqua over UBI:
- Pre-verified checksums and signatures
- Curated registry with consistent naming
- Reproducible installs
Browse the registry: aquaproj/aqua-registry
---
Choosing the Right Backend
Decision guide for common scenarios:
| Scenario | Recommended Backend |
|---|---|
| Language runtime (Node, Python, Go, .NET) | Core |
| Rust CLI tool | Aqua if listed, otherwise Cargo |
| Security-critical tool (Terraform, kubectl) | Aqua (verified checksums) |
| GitHub-released binary not in Aqua | UBI |
| Node.js global CLI tool | npm |
| Python application | Pipx |
| Company-internal tool | Lua plugin |
| Legacy asdf plugin exists | asdf (with migration plan to Lua) |
---
Lua Plugin Architecture
When no built-in or community backend fits, write a custom Lua 5.1 plugin. Plugins are stored in ~/.local/share/mise/plugins/<name>/.
Plugin Directory Structure
~/.local/share/mise/plugins/my-tool/
└── mise.lua # Main plugin filePlugin Lifecycle Hooks
| Hook | Purpose | Required |
|---|---|---|
Available | Returns list of installable versions | Yes |
PreInstall | Returns download URL and SHA256 checksum | Yes |
EnvKeys | Sets environment variables and PATH entries | Yes |
PostInstall | Runs after download (chmod, compile, etc.) | No |
ParseLegacyFile | Parses legacy version files (.nvmrc, etc.) | No |
Example Plugin
-- mise.lua for a hypothetical "mytool"
local PLUGIN = {}
function PLUGIN:Available(ctx)
-- Return a list of available version strings
return { "1.0.0", "1.1.0", "2.0.0" }
end
function PLUGIN:PreInstall(ctx)
local version = ctx.version
local os = ctx.os -- "linux", "macos", "windows"
local arch = ctx.arch -- "x64", "arm64"
return {
url = "https://example.com/mytool-" .. version .. "-" .. os .. "-" .. arch .. ".tar.gz",
sha256 = "abc123..." -- optional but recommended
}
end
function PLUGIN:EnvKeys(ctx)
return {
{ key = "MYTOOL_HOME", value = ctx.path },
{ key = "PATH", value = ctx.path .. "/bin" }
}
end
function PLUGIN:PostInstall(ctx)
-- Make binaries executable
os.execute("chmod +x " .. ctx.path .. "/bin/mytool")
end
return PLUGINInstalling a Custom Plugin
# From a local path
mise plugins install my-tool ~/.local/share/mise/plugins/my-tool
# From a git repo
mise plugins install my-tool https://github.com/you/mise-tool-plugin.git---
asdf Plugin Compatibility
Mise can use existing asdf plugins as a compatibility layer:
[tools]
"asdf:hashicorp/terraform" = "latest"# Or install asdf plugins directly
mise plugins install terraform https://github.com/asdf-community/asdf-hashicorp.git
mise use terraform@latestNote: asdf plugins run as bash scripts via process spawning and are significantly slower than Lua plugins. Migrate to Lua or Core/UBI/Aqua backends when performance matters.
Mise Commands Reference
Complete reference for all commonly-used mise commands.
---
Tool Installation & Version Management
mise use
Installs a tool and pins the version in the nearest mise.toml. The most common way to add or change a tool.
# Pin Node.js 22 in the current project's mise.toml
mise use node@22
# Pin multiple tools at once
mise use node@22 python@3.12 go@latest
# Pin globally (writes to ~/.config/mise/config.toml)
mise use --global node@lts
# Pin with a fuzzy version pattern
mise use node@22.x
# Install without modifying config (useful in CI)
mise use --no-config node@22mise install
Installs all tools defined in mise.toml (and any cascading config files) without modifying the config. Ideal for CI pipelines and initial project setup.
# Install everything declared in mise.toml
mise install
# Install a specific tool without pinning
mise install node@22
# Install all tools including those from parent configs
mise install --allmise exec (mise x)
Runs a command using a specific tool version without modifying any config.
# Run a command with a one-off tool version
mise x node@20 -- npm test
mise x python@3.11 -- pytest
# Shorthand
mise x node@20 -- node --versionmise run
Executes tasks defined in mise.toml. See also references/task-runner.md.
mise run build
mise run test
mise run --list # list all available tasks---
Listing and Inspecting Tools
mise ls
Lists all active tools and their version status for the current directory.
mise ls # tools in scope for current directory
mise ls --current # only active (installed and in-use) tools
mise ls --missing # tools declared but not yet installed
mise ls --outdated # tools where a newer version is availableExample output:
Tool Version Source Requested
node 22.11.0 ~/project/mise.toml 22
python 3.12.7 ~/project/mise.toml 3.12mise outdated
Shows tools that have newer versions available.
mise outdated # all tools
mise outdated node # specific toolmise current
Prints the current active version of a tool — useful for quick checks.
mise current # all tools
mise current node # specific toolmise where
Prints the installation path for a tool.
mise where node # /home/user/.local/share/mise/installs/node/22.11.0---
Upgrading and Cleanup
mise upgrade
Upgrades tools to the latest version matching the pinned constraint.
mise upgrade # upgrade all tools
mise upgrade node # upgrade only node (respects version constraint in mise.toml)
mise upgrade --bump # upgrade AND rewrite version constraints in mise.tomlmise prune
Removes tool versions that are no longer referenced by any config file.
mise prune # removes unused versions (interactive confirmation)
mise prune --dry-run # preview what would be removed
mise prune node # prune unused node versions only---
Environment Inspection
mise env
Prints the environment variables mise would set for the current directory. Useful for debugging.
mise env # show all env vars as export statements
mise env --json # JSON output
mise env NODE_ENV # show a specific variablemise doctor
Diagnoses shell integration issues, PATH conflicts, and configuration problems.
mise doctor # comprehensive environment health checkCommon issues reported:
- Shell not activated (missing
eval "$(mise activate zsh)") - asdf or nvm shims appearing before mise shims in PATH
- Missing tool versions
- Invalid
mise.tomlsyntax
---
Configuration Inspection
mise config
Shows the active configuration hierarchy for the current directory.
mise config # list all config files in scope
mise config ls # same as above
mise config get # dump merged configurationmise settings
Reads and writes mise settings.
mise settings # show all settings
mise settings get experimental # read a setting
mise settings set experimental true # write a setting---
Plugin Management
mise plugins
Lists and manages tool backends/plugins.
mise plugins ls # list installed plugins
mise plugins ls --all # list all available plugins (from registry)
mise plugins install <name> # install a plugin
mise plugins update # update all plugins
mise plugins uninstall <name>---
Shim Management
Mise can operate in two modes: PATH-based (recommended, via shell activation) and shim-based (for environments where you cannot modify PATH).
mise reshim # regenerate shims after manual changes
mise shims # list all shim executables---
Useful One-liners
# Check which mise.toml is setting a particular tool
mise ls node --json | jq '.[].source'
# List all versions of a tool available to install
mise ls-remote node
# Install mise in CI without shell interaction
curl https://mise.run | MISE_INSTALL_PATH=/usr/local/bin sh
# Run a task with a specific environment
MISE_ENV=production mise run deploy
# Print what mise.toml would look like after 'use'
mise use --dry-run node@22---
Exit Codes
| Code | Meaning |
|---|---|
0 | Success |
1 | General error |
2 | Tool not found or not installed |
3 | Configuration error |
---
Environment Variables for Scripting
Mise behaviour can be controlled via environment variables — useful in CI:
| Variable | Effect |
|---|---|
MISE_DATA_DIR | Override the data directory (default: ~/.local/share/mise) |
MISE_CONFIG_DIR | Override the config directory (default: ~/.config/mise) |
MISE_VERBOSE=1 | Enable verbose output |
MISE_DEBUG=1 | Enable debug output |
MISE_QUIET=1 | Suppress non-error output |
MISE_YES=1 | Auto-confirm all prompts |
MISE_NOT_FOUND_AUTO_INSTALL=false | Disable auto-install on missing tool |
Mise Core Concepts
What is Mise?
Mise (mise-en-place) is a single Rust-based binary that replaces a collection of separate tools:
| Replaced Tool | Mise Equivalent |
|---|---|
asdf | Tool version management |
nvm, pyenv, rbenv | Language-specific version managers |
direnv | Automatic environment variable loading |
make / package.json scripts | Task runner |
fnm, volta | Node.js version management |
It activates automatically when you enter a directory containing a mise.toml file, setting the correct tool versions and environment variables without any manual steps.
---
Installation
Linux / macOS
# Recommended: installer script
curl https://mise.run | sh
# macOS via Homebrew
brew install mise
# Cargo
cargo install miseWindows
# winget (recommended)
winget install jdx.mise
# Scoop
scoop install miseVerify installation
mise --version
mise doctor # checks shell integration, PATH, shims---
Shell Activation
Mise must be hooked into your shell to auto-activate when entering directories. Add the appropriate line to your shell profile:
| Shell | Profile File | Activation Command |
|---|---|---|
| zsh | ~/.zshrc | eval "$(mise activate zsh)" |
| bash | ~/.bashrc or ~/.bash_profile | eval "$(mise activate bash)" |
| fish | ~/.config/fish/config.fish | `mise activate fish \ |
| PowerShell | $PROFILE | `mise activate pwsh \ |
After editing your profile, restart your terminal or run source ~/.zshrc (etc.).
Testing activation:
cd /tmp && mise ls # should show no tools
cd /your/project && mise ls # should show project tools---
Configuration File: mise.toml
mise.toml is the primary configuration file for mise. It supports a hierarchical cascade:
| Location | Scope | Notes |
|---|---|---|
/etc/mise/config.toml | System-wide | Managed by sysadmin |
~/.config/mise/config.toml | Global user | Applies everywhere for this user |
~/<parent-dir>/mise.toml | Parent directory | Inherited by subdirectories |
./mise.toml | Project | Committed to source control |
./mise.local.toml | Personal override | Git-ignored; personal paths and secrets |
Cascade resolution: mise merges all applicable configs, with more specific files taking priority. If two files define node, the project-level version wins over the global one.
File Format
# mise.toml
[tools]
# pin exact versions
node = "22.11.0"
# use a major-version constraint
python = "3.12"
# use a named alias
go = "latest"
# install multiple versions
ruby = ["3.2", "3.3"]
# pin with options
java = { version = "21", jdk_id = "temurin" }
[env]
# static values
ASPNETCORE_ENVIRONMENT = "Development"
APP_PORT = "8080"
# add directory to PATH
_.path = ["./bin", "./node_modules/.bin"]
# load a .env file
_.file = ".env"
# computed from environment
HOME_DIR = "{{ env.HOME }}"
APP_NAME = "{{ cwd | basename }}"
[settings]
# opt-in to experimental features
experimental = truemise.local.toml
Personal overrides that should never be committed:
# mise.local.toml (add this file to .gitignore)
[env]
DATABASE_URL = "postgres://localhost/mydb_dev"
_.file = ".env.local"---
Legacy File Compatibility
Mise automatically reads legacy configuration files — no manual migration required:
| Legacy File | Handled by | Notes |
|---|---|---|
.tool-versions | asdf compatibility plugin | Exact asdf format |
.nvmrc | Node.js backend | lts/iron, v22.0.0, 22 all valid |
.node-version | Node.js backend | Same as .nvmrc |
.python-version | Python backend | 3.12, 3.12.5 |
.ruby-version | Ruby backend | 3.2.0 |
To disable legacy file reading:
# ~/.config/mise/config.toml
[settings]
legacy_version_file = false---
Migrating from asdf
Mise is a drop-in replacement for asdf. Migration steps:
1. Install mise and activate in shell 2. Existing .tool-versions files continue to work without changes 3. Run mise install in any project to install the tools declared in .tool-versions 4. Gradually migrate projects to mise.toml to gain env var and task features 5. Remove asdf from shell profile to avoid PATH conflicts
# Uninstall asdf (after confirming mise works)
rm -rf ~/.asdf
# Remove asdf lines from ~/.zshrc (or equivalent)Key differences from asdf:
- Mise is significantly faster (Rust vs shell)
mise.tomlis more expressive than.tool-versions- Mise includes env vars and task runner — asdf does not
- Mise plugins use Lua instead of bash
---
Migrating from nvm
1. Activate mise in shell profile 2. Mise reads .nvmrc automatically 3. Run mise install to install the Node version from .nvmrc 4. Remove nvm activation from shell profile: delete or comment the export NVM_DIR block 5. Optionally remove ~/.nvm to reclaim disk space
# After confirming mise manages Node correctly:
rm -rf ~/.nvm---
Migrating from direnv
Mise's [env] section replaces .envrc files, but the syntax differs:
| direnv (.envrc) | mise.toml equivalent |
|---|---|
export FOO=bar | FOO = "bar" under [env] |
PATH_add bin | _.path = ["./bin"] under [env] |
dotenv .env | _.file = ".env" under [env] |
source_env ../.envrc | Handled automatically via config hierarchy |
If you need both mise and direnv simultaneously, mise has a direnv integration mode. See the official docs.
---
Global Tools
Global tools are installed via the global config at ~/.config/mise/config.toml:
# Install a tool globally
mise use --global node@lts
mise use --global ripgrep cargo:ripgrep
# Or edit ~/.config/mise/config.toml directly:
[tools]
node = "lts"
ripgrep = "latest"Best practice: Use global config for CLI utilities (ripgrep, bat, gh, jq); always define runtimes (node, python, dotnet) at the project level for reproducibility.
---
mise doctor
Run mise doctor whenever something seems wrong. It checks:
- Shell integration: is mise properly hooked into the shell?
- Shims: are mise shims on PATH before system versions?
- Config files: are all active mise.toml files valid?
- Tool conflicts: are shadowed tools from asdf/nvm causing conflicts?
- Active tools: dumps the current effective tool versions
mise doctor
# Example output:
# [WARN] ~/.asdf/shims is earlier in PATH than mise shims
# [OK] mise shim dir: ~/.local/share/mise/shims
# [OK] mise config: /home/user/project/mise.tomlMise Environment Variables and Secrets
Mise replaces direnv for automatic environment variable management. Variables defined in [env] in mise.toml are loaded when you enter the directory and unloaded (restored) when you leave.
---
The [env] Section
Static Values
[env]
NODE_ENV = "development"
APP_PORT = "8080"
ASPNETCORE_ENVIRONMENT = "Development"
AZURE_TENANT_ID = "00000000-0000-0000-0000-000000000000"PATH Management
Mise provides special _.path to extend PATH without overwriting it:
[env]
# Prepend directories to PATH (array)
_.path = ["./bin", "./node_modules/.bin", "./scripts"]
# Single directory (string shorthand)
_.path = "./bin"The paths are relative to the mise.toml file's directory. Mise prepends them when entering the directory and removes them when leaving.
---
Loading .env Files
Plain .env Files
[env]
# Load a .env file alongside static values
_.file = ".env"
# Load multiple files (merged, later files win on conflict)
_.file = [".env", ".env.local"]Encrypted .env Files (SOPS)
[env]
# Decrypt and load on entry
_.file = { path = "secrets.enc.json", decrypt = true }See the SOPS section below for setup.
File Format Support
Mise _.file supports:
.env(KEY=VALUE pairs).json(flat key-value object).yaml/.yml.toml
---
Tera Templating
Mise uses Tera templating for dynamic env values. Templates are evaluated at directory entry.
Available Variables
| Template Variable | Description |
|---|---|
{{ env.HOME }} | Value of a current environment variable |
{{ cwd }} | Current working directory (full path) |
| `{{ cwd \ | basename }}` |
{{ config_root }} | Directory containing the mise.toml file |
Examples
[env]
# Derive app name from directory name
APP_NAME = "{{ cwd | basename }}"
# Build a connection string from existing env vars
DB_HOST = "{{ env.DB_HOST | default(value='localhost') }}"
CONNECTION_STRING = "Server={{ env.DB_HOST }};Database={{ env.APP_NAME }};Integrated Security=True"
# Reference an absolute path relative to the project root
ASSETS_DIR = "{{ config_root }}/assets"---
Secrets with SOPS
Mozilla SOPS encrypts secrets files at rest. Mise can decrypt them on the fly when loading environment variables, without storing secrets in plaintext.
Setup
# 1. Install SOPS
mise use --global sops
# 2. Install age (lightweight encryption key tool)
mise use --global age
# 3. Generate an age key pair
age-keygen -o ~/.config/sops/age/keys.txt
# Note the public key output (starts with age1...)
# 4. Configure SOPS to use your age key (~/.sops.yaml or project .sops.yaml)
cat > .sops.yaml << EOF
creation_rules:
- path_regex: secrets.enc.*
age: age1YOURPUBLICKEYHERE
EOFEncrypting Secrets
# Create a JSON secrets file
cat > secrets.json << EOF
{
"DATABASE_URL": "postgres://user:password@host/db",
"API_KEY": "sk-supersecret"
}
EOF
# Encrypt it with SOPS
sops --encrypt --input-type json --output-type json secrets.json > secrets.enc.json
# Delete the plaintext file
rm secrets.jsonLoading SOPS Secrets in mise.toml
[env]
# Mise decrypts on directory entry, sets vars, re-encrypts on leave
_.file = { path = "secrets.enc.json", decrypt = true }The SOPS private key must be available (via keys.txt or a cloud KMS) for decryption to work.
Checking What's Loaded
mise env --json | jq 'keys' # list all env vars mise would set---
fnox: Remote Secrets
fnox (Fort Knox) is a companion CLI tool for fetching secrets from remote providers (AWS Secrets Manager, Azure Key Vault, 1Password, HashiCorp Vault). It addresses a key limitation: mise's fast caching prevents it from making network calls for secrets without breaking prompt speed.
How fnox Works
fnox acts as a wrapper that fetches remote secrets and injects them before running your command:
# Fetch secrets from AWS and inject them into npm start
mise exec -- fnox exec -- npm start
# Or integrate with mise tasks:[tasks.start]
run = "fnox exec -- node server.js"fnox Configuration
# fnox.yaml
providers:
- type: aws_secrets_manager
region: eu-west-1
secrets:
- id: myapp/production/database
env:
DATABASE_URL: .password
- type: azure_key_vault
vault_url: https://myvault.vault.azure.net
secrets:
- name: api-key
env: API_KEYInstallation
mise use --global fnox---
Environment Scoping
Mise env vars are scoped to the directory and its subdirectories. When you cd out, variables are removed.
Verification
# See what would be set in current directory
mise env
# Verbose: see which config file sets each variable
mise env --verbose
# JSON output for scripting
mise env --json---
mise.local.toml for Personal Secrets
For per-developer secrets that shouldn't be committed:
# mise.local.toml (add to .gitignore)
[env]
# Personal database override
DATABASE_URL = "postgres://localhost/myapp_mark_dev"
# Load a local .env file not tracked in git
_.file = ".env.mark"Never commit mise.local.toml. Add it to .gitignore:
mise.local.toml
.env.local
.env.mark---
Common Patterns
.NET Project
[env]
ASPNETCORE_ENVIRONMENT = "Development"
ASPNETCORE_URLS = "https://localhost:5001;http://localhost:5000"
_.file = ".env.development"Node.js Project
[env]
NODE_ENV = "development"
PORT = "3000"
_.path = ["./node_modules/.bin"]
_.file = ".env.local"Python Project
[env]
PYTHONDONTWRITEBYTECODE = "1"
PYTHONUNBUFFERED = "1"
DJANGO_SETTINGS_MODULE = "myapp.settings.local"
_.path = ["./.venv/bin"]Monorepo Root Config
# Root mise.toml — shared across all services
[env]
ENVIRONMENT = "development"
LOG_LEVEL = "debug"
_.file = ".env.shared"
# Each service's mise.toml adds service-specific vars
# without overriding the rootMise Task Runner
Mise includes a built-in task runner that replaces Makefile, package.json scripts, and just. Tasks are defined in mise.toml and run with mise run <task>. They automatically inherit the tool versions and environment variables from mise.toml.
---
Defining Tasks
Inline Tasks
Short tasks defined directly in mise.toml:
[tasks.build]
description = "Build the project"
run = "cargo build --release"
[tasks.test]
description = "Run tests"
run = "cargo test"
[tasks.lint]
description = "Run linter"
run = "cargo clippy"Multi-line Tasks
[tasks.setup]
description = "Full project setup"
run = """
npm install
dotnet restore ./src/MyApp.sln
echo "Setup complete"
"""Task with Arguments
Arguments passed after -- are available as positional variables:
mise run greet -- Alice[tasks.greet]
run = "echo Hello, $1"---
Task Properties
| Property | Type | Description |
|---|---|---|
description | string | Human-readable summary shown in mise run --list |
run | string or array | Shell command(s) to execute |
depends | array | Task names that must complete before this task |
env | table | Extra environment variables for this task only |
dir | string | Working directory (default: project root) |
sources | array | File globs; task is skipped if sources unchanged |
outputs | array | File globs for output artifacts |
shell | string | Override the shell used (e.g., powershell, bash) |
hide | bool | Hide from mise run --list |
raw | bool | Stream output unbuffered (useful for interactive tasks) |
---
Task Dependencies
Use depends to declare task prerequisites. Mise runs independent dependencies in parallel by default.
[tasks.build]
run = "dotnet build ./src/MyApp.sln"
[tasks.frontend]
run = "npm run build --prefix ./src/Frontend"
[tasks.test]
description = "Run all tests"
depends = ["build", "frontend"] # runs build and frontend in parallel, then test
run = "dotnet test && npx playwright test"
[tasks.ci]
description = "Full CI pipeline"
depends = ["test", "lint"]
run = "echo CI complete"Sequential Dependencies
For dependencies that must run in order, use depends_post or chain with && inside run:
[tasks.release]
depends = ["test"] # test runs first
run = "cargo build --release && cargo publish"---
Running Tasks
# Run a task
mise run build
mise run test
mise run ci
# List all tasks with descriptions
mise run --list
mise tasks ls # alias
# Pass arguments to a task
mise run deploy -- --env production --force
# Run multiple tasks sequentially
mise run lint test build
# Run with verbose output
mise run --verbose build
# Dry run (show what would execute without running)
mise run --dry-run build---
File-based Tasks
Tasks can also be defined as standalone script files in a mise/tasks/ or .mise/tasks/ directory. Mise discovers these automatically.
project/
├── mise.toml
└── mise/
└── tasks/
├── build # executable shell script
├── test.py # Python script
└── deploy.ts # TypeScript script (requires deno or ts-node)File tasks are made executable and run directly. The description is read from the first comment line of the file:
#!/usr/bin/env bash
# Builds the production Docker image
set -euo pipefail
docker build -t myapp:latest .Use file tasks when:
- The task logic is complex (many lines)
- The task requires a specific language (Python, TypeScript)
- You want the task to be independently testable
---
Skipping Unchanged Tasks (Source Tracking)
Mise can skip task execution if source files haven't changed since the last run. Similar to make file tracking.
[tasks.build]
sources = ["src/**/*.rs", "Cargo.toml"]
outputs = ["target/release/myapp"]
run = "cargo build --release"Mise hashes the source files and only runs the task if they've changed.
---
Environment Variables in Tasks
Tasks inherit all environment variables from [env] in mise.toml. You can also add task-scoped variables:
[tasks.test-integration]
env = { TEST_DB_URL = "postgres://localhost/test_db", LOG_LEVEL = "debug" }
run = "pytest tests/integration"---
Monorepo Task Support
For monorepos, mise supports running tasks across sub-projects. Requires experimental = true in settings.
# root mise.toml
[settings]
experimental = trueMonorepo Task Globs
# Run 'build' task in a specific app
mise //apps/frontend:build
# Run 'test' task in all packages
mise //packages/*:test
# Run 'lint' everywhere
mise //...:lintPer-Package Task Definition
Each package has its own mise.toml with local task definitions:
monorepo/
├── mise.toml # root config (shared tools, root tasks)
├── apps/
│ └── frontend/
│ └── mise.toml # frontend-specific tasks
└── packages/
└── ui/
└── mise.toml # UI library tasks---
Usage: Tab Completion for Task Arguments
Mise bundles the Usage library for declaring task argument schemas, enabling shell tab-completion.
[tasks.deploy]
description = "Deploy to an environment"
usage = '''
flag "-f --force" help="Force deployment even if health checks fail"
flag "--dry-run" help="Print what would happen without deploying"
arg "<env>" choices="staging|production" help="Target environment"
'''
run = """
if [ "$usage_force" = "true" ]; then
echo "Force deploying to $usage_env"
fi
./scripts/deploy.sh "$usage_env"
"""Arguments are available as $usage_<name> variables in the run command. Flags become $usage_force, $usage_dry_run, etc.
Enable completions in shell:
# Add to shell profile
eval "$(mise completion zsh)"
# or
eval "$(mise completion bash)"---
Comparison with Alternatives
| Feature | mise tasks | Makefile | package.json scripts | just |
|---|---|---|---|---|
| Environment variable sharing | Yes (from mise.toml) | No | No | Partial |
| Dependency graph | Yes | Yes | No | Yes |
| Parallel execution | Yes (automatic) | Manual | No | Manual |
| Cross-platform | Yes | Partial | Yes | Yes |
| Tab completion | Yes (via Usage) | No | No | Yes |
| Language runtimes | Automatic | No | Manual | No |
| Monorepo support | Yes | No | Partial | No |