-
-
Notifications
You must be signed in to change notification settings - Fork 430
feat!: specify claude dir with CLAUDE_CONFIG_DIR and remove -p/--path option
#70
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
Changes from all commits
3ceebfa
f2d2127
03c81b9
4c0cfa7
a9a4a5a
4a9715a
a8dc85a
689d029
22dbb72
5100773
d7f4bb3
66864d7
c624326
3dab757
764ed25
844308e
84441d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| import { define } from 'gunshi'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { getDefaultClaudePath } from '../data-loader.ts'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { logger } from '../logger.ts'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { createMcpServer } from '../mcp.ts'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { sharedArgs } from '../shared-args.internal.ts'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -7,7 +8,6 @@ export const mcpCommand = define({ | |||||||||||||||||||||||||||||||||||||||||||||
| name: 'mcp', | ||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Show usage report for MCP', | ||||||||||||||||||||||||||||||||||||||||||||||
| args: { | ||||||||||||||||||||||||||||||||||||||||||||||
| path: sharedArgs.path, | ||||||||||||||||||||||||||||||||||||||||||||||
| mode: sharedArgs.mode, | ||||||||||||||||||||||||||||||||||||||||||||||
| type: { | ||||||||||||||||||||||||||||||||||||||||||||||
| type: 'enum', | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,14 +23,14 @@ export const mcpCommand = define({ | |||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| async run(ctx) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const { type, mode, path, port } = ctx.values; | ||||||||||||||||||||||||||||||||||||||||||||||
| const { type, mode, port } = ctx.values; | ||||||||||||||||||||||||||||||||||||||||||||||
| // disable info logging | ||||||||||||||||||||||||||||||||||||||||||||||
| if (type === 'stdio') { | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.level = 0; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const server = createMcpServer({ | ||||||||||||||||||||||||||||||||||||||||||||||
| claudePath: path, | ||||||||||||||||||||||||||||||||||||||||||||||
| claudePath: getDefaultClaudePath(), | ||||||||||||||||||||||||||||||||||||||||||||||
| mode, | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Inline duplication & potential exception propagation
- 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
Suggested change
🧰 Tools🪛 ESLint[error] 26-26: Unsafe assignment of an (ts/no-unsafe-assignment) [error] 26-26: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 29-29: Unsafe member access .level on an (ts/no-unsafe-member-access) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ import { | |
| createTotalsObject, | ||
| getTotalTokens, | ||
| } from '../calculate-cost.ts'; | ||
| import { loadMonthlyUsageData } from '../data-loader.ts'; | ||
| import { getDefaultClaudePath, loadMonthlyUsageData } from '../data-loader.ts'; | ||
| import { detectMismatches, printMismatchReport } from '../debug.ts'; | ||
| import { log, logger } from '../logger.ts'; | ||
| import { sharedCommandConfig } from '../shared-args.internal.ts'; | ||
|
|
@@ -25,7 +25,7 @@ export const monthlyCommand = define({ | |
| 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, | ||
| }); | ||
|
Comment on lines
25
to
31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Reuse a single Same duplication pattern as in - 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 (ts/no-unsafe-assignment) [error] 26-26: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 27-27: Unsafe assignment of an (ts/no-unsafe-assignment) [error] 27-27: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 29-29: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 30-30: Unsafe member access .values on an (ts/no-unsafe-member-access) 🤖 Prompt for AI Agents |
||
|
|
@@ -56,7 +56,7 @@ export const monthlyCommand = define({ | |
|
|
||
| // Show debug information if requested | ||
| if (ctx.values.debug && !ctx.values.json) { | ||
| const mismatchStats = await detectMismatches(ctx.values.path); | ||
| const mismatchStats = await detectMismatches(getDefaultClaudePath()); | ||
| printMismatchReport(mismatchStats, ctx.values.debugSamples); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ import { | |
| createTotalsObject, | ||
| getTotalTokens, | ||
| } from '../calculate-cost.ts'; | ||
| import { loadSessionData } from '../data-loader.ts'; | ||
| import { getDefaultClaudePath, loadSessionData } from '../data-loader.ts'; | ||
| import { detectMismatches, printMismatchReport } from '../debug.ts'; | ||
| import { log, logger } from '../logger.ts'; | ||
| import { sharedCommandConfig } from '../shared-args.internal.ts'; | ||
|
|
@@ -25,7 +25,7 @@ export const sessionCommand = define({ | |
| 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, | ||
| }); | ||
|
Comment on lines
25
to
31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Deduplicate 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 (ts/no-unsafe-assignment) [error] 26-26: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 27-27: Unsafe assignment of an (ts/no-unsafe-assignment) [error] 27-27: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 29-29: Unsafe member access .values on an (ts/no-unsafe-member-access) [error] 30-30: Unsafe member access .values on an (ts/no-unsafe-member-access) 🤖 Prompt for AI Agents |
||
|
|
@@ -45,7 +45,7 @@ export const sessionCommand = define({ | |
|
|
||
| // Show debug information if requested | ||
| if (ctx.values.debug && !ctx.values.json) { | ||
| const mismatchStats = await detectMismatches(ctx.values.path); | ||
| const mismatchStats = await detectMismatches(getDefaultClaudePath()); | ||
| printMismatchReport(mismatchStats, ctx.values.debugSamples); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,18 +2,34 @@ import type { CostMode, SortOrder } from './types.internal.ts'; | |
| import { readFile } from 'node:fs/promises'; | ||
| import { homedir } from 'node:os'; | ||
| import path from 'node:path'; | ||
| import process from 'node:process'; | ||
| import { unreachable } from '@core/errorutil'; | ||
| import { groupBy } from 'es-toolkit'; // TODO: after node20 is deprecated, switch to native Object.groupBy | ||
| import { sort } from 'fast-sort'; | ||
| import { isDirectorySync } from 'path-type'; | ||
| import { glob } from 'tinyglobby'; | ||
| import * as v from 'valibot'; | ||
| import { logger } from './logger.ts'; | ||
| import { | ||
| PricingFetcher, | ||
| } from './pricing-fetcher.ts'; | ||
|
|
||
| const DEFAULT_CLAUDE_CODE_PATH = path.join(homedir(), '.claude'); | ||
|
|
||
| /** | ||
| * Default path for Claude data directory | ||
| * Uses environment variable CLAUDE_CONFIG_DIR if set, otherwise defaults to ~/.claude | ||
| */ | ||
| export function getDefaultClaudePath(): string { | ||
| return path.join(homedir(), '.claude'); | ||
| 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(), | ||
| ); | ||
| } | ||
|
Comment on lines
+24
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against empty/undefined
This currently crashes the whole process (see CI failure).
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 🧰 Tools🪛 ESLint[error] 24-24: Unsafe assignment of an error typed value. (ts/no-unsafe-assignment) [error] 24-24: Unsafe call of a(n) (ts/no-unsafe-call) [error] 24-24: Unsafe member access .env on an (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) (ts/no-unsafe-call) 🪛 GitHub Check: ci[failure] 26-26: error: Claude data directory does not exist: /home/runner/.claude. 🤖 Prompt for AI Agents |
||
| return envClaudeCodePath; | ||
| } | ||
|
|
||
| export const UsageDataSchema = v.object({ | ||
|
|
||
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
Cache the resolved Claude path once instead of invoking
getDefaultClaudePath()twicegetDefaultClaudePath()performs I/O + validation on every call.Resolve it once, reuse it for both
loadDailyUsageDataanddetectMismatches, and avoid the (minor) overhead while guaranteeing both calls operate on the identical directory.Also applies to: 46-49
🧰 Tools
🪛 ESLint
[error] 26-26: Unsafe assignment of an
anyvalue.(ts/no-unsafe-assignment)
[error] 26-26: Unsafe member access .values on an
anyvalue.(ts/no-unsafe-member-access)
[error] 27-27: Unsafe assignment of an
anyvalue.(ts/no-unsafe-assignment)
[error] 27-27: Unsafe member access .values on an
anyvalue.(ts/no-unsafe-member-access)
[error] 29-29: Unsafe member access .values on an
anyvalue.(ts/no-unsafe-member-access)
[error] 30-30: Unsafe member access .values on an
anyvalue.(ts/no-unsafe-member-access)
🤖 Prompt for AI Agents