Files
gh-dashed-claude-marketplac…/tools/kill-session.sh
2025-11-29 18:17:58 +08:00

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