Initial commit
This commit is contained in:
308
tools/kill-session.sh
Executable file
308
tools/kill-session.sh
Executable file
@@ -0,0 +1,308 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user