Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
- run: bun install --frozen-lockfile
- run: bun lint
- run: bun typecheck
- name: Create default Claude directory for tests
run: mkdir -p $HOME/.claude
- run: bun test

npm-publish-dry-run-and-upload-pkg-pr-now:
Expand Down
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ ccusage daily
# Filter by date range
ccusage daily --since 20250525 --until 20250530

# Use custom Claude data directory
ccusage daily --path /custom/path/to/.claude
# Set CLAUDE_CONFIG_DIR environment variable for custom data directory
export CLAUDE_CONFIG_DIR="/custom/path/to/.claude"
ccusage daily

# Output in JSON format
ccusage daily --json
Expand Down Expand Up @@ -164,6 +165,10 @@ ccusage monthly --since 20250101 --until 20250531
# Use custom Claude data directory
ccusage monthly --path /custom/path/to/.claude

# Or set CLAUDE_CONFIG_DIR environment variable
export CLAUDE_CONFIG_DIR="/custom/path/to/.claude"
ccusage monthly

# Output in JSON format
ccusage monthly --json

Expand Down Expand Up @@ -191,8 +196,9 @@ ccusage session
# Filter sessions by last activity date
ccusage session --since 20250525

# Combine filters
ccusage session --since 20250525 --until 20250530 --path /custom/path
# Combine filters with environment variable
export CLAUDE_CONFIG_DIR="/custom/path"
ccusage session --since 20250525 --until 20250530

# Output in JSON format
ccusage session --json
Expand All @@ -216,7 +222,6 @@ All commands support the following options:

- `-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`)
- `-j, --json`: Output results in JSON format instead of table
- `-m, --mode <mode>`: Cost calculation mode: `auto` (default), `calculate`, or `display`
- `-o, --order <order>`: Sort order: `desc` (newest first, default) or `asc` (oldest first).
Expand All @@ -232,6 +237,21 @@ All commands support the following options:
- **`calculate`**: Always calculates costs from token counts using model pricing, ignores any pre-calculated `costUSD` values
- **`display`**: Always uses pre-calculated `costUSD` values only, shows $0.00 for entries without pre-calculated costs

#### Environment Variable Support

The tool supports the `CLAUDE_CONFIG_DIR` environment variable to specify the Claude data directory:

```bash
# Set the environment variable to use a custom Claude directory
export CLAUDE_CONFIG_DIR="/path/to/custom/claude/directory"
ccusage daily

# The environment variable determines the Claude data directory
ccusage daily
```

The tool will use the path specified in the `CLAUDE_CONFIG_DIR` environment variable, or fall back to the default `~/.claude` directory if not set.

### MCP (Model Context Protocol) Support

Exposes usage data through Model Context Protocol for integration with other tools:
Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"fs-fixture": "^2.8.0",
"gunshi": "^0.26.3",
"lint-staged": "^16.1.0",
"path-type": "^6.0.0",
"picocolors": "^1.1.1",
"publint": "^0.3.12",
"simple-git-hooks": "^2.13.0",
Expand Down
6 changes: 3 additions & 3 deletions src/commands/daily.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
createTotalsObject,
getTotalTokens,
} from '../calculate-cost.ts';
import { loadDailyUsageData } from '../data-loader.ts';
import { getDefaultClaudePath, loadDailyUsageData } from '../data-loader.ts';
import { detectMismatches, printMismatchReport } from '../debug.ts';
import { log, logger } from '../logger.ts';
import { sharedCommandConfig } from '../shared-args.internal.ts';
Expand All @@ -25,7 +25,7 @@ export const dailyCommand = define({
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,
});
Comment on lines 25 to 31
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.

Expand All @@ -45,7 +45,7 @@ export const dailyCommand = 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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/commands/mcp.ts
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';
Expand All @@ -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',
Expand All @@ -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
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.

});

Expand Down
6 changes: 3 additions & 3 deletions src/commands/monthly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
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.

Expand Down Expand Up @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/commands/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
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.

Expand All @@ -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);
}

Expand Down
74 changes: 73 additions & 1 deletion src/data-loader.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { describe, expect, test } from 'bun:test';
import { homedir } from 'node:os';
import { join } from 'node:path';
import process from 'node:process';
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { createFixture } from 'fs-fixture';
import {
calculateCostForEntry,
formatDate,
getDefaultClaudePath,
loadDailyUsageData,
loadMonthlyUsageData,
loadSessionData,
Expand All @@ -29,6 +33,74 @@ describe('formatDate', () => {
});
});

describe('getDefaultClaudePath', () => {
const originalEnv = process.env.CLAUDE_CONFIG_DIR;

beforeEach(() => {
// Clean up env var before each test
delete process.env.CLAUDE_CONFIG_DIR;
});

afterEach(() => {
// Restore original environment
if (originalEnv != null) {
process.env.CLAUDE_CONFIG_DIR = originalEnv;
}
else {
delete process.env.CLAUDE_CONFIG_DIR;
}
});

test('returns CLAUDE_CONFIG_DIR when environment variable is set', async () => {
await using fixture = await createFixture({
claude: {},
});
process.env.CLAUDE_CONFIG_DIR = fixture.path;

expect(getDefaultClaudePath()).toBe(fixture.path);
});

test('returns default path when CLAUDE_CONFIG_DIR is not set', () => {
// Ensure CLAUDE_CONFIG_DIR is not set
delete process.env.CLAUDE_CONFIG_DIR;

// Test that it returns the default path (which ends with .claude)
const actualPath = getDefaultClaudePath();
expect(actualPath).toMatch(/\.claude$/);
expect(actualPath).toContain(homedir());
});

test('returns default path with trimmed CLAUDE_CONFIG_DIR', async () => {
await using fixture = await createFixture({
claude: {},
});
// Test with extra spaces
process.env.CLAUDE_CONFIG_DIR = ` ${fixture.path} `;

expect(getDefaultClaudePath()).toBe(fixture.path);
});

test('throws an error when CLAUDE_CONFIG_DIR is not a directory', async () => {
await using fixture = await createFixture();
process.env.CLAUDE_CONFIG_DIR = join(fixture.path, 'not-a-directory');

expect(() => getDefaultClaudePath()).toThrow(/Claude data directory does not exist/);
});

test('throws an error when CLAUDE_CONFIG_DIR does not exist', async () => {
process.env.CLAUDE_CONFIG_DIR = '/nonexistent/path/that/does/not/exist';

expect(() => getDefaultClaudePath()).toThrow(/Claude data directory does not exist/);
});

test('throws an error when default path does not exist', () => {
// Set to a non-existent path
process.env.CLAUDE_CONFIG_DIR = '/nonexistent/path/.claude';

expect(() => getDefaultClaudePath()).toThrow(/Claude data directory does not exist/);
});
});

describe('loadDailyUsageData', () => {
test('returns empty array when no files found', async () => {
await using fixture = await createFixture({
Expand Down
18 changes: 17 additions & 1 deletion src/data-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.

return envClaudeCodePath;
}

export const UsageDataSchema = v.object({
Expand Down
7 changes: 0 additions & 7 deletions src/shared-args.internal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Args } from 'gunshi';
import type { CostMode, SortOrder } from './types.internal.ts';
import * as v from 'valibot';
import { getDefaultClaudePath } from './data-loader.ts';
import { CostModes, dateSchema, SortOrders } from './types.internal.ts';

function parseDateArg(value: string): string {
Expand All @@ -25,12 +24,6 @@ export const sharedArgs = {
description: 'Filter until date (YYYYMMDD format)',
parse: parseDateArg,
},
path: {
type: 'string',
short: 'p',
description: 'Custom path to Claude data directory',
default: getDefaultClaudePath(),
},
json: {
type: 'boolean',
short: 'j',
Expand Down