cc-logs-helper
v0.1.2
Published
Typed SDK over Claude Code's local logs — parse sessions, compute cost and token-weighted usage, detect 5-hour windows, tail live journals
Maintainers
Readme
cc-logs-helper
A typed TypeScript SDK for every data source Claude Code writes under ~/.claude/. Parse journals, compute token-weighted plan usage, roll up 5-hour session windows, watch for live changes, and detect cap hits — all from one package.
Claude-only. Designed specifically for the claude CLI's log format (JSONL sessions under ~/.claude/projects/<sanitized-cwd>/, plus sessions/, todos/, plans/, telemetry/, stats-cache.json, history.jsonl, and per-session subagents/).
Install
pnpm add cc-logs-helper
# or
npm install cc-logs-helperNode 20+. ESM only.
Quick start
import {
discoverSessions,
parseSession,
parseTurnsFromEvents,
detectSessionWindows,
inputFromAssistant,
loadPricing,
} from 'cc-logs-helper'
await loadPricing() // fetches LiteLLM prices (24h cache) or falls back to bundled table
const sessions = await discoverSessions({ includeSidechains: false })
for (const ref of sessions) {
for await (const ev of parseSession(ref)) {
if (ev.kind === 'assistant') {
console.log(ev.timestamp, ev.model, ev.costUSD, ev.weightedUnits)
}
}
}What's parsed
Every journal entry type is mapped to a strongly-typed event:
| event.kind | Source |
| ------------------ | ------------------------------------------ |
| user | user messages (incl. meta, slash commands) |
| assistant | assistant API calls with full usage |
| tool_result | tool output (size, preview, error flag) |
| turn_duration | system.turn_duration entries |
| compact | auto/manual compaction boundaries |
| slash_command | /foo invocations |
| queue | queue operations |
| permission_mode | permission mode transitions |
| file_snapshot | file-history snapshots |
| subagent_ref | Task() subagent spawns |
| unknown | anything else, with raw entry preserved |
Every AssistantEvent carries:
- Full
TokenUsage(input, output, cache-read, cache-create, 5m/1h tiers, web search/fetch) - Cost in USD (from LiteLLM pricing, respecting
speed: 'fast'multiplier) - Weighted units (the Anthropic plan-quota formula; see below)
tools,mcpTools,coreTools,bashCommands,hasAgentSpawn,hasPlanModethinkingChars,textChars,deduplicationKey- Git branch, entrypoint, version, cwd, session/project metadata
API overview
Discovery
discoverProjects() // list ~/.claude/projects/* directories
discoverSessions(opts) // list all session JSONLs, optionally filtered
discoverSubagents() // list subagent JSONLs per parent sessionParsing
parseSession(ref, opts) // async iterable of LogEvent
entriesToEvents(entries, ref) // for tail-watching
parseTurnsFromEvents(events) // user-turn → assistant-calls → Turn
classifyTurn(turn) // add TaskCategory + hasEdits + retriesAggregation
groupByHour / Day / Month / Weekday / HourOfDay // time buckets
groupByProject / Model / ServiceTier // by metadata
groupByGitBranch / Entrypoint / Version // by env
groupByTool / McpServer / BashCommand // by action
groupByCategory // task classifier buckets
heatmapHourByWeekday(calls) // 7×24 grid of cost
cacheHitRatio(usage) // 0..1
sumUsage([usage1, usage2, ...]) // combine usages5-hour windows & burn rate
detectSessionWindows(inputs, windowMs = 5h) // align calls into 5h windows
currentOpenWindow(windows) // currently-open window if any
slidingBurnRate(calls, windowMs, stepMs) // cost/hr timeseries
currentBurn(calls, lookbackMs, now) // recent rate snapshotPricing, cost, weights
loadPricing({ offline? }) // fetch or use fallback
getModelCosts(model) // per-token rates
calculateCost(model, usage, speed) // USD for a single call
computeWeightedUnits(model, usage, weights, modelMultipliers) // plan units
capForPlan('pro' | 'max5' | 'max20' | 'api') // cap in weighted units per 5h
peakAdjustedCap(plan, at) // 1.5× tighter during 5-11am PT
isPeakPT(date) // peak window checkDefault weighting (per Anthropic plan-quota reverse-engineered formula):
units = model_multiplier × (
input_tokens × 1.0 +
cache_create_5m × 1.25 +
cache_create_1h × 2.0 +
cache_read × 0.1 +
output_tokens × 5.0
)Model multipliers: Opus (4, 4.1, 4.5, 4.6, 4.7) = 5.0, Sonnet (3.5, 3.7, 4, 4.5, 4.6) = 1.0, Haiku (3.5, 4.5) = 0.25.
Peak: 5:00–11:00 PT, cap divided by 1.5.
Secondary sources
readStatsCache() // ~/.claude/stats-cache.json
readLiveSessions() // ~/.claude/sessions/*.json (currently open)
readAllHistory() // ~/.claude/history.jsonl
readTodos() // ~/.claude/todos/*.json
readPlans() // ~/.claude/plans/*
readTelemetryFailures() // ~/.claude/telemetry/*.jsonWatch mode
import { watchProjects } from 'cc-logs-helper'
const stop = await watchProjects({
onEvent: ev => console.log(ev.kind, ev.timestamp),
onError: err => console.error(err),
})
// later: stop()Tracks per-file read position and handles truncation.
Cap-hit detection
import { detectCapHits } from 'cc-logs-helper'
const hits = detectCapHits(events)
// { kind: 'explicit' | 'suspected', source: 'user_content' | 'telemetry' | 'abrupt_end' | 'manual', ... }Paths
import { projectsDir, sessionsLiveDir, todosDir, plansDir, telemetryDir, statsCachePath, historyPath } from 'cc-logs-helper'All resolve under ~/.claude/, or CLAUDE_HOME env var if set.
Why this exists
Claude Code already ships per-session observability in the live status line. cc-logs-helper exposes the same raw data as a normal library so tools can build reports, dashboards, schedulers, cost alerts, training data extractors, or integrations — without each one re-implementing the journal parser.
See cc-buddy for a reference consumer (a full-featured TUI + scheduler CLI built on top).
License
Not yet declared.
