A statusline has exactly one moving part — Claude Code runs your command and prints the first stdout line — so when it misbehaves, the cause is almost always one of a short list. Work through these in order; each step has a definitive test.
Step 1: run the command by hand
Everything starts here. Take the exact statusLine.command from ~/.claude/settings.json and run it with sample input:
echo '{"model":{"display_name":"Test"}}' | node ~/.claude/terminal-pro/terminal-pro-statusline.mjs
echo "exit: $?"Three outcomes, three diagnoses:
- Prints a line, exit 0 — the script is fine; the problem is in settings or refresh (steps 3–4).
- Error / non-zero exit — the script is broken. Node version, syntax error, or an unguarded field access on missing payload data.
- command not found— path problem. Use absolute paths; the statusline does not inherit your interactive shell's aliases or PATH additions.
Step 2: check what lands on stdout
Claude Code displays stdout only. Debug output to stderr is invisible (useful!), but a script that logs its result via console.error shows nothing. Also: only the first line counts — a leading blank line (say, from a stray console.log()) renders as an empty statusline even though your real content follows.
Step 3: validate the settings block
{
"statusLine": {
"type": "command",
"command": "node ~/.claude/terminal-pro/terminal-pro-statusline.mjs",
"padding": 0,
"refreshInterval": 10
}
}typemust be"command"— a typo here silently disables the feature.- JSON must parse.
node -e "JSON.parse(require('fs').readFileSync(process.env.HOME + '/.claude/settings.json'))"settles it. - settings.json is read at session start — restart the session after editing it. Config-file changes (like the theme in
statusline.config.json) apply on the next refresh without restart.
Step 4: stale numbers
The line re-runs every refreshIntervalseconds and on session events. If values lag, the interval is high, or your script caches aggressively. Device metrics in the generated runtime are cached on purpose (default 15s TTL) so the statusline doesn't hammer the system — context and rate numbers always come fresh from the payload.
Step 5: wrapping and overflow
A line longer than the terminal width wraps and breaks the UI. The fix is reading COLUMNS and trimming segments — pattern here. Quick test at a forced width:
echo '{}' | COLUMNS=64 node ~/.claude/terminal-pro/terminal-pro-statusline.mjs | wc -cThe count (including newline) must stay ≤ 65.
Step 6: missing fields
No rate_limits on fresh sessions, no repo outside git, no context_window in odd states — all normal. Segments must skip, not crash; guarded access patterns here. If your hand-rolled script keeps fighting you, the builder's generated runtime has these fallbacks built in and tested.