feat(codex): Added last subcommand: npx @ccusage/codex@latest last --day <n>#912
feat(codex): Added last subcommand: npx @ccusage/codex@latest last --day <n>#912Cheese-Yu wants to merge 4 commits intoryoppippi:mainfrom
Conversation
📝 WalkthroughWalkthroughThis PR introduces a new Changes
Sequence DiagramsequenceDiagram
participant CLI as CLI Parser
participant Validator as Arg Validator
participant DateUtil as Date Utils
participant Session as Session Loader
participant Pricer as Pricing Source
participant Report as Report Builder
participant Output as Output Formatter
CLI->>Validator: Validate --day argument
Validator-->>CLI: Valid day count or exit
CLI->>DateUtil: getLastNDaysRange(dayCount, timezone)
DateUtil-->>CLI: {since, until} date range
CLI->>Session: Load token events from session dirs
Session-->>CLI: Events or warnings for missing dirs
alt Events exist
CLI->>Pricer: Create CodexPricingSource
CLI->>Report: Build daily report within date bounds
Report-->>CLI: Rows with per-day metrics
CLI->>Output: Format as JSON or terminal table
Output-->>CLI: Render result
else No events
CLI->>Output: Emit empty JSON or message
Output-->>CLI: Output
end
CLI->>Pricer: Dispose via Symbol.dispose
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/codex/src/date-utils.ts (1)
115-136: UTC calendar math is the right approach here, but the exported helper still needs an input guard.
getLastNDaysRange(0)currently returns an inverted window (sinceafteruntil). The CLI validates--day, but this helper is exported, so it should reject non-positive values itself instead of relying only on the command layer.Proposed fix
export function getLastNDaysRange( dayCount: number, timezone?: string, ): { since: string; until: string } { + if (!Number.isSafeInteger(dayCount) || dayCount <= 0) { + throw new Error('dayCount must be a positive integer.'); + } + const todayDateKey = toDateKey(new Date().toISOString(), timezone); const until = shiftDateKey(todayDateKey, -1); const since = shiftDateKey(until, -(dayCount - 1));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/codex/src/date-utils.ts` around lines 115 - 136, getLastNDaysRange lacks validation for non-positive dayCount, allowing getLastNDaysRange(0) to return an inverted range; add an input guard at the start of getLastNDaysRange to reject values < 1 (e.g., throw a RangeError with a clear message), so callers cannot request zero or negative days. Keep the rest of the logic (using toDateKey and shiftDateKey) intact and only validate the dayCount parameter before computing todayDateKey, until, and since.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/codex/src/commands/last.ts`:
- Around line 58-65: The args object currently spreads sharedArgs (from
_shared-args.ts) which exposes --since/--until even though last.ts always uses
getLastNDaysRange(); remove the range flags by not spreading sharedArgs or by
creating an args object that only includes the needed flags (e.g., copy
sharedArgs fields you need except since/until) and keep the day field as
defined; update the args declaration in last.ts (the args variable in this file)
so --since/--until are no longer present in the exported CLI metadata.
- Around line 67-73: The code currently sets jsonOutput (logger.level = 0)
before validating the --day value, which hides parseDayCount() errors; reorder
and/or validate so parseDayCount(ctx.values.day) runs and any parsing errors are
handled before setting logger.level to 0 (or only set logger.level = 0 after
parseDayCount succeeds); specifically update the logic around jsonOutput,
logger.level and parseDayCount in the command handler so parseDayCount is called
first (or its error is surfaced) and only then enable silent JSON mode.
---
Nitpick comments:
In `@apps/codex/src/date-utils.ts`:
- Around line 115-136: getLastNDaysRange lacks validation for non-positive
dayCount, allowing getLastNDaysRange(0) to return an inverted range; add an
input guard at the start of getLastNDaysRange to reject values < 1 (e.g., throw
a RangeError with a clear message), so callers cannot request zero or negative
days. Keep the rest of the logic (using toDateKey and shiftDateKey) intact and
only validate the dayCount parameter before computing todayDateKey, until, and
since.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4e77ea5a-c50e-4255-abdd-48e92338ec1d
📒 Files selected for processing (8)
apps/codex/README.mdapps/codex/src/commands/last.tsapps/codex/src/date-utils.tsapps/codex/src/run.tsdocs/.vitepress/config.tsdocs/guide/codex/daily.mddocs/guide/codex/index.mddocs/guide/codex/last-n-days.md
| args: { | ||
| ...sharedArgs, | ||
| day: { | ||
| type: 'string', | ||
| description: 'Number of days to include (must be a positive integer)', | ||
| required: true, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Drop --since/--until from this subcommand's args.
...sharedArgs also exposes the range filters from apps/codex/src/_shared-args.ts, but this command always overwrites them with getLastNDaysRange(). That makes the help text advertise flags that do nothing.
Proposed fix
export const lastCommand = define({
name: 'last',
description: 'Show Codex token usage for the last N days (excluding today)',
args: {
- ...sharedArgs,
+ json: sharedArgs.json,
+ timezone: sharedArgs.timezone,
+ locale: sharedArgs.locale,
+ offline: sharedArgs.offline,
+ compact: sharedArgs.compact,
+ color: sharedArgs.color,
+ noColor: sharedArgs.noColor,
day: {
type: 'string',
description: 'Number of days to include (must be a positive integer)',
required: true,
},
},📝 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.
| args: { | |
| ...sharedArgs, | |
| day: { | |
| type: 'string', | |
| description: 'Number of days to include (must be a positive integer)', | |
| required: true, | |
| }, | |
| }, | |
| args: { | |
| json: sharedArgs.json, | |
| timezone: sharedArgs.timezone, | |
| locale: sharedArgs.locale, | |
| offline: sharedArgs.offline, | |
| compact: sharedArgs.compact, | |
| color: sharedArgs.color, | |
| noColor: sharedArgs.noColor, | |
| day: { | |
| type: 'string', | |
| description: 'Number of days to include (must be a positive integer)', | |
| required: true, | |
| }, | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/codex/src/commands/last.ts` around lines 58 - 65, The args object
currently spreads sharedArgs (from _shared-args.ts) which exposes
--since/--until even though last.ts always uses getLastNDaysRange(); remove the
range flags by not spreading sharedArgs or by creating an args object that only
includes the needed flags (e.g., copy sharedArgs fields you need except
since/until) and keep the day field as defined; update the args declaration in
last.ts (the args variable in this file) so --since/--until are no longer
present in the exported CLI metadata.
| const jsonOutput = Boolean(ctx.values.json); | ||
| if (jsonOutput) { | ||
| logger.level = 0; | ||
| } | ||
|
|
||
| const dayCount = parseDayCount(ctx.values.day); | ||
|
|
There was a problem hiding this comment.
Validate --day before enabling silent JSON mode.
logger.level = 0 is documented as silent, so last --json --day abc will exit with code 1 but no diagnostic from parseDayCount().
Proposed fix
async run(ctx) {
- const jsonOutput = Boolean(ctx.values.json);
- if (jsonOutput) {
- logger.level = 0;
- }
-
const dayCount = parseDayCount(ctx.values.day);
+ const jsonOutput = Boolean(ctx.values.json);
+ if (jsonOutput) {
+ logger.level = 0;
+ }
const range = getLastNDaysRange(dayCount, ctx.values.timezone);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/codex/src/commands/last.ts` around lines 67 - 73, The code currently
sets jsonOutput (logger.level = 0) before validating the --day value, which
hides parseDayCount() errors; reorder and/or validate so
parseDayCount(ctx.values.day) runs and any parsing errors are handled before
setting logger.level to 0 (or only set logger.level = 0 after parseDayCount
succeeds); specifically update the logic around jsonOutput, logger.level and
parseDayCount in the command handler so parseDayCount is called first (or its
error is surfaced) and only then enable silent JSON mode.
Motivation
Description
lastsubcommand implemented inapps/codex/src/commands/last.tsthat validates--day, computes the date range, builds the daily report, and emits table or JSON output.getLastNDaysRangeand helpershiftDateKeyinapps/codex/src/date-utils.tsto compute the closedsince/untilrange (until = yesterday, since =n-1days before yesterday).apps/codex/src/run.tsand add user-facing documentation and navigation changes includingdocs/guide/codex/last-n-days.md, updates toREADME.md, and sidebar changes indocs/.vitepress/config.tsand related guide pages.getLastNDaysRangeto ensure the date math excludes today and yields the expected range.Files Changed
Example Usage
Testing
Summary by CodeRabbit
New Features
last --day <n>.--jsonflag.Documentation