* test: cover OSC 8 close on truncation
Reproduces the case where wrapLineToWidth truncates inside an
unclosed OSC 8 hyperlink — without an explicit terminator the
hyperlink stays open and the terminal renders the link's underline
across the remainder of the line.
* fix: close OSC 8 hyperlink on truncation
truncateToWidth previously appended only an SGR reset (\x1b[0m)
after the ellipsis. SGR reset clears colors and underline as an
attribute, but it does not close an open OSC 8 hyperlink — so when
the slice cut inside a `\x1b]8;;URL\x1b\\…label…` sequence the link
remained open and most terminals (iTerm2, Ghostty, kitty, …) render
the link's underline through the rest of the line.
Detect the trailing unclosed OSC 8 in the truncated slice and emit
the close sequence (`\x1b]8;;\x1b\\`) before the ellipsis so the
suffix renders as plain text and the underline stops at the link's
last visible character.
Most visible in `renderGitFilesLine`, where each changed file's
basename is wrapped in an OSC 8 hyperlink and a long file list
truncates inside one of those links.
Restore the replaced config and width-fallback coverage, add guardrails for invalid forceMaxWidth inputs, and drop generated dist changes from the PR so review stays source-focused.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Allow users to explicitly apply maxWidth even when terminal width detection returns a smaller value, which helps in subprocess-driven statusline environments on Linux.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fix render width fallback handling, keep usage visible for Enterprise model aliases while still hiding Bedrock usage, and only generate branch hyperlinks for GitHub remotes.\n\nMaintainer review covered security, generated artifacts, subprocess/path/link handling, width-sensitive rendering regressions, and maintainability.\n\nTests: npm test (486 passed, 1 skipped)
When all terminal width detection methods fail (stdout.columns,
stderr.columns, COLUMNS env), the renderer falls back to the
hardcoded UNKNOWN_TERMINAL_WIDTH (40). This new `maxWidth` config
option lets users replace that fallback with a value suited to
their device — useful on mobile clients, piped subprocesses, or
terminals where auto-detection is unreliable.
When the terminal reports a real width, maxWidth is ignored.
Closes#385, closes#404
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
getProviderLabel was string-matching `anthropic.claude-` in model IDs,
which false-positived on cross-region IDs like `us.anthropic.claude-sonnet-4-6`.
Now checks `CLAUDE_CODE_USE_BEDROCK=1` env var instead.
Fixes#466
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When running as a CC statusLine subprocess, stdout/stderr are piped and
COLUMNS may not be set. getTerminalWidth() falls back to
UNKNOWN_TERMINAL_WIDTH (40), which is not a real measurement.
Previously, this fallback value was treated as real terminal width in two
places:
- canCombine in renderExpanded(): context + usage lines were always split
because combined width exceeded 40 columns
- wrapLineToWidth in render(): all output lines were forcefully wrapped
at 40 characters, breaking the layout
Fix: introduce a widthIsReal guard that checks whether the resolved width
is the UNKNOWN_TERMINAL_WIDTH sentinel. When width is unknown:
- canCombine defaults to true (combine lines optimistically)
- wrapLineToWidth is skipped (wrapWidth=0 → no-op)
The host process (Claude Code) handles final display layout, so wrapping
at an arbitrary fallback value is harmful rather than helpful.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: detect terminal width via stderr when stdout is piped
When Claude Code runs the statusLine as a subprocess, stdout is captured
(piped), so process.stdout.columns is undefined. With no terminal width,
long lines aren't wrapped by the HUD — the terminal wraps them silently
instead. Claude Code counts \n characters to determine how many lines to
erase on the next render, so the physical line count diverges from what
Claude Code expects. Over successive renders the cursor drifts upward into
the scrollback buffer, causing the terminal to jump to the top on every
tool execution (https://github.com/jarrodwatts/claude-hud/issues/209).
Fix: fall back to process.stderr.columns before the COLUMNS env var.
When the statusLine subprocess is spawned, only stdout is redirected;
stderr remains connected to the real TTY and returns the correct width.
With accurate width, lines are properly wrapped in the HUD output and
Claude Code's line count stays consistent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: drop dist artifacts and cover stderr width
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>