feat(kilo): add Kilo usage adapter#1032
Conversation
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.
|
@coderabbitai review it |
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis 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. ChangesKilo Agent Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Deploying with
|
| 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 |
🧩 Analysis chain🏁 Script executed: Length of output: 61206
Review🟡 Medium:
|
There was a problem hiding this comment.
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
📒 Files selected for processing (25)
apps/ccusage/README.mdapps/ccusage/config-schema.jsonapps/ccusage/scripts/generate-json-schema.tsapps/ccusage/src/adapter/index.tsapps/ccusage/src/adapter/kilo/index.tsapps/ccusage/src/adapter/kilo/loader.tsapps/ccusage/src/adapter/kilo/paths.tsapps/ccusage/src/adapter/kilo/pricing.tsapps/ccusage/src/adapter/kilo/schema.tsapps/ccusage/src/adapter/types.tsapps/ccusage/src/commands/index.tsapps/ccusage/src/config-loader-tokens.tsapps/ccusage/test/cli-output.test.tsapps/ccusage/test/snapshots/cli-output/kilo-direct-daily-json.txtdocs/.vitepress/config.tsdocs/guide/all-reports.mddocs/guide/claude/index.mddocs/guide/codex/index.mddocs/guide/config-files.mddocs/guide/configuration.mddocs/guide/environment-variables.mddocs/guide/getting-started.mddocs/guide/index.mddocs/guide/kilo/index.mddocs/index.md
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.
|
@coderabbitai addressed the review feedback in ce44907.
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 |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 4332
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.
|
@coderabbitai addressed the remaining Kilo review items in 8831ab2.
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 |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 2851
One small observation: the existing dedup test also received a companion update — The only remaining open nit is the snapshot trailing newline ( [approve] |
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
apps/ccusage/src/adapter/kilo/loader.tsdocs/guide/kilo/index.md
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/ccusage/src/adapter/kilo/loader.ts
|
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\n |
ccusage performance comparisonThis compares the PR build against the base branch build on the same CI runner. Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Package size
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. |
ccusage
@ccusage/ccusage-darwin-arm64
@ccusage/ccusage-darwin-x64
@ccusage/ccusage-linux-arm64
@ccusage/ccusage-linux-x64
@ccusage/ccusage-win32-arm64
@ccusage/ccusage-win32-x64
commit: |
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.
|
@coderabbitai review it |
|
@cubic-dev-ai review it |
@ryoppippi I have started the AI code review. It will take a few minutes to complete. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 12183
This matches the code behavior introduced in Full review status:
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.
|
@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:
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 46969 🏁 Script executed: Length of output: 8585
Review of d0531ce (Rust CLI port)✅ What's correct
🔵 Nit: missing Rust regression test for cross-DB fallback dedupThe TypeScript suite has a test asserting that two separate DB files sharing the same #[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:
|
There was a problem hiding this comment.
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
📒 Files selected for processing (20)
apps/ccusage/README.mdapps/ccusage/config-schema.jsondocs/.vitepress/config.tsdocs/guide/all-reports.mddocs/guide/claude/index.mddocs/guide/codex/index.mddocs/guide/config-files.mddocs/guide/configuration.mddocs/guide/environment-variables.mddocs/guide/getting-started.mddocs/guide/index.mddocs/index.mdrust/crates/ccusage/src/adapter/all.rsrust/crates/ccusage/src/adapter/kilo.rsrust/crates/ccusage/src/adapter/mod.rsrust/crates/ccusage/src/cli.rsrust/crates/ccusage/src/config.rsrust/crates/ccusage/src/config_schema.rsrust/crates/ccusage/src/main.rsrust/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
| candidates.sort(); | ||
| candidates.dedup(); | ||
| candidates |
There was a problem hiding this comment.
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.
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
# 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.
|
@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:
Local validation:
Pre-push validation also passed clippy/oxfmt/eslint hooks. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 51133
Diff review (055332b + de7db27)
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] |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
Try This PR
Summary
kilo.dbSQLite usage rowsKILO_DATA_DIRand add CLI JSON snapshot coverageValidation
Summary by cubic
Adds a Rust Kilo adapter that reads local
kilo.db, addskilodaily/monthly/session reports, and includes Kilo in unified views. Costs use Kilo’s recorded value (including 0) or fall back toLiteLLMpricing; reasoning tokens are counted in totals.New Features
kilo.dbread-only from~/.local/share/kiloorKILO_DATA_DIR(supports comma-separated paths)kilo daily,kilo monthly,kilo session; included in help,--alldetection, and unified reportskilonamespace and Kilo guideBug Fixes
Written for commit de7db27. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Release Notes
New Features
KILO_DATA_DIRenvironment variable for configuring Kilo data directory locationDocumentation