chore: add Phase 1.5 refinement tasks DIS-151 to DIS-154 #58
5 changed files with 309 additions and 2 deletions
|
|
@ -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
72
tasks/DIS-151.md
Normal 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
82
tasks/DIS-152.md
Normal 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
76
tasks/DIS-153.md
Normal 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
75
tasks/DIS-154.md
Normal 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.
|
||||||
Loading…
Reference in a new issue