From 53a5a9e9ae3a6e40610d81ac6fbfb9d30921ff97 Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Thu, 8 Jan 2026 15:40:07 -0800 Subject: [PATCH] x: redesign agent UI with minimal styling (#13650) --- x/agent/approval.go | 185 +++++++++++++++++--------------------------- x/cmd/run.go | 45 +++++------ 2 files changed, 95 insertions(+), 135 deletions(-) diff --git a/x/agent/approval.go b/x/agent/approval.go index 3a160e4d9..4d0959793 100644 --- a/x/agent/approval.go +++ b/x/agent/approval.go @@ -37,6 +37,25 @@ var optionLabels = []string{ "3. Deny", } +// toolDisplayNames maps internal tool names to human-readable display names. +var toolDisplayNames = map[string]string{ + "bash": "Bash", + "web_search": "Web Search", +} + +// ToolDisplayName returns the human-readable display name for a tool. +func ToolDisplayName(toolName string) string { + if displayName, ok := toolDisplayNames[toolName]; ok { + return displayName + } + // Default: capitalize first letter and replace underscores with spaces + name := strings.ReplaceAll(toolName, "_", " ") + if len(name) > 0 { + return strings.ToUpper(name[:1]) + name[1:] + } + return toolName +} + // autoAllowCommands are commands that are always allowed without prompting. // These are zero-risk, read-only commands. var autoAllowCommands = map[string]bool{ @@ -509,11 +528,12 @@ func (a *ApprovalManager) RequestApproval(toolName string, args map[string]any) // formatToolDisplay creates the display string for a tool call. func formatToolDisplay(toolName string, args map[string]any) string { var sb strings.Builder + displayName := ToolDisplayName(toolName) // For bash, show command directly if toolName == "bash" { if cmd, ok := args["command"].(string); ok { - sb.WriteString(fmt.Sprintf("Tool: %s\n", toolName)) + sb.WriteString(fmt.Sprintf("Tool: %s\n", displayName)) sb.WriteString(fmt.Sprintf("Command: %s", cmd)) return sb.String() } @@ -522,7 +542,7 @@ func formatToolDisplay(toolName string, args map[string]any) string { // For web search, show query and internet notice if toolName == "web_search" { if query, ok := args["query"].(string); ok { - sb.WriteString(fmt.Sprintf("Tool: %s\n", toolName)) + sb.WriteString(fmt.Sprintf("Tool: %s\n", displayName)) sb.WriteString(fmt.Sprintf("Query: %s\n", query)) sb.WriteString("Uses internet via ollama.com") return sb.String() @@ -530,7 +550,7 @@ func formatToolDisplay(toolName string, args map[string]any) string { } // Generic display - sb.WriteString(fmt.Sprintf("Tool: %s", toolName)) + sb.WriteString(fmt.Sprintf("Tool: %s", displayName)) if len(args) > 0 { sb.WriteString("\nArguments: ") first := true @@ -724,7 +744,7 @@ func wrapText(text string, maxWidth int) []string { // getHintLines returns the hint text wrapped to terminal width func getHintLines(state *selectorState) []string { - hint := "↑/↓ navigate, Enter confirm, 1-3 quick, Ctrl+C cancel" + hint := "up/down select, enter confirm, 1-3 quick select, ctrl+c cancel" if state.termWidth >= len(hint)+1 { return []string{hint} } @@ -734,86 +754,60 @@ func getHintLines(state *selectorState) []string { // calculateTotalLines calculates how many lines the selector will use func calculateTotalLines(state *selectorState) int { - toolLines := wrapText(state.toolDisplay, state.innerWidth) + toolLines := strings.Split(state.toolDisplay, "\n") hintLines := getHintLines(state) - // top border + (warning line if applicable) + tool lines + separator + options + bottom border + hint lines + // warning line (if applicable) + tool lines + blank line + options + blank line + hint lines warningLines := 0 if state.isWarning { - warningLines = 1 + warningLines = 2 // warning line + blank line after } - return 1 + warningLines + len(toolLines) + 1 + len(optionLabels) + 1 + len(hintLines) + return warningLines + len(toolLines) + 1 + len(optionLabels) + 1 + len(hintLines) } -// renderSelectorBox renders the complete selector box +// renderSelectorBox renders the selector (minimal, no box) func renderSelectorBox(state *selectorState) { - toolLines := wrapText(state.toolDisplay, state.innerWidth) + toolLines := strings.Split(state.toolDisplay, "\n") hintLines := getHintLines(state) - // Use red for warning (outside cwd), cyan for normal - boxColor := "\033[36m" // cyan + // Draw warning line if needed if state.isWarning { - boxColor = "\033[91m" // bright red + fmt.Fprintf(os.Stderr, "\033[1mwarning:\033[0m command targets paths outside project\033[K\r\n") + fmt.Fprintf(os.Stderr, "\033[K\r\n") // blank line after warning } - // Draw box top - fmt.Fprintf(os.Stderr, "%s┌%s┐\033[0m\033[K\r\n", boxColor, strings.Repeat("─", state.boxWidth-2)) - - // Draw warning line if needed (inside the box) - if state.isWarning { - warning := "!! OUTSIDE PROJECT !!" - padding := (state.innerWidth - len(warning)) / 2 - if padding < 0 { - padding = 0 - } - fmt.Fprintf(os.Stderr, "%s│\033[0m %s%s%s %s│\033[0m\033[K\r\n", boxColor, - strings.Repeat(" ", padding), warning, strings.Repeat(" ", state.innerWidth-len(warning)-padding), boxColor) - } - - // Draw tool info + // Draw tool info (plain white) for _, line := range toolLines { - fmt.Fprintf(os.Stderr, "%s│\033[0m %-*s %s│\033[0m\033[K\r\n", boxColor, state.innerWidth, line, boxColor) + fmt.Fprintf(os.Stderr, "%s\033[K\r\n", line) } - // Draw separator - fmt.Fprintf(os.Stderr, "%s├%s┤\033[0m\033[K\r\n", boxColor, strings.Repeat("─", state.boxWidth-2)) + // Blank line separator + fmt.Fprintf(os.Stderr, "\033[K\r\n") - // Draw options with numbers (Deny option includes reason input) + // Draw options for i, label := range optionLabels { - if i == 2 { // Deny option - show with reason input beside it + if i == 2 { // Deny option with input denyLabel := "3. Deny: " - availableWidth := state.innerWidth - 2 - len(denyLabel) - if availableWidth < 5 { - availableWidth = 5 - } inputDisplay := state.denyReason - if len(inputDisplay) > availableWidth { - inputDisplay = inputDisplay[len(inputDisplay)-availableWidth:] - } if i == state.selected { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[1;32m> %s\033[0m%-*s %s│\033[0m\033[K\r\n", boxColor, denyLabel, availableWidth, inputDisplay, boxColor) + fmt.Fprintf(os.Stderr, " \033[1m%s\033[0m%s\033[K\r\n", denyLabel, inputDisplay) } else { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[90m%s\033[0m%-*s %s│\033[0m\033[K\r\n", boxColor, denyLabel, availableWidth, inputDisplay, boxColor) + fmt.Fprintf(os.Stderr, " \033[37m%s\033[0m%s\033[K\r\n", denyLabel, inputDisplay) } } else { - displayLabel := label - if len(displayLabel) > state.innerWidth-2 { - displayLabel = displayLabel[:state.innerWidth-5] + "..." - } if i == state.selected { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[1;32m> %-*s\033[0m %s│\033[0m\033[K\r\n", boxColor, state.innerWidth-2, displayLabel, boxColor) + fmt.Fprintf(os.Stderr, " \033[1m%s\033[0m\033[K\r\n", label) } else { - fmt.Fprintf(os.Stderr, "%s│\033[0m %-*s %s│\033[0m\033[K\r\n", boxColor, state.innerWidth-2, displayLabel, boxColor) + fmt.Fprintf(os.Stderr, " \033[37m%s\033[0m\033[K\r\n", label) } } } - // Draw box bottom - fmt.Fprintf(os.Stderr, "%s└%s┘\033[0m\033[K\r\n", boxColor, strings.Repeat("─", state.boxWidth-2)) + // Blank line before hint + fmt.Fprintf(os.Stderr, "\033[K\r\n") - // Draw hint (may be multiple lines) + // Draw hint (dark grey) for i, line := range hintLines { if i == len(hintLines)-1 { - // Last line - no newline fmt.Fprintf(os.Stderr, "\033[90m%s\033[0m\033[K", line) } else { fmt.Fprintf(os.Stderr, "\033[90m%s\033[0m\033[K\r\n", line) @@ -825,50 +819,33 @@ func renderSelectorBox(state *selectorState) { func updateSelectorOptions(state *selectorState) { hintLines := getHintLines(state) - // Use red for warning (outside cwd), cyan for normal - boxColor := "\033[36m" // cyan - if state.isWarning { - boxColor = "\033[91m" // bright red - } - // Move up to the first option line // Cursor is at end of last hint line, need to go up: - // (hint lines - 1) + 1 (bottom border) + numOptions + // (hint lines - 1) + 1 (blank line) + numOptions linesToMove := len(hintLines) - 1 + 1 + len(optionLabels) fmt.Fprintf(os.Stderr, "\033[%dA\r", linesToMove) - // Redraw options (Deny option includes reason input) + // Redraw options for i, label := range optionLabels { if i == 2 { // Deny option denyLabel := "3. Deny: " - availableWidth := state.innerWidth - 2 - len(denyLabel) - if availableWidth < 5 { - availableWidth = 5 - } inputDisplay := state.denyReason - if len(inputDisplay) > availableWidth { - inputDisplay = inputDisplay[len(inputDisplay)-availableWidth:] - } if i == state.selected { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[1;32m> %s\033[0m%-*s %s│\033[0m\033[K\r\n", boxColor, denyLabel, availableWidth, inputDisplay, boxColor) + fmt.Fprintf(os.Stderr, " \033[1m%s\033[0m%s\033[K\r\n", denyLabel, inputDisplay) } else { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[90m%s\033[0m%-*s %s│\033[0m\033[K\r\n", boxColor, denyLabel, availableWidth, inputDisplay, boxColor) + fmt.Fprintf(os.Stderr, " \033[37m%s\033[0m%s\033[K\r\n", denyLabel, inputDisplay) } } else { - displayLabel := label - if len(displayLabel) > state.innerWidth-2 { - displayLabel = displayLabel[:state.innerWidth-5] + "..." - } if i == state.selected { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[1;32m> %-*s\033[0m %s│\033[0m\033[K\r\n", boxColor, state.innerWidth-2, displayLabel, boxColor) + fmt.Fprintf(os.Stderr, " \033[1m%s\033[0m\033[K\r\n", label) } else { - fmt.Fprintf(os.Stderr, "%s│\033[0m %-*s %s│\033[0m\033[K\r\n", boxColor, state.innerWidth-2, displayLabel, boxColor) + fmt.Fprintf(os.Stderr, " \033[37m%s\033[0m\033[K\r\n", label) } } } - // Redraw bottom and hint - fmt.Fprintf(os.Stderr, "%s└%s┘\033[0m\033[K\r\n", boxColor, strings.Repeat("─", state.boxWidth-2)) + // Blank line + hint + fmt.Fprintf(os.Stderr, "\033[K\r\n") for i, line := range hintLines { if i == len(hintLines)-1 { fmt.Fprintf(os.Stderr, "\033[90m%s\033[0m\033[K", line) @@ -882,36 +859,23 @@ func updateSelectorOptions(state *selectorState) { func updateReasonInput(state *selectorState) { hintLines := getHintLines(state) - // Use red for warning (outside cwd), cyan for normal - boxColor := "\033[36m" // cyan - if state.isWarning { - boxColor = "\033[91m" // bright red - } - // Move up to the Deny line (3rd option, index 2) // Cursor is at end of last hint line, need to go up: - // (hint lines - 1) + 1 (bottom border) + 1 (Deny is last option) + // (hint lines - 1) + 1 (blank line) + 1 (Deny is last option) linesToMove := len(hintLines) - 1 + 1 + 1 fmt.Fprintf(os.Stderr, "\033[%dA\r", linesToMove) // Redraw Deny line with reason denyLabel := "3. Deny: " - availableWidth := state.innerWidth - 2 - len(denyLabel) - if availableWidth < 5 { - availableWidth = 5 - } inputDisplay := state.denyReason - if len(inputDisplay) > availableWidth { - inputDisplay = inputDisplay[len(inputDisplay)-availableWidth:] - } if state.selected == 2 { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[1;32m> %s\033[0m%-*s %s│\033[0m\033[K\r\n", boxColor, denyLabel, availableWidth, inputDisplay, boxColor) + fmt.Fprintf(os.Stderr, " \033[1m%s\033[0m%s\033[K\r\n", denyLabel, inputDisplay) } else { - fmt.Fprintf(os.Stderr, "%s│\033[0m \033[90m%s\033[0m%-*s %s│\033[0m\033[K\r\n", boxColor, denyLabel, availableWidth, inputDisplay, boxColor) + fmt.Fprintf(os.Stderr, " \033[37m%s\033[0m%s\033[K\r\n", denyLabel, inputDisplay) } - // Redraw bottom and hint - fmt.Fprintf(os.Stderr, "%s└%s┘\033[0m\033[K\r\n", boxColor, strings.Repeat("─", state.boxWidth-2)) + // Blank line + hint + fmt.Fprintf(os.Stderr, "\033[K\r\n") for i, line := range hintLines { if i == len(hintLines)-1 { fmt.Fprintf(os.Stderr, "\033[90m%s\033[0m\033[K", line) @@ -935,11 +899,10 @@ func clearSelectorBox(state *selectorState) { // fallbackApproval handles approval when terminal control isn't available. func (a *ApprovalManager) fallbackApproval(toolDisplay string) (ApprovalResult, error) { fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Fprintln(os.Stderr, toolDisplay) - fmt.Fprintln(os.Stderr, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, "[1] Execute once [2] Always allow [3] Deny") - fmt.Fprint(os.Stderr, "Choice: ") + fmt.Fprint(os.Stderr, "choice: ") var input string fmt.Scanln(&input) @@ -982,19 +945,16 @@ func (a *ApprovalManager) AllowedTools() []string { // FormatApprovalResult returns a formatted string showing the approval result. func FormatApprovalResult(toolName string, args map[string]any, result ApprovalResult) string { - var status string - var icon string + var label string + displayName := ToolDisplayName(toolName) switch result.Decision { case ApprovalOnce: - status = "Approved" - icon = "\033[32m✓\033[0m" + label = "approved" case ApprovalAlways: - status = "Always allowed" - icon = "\033[32m✓\033[0m" + label = "always allowed" case ApprovalDeny: - status = "Denied" - icon = "\033[31m✗\033[0m" + label = "denied" } // Format based on tool type @@ -1004,7 +964,7 @@ func FormatApprovalResult(toolName string, args map[string]any, result ApprovalR if len(cmd) > 40 { cmd = cmd[:37] + "..." } - return fmt.Sprintf("▶ bash: %s [%s] %s", cmd, status, icon) + return fmt.Sprintf("\033[1m%s:\033[0m %s: %s", label, displayName, cmd) } } @@ -1014,11 +974,11 @@ func FormatApprovalResult(toolName string, args map[string]any, result ApprovalR if len(query) > 40 { query = query[:37] + "..." } - return fmt.Sprintf("▶ web_search: %s [%s] %s", query, status, icon) + return fmt.Sprintf("\033[1m%s:\033[0m %s: %s", label, displayName, query) } } - return fmt.Sprintf("▶ %s [%s] %s", toolName, status, icon) + return fmt.Sprintf("\033[1m%s:\033[0m %s", label, displayName) } // FormatDenyResult returns the tool result message when a tool is denied. @@ -1049,15 +1009,14 @@ func PromptYesNo(question string) (bool, error) { renderYesNo := func() { // Move to start of line and clear fmt.Fprintf(os.Stderr, "\r\033[K") - fmt.Fprintf(os.Stderr, "\033[36m%s\033[0m ", question) + fmt.Fprintf(os.Stderr, "%s ", question) for i, opt := range options { if i == selected { - fmt.Fprintf(os.Stderr, "\033[1;32m[%s]\033[0m ", opt) + fmt.Fprintf(os.Stderr, "\033[1m%s\033[0m ", opt) } else { - fmt.Fprintf(os.Stderr, "\033[90m %s \033[0m ", opt) + fmt.Fprintf(os.Stderr, "\033[37m%s\033[0m ", opt) } } - fmt.Fprintf(os.Stderr, "\033[90m(←/→ or y/n, Enter to confirm)\033[0m") } renderYesNo() diff --git a/x/cmd/run.go b/x/cmd/run.go index 8dd2f6bc1..5ec2ef942 100644 --- a/x/cmd/run.go +++ b/x/cmd/run.go @@ -91,8 +91,8 @@ func waitForOllamaSignin(ctx context.Context) error { var aErr api.AuthorizationError if errors.As(err, &aErr) && aErr.SigninURL != "" { fmt.Fprintf(os.Stderr, "\n To sign in, navigate to:\n") - fmt.Fprintf(os.Stderr, " \033[36m%s\033[0m\n\n", aErr.SigninURL) - fmt.Fprintf(os.Stderr, " \033[90mWaiting for sign in to complete...\033[0m") + fmt.Fprintf(os.Stderr, " %s\n\n", aErr.SigninURL) + fmt.Fprintf(os.Stderr, " \033[90mwaiting for sign in to complete...\033[0m") // Poll until auth succeeds ticker := time.NewTicker(2 * time.Second) @@ -106,7 +106,7 @@ func waitForOllamaSignin(ctx context.Context) error { case <-ticker.C: user, whoamiErr := client.Whoami(ctx) if whoamiErr == nil && user != nil && user.Name != "" { - fmt.Fprintf(os.Stderr, "\r\033[K \033[32mSigned in as %s\033[0m\n", user.Name) + fmt.Fprintf(os.Stderr, "\r\033[K\033[A\r\033[K \033[1msigned in:\033[0m %s\n", user.Name) return nil } // Still waiting, show dot @@ -264,12 +264,12 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { var authErr api.AuthorizationError if errors.As(err, &authErr) { p.StopAndClear() - fmt.Fprintf(os.Stderr, "\033[33mAuthentication required to use this cloud model.\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[1mauth required:\033[0m cloud model requires authentication\n") result, promptErr := agent.PromptYesNo("Sign in to Ollama?") if promptErr == nil && result { if signinErr := waitForOllamaSignin(ctx); signinErr == nil { // Retry the chat request - fmt.Fprintf(os.Stderr, "\033[90mRetrying...\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[90mretrying...\033[0m\n") continue // Retry the loop } } @@ -283,11 +283,11 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { p.StopAndClear() if consecutiveErrors >= 3 { - fmt.Fprintf(os.Stderr, "\033[31m✗ Too many consecutive errors, giving up\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[1merror:\033[0m too many consecutive errors, giving up\n") return nil, fmt.Errorf("too many consecutive server errors: %s", statusErr.ErrorMessage) } - fmt.Fprintf(os.Stderr, "\033[33m⚠ Server error (attempt %d/3): %s\033[0m\n", consecutiveErrors, statusErr.ErrorMessage) + fmt.Fprintf(os.Stderr, "\033[1mwarning:\033[0m server error (attempt %d/3): %s\n", consecutiveErrors, statusErr.ErrorMessage) // Include both the model's response and the error so it can learn assistantContent := fullResponse.String() @@ -353,8 +353,8 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { if cmd, ok := args["command"].(string); ok { // Check if command is denied (dangerous pattern) if denied, pattern := agent.IsDenied(cmd); denied { - fmt.Fprintf(os.Stderr, "\033[91m✗ Blocked: %s\033[0m\n", formatToolShort(toolName, args)) - fmt.Fprintf(os.Stderr, "\033[91m Matches dangerous pattern: %s\033[0m\n", pattern) + fmt.Fprintf(os.Stderr, "\033[1mblocked:\033[0m %s\n", formatToolShort(toolName, args)) + fmt.Fprintf(os.Stderr, " matches dangerous pattern: %s\n", pattern) toolResults = append(toolResults, api.Message{ Role: "tool", Content: agent.FormatDeniedResult(cmd, pattern), @@ -365,7 +365,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { // Check if command is auto-allowed (safe command) if agent.IsAutoAllowed(cmd) { - fmt.Fprintf(os.Stderr, "\033[90m▶ Auto-allowed: %s\033[0m\n", formatToolShort(toolName, args)) + fmt.Fprintf(os.Stderr, "\033[1mauto-allowed:\033[0m %s\n", formatToolShort(toolName, args)) skipApproval = true } } @@ -375,7 +375,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { // In yolo mode, skip all approval prompts if opts.YoloMode { if !skipApproval { - fmt.Fprintf(os.Stderr, "\033[90m▶ Running: %s\033[0m\n", formatToolShort(toolName, args)) + fmt.Fprintf(os.Stderr, "\033[1mrunning:\033[0m %s\n", formatToolShort(toolName, args)) } } else if !skipApproval && !approval.IsAllowed(toolName, args) { result, err := approval.RequestApproval(toolName, args) @@ -405,7 +405,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { } } else if !skipApproval { // Already allowed - show running indicator - fmt.Fprintf(os.Stderr, "\033[90m▶ Running: %s\033[0m\n", formatToolShort(toolName, args)) + fmt.Fprintf(os.Stderr, "\033[1mrunning:\033[0m %s\n", formatToolShort(toolName, args)) } // Execute the tool @@ -414,13 +414,13 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { // Check if web search needs authentication if errors.Is(err, tools.ErrWebSearchAuthRequired) { // Prompt user to sign in - fmt.Fprintf(os.Stderr, "\033[33m Web search requires authentication.\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[1mauth required:\033[0m web search requires authentication\n") result, promptErr := agent.PromptYesNo("Sign in to Ollama?") if promptErr == nil && result { // Get signin URL and wait for auth completion if signinErr := waitForOllamaSignin(ctx); signinErr == nil { // Retry the web search - fmt.Fprintf(os.Stderr, "\033[90m Retrying web search...\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[90mretrying web search...\033[0m\n") toolResult, err = toolRegistry.Execute(call) if err == nil { goto toolSuccess @@ -428,7 +428,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) { } } } - fmt.Fprintf(os.Stderr, "\033[31m Error: %v\033[0m\n", err) + fmt.Fprintf(os.Stderr, "\033[1merror:\033[0m %v\n", err) toolResults = append(toolResults, api.Message{ Role: "tool", Content: fmt.Sprintf("Error: %v", err), @@ -499,17 +499,18 @@ func truncateUTF8(s string, limit int) string { // formatToolShort returns a short description of a tool call. func formatToolShort(toolName string, args map[string]any) string { + displayName := agent.ToolDisplayName(toolName) if toolName == "bash" { if cmd, ok := args["command"].(string); ok { - return fmt.Sprintf("bash: %s", truncateUTF8(cmd, 50)) + return fmt.Sprintf("%s: %s", displayName, truncateUTF8(cmd, 50)) } } if toolName == "web_search" { if query, ok := args["query"].(string); ok { - return fmt.Sprintf("web_search: %s", truncateUTF8(query, 50)) + return fmt.Sprintf("%s: %s", displayName, truncateUTF8(query, 50)) } } - return toolName + return displayName } // Helper types and functions for display @@ -649,7 +650,7 @@ func GenerateInteractive(cmd *cobra.Command, modelName string, wordWrap bool, op // Check if model supports tools supportsTools, err := checkModelCapabilities(cmd.Context(), modelName) if err != nil { - fmt.Fprintf(os.Stderr, "\033[33mWarning: Could not check model capabilities: %v\033[0m\n", err) + fmt.Fprintf(os.Stderr, "\033[1mwarning:\033[0m could not check model capabilities: %v\n", err) supportsTools = false } @@ -658,13 +659,13 @@ func GenerateInteractive(cmd *cobra.Command, modelName string, wordWrap bool, op if supportsTools { toolRegistry = tools.DefaultRegistry() if toolRegistry.Count() > 0 { - fmt.Fprintf(os.Stderr, "\033[90mTools available: %s\033[0m\n", strings.Join(toolRegistry.Names(), ", ")) + fmt.Fprintf(os.Stderr, "\033[90mtools available: %s\033[0m\n", strings.Join(toolRegistry.Names(), ", ")) } if yoloMode { - fmt.Fprintf(os.Stderr, "\033[33m⚠ YOLO mode: All tool approvals will be skipped\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[1mwarning:\033[0m yolo mode - all tool approvals will be skipped\n") } } else { - fmt.Fprintf(os.Stderr, "\033[33mNote: Model does not support tools - running in chat-only mode\033[0m\n") + fmt.Fprintf(os.Stderr, "\033[1mnote:\033[0m model does not support tools - running in chat-only mode\n") } // Create approval manager for session