Communityライティング&編集github.com

BESSER-PEARL/BESSER-Skills

BESSERのためのAIエージェントスキル — UMLクラスモデリング、コード生成、トラブルシューティング、コントリビューターワークフローに関する深いBESSER知識を提供します。

BESSER-Skills とは?

BESSER-Skillsは、AIコーディングエージェント(Claude Code、Cursor、Codexなど)がBESSERプラットフォームの開発に貢献する際に必要な手順と知識を提供するスキルです。このスキルは、UMLクラスモデリングに基づくコード生成、新しいジェネレーターやメタモデルの追加、テスト、ドキュメント作成、プルリクエストのワークフローをカバーしています。主な活用例として、スキャフォールドスクリプトを使用したジェネレーターの雛形作成、Jinja2テンプレートを用いたコード生成、Webエディターバックエンドへの登録、pytestを用いたテスト作成などが含まれます。開発者はこのスキルを使用して、BESSERのコードベースに対する変更を迅速かつ正確に行うことができ、特にUMLからさまざまなプログラミング言語やフレームワークへのコード生成機能の拡張に役立ちます。

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

Installed? Explore more ライティング&編集 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

BESSERへの貢献

このスキルは、BESSERコードベースへの貢献に関する手続き的なワークフローをカバーしています。アーキテクチャの詳細やコーディング規約については、プロジェクトの CLAUDE.md ファイルも参照してください。このファイルは自動的にコンテキストに読み込まれます。


開発環境のセットアップ

git clone https://github.com/<あなたのユーザー名>/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  # ドキュメントビルド用
pip install -e .                       # 編集可能インストール

確認: python tests/BUML/metamodel/structural/library/library.py

Python 3.10+ が必要です(Django 5.x 依存関係のため)。


新しいジェネレーターの追加

最も一般的な貢献のタイプです。ボイラープレートをスキップするには、付属のスキャフォールドを実行します。

python scripts/scaffold_generator.py <名前> <BESSERリポジトリへのパス>
# 例: python scripts/scaffold_generator.py graphql ~/code/BESSER

これにより、動作可能なスタブパッケージ(ジェネレータークラス + Jinjaテンプレート + pytest)が書き込まれ、すぐに実行可能になります。ただし、実際のロジックの実装、Webエディターの設定への登録、ドキュメントの追加は、以下の手順で行う必要があります。

手動で行う場合は、以下の6つの手順に従ってください。

ステップ1: ジェネレーターパッケージの作成

besser/generators/my_generator/
  __init__.py               # エクスポート: from .my_generator import MyGenerator
  my_generator.py           # ジェネレータークラス
  templates/                # Jinja2テンプレート
    output_template.py.j2

既存のシンプルなジェネレーター(例: python_classes/)からスキャフォールドをコピーします。

ステップ2: 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):
        """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)

主な要件:

  • super().__init__(model, output_dir) を呼び出す — これにより self.model と self.output_dir が設定されます。
  • self.build_generation_path(filename) を使用して正しい出力ファイルパスを取得します。
  • ディレクトリパスのみが必要な場合は self.build_generation_dir() を使用します。
  • Jinja2テンプレートはジェネレーターと同じディレクトリの templates/ サブディレクトリに配置します。
  • 出力は決定論的である必要があります — 同じモデルは毎回同じ出力を生成します(タイムスタンプ、ランダム値などは不可)。
  • 再利用可能なヘルパーは besser/utilities/ に配置し、ジェネレーター内には配置しません。

ステップ3: Jinja2テンプレートの作成

{# besser/generators/my_generator/templates/output_template.py.j2 #}
# Generated by BESSER MyGenerator
{% for cls in classes %}
class {{ cls.name }}:
    {% for attr in cls.attributes %}
    {{ attr.name }}: {{ attr.type.name }}
    {% endfor %}

{% endfor %}

JSX/Reactを生成する場合は、競合を避けるために非標準のデリミタを使用します:

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

ステップ4: Webエディターバックエンドへの登録

ジェネレーターを besser/utilities/web_modeling_editor/backend/config/generators.py に追加します:

from besser.generators.my_generator import MyGenerator

GENERATORS = {
    # ... 既存のジェネレーター ...
    "my_generator": GeneratorInfo(
        generator_class=MyGenerator,
        output_type="file",           # "file"(単一ファイル)または "zip"(複数ファイル)
        file_extension=".py",         # 出力のファイル拡張子
        category="my_category",       # グループ化ラベル
        requires_class_diagram=True,  # DomainModelが必要な場合はTrue
    ),
}

ジェネレーターが特定のファイル名を生成する場合は、同じファイルの get_filename_for_generator() も更新します。

ステップ5: テストの作成

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():
    """小さなテストモデルを構築します。"""
    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

コードベース全体で使用されるテストパターン:

  • 出力ディレクトリには tmp_path または tmpdir フィクスチャを使用します(自動クリーンアップ)。
  • フィクスチャとして小さなインラインモデルを構築します — 外部ファイルに依存しないでください。
  • 構造(ファイルが存在する、クラス名が含まれている)と内容(ビジネスロジック)の両方をテストします。
  • インポート可能なPythonを生成するジェネレーターの場合は、importlib を使用して生成されたコードを動的にインポートしてテストします。
  • pytestのtmpフィクスチャを使用しない場合は、shutil.rmtree() でクリーンアップします。

ステップ6: ドキュメントの作成

docs/source/generators/my_generator.rst にドキュメントを追加します:

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

MyGenerator は、B-UMLドメインモデルから ... を生成します。

前提条件
------------

- BESSERがインストールされていること
- ...

使用方法
-----

.. code-block:: python

   from besser.generators.my_generator import MyGenerator

   gen = MyGenerator(model=my_model, output_dir="./output")
   gen.generate()

出力
------

このジェネレーターは、... を含む単一のファイル ``my_output.py`` を生成します。

docs/source/generators.rst のジェネレーター一覧に追加します。


新しいメタモデル/サブDSLの追加

ステップ1: メタモデルの設計

besser/BUML/metamodel/your_dsl/ に新しいクラスを作成します:

besser/BUML/metamodel/your_dsl/
  __init__.py
  your_dsl.py    # メタモデルクラス

既存のパターンに従います:

  • 基本クラスとして NamedElement または Element を拡張します。
  • ゲッター/セッターによる検証を行うプライベートプロパティを使用します。
  • 既存のメタモデルパッケージと一貫性のある命名を維持します。

ステップ2: コンバーターの追加(Webエディター統合が必要な場合)

コンバーターは、フロントエンドのJSONと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

コンバーターは対称的でなければなりません: 一方の方向でサポートされている機能は、もう一方の方向でも機能する必要があります。ラウンドトリップをテストします: JSON → BUML → JSON は元のデータと一致する必要があります。

ステップ3: バリデーションの追加

メタモデルにセッターで強制できる以上の制約がある場合:

def validate(self, raise_exception=True):
    errors = []
    warnings = []
    # 構造ルールのチェック
    if some_bad_condition:
        errors.append("説明的なエラーメッセージ")
    result = {"success": len(errors) == 0, "errors": errors, "warnings": warnings}
    if raise_exception and errors:
        raise ValueError("\n".join(errors))
    return result

ステップ4: バックエンドへの組み込み

  • besser/utilities/web_modeling_editor/backend/backend.py にエンドポイントを追加します。
  • backend/models/ にPydanticのリクエスト/レスポンスモデルを追加します。
  • 該当する場合は /validate-diagram の下にバリデーションルートを追加します。

ステップ5: テストの作成

テストは tests/BUML/metamodel/your_dsl/ に配置します:

  • 有効なモデル構築テスト。
  • 無効なモデルテスト(ValueError、TypeError が期待される)。
  • 該当する場合はラウンドトリップコンバーターテスト。

ステップ6: ドキュメントの作成

  • メタモデルのドキュメント: docs/source/buml_language/your_dsl.rst
  • docs/source/buml_language.rst 一覧に追加します。

テスト

すべてのテストを実行

python -m pytest

特定のテストを実行

# ディレクトリ指定
python -m pytest tests/generators/sqlalchemy/

# キーワード指定
python -m pytest -k "test_django"

# 詳細出力あり
python -m pytest -v tests/BUML/metamodel/structural/

# 最初の失敗で停止
python -m pytest -x

テストの規約

  • テストファイル: test_*.py
  • テスト関数: test_*
  • 再利用可能なモデルとディレクトリには @pytest.fixture を使用します。
  • 出力ディレクトリには tmp_path を使用します(pytestが自動クリーンアップ)。
  • ファイルの存在だけでなく、特定の内容をアサートします。
  • エラー条件は pytest.raises(ValueError) でテストします。

テスト対象

メタモデルの変更: 構築検証、セッター制約、無効な入力に対する期待されるエラー、validate() の結果。

ジェネレーター: 出力ファイルの存在、出力内のキーコンテンツ(クラス名、関連マッピング)、エッジケース(空のモデル、深い継承、多対多)。

コンバーター: ラウンドトリップの忠実性(JSON → BUML → JSON が同等であること)。


ドキュメントのビルド

cd docs

# Windows:
make.bat html

# macOS/Linux:
make html

# 表示:
# ブラウザで docs/build/html/index.html を開く

ドキュメントはSphinxとFuroテーマを使用しています。ソースファイルは docs/source/ にある reStructuredText(.rst)です。

ドキュメント構造

パス内容
docs/source/buml_language/メタモデルのドキュメント
docs/source/generators/ジェネレーターのドキュメント
docs/source/web_editor.rstWebエディターAPIドキュメント
docs/source/utilities/ユーティリティのドキュメント
docs/source/contributing/コントリビューターガイド
docs/source/releases/リリースノート
docs/source/_static/共有画像とアセット
docs/source/img/図とスクリーンショット

相互参照

クロスリンクにはSphinxのロールを使用します:

  • :doc:\generators/django`` — 別のドキュメントページへのリンク
  • :mod:\besser.generators.django`` — モジュールAPIへのリンク
  • :class:\besser.BUML.metamodel.structural.Class`` — クラスAPIへのリンク
  • :ref:\label-name`` — ラベル付きセクションへのリンク

コードスタイル

  • PEP 8、4スペースインデント。
  • 120文字の行制限(pyproject.toml の [tool.pylint.'FORMAT'] で設定)。
  • 公開APIには型ヒント。
  • PEP 257 docstring。
  • インポート順: 標準ライブラリ、サードパーティ、ローカルモジュール。
  • 関数/変数は snake_case、クラスは PascalCase、定数は UPPER_CASE。
  • ワイルドカードインポート、暗黙の再エクスポートは禁止。
  • リンター: pylint(現在ruff/black/pre-commitフックは設定されていません)。

コミットとPRの規約

コミットメッセージ

Conventional Commits を使用します:

feat: AWS EKS用のTerraformジェネレーターを追加
fix: 0..*関連の多重度解析を修正
refactor: 共有テンプレートヘルパーをbesser.utilitiesに抽出
docs: QiskitGeneratorの使用法をドキュメント化
test: GUIモデルコンバーターのラウンドトリップテストを追加

プルリクエストのワークフロー

  1. トピックブランチを作成: git checkout -b feature/add-my-generator。
  2. 焦点を絞った、論理的にグループ化されたコミットを行います。
  3. ローカルでテストを実行: python -m pytest。
  4. ドキュメントをビルド: cd docs && make html。
  5. 最新の master にリベース: git fetch origin && git rebase origin/master。
  6. プッシュして master に対してPRを開きます。
  7. PRテンプレートに記入: 説明、実行したテスト、追加のコンテキスト。
  8. マージには2人のメンテナー承認が必要です(14日後に1人の承認で十分)。

クロスリポジトリの変更(BESSER + フロントエンド)

変更がBESSERとWebエディターフロントエンドの両方に影響する場合:

  1. WMEリポジトリ でWMEの変更を実装してコミットします。
  2. このリポジトリでBESSERの変更を実装します。
  3. サブモジュールポインタを更新:
    cd besser/utilities/web_modeling_editor/frontend
    git fetch && git checkout <コミット>
    cd ../../../..
    git add besser/utilities/web_modeling_editor/frontend
    
  4. 2つのPRをリンクして、レビューアが正しい順序でマージできるようにします。

CI / リリース

  • CI: 自動テストワークフローはありません — テストはPR前にローカルで実行されます。
  • リリース: GitHub Releaseを作成することでトリガーされます。python-publish.yml ワークフローがビルドし、PYPI_API_TOKEN を使用してPyPIに公開します。
  • バージョン: setup.cfg の [metadata] version で定義されます。
  • リリースノート: docs/source/releases/ にRSTファイルとして追加されます。

よくある貢献の落とし穴

  1. ロジックを重複させない — 共有ヘルパーは個々のジェネレーターではなく besser/utilities/ に配置します。
  2. 決定論を維持する — 同一の入力に対してジェネレーターは同一の出力を生成する必要があります。
  3. コンバーターを対称的に保つ — JSON → BUML が機能をサポートしている場合、BUML → JSON もサポートする必要があります。
  4. ドキュメントを更新する — バックエンドの変更は、ほとんどの場合 docs/source/ の更新が必要です。
  5. 明示的に必要な場合を除き、フロントエンドサブモジュールに触れない — UIの変更は上流のWMEリポジトリに行います。
  6. リソースをクリーンアップする — 一時ディレクトリとファイルハンドルには常に try/finally を使用します。
  7. ラウンドトリップをテストする — 特にコンバーターの場合(JSON → BUML → JSON は同一である必要があります)。

関連スキル