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.
191 lines
10 KiB
Markdown
191 lines
10 KiB
Markdown
# 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`](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` — stdlib `http.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ür `SDeviceConfig` (740B, alle 3
|
||
Profile) und `SMacroTable` (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/PID
|
||
`239A: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 (Paket `mcp`, Klasse `mcp.server.mcpserver.MCPServer` —
|
||
**nicht** `FastMCP` aus `mcp.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 oder
|
||
`MCP_INFO_TEXT` in `desktop_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 `sw` ist 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=0` beendet 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 `VersaPadLink` kann 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.json` gespeichert (nicht erst bei explizitem
|
||
"Datei speichern"). Der Nur-Lese-Modus bevorzugt dieselbe Datei, falls
|
||
vorhanden, und fällt sonst auf die klassischen `versapad_config{1,2,3}.json`
|
||
zurü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
|
||
eine `queue.Queue`, der Main-Thread holt sie per `after()`-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.ps1` kopiert den
|
||
Quellcode deshalb zuerst nach `%TEMP%`, baut nur dort, deployt nach
|
||
`%LOCALAPPDATA%`.
|
||
- `--onedir`, nicht `--onefile` beim 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.cs` definiert 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 `.exe` im Repo/als Release-Asset — bewusst nicht committet
|
||
(Build-Artefakt), Anleitung zum Selbstbauen steht in `README.md`
|
||
|
||
Nicht an diesen Punkten arbeiten, ohne dass der User es explizit anfragt.
|
||
|
||
## Dokumentation und Verifikation
|
||
|
||
- `README.md` ist 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.md` sind Pflicht und
|
||
vorhanden, ein voller `docs/`-Baum ist nicht angemessen.
|
||
|
||
Prüfungen vor einem Commit an Binärformat/Protokoll:
|
||
```bash
|
||
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".
|