nodebench-mcp 2.15.0 → 2.18.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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/NODEBENCH_AGENTS.md +2 -2
  3. package/README.md +514 -82
  4. package/dist/__tests__/analytics.test.d.ts +11 -0
  5. package/dist/__tests__/analytics.test.js +546 -0
  6. package/dist/__tests__/analytics.test.js.map +1 -0
  7. package/dist/__tests__/architectComplex.test.d.ts +1 -0
  8. package/dist/__tests__/architectComplex.test.js +375 -0
  9. package/dist/__tests__/architectComplex.test.js.map +1 -0
  10. package/dist/__tests__/architectSmoke.test.d.ts +1 -0
  11. package/dist/__tests__/architectSmoke.test.js +92 -0
  12. package/dist/__tests__/architectSmoke.test.js.map +1 -0
  13. package/dist/__tests__/dynamicLoading.test.d.ts +1 -0
  14. package/dist/__tests__/dynamicLoading.test.js +278 -0
  15. package/dist/__tests__/dynamicLoading.test.js.map +1 -0
  16. package/dist/__tests__/evalHarness.test.js +7 -2
  17. package/dist/__tests__/evalHarness.test.js.map +1 -1
  18. package/dist/__tests__/gaiaCapabilityEval.test.js +229 -12
  19. package/dist/__tests__/gaiaCapabilityEval.test.js.map +1 -1
  20. package/dist/__tests__/gaiaCapabilityMediaEval.test.js +194 -109
  21. package/dist/__tests__/gaiaCapabilityMediaEval.test.js.map +1 -1
  22. package/dist/__tests__/helpers/answerMatch.js +22 -22
  23. package/dist/__tests__/presetRealWorldBench.test.js +11 -2
  24. package/dist/__tests__/presetRealWorldBench.test.js.map +1 -1
  25. package/dist/__tests__/tools.test.js +10 -4
  26. package/dist/__tests__/tools.test.js.map +1 -1
  27. package/dist/__tests__/toolsetGatingEval.test.js +12 -4
  28. package/dist/__tests__/toolsetGatingEval.test.js.map +1 -1
  29. package/dist/analytics/index.d.ts +10 -0
  30. package/dist/analytics/index.js +11 -0
  31. package/dist/analytics/index.js.map +1 -0
  32. package/dist/analytics/projectDetector.d.ts +19 -0
  33. package/dist/analytics/projectDetector.js +259 -0
  34. package/dist/analytics/projectDetector.js.map +1 -0
  35. package/dist/analytics/schema.d.ts +57 -0
  36. package/dist/analytics/schema.js +157 -0
  37. package/dist/analytics/schema.js.map +1 -0
  38. package/dist/analytics/smartPreset.d.ts +63 -0
  39. package/dist/analytics/smartPreset.js +300 -0
  40. package/dist/analytics/smartPreset.js.map +1 -0
  41. package/dist/analytics/toolTracker.d.ts +59 -0
  42. package/dist/analytics/toolTracker.js +163 -0
  43. package/dist/analytics/toolTracker.js.map +1 -0
  44. package/dist/analytics/usageStats.d.ts +64 -0
  45. package/dist/analytics/usageStats.js +252 -0
  46. package/dist/analytics/usageStats.js.map +1 -0
  47. package/dist/db.js +359 -321
  48. package/dist/db.js.map +1 -1
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.js +653 -84
  51. package/dist/index.js.map +1 -1
  52. package/dist/tools/architectTools.d.ts +15 -0
  53. package/dist/tools/architectTools.js +304 -0
  54. package/dist/tools/architectTools.js.map +1 -0
  55. package/dist/tools/critterTools.js +14 -14
  56. package/dist/tools/emailTools.d.ts +15 -0
  57. package/dist/tools/emailTools.js +664 -0
  58. package/dist/tools/emailTools.js.map +1 -0
  59. package/dist/tools/metaTools.js +660 -0
  60. package/dist/tools/metaTools.js.map +1 -1
  61. package/dist/tools/parallelAgentTools.js +176 -176
  62. package/dist/tools/patternTools.js +11 -11
  63. package/dist/tools/progressiveDiscoveryTools.d.ts +5 -1
  64. package/dist/tools/progressiveDiscoveryTools.js +113 -21
  65. package/dist/tools/progressiveDiscoveryTools.js.map +1 -1
  66. package/dist/tools/researchWritingTools.js +42 -42
  67. package/dist/tools/rssTools.d.ts +8 -0
  68. package/dist/tools/rssTools.js +833 -0
  69. package/dist/tools/rssTools.js.map +1 -0
  70. package/dist/tools/toolRegistry.d.ts +17 -0
  71. package/dist/tools/toolRegistry.js +236 -17
  72. package/dist/tools/toolRegistry.js.map +1 -1
  73. package/dist/tools/voiceBridgeTools.js +498 -498
  74. package/dist/toolsetRegistry.d.ts +10 -0
  75. package/dist/toolsetRegistry.js +84 -0
  76. package/dist/toolsetRegistry.js.map +1 -0
  77. package/package.json +12 -5
@@ -0,0 +1,59 @@
1
+ /**
2
+ * AnalyticsTracker — Singleton analytics engine for NodeBench MCP
3
+ *
4
+ * Owns the full lifecycle: DB open, tool-call recording, project context,
5
+ * session stats, retention cleanup, and graceful shutdown.
6
+ * index.ts calls tracker.record() instead of inline SQL.
7
+ */
8
+ import Database from 'better-sqlite3';
9
+ export interface TrackerConfig {
10
+ projectPath: string;
11
+ preset: string;
12
+ toolCount: number;
13
+ /** tool name → toolset name lookup */
14
+ toolToToolset: Map<string, string>;
15
+ /** Tools to skip tracking (avoid recursion) */
16
+ skipTools?: Set<string>;
17
+ /** Days to keep analytics data (default 90) */
18
+ retentionDays?: number;
19
+ }
20
+ export interface SessionStats {
21
+ totalCalls: number;
22
+ successCount: number;
23
+ failureCount: number;
24
+ totalDuration: number;
25
+ uniqueTools: number;
26
+ topTools: Array<{
27
+ name: string;
28
+ count: number;
29
+ }>;
30
+ errorRate: number;
31
+ }
32
+ export declare class AnalyticsTracker {
33
+ private db;
34
+ private projectPath;
35
+ private preset;
36
+ private toolToToolset;
37
+ private skipTools;
38
+ private sessionStart;
39
+ private _callCount;
40
+ private _insertStmt;
41
+ private constructor();
42
+ /** Get or create the singleton tracker */
43
+ static init(config: TrackerConfig): AnalyticsTracker;
44
+ /** Get existing instance (returns null if not initialized) */
45
+ static get(): AnalyticsTracker | null;
46
+ /** Underlying DB handle (for stats queries that need it) */
47
+ getDb(): Database.Database;
48
+ /**
49
+ * Record a tool call outcome. Called from the CallToolRequestSchema handler.
50
+ * Returns silently on any error — analytics must never break tool dispatch.
51
+ */
52
+ record(toolName: string, startMs: number, success: boolean, errorMsg: string | null, args?: Record<string, unknown>): void;
53
+ getSessionStats(): SessionStats;
54
+ get callCount(): number;
55
+ close(): void;
56
+ private _initProjectContext;
57
+ }
58
+ /** @deprecated Use AnalyticsTracker.init() instead */
59
+ export declare function initializeProjectContext(db: Database.Database, projectPath: string | undefined, preset: string, toolCount?: number): void;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * AnalyticsTracker — Singleton analytics engine for NodeBench MCP
3
+ *
4
+ * Owns the full lifecycle: DB open, tool-call recording, project context,
5
+ * session stats, retention cleanup, and graceful shutdown.
6
+ * index.ts calls tracker.record() instead of inline SQL.
7
+ */
8
+ import { getAnalyticsDb, closeAnalyticsDb, updateProjectContext, recordPresetSelection, clearOldRecords, } from './schema.js';
9
+ import { detectProject } from './projectDetector.js';
10
+ // ── Singleton ───────────────────────────────────────────────────────────
11
+ let _instance = null;
12
+ export class AnalyticsTracker {
13
+ db;
14
+ projectPath;
15
+ preset;
16
+ toolToToolset;
17
+ skipTools;
18
+ sessionStart;
19
+ _callCount = 0;
20
+ _insertStmt = null;
21
+ constructor(config) {
22
+ this.db = getAnalyticsDb();
23
+ this.projectPath = config.projectPath;
24
+ this.preset = config.preset;
25
+ this.toolToToolset = config.toolToToolset;
26
+ this.skipTools = config.skipTools ?? new Set();
27
+ this.sessionStart = Date.now();
28
+ // Run retention cleanup once on startup (non-blocking, best-effort)
29
+ const retentionDays = config.retentionDays ?? 90;
30
+ try {
31
+ clearOldRecords(this.db, retentionDays);
32
+ }
33
+ catch { /* ignore */ }
34
+ // Register project context
35
+ this._initProjectContext(config);
36
+ // Pre-compile the insert statement for performance
37
+ this._insertStmt = this.db.prepare(`INSERT INTO tool_usage (tool_name, toolset, timestamp, duration, success, error_message, project_path, preset, args)
38
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
39
+ }
40
+ /** Get or create the singleton tracker */
41
+ static init(config) {
42
+ if (!_instance) {
43
+ _instance = new AnalyticsTracker(config);
44
+ }
45
+ return _instance;
46
+ }
47
+ /** Get existing instance (returns null if not initialized) */
48
+ static get() {
49
+ return _instance;
50
+ }
51
+ /** Underlying DB handle (for stats queries that need it) */
52
+ getDb() {
53
+ return this.db;
54
+ }
55
+ // ── Core: record a tool call ────────────────────────────────────────
56
+ /**
57
+ * Record a tool call outcome. Called from the CallToolRequestSchema handler.
58
+ * Returns silently on any error — analytics must never break tool dispatch.
59
+ */
60
+ record(toolName, startMs, success, errorMsg, args) {
61
+ if (this.skipTools.has(toolName))
62
+ return;
63
+ try {
64
+ const toolset = this.toolToToolset.get(toolName) ?? 'unknown';
65
+ this._insertStmt.run(toolName, toolset, startMs, Date.now() - startMs, success ? 1 : 0, errorMsg, this.projectPath, this.preset, args ? JSON.stringify(args) : null);
66
+ this._callCount++;
67
+ }
68
+ catch { /* never break tool dispatch */ }
69
+ }
70
+ // ── Session stats ───────────────────────────────────────────────────
71
+ getSessionStats() {
72
+ const stmt = this.db.prepare(`
73
+ SELECT
74
+ COUNT(*) as totalCalls,
75
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successCount,
76
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failureCount,
77
+ SUM(duration) as totalDuration,
78
+ COUNT(DISTINCT tool_name) as uniqueTools
79
+ FROM tool_usage
80
+ WHERE project_path = ? AND timestamp >= ?
81
+ `);
82
+ const r = stmt.get(this.projectPath, this.sessionStart);
83
+ const topStmt = this.db.prepare(`
84
+ SELECT tool_name as name, COUNT(*) as count
85
+ FROM tool_usage
86
+ WHERE project_path = ? AND timestamp >= ?
87
+ GROUP BY tool_name ORDER BY count DESC LIMIT 5
88
+ `);
89
+ const topTools = topStmt.all(this.projectPath, this.sessionStart);
90
+ const total = r.totalCalls || 0;
91
+ const failures = r.failureCount || 0;
92
+ return {
93
+ totalCalls: total,
94
+ successCount: r.successCount || 0,
95
+ failureCount: failures,
96
+ totalDuration: r.totalDuration || 0,
97
+ uniqueTools: r.uniqueTools || 0,
98
+ topTools,
99
+ errorRate: total > 0 ? failures / total : 0,
100
+ };
101
+ }
102
+ get callCount() {
103
+ return this._callCount;
104
+ }
105
+ // ── Lifecycle ───────────────────────────────────────────────────────
106
+ close() {
107
+ try {
108
+ closeAnalyticsDb(this.db);
109
+ }
110
+ catch { /* ignore */ }
111
+ _instance = null;
112
+ }
113
+ // ── Private helpers ─────────────────────────────────────────────────
114
+ _initProjectContext(config) {
115
+ const context = detectProject(config.projectPath);
116
+ const now = Date.now();
117
+ updateProjectContext(this.db, {
118
+ projectPath: config.projectPath,
119
+ projectType: context.projectType,
120
+ detectedAt: now,
121
+ lastSeen: now,
122
+ language: context.language,
123
+ framework: context.framework,
124
+ hasTests: context.hasTests,
125
+ hasCI: context.hasCI,
126
+ hasDocs: context.hasDocs,
127
+ fileCount: context.fileCount,
128
+ });
129
+ recordPresetSelection(this.db, {
130
+ projectPath: config.projectPath,
131
+ preset: config.preset,
132
+ toolsetCount: config.toolCount,
133
+ selectedAt: now,
134
+ selectionReason: 'manual',
135
+ });
136
+ }
137
+ }
138
+ // ── Convenience re-exports for backward compat ────────────────────────
139
+ /** @deprecated Use AnalyticsTracker.init() instead */
140
+ export function initializeProjectContext(db, projectPath = process.cwd(), preset, toolCount = 44) {
141
+ const context = detectProject(projectPath);
142
+ const now = Date.now();
143
+ updateProjectContext(db, {
144
+ projectPath,
145
+ projectType: context.projectType,
146
+ detectedAt: now,
147
+ lastSeen: now,
148
+ language: context.language,
149
+ framework: context.framework,
150
+ hasTests: context.hasTests,
151
+ hasCI: context.hasCI,
152
+ hasDocs: context.hasDocs,
153
+ fileCount: context.fileCount,
154
+ });
155
+ recordPresetSelection(db, {
156
+ projectPath,
157
+ preset,
158
+ toolsetCount: toolCount,
159
+ selectedAt: now,
160
+ selectionReason: 'manual',
161
+ });
162
+ }
163
+ //# sourceMappingURL=toolTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolTracker.js","sourceRoot":"","sources":["../../src/analytics/toolTracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Usage Statistics Aggregation
3
+ *
4
+ * Aggregates tool usage data to generate insights and recommendations.
5
+ */
6
+ import Database from 'better-sqlite3';
7
+ export interface ToolUsageStats {
8
+ toolName: string;
9
+ toolset: string;
10
+ callCount: number;
11
+ successCount: number;
12
+ failureCount: number;
13
+ avgDuration: number;
14
+ lastUsed: number;
15
+ firstUsed: number;
16
+ }
17
+ export interface ToolsetUsageStats {
18
+ toolset: string;
19
+ toolCount: number;
20
+ totalCalls: number;
21
+ uniqueToolsUsed: number;
22
+ avgCallsPerTool: number;
23
+ }
24
+ export interface ProjectUsageSummary {
25
+ projectPath: string;
26
+ totalCalls: number;
27
+ uniqueToolsUsed: number;
28
+ totalDuration: number;
29
+ avgDuration: number;
30
+ successRate: number;
31
+ mostUsedTool: string;
32
+ mostUsedToolset: string;
33
+ topTools: Array<{
34
+ name: string;
35
+ count: number;
36
+ }>;
37
+ topToolsets: Array<{
38
+ name: string;
39
+ count: number;
40
+ }>;
41
+ }
42
+ export interface UsageTrend {
43
+ date: string;
44
+ callCount: number;
45
+ uniqueTools: number;
46
+ avgDuration: number;
47
+ }
48
+ export declare function getToolUsageStats(db: Database.Database, projectPath: string, days?: number): ToolUsageStats[];
49
+ export declare function getToolsetUsageStats(db: Database.Database, projectPath: string, days?: number): ToolsetUsageStats[];
50
+ export declare function getProjectUsageSummary(db: Database.Database, projectPath: string, days?: number): ProjectUsageSummary | null;
51
+ export declare function getUsageTrend(db: Database.Database, projectPath: string, days?: number): UsageTrend[];
52
+ export declare function getUnusedTools(db: Database.Database, projectPath: string, availableTools: string[], days?: number): string[];
53
+ export declare function getFrequentlyFailingTools(db: Database.Database, projectPath: string, days?: number, minFailures?: number): Array<{
54
+ toolName: string;
55
+ failureCount: number;
56
+ lastError: string | null;
57
+ }>;
58
+ export declare function getAllProjects(db: Database.Database): Array<{
59
+ projectPath: string;
60
+ lastSeen: number;
61
+ }>;
62
+ export declare function exportUsageStats(db: Database.Database, projectPath: string, days?: number): string;
63
+ export declare function getCachedProjectSummary(db: Database.Database, projectPath: string, days?: number): ProjectUsageSummary | null;
64
+ export declare function formatStatsDisplay(summary: ProjectUsageSummary, projectPath: string): string;
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Usage Statistics Aggregation
3
+ *
4
+ * Aggregates tool usage data to generate insights and recommendations.
5
+ */
6
+ import { getCachedStats, setCachedStats } from './schema.js';
7
+ export function getToolUsageStats(db, projectPath, days = 30) {
8
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
9
+ const stmt = db.prepare(`
10
+ SELECT
11
+ tool_name as toolName,
12
+ toolset,
13
+ COUNT(*) as callCount,
14
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successCount,
15
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failureCount,
16
+ AVG(duration) as avgDuration,
17
+ MAX(timestamp) as lastUsed,
18
+ MIN(timestamp) as firstUsed
19
+ FROM tool_usage
20
+ WHERE project_path = ? AND timestamp >= ?
21
+ GROUP BY tool_name, toolset
22
+ ORDER BY callCount DESC
23
+ `);
24
+ return stmt.all(projectPath, cutoff);
25
+ }
26
+ export function getToolsetUsageStats(db, projectPath, days = 30) {
27
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
28
+ const stmt = db.prepare(`
29
+ SELECT
30
+ toolset,
31
+ COUNT(DISTINCT tool_name) as toolCount,
32
+ SUM(call_count) as totalCalls,
33
+ COUNT(DISTINCT tool_name) as uniqueToolsUsed,
34
+ CAST(SUM(call_count) AS FLOAT) / COUNT(DISTINCT tool_name) as avgCallsPerTool
35
+ FROM (
36
+ SELECT tool_name, toolset, COUNT(*) as call_count
37
+ FROM tool_usage
38
+ WHERE project_path = ? AND timestamp >= ?
39
+ GROUP BY tool_name, toolset
40
+ )
41
+ GROUP BY toolset
42
+ ORDER BY totalCalls DESC
43
+ `);
44
+ return stmt.all(projectPath, cutoff);
45
+ }
46
+ export function getProjectUsageSummary(db, projectPath, days = 30) {
47
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
48
+ const stmt = db.prepare(`
49
+ SELECT
50
+ COUNT(*) as totalCalls,
51
+ COUNT(DISTINCT tool_name) as uniqueToolsUsed,
52
+ SUM(duration) as totalDuration,
53
+ AVG(duration) as avgDuration,
54
+ CAST(SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) AS FLOAT) / COUNT(*) as successRate
55
+ FROM tool_usage
56
+ WHERE project_path = ? AND timestamp >= ?
57
+ `);
58
+ const summary = stmt.get(projectPath, cutoff);
59
+ if (!summary || summary.totalCalls === 0)
60
+ return null;
61
+ // Get most used tool
62
+ const mostUsedToolStmt = db.prepare(`
63
+ SELECT tool_name, COUNT(*) as count
64
+ FROM tool_usage
65
+ WHERE project_path = ? AND timestamp >= ?
66
+ GROUP BY tool_name
67
+ ORDER BY count DESC
68
+ LIMIT 1
69
+ `);
70
+ const mostUsedTool = mostUsedToolStmt.get(projectPath, cutoff);
71
+ // Get most used toolset
72
+ const mostUsedToolsetStmt = db.prepare(`
73
+ SELECT toolset, COUNT(*) as count
74
+ FROM tool_usage
75
+ WHERE project_path = ? AND timestamp >= ?
76
+ GROUP BY toolset
77
+ ORDER BY count DESC
78
+ LIMIT 1
79
+ `);
80
+ const mostUsedToolset = mostUsedToolsetStmt.get(projectPath, cutoff);
81
+ // Get top 5 tools
82
+ const topToolsStmt = db.prepare(`
83
+ SELECT tool_name as name, COUNT(*) as count
84
+ FROM tool_usage
85
+ WHERE project_path = ? AND timestamp >= ?
86
+ GROUP BY tool_name
87
+ ORDER BY count DESC
88
+ LIMIT 5
89
+ `);
90
+ const topTools = topToolsStmt.all(projectPath, cutoff);
91
+ // Get top 5 toolsets
92
+ const topToolsetsStmt = db.prepare(`
93
+ SELECT toolset as name, COUNT(*) as count
94
+ FROM tool_usage
95
+ WHERE project_path = ? AND timestamp >= ?
96
+ GROUP BY toolset
97
+ ORDER BY count DESC
98
+ LIMIT 5
99
+ `);
100
+ const topToolsets = topToolsetsStmt.all(projectPath, cutoff);
101
+ return {
102
+ projectPath,
103
+ totalCalls: summary.totalCalls,
104
+ uniqueToolsUsed: summary.uniqueToolsUsed,
105
+ totalDuration: summary.totalDuration,
106
+ avgDuration: summary.avgDuration,
107
+ successRate: summary.successRate,
108
+ mostUsedTool: mostUsedTool?.tool_name || 'N/A',
109
+ mostUsedToolset: mostUsedToolset?.toolset || 'N/A',
110
+ topTools,
111
+ topToolsets,
112
+ };
113
+ }
114
+ export function getUsageTrend(db, projectPath, days = 30) {
115
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
116
+ const stmt = db.prepare(`
117
+ SELECT
118
+ DATE(timestamp / 1000, 'unixepoch') as date,
119
+ COUNT(*) as callCount,
120
+ COUNT(DISTINCT tool_name) as uniqueTools,
121
+ AVG(duration) as avgDuration
122
+ FROM tool_usage
123
+ WHERE project_path = ? AND timestamp >= ?
124
+ GROUP BY date
125
+ ORDER BY date ASC
126
+ `);
127
+ return stmt.all(projectPath, cutoff);
128
+ }
129
+ export function getUnusedTools(db, projectPath, availableTools, days = 30) {
130
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
131
+ const usedToolsStmt = db.prepare(`
132
+ SELECT DISTINCT tool_name
133
+ FROM tool_usage
134
+ WHERE project_path = ? AND timestamp >= ?
135
+ `);
136
+ const usedTools = usedToolsStmt.all(projectPath, cutoff);
137
+ const usedToolNames = new Set(usedTools.map(t => t.tool_name));
138
+ return availableTools.filter(tool => !usedToolNames.has(tool));
139
+ }
140
+ export function getFrequentlyFailingTools(db, projectPath, days = 30, minFailures = 3) {
141
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
142
+ const stmt = db.prepare(`
143
+ SELECT
144
+ tool_name as toolName,
145
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failureCount,
146
+ MAX(error_message) as lastError
147
+ FROM tool_usage
148
+ WHERE project_path = ? AND timestamp >= ?
149
+ GROUP BY tool_name
150
+ HAVING failureCount >= ?
151
+ ORDER BY failureCount DESC
152
+ `);
153
+ return stmt.all(projectPath, cutoff, minFailures);
154
+ }
155
+ export function getAllProjects(db) {
156
+ const stmt = db.prepare(`
157
+ SELECT DISTINCT project_path as projectPath, MAX(timestamp) as lastSeen
158
+ FROM tool_usage
159
+ GROUP BY project_path
160
+ ORDER BY lastSeen DESC
161
+ `);
162
+ return stmt.all();
163
+ }
164
+ export function exportUsageStats(db, projectPath, days = 30) {
165
+ const summary = getProjectUsageSummary(db, projectPath, days);
166
+ const toolStats = getToolUsageStats(db, projectPath, days);
167
+ const toolsetStats = getToolsetUsageStats(db, projectPath, days);
168
+ const trend = getUsageTrend(db, projectPath, days);
169
+ const failingTools = getFrequentlyFailingTools(db, projectPath, days);
170
+ return JSON.stringify({
171
+ projectPath,
172
+ period: `${days} days`,
173
+ summary,
174
+ toolStats,
175
+ toolsetStats,
176
+ trend,
177
+ failingTools,
178
+ exportedAt: new Date().toISOString(),
179
+ }, null, 2);
180
+ }
181
+ // ── Cached wrapper ──────────────────────────────────────────────────────
182
+ const SUMMARY_CACHE_TTL = 300; // 5 minutes in seconds
183
+ export function getCachedProjectSummary(db, projectPath, days = 30) {
184
+ const cacheKey = `summary_${days}d`;
185
+ const cached = getCachedStats(db, projectPath, cacheKey);
186
+ if (cached) {
187
+ try {
188
+ return JSON.parse(cached);
189
+ }
190
+ catch { /* fall through */ }
191
+ }
192
+ const summary = getProjectUsageSummary(db, projectPath, days);
193
+ if (summary) {
194
+ setCachedStats(db, {
195
+ projectPath,
196
+ cacheKey,
197
+ stats: JSON.stringify(summary),
198
+ computedAt: Date.now(),
199
+ ttl: SUMMARY_CACHE_TTL,
200
+ });
201
+ }
202
+ return summary;
203
+ }
204
+ // ── Rich formatted display ──────────────────────────────────────────────
205
+ function fmtDuration(ms) {
206
+ if (ms < 1000)
207
+ return `${Math.round(ms)}ms`;
208
+ if (ms < 60_000)
209
+ return `${(ms / 1000).toFixed(1)}s`;
210
+ return `${(ms / 60_000).toFixed(1)}m`;
211
+ }
212
+ function fmtPercent(n) {
213
+ return `${(n * 100).toFixed(1)}%`;
214
+ }
215
+ function bar(value, max, width = 20) {
216
+ const filled = max > 0 ? Math.round((value / max) * width) : 0;
217
+ return '\u2588'.repeat(filled) + '\u2591'.repeat(width - filled);
218
+ }
219
+ export function formatStatsDisplay(summary, projectPath) {
220
+ const lines = [];
221
+ const projectName = projectPath.split(/[\\/]/).pop() || projectPath;
222
+ lines.push(`\n=== Usage Analytics: ${projectName} (last 30 days) ===\n`);
223
+ // Overview
224
+ lines.push(` Total calls: ${summary.totalCalls.toLocaleString()}`);
225
+ lines.push(` Unique tools: ${summary.uniqueToolsUsed}`);
226
+ lines.push(` Success rate: ${fmtPercent(summary.successRate)}`);
227
+ lines.push(` Avg duration: ${fmtDuration(summary.avgDuration)}`);
228
+ lines.push(` Total time: ${fmtDuration(summary.totalDuration)}`);
229
+ // Top tools
230
+ if (summary.topTools.length > 0) {
231
+ lines.push(`\n--- Top Tools ---`);
232
+ const maxCount = summary.topTools[0].count;
233
+ for (const t of summary.topTools) {
234
+ const pct = summary.totalCalls > 0 ? (t.count / summary.totalCalls) : 0;
235
+ lines.push(` ${bar(t.count, maxCount, 16)} ${t.name} (${t.count} calls, ${fmtPercent(pct)})`);
236
+ }
237
+ }
238
+ // Top toolsets
239
+ if (summary.topToolsets.length > 0) {
240
+ lines.push(`\n--- Top Toolsets ---`);
241
+ const maxCount = summary.topToolsets[0].count;
242
+ for (const t of summary.topToolsets) {
243
+ const pct = summary.totalCalls > 0 ? (t.count / summary.totalCalls) : 0;
244
+ lines.push(` ${bar(t.count, maxCount, 16)} ${t.name} (${t.count} calls, ${fmtPercent(pct)})`);
245
+ }
246
+ }
247
+ lines.push(`\n Most used tool: ${summary.mostUsedTool}`);
248
+ lines.push(` Most used toolset: ${summary.mostUsedToolset}`);
249
+ lines.push('');
250
+ return lines.join('\n');
251
+ }
252
+ //# sourceMappingURL=usageStats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usageStats.js","sourceRoot":"","sources":["../../src/analytics/usageStats.ts"],"names":[],"mappings":"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"}