|
27 | 27 |
|
28 | 28 | set -e |
29 | 29 |
|
30 | | -VERSION="0.3.0" |
| 30 | +VERSION="0.4.0" |
31 | 31 | CONFIG_DIR="$HOME/.crabcode" |
32 | 32 | CONFIG_FILE="$CONFIG_DIR/config.yaml" |
33 | 33 | WIP_BASE="$CONFIG_DIR/wip" |
@@ -5137,6 +5137,239 @@ handle_rewind_command() { |
5137 | 5137 | esac |
5138 | 5138 | } |
5139 | 5139 |
|
| 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 | + |
5140 | 5373 | # ============================================================================= |
5141 | 5374 | # Help / Cheat Sheet |
5142 | 5375 | # ============================================================================= |
@@ -5284,6 +5517,17 @@ show_cheat() { |
5284 | 5517 | ║ ║ |
5285 | 5518 | ╠═══════════════════════════════════════════════════════════════════════════════╣ |
5286 | 5519 | ║ ║ |
| 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 | +║ ║ |
5287 | 5531 | ║ TMUX KEYBINDINGS (Prefix = Ctrl+a) ║ |
5288 | 5532 | ║ ──────────────────────────────────────────────────────────────────────── ║ |
5289 | 5533 | ║ Option+1,2,3... Switch to workspace 1, 2, 3... ║ |
@@ -5647,6 +5891,23 @@ main() { |
5647 | 5891 | "rewind"|"timetravel"|"tt") |
5648 | 5892 | handle_rewind_command "${@:2}" |
5649 | 5893 | ;; |
| 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 | + ;; |
5650 | 5911 | "snapshot") |
5651 | 5912 | load_config |
5652 | 5913 | validate_config |
|
0 commit comments