Skip to content

feat(droid): add Factory Droid usage adapter#1028

Merged
ryoppippi merged 17 commits into
mainfrom
codex/droid-usage-adapter
May 19, 2026
Merged

feat(droid): add Factory Droid usage adapter#1028
ryoppippi merged 17 commits into
mainfrom
codex/droid-usage-adapter

Conversation

@ryoppippi
Copy link
Copy Markdown
Owner

@ryoppippi ryoppippi commented May 17, 2026

Try This PR

bunx -p https://pkg.pr.new/ryoppippi/ccusage@1028 ccusage droid daily --help

Summary

  • add a Droid adapter that reads local Factory session settings JSON files
  • normalize Droid model/provider names and include thinking tokens in total usage
  • wire Droid into focused commands, config schema, CLI snapshots, README, and docs

Validation

  • pnpm run format
  • pnpm typecheck
  • pnpm run test
  • pnpm --filter docs build
  • git diff --check

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Droid as a supported coding agent CLI source with daily, monthly, and session report types
    • Includes token usage tracking and cost calculation via LiteLLM pricing, with thinking token support
    • Configurable sessions directory via DROID_SESSIONS_DIR environment variable
  • Documentation

    • Updated guides to include Droid configuration and usage examples
    • Added comprehensive Droid data source documentation including setup and troubleshooting

Review Change Stack

Read Droid settings files from local Factory session directories and expose focused daily, monthly, and session reports.

This adds model and provider normalization, token aggregation including thinking tokens, LiteLLM-based cost estimation, CLI/config/schema wiring, snapshots, and documentation for the new data source.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds Droid as a new supported coding agent in ccusage. The implementation spans configuration schema registration, CLI command parsing with droid-specific report kinds, a new adapter module for discovering and parsing Droid session settings files with cost calculation, framework wiring to integrate the adapter into the agent detection and output pipelines, and comprehensive documentation across guides, README, and navigation.

Changes

Droid Agent Support

Layer / File(s) Summary
Configuration schema: DroidConfig types and JSON schema
rust/crates/ccusage/src/config_schema.rs, apps/ccusage/config-schema.json
Adds DroidConfig and DroidCommandsConfig Rust structs and JSON schema definitions to support per-agent and per-report droid configuration with defaults and command-specific options, matching the pattern of existing agents.
CLI command parsing for droid
rust/crates/ccusage/src/cli.rs
Introduces Command::Droid variant and parse_droid_command to handle daily/monthly/session subcommands; updates validation helpers and help system to recognize droid as a supported agent; adds parsing tests.
Droid adapter: session discovery, parsing, and cost calculation
rust/crates/ccusage/src/adapter/droid.rs
Implements complete Droid adapter: discovers .settings.json files from DROID_SESSIONS_DIR or ~/.factory/sessions, parses token usage and model/provider identity, normalizes provider and model names, falls back to sidecar .jsonl for missing models, calculates cost via LiteLLM pricing with provider-specific variants, supports daily/monthly/session reports, and includes comprehensive tests.
Adapter module and framework wiring
rust/crates/ccusage/src/adapter/mod.rs, rust/crates/ccusage/src/adapter/all.rs, rust/crates/ccusage/src/main.rs, rust/crates/ccusage/src/progress.rs, rust/crates/ccusage/src/config.rs, rust/crates/ccusage/src/output.rs
Registers droid submodule; wires droid into parallel agent loading with deduplication and filtering; dispatches droid command to adapter; adds UsageLoadAgent::Droid enum variant; recognizes droid in agent detection; updates output table to include reasoning tokens from extra_total_tokens.
User documentation: guides, README, config reference, and navigation
docs/guide/droid/index.md, docs/guide/, apps/ccusage/README.md, docs/index.md, docs/.vitepress/config.ts
Adds new Droid guide explaining beta support, session directory discovery, report views, token/cost calculation, environment variables, and troubleshooting; documents DROID_SESSIONS_DIR across config, environment, and getting-started guides with comma-separated directory support; updates README, home page, all-reports, and cross-source guides to list Droid; adds VitePress navigation link.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ryoppippi/ccusage#1021: Extends the same ccusage agent integration pipeline (CLI, config schema, adapter/all.rs, progress tracking) to add Gemini support alongside other agents.
  • ryoppippi/ccusage#1032: Follows the identical agent-wiring pattern (CLI/config-schema/adapter/all.rs/progress) to integrate Kilo as a new agent.
  • ryoppippi/ccusage#1035: Extends the same framework integration points (CLI parsing, config schema root namespace, adapter registration) to add Goose as a new agent.

Poem

🐰 A factory of thoughts takes the stage,
Droid's reasoning tokens fill the page—
Sessions summed by day and month so bright,
LiteLLM prices calculated just right.
From settings files the data does flow,
New agent, old pipeline, steal the show! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding a Factory Droid usage adapter to the ccusage application. It directly reflects the substantial Rust implementation and integration work shown in the raw summary.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/droid-usage-adapter

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

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

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 667a2fd Commit Preview URL

Branch Preview URL
May 19 2026, 06:43 PM

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

🧹 Nitpick comments (1)
apps/ccusage/scripts/generate-json-schema.ts (1)

245-245: ⚡ Quick win

Add a regression assertion for the new droid schema namespace.

Please add a createConfigSchemaJson test assertion like expect(properties).toHaveProperty('droid') so future changes can’t silently drop this namespace.

🤖 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/scripts/generate-json-schema.ts` at line 245, Add a regression
assertion to the createConfigSchemaJson test to ensure the 'droid' namespace
isn't dropped: after the code that computes properties in the
createConfigSchemaJson test, add an assertion like
expect(properties).toHaveProperty('droid') so the generated schema includes the
entry produced by createAgentJsonSchema('droid', agentCommandSchemas.droid);
reference the properties variable, the 'droid' key, createAgentJsonSchema call,
and the createConfigSchemaJson test body when adding the assertion.
🤖 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/src/adapter/droid/parser.ts`:
- Around line 94-118: The provider normalization in normalizeDroidProvider
trims, lowercases and replaces '-' with '_', so values like "x-ai" become "x_ai"
and currently miss the xAI branch; update normalizeDroidProvider to recognize
"x_ai" (in addition to "xai") in the xAI conditional (the normalized variable
within function normalizeDroidProvider) so it returns "xai" for both "x_ai" and
"xai".

In `@apps/ccusage/test/snapshots/cli-output/all-agent-daily-table.txt`:
- Around line 12-26: The table snapshot totals are inconsistent with the JSON
snapshots because the table renderer's aggregation is missing Droid's reasoning
tokens (and/or double-counting another agent); update the aggregation logic so
computeDailyTotals / aggregateAgentTotals / buildDailyAgentTable (or whatever
function builds the CLI rows) sums each agent's tokens as directTokens +
reasoningTokens (use the same token fields used by the JSON renderer, e.g.,
getAgentUsage/getDirectTokens/getReasoningTokens) and ensure renderAgentRow uses
that aggregated value so the All/Total rows match the all-agent-daily-json and
droid-direct-daily-json fixtures.

---

Nitpick comments:
In `@apps/ccusage/scripts/generate-json-schema.ts`:
- Line 245: Add a regression assertion to the createConfigSchemaJson test to
ensure the 'droid' namespace isn't dropped: after the code that computes
properties in the createConfigSchemaJson test, add an assertion like
expect(properties).toHaveProperty('droid') so the generated schema includes the
entry produced by createAgentJsonSchema('droid', agentCommandSchemas.droid);
reference the properties variable, the 'droid' key, createAgentJsonSchema call,
and the createConfigSchemaJson test body when adding the assertion.
🪄 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: daab5cc1-3e59-4303-b407-7a012a3cf716

📥 Commits

Reviewing files that changed from the base of the PR and between ede7f71 and fbb785c.

📒 Files selected for processing (27)
  • apps/ccusage/README.md
  • apps/ccusage/config-schema.json
  • apps/ccusage/scripts/generate-json-schema.ts
  • apps/ccusage/src/adapter/droid/index.ts
  • apps/ccusage/src/adapter/droid/parser.ts
  • apps/ccusage/src/adapter/droid/paths.ts
  • apps/ccusage/src/adapter/droid/pricing-macro.ts
  • apps/ccusage/src/adapter/droid/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
  • apps/ccusage/test/cli-output.test.ts
  • apps/ccusage/test/snapshots/cli-output/all-agent-daily-json.txt
  • apps/ccusage/test/snapshots/cli-output/all-agent-daily-table.txt
  • apps/ccusage/test/snapshots/cli-output/droid-direct-daily-json.txt
  • 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/droid/index.md
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • docs/index.md

Comment thread apps/ccusage/src/adapter/droid/parser.ts Outdated
Comment thread apps/ccusage/test/snapshots/cli-output/all-agent-daily-table.txt Outdated
Handle x-ai provider locks, assert the generated Droid config namespace, and render adapter-provided totalTokens in focused and all-agent tables so reasoning token totals stay consistent with JSON output.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@cubic-dev-ai review it

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented May 17, 2026

@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
Owner Author

@coderabbitai review it

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat apps/ccusage/src/adapter/droid/parser.ts

Length of output: 12082


🏁 Script executed:

#!/bin/bash
cat apps/ccusage/src/adapter/droid/paths.ts
cat apps/ccusage/src/adapter/droid/pricing.ts
cat apps/ccusage/src/adapter/droid/pricing-macro.ts
cat apps/ccusage/src/adapter/droid/index.ts

Length of output: 9905


🏁 Script executed:

#!/bin/bash
cat apps/ccusage/src/adapter/index.ts
cat apps/ccusage/src/adapter/types.ts
cat apps/ccusage/src/config-loader-tokens.ts
cat packages/terminal/src/table.ts
cat apps/ccusage/src/commands/agent.ts
cat apps/ccusage/src/commands/all.ts

Length of output: 119125


@ryoppippi Great PR — the Droid adapter is well-structured and consistent with the existing agent pattern. Here are my findings:


Review

🐛 Bugs

1. createDroidModelCandidates never tries OpenRouter-prefixed names (src/adapter/droid/pricing.ts)

DROID_PROVIDER_PREFIXES advertises openrouter/anthropic/, openrouter/openai/, openrouter/x-ai/, etc., but providerPrefixes() — which drives the candidate list in calculateDroidCost — never emits those paths:

// providerPrefixes('anthropic') → ['anthropic/']
// openrouter/anthropic/ is never tried
for (const candidate of createDroidModelCandidates(entry)) {
    const result = await fetcher.calculateCostFromTokens(tokens, candidate);
    ...
}

If LiteLLM's dataset stores a model exclusively under openrouter/anthropic/claude-sonnet-4, the cost will silently be $0.00. Consider extending providerPrefixes() (or createDroidModelCandidates) to also emit the corresponding OpenRouter paths when the provider is known, e.g.:

case 'anthropic':
    return ['anthropic/', 'openrouter/anthropic/'];
case 'openai':
    return ['openai/', 'openrouter/openai/'];
case 'google':
    return ['google/', 'vertex_ai/', 'openrouter/google/'];
case 'xai':
    return ['xai/', 'openrouter/x-ai/'];

2. Eager module-level network request (src/adapter/droid/pricing.ts)

const PREFETCHED_DROID_PRICING = prefetchDroidPricing();  // fires on import

This fires unconditionally whenever the module is imported — even when the user runs ccusage claude daily, Droid is not detected, or --offline is set. Because this is a macro-generated constant (resolved at build time), double-check whether the fetch actually happens at runtime or is tree-shaken. If it does execute at runtime, it should be gated behind detection or offline checks, similar to how other adapters only fetch when offline !== true.


⚠️ Potential Issues

3. inferDroidProviderFromModel over-matches OpenAI (src/adapter/droid/parser.ts)

if (/(?:^|-)gpt-|chatgpt|(?:^|-)o\d/u.test(model)) {
    return 'openai';
}

The (?:^|-)o\d branch matches any model whose name contains -o followed by a digit (e.g., a hypothetical vertex-o2-custom). Consider anchoring it more tightly, e.g. /(?:^|-)o[1-9]\d*(?:-|$)/u to require it to be a standalone segment.


4. Session deduplication keeps earliest timestamp, not latest (src/adapter/droid/parser.ts)

for (const entry of entryGroups.flat().sort((a, b) => compareStrings(a.timestamp, b.timestamp))) {
    if (processedSessions.has(entry.sessionId)) continue;
    processedSessions.add(entry.sessionId);
    entries.push(entry);   // ← keeps the first (oldest)
}

*.settings.json files accumulate token counts over a session's lifetime. If the same sessionId ever appears more than once across comma-separated directories (e.g., a user symlinks or copies sessions), the entry with the smallest token total (earliest snapshot) would be kept. Sorting descending and keeping the first — i.e., the most recent state — is likely the more correct behavior.


5. discoverDroidSettingsFiles — non-deterministic ordering (src/adapter/droid/paths.ts)

The unit test asserts an exact file order:

await expect(discoverDroidSettingsFiles()).resolves.toEqual([
    fixture.getPath('a.settings.json'),
    fixture.getPath('nested/b.settings.json'),
]);

collectFilesRecursive's traversal order is filesystem-dependent. If the underlying readdir returns nested/ before a.settings.json (some Linux filesystems use hash ordering), the test will fail. Consider sorting the returned array, or using toEqual(expect.arrayContaining([...])) in the test.


🔍 Minor / Nits

6. toNonNegativeInteger silently rejects 0 (src/adapter/droid/parser.ts)

function toNonNegativeInteger(value: unknown): number {
    if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
        return 0;  // ← 0 is also treated as invalid
    }
    ...
}

The name implies 0 is valid, but the condition is value <= 0. This is intentional (skip zero-token entries), but renaming to toPositiveInteger or adding a comment would prevent confusion.


7. extractDroidModelFromLine scans only first 500 lines (src/adapter/droid/parser.ts)

const lines = readResult.value.split(/\r?\n/u).slice(0, 500);

This is a reasonable performance guard, but it's undocumented. For very active sessions with verbose JSONL output, the Model: line might appear later. A brief inline comment explaining the bound would help.


✅ What's Done Well

  • The normalizeDroidModelName handles all the gnarly casing/bracket/dot edge cases cleanly; the test cases cover the known formats.
  • The x-ai / x_ai / grok provider normalization from the last commit is correct.
  • Worker-thread integration mirrors the existing Codex/Amp pattern correctly.
  • getSettingsTimestamp gracefully falls back from providerLockTimestampmtime.
  • detectAllAgents correctly uses Promise.allSettled so a Droid detection failure doesn't block the other adapters.

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
Owner Author

@coderabbitai review it

@ryoppippi
Copy link
Copy Markdown
Owner Author

@cubic-dev-ai review it

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented May 17, 2026

@cubic-dev-ai review it

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

@ryoppippi Sure, kicking off a new review of this PR now!

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 17, 2026

Open in StackBlitz

ccusage

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

@ccusage/ccusage-darwin-arm64

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

@ccusage/ccusage-darwin-x64

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

@ccusage/ccusage-linux-arm64

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

@ccusage/ccusage-linux-x64

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

@ccusage/ccusage-win32-arm64

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

@ccusage/ccusage-win32-x64

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

commit: 667a2fd

Remove the Droid-specific pricing macro so importing the adapter for schema generation or tests does not trigger a pricing dataset fetch. Offline Droid reports now follow the existing no-network behavior and return zero calculated cost unless pricing is supplied by the shared fetcher context.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review it

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

@ryoppippi Sure, kicking off another review of this PR now!

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 17, 2026

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 53.4ms 53.4ms 1.00x 0.03 MiB/s 0.03 MiB/s
claude session --offline --json 0.00 MiB 54.1ms 53.1ms 1.02x 0.03 MiB/s 0.03 MiB/s
codex daily --offline --json 0.00 MiB 54.5ms 53.1ms 1.03x 0.02 MiB/s 0.02 MiB/s
codex session --offline --json 0.00 MiB 54.3ms 53.3ms 1.02x 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.367s 1.355s 1.01x 754.29 MiB/s 760.92 MiB/s
codex --offline --json 1.01 GiB 1.616s 1.605s 1.01x 637.86 MiB/s 642.30 MiB/s

Package size

Package artifact Base PR Delta Ratio
packed ccusage-*.tgz 67.58 KiB 69.31 KiB +1.73 KiB 0.98x

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.

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: 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/src/adapter/droid/pricing.ts`:
- Around line 22-45: providerPrefixes currently returns only native prefixes so
createDroidModelCandidates misses keys like openrouter/<provider>/...; update
providerPrefixes (used by createDroidModelCandidates and calculateDroidCost) to
include OpenRouter variants by returning both the native prefix and an
`openrouter/<nativePrefix>` variant for each provider (e.g., for 'google' return
['google/', 'vertex_ai/', 'openrouter/google/', 'openrouter/vertex_ai/'], for
generic providers include `openrouter/${provider}/` alongside `${provider}/`),
leaving 'unknown' as []. Ensure createDroidModelCandidates continues to map
those prefixes into candidate strings.
🪄 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: a8ddd7ea-8096-431c-83a4-813ced9ae051

📥 Commits

Reviewing files that changed from the base of the PR and between 7044eb7 and 29273fa.

📒 Files selected for processing (3)
  • apps/ccusage/src/adapter/droid/pricing.ts
  • apps/ccusage/test/snapshots/cli-output/all-agent-daily-json.txt
  • apps/ccusage/test/snapshots/cli-output/droid-direct-daily-json.txt
✅ Files skipped from review due to trivial changes (2)
  • apps/ccusage/test/snapshots/cli-output/droid-direct-daily-json.txt
  • apps/ccusage/test/snapshots/cli-output/all-agent-daily-json.txt

Comment thread apps/ccusage/src/adapter/droid/pricing.ts Outdated
Add OpenRouter-prefixed provider aliases when Droid usage records already identify the underlying provider. This lets the existing LiteLLM lookup path find prices published under OpenRouter without adding any separate pricing data fetch or macro layer.

Cover the fallback with an in-source pricing test so the adapter keeps matching the shared pricing behavior.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review it

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

@ryoppippi Sure, kicking off another review of PR #1028 now!

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

Merge the current main Rust CLI migration into the Droid PR and replace the old TypeScript Droid adapter with a Rust implementation.

The Rust adapter discovers *.settings.json files from DROID_SESSIONS_DIR or ~/.factory/sessions, normalizes Droid model/provider names, falls back to sidecar JSONL model hints, includes thinking tokens in total-token accounting, and prices thinking tokens as output-side usage.

Also wires Droid into direct reports, all-agent reports, progress labels, config schema, CLI parsing, README, and docs.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Updated this PR to merge current main and port Droid support to the Rust CLI implementation.

This now wires Droid into direct reports and all-agent reports, reads Droid *.settings.json files from DROID_SESSIONS_DIR/default ~/.factory/sessions, preserves thinking tokens in total-token accounting, and keeps Droid docs/config-schema integration.

Local validation passed:

  • nix develop --command cargo test --manifest-path rust/Cargo.toml --workspace droid
  • nix develop --command pnpm run format
  • nix develop --command cargo test --manifest-path rust/Cargo.toml --workspace
  • nix develop --command pnpm typecheck
  • nix develop --command pnpm run test

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find Rust files related to droid
fd -e rs | xargs grep -l -i "droid" 2>/dev/null
echo "---"
fd "droid" --type f

Length of output: 338


🏁 Script executed:

#!/bin/bash
# Check what changed in this PR vs main for Rust files
git diff origin/main...HEAD --name-only | grep -E "\.(rs|toml)$" | head -50
echo "---"
git diff origin/main...HEAD --name-only | head -80

Length of output: 1569


🏁 Script executed:

#!/bin/bash
cat rust/crates/ccusage/src/adapter/droid.rs

Length of output: 20783


🏁 Script executed:

#!/bin/bash
cat rust/crates/ccusage/src/adapter/mod.rs
cat rust/crates/ccusage/src/adapter/all.rs

Length of output: 33127


🏁 Script executed:

#!/bin/bash
cat rust/crates/ccusage/src/cli.rs | grep -A 30 -B 5 -i "droid"
echo "==="
cat rust/crates/ccusage/src/main.rs | grep -A 10 -B 5 -i "droid"
echo "==="
cat rust/crates/ccusage/src/config.rs | grep -A 10 -B 5 -i "droid"
echo "==="
cat rust/crates/ccusage/src/progress.rs | grep -A 10 -B 5 -i "droid"
echo "==="
# Check if there are snapshot tests or integration tests for droid in Rust
fd -e rs | xargs grep -l -i "droid" | grep -i test

Length of output: 11461


🏁 Script executed:

#!/bin/bash
# Check the deduplication logic - does it keep oldest or latest?
grep -n "seen_sessions\|processedSessions\|sort_by_key\|dedup" rust/crates/ccusage/src/adapter/droid.rs
echo "==="
# Check the infer_droid_provider logic in Rust for the false-positive o\d issue
grep -n -A 10 "infer_droid_provider" rust/crates/ccusage/src/adapter/droid.rs
echo "==="
# Check agent_label for droid in all.rs
grep -n "droid\|agent_label" rust/crates/ccusage/src/adapter/all.rs | head -30

Length of output: 1821


🏁 Script executed:

#!/bin/bash
# Check config and config_schema for droid
grep -n -A 10 -B 3 "droid" rust/crates/ccusage/src/config.rs
echo "==="
grep -n -A 5 -B 3 "droid" rust/crates/ccusage/src/config_schema.rs
echo "==="
# Check agent_label function fully
grep -n -A 15 "fn agent_label" rust/crates/ccusage/src/adapter/all.rs
echo "==="
# Check how infer_droid_provider handles the o\d case specifically
grep -n -A 8 "starts_with\('o'\)" rust/crates/ccusage/src/adapter/droid.rs

Length of output: 1512


@ryoppippi The Rust port is well-structured and mirrors the TypeScript adapter cleanly. Good test coverage and all three previously-flagged high-priority issues (OpenRouter candidates, eager prefetch, deterministic ordering) are handled correctly in the Rust implementation. Here are my findings:


Rust Port Review

🐛 Bug

agent_label is missing the "droid" arm (rust/crates/ccusage/src/adapter/all.rs, Line 792)

The agent_label function maps "pi""pi-agent" but has no explicit arm for "droid", so it falls through to the catch-all _ => agent, rendering lowercase droid in terminal output instead of Droid:

fn agent_label(agent: &str) -> &str {
    match agent {
        "all" => "All",
        "claude" => "Claude",
        "codex" => "Codex",
        "opencode" => "OpenCode",
        "amp" => "Amp",
        "pi" => "pi-agent",
        _ => agent,       // ← "droid" falls here
    }
}

Note that agent_display_name in cli.rs does have "droid" => "Droid" correctly — only agent_label in all.rs is missing it.

Fix:

        "amp" => "Amp",
+       "droid" => "Droid",
        "pi" => "pi-agent",

⚠️ Carried-Over Issue

Session deduplication keeps the oldest snapshot (rust/crates/ccusage/src/adapter/droid.rs, Line 133)

The Rust port carries over the same deduplication logic: sort ascending → keep first = keep oldest. Since .settings.json files accumulate token counts over a session's lifetime, the most recent snapshot should be kept instead. Sorting descending before the dedup loop would fix both the TS and Rust implementations:

- parsed.sort_by_key(|entry| entry.timestamp);
+ parsed.sort_by_key(|entry| std::cmp::Reverse(entry.timestamp));

✅ What's Done Correctly in the Rust Port

  • OpenRouter candidatesprovider_prefixes() in Rust correctly emits openrouter/anthropic/, openrouter/openai/, etc. ✅
  • No eager pricing prefetchPricingMap::load is called inside run(), not at module level ✅
  • Deterministic file orderingfiles.sort() is called before processing, unlike the TypeScript version ✅
  • infer_droid_provider_from_model — The Rust version uses starts_with('o') && get(1).is_some_and(u8::is_ascii_digit), which is already tighter than the TypeScript (?:^|-)o\d regex (only matches at the string start, not mid-segment). Still matches broadly for any model like o3-custom, but the blast radius is much smaller ✅ (reduced risk)
  • Thinking tokens wiredextra_total_tokens: entry.reasoning_tokens correctly adds thinking tokens to total counts ✅
  • Test coverage — 4 tests covering normalization, settings loading, sidecar JSONL fallback, and the thinking-token total ✅
✅ 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/crates/ccusage/src/adapter/all.rs (1)

792-801: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit display label for Droid in all-agent output.

Line 800 falls back to raw "droid" because agent_label is missing a "droid" arm, so output casing is inconsistent with other agents.

💡 Suggested fix
 fn agent_label(agent: &str) -> &str {
     match agent {
         "all" => "All",
         "claude" => "Claude",
         "codex" => "Codex",
         "opencode" => "OpenCode",
         "amp" => "Amp",
+        "droid" => "Droid",
         "pi" => "pi-agent",
         _ => agent,
     }
 }
🤖 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 792 - 801, The
agent_label function currently omits a case for "droid", causing raw "droid" to
be used inconsistently; update the match in agent_label to include a "droid" arm
that returns the desired display label "Droid" (i.e., add `"droid" => "Droid",`)
alongside the existing arms so all-agent output uses a consistent cased label.
🤖 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/droid.rs`:
- Around line 133-141: The current loop (parsed.sort_by_key, seen_sessions
HashSet and for entry in parsed) keeps the oldest snapshot per session_id
because parsed is sorted ascending; change the dedup strategy to retain the
newest snapshot by sorting parsed in descending order by timestamp (so the
first-seen per session_id is the latest) or alternatively build a Map keyed by
session_id that replaces stored entries when a newer timestamp is found; then
push the retained newest entries through to_loaded_entry(entry, tz.as_ref(),
pricing) as before.

In `@rust/crates/ccusage/src/output.rs`:
- Around line 77-84: The non-compact table footer is still computing "Total
Tokens" without including extra_total_tokens, causing a mismatch with
totals_json; update the footer total calculation to add extra_total (the sum
computed as let extra_total = rows.iter().map(|row|
row.extra_total_tokens).sum::<u64>();) into the total shown in the footer (where
input + output + cache_create + cache_read is currently used), and ensure the
same inclusion is applied wherever the non-compact/footer total is computed so
it matches the totals_json/totalTokens value.

---

Outside diff comments:
In `@rust/crates/ccusage/src/adapter/all.rs`:
- Around line 792-801: The agent_label function currently omits a case for
"droid", causing raw "droid" to be used inconsistently; update the match in
agent_label to include a "droid" arm that returns the desired display label
"Droid" (i.e., add `"droid" => "Droid",`) alongside the existing arms so
all-agent output uses a consistent cased label.
🪄 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: 716b710f-def1-4123-be52-95f187c9a040

📥 Commits

Reviewing files that changed from the base of the PR and between 29273fa and dc6cb2d.

📒 Files selected for processing (22)
  • apps/ccusage/README.md
  • apps/ccusage/config-schema.json
  • docs/.vitepress/config.ts
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • rust/crates/ccusage/src/adapter/all.rs
  • rust/crates/ccusage/src/adapter/amp.rs
  • rust/crates/ccusage/src/adapter/droid.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 skipped from review due to trivial changes (6)
  • rust/crates/ccusage/src/adapter/opencode/parser.rs
  • docs/.vitepress/config.ts
  • docs/guide/index.md
  • docs/guide/getting-started.md
  • apps/ccusage/README.md
  • rust/crates/ccusage/src/claude_loader.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/ccusage/config-schema.json
  • docs/guide/environment-variables.md

Comment thread rust/crates/ccusage/src/adapter/droid.rs
Comment thread rust/crates/ccusage/src/output.rs Outdated
@github-actions
Copy link
Copy Markdown

ccusage performance comparison

PR SHA: dc6cb2d1b329
Base SHA: 7971969e2157

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 7971969e2157 486.1ms 547.5ms 36.1ms 3
PR pkg.pr.new dc6cb2d1b329 801.1ms 507.4ms 35.9ms 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: 7971969e2157; PR package: dc6cb2d1b329. 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 685.9ms 688.3ms 1.00x 1.47 GiB/s 1.46 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 885.8ms 896.6ms 0.99x 1.14 GiB/s 1.12 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.9ms 1.48 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 648.7ms 1.55 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 758.9ms 1.33 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 889.4ms 1.13 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 858.9ms 1.17 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 840.3ms 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 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 33.4ms 33.8ms 0.99x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 33.3ms 33.7ms 0.99x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 33.0ms 32.3ms 1.02x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 32.7ms 32.6ms 1.00x 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 689.7ms 685.3ms 1.01x 1.46 GiB/s 1.47 GiB/s
codex --offline --json 1.01 GiB 892.3ms 880.1ms 1.01x 1.13 GiB/s 1.14 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.83 KiB 8.88 KiB +0.05 KiB 0.99x
installed native package binary 3032.24 KiB 3096.24 KiB +64.00 KiB 0.98x
Rust release binary rust/target/release/ccusage - 2763.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

ccusage performance comparison

PR SHA: dc6cb2d1b329
Base SHA: 7971969e2157

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 7971969e2157 595.9ms 444.9ms 35.5ms 3
PR pkg.pr.new dc6cb2d1b329 729.2ms 594.4ms 33.9ms 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: 7971969e2157; PR package: dc6cb2d1b329. 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 685.9ms 683.0ms 1.00x 1.47 GiB/s 1.47 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 873.3ms 870.5ms 1.00x 1.15 GiB/s 1.16 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 672.5ms 1.50 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 638.0ms 1.58 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 729.6ms 1.38 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 853.3ms 1.18 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 835.9ms 1.20 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 827.4ms 1.22 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 31.8ms 4.1ms 7.81x 0.05 MiB/s 0.38 MiB/s
claude session --offline --json 0.00 MiB 31.4ms 4.1ms 7.67x 0.05 MiB/s 0.38 MiB/s
codex daily --offline --json 0.00 MiB 31.5ms 3.8ms 8.31x 0.03 MiB/s 0.23 MiB/s
codex session --offline --json 0.00 MiB 31.5ms 3.7ms 8.51x 0.03 MiB/s 0.23 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 675.7ms 712.4ms 0.95x 1.49 GiB/s 1.41 GiB/s
codex --offline --json 1.01 GiB 862.1ms 829.6ms 1.04x 1.17 GiB/s 1.21 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.83 KiB 8.88 KiB +0.05 KiB 0.99x
installed native package binary 3032.24 KiB 3096.24 KiB +64.00 KiB 0.98x
Rust release binary rust/target/release/ccusage - 2763.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.

…pter

# Conflicts:
#	apps/ccusage/README.md
#	apps/ccusage/config-schema.json
#	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/environment-variables.md
#	docs/guide/getting-started.md
#	docs/guide/index.md
#	docs/index.md
#	rust/crates/ccusage/src/adapter/all.rs
#	rust/crates/ccusage/src/adapter/mod.rs
#	rust/crates/ccusage/src/cli.rs
#	rust/crates/ccusage/src/config.rs
#	rust/crates/ccusage/src/config_schema.rs
#	rust/crates/ccusage/src/output.rs
#	rust/crates/ccusage/src/types.rs
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the session deduplication fix (Reverse sort)
grep -n -B 3 -A 15 "seen_sessions\|sort_by_key\|Reverse\|dedup" rust/crates/ccusage/src/adapter/droid.rs
echo "==="
# Check for new regression test
grep -n -A 30 "archive\|duplicate\|latest.*snapshot\|snapshot.*latest" rust/crates/ccusage/src/adapter/droid.rs | head -60
echo "==="
# Also check TS side for the same fix
grep -n -A 5 "sort\|processedSessions\|compareStrings" apps/ccusage/src/adapter/droid/parser.ts 2>/dev/null | head -30

Length of output: 3165


@ryoppippi The fix is confirmed ✅

The deduplication now iterates with .into_iter().rev() after ascending sort (Line 140), which correctly visits the latest timestamp first and discards all older duplicates — functionally equivalent to a descending sort. The regression test at Line 603 (keeps_latest_snapshot_for_duplicate_session_ids) validates the exact archive/current scenario: archive snapshot (May 1, 10/20 tokens) is discarded and the current snapshot (May 2, 100/200 tokens) is retained.

All previously flagged issues are now resolved:

Issue Status
agent_label missing "droid" arm ✅ Fixed
Session deduplication keeps oldest snapshot ✅ Fixed (.rev() + regression test)
OpenRouter candidate prefixes ✅ Correct in Rust port
Eager pricing prefetch ✅ Gated at runtime in Rust port
Deterministic file ordering files.sort() in Rust port

🐰 ✨

…pter

# Conflicts:
#	rust/crates/ccusage/src/cli.rs
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Merged latest main into the Droid PR and resolved the current CLI help conflict using the namespace-only root help from main plus Droid.

Local checks passed:

  • pnpm run format
  • cargo test --manifest-path rust/Cargo.toml -p ccusage droid
  • pre-push oxfmt/clippy

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check CLI help for droid in Rust
grep -n -A 20 "fn droid_help\|droid.*help\|help.*droid" rust/crates/ccusage/src/cli.rs | head -60
echo "==="
# Check is_command/is_agent_command/agent_report_supported for droid
grep -n -B 2 -A 10 "is_agent_command\|agent_report_supported\|agent_display_name" rust/crates/ccusage/src/cli.rs | head -80
echo "==="
# Check the root help text includes droid
grep -n -A 30 "fn help\b\|fn print_help\|root_help\|general_help\|fn help_text" rust/crates/ccusage/src/cli.rs | head -60
echo "==="
# Check TS side - OpenRouter candidates still missing?
grep -n "openrouter" apps/ccusage/src/adapter/droid/pricing.ts 2>/dev/null
echo "==="
# Check TS dedup ordering
grep -n -A 5 "compareStrings\|processedSessions\|sort\b" apps/ccusage/src/adapter/droid/parser.ts 2>/dev/null | head -30
echo "==="
# Check what changed vs previous commit
git log --oneline -5

Length of output: 7535


@ryoppippi The merge looks clean — the CLI help conflict was resolved correctly and Droid is properly integrated alongside all other agents in the Rust CLI.

✅ Merge Integration — All Good

  • is_agent_command, agent_report_supported, agent_display_name, root help text, and droid_report_help routing are all correctly wired ✅
  • The session-dedup fix (fix(droid): keep latest session snapshot) from the previous commit is preserved ✅

⚠️ TypeScript-side issues still outstanding

Two issues from the original review have not been addressed on the TypeScript side:

1. createDroidModelCandidates — no OpenRouter-prefixed candidates (apps/ccusage/src/adapter/droid/pricing.ts)

openrouter/* prefixes are still absent from providerPrefixes() in the TS adapter. Models stored under openrouter/anthropic/... in LiteLLM will produce $0.00 costs. The Rust port already handles this correctly — the same fix should be ported back to TypeScript:

case 'anthropic':
    return ['anthropic/', 'openrouter/anthropic/'];
case 'openai':
    return ['openai/', 'openrouter/openai/'];
case 'xai':
    return ['xai/', 'openrouter/x-ai/'];

2. Session deduplication keeps the oldest snapshot (apps/ccusage/src/adapter/droid/parser.ts)

The ascending sort + first-wins dedup still exists on the TS side. The Rust fix (.into_iter().rev()) should be mirrored — sorting descending before the dedup loop in TypeScript as well.

Would you like to address these on the TypeScript side before merging, or track them as a follow-up issue?

@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Addressed the remaining Droid table-total review by making the non-compact footer use totals_json.totalTokens, so extra_total_tokens are included consistently with JSON and row totals.

Local checks passed:

  • pnpm run format
  • cargo test --manifest-path rust/Cargo.toml -p ccusage droid
  • cargo test --manifest-path rust/Cargo.toml -p ccusage totals_json_includes_extra_tokens_in_total
  • pre-push oxfmt/clippy

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the table-total fix they mention - non-compact footer using totals_json.totalTokens
grep -n -B 5 -A 15 "totals_json\|totalTokens\|extra_total_tokens\|non.compact\|footer" rust/crates/ccusage/src/adapter/droid.rs | head -80
echo "==="
# Check the specific test they ran
grep -n -A 20 "totals_json_includes_extra_tokens\|includes_extra_tokens" rust/crates/ccusage/src/adapter/droid.rs | head -50
echo "==="
# Check the TS side - were the two outstanding TS issues (OpenRouter candidates + dedup) addressed?
grep -n "openrouter" apps/ccusage/src/adapter/droid/pricing.ts 2>/dev/null
echo "==="
grep -n -A 5 "sort\|compareStrings\|processedSessions" apps/ccusage/src/adapter/droid/parser.ts 2>/dev/null | head -30
echo "==="
# Check the all.rs table rendering for extra_total_tokens/totalTokens in footer
grep -n -B 3 -A 10 "total_tokens\|extra_total\|footer\|totals_json" rust/crates/ccusage/src/adapter/all.rs | head -60
echo "==="
# What changed in the latest commit
git log --oneline -3
git diff HEAD~1..HEAD --stat | head -20

Length of output: 5307


🏁 Script executed:

#!/bin/bash
# Look at the output.rs changes specifically
grep -n -B 5 -A 20 "totals_json\|totalTokens\|extra_total\|non.compact\|footer\|compact" rust/crates/ccusage/src/output.rs | head -120
echo "==="
# Check the test they mentioned
grep -n -A 30 "totals_json_includes_extra_tokens" rust/crates/ccusage/src/output.rs
echo "==="
# See what the actual diff was for the latest commit
git show HEAD --stat
git show HEAD -- rust/crates/ccusage/src/output.rs | head -100

Length of output: 8021


@ryoppippi The fix is confirmed ✅

The root cause was exactly right: print_usage_table's non-compact footer was computing input + output + cache_create + cache_read inline (skipping extra_total_tokens), while totals_json already computed totalTokens correctly with the extra sum. The fix reads totalTokens from totals_json so both code paths stay in sync — and the regression test (100 + 50 + 10 + 5 + 7 = 172) locks that in.


⚠️ Two TypeScript-side issues still outstanding

These were called out in a previous review and don't appear to have been addressed yet:

1. createDroidModelCandidates — no OpenRouter-prefixed candidates (apps/ccusage/src/adapter/droid/pricing.ts)

case 'anthropic':
    return ['anthropic/', 'openrouter/anthropic/'];
case 'openai':
    return ['openai/', 'openrouter/openai/'];
case 'xai':
    return ['xai/', 'openrouter/x-ai/'];

2. Session deduplication keeps the oldest snapshot (apps/ccusage/src/adapter/droid/parser.ts)

Sort descending before the dedup loop (mirrors the Rust .rev() fix already merged).

Would you like to address these before merging, or shall I open a follow-up issue to track them?

@github-actions
Copy link
Copy Markdown

ccusage performance comparison

PR SHA: 40f6a04e5466
Base SHA: 2a02e16f966b

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 2a02e16f966b 464.6ms 639.1ms 35.4ms 3
PR pkg.pr.new 40f6a04e5466 758.5ms 468.5ms 35.6ms 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: 2a02e16f966b; PR package: 40f6a04e5466. 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 697.4ms 715.6ms 0.97x 1.44 GiB/s 1.41 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 896.3ms 880.2ms 1.02x 1.12 GiB/s 1.14 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 691.5ms 1.46 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 671.0ms 1.50 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 729.6ms 1.38 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 860.7ms 1.17 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 839.2ms 1.20 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 826.0ms 1.22 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 33.4ms 4.9ms 6.81x 0.05 MiB/s 0.32 MiB/s
claude session --offline --json 0.00 MiB 32.4ms 4.9ms 6.62x 0.05 MiB/s 0.32 MiB/s
codex daily --offline --json 0.00 MiB 32.8ms 4.6ms 7.15x 0.03 MiB/s 0.19 MiB/s
codex session --offline --json 0.00 MiB 33.4ms 4.5ms 7.38x 0.03 MiB/s 0.19 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 691.2ms 737.6ms 0.94x 1.46 GiB/s 1.37 GiB/s
codex --offline --json 1.01 GiB 901.3ms 836.6ms 1.08x 1.12 GiB/s 1.20 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.11 KiB 9.16 KiB +0.05 KiB 0.99x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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

ccusage performance comparison

PR SHA: 40f6a04e5466
Base SHA: 2a02e16f966b

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 2a02e16f966b 490.5ms 470.4ms 35.0ms 3
PR pkg.pr.new 40f6a04e5466 625.7ms 497.7ms 34.0ms 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: 2a02e16f966b; PR package: 40f6a04e5466. 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 697.7ms 708.2ms 0.99x 1.44 GiB/s 1.42 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 861.8ms 858.7ms 1.00x 1.17 GiB/s 1.17 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 686.8ms 1.47 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 672.8ms 1.50 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 719.8ms 1.40 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 852.3ms 1.18 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 821.8ms 1.23 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 823.0ms 1.22 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 32.2ms 32.9ms 0.98x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 32.7ms 32.5ms 1.01x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 32.0ms 32.2ms 0.99x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 32.4ms 32.2ms 1.01x 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 689.4ms 695.4ms 0.99x 1.46 GiB/s 1.45 GiB/s
codex --offline --json 1.01 GiB 877.5ms 860.6ms 1.02x 1.15 GiB/s 1.17 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.11 KiB 9.16 KiB +0.05 KiB 0.99x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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

ccusage performance comparison

PR SHA: 9103c183a328
Base SHA: 2a02e16f966b

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 2a02e16f966b 529.4ms 654.2ms 35.3ms 3
PR pkg.pr.new 9103c183a328 860.4ms 843.8ms 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: 2a02e16f966b; PR package: 9103c183a328. 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 744.8ms 703.6ms 1.06x 1.35 GiB/s 1.43 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 901.3ms 891.9ms 1.01x 1.12 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 682.0ms 1.48 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 673.6ms 1.49 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 743.9ms 1.35 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 877.8ms 1.15 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 838.6ms 1.20 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 835.4ms 1.21 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 33.2ms 5.0ms 6.62x 0.05 MiB/s 0.31 MiB/s
claude session --offline --json 0.00 MiB 33.3ms 5.0ms 6.63x 0.05 MiB/s 0.31 MiB/s
codex daily --offline --json 0.00 MiB 33.0ms 4.8ms 6.82x 0.03 MiB/s 0.18 MiB/s
codex session --offline --json 0.00 MiB 32.2ms 4.7ms 6.92x 0.03 MiB/s 0.18 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 689.7ms 749.2ms 0.92x 1.46 GiB/s 1.34 GiB/s
codex --offline --json 1.01 GiB 890.4ms 839.8ms 1.06x 1.13 GiB/s 1.20 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.11 KiB 9.15 KiB +0.05 KiB 0.99x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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

ccusage performance comparison

PR SHA: 9103c183a328
Base SHA: 2a02e16f966b

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 2a02e16f966b 572.7ms 495.8ms 35.1ms 3
PR pkg.pr.new 9103c183a328 713.4ms 713.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: 2a02e16f966b; PR package: 9103c183a328. 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 704.9ms 707.7ms 1.00x 1.43 GiB/s 1.42 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 890.2ms 870.6ms 1.02x 1.13 GiB/s 1.16 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 698.8ms 1.44 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 658.8ms 1.53 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 729.4ms 1.38 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 879.2ms 1.15 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 845.7ms 1.19 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 828.0ms 1.22 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 33.3ms 32.9ms 1.01x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 33.0ms 33.0ms 1.00x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 32.8ms 32.2ms 1.02x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 32.3ms 32.9ms 0.98x 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 696.8ms 698.3ms 1.00x 1.44 GiB/s 1.44 GiB/s
codex --offline --json 1.01 GiB 888.5ms 879.3ms 1.01x 1.13 GiB/s 1.14 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.11 KiB 9.15 KiB +0.05 KiB 0.99x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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.

ryoppippi added 2 commits May 19, 2026 19:18
…pter

# Conflicts:
#	apps/ccusage/README.md
#	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/index.md
#	docs/index.md
#	rust/crates/ccusage/src/adapter/all.rs
#	rust/crates/ccusage/src/cli.rs
#	rust/crates/ccusage/src/config_schema.rs
Regenerate and format the config schema after syncing the branch with the latest main changes. The pre-commit schema hook rewrites enum arrays using the current oxfmt output, so this keeps the checked-in schema aligned with the generated artifact.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Synced this PR with latest main after #1031 was merged, resolved the Kimi/Droid conflicts, regenerated/formatted config schema, and pushed follow-up commits.

Local checks:

  • pnpm run format
  • cargo test --manifest-path rust/Cargo.toml -p ccusage droid
  • cargo test --manifest-path rust/Cargo.toml -p ccusage totals_json_includes_extra_tokens_in_total
  • pre-push hook: oxfmt, eslint root/docs, clippy

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check TS openrouter candidates
grep -n "openrouter" apps/ccusage/src/adapter/droid/pricing.ts 2>/dev/null
echo "=== TS dedup ==="
grep -n -A 5 "sort\|compareStrings\|processedSessions" apps/ccusage/src/adapter/droid/parser.ts 2>/dev/null | head -30
echo "=== Rust all.rs Kimi+Droid ==="
grep -n "droid\|kimi\|gemini\|copilot" rust/crates/ccusage/src/adapter/all.rs | head -40
echo "=== agent_label ==="
grep -n -A 25 "fn agent_label" rust/crates/ccusage/src/adapter/all.rs
echo "=== config_schema Kimi+Droid ==="
grep -n "droid\|kimi\|Kimi\|Droid" rust/crates/ccusage/src/config_schema.rs | head -30
echo "=== TS config loader ==="
grep -n "droid\|kimi" apps/ccusage/src/config-loader-tokens.ts 2>/dev/null | head -20
echo "=== recent commits ==="
git log --oneline -5
echo "=== changed files ==="
git diff origin/main...HEAD --name-only | head -40

Length of output: 4142


🏁 Script executed:

#!/bin/bash
# Find TS droid adapter files
fd -e ts | xargs grep -l -i "droid" 2>/dev/null | grep -v node_modules | grep -v dist
echo "==="
# Read pricing.ts directly
cat apps/ccusage/src/adapter/droid/pricing.ts 2>/dev/null || echo "FILE NOT FOUND"
echo "==="
# Read parser.ts dedup section directly
grep -n -B 2 -A 8 "processedSessions\|compareStrings\|sort.*timestamp\|sort.*compare" apps/ccusage/src/adapter/droid/parser.ts 2>/dev/null | head -50
echo "==="
# Check if the TS summary.rs equivalent includes extra_total_tokens
grep -n "extra_total\|reasoning" apps/ccusage/src/adapter/droid/parser.ts 2>/dev/null | head -20

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find all droid-related TS files
fd -e ts "droid" 2>/dev/null | grep -v node_modules | grep -v dist
echo "==="
fd -e ts | xargs grep -l "droid\|Droid" 2>/dev/null | grep -v node_modules | grep -v dist | grep -v ".snap"
echo "==="
# Check the overall TS adapter structure
tree apps/ccusage/src/adapter/ 2>/dev/null || ls -la apps/ccusage/src/adapter/ 2>/dev/null

Length of output: 79


🏁 Script executed:

#!/bin/bash
# Check project structure
ls apps/ccusage/src/ 2>/dev/null || echo "no apps/ccusage/src"
echo "==="
fd -e ts "parser" 2>/dev/null | grep -v node_modules | grep -v dist | head -20
echo "==="
fd -e ts "pricing" 2>/dev/null | grep -v node_modules | grep -v dist | head -20
echo "==="
# Check what directories exist in apps/
ls apps/ 2>/dev/null
echo "==="
ls 2>/dev/null | head -20

Length of output: 264


🏁 Script executed:

#!/bin/bash
# Check full apps/ccusage structure
find apps/ccusage -type f -name "*.ts" | grep -i droid | grep -v node_modules | grep -v dist | head -30
echo "==="
find apps/ccusage -type d | head -30
echo "==="
# Check src directory listing more carefully
ls -la apps/ccusage/src/
echo "==="
# Maybe there's a different source path - check if it's a flat structure
find apps/ccusage -name "*.ts" -not -path "*/node_modules/*" -not -path "*/dist/*" | head -40

Length of output: 914


🏁 Script executed:

#!/bin/bash
# Check packages directory
ls packages/
echo "==="
find packages -name "*.ts" | grep -i droid | grep -v node_modules | grep -v dist | head -20
echo "==="
find packages -type d | head -30
echo "==="
# Check if droid adapter TS files are somewhere else - maybe ccusage is Rust only
find . -name "*.ts" | grep -i droid | grep -v node_modules | grep -v dist | head -20
echo "==="
# Check if there are .snap files that reference droid (test snapshots)
find . -name "*.snap" | xargs grep -l "droid\|Droid" 2>/dev/null | grep -v node_modules | head -10
echo "==="
# Check if the app imports from packages
head -30 apps/ccusage/src/cli.ts

Length of output: 920


@ryoppippi The Kimi/#1031 merge is clean — conflicts were resolved correctly and no regressions introduced. Here's the status:

✅ All Previously Flagged Issues Resolved

Issue Status
agent_label missing "droid" arm ✅ Fixed (Line 988)
Session deduplication keeps oldest snapshot ✅ Fixed (.rev() + regression test)
OpenRouter candidate prefixes ✅ Correct in Rust port
Eager pricing prefetch ✅ Gated at runtime in Rust
Deterministic file ordering files.sort() in place
Table totals missing extra_total_tokens ✅ Fixed via totals_json.totalTokens

✅ Kimi Integration Looks Clean

  • Kimi is wired into all.rs consistently alongside Droid, Copilot, and Gemini ✅
  • agent_label correctly maps "kimi""Kimi" (Line 995) ✅
  • DroidConfig and KimiConfig both properly defined in config_schema.rs with no conflicts ✅
  • The two previously flagged TypeScript-side concerns (OpenRouter candidates, session dedup) are no longer applicable — apps/ccusage is a thin Node.js launcher for the native Rust binary; no TypeScript adapter source files exist in the current branch ✅

The PR looks good to go! 🐰 🎉

[approve]

@github-actions
Copy link
Copy Markdown

ccusage performance comparison

PR SHA: 26b5c1f3d903
Base SHA: 1d2ef9bc75e6

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 1d2ef9bc75e6 506.3ms 452.3ms 34.4ms 3
PR pkg.pr.new 26b5c1f3d903 664.7ms 444.6ms 33.6ms 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: 1d2ef9bc75e6; PR package: 26b5c1f3d903. 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 687.5ms 692.5ms 0.99x 1.46 GiB/s 1.45 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 872.2ms 872.1ms 1.00x 1.15 GiB/s 1.15 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 679.6ms 1.48 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 662.5ms 1.52 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 725.2ms 1.39 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 867.9ms 1.16 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 860.9ms 1.17 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 825.5ms 1.22 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 32.1ms 4.8ms 6.72x 0.05 MiB/s 0.32 MiB/s
claude session --offline --json 0.00 MiB 32.1ms 4.8ms 6.63x 0.05 MiB/s 0.32 MiB/s
codex daily --offline --json 0.00 MiB 31.4ms 4.6ms 6.90x 0.03 MiB/s 0.19 MiB/s
codex session --offline --json 0.00 MiB 32.1ms 4.5ms 7.20x 0.03 MiB/s 0.19 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 684.8ms 726.8ms 0.94x 1.47 GiB/s 1.39 GiB/s
codex --offline --json 1.01 GiB 869.6ms 818.5ms 1.06x 1.16 GiB/s 1.23 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.15 KiB 9.20 KiB +0.05 KiB 0.99x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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

ccusage performance comparison

PR SHA: 26b5c1f3d903
Base SHA: 1d2ef9bc75e6

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 1d2ef9bc75e6 669.0ms 475.1ms 33.9ms 3
PR pkg.pr.new 26b5c1f3d903 718.1ms 467.2ms 33.2ms 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: 1d2ef9bc75e6; PR package: 26b5c1f3d903. 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 687.6ms 678.6ms 1.01x 1.46 GiB/s 1.48 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 842.4ms 841.9ms 1.00x 1.20 GiB/s 1.20 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 671.6ms 1.50 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 647.0ms 1.56 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 704.3ms 1.43 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 821.2ms 1.23 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 802.4ms 1.25 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 805.7ms 1.25 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 31.1ms 31.3ms 0.99x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 30.8ms 30.7ms 1.00x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 31.4ms 31.2ms 1.01x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 30.3ms 30.8ms 0.99x 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 677.3ms 698.4ms 0.97x 1.49 GiB/s 1.44 GiB/s
codex --offline --json 1.01 GiB 857.5ms 876.6ms 0.98x 1.17 GiB/s 1.15 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.15 KiB 9.20 KiB +0.05 KiB 0.99x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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.

ryoppippi added 2 commits May 19, 2026 19:41
…pter

# Conflicts:
#	apps/ccusage/README.md
#	docs/guide/all-reports.md
#	docs/guide/config-files.md
#	docs/guide/configuration.md
#	docs/guide/index.md
#	docs/index.md
#	rust/crates/ccusage/src/adapter/all.rs
#	rust/crates/ccusage/src/cli.rs
#	rust/crates/ccusage/src/config_schema.rs
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Synced this Droid PR with the latest main after #1019 landed, resolved the OpenClaw/Kimi integration conflicts, regenerated/formatted the config schema, and pushed the updates.

Local checks run:

  • pnpm --dir apps/ccusage run generate:schema
  • pnpm run format
  • cargo test --manifest-path rust/Cargo.toml -p ccusage droid
  • cargo test --manifest-path rust/Cargo.toml -p ccusage totals_json_includes_extra_tokens_in_total
  • pre-push hook: oxfmt, docs eslint, clippy

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check agent_label still correct after merge with `#1019`
grep -n -A 30 "fn agent_label" rust/crates/ccusage/src/adapter/all.rs
echo "==="
# Check droid + openclaw + kimi wired in all.rs
grep -n "droid\|kimi\|openclaw\|copilot\|gemini" rust/crates/ccusage/src/adapter/all.rs | head -40
echo "==="
# Check session dedup fix still in place
grep -n -B 3 -A 12 "seen_sessions\|sort_by_key\|Reverse\|rev()" rust/crates/ccusage/src/adapter/droid.rs | head -50
echo "==="
# Check config_schema has droid, kimi, openclaw
grep -n "droid\|kimi\|openclaw\|Droid\|Kimi\|OpenClaw" rust/crates/ccusage/src/config_schema.rs | head -30
echo "==="
# Check cli.rs still has droid wired
grep -n "droid\|Droid" rust/crates/ccusage/src/cli.rs | head -20
echo "==="
# Check config.rs is_agent_command
grep -n -A 3 "is_agent_command" rust/crates/ccusage/src/config.rs | head -15
echo "==="
# Check progress.rs UsageLoadAgent::Droid
grep -n "Droid\|droid" rust/crates/ccusage/src/progress.rs | head -10
echo "==="
# Check extra_total_tokens fix in output.rs still intact
grep -n "extra_total\|totals_json\|totalTokens" rust/crates/ccusage/src/output.rs | head -20
echo "==="
# Check recent commits
git log --oneline -5
echo "==="
# Changed files vs main
git diff origin/main...HEAD --name-only | head -50

Unrecoverable error: Failed to clone repository


@ryoppippi I wasn't able to clone the repository to manually inspect the OpenClaw conflict resolution, so I'm kicking off an incremental review to cover the new diff from the #1019 merge. Based on all previous review rounds, everything was in good shape — the incremental review will focus on verifying the OpenClaw integration doesn't introduce any regressions for Droid.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/guide/environment-variables.md (1)

15-31: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the debug grep pattern to include DROID.

This page now documents DROID_SESSIONS_DIR, but the debugging snippet later still won’t surface it. Add DROID to that regex so troubleshooting examples stay aligned with the documented variables.

Suggested doc fix
-env | grep -E "CLAUDE|CODEX|OPENCODE|AMP|HERMES|PI_AGENT|GOOSE|KILO|COPILOT|GEMINI|CCUSAGE|LOG_LEVEL"
+env | grep -E "CLAUDE|CODEX|OPENCODE|AMP|DROID|HERMES|PI_AGENT|GOOSE|KILO|COPILOT|GEMINI|CCUSAGE|LOG_LEVEL"
🤖 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 `@docs/guide/environment-variables.md` around lines 15 - 31, The debug grep
pattern used later in the document doesn't include DROID, so DROID_SESSIONS_DIR
won't be matched; update the troubleshooting/debugging regex to include "DROID"
(alongside existing tokens) so examples will surface DROID_SESSIONS_DIR, and
verify the pattern references DROID_SESSIONS_DIR by name in the example usage
(e.g., the export/grep demonstration) to keep docs and debug snippet aligned.
🤖 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.

Outside diff comments:
In `@docs/guide/environment-variables.md`:
- Around line 15-31: The debug grep pattern used later in the document doesn't
include DROID, so DROID_SESSIONS_DIR won't be matched; update the
troubleshooting/debugging regex to include "DROID" (alongside existing tokens)
so examples will surface DROID_SESSIONS_DIR, and verify the pattern references
DROID_SESSIONS_DIR by name in the example usage (e.g., the export/grep
demonstration) to keep docs and debug snippet aligned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dcd76bd2-2e6c-4131-abc5-a98e19d77e9e

📥 Commits

Reviewing files that changed from the base of the PR and between 66c660f and 667a2fd.

📒 Files selected for processing (22)
  • apps/ccusage/README.md
  • apps/ccusage/config-schema.json
  • 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/droid/index.md
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • docs/index.md
  • rust/crates/ccusage/src/adapter/all.rs
  • rust/crates/ccusage/src/adapter/droid.rs
  • rust/crates/ccusage/src/adapter/mod.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
✅ Files skipped from review due to trivial changes (9)
  • docs/guide/droid/index.md
  • docs/guide/config-files.md
  • docs/guide/all-reports.md
  • docs/index.md
  • docs/guide/index.md
  • apps/ccusage/README.md
  • docs/guide/codex/index.md
  • docs/guide/configuration.md
  • docs/guide/getting-started.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • rust/crates/ccusage/src/adapter/mod.rs
  • rust/crates/ccusage/src/progress.rs
  • rust/crates/ccusage/src/config.rs
  • rust/crates/ccusage/src/main.rs
  • docs/.vitepress/config.ts
  • apps/ccusage/config-schema.json
  • rust/crates/ccusage/src/adapter/all.rs
  • rust/crates/ccusage/src/config_schema.rs
  • rust/crates/ccusage/src/cli.rs
  • rust/crates/ccusage/src/adapter/droid.rs

@github-actions
Copy link
Copy Markdown

ccusage performance comparison

PR SHA: 667a2fd69546
Base SHA: 687be1906691

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 687be1906691 767.9ms 590.0ms 32.6ms 3
PR pkg.pr.new 667a2fd69546 6.729s 704.0ms 33.1ms 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: 687be1906691; PR package: 667a2fd69546. 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 690.6ms 686.9ms 1.01x 1.46 GiB/s 1.47 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 832.8ms 823.7ms 1.01x 1.21 GiB/s 1.22 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 678.0ms 1.48 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 644.6ms 1.56 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 705.7ms 1.43 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 817.1ms 1.23 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 777.1ms 1.30 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 791.8ms 1.27 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 31.8ms 4.7ms 6.82x 0.05 MiB/s 0.33 MiB/s
claude session --offline --json 0.00 MiB 31.4ms 4.7ms 6.66x 0.05 MiB/s 0.33 MiB/s
codex daily --offline --json 0.00 MiB 29.9ms 4.2ms 7.04x 0.03 MiB/s 0.20 MiB/s
codex session --offline --json 0.00 MiB 29.6ms 4.2ms 7.01x 0.03 MiB/s 0.20 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 678.9ms 715.8ms 0.95x 1.48 GiB/s 1.41 GiB/s
codex --offline --json 1.01 GiB 832.3ms 799.7ms 1.04x 1.21 GiB/s 1.26 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.29 KiB 9.33 KiB +0.04 KiB 1.00x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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

ccusage performance comparison

PR SHA: 667a2fd69546
Base SHA: 687be1906691

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 687be1906691 514.7ms 557.1ms 34.5ms 3
PR pkg.pr.new 667a2fd69546 3.335s 626.1ms 36.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: 687be1906691; PR package: 667a2fd69546. 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 698.5ms 691.0ms 1.01x 1.44 GiB/s 1.46 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 883.1ms 868.1ms 1.02x 1.14 GiB/s 1.16 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 679.7ms 1.48 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 659.1ms 1.53 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 726.7ms 1.39 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 855.0ms 1.18 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 835.2ms 1.21 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 831.0ms 1.21 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 32.7ms 32.7ms 1.00x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 32.6ms 33.0ms 0.99x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 32.5ms 32.4ms 1.00x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 32.5ms 32.7ms 0.99x 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 700.2ms 696.6ms 1.01x 1.44 GiB/s 1.45 GiB/s
codex --offline --json 1.01 GiB 879.9ms 863.5ms 1.02x 1.14 GiB/s 1.17 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 9.29 KiB 9.33 KiB +0.04 KiB 1.00x
installed native package binary 3224.24 KiB 3224.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2891.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.

@ryoppippi ryoppippi merged commit 1f72988 into main May 19, 2026
37 checks passed
@ryoppippi ryoppippi deleted the codex/droid-usage-adapter branch May 19, 2026 18:57
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