This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a monorepo containing multiple packages. For package-specific guidance, refer to the individual CLAUDE.md files:
- Main CLI Package: @apps/ccusage/CLAUDE.md - Core ccusage CLI tool and library
- Codex CLI Package: @apps/codex/CLAUDE.md - OpenAI Codex usage tracking CLI
- OpenCode CLI Package: @apps/opencode/CLAUDE.md - OpenCode usage tracking CLI
- MCP Server Package: @apps/mcp/CLAUDE.md - MCP server implementation for ccusage data
- Documentation: @docs/CLAUDE.md - VitePress-based documentation website
Each package has its own development commands, dependencies, and specific guidelines. Always check the relevant package's CLAUDE.md when working within that package directory.
All projects under apps/ ship as bundled CLIs/binaries. Treat their runtime dependencies as bundled assets: list everything in each app's devDependencies (never dependencies) so the bundler owns the runtime payload.
Testing and Quality:
pnpm run test- Run all tests (using vitest via pnpm, watch mode disabled)- Lint code using ESLint MCP server (available via Claude Code tools)
pnpm run format- Format code with ESLint (writes changes)pnpm typecheck- Type check with TypeScript
Build and Release:
pnpm run build- Build distribution files with tsdownpnpm run release- Full release workflow (lint + typecheck + test + build + version bump)
Development Usage:
pnpm run start daily- Show daily usage reportpnpm run start monthly- Show monthly usage reportpnpm run start session- Show session-based usage reportpnpm run start blocks- Show 5-hour billing blocks usage reportpnpm run start statusline- Show compact status line (Beta)pnpm run start daily --json- Show daily usage report in JSON formatpnpm run start monthly --json- Show monthly usage report in JSON formatpnpm run start session --json- Show session usage report in JSON formatpnpm run start blocks --json- Show blocks usage report in JSON formatpnpm run start daily --mode <mode>- Control cost calculation mode (auto/calculate/display)pnpm run start monthly --mode <mode>- Control cost calculation mode (auto/calculate/display)pnpm run start session --mode <mode>- Control cost calculation mode (auto/calculate/display)pnpm run start blocks --mode <mode>- Control cost calculation mode (auto/calculate/display)pnpm run start blocks --active- Show only active block with projectionspnpm run start blocks --recent- Show blocks from last 3 days (including active)pnpm run start blocks --token-limit <limit>- Token limit for quota warnings (number or "max")node ./src/index.ts- Direct execution for development
MCP Server Usage: (now provided by the @ccusage/mcp package)
pnpm dlx @ccusage/mcp@latest -- --help- Show available optionspnpm dlx @ccusage/mcp@latest -- --type http --port 8080- Start HTTP transport
Cost Calculation Modes:
auto(default) - Use pre-calculated costUSD when available, otherwise calculate from tokenscalculate- Always calculate costs from token counts using model pricing, ignore costUSDdisplay- Always use pre-calculated costUSD values, show 0 for missing costs
Environment Variables:
LOG_LEVEL- Control logging verbosity (0=silent, 1=warn, 2=log, 3=info, 4=debug, 5=trace)- Example:
LOG_LEVEL=0 pnpm run start dailyfor silent output - Useful for debugging or suppressing non-critical output
- Example:
Multiple Claude Data Directories:
This tool supports multiple Claude data directories to handle different Claude Code installations:
- Default Behavior: Automatically searches both
~/.config/claude/projects/(new default) and~/.claude/projects/(old default) - Environment Variable: Set
CLAUDE_CONFIG_DIRto specify custom path(s)- Single path:
export CLAUDE_CONFIG_DIR="/path/to/claude" - Multiple paths:
export CLAUDE_CONFIG_DIR="/path/to/claude1,/path/to/claude2"
- Single path:
- Data Aggregation: Usage data from all valid directories is automatically combined
- Backward Compatibility: Existing configurations continue to work without changes
This addresses the breaking change in Claude Code where logs moved from ~/.claude to ~/.config/claude.
This is a CLI tool that analyzes Claude Code usage data from local JSONL files stored in Claude data directories (supports both ~/.claude/projects/ and ~/.config/claude/projects/). The architecture follows a clear separation of concerns:
Core Data Flow:
- Data Loading (
data-loader.ts) - Parses JSONL files from multiple Claude data directories, including pre-calculated costs - Token Aggregation (
calculate-cost.ts) - Utility functions for aggregating token counts and costs - Command Execution (
commands/) - CLI subcommands that orchestrate data loading and presentation - CLI Entry (
index.ts) - Gunshi-based CLI setup with subcommand routing
Output Formats:
- Table format (default): Pretty-printed tables with colors for terminal display
- JSON format (
--json): Structured JSON output for programmatic consumption
Key Data Structures:
- Raw usage data is parsed from JSONL with timestamp, token counts, and pre-calculated costs
- Data is aggregated into daily summaries, monthly summaries, session summaries, or 5-hour billing blocks
- Important Note on Naming: The term "session" in this codebase has two different meanings:
- Session Reports (
pnpm run start session): Groups usage by project directories. What we call "sessionId" in these reports is actually derived from the directory structure (project/directory) - True Session ID: The actual Claude Code session ID found in the
sessionIdfield within JSONL entries and used as the filename ({sessionId}.jsonl)
- Session Reports (
- File structure:
projects/{project}/{sessionId}.jsonlwhere:{project}is the project directory name (used for grouping){sessionId}.jsonlis the JSONL file named with the actual session ID from Claude Code- Each JSONL file contains all usage entries for a single Claude Code session
- The sessionId in the filename matches the
sessionIdfield inside the JSONL entries
- 5-hour blocks group usage data by Claude's billing cycles with active block tracking
External Dependencies:
- Uses local timezone for date formatting
- CLI built with
gunshiframework, tables withcli-table3 - LiteLLM Integration: Cost calculations depend on LiteLLM's pricing database for model pricing data
MCP Integration:
- Built-in MCP Server: Exposes usage data through MCP protocol with tools:
daily- Daily usage reportssession- Session-based usage reportsmonthly- Monthly usage reportsblocks- 5-hour billing blocks usage reports
- External MCP Servers Available:
- ESLint MCP: Lint TypeScript/JavaScript files directly through Claude Code tools
- Context7 MCP: Look up documentation for libraries and frameworks
- Claude Code Skills Available:
- use-gunshi-cli: Guide for using gunshi CLI framework (via @gunshi/docs)
- byethrow: Guide for using @praha/byethrow Result type (via @praha/byethrow-docs)
Commit Message Format:
Follow the Conventional Commits specification with package/area prefixes:
<type>(<scope>): <subject>
Scope Naming Rules:
-
Apps: Use the app directory name
feat(ccusage):- Changes to apps/ccusagefix(mcp):- Fixes in apps/mcpfeat(codex):- Features for apps/codex (if exists)
-
Packages: Use the package directory name
feat(terminal):- Changes to packages/terminalfix(ui):- Fixes in packages/uirefactor(core):- Refactoring packages/core
-
Documentation: Use
docsscopedocs:ordocs(guide):- Documentation updatesdocs(api):- API documentation changes
-
Root-level changes: No scope (preferred) or use
rootchore:- Root config updatesci:- CI/CD changesfeat:- Root-level featuresdocs:- Root documentation updatesbuild:orbuild(root):- Root build system changes
Type Prefixes:
feat:- New featurefix:- Bug fixdocs:- Documentation only changesstyle:- Code style changes (formatting, missing semi-colons, etc)refactor:- Code change that neither fixes a bug nor adds a featureperf:- Performance improvementstest:- Adding missing tests or correcting existing testschore:- Changes to the build process or auxiliary toolsci:- CI/CD configuration changesrevert:- Reverting a previous commit
Examples:
feat(ccusage): add support for Claude 4.1 models
fix(mcp): resolve connection timeout issues
docs(guide): update installation instructions
refactor(ccusage): extract cost calculation to separate module
test(mcp): add integration tests for HTTP transport
chore: update dependencies
PR Title Convention:
PR titles should follow the same format as commit messages. When a PR contains multiple commits, the title should describe the main change:
feat(ccusage): implement session-based usage reports
fix(mcp): handle edge cases in data aggregation
docs: comprehensive API documentation update
- Uses ESLint for linting and formatting with tab indentation and double quotes
- TypeScript with strict mode and bundler module resolution
- No console.log allowed except where explicitly disabled with eslint-disable
- Error handling: silently skips malformed JSONL lines during parsing
- File paths always use Node.js path utilities for cross-platform compatibility
- Import conventions: Use
.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
Error Handling:
- Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
- Documentation: Available via byethrow skill (use
/byethrowor check.claude/skills/byethrow/)
- Documentation: Available via byethrow skill (use
- 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()andResult.isSuccess()type guards for better code clarity
Naming Conventions:
- 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 - Internal files: use underscore prefix - e.g.,
_types.ts,_utils.ts,_consts.ts
Export Rules:
- IMPORTANT: 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 constvs justconst - This follows the principle of minimizing the public API surface area
- Dependencies should always be added as
devDependenciesunless explicitly requested otherwise
Post-Code Change Workflow:
After making any code changes, ALWAYS run these commands in parallel:
pnpm run format- Auto-fix and format code with ESLint (includes linting)pnpm typecheck- Type check with TypeScriptpnpm run test- Run all tests
This ensures code quality and catches issues immediately after changes.
Screenshot Usage:
- Placement: Always place screenshots immediately after the main heading (H1) in documentation pages
- Purpose: Provide immediate visual context to users before textual explanations
- Guides with Screenshots:
/docs/guide/index.md(What is ccusage) - Main usage screenshot/docs/guide/daily-reports.md- Daily report output screenshot/docs/guide/live-monitoring.md- Live monitoring dashboard screenshot/docs/guide/mcp-server.md- Claude Desktop integration screenshot
- Image Path: Use relative paths like
/screenshot.pngfor images stored in/docs/public/ - Alt Text: Always include descriptive alt text for accessibility
Supported Claude 4 Models (as of 2025):
claude-sonnet-4-20250514- Latest Claude 4 Sonnet modelclaude-opus-4-20250514- Latest Claude 4 Opus model
Model Naming Convention:
- Pattern:
claude-{model-type}-{generation}-{date} - Example:
claude-sonnet-4-20250514(NOTclaude-4-sonnet-20250514) - The generation number comes AFTER the model type
Testing Guidelines:
- In-Source Testing Pattern: This project uses in-source testing with
if (import.meta.vitest != null)blocks - Tests are written directly in the same files as the source code, not in separate test files
- Vitest globals (
describe,it,expect) are available automatically without imports - IMPORTANT: DO NOT use
await import()dynamic imports anywhere in the codebase - this causes tree-shaking issues and should be avoided entirely - ESPECIALLY: Never use dynamic imports in vitest test blocks - this is particularly problematic for test execution
- Vitest globals are enabled: Use
describe,it,expectdirectly without any imports since globals are configured - Mock data is created using
fs-fixturewithcreateFixture()for Claude data directory simulation - 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
LiteLLM Integration Notes:
- Cost calculations require exact model name matches with LiteLLM's database
- Test failures often indicate model names don't exist in LiteLLM's pricing data
- Future model updates require checking LiteLLM compatibility first
- The application cannot calculate costs for models not supported by LiteLLM
- Context7 MCP server available for library documentation lookup
- use-gunshi-cli skill available for gunshi CLI framework documentation
- byethrow skill available for @praha/byethrow Result type documentation
- do not use console.log. use logger.ts instead
- CRITICAL VITEST REMINDER: Vitest globals are enabled - use
describe,it,expectdirectly WITHOUT imports. NEVER useawait import()dynamic imports anywhere, especially in test blocks.
Do what has been asked; nothing more, nothing less. NEVER create files unless they're absolutely necessary for achieving your goal. ALWAYS prefer editing an existing file to creating a new one. NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User. Dependencies should always be added as devDependencies unless explicitly requested otherwise.