find-skills 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #!/usr/bin/env bash
  2. # find-skills - Find and list skills with descriptions
  3. # Shows all skills by default, filters by pattern if provided
  4. # Searches personal superpowers first, then core (personal shadows core)
  5. set -euo pipefail
  6. # Determine directories
  7. PERSONAL_SUPERPOWERS_DIR="${PERSONAL_SUPERPOWERS_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/superpowers}"
  8. PERSONAL_SKILLS_DIR="${PERSONAL_SUPERPOWERS_DIR}/skills"
  9. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  10. PLUGIN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
  11. CORE_SKILLS_DIR="${PLUGIN_ROOT}/skills"
  12. LOG_FILE="${PERSONAL_SUPERPOWERS_DIR}/search-log.jsonl"
  13. # Show help
  14. if [[ "${1:-}" == "--help" ]] || [[ "${1:-}" == "-h" ]]; then
  15. cat <<'EOF'
  16. find-skills - Find and list skills with descriptions
  17. USAGE:
  18. find-skills Show all skills with descriptions
  19. find-skills PATTERN Filter skills by grep pattern
  20. find-skills --help Show this help
  21. EXAMPLES:
  22. find-skills # All skills
  23. find-skills test # Skills matching "test"
  24. find-skills 'test.*driven|TDD' # Regex pattern
  25. OUTPUT:
  26. Each line shows: skill-path - description
  27. Personal skills listed first, then core skills
  28. Personal skills shadow core skills when paths match
  29. SEARCH:
  30. Searches both skill content AND path names.
  31. Personal skills at: ~/.config/superpowers/skills/
  32. Core skills at: plugin installation directory
  33. EOF
  34. exit 0
  35. fi
  36. # Get pattern (optional)
  37. PATTERN="${1:-}"
  38. # Function to extract description from SKILL.md
  39. get_description() {
  40. local file="$1"
  41. grep "^description:" "$file" 2>/dev/null | sed 's/description: *//' || echo ""
  42. }
  43. # Function to get relative skill path
  44. get_skill_path() {
  45. local file="$1"
  46. local base_dir="$2"
  47. local rel_path="${file#$base_dir/}"
  48. echo "${rel_path%/SKILL.md}"
  49. }
  50. # Collect all matching skills (use simple list for bash 3.2 compatibility)
  51. seen_skills_list=""
  52. results=()
  53. # If pattern provided, log the search
  54. if [[ -n "$PATTERN" ]]; then
  55. timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
  56. echo "{\"timestamp\":\"$timestamp\",\"query\":\"$PATTERN\"}" >> "$LOG_FILE" 2>/dev/null || true
  57. fi
  58. # Search personal skills first
  59. if [[ -d "$PERSONAL_SKILLS_DIR" ]]; then
  60. while IFS= read -r file; do
  61. [[ -z "$file" ]] && continue
  62. skill_path=$(get_skill_path "$file" "$PERSONAL_SKILLS_DIR")
  63. description=$(get_description "$file")
  64. seen_skills_list="${seen_skills_list}${skill_path}"$'\n'
  65. results+=("$skill_path|$description")
  66. done < <(
  67. if [[ -n "$PATTERN" ]]; then
  68. # Pattern mode: search content and paths
  69. {
  70. grep -E -r "$PATTERN" "$PERSONAL_SKILLS_DIR/" --include="SKILL.md" -l 2>/dev/null || true
  71. find "$PERSONAL_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null | grep -E "$PATTERN" 2>/dev/null || true
  72. } | sort -u
  73. else
  74. # Show all
  75. find "$PERSONAL_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null || true
  76. fi
  77. )
  78. fi
  79. # Search core skills (only if not shadowed)
  80. while IFS= read -r file; do
  81. [[ -z "$file" ]] && continue
  82. skill_path=$(get_skill_path "$file" "$CORE_SKILLS_DIR")
  83. # Skip if shadowed by personal skill
  84. echo "$seen_skills_list" | grep -q "^${skill_path}$" && continue
  85. description=$(get_description "$file")
  86. results+=("$skill_path|$description")
  87. done < <(
  88. if [[ -n "$PATTERN" ]]; then
  89. # Pattern mode: search content and paths
  90. {
  91. grep -E -r "$PATTERN" "$CORE_SKILLS_DIR/" --include="SKILL.md" -l 2>/dev/null || true
  92. find "$CORE_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null | grep -E "$PATTERN" 2>/dev/null || true
  93. } | sort -u
  94. else
  95. # Show all
  96. find "$CORE_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null || true
  97. fi
  98. )
  99. # Check if we found anything
  100. if [[ ${#results[@]} -eq 0 ]]; then
  101. if [[ -n "$PATTERN" ]]; then
  102. echo "❌ No skills found matching: $PATTERN"
  103. echo ""
  104. echo "Search logged. If a skill should exist, consider writing it!"
  105. else
  106. echo "❌ No skills found"
  107. fi
  108. exit 0
  109. fi
  110. # Sort and display results
  111. printf "%s\n" "${results[@]}" | sort | while IFS='|' read -r skill_path description; do
  112. if [[ -n "$description" ]]; then
  113. echo "skills/$skill_path - $description"
  114. else
  115. echo "skills/$skill_path"
  116. fi
  117. done
  118. exit 0