Shell integration

Configure your shell prompt, environment tracking, completions, and session picker


shell prompt #

When you attach to a zmx session, we don't provide any indication that you are inside zmx. We do provide an environment variable ZMX_SESSION which contains the session name.

We recommend checking for that env var inside your prompt and displaying some indication there.

fish #

Place this file in ~/.config/fish/config.fish:

1functions -c fish_prompt _original_fish_prompt 2>/dev/null
2
3function fish_prompt --description 'Write out the prompt'
4  if set -q ZMX_SESSION
5    echo -n "[$ZMX_SESSION] "
6  end
7  _original_fish_prompt
8end

bash and zsh #

Depending on the shell, place this in either .bashrc or .zshrc:

1if [[ -n $ZMX_SESSION ]]; then
2  export PS1="[$ZMX_SESSION] ${PS1}"
3fi

powerlevel10k zsh theme #

powerlevel10k is a theme for zsh that overwrites the default prompt statusline.

Place this in .zshrc:

1function prompt_my_zmx_session() {
2  if [[ -n $ZMX_SESSION ]]; then
3    p10k segment -b '%k' -f '%f' -t "[$ZMX_SESSION]"
4  fi
5}
6POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS+=my_zmx_session

oh-my-posh #

oh-my-posh is a popular shell themeing and prompt engine. This code will display an icon and session name as part of the prompt if (and only if) you have zmx active:

1[[blocks.segments]]
2   template = '{{ if .Env.ZMX_SESSION }} {{ .Env.ZMX_SESSION }}{{ end }}'
3   foreground = 'p:orange'
4   background = 'p:black'
5   type = 'text'
6   style = 'plain'

Starship #

Starship is a popular shell themeing and prompt engine. This code will display an icon and session name as part of the prompt if (and only if) you have zmx active:

 1format = """
 2${env_var.ZMX_SESSION}\
 3...
 4"""
 5
 6[env_var.ZMX_SESSION]
 7symbol = " "
 8format = "[$symbol$env_value]($style) "
 9description = "zmx session name"
10style = "bold magenta"

environment variables #

A session's environment is initialized when the session is created. When re-attaching from a new terminal or a different SSH connection, environment variables describing the client connection (such as SSH_AUTH_SOCK, DISPLAY, or terminal remote control variables like KITTY_LISTEN_ON, KITTY_PID, KITTY_WINDOW_ID) become outdated in the running shell.

zmx tracks these environment variables from attaching clients. You can inspect the current leader client's environment with zmx print-env:

1# Print all tracked environment variables (set variables as KEY=VALUE, unset as -KEY)
2zmx print-env .
3zmx print-env dev
4
5# Print POSIX shell commands (export ... / unset ...) suitable for eval
6zmx print-env -s .
7
8# Print a specific variable value
9zmx print-env . SSH_AUTH_SOCK

You can automatically update your shell environment before each prompt:

bash #

Add to ~/.bashrc:

1_zmx_env_hook() {
2  if [[ -n $ZMX_SESSION ]]; then
3    eval "$(zmx print-env -s .)"
4  fi
5}
6PROMPT_COMMAND="_zmx_env_hook${PROMPT_COMMAND:+; $PROMPT_COMMAND}"

zsh #

Add to ~/.zshrc:

1precmd() {
2  if [[ -n $ZMX_SESSION ]]; then
3    eval "$(zmx print-env -s .)"
4  fi
5}

fish #

Add to ~/.config/fish/config.fish:

 1function _zmx_env_hook --on-event fish_prompt
 2  if test -n "$ZMX_SESSION"
 3    for line in (zmx print-env .)
 4      if string match -q -- "-*" $line
 5        set -e (string sub -s 2 $line)
 6      else
 7        set -gx (string split -m 1 "=" $line)
 8      end
 9    end
10  end
11end

configuring tracked variables #

By default, zmx tracks:

  • DISPLAY
  • SSH_AUTH_SOCK
  • SSH_AGENT_PID
  • SSH_CONNECTION
  • WINDOWID
  • XAUTHORITY
  • KITTY_LISTEN_ON
  • KITTY_PID
  • KITTY_WINDOW_ID

You can customize this list by setting ZMX_TRACK_ENV to a comma-separated list of variable names:

1export ZMX_TRACK_ENV="DISPLAY,SSH_AUTH_SOCK,GPG_AGENT_INFO"

shell completion #

Shell auto-completion for zmx commands and session names can be enabled using the completions subcommand. Once configured, you'll get auto-complete for both local zmx commands and sessions:

1ssh remote-server zmx attach session-na<TAB>
2# <- auto-complete suggestions appear here

NOTICE: when installing zmx with homebrew completions are automatically installed.

bash #

Add this to your .bashrc file:

1if command -v zmx &> /dev/null; then
2  eval "$(zmx completions bash)"
3fi

zsh #

Add this to your .zshrc file:

1if command -v zmx &> /dev/null; then
2  eval "$(zmx completions zsh)"
3fi

fish #

Add this to ~/.config/fish/completions/zmx.fish:

1if type -q zmx
2  zmx completions fish | source
3end

yash #

Add this to your ~/.config/yash/rc:

1if command -v zmx > /dev/null 2>&1; then
2  eval "$(zmx completions yash)"
3fi

Or save as an autoloaded script in your completion directory:

1zmx completions yash > ~/.config/yash/completion/zmx

nu #

Add this to your Nushell config (config.nu):

use ("~/.cache/zmx/completions.nu" | path expand)
# Or generate dynamically:
zmx completions nu | save -f ~/.cache/zmx/completions.nu

session picker #

An interactive session picker using fzf to attach to existing sessions, preview scrollback, or create new sessions.

  • Enter selects a matched session (or creates one if no sessions exist)
  • Ctrl-N creates a new session using the typed query, even when a fuzzy match is highlighted

bash and zsh #

 1zmx-select() {
 2  local display
 3  local prefix="${ZMX_SESSION_PREFIX:-}"
 4  display=$(zmx list 2>/dev/null | while IFS=$'\t' read -r name pid clients created dir; do
 5    name=${name#*name=}
 6    pid=${pid#*pid=}
 7    clients=${clients#*clients=}
 8    dir=${dir#*start_dir=}
 9    if [[ -n "$prefix" ]]; then
10      [[ "$name" == "$prefix"* ]] || continue
11      name=${name#"$prefix"}
12    fi
13    printf "%-20s  pid:%-8s  clients:%-2s  %s\n" "$name" "$pid" "$clients" "$dir"
14  done)
15
16  local output query key selected session_name
17  output=$({ [[ -n "$display" ]] && echo "$display"; } | fzf \
18    --print-query \
19    --expect=ctrl-n \
20    --height=80% \
21    --reverse \
22    --prompt="zmx> " \
23    --header="Enter: select | Ctrl-N: create new" \
24    --preview='zmx history {1}' \
25    --preview-window=right:60%:follow \
26  )
27  local rc=$?
28
29  query=$(echo "$output" | sed -n '1p')
30  key=$(echo "$output" | sed -n '2p')
31  selected=$(echo "$output" | sed -n '3p')
32
33  if [[ "$key" == "ctrl-n" && -n "$query" ]]; then
34    session_name="$query"
35  elif [[ $rc -eq 0 && -n "$selected" ]]; then
36    session_name=$(echo "$selected" | awk '{print $1}')
37  elif [[ -n "$query" ]]; then
38    session_name="$query"
39  else
40    return 130
41  fi
42
43  zmx attach "$session_name"
44}

Run zmx-select directly or launch it from your shell rc file when outside a session. With && exit, detaching or exiting the session closes the SSH connection, while ctrl+c in the picker drops into a regular shell:

1if command -v zmx &> /dev/null && command -v fzf &> /dev/null && [[ -z "$ZMX_SESSION" ]]; then
2  zmx-select && exit
3fi

alternative: gentle hint (server use) #

On shared servers where you don't want fzf to launch on every login, print a reminder when active sessions exist:

1if command -v zmx &> /dev/null && [[ -z "$ZMX_SESSION" ]]; then
2  local count
3  count=$(zmx ls --short 2>/dev/null | wc -l)
4  if [[ "$count" -gt 0 ]]; then
5    echo "zmx: $count session(s) active (run zmx-select to attach)" >&2
6  fi
7fi
<< PREV
Install & Usage
NEXT >>
SSH workflow