From 416784b2fd532a9fd527f85d8040c91c130c5038 Mon Sep 17 00:00:00 2001 From: Ricardo Gonzalez Date: Fri, 29 May 2026 02:20:32 -0500 Subject: [PATCH] AiProjectChat: contexto de equipo + SP/sprint para que la IA asigne HUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - buildContext() ahora incluye team members del proyecto (nombres, roles, célula) desde storage per-project - HUs incluyen story_points (sp) y sprint para que la IA pueda balancear cargas de trabajo - Épicas incluyen assigned_name y story_points - System prompt instruye a la IA a: · Usar el equipo para asignar HUs · Detectar sobrecarga por sprint · Sugerir recursos transversales cuando aplique · Alertar sobre riesgo de cronograma --- src/components/AiProjectChat.vue | 65 ++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/src/components/AiProjectChat.vue b/src/components/AiProjectChat.vue index 2f3ea9b..e62574d 100644 --- a/src/components/AiProjectChat.vue +++ b/src/components/AiProjectChat.vue @@ -2,8 +2,10 @@ import { ref, computed } from 'vue' import { useI18n } from 'vue-i18n' import { chatWithAI } from '@/services/ai' +import { storage } from '@/services/storage' import { useSettingsStore, AVAILABLE_MODELS, PROVIDER_CONFIG, hasProviderApiKey, type AIProvider } from '@/stores/settings' import { useWorkItemsStore } from '@/stores/workitems' +import { useUsersStore } from '@/stores/users' import { getSessionsByProject, getProjectState } from '@/services/transcriptions-db' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' @@ -21,6 +23,7 @@ import { Send, Loader2, AlertCircle, Brain, Settings2, Check, ChevronDown } from const { t } = useI18n() const settings = useSettingsStore() const workItems = useWorkItemsStore() +const usersStore = useUsersStore() const props = defineProps({ projectId: { type: Number, required: true }, @@ -53,20 +56,55 @@ function switchModel(provider: AIProvider, modelId: string) { async function buildContext(): Promise { const ctx: Record = {} - // 1. HUs: todas, formato compacto (sin límite) + // 1. Team members asignados a este proyecto + const teamIds = storage.getJSON(`project_team_${props.projectId}`) + if (teamIds && teamIds.length > 0) { + const members = teamIds + .map(id => usersStore.users.find(u => u.id === id)) + .filter(Boolean) + .map(u => ({ + n: u!.full_name || u!.email, + r: u!.role || '—', + c: u!.cell || '—', + })) + ctx.team = { count: members.length, members } + } + + // 2. HUs: todas, con story points, sprint y estado const hus = workItems.userStories if (hus.length > 0) { const pending = hus.filter(h => !['done','completed','closed','finalizado'].includes(String(h.status ?? '').toLowerCase())) - ctx.hus = { total: hus.length, pending: pending.length, items: hus.map(h => ({ t: h._cleanTitle || h.title, s: h.status, p: h.priority })) } + ctx.hus = { + total: hus.length, + pending: pending.length, + items: hus.map(h => ({ + t: h._cleanTitle || h.title, + s: h.status, + p: h.priority, + sp: h.story_points ?? null, + sprint: h.sprint ?? null, + })), + } } - // 2. Sesiones: últimas 3 + // 3. Épicas con asignación + const epics = workItems.epics + if (epics.length > 0) { + ctx.epics = epics.map(e => ({ + n: e._cleanName || e.name || e.title, + s: e.status, + assigned: e.assigned_name || null, + sp: e.story_points ?? null, + })) + } + + // 4. Sesiones: últimas 3 const sessions = await getSessionsByProject(props.projectId) if (sessions.length > 0) { ctx.sessions = { total: sessions.length, recent: sessions.slice(-3).reverse().map(s => ({ d: s.date, t: s.title })) } } - // 3. Estado + // 5. Estado const state = await getProjectState(props.projectId) if (state?.summary) ctx.summary = state.summary.slice(0, 300) @@ -84,9 +122,22 @@ async function sendPrompt() { try { const context = await buildContext() - const systemPrompt = `Proyecto: ${props.projectName}. ${props.projectDescription || ''} Épicas: ${props.epicCount} HUs: ${props.huCount}. -Datos del proyecto (JSON): ${context || '{}'} -Respondé en el mismo idioma del usuario. Sé conciso. Si no sabés, decilo.` + const systemPrompt = `Eres un asistente de gestión de proyectos ágiles para el equipo de desarrollo. + +Proyecto: ${props.projectName}. ${props.projectDescription || ''} Épicas: ${props.epicCount} HUs: ${props.huCount}. + +Tienes acceso a estos datos del proyecto (JSON): +${context || '{}'} + +El campo "team" contiene los miembros del equipo asignados a este proyecto con su rol. Úsalo para responder quién trabaja en qué, sugerir asignaciones de HUs, y alertar sobre capacidad. + +Cada HU puede tener "sp" (story points) y "sprint". Usa esta información para: +- Recomendar asignaciones equilibradas según capacidad del equipo +- Detectar sobrecarga de trabajo en un sprint +- Sugerir recursos adicionales si una HU requiere expertise que no está en el equipo (célula transversal) +- Alertar cuando el cronograma está en riesgo por falta de capacidad + +Respondé en el mismo idioma del usuario. Sé conciso y práctico. Si algo no está en los datos, decilo.` const msgs = [{ role: 'user' as const, content: text }] const result = await chatWithAI(msgs, systemPrompt)