A fully configured terminal that feels fast and intentional — Catppuccin throughout, every classic Unix tool replaced with a modern equivalent, and a Starship prompt that surfaces your Claude AI session state (model, context usage, and cost) alongside git and language info.
Modern CLI tools are better than their ancient counterparts. This setup replaces:
| Legacy | Modern | Why |
|---|---|---|
ls |
eza |
icons, git status, tree view |
cat |
bat |
syntax highlighting, git gutter |
diff |
git-delta |
side-by-side, Catppuccin themed |
cd |
zoxide |
frecency-based jumping |
top |
btop |
beautiful, interactive |
df |
duf |
colorful disk usage |
du |
dust |
visual tree |
ps |
procs |
color, search, tree |
man |
tldr |
practical examples |
| file browser | superfile (spf) |
TUI, previews, themes |
| shell history | atuin |
encrypted sync, fuzzy search |
| Component | Tool |
|---|---|
| Shell | Zsh + Sheldon |
| Prompt | Starship (Catppuccin Mocha) |
| Terminal | Ghostty |
| Editor | Neovim (LazyVim + Catppuccin) |
| Font | JetBrains Mono Nerd Font |
| Theme | Catppuccin Macchiato |
| Window Manager | OmniWM (tiling, Hyprland/Niri-inspired) |
| Launcher | Vicinae (open-source Raycast replacement) |
| Runtime Manager | Mise (Node, Python, Go, Ruby, …) |
| History | Atuin (encrypted sync, fuzzy search) |
| System Info | Fastfetch (on every new session) |
eza bat delta zoxide superfile
fzf atuin btop duf dust
procs tldr ripgrep fd jq
| Tool | Purpose |
|---|---|
asitop |
Real-time CPU/GPU/ANE performance monitor |
mactop |
macOS system monitor (htop-style) |
batt |
Battery health + charging limit management |
| Tool | Purpose |
|---|---|
croc |
Send files between machines, end-to-end encrypted |
rclone |
Rsync for cloud storage |
aria2 |
Multi-protocol downloader with resume + segmenting |
| Tool | Purpose |
|---|---|
lazygit |
TUI git client |
lazydocker |
TUI Docker client |
gitui |
Rust-based TUI git client |
git-delta |
Syntax-highlighted diffs (Catppuccin Frappé) |
mole |
macOS ultimate cleaner TUI |
xcodes |
Install & switch Xcode versions |
mint |
Swift CLI package manager |
neovim |
Editor (LazyVim) |
workmux |
Git worktree + tmux window per task, for parallel Claude Code sessions |
Managed by Sheldon (~/.config/sheldon/plugins.toml):
| Plugin | Purpose |
|---|---|
zsh-autosuggestions |
Fish-style inline suggestions |
zsh-syntax-highlighting |
Command highlighting |
alias-tips |
Reminds you when an alias exists |
fzf-tab |
Fuzzy tab completion |
The prompt is the main event. Every segment is designed to be visible when you need it and invisible when you don't.
Surfaces your active Claude Code session at a glance:
| Pill | Shows | Appears when |
|---|---|---|
| Model | Active Claude model (e.g. sonnet-4) |
Claude session is running |
| Context | Window usage gauge — color shifts yellow at 60%, red at 80% | Always (during session) |
| Cost | Accumulated session cost | After $0.10 spent |
These pills are Claude Code's own statusline: the claude-code profile in starship.toml,
rendered by starship statusline claude-code. Claude Code finds that command through the
statusLine key in ~/.claude/settings.json. That file isn't stowed, because Claude Code
rewrites it for permissions, plugins and MCP servers, so install.sh merges in just that
one key with jq. linux-dot-files sets the same key with a chezmoi modify_ template.
| Segment | Content |
|---|---|
| Claude | Model · context gauge · cost |
| Git | Branch + status (modified, staged, untracked, ahead/behind) |
| Languages | Swift, Node, Bun, Go, Python, Rust, Java, Kotlin — auto-detected per directory |
| Duration | Command execution time (shown for slow commands) |
| Exit status | Checkmark or ✗ on non-zero exit |
| Time | Current time (right side) |
One command sets up everything on a new machine — Homebrew, packages, Sheldon plugins, dotfiles, and mise runtimes:
curl -fsSL https://raw.githubusercontent.com/seifscape/macos-dot-files/main/bootstrap.sh | zshOr clone first if you prefer to inspect before running:
# SSH
git clone git@github.com:seifscape/macos-dot-files.git ~/path/to/macos-dot-files
# HTTPS
git clone https://github.com/seifscape/macos-dot-files.git ~/path/to/macos-dot-files
cd ~/path/to/macos-dot-files
./bootstrap.shBootstrap will prompt for your git identity (name + email) and write it to ~/.gitconfig.local which is not tracked.
If you'd rather step through it yourself:
- macOS (Apple Silicon recommended)
- Homebrew
- Xcode Command Line Tools:
xcode-select --install
# SSH
git clone git@github.com:seifscape/macos-dot-files.git ~/path/to/macos-dot-files
# HTTPS
git clone https://github.com/seifscape/macos-dot-files.git ~/path/to/macos-dot-files
cd ~/path/to/macos-dot-filesbrew bundle --file=homebrew/BrewfileTo regenerate the Brewfile after installing new things, use brewdump
(defined in zsh/.aliases) rather than brew bundle dump directly:
brewdumpA plain brew bundle dump crashes on this machine. aube shims npm and
its npm list -g --json returns a JSON array where real npm returns an
object, so Homebrew's npm extension dies with no implicit conversion of String into Integer. brewdump prefixes PATH with /opt/homebrew/bin
to put real npm first, and strips the mas ..., id: 0 lines that TestFlight
apps produce — those are not App Store purchases, so brew bundle install
can never satisfy them.
sheldon lock./install.shbrew install --cask font-jetbrains-mono-nerd-fontSet JetBrains Mono Nerd Font as your terminal font in Ghostty (already set in the included config).
# Catppuccin theme for git-delta
git clone https://github.com/catppuccin/delta.git ~/.config/delta-themes
# Git identity (not tracked — create on each machine)
cat > ~/.gitconfig.local << 'EOF'
[user]
name = Your Name
email = you@example.com
EOFmacos-dot-files/
├── install.sh # stows symlinks into ~/ (safe to re-run)
├── bootstrap.sh # full new-machine setup
├── zsh/ # .zshrc .zshenv .zprofile
│ # .aliases .exports .functions .zsh_bindings
├── git/ # .gitconfig .gitignore_global
├── nvim/ # .config/nvim/ — LazyVim + Catppuccin + plugins
├── sheldon/ # .config/sheldon/plugins.toml
├── delta/ # .config/delta/ — delta pager config (Catppuccin Frappé)
├── starship/ # starship.toml — Catppuccin Mocha + Claude segment
├── ghostty/ # terminal config + Catppuccin icon + starfield shader
├── tmux/ # .tmux.conf .config/tmux/tmux.reset.conf
├── superfile/ # .config/superfile/config.toml — Catppuccin Frappé, shared with Linux
├── atuin/ # .config/atuin/config.toml
├── btop/ # .config/btop/ — btop.conf + Catppuccin Frappé theme
├── cliamp/ # .config/cliamp/ — config.toml + Catppuccin Frappé theme, ClassicPeak visualizer
├── fastfetch/ # .config/fastfetch/ — Catppuccin Frappé, shared with Linux
├── gh-dash/ # .config/gh-dash/config.yml
├── mise/ # .config/mise/config.toml
├── omniwm/ # .config/omniwm/settings.toml — tiling WM
├── vicinae/ # .config/vicinae/settings.json — launcher
├── homebrew/ # Brewfile
└── scripts/ # dev-updates.sh
install.sh deploys symlinks, so each file in $HOME points back here:
~/.zshrc -> ~/Developer/macos-dot-files/zsh/.zshrc
There is only one real copy of every file. Editing ~/.zshrc and editing
zsh/.zshrc are the same action — there is no sync step, and git status in
this repo always reflects what is actually live on the machine.
Editing an existing file — edit either path, then commit from this repo.
Adding a new file — create it inside the package directory, never directly
in $HOME, then re-stow:
vim zsh/.zlogin # in the repo
./install.sh # re-stows everything; safe to re-runA config created directly in $HOME is invisible to git and becomes the next
thing to silently drift out of sync.
Watch for tools that replace symlinks. Some editors' atomic-save, and
sed -i '' on macOS, write a new file and rename it over the target — which
turns the symlink into a regular file, silently detaching it from the repo.
Check at any time with:
for f in ~/.zshrc ~/.zshenv ~/.zprofile ~/.aliases ~/.exports ~/.functions \
~/.zsh_bindings ~/.gitconfig ~/.gitignore_global ~/.tmux.conf; do
[ -L "$f" ] || echo "detached (should be a symlink): $f"
doneAnything printed is a real file that has drifted; re-run ./install.sh to
relink it. Note this discards whatever diverged, so diff it against the repo
copy first if the local version might hold changes worth keeping.
Adding a new tool — a config not yet tracked at all needs a package
directory that mirrors its path relative to $HOME, plus an entry in the
PACKAGES list in install.sh. Using zellij (~/.config/zellij/config.kdl)
as the example:
cd ~/Developer/macos-dot-files
# 1. mirror the path relative to $HOME
mkdir -p zellij/.config/zellij
# 2. MOVE the existing config in -- do not copy. stow refuses to overwrite
# a real file at the target, so a leftover original blocks the package.
mv ~/.config/zellij/config.kdl zellij/.config/zellij/
# 3. register it in install.sh
# PACKAGES=(zsh git nvim ... zellij)
# 4. deploy and commit
./install.sh
git add zellij install.sh && git commit -m "Track zellij config"Skipping step 3 does not fail loudly on its own, so install.sh warns about
any package directory missing from PACKAGES:
[warn] Unregistered directory (not in PACKAGES): zellij
Directories that live here without being deployed (screenshots, .claude)
are listed in NOT_PACKAGES and stay silent.
Deliberately untracked: ~/.gitconfig.local holds your git identity so it
never reaches GitHub. It is .gitignored and must be recreated on each machine
— see Manual steps.
The Pi 5 and the UM890 run the same terminal from
linux-dot-files, a chezmoi
repo that pulls these files straight from main here instead of keeping its
own copies:
zsh/.aliases zsh/.functions zsh/.exports zsh/.zsh_bindings
starship tmux nvim atuin btop delta gh-dash scripts/dev-updates.sh
ghostty/.config/ghostty/{config,shaders/space.glsl} superfile/.config/superfile/config.toml
git/.gitignore_global
So those files have to stay portable. macOS-only lines go behind
[[ $OSTYPE == darwin* ]] (see the bottom of .aliases), clipboard goes
through clip rather than pbcopy, and paths use $HOME, never
/Users/.... Push here, then chezmoi update on a server picks it up.
Two pieces, each running independently.
| Piece | Config | Role |
|---|---|---|
| OmniWM | ~/.config/omniwm/settings.toml |
tiling, workspaces, borders, quake terminal |
| Vicinae | ~/.config/vicinae/settings.json |
launcher / command palette (Ctrl+Space) |
OmniWM replaced AeroSpace, and absorbed JankyBorders along with it — both are uninstalled. It ships its own focus ring, so a separate borders daemon is redundant. It also ships a workspace bar, a menu bar hider and a Ghostty quake terminal, which is the SketchyBar job that was never worth wiring up by hand.
Requires macOS 26+ on Apple Silicon, and grants of Accessibility and Input Monitoring on first launch. Never needs SIP disabled.
defaultLayoutType = "dwindle" — Hyprland-style binary space partitioning, where
each new window bisects the focused tile along its wider axis. The [niri]
table is left at defaults, so Option+Shift+L flips a single workspace over to
scrolling columns without touching the config.
Note the settings file is generated: OmniWM owns the formatting and rewrites it
whenever you change something in its settings UI. Stow links the directory
(~/.config/omniwm → this repo), not the file, so those rewrites land in the
repo as a normal git diff and nothing clobbers the symlink. Hand-written
comments in it will not survive.
Catppuccin mauve, matching the ring JankyBorders used to draw:
[borders]
enabled = true
width = 3.0
[borders.color]
red = 0.7764705882352941 # #c6a0f6
green = 0.6274509803921569
blue = 0.9647058823529412
alpha = 1.0Corner radii are derived per window automatically, so it follows rounded window
shapes without a style key. Two things JankyBorders did that this cannot:
there is no inactive-window colour (the struct is enabled/width/color, one
colour only), and there is no blacklist — the Simulator and Droppy get a ring
again. Width is 3.0 rather than the old 6.0 because JankyBorders ran
hidpi=off and drew at 1x; OmniWM rounds to physical pixels, so half the number
is the same apparent weight.
Vicinae replaces Raycast — same command-palette shape, open source, native,
and no account or subscription attached to it. Bound to Ctrl+Space, themed
catppuccin-mocha to match everything else.
Its config is ~/.config/vicinae/settings.json, stowed from this repo like
everything else. Worth knowing: Vicinae writes to that file itself whenever you
change a setting through the GUI, and it writes through the symlink — so GUI
tweaks land in the repo and show up as a normal git diff. The tradeoff is that
Vicinae owns the formatting, so hand-written comments in that file get rewritten
away. If you want commentary that survives, put it in a separate file and list
it in the imports array.
vicinae config default # dump the full default config to see what is settableStock defaults except focus and move, which are remapped from arrow keys to
hjkl. The reason is macOS, not muscle memory: a global hotkey on Option+←/→
would eat word-wise cursor movement, and Option+Shift+←/→ word-wise
selection, in every text field on the machine. The cost is the dead-key glyphs
˙ ∆ ˚ ¬, which is the cheaper side of that trade.
Control+Option+L holds toggleWorkspaceLayout, moved off Option+Shift+L
where it collided with the new move.right.
| Key | Action |
|---|---|
alt + h j k l |
Focus window left / down / up / right |
alt-shift + h j k l |
Move window in that direction |
alt + 1…9 |
Switch to workspace |
alt-shift + 1…9 |
Move window to workspace |
ctrl-alt + 1…9 |
Focus column n |
ctrl-alt-tab |
Back and forth between last two workspaces |
alt-tab |
Focus previously focused window |
alt-enter |
Toggle fullscreen |
alt-t |
Toggle column tabbed — WM-level tabs |
alt-grave |
Quake terminal (Ghostty) |
alt-shift-o |
Overview |
ctrl-alt-l |
Flip workspace layout (dwindle ↔ niri) |
ctrl-alt-space |
Command palette |
ctrl-alt-m |
Open menu anywhere |
alt + - / = |
Container span −10% / +10% |
alt-shift + - / = |
Window span −10% / +10% |
alt + , / . |
Cycle preset size back / forward |
alt-shift-f |
Container to full span |
alt-shift-b |
Balance sizes |
alt-shift-r |
Raise all floating windows |
alt-home / alt-end |
Focus first / last column |
ctrl-cmd-tab |
Focus next monitor |
67 of 169 bindings are assigned; the rest arrive Unassigned — ten scratchpad slots,
preselect in four directions, per-monitor moves, toggleFocusedWindowFloating.
Fill them in [[hotkeys]] by id.
Nothing here uses a bare cmd, so app shortcuts are untouched, and nothing uses
a bare ctrl+arrow, so macOS Move left/right a space and Mission Control keep
working. routing.mode = "macOS" means OmniWM's workspaces ride on native
Spaces rather than replacing them.
alt-t is the one worth knowing. Ghostty's native macOS tabs are separate
NSWindows stitched into a tab group, so a single window with six tabs looks
like six windows to any Accessibility-API window manager — the thing that made
AeroSpace unusable here. OmniWM's own tabbed columns are the way around it:
stop using cmd-t, and let the WM stack windows into a tab group it understands.
Apps that resize themselves or open as panels fight tiling — float them.
[[appRules]]
bundleId = "com.example.app"
layout = "float"Also available per rule: titleRegex, assignToWorkspace, minWidth /
minHeight, and initialContainerPrimarySpan. Settings live-reload on save.
| Command | Action |
|---|---|
z <dir> |
Jump to frecent directory (zoxide) |
Ctrl+R |
Atuin fuzzy history search |
Ctrl+T |
fzf fuzzy file picker |
spf |
Superfile TUI file browser (Catppuccin Frappé; alias loads the repo config) |
workmux add <name> -p "<task>" |
New worktree, branch and tmux window with a Claude session; workmux merge when done |
ll |
eza long list with icons |
la |
eza long list including hidden |
lt |
eza tree view |
lg |
git-aware eza listing (shows git status if in a repo) |
lazygit |
TUI git client |
gd / gds |
Delta-powered diffs |
hist |
Interactive atuin history search |
fbat |
fzf file picker with bat preview |
dev-refresh |
Update brew + mise |
batpick <file> |
Interactively pick a bat theme with fzf |
- tmux setup (
.tmux.conf,tmux.reset.conf) and the plugins catppuccin-tmux, tmux-sessionx and tmux-floax come from omerxx/dotfiles. - The starfield behind Ghostty (
space.glsl) is a shader from omerxx/dotfiles. - Colours: Catppuccin.
