Community寫作與編輯github.com

BESSER-PEARL/BESSER-Skills

BESSER 的 AI 智慧技能 — 為 AI 程式碼代理提供深入的 BESSER 知識:UML 類別建模、程式碼生成器、故障排除與貢獻者工作流程。

BESSER-Skills 是什麼?

此技能為 AI 編碼代理(如 Claude Code、Cursor 或 Codex)提供完整的 BESSER 框架指引。它涵蓋了程式碼生成的標準實踐、B-UML 元模型設計、測試慣例以及貢獻工作流程。開發人員可以指導代理為新語言或框架搭建程式碼生成器(透過腳本或手動步驟),並遵循可重複使用性、確定性輸出和範本式生成的最佳實踐。技能也定義了新增元模型與子 DSL 的程序,包含轉換器、驗證器和對稱測試。此外,也詳細說明了文件建置、程式碼風格(PEP 8)以及帶有 Conventional Commits 的 PR 工作流程。透過作為 SKILL.md 檔案載入,它讓 AI 代理能直接引導貢獻者,保持與 BESSER 現有架構和品質標準的一致性。

相容平台✓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 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

貢獻 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 fixture(會自動清理)。
  • 將小型內聯模型建立為 fixture — 不要依賴外部檔案。
  • 測試結構(檔案存在、類別名稱存在)和內容(業務邏輯)。
  • 對於產生可匯入 Python 的生成器,使用 importlib 動態匯入並測試產生的程式碼。
  • 如果未使用 pytest tmp fixtures,請使用 shutil.rmtree() 進行清理。

步驟 6:撰寫文件

在 docs/source/generators/my_generator.rst 中新增文件:

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

The 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 作為基底類別進行擴展。
  • 使用具有 getter/setter 驗證的私有屬性。
  • 保持命名與現有元模型套件一致。

步驟 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 應產生原始的 JSON。

步驟 3:新增驗證

如果元模型有 setter 強制執行以外的約束:

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) 測試錯誤條件。

測試什麼

對於元模型變更:建構驗證、setter 約束、無效輸入的預期錯誤、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 文件字串。
  • 匯入順序:標準程式庫、第三方、本機模組。
  • 使用 snake_case 的函式/變數、PascalCase 的類別、UPPER_CASE 的常數。
  • 無萬用字元匯入,無隱含的重新匯出。
  • Linting:pylint(目前未設定 ruff/black/pre-commit hooks)。

提交和 PR 慣例

提交訊息

使用 Conventional Commits:

feat: add Terraform generator for AWS EKS
fix: correct multiplicity parsing for 0..* associations
refactor: extract shared template helpers to besser.utilities
docs: document QiskitGenerator usage
test: add round-trip tests for GUI model converter

Pull Request 工作流程

  1. 建立主題分支:git checkout -b feature/add-my-generator。
  2. 進行專注、邏輯分組的提交。
  3. 在本機執行測試:python -m pytest。
  4. 建置文件:cd docs && make html。
  5. 基於最新的 master 進行 rebase:git fetch origin && git rebase origin/master。
  6. 推送並針對 master 開啟 PR。
  7. 填寫 PR 範本:描述、執行的測試、額外上下文。
  8. 合併需要兩位維護者核准(14 天後只需一位核准即可)。

跨儲存庫變更(BESSER + 前端)

當變更同時影響 BESSER 和 Web 編輯器前端時:

  1. 在 WME 儲存庫 中實作並提交 WME 變更。
  2. 在此儲存庫中實作 BESSER 變更。
  3. 更新子模組指標:
    cd besser/utilities/web_modeling_editor/frontend
    git fetch && git checkout <commit>
    cd ../../../..
    git add besser/utilities/web_modeling_editor/frontend
    
  4. 連結兩個 PR,以便審查者以正確的順序合併。

CI / 發行

  • CI:沒有自動化的測試工作流程 — 測試在 PR 之前在本機執行。
  • 發行:透過建立 GitHub Release 觸發。python-publish.yml 工作流程會使用 PYPI_API_TOKEN 建置並發佈到 PyPI。
  • 版本:在 setup.cfg 的 [metadata] version 下定義。
  • 發行說明:以 RST 檔案形式新增到 docs/source/releases/ 中。

常見貢獻陷阱

  1. 不要重複邏輯 — 共用輔助函式應放在 besser/utilities/ 中,而非個別生成器內。
  2. 保持確定性 — 生成器必須對相同的輸入產生相同的輸出。
  3. 保持轉換器對稱 — 如果 JSON → BUML 支援某個功能,BUML → JSON 也必須支援。
  4. 更新文件 — 任何後端變更可能都需要更新 docs/source/。
  5. 除非明確要求,否則不要觸碰前端子模組。UI 變更應送往上游的 WME 儲存庫。
  6. 清理資源 — 對暫存目錄和檔案控制代碼始終使用 try/finally。
  7. 測試往返 — 特別是對於轉換器(JSON → BUML → JSON 應為恆等)。

相關技能