Skip to content

Zsh Setup ​

Modern shell with superpowers

Zsh configuration using Zinit for fast, lazy-loaded plugins and a comprehensive completion system.

Plugin Manager: Zinit ​

Zinit handles all plugin management with async loading for fast shell startup.

Auto-Installation ​

Zinit auto-installs if missing:

bash
ZINIT_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git"
# Automatically clones on first shell launch
# No manual setup needed

Just start a shell and it handles the rest.

Loaded Plugins ​

PluginPurpose
zsh-autosuggestionsHistory-based command suggestions
zsh-syntax-highlightingReal-time syntax coloring
starshipCross-shell prompt

Minimal plugin set. Quality over quantity.

Script Loading ​

sh/loader.sh owns module order for both shells. It runs in two phases so zsh can slot completions and plugins between them:

bash
source "${DOTFILES}/sh/loader.sh"
dotfiles_load_core      # shell-common, env, colors, terminal, in that order
# fpath, compinit, zinit plugins, starship
dotfiles_load_modules   # the rest of sh/*.sh, alphabetically

Modules are sourced directly rather than through zinit snippets, which cache local files forever. Tool inits inside them (starship, zoxide, fzf, atuin) are cached by cached_eval, so a warm interactive shell starts in well under a tenth of a second even with 150 aliases.

Shell Options ​

History ​

Persistent, shared history across sessions:

bash
setopt share_history          # Share across all sessions
setopt hist_ignore_all_dups   # No duplicates
setopt inc_append_history     # Immediate append (real-time sharing)
setopt EXTENDED_HISTORY       # Timestamps in history

HISTSIZE=50000                # Huge history
SAVEHIST=50000
HISTFILE=~/.zsh_history

Your command history is a valuable asset. These settings protect it.

Compatibility ​

Bash-like behavior where it matters:

bash
setopt SH_WORD_SPLIT          # Bash-style word splitting
setopt EXTENDED_GLOB          # Advanced pattern matching
setopt INTERACTIVE_COMMENTS   # Allow # comments in interactive shell
bash
setopt AUTO_CD                # Just type directory name to cd
setopt AUTO_PUSHD             # Automatic directory stack
setopt PUSHD_IGNORE_DUPS      # No duplicates in stack

Type ~/projects and it cds there automatically.

Completion System ​

Located in zsh/completion.zsh. Comprehensive and fast.

Features ​

  • Case-insensitive matching: cd doc matches Documents
  • Partial matching: cd d/p matches dev/project
  • Menu selection: Tab through options with arrow keys
  • Color-coded: Different colors for files, directories, executables
  • Context-aware: Completion adapts to the command

File Type Colors ​

bash
di=01;34   # Directories (bold blue)
ln=01;36   # Symlinks (bold cyan)
ex=01;32   # Executables (bold green)

Makes ls output and completions instantly scannable.

SSH Host Completion ​

Reads ~/.ssh/config automatically:

bash
ssh dev<Tab>
# Completes to: devserver, dev-staging, dev-prod

No more typing full hostnames.

Process Completion ​

kill command shows running processes:

bash
kill <Tab>
# Shows: PID, CPU%, TTY, Command
# Select the process to kill

Makes kill almost user-friendly.

Terminal Title ​

Managed by sh/terminal.sh:

  • Updates on each prompt
  • Shows: user@host:directory
  • Android build context in AOSP environments
  • Works in tmux, screen, and standard terminals

Your terminal tabs/windows have meaningful titles.

Initialization Flow ​

Startup sequence:

~/.zshrc
  │
  ├─ History & options configured
  │
  ├─ sh/loader.sh sourced, DOTFILES derived from this file's location
  │
  ├─ dotfiles_load_core:
  │  ├─ shell-common.sh  (platform detection, helpers, cached_eval)
  │  ├─ env.sh           (PATH setup)
  │  ├─ colors.sh        (SilkCircuit palette)
  │  └─ terminal.sh      (title management)
  │
  ├─ Completions, then Zinit plugins (auto-install if missing):
  │  ├─ zsh-autosuggestions
  │  ├─ zsh-syntax-highlighting
  │  ├─ zsh-history-substring-search
  │  └─ fzf-tab, then the starship prompt
  │
  ├─ dotfiles_load_modules: the rest of sh/*.sh, sourced directly
  │
  └─ Platform-specific handlers (macOS, Linux, WSL)

Carefully orchestrated for maximum speed.

Customization ​

Private Configuration ​

Create ~/.rc.local for machine-specific settings:

bash
# Machine-specific overrides
export MY_API_KEY="secret"
alias work='cd ~/work/current-project'

# Override defaults
export GWT_ROOT=~/custom/worktree/path

Never committed to dotfiles. Perfect for secrets and local preferences.

Adding Plugins ​

Edit zsh/zshrc:

bash
# Add after existing plugins
zinit light author/plugin-name

Keep it minimal. Every plugin adds startup time.

Updating Plugins ​

bash
zzz  # Alias for: zinit update

Updates all plugins and zinit itself.

Troubleshooting ​

Slow Startup ​

Profile startup time:

bash
# Measure overall time
time zsh -i -c exit

# Detailed profiling
zsh -xv 2>&1 | head -100

Target: < 0.5s startup time.

Missing Completions ​

Rebuild completion cache:

bash
rm ~/.zcompdump
compinit

Fixes most completion issues.

Plugin Issues ​

Reset zinit completely:

bash
rm -rf ~/.local/share/zinit
# Restart shell — will reinstall everything

Nuclear option for broken plugin states.

History Not Saving ​

Check file permissions:

bash
ls -la ~/.zsh_history
# Should be writable by you

# Fix if needed
chmod 600 ~/.zsh_history

Syntax Highlighting Not Working ​

Load order matters. Syntax highlighting must load last:

bash
# In zshrc, make sure this is near the end
zinit light zsh-users/zsh-syntax-highlighting

Pro Tips ​

Trust autosuggestions: Grey text from history? Press right arrow to accept. Massive time saver.

Learn completion: Tab completion is powerful. git <Tab><Tab> shows all git commands. cd /u/l/b<Tab> expands to /usr/local/bin.

Use directory stack: cd -<Tab> shows recent directories. cd -2 jumps to the second most recent.

History search: Ctrl-R for reverse search. Type a few letters, get matching commands from history.

Comment your experimentation: setopt INTERACTIVE_COMMENTS lets you comment out commands at the prompt. # ls -la won't execute.

Keep it fast: Resist the urge to add too many plugins. Fast shell startup > fancy features.

Use ~/.rc.local: Machine-specific settings go here. Keep your main dotfiles clean and portable.

Profile before adding plugins: Measure startup time before and after. If a plugin adds >100ms, consider if it's worth it.

Released under the MIT License