Local development environment patterns: runtime version pinning with mise/asdf, environment variables with direnv, Dev Containers for consistent team environments, Docker Compose for local services, and pre-commit hooks.
From clarcnpx claudepluginhub marvinrichter/clarc --plugin clarcThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
"Works on my machine" is a solved problem. Consistent environments across the team eliminate entire classes of bugs and onboarding friction.
/setup-dev)mise (formerly rtx) manages Node, Python, Go, Java, and 200+ other runtimes per-project:
# .mise.toml — commit this to git
[tools]
node = "22"
python = "3.12"
go = "1.24"
[env]
NODE_ENV = "development"
# Install mise
curl https://mise.run | sh
# Install all tools defined in .mise.toml
mise install
# Activate (add to shell profile)
mise activate zsh >> ~/.zshrc
Everyone on the team runs the same versions. CI should use the same .mise.toml:
# .github/workflows/ci.yml
- uses: jdx/mise-action@v2 # Reads .mise.toml automatically
direnv automatically loads .env when you cd into a project directory:
# Install
brew install direnv
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
# Project setup
echo 'dotenv' > .envrc # Load .env automatically
direnv allow # Approve this project
.env is gitignored. .env.example is committed with all variables documented.
# .env (local, gitignored)
DATABASE_URL=postgresql://app:app@localhost:5432/appdb
REDIS_URL=redis://localhost:6379
# .envrc (committed — just loads .env)
dotenv
Dev Containers give every developer (and CI) the exact same environment in Docker:
// .devcontainer/devcontainer.json
{
"name": "myapp",
"dockerComposeFile": ["../docker-compose.dev.yml", "docker-compose.devcontainer.yml"],
"service": "app",
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postCreateCommand": "npm install && npm run db:migrate",
"postStartCommand": "docker compose up -d postgres redis",
"customizations": {
"vscode": {
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome"
},
"extensions": [
"biomejs.biome",
"prisma.prisma",
"bradlc.vscode-tailwindcss"
]
}
}
}
# .devcontainer/docker-compose.devcontainer.yml
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
environment:
DATABASE_URL: postgresql://app:app@postgres:5432/appdb
REDIS_URL: redis://redis:6379
Run only infrastructure services locally — run the app code natively (better hot reload, debugger):
# docker-compose.dev.yml
services:
postgres:
image: postgres:18-alpine
environment: { POSTGRES_DB: appdb, POSTGRES_USER: app, POSTGRES_PASSWORD: app }
ports: ['5432:5432']
volumes: [postgres_data:/var/lib/postgresql/data]
healthcheck:
test: [CMD, pg_isready, -U, app]
interval: 5s
retries: 5
redis:
image: redis:8-alpine
ports: ['6379:6379']
mailpit:
image: axllent/mailpit
ports: ['1025:1025', '8025:8025'] # SMTP + Web UI for local email testing
# Test emails visible at http://localhost:8025
volumes:
postgres_data:
# Start services
docker compose -f docker-compose.dev.yml up -d
# Stop (keeps data)
docker compose -f docker-compose.dev.yml stop
# Reset data
docker compose -f docker-compose.dev.yml down -v && docker compose -f docker-compose.dev.yml up -d
These already exist via hooks.json — document the key ones:
| Hook | Trigger | What It Does |
|---|---|---|
| Post-edit format | Edit any TS/JS file | Runs Biome/Prettier |
| Post-edit typecheck | Edit any .ts file | Runs tsc --noEmit |
| Console.log warning | Edit any file | Warns if console.log added |
| Pre-push reminder | git push | Prompts to review changes |
For teams without Claude Code, use pre-commit:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: lint
name: Lint
entry: npm run lint
language: system
pass_filenames: false
- id: typecheck
name: Type Check
entry: npm run typecheck
language: system
pass_filenames: false
.mise.toml committed with pinned runtime versions.env.example committed with all variables documented.env is in .gitignoredocker-compose.dev.yml for local services