Skip to content

Conversation

@ryoppippi
Copy link
Owner

@ryoppippi ryoppippi commented Aug 9, 2025

Summary

  • Adds new command for Claude Code status bar integration
  • Shows real-time usage metrics in a compact single-line format
  • Beta feature with color-coded burn rate indicators

Features

Core Functionality

  • Session Cost Tracking: Displays current conversation session cost
  • Daily Total: Shows cumulative spending for the current day
  • Block Status: Tracks active 5-hour billing block with remaining time
  • Burn Rate: Color-coded cost per hour indicator (green/yellow/red)
  • Model Display: Shows active Claude model at the beginning

Technical Implementation

  • Reads session data from stdin as JSON (Claude Code hook protocol)
  • Uses picocolors for color-coded burn rate indicators
  • Compact output format optimized for status bar display
  • Includes test scripts and dummy test file for easy testing

Output Format

Test Plan

  • Test with dummy JSON input via 🤖 Opus | 💰 $0.00 session / $0.00 today / $30.57 block (3h 43m left) | 🔥 $37.50/hr
  • Verify color-coded burn rate indicators work correctly
  • Confirm session cost calculation from session ID
  • Test with no active block scenario
  • Run format and typecheck
  • Test with actual Claude Code integration

Documentation

  • Added comprehensive documentation in
  • Updated README with new command
  • Added test scripts to package.json

Notes

This is a Beta feature. Future enhancements planned:

  • Custom format templates
  • Configurable burn rate thresholds
  • Additional metrics display options

Summary by CodeRabbit

  • New Features
    • Introduced a new "Statusline Integration" (Beta) for displaying a compact, real-time status line with Claude Code usage statistics.
    • Added a CLI command to show the status line and updated documentation with setup and usage instructions.
  • Documentation
    • Added a detailed guide for the new status line feature, including setup, output examples, and troubleshooting.
    • Updated usage examples and feature list in the README to reflect the new status line integration.
  • Tests
    • Added a test fixture for simulating status line input and new test scripts for the status line feature.

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.
@coderabbitai
Copy link

coderabbitai bot commented Aug 9, 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

A 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

Cohort / File(s) Change Summary
Documentation Updates
README.md, CLAUDE.md, docs/guide/statusline.md
Added documentation and usage instructions for the new "Statusline Integration" feature, including setup, example usage, and feature description.
CLI Command Integration
src/commands/index.ts, src/commands/statusline.ts
Added and registered a new statusline CLI subcommand that processes hook JSON input and outputs a compact status line with usage statistics.
Type Definitions
src/_types.ts
Introduced a Zod schema and TypeScript type for validating and typing the statusline hook JSON input.
NPM Scripts and Test Fixtures
package.json, test/statusline-test.json
Added npm scripts for testing the statusline command and a JSON fixture for simulating input data.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

Poem

In the warren of code, a new line appears,
Compact and bright, reporting costs and cheers.
Statusline whispers, in colors so neat,
Of models and tokens and billing complete.
With hooks and with scripts, we test and we try—
A bunny’s delight as the numbers hop by!
🐇✨

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 details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cc8ded1 and 3ce1365.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • docs/guide/statusline.md (1 hunks)
  • package.json (2 hunks)
  • src/_types.ts (1 hunks)
  • src/commands/statusline.ts (1 hunks)
  • test/statusline-test.json (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch statusline

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
  • 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 src/utils.ts and explain its main purpose.
    • @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 comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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 @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

  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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Aug 9, 2025

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 9, 2025

Open in StackBlitz

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

commit: 3ce1365

@socket-security
Copy link

socket-security bot commented Aug 9, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedget-stdin@​9.0.01001008276100

View full report

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 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.

Comment on lines 17 to 41
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);
}
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

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);
			}
		});
	});
}

Comment on lines +58 to +60
- Green text: Normal (< 2,000 tokens/min)
- Yellow text: Moderate (2,000-5,000 tokens/min)
- Red text: High (> 5,000 tokens/min)
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 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).

Suggested change
- 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)

Comment on lines +161 to +171
// 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
}
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 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
						}

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: 3

🧹 Nitpick comments (5)
src/_types.ts (1)

138-160: Reuse branded schemas for stronger validation and wire the schema into runtime parsing

The new statuslineHookJsonSchema duplicates 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 true isActive – could mis-report

If 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

remaining can be negative for just-ended blocks, resulting in “-3m left”. Guard with:

const remaining = Math.max(0, Math.round(/* … */));

160-173: Color thresholds magic numbers

Extract 2000 & 5000 to 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 to jsonc or remove the comment to avoid copy-paste errors.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 81c2a00 and cc8ded1.

📒 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) using bun 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.ts
  • src/commands/statusline.ts
  • src/_types.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Type check with TypeScript using bun typecheck

Files:

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

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.ts: File paths always 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() for wrapping operations that may throw (JSON parsing, etc.)
Use Result.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 with Result.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 use Result.isFailure() and Result.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 it export const vs just const
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 using await import() should only be used within test blocks to avoid tree-shaking issues
Mock data is created using fs-fixture with createFixture() for Claude data directory simulation
In-source testing pattern: Tests are written...

Files:

  • src/commands/index.ts
  • src/commands/statusline.ts
  • src/_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.png for 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

statusline command is correctly listed under development usage.

src/commands/index.ts (1)

9-10: Subcommand registration OK

statuslineCommand is properly imported and registered; nothing else needed.

Also applies to: 22-22

README.md (1)

58-58: Documentation update looks correct

README now advertises the new statusline feature; wording and placement are consistent with other entries.

Also applies to: 83-83

src/commands/statusline.ts (1)

81-84: Session-ID extraction is brittle

Relying on split(path.sep)[len-2] fails if transcript location changes.
Use path.dirname() twice or embed the sessionId in the hook payload (preferred).

Comment on lines +55 to +56
"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",
Copy link

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.

Suggested change
"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.

Comment on lines 17 to 41
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);
}
});
});
}
Copy link

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.

Comment on lines 6 to 9
"model": {
"id": "claude-opus-4-1",
"display_name": "Opus"
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Suggested change
"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
@ryoppippi ryoppippi merged commit 6f1449c into main Aug 9, 2025
10 of 11 checks passed
@ryoppippi ryoppippi deleted the statusline branch August 9, 2025 00:57
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