diff --git a/src/kimi_cli/ui/shell/prompt.py b/src/kimi_cli/ui/shell/prompt.py index b820fe8a0..2a389eae9 100644 --- a/src/kimi_cli/ui/shell/prompt.py +++ b/src/kimi_cli/ui/shell/prompt.py @@ -884,6 +884,7 @@ def __bool__(self) -> bool: _GIT_BRANCH_TTL = 5.0 _GIT_STATUS_TTL = 15.0 _TIP_ROTATE_INTERVAL = 30.0 +_CONTEXT_USAGE_WARNING_THRESHOLD = 0.80 _MAX_CWD_COLS = 30 _MAX_BRANCH_COLS = 22 @@ -2204,7 +2205,7 @@ def _render_bottom_toolbar(self) -> FormattedText: if tip_text and _display_width(tip_text) <= remaining: fragments.append((tc.tip, tip_text)) - # ── line 2: toast (left) + context (right) — always rendered ────── + # ── line 2: toast (left) + optional warning/status text (right) ────── fragments.append(("", "\n")) right_text = self._render_right_span(status) @@ -2224,8 +2225,9 @@ def _render_bottom_toolbar(self) -> FormattedText: else: left_width = 0 - fragments.append(("", " " * max(0, columns - left_width - right_width))) - fragments.append(("", right_text)) + if right_text: + fragments.append(("", " " * max(0, columns - left_width - right_width))) + fragments.append(("", right_text)) return FormattedText(fragments) @@ -2250,10 +2252,12 @@ def _get_one_rotating_tip(self) -> str | None: @staticmethod def _render_right_span(status: StatusSnapshot) -> str: current_toast = _current_toast("right") - if current_toast is None: - return format_context_status( - status.context_usage, - status.context_tokens, - status.max_context_tokens, - ) - return current_toast.message + if current_toast is not None: + return current_toast.message + if status.context_usage < _CONTEXT_USAGE_WARNING_THRESHOLD: + return "" + return format_context_status( + status.context_usage, + status.context_tokens, + status.max_context_tokens, + ) diff --git a/tests/ui_and_conv/test_prompt_tips.py b/tests/ui_and_conv/test_prompt_tips.py index fc322804b..1dc326aa8 100644 --- a/tests/ui_and_conv/test_prompt_tips.py +++ b/tests/ui_and_conv/test_prompt_tips.py @@ -434,12 +434,30 @@ def test_mode_drops_model_name_and_dot_on_very_narrow_terminal(monkeypatch: Any) # ── Line 2 structural correctness ───────────────────────────────────────────── -def test_toolbar_line2_context_appears_on_line2_not_line1(monkeypatch: Any) -> None: +def test_toolbar_line2_hides_context_below_warning_threshold(monkeypatch: Any) -> None: prompt_session = _make_toolbar_session(tips=[]) + prompt_session._status_provider = lambda: StatusSnapshot( + context_usage=0.42, + context_tokens=3_000, + max_context_tokens=10_000, + ) + lines = _render_toolbar_lines(prompt_session, 80, monkeypatch) + + assert "context:" not in lines[2] + assert "context:" not in lines[1] + + +def test_toolbar_line2_context_appears_at_warning_threshold(monkeypatch: Any) -> None: + prompt_session = _make_toolbar_session(tips=[]) + prompt_session._status_provider = lambda: StatusSnapshot( + context_usage=0.80, + context_tokens=8_000, + max_context_tokens=10_000, + ) lines = _render_toolbar_lines(prompt_session, 80, monkeypatch) - assert "context: 0.0%" in lines[2] - assert "context: 0.0%" not in lines[1] + assert "context: 80.0% (8k/10k)" in lines[2] + assert "context: 80.0% (8k/10k)" not in lines[1] def test_toolbar_line2_left_toast_appears_on_line2_not_line1(monkeypatch: Any) -> None: @@ -619,7 +637,7 @@ def get_size() -> Any: rendered_toolbar = prompt_session._render_bottom_toolbar() plain_toolbar = "".join(fragment[1] for fragment in rendered_toolbar) assert "tip" in plain_toolbar - assert "context: 0.0%" in plain_toolbar + assert "context:" not in plain_toolbar def test_modal_prompt_hides_normal_separator_and_prompt_label(monkeypatch) -> None: