log-llm-config 1.0.7 → 1.0.9
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/dist/log_config_files.js +589 -32
- package/package.json +1 -1
package/dist/log_config_files.js
CHANGED
|
@@ -36,6 +36,11 @@ const JSON_FILE_PATHS = [
|
|
|
36
36
|
path: join(homedir(), 'Library', 'Application Support', 'Cursor', 'hooks.json'),
|
|
37
37
|
file_type: 'cursor_hooks',
|
|
38
38
|
},
|
|
39
|
+
// Project-level Cursor Cloud Agent environment config
|
|
40
|
+
{
|
|
41
|
+
path: join(PROJECT_ROOT, '.cursor', 'environment.json'),
|
|
42
|
+
file_type: 'cursor_cloud_agent_config',
|
|
43
|
+
},
|
|
39
44
|
// Project-level Claude settings (preferred over user)
|
|
40
45
|
{
|
|
41
46
|
path: join(PROJECT_ROOT, '.claude', 'settings.json'),
|
|
@@ -69,8 +74,22 @@ const CLAUDE_FILE_PATHS = [
|
|
|
69
74
|
];
|
|
70
75
|
// Claude rules directory
|
|
71
76
|
const CLAUDE_RULES_DIR = join(homedir(), '.claude', 'rules');
|
|
77
|
+
// Claude subagents directories
|
|
78
|
+
const CLAUDE_AGENTS_PROJECT_DIR = join(PROJECT_ROOT, '.claude', 'agents');
|
|
79
|
+
const CLAUDE_AGENTS_USER_DIR = join(homedir(), '.claude', 'agents');
|
|
72
80
|
// Cursor rules directory (project-level)
|
|
73
81
|
const CURSOR_RULES_DIR = join(PROJECT_ROOT, '.cursor', 'rules');
|
|
82
|
+
// Cursor commands directories
|
|
83
|
+
const CURSOR_COMMANDS_PROJECT_DIR = join(PROJECT_ROOT, '.cursor', 'commands');
|
|
84
|
+
const CURSOR_COMMANDS_USER_DIR = join(homedir(), '.cursor', 'commands');
|
|
85
|
+
// Cursor subagents directory (project-level)
|
|
86
|
+
const CURSOR_AGENTS_DIR = join(PROJECT_ROOT, '.cursor', 'agents');
|
|
87
|
+
// Plugin directories to scan
|
|
88
|
+
// Claude plugins can be in various locations (project, user home, etc.)
|
|
89
|
+
const PLUGIN_SEARCH_DIRS = [
|
|
90
|
+
PROJECT_ROOT, // Project-level plugins
|
|
91
|
+
join(homedir(), '.claude', 'plugins'), // User-level plugins (if this becomes a standard location)
|
|
92
|
+
];
|
|
74
93
|
/**
|
|
75
94
|
* Read and parse an MCP config file
|
|
76
95
|
*/
|
|
@@ -177,6 +196,90 @@ function getClaudeRuleFiles() {
|
|
|
177
196
|
}
|
|
178
197
|
return files;
|
|
179
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Get all command files from Cursor commands directories
|
|
201
|
+
* Collects from both project-level (.cursor/commands/) and user-level (~/.cursor/commands/)
|
|
202
|
+
*/
|
|
203
|
+
function getCursorCommandFiles() {
|
|
204
|
+
const files = [];
|
|
205
|
+
// Collect from project-level commands directory
|
|
206
|
+
try {
|
|
207
|
+
if (existsSync(CURSOR_COMMANDS_PROJECT_DIR)) {
|
|
208
|
+
const entries = readdirSync(CURSOR_COMMANDS_PROJECT_DIR, { withFileTypes: true });
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
211
|
+
files.push({
|
|
212
|
+
path: join(CURSOR_COMMANDS_PROJECT_DIR, entry.name),
|
|
213
|
+
file_type: 'cursor_command',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.warn(`Error reading Cursor project commands directory ${CURSOR_COMMANDS_PROJECT_DIR}:`, error instanceof Error ? error.message : String(error));
|
|
221
|
+
}
|
|
222
|
+
// Collect from user-level commands directory
|
|
223
|
+
try {
|
|
224
|
+
if (existsSync(CURSOR_COMMANDS_USER_DIR)) {
|
|
225
|
+
const entries = readdirSync(CURSOR_COMMANDS_USER_DIR, { withFileTypes: true });
|
|
226
|
+
for (const entry of entries) {
|
|
227
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
228
|
+
files.push({
|
|
229
|
+
path: join(CURSOR_COMMANDS_USER_DIR, entry.name),
|
|
230
|
+
file_type: 'cursor_command',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console.warn(`Error reading Cursor user commands directory ${CURSOR_COMMANDS_USER_DIR}:`, error instanceof Error ? error.message : String(error));
|
|
238
|
+
}
|
|
239
|
+
return files;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get all subagent files from Claude agents directories
|
|
243
|
+
* Collects from both project-level (.claude/agents/) and user-level (~/.claude/agents/)
|
|
244
|
+
*/
|
|
245
|
+
function getClaudeSubagentFiles() {
|
|
246
|
+
const files = [];
|
|
247
|
+
// Collect from project-level agents directory
|
|
248
|
+
try {
|
|
249
|
+
if (existsSync(CLAUDE_AGENTS_PROJECT_DIR)) {
|
|
250
|
+
const entries = readdirSync(CLAUDE_AGENTS_PROJECT_DIR, { withFileTypes: true });
|
|
251
|
+
for (const entry of entries) {
|
|
252
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
253
|
+
files.push({
|
|
254
|
+
path: join(CLAUDE_AGENTS_PROJECT_DIR, entry.name),
|
|
255
|
+
file_type: 'claude_subagent',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.warn(`Error reading Claude project agents directory ${CLAUDE_AGENTS_PROJECT_DIR}:`, error instanceof Error ? error.message : String(error));
|
|
263
|
+
}
|
|
264
|
+
// Collect from user-level agents directory
|
|
265
|
+
try {
|
|
266
|
+
if (existsSync(CLAUDE_AGENTS_USER_DIR)) {
|
|
267
|
+
const entries = readdirSync(CLAUDE_AGENTS_USER_DIR, { withFileTypes: true });
|
|
268
|
+
for (const entry of entries) {
|
|
269
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
270
|
+
files.push({
|
|
271
|
+
path: join(CLAUDE_AGENTS_USER_DIR, entry.name),
|
|
272
|
+
file_type: 'claude_subagent',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
console.warn(`Error reading Claude user agents directory ${CLAUDE_AGENTS_USER_DIR}:`, error instanceof Error ? error.message : String(error));
|
|
280
|
+
}
|
|
281
|
+
return files;
|
|
282
|
+
}
|
|
180
283
|
/**
|
|
181
284
|
* Get all rule files from Cursor rules directory
|
|
182
285
|
* Supports new format (.cursor/rules/rule-name/RULE.md) and legacy format (.cursor/rules/*.mdc or *.md)
|
|
@@ -263,6 +366,167 @@ function getCursorRulesFile(projectRoot) {
|
|
|
263
366
|
}
|
|
264
367
|
return files;
|
|
265
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Find all Claude plugin directories by scanning for .claude-plugin/plugin.json files
|
|
371
|
+
* Searches in project root and common plugin locations
|
|
372
|
+
*/
|
|
373
|
+
function findClaudePluginDirectories() {
|
|
374
|
+
const plugins = [];
|
|
375
|
+
// Search in project root and subdirectories (up to 3 levels deep)
|
|
376
|
+
const searchDirs = [PROJECT_ROOT];
|
|
377
|
+
// Also check common plugin locations
|
|
378
|
+
const commonPluginPaths = [
|
|
379
|
+
join(PROJECT_ROOT, 'plugins'),
|
|
380
|
+
join(PROJECT_ROOT, '.plugins'),
|
|
381
|
+
join(homedir(), '.claude', 'plugins'),
|
|
382
|
+
];
|
|
383
|
+
for (const searchPath of [...searchDirs, ...commonPluginPaths]) {
|
|
384
|
+
if (!existsSync(searchPath)) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
// Recursively search for .claude-plugin directories
|
|
389
|
+
const findPluginDirs = (dir, depth = 0) => {
|
|
390
|
+
if (depth > 3)
|
|
391
|
+
return; // Limit recursion depth
|
|
392
|
+
// Skip common directories that shouldn't contain plugins
|
|
393
|
+
const skipDirs = ['node_modules', '.git', '.venv', 'venv', 'dist', 'build', '.next', '.cache'];
|
|
394
|
+
const dirName = path.basename(dir);
|
|
395
|
+
if (skipDirs.includes(dirName)) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
400
|
+
for (const entry of entries) {
|
|
401
|
+
if (entry.isDirectory()) {
|
|
402
|
+
// Check if this is a .claude-plugin directory
|
|
403
|
+
if (entry.name === '.claude-plugin') {
|
|
404
|
+
const manifestPath = join(dir, '.claude-plugin', 'plugin.json');
|
|
405
|
+
if (existsSync(manifestPath)) {
|
|
406
|
+
plugins.push({
|
|
407
|
+
pluginDir: dir,
|
|
408
|
+
manifestPath: manifestPath,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
// Don't recurse into .claude-plugin directory
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
// Skip hidden directories (except .claude-plugin which we already handled)
|
|
415
|
+
// and common build/cache directories
|
|
416
|
+
if (entry.name.startsWith('.') || skipDirs.includes(entry.name)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
// Recursively search subdirectories
|
|
420
|
+
findPluginDirs(join(dir, entry.name), depth + 1);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
// Skip directories we can't read
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
findPluginDirs(searchPath);
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
// Skip if we can't read the directory
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return plugins;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Get Claude plugin manifest files (.claude-plugin/plugin.json)
|
|
438
|
+
*/
|
|
439
|
+
function getClaudePluginManifests() {
|
|
440
|
+
const files = [];
|
|
441
|
+
const plugins = findClaudePluginDirectories();
|
|
442
|
+
for (const { pluginDir, manifestPath } of plugins) {
|
|
443
|
+
files.push({
|
|
444
|
+
path: manifestPath,
|
|
445
|
+
file_type: 'claude_plugin_manifest',
|
|
446
|
+
pluginDir: pluginDir,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
return files;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Get command files from a plugin directory (commands/*.md)
|
|
453
|
+
*/
|
|
454
|
+
function getPluginCommandFiles(pluginDir) {
|
|
455
|
+
const files = [];
|
|
456
|
+
// pluginDir is the directory containing .claude-plugin, so commands are at pluginDir/.claude-plugin/commands
|
|
457
|
+
const commandsDir = join(pluginDir, '.claude-plugin', 'commands');
|
|
458
|
+
try {
|
|
459
|
+
if (!existsSync(commandsDir)) {
|
|
460
|
+
return files;
|
|
461
|
+
}
|
|
462
|
+
const entries = readdirSync(commandsDir, { withFileTypes: true });
|
|
463
|
+
for (const entry of entries) {
|
|
464
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
465
|
+
files.push({
|
|
466
|
+
path: join(commandsDir, entry.name),
|
|
467
|
+
file_type: 'claude_plugin_command',
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
console.warn(`Error reading plugin commands directory ${commandsDir}:`, error instanceof Error ? error.message : String(error));
|
|
474
|
+
}
|
|
475
|
+
return files;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Get hook files from a plugin directory (hooks/hooks.json)
|
|
479
|
+
*/
|
|
480
|
+
function getPluginHookFiles(pluginDir) {
|
|
481
|
+
const files = [];
|
|
482
|
+
// pluginDir is the directory containing .claude-plugin, so hooks are at pluginDir/.claude-plugin/hooks/hooks.json
|
|
483
|
+
const hooksPath = join(pluginDir, '.claude-plugin', 'hooks', 'hooks.json');
|
|
484
|
+
if (existsSync(hooksPath)) {
|
|
485
|
+
files.push({
|
|
486
|
+
path: hooksPath,
|
|
487
|
+
file_type: 'claude_plugin_hooks',
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
return files;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Get MCP config files from a plugin directory (.mcp.json)
|
|
494
|
+
*/
|
|
495
|
+
function getPluginAgentFiles(pluginDir) {
|
|
496
|
+
const files = [];
|
|
497
|
+
// pluginDir is the directory containing .claude-plugin, so agents are at pluginDir/.claude-plugin/agents/*.md
|
|
498
|
+
const agentsDir = join(pluginDir, '.claude-plugin', 'agents');
|
|
499
|
+
try {
|
|
500
|
+
if (!existsSync(agentsDir)) {
|
|
501
|
+
return files;
|
|
502
|
+
}
|
|
503
|
+
const entries = readdirSync(agentsDir, { withFileTypes: true });
|
|
504
|
+
for (const entry of entries) {
|
|
505
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
506
|
+
files.push({
|
|
507
|
+
path: join(agentsDir, entry.name),
|
|
508
|
+
file_type: 'claude_subagent',
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
console.warn(`Error reading plugin agents directory ${agentsDir}:`, error instanceof Error ? error.message : String(error));
|
|
515
|
+
}
|
|
516
|
+
return files;
|
|
517
|
+
}
|
|
518
|
+
function getPluginMcpFiles(pluginDir) {
|
|
519
|
+
const files = [];
|
|
520
|
+
// pluginDir is the directory containing .claude-plugin, so MCP config is at pluginDir/.claude-plugin/.mcp.json
|
|
521
|
+
const mcpPath = join(pluginDir, '.claude-plugin', '.mcp.json');
|
|
522
|
+
if (existsSync(mcpPath)) {
|
|
523
|
+
files.push({
|
|
524
|
+
path: mcpPath,
|
|
525
|
+
file_type: 'claude_plugin_mcp',
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return files;
|
|
529
|
+
}
|
|
266
530
|
/**
|
|
267
531
|
* Get subdirectory Claude files for monorepo support
|
|
268
532
|
* Checks immediate subdirectories (1 level deep) for CLAUDE.md files
|
|
@@ -293,7 +557,8 @@ function getSubdirectoryClaudeFiles(projectRoot) {
|
|
|
293
557
|
return files;
|
|
294
558
|
}
|
|
295
559
|
/**
|
|
296
|
-
* Read
|
|
560
|
+
* Read state data from Cursor's state.vscdb SQLite database
|
|
561
|
+
* Extracts composerState, extensionsAssistant, and extensions data
|
|
297
562
|
*/
|
|
298
563
|
function readVSCDBState() {
|
|
299
564
|
try {
|
|
@@ -308,34 +573,128 @@ function readVSCDBState() {
|
|
|
308
573
|
console.warn('sqlite3 command not found; skipping state.vscdb reading');
|
|
309
574
|
return null;
|
|
310
575
|
}
|
|
576
|
+
const stateData = {};
|
|
311
577
|
// Query the SQLite database for composerState
|
|
312
578
|
// Escape single quotes in the key for SQL safety
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
const result = execSync(`sqlite3 "${VSCDB_PATH}" "${query}"`, {
|
|
317
|
-
encoding: 'utf8',
|
|
318
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
319
|
-
}).trim();
|
|
320
|
-
if (!result || result === '') {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
// Parse the JSON value
|
|
324
|
-
let parsed;
|
|
579
|
+
const composerStateKey = "src.vs.platform.reactivestorage.browser.reactiveStorageServiceImpl.persistentStorage.applicationUser";
|
|
580
|
+
const escapedComposerKey = composerStateKey.replace(/'/g, "''");
|
|
581
|
+
const composerQuery = `SELECT value FROM ItemTable WHERE key='${escapedComposerKey}'`;
|
|
325
582
|
try {
|
|
326
|
-
|
|
583
|
+
const composerResult = execSync(`sqlite3 "${VSCDB_PATH}" "${composerQuery}"`, {
|
|
584
|
+
encoding: 'utf8',
|
|
585
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
586
|
+
}).trim();
|
|
587
|
+
if (composerResult && composerResult !== '') {
|
|
588
|
+
try {
|
|
589
|
+
const parsed = JSON.parse(composerResult);
|
|
590
|
+
// Extract composerState from the nested structure
|
|
591
|
+
const composerState = parsed?.composerState || parsed;
|
|
592
|
+
if (composerState && typeof composerState === 'object') {
|
|
593
|
+
stateData.composerState = composerState;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
catch (parseError) {
|
|
597
|
+
console.warn(`Failed to parse composerState from state.vscdb:`, parseError instanceof Error ? parseError.message : String(parseError));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
327
600
|
}
|
|
328
|
-
catch (
|
|
329
|
-
console.
|
|
330
|
-
return null;
|
|
601
|
+
catch (error) {
|
|
602
|
+
console.warn(`Error reading composerState from state.vscdb:`, error instanceof Error ? error.message : String(error));
|
|
331
603
|
}
|
|
332
|
-
//
|
|
333
|
-
// The
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
604
|
+
// Query for extensionsAssistant data
|
|
605
|
+
// The actual key in state.vscdb is "extensionsAssistant/recommendations" (with forward slash)
|
|
606
|
+
const extensionKeys = [
|
|
607
|
+
"extensionsAssistant/recommendations", // Actual key format in state.vscdb
|
|
608
|
+
"extensionsAssistant.recommendations",
|
|
609
|
+
"workbench.extensionsAssistant.recommendations",
|
|
610
|
+
];
|
|
611
|
+
for (const key of extensionKeys) {
|
|
612
|
+
try {
|
|
613
|
+
const escapedKey = key.replace(/'/g, "''");
|
|
614
|
+
const query = `SELECT value FROM ItemTable WHERE key='${escapedKey}'`;
|
|
615
|
+
const result = execSync(`sqlite3 "${VSCDB_PATH}" "${query}"`, {
|
|
616
|
+
encoding: 'utf8',
|
|
617
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
618
|
+
}).trim();
|
|
619
|
+
if (result && result !== '') {
|
|
620
|
+
try {
|
|
621
|
+
const parsed = JSON.parse(result);
|
|
622
|
+
// The value is an object with extension IDs as keys and timestamps as values
|
|
623
|
+
// Convert to array of extension IDs for the recommendations path
|
|
624
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
625
|
+
const extensionIds = Object.keys(parsed);
|
|
626
|
+
stateData.extensionsAssistant = { recommendations: extensionIds };
|
|
627
|
+
}
|
|
628
|
+
else if (Array.isArray(parsed)) {
|
|
629
|
+
stateData.extensionsAssistant = { recommendations: parsed };
|
|
630
|
+
}
|
|
631
|
+
break; // Found it, stop searching
|
|
632
|
+
}
|
|
633
|
+
catch (parseError) {
|
|
634
|
+
// Continue to next key
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
catch (error) {
|
|
639
|
+
// Continue to next key
|
|
640
|
+
}
|
|
337
641
|
}
|
|
338
|
-
|
|
642
|
+
// Query for extensions.trustedPublishers data
|
|
643
|
+
// The actual key in state.vscdb is "extensions.trustedPublishers"
|
|
644
|
+
const trustedPublisherKeys = [
|
|
645
|
+
"extensions.trustedPublishers", // Actual key format in state.vscdb
|
|
646
|
+
"workbench.extensions.trustedPublishers",
|
|
647
|
+
];
|
|
648
|
+
for (const key of trustedPublisherKeys) {
|
|
649
|
+
try {
|
|
650
|
+
const escapedKey = key.replace(/'/g, "''");
|
|
651
|
+
const query = `SELECT value FROM ItemTable WHERE key='${escapedKey}'`;
|
|
652
|
+
const result = execSync(`sqlite3 "${VSCDB_PATH}" "${query}"`, {
|
|
653
|
+
encoding: 'utf8',
|
|
654
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
655
|
+
}).trim();
|
|
656
|
+
if (result && result !== '') {
|
|
657
|
+
try {
|
|
658
|
+
const parsed = JSON.parse(result);
|
|
659
|
+
// The value is an object with publisher IDs as keys and publisher info as values
|
|
660
|
+
// Extract publisher IDs/names for the trustedPublishers array
|
|
661
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
662
|
+
// Extract publisher IDs from the object keys
|
|
663
|
+
const publisherIds = Object.keys(parsed);
|
|
664
|
+
stateData.extensions = { trustedPublishers: publisherIds };
|
|
665
|
+
}
|
|
666
|
+
else if (Array.isArray(parsed)) {
|
|
667
|
+
stateData.extensions = { trustedPublishers: parsed };
|
|
668
|
+
}
|
|
669
|
+
break; // Found it, stop searching
|
|
670
|
+
}
|
|
671
|
+
catch (parseError) {
|
|
672
|
+
// Continue to next key
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
// Continue to next key
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// If we found composerState, also check if extensionsAssistant/extensions are nested within it
|
|
681
|
+
if (stateData.composerState && typeof stateData.composerState === 'object') {
|
|
682
|
+
const composerState = stateData.composerState;
|
|
683
|
+
// Check if extensionsAssistant is in composerState
|
|
684
|
+
if (composerState.extensionsAssistant && !stateData.extensionsAssistant) {
|
|
685
|
+
stateData.extensionsAssistant = composerState.extensionsAssistant;
|
|
686
|
+
}
|
|
687
|
+
// Check if extensions is in composerState
|
|
688
|
+
if (composerState.extensions && !stateData.extensions) {
|
|
689
|
+
stateData.extensions = composerState.extensions;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
// Return the combined state data (composerState, extensionsAssistant, extensions)
|
|
693
|
+
// If we have any data, return it; otherwise return null
|
|
694
|
+
if (Object.keys(stateData).length > 0) {
|
|
695
|
+
return stateData;
|
|
696
|
+
}
|
|
697
|
+
return null;
|
|
339
698
|
}
|
|
340
699
|
catch (error) {
|
|
341
700
|
console.error(`Error reading state.vscdb:`, error instanceof Error ? error.message : String(error));
|
|
@@ -381,15 +740,51 @@ function collectConfigFiles() {
|
|
|
381
740
|
const vscdbState = readVSCDBState();
|
|
382
741
|
if (vscdbState) {
|
|
383
742
|
console.log(`Found Cursor state data at: ${VSCDB_PATH}`);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
743
|
+
// Store composerState as vscode_settings (general VS Code/Cursor state)
|
|
744
|
+
if (vscdbState.composerState) {
|
|
745
|
+
configFiles.push({
|
|
746
|
+
file_type: 'vscode_settings',
|
|
747
|
+
file_path: `${VSCDB_PATH}#composerState`, // Use fragment to distinguish
|
|
748
|
+
raw_content: {
|
|
749
|
+
composerState: vscdbState.composerState,
|
|
750
|
+
source: 'state.vscdb',
|
|
751
|
+
extracted_at: new Date().toISOString(),
|
|
752
|
+
},
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
// Store extensionsAssistant.recommendations as cursor_extensions
|
|
756
|
+
// (same type as .vscode/extensions.json recommendations)
|
|
757
|
+
// Structure must match evaluation plan path: extensionsAssistant.recommendations
|
|
758
|
+
const extensionsAssistant = vscdbState.extensionsAssistant;
|
|
759
|
+
if (extensionsAssistant?.recommendations && Array.isArray(extensionsAssistant.recommendations)) {
|
|
760
|
+
configFiles.push({
|
|
761
|
+
file_type: 'cursor_extensions',
|
|
762
|
+
file_path: `${VSCDB_PATH}#extensionsAssistant.recommendations`, // Use fragment to distinguish
|
|
763
|
+
raw_content: {
|
|
764
|
+
extensionsAssistant: {
|
|
765
|
+
recommendations: extensionsAssistant.recommendations,
|
|
766
|
+
},
|
|
767
|
+
source: 'state.vscdb',
|
|
768
|
+
extracted_at: new Date().toISOString(),
|
|
769
|
+
},
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
// Store extensions.trustedPublishers as cursor_extensions
|
|
773
|
+
// (extension-related data, should be checked by extension policies)
|
|
774
|
+
const extensions = vscdbState.extensions;
|
|
775
|
+
if (extensions?.trustedPublishers && Array.isArray(extensions.trustedPublishers)) {
|
|
776
|
+
configFiles.push({
|
|
777
|
+
file_type: 'cursor_extensions',
|
|
778
|
+
file_path: `${VSCDB_PATH}#extensions.trustedPublishers`, // Use fragment to distinguish
|
|
779
|
+
raw_content: {
|
|
780
|
+
extensions: {
|
|
781
|
+
trustedPublishers: extensions.trustedPublishers,
|
|
782
|
+
},
|
|
783
|
+
source: 'state.vscdb',
|
|
784
|
+
extracted_at: new Date().toISOString(),
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
}
|
|
393
788
|
}
|
|
394
789
|
// Read Claude configuration files (project root, local overrides, home directory)
|
|
395
790
|
for (const { path, file_type } of CLAUDE_FILE_PATHS) {
|
|
@@ -474,6 +869,48 @@ function collectConfigFiles() {
|
|
|
474
869
|
console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
475
870
|
}
|
|
476
871
|
}
|
|
872
|
+
// Read Claude subagent files from .claude/agents/*.md and ~/.claude/agents/*.md
|
|
873
|
+
const claudeSubagentFiles = getClaudeSubagentFiles();
|
|
874
|
+
for (const { path, file_type } of claudeSubagentFiles) {
|
|
875
|
+
try {
|
|
876
|
+
const markdownContent = readMarkdownFile(path);
|
|
877
|
+
if (markdownContent !== null) { // Log even if empty
|
|
878
|
+
console.log(`Found ${file_type} at: ${path}`);
|
|
879
|
+
configFiles.push({
|
|
880
|
+
file_type,
|
|
881
|
+
file_path: path,
|
|
882
|
+
raw_content: {
|
|
883
|
+
content: markdownContent,
|
|
884
|
+
source: 'claude_subagent_file',
|
|
885
|
+
},
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
catch (error) {
|
|
890
|
+
console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// Read Cursor command files from .cursor/commands/*.md and ~/.cursor/commands/*.md
|
|
894
|
+
const cursorCommandFiles = getCursorCommandFiles();
|
|
895
|
+
for (const { path, file_type } of cursorCommandFiles) {
|
|
896
|
+
try {
|
|
897
|
+
const markdownContent = readMarkdownFile(path);
|
|
898
|
+
if (markdownContent !== null) { // Log even if empty
|
|
899
|
+
console.log(`Found ${file_type} at: ${path}`);
|
|
900
|
+
configFiles.push({
|
|
901
|
+
file_type,
|
|
902
|
+
file_path: path,
|
|
903
|
+
raw_content: {
|
|
904
|
+
content: markdownContent,
|
|
905
|
+
source: 'cursor_command_file',
|
|
906
|
+
},
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
catch (error) {
|
|
911
|
+
console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
477
914
|
// Read Cursor rule files from .cursor/rules/ (new format: folders with RULE.md, legacy: .mdc/.md files)
|
|
478
915
|
const cursorRuleFiles = getCursorRuleFiles();
|
|
479
916
|
for (const { path, file_type } of cursorRuleFiles) {
|
|
@@ -537,6 +974,126 @@ function collectConfigFiles() {
|
|
|
537
974
|
console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
538
975
|
}
|
|
539
976
|
}
|
|
977
|
+
// Read Cursor extension recommendations (.vscode/extensions.json)
|
|
978
|
+
const extensionsJsonPath = join(PROJECT_ROOT, '.vscode', 'extensions.json');
|
|
979
|
+
if (existsSync(extensionsJsonPath)) {
|
|
980
|
+
try {
|
|
981
|
+
const extensionsContent = readJSONFile(extensionsJsonPath);
|
|
982
|
+
if (extensionsContent) {
|
|
983
|
+
console.log(`Found Cursor extensions.json at: ${extensionsJsonPath}`);
|
|
984
|
+
configFiles.push({
|
|
985
|
+
file_type: 'cursor_extensions',
|
|
986
|
+
file_path: extensionsJsonPath,
|
|
987
|
+
raw_content: extensionsContent,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
catch (error) {
|
|
992
|
+
console.warn(`Error reading ${extensionsJsonPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
// Read Claude plugin manifests and related files
|
|
996
|
+
const pluginManifests = getClaudePluginManifests();
|
|
997
|
+
for (const { path, file_type, pluginDir } of pluginManifests) {
|
|
998
|
+
try {
|
|
999
|
+
const manifestContent = readJSONFile(path);
|
|
1000
|
+
if (manifestContent) {
|
|
1001
|
+
configFiles.push({
|
|
1002
|
+
file_type,
|
|
1003
|
+
file_path: path,
|
|
1004
|
+
raw_content: manifestContent,
|
|
1005
|
+
});
|
|
1006
|
+
// Collect plugin command files (commands/*.md)
|
|
1007
|
+
const commandFiles = getPluginCommandFiles(pluginDir);
|
|
1008
|
+
for (const { path: cmdPath, file_type: cmdFileType } of commandFiles) {
|
|
1009
|
+
try {
|
|
1010
|
+
const cmdContent = readMarkdownFile(cmdPath);
|
|
1011
|
+
if (cmdContent !== null) {
|
|
1012
|
+
configFiles.push({
|
|
1013
|
+
file_type: cmdFileType,
|
|
1014
|
+
file_path: cmdPath,
|
|
1015
|
+
raw_content: {
|
|
1016
|
+
content: cmdContent,
|
|
1017
|
+
source: 'claude_rule_file', // Plugin command files are .md rule files
|
|
1018
|
+
pluginDir: pluginDir,
|
|
1019
|
+
pluginCommandFile: true, // Additional flag to identify as plugin command
|
|
1020
|
+
},
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
catch (error) {
|
|
1025
|
+
console.warn(`Error reading plugin command ${cmdPath}:`, error instanceof Error ? error.message : String(error));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
// Collect plugin hook files (hooks/hooks.json)
|
|
1029
|
+
const hookFiles = getPluginHookFiles(pluginDir);
|
|
1030
|
+
for (const { path: hookPath, file_type: hookFileType } of hookFiles) {
|
|
1031
|
+
try {
|
|
1032
|
+
const hookContent = readJSONFile(hookPath);
|
|
1033
|
+
if (hookContent) {
|
|
1034
|
+
configFiles.push({
|
|
1035
|
+
file_type: hookFileType,
|
|
1036
|
+
file_path: hookPath,
|
|
1037
|
+
raw_content: {
|
|
1038
|
+
...hookContent,
|
|
1039
|
+
source: 'claude_plugin_hooks_file',
|
|
1040
|
+
pluginDir: pluginDir,
|
|
1041
|
+
},
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
console.warn(`Error reading plugin hooks ${hookPath}:`, error instanceof Error ? error.message : String(error));
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// Collect plugin agent files (agents/*.md)
|
|
1050
|
+
const agentFiles = getPluginAgentFiles(pluginDir);
|
|
1051
|
+
for (const { path: agentPath, file_type: agentFileType } of agentFiles) {
|
|
1052
|
+
try {
|
|
1053
|
+
const agentContent = readMarkdownFile(agentPath);
|
|
1054
|
+
if (agentContent !== null) {
|
|
1055
|
+
configFiles.push({
|
|
1056
|
+
file_type: agentFileType,
|
|
1057
|
+
file_path: agentPath,
|
|
1058
|
+
raw_content: {
|
|
1059
|
+
content: agentContent,
|
|
1060
|
+
source: 'claude_plugin_agent_file',
|
|
1061
|
+
pluginDir: pluginDir,
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
catch (error) {
|
|
1067
|
+
console.warn(`Error reading plugin agent ${agentPath}:`, error instanceof Error ? error.message : String(error));
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
// Collect plugin MCP config files (.mcp.json)
|
|
1071
|
+
const mcpFiles = getPluginMcpFiles(pluginDir);
|
|
1072
|
+
for (const { path: mcpPath, file_type: mcpFileType } of mcpFiles) {
|
|
1073
|
+
try {
|
|
1074
|
+
const mcpContent = readJSONFile(mcpPath);
|
|
1075
|
+
if (mcpContent) {
|
|
1076
|
+
configFiles.push({
|
|
1077
|
+
file_type: mcpFileType,
|
|
1078
|
+
file_path: mcpPath,
|
|
1079
|
+
raw_content: {
|
|
1080
|
+
...mcpContent,
|
|
1081
|
+
source: 'claude_plugin_mcp_file',
|
|
1082
|
+
pluginDir: pluginDir,
|
|
1083
|
+
},
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
catch (error) {
|
|
1088
|
+
console.warn(`Error reading plugin MCP config ${mcpPath}:`, error instanceof Error ? error.message : String(error));
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
catch (error) {
|
|
1094
|
+
console.warn(`Error reading plugin manifest ${path}:`, error instanceof Error ? error.message : String(error));
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
540
1097
|
return configFiles;
|
|
541
1098
|
}
|
|
542
1099
|
/**
|