main
  1#!/usr/bin/env bash
  2# shpool-connect - Interactive shpool session selector with fuzzel/fzf
  3#
  4# Features:
  5# - Lists available hosts from SSH config (detects shpool-enabled hosts)
  6# - Lists sessions on selected host (shows connected/disconnected status)
  7# - Attaches to selected session using kitty (wayland) or current terminal
  8# - Handles already-attached sessions (force detach/attach)
  9set -euo pipefail
 10
 11# Error handler - show error and wait for user input before exiting
 12error_handler() {
 13    local exit_code=$?
 14    local line_no=$1
 15    if [[ $exit_code -ne 0 ]]; then
 16        echo ""
 17        echo "Error on line $line_no: Command exited with status $exit_code"
 18        echo "Press Enter to close..."
 19        read -r
 20    fi
 21    exit $exit_code
 22}
 23trap 'error_handler $LINENO' ERR
 24
 25# Extract shpool-enabled hosts from SSH config
 26# These are hosts with the pattern "host/*" (shpool session aliases)
 27get_shpool_hosts() {
 28    ssh -G "dummy.home/test" 2>/dev/null | head -1 >/dev/null  # validate ssh config works
 29    # Parse SSH config for "Host X/*" patterns and extract X
 30    grep -E '^Host [^*]+/\*$' ~/.ssh/config 2>/dev/null | \
 31        sed 's/^Host //' | sed 's|/\*$||' | sort -u
 32}
 33
 34# Detect environment
 35DESKTOP="${XDG_CURRENT_DESKTOP:-}"
 36GRAPHICS=1
 37
 38if [[ "$DESKTOP" != "sway" && "$DESKTOP" != "niri" ]]; then
 39    if ! command -v xset &>/dev/null; then
 40        GRAPHICS=0
 41    elif ! timeout 1s xset q &>/dev/null 2>&1; then
 42        GRAPHICS=0
 43    fi
 44fi
 45
 46# Select function based on environment
 47select_item() {
 48    local prompt="$1"
 49    if [[ GRAPHICS -eq 0 ]]; then
 50        fzf --prompt "$prompt"
 51    elif [[ "$DESKTOP" == "sway" || "$DESKTOP" == "niri" ]]; then
 52        fuzzel --dmenu --prompt "$prompt" --lines 15 --width 50
 53    else
 54        fzf --prompt "$prompt"
 55    fi
 56}
 57
 58notify() {
 59    local title="$1"
 60    local msg="$2"
 61    if [[ GRAPHICS -eq 1 ]]; then
 62        notify-send "$title" "$msg"
 63    else
 64        echo "$title: $msg"
 65    fi
 66}
 67
 68# Step 1: Select host
 69shpool_hosts=$(get_shpool_hosts)
 70
 71if [[ -z "$shpool_hosts" ]]; then
 72    notify "shpool" "No shpool-enabled hosts found in SSH config"
 73    exit 1
 74fi
 75
 76selected_host=$(echo "$shpool_hosts" | select_item "Host: ")
 77
 78if [[ -z "$selected_host" ]]; then
 79    exit 0
 80fi
 81
 82# Step 2: Get sessions on the host with status
 83# Format: "session-name (status)" where status is connected/disconnected
 84session_list=$(ssh "$selected_host" shpool list 2>/dev/null | tail -n +2 | awk '{
 85    status = $3
 86    if (status == "connected") {
 87        print $1 " (attached)"
 88    } else {
 89        print $1
 90    }
 91}')
 92
 93if [[ -z "$session_list" ]]; then
 94    notify "shpool" "No sessions on $selected_host"
 95    exit 0
 96fi
 97
 98# Add option to create new session
 99sessions="[new session]"$'\n'"$session_list"
100
101selected_session=$(echo "$sessions" | select_item "Session on $selected_host: ")
102
103# Strip status suffix if present (e.g., "session (attached)" -> "session")
104selected_session="${selected_session% (attached)}"
105
106if [[ -z "$selected_session" ]]; then
107    exit 0
108fi
109
110# Handle new session creation
111if [[ "$selected_session" == "[new session]" ]]; then
112    # Prompt for session name
113    if [[ GRAPHICS -eq 0 ]]; then
114        echo -n "Session name: "
115        read -r selected_session
116    elif [[ "$DESKTOP" == "sway" || "$DESKTOP" == "niri" ]]; then
117        selected_session=$(echo "" | fuzzel --dmenu --prompt "New session name: " --width 30)
118    else
119        echo -n "Session name: "
120        read -r selected_session
121    fi
122
123    if [[ -z "$selected_session" ]]; then
124        exit 0
125    fi
126fi
127
128# Step 3: Attach to session
129ssh_target="${selected_host}/${selected_session}"
130
131if [[ GRAPHICS -eq 1 ]] && [[ "$DESKTOP" == "sway" || "$DESKTOP" == "niri" ]]; then
132    # Use kitty for graphical wayland environments
133    if command -v kitty &>/dev/null; then
134        exec kitty --single-instance --instance-group=shpool ssh "$ssh_target"
135    else
136        notify "shpool" "kitty not found, falling back to terminal"
137        exec ssh "$ssh_target"
138    fi
139else
140    # Terminal mode - just SSH directly
141    exec ssh "$ssh_target"
142fi