@@ -3,6 +3,7 @@ package messages
33import (
44 "encoding/json"
55 "fmt"
6+ "os"
67 "strings"
78 "time"
89
@@ -95,7 +96,7 @@ func (br baseRenderer) renderWithParams(v *toolCallCmp, toolName string, args []
9596 if v.isNested {
9697 width -= 4 // Adjust for nested tool call indentation
9798 }
98− header := makeHeader(toolName, width, args...)
99+ header := br.makeHeader(v, toolName, width, args...)
99100 if v.isNested {
100101 return v.style().Render(header)
101102 }
@@ -111,6 +112,32 @@ func (br baseRenderer) unmarshalParams(input string, target any) error {
111112 return json.Unmarshal([]byte(input), target)
112113}
113114
115+// makeHeader builds "<Tool>: param (key=value)" and truncates as needed.
116+func (br baseRenderer) makeHeader(v *toolCallCmp, tool string, width int, params ...string) string {
117+ t := styles.CurrentTheme()
118+ icon := t.S().Base.Foreground(t.GreenDark).Render(styles.ToolPending)
119+ if v.result.ToolCallID != "" {
120+ if v.result.IsError {
121+ icon = t.S().Base.Foreground(t.RedDark).Render(styles.ToolError)
122+ } else {
123+ icon = t.S().Base.Foreground(t.Green).Render(styles.ToolSuccess)
124+ }
125+ } else if v.cancelled {
126+ icon = t.S().Muted.Render(styles.ToolPending)
127+ }
128+ tool = t.S().Base.Foreground(t.Blue).Render(tool)
129+ prefix := fmt.Sprintf("%s %s: ", icon, tool)
130+ return prefix + renderParamList(width-lipgloss.Width(prefix), params...)
131+}
132+
133+// renderError provides consistent error rendering
134+func (br baseRenderer) renderError(v *toolCallCmp, message string) string {
135+ t := styles.CurrentTheme()
136+ header := br.makeHeader(v, prettifyToolName(v.call.Name), v.textWidth(), "")
137+ message = t.S().Error.Render(v.fit(message, v.textWidth()-2)) // -2 for padding
138+ return joinHeaderBody(header, message)
139+}
140+
114141// Register tool renderers
115142func init() {
116143 registry.register(tools.BashToolName, func() renderer { return bashRenderer{} })
@@ -167,12 +194,6 @@ func (br bashRenderer) Render(v *toolCallCmp) string {
167194 })
168195}
169196
170−// renderError provides consistent error rendering
171−func (br baseRenderer) renderError(v *toolCallCmp, message string) string {
172− header := makeHeader("Error", v.textWidth(), message)
173− return joinHeaderBody(header, "")
174−}
175−
176197// -----------------------------------------------------------------------------
177198// View renderer
178199// -----------------------------------------------------------------------------
@@ -189,7 +210,7 @@ func (vr viewRenderer) Render(v *toolCallCmp) string {
189210 return vr.renderError(v, "Invalid view parameters")
190211 }
191212
192− file := removeWorkingDirPrefix(params.FilePath)
213+ file := prettyPath(params.FilePath)
193214 args := newParamBuilder().
194215 addMain(file).
195216 addKeyValue("limit", formatNonZero(params.Limit)).
@@ -229,7 +250,7 @@ func (er editRenderer) Render(v *toolCallCmp) string {
229250 return er.renderError(v, "Invalid edit parameters")
230251 }
231252
232− file := removeWorkingDirPrefix(params.FilePath)
253+ file := prettyPath(params.FilePath)
233254 args := newParamBuilder().addMain(file).build()
234255
235256 return er.renderWithParams(v, "Edit", args, func() string {
@@ -239,7 +260,7 @@ func (er editRenderer) Render(v *toolCallCmp) string {
239260 }
240261
241262 trunc := truncateHeight(meta.Diff, responseContextHeight)
242− diffView, _ := diff.FormatDiff(trunc, diff.WithTotalWidth(v.textWidth()))
263+ diffView, _ := diff.FormatDiff(trunc, diff.WithTotalWidth(v.textWidth()-2))
243264 return diffView
244265 })
245266}
@@ -260,7 +281,7 @@ func (wr writeRenderer) Render(v *toolCallCmp) string {
260281 return wr.renderError(v, "Invalid write parameters")
261282 }
262283
263− file := removeWorkingDirPrefix(params.FilePath)
284+ file := prettyPath(params.FilePath)
264285 args := newParamBuilder().addMain(file).build()
265286
266287 return wr.renderWithParams(v, "Write", args, func() string {
@@ -494,7 +515,7 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
494515 prompt = strings.ReplaceAll(prompt, "\n", " ")
495516 args := newParamBuilder().addMain(prompt).build()
496517
497− header := makeHeader("Task", v.textWidth(), args...)
518+ header := tr.makeHeader(v, "Task", v.textWidth(), args...)
498519 t := tree.Root(header)
499520
500521 for _, call := range v.nestedToolCalls {
@@ -524,12 +545,6 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
524545 return joinHeaderBody(header, body)
525546}
526547
527−// makeHeader builds "<Tool>: param (key=value)" and truncates as needed.
528−func makeHeader(tool string, width int, params ...string) string {
529− prefix := tool + ": "
530− return prefix + renderParamList(width-lipgloss.Width(prefix), params...)
531−}
532−
533548// renderParamList renders params, params[0] (params[1]=params[2] ....)
534549func renderParamList(paramsWidth int, params ...string) string {
535550 if len(params) == 0 {
@@ -575,20 +590,27 @@ func renderParamList(paramsWidth int, params ...string) string {
575590
576591// earlyState returns immediately‑rendered error/cancelled/ongoing states.
577592func earlyState(header string, v *toolCallCmp) (string, bool) {
593+ t := styles.CurrentTheme()
594+ message := ""
578595 switch {
579596 case v.result.IsError:
580− return lipgloss.JoinVertical(lipgloss.Left, header, v.renderToolError()), true
597+ message = v.renderToolError()
581598 case v.cancelled:
582− return lipgloss.JoinVertical(lipgloss.Left, header, "Cancelled"), true
599+ message = "Cancelled"
583600 case v.result.ToolCallID == "":
584− return lipgloss.JoinVertical(lipgloss.Left, header, "Waiting for tool to finish..."), true
601+ message = "Waiting for tool to start..."
585602 default:
586603 return "", false
587604 }
605+
606+ message = t.S().Base.PaddingLeft(2).Render(message)
607+ return lipgloss.JoinVertical(lipgloss.Left, header, message), true
588608}
589609
590610func joinHeaderBody(header, body string) string {
591− return lipgloss.JoinVertical(lipgloss.Left, header, "", body, "")
611+ t := styles.CurrentTheme()
612+ body = t.S().Base.PaddingLeft(2).Render(body)
613+ return lipgloss.JoinVertical(lipgloss.Left, header, body, "")
592614}
593615
594616func renderPlainContent(v *toolCallCmp, content string) string {
@@ -596,17 +618,18 @@ func renderPlainContent(v *toolCallCmp, content string) string {
596618 content = strings.TrimSpace(content)
597619 lines := strings.Split(content, "\n")
598620
621+ width := v.textWidth() - 2 // -2 for left padding
599622 var out []string
600623 for i, ln := range lines {
601624 if i >= responseContextHeight {
602625 break
603626 }
604627 ln = " " + ln // left padding
605− if len(ln) > v.textWidth() {
606− ln = v.fit(ln, v.textWidth())
628+ if len(ln) > width {
629+ ln = v.fit(ln, width)
607630 }
608631 out = append(out, t.S().Muted.
609− Width(v.textWidth()).
632+ Width(width).
610633 Background(t.BgSubtle).
611634 Render(ln))
612635 }
@@ -638,7 +661,7 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
638661 PaddingLeft(4).
639662 PaddingRight(2).
640663 Render(fmt.Sprintf("%d", i+1+offset))
641− w := v.textWidth() - lipgloss.Width(num)
664+ w := v.textWidth() - 2 - lipgloss.Width(num) // -2 for left padding
642665 lines[i] = lipgloss.JoinHorizontal(lipgloss.Left,
643666 num,
644667 t.S().Base.
@@ -669,6 +692,15 @@ func truncateHeight(s string, h int) string {
669692 return s
670693}
671694
695+func prettyPath(path string) string {
696+ // replace home directory with ~
697+ homeDir, err := os.UserHomeDir()
698+ if err == nil {
699+ path = strings.ReplaceAll(path, homeDir, "~")
700+ }
701+ return path
702+}
703+
672704func prettifyToolName(name string) string {
673705 switch name {
674706 case agent.AgentToolName: