309 lines
8.8 KiB
Bash
Executable File
309 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# kill-session.sh - Kill tmux session and remove from registry
|
|
#
|
|
# PURPOSE:
|
|
# Atomically kill a tmux session and remove it from the session registry.
|
|
# Provides a single operation to fully clean up a session.
|
|
#
|
|
# USAGE:
|
|
# ./kill-session.sh [options]
|
|
#
|
|
# OPTIONS:
|
|
# -s, --session NAME Session name (uses registry lookup)
|
|
# -S, --socket PATH Socket path (explicit mode, requires -t)
|
|
# -t, --target TARGET Target pane (explicit mode, requires -S)
|
|
# --dry-run Show what would be done without executing
|
|
# -v, --verbose Verbose output
|
|
# -h, --help Show this help message
|
|
#
|
|
# EXIT CODES:
|
|
# 0 - Complete success (tmux session killed AND deregistered)
|
|
# 1 - Partial success (one operation succeeded, one failed)
|
|
# 2 - Complete failure (both operations failed or session not found)
|
|
# 3 - Invalid arguments
|
|
#
|
|
# EXAMPLES:
|
|
# # Kill session by name (registry lookup)
|
|
# ./kill-session.sh -s claude-python
|
|
#
|
|
# # Kill with explicit socket/target
|
|
# ./kill-session.sh -S /tmp/claude.sock -t my-session:0.0
|
|
#
|
|
# # Dry-run to see what would happen
|
|
# ./kill-session.sh -s claude-python --dry-run
|
|
#
|
|
# # Auto-detect single session
|
|
# ./kill-session.sh
|
|
#
|
|
# DEPENDENCIES:
|
|
# - bash, tmux, jq
|
|
# - lib/registry.sh (for session registry operations)
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# Get script directory
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Source registry library
|
|
# shellcheck source=lib/registry.sh
|
|
source "$SCRIPT_DIR/lib/registry.sh"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Configuration
|
|
#------------------------------------------------------------------------------
|
|
|
|
session_name=""
|
|
socket=""
|
|
target=""
|
|
dry_run=false
|
|
verbose=false
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Functions
|
|
#------------------------------------------------------------------------------
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: kill-session.sh [options]
|
|
|
|
Kill tmux session and remove from registry (atomic operation).
|
|
|
|
Options:
|
|
-s, --session NAME Session name (uses registry lookup)
|
|
-S, --socket PATH Socket path (explicit mode, requires -t)
|
|
-t, --target TARGET Target pane (explicit mode, requires -S)
|
|
--dry-run Show what would be done without executing
|
|
-v, --verbose Verbose output
|
|
-h, --help Show this help message
|
|
|
|
Exit codes:
|
|
0 - Complete success (killed AND deregistered)
|
|
1 - Partial success (one operation succeeded)
|
|
2 - Complete failure (both failed or not found)
|
|
3 - Invalid arguments
|
|
|
|
Examples:
|
|
# Kill session by name
|
|
kill-session.sh -s claude-python
|
|
|
|
# Kill with explicit socket/target
|
|
kill-session.sh -S /tmp/claude.sock -t session:0.0
|
|
|
|
# Dry-run
|
|
kill-session.sh -s claude-python --dry-run
|
|
|
|
# Auto-detect (if only one session exists)
|
|
kill-session.sh
|
|
|
|
Priority order (if multiple methods specified):
|
|
1. Explicit -S and -t (highest priority)
|
|
2. Session name -s (registry lookup)
|
|
3. Auto-detect (if no flags and only one session exists)
|
|
EOF
|
|
}
|
|
|
|
log_verbose() {
|
|
if [[ "$verbose" == true ]]; then
|
|
echo "$@" >&2
|
|
fi
|
|
}
|
|
|
|
# Auto-detect session if only one exists in registry
|
|
# Sets session_name global variable
|
|
# Returns: 0 if detected, 1 if cannot auto-detect
|
|
auto_detect_session() {
|
|
local registry_data session_count session_names
|
|
|
|
registry_data=$(registry_list_sessions)
|
|
session_names=$(echo "$registry_data" | jq -r '.sessions | keys[]' 2>/dev/null || echo "")
|
|
|
|
if [[ -z "$session_names" ]]; then
|
|
echo "Error: No sessions in registry" >&2
|
|
return 1
|
|
fi
|
|
|
|
session_count=$(echo "$session_names" | wc -l | tr -d ' ')
|
|
|
|
if [[ "$session_count" -eq 1 ]]; then
|
|
session_name="$session_names"
|
|
log_verbose "Auto-detected session: $session_name"
|
|
return 0
|
|
else
|
|
echo "Error: Multiple sessions found, specify -s session-name:" >&2
|
|
# shellcheck disable=SC2001 # sed needed for adding prefix to multiple lines
|
|
echo "$session_names" | sed 's/^/ - /' >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Argument parsing
|
|
#------------------------------------------------------------------------------
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-s|--session)
|
|
session_name="${2:-}"
|
|
if [[ -z "$session_name" ]]; then
|
|
echo "Error: -s requires a session name" >&2
|
|
exit 3
|
|
fi
|
|
shift 2
|
|
;;
|
|
-S|--socket)
|
|
socket="${2:-}"
|
|
if [[ -z "$socket" ]]; then
|
|
echo "Error: -S requires a socket path" >&2
|
|
exit 3
|
|
fi
|
|
shift 2
|
|
;;
|
|
-t|--target)
|
|
target="${2:-}"
|
|
if [[ -z "$target" ]]; then
|
|
echo "Error: -t requires a target pane" >&2
|
|
exit 3
|
|
fi
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
dry_run=true
|
|
shift
|
|
;;
|
|
-v|--verbose)
|
|
verbose=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Error: Unknown option: $1" >&2
|
|
usage
|
|
exit 3
|
|
;;
|
|
esac
|
|
done
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Validate arguments
|
|
#------------------------------------------------------------------------------
|
|
|
|
# Priority 1: Explicit socket/target mode
|
|
if [[ -n "$socket" || -n "$target" ]]; then
|
|
if [[ -z "$socket" || -z "$target" ]]; then
|
|
echo "Error: Both -S and -t must be specified together" >&2
|
|
exit 3
|
|
fi
|
|
log_verbose "Using explicit mode: socket=$socket, target=$target"
|
|
|
|
# Priority 2: Session name mode (registry lookup)
|
|
elif [[ -n "$session_name" ]]; then
|
|
log_verbose "Using registry mode: session=$session_name"
|
|
|
|
# Look up socket and target from registry
|
|
if ! session_data=$(registry_get_session "$session_name" 2>/dev/null); then
|
|
echo "Error: Session '$session_name' not found in registry" >&2
|
|
exit 2
|
|
fi
|
|
|
|
socket=$(echo "$session_data" | jq -r '.socket')
|
|
target=$(echo "$session_data" | jq -r '.target')
|
|
|
|
if [[ -z "$socket" || -z "$target" ]]; then
|
|
echo "Error: Invalid session data in registry for '$session_name'" >&2
|
|
exit 2
|
|
fi
|
|
|
|
log_verbose "Resolved from registry: socket=$socket, target=$target"
|
|
|
|
# Priority 3: Auto-detect mode
|
|
else
|
|
log_verbose "Attempting auto-detect..."
|
|
if ! auto_detect_session; then
|
|
exit 2
|
|
fi
|
|
|
|
# Look up socket and target
|
|
session_data=$(registry_get_session "$session_name")
|
|
socket=$(echo "$session_data" | jq -r '.socket')
|
|
target=$(echo "$session_data" | jq -r '.target')
|
|
|
|
log_verbose "Auto-detected: socket=$socket, target=$target"
|
|
fi
|
|
|
|
# Extract session name from target if not already set
|
|
if [[ -z "$session_name" ]]; then
|
|
# Target format is typically "session-name:0.0"
|
|
session_name="${target%%:*}"
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Execute kill operations
|
|
#------------------------------------------------------------------------------
|
|
|
|
if [[ "$dry_run" == true ]]; then
|
|
echo "Dry-run mode: Would perform the following operations:"
|
|
echo " 1. Kill tmux session: tmux -S \"$socket\" kill-session -t \"$session_name\""
|
|
if registry_session_exists "$session_name"; then
|
|
echo " 2. Remove from registry: $session_name"
|
|
else
|
|
echo " 2. Session not in registry, skip deregistration"
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Actual execution
|
|
#------------------------------------------------------------------------------
|
|
|
|
tmux_killed=false
|
|
registry_removed=false
|
|
|
|
# Step 1: Kill tmux session
|
|
echo "Killing tmux session: $session_name"
|
|
if tmux -S "$socket" kill-session -t "$session_name" 2>/dev/null; then
|
|
echo " ✓ Tmux session killed successfully"
|
|
tmux_killed=true
|
|
log_verbose "Tmux kill-session succeeded"
|
|
else
|
|
exit_code=$?
|
|
echo " ✗ Failed to kill tmux session (exit code: $exit_code)" >&2
|
|
log_verbose "Tmux kill-session failed, session may not exist"
|
|
fi
|
|
|
|
# Step 2: Remove from registry
|
|
if registry_session_exists "$session_name"; then
|
|
echo "Removing from registry: $session_name"
|
|
if registry_remove_session "$session_name"; then
|
|
echo " ✓ Removed from registry successfully"
|
|
registry_removed=true
|
|
log_verbose "Registry removal succeeded"
|
|
else
|
|
echo " ✗ Failed to remove from registry" >&2
|
|
log_verbose "Registry removal failed"
|
|
fi
|
|
else
|
|
log_verbose "Session not in registry, skipping deregistration"
|
|
# Not in registry is OK if we killed the tmux session
|
|
registry_removed=true
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Determine final exit code
|
|
#------------------------------------------------------------------------------
|
|
|
|
if [[ "$tmux_killed" == true && "$registry_removed" == true ]]; then
|
|
echo "Session '$session_name' fully removed"
|
|
exit 0
|
|
elif [[ "$tmux_killed" == true || "$registry_removed" == true ]]; then
|
|
echo "Warning: Partial removal of session '$session_name'" >&2
|
|
exit 1
|
|
else
|
|
echo "Error: Failed to remove session '$session_name'" >&2
|
|
exit 2
|
|
fi
|