Skip to content

feat(kilo): add Kilo usage adapter#1032

Merged
ryoppippi merged 7 commits into
mainfrom
codex/kilo-usage-adapter
May 19, 2026
Merged

feat(kilo): add Kilo usage adapter#1032
ryoppippi merged 7 commits into
mainfrom
codex/kilo-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@1032 ccusage kilo daily --help

Summary

  • add a Kilo CLI adapter that reads local kilo.db SQLite usage rows
  • wire Kilo into focused/all-agent reports, config namespaces, and generated schema
  • document KILO_DATA_DIR and add CLI JSON snapshot coverage

Validation

  • pnpm --filter ccusage run generate:schema
  • pnpm run format
  • pnpm typecheck
  • pnpm run test
  • pnpm --filter docs build

Summary by cubic

Adds a Rust Kilo adapter that reads local kilo.db, adds kilo daily/monthly/session reports, and includes Kilo in unified views. Costs use Kilo’s recorded value (including 0) or fall back to LiteLLM pricing; reasoning tokens are counted in totals.

  • New Features

    • Rust adapter loads kilo.db read-only from ~/.local/share/kilo or KILO_DATA_DIR (supports comma-separated paths)
    • Commands: kilo daily, kilo monthly, kilo session; included in help, --all detection, and unified reports
    • Pricing: honors recorded cost (including explicit zero), otherwise computes from tokens; includes reasoning tokens in total-token accounting
    • Config/schema/docs: adds kilo namespace and Kilo guide
  • Bug Fixes

    • Scoped fallback row-id de-duplication per source database; skips messages without timestamps

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Kilo as a supported data source with daily, monthly, and session reports
    • Kilo usage data now included in unified reports alongside other supported sources
    • New KILO_DATA_DIR environment variable for configuring Kilo data directory location
  • Documentation

    • Added Kilo data source guide with configuration and usage examples
    • Updated all documentation to reference Kilo support across configuration and command examples

Review Change Stack

Add a Kilo CLI adapter that reads local kilo.db SQLite usage rows from KILO_DATA_DIR or the default data directory.

Wire Kilo into focused and all-agent reports, config namespaces, generated schema, and docs so it behaves like the other local usage sources.

Use recorded costs when Kilo provides them, fall back to LiteLLM pricing otherwise, and skip timestamp-less rows to avoid assigning usage to the wrong day.

Add adapter tests plus CLI JSON snapshot coverage for the new command surface.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review it

@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 complete support for Kilo, a new coding agent CLI, to ccusage. Changes span configuration schema, CLI command parsing, a new SQLite-based data adapter with cost calculation, all-agents aggregation integration, and comprehensive user documentation covering setup, usage, and troubleshooting.

Changes

Kilo Agent Integration

Layer / File(s) Summary
Configuration Schema & Types
apps/ccusage/config-schema.json, rust/crates/ccusage/src/config_schema.rs
Adds kilo configuration block with defaults and command-specific options (daily/monthly/session) to both JSON schema definitions and Rust config structs, following the existing agent pattern.
CLI Command Parsing & Validation
rust/crates/ccusage/src/cli.rs, rust/crates/ccusage/src/config.rs
Adds kilo variant to Command enum, implements parse_kilo_command parser, and updates agent validation/display/help functions to recognize kilo daily/monthly/session reports with support checks.
Kilo SQLite Adapter & Cost Calculation
rust/crates/ccusage/src/adapter/kilo.rs
Implements kilo adapter with SQLite message loading from configurable KILO_DATA_DIR paths, timestamp normalization, entry validation, cost calculation via stored values or LiteLLM token pricing, and unit tests for deduplication and filtering.
Adapter Integration & Dispatch
rust/crates/ccusage/src/adapter/mod.rs, rust/crates/ccusage/src/adapter/all.rs, rust/crates/ccusage/src/progress.rs, rust/crates/ccusage/src/main.rs
Exports kilo module, integrates kilo row loading into all-agents session/daily aggregation paths, adds UsageLoadAgent::Kilo variant with display mapping, and routes Command::Kilo dispatch to adapter::kilo::run.
User Documentation
docs/guide/kilo/index.md, docs/.vitepress/config.ts, apps/ccusage/README.md, docs/guide/*.md, docs/index.md
Adds dedicated Kilo guide page (SQLite setup, environment config, report types, cost behavior, troubleshooting), updates README examples and VitePress sidebar, and integrates Kilo references into environment-variable, configuration, and supported-sources documentation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ryoppippi/ccusage#1058: Refactors the Rust schema generator that this PR extends for kilo configuration block generation.
  • ryoppippi/ccusage#1023: Earlier agent integration (Copilot) that established the same adapter/CLI/config patterns now applied to kilo.
  • ryoppippi/ccusage#977: Introduced the native Rust CLI and adapter dispatch system that kilo now integrates into.

Suggested labels

enhancement

Poem

🐰 A fluffy agent hops into view—
Kilo's here, with SQLite and costs too!
From databases deep, the usage flows bright,
Pricing from tokens, daily and nightly.
bounces happily

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.67% 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
Title check ✅ Passed The title clearly and concisely describes the main change: adding a Kilo usage adapter. It directly reflects the primary feature addition across the changeset.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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/kilo-usage-adapter

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 de7db27 Commit Preview URL

Branch Preview URL
May 19 2026, 03:10 PM

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the PR details and files changed
gh pr view 1032 --repo ryoppippi/ccusage --json title,body,files,additions,deletions

echo "=== PR DIFF ==="
gh pr diff 1032 --repo ryoppippi/ccusage

Length of output: 61206


@ryoppippi: The Kilo adapter is well-structured and follows the existing adapter patterns closely. Here are my findings:


Review

🟡 Medium: calculateKiloCost doesn't honour explicitly-recorded zero costs

File: apps/ccusage/src/adapter/kilo/pricing.ts

if (entry.costUSD != null && entry.costUSD > 0) {
    return entry.costUSD;
}

When Kilo records cost: 0 (e.g. a free/trial model), entry.costUSD is 0 — not null — so this condition is false and the adapter falls through to LiteLLM recalculation instead of trusting the recorded zero. Consider:

if (entry.costUSD != null) {
    return entry.costUSD;   // includes 0, which is a valid recorded value
}

🟡 Minor: detectKiloSources is unnecessarily async

File: apps/ccusage/src/adapter/kilo/paths.ts

export async function detectKiloSources(kiloPath: string): Promise<boolean> {
    return hasKiloDatabase(kiloPath);   // synchronous
}

hasKiloDatabase is synchronous. The async wrapper creates a needless micro-task allocation. Other adapters expose a genuine async detect because they may touch the filesystem asynchronously, but here the body is a simple existsSync call. Either drop async, or leave it if the signature needs to match a common interface — but then it should be documented.


🟡 Minor: createConfigSchemaJson still hardcodes the agent list

File: apps/ccusage/scripts/generate-json-schema.ts, around line 241

properties: {
    claude: createAgentJsonSchema('claude', agentCommandSchemas.claude),
    codex:  createAgentJsonSchema('codex',  agentCommandSchemas.codex),
    opencode: ...,
    amp:    ...,
    pi:     ...,
    kilo:   createAgentJsonSchema('kilo', agentCommandSchemas.kilo),   // ← added
},

The PR correctly exports AGENT_CONFIG_KEYS and replaces the local AGENT_NAMES constant, but createConfigSchemaJson still hardcodes every agent name in the properties object. Each future adapter requires a manual addition here. A follow-up improvement would be to derive this object from AGENT_CONFIG_KEYS:

const agentProperties = Object.fromEntries(
    AGENT_CONFIG_KEYS.map((agent) => [agent, createAgentJsonSchema(agent, agentCommandSchemas[agent])]),
);

🔵 Nit: snapshot file missing trailing newline

File: apps/ccusage/test/snapshots/cli-output/kilo-direct-daily-json.txt

The diff ends with \ No newline at end of file. All other snapshot files in the directory should have a trailing newline — consider adding one for consistency.


🔵 Nit: Default data path is Linux XDG only

File: apps/ccusage/src/adapter/kilo/paths.ts

const DEFAULT_KILO_PATH = path.join(homedir(), '.local/share/kilo');

On macOS, Kilo may store its database under ~/Library/Application Support/kilo or a similar platform-specific location. The existing adapters (Amp, OpenCode) have the same assumption, so this may be intentional, but it's worth verifying against Kilo's actual storage convention and documenting in docs/guide/kilo/index.md if macOS uses a different path.


✅ What's done well

  • Robust deduplication across comma-separated KILO_DATA_DIR paths using message IDs.
  • normalizeTimestamp correctly handles both seconds and milliseconds timestamps.
  • it.skipIf(getSqliteDatabaseFactory() == null) guards are consistent with the rest of the test suite.
  • kiloAgents metadata field cleanly surfaces which Kilo sub-agents (e.g. build) contributed to a session.
  • AGENT_CONFIG_KEYS is now exported rather than duplicated — good cleanup.
  • Comprehensive docs with troubleshooting tips.
✅ 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: 3

🤖 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/kilo/loader.ts`:
- Around line 90-91: The fallback dedupe key currently uses rowId alone (id:
parsed.output.id ?? rowId), which can collide across different KILO_DATA_DIR
instances; change the fallback to a DB-scoped composite key by combining a
stable per-DB identifier with rowId (for example use the KILO_DATA_DIR or the DB
instance/path variable plus rowId) wherever the fallback is used (including the
id assignment in loader.ts and the similar block around lines 151-159) so that
keys are unique across multiple Kilo DBs while preserving parsed.output.id when
present.

In `@apps/ccusage/src/adapter/kilo/pricing.ts`:
- Around line 21-23: The guard currently ignores valid zero-cost records because
it checks entry.costUSD > 0; change the condition to treat any provided cost
(including 0) as authoritative by returning when entry.costUSD != null (or
typeof check) so that the code returns entry.costUSD even if it's 0 and only
falls back to recomputed pricing when costUSD is truly absent.

In `@docs/guide/kilo/index.md`:
- Around line 27-30: The fenced code block that shows the directory tree
starting with "~/.local/share/kilo/" is missing a language identifier and
triggers MD040; update that fenced block (the block containing
"~/.local/share/kilo/" and "└── kilo.db") to include a language tag such as text
(e.g., change ``` to ```text) so the markdown linter accepts it.
🪄 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: 02b28920-8303-4362-badd-89aa65e20b5e

📥 Commits

Reviewing files that changed from the base of the PR and between 4aedac0 and a789a43.

📒 Files selected for processing (25)
  • apps/ccusage/README.md
  • apps/ccusage/config-schema.json
  • apps/ccusage/scripts/generate-json-schema.ts
  • apps/ccusage/src/adapter/index.ts
  • apps/ccusage/src/adapter/kilo/index.ts
  • apps/ccusage/src/adapter/kilo/loader.ts
  • apps/ccusage/src/adapter/kilo/paths.ts
  • apps/ccusage/src/adapter/kilo/pricing.ts
  • apps/ccusage/src/adapter/kilo/schema.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/kilo-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/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • docs/guide/kilo/index.md
  • docs/index.md

Comment thread apps/ccusage/src/adapter/kilo/loader.ts Outdated
Comment thread apps/ccusage/src/adapter/kilo/pricing.ts Outdated
Comment thread docs/guide/kilo/index.md Outdated
Trust Kilo recorded costs whenever present, including explicit zero-cost rows, before falling back to LiteLLM token pricing.

Also simplify synchronous source detection and derive config schema agent properties from the configured agent key list.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai addressed the review feedback in ce44907.

  • recorded zero-cost Kilo rows are now trusted instead of recalculated
  • Kilo source detection is synchronous
  • config schema agent properties are derived from AGENT_CONFIG_KEYS
  • agent config labels now use an exhaustive switch

Validation: pnpm --filter ccusage run generate:schema, pnpm run format, pnpm --filter ccusage exec vitest run src/adapter/kilo scripts/generate-json-schema.ts test/cli-output.test.ts, pnpm typecheck.

@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
# Get the specific commit diff
gh api repos/ryoppippi/ccusage/commits/ce44907 --jq '.files[] | {filename: .filename, patch: .patch}' 2>/dev/null || \
gh api repos/ryoppippi/ccusage/commits/ce44907 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
for f in data.get('files', []):
    print('=== FILE:', f['filename'], '===')
    print(f.get('patch', '(no patch)'))
    print()
"

Length of output: 4332


@ryoppippi: All four items from the previous review are properly resolved in ce44907:

Item Status
Zero-cost rows trusted without recalculation (costUSD != null guard) ✅ Fixed + unit test added asserting calculateCostFromTokens is not called for costUSD === 0
detectKiloSources is now synchronous (boolean return, no async) ✅ Fixed + test updated to drop await …resolves
Agent config properties derived from AGENT_CONFIG_KEYS via Object.fromEntries ✅ Fixed
Exhaustive switch in getAgentConfigLabel covering all AgentName variants ✅ Fixed

The two low-priority nits (snapshot trailing newline, macOS default path) remain, but those are cosmetic and non-blocking. The implementation looks solid. Approving.

🐇 ✨

[approve]

Scope fallback row-id dedupe keys to the source Kilo database path so separate KILO_DATA_DIR databases cannot drop valid rows when a message lacks an embedded id.

Add regression coverage for fallback row-id collisions and label the Kilo docs directory-tree fence for markdown lint.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai addressed the remaining Kilo review items in 8831ab2.

  • fallback row-id dedupe keys are now scoped by DB path
  • added regression coverage for same row ids across separate Kilo DB files
  • added a language identifier to the Kilo docs directory tree fence

Validation: pnpm run format, pnpm --filter ccusage exec vitest run src/adapter/kilo scripts/generate-json-schema.ts test/cli-output.test.ts, pnpm typecheck, pnpm --filter docs build, git diff --check.

@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
gh api repos/ryoppippi/ccusage/commits/8831ab2 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
for f in data.get('files', []):
    print('=== FILE:', f['filename'], '===')
    print(f.get('patch', '(no patch)'))
    print()
"

Length of output: 2851


@ryoppippi: All three items from 8831ab2 are verified:

Item Status
Fallback row-id dedupe keys scoped by DB path (${dbPath}:${rowId}) ✅ Fixed
Regression test for same row-1 ID across two separate Kilo DB files ✅ Added — correctly asserts both messages are returned (length 2)
text language identifier on the Kilo docs directory tree fence ✅ Fixed

One small observation: the existing dedup test also received a companion update — id: 'embedded-msg-1' was added to the embedded JSON, which is the right thing to do so that test exercises the explicit-ID path and doesn't silently rely on the fallback. Nice attention to detail.

The only remaining open nit is the snapshot trailing newline (kilo-direct-daily-json.txt), but that's purely cosmetic and non-blocking. The implementation is solid. 🐇 ✨

[approve]

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 `@docs/guide/kilo/index.md`:
- Line 51: The docs state that "When Kilo records a positive cost, ccusage uses
that value", but the implementation treats any recorded non-null cost (including
0) as authoritative; update the sentence to reflect that behavior by replacing
the phrase about "positive cost" with wording like "any recorded non-null cost
(including 0)" so the docs align with the implementation.
🪄 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: b900f9aa-89c7-4b79-9ad8-eb5a00ee0d06

📥 Commits

Reviewing files that changed from the base of the PR and between ce44907 and 8831ab2.

📒 Files selected for processing (2)
  • apps/ccusage/src/adapter/kilo/loader.ts
  • docs/guide/kilo/index.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/ccusage/src/adapter/kilo/loader.ts

Comment thread docs/guide/kilo/index.md Outdated
@ryoppippi
Copy link
Copy Markdown
Owner Author

ryoppippi commented May 17, 2026

I checked the remaining CodeRabbit nit about the snapshot trailing newline. This snapshot is saved and asserted from test/cli-output.test.ts after stdout trailing newlines are stripped, so adding a trailing newline makes the target test fail.\n\nVerified:\n\nsh\npnpm --filter ccusage exec vitest run test/cli-output.test.ts\n# fails if the snapshot has a trailing newline\n\n\npnpm --filter ccusage exec vitest run test/cli-output.test.ts -u\n# restores the current snapshot shape and passes\n\n\nI am leaving this as-is.

@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 54.8ms 54.4ms 1.01x 0.03 MiB/s 0.03 MiB/s
claude session --offline --json 0.00 MiB 54.3ms 54.5ms 1.00x 0.03 MiB/s 0.03 MiB/s
codex daily --offline --json 0.00 MiB 54.0ms 54.8ms 0.98x 0.02 MiB/s 0.02 MiB/s
codex session --offline --json 0.00 MiB 54.1ms 54.7ms 0.99x 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.388s 1.367s 1.02x 742.75 MiB/s 754.38 MiB/s
codex --offline --json 1.01 GiB 1.641s 1.625s 1.01x 628.36 MiB/s 634.25 MiB/s

Package size

Package artifact Base PR Delta Ratio
packed ccusage-*.tgz 67.58 KiB 68.48 KiB +0.90 KiB 0.99x

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.

@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@1032

@ccusage/ccusage-darwin-arm64

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

@ccusage/ccusage-darwin-x64

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

@ccusage/ccusage-linux-arm64

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

@ccusage/ccusage-linux-x64

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

@ccusage/ccusage-win32-arm64

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

@ccusage/ccusage-win32-x64

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

commit: de7db27

Align the Kilo cost calculation guide with the loader behavior introduced in the zero-cost fix.

The adapter treats any non-null recorded cost as authoritative, including a recorded 0, before falling back to LiteLLM pricing.
@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

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get current PR state and latest commits
gh pr view 1032 --repo ryoppippi/ccusage --json title,state,commits,headRefName

echo "=== RECENT COMMITS ==="
gh api repos/ryoppippi/ccusage/pulls/1032/commits 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
for c in data:
    print(c['sha'][:8], c['commit']['message'][:80])
"

echo "=== LATEST DIFF (after 8831ab2) ==="
# Get any commits after 8831ab2
gh api repos/ryoppippi/ccusage/compare/8831ab2...HEAD --jq '.commits[].sha' 2>/dev/null || true

# Fetch overall PR diff to check current state
gh pr diff 1032 --repo ryoppippi/ccusage 2>/dev/null | tail -200

Length of output: 12183


@ryoppippi: The latest commit f6ad4a33 is a clean, docs-only update — the Cost Calculation section in docs/guide/kilo/index.md now correctly states:

"When Kilo records a cost (including 0), ccusage uses that value."

This matches the code behavior introduced in ce44907c exactly. Nothing else changed.

Full review status:

Item Status
Zero-cost rows trusted without recalculation ✅ Resolved (ce44907)
detectKiloSources synchronous ✅ Resolved (ce44907)
Agent config properties from AGENT_CONFIG_KEYS ✅ Resolved (ce44907)
Exhaustive switch in getAgentConfigLabel ✅ Resolved (ce44907)
Fallback dedupe keys scoped by DB path ✅ Resolved (8831ab2)
Docs directory tree fence identifier ✅ Resolved (8831ab2)
Docs aligned with zero-cost behavior ✅ Resolved (f6ad4a3)
Snapshot trailing newline ✅ Intentionally left as-is (stripping is part of the test harness)

The PR is in great shape. Approving. 🐇 ✨

[approve]

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

The Rust adapter discovers kilo.db from KILO_DATA_DIR or ~/.local/share/kilo, reads SQLite messages read-only, preserves recorded costs, supports comma-separated data roots, deduplicates messages, and includes reasoning tokens in total-token accounting.

Also wires Kilo 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 Kilo support to the Rust CLI implementation.

This now wires Kilo into direct reports and all-agent reports, reads kilo.db from KILO_DATA_DIR/default ~/.local/share/kilo, preserves recorded costs, supports comma-separated data roots, deduplicates messages, and includes reasoning tokens in total-token accounting.

Local validation passed:

  • nix develop --command cargo test --manifest-path rust/Cargo.toml --workspace kilo
  • 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

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== RECENT COMMITS ==="
gh api repos/ryoppippi/ccusage/pulls/1032/commits 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
for c in data:
    print(c['sha'][:8], c['commit']['message'][:100])
"

echo ""
echo "=== LATEST PR DIFF ==="
gh pr diff 1032 --repo ryoppippi/ccusage 2>/dev/null

Length of output: 46969


🏁 Script executed:

#!/bin/bash
echo "=== parse_*_command config usage in cli.rs ==="
rg -n "_config|config\." rust/crates/ccusage/src/cli.rs -A 2 -B 2 | head -120

echo ""
echo "=== config_schema agent_config lookup pattern ==="
rg -n "agent_config\|get_agent\|config\.kilo\|config\.amp\|config\.pi\|config\.copilot" rust/crates/ccusage/src/cli.rs | head -40

echo ""
echo "=== How other agents use config in parse functions ==="
ast-grep --pattern 'fn parse_$_(
    $_: &mut ArgParser,
    mut $_: SharedArgs,
    $$$
) -> $_ {
    $$$
}' rust/crates/ccusage/src/cli.rs 2>/dev/null | head -200

echo ""
echo "=== ConfigContext definition ==="
rg -n "ConfigContext" rust/crates/ccusage/src/ -A 5 | head -60

echo ""
echo "=== agent config application ==="
rg -n "apply_agent_config\|agent_config\|kilo.*config\|config.*kilo" rust/crates/ccusage/src/ -A 3 | head -80

Length of output: 8585


@ryoppippi: The Rust port looks well-structured and consistent with the existing adapter pattern. Here's what I found:


Review of d0531ce (Rust CLI port)

✅ What's correct

Item Status
CostMode::Auto uses unwrap_or_elseSome(0.0) is honored, no recalculation
Fallback dedupe key scoped to format!("{}:{row_id}", db_path.display())
_config in parse_kilo_command is intentional — apply_config_to_shared is called upstream before parse_command, so kilo.defaults.* and kilo.commands.* options from the config file are applied (consistent with amp, opencode, copilot which all use _config for the same reason — only codex/pi need it for speed/path options)
normalize_timestamp matches the TypeScript logic (scale seconds → millis when < 1_000_000_000_000)
is_agent_command and agent_report_supported updated in both cli.rs and config.rs

🔵 Nit: missing Rust regression test for cross-DB fallback dedup

The TypeScript suite has a test asserting that two separate DB files sharing the same row_id but no embedded message ID each produce a distinct entry (fallback keys differ by dbPath). The Rust deduplicates_kilo_messages_across_data_dirs test only covers the case where entries have an embedded id: "embedded-msg-1". The no-embedded-ID path is exercised by the fallback key logic but has no Rust counterpart test:

#[test]
fn cross_db_fallback_ids_are_not_deduplicated() {
    // two DBs, same row_id "row-1", no "id" field in JSON
    // → different db_path scopes → both entries returned
    assert_eq!(entries.len(), 2);
}

🔵 Nit: model_candidates ordering differs from TypeScript

The Rust implementation sorts candidates alphabetically before iterating:

candidates.sort();   // "anthropic/claude-sonnet" < "claude-sonnet" alphabetically
candidates.dedup();

The TypeScript implementation tries the bare model name first, then provider/model. This means for a model like gpt-4o with provider openai, Rust tries gpt-4o and openai/gpt-4o in alphabetical order (gpt-4o first there), while for claude-sonnet + anthropic, Rust would try anthropic/claude-sonnet first. This is inconsequential when only one candidate exists in the pricing map, but worth keeping in mind if both forms are ever present.


The implementation is solid — the two nits above are non-blocking. Approving.

🐇 ✨

[approve]

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 `@rust/crates/ccusage/src/adapter/kilo.rs`:
- Around line 358-360: The current candidates.sort(); candidates.dedup();
reorders pricing keys lexicographically and loses intended precedence; instead
preserve the original insertion order and remove later duplicates so the first
candidate wins. Replace the sort+dedup with an order-preserving de-dup: create a
HashSet (std::collections::HashSet) for seen keys and retain only the first
occurrence (e.g., candidates.retain(|c| seen.insert(c.clone()))), ensuring the
existing relative order of entries remains so precedence like "provider/model"
vs "model" is respected; update imports if needed.
🪄 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: ed1632d9-b61b-4cb0-a151-0628e225ef46

📥 Commits

Reviewing files that changed from the base of the PR and between f6ad4a3 and d0531ce.

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

Comment on lines +358 to +360
candidates.sort();
candidates.dedup();
candidates
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep pricing candidate precedence explicit instead of lexicographic

Line 358 currently reorders candidates before first-match pricing lookup. That can change which price key wins (model vs provider/model) based on string ordering, not intended precedence, and produce inconsistent cost calculations.

Suggested fix
 fn model_candidates(model: &str, provider: Option<&str>) -> Vec<String> {
     let mut candidates = vec![model.to_string()];
     if let Some(provider) = provider
         .map(normalize_provider)
         .filter(|provider| provider != "unknown" && provider != "kilo")
     {
         candidates.push(format!("{provider}/{model}"));
     }
-    candidates.sort();
-    candidates.dedup();
-    candidates
+    let mut seen = HashSet::new();
+    candidates
+        .into_iter()
+        .filter(|candidate| seen.insert(candidate.clone()))
+        .collect()
 }
🤖 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/kilo.rs` around lines 358 - 360, The current
candidates.sort(); candidates.dedup(); reorders pricing keys lexicographically
and loses intended precedence; instead preserve the original insertion order and
remove later duplicates so the first candidate wins. Replace the sort+dedup with
an order-preserving de-dup: create a HashSet (std::collections::HashSet) for
seen keys and retain only the first occurrence (e.g., candidates.retain(|c|
seen.insert(c.clone()))), ensuring the existing relative order of entries
remains so precedence like "provider/model" vs "model" is respected; update
imports if needed.

@github-actions
Copy link
Copy Markdown

ccusage performance comparison

PR SHA: d0531cec08cc
Base SHA: edb439221918

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 edb439221918 629.5ms 894.0ms 35.5ms 3
PR pkg.pr.new d0531cec08cc 778.6ms 748.6ms 34.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: edb439221918; PR package: d0531cec08cc. Both run through bunx -p <pkg.pr.new URL> ccusage using the warmed Bun install cache from package runner startup, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
bunx -p <pkg> ccusage claude --offline --json 1.01 GiB 676.0ms 696.2ms 0.97x 1.49 GiB/s 1.45 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 870.7ms 876.2ms 0.99x 1.16 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 674.6ms 1.49 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 650.7ms 1.55 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 730.4ms 1.38 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 868.7ms 1.16 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 846.7ms 1.19 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 835.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.2ms 31.8ms 1.01x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 31.7ms 32.0ms 0.99x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 30.9ms 31.5ms 0.98x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 31.7ms 31.3ms 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 684.0ms 687.7ms 0.99x 1.47 GiB/s 1.46 GiB/s
codex --offline --json 1.01 GiB 868.3ms 867.8ms 1.00x 1.16 GiB/s 1.16 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.90 KiB 8.94 KiB +0.04 KiB 1.00x
installed native package binary 3096.24 KiB 3096.24 KiB +0.00 KiB 1.00x
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: d0531cec08cc
Base SHA: edb439221918

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 edb439221918 670.6ms 491.7ms 33.7ms 3
PR pkg.pr.new d0531cec08cc 728.3ms 918.7ms 34.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: edb439221918; PR package: d0531cec08cc. 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 671.5ms 686.0ms 0.98x 1.50 GiB/s 1.47 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 871.0ms 838.1ms 1.04x 1.16 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 677.1ms 1.49 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 648.8ms 1.55 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 715.7ms 1.41 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 822.6ms 1.22 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 797.2ms 1.26 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 812.9ms 1.24 GiB/s 1

Committed fixture performance

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

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

Command Input Base median PR median PR vs base Base throughput PR throughput
claude daily --offline --json 0.00 MiB 30.6ms 3.9ms 7.89x 0.05 MiB/s 0.40 MiB/s
claude session --offline --json 0.00 MiB 30.9ms 3.9ms 7.85x 0.05 MiB/s 0.39 MiB/s
codex daily --offline --json 0.00 MiB 30.4ms 3.5ms 8.60x 0.03 MiB/s 0.24 MiB/s
codex session --offline --json 0.00 MiB 29.6ms 3.5ms 8.44x 0.03 MiB/s 0.24 MiB/s

Large real-world-shaped fixture performance

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

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

Command Input Base median PR median PR vs base Base throughput PR throughput
claude --offline --json 1.01 GiB 675.6ms 722.4ms 0.94x 1.49 GiB/s 1.39 GiB/s
codex --offline --json 1.01 GiB 824.5ms 804.5ms 1.02x 1.22 GiB/s 1.25 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.90 KiB 8.94 KiB +0.04 KiB 1.00x
installed native package binary 3096.24 KiB 3096.24 KiB +0.00 KiB 1.00x
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.

ryoppippi added 2 commits May 19, 2026 16:09
# Conflicts:
#	apps/ccusage/README.md
#	apps/ccusage/config-schema.json
#	docs/.vitepress/config.ts
#	docs/guide/all-reports.md
#	docs/guide/config-files.md
#	docs/guide/configuration.md
#	docs/guide/environment-variables.md
#	docs/guide/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/main.rs
#	rust/crates/ccusage/src/progress.rs
Keep the generated config schema formatting in sync with the pre-commit generator after merging origin/main into the Kilo adapter branch.
@ryoppippi
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Pushed the Kilo branch after merging the latest origin/main and resolving conflicts against current native Rust all-agent, Copilot/Gemini, config schema, pricing, and docs changes.

Changes in this push:

  • preserved the Kilo Rust adapter and wired it into current CLI/all-report/config schema/progress code
  • integrated Kilo docs/navigation/environment-variable references with the current Copilot/Gemini docs
  • regenerated and formatted the config schema

Local validation:

  • `nix develop --command cargo test --manifest-path rust/Cargo.toml -p ccusage kilo`
  • `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`

Pre-push validation also passed clippy/oxfmt/eslint hooks.

@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
echo "=== RECENT COMMITS ==="
gh api repos/ryoppippi/ccusage/pulls/1032/commits 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
for c in data:
    print(c['sha'][:8], c['commit']['message'][:100])
"

echo ""
echo "=== LATEST PR DIFF ==="
gh pr diff 1032 --repo ryoppippi/ccusage 2>/dev/null

Length of output: 51133


@ryoppippi: The merge conflict resolution looks clean throughout. Here's my verification:

Diff review (055332b + de7db27)

Area Status
Kilo inserted consistently in all docs tables/feature lists (pi-agent → Kilo → Copilot → Gemini ordering)
config-schema.json KiloConfig/KiloCommandsConfig definitions added; kilo property placed alphabetically (gemini → kilo → opencode)
CostMode::Auto uses unwrap_or_elseSome(0.0) honored, no recalculation
Dedup logic: data.message.id is always Some(…) so both the embedded-ID and fallback db_path:row_id paths enter the seen check correctly
normalize_timestamp matches TypeScript heuristic (< 1_000_000_000_000× 1000)
UsageLoadAgent::Kilo inserted between Pi and Copilot in progress tracking, consistent with the docs table ordering
load_kilo_rows in all.rs follows the same pattern as load_copilot_rows/load_gemini_rows
parse_kilo_command in cli.rs uses _config (intentional — apply_config_to_shared runs upstream), consistent with amp, copilot, gemini
rust/crates/ccusage/src/config.rs is_agent_command includes "kilo"

The two minor nits from the previous Rust review (model_candidates alphabetical sort vs TypeScript bare-model-first order; no cross-DB fallback-ID Rust test) remain open, but both are non-blocking cosmetic items with no correctness impact given how the pricing map is structured in practice.

The PR is in great shape. Approving. 🐇 ✨

[approve]

@github-actions
Copy link
Copy Markdown

ccusage performance comparison

PR SHA: de7db2700b2c
Base SHA: 1727a60f7df5

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 1727a60f7df5 420.5ms 535.4ms 32.2ms 3
PR pkg.pr.new de7db2700b2c 838.1ms 730.2ms 31.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: 1727a60f7df5; PR package: de7db2700b2c. 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 694.0ms 755.5ms 0.92x 1.45 GiB/s 1.33 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 813.3ms 823.9ms 0.99x 1.24 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 672.3ms 1.50 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 665.0ms 1.51 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 697.0ms 1.44 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 816.2ms 1.23 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 784.4ms 1.28 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 803.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 29.7ms 30.1ms 0.99x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 30.1ms 29.9ms 1.01x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 29.4ms 30.1ms 0.98x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 29.7ms 30.0ms 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 684.8ms 677.8ms 1.01x 1.47 GiB/s 1.49 GiB/s
codex --offline --json 1.01 GiB 806.7ms 807.9ms 1.00x 1.25 GiB/s 1.25 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.96 KiB 9.00 KiB +0.04 KiB 1.00x
installed native package binary 3160.24 KiB 3160.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2827.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: de7db2700b2c
Base SHA: 1727a60f7df5

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 1727a60f7df5 601.1ms 769.2ms 35.6ms 3
PR pkg.pr.new de7db2700b2c 735.4ms 475.5ms 36.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: 1727a60f7df5; PR package: de7db2700b2c. 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 732.6ms 705.2ms 1.04x 1.37 GiB/s 1.43 GiB/s
bunx -p <pkg> ccusage codex --offline --json 1.01 GiB 874.3ms 882.8ms 0.99x 1.15 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 684.1ms 1.47 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 664.7ms 1.51 GiB/s 1
claude --offline --json Workspace release binary 1.01 GiB 749.4ms 1.34 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 865.3ms 1.16 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 833.9ms 1.21 GiB/s 1
codex --offline --json Workspace release binary 1.01 GiB 833.6ms 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 32.5ms 4.7ms 6.88x 0.05 MiB/s 0.33 MiB/s
claude session --offline --json 0.00 MiB 32.5ms 5.0ms 6.55x 0.05 MiB/s 0.31 MiB/s
codex daily --offline --json 0.00 MiB 32.2ms 4.5ms 7.11x 0.03 MiB/s 0.19 MiB/s
codex session --offline --json 0.00 MiB 32.4ms 4.5ms 7.13x 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 706.5ms 732.6ms 0.96x 1.43 GiB/s 1.37 GiB/s
codex --offline --json 1.01 GiB 884.3ms 844.7ms 1.05x 1.14 GiB/s 1.19 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 8.96 KiB 9.00 KiB +0.04 KiB 1.00x
installed native package binary 3160.24 KiB 3160.24 KiB +0.00 KiB 1.00x
Rust release binary rust/target/release/ccusage - 2827.68 KiB - -

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant