#!/bin/bash # # script/doctor # Check dotfiles health and report any issues # Only emit ANSI color when stdout is a TTY; otherwise a piped or # redirected invocation (e.g. `./script/doctor > doctor.log`) would # embed raw escape codes. if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color else RED='' GREEN='' YELLOW='' NC='' fi passed=0 failed=0 warned=0 ok() { printf " ${GREEN}[OK]${NC} %s\n" "$1" passed=$((passed + 1)) } missing() { printf " ${RED}[MISSING]${NC} %s\n" "$1" failed=$((failed + 1)) } warn() { printf " ${YELLOW}[WARN]${NC} %s\n" "$1" warned=$((warned + 1)) } check_cmd() { if command -v "$1" &>/dev/null; then ok "$1" return 0 else missing "$1" return 1 fi } echo "=== Dotfiles Health Check ===" echo # ------------------------- echo "Core tools:" check_cmd nvim check_cmd fish check_cmd tmux check_cmd git check_cmd starship echo # ------------------------- echo "Development tools:" check_cmd mise check_cmd fzf check_cmd rg check_cmd bat check_cmd lazygit check_cmd jq check_cmd gh echo # ------------------------- # Mason is disabled in the Neovim config, so these come from mise # (common/.config/mise/config.toml) — `make mise` installs them. echo "Language servers:" check_cmd lua-language-server check_cmd harper-ls check_cmd bash-language-server check_cmd ruby-lsp check_cmd gopls echo # ------------------------- echo "Linters & formatters:" check_cmd shellcheck check_cmd stylua check_cmd gofumpt check_cmd goimports echo # ------------------------- echo "Shell configuration:" if [[ "$SHELL" == *"fish"* ]]; then ok "Default shell is Fish" else warn "Default shell is not Fish ($SHELL)" fi if [[ -f "$HOME/.config/fish/local_env.fish" ]]; then ok "local_env.fish exists" else warn "local_env.fish not found (copy from local_env.fish.example)" fi echo # ------------------------- echo "Git configuration:" if git config user.name &>/dev/null; then ok "user.name: $(git config user.name)" else missing "git user.name not set" fi if git config user.email &>/dev/null; then ok "user.email: $(git config user.email)" else missing "git user.email not set" fi if [[ "$(git config commit.gpgsign)" == "true" ]] \ && [[ "$(git config gpg.format)" == "ssh" ]]; then signing_program=$(git config gpg.ssh.program) if [[ -x "$signing_program" ]]; then ok "SSH commit signing enabled via 1Password" else warn "SSH signing program unavailable ($signing_program)" fi else warn "SSH commit signing not enabled" fi # Drift check: setup-git-config owns rendering and can compare its expected # output with the live file without changing it. REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" IDENTITY_FILE="$HOME/.config/local/git/identity.gitconfig" LIVE_CONFIG="$HOME/.config/git/config" if [[ -f "$LIVE_CONFIG" ]] \ && [[ -n "${CODESPACES:-}" || -f "$IDENTITY_FILE" ]]; then if "$REPO_ROOT/script/setup-git-config" --check; then # shellcheck disable=SC2088 # tilde is intentionally literal in display text ok '~/.config/git/config matches template' else # shellcheck disable=SC2088 warn '~/.config/git/config drifted from template (run: make regen-git-config)' fi fi echo # ------------------------- echo "Runtime versions (mise):" if command -v mise &>/dev/null; then mise list 2>/dev/null | while read -r line; do echo " $line" done if [[ -z "$(mise list 2>/dev/null)" ]]; then warn "No runtimes installed (run: mise install)" fi else warn "mise not available" fi echo # ------------------------- echo "Neovim:" if command -v nvim &>/dev/null; then nvim_version=$(nvim --version | head -1) ok "$nvim_version" # Check if LazyVim is bootstrapped if [[ -d "$HOME/.local/share/nvim/lazy/LazyVim" ]]; then ok "LazyVim installed" else warn "LazyVim not installed (open nvim to bootstrap)" fi fi echo # ------------------------- echo "Tmux:" if command -v tmux &>/dev/null; then tmux_version=$(tmux -V) ok "$tmux_version" if [[ -d "$HOME/.tmux/plugins/tpm" ]]; then ok "TPM installed" else warn "TPM not installed (run: git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm)" fi fi echo # ------------------------- echo "=== Summary ===" printf " ${GREEN}Passed:${NC} %d\n" "$passed" printf " ${YELLOW}Warnings:${NC} %d\n" "$warned" printf " ${RED}Failed:${NC} %d\n" "$failed" echo if [[ $failed -gt 0 ]]; then echo "Run 'make brew' for shell/terminal tools, 'make mise' for runtimes," echo "language servers, linters, and formatters." exit 1 elif [[ $warned -gt 0 ]]; then echo "Some optional items need attention." exit 0 else echo "Everything looks good!" exit 0 fi