feat(copilot): add GitHub Copilot CLI usage adapter by ryoppippi · Pull Request #1023 · ccusage/ccusage · GitHub
Skip to content

feat(copilot): add GitHub Copilot CLI usage adapter#1023

Merged
ryoppippi merged 8 commits into
mainfrom
codex/copilot-usage-adapter
May 19, 2026
Merged

feat(copilot): add GitHub Copilot CLI usage adapter#1023
ryoppippi merged 8 commits into
mainfrom
codex/copilot-usage-adapter

Conversation

@ryoppippi

@ryoppippi ryoppippi commented May 17, 2026

Copy link
Copy Markdown
Member

Try This PR

bunx -p https://pkg.pr.new/ryoppippi/ccusage@1023 ccusage copilot daily --help

Summary

  • add a GitHub Copilot CLI adapter for local OpenTelemetry JSONL exports
  • wire the copilot namespace into all-source detection, focused daily/monthly/session commands, and source-scoped config
  • document Copilot setup and regenerate the public config schema

Verification

  • pnpm run format
  • pnpm typecheck
  • pnpm run test

Summary by cubic

Adds a GitHub Copilot CLI usage adapter, now implemented in Rust, that reads local OpenTelemetry JSONL exports, integrates with unified reports, and provides daily/monthly/session views with pricing and token accounting that include reasoning tokens.

  • New Features

    • New Rust copilot adapter reads ~/.copilot/otel/*.jsonl and COPILOT_OTEL_FILE_EXPORTER_PATH.
    • Prefers chat spans; falls back to inference and agent-turn logs. Dedupes by trace/response ID.
    • Excludes cache-read tokens from inputs, includes reasoning tokens in totals, and computes costs from LiteLLM pricing.
    • New commands: ccusage copilot daily|monthly|session. Included in all-source detection, progress labels, and copilot-scoped config.
  • Docs

    • New Copilot guide with setup, env vars, and examples. Nav, README, and env docs updated.
    • Public config schema regenerated to add the copilot namespace with per-command overrides.

Written for commit a523c90. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Added support for GitHub Copilot CLI as a data source for usage tracking
    • Added configuration options for Copilot-specific report settings and defaults
  • Documentation

    • Expanded documentation to include Copilot CLI setup and usage guides
    • Updated all relevant guides to list Copilot CLI among supported sources

Review Change Stack

ryoppippi added 3 commits May 17, 2026 12:41
Read Copilot OpenTelemetry JSONL exports from the default ~/.copilot/otel directory and from COPILOT_OTEL_FILE_EXPORTER_PATH.

The adapter prefers chat spans, falls back to inference and agent-turn records, and suppresses lower-priority duplicates by trace or response id. It normalizes cached input tokens out of input totals, keeps reasoning tokens in total usage, and calculates costs from model pricing rather than trusting exported Copilot cost fields.

Wire the copilot namespace into focused daily, monthly, and session commands, all-source detection, and source-scoped config merging.
Regenerate the published ccusage configuration schema so the new copilot namespace is accepted by editors and config validation tooling.
Add a focused Copilot data source guide covering local OpenTelemetry JSONL export setup, report commands, calculated fields, environment variables, and troubleshooting.

Update the docs navigation, source tables, configuration examples, and landing copy so the new copilot namespace is discoverable alongside the other local data sources.
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

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

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide a523c90 Commit Preview URL

Branch Preview URL
May 19 2026, 11:33 AM

@pkg-pr-new

pkg-pr-new Bot commented May 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

ccusage

npx https://pkg.pr.new/ryoppippi/ccusage@1023

@ccusage/ccusage-darwin-arm64

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/ccusage-darwin-arm64@1023

@ccusage/ccusage-darwin-x64

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/ccusage-darwin-x64@1023

@ccusage/ccusage-linux-arm64

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/ccusage-linux-arm64@1023

@ccusage/ccusage-linux-x64

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/ccusage-linux-x64@1023

@ccusage/ccusage-win32-arm64

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/ccusage-win32-arm64@1023

@ccusage/ccusage-win32-x64

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/ccusage-win32-x64@1023

commit: a523c90

@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

ccusage performance comparison

This compares the PR build against the base branch build on the same CI runner.

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Runtime: package ccusage bin from apps/ccusage/package.json through bun -b, --offline --json, measured by hyperfine with 2 warmups and 7 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude daily --offline --json 0.00 MiB 56.2ms 56.5ms 0.99x 0.03 MiB/s 0.03 MiB/s
claude session --offline --json 0.00 MiB 56.5ms 57.0ms 0.99x 0.03 MiB/s 0.03 MiB/s
codex daily --offline --json 0.00 MiB 56.3ms 57.1ms 0.99x 0.02 MiB/s 0.02 MiB/s
codex session --offline --json 0.00 MiB 56.5ms 56.8ms 1.00x 0.02 MiB/s 0.02 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
Runtime: package ccusage bin from apps/ccusage/package.json through bun -b, --offline --json, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude --offline --json 1.01 GiB 1.490s 1.479s 1.01x 691.76 MiB/s 697.25 MiB/s
codex --offline --json 1.01 GiB 1.733s 1.882s 0.92x 594.90 MiB/s 547.77 MiB/s

Package size

Package artifact Base PR Delta Ratio
packed ccusage-*.tgz 67.58 KiB 69.83 KiB +2.25 KiB 0.97x

Lower medians and smaller packed package sizes are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/ccusage/config-schema.json (1)

635-781: ⚡ Quick win

Verify copilot namespace structure against established agent patterns.

All existing agents (claude, codex, opencode, amp, pi) follow a consistent structure with a top-level namespace containing defaults and commands sub-properties:

{
  "claude": {
    "defaults": { ... },
    "commands": { "daily": {...}, "monthly": {...} }
  }
}

The copilot commands are structured differently - they're added directly to the root-level commands object with prefixed names (copilot:daily, copilot:monthly, copilot:session) and there's no separate copilot namespace with defaults.

This structural difference means users cannot configure copilot defaults like copilot.defaults.offline, and must instead configure each command individually. Verify whether:

  1. A copilot namespace should exist (similar to lines 787-2314 for other agents)
  2. The prefixed-command approach is intentional

Run the following script to check if the copilot namespace pattern is defined elsewhere or if this divergent structure is intentional:

#!/bin/bash
# Check if copilot namespace structure exists elsewhere in the codebase

echo "=== Searching for copilot namespace definitions in config/schema files ==="
rg -n -C3 '"copilot"\s*:\s*\{' --type=json --type=ts --type=js

echo ""
echo "=== Searching for copilot.defaults or copilot.commands references ==="
rg -n 'copilot\.defaults|copilot\.commands' --type=ts --type=js

echo ""
echo "=== Checking config loader for copilot namespace handling ==="
rg -n -C5 'copilot.*namespace|namespace.*copilot' apps/ccusage/src/config-loader-tokens.ts

echo ""
echo "=== Comparing with other agent namespace patterns ==="
rg -n '"(claude|codex|opencode)"\s*:\s*\{' apps/ccusage/config-schema.json | head -3
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ccusage/config-schema.json` around lines 635 - 781, The copilot command
schemas are defined as root keys ("copilot:daily", "copilot:monthly",
"copilot:session") which breaks the established agent pattern of a top-level
"copilot" namespace with "defaults" and "commands"; change the schema so there
is a single "copilot" object containing a "defaults" property (including shared
flags like offline, color/noColor, compact, json, timezone, since/until
defaults) and a "commands" object that nests "daily", "monthly", and "session"
schemas (move the existing "copilot:daily"/"copilot:monthly"/"copilot:session"
definitions into copilot.commands.daily|monthly|session and remove the prefixed
root keys), keep "additionalProperties": false and mirror other agents'
structure to restore support for copilot.defaults.* configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/guide/environment-variables.md`:
- Around line 7-17: The env-debug grep example omitted the Copilot variable, so
update the debug snippet that filters environment variables to include COPILOT
(specifically COPILOT_OTEL_FILE_EXPORTER_PATH) so users see the Copilot export
path; locate the env-debug/diagnostic shell snippet in the same document (the
grep command around the "env-debug" example and the repeated snippet at lines
referenced 25-29) and add COPILOT to the grep pattern (e.g., include "COPILOT"
alongside existing tokens) so the COPILOT_OTEL_FILE_EXPORTER_PATH entry appears
in output when present.

---

Nitpick comments:
In `@apps/ccusage/config-schema.json`:
- Around line 635-781: The copilot command schemas are defined as root keys
("copilot:daily", "copilot:monthly", "copilot:session") which breaks the
established agent pattern of a top-level "copilot" namespace with "defaults" and
"commands"; change the schema so there is a single "copilot" object containing a
"defaults" property (including shared flags like offline, color/noColor,
compact, json, timezone, since/until defaults) and a "commands" object that
nests "daily", "monthly", and "session" schemas (move the existing
"copilot:daily"/"copilot:monthly"/"copilot:session" definitions into
copilot.commands.daily|monthly|session and remove the prefixed root keys), keep
"additionalProperties": false and mirror other agents' structure to restore
support for copilot.defaults.* configuration.
🪄 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: de4ab010-9112-46e4-8755-971df8a2a332

📥 Commits

Reviewing files that changed from the base of the PR and between a4d72e7 and efd2fef.

📒 Files selected for processing (20)
  • apps/ccusage/config-schema.json
  • apps/ccusage/src/adapter/copilot/index.ts
  • apps/ccusage/src/adapter/copilot/parser.ts
  • apps/ccusage/src/adapter/copilot/paths.ts
  • apps/ccusage/src/adapter/copilot/pricing.ts
  • apps/ccusage/src/adapter/index.ts
  • apps/ccusage/src/adapter/types.ts
  • apps/ccusage/src/commands/index.ts
  • apps/ccusage/src/config-loader-tokens.ts
  • docs/.vitepress/config.ts
  • docs/guide/all-reports.md
  • docs/guide/claude/index.md
  • docs/guide/codex/index.md
  • docs/guide/config-files.md
  • docs/guide/configuration.md
  • docs/guide/copilot/index.md
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • docs/index.md

Comment thread docs/guide/environment-variables.md
ryoppippi added 2 commits May 17, 2026 12:48
Mention GitHub Copilot CLI in the package README source summary, source-focused examples, and unified report feature list.
Add the Copilot OpenTelemetry variable family to the environment-variable grep example so the debugging snippet matches the documented Copilot source support.
@ryoppippi

Copy link
Copy Markdown
Member Author

@cubic-dev-ai review it

@cubic-dev-ai

cubic-dev-ai Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review it

@ryoppippi I have started the AI code review. It will take a few minutes to complete.

@ryoppippi

Copy link
Copy Markdown
Member Author

@coderabbitai review it

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

@ryoppippi Sure! I'll review the PR right away.

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

Replace the inline agent label conditional in the schema generator with a small exhaustive switch helper. This keeps future agent-specific labels out of nested ternaries and makes the config schema label rules easier to extend.
@ryoppippi

Copy link
Copy Markdown
Member Author

@coderabbitai review it

@ryoppippi

Copy link
Copy Markdown
Member Author

@cubic-dev-ai review it

@cubic-dev-ai

cubic-dev-ai Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review it

@ryoppippi I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

@ryoppippi Sure! I'll review the PR right away.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ccusage/scripts/generate-json-schema.ts`:
- Around line 160-170: Add the missing 'copilot' agent to the AGENT_NAMES
constant and update the getAgentConfigLabel function to return the correct label
for copilot: add 'copilot' to the list of AGENT_NAMES (where other agent names
like 'amp', 'claude' are defined) and add a case 'copilot': return 'copilot';
branch inside getAgentConfigLabel so the schema generator recognizes copilot
prefixes and creates the copilot namespace.
🪄 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: 75e92c65-97e3-4d60-81a6-353d1e82daee

📥 Commits

Reviewing files that changed from the base of the PR and between 9dd9be5 and fe44d9f.

📒 Files selected for processing (1)
  • apps/ccusage/scripts/generate-json-schema.ts

Comment thread apps/ccusage/scripts/generate-json-schema.ts Outdated
Register Copilot in the schema generator agent list and generated config schema so copilot command defaults live under the copilot namespace instead of top-level copilot:* command keys.
@ryoppippi

Copy link
Copy Markdown
Member Author

@coderabbitai review it

@ryoppippi

Copy link
Copy Markdown
Member Author

@cubic-dev-ai review it

@cubic-dev-ai

cubic-dev-ai Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review it

@ryoppippi I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

@ryoppippi Sure! I'll review the PR right away.

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

@ryoppippi

Copy link
Copy Markdown
Member Author

@coderabbitai review

Updated this PR to merge current main and port GitHub Copilot CLI usage support to the Rust CLI implementation. Local validation passed:

  • nix develop --command pnpm run format
  • nix develop --command pnpm typecheck
  • nix develop --command pnpm run test

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

@ryoppippi Sure! I'll review the updated PR right away — great to hear local validation passed.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/crates/ccusage/src/adapter/all.rs`:
- Around line 332-333: The table's total column is undercounting because it
recalculates totals from individual fields instead of using
summary.total_tokens(); update the table-building logic (where
input/output/cache token sums are combined) to use the already-computed
total_tokens (from summary.total_tokens()) for the displayed total, and remove
or reconcile the old recomputation (e.g., any vars like
input_tokens/output_tokens/cache_tokens used only to compute the table total) so
the table total matches the JSON summary.total_tokens().

In `@rust/crates/ccusage/src/output.rs`:
- Around line 83-84: The table footer currently sums input + output +
cache_create + cache_read but omits the new extra tokens field, causing a
mismatch with the JSON "totalTokens" (which uses input + output + cache_create +
cache_read + extra); update the non-compact footer calculation to include each
row's extra_total_tokens (or the local variable `extra`) when aggregating totals
(e.g., in the code that iterates over `rows` / `rows.iter()` and computes
`total_tokens`/footer totals) so the footer total matches the JSON
`totalTokens`.
🪄 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: 7a979980-cb50-4d11-9d79-f3801fcef001

📥 Commits

Reviewing files that changed from the base of the PR and between fe44d9f and a523c90.

📒 Files selected for processing (27)
  • apps/ccusage/CLAUDE.md
  • apps/ccusage/README.md
  • apps/ccusage/config-schema.json
  • apps/ccusage/package.json
  • apps/ccusage/src/cli.ts
  • apps/ccusage/src/index.ts
  • apps/ccusage/tsdown.config.ts
  • docs/.vitepress/config.ts
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • package.json
  • rust/crates/ccusage/src/adapter/all.rs
  • rust/crates/ccusage/src/adapter/amp.rs
  • rust/crates/ccusage/src/adapter/copilot.rs
  • rust/crates/ccusage/src/adapter/mod.rs
  • rust/crates/ccusage/src/adapter/opencode/mod.rs
  • rust/crates/ccusage/src/adapter/opencode/parser.rs
  • rust/crates/ccusage/src/adapter/pi.rs
  • rust/crates/ccusage/src/claude_loader.rs
  • rust/crates/ccusage/src/cli.rs
  • rust/crates/ccusage/src/config.rs
  • rust/crates/ccusage/src/config_schema.rs
  • rust/crates/ccusage/src/main.rs
  • rust/crates/ccusage/src/output.rs
  • rust/crates/ccusage/src/progress.rs
  • rust/crates/ccusage/src/summary.rs
  • rust/crates/ccusage/src/types.rs
💤 Files with no reviewable changes (3)
  • apps/ccusage/src/index.ts
  • apps/ccusage/CLAUDE.md
  • apps/ccusage/tsdown.config.ts
✅ Files skipped from review due to trivial changes (6)
  • rust/crates/ccusage/src/adapter/mod.rs
  • rust/crates/ccusage/src/claude_loader.rs
  • apps/ccusage/README.md
  • docs/.vitepress/config.ts
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md

Comment on lines +332 to 333
let total_tokens = summary.total_tokens();
if total_tokens == 0 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align table total-token math with summary.total_tokens()

Line 332 now uses summary.total_tokens(), but the table still recomputes totals from only input/output/cache fields. This causes table totals to undercount whenever extra tokens exist, diverging from JSON totals.

🔧 Proposed fix
 fn table_total_tokens(row: &AllRow) -> u64 {
-    row.input_tokens
-        .saturating_add(row.output_tokens)
-        .saturating_add(row.cache_creation_tokens)
-        .saturating_add(row.cache_read_tokens)
+    row.total_tokens
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let total_tokens = summary.total_tokens();
if total_tokens == 0 {
fn table_total_tokens(row: &AllRow) -> u64 {
row.total_tokens
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/crates/ccusage/src/adapter/all.rs` around lines 332 - 333, The table's
total column is undercounting because it recalculates totals from individual
fields instead of using summary.total_tokens(); update the table-building logic
(where input/output/cache token sums are combined) to use the already-computed
total_tokens (from summary.total_tokens()) for the displayed total, and remove
or reconcile the old recomputation (e.g., any vars like
input_tokens/output_tokens/cache_tokens used only to compute the table total) so
the table total matches the JSON summary.total_tokens().

Comment on lines +83 to 84
"totalTokens": input + output + cache_create + cache_read + extra,
"totalCost": rows.iter().map(|row| row.total_cost).sum::<f64>(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix footer total-tokens mismatch with new extra_total_tokens semantics.

After this change, JSON totals include extra tokens, but the non-compact table footer still excludes them, so totals diverge from row totals for Copilot data.

💡 Proposed fix
@@
-    let total_cost = totals
+    let total_cost = totals
         .get("totalCost")
         .and_then(Value::as_f64)
         .unwrap_or_default();
+    let total_tokens = totals
+        .get("totalTokens")
+        .and_then(Value::as_u64)
+        .unwrap_or_default();
@@
-            color(
-                shared,
-                format_number(input + output + cache_create + cache_read),
-                Color::Yellow,
-            ),
+            color(shared, format_number(total_tokens), Color::Yellow),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/crates/ccusage/src/output.rs` around lines 83 - 84, The table footer
currently sums input + output + cache_create + cache_read but omits the new
extra tokens field, causing a mismatch with the JSON "totalTokens" (which uses
input + output + cache_create + cache_read + extra); update the non-compact
footer calculation to include each row's extra_total_tokens (or the local
variable `extra`) when aggregating totals (e.g., in the code that iterates over
`rows` / `rows.iter()` and computes `total_tokens`/footer totals) so the footer
total matches the JSON `totalTokens`.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: a523c90679e4
Base SHA: 27047aca4573

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runner startup

Execution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one bunx -p <url> ccusage --version run with an empty Bun install cache. Warm reuses that cache and reports the median of repeated runs.

Package SHA Execution setup Bunx temp cache Bunx warm median Warm samples
Base pkg.pr.new 27047aca4573 471.6ms 474.7ms 35.2ms 3
PR pkg.pr.new a523c90679e4 976.3ms 708.1ms 35.3ms 3

Cached bunx execution performance

Runs the same large fixture through bunx -p <pkg.pr.new URL> ccusage after the Bun install cache has already been populated by the startup measurement. This separates cached package-runner execution from first-fetch package materialization.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
Base package: 27047aca4573; PR package: a523c90679e4. Both run through bunx -p <pkg.pr.new URL> ccusage using the warmed Bun install cache from package runner startup, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
bunx -p <pkg> ccusage claude --offline --json 1.01 GiB 676.2ms 682.1ms 0.99x 1.49 GiB/s 1.48 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 891.4ms 890.8ms 1.00x 1.13 GiB/s 1.13 GiB/s

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 680.0ms 1.48 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 649.0ms 1.55 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 738.9ms 1.36 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 882.2ms 1.14 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 855.3ms 1.18 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 837.2ms 1.20 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs rust/target/release/ccusage directly. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude daily --offline --json 0.00 MiB 30.6ms 4.0ms 7.64x 0.05 MiB/s 0.39 MiB/s
claude session --offline --json 0.00 MiB 29.8ms 3.9ms 7.70x 0.05 MiB/s 0.40 MiB/s
codex daily --offline --json 0.00 MiB 29.5ms 3.4ms 8.59x 0.03 MiB/s 0.25 MiB/s
codex session --offline --json 0.00 MiB 30.1ms 3.6ms 8.43x 0.03 MiB/s 0.24 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs rust/target/release/ccusage directly. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude --offline --json 1.01 GiB 667.7ms 737.4ms 0.91x 1.51 GiB/s 1.37 GiB/s
codex --offline --json 1.01 GiB 864.4ms 818.0ms 1.06x 1.16 GiB/s 1.23 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.86 KiB 8.78 KiB -0.09 KiB 1.01x
installed native package binary 2772.96 KiB 2772.96 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2699.68 KiB - -

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: a523c90679e4
Base SHA: 27047aca4573

This compares the PR package against the configured base package on the same CI runner.

Package runner startup

Execution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one bunx -p <url> ccusage --version run with an empty Bun install cache. Warm reuses that cache and reports the median of repeated runs.

Package SHA Execution setup Bunx temp cache Bunx warm median Warm samples
Base pkg.pr.new 27047aca4573 456.1ms 780.9ms 33.3ms 3
PR pkg.pr.new a523c90679e4 671.9ms 522.0ms 35.4ms 3

Cached bunx execution performance

Runs the same large fixture through bunx -p <pkg.pr.new URL> ccusage after the Bun install cache has already been populated by the startup measurement. This separates cached package-runner execution from first-fetch package materialization.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
Base package: 27047aca4573; PR package: a523c90679e4. Both run through bunx -p <pkg.pr.new URL> ccusage using the warmed Bun install cache from package runner startup, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
bunx -p <pkg> ccusage claude --offline --json 1.01 GiB 670.7ms 674.2ms 0.99x 1.50 GiB/s 1.49 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 837.6ms 894.5ms 0.94x 1.20 GiB/s 1.13 GiB/s

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 719.0ms 1.40 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 652.1ms 1.54 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 712.3ms 1.41 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 832.5ms 1.21 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 801.2ms 1.26 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 801.7ms 1.26 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude daily --offline --json 0.00 MiB 28.6ms 31.0ms 0.92x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 28.6ms 31.3ms 0.92x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 29.0ms 30.0ms 0.97x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 29.7ms 30.7ms 0.97x 0.03 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude --offline --json 1.01 GiB 666.3ms 681.5ms 0.98x 1.51 GiB/s 1.48 GiB/s
codex --offline --json 1.01 GiB 825.4ms 830.3ms 0.99x 1.22 GiB/s 1.21 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.86 KiB 8.78 KiB -0.09 KiB 1.01x
installed native package binary 2772.96 KiB 2772.96 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2699.68 KiB - -

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

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