← all posts

Migrating my terminal setup to Ghostty, Zinit, and Starship with Claude

Gordon Beeming
Gordon Beeming
On this page7 sections

I used Claude Code to migrate my terminal setup to Ghostty, Zinit, Starship and fnm. Shell startup measured as low as 210ms afterwards. I hadn't benchmarked the old setup, so I can describe the improvement I noticed, but can't give a measured speedup.

I'd been using Oh My Zsh, Powerlevel10k and nvm with Warp and iTerm2. Opening a new tab had a noticeable pause, and I'd put off changing it because there were plenty of aliases, PATH entries and integrations to preserve.

Matt Wicks' post about his modern Zsh setup gave me a starting point. He'd benchmarked a lighter stack at 120ms. Around the same time, Matt Goldman and several other developers I'd spoken to were using Ghostty, which made it worth trying alongside the shell changes.

#Planning the migration

I was already in a Claude Code session, so I tried something: I pasted Matt's blog post URL and said, roughly, "I want to migrate to this setup. Read the blog post, look at my current config, and tell me what the migration looks like."

Claude read the blog post, explored my dotfiles repo (I use GNU Stow), read my .zshrc, .zprofile, .zshenv, checked what Homebrew packages I had installed, catalogued my Nerd Fonts, found my Powerlevel10k config, and mapped out every PATH entry, alias, and integration I had.

The plan included backups, install commands, a rewritten .zshrc, verification steps and a rollback strategy.

I approved the plan, and Claude executed it.

#What Changed

ComponentBeforeAfter
TerminalWarp (+iTerm2)Ghostty
Shell frameworkOh My Zsh (full)Zinit (cherry-picked snippets)
PromptPowerlevel10k (1,738-line config)Starship (~2-line config)
Node managementnvm (slow)fnm
Shell historyDefault Ctrl+RAtuin (full-text search)
Tab completionDefaultfzf-tab (fuzzy interactive)
Directory jumpingcd onlyzoxide (z for fuzzy jumps)
lsDefaulteza (icons, git status)

Everything else (all 30+ custom aliases, PATH entries, 1Password SSH agent, rbenv, Cargo, pnpm, dotnet tools) was preserved exactly.

#Migration and checks

The migration covered the following steps:

Phase 0 - Backup. Claude backed up my entire dotfiles repo, Oh My Zsh installation, Powerlevel10k config, and recorded my nvm Node versions.

Phase 1 - Install new tools. One brew install for the CLI tools, one brew install --cask for Ghostty. Then fnm install for each Node version I had in nvm.

Phase 2-3 - Config files. Claude created the Ghostty config, and while it was at it, noticed I had several config files that weren't in my dotfiles repo (Claude settings, SSH config, GitHub CLI config, git global ignore). It moved those into my Stow-managed dotfiles too.

Phase 4 - The rewrite. Claude replaced my 202-line .zshrc (mostly Oh My Zsh boilerplate comments) with a clean Zinit-based config. It cherry-picked only the four OMZ libraries I actually used (git.zsh, directories.zsh, theme-and-appearance.zsh, async_prompt.zsh) and the git plugin, then added the new tools on top.

It also cleaned up my .zprofile. I'd been carrying Amazon Q/Fig hooks from an app I'd uninstalled ages ago.

Phase 5 - Verify. Claude ran the new config in an interactive shell and checked: Node version, git aliases, SSH authentication, custom aliases, rbenv, Cargo, pnpm, dotnet. Everything passed.

Phase 6 - Cleanup. Removed nvm, Oh My Zsh, Powerlevel10k, iTerm2, Warp, and the leftover Amazon Q/Fig cask. Even found I had Google Cloud SDK installed that I didn't know about and removed that too.

#The Result

The prompt shows the directory name, git branch and a >, with Starship adding relevant information such as a Node version or Python environment.

A few additions have been useful in daily work:

  • Atuin replaced Ctrl+R with a full-text fuzzy search across my entire shell history. I can search by command, directory, or even how long something took to run.
  • fzf-tab makes tab completion interactive. Type git checkout and press Tab and you get a fuzzy-searchable list of branches instead of cycling through them one by one.
  • zoxide learns which directories I visit most. After a day of use, z xylem takes me straight to my blog repo from anywhere.
  • eza adds file icons, git status indicators, and color-coded output by default.

#Why Ghostty

The single most important quality I need in a terminal is text-editor-like input. Being able to move around the command line the same way I move around text in a code editor (arrow keys, Option+Arrow to jump words, Cmd+Arrow for line start/end, Option+Backspace to delete a word) is a deal breaker for me. It's the reason I was primarily using Warp. If a terminal doesn't support that fully, I can't use it as my daily driver.

And Warp was great for that -- the best terminal I've used for text editing in the input. But over time, every other update seemed to enable some new AI feature I had to go find and switch off. The UI kept getting busier, more panels, more suggestions, more things competing for attention. It stopped feeling like a terminal and started feeling like a platform. I just wanted a fast, clean shell.

The other long-standing sore point with Warp was the lack of Secure Keyboard Entry. This is a macOS feature that prevents other applications from intercepting your keystrokes, which matters when you're typing passwords or tokens in a terminal. Terminal.app, iTerm2, and Ghostty all support it. Warp has had an open issue for it since April 2023 with 40+ upvotes and no resolution. It meant I was always slightly uneasy using Warp for sensitive work. Ghostty has Secure Keyboard Entry built in, right there in the app menu.

Ghostty handles most of the text input well out of the box because it uses standard macOS keybindings natively. Arrow keys, Option+Arrow for word jumping, Cmd+Arrow for line start/end all just work. To be fair, it's not quite at Warp's level for text manipulation. Things like Shift+Arrow text selection, Shift+Enter for multi-line input, and typing to replace a selection aren't built in. But with some ZLE keybindings in .zshrc, you can get most of the way there:

.zshrc
# Shift+Enter to insert a newline (like Warp)
shift-enter-newline() { LBUFFER+=$'\n'; }
zle -N shift-enter-newline
bindkey '^[[27;2;13~' shift-enter-newline

# Shift+Arrow selection, plain arrow deselect,
# typing replaces selection — all done via ZLE widgets
# (full config in my dotfiles repo)

It's also GPU-accelerated, the config is a simple key-value file, and split panes work with Cmd+D / Cmd+Shift+D. The input behaviour differs from Warp, but the ZLE bindings cover most of what I need.

#Follow-up configuration fixes

Claude had the existing config, installed packages and reference post available in the same session. That context helped when a few settings needed follow-up changes.

When I opened Ghostty and it started in ~ instead of my github folder, Claude checked the Ghostty docs and found the window-inherit-working-directory config that was overriding my setting. When I asked what the cloud icon in my prompt meant, it identified it as the gcloud module and disabled it. When I noticed claude wasn't found on fresh shell startup, it traced the issue to ~/.local/bin not being in my PATH (it was only added by a script that ran on reload).

Having the existing configuration in the same session made it easier to investigate those follow-up problems.

#The configuration I kept

The migration took about an hour, including time spent reading the plan and approving steps. Matt's reference post helped define the replacement stack, while my existing dotfiles supplied the details that needed to survive the change.

My .zshrc went from 202 lines, many of them comments, to 138 lines. The prompt configuration went from 1,738 lines to two because I'm now using Starship's defaults with the gcloud module disabled. Those line counts describe how much configuration I maintain; they aren't a performance benchmark.

Ghostty still needs the ZLE bindings above for the input behaviour I use. With those in place, the new setup covers my normal terminal work, and the shell checks confirmed that my existing tools and aliases were available.

Gordon Beeming
Gordon Beeming

Father • Husband • Triathlete • SSW Solution Architect

Related posts