A personal zsh configuration built on top of oh-my-zsh. Cross-platform (macOS and Linux), modular, and designed so machine-specific tweaks stay out of version control.
Once installed, the repo lives at ~/.zsh/zsh-config/ alongside ~/.zsh/ohmyzsh/, and system/zshrc is symlinked to ~/.zshrc.
Run the installer — it clones ohmyzsh, this repo, and fast-syntax-highlighting into ~/.zsh/, then symlinks system/zshrc to ~/.zshrc:
wget -qO - https://raw.githubusercontent.com/edieguez/zsh-config/master/system/install.sh | zshRestart your shell (or exec zsh) to finish setup.
Optional but recommended tools — this config activates extra features when it detects them on PATH:
fzf+fd— fuzzy file/dir navigation (Ctrl+T,Alt+C/Esc+C,**<Tab>completion)bat— syntax-highlighted file previews in fzf (falls back tocat)lsd— rich directory tree previews in fzf (falls back tols)nvmat~/.nvm— enables lazy-loaded Nodesdkmanat~/.sdkman— enables lazy-loaded JVM tooling
Plugins are oh-my-zsh plugins, and they must be declared before oh-my-zsh is sourced.
Always-on plugins — edit the plugins=(...) array in system/init.sh:
plugins=(
extract
fast-syntax-highlighting
git
sudo
virtualenv_py
z
my-new-plugin # <-- add here
)Conditional plugins (load only if a tool is installed) — append to plugins from system/bundles.sh, following the existing patterns:
(( $+commands[docker] )) && plugins+=(docker)
[ -d "$HOME/.my-tool" ] && plugins+=(my-tool-plugin)Custom plugins (code you wrote or cloned yourself) — drop the plugin directory under plugins/ and add its name to the plugins=(...) array. This directory acts as $ZSH_CUSTOM/plugins, so oh-my-zsh picks them up automatically.
Platform-specific plugins — use system/macos/bundles.sh or system/linux/bundles.sh (loaded right after the global bundles.sh).
PATH precedence, from highest to lowest, is set at the end of system/init.sh:
bin/local > bin/$PLATFORM > bin/ > CUSTOM_PATH > system $PATHPick the right spot based on what you're adding:
A script you want available as a command — drop it in one of the bin/ directories and make it executable (chmod +x). It's on PATH automatically:
| Where | When to use |
|---|---|
bin/ |
Cross-platform scripts, committed to the repo |
bin/macos/ |
macOS-only scripts |
bin/linux/ |
Linux-only scripts |
bin/local/ |
Machine-specific scripts or binaries (gitignored, highest precedence) |
Note: bin/ is added to PATH non-recursively. Subdirectories like bin/archive/ are not on PATH.
A toolchain directory (like $GOROOT/bin or a language version manager) — extend CUSTOM_PATH in system/environment.sh. CUSTOM_PATH is a zsh array:
CUSTOM_PATH+=("$HOME/.my-tool/bin")A path that shouldn't be committed (private tool, work-specific directory) — export it from system/environment.local.sh (see next section).
Every system/*.sh file has a *.local.sh counterpart that is gitignored and loaded after the global one. Use these for anything you don't want to commit: API keys, machine-specific aliases, per-host function overrides, etc.
| Local file | Purpose |
|---|---|
system/environment.local.sh |
Env vars, secrets, extra PATH entries |
system/bundles.local.sh |
Plugins only installed on this machine |
system/aliases.local.sh |
Personal aliases |
system/functions.local.sh |
Personal functions |
Example system/environment.local.sh:
export OPENAI_API_KEY="sk-..."
export WORK_VPN_HOST="vpn.example.com"
CUSTOM_PATH+=("$HOME/work/tools/bin")For private binaries and scripts, use bin/local/ — also gitignored, and highest PATH precedence.
These files are created as needed; nothing expects them to exist. Missing *.local.sh files and the bin/local/ directory are silently skipped.
~/.zsh/
├── ohmyzsh/ cloned by install.sh
└── zsh-config/ this repo (= $ZSH_CUSTOM)
├── system/
│ ├── zshrc entry point, symlinked to ~/.zshrc
│ ├── init.sh full loading sequence (sourced by zshrc)
│ ├── environment.sh history opts, SDKMAN paths, CUSTOM_PATH
│ ├── bundles.sh conditional plugins, lazy-loads
│ ├── aliases.sh global aliases
│ ├── functions.sh global functions
│ ├── install.sh installer
│ ├── macos/ macOS-specific overrides
│ └── linux/ Linux-specific overrides
├── bin/ scripts auto-added to PATH
├── lib/ sourced helper libraries
├── plugins/ custom oh-my-zsh plugins
│ └── fzf-improved/ fd-backed fzf integration with previews and toggles
└── themes/ custom oh-my-zsh themes (default: aya)Load order on every shell start: environment → bundles → oh-my-zsh core → aliases → functions → PATH. Each step loads the global file, then the platform variant, then the *.local.sh override (if present).