home / skills / thebushidocollective / han / environment-management

This skill helps you manage environment variables, per-directory settings, and tool configurations with Mise, improving consistency and security across

npx playbooks add skill thebushidocollective/han --skill environment-management

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
8.3 KB
---
name: mise-environment-management
user-invocable: false
description: Use when managing environment variables and project settings with Mise. Covers env configuration, direnv replacement, and per-directory settings.
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash
  - Grep
  - Glob
---

# Mise - Environment Management

Managing environment variables, project settings, and directory-specific configuration with Mise.

## Basic Environment Variables

### Defining Environment Variables

```toml
# mise.toml
[env]
NODE_ENV = "production"
DATABASE_URL = "postgresql://localhost/myapp"
API_KEY = "development-key"
LOG_LEVEL = "info"
```

### Loading Environment

```bash
# Activate mise environment
mise activate bash  # Or zsh, fish

# Or use mise exec
mise exec -- node app.js

# Or mise run
mise run start
```

## Advanced Environment Configuration

### Template Variables

```toml
[env]
PROJECT_ROOT = "{{ config_root }}"
DATA_DIR = "{{ config_root }}/data"
LOG_FILE = "{{ config_root }}/logs/app.log"
```

### Environment File Loading

```toml
[env]
_.file = ".env"
_.file = [".env", ".env.local"]
```

### Conditional Environment Variables

```toml
[env]
# Set based on other variables
DATABASE_URL = "postgresql://{{ env.DB_HOST | default(value='localhost') }}/{{ env.DB_NAME }}"
```

## Path Management

### Adding to PATH

```toml
[env]
_.path = [
  "{{ config_root }}/bin",
  "{{ config_root }}/scripts",
  "/usr/local/bin"
]
```

### Library Paths

```toml
[env]
LD_LIBRARY_PATH = "{{ config_root }}/lib:$LD_LIBRARY_PATH"
DYLD_LIBRARY_PATH = "{{ config_root }}/lib:$DYLD_LIBRARY_PATH"
```

## Tool-Specific Environments

### Python Virtual Environment

```toml
[tools]
python = "3.12"

[env]
_.python.venv = { path = ".venv", create = true }
VIRTUAL_ENV = "{{ config_root }}/.venv"
```

### Node.js Environment

```toml
[tools]
node = "20.10.0"

[env]
NODE_ENV = "development"
NODE_OPTIONS = "--max-old-space-size=4096"
NPM_CONFIG_PREFIX = "{{ config_root }}/.npm-global"
```

### Go Environment

```toml
[tools]
go = "1.21"

[env]
GOPATH = "{{ config_root }}/.go"
GOBIN = "{{ config_root }}/.go/bin"
GO111MODULE = "on"
```

## Replacing direnv

### Basic direnv Replacement

```toml
# Instead of .envrc
# export DATABASE_URL=postgresql://localhost/myapp
# export NODE_ENV=development

# Use mise.toml
[env]
DATABASE_URL = "postgresql://localhost/myapp"
NODE_ENV = "development"
```

### Allowed Directories

```toml
# mise.toml - mark as trusted
[settings]
experimental_monorepo_root = true  # Trust subdirectories
```

### Watch File Changes

Mise automatically reloads when mise.toml changes, similar to direnv.

## Hierarchical Configuration

### Global Settings

```toml
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"
GIT_AUTHOR_EMAIL = "[email protected]"
```

### Project Settings

```toml
# ~/projects/myapp/mise.toml
[env]
PROJECT_NAME = "myapp"
DATABASE_URL = "postgresql://localhost/myapp"
```

### Local Overrides

```toml
# ~/projects/myapp/mise.local.toml (gitignored)
[env]
DATABASE_URL = "postgresql://localhost/myapp-dev"
DEBUG = "true"
```

## Sensitive Data

### Environment Files

```toml
# mise.toml
[env]
_.file = ".env.local"  # Gitignored file with secrets
```

```bash
# .env.local (add to .gitignore)
API_KEY=secret-key-here
DATABASE_PASSWORD=secret-password
```

### Using System Environment

```toml
[env]
# Reference existing environment variables
API_KEY = "$API_KEY"
DATABASE_URL = "$DATABASE_URL"
```

### Secure Secrets Management

```bash
# Don't commit secrets to mise.toml
# Instead, reference from external secret managers

# Example with 1Password CLI
mise exec -- op run -- node app.js

# Or load from encrypted file
mise exec -- sops exec-env .env.encrypted -- node app.js
```

## Mise Environment Variables

### Built-in Variables

Mise provides these variables automatically:

```bash
$MISE_ORIGINAL_CWD      # Directory where mise was invoked
$MISE_CONFIG_ROOT       # Directory containing mise.toml
$MISE_PROJECT_ROOT      # Project root directory
$MISE_DATA_DIR          # Mise data directory
$MISE_CACHE_DIR         # Mise cache directory
```

### Using in Configuration

```toml
[env]
PROJECT_ROOT = "{{ env.MISE_PROJECT_ROOT }}"
CONFIG_FILE = "{{ env.MISE_CONFIG_ROOT }}/config.yaml"
```

## Configuration Validation

### Check Current Environment

```bash
# Show current environment
mise env

# Show specific variable
mise env DATABASE_URL

# Export as shell commands
mise env -s bash > .env.sh
source .env.sh
```

### Verify Configuration

```bash
# Check loaded config files
mise config

# Show resolved settings
mise settings
```

## Best Practices

### Separate Public and Private Config

```toml
# mise.toml (committed)
[env]
NODE_ENV = "development"
LOG_LEVEL = "info"
API_URL = "https://api.example.com"

# mise.local.toml (gitignored)
[env]
API_KEY = "secret-key"
DATABASE_PASSWORD = "secret-password"
```

### Use Descriptive Variable Names

```toml
# Good: Clear, descriptive names
[env]
DATABASE_CONNECTION_POOL_SIZE = "10"
API_REQUEST_TIMEOUT_MS = "5000"
FEATURE_FLAG_NEW_UI = "true"

# Avoid: Vague abbreviations
[env]
DB_POOL = "10"
TIMEOUT = "5000"
FLAG = "true"
```

### Document Required Variables

```toml
# mise.toml
[env]
# Database configuration (required)
DATABASE_URL = "postgresql://localhost/myapp"

# API keys (set in mise.local.toml)
# API_KEY = "your-key-here"

# Optional feature flags
FEATURE_ANALYTICS = "false"
```

### Use Templates for Paths

```toml
# Good: Relative to config root
[env]
DATA_DIR = "{{ config_root }}/data"
LOGS_DIR = "{{ config_root }}/logs"

# Avoid: Hardcoded paths
[env]
DATA_DIR = "/Users/me/project/data"
```

## Common Patterns

### Multi-Environment Setup

```toml
# mise.toml - base configuration
[env]
APP_NAME = "myapp"
LOG_FORMAT = "json"

# mise.development.toml
[env]
NODE_ENV = "development"
DEBUG = "true"
DATABASE_URL = "postgresql://localhost/myapp_dev"

# mise.production.toml
[env]
NODE_ENV = "production"
DEBUG = "false"
DATABASE_URL = "postgresql://prod-server/myapp"
```

```bash
# Switch environments
ln -sf mise.development.toml mise.local.toml
# Or
ln -sf mise.production.toml mise.local.toml
```

### Database Configuration

```toml
[env]
DATABASE_HOST = "localhost"
DATABASE_PORT = "5432"
DATABASE_NAME = "myapp"
DATABASE_USER = "postgres"
DATABASE_URL = "postgresql://{{ env.DATABASE_USER }}:{{ env.DATABASE_PASSWORD }}@{{ env.DATABASE_HOST }}:{{ env.DATABASE_PORT }}/{{ env.DATABASE_NAME }}"
```

### Feature Flags

```toml
[env]
# Feature toggles
FEATURE_NEW_DASHBOARD = "true"
FEATURE_BETA_API = "false"
FEATURE_EXPERIMENTAL_CACHE = "true"

# Feature rollout percentages
FEATURE_NEW_CHECKOUT_ROLLOUT = "25"
```

### CI/CD Environment

```toml
# mise.toml
[env]
NODE_ENV = "{{ env.CI | default(value='development') }}"
SKIP_PREFLIGHT_CHECK = "{{ env.CI | default(value='false') }}"
```

## Anti-Patterns

### Don't Commit Secrets

```toml
# Bad: Secrets in committed file
[env]
API_KEY = "sk-secret-key-12345"
DATABASE_PASSWORD = "password123"

# Good: Reference from secure location
[env]
_.file = ".env.local"  # Gitignored
```

### Don't Duplicate Global Config

```toml
# Bad: Repeating global settings in every project
# ~/project-a/mise.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"

# ~/project-b/mise.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"

# Good: Use global config
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
GIT_AUTHOR_NAME = "Your Name"
```

### Don't Hardcode Environment Names

```toml
# Bad: Hardcoded check
[tasks.deploy]
run = '''
if [ "$NODE_ENV" = "production" ]; then
  ./deploy-prod.sh
fi
'''

# Good: Use configuration
[tasks.deploy]
env = { DEPLOYMENT_TARGET = "production" }
run = "./deploy.sh"
```

## Advanced Patterns

### Dynamic Environment Loading

```toml
[env]
# Load environment based on git branch
BRANCH = "{{ exec(command='git branch --show-current') }}"
DEPLOY_ENV = "{{ env.BRANCH | replace(from='main', to='production') | replace(from='develop', to='staging') }}"
```

### Computed Variables

```toml
[env]
PROJECT_NAME = "myapp"
NAMESPACE = "{{ env.PROJECT_NAME }}-{{ env.ENVIRONMENT }}"
REDIS_URL = "redis://{{ env.NAMESPACE }}-redis:6379"
```

### Environment Inheritance

```toml
# Base configuration
[env]
LOG_LEVEL = "info"
CACHE_TTL = "3600"

# Override in specific contexts
[env.production]
LOG_LEVEL = "warn"
CACHE_TTL = "7200"
```

## Related Skills

- **task-configuration**: Using environment variables in tasks
- **tool-management**: Tool-specific environment configuration

Overview

This skill manages environment variables, per-directory project settings, and direnv-like workflows using Mise. It shows how to define, load, and validate environment configuration, handle secrets safely, and establish hierarchical and tool-specific settings. The guidance emphasizes reproducible, directory-scoped environments and safe practices for CI, development, and production.

How this skill works

The skill inspects mise.toml files (and optional local overrides) to build shell environment variables, PATH entries, and tool-specific settings. It supports template variables, loading git- or branch-derived values, importing .env files, and running commands under mise exec/run. Mise watches configuration changes and exposes built-in variables for config and project roots.

When to use it

  • When you need per-project environment configuration that replaces .envrc/direnv.
  • To centralize tool versions and virtualenv/node/npm/go paths for projects.
  • When you want safe handling of secrets via gitignored files or external secret managers.
  • To create multi-environment (development/staging/production) configuration variants.
  • When CI or deployment pipelines must consume the same resolved environment settings.

Best practices

  • Keep public config in mise.toml and secrets in gitignored mise.local.toml or .env.local files.
  • Use descriptive, explicit variable names and document required variables in the config file.
  • Prefer template variables ({{ config_root }}, built-ins) for portable, relative paths instead of hardcoded absolute paths.
  • Reference system environment or external secret tooling (1Password, sops) rather than committing secrets into mise.toml.
  • Validate with mise env, mise config, and mise settings; export with mise env -s for reproducible shells.

Example use cases

  • Project bootstrap: define NODE_ENV, DATABASE_URL, path additions, and Python venv creation for contributors.
  • direnv replacement: store per-directory environment in mise.toml and trust repositories via settings for allowed directories.
  • Multi-environment workflows: switch between mise.development.toml and mise.production.toml via symlinks for local testing and CI parity.
  • CI/CD: bake environment resolution into CI scripts using mise exec so builds use the same resolved variables and templates.
  • Secure secrets: keep API keys in .env.local and invoke commands with mise exec combined with secret managers.

FAQ

How do I avoid committing secrets?

Keep secrets in a gitignored local file (e.g., mise.local.toml or .env.local) and reference them via ._.file or use external secret managers with mise exec.

Can I replace direnv entirely with Mise?

Yes. Mise provides automatic reloads on config change, per-directory configs, and trusted directory settings so it can replace .envrc workflows while centralizing templates and hierarchical configs.