import os import shutil from pathlib import Path import re class ClaudeProjectOrganizer: def __init__(self): self.source_dir = Path.cwd() self.claude_dir = self.source_dir / 'claude' self.file_mapping = {} def should_skip_directory(self, dir_name): skip_dirs = {'.git', '__pycache__', 'venv', 'env', '.pytest_cache', '.vscode', 'claude'} return dir_name in skip_dirs def get_comment_prefix(self, file_extension): """Determina el prefijo de comentario según la extensión del archivo""" comment_styles = { '.py': '#', '.js': '//', '.css': '/*', '.html': '', } return comment_suffixes.get(file_extension.lower(), '') def normalize_path(self, path_str: str) -> str: """Normaliza la ruta usando forward slashes""" return str(path_str).replace('\\', '/') def check_existing_path_comment(self, content: str, normalized_path: str, comment_prefix: str) -> bool: """Verifica si ya existe un comentario con la ruta en el archivo""" # Escapar caracteres especiales en el prefijo de comentario para regex escaped_prefix = re.escape(comment_prefix) # Crear patrones para buscar tanto forward como backward slashes forward_pattern = f"{escaped_prefix}\\s*{re.escape(normalized_path)}\\b" backward_path = normalized_path.replace('/', '\\\\') # Doble backslash para el patrón backward_pattern = f"{escaped_prefix}\\s*{re.escape(backward_path)}" # Buscar en las primeras líneas del archivo first_lines = content.split('\n')[:5] for line in first_lines: if (re.search(forward_pattern, line) or re.search(backward_pattern, line)): return True return False def add_path_comment(self, file_path: Path, content: str) -> str: """Agrega un comentario con la ruta al inicio del archivo si no existe""" relative_path = file_path.relative_to(self.source_dir) normalized_path = self.normalize_path(relative_path) comment_prefix = self.get_comment_prefix(file_path.suffix) if comment_prefix is None: return content comment_suffix = self.get_comment_suffix(file_path.suffix) # Verificar si ya existe el comentario if self.check_existing_path_comment(content, normalized_path, comment_prefix): print(f" - Comentario de ruta ya existe en {file_path}") return content path_comment = f"{comment_prefix} {normalized_path}{comment_suffix}\n" # Para archivos HTML, insertar después del doctype si existe if file_path.suffix.lower() == '.html': if content.lower().startswith('') + 1 return content[:doctype_end] + '\n' + path_comment + content[doctype_end:] return path_comment + content def clean_claude_directory(self): if self.claude_dir.exists(): shutil.rmtree(self.claude_dir) self.claude_dir.mkdir() print(f"Directorio claude limpiado: {self.claude_dir}") def copy_files(self): self.clean_claude_directory() for root, dirs, files in os.walk(self.source_dir): dirs[:] = [d for d in dirs if not self.should_skip_directory(d)] current_path = Path(root) for file in files: file_path = current_path / file if file.endswith(('.py', '.js', '.css', '.html', '.json', '.yml', '.yaml', '.tsx', '.ts', '.jsx', '.scss', '.less')): target_path = self.claude_dir / file # Si el archivo ya existe en el directorio claude, agregar un sufijo numérico if target_path.exists(): base = target_path.stem ext = target_path.suffix counter = 1 while target_path.exists(): target_path = self.claude_dir / f"{base}_{counter}{ext}" counter += 1 try: # Leer el contenido del archivo with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # Agregar el comentario con la ruta si no existe modified_content = self.add_path_comment(file_path, content) # Escribir el nuevo contenido with open(target_path, 'w', encoding='utf-8', newline='\n') as f: f.write(modified_content) self.file_mapping[str(file_path)] = target_path.name print(f"Copiado: {file_path} -> {target_path}") except UnicodeDecodeError: print(f"Advertencia: No se pudo procesar {file_path} como texto. Copiando sin modificar...") shutil.copy2(file_path, target_path) except Exception as e: print(f"Error procesando {file_path}: {str(e)}") def generate_tree_report(self): """Genera el reporte en formato árbol visual""" report = ["Estructura del proyecto original:\n"] def add_to_report(path, prefix="", is_last=True): report.append(prefix + ("└── " if is_last else "├── ") + path.name) if path.is_dir() and not self.should_skip_directory(path.name): children = sorted(path.iterdir(), key=lambda x: (x.is_file(), x.name)) children = [c for c in children if not (c.is_dir() and self.should_skip_directory(c.name))] for i, child in enumerate(children): is_last_child = i == len(children) - 1 new_prefix = prefix + (" " if is_last else "│ ") add_to_report(child, new_prefix, is_last_child) add_to_report(self.source_dir) report_path = self.claude_dir / "project_structure.txt" with open(report_path, "w", encoding="utf-8") as f: f.write("\n".join(report)) print(f"\nReporte generado en: {report_path}") def main(): try: print("Iniciando organización de archivos para Claude...") organizer = ClaudeProjectOrganizer() organizer.copy_files() organizer.generate_tree_report() print("\n¡Proceso completado exitosamente!") except Exception as e: print(f"\nError durante la ejecución: {str(e)}") if __name__ == "__main__": main()