#!/usr/bin/env python3 """ Project Statistics Counter Counts lines, characters, and files in the Warfactory project """ import os import sys from pathlib import Path from collections import defaultdict def get_file_extension(file_path): """Get file extension, handling special cases""" if file_path.name in ['CLAUDE.md', 'README.md', 'TODO.md']: return '.md' return file_path.suffix.lower() def is_text_file(file_path): """Determine if file is likely a text file""" text_extensions = { '.md', '.txt', '.json', '.cpp', '.h', '.hpp', '.c', '.cc', '.cxx', '.py', '.js', '.ts', '.html', '.css', '.xml', '.yaml', '.yml', '.cmake', '.sh', '.bat', '.ps1', '.toml', '.ini', '.cfg', '.conf' } # Special files without extensions special_files = {'CLAUDE.md', 'README.md', 'TODO.md', 'CMakeLists.txt', 'Makefile'} return (get_file_extension(file_path) in text_extensions or file_path.name in special_files) def should_skip_directory(dir_path): """Check if directory should be skipped""" dir_name = Path(dir_path).name # Skip common build/temp directories skip_dirs = { '.git', '.vs', '.vscode', '__pycache__', 'node_modules', 'build', 'bin', 'obj', 'Debug', 'Release', '.idea', 'external' } # Skip any directory starting with build if dir_name.startswith('build'): return True # Skip any _deps directories (external dependencies) if '_deps' in dir_name: return True # Skip CMakeFiles directories if 'CMakeFiles' in dir_name: return True # Skip external libraries directory if dir_name == 'external': return True return dir_name in skip_dirs def count_file_stats(file_path): """Count lines and characters in a file""" try: with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() lines = content.count('\n') + (1 if content and not content.endswith('\n') else 0) chars = len(content) return lines, chars except Exception as e: print(f"Warning: Could not read {file_path}: {e}") return 0, 0 def main(): """Main function to count project statistics""" # Get project root (parent of tools directory) script_dir = Path(__file__).parent project_root = script_dir.parent print(f"Analyzing project: {project_root}") print("=" * 60) # Statistics tracking total_files = 0 total_lines = 0 total_chars = 0 stats_by_extension = defaultdict(lambda: {'files': 0, 'lines': 0, 'chars': 0}) stats_by_directory = defaultdict(lambda: {'files': 0, 'lines': 0, 'chars': 0}) # Walk through all files processed_files = 0 for root, dirs, files in os.walk(project_root): # Skip unwanted directories dirs[:] = [d for d in dirs if not should_skip_directory(d)] root_path = Path(root) relative_root = root_path.relative_to(project_root) # Show progress for each directory if files: print(f"Processing: {relative_root}") for file in files: file_path = root_path / file # Only process text files if not is_text_file(file_path): continue processed_files += 1 if processed_files % 10 == 0: print(f" Processed {processed_files} files...") lines, chars = count_file_stats(file_path) total_files += 1 total_lines += lines total_chars += chars # Track by extension ext = get_file_extension(file_path) if not ext: ext = '(no extension)' stats_by_extension[ext]['files'] += 1 stats_by_extension[ext]['lines'] += lines stats_by_extension[ext]['chars'] += chars # Track by directory dir_name = str(relative_root) if relative_root != Path('.') else '(root)' stats_by_directory[dir_name]['files'] += 1 stats_by_directory[dir_name]['lines'] += lines stats_by_directory[dir_name]['chars'] += chars # Print overall statistics print(f"📊 TOTAL PROJECT STATISTICS") print(f"Files: {total_files:,}") print(f"Lines: {total_lines:,}") print(f"Characters: {total_chars:,}") print() # Print statistics by file type print("📝 BY FILE TYPE:") print(f"{'Extension':<15} {'Files':<8} {'Lines':<12} {'Characters':<15}") print("-" * 60) for ext in sorted(stats_by_extension.keys()): stats = stats_by_extension[ext] print(f"{ext:<15} {stats['files']:<8} {stats['lines']:<12,} {stats['chars']:<15,}") print() # Print statistics by directory (top level) print("📁 BY DIRECTORY:") print(f"{'Directory':<20} {'Files':<8} {'Lines':<12} {'Characters':<15}") print("-" * 65) # Sort by line count (descending) sorted_dirs = sorted(stats_by_directory.items(), key=lambda x: x[1]['lines'], reverse=True) for dir_name, stats in sorted_dirs: if stats['files'] > 0: # Only show directories with files display_name = dir_name[:19] + "..." if len(dir_name) > 19 else dir_name print(f"{display_name:<20} {stats['files']:<8} {stats['lines']:<12,} {stats['chars']:<15,}") print() print("🎯 Analysis complete!") # Calculate some fun metrics avg_lines_per_file = total_lines / total_files if total_files > 0 else 0 avg_chars_per_line = total_chars / total_lines if total_lines > 0 else 0 print(f"📈 METRICS:") print(f"Average lines per file: {avg_lines_per_file:.1f}") print(f"Average characters per line: {avg_chars_per_line:.1f}") # Estimate pages (assuming ~50 lines per page) estimated_pages = total_lines / 50 print(f"Estimated printed pages: {estimated_pages:.0f}") return 0 if __name__ == "__main__": sys.exit(main())