AUR/Pacman has basic search with pipelining and grep; the user can filter the returned hits to narrow the results. However, why do AUR or pacman not have a built-in fzf support with an optional select list to select package hits for installation?
Probably when those tools were first designed, fzf didn’t exist or it wasn’t an important design decision to use fzf to select packages.
Here is the yay-find script vibe-coded by Gemini Pro 3.1
Features:
- fzf keyword search
- Tabulated selection list for the “hits” packages
- The user selects the package and is offered a list of optional packages to select and install.
- 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}"
pacseek exists, by the way, OP.
I tried pacseek and it did not work well with my zshell. So, I asked Gemini to improve on yay-opt (install optional dependencies of the package) and generate a yay-find bash script
What didn’t work exactly?
Packseek does not respond to the keystrokes; latency is too long, I do not know why. Also, I had a few crashes and corrupted output. I was forced to close the terminal (Konsole) since it was not usable.
Pacseek has a nice side panel for the package info; all the info is displayed. Nice touch,
Also, I noticed I cannot use keywords, like bash language, when I am searching for the bash language server, LSP.
I’ve noticed that you have to click the list with a mouse before you can use the keyboard. I’m hoping this is something that gets fixed in the near future. However seems to have been a while since any development has been done on pacseek. (maybe finished in the eyes of the dev
)
Just ask @moson. He is somewhere around here
Here is a shorter, not vide-coded script from my personal zsh config. Used it for years, never felt the need for more.
# fzf aided package installer;
# originally from https://github.com/IBArbitrary/dotfiles/blob/master/.config/fish/config.fish#L72-L78
function pacs {
cmd=$(yay -Slq | fzf --prompt 'yay> ' \
-q "$1" \
--header 'Install packages. CTRL+(Pacman/Aur/Installed)' \
--bind 'ctrl-p:change-prompt(pacman> )+reload(pacman -Slq)' \
--bind 'ctrl-a:change-prompt(yay> )+reload(yay -Slq)' \
--bind 'ctrl-i:change-prompt(inst> )+reload(yay -Qq)' \
--multi --height=80% --preview 'yay -Si {1}' \
--preview-window bottom) #| xargs -ro yay -S
cmd=${cmd//$'\n'/ } # newline -> space
if ! [ -z "$cmd" ];
then print -z yay -S $cmd ;
fi
}