toggle-external.sh 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # lib/toggle-external.sh — enable/disable non-plugin tools
  4. #
  5. # Marketplace plugins are toggled by `claude plugin enable|disable`.
  6. # Tools distributed outside the marketplace (gstack submodule, emil
  7. # curl install, npx-installed skills) have no such lever — they live
  8. # as symlinks inside skills/. This script moves those symlinks
  9. # to/from skills-disabled/ so Claude Code stops/starts scanning them.
  10. #
  11. # Usage:
  12. # toggle-external.sh list
  13. # toggle-external.sh status <tool>
  14. # toggle-external.sh enable <tool>
  15. # toggle-external.sh disable <tool>
  16. #
  17. # Managed tools:
  18. # gstack — per-skill symlinks populated by gstack's own setup
  19. # emil-design-eng — single symlink → skills-external/emil-design-eng
  20. # darwin-skill — single symlink → ~/.agents/skills/darwin-skill
  21. # find-skills — single symlink → ~/.agents/skills/find-skills
  22. # ============================================================
  23. set -euo pipefail
  24. REPO="$(cd "$(dirname "$0")/.." && pwd)"
  25. SKILLS_DIR="$REPO/skills"
  26. DISABLED_DIR="$REPO/skills-disabled"
  27. GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
  28. ok() { echo -e "${GREEN}✓${NC} $1"; }
  29. warn() { echo -e "${YELLOW}⚠${NC} $1"; }
  30. err() { echo -e "${RED}✗${NC} $1"; }
  31. # All non-plugin tools this script can toggle.
  32. MANAGED_TOOLS=(gstack emil-design-eng darwin-skill find-skills)
  33. # Prints the names (directory basenames) that belong to "gstack".
  34. # Source of truth: skills-external/gstack/*/SKILL.md. The repo's
  35. # skills/<name> symlinks are generated from these by gstack ./setup.
  36. gstack_skills() {
  37. local gstack_src="$REPO/skills-external/gstack"
  38. [ -d "$gstack_src" ] || return 0
  39. for d in "$gstack_src"/*/; do
  40. [ -f "${d}SKILL.md" ] || continue
  41. basename "$d"
  42. done
  43. }
  44. # Prints "enabled" / "disabled" / "missing" for a tool.
  45. status_tool() {
  46. local tool="$1"
  47. case "$tool" in
  48. gstack)
  49. [ -d "$REPO/skills-external/gstack" ] || { echo "missing"; return; }
  50. while read -r name; do
  51. [ -e "$SKILLS_DIR/$name" ] && { echo "enabled"; return; }
  52. done < <(gstack_skills)
  53. echo "disabled"
  54. ;;
  55. emil-design-eng)
  56. [ -d "$REPO/skills-external/emil-design-eng" ] || { echo "missing"; return; }
  57. [ -e "$SKILLS_DIR/emil-design-eng" ] && echo "enabled" || echo "disabled"
  58. ;;
  59. darwin-skill|find-skills)
  60. [ -d "$HOME/.agents/skills/$tool" ] || { echo "missing"; return; }
  61. [ -e "$SKILLS_DIR/$tool" ] && echo "enabled" || echo "disabled"
  62. ;;
  63. *)
  64. echo "unknown"; return 1 ;;
  65. esac
  66. }
  67. disable_tool() {
  68. local tool="$1"
  69. mkdir -p "$DISABLED_DIR"
  70. case "$tool" in
  71. gstack)
  72. local moved=0
  73. while read -r name; do
  74. [ -e "$SKILLS_DIR/$name" ] || continue
  75. mv "$SKILLS_DIR/$name" "$DISABLED_DIR/gstack__$name"
  76. moved=$((moved + 1))
  77. done < <(gstack_skills)
  78. ok "gstack disabled ($moved symlinks moved)"
  79. ;;
  80. emil-design-eng|darwin-skill|find-skills)
  81. if [ -e "$SKILLS_DIR/$tool" ]; then
  82. mv "$SKILLS_DIR/$tool" "$DISABLED_DIR/$tool"
  83. ok "$tool disabled"
  84. else
  85. warn "$tool already disabled"
  86. fi
  87. ;;
  88. *) err "Unknown tool: $tool"; return 1 ;;
  89. esac
  90. }
  91. enable_tool() {
  92. local tool="$1"
  93. case "$tool" in
  94. gstack)
  95. local moved=0
  96. if [ -d "$DISABLED_DIR" ]; then
  97. for entry in "$DISABLED_DIR"/gstack__*; do
  98. [ -e "$entry" ] || continue
  99. local name
  100. name="$(basename "$entry" | sed 's/^gstack__//')"
  101. mv "$entry" "$SKILLS_DIR/$name"
  102. moved=$((moved + 1))
  103. done
  104. fi
  105. if [ "$moved" -eq 0 ]; then
  106. warn "gstack was not disabled — re-run gstack setup to (re)create symlinks"
  107. else
  108. ok "gstack enabled ($moved symlinks restored)"
  109. fi
  110. ;;
  111. emil-design-eng|darwin-skill|find-skills)
  112. if [ -e "$DISABLED_DIR/$tool" ]; then
  113. mv "$DISABLED_DIR/$tool" "$SKILLS_DIR/$tool"
  114. ok "$tool enabled"
  115. elif [ -e "$SKILLS_DIR/$tool" ]; then
  116. warn "$tool already enabled"
  117. else
  118. err "$tool not installed — run: make plugin"
  119. return 1
  120. fi
  121. ;;
  122. *) err "Unknown tool: $tool"; return 1 ;;
  123. esac
  124. }
  125. list_all() {
  126. printf "%-20s %s\n" "TOOL" "STATUS"
  127. printf "%-20s %s\n" "----" "------"
  128. for t in "${MANAGED_TOOLS[@]}"; do
  129. printf "%-20s %s\n" "$t" "$(status_tool "$t")"
  130. done
  131. }
  132. usage() {
  133. sed -n '3,23p' "$0" | sed 's/^# \?//'
  134. exit "${1:-0}"
  135. }
  136. main() {
  137. local cmd="${1:-}"
  138. case "$cmd" in
  139. list) list_all ;;
  140. status) [ $# -ge 2 ] || usage 1; status_tool "$2" ;;
  141. enable) [ $# -ge 2 ] || usage 1; enable_tool "$2" ;;
  142. disable) [ $# -ge 2 ] || usage 1; disable_tool "$2" ;;
  143. ""|-h|--help|help) usage 0 ;;
  144. *) err "Unknown command: $cmd"; usage 1 ;;
  145. esac
  146. }
  147. main "$@"