Skip to content

Instantly share code, notes, and snippets.

@gatopeich
Last active February 25, 2026 09:26
Show Gist options
  • Select an option

  • Save gatopeich/2b4e805f44791fc817bc043eb82ecaac to your computer and use it in GitHub Desktop.

Select an option

Save gatopeich/2b4e805f44791fc817bc043eb82ecaac to your computer and use it in GitHub Desktop.
Claude Code Chooser - TUI project picker and launcher for Claude Code
#!/bin/bash
# Claude Code Chooser - TUI project picker and launcher for Claude Code
# Shows recent Claude projects, lets you pick model/effort/mode, then launches claude.
#
# https://gist.github.com/gatopeich/2b4e805f44791fc817bc043eb82ecaac
# Author: gatopeich @ github
#
# Usage: claude-chooser.sh
#
# Kitty terminal integration:
# # Desktop launcher (~/.local/share/applications/claude-kitty.desktop):
# Exec=kitty --class claude-kitty -T "Claude Code" --session ~/.config/kitty/claude-session.conf
#
# # Session file (~/.config/kitty/claude-session.conf) - new tabs also open chooser:
# launch /path/to/claude-chooser.sh
#
# Optional: set window icon on X11 with xseticon.py:
# https://gist.github.com/gatopeich/9d0c61cae0a4e01b23b0afc543c27fbb
#
# Requires: dialog, claude, python3 (for session discovery)
ICON="$HOME/.local/share/icons/claude-code.png"
EXTRA_FILE="$HOME/.config/claude-chooser-extra-args"
# Dialog theme: override color7 (WHITE) to actual white for dialog backgrounds
printf '\033]4;7;#f0f0f0\007'
printf '\033]4;15;#ffffff\007'
export DIALOGRC="$HOME/.config/claude-chooser-dialogrc"
# Extract recent project paths from Claude sessions, sorted by most recent session activity
DIRS=$(python3 -c "
import json, glob, os
seen = set()
for f in sorted(glob.glob(os.path.expanduser('~/.claude/projects/*/*.jsonl')), key=os.path.getmtime, reverse=True):
try:
with open(f) as fh:
for line in fh:
rec = json.loads(line)
path = rec.get('cwd') or rec.get('projectPath') or ''
if os.path.isdir(path) and path not in seen:
seen.add(path)
print(path)
break
if len(seen) >= 10: break
except: pass
" 2>/dev/null)
# Browse directories using dialog menus
browse_dir() {
local cur="${1:-$HOME}"
while true; do
local items=(">>> SELECT THIS FOLDER <<<" "$cur" ".." "Parent directory")
while IFS= read -r d; do
items+=("$(basename "$d")/" "")
done < <(find "$cur" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort)
local pick=$(dialog --clear --title "Browse: $cur" \
--menu "Choose directory:" 0 0 0 \
"${items[@]}" \
3>&1 1>&2 2>&3) || return 1
if [ "$pick" = ">>> SELECT THIS FOLDER <<<" ]; then
echo "$cur"
return 0
elif [ "$pick" = ".." ]; then
cur=$(dirname "$cur")
else
cur="$cur/${pick%/}"
fi
done
}
# State
DANGEROUS=off
EXTRA=$(cat "$EXTRA_FILE" 2>/dev/null)
while true; do
# Build single menu
MENU=()
i=0
while IFS= read -r d; do
[ -n "$d" ] && MENU+=("$d" "") && ((i++))
[ $i -ge 10 ] && break
done <<< "$DIRS"
MENU+=("Browse..." "")
MENU+=("---" "─────────────────────")
if [ "$DANGEROUS" = "on" ]; then
MENU+=("[X] Dangerous mode" "skip all permissions")
else
MENU+=("[ ] Dangerous mode" "skip all permissions")
fi
if [ -n "$EXTRA" ]; then
MENU+=("EXTRA" "args: $EXTRA")
else
MENU+=("EXTRA" "args...")
fi
PICK=$(dialog --clear --title "Claude Code" \
--menu "Select project to launch, or toggle options:" 0 0 0 \
"${MENU[@]}" \
3>&1 1>&2 2>&3) || exit 1
case "$PICK" in
"---") continue ;;
*"Dangerous mode")
[ "$DANGEROUS" = "off" ] && DANGEROUS=on || DANGEROUS=off
continue ;;
"EXTRA")
EXTRA=$(dialog --clear --title "Extra args" \
--inputbox "Claude arguments (e.g. --continue --model opus):" 8 60 "$EXTRA" \
3>&1 1>&2 2>&3) || EXTRA=""
echo "$EXTRA" > "$EXTRA_FILE"
continue ;;
"Browse...")
DIR=$(browse_dir "$HOME") || continue
break ;;
*)
DIR="$PICK"
break ;;
esac
done
# Restore original terminal colors
printf '\033]4;7;#6e7781\007'
printf '\033]4;15;#8c959f\007'
clear
CLAUDE_ARGS=""
if [ "$DANGEROUS" = "on" ]; then
CLAUDE_ARGS="$CLAUDE_ARGS --dangerously-skip-permissions"
printf '\033]11;#fcf3cf\007'
printf '\033]10;#1a1a1a\007'
fi
[ -n "$EXTRA" ] && CLAUDE_ARGS="$CLAUDE_ARGS $EXTRA"
# Set window icon (optional: requires xseticon and xdotool)
xseticon $(xdotool getactivewindow 2>/dev/null) "$ICON" 2>/dev/null
printf '\033]0;Claude on %s\007' "$(basename "$DIR")"
[ -n "$TMUX_PANE" ] && tmux rename-window -t "$TMUX_PANE" "$(basename "$DIR")" 2>/dev/null
cd "$DIR" && claude $CLAUDE_ARGS
echo ""
echo " [Claude exited. Press Enter to close, or scroll up to review output]"
read
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment