--- title: "Modul-System für aICentral — Entwurf" project: ai-control created: 2026-07-22T16:26:08Z source: ai-control description: "Modul-System für aICentral: Deskriptoren, Registry, Enablement, Migration in 5 Schritten, Plugin-Ausblick" tags: ["design", "module", "plugin", "panel", "architektur"] --- # Modul-System für aICentral — Entwurf Stand 2026-07-22. Grundlage: panel-wiring.ts, commands-view.ts, mcp.rs, terminal.rs, app.rs, domain/project.rs (Stand 513e1a7). ## 1. Ziel und Abgrenzung Alle Panel-Funktionen werden zu- und abwählbare **Module**; die anstehenden Features (ToDo-Liste, Commit-Dialog, Archiv-Ausbau, Authoring-Ansicht) entstehen direkt als Module. Ein System für **fremde Plugins** (zur Laufzeit geladener Code) wird jetzt nicht gebaut — der Entwurf legt aber die Schnittstellen so, dass es später ohne Umbau andockt (Abschnitt 9). ## 2. Bestandsaufnahme — was schon Modulform hat Die vier Panel-Ansichten folgen bereits einem einheitlichen Muster, es ist nur nicht als Schnittstelle benannt: | Baustein | draft | commands | search | wiki | |---|---|---|---|---| | Pufferdatei (Env an MCP) | AI_CONTROL_PANEL | …_COMMANDS | …_SEARCH | …_WIKI | | Watcher-Event | panel-update | commands-update | search-update | wiki-update | | View-Interface | set/flush | set/empty | set/empty | set/empty | | Read-Command | panel_read | commands_read | search_read | wiki_read | | MCP-Tools | write_panel | write/show_commands | search_archive | show_archive, archive_panel | Der Datenfluss ist überall gleich: MCP-Server oder Command schreibt die Pufferdatei → Datei-Watcher im Terminal-Prozess emittiert `-update` → View rendert. Es gibt auch schon ein abwählbares Modul: `archiveHome: null` blendet Wiki-/Suche-Tab und Archiv-Werkzeuge aus (panel-wiring.ts:45). Das Modul-System verallgemeinert genau diese zwei Präzedenzfälle. ## 3. Modulbegriff Ein Modul ist ein benannter Funktionsblock mit bis zu vier Beiträgen: 1. **Panel-Tab** mit View (Kachel- oder Dokumentansicht) 2. **MCP-Tools** (Angebot an claude) 3. **Pufferdatei + Watcher-Event** (der Transportweg dazwischen) 4. optional **Fenster/Dialoge** (z. B. Commit-Dialog) Geplanter Zuschnitt: | Modul | Tab | MCP-Tools | abwählbar | |---|---|---|---| | `draft` (Entwurf) | ja | write_panel | nein — Kern | | `commands` | ja | write_commands, show_commands | ja | | `archive` | Wiki + Suche | archive_panel, show_archive, search_archive | ja (heute: archiveHome) | | `todo` (neu) | ja | write_todos, show_todos | ja | | `commit` (neu) | nein, eigener Dialog | — | ja | | `authoring` (später) | ja | … | ja | `draft` bleibt Kern: Titel, Editier-Modus, flush und das Archivieren hängen daran; ohne Entwurf ist das Panel leer. Alles andere ist abschaltbar. ## 4. Backend: Deskriptoren und eine Durchsetzungsstelle Neu `domain/modules.rs`: ```rust pub(crate) struct ModuleDesc { pub id: &'static str, // "commands", "todo", ... pub default_enabled: bool, /// Modul setzt ein konfiguriertes archiveHome voraus. pub requires_archive: bool, pub mcp_tools: &'static [&'static str], /// Pufferdatei-Name im Projekt-Runtime-Ordner, None = kein Puffer. pub buffer: Option<&'static str>, } pub(crate) const MODULES: &[ModuleDesc] = &[ /* draft, commands, archive, todo, … */ ]; /// Die eine Quelle für „was ist in diesem Projekt an?“. pub(crate) fn enabled_modules_in(paths: &Paths, project: &str) -> Result, String> ``` `enabled_modules_in` = MODULES, gefiltert durch Projekt-Config und `requires_archive` (Archiv-Module fallen ohne archiveHome automatisch weg — das ersetzt die heutige Sonderbehandlung, statt eine zweite daneben zu stellen). Genau drei Aufrufer: 1. **Tauri-Command `enabled_modules(project)`** — das Frontend fragt einmal beim Verdrahten. 2. **mcp.rs `tools/list`** — die Tool-Liste wird aus `MODULES[…].mcp_tools` der aktiven Module gebaut (Projekt kommt wie heute über `AI_CONTROL_PROJECT`). claude sieht Tools abgeschalteter Module gar nicht erst. 3. **mcp.rs `tools/call`** — Guard vor dem Dispatch: Tool eines inaktiven Moduls → Fehler „Modul X ist in diesem Projekt abgeschaltet". Nötig, weil eine laufende claude-Session die Tool-Liste vom Start behält. Verallgemeinert wird außerdem: - **terminal.rs**: die vier `spawn_file_watcher`-Zeilen werden eine Schleife über die aktiven Module mit Puffer; Event-Name per Konvention `-update`, Env-Name `AI_CONTROL_`. - **Read-Commands**: `panel_read/commands_read/search_read/wiki_read` verschmelzen zu `module_read(project, module)` (alle vier sind „Pufferdatei als String"). `panel_set` bleibt eigenständig, es ist der einzige Rückkanal. Statisch bleiben: `generate_handler!` (Commands aller Module sind kompiliert registriert; Abwahl wirkt auf Sichtbarkeit und Tools, nicht auf die IPC-Registrierung), die Capability-Manifeste pro Fenster und die CSP. Die Modul-Abwahl ist Konfiguration, keine Sicherheitsgrenze — dieselbe Linie wie bei den Pools (Konfiguration trennen, nicht Zugriff). ## 5. Frontend: Registry statt Verdrahtung von Hand Neu `src/modules/` mit einem Deskriptor pro Modul: ```ts export interface PanelModule { id: string; tabLabelKey: string; // i18n-Key order: number; // Tab-Reihenfolge init(container: HTMLElement, ctx: ModuleCtx): ModuleView; /// Kopfzeilen-Werkzeuge des Moduls (z. B. Archivieren-Button). headerTools?(ctx: ModuleCtx): HTMLElement[]; } export interface ModuleView { set(text: string): void; empty(): boolean; } export interface ModuleCtx { project: string; toast(msg: string): void; openDoc(path: string): void; // Dokument in den Entwurfs-Tab laden openWiki(name: string): void; } ``` `panel-wiring.ts` wird generisch: `enabled_modules` abfragen, pro Modul Tab + Container erzeugen, `module_read` laden, `-update` abonnieren, `initPanelMode` über die Modulliste statt über vier feste Felder. Die `PanelMode`-Union und die `archiveHome`-Ausblendelogik entfallen. Die HTML-Dateien verlieren die fest verdrahteten Tab-/Container-Elemente; beide Panel-Markups (angedockt/abgelöst) entstehen aus derselben Registry. **Kachel-Renderer als gemeinsamer Baustein**: `commands-view.ts` wird in einen deklarativen Tile-Renderer zerlegt (Titel/Text/Note, Zeitmarke, Aktionen kopieren/löschen/öffnen, optionales Badge). Commands, ToDo (Due-Date-Ampel = Badge) und Suchtreffer rendern darüber — die ToDo-Ansicht „analog commandlist" ist dann im Wesentlichen ein Puffer-Format plus Badge-Logik. Das Sanitizing (stripInvisibles, kein innerHTML) liegt damit an einer Stelle. ## 6. Konfiguration Projekt-Config (`.ai-control/config.json`) bekommt: ```json { "modules": { "commands": false, "todo": true } } ``` Nur Abweichungen vom Default werden gespeichert; fehlender Key = alle Defaults (bestehende Configs bleiben unverändert gültig, kein Migrationsschritt). `archiveHome` bleibt eigenständiges Feld — es ist Pfad-Konfiguration, das Archiv-Modul hängt über `requires_archive` daran. Ein Eintrag `"archive": false` schaltet das Modul trotz gesetztem archiveHome ab. Settings-UI: im Projekt-Dialog eine Checkbox-Gruppe „Module" aus MODULES generiert; Archiv-Zeile kombiniert Checkbox + Pfadwahl (heutige Archiv-Einstellung wandert dorthin). Änderungen wirken beim nächsten Panel-Aufbau; eine laufende claude-Session behält ihre Tool-Liste, der tools/call-Guard fängt das ab. ## 7. Migration in Schritten 1. `domain/modules.rs` + `enabled_modules` + tools/list/call aus MODULES speisen — reine Umformung, Verhalten identisch. 2. Frontend-Registry: die vier Bestands-Views als Deskriptoren, panel-wiring generisch, archiveHome-Sonderlogik durch `requires_archive` ersetzen. Bestehende vitest-Tests laufen gegen die Deskriptoren weiter. 3. Kachel-Renderer aus commands-view extrahieren, Suche darauf umstellen. 4. Settings-UI mit Modul-Checkboxen + `modules`-Feld in der Config. 5. Erstes neues Modul: **ToDo** — validiert den Schnitt komplett (Deskriptor, Puffer, zwei MCP-Tools, Tile-Renderer mit Badge), ohne eine Zeile Sonderverdrahtung. Schritte 1–3 sind Refactoring ohne Funktionsänderung und einzeln committbar; ab Schritt 4 wird das System sichtbar. ## 8. Auswirkung auf die geplanten Features - **ToDo**: Modul nach Schema F (Schritt 5), Aufwand fast nur Puffer-Format und Badge. - **Commit-Dialog**: Modul ohne Tab — Beitrag ist ein Fenster samt Capability-Datei; die Abwahl blendet den Einstieg (Menü/Button) aus. Zeigt, dass der Modulbegriff nicht am Panel klebt. - **Archiv-Ausbau**: Seitennavigation, Metadaten, Ordnerverwaltung wachsen im Archiv-Modul, dessen Grenze (requires_archive, drei MCP-Tools, zwei Tabs) dann schon gezogen ist. - **Authoring-Ansicht**: bekommt mit der Registry eine definierte Andockstelle statt weiterer Handverdrahtung in zwei HTML-Dateien. ## 9. Ausblick fremde Plugins — drei Stufen, Empfehlung Stufe 2 1. **Kompilierte Module** (dieser Entwurf): Code im Repo, Registry statisch. 2. **Prozess-Plugins**: ein externes Programm liefert einen Deskriptor (JSON-Manifest: id, Label, Tile-Schema, MCP-Tool-Definitionen) und schreibt Pufferdateien im Tile-Format; die App rendert deklarativ und reicht Tool-Calls per stdio durch — dasselbe Muster, mit dem claude heute schon angebunden ist. Kein Fremdcode im Webview, Isolation = Prozessgrenze, CSP und ACL bleiben unangetastet. 3. **Geladener UI-Code** (JS/WASM im Webview): verworfen — hebelt CSP (`style-src 'self'` ohne unsafe-inline) und das App-ACL-Manifest aus und schafft genau die Angriffsfläche, die die Sicherheitsrunden geschlossen haben. Der Entwurf hält Stufe 2 offen, ohne sie zu bauen: Deskriptor, Tile-Format und Puffer/Event-Konvention sind bereits die Plugin-API — später kommt nur ein Loader dazu, der Deskriptoren aus Manifesten statt aus dem Repo bezieht.