[p10k] jj integration
https://andre.arko.net/2025/06/20/a-jj-prompt-for-powerlevel10k/
change txyqptzsstmowwkyvsurprqrmvmxonry
commit 1644fe2f4bb20a5f4f24e17950ace322c3c174dc
author Alpha Chen <alpha@kejadlen.dev>
date
parent ttktsppq
diff --git a/.config/zsh/async.zsh b/.config/zsh/async.zsh
new file mode 100644
index 00000000..ccddfc4f
--- /dev/null
+++ b/.config/zsh/async.zsh
@@ -0,0 +1,672 @@
+#!/usr/bin/env zsh
+
+#
+# zsh-async
+#
+# version: v1.8.6
+# author: Mathias Fredriksson
+# url: https://github.com/mafredri/zsh-async
+#
+
+typeset -g ASYNC_VERSION=1.8.6
+# Produce debug output from zsh-async when set to 1.
+typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0}
+
+# Execute commands that can manipulate the environment inside the async worker. Return output via callback.
+_async_eval() {
+	local ASYNC_JOB_NAME
+	# Rename job to _async_eval and redirect all eval output to cat running
+	# in _async_job. Here, stdout and stderr are not separated for
+	# simplicity, this could be improved in the future.
+	{
+		eval "$@"
+	} &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'command -p cat')
+}
+
+# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time
+_async_job() {
+	# Disable xtrace as it would mangle the output.
+	setopt localoptions noxtrace
+
+	# Store start time for job.
+	float -F duration=$EPOCHREALTIME
+
+	# Run the command and capture both stdout (`eval`) and stderr (`cat`) in
+	# separate subshells. When the command is complete, we grab write lock
+	# (mutex token) and output everything except stderr inside the command
+	# block, after the command block has completed, the stdin for `cat` is
+	# closed, causing stderr to be appended with a $'\0' at the end to mark the
+	# end of output from this job.
+	local jobname=${ASYNC_JOB_NAME:-$1} out
+	out="$(
+		local stdout stderr ret tok
+		{
+			stdout=$(eval "$@")
+			ret=$?
+			duration=$(( EPOCHREALTIME - duration ))  # Calculate duration.
+
+			print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration
+		} 2> >(stderr=$(command -p cat) && print -r -n - " "${(q)stderr}$'\0')
+	)"
+	if [[ $out != $'\0'*$'\0' ]]; then
+		# Corrupted output (aborted job?), skipping.
+		return
+	fi
+
+	# Grab mutex lock, stalls until token is available.
+	read -r -k 1 -p tok || return 1
+
+	# Return output (<job_name> <return_code> <stdout> <duration> <stderr>).
+	print -r -n - "$out"
+
+	# Unlock mutex by inserting a token.
+	print -n -p $tok
+}
+
+# The background worker manages all tasks and runs them without interfering with other processes
+_async_worker() {
+	# Reset all options to defaults inside async worker.
+	emulate -R zsh
+
+	# Make sure monitor is unset to avoid printing the
+	# pids of child processes.
+	unsetopt monitor
+
+	# Redirect stderr to `/dev/null` in case unforseen errors produced by the
+	# worker. For example: `fork failed: resource temporarily unavailable`.
+	# Some older versions of zsh might also print malloc errors (know to happen
+	# on at least zsh 5.0.2 and 5.0.8) likely due to kill signals.
+	exec 2>/dev/null
+
+	# When a zpty is deleted (using -d) all the zpty instances created before
+	# the one being deleted receive a SIGHUP, unless we catch it, the async
+	# worker would simply exit (stop working) even though visible in the list
+	# of zpty's (zpty -L). This has been fixed around the time of Zsh 5.4
+	# (not released).
+	if ! is-at-least 5.4.1; then
+		TRAPHUP() {
+			return 0  # Return 0, indicating signal was handled.
+		}
+	fi
+
+	local -A storage
+	local unique=0
+	local notify_parent=0
+	local parent_pid=0
+	local coproc_pid=0
+	local processing=0
+
+	local -a zsh_hooks zsh_hook_functions
+	zsh_hooks=(chpwd periodic precmd preexec zshexit zshaddhistory)
+	zsh_hook_functions=(${^zsh_hooks}_functions)
+	unfunction $zsh_hooks &>/dev/null   # Deactivate all zsh hooks inside the worker.
+	unset $zsh_hook_functions           # And hooks with registered functions.
+	unset zsh_hooks zsh_hook_functions  # Cleanup.
+
+	close_idle_coproc() {
+		local -a pids
+		pids=(${${(v)jobstates##*:*:}%\=*})
+
+		# If coproc (cat) is the only child running, we close it to avoid
+		# leaving it running indefinitely and cluttering the process tree.
+		if  (( ! processing )) && [[ $#pids = 1 ]] && [[ $coproc_pid = $pids[1] ]]; then
+			coproc :
+			coproc_pid=0
+		fi
+	}
+
+	child_exit() {
+		close_idle_coproc
+
+		# On older version of zsh (pre 5.2) we notify the parent through a
+		# SIGWINCH signal because `zpty` did not return a file descriptor (fd)
+		# prior to that.
+		if (( notify_parent )); then
+			# We use SIGWINCH for compatibility with older versions of zsh
+			# (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could
+			# cause a deadlock in the shell under certain circumstances.
+			kill -WINCH $parent_pid
+		fi
+	}
+
+	# Register a SIGCHLD trap to handle the completion of child processes.
+	trap child_exit CHLD
+
+	# Process option parameters passed to worker.
+	while getopts "np:uz" opt; do
+		case $opt in
+			n) notify_parent=1;;
+			p) parent_pid=$OPTARG;;
+			u) unique=1;;
+			z) notify_parent=0;;  # Uses ZLE watcher instead.
+		esac
+	done
+
+	# Terminate all running jobs, note that this function does not
+	# reinstall the child trap.
+	terminate_jobs() {
+		trap - CHLD   # Ignore child exits during kill.
+		coproc :      # Quit coproc.
+		coproc_pid=0  # Reset pid.
+
+		if is-at-least 5.4.1; then
+			trap '' HUP    # Catch the HUP sent to this process.
+			kill -HUP -$$  # Send to entire process group.
+			trap - HUP     # Disable HUP trap.
+		else
+			# We already handle HUP for Zsh < 5.4.1.
+			kill -HUP -$$  # Send to entire process group.
+		fi
+	}
+
+	killjobs() {
+		local tok
+		local -a pids
+		pids=(${${(v)jobstates##*:*:}%\=*})
+
+		# No need to send SIGHUP if no jobs are running.
+		(( $#pids == 0 )) && continue
+		(( $#pids == 1 )) && [[ $coproc_pid = $pids[1] ]] && continue
+
+		# Grab lock to prevent half-written output in case a child
+		# process is in the middle of writing to stdin during kill.
+		(( coproc_pid )) && read -r -k 1 -p tok
+
+		terminate_jobs
+		trap child_exit CHLD  # Reinstall child trap.
+	}
+
+	local request do_eval=0
+	local -a cmd
+	while :; do
+		# Wait for jobs sent by async_job.
+		read -r -d $'\0' request || {
+			# Unknown error occurred while reading from stdin, the zpty
+			# worker is likely in a broken state, so we shut down.
+			terminate_jobs
+
+			# Stdin is broken and in case this was an unintended
+			# crash, we try to report it as a last hurrah.
+			print -r -n $'\0'"'[async]'" $(( 127 + 3 )) "''" 0 "'$0:$LINENO: zpty fd died, exiting'"$'\0'
+
+			# We use `return` to abort here because using `exit` may
+			# result in an infinite loop that never exits and, as a
+			# result, high CPU utilization.
+			return $(( 127 + 1 ))
+		}
+
+		# We need to clean the input here because sometimes when a zpty
+		# has died and been respawned, messages will be prefixed with a
+		# carraige return (\r, or \C-M).
+		request=${request#$'\C-M'}
+
+		# Check for non-job commands sent to worker
+		case $request in
+			_killjobs)    killjobs; continue;;
+			_async_eval*) do_eval=1;;
+		esac
+
+		# Parse the request using shell parsing (z) to allow commands
+		# to be parsed from single strings and multi-args alike.
+		cmd=("${(z)request}")
+
+		# Name of the job (first argument).
+		local job=$cmd[1]
+
+		# Check if a worker should perform unique jobs, unless
+		# this is an eval since they run synchronously.
+		if (( !do_eval )) && (( unique )); then
+			# Check if a previous job is still running, if yes,
+			# skip this job and let the previous one finish.
+			for pid in ${${(v)jobstates##*:*:}%\=*}; do
+				if [[ ${storage[$job]} == $pid ]]; then
+					continue 2
+				fi
+			done
+		fi
+
+		# Guard against closing coproc from trap before command has started.
+		processing=1
+
+		# Because we close the coproc after the last job has completed, we must
+		# recreate it when there are no other jobs running.
+		if (( ! coproc_pid )); then
+			# Use coproc as a mutex for synchronized output between children.
+			coproc command -p cat
+			coproc_pid="$!"
+			# Insert token into coproc
+			print -n -p "t"
+		fi
+
+		if (( do_eval )); then
+			shift cmd  # Strip _async_eval from cmd.
+			_async_eval $cmd
+		else
+			# Run job in background, completed jobs are printed to stdout.
+			_async_job $cmd &
+			# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
+			storage[$job]="$!"
+		fi
+
+		processing=0  # Disable guard.
+
+		if (( do_eval )); then
+			do_eval=0
+
+			# When there are no active jobs we can't rely on the CHLD trap to
+			# manage the coproc lifetime.
+			close_idle_coproc
+		fi
+	done
+}
+
+#
+# Get results from finished jobs and pass it to the to callback function. This is the only way to reliably return the
+# job name, return code, output and execution time and with minimal effort.
+#
+# If the async process buffer becomes corrupt, the callback will be invoked with the first argument being `[async]` (job
+# name), non-zero return code and fifth argument describing the error (stderr).
+#
+# usage:
+# 	async_process_results <worker_name> <callback_function>
+#
+# callback_function is called with the following parameters:
+# 	$1 = job name, e.g. the function passed to async_job
+# 	$2 = return code
+# 	$3 = resulting stdout from execution
+# 	$4 = execution time, floating point e.g. 2.05 seconds
+# 	$5 = resulting stderr from execution
+#	$6 = has next result in buffer (0 = buffer empty, 1 = yes)
+#
+async_process_results() {
+	setopt localoptions unset noshwordsplit noksharrays noposixidentifiers noposixstrings
+
+	local worker=$1
+	local callback=$2
+	local caller=$3
+	local -a items
+	local null=$'\0' data
+	integer -l len pos num_processed has_next
+
+	typeset -gA ASYNC_PROCESS_BUFFER
+
+	# Read output from zpty and parse it if available.
+	while zpty -r -t $worker data 2>/dev/null; do
+		ASYNC_PROCESS_BUFFER[$worker]+=$data
+		len=${#ASYNC_PROCESS_BUFFER[$worker]}
+		pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]}  # Get index of NULL-character (delimiter).
+
+		# Keep going until we find a NULL-character.
+		if (( ! len )) || (( pos > len )); then
+			continue
+		fi
+
+		while (( pos <= len )); do
+			# Take the content from the beginning, until the NULL-character and
+			# perform shell parsing (z) and unquoting (Q) as an array (@).
+			items=("${(@Q)${(z)ASYNC_PROCESS_BUFFER[$worker][1,$pos-1]}}")
+
+			# Remove the extracted items from the buffer.
+			ASYNC_PROCESS_BUFFER[$worker]=${ASYNC_PROCESS_BUFFER[$worker][$pos+1,$len]}
+
+			len=${#ASYNC_PROCESS_BUFFER[$worker]}
+			if (( len > 1 )); then
+				pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]}  # Get index of NULL-character (delimiter).
+			fi
+
+			has_next=$(( len != 0 ))
+			if (( $#items == 5 )); then
+				items+=($has_next)
+				$callback "${(@)items}"  # Send all parsed items to the callback.
+				(( num_processed++ ))
+			elif [[ -z $items ]]; then
+				# Empty items occur between results due to double-null ($'\0\0')
+				# caused by commands being both pre and suffixed with null.
+			else
+				# In case of corrupt data, invoke callback with *async* as job
+				# name, non-zero exit status and an error message on stderr.
+				$callback "[async]" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next
+			fi
+		done
+	done
+
+	(( num_processed )) && return 0
+
+	# Avoid printing exit value when `setopt printexitvalue` is active.`
+	[[ $caller = trap || $caller = watcher ]] && return 0
+
+	# No results were processed
+	return 1
+}
+
+# Watch worker for output
+_async_zle_watcher() {
+	setopt localoptions noshwordsplit
+	typeset -gA ASYNC_PTYS ASYNC_CALLBACKS
+	local worker=$ASYNC_PTYS[$1]
+	local callback=$ASYNC_CALLBACKS[$worker]
+
+	if [[ -n $2 ]]; then
+		# from man zshzle(1):
+		# `hup' for a disconnect, `nval' for a closed or otherwise
+		# invalid descriptor, or `err' for any other condition.
+		# Systems that support only the `select' system call always use
+		# `err'.
+
+		# this has the side effect to unregister the broken file descriptor
+		async_stop_worker $worker
+
+		if [[ -n $callback ]]; then
+			$callback '[async]' 2 "" 0 "$0:$LINENO: error: fd for $worker failed: zle -F $1 returned error $2" 0
+		fi
+		return
+	fi;
+
+	if [[ -n $callback ]]; then
+		async_process_results $worker $callback watcher
+	fi
+}
+
+_async_send_job() {
+	setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
+
+	local caller=$1
+	local worker=$2
+	shift 2
+
+	zpty -t $worker &>/dev/null || {
+		typeset -gA ASYNC_CALLBACKS
+		local callback=$ASYNC_CALLBACKS[$worker]
+
+		if [[ -n $callback ]]; then
+			$callback '[async]' 3 "" 0 "$0:$LINENO: error: no such worker: $worker" 0
+		else
+			print -u2 "$caller: no such async worker: $worker"
+		fi
+		return 1
+	}
+
+	zpty -w $worker "$@"$'\0'
+}
+
+#
+# Start a new asynchronous job on specified worker, assumes the worker is running.
+#
+# Note if you are using a function for the job, it must have been defined before the worker was
+# started or you will get a `command not found` error.
+#
+# usage:
+# 	async_job <worker_name> <my_function> [<function_params>]
+#
+async_job() {
+	setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
+
+	local worker=$1; shift
+
+	local -a cmd
+	cmd=("$@")
+	if (( $#cmd > 1 )); then
+		cmd=(${(q)cmd})  # Quote special characters in multi argument commands.
+	fi
+
+	_async_send_job $0 $worker "$cmd"
+}
+
+#
+# Evaluate a command (like async_job) inside the async worker, then worker environment can be manipulated. For example,
+# issuing a cd command will change the PWD of the worker which will then be inherited by all future async jobs.
+#
+# Output will be returned via callback, job name will be [async/eval].
+#
+# usage:
+# 	async_worker_eval <worker_name> <my_function> [<function_params>]
+#
+async_worker_eval() {
+	setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
+
+	local worker=$1; shift
+
+	local -a cmd
+	cmd=("$@")
+	if (( $#cmd > 1 )); then
+		cmd=(${(q)cmd})  # Quote special characters in multi argument commands.
+	fi
+
+	# Quote the cmd in case RC_EXPAND_PARAM is set.
+	_async_send_job $0 $worker "_async_eval $cmd"
+}
+
+# This function traps notification signals and calls all registered callbacks
+_async_notify_trap() {
+	setopt localoptions noshwordsplit
+
+	local k
+	for k in ${(k)ASYNC_CALLBACKS}; do
+		async_process_results $k ${ASYNC_CALLBACKS[$k]} trap
+	done
+}
+
+#
+# Register a callback for completed jobs. As soon as a job is finnished, async_process_results will be called with the
+# specified callback function. This requires that a worker is initialized with the -n (notify) option.
+#
+# usage:
+# 	async_register_callback <worker_name> <callback_function>
+#
+async_register_callback() {
+	setopt localoptions noshwordsplit nolocaltraps
+
+	typeset -gA ASYNC_PTYS ASYNC_CALLBACKS
+	local worker=$1; shift
+
+	ASYNC_CALLBACKS[$worker]="$*"
+
+	# Enable trap when the ZLE watcher is unavailable, allows
+	# workers to notify (via -n) when a job is done.
+	if [[ ! -o interactive ]] || [[ ! -o zle ]]; then
+		trap '_async_notify_trap' WINCH
+	elif [[ -o interactive ]] && [[ -o zle ]]; then
+		local fd w
+		for fd w in ${(@kv)ASYNC_PTYS}; do
+			if [[ $w == $worker ]]; then
+				zle -F $fd _async_zle_watcher  # Register the ZLE handler.
+				break
+			fi
+		done
+	fi
+}
+
+#
+# Unregister the callback for a specific worker.
+#
+# usage:
+# 	async_unregister_callback <worker_name>
+#
+async_unregister_callback() {
+	typeset -gA ASYNC_CALLBACKS
+
+	unset "ASYNC_CALLBACKS[$1]"
+}
+
+#
+# Flush all current jobs running on a worker. This will terminate any and all running processes under the worker, use
+# with caution.
+#
+# usage:
+# 	async_flush_jobs <worker_name>
+#
+async_flush_jobs() {
+	setopt localoptions noshwordsplit
+
+	local worker=$1; shift
+
+	# Check if the worker exists
+	zpty -t $worker &>/dev/null || return 1
+
+	# Send kill command to worker
+	async_job $worker "_killjobs"
+
+	# Clear the zpty buffer.
+	local junk
+	if zpty -r -t $worker junk '*'; then
+		(( ASYNC_DEBUG )) && print -n "async_flush_jobs $worker: ${(V)junk}"
+		while zpty -r -t $worker junk '*'; do
+			(( ASYNC_DEBUG )) && print -n "${(V)junk}"
+		done
+		(( ASYNC_DEBUG )) && print
+	fi
+
+	# Finally, clear the process buffer in case of partially parsed responses.
+	typeset -gA ASYNC_PROCESS_BUFFER
+	unset "ASYNC_PROCESS_BUFFER[$worker]"
+}
+
+#
+# Start a new async worker with optional parameters, a worker can be told to only run unique tasks and to notify a
+# process when tasks are complete.
+#
+# usage:
+# 	async_start_worker <worker_name> [-u] [-n] [-p <pid>]
+#
+# opts:
+# 	-u unique (only unique job names can run)
+# 	-n notify through SIGWINCH signal
+# 	-p pid to notify (defaults to current pid)
+#
+async_start_worker() {
+	setopt localoptions noshwordsplit noclobber
+
+	local worker=$1; shift
+	local -a args
+	args=("$@")
+	zpty -t $worker &>/dev/null && return
+
+	typeset -gA ASYNC_PTYS
+	typeset -h REPLY
+	typeset has_xtrace=0
+
+	if [[ -o interactive ]] && [[ -o zle ]]; then
+		# Inform the worker to ignore the notify flag and that we're
+		# using a ZLE watcher instead.
+		args+=(-z)
+
+		if (( ! ASYNC_ZPTY_RETURNS_FD )); then
+			# When zpty doesn't return a file descriptor (on older versions of zsh)
+			# we try to guess it anyway.
+			integer -l zptyfd
+			exec {zptyfd}>&1  # Open a new file descriptor (above 10).
+			exec {zptyfd}>&-  # Close it so it's free to be used by zpty.
+		fi
+	fi
+
+	# Workaround for stderr in the main shell sometimes (incorrectly) being
+	# reassigned to /dev/null by the reassignment done inside the async
+	# worker.
+	# See https://github.com/mafredri/zsh-async/issues/35.
+	integer errfd=-1
+
+	# Redirect of errfd is broken on zsh 5.0.2.
+	if is-at-least 5.0.8; then
+		exec {errfd}>&2
+	fi
+
+	# Make sure async worker is started without xtrace
+	# (the trace output interferes with the worker).
+	[[ -o xtrace ]] && {
+		has_xtrace=1
+		unsetopt xtrace
+	}
+
+	if (( errfd != -1 )); then
+		zpty -b $worker _async_worker -p $$ $args 2>&$errfd
+	else
+		zpty -b $worker _async_worker -p $$ $args
+	fi
+	local ret=$?
+
+	# Re-enable it if it was enabled, for debugging.
+	(( has_xtrace )) && setopt xtrace
+	(( errfd != -1 )) && exec {errfd}>& -
+
+	if (( ret )); then
+		async_stop_worker $worker
+		return 1
+	fi
+
+	if ! is-at-least 5.0.8; then
+		# For ZSH versions older than 5.0.8 we delay a bit to give
+		# time for the worker to start before issuing commands,
+		# otherwise it will not be ready to receive them.
+		sleep 0.001
+	fi
+
+	if [[ -o interactive ]] && [[ -o zle ]]; then
+		if (( ! ASYNC_ZPTY_RETURNS_FD )); then
+			REPLY=$zptyfd  # Use the guessed value for the file desciptor.
+		fi
+
+		ASYNC_PTYS[$REPLY]=$worker  # Map the file desciptor to the worker.
+	fi
+}
+
+#
+# Stop one or multiple workers that are running, all unfetched and incomplete work will be lost.
+#
+# usage:
+# 	async_stop_worker <worker_name_1> [<worker_name_2>]
+#
+async_stop_worker() {
+	setopt localoptions noshwordsplit
+
+	local ret=0 worker k v
+	for worker in $@; do
+		# Find and unregister the zle handler for the worker
+		for k v in ${(@kv)ASYNC_PTYS}; do
+			if [[ $v == $worker ]]; then
+				zle -F $k
+				unset "ASYNC_PTYS[$k]"
+			fi
+		done
+		async_unregister_callback $worker
+		zpty -d $worker 2>/dev/null || ret=$?
+
+		# Clear any partial buffers.
+		typeset -gA ASYNC_PROCESS_BUFFER
+		unset "ASYNC_PROCESS_BUFFER[$worker]"
+	done
+
+	return $ret
+}
+
+#
+# Initialize the required modules for zsh-async. To be called before using the zsh-async library.
+#
+# usage:
+# 	async_init
+#
+async_init() {
+	(( ASYNC_INIT_DONE )) && return
+	typeset -g ASYNC_INIT_DONE=1
+
+	zmodload zsh/zpty
+	zmodload zsh/datetime
+
+	# Load is-at-least for reliable version check.
+	autoload -Uz is-at-least
+
+	# Check if zsh/zpty returns a file descriptor or not,
+	# shell must also be interactive with zle enabled.
+	typeset -g ASYNC_ZPTY_RETURNS_FD=0
+	[[ -o interactive ]] && [[ -o zle ]] && {
+		typeset -h REPLY
+		zpty _async_test :
+		(( REPLY )) && ASYNC_ZPTY_RETURNS_FD=1
+		zpty -d _async_test
+	}
+}
+
+async() {
+	async_init
+}
+
+async "$@"
\ No newline at end of file
diff --git a/.p10k.zsh b/.p10k.zsh
index 55c9f1e6..ace8a364 100644
--- a/.p10k.zsh
+++ b/.p10k.zsh
@@ -34,7 +34,7 @@
     # =========================[ Line #1 ]=========================
     # os_icon               # os identifier
     dir                     # current directory
-    vcs                     # git status
+    jj                      # jj status
     # =========================[ Line #2 ]=========================
     newline                 # \n
     context                 # user@hostname
@@ -1735,6 +1735,163 @@
   # really need it.
   typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
 
+  ####################################[ jj: jj status ]#########################################
+  # Shamelessly stolen from https://andre.arko.net/2025/06/20/a-jj-prompt-for-powerlevel10k/
+
+  # jj prompt table of contents:
+  # ----------------------------
+  # jj_add     | add changes to jj for this prompt   | (no output)
+  # jj_at      | bookmark name and distance from @   | main›1
+  # jj_remote  | count changes ahead/behind remote   | 2⇡1⇣
+  # jj_change  | the current jj change ID            | kkor
+  # jj_desc    | current change description          | first line of description (or  )
+  # jj_status  | counts of added, removed, modified  | +1 -4 ^2
+  # jj_op      | the current jj operation ID         | b44825e56a5a
+
+  function jj_status() {
+    emulate -L zsh
+    cd "$1"
+
+    local grey='%244F'
+    local green='%2F'
+    local blue='%39F'
+    local red='%196F'
+    local yellow='%3F'
+    local cyan='%6F'
+    local magenta='%5F'
+
+    ## jj_add
+    jj --at-operation=@ debug snapshot
+
+
+    ## jj_at
+    local branch=$(jj --ignore-working-copy --at-op=@ --no-pager log --no-graph --limit 1 -r "
+      coalesce(
+        heads(::@ & (bookmarks() | remote_bookmarks() | tags())),
+        heads(@:: & (bookmarks() | remote_bookmarks() | tags())),
+        trunk()
+      )" -T "separate(' ', bookmarks, tags)" 2> /dev/null | cut -d ' ' -f 1)
+    if [[ -n $branch ]]; then
+      [[ $branch =~ "\*$" ]] && branch=${branch::-1}
+
+      local VCS_STATUS_COMMITS_AFTER=$(jj --ignore-working-copy --at-op=@ --no-pager log --no-graph -r "$branch..@ & (~empty() | merges())" -T '"n"' 2> /dev/null | wc -c | tr -d ' ')
+      local VCS_STATUS_COMMITS_BEFORE=$(jj --ignore-working-copy --at-op=@ --no-pager log --no-graph -r "@..$branch & (~empty() | merges())" -T '"n"' 2> /dev/null | wc -c | tr -d ' ')
+      local counts=($(jj --ignore-working-copy --at-op=@ --no-pager bookmark list -r $branch -T '
+        if(remote,
+          separate(" ",
+            name ++ "@" ++ remote, 
+            coalesce(tracking_ahead_count.exact(), tracking_ahead_count.lower()),
+            coalesce(tracking_behind_count.exact(), tracking_behind_count.lower()),
+            if(tracking_ahead_count.exact(), "0", "+"),
+            if(tracking_behind_count.exact(), "0", "+"),
+          ) ++ "\n"
+        )
+      '))
+
+      local VCS_STATUS_LOCAL_BRANCH=$branch
+      local VCS_STATUS_COMMITS_AHEAD=$counts[2]
+      local VCS_STATUS_COMMITS_BEHIND=$counts[3]
+      local VCS_STATUS_COMMITS_AHEAD_PLUS=$counts[4]
+      local VCS_STATUS_COMMITS_BEHIND_PLUS=$counts[5]
+    fi
+
+    local status_color=${green}
+    (( VCS_STATUS_COMMITS_AHEAD )) && status_color=${cyan}
+    (( VCS_STATUS_COMMITS_BEHIND )) && status_color=${magenta}
+    (( VCS_STATUS_COMMITS_AHEAD && VCS_STATUS_COMMITS_BEHIND )) && status_color=${red}
+
+    local res
+    local where=${(V)VCS_STATUS_LOCAL_BRANCH}
+    # If local branch name or tag is at most 32 characters long, show it in full.
+    # Otherwise show the first 12 … the last 12.
+    (( $#where > 32 )) && where[13,-13]="…"
+    res+="${status_color}${where//\%/%%}"  # escape %
+
+    # ‹42 if before the local bookmark
+    # (( VCS_STATUS_COMMITS_BEFORE )) && res+="‹${VCS_STATUS_COMMITS_BEFORE}"
+    # ›42 if beyond the local bookmark
+    # (( VCS_STATUS_COMMITS_AFTER )) && res+="›${VCS_STATUS_COMMITS_AFTER}"
+
+
+    ## jj_remote
+    # # ⇣42 if behind the remote.
+    # (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${green}⇣${VCS_STATUS_COMMITS_BEHIND}"
+    # (( VCS_STATUS_COMMITS_BEHIND_PLUS )) && res+="${VCS_STATUS_COMMITS_BEHIND_PLUS}"
+    # # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42.
+    # (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" "
+    # (( VCS_STATUS_COMMITS_AHEAD  )) && res+="${green}⇡${VCS_STATUS_COMMITS_AHEAD}"
+    # (( VCS_STATUS_COMMITS_AHEAD_PLUS )) && res+="${VCS_STATUS_COMMITS_AHEAD_PLUS}"
+
+
+    ## jj_change
+    IFS="#" local change=($(jj --ignore-working-copy --at-op=@ --no-pager log --no-graph --limit 1 -r "@" -T '
+      separate("#", change_id.shortest(4).prefix(), coalesce(change_id.shortest(4).rest(), "\0"),
+        commit_id.shortest(4).prefix(),
+        coalesce(commit_id.shortest(4).rest(), "\0"),
+        concat(
+          if(conflict, "💥"),
+          if(divergent, "🚧"),
+          if(hidden, "👻"),
+          if(immutable, "🔒"),
+        ),
+      )'))
+    local VCS_STATUS_CHANGE=($change[1] $change[2])
+    local VCS_STATUS_COMMIT=($change[3] $change[4])
+    local VCS_STATUS_ACTION=$change[5]
+    # 'zyxw' with the standard jj color coding for shortest name
+    res+=" ${magenta}${VCS_STATUS_CHANGE[1]}${grey}${VCS_STATUS_CHANGE[2]}"
+    # '💥🚧👻🔒' if the repo is in an unusual state.
+    [[ -n $VCS_STATUS_ACTION     ]] && res+=" ${red}${VCS_STATUS_ACTION}"
+    # # '123abc' with the standard jj color coding for shortest name
+    # res+=" ${blue}${VCS_STATUS_COMMIT[1]}${grey}${VCS_STATUS_COMMIT[2]}"
+
+
+    ## jj_desc
+    # local VCS_STATUS_MESSAGE=$(jj --ignore-working-copy --at-op=@ --no-pager log --no-graph --limit 1 -r "@" -T "coalesce(description.first_line(), if(!empty, '\Uf040 '))")
+    # [[ -n $VCS_STATUS_MESSAGE ]] && res+=" ${green}${VCS_STATUS_MESSAGE}"
+
+
+    ## jj_status
+    # local VCS_STATUS_CHANGES=($(jj log --ignore-working-copy --at-op=@ --no-graph --no-pager -r @ -T "diff.summary()" 2> /dev/null | awk 'BEGIN {a=0;d=0;m=0} /^A / {a++} /^D / {d++} /^M / {m++} /^R / {m++} /^C / {a++} END {print(a,d,m)}'))
+    # (( VCS_STATUS_CHANGES[1] )) && res+=" %F{green}+${VCS_STATUS_CHANGES[1]}"
+    # (( VCS_STATUS_CHANGES[2] )) && res+=" %F{red}-${VCS_STATUS_CHANGES[2]}"
+    # (( VCS_STATUS_CHANGES[3] )) && res+=" ${yellow}^${VCS_STATUS_CHANGES[3]}"
+
+
+    ## jj_op
+    # local VCS_STATUS_MESSAGE=$(jj --ignore-working-copy --at-op=@ --no-pager op log --limit 1 --no-graph -T "id.short()")
+    # [[ -n $VCS_STATUS_MESSAGE ]] && res+=" ${blue}${VCS_STATUS_MESSAGE}"
+
+
+    # return results
+    echo $res
+  }
+  function jj_status_callback() {
+    emulate -L zsh
+    if [[ $2 -ne 0 ]]; then
+      typeset -g p10k_jj_status=
+    else
+      typeset -g p10k_jj_status="$3"
+    fi
+    typeset -g p10k_jj_status_stale= p10k_jj_status_updated=1
+    p10k display -r
+  }
+
+  source ~/.config/zsh/async.zsh
+  async_start_worker        jj_status_worker -u
+  async_unregister_callback jj_status_worker
+  async_register_callback   jj_status_worker jj_status_callback
+
+  function prompt_jj() {
+    emulate -L zsh -o extended_glob
+    (( $+commands[jj]         )) || return
+    [[ -n ./(../)#(.jj)(#qN/) ]] || return
+    typeset -g p10k_jj_status_stale=1 p10k_jj_status_updated=
+    p10k segment -f grey -c '$p10k_jj_status_stale' -e -t '$p10k_jj_status'
+    p10k segment -c '$p10k_jj_status_updated' -e -t '$p10k_jj_status'
+    async_job jj_status_worker jj_status $PWD
+  }
+
   # If p10k is already loaded, reload configuration.
   # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
   (( ! $+functions[p10k] )) || p10k reload