The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    ├── FUNDING.yaml
    ├── renovate.json
    └── workflows
    │   ├── ci.yaml
    │   └── release.yaml
├── .gitignore
├── .mcp.json
├── .npmrc
├── CLAUDE.md
├── LICENSE
├── README.md
├── bun.lock
├── docs
    ├── .gitignore
    ├── .vitepress
    │   └── config.ts
    ├── guide
    │   ├── blocks-reports.md
    │   ├── configuration.md
    │   ├── cost-modes.md
    │   ├── custom-paths.md
    │   ├── daily-reports.md
    │   ├── getting-started.md
    │   ├── index.md
    │   ├── installation.md
    │   ├── json-output.md
    │   ├── library-usage.md
    │   ├── live-monitoring.md
    │   ├── mcp-server.md
    │   ├── monthly-reports.md
    │   ├── related-projects.md
    │   ├── session-reports.md
    │   └── sponsors.md
    ├── index.md
    ├── index.ts
    ├── package.json
    ├── public
    │   ├── blocks-live.png
    │   ├── claude_code_protips_thumbnail_v1.png
    │   ├── favicon.svg
    │   ├── logo.png
    │   ├── logo.svg
    │   ├── mcp-claude-desktop.avif
    │   └── screenshot.png
    ├── tsconfig.json
    ├── typedoc.config.mjs
    ├── update-api-index.ts
    └── wrangler.jsonc
├── eslint.config.js
├── package.json
├── src
    ├── _consts.ts
    ├── _live-monitor.ts
    ├── _live-rendering.ts
    ├── _macro.ts
    ├── _session-blocks.ts
    ├── _shared-args.ts
    ├── _terminal-utils.ts
    ├── _token-utils.ts
    ├── _types.ts
    ├── _utils.ts
    ├── calculate-cost.ts
    ├── commands
    │   ├── _blocks.live.ts
    │   ├── blocks.ts
    │   ├── daily.ts
    │   ├── index.ts
    │   ├── mcp.ts
    │   ├── monthly.ts
    │   └── session.ts
    ├── data-loader.ts
    ├── debug.ts
    ├── index.ts
    ├── logger.ts
    ├── mcp.ts
    └── pricing-fetcher.ts
├── tsconfig.json
├── tsdown.config.ts
├── typos.toml
└── vitest.config.ts


/.github/FUNDING.yaml:
--------------------------------------------------------------------------------
1 | github: ryoppippi
2 | 


--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
 1 | {
 2 | 	"$schema": "https://docs.renovatebot.com/renovate-schema.json",
 3 | 	"vulnerabilityAlerts": {
 4 | 		"labels": ["security"]
 5 | 	},
 6 | 	"labels": ["dependencies", "renovate"],
 7 | 	"rangeStrategy": "bump",
 8 | 	"lockFileMaintenance": {
 9 | 		"enabled": true
10 | 	},
11 | 	"platformAutomerge": true,
12 | 	"dependencyDashboard": false,
13 | 	"automerge": true,
14 | 	"branchConcurrentLimit": 0,
15 | 	"stabilityDays": 3
16 | }
17 | 


--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
 1 | name: CI
 2 | 
 3 | on:
 4 |   push:
 5 |   pull_request:
 6 | 
 7 | jobs:
 8 |   ci:
 9 |     runs-on: ubuntu-latest
10 | 
11 |     steps:
12 |       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
13 |       - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
14 |         with:
15 |           bun-version: latest
16 |       - run: bun install --frozen-lockfile
17 |       - run: bun lint
18 |       - run: bun typecheck
19 |       - name: Create default Claude directories for tests
20 |         run: |
21 |           mkdir -p $HOME/.claude/projects
22 |           mkdir -p $HOME/.config/claude/projects
23 |       - run: bun run test
24 | 
25 |   npm-publish-dry-run-and-upload-pkg-pr-now:
26 |     runs-on: ubuntu-latest
27 | 
28 |     steps:
29 |       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
30 |       - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
31 |         with:
32 |           bun-version: latest
33 |       - run: bun install --frozen-lockfile
34 |       - run: bunx pkg-pr-new publish
35 | 
36 |   spell-check:
37 |     runs-on: ubuntu-latest
38 |     steps:
39 |       - name: Checkout Actions Repository
40 |         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41 | 
42 |       - uses: crate-ci/typos@master
43 |         with:
44 |           config: ./typos.toml
45 | 


--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
 1 | name: npm publish
 2 | 
 3 | on:
 4 |   push:
 5 |     tags:
 6 |       - '*'
 7 | 
 8 | jobs:
 9 |   npm:
10 |     runs-on: ubuntu-latest
11 |     timeout-minutes: 10
12 |     permissions:
13 |       contents: read
14 |       id-token: write
15 |     steps:
16 |       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
17 |         with:
18 |           fetch-depth: 0
19 |       - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
20 |         with:
21 |           registry-url: 'https://registry.npmjs.org'
22 |           node-version: ${{env.NODE_VERSION}}
23 |       - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
24 |       - run: bun install --frozen-lockfile
25 |       - run: npm publish --provenance --no-git-checks --access public
26 |         env:
27 |           NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
28 |           NPM_CONFIG_PROVENANCE: true
29 |         working-directory: ${{env.PACKAGE_DIR}}
30 | 
31 |   release:
32 |     needs:
33 |       - npm
34 |     runs-on: ubuntu-latest
35 |     permissions:
36 |       contents: write
37 |     steps:
38 |       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
39 |         with:
40 |           fetch-depth: 0
41 |       - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
42 |         with:
43 |           bun-version: latest
44 |       - run: bun x changelogithub
45 |         env:
46 |           GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
47 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | # dependencies (bun install)
 2 | node_modules
 3 | 
 4 | # output
 5 | out
 6 | dist
 7 | *.tgz
 8 | 
 9 | # code coverage
10 | coverage
11 | *.lcov
12 | 
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 | 
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 | 
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 | 
30 | # IntelliJ based IDEs
31 | .idea
32 | 
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 | 
36 | .claude
37 | 
38 | .eslintcache
39 | 


--------------------------------------------------------------------------------
/.mcp.json:
--------------------------------------------------------------------------------
 1 | {
 2 | 	"mcpServers": {
 3 | 		"context7": {
 4 | 			"type": "sse",
 5 | 			"url": "https://mcp.context7.com/sse"
 6 | 		},
 7 | 		"gunshi-doc": {
 8 | 			"command": "bun",
 9 | 			"args": [
10 | 				"x",
11 | 				"sitemcp",
12 | 				"https://gunshi.dev/",
13 | 				"--concurrency 10",
14 | 				"--no-cache"
15 | 			]
16 | 		},
17 | 		"eslint-mcp": {
18 | 			"type": "stdio",
19 | 			"command": "bun",
20 | 			"args": [
21 | 				"x",
22 | 				"@eslint/mcp@latest"
23 | 			],
24 | 			"env": {}
25 | 		},
26 | 		"@praha/byethrow": {
27 | 			"type": "stdio",
28 | 			"command": "bun",
29 | 			"args": [
30 | 				"byethrow-mcp"
31 | 			],
32 | 			"env": {}
33 | 		}
34 | 	}
35 | }
36 | 


--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | @jsr:registry=https://npm.jsr.io
2 | 


--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
  1 | # CLAUDE.md
  2 | 
  3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
  4 | 
  5 | ## Development Commands
  6 | 
  7 | **Testing and Quality:**
  8 | 
  9 | - `bun run test` - Run all tests (using vitest via bun)
 10 | - Lint code using ESLint MCP server (available via Claude Code tools)
 11 | - `bun run format` - Format code with ESLint (writes changes)
 12 | - `bun typecheck` - Type check with TypeScript
 13 | 
 14 | **Build and Release:**
 15 | 
 16 | - `bun run build` - Build distribution files with tsdown
 17 | - `bun run release` - Full release workflow (lint + typecheck + test + build + version bump)
 18 | 
 19 | **Development Usage:**
 20 | 
 21 | - `bun run start daily` - Show daily usage report
 22 | - `bun run start monthly` - Show monthly usage report
 23 | - `bun run start session` - Show session-based usage report
 24 | - `bun run start blocks` - Show 5-hour billing blocks usage report
 25 | - `bun run start daily --json` - Show daily usage report in JSON format
 26 | - `bun run start monthly --json` - Show monthly usage report in JSON format
 27 | - `bun run start session --json` - Show session usage report in JSON format
 28 | - `bun run start blocks --json` - Show blocks usage report in JSON format
 29 | - `bun run start daily --mode <mode>` - Control cost calculation mode (auto/calculate/display)
 30 | - `bun run start monthly --mode <mode>` - Control cost calculation mode (auto/calculate/display)
 31 | - `bun run start session --mode <mode>` - Control cost calculation mode (auto/calculate/display)
 32 | - `bun run start blocks --mode <mode>` - Control cost calculation mode (auto/calculate/display)
 33 | - `bun run start blocks --active` - Show only active block with projections
 34 | - `bun run start blocks --recent` - Show blocks from last 3 days (including active)
 35 | - `bun run start blocks --token-limit <limit>` - Token limit for quota warnings (number or "max")
 36 | - `bun run ./src/index.ts` - Direct execution for development
 37 | 
 38 | **MCP Server Usage:**
 39 | 
 40 | - `bun run start mcp` - Start MCP server with stdio transport (default)
 41 | - `bun run start mcp --type http --port 8080` - Start MCP server with HTTP transport
 42 | 
 43 | **Cost Calculation Modes:**
 44 | 
 45 | - `auto` (default) - Use pre-calculated costUSD when available, otherwise calculate from tokens
 46 | - `calculate` - Always calculate costs from token counts using model pricing, ignore costUSD
 47 | - `display` - Always use pre-calculated costUSD values, show 0 for missing costs
 48 | 
 49 | **Multiple Claude Data Directories:**
 50 | 
 51 | This tool supports multiple Claude data directories to handle different Claude Code installations:
 52 | 
 53 | - **Default Behavior**: Automatically searches both `~/.config/claude/projects/` (new default) and `~/.claude/projects/` (old default)
 54 | - **Environment Variable**: Set `CLAUDE_CONFIG_DIR` to specify custom path(s)
 55 |   - Single path: `export CLAUDE_CONFIG_DIR="/path/to/claude"`
 56 |   - Multiple paths: `export CLAUDE_CONFIG_DIR="/path/to/claude1,/path/to/claude2"`
 57 | - **Data Aggregation**: Usage data from all valid directories is automatically combined
 58 | - **Backward Compatibility**: Existing configurations continue to work without changes
 59 | 
 60 | This addresses the breaking change in Claude Code where logs moved from `~/.claude` to `~/.config/claude`.
 61 | 
 62 | ## Architecture Overview
 63 | 
 64 | 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:
 65 | 
 66 | **Core Data Flow:**
 67 | 
 68 | 1. **Data Loading** (`data-loader.ts`) - Parses JSONL files from multiple Claude data directories, including pre-calculated costs
 69 | 2. **Token Aggregation** (`calculate-cost.ts`) - Utility functions for aggregating token counts and costs
 70 | 3. **Command Execution** (`commands/`) - CLI subcommands that orchestrate data loading and presentation
 71 | 4. **CLI Entry** (`index.ts`) - Gunshi-based CLI setup with subcommand routing
 72 | 
 73 | **Output Formats:**
 74 | 
 75 | - Table format (default): Pretty-printed tables with colors for terminal display
 76 | - JSON format (`--json`): Structured JSON output for programmatic consumption
 77 | 
 78 | **Key Data Structures:**
 79 | 
 80 | - Raw usage data is parsed from JSONL with timestamp, token counts, and pre-calculated costs
 81 | - Data is aggregated into daily summaries, monthly summaries, session summaries, or 5-hour billing blocks
 82 | - Sessions are identified by directory structure: `projects/{project}/{session}/{file}.jsonl`
 83 | - 5-hour blocks group usage data by Claude's billing cycles with active block tracking
 84 | 
 85 | **External Dependencies:**
 86 | 
 87 | - Uses local timezone for date formatting
 88 | - CLI built with `gunshi` framework, tables with `cli-table3`
 89 | - **LiteLLM Integration**: Cost calculations depend on LiteLLM's pricing database for model pricing data
 90 | 
 91 | **MCP Integration:**
 92 | 
 93 | - **Built-in MCP Server**: Exposes usage data through MCP protocol with tools:
 94 |   - `daily` - Daily usage reports
 95 |   - `session` - Session-based usage reports
 96 |   - `monthly` - Monthly usage reports
 97 |   - `blocks` - 5-hour billing blocks usage reports
 98 | - **External MCP Servers Available:**
 99 |   - **ESLint MCP**: Lint TypeScript/JavaScript files directly through Claude Code tools
100 |   - **Context7 MCP**: Look up documentation for libraries and frameworks
101 |   - **Gunshi MCP**: Access gunshi.dev documentation and examples
102 | 
103 | ## Code Style Notes
104 | 
105 | - Uses ESLint for linting and formatting with tab indentation and double quotes
106 | - TypeScript with strict mode and bundler module resolution
107 | - No console.log allowed except where explicitly disabled with eslint-disable
108 | - Error handling: silently skips malformed JSONL lines during parsing
109 | - File paths always use Node.js path utilities for cross-platform compatibility
110 | - **Import conventions**: Use `.ts` extensions for local file imports (e.g., `import { foo } from './utils.ts'`)
111 | 
112 | **Error Handling:**
113 | 
114 | - **Prefer @praha/byethrow Result type** over traditional try-catch for functional error handling
115 | - Use `Result.try()` for wrapping operations that may throw (JSON parsing, etc.)
116 | - Use `Result.isFailure()` for checking errors (more readable than `!Result.isSuccess()`)
117 | - Use early return pattern (`if (Result.isFailure(result)) continue;`) instead of ternary operators
118 | - For async operations: create wrapper function with `Result.try()` then call it
119 | - Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
120 | - Always use `Result.isFailure()` and `Result.isSuccess()` type guards for better code clarity
121 | 
122 | **Naming Conventions:**
123 | 
124 | - Variables: start with lowercase (camelCase) - e.g., `usageDataSchema`, `modelBreakdownSchema`
125 | - Types: start with uppercase (PascalCase) - e.g., `UsageData`, `ModelBreakdown`
126 | - Constants: can use UPPER_SNAKE_CASE - e.g., `DEFAULT_CLAUDE_CODE_PATH`
127 | - Internal files: use underscore prefix - e.g., `_types.ts`, `_utils.ts`, `_consts.ts`
128 | 
129 | **Export Rules:**
130 | 
131 | - **IMPORTANT**: Only export constants, functions, and types that are actually used by other modules
132 | - Internal/private constants that are only used within the same file should NOT be exported
133 | - Always check if a constant is used elsewhere before making it `export const` vs just `const`
134 | - This follows the principle of minimizing the public API surface area
135 | - Dependencies should always be added as `devDependencies` unless explicitly requested otherwise
136 | 
137 | **Post-Code Change Workflow:**
138 | 
139 | After making any code changes, ALWAYS run these commands in parallel:
140 | 
141 | - `bun run format` - Auto-fix and format code with ESLint (includes linting)
142 | - `bun typecheck` - Type check with TypeScript
143 | - `bun run test` - Run all tests
144 | 
145 | This ensures code quality and catches issues immediately after changes.
146 | 
147 | ## Documentation Guidelines
148 | 
149 | **Screenshot Usage:**
150 | 
151 | - **Placement**: Always place screenshots immediately after the main heading (H1) in documentation pages
152 | - **Purpose**: Provide immediate visual context to users before textual explanations
153 | - **Guides with Screenshots**:
154 |   - `/docs/guide/index.md` (What is ccusage) - Main usage screenshot
155 |   - `/docs/guide/daily-reports.md` - Daily report output screenshot
156 |   - `/docs/guide/live-monitoring.md` - Live monitoring dashboard screenshot
157 |   - `/docs/guide/mcp-server.md` - Claude Desktop integration screenshot
158 | - **Image Path**: Use relative paths like `/screenshot.png` for images stored in `/docs/public/`
159 | - **Alt Text**: Always include descriptive alt text for accessibility
160 | 
161 | ## Claude Models and Testing
162 | 
163 | **Supported Claude 4 Models (as of 2025):**
164 | 
165 | - `claude-sonnet-4-20250514` - Latest Claude 4 Sonnet model
166 | - `claude-opus-4-20250514` - Latest Claude 4 Opus model
167 | 
168 | **Model Naming Convention:**
169 | 
170 | - Pattern: `claude-{model-type}-{generation}-{date}`
171 | - Example: `claude-sonnet-4-20250514` (NOT `claude-4-sonnet-20250514`)
172 | - The generation number comes AFTER the model type
173 | 
174 | **Testing Guidelines:**
175 | 
176 | - **In-Source Testing Pattern**: This project uses in-source testing with `if (import.meta.vitest != null)` blocks
177 | - Tests are written directly in the same files as the source code, not in separate test files
178 | - Vitest globals (`describe`, `it`, `expect`) are available automatically without imports
179 | - Dynamic imports using `await import()` should only be used within test blocks to avoid tree-shaking issues
180 | - Mock data is created using `fs-fixture` with `createFixture()` for Claude data directory simulation
181 | - All test files must use current Claude 4 models, not outdated Claude 3 models
182 | - Test coverage should include both Sonnet and Opus models for comprehensive validation
183 | - Model names in tests must exactly match LiteLLM's pricing database entries
184 | - When adding new model tests, verify the model exists in LiteLLM before implementation
185 | - Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
186 | 
187 | **LiteLLM Integration Notes:**
188 | 
189 | - Cost calculations require exact model name matches with LiteLLM's database
190 | - Test failures often indicate model names don't exist in LiteLLM's pricing data
191 | - Future model updates require checking LiteLLM compatibility first
192 | - The application cannot calculate costs for models not supported by LiteLLM
193 | 
194 | # Tips for Claude Code
195 | 
196 | - [gunshi](https://gunshi.dev/llms.txt) - Documentation available via Gunshi MCP server
197 | - Context7 MCP server available for library documentation lookup
198 | - do not use console.log. use logger.ts instead
199 | 
200 | # important-instruction-reminders
201 | 
202 | Do what has been asked; nothing more, nothing less.
203 | NEVER create files unless they're absolutely necessary for achieving your goal.
204 | ALWAYS prefer editing an existing file to creating a new one.
205 | NEVER proactively create documentation files (\*.md) or README files. Only create documentation files if explicitly requested by the User.
206 | Dependencies should always be added as devDependencies unless explicitly requested otherwise.
207 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2025 ryoppippi
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | <div align="center">
  2 |     <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/logo.svg" alt="ccusage logo" width="256" height="256">
  3 |     <h1>ccusage</h1>
  4 | </div>
  5 | 
  6 | <p align="center">
  7 |     <a href="https://npmjs.com/package/ccusage"><img src="https://img.shields.io/npm/v/ccusage?color=yellow" alt="npm version" /></a>
  8 |     <a href="https://tanstack.com/stats/npm?packageGroups=%5B%7B%22packages%22:%5B%7B%22name%22:%22ccusage%22%7D%5D%7D%5D&range=30-days&transform=none&binType=daily&showDataMode=all&height=400"><img src="https://img.shields.io/npm/dy/ccusage" alt="NPM Downloads" /></a>
  9 |     <a href="https://packagephobia.com/result?p=ccusage"><img src="https://packagephobia.com/badge?p=ccusage" alt="install size" /></a>
 10 |     <a href="https://deepwiki.com/ryoppippi/ccusage"><img src="https://img.shields.io/badge/DeepWiki-ryoppippi%2Fccusage-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==" alt="DeepWiki"></a>
 11 |     <!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
 12 |     <a href="https://github.com/hesreallyhim/awesome-claude-code"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code" /></a>
 13 | </p>
 14 | 
 15 | <div align="center">
 16 |     <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/screenshot.png">
 17 | </div>
 18 | 
 19 | > Analyze your Claude Code token usage and costs from local JSONL files — incredibly fast and informative!
 20 | 
 21 | ## Installation
 22 | 
 23 | ### Quick Start (Recommended)
 24 | 
 25 | Thanks to ccusage's incredibly small bundle size ([![install size](https://packagephobia.com/badge?p=ccusage)](https://packagephobia.com/result?p=ccusage)), you can run it directly without installation:
 26 | 
 27 | ```bash
 28 | # Using bunx (recommended for speed)
 29 | bunx ccusage
 30 | 
 31 | # Using npx
 32 | npx ccusage@latest
 33 | 
 34 | # Using deno (with security flags)
 35 | deno run -E -R=$HOME/.claude/projects/ -S=homedir -N='raw.githubusercontent.com:443' npm:ccusage@latest
 36 | ```
 37 | 
 38 | > 💡 **Tip**: We recommend using `bunx` instead of `npx` for a massive speed improvement!
 39 | 
 40 | ### Local Installation (Optional)
 41 | 
 42 | Since ccusage has such a small bundle size, installation is entirely optional:
 43 | 
 44 | ```bash
 45 | npm install -g ccusage
 46 | ```
 47 | 
 48 | ## Usage
 49 | 
 50 | ```bash
 51 | # Basic usage
 52 | ccusage          # Show daily report (default)
 53 | ccusage daily    # Daily token usage and costs
 54 | ccusage monthly  # Monthly aggregated report
 55 | ccusage session  # Usage by conversation session
 56 | ccusage blocks   # 5-hour billing windows
 57 | 
 58 | # Live monitoring
 59 | ccusage blocks --live  # Real-time usage dashboard
 60 | 
 61 | # Filters and options
 62 | ccusage daily --since 20250525 --until 20250530
 63 | ccusage daily --json  # JSON output
 64 | ccusage daily --breakdown  # Per-model cost breakdown
 65 | ```
 66 | 
 67 | ## Features
 68 | 
 69 | - 📊 **Daily Report**: View token usage and costs aggregated by date
 70 | - 📅 **Monthly Report**: View token usage and costs aggregated by month
 71 | - 💬 **Session Report**: View usage grouped by conversation sessions
 72 | - ⏰ **5-Hour Blocks Report**: Track usage within Claude's billing windows with active block monitoring
 73 | - 📈 **Live Monitoring**: Real-time dashboard showing active session progress, token burn rate, and cost projections with `blocks --live`
 74 | - 🤖 **Model Tracking**: See which Claude models you're using (Opus, Sonnet, etc.)
 75 | - 📊 **Model Breakdown**: View per-model cost breakdown with `--breakdown` flag
 76 | - 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
 77 | - 📁 **Custom Path**: Support for custom Claude data directory locations
 78 | - 🎨 **Beautiful Output**: Colorful table-formatted display with automatic responsive layout
 79 | - 📱 **Smart Tables**: Automatic compact mode for narrow terminals (< 100 characters) with essential columns
 80 | - 📋 **Enhanced Model Display**: Model names shown as bulleted lists for better readability
 81 | - 📄 **JSON Output**: Export data in structured JSON format with `--json`
 82 | - 💰 **Cost Tracking**: Shows costs in USD for each day/month/session
 83 | - 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
 84 | - 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
 85 | - 🔌 **MCP Integration**: Built-in Model Context Protocol server for integration with other tools
 86 | - 🚀 **Ultra-Small Bundle**: Unlike other CLI tools, we pay extreme attention to bundle size - incredibly small even without minification!
 87 | 
 88 | ## Documentation
 89 | 
 90 | Full documentation is available at **[ccusage.com](https://ccusage.com/)**
 91 | 
 92 | ## Sponsors
 93 | 
 94 | ### Featured Sponsor
 95 | 
 96 | Check out these [47 Claude Code ProTips from Greg Baugues.](https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg)
 97 | 
 98 | <p align="center">
 99 |     <a href="https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg">
100 |         <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/claude_code_protips_thumbnail_v1.png" alt="47 Claude Code ProTips from Greg Baugues" width="600">
101 |     </a>
102 | </p>
103 | 
104 | <p align="center">
105 |     <a href="https://github.com/sponsors/ryoppippi">
106 |         <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">
107 |     </a>
108 | </p>
109 | 
110 | ## Star History
111 | 
112 | <a href="https://www.star-history.com/#ryoppippi/ccusage&Date">
113 |     <picture>
114 |         <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date&theme=dark" />
115 |         <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
116 |         <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
117 |     </picture>
118 | </a>
119 | 
120 | ## License
121 | 
122 | [MIT](LICENSE) © [@ryoppippi](https://github.com/ryoppippi)
123 | 


--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
 1 | # VitePress build output
 2 | .vitepress/dist/
 3 | .vitepress/cache/
 4 | 
 5 | # Generated documentation
 6 | api/
 7 | 
 8 | # Dependencies
 9 | node_modules/
10 | 
11 | # Temporary files
12 | .temp/
13 | .cache/
14 | 
15 | # OS files
16 | .DS_Store
17 | Thumbs.db
18 | 
19 | public/_redirects
20 | 


--------------------------------------------------------------------------------
/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
  1 | import { defineConfig } from 'vitepress';
  2 | import * as path from 'node:path';
  3 | import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons';
  4 | import llmstxt from 'vitepress-plugin-llms';
  5 | import { withMermaid } from 'vitepress-plugin-mermaid';
  6 | import typedocSidebar from '../api/typedoc-sidebar.json';
  7 | import { cloudflareRedirect } from '@ryoppippi/vite-plugin-cloudflare-redirect'
  8 | 
  9 | export default withMermaid(defineConfig({
 10 | 	title: 'ccusage',
 11 | 	description: 'Usage analysis tool for Claude Code',
 12 | 	base: '/',
 13 | 	cleanUrls: true,
 14 | 	ignoreDeadLinks: true,
 15 | 
 16 | 	head: [
 17 | 		['link', { rel: 'icon', href: '/favicon.svg' }],
 18 | 		['meta', { name: 'theme-color', content: '#646cff' }],
 19 | 		['meta', { property: 'og:type', content: 'website' }],
 20 | 		['meta', { property: 'og:locale', content: 'en' }],
 21 | 		['meta', { property: 'og:title', content: 'ccusage | Claude Code Usage Analysis' }],
 22 | 		['meta', { property: 'og:site_name', content: 'ccusage' }],
 23 | 		['meta', { property: 'og:image', content: 'https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/logo.png' }],
 24 | 		['meta', { property: 'og:url', content: 'https://github.com/ryoppippi/ccusage' }],
 25 | 	],
 26 | 
 27 | 	themeConfig: {
 28 | 		logo: '/logo.svg',
 29 | 
 30 | 		nav: [
 31 | 			{ text: 'Guide', link: '/guide/' },
 32 | 			{ text: 'API Reference', link: '/api/' },
 33 | 			{
 34 | 				text: 'Links',
 35 | 				items: [
 36 | 					{ text: 'GitHub', link: 'https://github.com/ryoppippi/ccusage' },
 37 | 					{ text: 'npm', link: 'https://www.npmjs.com/package/ccusage' },
 38 | 					{ text: 'Changelog', link: 'https://github.com/ryoppippi/ccusage/releases' },
 39 | 					{ text: 'DeepWiki', link: 'https://deepwiki.com/ryoppippi/ccusage' },
 40 | 					{ text: 'Package Stats', link: 'https://tanstack.com/ccusage?npmPackage=ccusage' },
 41 | 				],
 42 | 			},
 43 | 		],
 44 | 
 45 | 		sidebar: {
 46 | 			'/guide/': [
 47 | 				{
 48 | 					text: 'Introduction',
 49 | 					items: [
 50 | 						{ text: 'Introduction', link: '/guide/' },
 51 | 						{ text: 'Getting Started', link: '/guide/getting-started' },
 52 | 						{ text: 'Installation', link: '/guide/installation' },
 53 | 					],
 54 | 				},
 55 | 				{
 56 | 					text: 'Usage',
 57 | 					items: [
 58 | 						{ text: 'Daily Reports', link: '/guide/daily-reports' },
 59 | 						{ text: 'Monthly Reports', link: '/guide/monthly-reports' },
 60 | 						{ text: 'Session Reports', link: '/guide/session-reports' },
 61 | 						{ text: 'Blocks Reports', link: '/guide/blocks-reports' },
 62 | 						{ text: 'Live Monitoring', link: '/guide/live-monitoring' },
 63 | 					],
 64 | 				},
 65 | 				{
 66 | 					text: 'Configuration',
 67 | 					items: [
 68 | 						{ text: 'Environment Variables', link: '/guide/configuration' },
 69 | 						{ text: 'Custom Paths', link: '/guide/custom-paths' },
 70 | 						{ text: 'Cost Calculation Modes', link: '/guide/cost-modes' },
 71 | 					],
 72 | 				},
 73 | 				{
 74 | 					text: 'Integration',
 75 | 					items: [
 76 | 						{ text: 'Library Usage', link: '/guide/library-usage' },
 77 | 						{ text: 'MCP Server', link: '/guide/mcp-server' },
 78 | 						{ text: 'JSON Output', link: '/guide/json-output' },
 79 | 					],
 80 | 				},
 81 | 				{
 82 | 					text: 'Community',
 83 | 					items: [
 84 | 						{ text: 'Related Projects', link: '/guide/related-projects' },
 85 | 						{ text: 'Sponsors', link: '/guide/sponsors' },
 86 | 					],
 87 | 				},
 88 | 			],
 89 | 			'/api/': [
 90 | 				{
 91 | 					text: 'API Reference',
 92 | 					items: [
 93 | 						{ text: 'Overview', link: '/api/' },
 94 | 						...typedocSidebar,
 95 | 					],
 96 | 				},
 97 | 			],
 98 | 		},
 99 | 
100 | 		socialLinks: [
101 | 			{ icon: 'github', link: 'https://github.com/ryoppippi/ccusage' },
102 | 			{ icon: 'npm', link: 'https://www.npmjs.com/package/ccusage' },
103 | 		],
104 | 
105 | 		footer: {
106 | 			message: 'Released under the MIT License.',
107 | 			copyright: 'Copyright © 2024 ryoppippi',
108 | 		},
109 | 
110 | 		search: {
111 | 			provider: 'local',
112 | 		},
113 | 
114 | 		editLink: {
115 | 			pattern: 'https://github.com/ryoppippi/ccusage/edit/main/docs/:path',
116 | 			text: 'Edit this page on GitHub',
117 | 		},
118 | 
119 | 		lastUpdated: {
120 | 			text: 'Updated at',
121 | 			formatOptions: {
122 | 				year: 'numeric',
123 | 				month: '2-digit',
124 | 				day: '2-digit',
125 | 				hour: '2-digit',
126 | 				minute: '2-digit',
127 | 				hour12: false,
128 | 				timeZone: 'UTC',
129 | 			},
130 | 		},
131 | 	},
132 | 
133 | 	vite: {
134 | 		plugins: [
135 | 			cloudflareRedirect({
136 |             mode: "generate",
137 |             entries: [
138 |                 { from: '/raycast', to: 'https://www.raycast.com/nyatinte/ccusage', status: 302 },
139 |                 { from: '/gh', to: 'https://github.com/ryoppippi/ccusage', status: 302 },
140 |                 { from: '/npm', to: 'https://www.npmjs.com/package/ccusage', status: 302 },
141 |                 { from: '/deepwiki', to: 'https://deepwiki.com/ryoppippi/ccusage', status: 302 },
142 |             ]
143 |         }),
144 | 			groupIconVitePlugin(),
145 | 			...llmstxt(),
146 | 		],
147 | 	},
148 | 
149 | 	markdown: {
150 | 		config(md) {
151 | 			md.use(groupIconMdPlugin);
152 | 		},
153 | 	},
154 | 	mermaid: {
155 | 		// Optional mermaid configuration
156 | 	},
157 | }));
158 | 


--------------------------------------------------------------------------------
/docs/guide/blocks-reports.md:
--------------------------------------------------------------------------------
  1 | # Blocks Reports
  2 | 
  3 | Blocks reports show your Claude Code usage grouped by 5-hour billing windows, helping you understand Claude's billing cycle and track active session progress.
  4 | 
  5 | ## Basic Usage
  6 | 
  7 | ```bash
  8 | ccusage blocks
  9 | ```
 10 | 
 11 | ## Example Output
 12 | 
 13 | ```
 14 | ╭──────────────────────────────────────────────────╮
 15 | │                                                  │
 16 | │  Claude Code Token Usage Report - Session Blocks │
 17 | │                                                  │
 18 | ╰──────────────────────────────────────────────────╯
 19 | 
 20 | ┌─────────────────────┬──────────────────┬────────┬─────────┬──────────────┬────────────┬──────────────┬────────────┐
 21 | │ Block Start Time    │ Models           │ Input  │ Output  │ Cache Create │ Cache Read │ Total Tokens │ Cost (USD) │
 22 | ├─────────────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┤
 23 | │ 2025-06-21 09:00:00 │ • opus-4         │  4,512 │ 285,846 │          512 │      1,024 │      291,894 │    $156.40 │
 24 | │ ⏰ Active (2h 15m)  │ • sonnet-4       │        │         │              │            │              │            │
 25 | │ 🔥 Rate: 2.1k/min   │                  │        │         │              │            │              │            │
 26 | │ 📊 Projected: 450k  │                  │        │         │              │            │              │            │
 27 | ├─────────────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┤
 28 | │ 2025-06-21 04:00:00 │ • sonnet-4       │  2,775 │ 186,645 │          256 │        768 │      190,444 │     $98.45 │
 29 | │ ✅ Completed (3h 42m)│                  │        │         │              │            │              │            │
 30 | ├─────────────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┤
 31 | │ 2025-06-20 15:30:00 │ • opus-4         │  1,887 │ 183,055 │          128 │        512 │      185,582 │     $81.73 │
 32 | │ ✅ Completed (4h 12m)│                  │        │         │              │            │              │            │
 33 | ├─────────────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┤
 34 | │ Total               │                  │  9,174 │ 655,546 │          896 │      2,304 │      667,920 │    $336.58 │
 35 | └─────────────────────┴──────────────────┴────────┴─────────┴──────────────┴────────────┴──────────────┴────────────┘
 36 | ```
 37 | 
 38 | ## Understanding Blocks
 39 | 
 40 | ### Session Block Concept
 41 | 
 42 | Claude Code uses **5-hour billing windows** for session tracking:
 43 | 
 44 | - **Block Start**: Triggered by your first message
 45 | - **Block Duration**: Lasts exactly 5 hours from start time
 46 | - **Rolling Windows**: New blocks start with activity after previous block expires
 47 | - **Billing Relevance**: Matches Claude's internal session tracking
 48 | - **UTC Time Handling**: Block boundaries are calculated in UTC to ensure consistent behavior across time zones
 49 | 
 50 | ### Block Status Indicators
 51 | 
 52 | - **⏰ Active**: Currently running block with time remaining
 53 | - **✅ Completed**: Finished block that ran its full duration or ended naturally
 54 | - **⌛ Gap**: Time periods with no activity (shown when relevant)
 55 | - **🔥 Rate**: Token burn rate (tokens per minute) for active blocks
 56 | - **📊 Projected**: Estimated total tokens if current rate continues
 57 | 
 58 | ## Command Options
 59 | 
 60 | ### Show Active Block Only
 61 | 
 62 | Focus on your current session with detailed projections:
 63 | 
 64 | ```bash
 65 | ccusage blocks --active
 66 | ```
 67 | 
 68 | This shows only the currently active block with:
 69 | 
 70 | - Time remaining in the 5-hour window
 71 | - Current token burn rate
 72 | - Projected final token count and cost
 73 | 
 74 | ### Show Recent Blocks
 75 | 
 76 | Display blocks from the last 3 days (including active):
 77 | 
 78 | ```bash
 79 | ccusage blocks --recent
 80 | ```
 81 | 
 82 | Perfect for understanding recent usage patterns without scrolling through all historical data.
 83 | 
 84 | ### Token Limit Tracking
 85 | 
 86 | Set token limits to monitor quota usage:
 87 | 
 88 | ```bash
 89 | # Set explicit token limit
 90 | ccusage blocks --token-limit 500000
 91 | 
 92 | # Use highest previous block as limit
 93 | ccusage blocks --token-limit max
 94 | # or short form:
 95 | ccusage blocks -t max
 96 | ```
 97 | 
 98 | When limits are set, blocks display:
 99 | 
100 | - ⚠️ **Warning indicators** when approaching limits
101 | - 🚨 **Alert indicators** when exceeding limits
102 | - **Progress bars** showing usage relative to limit
103 | 
104 | ### Live Monitoring
105 | 
106 | Real-time dashboard with automatic updates:
107 | 
108 | ```bash
109 | # Basic live monitoring (uses -t max automatically)
110 | ccusage blocks --live
111 | 
112 | # Live monitoring with explicit token limit
113 | ccusage blocks --live --token-limit 500000
114 | 
115 | # Custom refresh interval (1-60 seconds)
116 | ccusage blocks --live --refresh-interval 5
117 | ```
118 | 
119 | Live monitoring features:
120 | 
121 | - **Real-time updates** every 1-60 seconds (configurable)
122 | - **Automatic token limit** detection from usage history
123 | - **Progress bars** with color coding (green/yellow/red)
124 | - **Burn rate calculations** with trend analysis
125 | - **Time remaining** in current block
126 | - **Graceful shutdown** with Ctrl+C
127 | 
128 | ### Custom Session Duration
129 | 
130 | Change the block duration (default is 5 hours):
131 | 
132 | ```bash
133 | # 3-hour blocks
134 | ccusage blocks --session-length 3
135 | 
136 | # 8-hour blocks
137 | ccusage blocks --session-length 8
138 | ```
139 | 
140 | ### Date Filtering
141 | 
142 | Filter blocks by date range:
143 | 
144 | ```bash
145 | # Show blocks from specific date range
146 | ccusage blocks --since 20250620 --until 20250621
147 | 
148 | # Show blocks from last week
149 | ccusage blocks --since $(date -d '7 days ago' +%Y%m%d)
150 | ```
151 | 
152 | ### Sort Order
153 | 
154 | ```bash
155 | # Show newest blocks first (default)
156 | ccusage blocks --order desc
157 | 
158 | # Show oldest blocks first
159 | ccusage blocks --order asc
160 | ```
161 | 
162 | ### Cost Calculation Modes
163 | 
164 | ```bash
165 | # Use pre-calculated costs when available (default)
166 | ccusage blocks --mode auto
167 | 
168 | # Always calculate costs from tokens
169 | ccusage blocks --mode calculate
170 | 
171 | # Only show pre-calculated costs
172 | ccusage blocks --mode display
173 | ```
174 | 
175 | ### JSON Output
176 | 
177 | Export block data for analysis:
178 | 
179 | ```bash
180 | ccusage blocks --json
181 | ```
182 | 
183 | ```json
184 | {
185 | 	"blocks": [
186 | 		{
187 | 			"id": "2025-06-21T09:00:00.000Z",
188 | 			"startTime": "2025-06-21T09:00:00.000Z",
189 | 			"endTime": "2025-06-21T14:00:00.000Z",
190 | 			"actualEndTime": "2025-06-21T11:15:00.000Z",
191 | 			"isActive": true,
192 | 			"tokenCounts": {
193 | 				"inputTokens": 4512,
194 | 				"outputTokens": 285846,
195 | 				"cacheCreationInputTokens": 512,
196 | 				"cacheReadInputTokens": 1024
197 | 			},
198 | 			"costUSD": 156.40,
199 | 			"models": ["opus-4", "sonnet-4"]
200 | 		}
201 | 	]
202 | }
203 | ```
204 | 
205 | ### Offline Mode
206 | 
207 | Use cached pricing data without network access:
208 | 
209 | ```bash
210 | ccusage blocks --offline
211 | # or short form:
212 | ccusage blocks -O
213 | ```
214 | 
215 | ## Analysis Use Cases
216 | 
217 | ### Session Planning
218 | 
219 | Understanding 5-hour windows helps with:
220 | 
221 | ```bash
222 | # Check current active block
223 | ccusage blocks --active
224 | ```
225 | 
226 | - **Time Management**: Know how much time remains in current session
227 | - **Usage Pacing**: Monitor if you're on track for reasonable usage
228 | - **Break Planning**: Understand when current session will expire
229 | 
230 | ### Usage Optimization
231 | 
232 | ```bash
233 | # Find your highest usage patterns
234 | ccusage blocks -t max --recent
235 | ```
236 | 
237 | - **Peak Usage Identification**: Which blocks consumed the most tokens
238 | - **Efficiency Patterns**: Compare block efficiency (tokens per hour)
239 | - **Model Selection Impact**: How model choice affects block costs
240 | 
241 | ### Live Session Tracking
242 | 
243 | ```bash
244 | # Monitor active sessions in real-time
245 | ccusage blocks --live -t max
246 | ```
247 | 
248 | Perfect for:
249 | 
250 | - **Long coding sessions**: Track progress against historical limits
251 | - **Budget management**: Watch costs accumulate in real-time
252 | - **Productivity tracking**: Understand work intensity patterns
253 | 
254 | ### Historical Analysis
255 | 
256 | ```bash
257 | # Export data for detailed analysis
258 | ccusage blocks --json > blocks-history.json
259 | 
260 | # Analyze patterns over time
261 | ccusage blocks --since 20250601 --until 20250630
262 | ```
263 | 
264 | ## Block Analysis Tips
265 | 
266 | ### 1. Understanding Block Efficiency
267 | 
268 | Look for patterns in your block data:
269 | 
270 | - **High-efficiency blocks**: Lots of output tokens for minimal input
271 | - **Exploratory blocks**: High input/output ratios (research, debugging)
272 | - **Focused blocks**: Steady token burn rates with clear objectives
273 | 
274 | ### 2. Time Management
275 | 
276 | Use blocks to optimize your Claude usage:
277 | 
278 | - **Session planning**: Start important work at the beginning of blocks
279 | - **Break timing**: Use block boundaries for natural work breaks
280 | - **Batch processing**: Group similar tasks within single blocks
281 | 
282 | ### 3. Cost Optimization
283 | 
284 | Blocks help identify cost patterns:
285 | 
286 | - **Model switching**: When to use Opus vs Sonnet within blocks
287 | - **Cache efficiency**: How cache usage affects block costs
288 | - **Usage intensity**: Whether short focused sessions or long exploratory ones are more cost-effective
289 | 
290 | ### 4. Quota Management
291 | 
292 | When working with token limits:
293 | 
294 | - **Rate monitoring**: Watch burn rates to avoid exceeding limits
295 | - **Early warning**: Set limits below actual quotas for safety margin
296 | - **Usage spreading**: Distribute heavy usage across multiple blocks
297 | 
298 | ## Responsive Display
299 | 
300 | Blocks reports adapt to your terminal width:
301 | 
302 | - **Wide terminals (≥100 chars)**: Shows all columns with full timestamps
303 | - **Narrow terminals (<100 chars)**: Compact mode with abbreviated times and essential data
304 | 
305 | ## Advanced Features
306 | 
307 | ### Gap Detection
308 | 
309 | Blocks reports automatically detect and display gaps:
310 | 
311 | ```
312 | ┌─────────────────────┬──────────────────┬────────┬─────────┬────────────┐
313 | │ 2025-06-21 09:00:00 │ • opus-4         │  4,512 │ 285,846 │    $156.40 │
314 | │ ⏰ Active (2h 15m)  │ • sonnet-4       │        │         │            │
315 | ├─────────────────────┼──────────────────┼────────┼─────────┼────────────┤
316 | │ 2025-06-20 22:00:00 │ ⌛ 11h gap       │      0 │       0 │      $0.00 │
317 | │ 2025-06-21 09:00:00 │                  │        │         │            │
318 | ├─────────────────────┼──────────────────┼────────┼─────────┼────────────┤
319 | │ 2025-06-20 15:30:00 │ • opus-4         │  1,887 │ 183,055 │     $81.73 │
320 | │ ✅ Completed (4h 12m)│                  │        │         │            │
321 | └─────────────────────┴──────────────────┴────────┴─────────┴────────────┘
322 | ```
323 | 
324 | ### Burn Rate Calculations
325 | 
326 | For active blocks, the tool calculates:
327 | 
328 | - **Tokens per minute**: Based on activity within the block
329 | - **Cost per hour**: Projected hourly spend rate
330 | - **Projected totals**: Estimated final tokens/cost if current rate continues
331 | 
332 | ### Progress Visualization
333 | 
334 | When using token limits, blocks show visual progress:
335 | 
336 | - **Green**: Usage well below limit (< 70%)
337 | - **Yellow**: Approaching limit (70-90%)
338 | - **Red**: At or exceeding limit (≥ 90%)
339 | 
340 | ## Related Commands
341 | 
342 | - [Daily Reports](/guide/daily-reports) - Usage aggregated by calendar date
343 | - [Monthly Reports](/guide/monthly-reports) - Monthly usage summaries
344 | - [Session Reports](/guide/session-reports) - Individual conversation analysis
345 | - [Live Monitoring](/guide/live-monitoring) - Real-time session tracking
346 | 
347 | ## Next Steps
348 | 
349 | After understanding block patterns, consider:
350 | 
351 | 1. [Live Monitoring](/guide/live-monitoring) for real-time active session tracking
352 | 2. [Session Reports](/guide/session-reports) to analyze individual conversations within blocks
353 | 3. [Daily Reports](/guide/daily-reports) to see how blocks aggregate across days
354 | 


--------------------------------------------------------------------------------
/docs/guide/configuration.md:
--------------------------------------------------------------------------------
  1 | # Configuration
  2 | 
  3 | ccusage supports various configuration options to customize its behavior and adapt to different Claude Code installations.
  4 | 
  5 | ## Environment Variables
  6 | 
  7 | ### CLAUDE_CONFIG_DIR
  8 | 
  9 | The primary configuration option is the `CLAUDE_CONFIG_DIR` environment variable, which specifies where ccusage should look for Claude Code data.
 10 | 
 11 | #### Single Directory
 12 | 
 13 | ```bash
 14 | # Set a single custom Claude data directory
 15 | export CLAUDE_CONFIG_DIR="/path/to/your/claude/data"
 16 | ccusage daily
 17 | ```
 18 | 
 19 | #### Multiple Directories
 20 | 
 21 | ```bash
 22 | # Set multiple directories (comma-separated)
 23 | export CLAUDE_CONFIG_DIR="/path/to/claude1,/path/to/claude2"
 24 | ccusage daily
 25 | ```
 26 | 
 27 | When multiple directories are specified, ccusage automatically aggregates usage data from all valid locations.
 28 | 
 29 | ## Default Directory Detection
 30 | 
 31 | ### Automatic Detection
 32 | 
 33 | ccusage automatically searches for Claude Code data in these locations:
 34 | 
 35 | - **`~/.config/claude/projects/`** - New default location (Claude Code v1.0.30+)
 36 | - **`~/.claude/projects/`** - Legacy location (pre-v1.0.30)
 37 | 
 38 | ::: info Directory Change
 39 | The directory change from `~/.claude` to `~/.config/claude` in Claude Code v1.0.30 was an undocumented breaking change. ccusage handles both locations automatically for compatibility.
 40 | :::
 41 | 
 42 | ### Search Priority
 43 | 
 44 | When `CLAUDE_CONFIG_DIR` is not set, ccusage searches in this order:
 45 | 
 46 | 1. `~/.config/claude/projects/` (preferred)
 47 | 2. `~/.claude/projects/` (fallback)
 48 | 
 49 | Data from all valid directories is automatically combined.
 50 | 
 51 | ## Command-Line Options
 52 | 
 53 | ### Global Options
 54 | 
 55 | All ccusage commands support these configuration options:
 56 | 
 57 | ```bash
 58 | # Date filtering
 59 | ccusage daily --since 20250101 --until 20250630
 60 | 
 61 | # Output format
 62 | ccusage daily --json                    # JSON output
 63 | ccusage daily --breakdown              # Per-model breakdown
 64 | 
 65 | # Cost calculation modes
 66 | ccusage daily --mode auto              # Use costUSD when available (default)
 67 | ccusage daily --mode calculate         # Always calculate from tokens
 68 | ccusage daily --mode display           # Always use pre-calculated costUSD
 69 | 
 70 | # Sort order
 71 | ccusage daily --order desc             # Newest first (default)
 72 | ccusage daily --order asc              # Oldest first
 73 | 
 74 | # Offline mode
 75 | ccusage daily --offline                # Use cached pricing data
 76 | ccusage daily -O                       # Short alias
 77 | ```
 78 | 
 79 | ### Debug Options
 80 | 
 81 | ```bash
 82 | # Debug pricing mismatches
 83 | ccusage daily --debug
 84 | 
 85 | # Show sample discrepancies
 86 | ccusage daily --debug --debug-samples 10
 87 | ```
 88 | 
 89 | ## Cost Calculation Modes
 90 | 
 91 | ccusage supports three different cost calculation modes:
 92 | 
 93 | ### auto (Default)
 94 | 
 95 | Uses pre-calculated `costUSD` values when available, falls back to calculating costs from token counts:
 96 | 
 97 | ```bash
 98 | ccusage daily --mode auto
 99 | ```
100 | 
101 | - ✅ Most accurate when Claude provides cost data
102 | - ✅ Falls back gracefully for older data
103 | - ✅ Best for general use
104 | 
105 | ### calculate
106 | 
107 | Always calculates costs from token counts using model pricing, ignores pre-calculated values:
108 | 
109 | ```bash
110 | ccusage daily --mode calculate
111 | ```
112 | 
113 | - ✅ Consistent calculation method
114 | - ✅ Useful for comparing different time periods
115 | - ❌ May differ from actual Claude billing
116 | 
117 | ### display
118 | 
119 | Always uses pre-calculated `costUSD` values only, shows $0.00 for missing costs:
120 | 
121 | ```bash
122 | ccusage daily --mode display
123 | ```
124 | 
125 | - ✅ Shows only Claude-provided cost data
126 | - ✅ Most accurate for recent usage
127 | - ❌ Shows $0.00 for older entries without cost data
128 | 
129 | ## Offline Mode
130 | 
131 | ccusage can operate without network connectivity by using pre-cached pricing data:
132 | 
133 | ```bash
134 | # Use offline mode
135 | ccusage daily --offline
136 | ccusage monthly -O
137 | ```
138 | 
139 | ### When to Use Offline Mode
140 | 
141 | #### ✅ Ideal For
142 | 
143 | - **Air-gapped systems** - Networks with restricted internet access
144 | - **Corporate environments** - Behind firewalls or proxies
145 | - **Consistent pricing** - Using cached model pricing for consistent reports
146 | - **Fast execution** - Avoiding network delays
147 | 
148 | #### ❌ Limitations
149 | 
150 | - **Claude models only** - Only supports Claude models (Opus, Sonnet, etc.)
151 | - **Pricing updates** - Won't get latest pricing information
152 | - **New models** - May not support newly released models
153 | 
154 | ### Updating Cached Data
155 | 
156 | Cached pricing data is updated automatically when running in online mode. To refresh:
157 | 
158 | ```bash
159 | # Run online to update cache
160 | ccusage daily
161 | 
162 | # Then use offline mode
163 | ccusage daily --offline
164 | ```
165 | 
166 | ## MCP Server Configuration
167 | 
168 | ccusage includes a built-in MCP (Model Context Protocol) server for integration with other tools.
169 | 
170 | ### Basic Usage
171 | 
172 | ```bash
173 | # Start MCP server with stdio transport (default)
174 | ccusage mcp
175 | 
176 | # Start with HTTP transport
177 | ccusage mcp --type http --port 8080
178 | 
179 | # Configure cost calculation mode
180 | ccusage mcp --mode calculate
181 | ```
182 | 
183 | ### Claude Desktop Integration
184 | 
185 | Add to your Claude Desktop configuration file:
186 | 
187 | **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
188 | **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
189 | 
190 | ```json
191 | {
192 | 	"mcpServers": {
193 | 		"ccusage": {
194 | 			"command": "npx",
195 | 			"args": ["ccusage@latest", "mcp"],
196 | 			"env": {
197 | 				"CLAUDE_CONFIG_DIR": "/custom/path/to/claude"
198 | 			}
199 | 		}
200 | 	}
201 | }
202 | ```
203 | 
204 | Or with global installation:
205 | 
206 | ```json
207 | {
208 | 	"mcpServers": {
209 | 		"ccusage": {
210 | 			"command": "ccusage",
211 | 			"args": ["mcp"],
212 | 			"env": {}
213 | 		}
214 | 	}
215 | }
216 | ```
217 | 
218 | ### Available MCP Tools
219 | 
220 | - **`daily`** - Daily usage reports
221 | - **`monthly`** - Monthly usage reports
222 | - **`session`** - Session-based reports
223 | - **`blocks`** - 5-hour billing blocks reports
224 | 
225 | Each tool accepts `since`, `until`, and `mode` parameters.
226 | 
227 | ## Terminal Display Configuration
228 | 
229 | ccusage automatically adapts its display based on terminal width:
230 | 
231 | ### Wide Terminals (≥100 characters)
232 | 
233 | - Shows all columns with full model names
234 | - Displays cache metrics and total tokens
235 | - Uses bulleted model lists for readability
236 | 
237 | ### Narrow Terminals (<100 characters)
238 | 
239 | - Automatic compact mode with essential columns only
240 | - Shows Date, Models, Input, Output, Cost (USD)
241 | - Helpful message about expanding terminal width
242 | 
243 | ### Force Display Mode
244 | 
245 | Currently, display mode is automatic based on terminal width. Future versions may include manual override options.
246 | 
247 | ## Configuration Examples
248 | 
249 | ### Development Environment
250 | 
251 | ```bash
252 | # Set environment variables in your shell profile
253 | export CLAUDE_CONFIG_DIR="$HOME/.config/claude"
254 | 
255 | # Add aliases for common commands
256 | alias ccu-daily="ccusage daily --breakdown"
257 | alias ccu-live="ccusage blocks --live"
258 | alias ccu-json="ccusage daily --json"
259 | ```
260 | 
261 | ### CI/CD Environment
262 | 
263 | ```bash
264 | # Use offline mode in CI
265 | export CCUSAGE_OFFLINE=1
266 | ccusage daily --offline --json > usage-report.json
267 | ```
268 | 
269 | ### Multiple Team Members
270 | 
271 | ```bash
272 | # Each team member sets their own Claude directory
273 | export CLAUDE_CONFIG_DIR="/team-shared/claude-data/$USER"
274 | ccusage daily --since 20250101
275 | ```
276 | 
277 | ## Troubleshooting Configuration
278 | 
279 | ### Common Issues
280 | 
281 | #### No Data Found
282 | 
283 | If ccusage reports no data found:
284 | 
285 | ```bash
286 | # Check if Claude directories exist
287 | ls -la ~/.claude/projects/
288 | ls -la ~/.config/claude/projects/
289 | 
290 | # Verify environment variable
291 | echo $CLAUDE_CONFIG_DIR
292 | 
293 | # Test with explicit environment variable
294 | export CLAUDE_CONFIG_DIR="/path/to/claude/projects"
295 | ccusage daily
296 | ```
297 | 
298 | #### Permission Errors
299 | 
300 | ```bash
301 | # Check directory permissions
302 | ls -la ~/.claude/
303 | ls -la ~/.config/claude/
304 | 
305 | # Fix permissions if needed
306 | chmod -R 755 ~/.claude/
307 | chmod -R 755 ~/.config/claude/
308 | ```
309 | 
310 | #### Network Issues in Offline Mode
311 | 
312 | ```bash
313 | # Run online first to cache pricing data
314 | ccusage daily
315 | 
316 | # Then use offline mode
317 | ccusage daily --offline
318 | ```
319 | 
320 | ## Next Steps
321 | 
322 | After configuring ccusage:
323 | 
324 | - Learn about [Custom Paths](/guide/custom-paths) for advanced directory management
325 | - Explore [Cost Modes](/guide/cost-modes) for different calculation approaches
326 | - Try [Live Monitoring](/guide/live-monitoring) for real-time usage tracking
327 | 


--------------------------------------------------------------------------------
/docs/guide/cost-modes.md:
--------------------------------------------------------------------------------
  1 | # Cost Modes
  2 | 
  3 | ccusage supports three different cost calculation modes to handle various scenarios and data sources. Understanding these modes helps you get the most accurate cost estimates for your usage analysis.
  4 | 
  5 | ## Overview
  6 | 
  7 | Claude Code stores usage data in JSONL files with both token counts and pre-calculated cost information. ccusage can handle this data in different ways depending on your needs:
  8 | 
  9 | - **`auto`** - Smart mode using the best available data
 10 | - **`calculate`** - Always calculate from token counts
 11 | - **`display`** - Only show pre-calculated costs
 12 | 
 13 | ## Mode Details
 14 | 
 15 | ### auto (Default)
 16 | 
 17 | The `auto` mode intelligently chooses the best cost calculation method for each entry:
 18 | 
 19 | ```bash
 20 | ccusage daily --mode auto
 21 | # or simply:
 22 | ccusage daily
 23 | ```
 24 | 
 25 | #### How it works:
 26 | 
 27 | 1. **Pre-calculated costs available** → Uses Claude's `costUSD` values
 28 | 2. **No pre-calculated costs** → Calculates from token counts using model pricing
 29 | 3. **Mixed data** → Uses the best method for each entry
 30 | 
 31 | #### Best for:
 32 | 
 33 | - ✅ **General usage** - Works well for most scenarios
 34 | - ✅ **Mixed data sets** - Handles old and new data properly
 35 | - ✅ **Accuracy** - Uses official costs when available
 36 | - ✅ **Completeness** - Shows estimates for all entries
 37 | 
 38 | #### Example output:
 39 | 
 40 | ```
 41 | ┌──────────────┬─────────────┬────────┬─────────┬────────────┐
 42 | │ Date         │ Models      │ Input  │ Output  │ Cost (USD) │
 43 | ├──────────────┼─────────────┼────────┼─────────┼────────────┤
 44 | │ 2025-01-15   │ • opus-4    │  1,245 │  28,756 │    $12.45  │ ← Pre-calculated
 45 | │ 2024-12-20   │ • sonnet-4  │    856 │  19,234 │     $8.67  │ ← Calculated
 46 | │ 2024-11-10   │ • opus-4    │    634 │  15,678 │     $7.23  │ ← Calculated
 47 | └──────────────┴─────────────┴────────┴─────────┴────────────┘
 48 | ```
 49 | 
 50 | ### calculate
 51 | 
 52 | The `calculate` mode always computes costs from token counts using model pricing:
 53 | 
 54 | ```bash
 55 | ccusage daily --mode calculate
 56 | ccusage monthly --mode calculate --breakdown
 57 | ```
 58 | 
 59 | #### How it works:
 60 | 
 61 | 1. **Ignores `costUSD` values** from Claude Code data
 62 | 2. **Uses token counts** (input, output, cache) for all entries
 63 | 3. **Applies current model pricing** from LiteLLM database
 64 | 4. **Consistent methodology** across all time periods
 65 | 
 66 | #### Best for:
 67 | 
 68 | - ✅ **Consistent comparisons** - Same calculation method for all data
 69 | - ✅ **Token analysis** - Understanding pure token-based costs
 70 | - ✅ **Historical analysis** - Comparing costs across different time periods
 71 | - ✅ **Pricing research** - Analyzing cost per token trends
 72 | 
 73 | #### Example output:
 74 | 
 75 | ```
 76 | ┌──────────────┬─────────────┬────────┬─────────┬────────────┐
 77 | │ Date         │ Models      │ Input  │ Output  │ Cost (USD) │
 78 | ├──────────────┼─────────────┼────────┼─────────┼────────────┤
 79 | │ 2025-01-15   │ • opus-4    │  1,245 │  28,756 │    $12.38  │ ← Calculated
 80 | │ 2024-12-20   │ • sonnet-4  │    856 │  19,234 │     $8.67  │ ← Calculated
 81 | │ 2024-11-10   │ • opus-4    │    634 │  15,678 │     $7.23  │ ← Calculated
 82 | └──────────────┴─────────────┴────────┴─────────┴────────────┘
 83 | ```
 84 | 
 85 | ### display
 86 | 
 87 | The `display` mode only shows pre-calculated costs from Claude Code:
 88 | 
 89 | ```bash
 90 | ccusage daily --mode display
 91 | ccusage session --mode display --json
 92 | ```
 93 | 
 94 | #### How it works:
 95 | 
 96 | 1. **Uses only `costUSD` values** from Claude Code data
 97 | 2. **Shows $0.00** for entries without pre-calculated costs
 98 | 3. **No token-based calculations** performed
 99 | 4. **Exact Claude billing data** when available
100 | 
101 | #### Best for:
102 | 
103 | - ✅ **Official costs only** - Shows exactly what Claude calculated
104 | - ✅ **Billing verification** - Comparing with actual Claude charges
105 | - ✅ **Recent data** - Most accurate for newer usage entries
106 | - ✅ **Audit purposes** - Verifying pre-calculated costs
107 | 
108 | #### Example output:
109 | 
110 | ```
111 | ┌──────────────┬─────────────┬────────┬─────────┬────────────┐
112 | │ Date         │ Models      │ Input  │ Output  │ Cost (USD) │
113 | ├──────────────┼─────────────┼────────┼─────────┼────────────┤
114 | │ 2025-01-15   │ • opus-4    │  1,245 │  28,756 │    $12.45  │ ← Pre-calculated
115 | │ 2024-12-20   │ • sonnet-4  │    856 │  19,234 │     $0.00  │ ← No cost data
116 | │ 2024-11-10   │ • opus-4    │    634 │  15,678 │     $0.00  │ ← No cost data
117 | └──────────────┴─────────────┴────────┴─────────┴────────────┘
118 | ```
119 | 
120 | ## Practical Examples
121 | 
122 | ### Scenario 1: Mixed Data Analysis
123 | 
124 | You have data from different time periods with varying cost information:
125 | 
126 | ```bash
127 | # Auto mode handles mixed data intelligently
128 | ccusage daily --mode auto --since 20241201
129 | 
130 | # Shows:
131 | # - Pre-calculated costs for recent entries (Jan 2025)
132 | # - Calculated costs for older entries (Dec 2024)
133 | ```
134 | 
135 | ### Scenario 2: Consistent Cost Comparison
136 | 
137 | You want to compare costs across different months using the same methodology:
138 | 
139 | ```bash
140 | # Calculate mode ensures consistent methodology
141 | ccusage monthly --mode calculate --breakdown
142 | 
143 | # All months use the same token-based calculation
144 | # Useful for trend analysis and cost projections
145 | ```
146 | 
147 | ### Scenario 3: Billing Verification
148 | 
149 | You want to verify Claude's official cost calculations:
150 | 
151 | ```bash
152 | # Display mode shows only official Claude costs
153 | ccusage daily --mode display --since 20250101
154 | 
155 | # Compare with your Claude billing dashboard
156 | # Entries without costs show $0.00
157 | ```
158 | 
159 | ### Scenario 4: Historical Analysis
160 | 
161 | Analyzing usage patterns over time:
162 | 
163 | ```bash
164 | # Auto mode for complete picture
165 | ccusage daily --mode auto --since 20240101 --until 20241231
166 | 
167 | # Calculate mode for consistent comparison
168 | ccusage monthly --mode calculate --order asc
169 | ```
170 | 
171 | ## Cost Calculation Details
172 | 
173 | ### Token-Based Calculation
174 | 
175 | When calculating costs from tokens, ccusage uses:
176 | 
177 | #### Model Pricing Sources
178 | 
179 | - **LiteLLM database** - Up-to-date model pricing
180 | - **Automatic updates** - Pricing refreshed regularly
181 | - **Multiple models** - Supports Claude Opus, Sonnet, and other models
182 | 
183 | #### Token Types
184 | 
185 | ```typescript
186 | type TokenCosts = {
187 | 	input: number; // Input tokens
188 | 	output: number; // Output tokens
189 | 	cacheCreate: number; // Cache creation tokens
190 | 	cacheRead: number; // Cache read tokens
191 | };
192 | ```
193 | 
194 | #### Calculation Formula
195 | 
196 | ```typescript
197 | totalCost
198 | 	= (inputTokens * inputPrice)
199 | 		+ (outputTokens * outputPrice)
200 | 		+ (cacheCreateTokens * cacheCreatePrice)
201 | 		+ (cacheReadTokens * cacheReadPrice);
202 | ```
203 | 
204 | ### Pre-calculated Costs
205 | 
206 | Claude Code provides `costUSD` values in JSONL files:
207 | 
208 | ```json
209 | {
210 | 	"timestamp": "2025-01-15T10:30:00Z",
211 | 	"model": "claude-opus-4-20250514",
212 | 	"usage": {
213 | 		"input_tokens": 1245,
214 | 		"output_tokens": 28756,
215 | 		"cache_creation_input_tokens": 512,
216 | 		"cache_read_input_tokens": 256
217 | 	},
218 | 	"costUSD": 12.45
219 | }
220 | ```
221 | 
222 | ## Debug Mode
223 | 
224 | Use debug mode to understand cost calculation discrepancies:
225 | 
226 | ```bash
227 | ccusage daily --mode auto --debug
228 | ```
229 | 
230 | Shows:
231 | 
232 | - **Pricing mismatches** between calculated and pre-calculated costs
233 | - **Missing cost data** entries
234 | - **Calculation details** for each entry
235 | - **Sample discrepancies** for investigation
236 | 
237 | ```bash
238 | # Show more sample discrepancies
239 | ccusage daily --debug --debug-samples 10
240 | ```
241 | 
242 | ## Mode Selection Guide
243 | 
244 | ### When to use `auto` mode:
245 | 
246 | - **General usage** - Default for most scenarios
247 | - **Mixed data sets** - Combining old and new usage data
248 | - **Maximum accuracy** - Best available cost information
249 | - **Regular reporting** - Daily/monthly usage tracking
250 | 
251 | ### When to use `calculate` mode:
252 | 
253 | - **Consistent analysis** - Comparing different time periods
254 | - **Token cost research** - Understanding pure token costs
255 | - **Pricing validation** - Verifying calculated vs actual costs
256 | - **Historical comparison** - Analyzing cost trends over time
257 | 
258 | ### When to use `display` mode:
259 | 
260 | - **Billing verification** - Comparing with Claude charges
261 | - **Official costs only** - Trusting Claude's calculations
262 | - **Recent data analysis** - Most accurate for new usage
263 | - **Audit purposes** - Verifying pre-calculated costs
264 | 
265 | ## Advanced Usage
266 | 
267 | ### Combining with Other Options
268 | 
269 | ```bash
270 | # Calculate mode with breakdown by model
271 | ccusage daily --mode calculate --breakdown
272 | 
273 | # Display mode with JSON output for analysis
274 | ccusage session --mode display --json | jq '.[] | select(.totalCost > 0)'
275 | 
276 | # Auto mode with date filtering
277 | ccusage monthly --mode auto --since 20240101 --order asc
278 | ```
279 | 
280 | ### Performance Considerations
281 | 
282 | - **`display` mode** - Fastest (no calculations)
283 | - **`auto` mode** - Moderate (conditional calculations)
284 | - **`calculate` mode** - Slowest (always calculates)
285 | 
286 | ### Offline Mode Compatibility
287 | 
288 | ```bash
289 | # All modes work with offline pricing data
290 | ccusage daily --mode calculate --offline
291 | ccusage monthly --mode auto --offline
292 | ```
293 | 
294 | ## Common Issues and Solutions
295 | 
296 | ### Issue: Costs showing as $0.00
297 | 
298 | **Cause**: Using `display` mode with data that lacks pre-calculated costs
299 | 
300 | **Solution**:
301 | 
302 | ```bash
303 | # Switch to auto or calculate mode
304 | ccusage daily --mode auto
305 | ccusage daily --mode calculate
306 | ```
307 | 
308 | ### Issue: Inconsistent cost calculations
309 | 
310 | **Cause**: Mixed use of different modes or pricing changes
311 | 
312 | **Solution**:
313 | 
314 | ```bash
315 | # Use calculate mode for consistency
316 | ccusage daily --mode calculate --since 20240101
317 | ```
318 | 
319 | ### Issue: Large discrepancies in debug mode
320 | 
321 | **Cause**: Pricing updates or model changes
322 | 
323 | **Solution**:
324 | 
325 | ```bash
326 | # Check for pricing updates
327 | ccusage daily --mode auto  # Updates pricing cache
328 | ccusage daily --mode calculate --debug  # Compare calculations
329 | ```
330 | 
331 | ### Issue: Missing cost data for recent entries
332 | 
333 | **Cause**: Claude Code hasn't calculated costs yet
334 | 
335 | **Solution**:
336 | 
337 | ```bash
338 | # Use calculate mode as fallback
339 | ccusage daily --mode calculate
340 | ```
341 | 
342 | ## Next Steps
343 | 
344 | After understanding cost modes:
345 | 
346 | - Explore [Configuration](/guide/configuration) for environment setup
347 | - Learn about [Custom Paths](/guide/custom-paths) for multiple data sources
348 | - Try [Live Monitoring](/guide/live-monitoring) with different cost modes
349 | 


--------------------------------------------------------------------------------
/docs/guide/custom-paths.md:
--------------------------------------------------------------------------------
  1 | # Custom Paths
  2 | 
  3 | ccusage supports flexible path configuration to handle various Claude Code installation scenarios and custom data locations.
  4 | 
  5 | ## Overview
  6 | 
  7 | By default, ccusage automatically detects Claude Code data in standard locations. However, you can customize these paths for:
  8 | 
  9 | - **Multiple Claude installations** - Different versions or profiles
 10 | - **Custom data locations** - Non-standard installation directories
 11 | - **Shared environments** - Team or organization setups
 12 | - **Backup/archive analysis** - Analyzing historical data from different locations
 13 | 
 14 | ## CLAUDE_CONFIG_DIR Environment Variable
 15 | 
 16 | The primary method for specifying custom paths is the `CLAUDE_CONFIG_DIR` environment variable.
 17 | 
 18 | ### Single Custom Path
 19 | 
 20 | Specify one custom directory:
 21 | 
 22 | ```bash
 23 | # Set environment variable
 24 | export CLAUDE_CONFIG_DIR="/path/to/your/claude/data"
 25 | 
 26 | # Use with any command
 27 | ccusage daily
 28 | ccusage monthly --breakdown
 29 | ccusage blocks --live
 30 | ```
 31 | 
 32 | Example scenarios:
 33 | 
 34 | ```bash
 35 | # Custom installation location
 36 | export CLAUDE_CONFIG_DIR="/opt/claude-code/.claude"
 37 | 
 38 | # User-specific directory
 39 | export CLAUDE_CONFIG_DIR="/home/username/Documents/claude-data"
 40 | 
 41 | # Network drive
 42 | export CLAUDE_CONFIG_DIR="/mnt/shared/claude-usage"
 43 | ```
 44 | 
 45 | ### Multiple Custom Paths
 46 | 
 47 | Specify multiple directories separated by commas:
 48 | 
 49 | ```bash
 50 | # Multiple installations
 51 | export CLAUDE_CONFIG_DIR="/path/to/claude1,/path/to/claude2"
 52 | 
 53 | # Current and archived data
 54 | export CLAUDE_CONFIG_DIR="~/.claude,/backup/claude-archive"
 55 | 
 56 | # Team member data aggregation
 57 | export CLAUDE_CONFIG_DIR="/team/alice/.claude,/team/bob/.claude,/team/charlie/.claude"
 58 | ```
 59 | 
 60 | When multiple paths are specified:
 61 | 
 62 | - ✅ **Data aggregation** - Usage from all paths is automatically combined
 63 | - ✅ **Automatic filtering** - Invalid or empty directories are silently skipped
 64 | - ✅ **Consistent reporting** - All reports show unified data across paths
 65 | 
 66 | ## Default Path Detection
 67 | 
 68 | ### Standard Locations
 69 | 
 70 | When `CLAUDE_CONFIG_DIR` is not set, ccusage searches these locations automatically:
 71 | 
 72 | 1. **`~/.config/claude/projects/`** - New default (Claude Code v1.0.30+)
 73 | 2. **`~/.claude/projects/`** - Legacy location (pre-v1.0.30)
 74 | 
 75 | ### Version Compatibility
 76 | 
 77 | ::: info Breaking Change
 78 | Claude Code v1.0.30 moved data from `~/.claude` to `~/.config/claude` without documentation. ccusage handles both locations automatically for seamless compatibility.
 79 | :::
 80 | 
 81 | #### Migration Scenarios
 82 | 
 83 | **Scenario 1: Fresh Installation**
 84 | 
 85 | ```bash
 86 | # Claude Code v1.0.30+ - uses new location
 87 | ls ~/.config/claude/projects/
 88 | 
 89 | # ccusage automatically finds data
 90 | ccusage daily
 91 | ```
 92 | 
 93 | **Scenario 2: Upgraded Installation**
 94 | 
 95 | ```bash
 96 | # Old data still exists
 97 | ls ~/.claude/projects/
 98 | 
 99 | # New data in new location
100 | ls ~/.config/claude/projects/
101 | 
102 | # ccusage combines both automatically
103 | ccusage daily  # Shows data from both locations
104 | ```
105 | 
106 | **Scenario 3: Manual Migration**
107 | 
108 | ```bash
109 | # If you moved data manually
110 | export CLAUDE_CONFIG_DIR="/custom/location/claude"
111 | ccusage daily
112 | ```
113 | 
114 | ## Path Structure Requirements
115 | 
116 | ### Expected Directory Structure
117 | 
118 | ccusage expects this directory structure:
119 | 
120 | ```
121 | claude-data-directory/
122 | ├── projects/
123 | │   ├── project-1/
124 | │   │   ├── session-1/
125 | │   │   │   ├── file1.jsonl
126 | │   │   │   └── file2.jsonl
127 | │   │   └── session-2/
128 | │   │       └── file3.jsonl
129 | │   └── project-2/
130 | │       └── session-3/
131 | │           └── file4.jsonl
132 | ```
133 | 
134 | ### Validation
135 | 
136 | ccusage validates paths by checking:
137 | 
138 | - **Directory exists** and is readable
139 | - **Contains `projects/` subdirectory**
140 | - **Has JSONL files** in the expected structure
141 | 
142 | Invalid paths are automatically skipped with debug information available.
143 | 
144 | ## Common Use Cases
145 | 
146 | ### Multiple Claude Profiles
147 | 
148 | If you use multiple Claude profiles or installations:
149 | 
150 | ```bash
151 | # Work profile
152 | export CLAUDE_CONFIG_DIR="/Users/username/.config/claude-work"
153 | 
154 | # Personal profile
155 | export CLAUDE_CONFIG_DIR="/Users/username/.config/claude-personal"
156 | 
157 | # Combined analysis
158 | export CLAUDE_CONFIG_DIR="/Users/username/.config/claude-work,/Users/username/.config/claude-personal"
159 | ```
160 | 
161 | ### Team Environments
162 | 
163 | For team usage analysis:
164 | 
165 | ```bash
166 | # Individual analysis
167 | export CLAUDE_CONFIG_DIR="/shared/claude-data/$USER"
168 | ccusage daily
169 | 
170 | # Team aggregate
171 | export CLAUDE_CONFIG_DIR="/shared/claude-data/alice,/shared/claude-data/bob"
172 | ccusage monthly --breakdown
173 | ```
174 | 
175 | ### Development vs Production
176 | 
177 | Separate environments:
178 | 
179 | ```bash
180 | # Development environment
181 | export CLAUDE_CONFIG_DIR="/dev/claude-data"
182 | ccusage daily --since 20250101
183 | 
184 | # Production environment
185 | export CLAUDE_CONFIG_DIR="/prod/claude-data"
186 | ccusage daily --since 20250101
187 | ```
188 | 
189 | ### Historical Analysis
190 | 
191 | Analyzing archived or backup data:
192 | 
193 | ```bash
194 | # Current month
195 | export CLAUDE_CONFIG_DIR="~/.config/claude"
196 | ccusage monthly
197 | 
198 | # Compare with previous month backup
199 | export CLAUDE_CONFIG_DIR="/backup/claude-2024-12"
200 | ccusage monthly --since 20241201 --until 20241231
201 | 
202 | # Combined analysis
203 | export CLAUDE_CONFIG_DIR="~/.config/claude,/backup/claude-2024-12"
204 | ccusage monthly --since 20241201
205 | ```
206 | 
207 | ## Shell Integration
208 | 
209 | ### Setting Persistent Environment Variables
210 | 
211 | #### Bash/Zsh
212 | 
213 | Add to `~/.bashrc`, `~/.zshrc`, or `~/.profile`:
214 | 
215 | ```bash
216 | # Default Claude data directory
217 | export CLAUDE_CONFIG_DIR="$HOME/.config/claude"
218 | 
219 | # Or multiple directories
220 | export CLAUDE_CONFIG_DIR="$HOME/.config/claude,$HOME/.claude"
221 | ```
222 | 
223 | #### Fish Shell
224 | 
225 | Add to `~/.config/fish/config.fish`:
226 | 
227 | ```fish
228 | # Default Claude data directory
229 | set -gx CLAUDE_CONFIG_DIR "$HOME/.config/claude"
230 | 
231 | # Or multiple directories
232 | set -gx CLAUDE_CONFIG_DIR "$HOME/.config/claude,$HOME/.claude"
233 | ```
234 | 
235 | ### Temporary Path Override
236 | 
237 | For one-time analysis without changing environment:
238 | 
239 | ```bash
240 | # Temporary override for single command
241 | CLAUDE_CONFIG_DIR="/tmp/claude-backup" ccusage daily
242 | 
243 | # Multiple commands with temporary override
244 | (
245 |   export CLAUDE_CONFIG_DIR="/archive/claude-2024"
246 |   ccusage daily --json > 2024-report.json
247 |   ccusage monthly --breakdown > 2024-monthly.txt
248 | )
249 | ```
250 | 
251 | ### Aliases and Functions
252 | 
253 | Create convenient aliases:
254 | 
255 | ```bash
256 | # ~/.bashrc or ~/.zshrc
257 | alias ccu-work="CLAUDE_CONFIG_DIR='/work/claude' ccusage"
258 | alias ccu-personal="CLAUDE_CONFIG_DIR='/personal/claude' ccusage"
259 | alias ccu-archive="CLAUDE_CONFIG_DIR='/archive/claude' ccusage"
260 | 
261 | # Usage
262 | ccu-work daily
263 | ccu-personal monthly --breakdown
264 | ccu-archive session --since 20240101
265 | ```
266 | 
267 | Or use functions for more complex setups:
268 | 
269 | ```bash
270 | # Function to analyze specific time periods
271 | ccu-period() {
272 |   local period=$1
273 |   local path="/archive/claude-$period"
274 | 
275 |   if [[ -d "$path" ]]; then
276 |     CLAUDE_CONFIG_DIR="$path" ccusage daily --since "${period}01" --until "${period}31"
277 |   else
278 |     echo "Archive not found: $path"
279 |   fi
280 | }
281 | 
282 | # Usage
283 | ccu-period 202412  # December 2024
284 | ccu-period 202501  # January 2025
285 | ```
286 | 
287 | ## MCP Integration with Custom Paths
288 | 
289 | When using ccusage as an MCP server with custom paths:
290 | 
291 | ### Claude Desktop Configuration
292 | 
293 | ```json
294 | {
295 | 	"mcpServers": {
296 | 		"ccusage": {
297 | 			"command": "ccusage",
298 | 			"args": ["mcp"],
299 | 			"env": {
300 | 				"CLAUDE_CONFIG_DIR": "/path/to/your/claude/data"
301 | 			}
302 | 		},
303 | 		"ccusage-archive": {
304 | 			"command": "ccusage",
305 | 			"args": ["mcp"],
306 | 			"env": {
307 | 				"CLAUDE_CONFIG_DIR": "/archive/claude-2024,/archive/claude-2025"
308 | 			}
309 | 		}
310 | 	}
311 | }
312 | ```
313 | 
314 | This allows you to have multiple MCP servers analyzing different data sets.
315 | 
316 | ## Troubleshooting Custom Paths
317 | 
318 | ### Path Validation
319 | 
320 | Check if your custom path is valid:
321 | 
322 | ```bash
323 | # Test path manually
324 | ls -la "$CLAUDE_CONFIG_DIR/projects/"
325 | 
326 | # Run with debug output
327 | ccusage daily --debug
328 | ```
329 | 
330 | ### Common Issues
331 | 
332 | #### Path Not Found
333 | 
334 | ```bash
335 | # Error: Directory doesn't exist
336 | export CLAUDE_CONFIG_DIR="/nonexistent/path"
337 | ccusage daily
338 | # Result: No data found
339 | 
340 | # Solution: Verify path exists
341 | ls -la /nonexistent/path
342 | ```
343 | 
344 | #### Permission Issues
345 | 
346 | ```bash
347 | # Error: Permission denied
348 | export CLAUDE_CONFIG_DIR="/root/.claude"
349 | ccusage daily  # May fail if no read permission
350 | 
351 | # Solution: Check permissions
352 | ls -la /root/.claude
353 | ```
354 | 
355 | #### Multiple Paths Syntax
356 | 
357 | ```bash
358 | # Wrong: Using semicolon or space
359 | export CLAUDE_CONFIG_DIR="/path1;/path2"  # ❌
360 | export CLAUDE_CONFIG_DIR="/path1 /path2"  # ❌
361 | 
362 | # Correct: Using comma
363 | export CLAUDE_CONFIG_DIR="/path1,/path2"  # ✅
364 | ```
365 | 
366 | #### Data Structure Issues
367 | 
368 | ```bash
369 | # Wrong structure
370 | /custom/claude/
371 | ├── file1.jsonl  # ❌ Files in wrong location
372 | └── data/
373 |     └── file2.jsonl
374 | 
375 | # Correct structure
376 | /custom/claude/
377 | └── projects/
378 |     └── project1/
379 |         └── session1/
380 |             └── file1.jsonl
381 | ```
382 | 
383 | ### Debug Mode
384 | 
385 | Use debug mode to troubleshoot path issues:
386 | 
387 | ```bash
388 | ccusage daily --debug
389 | 
390 | # Shows:
391 | # - Which paths are being searched
392 | # - Which paths are valid/invalid
393 | # - How many files are found in each path
394 | # - Any permission or structure issues
395 | ```
396 | 
397 | ## Performance Considerations
398 | 
399 | ### Large Data Sets
400 | 
401 | When using multiple paths with large data sets:
402 | 
403 | ```bash
404 | # Filter by date to improve performance
405 | ccusage daily --since 20250101 --until 20250131
406 | 
407 | # Use JSON output for programmatic processing
408 | ccusage daily --json | jq '.[] | select(.totalCost > 10)'
409 | ```
410 | 
411 | ### Network Paths
412 | 
413 | For network-mounted directories:
414 | 
415 | ```bash
416 | # Ensure network path is mounted
417 | mount | grep claude-data
418 | 
419 | # Consider local caching for frequently accessed data
420 | rsync -av /network/claude-data/ /local/cache/claude-data/
421 | export CLAUDE_CONFIG_DIR="/local/cache/claude-data"
422 | ```
423 | 
424 | ## Next Steps
425 | 
426 | After setting up custom paths:
427 | 
428 | - Learn about [Configuration](/guide/configuration) for additional options
429 | - Explore [Cost Modes](/guide/cost-modes) for different calculation methods
430 | - Set up [Live Monitoring](/guide/live-monitoring) with your custom data
431 | 


--------------------------------------------------------------------------------
/docs/guide/daily-reports.md:
--------------------------------------------------------------------------------
  1 | # Daily Reports
  2 | 
  3 | ![Daily usage report showing token usage and costs by date with model breakdown](/screenshot.png)
  4 | 
  5 | Daily reports show token usage and costs aggregated by calendar date, giving you a clear view of your Claude Code usage patterns over time.
  6 | 
  7 | ## Basic Usage
  8 | 
  9 | Show all daily usage:
 10 | 
 11 | ```bash
 12 | ccusage daily
 13 | # or simply:
 14 | ccusage
 15 | ```
 16 | 
 17 | The daily command is the default, so you can omit it when running ccusage.
 18 | 
 19 | ## Example Output
 20 | 
 21 | ![Daily usage report showing token usage and costs by date with model breakdown](/screenshot.png)
 22 | 
 23 | ## Understanding the Columns
 24 | 
 25 | ### Basic Columns
 26 | 
 27 | - **Date**: Calendar date in YYYY-MM-DD format
 28 | - **Models**: Claude models used that day (shown as bulleted list)
 29 | - **Input**: Total input tokens sent to Claude
 30 | - **Output**: Total output tokens received from Claude
 31 | - **Cost (USD)**: Estimated cost for that day
 32 | 
 33 | ### Cache Columns
 34 | 
 35 | - **Cache Create**: Tokens used to create cache entries
 36 | - **Cache Read**: Tokens read from cache (typically cheaper)
 37 | 
 38 | ### Responsive Display
 39 | 
 40 | ccusage automatically adapts to your terminal width:
 41 | 
 42 | - **Wide terminals (≥100 chars)**: Shows all columns
 43 | - **Narrow terminals (<100 chars)**: Compact mode with essential columns only
 44 | 
 45 | ## Command Options
 46 | 
 47 | ### Date Filtering
 48 | 
 49 | Filter reports by date range:
 50 | 
 51 | ```bash
 52 | # Show usage from December 2024
 53 | ccusage daily --since 20241201 --until 20241231
 54 | 
 55 | # Show last week
 56 | ccusage daily --since 20241215 --until 20241222
 57 | 
 58 | # Show usage since a specific date
 59 | ccusage daily --since 20241201
 60 | ```
 61 | 
 62 | ### Sort Order
 63 | 
 64 | Control the order of dates:
 65 | 
 66 | ```bash
 67 | # Newest dates first (default)
 68 | ccusage daily --order desc
 69 | 
 70 | # Oldest dates first
 71 | ccusage daily --order asc
 72 | ```
 73 | 
 74 | ### Cost Calculation Modes
 75 | 
 76 | Control how costs are calculated:
 77 | 
 78 | ```bash
 79 | # Use pre-calculated costs when available (default)
 80 | ccusage daily --mode auto
 81 | 
 82 | # Always calculate costs from tokens
 83 | ccusage daily --mode calculate
 84 | 
 85 | # Only show pre-calculated costs
 86 | ccusage daily --mode display
 87 | ```
 88 | 
 89 | ### Model Breakdown
 90 | 
 91 | See per-model cost breakdown:
 92 | 
 93 | ```bash
 94 | ccusage daily --breakdown
 95 | ```
 96 | 
 97 | This shows costs split by individual models:
 98 | 
 99 | ```
100 | ┌──────────────┬──────────────────┬────────┬─────────┬────────────┐
101 | │ Date         │ Models           │ Input  │ Output  │ Cost (USD) │
102 | ├──────────────┼──────────────────┼────────┼─────────┼────────────┤
103 | │ 2025-06-21   │ opus-4, sonnet-4 │    277 │  31,456 │     $17.58 │
104 | ├──────────────┼──────────────────┼────────┼─────────┼────────────┤
105 | │   └─ opus-4  │                  │    100 │  15,000 │     $10.25 │
106 | ├──────────────┼──────────────────┼────────┼─────────┼────────────┤
107 | │   └─ sonnet-4│                  │    177 │  16,456 │      $7.33 │
108 | └──────────────┴──────────────────┴────────┴─────────┴────────────┘
109 | ```
110 | 
111 | ### JSON Output
112 | 
113 | Export data as JSON for further analysis:
114 | 
115 | ```bash
116 | ccusage daily --json
117 | ```
118 | 
119 | ```json
120 | {
121 | 	"type": "daily",
122 | 	"data": [
123 | 		{
124 | 			"date": "2025-06-21",
125 | 			"models": ["claude-opus-4-20250514", "claude-sonnet-4-20250514"],
126 | 			"inputTokens": 277,
127 | 			"outputTokens": 31456,
128 | 			"cacheCreationTokens": 512,
129 | 			"cacheReadTokens": 1024,
130 | 			"totalTokens": 33269,
131 | 			"costUSD": 17.58
132 | 		}
133 | 	],
134 | 	"summary": {
135 | 		"totalInputTokens": 277,
136 | 		"totalOutputTokens": 31456,
137 | 		"totalCacheCreationTokens": 512,
138 | 		"totalCacheReadTokens": 1024,
139 | 		"totalTokens": 33269,
140 | 		"totalCostUSD": 17.58
141 | 	}
142 | }
143 | ```
144 | 
145 | ### Offline Mode
146 | 
147 | Use cached pricing data without network access:
148 | 
149 | ```bash
150 | ccusage daily --offline
151 | # or short form:
152 | ccusage daily -O
153 | ```
154 | 
155 | ## Common Use Cases
156 | 
157 | ### Track Monthly Spending
158 | 
159 | ```bash
160 | # See December 2024 usage
161 | ccusage daily --since 20241201 --until 20241231
162 | ```
163 | 
164 | ### Find Expensive Days
165 | 
166 | ```bash
167 | # Sort by cost (highest first)
168 | ccusage daily --order desc
169 | ```
170 | 
171 | ### Export for Spreadsheet Analysis
172 | 
173 | ```bash
174 | ccusage daily --json > december-usage.json
175 | ```
176 | 
177 | ### Compare Model Usage
178 | 
179 | ```bash
180 | # See which models you use most
181 | ccusage daily --breakdown
182 | ```
183 | 
184 | ### Check Recent Activity
185 | 
186 | ```bash
187 | # Last 7 days
188 | ccusage daily --since $(date -d '7 days ago' +%Y%m%d)
189 | ```
190 | 
191 | ## Tips
192 | 
193 | 1. **Compact Mode**: If your terminal is narrow, expand it to see all columns
194 | 2. **Date Format**: Use YYYYMMDD format for date filters (e.g., 20241225)
195 | 3. **Regular Monitoring**: Run daily reports regularly to track usage patterns
196 | 4. **JSON Export**: Use `--json` for creating charts or additional analysis
197 | 
198 | ## Related Commands
199 | 
200 | - [Monthly Reports](/guide/monthly-reports) - Aggregate by month
201 | - [Session Reports](/guide/session-reports) - Per-conversation analysis
202 | - [Blocks Reports](/guide/blocks-reports) - 5-hour billing windows
203 | - [Live Monitoring](/guide/live-monitoring) - Real-time tracking
204 | 


--------------------------------------------------------------------------------
/docs/guide/getting-started.md:
--------------------------------------------------------------------------------
  1 | # Getting Started
  2 | 
  3 | Welcome to ccusage! This guide will help you get up and running with analyzing your Claude Code usage data.
  4 | 
  5 | ## Prerequisites
  6 | 
  7 | - Claude Code installed and used (generates JSONL files)
  8 | - Node.js 20+ or Bun runtime
  9 | 
 10 | ## Quick Start
 11 | 
 12 | The fastest way to try ccusage is to run it directly without installation:
 13 | 
 14 | ::: code-group
 15 | 
 16 | ```bash [npx]
 17 | npx ccusage@latest
 18 | ```
 19 | 
 20 | ```bash [bunx]
 21 | bunx ccusage
 22 | ```
 23 | 
 24 | ```bash [pnpm]
 25 | pnpm dlx ccusage
 26 | ```
 27 | 
 28 | :::
 29 | 
 30 | This will show your daily usage report by default.
 31 | 
 32 | ## Your First Report
 33 | 
 34 | When you run ccusage for the first time, you'll see a table showing your Claude Code usage by date:
 35 | 
 36 | ```
 37 | ╭──────────────────────────────────────────╮
 38 | │                                          │
 39 | │  Claude Code Token Usage Report - Daily  │
 40 | │                                          │
 41 | ╰──────────────────────────────────────────╯
 42 | 
 43 | ┌──────────────┬──────────────────┬────────┬─────────┬────────────┐
 44 | │ Date         │ Models           │  Input │  Output │ Cost (USD) │
 45 | ├──────────────┼──────────────────┼────────┼─────────┼────────────┤
 46 | │ 2025-06-21   │ • sonnet-4       │  1,234 │  15,678 │    $12.34  │
 47 | │ 2025-06-20   │ • opus-4         │    890 │  12,345 │    $18.92  │
 48 | └──────────────┴──────────────────┴────────┴─────────┴────────────┘
 49 | ```
 50 | 
 51 | ## Understanding the Output
 52 | 
 53 | ### Columns Explained
 54 | 
 55 | - **Date**: The date when Claude Code was used
 56 | - **Models**: Which Claude models were used (Sonnet, Opus, etc.)
 57 | - **Input**: Number of input tokens sent to Claude
 58 | - **Output**: Number of output tokens received from Claude
 59 | - **Cost (USD)**: Estimated cost based on model pricing
 60 | 
 61 | ### Cache Tokens
 62 | 
 63 | If you have a wide terminal, you'll also see cache token columns:
 64 | 
 65 | - **Cache Create**: Tokens used to create cache entries
 66 | - **Cache Read**: Tokens read from cache (typically cheaper)
 67 | 
 68 | ## Next Steps
 69 | 
 70 | Now that you have your first report, explore these features:
 71 | 
 72 | 1. **[Monthly Reports](/guide/monthly-reports)** - See usage aggregated by month
 73 | 2. **[Session Reports](/guide/session-reports)** - Analyze individual conversations
 74 | 3. **[Live Monitoring](/guide/live-monitoring)** - Real-time usage tracking
 75 | 4. **[Configuration](/guide/configuration)** - Customize ccusage behavior
 76 | 
 77 | ## Common Use Cases
 78 | 
 79 | ### Monitor Daily Usage
 80 | 
 81 | ```bash
 82 | ccusage daily --since 20241201 --until 20241231
 83 | ```
 84 | 
 85 | ### Find Expensive Sessions
 86 | 
 87 | ```bash
 88 | ccusage session --order desc
 89 | ```
 90 | 
 91 | ### Export for Analysis
 92 | 
 93 | ```bash
 94 | ccusage monthly --json > usage-data.json
 95 | ```
 96 | 
 97 | ### Live Session Monitoring
 98 | 
 99 | ```bash
100 | ccusage blocks --live
101 | ```
102 | 
103 | ## Colors
104 | 
105 | ccusage automatically colors the output based on the terminal's capabilities. If you want to disable colors, you can use the `--no-color` flag. Or you can use the `--color` flag to force colors on.
106 | 
107 | ## Troubleshooting
108 | 
109 | ### No Data Found
110 | 
111 | If ccusage shows no data, check:
112 | 
113 | 1. **Claude Code is installed and used** - ccusage reads from Claude Code's data files
114 | 2. **Data directory exists** - Default locations:
115 |    - `~/.config/claude/projects/` (new default)
116 |    - `~/.claude/projects/` (legacy)
117 | 
118 | ### Custom Data Directory
119 | 
120 | If your Claude data is in a custom location:
121 | 
122 | ```bash
123 | export CLAUDE_CONFIG_DIR="/path/to/your/claude/data"
124 | ccusage daily
125 | ```
126 | 
127 | ## Getting Help
128 | 
129 | - Use `ccusage --help` for command options
130 | - Visit our [GitHub repository](https://github.com/ryoppippi/ccusage) for issues
131 | - Check the [API Reference](/api/) for programmatic usage
132 | 


--------------------------------------------------------------------------------
/docs/guide/index.md:
--------------------------------------------------------------------------------
  1 | # Introduction
  2 | 
  3 | ![ccusage daily report showing token usage and costs by date](/screenshot.png)
  4 | 
  5 | **ccusage** (claude-code-usage) is a powerful CLI tool that analyzes your Claude Code usage from local JSONL files to help you understand your token consumption patterns and estimated costs.
  6 | 
  7 | ## The Problem
  8 | 
  9 | Claude Code's Max plan offers unlimited usage, which is fantastic! But many users are curious:
 10 | 
 11 | - How much am I actually using Claude Code?
 12 | - Which conversations are the most expensive?
 13 | - What would I be paying on a pay-per-use plan?
 14 | - Am I getting good value from my subscription?
 15 | 
 16 | ## The Solution
 17 | 
 18 | ccusage analyzes the local JSONL files that Claude Code automatically generates and provides:
 19 | 
 20 | - **Detailed Usage Reports** - Daily, monthly, and session-based breakdowns
 21 | - **Cost Analysis** - Estimated costs based on token usage and model pricing
 22 | - **Live Monitoring** - Real-time tracking of active sessions
 23 | - **Multiple Formats** - Beautiful tables or JSON for further analysis
 24 | 
 25 | ## How It Works
 26 | 
 27 | ```mermaid
 28 | graph LR
 29 |     A[Claude Code] --> B[Local JSONL Files]
 30 |     B --> C[ccusage]
 31 |     C --> D[Usage Reports]
 32 |     C --> E[Cost Analysis]
 33 |     C --> F[Live Monitoring]
 34 | ```
 35 | 
 36 | 1. **Claude Code generates JSONL files** containing usage data
 37 | 2. **ccusage reads these files** from your local machine
 38 | 3. **Analyzes and aggregates** the data by date, session, or time blocks
 39 | 4. **Calculates estimated costs** using model pricing information
 40 | 5. **Presents results** in beautiful tables or JSON format
 41 | 
 42 | ## Key Features
 43 | 
 44 | ### 🚀 Ultra-Small Bundle Size
 45 | 
 46 | Unlike other CLI tools, we pay extreme attention to bundle size. ccusage achieves an incredibly small footprint even without minification, which means you can run it directly without installation using `bunx ccusage` for instant access.
 47 | 
 48 | ### 📊 Multiple Report Types
 49 | 
 50 | - **Daily Reports** - Usage aggregated by calendar date
 51 | - **Monthly Reports** - Monthly summaries with trends
 52 | - **Session Reports** - Per-conversation analysis
 53 | - **Blocks Reports** - 5-hour billing window tracking
 54 | 
 55 | ### 💰 Cost Analysis
 56 | 
 57 | - Estimated costs based on token counts and model pricing
 58 | - Support for different cost calculation modes
 59 | - Model-specific pricing (Opus vs Sonnet vs other models)
 60 | - Cache token cost calculation
 61 | 
 62 | ### 📈 Live Monitoring
 63 | 
 64 | - Real-time dashboard for active sessions
 65 | - Progress bars and burn rate calculations
 66 | - Token limit warnings and projections
 67 | - Automatic refresh with configurable intervals
 68 | 
 69 | ### 🔧 Flexible Configuration
 70 | 
 71 | - Multiple Claude data directory support
 72 | - Environment variable configuration
 73 | - Custom date filtering and sorting
 74 | - Offline mode with cached pricing data
 75 | 
 76 | ## Data Sources
 77 | 
 78 | ccusage reads from Claude Code's local data directories:
 79 | 
 80 | - **New location**: `~/.config/claude/projects/` (Claude Code v1.0.30+)
 81 | - **Legacy location**: `~/.claude/projects/` (pre-v1.0.30)
 82 | 
 83 | The tool automatically detects and aggregates data from both locations for compatibility.
 84 | 
 85 | ## Privacy & Security
 86 | 
 87 | - **100% Local** - All analysis happens on your machine
 88 | - **No Data Transmission** - Your usage data never leaves your computer
 89 | - **Read-Only** - ccusage only reads files, never modifies them
 90 | - **Open Source** - Full transparency in how your data is processed
 91 | 
 92 | ## Limitations
 93 | 
 94 | ::: warning Important Limitations
 95 | 
 96 | - **Local Files Only** - Only analyzes data from your current machine
 97 | - **Language Model Tokens** - API calls for tools like Web Search are not included
 98 | - **Estimate Accuracy** - Costs are estimates and may not reflect actual billing
 99 |   :::
100 | 
101 | ## Acknowledgments
102 | 
103 | Thanks to [@milliondev](https://note.com/milliondev) for the [original concept and approach](https://note.com/milliondev/n/n1d018da2d769) to Claude Code usage analysis.
104 | 
105 | ## Getting Started
106 | 
107 | Ready to analyze your Claude Code usage? Check out our [Getting Started Guide](/guide/getting-started) to begin exploring your data!
108 | 


--------------------------------------------------------------------------------
/docs/guide/installation.md:
--------------------------------------------------------------------------------
  1 | # Installation
  2 | 
  3 | ccusage can be installed and used in several ways depending on your preferences and use case.
  4 | 
  5 | ## Why No Installation Needed?
  6 | 
  7 | Thanks to ccusage's incredibly small bundle size, you don't need to install it globally. Unlike other CLI tools, we pay extreme attention to bundle size optimization, achieving an impressively small footprint even without minification. This means:
  8 | 
  9 | - ✅ Near-instant startup times
 10 | - ✅ Minimal download overhead
 11 | - ✅ Always use the latest version
 12 | - ✅ No global pollution of your system
 13 | 
 14 | ## Quick Start (Recommended)
 15 | 
 16 | The fastest way to use ccusage is to run it directly:
 17 | 
 18 | ::: code-group
 19 | 
 20 | ```bash [bunx (Recommended)]
 21 | bunx ccusage
 22 | ```
 23 | 
 24 | ```bash [npx]
 25 | npx ccusage@latest
 26 | ```
 27 | 
 28 | ```bash [pnpm]
 29 | pnpm dlx ccusage
 30 | ```
 31 | 
 32 | ```bash [deno]
 33 | deno run -E -R=$HOME/.claude/projects/ -S=homedir -N='raw.githubusercontent.com:443' npm:ccusage@latest
 34 | ```
 35 | 
 36 | :::
 37 | 
 38 | ::: tip Speed Recommendation
 39 | We strongly recommend using `bunx` instead of `npx` due to the massive speed difference. Bunx caches packages more efficiently, resulting in near-instant startup times after the first run.
 40 | :::
 41 | 
 42 | ::: info Deno Security
 43 | Consider using `deno run` if you want additional security controls. Deno allows you to specify exact permissions, making it safer to run tools you haven't audited.
 44 | :::
 45 | 
 46 | ### Performance Comparison
 47 | 
 48 | Here's why runtime choice matters:
 49 | 
 50 | | Runtime | First Run | Subsequent Runs | Notes |
 51 | |---------|-----------|-----------------|-------|
 52 | | bunx | Fast | **Instant** | Best overall choice |
 53 | | npx | Slow | Moderate | Widely available |
 54 | | pnpm dlx | Fast | Fast | Good alternative |
 55 | | deno | Moderate | Fast | Best for security |
 56 | 
 57 | ## Global Installation (Optional)
 58 | 
 59 | While not necessary due to our small bundle size, you can still install ccusage globally if you prefer:
 60 | 
 61 | ::: code-group
 62 | 
 63 | ```bash [npm]
 64 | npm install -g ccusage
 65 | ```
 66 | 
 67 | ```bash [bun]
 68 | bun install -g ccusage
 69 | ```
 70 | 
 71 | ```bash [yarn]
 72 | yarn global add ccusage
 73 | ```
 74 | 
 75 | ```bash [pnpm]
 76 | pnpm add -g ccusage
 77 | ```
 78 | 
 79 | :::
 80 | 
 81 | After global installation, run commands directly:
 82 | 
 83 | ```bash
 84 | ccusage daily
 85 | ccusage monthly --breakdown
 86 | ccusage blocks --live
 87 | ```
 88 | 
 89 | ## Development Installation
 90 | 
 91 | For development or contributing to ccusage:
 92 | 
 93 | ```bash
 94 | # Clone the repository
 95 | git clone https://github.com/ryoppippi/ccusage.git
 96 | cd ccusage
 97 | 
 98 | # Install dependencies
 99 | bun install
100 | 
101 | # Run directly from source
102 | bun run start daily
103 | bun run start monthly --json
104 | ```
105 | 
106 | ### Development Scripts
107 | 
108 | ```bash
109 | # Run tests
110 | bun run test
111 | 
112 | # Type checking
113 | bun typecheck
114 | 
115 | # Build distribution
116 | bun run build
117 | 
118 | # Lint and format
119 | bun run format
120 | ```
121 | 
122 | ## Runtime Requirements
123 | 
124 | ### Node.js
125 | 
126 | - **Minimum**: Node.js 20.x
127 | - **Recommended**: Node.js 20.x or later
128 | - **LTS versions** are fully supported
129 | 
130 | ### Bun (Alternative)
131 | 
132 | - **Minimum**: Bun 1.2+
133 | - **Recommended**: Latest stable release
134 | - Often faster than Node.js for ccusage
135 | 
136 | ### Deno
137 | 
138 | Deno 2.0+ is fully supported with proper permissions:
139 | 
140 | ```bash
141 | deno run \
142 |   -E \
143 |   -R=$HOME/.claude/projects/ \
144 |   -S=homedir \
145 |   -N='raw.githubusercontent.com:443' \
146 |   npm:ccusage@latest
147 | ```
148 | 
149 | Also you can use `offline` mode to run ccusage without network access:
150 | 
151 | ```bash
152 | deno run \
153 |   -E \
154 |   -R=$HOME/.claude/projects/ \
155 |   -S=homedir \
156 |   npm:ccusage@latest --offline
157 | ```
158 | 
159 | ## Verification
160 | 
161 | After installation, verify ccusage is working:
162 | 
163 | ```bash
164 | # Check version
165 | ccusage --version
166 | 
167 | # Run help command
168 | ccusage --help
169 | 
170 | # Test with daily report
171 | ccusage daily
172 | ```
173 | 
174 | ## Updating
175 | 
176 | ### Direct Execution (npx/bunx)
177 | 
178 | Always gets the latest version automatically.
179 | 
180 | ### Global Installation
181 | 
182 | ```bash
183 | # Update with npm
184 | npm update -g ccusage
185 | 
186 | # Update with bun
187 | bun update -g ccusage
188 | ```
189 | 
190 | ### Check Current Version
191 | 
192 | ```bash
193 | ccusage --version
194 | ```
195 | 
196 | ## Uninstalling
197 | 
198 | ### Global Installation
199 | 
200 | ::: code-group
201 | 
202 | ```bash [npm]
203 | npm uninstall -g ccusage
204 | ```
205 | 
206 | ```bash [bun]
207 | bun remove -g ccusage
208 | ```
209 | 
210 | ```bash [yarn]
211 | yarn global remove ccusage
212 | ```
213 | 
214 | ```bash [pnpm]
215 | pnpm remove -g ccusage
216 | ```
217 | 
218 | :::
219 | 
220 | ### Development Installation
221 | 
222 | ```bash
223 | # Remove cloned repository
224 | rm -rf ccusage/
225 | ```
226 | 
227 | ## Troubleshooting Installation
228 | 
229 | ### Permission Errors
230 | 
231 | If you get permission errors during global installation:
232 | 
233 | ::: code-group
234 | 
235 | ```bash [npm]
236 | # Use npx instead of global install
237 | npx ccusage@latest
238 | 
239 | # Or configure npm to use a different directory
240 | npm config set prefix ~/.npm-global
241 | export PATH=~/.npm-global/bin:$PATH
242 | ```
243 | 
244 | ```bash [Node Version Managers]
245 | # Use nvm (recommended)
246 | nvm install node
247 | npm install -g ccusage
248 | 
249 | # Or use fnm
250 | fnm install node
251 | npm install -g ccusage
252 | ```
253 | 
254 | :::
255 | 
256 | ### Network Issues
257 | 
258 | If installation fails due to network issues:
259 | 
260 | ```bash
261 | # Try with different registry
262 | npm install -g ccusage --registry https://registry.npmjs.org
263 | 
264 | # Or use bunx for offline-capable runs
265 | bunx ccusage
266 | ```
267 | 
268 | ### Version Conflicts
269 | 
270 | If you have multiple versions installed:
271 | 
272 | ```bash
273 | # Check which version is being used
274 | which ccusage
275 | ccusage --version
276 | 
277 | # Uninstall and reinstall
278 | npm uninstall -g ccusage
279 | npm install -g ccusage@latest
280 | ```
281 | 
282 | ## Next Steps
283 | 
284 | After installation, check out:
285 | 
286 | - [Getting Started Guide](/guide/getting-started) - Your first usage report
287 | - [Configuration](/guide/configuration) - Customize ccusage behavior
288 | - [Daily Reports](/guide/daily-reports) - Understand daily usage patterns
289 | 


--------------------------------------------------------------------------------
/docs/guide/json-output.md:
--------------------------------------------------------------------------------
  1 | # JSON Output
  2 | 
  3 | ccusage supports structured JSON output for all report types, making it easy to integrate with other tools, scripts, or applications that need to process usage data programmatically.
  4 | 
  5 | ## Enabling JSON Output
  6 | 
  7 | Add the `--json` (or `-j`) flag to any command:
  8 | 
  9 | ```bash
 10 | # Daily report in JSON format
 11 | ccusage daily --json
 12 | 
 13 | # Monthly report in JSON format
 14 | ccusage monthly --json
 15 | 
 16 | # Session report in JSON format
 17 | ccusage session --json
 18 | 
 19 | # 5-hour blocks report in JSON format
 20 | ccusage blocks --json
 21 | ```
 22 | 
 23 | ## JSON Structure
 24 | 
 25 | ### Daily Reports
 26 | 
 27 | ```json
 28 | {
 29 | 	"type": "daily",
 30 | 	"data": [
 31 | 		{
 32 | 			"date": "2025-05-30",
 33 | 			"models": ["claude-opus-4-20250514", "claude-sonnet-4-20250514"],
 34 | 			"inputTokens": 277,
 35 | 			"outputTokens": 31456,
 36 | 			"cacheCreationTokens": 512,
 37 | 			"cacheReadTokens": 1024,
 38 | 			"totalTokens": 33269,
 39 | 			"costUSD": 17.58
 40 | 		}
 41 | 	],
 42 | 	"summary": {
 43 | 		"totalInputTokens": 11174,
 44 | 		"totalOutputTokens": 720366,
 45 | 		"totalCacheCreationTokens": 896,
 46 | 		"totalCacheReadTokens": 2304,
 47 | 		"totalTokens": 734740,
 48 | 		"totalCostUSD": 336.47
 49 | 	}
 50 | }
 51 | ```
 52 | 
 53 | ### Monthly Reports
 54 | 
 55 | ```json
 56 | {
 57 | 	"type": "monthly",
 58 | 	"data": [
 59 | 		{
 60 | 			"month": "2025-05",
 61 | 			"models": ["claude-opus-4-20250514", "claude-sonnet-4-20250514"],
 62 | 			"inputTokens": 11174,
 63 | 			"outputTokens": 720366,
 64 | 			"cacheCreationTokens": 896,
 65 | 			"cacheReadTokens": 2304,
 66 | 			"totalTokens": 734740,
 67 | 			"costUSD": 336.47
 68 | 		}
 69 | 	],
 70 | 	"summary": {
 71 | 		"totalInputTokens": 11174,
 72 | 		"totalOutputTokens": 720366,
 73 | 		"totalCacheCreationTokens": 896,
 74 | 		"totalCacheReadTokens": 2304,
 75 | 		"totalTokens": 734740,
 76 | 		"totalCostUSD": 336.47
 77 | 	}
 78 | }
 79 | ```
 80 | 
 81 | ### Session Reports
 82 | 
 83 | ```json
 84 | {
 85 | 	"type": "session",
 86 | 	"data": [
 87 | 		{
 88 | 			"session": "session-1",
 89 | 			"models": ["claude-opus-4-20250514", "claude-sonnet-4-20250514"],
 90 | 			"inputTokens": 4512,
 91 | 			"outputTokens": 350846,
 92 | 			"cacheCreationTokens": 512,
 93 | 			"cacheReadTokens": 1024,
 94 | 			"totalTokens": 356894,
 95 | 			"costUSD": 156.40,
 96 | 			"lastActivity": "2025-05-24"
 97 | 		}
 98 | 	],
 99 | 	"summary": {
100 | 		"totalInputTokens": 11174,
101 | 		"totalOutputTokens": 720445,
102 | 		"totalCacheCreationTokens": 768,
103 | 		"totalCacheReadTokens": 1792,
104 | 		"totalTokens": 734179,
105 | 		"totalCostUSD": 336.68
106 | 	}
107 | }
108 | ```
109 | 
110 | ### Blocks Reports
111 | 
112 | ```json
113 | {
114 | 	"type": "blocks",
115 | 	"data": [
116 | 		{
117 | 			"blockStart": "2025-05-30T10:00:00.000Z",
118 | 			"blockEnd": "2025-05-30T15:00:00.000Z",
119 | 			"isActive": true,
120 | 			"timeRemaining": "2h 15m",
121 | 			"models": ["claude-sonnet-4-20250514"],
122 | 			"inputTokens": 1250,
123 | 			"outputTokens": 15000,
124 | 			"cacheCreationTokens": 256,
125 | 			"cacheReadTokens": 512,
126 | 			"totalTokens": 17018,
127 | 			"costUSD": 8.75,
128 | 			"burnRate": 2400,
129 | 			"projectedTotal": 25000,
130 | 			"projectedCost": 12.50
131 | 		}
132 | 	],
133 | 	"summary": {
134 | 		"totalInputTokens": 11174,
135 | 		"totalOutputTokens": 720366,
136 | 		"totalCacheCreationTokens": 896,
137 | 		"totalCacheReadTokens": 2304,
138 | 		"totalTokens": 734740,
139 | 		"totalCostUSD": 336.47
140 | 	}
141 | }
142 | ```
143 | 
144 | ## Field Descriptions
145 | 
146 | ### Common Fields
147 | 
148 | - `models`: Array of Claude model names used
149 | - `inputTokens`: Number of input tokens consumed
150 | - `outputTokens`: Number of output tokens generated
151 | - `cacheCreationTokens`: Tokens used for cache creation
152 | - `cacheReadTokens`: Tokens read from cache
153 | - `totalTokens`: Sum of all token types
154 | - `costUSD`: Estimated cost in US dollars
155 | 
156 | ### Report-Specific Fields
157 | 
158 | #### Daily Reports
159 | 
160 | - `date`: Date in YYYY-MM-DD format
161 | 
162 | #### Monthly Reports
163 | 
164 | - `month`: Month in YYYY-MM format
165 | 
166 | #### Session Reports
167 | 
168 | - `session`: Session identifier
169 | - `lastActivity`: Date of last activity in the session
170 | 
171 | #### Blocks Reports
172 | 
173 | - `blockStart`: ISO timestamp of block start
174 | - `blockEnd`: ISO timestamp of block end
175 | - `isActive`: Whether the block is currently active
176 | - `timeRemaining`: Human-readable time remaining (active blocks only)
177 | - `burnRate`: Tokens per hour rate (active blocks only)
178 | - `projectedTotal`: Projected total tokens for the block
179 | - `projectedCost`: Projected total cost for the block
180 | 
181 | ## Filtering with JSON Output
182 | 
183 | All filtering options work with JSON output:
184 | 
185 | ```bash
186 | # Filter by date range
187 | ccusage daily --json --since 20250525 --until 20250530
188 | 
189 | # Different cost calculation modes
190 | ccusage monthly --json --mode calculate
191 | ccusage session --json --mode display
192 | 
193 | # Sort order
194 | ccusage daily --json --order asc
195 | 
196 | # With model breakdown
197 | ccusage daily --json --breakdown
198 | ```
199 | 
200 | ### Model Breakdown JSON
201 | 
202 | When using `--breakdown`, the JSON includes per-model details:
203 | 
204 | ```json
205 | {
206 | 	"type": "daily",
207 | 	"data": [
208 | 		{
209 | 			"date": "2025-05-30",
210 | 			"models": ["claude-opus-4-20250514", "claude-sonnet-4-20250514"],
211 | 			"inputTokens": 277,
212 | 			"outputTokens": 31456,
213 | 			"totalTokens": 33269,
214 | 			"costUSD": 17.58,
215 | 			"breakdown": {
216 | 				"claude-opus-4-20250514": {
217 | 					"inputTokens": 100,
218 | 					"outputTokens": 15000,
219 | 					"cacheCreationTokens": 256,
220 | 					"cacheReadTokens": 512,
221 | 					"totalTokens": 15868,
222 | 					"costUSD": 10.25
223 | 				},
224 | 				"claude-sonnet-4-20250514": {
225 | 					"inputTokens": 177,
226 | 					"outputTokens": 16456,
227 | 					"cacheCreationTokens": 256,
228 | 					"cacheReadTokens": 512,
229 | 					"totalTokens": 17401,
230 | 					"costUSD": 7.33
231 | 				}
232 | 			}
233 | 		}
234 | 	]
235 | }
236 | ```
237 | 
238 | ## Integration Examples
239 | 
240 | ### Using with jq
241 | 
242 | Process JSON output with jq for advanced filtering and formatting:
243 | 
244 | ```bash
245 | # Get total cost for the last 7 days
246 | ccusage daily --json --since $(date -d '7 days ago' +%Y%m%d) | jq '.summary.totalCostUSD'
247 | 
248 | # List all unique models used
249 | ccusage session --json | jq -r '.data[].models[]' | sort -u
250 | 
251 | # Find the most expensive session
252 | ccusage session --json | jq -r '.data | sort_by(.costUSD) | reverse | .[0].session'
253 | 
254 | # Get daily costs as CSV
255 | ccusage daily --json | jq -r '.data[] | [.date, .costUSD] | @csv'
256 | ```
257 | 
258 | ### Using with Python
259 | 
260 | ```python
261 | import json
262 | import subprocess
263 | 
264 | # Get daily usage data
265 | result = subprocess.run(['ccusage', 'daily', '--json'], capture_output=True, text=True)
266 | data = json.loads(result.stdout)
267 | 
268 | # Process the data
269 | for day in data['data']:
270 |     print(f"Date: {day['date']}, Cost: ${day['costUSD']:.2f}")
271 | 
272 | total_cost = data['summary']['totalCostUSD']
273 | print(f"Total cost: ${total_cost:.2f}")
274 | ```
275 | 
276 | ### Using with Node.js
277 | 
278 | ```javascript
279 | import { execSync } from 'node:child_process';
280 | 
281 | // Get session usage data
282 | const output = execSync('ccusage session --json', { encoding: 'utf-8' });
283 | const data = JSON.parse(output);
284 | 
285 | // Find sessions over $10
286 | const expensiveSessions = data.data.filter(session => session.costUSD > 10);
287 | console.log(`Found ${expensiveSessions.length} expensive sessions`);
288 | 
289 | expensiveSessions.forEach((session) => {
290 | 	console.log(`${session.session}: ${session.costUSD.toFixed(2)}`);
291 | });
292 | ```
293 | 
294 | ## Programmatic Usage
295 | 
296 | JSON output is designed for programmatic consumption:
297 | 
298 | - **Consistent structure**: All fields are always present (with 0 or empty values when not applicable)
299 | - **Standard types**: Numbers for metrics, strings for identifiers, arrays for lists
300 | - **ISO timestamps**: Standardized date/time formats for reliable parsing
301 | - **Stable schema**: Field names and structures remain consistent across versions
302 | 


--------------------------------------------------------------------------------
/docs/guide/library-usage.md:
--------------------------------------------------------------------------------
  1 | # Library Usage
  2 | 
  3 | While **ccusage** is primarily known as a CLI tool, it can also be used as a library in your JavaScript/TypeScript projects. This allows you to integrate Claude Code usage analysis directly into your applications.
  4 | 
  5 | ## Installation
  6 | 
  7 | ```bash
  8 | npm install ccusage
  9 | # or
 10 | yarn add ccusage
 11 | # or
 12 | pnpm add ccusage
 13 | # or
 14 | bun add ccusage
 15 | ```
 16 | 
 17 | ## Basic Usage
 18 | 
 19 | The library provides functions to load and analyze Claude Code usage data:
 20 | 
 21 | ```typescript
 22 | import { loadDailyUsageData, loadMonthlyUsageData, loadSessionData } from 'ccusage/data-loader';
 23 | 
 24 | // Load daily usage data
 25 | const dailyData = await loadDailyUsageData();
 26 | console.log(dailyData);
 27 | 
 28 | // Load monthly usage data
 29 | const monthlyData = await loadMonthlyUsageData();
 30 | console.log(monthlyData);
 31 | 
 32 | // Load session data
 33 | const sessionData = await loadSessionData();
 34 | console.log(sessionData);
 35 | ```
 36 | 
 37 | ## Cost Calculation
 38 | 
 39 | Use the cost calculation utilities to work with token costs:
 40 | 
 41 | ```typescript
 42 | import { calculateTotals, getTotalTokens } from 'ccusage/calculate-cost';
 43 | 
 44 | // Assume 'usageEntries' is an array of usage data objects
 45 | const totals = calculateTotals(usageEntries);
 46 | 
 47 | // Get total tokens from the same entries
 48 | const totalTokens = getTotalTokens(usageEntries);
 49 | ```
 50 | 
 51 | ## Advanced Configuration
 52 | 
 53 | You can customize the data loading behavior:
 54 | 
 55 | ```typescript
 56 | import { loadDailyUsageData } from 'ccusage/data-loader';
 57 | 
 58 | // Load data with custom options
 59 | const data = await loadDailyUsageData({
 60 | 	mode: 'calculate', // Force cost calculation
 61 | 	claudePaths: ['/custom/path/to/claude'], // Custom Claude data paths
 62 | });
 63 | ```
 64 | 
 65 | ## TypeScript Support
 66 | 
 67 | The library is fully typed with TypeScript definitions:
 68 | 
 69 | ```typescript
 70 | import type { DailyUsage, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData } from 'ccusage/data-loader';
 71 | 
 72 | // Use the types in your application
 73 | function processUsageData(data: UsageData[]): void {
 74 | 	// Your processing logic here
 75 | }
 76 | ```
 77 | 
 78 | ## MCP Server Integration
 79 | 
 80 | You can also create your own MCP server using the library:
 81 | 
 82 | ```typescript
 83 | import { createMcpServer } from 'ccusage/mcp';
 84 | 
 85 | // Create an MCP server instance
 86 | const server = createMcpServer();
 87 | 
 88 | // Start the server
 89 | server.start();
 90 | ```
 91 | 
 92 | ## API Reference
 93 | 
 94 | For detailed information about all available functions, types, and options, see the [API Reference](/api/) section.
 95 | 
 96 | ## Examples
 97 | 
 98 | Here are some common use cases:
 99 | 
100 | ### Building a Web Dashboard
101 | 
102 | ```typescript
103 | import { loadDailyUsageData } from 'ccusage/data-loader';
104 | 
105 | export async function GET() {
106 | 	const data = await loadDailyUsageData();
107 | 	return Response.json(data);
108 | }
109 | ```
110 | 
111 | ### Creating Custom Reports
112 | 
113 | ```typescript
114 | import { calculateTotals, loadSessionData } from 'ccusage';
115 | 
116 | async function generateCustomReport() {
117 | 	const sessions = await loadSessionData();
118 | 
119 | 	const report = sessions.map(session => ({
120 | 		project: session.project,
121 | 		session: session.session,
122 | 		totalCost: calculateTotals(session.usage).costUSD,
123 | 	}));
124 | 
125 | 	return report;
126 | }
127 | ```
128 | 
129 | ### Monitoring Usage Programmatically
130 | 
131 | ```typescript
132 | import { loadDailyUsageData } from 'ccusage/data-loader';
133 | 
134 | async function checkUsageAlert() {
135 | 	const dailyData = await loadDailyUsageData();
136 | 	const today = dailyData[0]; // Most recent day
137 | 
138 | 	if (today.totalCostUSD > 10) {
139 | 		console.warn(`High usage detected: ${today.totalCostUSD}`);
140 | 	}
141 | }
142 | ```
143 | 
144 | ## Next Steps
145 | 
146 | - Explore the [API Reference](/api/) for complete documentation
147 | - Check out the [MCP Server guide](/guide/mcp-server) for integration examples
148 | - See [JSON Output](/guide/json-output) for data format details
149 | 


--------------------------------------------------------------------------------
/docs/guide/live-monitoring.md:
--------------------------------------------------------------------------------
  1 | # Live Monitoring
  2 | 
  3 | ![Live monitoring dashboard showing real-time token usage, burn rate, and cost projections](/blocks-live.png)
  4 | 
  5 | Live monitoring provides a real-time dashboard that updates as you use Claude Code, showing progress bars, burn rates, and cost projections for your active session.
  6 | 
  7 | ## Quick Start
  8 | 
  9 | ```bash
 10 | ccusage blocks --live
 11 | ```
 12 | 
 13 | This starts live monitoring with automatic token limit detection based on your usage history.
 14 | 
 15 | ## Features
 16 | 
 17 | ### Real-time Updates
 18 | 
 19 | The dashboard refreshes every second, showing:
 20 | 
 21 | - **Current session progress** with visual progress bar
 22 | - **Token burn rate** (tokens per minute)
 23 | - **Time remaining** in current 5-hour block
 24 | - **Cost projections** based on current usage patterns
 25 | - **Quota warnings** with color-coded alerts
 26 | 
 27 | ### Visual Example
 28 | 
 29 | ![Live monitoring dashboard showing real-time token usage, burn rate, and cost projections](/blocks-live.png)
 30 | 
 31 | ## Command Options
 32 | 
 33 | ### Token Limits
 34 | 
 35 | Set custom token limits for quota warnings:
 36 | 
 37 | ```bash
 38 | # Use specific token limit
 39 | ccusage blocks --live -t 500000
 40 | 
 41 | # Use highest previous session as limit (default)
 42 | ccusage blocks --live -t max
 43 | 
 44 | # Explicitly set max (same as default)
 45 | ccusage blocks --live -t max
 46 | ```
 47 | 
 48 | ### Refresh Interval
 49 | 
 50 | Control update frequency:
 51 | 
 52 | ```bash
 53 | # Update every 5 seconds
 54 | ccusage blocks --live --refresh-interval 5
 55 | 
 56 | # Update every 10 seconds (lighter on CPU)
 57 | ccusage blocks --live --refresh-interval 10
 58 | 
 59 | # Fast updates (every 0.5 seconds)
 60 | ccusage blocks --live --refresh-interval 0.5
 61 | ```
 62 | 
 63 | ::: tip Refresh Rate
 64 | 
 65 | - **1 second (default)**: Good balance of responsiveness and performance
 66 | - **0.5-2 seconds**: For active monitoring during heavy usage
 67 | - **5-10 seconds**: For casual monitoring or slower systems
 68 |   :::
 69 | 
 70 | ### Combined Options
 71 | 
 72 | ```bash
 73 | # Custom limit with slower refresh
 74 | ccusage blocks --live -t 750000 --refresh-interval 3
 75 | 
 76 | # Maximum responsiveness
 77 | ccusage blocks --live -t max --refresh-interval 0.5
 78 | ```
 79 | 
 80 | ## Understanding the Display
 81 | 
 82 | ### Progress Bar
 83 | 
 84 | The progress bar shows token usage within the current 5-hour block:
 85 | 
 86 | - **Green**: Normal usage (0-60% of limit)
 87 | - **Yellow**: Moderate usage (60-80% of limit)
 88 | - **Red**: High usage (80-100% of limit)
 89 | 
 90 | ### Metrics Explained
 91 | 
 92 | #### Current Session
 93 | 
 94 | - **Tokens used** in the current 5-hour block
 95 | - **Percentage** of token limit consumed
 96 | 
 97 | #### Time Remaining
 98 | 
 99 | - **Hours and minutes** left in current block
100 | - Resets every 5 hours from first message
101 | 
102 | #### Burn Rate
103 | 
104 | - **Tokens per minute** based on recent activity
105 | - Calculated from last 10 minutes of usage
106 | - Used for projections
107 | 
108 | #### Cost Tracking
109 | 
110 | - **Current Cost**: Actual cost so far in this block
111 | - **Projected Cost**: Estimated total cost if current rate continues
112 | 
113 | ### Warning System
114 | 
115 | ccusage shows color-coded warnings based on usage:
116 | 
117 | - 🟢 **< 60%**: Normal usage
118 | - 🟡 **60-80%**: Moderate usage warning
119 | - 🔴 **80-100%**: High usage warning
120 | - ⚠️ **> 100%**: Over limit warning
121 | 
122 | ## Use Cases
123 | 
124 | ### Active Development
125 | 
126 | Monitor usage during intensive coding sessions:
127 | 
128 | ```bash
129 | # Monitor with reasonable limit
130 | ccusage blocks --live -t 500000
131 | ```
132 | 
133 | Perfect for:
134 | 
135 | - Large refactoring projects
136 | - Documentation generation
137 | - Code review sessions
138 | 
139 | ### Team Collaboration
140 | 
141 | Track usage during pair programming:
142 | 
143 | ```bash
144 | # Higher limit for team sessions
145 | ccusage blocks --live -t 1000000
146 | ```
147 | 
148 | ### Budget Management
149 | 
150 | Set strict limits for cost control:
151 | 
152 | ```bash
153 | # Conservative monitoring
154 | ccusage blocks --live -t 200000
155 | ```
156 | 
157 | ### Learning Sessions
158 | 
159 | Monitor while learning new technologies:
160 | 
161 | ```bash
162 | # Moderate limit with frequent updates
163 | ccusage blocks --live -t 300000 --refresh-interval 2
164 | ```
165 | 
166 | ## Tips for Effective Monitoring
167 | 
168 | ### 1. Set Appropriate Limits
169 | 
170 | Choose token limits based on your needs:
171 | 
172 | - **Conservative (100k-300k)**: Light usage, cost-conscious
173 | - **Moderate (300k-700k)**: Regular development work
174 | - **High (700k-1M+)**: Intensive projects, team sessions
175 | 
176 | ### 2. Monitor Burn Rate
177 | 
178 | Watch for sudden increases in burn rate:
179 | 
180 | - **Steady rate**: Normal conversation flow
181 | - **Spikes**: Complex queries or large code generation
182 | - **High sustained rate**: Consider taking breaks
183 | 
184 | ### 3. Use Projections Wisely
185 | 
186 | Projections help estimate session costs:
187 | 
188 | - **Early session**: Projections may be inaccurate
189 | - **Mid-session**: More reliable estimates
190 | - **Late session**: Highly accurate projections
191 | 
192 | ### 4. Plan Around Blocks
193 | 
194 | Remember that 5-hour blocks reset:
195 | 
196 | - **Block boundary**: Good time for breaks
197 | - **New block**: Fresh token allowance
198 | - **Block overlap**: Previous usage doesn't carry over
199 | 
200 | ## Keyboard Controls
201 | 
202 | While live monitoring is active:
203 | 
204 | - **Ctrl+C**: Exit monitoring gracefully
205 | - **Terminal resize**: Automatically adjusts display
206 | 
207 | ## Performance Notes
208 | 
209 | ### CPU Usage
210 | 
211 | Live monitoring uses minimal resources:
212 | 
213 | - **1-second refresh**: ~0.1% CPU usage
214 | - **0.5-second refresh**: ~0.2% CPU usage
215 | - **File watching**: Efficient incremental updates
216 | 
217 | ### Network Usage
218 | 
219 | - **Offline mode**: Zero network usage
220 | - **Online mode**: Minimal API calls for pricing
221 | - **Local analysis**: All processing happens locally
222 | 
223 | ## Troubleshooting
224 | 
225 | ### No Active Session
226 | 
227 | If no active session is detected:
228 | 
229 | ```
230 | No active session found. Start using Claude Code to begin monitoring.
231 | ```
232 | 
233 | **Solutions**:
234 | 
235 | 1. Send a message in Claude Code
236 | 2. Wait a few seconds for data to be written
237 | 3. Check that Claude Code is running
238 | 
239 | ### Incorrect Token Limits
240 | 
241 | If automatic limit detection fails:
242 | 
243 | ```bash
244 | # Manually set a reasonable limit
245 | ccusage blocks --live -t 500000
246 | ```
247 | 
248 | ### Performance Issues
249 | 
250 | If monitoring feels slow:
251 | 
252 | ```bash
253 | # Reduce refresh frequency
254 | ccusage blocks --live --refresh-interval 5
255 | ```
256 | 
257 | ## Related Commands
258 | 
259 | - [Blocks Reports](/guide/blocks-reports) - Static 5-hour block analysis
260 | - [Session Reports](/guide/session-reports) - Historical session data
261 | - [Daily Reports](/guide/daily-reports) - Day-by-day usage patterns
262 | 
263 | ## Advanced Usage
264 | 
265 | Combine live monitoring with other tools:
266 | 
267 | ```bash
268 | # Monitor in background, export data periodically
269 | ccusage blocks --live &
270 | ccusage session --json > session-backup.json
271 | ```
272 | 


--------------------------------------------------------------------------------
/docs/guide/mcp-server.md:
--------------------------------------------------------------------------------
  1 | # MCP Server
  2 | 
  3 | ccusage includes a built-in Model Context Protocol (MCP) server that exposes usage data through standardized tools. This allows integration with other applications that support MCP.
  4 | 
  5 | ## Starting the MCP Server
  6 | 
  7 | ### stdio transport (default)
  8 | 
  9 | ```bash
 10 | ccusage mcp
 11 | # or explicitly (--type stdio is optional):
 12 | ccusage mcp --type stdio
 13 | ```
 14 | 
 15 | The stdio transport is ideal for local integration where the client directly spawns the process.
 16 | 
 17 | ### HTTP Stream Transport
 18 | 
 19 | ```bash
 20 | ccusage mcp --type http --port 8080
 21 | ```
 22 | 
 23 | The HTTP stream transport is best for remote access when you need to call the server from another machine or network location.
 24 | 
 25 | ### Cost Calculation Mode
 26 | 
 27 | You can control how costs are calculated:
 28 | 
 29 | ```bash
 30 | # Use pre-calculated costs when available, calculate from tokens otherwise (default)
 31 | ccusage mcp --mode auto
 32 | 
 33 | # Always calculate costs from tokens using model pricing
 34 | ccusage mcp --mode calculate
 35 | 
 36 | # Always use pre-calculated costUSD values only
 37 | ccusage mcp --mode display
 38 | ```
 39 | 
 40 | ## Available MCP Tools
 41 | 
 42 | The MCP server provides four main tools for analyzing Claude Code usage:
 43 | 
 44 | ### daily
 45 | 
 46 | Returns daily usage reports with aggregated token usage and costs by date.
 47 | 
 48 | **Parameters:**
 49 | 
 50 | - `since` (optional): Filter from date (YYYYMMDD format)
 51 | - `until` (optional): Filter until date (YYYYMMDD format)
 52 | - `mode` (optional): Cost calculation mode (`auto`, `calculate`, or `display`)
 53 | 
 54 | ### monthly
 55 | 
 56 | Returns monthly usage reports with aggregated token usage and costs by month.
 57 | 
 58 | **Parameters:**
 59 | 
 60 | - `since` (optional): Filter from date (YYYYMMDD format)
 61 | - `until` (optional): Filter until date (YYYYMMDD format)
 62 | - `mode` (optional): Cost calculation mode (`auto`, `calculate`, or `display`)
 63 | 
 64 | ### session
 65 | 
 66 | Returns session-based usage reports grouped by conversation sessions.
 67 | 
 68 | **Parameters:**
 69 | 
 70 | - `since` (optional): Filter from date (YYYYMMDD format)
 71 | - `until` (optional): Filter until date (YYYYMMDD format)
 72 | - `mode` (optional): Cost calculation mode (`auto`, `calculate`, or `display`)
 73 | 
 74 | ### blocks
 75 | 
 76 | Returns 5-hour billing blocks usage reports showing usage within Claude's billing windows.
 77 | 
 78 | **Parameters:**
 79 | 
 80 | - `since` (optional): Filter from date (YYYYMMDD format)
 81 | - `until` (optional): Filter until date (YYYYMMDD format)
 82 | - `mode` (optional): Cost calculation mode (`auto`, `calculate`, or `display`)
 83 | 
 84 | ## Testing the MCP Server
 85 | 
 86 | ### Interactive Testing with MCP Inspector
 87 | 
 88 | You can test the MCP server using the MCP Inspector for interactive debugging:
 89 | 
 90 | ```bash
 91 | # Test with web UI (if you have the dev environment set up)
 92 | bun run mcp
 93 | 
 94 | # Test with the official MCP Inspector
 95 | bunx @modelcontextprotocol/inspector bunx ccusage mcp
 96 | ```
 97 | 
 98 | The MCP Inspector provides a web-based interface to:
 99 | 
100 | - Test individual MCP tools (daily, monthly, session, blocks)
101 | - Inspect tool schemas and parameters
102 | - Debug server responses
103 | - Export server configurations
104 | 
105 | ### Manual Testing
106 | 
107 | You can also manually test the server by running it and sending JSON-RPC messages:
108 | 
109 | ```bash
110 | # Start the server
111 | ccusage mcp
112 | 
113 | # The server will wait for JSON-RPC messages on stdin
114 | # Example: List available tools
115 | {"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
116 | ```
117 | 
118 | ## Integration Examples
119 | 
120 | ### With Claude Desktop
121 | 
122 | ![Claude Desktop MCP Configuration](/mcp-claude-desktop.avif)
123 | 
124 | To use ccusage MCP with Claude Desktop, add this to your Claude Desktop configuration file:
125 | 
126 | **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
127 | **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
128 | 
129 | #### Using npx (Recommended)
130 | 
131 | ```json
132 | {
133 | 	"mcpServers": {
134 | 		"ccusage": {
135 | 			"command": "npx",
136 | 			"args": ["ccusage@latest", "mcp"],
137 | 			"env": {}
138 | 		}
139 | 	}
140 | }
141 | ```
142 | 
143 | #### Using Global Installation
144 | 
145 | If you have ccusage installed globally:
146 | 
147 | ```json
148 | {
149 | 	"mcpServers": {
150 | 		"ccusage": {
151 | 			"command": "ccusage",
152 | 			"args": ["mcp"],
153 | 			"env": {}
154 | 		}
155 | 	}
156 | }
157 | ```
158 | 
159 | #### Custom Configuration
160 | 
161 | You can specify custom Claude data directories and cost calculation modes:
162 | 
163 | ```json
164 | {
165 | 	"mcpServers": {
166 | 		"ccusage": {
167 | 			"command": "npx",
168 | 			"args": ["ccusage@latest", "mcp", "--mode", "calculate"],
169 | 			"env": {
170 | 				"CLAUDE_CONFIG_DIR": "/path/to/your/claude/data"
171 | 			}
172 | 		}
173 | 	}
174 | }
175 | ```
176 | 
177 | After adding this configuration, restart Claude Desktop. You'll then be able to use the ccusage tools within Claude to analyze your usage data.
178 | 
179 | #### Available Commands in Claude Desktop
180 | 
181 | Once configured, you can ask Claude to:
182 | 
183 | - "Show me my Claude Code usage for today"
184 | - "Generate a monthly usage report"
185 | - "Which sessions used the most tokens?"
186 | - "Show me my current billing block usage"
187 | - "Analyze my 5-hour block patterns"
188 | 
189 | #### Troubleshooting Claude Desktop Integration
190 | 
191 | **Configuration Not Working:**
192 | 
193 | 1. Verify the config file is in the correct location for your OS
194 | 2. Check JSON syntax with a validator
195 | 3. Restart Claude Desktop completely
196 | 4. Ensure ccusage is installed and accessible
197 | 
198 | **Common Issues:**
199 | 
200 | - "Command not found": Install ccusage globally or use the npx configuration
201 | - "No usage data found": Verify your Claude Code data directory exists
202 | - Performance issues: Consider using `--mode display` or `--offline` flag
203 | 
204 | ### With Other MCP Clients
205 | 
206 | Any application that supports the Model Context Protocol can integrate with ccusage's MCP server. The server follows the MCP specification for tool discovery and execution.
207 | 
208 | ## Environment Variables
209 | 
210 | The MCP server respects the same environment variables as the CLI:
211 | 
212 | - `CLAUDE_CONFIG_DIR`: Specify custom Claude data directory paths
213 |   ```bash
214 |   export CLAUDE_CONFIG_DIR="/path/to/claude"
215 |   ccusage mcp
216 |   ```
217 | 
218 | ## Error Handling
219 | 
220 | The MCP server handles errors gracefully:
221 | 
222 | - Invalid date formats in parameters return descriptive error messages
223 | - Missing Claude data directories are handled with appropriate warnings
224 | - Malformed JSONL files are skipped during data loading
225 | - Network errors (when fetching pricing data) fall back to cached data when using `auto` mode
226 | 


--------------------------------------------------------------------------------
/docs/guide/monthly-reports.md:
--------------------------------------------------------------------------------
  1 | # Monthly Reports
  2 | 
  3 | Monthly reports aggregate your Claude Code usage by calendar month, providing a high-level view of your usage patterns and costs over longer time periods.
  4 | 
  5 | ## Basic Usage
  6 | 
  7 | ```bash
  8 | ccusage monthly
  9 | ```
 10 | 
 11 | ## Example Output
 12 | 
 13 | ```
 14 | ╭─────────────────────────────────────────────╮
 15 | │                                             │
 16 | │  Claude Code Token Usage Report - Monthly  │
 17 | │                                             │
 18 | ╰─────────────────────────────────────────────╯
 19 | 
 20 | ┌─────────┬──────────────────┬─────────┬──────────┬──────────────┬────────────┬──────────────┬────────────┐
 21 | │ Month   │ Models           │ Input   │ Output   │ Cache Create │ Cache Read │ Total Tokens │ Cost (USD) │
 22 | ├─────────┼──────────────────┼─────────┼──────────┼──────────────┼────────────┼──────────────┼────────────┤
 23 | │ 2025-06 │ • opus-4         │  45,231 │  892,456 │        2,048 │      4,096 │      943,831 │   $1,247.92│
 24 | │         │ • sonnet-4       │         │          │              │            │              │            │
 25 | │ 2025-05 │ • sonnet-4       │  38,917 │  756,234 │        1,536 │      3,072 │      799,759 │     $892.15│
 26 | │ 2025-04 │ • opus-4         │  22,458 │  534,789 │        1,024 │      2,048 │      560,319 │     $678.43│
 27 | ├─────────┼──────────────────┼─────────┼──────────┼──────────────┼────────────┼──────────────┼────────────┤
 28 | │ Total   │                  │ 106,606 │2,183,479 │        4,608 │      9,216 │    2,303,909 │   $2,818.50│
 29 | └─────────┴──────────────────┴─────────┴──────────┴──────────────┴────────────┴──────────────┴────────────┘
 30 | ```
 31 | 
 32 | ## Understanding Monthly Data
 33 | 
 34 | ### Month Format
 35 | 
 36 | Months are displayed in YYYY-MM format:
 37 | 
 38 | - `2025-06` = June 2025
 39 | - `2025-05` = May 2025
 40 | 
 41 | ### Aggregation Logic
 42 | 
 43 | All usage within a calendar month is aggregated:
 44 | 
 45 | - Input/output tokens summed across all days
 46 | - Costs calculated from total token usage
 47 | - Models listed if used at any point in the month
 48 | 
 49 | ## Command Options
 50 | 
 51 | ### Date Filtering
 52 | 
 53 | Filter by month range:
 54 | 
 55 | ```bash
 56 | # Show specific months
 57 | ccusage monthly --since 20250101 --until 20250630
 58 | 
 59 | # Show usage from 2024
 60 | ccusage monthly --since 20240101 --until 20241231
 61 | 
 62 | # Show last 6 months
 63 | ccusage monthly --since $(date -d '6 months ago' +%Y%m%d)
 64 | ```
 65 | 
 66 | ::: tip Date Filtering
 67 | Even though you specify full dates (YYYYMMDD), monthly reports group by month. The filters determine which months to include.
 68 | :::
 69 | 
 70 | ### Sort Order
 71 | 
 72 | ```bash
 73 | # Newest months first (default)
 74 | ccusage monthly --order desc
 75 | 
 76 | # Oldest months first
 77 | ccusage monthly --order asc
 78 | ```
 79 | 
 80 | ### Cost Calculation Modes
 81 | 
 82 | ```bash
 83 | # Use pre-calculated costs when available (default)
 84 | ccusage monthly --mode auto
 85 | 
 86 | # Always calculate costs from tokens
 87 | ccusage monthly --mode calculate
 88 | 
 89 | # Only show pre-calculated costs
 90 | ccusage monthly --mode display
 91 | ```
 92 | 
 93 | ### Model Breakdown
 94 | 
 95 | See costs broken down by model:
 96 | 
 97 | ```bash
 98 | ccusage monthly --breakdown
 99 | ```
100 | 
101 | Example with breakdown:
102 | 
103 | ```
104 | ┌─────────┬──────────────────┬─────────┬──────────┬────────────┐
105 | │ Month   │ Models           │ Input   │ Output   │ Cost (USD) │
106 | ├─────────┼──────────────────┼─────────┼──────────┼────────────┤
107 | │ 2025-06 │ opus-4, sonnet-4 │  45,231 │  892,456 │  $1,247.92 │
108 | ├─────────┼──────────────────┼─────────┼──────────┼────────────┤
109 | │  └─ opus-4                 │  20,000 │  400,000 │    $750.50 │
110 | ├─────────┼──────────────────┼─────────┼──────────┼────────────┤
111 | │  └─ sonnet-4               │  25,231 │  492,456 │    $497.42 │
112 | └─────────┴──────────────────┴─────────┴──────────┴────────────┘
113 | ```
114 | 
115 | ### JSON Output
116 | 
117 | ```bash
118 | ccusage monthly --json
119 | ```
120 | 
121 | ```json
122 | [
123 | 	{
124 | 		"month": "2025-06",
125 | 		"models": ["opus-4", "sonnet-4"],
126 | 		"inputTokens": 45231,
127 | 		"outputTokens": 892456,
128 | 		"cacheCreationTokens": 2048,
129 | 		"cacheReadTokens": 4096,
130 | 		"totalTokens": 943831,
131 | 		"totalCost": 1247.92
132 | 	}
133 | ]
134 | ```
135 | 
136 | ### Offline Mode
137 | 
138 | ```bash
139 | ccusage monthly --offline
140 | ```
141 | 
142 | ## Analysis Use Cases
143 | 
144 | ### Budget Planning
145 | 
146 | Monthly reports help with subscription planning:
147 | 
148 | ```bash
149 | # Check last year's usage
150 | ccusage monthly --since 20240101 --until 20241231
151 | ```
152 | 
153 | Look at the total cost to understand what you'd pay on usage-based pricing.
154 | 
155 | ### Usage Trends
156 | 
157 | Track how your usage changes over time:
158 | 
159 | ```bash
160 | # Compare year over year
161 | ccusage monthly --since 20230101 --until 20231231  # 2023
162 | ccusage monthly --since 20240101 --until 20241231  # 2024
163 | ```
164 | 
165 | ### Model Migration Analysis
166 | 
167 | See how your model usage evolves:
168 | 
169 | ```bash
170 | ccusage monthly --breakdown
171 | ```
172 | 
173 | This helps track transitions between Opus, Sonnet, and other models.
174 | 
175 | ### Seasonal Patterns
176 | 
177 | Identify busy/slow periods:
178 | 
179 | ```bash
180 | # Academic year analysis
181 | ccusage monthly --since 20240901 --until 20250630
182 | ```
183 | 
184 | ### Export for Business Analysis
185 | 
186 | ```bash
187 | # Create quarterly reports
188 | ccusage monthly --since 20241001 --until 20241231 --json > q4-2024.json
189 | ```
190 | 
191 | ## Tips for Monthly Analysis
192 | 
193 | ### 1. Cost Context
194 | 
195 | Monthly totals show:
196 | 
197 | - **Subscription Value**: How much you'd pay with usage-based billing
198 | - **Usage Intensity**: Months with heavy Claude usage
199 | - **Model Preferences**: Which models you favor over time
200 | 
201 | ### 2. Trend Analysis
202 | 
203 | Look for patterns:
204 | 
205 | - Increasing usage over time
206 | - Seasonal variations
207 | - Model adoption curves
208 | 
209 | ### 3. Business Planning
210 | 
211 | Use monthly data for:
212 | 
213 | - Team budget planning
214 | - Usage forecasting
215 | - Subscription optimization
216 | 
217 | ### 4. Comparative Analysis
218 | 
219 | Compare monthly reports with:
220 | 
221 | - Team productivity metrics
222 | - Project timelines
223 | - Business outcomes
224 | 
225 | ## Related Commands
226 | 
227 | - [Daily Reports](/guide/daily-reports) - Day-by-day breakdown
228 | - [Session Reports](/guide/session-reports) - Individual conversations
229 | - [Blocks Reports](/guide/blocks-reports) - 5-hour billing periods
230 | 
231 | ## Next Steps
232 | 
233 | After analyzing monthly trends, consider:
234 | 
235 | 1. [Session Reports](/guide/session-reports) to identify high-cost conversations
236 | 2. [Live Monitoring](/guide/live-monitoring) to track real-time usage
237 | 3. [Library Usage](/guide/library-usage) for programmatic analysis
238 | 


--------------------------------------------------------------------------------
/docs/guide/related-projects.md:
--------------------------------------------------------------------------------
 1 | # Related Projects
 2 | 
 3 | Projects that use ccusage internally or extend its functionality:
 4 | 
 5 | ## Desktop Applications
 6 | 
 7 | - [claude-usage-tracker-for-mac](https://github.com/penicillin0/claude-usage-tracker-for-mac) - macOS menu bar app for tracking Claude usage
 8 | - [ClaudeCode_Dashboard](https://github.com/m-sigepon/ClaudeCode_Dashboard) - Web dashboard with charts and visualizations
 9 | - [Ccusage App](https://github.com/EthanBarlo/ccusage-app) - Native application to display ccusage data in graphs and visualizations
10 | - [CCOwl](https://github.com/sivchari/ccowl) - A cross-platform status bar application that monitors Claude Code usage in real-time.
11 | 
12 | ## Extensions & Integrations
13 | 
14 | - [ccusage Raycast Extension](https://www.raycast.com/nyatinte/ccusage) - Raycast integration for quick usage checks
15 | 
16 | ## Contributing
17 | 
18 | If you've built something that uses ccusage, please feel free to open a pull request to add it to this list!
19 | 


--------------------------------------------------------------------------------
/docs/guide/session-reports.md:
--------------------------------------------------------------------------------
  1 | # Session Reports
  2 | 
  3 | Session reports show your Claude Code usage grouped by individual conversation sessions, making it easy to identify which conversations consumed the most tokens and cost the most.
  4 | 
  5 | ## Basic Usage
  6 | 
  7 | ```bash
  8 | ccusage session
  9 | ```
 10 | 
 11 | ## Example Output
 12 | 
 13 | ```
 14 | ╭───────────────────────────────────────────────╮
 15 | │                                               │
 16 | │  Claude Code Token Usage Report - By Session  │
 17 | │                                               │
 18 | ╰───────────────────────────────────────────────╯
 19 | 
 20 | ┌────────────┬──────────────────┬────────┬─────────┬──────────────┬────────────┬──────────────┬────────────┬───────────────┐
 21 | │ Session    │ Models           │ Input  │ Output  │ Cache Create │ Cache Read │ Total Tokens │ Cost (USD) │ Last Activity │
 22 | ├────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┼───────────────┤
 23 | │ abc123-def │ • opus-4         │  4,512 │ 350,846 │          512 │      1,024 │      356,894 │    $156.40 │ 2025-06-21    │
 24 | │            │ • sonnet-4       │        │         │              │            │              │            │               │
 25 | │ ghi456-jkl │ • sonnet-4       │  2,775 │ 186,645 │          256 │        768 │      190,444 │     $98.45 │ 2025-06-20    │
 26 | │ mno789-pqr │ • opus-4         │  1,887 │ 183,055 │          128 │        512 │      185,582 │     $81.73 │ 2025-06-19    │
 27 | ├────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┼───────────────┤
 28 | │ Total      │                  │  9,174 │ 720,546 │          896 │      2,304 │      732,920 │    $336.58 │               │
 29 | └────────────┴──────────────────┴────────┴─────────┴──────────────┴────────────┴──────────────┴────────────┴───────────────┘
 30 | ```
 31 | 
 32 | ## Understanding Session Data
 33 | 
 34 | ### Session Identification
 35 | 
 36 | Sessions are displayed using the last two segments of their full identifier:
 37 | 
 38 | - Full session ID: `project-20250621-session-abc123-def456`
 39 | - Displayed as: `abc123-def`
 40 | 
 41 | ### Session Metrics
 42 | 
 43 | - **Input/Output Tokens**: Total tokens exchanged in the conversation
 44 | - **Cache Tokens**: Cache creation and read tokens for context efficiency
 45 | - **Cost**: Estimated USD cost for the entire conversation
 46 | - **Last Activity**: Date of the most recent message in the session
 47 | 
 48 | ### Sorting
 49 | 
 50 | Sessions are sorted by cost (highest first) by default, making it easy to identify your most expensive conversations.
 51 | 
 52 | ## Command Options
 53 | 
 54 | ### Date Filtering
 55 | 
 56 | Filter sessions by their last activity date:
 57 | 
 58 | ```bash
 59 | # Show sessions active since May 25th
 60 | ccusage session --since 20250525
 61 | 
 62 | # Show sessions active in a specific date range
 63 | ccusage session --since 20250520 --until 20250530
 64 | 
 65 | # Show only recent sessions (last week)
 66 | ccusage session --since $(date -d '7 days ago' +%Y%m%d)
 67 | ```
 68 | 
 69 | ### Sort Order
 70 | 
 71 | ```bash
 72 | # Show most expensive sessions first (default)
 73 | ccusage session --order desc
 74 | 
 75 | # Show least expensive sessions first
 76 | ccusage session --order asc
 77 | ```
 78 | 
 79 | ### Cost Calculation Modes
 80 | 
 81 | ```bash
 82 | # Use pre-calculated costs when available (default)
 83 | ccusage session --mode auto
 84 | 
 85 | # Always calculate costs from tokens
 86 | ccusage session --mode calculate
 87 | 
 88 | # Only show pre-calculated costs
 89 | ccusage session --mode display
 90 | ```
 91 | 
 92 | ### Model Breakdown
 93 | 
 94 | See per-model cost breakdown within each session:
 95 | 
 96 | ```bash
 97 | ccusage session --breakdown
 98 | ```
 99 | 
100 | Example with breakdown:
101 | 
102 | ```
103 | ┌────────────┬──────────────────┬────────┬─────────┬────────────┬───────────────┐
104 | │ Session    │ Models           │ Input  │ Output  │ Cost (USD) │ Last Activity │
105 | ├────────────┼──────────────────┼────────┼─────────┼────────────┼───────────────┤
106 | │ abc123-def │ opus-4, sonnet-4 │  4,512 │ 350,846 │    $156.40 │ 2025-06-21    │
107 | ├────────────┼──────────────────┼────────┼─────────┼────────────┼───────────────┤
108 | │   └─ opus-4│                  │  2,000 │ 200,000 │     $95.50 │               │
109 | ├────────────┼──────────────────┼────────┼─────────┼────────────┼───────────────┤
110 | │   └─ sonnet-4                 │  2,512 │ 150,846 │     $60.90 │               │
111 | └────────────┴──────────────────┴────────┴─────────┴────────────┴───────────────┘
112 | ```
113 | 
114 | ### JSON Output
115 | 
116 | Export session data as JSON for further analysis:
117 | 
118 | ```bash
119 | ccusage session --json
120 | ```
121 | 
122 | ```json
123 | {
124 | 	"sessions": [
125 | 		{
126 | 			"sessionId": "abc123-def",
127 | 			"inputTokens": 4512,
128 | 			"outputTokens": 350846,
129 | 			"cacheCreationTokens": 512,
130 | 			"cacheReadTokens": 1024,
131 | 			"totalTokens": 356894,
132 | 			"totalCost": 156.40,
133 | 			"lastActivity": "2025-06-21",
134 | 			"modelsUsed": ["opus-4", "sonnet-4"],
135 | 			"modelBreakdowns": [
136 | 				{
137 | 					"model": "opus-4",
138 | 					"inputTokens": 2000,
139 | 					"outputTokens": 200000,
140 | 					"totalCost": 95.50
141 | 				}
142 | 			]
143 | 		}
144 | 	],
145 | 	"totals": {
146 | 		"inputTokens": 9174,
147 | 		"outputTokens": 720546,
148 | 		"totalCost": 336.58
149 | 	}
150 | }
151 | ```
152 | 
153 | ### Offline Mode
154 | 
155 | Use cached pricing data without network access:
156 | 
157 | ```bash
158 | ccusage session --offline
159 | # or short form:
160 | ccusage session -O
161 | ```
162 | 
163 | ## Analysis Use Cases
164 | 
165 | ### Identify Expensive Conversations
166 | 
167 | Session reports help you understand which conversations are most costly:
168 | 
169 | ```bash
170 | # Find your most expensive sessions
171 | ccusage session --order desc
172 | ```
173 | 
174 | Look at the top sessions to understand:
175 | 
176 | - Which types of conversations cost the most
177 | - Whether long coding sessions or research tasks are more expensive
178 | - How model choice (Opus vs Sonnet) affects costs
179 | 
180 | ### Track Conversation Patterns
181 | 
182 | ```bash
183 | # See recent conversation activity
184 | ccusage session --since 20250615
185 | 
186 | # Compare different time periods
187 | ccusage session --since 20250601 --until 20250615  # First half of month
188 | ccusage session --since 20250616 --until 20250630  # Second half of month
189 | ```
190 | 
191 | ### Model Usage Analysis
192 | 
193 | ```bash
194 | # See which models you use in different conversations
195 | ccusage session --breakdown
196 | ```
197 | 
198 | This helps understand:
199 | 
200 | - Whether you prefer Opus for complex tasks
201 | - If Sonnet is sufficient for routine work
202 | - How model mixing affects total costs
203 | 
204 | ### Budget Optimization
205 | 
206 | ```bash
207 | # Export data for spreadsheet analysis
208 | ccusage session --json > sessions.json
209 | 
210 | # Find sessions above a certain cost threshold
211 | ccusage session --json | jq '.sessions[] | select(.totalCost > 50)'
212 | ```
213 | 
214 | ## Tips for Session Analysis
215 | 
216 | ### 1. Cost Context Understanding
217 | 
218 | Session costs help you understand:
219 | 
220 | - **Conversation Value**: High-cost sessions should provide proportional value
221 | - **Efficiency Patterns**: Some conversation styles may be more token-efficient
222 | - **Model Selection**: Whether your model choices align with task complexity
223 | 
224 | ### 2. Usage Optimization
225 | 
226 | Use session data to:
227 | 
228 | - **Identify expensive patterns**: What makes some conversations cost more?
229 | - **Optimize conversation flow**: Break long sessions into smaller focused chats
230 | - **Choose appropriate models**: Use Sonnet for simpler tasks, Opus for complex ones
231 | 
232 | ### 3. Budget Planning
233 | 
234 | Session analysis helps with:
235 | 
236 | - **Conversation budgeting**: Understanding typical session costs
237 | - **Usage forecasting**: Predicting monthly costs based on session patterns
238 | - **Value assessment**: Ensuring expensive sessions provide good value
239 | 
240 | ### 4. Comparative Analysis
241 | 
242 | Compare sessions to understand:
243 | 
244 | - **Task types**: Coding vs writing vs research costs
245 | - **Model effectiveness**: Whether Opus provides value over Sonnet
246 | - **Time patterns**: Whether longer sessions are more or less efficient
247 | 
248 | ## Responsive Display
249 | 
250 | Session reports adapt to your terminal width:
251 | 
252 | - **Wide terminals (≥100 chars)**: Shows all columns including cache metrics
253 | - **Narrow terminals (<100 chars)**: Compact mode with essential columns (Session, Models, Input, Output, Cost, Last Activity)
254 | 
255 | When in compact mode, ccusage displays a message explaining how to see the full data.
256 | 
257 | ## Related Commands
258 | 
259 | - [Daily Reports](/guide/daily-reports) - Usage aggregated by date
260 | - [Monthly Reports](/guide/monthly-reports) - Monthly summaries
261 | - [Blocks Reports](/guide/blocks-reports) - 5-hour billing windows
262 | - [Live Monitoring](/guide/live-monitoring) - Real-time session tracking
263 | 
264 | ## Next Steps
265 | 
266 | After analyzing session patterns, consider:
267 | 
268 | 1. [Blocks Reports](/guide/blocks-reports) to understand timing within 5-hour windows
269 | 2. [Live Monitoring](/guide/live-monitoring) to track active conversations in real-time
270 | 3. [Daily Reports](/guide/daily-reports) to see how session patterns vary by day
271 | 


--------------------------------------------------------------------------------
/docs/guide/sponsors.md:
--------------------------------------------------------------------------------
 1 | # Sponsors
 2 | 
 3 | Support ccusage development by becoming a sponsor! Your contribution helps maintain and improve this tool.
 4 | 
 5 | ## Featured Sponsor
 6 | 
 7 | Check out these [47 Claude Code ProTips from Greg Baugues.](https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg)
 8 | 
 9 | <p align="center">
10 |     <a href="https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg">
11 |         <img src="/claude_code_protips_thumbnail_v1.png" alt="47 Claude Code ProTips from Greg Baugues" width="600">
12 |     </a>
13 | </p>
14 | 
15 | <p align="center">
16 |     <a href="https://github.com/sponsors/ryoppippi">
17 |         <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">
18 |     </a>
19 | </p>
20 | 
21 | ## How to Sponsor
22 | 
23 | Visit [GitHub Sponsors - @ryoppippi](https://github.com/sponsors/ryoppippi) to support the development of ccusage and other open source projects.
24 | 
25 | ## Star History
26 | 
27 | <a href="https://www.star-history.com/#ryoppippi/ccusage&Date">
28 |     <picture>
29 |         <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date&theme=dark" />
30 |         <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
31 |         <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
32 |     </picture>
33 | </a>
34 | 


--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: home
 3 | 
 4 | hero:
 5 |   name: ccusage
 6 |   text: Claude Code Usage Analysis
 7 |   tagline: A powerful CLI tool for analyzing Claude Code usage from local JSONL files
 8 |   image:
 9 |     src: /logo.svg
10 |     alt: ccusage logo
11 |   actions:
12 |     - theme: brand
13 |       text: Get Started
14 |       link: /guide/
15 |     - theme: alt
16 |       text: View on GitHub
17 |       link: https://github.com/ryoppippi/ccusage
18 | 
19 | features:
20 |   - icon: 📊
21 |     title: Daily Reports
22 |     details: View token usage and costs aggregated by date with detailed breakdowns
23 |     link: /guide/daily-reports
24 |   - icon: 📅
25 |     title: Monthly Reports
26 |     details: Analyze usage patterns over monthly periods with cost tracking
27 |   - icon: 💬
28 |     title: Session Reports
29 |     details: Group usage by conversation sessions for detailed analysis
30 |   - icon: ⏰
31 |     title: 5-Hour Blocks
32 |     details: Track usage within Claude's billing windows with active monitoring
33 |   - icon: 📈
34 |     title: Live Monitoring
35 |     details: Real-time dashboard with progress bars and cost projections
36 |   - icon: 🤖
37 |     title: Model Tracking
38 |     details: See which Claude models you're using (Opus, Sonnet, etc.)
39 |   - icon: 📋
40 |     title: Enhanced Display
41 |     details: Beautiful tables with responsive layout and smart formatting
42 |   - icon: 📄
43 |     title: JSON Output
44 |     details: Export data in structured JSON format for programmatic use
45 |   - icon: 💰
46 |     title: Cost Analysis
47 |     details: Shows estimated costs in USD for each day/month/session
48 |   - icon: 🔄
49 |     title: Cache Support
50 |     details: Tracks cache creation and cache read tokens separately
51 |   - icon: 🌐
52 |     title: Offline Mode
53 |     details: Use pre-cached pricing data without network connectivity
54 |   - icon: 🔌
55 |     title: MCP Integration
56 |     details: Built-in Model Context Protocol server for tool integration
57 | ---
58 | 
59 | <div style="text-align: center; margin: 2rem 0;">
60 |   <h2 style="margin-bottom: 1rem;">Support ccusage</h2>
61 |   <p style="margin-bottom: 1.5rem;">If you find ccusage helpful, please consider sponsoring the development!</p>
62 |   
63 |   <h3 style="margin-bottom: 1rem;">Featured Sponsor</h3>
64 |   <p style="margin-bottom: 1rem;">Check out these <a href="https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg" target="_blank">47 Claude Code ProTips from Greg Baugues.</a></p>
65 |   <a href="https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg" target="_blank">
66 |     <img src="/claude_code_protips_thumbnail_v1.png" alt="47 Claude Code ProTips from Greg Baugues" style="max-width: 600px; height: auto;">
67 |   </a>
68 |   
69 |   <div style="margin-top: 2rem;">
70 |     <a href="https://github.com/sponsors/ryoppippi" target="_blank">
71 |       <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg" alt="Sponsors" style="max-width: 100%; height: auto;">
72 |     </a>
73 |   </div>
74 | </div>
75 | 


--------------------------------------------------------------------------------
/docs/index.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 	fetch() {
3 | 		return new Response('ccusage');
4 | 	},
5 | };
6 | 


--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "@ccusage/docs",
 3 |   "version": "15.0.0",
 4 |   "private": true,
 5 |   "description": "Documentation for ccusage",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "bun run docs:api && ROLLDOWN_OPTIONS_VALIDATION=loose vitepress build",
 9 |     "deploy": "wrangler deploy",
10 |     "dev": "bun run docs:api && ROLLDOWN_OPTIONS_VALIDATION=loose vitepress dev",
11 |     "docs:api": "./update-api-index.ts",
12 |     "preview": "vitepress preview"
13 |   },
14 |   "overrides": {
15 |     "vite": "npm:rolldown-vite@latest"
16 |   },
17 |   "devDependencies": {
18 |     "@ryoppippi/vite-plugin-cloudflare-redirect": "npm:@jsr/ryoppippi__vite-plugin-cloudflare-redirect",
19 |     "@types/bun": "^1.2.18",
20 |     "tinyglobby": "^0.2.14",
21 |     "typedoc": "^0.28.7",
22 |     "typedoc-plugin-markdown": "^4.7.0",
23 |     "typedoc-vitepress-theme": "^1.1.2",
24 |     "vitepress": "^1.6.3",
25 |     "vitepress-plugin-group-icons": "^1.6.1",
26 |     "vitepress-plugin-llms": "^1.7.0",
27 |     "vitepress-plugin-mermaid": "^2.0.17",
28 |     "wrangler": "^4.24.3"
29 |   }
30 | }
31 | 


--------------------------------------------------------------------------------
/docs/public/blocks-live.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryoppippi/ccusage/86abb24ed2a8971e4104873ed5c3594183523c97/docs/public/blocks-live.png


--------------------------------------------------------------------------------
/docs/public/claude_code_protips_thumbnail_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryoppippi/ccusage/86abb24ed2a8971e4104873ed5c3594183523c97/docs/public/claude_code_protips_thumbnail_v1.png


--------------------------------------------------------------------------------
/docs/public/favicon.svg:
--------------------------------------------------------------------------------
1 | <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class=""><rect id="«r3g»" width="512" height="512" x="0" y="0" rx="128" fill="#F3E9D7" stroke="#FFFFFF" stroke-width="0" stroke-opacity="100%" paint-order="stroke"></rect><clipPath id="clip"><use xlink:href="#«r3g»"></use></clipPath><defs><linearGradient id="«r3h»" gradientUnits="userSpaceOnUse" gradientTransform="rotate(90)" style="transform-origin: center center;"><stop stop-color="#F3E9D7"></stop><stop offset="1" stop-color="#3F2B96"></stop></linearGradient><radialGradient id="«r3i»" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(256) rotate(90) scale(512)"><stop stop-color="white"></stop><stop offset="1" stop-color="white" stop-opacity="0"></stop></radialGradient></defs><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="400" height="400" x="56" y="56" alignment-baseline="middle" style="color: rgb(217, 119, 87);"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M1.75 9v3c0 1.243 1.903 2.25 4.25 2.25s4.25-1.007 4.25-2.25V9m-8.5 0c0 1.243 1.903 2.25 4.25 2.25s4.25-1.007 4.25-2.25m-8.5 0c0-1.243 1.903-2.25 4.25-2.25S10.25 7.757 10.25 9m-4.5-5c0-1.243 1.903-2.25 4.25-2.25S14.25 2.757 14.25 4m0 0c0 .86-.911 1.607-2.25 1.986M14.25 4v3c0 .623-.728 1.343-1.5 1.75"></path></svg></svg>


--------------------------------------------------------------------------------
/docs/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryoppippi/ccusage/86abb24ed2a8971e4104873ed5c3594183523c97/docs/public/logo.png


--------------------------------------------------------------------------------
/docs/public/logo.svg:
--------------------------------------------------------------------------------
1 | <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class=""><rect id="«r3g»" width="512" height="512" x="0" y="0" rx="128" fill="#F3E9D7" stroke="#FFFFFF" stroke-width="0" stroke-opacity="100%" paint-order="stroke"></rect><clipPath id="clip"><use xlink:href="#«r3g»"></use></clipPath><defs><linearGradient id="«r3h»" gradientUnits="userSpaceOnUse" gradientTransform="rotate(90)" style="transform-origin: center center;"><stop stop-color="#F3E9D7"></stop><stop offset="1" stop-color="#3F2B96"></stop></linearGradient><radialGradient id="«r3i»" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(256) rotate(90) scale(512)"><stop stop-color="white"></stop><stop offset="1" stop-color="white" stop-opacity="0"></stop></radialGradient></defs><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="400" height="400" x="56" y="56" alignment-baseline="middle" style="color: rgb(217, 119, 87);"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M1.75 9v3c0 1.243 1.903 2.25 4.25 2.25s4.25-1.007 4.25-2.25V9m-8.5 0c0 1.243 1.903 2.25 4.25 2.25s4.25-1.007 4.25-2.25m-8.5 0c0-1.243 1.903-2.25 4.25-2.25S10.25 7.757 10.25 9m-4.5-5c0-1.243 1.903-2.25 4.25-2.25S14.25 2.757 14.25 4m0 0c0 .86-.911 1.607-2.25 1.986M14.25 4v3c0 .623-.728 1.343-1.5 1.75"></path></svg></svg>


--------------------------------------------------------------------------------
/docs/public/mcp-claude-desktop.avif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryoppippi/ccusage/86abb24ed2a8971e4104873ed5c3594183523c97/docs/public/mcp-claude-desktop.avif


--------------------------------------------------------------------------------
/docs/public/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryoppippi/ccusage/86abb24ed2a8971e4104873ed5c3594183523c97/docs/public/screenshot.png


--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 | 	"extends": "../tsconfig.json",
 3 | 	"compilerOptions": {
 4 | 		"allowImportingTsExtensions": false,
 5 | 		"allowJs": true,
 6 | 		"noEmit": true,
 7 | 		"skipLibCheck": true
 8 | 	},
 9 | 	"include": [
10 | 		".vitepress/**/*",
11 | 		"**/*.ts",
12 | 		"**/*.md"
13 | 	]
14 | }
15 | 


--------------------------------------------------------------------------------
/docs/typedoc.config.mjs:
--------------------------------------------------------------------------------
 1 | // @ts-check
 2 | import { globSync } from 'tinyglobby'
 3 | 
 4 | const entryPoints = [
 5 | 	...globSync([
 6 | 		'../src/*.ts',
 7 | 		'!../src/**/*.test.ts', // Exclude test files
 8 | 		'!../src/_*.ts', // Exclude internal files with underscore prefix
 9 | 	], {
10 | 		absolute: false,
11 | 		onlyFiles: true,
12 | 	}),
13 | 	'../src/_consts.ts', // Include constants for documentation
14 | ];
15 | 
16 | /** @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions & { docsRoot?: string } } */
17 | export default {
18 | 	// typedoc options
19 | 	// ref: https://typedoc.org/documents/Options.html
20 | 	entryPoints,
21 | 	tsconfig: '../tsconfig.json',
22 | 	out: 'api',
23 | 	plugin: ['typedoc-plugin-markdown', 'typedoc-vitepress-theme'],
24 | 	readme: 'none',
25 | 	excludeInternal: true,
26 | 	groupOrder: ['Variables', 'Functions', 'Class'],
27 | 	categoryOrder: ['*', 'Other'],
28 | 	sort: ['source-order'],
29 | 
30 | 	// typedoc-plugin-markdown options
31 | 	// ref: https://typedoc-plugin-markdown.org/docs/options
32 | 	entryFileName: 'index',
33 | 	hidePageTitle: false,
34 | 	useCodeBlocks: true,
35 | 	disableSources: true,
36 | 	indexFormat: 'table',
37 | 	parametersFormat: 'table',
38 | 	interfacePropertiesFormat: 'table',
39 | 	classPropertiesFormat: 'table',
40 | 	propertyMembersFormat: 'table',
41 | 	typeAliasPropertiesFormat: 'table',
42 | 	enumMembersFormat: 'table',
43 | 
44 | 	// typedoc-vitepress-theme options
45 | 	// ref: https://typedoc-plugin-markdown.org/plugins/vitepress/options
46 | 	docsRoot: '.',
47 | };
48 | 


--------------------------------------------------------------------------------
/docs/update-api-index.ts:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env -S bun -b
 2 | 
 3 | /**
 4 |  * Post-processing script to update API index with module descriptions
 5 |  */
 6 | 
 7 | import { join } from 'node:path';
 8 | import process from 'node:process';
 9 | import { $ } from 'bun';
10 | 
11 | const descriptions = {
12 | 	'\\_consts': 'Internal constants (not exported in public API)',
13 | 	'calculate-cost': 'Cost calculation utilities for usage data analysis',
14 | 	'data-loader': 'Data loading utilities for Claude Code usage analysis',
15 | 	'debug': 'Debug utilities for cost calculation validation',
16 | 	'index': 'Main entry point for ccusage CLI tool',
17 | 	'logger': 'Logging utilities for the ccusage application',
18 | 	'mcp': 'MCP (Model Context Protocol) server implementation',
19 | 	'pricing-fetcher': 'Model pricing data fetcher for cost calculations',
20 | } as const;
21 | 
22 | async function updateApiIndex() {
23 | 	const apiIndexPath = join(process.cwd(), 'api', 'index.md');
24 | 
25 | 	try {
26 | 		let content = await Bun.file(apiIndexPath).text();
27 | 
28 | 		// Replace empty descriptions with actual ones
29 | 		for (const [module, description] of Object.entries(descriptions)) {
30 | 			let linkPath = `${module}/index.md`;
31 | 			// Special case for _consts which links to consts/
32 | 			if (module === '\\_consts') {
33 | 				linkPath = 'consts/index.md';
34 | 			}
35 | 
36 | 			const oldPattern = new RegExp(`\\|\\s*\\[${module}\\]\\(${linkPath}\\)\\s*\\|\\s*-\\s*\\|`, 'g');
37 | 			content = content.replace(oldPattern, `| [${module}](${linkPath}) | ${description} |`);
38 | 		}
39 | 
40 | 		await Bun.write(apiIndexPath, content);
41 | 		console.log('✅ Updated API index with module descriptions');
42 | 	}
43 | 	catch (error) {
44 | 		console.error('❌ Failed to update API index:', error);
45 | 		process.exit(1);
46 | 	}
47 | }
48 | 
49 | async function updateConstsPage() {
50 | 	const constsIndexPath = join(process.cwd(), 'api', 'consts', 'index.md');
51 | 
52 | 	try {
53 | 		let content = await Bun.file(constsIndexPath).text();
54 | 
55 | 		// Add note about constants not being exported (only if not already present)
56 | 		const noteText = '> **Note**: These constants are internal implementation details and are not exported in the public API. They are documented here for reference purposes only.';
57 | 
58 | 		if (!content.includes(noteText)) {
59 | 			const oldHeader = '# \\_consts';
60 | 			const newHeader = `# \\_consts
61 | 
62 | ${noteText}`;
63 | 
64 | 			content = content.replace(oldHeader, newHeader);
65 | 		}
66 | 
67 | 		await Bun.write(constsIndexPath, content);
68 | 		console.log('✅ Updated constants page with disclaimer');
69 | 	}
70 | 	catch (error) {
71 | 		console.error('❌ Failed to update constants page:', error);
72 | 		// Don't exit here as this is optional
73 | 	}
74 | }
75 | 
76 | async function main() {
77 | 	await 



    
    

    
    

    
    
    
    

    
    
    
    




    

    
The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
bun -b typedoc --excludeInternal` 78 | await updateApiIndex(); 79 | await updateConstsPage(); 80 | } 81 | 82 | await main(); 83 | -------------------------------------------------------------------------------- /docs/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/wrangler/config-schema.json", 3 | "name": "ccusage-guide", 4 | "main": "./index.ts", 5 | "compatibility_date": "2025-06-21", 6 | "build": { 7 | "command": "bun run build" 8 | }, 9 | "assets": { 10 | "binding": "ASSETS", 11 | "directory": ".vitepress/dist/", 12 | "html_handling": "drop-trailing-slash", 13 | "run_worker_first": false, 14 | "not_found_handling": "404-page" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import { ryoppippi } from '@ryoppippi/eslint-config'; 2 | 3 | export default ryoppippi({ 4 | type: 'lib', 5 | svelte: false, 6 | typescript: { 7 | tsconfigPath: './tsconfig.json', 8 | }, 9 | ignores: [ 10 | 'docs/**', 11 | ], 12 | }); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ccusage", 3 | "type": "module", 4 | "version": "15.3.1", 5 | "description": "Usage analysis tool for Claude Code", 6 | "author": "ryoppippi", 7 | "license": "MIT", 8 | "funding": "https://github.com/ryoppippi/ccusage?sponsor=1", 9 | "homepage": "https://github.com/ryoppippi/ccusage#readme", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ryoppippi/ccusage.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/ryoppippi/ccusage/issues" 16 | }, 17 | "exports": { 18 | ".": "./dist/index.js", 19 | "./calculate-cost": "./dist/calculate-cost.js", 20 | "./data-loader": "./dist/data-loader.js", 21 | "./debug": "./dist/debug.js", 22 | "./logger": "./dist/logger.js", 23 | "./mcp": "./dist/mcp.js", 24 | "./pricing-fetcher": "./dist/pricing-fetcher.js", 25 | "./package.json": "./package.json" 26 | }, 27 | "main": "./dist/index.js", 28 | "module": "./dist/index.js", 29 | "types": "./dist/index.d.ts", 30 | "bin": "./dist/index.js", 31 | "files": [ 32 | "dist" 33 | ], 34 | "workspaces": [ 35 | ".", 36 | "docs" 37 | ], 38 | "engines": { 39 | "node": ">=20.19.3" 40 | }, 41 | "scripts": { 42 | "build": "tsdown", 43 | "docs:build": "cd docs && bun run build", 44 | "docs:deploy": "cd docs && bun run deploy", 45 | "docs:dev": "cd docs && bun run dev", 46 | "docs:preview": "cd docs && bun run preview", 47 | "format": "bun run lint --fix", 48 | "lint": "eslint --cache .", 49 | "mcp": "bunx @modelcontextprotocol/inspector bun run start mcp", 50 | "prepack": "bun run build && clean-pkg-json", 51 | "prepare": "simple-git-hooks", 52 | "release": "bun lint && bun typecheck && vitest run && bun run build && bumpp", 53 | "start": "bun run ./src/index.ts", 54 | "test": "vitest", 55 | "typecheck": "tsgo --noEmit" 56 | }, 57 | "devDependencies": { 58 | "@antfu/utils": "^9.2.0", 59 | "@core/errorutil": "npm:@jsr/core__errorutil", 60 | "@hono/mcp": "^0.1.0", 61 | "@hono/node-server": "^1.15.0", 62 | "@jsr/std__async": "1", 63 | "@modelcontextprotocol/sdk": "^1.15.1", 64 | "@oxc-project/runtime": "^0.77.0", 65 | "@praha/byethrow": "^0.6.2", 66 | "@praha/byethrow-mcp": "^0.0.14", 67 | "@ryoppippi/eslint-config": "^0.3.7", 68 | "@types/bun": "^1.2.18", 69 | "@typescript/native-preview": "^7.0.0-dev.20250712.1", 70 | "ansi-escapes": "^7.0.0", 71 | "bumpp": "^10.2.0", 72 | "clean-pkg-json": "^1.3.0", 73 | "cli-table3": "^0.6.5", 74 | "consola": "^3.4.2", 75 | "es-toolkit": "^1.39.7", 76 | "eslint": "^9.31.0", 77 | "eslint-plugin-format": "^1.0.1", 78 | "fast-sort": "^3.4.1", 79 | "fs-fixture": "^2.8.1", 80 | "gunshi": "^0.26.3", 81 | "hono": "^4.8.4", 82 | "lint-staged": "^16.1.2", 83 | "path-type": "^6.0.0", 84 | "picocolors": "^1.1.1", 85 | "pretty-ms": "^9.2.0", 86 | "publint": "^0.3.12", 87 | "simple-git-hooks": "^2.13.0", 88 | "sort-package-json": "^3.4.0", 89 | "string-width": "^7.2.0", 90 | "tinyglobby": "^0.2.14", 91 | "tsdown": "^0.12.9", 92 | "type-fest": "^4.41.0", 93 | "unplugin-macros": "^0.17.0", 94 | "unplugin-unused": "^0.5.1", 95 | "vitest": "^3.2.4", 96 | "xdg-basedir": "^5.1.0", 97 | "zod": "^3.25.67" 98 | }, 99 | "overrides": { 100 | "vite": "npm:rolldown-vite@latest" 101 | }, 102 | "simple-git-hooks": { 103 | "pre-commit": "bun lint-staged" 104 | }, 105 | "lint-staged": { 106 | "*": [ 107 | "eslint --cache --fix" 108 | ], 109 | "package.json": [ 110 | "sort-package-json" 111 | ] 112 | }, 113 | "trustedDependencies": [ 114 | "simple-git-hooks" 115 | ] 116 | } 117 | -------------------------------------------------------------------------------- /src/_consts.ts: -------------------------------------------------------------------------------- 1 | import { homedir } from 'node:os'; 2 | import { xdgConfig } from 'xdg-basedir'; 3 | 4 | /** 5 | * URL for LiteLLM's model pricing and context window data 6 | */ 7 | export const LITELLM_PRICING_URL 8 | = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json'; 9 | 10 | /** 11 | * Default number of recent days to include when filtering blocks 12 | * Used in both session blocks and commands for consistent behavior 13 | */ 14 | export const DEFAULT_RECENT_DAYS = 3; 15 | 16 | /** 17 | * Threshold percentage for showing usage warnings in blocks command (80%) 18 | * When usage exceeds this percentage of limits, warnings are displayed 19 | */ 20 | export const BLOCKS_WARNING_THRESHOLD = 0.8; 21 | 22 | /** 23 | * Terminal width threshold for switching to compact display mode in blocks command 24 | * Below this width, tables use more compact formatting 25 | */ 26 | export const BLOCKS_COMPACT_WIDTH_THRESHOLD = 120; 27 | 28 | /** 29 | * Default terminal width when stdout.columns is not available in blocks command 30 | * Used as fallback for responsive table formatting 31 | */ 32 | export const BLOCKS_DEFAULT_TERMINAL_WIDTH = 120; 33 | 34 | /** 35 | * Threshold percentage for considering costs as matching (0.1% tolerance) 36 | * Used in debug cost validation to allow for minor calculation differences 37 | */ 38 | export const DEBUG_MATCH_THRESHOLD_PERCENT = 0.1; 39 | 40 | /** 41 | * User's home directory path 42 | * Centralized access to OS home directory for consistent path building 43 | */ 44 | export const USER_HOME_DIR: string = homedir(); 45 | 46 | /** 47 | * XDG config directory path 48 | * Uses XDG_CONFIG_HOME if set, otherwise falls back to ~/.config 49 | */ 50 | const XDG_CONFIG_DIR: string = xdgConfig ?? `${USER_HOME_DIR}/.config`; 51 | 52 | /** 53 | * Default Claude data directory path (~/.claude) 54 | * Used as base path for loading usage data from JSONL files 55 | */ 56 | export const DEFAULT_CLAUDE_CODE_PATH = '.claude'; 57 | 58 | /** 59 | * Default Claude data directory path using XDG config directory 60 | * Uses XDG_CONFIG_HOME if set, otherwise falls back to ~/.config/claude 61 | */ 62 | export const DEFAULT_CLAUDE_CONFIG_PATH = `${XDG_CONFIG_DIR}/claude`; 63 | 64 | /** 65 | * Environment variable for specifying multiple Claude data directories 66 | * Supports comma-separated paths for multiple locations 67 | */ 68 | export const CLAUDE_CONFIG_DIR_ENV = 'CLAUDE_CONFIG_DIR'; 69 | 70 | /** 71 | * Claude projects directory name within the data directory 72 | * Contains subdirectories for each project with usage data 73 | */ 74 | export const CLAUDE_PROJECTS_DIR_NAME = 'projects'; 75 | 76 | /** 77 | * JSONL file glob pattern for finding usage data files 78 | * Used to recursively find all JSONL files in project directories 79 | */ 80 | export const USAGE_DATA_GLOB_PATTERN = '**/*.jsonl'; 81 | 82 | /** 83 | * Default port for MCP server HTTP transport 84 | * Used when no port is specified for MCP server communication 85 | */ 86 | export const MCP_DEFAULT_PORT = 8080; 87 | 88 | /** 89 | * Default refresh interval in seconds for live monitoring mode 90 | * Used in blocks command for real-time updates 91 | */ 92 | export const DEFAULT_REFRESH_INTERVAL_SECONDS = 1; 93 | 94 | /** 95 | * Minimum refresh interval in seconds for live monitoring mode 96 | * Prevents too-frequent updates that could impact performance 97 | */ 98 | export const MIN_REFRESH_INTERVAL_SECONDS = 1; 99 | 100 | /** 101 | * Maximum refresh interval in seconds for live monitoring mode 102 | * Prevents too-slow updates that reduce monitoring effectiveness 103 | */ 104 | export const MAX_REFRESH_INTERVAL_SECONDS = 60; 105 | 106 | /** 107 | * Frame rate limit for live monitoring (16ms = ~60fps) 108 | * Prevents terminal flickering and excessive CPU usage during rapid updates 109 | */ 110 | export const MIN_RENDER_INTERVAL_MS = 16; 111 | 112 | /** 113 | * Burn rate thresholds for indicator display (tokens per minute) 114 | */ 115 | export const BURN_RATE_THRESHOLDS = { 116 | HIGH: 1000, 117 | MODERATE: 500, 118 | } as const; 119 | -------------------------------------------------------------------------------- /src/_live-monitor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Live monitoring implementation for Claude usage data 3 | * 4 | * This module provides efficient incremental data loading for the live monitoring feature 5 | * in the blocks command. It tracks file modifications and only reads changed data, 6 | * maintaining a cache of processed entries to minimize file I/O during live updates. 7 | * 8 | * Used exclusively by blocks-live.ts for the --live flag functionality. 9 | */ 10 | 11 | import type { LoadedUsageEntry, SessionBlock } from './_session-blocks.ts'; 12 | import type { CostMode, SortOrder } from './_types.ts'; 13 | import { readFile } from 'node:fs/promises'; 14 | import path from 'node:path'; 15 | import { glob } from 'tinyglobby'; 16 | import { CLAUDE_PROJECTS_DIR_NAME, USAGE_DATA_GLOB_PATTERN } from './_consts.ts'; 17 | import { identifySessionBlocks } from './_session-blocks.ts'; 18 | import { 19 | calculateCostForEntry, 20 | createUniqueHash, 21 | getEarliestTimestamp, 22 | getUsageLimitResetTime, 23 | sortFilesByTimestamp, 24 | usageDataSchema, 25 | } from './data-loader.ts'; 26 | import { PricingFetcher } from './pricing-fetcher.ts'; 27 | 28 | /** 29 | * Configuration for live monitoring 30 | */ 31 | export type LiveMonitorConfig = { 32 | claudePath: string; 33 | sessionDurationHours: number; 34 | mode: CostMode; 35 | order: SortOrder; 36 | }; 37 | 38 | /** 39 | * Manages live monitoring of Claude usage with efficient data reloading 40 | */ 41 | export class LiveMonitor implements Disposable { 42 | private config: LiveMonitorConfig; 43 | private fetcher: PricingFetcher | null = null; 44 | private lastFileTimestamps = new Map<string, number>(); 45 | private processedHashes = new Set<string>(); 46 | private allEntries: LoadedUsageEntry[] = []; 47 | 48 | constructor(config: LiveMonitorConfig) { 49 | this.config = config; 50 | // Initialize pricing fetcher once if needed 51 | if (config.mode !== 'display') { 52 | this.fetcher = new PricingFetcher(); 53 | } 54 | } 55 | 56 | /** 57 | * Implements Disposable interface 58 | */ 59 | [Symbol.dispose](): void { 60 | this.fetcher?.[Symbol.dispose](); 61 | } 62 | 63 | /** 64 | * Gets the current active session block with minimal file reading 65 | * Only reads new or modified files since last check 66 | */ 67 | async getActiveBlock(): Promise<SessionBlock | null> { 68 | const claudeDir = path.join(this.config.claudePath, CLAUDE_PROJECTS_DIR_NAME); 69 | const files = await glob([USAGE_DATA_GLOB_PATTERN], { 70 | cwd: claudeDir, 71 | absolute: true, 72 | }); 73 | 74 | if (files.length === 0) { 75 | return null; 76 | } 77 | 78 | // Check for new or modified files 79 | const filesToRead: string[] = []; 80 | for (const file of files) { 81 | const timestamp = await getEarliestTimestamp(file); 82 | const lastTimestamp = this.lastFileTimestamps.get(file); 83 | 84 | if (timestamp != null && (lastTimestamp == null || timestamp.getTime() > lastTimestamp)) { 85 | filesToRead.push(file); 86 | this.lastFileTimestamps.set(file, timestamp.getTime()); 87 | } 88 | } 89 | 90 | // Read only new/modified files 91 | if (filesToRead.length > 0) { 92 | const sortedFiles = await sortFilesByTimestamp(filesToRead); 93 | 94 | for (const file of sortedFiles) { 95 | const content = await readFile(file, 'utf-8') 96 | .catch(() => { 97 | // Skip files that can't be read 98 | return ''; 99 | }); 100 | 101 | const lines = content 102 | .trim() 103 | .split('\n') 104 | .filter(line => line.length > 0); 105 | 106 | for (const line of lines) { 107 | try { 108 | const parsed = JSON.parse(line) as unknown; 109 | const result = usageDataSchema.safeParse(parsed); 110 | if (!result.success) { 111 | continue; 112 | } 113 | const data = result.data; 114 | 115 | // Check for duplicates 116 | const uniqueHash = createUniqueHash(data); 117 | if (uniqueHash != null && this.processedHashes.has(uniqueHash)) { 118 | continue; 119 | } 120 | if (uniqueHash != null) { 121 | this.processedHashes.add(uniqueHash); 122 | } 123 | 124 | // Calculate cost if needed 125 | const costUSD: number = await (this.config.mode === 'display' 126 | ? Promise.resolve(data.costUSD ?? 0) 127 | : calculateCostForEntry( 128 | data, 129 | this.config.mode, 130 | this.fetcher!, 131 | )); 132 | 133 | const usageLimitResetTime = getUsageLimitResetTime(data); 134 | 135 | // Add entry 136 | this.allEntries.push({ 137 | timestamp: new Date(data.timestamp), 138 | usage: { 139 | inputTokens: data.message.usage.input_tokens ?? 0, 140 | outputTokens: data.message.usage.output_tokens ?? 0, 141 | cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0, 142 | cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0, 143 | }, 144 | costUSD, 145 | model: data.message.model ?? '<synthetic>', 146 | version: data.version, 147 | usageLimitResetTime: usageLimitResetTime ?? undefined, 148 | }); 149 | } 150 | catch { 151 | // Skip malformed lines 152 | } 153 | } 154 | } 155 | } 156 | 157 | // Generate blocks and find active one 158 | const blocks = identifySessionBlocks( 159 | this.allEntries, 160 | this.config.sessionDurationHours, 161 | ); 162 | 163 | // Sort blocks 164 | const sortedBlocks = this.config.order === 'asc' 165 | ? blocks 166 | : blocks.reverse(); 167 | 168 | // Find active block 169 | return sortedBlocks.find(block => block.isActive) ?? null; 170 | } 171 | 172 | /** 173 | * Clears all cached data to force a full reload 174 | */ 175 | clearCache(): void { 176 | this.lastFileTimestamps.clear(); 177 | this.processedHashes.clear(); 178 | this.allEntries = []; 179 | } 180 | } 181 | 182 | if (import.meta.vitest != null) { 183 | describe('LiveMonitor', () => { 184 | let tempDir: string; 185 | let monitor: LiveMonitor; 186 | 187 | beforeEach(async () => { 188 | const { createFixture } = await import('fs-fixture'); 189 | const now = new Date(); 190 | const recentTimestamp = new Date(now.getTime() - 60 * 60 * 1000); // 1 hour ago 191 | 192 | const fixture = await createFixture({ 193 | 'projects/test-project/session1/usage.jsonl': `${JSON.stringify({ 194 | timestamp: recentTimestamp.toISOString(), 195 | message: { 196 | model: 'claude-sonnet-4-20250514', 197 | usage: { 198 | input_tokens: 100, 199 | output_tokens: 50, 200 | cache_creation_input_tokens: 0, 201 | cache_read_input_tokens: 0, 202 | }, 203 | }, 204 | costUSD: 0.01, 205 | version: '1.0.0', 206 | })}\n`, 207 | }); 208 | tempDir = fixture.path; 209 | 210 | monitor = new LiveMonitor({ 211 | claudePath: tempDir, 212 | sessionDurationHours: 5, 213 | mode: 'display', 214 | order: 'desc', 215 | }); 216 | }); 217 | 218 | afterEach(() => { 219 | monitor[Symbol.dispose](); 220 | }); 221 | 222 | it('should initialize and handle clearing cache', async () => { 223 | // Test initial state by calling getActiveBlock which should work 224 | const initialBlock = await monitor.getActiveBlock(); 225 | expect(initialBlock).not.toBeNull(); 226 | 227 | // Clear cache and test again 228 | monitor.clearCache(); 229 | const afterClearBlock = await monitor.getActiveBlock(); 230 | expect(afterClearBlock).not.toBeNull(); 231 | }); 232 | 233 | it('should load and process usage data', async () => { 234 | const activeBlock = await monitor.getActiveBlock(); 235 | 236 | expect(activeBlock).not.toBeNull(); 237 | if (activeBlock != null) { 238 | expect(activeBlock.tokenCounts.inputTokens).toBe(100); 239 | expect(activeBlock.tokenCounts.outputTokens).toBe(50); 240 | expect(activeBlock.costUSD).toBe(0.01); 241 | expect(activeBlock.models).toContain('claude-sonnet-4-20250514'); 242 | } 243 | }); 244 | 245 | it('should handle empty directories', async () => { 246 | const { createFixture } = await import('fs-fixture'); 247 | const emptyFixture = await createFixture({}); 248 | 249 | const emptyMonitor = new LiveMonitor({ 250 | claudePath: emptyFixture.path, 251 | sessionDurationHours: 5, 252 | mode: 'display', 253 | order: 'desc', 254 | }); 255 | 256 | const activeBlock = await emptyMonitor.getActiveBlock(); 257 | expect(activeBlock).toBeNull(); 258 | 259 | emptyMonitor[Symbol.dispose](); 260 | }); 261 | }); 262 | } 263 | -------------------------------------------------------------------------------- /src/_macro.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Prefetch claude data for the current user. 3 | */ 4 | 5 | import type { ModelPricing } from './_types.ts'; 6 | import { LITELLM_PRICING_URL } from './_consts.ts'; 7 | import { modelPricingSchema } from './_types.ts'; 8 | 9 | /** 10 | * Prefetches the pricing data for Claude models from the LiteLLM API. 11 | * This function fetches the pricing data and filters out models that start with 'claude-'. 12 | * It returns a record of model names to their pricing information. 13 | * 14 | * @returns A promise that resolves to a record of model names and their pricing information. 15 | * @throws Will throw an error if the fetch operation fails. 16 | */ 17 | export async function prefetchClaudePricing(): Promise<Record<string, ModelPricing>> { 18 | const response = await fetch(LITELLM_PRICING_URL); 19 | if (!response.ok) { 20 | throw new Error(`Failed to fetch pricing data: ${response.statusText}`); 21 | } 22 | 23 | const data = await response.json() as Record<string, unknown>; 24 | 25 | const prefetchClaudeData: Record<string, ModelPricing> = {}; 26 | 27 | // Cache all models that start with 'claude-' 28 | for (const [modelName, modelData] of Object.entries(data)) { 29 | if (modelName.startsWith('claude-') && modelData != null && typeof modelData === 'object') { 30 | const parsed = modelPricingSchema.safeParse(modelData); 31 | if (parsed.success) { 32 | prefetchClaudeData[modelName] = parsed.data; 33 | } 34 | } 35 | } 36 | 37 | return prefetchClaudeData; 38 | } 39 | -------------------------------------------------------------------------------- /src/_shared-args.ts: -------------------------------------------------------------------------------- 1 | import type { Args } from 'gunshi'; 2 | import type { CostMode, SortOrder } from './_types.ts'; 3 | import { CostModes, filterDateSchema, SortOrders } from './_types.ts'; 4 | 5 | /** 6 | * Parses and validates a date argument in YYYYMMDD format 7 | * @param value - Date string to parse 8 | * @returns Validated date string 9 | * @throws TypeError if date format is invalid 10 | */ 11 | function parseDateArg(value: string): string { 12 | const result = filterDateSchema.safeParse(value); 13 | if (!result.success) { 14 | throw new TypeError(result.error.issues[0]?.message ?? 'Invalid date format'); 15 | } 16 | return result.data; 17 | } 18 | 19 | /** 20 | * Shared command line arguments used across multiple CLI commands 21 | */ 22 | export const sharedArgs = { 23 | since: { 24 | type: 'custom', 25 | short: 's', 26 | description: 'Filter from date (YYYYMMDD format)', 27 | parse: parseDateArg, 28 | }, 29 | until: { 30 | type: 'custom', 31 | short: 'u', 32 | description: 'Filter until date (YYYYMMDD format)', 33 | parse: parseDateArg, 34 | }, 35 | json: { 36 | type: 'boolean', 37 | short: 'j', 38 | description: 'Output in JSON format', 39 | default: false, 40 | }, 41 | mode: { 42 | type: 'enum', 43 | short: 'm', 44 | description: 45 | 'Cost calculation mode: auto (use costUSD if exists, otherwise calculate), calculate (always calculate), display (always use costUSD)', 46 | default: 'auto' as const satisfies CostMode, 47 | choices: CostModes, 48 | }, 49 | debug: { 50 | type: 'boolean', 51 | short: 'd', 52 | description: 'Show pricing mismatch information for debugging', 53 | default: false, 54 | }, 55 | debugSamples: { 56 | type: 'number', 57 | description: 58 | 'Number of sample discrepancies to show in debug output (default: 5)', 59 | default: 5, 60 | }, 61 | order: { 62 | type: 'enum', 63 | short: 'o', 64 | description: 'Sort order: desc (newest first) or asc (oldest first)', 65 | default: 'asc' as const satisfies SortOrder, 66 | choices: SortOrders, 67 | }, 68 | breakdown: { 69 | type: 'boolean', 70 | short: 'b', 71 | description: 'Show per-model cost breakdown', 72 | default: false, 73 | }, 74 | offline: { 75 | type: 'boolean', 76 | negatable: true, 77 | short: 'O', 78 | description: 'Use cached pricing data for Claude models instead of fetching from API', 79 | default: false, 80 | }, 81 | color: { // --color and FORCE_COLOR=1 is handled by picocolors 82 | type: 'boolean', 83 | description: 'Enable colored output (default: auto). FORCE_COLOR=1 has the same effect.', 84 | }, 85 | noColor: { // --no-color and NO_COLOR=1 is handled by picocolors 86 | type: 'boolean', 87 | description: 'Disable colored output (default: auto). NO_COLOR=1 has the same effect.', 88 | }, 89 | } as const satisfies Args; 90 | 91 | /** 92 | * Shared command configuration for Gunshi CLI commands 93 | */ 94 | export const sharedCommandConfig = { 95 | args: sharedArgs, 96 | toKebab: true, 97 | } as const; 98 | -------------------------------------------------------------------------------- /src/_terminal-utils.ts: -------------------------------------------------------------------------------- 1 | import type { WriteStream } from 'node:tty'; 2 | import process from 'node:process'; 3 | import * as ansiEscapes from 'ansi-escapes'; 4 | import stringWidth from 'string-width'; 5 | 6 | // DEC synchronized output mode - prevents screen flickering by buffering all terminal writes 7 | // until flush() is called. Think of it like double-buffering in graphics programming. 8 | // Reference: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 9 | const SYNC_START = '\x1B[?2026h'; // Start sync mode 10 | const SYNC_END = '\x1B[?2026l'; // End sync mode 11 | 12 | // Line wrap control sequences 13 | const DISABLE_LINE_WRAP = '\x1B[?7l'; // Disable automatic line wrapping 14 | const ENABLE_LINE_WRAP = '\x1B[?7h'; // Enable automatic line wrapping 15 | 16 | // ANSI reset sequence 17 | const ANSI_RESET = '\u001B[0m'; // Reset all formatting and colors 18 | 19 | /** 20 | * Manages terminal state for live updates 21 | * Provides a clean interface for terminal operations with automatic TTY checking 22 | * and cursor state management for live monitoring displays 23 | */ 24 | export class TerminalManager { 25 | private stream: WriteStream; 26 | private cursorHidden = false; 27 | private buffer: string[] = []; 28 | private useBuffering = false; 29 | private alternateScreenActive = false; 30 | private syncMode = false; 31 | 32 | constructor(stream: WriteStream = process.stdout) { 33 | this.stream = stream; 34 | } 35 | 36 | /** 37 | * Hides the terminal cursor for cleaner live updates 38 | * Only works in TTY environments (real terminals) 39 | */ 40 | hideCursor(): void { 41 | if (!this.cursorHidden && this.stream.isTTY) { 42 | // Only hide cursor in TTY environments to prevent issues with non-interactive streams 43 | this.stream.write(ansiEscapes.cursorHide); 44 | this.cursorHidden = true; 45 | } 46 | } 47 | 48 | /** 49 | * Shows the terminal cursor 50 | * Should be called during cleanup to restore normal terminal behavior 51 | */ 52 | showCursor(): void { 53 | if (this.cursorHidden && this.stream.isTTY) { 54 | this.stream.write(ansiEscapes.cursorShow); 55 | this.cursorHidden = false; 56 | } 57 | } 58 | 59 | /** 60 | * Clears the entire screen and moves cursor to top-left corner 61 | * Essential for live monitoring displays that need to refresh completely 62 | */ 63 | clearScreen(): void { 64 | if (this.stream.isTTY) { 65 | // Only clear screen in TTY environments to prevent issues with non-interactive streams 66 | this.stream.write(ansiEscapes.clearScreen); 67 | this.stream.write(ansiEscapes.cursorTo(0, 0)); 68 | } 69 | } 70 | 71 | /** 72 | * Writes text to the terminal stream 73 | * Supports buffering mode for performance optimization 74 | */ 75 | write(text: string): void { 76 | if (this.useBuffering) { 77 | this.buffer.push(text); 78 | } 79 | else { 80 | this.stream.write(text); 81 | } 82 | } 83 | 84 | /** 85 | * Enables buffering mode - collects all writes in memory instead of sending immediately 86 | * This prevents flickering when doing many rapid updates 87 | */ 88 | startBuffering(): void { 89 | this.useBuffering = true; 90 | this.buffer = []; 91 | } 92 | 93 | /** 94 | * Sends all buffered content to terminal at once 95 | * This creates smooth, atomic updates without flickering 96 | */ 97 | flush(): void { 98 | if (this.useBuffering && this.buffer.length > 0) { 99 | // Wrap output in sync mode for truly atomic screen updates 100 | if (this.syncMode && this.stream.isTTY) { 101 | this.stream.write(SYNC_START + this.buffer.join('') + SYNC_END); 102 | } 103 | else { 104 | this.stream.write(this.buffer.join('')); 105 | } 106 | this.buffer = []; 107 | } 108 | this.useBuffering = false; 109 | } 110 | 111 | /** 112 | * Switches to alternate screen buffer (like vim/less does) 113 | * This preserves what was on screen before and allows full-screen apps 114 | */ 115 | enterAlternateScreen(): void { 116 | if (!this.alternateScreenActive && this.stream.isTTY) { 117 | this.stream.write(ansiEscapes.enterAlternativeScreen); 118 | // Turn off line wrapping to prevent text from breaking badly 119 | this.stream.write(DISABLE_LINE_WRAP); 120 | this.alternateScreenActive = true; 121 | } 122 | } 123 | 124 | /** 125 | * Returns to normal screen, restoring what was there before 126 | */ 127 | exitAlternateScreen(): void { 128 | if (this.alternateScreenActive && this.stream.isTTY) { 129 | // Re-enable line wrap 130 | this.stream.write(ENABLE_LINE_WRAP); 131 | this.stream.write(ansiEscapes.exitAlternativeScreen); 132 | this.alternateScreenActive = false; 133 | } 134 | } 135 | 136 | /** 137 | * Enables sync mode - terminal will wait for END signal before showing updates 138 | * Prevents the user from seeing partial/torn screen updates 139 | */ 140 | enableSyncMode(): void { 141 | this.syncMode = true; 142 | } 143 | 144 | /** 145 | * Disables synchronized output mode 146 | */ 147 | disableSyncMode(): void { 148 | this.syncMode = false; 149 | } 150 | 151 | /** 152 | * Gets terminal width in columns 153 | * Falls back to 80 columns if detection fails 154 | */ 155 | get width(): number { 156 | return this.stream.columns || 80; 157 | } 158 | 159 | /** 160 | * Gets terminal height in rows 161 | * Falls back to 24 rows if detection fails 162 | */ 163 | get height(): number { 164 | return this.stream.rows || 24; 165 | } 166 | 167 | /** 168 | * Returns true if output goes to a real terminal (not a file or pipe) 169 | * We only send fancy ANSI codes to real terminals 170 | */ 171 | get isTTY(): boolean { 172 | return this.stream.isTTY ?? false; 173 | } 174 | 175 | /** 176 | * Restores terminal to normal state - MUST call before program exits 177 | * Otherwise user's terminal might be left in a broken state 178 | */ 179 | cleanup(): void { 180 | this.showCursor(); 181 | this.exitAlternateScreen(); 182 | this.disableSyncMode(); 183 | } 184 | } 185 | 186 | /** 187 | * Creates a progress bar string with customizable appearance 188 | * 189 | * Example: createProgressBar(75, 100, 20) -> "[████████████████░░░░] 75.0%" 190 | * 191 | * @param value - Current progress value 192 | * @param max - Maximum value (100% point) 193 | * @param width - Character width of the progress bar (excluding brackets and text) 194 | * @param options - Customization options for appearance and display 195 | * @param options.showPercentage - Whether to show percentage after the bar 196 | * @param options.showValues - Whether to show current/max values 197 | * @param options.fillChar - Character for filled portion (default: '█') 198 | * @param options.emptyChar - Character for empty portion (default: '░') 199 | * @param options.leftBracket - Left bracket character (default: '[') 200 | * @param options.rightBracket - Right bracket character (default: ']') 201 | * @param options.colors - Color configuration for different thresholds 202 | * @param options.colors.low - Color for low percentage values 203 | * @param options.colors.medium - Color for medium percentage values 204 | * @param options.colors.high - Color for high percentage values 205 | * @param options.colors.critical - Color for critical percentage values 206 | * @returns Formatted progress bar string with optional percentage/values 207 | */ 208 | export function createProgressBar( 209 | value: number, 210 | max: number, 211 | width: number, 212 | options: { 213 | showPercentage?: boolean; 214 | showValues?: boolean; 215 | fillChar?: string; 216 | emptyChar?: string; 217 | leftBracket?: string; 218 | rightBracket?: string; 219 | colors?: { 220 | low?: string; 221 | medium?: string; 222 | high?: string; 223 | critical?: string; 224 | }; 225 | } = {}, 226 | ): string { 227 | const { 228 | showPercentage = true, 229 | showValues = false, 230 | fillChar = '█', 231 | emptyChar = '░', 232 | leftBracket = '[', 233 | rightBracket = ']', 234 | colors = {}, 235 | } = options; 236 | 237 | const percentage = max > 0 ? Math.min(100, (value / max) * 100) : 0; 238 | const fillWidth = Math.round((percentage / 100) * width); 239 | const emptyWidth = width - fillWidth; 240 | 241 | // Determine color based on percentage 242 | let color = ''; 243 | if (colors.critical != null && percentage >= 90) { 244 | color = colors.critical; 245 | } 246 | else if (colors.high != null && percentage >= 80) { 247 | color = colors.high; 248 | } 249 | else if (colors.medium != null && percentage >= 50) { 250 | color = colors.medium; 251 | } 252 | else if (colors.low != null) { 253 | color = colors.low; 254 | } 255 | 256 | // Build progress bar 257 | let bar = leftBracket; 258 | if (color !== '') { 259 | bar += color; 260 | } 261 | bar += fillChar.repeat(fillWidth); 262 | bar += emptyChar.repeat(emptyWidth); 263 | if (color !== '') { 264 | bar += ANSI_RESET; // Reset color 265 | } 266 | bar += rightBracket; 267 | 268 | // Add percentage or values 269 | if (showPercentage) { 270 | bar += ` ${percentage.toFixed(1)}%`; 271 | } 272 | if (showValues) { 273 | bar += ` (${value}/${max})`; 274 | } 275 | 276 | return bar; 277 | } 278 | 279 | /** 280 | * Centers text within a specified width using spaces for padding 281 | * 282 | * Uses string-width to handle Unicode characters and ANSI escape codes properly. 283 | * If text is longer than width, returns original text without truncation. 284 | * 285 | * Example: centerText("Hello", 10) -> " Hello " 286 | * 287 | * @param text - Text to center (may contain ANSI color codes) 288 | * @param width - Total character width including padding 289 | * @returns Text with spaces added for centering 290 | */ 291 | export function centerText(text: string, width: number): string { 292 | const textLength = stringWidth(text); 293 | if (textLength >= width) { 294 | return text; 295 | } 296 | 297 | const leftPadding = Math.floor((width - textLength) / 2); 298 | const rightPadding = width - textLength - leftPadding; 299 | 300 | return ' '.repeat(leftPadding) + text + ' '.repeat(rightPadding); 301 | } 302 | -------------------------------------------------------------------------------- /src/_token-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Token calculation utilities 3 | * 4 | * This module provides shared utilities for calculating token totals 5 | * across different token types. Used throughout the application to 6 | * ensure consistent token counting logic. 7 | */ 8 | 9 | /** 10 | * Token counts structure for raw usage data (uses InputTokens suffix) 11 | */ 12 | export type TokenCounts = { 13 | inputTokens: number; 14 | outputTokens: number; 15 | cacheCreationInputTokens: number; 16 | cacheReadInputTokens: number; 17 | }; 18 | 19 | /** 20 | * Token counts structure for aggregated data (uses shorter names) 21 | */ 22 | export type AggregatedTokenCounts = { 23 | inputTokens: number; 24 | outputTokens: number; 25 | cacheCreationTokens: number; 26 | cacheReadTokens: number; 27 | }; 28 | 29 | /** 30 | * Union type that supports both token count formats 31 | */ 32 | export type AnyTokenCounts = TokenCounts | AggregatedTokenCounts; 33 | 34 | /** 35 | * Calculates the total number of tokens across all token types 36 | * Supports both raw usage data format and aggregated data format 37 | * @param tokenCounts - Object containing counts for each token type 38 | * @returns Total number of tokens 39 | */ 40 | export function getTotalTokens(tokenCounts: AnyTokenCounts): number { 41 | // Support both property naming conventions 42 | const cacheCreation = 'cacheCreationInputTokens' in tokenCounts 43 | ? tokenCounts.cacheCreationInputTokens 44 | : (tokenCounts).cacheCreationTokens; 45 | 46 | const cacheRead = 'cacheReadInputTokens' in tokenCounts 47 | ? tokenCounts.cacheReadInputTokens 48 | : (tokenCounts).cacheReadTokens; 49 | 50 | return ( 51 | tokenCounts.inputTokens 52 | + tokenCounts.outputTokens 53 | + cacheCreation 54 | + cacheRead 55 | ); 56 | } 57 | 58 | // In-source testing 59 | if (import.meta.vitest != null) { 60 | describe('getTotalTokens', () => { 61 | it('should sum all token types correctly (raw format)', () => { 62 | const tokens: TokenCounts = { 63 | inputTokens: 1000, 64 | outputTokens: 500, 65 | cacheCreationInputTokens: 2000, 66 | cacheReadInputTokens: 300, 67 | }; 68 | expect(getTotalTokens(tokens)).toBe(3800); 69 | }); 70 | 71 | it('should sum all token types correctly (aggregated format)', () => { 72 | const tokens: AggregatedTokenCounts = { 73 | inputTokens: 1000, 74 | outputTokens: 500, 75 | cacheCreationTokens: 2000, 76 | cacheReadTokens: 300, 77 | }; 78 | expect(getTotalTokens(tokens)).toBe(3800); 79 | }); 80 | 81 | it('should handle zero values (raw format)', () => { 82 | const tokens: TokenCounts = { 83 | inputTokens: 0, 84 | outputTokens: 0, 85 | cacheCreationInputTokens: 0, 86 | cacheReadInputTokens: 0, 87 | }; 88 | expect(getTotalTokens(tokens)).toBe(0); 89 | }); 90 | 91 | it('should handle zero values (aggregated format)', () => { 92 | const tokens: AggregatedTokenCounts = { 93 | inputTokens: 0, 94 | outputTokens: 0, 95 | cacheCreationTokens: 0, 96 | cacheReadTokens: 0, 97 | }; 98 | expect(getTotalTokens(tokens)).toBe(0); 99 | }); 100 | 101 | it('should handle missing cache tokens (raw format)', () => { 102 | const tokens: TokenCounts = { 103 | inputTokens: 1000, 104 | outputTokens: 500, 105 | cacheCreationInputTokens: 0, 106 | cacheReadInputTokens: 0, 107 | }; 108 | expect(getTotalTokens(tokens)).toBe(1500); 109 | }); 110 | 111 | it('should handle missing cache tokens (aggregated format)', () => { 112 | const tokens: AggregatedTokenCounts = { 113 | inputTokens: 1000, 114 | outputTokens: 500, 115 | cacheCreationTokens: 0, 116 | cacheReadTokens: 0, 117 | }; 118 | expect(getTotalTokens(tokens)).toBe(1500); 119 | }); 120 | }); 121 | } 122 | -------------------------------------------------------------------------------- /src/_types.ts: -------------------------------------------------------------------------------- 1 | import type { TupleToUnion } from 'type-fest'; 2 | import { z } from 'zod'; 3 | 4 | /** 5 | * Branded Zod schemas for type safety using Zod's built-in brand functionality 6 | */ 7 | 8 | // Core identifier schemas 9 | export const modelNameSchema = z.string() 10 | .min(1, 'Model name cannot be empty') 11 | .brand<'ModelName'>(); 12 | 13 | export const sessionIdSchema = z.string() 14 | .min(1, 'Session ID cannot be empty') 15 | .brand<'SessionId'>(); 16 | 17 | export const requestIdSchema = z.string() 18 | .min(1, 'Request ID cannot be empty') 19 | .brand<'RequestId'>(); 20 | 21 | export const messageIdSchema = z.string() 22 | .min(1, 'Message ID cannot be empty') 23 | .brand<'MessageId'>(); 24 | 25 | // Date and timestamp schemas 26 | export const isoTimestampSchema = z.string() 27 | .regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/, 'Invalid ISO timestamp') 28 | .brand<'ISOTimestamp'>(); 29 | 30 | export const dailyDateSchema = z.string() 31 | .regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in YYYY-MM-DD format') 32 | .brand<'DailyDate'>(); 33 | 34 | export const activityDateSchema = z.string() 35 | .regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in YYYY-MM-DD format') 36 | .brand<'ActivityDate'>(); 37 | 38 | export const monthlyDateSchema = z.string() 39 | .regex(/^\d{4}-\d{2}$/, 'Date must be in YYYY-MM format') 40 | .brand<'MonthlyDate'>(); 41 | 42 | export const filterDateSchema = z.string() 43 | .regex(/^\d{8}$/, 'Date must be in YYYYMMDD format') 44 | .brand<'FilterDate'>(); 45 | 46 | // Other domain-specific schemas 47 | export const projectPathSchema = z.string() 48 | .min(1, 'Project path cannot be empty') 49 | .brand<'ProjectPath'>(); 50 | 51 | export const versionSchema = z.string() 52 | .regex(/^\d+\.\d+\.\d+/, 'Invalid version format') 53 | .brand<'Version'>(); 54 | 55 | /** 56 | * Inferred branded types from schemas 57 | */ 58 | export type ModelName = z.infer<typeof modelNameSchema>; 59 | export type SessionId = z.infer<typeof sessionIdSchema>; 60 | export type RequestId = z.infer<typeof requestIdSchema>; 61 | export type MessageId = z.infer<typeof messageIdSchema>; 62 | export type ISOTimestamp = z.infer<typeof isoTimestampSchema>; 63 | export type DailyDate = z.infer<typeof dailyDateSchema>; 64 | export type ActivityDate = z.infer<typeof activityDateSchema>; 65 | export type MonthlyDate = z.infer<typeof monthlyDateSchema>; 66 | export type FilterDate = z.infer<typeof filterDateSchema>; 67 | export type ProjectPath = z.infer<typeof projectPathSchema>; 68 | export type Version = z.infer<typeof versionSchema>; 69 | 70 | /** 71 | * Helper functions to create branded values by parsing and validating input strings 72 | * These functions should be used when converting plain strings to branded types 73 | */ 74 | export const createModelName = (value: string): ModelName => modelNameSchema.parse(value); 75 | export const createSessionId = (value: string): SessionId => sessionIdSchema.parse(value); 76 | export const createRequestId = (value: string): RequestId => requestIdSchema.parse(value); 77 | export const createMessageId = (value: string): MessageId => messageIdSchema.parse(value); 78 | export const createISOTimestamp = (value: string): ISOTimestamp => isoTimestampSchema.parse(value); 79 | export const createDailyDate = (value: string): DailyDate => dailyDateSchema.parse(value); 80 | export const createActivityDate = (value: string): ActivityDate => activityDateSchema.parse(value); 81 | export const createMonthlyDate = (value: string): MonthlyDate => monthlyDateSchema.parse(value); 82 | export const createFilterDate = (value: string): FilterDate => filterDateSchema.parse(value); 83 | export const createProjectPath = (value: string): ProjectPath => projectPathSchema.parse(value); 84 | export const createVersion = (value: string): Version => versionSchema.parse(value); 85 | 86 | /** 87 | * Available cost calculation modes 88 | * - auto: Use pre-calculated costs when available, otherwise calculate from tokens 89 | * - calculate: Always calculate costs from token counts using model pricing 90 | * - display: Always use pre-calculated costs, show 0 for missing costs 91 | */ 92 | export const CostModes = ['auto', 'calculate', 'display'] as const; 93 | 94 | /** 95 | * Union type for cost calculation modes 96 | */ 97 | export type CostMode = TupleToUnion<typeof CostModes>; 98 | 99 | /** 100 | * Available sort orders for data presentation 101 | */ 102 | export const SortOrders = ['desc', 'asc'] as const; 103 | 104 | /** 105 | * Union type for sort order options 106 | */ 107 | export type SortOrder = TupleToUnion<typeof SortOrders>; 108 | 109 | /** 110 | * Zod schema for model pricing information from LiteLLM 111 | */ 112 | export const modelPricingSchema = z.object({ 113 | input_cost_per_token: z.number().optional(), 114 | output_cost_per_token: z.number().optional(), 115 | cache_creation_input_token_cost: z.number().optional(), 116 | cache_read_input_token_cost: z.number().optional(), 117 | }); 118 | 119 | /** 120 | * Type definition for model pricing information 121 | */ 122 | export type ModelPricing = z.infer<typeof modelPricingSchema>; 123 | -------------------------------------------------------------------------------- /src/calculate-cost.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Cost calculation utilities for usage data analysis 3 | * 4 | * This module provides functions for calculating costs and aggregating token usage 5 | * across different time periods and models. It handles both pre-calculated costs 6 | * and dynamic cost calculations based on model pricing. 7 | * 8 | * @module calculate-cost 9 | */ 10 | 11 | import type { AggregatedTokenCounts } from './_token-utils.ts'; 12 | import type { DailyUsage, MonthlyUsage, SessionUsage } from './data-loader.ts'; 13 | import { getTotalTokens } from './_token-utils.ts'; 14 | import { 15 | createActivityDate, 16 | createDailyDate, 17 | createModelName, 18 | createProjectPath, 19 | createSessionId, 20 | createVersion, 21 | } from './_types.ts'; 22 | 23 | /** 24 | * Alias for AggregatedTokenCounts from shared utilities 25 | * @deprecated Use AggregatedTokenCounts from _token-utils.ts instead 26 | */ 27 | type TokenData = AggregatedTokenCounts; 28 | 29 | /** 30 | * Token totals including cost information 31 | */ 32 | type TokenTotals = TokenData & { 33 | totalCost: number; 34 | }; 35 | 36 | /** 37 | * Complete totals object with token counts, cost, and total token sum 38 | */ 39 | type TotalsObject = TokenTotals & { 40 | totalTokens: number; 41 | }; 42 | 43 | /** 44 | * Calculates total token usage and cost across multiple usage data entries 45 | * @param data - Array of daily, monthly, or session usage data 46 | * @returns Aggregated token totals and cost 47 | */ 48 | export function calculateTotals( 49 | data: Array<DailyUsage | MonthlyUsage | SessionUsage>, 50 | ): TokenTotals { 51 | return data.reduce( 52 | (acc, item) => ({ 53 | inputTokens: acc.inputTokens + item.inputTokens, 54 | outputTokens: acc.outputTokens + item.outputTokens, 55 | cacheCreationTokens: acc.cacheCreationTokens + item.cacheCreationTokens, 56 | cacheReadTokens: acc.cacheReadTokens + item.cacheReadTokens, 57 | totalCost: acc.totalCost + item.totalCost, 58 | }), 59 | { 60 | inputTokens: 0, 61 | outputTokens: 0, 62 | cacheCreationTokens: 0, 63 | cacheReadTokens: 0, 64 | totalCost: 0, 65 | }, 66 | ); 67 | } 68 | 69 | // Re-export getTotalTokens from shared utilities for backward compatibility 70 | export { getTotalTokens }; 71 | 72 | /** 73 | * Creates a complete totals object by adding total token count to existing totals 74 | * @param totals - Token totals with cost information 75 | * @returns Complete totals object including total token sum 76 | */ 77 | export function createTotalsObject(totals: TokenTotals): TotalsObject { 78 | return { 79 | ...totals, 80 | totalTokens: getTotalTokens(totals), 81 | }; 82 | } 83 | 84 | if (import.meta.vitest != null) { 85 | describe('token aggregation utilities', () => { 86 | it('calculateTotals should aggregate daily usage data', () => { 87 | const dailyData: DailyUsage[] = [ 88 | { 89 | date: createDailyDate('2024-01-01'), 90 | inputTokens: 100, 91 | outputTokens: 50, 92 | cacheCreationTokens: 25, 93 | cacheReadTokens: 10, 94 | totalCost: 0.01, 95 | modelsUsed: [createModelName('claude-sonnet-4-20250514')], 96 | modelBreakdowns: [], 97 | }, 98 | { 99 | date: createDailyDate('2024-01-02'), 100 | inputTokens: 200, 101 | outputTokens: 100, 102 | cacheCreationTokens: 50, 103 | cacheReadTokens: 20, 104 | totalCost: 0.02, 105 | modelsUsed: [createModelName('claude-opus-4-20250514')], 106 | modelBreakdowns: [], 107 | }, 108 | ]; 109 | 110 | const totals = calculateTotals(dailyData); 111 | expect(totals.inputTokens).toBe(300); 112 | expect(totals.outputTokens).toBe(150); 113 | expect(totals.cacheCreationTokens).toBe(75); 114 | expect(totals.cacheReadTokens).toBe(30); 115 | expect(totals.totalCost).toBeCloseTo(0.03); 116 | }); 117 | 118 | it('calculateTotals should aggregate session usage data', () => { 119 | const sessionData: SessionUsage[] = [ 120 | { 121 | sessionId: createSessionId('session-1'), 122 | projectPath: createProjectPath('project/path'), 123 | inputTokens: 100, 124 | outputTokens: 50, 125 | cacheCreationTokens: 25, 126 | cacheReadTokens: 10, 127 | totalCost: 0.01, 128 | lastActivity: createActivityDate('2024-01-01'), 129 | versions: [createVersion('1.0.3')], 130 | modelsUsed: [createModelName('claude-sonnet-4-20250514')], 131 | modelBreakdowns: [], 132 | }, 133 | { 134 | sessionId: createSessionId('session-2'), 135 | projectPath: createProjectPath('project/path'), 136 | inputTokens: 200, 137 | outputTokens: 100, 138 | cacheCreationTokens: 50, 139 | cacheReadTokens: 20, 140 | totalCost: 0.02, 141 | lastActivity: createActivityDate('2024-01-02'), 142 | versions: [createVersion('1.0.3'), createVersion('1.0.4')], 143 | modelsUsed: [createModelName('claude-opus-4-20250514')], 144 | modelBreakdowns: [], 145 | }, 146 | ]; 147 | 148 | const totals = calculateTotals(sessionData); 149 | expect(totals.inputTokens).toBe(300); 150 | expect(totals.outputTokens).toBe(150); 151 | expect(totals.cacheCreationTokens).toBe(75); 152 | expect(totals.cacheReadTokens).toBe(30); 153 | expect(totals.totalCost).toBeCloseTo(0.03); 154 | }); 155 | 156 | it('getTotalTokens should sum all token types', () => { 157 | const tokens = { 158 | inputTokens: 100, 159 | outputTokens: 50, 160 | cacheCreationTokens: 25, 161 | cacheReadTokens: 10, 162 | }; 163 | 164 | const total = getTotalTokens(tokens); 165 | expect(total).toBe(185); 166 | }); 167 | 168 | it('getTotalTokens should handle zero values', () => { 169 | const tokens = { 170 | inputTokens: 0, 171 | outputTokens: 0, 172 | cacheCreationTokens: 0, 173 | cacheReadTokens: 0, 174 | }; 175 | 176 | const total = getTotalTokens(tokens); 177 | expect(total).toBe(0); 178 | }); 179 | 180 | it('createTotalsObject should create complete totals object', () => { 181 | const totals = { 182 | inputTokens: 100, 183 | outputTokens: 50, 184 | cacheCreationTokens: 25, 185 | cacheReadTokens: 10, 186 | totalCost: 0.01, 187 | }; 188 | 189 | const totalsObject = createTotalsObject(totals); 190 | expect(totalsObject).toEqual({ 191 | inputTokens: 100, 192 | outputTokens: 50, 193 | cacheCreationTokens: 25, 194 | cacheReadTokens: 10, 195 | totalTokens: 185, 196 | totalCost: 0.01, 197 | }); 198 | }); 199 | 200 | it('calculateTotals should handle empty array', () => { 201 | const totals = calculateTotals([]); 202 | expect(totals).toEqual({ 203 | inputTokens: 0, 204 | outputTokens: 0, 205 | cacheCreationTokens: 0, 206 | cacheReadTokens: 0, 207 | totalCost: 0, 208 | }); 209 | }); 210 | }); 211 | } 212 | -------------------------------------------------------------------------------- /src/commands/_blocks.live.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Live monitoring command orchestration 3 | * 4 | * This module provides the command-line interface for live monitoring, 5 | * handling process lifecycle, signal management, and terminal setup. 6 | * The actual rendering logic is handled by the _live-rendering module. 7 | */ 8 | 9 | import type { LiveMonitoringConfig } from '../_live-rendering.ts'; 10 | import process from 'node:process'; 11 | import { Result } from '@praha/byethrow'; 12 | import pc from 'picocolors'; 13 | import { MIN_RENDER_INTERVAL_MS } from '../_consts.ts'; 14 | import { LiveMonitor } from '../_live-monitor.ts'; 15 | import { 16 | delayWithAbort, 17 | renderActiveBlock, 18 | renderWaitingState, 19 | } from '../_live-rendering.ts'; 20 | import { TerminalManager } from '../_terminal-utils.ts'; 21 | import { logger } from '../logger.ts'; 22 | 23 | export async function startLiveMonitoring(config: LiveMonitoringConfig): Promise<void> { 24 | const terminal = new TerminalManager(); 25 | const abortController = new AbortController(); 26 | let lastRenderTime = 0; 27 | 28 | // Setup graceful shutdown 29 | const cleanup = (): void => { 30 | abortController.abort(); 31 | terminal.cleanup(); 32 | terminal.clearScreen(); 33 | logger.info('Live monitoring stopped.'); 34 | if (process.exitCode == null) { 35 | process.exit(0); 36 | } 37 | }; 38 | 39 | process.on('SIGINT', cleanup); 40 | process.on('SIGTERM', cleanup); 41 | 42 | // Setup terminal for optimal TUI performance 43 | terminal.enterAlternateScreen(); 44 | terminal.enableSyncMode(); 45 | terminal.clearScreen(); 46 | terminal.hideCursor(); 47 | 48 | // Create live monitor with efficient data loading 49 | using monitor = new LiveMonitor({ 50 | claudePath: config.claudePath, 51 | sessionDurationHours: config.sessionDurationHours, 52 | mode: config.mode, 53 | order: config.order, 54 | }); 55 | 56 | const monitoringResult = await Result.try({ 57 | try: async () => { 58 | while (!abortController.signal.aborted) { 59 | const now = Date.now(); 60 | const timeSinceLastRender = now - lastRenderTime; 61 | 62 | // Skip render if too soon (frame rate limiting) 63 | if (timeSinceLastRender < MIN_RENDER_INTERVAL_MS) { 64 | await delayWithAbort(MIN_RENDER_INTERVAL_MS - timeSinceLastRender, abortController.signal); 65 | continue; 66 | } 67 | 68 | // Get latest data 69 | const activeBlock = await monitor.getActiveBlock(); 70 | monitor.clearCache(); // TODO: debug LiveMonitor.getActiveBlock() efficiency 71 | 72 | if (activeBlock == null) { 73 | await renderWaitingState(terminal, config, abortController.signal); 74 | continue; 75 | } 76 | 77 | // Render active block 78 | renderActiveBlock(terminal, activeBlock, config); 79 | lastRenderTime = Date.now(); 80 | 81 | // Wait before next refresh 82 | await delayWithAbort(config.refreshInterval, abortController.signal); 83 | } 84 | }, 85 | catch: error => error, 86 | })(); 87 | 88 | if (Result.isFailure(monitoringResult)) { 89 | const error = monitoringResult.error; 90 | if ((error instanceof DOMException || error instanceof Error) && error.name === 'AbortError') { 91 | return; // Normal graceful shutdown 92 | } 93 | 94 | // Handle and display errors 95 | const errorMessage = error instanceof Error ? error.message : String(error); 96 | terminal.startBuffering(); 97 | terminal.clearScreen(); 98 | terminal.write(pc.red(`Error: ${errorMessage}\n`)); 99 | terminal.flush(); 100 | logger.error(`Live monitoring error: ${errorMessage}`); 101 | await delayWithAbort(config.refreshInterval, abortController.signal).catch(() => {}); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/commands/daily.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import { define } from 'gunshi'; 3 | import pc from 'picocolors'; 4 | import { sharedCommandConfig } from '../_shared-args.ts'; 5 | import { formatCurrency, formatModelsDisplayMultiline, formatNumber, pushBreakdownRows, ResponsiveTable } from '../_utils.ts'; 6 | import { 7 | calculateTotals, 8 | createTotalsObject, 9 | getTotalTokens, 10 | } from '../calculate-cost.ts'; 11 | import { formatDateCompact, loadDailyUsageData } from '../data-loader.ts'; 12 | import { detectMismatches, printMismatchReport } from '../debug.ts'; 13 | import { log, logger } from '../logger.ts'; 14 | 15 | export const dailyCommand = define({ 16 | name: 'daily', 17 | description: 'Show usage report grouped by date', 18 | ...sharedCommandConfig, 19 | async run(ctx) { 20 | if (ctx.values.json) { 21 | logger.level = 0; 22 | } 23 | 24 | const dailyData = await loadDailyUsageData({ 25 | since: ctx.values.since, 26 | until: ctx.values.until, 27 | mode: ctx.values.mode, 28 | order: ctx.values.order, 29 | offline: ctx.values.offline, 30 | }); 31 | 32 | if (dailyData.length === 0) { 33 | if (ctx.values.json) { 34 | log(JSON.stringify([])); 35 | } 36 | else { 37 | logger.warn('No Claude usage data found.'); 38 | } 39 | process.exit(0); 40 | } 41 | 42 | // Calculate totals 43 | const totals = calculateTotals(dailyData); 44 | 45 | // Show debug information if requested 46 | if (ctx.values.debug && !ctx.values.json) { 47 | const mismatchStats = await detectMismatches(undefined); 48 | printMismatchReport(mismatchStats, ctx.values.debugSamples); 49 | } 50 | 51 | if (ctx.values.json) { 52 | // Output JSON format 53 | const jsonOutput = { 54 | daily: dailyData.map(data => ({ 55 | date: data.date, 56 | inputTokens: data.inputTokens, 57 | outputTokens: data.outputTokens, 58 | cacheCreationTokens: data.cacheCreationTokens, 59 | cacheReadTokens: data.cacheReadTokens, 60 | totalTokens: getTotalTokens(data), 61 | totalCost: data.totalCost, 62 | modelsUsed: data.modelsUsed, 63 | modelBreakdowns: data.modelBreakdowns, 64 | })), 65 | totals: createTotalsObject(totals), 66 | }; 67 | log(JSON.stringify(jsonOutput, null, 2)); 68 | } 69 | else { 70 | // Print header 71 | logger.box('Claude Code Token Usage Report - Daily'); 72 | 73 | // Create table with compact mode support 74 | const table = new ResponsiveTable({ 75 | head: [ 76 | 'Date', 77 | 'Models', 78 | 'Input', 79 | 'Output', 80 | 'Cache Create', 81 | 'Cache Read', 82 | 'Total Tokens', 83 | 'Cost (USD)', 84 | ], 85 | style: { 86 | head: ['cyan'], 87 | }, 88 | colAligns: [ 89 | 'left', 90 | 'left', 91 | 'right', 92 | 'right', 93 | 'right', 94 | 'right', 95 | 'right', 96 | 'right', 97 | ], 98 | dateFormatter: formatDateCompact, 99 | compactHead: [ 100 | 'Date', 101 | 'Models', 102 | 'Input', 103 | 'Output', 104 | 'Cost (USD)', 105 | ], 106 | compactColAligns: [ 107 | 'left', 108 | 'left', 109 | 'right', 110 | 'right', 111 | 'right', 112 | ], 113 | compactThreshold: 100, 114 | }); 115 | 116 | // Add daily data 117 | for (const data of dailyData) { 118 | // Main row 119 | table.push([ 120 | data.date, 121 | formatModelsDisplayMultiline(data.modelsUsed), 122 | formatNumber(data.inputTokens), 123 | formatNumber(data.outputTokens), 124 | formatNumber(data.cacheCreationTokens), 125 | formatNumber(data.cacheReadTokens), 126 | formatNumber(getTotalTokens(data)), 127 | formatCurrency(data.totalCost), 128 | ]); 129 | 130 | // Add model breakdown rows if flag is set 131 | if (ctx.values.breakdown) { 132 | pushBreakdownRows(table, data.modelBreakdowns); 133 | } 134 | } 135 | 136 | // Add empty row for visual separation before totals 137 | table.push([ 138 | '', 139 | '', 140 | '', 141 | '', 142 | '', 143 | '', 144 | '', 145 | '', 146 | ]); 147 | 148 | // Add totals 149 | table.push([ 150 | pc.yellow('Total'), 151 | '', // Empty for Models column in totals 152 | pc.yellow(formatNumber(totals.inputTokens)), 153 | pc.yellow(formatNumber(totals.outputTokens)), 154 | pc.yellow(formatNumber(totals.cacheCreationTokens)), 155 | pc.yellow(formatNumber(totals.cacheReadTokens)), 156 | pc.yellow(formatNumber(getTotalTokens(totals))), 157 | pc.yellow(formatCurrency(totals.totalCost)), 158 | ]); 159 | 160 | log(table.toString()); 161 | 162 | // Show guidance message if in compact mode 163 | if (table.isCompactMode()) { 164 | logger.info('\nRunning in Compact Mode'); 165 | logger.info('Expand terminal width to see cache metrics and total tokens'); 166 | } 167 | } 168 | }, 169 | }); 170 | -------------------------------------------------------------------------------- /src/commands/index.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import { cli } from 'gunshi'; 3 | import { description, name, version } from '../../package.json'; 4 | import { blocksCommand } from './blocks.ts'; 5 | import { dailyCommand } from './daily.ts'; 6 | import { mcpCommand } from './mcp.ts'; 7 | import { monthlyCommand } from './monthly.ts'; 8 | import { sessionCommand } from './session.ts'; 9 | 10 | /** 11 | * Map of available CLI subcommands 12 | */ 13 | const subCommands = new Map(); 14 | subCommands.set('daily', dailyCommand); 15 | subCommands.set('monthly', monthlyCommand); 16 | subCommands.set('session', sessionCommand); 17 | subCommands.set('blocks', blocksCommand); 18 | subCommands.set('mcp', mcpCommand); 19 | 20 | /** 21 | * Default command when no subcommand is specified (defaults to daily) 22 | */ 23 | const mainCommand = dailyCommand; 24 | 25 | // eslint-disable-next-line antfu/no-top-level-await 26 | await cli(process.argv.slice(2), mainCommand, { 27 | name, 28 | version, 29 | description, 30 | subCommands, 31 | renderHeader: null, 32 | }); 33 | -------------------------------------------------------------------------------- /src/commands/mcp.ts: -------------------------------------------------------------------------------- 1 | import { serve } from '@hono/node-server'; 2 | import { define } from 'gunshi'; 3 | import { MCP_DEFAULT_PORT } from '../_consts.ts'; 4 | import { sharedArgs } from '../_shared-args.ts'; 5 | import { getClaudePaths } from '../data-loader.ts'; 6 | import { logger } from '../logger.ts'; 7 | import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from '../mcp.ts'; 8 | 9 | /** 10 | * MCP server command that supports both stdio and HTTP transports. 11 | * Allows starting an MCP server for external integrations with usage reporting tools. 12 | */ 13 | export const mcpCommand = define({ 14 | name: 'mcp', 15 | description: 'Start MCP server with usage reporting tools', 16 | args: { 17 | mode: sharedArgs.mode, 18 | type: { 19 | type: 'enum', 20 | short: 't', 21 | description: 'Transport type for MCP server', 22 | choices: ['stdio', 'http'] as const, 23 | default: 'stdio', 24 | }, 25 | port: { 26 | type: 'number', 27 | description: `Port for HTTP transport (default: ${MCP_DEFAULT_PORT})`, 28 | default: MCP_DEFAULT_PORT, 29 | }, 30 | }, 31 | async run(ctx) { 32 | const { type, mode, port } = ctx.values; 33 | // disable info logging for stdio 34 | if (type === 'stdio') { 35 | logger.level = 0; 36 | } 37 | 38 | const paths = getClaudePaths(); 39 | if (paths.length === 0) { 40 | logger.error('No valid Claude data directory found'); 41 | throw new Error('No valid Claude data directory found'); 42 | } 43 | 44 | const options = { 45 | claudePath: paths[0]!, 46 | mode, 47 | }; 48 | 49 | if (type === 'stdio') { 50 | const server = createMcpServer(options); 51 | await startMcpServerStdio(server); 52 | } 53 | else { 54 | const app = createMcpHttpApp(options); 55 | // Use the Hono app to handle requests 56 | serve({ 57 | fetch: app.fetch, 58 | port, 59 | }); 60 | logger.info(`MCP server is running on http://localhost:${port}`); 61 | } 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /src/commands/monthly.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import { define } from 'gunshi'; 3 | import pc from 'picocolors'; 4 | import { sharedCommandConfig } from '../_shared-args.ts'; 5 | import { formatCurrency, formatModelsDisplayMultiline, formatNumber, pushBreakdownRows, ResponsiveTable } from '../_utils.ts'; 6 | import { 7 | calculateTotals, 8 | createTotalsObject, 9 | getTotalTokens, 10 | } from '../calculate-cost.ts'; 11 | import { formatDateCompact, loadMonthlyUsageData } from '../data-loader.ts'; 12 | import { detectMismatches, printMismatchReport } from '../debug.ts'; 13 | import { log, logger } from '../logger.ts'; 14 | 15 | export const monthlyCommand = define({ 16 | name: 'monthly', 17 | description: 'Show usage report grouped by month', 18 | ...sharedCommandConfig, 19 | async run(ctx) { 20 | if (ctx.values.json) { 21 | logger.level = 0; 22 | } 23 | 24 | const monthlyData = await loadMonthlyUsageData({ 25 | since: ctx.values.since, 26 | until: ctx.values.until, 27 | mode: ctx.values.mode, 28 | order: ctx.values.order, 29 | offline: ctx.values.offline, 30 | }); 31 | 32 | if (monthlyData.length === 0) { 33 | if (ctx.values.json) { 34 | const emptyOutput = { 35 | monthly: [], 36 | totals: { 37 | inputTokens: 0, 38 | outputTokens: 0, 39 | cacheCreationTokens: 0, 40 | cacheReadTokens: 0, 41 | totalTokens: 0, 42 | totalCost: 0, 43 | }, 44 | }; 45 | log(JSON.stringify(emptyOutput, null, 2)); 46 | } 47 | else { 48 | logger.warn('No Claude usage data found.'); 49 | } 50 | process.exit(0); 51 | } 52 | 53 | // Calculate totals 54 | const totals = calculateTotals(monthlyData); 55 | 56 | // Show debug information if requested 57 | if (ctx.values.debug && !ctx.values.json) { 58 | const mismatchStats = await detectMismatches(undefined); 59 | printMismatchReport(mismatchStats, ctx.values.debugSamples); 60 | } 61 | 62 | if (ctx.values.json) { 63 | // Output JSON format 64 | const jsonOutput = { 65 | monthly: monthlyData.map(data => ({ 66 | month: data.month, 67 | inputTokens: data.inputTokens, 68 | outputTokens: data.outputTokens, 69 | cacheCreationTokens: data.cacheCreationTokens, 70 | cacheReadTokens: data.cacheReadTokens, 71 | totalTokens: getTotalTokens(data), 72 | totalCost: data.totalCost, 73 | modelsUsed: data.modelsUsed, 74 | modelBreakdowns: data.modelBreakdowns, 75 | })), 76 | totals: createTotalsObject(totals), 77 | }; 78 | log(JSON.stringify(jsonOutput, null, 2)); 79 | } 80 | else { 81 | // Print header 82 | logger.box('Claude Code Token Usage Report - Monthly'); 83 | 84 | // Create table with compact mode support 85 | const table = new ResponsiveTable({ 86 | head: [ 87 | 'Month', 88 | 'Models', 89 | 'Input', 90 | 'Output', 91 | 'Cache Create', 92 | 'Cache Read', 93 | 'Total Tokens', 94 | 'Cost (USD)', 95 | ], 96 | style: { 97 | head: ['cyan'], 98 | }, 99 | colAligns: [ 100 | 'left', 101 | 'left', 102 | 'right', 103 | 'right', 104 | 'right', 105 | 'right', 106 | 'right', 107 | 'right', 108 | ], 109 | dateFormatter: formatDateCompact, 110 | compactHead: [ 111 | 'Month', 112 | 'Models', 113 | 'Input', 114 | 'Output', 115 | 'Cost (USD)', 116 | ], 117 | compactColAligns: [ 118 | 'left', 119 | 'left', 120 | 'right', 121 | 'right', 122 | 'right', 123 | ], 124 | compactThreshold: 100, 125 | }); 126 | 127 | // Add monthly data 128 | for (const data of monthlyData) { 129 | // Main row 130 | table.push([ 131 | data.month, 132 | formatModelsDisplayMultiline(data.modelsUsed), 133 | formatNumber(data.inputTokens), 134 | formatNumber(data.outputTokens), 135 | formatNumber(data.cacheCreationTokens), 136 | formatNumber(data.cacheReadTokens), 137 | formatNumber(getTotalTokens(data)), 138 | formatCurrency(data.totalCost), 139 | ]); 140 | 141 | // Add model breakdown rows if flag is set 142 | if (ctx.values.breakdown) { 143 | pushBreakdownRows(table, data.modelBreakdowns); 144 | } 145 | } 146 | 147 | // Add empty row for visual separation before totals 148 | table.push([ 149 | '', 150 | '', 151 | '', 152 | '', 153 | '', 154 | '', 155 | '', 156 | '', 157 | ]); 158 | 159 | // Add totals 160 | table.push([ 161 | pc.yellow('Total'), 162 | '', // Empty for Models column in totals 163 | pc.yellow(formatNumber(totals.inputTokens)), 164 | pc.yellow(formatNumber(totals.outputTokens)), 165 | pc.yellow(formatNumber(totals.cacheCreationTokens)), 166 | pc.yellow(formatNumber(totals.cacheReadTokens)), 167 | pc.yellow(formatNumber(getTotalTokens(totals))), 168 | pc.yellow(formatCurrency(totals.totalCost)), 169 | ]); 170 | 171 | log(table.toString()); 172 | 173 | // Show guidance message if in compact mode 174 | if (table.isCompactMode()) { 175 | logger.info('\nRunning in Compact Mode'); 176 | logger.info('Expand terminal width to see cache metrics and total tokens'); 177 | } 178 | } 179 | }, 180 | }); 181 | -------------------------------------------------------------------------------- /src/commands/session.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import { define } from 'gunshi'; 3 | import pc from 'picocolors'; 4 | import { sharedCommandConfig } from '../_shared-args.ts'; 5 | import { formatCurrency, formatModelsDisplayMultiline, formatNumber, pushBreakdownRows, ResponsiveTable } from '../_utils.ts'; 6 | import { 7 | calculateTotals, 8 | createTotalsObject, 9 | getTotalTokens, 10 | } from '../calculate-cost.ts'; 11 | import { formatDateCompact, loadSessionData } from '../data-loader.ts'; 12 | import { detectMismatches, printMismatchReport } from '../debug.ts'; 13 | import { log, logger } from '../logger.ts'; 14 | 15 | export const sessionCommand = define({ 16 | name: 'session', 17 | description: 'Show usage report grouped by conversation session', 18 | ...sharedCommandConfig, 19 | async run(ctx) { 20 | if (ctx.values.json) { 21 | logger.level = 0; 22 | } 23 | 24 | const sessionData = await loadSessionData({ 25 | since: ctx.values.since, 26 | until: ctx.values.until, 27 | mode: ctx.values.mode, 28 | order: ctx.values.order, 29 | offline: ctx.values.offline, 30 | }); 31 | 32 | if (sessionData.length === 0) { 33 | if (ctx.values.json) { 34 | log(JSON.stringify([])); 35 | } 36 | else { 37 | logger.warn('No Claude usage data found.'); 38 | } 39 | process.exit(0); 40 | } 41 | 42 | // Calculate totals 43 | const totals = calculateTotals(sessionData); 44 | 45 | // Show debug information if requested 46 | if (ctx.values.debug && !ctx.values.json) { 47 | const mismatchStats = await detectMismatches(undefined); 48 | printMismatchReport(mismatchStats, ctx.values.debugSamples); 49 | } 50 | 51 | if (ctx.values.json) { 52 | // Output JSON format 53 | const jsonOutput = { 54 | sessions: sessionData.map(data => ({ 55 | sessionId: data.sessionId, 56 | inputTokens: data.inputTokens, 57 | outputTokens: data.outputTokens, 58 | cacheCreationTokens: data.cacheCreationTokens, 59 | cacheReadTokens: data.cacheReadTokens, 60 | totalTokens: getTotalTokens(data), 61 | totalCost: data.totalCost, 62 | lastActivity: data.lastActivity, 63 | modelsUsed: data.modelsUsed, 64 | modelBreakdowns: data.modelBreakdowns, 65 | })), 66 | totals: createTotalsObject(totals), 67 | }; 68 | log(JSON.stringify(jsonOutput, null, 2)); 69 | } 70 | else { 71 | // Print header 72 | logger.box('Claude Code Token Usage Report - By Session'); 73 | 74 | // Create table with compact mode support 75 | const table = new ResponsiveTable({ 76 | head: [ 77 | 'Session', 78 | 'Models', 79 | 'Input', 80 | 'Output', 81 | 'Cache Create', 82 | 'Cache Read', 83 | 'Total Tokens', 84 | 'Cost (USD)', 85 | 'Last Activity', 86 | ], 87 | style: { 88 | head: ['cyan'], 89 | }, 90 | colAligns: [ 91 | 'left', 92 | 'left', 93 | 'right', 94 | 'right', 95 | 'right', 96 | 'right', 97 | 'right', 98 | 'right', 99 | 'left', 100 | ], 101 | dateFormatter: formatDateCompact, 102 | compactHead: [ 103 | 'Session', 104 | 'Models', 105 | 'Input', 106 | 'Output', 107 | 'Cost (USD)', 108 | 'Last Activity', 109 | ], 110 | compactColAligns: [ 111 | 'left', 112 | 'left', 113 | 'right', 114 | 'right', 115 | 'right', 116 | 'left', 117 | ], 118 | compactThreshold: 100, 119 | }); 120 | 121 | let maxSessionLength = 0; 122 | for (const data of sessionData) { 123 | const sessionDisplay = data.sessionId.split('-').slice(-2).join('-'); // Display last two parts of session ID 124 | 125 | maxSessionLength = Math.max(maxSessionLength, sessionDisplay.length); 126 | 127 | // Main row 128 | table.push([ 129 | sessionDisplay, 130 | formatModelsDisplayMultiline(data.modelsUsed), 131 | formatNumber(data.inputTokens), 132 | formatNumber(data.outputTokens), 133 | formatNumber(data.cacheCreationTokens), 134 | formatNumber(data.cacheReadTokens), 135 | formatNumber(getTotalTokens(data)), 136 | formatCurrency(data.totalCost), 137 | data.lastActivity, 138 | ]); 139 | 140 | // Add model breakdown rows if flag is set 141 | if (ctx.values.breakdown) { 142 | // Session has 1 extra column before data and 1 trailing column 143 | pushBreakdownRows(table, data.modelBreakdowns, 1, 1); 144 | } 145 | } 146 | 147 | // Add empty row for visual separation before totals 148 | table.push([ 149 | '', 150 | '', 151 | '', 152 | '', 153 | '', 154 | '', 155 | '', 156 | '', 157 | '', 158 | ]); 159 | 160 | // Add totals 161 | table.push([ 162 | pc.yellow('Total'), 163 | '', // Empty for Models column in totals 164 | pc.yellow(formatNumber(totals.inputTokens)), 165 | pc.yellow(formatNumber(totals.outputTokens)), 166 | pc.yellow(formatNumber(totals.cacheCreationTokens)), 167 | pc.yellow(formatNumber(totals.cacheReadTokens)), 168 | pc.yellow(formatNumber(getTotalTokens(totals))), 169 | pc.yellow(formatCurrency(totals.totalCost)), 170 | '', 171 | ]); 172 | 173 | log(table.toString()); 174 | 175 | // Show guidance message if in compact mode 176 | if (table.isCompactMode()) { 177 | logger.info('\nRunning in Compact Mode'); 178 | logger.info('Expand terminal width to see cache metrics and total tokens'); 179 | } 180 | } 181 | }, 182 | }); 183 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @fileoverview Main entry point for ccusage CLI tool 5 | * 6 | * This is the main entry point for the ccusage command-line interface tool. 7 | * It provides analysis of Claude Code usage data from local JSONL files. 8 | * 9 | * @module index 10 | */ 11 | 12 | import './commands/index.ts'; 13 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Logging utilities for the ccusage application 3 | * 4 | * This module provides configured logger instances using consola for consistent 5 | * logging throughout the application with package name tagging. 6 | * 7 | * @module logger 8 | */ 9 | 10 | import type { ConsolaInstance } from 'consola'; 11 | import { consola } from 'consola'; 12 | 13 | import { name } from '../package.json'; 14 | 15 | /** 16 | * Application logger instance with package name tag 17 | */ 18 | export const logger: ConsolaInstance = consola.withTag(name); 19 | 20 | /** 21 | * Direct console.log function for cases where logger formatting is not desired 22 | */ 23 | // eslint-disable-next-line no-console 24 | export const log = console.log; 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "react-jsx", 5 | // Environment setup & latest features 6 | "lib": [ 7 | "ESNext" 8 | ], 9 | "moduleDetection": "force", 10 | "module": "Preserve", 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "types": [ 15 | "vitest/globals", 16 | "vitest/importMeta" 17 | ], 18 | "allowImportingTsExtensions": true, 19 | "allowJs": true, 20 | // Best practices 21 | "strict": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noImplicitOverride": true, 24 | "noPropertyAccessFromIndexSignature": false, 25 | "noUncheckedIndexedAccess": true, 26 | // Some stricter flags (disabled by default) 27 | "noUnusedLocals": false, 28 | "noUnusedParameters": false, 29 | "noEmit": true, 30 | "verbatimModuleSyntax": true, 31 | "skipLibCheck": true 32 | }, 33 | "exclude": [ 34 | "dist" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsdown'; 2 | import Macros from 'unplugin-macros/rolldown'; 3 | 4 | export default defineConfig({ 5 | entry: [ 6 | './src/*.ts', 7 | '!./src/**/*.test.ts', // Exclude test files 8 | '!./src/_*.ts', // Exclude internal files with underscore prefix 9 | ], 10 | outDir: 'dist', 11 | format: 'esm', 12 | clean: true, 13 | sourcemap: false, 14 | minify: 'dce-only', 15 | treeshake: true, 16 | dts: { 17 | tsgo: true, 18 | resolve: ['type-fest'], 19 | }, 20 | publint: true, 21 | unused: true, 22 | exports: true, 23 | nodeProtocol: true, 24 | plugins: [ 25 | Macros({ 26 | include: ['src/index.ts', 'src/pricing-fetcher.ts'], 27 | }), 28 | ], 29 | define: { 30 | 'import.meta.vitest': 'undefined', 31 | }, 32 | onSuccess: 'sort-package-json', 33 | }); 34 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | locale = 'en-us' 3 | extend-ignore-re = [ 4 | "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on", 5 | "(?s)<!--\\s*spellchecker:off.*?\\n\\s*spellchecker:on\\s*-->", 6 | "(?Rm)^.*#\\s*spellchecker:disable-line
quot;,
 7 |   "(?m)^.*<!--\\s*spellchecker:disable-line\\s*-->\\n.*
quot;
 8 | ]
 9 | 
10 | [default.extend-words]
11 | color = "color"
12 | 
13 | [files]
14 | extend-exclude = [
15 |   "node_modules",
16 |   "biome.json"
17 | ]
18 | 


--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
 1 | import Macros from 'unplugin-macros/vite';
 2 | import { defineConfig } from 'vitest/config';
 3 | 
 4 | export default defineConfig({
 5 | 	test: {
 6 | 		includeSource: ['src/**/*.{js,ts}'],
 7 | 		globals: true,
 8 | 	},
 9 | 	plugins: [
10 | 		Macros({
11 | 			include: ['src/index.ts', 'src/pricing-fetcher.ts'],
12 | 		}),
13 | 	],
14 | });
15 | 


--------------------------------------------------------------------------------