Leaving zsh, keeping the prompt
Moving to a distro that ships its own bash setup meant either fighting it or layering on top. I layered: the prompt I missed, the aliases I actually use, autosuggestions in bash, and two bugs that only show up over SSH.

Part five of a series on wiring my machines together. Previously: a screenshot on the laptop, a file path on the desktop. Next: every dev server gets a name.
Last year I wrote about my dotfiles: zsh, oh-my-zsh, GNU stow, and a handful of git settings worth carrying from machine to machine. That setup was for a machine where I owned the shell.
Omarchy is opinionated. It ships a bash configuration, a starship prompt, a set of aliases and functions, and it updates them. Switching the login shell to zsh would give me my old setup exactly, at the cost of quietly diverging from everything the distro assumes, and owning that divergence on every update.
So the question I actually had to answer was: what did I miss, specifically? Not “zsh”, but the handful of things zsh was doing for me. The list was short:
- The robbyrussell prompt.
➜ dir git:(branch) ✗. I’d been looking at it for years and the default felt like someone else’s machine. - The oh-my-zsh git aliases I actually type:
gst,gco,gd,gp,gl, and a few more. - Grey autosuggestions from history as I type.
All three can be done in bash. I stayed on bash.
Layer, don’t replace
The new repo has one rule, and it’s in the first line of the README: Omarchy’s defaults stay in charge; this repo only layers on top.
My .bashrc is Omarchy’s, with additions. It sources Omarchy’s defaults, then loads every file in ~/.config/bash/*.sh from the repo. If Omarchy changes a default, I get the change. If I want something different, I override it in my file, which loads afterwards, and the override sits in one obvious place.
The install script is still stow, with one improvement over the old setup: when a real file is in the way — which is always, on a fresh install, because the distro wrote its own — it gets moved to ~/.dotfiles-backup/<timestamp>/ instead of stopping the install. My old post told you to delete the file first. Moving it aside is better, because sometimes the file you’re replacing had something in it you wanted.
The prompt
Starship can approximate robbyrussell in a few lines. The two details worth sharing are the ones where starship didn’t have a setting for what I wanted.
One dirty marker. robbyrussell shows a single ✗ if anything is uncommitted. Starship’s git_status module shows a symbol per kind of change — modified, staged, untracked, and so on. There’s no option to collapse them into one. A custom module does it:
[custom.git_dirty]
when = 'test -n "$(git status --porcelain --ignore-submodules 2>/dev/null | head -c1)"'
require_repo = true
shell = ["bash", "--noprofile", "--norc"]
format = "[✗](bold yellow) "
head -c1 means git can stop as soon as it finds one change instead of listing all of them.
Which machine am I on. With three machines and SSH between them, I lost track of which shell was where more than once. Each machine now gets an emoji at the start of the prompt — 🦉 for the desktop, 🔥 for the Mac mini, 🧹 for the laptop. Starship has no per-hostname value, so it’s one tiny custom module per machine with a when on $HOSTNAME, and only the matching one prints:
➜ 🦉 nateparker.dev (main) ✗ ⇡2
Autosuggestions in bash
This was the part I expected to give up on. zsh-autosuggestions is the main reason people say they can’t go back to bash.
ble.sh is a full line editor for bash, written in bash, that does fish-style suggestions. It also does syntax highlighting, completion menus, and much more, and its defaults are busy. I turned most of it down. What’s left is grey history suggestions, accepted with → or End, up-arrow prefix search through history, and colour only where something’s wrong: an unknown command or an unclosed quote.
The one non-obvious thing: Omarchy’s bashrc binds keys through its own inputrc, and that runs after .blerc. So any key binding made in .blerc gets silently overridden. The bindings have to go at the very end of .bashrc, after everything else has loaded, right before ble.sh takes over the line.
Two bugs that only show up over SSH
Both of these only appeared once I was living in SSH and herdr sessions from the earlier posts, which is a good argument for actually using the setup before writing about it.
No locale. Shells started over SSH and inside herdr aren’t login shells, so they never read /etc/locale.conf. LANG was empty, which is how you get subtle Unicode breakage in a prompt full of emoji and arrows. Fixed with three lines at the top of .bashrc that source the system locale if LANG isn’t set.
The cursor jumps after every ssh. Omarchy wraps ssh with a cleanup function that resets the terminal when the connection ends, in case a remote program left it in a bad state. Part of that reset is \e[?1049l, “leave the alternate screen”. That escape sequence also restores the saved cursor position. After a plain ssh host command, the cursor jumped back up the screen, the next prompt drew over old output, and stale text stayed stuck below it.
There was already an open upstream fix that drops that sequence from the every-time cleanup. I put the same override in my dotfiles with a comment saying to delete it once the fix ships. That’s the layering rule doing its job: the patch sits in one file, says why it’s there, and says when to remove it.
What I didn’t port
- oh-my-zsh and znap. That old post admitted running both was redundant. Turns out the honest fix was running neither.
- Neovim. Omarchy ships LazyVim and I haven’t merged my old
init.viminto it yet. That’s its own post when it happens. - The laptop. The dotfiles are written to work on macOS too, but the laptop is a thin client and its old config is fine. It’ll get the new one when something gives me a reason.
Where the series goes next
That’s most of the desk side done: a desktop that sleeps when unused and wakes when asked, that can reboot without me in the room, that shares a clipboard, a drop folder and a screen with the laptop, and that feels like my machine when I’m in it.
One small piece turned out to be missing — dev servers that only answered on localhost — and that’s part six. After that, the piece with the most actual design in it: moving a project from where I write it to the Mac mini where it runs, in one command, without anything on that box ever being edited by hand. The desktop is x86 and the mini is ARM, which makes “just build it and copy it over” more interesting than it sounds. That’s part seven.