Skip to content

fix(config): don't require .claude directory for non-Claude agents#1017

Merged
ryoppippi merged 2 commits into
mainfrom
claude/fix-issue-1014-xwtDE
May 17, 2026
Merged

fix(config): don't require .claude directory for non-Claude agents#1017
ryoppippi merged 2 commits into
mainfrom
claude/fix-issue-1014-xwtDE

Conversation

@ryoppippi
Copy link
Copy Markdown
Owner

@ryoppippi ryoppippi commented May 17, 2026

Summary

Fixes #1014.

Running bunx ccusage on a machine without a Claude data directory crashed with:

error: No valid Claude data directories found. Please ensure at least one of the following exists:
- ~/.config/claude/projects
- ~/.claude/projects
- Or set CLAUDE_CONFIG_DIR environment variable to valid directory path(s) containing a 'projects' subdirectory

ccusage now supports codex, opencode, amp, and pi-agent in addition to Claude Code, so requiring a .claude directory just to load config (ccusage.json) is wrong — it blocks every command, including ccusage codex, on systems that never installed Claude Code.

The crash originated in getConfigSearchPaths() (apps/ccusage/src/config-loader-tokens.ts), which called the throwing getClaudePaths() while building the list of locations to search for ccusage.json.

Fix

Treat the Claude-derived config dirs as best-effort: if getClaudePaths() throws, swallow the error via Result.try + Result.unwrap([]) and fall back to the local .ccusage directory in cwd. All commands (ccusage, ccusage codex, ccusage opencode, ccusage amp, ccusage pi, and the explicit ccusage claude … ones) now load config without a Claude directory present. The Claude-specific loaders/debug command continue to surface the original error when actually loading Claude data, which is the right behavior.

Verification

  • bun run src/index.ts with CLAUDE_CONFIG_DIR=/tmp/does-not-exist now prints Detected: None and No usage data found. instead of throwing.
  • Added a regression test in config-loader-tokens.ts that points CLAUDE_CONFIG_DIR at a directory missing projects/ and asserts loadConfig() returns undefined rather than throwing.
  • pnpm typecheck, pnpm run format, and pnpm --filter ccusage test --run all pass (385 tests / 2 skipped).

Test plan

  • pnpm typecheck
  • pnpm run format
  • pnpm --filter ccusage test --run
  • Manual run with CLAUDE_CONFIG_DIR=/tmp/does-not-exist bun run src/index.ts

https://claude.ai/code/session_01H93Pfks8XvhJ5PAABFpLqh


Generated by Claude Code


Summary by cubic

Stop requiring a Claude data directory to load config so ccusage (including codex, opencode, amp, and pi-agent) runs on machines without .claude. Split Claude path discovery into safe and required variants so Claude-only commands still show a clear setup error; fixes #1014.

  • Bug Fixes
    • Make getClaudePaths() return possibly empty; add requireClaudePaths() for Claude-only entry points.
    • Use requireClaudePaths() in Claude data loaders; config discovery now calls getClaudePaths() and searches local ./.ccusage/ccusage.json first.
    • Add a regression test to ensure loadConfig() returns undefined when CLAUDE_CONFIG_DIR points to a dir without projects/.

Written for commit 238798c. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Commands that rely on external project data now surface a clear setup error if no valid project directories are found, preventing silent no-op behavior.
  • Documentation

    • Clarified that discovery of external project directories is best-effort; missing or unavailable directories simply contribute no additional search paths.
  • Tests

    • Added test coverage for discovery and loading when project directories are missing or invalid.

Review Change Stack

ccusage now supports codex, opencode, amp, and pi-agent in addition to
Claude Code. Running it on a machine without a Claude data directory
crashed with "No valid Claude data directories found" because
getConfigSearchPaths() called the throwing getClaudePaths() while
discovering ccusage.json locations.

Treat Claude config dirs as best-effort: swallow the error and fall back
to the local .ccusage directory so users of other agents (or no agent
yet) can run ccusage without setting CLAUDE_CONFIG_DIR.

Fixes #1014
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62aa8f37-50cc-4a79-a899-7b44bbeeae48

📥 Commits

Reviewing files that changed from the base of the PR and between 5164293 and 238798c.

📒 Files selected for processing (2)
  • apps/ccusage/src/adapter/claude/data-loader.ts
  • apps/ccusage/src/config-loader-tokens.ts

📝 Walkthrough

Walkthrough

Claude path discovery now returns only valid directories and no longer throws; requireClaudePaths() enforces presence and gives helpful setup errors. Data loaders now use requireClaudePaths(). Config search treats Claude resolution as best-effort and a Vitest test verifies loadConfig() does not throw when CLAUDE_CONFIG_DIR points to an empty fixture.

Changes

Claude discovery and resilient config

Layer / File(s) Summary
getClaudePaths() docs and non-throwing behavior
apps/ccusage/src/adapter/claude/data-loader.ts
Document that getClaudePaths() returns an empty array when no valid Claude dirs are found and ensure it returns collected valid paths (possibly empty) even when CLAUDE_CONFIG_DIR is set.
requireClaudePaths() and loader wiring
apps/ccusage/src/adapter/claude/data-loader.ts
Add requireClaudePaths() which calls getClaudePaths() and throws user-friendly setup errors when no valid Claude project directories exist; switch loadDailyUsageData, loadSessionData, loadSessionUsageById, and loadSessionBlockData to use requireClaudePaths() by default.
Config search comment and test
apps/ccusage/src/config-loader-tokens.ts
Update getConfigSearchPaths() comment to state Claude paths are resolved on a best-effort basis; add a Vitest test (issue #1014) that stubs CLAUDE_CONFIG_DIR to an empty fixture and asserts loadConfig() does not throw and returns undefined when no config is found.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ryoppippi/ccusage#365: Related changes to getClaudePaths() and CLAUDE_CONFIG_DIR handling; overlaps with discovery behavior changes.
  • ryoppippi/ccusage#95: Changes enforcing CLAUDE_CONFIG_DIR validation; this PR makes resolution more tolerant.
  • ryoppippi/ccusage#122: Prior modifications to getClaudePaths() throwing behavior; this PR centralizes and adjusts error handling.

Poem

🐰 In burrows of code I hop and peep,
When Claude's paths hide or run too deep,
I catch and fall back, tidy and spry,
No panic — config still gets by,
A tiny carrot patch — hop, reply! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: making Claude directory optional for non-Claude agents by treating it as best-effort during config discovery.
Linked Issues check ✅ Passed The PR successfully addresses issue #1014 by making Claude data directories optional for config discovery while preserving error behavior for Claude-specific commands.
Out of Scope Changes check ✅ Passed All changes are directly scoped to resolving issue #1014: refactoring getClaudePaths() to non-throwing, introducing requireClaudePaths() for Claude-specific loaders, and updating entry points accordingly.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-issue-1014-xwtDE

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/ccusage/src/adapter/claude/data-loader.ts

[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: npm i baseline-browser-mapping@latest -D
tsconfig.json is not found. we cannot use type-aware rules.

Oops! Something went wrong! :(

ESLint: 9.35.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'eslint-plugin-format' imported from /node_modules/.pnpm/@antfu+eslint-config@4.19.0_@vue+compiler-sfc@3.5.30_eslint@9.35.0_typescript@5.9.2_vit_670a2c5c75d4275eabd7bc195a173ee6/node_modules/@antfu/eslint-config/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9)
at packageResolve (node:internal/modules/esm/resolve:764:81)
at moduleResolve (node:internal/modules/esm/resolve:855:18)
at defaultResolve (node:internal/modules/esm/resolve:988:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:697:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:714:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:746:52)
at #resolve (node:internal/modules/esm/loader:679:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:599:35)
at node:internal/modules/esm/loader:628:32

apps/ccusage/src/config-loader-tokens.ts

[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: npm i baseline-browser-mapping@latest -D
tsconfig.json is not found. we cannot use type-aware rules.

Oops! Something went wrong! :(

ESLint: 9.35.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'eslint-plugin-format' imported from /node_modules/.pnpm/@antfu+eslint-config@4.19.0_@vue+compiler-sfc@3.5.30_eslint@9.35.0_typescript@5.9.2_vit_670a2c5c75d4275eabd7bc195a173ee6/node_modules/@antfu/eslint-config/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9)
at packageResolve (node:internal/modules/esm/resolve:764:81)
at moduleResolve (node:internal/modules/esm/resolve:855:18)
at defaultResolve (node:internal/modules/esm/resolve:988:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:697:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:714:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:746:52)
at #resolve (node:internal/modules/esm/loader:679:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:599:35)
at node:internal/modules/esm/loader:628:32


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 17, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide 238798c Commit Preview URL

Branch Preview URL
May 17 2026, 09:41 AM

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 17, 2026

Open in StackBlitz

npx https://pkg.pr.new/ryoppippi/ccusage@1017

commit: 238798c

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 17, 2026

ccusage performance comparison

This compares the PR build against the base branch build on the same CI runner.

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Runtime: package ccusage bin from apps/ccusage/package.json through bun -b, --offline --json, measured by hyperfine with 2 warmups and 7 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude daily --offline --json 0.00 MiB 52.5ms 53.0ms 0.99x 0.03 MiB/s 0.03 MiB/s
claude session --offline --json 0.00 MiB 53.1ms 52.6ms 1.01x 0.03 MiB/s 0.03 MiB/s
codex daily --offline --json 0.00 MiB 52.5ms 52.6ms 1.00x 0.02 MiB/s 0.02 MiB/s
codex session --offline --json 0.00 MiB 52.9ms 52.2ms 1.01x 0.02 MiB/s 0.02 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/work/_temp/ccusage-large-fixture (1.01 GiB, 2,597 files), Codex /home/runner/work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2,597 files)
Runtime: package ccusage bin from apps/ccusage/package.json through bun -b, --offline --json, measured by hyperfine with 0 warmups and 1 runs.

Command Input Base median PR median PR vs base Base throughput PR throughput
claude --offline --json 1.01 GiB 1.339s 1.334s 1.00x 770.00 MiB/s 772.75 MiB/s
codex --offline --json 1.01 GiB 1.571s 1.557s 1.01x 656.07 MiB/s 662.01 MiB/s

Package size

Package artifact Base PR Delta Ratio
packed ccusage-*.tgz 67.50 KiB 67.53 KiB +0.03 KiB 1.00x

Lower medians and smaller packed package sizes are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

Previously getClaudePaths() threw when no Claude data directory was found,
which forced callers that don't actually need a Claude dir (like config
discovery in getConfigSearchPaths) to wrap the call in try/catch or
Result.try. That asymmetry is what made #1014 easy to hit in the first
place.

Make getClaudePaths() the unsurprising "give me what you find, possibly
nothing" variant, and add requireClaudePaths() for explicit Claude-only
entry points (claude:* command loaders, statusline session lookup) that
want to surface the helpful setup hint when no data exists. debug.ts
already had its own length check so it keeps using getClaudePaths().

The config layer now just calls getClaudePaths() directly — no wrapping
needed — restoring the consistency with the other agents' detect
functions and getClaudeProjectPaths().

No behavior change for users:
- `ccusage` on a machine without .claude still runs and reports "None"
- `ccusage claude daily` without .claude still surfaces the original
  "No valid Claude data directories found" error
@ryoppippi ryoppippi merged commit c6337cf into main May 17, 2026
24 checks passed
@ryoppippi ryoppippi deleted the claude/fix-issue-1014-xwtDE branch May 17, 2026 11:00
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.

ccusage not working without .claude dir

2 participants