From a434bb97b99e93ffe9b023c0770b3ffd3eb312f5 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 2 Jul 2026 02:02:15 -0600 Subject: [PATCH 1/2] feat(doctor): add INSTALLATION section for Homebrew distribution health MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catches the class of bug fixed in homebrew-tap PR #135: a Homebrew formula install that "succeeds" (brew reports installed, brew upgrade says "already installed") but silently fails to link — man-page name collisions on case-insensitive filesystems, and shell/Cellar version drift after an upgrade the user forgot to restart their shell for. New `_doctor_check_installation` checks, scoped to flow-cli's own Homebrew install (skips silently for git-clone/plugin-manager installs): - opt/Cellar symlink integrity (installed-but-unlinked detection) - shell-loaded FLOW_VERSION vs. installed Cellar keg version drift - flow-cli's own man pages actually resolving into the shared man1 dir (vs. losing a case-insensitive name collision to another formula, scoped to formulae that actually link — Homebrew's own keg-only convention, e.g. lua vs lua@5.4, produces same-name kegs constantly and is not a bug) New tests/e2e-doctor-install.zsh (7 scenarios, all via mocked `brew`). --- commands/doctor.zsh | 87 +++++++++++ tests/e2e-doctor-install.zsh | 288 +++++++++++++++++++++++++++++++++++ tests/run-all.sh | 1 + 3 files changed, 376 insertions(+) create mode 100755 tests/e2e-doctor-install.zsh diff --git a/commands/doctor.zsh b/commands/doctor.zsh index 45802c36a..fd3383561 100644 --- a/commands/doctor.zsh +++ b/commands/doctor.zsh @@ -129,6 +129,11 @@ doctor() { _doctor_check_cmd "git" "" "shell" _doctor_log_quiet "" + # ────────────────────────────────────────────────────────────── + # INSTALLATION (Homebrew distribution health) + # ────────────────────────────────────────────────────────────── + _doctor_check_installation + # ────────────────────────────────────────────────────────────── # REQUIRED # ────────────────────────────────────────────────────────────── @@ -1160,6 +1165,88 @@ _doctor_update_docs() { # HELPER FUNCTIONS # ============================================================================ +# ============================================================================= +# INSTALLATION / DISTRIBUTION HEALTH +# ============================================================================= +# Catches the class of bug fixed in homebrew-tap PR #135: a Homebrew formula +# install that silently fails to link (man-page name collision, stale Cellar +# keg after a formula fix) leaves the user on an old version with no error +# message — `brew upgrade` reports "already installed" even when broken. +# Checks are read-only and only apply when flow-cli was installed via brew; +# git-clone / plugin-manager installs skip silently (nothing to check). + +_doctor_check_installation() { + _doctor_log_quiet "${FLOW_COLORS[bold]}📦 INSTALLATION${FLOW_COLORS[reset]}" + + if ! command -v brew >/dev/null 2>&1; then + _doctor_log_quiet " ${FLOW_COLORS[muted]}○ Homebrew not present — skipping${FLOW_COLORS[reset]}" + _doctor_log_quiet "" + return 0 + fi + + if ! brew list --formula 2>/dev/null | grep -qx "flow-cli"; then + _doctor_log_quiet " ${FLOW_COLORS[muted]}○ flow-cli not installed via Homebrew — skipping${FLOW_COLORS[reset]}" + _doctor_log_quiet "" + return 0 + fi + + local brew_prefix + brew_prefix=$(brew --prefix 2>/dev/null) + local opt_path="${brew_prefix}/opt/flow-cli" + local installed_version + installed_version=$(brew list --versions flow-cli 2>/dev/null | awk '{print $2}') + + # 1. Link integrity — installed-but-unlinked is the exact symptom of the + # man-page-collision bug: `brew install`/`upgrade` "succeeds" but the + # opt symlink is never created, so `source $(brew --prefix)/opt/flow-cli/...` fails. + if [[ -L "$opt_path" && -d "${opt_path}/." ]]; then + _doctor_log_quiet " ${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Homebrew link ${FLOW_COLORS[muted]}(v${installed_version})${FLOW_COLORS[reset]}" + else + _doctor_log_quiet " ${FLOW_COLORS[error]}✗${FLOW_COLORS[reset]} flow-cli installed but not linked ${FLOW_COLORS[muted]}← brew link flow-cli (or: brew reinstall flow-cli)${FLOW_COLORS[reset]}" + fi + + # 2. Version drift — the shell's already-sourced $FLOW_VERSION vs. what + # Homebrew currently has installed. Catches "reinstalled/upgraded but + # forgot to restart the shell" silently running stale code. + if [[ -n "$installed_version" && -n "$FLOW_VERSION" && "$installed_version" != "$FLOW_VERSION" ]]; then + _doctor_log_quiet " ${FLOW_COLORS[warning]}⚠${FLOW_COLORS[reset]} Shell has v${FLOW_VERSION} loaded, Homebrew has v${installed_version} installed ${FLOW_COLORS[muted]}← restart your shell${FLOW_COLORS[reset]}" + fi + + # 3. Man-page link check — does every man page flow-cli's own Cellar keg + # ships actually resolve (via the shared man1 symlink) back into + # flow-cli's keg? A page that "exists" at the shared path but resolves + # into a DIFFERENT formula's Cellar dir means flow-cli's own page lost + # a case-insensitive name collision and was silently never linked (the + # PR #135 class of bug — deliberately scoped to flow-cli's own pages, + # not a whole-Cellar audit: Homebrew's own keg-only-formula convention + # (e.g. lua vs lua@5.4) produces same-name kegs constantly and is not a + # bug — only formulae that both actually attempt to link collide). + local _doctor_brew_cellar + _doctor_brew_cellar=$(brew --cellar 2>/dev/null) + local flow_man1_dir="${_doctor_brew_cellar}/flow-cli/${installed_version}/share/man/man1" + if [[ -d "$flow_man1_dir" ]]; then + local shared_man1="${brew_prefix}/share/man/man1" + local not_linked=() + # `local` is hoisted OUT of the loop deliberately: redeclaring a local + # var on every glob-driven iteration triggers a spurious "name=value" + # echo to stdout on some zsh builds (reproduced independent of this + # codebase — a fresh `zsh -f` script with the same shape shows it too). + local mp_name link_target + for manpage in "$flow_man1_dir"/*.1(N); do + mp_name="${manpage:t}" + link_target=$(readlink "${shared_man1}/${mp_name}" 2>/dev/null) + [[ "$link_target" == *"/flow-cli/"* ]] || not_linked+=("$mp_name") + done + if (( ${#not_linked[@]} > 0 )); then + _doctor_log_quiet " ${FLOW_COLORS[warning]}⚠${FLOW_COLORS[reset]} flow-cli man page(s) not linked (collision with another formula) ${FLOW_COLORS[muted]}${not_linked[*]}${FLOW_COLORS[reset]}" + else + _doctor_log_quiet " ${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} All flow-cli man pages linked cleanly" + fi + fi + + _doctor_log_quiet "" +} + _doctor_check_cmd() { local cmd="$1" local install_spec="$2" # Format: "brew" or "brew:package" or "npm:package" or "pip" diff --git a/tests/e2e-doctor-install.zsh b/tests/e2e-doctor-install.zsh new file mode 100755 index 000000000..1aa97fec8 --- /dev/null +++ b/tests/e2e-doctor-install.zsh @@ -0,0 +1,288 @@ +#!/usr/bin/env zsh +# e2e-doctor-install.zsh - End-to-end tests for `doctor`'s INSTALLATION section +# +# Targets the exact failure class fixed in homebrew-tap PR #135: a Homebrew +# formula install that "succeeds" (brew reports installed, brew upgrade says +# "already installed") but never actually links — man-page name collisions +# on case-insensitive filesystems, stale Cellar kegs after a formula fix, +# and shell/Cellar version drift after an upgrade. All of these leave the +# user silently running broken or stale code with no error message. +# +# Drives the real `_doctor_check_installation` function against a mocked +# `brew` covering: no Homebrew, not brew-installed, healthy link, broken +# link (installed-but-unlinked), version drift, and man-page collisions +# across two formulae's Cellar kegs. +# +# Usage: zsh tests/e2e-doctor-install.zsh + +SCRIPT_DIR="${0:A:h}" +PROJECT_ROOT="${SCRIPT_DIR:h}" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +CYAN='\033[0;36m' +DIM='\033[2m' +RESET='\033[0m' + +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 + +run_test() { + local test_name="$1" + local test_func="$2" + + TESTS_RUN=$((TESTS_RUN + 1)) + echo -n " ${CYAN}[$TESTS_RUN] $test_name...${RESET} " + + local output + output=$(eval "$test_func" 2>&1) + local rc=$? + + if [[ $rc -eq 0 ]]; then + echo "${GREEN}PASS${RESET}" + TESTS_PASSED=$((TESTS_PASSED + 1)) + else + echo "${RED}FAIL${RESET}" + [[ -n "$output" ]] && echo " ${DIM}${output:0:300}${RESET}" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +echo "" +echo "${CYAN}══════════════════════════════════════════════════════════════${RESET}" +echo "${CYAN} E2E: doctor INSTALLATION checks (Homebrew distribution health)${RESET}" +echo "${CYAN}══════════════════════════════════════════════════════════════${RESET}" +echo "" + +FLOW_QUIET=1 +FLOW_ATLAS_ENABLED=no +FLOW_PLUGIN_DIR="$PROJECT_ROOT" +source "$PROJECT_ROOT/flow.plugin.zsh" 2>/dev/null || { + echo "${RED}Failed to load plugin${RESET}" + exit 1 +} + +exec < /dev/null + +TEST_TMP=$(mktemp -d) +trap 'rm -rf "$TEST_TMP"' EXIT + +# Builds a fake Cellar under a given root (each scenario gets its own root +# so man-page fixtures never leak across scenarios): $1=cellar_root +# $2=formula $3=version $4... =man1 basenames to ship +_fake_keg() { + local cellar_root="$1" formula="$2" version="$3" + shift 3 + local mandir="$cellar_root/$formula/$version/share/man/man1" + mkdir -p "$mandir" + for name in "$@"; do + touch "$mandir/$name" + done +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 1: brew not present at all — must skip silently, no error +# ───────────────────────────────────────────────────────────────── +test_no_brew() { + unfunction brew 2>/dev/null + local orig_path="$PATH" + PATH="/nonexistent_bin_dir_for_test" + local out + out=$(_doctor_check_installation 2>&1) + PATH="$orig_path" + assert_output_contains "$out" "not present" +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 2: brew present, flow-cli not installed via it — skip +# ───────────────────────────────────────────────────────────────── +test_not_brew_installed() { + brew() { + case "$1 $2" in + "list --formula") echo "some-other-formula" ;; + esac + } + local out + out=$(_doctor_check_installation 2>&1) + unfunction brew + assert_output_contains "$out" "not installed via Homebrew" +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 3: healthy install — linked, version matches, no collisions +# ───────────────────────────────────────────────────────────────── +test_healthy_install() { + local cellar="$TEST_TMP/cellar-healthy" + local optdir="$TEST_TMP/prefix-healthy/opt/flow-cli" + mkdir -p "$cellar/flow-cli/${FLOW_VERSION}" + mkdir -p "$TEST_TMP/prefix-healthy/opt" + ln -sfn "$cellar/flow-cli/${FLOW_VERSION}" "$optdir" + + brew() { + if [[ "$1 $2" == "list --formula" ]]; then echo "flow-cli" + elif [[ "$1 $2" == "list --versions" && "$3" == "flow-cli" ]]; then echo "flow-cli ${FLOW_VERSION}" + elif [[ "$1" == "--prefix" ]]; then echo "$TEST_TMP/prefix-healthy" + elif [[ "$1" == "--cellar" ]]; then echo "$cellar" + fi + } + + local out + out=$(_doctor_check_installation 2>&1) + unfunction brew + + assert_output_contains "$out" "Homebrew link" && + assert_output_not_contains "$out" "not linked" && + assert_output_not_contains "$out" "drift" && + assert_output_not_contains "$out" "Shell has" +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 4: installed but NOT linked (the PR #135 symptom before the fix) +# ───────────────────────────────────────────────────────────────── +test_installed_not_linked() { + local cellar="$TEST_TMP/cellar-broken" + mkdir -p "$cellar/flow-cli/${FLOW_VERSION}" + mkdir -p "$TEST_TMP/prefix-broken/opt" + # deliberately do NOT create the opt symlink + + brew() { + if [[ "$1 $2" == "list --formula" ]]; then echo "flow-cli" + elif [[ "$1 $2" == "list --versions" && "$3" == "flow-cli" ]]; then echo "flow-cli ${FLOW_VERSION}" + elif [[ "$1" == "--prefix" ]]; then echo "$TEST_TMP/prefix-broken" + elif [[ "$1" == "--cellar" ]]; then echo "$cellar" + fi + } + + local out + out=$(_doctor_check_installation 2>&1) + unfunction brew + + assert_output_contains "$out" "not linked" +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 5: version drift — shell's loaded version != installed keg +# ───────────────────────────────────────────────────────────────── +test_version_drift() { + local cellar="$TEST_TMP/cellar-drift" + local optdir="$TEST_TMP/prefix-drift/opt/flow-cli" + mkdir -p "$cellar/flow-cli/99.99.99" + mkdir -p "$TEST_TMP/prefix-drift/opt" + ln -sfn "$cellar/flow-cli/99.99.99" "$optdir" + + brew() { + if [[ "$1 $2" == "list --formula" ]]; then echo "flow-cli" + elif [[ "$1 $2" == "list --versions" && "$3" == "flow-cli" ]]; then echo "flow-cli 99.99.99" + elif [[ "$1" == "--prefix" ]]; then echo "$TEST_TMP/prefix-drift" + elif [[ "$1" == "--cellar" ]]; then echo "$cellar" + fi + } + + local out + out=$(_doctor_check_installation 2>&1) + unfunction brew + + assert_output_contains "$out" "99.99.99" && + assert_output_contains "$out" "restart your shell" +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 6: flow-cli's own man page lost a collision — its r.1 was never +# linked into the shared man1 dir because the `r` formula's R.1 took the +# case-insensitive slot instead (the exact PR #135 symptom, pre-fix). +# ───────────────────────────────────────────────────────────────── +test_manpage_collision_detected() { + local cellar="$TEST_TMP/cellar-collide" + local prefix="$TEST_TMP/prefix-collide" + local optdir="$prefix/opt/flow-cli" + mkdir -p "$prefix/opt" + _fake_keg "$cellar" "flow-cli" "${FLOW_VERSION}" "agenda.1" "r.1" + _fake_keg "$cellar" "r" "4.5.0" "R.1" + ln -sfn "$cellar/flow-cli/${FLOW_VERSION}" "$optdir" + + # Simulate the shared man1 dir after a lost collision: agenda.1 links to + # flow-cli as expected, but the r.1 slot resolved to the `r` formula's + # keg instead of flow-cli's. + mkdir -p "$prefix/share/man/man1" + ln -sfn "$cellar/flow-cli/${FLOW_VERSION}/share/man/man1/agenda.1" "$prefix/share/man/man1/agenda.1" + ln -sfn "$cellar/r/4.5.0/share/man/man1/R.1" "$prefix/share/man/man1/r.1" + + brew() { + if [[ "$1 $2" == "list --formula" ]]; then echo "flow-cli" + elif [[ "$1 $2" == "list --versions" && "$3" == "flow-cli" ]]; then echo "flow-cli ${FLOW_VERSION}" + elif [[ "$1" == "--prefix" ]]; then echo "$prefix" + elif [[ "$1" == "--cellar" ]]; then echo "$cellar" + fi + } + + local out + out=$(_doctor_check_installation 2>&1) + unfunction brew + + assert_output_contains "$out" "not linked (collision" && + assert_output_contains "$out" "r.1" +} + +# ───────────────────────────────────────────────────────────────── +# Scenario 7: all of flow-cli's own man pages link cleanly — including +# alongside an unrelated formula (jq) that ships its own, non-colliding page. +# ───────────────────────────────────────────────────────────────── +test_no_manpage_collision() { + local cellar="$TEST_TMP/cellar-clean" + local prefix="$TEST_TMP/prefix-clean" + local optdir="$prefix/opt/flow-cli" + mkdir -p "$prefix/opt" + _fake_keg "$cellar" "flow-cli" "${FLOW_VERSION}" "agenda.1" "dash.1" + _fake_keg "$cellar" "jq" "1.7" "jq.1" + ln -sfn "$cellar/flow-cli/${FLOW_VERSION}" "$optdir" + + mkdir -p "$prefix/share/man/man1" + ln -sfn "$cellar/flow-cli/${FLOW_VERSION}/share/man/man1/agenda.1" "$prefix/share/man/man1/agenda.1" + ln -sfn "$cellar/flow-cli/${FLOW_VERSION}/share/man/man1/dash.1" "$prefix/share/man/man1/dash.1" + ln -sfn "$cellar/jq/1.7/share/man/man1/jq.1" "$prefix/share/man/man1/jq.1" + + brew() { + if [[ "$1 $2" == "list --formula" ]]; then echo "flow-cli" + elif [[ "$1 $2" == "list --versions" && "$3" == "flow-cli" ]]; then echo "flow-cli ${FLOW_VERSION}" + elif [[ "$1" == "--prefix" ]]; then echo "$prefix" + elif [[ "$1" == "--cellar" ]]; then echo "$cellar" + fi + } + + local out + out=$(_doctor_check_installation 2>&1) + unfunction brew + + assert_output_contains "$out" "All flow-cli man pages linked cleanly" +} + +# ───────────────────────────────────────────────────────────────── +# Assertion helpers (self-contained, mirrors test-framework.zsh's contract) +# ───────────────────────────────────────────────────────────────── +assert_output_contains() { + local haystack="$1" needle="$2" + [[ "$haystack" == *"$needle"* ]] || { echo "expected to contain: $needle"; return 1; } +} + +assert_output_not_contains() { + local haystack="$1" needle="$2" + [[ "$haystack" != *"$needle"* ]] || { echo "expected NOT to contain: $needle"; return 1; } +} + +run_test "no brew on PATH → skip" test_no_brew +run_test "brew present, flow-cli not installed via it → skip" test_not_brew_installed +run_test "healthy install → linked, version matches, no collisions" test_healthy_install +run_test "installed but not linked → flagged" test_installed_not_linked +run_test "version drift (shell vs Cellar) → flagged" test_version_drift +run_test "man-page collision across formulae → detected" test_manpage_collision_detected +run_test "no man-page collision → clean" test_no_manpage_collision + +echo "" +echo "${CYAN}──────────────────────────────────────────────────────────────${RESET}" +echo " Results: ${GREEN}${TESTS_PASSED} passed${RESET}, ${RED}${TESTS_FAILED} failed${RESET} (${TESTS_RUN} total)" +echo "${CYAN}──────────────────────────────────────────────────────────────${RESET}" +echo "" + +[[ $TESTS_FAILED -eq 0 ]] diff --git a/tests/run-all.sh b/tests/run-all.sh index 6c0bbaa8e..03ad8341d 100755 --- a/tests/run-all.sh +++ b/tests/run-all.sh @@ -164,6 +164,7 @@ run_test ./tests/e2e-scholar-config-sync.zsh run_test ./tests/e2e-tok-sync.zsh run_test ./tests/e2e-agenda.zsh run_test ./tests/e2e-agenda-atlas.zsh +run_test ./tests/e2e-doctor-install.zsh echo "" echo "Atlas contract tests:" From 4f000a078cf41bdb27d143503f8363529e5064f2 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 2 Jul 2026 02:03:44 -0600 Subject: [PATCH 2/2] docs(doctor): document INSTALLATION check category Adds the new section to REFCARD-DOCTOR.md's check-category list (renumbering the categories after it) and to both CHANGELOGs. --- CHANGELOG.md | 4 ++++ docs/CHANGELOG.md | 4 ++++ docs/reference/REFCARD-DOCTOR.md | 29 +++++++++++++++++++---------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d96feb0..c98153ade 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **`doctor` INSTALLATION section** — detects Homebrew distribution failures that leave a user silently on a broken or stale install: opt/Cellar symlink integrity (installed-but-unlinked), shell-loaded version vs. installed Cellar keg drift, and flow-cli's own man pages losing a case-insensitive name collision to another formula (the class of bug fixed in homebrew-tap PR #135). Skips silently for non-Homebrew installs. + ## [7.14.0] — 2026-07-02 — planning-coordination: shared accessors + dark-ready atlas agenda + .STATUS enforcer ### Added diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 702242d73..2d50005df 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,10 @@ The format follows [Keep a Changelog](https://keepachangelog.com/), and this pro ## [Unreleased] +### Added + +- **`doctor` INSTALLATION section** — detects Homebrew distribution failures that leave a user silently on a broken or stale install: opt/Cellar symlink integrity (installed-but-unlinked), shell-loaded version vs. installed Cellar keg drift, and flow-cli's own man pages losing a case-insensitive name collision to another formula (the class of bug fixed in homebrew-tap PR #135). Skips silently for non-Homebrew installs. + ## [7.14.0] — 2026-07-02 — planning-coordination: shared accessors + dark-ready atlas agenda + .STATUS enforcer ### Added diff --git a/docs/reference/REFCARD-DOCTOR.md b/docs/reference/REFCARD-DOCTOR.md index 4071c9095..a80bbc2bc 100644 --- a/docs/reference/REFCARD-DOCTOR.md +++ b/docs/reference/REFCARD-DOCTOR.md @@ -96,11 +96,20 @@ After each run, teach doctor writes `.flow/doctor-status.json`. The health dot s - `zsh` - Shell - `git` - Version control -### 2. Required Tools +### 2. Installation (Homebrew distribution health) + +- Homebrew opt/Cellar link integrity (installed-but-unlinked detection) +- Shell-loaded version vs. installed Cellar keg version drift +- flow-cli's own man pages resolving cleanly (vs. losing a case-insensitive + name collision to another formula) + +Skips silently for non-Homebrew installs (git-clone, plugin manager). + +### 3. Required Tools - `fzf` - Fuzzy finder -### 3. Recommended Tools +### 4. Recommended Tools - `eza` - Enhanced ls - `bat` - Enhanced cat @@ -108,7 +117,7 @@ After each run, teach doctor writes `.flow/doctor-status.json`. The health dot s - `fd` - Enhanced find - `rg` (ripgrep) - Enhanced grep -### 4. Optional Tools +### 5. Optional Tools - `dust` - Disk usage - `duf` - Disk free @@ -117,12 +126,12 @@ After each run, teach doctor writes `.flow/doctor-status.json`. The health dot s - `gh` - GitHub CLI - `jq` - JSON processor -### 5. Integrations +### 6. Integrations - `atlas` - State management - `radian` - R console (if R exists) -### 6. Email (conditional — when `em` loaded) +### 7. Email (conditional — when `em` loaded) - `himalaya` - Email CLI backend (required, version >= 1.0.0) - `w3m`/`lynx`/`pandoc` - HTML rendering (any-of, recommended) @@ -132,33 +141,33 @@ After each run, teach doctor writes `.flow/doctor-status.json`. The health dot s - `claude`/`gemini` - AI backend (conditional on `$FLOW_EMAIL_AI`) - Config summary: AI backend, timeout, page size, folder, config file -### 7. ZSH Plugin Manager +### 8. ZSH Plugin Manager Checks: - antidote/zinit/oh-my-zsh installed - Plugin bundle file -### 8. ZSH Plugins +### 9. ZSH Plugins - powerlevel10k - zsh-autosuggestions - zsh-syntax-highlighting - zsh-completions -### 9. flow-cli Status +### 10. flow-cli Status - Plugin loaded - Version - Atlas connection -### 10. GitHub Token +### 11. GitHub Token - Token configured - Token validity - Token expiration - Token-dependent services (gh CLI, Claude MCP) -### 11. Aliases +### 12. Aliases - Total alias count - Shadow detection