A just wrapper for tmux and global recipes

speckx1 pts0 comments

A just wrapper for tmux and global recipes | eshlox<br>06 May 2026 &middot; Tech<br>I have a few posts about just already: the basic command runner, the global justfile, and tmux window names with just. The wrapper in my .zshrc rolls all three behaviors into one function so I do not have to think about them.

What the wrapper does

If a recipe is not in the project justfile but exists in ~/.config/just/justfile, the wrapper runs the global one with --working-directory $PWD, so it acts on the current directory. That way I can keep things like clean-docker or my-ip in one place and still call them from any project.

Before a recipe runs, the wrapper renames the tmux window to the recipe name. When it exits, the window goes back to zsh. Outside of tmux this part does nothing.

The third piece is --choose. Project and global recipes show up in one fuzzy picker. Globals are dimmed and prefixed with ∘. If both files have a recipe with the same name, the project one wins.

The CLI is the same as plain just, so teammates who do not source the function get plain just and nothing breaks.

Why a wrapper, not a justfile change

I first tried doing the tmux rename inside each project justfile, with helper recipes that call tmux rename-window. It works, but it adds the same six lines to every project, and every recipe has to remember to call the restore. A wrapper does it once in my shell config, and project justfiles stay clean for teammates.

The global fallback is the same logic. I want my personal recipes everywhere, but I do not want to teach each project about them.

The code

Terminal window# `just` wrapper — adds three things on top of plain `just`:

# 1. Falls back to the global justfile (~/.config/just/justfile) when the

# project doesn't define a recipe.

# 2. Renames the tmux window to the recipe name while it runs.

# 3. `--choose` shows project + global recipes in one fuzzy picker.

# Project justfiles are untouched; teammates without this function get plain

# `just`. Keep it identical to `just`'s CLI surface.

just() {

local global_justfile="${XDG_CONFIG_HOME:-$HOME/.config}/just/justfile"

# Detect whether $PWD (or any ancestor) has a project justfile.

local in_project=0

command just --show default &>/dev/null && in_project=1

if (( ! in_project )); then

command just --summary &>/dev/null && in_project=1

fi

# `--choose`: merged fuzzy picker over project + global recipes

if [[ "$1" == "--choose" ]]; then

local chooser="${JUST_CHOOSER:-fzf --ansi}"

local project_list="" global_list="" selected

if (( in_project )); then

project_list=$(command just --summary 2>/dev/null | tr ' ' '\n' | grep -v '^$')

fi

if [[ -f "$global_justfile" ]]; then

global_list=$(command just -g --summary 2>/dev/null | tr ' ' '\n' | grep -v '^$')

fi

selected=$(

[[ -n "$project_list" ]] && while IFS= read -r r; do

printf ' %s\n' "$r"

done "$project_list"

while IFS= read -r r; do

[[ -z "$r" ]] && continue

if [[ -z "$project_list" ]] || ! echo "$project_list" | grep -qxF "$r"; then

printf '\033[2m∘\033[0m %s\n' "$r"

fi

done

} | eval "$chooser" | awk '{print $NF}'

) || return

[[ -z "$selected" ]] && return

just "$selected"

return

fi

# First non-flag argument is the recipe name.

local recipe=""

for arg in "$@"; do

[[ "$arg" == -* ]] && continue

recipe="$arg"

break

done

# `--list` / `-l`: show project recipes (if any) followed by globals.

if [[ "$1" == "--list" || "$1" == "-l" ]]; then

if (( in_project )); then

command just "$@"

fi

if [[ -f "$global_justfile" ]] && command just -g --list &>/dev/null; then

(( in_project )) && echo ""

echo "Global recipes:"

command just -g "$@"

fi

return

fi

# Bare `just` outside any project: show globals instead of erroring.

if [[ $# -eq 0 ]] && (( ! in_project )) && [[ -f "$global_justfile" ]]; then

command just -g

return

fi

# Recipe missing in project but present globally: run the global one against $PWD.

local -a prefix=(command just)

if [[ -n "$recipe" ]] \

&& [[ -f "$global_justfile" ]] \

&& ! command just --show "$recipe" &>/dev/null \

&& command just --justfile "$global_justfile" --show "$recipe" &>/dev/null; then

prefix=(command just --justfile "$global_justfile" --working-directory "$PWD")

fi

# Rename the tmux window to the recipe while it runs, then back to "zsh".

if [[ -n "$TMUX" ]] && [[ -n "$recipe" ]]; then

tmux rename-window "$recipe"

"${prefix[@]}" "$@"

local exit_code=$?

tmux rename-window "zsh"

return $exit_code

fi

"${prefix[@]}" "$@"

alias j="just --choose"

/dev/null && in_project=1 if (( ! in_project )); then command just --summary &>/dev/null && in_project=1 fi if [[ "$1" == "--choose" ]]; then local chooser="${JUST_CHOOSER:-fzf --ansi}" local project_list="" global_list="" selected if (( in_project )); then project_list=$(command just --summary 2>/dev/null | tr ' ' '\n' | grep -v '^$') fi if [[ -f "$global_justfile" ]]; then global_list=$(command just -g --summary 2>/dev/null | tr ' ' '\n' | grep -v '^$')...

recipe command project tmux in_project global

Related Articles