Zsh Environment Manager
Manage a zsh shell environment on macOS running Oh My Zsh.
Assumptions
This skill assumes a common macOS setup, but always verify against the actual files before proposing changes — don't assume:
- Shell: zsh (default macOS shell)
- Framework: Oh My Zsh at
~/.oh-my-zsh/ - Config chain:
~/.zshenv→~/.zprofile→~/.zshrc
Config File Roles
~/.zshenv: Runs for ALL shell invocations (interactive, non-interactive, scripts, cron). Keep it minimal — anything here pays a cost on every subprocess.~/.zprofile: Login shells only. Typical contents:brew shellenv, other one-time PATH setup.~/.zshrc: Everything else — Oh My Zsh, plugins, PATH, tool inits, aliases. Most user-facing changes land here.~/.profile: Legacy; zsh ignores this file by default. If it still exists and has content, check for stale duplicate exports also present in~/.zshrc/~/.zshenv.
Safety Protocol
MANDATORY for ALL modifications to shell config files.
Before editing:
- Run:
bash scripts/config_backup.sh - Confirm the intended change and target file with the user before applying
- Show the user the exact edit (old → new) before applying
After editing:
- Syntax check:
zsh -n ~/.zshrc(or whichever file was edited) - Load test:
zsh -c 'source ~/.zshrc && echo "Config loads OK"' - Tell the user the backup location and restore command
If something breaks:
Tell the user: cp ~/.config/zsh-backups/zshrc.TIMESTAMP ~/.zshrc (use the actual timestamp from the backup step).
Mode 1: Diagnose
Use when the user has a shell problem (missing command, wrong version, broken config, etc.).
Steps:
- Read the relevant config files (
~/.zshenv,~/.zprofile,~/.zshrc) - Run diagnostic scripts as needed:
- General issues:
bash scripts/shell_audit.sh - PATH issues:
zsh scripts/path_analyzer.sh - Slow startup:
bash scripts/startup_profiler.sh
- General issues:
- Consult reference docs for details:
- File placement:
references/zsh_startup_order.md - PATH specifics:
references/path_management.md - Plugin issues:
references/omz_plugin_guide.md - Terminal issues:
references/terminal_apps.md
- File placement:
- Present findings as a numbered list with severity:
[ERR],[WARN],[INFO] - Propose specific fixes with before/after code blocks
- If a fix is approved, follow the Safety Protocol
Mode 2: Modify
Use when the user wants to change their shell config.
Choosing the right file
Need this in non-interactive scripts/cron? → ~/.zshenv
PATH for Homebrew/system tools? → ~/.zprofile
Everything else (default)? → ~/.zshrc
See references/zsh_startup_order.md for details.
Adding a PATH entry
- Check the directory exists
- Check it's not already in PATH (grep the config files)
- Use the idempotent case pattern:
# tool-name
case ":$PATH:" in
*":/path/to/dir:"*) ;;
*) export PATH="/path/to/dir:$PATH" ;;
esac
# tool-name end
- Place near related entries in
.zshrc - Follow Safety Protocol
Removing a PATH entry
- Find the block (look for comment markers like
# tool-name/# tool-name end) - Remove the entire block including comments
- Verify the command still resolves:
zsh -c 'source ~/.zshrc && which COMMAND' - Follow Safety Protocol
Adding an oh-my-zsh plugin
See references/omz_plugin_guide.md for full details.
- Bundled: Just add to the
plugins=()array - Third-party:
git cloneto~/.oh-my-zsh/custom/plugins/<name>/, then add to array - Custom: Scaffold
~/.oh-my-zsh/custom/plugins/<name>/<name>.plugin.zsh, then add to array - Ordering:
zsh-syntax-highlightingmust remain last in the array - Follow Safety Protocol
Removing an oh-my-zsh plugin
- Remove from
plugins=()array - Ask if user also wants to delete the plugin directory (for custom/third-party)
- Check for plugin-specific config below the
source $ZSH/oh-my-zsh.shline - Follow Safety Protocol
Adding/modifying environment variables
- Check for existing definitions across all config files:
grep -n 'VARNAME' ~/.zshrc ~/.zshenv ~/.zprofile - Avoid creating duplicates
- Place in the correct file based on scope (see decision tree above)
- For secrets: recommend
~/.config/secrets/envpattern (seereferences/secret_patterns.md) - Follow Safety Protocol
Mode 3: Audit
Use when the user wants a comprehensive health check.
Steps:
- Run all diagnostic scripts:
bash scripts/shell_audit.sh zsh scripts/path_analyzer.sh bash scripts/startup_profiler.sh - Check for exposed secrets (see
references/secret_patterns.md) - Report findings in these sections:
- Secrets: Any exposed API keys, tokens, passwords
- PATH Health: Duplicates, dead entries, unmounted volumes
- Config Duplicates: Duplicate exports, PATH additions
- Plugin Health: Missing plugins, orphaned custom plugins
- Performance: Startup time, slow operations
- Terminal Apps: VS Code, iTerm2, Ghostty config status
- Prioritize findings: errors first, then warnings, then info
Common Audit Findings (patterns to check for)
Independent of any specific user's config, these are the issue categories that show up most often:
- Secrets committed in plain text (API keys, tokens, passwords) in
.zshrc/.zshenv - The same PATH directory added twice via different code paths (e.g.
~/binand$HOME/binboth resolving to the same place) - The same environment variable or tool init sourced from more than one config file — redundant work on every shell start
- Manual sourcing of a tool's init script (nvm, pyenv, rbenv, etc.) that duplicates what an Oh My Zsh plugin already does for the same tool
eval-based init blocks (pyenv init -,rbenv init -, etc.) running on every shell open — measure withstartup_profiler.shbefore assuming a given block is the culprit- PATH entries pointing at removable or network volumes that go dead when unmounted, silently swallowing
command not founddiagnosis time
Terminal App Configuration
See references/terminal_apps.md for full details.
VS Code (most common issue)
Settings at: ~/Library/Application Support/Code/User/settings.json
Ensure these are set:
"terminal.integrated.defaultProfile.osx": "zsh"
For Cursor: ~/Library/Application Support/Cursor/User/settings.json
For Windsurf: ~/Library/Application Support/Windsurf/User/settings.json
iTerm2
Check profile shell settings: defaults read com.googlecode.iterm2 "New Bookmarks" | grep -A2 "Custom Command"
Ghostty
Config at ~/.config/ghostty/config. Create if it doesn't exist.
Common Recipes
Make shell startup faster
- Remove manual nvm sourcing if the OMZ nvm plugin is active (it lazy-loads)
- Use
pyenv init --pathinstead ofpyenv init -for PATH-only setup - Check for duplicate
compinitcalls (OMZ already calls it) - Run
startup_profiler.shto measure impact
Add a new tool to PATH
Use the idempotent case pattern (see Mode 2: Adding a PATH entry).
Create a custom oh-my-zsh plugin
mkdir -p ~/.oh-my-zsh/custom/plugins/my-plugin
cat > ~/.oh-my-zsh/custom/plugins/my-plugin/my-plugin.plugin.zsh << 'EOF'
# my-plugin: description
# Add functions, aliases, hooks here
EOF
Then add my-plugin to the plugins=() array in .zshrc.
Move secrets out of dotfiles
See references/secret_patterns.md for the ~/.config/secrets/env pattern and macOS Keychain approach.
Fix PATH ordering
Prepend = higher priority. Append = fallback. See references/path_management.md.
Preview an Oh My Zsh theme without switching
bash scripts/theme_preview.sh renders a sample prompt for a candidate theme so the user can compare before committing to a ZSH_THEME change.
Resources
scripts/shell_audit.sh— general config health scan (secrets, PATH, duplicates, plugins)scripts/path_analyzer.sh— PATH-specific analysis (duplicates, dead entries, ordering)scripts/startup_profiler.sh— measures shell startup time and flags slow blocksscripts/config_backup.sh— timestamped backup of dotfiles before an editscripts/theme_preview.sh— render a sample prompt for a candidate Oh My Zsh themereferences/zsh_startup_order.md— which file loads when, and why it mattersreferences/path_management.md— PATH ordering and idempotent-append patternsreferences/omz_plugin_guide.md— adding/removing bundled, third-party, and custom pluginsreferences/terminal_apps.md— terminal-specific shell profile configurationreferences/secret_patterns.md— detecting and relocating secrets out of dotfiles