#!/usr/bin/env python3 """ Claude.md Validator Validates existing claude.md files for completeness, accuracy, and quality. Checks for TODO markers, outdated information, and missing key sections. Usage: python validate_claude_md.py [--strict] [--auto-fix] Examples: python validate_claude_md.py /path/to/repo python validate_claude_md.py /path/to/src/claude.md python validate_claude_md.py /path/to/repo --strict """ import os import sys import argparse from pathlib import Path from typing import List, Dict, Tuple import re REQUIRED_SECTIONS = [ 'Overview', 'Purpose' # Alternative to Overview ] RECOMMENDED_SECTIONS = [ 'Directory Structure', 'Key Files', 'Important Patterns', 'Dependencies', 'Usage' ] def find_claude_md_files(root_path: Path) -> List[Path]: """Find all claude.md files in the directory tree.""" claude_md_files = [] for dirpath, dirnames, filenames in os.walk(root_path): # Skip common ignored directories dirnames[:] = [d for d in dirnames if not d.startswith('.') and d not in { 'node_modules', '__pycache__', 'venv', 'env', 'dist', 'build' }] if 'claude.md' in filenames: claude_md_files.append(Path(dirpath) / 'claude.md') return claude_md_files def validate_claude_md(file_path: Path, strict: bool = False) -> Dict: """Validate a single claude.md file.""" issues = [] warnings = [] try: with open(file_path, 'r') as f: content = f.read() except Exception as e: return { 'valid': False, 'issues': [f"Could not read file: {e}"], 'warnings': [], 'stats': {} } lines = content.split('\n') # Check for empty or very short files if len(content.strip()) < 50: issues.append("File is too short (less than 50 characters)") # Check for TODO markers todo_count = len(re.findall(r'TODO|FIXME|XXX', content, re.IGNORECASE)) if todo_count > 0: if strict: issues.append(f"Found {todo_count} TODO/FIXME markers") else: warnings.append(f"Found {todo_count} TODO/FIXME markers") # Check for required sections has_overview = any(re.search(r'^##?\s+(Overview|Purpose)', line, re.IGNORECASE) for line in lines) if not has_overview: issues.append("Missing required section: Overview or Purpose") # Check for recommended sections found_sections = [] for section in RECOMMENDED_SECTIONS: if any(re.search(rf'^##?\s+{section}', line, re.IGNORECASE) for line in lines): found_sections.append(section) missing_recommended = set(RECOMMENDED_SECTIONS) - set(found_sections) if missing_recommended and strict: warnings.append(f"Missing recommended sections: {', '.join(missing_recommended)}") # Check for placeholder text if '