Personal ZSH configuration files for macOS and Linux.
zsh-environment/
├── zshrc # Interactive shell config (Oh My Zsh, plugins, theme, tools, aliases, functions)
├── zprofile # Login profile (PATH and environment exports only)
├── p10k.zsh # Powerlevel10k theme configuration
├── p10k-tty.zsh # p10k overrides for the Linux console — opt-in, not symlinked (see below)
├── tty-theme.zsh # Linux console color palette + `ttytheme` command — not symlinked (see below)
├── setup.sh # One-shot setup: dependencies + symlinks + validation
├── bootstrap.sh # Installs required dependencies
├── install.sh # Sets up symlinks from ~/ to this repo
├── check.sh # Validates the installation
└── private/ # Per-machine files — gitignored, never committed
├── work.zsh # Work-specific configs, created manually on each machine (see below)
└── machine.zsh # Optional machine settings, e.g. the TTY prompt and theme (see below)
setup.sh is the single entry point for a new machine: it installs dependencies, creates symlinks, and validates the setup. Internally it calls bootstrap.sh, install.sh, and check.sh in sequence — each of which can also be run independently.
install.sh replaces ~/.zshrc, ~/.zprofile, and ~/.p10k.zsh with symlinks pointing to this repo. Any change committed here takes effect immediately in new terminal sessions.
check.sh validates the installation: confirms that the symlinks point to the correct files, that private/ is set up correctly, that no private files are tracked by git, and that zshrc and zprofile have no syntax errors.
Work-specific configuration lives in private/work.zsh, which is sourced at the end of zshrc if the file exists. This file is gitignored and must be created manually on each machine — it is never tracked by this repository.
Machine settings live in private/machine.zsh, also gitignored and optional. Unlike work.zsh, it holds no secrets — only switches that describe the hardware or terminal of that machine. It is sourced before the Powerlevel10k block, so its switches can change how the prompt is loaded.
The Linux console (the text VT on a headless machine's monitor, TERM=linux) only loads bitmap fonts of up to 512 glyphs, so Nerd Font icons and symbols such as ❯ and ✔ cannot be drawn there. p10k-tty.zsh overrides the prompt with ASCII equivalents (POWERLEVEL9K_MODE=ascii, > / < as the prompt char, ok for success).
It is loaded by zshrc only when all three conditions hold:
- The OS is Linux (
$OSTYPE == linux-gnu*) — macOS never loads it. - The machine opted in with
ZSH_ENV_TTY_PROMPT=1inprivate/machine.zsh. - The terminal is the console:
TERM=linux, or — inside tmux, whereTERMbelongs to tmux — the attached client's#{client_termname}islinux.
To enable it on a machine:
echo 'ZSH_ENV_TTY_PROMPT=1' >> ~/.config/zsh-environment/private/machine.zshThe console has only 8 colors. Outside tmux, zsh knows it (TERM=linux) and skips the 256-color greys of p10k.zsh, so they render as the default white. Inside tmux, TERM claims 256 colors and tmux rounds those greys down to black — invisible on the black background. So, in a tmux pane opened from the console, zshrc also sets TERM=tmux (8 colors); programs started from that pane (nvim, lazygit) see 8 colors as well.
The choice is made when the shell starts: a tmux pane opened over SSH keeps the Nerd Font prompt when later viewed on the console, and vice versa. Open a new pane (or exec zsh) to switch. The git ahead/behind arrows (⇣⇡⇠⇢) are hardcoded in p10k.zsh's formatter and still show as ? on the console.
For accented characters and box drawing to render, the console font must be a Unicode one — on Debian, set CODESET="Uni3", FONTFACE="Terminus" and FONTSIZE in /etc/default/console-setup, then run sudo setupcon.
The console's 16 colors can be re-tinted without root, through \e]P<index><RRGGBB> escape sequences (\e]R restores the kernel default). tty-theme.zsh ships a light palette (One Light) and the ttytheme command, defined on Linux only:
ttytheme light # apply the light palette now and save it as this machine's default
ttytheme dark # restore the kernel palette now and drop the saved default
ttytheme # show the saved themeSaving means one line in private/machine.zsh (ZSH_ENV_TTY_THEME=light); the command rewrites only that line. Nothing changes on a machine until someone runs ttytheme there — a fresh clone gets the command, never the theme.
- Inside tmux it works too: tmux does not forward
\e]P, sottythemewrites straight to the attached client's tty (#{client_tty}), as long as that client is the console. Anywhere else (e.g. plain SSH) it refuses with a warning. - On login,
zshrcre-applies the saved theme when the shell runs directly on the console (TERM=linux, outside tmux). The palette lasts until logout or reboot, and a tmux started from that shell inherits it. - It is independent of
ZSH_ENV_TTY_PROMPT: either can be enabled without the other.
-
Clone the repo:
git clone <repo-url> ~/.config/zsh-environment
-
Run the setup script:
cd ~/.config/zsh-environment chmod +x setup.sh bootstrap.sh install.sh check.sh ./setup.sh
setup.shwill ask before installing each dependency, then create the symlinks and validate everything. -
Create
private/work.zshwith your work-specific configs:touch ~/.config/zsh-environment/private/work.zsh # edit it with your work aliases, exports, and functions
-
Open a new terminal.
| Script | Purpose |
|---|---|
./bootstrap.sh |
Install dependencies only |
./install.sh |
Create symlinks only |
./check.sh |
Validate the installation |
| Tool | Purpose | Aliases | Config |
|---|---|---|---|
git |
Version control | add, st, ci, push, co, pull, lg, and others |
— |
curl |
HTTP requests | myip |
— |
zsh |
Shell | — | — |
tmux |
Terminal multiplexer | tx, txa, txn, txl, txk, tConfig, tConfigReload |
tmux-environment ¹ |
nvim |
Text editor (default $EDITOR) |
nv, nv., nvconfig, and others |
neo-vim-environment ¹ |
jq |
JSON processor | flattenJson, jwt-decode, catFileWithColors |
— |
lazygit |
Terminal UI for git | lg |
— |
¹ Personal configuration repository — optional, bring your own config.
| Tool | Purpose |
|---|---|
| Homebrew | Package manager for macOS and Linux |
| Oh My Zsh | ZSH framework |
| zplug | ZSH plugin manager (installs zsh-syntax-highlighting) |
| Powerlevel10k | ZSH prompt theme |
These tools are not required. If installed, their aliases are automatically activated in zshrc. If not installed, they are silently skipped.
| Tool | Purpose | Aliases | Config |
|---|---|---|---|
kitty |
Terminal emulator | kittytheme, nvkitty, updateKitty |
kitty-config ¹ |
dotnet |
.NET SDK | dn, dnb, dnr, dnc, dnrt, dnt |
— |
npm |
Node package manager | n, nr, ni |
— |
op |
1Password CLI | shell completion | — |
¹ Personal configuration repository — optional, bring your own config.
| Tool | When suggested | Purpose |
|---|---|---|
| NVM | When npm is found without NVM, or on first install |
Manages multiple Node.js versions |
| SDKMAN | When java is found without SDKMAN, or on first install |
Manages Java and other JVM tool versions |
| Tool | Notes |
|---|---|
vim |
Available on all Unix systems |
openssl |
Pre-installed on macOS; available via Homebrew on Linux |
lsof |
Pre-installed on macOS and most Linux distributions |
dig |
Pre-installed on macOS; part of dnsutils on Linux |
gcloud |
Sourced automatically if installed at standard paths |
| Type | Where |
|---|---|
| Aliases and shell functions | zshrc |
Environment exports (PATH, *_HOME, ...) |
zprofile |
| Work-specific configs (internal URLs, company tools, credentials) | private/work.zsh only |
| Machine switches (hardware/terminal of one machine, no secrets) | private/machine.zsh (ttytheme edits it for you) |
Aliases and functions must live in
zshrc. They are not part of the environment, so they are not inherited by child processes — defining them inzprofilemakes them reach login shells only, and they silently vanish in every non-login terminal (LXDE's lxterminal, tmux panes, most Linux emulators).