#!/usr/bin/env python3 """ AM.1.1: Inventory all uppercase files and directories for lowercase migration.
This script scans the coditect-core repository and generates a comprehensive inventory of all files and directories that need to be renamed.
Usage: python3 scripts/lowercase-migration/inventory-uppercase.py python3 scripts/lowercase-migration/inventory-uppercase.py --csv python3 scripts/lowercase-migration/inventory-uppercase.py --json """
import os import re import json import csv import sys from pathlib import Path from datetime import datetime from collections import defaultdict
Exceptions - these exact filenames should NOT be renamed
EXCEPTIONS = { 'README.md', 'CLAUDE.md', 'LICENSE', 'LICENSE.md', 'CHANGELOG.md', 'CONTRIBUTING.md', 'CODE_OF_CONDUCT.md', 'CODITECT.md', 'Makefile', 'Dockerfile', 'Cargo.toml', 'Cargo.lock', }
Directories to skip entirely
SKIP_DIRS = { '.git', '.venv', 'venv', 'node_modules', 'pycache', '.mypy_cache', '.pytest_cache', 'target', 'dist', 'build', '.tox', }
External standard directories to preserve (ISO standards, etc.)
PRESERVE_PATTERNS = [ r'ISO-IEC\d+', r'RFC\d+', ]
def has_uppercase(name: str) -> bool: """Check if a name contains uppercase letters.""" return bool(re.search(r'[A-Z]', name))
def is_exception(name: str) -> bool: """Check if a name is in the exceptions list.""" return name in EXCEPTIONS
def should_preserve(name: str) -> bool: """Check if a name matches external standard patterns.""" for pattern in PRESERVE_PATTERNS: if re.match(pattern, name): return True return False
def get_lowercase_name(name: str) -> str: """Convert a name to lowercase-kebab-case.""" # Simple lowercase conversion return name.lower()
def scan_directory(root_path: Path) -> dict: """Scan directory and return inventory of items to rename.""" inventory = { 'directories': [], 'files': [], 'exceptions': [], 'preserved': [], 'stats': defaultdict(int), }
for dirpath, dirnames, filenames in os.walk(root_path):
# Skip excluded directories
dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS]
rel_dirpath = Path(dirpath).relative_to(root_path)
# Check directories
for dirname in dirnames:
if has_uppercase(dirname):
full_path = Path(dirpath) / dirname
rel_path = rel_dirpath / dirname
if should_preserve(dirname):
inventory['preserved'].append({
'type': 'directory',
'path': str(rel_path),
'name': dirname,
'reason': 'external_standard',
})
inventory['stats']['directories_preserved'] += 1
else:
inventory['directories'].append({
'path': str(rel_path),
'name': dirname,
'new_name': get_lowercase_name(dirname),
'depth': len(rel_path.parts),
})
inventory['stats']['directories_to_rename'] += 1
# Check files
for filename in filenames:
if has_uppercase(filename):
full_path = Path(dirpath) / filename
rel_path = rel_dirpath / filename
if is_exception(filename):
inventory['exceptions'].append({
'type': 'file',
'path': str(rel_path),
'name': filename,
'reason': 'industry_standard',
})
inventory['stats']['files_exception'] += 1
elif should_preserve(filename):
inventory['preserved'].append({
'type': 'file',
'path': str(rel_path),
'name': filename,
'reason': 'external_standard',
})
inventory['stats']['files_preserved'] += 1
else:
inventory['files'].append({
'path': str(rel_path),
'name': filename,
'new_name': get_lowercase_name(filename),
'extension': Path(filename).suffix,
})
inventory['stats']['files_to_rename'] += 1
# Sort directories by depth (deepest first for safe renaming)
inventory['directories'].sort(key=lambda x: -x['depth'])
return inventory
def generate_csv_report(inventory: dict, output_path: Path): """Generate CSV report of items to rename.""" with open(output_path, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['Type', 'Current Path', 'Current Name', 'New Name', 'Depth/Extension'])
for item in inventory['directories']:
writer.writerow(['directory', item['path'], item['name'], item['new_name'], item['depth']])
for item in inventory['files']:
writer.writerow(['file', item['path'], item['name'], item['new_name'], item['extension']])
print(f"CSV report written to: {output_path}")
def generate_json_report(inventory: dict, output_path: Path): """Generate JSON report of items to rename.""" report = { 'generated': datetime.now().isoformat(), 'stats': dict(inventory['stats']), 'directories_to_rename': inventory['directories'], 'files_to_rename': inventory['files'], 'exceptions': inventory['exceptions'], 'preserved': inventory['preserved'], }
with open(output_path, 'w') as f:
json.dump(report, f, indent=2)
print(f"JSON report written to: {output_path}")
def print_summary(inventory: dict): """Print summary of inventory.""" stats = inventory['stats']
print("\n" + "=" * 60)
print("LOWERCASE MIGRATION INVENTORY SUMMARY")
print("=" * 60)
print(f"\nDirectories to rename: {stats['directories_to_rename']}")
print(f"Files to rename: {stats['files_to_rename']}")
print(f"Files (exceptions): {stats['files_exception']}")
print(f"Items preserved: {stats['directories_preserved'] + stats['files_preserved']}")
print(f"\nTOTAL TO RENAME: {stats['directories_to_rename'] + stats['files_to_rename']}")
# Group by directory prefix
print("\n" + "-" * 60)
print("BREAKDOWN BY AREA")
print("-" * 60)
area_counts = defaultdict(lambda: {'dirs': 0, 'files': 0})
for item in inventory['directories']:
parts = Path(item['path']).parts
area = parts[0] if parts else 'root'
area_counts[area]['dirs'] += 1
for item in inventory['files']:
parts = Path(item['path']).parts
area = parts[0] if parts else 'root'
area_counts[area]['files'] += 1
for area, counts in sorted(area_counts.items(), key=lambda x: -(x[1]['dirs'] + x[1]['files'])):
print(f" {area:40} dirs: {counts['dirs']:4} files: {counts['files']:5}")
def main(): # Find coditect-core root script_path = Path(file).resolve() # scripts/lowercase-migration/inventory-uppercase.py -> coditect-core root_path = script_path.parent.parent.parent
if not (root_path / 'CLAUDE.md').exists():
print(f"Error: Could not find coditect-core root at {root_path}")
sys.exit(1)
print(f"Scanning: {root_path}")
# Scan directory
inventory = scan_directory(root_path)
# Print summary
print_summary(inventory)
# Generate reports based on args
output_dir = root_path / 'context-storage' / 'lowercase-migration'
output_dir.mkdir(parents=True, exist_ok=True)
if '--csv' in sys.argv or '--all' in sys.argv:
generate_csv_report(inventory, output_dir / 'inventory.csv')
if '--json' in sys.argv or '--all' in sys.argv:
generate_json_report(inventory, output_dir / 'inventory.json')
# Always generate JSON for programmatic use
generate_json_report(inventory, output_dir / 'inventory.json')
print("\n" + "=" * 60)
print("NEXT STEPS")
print("=" * 60)
print("1. Review inventory in context-storage/lowercase-migration/")
print("2. Run: python3 scripts/lowercase-migration/execute-rename.py --dry-run")
print("3. Run: python3 scripts/lowercase-migration/execute-rename.py")
print("4. Run: python3 scripts/lowercase-migration/update-references.py")
if name == 'main': main()