@@ -49,6 +49,15 @@ get_version() {
4949SCAN_DIR=" ${HOME} /src"
5050TARGET_BRANCH=" "
5151UPDATE_CHECK_FILE=" /tmp/git-wt-update-check-$$ "
52+ PR_CACHE_DIR=" /tmp/git-wt-pr-cache-$$ "
53+ export PR_CACHE_DIR # Export for fzf preview subshell
54+
55+ # ANSI color helpers (defined early for use in exit trap)
56+ style_bold () { printf ' \033[1m%s\033[0m\n' " $1 " ; }
57+ style_warn () { printf ' \033[38;5;214m%s\033[0m\n' " $1 " ; }
58+ style_error () { printf ' \033[38;5;196m%s\033[0m\n' " $1 " ; }
59+ style_success () { printf ' \033[38;5;82m%s\033[0m\n' " $1 " ; }
60+ style_bold_error () { printf ' \033[1;38;5;196m%s\033[0m\n' " $1 " ; }
5261
5362# Background update check - runs asynchronously
5463check_for_updates () {
@@ -84,7 +93,10 @@ check_for_updates "$CURRENT_VERSION" &
8493UPDATE_CHECK_PID=$!
8594
8695# Cleanup and show update notification on exit
87- show_update_notification () {
96+ cleanup_and_notify () {
97+ # Cleanup PR cache directory
98+ rm -rf " $PR_CACHE_DIR " 2> /dev/null || true
99+
88100 # Wait briefly for background check (non-blocking if already done)
89101 wait " $UPDATE_CHECK_PID " 2> /dev/null || true
90102
@@ -97,12 +109,12 @@ show_update_notification() {
97109 if [[ " $update_info " == UPDATE_AVAILABLE= * ]]; then
98110 local new_version=" ${update_info# UPDATE_AVAILABLE=} "
99111 echo " "
100- gum style --foreground 214 " A new release of git-wt is available: ${CURRENT_VERSION# v} → $new_version "
112+ style_warn " A new release of git-wt is available: ${CURRENT_VERSION# v} → $new_version "
101113 echo " To upgrade, run: brew upgrade git-wt"
102114 fi
103115 fi
104116}
105- trap show_update_notification EXIT
117+ trap cleanup_and_notify EXIT
106118
107119show_help () {
108120 sed -n ' 2,17p' " $0 " | sed ' s/^# //' | sed ' s/^#//'
@@ -124,15 +136,31 @@ check_tool() {
124136 if ! command -v " $1 " & > /dev/null; then
125137 echo " Error: $1 is required but not installed" >&2
126138 case " $1 " in
127- gum ) echo " Install with: brew install gum " >&2 ;;
139+ fzf ) echo " Install with: brew install fzf " >&2 ;;
128140 esac
129141 exit 1
130142 fi
131143}
132144
133- check_tool gum
145+ check_tool fzf
134146check_tool git
135147
148+ # Confirm prompt (returns 0 for yes, 1 for no)
149+ confirm () {
150+ local prompt=" ${1:- Confirm?} "
151+ local result
152+ result=$( printf ' Yes\nNo' | fzf --header=" $prompt " --height=5 --no-info)
153+ [[ " $result " == " Yes" ]]
154+ }
155+
156+ # Text input prompt
157+ input_prompt () {
158+ local placeholder=" ${1:- Enter value} "
159+ local result
160+ result=$( fzf --print-query --header=" $placeholder " --height=3 --no-info < /dev/null 2> /dev/null | head -1)
161+ echo " $result "
162+ }
163+
136164# Function to find repos in a directory
137165find_repos_in_dir () {
138166 local dir=" $1 "
@@ -148,11 +176,10 @@ select_repo() {
148176 local repos=()
149177
150178 echo " "
151- gum style --bold " Not in a git repository"
179+ style_bold " Not in a git repository"
152180 echo " "
153181
154- echo " "
155- gum spin --title " Scanning $SCAN_DIR for git repos..." -- sleep 0.5
182+ echo " Scanning $SCAN_DIR for git repos..."
156183
157184 # Find repos
158185 while IFS= read -r repo_path; do
@@ -167,7 +194,7 @@ select_repo() {
167194 fi
168195
169196 # Let user select
170- SELECTED_REPO=$( printf ' %s\n' " ${repos[@]} " | gum filter --placeholder " Select repository... " )
197+ SELECTED_REPO=$( printf ' %s\n' " ${repos[@]} " | fzf --prompt= " Select repository> " )
171198 if [[ -z " $SELECTED_REPO " ]]; then
172199 echo " Cancelled"
173200 exit 0
@@ -239,11 +266,11 @@ switch_to_branch() {
239266
240267 # Ask what to base it on
241268 local base_options=(" $default_branch (default)" " other..." )
242- local base_selected=$( printf ' %s\n' " ${base_options[@]} " | gum choose --header " Create new branch based on:" )
269+ local base_selected=$( printf ' %s\n' " ${base_options[@]} " | fzf --header= " Create new branch based on:" --height=5 --no-info )
243270
244271 local base_branch=" $default_branch "
245272 if [[ " $base_selected " == " other..." * ]]; then
246- base_branch=$( git -C " $git_root " branch -a --format=' %(refname:short)' | gum filter --placeholder " Select base branch... " )
273+ base_branch=$( git -C " $git_root " branch -a --format=' %(refname:short)' | fzf --prompt= " Select base branch> " )
247274 fi
248275
249276 git -C " $git_root " worktree add -b " $branch " " $worktree_path " " $base_branch "
289316
290317# Fetch from remote first
291318echo " "
292- gum spin --title " Fetching from remote..." -- git -C " $GIT_ROOT " fetch origin --prune 2> /dev/null || true
319+ echo " Fetching from remote..."
320+ git -C " $GIT_ROOT " fetch origin --prune 2> /dev/null || true
321+
322+ # Setup for PR preview caching
323+ mkdir -p " $PR_CACHE_DIR "
324+ export GIT_ROOT # Export for fzf preview subshell
293325
294326# If branch specified, directly create/switch to that worktree
295327if [[ -n " $TARGET_BRANCH " ]]; then
296328 echo " "
297- gum style --bold " Switching to worktree for: $TARGET_BRANCH "
329+ style_bold " Switching to worktree for: $TARGET_BRANCH "
298330 echo " "
299331
300332 switch_to_branch " $TARGET_BRANCH " " $GIT_ROOT "
303335
304336# Interactive mode - offer worktree selection
305337echo " "
306- gum style --bold " Git Worktree Selector"
338+ style_bold " Git Worktree Selector"
307339echo " "
308340
309341# Get existing worktrees (including root checkout) and track their branches
@@ -358,19 +390,103 @@ done
358390# Add option to switch repo (step "up")
359391MENU_OPTIONS+=(" 🔄 (switch repository)" )
360392
361- # Show selection menu (--no-sort keeps existing worktrees at top while filtering)
362- SELECTED=$( printf ' %s\n' " ${MENU_OPTIONS[@]} " | gum filter --no-sort --placeholder " Select or create worktree..." )
393+ # Show selection menu with fzf
394+ # Preview shows PR info from cache, falls back to single fetch if not cached yet
395+ KEY_PRESSED=" "
396+ FZF_RESULT=$( printf ' %s\n' " ${MENU_OPTIONS[@]} " | fzf \
397+ --style=full \
398+ --expect=d \
399+ --no-sort \
400+ --header=" enter=select, d=delete worktree" \
401+ --prompt=" Select or create worktree> " \
402+ --preview='
403+ item={}
404+ # Extract branch name from different formats
405+ if [[ "$item" == "📂 [worktree]"* ]] || [[ "$item" == "🏠 [root]"* ]]; then
406+ branch=$(echo "$item" | sed "s/^[^ ]* \[[^]]*\] //" | sed "s/ →.*//")
407+ elif [[ "$item" == "📁"* ]] || [[ "$item" == "🔄"* ]]; then
408+ echo "No preview available"
409+ exit 0
410+ else
411+ branch="$item"
412+ fi
413+
414+ # Read from cache if available
415+ safe_branch=$(echo "$branch" | tr "/" "_")
416+ cache_file="$PR_CACHE_DIR/$safe_branch"
417+
418+ if [[ -f "$cache_file" ]]; then
419+ cat "$cache_file"
420+ elif command -v gh &>/dev/null; then
421+ # Cache miss - fetch just this branch from correct repo context
422+ cd "$GIT_ROOT" 2>/dev/null || true
423+ result=$(gh pr view "$branch" 2>&1)
424+ if [[ $? -eq 0 ]]; then
425+ echo "$result" | tee "$cache_file"
426+ else
427+ echo "No PR found for branch: $branch" | tee "$cache_file"
428+ fi
429+ else
430+ echo "No PR found for branch: $branch"
431+ fi
432+ ' \
433+ --preview-window=right:50%:wrap)
434+ KEY_PRESSED=$( echo " $FZF_RESULT " | head -1)
435+ SELECTED=$( echo " $FZF_RESULT " | tail -1)
363436
364437if [[ -z " $SELECTED " ]]; then
365438 echo " Cancelled"
366439 exit 0
367440fi
368441
442+ # Handle 'd' key press for delete
443+ if [[ " $KEY_PRESSED " == " d" ]]; then
444+ # Only allow deleting worktrees (not root, not create option, not branches)
445+ if [[ " $SELECTED " != " 📂 [worktree]" * ]]; then
446+ echo " "
447+ style_warn " Cannot delete: only worktrees can be deleted with 'd'"
448+ echo " Selected item: $SELECTED "
449+ exit 1
450+ fi
451+
452+ # Extract worktree path
453+ WT_PATH=$( echo " $SELECTED " | sed ' s/.*→ //' )
454+ WT_BRANCH=$( echo " $SELECTED " | sed ' s/📂 \[worktree\] //' | sed ' s/ →.*//' )
455+
456+ echo " "
457+ style_bold_error " Delete Worktree"
458+ echo " "
459+ echo " Branch: $WT_BRANCH "
460+ echo " Path: $WT_PATH "
461+ echo " "
462+
463+ if confirm " Are you sure you want to delete this worktree?" ; then
464+ # Remove the worktree
465+ if git -C " $GIT_ROOT " worktree remove " $WT_PATH " 2> /dev/null; then
466+ echo " "
467+ style_success " ✓ Worktree deleted successfully"
468+ else
469+ # Try force remove if normal remove fails
470+ echo " "
471+ style_warn " Worktree has uncommitted changes or other issues."
472+ if confirm " Force delete? (will discard any uncommitted changes)" ; then
473+ git -C " $GIT_ROOT " worktree remove --force " $WT_PATH "
474+ style_success " ✓ Worktree force deleted"
475+ else
476+ echo " Cancelled"
477+ fi
478+ fi
479+ else
480+ echo " Cancelled"
481+ fi
482+ exit 0
483+ fi
484+
369485# Handle selection
370486case " $SELECTED " in
371487 " 📁 (create new worktree)" )
372488 # Get branch name
373- BRANCH_NAME=$( gum input --placeholder " Enter new branch name... " )
489+ BRANCH_NAME=$( input_prompt " Enter new branch name" )
374490 if [[ -z " $BRANCH_NAME " ]]; then
375491 echo " Cancelled"
376492 exit 0
@@ -381,12 +497,12 @@ case "$SELECTED" in
381497 DEFAULT_BRANCH=$( git -C " $GIT_ROOT " symbolic-ref refs/remotes/origin/HEAD 2> /dev/null | sed ' s@^refs/remotes/origin/@@' || echo " main" )
382498
383499 BASE_OPTIONS=(" $DEFAULT_BRANCH (default)" " $CURRENT_BRANCH (current)" " other..." )
384- BASE_SELECTED=$( printf ' %s\n' " ${BASE_OPTIONS[@]} " | gum choose --header " Base branch:" )
500+ BASE_SELECTED=$( printf ' %s\n' " ${BASE_OPTIONS[@]} " | fzf --header= " Base branch:" --height=6 --no-info )
385501
386502 case " $BASE_SELECTED " in
387503 * " (default)" * ) BASE_BRANCH=" $DEFAULT_BRANCH " ;;
388504 * " (current)" * ) BASE_BRANCH=" $CURRENT_BRANCH " ;;
389- * ) BASE_BRANCH=$( git -C " $GIT_ROOT " branch -a --format=' %(refname:short)' | gum filter --placeholder " Select base branch... " ) ;;
505+ * ) BASE_BRANCH=$( git -C " $GIT_ROOT " branch -a --format=' %(refname:short)' | fzf --prompt= " Select base branch> " ) ;;
390506 esac
391507
392508 # Create worktree path
0 commit comments