log-llm-config 1.0.5 → 1.0.7

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
3
3
  import { join, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { homedir } from 'node:os';
@@ -49,6 +49,28 @@ const JSON_FILE_PATHS = [
49
49
  ];
50
50
  // VS Code/Cursor state database path
51
51
  const VSCDB_PATH = join(homedir(), 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'state.vscdb');
52
+ // Claude configuration file paths
53
+ const CLAUDE_FILE_PATHS = [
54
+ // Project Root
55
+ {
56
+ path: join(PROJECT_ROOT, 'CLAUDE.md'),
57
+ file_type: 'claude_rule_config',
58
+ },
59
+ // Local Overrides (project-level)
60
+ {
61
+ path: join(PROJECT_ROOT, 'CLAUDE.local.md'),
62
+ file_type: 'claude_rule_config',
63
+ },
64
+ // Home Directory
65
+ {
66
+ path: join(homedir(), '.claude', 'CLAUDE.md'),
67
+ file_type: 'claude_rule_config',
68
+ },
69
+ ];
70
+ // Claude rules directory
71
+ const CLAUDE_RULES_DIR = join(homedir(), '.claude', 'rules');
72
+ // Cursor rules directory (project-level)
73
+ const CURSOR_RULES_DIR = join(PROJECT_ROOT, '.cursor', 'rules');
52
74
  /**
53
75
  * Read and parse an MCP config file
54
76
  */
@@ -86,6 +108,190 @@ function readJSONFile(filePath) {
86
108
  return null;
87
109
  }
88
110
  }
111
+ /**
112
+ * Read a markdown/text file and return as content string
113
+ */
114
+ function readMarkdownFile(filePath) {
115
+ try {
116
+ if (!existsSync(filePath)) {
117
+ return null;
118
+ }
119
+ const content = readFileSync(filePath, 'utf-8');
120
+ return content;
121
+ }
122
+ catch (error) {
123
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
124
+ console.warn(`Permission denied reading ${filePath} (may require elevated permissions)`);
125
+ }
126
+ return null;
127
+ }
128
+ }
129
+ /**
130
+ * Get parent directory paths for monorepo support
131
+ * Checks parent directories up to 5 levels for CLAUDE.md files
132
+ */
133
+ function getParentClaudeFiles(projectRoot) {
134
+ const files = [];
135
+ let currentPath = projectRoot;
136
+ let levels = 0;
137
+ const maxLevels = 5; // Limit to prevent infinite loops
138
+ while (levels < maxLevels) {
139
+ const parentPath = path.dirname(currentPath);
140
+ if (parentPath === currentPath) {
141
+ // Reached filesystem root
142
+ break;
143
+ }
144
+ const claudePath = join(parentPath, 'CLAUDE.md');
145
+ if (existsSync(claudePath)) {
146
+ files.push({
147
+ path: claudePath,
148
+ file_type: 'claude_rule_config',
149
+ });
150
+ }
151
+ currentPath = parentPath;
152
+ levels++;
153
+ }
154
+ return files;
155
+ }
156
+ /**
157
+ * Get all markdown files from Claude rules directory
158
+ */
159
+ function getClaudeRuleFiles() {
160
+ const files = [];
161
+ try {
162
+ if (!existsSync(CLAUDE_RULES_DIR)) {
163
+ return files;
164
+ }
165
+ const entries = readdirSync(CLAUDE_RULES_DIR, { withFileTypes: true });
166
+ for (const entry of entries) {
167
+ if (entry.isFile() && entry.name.endsWith('.md')) {
168
+ files.push({
169
+ path: join(CLAUDE_RULES_DIR, entry.name),
170
+ file_type: 'claude_rule',
171
+ });
172
+ }
173
+ }
174
+ }
175
+ catch (error) {
176
+ console.warn(`Error reading Claude rules directory ${CLAUDE_RULES_DIR}:`, error instanceof Error ? error.message : String(error));
177
+ }
178
+ return files;
179
+ }
180
+ /**
181
+ * Get all rule files from Cursor rules directory
182
+ * Supports new format (.cursor/rules/rule-name/RULE.md) and legacy format (.cursor/rules/*.mdc or *.md)
183
+ */
184
+ function getCursorRuleFiles() {
185
+ const files = [];
186
+ try {
187
+ if (!existsSync(CURSOR_RULES_DIR)) {
188
+ return files;
189
+ }
190
+ const entries = readdirSync(CURSOR_RULES_DIR, { withFileTypes: true });
191
+ for (const entry of entries) {
192
+ if (entry.isDirectory()) {
193
+ // New format: rule folder containing RULE.md
194
+ const ruleMdPath = join(CURSOR_RULES_DIR, entry.name, 'RULE.md');
195
+ if (existsSync(ruleMdPath)) {
196
+ files.push({
197
+ path: ruleMdPath,
198
+ file_type: 'cursor_rule',
199
+ });
200
+ }
201
+ }
202
+ else if (entry.isFile()) {
203
+ // Legacy format: flat .mdc or .md files
204
+ if (entry.name.endsWith('.mdc') || entry.name.endsWith('.md')) {
205
+ files.push({
206
+ path: join(CURSOR_RULES_DIR, entry.name),
207
+ file_type: 'cursor_rule',
208
+ });
209
+ }
210
+ }
211
+ }
212
+ }
213
+ catch (error) {
214
+ console.warn(`Error reading Cursor rules directory ${CURSOR_RULES_DIR}:`, error instanceof Error ? error.message : String(error));
215
+ }
216
+ return files;
217
+ }
218
+ /**
219
+ * Get AGENTS.md files from project root and subdirectories
220
+ * Supports nested AGENTS.md files as per Cursor documentation
221
+ */
222
+ function getAgentsMdFiles(projectRoot) {
223
+ const files = [];
224
+ // Check project root
225
+ const rootAgentsMd = join(projectRoot, 'AGENTS.md');
226
+ if (existsSync(rootAgentsMd)) {
227
+ files.push({
228
+ path: rootAgentsMd,
229
+ file_type: 'cursor_rule',
230
+ });
231
+ }
232
+ // Check subdirectories (1 level deep for now, can be extended)
233
+ try {
234
+ const entries = readdirSync(projectRoot, { withFileTypes: true });
235
+ for (const entry of entries) {
236
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
237
+ const subdirAgentsMd = join(projectRoot, entry.name, 'AGENTS.md');
238
+ if (existsSync(subdirAgentsMd)) {
239
+ files.push({
240
+ path: subdirAgentsMd,
241
+ file_type: 'cursor_rule',
242
+ });
243
+ }
244
+ }
245
+ }
246
+ }
247
+ catch (error) {
248
+ console.warn(`Error reading subdirectories for AGENTS.md in ${projectRoot}:`, error instanceof Error ? error.message : String(error));
249
+ }
250
+ return files;
251
+ }
252
+ /**
253
+ * Get .cursorrules file from project root (legacy format, deprecated but still supported)
254
+ */
255
+ function getCursorRulesFile(projectRoot) {
256
+ const files = [];
257
+ const cursorRulesPath = join(projectRoot, '.cursorrules');
258
+ if (existsSync(cursorRulesPath)) {
259
+ files.push({
260
+ path: cursorRulesPath,
261
+ file_type: 'cursor_rule',
262
+ });
263
+ }
264
+ return files;
265
+ }
266
+ /**
267
+ * Get subdirectory Claude files for monorepo support
268
+ * Checks immediate subdirectories (1 level deep) for CLAUDE.md files
269
+ */
270
+ function getSubdirectoryClaudeFiles(projectRoot) {
271
+ const files = [];
272
+ try {
273
+ if (!existsSync(projectRoot)) {
274
+ return files;
275
+ }
276
+ const entries = readdirSync(projectRoot, { withFileTypes: true });
277
+ for (const entry of entries) {
278
+ // Only check immediate subdirectories (not files, not hidden dirs like .git, node_modules, etc.)
279
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
280
+ const claudePath = join(projectRoot, entry.name, 'CLAUDE.md');
281
+ if (existsSync(claudePath)) {
282
+ files.push({
283
+ path: claudePath,
284
+ file_type: 'claude_rule_config',
285
+ });
286
+ }
287
+ }
288
+ }
289
+ }
290
+ catch (error) {
291
+ console.warn(`Error reading subdirectories in ${projectRoot}:`, error instanceof Error ? error.message : String(error));
292
+ }
293
+ return files;
294
+ }
89
295
  /**
90
296
  * Read composerState from Cursor's state.vscdb SQLite database
91
297
  */
@@ -185,6 +391,152 @@ function collectConfigFiles() {
185
391
  },
186
392
  });
187
393
  }
394
+ // Read Claude configuration files (project root, local overrides, home directory)
395
+ for (const { path, file_type } of CLAUDE_FILE_PATHS) {
396
+ try {
397
+ const markdownContent = readMarkdownFile(path);
398
+ if (markdownContent !== null) { // Log even if empty (null means file doesn't exist or error)
399
+ console.log(`Found ${file_type} at: ${path}`);
400
+ configFiles.push({
401
+ file_type,
402
+ file_path: path,
403
+ raw_content: {
404
+ content: markdownContent,
405
+ source: 'claude_rule_config_file',
406
+ },
407
+ });
408
+ }
409
+ }
410
+ catch (error) {
411
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
412
+ }
413
+ }
414
+ // Read parent directory Claude files (for monorepo support)
415
+ const parentClaudeFiles = getParentClaudeFiles(PROJECT_ROOT);
416
+ for (const { path, file_type } of parentClaudeFiles) {
417
+ try {
418
+ const markdownContent = readMarkdownFile(path);
419
+ if (markdownContent !== null) { // Log even if empty
420
+ console.log(`Found ${file_type} at: ${path}`);
421
+ configFiles.push({
422
+ file_type,
423
+ file_path: path,
424
+ raw_content: {
425
+ content: markdownContent,
426
+ source: 'claude_rule_config_file',
427
+ },
428
+ });
429
+ }
430
+ }
431
+ catch (error) {
432
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
433
+ }
434
+ }
435
+ // Read subdirectory Claude files (for monorepo support - root/subdir/CLAUDE.md)
436
+ const subdirectoryClaudeFiles = getSubdirectoryClaudeFiles(PROJECT_ROOT);
437
+ for (const { path, file_type } of subdirectoryClaudeFiles) {
438
+ try {
439
+ const markdownContent = readMarkdownFile(path);
440
+ if (markdownContent !== null) { // Log even if empty
441
+ console.log(`Found ${file_type} at: ${path}`);
442
+ configFiles.push({
443
+ file_type,
444
+ file_path: path,
445
+ raw_content: {
446
+ content: markdownContent,
447
+ source: 'claude_rule_config_file',
448
+ },
449
+ });
450
+ }
451
+ }
452
+ catch (error) {
453
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
454
+ }
455
+ }
456
+ // Read Claude rule files from ~/.claude/rules/*.md
457
+ const claudeRuleFiles = getClaudeRuleFiles();
458
+ for (const { path, file_type } of claudeRuleFiles) {
459
+ try {
460
+ const markdownContent = readMarkdownFile(path);
461
+ if (markdownContent !== null) { // Log even if empty
462
+ console.log(`Found ${file_type} at: ${path}`);
463
+ configFiles.push({
464
+ file_type,
465
+ file_path: path,
466
+ raw_content: {
467
+ content: markdownContent,
468
+ source: 'claude_rule_file',
469
+ },
470
+ });
471
+ }
472
+ }
473
+ catch (error) {
474
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
475
+ }
476
+ }
477
+ // Read Cursor rule files from .cursor/rules/ (new format: folders with RULE.md, legacy: .mdc/.md files)
478
+ const cursorRuleFiles = getCursorRuleFiles();
479
+ for (const { path, file_type } of cursorRuleFiles) {
480
+ try {
481
+ const markdownContent = readMarkdownFile(path);
482
+ if (markdownContent !== null) { // Log even if empty
483
+ console.log(`Found ${file_type} at: ${path}`);
484
+ configFiles.push({
485
+ file_type,
486
+ file_path: path,
487
+ raw_content: {
488
+ content: markdownContent,
489
+ source: 'cursor_rule_file',
490
+ },
491
+ });
492
+ }
493
+ }
494
+ catch (error) {
495
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
496
+ }
497
+ }
498
+ // Read AGENTS.md files (project root and subdirectories)
499
+ const agentsMdFiles = getAgentsMdFiles(PROJECT_ROOT);
500
+ for (const { path, file_type } of agentsMdFiles) {
501
+ try {
502
+ const markdownContent = readMarkdownFile(path);
503
+ if (markdownContent !== null) { // Log even if empty
504
+ console.log(`Found ${file_type} at: ${path}`);
505
+ configFiles.push({
506
+ file_type,
507
+ file_path: path,
508
+ raw_content: {
509
+ content: markdownContent,
510
+ source: 'cursor_rule_file',
511
+ },
512
+ });
513
+ }
514
+ }
515
+ catch (error) {
516
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
517
+ }
518
+ }
519
+ // Read .cursorrules file (legacy format, deprecated but still supported)
520
+ const cursorRulesFiles = getCursorRulesFile(PROJECT_ROOT);
521
+ for (const { path, file_type } of cursorRulesFiles) {
522
+ try {
523
+ const markdownContent = readMarkdownFile(path);
524
+ if (markdownContent !== null) { // Log even if empty
525
+ console.log(`Found ${file_type} at: ${path}`);
526
+ configFiles.push({
527
+ file_type,
528
+ file_path: path,
529
+ raw_content: {
530
+ content: markdownContent,
531
+ source: 'cursor_rule_file',
532
+ },
533
+ });
534
+ }
535
+ }
536
+ catch (error) {
537
+ console.warn(`Error reading ${path}: ${error instanceof Error ? error.message : String(error)}`);
538
+ }
539
+ }
188
540
  return configFiles;
189
541
  }
190
542
  /**
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "log-llm-config",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "log-llm-config": "./dist/cli.js",
8
- "log_uuid": "./dist/log_uuid.js",
9
- "log_config_files": "./dist/log_config_files.js"
7
+ "log-llm-config": "dist/cli.js",
8
+ "log_uuid": "dist/log_uuid.js",
9
+ "log_config_files": "dist/log_config_files.js"
10
10
  },
11
11
  "scripts": {
12
12
  "build": "tsc -p tsconfig.json",
@@ -26,7 +26,7 @@
26
26
  "license": "UNLICENSED",
27
27
  "repository": {
28
28
  "type": "git",
29
- "url": "https://github.com/optimuslabs-io/optimus-secure-fdn.git",
29
+ "url": "git+https://github.com/optimuslabs-io/optimus-secure-fdn.git",
30
30
  "directory": "npx_packages/log-llm-config"
31
31
  },
32
32
  "bugs": {
@@ -41,11 +41,11 @@
41
41
  "node": ">=18"
42
42
  },
43
43
  "devDependencies": {
44
- "@types/node": "^20.11.30",
45
- "@vitest/ui": "^2.1.8",
44
+ "@types/node": "^24.10.1",
45
+ "@vitest/ui": "^4.0.15",
46
46
  "ts-node": "^10.9.2",
47
47
  "typescript": "^5.4.5",
48
- "vitest": "^2.1.8"
48
+ "vitest": "^4.0.15"
49
49
  },
50
50
  "dependencies": {
51
51
  "axios": "^1.7.9"