session-start.sh 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # Claude Code — Session start plugin status
  4. # Runs once per session. Zero API calls. Filesystem only.
  5. # ============================================================
  6. # ── Quick health check (filesystem only, no subprocesses) ──
  7. BROKEN=()
  8. for f in CLAUDE.md settings.json agents skills; do
  9. [ ! -e "$HOME/.claude/$f" ] && BROKEN+=("$f")
  10. done
  11. if [ ${#BROKEN[@]} -gt 0 ]; then
  12. # Try to find the repo path from an existing symlink
  13. _repo_hint=""
  14. for _probe in CLAUDE.md settings.json; do
  15. if [ -L "$HOME/.claude/$_probe" ]; then
  16. _repo_hint="$(cd "$(dirname "$(readlink "$HOME/.claude/$_probe")")" 2>/dev/null && pwd)"
  17. break
  18. fi
  19. done
  20. _fix_cmd="${_repo_hint:+cd $_repo_hint && }bash link.sh"
  21. echo ""
  22. echo "┌─ ⚠️ CONFIG ISSUES ────────────────────────────────┐"
  23. for b in "${BROKEN[@]}"; do
  24. printf "│ MISSING: ~/.claude/%-30s│\n" "$b"
  25. done
  26. printf "│ → %-47s│\n" "$_fix_cmd"
  27. echo "│ → /health for full diagnostic │"
  28. echo "└───────────────────────────────────────────────────┘"
  29. unset _repo_hint _fix_cmd
  30. fi
  31. # ── Load shared detection library ──
  32. _lib="$(dirname "${BASH_SOURCE[0]}")/../lib/detect-plugins.sh"
  33. if [ -f "$_lib" ]; then
  34. # shellcheck source=../lib/detect-plugins.sh
  35. source "$_lib"
  36. else
  37. echo "⚠️ lib/detect-plugins.sh not found — config broken, run: bash link.sh"
  38. exit 0
  39. fi
  40. unset _lib
  41. # ── Toggle plugin detection ──
  42. TOGGLE_ACTIVE=()
  43. TOGGLE_INACTIVE=()
  44. for plugin in gstack uiux_pro_max plugin_dev context7 graphifyy; do
  45. # Map function name to display name
  46. case "$plugin" in
  47. uiux_pro_max) display="ui-ux-pro-max" ;;
  48. plugin_dev) display="plugin-dev" ;;
  49. *) display="$plugin" ;;
  50. esac
  51. if "detect_$plugin" 2>/dev/null; then
  52. TOGGLE_ACTIVE+=("$display")
  53. else
  54. TOGGLE_INACTIVE+=("$display")
  55. fi
  56. done
  57. # --- Format output ---
  58. ACTIVE_STR="${TOGGLE_ACTIVE[*]:-none}"
  59. INACTIVE_STR="${TOGGLE_INACTIVE[*]:-none}"
  60. # GSD v2 — standalone CLI (not a Claude Code plugin — shown separately)
  61. if detect_gsd 2>/dev/null; then
  62. GSD_STATUS="gsd v2 ✓"
  63. else
  64. GSD_STATUS="gsd v2 ✗ (npm install -g gsd-pi)"
  65. fi
  66. # Version detection: follow CLAUDE.md symlink back to repo, then read version.txt
  67. _claude_real="$(readlink "$HOME/.claude/CLAUDE.md" 2>/dev/null || true)"
  68. if [ -n "$_claude_real" ]; then
  69. _repo_dir="$(cd "$(dirname "$_claude_real")" 2>/dev/null && pwd)"
  70. CONFIG_VERSION=$(cat "$_repo_dir/version.txt" 2>/dev/null || echo "?")
  71. else
  72. CONFIG_VERSION="?"
  73. fi
  74. REPO_DIR="${_repo_dir:-}"
  75. unset _claude_real _repo_dir
  76. # Detect plan and set passive token budget
  77. PLAN=$(detect_plan 2>/dev/null || echo "pro")
  78. case "$PLAN" in
  79. max) _budget=20000; PLAN_LABEL="Max" ;;
  80. pro) _budget=11000; PLAN_LABEL="Pro" ;;
  81. free) _budget=5000; PLAN_LABEL="Free" ;;
  82. *) _budget=11000; PLAN_LABEL="Pro" ;;
  83. esac
  84. # Quick passive token cost estimate
  85. # Only count plugins that are ACTIVE (detected as ON), not just installed
  86. _passive_t=0
  87. detect_superpowers 2>/dev/null && _passive_t=$((_passive_t + 800))
  88. # Token costs for toggle plugins — map display name to cost
  89. declare -A _plugin_costs=(
  90. [gstack]=2750
  91. [ui-ux-pro-max]=400
  92. [plugin-dev]=100
  93. [context7]=200
  94. [graphifyy]=300
  95. )
  96. for _p in "${TOGGLE_ACTIVE[@]}"; do
  97. _cost="${_plugin_costs[$_p]:-0}"
  98. _passive_t=$((_passive_t + _cost))
  99. done
  100. _budget_pct=$((_passive_t * 100 / _budget))
  101. if [ "$_budget_pct" -gt 50 ]; then
  102. TOKEN_WARN="⚠️ ~${_passive_t}t passif (${_budget_pct}% budget $PLAN_LABEL)"
  103. elif [ "$_budget_pct" -gt 25 ]; then
  104. TOKEN_WARN="~${_passive_t}t passif (${_budget_pct}% budget $PLAN_LABEL)"
  105. else
  106. TOKEN_WARN=""
  107. fi
  108. unset _passive_t _budget_pct _budget
  109. echo ""
  110. echo "┌─ Claude Code config ──────────────────────────────────┐"
  111. printf "│ ✅ ON : %-40s│\n" "security-guidance rtk superpowers"
  112. # Plugin display — all plugins shown, split across 2 lines if >4
  113. _active_count=${#TOGGLE_ACTIVE[@]}
  114. _inactive_count=${#TOGGLE_INACTIVE[@]}
  115. if [ "$_active_count" -eq 0 ]; then
  116. printf "│ 🟢 ON : %-40s│\n" "none"
  117. elif [ "$_active_count" -le 4 ]; then
  118. printf "│ 🟢 ON : %-40s│\n" "$ACTIVE_STR"
  119. else
  120. # Split: first 4 on line 1, rest on continuation line
  121. _line1="${TOGGLE_ACTIVE[0]} ${TOGGLE_ACTIVE[1]} ${TOGGLE_ACTIVE[2]} ${TOGGLE_ACTIVE[3]}"
  122. _rest=("${TOGGLE_ACTIVE[@]:4}")
  123. _line2="${_rest[*]}"
  124. printf "│ 🟢 ON : %-40s│\n" "$_line1"
  125. printf "│ %-40s│\n" "$_line2"
  126. unset _line1 _line2 _rest
  127. fi
  128. if [ "$_inactive_count" -eq 0 ]; then
  129. printf "│ ⚫ OFF : %-40s│\n" "none"
  130. elif [ "$_inactive_count" -le 4 ]; then
  131. printf "│ ⚫ OFF : %-40s│\n" "$INACTIVE_STR"
  132. else
  133. _line1="${TOGGLE_INACTIVE[0]} ${TOGGLE_INACTIVE[1]} ${TOGGLE_INACTIVE[2]} ${TOGGLE_INACTIVE[3]}"
  134. _rest=("${TOGGLE_INACTIVE[@]:4}")
  135. _line2="${_rest[*]}"
  136. printf "│ ⚫ OFF : %-40s│\n" "$_line1"
  137. printf "│ %-40s│\n" "$_line2"
  138. unset _line1 _line2 _rest
  139. fi
  140. unset _active_count _inactive_count
  141. printf "│ 🖥️ CLI : %-40s│\n" "$GSD_STATUS"
  142. [ -n "$TOKEN_WARN" ] && printf "│ 💰 %-44s│\n" "${TOKEN_WARN:0:44}"
  143. printf "│ 📦 v%-45s│\n" "$CONFIG_VERSION"
  144. # Version check: compare local vs remote (non-blocking)
  145. _remote_ver=""
  146. if [ -n "$REPO_DIR" ] && [ -d "$REPO_DIR/.git" ]; then
  147. _remote_ver=$(cd "$REPO_DIR" 2>/dev/null && git fetch origin --quiet 2>/dev/null && git show origin/master:version.txt 2>/dev/null || true)
  148. fi
  149. if [ -n "$_remote_ver" ] && [ "$_remote_ver" != "$CONFIG_VERSION" ]; then
  150. printf "│ 🔄 update available: v%-27s│\n" "$_remote_ver"
  151. fi
  152. unset _remote_ver REPO_DIR
  153. echo "│ 💡 /plugin-check before starting a new project │"
  154. echo "│ 🩺 /health to run full diagnostic │"
  155. echo "└───────────────────────────────────────────────────┘"
  156. echo ""
  157. unset TOKEN_WARN