@code-insights/cli 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +101 -9
  3. package/dist/commands/config.d.ts +3 -0
  4. package/dist/commands/config.d.ts.map +1 -0
  5. package/dist/commands/config.js +130 -0
  6. package/dist/commands/config.js.map +1 -0
  7. package/dist/commands/connect.d.ts.map +1 -1
  8. package/dist/commands/connect.js +7 -1
  9. package/dist/commands/connect.js.map +1 -1
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +52 -0
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/commands/install-hook.d.ts.map +1 -1
  14. package/dist/commands/install-hook.js +16 -3
  15. package/dist/commands/install-hook.js.map +1 -1
  16. package/dist/commands/open.d.ts +9 -0
  17. package/dist/commands/open.d.ts.map +1 -0
  18. package/dist/commands/open.js +59 -0
  19. package/dist/commands/open.js.map +1 -0
  20. package/dist/commands/reset.d.ts.map +1 -1
  21. package/dist/commands/reset.js +14 -1
  22. package/dist/commands/reset.js.map +1 -1
  23. package/dist/commands/stats/actions/cost.d.ts +3 -0
  24. package/dist/commands/stats/actions/cost.d.ts.map +1 -0
  25. package/dist/commands/stats/actions/cost.js +139 -0
  26. package/dist/commands/stats/actions/cost.js.map +1 -0
  27. package/dist/commands/stats/actions/error-handler.d.ts +6 -0
  28. package/dist/commands/stats/actions/error-handler.d.ts.map +1 -0
  29. package/dist/commands/stats/actions/error-handler.js +42 -0
  30. package/dist/commands/stats/actions/error-handler.js.map +1 -0
  31. package/dist/commands/stats/actions/models.d.ts +3 -0
  32. package/dist/commands/stats/actions/models.d.ts.map +1 -0
  33. package/dist/commands/stats/actions/models.js +101 -0
  34. package/dist/commands/stats/actions/models.js.map +1 -0
  35. package/dist/commands/stats/actions/overview.d.ts +3 -0
  36. package/dist/commands/stats/actions/overview.d.ts.map +1 -0
  37. package/dist/commands/stats/actions/overview.js +147 -0
  38. package/dist/commands/stats/actions/overview.js.map +1 -0
  39. package/dist/commands/stats/actions/projects.d.ts +3 -0
  40. package/dist/commands/stats/actions/projects.d.ts.map +1 -0
  41. package/dist/commands/stats/actions/projects.js +90 -0
  42. package/dist/commands/stats/actions/projects.js.map +1 -0
  43. package/dist/commands/stats/actions/today.d.ts +3 -0
  44. package/dist/commands/stats/actions/today.d.ts.map +1 -0
  45. package/dist/commands/stats/actions/today.js +118 -0
  46. package/dist/commands/stats/actions/today.js.map +1 -0
  47. package/dist/commands/stats/data/aggregation.d.ts +60 -0
  48. package/dist/commands/stats/data/aggregation.d.ts.map +1 -0
  49. package/dist/commands/stats/data/aggregation.js +614 -0
  50. package/dist/commands/stats/data/aggregation.js.map +1 -0
  51. package/dist/commands/stats/data/cache.d.ts +29 -0
  52. package/dist/commands/stats/data/cache.d.ts.map +1 -0
  53. package/dist/commands/stats/data/cache.js +197 -0
  54. package/dist/commands/stats/data/cache.js.map +1 -0
  55. package/dist/commands/stats/data/firestore.d.ts +13 -0
  56. package/dist/commands/stats/data/firestore.d.ts.map +1 -0
  57. package/dist/commands/stats/data/firestore.js +155 -0
  58. package/dist/commands/stats/data/firestore.js.map +1 -0
  59. package/dist/commands/stats/data/fuzzy-match.d.ts +5 -0
  60. package/dist/commands/stats/data/fuzzy-match.d.ts.map +1 -0
  61. package/dist/commands/stats/data/fuzzy-match.js +36 -0
  62. package/dist/commands/stats/data/fuzzy-match.js.map +1 -0
  63. package/dist/commands/stats/data/local.d.ts +12 -0
  64. package/dist/commands/stats/data/local.d.ts.map +1 -0
  65. package/dist/commands/stats/data/local.js +77 -0
  66. package/dist/commands/stats/data/local.js.map +1 -0
  67. package/dist/commands/stats/data/source.d.ts +13 -0
  68. package/dist/commands/stats/data/source.d.ts.map +1 -0
  69. package/dist/commands/stats/data/source.js +41 -0
  70. package/dist/commands/stats/data/source.js.map +1 -0
  71. package/dist/commands/stats/data/types.d.ts +229 -0
  72. package/dist/commands/stats/data/types.d.ts.map +1 -0
  73. package/dist/commands/stats/data/types.js +50 -0
  74. package/dist/commands/stats/data/types.js.map +1 -0
  75. package/dist/commands/stats/index.d.ts +3 -0
  76. package/dist/commands/stats/index.d.ts.map +1 -0
  77. package/dist/commands/stats/index.js +41 -0
  78. package/dist/commands/stats/index.js.map +1 -0
  79. package/dist/commands/stats/render/charts.d.ts +9 -0
  80. package/dist/commands/stats/render/charts.d.ts.map +1 -0
  81. package/dist/commands/stats/render/charts.js +45 -0
  82. package/dist/commands/stats/render/charts.js.map +1 -0
  83. package/dist/commands/stats/render/colors.d.ts +21 -0
  84. package/dist/commands/stats/render/colors.d.ts.map +1 -0
  85. package/dist/commands/stats/render/colors.js +47 -0
  86. package/dist/commands/stats/render/colors.js.map +1 -0
  87. package/dist/commands/stats/render/format.d.ts +10 -0
  88. package/dist/commands/stats/render/format.d.ts.map +1 -0
  89. package/dist/commands/stats/render/format.js +57 -0
  90. package/dist/commands/stats/render/format.js.map +1 -0
  91. package/dist/commands/stats/render/layout.d.ts +10 -0
  92. package/dist/commands/stats/render/layout.d.ts.map +1 -0
  93. package/dist/commands/stats/render/layout.js +48 -0
  94. package/dist/commands/stats/render/layout.js.map +1 -0
  95. package/dist/commands/stats/shared.d.ts +6 -0
  96. package/dist/commands/stats/shared.d.ts.map +1 -0
  97. package/dist/commands/stats/shared.js +26 -0
  98. package/dist/commands/stats/shared.js.map +1 -0
  99. package/dist/commands/status.d.ts.map +1 -1
  100. package/dist/commands/status.js +50 -37
  101. package/dist/commands/status.js.map +1 -1
  102. package/dist/commands/sync.d.ts +16 -1
  103. package/dist/commands/sync.d.ts.map +1 -1
  104. package/dist/commands/sync.js +173 -80
  105. package/dist/commands/sync.js.map +1 -1
  106. package/dist/commands/telemetry.d.ts +3 -0
  107. package/dist/commands/telemetry.d.ts.map +1 -0
  108. package/dist/commands/telemetry.js +106 -0
  109. package/dist/commands/telemetry.js.map +1 -0
  110. package/dist/firebase/client.d.ts +5 -0
  111. package/dist/firebase/client.d.ts.map +1 -1
  112. package/dist/firebase/client.js +5 -2
  113. package/dist/firebase/client.js.map +1 -1
  114. package/dist/index.js +21 -4
  115. package/dist/index.js.map +1 -1
  116. package/dist/parser/jsonl.js +3 -2
  117. package/dist/parser/jsonl.js.map +1 -1
  118. package/dist/providers/claude-code.d.ts.map +1 -1
  119. package/dist/providers/claude-code.js +5 -1
  120. package/dist/providers/claude-code.js.map +1 -1
  121. package/dist/providers/codex.d.ts +14 -0
  122. package/dist/providers/codex.d.ts.map +1 -0
  123. package/dist/providers/codex.js +364 -0
  124. package/dist/providers/codex.js.map +1 -0
  125. package/dist/providers/copilot-cli.d.ts +14 -0
  126. package/dist/providers/copilot-cli.d.ts.map +1 -0
  127. package/dist/providers/copilot-cli.js +363 -0
  128. package/dist/providers/copilot-cli.js.map +1 -0
  129. package/dist/providers/cursor.d.ts +27 -0
  130. package/dist/providers/cursor.d.ts.map +1 -0
  131. package/dist/providers/cursor.js +356 -0
  132. package/dist/providers/cursor.js.map +1 -0
  133. package/dist/providers/registry.d.ts +0 -4
  134. package/dist/providers/registry.d.ts.map +1 -1
  135. package/dist/providers/registry.js +9 -6
  136. package/dist/providers/registry.js.map +1 -1
  137. package/dist/types.d.ts +5 -1
  138. package/dist/types.d.ts.map +1 -1
  139. package/dist/utils/config.d.ts +10 -1
  140. package/dist/utils/config.d.ts.map +1 -1
  141. package/dist/utils/config.js +21 -0
  142. package/dist/utils/config.js.map +1 -1
  143. package/dist/utils/paths.d.ts +10 -0
  144. package/dist/utils/paths.d.ts.map +1 -0
  145. package/dist/utils/paths.js +16 -0
  146. package/dist/utils/paths.js.map +1 -0
  147. package/dist/utils/telemetry.d.ts +52 -0
  148. package/dist/utils/telemetry.d.ts.map +1 -0
  149. package/dist/utils/telemetry.js +304 -0
  150. package/dist/utils/telemetry.js.map +1 -0
  151. package/dist/utils/tips.d.ts +11 -0
  152. package/dist/utils/tips.d.ts.map +1 -0
  153. package/dist/utils/tips.js +106 -0
  154. package/dist/utils/tips.js.map +1 -0
  155. package/dist/utils/welcome.d.ts +11 -0
  156. package/dist/utils/welcome.d.ts.map +1 -0
  157. package/dist/utils/welcome.js +101 -0
  158. package/dist/utils/welcome.js.map +1 -0
  159. package/package.json +9 -2
@@ -0,0 +1,29 @@
1
+ import type { ParsedSession } from '../../../types.js';
2
+ import type { SessionRow } from './types.js';
3
+ /**
4
+ * Transform the CLI's ParsedSession into the stats SessionRow shape.
5
+ * Uses a module-level cache for projectId to avoid repeated git spawns.
6
+ */
7
+ export declare function parsedSessionToRow(session: ParsedSession): SessionRow;
8
+ export declare class StatsCache {
9
+ private data;
10
+ private dirty;
11
+ constructor();
12
+ /**
13
+ * Refresh the cache by discovering and parsing sessions from all providers.
14
+ * Returns counts of new and total sessions.
15
+ */
16
+ refresh(): Promise<{
17
+ newSessions: number;
18
+ totalSessions: number;
19
+ }>;
20
+ /**
21
+ * Return all cached session rows with dates properly deserialized.
22
+ * JSON serialization turns Date objects into ISO strings — reconstruct them.
23
+ */
24
+ getAllRows(): SessionRow[];
25
+ private load;
26
+ private save;
27
+ private empty;
28
+ }
29
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/cache.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA+C7C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,UAAU,CAoCrE;AAMD,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,KAAK,CAAU;;IAOvB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IA2ExE;;;OAGG;IACH,UAAU,IAAI,UAAU,EAAE;IAkB1B,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,197 @@
1
+ // ──────────────────────────────────────────────────────
2
+ // Stats cache — disk-based parsed session store
3
+ //
4
+ // Stores pre-parsed SessionRow[] keyed by source file path
5
+ // with mtime-based invalidation. Lives at:
6
+ // ~/.code-insights/stats-cache.json
7
+ // ──────────────────────────────────────────────────────
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import * as os from 'os';
11
+ import { generateStableProjectId } from '../../../utils/device.js';
12
+ import { ensureConfigDir } from '../../../utils/config.js';
13
+ import { splitVirtualPath } from '../../../utils/paths.js';
14
+ import { getAllProviders } from '../../../providers/registry.js';
15
+ // ──────────────────────────────────────────────────────
16
+ // Constants
17
+ // ──────────────────────────────────────────────────────
18
+ const CACHE_PATH = path.join(os.homedir(), '.code-insights', 'stats-cache.json');
19
+ // ──────────────────────────────────────────────────────
20
+ // Module-level projectId cache — avoids repeated git
21
+ // process spawns for the same project path
22
+ // ──────────────────────────────────────────────────────
23
+ const projectIdCache = new Map();
24
+ function getCachedProjectId(projectPath) {
25
+ const existing = projectIdCache.get(projectPath);
26
+ if (existing)
27
+ return existing;
28
+ const { projectId } = generateStableProjectId(projectPath);
29
+ projectIdCache.set(projectPath, projectId);
30
+ return projectId;
31
+ }
32
+ // ──────────────────────────────────────────────────────
33
+ // Exported helpers
34
+ // ──────────────────────────────────────────────────────
35
+ /**
36
+ * Transform the CLI's ParsedSession into the stats SessionRow shape.
37
+ * Uses a module-level cache for projectId to avoid repeated git spawns.
38
+ */
39
+ export function parsedSessionToRow(session) {
40
+ const projectId = getCachedProjectId(session.projectPath);
41
+ return {
42
+ // identity
43
+ id: session.id,
44
+ projectId,
45
+ projectName: session.projectName,
46
+ // timing
47
+ startedAt: session.startedAt,
48
+ endedAt: session.endedAt,
49
+ // counts
50
+ messageCount: session.messageCount,
51
+ userMessageCount: session.userMessageCount,
52
+ assistantMessageCount: session.assistantMessageCount,
53
+ toolCallCount: session.toolCallCount,
54
+ // cost / usage (optional)
55
+ estimatedCostUsd: session.usage?.estimatedCostUsd,
56
+ totalInputTokens: session.usage?.totalInputTokens,
57
+ totalOutputTokens: session.usage?.totalOutputTokens,
58
+ cacheCreationTokens: session.usage?.cacheCreationTokens,
59
+ cacheReadTokens: session.usage?.cacheReadTokens,
60
+ // metadata
61
+ primaryModel: session.usage?.primaryModel,
62
+ modelsUsed: session.usage?.modelsUsed,
63
+ generatedTitle: session.generatedTitle ?? undefined,
64
+ customTitle: session.customTitle,
65
+ summary: session.summary ?? undefined,
66
+ sessionCharacter: session.sessionCharacter ?? undefined,
67
+ sourceTool: session.sourceTool,
68
+ usageSource: session.usage?.usageSource,
69
+ };
70
+ }
71
+ // ──────────────────────────────────────────────────────
72
+ // StatsCache class
73
+ // ──────────────────────────────────────────────────────
74
+ export class StatsCache {
75
+ data;
76
+ dirty;
77
+ constructor() {
78
+ this.dirty = false;
79
+ this.data = this.load();
80
+ }
81
+ /**
82
+ * Refresh the cache by discovering and parsing sessions from all providers.
83
+ * Returns counts of new and total sessions.
84
+ */
85
+ async refresh() {
86
+ const providers = getAllProviders();
87
+ let newSessions = 0;
88
+ // Collect all discovered file paths (keyed by provider name for metadata)
89
+ const allDiscoveredPaths = new Set();
90
+ for (const provider of providers) {
91
+ const providerName = provider.getProviderName();
92
+ let filePaths;
93
+ try {
94
+ filePaths = await provider.discover();
95
+ }
96
+ catch {
97
+ // If discovery fails for a provider, skip it
98
+ continue;
99
+ }
100
+ for (const filePath of filePaths) {
101
+ allDiscoveredPaths.add(filePath);
102
+ // Check mtime for cache invalidation
103
+ const { realPath } = splitVirtualPath(filePath);
104
+ let currentMtime;
105
+ try {
106
+ currentMtime = fs.statSync(realPath).mtime.toISOString();
107
+ }
108
+ catch {
109
+ // File no longer accessible, skip
110
+ continue;
111
+ }
112
+ const cached = this.data.entries[filePath];
113
+ if (cached && cached.lastModified === currentMtime) {
114
+ // Cache hit — mtime unchanged
115
+ continue;
116
+ }
117
+ // Cache miss — parse the file
118
+ let session;
119
+ try {
120
+ session = await provider.parse(filePath);
121
+ }
122
+ catch {
123
+ // Parse error — skip this file
124
+ continue;
125
+ }
126
+ if (!session)
127
+ continue;
128
+ const row = parsedSessionToRow(session);
129
+ this.data.entries[filePath] = {
130
+ lastModified: currentMtime,
131
+ provider: providerName,
132
+ rows: [row],
133
+ };
134
+ this.dirty = true;
135
+ newSessions++;
136
+ }
137
+ }
138
+ // Prune entries for file paths that no longer exist in any provider's discovery
139
+ for (const cachedPath of Object.keys(this.data.entries)) {
140
+ if (!allDiscoveredPaths.has(cachedPath)) {
141
+ delete this.data.entries[cachedPath];
142
+ this.dirty = true;
143
+ }
144
+ }
145
+ // Persist if anything changed
146
+ if (this.dirty) {
147
+ this.data.lastRefresh = new Date().toISOString();
148
+ this.save();
149
+ }
150
+ return { newSessions, totalSessions: this.getAllRows().length };
151
+ }
152
+ /**
153
+ * Return all cached session rows with dates properly deserialized.
154
+ * JSON serialization turns Date objects into ISO strings — reconstruct them.
155
+ */
156
+ getAllRows() {
157
+ const rows = [];
158
+ for (const entry of Object.values(this.data.entries)) {
159
+ for (const row of entry.rows) {
160
+ rows.push({
161
+ ...row,
162
+ startedAt: new Date(row.startedAt),
163
+ endedAt: new Date(row.endedAt),
164
+ });
165
+ }
166
+ }
167
+ return rows;
168
+ }
169
+ // ──────────────────────────────────────────────────────
170
+ // Private methods
171
+ // ──────────────────────────────────────────────────────
172
+ load() {
173
+ try {
174
+ if (!fs.existsSync(CACHE_PATH)) {
175
+ return this.empty();
176
+ }
177
+ const content = fs.readFileSync(CACHE_PATH, 'utf-8');
178
+ const parsed = JSON.parse(content);
179
+ if (parsed.version !== 1) {
180
+ return this.empty();
181
+ }
182
+ return parsed;
183
+ }
184
+ catch {
185
+ return this.empty();
186
+ }
187
+ }
188
+ save() {
189
+ ensureConfigDir();
190
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(this.data, null, 2));
191
+ this.dirty = false;
192
+ }
193
+ empty() {
194
+ return { version: 1, lastRefresh: '', entries: {} };
195
+ }
196
+ }
197
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/commands/stats/data/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ClaudeInsightConfig } from '../../../types.js';
2
+ import type { StatsDataSource, SessionRow, SessionQueryOptions, UsageStatsDoc, ProjectResolution, PrepareResult, StatsFlags } from './types.js';
3
+ export declare class FirestoreDataSource implements StatsDataSource {
4
+ private config;
5
+ readonly name = "firestore";
6
+ constructor(config: ClaudeInsightConfig);
7
+ prepare(flags: StatsFlags): Promise<PrepareResult>;
8
+ getSessions(opts: SessionQueryOptions): Promise<SessionRow[]>;
9
+ getUsageStats(): Promise<UsageStatsDoc | null>;
10
+ resolveProjectId(name: string): Promise<ProjectResolution>;
11
+ getLastSession(): Promise<SessionRow | null>;
12
+ }
13
+ //# sourceMappingURL=firestore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/firestore.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AAgEpB,qBAAa,mBAAoB,YAAW,eAAe;IAG7C,OAAO,CAAC,MAAM;IAF1B,QAAQ,CAAC,IAAI,eAAe;gBAER,MAAM,EAAE,mBAAmB;IAEzC,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBlD,WAAW,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAiC7D,aAAa,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAgB9C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqB1D,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAWnD"}
@@ -0,0 +1,155 @@
1
+ // ──────────────────────────────────────────────────────
2
+ // Firestore data source for stats commands
3
+ // ──────────────────────────────────────────────────────
4
+ import admin from 'firebase-admin';
5
+ import { initializeFirebase, getDb, getProjects } from '../../../firebase/client.js';
6
+ import { ProjectNotFoundError, FirestoreIndexError } from './types.js';
7
+ import { findSimilarNames } from './fuzzy-match.js';
8
+ // ──────────────────────────────────────────────────────
9
+ // Helpers
10
+ // ──────────────────────────────────────────────────────
11
+ /** Checks if error is a Firestore FAILED_PRECONDITION (missing composite index) */
12
+ function isFirestoreIndexError(error) {
13
+ if (typeof error === 'object' && error !== null && 'code' in error) {
14
+ return error.code === 9;
15
+ }
16
+ return false;
17
+ }
18
+ /** Extracts the Firebase console index-creation URL from an error message */
19
+ function extractIndexUrl(error) {
20
+ if (typeof error === 'object' && error !== null && 'message' in error) {
21
+ const message = error.message;
22
+ const match = message.match(/(https:\/\/console\.firebase\.google\.com\/[^\s"]+)/);
23
+ return match ? match[1] : null;
24
+ }
25
+ return null;
26
+ }
27
+ // ──────────────────────────────────────────────────────
28
+ // Document mapper
29
+ // ──────────────────────────────────────────────────────
30
+ /** Maps a Firestore document to the universal SessionRow shape */
31
+ function docToSessionRow(doc) {
32
+ const data = doc.data();
33
+ if (!data)
34
+ throw new Error(`Session document ${doc.id} has no data`);
35
+ return {
36
+ id: doc.id,
37
+ projectId: data.projectId,
38
+ projectName: data.projectName,
39
+ startedAt: data.startedAt?.toDate() ?? new Date(),
40
+ endedAt: data.endedAt?.toDate() ?? new Date(),
41
+ messageCount: data.messageCount ?? 0,
42
+ userMessageCount: data.userMessageCount ?? 0,
43
+ assistantMessageCount: data.assistantMessageCount ?? 0,
44
+ toolCallCount: data.toolCallCount ?? 0,
45
+ estimatedCostUsd: data.estimatedCostUsd,
46
+ totalInputTokens: data.totalInputTokens,
47
+ totalOutputTokens: data.totalOutputTokens,
48
+ cacheCreationTokens: data.cacheCreationTokens,
49
+ cacheReadTokens: data.cacheReadTokens,
50
+ primaryModel: data.primaryModel,
51
+ modelsUsed: data.modelsUsed,
52
+ generatedTitle: data.generatedTitle,
53
+ customTitle: data.customTitle,
54
+ summary: data.summary,
55
+ sessionCharacter: data.sessionCharacter,
56
+ sourceTool: data.sourceTool,
57
+ usageSource: data.usageSource,
58
+ };
59
+ }
60
+ // ──────────────────────────────────────────────────────
61
+ // FirestoreDataSource
62
+ // ──────────────────────────────────────────────────────
63
+ export class FirestoreDataSource {
64
+ config;
65
+ name = 'firestore';
66
+ constructor(config) {
67
+ this.config = config;
68
+ }
69
+ async prepare(flags) {
70
+ initializeFirebase(this.config);
71
+ if (!flags.noSync) {
72
+ try {
73
+ const { runSync } = await import('../../sync.js');
74
+ const result = await runSync({ quiet: true });
75
+ if (result.syncedCount > 0) {
76
+ return { message: `Synced ${result.syncedCount} new sessions`, dataChanged: true };
77
+ }
78
+ return { message: 'Up to date', dataChanged: false };
79
+ }
80
+ catch {
81
+ return { message: 'Sync failed (showing cached data)', dataChanged: false };
82
+ }
83
+ }
84
+ return { message: 'Sync skipped', dataChanged: false };
85
+ }
86
+ async getSessions(opts) {
87
+ const firestore = getDb();
88
+ let query = firestore.collection('sessions');
89
+ if (opts.projectId) {
90
+ query = query.where('projectId', '==', opts.projectId);
91
+ }
92
+ if (opts.sourceTool) {
93
+ query = query.where('sourceTool', '==', opts.sourceTool);
94
+ }
95
+ if (opts.periodStart) {
96
+ query = query.where('startedAt', '>=', admin.firestore.Timestamp.fromDate(opts.periodStart));
97
+ }
98
+ query = query.orderBy('startedAt', 'desc');
99
+ try {
100
+ const snapshot = await query.get();
101
+ return snapshot.docs.map(docToSessionRow);
102
+ }
103
+ catch (error) {
104
+ if (isFirestoreIndexError(error)) {
105
+ const url = extractIndexUrl(error);
106
+ throw new FirestoreIndexError(url
107
+ ? `Missing Firestore index. Create it here: ${url}`
108
+ : 'Missing Firestore composite index. Check the error details in Firebase console.', url ?? '');
109
+ }
110
+ throw error;
111
+ }
112
+ }
113
+ async getUsageStats() {
114
+ const firestore = getDb();
115
+ const doc = await firestore.collection('stats').doc('usage').get();
116
+ if (!doc.exists)
117
+ return null;
118
+ const data = doc.data();
119
+ return {
120
+ totalInputTokens: data.totalInputTokens ?? 0,
121
+ totalOutputTokens: data.totalOutputTokens ?? 0,
122
+ cacheCreationTokens: data.cacheCreationTokens ?? 0,
123
+ cacheReadTokens: data.cacheReadTokens ?? 0,
124
+ estimatedCostUsd: data.estimatedCostUsd ?? 0,
125
+ sessionsWithUsage: data.sessionsWithUsage ?? 0,
126
+ lastUpdatedAt: data.lastUpdatedAt?.toDate() ?? new Date(),
127
+ };
128
+ }
129
+ async resolveProjectId(name) {
130
+ const projects = await getProjects();
131
+ // Exact match (case-insensitive)
132
+ const exact = projects.find((p) => p.name.toLowerCase() === name.toLowerCase());
133
+ if (exact)
134
+ return { projectId: exact.id, projectName: exact.name };
135
+ // Substring match
136
+ const substring = projects.filter((p) => p.name.toLowerCase().includes(name.toLowerCase()));
137
+ if (substring.length === 1)
138
+ return { projectId: substring[0].id, projectName: substring[0].name };
139
+ // No match — throw with suggestions
140
+ const suggestions = findSimilarNames(name, projects.map((p) => p.name));
141
+ throw new ProjectNotFoundError(`Project "${name}" not found.`, name, projects.map((p) => ({ name: p.name })), suggestions);
142
+ }
143
+ async getLastSession() {
144
+ const firestore = getDb();
145
+ const snapshot = await firestore
146
+ .collection('sessions')
147
+ .orderBy('startedAt', 'desc')
148
+ .limit(1)
149
+ .get();
150
+ if (snapshot.empty)
151
+ return null;
152
+ return docToSessionRow(snapshot.docs[0]);
153
+ }
154
+ }
155
+ //# sourceMappingURL=firestore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore.js","sourceRoot":"","sources":["../../../../src/commands/stats/data/firestore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Standard Levenshtein distance between two strings */
2
+ export declare function levenshtein(a: string, b: string): number;
3
+ /** Returns candidate names within maxDistance, sorted by distance */
4
+ export declare function findSimilarNames(input: string, candidates: string[], maxDistance?: number): string[];
5
+ //# sourceMappingURL=fuzzy-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy-match.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/fuzzy-match.ts"],"names":[],"mappings":"AAOA,wDAAwD;AACxD,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAoBxD;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,SAAI,GAAG,MAAM,EAAE,CAO/F"}
@@ -0,0 +1,36 @@
1
+ // ──────────────────────────────────────────────────────
2
+ // Fuzzy string matching utilities
3
+ //
4
+ // Shared between FirestoreDataSource and LocalDataSource
5
+ // for --project name resolution.
6
+ // ──────────────────────────────────────────────────────
7
+ /** Standard Levenshtein distance between two strings */
8
+ export function levenshtein(a, b) {
9
+ const m = a.length;
10
+ const n = b.length;
11
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
12
+ for (let i = 0; i <= m; i++)
13
+ dp[i][0] = i;
14
+ for (let j = 0; j <= n; j++)
15
+ dp[0][j] = j;
16
+ for (let i = 1; i <= m; i++) {
17
+ for (let j = 1; j <= n; j++) {
18
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
19
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, // deletion
20
+ dp[i][j - 1] + 1, // insertion
21
+ dp[i - 1][j - 1] + cost // substitution
22
+ );
23
+ }
24
+ }
25
+ return dp[m][n];
26
+ }
27
+ /** Returns candidate names within maxDistance, sorted by distance */
28
+ export function findSimilarNames(input, candidates, maxDistance = 3) {
29
+ const lower = input.toLowerCase();
30
+ return candidates
31
+ .map((name) => ({ name, distance: levenshtein(lower, name.toLowerCase()) }))
32
+ .filter(({ distance }) => distance <= maxDistance)
33
+ .sort((a, b) => a.distance - b.distance)
34
+ .map(({ name }) => name);
35
+ }
36
+ //# sourceMappingURL=fuzzy-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy-match.js","sourceRoot":"","sources":["../../../../src/commands/stats/data/fuzzy-match.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,kCAAkC;AAClC,EAAE;AACF,yDAAyD;AACzD,iCAAiC;AACjC,yDAAyD;AAEzD,wDAAwD;AACxD,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,MAAM,EAAE,GAAe,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAE1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CACjB,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAO,WAAW;YAClC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAO,YAAY;YACnC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,eAAe;aACxC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,UAAoB,EAAE,WAAW,GAAG,CAAC;IACnF,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,OAAO,UAAU;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;SAC3E,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,IAAI,WAAW,CAAC;SACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;SACvC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { StatsDataSource, SessionRow, SessionQueryOptions, UsageStatsDoc, ProjectResolution, PrepareResult, StatsFlags } from './types.js';
2
+ export declare class LocalDataSource implements StatsDataSource {
3
+ readonly name = "local";
4
+ private cache;
5
+ constructor();
6
+ prepare(flags: StatsFlags): Promise<PrepareResult>;
7
+ getSessions(opts: SessionQueryOptions): Promise<SessionRow[]>;
8
+ getUsageStats(): Promise<UsageStatsDoc | null>;
9
+ resolveProjectId(name: string): Promise<ProjectResolution>;
10
+ getLastSession(): Promise<SessionRow | null>;
11
+ }
12
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/local.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AASpB,qBAAa,eAAgB,YAAW,eAAe;IACrD,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,KAAK,CAAa;;IAMpB,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAgBlD,WAAW,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAW7D,aAAa,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK9C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4B1D,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAKnD"}
@@ -0,0 +1,77 @@
1
+ // ──────────────────────────────────────────────────────
2
+ // Local data source for stats commands
3
+ //
4
+ // Reads from the disk-based stats cache (no Firestore).
5
+ // Zero-config: works out of the box by discovering and
6
+ // parsing local session files from all providers.
7
+ // ──────────────────────────────────────────────────────
8
+ import { ProjectNotFoundError } from './types.js';
9
+ import { findSimilarNames } from './fuzzy-match.js';
10
+ import { StatsCache } from './cache.js';
11
+ // ──────────────────────────────────────────────────────
12
+ // LocalDataSource
13
+ // ──────────────────────────────────────────────────────
14
+ export class LocalDataSource {
15
+ name = 'local';
16
+ cache;
17
+ constructor() {
18
+ this.cache = new StatsCache();
19
+ }
20
+ async prepare(flags) {
21
+ if (flags.noSync) {
22
+ const total = this.cache.getAllRows().length;
23
+ return { message: `${total} sessions cached`, dataChanged: false };
24
+ }
25
+ const result = await this.cache.refresh();
26
+ if (result.newSessions > 0) {
27
+ return {
28
+ message: `Parsed ${result.newSessions} new sessions (${result.totalSessions} total)`,
29
+ dataChanged: true,
30
+ };
31
+ }
32
+ return { message: `${result.totalSessions} sessions cached`, dataChanged: false };
33
+ }
34
+ async getSessions(opts) {
35
+ let rows = this.cache.getAllRows();
36
+ if (opts.periodStart)
37
+ rows = rows.filter((r) => r.startedAt >= opts.periodStart);
38
+ if (opts.projectId)
39
+ rows = rows.filter((r) => r.projectId === opts.projectId);
40
+ if (opts.sourceTool)
41
+ rows = rows.filter((r) => r.sourceTool === opts.sourceTool);
42
+ rows.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
43
+ return rows;
44
+ }
45
+ async getUsageStats() {
46
+ // Local source has no pre-computed aggregate
47
+ return null;
48
+ }
49
+ async resolveProjectId(name) {
50
+ const rows = this.cache.getAllRows();
51
+ const projects = new Map();
52
+ for (const row of rows) {
53
+ if (!projects.has(row.projectId)) {
54
+ projects.set(row.projectId, { id: row.projectId, name: row.projectName });
55
+ }
56
+ }
57
+ const projectList = [...projects.values()];
58
+ // Exact match (case-insensitive)
59
+ const exact = projectList.find((p) => p.name.toLowerCase() === name.toLowerCase());
60
+ if (exact)
61
+ return { projectId: exact.id, projectName: exact.name };
62
+ // Substring match
63
+ const substring = projectList.filter((p) => p.name.toLowerCase().includes(name.toLowerCase()));
64
+ if (substring.length === 1)
65
+ return { projectId: substring[0].id, projectName: substring[0].name };
66
+ // No match — throw with suggestions
67
+ const suggestions = findSimilarNames(name, projectList.map((p) => p.name));
68
+ throw new ProjectNotFoundError(`Project "${name}" not found.`, name, projectList.map((p) => ({ name: p.name })), suggestions);
69
+ }
70
+ async getLastSession() {
71
+ const rows = this.cache.getAllRows();
72
+ if (rows.length === 0)
73
+ return null;
74
+ return rows.reduce((latest, row) => (row.startedAt > latest.startedAt ? row : latest));
75
+ }
76
+ }
77
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../../../src/commands/stats/data/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { StatsDataSource, StatsFlags } from './types.js';
2
+ /**
3
+ * Resolve which data source to use based on flags and config.
4
+ *
5
+ * Priority (highest to lowest):
6
+ * 1. --local flag -> always LocalDataSource
7
+ * 2. --remote flag -> always FirestoreDataSource (error if not configured)
8
+ * 3. config.dataSource -> as configured ('local' or 'firebase')
9
+ * 4. No config, Firebase creds present -> FirestoreDataSource (backward compat)
10
+ * 5. No config at all -> LocalDataSource (zero-config first run)
11
+ */
12
+ export declare function resolveDataSource(flags: StatsFlags): Promise<StatsDataSource>;
13
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI9D;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAoCnF"}
@@ -0,0 +1,41 @@
1
+ import { loadConfig } from '../../../utils/config.js';
2
+ import { ConfigNotFoundError } from './types.js';
3
+ /**
4
+ * Resolve which data source to use based on flags and config.
5
+ *
6
+ * Priority (highest to lowest):
7
+ * 1. --local flag -> always LocalDataSource
8
+ * 2. --remote flag -> always FirestoreDataSource (error if not configured)
9
+ * 3. config.dataSource -> as configured ('local' or 'firebase')
10
+ * 4. No config, Firebase creds present -> FirestoreDataSource (backward compat)
11
+ * 5. No config at all -> LocalDataSource (zero-config first run)
12
+ */
13
+ export async function resolveDataSource(flags) {
14
+ if (flags.local) {
15
+ const { LocalDataSource } = await import('./local.js');
16
+ return new LocalDataSource();
17
+ }
18
+ if (flags.remote) {
19
+ const config = loadConfig();
20
+ if (!config?.firebase) {
21
+ throw new ConfigNotFoundError('Firebase not configured. Run `code-insights init` first, or use `stats --local` for local-only stats.');
22
+ }
23
+ const { FirestoreDataSource } = await import('./firestore.js');
24
+ return new FirestoreDataSource(config);
25
+ }
26
+ const config = loadConfig();
27
+ if (!config) {
28
+ const { LocalDataSource } = await import('./local.js');
29
+ return new LocalDataSource();
30
+ }
31
+ // Explicit dataSource preference or backward-compat inference
32
+ const useFirebase = config.dataSource === 'firebase' ||
33
+ (!config.dataSource && config.firebase?.projectId);
34
+ if (useFirebase && config.firebase) {
35
+ const { FirestoreDataSource } = await import('./firestore.js');
36
+ return new FirestoreDataSource(config);
37
+ }
38
+ const { LocalDataSource } = await import('./local.js');
39
+ return new LocalDataSource();
40
+ }
41
+ //# sourceMappingURL=source.js.map