agentlytics 0.1.13 → 0.1.15
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/README.md +5 -3
- package/editors/claude.js +3 -1
- package/editors/codex.js +3 -0
- package/editors/commandcode.js +3 -1
- package/editors/copilot.js +3 -1
- package/editors/cursor-agent.js +3 -1
- package/editors/cursor.js +3 -1
- package/editors/gemini.js +3 -1
- package/editors/goose.js +287 -0
- package/editors/index.js +10 -2
- package/editors/kiro.js +296 -0
- package/editors/opencode.js +149 -68
- package/editors/vscode.js +3 -1
- package/editors/windsurf.js +195 -49
- package/editors/zed.js +45 -20
- package/index.js +7 -20
- package/package.json +1 -1
- package/ui/src/components/EditorIcon.jsx +4 -0
- package/ui/src/lib/constants.js +4 -0
package/editors/windsurf.js
CHANGED
|
@@ -1,20 +1,108 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const os = require('os');
|
|
4
1
|
const { execSync } = require('child_process');
|
|
5
|
-
const http = require('http');
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Windsurf-family variants: Windsurf, Windsurf Next, Antigravity
|
|
3
|
+
// Windsurf-family variants: Windsurf, Antigravity
|
|
10
4
|
const VARIANTS = [
|
|
11
5
|
{ id: 'windsurf', matchKey: 'ide', matchVal: 'windsurf', https: false },
|
|
12
6
|
{ id: 'windsurf-next', matchKey: 'ide', matchVal: 'windsurf-next', https: false },
|
|
13
7
|
{ id: 'antigravity', matchKey: 'appDataDir', matchVal: 'antigravity', https: true },
|
|
14
8
|
];
|
|
15
9
|
|
|
10
|
+
// Antigravity model ID to friendly name mapping
|
|
11
|
+
const ANTIGRAVITY_MODEL_MAP = {
|
|
12
|
+
'MODEL_PLACEHOLDER_M1': 'claude-3-5-sonnet-20241022',
|
|
13
|
+
'MODEL_PLACEHOLDER_M2': 'claude-3-5-sonnet-20241022',
|
|
14
|
+
'MODEL_PLACEHOLDER_M3': 'claude-3-5-sonnet-20241022',
|
|
15
|
+
'MODEL_PLACEHOLDER_M4': 'claude-3-5-haiku-20241022',
|
|
16
|
+
'MODEL_PLACEHOLDER_M5': 'claude-3-5-haiku-20241022',
|
|
17
|
+
'MODEL_PLACEHOLDER_M6': 'claude-3-5-haiku-20241022',
|
|
18
|
+
'MODEL_PLACEHOLDER_M7': 'claude-3-5-sonnet-20241022',
|
|
19
|
+
'MODEL_PLACEHOLDER_M8': 'claude-3.5-sonnet',
|
|
20
|
+
'MODEL_PLACEHOLDER_M9': 'claude-3.5-sonnet',
|
|
21
|
+
'MODEL_PLACEHOLDER_M10': 'claude-3.5-sonnet',
|
|
22
|
+
'MODEL_CLAUDE_4_5_SONNET': 'claude-4.5-sonnet',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function normalizeAntigravityModel(modelId) {
|
|
26
|
+
if (!modelId) return null;
|
|
27
|
+
return ANTIGRAVITY_MODEL_MAP[modelId] || modelId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ============================================================
|
|
31
|
+
// Cross-platform process utilities
|
|
32
|
+
// ============================================================
|
|
33
|
+
|
|
34
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
35
|
+
|
|
36
|
+
function getProcessList() {
|
|
37
|
+
try {
|
|
38
|
+
if (IS_WINDOWS) {
|
|
39
|
+
// wmic provides CSV-formatted process data
|
|
40
|
+
const output = execSync('wmic process get CommandLine,ProcessId /format:csv', {
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
43
|
+
});
|
|
44
|
+
// Parse CSV: skip header, split by comma
|
|
45
|
+
const lines = output.split('\n').slice(1);
|
|
46
|
+
return lines.map(line => {
|
|
47
|
+
const parts = line.split(',');
|
|
48
|
+
if (parts.length < 2) return null;
|
|
49
|
+
const commandLine = parts.slice(0, -1).join(',').trim().replace(/^"|"$/g, '');
|
|
50
|
+
const pid = parts[parts.length - 1].trim();
|
|
51
|
+
return { commandLine, pid };
|
|
52
|
+
}).filter(Boolean);
|
|
53
|
+
} else {
|
|
54
|
+
// ps aux on Unix-like systems
|
|
55
|
+
const output = execSync('ps aux', { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
56
|
+
return output.split('\n').slice(1).map(line => {
|
|
57
|
+
const parts = line.trim().split(/\s+/);
|
|
58
|
+
if (parts.length < 11) return null;
|
|
59
|
+
const pid = parts[1];
|
|
60
|
+
const commandLine = parts.slice(10).join(' ');
|
|
61
|
+
return { commandLine, pid };
|
|
62
|
+
}).filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
} catch { return []; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getListeningPorts(pid) {
|
|
68
|
+
try {
|
|
69
|
+
if (IS_WINDOWS) {
|
|
70
|
+
// netstat -ano shows PID in the last column
|
|
71
|
+
const output = execSync(`netstat -ano | findstr ${pid}`, {
|
|
72
|
+
encoding: 'utf-8',
|
|
73
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
74
|
+
});
|
|
75
|
+
const ports = [];
|
|
76
|
+
for (const line of output.split('\n')) {
|
|
77
|
+
// Match: 127.0.0.1:PORT ... LISTENING PID
|
|
78
|
+
// Check if line ends with the PID we're looking for
|
|
79
|
+
if (!line.trim().endsWith(pid)) continue;
|
|
80
|
+
const match = line.match(/127\.0\.0\.1:(\d+).*LISTENING/);
|
|
81
|
+
if (match) {
|
|
82
|
+
ports.push(parseInt(match[1]));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return ports;
|
|
86
|
+
} else {
|
|
87
|
+
// lsof on Unix-like systems
|
|
88
|
+
const output = execSync(`lsof -i TCP -P -n -a -p ${pid} 2>/dev/null`, {
|
|
89
|
+
encoding: 'utf-8',
|
|
90
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
91
|
+
});
|
|
92
|
+
const ports = [];
|
|
93
|
+
for (const line of output.split('\n')) {
|
|
94
|
+
const match = line.match(/TCP\s+127\.0\.0\.1:(\d+)\s+\(LISTEN\)/);
|
|
95
|
+
if (match) {
|
|
96
|
+
ports.push(parseInt(match[1]));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return ports;
|
|
100
|
+
}
|
|
101
|
+
} catch { return []; }
|
|
102
|
+
}
|
|
103
|
+
|
|
16
104
|
// ============================================================
|
|
17
|
-
// Find running Windsurf language server (port + CSRF token)
|
|
105
|
+
// Find running Windsurf/Antigravity language server (port + CSRF token)
|
|
18
106
|
// ============================================================
|
|
19
107
|
|
|
20
108
|
let _lsCache = null;
|
|
@@ -22,32 +110,71 @@ let _lsCache = null;
|
|
|
22
110
|
function findLanguageServers() {
|
|
23
111
|
if (_lsCache) return _lsCache;
|
|
24
112
|
_lsCache = [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const portMatch = l.match(/TCP\s+127\.0\.0\.1:(\d+)\s+\(LISTEN\)/);
|
|
44
|
-
if (portMatch) {
|
|
45
|
-
_lsCache.push({ ide, appDataDir, port: parseInt(portMatch[1]), csrf, pid });
|
|
46
|
-
}
|
|
113
|
+
|
|
114
|
+
// Language server executable name varies by platform
|
|
115
|
+
const serverProcessName = IS_WINDOWS
|
|
116
|
+
? 'language_server_windows'
|
|
117
|
+
: process.platform === 'darwin'
|
|
118
|
+
? 'language_server_macos'
|
|
119
|
+
: 'language_server_linux';
|
|
120
|
+
|
|
121
|
+
// On macOS/Linux, also check env vars for WINDSURF_CSRF_TOKEN (newer Windsurf Next passes CSRF via env, not CLI arg)
|
|
122
|
+
const envCsrfByPid = {};
|
|
123
|
+
if (!IS_WINDOWS) {
|
|
124
|
+
try {
|
|
125
|
+
const psEnv = execSync('ps eww -A', { encoding: 'utf-8', maxBuffer: 2 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
126
|
+
for (const envLine of psEnv.split('\n')) {
|
|
127
|
+
const envCsrf = envLine.match(/WINDSURF_CSRF_TOKEN=(\S+)/);
|
|
128
|
+
if (envCsrf) {
|
|
129
|
+
const envPid = envLine.match(/^\s*(\d+)/);
|
|
130
|
+
if (envPid) envCsrfByPid[envPid[1]] = envCsrf[1];
|
|
47
131
|
}
|
|
48
|
-
}
|
|
132
|
+
}
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const proc of getProcessList()) {
|
|
137
|
+
const { commandLine, pid } = proc;
|
|
138
|
+
if (!commandLine.includes(serverProcessName)) continue;
|
|
139
|
+
|
|
140
|
+
const csrfMatch = commandLine.match(/--csrf_token\s+(\S+)/);
|
|
141
|
+
const ideMatch = commandLine.match(/--ide_name\s+(\S+)/);
|
|
142
|
+
const appDirMatch = commandLine.match(/--app_data_dir\s+(\S+)/);
|
|
143
|
+
|
|
144
|
+
// Try CLI arg first, then env var fallback
|
|
145
|
+
const csrf = csrfMatch ? csrfMatch[1] : envCsrfByPid[pid] || null;
|
|
146
|
+
if (!csrf) continue;
|
|
147
|
+
|
|
148
|
+
const ide = ideMatch ? ideMatch[1] : null;
|
|
149
|
+
const appDataDir = appDirMatch ? appDirMatch[1] : null;
|
|
150
|
+
|
|
151
|
+
// Antigravity has a separate extension server CSRF token
|
|
152
|
+
const extCsrfMatch = commandLine.match(/--extension_server_csrf_token\s+(\S+)/);
|
|
153
|
+
|
|
154
|
+
// Check for explicit server port (Antigravity uses --server_port)
|
|
155
|
+
const serverPortMatch = commandLine.match(/--server_port\s+(\d+)/);
|
|
156
|
+
|
|
157
|
+
// Find actual listening ports for this process
|
|
158
|
+
const ports = getListeningPorts(pid);
|
|
159
|
+
if (ports.length === 0) continue;
|
|
160
|
+
|
|
161
|
+
// Use explicit server_port if available, otherwise use lowest port
|
|
162
|
+
let port;
|
|
163
|
+
if (serverPortMatch) {
|
|
164
|
+
port = parseInt(serverPortMatch[1], 10);
|
|
165
|
+
if (!ports.includes(port)) {
|
|
166
|
+
port = Math.min(...ports);
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
port = Math.min(...ports);
|
|
49
170
|
}
|
|
50
|
-
|
|
171
|
+
|
|
172
|
+
if (ide || appDataDir) {
|
|
173
|
+
const isHttps = appDataDir?.includes('antigravity');
|
|
174
|
+
_lsCache.push({ ide, appDataDir, port, csrf, pid, extCsrf: extCsrfMatch ? extCsrfMatch[1] : null, isHttps });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
51
178
|
return _lsCache;
|
|
52
179
|
}
|
|
53
180
|
|
|
@@ -55,10 +182,9 @@ function getLsForVariant(variant) {
|
|
|
55
182
|
const servers = findLanguageServers();
|
|
56
183
|
let matches;
|
|
57
184
|
if (variant.matchKey === 'appDataDir') {
|
|
58
|
-
matches = servers.filter(s => s.appDataDir
|
|
185
|
+
matches = servers.filter(s => s.appDataDir?.includes(variant.matchVal));
|
|
59
186
|
} else {
|
|
60
|
-
|
|
61
|
-
matches = servers.filter(s => s.ide === variant.matchVal && !s.appDataDir);
|
|
187
|
+
matches = servers.filter(s => s.ide === variant.matchVal);
|
|
62
188
|
}
|
|
63
189
|
return matches.length > 0 ? matches[0] : null;
|
|
64
190
|
}
|
|
@@ -67,16 +193,20 @@ function getLsForVariant(variant) {
|
|
|
67
193
|
// Connect protocol HTTP client for language server RPC
|
|
68
194
|
// ============================================================
|
|
69
195
|
|
|
70
|
-
function callRpc(port, csrf, method, body,
|
|
196
|
+
function callRpc(port, csrf, method, body, isHttps = false, extCsrf = null, useMainCsrf = false) {
|
|
71
197
|
const data = JSON.stringify(body || {});
|
|
72
|
-
const scheme =
|
|
198
|
+
const scheme = isHttps ? 'https' : 'http';
|
|
73
199
|
const url = `${scheme}://127.0.0.1:${port}/exa.language_server_pb.LanguageServerService/${method}`;
|
|
74
|
-
const insecure =
|
|
200
|
+
const insecure = isHttps ? '-k ' : '';
|
|
201
|
+
|
|
202
|
+
// For Antigravity, use main CSRF. For Windsurf, use extension CSRF if available.
|
|
203
|
+
const actualCsrf = useMainCsrf ? csrf : (extCsrf || csrf);
|
|
204
|
+
|
|
75
205
|
try {
|
|
76
206
|
const result = execSync(
|
|
77
207
|
`curl -s ${insecure}-X POST ${JSON.stringify(url)} ` +
|
|
78
208
|
`-H "Content-Type: application/json" ` +
|
|
79
|
-
`-H "x-codeium-csrf-token: ${
|
|
209
|
+
`-H "x-codeium-csrf-token: ${actualCsrf}" ` +
|
|
80
210
|
`-d ${JSON.stringify(data)} ` +
|
|
81
211
|
`--max-time 10`,
|
|
82
212
|
{ encoding: 'utf-8', maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
@@ -99,12 +229,17 @@ function getChats() {
|
|
|
99
229
|
const ls = getLsForVariant(variant);
|
|
100
230
|
if (!ls) continue;
|
|
101
231
|
|
|
102
|
-
|
|
232
|
+
// Antigravity uses main CSRF, Windsurf uses extension CSRF
|
|
233
|
+
const useMainCsrf = variant.id === 'antigravity';
|
|
234
|
+
const resp = callRpc(ls.port, ls.csrf, 'GetAllCascadeTrajectories', {}, ls.isHttps, ls.extCsrf, useMainCsrf);
|
|
103
235
|
if (!resp || !resp.trajectorySummaries) continue;
|
|
104
236
|
|
|
105
237
|
for (const [cascadeId, summary] of Object.entries(resp.trajectorySummaries)) {
|
|
106
238
|
const ws = (summary.workspaces || [])[0];
|
|
107
239
|
const folder = ws?.workspaceFolderAbsoluteUri?.replace('file://', '') || null;
|
|
240
|
+
const rawModel = summary.lastGeneratorModelUid;
|
|
241
|
+
// Normalize Antigravity models so they show correctly in dashboard
|
|
242
|
+
const normalizedModel = variant.id === 'antigravity' && rawModel ? normalizeAntigravityModel(rawModel) : rawModel;
|
|
108
243
|
chats.push({
|
|
109
244
|
source: variant.id,
|
|
110
245
|
composerId: cascadeId,
|
|
@@ -117,9 +252,11 @@ function getChats() {
|
|
|
117
252
|
bubbleCount: summary.stepCount || 0,
|
|
118
253
|
_port: ls.port,
|
|
119
254
|
_csrf: ls.csrf,
|
|
120
|
-
|
|
255
|
+
_extCsrf: ls.extCsrf,
|
|
256
|
+
_isHttps: ls.isHttps,
|
|
121
257
|
_stepCount: summary.stepCount,
|
|
122
|
-
_model:
|
|
258
|
+
_model: normalizedModel,
|
|
259
|
+
_rawModel: rawModel,
|
|
123
260
|
});
|
|
124
261
|
}
|
|
125
262
|
}
|
|
@@ -130,16 +267,19 @@ function getChats() {
|
|
|
130
267
|
function getSteps(chat) {
|
|
131
268
|
if (!chat._port || !chat._csrf) return [];
|
|
132
269
|
|
|
270
|
+
// Determine if this is Antigravity based on source
|
|
271
|
+
const isAntigravity = chat.source === 'antigravity';
|
|
272
|
+
|
|
133
273
|
// Prefer GetCascadeTrajectorySteps (returns more steps than GetCascadeTrajectory)
|
|
134
274
|
const resp = callRpc(chat._port, chat._csrf, 'GetCascadeTrajectorySteps', {
|
|
135
275
|
cascadeId: chat.composerId,
|
|
136
|
-
}, chat.
|
|
276
|
+
}, chat._isHttps, chat._extCsrf, isAntigravity);
|
|
137
277
|
if (resp && resp.steps && resp.steps.length > 0) return resp.steps;
|
|
138
278
|
|
|
139
279
|
// Fallback to old method
|
|
140
280
|
const resp2 = callRpc(chat._port, chat._csrf, 'GetCascadeTrajectory', {
|
|
141
281
|
cascadeId: chat.composerId,
|
|
142
|
-
}, chat.
|
|
282
|
+
}, chat._isHttps, chat._extCsrf, isAntigravity);
|
|
143
283
|
if (resp2 && resp2.trajectory && resp2.trajectory.steps) return resp2.trajectory.steps;
|
|
144
284
|
|
|
145
285
|
return [];
|
|
@@ -151,9 +291,10 @@ function getSteps(chat) {
|
|
|
151
291
|
* We find the overlap with step-based messages by matching the last user message content.
|
|
152
292
|
*/
|
|
153
293
|
function getTailMessages(chat, stepMessages) {
|
|
294
|
+
const isAntigravity = chat.source === 'antigravity';
|
|
154
295
|
const resp = callRpc(chat._port, chat._csrf, 'GetCascadeTrajectory', {
|
|
155
296
|
cascadeId: chat.composerId,
|
|
156
|
-
}, chat.
|
|
297
|
+
}, chat._isHttps, chat._extCsrf, isAntigravity);
|
|
157
298
|
if (!resp || !resp.trajectory) return [];
|
|
158
299
|
|
|
159
300
|
const gm = resp.trajectory.generatorMetadata || [];
|
|
@@ -209,7 +350,7 @@ function getTailMessages(chat, stepMessages) {
|
|
|
209
350
|
return tail;
|
|
210
351
|
}
|
|
211
352
|
|
|
212
|
-
function parseStep(step) {
|
|
353
|
+
function parseStep(step, isAntigravity = false) {
|
|
213
354
|
const type = step.type || '';
|
|
214
355
|
const meta = step.metadata || {};
|
|
215
356
|
|
|
@@ -245,10 +386,12 @@ function parseStep(step) {
|
|
|
245
386
|
}
|
|
246
387
|
}
|
|
247
388
|
if (parts.length > 0) {
|
|
389
|
+
// Try both generatorModel (Antigravity) and generatorModelUid (Windsurf)
|
|
390
|
+
const model = meta.generatorModel || meta.generatorModelUid;
|
|
248
391
|
return {
|
|
249
392
|
role: 'assistant',
|
|
250
393
|
content: parts.join('\n'),
|
|
251
|
-
_model:
|
|
394
|
+
_model: isAntigravity && model ? normalizeAntigravityModel(model) : model,
|
|
252
395
|
_toolCalls,
|
|
253
396
|
};
|
|
254
397
|
}
|
|
@@ -319,9 +462,10 @@ function parseStep(step) {
|
|
|
319
462
|
|
|
320
463
|
function getMessages(chat) {
|
|
321
464
|
const steps = getSteps(chat);
|
|
465
|
+
const isAntigravity = chat.source === 'antigravity';
|
|
322
466
|
const messages = [];
|
|
323
467
|
for (const step of steps) {
|
|
324
|
-
const msg = parseStep(step);
|
|
468
|
+
const msg = parseStep(step, isAntigravity);
|
|
325
469
|
if (msg) messages.push(msg);
|
|
326
470
|
}
|
|
327
471
|
|
|
@@ -336,4 +480,6 @@ function getMessages(chat) {
|
|
|
336
480
|
|
|
337
481
|
function resetCache() { _lsCache = null; }
|
|
338
482
|
|
|
339
|
-
|
|
483
|
+
const labels = { 'windsurf': 'Windsurf', 'windsurf-next': 'Windsurf Next', 'antigravity': 'Antigravity' };
|
|
484
|
+
|
|
485
|
+
module.exports = { name, sources, labels, getChats, getMessages, resetCache };
|
package/editors/zed.js
CHANGED
|
@@ -2,9 +2,26 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
|
-
const { getAppDataPath } = require('./base');
|
|
6
5
|
|
|
7
|
-
const
|
|
6
|
+
const Database = require('better-sqlite3');
|
|
7
|
+
|
|
8
|
+
// Zed stores data in different locations depending on the platform
|
|
9
|
+
// - Windows: %LOCALAPPDATA%\Zed (not Roaming)
|
|
10
|
+
// - macOS: ~/Library/Application Support/Zed
|
|
11
|
+
// - Linux: ~/.config/Zed
|
|
12
|
+
function getZedDataPath() {
|
|
13
|
+
const home = os.homedir();
|
|
14
|
+
switch (process.platform) {
|
|
15
|
+
case 'win32':
|
|
16
|
+
return path.join(home, 'AppData', 'Local', 'Zed');
|
|
17
|
+
case 'darwin':
|
|
18
|
+
return path.join(home, 'Library', 'Application Support', 'Zed');
|
|
19
|
+
default: // linux, etc.
|
|
20
|
+
return path.join(home, '.config', 'Zed');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const THREADS_DB = path.join(getZedDataPath(), 'threads', 'threads.db');
|
|
8
25
|
|
|
9
26
|
// ============================================================
|
|
10
27
|
// Decompress zstd blob via CLI
|
|
@@ -25,30 +42,32 @@ function decompressZstd(buf) {
|
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
// ============================================================
|
|
28
|
-
// Query SQLite
|
|
45
|
+
// Query SQLite using better-sqlite3 (cross-platform)
|
|
29
46
|
// ============================================================
|
|
30
47
|
|
|
31
48
|
function queryDb(sql) {
|
|
32
49
|
if (!fs.existsSync(THREADS_DB)) return [];
|
|
33
50
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
const db = new Database(THREADS_DB, { readonly: true });
|
|
52
|
+
const rows = db.prepare(sql).all();
|
|
53
|
+
db.close();
|
|
54
|
+
return rows;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
// Silently fail if database is locked or inaccessible
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
40
59
|
}
|
|
41
60
|
|
|
42
|
-
function
|
|
61
|
+
function queryBlob(id) {
|
|
43
62
|
if (!fs.existsSync(THREADS_DB)) return null;
|
|
44
63
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return
|
|
51
|
-
}
|
|
64
|
+
const db = new Database(THREADS_DB, { readonly: true });
|
|
65
|
+
const row = db.prepare('SELECT data FROM threads WHERE id = ?').get(id);
|
|
66
|
+
db.close();
|
|
67
|
+
return row ? row.data : null;
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
52
71
|
}
|
|
53
72
|
|
|
54
73
|
// ============================================================
|
|
@@ -71,13 +90,14 @@ function getChats() {
|
|
|
71
90
|
mode: 'thread',
|
|
72
91
|
folder: null,
|
|
73
92
|
encrypted: false,
|
|
93
|
+
bubbleCount: 0,
|
|
74
94
|
_dataType: row.data_type,
|
|
75
95
|
_gitBranch: row.worktree_branch,
|
|
76
96
|
}));
|
|
77
97
|
}
|
|
78
98
|
|
|
79
99
|
function getMessages(chat) {
|
|
80
|
-
const blob =
|
|
100
|
+
const blob = queryBlob(chat.composerId);
|
|
81
101
|
if (!blob) return [];
|
|
82
102
|
|
|
83
103
|
let json;
|
|
@@ -88,7 +108,10 @@ function getMessages(chat) {
|
|
|
88
108
|
} else {
|
|
89
109
|
json = blob.toString('utf-8');
|
|
90
110
|
}
|
|
91
|
-
} catch {
|
|
111
|
+
} catch (e) {
|
|
112
|
+
// Decompression failed - zstd CLI not available
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
92
115
|
|
|
93
116
|
let data;
|
|
94
117
|
try { data = JSON.parse(json); } catch { return []; }
|
|
@@ -135,4 +158,6 @@ function extractContent(content) {
|
|
|
135
158
|
return { text: parts.join('\n') || '', toolCalls };
|
|
136
159
|
}
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
const labels = { 'zed': 'Zed' };
|
|
162
|
+
|
|
163
|
+
module.exports = { name, labels, getChats, getMessages };
|
package/index.js
CHANGED
|
@@ -188,7 +188,7 @@ const WINDSURF_VARIANTS = [
|
|
|
188
188
|
try {
|
|
189
189
|
const ps = execSync('ps aux', { encoding: 'utf-8', maxBuffer: 1024 * 1024 });
|
|
190
190
|
for (const line of ps.split('\n')) {
|
|
191
|
-
if (!line.includes('language_server_macos')
|
|
191
|
+
if (!line.includes('language_server_macos')) continue;
|
|
192
192
|
const ideMatch = line.match(/--ide_name\s+(\S+)/);
|
|
193
193
|
const appDirMatch = line.match(/--app_data_dir\s+(\S+)/);
|
|
194
194
|
if (ideMatch) runningIdes.push(ideMatch[1]);
|
|
@@ -215,23 +215,7 @@ const WINDSURF_VARIANTS = [
|
|
|
215
215
|
cache.initDb();
|
|
216
216
|
|
|
217
217
|
// ── Detect editors & collect sessions ───────────────────────
|
|
218
|
-
const { editors: editorModules } = require('./editors');
|
|
219
|
-
const EDITOR_DISPLAY = [
|
|
220
|
-
['cursor', 'Cursor'],
|
|
221
|
-
['windsurf', 'Windsurf'],
|
|
222
|
-
['windsurf-next', 'Windsurf Next'],
|
|
223
|
-
['antigravity', 'Antigravity'],
|
|
224
|
-
['claude-code', 'Claude Code'],
|
|
225
|
-
['vscode', 'VS Code'],
|
|
226
|
-
['vscode-insiders', 'VS Code Insiders'],
|
|
227
|
-
['zed', 'Zed'],
|
|
228
|
-
['opencode', 'OpenCode'],
|
|
229
|
-
['codex', 'Codex'],
|
|
230
|
-
['gemini-cli', 'Gemini CLI'],
|
|
231
|
-
['copilot-cli', 'Copilot CLI'],
|
|
232
|
-
['cursor-agent', 'Cursor Agent'],
|
|
233
|
-
['commandcode', 'Command Code'],
|
|
234
|
-
];
|
|
218
|
+
const { editors: editorModules, editorLabels } = require('./editors');
|
|
235
219
|
|
|
236
220
|
console.log(chalk.dim(' Looking for AI coding agents...'));
|
|
237
221
|
const allChats = [];
|
|
@@ -247,8 +231,11 @@ allChats.sort((a, b) => (b.lastUpdatedAt || b.createdAt || 0) - (a.lastUpdatedAt
|
|
|
247
231
|
const bySource = {};
|
|
248
232
|
for (const chat of allChats) bySource[chat.source] = (bySource[chat.source] || 0) + 1;
|
|
249
233
|
|
|
250
|
-
|
|
251
|
-
|
|
234
|
+
const displayList = Object.entries(editorLabels)
|
|
235
|
+
.map(([src, label]) => [src, label, bySource[src] || 0])
|
|
236
|
+
.sort((a, b) => b[2] - a[2]);
|
|
237
|
+
|
|
238
|
+
for (const [src, label, count] of displayList) {
|
|
252
239
|
if (count > 0) {
|
|
253
240
|
console.log(` ${chalk.green('✓')} ${chalk.bold(label.padEnd(18))} ${chalk.dim(`${count} session${count === 1 ? '' : 's'}`)}`);
|
|
254
241
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlytics",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravity, OpenCode, Command Code",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,8 @@ const PATHS = {
|
|
|
11
11
|
vscode: 'M17.583.063a1.5 1.5 0 0 0-1.032.392 1.5 1.5 0 0 0-.001 0L7.04 9.708 2.81 6.442a1 1 0 0 0-1.32.098L.178 7.853a1 1 0 0 0 0 1.414l3.612 3.29-3.612 3.29a1 1 0 0 0 0 1.414L1.49 18.574a1 1 0 0 0 1.32.098L7.04 15.408l9.51 9.253a1.5 1.5 0 0 0 1.033.392A1.5 1.5 0 0 0 19.08 23.5V1.5A1.5 1.5 0 0 0 17.583.063M17.08 5.442l-5.5 7.115 5.5 7.115Z',
|
|
12
12
|
antigravity: 'M21.751 22.607c1.34 1.005 3.35.335 1.508-1.508C17.73 15.74 18.904 1 12.037 1 5.17 1 6.342 15.74.815 21.1c-2.01 2.009.167 2.511 1.507 1.506 5.192-3.517 4.857-9.714 9.715-9.714 4.857 0 4.522 6.197 9.714 9.715z',
|
|
13
13
|
command: 'M6,2A4,4 0 0,1 10,6V8H14V6A4,4 0 0,1 18,2A4,4 0 0,1 22,6A4,4 0 0,1 18,10H16V14H18A4,4 0 0,1 22,18A4,4 0 0,1 18,22A4,4 0 0,1 14,18V16H10V18A4,4 0 0,1 6,22A4,4 0 0,1 2,18A4,4 0 0,1 6,14H8V10H6A4,4 0 0,1 2,6A4,4 0 0,1 6,2M16,18A2,2 0 0,0 18,20A2,2 0 0,0 20,18A2,2 0 0,0 18,16H16V18M14,10H10V14H14V10M6,16A2,2 0 0,0 4,18A2,2 0 0,0 6,20A2,2 0 0,0 8,18V16H6M8,6A2,2 0 0,0 6,4A2,2 0 0,0 4,6A2,2 0 0,0 6,8H8V6M18,8A2,2 0 0,0 20,6A2,2 0 0,0 18,4A2,2 0 0,0 16,6V8H18Z',
|
|
14
|
+
goose: 'M22.112 23.596C23.018 23.399 23.979 22.864 23.979 22.864L22.297 21.479C21.466 20.795 20.759 19.973 20.206 19.05C19.441 17.774 18.385 16.697 17.125 15.908L16.509 15.55C16.298 15.403 16.151 15.175 16.13 14.917C16.117 14.751 16.157 14.602 16.25 14.471C16.57 14.019 18.227 12.053 18.531 11.802C18.922 11.479 19.357 11.21 19.762 10.902L19.934 10.77C19.936 10.768 19.938 10.767 19.94 10.765C20.07 10.663 20.192 10.554 20.29 10.425C20.641 10.018 20.726 9.658 20.747 9.499C20.7 9.346 20.558 9.003 20.163 8.608C20.411 8.623 20.71 8.819 20.982 9.05C21.165 8.758 21.356 8.45 21.547 8.141C21.674 7.934 21.485 7.78 21.48 7.775L21.479 7.775C21.479 7.775 21.479 7.774 21.479 7.774C21.474 7.769 21.319 7.579 21.114 7.707C20.673 7.979 20.234 8.252 19.842 8.5C19.842 8.5 19.379 8.49 18.829 8.964C18.7 9.062 18.591 9.184 18.489 9.314L18.484 9.32C18.439 9.377 18.396 9.434 18.352 9.492C18.044 9.897 17.775 10.332 17.452 10.723C17.201 11.027 15.235 12.684 14.783 13.004C14.652 13.097 14.504 13.137 14.337 13.124C14.08 13.103 13.851 12.956 13.704 12.745L13.346 12.129C12.557 10.868 11.48 9.813 10.204 9.048C9.281 8.495 8.459 7.787 7.775 6.957L6.39 5.275C6.39 5.275 5.854 6.236 5.658 7.141C5.931 7.474 6.644 8.298 7.473 8.928C6.581 8.509 5.922 8.184 5.402 7.913C5.322 8.506 5.353 9.403 5.436 10.098C5.999 10.344 6.957 10.724 7.933 10.926C7.152 11.108 6.296 11.141 5.635 11.128C5.751 11.558 5.914 11.997 6.132 12.438C6.224 12.642 6.326 12.842 6.435 13.037C6.785 13.133 8.159 13.333 8.89 13.169C8.163 13.428 6.942 13.865 6.942 13.865C7.88 15.031 8.916 15.98 8.916 15.98C10.492 15.133 10.852 15.017 12.034 14.244C10.119 15.802 9.622 16.438 9.085 17.091L8.71 17.617C8.516 17.89 8.347 18.18 8.206 18.484C7.734 19.5 7.065 21.666 7.065 21.666C6.946 22.043 7.222 22.32 7.589 22.189C7.589 22.189 9.754 21.521 10.77 21.048C11.074 20.907 11.364 20.738 11.637 20.544L12.163 20.169C12.339 20.024 12.514 19.882 12.707 19.714C12.707 19.714 14.03 21.282 15.39 22.313C15.39 22.313 15.826 21.092 16.086 20.364C15.921 21.096 16.121 22.469 16.218 22.819C16.412 22.929 16.612 23.03 16.816 23.123C17.258 23.341 17.696 23.503 18.126 23.619C18.113 22.958 18.146 22.102 18.329 21.321C18.531 22.297 18.91 23.256 19.157 23.819C19.851 23.902 20.748 23.933 21.341 23.853C21.07 23.333 20.746 22.673 20.326 21.781C20.956 22.611 21.781 23.324 22.113 23.597L22.112 23.596Z',
|
|
15
|
+
kiro: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m-1.5 14.5v-9l7 4.5-7 4.5',
|
|
14
16
|
// Terminal icon for generic editors
|
|
15
17
|
terminal: 'M4 17.27V19h16v-1.73ZM4 5v1.73l7.07 4.55L4 15.82v1.73l10-6.46Z',
|
|
16
18
|
}
|
|
@@ -32,6 +34,8 @@ const EDITOR_ICONS = {
|
|
|
32
34
|
'opencode': 'terminal',
|
|
33
35
|
'codex': 'terminal',
|
|
34
36
|
'commandcode': 'command',
|
|
37
|
+
'goose': 'goose',
|
|
38
|
+
'kiro': 'kiro',
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
export default function EditorIcon({ source, size = 16, className = '' }) {
|
package/ui/src/lib/constants.js
CHANGED
|
@@ -14,6 +14,8 @@ export const EDITOR_COLORS = {
|
|
|
14
14
|
'copilot-cli': '#8957e5',
|
|
15
15
|
'cursor-agent': '#f59e0b',
|
|
16
16
|
'commandcode': '#e11d48',
|
|
17
|
+
'goose': '#333333',
|
|
18
|
+
'kiro': '#ff9900',
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
export const EDITOR_LABELS = {
|
|
@@ -32,6 +34,8 @@ export const EDITOR_LABELS = {
|
|
|
32
34
|
'copilot-cli': 'Copilot CLI',
|
|
33
35
|
'cursor-agent': 'Cursor Agent',
|
|
34
36
|
'commandcode': 'Command Code',
|
|
37
|
+
'goose': 'Goose',
|
|
38
|
+
'kiro': 'Kiro',
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
export function editorColor(src) {
|