Skip to content

Conversation

@timrogers
Copy link
Contributor

@timrogers timrogers commented Jun 14, 2025

Summary

  • Remove -p short option: Removed the `-p` short option since Claude data directory can now be configured via `CLAUDE_CONFIG_DIR` environment variable
  • Remove --path option entirely: Completely removed the `--path` CLI option to simplify the interface and encourage environment variable usage
  • Update all commands: Modified daily, monthly, session, and mcp commands to use `getDefaultClaudePath()` directly
  • Update documentation: Updated README to remove all references to `--path` option and clarify environment variable usage

Changes Made

CLI Interface Changes

  • Removed `path` argument from `shared-args.internal.ts`
  • Updated all command files to import and use `getDefaultClaudePath()` directly
  • No longer accepts `--path` or `-p` options - users must use `CLAUDE_CONFIG_DIR` environment variable

Documentation Updates

  • Removed `--path` option from CLI options documentation in README
  • Updated usage examples to remove `--path` references
  • Clarified that only `CLAUDE_CONFIG_DIR` environment variable is supported for custom paths

Test Coverage

  • All existing tests continue to pass
  • No test changes needed as the functionality remains the same, just configured differently

Breaking Change Notice

⚠️ This is a breaking change: The `--path` and `-p` options are no longer available. Users who were using these options should set the `CLAUDE_CONFIG_DIR` environment variable instead:

```bash

Before (no longer works):

ccusage daily --path /custom/path

After:

export CLAUDE_CONFIG_DIR="/custom/path"
ccusage daily
```

Test Plan

  • All existing tests pass
  • Linting passes
  • CLI works with default path (~/.claude)
  • CLI works with CLAUDE_CONFIG_DIR environment variable
  • All commands (daily, monthly, session, mcp) use environment variable correctly
  • Documentation updated to reflect changes

Summary by CodeRabbit

  • New Features
    • The tool now supports specifying the Claude data directory via the CLAUDE_CONFIG_DIR environment variable.
  • Bug Fixes
    • Improved validation to ensure the specified data directory exists and is valid.
  • Documentation
    • Updated instructions to reflect the use of the CLAUDE_CONFIG_DIR environment variable and removed references to the deprecated --path option.
  • Chores
    • Added a new dependency to support directory validation.
  • Tests
    • Added comprehensive tests for environment variable handling and directory validation.

@coderabbitai
Copy link

coderabbitai bot commented Jun 14, 2025

Warning

Rate limit exceeded

@ryoppippi has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 40 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 764ed25 and 84441d6.

📒 Files selected for processing (2)
  • .github/workflows/ci.yaml (1 hunks)
  • src/data-loader.test.ts (2 hunks)

Walkthrough

The changes introduce support for the CLAUDE_CONFIG_DIR environment variable as the preferred way to specify the Claude configuration directory. The code now checks this variable and validates the directory before use, falling back to a default path if unset. Documentation, command implementations, and tests were updated accordingly, and the --path command-line option was removed.

Changes

File(s) Change Summary
README.md Updated to replace --path option with CLAUDE_CONFIG_DIR environment variable usage and examples.
src/data-loader.ts Modified getDefaultClaudePath to use and validate CLAUDE_CONFIG_DIR env var or fallback default.
src/data-loader.test.ts Added comprehensive tests for getDefaultClaudePath covering env var usage, trimming, and errors.
package.json Added path-type dependency for directory existence checks.
src/commands/daily.ts Changed to use getDefaultClaudePath() instead of user-provided path for loading daily usage data.
src/commands/monthly.ts Changed to use getDefaultClaudePath() instead of user-provided path for loading monthly usage data.
src/commands/session.ts Changed to use getDefaultClaudePath() instead of user-provided path for loading session data.
src/commands/mcp.ts Removed path argument; uses getDefaultClaudePath() for MCP server path instead of user input.
src/shared-args.internal.ts Removed path argument from shared CLI arguments, eliminating user path input option.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant DataLoader

    User->>CLI: Run ccusage (without --path)
    CLI->>DataLoader: getDefaultClaudePath()
    DataLoader->>process.env: Check CLAUDE_CONFIG_DIR
    alt CLAUDE_CONFIG_DIR is set and non-empty
        DataLoader-->>CLI: Return trimmed CLAUDE_CONFIG_DIR path if valid directory
    else
        DataLoader-->>CLI: Return default ~/.claude path if valid directory
    end
    CLI-->>User: Use resolved config directory for operations
Loading

Poem

A rabbit hopped through code and lore,
To find config paths, and then some more.
"CLAUDE_CONFIG_DIR," it now will seek,
If not, the old path it will keep.
With docs and tests, the path is clear—
Hooray for tidy config here!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 @timrogers, 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 enhances the flexibility of specifying the Claude data directory by adding support for the CLAUDE_CONFIG_DIR environment variable. This provides an alternative to the existing --path command-line option and addresses issue #66. The implementation ensures that the command-line option takes precedence over the environment variable.

Highlights

  • Environment Variable Support: Introduced support for the CLAUDE_CONFIG_DIR environment variable to specify the custom path to the Claude data directory.
  • Configuration Precedence: Implemented logic to prioritize the existing --path command-line option over the new CLAUDE_CONFIG_DIR environment variable, ensuring backward compatibility.
  • Documentation Updates: Updated the README.md to include examples of using the CLAUDE_CONFIG_DIR environment variable and clarify the precedence rules between the environment variable and the --path option.
  • Unit Tests: Added unit tests for the getDefaultClaudePath function to verify its behavior when the CLAUDE_CONFIG_DIR environment variable is set, not set, or set to an empty/whitespace value.
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 is currently in preview and 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 to provide feedback.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configureGemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 successfully implements the feature to allow specifying the Claude config directory using the $CLAUDE_CONFIG_DIR environment variable, addressing issue #66. The changes are well-contained within the data-loader.ts file and include comprehensive unit tests in data-loader.test.ts to cover the new logic, including edge cases like empty or whitespace environment variables. The README.md has been updated clearly to document this new functionality, explaining its usage and precedence relative to the --path command-line option. The implementation is clean and follows good practices for handling environment variables and testing. Overall, this is a solid and well-executed feature addition.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/data-loader.ts (1)

16-19: Consider normalising or resolving the user-supplied path

getDefaultClaudePath returns the raw value of CLAUDE_CONFIG_DIR.
If the variable is set to a relative path ("./.claude"), contains ~, or mixes slashes, callers will receive it unchanged, which can later surface as subtle “file not found” errors when code that expects an absolute, normalised path tries to join/resolve again.

A minimal improvement is to run the env value through path.resolve (or at least path.normalize) before returning:

-	return (envPath != null && envPath.trim() !== '') ? envPath : path.join(homedir(), '.claude');
+	if (envPath != null && envPath.trim() !== '') {
+		return path.resolve(envPath.trim());
+	}
+	return path.join(homedir(), '.claude');

Keeps backward compatibility while guarding against surprising relative inputs.

🧰 Tools
🪛 ESLint

[error] 17-17: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 17-17: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 18-18: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 18-18: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 18-18: Unsafe member access .trim on an error typed value.

(ts/no-unsafe-member-access)


[error] 18-18: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 18-18: Unsafe member access .join on an error typed value.

(ts/no-unsafe-member-access)


[error] 18-18: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)

src/data-loader.test.ts (1)

33-93: Prefer beforeEach/afterEach to manage process.env and avoid delete

All three tests temporarily mutate process.env.CLAUDE_CONFIG_DIR and then use delete to restore state. Besides the Biome warning about the delete operator, repeating the try/finally pattern clutters the spec.

You can DRY this up and avoid delete by snapshotting all env vars once per test:

-describe('getDefaultClaudePath', () => {
-	test('returns CLAUDE_CONFIG_DIR when environment variable is set', () => {
-		const originalEnv = process.env.CLAUDE_CONFIG_DIR;
-		...
-	});
-   ...
-});
+describe('getDefaultClaudePath', () => {
+	let originalEnv: NodeJS.ProcessEnv;
+
+	beforeEach(() => {
+		originalEnv = { ...process.env };
+	});
+
+	afterEach(() => {
+		process.env = originalEnv; // single assignment, no delete needed
+	});
+
+	test('returns CLAUDE_CONFIG_DIR when environment variable is set', () => {
+		process.env.CLAUDE_CONFIG_DIR = '/custom/claude/path';
+		expect(getDefaultClaudePath()).toBe('/custom/claude/path');
+	});
+
+	test('returns default path when CLAUDE_CONFIG_DIR is not set', () => {
+		delete process.env.CLAUDE_CONFIG_DIR;
+		const result = getDefaultClaudePath();
+		expect(result).toContain('.claude');
+		expect(result).not.toBe('.claude');
+	});
+
+	test('returns default path when CLAUDE_CONFIG_DIR is empty or whitespace', () => {
+		process.env.CLAUDE_CONFIG_DIR = '   ';
+		expect(getDefaultClaudePath()).toContain('.claude');
+	});
+});

Cleaner, avoids repeating restoration code, and removes the performance-related lint warning.

🧰 Tools
🪛 Biome (1.9.4)

[error] 48-48: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 57-57: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 90-90: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🪛 ESLint

[error] 33-33: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 34-34: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 35-35: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 35-35: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 39-39: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 40-40: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 40-40: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 40-40: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 45-45: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 45-45: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 48-48: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 53-53: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 54-54: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 54-54: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 57-57: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 59-59: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 59-59: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 59-59: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 60-60: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 60-60: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 60-60: Unsafe member access .not on an error typed value.

(ts/no-unsafe-member-access)


[error] 65-65: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 65-65: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 70-70: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 71-71: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 71-71: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 75-75: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 77-77: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 77-77: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 77-77: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 80-80: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 82-82: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 82-82: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 82-82: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 87-87: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 87-87: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 90-90: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)

README.md (1)

247-260: Minor wording tweak for clarity

The paragraph is missing the definite article “the” before “environment variable”.

-When both the environment variable and `--path` option are provided, the `--path` option takes precedence, ensuring backward compatibility.
+When both the environment variable and the `--path` option are provided, the `--path` option takes precedence, ensuring backward compatibility.

Pure documentation nit; feel free to ignore if house style differs.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~260-~260: You might be missing the article “the” here.
Context: ... When both the environment variable and --path option are provided, the `--path...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5efa904 and 03c81b9.

📒 Files selected for processing (3)
  • README.md (5 hunks)
  • src/data-loader.test.ts (2 hunks)
  • src/data-loader.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/data-loader.test.ts (1)
src/data-loader.ts (1)
  • getDefaultClaudePath (16-19)
🪛 ESLint
src/data-loader.ts

[error] 17-17: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 17-17: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 18-18: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 18-18: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 18-18: Unsafe member access .trim on an error typed value.

(ts/no-unsafe-member-access)


[error] 18-18: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 18-18: Unsafe member access .join on an error typed value.

(ts/no-unsafe-member-access)


[error] 18-18: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)

src/data-loader.test.ts

[error] 33-33: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 34-34: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 35-35: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 35-35: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 39-39: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 40-40: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 40-40: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 40-40: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 45-45: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 45-45: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 48-48: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 53-53: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 54-54: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 54-54: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 57-57: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 59-59: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 59-59: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 59-59: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 60-60: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 60-60: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 60-60: Unsafe member access .not on an error typed value.

(ts/no-unsafe-member-access)


[error] 65-65: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 65-65: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 70-70: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 71-71: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 71-71: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 75-75: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 77-77: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 77-77: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 77-77: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 80-80: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 82-82: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 82-82: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 82-82: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 87-87: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 87-87: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 90-90: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)

🪛 LanguageTool
README.md

[uncategorized] ~260-~260: You might be missing the article “the” here.
Context: ... When both the environment variable and --path option are provided, the `--path...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🪛 Biome (1.9.4)
src/data-loader.test.ts

[error] 48-48: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 57-57: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 90-90: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

@ryoppippi
Copy link
Owner

Thank you!
I request two things

  • it is ok to remove -p option!
  • please use Conventional Commits for your commit request.
  • Please use feat!: to remove -p option because we use changelogithub to manage release note

README.md Outdated
- `-s, --since <date>`: Filter from date (YYYYMMDD format)
- `-u, --until <date>`: Filter until date (YYYYMMDD format)
- `-p, --path <path>`: Custom path to Claude data directory (default: `~/.claude`)
- `-p, --path <path>`: Custom path to Claude data directory (default: `$CLAUDE_CONFIG_DIR` or `~/.claude`)
Copy link
Owner

Choose a reason for hiding this comment

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

you can remove -p option

const testPath = '/custom/claude/path';

try {
process.env.CLAUDE_CONFIG_DIR = testPath;
Copy link
Owner

Choose a reason for hiding this comment

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

because bun test is not isolated each other you need to mock process.env in each test case

Copy link
Owner

Choose a reason for hiding this comment

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

Also if you can mock claude project path using fs-fixture that'd be brilliant

Comment on lines 17 to 18
const envPath = process.env.CLAUDE_CONFIG_DIR;
return (envPath != null && envPath.trim() !== '') ? envPath : path.join(homedir(), '.claude');
Copy link
Owner

@ryoppippi ryoppippi Jun 14, 2025

Choose a reason for hiding this comment

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

Suggested change
const envPath = process.env.CLAUDE_CONFIG_DIR;
return (envPath != null && envPath.trim() !== '') ? envPath : path.join(homedir(), '.claude');
const defaultClaudeCodePath = path.join(homedir(), '.claude');
const envClaudeCodePath = process.env.CLAUDE_CONFIG_DIR?.trim();
if(existsSync(envClaudeCodePath)){
return envClaudeCodePath
}
return defaultClaudeCodePath;

I think this is more readable! (you need to import fs.existsSync tho), and format it.
or, you may use isDirectorySync from https://github.com/sindresorhus/path-type to check if the path is directory (I prefer this approach in this case over fs.existsSync)

@ryoppippi
Copy link
Owner

@timrogers if you don't have time i'll do that

@timrogers
Copy link
Contributor Author

@ryoppippi Go ahead! Feel free to take over as I won't have time for a few days.

@ryoppippi ryoppippi requested a review from Copilot June 15, 2025 13:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces support for specifying the Claude configuration directory via the CLAUDE_CONFIG_DIR environment variable, updating both the implementation and documentation, and adding tests to validate the behavior.

  • Introduces a new import and logic in getDefaultClaudePath() in src/data-loader.ts
  • Adds comprehensive tests in src/data-loader.test.ts to verify behavior with and without the environment variable
  • Updates README.md to include instructions for using the CLAUDE_CONFIG_DIR environment variable

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/data-loader.ts Implements environment variable check for CLAUDE_CONFIG_DIR in getDefaultClaudePath()
src/data-loader.test.ts Adds tests covering various cases of the CLAUDE_CONFIG_DIR environment variable
README.md Updates documentation to explain the use and precedence of the CLAUDE_CONFIG_DIR setting

@ryoppippi ryoppippi changed the title feat: allow specifying Claude config directory with $CLAUDE_CONFIG_DIR feat!: specify claude dir with CLAUDE_CONFIG_DIR and remove -p option Jun 16, 2025
@socket-security
Copy link

socket-security bot commented Jun 16, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpath-type@​6.0.01001008577100

View full report

Copy link

@coderabbitai coderabbitai bot left a 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 (3)
src/data-loader.ts (3)

5-5: process import is redundant in ESM Node – consider removing

process is exposed as a global in Node. Importing it adds no functional value and slightly increases cognitive load. Unless you have ESLint/tsconfig rules that forbid usage of globals, dropping the import is the leaner choice.

-import process from 'node:process';

8-8: New dependency path-type – double-check footprint & licence

path-type is lightweight but still adds a transitive dependency tree (≈2 KB JS + type-fest). Verify:

  1. It is already used elsewhere; otherwise importing only fs.statSync would suffice.
  2. Its licence is compatible with your project’s licence.

If the single sync call is the only usage, a small helper is arguably cheaper than an extra package.


17-18: Constant name mismatch with comment

Constant is DEFAULT_CLAUDE_CODE_PATH but comment refers to “path for Claude data directory”.
DEFAULT_CLAUDE_CONFIG_DIR would mirror the env-var name and reduce mental mapping.

Nit-level, but improves consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03c81b9 and 4a9715a.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • package.json (1 hunks)
  • src/data-loader.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • package.json
🧰 Additional context used
🪛 ESLint
src/data-loader.ts

[error] 17-17: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 17-17: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 17-17: Unsafe member access .join on an error typed value.

(ts/no-unsafe-member-access)


[error] 17-17: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 24-24: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 24-24: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 24-24: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 25-25: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 25-25: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 26-26: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 28-28: Unsafe return of a value of type error.

(ts/no-unsafe-return)

Refactor and expand tests for getDefaultClaudePath to cover additional
edge cases, including handling of whitespace, non-existent directories,
and invalid paths. Use beforeEach and afterEach hooks for environment
cleanup and restoration. This ensures more robust and reliable behavior
The -p short option is no longer needed since the path can now be configured
via the CLAUDE_CONFIG_DIR environment variable. Users can still use --path
for the full option name when needed.

This simplifies the CLI interface by reducing the number of short options
and encouraging use of the environment variable for persistent configuration.
Update the README documentation to reflect the removal of the -p short
option. The --path long option remains available for users who need
to override the default Claude data directory location.
@ryoppippi ryoppippi changed the title feat!: specify claude dir with CLAUDE_CONFIG_DIR and remove -p option feat!: specify claude dir with CLAUDE_CONFIG_DIR and remove -p/--path option Jun 16, 2025
…able

Remove the --path CLI option completely since the Claude data directory
can now be configured via the CLAUDE_CONFIG_DIR environment variable.

Changes:
- Remove path argument from shared-args.internal.ts
- Update all commands (daily, monthly, session, mcp) to use getDefaultClaudePath()
- Remove unused import from shared-args.internal.ts
- Fix import order in mcp.ts command

This simplifies the CLI interface and encourages users to set the
CLAUDE_CONFIG_DIR environment variable for persistent configuration
instead of passing the path on every command invocation.
The --path option has been completely removed from the CLI. Users should
now use the CLAUDE_CONFIG_DIR environment variable to configure custom
Claude data directory locations.
Update all example commands to remove --path option usage. The tool now
exclusively uses the CLAUDE_CONFIG_DIR environment variable for custom
data directory configuration.

Updated sections:
- Daily report examples
- Session report examples
- Environment variable documentation
Remove import of getDefaultClaudePath since the path option has been
removed entirely from the shared arguments.
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jun 16, 2025

Open in StackBlitz

npm i https://pkg.pr.new/ryoppippi/ccusage@70

commit: 84441d6

Update test to properly handle both local and CI environments:
- In local dev: ~/.claude might exist, test passes
- In CI: ~/.claude does not exist, test expects error

This makes the test work correctly in both environments while
maintaining proper validation behavior.
Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (3)
src/commands/daily.ts (1)

28-28: Surface a friendlier error when the path is invalid

getDefaultClaudePath() throws synchronously.
If the directory is missing, the whole command exits with an uncaught exception and an unformatted stack-trace. Consider wrapping the call in a try { } catch (err) block and logging a concise message (e.g. “Claude data directory not found – set CLAUDE_CONFIG_DIR or create ~/.claude”) before exiting with a non-zero code.

src/data-loader.test.ts (2)

39-44: Prefer assigning undefined over delete when clearing env vars in tests

Biome warns that delete on object properties has a perf hit; here the cost is negligible, but swapping to an undefined assignment silences the linter and keeps intent clear.

-		delete process.env.CLAUDE_CONFIG_DIR;
+		process.env.CLAUDE_CONFIG_DIR = undefined as unknown as string;

(Apply similarly in afterEach. No functional change, just lint-clean.)

Also applies to: 44-52


36-52: originalEnv capture can be stale if tests mutate it

originalEnv is captured once before all tests, so if the suite itself runs after a previous suite that already mutated process.env.CLAUDE_CONFIG_DIR, we propagate that value. Capture inside beforeEach to guarantee per-test isolation:

-	const originalEnv = process.env.CLAUDE_CONFIG_DIR;
-
-beforeEach(() => {
-	// Clean up env var before each test
-	delete process.env.CLAUDE_CONFIG_DIR;
+	let originalEnv: string | undefined;
+
+	beforeEach(() => {
+		originalEnv = process.env.CLAUDE_CONFIG_DIR;
+		process.env.CLAUDE_CONFIG_DIR = undefined as unknown as string;
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a9715a and 764ed25.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • README.md (4 hunks)
  • package.json (1 hunks)
  • src/commands/daily.ts (3 hunks)
  • src/commands/mcp.ts (2 hunks)
  • src/commands/monthly.ts (3 hunks)
  • src/commands/session.ts (3 hunks)
  • src/data-loader.test.ts (2 hunks)
  • src/data-loader.ts (1 hunks)
  • src/shared-args.internal.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • src/shared-args.internal.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • README.md
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/commands/monthly.ts (2)
src/data-loader.ts (1)
  • getDefaultClaudePath (23-33)
src/debug.ts (1)
  • detectMismatches (53-182)
src/commands/session.ts (2)
src/data-loader.ts (1)
  • getDefaultClaudePath (23-33)
src/debug.ts (1)
  • detectMismatches (53-182)
src/data-loader.test.ts (1)
src/data-loader.ts (1)
  • getDefaultClaudePath (23-33)
🪛 ESLint
src/commands/mcp.ts

[error] 26-26: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 26-26: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 29-29: Unsafe member access .level on an error typed value.

(ts/no-unsafe-member-access)

src/data-loader.test.ts

[error] 36-36: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 37-37: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 37-37: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 39-39: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 41-41: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 44-44: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 47-47: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 47-47: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 50-50: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 54-54: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 55-57: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 55-55: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 58-58: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 58-58: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 58-58: Unsafe member access .path on an error typed value.

(ts/no-unsafe-member-access)


[error] 60-60: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 60-60: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 60-60: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 60-60: Unsafe member access .path on an error typed value.

(ts/no-unsafe-member-access)


[error] 63-63: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 65-65: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 69-69: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 69-69: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 69-69: Unsafe member access .toMatch on an error typed value.

(ts/no-unsafe-member-access)


[error] 70-70: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 70-70: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 70-70: Unsafe member access .toContain on an error typed value.

(ts/no-unsafe-member-access)


[error] 70-70: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 73-73: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 74-76: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 74-74: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 78-78: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 78-78: Unsafe member access .path on an error typed value.

(ts/no-unsafe-member-access)


[error] 80-80: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 80-80: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 80-80: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 80-80: Unsafe member access .path on an error typed value.

(ts/no-unsafe-member-access)


[error] 83-83: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 84-84: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 84-84: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 85-85: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 85-85: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 85-85: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 85-85: Unsafe member access .path on an error typed value.

(ts/no-unsafe-member-access)


[error] 87-87: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 87-87: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 87-87: Unsafe member access .toThrow on an error typed value.

(ts/no-unsafe-member-access)


[error] 90-90: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 91-91: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 93-93: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 93-93: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 93-93: Unsafe member access .toThrow on an error typed value.

(ts/no-unsafe-member-access)


[error] 96-96: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 98-98: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 100-100: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 100-100: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 100-100: Unsafe member access .toThrow on an error typed value.

(ts/no-unsafe-member-access)

src/data-loader.ts

[error] 17-17: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 17-17: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 17-17: Unsafe member access .join on an error typed value.

(ts/no-unsafe-member-access)


[error] 17-17: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 24-24: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 24-24: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 24-24: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 25-25: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 25-25: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 32-32: Unsafe return of a value of type error.

(ts/no-unsafe-return)

🪛 Biome (1.9.4)
src/data-loader.test.ts

[error] 41-41: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 50-50: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 65-65: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🪛 GitHub Check: ci
src/data-loader.ts

[failure] 26-26: error: Claude data directory does not exist: /home/runner/.claude.
Please set CLAUDE_CONFIG_DIR to a valid path, or ensure /home/runner/.claude exists.
at getDefaultClaudePath (/home/runner/work/ccusage/ccusage/src/data-loader.ts:26:9)
at (/home/runner/work/ccusage/ccusage/src/data-loader.test.ts:68:22)
at (/home/runner/work/ccusage/ccusage/src/data-loader.test.ts:63:65)

Comment on lines 25 to 31
const dailyData = await loadDailyUsageData({
since: ctx.values.since,
until: ctx.values.until,
claudePath: ctx.values.path,
claudePath: getDefaultClaudePath(),
mode: ctx.values.mode,
order: ctx.values.order,
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Cache the resolved Claude path once instead of invoking getDefaultClaudePath() twice

getDefaultClaudePath() performs I/O + validation on every call.
Resolve it once, reuse it for both loadDailyUsageData and detectMismatches, and avoid the (minor) overhead while guaranteeing both calls operate on the identical directory.

-		const dailyData = await loadDailyUsageData({
+		const claudePath = getDefaultClaudePath();
+
+		const dailyData = await loadDailyUsageData({
 			since: ctx.values.since,
 			until: ctx.values.until,
-			claudePath: getDefaultClaudePath(),
+			claudePath,
 			mode: ctx.values.mode,
 			order: ctx.values.order,
 		});
 …
-			const mismatchStats = await detectMismatches(getDefaultClaudePath());
+			const mismatchStats = await detectMismatches(claudePath);
 			printMismatchReport(mismatchStats, ctx.values.debugSamples);

Also applies to: 46-49

🧰 Tools
🪛 ESLint

[error] 26-26: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 26-26: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 27-27: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 27-27: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 29-29: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 30-30: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/commands/daily.ts around lines 25 to 31 and 46 to 49, the function
getDefaultClaudePath() is called twice, causing redundant I/O and validation.
Fix this by calling getDefaultClaudePath() once, storing its result in a
variable, and then passing that variable to both loadDailyUsageData and
detectMismatches to ensure consistent usage and reduce overhead.

Comment on lines 25 to 31
const monthlyData = await loadMonthlyUsageData({
since: ctx.values.since,
until: ctx.values.until,
claudePath: ctx.values.path,
claudePath: getDefaultClaudePath(),
mode: ctx.values.mode,
order: ctx.values.order,
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reuse a single claudePath variable

Same duplication pattern as in daily – resolve once, pass to both loadMonthlyUsageData and detectMismatches.

-		const monthlyData = await loadMonthlyUsageData({
+		const claudePath = getDefaultClaudePath();
+		const monthlyData = await loadMonthlyUsageData({
 			since: ctx.values.since,
 			until: ctx.values.until,
-			claudePath: getDefaultClaudePath(),
+			claudePath,
 			mode: ctx.values.mode,
 			order: ctx.values.order,
 		});
 …
-			const mismatchStats = await detectMismatches(getDefaultClaudePath());
+			const mismatchStats = await detectMismatches(claudePath);

Also applies to: 57-60

🧰 Tools
🪛 ESLint

[error] 26-26: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 26-26: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 27-27: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 27-27: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 29-29: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 30-30: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/commands/monthly.ts around lines 25 to 31 and also lines 57 to 60, the
code calls getDefaultClaudePath() multiple times causing duplication. Fix this
by declaring a single variable claudePath before these calls, assign it the
result of getDefaultClaudePath(), and then pass this variable to both
loadMonthlyUsageData and detectMismatches functions to reuse the value.

Comment on lines 25 to 31
const sessionData = await loadSessionData({
since: ctx.values.since,
until: ctx.values.until,
claudePath: ctx.values.path,
claudePath: getDefaultClaudePath(),
mode: ctx.values.mode,
order: ctx.values.order,
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Deduplicate getDefaultClaudePath() invocations

Mirror the optimisation applied to the other commands.

-		const sessionData = await loadSessionData({
+		const claudePath = getDefaultClaudePath();
+		const sessionData = await loadSessionData({
 			since: ctx.values.since,
 			until: ctx.values.until,
-			claudePath: getDefaultClaudePath(),
+			claudePath,
 			mode: ctx.values.mode,
 			order: ctx.values.order,
 		});
 …
-			const mismatchStats = await detectMismatches(getDefaultClaudePath());
+			const mismatchStats = await detectMismatches(claudePath);

Also applies to: 46-49

🧰 Tools
🪛 ESLint

[error] 26-26: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 26-26: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 27-27: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 27-27: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 29-29: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 30-30: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/commands/session.ts around lines 25 to 31 and also lines 46 to 49, the
function getDefaultClaudePath() is called multiple times unnecessarily. To
optimize, call getDefaultClaudePath() once, store its result in a variable, and
reuse that variable in the loadSessionData calls instead of invoking the
function repeatedly.

Comment on lines +26 to 34
const { type, mode, port } = ctx.values;
// disable info logging
if (type === 'stdio') {
logger.level = 0;
}

const server = createMcpServer({
claudePath: path,
claudePath: getDefaultClaudePath(),
mode,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inline duplication & potential exception propagation

  1. Resolve the path once and reuse.
  2. As with the other commands, wrap the resolution in try/catch to avoid raw stack traces on invalid paths.
-		const { type, mode, port } = ctx.values;
+		const { type, mode, port } = ctx.values;
+
+		const claudePath = getDefaultClaudePath();-		const server = createMcpServer({
-			claudePath: getDefaultClaudePath(),
+		const server = createMcpServer({
+			claudePath,
 			mode,
 		});
📝 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.

Suggested change
const { type, mode, port } = ctx.values;
// disable info logging
if (type === 'stdio') {
logger.level = 0;
}
const server = createMcpServer({
claudePath: path,
claudePath: getDefaultClaudePath(),
mode,
const { type, mode, port } = ctx.values;
const claudePath = getDefaultClaudePath();
// disable info logging
if (type === 'stdio') {
logger.level = 0;
}
const server = createMcpServer({
claudePath,
mode,
});
🧰 Tools
🪛 ESLint

[error] 26-26: Unsafe assignment of an any value.

(ts/no-unsafe-assignment)


[error] 26-26: Unsafe member access .values on an any value.

(ts/no-unsafe-member-access)


[error] 29-29: Unsafe member access .level on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/commands/mcp.ts around lines 26 to 34, the code calls
getDefaultClaudePath() directly inside createMcpServer, causing duplicate path
resolution and risking unhandled exceptions. Fix this by resolving the Claude
path once before the server creation, wrapping the resolution in a try/catch
block to handle invalid paths gracefully and prevent raw stack traces, then pass
the resolved path variable to createMcpServer.

Comment on lines +24 to +31
const envClaudeCodePath = process.env.CLAUDE_CONFIG_DIR?.trim() ?? DEFAULT_CLAUDE_CODE_PATH;
if (!isDirectorySync(envClaudeCodePath)) {
throw new Error(
` Claude data directory does not exist: ${envClaudeCodePath}.
Please set CLAUDE_CONFIG_DIR to a valid path, or ensure ${DEFAULT_CLAUDE_CODE_PATH} exists.
`.trim(),
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against empty/undefined CLAUDE_CONFIG_DIR and avoid hard-fail on missing default dir

isDirectorySync(envClaudeCodePath) is invoked even when
a) CLAUDE_CONFIG_DIR is an empty string after .trim() or
b) the env var is unset (falls back to DEFAULT_CLAUDE_CODE_PATH, which may not exist in CI / first-run environments).

This currently crashes the whole process (see CI failure).
A safer approach:

  1. Validate the env var only if it is present and non-empty.
  2. Re-use previous behaviour for the default path – just return it without enforcing existence (let callers decide).
 export function getDefaultClaudePath(): string {
-	const envClaudeCodePath = process.env.CLAUDE_CONFIG_DIR?.trim() ?? DEFAULT_CLAUDE_CODE_PATH;
-	if (!isDirectorySync(envClaudeCodePath)) {
-		throw new Error(
-			` Claude data directory does not exist: ${envClaudeCodePath}. 
-Please set CLAUDE_CONFIG_DIR to a valid path, or ensure ${DEFAULT_CLAUDE_CODE_PATH} exists.
-			`.trim(),
-		);
-	}
-	return envClaudeCodePath;
+	const raw = process.env.CLAUDE_CONFIG_DIR;
+	const envPath = raw?.trim();
+
+	// If the user explicitly provided a path, validate it
+	if (envPath && envPath.length > 0) {
+		if (!isDirectorySync(envPath)) {
+			throw new Error(`CLAUDE_CONFIG_DIR points to a non-existent directory: ${envPath}`);
+		}
+		return envPath;
+	}
+
+	// Fallback to default path (preserves previous non-blocking behaviour)
+	return DEFAULT_CLAUDE_CODE_PATH;
 }

This prevents the empty-string crash, keeps the strict check for explicit custom paths, and lets CI/tests run without needing to create ~/.claude.

🧰 Tools
🪛 ESLint

[error] 24-24: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 24-24: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 24-24: Unsafe member access .env on an error typed value.

(ts/no-unsafe-member-access)


[error] 25-25: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 25-25: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)

🪛 GitHub Check: ci

[failure] 26-26: error: Claude data directory does not exist: /home/runner/.claude.
Please set CLAUDE_CONFIG_DIR to a valid path, or ensure /home/runner/.claude exists.
at getDefaultClaudePath (/home/runner/work/ccusage/ccusage/src/data-loader.ts:26:9)
at (/home/runner/work/ccusage/ccusage/src/data-loader.test.ts:68:22)
at (/home/runner/work/ccusage/ccusage/src/data-loader.test.ts:63:65)

🤖 Prompt for AI Agents
In src/data-loader.ts around lines 24 to 31, modify the code to only check if
the directory exists when the CLAUDE_CONFIG_DIR environment variable is set and
non-empty after trimming. If CLAUDE_CONFIG_DIR is empty or undefined, do not
perform the directory existence check and simply return the default path without
throwing an error. This avoids crashing the process on missing default
directories while maintaining strict validation for explicitly set custom paths.

- Add step to create default Claude directory in CI environment
- Remove try-catch block from test, making it clean and simple
- Test now works consistently in both local and CI environments
@ryoppippi ryoppippi merged commit 79b9043 into ryoppippi:main Jun 16, 2025
7 checks passed
ryoppippi added a commit that referenced this pull request Jun 16, 2025
…be0f-269b8b06a70d

feat!: specify claude dir with `CLAUDE_CONFIG_DIR` and remove `-p`/`--path` option
@ryoppippi
Copy link
Owner

fixes: #66

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants