i18ntk 3.3.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -84,6 +84,11 @@ class I18nUsageAnalyzer {
84
84
  this.startTime = Date.now(); // Track performance metrics
85
85
  this.version = '1.10.1'; // Version tracking
86
86
 
87
+ // Dead key detection properties
88
+ this.deadKeys = new Map();
89
+ this.cleanupMode = false;
90
+ this.dryRunDelete = false;
91
+
87
92
  // Use global translation function
88
93
  this.rl = null;
89
94
  }
@@ -187,7 +192,9 @@ class I18nUsageAnalyzer {
187
192
  help: a.help || a.h,
188
193
  noPrompt: a.noPrompt ?? a['no-prompt'],
189
194
  strict: a.strict,
190
- debug: a.debug
195
+ debug: a.debug,
196
+ cleanup: a.cleanup ?? a['cleanup'],
197
+ dryRunDelete: a.dryRunDelete ?? a['dry-run-delete']
191
198
  };
192
199
  }
193
200
 
@@ -376,6 +383,13 @@ class I18nUsageAnalyzer {
376
383
  console.log('🔍 Debug mode enabled');
377
384
  }
378
385
 
386
+ if (args.cleanup) {
387
+ this.cleanupMode = true;
388
+ }
389
+ if (args.dryRunDelete) {
390
+ this.dryRunDelete = true;
391
+ }
392
+
379
393
  try {
380
394
  // Ensure config is always initialized
381
395
  if (!this.config) {
@@ -599,6 +613,33 @@ class I18nUsageAnalyzer {
599
613
  }));
600
614
  }
601
615
 
616
+ if (this.cleanupMode) {
617
+ const deadKeys = this.findDeadKeys();
618
+ console.log('\n' + t('usage.deadKeysDetectionTitle'));
619
+ console.log(t('usage.deadKeysCount', { count: deadKeys.length }));
620
+
621
+ const highConfidence = deadKeys.filter(dk => dk.confidence >= 0.8).length;
622
+ const mediumConfidence = deadKeys.filter(dk => dk.confidence >= 0.4 && dk.confidence < 0.8).length;
623
+ const lowConfidence = deadKeys.filter(dk => dk.confidence < 0.4).length;
624
+
625
+ console.log(t('usage.deadKeysConfidenceBreakdown', { high: highConfidence, medium: mediumConfidence, low: lowConfidence }));
626
+
627
+ if (deadKeys.length > 0) {
628
+ console.log('\n' + t('usage.deadKeysSample'));
629
+ deadKeys.slice(0, 10).forEach(dk => {
630
+ console.log(` ${dk.key} [${(dk.confidence * 100).toFixed(0)}%] - ${dk.reason}`);
631
+ });
632
+ if (deadKeys.length > 10) {
633
+ console.log(t('usage.deadKeysMore', { count: deadKeys.length - 10 }));
634
+ }
635
+ }
636
+
637
+ if (this.dryRunDelete) {
638
+ const reportPath = this.saveDeadKeysReport(deadKeys, args.outputDir || this.config.outputDir || './i18ntk-reports/usage');
639
+ console.log(t('usage.deadKeysReportSaved', { path: reportPath }));
640
+ }
641
+ }
642
+
602
643
  if (args.outputReport) {
603
644
  const report = this.generateUsageReport();
604
645
  await this.saveReport(report, args.outputDir);
@@ -625,7 +666,7 @@ class I18nUsageAnalyzer {
625
666
  // Show help message
626
667
  showHelp() {
627
668
  console.log(`
628
- 📊 i18ntk usage - Translation key usage analysis (v1.8.3)
669
+ 📊 i18ntk usage - Translation key usage analysis (v1.10.1)
629
670
 
630
671
  Usage:
631
672
  node i18ntk-usage.js [options]
@@ -642,14 +683,17 @@ Options:
642
683
  --validate-placeholders Enable placeholder key validation
643
684
  --framework-detect Enable framework-specific pattern detection
644
685
  --performance-mode Enable performance metrics tracking
686
+ --cleanup Enable dead key detection for cleanup mode
687
+ --dry-run-delete Save dead keys report without deleting (requires --cleanup)
645
688
  --help, -h Show this help message
646
689
 
647
690
  Examples:
648
691
  node i18ntk-usage.js --source-dir=./src --i18n-dir=./translations --output-report
649
692
  npm run i18ntk:usage -- --strict --debug --validate-placeholders
650
693
  node i18ntk-usage.js --no-prompt --performance-mode --output-dir=./reports
694
+ node i18ntk-usage.js --cleanup --dry-run-delete
651
695
 
652
- Analysis Features (v1.8.3):
696
+ Analysis Features (v1.10.1):
653
697
  • Detects unused translation keys
654
698
  • Identifies missing translation keys
655
699
  • Shows translation completeness by language
@@ -662,6 +706,7 @@ Analysis Features (v1.8.3):
662
706
  • Key complexity analysis
663
707
  • Security-enhanced path validation
664
708
  • Detailed reporting with validation errors
709
+ • Dead key detection with confidence scoring
665
710
  `);
666
711
  }
667
712
 
@@ -1133,6 +1178,161 @@ Analysis Features (v1.8.3):
1133
1178
  return missing;
1134
1179
  }
1135
1180
 
1181
+ findDeadKeys() {
1182
+ const unusedKeys = this.findUnusedKeys();
1183
+ const deadKeys = [];
1184
+
1185
+ for (const key of unusedKeys) {
1186
+ let confidence = 0.9;
1187
+ let reason = 'Key not found in any source file';
1188
+
1189
+ if (this._matchesDynamicPattern(key)) {
1190
+ confidence = 0.3;
1191
+ reason = 'Key matches dynamic template pattern (likely used)';
1192
+ } else if (this._keyInSourceComments(key)) {
1193
+ confidence = 0.5;
1194
+ reason = 'Key referenced in comments/JSDoc';
1195
+ } else if (this._parentFileRecentlyModified(key)) {
1196
+ confidence = 0.4;
1197
+ reason = 'Translation file modified within last 30 days';
1198
+ }
1199
+
1200
+ deadKeys.push({ key, confidence, reason });
1201
+ }
1202
+
1203
+ deadKeys.sort((a, b) => b.confidence - a.confidence);
1204
+ return deadKeys;
1205
+ }
1206
+
1207
+ _matchesDynamicPattern(key) {
1208
+ const keyParts = key.split('.');
1209
+ if (keyParts.length < 2) return false;
1210
+
1211
+ const dynamicPatterns = [
1212
+ /t\(`[^`]*\$\{[^}]*\}[^`]*`\)/g,
1213
+ /i18n\.t\(`[^`]*\$\{[^}]*\}[^`]*`\)/g,
1214
+ /useTranslation\(\)\.t\(`[^`]*\$\{[^}]*\}[^`]*`\)/g
1215
+ ];
1216
+
1217
+ try {
1218
+ const sourceFiles = Array.from(this.fileUsage.keys());
1219
+ for (const filePath of sourceFiles) {
1220
+ const fullPath = path.join(this.sourceDir, filePath);
1221
+ if (!SecurityUtils.safeExistsSync(fullPath, this.sourceDir)) continue;
1222
+
1223
+ const content = SecurityUtils.safeReadFileSync(fullPath, this.sourceDir, 'utf8');
1224
+ if (!content) continue;
1225
+
1226
+ for (const pattern of dynamicPatterns) {
1227
+ const matches = content.match(pattern);
1228
+ if (matches) {
1229
+ for (const match of matches) {
1230
+ const matchLower = match.toLowerCase();
1231
+ if (keyParts.some(part => matchLower.includes(part.toLowerCase()))) {
1232
+ return true;
1233
+ }
1234
+ }
1235
+ }
1236
+ }
1237
+ }
1238
+ } catch (e) {
1239
+ // Silently fail - dynamic pattern detection is best-effort
1240
+ }
1241
+
1242
+ return false;
1243
+ }
1244
+
1245
+ _keyInSourceComments(key) {
1246
+ const commentPatterns = [
1247
+ /\/\/[^\n]*/g,
1248
+ /\/\*[\s\S]*?\*\//g,
1249
+ /\/\*\*[\s\S]*?\*\//g
1250
+ ];
1251
+
1252
+ try {
1253
+ const sourceFiles = Array.from(this.fileUsage.keys());
1254
+ for (const filePath of sourceFiles) {
1255
+ const fullPath = path.join(this.sourceDir, filePath);
1256
+ if (!SecurityUtils.safeExistsSync(fullPath, this.sourceDir)) continue;
1257
+
1258
+ const content = SecurityUtils.safeReadFileSync(fullPath, this.sourceDir, 'utf8');
1259
+ if (!content) continue;
1260
+
1261
+ for (const pattern of commentPatterns) {
1262
+ const comments = content.match(pattern);
1263
+ if (comments) {
1264
+ for (const comment of comments) {
1265
+ if (comment.includes(key)) {
1266
+ return true;
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+ }
1272
+ } catch (e) {
1273
+ // Silently fail - comment detection is best-effort
1274
+ }
1275
+
1276
+ return false;
1277
+ }
1278
+
1279
+ _parentFileRecentlyModified(key) {
1280
+ const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
1281
+ const now = Date.now();
1282
+
1283
+ try {
1284
+ for (const [filePath] of this.translationFiles) {
1285
+ if (!SecurityUtils.safeExistsSync(filePath, this.i18nDir)) continue;
1286
+
1287
+ const content = SecurityUtils.safeReadFileSync(filePath, this.i18nDir, 'utf8');
1288
+ if (!content) continue;
1289
+
1290
+ if (content.includes(key)) {
1291
+ const stats = SecurityUtils.safeStatSync(filePath, this.i18nDir);
1292
+ if (stats && stats.mtime) {
1293
+ const mtimeMs = new Date(stats.mtime).getTime();
1294
+ if ((now - mtimeMs) <= thirtyDaysMs) {
1295
+ return true;
1296
+ }
1297
+ }
1298
+ }
1299
+ }
1300
+ } catch (e) {
1301
+ // Silently fail - file stat is best-effort
1302
+ }
1303
+
1304
+ return false;
1305
+ }
1306
+
1307
+ generateDeadKeysReport(deadKeys) {
1308
+ const allCleanupReady = deadKeys.length === 0 || deadKeys.every(dk => dk.confidence >= 0.8);
1309
+
1310
+ return {
1311
+ deadKeys,
1312
+ totalAvailableKeys: this.availableKeys.size,
1313
+ totalUsedKeys: this.usedKeys.size,
1314
+ deadKeyCount: deadKeys.length,
1315
+ cleanupReady: allCleanupReady,
1316
+ generatedAt: new Date().toISOString(),
1317
+ version: this.version
1318
+ };
1319
+ }
1320
+
1321
+ saveDeadKeysReport(deadKeys, outputDir) {
1322
+ const report = this.generateDeadKeysReport(deadKeys);
1323
+ const resolvedDir = path.resolve(outputDir || './i18ntk-reports/usage');
1324
+
1325
+ if (!SecurityUtils.safeExistsSync(resolvedDir, process.cwd())) {
1326
+ SecurityUtils.safeMkdirSync(resolvedDir, process.cwd(), { recursive: true });
1327
+ }
1328
+
1329
+ const filename = '.dead-keys.json';
1330
+ const filepath = path.join(resolvedDir, filename);
1331
+
1332
+ SecurityUtils.safeWriteFileSync(filepath, JSON.stringify(report, null, 2), resolvedDir, 'utf8');
1333
+ return filepath;
1334
+ }
1335
+
1136
1336
  // Find files that use specific keys
1137
1337
  findKeyUsage(searchKey) {
1138
1338
  const usage = [];
@@ -68,6 +68,7 @@ class I18nValidator {
68
68
  this.config = config;
69
69
  this.errors = [];
70
70
  this.warnings = [];
71
+ this.keyNamingViolations = [];
71
72
  this.rl = null;
72
73
  }
73
74
 
@@ -167,6 +168,8 @@ class I18nValidator {
167
168
  const key = sanitizedArg.substring(2);
168
169
  if (['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(key)) {
169
170
  baseArgs.uiLanguage = key;
171
+ } else if (key === 'enforce-key-style') {
172
+ baseArgs.enforceKeyStyle = true;
170
173
  }
171
174
  }
172
175
  });
@@ -573,6 +576,25 @@ class I18nValidator {
573
576
  // Validate structure
574
577
  const structural = this.validateStructure(sourceContent, targetContent, language, fileName);
575
578
 
579
+ // Check key naming conventions
580
+ if (this.config.enforceKeyStyle) {
581
+ const keyNamingResult = this.validateKeyNaming(sourceContent);
582
+ keyNamingResult.violations.forEach(v => {
583
+ this.addWarning(
584
+ `Key naming violation in ${language}/${fileName}`,
585
+ { key: v.key, suggestedFix: v.suggestedFix, reason: v.reason, style: keyNamingResult.style }
586
+ );
587
+ this.keyNamingViolations.push({
588
+ language,
589
+ fileName,
590
+ key: v.key,
591
+ suggestedFix: v.suggestedFix,
592
+ reason: v.reason,
593
+ style: keyNamingResult.style
594
+ });
595
+ });
596
+ }
597
+
576
598
  // Validate translations
577
599
  const translations = this.validateTranslation(targetContent, language, fileName);
578
600
  this.checkPlaceholders(sourceContent, targetContent, language, fileName);
@@ -652,7 +674,68 @@ class I18nValidator {
652
674
  return warnings;
653
675
  }
654
676
 
655
- // Show help message
677
+ validateKeyNaming(sourceObj, style) {
678
+ const keyStyle = style || this.config.keyStyle || 'dot.notation';
679
+ const allKeys = this.getAllKeys(sourceObj);
680
+ const validators = {
681
+ 'dot.notation': /^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/,
682
+ 'snake_case': /^[a-z][a-z0-9]*(_[a-z][a-z0-9]*)*$/,
683
+ 'camelCase': /^[a-z][a-zA-Z0-9]*$/,
684
+ 'kebab-case': /^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*$/,
685
+ 'flat': /^[a-zA-Z][a-zA-Z0-9]*$/
686
+ };
687
+ const regex = validators[keyStyle];
688
+ if (!regex) {
689
+ return { violations: [], totalKeys: allKeys.size, violationCount: 0, style: keyStyle };
690
+ }
691
+ const violations = [];
692
+ for (const key of allKeys) {
693
+ const sanitizedKey = SecurityUtils.sanitizeInput(key);
694
+ if (!regex.test(sanitizedKey)) {
695
+ violations.push({
696
+ key: sanitizedKey,
697
+ suggestedFix: this.suggestKeyFix(sanitizedKey, keyStyle),
698
+ reason: `Key "${sanitizedKey}" does not match "${keyStyle}" naming convention`
699
+ });
700
+ }
701
+ }
702
+ return {
703
+ violations,
704
+ totalKeys: allKeys.size,
705
+ violationCount: violations.length,
706
+ style: keyStyle
707
+ };
708
+ }
709
+
710
+ suggestKeyFix(key, style) {
711
+ const sanitizedKey = SecurityUtils.sanitizeInput(key);
712
+ const segments = [];
713
+ const rawTokens = sanitizedKey.split(/[._\-]/);
714
+ for (const token of rawTokens) {
715
+ if (!token) continue;
716
+ const camelTokens = token.split(/(?=[A-Z])/).filter(Boolean);
717
+ segments.push(...camelTokens);
718
+ }
719
+ if (segments.length === 0) {
720
+ return sanitizedKey;
721
+ }
722
+ switch (style) {
723
+ case 'dot.notation':
724
+ return segments.map(s => s.toLowerCase()).join('.');
725
+ case 'snake_case':
726
+ return segments.map(s => s.toLowerCase()).join('_');
727
+ case 'camelCase':
728
+ return segments.map((s, i) => i === 0 ? s.toLowerCase() : s.charAt(0).toUpperCase() + s.slice(1).toLowerCase()).join('');
729
+ case 'kebab-case':
730
+ return segments.map(s => s.toLowerCase()).join('-');
731
+ case 'flat':
732
+ return segments.map((s, i) => i === 0 ? s.toLowerCase() : s.charAt(0).toUpperCase() + s.slice(1).toLowerCase()).join('');
733
+ default:
734
+ return sanitizedKey;
735
+ }
736
+ }
737
+
738
+ // Show help message
656
739
  showHelp() {
657
740
  console.log(t('validate.help_message'));
658
741
  }
@@ -895,8 +978,29 @@ class I18nValidator {
895
978
  console.log('');
896
979
  });
897
980
  }
898
-
899
- // Recommendations
981
+
982
+ // Key naming violations summary
983
+ if (this.keyNamingViolations.length > 0) {
984
+ console.log('');
985
+ console.log(t('validate.separator'));
986
+ console.log('🔑 Key Naming Convention Violations');
987
+ console.log('');
988
+ const displayStyle = this.config.keyStyle || 'dot.notation';
989
+ console.log(` Expected style: ${displayStyle}`);
990
+ console.log(` Violations found: ${this.keyNamingViolations.length}`);
991
+ console.log('');
992
+ console.log(' Suggested fixes:');
993
+ this.keyNamingViolations.slice(0, 10).forEach((v, i) => {
994
+ console.log(` ${i + 1}. "${v.key}" → "${v.suggestedFix}" (${v.language}/${v.fileName})`);
995
+ });
996
+ if (this.keyNamingViolations.length > 10) {
997
+ console.log(` ... and ${this.keyNamingViolations.length - 10} more`);
998
+ }
999
+ console.log('');
1000
+ console.log(' 💡 Tip: Use i18ntk:fix-keys to auto-fix or manually rename keys.');
1001
+ }
1002
+
1003
+ // Recommendations
900
1004
  console.log('');
901
1005
  console.log(t('validate.separator'));
902
1006
  console.log(t('validate.recommendationsSection'));
@@ -530,20 +530,20 @@ class FixerCommand {
530
530
  this.dryRun = args.dryRun || false;
531
531
  this.force = args.force || false;
532
532
 
533
- if (!args.json) {
534
- console.log(t('fixer.starting'));
535
- console.log(t('fixer.sourceDirectory', { dir: path.resolve(this.sourceDir) }));
536
- console.log(t('fixer.dryRunMode', { mode: this.dryRun ? 'ON' : 'OFF' }));
537
- }
533
+ const languages = this.getAvailableLanguages();
534
+
535
+ if (!args.json) {
536
+ console.log(t('fixer.starting', { languages: languages.join(', ') || 'none' }));
537
+ console.log(t('fixer.sourceDirectory', { sourceDir: path.resolve(this.sourceDir) }));
538
+ console.log(t('fixer.dryRunMode', { mode: this.dryRun ? 'ON' : 'OFF' }));
539
+ }
538
540
 
539
541
  // Create backup unless disabled
540
542
  if (!args.noBackup && !this.dryRun && this.config?.backup?.enabled === true) {
541
543
  await this.createBackup();
542
544
  }
543
545
 
544
- const languages = this.getAvailableLanguages();
545
-
546
- if (languages.length === 0) {
546
+ if (languages.length === 0) {
547
547
  const error = t('fixer.noLanguages') || 'No target languages found.';
548
548
  if (args.json) {
549
549
  jsonOutput.setStatus('error', error);
@@ -573,14 +573,16 @@ class FixerCommand {
573
573
  totalIssues += fixes.totalIssues;
574
574
  totalFixed += fixes.fixedIssues;
575
575
 
576
- if (!args.json) {
577
- console.log(t('fixer.languageFixed', {
578
- language,
579
- issues: fixes.totalIssues,
580
- fixed: fixes.fixedIssues
581
- }));
582
- }
583
- }
576
+ if (!args.json) {
577
+ const skipped = Math.max(0, fixes.totalIssues - fixes.fixedIssues);
578
+ console.log(t('fixer.languageFixed', {
579
+ language,
580
+ issues: fixes.totalIssues,
581
+ fixed: fixes.fixedIssues,
582
+ skipped
583
+ }));
584
+ }
585
+ }
584
586
 
585
587
  // Prepare JSON output
586
588
  if (args.json) {
@@ -595,11 +597,11 @@ class FixerCommand {
595
597
  return { success: true, totalIssues, totalFixed, results };
596
598
  }
597
599
 
598
- // Summary
599
- console.log(t('fixer.summary'));
600
- console.log('='.repeat(50));
601
- console.log(t('fixer.totalIssues', { count: totalIssues }));
602
- console.log(t('fixer.totalFixed', { count: totalFixed }));
600
+ // Summary
601
+ console.log(t('fixer.summary'));
602
+ console.log('='.repeat(50));
603
+ console.log(t('fixer.totalIssues', { totalIssues }));
604
+ console.log(t('fixer.totalFixed', { count: totalFixed }));
603
605
 
604
606
  if (this.backupDir && !args.noBackup && this.config?.backup?.enabled === true) {
605
607
  console.log(t('fixer.backupLocation', { dir: this.backupDir }));
@@ -1220,7 +1220,10 @@ class I18nManager {
1220
1220
  { path: path.join(process.cwd(), 'scripts', 'debug', 'logs'), name: 'Debug Logs', type: 'logs' },
1221
1221
  { path: path.join(process.cwd(), 'scripts', 'debug', 'reports'), name: 'Debug Reports', type: 'reports' },
1222
1222
  { path: path.join(process.cwd(), 'settings', 'backups'), name: 'Settings Backups', type: 'backups' },
1223
- { path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' }
1223
+ { path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' },
1224
+ { path: path.join(process.cwd(), '.cache'), name: 'Cache', type: 'cache', includeAllFiles: true },
1225
+ { path: path.join(process.cwd(), 'settings', '.cache'), name: 'Settings Cache', type: 'cache', includeAllFiles: true },
1226
+ { path: path.join(process.cwd(), '.cache-ultra'), name: 'Performance Cache', type: 'cache', includeAllFiles: true }
1224
1227
  ].filter(dir => dir.path && typeof dir.path === 'string');
1225
1228
 
1226
1229
  try {
@@ -1233,7 +1236,9 @@ class I18nManager {
1233
1236
  for (const dir of targetDirs) {
1234
1237
  const validatedDirPath = SecurityUtils.validatePath(dir.path, projectRoot);
1235
1238
  if (validatedDirPath && SecurityUtils.safeExistsSync(validatedDirPath, projectRoot)) {
1236
- const files = this.getAllReportFiles(validatedDirPath, validatedDirPath);
1239
+ const files = this.getAllReportFiles(validatedDirPath, validatedDirPath, {
1240
+ includeAllFiles: dir.includeAllFiles === true
1241
+ });
1237
1242
  if (files.length > 0) {
1238
1243
  availableDirs.push({
1239
1244
  ...dir,
@@ -1368,12 +1373,13 @@ class I18nManager {
1368
1373
  await this.showInteractiveMenu();
1369
1374
  }
1370
1375
 
1371
- getAllReportFiles(dir, rootDir = dir) {
1376
+ getAllReportFiles(dir, rootDir = dir, options = {}) {
1372
1377
  if (!dir || typeof dir !== 'string') {
1373
1378
  return [];
1374
1379
  }
1375
1380
 
1376
- let files = [];
1381
+ let files = [];
1382
+ const includeAllFiles = options.includeAllFiles === true;
1377
1383
 
1378
1384
  try {
1379
1385
  const validatedDir = SecurityUtils.validatePath(dir, rootDir);
@@ -1399,8 +1405,8 @@ class I18nManager {
1399
1405
  const stat = fs.statSync(safeFullPath);
1400
1406
 
1401
1407
  if (stat.isDirectory()) {
1402
- files.push(...this.getAllReportFiles(safeFullPath, rootDir));
1403
- } else if (
1408
+ files.push(...this.getAllReportFiles(safeFullPath, rootDir, options));
1409
+ } else if (includeAllFiles || (
1404
1410
  // Common report file extensions
1405
1411
  item.endsWith('.json') ||
1406
1412
  item.endsWith('.html') ||
@@ -1415,7 +1421,7 @@ class I18nManager {
1415
1421
  item.includes('report_') ||
1416
1422
  item.includes('analysis-') ||
1417
1423
  item.includes('validation-')
1418
- ) {
1424
+ )) {
1419
1425
  files.push(safeFullPath);
1420
1426
  }
1421
1427
  } catch (error) {
@@ -129,7 +129,10 @@ module.exports = class FileManagementService {
129
129
  { path: path.join(process.cwd(), 'scripts', 'debug', 'logs'), name: 'Debug Logs', type: 'logs' },
130
130
  { path: path.join(process.cwd(), 'scripts', 'debug', 'reports'), name: 'Debug Reports', type: 'reports' },
131
131
  { path: path.join(process.cwd(), 'settings', 'backups'), name: 'Settings Backups', type: 'backups' },
132
- { path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' }
132
+ { path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' },
133
+ { path: path.join(process.cwd(), '.cache'), name: 'Cache', type: 'cache', includeAllFiles: true },
134
+ { path: path.join(process.cwd(), 'settings', '.cache'), name: 'Settings Cache', type: 'cache', includeAllFiles: true },
135
+ { path: path.join(process.cwd(), '.cache-ultra'), name: 'Performance Cache', type: 'cache', includeAllFiles: true }
133
136
  ].filter(dir => dir.path && typeof dir.path === 'string');
134
137
 
135
138
  try {
@@ -140,7 +143,9 @@ module.exports = class FileManagementService {
140
143
  // Check which directories exist and have files
141
144
  for (const dir of targetDirs) {
142
145
  if (SecurityUtils.safeExistsSync(dir.path)) {
143
- const files = this.getAllReportFiles(dir.path);
146
+ const files = this.getAllReportFiles(dir.path, {
147
+ includeAllFiles: dir.includeAllFiles === true
148
+ });
144
149
  if (files.length > 0) {
145
150
  availableDirs.push({
146
151
  ...dir,
@@ -267,12 +272,13 @@ module.exports = class FileManagementService {
267
272
  * @param {string} dir - Directory to scan
268
273
  * @returns {string[]} Array of file paths
269
274
  */
270
- getAllReportFiles(dir) {
275
+ getAllReportFiles(dir, options = {}) {
271
276
  if (!dir || typeof dir !== 'string') {
272
277
  return [];
273
278
  }
274
279
 
275
280
  let files = [];
281
+ const includeAllFiles = options.includeAllFiles === true;
276
282
 
277
283
  try {
278
284
  if (!SecurityUtils.safeExistsSync(dir)) {
@@ -287,8 +293,8 @@ module.exports = class FileManagementService {
287
293
  const stat = fs.statSync(fullPath);
288
294
 
289
295
  if (stat.isDirectory()) {
290
- files.push(...this.getAllReportFiles(fullPath));
291
- } else if (
296
+ files.push(...this.getAllReportFiles(fullPath, options));
297
+ } else if (includeAllFiles || (
292
298
  // Common report file extensions
293
299
  item.endsWith('.json') ||
294
300
  item.endsWith('.html') ||
@@ -303,7 +309,7 @@ module.exports = class FileManagementService {
303
309
  item.includes('report_') ||
304
310
  item.includes('analysis-') ||
305
311
  item.includes('validation-')
306
- ) {
312
+ )) {
307
313
  files.push(fullPath);
308
314
  }
309
315
  } catch (error) {