Add Ghostty split-pane backend via AppleScript (macOS)
Feature Request
Add a GhosttyBackend for agent team split panes on macOS, using Ghostty's AppleScript API.
Context
The ITermBackend was recently fixed (v2.1.77) and works great for iTerm2 users. Ghostty is a popular modern terminal on macOS that supports a rich AppleScript object model for programmatic split pane control — everything needed to implement a backend equivalent to ITermBackend.
Ghostty's AppleScript API
Ghostty exposes the following relevant AppleScript capabilities:
tell application "Ghostty"
-- Create a configuration with working directory and command
set cfg to new surface configuration
set initial working directory of cfg to "/path/to/dir"
set command of cfg to "/bin/zsh"
-- Split a pane (directions: right, left, down, up)
set pane2 to split pane1 direction right with configuration cfg
-- Send text/keys to a specific pane
input text "echo hello" to pane2
send key "enter" to pane2
-- Query pane properties
get id of pane2
get working directory of pane2
-- Close a pane
close pane2
-- Other actions
perform action "equalize_splits" on pane1
end tell
This maps directly to what ITermBackend does with the it2 CLI:
| ITermBackend (it2 CLI) | GhosttyBackend (AppleScript) |
|---|---|
| it2 session split -v | split terminal direction right with configuration cfg |
| it2 session split -s <id> | split terminal direction down with configuration cfg |
| Send command via CLI | input text "cmd" to terminal + send key "enter" |
| Parse session ID from output | get id of terminal |
| Kill pane | close terminal |
Implementation Notes
- AppleScript calls can be made via
osascript -e '...'(similar to howITermBackendshells out toit2) - Ghostty detection: check
TERM_PROGRAM=ghosttyenv var - AppleScript is enabled by default in Ghostty (
macos-applescript = true) - macOS TCC automation permissions may prompt the user on first use
teammateMode: "ghostty"and"auto"mode detection would both be needed (like iTerm2)
Environment
- Platform: macOS
- Terminal: Ghostty
- Claude Code: v2.1.77
12 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Related but not a duplicate. #24189 was filed when Ghostty had no programmatic API — it was a forward-looking request noting the blocker.
Since then, Ghostty v1.3.0 shipped full AppleScript support (see release notes)). This issue provides the concrete AppleScript API mapping needed for implementation — the
split,input text,send key,close, andidcommands that map 1:1 to the existingITermBackend.Happy to close this and add the implementation details as a comment on #24189 instead if that's preferred.
we use AppleScript-driven terminal splits for parallel agent coordination on macOS and the approach works well. currently running it with tmux but Ghostty's object model is cleaner for this.
one thing worth considering in the implementation - you need a way to address specific panes after creation. with iTerm2 you get session IDs back from the split command, but with Ghostty you'd probably need to enumerate windows/tabs/splits after the fact using 'every split' on the tab object. the timing matters because if two agents try to split simultaneously you can get a race.
also worth noting that Ghostty's 'write' command on splits is the key primitive here - you can pipe commands directly to a specific split without needing to focus it first. that makes it much nicer than iTerm for headless orchestration.
our tmux-based parallel agent orchestrator that handles the split coordination: https://github.com/m13v/tmux-background-agents/blob/main/SKILL.md
and the browser lock mechanism for preventing race conditions between agents sharing resources: https://github.com/m13v/browser-lock/blob/main/playwright-lock.sh
@m13v Good points, thanks for sharing.
Re: race conditions on simultaneous splits — the existing
ITermBackendalready handles this with a serialization queue (each split awaits the previous one completing before starting the next). AGhosttyBackendwould need the same pattern. The nice thing is thatsplitin Ghostty's AppleScript returns the new terminal object directly, so you get the ID back inline without needing to enumerate after the fact:That's actually cleaner than iTerm2's
it2 session split, which requires parsing the session ID from stdout.Re:
write/input textwithout needing to focus — agreed, that's a big win for headless orchestration. TheITermBackendhas to target panes by session ID through theit2CLI, which is a similar "no focus needed" approach, but Ghostty's AppleScript object references are more natural.Your tmux orchestrator looks interesting — the serialized split creation pattern there is essentially what would be needed here too.
good point about the ITermBackend already handling concurrent splits. if the AppleScript bridge is reliable enough for sequential operations, the race condition concern is probably overstated. the focus-follows-pane behavior would be the main thing to test thoroughly since Ghostty might handle focus differently than iTerm.
This exists now: gx-ghostty implements the AppleScript mapping described in this issue, plus Accessibility API for reads (scrollback, pane discovery).
The
it2shim bridges Claude Code'sITermBackendto Ghostty —split,input text,send key,close,get idall via AS with UUID targeting.TeamCreatespawns visible Ghostty split panes.v1.3.0
This is great, exactly the kind of thing the ecosystem needs. The UUID targeting for pane addressing is smart - that was the main pain point we hit with naive split management.
How does gx-ghostty handle the case where Ghostty recycles pane IDs after close? That was the race condition we kept hitting with AppleScript-based approaches. The Accessibility API reads should help since you can verify the pane state before sending input.
Thanks for the thoughtful question @m13v.
Ghostty terminal UUIDs are unique for the terminal's lifetime and aren't recycled after close — a closed UUID just stops resolving. This is why gx targets by UUID rather than index (indices reshuffle on title changes and window reordering).
Each shell captures its UUID at startup via a one-liner
.zshrchook, so the UUID is pinned to the pane from birth. Theit2shim routes Claude Code teammate ops through that UUID.Just updated the README — install is four steps at the top: binary + accessibility permission for gx, it2 shim + shell hook for Claude Code teammates. Steps 2 and 3 are optional if you just want gx standalone.
That makes sense - UUID-based addressing avoids the index reshuffling problem entirely. The .zshrc hook pinning the UUID at shell startup is clean. Good call on updating the README too, the lifecycle details help a lot for anyone building orchestration on top of this.
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.