cursor-usage-analyzer 0.2.1 → 0.3.1
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/.claude/settings.local.json +12 -6
- package/README.md +14 -0
- package/analyze.js +277 -49
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_08-35-54_uu_app_aicoding_conv55.txt +49 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_08-36-35_uu_app_aicoding_conv54.txt +241 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_08-47-45_uu_app_aicoding_conv56.txt +122 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_08-56-31_uu_app_aicoding_conv40.txt +80 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_08-58-09__unmatched__conv108.txt +26 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_08-59-08_uu_app_aicoding_conv57.txt +306 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-00-49_uu_app_aicoding_conv41.txt +149 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-04-15_uu_app_aicoding_conv58.txt +143 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-06-29_uu_app_aicoding_conv59.txt +119 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-17-49_uu_app_aicoding_conv60.txt +227 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-18-36_uu_app_aicoding_conv70.txt +193 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-26-21_uu_app_aicoding_conv42.txt +111 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-31-34_uu_app_aicoding_conv71.txt +232 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-40-01_uu_app_aicoding_conv72.txt +125 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-49-58_uu_app_aicoding_conv73.txt +64 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_09-57-27_uu_entitymanage_conv43.txt +157 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_10-02-36_uu_app_aicoding_conv44.txt +294 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_10-48-21_uu_app_aicoding_conv79.txt +181 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-13-29_uu_app_aicoding_conv45.txt +160 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-19-00_uu_app_aicoding_conv46.txt +82 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-21-15_uu_app_aicoding_conv74.txt +103 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-25-21_uu_app_aicoding_conv75.txt +119 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-26-01_uu_app_aicoding_conv47.txt +266 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-31-42_uu_entitymanage_conv48.txt +130 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-33-00_uu_app_aicoding_conv1.txt +260 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_11-51-10_uu_app_aicoding_conv80.txt +68 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_12-24-42_cursor_usage_an_conv106.txt +769 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_12-37-27_uu_app_aicoding_conv2.txt +897 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_12-48-53__unmatched__conv109.txt +26 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_12-51-19_uu_app_aicoding_conv3.txt +72 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_13-01-28_uu_app_aicoding_conv4.txt +112 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_13-21-29_uu_app_aicoding_conv5.txt +286 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_14-14-37_uu_app_aicoding_conv76.txt +765 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_14-25-53_uu_app_aicoding_conv7.txt +134 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_14-31-19_uu_app_aicoding_conv8.txt +118 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_15-15-16_uu_app_aicoding_conv9.txt +4644 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_15-20-50_uu_app_aicoding_conv6.txt +945 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-00-41_cursor_usage_an_conv107.txt +85 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-25-01_uu_app_aicoding_conv11.txt +274 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-29-52_uu_app_aicoding_conv10.txt +1603 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-38-00_uu_app_aicoding_conv12.txt +96 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-43-55_uu_app_aicoding_conv13.txt +74 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-47-13_uu_app_aicoding_conv14.txt +172 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-48-38_uu_cloud_univer_conv82.txt +253 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-51-54_uu_app_aicoding_conv16.txt +189 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-51-54_uu_app_aicoding_conv17.txt +57 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_16-59-13_uu_app_aicoding_conv15.txt +36 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-03-28_uu_app_aicoding_conv18.txt +212 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-05-14_uu_app_aicoding_conv19.txt +87 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-13-17_uu_app_aicoding_conv20.txt +77 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-25-15_uu_app_aicoding_conv21.txt +131 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-31-30_uu_app_aicoding_conv23.txt +108 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-38-46_uu_app_aicoding_conv81.txt +428 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-43-08_uu_app_aicoding_conv24.txt +15297 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-51-39_uu_app_aicoding_conv22.txt +60 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_17-59-43_uu_app_aicoding_conv25.txt +189 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_18-03-50_uu_app_aicoding_conv26.txt +120 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_18-30-45_uu_app_aicoding_conv83.txt +523 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_18-32-40_uu_app_aicoding_conv27.txt +3941 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_18-39-32_uu_app_aicoding_conv84.txt +133 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_18-41-01_uu_app_aicoding_conv28.txt +136 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_18-56-27_uu_app_aicoding_conv85.txt +211 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-10-56_uu_app_aicoding_conv86.txt +319 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-22-42_uu_app_aicoding_conv87.txt +193 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-27-57_uu_app_aicoding_conv88.txt +272 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-32-27_uu_app_aicoding_conv89.txt +50 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-42-59_uu_app_aicoding_conv90.txt +125 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-47-01_uu_app_aicoding_conv91.txt +102 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_19-58-26_uu_app_aicoding_conv92.txt +145 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_20-43-25_uu_app_aicoding_conv93.txt +553 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_20-56-36_uu_app_aicoding_conv95.txt +195 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_20-58-23_uu_app_aicoding_conv96.txt +86 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-01-26_uu_app_aicoding_conv94.txt +116 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-03-46_uu_app_aicoding_conv61.txt +1743 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-06-54_uu_app_aicoding_conv97.txt +102 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-07-32_uu_app_aicoding_conv29.txt +9930 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-09-02_uu_app_aicoding_conv98.txt +111 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-11-07_uu_app_aicoding_conv49.txt +170 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-16-16_uu_app_aicoding_conv62.txt +200 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-17-18_uu_app_aicoding_conv31.txt +351 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-26-32_uu_app_aicoding_conv99.txt +219 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-29-18_uu_app_aicoding_conv100.txt +121 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-33-35_uu_app_aicoding_conv30.txt +204 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-38-37_uu_app_aicoding_conv63.txt +251 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-42-10_uu_entitymanage_conv33.txt +163 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-43-41_uu_app_aicoding_conv64.txt +139 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-43-53_uu_app_aicoding_conv101.txt +221 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-44-55_uu_app_aicoding_conv50.txt +156 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-47-10_uu_app_aicoding_conv65.txt +136 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-48-40_uu_app_aicoding_conv51.txt +130 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-49-31_uu_app_aicoding_conv102.txt +153 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-49-44_uu_app_aicoding_conv66.txt +54 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-51-05_uu_app_aicoding_conv67.txt +55 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-51-26_uu_app_aicoding_conv32.txt +6172 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-56-08_uu_app_aicoding_conv103.txt +102 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_21-59-00_uu_app_aicoding_conv52.txt +244 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-10-16_uu_app_aicoding_conv77.txt +61 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-11-24_uu_app_aicoding_conv68.txt +142 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-12-31_uu_app_aicoding_conv104.txt +66 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-16-03_uu_app_aicoding_conv53.txt +439 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-23-41_uu_entitymanage_conv34.txt +2251 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-25-56_uu_app_aicoding_conv69.txt +169 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-26-54_uu_app_aicoding_conv105.txt +70 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-33-45_uu_entitymanage_conv35.txt +144 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-39-23_uu_app_aicoding_conv37.txt +104 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_22-45-30_uu_app_aicoding_conv78.txt +187 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_23-04-38_uu_app_aicoding_conv36.txt +2292 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_23-08-50_uu_entitymanage_conv38.txt +109 -0
- package/cursor-logs-export/chats/2026-02-05_2026-02-10_23-14-01_uu_entitymanage_conv39.txt +112 -0
- package/cursor-logs-export/report.html +3071 -0
- package/html-template.js +610 -18
- package/package.json +19 -6
- package/.idea/cursor-usage-analyzer.iml +0 -12
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -11
- package/cursor-usage-analyzer-0.1.0.tgz +0 -0
- package/cursor-usage-analyzer-0.2.0.tgz +0 -0
- package/cursor-usage-analyzer-0.2.1.tgz +0 -0
- package/team-usage-events-10287858-2025-12-18.csv +0 -600
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"Bash(
|
|
4
|
+
"mcp__plus4u-mcp__document_read",
|
|
5
|
+
"mcp__plus4u-mcp__book_list_structure",
|
|
6
|
+
"Bash(npm install:*)",
|
|
7
7
|
"Bash(node -e:*)",
|
|
8
|
-
"Bash(
|
|
9
|
-
"
|
|
10
|
-
"Bash(
|
|
8
|
+
"Bash(node analyze.js:*)",
|
|
9
|
+
"mcp__skilled-plus4u-mcp__login",
|
|
10
|
+
"Bash(npm rebuild:*)",
|
|
11
|
+
"mcp__skilled-plus4u-mcp__executeSkill",
|
|
12
|
+
"Bash(node-gyp rebuild:*)",
|
|
13
|
+
"Bash(npx node-gyp rebuild:*)",
|
|
14
|
+
"Bash(npm run install:*)",
|
|
15
|
+
"Bash(npm run build:*)",
|
|
16
|
+
"Bash(node validate.js:*)"
|
|
11
17
|
]
|
|
12
18
|
}
|
|
13
19
|
}
|
package/README.md
CHANGED
|
@@ -34,8 +34,13 @@ cd cursor-usage-analyzer
|
|
|
34
34
|
|
|
35
35
|
# Install dependencies
|
|
36
36
|
npm install
|
|
37
|
+
|
|
38
|
+
# Build native bindings (runs automatically via postinstall, but run manually if needed)
|
|
39
|
+
npm run build
|
|
37
40
|
```
|
|
38
41
|
|
|
42
|
+
**Note**: The `postinstall` script automatically builds `better-sqlite3` native bindings. If you see SQLite errors, run `npm run build` manually.
|
|
43
|
+
|
|
39
44
|
## Quick Start
|
|
40
45
|
|
|
41
46
|
### Using npx
|
|
@@ -307,6 +312,15 @@ Ensure Cursor has been used and conversations exist. Check the database path for
|
|
|
307
312
|
- Verify you have conversations in Cursor from that period
|
|
308
313
|
- Ensure Cursor isn't currently running (may lock database)
|
|
309
314
|
|
|
315
|
+
### SQLite errors or "Database exceeds 2GB limit"
|
|
316
|
+
This means `better-sqlite3` native bindings weren't built properly. Fix it by:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
npm run build
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
This rebuilds the native SQLite bindings. The tool will fall back to `sql.js` (pure JavaScript) if native bindings fail, but `sql.js` has a 2GB database size limit.
|
|
323
|
+
|
|
310
324
|
### Project shows as "unknown"
|
|
311
325
|
This happens when the analyzer can't determine the workspace from file paths. The conversation is still exported with all content intact.
|
|
312
326
|
|
package/analyze.js
CHANGED
|
@@ -3,10 +3,83 @@
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import os from 'os';
|
|
6
|
-
import Database from 'better-sqlite3';
|
|
7
6
|
import { generateHTMLReport } from './html-template.js';
|
|
8
7
|
import { parse } from 'csv-parse/sync';
|
|
9
8
|
|
|
9
|
+
// Database abstraction - try better-sqlite3 first, fall back to sql.js
|
|
10
|
+
let dbEngine = null;
|
|
11
|
+
|
|
12
|
+
async function loadDatabaseEngine() {
|
|
13
|
+
// Try better-sqlite3 first (faster, handles large files)
|
|
14
|
+
try {
|
|
15
|
+
const { default: Database } = await import('better-sqlite3');
|
|
16
|
+
// Test that it actually works (native bindings compiled)
|
|
17
|
+
const testDb = new Database(':memory:');
|
|
18
|
+
testDb.close();
|
|
19
|
+
dbEngine = { type: 'better-sqlite3', Database };
|
|
20
|
+
return;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.log('Note: better-sqlite3 unavailable, trying sql.js fallback...');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Fall back to sql.js (pure JS, works everywhere, but 2GB limit)
|
|
26
|
+
try {
|
|
27
|
+
const { default: initSqlJs } = await import('sql.js');
|
|
28
|
+
const SQL = await initSqlJs();
|
|
29
|
+
dbEngine = { type: 'sql.js', SQL };
|
|
30
|
+
} catch (e) {
|
|
31
|
+
throw new Error('No SQLite library available. Please run: npm install -g cursor-usage-analyzer');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function openDatabase(dbPath) {
|
|
36
|
+
if (dbEngine.type === 'better-sqlite3') {
|
|
37
|
+
return new dbEngine.Database(dbPath, { readonly: true });
|
|
38
|
+
} else {
|
|
39
|
+
const fileSize = fs.statSync(dbPath).size;
|
|
40
|
+
if (fileSize > 2 * 1024 * 1024 * 1024) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Database file (${(fileSize / 1024 / 1024 / 1024).toFixed(2)} GB) exceeds sql.js 2GB limit.\n` +
|
|
43
|
+
`To fix this, install globally with native compilation:\n` +
|
|
44
|
+
` npm install -g cursor-usage-analyzer\n` +
|
|
45
|
+
`Or clone and build locally:\n` +
|
|
46
|
+
` git clone https://github.com/xaverric/cursor-usage-analyzer.git\n` +
|
|
47
|
+
` cd cursor-usage-analyzer && npm install && npm rebuild better-sqlite3`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const dbBuffer = fs.readFileSync(dbPath);
|
|
51
|
+
return new dbEngine.SQL.Database(dbBuffer);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function dbQueryAll(db, sql, params = []) {
|
|
56
|
+
if (dbEngine.type === 'better-sqlite3') {
|
|
57
|
+
return db.prepare(sql).all(...params);
|
|
58
|
+
} else {
|
|
59
|
+
const stmt = db.prepare(sql);
|
|
60
|
+
if (params.length > 0) stmt.bind(params);
|
|
61
|
+
const results = [];
|
|
62
|
+
while (stmt.step()) {
|
|
63
|
+
results.push(stmt.getAsObject());
|
|
64
|
+
}
|
|
65
|
+
stmt.free();
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function dbQueryOne(db, sql, params = []) {
|
|
71
|
+
if (dbEngine.type === 'better-sqlite3') {
|
|
72
|
+
return db.prepare(sql).get(...params);
|
|
73
|
+
} else {
|
|
74
|
+
const results = dbQueryAll(db, sql, params);
|
|
75
|
+
return results.length > 0 ? results[0] : null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function dbClose(db) {
|
|
80
|
+
db.close();
|
|
81
|
+
}
|
|
82
|
+
|
|
10
83
|
// Get Cursor storage paths based on OS
|
|
11
84
|
function getCursorPaths() {
|
|
12
85
|
const platform = os.platform();
|
|
@@ -135,13 +208,14 @@ function parseCSVUsage(csvPath) {
|
|
|
135
208
|
}
|
|
136
209
|
|
|
137
210
|
// Match API calls to a conversation based on timestamp and model
|
|
138
|
-
function matchAPICallsToConversation(conv, apiCalls, timeWindow =
|
|
211
|
+
function matchAPICallsToConversation(conv, apiCalls, timeWindow = 10 * 60 * 1000) {
|
|
139
212
|
if (!apiCalls || apiCalls.length === 0) return [];
|
|
140
213
|
|
|
141
|
-
const convStart = conv.timestamp;
|
|
142
|
-
const convEnd =
|
|
143
|
-
|
|
144
|
-
:
|
|
214
|
+
const convStart = conv.createdAt || conv.timestamp;
|
|
215
|
+
const convEnd = Math.max(
|
|
216
|
+
conv.timestamp,
|
|
217
|
+
conv.messages.length > 0 ? Math.max(...conv.messages.map(m => m.timestamp)) : 0
|
|
218
|
+
);
|
|
145
219
|
|
|
146
220
|
const normalizeModel = (model) => {
|
|
147
221
|
if (!model) return 'unknown';
|
|
@@ -149,20 +223,21 @@ function matchAPICallsToConversation(conv, apiCalls, timeWindow = 5 * 60 * 1000)
|
|
|
149
223
|
if (m.includes('sonnet')) return 'sonnet';
|
|
150
224
|
if (m.includes('opus')) return 'opus';
|
|
151
225
|
if (m.includes('composer')) return 'composer';
|
|
152
|
-
if (m
|
|
153
|
-
return
|
|
226
|
+
if (m.includes('gpt')) return 'gpt';
|
|
227
|
+
if (m.includes('gemini')) return 'gemini';
|
|
228
|
+
return m;
|
|
154
229
|
};
|
|
155
230
|
|
|
156
231
|
const convModel = normalizeModel(conv.model);
|
|
232
|
+
// For 'auto', 'default', 'unknown' models - match by time only
|
|
233
|
+
const isFlexibleModel = ['auto', 'default', 'unknown'].includes(convModel);
|
|
157
234
|
|
|
158
235
|
return apiCalls.filter(call => {
|
|
159
236
|
const callModel = normalizeModel(call.model);
|
|
160
237
|
const timeMatch = call.timestamp >= (convStart - timeWindow) &&
|
|
161
238
|
call.timestamp <= (convEnd + timeWindow);
|
|
162
239
|
|
|
163
|
-
const modelMatch = convModel ===
|
|
164
|
-
callModel === 'any' ||
|
|
165
|
-
convModel === callModel;
|
|
240
|
+
const modelMatch = isFlexibleModel || convModel === callModel;
|
|
166
241
|
|
|
167
242
|
return timeMatch && modelMatch;
|
|
168
243
|
});
|
|
@@ -215,6 +290,37 @@ function readWorkspaceJson(workspaceId) {
|
|
|
215
290
|
return null;
|
|
216
291
|
}
|
|
217
292
|
|
|
293
|
+
// Build composerId -> workspaceName map from workspace databases
|
|
294
|
+
function buildComposerWorkspaceMap() {
|
|
295
|
+
const map = {};
|
|
296
|
+
try {
|
|
297
|
+
const entries = fs.readdirSync(CURSOR_WORKSPACE_STORAGE);
|
|
298
|
+
for (const entry of entries) {
|
|
299
|
+
const wsName = readWorkspaceJson(entry);
|
|
300
|
+
if (!wsName) continue;
|
|
301
|
+
|
|
302
|
+
const dbFile = path.join(CURSOR_WORKSPACE_STORAGE, entry, 'state.vscdb');
|
|
303
|
+
if (!fs.existsSync(dbFile)) continue;
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
const db = openDatabase(dbFile);
|
|
307
|
+
const row = dbQueryOne(db, "SELECT value FROM ItemTable WHERE key = 'composer.composerData'");
|
|
308
|
+
if (row) {
|
|
309
|
+
const data = JSON.parse(row.value);
|
|
310
|
+
const composers = data.allComposers || [];
|
|
311
|
+
for (const c of composers) {
|
|
312
|
+
if (c.composerId) {
|
|
313
|
+
map[c.composerId] = wsName;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
dbClose(db);
|
|
318
|
+
} catch (e) {}
|
|
319
|
+
}
|
|
320
|
+
} catch (e) {}
|
|
321
|
+
return map;
|
|
322
|
+
}
|
|
323
|
+
|
|
218
324
|
// Find workspace name from file path
|
|
219
325
|
function findWorkspaceFromPath(filePath) {
|
|
220
326
|
try {
|
|
@@ -245,34 +351,89 @@ function findWorkspaceFromPath(filePath) {
|
|
|
245
351
|
return parts[parts.length - 2] || 'unknown';
|
|
246
352
|
}
|
|
247
353
|
|
|
354
|
+
// Extract file paths from various composer data sources
|
|
355
|
+
function extractFilePathsFromComposer(composerData) {
|
|
356
|
+
const paths = [];
|
|
357
|
+
|
|
358
|
+
// newlyCreatedFiles - array of objects with uri.path
|
|
359
|
+
if (Array.isArray(composerData.newlyCreatedFiles)) {
|
|
360
|
+
for (const f of composerData.newlyCreatedFiles) {
|
|
361
|
+
const p = f?.uri?.path || f?.uri?.fsPath;
|
|
362
|
+
if (p) paths.push(p);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// codeBlockData - can be array of file URI strings or object with file URI keys
|
|
367
|
+
if (Array.isArray(composerData.codeBlockData)) {
|
|
368
|
+
for (const item of composerData.codeBlockData) {
|
|
369
|
+
if (typeof item === 'string') paths.push(item.replace('file://', ''));
|
|
370
|
+
}
|
|
371
|
+
} else if (composerData.codeBlockData && typeof composerData.codeBlockData === 'object') {
|
|
372
|
+
for (const key of Object.keys(composerData.codeBlockData)) {
|
|
373
|
+
paths.push(key.replace('file://', ''));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// originalFileStates - array of file URI strings
|
|
378
|
+
if (Array.isArray(composerData.originalFileStates)) {
|
|
379
|
+
for (const item of composerData.originalFileStates) {
|
|
380
|
+
if (typeof item === 'string') paths.push(item.replace('file://', ''));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// allAttachedFileCodeChunksUris - array of file URI strings
|
|
385
|
+
if (Array.isArray(composerData.allAttachedFileCodeChunksUris)) {
|
|
386
|
+
for (const item of composerData.allAttachedFileCodeChunksUris) {
|
|
387
|
+
if (typeof item === 'string') paths.push(item.replace('file://', ''));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// addedFiles - can be array of file URI strings (sometimes it's a number)
|
|
392
|
+
if (Array.isArray(composerData.addedFiles)) {
|
|
393
|
+
for (const item of composerData.addedFiles) {
|
|
394
|
+
if (typeof item === 'string') paths.push(item.replace('file://', ''));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return paths;
|
|
399
|
+
}
|
|
400
|
+
|
|
248
401
|
// Determine workspace name from multiple sources
|
|
249
|
-
function resolveWorkspace(composerData, headers,
|
|
250
|
-
// Try
|
|
402
|
+
function resolveWorkspace(composerData, headers, composerId, composerWorkspaceMap) {
|
|
403
|
+
// Try composer->workspace map first (most reliable)
|
|
404
|
+
if (composerWorkspaceMap && composerId && composerWorkspaceMap[composerId]) {
|
|
405
|
+
return composerWorkspaceMap[composerId];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Try workspaceId field
|
|
251
409
|
if (composerData.workspaceId) {
|
|
252
410
|
const name = readWorkspaceJson(composerData.workspaceId);
|
|
253
411
|
if (name) return name;
|
|
254
412
|
}
|
|
255
413
|
|
|
256
|
-
// Try file paths from
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
Object.keys(composerData.codeBlockData || {})[0]?.replace('file://', ''),
|
|
260
|
-
composerData.addedFiles?.[0]?.replace('file://', ''),
|
|
261
|
-
composerData.allAttachedFileCodeChunksUris?.[0]?.replace('file://', '')
|
|
262
|
-
];
|
|
263
|
-
|
|
264
|
-
for (const filePath of fileSources) {
|
|
414
|
+
// Try file paths from all available sources
|
|
415
|
+
const filePaths = extractFilePathsFromComposer(composerData);
|
|
416
|
+
for (const filePath of filePaths) {
|
|
265
417
|
if (filePath) {
|
|
266
418
|
const name = findWorkspaceFromPath(filePath);
|
|
267
419
|
if (name !== 'unknown') return name;
|
|
268
420
|
}
|
|
269
421
|
}
|
|
270
422
|
|
|
423
|
+
return 'unknown';
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Determine workspace name with database access for messageRequestContext
|
|
427
|
+
function resolveWorkspaceWithDb(composerData, headers, db, composerId, composerWorkspaceMap) {
|
|
428
|
+
// Try basic resolution first
|
|
429
|
+
const basicResult = resolveWorkspace(composerData, headers, composerId, composerWorkspaceMap);
|
|
430
|
+
if (basicResult !== 'unknown') return basicResult;
|
|
431
|
+
|
|
271
432
|
// Last resort: messageRequestContext
|
|
272
433
|
if (headers?.length > 0) {
|
|
273
434
|
try {
|
|
274
435
|
const contextKey = `messageRequestContext:${composerId}:${headers[0].bubbleId}`;
|
|
275
|
-
const row = db
|
|
436
|
+
const row = dbQueryOne(db, "SELECT value FROM cursorDiskKV WHERE key = ?", [contextKey]);
|
|
276
437
|
if (row) {
|
|
277
438
|
const context = JSON.parse(row.value);
|
|
278
439
|
if (context.projectLayouts?.length > 0) {
|
|
@@ -288,7 +449,7 @@ function resolveWorkspace(composerData, headers, db, composerId) {
|
|
|
288
449
|
}
|
|
289
450
|
|
|
290
451
|
// Extract conversations from database
|
|
291
|
-
function extractConversations(startTime, endTime, apiCalls = []) {
|
|
452
|
+
async function extractConversations(startTime, endTime, apiCalls = []) {
|
|
292
453
|
const dbPath = path.join(CURSOR_GLOBAL_STORAGE, 'state.vscdb');
|
|
293
454
|
|
|
294
455
|
if (!fs.existsSync(dbPath)) {
|
|
@@ -297,11 +458,15 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
297
458
|
}
|
|
298
459
|
|
|
299
460
|
try {
|
|
300
|
-
|
|
461
|
+
console.log('Building workspace map...');
|
|
462
|
+
const composerWorkspaceMap = buildComposerWorkspaceMap();
|
|
463
|
+
console.log(`Mapped ${Object.keys(composerWorkspaceMap).length} composer IDs to workspaces`);
|
|
464
|
+
|
|
465
|
+
const db = openDatabase(dbPath);
|
|
301
466
|
|
|
302
467
|
// Load all bubbles
|
|
303
468
|
const bubbleMap = {};
|
|
304
|
-
const bubbleRows = db
|
|
469
|
+
const bubbleRows = dbQueryAll(db, "SELECT key, value FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'");
|
|
305
470
|
|
|
306
471
|
for (const row of bubbleRows) {
|
|
307
472
|
try {
|
|
@@ -312,9 +477,9 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
312
477
|
}
|
|
313
478
|
|
|
314
479
|
// Load composers
|
|
315
|
-
const composerRows = db
|
|
480
|
+
const composerRows = dbQueryAll(db,
|
|
316
481
|
"SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%' AND value LIKE '%fullConversationHeadersOnly%'"
|
|
317
|
-
)
|
|
482
|
+
);
|
|
318
483
|
|
|
319
484
|
const conversations = [];
|
|
320
485
|
|
|
@@ -322,9 +487,12 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
322
487
|
try {
|
|
323
488
|
const composerId = row.key.split(':')[1];
|
|
324
489
|
const composer = JSON.parse(row.value);
|
|
325
|
-
const
|
|
490
|
+
const createdAt = composer.createdAt || 0;
|
|
491
|
+
const lastUpdatedAt = composer.lastUpdatedAt || createdAt;
|
|
492
|
+
const timestamp = lastUpdatedAt || Date.now();
|
|
326
493
|
|
|
327
|
-
if
|
|
494
|
+
// Include conversation if its time range overlaps with the query range
|
|
495
|
+
if (lastUpdatedAt < startTime || createdAt > endTime) continue;
|
|
328
496
|
|
|
329
497
|
const headers = composer.fullConversationHeadersOnly || [];
|
|
330
498
|
if (headers.length === 0) continue;
|
|
@@ -341,7 +509,7 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
341
509
|
return {
|
|
342
510
|
role: h.type === 1 ? 'user' : 'assistant',
|
|
343
511
|
text: text.trim(),
|
|
344
|
-
timestamp: bubble.timestamp ||
|
|
512
|
+
timestamp: bubble.timestamp || timestamp
|
|
345
513
|
};
|
|
346
514
|
})
|
|
347
515
|
.filter(Boolean);
|
|
@@ -352,9 +520,10 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
352
520
|
composerId,
|
|
353
521
|
name: composer.name || 'Untitled Chat',
|
|
354
522
|
timestamp,
|
|
523
|
+
createdAt,
|
|
355
524
|
messages,
|
|
356
525
|
messageCount: messages.length,
|
|
357
|
-
workspace:
|
|
526
|
+
workspace: resolveWorkspaceWithDb(composer, headers, db, composerId, composerWorkspaceMap),
|
|
358
527
|
model: composer.modelConfig?.modelName || 'unknown',
|
|
359
528
|
contextTokensUsed: composer.contextTokensUsed || 0,
|
|
360
529
|
contextTokenLimit: composer.contextTokenLimit || 0,
|
|
@@ -372,22 +541,20 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
372
541
|
// Match API calls to conversations WITHOUT double-counting
|
|
373
542
|
// Each API call should only be matched to ONE conversation
|
|
374
543
|
if (apiCalls.length > 0) {
|
|
544
|
+
// Tag each API call with its original index for tracking
|
|
545
|
+
const indexedApiCalls = apiCalls.map((call, idx) => ({ ...call, _origIdx: idx }));
|
|
375
546
|
const usedApiCallIndices = new Set();
|
|
376
547
|
|
|
377
|
-
|
|
378
|
-
|
|
548
|
+
// Sort conversations by timestamp so matching is deterministic
|
|
549
|
+
const sortedConvs = [...conversations].sort((a, b) => a.timestamp - b.timestamp);
|
|
550
|
+
|
|
551
|
+
for (const conv of sortedConvs) {
|
|
552
|
+
const availableApiCalls = indexedApiCalls.filter(c => !usedApiCallIndices.has(c._origIdx));
|
|
379
553
|
const matchedCalls = matchAPICallsToConversation(conv, availableApiCalls);
|
|
380
554
|
|
|
381
|
-
// Mark these API calls as used
|
|
555
|
+
// Mark these API calls as used by their original index
|
|
382
556
|
matchedCalls.forEach(call => {
|
|
383
|
-
|
|
384
|
-
c.timestamp === call.timestamp &&
|
|
385
|
-
c.model === call.model &&
|
|
386
|
-
c.totalTokens === call.totalTokens
|
|
387
|
-
);
|
|
388
|
-
if (originalIndex !== -1) {
|
|
389
|
-
usedApiCallIndices.add(originalIndex);
|
|
390
|
-
}
|
|
557
|
+
usedApiCallIndices.add(call._origIdx);
|
|
391
558
|
});
|
|
392
559
|
|
|
393
560
|
conv.apiCalls = matchedCalls;
|
|
@@ -403,6 +570,48 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
403
570
|
cost: matchedCalls.reduce((sum, c) => sum + c.cost, 0)
|
|
404
571
|
};
|
|
405
572
|
}
|
|
573
|
+
|
|
574
|
+
// Group unmatched API calls by day and add as synthetic entries
|
|
575
|
+
const unmatchedCalls = indexedApiCalls.filter(c => !usedApiCallIndices.has(c._origIdx));
|
|
576
|
+
if (unmatchedCalls.length > 0) {
|
|
577
|
+
const byDay = {};
|
|
578
|
+
for (const call of unmatchedCalls) {
|
|
579
|
+
const day = new Date(call.timestamp).toLocaleDateString('en-US');
|
|
580
|
+
if (!byDay[day]) byDay[day] = [];
|
|
581
|
+
byDay[day].push(call);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
for (const [day, calls] of Object.entries(byDay)) {
|
|
585
|
+
const firstTs = Math.min(...calls.map(c => c.timestamp));
|
|
586
|
+
const models = [...new Set(calls.map(c => c.model))];
|
|
587
|
+
const modelStr = models.length <= 3 ? models.join(', ') : `${models.slice(0, 2).join(', ')} +${models.length - 2}`;
|
|
588
|
+
conversations.push({
|
|
589
|
+
composerId: `unmatched-${day}`,
|
|
590
|
+
name: `Unmatched API calls (${day})`,
|
|
591
|
+
timestamp: firstTs,
|
|
592
|
+
messages: [],
|
|
593
|
+
messageCount: 0,
|
|
594
|
+
workspace: '(unmatched)',
|
|
595
|
+
model: modelStr,
|
|
596
|
+
contextTokensUsed: 0,
|
|
597
|
+
contextTokenLimit: 0,
|
|
598
|
+
totalLinesAdded: 0,
|
|
599
|
+
totalLinesRemoved: 0,
|
|
600
|
+
filesChangedCount: 0,
|
|
601
|
+
isUnmatched: true,
|
|
602
|
+
apiCalls: calls,
|
|
603
|
+
apiCallCount: calls.length,
|
|
604
|
+
apiTokens: {
|
|
605
|
+
inputWithCache: calls.reduce((s, c) => s + c.inputWithCache, 0),
|
|
606
|
+
inputWithoutCache: calls.reduce((s, c) => s + c.inputWithoutCache, 0),
|
|
607
|
+
cacheRead: calls.reduce((s, c) => s + c.cacheRead, 0),
|
|
608
|
+
outputTokens: calls.reduce((s, c) => s + c.outputTokens, 0),
|
|
609
|
+
totalTokens: calls.reduce((s, c) => s + c.totalTokens, 0),
|
|
610
|
+
cost: calls.reduce((s, c) => s + c.cost, 0)
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
}
|
|
406
615
|
} else {
|
|
407
616
|
// No API calls to match
|
|
408
617
|
conversations.forEach(conv => {
|
|
@@ -419,7 +628,7 @@ function extractConversations(startTime, endTime, apiCalls = []) {
|
|
|
419
628
|
});
|
|
420
629
|
}
|
|
421
630
|
|
|
422
|
-
db
|
|
631
|
+
dbClose(db);
|
|
423
632
|
return conversations;
|
|
424
633
|
} catch (error) {
|
|
425
634
|
console.error('Database error:', error.message);
|
|
@@ -487,7 +696,7 @@ function exportConversation(conv, index, dateStr) {
|
|
|
487
696
|
}
|
|
488
697
|
|
|
489
698
|
// Generate statistics
|
|
490
|
-
function generateStats(conversations, startOfDay, endOfDay) {
|
|
699
|
+
function generateStats(conversations, startOfDay, endOfDay, apiCalls = []) {
|
|
491
700
|
const daysDiff = Math.ceil((endOfDay - startOfDay) / (1000 * 60 * 60 * 24));
|
|
492
701
|
|
|
493
702
|
const stats = {
|
|
@@ -560,12 +769,30 @@ function generateStats(conversations, startOfDay, endOfDay) {
|
|
|
560
769
|
apiTokens: conv.apiTokens,
|
|
561
770
|
linesChanged: `+${conv.totalLinesAdded}/-${conv.totalLinesRemoved}`,
|
|
562
771
|
files: conv.filesChangedCount,
|
|
563
|
-
preview
|
|
772
|
+
preview,
|
|
773
|
+
exportedFile: conv.exportedFile || null,
|
|
774
|
+
isUnmatched: conv.isUnmatched || false
|
|
564
775
|
});
|
|
565
776
|
}
|
|
566
777
|
|
|
567
778
|
stats.conversations.sort((a, b) => a.timestamp - b.timestamp);
|
|
568
779
|
|
|
780
|
+
// Compute CSV totals (all API calls in range, regardless of matching)
|
|
781
|
+
if (apiCalls.length > 0) {
|
|
782
|
+
const csvTotals = { total: 0, onDemand: 0, included: 0, errored: 0, callCount: apiCalls.length };
|
|
783
|
+
for (const call of apiCalls) {
|
|
784
|
+
csvTotals.total += call.cost;
|
|
785
|
+
const kind = (call.kind || '').toLowerCase();
|
|
786
|
+
if (kind.includes('on-demand')) csvTotals.onDemand += call.cost;
|
|
787
|
+
else if (kind.includes('included')) csvTotals.included += call.cost;
|
|
788
|
+
else if (kind.includes('error')) csvTotals.errored += call.cost;
|
|
789
|
+
else csvTotals.total += 0; // already counted
|
|
790
|
+
}
|
|
791
|
+
stats.csvTotals = csvTotals;
|
|
792
|
+
stats.unmatchedApiCalls = csvTotals.callCount - stats.totalApiCalls;
|
|
793
|
+
stats.unmatchedCost = csvTotals.total - stats.totalApiTokens.cost;
|
|
794
|
+
}
|
|
795
|
+
|
|
569
796
|
return stats;
|
|
570
797
|
}
|
|
571
798
|
|
|
@@ -612,7 +839,8 @@ async function main() {
|
|
|
612
839
|
|
|
613
840
|
console.log('Extracting conversations...');
|
|
614
841
|
|
|
615
|
-
|
|
842
|
+
await loadDatabaseEngine();
|
|
843
|
+
const conversations = await extractConversations(startOfDay, endOfDay, apiCalls);
|
|
616
844
|
|
|
617
845
|
console.log(`Found ${conversations.length} conversations\n`);
|
|
618
846
|
|
|
@@ -623,11 +851,11 @@ async function main() {
|
|
|
623
851
|
|
|
624
852
|
console.log('Exporting conversations...');
|
|
625
853
|
conversations.forEach((conv, i) => {
|
|
626
|
-
exportConversation(conv, i + 1, dateStr);
|
|
854
|
+
conv.exportedFile = exportConversation(conv, i + 1, dateStr);
|
|
627
855
|
});
|
|
628
856
|
|
|
629
857
|
console.log('Generating statistics...');
|
|
630
|
-
const stats = generateStats(conversations, startOfDay, endOfDay);
|
|
858
|
+
const stats = generateStats(conversations, startOfDay, endOfDay, apiCalls);
|
|
631
859
|
|
|
632
860
|
console.log('Generating HTML report...');
|
|
633
861
|
try {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
CONVERSATION #55
|
|
3
|
+
Name: Skills reload
|
|
4
|
+
Workspace: uu_app_aicodingg01
|
|
5
|
+
Time: 2/7/2026, 8:35:54 AM
|
|
6
|
+
Model: claude-4.5-opus-high-thinking
|
|
7
|
+
Context Tokens: 25,471 / 164,000 (15.5%)
|
|
8
|
+
Changes: +0 -0 lines in 0 files
|
|
9
|
+
Messages: 2
|
|
10
|
+
Composer ID: 21f2687c-5206-41ff-8d02-bcfe24037482
|
|
11
|
+
|
|
12
|
+
API TOKEN USAGE (from dashboard export):
|
|
13
|
+
API Calls: 4
|
|
14
|
+
Input (w/ Cache Write): 105,264
|
|
15
|
+
Input (w/o Cache Write): 429
|
|
16
|
+
Cache Read: 1,429,552
|
|
17
|
+
Output Tokens: 8,366
|
|
18
|
+
Total API Tokens: 1,543,611
|
|
19
|
+
Cost: $1.97
|
|
20
|
+
|
|
21
|
+
================================================================================
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
--------------------------------------------------------------------------------
|
|
25
|
+
[USER] 8:35:54 AM
|
|
26
|
+
--------------------------------------------------------------------------------
|
|
27
|
+
reload skills
|
|
28
|
+
|
|
29
|
+
--------------------------------------------------------------------------------
|
|
30
|
+
[ASSISTANT] 8:35:54 AM
|
|
31
|
+
--------------------------------------------------------------------------------
|
|
32
|
+
Skills reloaded successfully. 12 skills are now available:
|
|
33
|
+
|
|
34
|
+
- `bookkit-add-page`
|
|
35
|
+
- `bookkit-list-pages`
|
|
36
|
+
- `bookkit-read`
|
|
37
|
+
- `bookkit-update`
|
|
38
|
+
- `business-chat`
|
|
39
|
+
- `mngkit-read`
|
|
40
|
+
- `mngkit-update`
|
|
41
|
+
- `sls-read`
|
|
42
|
+
- `validate`
|
|
43
|
+
- `convert`
|
|
44
|
+
- `wtm-worklog`
|
|
45
|
+
- `skill`
|
|
46
|
+
|
|
47
|
+
================================================================================
|
|
48
|
+
End of conversation
|
|
49
|
+
================================================================================
|