Shape the shell
Create a small shell contract
Build a minimal, documented shell configuration and verify it in a fresh login and non-interactive shell.
10 minute lesson
You have mapped the startup files, built the PATH, and moved secrets out. This lesson assembles those pieces into a shell contract: a small, documented configuration you can verify, instead of a .zshrc that grew by accretion for five years.
Create one setup repository with focused files for PATH, interactive behavior, and project helpers. Focused files keep each change reviewable:
~/setup/zsh/
profile.zsh # PATH and exported environment (sourced from .zprofile)
interactive.zsh # prompt, aliases, completion (sourced from .zshrc)
projects.zsh # helper functions for your repositories
Your real .zprofile and .zshrc shrink to a couple of source lines each. When something misbehaves, you comment out one file, not one hundred lines.
Inside projects.zsh, prefer small functions over long aliases when arguments or error handling matter. An alias is text substitution; a function can validate input and fail loudly:
serve() {
local port="${1:-3000}"
[ -f package.json ] || { print -u2 "no package.json here"; return 1 }
npm run dev -- --port "$port"
}
Verify the contract
A shell configuration has two clients: you at a prompt, and every script that runs without you. Test the two important contexts:
zsh -lic "command -v node; node --version"
zsh -fc "command -v git"
The first spawns a fresh login interactive shell (-l login, -i interactive, -c run this command) and exercises normal Terminal startup, with .zprofile and .zshrc applied. It should print your version-managed Node.
The second, with -f, skips your startup files entirely. The non-interactive test catches configuration that scripts cannot see: if a command only resolves in the first test, automation running outside your interactive setup will not find it.
Keep it quiet
Remove output and prompts from files read by automation. A .zshenv or profile that prints a greeting corrupts every tool that captures shell output — the failure looks like a parser bug in some unrelated program, hours away from the actual cause.
The habit that makes the contract durable: run both verification commands after every configuration change. Ten seconds, and you know both clients still get what they were promised.
Lesson completed