-
-
Notifications
You must be signed in to change notification settings - Fork 319
refactor: extract table utilities to dedicated _table.ts file #603
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
Conversation
- Create new src/_table.ts with ResponsiveTable class and formatting functions - Move table-related utilities from _utils.ts to _table.ts: - ResponsiveTable class with responsive design and compact mode support - formatNumber(), formatCurrency() for data formatting - formatModelsDisplay(), formatModelsDisplayMultiline() for model display - pushBreakdownRows() for model breakdown table rows - TableRow, TableOptions, TableCellAlign types - Update imports in all command files to use _table.ts instead of _utils.ts - Keep only non-table utilities in _utils.ts (unreachable, getFileModifiedTime) - Move all table-related tests to _table.ts with comprehensive coverage - Improve type safety with dedicated TableCellAlign type for cell alignment This refactoring improves code organization by separating table-related utilities from general utilities, following the single responsibility principle. All tests pass and functionality remains unchanged.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Caution Review failedThe pull request is closed. WalkthroughMoves terminal table and formatting utilities from Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Cmd as Command (daily/weekly/monthly/session/blocks)
participant Table as _table.ts (createUsageReportTable / ResponsiveTable)
participant Env as Terminal (COLUMNS / stdout.columns)
participant CLI as cli-table3
User->>Cmd: invoke command
Cmd->>Table: createUsageReportTable(config) / formatUsageDataRow(...)
Table->>Env: read terminal width
alt compact criteria met
Table-->>Cmd: isCompactMode() = true
Note right of Table #f0f4ff: map compact headers<br/>filter columns
else full layout
Table-->>Cmd: isCompactMode() = false
end
loop add rows
Cmd->>Table: push(row) / pushBreakdownRows(...)
end
Cmd->>Table: toString()
Table->>CLI: render with computed widths & alignments
Table-->>Cmd: formatted table string
Cmd-->>User: print table
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (5)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Summary of Changes
Hello @ryoppippi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly refactors the codebase by extracting all table-related utility functions and the ResponsiveTable class from the general-purpose src/_utils.ts file into a new, dedicated module: src/_table.ts. This change aims to improve code organization, strictly adhere to the Single Responsibility Principle, and enhance the maintainability of the project. The refactoring ensures that _utils.ts now contains only truly general utilities, while all table-specific logic resides in its own well-defined module. All consuming files have been updated to reflect these new import paths, and the change introduces no breaking changes to existing functionality.
Highlights
- New Dedicated Table Utilities File: A new file,
src/_table.ts, has been introduced to centralize all table-related logic and components, including theResponsiveTableclass and various formatting utilities. - Refactored Table-Related Utilities: The
ResponsiveTableclass, along with functions likeformatNumber,formatCurrency,formatModelsDisplay,formatModelsDisplayMultiline, andpushBreakdownRows, have been relocated fromsrc/_utils.tstosrc/_table.ts. - Streamlined General Utilities File:
src/_utils.tshas been streamlined to contain only general-purpose utilities such asunreachable()andgetFileModifiedTime(), enhancing its focus and clarity. - Updated Import Paths Across the Project: All files that previously imported table utilities from
src/_utils.ts, including several command files and_live-rendering.ts, have been updated to import from the newsrc/_table.ts. - Improved Type Safety: The introduction of a dedicated
TableCellAligntype in_table.tsimproves type checking and overall code robustness for table cell alignment.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
commit: |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ccusage-guide | 1e99896 | Commit Preview URL Branch Preview URL |
Aug 23 2025, 08:48 PM |
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.
Code Review
This pull request effectively refactors table-related utilities into a new _table.ts file, which greatly improves code organization and adheres to the single responsibility principle. The changes are well-executed across the codebase. My review includes a couple of suggestions for the new _table.ts file to improve maintainability and fix a minor issue in the tests.
| toString(): string { | ||
| // Check environment variable first, then process.stdout.columns, then default | ||
| const terminalWidth = Number.parseInt(process.env.COLUMNS ?? '', 10) || process.stdout.columns || 120; | ||
|
|
||
| // Determine if we should use compact mode | ||
| this.compactMode = this.forceCompact || (terminalWidth < this.compactThreshold && this.compactHead != null); | ||
|
|
||
| // Get current table configuration | ||
| const { head, colAligns } = this.getCurrentTableConfig(); | ||
| const compactIndices = this.getCompactIndices(); | ||
|
|
||
| // Calculate actual content widths first (excluding separator rows) | ||
| const dataRows = this.rows.filter(row => !this.isSeparatorRow(row)); | ||
|
|
||
| // Filter rows to compact mode if needed | ||
| const processedDataRows = this.compactMode | ||
| ? dataRows.map(row => this.filterRowToCompact(row, compactIndices)) | ||
| : dataRows; | ||
|
|
||
| const allRows = [head.map(String), ...processedDataRows.map(row => row.map((cell) => { | ||
| if (typeof cell === 'object' && cell != null && 'content' in cell) { | ||
| return String(cell.content); | ||
| } | ||
| return String(cell ?? ''); | ||
| }))]; | ||
|
|
||
| const contentWidths = head.map((_, colIndex) => { | ||
| const maxLength = Math.max( | ||
| ...allRows.map(row => stringWidth(String(row[colIndex] ?? ''))), | ||
| ); | ||
| return maxLength; | ||
| }); | ||
|
|
||
| // Calculate table overhead | ||
| const numColumns = head.length; | ||
| const tableOverhead = 3 * numColumns + 1; // borders and separators | ||
| const availableWidth = terminalWidth - tableOverhead; | ||
|
|
||
| // Always use content-based widths with generous padding for numeric columns | ||
| const columnWidths = contentWidths.map((width, index) => { | ||
| const align = colAligns[index]; | ||
| // For numeric columns, ensure generous width to prevent truncation | ||
| if (align === 'right') { | ||
| return Math.max(width + 3, 11); // At least 11 chars for numbers, +3 padding | ||
| } | ||
| else if (index === 1) { | ||
| // Models column - can be longer | ||
| return Math.max(width + 2, 15); | ||
| } | ||
| return Math.max(width + 2, 10); // Other columns | ||
| }); | ||
|
|
||
| // Check if this fits in the terminal | ||
| const totalRequiredWidth = columnWidths.reduce((sum, width) => sum + width, 0) + tableOverhead; | ||
|
|
||
| if (totalRequiredWidth > terminalWidth) { | ||
| // Apply responsive resizing and use compact date format if available | ||
| const scaleFactor = availableWidth / columnWidths.reduce((sum, width) => sum + width, 0); | ||
| const adjustedWidths = columnWidths.map((width, index) => { | ||
| const align = colAligns[index]; | ||
| let adjustedWidth = Math.floor(width * scaleFactor); | ||
|
|
||
| // Apply minimum widths based on column type | ||
| if (align === 'right') { | ||
| adjustedWidth = Math.max(adjustedWidth, 10); | ||
| } | ||
| else if (index === 0) { | ||
| adjustedWidth = Math.max(adjustedWidth, 10); | ||
| } | ||
| else if (index === 1) { | ||
| adjustedWidth = Math.max(adjustedWidth, 12); | ||
| } | ||
| else { | ||
| adjustedWidth = Math.max(adjustedWidth, 8); | ||
| } | ||
|
|
||
| return adjustedWidth; | ||
| }); | ||
|
|
||
| const table = new Table({ | ||
| head, | ||
| style: this.style, | ||
| colAligns, | ||
| colWidths: adjustedWidths, | ||
| wordWrap: true, | ||
| wrapOnWordBoundary: true, | ||
| }); | ||
|
|
||
| // Add rows with special handling for separators and date formatting | ||
| for (const row of this.rows) { | ||
| if (this.isSeparatorRow(row)) { | ||
| // Skip separator rows - cli-table3 will handle borders automatically | ||
| continue; | ||
| } | ||
| else { | ||
| // Use compact date format for first column if dateFormatter available | ||
| let processedRow = row.map((cell, index) => { | ||
| if (index === 0 && this.dateFormatter != null && typeof cell === 'string' && this.isDateString(cell)) { | ||
| return this.dateFormatter(cell); | ||
| } | ||
| return cell; | ||
| }); | ||
|
|
||
| // Filter to compact columns if in compact mode | ||
| if (this.compactMode) { | ||
| processedRow = this.filterRowToCompact(processedRow, compactIndices); | ||
| } | ||
|
|
||
| table.push(processedRow); | ||
| } | ||
| } | ||
|
|
||
| return table.toString(); | ||
| } | ||
| else { | ||
| // Use generous column widths with normal date format | ||
| const table = new Table({ | ||
| head, | ||
| style: this.style, | ||
| colAligns, | ||
| colWidths: columnWidths, | ||
| wordWrap: true, | ||
| wrapOnWordBoundary: true, | ||
| }); | ||
|
|
||
| // Add rows with special handling for separators | ||
| for (const row of this.rows) { | ||
| if (this.isSeparatorRow(row)) { | ||
| // Skip separator rows - cli-table3 will handle borders automatically | ||
| continue; | ||
| } | ||
| else { | ||
| // Filter to compact columns if in compact mode | ||
| const processedRow = this.compactMode | ||
| ? this.filterRowToCompact(row, compactIndices) | ||
| : row; | ||
| table.push(processedRow); | ||
| } | ||
| } | ||
|
|
||
| return table.toString(); | ||
| } | ||
| } |
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.
The toString() method has a large if/else block where the logic for processing and adding rows to the table is duplicated. The only significant difference is that the dateFormatter is applied when the table requires resizing (totalRequiredWidth > terminalWidth), but not when it fits.
This duplication can be removed by refactoring the row processing logic. You can determine the final column widths and whether to use the date formatter first, then have a single loop to process and add all rows. This would make the method shorter, easier to read, and less prone to bugs (like the potentially inconsistent date formatting).
Additionally, there are several magic numbers used for calculating column widths (e.g., 11, 15, 10, 3, 2). It would be great to extract these into named constants at the top of the method or class for better readability and maintainability.
Here's a rough idea of how the refactoring could look:
toString(): string {
// ... (existing setup code)
const totalRequiredWidth = columnWidths.reduce((sum, width) => sum + width, 0) + tableOverhead;
let finalColWidths = columnWidths;
const useCompactDate = totalRequiredWidth > terminalWidth;
if (useCompactDate) {
// Apply responsive resizing
const scaleFactor = availableWidth / columnWidths.reduce((sum, width) => sum + width, 0);
finalColWidths = columnWidths.map((width, index) => {
// ... (adjustedWidths logic)
return adjustedWidth;
});
}
const table = new Table({
head,
style: this.style,
colAligns,
colWidths: finalColWidths,
wordWrap: true,
wrapOnWordBoundary: true,
});
// Single loop for adding rows
for (const row of this.rows) {
if (this.isSeparatorRow(row)) {
continue;
}
let processedRow = row;
// Apply date formatter if needed
if (useCompactDate && this.dateFormatter) {
processedRow = processedRow.map((cell, index) => {
if (index === 0 && typeof cell === 'string' && this.isDateString(cell)) {
return this.dateFormatter(cell);
}
return cell;
});
}
// Filter to compact columns if in compact mode
if (this.compactMode) {
processedRow = this.filterRowToCompact(processedRow, compactIndices);
}
table.push(processedRow);
}
return table.toString();
}| it('rounds to two decimal places', () => { | ||
| expect(formatCurrency(10.999)).toBe('$11.00'); | ||
| expect(formatCurrency(10.994)).toBe('$10.99'); | ||
| expect(formatCurrency(10.995)).toBe('$10.99'); // JavaScript's toFixed uses banker's rounding |
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.
The assertion in this test case seems to be incorrect. (10.995).toFixed(2) evaluates to '11.00' in JavaScript, not '10.99'. The comment about banker's rounding is also a bit misleading, as toFixed()'s rounding behavior for values ending in .5 is not standardized across all JavaScript environments and often rounds away from zero.
Please update the expected value to match the actual behavior of toFixed().
| expect(formatCurrency(10.995)).toBe('$10.99'); // JavaScript's toFixed uses banker's rounding | |
| expect(formatCurrency(10.995)).toBe('$11.00'); // JavaScript's toFixed rounding can be inconsistent for .5 values. |
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
🧹 Nitpick comments (4)
src/commands/blocks.ts (1)
17-17: Use shared TableCellAlign type instead of inline union.For consistency with the new _table.ts API and stronger type reuse, import the exported TableCellAlign and use it for align arrays.
-import { formatCurrency, formatModelsDisplayMultiline, formatNumber, ResponsiveTable } from '../_table.ts'; +import { formatCurrency, formatModelsDisplayMultiline, formatNumber, ResponsiveTable, type TableCellAlign } from '../_table.ts'; @@ -const tableAligns: ('left' | 'right' | 'center')[] = ['left', 'left', 'left', 'right']; +const tableAligns: TableCellAlign[] = ['left', 'left', 'left', 'right'];Also applies to: 386-387
src/_table.ts (3)
102-111: Avoid direct console.warn; use repository logger for consistencyThe coding guideline prefers
loggerover console. Replace theconsole.warnwithlogger.warn. This also helps unify output levels with other commands.Apply this diff:
@@ - // Log warning for debugging configuration issues - console.warn(`Warning: Compact header "${compactHeader}" not found in table headers [${this.head.join(', ')}]. Using first column as fallback.`); + // Log warning for debugging configuration issues + logger.warn(`Warning: Compact header "${compactHeader}" not found in table headers [${this.head.join(', ')}]. Using first column as fallback.`); return 0; // fallback to first column if not foundAnd add the import at the top of the file:
@@ -import stringWidth from 'string-width'; +import stringWidth from 'string-width'; +import { logger } from './logger.ts';Note: this will require updating the related unit test that currently spies on
console.warn. See the test suggestion below.
321-343: Model formatter is narrowly scoped toclaude-<name>-<major>-<yyyymmdd>; consider a more tolerant pattern (optional)If you expect any hyphenated or non-numeric “generation” segments, the current regex might fail to shorten (it will fall back to the original). Not a blocker for sonnet-4/opus-4, but we can support a broader set without breaking current outputs.
For example:
-const match = modelName.match(/claude-(\w+)-(\d+)-\d+/); +const match = modelName.match(/^claude-([a-z]+)-(\d+)-\d+$/i);or keep current and add a comment that we only target Claude 4 naming.
408-815: Add a focused unit test for pushBreakdownRows alignment and widthWe currently test
ResponsiveTableand formatters thoroughly, but notpushBreakdownRows. A minimal stubbed table lets us assert cell placement precisely and avoids brittle string parsing.Append this test inside the existing
if (import.meta.vitest != null)block:@@ describe('formatModelsDisplayMultiline', () => { @@ }); + + describe('pushBreakdownRows', () => { + it('places model bullet under the Models column (second column) and aligns metrics', () => { + const rows: (string | number)[][] = []; + const stubTable = { push: (r: (string | number)[]) => { rows.push(r); } }; + pushBreakdownRows(stubTable, [{ + modelName: 'claude-sonnet-4-20250514', + inputTokens: 100, + outputTokens: 50, + cacheCreationTokens: 10, + cacheReadTokens: 5, + cost: 1.5, + }]); + expect(rows).toHaveLength(1); + const r = rows[0]!; + // Date/Month column should be empty + expect(r[0]).toBe(''); + // Models column should contain the bullet with the shortened model name + expect(String(r[1])).toContain('└─ sonnet-4'); + // Then 6 metric columns + expect(r.slice(2)).toHaveLength(6); + }); + });If you switch to
logger.warnper the previous comment, also update the existing warning test to spy onlogger.warninstead ofconsole.warn.I can push these test updates as a follow-up commit if you’d like.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
src/_live-rendering.ts(1 hunks)src/_table.ts(1 hunks)src/_utils.ts(0 hunks)src/commands/_session_id.ts(1 hunks)src/commands/blocks.ts(1 hunks)src/commands/daily.ts(1 hunks)src/commands/monthly.ts(1 hunks)src/commands/session.ts(1 hunks)src/commands/statusline.ts(1 hunks)src/commands/weekly.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/_utils.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use tab indentation and double quotes; format with ESLint
Do not use console.log; use logger.ts instead (only allow console.log when explicitly eslint-disabled)
Use .ts extensions for local file imports (e.g., import { foo } from "./utils.ts")
Prefer @praha/byethrow Result type over traditional try-catch; use Result.try(), Result.isFailure()/isSuccess() and early returns; reserve try-catch for complex file I/O or legacy code
For async operations, wrap with a function using Result.try() and call it
Variables use camelCase; Types use PascalCase; Constants may use UPPER_SNAKE_CASE
Only export constants/functions/types that are used by other modules; keep internal/private items non-exported
Always use Node.js path utilities for file paths for cross-platform compatibility
Use in-source testing blocks guarded by if (import.meta.vitest != null); do not create separate test files
Use Vitest globals (describe, it, expect) without imports inside test blocks
Only use dynamic imports (await import()) inside test blocks to avoid tree-shaking issues
Mock data in tests should use fs-fixture createFixture() for Claude data directory simulation
Tests must use current Claude 4 models (Sonnet and Opus) and cover both; do not use Claude 3 models
Model names in tests must exactly match LiteLLM pricing database entries
Files:
src/commands/session.tssrc/commands/monthly.tssrc/commands/_session_id.tssrc/commands/blocks.tssrc/_live-rendering.tssrc/commands/daily.tssrc/commands/weekly.tssrc/commands/statusline.tssrc/_table.ts
**/_*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Internal files should use an underscore prefix (e.g., _types.ts, _utils.ts, _consts.ts)
Files:
src/commands/_session_id.tssrc/_live-rendering.tssrc/_table.ts
🧬 Code graph analysis (1)
src/_table.ts (2)
src/_terminal-utils.ts (1)
width(155-157)src/_utils.ts (1)
ResponsiveTable(34-292)
🪛 ESLint
src/_table.ts
[error] 107-107: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 107-107: Unsafe member access .warn on an error typed value.
(ts/no-unsafe-member-access)
[error] 129-129: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 129-129: Unsafe argument of type error typed assigned to a parameter of type string.
(ts/no-unsafe-argument)
[error] 129-129: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 129-129: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 129-129: Unsafe member access .stdout on an error typed value.
(ts/no-unsafe-member-access)
[error] 155-155: Unsafe spread of an error array type.
(ts/no-unsafe-argument)
[error] 155-155: Unsafe return of a value of type error.
(ts/no-unsafe-return)
[error] 155-155: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 206-213: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 206-213: Unsafe construction of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 235-235: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 235-235: Unsafe member access .push on an error typed value.
(ts/no-unsafe-member-access)
[error] 239-239: Unsafe return of a value of type error.
(ts/no-unsafe-return)
[error] 239-239: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 239-239: Unsafe member access .toString on an error typed value.
(ts/no-unsafe-member-access)
[error] 243-250: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 243-250: Unsafe construction of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 263-263: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 263-263: Unsafe member access .push on an error typed value.
(ts/no-unsafe-member-access)
[error] 267-267: Unsafe return of a value of type error.
(ts/no-unsafe-return)
[error] 267-267: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 267-267: Unsafe member access .toString on an error typed value.
(ts/no-unsafe-member-access)
[error] 341-341: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 341-341: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 342-342: Unsafe return of a value of type error.
(ts/no-unsafe-return)
[error] 342-342: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 342-342: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 342-342: Unsafe member access .sort on an error typed value.
(ts/no-unsafe-member-access)
[error] 342-342: Unsafe member access .join on an error typed value.
(ts/no-unsafe-member-access)
[error] 353-353: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 353-353: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 354-354: Unsafe return of a value of type error.
(ts/no-unsafe-return)
[error] 354-354: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 354-354: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 354-354: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 354-354: Unsafe member access .sort on an error typed value.
(ts/no-unsafe-member-access)
[error] 354-354: Unsafe member access .map on an error typed value.
(ts/no-unsafe-member-access)
[error] 354-354: Unsafe member access .join on an error typed value.
(ts/no-unsafe-member-access)
[error] 391-391: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 391-391: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 391-391: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 392-392: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 392-392: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 392-392: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 393-393: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 393-393: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 393-393: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 394-394: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 394-394: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 394-394: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 395-395: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 395-395: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 395-395: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 396-396: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 396-396: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 396-396: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 419-419: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 419-419: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 420-420: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 428-428: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 428-428: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 439-439: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 439-439: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 440-440: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 448-448: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 448-448: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 458-458: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 458-458: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 459-459: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 467-467: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 467-467: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 482-482: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 482-482: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 483-483: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 497-497: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 497-497: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 510-510: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 510-510: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 511-511: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 525-525: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 525-525: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 538-538: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 538-538: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 539-539: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 550-550: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 550-550: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 561-561: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 561-561: Unsafe member access .warn on an error typed value.
(ts/no-unsafe-member-access)
[error] 563-563: Unsafe member access .warn on an error typed value.
(ts/no-unsafe-member-access)
[error] 566-566: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 566-566: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 567-567: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 583-583: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 583-583: Unsafe member access .warn on an error typed value.
(ts/no-unsafe-member-access)
[error] 584-584: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 584-584: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 595-595: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 595-595: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 596-596: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 607-607: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 607-607: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 632-632: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 632-632: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 633-633: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 645-645: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 645-645: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 656-656: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 656-656: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 657-657: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 670-670: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 670-670: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 681-681: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 681-681: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 682-682: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 682-682: Unsafe member access .stdout on an error typed value.
(ts/no-unsafe-member-access)
[error] 684-684: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 694-694: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 694-694: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 695-695: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 695-695: Unsafe member access .stdout on an error typed value.
(ts/no-unsafe-member-access)
[error] 706-706: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 706-706: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 707-707: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 707-707: Unsafe member access .stdout on an error typed value.
(ts/no-unsafe-member-access)
[error] 709-709: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 720-720: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 720-720: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 721-721: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 721-721: Unsafe member access .stdout on an error typed value.
(ts/no-unsafe-member-access)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Workers Builds: ccusage-guide
🔇 Additional comments (8)
src/commands/blocks.ts (1)
17-17: Import path migration to _table.ts looks correct.The new source aligns with the refactor objective and keeps the local .ts extension as per guidelines. No behavioral changes here.
src/_live-rendering.ts (1)
19-19: Import source updated to _table.ts — looks good.Matches the centralization of formatting utilities; no logic change in this file.
src/commands/_session_id.ts (1)
6-6: Import migration LGTM.Switching to ../_table.ts maintains the same API surface and conforms to local .ts import style.
src/commands/session.ts (1)
9-9: Good move to _table.ts; consistent with the refactor.All imported symbols exist in the new module per PR description; callers unchanged.
src/commands/weekly.ts (1)
9-9: Weekly command imports correctly point to _table.ts.Consistent with other commands; no behavior change expected.
src/commands/daily.ts (1)
10-10: Migration of table utilities verifiedRan the provided ripgrep command to search for any remaining imports of these symbols from
../_utils.tsand confirmed there are no matches. The import insrc/commands/daily.tsis correct and no further changes are needed.src/commands/monthly.ts (1)
8-8: Import migration to _table.ts is consistent with the refactorThe move is cohesive with the new separation of concerns; usages below read the same.
src/commands/statusline.ts (1)
16-19: formatCurrency import now from _table.ts; utils imports remain scoped appropriatelyThis change is minimal and keeps
getFileModifiedTime/unreachablein_utils.ts. No runtime impact expected in this module.
| export function pushBreakdownRows( | ||
| table: { push: (row: (string | number)[]) => void }, | ||
| breakdowns: Array<{ | ||
| modelName: string; | ||
| inputTokens: number; | ||
| outputTokens: number; | ||
| cacheCreationTokens: number; | ||
| cacheReadTokens: number; | ||
| cost: number; | ||
| }>, | ||
| extraColumns = 1, | ||
| trailingColumns = 0, | ||
| ): void { | ||
| for (const breakdown of breakdowns) { | ||
| const row: (string | number)[] = [` └─ ${formatModelName(breakdown.modelName)}`]; | ||
|
|
||
| // Add extra empty columns before data | ||
| for (let i = 0; i < extraColumns; i++) { | ||
| row.push(''); | ||
| } | ||
|
|
||
| // Add data columns with gray styling | ||
| const totalTokens = breakdown.inputTokens + breakdown.outputTokens | ||
| + breakdown.cacheCreationTokens + breakdown.cacheReadTokens; | ||
|
|
||
| row.push( | ||
| pc.gray(formatNumber(breakdown.inputTokens)), | ||
| pc.gray(formatNumber(breakdown.outputTokens)), | ||
| pc.gray(formatNumber(breakdown.cacheCreationTokens)), | ||
| pc.gray(formatNumber(breakdown.cacheReadTokens)), | ||
| pc.gray(formatNumber(totalTokens)), | ||
| pc.gray(formatCurrency(breakdown.cost)), | ||
| ); | ||
|
|
||
| // Add trailing empty columns | ||
| for (let i = 0; i < trailingColumns; i++) { | ||
| row.push(''); | ||
| } | ||
|
|
||
| table.push(row); | ||
| } | ||
| } |
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.
🛠️ Refactor suggestion
Model breakdown rows are misaligned (model name currently appears under the Date/first column)
pushBreakdownRows starts the row with the model bullet, then adds an empty column. In tables like Daily/Monthly where the first column is Date/Month and the second is Models, this places the model bullet in the Date column and leaves the Models column blank.
Fix by:
- Prepending an empty first cell for the Date/Month column.
- Placing the model bullet in the Models column.
- Making
extraColumnsrepresent additional spacer columns after the Models column and before metrics (default 0).
Apply this diff:
@@
-export function pushBreakdownRows(
- table: { push: (row: (string | number)[]) => void },
- breakdowns: Array<{
+export function pushBreakdownRows(
+ table: { push: (row: (string | number)[]) => void },
+ breakdowns: Array<{
modelName: string;
inputTokens: number;
outputTokens: number;
cacheCreationTokens: number;
cacheReadTokens: number;
cost: number;
- }>,
- extraColumns = 1,
- trailingColumns = 0,
+ }>,
+ extraColumns = 0,
+ trailingColumns = 0,
): void {
for (const breakdown of breakdowns) {
- const row: (string | number)[] = [` └─ ${formatModelName(breakdown.modelName)}`];
-
- // Add extra empty columns before data
- for (let i = 0; i < extraColumns; i++) {
- row.push('');
- }
+ // Lead with an empty Date/Month column, then put the model bullet in the Models column
+ const row: (string | number)[] = [
+ '',
+ ` └─ ${formatModelName(breakdown.modelName)}`,
+ ];
+
+ // Optional spacer columns after Models before metrics (if a layout needs them)
+ for (let i = 0; i < extraColumns; i++) {
+ row.push('');
+ }
@@
row.push(
pc.gray(formatNumber(breakdown.inputTokens)),
pc.gray(formatNumber(breakdown.outputTokens)),
pc.gray(formatNumber(breakdown.cacheCreationTokens)),
pc.gray(formatNumber(breakdown.cacheReadTokens)),
pc.gray(formatNumber(totalTokens)),
pc.gray(formatCurrency(breakdown.cost)),
);
@@
table.push(row);
}
}Additionally, please update the JSDoc above to clarify the new default and semantics of extraColumns (spacer columns after the Models column, before metrics). I can add a focused test for this (see next comment).
📝 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.
| export function pushBreakdownRows( | |
| table: { push: (row: (string | number)[]) => void }, | |
| breakdowns: Array<{ | |
| modelName: string; | |
| inputTokens: number; | |
| outputTokens: number; | |
| cacheCreationTokens: number; | |
| cacheReadTokens: number; | |
| cost: number; | |
| }>, | |
| extraColumns = 1, | |
| trailingColumns = 0, | |
| ): void { | |
| for (const breakdown of breakdowns) { | |
| const row: (string | number)[] = [` └─ ${formatModelName(breakdown.modelName)}`]; | |
| // Add extra empty columns before data | |
| for (let i = 0; i < extraColumns; i++) { | |
| row.push(''); | |
| } | |
| // Add data columns with gray styling | |
| const totalTokens = breakdown.inputTokens + breakdown.outputTokens | |
| + breakdown.cacheCreationTokens + breakdown.cacheReadTokens; | |
| row.push( | |
| pc.gray(formatNumber(breakdown.inputTokens)), | |
| pc.gray(formatNumber(breakdown.outputTokens)), | |
| pc.gray(formatNumber(breakdown.cacheCreationTokens)), | |
| pc.gray(formatNumber(breakdown.cacheReadTokens)), | |
| pc.gray(formatNumber(totalTokens)), | |
| pc.gray(formatCurrency(breakdown.cost)), | |
| ); | |
| // Add trailing empty columns | |
| for (let i = 0; i < trailingColumns; i++) { | |
| row.push(''); | |
| } | |
| table.push(row); | |
| } | |
| } | |
| export function pushBreakdownRows( | |
| table: { push: (row: (string | number)[]) => void }, | |
| breakdowns: Array<{ | |
| modelName: string; | |
| inputTokens: number; | |
| outputTokens: number; | |
| cacheCreationTokens: number; | |
| cacheReadTokens: number; | |
| cost: number; | |
| }>, | |
| extraColumns = 0, | |
| trailingColumns = 0, | |
| ): void { | |
| for (const breakdown of breakdowns) { | |
| // Lead with an empty Date/Month column, then put the model bullet in the Models column | |
| const row: (string | number)[] = [ | |
| "", | |
| ` └─ ${formatModelName(breakdown.modelName)}`, | |
| ]; | |
| // Optional spacer columns after Models before metrics (if a layout needs them) | |
| for (let i = 0; i < extraColumns; i++) { | |
| row.push(""); | |
| } | |
| // Add data columns with gray styling | |
| const totalTokens = | |
| breakdown.inputTokens + | |
| breakdown.outputTokens + | |
| breakdown.cacheCreationTokens + | |
| breakdown.cacheReadTokens; | |
| row.push( | |
| pc.gray(formatNumber(breakdown.inputTokens)), | |
| pc.gray(formatNumber(breakdown.outputTokens)), | |
| pc.gray(formatNumber(breakdown.cacheCreationTokens)), | |
| pc.gray(formatNumber(breakdown.cacheReadTokens)), | |
| pc.gray(formatNumber(totalTokens)), | |
| pc.gray(formatCurrency(breakdown.cost)), | |
| ); | |
| // Add trailing empty columns | |
| for (let i = 0; i < trailingColumns; i++) { | |
| row.push(""); | |
| } | |
| table.push(row); | |
| } | |
| } |
🧰 Tools
🪛 ESLint
[error] 391-391: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 391-391: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 391-391: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 392-392: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 392-392: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 392-392: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 393-393: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 393-393: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 393-393: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 394-394: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 394-394: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 394-394: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 395-395: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 395-395: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 395-395: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
[error] 396-396: Unsafe argument of type error typed assigned to a parameter of type string | number.
(ts/no-unsafe-argument)
[error] 396-396: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 396-396: Unsafe member access .gray on an error typed value.
(ts/no-unsafe-member-access)
🤖 Prompt for AI Agents
In src/_table.ts around lines 365 to 406, the model bullet is currently placed
in the first column (Date/Month) because the row begins with the model cell then
adds extraColumns; change the row layout so the first cell is an empty spacer
(for Date/Month), the second cell contains the model bullet, and extraColumns
now represent additional spacer columns inserted after the Models column and
before the metrics (default 0). Update the function to start each row with an
empty string, then push the model cell (` └─ ${formatModelName(...)}`), then
push extraColumns empty strings, then the metric columns as before, and finally
trailingColumns empties; also update the JSDoc above the function to state that
extraColumns are spacer columns after the Models column (default 0) and adjust
the description accordingly.
|
|
||
| } from '../_session-blocks.ts'; | ||
| import { sharedCommandConfig } from '../_shared-args.ts'; | ||
| import { formatCurrency, formatModelsDisplayMultiline, formatNumber, ResponsiveTable } from '../_table.ts'; |
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.
💡 Verification agent
🧩 Analysis chain
Quick repo-wide verification for the import move.
Run this script to confirm no callers still import table utilities from _utils.ts and that all local imports include the .ts extension.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Ensure no migrated symbols are still imported from _utils.ts"
rg -nP --type=ts '(?m)^import\s+{[^}]*\b(formatNumber|formatCurrency|formatModelsDisplayMultiline|formatModelsDisplay|ResponsiveTable|pushBreakdownRows)\b[^}]*}\s+from\s+[\'"][./..]+/_utils\.ts[\'"]' || echo "OK: none found"
echo -e "\n2) List any remaining imports from _utils.ts (should be only allowed survivors like getFileModifiedTime/unreachable)"
rg -nP --type=ts '(?m)^import\s+.*from\s+[\'"][./..]+/_utils\.ts[\'"]' || echo "OK: no imports, or only expected survivors"
echo -e "\n3) Verify _table.ts exports expected surface"
rg -nP -n 'export\s+(?:function|const|class|type)\s+(formatNumber|formatCurrency|formatModelsDisplayMultiline|formatModelsDisplay|ResponsiveTable|pushBreakdownRows|TableCellAlign)\b' src/_table.ts || echo "WARNING: Could not verify exports in src/_table.ts"
echo -e "\n4) Ensure local imports consistently use .ts extensions"
rg -nP --type=ts '(?m)^import\s+.*from\s+[\'"](./|\.\./)[^\'"]+(?<!\.ts)[\'"]' || echo "OK: all local imports include .ts"Length of output: 266
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "1) Checking for migrated symbols still imported from _utils.ts"
rg -n --type ts -P "import\s+.*\b(formatNumber|formatCurrency|formatModelsDisplayMultiline|formatModelsDisplay|ResponsiveTable|pushBreakdownRows)\b.*from\s+['\"][.][./]+_utils\.ts['\"]" || echo "OK: none found"
echo
echo "2) Listing any remaining imports from _utils.ts"
rg -n --type ts -P "import\s+.*from\s+['\"][.][./]+_utils\.ts['\"]" || echo "OK: no imports or only expected survivors"
echo
echo "3) Verifying exports in src/_table.ts"
rg -n --type ts -P -n "export\s+(function|const|class|type)\s+(formatNumber|formatCurrency|formatModelsDisplayMultiline|formatModelsDisplay|ResponsiveTable|pushBreakdownRows|TableCellAlign)\b" src/_table.ts || echo "WARNING: Could not verify exports in src/_table.ts"
echo
echo "4) Ensuring local imports consistently use .ts extension"
rg -n --type ts -P "import\s+.*from\s+['\"][.][^'\"]+['\"]" | grep -nEv "\.ts['\"]$" && echo "ERROR: some local imports missing .ts extension" || echo "OK: all local imports include .ts extension"Length of output: 13622
Local imports missing .ts extension
The verification script ran cleanly for migrated symbols (none still import from _utils.ts) and confirmed the expected survivors in src/data-loader.ts:62 and src/commands/statusline.ts:18. It also verified that src/_table.ts exports the full set of moved utilities.
However, several local imports lack the required .ts extension. Please update all import statements below to include the .ts suffix and then re-run the verification script:
-
scripts/
scripts/generate-json-schema.ts: lines 16, 18, 20
-
project root
src/index.ts: line 14
-
src/debug.ts: lines 15–18
-
src/mcp.ts: lines 11, 21
-
src/_shared-args.ts: lines 2–3
-
src/_live-monitor.ts: lines 11–12, 16, 26
-
src/pricing-fetcher.ts: lines 11–16
-
src/_live-rendering.ts: lines 9–11, 17–19
-
src/commands/session.ts: lines 6–9, 50–54
-
src/commands/weekly.ts: lines 5–9, 59
-
src/commands/monthly.ts: lines 5–9, 66
-
src/commands/statusline.ts: lines 12–16, 18
-
src/commands/daily.ts: lines 5–10, 85
-
src/commands/_blocks.live.ts: lines 9, 13, 24–25
-
src/commands/_session_id.ts: lines 1–7, 112–114
-
src/commands/blocks.ts: lines 1–7, 17
-
src/commands/index.ts: lines 3–9
-
src/data-loader.ts: lines 62–64
-
src/_session-blocks.ts: lines 2–3
After adding the .ts extension to each of these import paths, re-run the script to confirm an “OK: all local imports include .ts extension” result.
🤖 Prompt for AI Agents
In src/commands/blocks.ts (lines 1–7 and 17) some local import paths are missing
the required .ts extension; update each relative/local import to explicitly
include the .ts suffix (e.g., change "../foo" to "../foo.ts"), save, and re-run
the verification script to confirm the "OK: all local imports include .ts
extension" result.
- Add shared table factory function createUsageReportTable() with consistent headers and styling - Add formatUsageDataRow() for standardized data row formatting with token calculations - Add formatTotalsRow() with yellow highlighting and optional Last Activity column support - Add addEmptySeparatorRow() utility for consistent visual separation - Define UsageReportConfig and UsageData interfaces for better type safety - Refactor daily.ts, monthly.ts, weekly.ts, and session.ts to use shared table functions - Support Last Activity column for session reports via includeLastActivity config option - Reduce code duplication by ~50-70 lines per command file - Maintain all existing functionality and table formatting consistency - Fix ESLint strict boolean expression warnings with nullish coalescing This consolidation improves maintainability by centralizing table creation logic while preserving responsive design and compact mode functionality across all commands.
Summary
This PR refactors table-related functionality to improve code organization and reduce duplication across the codebase.
Changes Made
1. Code Organization
src/_utils.tsto newsrc/_table.tsfile_table.tsmodule2. Type Safety Improvements
TableCellAligntype for better type safety (replaces generic string literals)UsageReportConfigandUsageDatainterfaces for consistent table configuration3. Code Consolidation
createUsageReportTable()with consistent headers and stylingformatUsageDataRow()for standardized data row formatting with token calculationsformatTotalsRow()with yellow highlighting and optional Last Activity column supportaddEmptySeparatorRow()utility for consistent visual separationdaily.ts,monthly.ts,weekly.ts, andsession.tsto use shared functionsincludeLastActivityconfig option4. Code Reduction
5. Quality Improvements
Files Changed
src/_table.ts(new) - Centralized table utilities with shared functionssrc/_utils.ts- Removed table code, kept utility functionssrc/commands/daily.ts- Refactored to use shared table functionssrc/commands/monthly.ts- Refactored to use shared table functionssrc/commands/weekly.ts- Refactored to use shared table functionssrc/commands/session.ts- Refactored to use shared table functionssrc/commands/blocks.ts- Updated importssrc/commands/statusline.ts- Updated importssrc/commands/_session_id.ts- Updated importssrc/_live-rendering.ts- Updated importsBenefits
All existing functionality is preserved with no breaking changes to the user interface.
Summary by CodeRabbit
New Features
Refactor