Problem
There's no cross-platform way to programmatically create and manage terminal workspaces in Ghostty. Users who want to script terminal layouts — opening windows, creating splits, running commands in each pane — have to resort to fragile workarounds like AppleScript keystroke simulation (see #12136) or external tools like tmux.
The existing +new-window action works on Linux via D-Bus but is not supported on macOS. Even on Linux, there's no CLI support for creating splits, targeting specific windows, or tearing down named panes.
Use Cases
AI agent integration: Tools like Claude Code need to open and manage terminal panes programmatically — e.g., opening an editor in one pane, a dev server in another, and a test runner in a third. Today this requires AppleScript hacks or running inside tmux. Native Ghostty commands would make this seamless.
Project startup scripts: Developers frequently set up the same terminal layout for each project. A simple shell script should be able to declare "I want a window with vim on the left, a server on the right, and tests on the bottom" without depending on tmux or a terminal multiplexer.
Idempotent workspace setup: Scripts should be re-runnable. Running the setup script a second time should focus the existing panes, not create duplicates. This enables workflows where the script can be bound to a hotkey or run from a launcher.
Window manager integration: Users with tiling window managers or automation tools should be able to open and close Ghostty panes from external scripts without accessibility permissions or keystroke simulation.
Proposal
Three new CLI capabilities, all cross-platform:
1. Named windows with --target
# Create a named window (or focus it if it already exists)
ghostty +new-window --target=myproject --working-directory=~/project --command=vim
2. +split action with named panes
# Add a named split to a specific window
ghostty +split --target=myproject --name=server --direction=right --command="npm run dev"
ghostty +split --target=myproject --name=tests --direction=down --command="npm test"
# If the pane already exists, focus it instead of creating a duplicate
ghostty +split --target=myproject --name=server --direction=right
3. +close action for teardown
# Close a specific pane
ghostty +close --target=server
# Close an entire window
ghostty +close --target=myproject
# Idempotent — closing something that's already gone is a no-op
ghostty +close --target=myproject
Full re-runnable workspace script
#!/bin/bash
ghostty +new-window --target=dev --working-directory=~/project --command=vim
ghostty +split --target=dev --name=server --direction=right --command="npm run dev"
ghostty +split --target=dev --name=tests --direction=down --command="npm test"
Run it once to set up. Run it again and it just focuses the existing panes. +close --target=dev tears everything down.
Design Considerations
- Cross-platform: The CLI actions and IPC types should be platform-agnostic. Only the transport layer differs (D-Bus on GTK/Linux, Unix domain sockets on macOS).
- Idempotent by default: Named targets (windows and panes) are registered in a lightweight registry with weak references. If a window or pane is closed manually, its entry is automatically cleaned up.
- Split creation bypasses
ghostty_surface_split(): The C export only accepts a direction — it can't pass a custom command or working directory. Instead, the handler posts Notification.ghosttyNewSplit (macOS) directly with a SurfaceConfiguration, which is the same mechanism the app uses internally.
- Shared namespace: Window names and pane names share one flat namespace. A name refers to exactly one thing.
- Protocol: Length-prefixed JSON over Unix domain sockets (macOS) or D-Bus actions (Linux). Simple, debuggable, no new dependencies.
Proof of Concept
I have a working implementation on macOS covering all three actions with idempotent behavior. Branch: dzearing/ghostty:macos-ipc
Files changed:
src/apprt/ipc.zig — NewWindow, Split, Close action types
src/cli/split.zig, src/cli/close.zig — new CLI actions
src/apprt/embedded.zig — Unix socket IPC client
macos/Sources/Features/IPC/IPCServer.swift — socket server, target registry, action handlers
macos/Sources/Features/IPC/IPCMessage.swift — protocol types
The GTK/Linux side would need equivalent split and close handlers via D-Bus, following the existing new-window D-Bus pattern in src/apprt/gtk/ipc/.
What I'm Looking For
Feedback on whether this direction aligns with Ghostty's vision before submitting a PR. Specifically:
- Are
+split and +close as top-level CLI actions the right abstraction?
- Is the
--target/--name naming approach reasonable, or would you prefer a different targeting mechanism?
- Any concerns about the Unix socket transport on macOS vs. extending D-Bus on Linux?
- Should the idempotent focus-if-exists behavior be opt-in (e.g.,
--reuse) rather than default?
Problem
There's no cross-platform way to programmatically create and manage terminal workspaces in Ghostty. Users who want to script terminal layouts — opening windows, creating splits, running commands in each pane — have to resort to fragile workarounds like AppleScript keystroke simulation (see #12136) or external tools like tmux.
The existing
+new-windowaction works on Linux via D-Bus but is not supported on macOS. Even on Linux, there's no CLI support for creating splits, targeting specific windows, or tearing down named panes.Use Cases
AI agent integration: Tools like Claude Code need to open and manage terminal panes programmatically — e.g., opening an editor in one pane, a dev server in another, and a test runner in a third. Today this requires AppleScript hacks or running inside tmux. Native Ghostty commands would make this seamless.
Project startup scripts: Developers frequently set up the same terminal layout for each project. A simple shell script should be able to declare "I want a window with vim on the left, a server on the right, and tests on the bottom" without depending on tmux or a terminal multiplexer.
Idempotent workspace setup: Scripts should be re-runnable. Running the setup script a second time should focus the existing panes, not create duplicates. This enables workflows where the script can be bound to a hotkey or run from a launcher.
Window manager integration: Users with tiling window managers or automation tools should be able to open and close Ghostty panes from external scripts without accessibility permissions or keystroke simulation.
Proposal
Three new CLI capabilities, all cross-platform:
1. Named windows with
--target2.
+splitaction with named panes3.
+closeaction for teardownFull re-runnable workspace script
Run it once to set up. Run it again and it just focuses the existing panes.
+close --target=devtears everything down.Design Considerations
ghostty_surface_split(): The C export only accepts a direction — it can't pass a custom command or working directory. Instead, the handler postsNotification.ghosttyNewSplit(macOS) directly with aSurfaceConfiguration, which is the same mechanism the app uses internally.Proof of Concept
I have a working implementation on macOS covering all three actions with idempotent behavior. Branch:
dzearing/ghostty:macos-ipcFiles changed:
src/apprt/ipc.zig—NewWindow,Split,Closeaction typessrc/cli/split.zig,src/cli/close.zig— new CLI actionssrc/apprt/embedded.zig— Unix socket IPC clientmacos/Sources/Features/IPC/IPCServer.swift— socket server, target registry, action handlersmacos/Sources/Features/IPC/IPCMessage.swift— protocol typesThe GTK/Linux side would need equivalent
splitandclosehandlers via D-Bus, following the existingnew-windowD-Bus pattern insrc/apprt/gtk/ipc/.What I'm Looking For
Feedback on whether this direction aligns with Ghostty's vision before submitting a PR. Specifically:
+splitand+closeas top-level CLI actions the right abstraction?--target/--namenaming approach reasonable, or would you prefer a different targeting mechanism?--reuse) rather than default?