Skip to content

Commit 3985975

Browse files
MrFlounderclaude
andcommitted
feat: add live pairing - real-time session sharing with teammates
- `crab pair <N>` starts shared tmux session - `crab join <N>` joins with full access - `crab spectate <N>` joins as view-only spectator - `crab pair <N> --tmate` creates public URLs via tmate - Socket-based sharing for local network - SSH command generation for remote pairing - `crab pair ls` shows active sessions Version bump to 0.4.0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fa01f3c commit 3985975

File tree

1 file changed

+262
-1
lines changed

1 file changed

+262
-1
lines changed

src/crabcode

Lines changed: 262 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
set -e
2929

30-
VERSION="0.3.0"
30+
VERSION="0.4.0"
3131
CONFIG_DIR="$HOME/.crabcode"
3232
CONFIG_FILE="$CONFIG_DIR/config.yaml"
3333
WIP_BASE="$CONFIG_DIR/wip"
@@ -5137,6 +5137,239 @@ handle_rewind_command() {
51375137
esac
51385138
}
51395139

5140+
# =============================================================================
5141+
# Live Pairing - Real-time session sharing with teammates
5142+
# =============================================================================
5143+
5144+
PAIR_SOCKET_DIR="$CONFIG_DIR/pair"
5145+
5146+
# Start a pairing session (host)
5147+
start_pair_session() {
5148+
local num="$1"
5149+
local mode="${2:-pair}" # pair or spectate
5150+
5151+
mkdir -p "$PAIR_SOCKET_DIR"
5152+
5153+
local socket_name="crab-pair-$num"
5154+
local socket_path="$PAIR_SOCKET_DIR/$socket_name"
5155+
5156+
# Check if session already exists
5157+
if [ -S "$socket_path" ]; then
5158+
echo -e "${YELLOW}Pairing session already active for workspace $num${NC}"
5159+
echo ""
5160+
echo "Share this command with your teammate:"
5161+
echo -e " ${GREEN}crab join $num${NC}"
5162+
echo ""
5163+
echo "Or for spectate-only mode:"
5164+
echo -e " ${GREEN}crab spectate $num${NC}"
5165+
return
5166+
fi
5167+
5168+
echo -e "${CYAN}╭────────────────────────────────────────────────────╮${NC}"
5169+
echo -e "${CYAN}${NC} ${BOLD}🦀 Live Pairing - Workspace $num${NC} ${CYAN}${NC}"
5170+
echo -e "${CYAN}╰────────────────────────────────────────────────────╯${NC}"
5171+
echo ""
5172+
5173+
# Create shared tmux session with socket
5174+
local window_name="ws$num"
5175+
5176+
if ! tmux -S "$socket_path" has-session -t "$SESSION_NAME" 2>/dev/null; then
5177+
echo " 🔌 Creating shared session..."
5178+
# Create a new session with the socket
5179+
tmux -S "$socket_path" new-session -d -s "$SESSION_NAME" -n "$window_name"
5180+
5181+
# Set permissions so others can join
5182+
chmod 777 "$socket_path" 2>/dev/null || true
5183+
fi
5184+
5185+
echo ""
5186+
echo -e "${GREEN}✓ Pairing session active!${NC}"
5187+
echo ""
5188+
echo -e "${BOLD}Share with teammates:${NC}"
5189+
echo ""
5190+
echo -e " ${CYAN}Same machine:${NC}"
5191+
echo " crab join $num"
5192+
echo " crab spectate $num (view only)"
5193+
echo ""
5194+
echo -e " ${CYAN}Remote (via SSH):${NC}"
5195+
echo " ssh $(whoami)@$(hostname) -t 'crab join $num'"
5196+
echo ""
5197+
echo -e " ${CYAN}Share via tmate (public URL):${NC}"
5198+
if command_exists tmate; then
5199+
echo " crab pair $num --tmate"
5200+
else
5201+
echo " Install tmate: brew install tmate"
5202+
fi
5203+
echo ""
5204+
5205+
# Attach to the session
5206+
tmux -S "$socket_path" attach-session -t "$SESSION_NAME"
5207+
}
5208+
5209+
# Join a pairing session
5210+
join_pair_session() {
5211+
local num="$1"
5212+
local mode="${2:-pair}" # pair or spectate
5213+
5214+
local socket_name="crab-pair-$num"
5215+
local socket_path="$PAIR_SOCKET_DIR/$socket_name"
5216+
5217+
if [ ! -S "$socket_path" ]; then
5218+
error "No active pairing session for workspace $num"
5219+
echo ""
5220+
echo "Ask the host to start one with: crab pair $num"
5221+
exit 1
5222+
fi
5223+
5224+
echo -e "${CYAN}Joining workspace $num...${NC}"
5225+
5226+
if [ "$mode" = "spectate" ]; then
5227+
echo -e "${YELLOW}Spectate mode: view only, no input${NC}"
5228+
tmux -S "$socket_path" attach-session -t "$SESSION_NAME" -r
5229+
else
5230+
tmux -S "$socket_path" attach-session -t "$SESSION_NAME"
5231+
fi
5232+
}
5233+
5234+
# Start tmate session for public sharing
5235+
start_tmate_session() {
5236+
local num="$1"
5237+
5238+
if ! command_exists tmate; then
5239+
error "tmate is not installed"
5240+
echo ""
5241+
echo "Install with: brew install tmate"
5242+
exit 1
5243+
fi
5244+
5245+
echo -e "${CYAN}╭────────────────────────────────────────────────────╮${NC}"
5246+
echo -e "${CYAN}${NC} ${BOLD}🦀 Live Pairing via tmate - Workspace $num${NC} ${CYAN}${NC}"
5247+
echo -e "${CYAN}╰────────────────────────────────────────────────────╯${NC}"
5248+
echo ""
5249+
echo " Starting tmate session..."
5250+
echo ""
5251+
5252+
# Start tmate
5253+
tmate -F -v -S "/tmp/tmate-crab-$num.sock" new-session -d -s "crab-$num" 2>/dev/null &
5254+
local tmate_pid=$!
5255+
5256+
sleep 2
5257+
5258+
# Get connection info
5259+
local ssh_url=$(tmate -S "/tmp/tmate-crab-$num.sock" display -p '#{tmate_ssh}' 2>/dev/null)
5260+
local web_url=$(tmate -S "/tmp/tmate-crab-$num.sock" display -p '#{tmate_web}' 2>/dev/null)
5261+
local ssh_ro_url=$(tmate -S "/tmp/tmate-crab-$num.sock" display -p '#{tmate_ssh_ro}' 2>/dev/null)
5262+
local web_ro_url=$(tmate -S "/tmp/tmate-crab-$num.sock" display -p '#{tmate_web_ro}' 2>/dev/null)
5263+
5264+
echo -e "${GREEN}✓ tmate session ready!${NC}"
5265+
echo ""
5266+
echo -e "${BOLD}Share these links with your teammate:${NC}"
5267+
echo ""
5268+
echo -e " ${CYAN}Full access (pair):${NC}"
5269+
[ -n "$ssh_url" ] && echo " SSH: $ssh_url"
5270+
[ -n "$web_url" ] && echo " Web: $web_url"
5271+
echo ""
5272+
echo -e " ${CYAN}View only (spectate):${NC}"
5273+
[ -n "$ssh_ro_url" ] && echo " SSH: $ssh_ro_url"
5274+
[ -n "$web_ro_url" ] && echo " Web: $web_ro_url"
5275+
echo ""
5276+
5277+
# Attach
5278+
tmate -S "/tmp/tmate-crab-$num.sock" attach-session -t "crab-$num"
5279+
}
5280+
5281+
# List active pairing sessions
5282+
list_pair_sessions() {
5283+
echo -e "${CYAN}Active Pairing Sessions:${NC}"
5284+
echo ""
5285+
5286+
local found=false
5287+
for socket in "$PAIR_SOCKET_DIR"/crab-pair-*; do
5288+
[ -S "$socket" ] || continue
5289+
found=true
5290+
5291+
local num=$(basename "$socket" | sed 's/crab-pair-//')
5292+
local clients=$(tmux -S "$socket" list-clients 2>/dev/null | wc -l | tr -d ' ')
5293+
5294+
echo -e " ${BOLD}Workspace $num${NC} - $clients connected"
5295+
echo " Join: crab join $num"
5296+
echo " Spectate: crab spectate $num"
5297+
echo ""
5298+
done
5299+
5300+
if [ "$found" = false ]; then
5301+
echo " No active sessions."
5302+
echo ""
5303+
echo " Start one with: crab pair <workspace-number>"
5304+
fi
5305+
}
5306+
5307+
# End a pairing session
5308+
end_pair_session() {
5309+
local num="$1"
5310+
local socket_path="$PAIR_SOCKET_DIR/crab-pair-$num"
5311+
5312+
if [ ! -S "$socket_path" ]; then
5313+
echo "No active session for workspace $num"
5314+
return
5315+
fi
5316+
5317+
echo -e "${YELLOW}Ending pairing session for workspace $num...${NC}"
5318+
tmux -S "$socket_path" kill-session -t "$SESSION_NAME" 2>/dev/null || true
5319+
rm -f "$socket_path"
5320+
success "Session ended"
5321+
}
5322+
5323+
# Handle pair commands
5324+
handle_pair_command() {
5325+
load_config 2>/dev/null || true
5326+
5327+
case "${1:-}" in
5328+
"ls"|"list")
5329+
list_pair_sessions
5330+
;;
5331+
"end"|"stop")
5332+
if [ -z "${2:-}" ]; then
5333+
error "Usage: crab pair end <workspace-number>"
5334+
exit 1
5335+
fi
5336+
end_pair_session "$2"
5337+
;;
5338+
"")
5339+
# Auto-detect workspace
5340+
load_config
5341+
validate_config
5342+
local num=$(detect_workspace)
5343+
if [ -z "$num" ]; then
5344+
error "Specify workspace: crab pair <N>"
5345+
exit 1
5346+
fi
5347+
start_pair_session "$num"
5348+
;;
5349+
*)
5350+
if [[ "$1" =~ ^[0-9]+$ ]]; then
5351+
local num="$1"
5352+
if [ "${2:-}" = "--tmate" ]; then
5353+
start_tmate_session "$num"
5354+
else
5355+
start_pair_session "$num"
5356+
fi
5357+
else
5358+
error "Unknown pair command: $1"
5359+
echo ""
5360+
echo "Usage:"
5361+
echo " crab pair <N> Start pairing session"
5362+
echo " crab pair <N> --tmate Start with public tmate URLs"
5363+
echo " crab join <N> Join a pairing session"
5364+
echo " crab spectate <N> Join as spectator (view only)"
5365+
echo " crab pair ls List active sessions"
5366+
echo " crab pair end <N> End a session"
5367+
exit 1
5368+
fi
5369+
;;
5370+
esac
5371+
}
5372+
51405373
# =============================================================================
51415374
# Help / Cheat Sheet
51425375
# =============================================================================
@@ -5284,6 +5517,17 @@ show_cheat() {
52845517
║ ║
52855518
╠═══════════════════════════════════════════════════════════════════════════════╣
52865519
║ ║
5520+
║ LIVE PAIRING (crab pair ...) ║
5521+
║ ──────────────────────────────────────────────────────────────────────── ║
5522+
║ crab pair <N> Start pairing session for workspace N ║
5523+
║ crab pair <N> --tmate Start with public tmate URLs ║
5524+
║ crab join <N> Join a pairing session (full access) ║
5525+
║ crab spectate <N> Join as spectator (view only) ║
5526+
║ crab pair ls List active pairing sessions ║
5527+
║ crab pair end <N> End a pairing session ║
5528+
║ ║
5529+
╠═══════════════════════════════════════════════════════════════════════════════╣
5530+
║ ║
52875531
║ TMUX KEYBINDINGS (Prefix = Ctrl+a) ║
52885532
║ ──────────────────────────────────────────────────────────────────────── ║
52895533
║ Option+1,2,3... Switch to workspace 1, 2, 3... ║
@@ -5647,6 +5891,23 @@ main() {
56475891
"rewind"|"timetravel"|"tt")
56485892
handle_rewind_command "${@:2}"
56495893
;;
5894+
"pair")
5895+
handle_pair_command "${@:2}"
5896+
;;
5897+
"join")
5898+
if [ -z "${2:-}" ]; then
5899+
error "Usage: crab join <workspace-number>"
5900+
exit 1
5901+
fi
5902+
join_pair_session "$2" "pair"
5903+
;;
5904+
"spectate"|"watch")
5905+
if [ -z "${2:-}" ]; then
5906+
error "Usage: crab spectate <workspace-number>"
5907+
exit 1
5908+
fi
5909+
join_pair_session "$2" "spectate"
5910+
;;
56505911
"snapshot")
56515912
load_config
56525913
validate_config

0 commit comments

Comments
 (0)