Skip to content

Conversation

@VladyslavKochetkov
Copy link
Contributor

@VladyslavKochetkov VladyslavKochetkov commented Aug 15, 2025

Problem

The statusline command uses color-only indicators to show burn rate status (green for normal, yellow for moderate, red for high).
This creates accessibility issues in environments where:

  • Shells don't support color output
  • Color output is standardized/stripped
  • Users have color vision deficiencies
  • Terminal configurations don't display colors properly

Solution

This PR adds a new --visual-burn-rate option that provides alternative visual indicators beyond color:

  • off (default): Color-only output (maintains backward compatibility)
  • emoji: Adds emoji indicators (🟢 Normal, ⚠️ Moderate, 🚨 High)
  • text: Adds text labels (Normal), (Moderate), (High)
  • emoji-text: Combines both emoji and text indicators

Changes

  • Added visualBurnRate argument with enum choices and short flag -vb
  • Refactored burn rate display logic to support multiple visual indicators
  • Maintains existing color coding while adding optional visual enhancements
  • Added proper TypeScript typing for burn status mappings

Examples

Default behavior (color only)

bun run start statusline

Add emoji indicators

bun run start statusline --visual-burn-rate emoji

Add text indicators

bun run start statusline --visual-burn-rate text

Add both emoji and text

bun run start statusline --visual-burn-rate emoji-text

Output Examples

  • Color only: 🔥 $2.50/hr (with color)
  • With emoji: 🔥 $2.50/hr 🟢
  • With text: 🔥 $2.50/hr (Normal)
  • With both: 🔥 $2.50/hr 🟢 (Normal)

This enhancement improves accessibility while maintaining full backward compatibility.

Summary by CodeRabbit

  • New Features

    • Added a CLI option to control burn-rate visualization in the status line: off (default), emoji, text, or emoji-text (short flag: -vb).
    • Burn rate now classifies usage into Normal, Moderate, or High and displays colored indicators (green/yellow/red) with optional emoji and/or text alongside cost-per-hour.
  • Documentation

    • Added a Visual Burn Rate section with examples; content was inserted twice (duplicate copies).

@coderabbitai
Copy link

coderabbitai bot commented Aug 15, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a new CLI argument --visual-burn-rate (choices: off, emoji, text, emoji-text), enables kebab-case flags, implements runtime burn-rate visualization (emoji/text/emoji-text with colored labels), updates config schema, and adds documentation (duplicated insertion of Visual Burn Rate section).

Changes

Cohort / File(s) Summary
Statusline Command
src/commands/statusline.ts
Added visualBurnRate enum arg (['off','emoji','text','emoji-text'], default off, short -vb, negatable: false, toKebab: true) and enabled top-level toKebab. Implemented burn-rate visualization: derive renderEmojiStatus/renderTextStatus, BurnStatus classification (normal/moderate/high), burnStatusMappings (emoji, label, colored formatter), compose burnRateOutputSegments, and format the "
Configuration Schema
config-schema.json
Added visualBurnRate property under statusline: string enum ["off","emoji","text","emoji-text"], default "off", with description/markdownDescription "Controls the visualization of the burn rate status".
Documentation
docs/guide/statusline.md
Added "Visual Burn Rate" section, examples showing --visual-burn-rate modes (off, emoji, text, emoji-text), updated output format bullet, and inserted the Visual Burn Rate content in two places (duplicate).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI as "CLI Parser"
  participant Cmd as "statuslineCommand"
  participant Renderer as "BurnRate Renderer"
  participant Out as "Console"

  User->>CLI: run statusline --visual-burn-rate=<mode>
  CLI->>Cmd: invoke with visualBurnRate value
  Cmd->>Cmd: load data & compute tokensPerMinuteForIndicator
  Cmd->>Renderer: determine BurnStatus (normal/moderate/high)
  Renderer-->>Cmd: return emoji, text label, colored formatter
  Cmd->>Out: print statusline including " | 🔥 {segments}"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

I nibble numbers, whiskers bright,
Flames and symbols in the night,
Emoji hops and labels clear,
I show the burn you need to hear,
Hop on—status gleams with light! 🐇🔥


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 891b011 and 5af4693.

📒 Files selected for processing (1)
  • config-schema.json (1 hunks)
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @VladyslavKochetkov, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the statusline command by introducing new visual indicators for burn rate, directly addressing accessibility issues present with the previous color-only display. The update provides a --visual-burn-rate option, allowing users to select between color-only, emoji, text, or combined emoji-text indicators. This change ensures that burn rate information is accessible in diverse environments and for users with various needs, all while maintaining full backward compatibility with existing functionality.

Highlights

  • Enhanced Accessibility: Introduced a new --visual-burn-rate option to provide alternative visual indicators for burn rate, addressing accessibility concerns where color-only indicators might be insufficient.
  • Flexible Display Options: Users can now choose from 'off' (maintaining color-only output), 'emoji', 'text', or 'emoji-text' to customize how burn rate status is displayed.
  • Refactored Display Logic: The internal logic for displaying burn rate has been refactored to support the new visual options while ensuring existing color coding is maintained and backward compatibility is preserved.
  • Improved Type Safety: Proper TypeScript typing has been added for burn status mappings, improving the robustness and maintainability of the code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a valuable accessibility feature by adding visual indicators for burn rate in the statusline. The implementation is well-structured, particularly the use of a mapping object for different burn rate statuses, which improves code clarity and maintainability. The new command-line option is clearly defined and functions as described. I have one minor suggestion to further simplify a small piece of logic, but overall this is a solid contribution.

Comment on lines 155 to 156
const renderEmojiStatus = ctx.values.visualBurnRate === 'emoji' || ctx.values.visualBurnRate === 'emoji-text';
const renderTextStatus = ctx.values.visualBurnRate === 'text' || ctx.values.visualBurnRate === 'emoji-text';
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic to determine renderEmojiStatus and renderTextStatus is correct, but it can be made more concise and maintainable by using String.prototype.includes(). Since the visualBurnRate choices are 'emoji', 'text', and 'emoji-text', checking for the inclusion of these substrings achieves the same result with less code and would be easier to extend if more combinations were added in the future.

Suggested change
const renderEmojiStatus = ctx.values.visualBurnRate === 'emoji' || ctx.values.visualBurnRate === 'emoji-text';
const renderTextStatus = ctx.values.visualBurnRate === 'text' || ctx.values.visualBurnRate === 'emoji-text';
const renderEmojiStatus = ctx.values.visualBurnRate.includes('emoji');
const renderTextStatus = ctx.values.visualBurnRate.includes('text');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ryoppippi I'd be happy to add this feedback, but I feel like it depends on how you imagine this going forward or if you have suggestions on changing emoji-text to both. If we want to do something like text-short to simply show L/M/H for low, moderate, or high in the future this wouldn't work.

Copy link

@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: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a32a322 and 192afc3.

📒 Files selected for processing (1)
  • src/commands/statusline.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Use tab indentation
Use double quotes for strings
Do not use console.log except where explicitly disabled; use logger.ts instead
File paths must use Node.js path utilities for cross-platform compatibility
Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
Use Result.try() to wrap operations that may throw (e.g., JSON parsing)
Use Result.isFailure() and Result.isSuccess() type guards instead of negations
Prefer early return pattern over ternary operators when handling Result
Keep traditional try-catch only for file I/O with complex handling or legacy code that's hard to refactor
For async operations, create a wrapper function with Result.try() and call it
Variables use camelCase (e.g., usageDataSchema, modelBreakdownSchema)
Types use PascalCase (e.g., UsageData, ModelBreakdown)
Constants may use UPPER_SNAKE_CASE (e.g., DEFAULT_CLAUDE_CODE_PATH)
Use in-source testing blocks guarded by if (import.meta.vitest != null); do not create separate test files

Files:

  • src/commands/statusline.ts
**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.ts: Only export constants, functions, and types that are used by other modules; keep private values non-exported
Dynamic imports using await import() should only appear within in-source test blocks
Vitest globals (describe, it, expect) are available automatically without imports
Use fs-fixture createFixture() for mock data in tests that simulate Claude data directories
Model names in tests must use current Claude 4 models (Sonnet and Opus) and cover both for test coverage
Model naming must follow claude-{model-type}-{generation}-{date} (e.g., claude-sonnet-4-20250514)
Model names in tests must exactly match LiteLLM pricing database; verify existence before adding new tests
Cost calculations should only target models supported by LiteLLM; unsupported models cannot be calculated

Files:

  • src/commands/statusline.ts
🧬 Code Graph Analysis (1)
src/commands/statusline.ts (2)
src/_utils.ts (1)
  • formatCurrency (302-304)
src/_live-rendering.ts (8)
  • getRateIndicator (26-40)
  • burnRate (556-564)
  • burnRate (526-534)
  • burnRate (576-584)
  • useShort (266-273)
  • burnRate (516-524)
  • burnRate (536-544)
  • burnRate (546-554)
🪛 ESLint
src/commands/statusline.ts

[error] 155-155: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 155-155: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 156-156: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 156-156: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 169-169: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 169-169: Unsafe member access .green on an error typed value.

(ts/no-unsafe-member-access)


[error] 170-170: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 170-170: Unsafe member access .yellow on an error typed value.

(ts/no-unsafe-member-access)


[error] 171-171: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 171-171: Unsafe member access .red on an error typed value.

(ts/no-unsafe-member-access)


[error] 174-174: Unsafe object destructuring of a property with an error typed value.

(ts/no-unsafe-assignment)


[error] 176-178: Unsafe assignment of type any[] to a variable of type string[].

(ts/no-unsafe-assignment)


[error] 177-177: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 185-185: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 185-185: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)

🔇 Additional comments (6)
src/commands/statusline.ts (6)

1-1: Import type properly declared for TypeScript safety.

The Formatter type import from picocolors is correctly used for typing the color functions in the burn status mappings.


30-31: Good use of const assertion for type safety.

The as const assertion ensures the choices array is treated as a readonly tuple, providing better type inference for the enum values.


40-48: Well-structured CLI option definition.

The visualBurnRate option is properly configured with appropriate choices, default value, and kebab-case conversion for CLI usage.


168-186: Excellent visual indicator implementation with accessibility focus.

The implementation properly supports multiple visual modes (emoji, text, emoji-text) and maintains color coding for accessibility. The logic correctly handles the different combinations as specified in the PR objectives.


176-188: Clean output formatting with proper segment assembly.

The burn rate output segments are properly constructed and joined, maintaining the existing format while adding the new visual indicators. The pipe separator and fire emoji maintain consistency with the existing statusline format.


155-156: Incorrect — gunshi already types ctx.values; no change needed

Gunshi's define() types run(ctx) as Readonly<CommandContext> and ctx.values is derived from the command args (ArgValues<...>). In this repo sharedArgs uses as const satisfies Args and statusline's visualBurnRate is declared with as const choices, so ctx.values.visualBurnRate is correctly typed — the original claim of a framework typing bug is incorrect.

Checked locations:

  • src/commands/statusline.ts — usage at lines 155–156 is safe (visualBurnRate defined in the command args).
  • src/_shared-args.ts — sharedArgs declared as const satisfies Args, ensuring typed values across commands.

Likely an incorrect or invalid review comment.

Comment on lines 162 to 166
const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator < 2000
? 'normal'
: burnRate.tokensPerMinuteForIndicator < 5000
? pc.yellow(costPerHourStr) // Moderate
: pc.red(costPerHourStr); // High
? 'moderate'
: 'high';
Copy link

@coderabbitai coderabbitai bot Aug 15, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify burn rate threshold consistency with existing code.

The thresholds used here (2000 and 5000 tokens per minute) should match the constants used in _live-rendering.ts to ensure consistency across the application.


🏁 Script executed:

#!/bin/bash
# Check for burn rate threshold constants in the codebase
rg -n "BURN_RATE_THRESHOLDS|2000|5000" --type=ts -C3

Length of output: 13911


Mismatch in burn-rate thresholds — statusline.ts is using hardcoded 2000/5000 and needs to use BURN_RATE_THRESHOLDS (and the same comparison direction) from _consts.ts

The hardcoded checks in src/commands/statusline.ts (lines 162–166) do not match the canonical constants in src/_consts.ts (HIGH = 1000, MODERATE = 500) and the comparison logic used in src/_live-rendering.ts. Update statusline.ts to use the constants and the same ">" logic.

Files to update:

  • src/commands/statusline.ts — lines ~162–166 (replace hardcoded thresholds and comparison direction)
  • src/_consts.ts — lines ~138–141 (BURN_RATE_THRESHOLDS: HIGH = 1000, MODERATE = 500)
  • src/_live-rendering.ts — lines ~33–38 (reference implementation / canonical logic)

Suggested replacement (keep import if not already present):

Original:

const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator < 2000
	? 'normal'
	: burnRate.tokensPerMinuteForIndicator < 5000
		? 'moderate'
		: 'high';

Proposed:

// add import if missing (adjust relative path if required)
import { BURN_RATE_THRESHOLDS } from '../_consts.ts';

const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.HIGH
	? 'high'
	: burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.MODERATE
		? 'moderate'
		: 'normal';
🤖 Prompt for AI Agents
In src/commands/statusline.ts around lines 162–166, replace the hardcoded
2000/5000 threshold checks with the canonical BURN_RATE_THRESHOLDS from
src/_consts.ts and flip the comparison direction to use ">" like the canonical
implementation: import BURN_RATE_THRESHOLDS (adjust relative path if needed) and
compute burnStatus by checking if tokensPerMinuteForIndicator >
BURN_RATE_THRESHOLDS.HIGH then 'high', else if > BURN_RATE_THRESHOLDS.MODERATE
then 'moderate', else 'normal'; ensure the import is present and matches the
same logic used in src/_live-rendering.ts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a preexisting issue. I noticed that when creating the PR but was not sure which one to go with so I kept the values as they originally were.

Copy link

Choose a reason for hiding this comment

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

@VladyslavKochetkov Thanks for acknowledging this preexisting issue! You made a reasonable decision to keep your PR focused on the visual burn rate indicators feature rather than mixing in unrelated fixes.

This threshold inconsistency should definitely be addressed in a separate effort to ensure consistency across the codebase. Based on the usage patterns I found, it looks like the constants in _consts.ts (HIGH = 1000, MODERATE = 500) are likely the canonical values that should be used everywhere.

Would you like me to create a GitHub issue to track this inconsistency? This would help ensure it gets addressed in a future PR and doesn't get forgotten.

  (\   /)
 ( ._. )
o_(")(")  *thinking about consistency*

@ryoppippi
Copy link
Owner

this looks good! I'll review it after my travel

@VladyslavKochetkov
Copy link
Contributor Author

Resolved conflicts with new changes to main

@VladyslavKochetkov
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a valuable accessibility feature by adding visual indicators for the burn rate in the status line. The implementation is well-structured, using a mapping object that improves code clarity and maintainability. The documentation has also been updated to cover the new --visual-burn-rate option. I have a couple of minor suggestions to further improve the documentation's clarity and the code's readability.

Copy link

@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

♻️ Duplicate comments (1)
src/commands/statusline.ts (1)

288-289: Optional: simplify toggles using includes()

This is equivalent today and a bit more compact. Only consider if you don't plan to introduce overlapping substrings like "text-short" later (not blocking).

-const renderEmojiStatus = ctx.values.visualBurnRate === 'emoji' || ctx.values.visualBurnRate === 'emoji-text';
-const renderTextStatus = ctx.values.visualBurnRate === 'text' || ctx.values.visualBurnRate === 'emoji-text';
+const renderEmojiStatus = ctx.values.visualBurnRate.includes('emoji');
+const renderTextStatus = ctx.values.visualBurnRate.includes('text');
🧹 Nitpick comments (2)
src/commands/statusline.ts (1)

293-300: 5000 boundary: code vs. docs/canonical thresholds mismatch

Current code treats 5,000 tpm as High (since it’s the “else” after < 5000), while docs say High is “> 5,000” (i.e., 5,000 is Moderate). Also, there’s a known preexisting mismatch with canonical thresholds elsewhere. Not blocking for this PR, but worth aligning.

Two paths (pick one; I can open a follow-up issue if you want):

  • Keep current code and fix docs to say “≥ 5,000” and “2,000–4,999”.
  • Or switch to canonical constants and comparison direction used elsewhere.

Proposed code change (if aligning to canonical constants; otherwise ignore and just adjust docs):

-const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator < 2000
-  ? 'normal'
-  : burnRate.tokensPerMinuteForIndicator < 5000
-    ? 'moderate'
-    : 'high';
+const burnStatus: BurnStatus =
+  burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.HIGH
+    ? 'high'
+    : burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.MODERATE
+      ? 'moderate'
+      : 'normal';

If you choose this, also update the existing import to bring in the thresholds (outside this hunk):

import { BURN_RATE_THRESHOLDS, DEFAULT_REFRESH_INTERVAL_SECONDS } from "../_consts.ts";

If you prefer to keep the current logic, I recommend we update the docs’ “Output Format” thresholds to:

  • Green: Normal (< 2,000 tokens/min)
  • Yellow: Moderate (2,000–4,999 tokens/min)
  • Red: High (≥ 5,000 tokens/min)
docs/guide/statusline.md (1)

164-176: Nit: use a “text” fence for output examples

These are outputs, not shell commands. “text” (or no language) better conveys that.

-```bash
+```text
 # Default (off)
 🔥 $0.12/hr

 # With emoji
 🔥 $0.12/hr 🟢

 # With text  
 🔥 $0.12/hr (Normal)

 # With both emoji and text
 🔥 $0.12/hr 🟢 (Normal)

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

**💡 Knowledge Base configuration:**

- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 192afc34a385722b5cd5f9d8e70bde176a01d3dd and 4267df17e298d19cf9b9fb7f2b7cd689f9dfa3e5.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `docs/guide/statusline.md` (3 hunks)
* `src/commands/statusline.ts` (4 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (2)</summary>

<details>
<summary>docs/guide/*.md</summary>


**📄 CodeRabbit Inference Engine (CLAUDE.md)**

> Use image paths like "/screenshot.png" for images stored in /docs/public/ and always include descriptive alt text

Files:
- `docs/guide/statusline.md`

</details>
<details>
<summary>**/*.{ts,tsx}</summary>


**📄 CodeRabbit Inference Engine (CLAUDE.md)**

> `**/*.{ts,tsx}`: Use tab indentation and double quotes; format with ESLint
> Do not use console.log; use logger.ts instead (only allow console.log when explicitly eslint-disabled)
> Use .ts extensions for local file imports (e.g., import { foo } from "./utils.ts")
> Prefer @praha/byethrow Result type over traditional try-catch; use Result.try(), Result.isFailure()/isSuccess() and early returns; reserve try-catch for complex file I/O or legacy code
> For async operations, wrap with a function using Result.try() and call it
> Variables use camelCase; Types use PascalCase; Constants may use UPPER_SNAKE_CASE
> Only export constants/functions/types that are used by other modules; keep internal/private items non-exported
> Always use Node.js path utilities for file paths for cross-platform compatibility
> Use in-source testing blocks guarded by if (import.meta.vitest != null); do not create separate test files
> Use Vitest globals (describe, it, expect) without imports inside test blocks
> Only use dynamic imports (await import()) inside test blocks to avoid tree-shaking issues
> Mock data in tests should use fs-fixture createFixture() for Claude data directory simulation
> Tests must use current Claude 4 models (Sonnet and Opus) and cover both; do not use Claude 3 models
> Model names in tests must exactly match LiteLLM pricing database entries

Files:
- `src/commands/statusline.ts`

</details>

</details><details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>📚 Learning: 2025-07-20T23:46:21.740Z</summary>

Learnt from: AsPulse
PR: #328
File: src/_live-rendering.ts:133-167
Timestamp: 2025-07-20T23:46:21.740Z
Learning: When AsPulse points out that variables are clearly type-inferred as strings from their construction (using template literals, string methods like .toFixed(), .padStart(), etc.), avoid suggesting unnecessary type assertions that would reduce code readability. ESLint type safety errors about "error type" often indicate type definition issues with imported packages rather than actual type problems in the user's code.


**Applied to files:**
- `src/commands/statusline.ts`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/commands/statusline.ts (1)</summary><blockquote>

<details>
<summary>src/_utils.ts (1)</summary>

* `formatCurrency` (305-307)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>docs/guide/statusline.md</summary>

[grammar] ~162-~162: There might be a mistake here.
Context: ...mbine both emoji and text indicators  **Examples:**  ```bash # Default (off) 🔥 $0...

(QB_NEW_EN)

---

[grammar] ~178-~178: There might be a mistake here.
Context: ...hr 🟢 (Normal) ```  **Status Indicators:** - 🟢 Normal (Green) - ⚠️ Moderate (Yellow)...

(QB_NEW_EN)

---

[grammar] ~180-~180: There might be a mistake here.
Context: ...tus Indicators:** - 🟢 Normal (Green) - ⚠️ Moderate (Yellow) - 🚨 High (Red)  ## ...

(QB_NEW_EN)

---

[grammar] ~181-~181: There might be a mistake here.
Context: ...Normal (Green) - ⚠️ Moderate (Yellow) - 🚨 High (Red)  ## Troubleshooting  ### No ...

(QB_NEW_EN)

</details>

</details>
<details>
<summary>🪛 ESLint</summary>

<details>
<summary>src/commands/statusline.ts</summary>

[error] 288-288: Unsafe member access .values on an `any` value.

(ts/no-unsafe-member-access)

---

[error] 288-288: Unsafe member access .values on an `any` value.

(ts/no-unsafe-member-access)

---

[error] 289-289: Unsafe member access .values on an `any` value.

(ts/no-unsafe-member-access)

---

[error] 289-289: Unsafe member access .values on an `any` value.

(ts/no-unsafe-member-access)

---

[error] 302-302: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)

---

[error] 302-302: Unsafe member access .green on an `error` typed value.

(ts/no-unsafe-member-access)

---

[error] 303-303: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)

---

[error] 303-303: Unsafe member access .yellow on an `error` typed value.

(ts/no-unsafe-member-access)

---

[error] 304-304: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)

---

[error] 304-304: Unsafe member access .red on an `error` typed value.

(ts/no-unsafe-member-access)

---

[error] 307-307: Unsafe object destructuring of a property with an error typed value.

(ts/no-unsafe-assignment)

---

[error] 309-311: Unsafe assignment of type `any[]` to a variable of type `string[]`.

(ts/no-unsafe-assignment)

---

[error] 310-310: Unsafe call of a(n) `error` type typed value.

(ts/no-unsafe-call)

---

[error] 318-318: Unsafe argument of type error typed assigned to a parameter of type `string`.

(ts/no-unsafe-argument)

---

[error] 318-318: Unsafe call of a(n) `error` type typed value.

(ts/no-unsafe-call)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>src/commands/statusline.ts (4)</summary>

`71-71`: **LGTM: clear, typed choices for visual burn-rate modes**

The `as const` array gives you a nice string-literal union for downstream inference. Good call.

---

`82-90`: **CLI arg wiring looks solid and consistent with docs**

Enum choices, default `off`, `short: 'vb'`, and `toKebab: true` match the documented UX. Nice.

---

`301-308`: **LGTM: mapping typed against picocolors Formatter**

The mapping is clear and correctly typed; the picocolors formatters are appropriate here. Any “error typed value” warnings from static analyzers around `pc.*` are likely environment false-positives—no action needed.

---

`309-321`: **Rendering pipeline reads cleanly and preserves back-compat**

Base colored cost-per-hour plus optional emoji/text segments is straightforward and keeps default behavior unchanged. Good ergonomics.

</details>
<details>
<summary>docs/guide/statusline.md (1)</summary>

`47-62`: **Nice quick-start snippet for visual indicators**

Good placement and clear cross-link to the full section. Matches the new CLI flag.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

- Green text: Normal (< 2,000 tokens/min)
- Yellow text: Moderate (2,000-5,000 tokens/min)
- Red text: High (> 5,000 tokens/min)
- Optional visual status indicators (see [Visual Burn Rate](#visual-burn-rate))
Copy link

@coderabbitai coderabbitai bot Aug 18, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix threshold boundary in docs to match code (5000 is currently High in code)

The “Output Format” bullets say High is “> 5,000 tokens/min” (implying 5,000 is Moderate), but the code classifies 5,000 as High. Please update the bullets to avoid user confusion.

Suggested wording (outside this hunk):

- Green text: Normal (< 2,000 tokens/min)
- Yellow text: Moderate (2,000–4,999 tokens/min)
- Red text: High (≥ 5,000 tokens/min)

If you intend to keep the docs as-is, then the code should be adjusted to treat 5,000 as Moderate (i.e., use “> 5,000” for High). Given this PR’s scope, updating the docs is the lighter change.

🤖 Prompt for AI Agents
In docs/guide/statusline.md around line 81, the documentation describes High as
“> 5,000 tokens/min” which conflicts with the code that treats 5,000 as High;
update the bullet list to match the code by changing the ranges to: Normal (<
2,000 tokens/min), Moderate (2,000–4,999 tokens/min), and High (≥ 5,000
tokens/min). Make only the text change in the Output Format bullets to use “≥
5,000” for High (or the equivalent suggested wording) so the docs and code
boundaries align.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ryoppippi I can address this if you want in this PR but this is quite literally an edge case that users wont likely hit haha

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 19, 2025

Open in StackBlitz

npm i https://pkg.pr.new/ryoppippi/ccusage@516

commit: 5af4693

@ryoppippi ryoppippi merged commit 63e90c3 into ryoppippi:main Aug 19, 2025
7 checks passed
Copy link

@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: 0

🧹 Nitpick comments (6)
src/commands/statusline.ts (6)

72-73: Nice: explicit, closed set of choices for visualBurnRate.

Clear, forward-compatible enum. Consider exporting a local alias for reuse where needed.

Apply this diff to introduce a reusable type alias:

-const visualBurnRateChoices = ['off', 'emoji', 'text', 'emoji-text'] as const;
+const visualBurnRateChoices = ['off', 'emoji', 'text', 'emoji-text'] as const;
+type VisualBurnRate = typeof visualBurnRateChoices[number];

83-91: CLI arg looks good; verify multi-letter short flag and consider removing redundant toKebab.

  • short: 'vb' is convenient, but some parsers only allow single-char shorts. Please confirm gunshi supports multi-letter short aliases for flags.
  • You already enabled toKebab: true at the command level; repeating it here is likely redundant.

Would you confirm locally that the following variants work end-to-end?

  • bun run start statusline -vb emoji
  • bun run start statusline --visual-burn-rate emoji
  • bun run start statusline --visual-burn-rate=emoji

If -vb is not supported, fall back to a single-letter alias (e.g., -v) or omit the short flag.

Optional cleanup:

 		visualBurnRate: {
 			type: 'enum',
 			choices: visualBurnRateChoices,
 			description: 'Controls the visualization of the burn rate status',
 			default: 'off',
 			short: 'vb',
 			negatable: false,
-			toKebab: true,
 		},

294-296: Tighten types for ctx.values access to avoid ts/no-unsafe-member-access warnings.

Static analysis flags ctx.values.* as any. A minimal, local narrowing removes the noise without changing behavior.

-											const renderEmojiStatus = ctx.values.visualBurnRate === 'emoji' || ctx.values.visualBurnRate === 'emoji-text';
-											const renderTextStatus = ctx.values.visualBurnRate === 'text' || ctx.values.visualBurnRate === 'emoji-text';
+											const visualBurnRate = ctx.values.visualBurnRate as VisualBurnRate;
+											const renderEmojiStatus = visualBurnRate === 'emoji' || visualBurnRate === 'emoji-text';
+											const renderTextStatus = visualBurnRate === 'text' || visualBurnRate === 'emoji-text';

Note: I’m purposefully keeping the assertion scoped to this value to avoid bleeding assertions elsewhere. If you’d rather avoid any assertions, a small type guard like function isVisualBurnRate(x: string): x is VisualBurnRate using visualBurnRateChoices.includes(x) would also work.


299-305: Avoid hardcoded thresholds; use canonical BURN_RATE_THRESHOLDS and align comparison direction.

This block re-encodes thresholds (2000/5000) and "<" direction, which has been inconsistent with the canonical constants/logic elsewhere. Import BURN_RATE_THRESHOLDS and use the same ">" direction for consistency and single source of truth.

-											type BurnStatus = 'normal' | 'moderate' | 'high';
+											type BurnStatus = 'normal' | 'moderate' | 'high';
 
-											const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator < 2000
-												? 'normal'
-												: burnRate.tokensPerMinuteForIndicator < 5000
-													? 'moderate'
-													: 'high';
+											const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.HIGH
+												? 'high'
+												: burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.MODERATE
+													? 'moderate'
+													: 'normal';

And update the import at the top accordingly:

-import { DEFAULT_REFRESH_INTERVAL_SECONDS } from '../_consts.ts';
+import { DEFAULT_REFRESH_INTERVAL_SECONDS, BURN_RATE_THRESHOLDS } from '../_consts.ts';

If you prefer to keep this PR focused, I can open a follow-up issue to track harmonizing burn-rate thresholds across modules.


307-314: Use satisfies to keep types precise and avoid picocolors typing warnings.

Explicitly annotating the mapping with Record<..., Formatter> is tripping static analysis (pc.* seen as error-typed). The satisfies operator preserves type safety without forcing a type on pc.* functions.

-											const burnStatusMappings: Record<BurnStatus, { emoji: string; textValue: string; coloredString: Formatter }> = {
-												normal: { emoji: '🟢', textValue: 'Normal', coloredString: pc.green },
-												moderate: { emoji: '⚠️', textValue: 'Moderate', coloredString: pc.yellow },
-												high: { emoji: '🚨', textValue: 'High', coloredString: pc.red },
-											};
+											const burnStatusMappings = {
+												normal: { emoji: '🟢', textValue: 'Normal', coloredString: pc.green },
+												moderate: { emoji: '⚠️', textValue: 'Moderate', coloredString: pc.yellow },
+												high: { emoji: '🚨', textValue: 'High', coloredString: pc.red },
+											} satisfies Record<BurnStatus, { emoji: string; textValue: string; coloredString: (s: string) => string }>;

This keeps the same runtime behavior, eliminates the "error type" cascade, and lets you drop the dedicated Formatter import.


1-1: Remove now-unused Formatter import if you adopt the satisfies refactor.

If you switch the mapping to use satisfies with a simple string->string function type, this import becomes unused.

-import type { Formatter } from 'picocolors/types';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4267df1 and 891b011.

📒 Files selected for processing (1)
  • src/commands/statusline.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Use tab indentation and double quotes; format with ESLint
Do not use console.log; use logger.ts instead (only allow console.log when explicitly eslint-disabled)
Use .ts extensions for local file imports (e.g., import { foo } from "./utils.ts")
Prefer @praha/byethrow Result type over traditional try-catch; use Result.try(), Result.isFailure()/isSuccess() and early returns; reserve try-catch for complex file I/O or legacy code
For async operations, wrap with a function using Result.try() and call it
Variables use camelCase; Types use PascalCase; Constants may use UPPER_SNAKE_CASE
Only export constants/functions/types that are used by other modules; keep internal/private items non-exported
Always use Node.js path utilities for file paths for cross-platform compatibility
Use in-source testing blocks guarded by if (import.meta.vitest != null); do not create separate test files
Use Vitest globals (describe, it, expect) without imports inside test blocks
Only use dynamic imports (await import()) inside test blocks to avoid tree-shaking issues
Mock data in tests should use fs-fixture createFixture() for Claude data directory simulation
Tests must use current Claude 4 models (Sonnet and Opus) and cover both; do not use Claude 3 models
Model names in tests must exactly match LiteLLM pricing database entries

Files:

  • src/commands/statusline.ts
🧠 Learnings (1)
📚 Learning: 2025-07-20T23:46:21.740Z
Learnt from: AsPulse
PR: ryoppippi/ccusage#328
File: src/_live-rendering.ts:133-167
Timestamp: 2025-07-20T23:46:21.740Z
Learning: When AsPulse points out that variables are clearly type-inferred as strings from their construction (using template literals, string methods like .toFixed(), .padStart(), etc.), avoid suggesting unnecessary type assertions that would reduce code readability. ESLint type safety errors about "error type" often indicate type definition issues with imported packages rather than actual type problems in the user's code.

Applied to files:

  • src/commands/statusline.ts
🧬 Code Graph Analysis (1)
src/commands/statusline.ts (1)
src/_utils.ts (1)
  • formatCurrency (308-310)
🪛 ESLint
src/commands/statusline.ts

[error] 294-294: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 294-294: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 295-295: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 295-295: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 308-308: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 308-308: Unsafe member access .green on an error typed value.

(ts/no-unsafe-member-access)


[error] 309-309: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 309-309: Unsafe member access .yellow on an error typed value.

(ts/no-unsafe-member-access)


[error] 310-310: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 310-310: Unsafe member access .red on an error typed value.

(ts/no-unsafe-member-access)


[error] 313-313: Unsafe object destructuring of a property with an error typed value.

(ts/no-unsafe-assignment)


[error] 315-317: Unsafe assignment of type any[] to a variable of type string[].

(ts/no-unsafe-assignment)


[error] 316-316: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 324-324: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 324-324: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)

🔇 Additional comments (1)
src/commands/statusline.ts (1)

315-327: LGTM on the output assembly.

  • Clean separation of segments and conditional inclusion for emoji/text.
  • Coloring cost per hour and the optional label reads well; join spacing is consistent.

@VladyslavKochetkov
Copy link
Contributor Author

Thank you @ryoppippi!

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.

2 participants