Personal zsh config
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-26 15:44:24 +03:00
.gitignore Initial commit 2026-09-05 20:04:14 +03:00
aliases.zsh feat(alias): bat no longer simple by default 2026-09-26 15:44:24 +03:00
completions.zsh feat: removed zeditor, added zed completions 2026-09-05 20:29:15 +03:00
env.zsh feat: jenv 2026-09-10 08:35:12 +03:00
functions.zsh feat: removed zeditor, added zed completions 2026-09-05 20:29:15 +03:00
integrations.zsh feat: jenv 2026-09-10 08:35:12 +03:00
lib.zsh docs: refreshed documentation 2026-09-05 20:22:28 +03:00
local.zsh.example Initial commit 2026-09-05 20:04:14 +03:00
options.zsh fix: removed already-default emacs line that had nothing to do with emacs 2026-09-05 20:25:21 +03:00
plugins.txt Initial commit 2026-09-05 20:04:14 +03:00
plugins.zsh docs: refreshed documentation 2026-09-05 20:22:28 +03:00
prompt.zsh docs: refreshed documentation 2026-09-05 20:22:28 +03:00
README.md feat: jenv 2026-09-10 08:35:12 +03:00
theme.toml Initial commit 2026-09-05 20:04:14 +03:00
update.zsh Initial commit 2026-09-05 20:04:14 +03:00
zshrc fix: removed already-default emacs line that had nothing to do with emacs 2026-09-05 20:25:21 +03:00

zsh config

A modular, stand-alone zsh setup: small focused files, guard everything (has_command / alias_if — a missing tool never breaks the shell), only add PATH entries that exist, and one config for linux + macOS. Windows/WSL is handled where trivial but not a priority.

Mostly written with LLM assistance; all generated code has been reviewed.

Wiring it up

Everything is sourced through one clear entry file: zshrc. Point your real ~/.zshrc at it. Assuming this folder ends up at ~/.config/zsh:

echo 'source ~/.config/zsh/zshrc' >> ~/.zshrc

The config finds its own directory (${ZSH_CFG_DIR}), so you can put it anywhere — no paths are hardcoded. Then open a new shell.

What's where

File Responsibility
zshrc Entry point. Resolves its own dir, sources the modules in order.
lib.zsh Helpers + OS detection: has_command, alias_if, alias_unless_disabled, path_add, note, IS_LINUX/IS_MACOS/IS_WINDOWS. Also loads the persistent config state (below).
env.zsh Env vars (EDITOR=micro, cargo paths…) + PATH; sources local.zsh if present; Homebrew via brew shellenv (linuxbrew and both macOS arches); jenv's shims ahead of Homebrew on macOS.
update.zsh Rate-limited git pull --ff-only of this config from its remote on interactive shell start; exposes update-config for a manual on-demand run.
local.zsh Per-machine aliases, configs go here (API keys, etc). Gitignored, machine-local — never commit. Copy from local.zsh.example.
local.zsh.example Committed template listing the keys this config expects.
options.zsh setopts, history.
completions.zsh compinit + carapace.
aliases.zsh Modern-tool swaps + editors→micro (all guarded).
functions.zsh clipboard, moodle, list_optional_deps (Arch-only), alias-disable/alias-enable, config.
integrations.zsh thefuck, mcfly, upkg, zoxide, broot, jenv — guarded eval/source hooks.
prompt.zsh oh-my-posh + theme.toml, with a native fallback prompt.
plugins.zsh Antidote bundle (autosuggestions, syntax-highlighting, history-substring-search) + lavender highlight theming.
plugins.txt The antidote plugin list.
theme.toml oh-my-posh theme.
generated/ Auto-created caches & plugin clones. Gitignored — don't commit.

Dependencies

Nothing is required — missing tools are skipped. To get the full experience, install (all available via Homebrew on linux & macOS, or your distro):

  • Prompt/completions: oh-my-posh, carapace
  • CLI swaps: eza, bat, fd, dust, broot, ripgrep (rg), procs, gping, btop, micro
  • Integrations: thefuck, mcfly
  • Plugins: git (antidote auto-clones the rest on first run)

Per-machine aliases, configs

env.zsh sources local.zsh if it exists, and nothing else depends on it. On a fresh clone, env.zsh auto-creates local.zsh from local.zsh.example the first time it's missing, then sources it right away — no manual cp needed. The copied file nags you (in yellow, on every interactive shell start) to edit it and reload, until you do:

$EDITOR ~/.config/zsh/local.zsh
source ~/.config/zsh/local.zsh

local.zsh is gitignored; only the .example template is committed.

Managing aliases & settings

Every swap in aliases.zsh is also individually toggleable, independent of whether its target tool is installed — e.g. you have btop but don't want htop remapped to it:

alias-disable htop   # turn it off (also unaliases it in the current shell)
alias-enable  htop   # turn it back on (run `exec zsh` to re-apply)

Both validate the name against aliases.zsh's ZSH_CFG_MANAGED_ALIASES list and print a clear success/failure message. State is a plain-text list of names in generated/disabled_aliases.conf (gitignored, machine-local, one name per line, # comments allowed) — lib.zsh reads it at shell start; no file means no disables.

The some optional tools are missing startup notice (and any other collected note) can be toggled off:

config notices off      # silence the summary
config notices on       # (default)
config notices status

This is backed by a small general-purpose key=value store — config set/get/ unset/list <key> [value] — in generated/settings.conf (gitignored, machine-local), for future shell-behaviour toggles beyond notices.

Auto-update

Every interactive shell start, update.zsh checks (at most once every 3h, tracked in generated/last_update_check) whether this repo can be fast-forwarded from its remote, and pulls if so — so a fresh terminal is usually already on the latest commit. It only ever does git pull --ff-only: if the working tree is dirty or history has diverged, it skips the pull and raises a note instead of touching anything. It runs first in the interactive load order, so a pulled update is picked up by the rest of that same shell's startup, not just the next one.

Run it on demand, bypassing the rate limit, with:

update-config

Turn it off like any other config toggle:

config auto-update off
config auto-update on    # (default)
config auto-update status

Notes

  • History lives at ~/.zsh_history — outside this managed dir on purpose, so chezmoi/git ignore it and it can't sync-conflict.
  • Plugins are managed by antidote, bootstrapped automatically into generated/ on first launch. Add a plugin by editing plugins.txt; the static bundle rebuilds when that file changes.
  • Per-OS configs: when you split linux/macOS later, add an os.$(uname).zsh source line near the end of zshrc — lib.zsh already exports IS_LINUX/IS_MACOS for inline branching in the meantime.