-
-
Notifications
You must be signed in to change notification settings - Fork 312
feat(opencode): add OpenCode CLI package #758
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
refactor opencode integration by creating standalone apps/opencode package following the same architecture as apps/codex. this separates opencode-specific code from ccusage for better maintainability. changes: - create @ccusage/opencode package with daily command - integrate pricingfetcher for accurate cost calculation from token data - move opencode data loader and utilities to dedicated package - remove opencode commands from apps/ccusage - update root documentation to list opencode in ccusage family the package currently supports daily usage reports with accurate cost calculation for claude models. costs are calculated from token data using litellm pricing database since opencode stores cost: 0 in message files. binary: ccusage-opencode available commands: daily (session, weekly, monthly, blocks to be added)
implement remaining usage reporting commands for opencode package following the same patterns as ccusage and codex packages. changes: - add session command: groups usage by conversation sessions with accurate cost calculation - add monthly command: aggregates usage by month (yyyy-mm format) - add weekly command: aggregates usage by iso week (yyyy-www format) with in-source tests - update data-loader to include sessionid in loadedusageentry type - update cli routing to include all commands (daily, session, monthly, weekly) - add readme.md with comprehensive usage documentation and examples all commands use pricingfetcher for accurate cost calculation from token data since opencode stores cost: 0 in message files. commands support --json and --compact flags for flexible output formatting. test coverage: 331 tests passing (including 4 new weekly command tests)
WalkthroughAdds a new CLI package Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant CLI as Gunshi CLI
participant Run as run.ts
participant Cmd as Command (daily|weekly|monthly|session)
participant Loader as Data Loader
participant Pricing as LiteLLM Pricing
participant Formatter as Table/JSON Formatter
participant Stdout as stdout
User->>CLI: invoke ccusage-opencode [command] [flags]
CLI->>Run: normalized args (strip npx/binary)
Run->>Cmd: dispatch selected command
Cmd->>Loader: load messages & sessions
Loader-->>Cmd: return LoadedUsageEntry[]
Cmd->>Cmd: group & aggregate tokens/models
alt entry.costUSD missing
Cmd->>Pricing: calculateCostFromTokens(model,tokens)
Pricing-->>Cmd: return costUSD
else cached cost present
Cmd-->>Cmd: use entry.costUSD
end
Cmd->>Formatter: produce JSON or formatted table (compact optional)
Formatter->>Stdout: write output
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
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: 5
♻️ Duplicate comments (2)
apps/opencode/src/commands/daily.ts (1)
18-37: Extract duplicated calculateCostForEntry helper.This function is duplicated identically across daily.ts, monthly.ts, and session.ts. See the refactoring suggestion in monthly.ts (lines 18-37) to extract this to a shared module.
apps/opencode/src/commands/session.ts (1)
18-37: Extract duplicated calculateCostForEntry helper.This function is duplicated identically across daily.ts, monthly.ts, and session.ts. See the refactoring suggestion in monthly.ts (lines 18-37) to extract this to a shared module.
🧹 Nitpick comments (2)
apps/opencode/src/data-loader.ts (2)
42-42: Consider usinghomedir()fromnode:osfor consistency.The
apps/ccusagepackage useshomedir()fromnode:osfor user home directory resolution. Using the same approach here would improve consistency across sibling packages.🔎 Proposed refactor
+import { homedir } from 'node:os'; import { readFile } from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; ... -const USER_HOME_DIR = process.env.HOME ?? process.env.USERPROFILE ?? process.cwd(); +const USER_HOME_DIR = homedir();Based on learnings: treating OpenCode as a sibling to ccusage means reusing patterns where appropriate.
47-60: Extract shared schemas to avoid duplication.The
modelNameSchemaandsessionIdSchemaare duplicated fromapps/ccusage/src/_types.ts. These branded schemas should be extracted to a shared internal package to ensure consistency acrossccusage,codex, andopencode.Consider moving these schemas to
packages/internal/src/types.tsor a similar shared location, then importing them here and inapps/ccusage.Based on learnings: OpenCode should reuse shared packages like its sibling Codex rather than duplicating code.
📜 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)
CLAUDE.md(1 hunks)apps/ccusage/README.md(1 hunks)apps/opencode/AGENTS.md(1 hunks)apps/opencode/CLAUDE.md(1 hunks)apps/opencode/README.md(1 hunks)apps/opencode/eslint.config.js(1 hunks)apps/opencode/package.json(1 hunks)apps/opencode/src/commands/daily.ts(1 hunks)apps/opencode/src/commands/index.ts(1 hunks)apps/opencode/src/commands/monthly.ts(1 hunks)apps/opencode/src/commands/session.ts(1 hunks)apps/opencode/src/commands/weekly.ts(1 hunks)apps/opencode/src/data-loader.ts(1 hunks)apps/opencode/src/index.ts(1 hunks)apps/opencode/src/run.ts(1 hunks)apps/opencode/tsconfig.json(1 hunks)apps/opencode/tsdown.config.ts(1 hunks)apps/opencode/vitest.config.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Place screenshots immediately after the main heading (H1) in documentation pages for immediate visual context
Use relative image paths like/screenshot.pngfor images stored in/docs/public/in documentation
Always include descriptive alt text for images in documentation for accessibility
Files:
apps/opencode/CLAUDE.mdapps/opencode/AGENTS.mdapps/opencode/README.mdCLAUDE.mdapps/ccusage/README.md
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use ESLint for linting and formatting with tab indentation and double quotes
No console.log allowed except where explicitly disabled with eslint-disable; use logger.ts instead
Use file paths with Node.js path utilities for cross-platform compatibility
Use variables starting with lowercase (camelCase) for variable names
Can use UPPER_SNAKE_CASE for constants
Files:
apps/opencode/eslint.config.jsapps/opencode/src/index.tsapps/opencode/tsdown.config.tsapps/opencode/src/commands/index.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/run.tsapps/opencode/vitest.config.tsapps/opencode/src/data-loader.tsapps/opencode/src/commands/session.tsapps/opencode/src/commands/daily.tsapps/opencode/src/commands/weekly.ts
apps/*/package.json
📄 CodeRabbit inference engine (CLAUDE.md)
All projects under
apps/ship as bundled CLIs/binaries - treat runtime dependencies as bundled assets by listing everything in each app'sdevDependencies(neverdependencies)
Files:
apps/opencode/package.json
**/*.{ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Claude model naming convention:
claude-{model-type}-{generation}-{date}(e.g.,claude-sonnet-4-20250514, NOTclaude-4-sonnet-20250514)
Files:
apps/opencode/package.jsonapps/opencode/src/index.tsapps/opencode/tsconfig.jsonapps/opencode/tsdown.config.tsapps/opencode/src/commands/index.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/run.tsapps/opencode/vitest.config.tsapps/opencode/src/data-loader.tsapps/opencode/src/commands/session.tsapps/opencode/src/commands/daily.tsapps/opencode/src/commands/weekly.ts
**/package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Dependencies should always be added as devDependencies unless explicitly requested otherwise
Files:
apps/opencode/package.json
**/*.ts{,x}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript with strict mode and bundler module resolution
Files:
apps/opencode/src/index.tsapps/opencode/tsdown.config.tsapps/opencode/src/commands/index.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/run.tsapps/opencode/vitest.config.tsapps/opencode/src/data-loader.tsapps/opencode/src/commands/session.tsapps/opencode/src/commands/daily.tsapps/opencode/src/commands/weekly.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
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 when checking Results
Keep traditional try-catch only for file I/O with complex error handling or legacy code that's hard to refactor
Always useResult.isFailure()andResult.isSuccess()type guards for better code clarity
Use uppercase (PascalCase) for type names
Only export constants, functions, and types that are actually used by other modules - internal constants used only within the same file should NOT be exported
In-source testing pattern: write tests directly in source files usingif (import.meta.vitest != null)blocks
CRITICAL: DO NOT useawait import()dynamic imports anywhere in the codebase - this causes tree-shaking issues
CRITICAL: Never use dynamic imports withawait import()in vitest test blocks - this is particularly problematic for test execution
Vitest globals (describe,it,expect) are enabled and available without imports since globals are configured
Create mock data usingfs-fixturewithcreateFixture()for Claude data directory simulation in tests
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/opencode/src/index.tsapps/opencode/tsdown.config.tsapps/opencode/src/commands/index.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/run.tsapps/opencode/vitest.config.tsapps/opencode/src/data-loader.tsapps/opencode/src/commands/session.tsapps/opencode/src/commands/daily.tsapps/opencode/src/commands/weekly.ts
**/data-loader.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Silently skip malformed JSONL lines during parsing in data loading operations
Files:
apps/opencode/src/data-loader.ts
🧠 Learnings (33)
📓 Common learnings
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Treat Codex as a sibling to apps/ccusage; reuse shared packages, command names, and flag semantics; diverge only when Codex-specific data requires it and document inline
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: For package directories in commits, use the package directory name as scope (e.g., `feat(terminal):`, `fix(ui):`)
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Refer to individual CLAUDE.md files in apps/ccusage/CLAUDE.md, apps/mcp/CLAUDE.md, and docs/CLAUDE.md for package-specific guidance
Applied to files:
apps/opencode/CLAUDE.mdapps/opencode/README.mdCLAUDE.mdapps/ccusage/README.md
📚 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: Treat Codex as a sibling to apps/ccusage; reuse shared packages, command names, and flag semantics; diverge only when Codex-specific data requires it and document inline
Applied to files:
apps/opencode/CLAUDE.mdapps/opencode/AGENTS.mdapps/opencode/README.mdCLAUDE.mdapps/ccusage/README.md
📚 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/opencode/CLAUDE.mdapps/opencode/package.jsonCLAUDE.md
📚 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/opencode/CLAUDE.mdCLAUDE.md
📚 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/opencode/CLAUDE.mdCLAUDE.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Vitest globals (`describe`, `it`, `expect`) are enabled and available without imports since globals are configured
Applied to files:
apps/opencode/eslint.config.jsapps/opencode/tsconfig.jsonapps/opencode/tsdown.config.tsapps/opencode/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/opencode/eslint.config.jsapps/opencode/tsconfig.jsonapps/opencode/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/opencode/eslint.config.jsapps/opencode/tsconfig.jsonapps/opencode/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 : Use Vitest globals (`describe`, `it`, `expect`) without imports in test blocks
Applied to files:
apps/opencode/eslint.config.jsapps/opencode/tsconfig.jsonapps/opencode/vitest.config.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Only export constants, functions, and types that are actually used by other modules - internal constants used only within the same file should NOT be exported
Applied to files:
apps/opencode/eslint.config.jsapps/opencode/tsconfig.jsonapps/opencode/tsdown.config.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : In-source testing pattern: write tests directly in source files using `if (import.meta.vitest != null)` blocks
Applied to files:
apps/opencode/eslint.config.jsapps/opencode/tsconfig.jsonapps/opencode/vitest.config.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use ESLint for linting and formatting with tab indentation and double quotes
Applied to files:
apps/opencode/eslint.config.jsapps/opencode/tsconfig.json
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to apps/*/package.json : All projects under `apps/` ship as bundled CLIs/binaries - treat runtime dependencies as bundled assets by listing everything in each app's `devDependencies` (never `dependencies`)
Applied to files:
apps/opencode/package.jsonapps/opencode/src/run.tsCLAUDE.md
📚 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/opencode/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/opencode/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/opencode/package.jsonCLAUDE.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : CRITICAL: DO NOT use `await import()` dynamic imports anywhere in the codebase - this causes tree-shaking issues
Applied to files:
apps/opencode/src/index.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : CRITICAL: Never use dynamic imports with `await import()` in vitest test blocks - this is particularly problematic for test execution
Applied to files:
apps/opencode/src/index.tsapps/opencode/tsconfig.jsonapps/opencode/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: After any code change, run format, typecheck, and tests in parallel (`pnpm run format`, `pnpm typecheck`, `pnpm run test`)
Applied to files:
apps/opencode/src/index.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Run `pnpm typecheck` to type check code with TypeScript
Applied to files:
apps/opencode/src/index.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: After making any code changes, run `pnpm run format`, `pnpm typecheck`, and `pnpm run test` in parallel
Applied to files:
apps/opencode/src/index.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.ts{,x} : Use TypeScript with strict mode and bundler module resolution
Applied to files:
apps/opencode/tsconfig.jsonapps/opencode/tsdown.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/**/*.ts : Only export what is actually used
Applied to files:
apps/opencode/tsconfig.jsonapps/opencode/tsdown.config.tsapps/opencode/src/commands/index.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Use `.ts` extensions for local file imports (e.g., `import { foo } from './utils.ts'`)
Applied to files:
apps/opencode/tsconfig.jsonapps/opencode/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/opencode/tsconfig.jsonapps/opencode/src/commands/index.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Use uppercase (PascalCase) for type names
Applied to files:
apps/opencode/tsconfig.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/**/*.ts : Use `.ts` extensions for local imports (e.g., `import { foo } from './utils.ts'`)
Applied to files:
apps/opencode/tsconfig.jsonapps/opencode/tsdown.config.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to src/**/_*.ts{,x} : Use underscore prefix for internal files (e.g., `_types.ts`, `_utils.ts`, `_consts.ts`)
Applied to files:
apps/opencode/tsdown.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: Entry point remains Gunshi-based; only the daily subcommand is wired for now
Applied to files:
apps/opencode/src/run.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: All vitest blocks live alongside implementation files using if (import.meta.vitest != null)
Applied to files:
apps/opencode/vitest.config.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/data-loader.ts : Silently skip malformed JSONL lines during parsing in data loading operations
Applied to files:
apps/opencode/src/data-loader.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx,json} : Claude model naming convention: `claude-{model-type}-{generation}-{date}` (e.g., `claude-sonnet-4-20250514`, NOT `claude-4-sonnet-20250514`)
Applied to files:
CLAUDE.md
🧬 Code graph analysis (6)
apps/opencode/src/index.ts (2)
apps/opencode/src/run.ts (1)
run(15-30)apps/ccusage/src/commands/index.ts (1)
run(44-59)
apps/opencode/src/commands/monthly.ts (3)
apps/opencode/src/data-loader.ts (2)
LoadedUsageEntry(102-113)loadOpenCodeMessages(254-311)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)packages/terminal/src/table.ts (4)
ResponsiveTable(38-298)formatModelsDisplayMultiline(355-359)formatNumber(305-307)addEmptySeparatorRow(566-569)
apps/opencode/src/data-loader.ts (2)
apps/ccusage/src/_consts.ts (1)
USER_HOME_DIR(38-38)apps/ccusage/src/_types.ts (2)
modelNameSchema(9-13)sessionIdSchema(15-19)
apps/opencode/src/commands/session.ts (4)
apps/opencode/src/data-loader.ts (3)
LoadedUsageEntry(102-113)loadOpenCodeMessages(254-311)loadOpenCodeSessions(213-248)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)apps/opencode/src/commands/index.ts (1)
sessionCommand(3-3)packages/terminal/src/table.ts (3)
ResponsiveTable(38-298)formatNumber(305-307)addEmptySeparatorRow(566-569)
apps/opencode/src/commands/daily.ts (4)
apps/ccusage/src/data-loader.ts (1)
calculateCostForEntry(633-665)apps/opencode/src/data-loader.ts (2)
LoadedUsageEntry(102-113)loadOpenCodeMessages(254-311)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)packages/terminal/src/table.ts (4)
ResponsiveTable(38-298)formatModelsDisplayMultiline(355-359)formatNumber(305-307)addEmptySeparatorRow(566-569)
apps/opencode/src/commands/weekly.ts (3)
apps/opencode/src/data-loader.ts (2)
LoadedUsageEntry(102-113)loadOpenCodeMessages(254-311)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)packages/terminal/src/table.ts (4)
ResponsiveTable(38-298)formatModelsDisplayMultiline(355-359)formatNumber(305-307)addEmptySeparatorRow(566-569)
🪛 LanguageTool
apps/opencode/README.md
[grammar] ~71-~71: Ensure spelling is correct
Context: ...hare/opencode) - LOG_LEVEL` – control consola log verbosity (0 silent … 5 trace) ## ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (25)
CLAUDE.md (1)
10-11: LGTM!The documentation entries for Codex and OpenCode CLI packages follow the established pattern and maintain consistency with the monorepo structure.
apps/ccusage/README.md (1)
32-35: LGTM!The OpenCode tool entry is well-structured and maintains consistency with the existing ccusage family documentation format.
apps/opencode/tsconfig.json (1)
1-32: LGTM!The TypeScript configuration properly enables strict mode, bundler module resolution, and Vitest integration, aligning with the project's coding guidelines and sibling package patterns.
apps/opencode/tsdown.config.ts (1)
1-11: LGTM!The tsdown build configuration is properly set up for a Node.js CLI with ESM format, node20 target, and appropriate entry point matching the package manifest.
apps/opencode/eslint.config.js (1)
1-12: LGTM!The ESLint configuration properly uses the shared preset with app type and correctly enforces the project's Vitest globals convention.
apps/opencode/AGENTS.md (1)
1-40: LGTM!The documentation clearly explains OpenCode's data structure, token handling, cost calculation, and testing conventions, maintaining consistency with sibling packages.
apps/opencode/vitest.config.ts (1)
1-15: LGTM!The Vitest configuration properly enables globals, in-source testing, and V8 coverage, aligning with the project's testing conventions.
apps/opencode/package.json (3)
38-59: LGTM!All runtime dependencies are correctly placed in
devDependencies, following the bundled CLI pattern for apps underapps/. The workspace package references and catalog dependencies are properly configured.Based on learnings: All projects under
apps/ship as bundled CLIs/binaries - treat runtime dependencies as bundled assets by listing everything in devDependencies.
65-78: [Rewritten review comment]
[Classification tag]
1-79: Package structure is correctly aligned with sibling packages.Verification confirms that opencode's package.json matches the established patterns: bin entry follows the naming convention, main/module entries point to the compiled output, build script uses tsdown, and all runtime dependencies are correctly listed in devDependencies per project guidelines.
apps/opencode/src/index.ts (1)
1-6: LGTM!The entry point is clean and follows the established pattern from apps/ccusage. Proper use of .ts extension for imports and appropriate eslint-disable for top-level await.
apps/opencode/CLAUDE.md (1)
1-66: LGTM!The package documentation is well-structured and aligns with the established patterns from codex and ccusage packages. Clear guidance on architecture, testing, and code style.
Based on learnings, this appropriately treats OpenCode as a sibling to apps/ccusage and apps/codex.
apps/opencode/src/commands/index.ts (1)
1-4: LGTM!Clean barrel export that follows the guideline to only export symbols that are actually used by other modules. All four commands are consumed by the run.ts CLI wiring.
apps/opencode/src/run.ts (1)
1-30: LGTM!The CLI wiring follows the established pattern from apps/ccusage. Proper handling of npx binary name normalization and clean subcommands registration with Gunshi.
apps/opencode/src/commands/session.ts (2)
16-16: TABLE_COLUMN_COUNT is correct here.Unlike daily.ts and monthly.ts which incorrectly use 7, this correctly sets TABLE_COLUMN_COUNT to 8, matching the 8-column table layout.
162-216: Well-implemented parent/subagent hierarchy.The logic for grouping sessions by parent, rendering parent sessions with bold titles, indenting subsessions with
↳, and computing subtotals is well-structured and readable.apps/opencode/src/data-loader.ts (3)
151-162: LGTM - Silent error handling is appropriate here.The try-catch pattern with null return correctly implements the "silently skip malformed data" principle for data loading operations. While the codebase generally prefers
Result.try(), the traditional try-catch is acceptable for these internal data loading utilities.Based on learnings: data-loader files should silently skip malformed data during parsing.
254-311: LGTM - Comprehensive message loading with appropriate filtering.The implementation correctly:
- Discovers message files using glob patterns
- Filters out messages with no tokens or missing provider/model
- Deduplicates by message ID
- Converts to the internal
LoadedUsageEntryformatThe filtering logic aligns with the PR objectives where actual costs are computed from tokens rather than reading the stored
cost: 0values.
313-372: LGTM - Test coverage for conversion logic.The in-source tests appropriately verify:
- Conversion of messages with full token and cache data
- Handling of missing optional fields with proper defaults
Tests follow the coding guidelines for in-source testing pattern using
if (import.meta.vitest).apps/opencode/src/commands/weekly.ts (6)
18-37: LGTM - Cost calculation logic is sound.The function appropriately:
- Uses stored
costUSDwhen available and non-zero- Falls back to computing cost from tokens using
LiteLLMPricingFetcher- Handles errors gracefully with
Result.unwrapdefaulting to 0This aligns with the PR description where OpenCode stores
cost: 0and actual costs are computed from token counts.
45-62: LGTM - ISO week calculation follows standard algorithm.The ISO 8601 week date calculation is correctly implemented:
- Sets to nearest Thursday (as per ISO standard)
- Handles year boundaries appropriately
- Returns properly formatted week strings (YYYY-Www)
The comprehensive test coverage at lines 212-239 verifies correctness for various edge cases.
90-90: LGTM - Correct use of explicit resource management.The
usingkeyword is the correct choice here for TypeScript's explicit resource management (ES2024/TS5.2+). SinceLiteLLMPricingFetcherimplements theDisposableinterface, theusingdeclaration ensures automatic disposal at the end of the scope, properly managing the resource lifecycle.
92-148: LGTM - Data aggregation and totals calculation.The weekly aggregation correctly:
- Groups entries by ISO week using
groupBy- Accumulates all token types (input, output, cache read/write)
- Computes per-entry costs asynchronously
- Tracks unique models per week
- Calculates overall totals across all weeks
The logic is clear and handles all the required metrics.
162-205: LGTM - Table rendering with responsive layout.The table implementation correctly:
- Configures 8 columns for full mode, 5 for compact mode
- Formats numbers and currency appropriately
- Adds a separator before the totals row
- Highlights totals in yellow for visibility
- Shows a helpful compact mode notice when applicable
The responsive table pattern provides a good UX for different terminal widths.
209-240: LGTM - Comprehensive test coverage for ISO week calculation.The tests appropriately verify:
- Mid-year date calculation
- Year boundary handling (Dec 29, 2025 → week 01 of 2026)
- First week of year
- Last days of previous year belonging to week 1
These edge cases are critical for ISO 8601 week dates and the tests provide good coverage.
apps/opencode/README.md
Outdated
|
|
||
| **Supported models**: | ||
|
|
||
| - ✅ Claude models (opus-4-5, haiku-4-5, sonnet-4-5, etc.) - Accurate pricing from LiteLLM |
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.
Correct the model naming format.
The model names listed here don't follow the project's naming convention. According to the coding guidelines, Claude models should follow the pattern claude-{model-type}-{generation}-{date} (e.g., claude-sonnet-4-20250514).
🔎 Suggested fix
-- ✅ Claude models (opus-4-5, haiku-4-5, sonnet-4-5, etc.) - Accurate pricing from LiteLLM
+- ✅ Claude models (claude-opus-4, claude-haiku-4, claude-sonnet-4, etc.) - Accurate pricing from LiteLLM📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - ✅ Claude models (opus-4-5, haiku-4-5, sonnet-4-5, etc.) - Accurate pricing from LiteLLM | |
| - ✅ Claude models (claude-opus-4, claude-haiku-4, claude-sonnet-4, etc.) - Accurate pricing from LiteLLM |
apps/opencode/src/commands/weekly.ts
Outdated
| import type { LoadedUsageEntry } from '../data-loader'; | ||
| import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing'; | ||
| import { | ||
| addEmptySeparatorRow, | ||
| formatCurrency, | ||
| formatModelsDisplayMultiline, | ||
| formatNumber, | ||
| ResponsiveTable, | ||
| } from '@ccusage/terminal/table'; | ||
| import { Result } from '@praha/byethrow'; | ||
| import { groupBy } from 'es-toolkit'; | ||
| import { define } from 'gunshi'; | ||
| import pc from 'picocolors'; | ||
| import { loadOpenCodeMessages } from '../data-loader'; |
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.
Add .ts extensions to local file imports.
Local file imports are missing the required .ts extension. The coding guidelines explicitly require: "Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')".
🔎 Proposed fix
-import type { LoadedUsageEntry } from '../data-loader';
+import type { LoadedUsageEntry } from '../data-loader.ts';
import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing';
import {
addEmptySeparatorRow,
formatCurrency,
formatModelsDisplayMultiline,
formatNumber,
ResponsiveTable,
} from '@ccusage/terminal/table';
import { Result } from '@praha/byethrow';
import { groupBy } from 'es-toolkit';
import { define } from 'gunshi';
import pc from 'picocolors';
-import { loadOpenCodeMessages } from '../data-loader';
+import { loadOpenCodeMessages } from '../data-loader.ts';As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import type { LoadedUsageEntry } from '../data-loader'; | |
| import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing'; | |
| import { | |
| addEmptySeparatorRow, | |
| formatCurrency, | |
| formatModelsDisplayMultiline, | |
| formatNumber, | |
| ResponsiveTable, | |
| } from '@ccusage/terminal/table'; | |
| import { Result } from '@praha/byethrow'; | |
| import { groupBy } from 'es-toolkit'; | |
| import { define } from 'gunshi'; | |
| import pc from 'picocolors'; | |
| import { loadOpenCodeMessages } from '../data-loader'; | |
| import type { LoadedUsageEntry } from '../data-loader.ts'; | |
| import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing'; | |
| import { | |
| addEmptySeparatorRow, | |
| formatCurrency, | |
| formatModelsDisplayMultiline, | |
| formatNumber, | |
| ResponsiveTable, | |
| } from '@ccusage/terminal/table'; | |
| import { Result } from '@praha/byethrow'; | |
| import { groupBy } from 'es-toolkit'; | |
| import { define } from 'gunshi'; | |
| import pc from 'picocolors'; | |
| import { loadOpenCodeMessages } from '../data-loader.ts'; |
🤖 Prompt for AI Agents
In apps/opencode/src/commands/weekly.ts lines 1-14, local imports from
'../data-loader' are missing the required .ts extension; update all local file
imports (e.g., both "import type { LoadedUsageEntry } from '../data-loader';"
and "import { loadOpenCodeMessages } from '../data-loader';") to include the .ts
extension (../data-loader.ts) so they comply with the coding guideline requiring
.ts extensions for local imports.
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: 1
🧹 Nitpick comments (1)
apps/opencode/README.md (1)
2-2: Prefer relative paths for images in/docs/public/.Per the coding guidelines, images stored in
/docs/public/should use relative paths (e.g.,/logo.svg) rather than external CDN URLs. Lines 2, 111, and 117 reference images that appear to be in the repository's/docs/public/directory but use full CDN URLs instead.🔎 Proposed refactor to use relative paths
- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/logo.svg" alt="ccusage logo" width="256" height="256"> + <img src="/logo.svg" alt="ccusage logo" width="256" height="256">- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/ccusage_thumbnail.png" alt="ccusage: The Claude Code cost scorecard that went viral" width="600"> + <img src="/ccusage_thumbnail.png" alt="ccusage: The Claude Code cost scorecard that went viral" width="600">- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg"> + <img src="/sponsors.svg" alt="ryoppippi sponsors">Note: This refactor depends on the build process or hosting configuration serving these files correctly at the repository root's public directory. If the CDN URLs are intentional for distribution/longevity reasons, this can be deferred.
Also applies to: 111-111, 117-117
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/opencode/README.md(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Place screenshots immediately after the main heading (H1) in documentation pages for immediate visual context
Use relative image paths like/screenshot.pngfor images stored in/docs/public/in documentation
Always include descriptive alt text for images in documentation for accessibility
Files:
apps/opencode/README.md
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: For package directories in commits, use the package directory name as scope (e.g., `feat(terminal):`, `fix(ui):`)
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Treat Codex as a sibling to apps/ccusage; reuse shared packages, command names, and flag semantics; diverge only when Codex-specific data requires it and document inline
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Refer to individual CLAUDE.md files in apps/ccusage/CLAUDE.md, apps/mcp/CLAUDE.md, and docs/CLAUDE.md for package-specific guidance
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx,json} : Claude model naming convention: `claude-{model-type}-{generation}-{date}` (e.g., `claude-sonnet-4-20250514`, NOT `claude-4-sonnet-20250514`)
Applied to files:
apps/opencode/README.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : All test files must use current Claude 4 models (claude-sonnet-4-20250514, claude-opus-4-20250514), not outdated Claude 3 models
Applied to files:
apps/opencode/README.md
📚 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/opencode/README.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Model names in tests must exactly match LiteLLM's pricing database entries
Applied to files:
apps/opencode/README.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/calculate-cost.ts : Cost calculations require exact model name matches with LiteLLM's database
Applied to files:
apps/opencode/README.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Refer to individual CLAUDE.md files in apps/ccusage/CLAUDE.md, apps/mcp/CLAUDE.md, and docs/CLAUDE.md for package-specific guidance
Applied to files:
apps/opencode/README.md
📚 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: Treat Codex as a sibling to apps/ccusage; reuse shared packages, command names, and flag semantics; diverge only when Codex-specific data requires it and document inline
Applied to files:
apps/opencode/README.md
🪛 LanguageTool
apps/opencode/README.md
[grammar] ~71-~71: Ensure spelling is correct
Context: ...hare/opencode) - LOG_LEVEL` – control consola log verbosity (0 silent … 5 trace) ## ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.18.1)
apps/opencode/README.md
117-117: Images should have alternate text (alt text)
(MD045, no-alt-text)
|
|
||
| <p align="center"> | ||
| <a href="https://github.com/sponsors/ryoppippi"> | ||
| <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg"> |
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.
Add missing alt text to the sponsors image.
The image on Line 117 is missing the required alt attribute, which violates accessibility standards and the coding guidelines. All images must include descriptive alt text.
🔎 Proposed fix
<a href="https://github.com/sponsors/ryoppippi">
- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg" alt="ryoppippi sponsors">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg"> | |
| <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg" alt="ryoppippi sponsors"> |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
117-117: Images should have alternate text (alt text)
(MD045, no-alt-text)
🤖 Prompt for AI Agents
In apps/opencode/README.md around line 117, the <img> tag for the sponsors SVG
is missing an alt attribute; add a concise, descriptive alt attribute (for
example: alt="OpenCode sponsors logos" or alt="Sponsors") to the image tag so it
meets accessibility guidelines and project coding standards.
|
@AnishDe12020 nice! |
commit: |
done! |
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
♻️ Duplicate comments (2)
apps/opencode/src/commands/monthly.ts (1)
15-15: LGTM! TABLE_COLUMN_COUNT now correct.The TABLE_COLUMN_COUNT constant is now correctly set to 8, matching the 8-column table header defined on line 116. This resolves the issue flagged in previous reviews.
apps/opencode/src/commands/weekly.ts (1)
12-13: Add.tsextensions to local file imports.Local imports are missing the required
.tsextension as specified in the coding guidelines.🔎 Proposed fix
-import { calculateCostForEntry } from '../cost-utils'; -import { loadOpenCodeMessages } from '../data-loader'; +import { calculateCostForEntry } from '../cost-utils.ts'; +import { loadOpenCodeMessages } from '../data-loader.ts';As per coding guidelines.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/opencode/src/commands/daily.ts(1 hunks)apps/opencode/src/commands/monthly.ts(1 hunks)apps/opencode/src/commands/session.ts(1 hunks)apps/opencode/src/commands/weekly.ts(1 hunks)apps/opencode/src/cost-utils.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/opencode/src/commands/daily.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use ESLint for linting and formatting with tab indentation and double quotes
No console.log allowed except where explicitly disabled with eslint-disable; use logger.ts instead
Use file paths with Node.js path utilities for cross-platform compatibility
Use variables starting with lowercase (camelCase) for variable names
Can use UPPER_SNAKE_CASE for constants
Files:
apps/opencode/src/cost-utils.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/commands/weekly.tsapps/opencode/src/commands/session.ts
**/*.ts{,x}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript with strict mode and bundler module resolution
Files:
apps/opencode/src/cost-utils.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/commands/weekly.tsapps/opencode/src/commands/session.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
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 when checking Results
Keep traditional try-catch only for file I/O with complex error handling or legacy code that's hard to refactor
Always useResult.isFailure()andResult.isSuccess()type guards for better code clarity
Use uppercase (PascalCase) for type names
Only export constants, functions, and types that are actually used by other modules - internal constants used only within the same file should NOT be exported
In-source testing pattern: write tests directly in source files usingif (import.meta.vitest != null)blocks
CRITICAL: DO NOT useawait import()dynamic imports anywhere in the codebase - this causes tree-shaking issues
CRITICAL: Never use dynamic imports withawait import()in vitest test blocks - this is particularly problematic for test execution
Vitest globals (describe,it,expect) are enabled and available without imports since globals are configured
Create mock data usingfs-fixturewithcreateFixture()for Claude data directory simulation in tests
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/opencode/src/cost-utils.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/commands/weekly.tsapps/opencode/src/commands/session.ts
**/*.{ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Claude model naming convention:
claude-{model-type}-{generation}-{date}(e.g.,claude-sonnet-4-20250514, NOTclaude-4-sonnet-20250514)
Files:
apps/opencode/src/cost-utils.tsapps/opencode/src/commands/monthly.tsapps/opencode/src/commands/weekly.tsapps/opencode/src/commands/session.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Refer to individual CLAUDE.md files in apps/ccusage/CLAUDE.md, apps/mcp/CLAUDE.md, and docs/CLAUDE.md for package-specific guidance
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Treat Codex as a sibling to apps/ccusage; reuse shared packages, command names, and flag semantics; diverge only when Codex-specific data requires it and document inline
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/calculate-cost.ts : Cost calculations require exact model name matches with LiteLLM's database
Applied to files:
apps/opencode/src/cost-utils.tsapps/opencode/src/commands/monthly.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/opencode/src/cost-utils.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Model names in tests must exactly match LiteLLM's pricing database entries
Applied to files:
apps/opencode/src/cost-utils.tsapps/opencode/src/commands/weekly.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: Cost calculation per model/date: charge non-cached input, cached input (fallback to input rate if missing), and output using the specified per-million token rates
Applied to files:
apps/opencode/src/cost-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 : Do not use console.log; use the logger utilities from `src/logger.ts` instead
Applied to files:
apps/opencode/src/commands/monthly.tsapps/opencode/src/commands/weekly.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/opencode/src/commands/weekly.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Use `.ts` extensions for local file imports (e.g., `import { foo } from './utils.ts'`)
Applied to files:
apps/opencode/src/commands/weekly.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/**/*.ts : Use `.ts` extensions for local (relative) imports
Applied to files:
apps/opencode/src/commands/weekly.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : CRITICAL: DO NOT use `await import()` dynamic imports anywhere in the codebase - this causes tree-shaking issues
Applied to files:
apps/opencode/src/commands/weekly.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : CRITICAL: Never use dynamic imports with `await import()` in vitest test blocks - this is particularly problematic for test execution
Applied to files:
apps/opencode/src/commands/weekly.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 : NEVER use `await import()` dynamic imports anywhere (especially in tests)
Applied to files:
apps/opencode/src/commands/weekly.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Use uppercase (PascalCase) for type names
Applied to files:
apps/opencode/src/commands/weekly.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use file paths with Node.js path utilities for cross-platform compatibility
Applied to files:
apps/opencode/src/commands/weekly.ts
🧬 Code graph analysis (3)
apps/opencode/src/commands/monthly.ts (4)
apps/opencode/src/data-loader.ts (1)
loadOpenCodeMessages(254-311)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)apps/opencode/src/cost-utils.ts (1)
calculateCostForEntry(22-42)packages/terminal/src/table.ts (4)
ResponsiveTable(38-298)formatModelsDisplayMultiline(355-359)formatNumber(305-307)addEmptySeparatorRow(566-569)
apps/opencode/src/commands/weekly.ts (4)
apps/opencode/src/data-loader.ts (1)
loadOpenCodeMessages(254-311)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)apps/opencode/src/cost-utils.ts (1)
calculateCostForEntry(22-42)packages/terminal/src/table.ts (4)
ResponsiveTable(38-298)formatModelsDisplayMultiline(355-359)formatNumber(305-307)addEmptySeparatorRow(566-569)
apps/opencode/src/commands/session.ts (4)
apps/opencode/src/data-loader.ts (2)
loadOpenCodeMessages(254-311)loadOpenCodeSessions(213-248)packages/internal/src/pricing.ts (1)
LiteLLMPricingFetcher(89-355)apps/opencode/src/cost-utils.ts (1)
calculateCostForEntry(22-42)packages/terminal/src/table.ts (1)
ResponsiveTable(38-298)
🔇 Additional comments (5)
apps/opencode/src/cost-utils.ts (2)
22-42: LGTM! Cost calculation extraction resolves duplication.The extraction of
calculateCostForEntryto a shared module successfully addresses the code duplication issue flagged in previous reviews across daily.ts, monthly.ts, and session.ts. The implementation correctly prioritizes pre-calculated costs and falls back to token-based calculation using the pricing fetcher with model name resolution.
9-12: Ensure MODEL_ALIASES covers all custom model name variants used with LiteLLM.The alias for 'gemini-3-pro-high' → 'gemini-3-pro-preview' is correct, since LiteLLM's pricing database uses 'gemini-3-pro-preview' as the official model name and handles thinking modes via the reasoning_effort parameter. However, if OpenCode's data includes other custom-named variants (e.g., 'gemini-3-pro-low', 'gemini-3-flash-high'), add corresponding aliases before they're used in cost calculations.
apps/opencode/src/commands/session.ts (1)
15-15: LGTM! TABLE_COLUMN_COUNT correctly set.The TABLE_COLUMN_COUNT constant correctly matches the 8-column table header defined on line 131.
apps/opencode/src/commands/weekly.ts (2)
23-40: LGTM! ISO week calculation correctly implemented.The
getISOWeekfunction correctly implements the ISO 8601 week date algorithm using the "nearest Thursday" approach. The logic properly handles week boundaries, year transitions, and the ISO week definition (weeks start on Monday, first week contains January 4th).
187-218: LGTM! Comprehensive test coverage for ISO week edge cases.The test suite covers key scenarios: mid-year dates, year boundary transitions, first week identification, and edge cases where early January dates belong to week 1. This provides good confidence in the
getISOWeekimplementation.
| import { calculateCostForEntry } from '../cost-utils'; | ||
| import { loadOpenCodeMessages } from '../data-loader'; |
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.
Add .ts extensions to local file imports.
Local imports are missing the required .ts extension as specified in the coding guidelines.
🔎 Proposed fix
-import { calculateCostForEntry } from '../cost-utils';
-import { loadOpenCodeMessages } from '../data-loader';
+import { calculateCostForEntry } from '../cost-utils.ts';
+import { loadOpenCodeMessages } from '../data-loader.ts';As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { calculateCostForEntry } from '../cost-utils'; | |
| import { loadOpenCodeMessages } from '../data-loader'; | |
| import { calculateCostForEntry } from '../cost-utils.ts'; | |
| import { loadOpenCodeMessages } from '../data-loader.ts'; |
🤖 Prompt for AI Agents
In apps/opencode/src/commands/monthly.ts around lines 12 to 13, local imports
are missing the required .ts file extensions; update the import paths to include
the .ts extension for each local module (e.g., '../cost-utils.ts' and
'../data-loader.ts') so they conform to the coding guidelines and the project's
module resolution rules.
| import { calculateCostForEntry } from '../cost-utils'; | ||
| import { loadOpenCodeMessages, loadOpenCodeSessions } from '../data-loader'; |
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.
Add .ts extensions to local file imports.
Local imports are missing the required .ts extension as specified in the coding guidelines.
🔎 Proposed fix
-import { calculateCostForEntry } from '../cost-utils';
-import { loadOpenCodeMessages, loadOpenCodeSessions } from '../data-loader';
+import { calculateCostForEntry } from '../cost-utils.ts';
+import { loadOpenCodeMessages, loadOpenCodeSessions } from '../data-loader.ts';As per coding guidelines.
🤖 Prompt for AI Agents
In apps/opencode/src/commands/session.ts around lines 12 to 13, the two local
imports are missing the required .ts file extensions; update the import
specifiers to include the .ts extension (e.g. '../cost-utils.ts' and
'../data-loader.ts') so they comply with the project's coding guidelines and
resolve module resolution errors.
|
@AnishDe12020 i was back from my vacation. thanks |
|
It would be great to see this merged. @ryoppippi is there anything specific needed to move this along? |
|
i think it looks good. i'll merge it in a couple of days |
Summary
Adds a complete standalone CLI package
@ccusage/opencodefor analyzing OpenCode (Claude Code fork) usage logs, following the same architecture as the existingapps/codexpackage.Features
Four CLI Commands
Session Hierarchy Support
↳prefixCost Calculation
cost: 0in message filesPricingFetcherfrom@ccusage/internal/pricingData Sources
OpenCode Storage (default:
~/.local/share/opencode):storage/message/{sessionID}/msg_{messageID}.json(individual JSON files)storage/session/{projectHash}/{sessionID}.json(session metadata)OPENCODE_DATA_DIRenvironment variablePackage Structure
```
apps/opencode/
├── src/
│ ├── index.ts # CLI entry point using Gunshi framework
│ ├── data-loader.ts # Load OpenCode messages & session metadata
│ └── commands/
│ ├── daily.ts # Daily usage report
│ ├── weekly.ts # Weekly usage report
│ ├── monthly.ts # Monthly usage report
│ └── session.ts # Session report with hierarchy
├── package.json # @ccusage/opencode@0.1.0
├── README.md # Usage documentation
└── tsconfig.json
```
Testing
Commits
Usage Example
```bash
Install
pnpm add @ccusage/opencode
Run commands
npx opencode daily
npx opencode weekly
npx opencode monthly
npx opencode session
```
Related
This package complements the existing ecosystem:
@ccusage/ccusage- Analyze Claude Code usage@ccusage/codex- Analyze OpenAI Codex usage@ccusage/opencode- ✨ NEW: Analyze OpenCode usageSummary by CodeRabbit
New Features
Documentation
Tests & Tooling
Other
✏️ Tip: You can customize this high-level summary in your review settings.
closes #757