Fügt AGENTS.md mit umfassenden Richtlinien für Coding-Agenten hinzu: - Sprachkonventionen (Deutsch) - Code-Style & Type Annotations - Build-/Test-Kommandos mit uv - PySide6 UI-Integration - Pydantic Models & Settings - Import-Organisation & Error Handling
9.2 KiB
AGENTS.md
Entwicklerrichtlinien für DocuMentor (KI-Coding-Agenten)
Diese Datei enthält wichtige Richtlinien für Coding-Agenten, die in diesem Repository arbeiten.
Sprache
Alle Kommunikation, Code-Kommentare und UI-Texte auf Deutsch!
- Kommentare: Deutsch
- Docstrings: Deutsch
- Log-Meldungen: Deutsch
- Variablennamen: Deutsch wo kontextuell passend
- UI-Labels: Deutsch
Build & Lint Kommandos
Paketmanager: uv (NICHT pip oder poetry!)
# Abhängigkeiten installieren
uv sync
# Anwendung starten
uv run python src/main.py
# Code-Style prüfen (Zeilenlänge: 120)
uv run ruff check
# Code automatisch formatieren
uv run ruff format
Tests ausführen
# Alle Hash-Tests
uv run python test_hash_implementation.py
# Duplikatserkennung testen
uv run python test_xml_hash_duplicate_detection.py
# Einzelnen Test ausführen (direkter Python-Aufruf)
uv run python test_hash_implementation.py
Hinweis: Dieses Projekt verwendet KEINE pytest/unittest-Frameworks. Tests sind standalone Python-Skripte mit if name == "main".
Code-Style-Richtlinien
Import-Organisation
Reihenfolge (keine Leerzeilen zwischen Gruppen):
# 1. Standard Library
import os
import sys
import logging
from pathlib import Path
from typing import Optional, TYPE_CHECKING
# 2. Drittanbieter (Third-Party)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtWidgets import QDialog, QMainWindow
from pydantic import BaseModel, Field
# 3. Lokale Imports (IMMER absolute Imports, KEINE relativen .imports)
from conf import app_settings, TreeNode, XslFile
from ui.MainWindow import MainWindow
from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
Wichtig:
- Immer
from pathlib import Pathverwenden - NIEMALS String-Pfade verwenden, IMMER
Path-Objekte TYPE_CHECKINGfür zirkuläre Import-Vermeidung nutzen- Keine relativen Imports (
.oder..)
Type Annotations
Moderne Union-Syntax verwenden:
# RICHTIG
def transform(xml_path: Path, params: dict[str, str]) -> tuple[bool, str]:
result: str | None = None
files: list[Path] = []
# FALSCH
def transform(xml_path, params): # Keine Annotations
result: Optional[str] = None # Alte Union-Syntax
files: List[Path] = [] # Großgeschriebene Types
Pflicht:
- Alle Funktionsparameter annotieren
- Alle Rückgabewerte annotieren
- Moderne Syntax:
str | NonestattOptional[str] - Container:
list[str],dict[str, str],tuple[int, int]
Naming Conventions
# Klassen: PascalCase
class SaxonWorkerPool:
class TransformationJob:
# Funktionen/Methoden: snake_case
def transform_saxon(xml_file: Path) -> bool:
def calculate_hash(content: bytes) -> str:
# Private Methoden: _snake_case mit Unterstrich
def _create_tree_item(self, node: TreeNode):
def _load_project_data(self):
# Variablen: snake_case
xml_file_path = Path("test.xml")
diff_pdf_count = 0
self.current_zoom = 100
# Konstanten: UPPER_CASE
SAXON_WORKER_JAVA = """..."""
MAX_RETRY_COUNT = 3
Formatierung & Linting
- Zeilenlänge: 120 Zeichen (via Ruff konfiguriert)
- Strings: Bevorzugt Double-Quotes
"...", aber konsistent im File - Trailing Commas: Bei mehrzeiligen Strukturen verwenden
- Ruff: Alle Warnings beheben vor Commit
Error Handling
IMMER Logging statt print() verwenden:
import logging
logger = logging.getLogger(__name__)
def transform(xml_path: Path) -> tuple[bool, str]:
try:
# Operation durchführen
logger.info(f"Transformation gestartet: {xml_path}")
result = do_transform(xml_path)
logger.debug(f"Zwischenergebnis: {result}")
return True, "Erfolg"
except FileNotFoundError as e:
error_msg = f"XML-Datei nicht gefunden: {xml_path}"
logger.error(error_msg)
return False, error_msg
except Exception as e:
error_msg = f"Fehler bei Transformation: {str(e)}"
logger.exception(error_msg) # Mit Stack Trace
return False, error_msg
Pattern:
logger.debug()für Debugging-Infoslogger.info()für normale Operationenlogger.warning()für Warnungenlogger.error()für Fehler ohne Stack Tracelogger.exception()für Fehler MIT Stack Trace- Fehlermeldungen auf Deutsch
Docstrings
Google-Style auf Deutsch:
def transform_xml_to_pdf(xml_path: Path, xsl_path: Path, output_dir: Path) -> tuple[bool, str]:
"""
Transformiert eine XML-Datei mit XSL zu PDF.
Args:
xml_path: Pfad zur XML-Eingabedatei
xsl_path: Pfad zum XSL-Stylesheet
output_dir: Zielverzeichnis für PDF-Ausgabe
Returns:
tuple[bool, str]: (Erfolg, Fehlermeldung oder Info-Text)
Raises:
FileNotFoundError: Wenn XML- oder XSL-Datei nicht existiert
"""
Modul-Docstrings am Dateianfang:
"""
Saxon Worker Pool - Persistente JVM-Prozesse für schnelle XSLT-Transformationen.
Eliminiert JVM-Startup-Overhead durch Vorinitialisierung von N Worker-Prozessen.
Verwendet multiprocessing.Queue für Thread-sichere Kommunikation.
"""
Pydantic Models
Definition
from pydantic import BaseModel, Field
class Project(BaseModel):
id: int = Field(..., description="Eindeutige Projekt-ID", gt=0)
name: str = Field(..., description="Projekt-Name", min_length=1, max_length=255)
project_dir: Path = Field(..., description="Pfad zum Projekt-Verzeichnis")
# Helper-Methoden direkt im Model erlaubt
def getJavaVm(self) -> str:
global app_settings
value = [x.version for x in app_settings.java_vms if x.id == self.java_vm_id]
return value[0] if len(value) else ""
Settings speichern
from conf import app_settings, ProjectData
# Globale Einstellungen
app_settings.theme = "Fusion"
app_settings.save() # WICHTIG: Nicht vergessen!
# Projekteinstellungen
project_data = ProjectData.readSettings(project_dir)
project_data.nodes.append(new_node)
project_data.writeSettings(project_dir) # WICHTIG: Persistieren!
PySide6 UI-Integration
KRITISCH: UI-Dateien nicht manuell bearbeiten!
src/ui/
├── MainWindow.ui # Qt Designer Datei (editieren erlaubt)
├── MainWinddow_ui.py # AUTO-GENERIERT (NICHT BEARBEITEN!)
└── MainWindow.py # Implementierung (hier Code schreiben)
UI-Import-Pattern
# In src/ui/JavaVmConfigDialog.py
from PySide6.QtWidgets import QDialog
from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
class JavaVmConfigDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# UI einrichten
self.ui = Ui_JavaVmConfigDialog()
self.ui.setupUi(self)
# Signale NACH setupUi() verbinden
self.ui.browseButton.clicked.connect(self._browse_file)
def _browse_file(self):
# Widgets über self.ui.widgetName zugreifen
current_path = self.ui.pathLineEdit.text()
...
Wichtig:
- UI-Klassen NIEMALS direkt erben, nur als
self.uiMember - Alle Widgets über
self.ui.widgetNamezugreifen - Signal-Verbindungen immer NACH
setupUi()aufrufen
Projektstruktur-Änderungen
Beim Modifizieren der Baumstruktur (TreeNode, XslFile, XmlFile):
# 1. ProjectData modifizieren
self.project_data.nodes.append(new_node)
# 2. SOFORT persistieren
self.project_data.writeSettings(self.project.project_dir)
# 3. UI neu laden
self._load_nodes_to_tree()
Pattern: Immer in dieser Reihenfolge: Modifizieren → Speichern → UI aktualisieren
Wichtige Konventionen
Pathlib IMMER verwenden
# RICHTIG
from pathlib import Path
xml_path = Path("data/test.xml")
xml_path = Path.home() / ".config" / "app" / "config.json"
xml_path = Path(os.path.expandvars("$HOME/data")).expanduser()
if xml_path.exists():
content = xml_path.read_text(encoding="utf-8")
# FALSCH
xml_path = "data/test.xml" # String-Pfad
xml_path = os.path.join("data", "test.xml") # os.path statt pathlib
Globale Singletons
# In conf.py am Modulende
app_settings = AppSettings()
# In anderen Modulen
from conf import app_settings
# Verwendung
java_vm = [x for x in app_settings.java_vms if x.id == vm_id][0]
Thread-basierte Operationen
from PySide6.QtCore import QThread, Signal
class HashCalculatorThread(QThread):
# Signale für Thread-sichere Kommunikation
progress = Signal(int)
finished = Signal(dict)
def __init__(self, files: list[Path]):
super().__init__()
self.files = files
def run(self):
for i, file_path in enumerate(self.files):
hash_value = calculate_hash(file_path)
self.progress.emit(i + 1)
self.finished.emit(results)
# Verwendung
thread = HashCalculatorThread(xml_files)
thread.progress.connect(self._on_progress)
thread.finished.connect(self._on_finished)
thread.start() # NICHT run() direkt aufrufen!
RAM-Optimierung
Da DocuMentor permanent läuft, sparsam mit RAM umgehen:
- Worker-Pools nach Verwendung herunterfahren
- Große Datenstrukturen frühzeitig freigeben
- Polars DataFrames statt Pandas (geringerer RAM-Verbrauch)
- Lazy Loading wo möglich
Zusammenfassung: Deutsch sprechen, pathlib verwenden, Typen annotieren, Ruff nutzen, UI-Dateien nicht anfassen!