Skip to main content

Powerlevel10k

Introduction

powerlevel10k (p10k) is one of the fastest zsh themes. It uses gitstatus — a purpose-built C++ daemon — for git information, tightly integrated into its async rendering pipeline. beachcomber can replace gitstatus as the git data source, consolidating all shell state queries into a single daemon instead of running two.

beachcomber also provides cached data for things p10k does not cover natively, such as mise project tool versions.

There are two levels of integration:

  • Complementary segments — add beachcomber-backed segments alongside p10k's built-in ones
  • Full git replacement — replace gitstatus entirely with a beachcomber-backed segment

Prerequisites

  • beachcomber installed and the daemon running (comb s should succeed)
  • powerlevel10k installed and configured (p10k configure has been run at least once)
  • ~/.p10k.zsh exists (generated by the configuration wizard)

The durability problem

~/.p10k.zsh is regenerated when you run p10k configure. Any edits to that file will be overwritten. beachcomber integration must survive regeneration.

The solution is an overlay file — a separate zsh script sourced immediately after ~/.p10k.zsh. It patches the prompt element arrays and defines custom segment functions. When p10k configure regenerates ~/.p10k.zsh, the overlay re-applies its changes on next shell startup. The generated file is never modified.

Setting up the overlay

Create ~/.config/zsh/beachcomber-p10k.zsh (or any path you prefer) and source it in your .zshrc immediately after the p10k config:

# .zshrc
source ~/.p10k.zsh
source ~/.config/zsh/beachcomber-p10k.zsh

All beachcomber segment definitions and array patches go in the overlay file. The rest of this guide shows what to put in it.

Complementary segments

These segments add data that p10k does not provide natively. They work alongside p10k's built-in segments without replacing anything.

Kubernetes context

# comb g returns plain text by default. g = get, no suffix needed.
function prompt_bc_kube() {
local ctx=$(comb g kubecontext.context 2>/dev/null)
[[ -n "$ctx" ]] || return
p10k segment -f cyan -i '☸' -t "$ctx"
}

AWS profile

function prompt_bc_aws() {
local profile=$(comb g aws.profile 2>/dev/null)
[[ -n "$profile" ]] || return
p10k segment -f yellow -t "$profile"
}

The beachcomber AWS provider detects profiles from both $AWS_PROFILE (native CLI, granted) and $AWS_VAULT (aws-vault), with session expiration tracking via $AWS_CREDENTIAL_EXPIRATION.

GCloud project

function prompt_bc_gcloud() {
local project=$(comb g gcloud.project 2>/dev/null)
[[ -n "$project" ]] || return
p10k segment -f blue -t "$project"
}

mise project tools

Shows tool versions that are overridden at the project level (from a local mise.toml), distinguishing them from the global baseline. Only appears in directories with a project-level mise config.

typeset -g POWERLEVEL9K_COMB_MISE_FOREGROUND=147
typeset -g POWERLEVEL9K_COMB_MISE_VISUAL_IDENTIFIER_EXPANSION=$'\uF07C' # folder-open icon

function prompt_comb_mise() {
local cwd=${(%):-%/}
local project
project=$(comb g mise.project "$cwd" 2>/dev/null) || return
[[ -z "$project" ]] && return

local display=()
local entry tool ver
for entry in "${(@s:,:)project}"; do
[[ -z "$entry" ]] && continue
tool=${entry%%=*}
ver=${entry#*=}
display+=("${tool} ${ver}")
done
p10k segment -f 147 -i $'\uF07C' -t "${(j: | :)display}"
}

The beachcomber mise provider exposes three fields:

  • mise.project — only tools sourced from a local mise.toml (project-scoped overrides)
  • mise.global — only tools sourced from ~/.config/mise/ (the global baseline)
  • mise.tools — all tools combined with P: and G: prefixes

Adding complementary segments to the prompt

After defining the functions, patch the elements array in the overlay. This example inserts comb_mise before the time segment:

typeset -ga _comb_rpe=()
for _comb_e in "${POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS[@]}"; do
[[ "$_comb_e" == "time" ]] && _comb_rpe+=(comb_mise)
_comb_rpe+=("$_comb_e")
done
POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=("${_comb_rpe[@]}")
unset _comb_rpe _comb_e

For simpler cases, append to the end:

POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS+=(bc_kube bc_aws bc_gcloud)

Full git replacement

This replaces p10k's gitstatus-backed vcs segment with a custom comb_git segment that reads all git state from beachcomber. When vcs is removed from the elements array, p10k does not start the gitstatus daemon — eliminating one long-running process from your system.

Why replace gitstatus?

gitstatus is already fast. The motivation is architectural, not performance:

  • One daemon instead of two. gitstatus runs one C++ daemon per shell session. beachcomber runs one daemon shared across all shells, tmux panes, and other consumers. Fewer processes, less memory, shared cache.
  • Consistent data. The same git state that feeds your prompt also feeds your tmux status bar, neovim statusline, and any other beachcomber consumer. No divergence between tools.
  • Broader coverage. beachcomber's git provider includes fields that gitstatus does not expose, such as line-level diff stats (lines_added, lines_removed), stash counts, and the current tag.

Current limitations

beachcomber's git provider now exposes commit_summary (the first line of the HEAD commit message) and push_ahead/push_behind (commits ahead of or behind the push remote). The comb_git segment above can be extended to use these fields. WIP detection and push indicator rendering require wiring them into the segment function — see the field table in Built-in Providers for the field names.

The comb_git segment

Add this to your overlay file:

# Swap vcs for comb_git in the left prompt elements.
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(${POWERLEVEL9K_LEFT_PROMPT_ELEMENTS[@]/vcs/comb_git})

# Colours matching the default vcs segment config
typeset -g POWERLEVEL9K_COMB_GIT_CLEAN_FOREGROUND=76
typeset -g POWERLEVEL9K_COMB_GIT_MODIFIED_FOREGROUND=178
typeset -g POWERLEVEL9K_COMB_GIT_UNTRACKED_FOREGROUND=76
typeset -g POWERLEVEL9K_COMB_GIT_VISUAL_IDENTIFIER_EXPANSION=
typeset -g POWERLEVEL9K_COMB_GIT_LOADING_VISUAL_IDENTIFIER_COLOR=244

function prompt_comb_git() {
local cwd=${(%):-%/}
local output
output=$(comb g git "$cwd" 2>/dev/null) || return
[[ -z "$output" ]] && return

# Parse key=value lines into an associative array
local -A g
local line
for line in ${(f)output}; do
local key=${line%%=*}
local val=${line#*=}
g[$key]=$val
done

[[ -z ${g[branch]} ]] && [[ -z ${g[commit]} ]] && return

# Determine state for foreground colour
local state=CLEAN
if (( ${g[unstaged]:-0} || ${g[conflicted]:-0} || ${g[staged]:-0} )); then
state=MODIFIED
elif (( ${g[untracked]:-0} )); then
state=UNTRACKED
fi

# Build display string matching p10k's default git formatting
local meta='%f'
local clean='%76F'
local modified='%178F'
local untracked='%39F'
local conflicted='%196F'

local res

# Branch name (truncate if > 32 chars)
if [[ -n ${g[branch]} ]]; then
local branch=${g[branch]}
(( $#branch > 32 )) && branch="${branch[1,12]}…${branch[-12,-1]}"
res+="${clean}${branch//\%/%%}"
elif [[ -n ${g[tag]} && ${g[tag]} != "null" ]]; then
local tag=${g[tag]}
(( $#tag > 32 )) && tag="${tag[1,12]}…${tag[-12,-1]}"
res+="${meta}#${clean}${tag//\%/%%}"
elif [[ -n ${g[commit]} ]]; then
res+="${meta}@${clean}${g[commit][1,8]}"
fi

# Upstream tracking branch (only if different from local)
if [[ -n ${g[upstream]} && ${g[upstream]} != "null" ]]; then
local remote_branch=${g[upstream]#*/}
if [[ -n ${remote_branch} && ${remote_branch} != ${g[branch]} ]]; then
res+="${meta}:${clean}${remote_branch//\%/%%}"
fi
fi

# Ahead/behind
if (( ${g[ahead]:-0} || ${g[behind]:-0} )); then
(( ${g[behind]:-0} )) && res+=" ${clean}⇣${g[behind]}"
(( ${g[ahead]:-0} && !${g[behind]:-0} )) && res+=" "
(( ${g[ahead]:-0} )) && res+="${clean}⇡${g[ahead]}"
fi

# Stashes
(( ${g[stash]:-0} )) && res+=" ${clean}*${g[stash]}"
# Merge/rebase state
[[ ${g[state]} != "clean" && -n ${g[state]} ]] && res+=" ${conflicted}${g[state]}"
# Conflicted
(( ${g[conflicted]:-0} )) && res+=" ${conflicted}~${g[conflicted]}"
# Staged
(( ${g[staged]:-0} )) && res+=" ${modified}+${g[staged]}"
# Unstaged
(( ${g[unstaged]:-0} )) && res+=" ${modified}!${g[unstaged]}"
# Untracked
(( ${g[untracked]:-0} )) && res+=" ${untracked}?${g[untracked]}"

p10k segment -s $state -t "$res"
}

The element substitution ${POWERLEVEL9K_LEFT_PROMPT_ELEMENTS[@]/vcs/comb_git} replaces vcs with comb_git in whatever array ~/.p10k.zsh defined. If p10k configure regenerates the file with vcs back in the array, the overlay swaps it out again on next shell startup.

Verifying gitstatus is not running

After opening a new shell with the overlay active, confirm gitstatus is stopped:

pgrep -P $$ gitstatusd

This should return nothing. If it returns a PID, the vcs segment is still in the elements array — check that the overlay is being sourced after ~/.p10k.zsh.

First-prompt cache miss

On the very first prompt after the daemon starts (or after switching to a new directory that has not been queried before), the git provider executes inline. The comb g call blocks briefly while the provider runs and returns the result directly — the segment will have data on that first prompt, with a short delay while git runs. Subsequent prompts return the cached value with no delay.

Once the provider has run for a given directory, the cache is kept warm by ongoing demand from prompt queries.

Customizing colours

All beachcomber segments follow p10k's standard colour configuration. For a segment named comb_git, set:

typeset -g POWERLEVEL9K_COMB_GIT_FOREGROUND=76
typeset -g POWERLEVEL9K_COMB_GIT_CLEAN_FOREGROUND=76
typeset -g POWERLEVEL9K_COMB_GIT_MODIFIED_FOREGROUND=178

The -s $state argument in p10k segment selects which colour variant to use. States map to the _CLEAN_, _MODIFIED_, and _UNTRACKED_ colour keys.

Reverting

To revert all beachcomber integration:

  1. Comment out or delete the source line for the overlay in .zshrc
  2. Restart zsh

The ~/.p10k.zsh file was never modified. The vcs segment will reappear and gitstatus will start again automatically.

To revert just the git replacement while keeping other segments, remove the POWERLEVEL9K_LEFT_PROMPT_ELEMENTS substitution line from the overlay. The vcs segment from ~/.p10k.zsh will take effect again.

Troubleshooting

  • Segment not showing: verify the function name exactly matches prompt_<segment_name> and the segment name is in the PROMPT_ELEMENTS array. Run echo $POWERLEVEL9K_LEFT_PROMPT_ELEMENTS to check. A typo in either causes p10k to silently skip it.
  • Git segment slow on first prompt: on a cold cache the provider executes inline, which takes as long as a normal git invocation (typically a few milliseconds). This is a one-time cost per directory.
  • Overlay not taking effect: ensure the source line comes after the ~/.p10k.zsh source in .zshrc. The overlay must run second to patch the arrays.
  • gitstatus still running: check pgrep -P $$ gitstatusd. If it returns a PID, vcs is still in the elements array. The overlay may not be loaded — check type prompt_comb_git to see if the function exists.
  • Icons not rendering: the Nerd Font glyphs require a patched font. p10k's recommended fonts include all required glyphs. Replace icons with text labels if your font lacks them.
  • Changes lost after p10k configure: this is expected — p10k configure regenerates ~/.p10k.zsh. The overlay re-applies changes on next shell startup. Just open a new shell.

See the Troubleshooting guide for general diagnostics.