-
-
Notifications
You must be signed in to change notification settings - Fork 318
feat(droid): add Factory Droid usage tracker #795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Warning Rate limit exceeded@iipanda has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 0 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a new Droid CLI app that parses Factory Droid logs, builds daily/monthly/session token-usage reports, integrates pricing (online/offline) with prefetch support, and renders JSON or human-readable tables; includes loaders, report builders, utilities, configs, tests, and documentation. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI
participant DataLoader
participant ReportBuilder
participant PricingSource
participant Formatter
User->>CLI: invoke command (daily/monthly/session) + args
CLI->>DataLoader: loadFactoryTokenUsageEvents(factoryDir)
DataLoader-->>CLI: TokenUsageEvent[]
CLI->>ReportBuilder: buildReport(events, {timezone, locale, since, until, pricingSource})
ReportBuilder->>PricingSource: calculateCost(pricingModel, usage) (per pricing model)
PricingSource-->>ReportBuilder: PricingResult or error
ReportBuilder-->>CLI: ReportResult (rows + missingPricingModels)
CLI->>Formatter: format output (json | table)
Formatter-->>User: display output
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @apps/droid/package.json:
- Around line 31-33: The engines/devEngines mismatch should be resolved by
picking a single supported Node baseline and making both fields consistent:
either set "engines.node" to match the development runtime
("devEngines.runtime": "^24.11.0") if Node 24+ is required, or relax
"devEngines.runtime" to match the published requirement ("engines.node":
">=20.19.4") if Node 20+ is supported; update the package.json entries "engines"
and "devEngines.runtime" accordingly so both reflect the chosen Node version
range (and update any related docs/scripts that assert a Node version if
present).
In @apps/droid/src/_macro.ts:
- Line 29: Replace the console.warn call that logs "Failed to prefetch Factory
pricing data, proceeding with empty cache." with the module logger by calling
logger.warn and passing the same message and error; if a logger binding is not
present in _macro.ts, import or obtain the existing logger instance used by the
project and use it instead of console to comply with coding guidelines,
preserving the message and the error argument.
🧹 Nitpick comments (6)
apps/droid/src/factory-settings.ts (2)
5-5: Move test-only import inside the vitest block.
createFixturefromfs-fixtureis only used in tests but is imported at the module level. This causes the test dependency to be bundled into production code.Proposed fix
import { readFile } from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; import { Result } from '@praha/byethrow'; -import { createFixture } from 'fs-fixture'; import * as v from 'valibot'; import { DEFAULT_FACTORY_DIR, FACTORY_DIR_ENV } from './_consts.ts'; import { logger } from './logger.ts'; // ... rest of production code ... if (import.meta.vitest != null) { + const { createFixture } = await import('fs-fixture'); + describe('loadFactoryCustomModels', () => {Note: If dynamic imports are disallowed per coding guidelines, consider moving the test to a separate test file or using a different pattern for test-only dependencies.
77-90: Add fixture cleanup withusingstatement.Per the codebase patterns,
fs-fixtureshould be used with theusingstatement to ensure automatic cleanup after the test.Proposed fix
describe('loadFactoryCustomModels', () => { it('loads custom model ids from settings.json', async () => { - const fixture = await createFixture({ + using fixture = await createFixture({ 'settings.json': JSON.stringify( { customModels: [{ id: 'custom:Test-0', model: 'gpt-5.2', provider: 'openai' }], }, null, 2, ), }); const models = await loadFactoryCustomModels(fixture.path); expect(models.get('custom:Test-0')?.model).toBe('gpt-5.2'); }); });apps/droid/src/_consts.ts (1)
1-11: Consider defensive initialization for DEFAULT_TIMEZONE.The initialization of
DEFAULT_TIMEZONEusingIntl.DateTimeFormat().resolvedOptions().timeZonecould throw in some environments or if the Intl API is not fully supported. While the nullish coalescing operator handles anull/undefinedreturn, it won't catch exceptions.🛡️ Proposed defensive initialization
-export const DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone ?? 'UTC'; +export const DEFAULT_TIMEZONE = (() => { + try { + return Intl.DateTimeFormat().resolvedOptions().timeZone ?? 'UTC'; + } catch { + return 'UTC'; + } +})();apps/droid/src/commands/daily.ts (1)
22-31: Extract duplicated helper to shared utility.The
summarizeMissingPricingfunction is identical in bothdaily.tsandsession.ts(lines 26-35). This code duplication should be extracted to a shared utility module to follow DRY principles and improve maintainability.♻️ Proposed refactor
Create a new utility file
apps/droid/src/_report-utils.ts:import { logger } from './logger.ts'; export function summarizeMissingPricing(models: string[]): void { if (models.length === 0) { return; } const preview = models.slice(0, 5).join(', '); const suffix = models.length > 5 ? ', …' : ''; logger.warn( `Missing pricing for ${models.length} models (cost treated as $0): ${preview}${suffix}`, ); }Then import and use in both command files:
+import { summarizeMissingPricing } from '../_report-utils.ts'; -function summarizeMissingPricing(models: string[]): void { - if (models.length === 0) { - return; - } - const preview = models.slice(0, 5).join(', '); - const suffix = models.length > 5 ? ', …' : ''; - logger.warn( - `Missing pricing for ${models.length} models (cost treated as $0): ${preview}${suffix}`, - ); -}Note: Apply the same change to
apps/droid/src/commands/session.tsandapps/droid/src/commands/monthly.tsif it exists there too.apps/droid/src/monthly-report.ts (1)
26-54: Extract duplicated helper functions to a shared module.
formatModelDisplay,addEventUsage, andgetOrCreateModelUsageare identical acrossdaily-report.ts,monthly-report.ts, andsession-report.ts. Consider extracting these to a shared utility module (e.g.,report-utils.ts) to reduce duplication and improve maintainability.apps/droid/src/date-utils.ts (1)
67-93: Consider removing unused_timezoneparameters.The
_timezoneparameters informatDisplayDateandformatDisplayMonthare unused. If these are intentionally reserved for future use, adding a brief comment would clarify the intent. Otherwise, consider removing them.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (25)
apps/droid/README.mdapps/droid/eslint.config.jsapps/droid/package.jsonapps/droid/src/_consts.tsapps/droid/src/_macro.tsapps/droid/src/_shared-args.tsapps/droid/src/_types.tsapps/droid/src/commands/daily.tsapps/droid/src/commands/index.tsapps/droid/src/commands/monthly.tsapps/droid/src/commands/session.tsapps/droid/src/daily-report.tsapps/droid/src/data-loader.tsapps/droid/src/date-utils.tsapps/droid/src/factory-settings.tsapps/droid/src/index.tsapps/droid/src/logger.tsapps/droid/src/monthly-report.tsapps/droid/src/pricing.tsapps/droid/src/run.tsapps/droid/src/session-report.tsapps/droid/src/token-utils.tsapps/droid/tsconfig.jsonapps/droid/tsdown.config.tsapps/droid/vitest.config.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Import conventions: Use.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
Use ESLint for linting and formatting with tab indentation and double quotes
No console.log allowed except where explicitly disabled with eslint-disable
Use camelCase for variable names (e.g.,usageDataSchema,modelBreakdownSchema)
Use PascalCase for type names (e.g.,UsageData,ModelBreakdown)
Use UPPER_SNAKE_CASE for constants (e.g.,DEFAULT_CLAUDE_CODE_PATH)
Only export constants, functions, and types that are actually used by other modules - do not export internal/private constants used only within the same file
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
UseResult.try()for wrapping operations that may throw (JSON parsing, etc.)
UseResult.isFailure()for checking errors (more readable than!Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators with Result types
NEVER useawait import()dynamic imports anywhere in the codebase - this causes tree-shaking issues
Do not use console.log - use logger.ts instead
Vitest globals (describe,it,expect) are available automatically without imports in test blocks
NEVER useawait import()dynamic imports in vitest test blocks - this is particularly problematic for test execution
All test files must use current Claude 4 models (claude-sonnet-4-20250514, claude-opus-4-20250514), not outdated Claude 3 models
Model names in tests must exactly match LiteLLM's pricing database entries
Files:
apps/droid/src/commands/monthly.tsapps/droid/src/_consts.tsapps/droid/src/commands/session.tsapps/droid/src/_shared-args.tsapps/droid/src/commands/daily.tsapps/droid/src/commands/index.tsapps/droid/src/session-report.tsapps/droid/src/run.tsapps/droid/src/monthly-report.tsapps/droid/vitest.config.tsapps/droid/src/_macro.tsapps/droid/src/date-utils.tsapps/droid/src/data-loader.tsapps/droid/tsdown.config.tsapps/droid/src/daily-report.tsapps/droid/src/pricing.tsapps/droid/src/factory-settings.tsapps/droid/src/logger.tsapps/droid/src/token-utils.tsapps/droid/src/index.tsapps/droid/src/_types.ts
**/_*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use underscore prefix for internal files (e.g.,
_types.ts,_utils.ts,_consts.ts)
Files:
apps/droid/src/_consts.tsapps/droid/src/_shared-args.tsapps/droid/src/_macro.tsapps/droid/src/_types.ts
apps/**/package.json
📄 CodeRabbit inference engine (CLAUDE.md)
All projects under
apps/ship as bundled CLIs/binaries - list runtime dependencies indevDependencies(neverdependencies) so the bundler owns the runtime payload
Files:
apps/droid/package.json
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: In documentation pages with screenshots, always place screenshots immediately after the main heading (H1)
Use relative image paths like/screenshot.pngfor images stored in/docs/public/
Always include descriptive alt text for images in documentation for accessibility
Files:
apps/droid/README.md
🧠 Learnings (52)
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/src/**/*.{ts,tsx} : Use `.ts` extensions for local imports
Applied to files:
apps/droid/tsconfig.jsonapps/droid/src/_consts.tsapps/droid/tsdown.config.tsapps/droid/src/index.ts
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/src/**/*.{ts,tsx} : Only export what's actually used from modules
Applied to files:
apps/droid/tsconfig.jsonapps/droid/src/commands/index.tsapps/droid/tsdown.config.tsapps/droid/src/logger.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Only export constants, functions, and types that are actually used by other modules - do not export internal/private constants used only within the same file
Applied to files:
apps/droid/tsconfig.jsonapps/droid/src/_consts.tsapps/droid/src/commands/index.tsapps/droid/tsdown.config.ts
📚 Learning: 2026-01-09T11:07:24.081Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/AGENTS.md:0-0
Timestamp: 2026-01-09T11:07:24.081Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Add JSDoc comments to exported functions and classes to document their purpose, parameters, and return values
Applied to files:
apps/droid/tsconfig.jsonapps/droid/tsdown.config.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/**/*.ts : Use `.ts` extensions for local imports (e.g., `import { foo } from './utils.ts'`)
Applied to files:
apps/droid/tsconfig.json
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.ts : Use `.ts` extensions for local (relative) imports
Applied to files:
apps/droid/tsconfig.json
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Place all vitest blocks alongside implementation files via `if (import.meta.vitest != null)` guards
Applied to files:
apps/droid/tsconfig.jsonapps/droid/eslint.config.jsapps/droid/vitest.config.tsapps/droid/tsdown.config.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Import conventions: Use `.ts` extensions for local file imports (e.g., `import { foo } from './utils.ts'`)
Applied to files:
apps/droid/tsconfig.json
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.ts : Only export what is actually used
Applied to files:
apps/droid/tsconfig.jsonapps/droid/src/commands/index.tsapps/droid/tsdown.config.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Only export symbols that are actually used by other modules
Applied to files:
apps/droid/tsconfig.jsonapps/droid/src/commands/index.tsapps/droid/package.jsonapps/droid/src/token-utils.ts
📚 Learning: 2026-01-09T12:35:27.240Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/amp/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:35:27.240Z
Learning: Applies to apps/amp/**/*.{test,spec}.{js,ts,jsx,tsx} : Enable vitest globals (describe, it, expect) without imports in test blocks
Applied to files:
apps/droid/tsconfig.jsonapps/droid/eslint.config.jsapps/droid/vitest.config.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.test.{ts,tsx,js,jsx} : Vitest globals are enabled - use `describe`, `it`, `expect` directly without imports
Applied to files:
apps/droid/tsconfig.jsonapps/droid/eslint.config.jsapps/droid/vitest.config.ts
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.{test,spec}.ts : Vitest globals enabled: use `describe`, `it`, `expect` directly without importing them
Applied to files:
apps/droid/tsconfig.jsonapps/droid/vitest.config.ts
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/**/*.test.{ts,tsx} : Use vitest globals (`describe`, `it`, `expect`) directly without imports in test files
Applied to files:
apps/droid/tsconfig.jsonapps/droid/eslint.config.jsapps/droid/vitest.config.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Vitest globals (`describe`, `it`, `expect`) are available automatically without imports in test blocks
Applied to files:
apps/droid/tsconfig.jsonapps/droid/vitest.config.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/**.test.ts : Use in-source testing with `if (import.meta.vitest != null)` blocks instead of separate test files
Applied to files:
apps/droid/tsconfig.jsonapps/droid/vitest.config.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Write tests in-source using `if (import.meta.vitest != null)` blocks instead of separate test files
Applied to files:
apps/droid/tsconfig.jsonapps/droid/eslint.config.jsapps/droid/vitest.config.tsapps/droid/src/token-utils.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Use Vitest globals (`describe`, `it`, `expect`) without imports in test blocks
Applied to files:
apps/droid/tsconfig.jsonapps/droid/vitest.config.tsapps/droid/src/token-utils.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Use ESLint for linting and formatting with tab indentation and double quotes
Applied to files:
apps/droid/tsconfig.json
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: The data loading architecture follows a clear separation of concerns: Data Loading (data-loader.ts), Token Aggregation (calculate-cost.ts), Command Execution (commands/), and CLI Entry (index.ts)
Applied to files:
apps/droid/src/commands/monthly.tsapps/droid/src/commands/daily.tsapps/droid/src/commands/index.tsapps/droid/src/data-loader.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Use UPPER_SNAKE_CASE for constants (e.g., `DEFAULT_CLAUDE_CODE_PATH`)
Applied to files:
apps/droid/src/_consts.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Data discovery relies on `OPENCODE_DATA_DIR` environment variable with default path `~/.local/share/opencode`
Applied to files:
apps/droid/src/_consts.ts
📚 Learning: 2026-01-09T12:35:27.240Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/amp/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:35:27.240Z
Learning: Use Gunshi framework with subcommands (daily, monthly, session) as entry point for Amp CLI
Applied to files:
apps/droid/src/commands/index.tsapps/droid/src/run.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Reuse shared packages (`ccusage/terminal`, `ccusage/internal`) wherever possible
Applied to files:
apps/droid/src/commands/index.tsapps/droid/package.jsonapps/droid/src/logger.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Entry point remains Gunshi-based; only the daily subcommand is wired for now
Applied to files:
apps/droid/src/commands/index.tsapps/droid/src/run.ts
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/package.json : Add new dependencies as `devDependencies` unless explicitly requested otherwise
Applied to files:
apps/droid/package.json
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to apps/**/package.json : All projects under `apps/` ship as bundled CLIs/binaries - list runtime dependencies in `devDependencies` (never `dependencies`) so the bundler owns the runtime payload
Applied to files:
apps/droid/package.json
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/**/package.json : Add dependencies as `devDependencies` unless explicitly requested to add as runtime dependencies
Applied to files:
apps/droid/package.json
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Applies to apps/codex/**/package.json : Package Codex as a bundled CLI and keep every runtime dependency in devDependencies so the bundle includes shipped code
Applied to files:
apps/droid/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/**/package.json : Add dependencies as devDependencies unless explicitly required otherwise
Applied to files:
apps/droid/package.json
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/package.json : Keep every runtime dependency in `devDependencies` because OpenCode is packaged as a bundled CLI
Applied to files:
apps/droid/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/**/package.json : Because the CLI is bundled, keep all runtime libraries in devDependencies so the bundler captures them
Applied to files:
apps/droid/package.json
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Calculate costs using model pricing data when pre-calculated `cost` field is not present in OpenCode messages
Applied to files:
apps/droid/src/monthly-report.tsapps/droid/src/_macro.tsapps/droid/src/pricing.tsapps/droid/src/_types.ts
📚 Learning: 2026-01-09T12:35:27.240Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/amp/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:35:27.240Z
Learning: Write all vitest test blocks alongside implementation files using if (import.meta.vitest != null) pattern
Applied to files:
apps/droid/vitest.config.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Fetch per-model pricing from LiteLLM model_prices_and_context_window.json via LiteLLMPricingFetcher using an offline cache scoped to Codex-prefixed models; handle aliases (e.g., gpt-5-codex → gpt-5) in CodexPricingSource
Applied to files:
apps/droid/src/_macro.tsapps/droid/src/pricing.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Map token fields: `inputTokens` ← `tokens.input`, `outputTokens` ← `tokens.output`, `cacheReadInputTokens` ← `tokens.cache.read`, `cacheCreationInputTokens` ← `tokens.cache.write`
Applied to files:
apps/droid/src/data-loader.tsapps/droid/src/token-utils.tsapps/droid/src/_types.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Use PascalCase for type names (e.g., `UsageData`, `ModelBreakdown`)
Applied to files:
apps/droid/src/data-loader.tsapps/droid/src/_types.ts
📚 Learning: 2026-01-09T12:35:27.240Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/amp/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:35:27.240Z
Learning: Extract token usage from usageLedger.events[] array and cache token information from messages[].usage in thread JSON files
Applied to files:
apps/droid/src/data-loader.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Do not use console.log; use the logger utilities from `src/logger.ts` instead
Applied to files:
apps/droid/src/data-loader.tsapps/droid/src/logger.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Use `fs-fixture` with `createFixture()` to simulate Claude data in tests
Applied to files:
apps/droid/src/data-loader.tsapps/droid/src/factory-settings.ts
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/**/*.test.{ts,tsx} : Use `fs-fixture` for creating mock data in tests for testing data loading functionality
Applied to files:
apps/droid/src/data-loader.tsapps/droid/src/factory-settings.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: For sessions missing model metadata, fall back to model gpt-5 to keep tokens visible; tag events isFallbackModel === true and surface isFallback in aggregates
Applied to files:
apps/droid/src/data-loader.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.test.{ts,tsx,js,jsx} : Use `fs-fixture` with `using` statement to ensure cleanup in tests
Applied to files:
apps/droid/src/data-loader.tsapps/droid/src/factory-settings.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Pricing tests must inject stub offline loaders to avoid network access
Applied to files:
apps/droid/src/pricing.ts
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.{test,spec}.ts : Use `fs-fixture` for mock data in tests of MCP server functionality
Applied to files:
apps/droid/src/factory-settings.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Tests should use fs-fixture with using to ensure cleanup
Applied to files:
apps/droid/src/factory-settings.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Do not use console.log - use logger.ts instead
Applied to files:
apps/droid/src/logger.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Message structure must include `tokens.input`, `tokens.output`, `tokens.cache.read`, and `tokens.cache.write` fields
Applied to files:
apps/droid/src/token-utils.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : In tests, use current Claude 4 models (sonnet-4, opus-4)
Applied to files:
apps/droid/src/token-utils.ts
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/src/**/*.{ts,tsx} : NEVER use `await import()` dynamic imports anywhere in the codebase
Applied to files:
apps/droid/src/index.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : NEVER use `await import()` dynamic imports in vitest test blocks - this is particularly problematic for test execution
Applied to files:
apps/droid/src/index.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: After any code change, run format, typecheck, and tests in parallel (`pnpm run format`, `pnpm typecheck`, `pnpm run test`)
Applied to files:
apps/droid/src/index.ts
🧬 Code graph analysis (9)
apps/droid/src/commands/monthly.ts (7)
apps/droid/src/_shared-args.ts (1)
sharedArgs(4-57)apps/droid/src/date-utils.ts (1)
normalizeFilterDate(38-49)apps/droid/src/data-loader.ts (1)
loadFactoryTokenUsageEvents(212-340)apps/droid/src/pricing.ts (1)
FactoryPricingSource(62-108)apps/droid/src/monthly-report.ts (1)
buildMonthlyReport(56-120)apps/droid/src/_consts.ts (1)
DEFAULT_TIMEZONE(6-6)packages/terminal/src/table.ts (4)
ResponsiveTable(87-352)formatModelsDisplayMultiline(428-435)formatNumber(359-361)addEmptySeparatorRow(638-641)
apps/droid/src/_shared-args.ts (1)
apps/droid/src/_consts.ts (2)
DEFAULT_TIMEZONE(6-6)DEFAULT_LOCALE(7-7)
apps/droid/src/session-report.ts (3)
apps/droid/src/_types.ts (4)
ModelUsage(18-25)PricingSource(32-34)SessionReportRow(60-72)TokenUsageEvent(3-16)apps/droid/src/token-utils.ts (2)
addUsage(55-62)createEmptyUsage(23-32)apps/droid/src/date-utils.ts (2)
toDateKey(14-24)isWithinRange(51-65)
apps/droid/src/monthly-report.ts (3)
apps/droid/src/_types.ts (4)
ModelUsage(18-25)PricingSource(32-34)MonthlyReportRow(48-58)TokenUsageEvent(3-16)apps/droid/src/token-utils.ts (2)
addUsage(55-62)createEmptyUsage(23-32)apps/droid/src/date-utils.ts (4)
toDateKey(14-24)isWithinRange(51-65)toMonthKey(26-36)formatDisplayMonth(82-93)
apps/droid/src/_macro.ts (2)
packages/internal/src/pricing.ts (1)
LiteLLMModelPricing(49-49)packages/internal/src/pricing-fetch-utils.ts (3)
fetchLiteLLMPricingDataset(11-34)filterPricingDataset(36-47)createPricingDataset(7-9)
apps/droid/src/data-loader.ts (5)
apps/droid/src/_consts.ts (3)
FACTORY_SESSIONS_SUBDIR(11-11)FACTORY_LOGS_SUBDIR(10-10)DROID_LOG_GLOB(9-9)apps/droid/src/_types.ts (2)
TokenUsageEvent(3-16)ModelIdSource(1-1)apps/droid/src/factory-settings.ts (2)
resolveFactoryDir(31-33)loadFactoryCustomModels(35-73)apps/droid/src/token-utils.ts (3)
toTotalTokens(7-21)createEmptyUsage(23-32)subtractUsage(64-104)apps/droid/src/logger.ts (1)
logger(5-5)
apps/droid/src/logger.ts (1)
packages/internal/src/logger.ts (1)
createLogger(5-17)
apps/droid/src/token-utils.ts (1)
apps/droid/src/_types.ts (1)
ModelUsage(18-25)
apps/droid/src/index.ts (1)
apps/droid/src/run.ts (1)
run(14-29)
🔇 Additional comments (50)
apps/droid/eslint.config.js (1)
1-16: No issues found—this ESLint config file is correct.The ESM module type is properly configured (
"type": "module"set at both repo root and app level), andtest/no-importing-vitest-globalsis a standard rule enforced consistently across the entire repository via the@ryoppippi/eslint-configpreset. The configuration aligns with the repo's vitest testing approach.apps/droid/tsconfig.json (1)
1-25: LGTM! Modern, strict TypeScript configuration aligned with Bun 1.2.The configuration correctly uses
"module": "Preserve"and"moduleResolution": "bundler"per Bun documentation, enables strict type safety, and includes vitest types for in-source testing. TheverbatimModuleSyntax+erasableSyntaxOnlycombination ensures proper type-only import handling.The minor differences from React apps (ccusage, pi)—omitting
@types/bun, JSX, andallowJs: true—are intentional and appropriate for a CLI tool. The vitest.config.ts is properly configured withincludeSourceandglobals: truefor the in-source testing pattern.apps/droid/src/logger.ts (1)
1-7: LGTM!Clean logger setup following the established pattern. Properly imports from
@ccusage/internal/loggerand creates a package-scoped logger instance.apps/droid/src/pricing.ts (2)
91-93: Verify: Thinking tokens billed as output tokens.The implementation adds
thinkingTokenstooutputTokenswhen calculating cost. This is likely intentional (thinking tokens are typically billed at output rates), but please confirm this matches the expected pricing behavior for Factory models.
110-137: LGTM!Well-structured in-source test using
import.meta.vitestguard. The test verifies parentheses normalization and offline pricing calculation with a stub loader, correctly using theusingkeyword for automatic disposal.apps/droid/src/token-utils.ts (1)
1-104: LGTM!Well-designed token utility functions with proper handling of normalization, aggregation, and delta computation. The reset detection in
subtractUsageis a thoughtful approach for handling counter resets in log data.apps/droid/src/_types.ts (1)
1-72: LGTM!Well-organized type definitions following naming conventions (PascalCase for types, camelCase for fields). The types provide a clear contract for the token usage tracking and reporting features.
apps/droid/vitest.config.ts (1)
10-14: The Macros plugin filter does not need to include_macro.ts.While
_macro.tsis imported bypricing.ts, it contains no macro directives that require processing by the Macros plugin. It is a regular TypeScript module that exports a standard async function. The filename is misleading but does not indicate actual macro usage.apps/droid/src/index.ts (1)
1-6: LGTM! Clean CLI entry point.The entry point correctly uses a Node shebang, imports with
.tsextension per guidelines, and leverages top-level await for async CLI execution. The structure is simple and appropriate for a CLI binary.apps/droid/src/run.ts (1)
14-29: LGTM! CLI wiring is well-structured.The
run()function correctly normalizes arguments for npx invocation, wires the gunshi CLI framework with all three subcommands, and sets appropriate metadata. The JSON import from package.json is supported in modern Node/TypeScript environments.apps/droid/src/commands/index.ts (1)
1-3: LGTM! Clean command barrel export.The module correctly re-exports the three command implementations using
.tsextensions and only exports what's consumed byrun.ts. This follows the established pattern and guidelines.apps/droid/package.json (1)
44-63: LGTM! Dependencies correctly configured for bundled CLI.All runtime dependencies are properly listed in
devDependenciesper the guidelines for bundled CLIs. This ensures the bundler controls the runtime payload and nothing is unnecessarily included in the published package dependencies.apps/droid/tsdown.config.ts (1)
1-25: LGTM!The tsdown build configuration is well-structured with appropriate settings for a CLI application. The Macros plugin integration and vitest stripping are correctly configured.
apps/droid/README.md (1)
1-90: LGTM!The documentation is comprehensive, well-organized, and provides clear examples for users. The custom model resolution feature and environment variable documentation are particularly helpful.
apps/droid/src/commands/session.ts (4)
26-35: LGTM!The helper function provides clear user feedback for missing pricing data with appropriate truncation for readability.
37-56: LGTM!Proper handling of JSON mode, date normalization, and error scenarios. The logger level adjustment for JSON output is essential for clean JSON responses.
58-94: LGTM!Proper handling of data loading, empty results, and missing directory scenarios. The separation between "no data" and "no data matching filters" improves user experience.
96-222: LGTM!The totals calculation, table rendering, and resource cleanup are all correctly implemented. Proper use of
Symbol.dispose()in the finally block ensures pricing resources are always cleaned up.apps/droid/src/commands/daily.ts (3)
33-76: LGTM!The command setup, date filtering, data loading, and report building follow the same solid patterns as the session command. Consistent error handling and user feedback improve the overall CLI experience.
78-125: LGTM!The totals calculation and JSON output formatting are correctly implemented with appropriate structure for programmatic consumption.
127-189: LGTM!The table rendering with responsive layout and resource cleanup is correctly implemented. The compact mode messaging helps users understand why they're not seeing all columns.
apps/droid/src/_shared-args.ts (1)
1-57: LGTM!The shared arguments are well-structured with clear descriptions, appropriate types, and sensible defaults. The use of
satisfies Argsensures type safety, and the export is correct since these arguments are consumed by multiple command modules.apps/droid/src/commands/monthly.ts (8)
1-18: LGTM!Imports are well-organized with proper
.tsextensions for local files, and the module structure follows the established patterns in the codebase.
21-30: LGTM!The helper function provides clear, user-friendly warnings about missing pricing models with appropriate truncation for long lists.
42-51: LGTM!Date filter normalization with proper error handling and early exit on invalid input.
53-65: LGTM!Data loading with appropriate handling for missing logs directory and empty results.
67-87: LGTM!Proper resource lifecycle management with try/finally ensuring
FactoryPricingSourcedisposal.
89-109: LGTM!Totals aggregation logic is correct.
111-124: LGTM!JSON output includes all relevant data in a well-structured format.
126-184: LGTM!Table output implementation with responsive layout, compact mode support, and clear user feedback.
apps/droid/src/monthly-report.ts (3)
1-5: LGTM!Imports are correct with proper
.tsextensions for local modules.
6-24: LGTM!Type definitions follow PascalCase convention and are appropriately scoped (internal vs exported).
56-119: LGTM!The report building logic is well-structured with proper date filtering, lazy summary initialization, and graceful handling of missing pricing data.
apps/droid/src/daily-report.ts (3)
1-5: LGTM!Imports are correct with proper
.tsextensions for local modules.
56-118: LGTM!The daily report logic correctly aggregates by date key and follows the same well-structured pattern as the monthly report.
120-183: LGTM!Inline tests properly use vitest globals without imports and cover key scenarios including aggregation, missing pricing tolerance, and cost calculation.
apps/droid/src/session-report.ts (2)
1-5: LGTM!Imports are correct with proper
.tsextensions for local modules.
58-128: LGTM!Session report logic correctly groups by project and session, tracks last activity timestamps, and sorts by most recent activity first.
apps/droid/src/date-utils.ts (5)
1-12: LGTM!
safeTimeZoneprovides robust timezone validation with sensible fallbacks.
14-36: LGTM!Date key functions use
en-CAlocale for consistent ISO-like date formatting with proper timezone support.
38-49: LGTM!Date normalization handles both compact and hyphenated formats with clear error messaging.
51-65: LGTM!Range check logic correctly handles optional bounds with inclusive comparison.
95-108: LGTM!DateTime formatting with proper timezone support and locale-aware style.
apps/droid/src/data-loader.ts (7)
16-32: LGTM!Error wrapper and type definition are correct.
34-58: LGTM!Valibot schemas are well-structured with appropriate optional field handling.
60-140: LGTM!Helper functions are well-implemented with proper Result type usage and efficient mtime-based caching.
142-201: LGTM!Log line parsing is defensive with proper null returns on any parsing failure and schema validation.
203-340: LGTM!The main data loading function is well-structured with proper resource cleanup, Result type usage for error handling, and delta computation for token usage.
342-506: LGTM!Comprehensive test coverage for log parsing, delta computation, counter resets, and model ID resolution fallbacks.
1-14: The current code structure is correct—do not move the import.The top-level
fs-fixtureimport is the established pattern across the entire codebase (apps/ccusage,apps/amp,apps/codex, etc.). Vitest'sincludeSourceconfiguration combined withdefine: { 'import.meta.vitest': 'undefined' }automatically strips test blocks and their imports from production builds, so tree-shaking concerns do not apply. Usingawait import()would violate coding guidelines that explicitly forbid dynamic imports in this context.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
apps/droid/src/_macro.ts (1)
29-37: Consider using Result type pattern for error handling.The coding guidelines recommend preferring
@praha/byethrow Resulttype over traditional try-catch for functional error handling. Consider refactoring this function to useResult.try().♻️ Refactor to use Result pattern
-export async function prefetchFactoryPricing(): Promise<Record<string, LiteLLMModelPricing>> { - try { - const dataset = await fetchLiteLLMPricingDataset(); - return filterPricingDataset(dataset, isFactoryModel); - } catch (error) { - logger.warn('Failed to prefetch Factory pricing data, proceeding with empty cache.', error); - return createPricingDataset(); - } -} +export async function prefetchFactoryPricing(): Promise<Record<string, LiteLLMModelPricing>> { + const result = await Result.try(async () => { + const dataset = await fetchLiteLLMPricingDataset(); + return filterPricingDataset(dataset, isFactoryModel); + }); + + if (Result.isFailure(result)) { + logger.warn('Failed to prefetch Factory pricing data, proceeding with empty cache.', result.error); + return createPricingDataset(); + } + + return result.value; +}Note: You'll need to import
Resultfrom@praha/byethrow.Based on coding guidelines.
apps/droid/src/date-utils.ts (1)
91-104: Consider clarifying unused timezone parameters.The
_timezoneparameter informatDisplayDateandformatDisplayMonthis marked as unused (underscore prefix) and both functions hardcodetimeZone: 'UTC'. If these parameters are present for API consistency withformatDisplayDateTime, consider adding a comment to clarify. Alternatively, if timezone-aware formatting isn't needed for these functions, the parameter could be removed.Also applies to: 109-120
apps/droid/src/daily-report.ts (1)
30-67: Consider extracting shared helpers to a common module.The functions
formatModelDisplay,addEventUsage, andgetOrCreateModelUsageare duplicated verbatim insession-report.ts(and likely inmonthly-report.ts). Extracting them to a shared module likereport-utils.tswould improve maintainability and reduce duplication.♻️ Suggested extraction
Create a new file
apps/droid/src/report-utils.ts:// apps/droid/src/report-utils.ts import type { ModelUsage, TokenUsageEvent } from './_types.ts'; import { addUsage, createEmptyUsage } from './token-utils.ts'; export function formatModelDisplay(event: TokenUsageEvent): string { const suffix = event.modelIdSource === 'settings' ? ' [inferred]' : ''; if (event.modelId.startsWith('custom:')) { const base = event.pricingModel.trim() !== '' ? event.pricingModel : event.modelId; return `${base} [custom]${suffix}`; } return `${event.modelId}${suffix}`; } export function addEventUsage(target: ModelUsage, event: TokenUsageEvent): void { addUsage(target, { inputTokens: event.inputTokens, outputTokens: event.outputTokens, thinkingTokens: event.thinkingTokens, cacheReadTokens: event.cacheReadTokens, cacheCreationTokens: event.cacheCreationTokens, }); } export function getOrCreateModelUsage(map: Map<string, ModelUsage>, key: string): ModelUsage { const existing = map.get(key); if (existing != null) { return existing; } const created = createEmptyUsage(); map.set(key, created); return created; }apps/droid/src/factory-settings.ts (3)
1-16: Movefs-fixtureimport inside the test block.The
fs-fixtureimport at line 12 is only used within the test block. Importing it at the module level means it will be loaded in production, which may fail if it's a dev dependency or cause unnecessary overhead.♻️ Proposed fix
Move the import inside the test block:
-import { createFixture } from 'fs-fixture';Then at line 98:
if (import.meta.vitest != null) { + const { createFixture } = await import('fs-fixture'); describe('loadFactoryCustomModels', () => {Note: Per coding guidelines,
await import()is discouraged. However, for test-only dependencies withinimport.meta.vitestblocks, this is the standard pattern in the codebase to avoid bundling test dependencies in production.
17-29: Consider extractingtoErrorto a shared module.The
toErrorhelper is duplicated indata-loader.ts(lines 26-28). Consider extracting it to a shared utility module.
98-115: Test fixture cleanup missing.The test creates a fixture but doesn't clean it up. Consider using
fixture.rm()in anafterEachor wrapping in a try/finally.♻️ Proposed fix
it('loads custom model ids from settings.json', async () => { const fixture = await createFixture({ 'settings.json': JSON.stringify( { customModels: [{ id: 'custom:Test-0', model: 'gpt-5.2', provider: 'openai' }], }, null, 2, ), }); - const models = await loadFactoryCustomModels(fixture.path); - expect(models.get('custom:Test-0')?.model).toBe('gpt-5.2'); + try { + const models = await loadFactoryCustomModels(fixture.path); + expect(models.get('custom:Test-0')?.model).toBe('gpt-5.2'); + } finally { + await fixture.rm(); + } });apps/droid/src/data-loader.ts (1)
1-22: Movefs-fixtureimport inside the test block.Same issue as
factory-settings.ts: thefs-fixtureimport is only used within tests but imported at the module level.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
apps/droid/src/_consts.tsapps/droid/src/_macro.tsapps/droid/src/_shared-args.tsapps/droid/src/_types.tsapps/droid/src/commands/daily.tsapps/droid/src/commands/monthly.tsapps/droid/src/commands/session.tsapps/droid/src/daily-report.tsapps/droid/src/data-loader.tsapps/droid/src/date-utils.tsapps/droid/src/factory-settings.tsapps/droid/src/index.tsapps/droid/src/logger.tsapps/droid/src/monthly-report.tsapps/droid/src/pricing.tsapps/droid/src/run.tsapps/droid/src/session-report.tsapps/droid/src/token-utils.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/droid/src/commands/session.ts
- apps/droid/src/logger.ts
- apps/droid/src/index.ts
- apps/droid/src/_consts.ts
- apps/droid/src/run.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Import conventions: Use.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
Use ESLint for linting and formatting with tab indentation and double quotes
No console.log allowed except where explicitly disabled with eslint-disable
Use camelCase for variable names (e.g.,usageDataSchema,modelBreakdownSchema)
Use PascalCase for type names (e.g.,UsageData,ModelBreakdown)
Use UPPER_SNAKE_CASE for constants (e.g.,DEFAULT_CLAUDE_CODE_PATH)
Only export constants, functions, and types that are actually used by other modules - do not export internal/private constants used only within the same file
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
UseResult.try()for wrapping operations that may throw (JSON parsing, etc.)
UseResult.isFailure()for checking errors (more readable than!Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators with Result types
NEVER useawait import()dynamic imports anywhere in the codebase - this causes tree-shaking issues
Do not use console.log - use logger.ts instead
Vitest globals (describe,it,expect) are available automatically without imports in test blocks
NEVER useawait import()dynamic imports in vitest test blocks - this is particularly problematic for test execution
All test files must use current Claude 4 models (claude-sonnet-4-20250514, claude-opus-4-20250514), not outdated Claude 3 models
Model names in tests must exactly match LiteLLM's pricing database entries
Files:
apps/droid/src/_macro.tsapps/droid/src/commands/daily.tsapps/droid/src/factory-settings.tsapps/droid/src/_shared-args.tsapps/droid/src/monthly-report.tsapps/droid/src/pricing.tsapps/droid/src/commands/monthly.tsapps/droid/src/session-report.tsapps/droid/src/date-utils.tsapps/droid/src/daily-report.tsapps/droid/src/_types.tsapps/droid/src/token-utils.tsapps/droid/src/data-loader.ts
**/_*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use underscore prefix for internal files (e.g.,
_types.ts,_utils.ts,_consts.ts)
Files:
apps/droid/src/_macro.tsapps/droid/src/_shared-args.tsapps/droid/src/_types.ts
🧠 Learnings (18)
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Calculate costs using model pricing data when pre-calculated `cost` field is not present in OpenCode messages
Applied to files:
apps/droid/src/_macro.tsapps/droid/src/monthly-report.tsapps/droid/src/pricing.tsapps/droid/src/session-report.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Model names in tests must exactly match LiteLLM's pricing database entries
Applied to files:
apps/droid/src/_macro.tsapps/droid/src/pricing.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Do not use console.log; use the logger utilities from `src/logger.ts` instead
Applied to files:
apps/droid/src/_macro.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Do not use console.log - use logger.ts instead
Applied to files:
apps/droid/src/_macro.ts
📚 Learning: 2025-09-18T17:42:16.550Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: docs/CLAUDE.md:0-0
Timestamp: 2025-09-18T17:42:16.550Z
Learning: Applies to docs/**/*.{js,cjs,mjs,ts,cts,mts} : Do not use console.log in documentation scripts; use appropriate logging instead
Applied to files:
apps/droid/src/_macro.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Fetch per-model pricing from LiteLLM model_prices_and_context_window.json via LiteLLMPricingFetcher using an offline cache scoped to Codex-prefixed models; handle aliases (e.g., gpt-5-codex → gpt-5) in CodexPricingSource
Applied to files:
apps/droid/src/_macro.tsapps/droid/src/pricing.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: The data loading architecture follows a clear separation of concerns: Data Loading (data-loader.ts), Token Aggregation (calculate-cost.ts), Command Execution (commands/), and CLI Entry (index.ts)
Applied to files:
apps/droid/src/commands/daily.tsapps/droid/src/data-loader.ts
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/**/*.test.{ts,tsx} : Use `fs-fixture` for creating mock data in tests for testing data loading functionality
Applied to files:
apps/droid/src/factory-settings.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Use `fs-fixture` with `createFixture()` to simulate Claude data in tests
Applied to files:
apps/droid/src/factory-settings.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Reuse shared packages (`ccusage/terminal`, `ccusage/internal`) wherever possible
Applied to files:
apps/droid/src/_shared-args.ts
📚 Learning: 2026-01-09T12:35:27.240Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/amp/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:35:27.240Z
Learning: Reuse shared packages ccusage/terminal and ccusage/internal wherever possible in Amp CLI
Applied to files:
apps/droid/src/_shared-args.ts
📚 Learning: 2026-01-09T12:22:39.520Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T12:22:39.520Z
Learning: Applies to **/*.ts : Use PascalCase for type names (e.g., `UsageData`, `ModelBreakdown`)
Applied to files:
apps/droid/src/_types.tsapps/droid/src/data-loader.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Map token fields: `inputTokens` ← `tokens.input`, `outputTokens` ← `tokens.output`, `cacheReadInputTokens` ← `tokens.cache.read`, `cacheCreationInputTokens` ← `tokens.cache.write`
Applied to files:
apps/droid/src/_types.tsapps/droid/src/token-utils.tsapps/droid/src/data-loader.ts
📚 Learning: 2026-01-09T12:07:00.515Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/pi/CLAUDE.md:0-0
Timestamp: 2026-01-09T12:07:00.515Z
Learning: Applies to apps/pi/src/**/*.{ts,tsx} : Use `.ts` extensions for local imports
Applied to files:
apps/droid/src/_types.ts
📚 Learning: 2026-01-09T11:07:17.610Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/opencode/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:17.610Z
Learning: Applies to apps/opencode/**/*.{ts,tsx,js,jsx} : Message structure must include `tokens.input`, `tokens.output`, `tokens.cache.read`, and `tokens.cache.write` fields
Applied to files:
apps/droid/src/_types.tsapps/droid/src/token-utils.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Only export symbols that are actually used by other modules
Applied to files:
apps/droid/src/token-utils.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Write tests in-source using `if (import.meta.vitest != null)` blocks instead of separate test files
Applied to files:
apps/droid/src/token-utils.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Use payload.info.total_token_usage as cumulative totals and payload.info.last_token_usage as per-turn delta; when only cumulative totals exist, compute delta by subtracting previous totals
Applied to files:
apps/droid/src/token-utils.ts
🧬 Code graph analysis (7)
apps/droid/src/_macro.ts (3)
packages/internal/src/pricing.ts (1)
LiteLLMModelPricing(49-49)packages/internal/src/pricing-fetch-utils.ts (3)
fetchLiteLLMPricingDataset(11-34)filterPricingDataset(36-47)createPricingDataset(7-9)apps/droid/src/logger.ts (1)
logger(9-9)
apps/droid/src/factory-settings.ts (2)
apps/droid/src/_consts.ts (2)
FACTORY_DIR_ENV(8-8)DEFAULT_FACTORY_DIR(9-9)apps/droid/src/logger.ts (1)
logger(9-9)
apps/droid/src/_shared-args.ts (1)
apps/droid/src/_consts.ts (2)
DEFAULT_TIMEZONE(10-10)DEFAULT_LOCALE(11-11)
apps/droid/src/monthly-report.ts (3)
apps/droid/src/_types.ts (4)
ModelUsage(31-38)PricingSource(51-53)MonthlyReportRow(73-83)TokenUsageEvent(13-26)apps/droid/src/token-utils.ts (2)
addUsage(79-86)createEmptyUsage(39-48)apps/droid/src/date-utils.ts (4)
toDateKey(24-34)isWithinRange(72-86)toMonthKey(39-49)formatDisplayMonth(109-120)
apps/droid/src/session-report.ts (3)
apps/droid/src/_types.ts (4)
ModelUsage(31-38)PricingSource(51-53)SessionReportRow(88-100)TokenUsageEvent(13-26)apps/droid/src/token-utils.ts (2)
addUsage(79-86)createEmptyUsage(39-48)apps/droid/src/date-utils.ts (2)
toDateKey(24-34)isWithinRange(72-86)
apps/droid/src/token-utils.ts (1)
apps/droid/src/_types.ts (1)
ModelUsage(31-38)
apps/droid/src/data-loader.ts (5)
apps/droid/src/_consts.ts (3)
FACTORY_SESSIONS_SUBDIR(15-15)FACTORY_LOGS_SUBDIR(14-14)DROID_LOG_GLOB(13-13)apps/droid/src/_types.ts (2)
TokenUsageEvent(13-26)ModelIdSource(8-8)apps/droid/src/factory-settings.ts (2)
resolveFactoryDir(49-51)loadFactoryCustomModels(58-96)apps/droid/src/token-utils.ts (3)
toTotalTokens(20-34)createEmptyUsage(39-48)subtractUsage(93-133)apps/droid/src/logger.ts (1)
logger(9-9)
🔇 Additional comments (36)
apps/droid/src/monthly-report.ts (1)
1-136: LGTM! Well-structured monthly aggregation logic.The implementation follows established patterns from daily-report.ts, properly separates internal helpers from the public API, and handles missing pricing gracefully by collecting affected models for user feedback.
apps/droid/src/date-utils.ts (1)
8-86: LGTM! Solid date utility implementations.The functions provide robust date handling with:
- Safe timezone fallbacks
- Consistent ISO-like date formatting using en-CA locale
- Input validation with clear error messages
- Appropriate use of UTC for display functions to avoid timezone-related display issues
apps/droid/src/_shared-args.ts (1)
1-64: LGTM! Well-defined shared CLI arguments.The argument definitions are comprehensive and properly structured with:
- Type-safe constraints via
satisfies Args- Consistent short flags for common options
- Appropriate defaults using constants
- Support for negatable boolean flags (offline)
This will provide a consistent UX across daily, monthly, and session commands.
apps/droid/src/commands/daily.ts (4)
1-24: LGTM! Proper imports and constants.All imports use
.tsextensions as required, and theTABLE_COLUMN_COUNTconstant is appropriately defined and matches the table structure.
29-38: LGTM! User-friendly warning helper.The function provides clear feedback about missing pricing models with a preview, helping users understand potential cost estimation gaps without overwhelming them with long lists.
44-73: LGTM! Robust validation and error handling.The command properly:
- Configures logger level for JSON mode
- Validates date inputs with appropriate error messages and exit codes
- Handles missing log directories gracefully
- Provides early returns with appropriate output for edge cases
75-196: LGTM! Well-structured command implementation with proper resource management.The implementation demonstrates good practices:
- Resource cleanup via
Symbol.disposein finally block- Comprehensive totals calculation
- Dual output modes (JSON and table) with appropriate formatting
- Responsive table with compact mode support
- User-friendly feedback for missing pricing and compact mode
apps/droid/src/commands/monthly.ts (5)
1-22: LGTM!Clean imports and file structure. The command follows established patterns from other ccusage apps with proper module organization.
23-37: LGTM!The
TABLE_COLUMN_COUNTconstant correctly matches the 9 columns defined in the table header, and thesummarizeMissingPricinghelper provides useful feedback without overwhelming the user.
39-72: LGTM!The command setup follows best practices: proper logger silencing for JSON mode, date normalization with error handling, and graceful handling of missing logs directory.
74-131: LGTM!Report building and JSON output logic are well-structured with proper try/finally for resource cleanup. The totals accumulation is correct.
133-195: LGTM!Table rendering with responsive mode support and compact mode messaging follows established patterns. The
Symbol.disposeusage in the finally block ensures proper cleanup of the pricing source.apps/droid/src/daily-report.ts (3)
1-29: LGTM!Clean type definitions and imports. The
DailyReportOptionsandDailyReportResulttypes provide a clear public API.
69-137: LGTM!The
buildDailyReportfunction correctly aggregates events by day, handles missing pricing gracefully, and produces well-sorted output. The pricing error handling via try/catch with accumulation intomissingPricingModelsis appropriate.
139-201: LGTM!The test correctly validates aggregation behavior and missing pricing handling. The stub pricing source appropriately simulates partial pricing availability.
apps/droid/src/pricing.ts (5)
1-33: LGTM!Clean imports and type definitions. The provider prefixes cover common Factory-supported providers.
35-45: LGTM!The singleton promise pattern for prefetched pricing is appropriate for caching expensive network calls across multiple
FactoryPricingSourceinstances within the same process.
47-73: LGTM!The
normalizeModelCandidatesfunction correctly generates fallback candidates by stripping parenthetical suffixes and handling-thinkingvariants. This improves LiteLLM pricing match rates for Factory's model naming conventions.
75-132: LGTM!The
FactoryPricingSourceclass properly implements thePricingSourceinterface with:
- Correct disposal forwarding to the underlying fetcher
- Iterative candidate matching for pricing lookups
- Proper error propagation when all candidates fail
134-161: LGTM!The test correctly validates parentheses-suffix normalization by providing a custom
offlineLoaderwith mocked pricing data. Since the test uses its own pricing dataset, the model name doesn't need to match LiteLLM's actual database entries.apps/droid/src/token-utils.ts (4)
1-48: LGTM!Clean utility functions with proper documentation. The
ensureNonNegativeNumberhelper correctly handles edge cases (non-numbers, negative values, non-finite values), andcreateEmptyUsageprovides a consistent factory function.
50-86: LGTM!The
normalizeUsageandaddUsagefunctions correctly handle unknown input validation and mutable aggregation with automatictotalTokensrecalculation.
88-133: LGTM!The
subtractUsagefunction correctly handles delta computation with reset detection. When any counter decreases (indicating a session reset), it returns the current values as the delta rather than a negative value.
135-184: LGTM!Tests correctly validate delta computation and reset behavior. The test cases cover normal deltas and counter reset scenarios.
apps/droid/src/session-report.ts (2)
1-31: LGTM!Clean type definitions and imports. The
SessionReportOptionsandSessionReportResulttypes provide a clear public API consistent with other report modules.
71-146: LGTM, but consider adding unit tests.The
buildSessionReportfunction correctly aggregates events by session, handles missing pricing gracefully, and sorts by most recent activity. However, unlikedaily-report.ts, this file lacks inline tests. Consider adding test coverage for key behaviors like session grouping and pricing error handling.apps/droid/src/factory-settings.ts (2)
31-51: LGTM!The schemas are well-defined with appropriate optionality, and
resolveFactoryDircorrectly implements the precedence chain: CLI → environment → default.
53-96: LGTM!The
loadFactoryCustomModelsfunction properly handles all error cases (missing file, invalid JSON, schema validation failures) with appropriate warnings. The ENOENT case correctly returns an empty map silently.apps/droid/src/data-loader.ts (7)
23-68: LGTM!Well-structured schemas for parsing log line payloads. The
tokenUsageSchemawith optional fields handles partial data gracefully.
70-110: LGTM!The helper functions
asNonEmptyString,extractTimestampFromLogLine, andextractProjectKeyFromSettingsPathare clean and handle edge cases appropriately.
112-164: LGTM!The
loadModelIdFromSessionSettingsfunction correctly implements mtime-based caching to avoid redundant file reads. The cache invalidation logic is sound.
166-230: LGTM!The
parseSessionSettingsLogLinefunction is well-structured with proper validation at each step. The early returns for invalid data keep the happy path clear.
232-261: LGTM!Clean type definitions for the public API. The
LoadFactoryResultcorrectly signals missing logs directory separately from empty events.
254-382: LGTM!The
loadFactoryTokenUsageEventsfunction is well-implemented:
- Log files are sorted by mtime for chronological processing
- Model ID resolution follows correct precedence (tag → settings → session)
- Delta computation handles counter resets via
subtractUsage- Stream resources are properly cleaned up in the finally block
384-547: LGTM!Comprehensive test coverage for:
- Delta computation from cumulative counters
- Counter reset detection
- Model ID fallback from session history
- Model ID resolution from settings files
Consider adding fixture cleanup (as noted for
factory-settings.ts), but the test logic is solid.apps/droid/src/_types.ts (1)
1-100: LGTM! Well-structured type definitions.The type definitions are clean, consistent, and follow all coding guidelines:
- PascalCase for type names, camelCase for fields ✓
- Appropriate underscore prefix for internal file ✓
- Consistent token field structure across ModelUsage and all report row types ✓
- Clear JSDoc documentation for each type ✓
- All exports are used by other modules per the AI summary ✓
The file provides a solid API surface for the Droid usage pipeline with no runtime logic, which is exactly what a
_types.tsfile should contain.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
Adds a new ccusage-style CLI app for Factory’s
droidto report token usage and estimated cost from local Factory data.This introduces a new package:
@ccusage/droid(binary:ccusage-droid)with subcommands:
daily(default)monthlysessionMotivation
Factory’s
droidCLI records cumulative token counters in~/.factory/logs/droid-log-*.logand writes per-session settings in~/.factory/sessions/*.settings.json. This PR makes those artifacts usable for the same daily/monthly/session reporting workflow that ccusage provides for other tools.What’s included
Log parsing + delta aggregation
"[Session] Saving session settings"log lines and extracts cumulativetokenUsage.sessionId(handles counter resets).Model attribution improvements (reduces
unknown)value.path) and uses itsmodelfield.unknown.[inferred].Custom model support
custom:*) via~/.factory/settings.json(customModels[]).${pricingModel} [custom]in tables.Cost calculation via LiteLLM pricing
--offlineto avoid fetching updated pricing.$0with a warning (consistent with other apps).How to test
pnpm --filter @ccusage/droid test pnpm --filter @ccusage/droid start -- daily pnpm --filter @ccusage/droid start -- monthly --since 2026-01-01 --until 2026-01-10 pnpm --filter @ccusage/droid start -- session --jsonNotes
For full transparency - this PR was almost fully vibe coded, but It includes unit tests and I also extensively tested it manually and didn't find any bugs.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.