chore: add Phase 1.5 refinement tasks DIS-151 to DIS-154 #58

Merged
dev merged 1 commit from chore/refinement-issues-151-154 into main 2026-04-10 09:19:59 +00:00
5 changed files with 309 additions and 2 deletions

View file

@ -13,7 +13,7 @@ Letzte Aktualisierung: 2026-04-10
| ID | Titel | Phase | Branch | Blockiert durch | | 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 | | 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 |
--- ---

72
tasks/DIS-151.md Normal file
View file

@ -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/<name>`) 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

82
tasks/DIS-152.md Normal file
View file

@ -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).

76
tasks/DIS-153.md Normal file
View file

@ -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 <id>` 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 <id>` 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.

75
tasks/DIS-154.md Normal file
View file

@ -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 `<workspace>/.disclaw-inbox/<timestamp>-<filename>` 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.