Skip to content

feat(ccusage): add all command to show combined Claude Code and Codex usage#914

Open
qianhaoq wants to merge 7 commits intoryoppippi:mainfrom
qianhaoq:feat/all-command
Open

feat(ccusage): add all command to show combined Claude Code and Codex usage#914
qianhaoq wants to merge 7 commits intoryoppippi:mainfrom
qianhaoq:feat/all-command

Conversation

@qianhaoq
Copy link
Copy Markdown

@qianhaoq qianhaoq commented Mar 29, 2026

Summary

  • Adds a new ccusage all subcommand that shows daily token usage and cost for both Claude Code and OpenAI Codex in a single invocation, with a combined total cost line at the end
  • Adds exports fields to @ccusage/codex package so its internal modules can be imported by sibling packages in the monorepo
  • Adds @ccusage/codex as a devDependency of ccusage (bundled CLI — kept in devDeps per monorepo convention)

Motivation

Users who use both Claude Code and Codex CLI currently need to run two separate commands (ccusage and npx @ccusage/codex) to see their total AI spend. This PR enables a single ccusage all to show both reports and a combined cost.

Usage

ccusage all
ccusage all --since 20260301
ccusage all --json

Output:

  1. Claude Code daily usage table (existing format)
  2. Codex daily usage table (same format as @ccusage/codex)
  3. Combined Total Cost: $X.XX

Test plan

  • pnpm run typecheck passes (zero errors)
  • pnpm run test passes (252 tests)
  • pnpm run format passes (zero lint errors)
  • Manually ran ccusage all and verified both tables render correctly with combined total

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added "all" command to produce combined Claude Code + Codex daily usage reports (tables and JSON/jq), with ordering, breakdowns, and combined cost totals.
    • Daily report rows now include a stable dateKey for consistent date formatting.
  • Chores

    • Configuration schema extended with a strict commands.all section to validate allowed options.
    • Package exports surface updated and a development helper dependency added.
  • Tests

    • Unit tests for report dateKey, ordering, and combined-cost calculation.

…ex usage

Adds a new `ccusage all` subcommand that displays daily token usage and
cost for both Claude Code and OpenAI Codex in a single invocation, with
a combined total cost line at the end.

- Add `exports` fields to `@ccusage/codex` package.json so its internal
  modules (data-loader, daily-report, pricing, etc.) can be imported by
  sibling packages
- Add `@ccusage/codex` as a devDependency of `ccusage` (bundled CLI)
- Implement `apps/ccusage/src/commands/all.ts` reusing codex data-loader
  and report-builder alongside the existing Claude Code data-loader
- Register `all` in the subcommand map in `commands/index.ts`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee0ee9ac-0465-4d46-84f7-aef7930a452a

📥 Commits

Reviewing files that changed from the base of the PR and between 27c45ac and 92534de.

📒 Files selected for processing (1)
  • apps/ccusage/src/commands/all.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/ccusage/src/commands/all.ts

📝 Walkthrough

Walkthrough

Adds a new all CLI command that aggregates Claude Code and Codex daily usage, updates ccusage JSON schema with commands.all, adds a devDependency on @ccusage/codex, exposes codex subpath exports, and adds dateKey to codex daily report rows.

Changes

Cohort / File(s) Summary
Config Schema
apps/ccusage/config-schema.json
Added top-level commands.all schema object with properties (since,until,json,mode,debug,debugSamples,order,breakdown,offline,color,noColor,timezone,locale,jq,compact) and additionalProperties: false.
New Command Implementation
apps/ccusage/src/commands/all.ts
Adds all command: loads/merges config and args, treats --jq as implying --json, loads Claude and Codex daily data, normalizes dates, constructs/disposes Codex pricing source (honors --offline), builds daily rows including dateKey, computes per-day and aggregate tokens/costs, supports --json + --jq and human table output, and includes Vitest tests.
Command Registry
apps/ccusage/src/commands/index.ts
Imported and re-exported allCommand; registered ['all', allCommand] in subCommandUnion, adding 'all' to CommandName.
Dependencies
apps/ccusage/package.json
Added devDependency @ccusage/codex (workspace:*).
Package Exports
apps/codex/package.json
Added top-level exports map and publishConfig.exports exposing root and subpaths (./data-loader,./daily-report,./monthly-report,./session-report,./pricing,./date-utils,./command-utils) mapped to src/*.ts and dist/*.js.
Codex Types & Report
apps/codex/src/_types.ts, apps/codex/src/daily-report.ts
Added dateKey: string to DailyReportRow and populated it in buildDailyReport; updated tests to assert dateKey.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI Handler
    participant Claude as Claude Loader
    participant Codex as Codex Loader
    participant Pricing as Codex Pricing Source
    participant Output as Formatter

    CLI->>Claude: request daily usage (since/until)
    activate Claude
    Claude-->>CLI: claude daily rows
    deactivate Claude

    CLI->>Codex: request sessions & daily data
    activate Codex
    Codex-->>CLI: codex daily rows / session warnings
    deactivate Codex

    CLI->>Pricing: init CodexPricingSource (maybe offline)
    activate Pricing
    Pricing-->>CLI: pricing source ready
    deactivate Pricing

    CLI->>CLI: aggregate rows, compute totals (tokens, cost)
    CLI->>Output: render JSON (optional jq) or human tables
    activate Output
    Output-->>CLI: formatted output
    deactivate Output

    CLI->>Pricing: dispose pricing resources (finally)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • ryoppippi

Poem

🐇 Hopping through dates and token light,

Claude and Codex join in bright delight,
Rows aligned, totals hum along,
The rabbit nudges charts in song,
Reports compiled — a joyful byte!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ccusage): add all command to show combined Claude Code and Codex usage' accurately summarizes the primary change—adding a new all subcommand that displays combined token usage and cost for both Claude Code and Codex in a single invocation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ccusage/src/commands/all.ts`:
- Around line 137-146: The table configuration is incorrectly reading the CLI
flag directly (ctx.values.compact) so config defaults in mergedOptions are
ignored; change both occurrences that set forceCompact (in the tableConfig
objects used when building tables in this command) to use mergedOptions.compact
instead of ctx.values.compact so the merged config (defaults/commands.all) is
respected; search for the tableConfig declarations (and any other places where
forceCompact is assigned) in this file and replace ctx.values.compact with
mergedOptions.compact.
- Around line 66-72: The Codex rows are built without applying the requested
ordering; update the build/emission to respect mergedOptions.order by passing
the order into buildDailyReport (i.e., call buildDailyReport(..., { ..., order:
mergedOptions.order }) when constructing codexRows) and/or apply the same
ordering step used for Claude rows right after codexRows is produced; then
ensure the JSON emission block (the codex JSON path around the current emit at
lines 121-123) and the table rendering block (the codex table path around lines
216-249) use the ordered codexRows (or call the shared order helper used
elsewhere) before emitting.
- Around line 38-41: The code treats --jq as toggling JSON mode (useJson =
Boolean(mergedOptions.json) || mergedOptions.jq != null) but still always dumps
the entire payload before returning; update the output path (the block around
lines 97–128 that currently prints the full payload and returns) to detect
mergedOptions.jq and, when present, run the jq filter against the JSON payload
and print the filtered result (fall back to printing full JSON only when
mergedOptions.jq is absent); keep the existing logger.level adjustment for pure
JSON mode (useJson) but ensure applying mergedOptions.jq controls final printed
output rather than always dumping the raw payload.
- Around line 30-259: The current try/catch around normalizeFilterDate should be
replaced with the repository's Result-based pattern: use Result.try to call
normalizeFilterDate for mergedOptions.since and mergedOptions.until, check
Result.isFailure for each result, call logger.error with the
result.error.message and process.exit(1) on failure, and assign
codexSince/codexUntil from the successful result.value; update the block that
currently references normalizeFilterDate, mergedOptions, logger.error, and
process.exit to use Result.try, Result.isFailure, and result.value instead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e17ec30a-4f49-4fe4-a74e-6b2c20b3386a

📥 Commits

Reviewing files that changed from the base of the PR and between 61ee04d and 6003086.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/ccusage/config-schema.json
  • apps/ccusage/package.json
  • apps/ccusage/src/commands/all.ts
  • apps/ccusage/src/commands/index.ts
  • apps/codex/package.json

qianhao02 and others added 2 commits March 29, 2026 18:51
…c registry

- Fix --jq flag not working: call processWithJq() instead of raw JSON.stringify
- Fix --order only applying to Claude section: sort codexRows by mergedOptions.order
- Fix compact config file setting silently ignored: use mergedOptions.compact instead of ctx.values.compact
- Regenerate pnpm-lock.yaml using public registry (registry.npmjs.org) to remove internal Meituan registry URLs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…malization

Use Result.try + Result.isFailure from @praha/byethrow for normalizeFilterDate
calls in the all command, consistent with the repository's error-handling convention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ccusage/src/commands/all.ts`:
- Around line 75-81: The Codex rows returned by buildDailyReport (used in
codexRowsRaw) are formatted for display with locale/timezone, so for --json/--jq
output create a separate JSON payload that preserves canonical ISO timestamps:
iterate codexRowsRaw (or better, map directly from codexEvents if original
timestamps are available) and produce codexJsonRows where each item contains a
stable date field (e.g., date_iso or date) set to an ISO 8601 UTC string
(Date.toISOString of the original timestamp) plus the remaining numeric/metric
fields; replace usages that currently emit the display rows (the code around
codexRowsRaw and the similar block at lines 132-134) to emit this new
codexJsonRows for JSON/JQ output while leaving display formatting unchanged for
human output.
- Around line 32-280: Add an in-source Vitest block guarded by if
(import.meta.vitest != null) that unit-tests the allCommand export covering key
branches: JSON output (--json), jq processing (--jq), row ordering
(mergedOptions.order asc/desc) and the combined total line; create a mock ctx
and stub/spy the helper functions used by allCommand (loadDailyUsageData,
loadTokenUsageEvents, buildDailyReport, processWithJq) and logger methods so you
can assert outputs and error paths, and stub CodexPricingSource (pricingSource)
to avoid real disposal side-effects (ensure its Symbol.dispose is a no-op).
Locate tests around the allCommand definition and use the same mergedOptions
inputs (e.g., set mergedOptions.jq, mergedOptions.json, mergedOptions.order) to
drive each branch and assert the expected log/output, and verify combinedCost is
computed from claudeTotals.totalCost and codexTotals.costUSD.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 25fc9f21-31de-49f8-857a-46fdebfb1f65

📥 Commits

Reviewing files that changed from the base of the PR and between ff6dcb5 and 73de73c.

📒 Files selected for processing (1)
  • apps/ccusage/src/commands/all.ts

qianhao02 and others added 2 commits March 29, 2026 20:28
- Add `dateKey: string` (YYYY-MM-DD) to DailyReportRow type alongside the
  existing locale-formatted `date` display field
- Populate dateKey from summary.date in buildDailyReport
- Use dateKey in `ccusage all --json` Codex output for stable ISO dates
- Add in-source Vitest tests to all.ts covering dateKey format, order
  sorting, and combined cost calculation
- Add dateKey assertion to existing buildDailyReport test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass the full DailyReportRow (including both dateKey and date fields)
directly to the JSON output instead of remapping. This ensures schema
consistency with codex daily --json and exposes the new stable dateKey
alongside the locale-formatted date display field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qianhaoq
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/ccusage/src/commands/all.ts (1)

41-44: Restore logger level after JSON/JQ execution to avoid global side effects.

Line 43 mutates global logger state, but it is never reset. If this command is invoked multiple times in-process, later commands may inherit quiet logging unexpectedly.

♻️ Proposed refactor
 		// --jq implies --json
 		const useJson = Boolean(mergedOptions.json) || mergedOptions.jq != null;
+		const previousLoggerLevel = logger.level;
 		if (useJson) {
 			logger.level = 0;
 		}
@@
 		} finally {
+			logger.level = previousLoggerLevel;
 			pricingSource[Symbol.dispose]();
 		}

Also applies to: 277-279

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ccusage/src/commands/all.ts` around lines 41 - 44, The code sets
logger.level = 0 when useJson (computed from mergedOptions.json or
mergedOptions.jq) but never restores it, causing global side effects; update the
all command to save the original logger.level into a local variable before
setting it to 0 and then restore that saved level after the JSON/JQ execution
completes (ensure restoration runs in finally/callback to cover errors). Apply
the same save-and-restore pattern to the second occurrence that currently sets
logger.level (the block around the other JSON/JQ handling) so subsequent
in-process invocations do not inherit the muted logging state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/ccusage/src/commands/all.ts`:
- Around line 41-44: The code sets logger.level = 0 when useJson (computed from
mergedOptions.json or mergedOptions.jq) but never restores it, causing global
side effects; update the all command to save the original logger.level into a
local variable before setting it to 0 and then restore that saved level after
the JSON/JQ execution completes (ensure restoration runs in finally/callback to
cover errors). Apply the same save-and-restore pattern to the second occurrence
that currently sets logger.level (the block around the other JSON/JQ handling)
so subsequent in-process invocations do not inherit the muted logging state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 456c1259-0d16-428f-9b19-e1ac6ca522b5

📥 Commits

Reviewing files that changed from the base of the PR and between 419f467 and 300f413.

📒 Files selected for processing (1)
  • apps/ccusage/src/commands/all.ts

- Compute codexTotals from unsorted codexRowsRaw so sort order never
  affects aggregation (ryoppippi#1)
- Add comments documenting the since/until normalization asymmetry
  between Claude and Codex sides (ryoppippi#2)
- Remove codexDisplayTotals accumulator; derive table totals row from
  codexTotals via splitUsageTokens (single source of truth) (ryoppippi#3)
- Remove redundant `?? undefined` in locale cast (ryoppippi#7)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qianhaoq
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

splitUsageTokens clamps reasoning to output per row, so splitting the
aggregate produces a different result than summing per-row splits when
any row has reasoningOutputTokens > outputTokens. Restore the original
accumulation approach (sum splits row-by-row) so the Total row stays
consistent with the visible per-row values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qianhaoq
Copy link
Copy Markdown
Author

Thanks for the review! Regarding the logger.level save-and-restore suggestion: this is an existing pattern used consistently across all commands in this codebase (daily.ts, monthly.ts, session.ts, blocks.ts, weekly.ts, statusline.ts). Since this is a CLI tool where each invocation runs in its own process, the level is never restored in any command. Fixing this in all.ts alone would introduce inconsistency. If this is a concern, it should be addressed as a separate refactor across all commands.

@qianhaoq
Copy link
Copy Markdown
Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

✅ Actions performed

Reviews resumed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant