Mise en Place: The Art of Preparation
A practical walkthrough of the "holy grail" developer CLI, mise, and how it unifies tool version management, environment variables, and task running for a smoother dev experience
📑 Table of Contents
- The “holy grail” dev CLI, explained: a practical walkthrough of mise
- Why this class of tool keeps showing up
- What mise is trying to unify
- Walkthrough: setting up a repo with mise
- Step 3: Create a mise.toml and pin tool versions
- Step 4: Run commands with pinned versions
- Step 5: Put environment variables under version control (carefully)
- Step 6: Add tasks so everyone runs the same commands
- Step 7: Understand the trust prompt (this matters)
- Bonus: the “utilities like nix” angle
- A “full” example that looks like a real project
- My honest thoughts after digging in
- If you try it, start small
The “holy grail” dev CLI, explained: a practical walkthrough of mise
I ran into the YouTube video titled “The Holy Grail of Developer CLIs” from the DevOps Toolbox channel a little while ago, and it hit a nerve in the best way. (YouTube)
The vibe is simple: most of us already juggle a pile of small tools to make local dev feel “normal”. A version manager here, some .env plumbing there, a task runner duct-taped on top. The video’s pitch is that mise pulls that scattered setup into one place.
I’m going to walk through the same core idea the video spotlights, then layer in concrete config examples so you can try it in a real repo. Along the way, I’ll sprinkle in the thoughts I had while reading up on the docs and seeing how the pieces fit.
Why this class of tool keeps showing up
If you’ve ever cloned a repo and immediately hit:
- “You need Node 20, not Node 22”
- “Oh, you also need
terraform” - “Set
FOO_API_URLor nothing works” - “Run
make dev… unless you’re on Windows… unless you usejust… unless…”
…you already know the shape of the problem. Every team eventually invents a “developer experience stack”. It usually works, until it doesn’t. Then a new hire loses an afternoon to PATH issues.
What I liked about the video’s framing is that it doesn’t pretend we can avoid complexity. It argues we can centralize it.
That’s the whole hook of mise: it positions itself as the front-end for your dev environment. (GitHub)
What mise is trying to unify
The official description is almost cheeky because it maps to tools we all recognize:
- Like
asdf(ornvm,pyenv, and friends), it manages dev tool versions. - Like
direnv, it manages project-level environment variables. - Like
make, it manages tasks. (GitHub)
The video basically takes that mapping and says: “Stop stitching these together yourself.” (YouTube)
And yeah, that “one CLI to rule them all” idea sounds suspicious at first. I had the same reaction. I’ve been burned by “unifying tools” that become a new dependency you can’t escape.
What calmed me down here is that mise is doing fairly boring things, just in a coordinated way:
- Pin versions per directory.
- Export env vars when you enter that directory.
- Provide a task runner that can see the same tools and env vars.
Boring is good. Boring is dependable.
Walkthrough: setting up a repo with mise
This walkthrough assumes a single repo that needs:
- a pinned runtime
- a couple of CLI tools
- some env vars
- a task flow that everyone can run the same way
You can adapt it to almost anything.
Step 1: Install mise
The docs list several install methods (Homebrew, apt, dnf, pacman, Scoop, winget, and more). (mise-en-place)
One common quick start looks like this:
curl https://mise.run | sh
~/.local/bin/mise --versionThat command shows up in the project README too, along with the suggested shell hook. (GitHub)
Step 2: Hook it into your shell
To make per-directory switching work, you typically activate it in your shell profile:
# bash
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
# zsh
echo 'eval "$(~/.local/bin/mise activate zsh)"' >> ~/.zshrc(There are equivalent snippets for fish and PowerShell as well.) (GitHub)
This is the moment where I always pause and ask: “Am I about to let random repos execute stuff when I cd into them?” Good news: mise has an explicit trust model. We’ll get to that.
Step 3: Create a mise.toml and pin tool versions
In the root of your repo, create mise.toml:
# mise.toml
[tools]
node = "24"
python = "3.12"
terraform = "1"A few notes:
misesupports lots of tools and can switch versions based on the directory you’re in. (mise-en-place)- The main workflow command is
mise use. It installs (if needed) and writes the chosen version intomise.toml. (mise-en-place)
So instead of editing by hand, you can do:
mise use node@24
mise use python@3.12
mise use terraform@1Then:
mise installAt this point, the repo has a single source of truth for versions.
Here’s the little dopamine hit: you stop thinking “what version am I supposed to have installed?” and start thinking “the repo tells me”.
Step 4: Run commands with pinned versions
Two patterns are worth knowing:
Pattern A: normal commands after activation
Once mise is active in your shell, moving into the directory can switch tools for you. The dev tools docs describe that behavior explicitly. (mise-en-place)
Pattern B: one-off execution
There’s also an execution mode that installs missing tool versions on demand when you run a command through mise. (mise-en-place)
Example:
mise exec node@24 -- node -vThat style is nice in CI, or anytime you want to be explicit.
Step 5: Put environment variables under version control (carefully)
The environments docs are direct about the goal: per-directory env vars, similar to direnv. (mise-en-place)
Add an [env] section:
# mise.toml
[env]
NODE_ENV = "development"
API_URL = "http://localhost:3000"You can also unset an env var by setting it to false. (mise-en-place)
And there’s a CLI helper:
mise set API_URL=http://localhost:3000That command edits mise.toml for you by default. (mise-en-place)
Loading an existing .env file
If your project already uses dotenv files, you don’t have to throw that away. You can load .env from the [env] section:
[env]
_.file = ".env"That pattern is shown in guides and keeps compatibility with existing workflows. (Better Stack)
A quick reality check about secrets
My personal rule: do not casually commit secrets into mise.toml. Treat it like any other config file. Use a local override file, your secret manager, or a file that’s gitignored.
If you want the “team default” committed, keep it non-secret. If you need per-developer values, keep them local.
Step 6: Add tasks so everyone runs the same commands
This is where the video’s enthusiasm makes a lot of sense. The tasks doc calls out features that are usually painful when you roll your own:
- dependency building in parallel by default
- last-modified checks to avoid pointless rebuilds
mise watchfor rebuild-on-change- tasks as real script files (so you get syntax highlighting) (mise-en-place)
Option A: tasks in mise.toml
[tasks.dev]
description = "Start the dev server"
run = "node server.js"
[tasks.test]
description = "Run tests"
run = "node --test"
[tasks.lint]
description = "Lint"
run = "eslint ."Run them like this:
mise run dev
mise run testThe tasks doc also notes a shorter form: mise dev, as long as it doesn’t conflict with an existing mise subcommand. (mise-en-place)
Option B: “real files” task scripts
Create a directory like mise-tasks/ and drop executable scripts in it:
mkdir -p mise-tasksmise-tasks/dev:
#!/usr/bin/env bash
#MISE description="Start the dev server"
node server.jsMake it executable:
chmod +x mise-tasks/devThen:
mise run devSame invocation, nicer authoring experience. (mise-en-place)
Step 7: Understand the trust prompt (this matters)
This is the part I wish every “smart shell tool” made obvious.
mise trust exists because some config features can be dangerous. The command marks a config file as trusted so it can be parsed with those features enabled. (mise-en-place)
There’s also a “paranoid” mode described in the docs, where trust is required more broadly. (mise-en-place)
My takeaway: treat it like your editor’s workspace trust.
- Read the config first.
- Trust it after you understand what it’s doing.
- If you’re automating onboarding, document the trust step.
That small bit of friction is worth it.
Bonus: the “utilities like nix” angle
The YouTube snippet mentions “utilities like nix”, which is a fun rabbit hole if you’re in that world. (YouTube)
There’s a community plugin called mise-nix that installs packages via nix instead of the usual plugin flow. It’s explicitly aimed at Nix users who still want mise features, and it even supports pinning a nixpkgs ref. (GitHub)
Example from the plugin README:
mise plugin install https://github.com/chadac/mise-nix.git
mise install nix@python310
mise install nix@release-23.11/python310I wouldn’t start here unless your team is already comfortable with Nix, but it’s nice evidence that mise can stretch without becoming a monolith.
A “full” example that looks like a real project
Here’s a compact setup that combines versions, env vars, and tasks:
# mise.toml
[tools]
node = "24"
terraform = "1"
aws-cli = "2"
[env]
TF_WORKSPACE = "development"
AWS_REGION = "us-west-2"
_.file = ".env"
[tasks.plan]
description = "Terraform plan"
run = """
terraform init
terraform workspace select $TF_WORKSPACE
terraform plan
"""
[tasks.deploy]
description = "Apply infra"
depends = ["plan"]
run = "terraform apply -auto-approve"The official repo README includes a very similar combined example and suggests the same flow:
mise install
mise run deploy(GitHub)
This is the “I get it now” moment. You can hand someone the repo and say: “Install mise, trust the config, run the tasks.” The rest becomes repeatable.
My honest thoughts after digging in
I like the idea more than I expected to.
- The tool-version side is familiar. It feels like the natural evolution of “we all have 5 version managers installed”.
- The env var side is where teams get messy fast, and having a consistent pattern helps.
- The task runner is the surprise. The design choices feel opinionated in a good way, especially the “task scripts can be files” approach. (mise-en-place)
The main risk is social, not technical: adoption. Any time you introduce “one more tool”, someone will ask why you didn’t just keep using what you already have. The best answer is to show the repo getting simpler:
- fewer setup docs
- fewer local scripts
- fewer “works on my machine” gaps
If you can make that improvement obvious, the tool sells itself.
If you try it, start small
If I were rolling this into an existing project, I’d do it in this order:
- Add
mise.tomlwith tool versions. - Get the team comfortable with
mise install. - Move one or two common tasks into
mise run. - Only then touch env var management.
That keeps the change easy to review and easy to back out, if you hate it.
And if you end up loving it, you’ll understand why a video would call it the “holy grail” with a straight face. (YouTube)
Last updated: 2026-02-22