teammateLayoutManager.ts
1 import type { AgentColorName } from '../../tools/AgentTool/agentColorManager.js' 2 import { AGENT_COLORS } from '../../tools/AgentTool/agentColorManager.js' 3 import { detectAndGetBackend } from './backends/registry.js' 4 import type { PaneBackend } from './backends/types.js' 5 6 // Track color assignments for teammates (persisted per session) 7 const teammateColorAssignments = new Map<string, AgentColorName>() 8 let colorIndex = 0 9 10 /** 11 * Gets the appropriate backend for the current environment. 12 * detectAndGetBackend() caches internally — no need for a second cache here. 13 */ 14 async function getBackend(): Promise<PaneBackend> { 15 return (await detectAndGetBackend()).backend 16 } 17 18 /** 19 * Assigns a unique color to a teammate from the available palette. 20 * Colors are assigned in round-robin order. 21 */ 22 export function assignTeammateColor(teammateId: string): AgentColorName { 23 const existing = teammateColorAssignments.get(teammateId) 24 if (existing) { 25 return existing 26 } 27 28 const color = AGENT_COLORS[colorIndex % AGENT_COLORS.length]! 29 teammateColorAssignments.set(teammateId, color) 30 colorIndex++ 31 32 return color 33 } 34 35 /** 36 * Gets the assigned color for a teammate, if any. 37 */ 38 export function getTeammateColor( 39 teammateId: string, 40 ): AgentColorName | undefined { 41 return teammateColorAssignments.get(teammateId) 42 } 43 44 /** 45 * Clears all teammate color assignments. 46 * Called during team cleanup to reset state for potential new teams. 47 */ 48 export function clearTeammateColors(): void { 49 teammateColorAssignments.clear() 50 colorIndex = 0 51 } 52 53 /** 54 * Checks if we're currently running inside a tmux session. 55 * Uses the detection module directly for this check. 56 */ 57 export async function isInsideTmux(): Promise<boolean> { 58 const { isInsideTmux: checkTmux } = await import('./backends/detection.js') 59 return checkTmux() 60 } 61 62 /** 63 * Creates a new teammate pane in the swarm view. 64 * Automatically selects the appropriate backend (tmux or iTerm2) based on environment. 65 * 66 * When running INSIDE tmux: 67 * - Uses TmuxBackend to split the current window 68 * - Leader stays on left (30%), teammates on right (70%) 69 * 70 * When running in iTerm2 (not in tmux) with it2 CLI: 71 * - Uses ITermBackend for native iTerm2 split panes 72 * 73 * When running OUTSIDE tmux/iTerm2: 74 * - Falls back to TmuxBackend with external claude-swarm session 75 */ 76 export async function createTeammatePaneInSwarmView( 77 teammateName: string, 78 teammateColor: AgentColorName, 79 ): Promise<{ paneId: string; isFirstTeammate: boolean }> { 80 const backend = await getBackend() 81 return backend.createTeammatePaneInSwarmView(teammateName, teammateColor) 82 } 83 84 /** 85 * Enables pane border status for a window (shows pane titles). 86 * Delegates to the detected backend. 87 */ 88 export async function enablePaneBorderStatus( 89 windowTarget?: string, 90 useSwarmSocket = false, 91 ): Promise<void> { 92 const backend = await getBackend() 93 return backend.enablePaneBorderStatus(windowTarget, useSwarmSocket) 94 } 95 96 /** 97 * Sends a command to a specific pane. 98 * Delegates to the detected backend. 99 */ 100 export async function sendCommandToPane( 101 paneId: string, 102 command: string, 103 useSwarmSocket = false, 104 ): Promise<void> { 105 const backend = await getBackend() 106 return backend.sendCommandToPane(paneId, command, useSwarmSocket) 107 }