From deaefb5cd3b2b1747cf80199bd67a84c4505573d Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 10 Apr 2026 23:14:34 +0000 Subject: [PATCH] feat: mejorar ProgressReporter con deteccion de pasos del pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El ProgressReporter ahora muestra mensajes legibles cuando detecta comandos conocidos del pipeline de creacion de agentes: - create-full.sh โ†’ "๐Ÿ“ฆ Creando agente: scaffold, build, register..." - health-check.sh โ†’ "๐Ÿฅ Verificando health check..." - notify-developer.sh โ†’ "๐Ÿ“จ Enviando bienvenida a developers..." - restart.sh / start.sh โ†’ "๐Ÿ”„ Reiniciando launcher..." - go build โ†’ "๐Ÿ”จ Compilando..." - go test โ†’ "๐Ÿงช Ejecutando tests..." - Edit/Write โ†’ "โœ๏ธ Editando: " - Read โ†’ "๐Ÿ“– Leyendo: " - Glob/Grep โ†’ "๐Ÿ” Buscando: " Incluye contador de pasos visible ("Paso N โ€” ") para que el usuario pueda seguir el progreso. Si no reconoce el comando, usa el formato generico anterior. Co-Authored-By: Claude Opus 4.6 (1M context) --- shell/effects/progress.go | 95 +++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/shell/effects/progress.go b/shell/effects/progress.go index b26031c..cbc4ab0 100644 --- a/shell/effects/progress.go +++ b/shell/effects/progress.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log/slog" + "strings" "sync" "time" @@ -15,16 +16,19 @@ import ( // emits streaming events (tool_use, text, result). // // It rate-limits edits to at most one per second to avoid flooding the -// homeserver. +// homeserver. When it recognises well-known pipeline commands (e.g. +// create-full.sh, health-check.sh) it shows a human-readable step name +// instead of the raw command. type ProgressReporter struct { sender MatrixSender roomID string logger *slog.Logger - mu sync.Mutex - eventID string // Matrix event ID of the progress message (empty until first send) - lastEdit time.Time // timestamp of last edit, for rate limiting + mu sync.Mutex + eventID string // Matrix event ID of the progress message (empty until first send) + lastEdit time.Time // timestamp of last edit, for rate limiting minInterval time.Duration + step int // visible step counter (incremented on each tool_use) } // NewProgressReporter creates a ProgressReporter that sends progress updates @@ -53,16 +57,12 @@ func (p *ProgressReporter) handleEvent(evt coretypes.StreamEvent) { switch evt.Kind { case coretypes.StreamToolUse: - // Show which tool is being used - input := evt.ToolInput - if len(input) > 60 { - input = input[:57] + "..." - } - if input != "" { - markdown = fmt.Sprintf("\U0001f527 *%s*: `%s`", evt.ToolName, input) - } else { - markdown = fmt.Sprintf("\U0001f527 *%s*", evt.ToolName) - } + p.mu.Lock() + p.step++ + step := p.step + p.mu.Unlock() + + markdown = formatToolEvent(step, evt.ToolName, evt.ToolInput) case coretypes.StreamResult: // Final result โ€” no need to update progress; the handler will send the actual reply @@ -86,6 +86,73 @@ func (p *ProgressReporter) handleEvent(evt coretypes.StreamEvent) { p.updateMessage(markdown) } +// pipelineHint describes a well-known command pattern and its human-readable label. +type pipelineHint struct { + substr string // substring to match in the tool input + emoji string + label string +} + +// pipelineHints maps well-known pipeline commands to friendly labels. +// Order matters: first match wins. +var pipelineHints = []pipelineHint{ + {"create-full.sh", "\U0001f4e6", "Creando agente: scaffold, build, register, E2EE, avatar..."}, + {"health-check.sh", "\U0001f3e5", "Verificando health check..."}, + {"notify-developer.sh", "\U0001f4e8", "Enviando bienvenida a developers..."}, + {"restart.sh", "\U0001f504", "Reiniciando launcher..."}, + {"start.sh", "\U0001f504", "Arrancando launcher..."}, + {"go build", "\U0001f528", "Compilando..."}, + {"go test", "\U0001f9ea", "Ejecutando tests..."}, +} + +// formatToolEvent returns a human-readable markdown line for a streaming tool event. +// If the tool/input matches a well-known pipeline pattern, a friendly label is shown; +// otherwise falls back to a generic format. +func formatToolEvent(step int, toolName, toolInput string) string { + prefix := fmt.Sprintf("**Paso %d** \u2014 ", step) + + // Check pipeline hints for Bash commands + if toolName == "Bash" { + for _, h := range pipelineHints { + if strings.Contains(toolInput, h.substr) { + return prefix + h.emoji + " " + h.label + } + } + // Generic Bash command + input := truncateInput(toolInput, 50) + return prefix + "\U0001f527 `" + input + "`" + } + + // File operation tools with agent path detection + if toolName == "Edit" || toolName == "Write" { + file := truncateInput(toolInput, 60) + return prefix + "\u270f\ufe0f Editando: `" + file + "`" + } + if toolName == "Read" { + file := truncateInput(toolInput, 60) + return prefix + "\U0001f4d6 Leyendo: `" + file + "`" + } + if toolName == "Glob" || toolName == "Grep" { + input := truncateInput(toolInput, 50) + return prefix + "\U0001f50d Buscando: `" + input + "`" + } + + // Generic fallback + if toolInput != "" { + input := truncateInput(toolInput, 50) + return prefix + "\U0001f527 *" + toolName + "*: `" + input + "`" + } + return prefix + "\U0001f527 *" + toolName + "*" +} + +// truncateInput shortens a string for display, appending "..." if truncated. +func truncateInput(s string, maxLen int) string { + if len(s) <= maxLen { + return s + } + return s[:maxLen-3] + "..." +} + // updateMessage sends or edits the progress message, respecting rate limits. func (p *ProgressReporter) updateMessage(markdown string) { p.mu.Lock()