-
-
Save d0zingcat/df58a8aef876df7586bb561043545921 to your computer and use it in GitHub Desktop.
Ghostty terminal config + Claude Code worktree hook (2x2 layout: Claude Code, lazygit, terminal, yazi)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # vim: set filetype=toml : | |
| # ============================================ | |
| # Ghostty Terminal Configuration | |
| # ============================================ | |
| # File: ~/.config/ghostty/config | |
| # Reload: Cmd+Shift+, | |
| # View options: ghostty +show-config --default --docs | |
| # --- Typography --- | |
| font-family = "JetBrainsMono Nerd Font Mono" | |
| font-size = 13 | |
| font-thicken = true | |
| adjust-cell-height = 2 | |
| # --- Theme and Colors --- | |
| 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) --- | |
| 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 --- | |
| keybind = cmd+t=new_tab | |
| keybind = cmd+alt+h=previous_tab | |
| keybind = cmd+alt+l=next_tab | |
| keybind = cmd+w=close_surface | |
| keybind = cmd+d=new_split:right | |
| keybind = cmd+shift+d=new_split:down | |
| keybind = cmd+shift+h=goto_split:left | |
| keybind = cmd+shift+j=goto_split:bottom | |
| keybind = cmd+shift+k=goto_split:top | |
| keybind = cmd+shift+l=goto_split:right | |
| keybind = global:ctrl+grave_accent=toggle_quick_terminal | |
| keybind = cmd+shift+e=equalize_splits | |
| keybind = cmd+shift+f=toggle_split_zoom | |
| keybind = cmd+shift+comma=reload_config | |
| keybind = cmd+plus=increase_font_size:1 | |
| keybind = cmd+minus=decrease_font_size:1 | |
| keybind = cmd+zero=reset_font_size | |
| # --- Performance --- | |
| scrollback-limit = 25000000 | |
| confirm-close-surface = false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/zsh | |
| # 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) | |
| # | |
| # Location: ~/.claude/hooks/worktree-ghostty-layout.sh | |
| # | |
| # Installation (add to ~/.claude/settings.json): | |
| # "hooks": { | |
| # "WorktreeCreate": [{ "hooks": [{ "type": "command", "command": "/Users/d0zingcat/.claude/hooks/worktree-ghostty-layout.sh" }] }], | |
| # "WorktreeRemove": [{ "hooks": [{ "type": "command", "command": "/Users/d0zingcat/.claude/hooks/worktree-ghostty-layout.sh" }] }] | |
| # } | |
| # Note: use absolute paths only — no ~ expansion in settings.json hooks. | |
| # | |
| # Usage: | |
| # 1. Open Claude Code in any git repository directory | |
| # 2. Ask Claude to create a worktree, e.g.: "work in a worktree" or "start a worktree" | |
| # 3. Claude calls EnterWorktree → WorktreeCreate hook fires automatically | |
| # 4. The hook creates the worktree and opens a Ghostty 2x2 split layout | |
| # To remove: ask Claude to "exit the worktree" → WorktreeRemove hook fires | |
| # | |
| # Ghostty keybindings required: | |
| # super+d = new_split:right | |
| # super+shift+d = new_split:down | |
| # super+shift+h = 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 | |
| local BASE_REF="origin/$DEFAULT_BRANCH" | |
| if ! git rev-parse "$BASE_REF" &>/dev/null; then | |
| BASE_REF="HEAD" | |
| fi | |
| git worktree add -b "$BRANCH_NAME" "$WORKTREE_DIR" "$BASE_REF" >&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+Shift+H) | |
| keystroke "h" using {command down, shift 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment