AGENTS.md now follows the mandated section structure (Project Goal, Aktuell unterstützte Architektur, Kritische Domänenregeln, Existing-Codebase-Regel, Implementierungsdisziplin, Naming, Deferred Work, Dokumentation und Verifikation) instead of an organically grown set of headings. Folds in all prior technical notes (protocol validation, network-drive build/runtime gotchas, threading rules) under the appropriate section, and fixes a stale "no tray icon yet" line that no longer matched the code. README gets an explicit honest status paragraph (what's tested, what's not: no automated tests, no prebuilt exe download, hardcoded paths). No docs/ tree: project has no database or persistent multi-user service, so the full doc structure isn't warranted per the standard.
10 KiB
AGENTS.md
Project Goal
Eigenständiges Begleit-Tool für das VersaPad-Makropad (Grovy311-Kontext,
nicht Teil der fremden jappel-Repos VersaGUI/VersaMCU). Zeigt die
Steuermatrix (4×5-Button-Grid + 4 Encoder) an, synchronisiert sie live mit
dem Board und kann sie komplett neu programmieren — inzwischen ein
schlankes Python-Pendant zur offiziellen VersaGUI (C#/.NET), plus ein
MCP-Server, der dieselbe Programmierung per KI-Tool-Aufruf ermöglicht.
Nutzerorientierte Einführung: README.md.
Aktuell unterstützte Architektur
Read-only-Schicht (JSON, beide Frontends):
versapad_data.py— Decoding für Anzeige: JSON laden, HID-Keycode/ Consumer-Usage/Modifier → lesbarer Text, Grid-Geometrie (index = spalte*5+reihe),hid_key_choices()/consumer_choices().server.py— stdlibhttp.server, generiert HTML pro Request neu, Profil-Wechsel über?profile=0|1|2, Auto-Reload alle 4s. Rein lesend, kein Programmiermodus (bewusst einfach gehalten).
Binär-/Serial-Schicht (Programmiermodus + MCP):
versapad_protocol.py— pack/unpack fürSDeviceConfig(740B, alle 3 Profile) undSMacroTable(512B, 32 Slots) + CRC16 (Poly 0x1021, Init 0xFFFF), 1:1 aus den Firmware-Structs. Gegen echtes Board validiert: read → unpack → pack ist byte-identisch zum Original, inkl. CRC.versapad_serial.py—VersaPadLink: liest/schreibt komplette Config + Makros per 8-Byte-Paket-Protokoll, Board-Identifikation per VID/PID239A:0042. Schreiben ist sicher im Sinne von "kann NVM nicht zerlegen" — Firmware prüft Magic/CRC/Keycode-Bereich vor jedem Save, antwortet sonst nur mit NACK.versapad_combined.py— Ein-Datei-Format (alle 3 Profile + Makros + nur lokal gespeicherte Profilnamen), Default-Pfad~\OneDrive\Desktop\versapad_config_all.json. Passt zum Wire-Protokoll:CONFIG_BEGIN/COMMITüberträgt ohnehin immer den kompletten 740B-Block.
UI:
desktop_viewer.py— Tkinter, flaches Design, drei unabhängige Modi (Nur lesen / Live-Sync / Programmiermodus, siehe Kritische Domänenregeln), Tray-Icon statt Taskleisten-Minimierung, Info-Button mit MCP-Doku.action_dialog.py— Bearbeiten-Dialoge (ActionEditDialog,MacroStepsDialog) für den Programmiermodus.
MCP-Server:
versapad_mcp_server.py— registriert als User-Scope-MCP-Server "versapad" (claude mcp add -s user versapad -- <python> versapad_mcp_server.py). Nutzt MCP-SDK v2 (Paketmcp, Klassemcp.server.mcpserver.MCPServer— nichtFastMCPausmcp.server.fastmcp, das existiert in dieser SDK-Version nicht mehr, wurde umbenannt).@mcp.tool()-dekorierte Funktionen bleiben direkt aufrufbar (kein.fn-Unterschied wie bei älteren FastMCP-Versionen). Tool-Liste: siehe README oderMCP_INFO_TEXTindesktop_viewer.py.
Vollständige Modulübersicht mit Zeilenreferenzen bei Bedarf direkt im Code
nachschlagen — die Dateien sind klein genug, dass eine separate
docs/current-architecture.md hier keinen Mehrwert hätte (siehe
Dokumentation und Verifikation unten für die Größeneinschätzung).
Kritische Domänenregeln
- Geometrie:
index = spalte*5 + reihe, Reihe 0 = oben, Reihe 4 = unten (Firmware-Reihenfolge). Nicht raten, nicht neu herleiten. - Encoder 0
swist auf allen 3 Profilen der Profilwechsel — nicht ohne Rücksprache mit dem User ändern. - Makros sind eine EINE globale 32-Slot-Tabelle, nicht pro Profil. Slot 0-19
= MX-Button-Index, Slot 20-31 =
20 + enc*3 + act_idx. Zwei Profile, die denselben Slot referenzieren, spielen dieselben Schritte ab. - Ein Makro-Schritt mit
keycode=0beendet die Sequenz (Firmware-Konvention) — keine Lücken vor dem letzten belegten Schritt lassen. - Makro-Schritte erlauben nur Strg/Shift/Alt als Modifier, kein Win (passend
zum Original-
ActionDialog.cs-Verhalten). - Profilnamen leben NUR lokal in
versapad_config_all.json(profile_names) — die Firmware-Structs haben keinen Platz für einen String (Header exakt 32B, jedes Profil exakt 236B, alles verplant). Sie landen nie aufs Board, egal welcher Schreibpfad benutzt wird. - Der COM-Port ist exklusiv. Live-Sync und Programmiermodus schalten sich
gegenseitig aus (ein
VersaPadLinkkann nicht von zwei Konsumenten gleichzeitig genutzt werden); VersaGUI läuft als Tray-App dauerhaft im Hintergrund weiter, auch wenn nur ihr Konfigurationsfenster geschlossen wird — für Parallelbetrieb mit diesem Tool muss sie über ihr eigenes Tray-Menü ("Beenden") wirklich beendet werden. - Programmiermodus-Änderungen werden bei jeder Bearbeitung automatisch in
versapad_config_all.jsongespeichert (nicht erst bei explizitem "Datei speichern"). Der Nur-Lese-Modus bevorzugt dieselbe Datei, falls vorhanden, und fällt sonst auf die klassischenversapad_config{1,2,3}.jsonzurück — beide Ansichten müssen dieselbe Quelle zeigen, sonst wirkt eine Bearbeitung "verschwunden". - HID-Tasten-Auswahl im Programmiermodus ist ein Dropdown, kein Tastendruck-Capture (bewusst — kein WinAPI-Hook, um keinen AV-Fehlalarm wie bei den Fensterverstecktricks in anderen Projekten zu riskieren).
- Zeichentasten-Labels zeigen eine US-Layout-Näherung, nicht das tatsächlich
aktive Windows-Tastaturlayout (die echte VersaGUI löst das über
GetKeyNameText(), das bilden wir ohne WinAPI-Call nicht nach). - Tk-Aufrufe (
self.after(), Widget-Konfiguration) NIE direkt aus einem Fremdthread (Serial-Thread, pystray-Thread) — hat in einer früheren Version einen stillen Absturz verursacht. Threads legen Ergebnisse nur in einequeue.Queue, der Main-Thread holt sie perafter()-Polling ab. .exe(PyInstaller) läuft NICHT vom Netzlaufwerk aus (Z:\Git\..., SMB-Share) — Windows blockiert das Nachladen der_internal-DLLs von einem Netzwerkpfad ohne jede Fehlermeldung. Bauen scheitert dort zusätzlich an Tcl/Tk-tzdata-Pfadlängen.build_and_deploy.ps1kopiert den Quellcode deshalb zuerst nach%TEMP%, baut nur dort, deployt nach%LOCALAPPDATA%.--onedir, nicht--onefilebeim PyInstaller-Build — Single-File-Bundles lösen öfter AV-Fehlalarme aus (sehen strukturell wie ein Packer aus).
Existing-Codebase-Regel
Es gibt bereits eine Codebasis mit validierter, gegen die echte Firmware getesteter Logik (Binärformat, CRC, Protokoll). Nicht blind neu raten oder umschreiben. Vor größeren Änderungen am Binärformat/Protokoll immer gegen die Firmware- und GUI-Quellen abgleichen, nicht aus dem Gedächtnis rekonstruieren:
VersaGUI/src/ActionDialog.cs(HidKeyName, s_consumer, Modifier-Bits)VersaGUI/src/Protocol.cs(Paket-IDs, Chunk-Größen)VersaMCU/src/config/nvm_config.h+.cpp(SDeviceConfig-Layout, CRC)VersaMCU/src/config/action.h(SAction, ActionType-Enum-Werte)VersaMCU/src/config/macro_config.h+.cpp(SMacroTable-Layout)VersaMCU/src/CButton.h(LEDAnim-Enum-Werte)VersaMCU/src/CMainController.cpp(welche Commands die Firmware tatsächlich behandelt —Protocol.csdefiniert mehr Konstanten als zwingend implementiert sind, immer hier gegenchecken)
Frühere falsche Annahme, die hier stand: Makros seien nur per SWD/JTAG
schreibbar. Falsch — USB_CMD_MACRO_BEGIN/DATA/COMMIT in
CMainController.cpp sind normal über USB implementiert. Korrigiert, auch
im versapad-Skill (~/.claude/skills/versapad/SKILL.md).
Implementierungsdisziplin
In Phasen arbeiten, nicht mehrere unabhängige Features in einem Rutsch ohne Zwischenverifikation bauen. Vor jeder Änderung am Binärformat/Protokoll: gegen ein echtes Board testen (read → unpack → pack → Vergleich auf Byte-Identität), nicht nur gegen Beispieldaten. Vor jedem Schreibvorgang aufs Board: erst mit unveränderten Daten testen (Identity-Write), bevor echte Änderungen geschrieben werden.
Naming
Code-Identifier englisch (Python-Konvention: pack_config, read_macros,
ActionEditDialog). UI-Text, Kommentare und Docstrings deutsch (Projekt-
und User-Konvention). Domänenbegriffe aus der Firmware direkt übernehmen,
nicht neu erfinden: profile, action, led, slot, macro, encoder,
chunk. Keine generischen Namen wie data/item/entry, wo eine
Bedeutung existiert — Ausnahme: data als Feldname ist durch die Firmware
selbst vorgegeben (SAction.data), dort beibehalten statt umzubenennen.
Deferred Work
- Volle
docs/-Baumstruktur (siehe Dokumentation und Verifikation unten — Projektgröße rechtfertigt das aktuell nicht, kein DB-/API-Dienst) - Board-seitiges Umschalten des aktiven Profils per Button in der GUI — explizit vom User abgelehnt ("lass uns weg"), Live-Sync bleibt read-only
- Profilnamen aufs Board schreiben — technisch unmöglich (kein Platz im Firmware-Struct), bleibt lokal
- Tastendruck-Capture statt Dropdown im Programmiermodus — bewusst vermieden (WinAPI-Hook-Risiko)
- Hintergrund-Thread für "Vom Board laden"/"Zum Board übertragen" — laufen aktuell synchron im UI-Thread (kurzzeitiges Einfrieren möglich)
- Vorgefertigte
.exeim Repo/als Release-Asset — bewusst nicht committet (Build-Artefakt), Anleitung zum Selbstbauen steht inREADME.md
Nicht an diesen Punkten arbeiten, ohne dass der User es explizit anfragt.
Dokumentation und Verifikation
README.mdist der Einstiegspunkt (Installation, Nutzung, Architektur- Überblick).AGENTS.md(diese Datei) ist die agentenseitige Quelle der Wahrheit für Domänenregeln und Architekturgrenzen — jede Session aktualisieren, die daran etwas ändert oder etwas Wichtiges lernt.- Größeneinschätzung nach Projekt-Dokumentationsstandard: kleines/mittleres
Tool ohne eigene Datenbank und ohne persistenten API-Dienst (der
Browser-Server ist ein einfacher lokaler Lese-Viewer, kein
Mehrbenutzer-Backend) →
README.md+AGENTS.mdsind Pflicht und vorhanden, ein vollerdocs/-Baum ist nicht angemessen.
Prüfungen vor einem Commit an Binärformat/Protokoll:
python -m py_compile *.py
Danach ein Live-Testskript gegen ein angeschlossenes Board laufen lassen (read → unpack → pack → Bytevergleich, siehe Existing-Codebase-Regel) — es gibt keine automatisierten Unit-Tests dafür, die Verifikation läuft manuell/interaktiv pro Session.
Committen: prägnante Commit-Message je abgeschlossenem, verifiziertem
Arbeitspaket. Nach jedem Push: alle bekannten Remotes prüfen (origin auf
GitHub, jappel auf git.jappel.io) — beide müssen synchron bleiben, siehe
Speicher-Notiz "Multi-Remote-Repos synchron halten".