CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Monorepo Structure
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.
Apps Are Bundled
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.
Development Commands
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.
Architecture Overview
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)
Git Commit and PR Conventions
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
Code Style Notes
- 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.
Documentation Guidelines
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
Claude Models and Testing
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
Tips for Claude Code
- 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.
important-instruction-reminders
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.