workspace-v1/scripts/fix_yaml_frontmatter.py
rckrdmrd cb4c0681d3 feat(workspace): Add new projects and update architecture
New projects created:
- michangarrito (marketplace mobile)
- template-saas (SaaS template)
- clinica-dental (dental ERP)
- clinica-veterinaria (veterinary ERP)

Architecture updates:
- Move catalog from core/ to shared/
- Add MCP servers structure and templates
- Add git management scripts
- Update SUBREPOSITORIOS.md with 15 new repos
- Update .gitignore for new projects

Repository infrastructure:
- 4 main repositories
- 11 subrepositorios
- Gitea remotes configured

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 04:43:28 -06:00

216 lines
7.1 KiB
Python

#!/usr/bin/env python3
"""
Script para agregar YAML front-matter a archivos markdown que no lo tienen.
Soporta múltiples proyectos del workspace.
"""
import os
import re
from datetime import datetime
from pathlib import Path
def extract_title_from_content(content: str, filename: str) -> str:
"""Extrae título del primer H1 o usa el nombre del archivo."""
match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
if match:
return match.group(1).strip()
# Fallback: usar nombre de archivo
name = Path(filename).stem
return name.replace('-', ' ').replace('_', ' ').title()
def determine_file_type(filepath: str, filename: str) -> str:
"""Determina el tipo de documento basado en path y nombre."""
filepath_lower = filepath.lower()
filename_lower = filename.lower()
# Por prefijo de archivo
if filename_lower.startswith('rf-'):
return 'Requirement'
elif filename_lower.startswith('us-'):
return 'User Story'
elif filename_lower.startswith('et-'):
return 'Technical Specification'
elif filename_lower.startswith('adr-'):
return 'ADR'
elif filename_lower.startswith('epic-'):
return 'Epic'
elif filename_lower.startswith('pmc-') or filename_lower.startswith('oqi-'):
return 'Module Definition'
# Por nombre específico
if '_map' in filename_lower or '_index' in filename_lower:
return 'Index'
elif 'readme' in filename_lower:
return 'README'
elif 'vision' in filename_lower:
return 'Vision'
elif 'arquitectura' in filename_lower or 'architecture' in filename_lower:
return 'Architecture'
elif 'roadmap' in filename_lower:
return 'Roadmap'
elif 'guia' in filename_lower or 'guide' in filename_lower:
return 'Guide'
elif 'glosario' in filename_lower:
return 'Glossary'
elif 'board' in filename_lower:
return 'Planning'
elif 'definition-of' in filename_lower:
return 'Process'
elif 'auditoria' in filename_lower:
return 'Audit'
elif 'analisis' in filename_lower:
return 'Analysis'
elif 'modelo' in filename_lower or 'esquema' in filename_lower:
return 'Model'
elif 'api' in filename_lower:
return 'API Documentation'
# Por carpeta
if '/requerimientos/' in filepath_lower or '/requirements/' in filepath_lower:
return 'Requirement'
elif '/historias-usuario/' in filepath_lower or '/user-stories/' in filepath_lower:
return 'User Story'
elif '/especificaciones/' in filepath_lower:
return 'Technical Specification'
elif '/adr/' in filepath_lower or '97-adr' in filepath_lower:
return 'ADR'
elif '/planning/' in filepath_lower:
return 'Planning'
elif '/guias/' in filepath_lower or '/guides/' in filepath_lower:
return 'Guide'
return 'Documentation'
def extract_epic_from_path(filepath: str) -> str:
"""Extrae el epic/módulo del path si existe."""
# Buscar patrones como OQI-001, PMC-001, EPIC-001, etc.
match = re.search(r'(OQI-\d+|PMC-\d+|EPIC-\d+|BA-\d+|IA-\d+)', filepath, re.IGNORECASE)
if match:
return match.group(1).upper()
return ''
def generate_id(filename: str) -> str:
"""Genera un ID único basado en el nombre del archivo."""
name = Path(filename).stem
return name.upper().replace(' ', '-')
def determine_status(doc_type: str) -> str:
"""Determina el status por defecto según el tipo."""
if doc_type in ['Requirement', 'User Story', 'Technical Specification']:
return 'Draft'
elif doc_type in ['Vision', 'Architecture', 'Guide']:
return 'Active'
return 'Draft'
def has_yaml_frontmatter(content: str) -> bool:
"""Verifica si el contenido ya tiene YAML front-matter."""
return content.strip().startswith('---')
def add_yaml_frontmatter(filepath: str, project_name: str) -> bool:
"""Agrega YAML front-matter a un archivo si no lo tiene."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
if has_yaml_frontmatter(content):
return False
filename = os.path.basename(filepath)
title = extract_title_from_content(content, filename)
doc_type = determine_file_type(filepath, filename)
doc_id = generate_id(filename)
epic = extract_epic_from_path(filepath)
status = determine_status(doc_type)
today = datetime.now().strftime('%Y-%m-%d')
# Construir YAML
yaml_lines = [
'---',
f'id: "{doc_id}"',
f'title: "{title}"',
f'type: "{doc_type}"',
]
if epic:
yaml_lines.append(f'epic: "{epic}"')
yaml_lines.extend([
f'status: "{status}"',
f'project: "{project_name}"',
f'version: "1.0.0"',
f'created_date: "{today}"',
f'updated_date: "{today}"',
'---',
''
])
yaml_header = '\n'.join(yaml_lines)
new_content = yaml_header + content
with open(filepath, 'w', encoding='utf-8') as f:
f.write(new_content)
return True
except Exception as e:
print(f"Error procesando {filepath}: {e}")
return False
def process_project(docs_path: str, project_name: str):
"""Procesa todos los archivos .md de un proyecto."""
processed = 0
skipped = 0
errors = 0
for root, dirs, files in os.walk(docs_path):
for filename in files:
if filename.endswith('.md'):
filepath = os.path.join(root, filename)
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
if has_yaml_frontmatter(content):
skipped += 1
else:
if add_yaml_frontmatter(filepath, project_name):
processed += 1
print(f"{filepath}")
else:
errors += 1
except Exception as e:
errors += 1
print(f"{filepath}: {e}")
return processed, skipped, errors
def main():
base_path = '/home/isem/workspace-v1/projects'
projects = [
('platform_marketing_content', 'platform_marketing_content'),
('trading-platform', 'trading-platform'),
]
total_processed = 0
total_skipped = 0
total_errors = 0
for folder, project_name in projects:
docs_path = os.path.join(base_path, folder, 'docs')
if os.path.exists(docs_path):
print(f"\n=== Procesando: {project_name} ===")
processed, skipped, errors = process_project(docs_path, project_name)
print(f" Processed: {processed}, Skipped: {skipped}, Errors: {errors}")
total_processed += processed
total_skipped += skipped
total_errors += errors
else:
print(f"No existe: {docs_path}")
print(f"\n=== TOTAL ===")
print(f"Processed: {total_processed}")
print(f"Skipped: {total_skipped}")
print(f"Errors: {total_errors}")
if __name__ == '__main__':
main()