Yay-find script with fzf search, install with selectable optional deps

A yay-find script vibe-coded by Gemini Pro 3.1
Features:

  1. fzf keyword search
  2. Tabulated selection list for the “hits” packages
  3. The user selects the package and is offered a list of optional packages to select and install.
  4. Options for the script:
    4.1 Help
    4.2 Version
    4.3 Install the selected package with -noconfirn
#!/usr/bin/env bash

# Script Metadata
VERSION="1.1.0"
TIMESTAMP="2026-07-21"

# Ensure standard English output for reliable parsing
export LANG=C

# --- Color Definitions ---
C_RESET='\e[0m'
C_CYAN='\e[1;36m'
C_GREEN='\e[1;32m'
C_YELLOW='\e[1;33m'
C_RED='\e[1;31m'
C_MAGENTA='\e[1;35m'
C_BOLD='\e[1m'

# Function to display help information
show_help() {
    echo -e "${C_BOLD}==================================================${C_RESET}"
    echo -e " ${C_CYAN}yay-find${C_RESET} - Interactive Search & Install with Opt-Deps"
    echo -e "${C_BOLD}==================================================${C_RESET}"
    echo -e "${C_BOLD}Usage:${C_RESET} $(basename "$0") [OPTIONS] <keyword...>"
    echo ""
    echo -e "${C_BOLD}Options:${C_RESET}"
    echo -e "  -y, --noconfirm Bypass any and all confirmation messages"
    echo -e "  -h, --help      Show this help message and exit"
    echo -e "  -v, --version   Show version and timestamp information and exit"
    echo ""
    echo -e "${C_BOLD}Description:${C_RESET}"
    echo "  Searches pacman, Chaotic-AUR, and AUR for packages matching the keyword(s)."
    echo "  Displays a tabulated fzf list of matching packages."
    echo "  If packages are selected, a second panel presents their optional"
    echo "  dependencies for selection. Installs selected packages and optionally"
    echo "  selected dependencies as clean dependencies (--asdeps)."
}

# Function to display version information
show_version() {
    echo -e "${C_CYAN}yay-find${C_RESET} version ${C_GREEN}$VERSION${C_RESET}"
    echo "Last updated: $TIMESTAMP"
}

# Check if no arguments were provided
if [ -z "$1" ]; then
    show_help
    exit 1
fi

NOCONFIRM_FLAG=""
KEYWORDS=""

# Argument parsing loop
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -v|--version)
            show_version
            exit 0
            ;;
        -y|--noconfirm)
            NOCONFIRM_FLAG="--noconfirm"
            shift
            ;;
        -*)
            echo -e "${C_RED}Error:${C_RESET} Unknown parameter passed: $1"
            echo "Run '$(basename "$0") --help' for usage."
            exit 1
            ;;
        *)
            # Append keywords
            KEYWORDS="$KEYWORDS $1"
            shift
            ;;
    esac
done

# Trim leading/trailing whitespace from keywords
KEYWORDS=$(echo "$KEYWORDS" | xargs)

if [ -z "$KEYWORDS" ]; then
    echo -e "${C_RED}Error:${C_RESET} Search keyword(s) required."
    exit 1
fi

echo -e "${C_BOLD}Searching for: ${C_CYAN}$KEYWORDS${C_RESET}..."

# Fetch search results from yay
SEARCH_RAW=$(yay -Ss $KEYWORDS 2>/dev/null)

if [ -z "$SEARCH_RAW" ]; then
    echo -e "${C_YELLOW}No packages found matching '$KEYWORDS'.${C_RESET}"
    exit 0
fi

# Parse yay -Ss output into a strictly tabulated format for fzf
SEARCH_FZF=$(echo "$SEARCH_RAW" | awk -v c_mag="\033[1;35m" -v c_grn="\033[1;32m" -v c_yel="\033[1;33m" -v c_rst="\033[0m" '
    /^[^ \t]/ {
        pkg = $1
        ver = $2
        installed = match($0, /\(Installed\)/) ? "[Installed]" : ""
        getline desc
        sub(/^[ \t]+/, "", desc)

        # Formatted with fixed-width columns:
        # 45 chars for Package, 20 chars for Version, 12 chars for Status
        printf "%s%-45s%s %s%-20s%s %s%-12s%s │ %s\n", c_mag, pkg, c_rst, c_grn, ver, c_rst, c_yel, installed, c_rst, desc
    }
')

# --- Panel 1: Package Selection ---
echo -e "\n${C_BOLD}[Package Selection]${C_RESET} Use ${C_BOLD}TAB${C_RESET} to select multiple, ${C_BOLD}ENTER${C_RESET} to confirm."
SELECTED_PKGS_RAW=$(echo -e "$SEARCH_FZF" | fzf -m --ansi --header="Select packages to install (Tab to mark multiple)")

# Extract just the "repo/pkgname" or "pkgname" from the selection
SELECTED_PKGS=$(echo "$SELECTED_PKGS_RAW" | sed $'s/\033\\[[0-9;]*m//g' | awk '{print $1}')

if [ -z "$SELECTED_PKGS" ]; then
    echo -e "${C_YELLOW}No packages selected. Exiting.${C_RESET}"
    exit 0
fi

# Clean package names (remove 'repo/' prefix for installation and dependency checking)
SELECTED_PKGS_CLEAN=$(echo "$SELECTED_PKGS" | awk -F'/' '{print $NF}' | tr '\n' ' ')

echo -e "\n${C_BOLD}Selected Packages:${C_RESET} ${C_CYAN}$SELECTED_PKGS_CLEAN${C_RESET}"
echo -e "${C_BOLD}Gathering optional dependencies...${C_RESET}"

# --- Gather Optional Dependencies ---
ALL_OPT_DEPS_FZF=""

for PKG in $SELECTED_PKGS_CLEAN; do
    INFO_CMD=""
    if yay -Qi "$PKG" &>/dev/null; then
        INFO_CMD="yay -Qi"
    elif yay -Si "$PKG" &>/dev/null; then
        INFO_CMD="yay -Si"
    else
        continue
    fi

    RAW_OPT=$($INFO_CMD "$PKG" 2>/dev/null | awk -F':' '
        /^Optional Deps/ {
            flag=1
            sub(/^Optional Deps[ \t]*:[ \t]*/, "")
            if ($0 != "") print $0
            next
        }
        /^[A-Z]/ { flag=0 }
        flag {
            sub(/^[ \t]+/, "")
            print $0
        }
    ' | grep -v '^$')

    if [ -n "$RAW_OPT" ] && [[ "$RAW_OPT" != *"None"* ]]; then
        while IFS= read -r line; do
            opt_pkg_name=$(echo "$line" | cut -d':' -f1 | xargs)
            desc=$(echo "$line" | cut -d':' -f2- | xargs | sed -e 's/ \[installed\]//i' -e 's/ \[pending\]//i')

            clean_pkg=$(echo "$opt_pkg_name" | sed -E 's/[<>=].*//')

            # Determine text and color separately to prevent ANSI-code padding issues
            if pacman -Qq "$clean_pkg" &>/dev/null; then
                status_text="[Installed]"
                status_color="${C_GREEN}"
            else
                status_text="[Pending]"
                status_color="${C_YELLOW}"
            fi

            # Create perfectly sized columns without colors messing up the character count
            padded_pkg=$(printf "%-35s" "$opt_pkg_name")
            padded_status=$(printf "%-12s" "$status_text")
            padded_parent=$(printf "%-25s" "(for $PKG)")

            # Assemble the final string, injecting colors around the evenly padded blocks
            ALL_OPT_DEPS_FZF+="${C_CYAN}${padded_pkg}${C_RESET} ${status_color}${padded_status}${C_RESET} ${C_MAGENTA}${padded_parent}${C_RESET} │ ${desc}\n"
        done <<< "$RAW_OPT"
    fi
done

# --- Panel 2: Optional Dependency Selection ---
SELECTED_OPT_DEPS_CLEAN=""

if [ -n "$ALL_OPT_DEPS_FZF" ]; then
    echo -e "\n${C_BOLD}[Optional Dependencies]${C_RESET} Use ${C_BOLD}TAB${C_RESET} to select, ${C_BOLD}ENTER${C_RESET} to confirm, or ${C_BOLD}ESC${C_RESET} to skip."

    SELECTED_OPT_RAW=$(echo -e "$ALL_OPT_DEPS_FZF" | fzf -m --ansi --header="Select optional dependencies (Tab to mark, ESC to skip)")
    SELECTED_OPT_DEPS_CLEAN=$(echo "$SELECTED_OPT_RAW" | sed $'s/\033\\[[0-9;]*m//g' | awk '{print $1}' | tr '\n' ' ')
else
    echo -e "  ${C_YELLOW}No optional dependencies found for selected packages.${C_RESET}"
fi

# --- Execution Phase ---
echo -e "\n${C_BOLD}==================================================${C_RESET}"
echo -e "${C_BOLD}Summary of Installations:${C_RESET}"
echo -e "Packages:      ${C_CYAN}${SELECTED_PKGS_CLEAN:-None}${C_RESET}"
echo -e "Opt-Depends:   ${C_CYAN}${SELECTED_OPT_DEPS_CLEAN:-None}${C_RESET}"
echo -e "${C_BOLD}==================================================${C_RESET}\n"

# Install main packages
if [ -n "$SELECTED_PKGS_CLEAN" ]; then
    echo -e "${C_BOLD}Installing main packages...${C_RESET}"
    yay -S $NOCONFIRM_FLAG $SELECTED_PKGS_CLEAN
fi

# Install optional dependencies as dependencies (--asdeps)
if [ -n "$SELECTED_OPT_DEPS_CLEAN" ]; then
    echo -e "\n${C_BOLD}Installing optional dependencies (--asdeps)...${C_RESET}"
    yay -S --asdeps $NOCONFIRM_FLAG $SELECTED_OPT_DEPS_CLEAN
fi

echo -e "\n${C_GREEN}Finished!${C_RESET}"

I guess this is cool. I don’t know I really don’t see the purpose of it. If I need to search for a package I use pacseek.