Преглед на файлове

feat: add build validation system with auto-registry updates

- Add scripts/validate-registry.sh to validate all registry paths exist
- Add scripts/auto-detect-components.sh to auto-detect new components
- Add GitHub Actions workflow for PR validation
- Fix registry.json prompt-enhancer path typo
- Auto-detect and add new components on PR
- Block PR merge if registry validation fails

Resolves installation 404 errors by ensuring registry accuracy
darrenhinde преди 8 месеца
родител
ревизия
4613fc083d
променени са 4 файла, в които са добавени 799 реда и са изтрити 1 реда
  1. 141 0
      .github/workflows/validate-registry.yml
  2. 1 1
      registry.json
  3. 322 0
      scripts/auto-detect-components.sh
  4. 335 0
      scripts/validate-registry.sh

+ 141 - 0
.github/workflows/validate-registry.yml

@@ -0,0 +1,141 @@
+name: Validate Registry on PR
+
+on:
+  pull_request:
+    branches:
+      - main
+      - dev
+    paths:
+      - '.opencode/**'
+      - 'registry.json'
+      - 'scripts/validate-registry.sh'
+      - 'scripts/auto-detect-components.sh'
+  workflow_dispatch:
+
+permissions:
+  contents: write
+  pull-requests: write
+
+jobs:
+  validate-and-update:
+    runs-on: ubuntu-latest
+    
+    steps:
+      - name: Checkout PR branch
+        uses: actions/checkout@v4
+        with:
+          ref: ${{ github.head_ref }}
+          fetch-depth: 0
+          token: ${{ secrets.GITHUB_TOKEN }}
+      
+      - name: Install dependencies
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y jq
+      
+      - name: Make scripts executable
+        run: |
+          chmod +x scripts/validate-registry.sh
+          chmod +x scripts/auto-detect-components.sh
+          chmod +x scripts/register-component.sh
+      
+      - name: Auto-detect new components
+        id: auto_detect
+        run: |
+          echo "## 🔍 Auto-Detection Results" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          
+          # Run auto-detect in dry-run mode first to see what would be added
+          if ./scripts/auto-detect-components.sh --dry-run > /tmp/detect-output.txt 2>&1; then
+            cat /tmp/detect-output.txt >> $GITHUB_STEP_SUMMARY
+            
+            # Check if new components were found
+            if grep -q "Found.*new component" /tmp/detect-output.txt; then
+              echo "new_components=true" >> $GITHUB_OUTPUT
+              echo "" >> $GITHUB_STEP_SUMMARY
+              echo "⚠️ New components detected - will auto-add to registry" >> $GITHUB_STEP_SUMMARY
+            else
+              echo "new_components=false" >> $GITHUB_OUTPUT
+              echo "✅ No new components found" >> $GITHUB_STEP_SUMMARY
+            fi
+          else
+            echo "new_components=false" >> $GITHUB_OUTPUT
+            echo "❌ Auto-detection failed" >> $GITHUB_STEP_SUMMARY
+          fi
+      
+      - name: Add new components to registry
+        if: steps.auto_detect.outputs.new_components == 'true'
+        run: |
+          echo "## 📝 Adding New Components" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          
+          ./scripts/auto-detect-components.sh --auto-add | tee -a $GITHUB_STEP_SUMMARY
+      
+      - name: Validate registry
+        id: validate
+        run: |
+          echo "## ✅ Registry Validation" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          
+          if ./scripts/validate-registry.sh -v > /tmp/validation-output.txt 2>&1; then
+            echo "validation=passed" >> $GITHUB_OUTPUT
+            echo "✅ All registry paths are valid!" >> $GITHUB_STEP_SUMMARY
+            echo "" >> $GITHUB_STEP_SUMMARY
+            echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
+            tail -20 /tmp/validation-output.txt >> $GITHUB_STEP_SUMMARY
+            echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
+          else
+            echo "validation=failed" >> $GITHUB_OUTPUT
+            echo "❌ Registry validation failed!" >> $GITHUB_STEP_SUMMARY
+            echo "" >> $GITHUB_STEP_SUMMARY
+            echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
+            cat /tmp/validation-output.txt >> $GITHUB_STEP_SUMMARY
+            echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+      
+      - name: Commit registry updates
+        if: steps.auto_detect.outputs.new_components == 'true'
+        run: |
+          git config --local user.email "github-actions[bot]@users.noreply.github.com"
+          git config --local user.name "github-actions[bot]"
+          
+          if ! git diff --quiet registry.json; then
+            git add registry.json
+            git commit -m "chore: auto-update registry with new components [skip ci]"
+            git push origin ${{ github.head_ref }}
+            
+            echo "## 🚀 Registry Updated" >> $GITHUB_STEP_SUMMARY
+            echo "" >> $GITHUB_STEP_SUMMARY
+            echo "Registry has been automatically updated with new components." >> $GITHUB_STEP_SUMMARY
+            echo "Changes have been pushed to this PR branch." >> $GITHUB_STEP_SUMMARY
+          fi
+      
+      - name: Post validation summary
+        if: always()
+        run: |
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "---" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          
+          if [ "${{ steps.validate.outputs.validation }}" = "passed" ]; then
+            echo "### ✅ Validation Passed" >> $GITHUB_STEP_SUMMARY
+            echo "" >> $GITHUB_STEP_SUMMARY
+            echo "All registry paths are valid and point to existing files." >> $GITHUB_STEP_SUMMARY
+            echo "This PR is ready for review!" >> $GITHUB_STEP_SUMMARY
+          else
+            echo "### ❌ Validation Failed" >> $GITHUB_STEP_SUMMARY
+            echo "" >> $GITHUB_STEP_SUMMARY
+            echo "Registry validation failed. Please fix the issues above." >> $GITHUB_STEP_SUMMARY
+            echo "" >> $GITHUB_STEP_SUMMARY
+            echo "**Common fixes:**" >> $GITHUB_STEP_SUMMARY
+            echo "- Update paths in registry.json to match actual file locations" >> $GITHUB_STEP_SUMMARY
+            echo "- Remove entries for deleted files" >> $GITHUB_STEP_SUMMARY
+            echo "- Run \`./scripts/validate-registry.sh --fix\` locally for suggestions" >> $GITHUB_STEP_SUMMARY
+          fi
+      
+      - name: Fail if validation failed
+        if: steps.validate.outputs.validation == 'failed'
+        run: |
+          echo "❌ Registry validation failed - blocking PR merge"
+          exit 1

+ 1 - 1
registry.json

@@ -332,7 +332,7 @@
         "id": "prompt-enhancer",
         "name": "Prompt Enhancer",
         "type": "command",
-        "path": ".opencode/command/prompt-enchancer.md",
+        "path": ".opencode/command/prompt-engineering/prompt-enhancer.md",
         "description": "Enhance and improve AI prompts",
         "tags": [
           "prompts",

+ 322 - 0
scripts/auto-detect-components.sh

@@ -0,0 +1,322 @@
+#!/usr/bin/env bash
+
+#############################################################################
+# Auto-Detect Components Script
+# Scans .opencode directory for new components not in registry
+# Suggests additions with proper metadata
+#############################################################################
+
+set -e
+
+# Colors
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+CYAN='\033[0;36m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Configuration
+REGISTRY_FILE="registry.json"
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+AUTO_ADD=false
+DRY_RUN=false
+
+# Arrays to store new components
+declare -a NEW_COMPONENTS
+
+#############################################################################
+# Utility Functions
+#############################################################################
+
+print_header() {
+    echo -e "${CYAN}${BOLD}"
+    echo "╔════════════════════════════════════════════════════════════════╗"
+    echo "║                                                                ║"
+    echo "║           Auto-Detect Components v1.0.0                       ║"
+    echo "║                                                                ║"
+    echo "╚════════════════════════════════════════════════════════════════╝"
+    echo -e "${NC}"
+}
+
+print_success() {
+    echo -e "${GREEN}✓${NC} $1"
+}
+
+print_error() {
+    echo -e "${RED}✗${NC} $1"
+}
+
+print_warning() {
+    echo -e "${YELLOW}⚠${NC} $1"
+}
+
+print_info() {
+    echo -e "${BLUE}ℹ${NC} $1"
+}
+
+usage() {
+    echo "Usage: $0 [OPTIONS]"
+    echo ""
+    echo "Options:"
+    echo "  -a, --auto-add      Automatically add new components to registry"
+    echo "  -d, --dry-run       Show what would be added without modifying registry"
+    echo "  -h, --help          Show this help message"
+    echo ""
+    exit 0
+}
+
+#############################################################################
+# Component Detection
+#############################################################################
+
+extract_metadata_from_file() {
+    local file=$1
+    local id=""
+    local name=""
+    local description=""
+    
+    # Try to extract from frontmatter (YAML)
+    if grep -q "^---$" "$file" 2>/dev/null; then
+        # Extract description from frontmatter
+        description=$(sed -n '/^---$/,/^---$/p' "$file" | grep "^description:" | sed 's/description: *"\?\(.*\)"\?/\1/' | head -1)
+    fi
+    
+    # If no description in frontmatter, try to get from first heading or paragraph
+    if [ -z "$description" ]; then
+        description=$(grep -m 1 "^# " "$file" | sed 's/^# //' || echo "")
+    fi
+    
+    # Generate ID from filename
+    local filename=$(basename "$file" .md)
+    id=$(echo "$filename" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
+    
+    # Generate name from filename (capitalize words)
+    name=$(echo "$filename" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
+    
+    echo "${id}|${name}|${description}"
+}
+
+detect_component_type() {
+    local path=$1
+    
+    if [[ "$path" == *"/agent/subagents/"* ]]; then
+        echo "subagent"
+    elif [[ "$path" == *"/agent/"* ]]; then
+        echo "agent"
+    elif [[ "$path" == *"/command/"* ]]; then
+        echo "command"
+    elif [[ "$path" == *"/tool/"* ]]; then
+        echo "tool"
+    elif [[ "$path" == *"/plugin/"* ]]; then
+        echo "plugin"
+    elif [[ "$path" == *"/context/"* ]]; then
+        echo "context"
+    else
+        echo "unknown"
+    fi
+}
+
+get_registry_key() {
+    local type=$1
+    case "$type" in
+        config) echo "config" ;;
+        *) echo "${type}s" ;;
+    esac
+}
+
+scan_for_new_components() {
+    print_info "Scanning for new components..."
+    echo ""
+    
+    # Get all paths from registry
+    local registry_paths=$(jq -r '.components | to_entries[] | .value[] | .path' "$REGISTRY_FILE" 2>/dev/null | sort -u)
+    
+    # Scan .opencode directory
+    local categories=("agent" "command" "tool" "plugin" "context")
+    
+    for category in "${categories[@]}"; do
+        local category_dir="$REPO_ROOT/.opencode/$category"
+        
+        if [ ! -d "$category_dir" ]; then
+            continue
+        fi
+        
+        # Find all .md files (excluding node_modules, tests, docs)
+        while IFS= read -r file; do
+            local rel_path="${file#$REPO_ROOT/}"
+            
+            # Skip node_modules, tests, docs, templates
+            if [[ "$rel_path" == *"/node_modules/"* ]] || \
+               [[ "$rel_path" == *"/tests/"* ]] || \
+               [[ "$rel_path" == *"/docs/"* ]] || \
+               [[ "$rel_path" == *"/template"* ]] || \
+               [[ "$rel_path" == *"README.md" ]] || \
+               [[ "$rel_path" == *"index.md" ]]; then
+                continue
+            fi
+            
+            # Check if this path is in registry
+            if ! echo "$registry_paths" | grep -q "^${rel_path}$"; then
+                # Extract metadata
+                local metadata=$(extract_metadata_from_file "$file")
+                IFS='|' read -r id name description <<< "$metadata"
+                
+                # Detect component type
+                local comp_type=$(detect_component_type "$rel_path")
+                
+                if [ "$comp_type" != "unknown" ]; then
+                    NEW_COMPONENTS+=("${comp_type}|${id}|${name}|${description}|${rel_path}")
+                    print_warning "New ${comp_type}: ${name} (${id})"
+                    echo "  Path: ${rel_path}"
+                    [ -n "$description" ] && echo "  Description: ${description}"
+                    echo ""
+                fi
+            fi
+        done < <(find "$category_dir" -type f -name "*.md" 2>/dev/null)
+    done
+}
+
+add_component_to_registry() {
+    local comp_type=$1
+    local id=$2
+    local name=$3
+    local description=$4
+    local path=$5
+    
+    # Default description if empty
+    if [ -z "$description" ]; then
+        description="Component: ${name}"
+    fi
+    
+    # Get registry key (agents, subagents, commands, etc.)
+    local registry_key=$(get_registry_key "$comp_type")
+    
+    # Create component JSON
+    local component_json=$(cat <<EOF
+{
+  "id": "${id}",
+  "name": "${name}",
+  "type": "${comp_type}",
+  "path": "${path}",
+  "description": "${description}",
+  "tags": [],
+  "dependencies": [],
+  "category": "standard"
+}
+EOF
+)
+    
+    # Add to registry
+    local temp_file="${REGISTRY_FILE}.tmp"
+    jq ".components.${registry_key} += [${component_json}]" "$REGISTRY_FILE" > "$temp_file"
+    
+    if [ $? -eq 0 ]; then
+        mv "$temp_file" "$REGISTRY_FILE"
+        print_success "Added ${comp_type}: ${name}"
+    else
+        print_error "Failed to add ${comp_type}: ${name}"
+        rm -f "$temp_file"
+        return 1
+    fi
+}
+
+#############################################################################
+# Main
+#############################################################################
+
+main() {
+    # Parse arguments
+    while [ $# -gt 0 ]; do
+        case "$1" in
+            -a|--auto-add)
+                AUTO_ADD=true
+                shift
+                ;;
+            -d|--dry-run)
+                DRY_RUN=true
+                shift
+                ;;
+            -h|--help)
+                usage
+                ;;
+            *)
+                echo "Unknown option: $1"
+                usage
+                ;;
+        esac
+    done
+    
+    print_header
+    
+    # Check dependencies
+    if ! command -v jq &> /dev/null; then
+        print_error "jq is required but not installed"
+        exit 1
+    fi
+    
+    # Validate registry file
+    if [ ! -f "$REGISTRY_FILE" ]; then
+        print_error "Registry file not found: $REGISTRY_FILE"
+        exit 1
+    fi
+    
+    if ! jq empty "$REGISTRY_FILE" 2>/dev/null; then
+        print_error "Registry file is not valid JSON"
+        exit 1
+    fi
+    
+    # Scan for new components
+    scan_for_new_components
+    
+    # Summary
+    echo ""
+    echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
+    echo -e "${BOLD}Summary${NC}"
+    echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
+    echo ""
+    
+    if [ ${#NEW_COMPONENTS[@]} -eq 0 ]; then
+        print_success "No new components found. Registry is up to date!"
+        exit 0
+    fi
+    
+    echo -e "Found ${YELLOW}${#NEW_COMPONENTS[@]}${NC} new component(s)"
+    echo ""
+    
+    # Add components if auto-add is enabled
+    if [ "$AUTO_ADD" = true ] && [ "$DRY_RUN" = false ]; then
+        print_info "Adding new components to registry..."
+        echo ""
+        
+        local added=0
+        for entry in "${NEW_COMPONENTS[@]}"; do
+            IFS='|' read -r comp_type id name description path <<< "$entry"
+            if add_component_to_registry "$comp_type" "$id" "$name" "$description" "$path"; then
+                added=$((added + 1))
+            fi
+        done
+        
+        # Update timestamp
+        jq '.metadata.lastUpdated = (now | strftime("%Y-%m-%d"))' "$REGISTRY_FILE" > "${REGISTRY_FILE}.tmp"
+        mv "${REGISTRY_FILE}.tmp" "$REGISTRY_FILE"
+        
+        echo ""
+        print_success "Added ${added} component(s) to registry"
+        
+    elif [ "$DRY_RUN" = true ]; then
+        print_info "Dry run mode - no changes made to registry"
+        echo ""
+        echo "Run without --dry-run to add these components"
+        
+    else
+        print_info "Run with --auto-add to add these components to registry"
+        echo ""
+        echo "Or manually add them to registry.json"
+    fi
+    
+    exit 0
+}
+
+main "$@"

+ 335 - 0
scripts/validate-registry.sh

@@ -0,0 +1,335 @@
+#!/usr/bin/env bash
+
+#############################################################################
+# Registry Validator Script
+# Validates that all paths in registry.json point to actual files
+# Exit codes:
+#   0 = All paths valid
+#   1 = Missing files found
+#   2 = Registry parse error or missing dependencies
+#############################################################################
+
+set -e
+
+# Colors
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+CYAN='\033[0;36m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+# Configuration
+REGISTRY_FILE="registry.json"
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+VERBOSE=false
+FIX_MODE=false
+
+# Counters
+TOTAL_PATHS=0
+VALID_PATHS=0
+MISSING_PATHS=0
+ORPHANED_FILES=0
+
+# Arrays to store results
+declare -a MISSING_FILES
+declare -a ORPHANED_COMPONENTS
+
+#############################################################################
+# Utility Functions
+#############################################################################
+
+print_header() {
+    echo -e "${CYAN}${BOLD}"
+    echo "╔════════════════════════════════════════════════════════════════╗"
+    echo "║                                                                ║"
+    echo "║           Registry Validator v1.0.0                           ║"
+    echo "║                                                                ║"
+    echo "╚════════════════════════════════════════════════════════════════╝"
+    echo -e "${NC}"
+}
+
+print_success() {
+    echo -e "${GREEN}✓${NC} $1"
+}
+
+print_error() {
+    echo -e "${RED}✗${NC} $1"
+}
+
+print_warning() {
+    echo -e "${YELLOW}⚠${NC} $1"
+}
+
+print_info() {
+    echo -e "${BLUE}ℹ${NC} $1"
+}
+
+usage() {
+    echo "Usage: $0 [OPTIONS]"
+    echo ""
+    echo "Options:"
+    echo "  -v, --verbose       Show detailed validation output"
+    echo "  -f, --fix           Suggest fixes for missing files"
+    echo "  -h, --help          Show this help message"
+    echo ""
+    echo "Exit codes:"
+    echo "  0 = All paths valid"
+    echo "  1 = Missing files found"
+    echo "  2 = Registry parse error or missing dependencies"
+    exit 0
+}
+
+#############################################################################
+# Dependency Checks
+#############################################################################
+
+check_dependencies() {
+    local missing_deps=()
+    
+    if ! command -v jq &> /dev/null; then
+        missing_deps+=("jq")
+    fi
+    
+    if [ ${#missing_deps[@]} -ne 0 ]; then
+        print_error "Missing required dependencies: ${missing_deps[*]}"
+        echo ""
+        echo "Please install them:"
+        echo "  macOS:   brew install ${missing_deps[*]}"
+        echo "  Ubuntu:  sudo apt-get install ${missing_deps[*]}"
+        echo "  Fedora:  sudo dnf install ${missing_deps[*]}"
+        exit 2
+    fi
+}
+
+#############################################################################
+# Registry Validation
+#############################################################################
+
+validate_registry_file() {
+    if [ ! -f "$REGISTRY_FILE" ]; then
+        print_error "Registry file not found: $REGISTRY_FILE"
+        exit 2
+    fi
+    
+    if ! jq empty "$REGISTRY_FILE" 2>/dev/null; then
+        print_error "Registry file is not valid JSON"
+        exit 2
+    fi
+    
+    print_success "Registry file is valid JSON"
+}
+
+validate_component_paths() {
+    local category=$1
+    local category_display=$2
+    
+    [ "$VERBOSE" = true ] && echo -e "\n${BOLD}Checking ${category_display}...${NC}"
+    
+    # Get all components in this category
+    local components=$(jq -r ".components.${category}[]? | @json" "$REGISTRY_FILE" 2>/dev/null)
+    
+    if [ -z "$components" ]; then
+        [ "$VERBOSE" = true ] && print_info "No ${category_display} found in registry"
+        return
+    fi
+    
+    while IFS= read -r component; do
+        local id=$(echo "$component" | jq -r '.id')
+        local path=$(echo "$component" | jq -r '.path')
+        local name=$(echo "$component" | jq -r '.name')
+        
+        TOTAL_PATHS=$((TOTAL_PATHS + 1))
+        
+        # Check if file exists
+        if [ -f "$REPO_ROOT/$path" ]; then
+            VALID_PATHS=$((VALID_PATHS + 1))
+            [ "$VERBOSE" = true ] && print_success "${category_display}: ${name} (${id})"
+        else
+            MISSING_PATHS=$((MISSING_PATHS + 1))
+            MISSING_FILES+=("${category}:${id}|${name}|${path}")
+            print_error "${category_display}: ${name} (${id}) - File not found: ${path}"
+            
+            # Try to find similar files if in fix mode
+            if [ "$FIX_MODE" = true ]; then
+                suggest_fix "$path" "$id"
+            fi
+        fi
+    done <<< "$components"
+}
+
+suggest_fix() {
+    local missing_path=$1
+    local component_id=$2
+    
+    # Extract directory and filename
+    local dir=$(dirname "$missing_path")
+    local filename=$(basename "$missing_path")
+    local base_dir=$(echo "$dir" | cut -d'/' -f1-3)  # e.g., .opencode/command
+    
+    # Look for similar files in the expected directory and subdirectories
+    local similar_files=$(find "$REPO_ROOT/$base_dir" -type f -name "*.md" 2>/dev/null | grep -i "$component_id" || true)
+    
+    if [ -n "$similar_files" ]; then
+        echo -e "  ${YELLOW}→ Possible matches:${NC}"
+        while IFS= read -r file; do
+            local rel_path="${file#$REPO_ROOT/}"
+            echo -e "    ${CYAN}${rel_path}${NC}"
+        done <<< "$similar_files"
+    fi
+}
+
+scan_for_orphaned_files() {
+    [ "$VERBOSE" = true ] && echo -e "\n${BOLD}Scanning for orphaned files...${NC}"
+    
+    # Get all paths from registry
+    local registry_paths=$(jq -r '.components | to_entries[] | .value[] | .path' "$REGISTRY_FILE" 2>/dev/null | sort -u)
+    
+    # Scan .opencode directory for markdown files
+    local categories=("agent" "command" "tool" "plugin" "context")
+    
+    for category in "${categories[@]}"; do
+        local category_dir="$REPO_ROOT/.opencode/$category"
+        
+        if [ ! -d "$category_dir" ]; then
+            continue
+        fi
+        
+        # Find all .md and .ts files (excluding node_modules)
+        while IFS= read -r file; do
+            local rel_path="${file#$REPO_ROOT/}"
+            
+            # Skip node_modules
+            if [[ "$rel_path" == *"/node_modules/"* ]]; then
+                continue
+            fi
+            
+            # Check if this path is in registry
+            if ! echo "$registry_paths" | grep -q "^${rel_path}$"; then
+                ORPHANED_FILES=$((ORPHANED_FILES + 1))
+                ORPHANED_COMPONENTS+=("$rel_path")
+                [ "$VERBOSE" = true ] && print_warning "Orphaned file (not in registry): ${rel_path}"
+            fi
+        done < <(find "$category_dir" -type f \( -name "*.md" -o -name "*.ts" \) 2>/dev/null)
+    done
+}
+
+#############################################################################
+# Reporting
+#############################################################################
+
+print_summary() {
+    echo ""
+    echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
+    echo -e "${BOLD}Validation Summary${NC}"
+    echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
+    echo ""
+    echo -e "Total paths checked:    ${CYAN}${TOTAL_PATHS}${NC}"
+    echo -e "Valid paths:            ${GREEN}${VALID_PATHS}${NC}"
+    echo -e "Missing paths:          ${RED}${MISSING_PATHS}${NC}"
+    
+    if [ "$VERBOSE" = true ]; then
+        echo -e "Orphaned files:         ${YELLOW}${ORPHANED_FILES}${NC}"
+    fi
+    
+    echo ""
+    
+    if [ $MISSING_PATHS -eq 0 ]; then
+        print_success "All registry paths are valid!"
+        
+        if [ $ORPHANED_FILES -gt 0 ] && [ "$VERBOSE" = true ]; then
+            echo ""
+            print_warning "Found ${ORPHANED_FILES} orphaned file(s) not in registry"
+            echo ""
+            echo "Orphaned files:"
+            for file in "${ORPHANED_COMPONENTS[@]}"; do
+                echo "  - $file"
+            done
+            echo ""
+            echo "Consider adding these to registry.json or removing them."
+        fi
+        
+        return 0
+    else
+        print_error "Found ${MISSING_PATHS} missing file(s)"
+        echo ""
+        echo "Missing files:"
+        for entry in "${MISSING_FILES[@]}"; do
+            IFS='|' read -r cat_id name path <<< "$entry"
+            echo "  - ${path} (${cat_id})"
+        done
+        echo ""
+        echo "Please fix these issues before proceeding."
+        
+        if [ "$FIX_MODE" = false ]; then
+            echo ""
+            print_info "Run with --fix flag to see suggested fixes"
+        fi
+        
+        return 1
+    fi
+}
+
+#############################################################################
+# Main
+#############################################################################
+
+main() {
+    # Parse arguments
+    while [ $# -gt 0 ]; do
+        case "$1" in
+            -v|--verbose)
+                VERBOSE=true
+                shift
+                ;;
+            -f|--fix)
+                FIX_MODE=true
+                VERBOSE=true
+                shift
+                ;;
+            -h|--help)
+                usage
+                ;;
+            *)
+                echo "Unknown option: $1"
+                usage
+                ;;
+        esac
+    done
+    
+    print_header
+    
+    # Check dependencies
+    check_dependencies
+    
+    # Validate registry file
+    validate_registry_file
+    
+    echo ""
+    print_info "Validating component paths..."
+    echo ""
+    
+    # Validate each category
+    validate_component_paths "agents" "Agents"
+    validate_component_paths "subagents" "Subagents"
+    validate_component_paths "commands" "Commands"
+    validate_component_paths "tools" "Tools"
+    validate_component_paths "plugins" "Plugins"
+    validate_component_paths "contexts" "Contexts"
+    validate_component_paths "config" "Config"
+    
+    # Scan for orphaned files if verbose
+    if [ "$VERBOSE" = true ]; then
+        scan_for_orphaned_files
+    fi
+    
+    # Print summary and exit with appropriate code
+    if print_summary; then
+        exit 0
+    else
+        exit 1
+    fi
+}
+
+main "$@"