Summary
Investigate replacing the current architecture — one embedded Ghostty window per session — with a single embedded Ghostty instance backed by tmux, where each Crow session/tab maps to a tmux session (or window) that we switch into when the user selects the tab.
This is a research/design spike. Output is a design doc + go/no-go recommendation, not production code.
Motivation
Current pain points
- Resource overhead — every session spawns its own embedded Ghostty/PTY. With 5–10 sessions open, that's 5–10 terminal processes plus duplicated rendering and memory overhead.
- Tab-switch cost — switching tabs swaps which embedded terminal view is visible; under load this is noticeably heavier than it needs to be.
- No standardized "prompt ready" signal — today we rely on
sleep-and-hope heuristics in launcher code to know when the embedded shell is ready to receive send-keys. This is fragile and a recurring source of bugs.
- No session persistence — if Crow restarts, embedded terminal state is lost. Users have to rebuild context.
Proposed model
- One embedded Ghostty instance (via libghostty) for the whole app.
- A tmux server runs in the background. Each Crow session is either:
- Option A: a tmux window inside one shared tmux session (e.g.
crow-cockpit), or
- Option B: a separate tmux session per Crow session, switched via
switch-client.
- Clicking a tab in Crow's UI fires
tmux select-window (or switch-client) — the embedded Ghostty instantly reflects the new context. No new process, no view swap.
- A bundled shell wrapper sources the user's normal shell config and emits a
CLAUDE-READY marker (OSC 9 or OSC 133) so we can detect prompt-ready without sleeps.
Wins if this works
- 1 Ghostty process instead of N
- Faster, lighter tab switching (tmux window switch is ~ms)
- Reliable prompt-ready detection via
tmux capture-pane polling or tmux -CC control mode events
- Session persistence across Crow restarts — reattach the same tmux session and pick up where the user left off
- Cleaner API surface for
send-keys and capture-pane than driving N independent PTYs
Investigation scope
A. Architecture options
B. Prompt-readiness signal
C. Distribution / first-run UX
D. Risks / unknowns
Deliverables
- Design doc in
docs/ (proposed: docs/tmux-backend-spec.md) covering:
- Recommended option (A vs B) with rationale
- Architecture diagram: Crow UI ↔ libghostty ↔ tmux ↔ shell wrapper ↔ Claude/Codex
- Prompt-readiness mechanism (chosen approach + fallback)
- Distribution plan for tmux + shell wrapper
- Migration plan for existing users
- Working prototype (throwaway branch is fine) demonstrating:
- One embedded Ghostty, two Crow tabs, tab switching via
tmux select-window or switch-client
waitForPrompt(session: ..., timeout: ...) Swift helper using OSC marker detection
- Recommendation: ship / ship-with-conditions / don't ship, with concrete next-phase tickets if green-lit.
Non-goals
Source
Captured from a brainstorming conversation exploring whether a tmux-backed cockpit pattern (used by several Claude-heavy power-user tools) would be a better fit for Crow than the current per-session Ghostty embedding.
Summary
Investigate replacing the current architecture — one embedded Ghostty window per session — with a single embedded Ghostty instance backed by tmux, where each Crow session/tab maps to a tmux session (or window) that we switch into when the user selects the tab.
This is a research/design spike. Output is a design doc + go/no-go recommendation, not production code.
Motivation
Current pain points
sleep-and-hope heuristics in launcher code to know when the embedded shell is ready to receivesend-keys. This is fragile and a recurring source of bugs.Proposed model
crow-cockpit), orswitch-client.tmux select-window(orswitch-client) — the embedded Ghostty instantly reflects the new context. No new process, no view swap.CLAUDE-READYmarker (OSC 9 or OSC 133) so we can detect prompt-ready without sleeps.Wins if this works
tmux capture-panepolling ortmux -CCcontrol mode eventssend-keysandcapture-panethan driving N independent PTYsInvestigation scope
A. Architecture options
Packages/CrowGhostty/ embedded Ghostty integration? Does libghostty support a single long-lived embedded view that we re-target, or do we need to manage the surface differently?ClaudeLauncher(Packages/CrowClaude/) fit? Today it spawns Claude in a fresh embedded terminal. New flow: launch Claude inside a tmux window we created.ClaudeCodeAgentand any futureCodexAgentwould still launch via tmux. Does the abstraction need aTerminalBackendconcept too?B. Prompt-readiness signal
.zshenv→.zprofile→.zshrc, or bash equivalents) so aliases / oh-my-zsh / asdf / nvm / conda all keep working\033]9;CROW-READY\007) and/or OSC 133 (\033]133;A...) marker on everyprecmd/PROMPT_COMMANDprecmd/PROMPT_COMMANDexec "$SHELL" -itmux capture-pane -p -t <target>every 20–50 ms, scan for marker. Simple, no parsing infra.tmux -CC, parse the%outputevent stream live. Zero polling, harder to implement, much closer to "real" event-driven.C. Distribution / first-run UX
which tmux); options if missing:brew install tmux(most users already have brew).app(heavier, fully self-contained)AppState/ persistence layer so tabs survive restart and can rebind to existing tmux sessions.D. Risks / unknowns
tmux attachclient.Deliverables
docs/(proposed:docs/tmux-backend-spec.md) covering:tmux select-windoworswitch-clientwaitForPrompt(session: ..., timeout: ...)Swift helper using OSC marker detectionNon-goals
Source
Captured from a brainstorming conversation exploring whether a tmux-backed cockpit pattern (used by several Claude-heavy power-user tools) would be a better fit for Crow than the current per-session Ghostty embedding.