Skip to content

Instantly share code, notes, and snippets.

@eherrerosj
Last active March 31, 2026 11:13
Show Gist options
  • Select an option

  • Save eherrerosj/cf10dd15d12f55874be53264aa885ce8 to your computer and use it in GitHub Desktop.

Select an option

Save eherrerosj/cf10dd15d12f55874be53264aa885ce8 to your computer and use it in GitHub Desktop.
Ghostty terminal config + Claude Code worktree hook (2x2 layout: Claude Code, lazygit, terminal, yazi)
# ============================================
# Ghostty Terminal - Complete Configuration
# ============================================
# File: ~/.config/ghostty/config
# Reload: Cmd+Shift+, (macOS)
# View options: ghostty +show-config --default --docs
# --- Typography ---
font-family = JetBrainsMonoNerdFont
font-size = 12
font-thicken = true
adjust-cell-height = 2
# --- Theme and Colors ---
# Catppuccin with automatic light/dark switching
theme = Catppuccin Mocha
# --- Window and Appearance ---
background-opacity = 0.9
background-blur-radius = 20
macos-titlebar-style = transparent
window-padding-x = 10
window-padding-y = 8
window-save-state = always
window-theme = auto
# --- Cursor ---
cursor-style = bar
cursor-style-blink = true
cursor-opacity = 0.8
# --- Mouse ---
mouse-hide-while-typing = true
copy-on-select = clipboard
# --- Quick Terminal (Quake-style dropdown) ---
# Activate with the global keybind configured below
quick-terminal-position = top
quick-terminal-screen = mouse
quick-terminal-autohide = true
quick-terminal-animation-duration = 0.15
# --- Security ---
clipboard-paste-protection = true
clipboard-paste-bracketed-safe = true
# --- Shell Integration ---
shell-integration = detect
# --- Keybindings ---
# Tabs
keybind = cmd+t=new_tab
keybind = cmd+shift+left=previous_tab
keybind = cmd+shift+right=next_tab
keybind = cmd+w=close_surface
# Splits
keybind = cmd+d=new_split:right
keybind = cmd+shift+d=new_split:down
keybind = cmd+alt+left=goto_split:left
keybind = cmd+alt+right=goto_split:right
keybind = cmd+alt+up=goto_split:top
keybind = cmd+alt+down=goto_split:bottom
# Font size
keybind = cmd+plus=increase_font_size:1
keybind = cmd+minus=decrease_font_size:1
keybind = cmd+zero=reset_font_size
# Quick terminal global hotkey
keybind = global:ctrl+grave_accent=toggle_quick_terminal
# Splits management
keybind = cmd+shift+e=equalize_splits
keybind = cmd+shift+f=toggle_split_zoom
# Reload config
keybind = cmd+shift+comma=reload_config
# --- Performance ---
# Generous scrollback (25MB)
scrollback-limit = 25000000
#!/bin/bash
# Hook: Worktree Ghostty Layout
#
# Creates git worktrees in a sibling directory and opens a Ghostty terminal
# in a 2x2 layout: Claude Code (TL), lazygit (TR), terminal (BL), yazi (BR).
# Prompts for a custom branch name via macOS dialog; leave empty for auto-generated.
#
# Events:
# - WorktreeCreate: Creates worktree + opens Ghostty 2x2 layout
# - WorktreeRemove: Removes worktree, branch, and empty directories
#
# Requirements:
# - jq (JSON parsing)
# - Ghostty terminal (macOS)
# - lazygit (git TUI)
# - yazi (file manager TUI)
#
# Ghostty keybindings required:
# super+d = new_split:right
# super+shift+d = new_split:down
# super+alt+left = goto_split:left
INPUT=$(cat)
if ! command -v jq &>/dev/null; then
echo "jq is required but not installed" >&2
exit 1
fi
HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name')
CWD=$(echo "$INPUT" | jq -r '.cwd')
#-----------------------------------------------------------------------
# WorktreeCreate: Create worktree in sibling dir + open Ghostty layout
#-----------------------------------------------------------------------
create_worktree() {
local NAME
NAME=$(echo "$INPUT" | jq -r '.name')
# Prompt for branch name via macOS dialog (can't use /dev/tty - Claude Code owns the terminal)
local CUSTOM_BRANCH=""
CUSTOM_BRANCH=$(osascript -e 'display dialog "Branch name (leave empty for auto-generated):" default answer "" buttons {"Cancel", "OK"} default button "OK"' -e 'text returned of result' 2>/dev/null) || CUSTOM_BRANCH=""
local REPO_NAME PARENT_DIR WORKTREE_DIR BRANCH_NAME
REPO_NAME=$(basename "$CWD")
PARENT_DIR=$(cd "$CWD/.." && pwd)
WORKTREE_DIR="$PARENT_DIR/worktrees/$REPO_NAME/$NAME"
if [ -n "$CUSTOM_BRANCH" ]; then
BRANCH_NAME="$CUSTOM_BRANCH"
else
BRANCH_NAME="worktree-$NAME"
fi
# Detect default remote branch
local DEFAULT_BRANCH
DEFAULT_BRANCH=$(cd "$CWD" && git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
: "${DEFAULT_BRANCH:=main}"
# Create git worktree in sibling directory
mkdir -p "$PARENT_DIR/worktrees/$REPO_NAME" >&2
cd "$CWD" || exit 1
git fetch origin &>/dev/null || true
git worktree add -b "$BRANCH_NAME" "$WORKTREE_DIR" "origin/$DEFAULT_BRANCH" >&2 || {
echo "Failed to create worktree: $NAME" >&2
exit 1
}
# Open Ghostty 2x2 layout:
# +---------------+---------------+
# | Claude Code | lazygit |
# | (top-left) | (top-right) |
# +---------------+---------------+
# | Terminal | yazi |
# | (bottom-left) | (bottom-right)|
# +---------------+---------------+
# Uses clipboard + Cmd+V for reliable text input (keystroke is unreliable for long paths)
{
sleep 1.5
osascript <<APPLESCRIPT >/dev/null 2>&1
-- Save current clipboard
try
set oldClip to the clipboard as text
on error
set oldClip to ""
end try
tell application "System Events"
tell process "Ghostty"
-- 1. Split right (Cmd+D) -> focus moves to right pane
keystroke "d" using {command down}
delay 1.0
-- 2. cd + lazygit in right pane (top-right)
set the clipboard to "cd '${WORKTREE_DIR}' && lazygit"
keystroke "v" using {command down}
delay 0.3
key code 36
delay 2.0
-- 3. Split down (Cmd+Shift+D) -> focus moves to bottom-right
keystroke "d" using {command down, shift down}
delay 1.0
-- 4. cd + yazi in bottom-right
set the clipboard to "cd '${WORKTREE_DIR}' && yazi"
keystroke "v" using {command down}
delay 0.3
key code 36
delay 0.5
-- 5. Navigate to Claude Code (Cmd+Option+Left)
key code 123 using {command down, option down}
delay 0.5
-- 6. Split down (Cmd+Shift+D) -> focus moves to bottom-left
keystroke "d" using {command down, shift down}
delay 1.0
-- 7. cd into worktree (terminal pane, bottom-left)
set the clipboard to "cd '${WORKTREE_DIR}'"
keystroke "v" using {command down}
delay 0.3
key code 36
end tell
end tell
-- Restore clipboard
delay 0.5
set the clipboard to oldClip
APPLESCRIPT
} &>/dev/null &
# Output the worktree path (the ONLY stdout Claude Code reads)
echo "$WORKTREE_DIR"
}
#-----------------------------------------------------------------------
# WorktreeRemove: Clean up worktree, branch, and empty directories
#-----------------------------------------------------------------------
remove_worktree() {
local WORKTREE_PATH
WORKTREE_PATH=$(echo "$INPUT" | jq -r '.worktree_path')
[ ! -d "$WORKTREE_PATH" ] && exit 0
# Find main repo (first entry in worktree list)
local MAIN_REPO BRANCH_NAME
MAIN_REPO=$(git -C "$WORKTREE_PATH" worktree list --porcelain 2>/dev/null | head -1 | sed 's/^worktree //')
BRANCH_NAME="worktree-$(basename "$WORKTREE_PATH")"
# Remove worktree and branch
cd "$MAIN_REPO" 2>/dev/null || exit 0
git worktree remove "$WORKTREE_PATH" --force 2>/dev/null || rm -rf "$WORKTREE_PATH"
git branch -D "$BRANCH_NAME" 2>/dev/null
# Clean up empty parent directories
rmdir "$(dirname "$WORKTREE_PATH")" 2>/dev/null
}
#-----------------------------------------------------------------------
# Event dispatcher
#-----------------------------------------------------------------------
case "$HOOK_EVENT" in
WorktreeCreate) create_worktree ;;
WorktreeRemove) remove_worktree ;;
esac
@d0zingcat
Copy link
Copy Markdown

Thanks!
It helps me a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment