diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..2f7bda6 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,58 @@ +name: Lint + +on: + push: + branches: [main] + pull_request: + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@2.0.0 + with: + scandir: . + severity: warning + ignore_paths: macOS + + json-validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate JSON files + run: | + failed=0 + for f in $(find . -name '*.json' -not -path './.git/*'); do + if ! jq empty "$f" 2>/dev/null; then + echo "❌ Invalid JSON: $f" + failed=1 + else + echo "✓ $f" + fi + done + exit $failed + + zsh-syntax: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install zsh + run: sudo apt-get update && sudo apt-get install -y zsh + + - name: Run zsh -n on zsh scripts + run: | + failed=0 + for f in $(find . -not -path './.git/*' \( \( -name '*.sh' -path '*/macOS/*' \) -o -name '*.zsh' -o -name 'zshrc.example' \)); do + if zsh -n "$f" 2>&1; then + echo "✓ $f" + else + echo "❌ Syntax error: $f" + failed=1 + fi + done + exit $failed diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..23abdf9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + args: [--severity=warning] + exclude: ^macOS/ + + - repo: local + hooks: + - id: zsh-syntax-check + name: zsh -n (syntax check) + language: system + entry: zsh -n + types: [file] + files: (^macOS/.*\.sh$|\.zsh$|zshrc\.example$) + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-json + - id: end-of-file-fixer + - id: trailing-whitespace diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e031fac --- /dev/null +++ b/TODO.md @@ -0,0 +1,45 @@ +# TODO + +## macOS zshrc modernization + +Port the linux-desktop zsh enhancements to the macOS config. See +[macOS/zshrc-upgrade.md](macOS/zshrc-upgrade.md) for the full plan. + +- [ ] Switch from manual Homebrew plugin sourcing to antidote +- [ ] Add eza aliases with `--icons --group-directories-first` +- [ ] Add RPROMPT with command duration +- [ ] Add colorized man pages via bat +- [ ] Add zsh-notify for desktop notifications on long commands +- [ ] Add fish-style abbreviations via zsh-abbr +- [ ] Add zsh-history-substring-search with Up/Down keybindings +- [ ] Add grouped completion descriptions +- [ ] Add path underlining in syntax highlighting +- [ ] Add zoxide init +- [ ] Add FZF keybindings and completion (Homebrew paths) +- [ ] Create macOS zsh_plugins.txt + +## linux-desktop (personal) — CachyOS / Arch + +Test and validate the linux-desktop setup on the personal CachyOS desktop +(Arch-based, yay as AUR helper). The package JSON already has Arch support +(`package_manager.arch`, `arch_name` overrides) but nothing has been tested. + +- [ ] Create an Arch-aware setup script (or extend `setup.sh` with distro detection) +- [ ] Verify all `arch_name` overrides resolve to real AUR/pacman packages +- [ ] Test antidote, zsh-notify, eza icons, and zoxide on CachyOS +- [ ] Handle CachyOS defaults that may conflict (e.g., existing fish config) +- [ ] Add personal-only packages: discord, spotify, steam, bolt-launcher, notion +- [ ] Test `--personal` flag end-to-end +- [ ] Create PR for CachyOS support + +## linux-server — Raspberry Pi 4 + +Set up the Raspberry Pi 4 headless server config under `linux-server/`. + +- [ ] Audit existing linux-server/ files and update as needed +- [ ] Create or update packages JSON for the Pi (arm64, Debian-based) +- [ ] Create setup script for headless server (no GUI packages, no snap) +- [ ] Zsh config (server variant — no Ghostty, no fastfetch on launch, no desktop notifications) +- [ ] Tailscale, Docker, SSH hardening +- [ ] Homepage dashboard config (already exists under linux-server/homepage/) +- [ ] Test on Raspberry Pi 4 diff --git a/linux-desktop/ghostty.config b/linux-desktop/ghostty.config new file mode 100644 index 0000000..343919f --- /dev/null +++ b/linux-desktop/ghostty.config @@ -0,0 +1,4 @@ +# Ghostty Config + +theme = Dark Pastel +shell-integration = zsh diff --git a/linux-desktop/linux_desktop_packages.json b/linux-desktop/linux_desktop_packages.json new file mode 100644 index 0000000..3ed75ec --- /dev/null +++ b/linux-desktop/linux_desktop_packages.json @@ -0,0 +1,458 @@ +[ + { + "name": "zsh", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "high", + "optional": false, + "description": "Z shell — common shell across all devices; install first so plugins are available" + }, + { + "name": "zsh-antidote", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "high", + "optional": false, + "description": "Lightweight zsh plugin manager — handles autosuggestions, syntax highlighting, completions, substring search, and abbreviations via a plugins file" + }, + { + "name": "zoxide", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "high", + "optional": false, + "description": "Smarter cd — learns your frequent directories, use z to jump instantly" + }, + { + "name": "pyenv", + "package_manager": { "ubuntu": "curl", "arch": "yay" }, + "install_command": { "ubuntu": "curl https://pyenv.run | bash" }, + "priority": "high", + "optional": false, + "description": "Python version manager — compiles from source to avoid distro-packaged Python quirks" + }, + { + "name": "nvm", + "package_manager": { "ubuntu": "curl", "arch": "yay" }, + "install_command": { "ubuntu": "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash" }, + "priority": "high", + "optional": false, + "description": "Node version manager — installed via curl on Ubuntu per nvm recommendation" + }, + { + "name": "pipx", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "arch_name": "python-pipx", + "priority": "high", + "optional": false, + "description": "Installs Python CLI tools in isolated virtual environments" + }, + { + "name": "build-essential", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "arch_name": "base-devel", + "priority": "high", + "optional": false, + "description": "Compiler toolchain — gcc, make, etc.; required for pyenv, native Node modules, and AUR builds" + }, + { + "name": "git", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "high", + "optional": false, + "description": "Version control — usually pre-installed but listed to ensure it's present" + }, + { + "name": "ghostty", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "install_command": { "ubuntu": "sudo snap install ghostty --classic" }, + "priority": "medium", + "optional": false, + "description": "GPU-accelerated terminal emulator — requires --classic on snap for full system access" + }, + { + "name": "fastfetch", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Fast system info display — shown on Ghostty startup" + }, + { + "name": "gh", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "arch_name": "github-cli", + "priority": "medium", + "optional": false, + "description": "GitHub CLI — PRs, issues, Actions from the terminal" + }, + { + "name": "git-lfs", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Git extension for versioning large binary files" + }, + { + "name": "gnupg", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "GPG — required for commit signing" + }, + { + "name": "htop", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Interactive process viewer — colorized alternative to top" + }, + { + "name": "nvtop", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "GPU process monitor — complements htop with GPU visibility" + }, + { + "name": "tmux", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Terminal multiplexer — split panes, persist sessions" + }, + { + "name": "micro", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Modern terminal text editor with mouse support and familiar keybindings" + }, + { + "name": "eza", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Modern ls replacement — colors, icons, git status, tree view" + }, + { + "name": "fzf", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Fuzzy finder — Ctrl+R history search, Ctrl+T file search" + }, + { + "name": "ripgrep", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Fast recursive grep replacement — command: rg" + }, + { + "name": "bat", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "ubuntu_name": "bat", + "priority": "medium", + "optional": false, + "description": "cat with syntax highlighting and git integration — Ubuntu installs binary as batcat" + }, + { + "name": "fd", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "ubuntu_name": "fd-find", + "priority": "medium", + "optional": false, + "description": "Fast find alternative — Ubuntu installs binary as fdfind" + }, + { + "name": "jq", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "JSON processor — required by setup.sh to parse this file" + }, + { + "name": "curl", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "HTTP client — usually pre-installed; required by various install scripts" + }, + { + "name": "wget", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "HTTP file downloader — used by setup scripts for key imports" + }, + { + "name": "sshpass", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Non-interactive SSH password authentication — used by SSH_and_GPG/add_remote_host.sh" + }, + { + "name": "libnotify-bin", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "arch_name": "libnotify", + "priority": "medium", + "optional": false, + "description": "Desktop notifications CLI (notify-send) — used by zsh-notify plugin" + }, + { + "name": "xdotool", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "X11 window automation — used by zsh-notify for terminal focus detection" + }, + { + "name": "tailscale", + "package_manager": { "ubuntu": "custom", "arch": "yay" }, + "install_command": { "ubuntu": "curl -fsSL https://tailscale.com/install.sh | sudo sh" }, + "priority": "medium", + "optional": false, + "description": "Zero-config VPN — access all devices on a private network" + }, + { + "name": "docker", + "package_manager": { "ubuntu": "custom", "arch": "yay" }, + "install_command": { "ubuntu": "curl -fsSL https://get.docker.com | sudo sh" }, + "priority": "low", + "optional": true, + "description": "Container platform — official Docker Engine for Linux" + }, + { + "name": "claude-code", + "package_manager": { "ubuntu": "curl", "arch": "curl" }, + "install_command": { "ubuntu": "curl -fsSL https://claude.ai/install.sh | bash", "arch": "curl -fsSL https://claude.ai/install.sh | bash" }, + "priority": "medium", + "optional": false, + "description": "Claude Code — terminal-based AI coding assistant" + }, + { + "name": "claude-desktop", + "package_manager": { "ubuntu": "custom", "arch": "yay" }, + "arch_name": "claude-desktop-bin", + "install_command": { "ubuntu": "curl -fsSL https://pkg.claude-desktop-debian.dev/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/claude-desktop.gpg && echo 'deb [signed-by=/usr/share/keyrings/claude-desktop.gpg arch=amd64,arm64] https://pkg.claude-desktop-debian.dev stable main' | sudo tee /etc/apt/sources.list.d/claude-desktop.list && sudo apt update && sudo apt install -y claude-desktop" }, + "priority": "medium", + "optional": false, + "description": "Anthropic Claude desktop app — Ubuntu uses aaddrick/claude-desktop-debian apt repo" + }, + { + "name": "visual-studio-code", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "ubuntu_name": "code", + "arch_name": "visual-studio-code-bin", + "install_command": { "ubuntu": "sudo snap install code --classic" }, + "priority": "medium", + "optional": false, + "description": "Code editor with extensions — requires --classic on snap" + }, + { + "name": "firefox", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Open source web browser" + }, + { + "name": "chromium", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "priority": "medium", + "optional": false, + "environment": ["work"], + "description": "Open source Chromium browser — used for work alongside Firefox" + }, + { + "name": "obs-studio", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Open source broadcasting and screen recording" + }, + { + "name": "vlc", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Open source media player for nearly every format" + }, + { + "name": "discord", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "priority": "medium", + "optional": false, + "environment": ["personal"], + "description": "Voice, video, and text chat platform" + }, + { + "name": "bitwarden", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "priority": "medium", + "optional": false, + "description": "Open source password manager" + }, + { + "name": "slack", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "arch_name": "slack-desktop", + "priority": "medium", + "optional": false, + "environment": ["work"], + "description": "Team messaging and collaboration" + }, + { + "name": "zoom", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "ubuntu_name": "zoom-client", + "priority": "medium", + "optional": false, + "environment": ["work"], + "description": "Video conferencing" + }, + { + "name": "teams-for-linux", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "priority": "medium", + "optional": false, + "environment": ["work"], + "description": "Unofficial Microsoft Teams client for Linux" + }, + { + "name": "poetry", + "package_manager": { "ubuntu": "pipx", "arch": "yay" }, + "arch_name": "python-poetry", + "priority": "medium", + "optional": false, + "description": "Python project and dependency manager" + }, + { + "name": "codeburn", + "package_manager": { "ubuntu": "npm", "arch": "npm" }, + "priority": "medium", + "optional": false, + "description": "Track AI coding token usage by task, tool, model, and project" + }, + { + "name": "hf", + "package_manager": { "ubuntu": "pipx", "arch": "yay" }, + "arch_name": "huggingface-hub", + "install_command": { "ubuntu": "pipx install huggingface-hub[cli]" }, + "priority": "medium", + "optional": false, + "environment": ["personal"], + "description": "Hugging Face Hub CLI — download, upload, and manage ML models" + }, + { + "name": "git-xet", + "package_manager": { "ubuntu": "custom", "arch": "yay" }, + "arch_name": "git-xet-bin", + "install_command": { "ubuntu": "curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/huggingface/xet-core/refs/heads/main/git_xet/install.sh | sh" }, + "priority": "medium", + "optional": false, + "environment": ["personal"], + "description": "Git LFS plugin using the Xet protocol for Hugging Face Hub" + }, + { + "name": "net-tools", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "low", + "optional": true, + "description": "Legacy network utilities — ifconfig, netstat, route" + }, + { + "name": "poppler", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "ubuntu_name": "poppler-utils", + "priority": "low", + "optional": true, + "description": "PDF CLI tools — pdftotext, pdfimages, pdfinfo, pdftoppm" + }, + { + "name": "ffmpeg", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "low", + "optional": true, + "description": "Audio/video processing toolkit" + }, + { + "name": "exiftool", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "ubuntu_name": "libimage-exiftool-perl", + "arch_name": "perl-image-exiftool", + "priority": "low", + "optional": true, + "description": "Read and write metadata in photos, videos, and other files" + }, + { + "name": "tesseract", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "ubuntu_name": "tesseract-ocr", + "priority": "low", + "optional": true, + "description": "OCR engine — extract text from images and scanned documents" + }, + { + "name": "smartmontools", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "low", + "optional": true, + "description": "Monitor SSD/HDD health via S.M.A.R.T. data" + }, + { + "name": "nmap", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "low", + "optional": true, + "description": "Network scanner — discover hosts and open ports on the local network" + }, + { + "name": "notion", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "ubuntu_name": "notion-snap-reborn", + "arch_name": "notion-app-electron", + "priority": "low", + "optional": true, + "environment": ["personal"], + "description": "Notes and project management — unofficial snap/AUR packages on Linux" + }, + { + "name": "spotify", + "package_manager": { "ubuntu": "snap", "arch": "yay" }, + "priority": "low", + "optional": true, + "environment": ["personal"], + "description": "Music streaming" + }, + { + "name": "steam", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "low", + "optional": true, + "environment": ["personal"], + "description": "Game library and storefront" + }, + { + "name": "bolt-launcher", + "package_manager": { "arch": "yay" }, + "priority": "low", + "optional": true, + "environment": ["personal"], + "description": "Open-source RuneScape/OSRS launcher for Linux — replaces Jagex Launcher" + }, + { + "name": "octave", + "package_manager": { "ubuntu": "apt", "arch": "yay" }, + "priority": "none", + "optional": true, + "environment": ["work"], + "description": "MATLAB-compatible numerical computing environment" + }, + { + "name": "llama.cpp", + "package_manager": { "ubuntu": "custom", "arch": "yay" }, + "install_command": { "ubuntu": "See https://github.com/ggml-org/llama.cpp#build" }, + "priority": "none", + "optional": true, + "description": "LLM inference engine — run local models on CPU/GPU" + } +] diff --git a/linux-desktop/setup.sh b/linux-desktop/setup.sh new file mode 100755 index 0000000..9b45170 --- /dev/null +++ b/linux-desktop/setup.sh @@ -0,0 +1,482 @@ +#!/usr/bin/env bash +# Linux desktop initial setup script (Ubuntu) +# Usage: bash linux-desktop/setup.sh [--optional] [--work] [--personal] [--dry-run] +# --optional also install low-priority optional packages +# --work also install work-only packages (Slack, Zoom, Teams, Chromium) +# --personal also install personal-only packages (Discord, VLC, Spotify, Steam) +# --dry-run print all commands without executing anything +# +# Environment filtering: +# No flags → installs packages with no environment tag (shared across all) +# --work → adds packages tagged environment=["work"] +# --personal → adds packages tagged environment=["personal"] +# Both flags → installs everything + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PACKAGES_JSON="$SCRIPT_DIR/linux_desktop_packages.json" +DISTRO="ubuntu" +INCLUDE_OPTIONAL=false +INCLUDE_WORK=false +INCLUDE_PERSONAL=false +DRY_RUN=false + +for arg in "$@"; do + case "$arg" in + --optional) INCLUDE_OPTIONAL=true ;; + --work) INCLUDE_WORK=true ;; + --personal) INCLUDE_PERSONAL=true ;; + --dry-run) DRY_RUN=true ;; + esac +done + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +run() { + if [[ "$DRY_RUN" == true ]]; then + echo " [dry-run] $*" + else + "$@" + fi +} + +run_eval() { + if [[ "$DRY_RUN" == true ]]; then + echo " [dry-run] eval: $1" + else + eval "$1" + fi +} + +# Build a jq filter for environment tags. +# Packages with no environment field always install. +# Packages with environment=["work"] only install if --work is passed, etc. +env_filter() { + local work="$INCLUDE_WORK" personal="$INCLUDE_PERSONAL" + cat < Updating package list..." +run sudo apt update + +# ── Bootstrap: jq ──────────────────────────────────────────────────────────── +if ! command -v jq &>/dev/null; then + echo "==> Bootstrapping jq..." + run sudo apt install -y jq +fi + +# ── External apt repos (idempotent) ────────────────────────────────────────── +NEED_UPDATE=false + +# fastfetch — not in standard Ubuntu repos +if ! apt-cache show fastfetch &>/dev/null 2>&1; then + echo "==> Adding fastfetch PPA..." + run sudo add-apt-repository -y ppa:zhangsongcui3371/fastfetch + NEED_UPDATE=true +fi + +# eza — not in standard Ubuntu repos +if ! apt-cache show eza &>/dev/null 2>&1; then + echo "==> Adding eza apt repo..." + if [[ "$DRY_RUN" != true ]]; then + sudo apt install -y gpg + wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc \ + | sudo gpg --dearmor -o /etc/apt/keyrings/gierens.gpg + echo "deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://deb.gierens.de stable main" \ + | sudo tee /etc/apt/sources.list.d/gierens.list > /dev/null + sudo chmod 644 /etc/apt/keyrings/gierens.gpg /etc/apt/sources.list.d/gierens.list + else + echo " [dry-run] add eza community apt repo" + fi + NEED_UPDATE=true +fi + +# gh — GitHub CLI apt repo +if ! command -v gh &>/dev/null; then + echo "==> Adding GitHub CLI apt repo..." + if [[ "$DRY_RUN" != true ]]; then + wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + | sudo gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg + sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ + | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null + else + echo " [dry-run] add GitHub CLI apt repo" + fi + NEED_UPDATE=true +fi + +[[ "$NEED_UPDATE" == true ]] && run sudo apt update + +# ── High-priority apt packages ─────────────────────────────────────────────── +echo "" +echo "==> Installing high-priority apt packages..." +apt_install "high" + +# ── pyenv ──────────────────────────────────────────────────────────────────── +PYTHON_VERSION="3.12.13" + +echo "" +if [[ "$DRY_RUN" == true ]]; then + echo "==> pyenv..." + echo " [dry-run] eval: curl https://pyenv.run | bash" + echo " [dry-run] pyenv install $PYTHON_VERSION && pyenv global $PYTHON_VERSION" +else + if [[ ! -d "$HOME/.pyenv" ]]; then + echo "==> Installing pyenv..." + curl_installs "high" | while IFS='|' read -r name cmd; do + [[ "$name" == "pyenv" ]] && eval "$cmd" + done + else + echo "==> pyenv already installed" + fi + + export PYENV_ROOT="$HOME/.pyenv" + export PATH="$PYENV_ROOT/bin:$PATH" + if command -v pyenv &>/dev/null; then + eval "$(pyenv init -)" + if ! pyenv versions 2>/dev/null | grep -q "$PYTHON_VERSION"; then + echo "==> Installing pyenv build dependencies..." + sudo apt install -y libssl-dev libffi-dev libncurses-dev libreadline-dev \ + libbz2-dev libsqlite3-dev liblzma-dev zlib1g-dev tk-dev + echo "==> Installing Python $PYTHON_VERSION via pyenv..." + pyenv install "$PYTHON_VERSION" + pyenv global "$PYTHON_VERSION" + else + echo "==> Python $PYTHON_VERSION already installed" + fi + fi +fi + +# ── nvm + Node ─────────────────────────────────────────────────────────────── +echo "" +if [[ "$DRY_RUN" == true ]]; then + echo "==> nvm..." + echo " [dry-run] install nvm via curl" + echo " [dry-run] nvm install lts/* && nvm alias default lts/*" +else + if [ ! -d "$HOME/.nvm" ]; then + echo "==> Installing nvm..." + curl_installs "high" | while IFS='|' read -r name cmd; do + [[ "$name" == "nvm" ]] && eval "$cmd" + done + else + echo "==> nvm already installed" + fi + + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + if command -v nvm &>/dev/null; then + nvm install 'lts/*' + nvm alias default 'lts/*' + nvm use default + fi +fi + +# ── Medium-priority apt packages ───────────────────────────────────────────── +echo "" +echo "==> Installing medium-priority apt packages..." +apt_install "medium" + +# ── Medium-priority snap packages ──────────────────────────────────────────── +echo "" +echo "==> Installing snap packages..." +snap_install "medium" + +# ── bat / fd-find symlinks ─────────────────────────────────────────────────── +echo "" +echo "==> Setting up bat and fd symlinks..." +for pair in "batcat bat" "fdfind fd"; do + src="${pair%% *}"; dst="${pair##* }" + if command -v "$src" &>/dev/null && ! command -v "$dst" &>/dev/null; then + run sudo ln -sf "$(command -v "$src")" /usr/local/bin/"$dst" + echo " linked $dst → $src" + else + echo " ✓ $dst" + fi +done + +# ── Tailscale ──────────────────────────────────────────────────────────────── +echo "" +if [[ "$DRY_RUN" == true ]]; then + command -v tailscale &>/dev/null \ + && echo "==> Tailscale already installed" \ + || echo " [dry-run] eval: curl -fsSL https://tailscale.com/install.sh | sudo sh" +elif ! command -v tailscale &>/dev/null; then + echo "==> Installing Tailscale..." + custom_installs "medium" | while IFS='|' read -r name cmd; do + [[ "$name" == "tailscale" ]] && eval "$cmd" + done +else + echo "==> Tailscale already installed ($(tailscale version | head -1))" +fi + +# ── claude-code ────────────────────────────────────────────────────────────── +echo "" +if [[ "$DRY_RUN" == true ]]; then + command -v claude &>/dev/null \ + && echo "==> claude-code already installed" \ + || echo " [dry-run] eval: curl -fsSL https://claude.ai/install.sh | bash" +elif ! command -v claude &>/dev/null; then + echo "==> Installing claude-code..." + curl_installs "medium" | while IFS='|' read -r name cmd; do + [[ "$name" == "claude-code" ]] && eval "$cmd" + done +else + echo "==> claude-code already installed" +fi + +# ── pipx packages ──────────────────────────────────────────────────────────── +echo "" +echo "==> Installing pipx packages..." +if command -v pipx &>/dev/null; then + pipx ensurepath + pipx_install "medium" +else + echo " pipx not found — skipping" +fi + +# ── npm packages ───────────────────────────────────────────────────────────── +echo "" +echo "==> Installing npm packages..." +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" +if command -v npm &>/dev/null; then + npm_install "medium" +else + echo " npm not found — skipping" +fi + +# ── zsh as default shell ───────────────────────────────────────────────────── +ZSH_BIN="$(command -v zsh 2>/dev/null || true)" +if [[ -n "$ZSH_BIN" && "$SHELL" != "$ZSH_BIN" ]]; then + echo "" + echo "==> Setting zsh as default shell..." + run sudo usermod -s "$ZSH_BIN" "$USER" +fi + +# ── zshrc ──────────────────────────────────────────────────────────────────── +echo "" +if [[ ! -f "$HOME/.zshrc" ]]; then + run cp "$SCRIPT_DIR/zshrc.example" "$HOME/.zshrc" + echo "==> ~/.zshrc installed from zshrc.example" +elif ! diff -q "$SCRIPT_DIR/zshrc.example" "$HOME/.zshrc" &>/dev/null; then + BACKUP="$HOME/.zshrc.bak.$(date +%Y%m%d_%H%M%S)" + run cp "$HOME/.zshrc" "$BACKUP" + run cp "$SCRIPT_DIR/zshrc.example" "$HOME/.zshrc" + echo "==> ~/.zshrc updated (backup saved to $BACKUP)" +else + echo "==> ~/.zshrc already up to date" +fi + +# ── antidote plugins file ──────────────────────────────────────────────────── +echo "" +if [[ ! -f "$HOME/.zsh_plugins.txt" ]]; then + run cp "$SCRIPT_DIR/zsh_plugins.txt" "$HOME/.zsh_plugins.txt" + echo "==> ~/.zsh_plugins.txt installed" +elif ! diff -q "$SCRIPT_DIR/zsh_plugins.txt" "$HOME/.zsh_plugins.txt" &>/dev/null; then + run cp "$SCRIPT_DIR/zsh_plugins.txt" "$HOME/.zsh_plugins.txt" + echo "==> ~/.zsh_plugins.txt updated" +else + echo "==> ~/.zsh_plugins.txt already up to date" +fi + +# ── Ghostty config ─────────────────────────────────────────────────────────── +GHOSTTY_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/ghostty" +GHOSTTY_CONFIG="$GHOSTTY_CONFIG_DIR/config" + +echo "" +if [[ "$DRY_RUN" == true ]]; then + echo " [dry-run] mkdir -p $GHOSTTY_CONFIG_DIR" + echo " [dry-run] cp ghostty.config → $GHOSTTY_CONFIG" +else + mkdir -p "$GHOSTTY_CONFIG_DIR" + if [[ -f "$GHOSTTY_CONFIG" ]] && ! diff -q "$SCRIPT_DIR/ghostty.config" "$GHOSTTY_CONFIG" &>/dev/null; then + cp "$GHOSTTY_CONFIG" "${GHOSTTY_CONFIG}.bak" + cp "$SCRIPT_DIR/ghostty.config" "$GHOSTTY_CONFIG" + echo "==> Ghostty config updated (backup saved)" + elif [[ ! -f "$GHOSTTY_CONFIG" ]]; then + cp "$SCRIPT_DIR/ghostty.config" "$GHOSTTY_CONFIG" + echo "==> Ghostty config installed to $GHOSTTY_CONFIG" + else + echo "==> Ghostty config already up to date" + fi +fi + +# ── Optional low-priority packages (--optional flag) ───────────────────────── +if [[ "$INCLUDE_OPTIONAL" == true ]]; then + echo "" + echo "==> Installing optional (low) packages..." + apt_install "low" + snap_install "low" + pipx_install "low" + npm_install "low" + + # Docker (optional) + if ! command -v docker &>/dev/null; then + echo "==> Installing Docker..." + curl -fsSL https://get.docker.com | sudo sh + sudo usermod -aG docker "$USER" + echo " Log out and back in for the docker group to take effect." + else + echo "==> Docker already installed ($(docker --version | head -1))" + fi +fi + +# ── Done ───────────────────────────────────────────────────────────────────── +echo "" +if [[ "$DRY_RUN" == true ]]; then + echo "Dry run complete — nothing was installed." +else + echo "================================================================" + echo " Done. A few manual steps remain:" + echo "================================================================" + echo "" + echo " 1. Log out and back in — activates zsh (and docker group if installed)" + echo "" + echo " 2. Open a new Ghostty terminal — antidote will clone plugins" + echo " on first launch (takes ~10 seconds)" + echo "" + echo " 3. Authenticate Tailscale:" + echo " sudo tailscale up" + echo "" + echo " 4. SSH / GPG keys:" + echo " bash SSH_and_GPG/create_ssh_key.sh" + echo " bash SSH_and_GPG/create_gpg_key.sh" + echo "" + echo "================================================================" +fi diff --git a/linux-desktop/zsh_plugins.txt b/linux-desktop/zsh_plugins.txt new file mode 100644 index 0000000..145b2e2 --- /dev/null +++ b/linux-desktop/zsh_plugins.txt @@ -0,0 +1,23 @@ +# ═══════════════════════════════════════════════════════════════ +# ~/.zsh_plugins.txt — antidote plugin list +# ═══════════════════════════════════════════════════════════════ +# antidote clones these from GitHub and generates a static +# source file. Run `antidote update` to pull latest versions. + +# Extra completion definitions (load before compinit) +zsh-users/zsh-completions kind:fpath path:src + +# Fish-style autosuggestions +zsh-users/zsh-autosuggestions + +# Syntax highlighting (load near end — must see all widgets) +zsh-users/zsh-syntax-highlighting + +# Substring history search (load after syntax highlighting) +zsh-users/zsh-history-substring-search + +# Fish-style abbreviations +olets/zsh-abbr + +# Desktop notifications for long-running commands (like fish done.fish) +marzocchi/zsh-notify diff --git a/linux-desktop/zshrc.example b/linux-desktop/zshrc.example new file mode 100644 index 0000000..f9350c2 --- /dev/null +++ b/linux-desktop/zshrc.example @@ -0,0 +1,204 @@ +# ═══════════════════════════════════════════════════════════════ +# ~/.zshrc — Linux desktop zsh config (Ubuntu / Arch) +# ═══════════════════════════════════════════════════════════════ +# Requires: zsh, zsh-antidote, zoxide, fastfetch, fzf, bat, fd, eza +# Plugins managed by antidote — see ~/.zsh_plugins.txt +# Install: bash linux-desktop/setup.sh + + +# ═══════════════════════════════════════════════════════════════ +# PATH +# ═══════════════════════════════════════════════════════════════ +export PATH="$HOME/.local/bin:$PATH" + + +# ═══════════════════════════════════════════════════════════════ +# HISTORY +# ═══════════════════════════════════════════════════════════════ +HISTFILE="$HOME/.zsh_history" +HISTSIZE=10000 +SAVEHIST=10000 +setopt SHARE_HISTORY +setopt HIST_IGNORE_DUPS +setopt HIST_IGNORE_SPACE +setopt HIST_REDUCE_BLANKS + + +# ═══════════════════════════════════════════════════════════════ +# ANTIDOTE (plugin manager) +# ═══════════════════════════════════════════════════════════════ +# antidote sources all plugins listed in ~/.zsh_plugins.txt: +# zsh-completions, zsh-autosuggestions, zsh-syntax-highlighting, +# zsh-history-substring-search, zsh-abbr +if [[ -f /usr/share/zsh-antidote/antidote.zsh ]]; then + source /usr/share/zsh-antidote/antidote.zsh + antidote load +fi + + +# ═══════════════════════════════════════════════════════════════ +# COMPLETION +# ═══════════════════════════════════════════════════════════════ +autoload -Uz compinit && compinit +zstyle ':completion:*' menu select +zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' +zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}" +zstyle ':completion:*' group-name '' +zstyle ':completion:*:descriptions' format '%F{yellow}── %d ──%f' + + +# ═══════════════════════════════════════════════════════════════ +# OPTIONS +# ═══════════════════════════════════════════════════════════════ +setopt AUTO_CD +setopt CORRECT +setopt GLOB_DOTS +setopt NO_BEEP + + +# ═══════════════════════════════════════════════════════════════ +# PROMPT (branch · * unstaged · + staged · duration right) +# ═══════════════════════════════════════════════════════════════ +autoload -Uz vcs_info + +_prompt_precmd() { + local exit_code=$? + vcs_info + _last_exit=$exit_code + _cmd_end=$EPOCHSECONDS + if (( _cmd_start > 0 && _cmd_end - _cmd_start >= 2 )); then + local elapsed=$(( _cmd_end - _cmd_start )) + local mins=$(( elapsed / 60 )) + local secs=$(( elapsed % 60 )) + if (( mins > 0 )); then + _cmd_duration="${mins}m${secs}s" + else + _cmd_duration="${secs}s" + fi + else + _cmd_duration="" + fi + _cmd_start=0 +} + +_prompt_preexec() { + _cmd_start=$EPOCHSECONDS +} + +precmd_functions+=(_prompt_precmd) +preexec_functions+=(_prompt_preexec) + +zstyle ':vcs_info:*' enable git +zstyle ':vcs_info:*' check-for-changes true +zstyle ':vcs_info:git:*' stagedstr '%F{green}+%f' +zstyle ':vcs_info:git:*' unstagedstr '%F{red}*%f' +zstyle ':vcs_info:git:*' formats ' (%F{magenta}%b%f%u%c)' +zstyle ':vcs_info:git:*' actionformats ' (%F{magenta}%b%f|%F{yellow}%a%f%u%c)' +setopt PROMPT_SUBST +PROMPT='%F{blue}%n%f%F{white}@%f%F{cyan}%m%f %F{yellow}%~%f${vcs_info_msg_0_} %# ' +RPROMPT='${_cmd_duration:+%F{242}${_cmd_duration}%f}' + + +# ═══════════════════════════════════════════════════════════════ +# ALIASES +# ═══════════════════════════════════════════════════════════════ +alias ls='ls --color=auto' +if command -v eza &>/dev/null; then + alias ll='eza -lah --git --icons --group-directories-first' + alias la='eza -a --icons --group-directories-first' + alias lt='eza --tree --icons --level=2 --group-directories-first' + alias ls='eza -al --color=always --icons --group-directories-first' +else + alias ll='ls -lAh' + alias la='ls -A' +fi +alias grep='grep --color=auto' +alias df='df -h' +alias du='du -sh' +alias reload='source ~/.zshrc' + +# Ubuntu binary name shims (bat → batcat, fd → fdfind) +[[ -x /usr/bin/batcat ]] && ! command -v bat &>/dev/null && alias bat='batcat' +[[ -x /usr/bin/fdfind ]] && ! command -v fd &>/dev/null && alias fd='fdfind' + +# Colorized man pages via bat +if command -v bat &>/dev/null; then + export MANPAGER="sh -c 'col -bx | bat -l man -p'" + export MANROFFOPT="-c" +fi + + +# ═══════════════════════════════════════════════════════════════ +# ABBREVIATIONS (fish-style — expand inline, history shows real command) +# ═══════════════════════════════════════════════════════════════ +if type abbr &>/dev/null; then + abbr set -q gs='git status' 2>/dev/null + abbr set -q ga='git add' 2>/dev/null + abbr set -q gc='git commit' 2>/dev/null + abbr set -q gp='git push' 2>/dev/null + abbr set -q gl='git log --oneline --graph --decorate' 2>/dev/null + abbr set -q ..='cd ..' 2>/dev/null + abbr set -q ...='cd ../..' 2>/dev/null +fi + + +# ═══════════════════════════════════════════════════════════════ +# ENVIRONMENT +# ═══════════════════════════════════════════════════════════════ +export EDITOR='micro' +export LANG='en_US.UTF-8' +export LC_ALL='en_US.UTF-8' + + +# ═══════════════════════════════════════════════════════════════ +# ZOXIDE (smarter cd — learns frequent directories) +# ═══════════════════════════════════════════════════════════════ +command -v zoxide &>/dev/null && eval "$(zoxide init zsh)" + + +# ═══════════════════════════════════════════════════════════════ +# FZF +# ═══════════════════════════════════════════════════════════════ +# Ubuntu (apt) +[[ -f /usr/share/doc/fzf/examples/key-bindings.zsh ]] && \ + source /usr/share/doc/fzf/examples/key-bindings.zsh +[[ -f /usr/share/doc/fzf/examples/completion.zsh ]] && \ + source /usr/share/doc/fzf/examples/completion.zsh +# Arch (pacman) +[[ -f /usr/share/fzf/key-bindings.zsh ]] && \ + source /usr/share/fzf/key-bindings.zsh +[[ -f /usr/share/fzf/completion.zsh ]] && \ + source /usr/share/fzf/completion.zsh + + +# ═══════════════════════════════════════════════════════════════ +# SYNTAX HIGHLIGHTING (path underlining + bracket matching) +# ═══════════════════════════════════════════════════════════════ +ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets pattern) +ZSH_HIGHLIGHT_STYLES[path]='fg=cyan,underline' +ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=cyan,underline' + + +# ═══════════════════════════════════════════════════════════════ +# HISTORY SUBSTRING SEARCH (Up/Down filter like fish) +# ═══════════════════════════════════════════════════════════════ +bindkey '^[[A' history-substring-search-up +bindkey '^[[B' history-substring-search-down +bindkey '^[OA' history-substring-search-up +bindkey '^[OB' history-substring-search-down + + +# ═══════════════════════════════════════════════════════════════ +# NOTIFY (desktop notification after long commands) +# ═══════════════════════════════════════════════════════════════ +# Requires: notify-send (libnotify), xdotool or wmctrl (focus detection) +# Loaded via antidote (marzocchi/zsh-notify) +zstyle ':notify:*' command-complete-timeout 30 +zstyle ':notify:*' error-title "Command failed" +zstyle ':notify:*' success-title "Command finished" + + +# ═══════════════════════════════════════════════════════════════ +# SYSINFO DISPLAY (Ghostty startup) +# ═══════════════════════════════════════════════════════════════ +[[ -o interactive && "$TERM_PROGRAM" == "ghostty" ]] && fastfetch diff --git a/linux-server/homepage/config/bookmarks.yaml b/linux-server/homepage/config/bookmarks.yaml index 8b13789..e69de29 100644 --- a/linux-server/homepage/config/bookmarks.yaml +++ b/linux-server/homepage/config/bookmarks.yaml @@ -1 +0,0 @@ - diff --git a/macOS/zshrc-upgrade.md b/macOS/zshrc-upgrade.md new file mode 100644 index 0000000..e9a5e8c --- /dev/null +++ b/macOS/zshrc-upgrade.md @@ -0,0 +1,227 @@ +# macOS zshrc upgrade plan + +Bring the macOS zshrc.example up to parity with the linux-desktop config, +which now has CachyOS Fish-style enhancements via antidote. + +## Current state + +The macOS zshrc manually sources two plugins from Homebrew: + +```zsh +source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh +source $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh +``` + +No icons, no abbreviations, no substring search, no command duration, no +notify, no zoxide, no fzf keybindings, no bat man pages. + +## Target state + +Match linux-desktop/zshrc.example feature set while keeping macOS-specific +bits (Homebrew PATH, brew completions, `brewup` alias). + +## Changes required + +### 1. Switch to antidote + +**Install:** `brew install antidote` + +**Replace** the manual `source $(brew --prefix)/share/...` lines with: + +```zsh +if [[ -f $(brew --prefix)/opt/antidote/share/antidote/antidote.zsh ]]; then + source $(brew --prefix)/opt/antidote/share/antidote/antidote.zsh + antidote load +fi +``` + +**Create** `macOS/zsh_plugins.txt` (identical to linux-desktop): + +``` +zsh-users/zsh-completions kind:fpath path:src +zsh-users/zsh-autosuggestions +zsh-users/zsh-syntax-highlighting +zsh-users/zsh-history-substring-search +olets/zsh-abbr +marzocchi/zsh-notify +``` + +This replaces two Homebrew-managed plugins with six antidote-managed ones. +The Homebrew `zsh-autosuggestions` and `zsh-syntax-highlighting` formulae +can be uninstalled afterward. + +### 2. eza aliases with icons + +**Prerequisite:** A Nerd Font must be set in Ghostty (e.g., JetBrains Mono +Nerd Font). Check ghostty.config. + +**Replace** current eza block: + +```zsh +alias ls='ls -G' +if command -v eza &>/dev/null; then + alias ll='eza -lah --git' + alias la='eza -a' +else + ... +fi +``` + +**With:** + +```zsh +alias ls='ls -G' +if command -v eza &>/dev/null; then + alias ls='eza -al --color=always --icons --group-directories-first' + alias ll='eza -lah --git --icons --group-directories-first' + alias la='eza -a --icons --group-directories-first' + alias lt='eza --tree --icons --level=2 --group-directories-first' +else + ... +fi +``` + +**Packages needed:** `brew install eza` (may already be installed). + +### 3. RPROMPT with command duration + +**Add** the `_prompt_precmd` / `_prompt_preexec` hooks and RPROMPT from +linux-desktop/zshrc.example. Replace the simple `precmd() { vcs_info }` +with the full precmd that also tracks command duration. + +```zsh +_prompt_precmd() { + local exit_code=$? + vcs_info + _last_exit=$exit_code + _cmd_end=$EPOCHSECONDS + if (( _cmd_start > 0 && _cmd_end - _cmd_start >= 2 )); then + local elapsed=$(( _cmd_end - _cmd_start )) + local mins=$(( elapsed / 60 )) + local secs=$(( elapsed % 60 )) + if (( mins > 0 )); then + _cmd_duration="${mins}m${secs}s" + else + _cmd_duration="${secs}s" + fi + else + _cmd_duration="" + fi + _cmd_start=0 +} + +_prompt_preexec() { _cmd_start=$EPOCHSECONDS } + +precmd_functions+=(_prompt_precmd) +preexec_functions+=(_prompt_preexec) + +RPROMPT='${_cmd_duration:+%F{242}${_cmd_duration}%f}' +``` + +### 4. Colorized man pages via bat + +```zsh +if command -v bat &>/dev/null; then + export MANPAGER="sh -c 'col -bx | bat -l man -p'" + export MANROFFOPT="-c" +fi +``` + +**Packages needed:** `brew install bat` (may already be installed). + +### 5. zsh-notify + +On macOS, zsh-notify uses `terminal-notifier` instead of `notify-send`. + +**Packages needed:** `brew install terminal-notifier` + +**Config:** + +```zsh +zstyle ':notify:*' command-complete-timeout 30 +zstyle ':notify:*' error-title "Command failed" +zstyle ':notify:*' success-title "Command finished" +``` + +No `xdotool` needed — macOS focus detection uses native AppleScript. + +### 6. Fish-style abbreviations + +```zsh +if type abbr &>/dev/null; then + abbr set -q gs='git status' 2>/dev/null + abbr set -q ga='git add' 2>/dev/null + abbr set -q gc='git commit' 2>/dev/null + abbr set -q gp='git push' 2>/dev/null + abbr set -q gl='git log --oneline --graph --decorate' 2>/dev/null + abbr set -q ..='cd ..' 2>/dev/null + abbr set -q ...='cd ../..' 2>/dev/null +fi +``` + +This replaces the existing static git aliases (`alias gs=...`) — abbr +expands inline so your history shows the real command. + +### 7. History substring search + +```zsh +bindkey '^[[A' history-substring-search-up +bindkey '^[[B' history-substring-search-down +bindkey '^[OA' history-substring-search-up +bindkey '^[OB' history-substring-search-down +``` + +### 8. Completion enhancements + +```zsh +zstyle ':completion:*' group-name '' +zstyle ':completion:*:descriptions' format '%F{yellow}── %d ──%f' +``` + +### 9. Syntax highlighting config + +```zsh +ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets pattern) +ZSH_HIGHLIGHT_STYLES[path]='fg=cyan,underline' +ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=cyan,underline' +``` + +### 10. Zoxide + +```zsh +command -v zoxide &>/dev/null && eval "$(zoxide init zsh)" +``` + +**Packages needed:** `brew install zoxide` (may already be installed). + +### 11. FZF keybindings (Homebrew paths) + +```zsh +[[ -f $(brew --prefix)/opt/fzf/shell/key-bindings.zsh ]] && \ + source $(brew --prefix)/opt/fzf/shell/key-bindings.zsh +[[ -f $(brew --prefix)/opt/fzf/shell/completion.zsh ]] && \ + source $(brew --prefix)/opt/fzf/shell/completion.zsh +``` + +**Packages needed:** `brew install fzf` (may already be installed). + +## Packages to install (Homebrew) + +```bash +brew install antidote eza bat zoxide fzf terminal-notifier +``` + +## Packages to uninstall (replaced by antidote) + +```bash +brew uninstall zsh-autosuggestions zsh-syntax-highlighting +``` + +## Migration steps + +1. Install new Homebrew packages +2. Create `~/.zsh_plugins.txt` from `macOS/zsh_plugins.txt` +3. Replace `~/.zshrc` with updated `macOS/zshrc.example` +4. Open new Ghostty terminal (antidote clones plugins on first load) +5. Uninstall replaced Homebrew formulae +6. Verify: icons, abbreviations, notifications, man pages, duration diff --git a/macOS/zshrc.example b/macOS/zshrc.example index 4d5d2d0..6027838 100644 --- a/macOS/zshrc.example +++ b/macOS/zshrc.example @@ -120,4 +120,4 @@ export PATH="$PATH:/Users/ulises/.local/bin" # zsh auto suggestions & syntax highlighting (installed via Homebrew) source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh -source $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh \ No newline at end of file +source $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh