Compare commits
No commits in common. "48c7b2a30ce2afd26b340b3748d9f5386810f019" and "9cc97311b02fcf5425258457cbb9c2e14c1aa2a0" have entirely different histories.
48c7b2a30c
...
9cc97311b0
7 changed files with 554 additions and 381 deletions
192
CLAUDE.md
192
CLAUDE.md
|
|
@ -1,131 +1,107 @@
|
|||
# Claude Desktop — Projekt-Kontext
|
||||
# Claude Desktop
|
||||
|
||||
Native Tauri-2.0-Desktop-App die Claude Code/Agent SDK als Backend nutzt.
|
||||
Schneller und maechtiger als Codium + Claude Code Extension.
|
||||
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.
|
||||
|
||||
**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)
|
||||
Detail-Übersicht + Status: [README.md](README.md). Phasen-Stand: [ROADMAP.md](ROADMAP.md).
|
||||
|
||||
## Tech-Stack
|
||||
|
||||
- **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
|
||||
- **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
|
||||
|
||||
## Wichtige Pfade
|
||||
|
||||
| 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` |
|
||||
- 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)
|
||||
|
||||
## Architektur-Kurzreferenz
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 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>,
|
||||
}
|
||||
```
|
||||
|
||||
### 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() |
|
||||
|
||||
### 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`
|
||||
|
||||
### Offline-Queue (Phase 5)
|
||||
- SQLite-Tabelle `offline_queue`
|
||||
- Commands: `queue_message`, `list_queued_messages`, `flush_offline_queue`, `clear_offline_queue`, `queue_count`
|
||||
|
||||
## Build & Deploy
|
||||
## Build & Run
|
||||
|
||||
### Native Dev (Hot-Reload)
|
||||
```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!
|
||||
nix-shell shell.nix --run 'npm ci && npm run tauri:dev'
|
||||
```
|
||||
|
||||
### 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`
|
||||
### 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
|
||||
```
|
||||
|
||||
## NixOS-Spezialfall
|
||||
### 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*`.
|
||||
|
||||
## NixOS-Spezialfall — AppImage funktioniert NICHT
|
||||
|
||||
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.
|
||||
|
||||
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, 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
|
||||
- **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
|
||||
|
||||
## Haeufige Probleme & Loesungen
|
||||
## Workflow-Eigenheiten (CI/CD)
|
||||
|
||||
| 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) |
|
||||
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)
|
||||
|
||||
## Wissensbasis-Referenzen
|
||||
## Wissensbasis (Claude-DB)
|
||||
|
||||
| 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 |
|
||||
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
|
||||
```
|
||||
|
|
|
|||
643
README.md
643
README.md
|
|
@ -1,242 +1,491 @@
|
|||
# Claude Desktop
|
||||
# Claude Desktop — Nativer AI-Assistent
|
||||
|
||||
Nativer AI-Desktop-Assistent fuer Linux. Schneller und maechtiger als Codium + Claude Code Extension.
|
||||
Eigenständige Desktop-Anwendung die Claude Code als Backend nutzt, mit nativem UI, Live-Übersicht und kontrolliertem OS-Zugriff.
|
||||
|
||||
**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
|
||||
## 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.
|
||||
|
||||
## Schnellstart
|
||||
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
|
||||
# Development (Hot-Reload)
|
||||
cd "/mnt/17 - Entwicklungen/20 - Projekte/ClaudeDesktop"
|
||||
CARGO_TARGET_DIR=/tmp/claude-desktop-target nix-shell --run "npx tauri dev"
|
||||
|
||||
# Produktion (AppImage)
|
||||
CARGO_TARGET_DIR=/tmp/claude-desktop-target nix-shell --run "npx tauri build -- --bundles appimage"
|
||||
|
||||
# CI: Commit mit [appimage] im Message → Forgejo Runner baut + uploaded automatisch
|
||||
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 |
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
## Motivation
|
||||
|
||||
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)
|
||||
|
||||
## Vision
|
||||
|
||||
### Variante A: Native Desktop-App (begleitend)
|
||||
|
||||
Claude arbeitet begleitend — der User sieht alles mit und kann jederzeit eingreifen.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ 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) │
|
||||
└──────────────────────┴───────────────────────────────────┘
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 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 │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
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)
|
||||
### 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
|
||||
|
||||
### Bridge-Modi
|
||||
### 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
|
||||
}
|
||||
```
|
||||
|
||||
| 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 |
|
||||
## Sprach-Interface — Reden mit Claude
|
||||
|
||||
Socket-Pfad: `/tmp/claude-bridge.sock`, PID-File: `/tmp/claude-bridge.pid`
|
||||
### Konzept
|
||||
|
||||
---
|
||||
Echtes Gespräch mit Claude — reden, unterbrechen, weiterreden. Kein "Aufnahme starten/stoppen", sondern natürlicher Dialog.
|
||||
|
||||
## Dateistruktur
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 🎤 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 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Rust Backend (`src-tauri/src/`)
|
||||
### Technologie
|
||||
|
||||
| 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/) |
|
||||
| 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 |
|
||||
|
||||
### Frontend (`src/lib/components/`)
|
||||
### Gesprächs-Modi
|
||||
|
||||
| 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 |
|
||||
**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
|
||||
|
||||
### Bridge (`scripts/claude-bridge.js`)
|
||||
**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
|
||||
|
||||
~1100 Zeilen Node.js. Zentrale Datei fuer die Claude-Kommunikation:
|
||||
**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?"
|
||||
|
||||
- **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
|
||||
### Stimmen-Optionen
|
||||
|
||||
### Stores (`src/lib/stores/`)
|
||||
**OpenAI TTS API:**
|
||||
- 6 Stimmen (alloy, echo, fable, onyx, nova, shimmer)
|
||||
- Sehr natürlich, Streaming-fähig (~200ms Latenz)
|
||||
- Kosten: ~$15 pro 1M Zeichen
|
||||
|
||||
| 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 |
|
||||
**ElevenLabs:**
|
||||
- Hunderte Stimmen, eigene Stimmen klonbar
|
||||
- Noch natürlicher, emotionaler
|
||||
- Deutsch-Support gut
|
||||
- Kosten: ab $5/Monat (30 Min)
|
||||
|
||||
---
|
||||
**Lokal (Piper TTS):**
|
||||
- Kostenlos, keine Cloud
|
||||
- Deutsche Stimmen verfügbar
|
||||
- Qualität gut aber nicht so natürlich wie Cloud
|
||||
- Keine Latenz durch Netzwerk
|
||||
|
||||
## Features komplett
|
||||
### Latenz-Budget (Ziel: < 2 Sekunden)
|
||||
|
||||
### 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
|
||||
```
|
||||
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 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**
|
||||
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 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)
|
||||
### Integration in die Desktop-App
|
||||
|
||||
### 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)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 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 6: Desktop-Integration
|
||||
- D-Bus Actions (10 Aktionen), Clipboard-Watch, File-Drop
|
||||
- Screenshot-Analyse, Global Hotkey (Super+C)
|
||||
- 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)
|
||||
|
||||
---
|
||||
### Whisper lokal auf NixOS
|
||||
|
||||
## MCP-Server (6 Stueck, automatisch geladen)
|
||||
```nix
|
||||
# In configuration.nix
|
||||
environment.systemPackages = with pkgs; [
|
||||
whisper-cpp # C++ Port, schnell, CPU/GPU
|
||||
# oder
|
||||
openai-whisper # Original Python, braucht mehr RAM
|
||||
];
|
||||
```
|
||||
|
||||
| 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 |
|
||||
Whisper "small" oder "medium" Modell reicht für Deutsch — ~500 MB RAM, Echtzeit auf CPU.
|
||||
|
||||
Configs in `~/.claude.json` → werden beim Bridge-Start automatisch injiziert.
|
||||
## Voraussetzungen
|
||||
|
||||
---
|
||||
### 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`)
|
||||
|
||||
## CI/CD
|
||||
### 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
|
||||
|
||||
**Workflow:** `.forgejo/workflows/build-appimage.yml`
|
||||
**Runner:** `16-Forgejo-Runner-AppImage` (Debian Bookworm)
|
||||
**Trigger:** `[appimage]` in Commit-Message auf `main`
|
||||
## Offene Fragen
|
||||
|
||||
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)
|
||||
- [ ] 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?
|
||||
|
|
|
|||
|
|
@ -1089,39 +1089,16 @@ 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) => {
|
||||
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.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 });
|
||||
});
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
try {
|
||||
const msg = reason instanceof Error
|
||||
? String(reason.message).slice(0, 500)
|
||||
: String(reason).slice(0, 500);
|
||||
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}`, {});
|
||||
} 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)
|
||||
|
|
|
|||
|
|
@ -173,10 +173,7 @@ 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)
|
||||
|
|
@ -331,7 +328,6 @@ 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())
|
||||
|
|
|
|||
|
|
@ -208,18 +208,6 @@
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -246,8 +246,8 @@ export function updateAgentStatus(id: string, status: Agent['status']) {
|
|||
);
|
||||
}
|
||||
|
||||
export function addToolCall(agentId: string, tool: string, args: Record<string, unknown>, fixedId?: string): string {
|
||||
const id = fixedId || crypto.randomUUID();
|
||||
export function addToolCall(agentId: string, tool: string, args: Record<string, unknown>): string {
|
||||
const id = crypto.randomUUID();
|
||||
const call: ToolCall = {
|
||||
id,
|
||||
agentId,
|
||||
|
|
@ -270,30 +270,18 @@ export function addToolCall(agentId: string, tool: string, args: Record<string,
|
|||
}
|
||||
|
||||
export function completeToolCall(id: string, result: unknown, failed = false) {
|
||||
toolCalls.update((calls) => {
|
||||
// Exakte ID-Suche
|
||||
let found = calls.some((c) => c.id === id);
|
||||
|
||||
if (found) {
|
||||
return calls.map((c) =>
|
||||
toolCalls.update((calls) =>
|
||||
calls.map((c) =>
|
||||
c.id === id
|
||||
? { ...c, status: (failed ? 'failed' : 'completed') as ToolCall['status'], completedAt: new Date(), result }
|
||||
: c
|
||||
);
|
||||
? {
|
||||
...c,
|
||||
status: failed ? 'failed' : 'completed',
|
||||
completedAt: new Date(),
|
||||
result
|
||||
}
|
||||
|
||||
// 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() {
|
||||
|
|
|
|||
|
|
@ -226,8 +226,8 @@ export async function initEventListeners(): Promise<void> {
|
|||
// Tool Start
|
||||
listeners.push(
|
||||
await listen<ToolEvent>('tool-start', async (event) => {
|
||||
const { id, tool, input } = event.payload;
|
||||
console.log('🔧 Tool Start:', tool, id);
|
||||
const { tool, input } = event.payload;
|
||||
console.log('🔧 Tool Start:', tool);
|
||||
|
||||
// Inline-Aktivitätsanzeige aktualisieren
|
||||
currentTool.set({ tool: tool || 'unknown', input: input || {} });
|
||||
|
|
@ -236,8 +236,7 @@ export async function initEventListeners(): Promise<void> {
|
|||
agents.update((ags) => {
|
||||
const activeAgent = ags.find((a) => a.status === 'active');
|
||||
if (activeAgent) {
|
||||
// Backend-ID durchreichen damit tool-end den Call matchen kann
|
||||
addToolCall(activeAgent.id, tool || 'unknown', input || {}, id);
|
||||
addToolCall(activeAgent.id, tool || 'unknown', input || {});
|
||||
}
|
||||
return ags;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue