Ghostty workspace preset manager — save and restore multi-tab, multi-pane terminal layouts with a single command.
Written in OCaml. Uses al for alias integration.
- All layouts live in
~/.config/pre/layouts/ pre apply <name>orchestrates Ghostty via AppleScript to create the full layout- Each layout is auto-registered as a shell alias via
al, so you can just typework
- macOS (uses AppleScript to control Ghostty)
- Ghostty terminal
- OCaml 5.x with opam, dune, otoml (
opam install otoml) - al for alias management
./install.shpre save work # Capture current Ghostty window as a layout
pre apply work # Apply a saved layout
pre list # List all layouts
pre show work # Show layout details
pre preview work # Preview generated AppleScript
pre init myproject # Create a new layout from template
pre edit myproject # Open layout in $EDITOR
pre which work # Show which file a layout resolves to
pre delete myproject # Delete a layout
pre register work # Register as al alias
pre register-all # Register all layouts as al aliases
pre unregister work # Remove al aliasOr just type the layout name directly (thanks to al aliases):
work # applies the "work" layoutSet up your Ghostty window exactly how you want it — tabs, splits, directories — then save it:
pre save myworkspaceThis inspects the running Ghostty window and captures:
- Working directories from the process tree (via
lsof) - Tab count via macOS Accessibility / AppleScript
- Split structure (horizontal/vertical) via the accessibility tree
The layout is saved to ~/.config/pre/layouts/.
If the accessibility inspection can't determine the split structure (e.g. permissions not granted), it falls back to a sensible default (all right-splits). You can then fine-tune with pre edit myworkspace.
Tip: Make sure Ghostty has accessibility permissions in System Settings → Privacy & Security → Accessibility.
All layouts live in ~/.config/pre/layouts/. The repo layouts/ directory contains starter layouts that get copied on install (existing files are not overwritten).
# ~/.config/pre/layouts/work.toml
[layout]
name = "work"
# First tab
[[tab]]
name = "backend"
[[tab.pane]]
path = "/path/to/backend"
command = "" # optional command to run
split = "first" # first pane (no split)
[[tab.pane]]
path = "/path/to/backend"
command = "make dev"
split = "right" # split right from previous
# Second tab
[[tab]]
name = "frontend"
[[tab.pane]]
path = "/path/to/frontend"
split = "first"
[[tab.pane]]
path = "/path/to/frontend"
command = "npm start"
split = "down" # split below previous| Value | Description |
|---|---|
first |
First pane in the tab (no split) |
right |
Split right (Cmd+D) |
down |
Split down (Cmd+Shift+D) |
MIT