"Restructuring, deprecating, blah blah."

This commit is contained in:
Aaron D. Lee
2025-12-15 21:31:14 -05:00
parent 9d632c16ed
commit 9673a1cb94
13 changed files with 467 additions and 390 deletions

511
bin/dotfile-stats.sh Executable file
View File

@@ -0,0 +1,511 @@
#!/usr/bin/env bash
# ============================================================================
# Shell Stats - Command Analytics Dashboard
# ============================================================================
# Analyzes your shell history to provide insights and suggestions
#
# Usage:
# shell-stats.sh # Show dashboard
# shell-stats.sh --top [n] # Top N commands
# shell-stats.sh --suggest # Suggest aliases
# shell-stats.sh --hours # Commands by hour
# shell-stats.sh --dirs # Most used directories
# shell-stats.sh --export # Export stats as JSON
# ============================================================================
set -e
# ============================================================================
# Configuration
# ============================================================================
HISTFILE="${HISTFILE:-$HOME/.zsh_history}"
BASH_HISTFILE="$HOME/.bash_history"
STATS_CACHE="$HOME/.cache/shell-stats"
STATS_FILE="$STATS_CACHE/stats.json"
mkdir -p "$STATS_CACHE"
# ============================================================================
# Colors
# ============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
DIM='\033[2m'
BOLD='\033[1m'
NC='\033[0m'
# ============================================================================
# History Parsing
# ============================================================================
get_history_file() {
if [[ -f "$HISTFILE" ]]; then
echo "$HISTFILE"
elif [[ -f "$BASH_HISTFILE" ]]; then
echo "$BASH_HISTFILE"
else
echo ""
fi
}
parse_zsh_history() {
# Zsh extended history format: : timestamp:0;command
local histfile=$(get_history_file)
[[ -z "$histfile" ]] && return
if [[ "$histfile" == *"zsh"* ]]; then
# Zsh format
cat "$histfile" 2>/dev/null | sed 's/^: [0-9]*:[0-9]*;//' | grep -v '^$'
else
# Bash format
cat "$histfile" 2>/dev/null | grep -v '^#' | grep -v '^$'
fi
}
get_command_count() {
parse_zsh_history | wc -l | tr -d ' '
}
get_unique_commands() {
parse_zsh_history | awk '{print $1}' | sort -u | wc -l | tr -d ' '
}
# ============================================================================
# Analysis Functions
# ============================================================================
top_commands() {
local count="${1:-15}"
parse_zsh_history | \
awk '{print $1}' | \
sort | \
uniq -c | \
sort -rn | \
head -n "$count"
}
top_full_commands() {
local count="${1:-10}"
parse_zsh_history | \
sort | \
uniq -c | \
sort -rn | \
head -n "$count"
}
commands_by_hour() {
local histfile=$(get_history_file)
[[ -z "$histfile" ]] && return
# Try to extract timestamps from zsh history
if [[ "$histfile" == *"zsh"* ]]; then
grep '^:' "$histfile" 2>/dev/null | \
sed 's/^: \([0-9]*\):.*/\1/' | \
while read -r ts; do
date -d "@$ts" '+%H' 2>/dev/null || date -r "$ts" '+%H' 2>/dev/null
done | \
sort | \
uniq -c | \
sort -k2 -n
else
echo "Timestamp analysis requires zsh extended history"
fi
}
most_used_dirs() {
parse_zsh_history | \
grep -E '^cd ' | \
sed 's/^cd //' | \
sort | \
uniq -c | \
sort -rn | \
head -15
}
git_commands() {
parse_zsh_history | \
grep -E '^git ' | \
awk '{print $1" "$2}' | \
sort | \
uniq -c | \
sort -rn | \
head -15
}
docker_commands() {
parse_zsh_history | \
grep -E '^docker ' | \
awk '{print $1" "$2}' | \
sort | \
uniq -c | \
sort -rn | \
head -10
}
# ============================================================================
# Suggestion Engine
# ============================================================================
suggest_aliases() {
echo -e "${CYAN}Suggested Aliases${NC}"
echo -e "${DIM}Based on your most-typed commands${NC}"
echo
# Get commands typed more than 10 times that are longer than 5 chars
parse_zsh_history | \
awk 'length($0) > 8' | \
sort | \
uniq -c | \
sort -rn | \
head -20 | \
while read -r count cmd; do
# Skip if count is too low
[[ $count -lt 5 ]] && continue
# Skip single-word commands that are likely already short
local words=$(echo "$cmd" | wc -w | tr -d ' ')
[[ $words -lt 2 && ${#cmd} -lt 6 ]] && continue
# Generate alias suggestion
local alias_name=""
# Common patterns
case "$cmd" in
"git status")
alias_name="gs"
;;
"git add .")
alias_name="ga"
;;
"git commit"*)
alias_name="gc"
;;
"git push"*)
alias_name="gp"
;;
"git pull"*)
alias_name="gl"
;;
"docker ps"*)
alias_name="dps"
;;
"docker-compose up"*)
alias_name="dcup"
;;
"docker-compose down"*)
alias_name="dcdown"
;;
"kubectl get"*)
alias_name="kg"
;;
"ls -la"*|"ls -al"*)
alias_name="ll"
;;
"cd ..")
alias_name=".."
;;
*)
# Generate from first letters
alias_name=$(echo "$cmd" | awk '{for(i=1;i<=NF && i<=3;i++) printf substr($i,1,1)}')
;;
esac
# Check if alias already exists
if alias "$alias_name" &>/dev/null 2>&1; then
echo -e " ${GREEN}${NC} ${DIM}$alias_name${NC} already defined (used $count times)"
else
local saved_chars=$(( (${#cmd} - ${#alias_name}) * count ))
echo -e " ${YELLOW}${NC} alias ${CYAN}$alias_name${NC}='$cmd'"
echo -e " ${DIM}Used $count times, would save ~$saved_chars keystrokes${NC}"
fi
done
}
# ============================================================================
# Dashboard
# ============================================================================
draw_bar() {
local value=$1
local max=$2
local width=${3:-30}
local filled=$((value * width / max))
local empty=$((width - filled))
printf "${GREEN}"
printf "%${filled}s" | tr ' ' '█'
printf "${DIM}"
printf "%${empty}s" | tr ' ' '░'
printf "${NC}"
}
show_dashboard() {
clear
local total=$(get_command_count)
local unique=$(get_unique_commands)
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}Shell Analytics Dashboard${NC} ${BLUE}${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════════╝${NC}"
echo
# Summary stats
echo -e "${CYAN}Overview${NC}"
echo -e " Total commands: ${GREEN}${total}${NC}"
echo -e " Unique commands: ${GREEN}${unique}${NC}"
echo -e " History file: ${DIM}$(get_history_file)${NC}"
echo
# Top commands
echo -e "${CYAN}Top Commands${NC}"
echo
local max_count=$(top_commands 1 | awk '{print $1}')
top_commands 10 | while read -r count cmd; do
printf " %-12s %5d " "$cmd" "$count"
draw_bar "$count" "$max_count" 25
echo
done
echo
# Git breakdown (if git is in top commands)
if parse_zsh_history | grep -q '^git '; then
echo -e "${CYAN}Git Commands${NC}"
echo
git_commands | head -5 | while read -r count cmd; do
printf " %-20s %5d\n" "$cmd" "$count"
done
echo
fi
# Directory usage
echo -e "${CYAN}Most Visited Directories${NC}"
echo
most_used_dirs | head -5 | while read -r count dir; do
printf " %-35s %5d\n" "$dir" "$count"
done
echo
# Quick suggestions
echo -e "${CYAN}💡 Quick Tips${NC}"
echo
# Find most-typed long command
local long_cmd=$(parse_zsh_history | awk 'length($0) > 15' | sort | uniq -c | sort -rn | head -1)
local long_count=$(echo "$long_cmd" | awk '{print $1}')
local long_text=$(echo "$long_cmd" | sed 's/^[[:space:]]*[0-9]*[[:space:]]*//')
if [[ $long_count -gt 10 ]]; then
echo -e " ${YELLOW}${NC} You've typed '${CYAN}$long_text${NC}' $long_count times"
echo -e " Consider creating an alias for it!"
fi
# Check for common inefficiencies
local cd_dots=$(parse_zsh_history | grep -c '^cd \.\.' || echo 0)
if [[ $cd_dots -gt 50 ]]; then
echo -e " ${YELLOW}${NC} You use 'cd ..' a lot ($cd_dots times)"
echo -e " Tip: alias ..='cd ..' and ...='cd ../..'"
fi
echo
echo -e "${DIM}Run 'shell-stats.sh --suggest' for detailed alias suggestions${NC}"
}
# ============================================================================
# Export
# ============================================================================
export_stats() {
local output="${1:-$STATS_FILE}"
echo "{"
echo " \"generated\": \"$(date -Iseconds)\","
echo " \"total_commands\": $(get_command_count),"
echo " \"unique_commands\": $(get_unique_commands),"
echo " \"top_commands\": ["
top_commands 20 | awk 'BEGIN{first=1} {
if (!first) printf ",\n"
printf " {\"command\": \"%s\", \"count\": %d}", $2, $1
first=0
}'
echo
echo " ]"
echo "}"
}
# ============================================================================
# Activity Heatmap
# ============================================================================
show_heatmap() {
echo -e "${CYAN}Activity by Hour${NC}"
echo
# Create array for 24 hours
declare -a hours
for i in {0..23}; do
hours[$i]=0
done
# Count commands per hour
local histfile=$(get_history_file)
if [[ "$histfile" == *"zsh"* && -f "$histfile" ]]; then
while IFS= read -r line; do
if [[ "$line" =~ ^:\ ([0-9]+): ]]; then
local ts="${BASH_REMATCH[1]}"
local hour=$(date -d "@$ts" '+%H' 2>/dev/null || date -r "$ts" '+%H' 2>/dev/null)
hour=${hour#0} # Remove leading zero
((hours[$hour]++)) || true
fi
done < "$histfile"
# Find max for scaling
local max=1
for count in "${hours[@]}"; do
[[ $count -gt $max ]] && max=$count
done
# Draw heatmap
echo -n " "
for i in {0..23}; do
local intensity=$((hours[$i] * 4 / max))
case $intensity in
0) echo -ne "${DIM}${NC}" ;;
1) echo -ne "${GREEN}${NC}" ;;
2) echo -ne "${YELLOW}${NC}" ;;
3) echo -ne "${RED}${NC}" ;;
*) echo -ne "${MAGENTA}${NC}" ;;
esac
done
echo
echo -ne " "
echo -e "${DIM}0 6 12 18 23${NC}"
echo
# Peak hours
local peak_hour=0
local peak_count=0
for i in {0..23}; do
if [[ ${hours[$i]} -gt $peak_count ]]; then
peak_count=${hours[$i]}
peak_hour=$i
fi
done
echo -e " Peak activity: ${GREEN}${peak_hour}:00${NC} ($peak_count commands)"
else
echo -e " ${YELLOW}${NC} Heatmap requires zsh with extended history"
fi
}
# ============================================================================
# Main
# ============================================================================
show_help() {
echo "Usage: $0 [COMMAND] [OPTIONS]"
echo
echo "Commands:"
echo " (none) Show dashboard"
echo " --top [n] Top N commands (default: 15)"
echo " --full [n] Top N full command lines"
echo " --suggest Suggest aliases based on usage"
echo " --hours Show activity by hour"
echo " --heatmap Show activity heatmap"
echo " --dirs Most visited directories"
echo " --git Git command breakdown"
echo " --docker Docker command breakdown"
echo " --export Export stats as JSON"
echo " --help Show this help"
echo
}
main() {
local histfile=$(get_history_file)
if [[ -z "$histfile" || ! -f "$histfile" ]]; then
echo -e "${RED}${NC} No history file found"
echo " Checked: $HISTFILE"
echo " Checked: $BASH_HISTFILE"
exit 1
fi
case "${1:-}" in
--top|-t)
echo -e "${CYAN}Top Commands${NC}"
echo
top_commands "${2:-15}" | while read -r count cmd; do
printf " %5d %s\n" "$count" "$cmd"
done
;;
--full|-f)
echo -e "${CYAN}Top Full Commands${NC}"
echo
top_full_commands "${2:-10}" | while read -r count cmd; do
printf " %5d %s\n" "$count" "$cmd"
done
;;
--suggest|-s)
suggest_aliases
;;
--hours)
commands_by_hour
;;
--heatmap|-m)
show_heatmap
;;
--dirs|-d)
echo -e "${CYAN}Most Visited Directories${NC}"
echo
most_used_dirs | while read -r count dir; do
printf " %5d %s\n" "$count" "$dir"
done
;;
--git|-g)
echo -e "${CYAN}Git Commands${NC}"
echo
git_commands | while read -r count cmd; do
printf " %5d %s\n" "$count" "$cmd"
done
;;
--docker)
echo -e "${CYAN}Docker Commands${NC}"
echo
docker_commands | while read -r count cmd; do
printf " %5d %s\n" "$count" "$cmd"
done
;;
--export|-e)
export_stats "${2:-}"
;;
--help|-h)
show_help
;;
"")
show_dashboard
;;
*)
echo "Unknown command: $1"
show_help
exit 1
;;
esac
}
main "$@"