agentleFS
BESSER-PEARLBESSER-PEARL/BESSER-Skills/skills/besser-generators/SKILL.md

besser-generators

Skill9 starsChanged 5 months agoApache-2.0View on GitHub

Operational reference for BESSER code generators — covers per-generator options, generated file layout, regeneration/overwrite behavior, safe customization patterns, template overrides, and debugging generation failures. Use this skill whenever the user is configuring or running a BESSER generator (PythonGenerator, PydanticGenerator, SQLAlchemyGenerator, SQLGenerator, BackendGenerator, RESTAPIGenerator, DjangoGenerator, WebAppGenerator, ReactGenerator, BAFGenerator, QiskitGenerator, JSONSchemaGenerator, JSONObjectGenerator, RDFGenerator, SupabaseGenerator, TerraformGenerator, PytorchGenerator, TFGenerator, FlutterGenerator, JavaGenerator), wondering "where does the output go", "will my edits survive regeneration", "how do I add custom endpoints to a generated FastAPI app", or "how do I switch the database dialect". Trigger on questions about generator parameters (`http_methods`, `nested_creations`, `dbms`, `containerization`, `backend_type`, `shots`, `generation_mode`), generated file paths, template overrides, or how to extend generated code without losing edits across regeneration cycles. Prefer this skill over besser-user when the question is about a specific generator's behavior rather than how to build a model.

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every BESSER generator implements the same shape:

    line 37
  2. Very specific:

    If output_dir is omitted, output goes to <cwd>/output/.

    line 45
  3. Very specific:

    Regeneration always overwrites: Every call to generate() replaces the output files entirely.

    line 48
  4. Distinctive:

    Not generating, but stuck?

    line 97
  5. Distinctive:

    Put custom code in separate files that import from generated code.

    line 135
  6. Very specific:

    Treat the body as code you are responsible for: read routers/<class>.py to see what is in scope where the body lands, and database.py for …

    line 198

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

BESSER Generator Operations

Every BESSER generator implements the same shape:

class GeneratorInterface(ABC):
    def __init__(self, model, output_dir=None): ...
    def generate(self): ...

If output_dir is omitted, output goes to <cwd>/output/. The directory is created automatically (os.makedirs(..., exist_ok=True)).

Regeneration always overwrites. Every call to generate() replaces the output files entirely. This is by design — the model is the source of truth. Customizations belong in separate files (see "Safe customization" below). Once you have generated, the besser-user skill's "Verification checklist" covers what to check next (files exist, syntax parses, it runs).

Generate the baseline, then build on top. The point of these generators is that you don't hand-write the boilerplate. Regenerate the baseline freely and spend your effort only where the work is actually custom: extending and overriding in separate files so a re-run never clobbers it. Often the generated code is all you need; when it isn't, the "Safe customization" patterns below are how you build on top without fighting regeneration.

The output is equivalent across runs, but not byte-identical. Generators iterate unordered collections, so the same model can emit the same members in a different order each run — fields, columns, constructor arguments, table columns. Nothing changes semantically. Two consequences worth knowing: don't diff one regeneration against another expecting silence, and don't hard-code a column position when reading generated output (in a test, say) — look columns up by name.


Generator picker

Per-generator detail (inputs, outputs, options, templates, gotchas) lives in references/. Find your target, then read the listed reference:

WantGeneratorReference
Python data classesPythonGeneratorpython-and-data.md
Java classesJavaGeneratorpython-and-data.md
Pydantic models (with optional OCL→validator)PydanticGeneratorpython-and-data.md
JSON Schema (regular or NGSI-LD)JSONSchemaGeneratorpython-and-data.md
JSON data from an ObjectModel (instances)JSONObjectGeneratorpython-and-data.md
RDF Turtle vocabularyRDFGeneratorpython-and-data.md
SQLAlchemy ORMSQLAlchemyGeneratorpersistence.md
Raw SQL DDLSQLGeneratorpersistence.md
Supabase (Postgres + RLS) schemaSupabaseGeneratorpersistence.md
FastAPI + ORM + Pydantic stackBackendGeneratorapi-and-web.md
Standalone REST APIRESTAPIGeneratorapi-and-web.md
Django projectDjangoGeneratorapi-and-web.md
Full-stack web app (React + FastAPI + Docker)WebAppGeneratorapi-and-web.md
Flutter mobile appFlutterGeneratorapi-and-web.md
Conversational agent / chatbotBAFGeneratoragents-and-other.md
Quantum circuitQiskitGeneratoragents-and-other.md
Terraform infrastructureTerraformGeneratoragents-and-other.md
PyTorch / TensorFlow neural netPytorchGenerator / TFGeneratoragents-and-other.md

Not generating, but stuck?

  • A generator runs but produces wrong or empty output → references/debugging.md.
  • Installation / import errors (not generation itself) → the besser-troubleshooting skill.

Output locations

By default a generator writes to <cwd>/output/ and produces the single file documented in its reference. The exceptions worth memorizing:

  • BackendGenerator → defaults to ./output_backend/ (not ./output/); emits a package, not a single module: main_api.py (app setup only), database.py, bal_stdlib.py, routers/<class>.py per class, plus pydantic_classes.py and sql_alchemy.py.
  • WebAppGenerator → output_dir must be specified; emits frontend/, backend/, docker-compose.yml.
  • DjangoGenerator → creates a project folder in the CWD (myproject/myapp/models.py, …).
  • SupabaseGenerator → timestamped filename, so every run creates a new file and never overwrites.

For the exact output filename of any other generator, see its row in the relevant references/ file.


Safe customization patterns

Since generate() overwrites everything, customizations need a strategy that survives regeneration.

1. Generator options (try this first)

Many "I need to tweak the output" requests are already covered by parameters:

  • SQLAlchemyGenerator.generate(dbms="postgresql") — switch dialect
  • BackendGenerator(http_methods=["GET", "POST"]) — limit endpoints
  • BackendGenerator(nested_creations=True) — allow nested creates
  • DjangoGenerator(containerization=True) — add Docker support
  • QiskitGenerator(backend_type="ibm_quantum", shots=2048)
  • BAFGenerator(generation_mode=GenerationMode.CODE_ONLY)

2. Post-generation extension files

Put custom code in separate files that import from generated code. Generated files are overwritten; your extension files are not.

output/
  sql_alchemy.py       # GENERATED — do not edit
  custom_queries.py    # YOUR CODE — imports from sql_alchemy.py
  custom_endpoints.py  # YOUR CODE — imports from main_api.py
# custom_queries.py — safe from regeneration
# The generated sql_alchemy.py exposes `Base` and `engine` (not a Session);
# create the Session yourself from SQLAlchemy.
from sqlalchemy.orm import Session
from sql_alchemy import Book, Author, engine

def get_books_by_author(author_name: str):
    with Session(engine) as session:
        return session.query(Book).join(Author).filter(Author.name == author_name).all()

3. Wrapper scripts for backends

Mount the generated FastAPI app and add your routes on top:

# app.py — YOUR CODE, not generated
from main_api import app

@app.get("/custom/stats")
def custom_stats():
    return {"status": "ok"}

4. Model-level custom endpoints (BAL / CODE)

Often the cleanest fix: define methods on your model classes with MethodImplementationType.BAL or MethodImplementationType.CODE, and BackendGenerator emits matching REST endpoints. Custom logic stays in the model, where regeneration cannot touch it.

from besser.BUML.metamodel.structural import (
    Method, Parameter, StringType, MethodImplementationType,
)

# Pass the implementation via the Method constructor: `code` holds the body
# and `implementation_type` selects BAL or CODE. (There is no separate
# MethodImplementation class, and the body is not assigned afterwards.)
search = Method(
    name="search_by_title",
    parameters=[Parameter(name="keyword", type=StringType)],
    type=StringType,
    code="return session.query(Book).filter(Book.title.contains(keyword)).all()",
    implementation_type=MethodImplementationType.BAL,
)
book.add_method(search)

The method body is inserted verbatim into the generated FastAPI handler — it is not parsed or scoped by the metamodel. So any name it references (here session) must actually exist in the generated handler at that point. Treat the body as code you are responsible for: read routers/<class>.py to see what is in scope where the body lands, and database.py for the session dependency, then reference exactly those names.

Two things the body cannot assume. A method may not share its name with an attribute or an association end of the same class — the model will not validate, because one generated name cannot be both. And a name the diagram uses is not always the name the generated object uses, so confirm against the generated class before calling it.

5. Template overrides (advanced)

Copy a generator's template, edit it, and point the generator at your copy. Templates live in besser/generators/{name}/templates/. Powerful, but BESSER updates won't propagate to your overrides — use sparingly.

6. Git-based patch workflow (small repeatable edits)

git diff output/ > my_customizations.patch          # save once
cd output && git apply ../my_customizations.patch   # reapply after each generate

Works for small, stable changes. For larger customizations, prefer #2 or #4.

What NOT to do

  • Don't edit generated files directly as your primary workflow — they will be overwritten next time you generate.
  • Don't fork a generator to add one small feature. Generator options or template overrides almost always suffice.
  • Don't mix generated and hand-written code in the same file. Separation keeps your code safe across regeneration cycles.

Composite generator architecture

Several generators orchestrate sub-generators (e.g. WebAppGenerator drives ReactGenerator + BackendGenerator + optional BAFGenerator; BackendGenerator drives RESTAPIGenerator + PydanticGenerator + SQLAlchemyGenerator; SQLGenerator drives SQLAlchemyGenerator in a subprocess). When debugging, trace which sub-generator is actually failing — the error rarely lives in the wrapper. The full tree and per-symptom recipes are in references/debugging.md.


Web editor registry

A generator only appears in the web editor's "Generate" dropdown once it is registered in besser/utilities/web_modeling_editor/backend/config/generators.py via two hooks: an entry in SUPPORTED_GENERATORS and its filename in get_filename_for_generator(). Both are required for the dropdown to populate. For the annotated GeneratorInfo snippet and the full authoring workflow (package layout, tests, docs, PR), see the besser-dev skill's references/adding-a-generator.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.