
Gardening Skills Wiki
- 266 installs
- 733 repo stars
- Updated October 14, 2025
- obra/superpowers-skills
Create or refine agent skills using TDD—pressure scenarios, baseline failure, minimal SKILL.md, refactor loopholes.
About
gardening skills wiki — Writing Skills applies test-driven development to SKILL.md authoring—baseline subagent scenarios, minimal docs that fix observed rationalizations, then refactor loopholes. It covers directory layout, frontmatter, and Anthropic best practices. Solo builders use it when packaging repeatable agent workflows as installable skills.
- Maps TDD red-green-refactor directly to skill authoring workflow.
- SKILL.md structure rules: when-to-use descriptions, progressive disclosure, flat namespace.
- Requires watching agents fail without the skill before writing it.
Gardening Skills Wiki by the numbers
- 266 all-time installs (skills.sh)
- +24 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #156 of 826 Skill Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/obra/superpowers-skills --skill gardening-skills-wikiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 266 |
|---|---|
| repo stars | ★ 733 |
| Security audit | 3 / 3 scanners passed |
| Last updated | October 14, 2025 |
| Repository | obra/superpowers-skills ↗ |
What it does
Create or refine agent skills using TDD—pressure scenarios, baseline failure, minimal SKILL.md, refactor loopholes.
Files
Gardening Skills Wiki
Overview
The skills wiki needs regular maintenance to stay healthy: links break, skills get orphaned, naming drifts, INDEX files fall out of sync.
Core principle: Automate health checks to maintain wiki quality without burning tokens on manual inspection.
When to Use
Run gardening after:
- Adding new skills
- Removing or renaming skills
- Reorganizing categories
- Updating cross-references
- Suspicious that links are broken
Periodic maintenance:
- Weekly during active development
- Monthly during stable periods
Quick Health Check
# Run all checks
~/.claude/skills/meta/gardening-skills-wiki/garden.sh
# Or run specific checks
~/.claude/skills/meta/gardening-skills-wiki/check-links.sh
~/.claude/skills/meta/gardening-skills-wiki/check-naming.sh
~/.claude/skills/meta/gardening-skills-wiki/check-index-coverage.sh
# Analyze search gaps (what skills are missing)
~/.claude/skills/meta/gardening-skills-wiki/analyze-search-gaps.shThe master script runs all checks and provides a health report.
What Gets Checked
1. Link Validation (check-links.sh)
Checks:
- Backtick-wrapped
@links - backticks disable resolution - Relative paths like skills/ or skills/gardening-skills-wiki/~/ - should use skills/ absolute paths
- All
skills/references resolve to existing files - Skills referenced in INDEX files exist
- Orphaned skills (not in any INDEX)
Fixes:
- Remove backticks from @ references
- Convert skills/ and skills/gardening-skills-wiki/~/ relative paths to skills/ absolute paths
- Update broken skills/ references to correct paths
- Add orphaned skills to their category INDEX
- Remove references to deleted skills
2. Naming Consistency (check-naming.sh)
Checks:
- Directory names are kebab-case
- No uppercase or underscores in directory names
- Frontmatter fields present (name, description, when_to_use, version, type)
- Skill names use active voice (not "How to...")
- Empty directories
Fixes:
- Rename directories to kebab-case
- Add missing frontmatter fields
- Remove empty directories
- Rephrase names to active voice
3. INDEX Coverage (check-index-coverage.sh)
Checks:
- All skills listed in their category INDEX
- All category INDEX files linked from main INDEX
- Skills have descriptions in INDEX entries
Fixes:
- Add missing skills to INDEX files
- Add category links to main INDEX
- Add descriptions for INDEX entries
Common Issues and Fixes
Broken Links
❌ BROKEN: skills/debugging/root-cause-tracing
Target: /path/to/skills/debugging/root-cause-tracing/SKILL.mdFix: Update the reference path - skill might have moved or been renamed.
Orphaned Skills
⚠️ ORPHANED: test-invariants/SKILL.md not in testing/INDEX.mdFix: Add to the category INDEX:
- skills/gardening-skills-wiki/test-invariants - Description of skillBacktick-Wrapped Links
❌ BACKTICKED: skills/testing/condition-based-waiting on line 31
File: getting-started/SKILL.md
Fix: Remove backticks - use bare @ referenceFix: Remove backticks:
# ❌ Bad - backticks disable link resolution
`skills/testing/condition-based-waiting`
# ✅ Good - bare @ reference
skills/testing/condition-based-waitingRelative Path Links
❌ RELATIVE: skills/testing in coding/SKILL.md
Fix: Use skills/ absolute path insteadFix: Convert to absolute path:
# ❌ Bad - relative paths are brittle
skills/testing/condition-based-waiting
# ✅ Good - absolute skills/ path
skills/testing/condition-based-waitingNaming Issues
⚠️ Mixed case: TestingPatterns (should be kebab-case)Fix: Rename directory:
cd ~/.claude/skills/testing
mv TestingPatterns testing-patterns
# Update all references to old nameMissing from INDEX
❌ NOT INDEXED: condition-based-waiting/SKILL.mdFix: Add to testing/INDEX.md:
## Available Skills
- skills/gardening-skills-wiki/condition-based-waiting - Replace timeouts with condition pollingEmpty Directories
⚠️ EMPTY: event-based-testingFix: Remove if no longer needed:
rm -rf ~/.claude/skills/event-based-testingNaming Conventions
Directory Names
- Format: kebab-case (lowercase with hyphens)
- Process skills: Use gerunds when appropriate (
creating-skills,testing-skills) - Pattern skills: Use core concept (
flatten-with-flags,test-invariants) - Avoid: Mixed case, underscores, passive voice starters ("how-to-")
Frontmatter Requirements
Required fields:
name: Human-readable namedescription: One-line summarywhen_to_use: Symptoms and situations (CSO-critical)version: Semantic version
Optional fields:
languages: Applicable languagesdependencies: Required toolscontext: Special context (e.g., "AI-assisted development")
Automation Workflow
After Adding New Skill
# 1. Create skill
mkdir -p ~/.claude/skills/category/new-skill
vim ~/.claude/skills/category/new-skill/SKILL.md
# 2. Add to category INDEX
vim ~/.claude/skills/category/INDEX.md
# 3. Run health check
~/.claude/skills/meta/gardening-skills-wiki/garden.sh
# 4. Fix any issues reportedAfter Reorganizing
# 1. Move/rename skills
mv ~/.claude/skills/old-category/skill ~/.claude/skills/new-category/
# 2. Update all references (grep for old paths)
grep -r "skills/gardening-skills-wiki/old-category/skill" ~/.claude/skills/
# 3. Run health check
~/.claude/skills/meta/gardening-skills-wiki/garden.sh
# 4. Fix broken linksPeriodic Maintenance
# Monthly: Run full health check
~/.claude/skills/meta/gardening-skills-wiki/garden.sh
# Review and fix:
# - ❌ errors (broken links, missing skills)
# - ⚠️ warnings (naming, empty dirs)The Scripts
garden.sh (Master)
Runs all health checks and provides comprehensive report.
Usage:
~/.claude/skills/meta/gardening-skills-wiki/garden.sh [skills_dir]check-links.sh
Validates all @ references and cross-links.
Checks:
- Backtick-wrapped
@links (disables resolution) - Relative paths (
skills/orskills/gardening-skills-wiki/~/) - should beskills/ @reference resolution to existing files- Skills in INDEX files exist
- Orphaned skills detection
check-naming.sh
Validates naming conventions and frontmatter.
Checks:
- Directory name format
- Frontmatter completeness
- Empty directories
check-index-coverage.sh
Validates INDEX completeness.
Checks:
- Skills listed in category INDEX
- Categories linked in main INDEX
- Descriptions present
Quick Reference
| Issue | Script | Fix |
|---|---|---|
| Backtick-wrapped links | check-links.sh | Remove backticks from @ refs |
| Relative paths | check-links.sh | Convert to skills/ absolute |
| Broken links | check-links.sh | Update @ references |
| Orphaned skills | check-links.sh | Add to INDEX |
| Naming issues | check-naming.sh | Rename directories |
| Empty dirs | check-naming.sh | Remove with rm -rf |
| Missing from INDEX | check-index-coverage.sh | Add to INDEX.md |
| No description | check-index-coverage.sh | Add to INDEX entry |
Output Symbols
- ✅ Pass - Item is correct
- ❌ Error - Must fix (broken link, missing skill)
- ⚠️ Warning - Should fix (naming, empty dir)
- ℹ️ Info - Informational (no action needed)
Integration with Workflow
Before committing skill changes:
~/.claude/skills/meta/gardening-skills-wiki/garden.sh
# Fix all ❌ errors
# Consider fixing ⚠️ warnings
git add .
git commit -m "Add/update skills"When links feel suspicious:
~/.claude/skills/meta/gardening-skills-wiki/check-links.shWhen INDEX seems incomplete:
~/.claude/skills/meta/gardening-skills-wiki/check-index-coverage.shCommon Rationalizations
| Excuse | Reality |
|---|---|
| "Will check links manually" | Automated check is faster and more thorough |
| "INDEX probably fine" | Orphaned skills happen - always verify |
| "Naming doesn't matter" | Consistency aids discovery and maintenance |
| "Empty dir harmless" | Clutter confuses future maintainers |
| "Can skip periodic checks" | Issues compound - regular maintenance prevents big cleanups |
Real-World Impact
Without gardening:
- Broken links discovered during urgent tasks
- Orphaned skills never found
- Naming drifts over time
- INDEX files fall out of sync
With gardening:
- 30-second health check catches issues early
- Automated validation prevents manual inspection
- Consistent structure aids discovery
- Wiki stays maintainable
The Bottom Line
Don't manually inspect - automate the checks.
Run garden.sh after changes and periodically. Fix ❌ errors immediately, address ⚠️ warnings when convenient.
Maintained wiki = findable skills = reusable knowledge.
#!/usr/bin/env bash
# Analyze failed skills-search queries to identify missing skills
set -euo pipefail
SKILLS_DIR="${HOME}/.claude/skills"
LOG_FILE="${SKILLS_DIR}/.search-log.jsonl"
if [[ ! -f "$LOG_FILE" ]]; then
echo "No search log found at $LOG_FILE"
exit 0
fi
echo "Skills Search Gap Analysis"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Count total searches
total=$(wc -l < "$LOG_FILE")
echo "Total searches: $total"
echo ""
# Extract and count unique queries
echo "Most common searches:"
jq -r '.query' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -20
echo ""
echo "Recent searches (last 10):"
tail -10 "$LOG_FILE" | jq -r '"\(.timestamp) - \(.query)"' 2>/dev/null
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "High-frequency searches indicate missing skills."
echo "Review patterns and create skills as needed."
#!/bin/bash
# Check that all skills are properly listed in INDEX files
SKILLS_DIR="${1:-$HOME/Documents/GitHub/dotfiles/.claude/skills}"
echo "## INDEX Coverage"
# For each category with an INDEX
for category_dir in "$SKILLS_DIR"/*/; do
category=$(basename "$category_dir")
# Skip if not a directory
[[ ! -d "$category_dir" ]] && continue
index_file="$category_dir/INDEX.md"
# Skip if no INDEX (meta directories might not have one)
[[ ! -f "$index_file" ]] && continue
# Find all SKILL.md files in this category
skill_count=0
indexed_count=0
missing_count=0
while IFS= read -r skill_file; do
skill_count=$((skill_count + 1))
skill_name=$(basename $(dirname "$skill_file"))
# Check if skill is referenced in INDEX
if grep -q "@$skill_name/SKILL.md" "$index_file"; then
indexed_count=$((indexed_count + 1))
else
echo " ❌ NOT INDEXED: $skill_name/SKILL.md"
missing_count=$((missing_count + 1))
fi
done < <(find "$category_dir" -mindepth 2 -type f -name "SKILL.md")
if [ $skill_count -gt 0 ] && [ $missing_count -eq 0 ]; then
echo " ✅ $category: all $skill_count skills indexed"
elif [ $missing_count -gt 0 ]; then
echo " ⚠️ $category: $missing_count/$skill_count skills missing"
fi
done
echo ""
# Verify INDEX entries have descriptions
find "$SKILLS_DIR" -type f -name "INDEX.md" | while read -r index_file; do
category=$(basename $(dirname "$index_file"))
# Extract skill references
grep -o '@[a-zA-Z0-9-]*/SKILL\.md' "$index_file" | while read -r ref; do
skill_name=${ref#@}
skill_name=${skill_name%/SKILL.md}
# Get the line with the reference
line_num=$(grep -n "$ref" "$index_file" | cut -d: -f1)
# Check if there's a description on the same line or next line
description=$(sed -n "${line_num}p" "$index_file" | sed "s|.*$ref *- *||")
if [[ -z "$description" || "$description" == *"$ref"* ]]; then
# No description on same line, check next line
next_line=$((line_num + 1))
description=$(sed -n "${next_line}p" "$index_file")
if [[ -z "$description" ]]; then
echo " ⚠️ NO DESCRIPTION: $category/INDEX.md reference to $skill_name"
fi
fi
done
done
#!/bin/bash
# Check for @ links (force-load context) and validate skill path references
SKILLS_DIR="${1:-$HOME/Documents/GitHub/dotfiles/.claude/skills}"
echo "## Links & References"
broken_refs=0
backticked_refs=0
relative_refs=0
at_links=0
while IFS= read -r file; do
# Extract @ references - must start line, be after space/paren/dash, or be standalone
# Exclude: emails, decorators, code examples with @staticmethod/@example
# First, check for backtick-wrapped @ links
grep -nE '`[^`]*@[a-zA-Z0-9._~/-]+\.(md|sh|ts|js|py)[^`]*`' "$file" | while IFS=: read -r line_num match; do
# Get actual line to check if in code block
actual_line=$(sed -n "${line_num}p" "$file")
# Skip if line is indented (code block) or in fenced code
if [[ "$actual_line" =~ ^[[:space:]]{4,} ]]; then
continue
fi
code_block_count=$(sed -n "1,${line_num}p" "$file" | grep -c '^```')
if [ $((code_block_count % 2)) -eq 1 ]; then
continue
fi
ref=$(echo "$match" | grep -o '@[a-zA-Z0-9._~/-]*\.[a-zA-Z0-9]*')
echo " ❌ BACKTICKED: $ref on line $line_num"
echo " File: $(basename $(dirname "$file"))/$(basename "$file")"
echo " Fix: Remove backticks - use bare @ reference"
backticked_refs=$((backticked_refs + 1))
done
# Check for ANY @ links to .md/.sh/.ts/.js/.py files (force-loads, burns context)
grep -nE '(^|[ \(>-])@[a-zA-Z0-9._/-]+\.(md|sh|ts|js|py)' "$file" | \
grep -v '@[a-zA-Z0-9._%+-]*@' | \
grep -v 'email.*@' | \
grep -v '`.*@.*`' | while IFS=: read -r line_num match; do
ref=$(echo "$match" | grep -o '@[a-zA-Z0-9._/-]+\.(md|sh|ts|js|py)')
ref_path="${ref#@}"
# Skip if in fenced code block
actual_line=$(sed -n "${line_num}p" "$file")
if [[ "$actual_line" =~ ^[[:space:]]{4,} ]]; then
continue
fi
code_block_count=$(sed -n "1,${line_num}p" "$file" | grep -c '^```')
if [ $((code_block_count % 2)) -eq 1 ]; then
continue
fi
# Any @ link is wrong - should use skills/path format
echo " ❌ @ LINK: $ref on line $line_num"
echo " File: $(basename $(dirname "$file"))/$(basename "$file")"
# Suggest correct format
if [[ "$ref_path" == skills/* ]]; then
# @skills/category/name/SKILL.md → skills/category/name
corrected="${ref_path#skills/}"
corrected="${corrected%/SKILL.md}"
echo " Fix: $ref → skills/$corrected"
elif [[ "$ref_path" == ../* ]]; then
echo " Fix: Convert to skills/category/skill-name format"
else
echo " Fix: Convert to skills/category/skill-name format"
fi
at_links=$((at_links + 1))
done
done < <(find "$SKILLS_DIR" -type f -name "*.md")
# Summary
total_issues=$((backticked_refs + at_links))
if [ $total_issues -eq 0 ]; then
echo " ✅ All skill references OK"
else
[ $backticked_refs -gt 0 ] && echo " ❌ $backticked_refs backticked references"
[ $at_links -gt 0 ] && echo " ❌ $at_links @ links (force-load context)"
fi
echo ""
echo "Correct format: skills/category/skill-name"
echo " ❌ Bad: @skills/path/SKILL.md (force-loads) or @../path (brittle)"
echo " ✅ Good: skills/category/skill-name (load with Read tool when needed)"
echo ""
# Verify all skills mentioned in INDEX files exist
find "$SKILLS_DIR" -type f -name "INDEX.md" | while read -r index_file; do
index_dir=$(dirname "$index_file")
# Extract skill references (format: @skill-name/SKILL.md)
grep -o '@[a-zA-Z0-9-]*/SKILL\.md' "$index_file" | while read -r skill_ref; do
skill_path="$index_dir/${skill_ref#@}"
if [[ ! -f "$skill_path" ]]; then
echo " ❌ BROKEN: $skill_ref in $(basename "$index_dir")/INDEX.md"
echo " Expected: $skill_path"
fi
done
done
echo ""
find "$SKILLS_DIR" -type f -path "*/*/SKILL.md" | while read -r skill_file; do
skill_dir=$(basename $(dirname "$skill_file"))
category_dir=$(dirname $(dirname "$skill_file"))
index_file="$category_dir/INDEX.md"
if [[ -f "$index_file" ]]; then
if ! grep -q "@$skill_dir/SKILL.md" "$index_file"; then
echo " ⚠️ ORPHANED: $skill_dir/SKILL.md not in $(basename "$category_dir")/INDEX.md"
fi
fi
done
#!/bin/bash
# Check naming consistency in skills wiki
SKILLS_DIR="${1:-$HOME/Documents/GitHub/dotfiles/.claude/skills}"
echo "## Naming & Structure"
issues=0
find "$SKILLS_DIR" -type d -mindepth 2 -maxdepth 2 | while read -r dir; do
dir_name=$(basename "$dir")
# Skip if it's an INDEX or top-level category
if [[ "$dir_name" == "INDEX.md" ]] || [[ $(dirname "$dir") == "$SKILLS_DIR" ]]; then
continue
fi
# Check for naming issues
if [[ "$dir_name" =~ [A-Z] ]]; then
echo " ⚠️ Mixed case: $dir_name (should be kebab-case)"
issues=$((issues + 1))
fi
if [[ "$dir_name" =~ _ ]]; then
echo " ⚠️ Underscore: $dir_name (should use hyphens)"
issues=$((issues + 1))
fi
# Check if name follows gerund pattern for process skills
if [[ -f "$dir/SKILL.md" ]]; then
type=$(grep "^type:" "$dir/SKILL.md" | head -1 | cut -d: -f2 | xargs)
if [[ "$type" == "technique" ]] && [[ ! "$dir_name" =~ ing$ ]] && [[ ! "$dir_name" =~ -with- ]] && [[ ! "$dir_name" =~ ^test- ]]; then
# Techniques might want -ing but not required
:
fi
fi
done
[ $issues -eq 0 ] && echo " ✅ Directory names OK" || echo " ⚠️ $issues naming issues"
echo ""
find "$SKILLS_DIR" -type d -empty | while read -r empty_dir; do
echo " ⚠️ EMPTY: $(realpath --relative-to="$SKILLS_DIR" "$empty_dir" 2>/dev/null || echo "$empty_dir")"
done
echo ""
find "$SKILLS_DIR" -type f -name "SKILL.md" | while read -r skill_file; do
skill_name=$(basename $(dirname "$skill_file"))
# Check for required fields
if ! grep -q "^name:" "$skill_file"; then
echo " ❌ MISSING 'name': $skill_name/SKILL.md"
fi
if ! grep -q "^description:" "$skill_file"; then
echo " ❌ MISSING 'description': $skill_name/SKILL.md"
fi
if ! grep -q "^when_to_use:" "$skill_file"; then
echo " ❌ MISSING 'when_to_use': $skill_name/SKILL.md"
fi
if ! grep -q "^version:" "$skill_file"; then
echo " ⚠️ MISSING 'version': $skill_name/SKILL.md"
fi
# Check for active voice in name (should not start with "How to")
name_value=$(grep "^name:" "$skill_file" | head -1 | cut -d: -f2- | xargs)
if [[ "$name_value" =~ ^How\ to ]]; then
echo " ⚠️ Passive name: $skill_name has 'How to' prefix (prefer active voice)"
fi
done
#!/bin/bash
# Master gardening script for skills wiki maintenance
SKILLS_DIR="${1:-$HOME/Documents/GitHub/dotfiles/.claude/skills}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "=== Skills Wiki Health Check ==="
echo ""
# Make scripts executable if not already
chmod +x "$SCRIPT_DIR"/*.sh 2>/dev/null
# Run all checks
bash "$SCRIPT_DIR/check-naming.sh" "$SKILLS_DIR"
echo ""
bash "$SCRIPT_DIR/check-links.sh" "$SKILLS_DIR"
echo ""
bash "$SCRIPT_DIR/check-index-coverage.sh" "$SKILLS_DIR"
echo ""
echo "=== Health Check Complete ==="
echo ""
echo "Fix: ❌ errors (broken/missing) | Consider: ⚠️ warnings | ✅ = correct"
Related skills
FAQ
Is Gardening Skills Wiki safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.