@code-insights/cli 1.2.0 → 2.1.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.
- package/CHANGELOG.md +26 -0
- package/README.md +101 -9
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +130 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +7 -1
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +52 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-hook.d.ts.map +1 -1
- package/dist/commands/install-hook.js +16 -3
- package/dist/commands/install-hook.js.map +1 -1
- package/dist/commands/open.d.ts +9 -0
- package/dist/commands/open.d.ts.map +1 -0
- package/dist/commands/open.js +59 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/reset.d.ts.map +1 -1
- package/dist/commands/reset.js +14 -1
- package/dist/commands/reset.js.map +1 -1
- package/dist/commands/stats/actions/cost.d.ts +3 -0
- package/dist/commands/stats/actions/cost.d.ts.map +1 -0
- package/dist/commands/stats/actions/cost.js +139 -0
- package/dist/commands/stats/actions/cost.js.map +1 -0
- package/dist/commands/stats/actions/error-handler.d.ts +6 -0
- package/dist/commands/stats/actions/error-handler.d.ts.map +1 -0
- package/dist/commands/stats/actions/error-handler.js +42 -0
- package/dist/commands/stats/actions/error-handler.js.map +1 -0
- package/dist/commands/stats/actions/models.d.ts +3 -0
- package/dist/commands/stats/actions/models.d.ts.map +1 -0
- package/dist/commands/stats/actions/models.js +101 -0
- package/dist/commands/stats/actions/models.js.map +1 -0
- package/dist/commands/stats/actions/overview.d.ts +3 -0
- package/dist/commands/stats/actions/overview.d.ts.map +1 -0
- package/dist/commands/stats/actions/overview.js +147 -0
- package/dist/commands/stats/actions/overview.js.map +1 -0
- package/dist/commands/stats/actions/projects.d.ts +3 -0
- package/dist/commands/stats/actions/projects.d.ts.map +1 -0
- package/dist/commands/stats/actions/projects.js +90 -0
- package/dist/commands/stats/actions/projects.js.map +1 -0
- package/dist/commands/stats/actions/today.d.ts +3 -0
- package/dist/commands/stats/actions/today.d.ts.map +1 -0
- package/dist/commands/stats/actions/today.js +118 -0
- package/dist/commands/stats/actions/today.js.map +1 -0
- package/dist/commands/stats/data/aggregation.d.ts +60 -0
- package/dist/commands/stats/data/aggregation.d.ts.map +1 -0
- package/dist/commands/stats/data/aggregation.js +614 -0
- package/dist/commands/stats/data/aggregation.js.map +1 -0
- package/dist/commands/stats/data/cache.d.ts +29 -0
- package/dist/commands/stats/data/cache.d.ts.map +1 -0
- package/dist/commands/stats/data/cache.js +197 -0
- package/dist/commands/stats/data/cache.js.map +1 -0
- package/dist/commands/stats/data/firestore.d.ts +13 -0
- package/dist/commands/stats/data/firestore.d.ts.map +1 -0
- package/dist/commands/stats/data/firestore.js +170 -0
- package/dist/commands/stats/data/firestore.js.map +1 -0
- package/dist/commands/stats/data/fuzzy-match.d.ts +5 -0
- package/dist/commands/stats/data/fuzzy-match.d.ts.map +1 -0
- package/dist/commands/stats/data/fuzzy-match.js +36 -0
- package/dist/commands/stats/data/fuzzy-match.js.map +1 -0
- package/dist/commands/stats/data/local.d.ts +12 -0
- package/dist/commands/stats/data/local.d.ts.map +1 -0
- package/dist/commands/stats/data/local.js +81 -0
- package/dist/commands/stats/data/local.js.map +1 -0
- package/dist/commands/stats/data/source.d.ts +13 -0
- package/dist/commands/stats/data/source.d.ts.map +1 -0
- package/dist/commands/stats/data/source.js +41 -0
- package/dist/commands/stats/data/source.js.map +1 -0
- package/dist/commands/stats/data/types.d.ts +229 -0
- package/dist/commands/stats/data/types.d.ts.map +1 -0
- package/dist/commands/stats/data/types.js +50 -0
- package/dist/commands/stats/data/types.js.map +1 -0
- package/dist/commands/stats/index.d.ts +3 -0
- package/dist/commands/stats/index.d.ts.map +1 -0
- package/dist/commands/stats/index.js +41 -0
- package/dist/commands/stats/index.js.map +1 -0
- package/dist/commands/stats/render/charts.d.ts +9 -0
- package/dist/commands/stats/render/charts.d.ts.map +1 -0
- package/dist/commands/stats/render/charts.js +45 -0
- package/dist/commands/stats/render/charts.js.map +1 -0
- package/dist/commands/stats/render/colors.d.ts +21 -0
- package/dist/commands/stats/render/colors.d.ts.map +1 -0
- package/dist/commands/stats/render/colors.js +47 -0
- package/dist/commands/stats/render/colors.js.map +1 -0
- package/dist/commands/stats/render/format.d.ts +10 -0
- package/dist/commands/stats/render/format.d.ts.map +1 -0
- package/dist/commands/stats/render/format.js +57 -0
- package/dist/commands/stats/render/format.js.map +1 -0
- package/dist/commands/stats/render/layout.d.ts +10 -0
- package/dist/commands/stats/render/layout.d.ts.map +1 -0
- package/dist/commands/stats/render/layout.js +48 -0
- package/dist/commands/stats/render/layout.js.map +1 -0
- package/dist/commands/stats/shared.d.ts +6 -0
- package/dist/commands/stats/shared.d.ts.map +1 -0
- package/dist/commands/stats/shared.js +26 -0
- package/dist/commands/stats/shared.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +50 -37
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts +16 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +196 -79
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/telemetry.d.ts +3 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +106 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/firebase/client.d.ts +5 -0
- package/dist/firebase/client.d.ts.map +1 -1
- package/dist/firebase/client.js +5 -2
- package/dist/firebase/client.js.map +1 -1
- package/dist/index.js +21 -4
- package/dist/index.js.map +1 -1
- package/dist/parser/jsonl.js +3 -2
- package/dist/parser/jsonl.js.map +1 -1
- package/dist/providers/claude-code.d.ts.map +1 -1
- package/dist/providers/claude-code.js +5 -1
- package/dist/providers/claude-code.js.map +1 -1
- package/dist/providers/codex.d.ts +14 -0
- package/dist/providers/codex.d.ts.map +1 -0
- package/dist/providers/codex.js +364 -0
- package/dist/providers/codex.js.map +1 -0
- package/dist/providers/copilot-cli.d.ts +14 -0
- package/dist/providers/copilot-cli.d.ts.map +1 -0
- package/dist/providers/copilot-cli.js +363 -0
- package/dist/providers/copilot-cli.js.map +1 -0
- package/dist/providers/cursor.d.ts +27 -0
- package/dist/providers/cursor.d.ts.map +1 -0
- package/dist/providers/cursor.js +446 -0
- package/dist/providers/cursor.js.map +1 -0
- package/dist/providers/registry.d.ts +0 -4
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +9 -6
- package/dist/providers/registry.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/config.d.ts +10 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +21 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/paths.d.ts +10 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +16 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/telemetry.d.ts +52 -0
- package/dist/utils/telemetry.d.ts.map +1 -0
- package/dist/utils/telemetry.js +304 -0
- package/dist/utils/telemetry.js.map +1 -0
- package/dist/utils/tips.d.ts +11 -0
- package/dist/utils/tips.d.ts.map +1 -0
- package/dist/utils/tips.js +106 -0
- package/dist/utils/tips.js.map +1 -0
- package/dist/utils/welcome.d.ts +11 -0
- package/dist/utils/welcome.d.ts.map +1 -0
- package/dist/utils/welcome.js +72 -0
- package/dist/utils/welcome.js.map +1 -0
- 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(opts?: Pick<SessionQueryOptions, 'sourceTool' | 'projectId'>): 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,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,YAAY,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CA8B/G"}
|
|
@@ -0,0 +1,170 @@
|
|
|
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(opts) {
|
|
144
|
+
const firestore = getDb();
|
|
145
|
+
let query = firestore.collection('sessions');
|
|
146
|
+
if (opts?.sourceTool) {
|
|
147
|
+
query = query.where('sourceTool', '==', opts.sourceTool);
|
|
148
|
+
}
|
|
149
|
+
if (opts?.projectId) {
|
|
150
|
+
query = query.where('projectId', '==', opts.projectId);
|
|
151
|
+
}
|
|
152
|
+
query = query.orderBy('startedAt', 'desc').limit(1);
|
|
153
|
+
try {
|
|
154
|
+
const snapshot = await query.get();
|
|
155
|
+
if (snapshot.empty)
|
|
156
|
+
return null;
|
|
157
|
+
return docToSessionRow(snapshot.docs[0]);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
if (isFirestoreIndexError(error)) {
|
|
161
|
+
const url = extractIndexUrl(error);
|
|
162
|
+
throw new FirestoreIndexError(url
|
|
163
|
+
? `Missing Firestore index. Create it here: ${url}`
|
|
164
|
+
: 'Missing Firestore composite index. Check the error details in Firebase console.', url ?? '');
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# 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(opts?: Pick<SessionQueryOptions, 'sourceTool' | 'projectId'>): 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,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,YAAY,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAO/G"}
|
|
@@ -0,0 +1,81 @@
|
|
|
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(opts) {
|
|
71
|
+
let rows = this.cache.getAllRows();
|
|
72
|
+
if (opts?.sourceTool)
|
|
73
|
+
rows = rows.filter(r => r.sourceTool === opts.sourceTool);
|
|
74
|
+
if (opts?.projectId)
|
|
75
|
+
rows = rows.filter(r => r.projectId === opts.projectId);
|
|
76
|
+
if (rows.length === 0)
|
|
77
|
+
return null;
|
|
78
|
+
return rows.reduce((latest, row) => (row.startedAt > latest.startedAt ? row : latest));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# 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"}
|