Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

135 lines
3.9 KiB
Bash
Executable File

#!/bin/bash
# Hook: Worktree Ghostty Layout
#
# Creates git worktrees in a sibling directory and opens a Ghostty terminal
# layout with lazygit (top-right) and yazi (bottom-right).
#
# Events:
# - WorktreeCreate: Creates worktree + opens Ghostty 3-panel 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
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')
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"
BRANCH_NAME="worktree-$NAME"
# 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 layout: Claude (left) | lazygit (top-right) / yazi (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"
-- Split right (Cmd+D)
keystroke "d" using {command down}
delay 1.0
-- cd + lazygit
set the clipboard to "cd '${WORKTREE_DIR}' && lazygit"
keystroke "v" using {command down}
delay 0.3
key code 36
delay 2.0
-- Split down (Cmd+Shift+D)
keystroke "d" using {command down, shift down}
delay 1.0
-- cd + yazi
set the clipboard to "cd '${WORKTREE_DIR}' && yazi"
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