A two-line transient zsh prompt for liquidprompt, on macOS, plus atuin, fzf-tab and zoxide, wired in the right order.
liquidprompt has no transient prompt and no two-line mode. This adds both on
top of it, without patching it — so you keep liquidprompt's segments and
config and still get a prompt that gets out of the way. Around that sits a
complete zsh setup: eza, bat, a Claude Code statusline, an installer that
backs up everything it touches, and 82 tests.
Once a command runs, that whole bar collapses to just the mark, so scrollback reads as what you did rather than forty repetitions of where you were:
❯ npm run build
...output...
❯ git push
...output...
powerlevel10k and starship both have transient prompts. liquidprompt does not — and if you're already on liquidprompt, switching themes to get one means giving up its segment set and config. This repo adds transient and two-line support on top of liquidprompt 2.2.1, without patching it.
| this repo | powerlevel10k | starship | |
|---|---|---|---|
| Transient prompt | ✅ (added here) | ✅ built-in | ✅ enable_transience |
| Built on liquidprompt | ✅ | ❌ | ❌ |
| Keeps liquidprompt's config | ✅ | ❌ | ❌ |
| Standalone plugin | ✅ prompt/ |
n/a | n/a |
The prompt module is dependency-free and extractable — see
prompt/README.md if you only want that part.
- Transient prompt — old prompts collapse to
❯; clean scrollback - Two-line layout — full width to type, however long the path
Ctrl-R→ atuin — SQLite history with dir, exit code and durationTab→ fzf-tab — fuzzy completions with previews (ezatrees,batfiles,git diff)p <query>— jump to any project two levels under~/Projects- Claude Code statusline — model, dir, branch, session cost, lines changed
- Safe installer — timestamped backups,
--dry-run,--uninstall - Degrades gracefully — every tool is optional; missing ones no-op, never error
- 82 tests, including a real-pty harness for the zle code
- macOS (Intel or Apple Silicon — the Homebrew prefix is resolved, not hardcoded)
- zsh and oh-my-zsh — not installed by this repo
- A Nerd Font — without one the powerline separators render as blank space
# oh-my-zsh, if you don't have it
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"git clone https://github.com/ggapp1/liquidkit ~/liquidkit
cd ~/liquidkit
brew bundle # optional — everything degrades gracefully without it
./install.sh --dry-run # see every action first
./install.sh
exec zshSet your terminal font to MesloLGS Nerd Font, or import
iterm2/Profile.json.
Nothing is destroyed. Every file install.sh replaces is moved to
<name>.bak-<timestamp> first, and ./install.sh --uninstall puts them back.
Re-running is always safe.
The installer also clones liquidprompt (pinned to v2.2.1) and, if atuin is
present, imports your existing shell history once. It finishes by printing an
Optional tools report saying what's missing and exactly what each absence
costs you.
Why liquidprompt is cloned rather than installed from Homebrew
Homebrew ships liquidprompt 2.3.0; this repo pins v2.2.1, because the
transient plugin hooks powerline_full's internals and those were verified
against that version. Installing both would put two copies on disk and
reintroduce exactly the version drift the pin exists to prevent.
zsh/60-prompt.zsh looks for liquidprompt at ~/.local/share/liquidprompt and
silently does nothing if it isn't there — no error, just zsh's plain default
prompt. If the clone fails (offline first install), the installer logs a warning
rather than aborting, and the Optional tools report keeps flagging it until it
succeeds.
oh-my-zsh's git plugin defines these. This repo deliberately adds none of
its own — a fifth name for something that already has three makes it harder to
remember, not easier.
| Alias | Command | Alias | Command | |
|---|---|---|---|---|
gst |
git status |
gl |
git pull |
|
gaa |
git add --all |
gco |
git checkout |
|
gcmsg |
git commit --message |
gd |
git diff |
|
gp |
git push |
glo |
git log --oneline --decorate |
Prefer
gaaovergit add *. The shell expands*before git sees it, so it skips dotfiles and misses deleted files.git add --allstages new, modified and deleted — dotfiles included.
| Alias | Command |
|---|---|
.. ... .... |
cd .. and further up |
ls |
eza --group-directories-first --icons=auto |
ll |
eza -lah --git --group-directories-first --icons=auto |
tree |
eza --tree --level=2 --icons=auto |
cat |
bat --paging=never --style=plain |
fr fbi fc fpg |
flutter run / build ipa --release / clean / pub get |
nrd |
npm run dev |
Every one is guarded on its binary existing. p [query] jumps to any project
under $PROJECTS_DIR (default ~/Projects), searching two levels deep.
... and .... shadow oh-my-zsh, and that changes their behaviour
oh-my-zsh's lib/directories.zsh defines ..., .... and further dots as
global aliases (alias -g), which expand anywhere on a line — so cp foo ...
would expand too. This repo's 40-navigation.zsh loads later and redefines
.../.... as plain, non-global aliases, which only expand in command position.
The later definition replaces the earlier one outright — not just its value, its
type — so after both modules load, .../.... are ordinary aliases and
cp foo ... does not expand. (Verified by sourcing both modules and
inspecting zsh's $galiases and $aliases tables directly.)
.. is untouched by oh-my-zsh; it exists only because this repo defines it.
Add to ~/.claude/settings.json, using an absolute path:
{ "statusLine": { "type": "command", "command": "/Users/you/.claude/statusline.sh" } }Opus 5 | liquidkit main | $1.23 | +42/-7
Model, directory, git branch, session cost and lines changed — colourised, and
honouring NO_COLOR.
Hide the cost segment with a config file at
~/.config/liquidkit/statusline.conf:
LIQUIDKIT_STATUSLINE_COST=0The absolute path above is deliberate. A prefixed form like
"VAR=0 ~/.claude/statusline.sh"only works if the host runs the command through a shell. Executed directly it fails outright —VAR=0is looked up as a program name, and~is never expanded. The config file keeps the registered command free of anything that can go wrong. Environment variables still override it where a shell is involved.
Two display details worth knowing:
- In a git worktree, directories are conventionally named after their branch,
so
dir + branchreads as a stutter (api feat/api). When the branch's last segment matches the directory, only the branch is shown — it carries strictly more information. - In
$HOMEthe directory shows as~, not your username.
Claude Code's status payload exposes no context-remaining figure — there is no way to render "73% left". A
⚠ 200kmarker is the only signal available, and it appears only after the threshold is already crossed.
~/.zshrc.local is sourced last and never tracked. Put personal paths, tokens
and one-off functions there.
It is not a general override hook: zsh/zshrc sources every numbered module
before reaching it, so anything that must run before a module — a PATH entry
a later command -v check depends on, say — cannot live there.
zsh/zshrc sources zsh/[0-9][0-9]-*.zsh in sorted order:
| Module | Does |
|---|---|
00-path |
PATH, resolves $BREW_PREFIX |
10-ohmyzsh |
oh-my-zsh + plugins (git, flutter, uv, …) |
15-fzf |
fzf key bindings and completion |
20-history |
HIST_* options, atuin |
30-completion |
compinit, fzf-tab previews |
40-navigation |
zoxide, .., the p jumper |
50-aliases |
eza/bat/flutter/npm aliases |
60-prompt |
autosuggestions, syntax highlighting, liquidprompt, transient plugin |
Three ordering constraints are load-bearing, and all three fail silently:
| Constraint | Why |
|---|---|
15-fzf before 20-history |
Both bind Ctrl-R; last wins, and atuin must win |
30-completion after compinit, before autosuggestions |
fzf-tab must wrap the completion widget first |
60-prompt last |
Transient binds zle-line-init after syntax highlighting is in place |
Do not merge
15-fzf.zshinto30-completion.zsh. It looks like an obvious tidy-up — both deal with fzf — and it silently handsCtrl-Rback to fzf, because30-loads after20-. No error marks the moment it breaks.
The prompt shows blank gaps instead of separators. Your font has no Nerd Font glyphs. Set the terminal font to MesloLGS Nerd Font.
Ctrl-R opens fzf, not atuin.
Load order is inverted — check 15-fzf.zsh still sorts before 20-history.zsh.
No colours, no two-line prompt, nothing.
liquidprompt isn't at ~/.local/share/liquidprompt. Re-run ./install.sh and
read the Optional tools report.
I want out.
./install.sh --uninstall restores the backups it made.
./tests/run.sh # 82 tests, ~70sInteractive prompt behaviour runs against a real pty
(tests/lib/harness.py) — zle widgets don't execute
without a terminal, so there is no other way to test them honestly.
Built on oh-my-zsh and
liquidprompt. The transient
prompt technique is the one starship uses:
zle-line-init plus .recursive-edit.
MIT — see LICENSE.
