
Mise Configuration
- 151 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
Use mise-configuration for development tasks
About
mise-configuration: A skill for development. This provides functionality for development workflows.
- mise-configuration
Mise Configuration by the numbers
- 151 all-time installs (skills.sh)
- +3 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #2,506 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill mise-configurationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 151 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
What it does
Use mise-configuration for development tasks
Files
mise Configuration as Single Source of Truth
Use mise [env] as centralized configuration with backward-compatible defaults.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
When to Use This Skill
Use this skill when:
- Centralizing environment variables in mise.toml
- Setting up Python venv auto-creation with mise
- Implementing hub-spoke configuration for monorepos
- Creating backward-compatible environment patterns
Core Principle
Define all configurable values in .mise.toml [env] section. Scripts read via environment variables with fallback defaults. Same code path works WITH or WITHOUT mise installed.
Key insight: mise auto-loads [env] values when shell has mise activate configured. Scripts using os.environ.get("VAR", "default") pattern work identically whether mise is present or not.
Quick Reference
Language Patterns
| Language | Pattern | Notes |
|---|---|---|
| Python | os.environ.get("VAR", "default") | Returns string, cast if int |
| Bash | ${VAR:-default} | Standard POSIX expansion |
| JavaScript | `process.env.VAR \ | \ |
| Go | os.Getenv("VAR") with default | Empty string if unset |
| Rust | std::env::var("VAR").unwrap_or() | Returns Result<String> |
Special Directives
| Directive | Purpose | Example |
|---|---|---|
_.file | Load from .env files | _.file = ".env" |
_.path | Extend PATH | _.path = ["bin", "node_modules/.bin"] |
_.source | Execute bash scripts | _.source = "./scripts/env.sh" |
_.python.venv | Auto-create Python venv | _.python.venv = { path = ".venv", create = true } |
For detailed directive examples with options (redact, tools, multi-file): Code Patterns
Python Venv Auto-Creation (Critical)
Auto-create and activate Python virtual environments:
[env]
_.python.venv = { path = ".venv", create = true }This pattern is used in ALL projects. When entering the directory with mise activated:
1. Creates .venv if it doesn't exist 2. Activates the venv automatically 3. Works with uv for fast venv creation
Alternative via [settings]:
[settings]
python.uv_venv_auto = trueHub-Spoke Architecture (CRITICAL)
Keep root mise.toml lean by delegating domain-specific tasks to subfolder mise.toml files. Applies to monorepos, ML/research projects, infrastructure, and data pipelines.
Key rules:
- Hub owns
[tools]and orchestration tasks - Spokes inherit hub's
[tools]automatically - Spoke
[env]extends hub's[env](can override per domain) .mise.local.tomlapplies at directory level (secrets stay local)
Full guide with directory structures, examples, and anti-patterns: Hub-Spoke Architecture
Wiki Reference: Pattern-mise-Configuration
Monorepo Workspace Pattern
For Python monorepos using uv workspaces, the venv is created at the workspace root. Dev dependencies should be hoisted to root pyproject.toml using [dependency-groups] (PEP 735).
Full guide: Monorepo Workspace Pattern
Template Syntax (Tera)
mise uses Tera templating. Delimiters: {{ }} expressions, {% %} statements, {# #} comments.
Built-in Variables
| Variable | Description |
|---|---|
{{config_root}} | Directory containing .mise.toml |
{{cwd}} | Current working directory |
{{env.VAR}} | Environment variable |
{{mise_bin}} | Path to mise binary |
{{mise_pid}} | mise process ID |
{{xdg_cache_home}} | XDG cache directory |
{{xdg_config_home}} | XDG config directory |
{{xdg_data_home}} | XDG data directory |
For functions (get_env, exec, arch, read_file, hash_file), filters (snakecase, trim, absolute), and conditionals: Code Patterns - Template Syntax
Required & Redacted Variables
[env]
# Required - fails if not set
DATABASE_URL = { required = true }
API_KEY = { required = "Get from https://example.com/api-keys" }
# Redacted - hides from output
SECRET = { value = "my_secret", redact = true }
_.file = { path = ".env.secrets", redact = true }
# Pattern-based redactions
redactions = ["*_TOKEN", "*_KEY", "PASSWORD"]For combined patterns and detailed examples: Code Patterns - Required & Redacted
Lazy Evaluation (tools = true)
By default, env vars resolve BEFORE tools install. Use tools = true to access tool-generated paths:
[env]
GEM_BIN = { value = "{{env.GEM_HOME}}/bin", tools = true }
_.file = { path = ".env", tools = true }[settings] and [tools]
[settings]
experimental = true
python.uv_venv_auto = true
[tools]
python = "<version>"
node = "latest"
uv = "latest"
rust = { version = "<version>", profile = "minimal" }
# SSoT-OK: mise min_version directive, not a package version
min_version = "2024.9.5"For full settings reference and version pinning options: Code Patterns - Settings & Tools
Implementation Steps
1. Identify hardcoded values - timeouts, paths, thresholds, feature flags 2. Create `.mise.toml` - add [env] section with documented variables 3. Add venv auto-creation - _.python.venv = { path = ".venv", create = true } 4. Update scripts - use env vars with original values as defaults 5. Add ADR reference - comment: # ADR: 2025-12-08-mise-env-centralized-config 6. Test without mise - verify script works using defaults 7. Test with mise - verify activated shell uses .mise.toml values
GitHub Token Multi-Account Patterns {#github-token-multi-account-patterns}
mise does NOT manage GitHub tokens (ADR 2026-06-21). Per-directory GH_TOKENinjection via mise [env] is retired. GitHub identity is driven by the repo'soriginhost-alias (git@github.com-<account>:owner/repo): SSH key, commit
identity (includeIf hasconfig:remote.*.url), and gh account all derive from it.Theghwrapper in~/.zshrcstrips any ambientGH_TOKEN. When a script needs a
token, resolve it fresh: GH_PAT="$(~/.claude/tools/bin/gh-token-for-repo)".Full guide: GitHub Multi-Account Auth (host-alias model)
Anti-Patterns
| Anti-Pattern | Why | Instead |
|---|---|---|
mise exec -- script.py | Forces mise dependency | Use env vars with defaults |
Secrets in .mise.toml | Visible in repo | Use Doppler or redact = true |
| No defaults in scripts | Breaks without mise | Always provide fallback |
[env] secrets for pueue jobs | Pueue runs clean shell, no mise | Use python-dotenv + .env file |
__MISE_DIFF leaks via SSH | Remote trust errors | unset __MISE_DIFF before SSH |
Critical detail on non-interactive shell secrets: Anti-Patterns Guide
Task Orchestration Integration
When detecting multi-step project workflows during mise configuration, invoke the mise-tasks skill for task definitions with dependency management.
Detection triggers: multi-step workflows, repeatable commands, dependency chains, file-tracked builds.
Full guide with examples: Task Orchestration | mise-tasks skill
---
Additional Resources
- [Code Patterns & Templates](./references/patterns.md) - Complete code examples for Python, Bash, JS, Go, Rust, and full
.mise.tomltemplate - [Hub-Spoke Architecture](./references/hub-spoke-architecture.md) - Directory structures, hub/spoke responsibilities, inheritance rules
- [GitHub Token Patterns](./references/github-tokens.md) - Multi-account setup, verification, 1Password integration
- [Anti-Patterns Guide](./references/anti-patterns.md) - Non-interactive shell secrets, pueue/cron/systemd gotchas
- [Task Orchestration](./references/task-orchestration.md) - Workflow detection triggers, environment-to-tasks example
- [Monorepo Workspace](./references/monorepo-workspace.md) - uv workspaces, hoisted dev dependencies (PEP 735)
- Wiki: Pattern-mise-Configuration
ADR Reference: When implementing mise configuration, create an ADR at docs/adr/YYYY-MM-DD-mise-env-centralized-config.md in your project.
---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Env vars not loading | mise not activated | Add mise activate to shell rc file |
| Venv not created | Python not installed | Run mise install python |
| Tasks not found | Wrong mise.toml location | Ensure mise.toml is in project root |
| PATH not updated | Shims not in PATH | Add mise shims to ~/.zshenv |
| \.file not loading | .env file missing | Create .env file or remove \.file directive |
| Subfolder config ignored | Missing min_version | Add min_version to subfolder mise.toml |
Post-Execution Reflection
After this skill completes, check before closing:
1. Did the command succeed? — If not, fix the instruction or error table that caused the failure. 2. Did parameters or output change? — If the underlying tool's interface drifted, update Usage examples and Parameters table to match. 3. Was a workaround needed? — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.
Only update if the issue is real and reproducible — not speculative.
mise Configuration Anti-Patterns
General Anti-Patterns
| Anti-Pattern | Why | Instead |
|---|---|---|
mise exec -- script.py | Forces mise dependency | Use env vars with defaults |
Secrets in .mise.toml | Visible in repo | Use Doppler or redact = true |
| No defaults in scripts | Breaks without mise | Always provide fallback |
| Mixing env/tools resolution | Order matters | Use tools = true for tool-dependent vars |
[env] secrets for pueue jobs | Pueue runs clean shell, no mise | Use python-dotenv + .env file |
__MISE_DIFF leaks via SSH | Remote trust errors | unset __MISE_DIFF before SSH |
Critical: mise [env] Secrets and Non-Interactive Shells
Do NOT put secrets in mise.toml [env] if they will be consumed by pueue jobs, cron jobs, or systemd services. These execution contexts run in clean shells without mise activation — [env] variables are invisible.
Preferred pattern: Use mise.toml for task definitions and non-secret configuration only. Put secrets in a .env file (gitignored) and load them with python-dotenv at runtime:
# mise.toml — tasks only, no secrets in [env]
[env]
DATABASE_NAME = "mydb" # OK: non-secret defaults
[tasks.backfill]
run = "bash scripts/backfill.sh"# .env (gitignored) — secrets loaded by python-dotenv
API_KEY=sk-abc123
DATABASE_PASSWORD=hunter2This works identically in interactive shells, pueue jobs, cron, systemd, and across macOS/Linux.
Cross-reference: See devops-tools:distributed-job-safety — G-15, AP-16
Evolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---
GitHub Multi-Account Auth (host-alias model)
Parent Skill: mise-configuration
⚠️ 2026-06-21 — this page was rewritten. The previous version taught mise
[env]GH_TOKENinjection from plaintext~/.claude/.secrets/gh-token-*
files and cwd-based SSH Match directives. All of that is retired. mise nolonger touches GitHub tokens (it manages tool versions only). The canonical
model below is the host-alias single-source-of-truth. See the ADR:
~/.claude/docs/adr/2025-12-17-github-multi-account-authentication.md (§2026-06-21).The model: the remote URL host-alias is the single source of truth
A repo's origin remote names its account, and that one signal drives all three layers — so identity travels with the repo, not its folder location.
git@github.com-<account>:owner/repo.git
^^^^^^^^^ the account| Layer | Mechanism (keyed off the alias) |
|---|---|
| SSH key | Host github.com-<account> block in ~/.ssh/config → IdentityFile, with IdentitiesOnly yes + ControlMaster no + ControlPath none. A top-of-file Host github.com github.com-* ssh.github.com block with ControlPath none is the multiplexing kill-switch (defeats the wrong-identity bug). |
| Commit identity | ~/.gitconfig: [includeIf "hasconfig:remote.*.url:git@github.com-<account>:*/**"] path = ~/.gitconfig-<account> — binds identity to the remote, not the folder (git ≥ 2.36). |
| gh CLI | The neutral gh wrapper in ~/.zshrc derives the account from the remote alias → GH_CONFIG_DIR=~/.config/gh-<account> (isolated profile) and strips `GH_TOKEN` (a stale ambient token would otherwise outrank the profile and 401 after a rotation). |
Tokens are resolved fresh — never stored or injected
There are no plaintext token files and no ambient GH_TOKEN. Anything that needs an HTTP token (semantic-release, CI scripts) resolves one at the moment of use:
# Account derived from the repo's origin alias → that account's gh profile → fresh token
GH_PAT="$(~/.claude/tools/bin/gh-token-for-repo)"
GITHUB_TOKEN="$GH_PAT" GH_TOKEN="$GH_PAT" npx semantic-releasegh-token-for-repo runs GH_CONFIG_DIR=~/.config/gh-<account> gh auth token (verified to work headless under launchd with no Touch ID). 1Password is the at-rest SSoT (each account's gh login is provisioned from it; ~/.gitconfig-<account> references githubToken1PasswordID).
Account → alias → key map
| Account | Remote alias | SSH key | gh profile |
|---|---|---|---|
| terrylica | github.com-terrylica | id_ed25519_terrylica | ~/.config/gh-terrylica |
| tainora | github.com-tainora | id_ed25519_tainora | ~/.config/gh-tainora |
| 459ecs | github.com-459ecs | id_ed25519_459ecs | ~/.config/gh-459ecs |
| vanjobbers | github.com-vanjobbers | id_ed25519_vanjobbers | ~/.config/gh-vanjobbers |
| Eon-Labs org | github.com-eonlabs | id_ed25519_terrylica (member) | ~/.config/gh-terrylica |
Verification
# In any repo: account is whatever the origin alias names
git -C <repo> remote get-url origin # → git@github.com-<account>:...
~/.claude/tools/bin/gh-token-for-repo | GH_TOKEN=$(cat) gh api user --jq .login # → <account>RETIRED — do NOT reintroduce
- mise
[env]injectingGH_TOKEN/GITHUB_TOKEN/GH_CONFIG_DIR/GH_ACCOUNT
(including via read_file(... .secrets/gh-token-*) or op read — any ambient token is the failure mode).
- plaintext
~/.claude/.secrets/gh-token-*files (deleted). - the
git-credential-gh-tokenhelper; "HTTPS-first" git remotes. - cwd-based
Match host github.com exec "pwd | grep ..."SSH directives. includeIf "gitdir:..."account selection (replaced byhasconfig:remote.*.url).- the
~/.config/gh-profiles/*and~/.config/gh/profiles/*conventions (deleted).
References
- ADR:
~/.claude/docs/adr/2025-12-17-github-multi-account-authentication.md(§2026-06-21) - Resolver:
~/.claude/tools/bin/gh-token-for-repo
Hub-Spoke Architecture for mise Configuration
Keep root mise.toml lean by delegating domain-specific tasks to subfolder mise.toml files.
Wiki Reference: Pattern-mise-Configuration - Complete documentation with CLAUDE.md footer prompt
When to Use
- Root
mise.tomlexceeds ~50 lines - Project has multiple domains (packages, experiments, infrastructure)
- Different subfolders need different task sets
Spoke Scenarios
Hub-spoke applies to any multi-domain project, not just packages:
| Scenario | Spoke Folders | Spoke Tasks |
|---|---|---|
| Monorepo | packages/api/, packages/web/ | build, test, lint, deploy |
| ML/Research | experiments/exp-001/, training/, evaluation/ | train, evaluate, notebook, sweep |
| Infrastructure | terraform/, kubernetes/, ansible/ | plan, apply, deploy, validate |
| Data Pipeline | ingestion/, transform/, export/ | extract, load, validate, export |
Directory Structure Examples
Monorepo:
project/
├── mise.toml # Hub: [tools] + [env] + orchestration
├── packages/
│ ├── api/mise.toml # Spoke: API tasks
│ └── web/mise.toml # Spoke: Web tasks
└── scripts/mise.toml # Spoke: Utility scriptsML/Research Project:
ml-project/
├── mise.toml # Hub: python, cuda, orchestration
├── experiments/
│ ├── baseline/mise.toml # Spoke: baseline experiment
│ └── ablation/mise.toml # Spoke: ablation study
├── training/mise.toml # Spoke: training pipelines
└── evaluation/mise.toml # Spoke: metrics, benchmarksInfrastructure:
infra/
├── mise.toml # Hub: terraform, kubectl, helm
├── terraform/
│ ├── prod/mise.toml # Spoke: production infra
│ └── staging/mise.toml # Spoke: staging infra
└── kubernetes/mise.toml # Spoke: k8s manifestsHub Responsibilities (Root mise.toml)
# mise.toml - Hub: Keep this LEAN
[tools]
python = "<version>"
uv = "latest"
[env]
PROJECT_NAME = "my-project"
_.python.venv = { path = ".venv", create = true }
# Orchestration: delegate to spokes
[tasks.train-all]
run = """
cd experiments/baseline && mise run train
cd experiments/ablation && mise run train
"""
[tasks."build:api"]
run = "cd packages/api && mise run build"Spoke Responsibilities (Subfolder mise.toml)
# experiments/baseline/mise.toml - Spoke
[env]
EXPERIMENT_NAME = "baseline"
EPOCHS = "<num>" # e.g., 100
LEARNING_RATE = "<float>" # e.g., 0.001
[tasks.train]
run = "uv run python train.py"
sources = ["*.py", "config.yaml"]
outputs = ["checkpoints/*.pt"]
[tasks.evaluate]
depends = ["train"]
run = "uv run python evaluate.py"Inheritance Rules
- Spoke
mise.tomlinherits hub's[tools]automatically - Spoke
[env]extends hub's[env](can override per domain) .mise.local.tomlapplies at directory level (secrets stay local)
Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|---|---|
| All tasks in root | Root grows to 200+ lines | Delegate to spoke files |
| Duplicated [tools] | Version drift between spokes | Define [tools] only in hub |
| Spoke defines runtimes | Conflicts with hub | Spokes inherit hub's [tools] |
| No orchestration | Must cd manually | Hub orchestrates spoke tasks |
Monorepo Workspace Pattern
For Python monorepos using uv workspaces, the venv is created at the workspace root. Sub-packages share the root venv.
# Root mise.toml
[env]
_.python.venv = { path = ".venv", create = true }Hoisted Dev Dependencies (PEP 735)
Dev dependencies (pytest, ruff, jupyterlab, etc.) should be hoisted to workspace root pyproject.toml using [dependency-groups]:
# SSoT-OK: example workspace configuration
# Root pyproject.toml
[tool.uv.workspace]
members = ["packages/*"]
[dependency-groups]
dev = [
"pytest>=<version>",
"ruff>=<version>",
"jupyterlab>=<version>",
]Why hoist? Sub-package [dependency-groups] are NOT automatically installed by uv sync from root. Hoisting ensures:
- Single command:
uv sync --group dev - No "unnecessary package" warnings
- Unified dev environment across all packages
Reference: bootstrap-monorepo.md for complete workspace setup
mise [env] Code Patterns
Table of Contents
- Python Venv Auto-Creation
- Basic Pattern
- With uv Auto-Venv via Settings
- Project Template with Venv
- Special Directives
- Load from .env Files (`_.file`)
- Extend PATH (`_.path`)
- Source Bash Scripts (`_.source`)
- Complete Special Directives Example
- Template Syntax (Tera)
- Built-in Variables
- Functions
- Filters
- Conditionals
- Complete Template Example
- Required & Redacted Variables
- Required Variables
- Redacted Variables
- Combined Patterns
- [[settings] Section](#settings-section)
- Python Development Setup
- [[tools] Version Pinning](#tools-version-pinning)
- Basic Pinning
- With Options
- min_version Enforcement
- Full Development Environment
- Python Pattern
- Bash Pattern
- JavaScript/Node.js Pattern
- Go Pattern
- Rust Pattern
- Complete .mise.toml Template
- Real-World Examples
- Testing Pattern
- Migration Checklist
Complete code patterns for implementing mise [env] configuration with backward-compatible defaults.
Python Venv Auto-Creation
The most critical mise pattern - auto-create and activate Python virtual environments:
Basic Pattern
# .mise.toml
[env]
_.python.venv = { path = ".venv", create = true }What it does:
1. Creates .venv if it doesn't exist when entering directory 2. Automatically activates the venv 3. Works with uv for fast venv creation
With uv Auto-Venv via Settings
# .mise.toml
[settings]
python.uv_venv_auto = true
[tools]
python = "3.11" # baseline >=3.11; pin to project needs
uv = "latest"Project Template with Venv
# .mise.toml - Python project with auto-venv
[env]
_.python.venv = { path = ".venv", create = true }
PYTHONUNBUFFERED = "1"
PYTHONDONTWRITEBYTECODE = "1"
[tools]
python = "3.11" # baseline >=3.11; pin to project needs
uv = "latest"Special Directives
Load from .env Files (_.file)
[env]
# Single .env file
_.file = ".env"
# Multiple files with options
_.file = [
".env",
".env.local",
{ path = ".env.secrets", redact = true }
]
# Load after tools are installed
_.file = { path = ".env", tools = true }Use case: Load existing .env files without duplicating values in .mise.toml.
Extend PATH (_.path)
[env]
# Add project directories to PATH
_.path = [
"{{config_root}}/bin",
"{{config_root}}/scripts",
"node_modules/.bin"
]Use case: Make project scripts and tool binaries available without full path.
Source Bash Scripts (_.source)
[env]
# Simple script
_.source = "./scripts/env.sh"
# With secret redaction
_.source = { path = ".secrets.sh", redact = true }Use case: Complex environment setup that requires bash logic.
Complete Special Directives Example
# .mise.toml - Full-featured project setup
[env]
# 1. Auto-create Python venv
_.python.venv = { path = ".venv", create = true }
# 2. Load .env files
_.file = [
".env",
{ path = ".env.local", redact = true }
]
# 3. Extend PATH
_.path = [
"{{config_root}}/bin",
"{{config_root}}/scripts"
]
# 4. Project configuration
PROJECT_NAME = "my-project"
LOG_LEVEL = "info"Template Syntax (Tera)
mise uses Tera templating engine. Reference for common patterns:
Built-in Variables
[env]
# Directory paths
PROJECT_ROOT = "{{config_root}}" # .mise.toml directory
CURRENT_DIR = "{{cwd}}" # Current working directory
# XDG directories
CACHE = "{{xdg_cache_home}}/myapp"
CONFIG = "{{xdg_config_home}}/myapp"
DATA = "{{xdg_data_home}}/myapp"
# mise info
MISE_BIN = "{{mise_bin}}"
MISE_PID = "{{mise_pid}}"Functions
[env]
# Get env var with fallback
NODE_VER = "{{ get_env(name='NODE_VERSION', default='20') }}"
# Execute shell command
BUILD_TIME = "{{ exec(command='date +%Y-%m-%d') }}"
GIT_SHA = "{{ exec(command='git rev-parse --short HEAD') }}"
# System info
ARCH = "{{ arch() }}" # x64, arm64
OS = "{{ os() }}" # linux, macos, windows
CPUS = "{{ num_cpus() }}"
OS_FAMILY = "{{ os_family() }}" # unix, windows
# File operations
VERSION = "{{ read_file(path='VERSION') | trim }}"
CONFIG_HASH = "{{ hash_file(path='config.json', len=8) }}"
# Directory check
{% if is_dir("src") %}
SRC_EXISTS = "true"
{% endif %}Filters
[env]
# Case conversion
SNAKE_NAME = "{{ project_name | snakecase }}" # my_project
KEBAB_NAME = "{{ project_name | kebabcase }}" # my-project
CAMEL_NAME = "{{ project_name | lowercamelcase }}" # myProject
PASCAL_NAME = "{{ project_name | uppercamelcase }}" # MyProject
# String manipulation
CLEAN = "{{ raw_value | trim }}"
UPPER = "{{ name | upper }}"
LOWER = "{{ name | lower }}"
REPLACED = "{{ text | replace(from='-', to='_') }}"
# Path operations
ABS_PATH = "{{ relative_path | absolute }}"
FILE_NAME = "{{ full_path | basename }}"
DIR_NAME = "{{ full_path | dirname }}"
FILE_STEM = "{{ full_path | file_stem }}" # without extension
EXTENSION = "{{ full_path | file_extension }}"
# String utilities
QUOTED = "{{ value | quote }}"
LAST_ITEM = "{{ list | last }}"
FIRST_ITEM = "{{ list | first }}"Conditionals
[env]
{% if env.CI %}
# CI-specific settings
LOG_LEVEL = "error"
PARALLEL = "{{ num_cpus() }}"
{% else %}
# Local development
LOG_LEVEL = "debug"
PARALLEL = "2"
{% endif %}
{% if os() == "macos" %}
BREW_PREFIX = "/opt/homebrew"
{% elif os() == "linux" %}
BREW_PREFIX = "/home/linuxbrew/.linuxbrew"
{% endif %}Complete Template Example
# .mise.toml - Template-heavy configuration
[env]
# Computed paths
PROJECT_ROOT = "{{config_root}}"
BUILD_DIR = "{{config_root}}/build/{{ os() }}-{{ arch() }}"
CACHE_DIR = "{{xdg_cache_home}}/{{ cwd | basename }}"
# Git-derived values
GIT_BRANCH = "{{ exec(command='git branch --show-current') | trim }}"
GIT_SHA = "{{ exec(command='git rev-parse --short HEAD') | trim }}"
VERSION = "{{ read_file(path='VERSION') | trim | default(value='0.0.0') }}"
# Platform-specific
{% if os() == "macos" %}
DYLD_LIBRARY_PATH = "{{config_root}}/lib"
{% else %}
LD_LIBRARY_PATH = "{{config_root}}/lib"
{% endif %}
# Environment-aware
{% if get_env(name='CI', default='false') == 'true' %}
LOG_LEVEL = "error"
PARALLEL_JOBS = "{{ num_cpus() }}"
{% else %}
LOG_LEVEL = "debug"
PARALLEL_JOBS = "4"
{% endif %}Required & Redacted Variables
Required Variables
[env]
# Simple required - fails if not set
DATABASE_URL = { required = true }
# Required with help message
API_KEY = { required = "Get your API key from https://example.com/settings" }
GITHUB_TOKEN = { required = "Run: gh auth token" }Behavior: mise shows error and refuses to activate if required variable is unset.
Redacted Variables
[env]
# Redact specific variable
SECRET_KEY = { value = "{{ exec(command='op read op://vault/item/password') }}", redact = true }
# Redact entire .env file
_.file = { path = ".env.secrets", redact = true }
# Pattern-based redactions (hides in `mise env` output)
redactions = ["*_TOKEN", "*_KEY", "*_SECRET", "PASSWORD", "CREDENTIAL"]Combined Patterns
[env]
# Public configuration
LOG_LEVEL = "info"
OUTPUT_DIR = "output"
# Required with help
DOPPLER_PROJECT = { required = "Set your Doppler project name" }
# Secrets from external sources (redacted)
API_KEY = { value = "{{ exec(command='doppler secrets get API_KEY --plain') }}", redact = true }
# Pattern-based redaction for anything else
redactions = ["*_TOKEN", "*_KEY"][settings] Section
Configure mise behavior:
[settings]
# Enable experimental features
experimental = true
# Python-specific
python.uv_venv_auto = true # Auto-create venv with uv
python.default_packages_file = ".default-python-packages"
# Node.js-specific
node.default_packages_file = ".default-npm-packages"
# Task runner
task.auto_install = true # Auto-install task dependencies
# General
always_keep_download = false
always_keep_install = false
verbose = falsePython Development Setup
[settings]
experimental = true
python.uv_venv_auto = true
[tools]
python = "3.11" # baseline >=3.11; pin to project needs
uv = "latest"
[env]
PYTHONUNBUFFERED = "1"[tools] Version Pinning
Pin tool versions for reproducibility:
Basic Pinning
[tools]
python = "3.11" # baseline >=3.11; pin to project needs
node = "latest"
uv = "latest"
rust = "1.75"With Options
[tools]
# Specific version
python = "3.12.3"
# Version prefix (latest 3.12.x)
python = "3.11" # baseline >=3.11; pin to project needs
# Latest
uv = "latest"
# With backend options
rust = { version = "1.75", profile = "minimal" }
# Multiple versions (first is default)
node = ["22", "20", "18"]min_version Enforcement
# Require minimum mise version
min_version = "2024.9.5"
[tools]
python = "3.11" # baseline >=3.11; pin to project needsFull Development Environment
# .mise.toml - Complete development environment
min_version = "2024.9.5"
[settings]
experimental = true
python.uv_venv_auto = true
[tools]
python = "3.11" # baseline >=3.11; pin to project needs
node = "latest"
uv = "latest"
rust = "1.75"
[env]
_.python.venv = { path = ".venv", create = true }
_.path = ["{{config_root}}/bin", "node_modules/.bin"]
PYTHONUNBUFFERED = "1"
NODE_ENV = "development"Python Pattern
#!/usr/bin/env python3
"""Example script with mise [env] configuration."""
import os
# ADR: 2025-12-08-mise-env-centralized-config
# Configuration from environment with defaults
TIMEOUT = int(os.environ.get("SCRIPT_TIMEOUT", "300"))
OUTPUT_DIR = os.environ.get("OUTPUT_DIR", "output")
PARALLEL_WORKERS = int(os.environ.get("PARALLEL_WORKERS", "4"))
DEBUG_MODE = os.environ.get("DEBUG_MODE", "false").lower() == "true"
def main():
print(f"Running with timeout={TIMEOUT}, workers={PARALLEL_WORKERS}")
# ... script logic
if __name__ == "__main__":
main()Key points:
- Import
osat top - Define constants immediately after imports
- Cast to int/bool as needed (env vars are always strings)
- Use descriptive variable names matching .mise.toml
Bash Pattern
/usr/bin/env bash << 'CONFIG_EOF'
#!/usr/bin/env bash
set -euo pipefail
# ADR: 2025-12-08-mise-env-centralized-config
# Configuration from environment with defaults
SCRIPT_TIMEOUT="${SCRIPT_TIMEOUT:-300}"
OUTPUT_DIR="${OUTPUT_DIR:-output}"
PARALLEL_WORKERS="${PARALLEL_WORKERS:-4}"
DEBUG_MODE="${DEBUG_MODE:-false}"
main() {
echo "Running with timeout=$SCRIPT_TIMEOUT, workers=$PARALLEL_WORKERS"
# ... script logic
}
main "$@"
CONFIG_EOFKey points:
- Use
${VAR:-default}POSIX syntax - Define after shebang and set options
- No export needed - variables are local to script
- For boolean checks:
[[ "$DEBUG_MODE" == "true" ]]
JavaScript/Node.js Pattern
#!/usr/bin/env node
/**
* Example script with mise [env] configuration.
*/
// ADR: 2025-12-08-mise-env-centralized-config
// Configuration from environment with defaults
const TIMEOUT = parseInt(process.env.SCRIPT_TIMEOUT || "300", 10);
const OUTPUT_DIR = process.env.OUTPUT_DIR || "output";
const PARALLEL_WORKERS = parseInt(process.env.PARALLEL_WORKERS || "4", 10);
const DEBUG_MODE = process.env.DEBUG_MODE === "true";
async function main() {
console.log(`Running with timeout=${TIMEOUT}, workers=${PARALLEL_WORKERS}`);
// ... script logic
}
main().catch(console.error);Key points:
- Use
process.env.VAR || "default"pattern - parseInt with radix 10 for numbers
- Boolean: strict equality check
=== "true" - Watch for falsy "0" - use
?? "default"if "0" is valid
Go Pattern
package main
import (
"fmt"
"os"
"strconv"
)
// ADR: 2025-12-08-mise-env-centralized-config
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func getEnvInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if i, err := strconv.Atoi(value); err == nil {
return i
}
}
return defaultValue
}
var (
Timeout = getEnvInt("SCRIPT_TIMEOUT", 300)
OutputDir = getEnv("OUTPUT_DIR", "output")
ParallelWorkers = getEnvInt("PARALLEL_WORKERS", 4)
)
func main() {
fmt.Printf("Running with timeout=%d, workers=%d\n", Timeout, ParallelWorkers)
}Rust Pattern
use std::env;
// ADR: 2025-12-08-mise-env-centralized-config
fn get_env_or(key: &str, default: &str) -> String {
env::var(key).unwrap_or_else(|_| default.to_string())
}
fn get_env_int(key: &str, default: i32) -> i32 {
env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn main() {
let timeout = get_env_int("SCRIPT_TIMEOUT", 300);
let output_dir = get_env_or("OUTPUT_DIR", "output");
let workers = get_env_int("PARALLEL_WORKERS", 4);
println!("Running with timeout={}, workers={}", timeout, workers);
}Complete .mise.toml Template
# .mise.toml - Centralized configuration for this skill/project
# Values auto-load when shell has `mise activate` configured
# Scripts MUST work without mise (use defaults)
# Enforce minimum mise version for compatibility
min_version = "2024.9.5"
# ==============================================================================
# SETTINGS - mise behavior configuration
# ==============================================================================
[settings]
experimental = true
python.uv_venv_auto = true
# ==============================================================================
# TOOLS - Version pinning for reproducibility
# ==============================================================================
[tools]
python = "3.11" # baseline >=3.11; pin to project needs
node = "latest"
uv = "latest"
# ==============================================================================
# ENVIRONMENT CONFIGURATION
# ==============================================================================
[env]
# --- Special Directives ---
# Auto-create Python venv
_.python.venv = { path = ".venv", create = true }
# Load .env files (optional)
# _.file = [".env", { path = ".env.local", redact = true }]
# Extend PATH with project binaries
_.path = ["{{config_root}}/bin", "{{config_root}}/scripts"]
# --- Project Paths ---
PROJECT_ROOT = "{{config_root}}"
OUTPUT_DIR = "output"
ADR_DIR = "docs/adr"
DESIGN_DIR = "docs/design"
# --- Timeouts (seconds) ---
SCRIPT_TIMEOUT = "300"
JSCPD_TIMEOUT = "120"
# --- Performance ---
PARALLEL_WORKERS = "4"
# --- Feature Flags ---
DEBUG_MODE = "false"
VERBOSE = "false"
# --- Python ---
PYTHONUNBUFFERED = "1"
# --- External Services (non-secrets only) ---
DOPPLER_PROJECT = "my-project"
DOPPLER_CONFIG = "prd"
# --- Redaction patterns for sensitive values ---
redactions = ["*_TOKEN", "*_KEY", "*_SECRET"]
# ==============================================================================
# TASKS - See mise-tasks skill for comprehensive task orchestration
# ==============================================================================
# [tasks]
# For task definitions with dependencies, arguments, and file tracking,
# invoke the mise-tasks skill: ../mise-tasks/SKILL.md
#
# Example tasks (uncomment and customize):
# [tasks.test]
# description = "Run test suite"
# run = "pytest tests/"
#
# [tasks.lint]
# description = "Run linters"
# run = "ruff check . && ruff format --check ."
#
# [tasks.build]
# description = "Build package"
# depends = ["lint", "test"]
# run = "uv build"Real-World Examples
code-hardcode-audit/.mise.toml
[env]
AUDIT_PARALLEL_WORKERS = "4"
AUDIT_JSCPD_TIMEOUT = "300"
AUDIT_GITLEAKS_TIMEOUT = "120"
AUDIT_OUTPUT_FORMAT = "both"
PYTHONUNBUFFERED = "1"pypi-doppler/.mise.toml
[env]
DOPPLER_PROJECT = "claude-config"
DOPPLER_CONFIG = "prd"
DOPPLER_PYPI_SECRET = "PYPI_TOKEN"
PYPI_VERIFY_DELAY = "3"implement-plan-preflight/.mise.toml
[env]
ADR_DIR = "docs/adr"
DESIGN_DIR = "docs/design"
DESIGN_SPEC_FILENAME = "spec.md"
PREFLIGHT_STRICT_MODE = "true"Testing Pattern
# Test 1: Without mise (uses defaults)
unset SCRIPT_TIMEOUT OUTPUT_DIR
./script.py # Should work with defaults
# Test 2: With mise activated
cd /path/to/skill
mise trust .mise.toml # First time only
# Values auto-load from .mise.toml
./script.py # Uses mise values
# Test 3: Override specific value
SCRIPT_TIMEOUT=60 ./script.py # Explicit override winsMigration Checklist
When refactoring existing scripts to use mise [env]:
- [ ] Identify all hardcoded values (grep for magic numbers, paths)
- [ ] Create
.mise.tomlwith[env]section - [ ] Update script: add
os.environ.get()with original as default - [ ] Add ADR reference comment at config section
- [ ] Test: unset env vars, verify defaults work
- [ ] Test: set env vars manually, verify override works
- [ ] Test: in mise-activated shell, verify .mise.toml values load
- [ ] Document variables in skill's SKILL.md
Task Orchestration Integration
When you detect workflow opportunities during mise configuration, prescriptively invoke the mise-tasks skill.
Detection Triggers
- Multi-step workflows (test + build + deploy)
- Repeatable project commands (lint, format, release)
- Dependency chains (db-migrate depends on db-init)
- File-tracked builds (sources/outputs caching)
Prescriptive AI Reminder
PRESCRIPTIVE: When detecting multi-step project workflows, invoke [`mise-tasks` skill](../../mise-tasks/SKILL.md) to generate appropriate [tasks] definitions with dependency management.Example: From Environment to Tasks
Step 1: Configure environment (mise-configuration skill):
[env]
DATABASE_URL = "postgresql://localhost/mydb"
_.python.venv = { path = ".venv", create = true }Step 2: Define tasks (mise-tasks skill):
[tasks.test]
depends = ["lint"]
run = "pytest tests/"
[tasks.deploy]
depends = ["test", "build"]
run = "deploy.sh"Tasks automatically inherit [env] values.