From mise-toolkit
The direnv vs mise [env] overlap story — when mise's built-in [env] is enough to replace direnv entirely, when to keep direnv running alongside mise via the use mise integration, and how to translate common .envrc patterns to mise.toml. Use when a user is currently using direnv and wants to consolidate or coexist with mise.
npx claudepluginhub ray-manaloto/claude-code-marketplace --plugin mise-toolkitThis skill uses the workspace's default tool permissions.
direnv is the classic per-directory env-var manager — it runs `.envrc` on `cd`, injects env vars into your shell, and unsets them when you leave. mise has a built-in `[env]` block that does most of the same things, plus tool version management. For many users, mise replaces direnv entirely; for others, the two coexist.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Share bugs, ideas, or general feedback.
direnv is the classic per-directory env-var manager — it runs .envrc on cd, injects env vars into your shell, and unsets them when you leave. mise has a built-in [env] block that does most of the same things, plus tool version management. For many users, mise replaces direnv entirely; for others, the two coexist.
| You use direnv for… | Verdict |
|---|---|
Just setting env vars (.envrc with export FOO=bar) | Replace with mise [env] |
| Setting env vars + activating a venv | Replace with _.python.venv |
Setting env vars + sourcing .env files | Replace with _.file = ".env" |
use flake (Nix integration) | Coexist — mise doesn't do Nix |
use node / use python (version switching) | Replace — that's what mise is for |
Complex shell logic in .envrc (functions, conditionals) | Coexist or replace case-by-case |
dotenv_if_exists, watch_file, path_add | Replace — mise has all of these |
| Secrets from keychain via a helper | Coexist if the helper is direnv-specific, or replace by calling the helper from [hooks] enter |
Most users can replace direnv entirely. The exceptions are Nix-flake shops and people with heavily custom .envrc logic.
.envrc → mise.toml translation cheat sheet# .envrc
export DATABASE_URL=postgres://localhost/myapp
export LOG_LEVEL=debug
export PORT=3000
# mise.toml
[env]
DATABASE_URL = "postgres://localhost/myapp"
LOG_LEVEL = "debug"
PORT = "3000"
.env file# .envrc
dotenv
# or
dotenv_if_exists .env
# mise.toml
[env]
_.file = ".env"
_.file reads a dotenv-format file and loads its variables. Error if missing, unless:
[env]
_.file = { path = ".env", optional = true }
# .envrc
PATH_add node_modules/.bin
PATH_add bin
# mise.toml
[env]
_.path = ["node_modules/.bin", "bin"]
Paths are resolved relative to the mise.toml location.
# .envrc
layout python3
# or
source .venv/bin/activate
# mise.toml
[env]
_.python.venv = { path = ".venv", create = true }
The create = true flag auto-creates the venv if missing, using the mise-pinned Python.
# .envrc
watch_file Gemfile.lock
In mise, files that affect config are auto-reloaded — mise.toml, mise.lock, and any _.file sources are watched automatically. For explicit task-level watches, use [tasks.<name>].sources.
# .envrc
if [[ "$(uname)" == "Darwin" ]]; then
export CFLAGS="-I$(brew --prefix openssl)/include"
fi
mise supports tera templates in env values:
[env]
CFLAGS = "{{ exec(command='brew --prefix openssl') | trim }}/include"
Or, for more complex logic, shell out via [hooks] enter:
[hooks]
enter = "./scripts/setup-env.sh"
Most .envrc files don't actually need complex logic — the conditional was usually copied from a template. Try simplifying first.
.envrc to a mise.toml [env] block.eval "$(direnv hook zsh)" from shell rc.eval "$(mise activate zsh)" (if not already there)..envrc files after confirming mise env shows the right vars in each project.brew uninstall direnv or equivalent.If you have Nix flakes, complex .envrc logic, or want mise for tools-only:
# .envrc — keep this
use mise # tells direnv to load mise's env
use flake # your Nix integration
direnv's use mise function (from mise activate's output or the direnv-mise plugin) loads mise's tools and env into direnv's sandbox. mise handles tool versions; direnv handles the nix-flake layer on top.
Order in shell rc:
# ~/.zshrc
eval "$(mise activate zsh)" # mise first
eval "$(direnv hook zsh)" # direnv after
mise provides tools; direnv adds more env on top via .envrc.
[env] is usually enough[env] in mise.toml is plain TOML; no shell to debug..envrc required direnv allow for the same reason._.python.venv, _.file, _.path are purpose-built for common cases.mise.toml holds tools + env + tasks. .envrc only held env..envrc just to avoid "the migration effort" — it's usually 5 minutes per project.use node in .envrc when mise is already managing Node. Redundant..envrc or mise.toml — use a secret manager + _.file pointing at a gitignored dotenv.Reinstall direnv, restore .envrc from git, re-add direnv hook to shell rc.
brew install direnv
git checkout .envrc
mise-env-directives — the full [env] reference (_.file, _.path, _.python.venv, _.python.uv_venv_auto, _.source).mise-lang-python-overview — _.python.venv depth.mise-trust-and-security — mise's trust system and why it exists.mise-migrate-from-asdf — general migration shape.direnv.net.