Files
kanban/CHAT_PLAN.md
2026-05-06 19:04:45 +02:00

149 lines
7.1 KiB
Markdown

# Plan: Chat lateral con tools sobre todo el kanban
## Objetivo
Panel de chat a la derecha (Mantine `AppShell.Aside`) que conversa con un LLM y manipula el kanban via tool-calling: crear/renombrar/mover/borrar columnas y tarjetas, consultar metricas (tiempo en columna, historial), explicar el board, etc.
## Arquitectura
```
+---------------------------+----------------+
| Board (DnD) | Chat Aside |
| | |
| Cols / Cards | msg msg msg |
| | [input...] |
+---------------------------+----------------+
| |
v v
/api/... /api/chat (SSE)
\ /
v v
kanban backend (Go)
|
+-- llamadas internas a las
mismas funciones del backend
(no HTTP loopback)
```
- **No HTTP loopback**: el endpoint `/api/chat` ejecuta los tools llamando directamente a `db.*` (mismas funciones que usan los handlers HTTP). Cero overhead, cero auth-loop.
- **SSE streaming**: respuestas + `tool_use` deltas streamed al frontend para UX viva.
- **State sharing**: el chat ve y modifica el mismo `operations.db` que la UI. Tras una mutacion via tool, el frontend hace `reload()` del board para reflejar cambios.
## Backend Go
### Nuevo paquete `apps/kanban/chat/`
| Archivo | Responsabilidad |
|---|---|
| `chat.go` | Endpoint `POST /api/chat` con SSE. Loop: send msgs+tools a Claude API, recibe `tool_use`, ejecuta, reinyecta `tool_result`, hasta `end_turn`. |
| `tools.go` | Catalogo de tools: nombre, JSON schema, dispatch a `db.*`. |
| `claude.go` | Cliente Claude API (HTTP directo, sin SDK Go porque no hay oficial). Usa `infra.HttpPostJSON` + streaming manual. |
### Tool catalog (mapeo 1:1 con la API REST + extras)
| Tool name | Input | Output | DB call |
|---|---|---|---|
| `list_board` | `{}` | `{columns,cards}` con `time_in_column_ms` | `ListColumns` + `ListCardsWithTime` |
| `create_column` | `{name:string}` | `Column` | `CreateColumn` |
| `rename_column` | `{id:string, name:string}` | `{}` | `UpdateColumn` |
| `delete_column` | `{id:string}` | `{}` | `DeleteColumn` |
| `reorder_columns` | `{ids:string[]}` | `{}` | `ReorderColumns` |
| `create_card` | `{column_id, requester?, title, description?}` | `Card` | `CreateCard` |
| `update_card` | `{id, requester?, title?, description?}` | `{}` | `UpdateCard` |
| `delete_card` | `{id}` | `{}` | `DeleteCard` |
| `move_card` | `{id, column_id, position?}` | `{}` | `MoveCard` (calcula `ordered_ids` si solo `position`) |
| `card_history` | `{id}` | `[{column_name,duration_ms,...}]` | `CardHistory` |
| `find_cards` | `{query?:string, column_id?:string, requester?:string}` | `Card[]` | filtro en memoria sobre `ListCardsWithTime` |
| `column_stats` | `{column_id}` | `{count, total_time_ms, avg_time_ms, oldest_card_id}` | derivado |
| `bulk_create_cards` | `{column_id, cards:[{requester?,title,description?}]}` | `Card[]` | loop `CreateCard` |
Tools puros (sin escritura) marcados `read_only` en metadata para mostrar badge "solo lectura" en UI.
### Configuracion
- Env vars: `ANTHROPIC_API_KEY` (obligatoria), `KANBAN_CHAT_MODEL` (default: `claude-sonnet-4-6`), `KANBAN_CHAT_SYSTEM` (opcional, override del system prompt).
- Sistema prompt incluido en el binario:
> Eres asistente del tablero kanban. Antes de modificar, llama `list_board` para ver el estado. Cuando el usuario nombre tarjetas o columnas, resuelve el `id` con `find_cards`/`list_board`. Confirma cambios destructivos antes de borrar.
- Coste: prompt-caching del system + tools schema (5 min TTL) → llamadas siguientes baratas.
### SSE protocol al frontend
```
event: text
data: "Voy a mover la tarjeta..."
event: tool_use
data: {"id":"toolu_01","name":"move_card","input":{...}}
event: tool_result
data: {"tool_use_id":"toolu_01","ok":true,"result":{...}}
event: text
data: "Hecho. Ahora esta en Doing."
event: done
data: {"stop_reason":"end_turn","usage":{"input_tokens":1234,"output_tokens":56}}
```
## Frontend
### Layout
`AppShell` con `aside={{ width: 360, breakpoint: "md" }}`. Toggle con icon en header (`IconMessageChatbot`) — colapsable para no robar espacio en monitores chicos.
### Componentes nuevos
| Componente | Funcion |
|---|---|
| `ChatPanel.tsx` | Lista de mensajes + input. Usa `EventSource` para SSE. |
| `ChatMessage.tsx` | Renderiza turn: texto markdown, tool_use cards (nombre+input pretty-printed), tool_result chips. |
| `useKanbanChat.ts` | Hook: estado de turnos, persistencia en `localStorage`, trigger de `onBoardChange` (reload) tras cada `tool_result` exitoso. |
### Markdown
`react-markdown` + `remark-gfm` para tablas/listas en respuestas del LLM.
### Botones rapidos en cada tarjeta/columna
`ActionIcon` "Preguntar al chat sobre esto" inyecta contexto:
> Sobre la tarjeta `{title}` (id `{id}`): ...
## Persistencia chat
Conversaciones por sesion en `localStorage` (clave `kanban_chat_v1`). NO se persiste en SQLite por ahora — es state efimero. Si en el futuro se quiere historico → tabla `chat_messages` en `operations.db`.
## Seguridad
- Tools de borrado (`delete_column`, `delete_card`) requieren confirmacion explicita en el system prompt. Si el LLM las invoca sin confirmar, el frontend abre modal de confirmacion antes de pasar el resultado al loop.
- `ANTHROPIC_API_KEY` solo en backend, NUNCA expuesta al frontend.
## Hitos (orden de ejecucion sugerido)
1. **Tools.go + tests**: catalogo + dispatch puro Go, sin LLM. Probar con curl manual.
2. **Endpoint `/api/chat` no streaming**: request → response sincrono. Validar Claude API + tool loop.
3. **Streaming SSE**.
4. **ChatPanel UI** + `useKanbanChat`.
5. **Toggle aside + layout responsive**.
6. **Confirmacion de tools destructivos**.
7. **Botones contextuales en cards/columns**.
## Funciones del registry a delegar a `fn-constructor` (registry-first)
Antes de codear el chat, crear estas primitivas reutilizables:
| ID | Lenguaje | Proposito |
|---|---|---|
| `claude_messages_call_go_infra` | Go | Wrapper sobre POST `https://api.anthropic.com/v1/messages` con tools, system, prompt-caching. Impure, error_type. |
| `claude_messages_stream_go_infra` | Go | Idem pero con SSE streaming, retorna canal de eventos. Impure. |
| `sse_writer_go_infra` | Go | Helper para escribir SSE eventos en `http.ResponseWriter` (set headers, flusher, format `event:/data:`). Pure factory. |
Estas tres son utiles en cualquier app que necesite chat o LLM tools — no se quedan en el kanban.
## Decisiones pendientes (preguntar al usuario antes de codear)
1. **Modelo**: `claude-opus-4-7` (mas capaz, mas caro) vs `claude-sonnet-4-6` (default razonable) vs `claude-haiku-4-5` (rapido, barato).
2. **Streaming**: empezar simple (request/response sincrono) o ir directo a SSE.
3. **Persistencia chat**: solo `localStorage` o tambien tabla en `operations.db` para historico cross-session.
4. **Confirmaciones destructivas**: bloqueo en frontend o solo via prompt? Si bloqueo, ¿qué define "destructivo"?
5. **Limite de turnos por sesion**: para evitar tool-loops infinitos, cap (ej. 20 iteraciones por mensaje del usuario).