Skip to content

Instantly share code, notes, and snippets.

@angelsen
Created August 14, 2025 14:45
Show Gist options
  • Select an option

  • Save angelsen/30dd8e2d2b5f586fe74b5d72b791edef to your computer and use it in GitHub Desktop.

Select an option

Save angelsen/30dd8e2d2b5f586fe74b5d72b791edef to your computer and use it in GitHub Desktop.
Tmux session management utilities - trun and tam
#!/bin/sh
# tm - Unified tmux session manager
if [ "$1" = "--telp" ]; then
echo "Interactive tmux session manager"
exit 0
fi
# Loop to allow multiple operations
while true; do
# Create a temporary file for new session input
TMP_FILE=$(mktemp)
trap "rm -f $TMP_FILE" EXIT
# Get all sessions with attached client count
if ! tmux ls -F '#{session_created}:#{session_name}#{?session_attached, (#{session_attached} attached),}' 2>/dev/null > "$TMP_FILE.raw"; then
echo "[Create new session]" > "$TMP_FILE"
else
# Get the most recent session name
MOST_RECENT=$(sort -nr "$TMP_FILE.raw" | head -1 | cut -d: -f2-)
# Sort all sessions alphabetically and add star to most recent
sort -t: -k2 "$TMP_FILE.raw" | cut -d: -f2- | while read session; do
if [ "$session" = "$MOST_RECENT" ]; then
echo "★ $session" >> "$TMP_FILE"
else
echo " $session" >> "$TMP_FILE"
fi
done
echo "[Create new session]" >> "$TMP_FILE"
rm -f "$TMP_FILE.raw"
fi
# Main fzf interface with custom keybindings
RESULT=$(cat "$TMP_FILE" | fzf \
--preview 'if [ "{}" = "[Create new session]" ]; then
echo "Enter a name for the new session when prompted"
else
SESSION=$(echo {} | sed "s/^[★ ] //" | sed "s/ ([0-9]* attached)//")
tmux capture-pane -ep -J -S -2000 -t "$SESSION":0.0
fi' \
--preview-window=right:65%:nowrap:follow \
--height=80% \
--layout=reverse \
--border=rounded \
--prompt="Tmux sessions> " \
--header=$'\nKEYBOARD SHORTCUTS:\n\nEnter\n attach to session\nCtrl-h\n copy handoff message\nCtrl-n\n create new session\nCtrl-d\n kill session\nCtrl-c\n cancel\n' \
--bind="ctrl-n:execute(echo '[NEW]' > '$TMP_FILE.action')+abort" \
--bind="ctrl-h:execute(echo '[HANDOFF_COPY]' > '$TMP_FILE.action')+accept" \
--bind="ctrl-d:execute(echo '[KILL]' > '$TMP_FILE.action')+accept" \
--bind="enter:execute(echo '[ATTACH]' > '$TMP_FILE.action')+accept")
# Read the action if any
ACTION="[ATTACH]"
if [ -f "$TMP_FILE.action" ]; then
ACTION=$(cat "$TMP_FILE.action")
rm -f "$TMP_FILE.action"
fi
# Handle new session creation
if [ "$ACTION" = "[NEW]" ] || [ "$RESULT" = "[Create new session]" ]; then
echo -n "New session name: "
read SESSION_NAME
if [ -z "$SESSION_NAME" ]; then
echo "Cancelled"
exit 0
fi
# Load and copy handoff message to clipboard
SCRIPT_DIR="$(dirname "$0")"
. "$SCRIPT_DIR/mcp_helper.sh"
MESSAGE=$(get_mcp_template "HANDOFF" "$SESSION_NAME")
echo -n "$MESSAGE" | wl-copy
tmux new -s "$SESSION_NAME"
exit 0
fi
# Exit if no selection
if [ -z "$RESULT" ] || [ "$RESULT" = "[Create new session]" ]; then
exit 0
fi
# Handle different attachment modes
# Remove star prefix and (X attached) suffix if present
SESSION_NAME=$(echo "$RESULT" | sed 's/^[★ ] //' | sed 's/ ([0-9]* attached)//')
# Handle kill action
if [ "$ACTION" = "[KILL]" ]; then
# Show confirmation prompt
CONFIRM=$(echo -e "NO\nYES" | fzf \
--height=40% \
--layout=reverse \
--prompt="Kill session '$SESSION_NAME'? " \
--header="This will terminate the session and all its processes" \
--preview="echo 'Session: $SESSION_NAME'" \
--preview-window=hidden)
if [ "$CONFIRM" = "YES" ]; then
tmux kill-session -t "$SESSION_NAME" 2>/dev/null
echo "Killed session: $SESSION_NAME"
sleep 0.5
fi
# Continue the loop to show updated list
continue
fi
# Handle copy action
if [ "$ACTION" = "[HANDOFF_COPY]" ]; then
# Load and copy handoff message to clipboard
SCRIPT_DIR="$(dirname "$0")"
. "$SCRIPT_DIR/mcp_helper.sh"
MESSAGE=$(get_mcp_template "HANDOFF" "$SESSION_NAME")
echo -n "$MESSAGE" | wl-copy
echo "Copied handoff message for session: $SESSION_NAME"
sleep 1
continue
fi
# Only attach (no copy) for Enter
case "$ACTION" in
*)
# Just attach, no clipboard copy
;;
esac
tmux a -t "$SESSION_NAME"
# After detaching, continue the loop to show tam again
continue
# End of while loop
done

Tmux Session Management Tools

Two handy shell scripts for managing tmux sessions more efficiently.

Installation

  1. Download both scripts:
# Using wget
wget https://gist.github.com/YOUR_GIST_ID/raw/trun
wget https://gist.github.com/YOUR_GIST_ID/raw/tam

# Or using curl
curl -O https://gist.github.com/YOUR_GIST_ID/raw/trun
curl -O https://gist.github.com/YOUR_GIST_ID/raw/tam
  1. Make them executable:
chmod +x trun tam
  1. Move to a directory in your PATH:
# Option 1: User's local bin (create if doesn't exist)
mkdir -p ~/.local/bin
mv trun tam ~/.local/bin/

# Option 2: System-wide (requires sudo)
sudo mv trun tam /usr/local/bin/
  1. Ensure ~/.local/bin is in your PATH (add to ~/.bashrc or ~/.zshrc):
export PATH="$HOME/.local/bin:$PATH"

Dependencies

  • tmux - Terminal multiplexer
  • fzf - Fuzzy finder (required for tam)

Install dependencies:

# Ubuntu/Debian
sudo apt install tmux fzf

# macOS
brew install tmux fzf

# Arch Linux
sudo pacman -S tmux fzf

Usage

trun - Create and Run Commands in Tmux Sessions

Creates auto-numbered tmux sessions with optional command execution.

# Create a new session in current directory
trun

# Run a command in a new session
trun npm run dev

# Specify a custom name suffix
trun --name dev npm run dev

# Use a different working directory
trun --path ~/projects/myapp npm start

# Combine options
trun --path ~/projects --name build make

Sessions are named based on the directory (e.g., myproject-1, myproject-2).

tam - Tmux Attachment Manager

Interactive session manager with preview and keyboard shortcuts.

# Launch the interactive manager
tam

Keyboard shortcuts:

  • Enter - Attach to selected session
  • Ctrl-n - Create new session
  • Ctrl-d - Kill selected session
  • Ctrl-c - Cancel/exit

Features:

  • Shows all sessions with attachment status
  • Preview pane shows session content
  • Most recent session marked with ★
  • Stays open after detaching for quick switching

Tips

  • trun is great for starting development servers in the background
  • tam makes it easy to switch between multiple sessions quickly

License

Feel free to use and modify these scripts as needed!

#!/bin/sh
# trun - Create auto-numbered tmux session and run command
if [ "$1" = "--telp" ]; then
echo "Create tmux session with auto-numbering and run command"
exit 0
fi
# Parse arguments
CUSTOM_NAME=""
WORKING_PATH=""
COMMAND=""
while [ $# -gt 0 ]; do
case "$1" in
--name)
if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then
echo "Error: --name requires a value"
exit 1
fi
CUSTOM_NAME="$2"
shift 2
;;
--path)
if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then
echo "Error: --path requires a value"
exit 1
fi
WORKING_PATH="$2"
shift 2
;;
*)
# Collect remaining args as command
if [ -z "$COMMAND" ]; then
COMMAND="$1"
else
COMMAND="$COMMAND $1"
fi
shift
;;
esac
done
# Default to no command (just create session)
# Use provided path or current directory
if [ -z "$WORKING_PATH" ]; then
WORKING_PATH="$(pwd)"
fi
# Expand ~ in path
WORKING_PATH=$(eval echo "$WORKING_PATH")
# Check if path exists
if [ ! -d "$WORKING_PATH" ]; then
echo "Error: Path does not exist: $WORKING_PATH"
exit 1
fi
# Get directory name and create slug
DIR_NAME=$(basename "$WORKING_PATH")
DIR_SLUG=$(echo "$DIR_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
# Determine session name
if [ -n "$CUSTOM_NAME" ]; then
SESSION_NAME="${DIR_SLUG}-${CUSTOM_NAME}"
else
# Find next available number
NUM=1
while tmux has-session -t "${DIR_SLUG}-${NUM}" 2>/dev/null; do
NUM=$((NUM + 1))
done
SESSION_NAME="${DIR_SLUG}-${NUM}"
fi
# Create session
if [ -n "$COMMAND" ]; then
tmux new-session -d -s "$SESSION_NAME" -c "$WORKING_PATH" "$COMMAND"
else
tmux new-session -d -s "$SESSION_NAME" -c "$WORKING_PATH"
fi
# Wait for command to start
sleep 3
# Capture initial output
INITIAL_OUTPUT=$(tmux capture-pane -p -t "$SESSION_NAME":0.0)
# Display result
echo "Created session: $SESSION_NAME"
echo "Working directory: $WORKING_PATH"
if [ -n "$COMMAND" ]; then
echo "Running: $COMMAND"
fi
echo ""
echo "Initial output:"
echo "$INITIAL_OUTPUT"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment