-
-
Notifications
You must be signed in to change notification settings - Fork 317
feat: add statusline command for Claude Code status bar integration #450
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
Conversation
Adds a new statusline command that displays compact usage information designed for Claude Code status line feature. Features: - Shows current session cost, today total, and active block status - Color-coded burn rate indicator (green/yellow/red) using picocolors - Reads session data from stdin as per Claude Code hook protocol - Compact single-line output optimized for status bar display - Test scripts and dummy test file included for easy testing Output format: 🤖 Model | 💰 $X.XX session / $X.XX today / $X.XX block (Xh Xm left) | 🔥 $X.XX/hr This is a Beta feature with more customization options planned.
|
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. WalkthroughA new "Statusline Integration" feature was introduced, including a CLI subcommand and supporting documentation. The implementation adds schema definitions, a new command for generating a compact status line, updates to usage instructions in documentation, and new npm scripts and test fixtures to support and demonstrate the feature. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI (statusline)
participant Stdin (JSON)
participant DataLoader
participant Output
User->>CLI (statusline): Run `ccusage statusline` (receives hook JSON)
Stdin (JSON)->>CLI (statusline): Provide statusline hook event JSON
CLI (statusline)->>CLI (statusline): Validate and parse JSON
CLI (statusline)->>DataLoader: Load session, daily, and block data
DataLoader-->>CLI (statusline): Return usage statistics
CLI (statusline)->>CLI (statusline): Compute costs, burn rate, format status line
CLI (statusline)->>Output: Print compact status line
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Possibly related PRs
Poem
Note 🔌 MCP (Model Context Protocol) integration is now available in Early Access!Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context. 📜 Recent review detailsConfiguration used: CodeRabbit UI ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (5)
✨ 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 comments)
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 @ryoppippi, 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!
I've introduced a new statusline command to integrate ccusage with the Claude Code status bar. This command provides real-time, compact usage metrics directly in the status line, helping users monitor their Claude Code session costs, daily totals, 5-hour billing block status, and burn rate at a glance.
Highlights
- New statusline Command: A new command ccusage statusline has been added to display real-time Claude Code usage metrics.
- Real-time Metrics Display: The command shows current session cost, daily total cost, active 5-hour billing block status (cost and time remaining), and a color-coded burn rate.
- Claude Code Integration: Designed to work with Claude Code's status line hooks, reading session data as JSON from stdin.
- Color-Coded Burn Rate: The burn rate indicator is color-coded (green, yellow, red) based on token consumption, providing quick visual feedback.
- Comprehensive Documentation: New documentation (docs/guide/statusline.md) has been added detailing setup, output format, components, and troubleshooting for the statusline feature.
- Test Scripts: New npm scripts (test:statusline, test:statusline:dev) and a dummy JSON test file (test/statusline-test.json) were added to facilitate testing.
- Beta Feature: This new functionality is currently in beta, with plans for future enhancements like custom format templates and configurable thresholds.
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 or fill out our survey to provide feedback.
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. ↩
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ccusage-guide | 3ce1365 | Commit Preview URL Branch Preview URL |
Aug 09 2025, 12:57 AM |
commit: |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
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 new statusline command, a great feature for real-time usage monitoring in the status bar. The implementation is well-structured, with good separation of concerns and resilient error handling. I've included a few suggestions to improve robustness, maintainability, and documentation clarity. Overall, this is a solid addition to the tool.
src/commands/statusline.ts
Outdated
| async function readStdinJson(): Promise<StatuslineHookJson | null> { | ||
| return new Promise((resolve) => { | ||
| let input = ''; | ||
| process.stdin.on('data', (chunk) => { | ||
| input += String(chunk); | ||
| }); | ||
| process.stdin.on('end', () => { | ||
| try { | ||
| const data = JSON.parse(input) as unknown; | ||
| const parsed = statuslineHookJsonSchema.safeParse(data); | ||
| if (parsed.success) { | ||
| resolve(parsed.data); | ||
| } | ||
| else { | ||
| logger.error('Invalid input JSON:', parsed.error); | ||
| resolve(null); | ||
| } | ||
| } | ||
| catch (error) { | ||
| logger.error('Failed to parse JSON input:', error); | ||
| resolve(null); | ||
| } | ||
| }); | ||
| }); | ||
| } |
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 readStdinJson function can be made more robust. It currently doesn't handle cases where stdin is empty or if an error occurs on the stream, which could lead to an unhandled exception. Adding an error handler and a check for empty input would make it more resilient.
async function readStdinJson(): Promise<StatuslineHookJson | null> {
return new Promise((resolve) => {
let input = '';
process.stdin.on('data', (chunk) => {
input += String(chunk);
});
process.stdin.on('error', (err) => {
logger.error('Error reading from stdin:', err);
resolve(null);
});
process.stdin.on('end', () => {
if (input.trim() === '') {
resolve(null);
return;
}
try {
const data = JSON.parse(input) as unknown;
const parsed = statuslineHookJsonSchema.safeParse(data);
if (parsed.success) {
resolve(parsed.data);
}
else {
logger.error('Invalid input JSON:', parsed.error);
resolve(null);
}
}
catch (error) {
logger.error('Failed to parse JSON input:', error);
resolve(null);
}
});
});
}| - Green text: Normal (< 2,000 tokens/min) | ||
| - Yellow text: Moderate (2,000-5,000 tokens/min) | ||
| - Red text: High (> 5,000 tokens/min) |
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 documentation for burn rate thresholds is slightly ambiguous. For example, 2,000-5,000 tokens/min for 'Moderate' doesn't clarify if the boundaries are inclusive. The documentation would be clearer if it precisely reflected the ranges implemented in the code (< 2000, [2000, 5000), >= 5000).
| - Green text: Normal (< 2,000 tokens/min) | |
| - Yellow text: Moderate (2,000-5,000 tokens/min) | |
| - Red text: High (> 5,000 tokens/min) | |
| - Green text: Normal (< 2,000 tokens/min) | |
| - Yellow text: Moderate (>= 2,000 to < 5,000 tokens/min) | |
| - Red text: High (>= 5,000 tokens/min) |
| // Apply color based on burn rate (tokens per minute non-cache) | ||
| let coloredBurnRate = costPerHourStr; | ||
| if (burnRate.tokensPerMinuteForIndicator < 2000) { | ||
| coloredBurnRate = pc.green(costPerHourStr); // Normal | ||
| } | ||
| else if (burnRate.tokensPerMinuteForIndicator < 5000) { | ||
| coloredBurnRate = pc.yellow(costPerHourStr); // Moderate | ||
| } | ||
| else { | ||
| coloredBurnRate = pc.red(costPerHourStr); // 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.
The burn rate thresholds (2000 and 5000) are currently hardcoded as magic numbers. To improve readability and maintainability, it's best to define them as named constants. This also aligns with the plan to make them configurable in the future.
// Apply color based on burn rate (tokens per minute non-cache)
const BURN_RATE_NORMAL_THRESHOLD = 2000;
const BURN_RATE_MODERATE_THRESHOLD = 5000;
let coloredBurnRate = costPerHourStr;
if (burnRate.tokensPerMinuteForIndicator < BURN_RATE_NORMAL_THRESHOLD) {
coloredBurnRate = pc.green(costPerHourStr); // Normal
}
else if (burnRate.tokensPerMinuteForIndicator < BURN_RATE_MODERATE_THRESHOLD) {
coloredBurnRate = pc.yellow(costPerHourStr); // Moderate
}
else {
coloredBurnRate = pc.red(costPerHourStr); // 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.
Actionable comments posted: 3
🧹 Nitpick comments (5)
src/_types.ts (1)
138-160: Reuse branded schemas for stronger validation and wire the schema into runtime parsingThe new
statuslineHookJsonSchemaduplicates validations already defined above (e.g.sessionIdSchema,modelNameSchema) and leaves several string fields completely un-validated.
Prefer composing with the existing branded schemas:export const statuslineHookJsonSchema = z.object({ hook_event_name: z.literal('Status'), session_id: sessionIdSchema, transcript_path: projectPathSchema, // if path-like cwd: projectPathSchema, model: z.object({ id: modelNameSchema, display_name: z.string().min(1), }), workspace: z.object({ current_dir: projectPathSchema, project_dir: projectPathSchema, }), });In addition, make sure the CLI actually calls
statuslineHookJsonSchema.safeParse()on the stdin payload so type-safety isn’t lost downstream.src/commands/statusline.ts (3)
137-149: Active-block detection picks first trueisActive– could mis-reportIf multiple blocks are marked active (data corruption / overlap), you’ll get an arbitrary block.
Consider:const activeBlock = blocks .filter(b => b.isActive) .sort((a,b) => a.startTime.getTime() - b.startTime.getTime())[0];or validate uniqueness earlier.
150-156: Clamp negative remaining time
remainingcan be negative for just-ended blocks, resulting in “-3m left”. Guard with:const remaining = Math.max(0, Math.round(/* … */));
160-173: Color thresholds magic numbersExtract
2000&5000to named constants for clarity / future config.docs/guide/statusline.md (1)
21-28: Inline comment invalidates JSON snippet
// Optional: …makes this block non-parsable JSON.
Either switch the fence tojsoncor remove the comment to avoid copy-paste errors.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
CLAUDE.md(1 hunks)README.md(2 hunks)docs/guide/statusline.md(1 hunks)package.json(1 hunks)src/_types.ts(1 hunks)src/commands/index.ts(2 hunks)src/commands/statusline.ts(1 hunks)test/statusline-test.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Lint code using ESLint MCP server (available via Claude Code tools)
Format code with ESLint (writes changes) usingbun run format
No console.log allowed except where explicitly disabled with eslint-disable
Do not use console.log. Use logger.ts instead.
Files:
src/commands/index.tssrc/commands/statusline.tssrc/_types.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Type check with TypeScript using
bun typecheck
Files:
src/commands/index.tssrc/commands/statusline.tssrc/_types.ts
**/*.ts
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.ts: File paths always use Node.js path utilities for cross-platform compatibility
Use.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
UseResult.try()for wrapping operations that may throw (JSON parsing, etc.)
UseResult.isFailure()for checking errors (more readable than!Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators
For async operations: create wrapper function withResult.try()then call it
Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
Always useResult.isFailure()andResult.isSuccess()type guards for better code clarity
Variables: start with lowercase (camelCase) - e.g.,usageDataSchema,modelBreakdownSchema
Types: start with uppercase (PascalCase) - e.g.,UsageData,ModelBreakdown
Constants: can use UPPER_SNAKE_CASE - e.g.,DEFAULT_CLAUDE_CODE_PATH
Only export constants, functions, and types that are actually used by other modules
Internal/private constants that are only used within the same file should NOT be exported
Always check if a constant is used elsewhere before making itexport constvs justconst
All test files must use current Claude 4 models, not outdated Claude 3 models
Test coverage should include both Sonnet and Opus models for comprehensive validation
Model names in tests must exactly match LiteLLM's pricing database entries
When adding new model tests, verify the model exists in LiteLLM before implementation
Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
Dynamic imports usingawait import()should only be used within test blocks to avoid tree-shaking issues
Mock data is created usingfs-fixturewithcreateFixture()for Claude data directory simulation
In-source testing pattern: Tests are written...
Files:
src/commands/index.tssrc/commands/statusline.tssrc/_types.ts
package.json
📄 CodeRabbit Inference Engine (CLAUDE.md)
Dependencies should always be added as devDependencies unless explicitly requested otherwise
Files:
package.json
**/_*.ts
📄 CodeRabbit Inference Engine (CLAUDE.md)
Internal files: use underscore prefix - e.g.,
_types.ts,_utils.ts,_consts.ts
Files:
src/_types.ts
docs/**/*.md
📄 CodeRabbit Inference Engine (CLAUDE.md)
docs/**/*.md: Screenshot images in documentation should use relative paths like/screenshot.pngfor images stored in/docs/public/
Always include descriptive alt text for screenshots in documentation for accessibility
Always place screenshots immediately after the main heading (H1) in documentation pages
Provide immediate visual context to users before textual explanations by placing screenshots early in documentation
Files:
docs/guide/statusline.md
🧠 Learnings (9)
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Lint code using ESLint MCP server (available via Claude Code tools)
Applied to files:
src/commands/index.ts
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Format code with ESLint (writes changes) using `bun run format`
Applied to files:
package.json
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.{ts,tsx} : Type check with TypeScript using `bun typecheck`
Applied to files:
package.json
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Vitest globals (`describe`, `it`, `expect`) are available automatically without imports in test blocks
Applied to files:
package.json
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : In-source testing pattern: Tests are written directly in the same files as the source code, not in separate test files, using `if (import.meta.vitest != null)` blocks
Applied to files:
package.json
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Variables: start with lowercase (camelCase) - e.g., `usageDataSchema`, `modelBreakdownSchema`
Applied to files:
src/_types.ts
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Types: start with uppercase (PascalCase) - e.g., `UsageData`, `ModelBreakdown`
Applied to files:
src/_types.ts
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Model naming convention: Pattern is `claude-{model-type}-{generation}-{date}` (e.g., `claude-sonnet-4-20250514`), not `claude-4-sonnet-20250514`
Applied to files:
src/_types.ts
📚 Learning: 2025-07-19T10:58:04.397Z
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Mock data is created using `fs-fixture` with `createFixture()` for Claude data directory simulation
Applied to files:
src/_types.ts
🧬 Code Graph Analysis (1)
src/commands/index.ts (1)
src/commands/statusline.ts (1)
statuslineCommand(58-195)
🪛 ESLint
src/commands/statusline.ts
[error] 20-20: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 20-20: Unsafe member access .stdin on an error typed value.
(ts/no-unsafe-member-access)
[error] 23-23: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 23-23: Unsafe member access .stdin on an error typed value.
(ts/no-unsafe-member-access)
[error] 26-26: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 26-26: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 26-26: Unsafe member access .safeParse on an error typed value.
(ts/no-unsafe-member-access)
[error] 27-27: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 27-27: Unsafe member access .success on an error typed value.
(ts/no-unsafe-member-access)
[error] 28-28: Unsafe member access .data on an error typed value.
(ts/no-unsafe-member-access)
[error] 31-31: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 31-31: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 31-31: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 36-36: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 36-36: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 58-195: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 58-58: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 64-64: Unsafe member access .level on an error typed value.
(ts/no-unsafe-member-access)
[error] 67-67: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 69-69: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 70-70: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 70-70: Unsafe member access .exit on an error typed value.
(ts/no-unsafe-member-access)
[error] 76-76: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 77-77: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 77-77: Unsafe member access .exit on an error typed value.
(ts/no-unsafe-member-access)
[error] 82-82: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 82-82: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 82-82: Unsafe member access .transcript_path on an error typed value.
(ts/no-unsafe-member-access)
[error] 82-82: Unsafe member access .sep on an error typed value.
(ts/no-unsafe-member-access)
[error] 83-83: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 83-83: Unsafe member access [transcriptParts.length - 2] on an error typed value.
(ts/no-unsafe-member-access)
[error] 83-83: Unsafe member access .length on an error typed value.
(ts/no-unsafe-member-access)
[error] 93-93: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 93-93: Unsafe member access .sessionId on an error typed value.
(ts/no-unsafe-member-access)
[error] 95-95: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 95-95: Unsafe member access .totalCost on an error typed value.
(ts/no-unsafe-member-access)
[error] 99-99: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 99-99: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 120-120: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 120-120: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 164-164: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 164-164: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 164-164: Unsafe member access .green on an error typed value.
(ts/no-unsafe-member-access)
[error] 167-167: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 167-167: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 167-167: Unsafe member access .yellow 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 call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 170-170: Unsafe member access .red on an error typed value.
(ts/no-unsafe-member-access)
[error] 182-182: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 182-182: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 187-187: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 187-187: Unsafe member access .model on an error typed value.
(ts/no-unsafe-member-access)
[error] 193-193: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
src/_types.ts
[error] 142-155: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 142-142: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 142-142: Unsafe member access .object on an error typed value.
(ts/no-unsafe-member-access)
[error] 143-143: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 143-143: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 143-143: Unsafe member access .literal on an error typed value.
(ts/no-unsafe-member-access)
[error] 144-144: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 144-144: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 144-144: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
[error] 145-145: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 145-145: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 145-145: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
[error] 146-146: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 146-146: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 146-146: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
[error] 147-150: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 147-147: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 147-147: Unsafe member access .object on an error typed value.
(ts/no-unsafe-member-access)
[error] 148-148: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 148-148: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 148-148: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
[error] 149-149: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 149-149: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 149-149: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
[error] 151-154: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 151-151: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 151-151: Unsafe member access .object on an error typed value.
(ts/no-unsafe-member-access)
[error] 152-152: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 152-152: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 152-152: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
[error] 153-153: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 153-153: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 153-153: Unsafe member access .string on an error typed value.
(ts/no-unsafe-member-access)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Workers Builds: ccusage-guide
🔇 Additional comments (4)
CLAUDE.md (1)
25-25: Entry looks good
statuslinecommand is correctly listed under development usage.src/commands/index.ts (1)
9-10: Subcommand registration OK
statuslineCommandis properly imported and registered; nothing else needed.Also applies to: 22-22
README.md (1)
58-58: Documentation update looks correctREADME now advertises the new
statuslinefeature; wording and placement are consistent with other entries.Also applies to: 83-83
src/commands/statusline.ts (1)
81-84: Session-ID extraction is brittleRelying on
split(path.sep)[len-2]fails if transcript location changes.
Usepath.dirname()twice or embed the sessionId in the hook payload (preferred).
| "test:statusline": "cat test/statusline-test.json | bun run ./src/index.ts statusline", | ||
| "test:statusline:dev": "cat test/statusline-test.json | bun run start statusline", |
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.
🛠️ Refactor suggestion
*Scripts are nix-only; break on Windows shells
cat … | bun run … relies on POSIX cat.
For cross-platform dev-scripts, prefer shell-agnostic redirection:
-"test:statusline": "cat test/statusline-test.json | bun run ./src/index.ts statusline",
-"test:statusline:dev": "cat test/statusline-test.json | bun run start statusline",
+"test:statusline": "bun run ./src/index.ts statusline < test/statusline-test.json",
+"test:statusline:dev": "bun run start statusline < test/statusline-test.json",📝 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.
| "test:statusline": "cat test/statusline-test.json | bun run ./src/index.ts statusline", | |
| "test:statusline:dev": "cat test/statusline-test.json | bun run start statusline", | |
| "test:statusline": "bun run ./src/index.ts statusline < test/statusline-test.json", | |
| "test:statusline:dev": "bun run start statusline < test/statusline-test.json", |
🤖 Prompt for AI Agents
In package.json lines 55 to 56, the test scripts use 'cat' piped into 'bun run',
which relies on POSIX 'cat' and breaks on Windows shells. Replace the pipe with
shell-agnostic input redirection by using '<' to feed the file into the command,
ensuring cross-platform compatibility.
src/commands/statusline.ts
Outdated
| async function readStdinJson(): Promise<StatuslineHookJson | null> { | ||
| return new Promise((resolve) => { | ||
| let input = ''; | ||
| process.stdin.on('data', (chunk) => { | ||
| input += String(chunk); | ||
| }); | ||
| process.stdin.on('end', () => { | ||
| try { | ||
| const data = JSON.parse(input) as unknown; | ||
| const parsed = statuslineHookJsonSchema.safeParse(data); | ||
| if (parsed.success) { | ||
| resolve(parsed.data); | ||
| } | ||
| else { | ||
| logger.error('Invalid input JSON:', parsed.error); | ||
| resolve(null); | ||
| } | ||
| } | ||
| catch (error) { | ||
| logger.error('Failed to parse JSON input:', error); | ||
| resolve(null); | ||
| } | ||
| }); | ||
| }); | ||
| } |
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.
🛠️ Refactor suggestion
Use Result.try() instead of manual try/catch for JSON parsing
Guidelines mandate functional error handling:
- process.stdin.on('end', () => {
- try {
- const data = JSON.parse(input) as unknown;
- const parsed = statuslineHookJsonSchema.safeParse(data);
- if (parsed.success) {
- resolve(parsed.data);
- }
- else {
- logger.error('Invalid input JSON:', parsed.error);
- resolve(null);
- }
- }
- catch (error) {
- logger.error('Failed to parse JSON input:', error);
- resolve(null);
- }
- });
+ process.stdin.on('end', () => {
+ const parsed = Result.try(() => JSON.parse(input))
+ .andThen(json => Result.fromZod(statuslineHookJsonSchema, json));
+
+ if (Result.isFailure(parsed)) {
+ logger.error('Invalid input JSON:', parsed.error);
+ return resolve(null);
+ }
+ resolve(parsed.value);
+ });This satisfies the “prefer Result.try() over try/catch” rule and removes most no-unsafe-* ESLint noise.
🧰 Tools
🪛 ESLint
[error] 20-20: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 20-20: Unsafe member access .stdin on an error typed value.
(ts/no-unsafe-member-access)
[error] 23-23: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 23-23: Unsafe member access .stdin on an error typed value.
(ts/no-unsafe-member-access)
[error] 26-26: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 26-26: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 26-26: Unsafe member access .safeParse on an error typed value.
(ts/no-unsafe-member-access)
[error] 27-27: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 27-27: Unsafe member access .success on an error typed value.
(ts/no-unsafe-member-access)
[error] 28-28: Unsafe member access .data on an error typed value.
(ts/no-unsafe-member-access)
[error] 31-31: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 31-31: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 31-31: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
[error] 36-36: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 36-36: Unsafe member access .error on an error typed value.
(ts/no-unsafe-member-access)
🤖 Prompt for AI Agents
In src/commands/statusline.ts between lines 17 and 41, replace the manual
try/catch block used for JSON parsing with the functional error handling
approach using Result.try(). This involves wrapping the JSON.parse call inside
Result.try() to handle errors functionally, then processing the result
accordingly. This change will align with the guideline to prefer Result.try()
over try/catch and reduce ESLint no-unsafe-* warnings.
| "model": { | ||
| "id": "claude-opus-4-1", | ||
| "display_name": "Opus" | ||
| }, |
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 invalid model ID in fixture
"id": "claude-opus-4-1" does not follow the required pattern claude-{model-type}-{generation}-{date} (e.g. claude-opus-4-20250514).
An invalid model name will cause cost look-ups with LiteLLM to fail, breaking tests and runtime calculations.
- "id": "claude-opus-4-1",
+ "id": "claude-opus-4-20250514",Update the fixture (and any related expectations) to a real, dated model name.
📝 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.
| "model": { | |
| "id": "claude-opus-4-1", | |
| "display_name": "Opus" | |
| }, | |
| "model": { | |
| - "id": "claude-opus-4-1", | |
| + "id": "claude-opus-4-20250514", | |
| "display_name": "Opus" | |
| }, |
🤖 Prompt for AI Agents
In test/statusline-test.json around lines 6 to 9, the model ID "claude-opus-4-1"
is invalid because it does not follow the required pattern
"claude-{model-type}-{generation}-{date}". Update the "id" field to a valid
model name that includes a proper date suffix, such as "claude-opus-4-20250514",
and ensure any related test expectations are updated accordingly to match this
corrected model ID.
Removed hook_event_name field which is not present in actual data Added optional version field Updated test file to match real data structure Replaced logger with console.log for cleaner output Removed error displays for silent failures
Summary
Features
Core Functionality
Technical Implementation
Output Format
Test Plan
Documentation
Notes
This is a Beta feature. Future enhancements planned:
Summary by CodeRabbit