2fb20b0682
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>
1490 lines
60 KiB
Bash
Executable File
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
|