ceae38a705
- Fix ASCII banner color display (no more escape code leakage) - Add syntax display to all modes (explain, interactive, direct) - Update COMMANDS array with syntax for all 84 commands - Enhance interactive mode with syntax preview in fzf - Improve show_flags() to match explain mode format with SYNTAX section - Enhance filter logic to search both flag names and descriptions - Update list_commands() to show syntax alongside descriptions - Consistent color-coded display across all modes - Better user experience with clear syntax and command reference Format: cmd:Description | Syntax Example: ls:List directory contents | ls [OPTION]... [FILE]... All modes now show: - Command name - Syntax/usage pattern - Description - Detailed flags with improved filtering
921 lines
32 KiB
Bash
Executable File
921 lines
32 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 -e "${CYAN} ____ __ ____ __ __"
|
|
echo -e " / __ )____ ______/ /_ / __ )__ ______/ /___/ /_ __"
|
|
echo -e " / __ / __ \`/ ___/ __ \\ / __ / / / / __ / __ / / / /"
|
|
echo -e " / /_/ / /_/ (__ ) / / / / /_/ / /_/ / /_/ / /_/ / /_/ /"
|
|
echo -e "/_____/\\__,_/____/_/ /_/ /_____/\\__,_/\\__,_/\\__,_/\\__, /"
|
|
echo -e " /____/${NC}"
|
|
echo -e "${YELLOW} Your Intelligent CLI Assistant for Bash${NC}"
|
|
echo ""
|
|
}
|
|
|
|
show_help() {
|
|
show_banner
|
|
echo -e "${BOLD}${YELLOW}QUICK START${NC}"
|
|
echo -e " ${GREEN}bash-helper ask \"find large files\"${NC} # Ask a question"
|
|
echo -e " ${GREEN}bash-helper category files${NC} # Browse by category"
|
|
echo -e " ${GREEN}bash-helper 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 " bash-helper ask \"compress folder\""
|
|
echo " bash-helper ask \"search text in files\""
|
|
echo " bash-helper task \"monitor cpu usage\""
|
|
echo ""
|
|
echo -e " ${DIM}# Browse and learn${NC}"
|
|
echo " bash-helper category network"
|
|
echo " bash-helper explain \"find . -name '*.txt'\""
|
|
echo ""
|
|
echo -e " ${DIM}# Original modes (still work)${NC}"
|
|
echo " bash-helper ls size # Direct flag lookup"
|
|
echo " bash-helper # 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}bash-helper ai \"find python files modified last week\"${NC}"
|
|
echo -e " ${GREEN}bash-helper 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 (90+ 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 " bash-helper COMMAND [FILTER]"
|
|
echo " bash-helper --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: bash-helper 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: bash-helper category files"
|
|
echo " • List all commands: bash-helper --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: bash-helper 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: bash-helper ai \"your complex query\""
|
|
echo ""
|
|
echo "Example:"
|
|
echo " bash-helper 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: bash-helper 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: bash-helper 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: bash-helper explain \"command with flags\""
|
|
echo ""
|
|
echo "Example:"
|
|
echo " bash-helper 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}"
|
|
|
|
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
|
|
# For builtins, get the first line of help
|
|
local syntax=$(help "$base_cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
|
|
if [ -n "$syntax" ]; then
|
|
echo -e " ${GREEN}$syntax${NC}"
|
|
else
|
|
echo -e " ${GREEN}$base_cmd [options]${NC}"
|
|
fi
|
|
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 | sed 's/^[[:space:]]*/ /')
|
|
if [ -n "$synopsis" ]; then
|
|
echo -e "${GREEN}$synopsis${NC}"
|
|
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
|
|
echo -e " ${GREEN}$usage${NC}"
|
|
else
|
|
echo -e " ${GREEN}$base_cmd [options]${NC}"
|
|
fi
|
|
fi
|
|
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"
|
|
}
|
|
|
|
show_flags() {
|
|
CMD_NAME=$1
|
|
FILTER=$2
|
|
|
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo -e "${YELLOW}Command Details: ${GREEN}$CMD_NAME${NC}"
|
|
if [ -n "$FILTER" ]; then
|
|
echo -e "${YELLOW}Filter: ${GREEN}$FILTER${NC}"
|
|
fi
|
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo ""
|
|
|
|
# Display SYNTAX section
|
|
if command -v "$CMD_NAME" &> /dev/null || type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
|
|
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
|
|
|
|
if type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
|
|
# For builtins, get the first line of help
|
|
local syntax=$(help "$CMD_NAME" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
|
|
if [ -n "$syntax" ]; then
|
|
echo -e " ${GREEN}$syntax${NC}"
|
|
else
|
|
echo -e " ${GREEN}$CMD_NAME [options]${NC}"
|
|
fi
|
|
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 | sed 's/^[[:space:]]*/ /')
|
|
if [ -n "$synopsis" ]; then
|
|
echo -e "${GREEN}$synopsis${NC}"
|
|
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
|
|
echo -e " ${GREEN}$usage${NC}"
|
|
else
|
|
echo -e " ${GREEN}$CMD_NAME [options]${NC}"
|
|
fi
|
|
fi
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# Display FLAGS/OPTIONS section
|
|
echo -e "${YELLOW}${BOLD}FLAGS/OPTIONS:${NC}"
|
|
echo ""
|
|
|
|
# Try to get flags from man page
|
|
if type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
|
|
# Shell builtin - use help command
|
|
if [ -n "$FILTER" ]; then
|
|
help "$CMD_NAME" 2>/dev/null | grep -iE "(^|\s)-.*$FILTER" | head -30
|
|
else
|
|
help "$CMD_NAME" 2>/dev/null | grep -E '^\s*-' | head -30
|
|
fi
|
|
elif command -v "$CMD_NAME" &> /dev/null; then
|
|
# External command - use man page
|
|
# Extract the OPTIONS section from man page
|
|
local man_output=$(man "$CMD_NAME" 2>/dev/null | col -b)
|
|
|
|
if [ -z "$man_output" ]; then
|
|
echo -e "${RED}No manual page available for: $CMD_NAME${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Try to extract just the OPTIONS/FLAGS section
|
|
local options_section=$(echo "$man_output" | sed -n '/^OPTIONS/,/^[A-Z][A-Z]/p' | head -100)
|
|
|
|
if [ -z "$options_section" ]; then
|
|
# Fallback: get all lines that look like flags
|
|
options_section=$(echo "$man_output" | grep -E '^\s*-' | head -50)
|
|
fi
|
|
|
|
if [ -n "$FILTER" ]; then
|
|
# Improved filter logic: match flag name or description
|
|
echo "$options_section" | grep -iE "(^|\s)-.*$FILTER|$FILTER.*-" | head -40
|
|
else
|
|
echo "$options_section" | head -40
|
|
fi
|
|
else
|
|
echo -e "${RED}Could not find help for: $CMD_NAME${NC}"
|
|
echo ""
|
|
echo "Make sure the command is installed or check the spelling."
|
|
return 1
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${DIM}Tip: Use the filter to search for specific flags or keywords${NC}"
|
|
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"
|
|
)
|
|
|
|
# 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
|
|
|
|
# Interactive mode with fzf
|
|
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: bash-helper COMMAND [FILTER]"
|
|
exit 1
|
|
fi
|
|
|
|
# Enhanced preview showing command, description, and syntax
|
|
FZF_COMMAND=$(printf "%s\n" "${COMMANDS[@]}" | fzf --height 60% --border \
|
|
--preview 'echo -e "\033[0;36mCommand:\033[0m {1}\n\033[0;36mDescription:\033[0m {2}\n\033[0;36mSyntax:\033[0m {3}"' \
|
|
--preview-window=up:3:wrap \
|
|
--delimiter ':|\|' \
|
|
--with-nth 1,2,3)
|
|
|
|
if [ -n "$FZF_COMMAND" ]; then
|
|
# Parse the selected command: "cmd:description | syntax"
|
|
CMD=${FZF_COMMAND%%:*}
|
|
REST=${FZF_COMMAND#*:}
|
|
|
|
# Extract description and syntax
|
|
if [[ "$REST" =~ (.+)\ \|\ (.+) ]]; then
|
|
DESC="${BASH_REMATCH[1]}"
|
|
SYNTAX="${BASH_REMATCH[2]}"
|
|
else
|
|
DESC="$REST"
|
|
SYNTAX="$CMD [options]"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
|
|
echo -e "${YELLOW}Command:${NC} ${MAGENTA}$CMD${NC}"
|
|
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
|
|
echo -e " ${GREEN}$SYNTAX${NC}"
|
|
echo ""
|
|
echo -e "${GREEN}Description:${NC} $DESC"
|
|
echo ""
|
|
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
read -p "Filter flags (optional, press Enter to skip): " FILTER
|
|
echo ""
|
|
show_flags "$CMD" "$FILTER"
|
|
else
|
|
echo "No command selected"
|
|
exit 0
|
|
fi
|
|
# 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
|