Community寫作與編輯github.com

BESSER-PEARL/BESSER-Skills

Agent Skills for BESSER — deep BESSER knowledge for AI coding agents: UML class modeling, code generators, troubleshooting, and contributor workflows.

相容平台Claude Code~Codex CLICursorWindsurf
npx add-skill BESSER-PEARL/BESSER-Skills

name: besser-dev description: > Contributor guide for developing BESSER itself (https://github.com/BESSER-PEARL/BESSER). Use this skill whenever the user is working inside the BESSER source tree — adding a new generator (the most common contribution), adding a new metamodel or sub-DSL under besser/BUML/metamodel/, writing pytest tests for generators or metamodels, writing JSON↔BUML converters for the web editor, building Sphinx documentation under docs/source/, registering a generator in SUPPORTED_GENERATORS, or preparing a pull request to BESSER. Trigger on phrases like "add a new generator", "register in the web editor", "GeneratorInterface", "json_to_buml", "buml_to_json", "write tests for my generator", "build the docs", "open a PR to BESSER", or any work that touches besser/generators/, besser/BUML/metamodel/, besser/utilities/web_modeling_editor/, or tests/. Prefer this skill over besser-user when the user is contributing to BESSER rather than using BESSER to build something else. license: Apache-2.0 compatibility:


Contributing to BESSER

This skill covers the procedural workflows for contributing to the BESSER codebase. For architecture details and code conventions, also consult the project's CLAUDE.md file which is loaded into context automatically.


Development Setup

git clone https://github.com/<your-username>/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  # for building docs
pip install -e .                       # editable install

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

Python 3.10+ is required (Django 5.x dependency).


Adding a New Generator

This is the most common type of contribution. To skip the boilerplate, run the bundled scaffold:

python scripts/scaffold_generator.py <name> <path/to/BESSER/repo>
# e.g.: python scripts/scaffold_generator.py graphql ~/code/BESSER

That writes a working stub package (generator class + Jinja template + pytest), runnable immediately. You still need to implement real logic, register the generator in the web-editor config, and add docs — covered in the steps below.

If you prefer to do it by hand, follow these 6 steps:

Step 1: Create the Generator Package

besser/generators/my_generator/
  __init__.py               # export: from .my_generator import MyGenerator
  my_generator.py           # the generator class
  templates/                # Jinja2 templates
    output_template.py.j2

Copy the scaffold from an existing simple generator (e.g., python_classes/).

Step 2: Implement 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):
        """Generate output from the B-UML model."""
        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)

Key requirements:

  • Call super().__init__(model, output_dir) — this sets up self.model and self.output_dir.
  • Use self.build_generation_path(filename) to get the correct output file path.
  • Use self.build_generation_dir() if you need just the directory path.
  • Jinja2 templates should be in a templates/ subdirectory alongside the generator.
  • Output must be deterministic — same model produces same output every time (no timestamps, random values, etc.).
  • Reusable helpers go in besser/utilities/, not in the generator.

Step 3: Write Jinja2 Templates

{# 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 %}

If generating JSX/React, use non-standard delimiters to avoid conflicts:

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

Step 4: Register in the Web Editor Backend

Add your generator to besser/utilities/web_modeling_editor/backend/config/generators.py:

from besser.generators.my_generator import MyGenerator

GENERATORS = {
    # ... existing generators ...
    "my_generator": GeneratorInfo(
        generator_class=MyGenerator,
        output_type="file",           # "file" for single file, "zip" for multi-file
        file_extension=".py",         # file extension of the output
        category="my_category",       # grouping label
        requires_class_diagram=True,  # True if it needs a DomainModel
    ),
}

Also update get_filename_for_generator() in the same file if the generator produces a specific filename.

Step 5: Write Tests

Create 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():
    """Build a small test 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

Test patterns used throughout the codebase:

  • Use tmp_path or tmpdir fixture for output directories (auto-cleaned).
  • Build small inline models as fixtures — don't depend on external files.
  • Test both structure (files exist, class names present) and content (business logic).
  • For generators that produce importable Python, use importlib to dynamically import and test the generated code.
  • Clean up with shutil.rmtree() if not using pytest tmp fixtures.

Step 6: Document

Add documentation in docs/source/generators/my_generator.rst:

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

The MyGenerator produces ... from a B-UML domain model.

Prerequisites
-------------

- BESSER installed
- ...

Usage
-----

.. code-block:: python

   from besser.generators.my_generator import MyGenerator

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

Output
------

The generator produces a single file ``my_output.py`` containing ...

Add it to the generators index in docs/source/generators.rst.


Adding a New Metamodel / Sub-DSL

Step 1: Design the Metamodel

Create new classes in besser/BUML/metamodel/your_dsl/:

besser/BUML/metamodel/your_dsl/
  __init__.py
  your_dsl.py    # metamodel classes

Follow existing patterns:

  • Extend NamedElement or Element as base classes.
  • Use private properties with getter/setter validation.
  • Keep naming consistent with existing metamodel packages.

Step 2: Add Converters (if web editor integration needed)

Converters translate between frontend JSON and B-UML objects:

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

Converters must be symmetric: any feature supported in one direction must work in the other. Test round-trips: JSON → BUML → JSON should produce the original.

Step 3: Add Validation

If the metamodel has constraints beyond what setters enforce:

def validate(self, raise_exception=True):
    errors = []
    warnings = []
    # Check structural rules
    if some_bad_condition:
        errors.append("Descriptive error message")
    result = {"success": len(errors) == 0, "errors": errors, "warnings": warnings}
    if raise_exception and errors:
        raise ValueError("\n".join(errors))
    return result

Step 4: Wire into Backend

  • Add endpoint(s) in besser/utilities/web_modeling_editor/backend/backend.py.
  • Add Pydantic request/response models in backend/models/.
  • Add validation route under /validate-diagram if applicable.

Step 5: Write Tests

Place tests in tests/BUML/metamodel/your_dsl/:

  • Valid model construction tests.
  • Invalid model tests (expect ValueError, TypeError).
  • Round-trip converter tests if applicable.

Step 6: Document

  • Metamodel docs: docs/source/buml_language/your_dsl.rst
  • Add to docs/source/buml_language.rst index.

Testing

Run All Tests

python -m pytest

Run Specific Tests

# By directory
python -m pytest tests/generators/sqlalchemy/

# By keyword
python -m pytest -k "test_django"

# With verbose output
python -m pytest -v tests/BUML/metamodel/structural/

# Stop on first failure
python -m pytest -x

Test Conventions

  • Test files: test_*.py
  • Test functions: test_*
  • Use @pytest.fixture for reusable models and directories.
  • Use tmp_path for output directories (auto-cleaned by pytest).
  • Assert specific content, not just file existence.
  • Test error conditions with pytest.raises(ValueError).

What to Test

For metamodel changes: construction validation, setter constraints, expected errors for invalid input, validate() results.

For generators: output file existence, key content in output (class names, relationship mappings), edge cases (empty model, deep inheritance, many-to-many).

For converters: round-trip fidelity (JSON → BUML → JSON should be equivalent).


Building Documentation

cd docs

# Windows:
make.bat html

# macOS/Linux:
make html

# View:
# Open docs/build/html/index.html in your browser

Documentation uses Sphinx with the Furo theme. Source files are reStructuredText (.rst) in docs/source/.

Doc Structure

PathContains
docs/source/buml_language/Metamodel documentation
docs/source/generators/Generator documentation
docs/source/web_editor.rstWeb editor API docs
docs/source/utilities/Utility documentation
docs/source/contributing/Contributor guides
docs/source/releases/Release notes
docs/source/_static/Shared images and assets
docs/source/img/Diagrams and screenshots

Cross-References

Use Sphinx roles for cross-linking:

  • :doc:\generators/django`` — link to another doc page
  • :mod:\besser.generators.django`` — link to module API
  • :class:\besser.BUML.metamodel.structural.Class`` — link to class API
  • :ref:\label-name`` — link to a labeled section

Code Style

  • PEP 8 with 4-space indentation.
  • 120-character line limit (configured in pyproject.toml under [tool.pylint.'FORMAT']).
  • Type hints for public APIs.
  • PEP 257 docstrings.
  • Import order: standard library, third-party, local modules.
  • snake_case functions/variables, PascalCase classes, UPPER_CASE constants.
  • No wildcard imports, no implicit re-exports.
  • Linting: pylint (no ruff/black/pre-commit hooks currently configured).

Commit and PR Conventions

Commit Messages

Use 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 Workflow

  1. Create a topic branch: git checkout -b feature/add-my-generator.
  2. Make focused, logically grouped commits.
  3. Run tests locally: python -m pytest.
  4. Build docs: cd docs && make html.
  5. Rebase on latest master: git fetch origin && git rebase origin/master.
  6. Push and open PR against master.
  7. Fill in the PR template: description, tests executed, extra context.
  8. Two maintainer approvals required for merge (one approval enough after 14 days).

Cross-Repo Changes (BESSER + Frontend)

When changes affect both BESSER and the web editor frontend:

  1. Implement and commit WME changes in the WME repo.
  2. Implement BESSER changes in this repo.
  3. Update the submodule pointer:
    cd besser/utilities/web_modeling_editor/frontend
    git fetch && git checkout <commit>
    cd ../../../..
    git add besser/utilities/web_modeling_editor/frontend
    
  4. Link the two PRs so reviewers merge in correct order.

CI / Release

  • CI: There is no automated test workflow — tests run locally before PRs.
  • Release: Triggered by creating a GitHub Release. The python-publish.yml workflow builds and publishes to PyPI using PYPI_API_TOKEN.
  • Version: Defined in setup.cfg under [metadata] version.
  • Release notes: Added as RST files in docs/source/releases/.

Common Contribution Pitfalls

  1. Don't duplicate logic — shared helpers go in besser/utilities/, not in individual generators.
  2. Maintain determinism — generators must produce identical output for identical input.
  3. Keep converters symmetric — if JSON → BUML supports a feature, BUML → JSON must too.
  4. Update docs — any backend change likely needs docs/source/ updates.
  5. Don't touch the frontend submodule unless explicitly required. UI changes go to the upstream WME repo.
  6. Clean up resources — always use try/finally for temp directories and file handles.
  7. Test round-trips — especially for converters (JSON → BUML → JSON should be identity).

相關技能

zczjyq/de-AIGC-skill

降低中文学术写作(本/专科毕业论文)AIGC检测率的专项 skill。 基于真实论文改写实验(AIGC率从 >50% 降至 11%)归纳的规律,并参考 Wikipedia「Signs of AI writing」项目的系统性模式分类。 检测并修复中文AI写作的典型模式;含中英术语与缩写规范、会话记忆库(避免 重复释义、滚动段落摘要防前后不一致)、LaTeX 源文件保护规则。支持:降低AI率、人工润色、降低AIGC、 humanize、去AI味、学术写作润色、论文降重、AIGC检测、AI痕迹消除、学术文本 人工化处理、AIGC检测报告。

community

KokuYu-sysu/biomedical_abstract_writing

Here are the skills for agent to write the abstract related to biomedical subject.

community

hoangtng/real-estate-agents

A complete AI-powered real estate agency — 12 specialized agent paired with 12 production skills that generate CMAs, offer strategies, marketing plans, underwriting models, and compliance audits. Built on the agency-agents framework.

community

ddewinter/claude-writing-skills

Skills for incorporating your voice into both Transactional and Strategic content

community

leonsong09/worktree-closeout

Read-only closeout triage for worktrees and branches across sessions, with artifact summaries and follow-up prompts.

community

dickman419/claude-haiku-4.5-evaluation

🔍 Evaluate Claude Haiku 4.5's code analysis against 27 LLM models to enhance understanding, documentation, and troubleshooting of your nftables wrapper.

community