import { type SourceType } from './session-parser.js'; export interface StatsOptions { /** Restrict to sessions whose cwd matches (exact path). Default: all cwds. */ cwd?: string; /** Restrict to specific source types. Default: all known sources. */ sources?: SourceType[]; /** Time window in ms from now. Default 30 days. */ sinceMs?: number; /** Gap above which activity is considered idle and intervals split (default 15m). */ idleGapMs?: number; /** Include `entrypoint: 'sdk-cli'` claude-code sessions (Claude Code's own automation). Default false. */ includeSdkCli?: boolean; /** Cap on sessions parsed (performance guardrail, default 2000). */ maxSessions?: number; /** * Use the per-file stamp cache in the OS temp dir (default true). The cache * is EXACT, not a TTL: entries are keyed on file size + mtime, and session * journals are append-only, so a hit means the file is byte-identical to * when it was parsed. Only changed files are re-parsed. */ cache?: boolean; /** Clock override for tests. Default Date.now(). */ now?: number; } /** One message timestamp with just enough context to aggregate. */ export interface MessageStamp { /** Epoch ms. */ t: number; role: 'user' | 'assistant'; sourceType: SourceType; sessionId: string; /** Resolved working directory (project identity). */ cwd: string; } export interface DayStats { /** Local-time calendar day, YYYY-MM-DD. */ day: string; /** Union of gap-merged intervals over all messages, sliced to this day. */ activeMs: number; /** Same, over user messages only — presence, not agent runtime. */ humanMs: number; userMessages: number; assistantMessages: number; /** Distinct sessions with at least one message this day. */ sessions: number; /** Distinct cwds with at least one message this day. */ projects: number; /** Message counts per source type this day. */ sources: Partial>; } export interface ProjectStats { /** Resolved working directory (project identity). */ cwd: string; /** ISO timestamp of the project's last message in the window. */ lastActivity: string; /** Union of gap-merged intervals over this project's messages. */ activeMs: number; userMessages: number; assistantMessages: number; sessions: number; sources: Partial>; } export interface StatsTotals { activeMs: number; humanMs: number; userMessages: number; assistantMessages: number; sessions: number; projects: number; /** Days in the window with at least one message. */ activeDays: number; /** The single busiest day in the window by active time. Null when no activity. */ peakDay: { day: string; activeMs: number; humanMs: number; } | null; /** * Averages over CLOSED active days only — the current local day is excluded * because it's still accruing and would drag the average down. (Peak day * keeps today: a partial day can only undercount, never falsely win.) * Null when the window has no closed active days. */ averages: { activeMsPerDay: number; humanMsPerDay: number; closedActiveDays: number; } | null; sources: Partial>; } export interface StatsResult { sinceMs: number; idleGapMs: number; days: DayStats[]; /** All projects in the window, ordered by last activity, newest first. */ projects: ProjectStats[]; totals: StatsTotals; /** User messages per local hour-of-day, index 0-23. */ hourHistogram: number[]; sessionsScanned: number; /** * Session FILES served from the stamp cache rather than parsed fresh. * Counted before the empty/sdk-cli/cwd filters, so it can exceed * sessionsScanned (which counts sessions surviving those filters). */ cacheHits: number; truncated: boolean; /** Pre-rendered table + histogram for terminal output. */ text: string; } /** Exposed for tests. Not part of the public CLI surface. */ export interface StampCacheEntry { version: number; size: number; mtimeMs: number; sessionId: string; /** Resolved cwd, or null when the source doesn't record one. */ cwd: string | null; entrypoint?: string; stamps: Array<{ t: number; role: 'user' | 'assistant'; }>; } /** Exposed for tests. */ export declare function cachePathFor(sessionFilePath: string): string; /** Exposed for tests. Returns null on miss, mismatch, or unreadable entry. */ export declare function readCacheEntry(sessionFilePath: string, size: number, mtimeMs: number): StampCacheEntry | null; export declare function writeCacheEntry(sessionFilePath: string, entry: StampCacheEntry): void; /** Local-time calendar day key (YYYY-MM-DD) for an epoch-ms timestamp. */ export declare function localDayKey(t: number): string; /** * Merge sorted epoch-ms timestamps into [start, end] intervals, splitting * where consecutive stamps are more than gapMs apart. Exported for tests. */ export declare function mergeIntervals(sortedTimes: number[], gapMs: number): Array<[number, number]>; /** * Pure aggregation core: stamps in, day buckets + totals + histogram out. * Stamps need not be sorted. Exported for tests. */ export declare function computeStats(stamps: MessageStamp[], idleGapMs: number, now?: number): { days: DayStats[]; projects: ProjectStats[]; totals: StatsTotals; hourHistogram: number[]; }; /** Render day buckets as CSV (header + one row per day) for external graphing. */ export declare function renderCsv(days: DayStats[]): string; export declare function stats(opts?: StatsOptions): StatsResult; //# sourceMappingURL=stats.d.ts.map