Every time I start a new project I do the same thing: open three splits, cd each into the right directory, start the dev server, open a test watcher, leave a scratch pane. I automated it. Here’s how.
The ritual nobody talks about
In my Ghostty panes vs tmux post, I wrote that scripted layouts were “a tmux thing” - you couldn’t script Ghostty splits. That was true until March 2026. Ghostty 1.3 shipped AppleScript support and now it’s not.
The target layout:
┌─────────────────────┬────────────────────┐
│ │ │
│ bun dev │ bun test --watch │
│ (dev server) │ (test watcher) │
│ │ │
│ ├────────────────────┤
│ │ │
│ │ ~/Projects/myapp │
│ │ (scratch pane) │
│ │ │
└─────────────────────┴────────────────────┘
One command. That’s what we’re building.
How Ghostty’s AppleScript works
Ghostty 1.3 added a scripting dictionary, so you can drive it from AppleScript like any macOS app. The practical approach for splits: control Ghostty via System Events, sending keystrokes that match Ghostty’s own shortcuts.
Minimal proof of concept:
tell application "Ghostty"
activate
end tell
delay 0.5
tell application "System Events"
tell process "Ghostty"
keystroke "cd ~/Projects/myapp && bun dev"
keystroke return
keystroke "d" using command down -- Cmd+D: new vertical split
keystroke "cd ~/Projects/myapp"
keystroke return
end tell
end tell
Run it with osascript yourscript.applescript. The delay 0.5 gives Ghostty time to focus before System Events starts typing. Skip it and you’ll lose the first few characters.
The full layout script
Save this as ~/scripts/project-layout.applescript:
-- Set your project path
set projectPath to "~/Projects/myapp"
-- Focus Ghostty
tell application "Ghostty"
activate
end tell
delay 0.5
tell application "System Events"
tell process "Ghostty"
-- Pane 1: dev server (active pane on open)
keystroke "cd " & projectPath
keystroke return
delay 0.2
keystroke "bun dev"
keystroke return
-- Create right split (Ghostty default: Cmd+D)
keystroke "d" using command down
delay 0.3
-- Pane 2: test watcher
keystroke "cd " & projectPath
keystroke return
delay 0.2
keystroke "bun test --watch"
keystroke return
-- Create bottom split below pane 2 (Cmd+Shift+D)
keystroke "D" using {command down, shift down}
delay 0.3
-- Pane 3: scratch shell, just cd in
keystroke "cd " & projectPath
keystroke return
-- Navigate back to pane 1 (Cmd+Option+Left)
key code 123 using {command down, option down}
end tell
end tell
The delay calls are load-bearing. Ghostty needs a beat between creating a split and accepting input in it. If commands keep landing in the wrong pane, bump the delays up slightly.
How to trigger it
Shell alias - the obvious approach:
# ~/.zshrc
alias devstart='osascript ~/scripts/project-layout.applescript'
Type devstart from anywhere and your layout builds itself.
Raycast script command - if you want a hotkey:
#!/bin/bash
# @raycast.schemaVersion 1
# @raycast.title Dev Layout
# @raycast.mode silent
osascript ~/scripts/project-layout.applescript
Save as dev-layout.sh, chmod +x it, add to Raycast, assign a shortcut. Done.
Per-project layouts
The script above hardcodes one project. Here’s a version that reads a .ghostty-layout file from whichever directory you’re in.
.ghostty-layout in your project root:
pane1=bun dev
pane2=bun test --watch
pane3=
Loader script (~/scripts/layout-loader.applescript):
set layoutFile to (do shell script "pwd") & "/.ghostty-layout"
set pane1cmd to do shell script "grep '^pane1=' " & quoted form of layoutFile & " | cut -d= -f2"
set pane2cmd to do shell script "grep '^pane2=' " & quoted form of layoutFile & " | cut -d= -f2"
tell application "Ghostty"
activate
end tell
delay 0.5
tell application "System Events"
tell process "Ghostty"
if pane1cmd is not "" then
keystroke pane1cmd
keystroke return
end if
keystroke "d" using command down
delay 0.3
if pane2cmd is not "" then
keystroke pane2cmd
keystroke return
end if
end tell
end tell
Run it with osascript ~/scripts/layout-loader.applescript from inside the project. Each project owns its own layout, no script edits needed.
A few real limitations
macOS only. System Events is Apple-specific. If you’re on Linux, tmux scripting is still the move - I wrote about that in my tmux productivity setup.
Ghostty should already be running. The script activates it if it isn’t, but the fresh window might not be where you want it. Easiest fix: set Ghostty to open at login.
Split ordering matters more than you’d think. Complex layouts with 4+ panes need careful sequencing and delays. Start with a simple 2-pane script, verify it works, then add panes one at a time.
You spend 2 minutes on setup every morning. Automate it once.