Compare commits

..

4 commits

Author SHA1 Message Date
Eddy
48c7b2a30c fix: Bridge OOM-Crash — Handler defensiv + Node-Heap 4GB [appimage]
Some checks failed
Build AppImage / build (push) Has been cancelled
Der uncaughtException-Handler aus f69f566 las err.stack dreimal und
serialisierte ihn zweimal per JSON.stringify. err.stack ist ein lazy
Getter der bei jedem Zugriff neu formatiert wird — bei V8-nahe-OOM
hat genau das den Abort ausgelöst (Crash-Dump Frames 20-24:
ErrorStackGetter → FormatStackTrace → FatalProcessOutOfMemory).

- Handler: err.stack einmal lesen, auf 2000 Zeichen kürzen, try/catch
  drumrum. Handler darf nicht selbst crashen.
- Bridge-Start: --max-old-space-size=4096 (Node-Default ~2GB reicht
  bei langen Sessions mit großen Thinking-Blocks + Agent-SDK-History
  nicht). Betrifft Daemon- UND stdio-Modus.
2026-04-21 16:19:51 +02:00
Eddy
94c7b9358d fix: Aktivitäts-Liste — grüner Puls-Punkt bleibt nicht mehr hängen
Tool-Start und Tool-End hatten unterschiedliche IDs: tool-start generierte
eine zufällige UUID, tool-end suchte nach der Backend-ID → kein Match →
alle Tool-Calls blieben ewig auf "running" mit pulsierendem grünen Punkt.

Fix: Backend-ID wird jetzt bei tool-start durchgereicht + Fallback auf
letzten laufenden Tool-Call wenn ID nicht matcht.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 15:04:32 +02:00
Eddy
50c92072f5 fix: Projekt wird beim Session-Klick automatisch mitgewechselt
Wenn eine Session angeklickt wird die zu einem anderen Projekt gehört,
wird jetzt automatisch switch_project aufgerufen (CWD, Context, KB-Filter).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 15:02:56 +02:00
Eddy
8725e2f4e6 docs: Komplette Dokumentation überarbeitet — README als Entwickler-Handbuch, CLAUDE.md als Session-Kurzreferenz
README.md: Architektur-Diagramm, alle 18 Rust-Module + 27 Svelte-Komponenten dokumentiert,
Features nach Phasen, MCP-Server, CI/CD, Tastenkürzel, KB-Referenzen.
CLAUDE.md: Tech-Stack, Kommunikationskette, Bridge-Daemon Details, Agent-Modi,
häufige Probleme & Lösungen Tabelle, Build-Befehle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 14:59:42 +02:00
7 changed files with 378 additions and 551 deletions

188
CLAUDE.md
View file

@ -1,107 +1,131 @@
# Claude Desktop
# Claude Desktop — Projekt-Kontext
Native Tauri-2.0-Desktop-App die Claude Code/Agent SDK als Backend nutzt. Sprach-Interface, Live-Aktivität, Guard-Rails. Eigenes Pendant zur VSCodium-Sidebar mit voller Kontrolle über das System.
Native Tauri-2.0-Desktop-App die Claude Code/Agent SDK als Backend nutzt.
Schneller und maechtiger als Codium + Claude Code Extension.
Detail-Übersicht + Status: [README.md](README.md). Phasen-Stand: [ROADMAP.md](ROADMAP.md).
**Stand: 22.04.2026 — Alle Roadmap-Phasen (3-6) abgeschlossen.**
Detail-Uebersicht: [README.md](README.md) | Phasen-Status: [ROADMAP.md](ROADMAP.md) | Aenderungen: [CHANGELOG.md](CHANGELOG.md)
## Tech-Stack
- **Backend**: Rust 2021, Tauri 2 (`tauri = "2"` mit `tray-icon`-Feature), tokio, mysql_async, rusqlite, reqwest
- **Frontend**: SvelteKit 2, Svelte 5, TypeScript, Vite 5, paneforge (Multi-Pane-Layout)
- **AI**: `@anthropic-ai/claude-agent-sdk` + `@anthropic-ai/claude-code`
- **Sprache**: Whisper (STT lokal), TTS Cloud-Streaming
- **DB**: claude-DB (MySQL 192.168.155.11 `claude`) für Wissensbasis, SQLite lokal für Sessions/Persistenz
- **Build**: NixOS-Dev-Shell (`shell.nix`), Forgejo CI für AppImage
- **Backend**: Rust 2021, Tauri 2 (`tray-icon`), tokio, mysql_async, rusqlite, reqwest, libc
- **Frontend**: SvelteKit 2, Svelte 5 (Runes: $state/$derived/$effect), TypeScript, Vite 5, paneforge
- **AI**: `@anthropic-ai/claude-agent-sdk` (query-Funktion), Ollama (optional, lokal)
- **Sprache**: whisper-cli (STT, lokal), piper-tts (TTS, lokal, 5 deutsche Stimmen)
- **DB**: MySQL (claude-db, 192.168.155.11) fuer Wissensbasis, SQLite lokal fuer Sessions/Settings/Queue
- **IPC**: Unix Domain Socket (bevorzugt) oder stdio JSON-Lines
- **Build**: NixOS shell.nix, Forgejo Actions → AppImage
## Wichtige Pfade
- Rust-Module: [src-tauri/src/](src-tauri/src/) — 16 Module (`main.rs`, `lib.rs`, `claude.rs`, `db.rs`, `guard.rs`, `voice.rs`, `hooks.rs`, `ide.rs`, …)
- UI-Komponenten: [src/lib/components/](src/lib/components/) — 24 Panels
- Routes: [src/routes/](src/routes/) (`+layout.svelte`, `+page.svelte`, `presentation/+page.svelte`)
- VS-Code-Extension: [vscode-extension/](vscode-extension/) (Bridge auf WebSocket-Port 7890)
- Workflow: [.forgejo/workflows/build-appimage.yml](.forgejo/workflows/build-appimage.yml)
- Dev-Shell: [shell.nix](shell.nix)
- Tauri-Config: [src-tauri/tauri.conf.json](src-tauri/tauri.conf.json), [src-tauri/Cargo.toml](src-tauri/Cargo.toml)
| Was | Pfad |
|-----|------|
| Rust-Module (18) | `src-tauri/src/` |
| UI-Komponenten (27) | `src/lib/components/` |
| Stores | `src/lib/stores/` (app.ts, events.ts) |
| Bridge | `scripts/claude-bridge.js` (~1100 Zeilen) |
| Hauptlayout | `src/routes/+page.svelte` |
| Chat-Fenster | `src/routes/chat-window/+page.svelte` |
| Praesentation | `src/routes/presentation/+page.svelte` |
| CI/CD | `.forgejo/workflows/build-appimage.yml` |
| Dev-Shell | `shell.nix` |
## Build & Run
## Architektur-Kurzreferenz
### Native Dev (Hot-Reload)
```bash
nix-shell shell.nix --run 'npm ci && npm run tauri:dev'
### Kommunikationskette
```
User → ChatPanel.svelte → invoke('send_message') → claude.rs
→ Bridge (UDS/stdio) → query() → Claude API → Events zurueck
→ claude.rs handle_bridge_message() → emit() → events.ts → UI
```
### Native Production-Build (NixOS — **Pflicht-Weg auf NixOS!**)
```bash
# Cargo-target NICHT auf SMB-Mount! → I/O-Errors. Lokales tmpfs nutzen:
CARGO_TARGET_DIR=/tmp/claude-target \
nix-shell shell.nix --run 'npm run tauri build -- --bundles appimage'
# Bundling kann mit pkg-config-Bug crashen — Binary unter
# /tmp/claude-target/release/claude-desktop ist trotzdem fertig & startbar:
nix-shell shell.nix --run /tmp/claude-target/release/claude-desktop
### Bridge-Daemon (Phase 3)
- **Socket**: `/tmp/claude-bridge.sock`
- **PID-File**: `/tmp/claude-bridge.pid`
- Start: `node claude-bridge.js --socket /tmp/claude-bridge.sock`
- Rust verbindet sich per `UnixStream::connect()`
- Auto-Reconnect bei Verbindungsverlust (3 Versuche)
- Fallback auf stdio wenn UDS fehlschlaegt
### ClaudeState (claude.rs)
```rust
pub struct ClaudeState {
pub connection: Option<BridgeConnection>, // Stdio oder Uds
pub request_counter: u64,
pub agents: Vec<AgentStatus>,
}
```
### CI-Build via Forgejo Actions
Commit mit `[appimage]` in der Message → Build auf `16-Forgejo-Runner-AppImage` (Debian) → Upload in Package-Registry. Triggert auch bei Tag-Push `v*`.
### Agent-Modi (claude-bridge.js)
| Modus | Beschreibung | Prompt |
|-------|-------------|--------|
| `solo` | Claude arbeitet direkt | Kein Orchestrator |
| `handlanger` | Haupt-Agent delegiert an Haiku-Worker | ORCHESTRATOR_PROMPTS.handlanger |
| `experten` | 4 Experten: Research/Implement/Test/Review | ORCHESTRATOR_PROMPTS.experten |
| `auto` | Heuristik waehlt Modus nach Nachrichtenlaenge/-inhalt | chooseAutoMode() |
## NixOS-Spezialfall — AppImage funktioniert NICHT
### MCP-Hub (Phase 4)
- 6 Server aus `~/.claude.json` → beim Bridge-Start per `set-mcp-servers` injiziert
- Tauri-Commands: `list_mcp_servers`, `add_mcp_server`, `remove_mcp_server`
- Bridge: `queryOptions.mcpServers = mcpServerConfigs`
Das vom CI gebaute AppImage hat auf NixOS einen WebKit2GTK ↔ Mesa ABI-Konflikt (`EGL_BAD_PARAMETER` im WebKitWebProcess). **Keine** Kombination aus `WEBKIT_DISABLE_DMABUF_RENDERER` / `_COMPOSITING_MODE` / `_SANDBOX_THIS_IS_DANGEROUS` / `GDK_BACKEND=x11` / `LIBGL_ALWAYS_SOFTWARE` löst es. Workarounds erschöpft → **immer nativ bauen via `shell.nix`**. Vollständige Diagnose: KB-Eintrag #381.
### Offline-Queue (Phase 5)
- SQLite-Tabelle `offline_queue`
- Commands: `queue_message`, `list_queued_messages`, `flush_offline_queue`, `clear_offline_queue`, `queue_count`
## Build & Deploy
```bash
# Dev (Hot-Reload)
CARGO_TARGET_DIR=/tmp/claude-desktop-target nix-shell --run "npx tauri dev"
# Produktion
CARGO_TARGET_DIR=/tmp/claude-desktop-target nix-shell --run "npx tauri build -- --bundles appimage"
# CI: Commit mit [appimage] → Forgejo baut automatisch
# WICHTIG: CARGO_TARGET_DIR nie auf SMB-Mount!
```
### CI-Eigenheiten
- Runner: `16-Forgejo-Runner-AppImage` (Debian Bookworm)
- AppImage-Filename mit Leerzeichen → `tr ' ' '-'` vor Upload
- DELETE vor PUT (Forgejo 409 Conflict)
- Custom-AppRun muss `apprun-hooks/linuxdeploy-plugin-gtk.sh` sourcen (KB #384)
- Ntfy-Notifications auf Topic `vk-builds`
## NixOS-Spezialfall
AppImage hat WebKit2GTK/Mesa ABI-Konflikt auf NixOS → **immer nativ bauen** via `shell.nix`.
Auf Debian/Ubuntu/Fedora/Arch funktioniert das AppImage problemlos.
## Konventionen
- **Sprache**: Deutsch in Code-Kommentaren, README, CHANGELOG, Commit-Messages, UI-Strings
- **Modell-Defaults**: Sonnet 4.6 für normale Tasks, Opus für Komplex, Haiku für Routineanfragen — UI-Auswahl im Footer
- **Guard-Rails**: alle System-Aktionen über `guard.rs` klassifizieren (Safe / Moderate / Critical / Blocked) — siehe `src-tauri/src/guard.rs`
- **Audit**: jede Tool-Ausführung landet im Audit-Log (`audit.rs` → SQLite + UI-Panel)
- **Sessions**: persistent in SQLite, restartbar
- **Bridge zur VSCodium-Extension**: Port 7890, einfaches WebSocket-Protokoll, in `ide.rs` definiert
- **Sprache**: Deutsch in Code-Kommentaren, README, Commits, UI
- **Svelte 5**: Runes (`$state`, `$derived`, `$effect`), `onclick` statt `on:click`
- **Guard-Rails**: Alle System-Aktionen ueber `guard.rs` klassifizieren
- **Audit**: Jede Tool-Ausfuehrung im Audit-Log
- **Sessions**: Persistent in SQLite, nach Projekt gefiltert
- **PaneForge**: `{#key}` verwenden wenn Panes dynamisch ein-/ausgeblendet werden
## Workflow-Eigenheiten (CI/CD)
## Haeufige Probleme & Loesungen
Beim Anpassen von [.forgejo/workflows/build-appimage.yml](.forgejo/workflows/build-appimage.yml) **nicht vergessen**:
- AppImage-Filename hat Leerzeichen → vor Upload `tr ' ' '-'` (curl-URL-Bug)
- Vor jedem Upload **DELETE auf `latest/` UND `VERSION/`** (Forgejo wirft 409 Conflict bei PUT auf existing)
- Custom-AppRun **muss `apprun-hooks/linuxdeploy-plugin-gtk.sh` sourcen + `AppRun.wrapped` aufrufen**, sonst finden WebKit-Subprozesse ihre Helpers nicht (KB #384)
- Re-Bundle nach AppRun-Patch mit `appimagetool --no-appstream`
- Ntfy-Notifications inline (nicht via `data/ntfy-action`, weil das Repo cross-org ist — für `data-it/*`-Repos siehe KB #220 für vollständige URL-Variante)
| Problem | Loesung |
|---------|---------|
| Rusqlite Borrow-Lifetime | `let result = stmt.query_map(...)?.collect(); result` |
| UTF-8 Panic bei Truncation | `char_indices().take_while()` statt `&s[..n]` |
| PaneForge Resize nach Detach | `{#key $chatDetached}` um PaneGroup |
| Piper-TTS nicht gefunden | `nix-env -f '<nixpkgs>' -iA piper-tts`, Modelle in `~/.local/share/claude-desktop/models/` |
| Bridge-Nachricht nicht parsbar | `match serde_json::from_str()` statt `if let Ok()` |
| Unhandled Promise Rejection | Global Handler in Bridge (uncaughtException + unhandledRejection) |
## Wissensbasis (Claude-DB)
## Wissensbasis-Referenzen
Bei spezifischen Bug-Themen vorab `mysql_search_knowledge` mit Schlüsselwort:
| Thema | KB-ID |
|---|---|
| Pipeline-Übersicht | #311 |
| Debian-Runner Setup | #371 |
| libssl-dev / openssl-sys | #372 |
| NixOS WebKit-EGL-Crash | #381 |
| Cargo auf SMB → CARGO_TARGET_DIR | #382 |
| Custom-AppRun + linuxdeploy-Hook | #384 |
| Bluetooth 5.2 ≠ LE Audio | #385 |
| Tauri shell.nix-Vorlage | #248 |
| Forgejo Package-Registry 409 | #161 |
| Ntfy-Pattern (cross-org URL) | #190/#191/#220 |
## Häufige Befehle
```bash
# Build-Status checken
curl -sS -u "token:<forgejo-token>" \
'https://git.data-it-solution.de/api/v1/repos/data/claude-desktop/actions/tasks?limit=3' | jq
# AppImage frisch ziehen (Standard-Linux)
rm -f ~/Applications/Claude-Desktop.AppImage
curl -sSL -o ~/Applications/Claude-Desktop.AppImage \
-u "token:<forgejo-token>" \
'https://git.data-it-solution.de/api/packages/data/generic/claude-desktop/latest/Claude-Desktop_0.1.0_amd64.AppImage'
chmod +x ~/Applications/Claude-Desktop.AppImage
# Native NixOS-Start (nach einmaligem Build)
nix-shell shell.nix --run /tmp/claude-target/release/claude-desktop
# Build-Logs aus Forgejo-Container ziehen (bei Failure)
ssh unraid "docker cp 18-Forgejo:\$(docker exec 18-Forgejo find /data/gitea/actions_log/data/claude-desktop -name '<RUN_ID>.log.zst' | head -1) /tmp/build.log.zst"
ssh unraid "zstd -d -c /tmp/build.log.zst" | tail -100
```
| KB-ID | Thema |
|-------|-------|
| #248 | Tauri + SvelteKit shell.nix |
| #311 | CI/CD Pipeline |
| #371 | Debian Forgejo-Runner |
| #381 | NixOS WebKit-EGL-Crash |
| #382 | Cargo auf SMB |
| #384 | Custom-AppRun |

641
README.md
View file

@ -1,491 +1,242 @@
# Claude Desktop — Nativer AI-Assistent
# Claude Desktop
Eigenständige Desktop-Anwendung die Claude Code als Backend nutzt, mit nativem UI, Live-Übersicht und kontrolliertem OS-Zugriff.
Nativer AI-Desktop-Assistent fuer Linux. Schneller und maechtiger als Codium + Claude Code Extension.
## Status (Stand 2026-04-20)
**Variante A (Native Desktop-App) ist umgesetzt** — Tauri 2.0 + SvelteKit 5, Phase 1-13 fertig (siehe [ROADMAP.md](ROADMAP.md)). Variante B (autonome VM) bleibt Vision.
Was läuft:
- 4-Panel-Layout, 24 UI-Komponenten (Chat, Activity, Memory, Audit, Knowledge, Voice, Hooks, IDE, Programs, Performance, Settings, …)
- 16 Rust-Backend-Module (`claude.rs`, `db.rs`, `guard.rs`, `memory.rs`, `voice.rs`, `hooks.rs`, `ide.rs`, …)
- Sprach-Interface mit Push-to-Talk (`VoicePanel`), Whisper STT
- Modell-Auswahl Haiku/Sonnet/Opus, Token-/Kosten-Anzeige
- Subagent-Hierarchie, Multi-Agent-Modi, Hook-System
- Session-Persistenz (SQLite), Audit-Log
- VS-Code-Extension `claude-desktop-bridge` (steuert VSCodium aus der App heraus, WebSocket-Port 7890)
- CI/CD-Pipeline (Forgejo Actions) → AppImage in Package-Registry
## Installation
### AppImage (Debian/Ubuntu/Fedora/Arch — **nicht NixOS**)
```bash
mkdir -p ~/Applications
curl -sSL -o ~/Applications/Claude-Desktop.AppImage \
-u "token:<dein-forgejo-token>" \
'https://git.data-it-solution.de/api/packages/data/generic/claude-desktop/latest/Claude-Desktop_0.1.0_amd64.AppImage'
chmod +x ~/Applications/Claude-Desktop.AppImage
~/Applications/Claude-Desktop.AppImage
```
Auf NixOS hat das AppImage einen WebKit2GTK ↔ Mesa ABI-Konflikt → siehe nächster Abschnitt.
### NixOS — nativer Build (Pflicht)
Das AppImage läuft auf NixOS nicht (KB-Eintrag #381 zur Diagnose). Stattdessen lokal bauen mit der mitgelieferten `shell.nix`:
```bash
cd "/mnt/17 - Entwicklungen/20 - Projekte/ClaudeDesktop"
# Cargo-Target auf lokales tmpfs (SMB-Mount macht Cargo-Build-Errors)
CARGO_TARGET_DIR=/tmp/claude-target \
nix-shell shell.nix --run 'npm ci && npm run tauri build -- --bundles appimage'
# Tauri-Bundling kann an pkg-config-Bug scheitern — Binary reicht aber:
nix-shell shell.nix --run /tmp/claude-target/release/claude-desktop
```
Permanenter Wrapper:
```bash
cat > ~/.local/bin/claude-desktop <<'EOF'
#!/usr/bin/env bash
cd "/mnt/17 - Entwicklungen/20 - Projekte/ClaudeDesktop"
exec nix-shell shell.nix --run /tmp/claude-target/release/claude-desktop
EOF
chmod +x ~/.local/bin/claude-desktop
```
### Development
```bash
cd "/mnt/17 - Entwicklungen/20 - Projekte/ClaudeDesktop"
nix-shell shell.nix --run 'npm ci && npm run tauri:dev' # mit Hot-Reload
```
## CI/CD-Pipeline
Workflow: `.forgejo/workflows/build-appimage.yml`
| Trigger | Was passiert |
|---|---|
| Push auf `main` mit `[appimage]` in Commit-Message | AppImage bauen + in Package-Registry hochladen |
| Push eines Tags `v*` | zusätzlich als Release-Asset anhängen |
**Runner:** `16-Forgejo-Runner-AppImage` (Debian Bookworm, glibc, mit linuxdeploy + appimagetool + Tauri-Build-Stack vorinstalliert). Der Standard-Alpine-Runner kann Tauri-AppImages nicht bauen (musl-Inkompatibilität von linuxdeploy). Setup des Runners: KB-Eintrag #371.
**Workflow-Eigenheiten** (gut zu wissen wenn was ändern):
- AppImage-Filename mit Leerzeichen (`Claude Desktop_…`) wird vor Upload zu `Claude-Desktop_…` umbenannt (curl-URL-Bug)
- Vor jedem Upload werden alte Versionen gelöscht (Forgejo Package-Registry weist PUT auf existierenden Pfad mit HTTP 409 ab)
- Custom-AppRun mit NixOS-Detection wird nach `tauri build` eingesetzt + mit `appimagetool` re-bundled. Der Hook `apprun-hooks/linuxdeploy-plugin-gtk.sh` darf dabei nicht überschrieben werden (KB #384), sonst finden WebKit-Subprozesse ihre Helpers nicht
- Ntfy-Notifications (Build-Start/Success/Failure) — Topic `vk-builds`
Ntfy-Setup für andere Projekte: KB #190/#191/#220.
## VS-Code-Extension `claude-desktop-bridge`
Eigenes Subprojekt unter [vscode-extension/](vscode-extension/). Ermöglicht Claude Desktop, VSCodium fernzusteuern (Datei öffnen, Cursor-Position setzen, Terminal-Befehle absetzen) über einen WebSocket-Server (Port 7890 default).
```bash
cd vscode-extension
npm ci && npm run compile
# Verpacktes vsix: claude-desktop-bridge-0.1.0.vsix
codium --install-extension claude-desktop-bridge-0.1.0.vsix
```
Befehle in VSCodium: `Claude Desktop: Verbindung starten/beenden`. Die App meldet sich beim Start automatisch.
## Projektstruktur (Stand)
```
ClaudeDesktop/
├── src-tauri/src/ # 16 Rust-Module
│ ├── main.rs # Entry (setzt WEBKIT_DISABLE_*-Defaults für Linux)
│ ├── lib.rs # Tauri-App-Setup
│ ├── claude.rs # Claude Agent SDK Integration
│ ├── db.rs # MySQL (claude-DB) + SQLite-Persistierung
│ ├── guard.rs # Guard-Rails (Critical/Moderate/Safe)
│ ├── hooks.rs # Hook-System
│ ├── memory.rs # Memory/Knowledge-Graph
│ ├── voice.rs # Whisper STT + Voice-Activity-Detection
│ ├── ide.rs # Bridge zur VSCodium-Extension
│ ├── audit.rs, knowledge.rs, programs.rs, session.rs,
│ │ teaching.rs, update.rs, context.rs
│ └── ...
├── src/ # SvelteKit Frontend
│ ├── routes/+layout.svelte
│ ├── routes/+page.svelte
│ ├── routes/presentation/+page.svelte
│ └── lib/components/ # 24 UI-Panels
├── vscode-extension/ # VSCodium-Bridge
├── .forgejo/workflows/ # CI/CD
├── shell.nix # NixOS Dev-Shell
├── ROADMAP.md # Phase-Status
├── TEST-ROADMAP.md # Test-Plan
├── tools.yaml # MCP-Tool-Inventar
└── package.json
```
## Wissensbasis-Referenzen (für künftige Sessions)
| KB-ID | Thema |
|---|---|
| #311 | Diese Pipeline (Workflow-Tricks) |
| #371 | Debian Forgejo-Runner Setup |
| #372 | libssl-dev für openssl-sys |
| #381 | NixOS WebKit2GTK EGL-Crash → native build |
| #382 | Cargo auf SMB → CARGO_TARGET_DIR umleiten |
| #384 | Custom-AppRun + linuxdeploy-Hook |
| #248 | Tauri 2.0 + SvelteKit auf NixOS shell.nix |
**Tech-Stack:** Tauri 2.0 (Rust) + SvelteKit 5 + Claude Agent SDK (Node.js)
**Codebase:** ~24.000 Zeilen, 154 Commits, 27 Svelte-Komponenten, 18 Rust-Module
---
## Motivation
## Schnellstart
Claude Code in VSCodium funktioniert, hat aber Grenzen:
- Sidebar-Chat ist eng, keine eigene Fensterverwaltung
- Kein Überblick was Claude gerade tut (Dateien, Befehle, DB-Queries)
- Kein "Stopp"-Button bei laufenden Aktionen
- Keine Präsentations-Ansicht für Ergebnisse
- Keine native OS-Integration (Fenster steuern, Programme öffnen)
```bash
# Development (Hot-Reload)
cd "/mnt/17 - Entwicklungen/20 - Projekte/ClaudeDesktop"
CARGO_TARGET_DIR=/tmp/claude-desktop-target nix-shell --run "npx tauri dev"
## Vision
# Produktion (AppImage)
CARGO_TARGET_DIR=/tmp/claude-desktop-target nix-shell --run "npx tauri build -- --bundles appimage"
### Variante A: Native Desktop-App (begleitend)
Claude arbeitet begleitend — der User sieht alles mit und kann jederzeit eingreifen.
```
┌─────────────────────────────────────────────────────┐
│ Claude Desktop [─][□][×]│
├──────────────┬──────────────────────────────────────┤
│ │ │
│ 💬 Chat │ 📋 Live-Aktivität │
│ │ │
│ Du: Fixe │ ▶ Lese product/price.php:1609 │
│ den Bug in │ ▶ Grep "addMoreActions" in 3 Files │
│ der Preis- │ ▶ Edit actions_produktkarte.class.php│
│ seite │ ✓ Deploy nach /var/www/dolibarr/... │
│ │ │
│ Claude: │ ⚠ Will deployen auf PROD │
│ Gefunden, │ [Erlauben] [Ablehnen] │
│ der Hook... │ │
│ ├──────────────────────────────────────┤
│ │ 📊 Ergebnis-Präsentation │
│ │ │
│ │ Vorher: list=0 (unsichtbar) │
│ │ Nachher: list=3 (auf Karte) │
│ │ │
│ │ [Diff anzeigen] [Screenshot] │
│ │ │
├──────────────┴──────────────────────────────────────┤
│ [⏹ STOPP] CPU: 2% │ DB: claude │ Git: main │
└─────────────────────────────────────────────────────┘
# CI: Commit mit [appimage] im Message → Forgejo Runner baut + uploaded automatisch
```
**Kernfeatures:**
- **Chat-Panel** — Aufgaben eingeben, Antworten lesen
- **Live-Aktivität** — Echtzeit was Claude tut (Dateien, Befehle, Queries)
- **Kritische Aktionen** — Popup bei Prod-Deploy, DB-Änderungen, Git Push
- **Präsentations-View** — Nach einer Aufgabe: Vorher/Nachher, Diffs, Screenshots
- **STOPP-Button** — Sofort alles abbrechen
- **Statusleiste** — Aktive DB, Git-Branch, CPU/RAM
---
**Technologie-Stack:**
- **Tauri 2.0** (Rust + WebView) — native App, ~5 MB statt 200 MB Electron
- **SvelteKit** — Frontend (gleiche Technologie wie Leckerbuch, VDE Katalog)
- **Claude Code SDK** (`@anthropic-ai/claude-code`) — AI-Backend
- **Claude DB** — Direkte MySQL-Anbindung (kein REST-Umweg)
- **MCP-Tools** — Docker, Forgejo, Wissensbasis
### Variante B: Autonome VM (selbständig arbeitend)
Claude hat einen eigenen Rechner (VM) mit Desktop und arbeitet Aufgaben selbständig ab. Der User überwacht remote.
## Architektur
```
┌──────────────────────────────────────────┐
│ Unraid Server │
│ │
│ ┌────────────────────────────────────┐ │
│ │ VM: Claude Agent │ │
│ │ │ │
│ │ ┌──────────┐ ┌───────────────┐ │ │
│ │ │ Desktop │ │ Claude Agent │ │ │
│ │ │ (XFCE) │←→│ Computer Use │ │ │
│ │ │ │ │ Terminal │ │ │
│ │ │ Dolibarr │ │ Claude DB │ │ │
│ │ │ Browser │ │ MCP Tools │ │ │
│ │ │ IDE │ │ Git/Forgejo │ │ │
│ │ └──────────┘ └───────────────┘ │ │
│ │ ↕ │ │
│ │ VNC/noVNC (Port 6080) │ │
│ └────────────────────────────────────┘ │
│ │
│ Netzwerk: ISOLIERT von Prod! │
│ - Eigenes VLAN / Bridge │
│ - Zugriff nur auf Test-DB │
│ - Kein SSH zu Unraid │
│ - Kein Zugriff auf Prod-Dolibarr │
└──────────────────────────────────────────┘
Eddy (Browser → VNC)
Sieht Claude arbeiten
Kann jederzeit eingreifen
```
**Wie Claude Computer Use funktioniert:**
1. Claude bekommt einen Screenshot des Desktops
2. Claude analysiert was er sieht
3. Claude sendet Maus-/Tastatur-Befehle
4. Nächster Screenshot → nächste Aktion
5. Kann jedes Programm bedienen das ein Mensch bedienen kann
**Sicherheitskonzept für die VM:**
- **Netzwerk-Isolation** — eigenes VLAN, kein Zugriff auf Prod-Server
- **Nur Test-DB** — dolibarr_test auf 192.168.155.11, nie Prod-DB
- **Kein SSH nach außen** — keine SSH-Keys zu Unraid oder anderen Servern
- **Snapshot-basiert** — VM-Snapshot vor jeder Aufgabe, Rollback bei Problemen
- **Audit-Log** — jeder Befehl, jede Aktion wird geloggt
- **Zeitlimit** — maximale Laufzeit pro Aufgabe
- **Kill-Switch** — ein Befehl stoppt alles sofort
**Use Cases für die VM:**
- Dolibarr-Module entwickeln und testen (Browser + Terminal)
- Automatische Code-Reviews über mehrere Repos
- Dokumentation erstellen mit Screenshots
- UI-Tests durchführen (Dolibarr durchklicken, Fehler finden)
- Batch-Aufgaben (alle Module updaten, Lang-Dateien synchronisieren)
## Empfehlung: Stufenweise vorgehen
### Stufe 1: Native Desktop-App (Variante A)
- Sofort umsetzbar mit vorhandenem Wissen (Svelte, Tauri)
- Claude arbeitet begleitend, User behält Kontrolle
- Ersetzt die VSCodium-Sidebar durch bessere UX
- Geschätzter Aufwand: 2-3 Wochen Grundgerüst
### Stufe 2: Autonome VM (Variante B) — später
- Erst wenn Stufe 1 stabil läuft und Vertrauen aufgebaut ist
- Guard-Rails und Audit-Log müssen wasserdicht sein
- Netzwerk-Isolation auf Unraid einrichten
- Geschätzter Aufwand: 1-2 Wochen Setup, dann iterativ
## Architektur — Variante A im Detail
### Projektstruktur
```
ClaudeDesktop/
├── src-tauri/ # Rust-Backend (Tauri)
│ ├── src/
│ │ ├── main.rs # App-Einstiegspunkt
│ │ ├── claude.rs # Claude SDK Integration
│ │ ├── db.rs # Direkte MySQL-Anbindung
│ │ └── guard.rs # Sicherheits-Regeln
│ ├── Cargo.toml
│ └── tauri.conf.json
├── src/ # SvelteKit Frontend
│ ├── routes/
│ │ ├── +layout.svelte # Haupt-Layout (Chat + Panels)
│ │ ├── chat/ # Chat-Ansicht
│ │ ├── activity/ # Live-Aktivität
│ │ ├── presentation/ # Ergebnis-Präsentation
│ │ └── settings/ # Einstellungen
│ ├── lib/
│ │ ├── claude-bridge.ts # Kommunikation mit Tauri-Backend
│ │ ├── db.ts # Claude-DB Queries
│ │ └── stores.ts # Svelte Stores (State)
│ └── app.html
├── package.json
├── svelte.config.js
├── vite.config.ts
└── README.md
┌──────────────────────────────────────────────────────────┐
│ SvelteKit Frontend │
│ 27 Svelte-Komponenten (Chat, Voice, Guard-Rails, ...) │
├──────────────────────────────────────────────────────────┤
│ Tauri IPC │
├──────────────────────────────────────────────────────────┤
│ Rust Backend │
│ 18 Module (claude.rs, db.rs, guard.rs, voice.rs, ...) │
├──────────────────────┬───────────────────────────────────┤
│ SQLite (lokal) │ Unix Domain Socket / stdio │
│ Sessions, Settings │ ↕ │
│ Memory, Audit │ Claude Bridge (Node.js) │
│ Offline-Queue │ @anthropic-ai/claude-agent-sdk │
├──────────────────────┤ ↕ │
│ MySQL (claude-db) │ Claude API (Anthropic) │
│ Wissensbasis │ + MCP-Server (6 Stueck) │
└──────────────────────┴───────────────────────────────────┘
```
### Datenfluss
```
User-Eingabe (Chat)
SvelteKit Frontend
↓ (Tauri IPC)
Rust Backend
Claude Code SDK (Node.js child process)
Claude API (Anthropic)
Tool-Ausführung (Bash, Dateien, DB, MCP)
Ergebnis zurück an Frontend
Live-Aktivität + Präsentation anzeigen
```
### Claude-DB Integration
Statt REST-API (aktuell) → direkte MySQL-Verbindung im Rust-Backend:
```rust
// Direkte DB-Abfrage, kein MCP-Umweg
let results = db.query(
"SELECT * FROM knowledge WHERE MATCH(title,content) AGAINST(? IN BOOLEAN MODE)",
&[search_term]
).await?;
```
- Schneller (kein HTTP-Roundtrip)
- Keine Token-Limits bei großen Ergebnissen
- Full-Text-Search direkt in MySQL
1. **User** → Eingabe im ChatPanel (Text/Sprache/File-Drop)
2. **Frontend**`invoke('send_message')` via Tauri IPC
3. **Rust** → Sticky Context + KB-Hints laden, an Bridge senden
4. **Bridge**`query()` mit Claude Agent SDK, Events streamen
5. **Claude API** → Antwort + Tool-Calls (Bash, Read, Edit, MCP, ...)
6. **Bridge** → Events via JSON-Lines an Rust zurueck
7. **Rust**`emit()` Events ans Frontend
8. **Frontend** → Live-Rendering (Text, Tools, Subagents)
### Guard-Rails im nativen Programm
```rust
enum ActionRisk {
Safe, // Dateien lesen, Code schreiben → automatisch
Moderate, // Git commit, lokaler Deploy → Statusbar-Hinweis
Critical, // Prod-Deploy, DB-Schema, Git Push → Popup + Bestätigung
Blocked, // rm -rf, force push main → hart blockiert
}
```
### Bridge-Modi
## Sprach-Interface — Reden mit Claude
| Modus | Beschreibung | Wann |
|-------|-------------|------|
| **UDS-Daemon** | Bridge als eigenstaendiger Prozess, ueberlebt App-Neustart | Standard (bevorzugt) |
| **stdio** | Bridge als Child-Process mit stdin/stdout | Fallback wenn UDS fehlschlaegt |
### Konzept
Socket-Pfad: `/tmp/claude-bridge.sock`, PID-File: `/tmp/claude-bridge.pid`
Echtes Gespräch mit Claude — reden, unterbrechen, weiterreden. Kein "Aufnahme starten/stoppen", sondern natürlicher Dialog.
---
```
┌─────────────────────────────────────────────────┐
│ │
│ 🎤 Du sprichst │
│ ↓ │
│ Whisper (Speech-to-Text, lokal) │
│ ↓ │
│ VAD erkennt: "User hat aufgehört zu reden" │
│ ↓ │
│ Text → Claude API → Antwort-Text │
│ ↓ │
│ TTS (Text-to-Speech) → Lautsprecher 🔊 │
│ ↓ │
│ Du unterbrichst → VAD erkennt Sprache │
│ → TTS stoppt sofort │
│ → Whisper nimmt deine neue Eingabe auf │
│ → Kreislauf beginnt von vorn │
│ │
└─────────────────────────────────────────────────┘
```
## Dateistruktur
### Technologie
### Rust Backend (`src-tauri/src/`)
| Komponente | Technologie | Läuft wo |
|---|---|---|
| **Speech-to-Text** | OpenAI Whisper (whisper.cpp) | Lokal auf NixOS, kein Cloud-Upload |
| **Voice Activity Detection** | Silero VAD oder WebRTC VAD | Lokal, erkennt Sprache vs. Stille |
| **Text-to-Speech** | OpenAI TTS API oder ElevenLabs | Cloud (Streaming) |
| **Interrupt-Erkennung** | VAD + sofortiger TTS-Stopp | Lokal |
| Datei | Zeilen | Funktion |
|-------|--------|----------|
| `lib.rs` | ~340 | App-Setup, Tray-Icon, Global Hotkey (Super+C), Plugin-Init |
| `claude.rs` | ~1000 | **Kernmodul**: Bridge-Kommunikation (UDS/stdio), MCP-Hub, Ollama, send_message |
| `db.rs` | ~800 | SQLite: Sessions, Messages, Settings, Projekte, Monitor-Events, Fehler-Tracking |
| `knowledge.rs` | ~1000 | MySQL-Wissensbasis: Suche, Cache (60s TTL), KB-Hints, Smart Hints v2 |
| `session.rs` | ~300 | Session-CRUD, Offline-Queue (queue/flush/clear) |
| `guard.rs` | ~250 | Guard-Rails: Safe/Moderate/Critical/Blocked, Permissions |
| `memory.rs` | ~200 | Persistentes Gedaechtnis: Auto-Load, Patterns, Cross-Session |
| `context.rs` | ~300 | 3-Schichten Context: Sticky/Projekt/Hints, Render fuer Prompt |
| `voice.rs` | ~355 | Whisper STT + Piper TTS (5 deutsche Stimmen), offline |
| `hooks.rs` | ~200 | Hook-System: SessionStart, PreToolUse, PostToolUse |
| `audit.rs` | ~150 | Audit-Log: Alle Aktionen mit Timestamp + Risk-Level |
| `programs.rs` | ~300 | D-Bus Aktionen, Screenshot-Capture, Xvfb |
| `ide.rs` | ~200 | VSCodium-Extension Bridge (WebSocket Port 7890) |
| `clipboard.rs` | ~150 | Clipboard-Watch: Code/URL/Fehler erkennen |
| `teaching.rs` | ~150 | Praesentations-/Schulungsmodus (separates Fenster) |
| `chat_window.rs` | ~100 | Chat-Detach: Separates Fenster herausloesen/zurueckholen |
| `update.rs` | ~250 | Auto-Updater: Forgejo Package Registry, Lock-Datei |
| `commands.rs` | ~100 | Slash-Command Registry (scannt ~/.claude/commands/) |
### Gesprächs-Modi
### Frontend (`src/lib/components/`)
**Freies Gespräch** — wie mit einem Kollegen reden:
- Du redest, Claude hört zu (Whisper transkribiert live)
- Pause > 1,5 Sekunden → Claude antwortet
- Du unterbrichst → Claude stoppt sofort, hört dir zu
- Claude kann nachfragen wenn etwas unklar ist
| Komponente | Funktion |
|-----------|----------|
| `ChatPanel.svelte` | **Hauptkomponente**: Nachrichtenliste, Eingabe, File-Drop, Modus-Indikator |
| `SessionList.svelte` | Sidebar: Sessions, Projekt-Wechsel, Suche |
| `ActivityPanel.svelte` | Live-Aktivitaet: Tool-Calls, Agent-Status |
| `AgentView.svelte` | Subagent-Hierarchie als Baumansicht |
| `MonitorPanel.svelte` | System-Monitor: API/Tool/Error Events |
| `PerformancePanel.svelte` | Token-/Kosten-Statistiken |
| `GuardRailsPanel.svelte` | 3-Tab: Live-Feed / Regeln / Blockiert |
| `VoicePanel.svelte` | Push-to-Talk, Gespraechsmodus, TTS-Wiedergabe |
| `ContextPanel.svelte` | Sticky Context anzeigen/editieren |
| `MemoryPanel.svelte` | Persistentes Gedaechtnis CRUD |
| `KnowledgePanel.svelte` | Wissensbasis durchsuchen |
| `HooksPanel.svelte` | Hook-Verwaltung |
| `SettingsPanel.svelte` | VS-Code-artiges Settings mit Suche/Kategorien |
| `ProgramsPanel.svelte` | D-Bus Aktionen, Screenshot, Xvfb |
| `IdePanel.svelte` | VSCodium-Verbindung Status |
| `AuditLog.svelte` | Audit-Eintraege durchsuchen |
| `QuickActions.svelte` | Ctrl+K Kommandopalette |
| `CommandPalette.svelte` | Slash-Command Autocomplete (/-Eingabe) |
| `CodeBlock.svelte` | Syntax-Highlighting + Copy-Button |
| `DiffView.svelte` | Diff-Anzeige fuer Code-Aenderungen |
| `MermaidDiagram.svelte` | Mermaid-Diagramme rendern |
| `StopButton.svelte` | Abbruch-Button waehrend Agent laeuft |
| `UpdateDialog.svelte` | Auto-Update Bestaetigungsdialog |
| `FilePreview.svelte` | Datei-Vorschau (Text/Bild) |
| `AnimatedCode.svelte` | Code-Tipp-Animation fuer Praesentation |
| `AutoCorrectionModal.svelte` | Fehler-Pattern Vorschlag |
**Diktier-Modus** — Claude führt aus was du sagst:
- "Fixe den Bug in der Preisseite, der Umrechnungsfaktor wird nicht berücksichtigt"
- Claude arbeitet, kommentiert per Sprache was er tut
- Du kannst jederzeit "Stopp" oder "Warte mal" sagen
### Bridge (`scripts/claude-bridge.js`)
**Präsentations-Modus** — Claude erklärt was er gemacht hat:
- "Zeig mir was du geändert hast"
- Claude öffnet Diff-View und erklärt per Sprache die Änderungen
- Du kannst zwischenfragen: "Warum hast du das so gemacht?"
~1100 Zeilen Node.js. Zentrale Datei fuer die Claude-Kommunikation:
### Stimmen-Optionen
- **query()** via `@anthropic-ai/claude-agent-sdk` — streamt Events
- **Multi-Agent-Modi**: Solo / Handlanger (Haiku-Worker) / Experten (4 spezialisierte Agents) / Auto
- **Subagent-Tracking**: Map mit toolUseId → agentId/type/depth
- **MCP-Server Injection**: Configs von Rust empfangen, in queryOptions injizieren
- **Ollama-Integration**: Auto-Detect beim Start, local-query fuer einfache Tasks
- **Auto-Retry**: 3x Backoff bei Rate-Limit/5xx/Netzwerkfehler
- **Session-Resume**: Stale Session-ID → automatisch neue Session starten
- **UDS-Server-Modus**: `--socket /tmp/claude-bridge.sock` fuer Daemon-Betrieb
**OpenAI TTS API:**
- 6 Stimmen (alloy, echo, fable, onyx, nova, shimmer)
- Sehr natürlich, Streaming-fähig (~200ms Latenz)
- Kosten: ~$15 pro 1M Zeichen
### Stores (`src/lib/stores/`)
**ElevenLabs:**
- Hunderte Stimmen, eigene Stimmen klonbar
- Noch natürlicher, emotionaler
- Deutsch-Support gut
- Kosten: ab $5/Monat (30 Min)
| Store | Inhalt |
|-------|--------|
| `app.ts` | activeSession, activeProject, agentMode, chatDetached, processingPhase |
| `events.ts` | Event-Handler fuer alle Bridge-Events (text, result, tool-start/end, ...) |
| `updateTrigger.ts` | Reactive Trigger fuer Panel-Updates |
**Lokal (Piper TTS):**
- Kostenlos, keine Cloud
- Deutsche Stimmen verfügbar
- Qualität gut aber nicht so natürlich wie Cloud
- Keine Latenz durch Netzwerk
---
### Latenz-Budget (Ziel: < 2 Sekunden)
## Features komplett
```
VAD erkennt Stille: ~300ms
Whisper transkribiert: ~500ms (lokal, whisper.cpp mit GPU)
Claude API Antwort: ~800ms (erstes Token, Streaming)
TTS erstes Audio: ~200ms (Streaming)
─────────────────────────────────
Gesamt bis erste Silbe: ~1.800ms
```
### Phase 1-2 (v0.1.0 — 14.04.2026)
- Tauri 2.0 + SvelteKit 5 Grundgeruest
- 4-Panel Layout, Claude Bridge (stdio), Guard-Rails
- SQLite + Session-Management, Claude-DB Integration
- 3-Schichten Context, Multi-Agent-Modi, Hook-System
- VSCodium-Extension, Programm-Steuerung (D-Bus, Xvfb)
- Praesentationsmodus, System-Monitor, Subagent-Hierarchie
- Auto-Updater, Slash-Commands, KB-Hints, Error-Patterns
Mit Streaming: Claude beginnt zu "reden" während er noch denkt — wie ein Mensch der anfängt zu antworten bevor der Gedanke fertig ist.
### Phase 3: Performance (22.04.2026)
- KB-Cache (RAM, 60s TTL), Bridge Warm-Start, Lazy Panel-Load
- Session-Resume Fix, Auto-Retry (3x Backoff), Heartbeat
- FIFO Message Queue, **Bridge-Daemon** (UDS), **Unix Socket IPC**
### Integration in die Desktop-App
### Phase 4: Codium-Killer Features
- Projekt-Wechsel (Ein-Klick), **MCP-Hub nativ** (6 Server)
- Guard-Rails UI (3 Tabs), Persistent Memory, Quick-Actions (Ctrl+K)
- Voice (Whisper + Piper, offline), Settings-Panel, Chat-Detach
- Aktivitaets-Phasen (Denkt/Streamt/Tool/Subagent)
```
┌─────────────────────────────────────────────────────┐
│ Claude Desktop [─][□][×]│
├──────────────┬──────────────────────────────────────┤
│ │ │
│ 💬 Chat │ 📋 Live-Aktivität │
│ │ │
│ (Text wird │ ▶ Lese product/price.php │
│ live mit- │ ▶ Edit actions_produktkarte.class.php│
│ geschrieben │ ✓ Deploy nach /var/www/dolibarr/... │
│ während │ │
│ gesprochen) │ │
│ │ │
├──────────────┴──────────────────────────────────────┤
│ 🎤 ████████░░░░░░ Zuhören... [🔇 Stumm] [⏹] │
└─────────────────────────────────────────────────────┘
```
### Phase 5: Lokale KI + Offline
- Whisper.cpp + Piper-TTS (komplett offline, 5 deutsche Stimmen)
- **Ollama-Integration** (Auto-Detect, local-query)
- **Offline-Queue** (SQLite, flush bei Reconnect)
- Mikrofon-Leiste unten zeigt Pegel
- Gesprochenes wird als Text im Chat mitgeschrieben (Transkript)
- Claudes Antwort wird gleichzeitig als Text angezeigt und vorgelesen
- Stumm-Taste schaltet Mikrofon aus (nur Text-Modus)
### Phase 6: Desktop-Integration
- D-Bus Actions (10 Aktionen), Clipboard-Watch, File-Drop
- Screenshot-Analyse, Global Hotkey (Super+C)
### Whisper lokal auf NixOS
---
```nix
# In configuration.nix
environment.systemPackages = with pkgs; [
whisper-cpp # C++ Port, schnell, CPU/GPU
# oder
openai-whisper # Original Python, braucht mehr RAM
];
```
## MCP-Server (6 Stueck, automatisch geladen)
Whisper "small" oder "medium" Modell reicht für Deutsch — ~500 MB RAM, Echtzeit auf CPU.
| Name | Funktion | Transport |
|------|----------|-----------|
| `forgejo` | Git: Repos, Issues, PRs, Releases | stdio |
| `claude-db` | MySQL Wissensbasis: Suche, CRUD | SSE (MCP-Hub) |
| `playwright` | Browser-Automatisierung | stdio |
| `context7` | Library-Dokumentation | stdio |
| `portainer` | Docker-Container-Management | stdio |
| `homeassistant` | Smart-Home Steuerung | stdio |
## Voraussetzungen
Configs in `~/.claude.json` → werden beim Bridge-Start automatisch injiziert.
### Für Variante A (Desktop-App)
- NixOS: `rustc`, `cargo`, `nodejs`, `tauri-cli` in der Nix-Config
- Anthropic API Key (bereits vorhanden)
- Claude Code SDK npm-Paket
- MySQL-Client-Library für Rust (`sqlx` oder `mysql_async`)
---
### Für Variante B (VM)
- Unraid: VM mit Linux + Desktop (Ubuntu/Debian + XFCE)
- VNC-Server in der VM
- noVNC-Container auf Unraid für Browser-Zugriff
- Isoliertes Netzwerk (VLAN oder Bridge)
- Claude API Key + Computer Use Beta-Zugang
## CI/CD
## Offene Fragen
**Workflow:** `.forgejo/workflows/build-appimage.yml`
**Runner:** `16-Forgejo-Runner-AppImage` (Debian Bookworm)
**Trigger:** `[appimage]` in Commit-Message auf `main`
- [ ] Anthropic API Key Kosten für Computer Use (Screenshot-intensiv)?
- [ ] Tauri 2.0 auf NixOS — Nix-Paket verfügbar?
- [ ] Claude Code SDK — stabil genug für Production?
- [ ] VM auf Unraid — genug RAM/CPU frei?
1. Commit mit `[appimage]` → Push
2. Forgejo Runner baut AppImage (~8-10 Min)
3. Upload in Forgejo Package Registry
4. Ntfy-Notification (Topic: `vk-builds`)
5. App zieht Update beim naechsten Start (Auto-Updater)
**NixOS:** AppImage hat WebKit2GTK/Mesa ABI-Konflikt → lokaler Build mit `shell.nix` noetig. Nix-Wrapper in `~/.local/bin/claude-desktop`.
---
## Tastenkuerzel
| Kuerzel | Aktion |
|---------|--------|
| `Super+C` | Claude-Eingabe von ueberall oeffnen |
| `Ctrl+K` | Quick-Actions Palette |
| `Ctrl+Enter` | Nachricht senden |
| `Escape` | Quick-Actions/Autocomplete schliessen |
| `/` | Slash-Command Autocomplete |
---
## Wissensbasis-Referenzen
| KB-ID | Thema |
|-------|-------|
| #248 | Tauri 2.0 + SvelteKit auf NixOS shell.nix |
| #311 | CI/CD Pipeline (Workflow-Tricks) |
| #371 | Debian Forgejo-Runner Setup |
| #381 | NixOS WebKit2GTK EGL-Crash |
| #382 | Cargo auf SMB → CARGO_TARGET_DIR |
| #384 | Custom-AppRun + linuxdeploy-Hook |
---
## Nicht geplant
- Multi-User / Team-Features
- Cloud-Sync
- Plugin-System (overkill fuer 1-User-App)
- Electron-Port (Tauri bleibt)

View file

@ -1089,16 +1089,39 @@ process.on('SIGINT', () => { clearInterval(keepAlive); cleanupDaemon(); process.
process.on('exit', () => { cleanupDaemon(); });
// Globale Fehler-Handler — Bridge darf nicht still abstürzen
// WICHTIG: err.stack ist ein lazy Getter der bei jedem Zugriff neu formatiert wird
// und bei V8-OOM selbst einen OOM-Abort auslösen kann. Daher: einmal lesen, kürzen,
// try/catch drumrum — der Handler darf nicht selbst crashen.
function safeStack(err) {
try {
const raw = (err && err.stack) ? String(err.stack) : '';
return raw.length > 2000 ? raw.slice(0, 2000) + '\n...[gekürzt]' : raw;
} catch {
return '[stack nicht lesbar]';
}
}
process.on('uncaughtException', (err) => {
process.stderr.write(`❌ Unbehandelter Fehler: ${err.message}\n${err.stack}\n`);
sendEvent('bridge-error', { type: 'uncaughtException', message: err.message, stack: err.stack });
sendMonitorEvent('error', `Bridge Crash: ${err.message}`, { stack: err.stack });
try {
const msg = (err && err.message) ? String(err.message).slice(0, 500) : String(err).slice(0, 500);
const stack = safeStack(err);
process.stderr.write(`❌ Unbehandelter Fehler: ${msg}\n${stack}\n`);
sendEvent('bridge-error', { type: 'uncaughtException', message: msg, stack });
sendMonitorEvent('error', `Bridge Crash: ${msg}`, {});
} catch (inner) {
try { process.stderr.write(`❌ Handler-Fehler: ${inner && inner.message}\n`); } catch {}
}
});
process.on('unhandledRejection', (reason) => {
const msg = reason instanceof Error ? reason.message : String(reason);
process.stderr.write(`❌ Unhandled Promise Rejection: ${msg}\n`);
sendEvent('bridge-error', { type: 'unhandledRejection', message: msg });
sendMonitorEvent('error', `Unhandled Rejection: ${msg}`, {});
try {
const msg = reason instanceof Error
? String(reason.message).slice(0, 500)
: String(reason).slice(0, 500);
process.stderr.write(`❌ Unhandled Promise Rejection: ${msg}\n`);
sendEvent('bridge-error', { type: 'unhandledRejection', message: msg });
sendMonitorEvent('error', `Unhandled Rejection: ${msg}`, {});
} catch (inner) {
try { process.stderr.write(`❌ Handler-Fehler: ${inner && inner.message}\n`); } catch {}
}
});
// Bereit-Signal (im stdio-Modus sofort senden, im Daemon-Modus pro Client bei Connect)

View file

@ -173,7 +173,10 @@ fn start_daemon(script_path: &std::path::Path) -> Result<u32, String> {
println!("🔌 Starte Bridge-Daemon: {:?} --socket {}", script_path, SOCKET_PATH);
// --max-old-space-size=4096: Node-Default (~2GB) reicht nicht bei langen Sessions
// mit großen Thinking-Blocks/Agent-SDK-History (KB #crash-oom-stacktrace).
let child = Command::new("node")
.arg("--max-old-space-size=4096")
.arg(script_path)
.arg("--socket")
.arg(SOCKET_PATH)
@ -328,6 +331,7 @@ fn start_bridge_stdio(app: &AppHandle, script_path: &std::path::Path) -> Result<
println!("📂 Bridge Arbeitsverzeichnis: {:?}", project_dir);
let mut child = Command::new("node")
.arg("--max-old-space-size=4096")
.arg(script_path)
.current_dir(project_dir)
.stdin(Stdio::piped())

View file

@ -208,6 +208,18 @@
activeSessionId = session.id;
$currentSessionId = session.id;
clearAll();
// Projekt automatisch mitwechseln wenn Session ein working_dir hat
if (session.working_dir) {
const matchingProject = projects.find(p => p.working_dir === session.working_dir);
if (matchingProject && matchingProject.id !== activeProject?.id) {
// Projekt auf Rust-Seite wechseln (CWD, Context), aber Sessions NICHT neu laden
const project: Project = await invoke('switch_project', { projectId: matchingProject.id });
activeProject = project;
console.log(`📂 Projekt mitwechselt: ${project.name}`);
}
}
// Nachrichten aus DB laden
await loadSessionMessages(session.id);
} catch (err) {

View file

@ -246,8 +246,8 @@ export function updateAgentStatus(id: string, status: Agent['status']) {
);
}
export function addToolCall(agentId: string, tool: string, args: Record<string, unknown>): string {
const id = crypto.randomUUID();
export function addToolCall(agentId: string, tool: string, args: Record<string, unknown>, fixedId?: string): string {
const id = fixedId || crypto.randomUUID();
const call: ToolCall = {
id,
agentId,
@ -270,18 +270,30 @@ export function addToolCall(agentId: string, tool: string, args: Record<string,
}
export function completeToolCall(id: string, result: unknown, failed = false) {
toolCalls.update((calls) =>
calls.map((c) =>
c.id === id
? {
...c,
status: failed ? 'failed' : 'completed',
completedAt: new Date(),
result
}
: c
)
);
toolCalls.update((calls) => {
// Exakte ID-Suche
let found = calls.some((c) => c.id === id);
if (found) {
return calls.map((c) =>
c.id === id
? { ...c, status: (failed ? 'failed' : 'completed') as ToolCall['status'], completedAt: new Date(), result }
: c
);
}
// Fallback: Letzten laufenden Tool-Call abschließen (für Events ohne passende ID)
const lastRunning = [...calls].reverse().find((c) => c.status === 'running');
if (lastRunning) {
return calls.map((c) =>
c.id === lastRunning.id
? { ...c, status: (failed ? 'failed' : 'completed') as ToolCall['status'], completedAt: new Date(), result }
: c
);
}
return calls;
});
}
export function clearAll() {

View file

@ -226,8 +226,8 @@ export async function initEventListeners(): Promise<void> {
// Tool Start
listeners.push(
await listen<ToolEvent>('tool-start', async (event) => {
const { tool, input } = event.payload;
console.log('🔧 Tool Start:', tool);
const { id, tool, input } = event.payload;
console.log('🔧 Tool Start:', tool, id);
// Inline-Aktivitätsanzeige aktualisieren
currentTool.set({ tool: tool || 'unknown', input: input || {} });
@ -236,7 +236,8 @@ export async function initEventListeners(): Promise<void> {
agents.update((ags) => {
const activeAgent = ags.find((a) => a.status === 'active');
if (activeAgent) {
addToolCall(activeAgent.id, tool || 'unknown', input || {});
// Backend-ID durchreichen damit tool-end den Call matchen kann
addToolCall(activeAgent.id, tool || 'unknown', input || {}, id);
}
return ags;
});