Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/landing-page/__tests__/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('Next.js 16 Project Setup', () => {
});

test('Next.js 16.x is installed and locked', () => {
expect(pkg.dependencies.next).toBe('16.1.6');
expect(pkg.dependencies.next).toBe('16.2.3');
});

test('React 19.x is installed and locked', () => {
Expand Down
4 changes: 2 additions & 2 deletions apps/landing-page/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.563.0",
"next": "16.1.6",
"next": "16.2.3",
"next-intl": "^4.8.2",
"next-themes": "^0.4.6",
"prism-react-renderer": "^2.4.1",
Expand All @@ -56,7 +56,7 @@
"axe-core": "^4.11.1",
"babel-plugin-react-compiler": "1.0.0",
"eslint": "^9",
"eslint-config-next": "16.1.6",
"eslint-config-next": "16.2.3",
"happy-dom": "^20.8.8",
"jest-axe": "^10.0.0",
"madge": "^8.0.0",
Expand Down
42 changes: 27 additions & 15 deletions packages/claude-code-plugin/hooks/codingbuddy-hud.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,40 @@
sys.path.insert(0, _LIB_DIR)

# === test_hud.py compatibility re-exports — DO NOT REMOVE without coordinated test update ===
# Defensive fallback: statusLine is a hot path invoked by Claude Code on
# every render. If any lib module is temporarily broken (e.g. mid-wave
# refactor), fall back to minimal inline implementations so the status
# bar still renders instead of crashing the Claude Code subprocess.
# Narrow the fallback to ImportError only: real logic bugs in lib modules
# (SyntaxError, NameError, AttributeError) must surface immediately instead
# of being silently swallowed by a catch-all. If a lib module fails to import
# entirely, the outer main() try/except at the bottom of this file still
# emits the minimal safe output via the BUDDY_FACE constant.
try:
from hud_buddy import BUDDY_FACE # canonical SSoT via tiny_actor_presets
except Exception: # pragma: no cover - defensive
BUDDY_FACE = "\u25d5\u203f\u25d5" # ◕‿◕
except ImportError: # pragma: no cover - defensive
BUDDY_FACE = "◕‿◕" # minimal constant for safe-output path

try:
from hud_rate_limits import format_rate_limits
except Exception: # pragma: no cover - defensive
def format_rate_limits(stdin_data: dict) -> str: # type: ignore[misc]
return ""
from hud_rate_limits import format_rate_limits # noqa: F401 re-exported for test_hud.py
except ImportError: # pragma: no cover - defensive
pass # main() catch-all handles absence

try:
from hud_version import get_fresh_version as _get_fresh_version # backcompat alias
except Exception: # pragma: no cover - defensive
def _get_fresh_version( # type: ignore[misc]
hud_state: dict, *, plugins_file: str = ""
) -> str:
return hud_state.get("version", "")
except ImportError: # pragma: no cover - defensive
pass # main() catch-all handles absence

# Wave 2-B velocity + Wave 2-C cache savings hot-path suffixes for the cost segment.
# Hoisted to module top per perf-1485 H1 so format_status_line avoids a
# sys.modules lookup on every render (~0.47μs saved per call).
try:
from hud_velocity import format_velocity_segment as _format_velocity_segment
except ImportError: # pragma: no cover - defensive
def _format_velocity_segment(stdin_data, hud_state=None): # type: ignore[misc]
return ""

try:
from hud_cache_savings import format_cache_savings as _format_cache_savings
except ImportError: # pragma: no cover - defensive
def _format_cache_savings(stdin_data): # type: ignore[misc]
return ""

# Agent eye glyphs from .ai-rules agent definitions.
AGENT_GLYPHS = {
Expand Down
86 changes: 74 additions & 12 deletions packages/claude-code-plugin/hooks/lib/hud_rate_limits.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,93 @@
"""Rate-limit formatting for CodingBuddy statusLine (#1326).
"""Rate-limit formatting for CodingBuddy statusLine (#1326, Wave 1-C).

Extracted verbatim from codingbuddy-hud.py as part of the Wave 0 refactor.
Behavior-preserving — see tests/test_hud.py for the contract.
Wave 0 extracted ``format_rate_limits`` verbatim from the monolith.
Wave 1-C upgrades the badge presentation:

* severity-icon rendering instead of colon-delimited percentages
* space separator between tiers instead of comma
* defensive float coercion so a non-numeric ``used_percentage``
(e.g. ``None``, ``"N/A"``) is silently treated as ``0`` instead
of crashing the entire status line
* ``"RL:"`` prefix is retained so downstream surfaces keep a stable
``startswith/contains`` anchor

Output example:

``RL:5h░13% 7d▓96%``

Severity icons (U+2591 / U+2592 / U+2593 block-drawing shades):

* ``░`` light — ``pct <= 60`` (low / healthy)
* ``▒`` medium — ``60 < pct <= 85`` (warning)
* ``▓`` dark — ``pct > 85`` (critical)
"""
from __future__ import annotations

from typing import Any, Dict

# Block-drawing glyphs (U+2591 light, U+2592 medium, U+2593 dark).
_ICON_LOW = "\u2591" # ░
_ICON_MID = "\u2592" # ▒
_ICON_HIGH = "\u2593" # ▓


def _severity_icon(pct: float) -> str:
"""Return a single block-drawing character reflecting quota usage.

Tiers:
* ``pct > 85`` → ▓ (dark / critical)
* ``pct > 60`` → ▒ (medium / warning)
* otherwise → ░ (light / healthy)
"""
if pct > 85:
return _ICON_HIGH
if pct > 60:
return _ICON_MID
return _ICON_LOW


def _coerce_percentage(raw: Any) -> float:
"""Defensively turn a ``used_percentage`` field into a float.

Accepts ``int``/``float``/numeric string. Returns ``0.0`` for
``None``, empty strings, non-numeric strings, or any other
unexpected type. Never raises — ``format_rate_limits`` lives on
the statusLine hot path and must degrade gracefully (see Wave 1-A
review HIGH finding for context on why defensive coercion matters).
"""
if raw is None:
return 0.0
try:
return float(raw)
except (TypeError, ValueError):
return 0.0


def format_rate_limits(stdin_data: Dict[str, Any]) -> str:
"""Format Claude Code rate-limit badge.
"""Format Claude Code rate-limit badge with severity icons.

Returns an empty string when no rate-limit data is supplied so the
badge can be dropped from the status line silently.
Returns an empty string when no rate-limit data is supplied so
the badge can be dropped from the status line silently.
"""
rl = stdin_data.get("rate_limits")
if not rl:
return ""
parts = []

parts: list = []

five = rl.get("five_hour")
if five:
pct = five.get("used_percentage", 0)
parts.append(f"5h:{pct:.0f}%")
pct = _coerce_percentage(five.get("used_percentage", 0))
icon = _severity_icon(pct)
parts.append(f"5h{icon}{pct:.0f}%")

seven = rl.get("seven_day")
if seven:
pct = seven.get("used_percentage", 0)
parts.append(f"7d:{pct:.0f}%")
pct = _coerce_percentage(seven.get("used_percentage", 0))
icon = _severity_icon(pct)
parts.append(f"7d{icon}{pct:.0f}%")

if not parts:
return ""
return "RL:" + ",".join(parts)

return "RL:" + " ".join(parts)
12 changes: 8 additions & 4 deletions packages/claude-code-plugin/tests/test_hud.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,16 +366,19 @@ def test_no_rate_limits(self):

def test_five_hour_only(self):
stdin = {"rate_limits": {"five_hour": {"used_percentage": 23.5}}}
assert hud.format_rate_limits(stdin) == "RL:5h:24%"
# Wave 1-C: severity icon replaces colon separator
# 23.5% ≤ 60 → U+2591 ░ (low)
assert hud.format_rate_limits(stdin) == "RL:5h\u259124%"

def test_both_limits(self):
stdin = {"rate_limits": {
"five_hour": {"used_percentage": 10},
"seven_day": {"used_percentage": 40},
}}
result = hud.format_rate_limits(stdin)
assert "5h:10%" in result
assert "7d:40%" in result
# Wave 1-C: severity icon replaces colon; space replaces comma
assert "5h\u259110%" in result
assert "7d\u259140%" in result


class TestFormatWorktree:
Expand Down Expand Up @@ -543,7 +546,8 @@ def test_rate_limits_shown(self):
}}
result = hud.format_status_line(stdin, {})
assert "RL:" in result
assert "5h:50%" in result
# Wave 1-C: severity icon replaces colon (50% ≤ 60 → ░ low)
assert "5h\u259150%" in result

def test_worktree_shown(self):
stdin = {"worktree": {"name": "feat-x"}}
Expand Down
Loading
Loading