-
-
Notifications
You must be signed in to change notification settings - Fork 317
feat(statusline): Add visual burn rate indicators to statusline for improved accessibility #516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(statusline): Add visual burn rate indicators to statusline for improved accessibility #516
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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 failedThe pull request is closed. WalkthroughAdds 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
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}"
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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-rateoption 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
-
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. ↩
There was a problem hiding this 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.
src/commands/statusline.ts
Outdated
| const renderEmojiStatus = ctx.values.visualBurnRate === 'emoji' || ctx.values.visualBurnRate === 'emoji-text'; | ||
| const renderTextStatus = ctx.values.visualBurnRate === 'text' || ctx.values.visualBurnRate === 'emoji-text'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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'); |
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
📒 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
Formattertype 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 constassertion 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
visualBurnRateoption 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 neededGunshi'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 Argsand statusline'svisualBurnRateis declared withas constchoices, soctx.values.visualBurnRateis 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.
src/commands/statusline.ts
Outdated
| const burnStatus: BurnStatus = burnRate.tokensPerMinuteForIndicator < 2000 | ||
| ? 'normal' | ||
| : burnRate.tokensPerMinuteForIndicator < 5000 | ||
| ? pc.yellow(costPerHourStr) // Moderate | ||
| : pc.red(costPerHourStr); // High | ||
| ? 'moderate' | ||
| : 'high'; |
There was a problem hiding this comment.
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 -C3Length 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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*
|
this looks good! I'll review it after my travel |
…rn-rate-to-statusline
|
Resolved conflicts with new changes to main |
|
/gemini review |
There was a problem hiding this 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.
There was a problem hiding this 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 mismatchCurrent 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 examplesThese 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)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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!
commit: |
There was a problem hiding this 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 VisualBurnRateusingvisualBurnRateChoices.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.
📒 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.
|
Thank you @ryoppippi! |
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:
Solution
This PR adds a new --visual-burn-rate option that provides alternative visual indicators beyond color:
Changes
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
This enhancement improves accessibility while maintaining full backward compatibility.
Summary by CodeRabbit
New Features
Documentation