Skip to main content

Introduction

Let’s be honest: the default terminal experience on macOS and Linux is… rough. Like, really rough. You open Terminal.app on a Mac and you’re greeted with this bland, uninspiring blank rectangle that feels like it was designed in 2003 (spoiler: it basically was). The default shell prompt tells you almost nothing useful, the colors make reading logs an eye-straining nightmare, and don’t even get me started on trying to split panes or manage multiple sessions. Ugly terminal And sure, you could live with it. Plenty of people do. But why would you want to when you spend half your day in that thing? I’ve been down the terminal customization rabbit hole more times than I’d like to admit. I’ve tried iTerm2, Alacritty, Kitty, Warp, and probably a dozen others I’ve forgotten about. I’ve cycled through Zsh frameworks, tried Nushell for a hot minute, and even flirted with exotic shells that promised to revolutionize my workflow. But I think I’ve finally landed on a setup that actually works. It’s fast, it looks good, it’s packed with genuinely useful features, and most importantly, it gets out of my way when I need to get stuff done. Intro So here’s the deal: I’m going to walk you through my current terminal setup that works beautifully on both macOS and Linux. We’ll start with GhostTTY (the new kid on the block that’s actually worth the hype), configure a shell environment that doesn’t suck, set up NeoVim with LazyVim for those times when you need a proper editor, and top it all off with Claude Code integration because, let’s face it, AI assistance in your terminal is pretty sweet. Ready? Let’s turn that sad default terminal into something you’ll actually enjoy using.

My palette of choice

Catppuccin Before we dive into specific tools, let’s talk about something that ties everything together: color schemes. I’ve tried dozens over the years: Dracula, Nord, Gruvbox, Solarized, One Dark. And while they’re all decent, I kept coming back to one: Catppuccin. The Mocha variant to be exact. Catppuccin is a community-driven pastel theme that describes itself as “for the high-spirited,” and honestly? That’s pretty accurate. The colors are soft without being washed out, vibrant without being eye-searing. It’s the kind of palette you can stare at for 10 hours straight and not feel like your retinas are being assaulted. What really sold me on Catppuccin is the consistency. This thing has ports for everything. Your terminal? Covered. NeoVim? Of course. VS Code, JetBrains IDEs, Slack, Discord, Firefox, Chrome, Obsidian, Spotify, even your system themes. There’s probably a Catppuccin port for it. That means my entire desktop environment has the same cohesive look, which sounds superficial until you realize how jarring it is to jump between apps that all look completely different. Catppuccin I run Mocha everywhere. It has deep, rich backgrounds with colors that pop just enough to differentiate syntax elements without turning your code into a rainbow explosion. You’ll see it referenced throughout this setup guide because, like I said, consistency is king.

My preferred font: Fira Code

Talking about consistency, I love having matching fonts in all my daily tools. A good monospace font might seem like a minor detail, but when you’re staring at code for hours, it makes a real difference. Fira Code has been my go-to for years. It’s a free, open-source monospaced font designed specifically for developers, and its killer feature is ligatures. You know how operators like ->, <=, !=, or === are technically multiple characters but represent single logical concepts? Fira Code renders them as unified glyphs. It sounds gimmicky until you try it. Suddenly your code just looks cleaner and is easier to scan. But here’s the thing: if you want fancy icons in your terminal prompt (git branch symbols, folder icons, language logos), you need more than just Fira Code. You need the Nerd Font version. Nerd Fonts is a project that patches popular programming fonts with thousands of additional glyphs from icon sets like Font Awesome, Devicons, Octicons, and Powerline symbols. The Fira Code Nerd Font gives you all the ligature goodness plus the icons that make modern shell prompts and file explorers look gorgeous. Installing is straightforward:
# Mac
brew install --cask font-fira-code-nerd-font

# Ubuntu/Debian
sudo apt install fonts-firacode
# For the Nerd Font version, download from the releases page
# or use the official install script:
curl -fsSL https://raw.githubusercontent.com/ryanoasis/nerd-fonts/master/install.sh | bash -s -- FiraCode

Other solid options

Fira Code isn’t the only game in town. Here are some other excellent monospace fonts worth considering:
  • JetBrains Mono - Created by JetBrains, excellent ligatures and optimized for reading code. Very popular.
  • Ubuntu Mono - Clean and highly readable, the default on Ubuntu systems.
  • Cascadia Code - Microsoft’s modern font with ligatures, designed for Windows Terminal.
  • Source Code Pro - Adobe’s take on a programming font, clean and professional.
  • Hack - Designed for source code, great readability at small sizes.
All of these have Nerd Font versions available, so pick whatever looks best to you. The important thing is to actually choose a font rather than settling for whatever your system defaults to.

GhostTTY: The Terminal that finally gets it right

Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration. It was created by Mitchell Hashimoto who co-founded Hashicorp and is definitely worth a follow!
# Mac
brew install --cask ghostty

# Ubuntu using snap
snap install ghostty --classic
For more options, head to the install guide. One great thing about Ghostty is the simplicity of the configuration file. Here is mine:
theme = catppuccin-mocha.conf
font-size = 19
font-family = FiraCode Nerd Font Mono
mouse-hide-while-typing = true
window-decoration = true
macos-option-as-alt = true

window-padding-x=12
window-padding-y=12
window-padding-balance=true
Feel free to tweak it as you need. The config references the catppuccin-mocha.conf available in my dotfiles repository that should be put in ~/.config/ghostty/themes/.

which shell?

Now that we have a proper terminal emulator, let’s make what happens inside it actually useful.

fish

fish (friendly interactive shell) is my shell of choice. It’s actively maintained and is a modern alternative to bash or zsh that prioritizes usability out of the box. It comes with syntax highlighting, intelligent autosuggestions from your command history, and robust tab completions without any configuration. Installing and switching to fish is straightforward:
# On macOS
brew install fish

# On Ubuntu/Debian
sudo apt install fish

# Make it your default shell
chsh -s $(which fish)
Log out and back in, and you’re running fish. Unlike other shells that drop you into an empty config file, fish just works immediately. The syntax highlighting and autosuggestions kick in on first launch without any setup. However, I’ve seen rare occasions where some features were not working when launched as the default shell. So instead of changing my user shell, I just launch fish over zsh within Ghostty:
command = /opt/homebrew/bin/fish
The main trade-off with fish is that it intentionally breaks from POSIX shell syntax. This means scripts and commands copied from most online tutorials won’t work directly in fish unless you run them through bash. For me, the improved interactive experience is worth it, and I can always run bash -c "command" for POSIX-specific scripts when needed.
My whole config.fish file: See on Github

Zsh: The macOS default

If you prefer to stick with something more traditional, Zsh has been the default shell on macOS since Catalina. You’re probably already using it if you’re on a Mac. Zsh offers better tab completion than Bash, more powerful globbing, shared history across sessions, and a massive ecosystem of plugins and themes through frameworks like Oh My Zsh. It’s a solid choice if you want something more powerful than Bash while maintaining POSIX compatibility. On Ubuntu or other Linux distros still running Bash, switching to Zsh is easy:
# Install Zsh
sudo apt install zsh

# Make it your default shell
chsh -s $(which zsh)
My whole .zshrc file: See on Github

Starship: A prompt that actually helps

Your shell prompt is prime real estate. The default username@hostname:~$ tells you almost nothing useful. Starship fixes that. Starship is a minimal, blazing-fast prompt written in Rust. It automatically detects what you’re working on and shows relevant context: git branch and status, current language versions, cloud provider profiles, Kubernetes context or whatever’s relevant to your current directory.
# Mac
brew install starship

# Linux
curl -sS https://starship.rs/install.sh | sh
Then add this to the end of your fish config (~/.config/fish/config.fish):
starship init fish | source
For Zsh users, add this to ~/.zshrc instead:
eval "$(starship init zsh)"
Configuration lives in ~/.config/starship.toml. Starship The above screenshot shows that I am in my devcenter project/folder on the my-terminal git branch with unstaged changes ([?]). It shows my package.json version (1.0.0) as well as my node.js runtime version. Especially useful if you are switching node versions with nvm to get rid of compatibility issues between major releases. Here’s my whole config:
# Use Catppuccin Mocha palette
palette = "catppuccin_mocha"

# Don't add a newline at the start of the prompt
add_newline = false

# Minimal left prompt
format = """$directory$character"""

# Everything else goes on the right
right_format = """$git_branch$golang$aws"""

[character]
success_symbol = "[➜](bold green)"
error_symbol = "[✗](bold red)"

[directory]
truncation_length = 3
truncate_to_repo = true

[git_branch]
format = "[$symbol$branch(:$remote_branch)]($style) "

[aws]
format = '[$symbol($profile )(\($region\))]($style) '
symbol = "󰸏 "
style = "bold blue"

# Catppuccin Mocha colors
[palettes.catppuccin_mocha]
rosewater = "#f5e0dc"
flamingo = "#f2cdcd"
pink = "#f5c2e7"
mauve = "#cba6f7"
red = "#f38ba8"
maroon = "#eba0ac"
peach = "#fab387"
yellow = "#f9e2af"
green = "#a6e3a1"
teal = "#94e2d5"
sky = "#89dceb"
sapphire = "#74c7ec"
blue = "#89b4fa"
lavender = "#b4befe"
text = "#cdd6f4"
subtext1 = "#bac2de"
subtext0 = "#a6adc8"
overlay2 = "#9399b2"
overlay1 = "#7f849c"
overlay0 = "#6c7086"
surface2 = "#585b70"
surface1 = "#45475a"
surface0 = "#313244"
base = "#1e1e2e"
mantle = "#181825"
crust = "#11111b"
The key here is keeping the left side minimal (just directory and prompt character) while putting contextual info on the right where it doesn’t get in the way.
Some modules may take too long to evaluate. If the prompt, feel sluggish, run env STARSHIP_LOG=trace starship timings to get a detailed trace of the execution.
Starship Timings

eza: ls, but actually good

The default ls command is… fine. But eza is better in every way. It’s a modern replacement with colors, icons, git status integration, and sensible defaults.
# Mac
brew install eza

# Ubuntu/Debian
sudo apt install eza
I alias ls to eza so it’s automatic. In fish, add this to your ~/.config/fish/config.fish:
if command -v eza &> /dev/null
    alias ls='eza -lh --group-directories-first --icons=auto'
    alias lsa='ls -a'
    alias lt='eza --tree --level=2 --long --icons --git'
    alias lta='lt -a'
end
For Zsh users (~/.zshrc):
if command -v eza &> /dev/null; then
  alias ls='eza -lh --group-directories-first --icons=auto'
  alias lsa='ls -a'
  alias lt='eza --tree --level=2 --long --icons --git'
  alias lta='lt -a'
fi
eza Now ls gives you a beautiful, informative file listing. lt shows a tree view with git status. Once you’ve used it, going back to plain ls feels painful.

fzf: Fuzzy find everything

fzf is a general-purpose fuzzy finder, and once you start using it, you’ll wonder how you ever lived without it. It lets you interactively filter any list: files, command history, processes, git branches, you name it.
# Mac
brew install fzf

# Ubuntu/Debian
sudo apt install fzf
Set up the key bindings and shell integration. For fish:
# Set up fzf key bindings
fzf --fish | source
For Zsh:
# Set up fzf key bindings and fuzzy completion
source <(fzf --zsh)
Now you get:
  • Ctrl+R - Fuzzy search through your command history (game changer)
  • Ctrl+T - Fuzzy find files and insert the path
  • Alt+C - Fuzzy find directories and cd into them
fzf The history search alone is worth the install. Instead of mashing the up arrow trying to find that docker command you ran three days ago, just hit Ctrl+R and type a few characters.

zoxide: cd that remembers

zoxide is a smarter cd command. It tracks which directories you visit most frequently and lets you jump to them with minimal typing.
# Mac
brew install zoxide

# Ubuntu/Debian
curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh
For fish, add this to your config:
zoxide init fish | source
For Zsh:
eval "$(zoxide init zsh)"
Now instead of typing cd ~/projects/company/frontend/src/components, you just type z components and zoxide figures out which “components” directory you probably mean based on your history. The more you use it, the smarter it gets. zoxide Combine it with fzf for interactive selection: zi opens a fuzzy finder with your most-visited directories.

My aliases

Beyond the tool-specific aliases above, here are some shortcuts I use constantly. In fish:
# Directory navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'

# Tools
alias d='docker'
alias v='nvim'

# Git shortcuts
alias g='git'
alias gcm='git commit -m'
alias gcam='git commit -a -m'
alias gcad='git commit -a --amend'

# Upsun deployment
alias u='upsun'
alias ud='git push upsun && upsun deploy'
For Zsh, the syntax is identical, just put these in your ~/.zshrc instead. The philosophy here is simple: if you type something more than a few times a day, alias it. Your fingers will thank you.

Bonus: Nushell

If you’re feeling adventurous and want to try something completely different, check out Nushell. It’s a modern shell that treats everything as structured data instead of text strings. Instead of piping text through grep and awk and hoping you got the parsing right, Nu pipelines work with actual typed data. You can work with JSON, YAML, SQLite, even Excel files natively. Error messages actually tell you what went wrong and where. nu I’m not using it as my daily driver yet. Too much muscle memory in Zsh and nushell is not POSIX compatible. But it’s genuinely impressive and worth experimenting with if you do a lot of data wrangling in the terminal.
# Mac
brew install nushell

# Ubuntu/Debian
cargo install nu
# or download from GitHub releases

NeoVim + LazyVim: When you need real editing power

nvim Let’s be real: learning Vim is hard. The keybindings are completely alien if you’ve spent your life in GUI editors, and the learning curve is steep enough to make most people quit within the first hour. I’m still on that journey myself. I regularly switch between VS Code forks like Cursor and Anti Gravity for my daily work while slowly building up Vim muscle memory. Some days I’m productive in NeoVim; other days I just need to get stuff done and reach for what I know. That’s fine. It’s a marathon, not a sprint. That said, if you want to try NeoVim without spending weeks configuring it from scratch, LazyVim is the answer. It’s a pre-configured NeoVim setup that gives you a modern IDE experience out of the box: file explorer, fuzzy finding, LSP support, git integration, syntax highlighting. All the things you’d expect, ready to go.
# Mac
brew install neovim

# Ubuntu/Debian
sudo apt install neovim

# Then install LazyVim (backs up your existing config)
git clone https://github.com/LazyVim/starter ~/.config/nvim
Launch nvim and let it install all the plugins. First startup takes a minute, but after that it’s instant. The best resource I’ve found for getting started is this video by Elijah Manor:
It walks you through the LazyVim setup and the essential keybindings you need to be productive. Bookmark it, you’ll reference it often in your first few weeks. A few tips for the learning phase:
  • Start with :Tutor - NeoVim has a built-in tutorial. Run it. Actually do the exercises.
  • Use Vim keybindings in your regular editor first - Both VS Code and JetBrains IDEs have excellent Vim plugins. Build muscle memory without sacrificing productivity.
  • Don’t try to learn everything at once - Master hjkl navigation, then w/b/e for word movement, then gradually add more.
  • Keep a cheat sheet handy - You’ll need it for months. That’s normal.
The payoff is real, though. Once the keybindings become muscle memory, editing text in NeoVim feels like flying. And since it runs in your terminal, it fits perfectly into this whole setup.

Claude Code: Your AI Programmer Buddy in the Terminal

Claude Code I’m sure you’ve heard about Claude Code at this point … This is the AI assistant I have been using for a year now and without going into a lengthy debate on which one is the best, here is a quick run-through: Claude Code works directly with your codebase: reading files, making edits, running commands, and creating commits. All from your terminal.
# Mac
brew install claude-code

# Linux (via npm)
npm install -g @anthropic-ai/claude-code
Run claude in any project directory, authenticate, and you’re good to go.
Pro tip: create a CLAUDE.md file in your project root describing your codebase and conventions. Review my other article on the topic for a detailed walkthrough.

Integrating with NeoVim

claude-code.nvim brings Claude Code directly into NeoVim. Add this to your LazyVim config:
return {
  "greggh/claude-code.nvim",
  dependencies = { "nvim-lua/plenary.nvim" },
  keys = {
    { "<leader>ac", "<cmd>ClaudeCode<cr>", desc = "Toggle Claude Code" },
  },
  config = function()
    require("claude-code").setup({
      window = {
        position = "float",
        float = { width = "80%", height = "80%", border = "rounded" },
      },
      refresh = { enable = true, show_notifications = true },
    })
  end,
}
Hit <leader>ac and Claude Code pops up in a floating window. When it makes changes to files, NeoVim automatically reloads them. The workflow is seamless: edit, toggle Claude, describe what you need, watch it work, and you’re back to editing with changes already applied. No context switching. If you’re using a different AI assistant (or want to use multiple), check out avante.nvim. It’s a Cursor-like experience for NeoVim that supports Claude, GPT, Gemini, and local models.

Conclusion

Look, I’m not going to tell you this is the “perfect” setup or the “only” way to configure your terminal. There’s no such thing. Your workflow is different from mine, your muscle memory is different, and what drives you crazy might not bother me at all. But what I will tell you is this: investing time in your terminal setup is absolutely worth it. You’re going to spend thousands of hours in this environment over your career. Making it fast, pleasant, and actually helpful isn’t being picky. It’s being smart. And I love the occasional “Oh wait. How did you do that?”
You can find all my configurations on my dotfiles Github repository.
Your turn. Take what makes sense, ignore what doesn’t, and most importantly: stop settling for that default terminal. You deserve better. Happy hacking! 🚀
Last modified on April 14, 2026