diff --git a/tasks/BOARD.md b/tasks/BOARD.md index efcec4f..a3bf71c 100644 --- a/tasks/BOARD.md +++ b/tasks/BOARD.md @@ -13,7 +13,7 @@ Letzte Aktualisierung: 2026-04-10 | ID | Titel | Phase | Branch | Blockiert durch | |----|-------|-------|--------|-----------------| -| — | | | | | +| [DIS-152](DIS-152.md) | Gemappte Projektpfade (host:virtual) | 1.5 | `refinement/mapped-project-paths` | DIS-151 empfohlen | --- @@ -22,7 +22,9 @@ Letzte Aktualisierung: 2026-04-10 | ID | Titel | Phase | Branch | Priorität | |----|-------|-------|--------|-----------| -| — | | | | | +| [DIS-151](DIS-151.md) | Channel-Kategorisierung + Lifecycle | 1.5 | `refinement/channel-categories` | p1 | +| [DIS-153](DIS-153.md) | Model-Auswahl pro Agent | 1.5 | `refinement/model-selection` | p2 | +| [DIS-154](DIS-154.md) | Discord Rich Features (Reactions, Dateien) | 1.5 | `refinement/discord-rich-features` | p1 | --- diff --git a/tasks/DIS-151.md b/tasks/DIS-151.md new file mode 100644 index 0000000..d1420ba --- /dev/null +++ b/tasks/DIS-151.md @@ -0,0 +1,72 @@ +--- +id: DIS-151 +status: ready +phase: 1.5 +priority: p1 +labels: [phase:1.5, type:feat, priority:p1] +branch: refinement/channel-categories +assignee: null +started: null +pr: null +merged: null +--- + +# DIS-151: Channel-Kategorisierung (aktiv / inaktiv / archiv) + Lifecycle-Management + +## Ziel +Discord-Channels werden beim Start von DisClaw automatisch in Kategorien eingeordnet. +Verwaiste Workspaces und Channels werden aufgeräumt. Live-Events halten den Zustand aktuell. + +## Kontext +Beim Betrieb entstehen Inkonsistenzen: Channels ohne Workspace, Workspaces ohne Channel, +oder Agents die aus der DB entfernt wurden aber noch einen Workspace haben. +→ `docs/ideas/known-issues.md` KI-002, KI-002.1 + +## Scope + +### Kategorien (werden beim ersten Start automatisch angelegt falls nicht vorhanden) +- **`🟢 Aktive Agents`** — Channel ist in DB, Workspace existiert unter `~/.disclaw/workspaces/` +- **`🔴 Inaktive Agents`** — Workspace existiert, aber kein Channel in Discord (oder Channel nicht in DB) +- **`🗄️ Archiv`** — Kein DB-Eintrag, kein Workspace — Channel existiert aber ist unbekannt + +### Startup-Reconciliation (`src/lifecycle/reconcile.ts`) +Beim Start einmalig ausführen, nach DB-Init und Bot-Login: + +1. **Alle Channels des Servers scannen** — welche sind in welcher Kategorie? +2. **DB-Einträge abgleichen** — für jeden Workspace-Eintrag in DB prüfen ob Channel noch existiert +3. **Workspace-Verzeichnisse scannen** — alle Verzeichnisse unter `workspaces_root` mit `agent.yaml` +4. **Einordnung**: + - DB-Eintrag + Workspace vorhanden + Channel existiert → **aktiv** (Kategorie + Channel-Name) + - Workspace vorhanden, aber kein DB-Eintrag / kein Channel → **inaktiv** (Kategorie verschieben oder Channel neu anlegen) + - Weder DB noch Workspace, aber Channel existiert → **archiv** (Channel in Archiv-Kategorie verschieben) + - Workspace existiert, kein Channel in aktiv/inaktiv/archiv → Workspace-Verzeichnis löschen (Cleanup) + +### Live-Event: `channelDelete` +- Wenn ein Agent-Channel gelöscht wird: Workspace-Verzeichnis (`~/.disclaw/workspaces/`) ebenfalls löschen, DB-Eintrag entfernen +- Wenn Management-Channel gelöscht wird: automatisch neu anlegen (KI-002.1) +- Kategorie-Channels (aktiv/inaktiv/archiv) werden bei Löschung neu angelegt + +### `src/bot.ts` / `src/index.ts` +- `reconcileChannels(guild, db, config)` nach Bot-Ready aufrufen +- `channelDelete`-Event-Handler registrieren + +## Definition of Done +- [ ] Kategorien werden beim Start automatisch angelegt (falls nicht vorhanden) +- [ ] Channels werden korrekt eingeordnet (aktiv/inaktiv/archiv) +- [ ] Workspace-Verzeichnisse ohne Channel werden beim Start gelöscht +- [ ] `channelDelete`-Event: Workspace + DB-Eintrag werden entfernt +- [ ] Management-Channel-Löschung: wird neu angelegt +- [ ] Unit-Test `tests/unit/reconcile.test.ts`: Mock-Guild, Mock-DB — alle Reconcile-Cases +- [ ] `npm run build && npm test` grün + +## Dateien (erwartet) +- `src/lifecycle/reconcile.ts` +- `src/bot.ts` +- `src/index.ts` +- `tests/unit/reconcile.test.ts` + +## Abhängigkeiten +Phase 0 + Phase 1 abgeschlossen. + +## Spec +→ `docs/ideas/known-issues.md` KI-002, KI-002.1 diff --git a/tasks/DIS-152.md b/tasks/DIS-152.md new file mode 100644 index 0000000..931def1 --- /dev/null +++ b/tasks/DIS-152.md @@ -0,0 +1,82 @@ +--- +id: DIS-152 +status: ready +phase: 1.5 +priority: p2 +labels: [phase:1.5, type:feat, priority:p2] +branch: refinement/mapped-project-paths +assignee: null +started: null +pr: null +merged: null +--- + +# DIS-152: Gemappte Projektpfade für Agents (host:virtual) + +## Ziel +Beim Erstellen eines Agents kann ein zusätzlicher Host-Pfad angegeben werden, +auf den der Agent zugreifen kann. Im Discord-Output erscheint immer nur der +konfigurierte virtuelle Pfad — nie der echte Host-Pfad. + +## Kontext +Agents sollen auf bestehende Projektverzeichnisse zugreifen können (z.B. ein +laufendes Codeprojekt) ohne dass der absolute Host-Pfad in Discord-Nachrichten +durchsickert. + +**Wichtiger Hinweis**: Es handelt sich um *keine* echte Isolation/Containerisierung. +Claude Code sieht den echten Pfad intern. Die Sanitisierung ersetzt den Host-Pfad +in allen Discord-Ausgaben durch den konfigurierten virtuellen Pfad. + +## Scope + +### `agent.yaml` Erweiterung +```yaml +name: my-agent +channel_id: "123456789" +role: Developer +model: claude-sonnet-4-6 +path_mappings: + - host: "C:/Code/side/testProject" + virtual: "~/testProject" +``` + +### `/new-agent`-Command Erweiterung +- Neuer optionaler Parameter `path` im Format `host:virtual` + (z.B. `C:\Code\side\testProject:~/testProject`) +- Wird in `agent.yaml` unter `path_mappings` gespeichert +- Validierung: Host-Pfad muss existieren + +### CLAUDE.md-Template Erweiterung +Beim Erstellen des Agents wird in die CLAUDE.md geschrieben: +``` +## Verfügbare Pfade +- ~/testProject → dein Projekt-Workspace (verwende immer diesen Pfad in Antworten) +Nenne niemals absolute Host-Pfade. Verwende ausschließlich die oben gelisteten virtuellen Pfade. +``` + +### `sanitizeForDiscord` Erweiterung (`src/runtime/sanitize.ts`) +- `path_mappings` aus `agent.yaml` werden beim Routing geladen +- Host-Pfad (beide Slash-Varianten) → virtueller Pfad ersetzen +- Reihenfolge: spezifischste Pfade zuerst (längste zuerst sortieren) + +### `src/router.ts` +- `sanitizeOpts` bekommt zusätzlich `pathMappings: Array<{host: string, virtual: string}>` + +## Definition of Done +- [ ] `agent.yaml` unterstützt `path_mappings`-Feld (Zod-Schema aktualisiert) +- [ ] `/new-agent` akzeptiert optionalen `path`-Parameter +- [ ] Host-Pfade erscheinen nicht in Discord-Ausgaben +- [ ] Agent-CLAUDE.md enthält Hinweis auf virtuelle Pfade +- [ ] Unit-Test: Sanitisierung ersetzt Host- durch virtuellen Pfad (beide Slash-Stile) +- [ ] `npm run build && npm test` grün + +## Dateien (erwartet) +- `src/agent/schema.ts` (path_mappings Feld) +- `src/agent/identity.ts` (CLAUDE.md-Template) +- `src/commands/new-agent.ts` (path-Parameter) +- `src/runtime/sanitize.ts` (pathMappings Support) +- `src/router.ts` (sanitizeOpts erweitern) +- `tests/unit/sanitize-for-discord.test.ts` (neue Cases) + +## Abhängigkeiten +DIS-151 empfohlen (aber kein harter Blocker). diff --git a/tasks/DIS-153.md b/tasks/DIS-153.md new file mode 100644 index 0000000..22a9bd1 --- /dev/null +++ b/tasks/DIS-153.md @@ -0,0 +1,76 @@ +--- +id: DIS-153 +status: ready +phase: 1.5 +priority: p2 +labels: [phase:1.5, type:feat, priority:p2] +branch: refinement/model-selection +assignee: null +started: null +pr: null +merged: null +--- + +# DIS-153: Model-Auswahl pro Agent + +## Ziel +Beim Erstellen eines Agents kann das Claude-Modell ausgewählt werden. +`/new-agent` zeigt ein Discord-Select-Menü mit verfügbaren Modellen. +Der Agent verwendet ausschließlich das konfigurierte Modell. + +## Kontext +Verschiedene Agents haben unterschiedliche Anforderungen: ein einfacher +Research-Agent kommt mit Haiku aus, ein Code-Agent braucht Sonnet oder Opus. + +## Scope + +### Verfügbare Modelle (hardcodierte Allowlist) +```typescript +export const ALLOWED_MODELS = [ + { id: "claude-opus-4-6", label: "Claude Opus 4.6 (leistungsstark, langsamer)" }, + { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 (Standard, empfohlen)" }, + { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5 (schnell, günstig)" }, +] as const; + +export type AllowedModel = typeof ALLOWED_MODELS[number]["id"]; +``` + +### `agent.yaml` Erweiterung +```yaml +name: my-agent +channel_id: "123456789" +role: Developer +model: claude-sonnet-4-6 # neu, default: claude-sonnet-4-6 +``` + +### `/new-agent`-Command (`src/commands/new-agent.ts`) +- Discord `StringSelectMenuBuilder` mit den 3 Modellen als Optionen +- Nach Auswahl: `model` in `agent.yaml` speichern +- Kein Freitext-Feld — nur Auswahl aus der Allowlist + +### `src/agent/runner.ts` +- `agent.yaml` lesen und `model`-Feld auslesen +- `--model ` zu den Claude-CLI-Args hinzufügen +- Fallback: wenn kein Model in `agent.yaml` → `claude-sonnet-4-6` + +### Zod-Schema (`src/agent/schema.ts`) +- `model: z.enum([...ALLOWED_MODELS.map(m => m.id)]).default("claude-sonnet-4-6")` + +## Definition of Done +- [ ] `agent.yaml` hat `model`-Feld (Zod-validiert, Allowlist) +- [ ] `/new-agent` zeigt Select-Menü (kein Freitext) +- [ ] Runner übergibt `--model ` an Claude CLI +- [ ] Ungültiges Modell in `agent.yaml` → klare Fehlermeldung beim Start +- [ ] Unit-Test: Runner-Args enthalten `--model` +- [ ] `npm run build && npm test` grün + +## Dateien (erwartet) +- `src/agent/schema.ts` +- `src/agent/identity.ts` +- `src/commands/new-agent.ts` +- `src/agent/runner.ts` +- `src/config/models.ts` (Allowlist) +- `tests/unit/runner-model.test.ts` + +## Abhängigkeiten +Keine harten. Kann parallel zu DIS-151 laufen. diff --git a/tasks/DIS-154.md b/tasks/DIS-154.md new file mode 100644 index 0000000..dcbb515 --- /dev/null +++ b/tasks/DIS-154.md @@ -0,0 +1,75 @@ +--- +id: DIS-154 +status: ready +phase: 1.5 +priority: p1 +labels: [phase:1.5, type:feat, priority:p1] +branch: refinement/discord-rich-features +assignee: null +started: null +pr: null +merged: null +--- + +# DIS-154: Discord Rich Features für Agents (Reactions, Embeds, Dateien) + +## Ziel +Agents nutzen Discord vollständiger: Status-Reactions, strukturierte Embeds für +lange Antworten, Datei-Uploads für Code/Outputs, und Datei-Empfang aus dem Channel. +Vorgezogen aus Phase 3 / SF-004. + +## Kontext +Aktuell senden Agents nur Plaintext. Discord bietet viel mehr: +→ `docs/ideas/future-features.md` SF-004 +→ `docs/development-plan.md` Phase 3 + +## Scope + +### 1. Status-Reactions (`src/router.ts`) +- `👀` sobald Nachricht in Queue eingereiht wird (sofort, nicht blockierend) +- `✅` nach erfolgreicher Antwort +- `❌` bei Fehler (timeout, cli-error) +- Alle `.catch(() => {})` — nie blockierend + +### 2. Typing-Refresh-Intervall +- Aktuell: 5s Intervall — auf **8s** erhöhen (Discord-Timeout ist 10s) +- Typing endet exakt wenn `runAgent` fertig ist (kein Overshoot) + +### 3. Lange Antworten als Datei-Attachment (`src/discord/send-response.ts`) +- Neue Funktion `sendResponse(channel, text, message)` ersetzt direktes `channel.send()` +- Wenn Text > **4000 Zeichen**: als `.md`-Attachment senden statt mehrerer Chunks +- Unter 4000 Zeichen: bisheriges Split-Verhalten (`splitForDiscord`) + +### 4. Eingehende Dateien verarbeiten (`src/discord/attachment-inbox.ts`) +- `messageCreate`-Handler prüft `message.attachments` +- Dateien werden in `/.disclaw-inbox/-` gespeichert +- Im Prompt-Kontext wird ein Hinweis auf die Datei eingefügt: + `[Datei verfügbar: .disclaw-inbox/1234-example.py]` +- TTL-Cleanup: Dateien älter als 24h werden beim nächsten Start gelöscht + +### 5. Input-Limit +- Nachrichten > **4000 Zeichen** → `⚠️`-Reaction + kurze Hinweis-Nachricht +- Agent wird nicht aufgerufen + +## Definition of Done +- [ ] Reactions `👀` / `✅` / `❌` werden korrekt gesetzt +- [ ] Typing-Intervall 8s, endet mit Agent-Antwort +- [ ] Antworten > 4000 Zeichen kommen als `.md`-Attachment +- [ ] Eingehende Dateien landen im `.disclaw-inbox/`-Verzeichnis +- [ ] Input > 4000 Zeichen → `⚠️` + Hinweis, kein Agent-Aufruf +- [ ] Unit-Tests: `send-response`, `attachment-inbox` +- [ ] `npm run build && npm test` grün + +## Dateien (erwartet) +- `src/discord/send-response.ts` +- `src/discord/attachment-inbox.ts` +- `src/router.ts` (Reactions, Typing, Input-Limit) +- `tests/unit/send-response.test.ts` +- `tests/unit/attachment-inbox.test.ts` + +## Abhängigkeiten +Phase 1 abgeschlossen (splitForDiscord, RunResult). + +## Hinweis +Reactions, Embeds, Threads und Polls (voller SF-004-Scope) können in Folge-Issues +ausgebaut werden. Dieses Issue implementiert den Kern-QoL-Teil.