* Handle spawn operation using split-pane view (default). * When inside tmux: Creates teammates in a shared window with leader on left, teammates on right. * When outside tmux: Creates a claude-swarm session with all teammates in a tiled layout.
( input: SpawnInput, context: ToolUseContext, )
| 303 | * When outside tmux: Creates a claude-swarm session with all teammates in a tiled layout. |
| 304 | */ |
| 305 | async function handleSpawnSplitPane( |
| 306 | input: SpawnInput, |
| 307 | context: ToolUseContext, |
| 308 | ): Promise<{ data: SpawnOutput }> { |
| 309 | const { setAppState, getAppState } = context |
| 310 | const { name, prompt, agent_type, cwd, plan_mode_required } = input |
| 311 | |
| 312 | // Resolve model: 'inherit' → leader's model; undefined → default Opus |
| 313 | const model = resolveTeammateModel(input.model, getAppState().mainLoopModel) |
| 314 | |
| 315 | if (!name || !prompt) { |
| 316 | throw new Error('name and prompt are required for spawn operation') |
| 317 | } |
| 318 | |
| 319 | // Get team name from input or inherit from leader's team context |
| 320 | const appState = getAppState() |
| 321 | const teamName = input.team_name || appState.teamContext?.teamName |
| 322 | |
| 323 | if (!teamName) { |
| 324 | throw new Error( |
| 325 | 'team_name is required for spawn operation. Either provide team_name in input or call spawnTeam first to establish team context.', |
| 326 | ) |
| 327 | } |
| 328 | |
| 329 | // Generate unique name if duplicate exists in team |
| 330 | const uniqueName = await generateUniqueTeammateName(name, teamName) |
| 331 | |
| 332 | // Sanitize the name to prevent @ in agent IDs (would break agentName@teamName format) |
| 333 | const sanitizedName = sanitizeAgentName(uniqueName) |
| 334 | |
| 335 | // Generate deterministic agent ID from name and team |
| 336 | const teammateId = formatAgentId(sanitizedName, teamName) |
| 337 | const workingDir = cwd || getCwd() |
| 338 | |
| 339 | // Detect the appropriate backend and check if setup is needed |
| 340 | let detectionResult = await detectAndGetBackend() |
| 341 | |
| 342 | // If in iTerm2 but it2 isn't set up, prompt the user |
| 343 | if (detectionResult.needsIt2Setup && context.setToolJSX) { |
| 344 | const tmuxAvailable = await isTmuxAvailable() |
| 345 | |
| 346 | // Show the setup prompt and wait for user decision |
| 347 | const setupResult = await new Promise< |
| 348 | 'installed' | 'use-tmux' | 'cancelled' |
| 349 | >(resolve => { |
| 350 | context.setToolJSX!({ |
| 351 | jsx: React.createElement(It2SetupPrompt, { |
| 352 | onDone: resolve, |
| 353 | tmuxAvailable, |
| 354 | }), |
| 355 | shouldHidePromptInput: true, |
| 356 | }) |
| 357 | }) |
| 358 | |
| 359 | // Clear the JSX |
| 360 | context.setToolJSX(null) |
| 361 | |
| 362 | if (setupResult === 'cancelled') { |
no test coverage detected