CommunityRedacción y edicióngithub.com

BESSER-PEARL/BESSER-Skills

Habilidades de agente para BESSER: conocimiento profundo de BESSER para agentes de codificación de IA: modelado de clases UML, generadores de código, resolución de problemas y flujos de trabajo de contribución.

¿Qué es BESSER-Skills?

Este skill está diseñado para agentes de IA que trabajan con el framework BESSER de modelado y generación de código. Proporciona el conocimiento completo necesario para contribuir al repositorio BESSER, incluyendo la creación de nuevos generadores de código, la adición de meta-modelos y sub-DSLs, y la integración con el editor web. El skill guía al agente a través de todo el proceso: desde la configuración del entorno de desarrollo (Python 3.10+, pip, venv) hasta la implementación de la interfaz GeneratorInterface, la escritura de plantillas Jinja2, el registro en el backend del editor web, la creación de pruebas con pytest y la documentación con Sphinx. Además, detalla las convenciones de estilo de código, los formatos de mensajes de commit (Conventional Commits) y el flujo de trabajo de pull requests, incluyendo casos especiales como cambios cruzados entre BESSER y su frontend (WME). Es útil para desarrolladores que deseen automatizar tareas de contribución, como añadir un generador de Python a GraphQL, manteniendo la determinismo y la simetría en los convertidores de modelo. El skill está optimizado para agentes en plataformas como Claude Code, Cursor o Codex, asegurando que sigan las reglas del proyecto sin duplicar lógica ni romper la integración.

Compatible con✓Claude Code~Codex CLI✓Cursor✓Windsurf
npx skills add BESSER-PEARL/BESSER-Skills

Installed? Explore more Redacción y edición skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Contribuir a BESSER

Este skill cubre los flujos de trabajo procedurales para contribuir al código base de BESSER. Para detalles de arquitectura y convenciones de código, consulta también el archivo CLAUDE.md del proyecto, que se carga automáticamente en el contexto.


Configuración de Desarrollo

git clone https://github.com/<tu-usuario>/BESSER.git
cd BESSER
python -m venv venv
# Windows: venv\Scripts\activate
# macOS/Linux: source venv/bin/activate
pip install -r requirements.txt
pip install -r docs/requirements.txt  # para construir la documentación
pip install -e .                       # instalación editable

Verificar: python tests/BUML/metamodel/structural/library/library.py

Se requiere Python 3.10+ (dependencia de Django 5.x).


Añadir un Nuevo Generador

Este es el tipo de contribución más común. Para omitir el código repetitivo, ejecuta el andamio incluido:

python scripts/scaffold_generator.py <nombre> <ruta/al/repositorio/BESSER>
# ej.: python scripts/scaffold_generator.py graphql ~/code/BESSER

Esto escribe un paquete básico funcional (clase generadora + plantilla Jinja + pytest), ejecutable inmediatamente. Aún necesitas implementar la lógica real, registrar el generador en la configuración del editor web y añadir documentación, cubierto en los pasos siguientes.

Si prefieres hacerlo manualmente, sigue estos 6 pasos:

Paso 1: Crear el Paquete del Generador

besser/generators/mi_generador/
  __init__.py               # exportar: from .mi_generador import MiGenerador
  mi_generador.py           # la clase generadora
  templates/                # plantillas Jinja2
    plantilla_salida.py.j2

Copia el andamio de un generador simple existente (ej., python_classes/).

Paso 2: Implementar GeneratorInterface

# besser/generators/mi_generador/mi_generador.py
import os
from jinja2 import Environment, FileSystemLoader
from besser.generators import GeneratorInterface

class MiGenerador(GeneratorInterface):

    def __init__(self, model, output_dir: str = None):
        super().__init__(model, output_dir)

    def generate(self):
        """Generar salida a partir del modelo B-UML."""
        templates_path = os.path.join(os.path.dirname(__file__), "templates")
        env = Environment(
            loader=FileSystemLoader(templates_path),
            trim_blocks=True,
            lstrip_blocks=True,
        )
        template = env.get_template("plantilla_salida.py.j2")
        output = template.render(
            classes=self.model.get_classes(),
            associations=self.model.associations,
        )
        file_path = self.build_generation_path("mi_salida.py")
        with open(file_path, "w", encoding="utf-8") as f:
            f.write(output)

Requisitos clave:

  • Llamar a super().__init__(model, output_dir) — esto configura self.model y self.output_dir.
  • Usar self.build_generation_path(filename) para obtener la ruta correcta del archivo de salida.
  • Usar self.build_generation_dir() si solo necesitas la ruta del directorio.
  • Las plantillas Jinja2 deben estar en un subdirectorio templates/ junto al generador.
  • La salida debe ser determinista — el mismo modelo produce la misma salida cada vez (sin marcas de tiempo, valores aleatorios, etc.).
  • Los ayudantes reutilizables van en besser/utilities/, no en el generador.

Paso 3: Escribir Plantillas Jinja2

{# besser/generators/mi_generador/templates/plantilla_salida.py.j2 #}
# Generado por BESSER MiGenerador
{% for cls in classes %}
class {{ cls.name }}:
    {% for attr in cls.attributes %}
    {{ attr.name }}: {{ attr.type.name }}
    {% endfor %}

{% endfor %}

Si se genera JSX/React, usa delimitadores no estándar para evitar conflictos:

env = Environment(
    loader=FileSystemLoader(templates_path),
    variable_start_string="[[",
    variable_end_string="]]",
)

Paso 4: Registrar en el Backend del Editor Web

Añade tu generador a besser/utilities/web_modeling_editor/backend/config/generators.py:

from besser.generators.mi_generador import MiGenerador

GENERATORS = {
    # ... generadores existentes ...
    "mi_generador": GeneratorInfo(
        generator_class=MiGenerador,
        output_type="file",           # "file" para un solo archivo, "zip" para múltiples archivos
        file_extension=".py",         # extensión del archivo de salida
        category="mi_categoria",      # etiqueta de agrupación
        requires_class_diagram=True,  # True si necesita un DomainModel
    ),
}

También actualiza get_filename_for_generator() en el mismo archivo si el generador produce un nombre de archivo específico.

Paso 5: Escribir Pruebas

Crea tests/generators/mi_generador/test_mi_generador.py:

import os
import shutil
import pytest
from besser.BUML.metamodel.structural import (
    DomainModel, Class, Property, BinaryAssociation,
    Multiplicity, StringType, IntegerType,
)
from besser.generators.mi_generador import MiGenerador


@pytest.fixture
def domain_model():
    """Construir un modelo de prueba pequeño."""
    name_prop = Property(name="name", type=StringType)
    age_prop = Property(name="age", type=IntegerType)
    person = Class(name="Person", attributes={name_prop, age_prop})
    model = DomainModel(name="TestModel", types={person})
    return model


@pytest.fixture
def output_dir(tmp_path):
    return str(tmp_path)


def test_generate_creates_output_file(domain_model, output_dir):
    gen = MiGenerador(model=domain_model, output_dir=output_dir)
    gen.generate()
    assert os.path.exists(os.path.join(output_dir, "mi_salida.py"))


def test_generate_contains_class_name(domain_model, output_dir):
    gen = MiGenerador(model=domain_model, output_dir=output_dir)
    gen.generate()
    with open(os.path.join(output_dir, "mi_salida.py")) as f:
        content = f.read()
    assert "Person" in content
    assert "name" in content
    assert "age" in content

Patrones de prueba utilizados en todo el código base:

  • Usa la fixture tmp_path o tmpdir para directorios de salida (se limpian automáticamente).
  • Construye modelos pequeños en línea como fixtures — no dependas de archivos externos.
  • Prueba tanto la estructura (archivos existen, nombres de clases presentes) como el contenido (lógica de negocio).
  • Para generadores que producen Python importable, usa importlib para importar dinámicamente y probar el código generado.
  • Limpia con shutil.rmtree() si no usas fixtures tmp de pytest.

Paso 6: Documentar

Añade documentación en docs/source/generators/mi_generador.rst:

MiGenerador
===========

El MiGenerador produce ... a partir de un modelo de dominio B-UML.

Prerrequisitos
--------------

- BESSER instalado
- ...

Uso
----

.. code-block:: python

   from besser.generators.mi_generador import MiGenerador

   gen = MiGenerador(model=mi_modelo, output_dir="./output")
   gen.generate()

Salida
------

El generador produce un solo archivo ``mi_salida.py`` que contiene ...

Añádelo al índice de generadores en docs/source/generators.rst.


Añadir un Nuevo Meta-Modelo / Sub-DSL

Paso 1: Diseñar el Meta-Modelo

Crea nuevas clases en besser/BUML/metamodel/tu_dsl/:

besser/BUML/metamodel/tu_dsl/
  __init__.py
  tu_dsl.py    # clases del metamodelo

Sigue los patrones existentes:

  • Extiende NamedElement o Element como clases base.
  • Usa propiedades privadas con validación de getter/setter.
  • Mantén la nomenclatura coherente con los paquetes de metamodelo existentes.

Paso 2: Añadir Convertidores (si se necesita integración con el editor web)

Los convertidores traducen entre el JSON del frontend y los objetos B-UML:

besser/utilities/web_modeling_editor/backend/services/converters/
  json_to_buml/process_tu_dsl.py    # JSON → BUML
  buml_to_json/tu_dsl_to_json.py    # BUML → JSON

Los convertidores deben ser simétricos: cualquier característica soportada en una dirección debe funcionar en la otra. Prueba viajes de ida y vuelta: JSON → BUML → JSON debe producir el original.

Paso 3: Añadir Validación

Si el metamodelo tiene restricciones más allá de lo que aplican los setters:

def validate(self, raise_exception=True):
    errors = []
    warnings = []
    # Verificar reglas estructurales
    if alguna_mala_condicion:
        errors.append("Mensaje de error descriptivo")
    result = {"success": len(errors) == 0, "errors": errors, "warnings": warnings}
    if raise_exception and errors:
        raise ValueError("\n".join(errors))
    return result

Paso 4: Conectar al Backend

  • Añade endpoint(s) en besser/utilities/web_modeling_editor/backend/backend.py.
  • Añade modelos Pydantic de solicitud/respuesta en backend/models/.
  • Añade una ruta de validación bajo /validate-diagram si corresponde.

Paso 5: Escribir Pruebas

Coloca las pruebas en tests/BUML/metamodel/tu_dsl/:

  • Pruebas de construcción de modelos válidos.
  • Pruebas de modelos inválidos (esperar ValueError, TypeError).
  • Pruebas de ida y vuelta para convertidores si corresponde.

Paso 6: Documentar

  • Documentación del metamodelo: docs/source/buml_language/tu_dsl.rst
  • Añadir al índice docs/source/buml_language.rst.

Pruebas

Ejecutar Todas las Pruebas

python -m pytest

Ejecutar Pruebas Específicas

# Por directorio
python -m pytest tests/generators/sqlalchemy/

# Por palabra clave
python -m pytest -k "test_django"

# Con salida verbosa
python -m pytest -v tests/BUML/metamodel/structural/

# Detener en el primer fallo
python -m pytest -x

Convenciones de Pruebas

  • Archivos de prueba: test_*.py
  • Funciones de prueba: test_*
  • Usa @pytest.fixture para modelos y directorios reutilizables.
  • Usa tmp_path para directorios de salida (se limpian automáticamente por pytest).
  • Afirma contenido específico, no solo la existencia del archivo.
  • Prueba condiciones de error con pytest.raises(ValueError).

Qué Probar

Para cambios en el metamodelo: validación de construcción, restricciones de setters, errores esperados para entrada inválida, resultados de validate().

Para generadores: existencia de archivo de salida, contenido clave en la salida (nombres de clases, mapeos de relaciones), casos límite (modelo vacío, herencia profunda, muchos a muchos).

Para convertidores: fidelidad de ida y vuelta (JSON → BUML → JSON debe ser equivalente).


Construir Documentación

cd docs

# Windows:
make.bat html

# macOS/Linux:
make html

# Ver:
# Abre docs/build/html/index.html en tu navegador

La documentación usa Sphinx con el tema Furo. Los archivos fuente son reStructuredText (.rst) en docs/source/.

Estructura de la Documentación

RutaContenido
docs/source/buml_language/Documentación del metamodelo
docs/source/generators/Documentación del generador
docs/source/web_editor.rstDocumentación de la API del editor web
docs/source/utilities/Documentación de utilidades
docs/source/contributing/Guías para contribuidores
docs/source/releases/Notas de versión
docs/source/_static/Imágenes y activos compartidos
docs/source/img/Diagramas y capturas de pantalla

Referencias Cruzadas

Usa roles de Sphinx para enlaces cruzados:

  • :doc:\generators/django`` — enlace a otra página de documentación
  • :mod:\besser.generators.django`` — enlace a la API del módulo
  • :class:\besser.BUML.metamodel.structural.Class`` — enlace a la API de la clase
  • :ref:\nombre-etiqueta`` — enlace a una sección etiquetada

Estilo de Código

  • PEP 8 con sangría de 4 espacios.
  • Límite de línea de 120 caracteres (configurado en pyproject.toml bajo [tool.pylint.'FORMAT']).
  • Anotaciones de tipo para APIs públicas.
  • Docstrings PEP 257.
  • Orden de importaciones: biblioteca estándar, terceros, módulos locales.
  • snake_case para funciones/variables, PascalCase para clases, UPPER_CASE para constantes.
  • Sin importaciones comodín, sin reexportaciones implícitas.
  • Linting: pylint (sin hooks de pre-commit de ruff/black actualmente configurados).

Convenciones de Commits y PR

Mensajes de Commit

Usa Conventional Commits:

feat: añadir generador Terraform para AWS EKS
fix: corregir análisis de multiplicidad para asociaciones 0..*
refactor: extraer ayudantes de plantilla compartidos a besser.utilities
docs: documentar uso de QiskitGenerator
test: añadir pruebas de ida y vuelta para el convertidor de modelo GUI

Flujo de Trabajo de Pull Request

  1. Crea una rama temática: git checkout -b feature/añadir-mi-generador.
  2. Realiza commits enfocados y agrupados lógicamente.
  3. Ejecuta pruebas localmente: python -m pytest.
  4. Construye docs: cd docs && make html.
  5. Rebase sobre el último master: git fetch origin && git rebase origin/master.
  6. Sube y abre PR contra master.
  7. Completa la plantilla del PR: descripción, pruebas ejecutadas, contexto adicional.
  8. Se requieren dos aprobaciones de mantenedores para fusionar (una aprobación es suficiente después de 14 días).

Cambios Cruzados entre Repositorios (BESSER + Frontend)

Cuando los cambios afectan tanto a BESSER como al frontend del editor web:

  1. Implementa y confirma los cambios de WME en el repositorio WME.
  2. Implementa los cambios de BESSER en este repositorio.
  3. Actualiza el puntero del submódulo:
    cd besser/utilities/web_modeling_editor/frontend
    git fetch && git checkout <commit>
    cd ../../../..
    git add besser/utilities/web_modeling_editor/frontend
    
  4. Vincula los dos PRs para que los revisores fusionen en el orden correcto.

CI / Lanzamiento

  • CI: No hay un flujo de trabajo de pruebas automatizado — las pruebas se ejecutan localmente antes de los PRs.
  • Lanzamiento: Se activa creando un lanzamiento de GitHub. El flujo de trabajo python-publish.yml construye y publica en PyPI usando PYPI_API_TOKEN.
  • Versión: Definida en setup.cfg bajo [metadata] version.
  • Notas de versión: Añadidas como archivos RST en docs/source/releases/.

Errores Comunes al Contribuir

  1. No duplicar lógica — los ayudantes compartidos van en besser/utilities/, no en generadores individuales.
  2. Mantener determinismo — los generadores deben producir salida idéntica para entrada idéntica.
  3. Mantener convertidores simétricos — si JSON → BUML soporta una característica, BUML → JSON también debe.
  4. Actualizar docs — cualquier cambio en el backend probablemente necesita actualizaciones en docs/source/.
  5. No tocar el submódulo del frontend a menos que sea explícitamente necesario. Los cambios de UI van al repositorio WME ascendente.
  6. Limpiar recursos — usa siempre try/finally para directorios temporales y manejadores de archivos.
  7. Probar ida y vuelta — especialmente para convertidores (JSON → BUML → JSON debe ser identidad).

Skills relacionados