#!/bin/bash # Launch tmux session with an LLM in fzf-selected project directories SESSION="ai" # Switch to or attach to the session attach_session() { if [ -n "$TMUX" ]; then tmux switch-client -t "$SESSION" else tmux attach-session -t "$SESSION" fi } # Attach directly to existing session if [ "$1" = "--attach" ]; then if ! tmux has-session -t "$SESSION" 2>/dev/null; then echo "No $SESSION session to attach to." >&2 exit 1 fi attach_session exit 0 fi # Dependency checks for cmd in fzf tmux claude; do if ! command -v "$cmd" &>/dev/null; then echo "Error: $cmd is not installed" >&2 exit 1 fi done # Claude command — separated for safe editing AI_CMD="claude" AI_INSTRUCTIONS='Read docs/protocols.org and follow all instructions.' # Create a window in the session and launch claude; prints window ID create_window() { local dir="$1" name="$2" wid wid=$(tmux new-window -t "$SESSION" -n "$name" -c "$dir" -P -F '#{window_id}') sleep 0.1 tmux send-keys -t "$wid" "$AI_CMD \"$AI_INSTRUCTIONS\"" Enter echo "$wid" } # Read fzf selections into the 'selected' array read_selections() { selected=() while IFS= read -r line; do selected+=("$line") done <<<"$1" } # Build candidate directory list build_candidates() { candidates=() [ -d "$HOME/.emacs.d" ] && candidates+=("~/.emacs.d") if [ -d "$HOME/code" ]; then while IFS= read -r d; do candidates+=("~/${d#"$HOME"/}") done < <(find "$HOME/code" -maxdepth 1 -mindepth 1 -type d | sort) fi if [ -d "$HOME/projects" ]; then while IFS= read -r d; do candidates+=("~/${d#"$HOME"/}") done < <(find "$HOME/projects" -maxdepth 1 -mindepth 1 -type d | sort) fi } # Sort windows alphabetically by name, shells last sort_windows() { local windows shells projects windows=$(tmux list-windows -t "$SESSION" -F '#{window_name}'$'\t''#{window_id}') shells=$(echo "$windows" | grep -iE '^(bash|zsh|fish|sh)'$'\t' | sort -t$'\t' -k1,1f) projects=$(echo "$windows" | grep -viE '^(bash|zsh|fish|sh)'$'\t' | sort -t$'\t' -k1,1f) local all all=$(printf '%s\n' "$shells" "$projects" | sed '/^$/d') # Move all to high temp indices to avoid collisions local i=900 while IFS=$'\t' read -r _name wid; do tmux move-window -s "$wid" -t "$SESSION:$i" ((i++)) done <<<"$all" # Place shells at 0, projects from 1 i=0 if [ -n "$shells" ]; then while IFS=$'\t' read -r _name wid; do tmux move-window -s "$wid" -t "$SESSION:$i" ((i++)) done <<<"$shells" fi i=1 if [ -n "$projects" ]; then while IFS=$'\t' read -r _name wid; do tmux move-window -s "$wid" -t "$SESSION:$i" ((i++)) done <<<"$projects" fi } # If session exists, add new windows to it if tmux has-session -t "$SESSION" 2>/dev/null; then # Get existing window names to filter duplicates existing=$(tmux list-windows -t "$SESSION" -F '#{window_name}') build_candidates # Filter out candidates that already have a window open filtered=() for c in "${candidates[@]}"; do name="$(basename "${c/#\~/$HOME}")" if ! echo "$existing" | grep -qxF "$name"; then filtered+=("$c") fi done if [ ${#filtered[@]} -eq 0 ]; then echo "All projects already have windows open." else selections=$(printf '%s\n' "${filtered[@]}" | fzf --multi --height=70% --reverse) if [ -n "$selections" ]; then read_selections "$selections" first_wid="" for entry in "${selected[@]}"; do dir="${entry/#\~/$HOME}" name="$(basename "$dir")" wid=$(create_window "$dir" "$name") [ -z "$first_wid" ] && first_wid="$wid" done sort_windows tmux select-window -t "$first_wid" fi fi attach_session exit 0 fi # New session: select projects and create session build_candidates selections=$(printf '%s\n' "${candidates[@]}" | fzf --multi --height=70% --reverse) [ -z "$selections" ] && exit 0 read_selections "$selections" # Create session with first selection first="${selected[0]}" dir="${first/#\~/$HOME}" name="$(basename "$dir")" first_wid=$(tmux new-session -d -s "$SESSION" -n "$name" -c "$dir" -P -F '#{window_id}') tmux send-keys -t "$first_wid" "$AI_CMD \"$AI_INSTRUCTIONS\"" Enter # Create remaining windows for entry in "${selected[@]:1}"; do dir="${entry/#\~/$HOME}" name="$(basename "$dir")" create_window "$dir" "$name" > /dev/null done # Sort windows and select first sort_windows tmux select-window -t "$SESSION:1" attach_session