Objective
Add a claude source adapter for local Claude Code session files so Claude usage flows through the existing reporting pipeline like the current pi, codex, gemini, droid, and opencode adapters.
Source shape to support
- directory-backed source under
~/.claude/projects/**/*.jsonl
- JSONL session transcripts containing assistant rows with usage payloads
- duplicate usage rows can appear for the same assistant/request pair and should not double count
- timestamps may be missing or malformed on individual rows
Implementation plan
-
Adapter file
- add
src/sources/claude/claude-source-adapter.ts
- implement
SourceAdapter
- expose
id = 'claude'
- expose
capabilities = { fixedProviderRoots: ['anthropic'] }
-
Discovery
- follow the directory-backed pattern already used by
PiSourceAdapter / CodexSourceAdapter
- use
discoverJsonlFiles() for deterministic recursive discovery
- validate explicit directory overrides the same way current adapters do (
non-empty, exists/readable when explicitly required)
-
Parsing
- use
readJsonlObjects() with line prefiltering so only relevant assistant/usage rows are parsed
- emit usage events only for assistant rows with real usage signal
- normalize timestamps through
normalizeTimestampCandidate() before createUsageEvent
- preserve repo attribution metadata when present (
cwd, repo_root, repoRoot, project_root, projectRoot-style fields)
- deduplicate repeated usage rows within a file using a stable composite key when the transcript exposes both request and message identity
- prefer
parseFileWithDiagnostics() so invalid timestamps / malformed rows can contribute skipped-row diagnostics instead of silently disappearing
-
Wiring
- register the adapter in
src/sources/create-default-adapters.ts
- make sure
getDefaultSourceIds() now includes claude
- keep this source directory-backed and reachable through generic
--source-dir claude=...
- update any stable-order expectations in adapter registry tests/help output tests
-
Tests and docs
- add
tests/sources/claude-source-adapter.test.ts
- extend
tests/sources/create-default-adapters.test.ts for registry order, supported IDs, and --source-dir coverage
- update
tests/cli/create-cli.test.ts expectations that currently hard-code the supported-source count/help text
- update
README.md and generated docs/help output if source examples or supported IDs change
Code paths to touch
src/sources/claude/claude-source-adapter.ts
src/sources/create-default-adapters.ts
src/sources/source-adapter.ts
src/sources/parsing-utils.ts
src/utils/discover-jsonl-files.ts
src/utils/read-jsonl-objects.ts
src/domain/usage-event.ts
tests/sources/claude-source-adapter.test.ts
tests/sources/create-default-adapters.test.ts
tests/cli/create-cli.test.ts
README.md
Verification
- parser fixtures cover:
- valid assistant usage rows
- duplicate request/message rows not double counted
- malformed JSONL lines or irrelevant rows skipped cleanly
- invalid/missing timestamps surfaced via diagnostics
- repo-root extraction when available
llm-usage daily --source claude --source-dir claude=<fixture-dir> works
- provider filtering can prune/select this source correctly via
fixedProviderRoots: ['anthropic']
- full validation passes:
pnpm run lint
pnpm run typecheck
pnpm run test
pnpm run format:check
Objective
Add a
claudesource adapter for local Claude Code session files so Claude usage flows through the existing reporting pipeline like the currentpi,codex,gemini,droid, andopencodeadapters.Source shape to support
~/.claude/projects/**/*.jsonlImplementation plan
Adapter file
src/sources/claude/claude-source-adapter.tsSourceAdapterid = 'claude'capabilities = { fixedProviderRoots: ['anthropic'] }Discovery
PiSourceAdapter/CodexSourceAdapterdiscoverJsonlFiles()for deterministic recursive discoverynon-empty,exists/readablewhen explicitly required)Parsing
readJsonlObjects()with line prefiltering so only relevant assistant/usage rows are parsednormalizeTimestampCandidate()beforecreateUsageEventcwd,repo_root,repoRoot,project_root,projectRoot-style fields)parseFileWithDiagnostics()so invalid timestamps / malformed rows can contribute skipped-row diagnostics instead of silently disappearingWiring
src/sources/create-default-adapters.tsgetDefaultSourceIds()now includesclaude--source-dir claude=...Tests and docs
tests/sources/claude-source-adapter.test.tstests/sources/create-default-adapters.test.tsfor registry order, supported IDs, and--source-dircoveragetests/cli/create-cli.test.tsexpectations that currently hard-code the supported-source count/help textREADME.mdand generated docs/help output if source examples or supported IDs changeCode paths to touch
src/sources/claude/claude-source-adapter.tssrc/sources/create-default-adapters.tssrc/sources/source-adapter.tssrc/sources/parsing-utils.tssrc/utils/discover-jsonl-files.tssrc/utils/read-jsonl-objects.tssrc/domain/usage-event.tstests/sources/claude-source-adapter.test.tstests/sources/create-default-adapters.test.tstests/cli/create-cli.test.tsREADME.mdVerification
llm-usage daily --source claude --source-dir claude=<fixture-dir>worksfixedProviderRoots: ['anthropic']pnpm run lintpnpm run typecheckpnpm run testpnpm run format:check