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.
- package/LICENSE +21 -0
- package/NODEBENCH_AGENTS.md +2 -2
- package/README.md +514 -82
- package/dist/__tests__/analytics.test.d.ts +11 -0
- package/dist/__tests__/analytics.test.js +546 -0
- package/dist/__tests__/analytics.test.js.map +1 -0
- package/dist/__tests__/architectComplex.test.d.ts +1 -0
- package/dist/__tests__/architectComplex.test.js +375 -0
- package/dist/__tests__/architectComplex.test.js.map +1 -0
- package/dist/__tests__/architectSmoke.test.d.ts +1 -0
- package/dist/__tests__/architectSmoke.test.js +92 -0
- package/dist/__tests__/architectSmoke.test.js.map +1 -0
- package/dist/__tests__/dynamicLoading.test.d.ts +1 -0
- package/dist/__tests__/dynamicLoading.test.js +278 -0
- package/dist/__tests__/dynamicLoading.test.js.map +1 -0
- package/dist/__tests__/evalHarness.test.js +7 -2
- package/dist/__tests__/evalHarness.test.js.map +1 -1
- package/dist/__tests__/gaiaCapabilityEval.test.js +229 -12
- package/dist/__tests__/gaiaCapabilityEval.test.js.map +1 -1
- package/dist/__tests__/gaiaCapabilityMediaEval.test.js +194 -109
- package/dist/__tests__/gaiaCapabilityMediaEval.test.js.map +1 -1
- package/dist/__tests__/helpers/answerMatch.js +22 -22
- package/dist/__tests__/presetRealWorldBench.test.js +11 -2
- package/dist/__tests__/presetRealWorldBench.test.js.map +1 -1
- package/dist/__tests__/tools.test.js +10 -4
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/__tests__/toolsetGatingEval.test.js +12 -4
- package/dist/__tests__/toolsetGatingEval.test.js.map +1 -1
- package/dist/analytics/index.d.ts +10 -0
- package/dist/analytics/index.js +11 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/projectDetector.d.ts +19 -0
- package/dist/analytics/projectDetector.js +259 -0
- package/dist/analytics/projectDetector.js.map +1 -0
- package/dist/analytics/schema.d.ts +57 -0
- package/dist/analytics/schema.js +157 -0
- package/dist/analytics/schema.js.map +1 -0
- package/dist/analytics/smartPreset.d.ts +63 -0
- package/dist/analytics/smartPreset.js +300 -0
- package/dist/analytics/smartPreset.js.map +1 -0
- package/dist/analytics/toolTracker.d.ts +59 -0
- package/dist/analytics/toolTracker.js +163 -0
- package/dist/analytics/toolTracker.js.map +1 -0
- package/dist/analytics/usageStats.d.ts +64 -0
- package/dist/analytics/usageStats.js +252 -0
- package/dist/analytics/usageStats.js.map +1 -0
- package/dist/db.js +359 -321
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +653 -84
- package/dist/index.js.map +1 -1
- package/dist/tools/architectTools.d.ts +15 -0
- package/dist/tools/architectTools.js +304 -0
- package/dist/tools/architectTools.js.map +1 -0
- package/dist/tools/critterTools.js +14 -14
- package/dist/tools/emailTools.d.ts +15 -0
- package/dist/tools/emailTools.js +664 -0
- package/dist/tools/emailTools.js.map +1 -0
- package/dist/tools/metaTools.js +660 -0
- package/dist/tools/metaTools.js.map +1 -1
- package/dist/tools/parallelAgentTools.js +176 -176
- package/dist/tools/patternTools.js +11 -11
- package/dist/tools/progressiveDiscoveryTools.d.ts +5 -1
- package/dist/tools/progressiveDiscoveryTools.js +113 -21
- package/dist/tools/progressiveDiscoveryTools.js.map +1 -1
- package/dist/tools/researchWritingTools.js +42 -42
- package/dist/tools/rssTools.d.ts +8 -0
- package/dist/tools/rssTools.js +833 -0
- package/dist/tools/rssTools.js.map +1 -0
- package/dist/tools/toolRegistry.d.ts +17 -0
- package/dist/tools/toolRegistry.js +236 -17
- package/dist/tools/toolRegistry.js.map +1 -1
- package/dist/tools/voiceBridgeTools.js +498 -498
- package/dist/toolsetRegistry.d.ts +10 -0
- package/dist/toolsetRegistry.js +84 -0
- package/dist/toolsetRegistry.js.map +1 -0
- 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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"}
|