Skip to content

feat(pi): add pi / oh-my-pi usage adapter with omp auto-detection - #50

Merged
cobra91 merged 5 commits into
mainfrom
feat/pi-adapter-omp
Jul 19, 2026
Merged

feat(pi): add pi / oh-my-pi usage adapter with omp auto-detection#50
cobra91 merged 5 commits into
mainfrom
feat/pi-adapter-omp

Conversation

@cobra91

@cobra91 cobra91 commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a new pi source adapter that reads JSONL session files from pi and its fork oh-my-pi (omp), aggregated alongside the existing 6 sources (claude/droid/zcode/codex/opencode/devin).

Ported from upstream ccusage (pi adapter default branch + PR ccusage/ccusage#1338 for omp auto-detection).

What it reads

  • JSONL sessions under ~/.pi/agent/sessions/**/*.jsonl AND ~/.omp/agent/sessions/**/*.jsonl
  • Auto-detection: when neither PI_AGENT_DIR nor a custom path is set, both directories are scanned (existing ones only), order .pi then .omp. Matches upstream PR #1338.
  • Dedup: entries are collapsed by the loader's createUniqueHash, so a session present in both dirs is counted once.

Token & cost semantics (verified against upstream Rust)

  • Additive token model: input/output/cacheRead/cacheWrite are independent buckets, summed. NO cache subtraction from input \u2014 opposite of Codex. Matches upstream pi/parser.rs.
  • totalTokens fallback: when totalTokens > known sum, surplus folds into output_tokens (when output is 0), else dropped (no cost impact). Matches upstream apply_total_token_fallback.
  • Cost: message.usage.cost.total (USD) emitted as costUSD. Default auto mode uses it directly.
  • No model prefix ([pi]/[omp]): both dirs share the pi source label and pricing lookup (matches PR #1338 \u2014 the Default scope upstream).
  • Record acceptance: type absent or "message" + role === "assistant" + usage present.

Changes

  • _consts.ts: SOURCE_ORDER += 'pi' (now 7 sources \u2192 127 auto-generated subsets); pi/omp path constants + PI_SESSION_GLOB.
  • pi-adapter.ts (new, ~600 lines incl. tests): getPiPaths(): string[] (multi-dirs!) + processPiSessions(dirs, options) + valibot schemas + applyTotalTokenFallback + extractProject (with path.normalize for Windows glob paths).
  • data-loader.ts: pi wired into all 3 loaders following the devin template, with multi-dir guards (piPaths.length === 0 || (piPaths.length === 1 && piPaths[0] === '')).
  • Docs: new docs/guide/pi.md, README updated, PI_AGENT_DIR in environment-variables.md, Vitepress sidebar.

Verification

  • pnpm --filter='better-ccusage' typecheck: clean
  • pnpm --filter='better-ccusage' test: 336 passed (+17 new: 11 processPiSessions + 3 getPiPaths + 3 applyTotalTokenFallback)
  • pnpm --filter='better-ccusage' lint: clean
  • pnpm --filter='better-ccusage' build: clean
  • Backward compat e2e: monthly --compact runs without regression on existing data.

Code review (pre-push subagent)

The critical question \u2014 does pi's input include cacheRead (double-counting)? \u2014 verified against upstream Rust pi/parser.rs: no, additive model is correct (matches upstream). No hidden-session leak analog (pi has no DB). One review finding fixed before push: the uniqueId dedup key now includes model + cacheRead:cacheCreation (matching upstream entry_id) so same-instant messages with different buckets/models stay distinct.

Follow-up (not blocking): loader-level dedup integration test; extra_total_tokens surplus tracking (currently dropped, no cost impact).

Refs

Upstream: ccusage/ccusage#1338
Builds on #48 (sourceSchema) + #49 (devin adapter).

Summary by CodeRabbit

  • New Features

    • Added usage tracking for pi and oh-my-pi sessions across reports and billing analysis.
    • Automatically detects pi/oh-my-pi session files, with support for custom directories through PI_AGENT_DIR.
    • Added “pi” as a selectable usage source.
  • Documentation

    • Added a pi Source guide covering setup, file locations, token and cost handling, and reporting.
    • Updated CLI and environment-variable documentation with pi/oh-my-pi support details.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cobra91, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ecb6ed03-c7b8-4140-93dc-388d51182b1e

📥 Commits

Reviewing files that changed from the base of the PR and between 62de234 and 0edbc65.

📒 Files selected for processing (10)
  • apps/better-ccusage/README.md
  • apps/better-ccusage/src/_consts.ts
  • apps/better-ccusage/src/data-loader.ts
  • apps/better-ccusage/src/pi-adapter.ts
  • docs/.vitepress/config.ts
  • docs/guide/devin.md
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • docs/guide/pi.md
📝 Walkthrough

Walkthrough

Adds built-in pi and oh-my-pi JSONL session support, including source discovery, token and cost normalization, integration into daily/session/block reports, tests, configuration documentation, and a new guide page.

Changes

Pi usage integration

Layer / File(s) Summary
Source contract and session discovery
apps/better-ccusage/src/_consts.ts, apps/better-ccusage/src/pi-adapter.ts
Adds pi to source ordering and defines pi/omp session paths, environment configuration, JSONL discovery, validation helpers, and session metadata extraction.
Session parsing and validation
apps/better-ccusage/src/pi-adapter.ts
Parses billable assistant messages, normalizes token totals and costs, skips invalid entries, generates stable identifiers, and adds Vitest coverage.
Reporting pipeline integration
apps/better-ccusage/src/data-loader.ts
Loads pi entries into daily, session, and session-block aggregation with deduplication, source labeling, project paths, and configured cost handling.
Documentation and navigation
apps/better-ccusage/README.md, docs/.vitepress/config.ts, docs/guide/environment-variables.md, docs/guide/pi.md
Documents pi/oh-my-pi support, PI_AGENT_DIR, data locations, report semantics, filtering, pricing, and navigation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SessionFiles as pi/omp JSONL sessions
  participant PiAdapter as processPiSessions
  participant DataLoader as data-loader
  participant Reports as usage reports
  SessionFiles->>PiAdapter: provide session records
  PiAdapter->>DataLoader: return normalized UsageData
  DataLoader->>Reports: aggregate pi usage
Loading

Possibly related PRs

Poem

A bunny found JSONL in the moonlit night,
Pi tokens hopped into reports just right.
Omp paths joined the cheerful parade,
Costs and sessions were neatly displayed.
“One more source!” the rabbit sings,
While stable IDs sprout tiny wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a pi/oh-my-pi usage adapter with omp auto-detection.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pi-adapter-omp

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.

@pkg-pr-new

pkg-pr-new Bot commented Jul 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cobra91/better-ccusage@50
npm i https://pkg.pr.new/cobra91/better-ccusage/@better-ccusage/codex@50
npm i https://pkg.pr.new/cobra91/better-ccusage/@better-ccusage/mcp@50
npm i https://pkg.pr.new/cobra91/better-ccusage/@better-ccusage/opencode@50

commit: 0edbc65

@chatllm-code-bot

chatllm-code-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

⚠️ Code review agent could not produce a complete summary (did not submit a review within the step budget). Manual review recommended.

📁 Files Reviewed (10)
  • apps/better-ccusage/README.md
  • apps/better-ccusage/src/_consts.ts
  • apps/better-ccusage/src/data-loader.ts
  • apps/better-ccusage/src/pi-adapter.ts
  • docs/.vitepress/config.ts
  • docs/guide/devin.md
  • docs/guide/environment-variables.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
  • docs/guide/pi.md
🕓 Previous Review Summaries (3 snapshots · latest 10345b2)
Snapshot — 10345b2

Code Review Roast 🔥 — ISSUES_FOUND | Recommendation: Address before merge

Severity Count
🚨 critical 0
⚠️ warning 1
💡 suggestion 0
🤏 nitpick 1

Previous Findings Status

Finding Status Note
#1: Missing dedup in loadDailyUsageData pi loop ✅ Resolved The pi loop now includes createUniqueHash/isDuplicateEntry/markAsProcessed — matches other sources
#2: Missing dedup in loadSessionBlockData pi loop 🟡 Still present pi entries are mapped and spread into combinedEntries without dedup — see below
#3: Outdated pi/omp links in pi.md 🟡 Still present Links still point to anthropics/pi and oh-my-pi/omp — should be earendil-works/pi and canst357/oh-my-pi
#4: Same links (nitpick) 🟡 Still present Duplicate of #3

Body

🏆 Best Part

The pi-adapter.ts is a well-crafted ~600 line file with thorough test coverage (17 new tests). The additive token model, totalTokens fallback logic, and dedup key construction (sessionId#timestamp#model#buckets) all match upstream Rust faithfully. The adapter handles edge cases like malformed JSON, zero-token messages, missing models, and non-object costs gracefully.

💀 Worst Part — Still Present Finding #2

In loadSessionBlockData (data-loader.ts), pi entries are freshly loaded from processPiSessions and mapped to LoadedUsageEntry format, then spread into combinedEntries without any deduplication. Since pi scans two directories (.pi and .omp) that may contain the same session files, identical entries from both dirs will be double-counted in the session blocks report. The other sources don't need this because they come pre-deduplicated from their loaders, but pi entries are raw here. Add the same createUniqueHash/isDuplicateEntry/markAsProcessed filtering used in loadDailyUsageData before pushing into combinedEntries.

🤏 Nitpick — Finding #3/#4

docs/guide/pi.md line 3 still links to anthropics/pi and oh-my-pi/omp. The pi project moved to earendil-works/pi and oh-my-pi is at canst357/oh-my-pi. Fix the hyperlinks.

📊 Overall Assessment

The pi adapter code itself is solid — well-tested, faithful to upstream, handles edge cases. The dedup gap in loadSessionBlockData is the only real bug: it means if a user has the same session file in both .pi and .omp, their blocks report will double-count those entries. The outdated links are cosmetic but worth fixing for correctness.

📁 Files Reviewed (9)
  • apps/better-ccusage/src/data-loader.ts
  • apps/better-ccusage/src/pi-adapter.ts
  • apps/better-ccusage/src/_consts.ts
  • docs/guide/pi.md
  • docs/guide/environment-variables.md
  • docs/.vitepress/config.ts
  • apps/better-ccusage/README.md
  • docs/guide/getting-started.md
  • docs/guide/index.md
Snapshot — 478e9e6

Code Review Roast 🔥 — ISSUES_FOUND | Recommendation: Address before merge

Severity Count
🚨 critical 0
⚠️ warning 2
💡 suggestion 0
🤏 nitpick 1

Changes Since Last Review

This is the initial review of the PR. The diff adds a new pi source adapter with oh-my-pi auto-detection, wiring it into all 3 loaders (daily, session, blocks), plus docs and config updates.

Previous Findings Status

Finding Status Note
#1: loadDailyUsageData pi dedup ✅ Resolved The pi loop in loadDailyUsageData now has createUniqueHash/isDuplicateEntry/markAsProcessed — properly deduplicates entries from both .pi and .omp dirs
#2: loadSessionData pi dedup ❌ Still Present The pi loop in loadSessionData (line ~1553) still lacks dedup — raw pi entries are pushed directly into allEntries without hash-based dedup, so sessions present in both .pi and .omp get double-counted
#3: loadSessionBlockData pi dedup ❌ Still Present The pi entries in loadSessionBlockData are mapped into LoadedUsageEntry and spread into combinedEntries without any dedup — same double-counting risk
#4: pi.md links ❌ Still Present Links still point to anthropics/pi and oh-my-pi/omp instead of earendil-works/pi and can1357/oh-my-pi

🏆 Best Part

The pi-adapter.ts is a well-crafted ~600 line file with thorough test coverage (11 processPiSessions tests + 3 getPiPaths + 3 applyTotalTokenFallback). The additive token model handling, totalTokens fallback logic, and the uniqueId construction (session + timestamp + model + all four token buckets) are all solid. The dedup in loadDailyUsageData is correctly implemented.

💀 Worst Part

The pi loops in loadSessionData and loadSessionBlockData are missing the deduplication step that the other sources (droid, zcode, codex, opencode, devin) all use. Since pi scans two directories that can contain the same session files, this means double-counting — the exact same session's tokens and costs get summed twice in session reports and block reports. This is a data integrity bug that inflates usage numbers.

📊 Overall Assessment

The pi adapter itself is excellent — well-tested, follows the established patterns, and the daily loader dedup is correct. But the session and block loaders have a critical gap: they skip the deduplication step that prevents double-counting when the same session exists in both .pi and .omp directories. The docs also still have outdated GitHub links. Fix these and it's a clean merge.

📁 Files Reviewed (7)
  • apps/better-ccusage/src/data-loader.ts
  • apps/better-ccusage/src/pi-adapter.ts
  • apps/better-ccusage/src/_consts.ts
  • docs/guide/pi.md
  • docs/guide/environment-variables.md
  • docs/.vitepress/config.ts
  • apps/better-ccusage/README.md
Snapshot — 62de234

Code Review Roast 🔥 — CLEAN | Recommendation: Merge

Severity Count
🚨 critical 0
⚠️ warning 0
💡 suggestion 0
🤏 nitpick 0

Overall: This is a beautifully implemented pi/oh-my-pi adapter that slots perfectly into the existing architecture. The code is clean, well-tested, and follows all the established patterns.

🏆 Best parts:

  • The additive token model implementation is spot-on — cacheRead/cacheWrite as independent buckets, no subtraction from input (matching Claude's cost model)
  • applyTotalTokenFallback correctly handles the surplus folding logic from upstream Rust
  • Multi-dir scanning with proper empty-path sentinel for VITEST
  • Comprehensive test coverage (17 tests) including edge cases like malformed JSON, missing models, and dedup verification
  • The extractProject function properly handles Windows paths via path.normalize since tinyglobby returns forward-slash paths even on Windows

💀 Worst parts:

  • None. This is clean, production-ready code.

📊 Overall assessment:
This PR adds pi/omp support with the same level of care and attention to detail as the existing adapters. The integration into all three loaders is consistent, the dedup logic works correctly, and the documentation is thorough. The code is ready to merge.

📁 Files Reviewed (7)
  • apps/better-ccusage/src/pi-adapter.ts
  • apps/better-ccusage/src/data-loader.ts
  • apps/better-ccusage/src/_consts.ts
  • apps/better-ccusage/README.md
  • docs/.vitepress/config.ts
  • docs/guide/environment-variables.md
  • docs/guide/pi.md

Review automated by ChatLLM Code Bot using remote/Qwen3.6-35B-A3B with 0 tokens (0 in / 0 out)

@kilo-code-bot

kilo-code-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

Code Review Roast 🔥

Verdict: No Issues Found | Recommendation: Merge

Incremental pass over commit 0edbc65 (pi adapter dedup relocation + doc link removal). The dedup moved from "patch each loader individually" to "bake it into the adapter" — one bouncer at the door instead of three checkpoints inside. Genuinely better architecture, and I'm almost suspicious.

The dedup itself is textbook: Set-based O(n), keys on message.id (which encodes sessionId + timestamp + model + all four token buckets), keeps the first occurrence. The new test proving distinct sessions at the same timestamp survive dedup is the kind of test I wish more people wrote. The pluralization logic in the log message (directory/directories, duplicate/duplicates) is gratuitously thorough — I respect that.

The doc link "fix" is actually a removal: [pi](stale-url)**pi**. The wrong URLs are gone, which resolves the stale-link threads from other bots, but users lose the click-throughs. Defensible choice — a dead link is worse than no link, and the projects' organizational homes have been churning lately anyway.

One observation, not a finding: the file-level @fileoverview (pi-adapter.ts:12) and the inline comment at pi-adapter.ts:352 still say "dedup relies on the loader's createUniqueHash" — now slightly stale since dedup moved into the adapter. Not worth blocking on, but a future reader debugging dedup might briefly wonder which layer actually does it.

📊 Overall: Like finally fixing the leaky faucet by replacing the whole pipe — more effort upfront, but you'll never think about it again.

Files Reviewed (3 files, incremental)
  • apps/better-ccusage/src/pi-adapter.ts — adapter-level dedup pass + updated/added tests
  • docs/guide/devin.md — added pi/oh-my-pi to the sources list
  • docs/guide/pi.md — removed stale GitHub links (bold text instead)
Previous Review Summaries (3 snapshots, latest commit 10345b2)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 10345b2)

Verdict: No Issues Found | Recommendation: Merge

Incremental pass over commit 10345b2 (docs only: source inventory refresh + fork positioning vs upstream). Clean as a freshly squeegeed window.

The README's "Claude/Droid/ZCode" tunnel vision finally got corrected to acknowledge all seven aggregated sources, complete with a tidy data-sources table mapping each tool to its on-disk location. The "Why the fork?" rewrite repositions better-ccusage honestly — a diverged TypeScript monorepo vs upstream's Rust workspace, with feature ports going both ways. Refreshingly candid for a README; those usually read like a press release drafted by someone who's never run the binary.

One observation, not a new finding: the new README data-sources table row for pi repeats the same outdated GitHub URLs (anthropics/pi, oh-my-pi/omp) already flagged inline on docs/guide/pi.md:3 by two other bots. Per the same-defect rule, no duplicate inline comment posted — the author will fix both occurrences when addressing that existing thread.

📊 Overall: Like proofreading a résumé you actually believe — accurate, concise, and mercifully free of "synergy."

Files Reviewed (3 files, incremental)
  • apps/better-ccusage/README.md — tagline + data-sources table + "Why the fork?" rewrite + pi CLI example
  • docs/guide/getting-started.md — intro + prerequisites updated for all 7 tools
  • docs/guide/index.md — intro + "Why better-ccusage?" restructured

Previous review (commit 478e9e6)

Verdict: No Issues Found | Recommendation: Merge

Incremental pass over the two new commits since the last review (9335a2d typo fix + 478e9e6 sourceFilter gate integration). Both are clean.

The sourceFilter == null || sourceFilter === 'pi' gate was bolted onto all three pi loader blocks (daily/session/blocks), and — shock of shocks — it's a carbon copy of the exact same guard already wrapping droid, zcode, codex, opencode, and devin. Consistency? In my codebase? Unprecedented. The piPaths declaration got correctly pulled inside the gate so it doesn't linger around like an uninvited guest when pi is filtered out, and piEntries stays initialized to [] upstream so the downstream spreads don't implode. Wild concept.

The typo fix (unparseableunparsable) was already chased down by CodeRabbit, so nothing to add there except mild disappointment that the spellchecker has stronger opinions about English than most humans.

📊 Overall: Like a sequel that doesn't ruin the original — the sourceFilter integration does exactly what it says on the tin, no surprises, no drama.

Files Reviewed (2 files, incremental)
  • apps/better-ccusage/src/data-loader.ts — sourceFilter gate added to 3 pi loader blocks (daily/session/blocks)
  • apps/better-ccusage/src/pi-adapter.ts — comment typo fix (already addressed)

Previous review (commit de7bec2)

Verdict: No Issues Found | Recommendation: Merge

Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything.

I came in hot expecting the usual "ported from upstream" special — mismatched token buckets, a dedup key held together with hope and string interpolation, an empty-path sentinel that breaks production. Instead I got an adapter that mirrors the devin template down to the log messages, an additive token model that actually matches upstream pi/parser.rs, a totalTokens fallback that correctly folds surplus into output only when output is 0, and 17 tests covering the awkward edge cases (malformed JSON, missing model, zero tokens, non-object cost, omp-vs-pi dedup).

The uniqueId even includes all four token buckets so same-instant messages with different buckets stay distinct — more dedup discipline than I usually see in code that "works on my machine."

📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are.

Files Reviewed (7 files)
  • apps/better-ccusage/src/pi-adapter.ts — new ~670-line adapter, schemas + path resolution + parsing + 17 inline tests
  • apps/better-ccusage/src/data-loader.ts — pi wired into all 3 loaders (daily/session/blocks) following the devin template
  • apps/better-ccusage/src/_consts.tsSOURCE_ORDER += 'pi', path + env constants
  • apps/better-ccusage/README.md — family blurb + data source description
  • docs/.vitepress/config.ts — sidebar entry for pi Source
  • docs/guide/environment-variables.mdPI_AGENT_DIR documentation
  • docs/guide/pi.md — new guide covering paths, token semantics, metric mapping

Reviewed by glm-5.2 · Input: 80.7K · Output: 18K · Cached: 472.8K

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

Inline comments:
In `@apps/better-ccusage/src/data-loader.ts`:
- Around line 2178-2208: Deduplicate pi session entries before merging them into
combinedEntries, matching the hash-based deduplication used by loadSessionData.
Apply deduplication to the rawPiEntries or mapped piEntries while preserving
distinct sessions, then merge the resulting piEntries without double-counting
sessions present in both .pi and .omp sources.
- Around line 1507-1526: Add the same createUniqueHash, isDuplicateEntry, and
markAsProcessed filtering used by loadDailyUsageData to the piEntries loop
before pushing into allEntries. Reuse the existing deduplication state and
preserve the current cost and entry construction for unique pi sessions; apply
the equivalent fix to the pi loop in loadSessionBlockData as noted by the
consolidated comment.

In `@apps/better-ccusage/src/pi-adapter.ts`:
- Line 321: Update the comment near the timestamp resolution logic to replace
“unparseable” with the spell-checker-approved spelling “unparsable,” without
changing the surrounding behavior.

In `@docs/guide/pi.md`:
- Line 3: Update the pi and oh-my-pi hyperlinks in the introductory paragraph of
docs/guide/pi.md to reference earendil-works/pi and can1357/oh-my-pi,
respectively, while leaving the surrounding description unchanged.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d49d9eb-b35d-4f4c-a052-f85bd67c83e3

📥 Commits

Reviewing files that changed from the base of the PR and between 5f7df82 and 62de234.

📒 Files selected for processing (7)
  • apps/better-ccusage/README.md
  • apps/better-ccusage/src/_consts.ts
  • apps/better-ccusage/src/data-loader.ts
  • apps/better-ccusage/src/pi-adapter.ts
  • docs/.vitepress/config.ts
  • docs/guide/environment-variables.md
  • docs/guide/pi.md

Comment on lines +1507 to +1526
// Add pi entries to the collection
for (const piData of piEntries) {
piData.source ??= createSource('pi');

const sessionKey = path.join('pi', piData.sessionId ?? 'unknown-session');
const cost = fetcher == null
? piData.costUSD ?? 0
: await calculateCostForEntry(piData, mode, fetcher);

allEntries.push({
data: piData,
sessionKey,
sessionId: piData.sessionId ?? 'unknown-session',
projectPath: piData.cwd ?? path.join('pi', 'unknown'),
cost,
timestamp: piData.timestamp,
model: piData.message.model,
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Missing dedup for pi entries — see consolidated comment.

This loop skips the createUniqueHash/isDuplicateEntry/markAsProcessed dedup step that loadDailyUsageData applies to pi entries, which is needed here because pi scans two directories that can contain the same session. Details and fix in the consolidated comment below (shared root cause with the loadSessionBlockData pi loop).

🤖 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/better-ccusage/src/data-loader.ts` around lines 1507 - 1526, Add the
same createUniqueHash, isDuplicateEntry, and markAsProcessed filtering used by
loadDailyUsageData to the piEntries loop before pushing into allEntries. Reuse
the existing deduplication state and preserve the current cost and entry
construction for unique pi sessions; apply the equivalent fix to the pi loop in
loadSessionBlockData as noted by the consolidated comment.

Comment thread apps/better-ccusage/src/data-loader.ts
Comment thread apps/better-ccusage/src/pi-adapter.ts Outdated
Comment thread docs/guide/pi.md Outdated
cobra91 added 3 commits July 19, 2026 21:04
Read the JSONL session files that pi and its widely used fork oh-my-pi
(omp) write under ~/.pi/agent/sessions and ~/.omp/agent/sessions, and
normalize each billable assistant message into the shared UsageData
shape, aggregated alongside the other 6 sources.

Both directories are auto-detected when neither PI_AGENT_DIR nor a
custom path is set (matches upstream ccusage PR ccusage/ccusage#1338).
Entries are deduplicated by the loader's createUniqueHash, so a session
file present in both directories is counted once.

Token accounting is additive (the Claude model): the four buckets
(input/output/cacheRead/cacheWrite) are independent and summed, with NO
subtraction of cached tokens from input (unlike Codex). The
totalTokens fallback folds the surplus into output_tokens when output
is 0 (matches upstream's apply_total_token_fallback).

Cost: emits message.usage.cost.total (USD) so the default 'auto' cost
mode uses it directly; 'calculate' mode recomputes from tokens.

Per upstream omp PR #1338, models are NOT prefixed ([pi]/[omp]): both
default directories share the 'pi' source label and pricing lookup.

17 new inline tests (record accept/reject, cache buckets, totalTokens
fallback, non-object cost, zero-token/no-model skip, omp vs pi dedup,
nested path extraction, path resolution, totalTokens fallback unit).

Docs: new guide pi.md, README + env var (PI_AGENT_DIR) + sidebar.
Backward compat verified: monthly --compact runs without regression.

Ported from upstream ccusage (pi adapter default branch + PR #1338).
Rebased onto main (which merged #51 adding source filtering via
`better-ccusage <source> <report>`). The pi load blocks in the 3
loaders (loadDailyUsageData, loadSessionData, loadSessionBlockData)
were added before #51 and lacked the sourceFilter gate, so
`better-ccusage pi daily` would not isolate pi. Wrap each pi block in
`if (sourceFilter == null || sourceFilter === 'pi')` to match the
devin/codex/opencode pattern.

Verified e2e: `better-ccusage pi daily` now isolates pi; global
`daily` still aggregates all sources (backward compat).
@cobra91
cobra91 force-pushed the feat/pi-adapter-omp branch from de7bec2 to 478e9e6 Compare July 19, 2026 19:08

@chatllm-code-bot chatllm-code-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review automated by ChatLLM Code Bot using remote/Qwen3.6-35B-A3B with 926770 tokens (924383 in / 2387 out)

Comment thread docs/guide/pi.md Outdated
The README 'About'/'Why the Fork' and the docs index/getting-started
sections were outdated: they listed only Claude/Droid/ZCode and framed
the fork as 'ccusage only supports Claude Code'. Both are now inaccurate
— better-ccusage supports 7 sources and upstream ccusage has itself
diverged into a Rust workspace with its own source set.

- README: new 'Supported data sources' table (7 tools + data locations),
  rewritten 'Why the fork' covering multi-source AND multi-provider,
  explicit divergence note (TS monorepo vs Rust workspace), pi added
  to tagline + source-filter examples.
- docs/index.md: intro mentions all 7 tools; 'Why better-ccusage'
  rewritten with the same multi-source + multi-provider framing and the
  upstream-divergence callout.
- getting-started.md: prerequisites list all 7 tools and the SQLite
  runtime requirement (Node 22.13+ for zcode/opencode/devin).
@chatllm-code-bot
chatllm-code-bot Bot dismissed their stale review July 19, 2026 19:17

Superseded by a new review

@chatllm-code-bot chatllm-code-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review automated by ChatLLM Code Bot using remote/Qwen3.6-35B-A3B with 257084 tokens (254917 in / 2167 out)

Comment thread docs/guide/pi.md Outdated
Address CodeRabbit major review (2x): loadSessionData and
loadSessionBlockData don't run the createUniqueHash gate on non-Claude
source loops, so pi (which scans BOTH ~/.pi and ~/.omp) could
double-count sessions present in both directories. Fix at the adapter
boundary: processPiSessions now deduplicates by message.id (which
encodes sessionId + timestamp + model + token buckets) before returning,
so no entry set is counted twice regardless of the loader.

Test 'deduplicates omp vs pi' updated: same content in both dirs now
yields 1 entry (was 2, relying on downstream dedup that doesn't exist
for these loaders). New test 'keeps distinct sessions that share a
timestamp' verifies genuinely different sessions are preserved.

Also drop speculative pi/omp GitHub links from docs/guide/pi.md (the
repos are not verified; pi is a product, not an open CLI repo) and add
pi to the devin.md source list.
@chatllm-code-bot
chatllm-code-bot Bot dismissed their stale review July 19, 2026 19:47

Superseded by a new review

@cobra91
cobra91 merged commit d0c596a into main Jul 19, 2026
12 checks passed
@cobra91
cobra91 deleted the feat/pi-adapter-omp branch July 19, 2026 19:51
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