Skip to content

feat: built-in status line with usage quota display#51

Open
amDosion wants to merge 1 commit intoclaude-code-best:mainfrom
amDosion:feat/status-line-clean
Open

feat: built-in status line with usage quota display#51
amDosion wants to merge 1 commit intoclaude-code-best:mainfrom
amDosion:feat/status-line-clean

Conversation

@amDosion
Copy link
Copy Markdown

@amDosion amDosion commented Apr 2, 2026

Summary

  • Add BuiltinStatusLine component with token counts, cost tracking, and usage quota display
  • Refactor StatusLine to use the built-in implementation

Files changed (2 only)

  • src/components/BuiltinStatusLine.tsx — new component
  • src/components/StatusLine.tsx — refactored to use built-in

Replaces #44 (cleaned up unrelated files).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Status line now displays model name, context usage percentage, rate-limit utilization with progress bars, reset countdowns (refreshed every 60 seconds), and costs formatted in USD.
    • Status line layout intelligently adapts to terminal width, showing or suppressing detailed information based on available space.

Add BuiltinStatusLine component with token counts, cost tracking,
and usage quota display. Refactor StatusLine to use the built-in
implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

A new BuiltinStatusLine component renders terminal status information synchronously, displaying model name, token usage, rate limits, and costs with adaptive layout. StatusLine is refactored to use this component directly, removing async command execution and telemetry workflows.

Changes

Cohort / File(s) Summary
New Status Line Component
src/components/BuiltinStatusLine.tsx
New component rendering compact terminal status with model info, token context usage, rate-limit utilization with progress bars, countdowns refreshed on 60s interval, and USD cost formatting. Includes formatCountdown() utility converting epoch timestamps to human-readable format. Layout adapts to terminal width.
Status Line Refactoring
src/components/StatusLine.tsx
Removes debounced async command execution, telemetry, and AbortController logic. Simplifies statusLineShouldDisplay to return true when not in KAIROS mode. Reworks StatusLineInner to derive model/context/cost data synchronously from app state and render BuiltinStatusLine directly. Changes vimMode prop type to unknown.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A status line hops into view,
No async commands to pursue!
With countdowns and costs on display,
Terminal telemetry fades away—
Clean rendering saves the day! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: built-in status line with usage quota display' accurately summarizes the main change: adding a new BuiltinStatusLine component with quota/usage display and refactoring StatusLine to use it.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/components/BuiltinStatusLine.tsx (1)

1-6: Use src/ path alias instead of relative imports.

The coding guidelines require using src/ path alias via tsconfig mapping instead of relative paths. These imports should be updated for consistency.

♻️ Suggested refactor
 import React, { useEffect, useState } from 'react';
-import { formatCost } from '../cost-tracker.js';
-import { Box, Text } from '../ink.js';
-import { formatTokens } from '../utils/format.js';
-import { ProgressBar } from './design-system/ProgressBar.js';
-import { useTerminalSize } from '../hooks/useTerminalSize.js';
+import { formatCost } from 'src/cost-tracker.js';
+import { Box, Text } from 'src/ink.js';
+import { formatTokens } from 'src/utils/format.js';
+import { ProgressBar } from 'src/components/design-system/ProgressBar.js';
+import { useTerminalSize } from 'src/hooks/useTerminalSize.js';

As per coding guidelines: "Import src/ path alias via tsconfig mapping instead of relative paths in imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/BuiltinStatusLine.tsx` around lines 1 - 6, Replace the
relative import paths in BuiltinStatusLine.tsx with the project tsconfig path
alias (src/...) for consistency; update imports for formatCost, Box, Text,
formatTokens, ProgressBar, and useTerminalSize to use the src/ path alias (e.g.,
import { formatCost } from 'src/…') while preserving the same exported symbols
and filenames so function/component references (formatCost, Box, Text,
formatTokens, ProgressBar, useTerminalSize) continue to resolve correctly.
src/components/StatusLine.tsx (2)

22-26: Remove unused vimMode prop.

The vimMode prop is declared but never used in StatusLineInner (line 32 doesn't destructure it). If it's no longer needed, remove it from the Props type to avoid confusion.

♻️ Suggested refactor
 type Props = {
   messagesRef: React.RefObject<Message[]>;
   lastAssistantMessageId: string | null;
-  vimMode?: unknown;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/StatusLine.tsx` around lines 22 - 26, The Props type declares
an unused vimMode property; remove vimMode from the Props definition and any
related optional typing to keep the shape consistent with StatusLineInner (which
does not destructure vimMode). Update the Props type in the file (and any local
uses of Props) so it only includes messagesRef and lastAssistantMessageId, and
run a quick search for any external references to Props or a vimMode prop usage
to remove or adjust them accordingly.

3-15: Inconsistent import style: mix of src/ alias and relative paths.

Line 4 uses the src/ path alias while the remaining imports use relative paths. Standardize on the src/ alias per coding guidelines.

♻️ Suggested refactor
 import { feature } from 'bun:bundle';
 import * as React from 'react';
 import { memo } from 'react';
 import { useAppState } from 'src/state/AppState.js';
-import { getSdkBetas, getKairosActive } from '../bootstrap/state.js';
-import { getTotalCost, getTotalInputTokens, getTotalOutputTokens } from '../cost-tracker.js';
-import { useMainLoopModel } from '../hooks/useMainLoopModel.js';
-import { type ReadonlySettings } from '../hooks/useSettings.js';
-import { getRawUtilization } from '../services/claudeAiLimits.js';
-import type { Message } from '../types/message.js';
-import { calculateContextPercentages, getContextWindowForModel } from '../utils/context.js';
-import { getLastAssistantMessage } from '../utils/messages.js';
-import { getRuntimeMainLoopModel, renderModelName } from '../utils/model/model.js';
-import { doesMostRecentAssistantMessageExceed200k, getCurrentUsage } from '../utils/tokens.js';
-import { BuiltinStatusLine } from './BuiltinStatusLine.js';
+import { getSdkBetas, getKairosActive } from 'src/bootstrap/state.js';
+import { getTotalCost, getTotalInputTokens, getTotalOutputTokens } from 'src/cost-tracker.js';
+import { useMainLoopModel } from 'src/hooks/useMainLoopModel.js';
+import { type ReadonlySettings } from 'src/hooks/useSettings.js';
+import { getRawUtilization } from 'src/services/claudeAiLimits.js';
+import type { Message } from 'src/types/message.js';
+import { calculateContextPercentages, getContextWindowForModel } from 'src/utils/context.js';
+import { getLastAssistantMessage } from 'src/utils/messages.js';
+import { getRuntimeMainLoopModel, renderModelName } from 'src/utils/model/model.js';
+import { doesMostRecentAssistantMessageExceed200k, getCurrentUsage } from 'src/utils/tokens.js';
+import { BuiltinStatusLine } from 'src/components/BuiltinStatusLine.js';

As per coding guidelines: "Import src/ path alias via tsconfig mapping instead of relative paths in imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/StatusLine.tsx` around lines 3 - 15, The import list in
StatusLine.tsx mixes the src/ alias with relative paths; update all relative
imports to use the src/ path alias to match project guidelines. Specifically
replace imports for useAppState, getSdkBetas, getKairosActive, getTotalCost,
getTotalInputTokens, getTotalOutputTokens, useMainLoopModel, ReadonlySettings,
getRawUtilization, Message type, calculateContextPercentages,
getContextWindowForModel, getLastAssistantMessage, getRuntimeMainLoopModel,
renderModelName, doesMostRecentAssistantMessageExceed200k, getCurrentUsage, and
BuiltinStatusLine so they import from 'src/...' (e.g. 'src/bootstrap/state',
'src/cost-tracker', 'src/hooks/useMainLoopModel', 'src/hooks/useSettings',
'src/services/claudeAiLimits', 'src/types/message', 'src/utils/context',
'src/utils/messages', 'src/utils/model/model', 'src/utils/tokens',
'src/components/BuiltinStatusLine') while keeping the existing named symbols
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/StatusLine.tsx`:
- Around line 36-42: Guard against messagesRef.current being null before passing
it to functions that require Message[]: update the logic around
doesMostRecentAssistantMessageExceed200k and getCurrentUsage so they receive a
non-null array (e.g., use messagesRef.current ?? [] or short-circuit when null).
Specifically, ensure the call to doesMostRecentAssistantMessageExceed200k (used
to set exceeds200kTokens) and the call to getCurrentUsage both never get a null
input by checking messagesRef.current and falling back to an empty array before
invoking those functions.

---

Nitpick comments:
In `@src/components/BuiltinStatusLine.tsx`:
- Around line 1-6: Replace the relative import paths in BuiltinStatusLine.tsx
with the project tsconfig path alias (src/...) for consistency; update imports
for formatCost, Box, Text, formatTokens, ProgressBar, and useTerminalSize to use
the src/ path alias (e.g., import { formatCost } from 'src/…') while preserving
the same exported symbols and filenames so function/component references
(formatCost, Box, Text, formatTokens, ProgressBar, useTerminalSize) continue to
resolve correctly.

In `@src/components/StatusLine.tsx`:
- Around line 22-26: The Props type declares an unused vimMode property; remove
vimMode from the Props definition and any related optional typing to keep the
shape consistent with StatusLineInner (which does not destructure vimMode).
Update the Props type in the file (and any local uses of Props) so it only
includes messagesRef and lastAssistantMessageId, and run a quick search for any
external references to Props or a vimMode prop usage to remove or adjust them
accordingly.
- Around line 3-15: The import list in StatusLine.tsx mixes the src/ alias with
relative paths; update all relative imports to use the src/ path alias to match
project guidelines. Specifically replace imports for useAppState, getSdkBetas,
getKairosActive, getTotalCost, getTotalInputTokens, getTotalOutputTokens,
useMainLoopModel, ReadonlySettings, getRawUtilization, Message type,
calculateContextPercentages, getContextWindowForModel, getLastAssistantMessage,
getRuntimeMainLoopModel, renderModelName,
doesMostRecentAssistantMessageExceed200k, getCurrentUsage, and BuiltinStatusLine
so they import from 'src/...' (e.g. 'src/bootstrap/state', 'src/cost-tracker',
'src/hooks/useMainLoopModel', 'src/hooks/useSettings',
'src/services/claudeAiLimits', 'src/types/message', 'src/utils/context',
'src/utils/messages', 'src/utils/model/model', 'src/utils/tokens',
'src/components/BuiltinStatusLine') while keeping the existing named symbols
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7a7f74a-0f25-4195-82c6-3b8eba7f9d0c

📥 Commits

Reviewing files that changed from the base of the PR and between d04e00f and 28bd4a0.

📒 Files selected for processing (2)
  • src/components/BuiltinStatusLine.tsx
  • src/components/StatusLine.tsx

Comment on lines +36 to +42
const exceeds200kTokens = lastAssistantMessageId
? doesMostRecentAssistantMessageExceed200k(messagesRef.current)
: false;

// StatusLine must have stable height in fullscreen — the footer is
// flexShrink:0 so a 0→1 row change when the command finishes steals
// a row from ScrollBox and shifts content. Reserve the row while loading
// (same trick as PromptInputFooterLeftSide).
return <Box paddingX={paddingX} gap={2}>
{statusLineText ? <Text dimColor wrap="truncate">
<Ansi>{statusLineText}</Ansi>
</Text> : isFullscreenEnvEnabled() ? <Text> </Text> : null}
</Box>;
const runtimeModel = getRuntimeMainLoopModel({ permissionMode, mainLoopModel, exceeds200kTokens });
const modelDisplay = renderModelName(runtimeModel);
const currentUsage = getCurrentUsage(messagesRef.current);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if these functions handle null/undefined arrays gracefully
ast-grep --pattern 'function doesMostRecentAssistantMessageExceed200k($_) {
  $$$
}'

ast-grep --pattern 'function getCurrentUsage($_) {
  $$$
}'

Repository: claude-code-best/claude-code

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Search for the function definitions with broader patterns
rg "doesMostRecentAssistantMessageExceed200k" -A 5
rg "getCurrentUsage" -A 5

Repository: claude-code-best/claude-code

Length of output: 9213


🏁 Script executed:

#!/bin/bash
# Read the StatusLine.tsx file to understand the context
head -50 src/components/StatusLine.tsx

Repository: claude-code-best/claude-code

Length of output: 2370


🏁 Script executed:

#!/bin/bash
# Check for messagesRef definition and its type
rg "messagesRef" src/components/StatusLine.tsx -B 2 -A 2

Repository: claude-code-best/claude-code

Length of output: 932


🏁 Script executed:

#!/bin/bash
# Find where StatusLineInner or StatusLine component is called
rg "StatusLineInner|<StatusLine" -B 3 -A 3

Repository: claude-code-best/claude-code

Length of output: 2710


🏁 Script executed:

#!/bin/bash
# Search for messagesRef creation patterns
rg "messagesRef.*useRef|useRef.*Message" -B 2 -A 2

Repository: claude-code-best/claude-code

Length of output: 5260


🏁 Script executed:

#!/bin/bash
# Check the full implementation of both functions to understand null handling
sed -n '1,30p' src/utils/tokens.ts

Repository: claude-code-best/claude-code

Length of output: 1384


Guard messagesRef.current before passing to functions expecting non-null arrays.

dossMostRecentAssistantMessageExceed200k and getCurrentUsage are typed to accept Message[], but messagesRef.current has type Message[] | null per React's RefObject typing. The second call at line 42 is unconditional with no guard. While the functions handle empty arrays, they cannot handle null input.

🛡️ Proposed defensive guard
+  const messages = messagesRef.current ?? [];
+
   const exceeds200kTokens = lastAssistantMessageId
-    ? doesMostRecentAssistantMessageExceed200k(messagesRef.current)
+    ? doesMostRecentAssistantMessageExceed200k(messages)
     : false;

   const runtimeModel = getRuntimeMainLoopModel({ permissionMode, mainLoopModel, exceeds200kTokens });
   const modelDisplay = renderModelName(runtimeModel);
-  const currentUsage = getCurrentUsage(messagesRef.current);
+  const currentUsage = getCurrentUsage(messages);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const exceeds200kTokens = lastAssistantMessageId
? doesMostRecentAssistantMessageExceed200k(messagesRef.current)
: false;
// StatusLine must have stable height in fullscreen — the footer is
// flexShrink:0 so a 0→1 row change when the command finishes steals
// a row from ScrollBox and shifts content. Reserve the row while loading
// (same trick as PromptInputFooterLeftSide).
return <Box paddingX={paddingX} gap={2}>
{statusLineText ? <Text dimColor wrap="truncate">
<Ansi>{statusLineText}</Ansi>
</Text> : isFullscreenEnvEnabled() ? <Text> </Text> : null}
</Box>;
const runtimeModel = getRuntimeMainLoopModel({ permissionMode, mainLoopModel, exceeds200kTokens });
const modelDisplay = renderModelName(runtimeModel);
const currentUsage = getCurrentUsage(messagesRef.current);
const messages = messagesRef.current ?? [];
const exceeds200kTokens = lastAssistantMessageId
? doesMostRecentAssistantMessageExceed200k(messages)
: false;
const runtimeModel = getRuntimeMainLoopModel({ permissionMode, mainLoopModel, exceeds200kTokens });
const modelDisplay = renderModelName(runtimeModel);
const currentUsage = getCurrentUsage(messages);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/StatusLine.tsx` around lines 36 - 42, Guard against
messagesRef.current being null before passing it to functions that require
Message[]: update the logic around doesMostRecentAssistantMessageExceed200k and
getCurrentUsage so they receive a non-null array (e.g., use messagesRef.current
?? [] or short-circuit when null). Specifically, ensure the call to
doesMostRecentAssistantMessageExceed200k (used to set exceeds200kTokens) and the
call to getCurrentUsage both never get a null input by checking
messagesRef.current and falling back to an empty array before invoking those
functions.

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.

1 participant