My Terminal Setup in 2025
I’ve been tweaking my terminal setup for years. At some point this year I realized I hadn’t touched my config in months, which probably means I’m done. So here’s what I landed on.
Ghostty
I used iTerm2 forever, tried Alacritty for a while (too barebones), tried WezTerm (Lua config was fun but I don’t need that), and then Ghostty came along and I just stopped looking.
It’s Mitchell Hashimoto’s project. GPU-accelerated, written in Zig, uses native AppKit on macOS. The thing that got me is that I haven’t configured it at all. Zero lines of config. Fonts look right, colors look right, it feels like a native Mac app because it is a native Mac app. I open it and forget it exists, which is exactly what a terminal emulator should do.
Fish
I came from zsh. The switch to fish happened a few years ago and at this point I couldn’t go back. Autosuggestions, syntax highlighting, completions. It all just works without sourcing 40 plugins through oh-my-zsh.
The core of my config is tiny:
set -g fish_greeting ''
set -gx EDITOR nvim
set -gx VISUAL nvim
fish_vi_key_bindings
Vi bindings in fish are actually good, with proper modal editing and cursor shape changes. Since I live in Neovim the rest of the time, having the same mental model in the shell matters more than I expected.
For the prompt I use Tide through Fisher. It’s async, which sounds like a minor thing until you work on a monorepo and your prompt hangs for 200ms on every command because it’s synchronously running git status. Tide does that in the background and updates when it’s ready.
Replacing the Classics
This is the part of my config I enjoy the most. I alias basically every classic Unix tool to a modern Rust/Go rewrite:
alias cat "bat --paging=never"
alias ls "eza --group-directories-first --icons"
alias la "eza -la --icons"
alias ll "eza -l --icons"
alias du "dust"
alias df "duf"
alias ping "gping"
alias ps "procs"
alias dig "doggo"
The big ones:
bat gives you syntax highlighting on cat. I turn paging off because I pipe things around a lot, but when I actually want to read a file it uses less with color support (BAT_PAGER='less -RF').
eza is a fork of the now-abandoned exa. Icons, git status per file, directories grouped first. It’s one of those things where you go back to regular ls on a server and it feels broken.
dust and duf for disk usage. dust gives you a visual tree sorted by size (way better than piping du through sort), and duf formats mount points into a readable table instead of whatever df -h thinks is helpful.
gping draws a latency graph in real time. Sounds gimmicky but it’s not. When you’re debugging network stuff, seeing the pattern is 10x more useful than watching numbers scroll.
procs and doggo replace ps and dig respectively. Both do the same thing as the originals but with readable output and color. doggo also supports DNS-over-HTTPS which is occasionally useful.
zoxide
zoxide deserves its own section. It tracks directories you visit and lets you jump to them with partial matches based on frecency (frequency + recency).
zoxide init --cmd cd fish | source
I alias it to cd directly so I don’t have to think about it. After a week of usage, cd proj takes me to ~/Developer/projects and cd api goes to whichever API folder I’ve been in recently. I forget this isn’t how cd normally works until I’m on someone else’s machine.
fzf
fzf is the fuzzy finder. Ctrl+R for history search, Ctrl+T for files, Alt+C for directories. I don’t have much to say about it. Once you use it you can’t imagine not having it. The fish integration is one line:
fzf_configure_bindings
Everything Else
direnv loads .envrc files automatically when you cd into a project. No more source .env or forgetting which Python virtualenv you’re supposed to be in.
direnv hook fish | source
mise manages language runtimes. Node, Python, Go, whatever. One .mise.toml per project, everything pinned. It replaced nvm + pyenv + a dozen other version managers I was tired of maintaining separately. It’s compatible with asdf plugins but faster and doesn’t need a shell hook that slows down startup.
uv for Python packaging. It’s from the Astral team (same people behind Ruff) and it’s comically fast compared to pip. I mostly use it for quick uvx one-off runs and managing virtualenvs.
The whole config is ~95 lines of fish. No framework, no plugin manager beyond Fisher, no shell startup that takes half a second. Fish gives you enough out of the box that you don’t need much on top of it. Just point the aliases at better tools and get out of the way.