171 lines
7.3 KiB
Python
171 lines
7.3 KiB
Python
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': '<!--',
|
|
'.scss': '//',
|
|
'.less': '//',
|
|
'.tsx': '//',
|
|
'.ts': '//',
|
|
'.jsx': '//',
|
|
}
|
|
return comment_styles.get(file_extension.lower(), None)
|
|
|
|
def get_comment_suffix(self, file_extension):
|
|
"""Determina el sufijo de comentario si es necesario"""
|
|
comment_suffixes = {
|
|
'.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('<!doctype'):
|
|
doctype_end = content.find('>') + 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() |