@@ -143,8 +143,9 @@ func (br baseRenderer) makeHeader(v *toolCallCmp, tool string, width int, params
143func (br baseRenderer) renderError(v *toolCallCmp, message string) string {
143func (br baseRenderer) renderError(v *toolCallCmp, message string) string {
144 t := styles.CurrentTheme()
144 t := styles.CurrentTheme()
145 header := br.makeHeader(v, prettifyToolName(v.call.Name), v.textWidth(), "")
145 header := br.makeHeader(v, prettifyToolName(v.call.Name), v.textWidth(), "")
146+ errorTag := t.S().Base.Padding(0, 1).Background(t.Red).Foreground(t.White).Render("ERROR")
146− message = t.S().Error.Render(v.fit(message, v.textWidth()-2)) // -2 for padding
147+ message = t.S().Base.Foreground(t.FgHalfMuted).Render(v.fit(message, v.textWidth()-3-lipgloss.Width(errorTag))) // -2 for padding and space
147− return joinHeaderBody(header, message)
148+ return joinHeaderBody(header, errorTag+" "+message)
150// Register tool renderers
151// Register tool renderers
@@ -198,6 +199,9 @@ func (br bashRenderer) Render(v *toolCallCmp) string {
198 args := newParamBuilder().addMain(cmd).build()
199 args := newParamBuilder().addMain(cmd).build()
200 return br.renderWithParams(v, "Bash", args, func() string {
201 return br.renderWithParams(v, "Bash", args, func() string {
202+ if v.result.Content == tools.BashNoOutput {
201 return renderPlainContent(v, v.result.Content)
205 return renderPlainContent(v, v.result.Content)
@@ -254,13 +258,12 @@ type editRenderer struct {
254// Render displays the edited file with a formatted diff of changes
258// Render displays the edited file with a formatted diff of changes
255func (er editRenderer) Render(v *toolCallCmp) string {
259func (er editRenderer) Render(v *toolCallCmp) string {
256 var params tools.EditParams
260 var params tools.EditParams
257− if err := er.unmarshalParams(v.call.Input, ¶ms); err != nil {
262+ if err := er.unmarshalParams(v.call.Input, ¶ms); err == nil {
258− return er.renderError(v, "Invalid edit parameters")
263+ file := fsext.PrettyPath(params.FilePath)
264+ args = newParamBuilder().addMain(file).build()
261− file := fsext.PrettyPath(params.FilePath)
262− args := newParamBuilder().addMain(file).build()
264 return er.renderWithParams(v, "Edit", args, func() string {
267 return er.renderWithParams(v, "Edit", args, func() string {
265 var meta tools.EditResponseMetadata
268 var meta tools.EditResponseMetadata
266 if err := er.unmarshalParams(v.result.Metadata, &meta); err != nil {
269 if err := er.unmarshalParams(v.result.Metadata, &meta); err != nil {
@@ -290,13 +293,13 @@ type writeRenderer struct {
290// Render displays the file being written with syntax highlighting
293// Render displays the file being written with syntax highlighting
291func (wr writeRenderer) Render(v *toolCallCmp) string {
294func (wr writeRenderer) Render(v *toolCallCmp) string {
292 var params tools.WriteParams
295 var params tools.WriteParams
293− if err := wr.unmarshalParams(v.call.Input, ¶ms); err != nil {
298+ if err := wr.unmarshalParams(v.call.Input, ¶ms); err == nil {
294− return wr.renderError(v, "Invalid write parameters")
299+ file = fsext.PrettyPath(params.FilePath)
300+ args = newParamBuilder().addMain(file).build()
297− file := fsext.PrettyPath(params.FilePath)
298− args := newParamBuilder().addMain(file).build()
300 return wr.renderWithParams(v, "Write", args, func() string {
303 return wr.renderWithParams(v, "Write", args, func() string {
301 return renderCodeContent(v, file, params.Content, 0)
304 return renderCodeContent(v, file, params.Content, 0)
@@ -314,16 +317,15 @@ type fetchRenderer struct {
314// Render displays the fetched URL with format and timeout parameters
317// Render displays the fetched URL with format and timeout parameters
315func (fr fetchRenderer) Render(v *toolCallCmp) string {
318func (fr fetchRenderer) Render(v *toolCallCmp) string {
316 var params tools.FetchParams
319 var params tools.FetchParams
317− if err := fr.unmarshalParams(v.call.Input, ¶ms); err != nil {
321+ if err := fr.unmarshalParams(v.call.Input, ¶ms); err == nil {
318− return fr.renderError(v, "Invalid fetch parameters")
322+ args = newParamBuilder().
323+ addMain(params.URL).
324+ addKeyValue("format", params.Format).
325+ addKeyValue("timeout", formatTimeout(params.Timeout)).
321− args := newParamBuilder().
322− addMain(params.URL).
323− addKeyValue("format", params.Format).
324− addKeyValue("timeout", formatTimeout(params.Timeout)).
327 return fr.renderWithParams(v, "Fetch", args, func() string {
329 return fr.renderWithParams(v, "Fetch", args, func() string {
328 file := fr.getFileExtension(params.Format)
330 file := fr.getFileExtension(params.Format)
329 return renderCodeContent(v, file, v.result.Content, 0)
331 return renderCodeContent(v, file, v.result.Content, 0)
@@ -362,15 +364,14 @@ type globRenderer struct {
362// Render displays the glob pattern with optional path parameter
364// Render displays the glob pattern with optional path parameter
363func (gr globRenderer) Render(v *toolCallCmp) string {
365func (gr globRenderer) Render(v *toolCallCmp) string {
364 var params tools.GlobParams
366 var params tools.GlobParams
365− if err := gr.unmarshalParams(v.call.Input, ¶ms); err != nil {
368+ if err := gr.unmarshalParams(v.call.Input, ¶ms); err == nil {
366− return gr.renderError(v, "Invalid glob parameters")
369+ args = newParamBuilder().
370+ addMain(params.Pattern).
371+ addKeyValue("path", params.Path).
369− args := newParamBuilder().
370− addMain(params.Pattern).
371− addKeyValue("path", params.Path).
374 return gr.renderWithParams(v, "Glob", args, func() string {
375 return gr.renderWithParams(v, "Glob", args, func() string {
375 return renderPlainContent(v, v.result.Content)
376 return renderPlainContent(v, v.result.Content)
@@ -388,17 +389,16 @@ type grepRenderer struct {
388// Render displays the search pattern with path, include, and literal text options
389// Render displays the search pattern with path, include, and literal text options
389func (gr grepRenderer) Render(v *toolCallCmp) string {
390func (gr grepRenderer) Render(v *toolCallCmp) string {
390 var params tools.GrepParams
391 var params tools.GrepParams
391− if err := gr.unmarshalParams(v.call.Input, ¶ms); err != nil {
393+ if err := gr.unmarshalParams(v.call.Input, ¶ms); err == nil {
392− return gr.renderError(v, "Invalid grep parameters")
394+ args = newParamBuilder().
395+ addMain(params.Pattern).
396+ addKeyValue("path", params.Path).
397+ addKeyValue("include", params.Include).
398+ addFlag("literal", params.LiteralText).
395− args := newParamBuilder().
396− addMain(params.Pattern).
397− addKeyValue("path", params.Path).
398− addKeyValue("include", params.Include).
399− addFlag("literal", params.LiteralText).
402 return gr.renderWithParams(v, "Grep", args, func() string {
402 return gr.renderWithParams(v, "Grep", args, func() string {
403 return renderPlainContent(v, v.result.Content)
403 return renderPlainContent(v, v.result.Content)
@@ -416,17 +416,16 @@ type lsRenderer struct {
416// Render displays the directory path, defaulting to current directory
416// Render displays the directory path, defaulting to current directory
417func (lr lsRenderer) Render(v *toolCallCmp) string {
417func (lr lsRenderer) Render(v *toolCallCmp) string {
418 var params tools.LSParams
418 var params tools.LSParams
419− if err := lr.unmarshalParams(v.call.Input, ¶ms); err != nil {
420+ if err := lr.unmarshalParams(v.call.Input, ¶ms); err == nil {
420− return lr.renderError(v, "Invalid ls parameters")
421+ path := params.Path
425+ path = fsext.PrettyPath(path)
423− path := params.Path
427+ args = newParamBuilder().addMain(path).build()
427− path = fsext.PrettyPath(path)
429− args := newParamBuilder().addMain(path).build()
431 return lr.renderWithParams(v, "List", args, func() string {
430 return lr.renderWithParams(v, "List", args, func() string {
432 return renderPlainContent(v, v.result.Content)
431 return renderPlainContent(v, v.result.Content)
@@ -445,16 +444,15 @@ type sourcegraphRenderer struct {
445// Render displays the search query with optional count and context window parameters
444// Render displays the search query with optional count and context window parameters
446func (sr sourcegraphRenderer) Render(v *toolCallCmp) string {
445func (sr sourcegraphRenderer) Render(v *toolCallCmp) string {
447 var params tools.SourcegraphParams
446 var params tools.SourcegraphParams
448− if err := sr.unmarshalParams(v.call.Input, ¶ms); err != nil {
448+ if err := sr.unmarshalParams(v.call.Input, ¶ms); err == nil {
449− return sr.renderError(v, "Invalid sourcegraph parameters")
449+ args = newParamBuilder().
450+ addMain(params.Query).
451+ addKeyValue("count", formatNonZero(params.Count)).
452+ addKeyValue("context", formatNonZero(params.ContextWindow)).
452− args := newParamBuilder().
453− addMain(params.Query).
454− addKeyValue("count", formatNonZero(params.Count)).
455− addKeyValue("context", formatNonZero(params.ContextWindow)).
458 return sr.renderWithParams(v, "Sourcegraph", args, func() string {
456 return sr.renderWithParams(v, "Sourcegraph", args, func() string {
459 return renderPlainContent(v, v.result.Content)
457 return renderPlainContent(v, v.result.Content)
@@ -498,13 +496,15 @@ func RoundedEnumerator(children tree.Children, index int) string {
498func (tr agentRenderer) Render(v *toolCallCmp) string {
496func (tr agentRenderer) Render(v *toolCallCmp) string {
499 t := styles.CurrentTheme()
497 t := styles.CurrentTheme()
500 var params agent.AgentParams
498 var params agent.AgentParams
501− if err := tr.unmarshalParams(v.call.Input, ¶ms); err != nil {
499+ tr.unmarshalParams(v.call.Input, ¶ms)
502− return tr.renderError(v, "Invalid task parameters")
500+
504 prompt := params.Prompt
501 prompt := params.Prompt
505 prompt = strings.ReplaceAll(prompt, "\n", " ")
502 prompt = strings.ReplaceAll(prompt, "\n", " ")
507 header := tr.makeHeader(v, "Agent", v.textWidth())
504 header := tr.makeHeader(v, "Agent", v.textWidth())
505+ if res, done := earlyState(header, v); done {
508 taskTag := t.S().Base.Padding(0, 1).MarginLeft(1).Background(t.BlueLight).Foreground(t.White).Render("Task")
508 taskTag := t.S().Base.Padding(0, 1).MarginLeft(1).Background(t.BlueLight).Foreground(t.White).Render("Task")
509 remainingWidth := v.textWidth() - lipgloss.Width(header) - lipgloss.Width(taskTag) - 2 // -2 for padding
509 remainingWidth := v.textWidth() - lipgloss.Width(header) - lipgloss.Width(taskTag) - 2 // -2 for padding
510 prompt = t.S().Muted.Width(remainingWidth).Render(prompt)
510 prompt = t.S().Muted.Width(remainingWidth).Render(prompt)
@@ -608,15 +608,15 @@ func earlyState(header string, v *toolCallCmp) (string, bool) {
608 case v.result.IsError:
608 case v.result.IsError:
609 message = v.renderToolError()
609 message = v.renderToolError()
610 case v.cancelled:
610 case v.cancelled:
611− message = "Cancelled"
611+ message = t.S().Base.Padding(0, 1).Background(t.Border).Render("Cancelled")
612 case v.result.ToolCallID == "":
612 case v.result.ToolCallID == "":
613− message = "Waiting for tool to start..."
613+ message = t.S().Base.Padding(0, 1).Background(t.Accent).Foreground(t.FgSubtle).Render("Waiting for tool to start...")
615 return "", false
615 return "", false
618 message = t.S().Base.PaddingLeft(2).Render(message)
618 message = t.S().Base.PaddingLeft(2).Render(message)
619− return lipgloss.JoinVertical(lipgloss.Left, header, message), true
619+ return lipgloss.JoinVertical(lipgloss.Left, header, "", message), true
622func joinHeaderBody(header, body string) string {
622func joinHeaderBody(header, body string) string {
@@ -699,8 +699,9 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
699func (v *toolCallCmp) renderToolError() string {
699func (v *toolCallCmp) renderToolError() string {
700 t := styles.CurrentTheme()
700 t := styles.CurrentTheme()
701 err := strings.ReplaceAll(v.result.Content, "\n", " ")
701 err := strings.ReplaceAll(v.result.Content, "\n", " ")
702− err = fmt.Sprintf("Error: %s", err)
702+ errTag := t.S().Base.Padding(0, 1).Background(t.Red).Foreground(t.White).Render("ERROR")
703− return t.S().Base.Foreground(t.Error).Render(v.fit(err, v.textWidth()-2))
703+ err = fmt.Sprintf("%s %s", errTag, t.S().Base.Foreground(t.FgHalfMuted).Render(v.fit(err, v.textWidth()-2-lipgloss.Width(errTag))))
706func truncateHeight(s string, h int) string {
707func truncateHeight(s string, h int) string {