Skip to content

feat(terminal): honor CCSTATUSLINE_WIDTH env var to override probe#380

Merged
sirmalloc merged 3 commits into
sirmalloc:mainfrom
danielnc:feat/terminal-width-env-override
May 17, 2026
Merged

feat(terminal): honor CCSTATUSLINE_WIDTH env var to override probe#380
sirmalloc merged 3 commits into
sirmalloc:mainfrom
danielnc:feat/terminal-width-env-override

Conversation

@danielnc
Copy link
Copy Markdown
Contributor

Summary

Adds an explicit width override so users can bypass the TTY probe entirely when both ancestor-walk and tput cols fall through. Set CCSTATUSLINE_WIDTH=<cols> on the statusLine command, get exactly that width.

Motivation

PR #377 (stty -F/stty -f) closed #376 by fixing the case where an ancestor process holds a TTY but the legacy stty size < /dev/tty form errors with ENOTTY (Claude Code ≥ 2.1.139 spawns statusline/hooks without terminal access). That fix lands the common path.

It does not cover the case where no ancestor process owns a TTY at all — observed under some Claude Code agent-mode spawn paths, IDE integrations (Cursor, JetBrains plugin), and nested-shell scenarios. The ancestor walk runs out of TTY-bearing PIDs, the tput cols fallback returns 80, and flexMode: "full-minus-40" truncates the multi-line layout to 40 columns regardless of the real terminal width.

Concretely: on CC 2.1.141 + ccstatusline 2.2.17 + a wide iTerm2, the terminal-width widget reports Term: 80 in this configuration. The fallback to tput cols is the symptom; the absence of any TTY ancestor is the root cause.

This patch is complementary to #377 — it adds an explicit user-controlled escape hatch for cases the probe cannot solve on its own, and gives users a knob today while upstream work on passing terminalWidth via stdin JSON (#308) lands.

Change

src/utils/terminal.tsprobeTerminalWidth reads CCSTATUSLINE_WIDTH before any platform check or probe call. A valid positive integer short-circuits with that value. Anything else (missing, empty, non-numeric, zero, negative) falls through to existing logic — no behavior change for users who don't set it.

const overrideRaw = process.env.CCSTATUSLINE_WIDTH;
if (overrideRaw) {
    const override = parsePositiveInteger(overrideRaw);
    if (override !== null) {
        return override;
    }
}

Usage

In ~/.claude/settings.json:

"statusLine": {
  "type": "command",
  "command": "CCSTATUSLINE_WIDTH=200 ccstatusline"
}

(or via a wrapper script, or any other mechanism that sets the env on the spawned process.)

Test plan

Four new cases in src/utils/__tests__/terminal.test.ts:

  • override short-circuits probing entirely (asserts execSync is never called)
  • non-positive override ("0") falls back to probing → returns probed width
  • non-numeric override ("wide") falls back to probing → returns probed width
  • override applies on Windows where probing is otherwise disabled (asserts canDetectTerminalWidth() returns true and execSync is never called)
$ bunx vitest run src/utils/__tests__/terminal.test.ts
 Test Files  1 passed (1)
      Tests  12 passed (12)

Full-suite delta vs main: +4 passing tests, 0 new failures. The 105 pre-existing failures in claude-settings.test.ts, compaction.test.ts, global-command-resolution.test.ts, ClaudeAccountEmail.test.ts reproduce identically on main and on this branch — they're env-specific (platform/shell paths) and unrelated to terminal probing.

$ bun run lint
$ # clean, exit 0

Related

Risk

  • Behavior change is opt-in via env var; users who don't set CCSTATUSLINE_WIDTH get identical probe behavior to today.
  • Invalid env values (empty, non-numeric, ≤ 0) fall through — same probe path as no env at all.
  • Cross-platform: the override happens before the Windows short-circuit, so Windows users gain a working width source for the first time when they set it (previously null).

danielnc and others added 3 commits May 14, 2026 16:53
Provide an explicit width override so users can bypass the TTY probe
entirely when both ancestor-walk and `tput cols` fall through.

This is the fallback case the existing probe cannot solve on its own:
Claude Code >= 2.1.139 spawns statusline/hooks without terminal access,
and in some configurations (IDE integrations, nested shells, certain
agent-mode spawn paths) no ancestor process owns a TTY either. The
ancestor walk fails, `tput cols` returns 80, and the multi-line layout
truncates regardless of the actual iTerm2/terminal width.

PR sirmalloc#377 (`stty -F`/`stty -f`) covers the case where an ancestor does
hold a TTY but the legacy `< /dev/tty` form errors with ENOTTY. This
patch is complementary -- it handles the case where the ancestor walk
finds no TTY at all -- and gives users a knob today while upstream
work on passing `terminalWidth` via stdin JSON (sirmalloc#308) lands.

Change
------

`src/utils/terminal.ts` -- `probeTerminalWidth` now reads
`CCSTATUSLINE_WIDTH` before any platform check or probe. A valid
positive integer short-circuits with that value; anything else
(missing, empty, non-numeric, zero, negative) falls through to the
existing probe logic.

Usage
-----

Set the env var on the statusLine command in `~/.claude/settings.json`:

```json
"statusLine": {
  "type": "command",
  "command": "CCSTATUSLINE_WIDTH=200 ccstatusline"
}
```

Tests
-----

Four new cases in `src/utils/__tests__/terminal.test.ts`:

- override short-circuits probing entirely
- non-positive override (`0`) falls back to probing
- non-numeric override falls back to probing
- override applies on Windows where probing is otherwise disabled

`bunx vitest run src/utils/__tests__/terminal.test.ts` -> 12/12 pass.
Full-suite delta vs `main`: 4 new passing tests, zero new failures
(the pre-existing 105 env-specific failures in other suites are
unchanged on both branches).

`bun run lint` -> clean.
@sirmalloc sirmalloc merged commit ea97ddd into sirmalloc:main May 17, 2026
3 checks passed
@sirmalloc
Copy link
Copy Markdown
Owner

Thanks for this, it'll be published in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Terminal width probe fails under Claude Code ≥ 2.1.139 (no controlling TTY for spawned statusline)

2 participants