CommunityRédaction et éditiongithub.com

BESSER-PEARL/BESSER-Skills

Compétences agent pour BESSER — connaissance approfondie de BESSER pour les agents IA de codage : modélisation UML, générateurs de code, dépannage et workflows contributeurs.

Qu'est-ce que BESSER-Skills ?

Ce skill fournit à un agent IA (comme Claude Code, Cursor, Codex) une connaissance exhaustive du framework BESSER, un outil de modélisation basé sur UML et de génération de code. Il couvre à la fois l'architecture du métamodèle B-UML, les workflows de contribution, et les bonnes pratiques de développement. L'agent peut ainsi guider efficacement un développeur dans la création de nouveaux générateurs de code (en suivant un processus en six étapes incluant l'implémentation de `GeneratorInterface`, l'écriture de templates Jinja2, l'enregistrement dans l'éditeur web, l'écriture de tests, et la documentation), l'ajout de nouveaux métamodèles ou sous-DSL, et la résolution de problèmes courants. Le skill détaille également les conventions de test, de style de code, de messages de commit (Conventional Commits), et le processus de pull request. Il est conçu pour être utilisé avec les agents de codage IA qui ont besoin d'un contexte profond sur le projet BESSER pour assister les contributeurs de manière précise et efficace. Le support est assuré pour les plateformes d'agents exploitant un fichier SKILL.md, comme Claude Code, Cursor, ou Codex.

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

Installed? Explore more Rédaction et édition skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Contribution à BESSER

Ce skill couvre les workflows procéduraux pour contribuer à la base de code BESSER. Pour les détails d'architecture et les conventions de code, consultez également le fichier CLAUDE.md du projet qui est chargé automatiquement dans le contexte.


Configuration de développement

git clone https://github.com/<votre-utilisateur>/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  # pour construire la doc
pip install -e .                       # installation modifiable

Vérification : python tests/BUML/metamodel/structural/library/library.py

Python 3.10+ est requis (dépendance Django 5.x).


Ajout d'un nouveau générateur

C'est le type de contribution le plus courant. Pour éviter le code standard, exécutez le squelette fourni :

python scripts/scaffold_generator.py <nom> <chemin/vers/dépôt/BESSER>
# ex : python scripts/scaffold_generator.py graphql ~/code/BESSER

Cela crée un package squelette fonctionnel (classe générateur + template Jinja + pytest), exécutable immédiatement. Vous devez encore implémenter la logique réelle, enregistrer le générateur dans la configuration de l'éditeur web et ajouter la documentation — couvert dans les étapes ci-dessous.

Si vous préférez le faire à la main, suivez ces 6 étapes :

Étape 1 : Créer le package du générateur

besser/generators/my_generator/
  __init__.py               # export : from .my_generator import MyGenerator
  my_generator.py           # la classe du générateur
  templates/                # templates Jinja2
    output_template.py.j2

Copiez le squelette d'un générateur simple existant (par exemple, python_classes/).

Étape 2 : Implémenter GeneratorInterface

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

class MyGenerator(GeneratorInterface):

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

    def generate(self):
        """Génère la sortie à partir du modèle 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("output_template.py.j2")
        output = template.render(
            classes=self.model.get_classes(),
            associations=self.model.associations,
        )
        file_path = self.build_generation_path("my_output.py")
        with open(file_path, "w", encoding="utf-8") as f:
            f.write(output)

Exigences clés :

  • Appelez super().__init__(model, output_dir) — cela configure self.model et self.output_dir.
  • Utilisez self.build_generation_path(filename) pour obtenir le chemin de fichier de sortie correct.
  • Utilisez self.build_generation_dir() si vous avez seulement besoin du chemin du répertoire.
  • Les templates Jinja2 doivent être dans un sous-répertoire templates/ à côté du générateur.
  • La sortie doit être déterministe — le même modèle produit la même sortie à chaque fois (pas d'horodatages, valeurs aléatoires, etc.).
  • Les aides réutilisables vont dans besser/utilities/, pas dans le générateur.

Étape 3 : Écrire les templates Jinja2

{# besser/generators/my_generator/templates/output_template.py.j2 #}
# Généré par BESSER MyGenerator
{% for cls in classes %}
class {{ cls.name }}:
    {% for attr in cls.attributes %}
    {{ attr.name }}: {{ attr.type.name }}
    {% endfor %}

{% endfor %}

Si vous générez du JSX/React, utilisez des délimiteurs non standard pour éviter les conflits :

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

Étape 4 : Enregistrer dans le backend de l'éditeur web

Ajoutez votre générateur à besser/utilities/web_modeling_editor/backend/config/generators.py :

from besser.generators.my_generator import MyGenerator

GENERATORS = {
    # ... générateurs existants ...
    "my_generator": GeneratorInfo(
        generator_class=MyGenerator,
        output_type="file",           # "file" pour un fichier unique, "zip" pour plusieurs fichiers
        file_extension=".py",         # extension de fichier de la sortie
        category="my_category",       # étiquette de regroupement
        requires_class_diagram=True,  # True s'il a besoin d'un DomainModel
    ),
}

Mettez également à jour get_filename_for_generator() dans le même fichier si le générateur produit un nom de fichier spécifique.

Étape 5 : Écrire les tests

Créez tests/generators/my_generator/test_my_generator.py :

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


@pytest.fixture
def domain_model():
    """Construit un petit modèle de test."""
    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 = MyGenerator(model=domain_model, output_dir=output_dir)
    gen.generate()
    assert os.path.exists(os.path.join(output_dir, "my_output.py"))


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

Modèles de test utilisés dans toute la base de code :

  • Utilisez la fixture tmp_path ou tmpdir pour les répertoires de sortie (nettoyés automatiquement).
  • Construisez de petits modèles en ligne comme fixtures — ne dépendez pas de fichiers externes.
  • Testez à la fois la structure (fichiers existent, noms de classe présents) et le contenu (logique métier).
  • Pour les générateurs qui produisent du Python importable, utilisez importlib pour importer dynamiquement et tester le code généré.
  • Nettoyez avec shutil.rmtree() si vous n'utilisez pas les fixtures tmp de pytest.

Étape 6 : Documenter

Ajoutez la documentation dans docs/source/generators/my_generator.rst :

MyGenerator
===========

Le MyGenerator produit ... à partir d'un modèle de domaine B-UML.

Prérequis
---------

- BESSER installé
- ...

Utilisation
----------

.. code-block:: python

   from besser.generators.my_generator import MyGenerator

   gen = MyGenerator(model=mon_modèle, output_dir="./sortie")
   gen.generate()

Sortie
------

Le générateur produit un seul fichier ``my_output.py`` contenant ...

Ajoutez-le à l'index des générateurs dans docs/source/generators.rst.


Ajout d'un nouveau métamodèle / sous-DSL

Étape 1 : Concevoir le métamodèle

Créez de nouvelles classes dans besser/BUML/metamodel/your_dsl/ :

besser/BUML/metamodel/your_dsl/
  __init__.py
  your_dsl.py    # classes du métamodèle

Suivez les schémas existants :

  • Étendez NamedElement ou Element comme classes de base.
  • Utilisez des propriétés privées avec validation getter/setter.
  • Gardez la nomenclature cohérente avec les packages de métamodèles existants.

Étape 2 : Ajouter des convertisseurs (si intégration dans l'éditeur web nécessaire)

Les convertisseurs traduisent entre le JSON du frontend et les objets B-UML :

besser/utilities/web_modeling_editor/backend/services/converters/
  json_to_buml/process_your_dsl.py    # JSON → BUML
  buml_to_json/your_dsl_to_json.py    # BUML → JSON

Les convertisseurs doivent être symétriques : toute fonctionnalité prise en charge dans un sens doit fonctionner dans l'autre. Testez les allers-retours : JSON → BUML → JSON doit produire l'original.

Étape 3 : Ajouter la validation

Si le métamodèle a des contraintes au-delà de ce que les setters appliquent :

def validate(self, raise_exception=True):
    errors = []
    warnings = []
    # Vérifier les règles structurelles
    if une_mauvaise_condition:
        errors.append("Message d'erreur descriptif")
    result = {"success": len(errors) == 0, "errors": errors, "warnings": warnings}
    if raise_exception and errors:
        raise ValueError("\n".join(errors))
    return result

Étape 4 : Câbler dans le backend

  • Ajoutez des points d'accès dans besser/utilities/web_modeling_editor/backend/backend.py.
  • Ajoutez des modèles de requête/réponse Pydantic dans backend/models/.
  • Ajoutez une route de validation sous /validate-diagram si applicable.

Étape 5 : Écrire les tests

Placez les tests dans tests/BUML/metamodel/your_dsl/ :

  • Tests de construction de modèle valide.
  • Tests de modèle invalide (attendez ValueError, TypeError).
  • Tests de conversion aller-retour si applicable.

Étape 6 : Documenter

  • Documentation du métamodèle : docs/source/buml_language/your_dsl.rst
  • Ajoutez à l'index docs/source/buml_language.rst.

Tests

Exécuter tous les tests

python -m pytest

Exécuter des tests spécifiques

# Par répertoire
python -m pytest tests/generators/sqlalchemy/

# Par mot-clé
python -m pytest -k "test_django"

# Avec sortie verbose
python -m pytest -v tests/BUML/metamodel/structural/

# Arrêt à la première erreur
python -m pytest -x

Conventions de test

  • Fichiers de test : test_*.py
  • Fonctions de test : test_*
  • Utilisez @pytest.fixture pour les modèles et répertoires réutilisables.
  • Utilisez tmp_path pour les répertoires de sortie (nettoyés automatiquement par pytest).
  • Affirmez un contenu spécifique, pas seulement l'existence du fichier.
  • Testez les conditions d'erreur avec pytest.raises(ValueError).

Que tester

Pour les modifications du métamodèle : validation de construction, contraintes des setters, erreurs attendues pour une entrée invalide, résultats de validate().

Pour les générateurs : existence du fichier de sortie, contenu clé dans la sortie (noms de classe, mappings de relations), cas limites (modèle vide, héritage profond, plusieurs-à-plusieurs).

Pour les convertisseurs : fidélité aller-retour (JSON → BUML → JSON doit être équivalent).


Construction de la documentation

cd docs

# Windows :
make.bat html

# macOS/Linux :
make html

# Visualisation :
# Ouvrez docs/build/html/index.html dans votre navigateur

La documentation utilise Sphinx avec le thème Furo. Les fichiers source sont en reStructuredText (.rst) dans docs/source/.

Structure de la documentation

CheminContenu
docs/source/buml_language/Documentation du métamodèle
docs/source/generators/Documentation du générateur
docs/source/web_editor.rstDocumentation de l'API de l'éditeur web
docs/source/utilities/Documentation des utilitaires
docs/source/contributing/Guides du contributeur
docs/source/releases/Notes de version
docs/source/_static/Images et ressources partagées
docs/source/img/Diagrammes et captures d'écran

Références croisées

Utilisez les rôles Sphinx pour les liens croisés :

  • :doc:\generators/django`` — lien vers une autre page de documentation
  • :mod:\besser.generators.django`` — lien vers l'API du module
  • :class:\besser.BUML.metamodel.structural.Class`` — lien vers l'API de la classe
  • :ref:\label-name`` — lien vers une section étiquetée

Style de code

  • PEP 8 avec indentation de 4 espaces.
  • Limite de ligne de 120 caractères (configurée dans pyproject.toml sous [tool.pylint.'FORMAT']).
  • Indications de type pour les API publiques.
  • Docstrings PEP 257.
  • Ordre des importations : bibliothèque standard, tiers, modules locaux.
  • snake_case pour fonctions/variables, PascalCase pour classes, UPPER_CASE pour constantes.
  • Pas d'importations génériques, pas de réexportations implicites.
  • Linting : pylint (pas de hooks pre-commit ruff/black/pre-commit actuellement configurés).

Conventions de commit et PR

Messages de commit

Utilisez les commits conventionnels :

feat : ajouter un générateur Terraform pour AWS EKS
fix : corriger l'analyse de multiplicité pour les associations 0..*
refactor : extraire les aides de template partagés vers besser.utilities
docs : documenter l'utilisation de QiskitGenerator
test : ajouter des tests aller-retour pour le convertisseur de modèle GUI

Workflow de Pull Request

  1. Créez une branche de sujet : git checkout -b feature/ajouter-mon-generateur.
  2. Faites des commits ciblés et logiquement groupés.
  3. Exécutez les tests localement : python -m pytest.
  4. Construisez la doc : cd docs && make html.
  5. Rebasculez sur le dernier master : git fetch origin && git rebase origin/master.
  6. Poussez et ouvrez la PR contre master.
  7. Remplissez le modèle de PR : description, tests exécutés, contexte supplémentaire.
  8. Deux approbations de responsables sont requises pour la fusion (une approbation suffit après 14 jours).

Changements cross-dépôts (BESSER + Frontend)

Lorsque les changements affectent à la fois BESSER et le frontend de l'éditeur web :

  1. Implémentez et validez les changements WME dans le dépôt WME.
  2. Implémentez les changements BESSER dans ce dépôt.
  3. Mettez à jour le pointeur de sous-module :
    cd besser/utilities/web_modeling_editor/frontend
    git fetch && git checkout <commit>
    cd ../../../..
    git add besser/utilities/web_modeling_editor/frontend
    
  4. Liez les deux PR afin que les examinateurs les fusionnent dans le bon ordre.

CI / Release

  • CI : Il n'y a pas de workflow de test automatisé — les tests sont exécutés localement avant les PR.
  • Release : Déclenché par la création d'une version GitHub. Le workflow python-publish.yml construit et publie sur PyPI en utilisant PYPI_API_TOKEN.
  • Version : Définie dans setup.cfg sous [metadata] version.
  • Notes de version : Ajoutées en fichiers RST dans docs/source/releases/.

Pièges courants de contribution

  1. Ne dupliquez pas la logique — les aides partagés vont dans besser/utilities/, pas dans des générateurs individuels.
  2. Maintenez le déterminisme — les générateurs doivent produire une sortie identique pour une entrée identique.
  3. Gardez les convertisseurs symétriques — si JSON → BUML prend en charge une fonctionnalité, BUML → JSON doit aussi.
  4. Mettez à jour la documentation — tout changement dans le backend nécessite probablement des mises à jour dans docs/source/.
  5. Ne touchez pas au sous-module frontend sauf si explicitement requis. Les changements UI vont dans le dépôt WME amont.
  6. Nettoyez les ressources — utilisez toujours try/finally pour les répertoires temporaires et les descripteurs de fichiers.
  7. Testez les allers-retours — en particulier pour les convertisseurs (JSON → BUML → JSON doit être l'identité).

Skills associés