Tmux Sessionizer Workflow

This is the terminal workflow I use daily for development. One launcher gives me quick access to:

From there, I jump straight into Neovim and OpenCode in the right context.

Why this makes terminal navigation easy

Instead of manually typing cd, ssh, and mount commands all day, I hit one keybinding, fuzzy-find the target, and tmux switches me into the right session.

This keeps context switching fast while coding, editing, and hopping between local and remote machines.

1) Make the script callable

Save the script as:

~/.local/bin/tmux-sessionizer

Make it executable:

chmod +x ~/.local/bin/tmux-sessionizer

2) Terminal keybinding (Alt+f)

In Bash, add this to ~/.bashrc:

bind -x '"\ef":"~/.local/bin/tmux-sessionizer"'

Reload shell config:

source ~/.bashrc

Now Alt+f launches the sessionizer directly from your terminal.

3) tmux config is important

To trigger sessionizer from inside tmux (including SSH sessions), add this to ~/.tmux.conf:

bind-key -n C-f run-shell "tmux neww ~/.local/bin/tmux-sessionizer"

Reload tmux config:

tmux source-file ~/.tmux.conf

With this in place, the launcher is always available whether you are in local panes or remote shells.

Script (IP addresses redacted)

#!/usr/bin/env bash

# Define SSH servers (embedded directly in script)
declare -A ssh_servers=(
    ["Karto-5"]="ssh -A -J azureuser@<jump-host-ip> zac@localhost -p 35725"
    ["Karto-7"]="ssh -A -J azureuser@<jump-host-ip> zac@localhost -p 35727"
    ["Karto-8"]="ssh -A -J azureuser@<jump-host-ip> zac@localhost -p 35728"
    ["Readar-DB"]="ssh -L 5432:localhost:5432 zac@<db-host-ip>"
    ["Transip-Webportal"]="ssh zac@<web-host-ip>"
    ["Vultr"]="ssh root@<vps-host-ip>"
    ["Truenas"]="ssh truenas_admin@<truenas-lan-ip>"
    ["RPi-5"]="ssh zac@<rpi-lan-ip>"
)

# Define network mounts
declare -A network_mounts=(
    ["Dropbox"]="/mnt/dropbox|rclone mount \"Readar_Dropbox:/\" /mnt/dropbox --daemon --vfs-cache-mode writes"
    ["NAS"]="/mnt/NAS|sshfs karto@<nas-lan-ip>:/NAS /mnt/NAS -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,ConnectTimeout=10"
    ["NAS2"]="/mnt/NAS2|sshfs karto@<nas-lan-ip>:/NAS2 /mnt/NAS2 -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,ConnectTimeout=10"
)

if [[ $# -eq 1 ]]; then
    selected=$1
else
    fifo=$(mktemp -u /tmp/tmux_dirs.XXXXXX)
    mkfifo "$fifo"
    # Get SSH servers with [SSH] prefix
    ssh_list=""
    for server in "${!ssh_servers[@]}"; do
        ssh_list+="[SSH] $server"$'\n'
    done
    # Get network mounts with [MOUNT] prefix
    mount_list=""
    for mount in "${!network_mounts[@]}"; do
        mount_list+="[MOUNT] $mount"$'\n'
    done
    # Run find in background, append SSH and mount lists
    (echo -e "$ssh_list$mount_list"; find ~/Documents/3DPrinting/3DPrinting ~/Documents/Work/ ~/.config/ ~/.local/ ~/.cache/ ~/.local/src/ ~/.local/share/ ~/ ~/Documents/ /mnt/ -mindepth 1 -maxdepth 2 -type d 2>/dev/null) > "$fifo" &
    # Read from fifo with fzf
    selected=$(fzf < "$fifo")
    # Clean up
    rm "$fifo"
fi

if [[ -z $selected ]]; then
    exit 0
fi

# Check if this is an SSH selection
if [[ $selected == \[SSH\]* ]]; then
    # Extract server name (remove "[SSH] " prefix)
    server_name="${selected#\[SSH\] }"
    selected_name=$(echo "$server_name" | tr . _)
    ssh_command="${ssh_servers[$server_name]}"

    tmux_running=$(pgrep tmux)

    # Create or switch to SSH session
    if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then
        tmux new-session -s "$selected_name" "$ssh_command"
        exit 0
    fi

    if ! tmux has-session -t="$selected_name" 2> /dev/null; then
        tmux new-session -ds "$selected_name" "$ssh_command"
    fi

    # Switch to the session
    if [[ -z $TMUX ]]; then
        tmux attach-session -t "$selected_name"
    else
        tmux switch-client -t "$selected_name"
    fi
# Check if this is a mount selection
elif [[ $selected == \[MOUNT\]* ]]; then
    # Extract mount name (remove "[MOUNT] " prefix)
    mount_name="${selected#\[MOUNT\] }"
    mount_info="${network_mounts[$mount_name]}"

    # Split mount info into path and command
    mount_path="${mount_info%%|*}"
    mount_command="${mount_info#*|}"

    # Mount if directory is empty
    if [ -z "$(ls -A "$mount_path" 2>/dev/null)" ]; then
        eval "$mount_command"
    fi

    # Set selected to the mount path and continue with directory logic
    selected="$mount_path"
fi

# Directory selection (original behavior)
if [[ $selected != \[SSH\]* ]]; then
    selected_name=$(basename "$selected" | tr . _)
    tmux_running=$(pgrep tmux)

    if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then
        tmux new-session -s "$selected_name" -c "$selected"
        exit 0
    fi

    if ! tmux has-session -t="$selected_name" 2> /dev/null; then
        tmux new-session -ds "$selected_name" -c "$selected"
    fi

    # Switch to the session
    if [[ -z $TMUX ]]; then
        tmux attach-session -t "$selected_name"
    else
        tmux switch-client -t "$selected_name"
    fi
fi

Development flow

Typical loop:

  1. Alt+f -> pick project folder
  2. Open Neovim in that tmux session
  3. Use OpenCode from the same working context
  4. Jump to SSH host or mounted storage when needed
  5. Return to local sessions instantly with tmux switching

It is a simple setup, but it removes a lot of friction from daily development.

Related
Tmux · Terminal · Neovim · Cli

https://zaclloyd.net/

RSS Feed