statusLine: multi-line output truncated to single terminal width by cli-truncate

Resolved 💬 7 comments Opened Mar 22, 2026 by Nutcasey Closed May 14, 2026

Summary

Multi-line statusLine output is rendered inside a single <Text wrap="truncate"> component. The bundled cli-truncate (BXq) measures the total stringWidth across all lines (treating \n as width-0) and truncates the entire concatenated string to fit within terminal columns. This means Row 1 consumes most of the character budget, Row 2 gets a few characters, and Row 3 disappears entirely.

Steps to reproduce

  1. Configure a statusLine command that outputs 3 lines, each ~80-110 visible characters:

``bash
printf "Row1 with emoji and ANSI colours (%s chars)\n" "..."
printf "Row2 with prompt text\n"
printf "Row3 with insight text\n"
``

  1. Use a terminal width of ~120-200 columns
  2. Observe that Row 2 is aggressively truncated with (Unicode ellipsis) and Row 3 is missing

Expected behaviour

Each row should be independently truncated to the terminal width. A 3-row status line with rows of 80-110 chars should display all 3 rows, each clipped independently if needed.

Actual behaviour

All rows are concatenated into one string, stringWidth sums the visible width across all lines (~250+ chars), which exceeds the terminal width (~120-200), and cli-truncate slices the entire string from the end. Row 1 (~110 chars) consumes the budget, Row 2 gets ~5-10 chars + , Row 3 is lost.

Screenshot showing the issue (Row 2 truncated to "I ju…", Row 3 gone):

!status-truncation

Root cause (traced through v2.1.81 binary)

Rendering component (RR$):

// All rows passed as one string into a single <Text>
return createElement(Box, {paddingX: Z, gap: 2},
  O ? createElement(Text, {dimColor: true, wrap: "truncate"},
        createElement(J4, null, O)   // O = "Row1\nRow2\nRow3"
      ) : null
)

stdout processing (Wiq):

// Joins rows back into a single newline-delimited string
let H = A.stdout.trim().split('\n').flatMap((j) => j.trim() || []).join('\n');

Truncation (BXq / cli-truncate):

let width = stringWidth(input);     // sums ALL lines: 111 + 51 + 84 = 246
if (width <= columns) return input;  // 246 > 120 → truncate
return sliceAnsi(input, 0, columns - 1) + '…';  // slices across line boundaries

stringWidth treats \n as width-0 and sums character widths across all lines. cli-truncate was designed for single-line strings and has no multi-line awareness.

Suggested fix

Split the statusLine output into separate <Text> elements per line:

// Instead of one <Text> for everything:
return createElement(Box, {paddingX: Z, gap: 2, flexDirection: "column"},
  O ? O.split('\n').map((line, i) =>
    createElement(Text, {key: i, dimColor: true, wrap: "truncate"}, 
      createElement(J4, null, line)
    )
  ) : null
)

This way each row gets independently truncated to the terminal width, which is the expected behaviour for multi-line status output.

Environment

  • Claude Code v2.1.81
  • macOS (Darwin, Apple M1 Max)
  • Ghostty terminal (confirmed not the source of truncation)
  • Bun runtime (uses Bun.stringWidth)

View original on GitHub ↗

This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗