Files
super-man/super-man.sh
T
leetcrypt 2fb20b0682 refactor: rebrand bash-helper to super-man + ESC returns straight to menu
Rename bash-helper.sh -> super-man.sh and update all docs/tests to the
super-man name and alias. In interactive mode, pressing Esc in the flag
browser now returns directly to the home menu, removing the intermediary
"Press Enter to search another command" prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 20:52:18 -07:00

1490 lines
60 KiB
Bash
Executable File

#!/bin/bash
# Get script directory for commands-db.json location
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DB_FILE="$SCRIPT_DIR/commands-db.json"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m' # No Color
# ASCII Banner
show_banner() {
echo -en "${CYAN}"
cat <<'BANNER'
____ __ __
/ ___| _ _ _ __ ___ _ __ | \/ | __ _ _ __
\___ \| | | | '_ \ / _ \ '__| | |\/| |/ _` | '_ \
___) | |_| | |_) | __/ | | | | | (_| | | | |
|____/ \__,_| .__/ \___|_| |_| |_|\__,_|_| |_|
|_|
BANNER
echo -e "${NC}${YELLOW} Your Intelligent CLI Assistant for Bash${NC}"
echo ""
}
show_help() {
show_banner
echo -e "${BOLD}${YELLOW}QUICK START${NC}"
echo -e " ${GREEN}super-man ask \"find large files\"${NC} # Ask a question"
echo -e " ${GREEN}super-man category files${NC} # Browse by category"
echo -e " ${GREEN}super-man explain \"tar -czf\"${NC} # Explain a command"
echo ""
echo -e "${BOLD}${YELLOW}MODES${NC}"
echo -e " ${CYAN}Natural Language Queries:${NC}"
echo -e " ${GREEN}ask${NC} \"query\" Ask in natural language (database)"
echo -e " ${GREEN}task${NC} \"description\" Describe what you want to do (database)"
echo -e " ${GREEN}ai${NC} \"complex query\" AI-powered NL2SH model (advanced) 🤖"
echo ""
echo -e " ${CYAN}Browse & Explore:${NC}"
echo -e " ${GREEN}category${NC} NAME Browse by category (files/text/network/system)"
echo -e " ${GREEN}explain${NC} \"command\" Explain what a command does"
echo ""
echo -e " ${CYAN}Direct Lookup:${NC}"
echo -e " ${GREEN}COMMAND [FILTER]${NC} Show flags for command (e.g., ls size)"
echo -e " ${GREEN}-i${NC}, ${GREEN}--interactive${NC} Interactive fzf mode"
echo -e " ${GREEN}-l${NC}, ${GREEN}--list${NC} List all commands"
echo ""
echo -e "${BOLD}${YELLOW}CATEGORIES${NC} (20+ tasks)"
echo -e " ${CYAN}files${NC} File operations (compress, find, permissions, symlinks)"
echo -e " ${CYAN}text${NC} Text processing (search, replace, compare, count)"
echo -e " ${CYAN}network${NC} Network ops (download, ports, ping)"
echo -e " ${CYAN}system${NC} System monitoring (cpu, memory, processes)"
echo ""
echo -e "${BOLD}${YELLOW}EXAMPLES${NC}"
echo -e " ${DIM}# Natural language${NC}"
echo " super-man ask \"compress folder\""
echo " super-man ask \"search text in files\""
echo " super-man task \"monitor cpu usage\""
echo ""
echo -e " ${DIM}# Browse and learn${NC}"
echo " super-man category network"
echo " super-man explain \"find . -name '*.txt'\""
echo ""
echo -e " ${DIM}# Original modes (still work)${NC}"
echo " super-man ls size # Direct flag lookup"
echo " super-man # Interactive fzf mode"
echo ""
echo -e "${BOLD}${YELLOW}REQUIREMENTS${NC}"
echo -e " ${GREEN}${NC} jq (for natural language queries)"
echo -e " ${GREEN}${NC} fzf (for interactive mode only)"
echo ""
echo -e "${BOLD}${YELLOW}AI MODE${NC} 🤖"
echo -e " ${GREEN}✓ NL2SH model detected!${NC} Use AI for complex queries:"
echo -e " ${GREEN}super-man ai \"find python files modified last week\"${NC}"
echo -e " ${GREEN}super-man ai \"count lines in all js files\"${NC}"
echo ""
echo " Other models available: llama3.2, qwen2.5"
echo " To switch models, edit mode_ai() function"
echo ""
echo -e "${YELLOW}Database:${NC} $DB_FILE"
echo -e "${YELLOW}Version:${NC} 2.1.0 (250+ commands, AI-powered)"
echo ""
}
# Check if jq is available
check_jq() {
if ! command -v jq &> /dev/null; then
echo -e "${RED}Error: jq is not installed${NC}"
echo "jq is required for database search features."
echo "Install it with: sudo apt install jq"
echo ""
echo "You can still use the original modes:"
echo " super-man COMMAND [FILTER]"
echo " super-man --interactive"
return 1
fi
return 0
}
# Check if database exists
check_database() {
if [ ! -f "$DB_FILE" ]; then
echo -e "${RED}Error: Database file not found${NC}"
echo "Expected location: $DB_FILE"
echo ""
echo "You can still use the original modes for direct command lookup."
return 1
fi
return 0
}
# Search database by keywords
search_by_keywords() {
local query="$1"
local max_results="${2:-3}"
if ! check_jq || ! check_database; then
return 1
fi
# Convert query to lowercase for matching
local query_lower=$(echo "$query" | tr '[:upper:]' '[:lower:]')
# Search and score tasks based on keyword matches
jq -r --arg query "$query_lower" --argjson max "$max_results" '
.tasks[] |
# Calculate score based on keyword matches
. as $task |
($query | split(" ") | map(select(length > 2))) as $query_words |
(
[
$query_words[] as $word |
(
(.keywords | map(select(. | ascii_downcase | contains($word))) | length) * 2 +
(if (.description | ascii_downcase | contains($word)) then 1 else 0 end) +
(if (.command | ascii_downcase | contains($word)) then 1 else 0 end)
)
] | add // 0
) as $score |
select($score > 0) |
{score: $score, task: .}
' "$DB_FILE" | jq -s --argjson max "$max_results" 'sort_by(-.score) | .[0:$max] | .[] | .task'
}
# Display a task with full details
show_task() {
local task_json="$1"
local id=$(echo "$task_json" | jq -r '.id')
local desc=$(echo "$task_json" | jq -r '.description')
local cmd=$(echo "$task_json" | jq -r '.command')
local category=$(echo "$task_json" | jq -r '.category')
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN}Task:${NC} $desc"
echo -e "${CYAN}Category:${NC} $category"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${YELLOW}Command:${NC}"
echo -e " ${MAGENTA}$cmd${NC}"
echo ""
# Show flags explanation if available
local flags=$(echo "$task_json" | jq -r '.flags_explained // empty')
if [ -n "$flags" ]; then
echo -e "${YELLOW}Flags explained:${NC}"
echo "$task_json" | jq -r '.flags_explained | to_entries[] | " \(.key) : \(.value)"'
echo ""
fi
# Show examples
local examples=$(echo "$task_json" | jq -r '.examples // empty')
if [ -n "$examples" ]; then
echo -e "${YELLOW}Examples:${NC}"
echo "$task_json" | jq -r '.examples[] | " \(.desc)\n → \(.cmd)\n"'
fi
# Show related tasks
local related=$(echo "$task_json" | jq -r '.related // empty')
if [ -n "$related" ] && [ "$related" != "null" ]; then
echo -e "${YELLOW}Related tasks:${NC}"
echo "$task_json" | jq -r '.related[]' | while read -r rel_id; do
local rel_desc=$(jq -r --arg id "$rel_id" '.tasks[] | select(.id == $id) | .description' "$DB_FILE")
if [ -n "$rel_desc" ] && [ "$rel_desc" != "null" ]; then
echo " • $rel_desc (id: $rel_id)"
fi
done
echo ""
fi
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
}
# Show compact task result (for multiple results)
show_task_compact() {
local task_json="$1"
local number="$2"
local desc=$(echo "$task_json" | jq -r '.description')
local cmd=$(echo "$task_json" | jq -r '.command')
echo -e "${CYAN}${number}.${NC} ${GREEN}$desc${NC}"
echo -e " ${MAGENTA}$cmd${NC}"
echo ""
}
# Ask mode - search database and show results
mode_ask() {
local query="$1"
if [ -z "$query" ]; then
echo -e "${RED}Error: No query provided${NC}"
echo "Usage: super-man ask \"your question\""
return 1
fi
echo -e "${CYAN}Searching for:${NC} \"$query\""
# Search database
local results=$(search_by_keywords "$query" 5)
if [ -z "$results" ]; then
echo ""
echo -e "${YELLOW}No matches found for: \"$query\"${NC}"
echo ""
echo "Try:"
echo " • Using different keywords"
echo " • Browse by category: super-man category files"
echo " • List all commands: super-man --list"
return 1
fi
# Count results
local count=$(echo "$results" | jq -s 'length')
if [ "$count" -eq 1 ]; then
# Single result - show full details
show_task "$results"
else
# Multiple results - show compact list
echo ""
echo -e "${GREEN}Found $count matches:${NC}"
echo ""
local i=1
echo "$results" | jq -c '.' | while read -r task; do
show_task_compact "$task" "$i"
i=$((i+1))
done
echo -e "${YELLOW}Tip:${NC} Use more specific keywords to narrow results"
fi
}
# Category mode - browse tasks by category
mode_category() {
local cat_name="$1"
if ! check_jq || ! check_database; then
return 1
fi
if [ -z "$cat_name" ]; then
echo -e "${YELLOW}Available categories:${NC}"
echo ""
jq -r '.categories | to_entries[] | " \(.key) : \(.value.name)"' "$DB_FILE"
echo ""
echo "Usage: super-man category NAME"
return 1
fi
# Get category info
local cat_data=$(jq --arg cat "$cat_name" '.categories[$cat]' "$DB_FILE")
if [ "$cat_data" = "null" ]; then
echo -e "${RED}Category not found:${NC} $cat_name"
echo ""
echo "Available categories:"
jq -r '.categories | keys[]' "$DB_FILE" | sed 's/^/ /'
return 1
fi
local cat_title=$(echo "$cat_data" | jq -r '.name')
local task_ids=$(echo "$cat_data" | jq -r '.tasks[]')
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN}Category:${NC} ${GREEN}$cat_title${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
local i=1
for task_id in $task_ids; do
local task=$(jq --arg id "$task_id" '.tasks[] | select(.id == $id)' "$DB_FILE")
if [ -n "$task" ] && [ "$task" != "null" ]; then
show_task_compact "$task" "$i"
i=$((i+1))
fi
done
}
# AI mode - use NL2SH model for complex queries
mode_ai() {
local user_query="$1"
if [ -z "$user_query" ]; then
echo -e "${RED}Error: No query provided${NC}"
echo "Usage: super-man ai \"your complex query\""
echo ""
echo "Example:"
echo " super-man ai \"find all python files modified in last week\""
return 1
fi
# Check if ollama is available
if ! command -v ollama &> /dev/null; then
echo -e "${RED}Error: Ollama is not installed${NC}"
echo ""
echo "Install Ollama:"
echo " curl -fsSL https://ollama.com/install.sh | sh"
echo ""
echo "For now, try: super-man ask \"$user_query\""
return 1
fi
# Check if NL2SH model is available
if ! ollama list | grep -q "westenfelder/NL2SH"; then
echo -e "${YELLOW}NL2SH model not found. Available models:${NC}"
ollama list
echo ""
echo "Pull NL2SH model with:"
echo " ollama pull westenfelder/NL2SH"
return 1
fi
echo ""
echo -e "${CYAN}🤖 AI Assistant - Using NL2SH Model${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}Query:${NC} $user_query"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${DIM}Generating bash command...${NC}"
echo ""
# Query the NL2SH model
local response=$(ollama run westenfelder/NL2SH "$user_query" 2>/dev/null)
if [ -z "$response" ]; then
echo -e "${RED}Error: No response from AI model${NC}"
echo ""
echo "Try a simpler query or use: super-man ask \"$user_query\""
return 1
fi
# Display the generated command
echo -e "${GREEN}Generated Command:${NC}"
echo -e " ${MAGENTA}$response${NC}"
echo ""
# Extract the base command for explanation
local base_cmd=$(echo "$response" | awk '{print $1}')
# Try to provide explanation
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${YELLOW}Command Info:${NC}"
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
help "$base_cmd" 2>/dev/null | head -3
else
man "$base_cmd" 2>/dev/null | col -b | grep -A 2 "^NAME" | tail -2
fi
echo ""
fi
echo -e "${DIM}Note: Always review AI-generated commands before running them!${NC}"
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
}
# Explain mode - parse and explain a command
mode_explain() {
local cmd_line="$1"
if [ -z "$cmd_line" ]; then
echo -e "${RED}Error: No command provided${NC}"
echo "Usage: super-man explain \"command with flags\""
echo ""
echo "Example:"
echo " super-man explain \"tar -czf archive.tar.gz folder/\""
return 1
fi
# Extract the base command
local base_cmd=$(echo "$cmd_line" | awk '{print $1}')
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}Command:${NC} ${MAGENTA}$cmd_line${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
# Get syntax from man page SYNOPSIS section
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
local raw_syntax=""
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
# For builtins, get the first line of help
raw_syntax=$(help "$base_cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
[ -z "$raw_syntax" ] && raw_syntax="$base_cmd [options]"
else
# For regular commands, extract SYNOPSIS section from man page
local synopsis=$(man "$base_cmd" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | head -15 | tail -n +2 | head -n -1 | tr '\n' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
if [ -n "$synopsis" ]; then
raw_syntax="$synopsis"
else
# Fallback: try to get usage from command itself
local usage=$("$base_cmd" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: //')
if [ -n "$usage" ]; then
raw_syntax="$usage"
else
raw_syntax="$base_cmd [options]"
fi
fi
fi
# Display colorized syntax
if [ -n "$raw_syntax" ]; then
local colored=$(colorize_syntax "$raw_syntax")
printf " %b\n" "$colored"
fi
echo ""
# Add practical example
echo -e "${CYAN}${BOLD}EXAMPLE:${NC}"
local example=$(get_practical_example "$base_cmd")
if [ -n "$example" ]; then
echo -e " ${GREEN}$example${NC}"
fi
echo ""
fi
# Try to find in database
local found_in_db=false
if check_jq && check_database; then
local task=$(jq --arg cmd "$base_cmd" '.tasks[] | select(.command | contains($cmd))' "$DB_FILE" 2>/dev/null | jq -s '.[0]' 2>/dev/null)
if [ -n "$task" ] && [ "$task" != "null" ] && [ "$task" != "" ]; then
local desc=$(echo "$task" | jq -r '.description // empty' 2>/dev/null)
if [ -n "$desc" ]; then
echo -e "${GREEN}Description:${NC} $desc"
echo ""
found_in_db=true
# Show flags if available
local flags=$(echo "$task" | jq -r '.flags_explained // empty' 2>/dev/null)
if [ -n "$flags" ] && [ "$flags" != "null" ]; then
echo -e "${YELLOW}Flag explanations:${NC}"
echo "$task" | jq -r '.flags_explained | to_entries[] | " \(.key) : \(.value)"' 2>/dev/null
echo ""
fi
fi
fi
fi
# Fall back to man page if not found in database
if [ "$found_in_db" = false ]; then
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${YELLOW}Getting help from man page...${NC}"
echo ""
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
help "$base_cmd" 2>/dev/null | head -20
else
man "$base_cmd" 2>/dev/null | col -b | head -30
fi
else
echo -e "${RED}Command not found:${NC} $base_cmd"
fi
fi
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
}
# Get quick syntax for a command
get_quick_syntax() {
local cmd="$1"
local syntax=""
if type "$cmd" 2>/dev/null | grep -q "shell builtin"; then
syntax=$(help "$cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//' | cut -c1-60)
else
# Try to get first line from man page SYNOPSIS
syntax=$(man "$cmd" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | sed -n '2p' | sed 's/^[[:space:]]*//' | cut -c1-60)
if [ -z "$syntax" ]; then
# Fallback to --help
syntax=$("$cmd" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: //' | cut -c1-60)
fi
if [ -z "$syntax" ]; then
syntax="$cmd [options]"
fi
fi
echo "$syntax"
}
# Get practical example for a command
get_practical_example() {
local cmd="$1"
local example=""
case "$cmd" in
# File Operations
ls) example="ls -lah # List all files with human-readable sizes";;
cd) example="cd /path/to/directory # Change to a directory";;
cp) example="cp file.txt backup.txt # Copy a file";;
mv) example="mv old.txt new.txt # Rename or move a file";;
rm) example="rm file.txt # Delete a file";;
mkdir) example="mkdir new_folder # Create a new directory";;
touch) example="touch newfile.txt # Create an empty file";;
cat) example="cat file.txt # Display file contents";;
chmod) example="chmod 755 script.sh # Make a script executable";;
chown) example="chown user:group file # Change file owner";;
ln) example="ln -s /path/to/file link # Create a symbolic link";;
# Text Processing
grep) example="grep 'pattern' file.txt # Search for text in a file";;
sed) example="sed 's/old/new/g' file # Replace text in a file";;
awk) example="awk '{print \$1}' file # Print first column";;
cut) example="cut -d',' -f1 file.csv # Extract first column from CSV";;
sort) example="sort file.txt # Sort lines alphabetically";;
uniq) example="sort file.txt | uniq # Remove duplicate lines";;
wc) example="wc -l file.txt # Count lines in a file";;
diff) example="diff file1.txt file2.txt # Compare two files";;
# File Search
find) example="find . -name '*.txt' # Find all .txt files";;
locate) example="locate filename # Quickly find a file";;
which) example="which python # Show full path of command";;
# Compression
tar) example="tar -czf archive.tar.gz folder/ # Compress a folder";;
gzip) example="gzip file.txt # Compress a file";;
gunzip) example="gunzip file.txt.gz # Decompress a file";;
zip) example="zip archive.zip file1.txt file2.txt # Create zip archive";;
unzip) example="unzip archive.zip # Extract zip archive";;
# Network
ping) example="ping google.com # Test network connectivity";;
curl) example="curl https://example.com # Download webpage";;
wget) example="wget https://example.com/file.zip # Download a file";;
ssh) example="ssh user@hostname # Connect to remote server";;
scp) example="scp file.txt user@host:/path/ # Copy file to remote server";;
# System Info
top) example="top # Monitor system processes";;
ps) example="ps aux # List all running processes";;
df) example="df -h # Show disk space usage";;
du) example="du -sh folder/ # Show folder size";;
free) example="free -h # Show memory usage";;
uname) example="uname -a # Show system information";;
# Process Management
kill) example="kill 1234 # Terminate process with PID 1234";;
pkill) example="pkill firefox # Kill all firefox processes";;
# System Control
sudo) example="sudo apt update # Run command as root";;
systemctl) example="systemctl status nginx # Check service status";;
# Package Management
apt) example="apt install package # Install a package";;
dpkg) example="dpkg -i package.deb # Install .deb package";;
# Misc
echo) example="echo 'Hello World' # Print text to screen";;
date) example="date '+%Y-%m-%d' # Show current date";;
history) example="history # Show command history";;
man) example="man ls # Show manual for 'ls' command";;
*)
# Generic fallback
example="$cmd --help # Show help for $cmd"
;;
esac
echo "$example"
}
# Colorize syntax elements by type for instant recognition
# Returns raw string with ANSI codes (caller should use echo -e or printf %b)
colorize_syntax() {
local syntax="$1"
local colored_syntax="$syntax"
# Color scheme for syntax elements:
# - Command name (first word): Bright cyan + bold
# - [OPTIONS], [OPTION], etc.: Yellow
# - UPPERCASE args (FILE, STRING, SET1, etc.): Magenta
# - Lowercase/optional args: Green
# - ... (ellipsis): Dim white
# Extract command name (first word) - escape special chars for sed
local cmd_word=$(echo "$syntax" | awk '{print $1}' | sed 's/[]\/$*.^[]/\\&/g')
# Start with command in bright cyan + bold
colored_syntax=$(echo "$syntax" | sed "s/^$cmd_word/\\\\033[0;36m\\\\033[1m$cmd_word\\\\033[0m/")
# Color [OPTIONS], [OPTION], [EXPRESSION], etc. in yellow
colored_syntax=$(echo "$colored_syntax" | sed -E "s/\[([A-Z][A-Z_-]*)\]/[\\\\033[0;33m\1\\\\033[0m]/g")
# Color UPPERCASE arguments in magenta (FILE, STRING, PATH, SET1, etc.)
colored_syntax=$(echo "$colored_syntax" | sed -E "s/([[:space:]])([A-Z][A-Z0-9_-]*[A-Z])([[:space:][:punct:]$])/\1\\\\033[0;35m\2\\\\033[0m\3/g")
# Color lowercase optional arguments in green
colored_syntax=$(echo "$colored_syntax" | sed -E "s/\[([a-z][a-z-]*)\]/[\\\\033[0;32m\1\\\\033[0m]/g")
# Color ellipsis (...) in dim white
colored_syntax=$(echo "$colored_syntax" | sed "s/\.\.\./\\\\033[2m...\\\\033[0m/g")
# Return raw string (no echo -e, let caller handle interpretation)
printf "%s" "$colored_syntax"
}
# Extract flags with descriptions from man pages
# New enhanced version that includes what each flag does
extract_all_flags() {
local cmd_name="$1"
local flags_output=""
if type "$cmd_name" 2>/dev/null | grep -q "shell builtin"; then
# Shell builtin - parse the "Options:" block from `help`, formatting each
# entry as "flag — description" to match the man-page output below.
flags_output=$(help "$cmd_name" 2>/dev/null | awk '
/^[[:space:]]+-/ {
line=$0; sub(/^[[:space:]]+/, "", line);
if (match(line, /[ ][ ]+|\t/)) {
flag=substr(line,1,RSTART-1);
desc=substr(line,RSTART+RLENGTH); gsub(/^[[:space:]]+|[[:space:]]+$/,"",desc);
printf "\033[1;33m%s\033[0m \033[2m—\033[0m %s\n", flag, desc;
} else {
printf "\033[1;33m%s\033[0m\n", line;
}
}')
elif command -v "$cmd_name" &> /dev/null; then
# External command - parse the FULL man page. Instead of reading a single
# section (the old behaviour, which truncated curl/find/ps/git/tar/... to a
# handful of flags), classify every section as option-bearing or not, then
# extract every flag with its description across all option sections.
# `col -bx` normalises tabs to spaces so indentation can be compared.
local man_content=$(man "$cmd_name" 2>/dev/null | col -bx)
if [ -n "$man_content" ]; then
flags_output=$(printf '%s\n' "$man_content" | awk '
{ lines[++N]=$0; }
END {
# --- pre-pass: mark which sections actually contain option tags ---
secid=0;
for (i=1; i<=N; i++) {
raw=lines[i];
if (raw ~ /^[A-Z]/) { secid++; sname[secid]=fw(raw); }
lsec[i]=secid;
if (taglike(i)) tcount[secid]++;
}
for (s=1; s<=secid; s++)
secscan[s] = (sname[s] ~ /^OPTION/ || tcount[s] >= 3) ? 1 : 0;
# --- main pass: emit "flag — description" for every option ---
flag=""; desc=""; curind=-1;
for (i=1; i<=N; i++) {
raw=lines[i];
if (raw ~ /^[[:space:]]*$/) continue; # blank line
if (raw ~ /^[A-Z]/) { flush(); curind=-1; continue; } # section header
if (!secscan[lsec[i]]) continue; # skip prose sections
ind = match(raw, /[^ ]/) - 1;
t = raw; sub(/^[ ]+/, "", t);
if (t ~ /^-/) {
if (ncommaopt(t) >= 3) { # comma-list prose, not a tag
if (flag != "" && ind > curind) adddesc(t);
continue;
}
if (flag != "" && ind > curind) { adddesc(t); continue; } # wrapped desc line
if (flag != "" && desc == "" && ind == curind) { # stacked alias
if (match(t, / +/)) { flag=flag", "substr(t,1,RSTART-1); desc=trim(substr(t,RSTART+RLENGTH)); }
else { flag=flag", "t; }
continue;
}
if (match(t, / +/)) { # description on same line
start_flag(substr(t,1,RSTART-1), trim(substr(t,RSTART+RLENGTH)), ind); continue;
}
j=i+1; while (j<=N && lines[j] ~ /^[[:space:]]*$/) j++; # look ahead
deeper = (j<=N && (match(lines[j],/[^ ]/)-1) > ind && trim(lines[j]) !~ /^-/);
if (deeper) { start_flag(t, "", ind); continue; } # description on next line(s)
split_tag(t, ind); continue; # single-space inline desc
}
if (flag != "") adddesc(t); # description continuation
}
flush();
}
function fw(s, w){ w=s; sub(/[[:space:]].*$/, "", w); return w; }
function ncommaopt(s, c,tmp){ tmp=s; c=gsub(/, +--?[A-Za-z]/, "", tmp); return c; }
function trim(s){ gsub(/^[[:space:]]+|[[:space:]]+$/, "", s); return s; }
function adddesc(s){ desc = (desc=="") ? trim(s) : desc" "trim(s); }
function start_flag(sig, d, k){ flush(); flag=sig; desc=d; curind=k; }
function taglike(i, raw,t,ind,j,nind){
raw=lines[i];
if (raw !~ /^[ ]+-/) return 0;
if (raw ~ /^[ ]+-{1,2}[A-Za-z0-9?][A-Za-z0-9?+._=-]* /) return 1;
ind = match(raw,/[^ ]/)-1;
j=i+1; while (j<=N && lines[j] ~ /^[[:space:]]*$/) j++;
if (j<=N) { nind=match(lines[j],/[^ ]/)-1; if (nind>ind && trim(lines[j]) !~ /^-/) return 1; }
return 0;
}
function split_tag(s, k, n,toks,p,tk,optend,sig,d){
n=split(s,toks,/[ ]+/); optend=0;
for (p=1;p<=n;p++){ tk=toks[p];
if (tk ~ /^-/) { optend=p; continue; }
if (tk ~ /^[A-Z0-9][A-Z0-9_<>|.=-]*[,]?$/) { optend=p; continue; }
if (tk ~ /^[<\[]/) { optend=p; continue; }
break;
}
if (optend==0) optend=1;
sig=""; for(p=1;p<=optend;p++) sig=(sig=="")?toks[p]:sig" "toks[p];
d=""; for(p=optend+1;p<=n;p++) d=(d=="")?toks[p]:d" "toks[p];
start_flag(sig, d, k);
}
function flush(){
if (flag != "") {
desc=trim(desc);
if (desc != "") printf "\033[1;33m%s\033[0m \033[2m—\033[0m %s\n", flag, desc;
else printf "\033[1;33m%s\033[0m\n", flag;
}
flag=""; desc="";
}
')
fi
# Fallback: nothing parsed (e.g. no man page) - grab raw flag lines
if [ -z "$flags_output" ]; then
flags_output=$(printf '%s\n' "$man_content" | grep -E '^[[:space:]]*-' | head -100)
fi
fi
echo "$flags_output"
}
# Interactive flag browser with fuzzy search
# Now includes syntax and description in the fzf interface itself
browse_flags_fuzzy() {
local cmd_name="$1"
local cmd_syntax="$2"
local cmd_desc="$3"
# Extract all flags silently
local all_flags=$(extract_all_flags "$cmd_name")
if [ -z "$all_flags" ]; then
echo ""
echo -e "${RED}No flags found for: $cmd_name${NC}"
echo ""
return 1
fi
# Count flags
local flag_count=$(echo "$all_flags" | wc -l)
# Colorize the syntax for the header
# colorize_syntax now returns raw \033 codes that printf %b will interpret
local colored_syntax=$(colorize_syntax "$cmd_syntax")
# Create multi-line header with command info using printf %b for proper escape code interpretation
# Use actual ANSI codes for command and description, use %b for colored syntax
local header
printf -v header "╔═══════════════════════════════════════════════════════════════════════════════╗\n║ Command: \033[0;36m\033[1m%s\033[0m\n║ Syntax: %b\n║ Desc: \033[0;32m%s\033[0m\n╚═══════════════════════════════════════════════════════════════════════════════╝\n🔍 Fuzzy search %d flags | Multi-select: Ctrl-A/D | Esc: Back to menu" \
"$cmd_name" \
"$colored_syntax" \
"$cmd_desc" \
"$flag_count"
# Show in fzf with integrated command info and enhanced bottom display
echo "$all_flags" | fzf \
--height 95% \
--border=none \
--header "$header" \
--preview 'line=$(echo {} | sed -E "s/\x1b\[[0-9;]*m//g");
if echo "$line" | grep -qF " — "; then
flag=$(echo "$line" | awk -F " — " "{print \$1}" | sed -e "s/^[[:space:]]*//" -e "s/[[:space:]]*$//");
desc=$(echo "$line" | awk -F " — " "{for(i=2;i<=NF;i++) printf \"%s \", \$i; print \"\"}" | sed -e "s/^[[:space:]]*//" -e "s/[[:space:]]*$//");
else
flag=$(echo "$line" | sed -e "s/^[[:space:]]*//" -e "s/[[:space:]]*$//");
desc="(No description available)";
fi;
if [ -z "$desc" ] || [ "$desc" = "$flag" ]; then
desc="(No description available)";
fi;
echo "┌─────────────────────────────────────────────────────────────────────────────┐";
printf "│ 🏴 Flag: \033[1;36m%-66s\033[0m│\n" "$flag";
echo "├─────────────────────────────────────────────────────────────────────────────┤";
echo "│ 📝 Description: │";
if [ "$desc" != "(No description available)" ]; then
echo "$desc" | fold -s -w 72 | while IFS= read -r dline; do
printf "│ \033[0;32m%-72s\033[0m│\n" "$dline";
done;
else
printf "│ \033[2;37m%-72s\033[0m│\n" "$desc";
fi;
echo "└─────────────────────────────────────────────────────────────────────────────┘"' \
--preview-window=down:8:wrap \
--prompt "⚡ Search: " \
--multi \
--bind 'ctrl-a:select-all' \
--bind 'ctrl-d:deselect-all' \
--ansi \
--color=header:bold:cyan
return 0
}
show_flags() {
CMD_NAME=$1
CMD_SYNTAX=$2
CMD_DESC=$3
# Extract syntax if not provided
if [ -z "$CMD_SYNTAX" ]; then
if type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
# For builtins, get the first line of help
CMD_SYNTAX=$(help "$CMD_NAME" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
[ -z "$CMD_SYNTAX" ] && CMD_SYNTAX="$CMD_NAME [options]"
else
# For regular commands, extract SYNOPSIS section from man page
local synopsis=$(man "$CMD_NAME" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | head -15 | tail -n +2 | head -n -1 | tr '\n' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
if [ -n "$synopsis" ]; then
CMD_SYNTAX="$synopsis"
else
# Fallback: try to get usage from command itself
local usage=$("$CMD_NAME" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: //')
if [ -n "$usage" ]; then
CMD_SYNTAX="$usage"
else
CMD_SYNTAX="$CMD_NAME [options]"
fi
fi
fi
fi
# Default description if not provided
[ -z "$CMD_DESC" ] && CMD_DESC="No description available"
# Directly open fuzzy flag browser with integrated command info (no wasted space)
browse_flags_fuzzy "$CMD_NAME" "$CMD_SYNTAX" "$CMD_DESC"
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
list_commands() {
echo "Available commands:"
echo ""
for cmd in "${COMMANDS[@]}"; do
CMD_NAME=${cmd%%:*}
REST=${cmd#*:}
# Extract description (before pipe) and syntax (after pipe)
if [[ "$REST" =~ (.+)\ \|\ (.+) ]]; then
DESC="${BASH_REMATCH[1]}"
SYNTAX="${BASH_REMATCH[2]}"
printf " %-10s - %-40s | %s\n" "$CMD_NAME" "$DESC" "$SYNTAX"
else
printf " %-10s - %s\n" "$CMD_NAME" "$REST"
fi
done
}
get_command_description() {
local search_cmd=$1
for cmd in "${COMMANDS[@]}"; do
CMD_NAME=${cmd%%:*}
REST=${cmd#*:}
if [ "$CMD_NAME" = "$search_cmd" ]; then
# Extract just the description part (before the pipe)
if [[ "$REST" =~ (.+)\ \|\ (.+) ]]; then
echo "${BASH_REMATCH[1]}"
else
echo "$REST"
fi
return 0
fi
done
echo "Command help"
}
# Command definitions for interactive mode (format: "cmd:Description | Syntax")
COMMANDS=(
# File Operations
"ls:List directory contents | ls [OPTION]... [FILE]..."
"cd:Change the current directory | cd [DIRECTORY]"
"pwd:Print the name of the current working directory | pwd [OPTION]"
"cp:Copy files and directories | cp [OPTION]... SOURCE DEST"
"mv:Move or rename files and directories | mv [OPTION]... SOURCE DEST"
"rm:Remove files or directories | rm [OPTION]... FILE..."
"mkdir:Create a new directory | mkdir [OPTION]... DIRECTORY..."
"rmdir:Remove an empty directory | rmdir [OPTION]... DIRECTORY..."
"touch:Create empty file or update timestamp | touch [OPTION]... FILE..."
"cat:Concatenate and display files | cat [OPTION]... [FILE]..."
"less:View file contents with pagination | less [OPTION]... [FILE]..."
"head:Display first lines of a file | head [OPTION]... [FILE]..."
"tail:Display last lines of a file | tail [OPTION]... [FILE]..."
"chmod:Change file permissions | chmod [OPTION] MODE FILE..."
"chown:Change file owner | chown [OPTION]... OWNER[:GROUP] FILE..."
"ln:Create links between files | ln [OPTION]... TARGET LINK_NAME"
"file:Determine file type | file [OPTION]... FILE..."
"stat:Display file status | stat [OPTION]... FILE..."
# Text Processing
"grep:Search for patterns in files | grep [OPTION]... PATTERN [FILE]..."
"sed:Stream editor for text manipulation | sed [OPTION]... SCRIPT [FILE]..."
"awk:Pattern scanning and text processing | awk [OPTION]... PROGRAM [FILE]..."
"cut:Remove sections from lines of files | cut [OPTION]... [FILE]..."
"sort:Sort lines of text | sort [OPTION]... [FILE]..."
"uniq:Report or filter out repeated lines | uniq [OPTION]... [FILE]..."
"tr:Translate or delete characters | tr [OPTION]... SET1 [SET2]"
"wc:Count lines, words, and characters | wc [OPTION]... [FILE]..."
"diff:Compare files line by line | diff [OPTION]... FILE1 FILE2"
# File Search
"find:Search for files in directory hierarchy | find [PATH] [OPTIONS] [EXPRESSION]"
"locate:Find files by name quickly | locate [OPTION]... PATTERN..."
"which:Show full path of commands | which [OPTION]... COMMAND..."
"whereis:Locate binary, source, and manual pages | whereis [OPTION]... COMMAND..."
# Compression
"tar:Archive files | tar [OPTION]... [FILE]..."
"gzip:Compress files | gzip [OPTION]... [FILE]..."
"gunzip:Decompress gzip files | gunzip [OPTION]... [FILE]..."
"zip:Package and compress files | zip [OPTION]... ZIPFILE FILES..."
"unzip:Extract compressed zip files | unzip [OPTION]... FILE..."
# Network
"ping:Test network connectivity | ping [OPTION]... HOST"
"curl:Transfer data from URLs | curl [OPTION]... URL..."
"wget:Download files from the web | wget [OPTION]... URL..."
"ssh:Secure shell remote login | ssh [OPTION]... USER@HOST [COMMAND]"
"scp:Secure copy files between hosts | scp [OPTION]... SOURCE DEST"
"netstat:Network statistics | netstat [OPTION]..."
"ss:Socket statistics | ss [OPTION]..."
"ip:Show/manipulate routing and devices | ip [OPTION]... OBJECT {COMMAND}"
"ifconfig:Configure network interfaces | ifconfig [INTERFACE] [OPTIONS]"
# System Info
"top:Display system processes | top [OPTION]..."
"htop:Interactive process viewer | htop [OPTION]..."
"ps:Report process status | ps [OPTION]..."
"df:Show disk space usage | df [OPTION]... [FILE]..."
"du:Estimate file space usage | du [OPTION]... [FILE]..."
"free:Display memory usage | free [OPTION]..."
"uname:Print system information | uname [OPTION]..."
"uptime:Show how long system has been running | uptime [OPTION]..."
# Process Management
"kill:Send signal to a process | kill [OPTION]... PID..."
"pkill:Kill processes by name | pkill [OPTION]... PATTERN"
"killall:Kill processes by name | killall [OPTION]... NAME..."
"bg:Put job in background | bg [JOB_SPEC]"
"fg:Bring job to foreground | fg [JOB_SPEC]"
"jobs:List active jobs | jobs [OPTION]..."
# System Control
"sudo:Execute command as another user | sudo [OPTION]... COMMAND"
"systemctl:Control systemd services | systemctl [OPTION]... COMMAND"
"service:Control system services | service COMMAND [OPTIONS]"
"reboot:Reboot the system | reboot [OPTION]..."
"shutdown:Shut down the system | shutdown [OPTION]... [TIME]"
# Package Management
"apt:Package manager (Debian/Ubuntu) | apt [OPTION]... COMMAND"
"apt-get:APT package handling utility | apt-get [OPTION]... COMMAND"
"dpkg:Debian package manager | dpkg [OPTION]... ACTION"
"snap:Snap package manager | snap [OPTION]... COMMAND"
# User Management
"useradd:Create new user | useradd [OPTION]... LOGIN"
"userdel:Delete user account | userdel [OPTION]... LOGIN"
"passwd:Change user password | passwd [OPTION]... [USER]"
"su:Switch user | su [OPTION]... [USER]"
"whoami:Print current username | whoami"
"w:Show who is logged in | w [OPTION]... [USER]"
"who:Show who is logged in | who [OPTION]..."
# Misc Utilities
"echo:Display a line of text | echo [OPTION]... [STRING]..."
"date:Display or set system date and time | date [OPTION]... [+FORMAT]"
"cal:Display calendar | cal [OPTION]... [[MONTH] YEAR]"
"history:Show command history | history [n]"
"alias:Create command aliases | alias [NAME[=VALUE]...]"
"export:Set environment variables | export [NAME[=VALUE]...]"
"env:Display environment variables | env [OPTION]... [NAME=VALUE]... [COMMAND]"
"man:Display manual pages | man [OPTION]... [SECTION] PAGE"
"watch:Execute command periodically | watch [OPTION]... COMMAND"
# File Operations (extended)
"dd:Convert and copy a file (block-level) | dd if=SOURCE of=DEST [OPERAND]..."
"shred:Overwrite a file to securely delete it | shred [OPTION]... FILE..."
"truncate:Shrink or extend file size | truncate [OPTION]... -s SIZE FILE..."
"mktemp:Create a temporary file or directory | mktemp [OPTION]... [TEMPLATE]"
"mkfifo:Create a named pipe (FIFO) | mkfifo [OPTION]... NAME..."
"install:Copy files and set attributes | install [OPTION]... SOURCE DEST"
"basename:Strip directory and suffix from a path | basename NAME [SUFFIX]"
"dirname:Strip last component from a path | dirname [OPTION] NAME..."
"realpath:Print the resolved absolute path | realpath [OPTION]... FILE..."
"readlink:Print resolved symbolic link target | readlink [OPTION]... FILE..."
"tree:List directory contents as a tree | tree [OPTION]... [DIRECTORY]"
"rsync:Fast incremental file transfer/sync | rsync [OPTION]... SOURCE DEST"
"pushd:Push directory onto the stack | pushd [DIRECTORY]"
"popd:Pop directory off the stack | popd [+N|-N]"
"rename:Rename multiple files via pattern | rename [OPTION]... EXPRESSION FILE..."
# Text Processing (extended)
"tee:Read stdin and write to files and stdout | tee [OPTION]... [FILE]..."
"tac:Concatenate and print files in reverse | tac [OPTION]... [FILE]..."
"nl:Number lines of files | nl [OPTION]... [FILE]..."
"fold:Wrap lines to a fixed width | fold [OPTION]... [FILE]..."
"fmt:Reformat paragraph text | fmt [OPTION]... [FILE]..."
"paste:Merge lines of files side by side | paste [OPTION]... [FILE]..."
"join:Join lines of two files on a field | join [OPTION]... FILE1 FILE2"
"comm:Compare two sorted files line by line | comm [OPTION]... FILE1 FILE2"
"column:Format input into columns | column [OPTION]... [FILE]..."
"expand:Convert tabs to spaces | expand [OPTION]... [FILE]..."
"unexpand:Convert spaces to tabs | unexpand [OPTION]... [FILE]..."
"rev:Reverse characters in each line | rev [OPTION]... [FILE]..."
"split:Split a file into pieces | split [OPTION]... [FILE [PREFIX]]"
"csplit:Split a file by context lines | csplit [OPTION]... FILE PATTERN..."
"od:Dump files in octal/other formats | od [OPTION]... [FILE]..."
"hexdump:Display file contents in hex | hexdump [OPTION]... [FILE]..."
"xxd:Make a hexdump or reverse it | xxd [OPTION]... [FILE]"
"strings:Print printable strings in a file | strings [OPTION]... [FILE]..."
"iconv:Convert text between encodings | iconv [OPTION]... [FILE]..."
"jq:Process and query JSON data | jq [OPTION]... FILTER [FILE]..."
"pr:Paginate/columnate files for printing | pr [OPTION]... [FILE]..."
"printf:Format and print data | printf FORMAT [ARGUMENT]..."
# File Search (extended)
"type:Describe how a command name is resolved | type [OPTION]... NAME..."
"command:Run a command bypassing aliases | command [OPTION]... NAME [ARG]..."
"apropos:Search the manual page names | apropos [OPTION]... KEYWORD..."
"updatedb:Update the locate file database | updatedb [OPTION]..."
"xargs:Build and execute commands from stdin | xargs [OPTION]... [COMMAND]"
# Compression (extended)
"bzip2:Compress files with bzip2 | bzip2 [OPTION]... [FILE]..."
"bunzip2:Decompress bzip2 files | bunzip2 [OPTION]... [FILE]..."
"xz:Compress files with xz/LZMA | xz [OPTION]... [FILE]..."
"unxz:Decompress xz files | unxz [OPTION]... [FILE]..."
"zcat:Print gzip-compressed files | zcat [OPTION]... [FILE]..."
"zstd:Compress files with Zstandard | zstd [OPTION]... [FILE]..."
"7z:7-Zip archive manager | 7z COMMAND [OPTION]... ARCHIVE [FILE]..."
"cpio:Copy files to/from archives | cpio [OPTION]..."
# Network (extended)
"dig:DNS lookup utility | dig [OPTION]... [@SERVER] NAME [TYPE]"
"nslookup:Query DNS records interactively | nslookup [OPTION]... [HOST] [SERVER]"
"host:DNS lookup utility (simple) | host [OPTION]... NAME [SERVER]"
"traceroute:Trace route packets take to a host | traceroute [OPTION]... HOST"
"mtr:Combined traceroute and ping tool | mtr [OPTION]... HOST"
"nc:Netcat - read/write network connections | nc [OPTION]... [HOST] [PORT]"
"telnet:Connect to a host over Telnet | telnet [OPTION]... [HOST [PORT]]"
"sftp:Secure file transfer over SSH | sftp [OPTION]... USER@HOST"
"nmap:Network exploration and port scanner | nmap [OPTION]... TARGET..."
"arp:Show/manipulate the ARP cache | arp [OPTION]..."
"route:Show/manipulate the routing table | route [OPTION]..."
"tcpdump:Capture and analyze network traffic | tcpdump [OPTION]... [EXPRESSION]"
"iptables:Configure IPv4 packet filtering | iptables [OPTION]... CHAIN [RULE]"
"ethtool:Query/control network driver settings | ethtool [OPTION]... DEVICE"
"nmcli:Control NetworkManager from the CLI | nmcli [OPTION]... OBJECT {COMMAND}"
"hostname:Show or set the system hostname | hostname [OPTION]... [NAME]"
"whois:Look up domain/IP registration info | whois [OPTION]... OBJECT"
"ssh-keygen:Generate/manage SSH keys | ssh-keygen [OPTION]..."
# System Info (extended)
"lscpu:Display CPU architecture information | lscpu [OPTION]..."
"lsblk:List block devices | lsblk [OPTION]... [DEVICE]..."
"lsusb:List USB devices | lsusb [OPTION]..."
"lspci:List PCI devices | lspci [OPTION]..."
"lsmod:Show status of kernel modules | lsmod"
"lsof:List open files and the processes using them | lsof [OPTION]..."
"vmstat:Report virtual memory statistics | vmstat [OPTION]... [DELAY [COUNT]]"
"iostat:Report CPU and I/O statistics | iostat [OPTION]... [DELAY [COUNT]]"
"dmidecode:Dump DMI/SMBIOS hardware table | dmidecode [OPTION]..."
"dmesg:Print kernel ring buffer messages | dmesg [OPTION]..."
"journalctl:Query the systemd journal | journalctl [OPTION]..."
"timedatectl:Query/change system clock settings | timedatectl [OPTION]... [COMMAND]"
"hostnamectl:Query/change system hostname | hostnamectl [OPTION]... [COMMAND]"
"nproc:Print the number of processing units | nproc [OPTION]..."
"sysctl:Read/modify kernel parameters | sysctl [OPTION]... [VARIABLE[=VALUE]]..."
# Process Management (extended)
"nice:Run a program with modified priority | nice [OPTION]... [COMMAND]"
"renice:Alter priority of running processes | renice [OPTION]... PRIORITY PID..."
"nohup:Run a command immune to hangups | nohup COMMAND [ARG]..."
"disown:Remove jobs from the shell's job table | disown [OPTION]... [JOB]..."
"pgrep:Find process IDs by name/attributes | pgrep [OPTION]... PATTERN"
"pidof:Find the PID of a running program | pidof [OPTION]... PROGRAM..."
"fuser:Identify processes using files/sockets | fuser [OPTION]... NAME..."
"time:Time a command's execution | time [OPTION]... COMMAND"
"timeout:Run a command with a time limit | timeout [OPTION]... DURATION COMMAND"
"strace:Trace system calls and signals | strace [OPTION]... COMMAND"
"ltrace:Trace library calls | ltrace [OPTION]... COMMAND"
"at:Schedule a command for later | at [OPTION]... TIME"
"crontab:Manage cron job schedules | crontab [OPTION]... [FILE]"
"sleep:Pause for a specified duration | sleep NUMBER[SUFFIX]..."
# System Control / Disk (extended)
"mount:Mount a filesystem | mount [OPTION]... DEVICE DIR"
"umount:Unmount a filesystem | umount [OPTION]... {DIR|DEVICE}"
"fdisk:Manipulate disk partition tables | fdisk [OPTION]... DEVICE"
"parted:Partition manipulation program | parted [OPTION]... [DEVICE [COMMAND]]"
"mkfs:Build a Linux filesystem | mkfs [OPTION]... DEVICE"
"fsck:Check and repair a filesystem | fsck [OPTION]... [DEVICE]..."
"swapon:Enable devices/files for swapping | swapon [OPTION]... [DEVICE]"
"swapoff:Disable swapping on devices/files | swapoff [OPTION]... [DEVICE]"
"blkid:Locate/print block device attributes | blkid [OPTION]... [DEVICE]..."
"sync:Flush filesystem buffers to disk | sync [OPTION]... [FILE]..."
"modprobe:Add or remove kernel modules | modprobe [OPTION]... MODULE"
"poweroff:Power off the machine | poweroff [OPTION]..."
"ncdu:NCurses disk usage analyzer | ncdu [OPTION]... [DIRECTORY]"
# Package Management (extended)
"yum:Package manager (RHEL/CentOS) | yum [OPTION]... COMMAND"
"dnf:Package manager (Fedora/RHEL) | dnf [OPTION]... COMMAND"
"rpm:RPM package manager | rpm [OPTION]... PACKAGE..."
"pacman:Package manager (Arch Linux) | pacman [OPTION]... [TARGET]..."
"zypper:Package manager (openSUSE) | zypper [OPTION]... COMMAND"
"flatpak:Manage Flatpak applications | flatpak [OPTION]... COMMAND"
"pip:Python package installer | pip [OPTION]... COMMAND"
"npm:Node.js package manager | npm [OPTION]... COMMAND"
"apt-cache:Query the APT package cache | apt-cache [OPTION]... COMMAND"
# User & Group Management (extended)
"usermod:Modify a user account | usermod [OPTION]... LOGIN"
"groupadd:Create a new group | groupadd [OPTION]... GROUP"
"groupdel:Delete a group | groupdel [OPTION]... GROUP"
"groupmod:Modify a group | groupmod [OPTION]... GROUP"
"groups:Show groups a user belongs to | groups [USER]..."
"id:Print user and group IDs | id [OPTION]... [USER]"
"chage:Change user password expiry info | chage [OPTION]... LOGIN"
"newgrp:Log in to a new group | newgrp [GROUP]"
"last:Show last logged-in users | last [OPTION]... [NAME]..."
"logname:Print the user's login name | logname"
# Permissions & Security (extended)
"umask:Set default file permission mask | umask [OPTION]... [MODE]"
"chgrp:Change group ownership | chgrp [OPTION]... GROUP FILE..."
"setfacl:Set file access control lists | setfacl [OPTION]... FILE..."
"getfacl:Get file access control lists | getfacl [OPTION]... FILE..."
"chattr:Change file attributes on Linux FS | chattr [OPTION]... [+-=MODE] FILE..."
"lsattr:List file attributes on Linux FS | lsattr [OPTION]... [FILE]..."
"gpg:Encrypt, sign, and manage keys | gpg [OPTION]... [FILE]..."
"openssl:Cryptography and TLS toolkit | openssl COMMAND [OPTION]..."
"md5sum:Compute/check MD5 checksums | md5sum [OPTION]... [FILE]..."
"sha256sum:Compute/check SHA-256 checksums | sha256sum [OPTION]... [FILE]..."
"sha1sum:Compute/check SHA-1 checksums | sha1sum [OPTION]... [FILE]..."
"cksum:Compute CRC checksum and byte count | cksum [OPTION]... [FILE]..."
"base64:Encode/decode data in base64 | base64 [OPTION]... [FILE]"
# Shell & Scripting (extended)
"read:Read a line into shell variables | read [OPTION]... [NAME]..."
"test:Evaluate a conditional expression | test EXPRESSION"
"eval:Evaluate arguments as a command | eval [ARG]..."
"exec:Replace the shell with a command | exec [COMMAND [ARG]...]"
"source:Execute commands from a file | source FILE [ARG]..."
"set:Set shell options and positional params | set [OPTION]... [ARG]..."
"unset:Unset shell variables or functions | unset [OPTION]... NAME..."
"shift:Shift positional parameters | shift [N]"
"getopts:Parse positional option arguments | getopts OPTSTRING NAME [ARG]..."
"declare:Declare variables and attributes | declare [OPTION]... [NAME[=VALUE]]..."
"trap:Run commands on signals/events | trap [OPTION]... [ARG] [SIGNAL]..."
"ulimit:Set/show user resource limits | ulimit [OPTION]... [LIMIT]"
"hash:Remember/show command locations | hash [OPTION]... [NAME]..."
"true:Return a successful exit status | true"
"false:Return an unsuccessful exit status | false"
"seq:Print a sequence of numbers | seq [OPTION]... [FIRST [INC]] LAST"
"yes:Repeatedly print a string | yes [STRING]..."
"expr:Evaluate expressions | expr EXPRESSION"
"bc:Arbitrary-precision calculator | bc [OPTION]... [FILE]..."
"shuf:Generate random permutations | shuf [OPTION]... [FILE]"
"factor:Print the prime factors of numbers | factor [NUMBER]..."
# Terminal & Misc (extended)
"clear:Clear the terminal screen | clear [OPTION]..."
"reset:Reset the terminal to defaults | reset [OPTION]..."
"tput:Query/set terminal capabilities | tput [OPTION]... CAPNAME [ARG]..."
"stty:Change/print terminal line settings | stty [OPTION]... [SETTING]..."
"tty:Print the terminal connected to stdin | tty [OPTION]..."
"screen:Terminal multiplexer | screen [OPTION]... [COMMAND]"
"tmux:Terminal multiplexer | tmux [OPTION]... [COMMAND]"
"nano:Simple terminal text editor | nano [OPTION]... [FILE]..."
"vim:Vi IMproved text editor | vim [OPTION]... [FILE]..."
"xdg-open:Open a file/URL in the default app | xdg-open {FILE|URL}"
"notify-send:Send a desktop notification | notify-send [OPTION]... TITLE [BODY]"
"lolcat:Rainbow-colorize text output | lolcat [OPTION]... [FILE]..."
"figlet:Render text as ASCII art banners | figlet [OPTION]... [MESSAGE]"
)
# Parse arguments
MODE=""
QUERY=""
INTERACTIVE=false
COMMAND=""
FILTER=""
if [ $# -eq 0 ]; then
INTERACTIVE=true
else
case "$1" in
-h|--help)
show_help
exit 0
;;
-l|--list)
list_commands
exit 0
;;
-i|--interactive)
INTERACTIVE=true
;;
ask)
MODE="ask"
QUERY="$2"
;;
task)
MODE="task"
QUERY="$2"
;;
explain)
MODE="explain"
QUERY="$2"
;;
category)
MODE="category"
QUERY="$2"
;;
ai)
MODE="ai"
QUERY="$2"
;;
-*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
*)
COMMAND=$1
FILTER=$2
;;
esac
fi
# Handle new modes
if [ -n "$MODE" ]; then
case "$MODE" in
ask|task)
mode_ask "$QUERY"
exit $?
;;
explain)
mode_explain "$QUERY"
exit $?
;;
category)
mode_category "$QUERY"
exit $?
;;
ai)
mode_ai "$QUERY"
exit $?
;;
esac
fi
# Format commands for display - called once and cached for performance
format_commands_for_display() {
# Get terminal width for proper alignment
local term_width=$(tput cols 2>/dev/null || echo 120)
# Conservative approach to prevent ANY clipping
# Account for fzf border, padding, and any other overhead
local safe_width=$((term_width - 6)) # 2 for border + 4 buffer for safety
# Fixed 2-column layout: description on left, syntax on right
# Prioritize syntax visibility - give it 50% of space
local syntax_col_width=$((safe_width * 50 / 100))
[ $syntax_col_width -lt 30 ] && syntax_col_width=30
# Description gets the rest (with separator space)
local desc_col_width=$((safe_width - syntax_col_width - 8))
[ $desc_col_width -lt 25 ] && desc_col_width=25
# Format all commands with fixed column widths
FORMATTED_COMMANDS=()
for cmd in "${COMMANDS[@]}"; do
local cmd_name=${cmd%%:*}
local rest=${cmd#*:}
# Extract description and syntax
local desc_part syntax_part
if [[ "$rest" =~ (.+)\ \|\ (.+) ]]; then
desc_part="${BASH_REMATCH[1]}"
syntax_part="${BASH_REMATCH[2]}"
else
desc_part="$rest"
syntax_part="$cmd_name [options]"
fi
# Truncate description to fit column (cmd: + desc)
local cmd_desc_str="${cmd_name}: ${desc_part}"
local max_cmd_desc_len=$desc_col_width
if [ ${#cmd_desc_str} -gt $max_cmd_desc_len ]; then
# Truncate description, keeping command name intact
local desc_available=$((max_cmd_desc_len - ${#cmd_name} - 5)) # 2 for ": ", 3 for "..."
if [ $desc_available -gt 10 ]; then
desc_part="${desc_part:0:$desc_available}..."
else
desc_part="..."
fi
cmd_desc_str="${cmd_name}: ${desc_part}"
fi
# Truncate syntax to fit its column
local plain_syntax="$syntax_part"
if [ ${#syntax_part} -gt $syntax_col_width ]; then
plain_syntax="${syntax_part:0:$((syntax_col_width-3))}..."
fi
# Colorize syntax after truncation
local colorized_syntax=$(colorize_syntax "$plain_syntax")
# Calculate padding to place syntax in its column
local left_len=${#cmd_desc_str}
local syntax_len=${#plain_syntax}
# Place syntax at fixed position (right-aligned within safe width)
local padding=$((safe_width - left_len - syntax_len - 2))
[ $padding -lt 3 ] && padding=3
local spaces=$(printf '%*s' "$padding" '')
# Format line with colors
local formatted_line="\033[0;36m${cmd_name}\033[0m: ${desc_part}${spaces}${colorized_syntax}"
FORMATTED_COMMANDS+=("$formatted_line")
done
}
# Interactive mode with fzf (loopable)
if [ "$INTERACTIVE" = true ]; then
# Check if fzf is available
if ! command -v fzf &> /dev/null; then
echo "Error: fzf is not installed"
echo "Install it with: sudo apt install fzf"
echo ""
echo "Or use direct mode: super-man COMMAND [FILTER]"
exit 1
fi
# Format commands once before entering loop (performance optimization)
format_commands_for_display
# Set up window resize detection to trigger reformatting
NEEDS_REFORMAT=false
trap 'NEEDS_REFORMAT=true' WINCH
# Main interactive loop
while true; do
# Reformat commands if terminal was resized
if [ "$NEEDS_REFORMAT" = true ]; then
format_commands_for_display
NEEDS_REFORMAT=false
fi
# Show in fzf with formatted display
SELECTED=$(printf "%b\n" "${FORMATTED_COMMANDS[@]}" | fzf \
--height 60% \
--border \
--header "Select a command (Esc to exit) | Colorized: cmd=[cyan] [OPTIONS]=[yellow] FILE=[magenta]" \
--prompt "🔍 Search: " \
--preview-window=hidden \
--ansi)
if [ -z "$SELECTED" ]; then
# User pressed Esc or cancelled
echo ""
echo -e "${YELLOW}Exiting Super Man. Happy coding! 👋${NC}"
exit 0
fi
# Extract command name from selection (strip color codes)
CMD=$(echo "$SELECTED" | sed 's/\x1b\[[0-9;]*m//g' | awk -F: '{print $1}' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
# Find the original command entry to get full details
for cmd in "${COMMANDS[@]}"; do
CMD_NAME=${cmd%%:*}
if [ "$CMD_NAME" = "$CMD" ]; then
REST=${cmd#*:}
# Extract description and syntax
if [[ "$REST" =~ (.+)\ \|\ (.+) ]]; then
DESC="${BASH_REMATCH[1]}"
SYNTAX="${BASH_REMATCH[2]}"
else
DESC="$REST"
SYNTAX="$CMD [options]"
fi
break
fi
done
# Directly show fuzzy flag browser with integrated command info (no wasted space)
clear
show_flags "$CMD" "$SYNTAX" "$DESC"
# Esc in the flag browser returns straight to the home menu (no intermediary step)
clear
done
# Direct mode
elif [ -n "$COMMAND" ]; then
# Validate command exists in our list
FOUND=false
DESC_PART=""
SYNTAX_PART=""
for cmd in "${COMMANDS[@]}"; do
CMD_NAME=${cmd%%:*}
if [ "$CMD_NAME" = "$COMMAND" ]; then
FOUND=true
REST=${cmd#*:}
# Extract description and syntax
if [[ "$REST" =~ (.+)\ \|\ (.+) ]]; then
DESC_PART="${BASH_REMATCH[1]}"
SYNTAX_PART="${BASH_REMATCH[2]}"
else
DESC_PART="$REST"
SYNTAX_PART="$COMMAND [options]"
fi
break
fi
done
if [ "$FOUND" = false ]; then
echo "Warning: '$COMMAND' is not in the predefined list"
echo "Available commands:"
list_commands
echo ""
echo "Attempting to show flags anyway..."
echo ""
DESC_PART=$(get_command_description "$COMMAND")
SYNTAX_PART="$COMMAND [options]"
fi
# Show command info in enhanced format
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}Command:${NC} ${MAGENTA}$COMMAND${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
echo -e " ${GREEN}$SYNTAX_PART${NC}"
echo ""
echo -e "${GREEN}Description:${NC} $DESC_PART"
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
show_flags "$COMMAND" "$FILTER"
else
show_help
exit 1
fi