瀏覽代碼

feat(tui): better task tool rendering

adamdottv 8 月之前
父節點
當前提交
4e4cff49c0

+ 50 - 5
packages/tui/internal/components/chat/message.go

@@ -1,6 +1,7 @@
 package chat
 
 import (
+	"encoding/json"
 	"fmt"
 	"path/filepath"
 	"slices"
@@ -252,6 +253,7 @@ func renderToolInvocation(
 	metadata client.MessageInfo_Metadata_Tool_AdditionalProperties,
 	showDetails bool,
 	isLast bool,
+	contentOnly bool,
 ) string {
 	ignoredTools := []string{"todoread"}
 	if slices.Contains(ignoredTools, toolCall.ToolName) {
@@ -313,7 +315,6 @@ func renderToolInvocation(
 				keys = append(keys, key)
 			}
 			slices.Sort(keys)
-
 			firstKey := ""
 			if len(keys) > 0 {
 				firstKey = keys[0]
@@ -454,14 +455,60 @@ func renderToolInvocation(
 			body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
 			body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
 		}
+	case "task":
+		if description, ok := toolArgsMap["description"].(string); ok {
+			title = fmt.Sprintf("TASK %s   %s", description, elapsed)
+			if summary, ok := metadata.Get("summary"); ok {
+				toolcalls := summary.([]any)
+				// toolcalls :=
+
+				steps := []string{}
+				for _, toolcall := range toolcalls {
+					call := toolcall.(map[string]any)
+					if toolInvocation, ok := call["toolInvocation"].(map[string]any); ok {
+						data, _ := json.Marshal(toolInvocation)
+						var toolCall client.MessageToolInvocationToolCall
+						_ = json.Unmarshal(data, &toolCall)
+
+						if metadata, ok := call["metadata"].(map[string]any); ok {
+							data, _ = json.Marshal(metadata)
+							var toolMetadata client.MessageInfo_Metadata_Tool_AdditionalProperties
+							_ = json.Unmarshal(data, &toolMetadata)
+
+							step := renderToolInvocation(
+								toolCall,
+								nil,
+								toolMetadata,
+								false,
+								false,
+								true,
+							)
+							steps = append(steps, step)
+						}
+					}
+				}
+				body = strings.Join(steps, "\n")
+				body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
+			}
+		}
+
 	default:
 		toolName := renderToolName(toolCall.ToolName)
 		title = fmt.Sprintf("%s %s   %s", toolName, toolArgs, elapsed)
+		if result == nil {
+			empty := ""
+			result = &empty
+		}
 		body = *result
 		body = truncateHeight(body, 10)
 		body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
 	}
 
+	if contentOnly {
+		title = "∟ " + title
+		return title
+	}
+
 	if !showDetails {
 		title = "∟ " + title
 		padding := calculatePadding()
@@ -502,8 +549,6 @@ func renderToolInvocation(
 
 func renderToolName(name string) string {
 	switch name {
-	// case agent.AgentToolName:
-	// 	return "Task"
 	case "list":
 		return "LIST"
 	case "webfetch":
@@ -563,8 +608,8 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
 
 func renderToolAction(name string) string {
 	switch name {
-	// case agent.AgentToolName:
-	// 	return "Preparing prompt..."
+	case "task":
+		return "Searching..."
 	case "bash":
 		return "Building command..."
 	case "edit":

+ 2 - 0
packages/tui/internal/components/chat/messages.go

@@ -191,6 +191,7 @@ func (m *messagesComponent) renderView() {
 							metadata,
 							m.showToolDetails,
 							isLastToolInvocation,
+							false,
 						)
 						m.cache.Set(key, content)
 					}
@@ -202,6 +203,7 @@ func (m *messagesComponent) renderView() {
 						metadata,
 						m.showToolDetails,
 						isLastToolInvocation,
+						false,
 					)
 				}
 

+ 5 - 0
packages/tui/internal/tui/tui.go

@@ -212,6 +212,11 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	case dialog.CompletionDialogCloseMsg:
 		a.showCompletionDialog = false
 		a.completions.SetProvider(a.completionManager.DefaultProvider())
+	case client.EventInstallationUpdated:
+		return a, toast.NewSuccessToast(
+			"New version installed",
+			toast.WithTitle("opencode updated to "+msg.Properties.Version+", restart to apply."),
+		)
 	case client.EventSessionUpdated:
 		if msg.Properties.Info.Id == a.app.Session.Id {
 			a.app.Session = &msg.Properties.Info

+ 102 - 65
packages/tui/pkg/client/gen/openapi.json

@@ -525,6 +525,9 @@
           {
             "$ref": "#/components/schemas/Event.storage.write"
           },
+          {
+            "$ref": "#/components/schemas/Event.installation.updated"
+          },
           {
             "$ref": "#/components/schemas/Event.lsp.client.diagnostics"
           },
@@ -537,9 +540,6 @@
           {
             "$ref": "#/components/schemas/Event.message.part.updated"
           },
-          {
-            "$ref": "#/components/schemas/Event.installation.updated"
-          },
           {
             "$ref": "#/components/schemas/Event.session.updated"
           },
@@ -551,11 +551,11 @@
           "propertyName": "type",
           "mapping": {
             "storage.write": "#/components/schemas/Event.storage.write",
+            "installation.updated": "#/components/schemas/Event.installation.updated",
             "lsp.client.diagnostics": "#/components/schemas/Event.lsp.client.diagnostics",
             "permission.updated": "#/components/schemas/Event.permission.updated",
             "message.updated": "#/components/schemas/Event.message.updated",
             "message.part.updated": "#/components/schemas/Event.message.part.updated",
-            "installation.updated": "#/components/schemas/Event.installation.updated",
             "session.updated": "#/components/schemas/Event.session.updated",
             "session.error": "#/components/schemas/Event.session.error"
           }
@@ -586,6 +586,30 @@
           "properties"
         ]
       },
+      "Event.installation.updated": {
+        "type": "object",
+        "properties": {
+          "type": {
+            "type": "string",
+            "const": "installation.updated"
+          },
+          "properties": {
+            "type": "object",
+            "properties": {
+              "version": {
+                "type": "string"
+              }
+            },
+            "required": [
+              "version"
+            ]
+          }
+        },
+        "required": [
+          "type",
+          "properties"
+        ]
+      },
       "Event.lsp.client.diagnostics": {
         "type": "object",
         "properties": {
@@ -1201,30 +1225,6 @@
           "properties"
         ]
       },
-      "Event.installation.updated": {
-        "type": "object",
-        "properties": {
-          "type": {
-            "type": "string",
-            "const": "installation.updated"
-          },
-          "properties": {
-            "type": "object",
-            "properties": {
-              "version": {
-                "type": "string"
-              }
-            },
-            "required": [
-              "version"
-            ]
-          }
-        },
-        "required": [
-          "type",
-          "properties"
-        ]
-      },
       "Event.session.updated": {
         "type": "object",
         "properties": {
@@ -1391,13 +1391,16 @@
         "type": "object",
         "properties": {
           "$schema": {
-            "type": "string"
+            "type": "string",
+            "description": "JSON schema reference for configuration validation"
           },
           "theme": {
-            "type": "string"
+            "type": "string",
+            "description": "Theme name to use for the interface"
           },
           "keybinds": {
-            "$ref": "#/components/schemas/Config.Keybinds"
+            "$ref": "#/components/schemas/Config.Keybinds",
+            "description": "Custom keybind configurations"
           },
           "autoshare": {
             "type": "boolean",
@@ -1508,7 +1511,8 @@
               "required": [
                 "models"
               ]
-            }
+            },
+            "description": "Custom provider configurations and model overrides"
           },
           "mcp": {
             "type": "object",
@@ -1528,7 +1532,8 @@
                   "remote": "#/components/schemas/Config.McpRemote"
                 }
               }
-            }
+            },
+            "description": "MCP (Model Context Protocol) server configurations"
           }
         }
       },
@@ -1536,85 +1541,112 @@
         "type": "object",
         "properties": {
           "leader": {
-            "type": "string"
+            "type": "string",
+            "description": "Leader key for keybind combinations"
           },
           "help": {
-            "type": "string"
+            "type": "string",
+            "description": "Show help dialog"
           },
           "editor_open": {
-            "type": "string"
+            "type": "string",
+            "description": "Open external editor"
           },
           "session_new": {
-            "type": "string"
+            "type": "string",
+            "description": "Create a new session"
           },
           "session_list": {
-            "type": "string"
+            "type": "string",
+            "description": "List all sessions"
           },
           "session_share": {
-            "type": "string"
+            "type": "string",
+            "description": "Share current session"
           },
           "session_interrupt": {
-            "type": "string"
+            "type": "string",
+            "description": "Interrupt current session"
           },
           "session_compact": {
-            "type": "string"
+            "type": "string",
+            "description": "Toggle compact mode for session"
           },
           "tool_details": {
-            "type": "string"
+            "type": "string",
+            "description": "Show tool details"
           },
           "model_list": {
-            "type": "string"
+            "type": "string",
+            "description": "List available models"
           },
           "theme_list": {
-            "type": "string"
+            "type": "string",
+            "description": "List available themes"
           },
           "project_init": {
-            "type": "string"
+            "type": "string",
+            "description": "Initialize project configuration"
           },
           "input_clear": {
-            "type": "string"
+            "type": "string",
+            "description": "Clear input field"
           },
           "input_paste": {
-            "type": "string"
+            "type": "string",
+            "description": "Paste from clipboard"
           },
           "input_submit": {
-            "type": "string"
+            "type": "string",
+            "description": "Submit input"
           },
           "input_newline": {
-            "type": "string"
+            "type": "string",
+            "description": "Insert newline in input"
           },
           "history_previous": {
-            "type": "string"
+            "type": "string",
+            "description": "Navigate to previous history item"
           },
           "history_next": {
-            "type": "string"
+            "type": "string",
+            "description": "Navigate to next history item"
           },
           "messages_page_up": {
-            "type": "string"
+            "type": "string",
+            "description": "Scroll messages up by one page"
           },
           "messages_page_down": {
-            "type": "string"
+            "type": "string",
+            "description": "Scroll messages down by one page"
           },
           "messages_half_page_up": {
-            "type": "string"
+            "type": "string",
+            "description": "Scroll messages up by half page"
           },
           "messages_half_page_down": {
-            "type": "string"
+            "type": "string",
+            "description": "Scroll messages down by half page"
           },
           "messages_previous": {
-            "type": "string"
+            "type": "string",
+            "description": "Navigate to previous message"
           },
           "messages_next": {
-            "type": "string"
+            "type": "string",
+            "description": "Navigate to next message"
           },
           "messages_first": {
-            "type": "string"
+            "type": "string",
+            "description": "Navigate to first message"
           },
           "messages_last": {
-            "type": "string"
+            "type": "string",
+            "description": "Navigate to last message"
           },
           "app_exit": {
-            "type": "string"
+            "type": "string",
+            "description": "Exit the application"
           }
         }
       },
@@ -1723,19 +1755,22 @@
         "properties": {
           "type": {
             "type": "string",
-            "const": "local"
+            "const": "local",
+            "description": "Type of MCP server connection"
           },
           "command": {
             "type": "array",
             "items": {
               "type": "string"
-            }
+            },
+            "description": "Command and arguments to run the MCP server"
           },
           "environment": {
             "type": "object",
             "additionalProperties": {
               "type": "string"
-            }
+            },
+            "description": "Environment variables to set when running the MCP server"
           }
         },
         "required": [
@@ -1748,10 +1783,12 @@
         "properties": {
           "type": {
             "type": "string",
-            "const": "remote"
+            "const": "remote",
+            "description": "Type of MCP server connection"
           },
           "url": {
-            "type": "string"
+            "type": "string",
+            "description": "URL of the remote MCP server"
           }
         },
         "required": [

+ 129 - 61
packages/tui/pkg/client/generated-client.go

@@ -41,6 +41,7 @@ type AppInfo struct {
 
 // ConfigInfo defines model for Config.Info.
 type ConfigInfo struct {
+	// Schema JSON schema reference for configuration validation
 	Schema *string `json:"$schema,omitempty"`
 
 	// Autoshare Share newly created sessions automatically
@@ -50,12 +51,16 @@ type ConfigInfo struct {
 	Autoupdate *bool `json:"autoupdate,omitempty"`
 
 	// DisabledProviders Disable providers that are loaded automatically
-	DisabledProviders *[]string                                       `json:"disabled_providers,omitempty"`
-	Keybinds          *ConfigKeybinds                                 `json:"keybinds,omitempty"`
-	Mcp               *map[string]ConfigInfo_Mcp_AdditionalProperties `json:"mcp,omitempty"`
+	DisabledProviders *[]string       `json:"disabled_providers,omitempty"`
+	Keybinds          *ConfigKeybinds `json:"keybinds,omitempty"`
+
+	// Mcp MCP (Model Context Protocol) server configurations
+	Mcp *map[string]ConfigInfo_Mcp_AdditionalProperties `json:"mcp,omitempty"`
 
 	// Model Model to use in the format of provider/model, eg anthropic/claude-2
-	Model    *string `json:"model,omitempty"`
+	Model *string `json:"model,omitempty"`
+
+	// Provider Custom provider configurations and model overrides
 	Provider *map[string]struct {
 		Api    *string   `json:"api,omitempty"`
 		Env    *[]string `json:"env,omitempty"`
@@ -81,6 +86,8 @@ type ConfigInfo struct {
 		Npm     *string                 `json:"npm,omitempty"`
 		Options *map[string]interface{} `json:"options,omitempty"`
 	} `json:"provider,omitempty"`
+
+	// Theme Theme name to use for the interface
 	Theme *string `json:"theme,omitempty"`
 }
 
@@ -91,46 +98,107 @@ type ConfigInfo_Mcp_AdditionalProperties struct {
 
 // ConfigKeybinds defines model for Config.Keybinds.
 type ConfigKeybinds struct {
-	AppExit              *string `json:"app_exit,omitempty"`
-	EditorOpen           *string `json:"editor_open,omitempty"`
-	Help                 *string `json:"help,omitempty"`
-	HistoryNext          *string `json:"history_next,omitempty"`
-	HistoryPrevious      *string `json:"history_previous,omitempty"`
-	InputClear           *string `json:"input_clear,omitempty"`
-	InputNewline         *string `json:"input_newline,omitempty"`
-	InputPaste           *string `json:"input_paste,omitempty"`
-	InputSubmit          *string `json:"input_submit,omitempty"`
-	Leader               *string `json:"leader,omitempty"`
-	MessagesFirst        *string `json:"messages_first,omitempty"`
+	// AppExit Exit the application
+	AppExit *string `json:"app_exit,omitempty"`
+
+	// EditorOpen Open external editor
+	EditorOpen *string `json:"editor_open,omitempty"`
+
+	// Help Show help dialog
+	Help *string `json:"help,omitempty"`
+
+	// HistoryNext Navigate to next history item
+	HistoryNext *string `json:"history_next,omitempty"`
+
+	// HistoryPrevious Navigate to previous history item
+	HistoryPrevious *string `json:"history_previous,omitempty"`
+
+	// InputClear Clear input field
+	InputClear *string `json:"input_clear,omitempty"`
+
+	// InputNewline Insert newline in input
+	InputNewline *string `json:"input_newline,omitempty"`
+
+	// InputPaste Paste from clipboard
+	InputPaste *string `json:"input_paste,omitempty"`
+
+	// InputSubmit Submit input
+	InputSubmit *string `json:"input_submit,omitempty"`
+
+	// Leader Leader key for keybind combinations
+	Leader *string `json:"leader,omitempty"`
+
+	// MessagesFirst Navigate to first message
+	MessagesFirst *string `json:"messages_first,omitempty"`
+
+	// MessagesHalfPageDown Scroll messages down by half page
 	MessagesHalfPageDown *string `json:"messages_half_page_down,omitempty"`
-	MessagesHalfPageUp   *string `json:"messages_half_page_up,omitempty"`
-	MessagesLast         *string `json:"messages_last,omitempty"`
-	MessagesNext         *string `json:"messages_next,omitempty"`
-	MessagesPageDown     *string `json:"messages_page_down,omitempty"`
-	MessagesPageUp       *string `json:"messages_page_up,omitempty"`
-	MessagesPrevious     *string `json:"messages_previous,omitempty"`
-	ModelList            *string `json:"model_list,omitempty"`
-	ProjectInit          *string `json:"project_init,omitempty"`
-	SessionCompact       *string `json:"session_compact,omitempty"`
-	SessionInterrupt     *string `json:"session_interrupt,omitempty"`
-	SessionList          *string `json:"session_list,omitempty"`
-	SessionNew           *string `json:"session_new,omitempty"`
-	SessionShare         *string `json:"session_share,omitempty"`
-	ThemeList            *string `json:"theme_list,omitempty"`
-	ToolDetails          *string `json:"tool_details,omitempty"`
+
+	// MessagesHalfPageUp Scroll messages up by half page
+	MessagesHalfPageUp *string `json:"messages_half_page_up,omitempty"`
+
+	// MessagesLast Navigate to last message
+	MessagesLast *string `json:"messages_last,omitempty"`
+
+	// MessagesNext Navigate to next message
+	MessagesNext *string `json:"messages_next,omitempty"`
+
+	// MessagesPageDown Scroll messages down by one page
+	MessagesPageDown *string `json:"messages_page_down,omitempty"`
+
+	// MessagesPageUp Scroll messages up by one page
+	MessagesPageUp *string `json:"messages_page_up,omitempty"`
+
+	// MessagesPrevious Navigate to previous message
+	MessagesPrevious *string `json:"messages_previous,omitempty"`
+
+	// ModelList List available models
+	ModelList *string `json:"model_list,omitempty"`
+
+	// ProjectInit Initialize project configuration
+	ProjectInit *string `json:"project_init,omitempty"`
+
+	// SessionCompact Toggle compact mode for session
+	SessionCompact *string `json:"session_compact,omitempty"`
+
+	// SessionInterrupt Interrupt current session
+	SessionInterrupt *string `json:"session_interrupt,omitempty"`
+
+	// SessionList List all sessions
+	SessionList *string `json:"session_list,omitempty"`
+
+	// SessionNew Create a new session
+	SessionNew *string `json:"session_new,omitempty"`
+
+	// SessionShare Share current session
+	SessionShare *string `json:"session_share,omitempty"`
+
+	// ThemeList List available themes
+	ThemeList *string `json:"theme_list,omitempty"`
+
+	// ToolDetails Show tool details
+	ToolDetails *string `json:"tool_details,omitempty"`
 }
 
 // ConfigMcpLocal defines model for Config.McpLocal.
 type ConfigMcpLocal struct {
-	Command     []string           `json:"command"`
+	// Command Command and arguments to run the MCP server
+	Command []string `json:"command"`
+
+	// Environment Environment variables to set when running the MCP server
 	Environment *map[string]string `json:"environment,omitempty"`
-	Type        string             `json:"type"`
+
+	// Type Type of MCP server connection
+	Type string `json:"type"`
 }
 
 // ConfigMcpRemote defines model for Config.McpRemote.
 type ConfigMcpRemote struct {
+	// Type Type of MCP server connection
 	Type string `json:"type"`
-	Url  string `json:"url"`
+
+	// Url URL of the remote MCP server
+	Url string `json:"url"`
 }
 
 // Error defines model for Error.
@@ -684,6 +752,34 @@ func (t *Event) MergeEventStorageWrite(v EventStorageWrite) error {
 	return err
 }
 
+// AsEventInstallationUpdated returns the union data inside the Event as a EventInstallationUpdated
+func (t Event) AsEventInstallationUpdated() (EventInstallationUpdated, error) {
+	var body EventInstallationUpdated
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+// FromEventInstallationUpdated overwrites any union data inside the Event as the provided EventInstallationUpdated
+func (t *Event) FromEventInstallationUpdated(v EventInstallationUpdated) error {
+	v.Type = "installation.updated"
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+// MergeEventInstallationUpdated performs a merge with any union data inside the Event, using the provided EventInstallationUpdated
+func (t *Event) MergeEventInstallationUpdated(v EventInstallationUpdated) error {
+	v.Type = "installation.updated"
+	b, err := json.Marshal(v)
+	if err != nil {
+		return err
+	}
+
+	merged, err := runtime.JSONMerge(t.union, b)
+	t.union = merged
+	return err
+}
+
 // AsEventLspClientDiagnostics returns the union data inside the Event as a EventLspClientDiagnostics
 func (t Event) AsEventLspClientDiagnostics() (EventLspClientDiagnostics, error) {
 	var body EventLspClientDiagnostics
@@ -796,34 +892,6 @@ func (t *Event) MergeEventMessagePartUpdated(v EventMessagePartUpdated) error {
 	return err
 }
 
-// AsEventInstallationUpdated returns the union data inside the Event as a EventInstallationUpdated
-func (t Event) AsEventInstallationUpdated() (EventInstallationUpdated, error) {
-	var body EventInstallationUpdated
-	err := json.Unmarshal(t.union, &body)
-	return body, err
-}
-
-// FromEventInstallationUpdated overwrites any union data inside the Event as the provided EventInstallationUpdated
-func (t *Event) FromEventInstallationUpdated(v EventInstallationUpdated) error {
-	v.Type = "installation.updated"
-	b, err := json.Marshal(v)
-	t.union = b
-	return err
-}
-
-// MergeEventInstallationUpdated performs a merge with any union data inside the Event, using the provided EventInstallationUpdated
-func (t *Event) MergeEventInstallationUpdated(v EventInstallationUpdated) error {
-	v.Type = "installation.updated"
-	b, err := json.Marshal(v)
-	if err != nil {
-		return err
-	}
-
-	merged, err := runtime.JSONMerge(t.union, b)
-	t.union = merged
-	return err
-}
-
 // AsEventSessionUpdated returns the union data inside the Event as a EventSessionUpdated
 func (t Event) AsEventSessionUpdated() (EventSessionUpdated, error) {
 	var body EventSessionUpdated