Description
Implement the cross-surface sibling-tab action — lets a user open a running Claude session in the opposite surface while preserving conversation context via claude --resume <session_id>.
Spec: Epic #250 §5 (cross-surface resume semantics).
Scope
Action surface (MainFeature + TabFeature)
- Menu item on active session:
Open as Terminal (⌘⇧T) or Open as Dialogue (⌘⇧D) — visible based on current surface.
- Action dispatches
AgentOrchestratorFeature.openAsSibling(currentTabID:targetSurface:).
Orchestrator logic
```swift
func openAsSibling(currentTabID: UUID, targetSurface: SessionSurface) {
// 1. Look up active session_id from current tab's AgentSessionFeature / AgentChatFeature state.
// 2. Create a new Tab with targetSurface, same worktree, same workingDirectory.
// 3. In the new tab's AgentSessionFeature.startAgent flow — pass sessionIDForResume.
// 4. ClaudeCommandBuilder emits --resume <UUID> + surface-specific flags.
// 5. Old tab remains — user can close it or keep both open in parallel.
}
```
Extraction of session_id
- For
.agentDialogue source: from AgentChatFeature.State.sessionInit.sessionID.
- For
.agentTerminal source: terminal doesn't structurally know session_id — read it from claude output by parsing a system/init line OR from the Claude Code session dir (~/.claude/projects/<hash>/). Prefer reading a sidecar file written by Claude (if available), else parse the first JSONL line after a one-shot claude status --output-format json probe.
- If session_id cannot be determined (very old Claude CLI, session too short) — fall back: open new session without
--resume; warn user in UI.
UX
- Spinner / toast during the 1-2 seconds of sibling creation.
- On success: focus switches to new tab.
- On failure: toast "Couldn't open as {surface}. Starting new session instead." + new tab with fresh session.
Acceptance Criteria
Relationships
Description
Implement the cross-surface sibling-tab action — lets a user open a running Claude session in the opposite surface while preserving conversation context via
claude --resume <session_id>.Spec: Epic #250 §5 (cross-surface resume semantics).
Scope
Action surface (MainFeature + TabFeature)
Open as Terminal(⌘⇧T) orOpen as Dialogue(⌘⇧D) — visible based on current surface.AgentOrchestratorFeature.openAsSibling(currentTabID:targetSurface:).Orchestrator logic
```swift
func openAsSibling(currentTabID: UUID, targetSurface: SessionSurface) {
// 1. Look up active session_id from current tab's AgentSessionFeature / AgentChatFeature state.
// 2. Create a new Tab with targetSurface, same worktree, same workingDirectory.
// 3. In the new tab's AgentSessionFeature.startAgent flow — pass sessionIDForResume.
// 4. ClaudeCommandBuilder emits
--resume <UUID>+ surface-specific flags.// 5. Old tab remains — user can close it or keep both open in parallel.
}
```
Extraction of session_id
.agentDialoguesource: fromAgentChatFeature.State.sessionInit.sessionID..agentTerminalsource: terminal doesn't structurally know session_id — read it fromclaudeoutput by parsing a system/init line OR from the Claude Code session dir (~/.claude/projects/<hash>/). Prefer reading a sidecar file written by Claude (if available), else parse the first JSONL line after a one-shotclaude status --output-format jsonprobe.--resume; warn user in UI.UX
Acceptance Criteria
--resume <session_id>, shows prior transcript on first interaction (verified manually with live CLI or scripted).--resume <id>+ correct surface flags.Relationships