A zsh prompt with a configurator built in. Five layouts, five palettes, and an interactive menu that rebuilds your real prompt as you arrow through it.
It needs zsh and git, and nothing else — no framework, no plugin, no binary.
Git status comes from one git status --porcelain=v2 call rather than the four
processes oh-my-zsh's helpers spend, so a prompt in a repository costs about
15 ms instead of 73, and a prompt outside one costs half a millisecond instead
of the same 73.
git clone https://github.com/eishexac/omzppuccin.git ~/omzppuccinThen pick whichever line matches how you load things.
Oh My Zsh — as a theme:
ln -s ~/omzppuccin/omzppuccin.zsh-theme ~/.oh-my-zsh/custom/themes/omzppuccin.zsh-themeZSH_THEME="omzppuccin"ZSH_THEME="catppuccin" still works — the old filename is kept as a shim, since
the project shipped under it. Switch when convenient.
zinit / antidote / zplug / sheldon — as a plugin:
zinit light eishexac/omzppuccin
antidote bundle eishexac/omzppuccinPlain zsh, no framework:
fpath+=(~/omzppuccin)
autoload -Uz promptinit && promptinit
prompt omzppuccinOr just source it:
source ~/omzppuccin/omzppuccin.zshAll four load the same implementation. The prompt and the omzppuccin command
are deliberately one thing: the configurator previews by expanding the live
PROMPT in your shell, which a separate process could not do.
omzp is an alias for omzppuccin, with the same completions.
No font is required. One optional setting needs one:
omzppuccin symbol context draws an icon of the machine you're on, and those
live in the Nerd Fonts private-use area.
brew install --cask font-sauce-code-pro-nerd-font # macOSEverything else is ordinary Unicode, but coverage varies more than you'd expect. Checking Source Code Pro's Nerd Font build, which is a typical programming font:
| in a programming font | example | |
|---|---|---|
| Box drawing | essentially always | ╰──╼ └──╼ ┗━━╸ ╔═╗ |
| Latin-1 | always | » $ # |
| Dingbats | often missing | ➜ ✔ ✗ ✘ ➤ |
So the tail presets arc, parrot, short and heavy are the safe ones, and
guillemet is safest of all. The default ➜ ✔ ✗ markers are dingbats and are
frequently supplied by system fallback rather than by your terminal font — which
works fine, and is why every glyph carries a %N{…%} width hint: a fallback
face can have a different advance width, and without the hint zsh would lose
track of the cursor column.
Over SSH the font that matters is the one on the machine you're typing at. Terminal emulators render text locally, so a remote box needs no fonts at all.
They're OMZP_* now, since the palettes are no longer all Catppuccin. The old
names still work — anything set under one is copied across at load, and an
explicit OMZP_* wins over its CATPPUCCIN_* counterpart. omzppuccin save
writes the new names.
omzppuccin on its own opens a configurator. Arrow keys move and change; the
preview at the top is your real prompt, rebuilt on every keystroke:
╔═╗╔╦╗╔═╗╔═╗╔═╗╦ ╦╔═╗╔═╗╦╔╗╔
║ ║║║║╔═╝╠═╝╠═╝║ ║║ ║ ║║║║
╚═╝╩ ╩╚═╝╩ ╩ ╚═╝╚═╝╚═╝╩╝╚╝
➜ hexac ~/coding/hexac/omzppuccin git:(main) ✗
╰──╼
› style line elbow bare rail ledge
flavor mocha
tail arc
tail-color auto
…
↑↓ move ←→ change e edit s save d defaults q done
| Key | |
|---|---|
↑ ↓ |
move between settings |
← → |
change the selected one |
e |
edit a text field (sep, badge-text) |
s |
save to ~/.zshrc |
d |
back to defaults |
q |
done |
Changes apply to the running shell the moment you make them, and last only for
that shell until you press s. omzppuccin status prints the settings instead
of opening the menu, and is what you get in a script or a pipeline.
Three commands draw options instead of listing them, so you choose by looking:
omzppuccin styles # every style, rendered with your settings
omzppuccin flavors # every flavor, rendered with your settings
omzppuccin tails # every tail preset, side by sideomzppuccin preview draws a configuration without applying it — after a
command that worked and after one that exited 127, so you can see both halves
of the exit-status coloring:
omzppuccin preview OMZP_STYLE=rail OMZP_FLAVOR=lattePreviews inherit the real session, so an SSH shell previews red anchors and a root shell previews yellow ones. What you see is what you get.
It also reconfigures the prompt in the running shell — no restart, no editing files to try something out:
omzppuccin flavor latte # applies to the next prompt drawn
omzppuccin flavor celestial
omzppuccin badge off
omzppuccin host ssh
omzppuccin path truncated
omzppuccin help # every commandChanges last as long as the shell. To keep them:
omzppuccin save # writes to ~/.zshrcsave writes only the settings that differ from the defaults, into a managed
block placed above whatever line loads the prompt — source $ZSH/oh-my-zsh.sh,
a zinit/antidote line, promptinit, or a plain source of the file itself.
The settings are read as the prompt loads, so anything below that line would be
too late. It backs the file up to ~/.zshrc.omzppuccin.bak first. Anything inside the block is replaced on the next
save; the rest of your .zshrc is left alone.
| Command | Values |
|---|---|
flavor |
mocha | macchiato | frappe | latte | celestial | cyberpunk |
style |
line | elbow | bare | rail | ledge |
tail |
preset name or any string |
tail-color |
auto | palette color name | #rrggbb |
prompt-char |
auto | off | any string, plus an optional root variant |
prompt-char-color |
auto | status | session | role name | #rrggbb |
symbol |
arrow | context | none |
gap |
1–4 |
badge |
on | off |
badge-text |
any string |
badge-color |
#rrggbb | auto |
host |
never | ssh | always |
sep |
any string, plus an optional root variant |
path |
short | full | truncated |
path-dim |
on | off |
git |
classic | bare |
user |
on | off |
time |
on | off |
blank-line |
auto | on | off |
exit-code |
off | inline | right |
exec-time |
seconds |
tails styles flavors |
— (draw the options) |
preview |
KEY=VALUE … |
menu status logo |
— |
save |
optional file path |
reset |
— |
Tab-completion is available for every command and value.
line is the original prompt and stays the default. Everything below is opt-in,
and every setting added alongside these styles is off until you turn it on.
omzppuccin style elbow| Style | Shape |
|---|---|
line |
➜ [ssh] user directory git:(main) ✔ |
elbow |
context row, then a corner and stem ending in the tail |
bare |
context row, then the tail alone — a blank line separates the blocks |
rail |
a background-colored cell at column 0 on both rows |
ledge |
one line, with the identity right-aligned instead of leading |
The framed styles exist to solve one problem: when you scroll back through a few thousand lines of output, you need to find where each command started. A frame is an anchor, and an anchor is worth its columns.
Under rail the anchor is a background-colored space rather than a glyph, so
there is nothing to render at the wrong width and nothing a program's own
output can imitate. Its color is a free channel — grey on a local shell, red
over SSH, yellow as root — which is the whole job of the [ssh] badge at no
cost in columns. elbow colors its stem the same way.
Under every style but line, the icon and the [ssh] badge lead the context
row — the same place the icon sits today, next to what it describes. The tail
below is structural, and is drawn in two colors: the stem recedes with the rest
of the frame, and the tip takes the tail color. Exit status stays with the
icon, so the two marks never compete over the same channel.
The corner and stem drawn under elbow; bare draws the tip alone. Run
omzppuccin tails to see each one rendered as a real prompt.
| Preset | Tail | Notes |
|---|---|---|
arc |
╰──╼ |
the default |
parrot |
└──╼ |
box-drawing throughout |
short |
╰─╼ |
one stem segment |
heavy |
┗━━╸ |
heavy weight end to end |
angle |
╰─ ❯ |
gapped, see below |
head |
╰─➤ |
flat-backed, so it butts cleanly |
guillemet |
╰─» |
Latin-1, present in every font |
classic |
╰─$ |
the shell's own character as a stem |
tree |
`-> |
pure ASCII, after tree(1) |
minimal |
╰❯ |
no stem, so no seam |
Box-drawing characters are designed to tile: they meet edge to edge at an exact
vertical center in any font, which is why └──╼ looks solid. An ornament like
❯ is not, and butted against ─ the stem disappears into the chevron's open
notch — so those presets carry their own gap instead.
Any string works too, and its last character becomes the tip:
omzppuccin tail '╰━━▶'
omzppuccin tail arcOMZP_PROMPT_CHAR="auto" # auto (default) | off | any string
OMZP_PROMPT_CHAR_ROOT="" # counterpart when running as root
OMZP_PROMPT_CHAR_COLOR="auto" # auto | status | session | role | #rrggbbauto is $, and # when you're root — the way sh has done it since the
seventies. It closes the input line under every style but line:
➜ hexac@htb-brcong4yib ~/coding/hexac/omzppuccin main ✔
╰──╼ $
bare has no frame to hang it from, so the character stands on its own there;
if you turn it off, the tail's tip steps in so the input line is never blank.
prompt-char-color |
|
|---|---|
auto |
plain text, and the error color the moment it becomes a # |
status |
green or red, by how the last command ended |
session |
the tail tip's color — accent, red over SSH, yellow as root |
| a role name | accent, path, err… follows whichever palette you're on |
#rrggbb |
pinned |
auto tracks privilege, because that's the one signal the character itself
already reflects — $ becomes #, and the color is what makes you notice.
Both are left to zsh's %(!…), so they follow privilege as the prompt is
drawn. sudo -i and su start a new shell anyway, but a privileged shell can
drop itself by assigning to EUID, and this is the convention that exists to
show that. A custom character stays resolved at build time, since one
containing a . would split the ternary's delimiter.
Everything else is spoken for: exit status belongs to the icon a row above, and
the session to the tail's tip. status is there if you'd rather have the
conventional green/red under your cursor anyway.
omzppuccin prompt-char '»' '#' # your own, with a root counterpart
omzppuccin prompt-char offOMZP_TAIL_COLOR="auto" # auto (default) | palette color name | #rrggbbauto is sapphire on a local shell, red over SSH, yellow as root — so the tip
doubles as the session indicator, and the [ssh] badge becomes optional.
Sapphire because it is the one cool tone the prompt does not already spend:
green and red are the exit status, blue is the path, peach the branch, pink the
user, mauve the host, teal the git:() wrapper, yellow a dirty tree. Any other
palette name works — omzppuccin tail-color teal — and tracks the flavor when
you switch it.
Everything above is also settable directly. Add any of these to your ~/.zshrc below
ZSH_THEME="omzppuccin" and above source $ZSH/oh-my-zsh.sh — the prompt reads them
as it loads, so anything after that line is too late:
OMZP_FLAVOR="mocha" # mocha (default) | latte | frappe | macchiato | celestial | cyberpunkPalettes are discovered from flavors/, so dropping a file in is the whole of
adding one. omzppuccin flavors draws each one as a real prompt.
The prompt never names a color. It names a role, and a palette decides which of its colors fills each one — so a palette with no rosewater and no mauve can still describe a prompt completely.
| Role | Role | ||
|---|---|---|---|
text |
what you type | dim |
what recedes |
muted |
secondary labels | frame |
the tail's stem |
user |
username | host |
hostname |
path |
working directory | branch |
the git branch |
git |
the git:() wrapper |
stash |
the stash count |
ok |
success | warn |
a dirty tree |
err |
failure | accent |
the tail's tip |
alt |
the root separator | info |
spare, used by the wordmark |
A Catppuccin flavor file lists its 26 colors and the theme maps those onto the roles, so the four flavor files are unchanged. A palette that isn't Catppuccin fills the roles itself:
# flavors/mine.zsh
_omzp_roles_native=true
_omzp_is_light=false
_omzp_text="#d5d8da"
_omzp_path="#26bbd9"
_omzp_accent="#ee64ac"
# …_omzp_is_light matters: a light palette's dim needs one more step away from
the background than a dark one's to stay readable. Declaring it means the rule
applies to any light palette rather than being hardcoded for latte.
tail-color and badge-color take role names, so omzppuccin tail-color accent
follows whichever palette you switch to. Catppuccin's own color names still work
where they exist — sapphire resolves on a Catppuccin flavor and falls back to
accent elsewhere.
The JetBrains theme of the same name, a Horizon-family palette. Values read out of the plugin's own color scheme rather than sampled by eye:
text #d5d8da |
dim #6c6f93 |
user #e95678 |
host #ffb373 |
path #fab795 |
branch #d2d922 |
git #25b0bc |
stash #fac29a |
ok #29d398 |
warn #f09483 |
err #f43e5c |
accent #ee64ac |
Celestial is a warm theme — crimson, salmon, rose and peach outnumber
everything else in its scheme by a wide margin, and cyan is the counterpoint
rather than the lead. The roles follow that: the path takes peach, identity
takes rose and orange, and cyan appears only in the git:() wrapper. Magenta
takes accent because it's the one hue nothing else spends.
OMZP_STYLE="line" # line (default) | elbow | bare | rail | ledge
OMZP_TAIL="icon" # icon (default), a preset name, or any string
OMZP_BLANK_LINE="auto" # auto (on under 'bare') | on | offOMZP_PATH="short" # short (default) | full | truncated
OMZP_PATH_DIM=false # false (default) | true| Mode | Example |
|---|---|
short |
project |
full |
~/coding/hexac/project |
truncated |
~/cod…/hex…/project |
PATH_DIM dims the parent directories so a long path reads as its last
component without being shortened — which is what makes full comfortable to
leave on.
The dim step is chosen per flavor rather than by one token name. Catppuccin's
overlay ramp always moves away from base, so overlay0 recedes in every
flavor, but not by the same amount: against mocha's near-black base it sits
around 3:1, and against latte's near-white base it drops to roughly 2:1 and
stops being readable. Latte therefore takes overlay2 instead.
OMZP_GIT_STYLE="classic" # classic (default) | bareclassic is git:(main ✔). bare drops the wrapper and lets the color mark
the boundary, leaving main ✔ — two spaces and a hue change already read as a
new segment.
OMZP_EXIT_CODE="off" # off (default) | inline | rightA red prompt symbol says something broke; ✘ 127 says it was a missing binary
rather than a failed test. It's already sitting in $?.
| Mode | Where |
|---|---|
off |
nowhere |
inline |
beside the prompt icon |
right |
right-aligned, next to the execution time |
right reads best: both facts describe the command that just finished, so both
recede to the same edge and leave the prompt itself alone.
➜ hexac@htb-brcong4yib ~/coding/hexac/omzppuccin main ✔ ✘ 127 3m12s
╰──╼ $
Nothing is printed when the last command succeeded — not even the space that
would separate it from the time. The padding lives inside the %(?..)
conditional, so it disappears along with the rest.
OMZP_SEP="@" # "@" (default) — any string
OMZP_SEP_ROOT="" # optional replacement when running as rootomzppuccin sep ' ☺ ' ' ☠ 'Remote sessions get a badge next to the prompt symbol, so you always know when you're on someone else's machine. It's on by default.
➜ [ssh] user directory git:(main) ✔
OMZP_SSH_BADGE=true # true (default) | false
OMZP_SSH_BADGE_TEXT="[ssh]" # any string
OMZP_SSH_BADGE_COLOR="#f38ba8"A text badge is deliberate: it needs no special font, and a word reads at a glance in a
way a glyph doesn't. OMZP_SHOW_HOST="ssh" pairs well with it, adding which
machine you're on.
The session is detected from SSH_CONNECTION, SSH_CLIENT or SSH_TTY — the latter two
catch shells where the first got dropped, such as after su or sudo -i.
By default the prompt opens with ➜, colored green or red by the last command's exit
status. Setting context swaps it for an icon of the machine you're on:
OMZP_PROMPT_ICON="arrow" # arrow (default) | context| Context | Nerd Font name | Codepoint |
|---|---|---|
| SSH session | nf-fa-server |
U+F233 |
| macOS | nf-fa-apple |
U+F179 |
| Linux | nf-fa-linux |
U+F17C |
| Windows (Cygwin/MSYS) | nf-fa-windows |
U+F17A |
| Anything else | ➜ |
U+279C |
This mode needs a Nerd Font — without one the glyphs
render as empty boxes. On macOS: brew install --cask font-sauce-code-pro-nerd-font.
The glyphs above sit in the Font Awesome block, which is unchanged between Nerd Fonts
v2 and v3. SSH takes precedence over the OS icon, and the exit-status coloring is the same
as the arrow's.
Each icon is overridable — any string works, so you can swap in a distro logo, an ethernet port, or plain text:
OMZP_ICON_SSH=$'\U000f0200' # nf-md-ethernet, a literal port (Nerd Fonts v3 only)
OMZP_ICON_MACOS=$'\uf8ff' # Apple logo; renders on macOS even without a Nerd Font
OMZP_ICON_LINUX="[linux]" # plain text works tooOMZP_SHOW_USER=true # true (default) | false
OMZP_SHOW_HOST="never" # never (default) | ssh | alwaysSHOW_HOST |
Behavior |
|---|---|
never |
Hostname hidden |
ssh |
Hostname shown only in SSH sessions |
always |
Hostname always shown |
OMZP_SHOW_TIME=true # false by defaultLong-running commands display their elapsed time on the right side of the prompt.
OMZP_EXEC_TIME_THRESHOLD=5 # seconds (default: 5)➜ [ssh] user directory git:(branch) ✔↑ 5s
- Green/red prompt symbol based on last command exit status, optionally an OS/SSH icon
[ssh]badge on remote sessions- Optional timestamp
- Configurable username and hostname
- Configurable path display
- Git branch with clean/dirty indicator
- Git ahead (↑) / behind (↓) remote indicators
- Git stash count (≡)
- Command execution time (right-aligned, for commands exceeding threshold)
The prompt began as catppuccin-zsh by JannoTjarks.
Catppuccin — mocha, macchiato, frappé and latte are the Catppuccin palettes, by the Catppuccin org and its contributors, used under the MIT license.
Celestial — by Alex Adewole, from the JetBrains IDE theme of the same name. The values here were read out of the plugin's own color scheme rather than sampled from screenshots, so they are the theme's actual colors. Celestial belongs to the Horizon family; the palette is reproduced here for use in a terminal prompt, and all credit for the color work is theirs.
Neither project is affiliated with this one. If you are an author here and want a palette changed or removed, open an issue and it will be.