Phase 5: Subagent-Hierarchie + ROADMAP erweitert
Subagent-Hierarchie:
- Agent Interface erweitert: parentAgentId, depth, model
- claude-bridge.js: Erkennt Task-Tool als Subagent-Start
- events.ts: Listener für subagent-started/stopped
- AgentView.svelte: Baumansicht mit Einrückung + Collapse
ROADMAP erweitert (Phase 5-16):
- Phase 5: Subagent-Hierarchie ✅
- Phase 6-9: Session, UI, Claude-DB, Context
- Phase 10: Sprach-Interface
- Phase 11: Multi-Agent-Modi (Solo/Handlanger/Experten)
- Phase 12: Hook-System
- Phase 13: VSCodium Integration
- Phase 14: Programm-Steuerung (Playwright, D-Bus)
- Phase 15: Schulungsmodus (Mermaid, animierter Code)
- Phase 16: System-Monitor (Debug-Panel)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
18c8ef2f4f
commit
6cfcdb2c79
5 changed files with 1096 additions and 51 deletions
663
ROADMAP.md
663
ROADMAP.md
|
|
@ -594,9 +594,668 @@ CREATE TABLE concept_cache (
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Nicht geplant / Zukunft
|
## Phase 13: VSCodium Integration (IDE-Steuerung)
|
||||||
|
|
||||||
- [ ] Computer Use (VM mit Desktop)
|
### Das Ziel
|
||||||
|
|
||||||
|
Claude bedient VSCodium komplett — OHNE deine Maus zu blockieren.
|
||||||
|
|
||||||
|
### Architektur
|
||||||
|
|
||||||
|
```
|
||||||
|
Claude Desktop ←→ VSCode Extension Bridge ←→ VSCodium
|
||||||
|
│ │
|
||||||
|
│ ├─ VSCode Commands (Tabs, Panels, Git)
|
||||||
|
│ ├─ LSP (Refactoring, Go-To, Find References)
|
||||||
|
│ └─ Debug Adapter (Breakpoints, Step)
|
||||||
|
│
|
||||||
|
└─ Kein Computer Use! Keine Screenshots! Keine Maus-Simulation!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Was Claude damit kann
|
||||||
|
|
||||||
|
| Aktion | Wie | Aktuell? |
|
||||||
|
|--------|-----|----------|
|
||||||
|
| Datei öffnen | `workbench.action.files.openFile` | ❌ Nein |
|
||||||
|
| Code formatieren | `editor.action.formatDocument` | ❌ Nein |
|
||||||
|
| Suche in Projekt | `workbench.action.findInFiles` | ❌ Nein |
|
||||||
|
| Git Commit | `git.commit` | ❌ (nur CLI) |
|
||||||
|
| Debugger starten | `workbench.action.debug.start` | ❌ Nein |
|
||||||
|
| Refactoring | LSP `textDocument/rename` | ❌ Nein |
|
||||||
|
| Go-To Definition | LSP `textDocument/definition` | ❌ Nein |
|
||||||
|
| Terminal öffnen | `workbench.action.terminal.new` | ❌ Nein |
|
||||||
|
|
||||||
|
### Aufgaben
|
||||||
|
|
||||||
|
- [ ] **VSCode Extension: Claude Bridge**
|
||||||
|
- [ ] Neues Extension-Projekt erstellen
|
||||||
|
- [ ] WebSocket-Server für Kommunikation mit Claude Desktop
|
||||||
|
- [ ] Command-Executor: VSCode Commands ausführen
|
||||||
|
- [ ] LSP-Bridge: Language Server Anfragen weiterleiten
|
||||||
|
- [ ] Status-Reporter: Was ist gerade offen? Wo ist Cursor?
|
||||||
|
|
||||||
|
- [ ] **Claude Desktop: IDE-Connector**
|
||||||
|
- [ ] `src-tauri/src/ide.rs` — Verbindung zur Extension
|
||||||
|
- [ ] `connectToIDE()` — WebSocket zu VSCodium
|
||||||
|
- [ ] `executeIDECommand(command, args)` — Befehl ausführen
|
||||||
|
- [ ] `getLSPInfo(file, position)` — Semantische Infos
|
||||||
|
|
||||||
|
- [ ] **Neue Tools für Claude**
|
||||||
|
- [ ] `IDE.openFile(path)` — Datei im Editor öffnen
|
||||||
|
- [ ] `IDE.goToLine(path, line)` — Zu Zeile springen
|
||||||
|
- [ ] `IDE.formatDocument()` — Formatieren
|
||||||
|
- [ ] `IDE.rename(oldName, newName)` — Refactoring
|
||||||
|
- [ ] `IDE.findReferences(symbol)` — Alle Verwendungen
|
||||||
|
- [ ] `IDE.startDebug(config)` — Debugger starten
|
||||||
|
- [ ] `IDE.gitCommit(message)` — Via Git-Panel
|
||||||
|
|
||||||
|
- [ ] **UI: IDE-Status anzeigen**
|
||||||
|
- [ ] Verbindungsstatus zu VSCodium
|
||||||
|
- [ ] Aktuell offene Datei
|
||||||
|
- [ ] Cursor-Position
|
||||||
|
|
||||||
|
### Warum NICHT Computer Use?
|
||||||
|
|
||||||
|
| Aspekt | Computer Use | Extension Commands |
|
||||||
|
|--------|--------------|-------------------|
|
||||||
|
| Geschwindigkeit | Langsam (Screenshots) | Sofort |
|
||||||
|
| Zuverlässigkeit | Fehleranfällig | 100% präzise |
|
||||||
|
| Maus blockiert? | Ja | Nein |
|
||||||
|
| Kosten | Hoch (viele API-Calls) | Minimal |
|
||||||
|
| Extra Display? | Ja (oder VM) | Nein |
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
```bash
|
||||||
|
# Claude Desktop starten, VSCodium starten
|
||||||
|
# In Claude: "Öffne die Datei src/lib.rs"
|
||||||
|
# → VSCodium öffnet die Datei, DU kannst weiter tippen
|
||||||
|
# In Claude: "Formatiere das Dokument"
|
||||||
|
# → Code wird formatiert, ohne dass du was tust
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 14: Programm-Steuerung (Nicht nur IDE!)
|
||||||
|
|
||||||
|
### Das Problem
|
||||||
|
|
||||||
|
Terminal ist ein Flaschenhals für:
|
||||||
|
- **GUI-Programme** (Dolibarr im Browser, Systemeinstellungen)
|
||||||
|
- **Visuelle Aufgaben** (Screenshots prüfen, UI testen)
|
||||||
|
- **Programme ohne CLI** (viele Desktop-Apps)
|
||||||
|
|
||||||
|
### Lösungs-Stack (je nach Programm-Typ)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ PROGRAMM-TYP │ STEUERUNG │
|
||||||
|
├────────────────────────────────────────────────────────────────┤
|
||||||
|
│ VSCodium/IDE │ Extension Commands (Phase 13) │
|
||||||
|
│ Browser/Web-Apps │ Playwright MCP ← Dolibarr! │
|
||||||
|
│ Terminal/CLI │ Bash (bereits vorhanden) │
|
||||||
|
│ Native Linux-Apps │ D-Bus + XDG-Portals │
|
||||||
|
│ Alles andere │ Computer Use (separates Display) │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priorität 1: Playwright für Web-Apps (Dolibarr!)
|
||||||
|
|
||||||
|
Der Playwright MCP-Server ist bereits konfiguriert! Damit kann Claude:
|
||||||
|
|
||||||
|
| Aktion | Playwright-Tool | Use Case |
|
||||||
|
|--------|----------------|----------|
|
||||||
|
| Seite öffnen | `browser_navigate` | Dolibarr aufrufen |
|
||||||
|
| Element klicken | `browser_click` | Buttons, Links |
|
||||||
|
| Formular ausfüllen | `browser_fill_form` | Daten eingeben |
|
||||||
|
| Screenshot | `browser_take_screenshot` | UI prüfen |
|
||||||
|
| Warten | `browser_wait_for` | Auf Ladezeit warten |
|
||||||
|
| Snapshot (DOM) | `browser_snapshot` | Struktur analysieren |
|
||||||
|
|
||||||
|
**Vorteil:** Läuft im Hintergrund, blockiert NICHT deine Maus!
|
||||||
|
|
||||||
|
### Priorität 2: D-Bus für Linux-Apps
|
||||||
|
|
||||||
|
Viele Linux-Programme sind über D-Bus steuerbar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Beispiele
|
||||||
|
dbus-send --session --dest=org.kde.dolphin ... # Dateimanager
|
||||||
|
dbus-send --session --dest=org.kde.kate ... # Kate Editor
|
||||||
|
qdbus org.freedesktop.Notifications ... # Benachrichtigungen
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priorität 3: Computer Use (Notfall-Option)
|
||||||
|
|
||||||
|
Für Programme die KEINE API/CLI/D-Bus haben:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ LÖSUNG: Separates Display (:1) │
|
||||||
|
│ │
|
||||||
|
│ Display :0 (Haupt) │ Display :1 (Claude) │
|
||||||
|
│ ├─ Dein Desktop │ ├─ Xvfb (virtuell) │
|
||||||
|
│ ├─ Deine Maus ✓ │ ├─ Claude steuert │
|
||||||
|
│ └─ Kein Konflikt │ └─ VNC-Zugang optional │
|
||||||
|
│ │
|
||||||
|
│ Du arbeitest normal │ Claude bedient GUI-Programme │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Aufgaben
|
||||||
|
|
||||||
|
- [ ] **Playwright Integration aktivieren**
|
||||||
|
- [ ] MCP-Server ist bereits da → Tools verfügbar machen
|
||||||
|
- [ ] Dolibarr-Login automatisieren (Session merken)
|
||||||
|
- [ ] UI in App: Browser-Aktionen anzeigen
|
||||||
|
|
||||||
|
- [ ] **D-Bus Modul**
|
||||||
|
- [ ] `src-tauri/src/dbus.rs` (NEU)
|
||||||
|
- [ ] `list_dbus_services()` — Was ist verfügbar?
|
||||||
|
- [ ] `call_dbus_method(service, path, method, args)`
|
||||||
|
- [ ] Wrapper für häufige Aktionen (Dolphin, Kate)
|
||||||
|
|
||||||
|
- [ ] **Computer Use (separates Display)**
|
||||||
|
- [ ] `src-tauri/src/xdisplay.rs` (NEU)
|
||||||
|
- [ ] `start_virtual_display()` — Xvfb auf :1 starten
|
||||||
|
- [ ] `capture_display(display_num)` — Screenshot
|
||||||
|
- [ ] `send_input(display_num, action)` — Maus/Tastatur
|
||||||
|
- [ ] VNC-Server optional für Monitoring
|
||||||
|
|
||||||
|
- [ ] **UI: Programm-Steuerung Panel**
|
||||||
|
- [ ] Browser-View (Playwright-Screenshots)
|
||||||
|
- [ ] D-Bus Services auflisten
|
||||||
|
- [ ] Computer-Use Display einbetten (VNC-Widget)
|
||||||
|
- [ ] Toggle: "Zeige was Claude gerade bedient"
|
||||||
|
|
||||||
|
### Entscheidungsbaum für Claude
|
||||||
|
|
||||||
|
```
|
||||||
|
Aufgabe erhalten
|
||||||
|
│
|
||||||
|
├─ Ist es eine Web-App? ────────────→ Playwright
|
||||||
|
│
|
||||||
|
├─ Ist es VSCodium/IDE? ────────────→ Extension Commands
|
||||||
|
│
|
||||||
|
├─ Hat das Programm CLI? ───────────→ Terminal/Bash
|
||||||
|
│
|
||||||
|
├─ Hat das Programm D-Bus? ─────────→ D-Bus
|
||||||
|
│
|
||||||
|
└─ Alles andere ────────────────────→ Computer Use (Display :1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
```bash
|
||||||
|
# Test: Playwright
|
||||||
|
# Claude: "Öffne Dolibarr und zeige mir die offenen Rechnungen"
|
||||||
|
# → Browser öffnet, navigiert, Screenshot im App
|
||||||
|
|
||||||
|
# Test: D-Bus
|
||||||
|
# Claude: "Öffne Dolphin im Ordner ~/Downloads"
|
||||||
|
# → Dolphin öffnet sich mit dem Ordner
|
||||||
|
|
||||||
|
# Test: Computer Use
|
||||||
|
# Claude: "Öffne LibreOffice und erstelle ein Dokument"
|
||||||
|
# → Display :1 zeigt LibreOffice, VNC optional zum Zuschauen
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 15: Präsentations- & Schulungsmodus (Lehrer-Modus)
|
||||||
|
|
||||||
|
### Das Ziel
|
||||||
|
|
||||||
|
Claude als **Lehrer** — erklärt, visualisiert, programmiert langsam und nachvollziehbar.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ NORMALER MODUS │ SCHULUNGSMODUS │
|
||||||
|
│ • Claude arbeitet schnell │ • Claude erklärt jeden Schritt │
|
||||||
|
│ • Ergebnis im Chat │ • Visualisierungen live │
|
||||||
|
│ • Effizient │ • Code wird "getippt" │
|
||||||
|
│ │ • Pause/Play jederzeit │
|
||||||
|
│ │ • Geschwindigkeit einstellbar │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
#### 1. Präsentationsfenster
|
||||||
|
|
||||||
|
Separates Fenster (oder Panel) für Visualisierungen:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Präsentation [─][□][×] │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌─────────┐ │
|
||||||
|
│ │ Thema │ │
|
||||||
|
│ └────┬────┘ │
|
||||||
|
│ ┌───────┼───────┐ │
|
||||||
|
│ ▼ ▼ ▼ │
|
||||||
|
│ ┌───────┐ ┌───────┐ ┌───────┐ │
|
||||||
|
│ │Punkt 1│ │Punkt 2│ │Punkt 3│ ← Live Mindmap │
|
||||||
|
│ └───────┘ └───────┘ └───────┘ │
|
||||||
|
│ │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ ◀◀ ▶ Pause ▶▶ │ Geschwindigkeit: [████░░░░] 60% │ 📢 TTS │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Visualisierungs-Typen
|
||||||
|
|
||||||
|
| Typ | Technologie | Use Case |
|
||||||
|
|-----|-------------|----------|
|
||||||
|
| **Mindmap** | Mermaid.js | Konzepte erklären |
|
||||||
|
| **Flowchart** | Mermaid.js | Programmablauf |
|
||||||
|
| **Sequenzdiagramm** | Mermaid.js | API-Calls, Prozesse |
|
||||||
|
| **Code** | Monaco Editor | Live Coding |
|
||||||
|
| **Architektur** | Mermaid/D3.js | System-Übersicht |
|
||||||
|
| **Freihand** | Excalidraw | Skizzen |
|
||||||
|
| **Terminal** | xterm.js | Befehle zeigen |
|
||||||
|
|
||||||
|
#### 3. Animiertes Coding
|
||||||
|
|
||||||
|
Claude "tippt" Code langsam — wie ein Mensch:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Geschwindigkeit einstellbar: 30-200 WPM
|
||||||
|
function animateCode(code: string, wpm: number) {
|
||||||
|
const charDelay = 60000 / (wpm * 5); // ~5 Zeichen/Wort
|
||||||
|
for (const char of code) {
|
||||||
|
await sleep(charDelay);
|
||||||
|
appendChar(char);
|
||||||
|
highlightLine(currentLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Syntax-Highlighting während des Tippens
|
||||||
|
- Cursor-Animation
|
||||||
|
- Zeile wird hervorgehoben
|
||||||
|
- Erklärung erscheint parallel (Chat oder TTS)
|
||||||
|
|
||||||
|
#### 4. Erklär-Modus
|
||||||
|
|
||||||
|
Claude erklärt **während** er arbeitet:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ Code-Panel ──────────────┬─ Erklärung ─────────────────────────┐
|
||||||
|
│ │ │
|
||||||
|
│ function add(a, b) { │ "Ich definiere eine Funktion │
|
||||||
|
│ return a + b; ← cursor │ namens 'add' mit zwei Parametern. │
|
||||||
|
│ } │ Diese gibt die Summe zurück." │
|
||||||
|
│ │ │
|
||||||
|
│ │ [🔊 Vorlesen] │
|
||||||
|
└───────────────────────────┴──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. Interaktive Elemente
|
||||||
|
|
||||||
|
- **Pause** — Jederzeit anhalten
|
||||||
|
- **Zurück** — Letzten Schritt wiederholen
|
||||||
|
- **Frage stellen** — Unterbricht Präsentation
|
||||||
|
- **Geschwindigkeit** — Slider (langsam ↔ schnell)
|
||||||
|
- **TTS ein/aus** — Sprachausgabe toggle
|
||||||
|
- **Vollbild** — Für Präsentationen
|
||||||
|
|
||||||
|
### Schulungs-Modi
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ MODUS: ERKLÄRER │
|
||||||
|
│ Claude erklärt ein Konzept mit Visualisierungen │
|
||||||
|
│ "Erkläre mir wie async/await funktioniert" │
|
||||||
|
│ → Mindmap + Flowchart + Code-Beispiele │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ MODUS: LIVE CODING │
|
||||||
|
│ Claude programmiert langsam und erklärt jeden Schritt │
|
||||||
|
│ "Zeig mir wie man einen REST-API-Client baut" │
|
||||||
|
│ → Code wird getippt, jede Zeile erklärt │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ MODUS: WALKTHROUGH │
|
||||||
|
│ Claude führt durch existierenden Code │
|
||||||
|
│ "Erkläre mir diese Datei" │
|
||||||
|
│ → Code wird Zeile für Zeile durchgegangen │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ MODUS: KONFIGURATION │
|
||||||
|
│ Claude erklärt Config-Dateien │
|
||||||
|
│ "Erkläre mir die nginx.conf" │
|
||||||
|
│ → Optionen werden einzeln erklärt + visualisiert │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Aufgaben
|
||||||
|
|
||||||
|
- [ ] **Präsentationsfenster**
|
||||||
|
- [ ] Neues Tauri-Window für Präsentation
|
||||||
|
- [ ] Kann neben Hauptfenster existieren
|
||||||
|
- [ ] Steuerleiste (Play/Pause/Speed)
|
||||||
|
- [ ] Vollbild-Toggle
|
||||||
|
|
||||||
|
- [ ] **Mermaid.js Integration**
|
||||||
|
- [ ] `src/lib/components/MermaidDiagram.svelte`
|
||||||
|
- [ ] Live-Rendering von Diagrammen
|
||||||
|
- [ ] Animierte Knoten-Hinzufügung
|
||||||
|
- [ ] Zoom und Pan
|
||||||
|
|
||||||
|
- [ ] **Animiertes Code-Panel**
|
||||||
|
- [ ] `src/lib/components/AnimatedCode.svelte`
|
||||||
|
- [ ] Monaco Editor mit Tipp-Animation
|
||||||
|
- [ ] Geschwindigkeits-Kontrolle
|
||||||
|
- [ ] Zeilen-Highlighting
|
||||||
|
- [ ] Cursor-Animation
|
||||||
|
|
||||||
|
- [ ] **Erklär-System**
|
||||||
|
- [ ] Claude generiert Erklärungen parallel zum Code
|
||||||
|
- [ ] Synchronisation: Code-Position ↔ Erklärung
|
||||||
|
- [ ] Optional: TTS für Erklärungen (Phase 10)
|
||||||
|
|
||||||
|
- [ ] **Schulungs-Prompts**
|
||||||
|
- [ ] Spezieller System-Prompt für Lehrer-Modus
|
||||||
|
- [ ] "Du bist ein geduldiger Lehrer. Erkläre jeden Schritt."
|
||||||
|
- [ ] Strukturierte Ausgabe: `{code, explanation, visualization}`
|
||||||
|
|
||||||
|
- [ ] **UI: Modus-Wechsel**
|
||||||
|
- [ ] Button: "Schulungsmodus aktivieren"
|
||||||
|
- [ ] Oder: "Erkläre mir das" Button bei jeder Antwort
|
||||||
|
- [ ] Settings: Standard-Geschwindigkeit, TTS an/aus
|
||||||
|
|
||||||
|
- [ ] **Excalidraw-ähnliche Skizzen**
|
||||||
|
- [ ] Für Freihand-Diagramme
|
||||||
|
- [ ] Claude kann "zeichnen" (Koordinaten → Shapes)
|
||||||
|
- [ ] Export als PNG/SVG
|
||||||
|
|
||||||
|
### Beispiel-Ablauf
|
||||||
|
|
||||||
|
**User:** "Erkläre mir wie Git Branches funktionieren"
|
||||||
|
|
||||||
|
**Claude (Schulungsmodus):**
|
||||||
|
|
||||||
|
1. **Mindmap erscheint:**
|
||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root((Git Branches))
|
||||||
|
Was ist ein Branch?
|
||||||
|
Zeiger auf Commit
|
||||||
|
Leichtgewichtig
|
||||||
|
Warum Branches?
|
||||||
|
Parallele Entwicklung
|
||||||
|
Features isolieren
|
||||||
|
Befehle
|
||||||
|
git branch
|
||||||
|
git checkout
|
||||||
|
git merge
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Code-Animation:**
|
||||||
|
```bash
|
||||||
|
# Neuen Branch erstellen
|
||||||
|
git branch feature-login ← tippt langsam
|
||||||
|
|
||||||
|
# Zum Branch wechseln
|
||||||
|
git checkout feature-login
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Erklärung (parallel):**
|
||||||
|
> "Ein Branch ist wie ein paralleles Universum für deinen Code.
|
||||||
|
> Du kannst Änderungen machen ohne den Hauptcode zu beeinflussen..."
|
||||||
|
|
||||||
|
4. **Flowchart:**
|
||||||
|
```mermaid
|
||||||
|
gitGraph
|
||||||
|
commit id: "Initial"
|
||||||
|
branch feature-login
|
||||||
|
commit id: "Login-Form"
|
||||||
|
checkout main
|
||||||
|
commit id: "Hotfix"
|
||||||
|
merge feature-login
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
```bash
|
||||||
|
# Test: Schulungsmodus aktivieren
|
||||||
|
# "Erkläre mir Promises in JavaScript"
|
||||||
|
# → Präsentationsfenster öffnet sich
|
||||||
|
# → Mindmap erscheint animiert
|
||||||
|
# → Code wird langsam getippt
|
||||||
|
# → Erklärungen synchron angezeigt
|
||||||
|
# → Pause funktioniert
|
||||||
|
# → Geschwindigkeit änderbar
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 16: System-Monitor (Debug-Panel)
|
||||||
|
|
||||||
|
### Das Ziel
|
||||||
|
|
||||||
|
**Echtzeit-Einblick** was im Hintergrund passiert — vom Programm generiert, NICHT von der KI!
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ System-Monitor [Filter ▼] │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 14:32:01.234 🔵 API → claude-3-opus [1.2s] 2.4k tok │
|
||||||
|
│ 14:32:02.891 🟢 HOOK SessionStart fired │
|
||||||
|
│ 14:32:03.012 🟡 TOOL Read src/lib.rs (245 lines) │
|
||||||
|
│ 14:32:03.456 🔵 API ← Response streaming... │
|
||||||
|
│ 14:32:04.123 🟡 TOOL Grep "handleError" (3 matches) │
|
||||||
|
│ 14:32:04.567 🟣 MCP playwright.browser_navigate │
|
||||||
|
│ 14:32:05.234 🔴 ERROR Connection timeout (retry 1/3) │
|
||||||
|
│ 14:32:05.890 🟢 HOOK PreToolUse → injected 2 hints │
|
||||||
|
│ │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ ▶ Details: API Request claude-3-opus [Copy] │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||||
|
│ │ Model: claude-3-opus-20240229 ││
|
||||||
|
│ │ Input: 12,456 tokens ││
|
||||||
|
│ │ Output: 2,341 tokens ││
|
||||||
|
│ │ Cost: $0.42 ││
|
||||||
|
│ │ Latency: 1,234ms (first token: 234ms) ││
|
||||||
|
│ │ System Prompt: 2,100 tokens ││
|
||||||
|
│ │ Context: Session abc123, Turn 5 ││
|
||||||
|
│ └─────────────────────────────────────────────────────────────┘│
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event-Typen (Farbcodiert)
|
||||||
|
|
||||||
|
| Farbe | Typ | Was wird geloggt |
|
||||||
|
|-------|-----|------------------|
|
||||||
|
| 🔵 Blau | **API** | Requests/Responses zu Claude API |
|
||||||
|
| 🟢 Grün | **HOOK** | Hook-Ausführungen (SessionStart, PreTool, etc.) |
|
||||||
|
| 🟡 Gelb | **TOOL** | Tool-Aufrufe (Read, Grep, Edit, Bash) |
|
||||||
|
| 🟣 Lila | **MCP** | MCP-Server Kommunikation |
|
||||||
|
| 🟠 Orange | **AGENT** | Subagent Start/Stop/Delegation |
|
||||||
|
| 🔴 Rot | **ERROR** | Fehler, Timeouts, Retries |
|
||||||
|
| ⚪ Grau | **DEBUG** | Interne Events (optional) |
|
||||||
|
|
||||||
|
### Zwei Ansichten
|
||||||
|
|
||||||
|
#### 1. Kompakt-Ansicht (Standard)
|
||||||
|
Eine Zeile pro Event:
|
||||||
|
```
|
||||||
|
14:32:01 🔵 API → claude-3-opus [1.2s] 2.4k tok
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Detail-Ansicht (Klick auf Event)
|
||||||
|
Vollständige Informationen:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "api_request",
|
||||||
|
"timestamp": "2026-04-14T14:32:01.234Z",
|
||||||
|
"model": "claude-3-opus-20240229",
|
||||||
|
"input_tokens": 12456,
|
||||||
|
"output_tokens": 2341,
|
||||||
|
"latency_ms": 1234,
|
||||||
|
"first_token_ms": 234,
|
||||||
|
"cost_usd": 0.42,
|
||||||
|
"session_id": "abc123",
|
||||||
|
"turn": 5,
|
||||||
|
"tools_available": ["Read", "Edit", "Bash", ...],
|
||||||
|
"system_prompt_tokens": 2100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Was wird geloggt?
|
||||||
|
|
||||||
|
#### API-Events
|
||||||
|
- Request gesendet (Modell, Token-Schätzung)
|
||||||
|
- Streaming gestartet
|
||||||
|
- Response komplett (Token, Kosten, Latenz)
|
||||||
|
- Errors (Rate Limit, Timeout, etc.)
|
||||||
|
|
||||||
|
#### Hook-Events
|
||||||
|
- Hook Name + Zeitpunkt
|
||||||
|
- Payload (gekürzt)
|
||||||
|
- Ergebnis (injizierte Hints, blockiert, etc.)
|
||||||
|
- Dauer
|
||||||
|
|
||||||
|
#### Tool-Events
|
||||||
|
- Tool-Name + Parameter (gekürzt)
|
||||||
|
- Ergebnis-Zusammenfassung
|
||||||
|
- Dauer
|
||||||
|
- Fehler wenn vorhanden
|
||||||
|
|
||||||
|
#### MCP-Events
|
||||||
|
- Server-Name
|
||||||
|
- Tool/Resource aufgerufen
|
||||||
|
- Parameter + Ergebnis
|
||||||
|
- Verbindungsstatus
|
||||||
|
|
||||||
|
#### Agent-Events
|
||||||
|
- Agent gestartet (ID, Typ, Parent)
|
||||||
|
- Agent beendet (Tokens verbraucht)
|
||||||
|
- Delegation (von → an)
|
||||||
|
|
||||||
|
### Aufgaben
|
||||||
|
|
||||||
|
- [ ] **Event-System im Backend**
|
||||||
|
- [ ] `src-tauri/src/monitor.rs` (NEU)
|
||||||
|
- [ ] `EventBus` Struct mit Ringbuffer (max 10.000 Events)
|
||||||
|
- [ ] `emit_event(type, data)` — Thread-safe
|
||||||
|
- [ ] Events an Frontend via Tauri Events
|
||||||
|
- [ ] Persistierung optional (SQLite)
|
||||||
|
|
||||||
|
- [ ] **API-Interceptor**
|
||||||
|
- [ ] `scripts/claude-bridge.js` erweitern
|
||||||
|
- [ ] Vor/Nach jedem `query()` Call loggen
|
||||||
|
- [ ] Token zählen, Kosten berechnen
|
||||||
|
- [ ] Latenz messen (first token, total)
|
||||||
|
|
||||||
|
- [ ] **Hook-Logger**
|
||||||
|
- [ ] Bei jedem Hook-Fire: Event emittieren
|
||||||
|
- [ ] Payload (gekürzt auf 500 Zeichen)
|
||||||
|
- [ ] Ergebnis + Dauer
|
||||||
|
|
||||||
|
- [ ] **Tool-Logger**
|
||||||
|
- [ ] Wrapper um jedes Tool
|
||||||
|
- [ ] Parameter loggen (sensitive Daten maskieren)
|
||||||
|
- [ ] Ergebnis-Zusammenfassung
|
||||||
|
- [ ] Fehler + Stack Trace
|
||||||
|
|
||||||
|
- [ ] **UI: Monitor-Panel**
|
||||||
|
- [ ] `src/lib/components/MonitorPanel.svelte` (NEU)
|
||||||
|
- [ ] Virtualisierte Liste (Performance bei vielen Events)
|
||||||
|
- [ ] Farbcodierung nach Event-Typ
|
||||||
|
- [ ] Filter-Dropdown (nur API, nur Errors, etc.)
|
||||||
|
- [ ] Suchfeld
|
||||||
|
- [ ] Auto-Scroll (abschaltbar)
|
||||||
|
- [ ] Detail-Ansicht bei Klick
|
||||||
|
- [ ] Copy-Button für Details
|
||||||
|
|
||||||
|
- [ ] **Log-Export**
|
||||||
|
- [ ] Als JSON exportieren
|
||||||
|
- [ ] Als Text (lesbar) exportieren
|
||||||
|
- [ ] Zeitraum wählbar
|
||||||
|
|
||||||
|
- [ ] **Performance-Metriken**
|
||||||
|
- [ ] Sidebar-Widget: Requests/min, Avg Latency
|
||||||
|
- [ ] Token-Verbrauch Grafik (letzte Stunde)
|
||||||
|
- [ ] Kosten-Tracker (Session, Tag, Monat)
|
||||||
|
|
||||||
|
### Sensitive Daten maskieren!
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function maskSensitive(data: string): string {
|
||||||
|
return data
|
||||||
|
.replace(/password[=:]\s*\S+/gi, 'password=***')
|
||||||
|
.replace(/api[_-]?key[=:]\s*\S+/gi, 'api_key=***')
|
||||||
|
.replace(/bearer\s+\S+/gi, 'Bearer ***')
|
||||||
|
.replace(/sk-[a-zA-Z0-9]+/g, 'sk-***');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Datenstruktur
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface MonitorEvent {
|
||||||
|
id: string;
|
||||||
|
timestamp: Date;
|
||||||
|
type: 'api' | 'hook' | 'tool' | 'mcp' | 'agent' | 'error' | 'debug';
|
||||||
|
|
||||||
|
// Kompakt-Ansicht
|
||||||
|
summary: string; // "→ claude-3-opus [1.2s] 2.4k tok"
|
||||||
|
|
||||||
|
// Detail-Ansicht
|
||||||
|
details: {
|
||||||
|
// Je nach Typ unterschiedlich
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Metadaten
|
||||||
|
session_id?: string;
|
||||||
|
agent_id?: string;
|
||||||
|
duration_ms?: number;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQLite-Schema (optional für Persistierung)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE monitor_events (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
timestamp TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
summary TEXT NOT NULL,
|
||||||
|
details JSON,
|
||||||
|
session_id TEXT,
|
||||||
|
agent_id TEXT,
|
||||||
|
duration_ms INTEGER,
|
||||||
|
error TEXT,
|
||||||
|
|
||||||
|
INDEX idx_timestamp (timestamp),
|
||||||
|
INDEX idx_type (type),
|
||||||
|
INDEX idx_session (session_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Automatisch alte Events löschen (> 7 Tage)
|
||||||
|
CREATE TRIGGER cleanup_old_events
|
||||||
|
AFTER INSERT ON monitor_events
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM monitor_events
|
||||||
|
WHERE timestamp < datetime('now', '-7 days');
|
||||||
|
END;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
```bash
|
||||||
|
# Test: Monitor-Panel öffnen
|
||||||
|
# Nachricht senden → Events erscheinen live
|
||||||
|
# 🔵 API Request/Response sichtbar
|
||||||
|
# 🟡 Tool-Aufrufe sichtbar
|
||||||
|
# Auf Event klicken → Details erscheinen
|
||||||
|
# Filter auf "ERROR" → nur Fehler sichtbar
|
||||||
|
# Export → JSON-Datei mit allen Events
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nicht geplant / Zukunft
|
||||||
- [ ] MCP-Server Integration in App
|
- [ ] MCP-Server Integration in App
|
||||||
- [ ] Plugin-System
|
- [ ] Plugin-System
|
||||||
- [ ] Multi-User / Team-Features
|
- [ ] Multi-User / Team-Features
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ let activeAbort = null;
|
||||||
let currentAgentId = null;
|
let currentAgentId = null;
|
||||||
let currentModel = process.env.CLAUDE_MODEL || 'opus';
|
let currentModel = process.env.CLAUDE_MODEL || 'opus';
|
||||||
|
|
||||||
|
// Subagent-Tracking
|
||||||
|
// Map: toolUseId → { agentId, parentId, type, task, depth }
|
||||||
|
const activeSubagents = new Map();
|
||||||
|
|
||||||
// Verfügbare Modelle
|
// Verfügbare Modelle
|
||||||
const AVAILABLE_MODELS = [
|
const AVAILABLE_MODELS = [
|
||||||
{ id: 'haiku', name: 'Claude Haiku', description: 'Schnell & günstig' },
|
{ id: 'haiku', name: 'Claude Haiku', description: 'Schnell & günstig' },
|
||||||
|
|
@ -26,6 +30,26 @@ const AVAILABLE_MODELS = [
|
||||||
{ id: 'opus', name: 'Claude Opus', description: 'Leistungsstark' },
|
{ id: 'opus', name: 'Claude Opus', description: 'Leistungsstark' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Tools die Subagents spawnen
|
||||||
|
const SUBAGENT_TOOLS = ['Task', 'Agent', 'spawn_agent', 'launch_agent'];
|
||||||
|
|
||||||
|
// Subagent-Typ aus Tool-Input ermitteln
|
||||||
|
function getSubagentType(toolName, input) {
|
||||||
|
if (input?.subagent_type) return input.subagent_type.toLowerCase();
|
||||||
|
if (input?.agent_type) return input.agent_type.toLowerCase();
|
||||||
|
|
||||||
|
// Fallback basierend auf description/prompt
|
||||||
|
const desc = (input?.description || input?.prompt || '').toLowerCase();
|
||||||
|
if (desc.includes('explore') || desc.includes('search') || desc.includes('find')) return 'explore';
|
||||||
|
if (desc.includes('plan') || desc.includes('design')) return 'plan';
|
||||||
|
if (desc.includes('bash') || desc.includes('command') || desc.includes('terminal')) return 'bash';
|
||||||
|
if (desc.includes('code') || desc.includes('implement') || desc.includes('write')) return 'code';
|
||||||
|
if (desc.includes('test') || desc.includes('verify')) return 'test';
|
||||||
|
if (desc.includes('review') || desc.includes('check')) return 'review';
|
||||||
|
|
||||||
|
return 'explore'; // Default
|
||||||
|
}
|
||||||
|
|
||||||
// ============ Kommunikation mit Tauri ============
|
// ============ Kommunikation mit Tauri ============
|
||||||
|
|
||||||
function sendToTauri(msg) {
|
function sendToTauri(msg) {
|
||||||
|
|
@ -93,20 +117,73 @@ async function sendMessage(message, requestId, model = null) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'tool_use':
|
case 'tool_use': {
|
||||||
sendEvent('tool-start', {
|
const toolId = event.tool_use_id || randomUUID();
|
||||||
id: event.tool_use_id || randomUUID(),
|
const toolName = event.name || 'unknown';
|
||||||
tool: event.name || 'unknown',
|
const toolInput = event.input || {};
|
||||||
input: event.input || {},
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'tool_result':
|
// Prüfen ob dieses Tool einen Subagent startet
|
||||||
sendEvent('tool-end', {
|
if (SUBAGENT_TOOLS.includes(toolName)) {
|
||||||
id: event.tool_use_id || '',
|
const subagentId = randomUUID();
|
||||||
success: !event.is_error,
|
const subagentType = getSubagentType(toolName, toolInput);
|
||||||
|
const subagentTask = toolInput.description || toolInput.prompt || toolInput.task || 'Subagent-Aufgabe';
|
||||||
|
const subagentModel = toolInput.model || useModel;
|
||||||
|
|
||||||
|
// Tiefe berechnen (Main = 0, erster Sub = 1, etc.)
|
||||||
|
// Für jetzt: immer depth 1 (direkter Subagent vom Main)
|
||||||
|
const depth = 1;
|
||||||
|
|
||||||
|
activeSubagents.set(toolId, {
|
||||||
|
agentId: subagentId,
|
||||||
|
parentId: currentAgentId,
|
||||||
|
type: subagentType,
|
||||||
|
task: subagentTask,
|
||||||
|
depth,
|
||||||
|
model: subagentModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
sendEvent('subagent-started', {
|
||||||
|
id: subagentId,
|
||||||
|
parentAgentId: currentAgentId,
|
||||||
|
type: subagentType,
|
||||||
|
task: subagentTask.substring(0, 100),
|
||||||
|
depth,
|
||||||
|
model: subagentModel,
|
||||||
|
toolUseId: toolId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEvent('tool-start', {
|
||||||
|
id: toolId,
|
||||||
|
tool: toolName,
|
||||||
|
input: toolInput,
|
||||||
|
agentId: currentAgentId,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'tool_result': {
|
||||||
|
const toolId = event.tool_use_id || '';
|
||||||
|
|
||||||
|
// Prüfen ob dieser Tool-Call ein Subagent war
|
||||||
|
if (activeSubagents.has(toolId)) {
|
||||||
|
const subagent = activeSubagents.get(toolId);
|
||||||
|
sendEvent('subagent-stopped', {
|
||||||
|
id: subagent.agentId,
|
||||||
|
parentAgentId: subagent.parentId,
|
||||||
|
success: !event.is_error,
|
||||||
|
toolUseId: toolId,
|
||||||
|
});
|
||||||
|
activeSubagents.delete(toolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEvent('tool-end', {
|
||||||
|
id: toolId,
|
||||||
|
success: !event.is_error,
|
||||||
|
agentId: currentAgentId,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'result':
|
case 'result':
|
||||||
// Endergebnis
|
// Endergebnis
|
||||||
|
|
@ -135,6 +212,17 @@ async function sendMessage(message, requestId, model = null) {
|
||||||
sendEvent('text', { text: `\n\n**Fehler:** ${err.message || err}` });
|
sendEvent('text', { text: `\n\n**Fehler:** ${err.message || err}` });
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
// Alle noch aktiven Subagents stoppen
|
||||||
|
for (const [toolId, subagent] of activeSubagents) {
|
||||||
|
sendEvent('subagent-stopped', {
|
||||||
|
id: subagent.agentId,
|
||||||
|
parentAgentId: subagent.parentId,
|
||||||
|
success: false, // Vorzeitig beendet
|
||||||
|
toolUseId: toolId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
activeSubagents.clear();
|
||||||
|
|
||||||
sendEvent('agent-stopped', { id: currentAgentId, code: 0 });
|
sendEvent('agent-stopped', { id: currentAgentId, code: 0 });
|
||||||
sendEvent('all-stopped');
|
sendEvent('all-stopped');
|
||||||
currentAgentId = null;
|
currentAgentId = null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { agents, selectedAgentId, agentCount } from '$lib/stores/app';
|
import { agents, selectedAgentId, agentCount, agentTree, type AgentTreeNode } from '$lib/stores/app';
|
||||||
import type { Agent } from '$lib/stores/app';
|
import type { Agent } from '$lib/stores/app';
|
||||||
|
|
||||||
// Status-Icons
|
// Status-Icons
|
||||||
|
|
@ -10,14 +10,40 @@
|
||||||
stopped: '🔴'
|
stopped: '🔴'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Typ-Namen
|
// Typ-Namen (erweitert für alle Agent-Typen)
|
||||||
const typeNames: Record<Agent['type'], string> = {
|
const typeNames: Record<Agent['type'], string> = {
|
||||||
main: 'Main Agent',
|
main: 'Main Agent',
|
||||||
explore: 'Explore',
|
explore: 'Explore',
|
||||||
plan: 'Plan',
|
plan: 'Plan',
|
||||||
bash: 'Bash'
|
bash: 'Bash',
|
||||||
|
code: 'Code',
|
||||||
|
test: 'Test',
|
||||||
|
review: 'Review'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Typ-Icons
|
||||||
|
const typeIcons: Record<Agent['type'], string> = {
|
||||||
|
main: '🤖',
|
||||||
|
explore: '🔍',
|
||||||
|
plan: '📋',
|
||||||
|
bash: '💻',
|
||||||
|
code: '✏️',
|
||||||
|
test: '🧪',
|
||||||
|
review: '👀'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collapsed State für Baumknoten
|
||||||
|
let collapsedNodes = new Set<string>();
|
||||||
|
|
||||||
|
function toggleCollapse(id: string) {
|
||||||
|
if (collapsedNodes.has(id)) {
|
||||||
|
collapsedNodes.delete(id);
|
||||||
|
} else {
|
||||||
|
collapsedNodes.add(id);
|
||||||
|
}
|
||||||
|
collapsedNodes = collapsedNodes; // Trigger reactivity
|
||||||
|
}
|
||||||
|
|
||||||
function selectAgent(id: string) {
|
function selectAgent(id: string) {
|
||||||
$selectedAgentId = $selectedAgentId === id ? null : id;
|
$selectedAgentId = $selectedAgentId === id ? null : id;
|
||||||
}
|
}
|
||||||
|
|
@ -31,13 +57,84 @@
|
||||||
}
|
}
|
||||||
return `${seconds}s`;
|
return `${seconds}s`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rekursives Rendern von Baum-Knoten
|
||||||
|
function hasChildren(node: AgentTreeNode): boolean {
|
||||||
|
return node.children.length > 0;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Rekursive Komponente für Baum-Knoten -->
|
||||||
|
{#snippet agentNode(node: AgentTreeNode, depth: number)}
|
||||||
|
{@const agent = node.agent}
|
||||||
|
{@const isCollapsed = collapsedNodes.has(agent.id)}
|
||||||
|
{@const hasKids = hasChildren(node)}
|
||||||
|
|
||||||
|
<div class="agent-node" style="--depth: {depth}">
|
||||||
|
<!-- Verbindungslinie -->
|
||||||
|
{#if depth > 0}
|
||||||
|
<div class="tree-line"></div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="agent-item"
|
||||||
|
class:selected={$selectedAgentId === agent.id}
|
||||||
|
class:active={agent.status === 'active'}
|
||||||
|
class:is-subagent={depth > 0}
|
||||||
|
on:click={() => selectAgent(agent.id)}
|
||||||
|
>
|
||||||
|
<!-- Collapse-Toggle wenn Kinder vorhanden -->
|
||||||
|
{#if hasKids}
|
||||||
|
<button
|
||||||
|
class="collapse-toggle"
|
||||||
|
on:click|stopPropagation={() => toggleCollapse(agent.id)}
|
||||||
|
title={isCollapsed ? 'Aufklappen' : 'Zuklappen'}
|
||||||
|
>
|
||||||
|
{isCollapsed ? '▶' : '▼'}
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<span class="collapse-spacer"></span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="agent-content">
|
||||||
|
<div class="agent-main">
|
||||||
|
<span class="agent-status">{statusIcons[agent.status]}</span>
|
||||||
|
<span class="agent-type-icon" title={typeNames[agent.type]}>{typeIcons[agent.type]}</span>
|
||||||
|
<span class="agent-type">{typeNames[agent.type]}</span>
|
||||||
|
{#if agent.model}
|
||||||
|
<span class="agent-model">{agent.model}</span>
|
||||||
|
{/if}
|
||||||
|
<span class="agent-duration">({formatDuration(agent.startedAt)})</span>
|
||||||
|
</div>
|
||||||
|
<div class="agent-task">{agent.task}</div>
|
||||||
|
<div class="agent-meta">
|
||||||
|
<span class="agent-tools">🔧 {agent.toolCalls.length}</span>
|
||||||
|
{#if depth > 0}
|
||||||
|
<span class="agent-depth">Ebene {depth}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Kinder rekursiv rendern -->
|
||||||
|
{#if hasKids && !isCollapsed}
|
||||||
|
<div class="agent-children">
|
||||||
|
{#each node.children as child}
|
||||||
|
{@render agentNode(child, depth + 1)}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
<div class="agent-view">
|
<div class="agent-view">
|
||||||
<div class="agent-header">
|
<div class="agent-header">
|
||||||
<h2>🤖 Agents & Sub-Agents</h2>
|
<h2>🤖 Agents</h2>
|
||||||
<div class="agent-summary">
|
<div class="agent-summary">
|
||||||
{$agentCount.total} gesamt | {$agentCount.active} aktiv
|
{$agentCount.total} gesamt |
|
||||||
|
{$agentCount.mainAgents} Main |
|
||||||
|
{$agentCount.subAgents} Sub |
|
||||||
|
{$agentCount.active} aktiv
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -47,24 +144,9 @@
|
||||||
<p class="hint">Agents erscheinen hier wenn Claude arbeitet.</p>
|
<p class="hint">Agents erscheinen hier wenn Claude arbeitet.</p>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="agent-list">
|
<div class="agent-tree">
|
||||||
{#each $agents as agent}
|
{#each $agentTree as rootNode}
|
||||||
<button
|
{@render agentNode(rootNode, 0)}
|
||||||
class="agent-item"
|
|
||||||
class:selected={$selectedAgentId === agent.id}
|
|
||||||
class:active={agent.status === 'active'}
|
|
||||||
on:click={() => selectAgent(agent.id)}
|
|
||||||
>
|
|
||||||
<div class="agent-main">
|
|
||||||
<span class="agent-status">{statusIcons[agent.status]}</span>
|
|
||||||
<span class="agent-type">{typeNames[agent.type]}</span>
|
|
||||||
<span class="agent-duration">({formatDuration(agent.startedAt)})</span>
|
|
||||||
</div>
|
|
||||||
<div class="agent-task">{agent.task}</div>
|
|
||||||
<div class="agent-tools">
|
|
||||||
Tools: {agent.toolCalls.length} Aufrufe
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -73,7 +155,8 @@
|
||||||
{@const selectedAgent = $agents.find((a) => a.id === $selectedAgentId)}
|
{@const selectedAgent = $agents.find((a) => a.id === $selectedAgentId)}
|
||||||
{#if selectedAgent}
|
{#if selectedAgent}
|
||||||
<div class="agent-details">
|
<div class="agent-details">
|
||||||
<h3>Details: {typeNames[selectedAgent.type]}</h3>
|
<h3>{typeIcons[selectedAgent.type]} {typeNames[selectedAgent.type]}</h3>
|
||||||
|
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label">Status:</span>
|
<span class="detail-label">Status:</span>
|
||||||
<span class="detail-value">{statusIcons[selectedAgent.status]} {selectedAgent.status}</span>
|
<span class="detail-value">{statusIcons[selectedAgent.status]} {selectedAgent.status}</span>
|
||||||
|
|
@ -82,6 +165,12 @@
|
||||||
<span class="detail-label">Aufgabe:</span>
|
<span class="detail-label">Aufgabe:</span>
|
||||||
<span class="detail-value">{selectedAgent.task}</span>
|
<span class="detail-value">{selectedAgent.task}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{#if selectedAgent.model}
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="detail-label">Modell:</span>
|
||||||
|
<span class="detail-value">{selectedAgent.model}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label">Gestartet:</span>
|
<span class="detail-label">Gestartet:</span>
|
||||||
<span class="detail-value">{selectedAgent.startedAt.toLocaleTimeString('de-DE')}</span>
|
<span class="detail-value">{selectedAgent.startedAt.toLocaleTimeString('de-DE')}</span>
|
||||||
|
|
@ -90,6 +179,18 @@
|
||||||
<span class="detail-label">Laufzeit:</span>
|
<span class="detail-label">Laufzeit:</span>
|
||||||
<span class="detail-value">{formatDuration(selectedAgent.startedAt)}</span>
|
<span class="detail-value">{formatDuration(selectedAgent.startedAt)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{#if selectedAgent.parentAgentId}
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="detail-label">Parent:</span>
|
||||||
|
<span class="detail-value parent-link" on:click={() => selectAgent(selectedAgent.parentAgentId!)}>
|
||||||
|
{selectedAgent.parentAgentId.substring(0, 8)}...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="detail-label">Tiefe:</span>
|
||||||
|
<span class="detail-value">{selectedAgent.depth}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h4>Tool-Aufrufe ({selectedAgent.toolCalls.length})</h4>
|
<h4>Tool-Aufrufe ({selectedAgent.toolCalls.length})</h4>
|
||||||
<div class="tool-list">
|
<div class="tool-list">
|
||||||
|
|
@ -150,13 +251,34 @@
|
||||||
margin-top: var(--spacing-sm);
|
margin-top: var(--spacing-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-list {
|
/* Baum-Ansicht */
|
||||||
|
.agent-tree {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: var(--spacing-sm);
|
padding: var(--spacing-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.agent-node {
|
||||||
|
position: relative;
|
||||||
|
margin-left: calc(var(--depth) * 1.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verbindungslinie für Subagents */
|
||||||
|
.tree-line {
|
||||||
|
position: absolute;
|
||||||
|
left: -1rem;
|
||||||
|
top: 0;
|
||||||
|
bottom: 50%;
|
||||||
|
width: 1rem;
|
||||||
|
border-left: 2px solid var(--bg-tertiary);
|
||||||
|
border-bottom: 2px solid var(--bg-tertiary);
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.agent-item {
|
.agent-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: var(--spacing-sm) var(--spacing-md);
|
padding: var(--spacing-sm) var(--spacing-md);
|
||||||
|
|
@ -180,15 +302,57 @@
|
||||||
border-left: 3px solid var(--success);
|
border-left: 3px solid var(--success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.agent-item.is-subagent {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-toggle {
|
||||||
|
padding: 2px 4px;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-toggle:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-spacer {
|
||||||
|
width: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.agent-main {
|
.agent-main {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-sm);
|
gap: var(--spacing-xs);
|
||||||
margin-bottom: var(--spacing-xs);
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-type-icon {
|
||||||
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-type {
|
.agent-type {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-model {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
padding: 1px 4px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-duration {
|
.agent-duration {
|
||||||
|
|
@ -202,14 +366,27 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-tools {
|
.agent-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
font-size: 0.625rem;
|
font-size: 0.625rem;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
margin-top: var(--spacing-xs);
|
margin-top: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.agent-depth {
|
||||||
|
padding: 1px 4px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-children {
|
||||||
|
margin-top: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
/* Detail-Ansicht */
|
/* Detail-Ansicht */
|
||||||
.agent-details {
|
.agent-details {
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
|
|
@ -242,6 +419,15 @@
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.parent-link {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.tool-list {
|
.tool-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,15 @@ import { writable, derived } from 'svelte/store';
|
||||||
// Typen
|
// Typen
|
||||||
export interface Agent {
|
export interface Agent {
|
||||||
id: string;
|
id: string;
|
||||||
type: 'main' | 'explore' | 'plan' | 'bash';
|
type: 'main' | 'explore' | 'plan' | 'bash' | 'code' | 'test' | 'review';
|
||||||
status: 'active' | 'waiting' | 'idle' | 'stopped';
|
status: 'active' | 'waiting' | 'idle' | 'stopped';
|
||||||
task: string;
|
task: string;
|
||||||
startedAt: Date;
|
startedAt: Date;
|
||||||
toolCalls: ToolCall[];
|
toolCalls: ToolCall[];
|
||||||
|
// Subagent-Hierarchie
|
||||||
|
parentAgentId?: string; // undefined = Main Agent
|
||||||
|
depth: number; // 0 = Main, 1 = direkter Subagent, etc.
|
||||||
|
model?: string; // Welches Modell nutzt dieser Agent
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolCall {
|
export interface ToolCall {
|
||||||
|
|
@ -74,9 +78,34 @@ export const agentCount = derived(agents, ($agents) => ({
|
||||||
total: $agents.length,
|
total: $agents.length,
|
||||||
active: $agents.filter((a) => a.status === 'active').length,
|
active: $agents.filter((a) => a.status === 'active').length,
|
||||||
waiting: $agents.filter((a) => a.status === 'waiting').length,
|
waiting: $agents.filter((a) => a.status === 'waiting').length,
|
||||||
idle: $agents.filter((a) => a.status === 'idle').length
|
idle: $agents.filter((a) => a.status === 'idle').length,
|
||||||
|
mainAgents: $agents.filter((a) => !a.parentAgentId).length,
|
||||||
|
subAgents: $agents.filter((a) => a.parentAgentId).length,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Agent-Baum Typen und Builder (muss vor agentTree Store sein)
|
||||||
|
export interface AgentTreeNode {
|
||||||
|
agent: Agent;
|
||||||
|
children: AgentTreeNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildAgentTree(agentsList: Agent[]): AgentTreeNode[] {
|
||||||
|
// Nur Root-Agents (ohne Parent)
|
||||||
|
const roots = agentsList.filter((a) => !a.parentAgentId);
|
||||||
|
|
||||||
|
function buildNode(agent: Agent): AgentTreeNode {
|
||||||
|
const children = agentsList
|
||||||
|
.filter((a) => a.parentAgentId === agent.id)
|
||||||
|
.map(buildNode);
|
||||||
|
return { agent, children };
|
||||||
|
}
|
||||||
|
|
||||||
|
return roots.map(buildNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent-Baum als reaktiver Store
|
||||||
|
export const agentTree = derived(agents, ($agents) => buildAgentTree($agents));
|
||||||
|
|
||||||
// Aktionen
|
// Aktionen
|
||||||
export function addMessage(role: Message['role'], content: string, agentId?: string) {
|
export function addMessage(role: Message['role'], content: string, agentId?: string) {
|
||||||
messages.update((msgs) => [
|
messages.update((msgs) => [
|
||||||
|
|
@ -91,8 +120,27 @@ export function addMessage(role: Message['role'], content: string, agentId?: str
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addAgent(type: Agent['type'], task: string): string {
|
export interface AddAgentOptions {
|
||||||
const id = crypto.randomUUID();
|
id?: string;
|
||||||
|
parentAgentId?: string;
|
||||||
|
model?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addAgent(type: Agent['type'], task: string, options?: AddAgentOptions): string {
|
||||||
|
const id = options?.id || crypto.randomUUID();
|
||||||
|
const parentAgentId = options?.parentAgentId;
|
||||||
|
|
||||||
|
// Tiefe berechnen: Parent-Tiefe + 1 (oder 0 wenn kein Parent)
|
||||||
|
let depth = 0;
|
||||||
|
if (parentAgentId) {
|
||||||
|
agents.subscribe((ags) => {
|
||||||
|
const parent = ags.find((a) => a.id === parentAgentId);
|
||||||
|
if (parent) {
|
||||||
|
depth = parent.depth + 1;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
agents.update((ags) => [
|
agents.update((ags) => [
|
||||||
...ags,
|
...ags,
|
||||||
{
|
{
|
||||||
|
|
@ -101,12 +149,30 @@ export function addAgent(type: Agent['type'], task: string): string {
|
||||||
status: 'active',
|
status: 'active',
|
||||||
task,
|
task,
|
||||||
startedAt: new Date(),
|
startedAt: new Date(),
|
||||||
toolCalls: []
|
toolCalls: [],
|
||||||
|
parentAgentId,
|
||||||
|
depth,
|
||||||
|
model: options?.model,
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subagent hinzufügen (Kurzform)
|
||||||
|
export function addSubAgent(
|
||||||
|
parentId: string,
|
||||||
|
type: Agent['type'],
|
||||||
|
task: string,
|
||||||
|
options?: Omit<AddAgentOptions, 'parentAgentId'>
|
||||||
|
): string {
|
||||||
|
return addAgent(type, task, { ...options, parentAgentId: parentId });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Kinder eines Agents finden
|
||||||
|
export function getChildAgents(parentId: string, agentsList: Agent[]): Agent[] {
|
||||||
|
return agentsList.filter((a) => a.parentAgentId === parentId);
|
||||||
|
}
|
||||||
|
|
||||||
export function updateAgentStatus(id: string, status: Agent['status']) {
|
export function updateAgentStatus(id: string, status: Agent['status']) {
|
||||||
agents.update((ags) =>
|
agents.update((ags) =>
|
||||||
ags.map((a) => (a.id === id ? { ...a, status } : a))
|
ags.map((a) => (a.id === id ? { ...a, status } : a))
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
isProcessing,
|
isProcessing,
|
||||||
addMessage,
|
addMessage,
|
||||||
addAgent,
|
addAgent,
|
||||||
|
addSubAgent,
|
||||||
updateAgentStatus,
|
updateAgentStatus,
|
||||||
addToolCall,
|
addToolCall,
|
||||||
completeToolCall,
|
completeToolCall,
|
||||||
|
|
@ -19,7 +20,8 @@ import {
|
||||||
sessionStats,
|
sessionStats,
|
||||||
currentSessionId,
|
currentSessionId,
|
||||||
messageToDb,
|
messageToDb,
|
||||||
type Message
|
type Message,
|
||||||
|
type Agent
|
||||||
} from './app';
|
} from './app';
|
||||||
|
|
||||||
// Event-Typen vom Backend
|
// Event-Typen vom Backend
|
||||||
|
|
@ -28,6 +30,18 @@ interface AgentEvent {
|
||||||
type?: string;
|
type?: string;
|
||||||
task?: string;
|
task?: string;
|
||||||
code?: number;
|
code?: number;
|
||||||
|
model?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubagentEvent {
|
||||||
|
id: string;
|
||||||
|
parentAgentId: string;
|
||||||
|
type?: string;
|
||||||
|
task?: string;
|
||||||
|
depth?: number;
|
||||||
|
model?: string;
|
||||||
|
toolUseId?: string;
|
||||||
|
success?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolEvent {
|
interface ToolEvent {
|
||||||
|
|
@ -137,6 +151,30 @@ export async function initEventListeners(): Promise<void> {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Subagent gestartet
|
||||||
|
listeners.push(
|
||||||
|
await listen<SubagentEvent>('subagent-started', (event) => {
|
||||||
|
const { id, parentAgentId, type, task, depth, model } = event.payload;
|
||||||
|
console.log('🤖 Subagent gestartet:', id, type, '(Parent:', parentAgentId, ')');
|
||||||
|
|
||||||
|
addSubAgent(
|
||||||
|
parentAgentId,
|
||||||
|
mapAgentType(type || 'explore'),
|
||||||
|
task || 'Subagent-Aufgabe',
|
||||||
|
{ id, model }
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Subagent gestoppt
|
||||||
|
listeners.push(
|
||||||
|
await listen<SubagentEvent>('subagent-stopped', (event) => {
|
||||||
|
const { id, success } = event.payload;
|
||||||
|
console.log('⏹️ Subagent gestoppt:', id, success ? 'OK' : 'FEHLER');
|
||||||
|
updateAgentStatus(id, 'stopped');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Tool Start
|
// Tool Start
|
||||||
listeners.push(
|
listeners.push(
|
||||||
await listen<ToolEvent>('tool-start', (event) => {
|
await listen<ToolEvent>('tool-start', (event) => {
|
||||||
|
|
@ -241,17 +279,25 @@ export async function cleanupEventListeners(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agent-Typ mappen
|
// Agent-Typ mappen
|
||||||
function mapAgentType(type: string): 'main' | 'explore' | 'plan' | 'bash' {
|
function mapAgentType(type: string): Agent['type'] {
|
||||||
const typeMap: Record<string, 'main' | 'explore' | 'plan' | 'bash'> = {
|
const typeMap: Record<string, Agent['type']> = {
|
||||||
main: 'main',
|
main: 'main',
|
||||||
'Main Agent': 'main',
|
'Main Agent': 'main',
|
||||||
Main: 'main',
|
Main: 'main',
|
||||||
explore: 'explore',
|
explore: 'explore',
|
||||||
Explore: 'explore',
|
Explore: 'explore',
|
||||||
|
'general-purpose': 'explore',
|
||||||
plan: 'plan',
|
plan: 'plan',
|
||||||
Plan: 'plan',
|
Plan: 'plan',
|
||||||
bash: 'bash',
|
bash: 'bash',
|
||||||
Bash: 'bash'
|
Bash: 'bash',
|
||||||
|
code: 'code',
|
||||||
|
Code: 'code',
|
||||||
|
implement: 'code',
|
||||||
|
test: 'test',
|
||||||
|
Test: 'test',
|
||||||
|
review: 'review',
|
||||||
|
Review: 'review',
|
||||||
};
|
};
|
||||||
return typeMap[type] || 'main';
|
return typeMap[type] || 'explore';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue