I have deleted a production namespace exactly once. Wrong terminal, wrong context, right command. The cluster did precisely what I asked. After the apologies and the restore, I sat down and decided my prompt was going to start telling me things, because clearly I could not be trusted to remember them.
That is the whole philosophy here. A prompt is not decoration. It is the one piece of UI you look at thousands of times a day, and every glance is a free chance to answer a question you would otherwise answer wrong. The trick is choosing the right handful of questions and then ruthlessly not adding any more.
What I actually want to know
When I hit Enter, before I think about the command, I want three things visible without having to ask.
Which machine am I on, and as whom. Local, a jump host, root after a sudo I forgot about. The number of incidents that begin with "oh, I was still on the bastion" is not small.
What git thinks. Branch, whether the tree is dirty, whether I am ahead or behind the remote. Not a full status, just enough to stop me committing to main or force-pushing over someone's afternoon.
And, when it is relevant, which Kubernetes context and namespace are armed. This is the one that cost me the namespace. It is now the loudest thing on the line when it is set, and entirely absent when it is not, so I am never reading stale information.
Starting from starship, then trimming
I used to maintain this by hand in zsh, a tangle of vcs_info and precmd hooks that I was faintly proud of and quietly afraid to touch. A couple of years ago I moved to starship and stopped maintaining a small program just to draw a prompt. The config is a single TOML file, it is fast because the heavy lifting is in Rust rather than forked subshells, and it works the same in zsh, bash and fish, which matters when half my boxes have whatever shell shipped with the distro.
The default starship prompt is generous to a fault. It will happily tell you the version of every toolchain in the directory, your battery percentage, the phase of the moon. The work is in turning most of it off.
# ~/.config/starship.toml
format = """
$username\
$hostname\
$directory\
$git_branch\
$git_status\
$kubernetes\
$character"""
[hostname]
ssh_only = true
format = "[@$hostname](bold yellow) "
[kubernetes]
disabled = false
format = '[⎈ $context\($namespace\)](bold red) '
[git_status]
disabled = false
The two lines that matter most are ssh_only = true on the hostname, so my own machine stays quiet and a remote host shouts, and enabling the kubernetes module, which is off by default. I also coloured the kube segment an alarming red, because subtlety is exactly what got me into trouble.
The part people get wrong: speed
A prompt that takes 300ms to render is a prompt you will eventually rip out, because you feel every one of those milliseconds as a stutter between commands. The fastest way to slow a prompt to a crawl is to shell out to git status in a large repository on every keystroke.
starship handles most of this sensibly, but the kubernetes module can still be slow if it has to parse a large kubeconfig, and the git modules struggle in enormous monorepos. There is a per-module timeout (command_timeout) and you can disable modules wholesale for a given directory. On the one repository large enough to be a problem, I simply turned the git status detail down to branch-only. A prompt that is occasionally less detailed beats a prompt that is reliably annoying.
command_timeout = 1000
What I deliberately left out
No clock. My terminal multiplexer has one, and I do not need the time stamped on every line until I am reading back a log, at which point the multiplexer's history serves me fine.
No return code as a number. I show the prompt character in red when the last command failed and that is enough. If I care about the exact code I will type echo $?, which is muscle memory anyway.
No language versions on the main line. They are interesting maybe once a session, when I am setting up, and clutter the other nine hundred and ninety-nine times. If I genuinely need to know which Python is active, that is a question for which, not for the prompt.
The discipline is the point. Every segment you add costs a little reading attention forever, so each one has to earn its place by preventing a real mistake or answering a question you actually ask. Git state earns it. Kube context earns it, in blood. Hostname earns it. Almost nothing else does.
It has been a year since the namespace incident, and the prompt has caught me about to do something stupid more times than I would like to admit in writing. That is the measure of it. Not how it looks in a screenshot, but how many times the red text made me pause for the half a second it took to think, "hang on, am I really on that cluster?"
Usually I was not. Once, I was.