From f6b917afe1f7b9333f61943bb8a4c4206d8d139d Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 6 Mar 2026 20:55:57 +0800 Subject: [PATCH] Remove TUI support from start command The bubbletea-based TUI for agent request visualization was not practical and added significant binary size. This removes the TUI entirely: the --tui flag, the agent context TUI model/messages, and all SendTUI/GetTUIProgram call sites. Development-mode logging now always prints ANSI-colored output to stdout. Drops direct dependencies on charmbracelet/bubbletea and lipgloss. Made-with: Cursor --- agent/caller/orchestrator.go | 20 - agent/context/log.go | 135 +----- agent/context/tui.go | 799 ----------------------------------- agent/context/tui_msg.go | 90 ---- cmd/start.go | 38 -- go.mod | 16 +- go.sum | 29 -- 7 files changed, 5 insertions(+), 1122 deletions(-) delete mode 100644 agent/context/tui.go delete mode 100644 agent/context/tui_msg.go diff --git a/agent/caller/orchestrator.go b/agent/caller/orchestrator.go index fdf78655..f4b1ca54 100644 --- a/agent/caller/orchestrator.go +++ b/agent/caller/orchestrator.go @@ -277,26 +277,11 @@ func (o *Orchestrator) callAgentWithContext(ctx *agentContext.Context, req *Requ ) } - // Notify TUI of A2A call start (use parent requestID so it appears in parent panel) - parentRequestID := o.ctx.RequestID() - agentContext.SendTUI(agentContext.AgentEventMsg{ - RequestID: parentRequestID, - Event: agentContext.EventA2AStart, - Data: map[string]interface{}{"target": req.AgentID}, - }) - - // Execute the agent call with the provided context - // The agent.Stream method will use the context's Writer for output resp, err := agent.Stream(ctx, req.Messages, ctxOpts) if err != nil { if a2aNode != nil { a2aNode.Fail(err) } - agentContext.SendTUI(agentContext.AgentEventMsg{ - RequestID: parentRequestID, - Event: agentContext.EventA2ADone, - Data: map[string]interface{}{"target": req.AgentID, "error": err.Error()}, - }) return NewResult(req.AgentID, nil, fmt.Errorf("agent call failed: %w", err)) } @@ -306,11 +291,6 @@ func (o *Orchestrator) callAgentWithContext(ctx *agentContext.Context, req *Requ "status": "completed", }) } - agentContext.SendTUI(agentContext.AgentEventMsg{ - RequestID: parentRequestID, - Event: agentContext.EventA2ADone, - Data: map[string]interface{}{"target": req.AgentID}, - }) return NewResult(req.AgentID, resp, nil) } diff --git a/agent/context/log.go b/agent/context/log.go index 503fc3fe..af3ed978 100644 --- a/agent/context/log.go +++ b/agent/context/log.go @@ -192,11 +192,8 @@ func (l *RequestLogger) processEntry(entry LogEntry) { } } -// printDev sends to TUI if available, otherwise prints colored output to stdout +// printDev prints colored output to stdout in development mode func (l *RequestLogger) printDev(entry LogEntry) { - if GetTUIProgram() != nil { - return - } switch entry.Level { case LogLevelTrace: fmt.Printf("%s → %s%s\n", colorGray, entry.Message, colorReset) @@ -315,17 +312,6 @@ func (l *RequestLogger) Start() { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - ParentID: l.parentID, - AssistantID: l.currentAssistantID(), - Event: EventRequestStart, - }) - - if GetTUIProgram() != nil { - return - } - fmt.Println() fmt.Printf("%s%s%s\n", colorBoldCyan, strings.Repeat("═", 60), colorReset) fmt.Printf("%s AGENT REQUEST %s%s\n", colorBoldCyan, l.shortID, colorReset) @@ -357,21 +343,6 @@ func (l *RequestLogger) End(success bool, err error) { return } - data := map[string]interface{}{"duration": duration.Round(time.Millisecond)} - if err != nil { - data["error"] = err.Error() - } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - AssistantID: l.currentAssistantID(), - Event: EventRequestEnd, - Data: data, - }) - - if GetTUIProgram() != nil { - return - } - fmt.Printf("%s%s%s\n", colorCyan, strings.Repeat("─", 60), colorReset) if success { fmt.Printf("%s REQUEST %s COMPLETED%s\n", colorBoldGreen, l.shortID, colorReset) @@ -400,15 +371,6 @@ func (l *RequestLogger) Phase(name string) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventPhase, - Data: map[string]interface{}{"name": name, "elapsed": elapsed}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s > %s%s %s[+%v]%s\n", colorBoldBlue, name, colorReset, colorGray, elapsed, colorReset) } @@ -425,15 +387,6 @@ func (l *RequestLogger) PhaseComplete(name string) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventPhaseDone, - Data: map[string]interface{}{"name": name, "elapsed": elapsed}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s + %s%s %s[+%v]%s\n", colorGreen, name, colorReset, colorGray, elapsed, colorReset) } @@ -447,15 +400,6 @@ func (l *RequestLogger) PhaseSkip(name, reason string) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventPhaseSkip, - Data: map[string]interface{}{"name": name, "reason": reason}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s - %s (%s)%s\n", colorGray, name, reason, colorReset) } @@ -472,19 +416,6 @@ func (l *RequestLogger) LLMStart(connector, model string, messageCount int) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventLLMCall, - Data: map[string]interface{}{ - "connector": connector, - "model": model, - "messages": messageCount, - }, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s LLM Call%s %s[+%v]%s\n", colorBoldMagenta, colorReset, colorGray, elapsed, colorReset) fmt.Printf("%s Connector: %s%s%s\n", colorGray, colorWhite, connector, colorReset) if model != "" { @@ -511,19 +442,6 @@ func (l *RequestLogger) LLMComplete(tokens int, hasToolCalls bool) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventLLMDone, - Data: map[string]interface{}{ - "detail": fmt.Sprintf("%s [tokens:%d, %v]", status, tokens, elapsed), - "tokens": tokens, - "status": status, - }, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s + LLM Response (%s)%s", colorGreen, status, colorReset) if tokens > 0 { fmt.Printf(" %s[tokens: %d]%s", colorGray, tokens, colorReset) @@ -543,15 +461,6 @@ func (l *RequestLogger) ToolStart(toolName string) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventToolCall, - Data: map[string]interface{}{"name": toolName}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s Tool: %s%s\n", colorYellow, toolName, colorReset) } @@ -571,15 +480,6 @@ func (l *RequestLogger) ToolComplete(toolName string, success bool) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventToolDone, - Data: map[string]interface{}{"name": toolName, "success": success}, - }) - - if GetTUIProgram() != nil { - return - } if success { fmt.Printf("%s + %s completed%s\n", colorGreen, toolName, colorReset) } else { @@ -600,15 +500,6 @@ func (l *RequestLogger) HookStart(hookName string) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventHook, - Data: map[string]interface{}{"name": hookName}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s Hook: %s%s %s[+%v]%s\n", colorMagenta, hookName, colorReset, colorGray, elapsed, colorReset) } @@ -624,15 +515,6 @@ func (l *RequestLogger) HookComplete(hookName string) { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventHookDone, - Data: map[string]interface{}{"name": hookName}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s + %s done%s\n", colorGreen, hookName, colorReset) } @@ -644,7 +526,7 @@ func (l *RequestLogger) Cleanup(resource string) { kunlog.Trace("[AGENT] %s Cleanup: %s", l.shortID, resource) - if !config.IsDevelopment() || GetTUIProgram() != nil { + if !config.IsDevelopment() { return } fmt.Printf("%s + %s%s\n", colorGray, resource, colorReset) @@ -658,7 +540,7 @@ func (l *RequestLogger) HistoryLoad(count, maxSize int) { kunlog.Trace("[AGENT] %s History loaded: %d/%d messages", l.shortID, count, maxSize) - if !config.IsDevelopment() || GetTUIProgram() != nil { + if !config.IsDevelopment() { return } fmt.Printf("%s Loaded %d/%d history messages%s\n", colorGray, count, maxSize, colorReset) @@ -673,7 +555,7 @@ func (l *RequestLogger) HistoryOverlap(overlapCount int) { if overlapCount > 0 { kunlog.Trace("[AGENT] %s History overlap removed: %d messages", l.shortID, overlapCount) - if !config.IsDevelopment() || GetTUIProgram() != nil { + if !config.IsDevelopment() { return } fmt.Printf("%s Removed %d overlapping messages%s\n", colorYellow, overlapCount, colorReset) @@ -692,15 +574,6 @@ func (l *RequestLogger) Release() { return } - SendTUI(AgentEventMsg{ - RequestID: l.requestID, - Event: EventContextRelease, - Data: map[string]interface{}{"assistant": l.currentAssistantID()}, - }) - - if GetTUIProgram() != nil { - return - } fmt.Printf("%s RELEASE %s%s %s(%s)%s\n", colorBoldYellow, l.shortID, colorReset, colorGray, l.currentAssistantID(), colorReset) } diff --git a/agent/context/tui.go b/agent/context/tui.go deleted file mode 100644 index 5062bcc9..00000000 --- a/agent/context/tui.go +++ /dev/null @@ -1,799 +0,0 @@ -package context - -import ( - "fmt" - "strings" - "sync" - "time" - - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -var ( - tuiProgram *tea.Program - tuiProgramMu sync.RWMutex -) - -// SetTUIProgram sets the global TUI program (called from start.go after HTTP READY) -func SetTUIProgram(p *tea.Program) { - tuiProgramMu.Lock() - tuiProgram = p - tuiProgramMu.Unlock() -} - -// GetTUIProgram returns the global TUI program (nil if not in TUI mode) -func GetTUIProgram() *tea.Program { - tuiProgramMu.RLock() - defer tuiProgramMu.RUnlock() - return tuiProgram -} - -// SendTUI sends a message to the TUI program if available -func SendTUI(msg tea.Msg) { - if p := GetTUIProgram(); p != nil { - p.Send(msg) - } -} - -// TUILogWriter implements io.Writer to bridge gou DevWriter -> TUI AppLogMsg -type TUILogWriter struct { - Program *tea.Program -} - -func (w *TUILogWriter) Write(p []byte) (n int, err error) { - content := strings.TrimRight(string(p), "\n") - if content == "" { - return len(p), nil - } - w.Program.Send(AppLogMsg{Content: content}) - return len(p), nil -} - -// ─── Styles ─────────────────────────────────────────────────────────────────── - -var ( - boxRunning = lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("33")). - PaddingLeft(1).PaddingRight(1) - - boxDone = lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("240")). - PaddingLeft(1).PaddingRight(1) - - boxFailed = lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("31")). - PaddingLeft(1).PaddingRight(1) - - boxAppLog = lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("240")). - PaddingLeft(1).PaddingRight(1) - - sRunning = lipgloss.NewStyle().Foreground(lipgloss.Color("33")) - sDone = lipgloss.NewStyle().Foreground(lipgloss.Color("34")) - sFailed = lipgloss.NewStyle().Foreground(lipgloss.Color("31")) - sDim = lipgloss.NewStyle().Foreground(lipgloss.Color("240")) - sBold = lipgloss.NewStyle().Bold(true) - sYellow = lipgloss.NewStyle().Foreground(lipgloss.Color("33")) - sRed = lipgloss.NewStyle().Foreground(lipgloss.Color("31")) - sBlue = lipgloss.NewStyle().Foreground(lipgloss.Color("34")) - sMagenta = lipgloss.NewStyle().Foreground(lipgloss.Color("35")) - sTree = lipgloss.NewStyle().Foreground(lipgloss.Color("240")) -) - -// ─── Data ───────────────────────────────────────────────────────────────────── - -// RequestPanel represents a single top-level agent request -type RequestPanel struct { - RequestID string - ShortID string - AssistantID string - StartTime time.Time - EndTime time.Time // set when done/failed, freezes elapsed display - Status PanelStatus - Nodes []TreeNode - ParentID string - Collapsed bool - viewRow int // Y offset of the header line (for mouse click) -} - -// TreeNode represents a step within a request panel -type TreeNode struct { - Kind NodeKind - Label string - Status NodeStatus - Detail string - Children []*TreeNode - StartTime time.Time - EndTime time.Time - Collapsed bool -} - -// AgentTUIModel is the bubbletea Model for agent request visualization -type AgentTUIModel struct { - panels []*RequestPanel - panelIndex map[string]int // requestID -> index in panels (first registration wins) - appLogs []AppLogEntry - appLogExpand bool - appLogRow int // Y offset of app log header - cursor int - width int - height int - scrollOffset int - autoFollow bool // auto-scroll to bottom when new content arrives - mouseOn bool - quitting bool -} - -// NewAgentTUIModel creates a new TUI model -func NewAgentTUIModel() AgentTUIModel { - return AgentTUIModel{ - panels: []*RequestPanel{}, - panelIndex: map[string]int{}, - appLogs: []AppLogEntry{}, - width: 80, - height: 24, - autoFollow: true, - } -} - -func (m AgentTUIModel) Init() tea.Cmd { - return tickCmd() -} - -func tickCmd() tea.Cmd { - return tea.Tick(500*time.Millisecond, func(t time.Time) tea.Msg { - return TickMsg(t) - }) -} - -// ─── Update ─────────────────────────────────────────────────────────────────── - -func (m AgentTUIModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.width = msg.Width - m.height = msg.Height - return m, nil - - case tea.KeyMsg: - return m.handleKey(msg) - - case tea.MouseMsg: - return m.handleMouse(msg) - - case AgentEventMsg: - return m.handleAgentEvent(msg), nil - - case AppLogMsg: - m.appLogs = append(m.appLogs, AppLogEntry{ - Content: msg.Content, - Time: time.Now(), - }) - return m, nil - - case TickMsg: - return m, tickCmd() - } - return m, nil -} - -func (m AgentTUIModel) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - topPanels := m.topLevelPanels() - total := len(topPanels) + 1 // +1 for app log - viewH := m.viewHeight() - - switch msg.String() { - case "q", "ctrl+c": - m.quitting = true - return m, tea.Quit - - // Scrolling - case "j", "down": - m.scrollOffset++ - m.autoFollow = false - case "k", "up": - if m.scrollOffset > 0 { - m.scrollOffset-- - } - m.autoFollow = false - case "pgdown", "ctrl+d": - m.scrollOffset += viewH / 2 - m.autoFollow = false - case "pgup", "ctrl+u": - m.scrollOffset -= viewH / 2 - if m.scrollOffset < 0 { - m.scrollOffset = 0 - } - m.autoFollow = false - case "G", "end": - m.autoFollow = true - case "g", "home": - m.scrollOffset = 0 - m.autoFollow = false - - // Cursor navigation for panel selection (wraps around) - case "tab": - m.cursor = (m.cursor + 1) % total - m.scrollToCursor(topPanels) - case "shift+tab": - m.cursor = (m.cursor - 1 + total) % total - m.scrollToCursor(topPanels) - - case "enter", " ": - if m.cursor < len(topPanels) { - topPanels[m.cursor].Collapsed = !topPanels[m.cursor].Collapsed - } else { - m.appLogExpand = !m.appLogExpand - } - case "c": - m.appLogExpand = !m.appLogExpand - case "a": - for _, p := range m.panels { - p.Collapsed = false - } - m.appLogExpand = true - case "A": - for _, p := range m.panels { - p.Collapsed = true - } - m.appLogExpand = false - case "m": - m.mouseOn = !m.mouseOn - if m.mouseOn { - return m, tea.EnableMouseCellMotion - } - return m, tea.DisableMouse - } - return m, nil -} - -func (m AgentTUIModel) viewHeight() int { - h := m.height - 2 // reserve for status bar - if h < 4 { - h = 4 - } - return h -} - -func (m *AgentTUIModel) scrollToCursor(topPanels []*RequestPanel) { - targetRow := 0 - if m.cursor < len(topPanels) { - targetRow = topPanels[m.cursor].viewRow - } else { - targetRow = m.appLogRow - } - viewH := m.viewHeight() - if targetRow < m.scrollOffset { - m.scrollOffset = targetRow - } else if targetRow >= m.scrollOffset+viewH { - m.scrollOffset = targetRow - viewH + 3 - } -} - -func (m AgentTUIModel) handleMouse(msg tea.MouseMsg) (tea.Model, tea.Cmd) { - switch { - case msg.Button == tea.MouseButtonWheelUp: - m.scrollOffset -= 3 - if m.scrollOffset < 0 { - m.scrollOffset = 0 - } - m.autoFollow = false - return m, nil - case msg.Button == tea.MouseButtonWheelDown: - m.scrollOffset += 3 - m.autoFollow = false - return m, nil - } - - if msg.Button != tea.MouseButtonLeft || msg.Action != tea.MouseActionRelease { - return m, nil - } - y := msg.Y + m.scrollOffset - - // Check app log header - if y == m.appLogRow { - m.appLogExpand = !m.appLogExpand - return m, nil - } - - // Check panel headers - for _, p := range m.panels { - if p.ParentID != "" { - continue - } - if y == p.viewRow { - p.Collapsed = !p.Collapsed - return m, nil - } - } - return m, nil -} - -// ─── Agent Events ───────────────────────────────────────────────────────────── - -func (m *AgentTUIModel) handleAgentEvent(msg AgentEventMsg) tea.Model { - switch msg.Event { - case EventRequestStart: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - // Delegate sub-call: same requestID, different assistantID. - // Add as a tree node inside the existing panel instead of creating a new one. - p := m.panels[idx] - p.Nodes = append(p.Nodes, TreeNode{ - Kind: NodeA2A, - Label: msg.AssistantID, - Status: NodeRunning, - StartTime: time.Now(), - }) - return m - } - - panel := &RequestPanel{ - RequestID: msg.RequestID, - ShortID: shortID(msg.RequestID), - AssistantID: msg.AssistantID, - StartTime: time.Now(), - Status: PanelRunning, - ParentID: msg.ParentID, - } - m.panelIndex[msg.RequestID] = len(m.panels) - m.panels = append(m.panels, panel) - - case EventRequestEnd: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - p := m.panels[idx] - - // Only mark panel done if the ending assistantID matches the panel's original assistantID - // (delegate sub-calls End with a different assistantID, they update their tree node instead) - if msg.AssistantID == p.AssistantID || msg.AssistantID == "" { - if errVal, has := msg.Data["error"]; has && errVal != nil { - p.Status = PanelFailed - } else { - p.Status = PanelSuccess - } - p.EndTime = time.Now() - p.Collapsed = true - - // Finalize any still-running child nodes (e.g. hook interrupted mid-execution) - finalStatus := NodeDone - if p.Status == PanelFailed { - finalStatus = NodeFailed - } - for i := range p.Nodes { - if p.Nodes[i].Status == NodeRunning { - p.Nodes[i].Status = finalStatus - p.Nodes[i].EndTime = p.EndTime - } - } - } else { - // Delegate sub-call finished: mark its tree node as done - for i := len(p.Nodes) - 1; i >= 0; i-- { - if p.Nodes[i].Kind == NodeA2A && p.Nodes[i].Label == msg.AssistantID && p.Nodes[i].Status == NodeRunning { - p.Nodes[i].Status = NodeDone - p.Nodes[i].EndTime = time.Now() - break - } - } - } - } - - case EventLLMCall: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - m.panels[idx].Nodes = append(m.panels[idx].Nodes, TreeNode{ - Kind: NodeLLM, Label: "LLM", Status: NodeRunning, StartTime: time.Now(), - }) - } - - case EventLLMDone: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - p := m.panels[idx] - for i := len(p.Nodes) - 1; i >= 0; i-- { - if p.Nodes[i].Kind == NodeLLM && p.Nodes[i].Status == NodeRunning { - p.Nodes[i].Status = NodeDone - p.Nodes[i].EndTime = time.Now() - if d, has := msg.Data["detail"]; has { - p.Nodes[i].Detail = fmt.Sprintf("%v", d) - } - break - } - } - } - - case EventToolCall: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - name := dataStr(msg.Data, "name") - m.panels[idx].Nodes = append(m.panels[idx].Nodes, TreeNode{ - Kind: NodeTool, Label: name, Status: NodeRunning, StartTime: time.Now(), - }) - } - - case EventToolDone: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - name := dataStr(msg.Data, "name") - p := m.panels[idx] - for i := len(p.Nodes) - 1; i >= 0; i-- { - if p.Nodes[i].Kind == NodeTool && p.Nodes[i].Label == name && p.Nodes[i].Status == NodeRunning { - p.Nodes[i].Status = NodeDone - p.Nodes[i].EndTime = time.Now() - break - } - } - } - - case EventHook: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - name := dataStr(msg.Data, "name") - m.panels[idx].Nodes = append(m.panels[idx].Nodes, TreeNode{ - Kind: NodeHook, Label: name, Status: NodeRunning, StartTime: time.Now(), - }) - } - - case EventHookDone: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - p := m.panels[idx] - for i := len(p.Nodes) - 1; i >= 0; i-- { - if p.Nodes[i].Kind == NodeHook && p.Nodes[i].Status == NodeRunning { - p.Nodes[i].Status = NodeDone - p.Nodes[i].EndTime = time.Now() - break - } - } - } - - case EventA2AStart: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - target := dataStr(msg.Data, "target") - m.panels[idx].Nodes = append(m.panels[idx].Nodes, TreeNode{ - Kind: NodeA2A, Label: target, Status: NodeRunning, StartTime: time.Now(), - }) - } - - case EventA2ADone: - if idx, ok := m.panelIndex[msg.RequestID]; ok { - target := dataStr(msg.Data, "target") - p := m.panels[idx] - for i := len(p.Nodes) - 1; i >= 0; i-- { - if p.Nodes[i].Kind == NodeA2A && p.Nodes[i].Status == NodeRunning && (target == "" || p.Nodes[i].Label == target) { - p.Nodes[i].Status = NodeDone - p.Nodes[i].EndTime = time.Now() - break - } - } - } - } - return m -} - -// ─── View ───────────────────────────────────────────────────────────────────── - -func (m AgentTUIModel) View() string { - if m.quitting { - return "" - } - - boxW := m.width - 2 - if boxW < 40 { - boxW = 40 - } - - // Render full content - var sb strings.Builder - row := 0 - topIdx := 0 - - for _, panel := range m.panels { - if panel.ParentID != "" { - continue - } - selected := (topIdx == m.cursor) - rendered := m.renderPanelBox(panel, boxW, selected, &row) - sb.WriteString(rendered) - sb.WriteString("\n") - row++ - topIdx++ - } - - // App Log - m.appLogRow = row - sb.WriteString(m.renderAppLogBox(boxW, topIdx == m.cursor, &row)) - - fullContent := sb.String() - lines := strings.Split(fullContent, "\n") - totalLines := len(lines) - viewH := m.viewHeight() - - // Auto-follow: snap to bottom - if m.autoFollow { - m.scrollOffset = totalLines - viewH - } - - // Clamp scroll offset - maxScroll := totalLines - viewH - if maxScroll < 0 { - maxScroll = 0 - } - if m.scrollOffset > maxScroll { - m.scrollOffset = maxScroll - } - if m.scrollOffset < 0 { - m.scrollOffset = 0 - } - - // Slice visible lines - end := m.scrollOffset + viewH - if end > totalLines { - end = totalLines - } - visible := lines[m.scrollOffset:end] - - // Build output - var out strings.Builder - out.WriteString(strings.Join(visible, "\n")) - - // Status bar with scroll indicator - mouseLabel := "off" - if m.mouseOn { - mouseLabel = "on" - } - scrollInfo := "" - if totalLines > viewH { - pct := 100 - if maxScroll > 0 { - pct = m.scrollOffset * 100 / maxScroll - } - scrollInfo = fmt.Sprintf(" [%d%%]", pct) - } - followLabel := "" - if m.autoFollow { - followLabel = " AUTO" - } - hint := sDim.Render(fmt.Sprintf(" j/k:scroll tab:select space:toggle a/A:all G:bottom g:top m:mouse(%s)%s%s q:quit", - mouseLabel, scrollInfo, followLabel)) - out.WriteString("\n" + hint) - - return out.String() -} - -func (m AgentTUIModel) renderPanelBox(panel *RequestPanel, boxW int, selected bool, row *int) string { - // Record header row for mouse - panel.viewRow = *row - - elapsed := m.panelElapsed(panel) - icon, statusText, style := panelStatusDisplay(panel.Status, elapsed) - - // Title line - collapser := "▾" - if panel.Collapsed { - collapser = "▸" - } - cursor := " " - if selected { - cursor = "›" - } - title := fmt.Sprintf("%s %s %s %s %s", - sDim.Render(cursor), - sDim.Render(collapser), - sBold.Render(panel.ShortID), - panel.AssistantID, - style.Render(icon+" "+statusText), - ) - - if panel.Collapsed { - box := boxForStatus(panel.Status).Width(boxW) - result := box.Render(title) - *row += strings.Count(result, "\n") + 1 - return result - } - - // Build body - var body strings.Builder - body.WriteString(title + "\n") - - for _, node := range panel.Nodes { - body.WriteString(m.renderTreeNode(node, " ", false, panel)) - } - - // Render fork children (different requestID, parentID matches) - children := m.childPanels(panel.RequestID) - for i, child := range children { - isLast := (i == len(children)-1) - body.WriteString(m.renderChildSummary(child, " ", isLast)) - } - - box := boxForStatus(panel.Status).Width(boxW) - result := box.Render(body.String()) - *row += strings.Count(result, "\n") + 1 - return result -} - -func (m AgentTUIModel) renderTreeNode(node TreeNode, prefix string, isChild bool, panel *RequestPanel) string { - panelEnded := panel != nil && panel.Status != PanelRunning - displayNode := node - if panelEnded && displayNode.Status == NodeRunning { - displayNode.Status = NodeFailed - } - icon, statusText := nodeStatusDisplay(displayNode) - elapsed := m.nodeElapsed(node, panelEnded, panel.EndTime) - - label := "" - switch node.Kind { - case NodeHook: - label = sMagenta.Render("Hook: "+node.Label) + " " + statusText - case NodeLLM: - detail := "" - if node.Detail != "" { - detail = " " + sDim.Render("["+node.Detail+"]") - } - label = sBlue.Render("LLM") + " " + statusText + detail - case NodeTool: - label = sTree.Render("├ ") + sYellow.Render(node.Label) + " " + statusText - case NodeA2A: - label = sTree.Render("⤷ ") + sBold.Render(node.Label) + " " + statusText - case NodePhase: - label = node.Label + " " + statusText - default: - label = node.Label + " " + statusText - } - - _ = icon - line := prefix + label - if elapsed != "" { - line += " " + sDim.Render(elapsed) - } - return line + "\n" -} - -func (m AgentTUIModel) renderChildSummary(panel *RequestPanel, prefix string, isLast bool) string { - elapsed := m.panelElapsed(panel) - icon, statusText, style := panelStatusDisplay(panel.Status, elapsed) - - branch := sTree.Render("├─ ") - if isLast { - branch = sTree.Render("└─ ") - } - return fmt.Sprintf("%s%s%s %s %s\n", - prefix, branch, - sBold.Render(panel.ShortID+" "+panel.AssistantID), - style.Render(icon+" "+statusText), - sDim.Render(elapsed), - ) -} - -func (m AgentTUIModel) renderAppLogBox(boxW int, selected bool, row *int) string { - cursor := " " - if selected { - cursor = "›" - } - collapser := "▸" - if m.appLogExpand { - collapser = "▾" - } - - count := len(m.appLogs) - title := fmt.Sprintf("%s %s %s (%d)", - sDim.Render(cursor), - sDim.Render(collapser), - sBold.Render("App Output"), - count, - ) - - if !m.appLogExpand || count == 0 { - result := boxAppLog.Width(boxW).Render(title) - *row += strings.Count(result, "\n") + 1 - return result - } - - var body strings.Builder - body.WriteString(title + "\n") - - start := 0 - if count > 50 { - start = count - 50 - } - for _, entry := range m.appLogs[start:] { - body.WriteString(" " + entry.Content + "\n") - } - - result := boxAppLog.Width(boxW).Render(body.String()) - *row += strings.Count(result, "\n") + 1 - return result -} - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -func (m AgentTUIModel) topLevelPanels() []*RequestPanel { - var result []*RequestPanel - for _, p := range m.panels { - if p.ParentID == "" { - result = append(result, p) - } - } - return result -} - -func (m AgentTUIModel) childPanels(parentRequestID string) []*RequestPanel { - var result []*RequestPanel - for _, p := range m.panels { - if p.ParentID == parentRequestID { - result = append(result, p) - } - } - return result -} - -func (m AgentTUIModel) panelElapsed(p *RequestPanel) string { - if p.Status != PanelRunning && !p.EndTime.IsZero() { - return fmtDuration(p.EndTime.Sub(p.StartTime)) - } - return fmtDuration(time.Since(p.StartTime)) -} - -func (m AgentTUIModel) nodeElapsed(n TreeNode, panelEnded bool, panelEndTime time.Time) string { - if n.Status == NodeDone || n.Status == NodeFailed { - if !n.EndTime.IsZero() { - return fmtDuration(n.EndTime.Sub(n.StartTime)) - } - } - if n.Status == NodeRunning { - if panelEnded && !panelEndTime.IsZero() { - return fmtDuration(panelEndTime.Sub(n.StartTime)) - } - return fmtDuration(time.Since(n.StartTime)) - } - return "" -} - -func panelStatusDisplay(status PanelStatus, elapsed string) (icon string, text string, style lipgloss.Style) { - switch status { - case PanelRunning: - return "⟳", "running " + elapsed, sRunning - case PanelSuccess: - return "✓", "done " + elapsed, sDone - case PanelFailed: - return "✗", "failed " + elapsed, sFailed - } - return "", "", sDim -} - -func nodeStatusDisplay(n TreeNode) (icon string, text string) { - switch n.Status { - case NodePending: - return "…", sDim.Render("…") - case NodeRunning: - return "⟳", sRunning.Render("⟳") - case NodeDone: - return "✓", sDone.Render("✓") - case NodeFailed: - return "✗", sFailed.Render("✗") - } - return "", "" -} - -func boxForStatus(status PanelStatus) lipgloss.Style { - switch status { - case PanelRunning: - return boxRunning - case PanelFailed: - return boxFailed - default: - return boxDone - } -} - -func dataStr(data map[string]interface{}, key string) string { - if v, ok := data[key]; ok { - return fmt.Sprintf("%v", v) - } - return "" -} - -func fmtDuration(d time.Duration) string { - if d < time.Second { - return fmt.Sprintf("%dms", d.Milliseconds()) - } - return fmt.Sprintf("%.1fs", d.Seconds()) -} diff --git a/agent/context/tui_msg.go b/agent/context/tui_msg.go deleted file mode 100644 index f910df44..00000000 --- a/agent/context/tui_msg.go +++ /dev/null @@ -1,90 +0,0 @@ -package context - -import "time" - -// EventType represents the type of agent lifecycle event -type EventType int - -const ( - EventRequestStart EventType = iota - EventPhase - EventPhaseDone - EventPhaseSkip - EventLLMCall - EventLLMDone - EventToolCall - EventToolDone - EventHook - EventHookDone - EventA2AStart - EventA2ADone - EventRequestEnd - EventContextFork - EventContextRelease -) - -// AgentEventMsg is sent from RequestLogger to the TUI Program -type AgentEventMsg struct { - RequestID string - ParentID string - AssistantID string - Event EventType - Data map[string]interface{} -} - -// AppLogLevel represents the severity of application-side output -type AppLogLevel int - -const ( - AppLogLevelLog AppLogLevel = iota - AppLogLevelInfo - AppLogLevelWarn - AppLogLevelError - AppLogLevelException -) - -// AppLogMsg is sent from the DevWriter (gou layer) to the TUI Program -type AppLogMsg struct { - Level AppLogLevel - Content string -} - -// AppLogEntry stores a single application output entry -type AppLogEntry struct { - Level AppLogLevel - Content string - Time time.Time -} - -// PanelStatus represents the lifecycle state of a request panel -type PanelStatus int - -const ( - PanelRunning PanelStatus = iota - PanelSuccess - PanelFailed -) - -// NodeKind represents the type of a tree node within a request panel -type NodeKind int - -const ( - NodePhase NodeKind = iota - NodeLLM - NodeTool - NodeHook - NodeA2A -) - -// NodeStatus represents the state of a tree node -type NodeStatus int - -const ( - NodePending NodeStatus = iota - NodeRunning - NodeDone - NodeFailed -) - -// TickMsg triggers periodic UI refresh for elapsed time display -type TickMsg time.Time diff --git a/cmd/start.go b/cmd/start.go index 689e3fe4..fb86503e 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -8,14 +8,11 @@ import ( "strings" "syscall" - tea "github.com/charmbracelet/bubbletea" "github.com/fatih/color" - "github.com/mattn/go-isatty" "github.com/spf13/cobra" "github.com/yaoapp/gou/api" "github.com/yaoapp/gou/connector" "github.com/yaoapp/gou/fs" - "github.com/yaoapp/gou/helper" "github.com/yaoapp/gou/mcp" "github.com/yaoapp/gou/plugin" "github.com/yaoapp/gou/schedule" @@ -23,9 +20,7 @@ import ( "github.com/yaoapp/gou/store" "github.com/yaoapp/gou/task" "github.com/yaoapp/gou/websocket" - "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" - agentcontext "github.com/yaoapp/yao/agent/context" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" yaogrpc "github.com/yaoapp/yao/grpc" @@ -41,7 +36,6 @@ import ( var startDebug = false var startDisableWatching = false -var startTUI = false var startCmd = &cobra.Command{ Use: "start", @@ -293,7 +287,6 @@ var startCmd = &cobra.Command{ case http.READY: fmt.Println(color.GreenString(L("Server is up and running..."))) fmt.Println(color.GreenString("Ctrl+C to stop")) - initAgentTUI() break case http.CLOSED: @@ -654,38 +647,7 @@ func colorMehtod(method string) string { } } -// initAgentTUI initializes the TUI for agent request visualization in dev mode. -// Must be called after HTTP READY to avoid interfering with startup messages. -func initAgentTUI() { - if !config.IsDevelopment() { - return - } - - if !startTUI && os.Getenv("YAO_TUI") != "on" { - return - } - - if !isatty.IsTerminal(os.Stdout.Fd()) { - return - } - - model := agentcontext.NewAgentTUIModel() - p := tea.NewProgram(model, tea.WithoutSignalHandler()) - - agentcontext.SetTUIProgram(p) - tuiWriter := &agentcontext.TUILogWriter{Program: p} - helper.SetDevWriter(tuiWriter) - exception.SetWriter(tuiWriter) - - go func() { - if _, err := p.Run(); err != nil { - log.Error("TUI error: %s", err.Error()) - } - }() -} - func init() { startCmd.PersistentFlags().BoolVarP(&startDebug, "debug", "", false, L("Development mode")) startCmd.PersistentFlags().BoolVarP(&startDisableWatching, "disable-watching", "", false, L("Disable watching")) - startCmd.PersistentFlags().BoolVarP(&startTUI, "tui", "", false, L("Enable TUI for agent request visualization")) } diff --git a/go.mod b/go.mod index 8290777e..522c0406 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,6 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/bwmarrin/discordgo v0.29.0 github.com/caarlos0/env/v6 v6.10.1 - github.com/charmbracelet/bubbletea v1.3.10 - github.com/charmbracelet/lipgloss v1.1.0 github.com/dchest/captcha v1.1.0 github.com/docker/docker v28.5.2+incompatible github.com/docker/go-connections v0.5.0 @@ -34,7 +32,6 @@ require ( github.com/kaptinlin/jsonschema v0.6.1 github.com/larksuite/oapi-sdk-go/v3 v3.5.3 github.com/matoous/go-nanoid/v2 v2.1.0 - github.com/mattn/go-isatty v0.0.20 github.com/mozillazg/go-pinyin v0.20.0 github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.1 github.com/pierrec/lz4/v4 v4.1.25 @@ -79,17 +76,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect github.com/aws/smithy-go v1.22.3 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect - github.com/charmbracelet/x/ansi v0.10.1 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/coder/websocket v1.8.14 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -101,7 +93,6 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect @@ -153,11 +144,10 @@ require ( github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mark3labs/mcp-go v0.32.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v1.14.28 // indirect github.com/miekg/dns v1.1.66 // indirect @@ -167,9 +157,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/montanaflynn/stats v0.7.1 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/neo4j/neo4j-go-driver/v5 v5.28.1 // indirect @@ -211,7 +198,6 @@ require ( github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xuri/efp v0.0.1 // indirect github.com/xuri/nfp v0.0.1 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect diff --git a/go.sum b/go.sum index 300d01e9..09ce1856 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,6 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.79.3 h1:BRXS0U76Z8wfF+bnkilA2QwpIch6U github.com/aws/aws-sdk-go-v2/service/s3 v1.79.3/go.mod h1:bNXKFFyaiVvWuR6O16h/I1724+aXe/tAkA9/QS01t5k= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -70,18 +68,6 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= -github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= -github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= @@ -123,8 +109,6 @@ github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTe github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/evanw/esbuild v0.25.4 h1:k1bTSim+usBG27w7BfOCorhgx3tO+6bAfMj5pR+6SKg= github.com/evanw/esbuild v0.25.4/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8= @@ -304,8 +288,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= -github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8= @@ -320,8 +302,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= @@ -351,12 +331,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ= github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= @@ -488,8 +462,6 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/excelize/v2 v2.9.1 h1:VdSGk+rraGmgLHGFaGG9/9IWu1nj4ufjJ7uwMDtj8Qw= @@ -612,7 +584,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=