eza | ls replacement with icons + git status |
bat | syntax-highlighted cat / man pager backend |
git-delta | git diff/log/blame pager |
difftastic | syntactic diff for ad-hoc compares (non-git) |
glow | render markdown to ANSI (powers md alias) |
vivid | generates LS_COLORS palettes |
procs | modern ps replacement (Rust) |
tailspin | live-log highlighter (tspin); Solarized via ~/.config/tailspin/theme.toml |
lnav | TUI log navigator; Solarized via built-in theme (see lnav section) |
btop | modern top replacement; Solarized via ~/.config/btop/btop.conf |
xh | modern HTTP client (HTTPie-compatible CLI); Solarized via ~/.config/xh/config.json |
zsh-autosuggestions | ghost-text completion from history |
zsh-syntax-highlighting | live command-line highlighting |
fzf | fuzzy finder + Ctrl-R / Ctrl-T bindings |
fzf-tab | replaces zsh's Tab menu with an fzf picker |
exa, lsd, diff-so-fancy, delta for non-git, …) without asking..zshrc is fixed: fzf → bindkey -r '^[c' (Alt-C unbind) → zoxide → fzf-tab → zsh-autosuggestions → zsh-syntax-highlighting (must be last).vivid generate solarized-dark, bat --theme="Solarized (dark)", delta.syntax-theme = "Solarized (dark)".eza --icons.cat | → bat --paging=never |
less | function → bat wrapper (file = full decoration; pipe = --plain) |
md | → glow --style ~/.config/glow/glamour.json |
mdp | → md -p (paged via real less) |
ls | → eza --group-directories-first --icons |
ll | → eza -lh --git --icons --group-directories-first |
la | → ll -a |
vim / vimdiff | → nvim (guarded on command -v nvim) |
vi | → command vim (legacy minimal vim, suppresses recursive alias) |
ps | → procs (Solarized; PID asc; ps-like columns) |
psh | → procs --load-config ~/.config/procs/procs-heavy.toml (CPU desc, trimmed) |
top | → btop (modern colorful TUI, Solarized; command top for BSD top) |
BSD \ls still works (escape skips alias) and uses OMZ's default LSCOLORS. Use command less to reach real less for less +F, -R, etc. command ps / \ps / /bin/ps reach legacy ps.
eza | simple list (icons via alias) |
eza -l | long form |
eza -lh | long + human sizes |
eza -la | show hidden |
eza --git | add a git-status column |
eza --tree -L 2 | tree view, depth 2 |
eza -s modified | sort by mtime |
eza -s size | sort by size |
eza --group-directories-first | dirs on top (default in alias) |
ll -s modified -r | most recently changed first |
ll --git-ignore | hide gitignored files |
eza -laT --git-ignore -L 3 | tree, hidden, no gitignored, 3 deep |
eza -l --time-style=long-iso | ISO timestamps |
Colors come from LS_COLORS (set by vivid). Icons need a Nerd Font in your terminal.
procs | all your processes, Solarized columns, sort by PID |
procs zsh | regex search across PID/User/Command (replaces ps | grep) |
procs --tree | parent/child tree view |
procs --watch | refresh every 1s; --watch-interval N for custom |
procs --sortd UsageCpu | sort by CPU descending (asc = --sorta) |
procs --insert VmRss | add a column on the fly (kinds: procs --list) |
ps | → procs (default Solarized view) |
psh | → procs --load-config ~/.config/procs/procs-heavy.toml (CPU desc, trimmed) |
~/.config/procs/procs.toml | default config (in-repo, symlinked) |
~/.config/procs/procs-heavy.toml | trimmed columns + CPU-desc; loaded by psh |
Both TOMLs duplicate their [style.*] blocks; procs --load-config replaces the whole config (no inheritance). Edit both when tweaking colors.
procs on macOS only shows your own processes — Apple gates
cross-user visibility behind elevated privileges. To see system daemons (the
_appstore, _audiomxd, etc. that ps -ax shows), fall through to legacy:
\ps -ax or command ps -ax.
Pipes strip color (color_mode = "Auto"). Prefer procs <pat> over ps | grep <pat> — same result, colors preserved.
lnav file.log | open one file (auto-tails new lines) |
lnav dir/ | open every log file in a directory; merged by timestamp |
cmd | lnav | read from stdin (no positional arg) |
lnav -n file.log | headless / no UI (useful for format debugging) |
~/.config/lnav/configs/installed/solarized-dark.json | activates lnav's built-in Solarized Dark theme |
~/.config/lnav/formats/installed/inngest.json | JSON format for inngest-cli dev stdout |
~/.config/lnav/config.json | lnav's runtime mutable config (not in repo) |
Add new formats by dropping a JSON file in formats/installed/ — the installed/ dir is symlinked from the repo, so no bootstrap.sh re-run is needed. (lnav owns the rest of ~/.config/lnav/.) Theme is lnav's built-in (palette matches bat / delta / procs / btop); we don't redefine slots.
| q | quit |
| e / E | next / prev error |
| w / W | next / prev warning |
| /pat / n | search / next match |
| : | command prompt (e.g. :filter-in, :goto) |
| ; | SQL prompt — query log lines as a table (e.g. SELECT * FROM inngest_dev WHERE level = 'error') |
| t / i | time histogram / global histogram view |
| TAB | switch between log view and bottom prompt |
Bottom status bar shows the matched format name (e.g. inngest_dev) when an installed format matches the file.
cat file.rb | aliased to bat --paging=never |
bat file.rb | paged, with line numbers + git gutter |
bat -p file | plain (no decoration) |
bat -A file | show non-printables (whitespace, line endings) |
bat -r 10:50 file | range lines 10–50 |
bat -l json data | force language |
bat --diff file | only show modified hunks |
man <cmd> | uses bat -l man via $MANPAGER |
bat --list-themes | all themes |
bat --list-languages | supported languages |
bat --theme="Solarized (dark)" | force theme |
$BAT_THEME | env override |
$MANPAGER is set so man stays Solarized: sh -c 'col -bx | bat -l man -p --paging=always'. $MANROFFOPT=-c keeps ANSI sequences intact.
| Space / b | page down / up |
| /pat / n | search / next match |
| g / G | top / bottom |
| q | quit |
bat shells out to less by default — these are less keys.
less is a zsh function (defined next to the cat alias in
.zshrc) that delegates to bat. File arguments get bat's full
decoration (line numbers, git gutter); piped input falls back to bat --plain
so cmd | less doesn't get bat's STDIN header and stays clean.
$PAGER is not set to bat globally — git/delta
and other tools manage their own pagers. The wrapper only shadows interactive
less use.
less file.rb | bat with full decoration (paged) |
cat file | less | bat --plain (no header) |
command less +F log | real less follow-mode |
command less -R | real less raw control chars |
Inside the pager, all less keys work (Space/b, /pat, g/G, q) — bat shells out to less.
Wired through git config — anything that paginates a diff uses delta:
git diff | side-by-side or unified, syntax-highlighted |
git log -p | commit-by-commit diff |
git show <rev> | single commit |
git blame | styled blame |
git add -p | interactive — uses delta as the diff filter |
| n / N | next / prev file (navigate=true) |
| / / ? | search |
| Space / b | page down / up |
| q | quit |
Built on less, so less keys all work.
git diff --side-by-side | two-column layout |
delta --light | force light theme one-shot |
delta --no-gitconfig --diff-highlight | minimal preview |
git -c core.pager=cat diff | bypass delta one-shot |
Aliased over diff for ad-hoc file compares outside git. Git diffs still flow through delta (see above) — the two cover different surfaces.
diff a b | syntactic, language-aware diff between two files |
diff a/ b/ | recursive directory diff |
diff old.json new.json | tree-sitter parses each side; structural moves are recognized |
diffcommand diff a b | skip the alias (zsh built-in) |
\diff a b | same; backslash quotes the alias name |
/usr/bin/diff a b | absolute path bypasses $PATH |
Non-interactive shells (scripts, Make, CI) never see the alias — zsh aliases don't propagate without -i.
Difftastic uses ANSI 16-color directly; Ghostty's terminal palette is already Solarized Dark, so it inherits on-palette colors with no extra config. The default --background dark matches; nothing to wire.
Don't introduce delta, diff-so-fancy, or another diff renderer for non-git — they're line-based, not syntactic, and would duplicate what delta already does for git.
md alias
md renders markdown to ANSI via glow with a pinned Solarized
style:
alias md='glow --style $HOME/.config/glow/glamour.json'
bat / less / cat still show the source with
syntax highlighting; md shows the rendered output (headings, lists,
code-block themes).
--style, not glow.yml
glow on macOS reads its yaml from ~/Library/Preferences/glow/, not
~/.config/glow/. The alias passes --style directly so the
Solarized JSON in this repo (.config/glow/glamour.json) is used regardless.
Fenced code blocks inside markdown use chroma's solarized-dark theme to
stay on-palette.
md README.md | render to terminal |
md -p README.md | paged (uses $PAGER / less) |
mdp README.md | same as md -p (dedicated alias) |
md -w 100 README.md | force width 100 |
cat foo.md | md - | render from stdin |
command glow ... | bypass alias (no --style) |
Don't swap to mdcat / frogmouth without an explicit ask — mdcat was archived upstream 2025-01-10.
tspin file.log | open file (paged via less) |
tspin -f file.log | follow (live tail with highlights) |
cmd | tspin -p | pipe stdin, print to stdout (no pager) |
tspin -e 'kubectl logs -f pod' | run command, view paged output |
No tail alias and no t shortcut — tail / tail -n stay vanilla. tspin's CLI isn't a tail superset (no -n), so wrapping tail would break common uses.
theme.toml
tspin reads ~/.config/tailspin/theme.toml. It accepts ANSI color
names only (red, blue, bright_red, …);
Ghostty's Solarized Dark palette resolves them to hex via the canonical
Solarized terminal mapping:
red | red #dc322f |
bright_red | orange #cb4b16 |
bright_magenta | violet #6c71c4 |
bright_green | base01 #586e75 |
bright_cyan | base1 #93a1a1 |
Severity (error / warn / info / debug) is shipped as [[keywords]] blocks — tspin has no built-in groups for them.
Schema: github.com/bensadeh/tailspin/blob/main/src/theme/mod.rs. Override anything with extra [[keywords]] or block-level entries.
xh GET httpbin.org/get | basic GET; pretty-prints JSON in Solarized colors |
xh POST httpbin.org/post name=alice | JSON body via key=value shorthand (no --data) |
xh -a user:pass api.example.com | basic auth (HTTPie-compatible flag) |
xh --download url | save body to a file (filename inferred) |
xhs api.github.com/zen | HTTPS-default companion binary (no https:// needed) |
xh --style=monokai GET … | override the default Solarized theme per-invocation |
Solarized is pinned via ~/.config/xh/config.json:
{"default_options": ["--style=solarized"]}
xh --style accepts auto, solarized, monokai,
fruity. xh renders through syntect — the same library family as
bat — so highlighting matches the rest of the Solarized Dark setup.
No .zshrc alias here. Aliases in this repo are reserved for swapping commands; default flags belong in config files.
curl
curl is unchanged. Scripts, CI, and any non-interactive use keep calling
curl verbatim. xh/xhs are the interactive surface only.
Don't alias curl to xh, and don't introduce an http/https alias either — the binary's own name is the only entry point.
| Command | What it does |
|---|---|
hyperfine 'cmd' | 10 runs, no warmup, summary with mean/stddev |
hyperfine --warmup 3 'cmd' | discard 3 warmup runs first (cache, JIT) |
hyperfine 'a' 'b' | A/B compare; prints "X is N× faster than Y" |
hyperfine -N 'cmd' | skip shell wrapper; measure binary directly |
hyperfine --export-markdown out.md 'cmd' | share results in markdown |
time
time (zsh builtin / /usr/bin/time) is the right tool for
one-shot wall-clock measurements — scripts, CI, "did this finish quickly?".
Reach for hyperfine when you want warmups, multiple runs, variance
characterization, or A/B comparison.
Deliberately not aliased to time — semantics differ. See
CLAUDE.md for the catalog-rejection rationale.
First run includes filesystem cold-cache effects. Use --warmup N
or --prepare 'sync' to control. macOS purge needs
sudo if you want to flush page cache between runs.
One-line nudge after running a default tool whose modern alternative is installed and deliberately left unaliased. Fires at most once per zsh process, per command name. A fresh tmux pane resets the seen set.
| Default | Modern |
|---|---|
tail | tspin |
grep | rg (ripgrep) |
curl | xh (HTTPie-compatible) |
export MODERN_REMINDER=1 in ~/.zshrc turns it on
(set there by default). Comment that line — or unset MODERN_REMINDER
in a single shell — to silence.
Hooks register only in interactive shells, so scripts and CI never see them.
Implementation: _modern_reminder_preexec +
_modern_reminder_precmd in ~/.zshrc; tests in
scripts/test-modern-reminder.zsh (which holds a synced copy
of the function bodies — keep both in sync).
vivid generates a LS_COLORS string from a named palette. .zshrc exports it on startup:
command -v vivid >/dev/null 2>&1 && \ export LS_COLORS="$(vivid generate solarized-dark)"
Both eza and GNU ls read $LS_COLORS. BSD ls uses $LSCOLORS (different format) — left at OMZ default.
vivid themes | list available palettes |
vivid generate molokai | try another palette one-shot |
echo $LS_COLORS | tr ':' '\n' | inspect current rules |
vivid -m 8-bit generate solarized-dark | 256-color mode (24-bit is default) |
Solarized-dark is the pinned palette — switching is OK to try, but persist anything else only after asking.
| Ctrl-R | fuzzy history search |
| Ctrl-T | file picker → inserts paths at the cursor |
| Alt-C | UNBOUND — Alt is reserved for Polish diacritics |
Tab-complete works through fzf for kill **<Tab>, cd **<Tab>, ssh **<Tab>, etc.
| Ctrl-J / Ctrl-K | next / prev item |
| Tab / Shift-Tab | multi-select |
| Enter | accept |
| Esc / Ctrl-C | cancel |
'foo | exact match (single-quote prefix) |
!foo | negate |
^foo / foo$ | prefix / suffix anchor |
foo | bar | OR |
vim $(fzf) | pick a file, open in vim |
git checkout $(git branch | fzf) | branch picker |
fd | fzf --preview 'bat --color=always {}' | live preview |
history | fzf | manual history search |
FZF colors are pinned to Solarized via $FZF_DEFAULT_OPTS in .zshrc.
Replaces zsh's default Tab completion menu with an fzf picker — fuzzy filter as you type, arrow-key navigation, and per-command previews. Works for every command (cd, git, kill, ssh, brew, …).
For z with zoxide-ranked results, use zi <query> — fzf-tab won't show zoxide entries because zoxide init zsh doesn't register a _z completion function.
| Tab | accept selected completion |
| < / > | switch between completion groups (e.g. [heads] ↔ [tags] for git checkout) |
| Ctrl-Space | multi-select |
| / | continuous completion (useful for deep paths) |
| Esc | cancel |
cd <Tab> | directory picker with eza preview pane |
git checkout <Tab> | branches + tags grouped with headers |
kill <Tab> | process picker |
brew install <Tab> | formula picker |
Picker colors inherit $FZF_DEFAULT_OPTS via use-fzf-default-opts yes.
disable-fzf-tab | fall back to compsys for this shell |
enable-fzf-tab | re-enable |
toggle-fzf-tab | flip between the two |
| → (right arrow) | accept entire suggestion (end-of-line) |
| Ctrl-E | same — move to end of line (accepts) |
| Ctrl-F / M-f | forward by char/word — Alt is unbound here |
| Esc then Enter | discard suggestion, run only what you typed |
fg=8 (bright black) — readable on Solarized Dark.$PATH.It's the last sourced plugin — that's a hard requirement of the project, not a style choice. Sourcing it earlier breaks coloring of subsequent plugins' bindings.
# In ~/.zshrc.local (machine-local, untracked) typeset -A ZSH_HIGHLIGHT_STYLES ZSH_HIGHLIGHT_STYLES[command]='fg=#859900,bold' # green ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#dc322f' # red
Don't change the global Solarized palette — palette pins are intentional.
Full new-machine setup is in README.md → "Setup (new machine)". The steps below are the terminal-relevant subset.
# 1) Brew packages brew bundle --file=$PROJECTS_HOME/dotfiles/Brewfile # 2) Symlinks (idempotent) $PROJECTS_HOME/dotfiles/bootstrap.sh # 3) Wire delta into git (one-time, global) git config --global core.pager delta git config --global interactive.diffFilter "delta --color-only" git config --global delta.navigate true git config --global delta.line-numbers true git config --global delta.syntax-theme "Solarized (dark)" # 4) Verify brew bundle check --file=$PROJECTS_HOME/dotfiles/Brewfile --verbose echo $LS_COLORS | head -c 80 # should not be empty git log -p | head # should look styled
.zshrc (do not reorder)completion.zsh + key-bindings.zsh — binds ^Ibindkey -r '^[c' — remove fzf's Alt-C binding (Polish diacritics)zoxide init zshfzf-tab.zsh — needs fzf's ^I already bound; must be before any plugin that wraps widgetszsh-autosuggestions.zshzsh-syntax-highlighting.zsh — must be last