detect-plugins.sh 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # lib/detect-plugins.sh — Single source of truth for plugin detection
  4. # Sourced by: session-start.sh, doctor.sh, install-plugins.sh
  5. #
  6. # Each function returns 0 (detected) or 1 (not detected).
  7. # No output — callers handle messaging.
  8. # ============================================================
  9. # --- Always-on plugins ---
  10. detect_rtk() {
  11. command -v rtk &>/dev/null
  12. }
  13. detect_superpowers() {
  14. # Fast check: filesystem (plugin cache)
  15. local cache_dir="$HOME/.claude/plugins/cache"
  16. if [ -d "$cache_dir" ]; then
  17. compgen -G "$cache_dir"/*superpowers* &>/dev/null && return 0
  18. fi
  19. # Slow fallback: CLI (only if fast check fails)
  20. claude plugin list 2>/dev/null | grep -qi "superpowers" && return 0
  21. return 1
  22. }
  23. detect_security_guidance() {
  24. local cache_dir="$HOME/.claude/plugins/cache"
  25. [ -d "$cache_dir" ] && compgen -G "$cache_dir"/*security-guidance* &>/dev/null
  26. }
  27. # --- Toggle plugins ---
  28. detect_gstack() {
  29. # gstack is exposed via per-skill symlinks (browse, canary, qa, …);
  30. # the legacy top-level symlink was removed to avoid duplicate entries.
  31. # Detect by checking any of its individual skills.
  32. [ -L "$HOME/.claude/skills/browse" ] || [ -L "$HOME/.claude/skills/qa" ]
  33. }
  34. detect_gsd() {
  35. # GSD v2 (gsd-pi) is a standalone CLI, not a Claude Code plugin.
  36. # Detection: check for 'gsd' binary in PATH.
  37. command -v gsd &>/dev/null
  38. }
  39. detect_plugin_dev() {
  40. # plugin-dev replaces the old "skill-creator" reference
  41. local cache_dir="$HOME/.claude/plugins/cache"
  42. [ -d "$cache_dir" ] && compgen -G "$cache_dir"/*plugin-dev* &>/dev/null
  43. }
  44. detect_uiux_pro_max() {
  45. local cache_dir="$HOME/.claude/plugins/cache"
  46. [ -d "$cache_dir" ] && compgen -G "$cache_dir"/*ui-ux-pro-max* &>/dev/null
  47. }
  48. detect_context7() {
  49. # Context7 CLI (ctx7) — installed globally via npm
  50. command -v ctx7 &>/dev/null
  51. }
  52. detect_graphifyy() {
  53. # Graphifyy — codebase knowledge graph, installed via pipx
  54. command -v graphify &>/dev/null
  55. }
  56. detect_caveman() {
  57. # Caveman — output-token compression via caveman-speak (marketplace plugin)
  58. local cache_dir="$HOME/.claude/plugins/cache"
  59. [ -d "$cache_dir" ] && compgen -G "$cache_dir"/*caveman* &>/dev/null
  60. }
  61. # True if a plugin is registered as enabled in settings.json's
  62. # enabledPlugins map. Filesystem only (no subprocess to claude CLI).
  63. # Argument is the full "name@marketplace" key.
  64. plugin_enabled() {
  65. local key="$1"
  66. [ -f "$HOME/.claude/settings.json" ] || return 1
  67. grep -qE "\"${key}\"[[:space:]]*:[[:space:]]*true" "$HOME/.claude/settings.json"
  68. }
  69. detect_caveman_hooks() {
  70. # Standalone hooks (statusline + stats) deployed by caveman hooks/install.sh
  71. [ -f "$HOME/.claude/hooks/caveman-statusline.sh" ]
  72. }
  73. detect_caveman_shrink() {
  74. # caveman-shrink is a proxy — only valid when registered with an
  75. # upstream wrapper (e.g. caveman-shrink-fs:, caveman-shrink-github:).
  76. # Bare 'caveman-shrink:' fails health checks and is treated as missing.
  77. command -v claude &>/dev/null \
  78. && claude mcp list 2>/dev/null | grep -q '^caveman-shrink-'
  79. }
  80. # --- Plan detection ---
  81. detect_plan() {
  82. # Detect Claude plan: max, pro, or free.
  83. # Checks ~/.claude.json for model access hints.
  84. # Returns plan name on stdout, always exits 0.
  85. local claude_json="$HOME/.claude.json"
  86. if [ -f "$claude_json" ]; then
  87. # Max plan: has opus model access or max flag
  88. if grep -q '"planType".*"max"' "$claude_json" 2>/dev/null; then
  89. echo "max"; return 0
  90. fi
  91. # Check cached features for max indicators
  92. if grep -q '"tengu_cobalt_compass": true' "$claude_json" 2>/dev/null \
  93. && grep -q '"tengu_harbor": true' "$claude_json" 2>/dev/null; then
  94. echo "max"; return 0
  95. fi
  96. fi
  97. # Fallback: check if claude CLI reports plan
  98. local plan
  99. plan=$(claude config get planType 2>/dev/null || true)
  100. case "$plan" in
  101. max|Max|MAX) echo "max" ;;
  102. pro|Pro|PRO) echo "pro" ;;
  103. free|Free|FREE) echo "free" ;;
  104. *) echo "pro" ;; # default assumption
  105. esac
  106. }