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