From 26894bc40be18f5ad1052e76221e1f595209a973 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Tue, 16 Dec 2025 08:53:28 -0500 Subject: [PATCH] Auto-sync from catchthesethighs --- CHANGELOG.md | 81 ++++++++++-- README.md | 2 + docs/SETUP_GUIDE.md | 59 ++++++++- dotfiles.conf | 2 +- install.sh | 29 +++++ zsh/aliases.zsh | 46 ++++--- zsh/functions/command-palette.zsh | 9 +- zsh/functions/motd.zsh | 207 +++++++++++++++++++----------- 8 files changed, 319 insertions(+), 116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ec5f5f..e6e079c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,64 @@ All notable changes to this dotfiles project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2025-12-16 + +### Added + +#### Shell Startup Optimization +- **Deferred Loading** - Heavy plugins and functions load after first prompt displays +- **Cached Command Checks** - `_has_cmd()` function caches `command -v` results for faster lookups +- **Lazy kubectl** - kubectl completions only load when first used (saves ~200-500ms) +- **Background Tasks** - Dotfiles sync check runs fully async with `&!` +- **Compile Script** (`dotfiles-compile.sh`) - Pre-compile zsh files to `.zwc` bytecode for 20-50ms speedup + +#### Smart Path Resolution +- **`_df_run()` Helper** - All aliases use full path with fallback to PATH +- Fixes "command not found" errors on fresh installs +- Scripts work even before symlinks are created or PATH is set + +#### New Aliases +- `dfcompile` - Compile zsh files for faster startup +- `dfcd` - Navigate to dotfiles directory (replaces `df` to avoid disk utility conflict) + +### Changed + +#### Script Reorganization +- Renamed `bin/shell-stats.sh` → `bin/dotfiles-stats.sh` +- Renamed `bin/vault.sh` → `bin/dotfiles-vault.sh` +- Renamed `bin/update-dotfiles.sh` → `bin/dotfiles-update.sh` +- Moved `bin/setup-wizard.sh` → `setup/setup-wizard.sh` +- Moved `bin/setup-espanso.sh` → `setup/setup-espanso.sh` +- Removed deprecated `bin/deploy-zshtheme-systemwide.sh` + +#### Alias System Overhaul +- All command aliases now use `_df_run()` function wrapper +- Uses full path `$_df_dir/bin/script.sh` with fallback to PATH lookup +- Better error messages when scripts not found +- Removed `df` alias (conflicts with disk free utility) + +#### .zshrc Optimizations +- Reduced default plugins (removed `docker`, `docker-compose`, `kubectl` from immediate load) +- Disabled oh-my-zsh auto-update check on every load (`DISABLE_AUTO_UPDATE="true"`) +- Tool aliases (eza, bat) now set up in deferred loading +- FZF configuration deferred until after prompt +- Vault secrets loaded with full path to avoid command_not_found issues +- Background sync check uses full script path + +### Fixed + +- **Fresh Install Bug** - "Command not found: dotfiles-sync.sh" error on new user accounts +- **Path Resolution** - Scripts now work before `~/.local/bin` is in PATH +- **Smart Suggest Conflicts** - Background tasks no longer trigger `command_not_found_handler` + +### Removed + +- `df` alias (conflicted with `df` disk utility) +- Heavy plugins from default load (now lazy-loaded) +- `bin/deploy-zshtheme-systemwide.sh` (redundant with installer) + +--- + ## [1.0.0] - 2025-12-15 ### Added @@ -14,8 +72,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Dynamic MOTD** (`motd.zsh`) - Compact system info on shell start (uptime, CPU, memory, docker, git status) - **Command Palette** (`command-palette.zsh`) - Raycast-style fuzzy launcher triggered by Ctrl+Space or Ctrl+P - **Smart Suggestions** (`smart-suggest.zsh`) - Typo correction for 100+ common mistakes + alias recommendations -- **Shell Analytics** (`shell-stats.sh`) - Dashboard showing command usage, suggestions, and activity heatmap -- **Secrets Vault** (`vault.sh`) - Encrypted storage for API keys using age/gpg +- **Shell Analytics** (`dotfiles-stats.sh`) - Dashboard showing command usage, suggestions, and activity heatmap +- **Secrets Vault** (`dotfiles-vault.sh`) - Encrypted storage for API keys using age/gpg - **Password Manager Integration** (`password-manager.zsh`) - Unified CLI for 1Password, LastPass, Bitwarden - **Dotfiles Sync** (`dotfiles-sync.sh`) - Multi-machine synchronization with watch mode - **Dotfiles Doctor** (`dotfiles-doctor.sh`) - Health checker with auto-fix capability @@ -86,35 +144,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Checking Your Version ```bash +dfv +# or dotfiles-version.sh ``` ### Updating ```bash -cd ~/.dotfiles -git pull origin main -./install.sh --skip-deps -``` - -Or use: - -```bash -update-dotfiles.sh +dfu +# or +dotfiles-update.sh ``` --- ## Roadmap -### Planned for 1.1.0 +### Planned for 1.2.0 - [ ] Multiple theme support with live preview - [ ] Project scaffolding templates - [ ] SSH key generation helper - [ ] Machine profiles (work, personal, server) -### Planned for 1.2.0 -- [ ] Dynamic MOTD/welcome screen +### Planned for 1.3.0 - [ ] Remote machine bootstrap script - [ ] Neovim configuration support diff --git a/README.md b/README.md index 17bbadf..8652b9c 100644 --- a/README.md +++ b/README.md @@ -357,9 +357,11 @@ All dotfiles commands have convenient aliases: | `dfu` | `dotfiles-update.sh` | Update dotfiles | | `dfv` | `dotfiles-version.sh` | Version info | | `dfstats` | `dotfiles-stats.sh` | Shell analytics | +| `dfcompile` | `dotfiles-compile.sh` | Compile zsh for speed | | `vault` | `dotfiles-vault.sh` | Secrets manager | | `reload` | `source ~/.zshrc` | Reload shell | | `dfc` | `dotfiles-cli` | CLI with subcommands | +| `dotfiles` | `cd ~/.dotfiles` | Go to dotfiles dir | ## 🗑️ Uninstalling diff --git a/docs/SETUP_GUIDE.md b/docs/SETUP_GUIDE.md index 544d1a3..444c0eb 100644 --- a/docs/SETUP_GUIDE.md +++ b/docs/SETUP_GUIDE.md @@ -409,7 +409,7 @@ All dotfiles commands have convenient aliases defined in `~/.dotfiles/zsh/aliase | Alias | Full Command | Description | |-------|--------------|-------------| -| `dotfiles` / `df` | `cd ~/.dotfiles` | Go to dotfiles directory | +| `dotfiles` / `dfcd` | `cd ~/.dotfiles` | Go to dotfiles directory | | `dfd` / `doctor` | `dotfiles-doctor.sh` | Run health check | | `dffix` | `dotfiles-doctor.sh --fix` | Auto-fix issues | | `dfs` / `dfsync` | `dotfiles-sync.sh` | Interactive sync | @@ -421,6 +421,7 @@ All dotfiles commands have convenient aliases defined in `~/.dotfiles/zsh/aliase | `dfstats` / `stats` | `dotfiles-stats.sh` | Shell analytics | | `tophist` | `dotfiles-stats.sh --top` | Top commands | | `suggest` | `dotfiles-stats.sh --suggest` | Alias suggestions | +| `dfcompile` | `dotfiles-compile.sh` | Compile zsh for speed | ### Vault Commands @@ -451,6 +452,7 @@ dfc update # Update dotfiles dfc version # Show version dfc stats # Shell analytics dfc vault # Secrets manager +dfc compile # Compile zsh for speed dfc edit # Open in editor dfc cd # Go to dotfiles dir dfc help # Show help @@ -458,6 +460,61 @@ dfc help # Show help --- +## Shell Optimization + +The `.zshrc` is optimized for fast startup while maintaining full functionality. + +### Measuring Startup Time + +```bash +# Quick measurement +time zsh -i -c exit + +# More accurate (requires hyperfine) +hyperfine 'zsh -i -c exit' +``` + +### Compile Zsh Files + +Pre-compile `.zsh` files to bytecode for 20-50ms speedup: + +```bash +dfcompile # Compile all zsh files +dfcompile --clean # Remove compiled files +``` + +### Loading Strategy + +| Phase | What Loads | Timing | +|-------|------------|--------| +| **Immediate** | PATH, history, oh-my-zsh, basic aliases, keybindings | Blocks prompt | +| **Deferred** | Tool aliases (eza, bat), FZF, smart-suggest, snapper, vault | After first prompt | +| **Background** | Dotfiles sync check | Fully async | +| **Lazy** | NVM, kubectl, virtualenvwrapper | When first used | + +### Profiling + +To debug slow startup, edit `~/.zshrc`: + +```zsh +# Uncomment at the TOP of file: +zmodload zsh/zprof + +# Uncomment at the BOTTOM of file: +zprof +``` + +Then run `zsh -i -c exit` to see timing breakdown. + +### Tips for Fast Startup + +1. **Run `dfcompile`** after installation +2. **Avoid adding** `command -v` checks in `.zshrc` (use `_has_cmd` cache instead) +3. **Use lazy loading** for heavy tools (NVM, kubectl already lazy-loaded) +4. **Keep `.zshrc.local`** minimal + +--- + ## Customization ### Adding Aliases diff --git a/dotfiles.conf b/dotfiles.conf index 0a34111..29295dd 100644 --- a/dotfiles.conf +++ b/dotfiles.conf @@ -6,7 +6,7 @@ # ============================================================================ # --- Version --- -DOTFILES_VERSION="1.0.0" +DOTFILES_VERSION="1.1.0" # --- GitHub Settings --- DOTFILES_GITHUB_USER="adlee-was-taken" diff --git a/install.sh b/install.sh index ee0d892..5dd310e 100755 --- a/install.sh +++ b/install.sh @@ -26,6 +26,7 @@ SKIP_DEPS=false DEPS_ONLY=false UNINSTALL=false UNINSTALL_PURGE=false +RUN_WIZARD=false for arg in "$@"; do case "$arg" in @@ -41,10 +42,14 @@ for arg in "$@"; do --purge) UNINSTALL_PURGE=true ;; + --wizard) + RUN_WIZARD=true + ;; --help|-h) echo "Usage: $0 [OPTIONS]" echo echo "Options:" + echo " --wizard Run interactive setup wizard (recommended)" echo " --skip-deps Skip dependency installation (useful for re-runs)" echo " --deps-only Only install dependencies, then exit" echo " --uninstall Remove symlinks and restore backups" @@ -57,6 +62,7 @@ for arg in "$@"; do echo echo "Examples:" echo " ./install.sh # Full install" + echo " ./install.sh --wizard # Interactive wizard" echo " ./install.sh --skip-deps # Re-run without checking deps" echo " ./install.sh --uninstall # Remove symlinks" echo " ./install.sh --uninstall --purge # Remove everything" @@ -800,6 +806,28 @@ main() { do_uninstall fi + # Handle wizard mode + if [[ "$RUN_WIZARD" == true ]]; then + load_config + local wizard_script="$DOTFILES_DIR/setup/setup-wizard.sh" + + # If dotfiles not yet cloned, clone first + if [[ ! -f "$wizard_script" ]]; then + detect_os + install_dependencies + clone_or_update_dotfiles + load_config + wizard_script="$DOTFILES_DIR/setup/setup-wizard.sh" + fi + + if [[ -f "$wizard_script" ]]; then + exec bash "$wizard_script" + else + print_error "Wizard script not found: $wizard_script" + exit 1 + fi + fi + print_header detect_os @@ -844,6 +872,7 @@ main() { echo " dfs / dfsync - Sync dotfiles" echo " dfu / dfupdate - Update dotfiles" echo " dfstats / stats - Shell analytics" + echo " dfcompile - Compile zsh for speed" echo " vault - Secrets manager" echo echo -e "${BLUE}To uninstall:${NC}" diff --git a/zsh/aliases.zsh b/zsh/aliases.zsh index 6afb238..6ebcaab 100644 --- a/zsh/aliases.zsh +++ b/zsh/aliases.zsh @@ -25,39 +25,45 @@ _df_run() { fi } +# --- Core Dotfiles Commands --- +alias dotfiles='cd ~/.dotfiles' +alias dfcd='cd ~/.dotfiles' +# Note: 'df' not aliased to avoid conflict with disk free utility + # Doctor - health check -function dfd() { _df_run dotfiles-doctor.sh "$@"; } -function doctor() { _df_run dotfiles-doctor.sh "$@"; } -function dffix() { _df_run dotfiles-doctor.sh --fix "$@"; } +dfd() { _df_run dotfiles-doctor.sh "$@"; } +doctor() { _df_run dotfiles-doctor.sh "$@"; } +dffix() { _df_run dotfiles-doctor.sh --fix "$@"; } # Sync - multi-machine synchronization -function dfs() { _df_run dotfiles-sync.sh "$@"; } -function dfsync() { _df_run dotfiles-sync.sh "$@"; } -function dfpush() { _df_run dotfiles-sync.sh --push "$@"; } -function dfpull() { _df_run dotfiles-sync.sh --pull "$@"; } -function dfstatus() { _df_run dotfiles-sync.sh --status "$@"; } +dfs() { _df_run dotfiles-sync.sh "$@"; } +dfsync() { _df_run dotfiles-sync.sh "$@"; } +dfpush() { _df_run dotfiles-sync.sh --push "$@"; } +dfpull() { _df_run dotfiles-sync.sh --pull "$@"; } +dfstatus() { _df_run dotfiles-sync.sh --status "$@"; } # Update - pull latest and reinstall -function dfu() { _df_run dotfiles-update.sh "$@"; } -function dfupdate() { _df_run dotfiles-update.sh "$@"; } +dfu() { _df_run dotfiles-update.sh "$@"; } +dfupdate() { _df_run dotfiles-update.sh "$@"; } # Version - check version info -function dfv() { _df_run dotfiles-version.sh "$@"; } -function dfversion() { _df_run dotfiles-version.sh "$@"; } +dfv() { _df_run dotfiles-version.sh "$@"; } +dfversion() { _df_run dotfiles-version.sh "$@"; } # Stats - shell analytics -function dfstats() { _df_run dotfiles-stats.sh "$@"; } -function tophist() { _df_run dotfiles-stats.sh --top "$@"; } -function suggest() { _df_run dotfiles-stats.sh --suggest "$@"; } +dfstats() { _df_run dotfiles-stats.sh "$@"; } +stats() { _df_run dotfiles-stats.sh "$@"; } +tophist() { _df_run dotfiles-stats.sh --top "$@"; } +suggest() { _df_run dotfiles-stats.sh --suggest "$@"; } # Vault - secrets management -function vault() { _df_run dotfiles-vault.sh "$@"; } -function vls() { _df_run dotfiles-vault.sh list "$@"; } -function vget() { _df_run dotfiles-vault.sh get "$@"; } -function vset() { _df_run dotfiles-vault.sh set "$@"; } +vault() { _df_run dotfiles-vault.sh "$@"; } +vls() { _df_run dotfiles-vault.sh list "$@"; } +vget() { _df_run dotfiles-vault.sh get "$@"; } +vset() { _df_run dotfiles-vault.sh set "$@"; } # Compile - compile zsh files for speed -function dfcompile() { _df_run dotfiles-compile.sh "$@"; } +dfcompile() { _df_run dotfiles-compile.sh "$@"; } # --- Quick Edit Aliases --- alias zshrc='${EDITOR:-vim} ~/.zshrc' diff --git a/zsh/functions/command-palette.zsh b/zsh/functions/command-palette.zsh index 8e174b2..71f8384 100644 --- a/zsh/functions/command-palette.zsh +++ b/zsh/functions/command-palette.zsh @@ -127,10 +127,11 @@ _palette_get_actions() { printf "%s\t%s\t%s\t%s\n" "$ICON_EDIT" "action" "Edit .zshrc" "${EDITOR:-vim} ~/.zshrc" printf "%s\t%s\t%s\t%s\n" "$ICON_EDIT" "action" "Edit dotfiles.conf" "${EDITOR:-vim} $DOTFILES_DIR/dotfiles.conf" printf "%s\t%s\t%s\t%s\n" "$ICON_EDIT" "action" "Edit theme" "${EDITOR:-vim} $DOTFILES_DIR/zsh/themes/adlee.zsh-theme" - printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Dotfiles doctor" "dotfiles-doctor.sh" - printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Dotfiles sync" "dotfiles-sync.sh" - printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Shell stats" "shell-stats.sh" - printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Vault list" "vault.sh list" + printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Dotfiles doctor" "dfd" + printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Dotfiles sync" "dfs" + printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Shell stats" "dfstats" + printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Compile zsh" "dfcompile" + printf "%s\t%s\t%s\t%s\n" "$ICON_SCRIPT" "action" "Vault list" "vault list" printf "%s\t%s\t%s\t%s\n" "$ICON_ACTION" "action" "Clear screen" "clear" printf "%s\t%s\t%s\t%s\n" "$ICON_DIR" "action" "Home" "cd ~" printf "%s\t%s\t%s\t%s\n" "$ICON_DIR" "action" "Dotfiles" "cd $DOTFILES_DIR" diff --git a/zsh/functions/motd.zsh b/zsh/functions/motd.zsh index 54ac086..140d6bd 100644 --- a/zsh/functions/motd.zsh +++ b/zsh/functions/motd.zsh @@ -1,102 +1,157 @@ #!/usr/bin/env zsh # ============================================================================ -# MOTD — Width-Safe, Color-Safe, Reload-Safe +# MOTD (Message of the Day) - Dynamic System Info +# ============================================================================ +# Displays system information on shell startup +# +# Functions: +# show_motd - Compact box format +# show_motd_mini - Single line format # ============================================================================ -# ---------------------------- Guards ----------------------------------------- +# Only run in interactive shells +[[ -o interactive ]] || return 0 -[[ -o interactive ]] || return -[[ -n $__MOTD_SHOWN ]] && return -typeset -g __MOTD_SHOWN=1 +# ============================================================================ +# Colors (ANSI escape codes) +# ============================================================================ -# ---------------------------- Configuration ---------------------------------- +_M_RESET=$'\033[0m' +_M_BOLD=$'\033[1m' +_M_DIM=$'\033[2m' +_M_BLUE=$'\033[38;5;39m' +_M_CYAN=$'\033[38;5;51m' +_M_GREEN=$'\033[38;5;82m' +_M_YELLOW=$'\033[38;5;220m' +_M_GREY=$'\033[38;5;242m' -BOX_WIDTH=78 # total width INCLUDING borders -INNER_WIDTH=$(( BOX_WIDTH - 2 )) -LABEL_WIDTH=12 +# ============================================================================ +# Info Gathering +# ============================================================================ -# ---------------------------- Colors ----------------------------------------- - -autoload -Uz colors && colors - -C_DIM="%F{242}" -C_HEAD="%B%F{39}" -C_LABEL="%F{51}" -C_OK="%F{82}" -C_RESET="%f%b" - -# ---------------------------- Low-level helpers ------------------------------ - -repeat_char() { - local ch="$1" n="$2" - printf '%*s' "$n" '' | tr ' ' "$ch" +_motd_uptime() { + local up=$(uptime 2>/dev/null) + if [[ "$up" =~ "up "([^,]+) ]]; then + echo "${match[1]}" | sed 's/^ *//' + else + echo "?" + fi } -pad_right() { - local s="$1" w="$2" - printf '%-*s' "$w" "$s" +_motd_load() { + if [[ -f /proc/loadavg ]]; then + awk '{print $1}' /proc/loadavg + else + uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | xargs + fi } -center_text() { - local s="$1" w="$2" - local len=${#s} - (( len >= w )) && { print "${s[1,w]}"; return } - local pad=$(( (w - len) / 2 )) - printf '%*s%s%*s' "$pad" '' "$s" "$(( w - len - pad ))" '' +_motd_mem() { + free -h 2>/dev/null | awk '/^Mem:/ {print $3 "/" $2}' || echo "N/A" } -# ---------------------------- Box primitives --------------------------------- - -box_top() { - print "${C_DIM}┌$(repeat_char '─' $INNER_WIDTH)┐${C_RESET}" +_motd_disk() { + df -h / 2>/dev/null | awk 'NR==2 {print $4 " free"}' || echo "N/A" } -box_bottom() { - print "${C_DIM}└$(repeat_char '─' $INNER_WIDTH)┘${C_RESET}" +# ============================================================================ +# Box Drawing - Fixed Width +# ============================================================================ + +# Fixed box width +_M_WIDTH=62 + +_motd_line() { + local char="$1" + local i + local line="" + for ((i=0; i<_M_WIDTH; i++)); do + line+="$char" + done + echo "$line" } -box_blank() { - print "${C_DIM}│$(repeat_char ' ' $INNER_WIDTH)│${C_RESET}" +_motd_pad() { + # Pad a plain string to exact width + local str="$1" + local width="$2" + local len=${#str} + if (( len >= width )); then + echo "${str:0:$width}" + else + printf "%-${width}s" "$str" + fi } -box_line() { - local content="$1" - content="$(pad_right "$content" "$INNER_WIDTH")" - print "${C_DIM}│${C_RESET}${content}${C_DIM}│${C_RESET}" +# ============================================================================ +# Main Display Function +# ============================================================================ + +show_motd() { + [[ -n "$_MOTD_SHOWN" && "$1" != "--force" ]] && return 0 + typeset -g _MOTD_SHOWN=1 + + local hostname="${HOST:-$(hostname -s 2>/dev/null)}" + local datetime=$(date '+%a %b %d %H:%M') + local uptime=$(_motd_uptime) + local load=$(_motd_load) + local mem=$(_motd_mem) + local disk=$(_motd_disk) + + local hline=$(_motd_line '─') + local inner=$((_M_WIDTH - 2)) + + echo "" + + # Top border + echo "${_M_GREY}┌${hline}┐${_M_RESET}" + + # Header: hostname + datetime + local h_left="✦ ${hostname}" + local h_right="${datetime}" + local h_pad=$((inner - ${#h_left} - ${#h_right})) + local h_spaces="" + for ((i=0; i