@aiready/core 0.9.32 → 0.9.35

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/index.js CHANGED
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  SIZE_ADJUSTED_THRESHOLDS: () => SIZE_ADJUSTED_THRESHOLDS,
44
44
  TOOL_NAME_MAP: () => TOOL_NAME_MAP,
45
45
  TypeScriptParser: () => TypeScriptParser,
46
+ VAGUE_FILE_NAMES: () => VAGUE_FILE_NAMES,
46
47
  calculateAgentGrounding: () => calculateAgentGrounding,
47
48
  calculateAiSignalClarity: () => calculateAiSignalClarity,
48
49
  calculateChangeAmplification: () => calculateChangeAmplification,
@@ -77,8 +78,10 @@ __export(index_exports, {
77
78
  generateHTML: () => generateHTML,
78
79
  getDebtBreakdown: () => getDebtBreakdown,
79
80
  getElapsedTime: () => getElapsedTime,
81
+ getFileCommitTimestamps: () => getFileCommitTimestamps,
80
82
  getFileExtension: () => getFileExtension,
81
83
  getHistorySummary: () => getHistorySummary,
84
+ getLineRangeLastModifiedCached: () => getLineRangeLastModifiedCached,
82
85
  getModelPreset: () => getModelPreset,
83
86
  getParser: () => getParser,
84
87
  getProjectSizeTier: () => getProjectSizeTier,
@@ -104,6 +107,7 @@ __export(index_exports, {
104
107
  readFileContent: () => readFileContent,
105
108
  resolveOutputPath: () => resolveOutputPath,
106
109
  saveScoreEntry: () => saveScoreEntry,
110
+ scanEntries: () => scanEntries,
107
111
  scanFiles: () => scanFiles
108
112
  });
109
113
  module.exports = __toCommonJS(index_exports);
@@ -159,6 +163,8 @@ var DEFAULT_EXCLUDE = [
159
163
  "**/cdk.out/**",
160
164
  // Framework-specific build dirs
161
165
  "**/.next/**",
166
+ "**/.sst/**",
167
+ "**/.open-next/**",
162
168
  "**/.nuxt/**",
163
169
  "**/.vuepress/**",
164
170
  "**/.cache/**",
@@ -190,6 +196,28 @@ var DEFAULT_EXCLUDE = [
190
196
  "**/*.log",
191
197
  "**/.DS_Store"
192
198
  ];
199
+ var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
200
+ "utils",
201
+ "helpers",
202
+ "helper",
203
+ "misc",
204
+ "common",
205
+ "shared",
206
+ "tools",
207
+ "util",
208
+ "lib",
209
+ "libs",
210
+ "stuff",
211
+ "functions",
212
+ "methods",
213
+ "handlers",
214
+ "data",
215
+ "temp",
216
+ "tmp",
217
+ "test-utils",
218
+ "test-helpers",
219
+ "mocks"
220
+ ]);
193
221
  async function scanFiles(options) {
194
222
  const {
195
223
  rootDir,
@@ -207,18 +235,35 @@ async function scanFiles(options) {
207
235
  ignoreFromFile = [];
208
236
  }
209
237
  }
210
- const finalExclude = [.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])];
238
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
239
+ const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
240
+ const finalExclude = [
241
+ .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
242
+ ];
211
243
  const files = await (0, import_glob.glob)(include, {
212
244
  cwd: rootDir,
213
245
  ignore: finalExclude,
214
246
  absolute: true
215
247
  });
216
- const gitignorePath = (0, import_path.join)(rootDir || ".", ".gitignore");
217
- if ((0, import_fs.existsSync)(gitignorePath)) {
248
+ const gitignoreFiles = await (0, import_glob.glob)("**/.gitignore", {
249
+ cwd: rootDir,
250
+ ignore: finalExclude,
251
+ absolute: true
252
+ });
253
+ if (gitignoreFiles.length > 0) {
218
254
  try {
219
- const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
220
255
  const ig = (0, import_ignore.default)();
221
- ig.add(gitTxt);
256
+ for (const gitignorePath of gitignoreFiles) {
257
+ const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
258
+ const gitignoreDir = (0, import_path.dirname)(gitignorePath);
259
+ const relativePrefix = (0, import_path.relative)(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
260
+ const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
261
+ if (relativePrefix === "." || relativePrefix === "") {
262
+ ig.add(patterns);
263
+ } else {
264
+ ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
265
+ }
266
+ }
222
267
  const filtered = files.filter((f) => {
223
268
  let rel = (0, import_path.relative)(rootDir || ".", f).replace(/\\/g, "/");
224
269
  if (rel === "") rel = f;
@@ -231,6 +276,54 @@ async function scanFiles(options) {
231
276
  }
232
277
  return files;
233
278
  }
279
+ async function scanEntries(options) {
280
+ const files = await scanFiles(options);
281
+ const { rootDir, include = ["**/*"], exclude, includeTests } = options;
282
+ const ignoreFilePath = (0, import_path.join)(rootDir || ".", ".aireadyignore");
283
+ let ignoreFromFile = [];
284
+ if ((0, import_fs.existsSync)(ignoreFilePath)) {
285
+ try {
286
+ const txt = await (0, import_promises.readFile)(ignoreFilePath, "utf-8");
287
+ ignoreFromFile = txt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#")).filter((l) => !l.startsWith("!"));
288
+ } catch (e) {
289
+ ignoreFromFile = [];
290
+ }
291
+ }
292
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
293
+ const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
294
+ const finalExclude = [.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])];
295
+ const dirs = await (0, import_glob.glob)("**/", {
296
+ cwd: rootDir,
297
+ ignore: finalExclude,
298
+ absolute: true
299
+ });
300
+ const gitignoreFiles = await (0, import_glob.glob)("**/.gitignore", {
301
+ cwd: rootDir,
302
+ ignore: finalExclude,
303
+ absolute: true
304
+ });
305
+ if (gitignoreFiles.length > 0) {
306
+ const ig = (0, import_ignore.default)();
307
+ for (const gitignorePath of gitignoreFiles) {
308
+ const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
309
+ const gitignoreDir = (0, import_path.dirname)(gitignorePath);
310
+ const relativePrefix = (0, import_path.relative)(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
311
+ const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
312
+ if (relativePrefix === "." || relativePrefix === "") {
313
+ ig.add(patterns);
314
+ } else {
315
+ ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
316
+ }
317
+ }
318
+ const filteredDirs = dirs.filter((d) => {
319
+ let rel = (0, import_path.relative)(rootDir || ".", d).replace(/\\/g, "/").replace(/\/$/, "");
320
+ if (rel === "") return true;
321
+ return !ig.ignores(rel);
322
+ });
323
+ return { files, dirs: filteredDirs };
324
+ }
325
+ return { files, dirs };
326
+ }
234
327
  async function readFileContent(filePath) {
235
328
  return (0, import_promises.readFile)(filePath, "utf-8");
236
329
  }
@@ -455,7 +548,14 @@ async function loadConfig(rootDir) {
455
548
  return config;
456
549
  } catch (error) {
457
550
  const errorMessage = error instanceof Error ? error.message : String(error);
458
- throw new Error(`Failed to load config from ${configPath}: ${errorMessage}`);
551
+ const e = new Error(
552
+ `Failed to load config from ${configPath}: ${errorMessage}`
553
+ );
554
+ try {
555
+ e.cause = error instanceof Error ? error : void 0;
556
+ } catch {
557
+ }
558
+ throw e;
459
559
  }
460
560
  }
461
561
  }
@@ -707,26 +807,26 @@ function generateHTML(graph) {
707
807
  var DEFAULT_TOOL_WEIGHTS = {
708
808
  "pattern-detect": 22,
709
809
  "context-analyzer": 19,
710
- "consistency": 14,
810
+ consistency: 14,
711
811
  "ai-signal-clarity": 11,
712
812
  "agent-grounding": 10,
713
- "testability": 10,
813
+ testability: 10,
714
814
  "doc-drift": 8,
715
- "deps": 6
815
+ deps: 6
716
816
  };
717
817
  var TOOL_NAME_MAP = {
718
- "patterns": "pattern-detect",
719
- "context": "context-analyzer",
720
- "consistency": "consistency",
818
+ patterns: "pattern-detect",
819
+ context: "context-analyzer",
820
+ consistency: "consistency",
721
821
  "AI signal clarity": "ai-signal-clarity",
722
822
  "ai-signal-clarity": "ai-signal-clarity",
723
- "grounding": "agent-grounding",
823
+ grounding: "agent-grounding",
724
824
  "agent-grounding": "agent-grounding",
725
- "testability": "testability",
726
- "tests": "testability",
825
+ testability: "testability",
826
+ tests: "testability",
727
827
  "doc-drift": "doc-drift",
728
- "docs": "doc-drift",
729
- "deps": "deps"
828
+ docs: "doc-drift",
829
+ deps: "deps"
730
830
  };
731
831
  var CONTEXT_TIER_THRESHOLDS = {
732
832
  compact: { idealTokens: 3e3, criticalTokens: 1e4, idealDepth: 4 },
@@ -735,15 +835,15 @@ var CONTEXT_TIER_THRESHOLDS = {
735
835
  frontier: { idealTokens: 5e4, criticalTokens: 15e4, idealDepth: 10 }
736
836
  };
737
837
  var SIZE_ADJUSTED_THRESHOLDS = {
738
- "xs": 80,
838
+ xs: 80,
739
839
  // < 50 files
740
- "small": 75,
840
+ small: 75,
741
841
  // 50-200 files
742
- "medium": 70,
842
+ medium: 70,
743
843
  // 200-500 files
744
- "large": 65,
844
+ large: 65,
745
845
  // 500-2000 files
746
- "enterprise": 58
846
+ enterprise: 58
747
847
  // 2000+ files
748
848
  };
749
849
  function getProjectSizeTier(fileCount) {
@@ -816,10 +916,12 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
816
916
  }
817
917
  const overall = Math.round(weightedSum / totalWeight);
818
918
  const rating = getRating(overall);
819
- const formulaParts = Array.from(toolOutputs.entries()).map(([name, output]) => {
820
- const w = weights.get(name) || 10;
821
- return `(${output.score} \xD7 ${w})`;
822
- });
919
+ const formulaParts = Array.from(toolOutputs.entries()).map(
920
+ ([name, output]) => {
921
+ const w = weights.get(name) || 10;
922
+ return `(${output.score} \xD7 ${w})`;
923
+ }
924
+ );
823
925
  const formulaStr = `[${formulaParts.join(" + ")}] / ${totalWeight} = ${overall}`;
824
926
  return {
825
927
  overall,
@@ -951,7 +1053,7 @@ var MODEL_PRICING_PRESETS = {
951
1053
  contextTier: "frontier",
952
1054
  typicalQueriesPerDevPerDay: 150
953
1055
  },
954
- "copilot": {
1056
+ copilot: {
955
1057
  name: "GitHub Copilot (subscription)",
956
1058
  // Amortized per-request cost for a $19/month plan at 80 queries/day
957
1059
  pricePer1KInputTokens: 1e-4,
@@ -1017,9 +1119,18 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
1017
1119
  hourlyRate,
1018
1120
  totalCost: Math.round(totalCost),
1019
1121
  bySeverity: {
1020
- critical: { hours: Math.round(hours.critical * 10) / 10, cost: Math.round(hours.critical * hourlyRate) },
1021
- major: { hours: Math.round(hours.major * 10) / 10, cost: Math.round(hours.major * hourlyRate) },
1022
- minor: { hours: Math.round(hours.minor * 10) / 10, cost: Math.round(hours.minor * hourlyRate) }
1122
+ critical: {
1123
+ hours: Math.round(hours.critical * 10) / 10,
1124
+ cost: Math.round(hours.critical * hourlyRate)
1125
+ },
1126
+ major: {
1127
+ hours: Math.round(hours.major * 10) / 10,
1128
+ cost: Math.round(hours.major * hourlyRate)
1129
+ },
1130
+ minor: {
1131
+ hours: Math.round(hours.minor * 10) / 10,
1132
+ cost: Math.round(hours.minor * hourlyRate)
1133
+ }
1023
1134
  }
1024
1135
  };
1025
1136
  }
@@ -1077,12 +1188,18 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
1077
1188
  const criticalBudget = tierThresholds.criticalTokens;
1078
1189
  const idealDepth = tierThresholds.idealDepth;
1079
1190
  const budgetRange = criticalBudget - idealBudget;
1080
- const budgetFactor = Math.min(100, Math.max(
1081
- 0,
1082
- (contextBudget - idealBudget) / budgetRange * 100
1083
- ));
1084
- const depthFactor = Math.min(100, Math.max(0, (importDepth - idealDepth) * 10));
1085
- const fragmentationFactor = Math.min(100, Math.max(0, (fragmentation - 0.3) * 250));
1191
+ const budgetFactor = Math.min(
1192
+ 100,
1193
+ Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
1194
+ );
1195
+ const depthFactor = Math.min(
1196
+ 100,
1197
+ Math.max(0, (importDepth - idealDepth) * 10)
1198
+ );
1199
+ const fragmentationFactor = Math.min(
1200
+ 100,
1201
+ Math.max(0, (fragmentation - 0.3) * 250)
1202
+ );
1086
1203
  const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
1087
1204
  const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
1088
1205
  const score = Math.round(
@@ -1162,8 +1279,12 @@ function calculateScoreTrend(history) {
1162
1279
  const now = /* @__PURE__ */ new Date();
1163
1280
  const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
1164
1281
  const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
1165
- const last30Days = history.filter((e) => new Date(e.timestamp) >= thirtyDaysAgo);
1166
- const last90Days = history.filter((e) => new Date(e.timestamp) >= ninetyDaysAgo);
1282
+ const last30Days = history.filter(
1283
+ (e) => new Date(e.timestamp) >= thirtyDaysAgo
1284
+ );
1285
+ const last90Days = history.filter(
1286
+ (e) => new Date(e.timestamp) >= ninetyDaysAgo
1287
+ );
1167
1288
  const currentScore = history[history.length - 1].overallScore;
1168
1289
  const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
1169
1290
  const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
@@ -1176,7 +1297,10 @@ function calculateScoreTrend(history) {
1176
1297
  if (change30Days > 3) direction = "improving";
1177
1298
  else if (change30Days < -3) direction = "degrading";
1178
1299
  else direction = "stable";
1179
- const projectedScore = Math.max(0, Math.min(100, currentScore + velocity * 4));
1300
+ const projectedScore = Math.max(
1301
+ 0,
1302
+ Math.min(100, currentScore + velocity * 4)
1303
+ );
1180
1304
  return {
1181
1305
  direction,
1182
1306
  change30Days,
@@ -1237,9 +1361,13 @@ function calculateKnowledgeConcentration(files, authorData) {
1237
1361
  recommendations: ["No files to analyze"]
1238
1362
  };
1239
1363
  }
1240
- const orphanFiles = files.filter((f) => f.exports < 2 && f.imports < 2).length;
1364
+ const orphanFiles = files.filter(
1365
+ (f) => f.exports < 2 && f.imports < 2
1366
+ ).length;
1241
1367
  const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
1242
- const uniqueConceptFiles = files.filter((f) => f.exports > avgExports * 2).length;
1368
+ const uniqueConceptFiles = files.filter(
1369
+ (f) => f.exports > avgExports * 2
1370
+ ).length;
1243
1371
  const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
1244
1372
  const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
1245
1373
  let singleAuthorFiles = 0;
@@ -1251,7 +1379,10 @@ function calculateKnowledgeConcentration(files, authorData) {
1251
1379
  const orphanRisk = orphanFiles / files.length * 30;
1252
1380
  const uniqueRisk = concentrationRatio * 40;
1253
1381
  const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
1254
- const score = Math.min(100, Math.round(orphanRisk + uniqueRisk + singleAuthorRisk));
1382
+ const score = Math.min(
1383
+ 100,
1384
+ Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
1385
+ );
1255
1386
  let rating;
1256
1387
  if (score < 20) rating = "low";
1257
1388
  else if (score < 40) rating = "moderate";
@@ -1259,13 +1390,19 @@ function calculateKnowledgeConcentration(files, authorData) {
1259
1390
  else rating = "critical";
1260
1391
  const recommendations = [];
1261
1392
  if (orphanFiles > files.length * 0.2) {
1262
- recommendations.push(`Reduce ${orphanFiles} orphan files by connecting them to main modules`);
1393
+ recommendations.push(
1394
+ `Reduce ${orphanFiles} orphan files by connecting them to main modules`
1395
+ );
1263
1396
  }
1264
1397
  if (uniqueConceptFiles > files.length * 0.1) {
1265
- recommendations.push("Distribute high-export files into more focused modules");
1398
+ recommendations.push(
1399
+ "Distribute high-export files into more focused modules"
1400
+ );
1266
1401
  }
1267
1402
  if (authorData && singleAuthorFiles > files.length * 0.3) {
1268
- recommendations.push("Increase knowledge sharing to reduce single-author dependencies");
1403
+ recommendations.push(
1404
+ "Increase knowledge sharing to reduce single-author dependencies"
1405
+ );
1269
1406
  }
1270
1407
  return {
1271
1408
  score,
@@ -1419,7 +1556,10 @@ var TypeScriptParser = class {
1419
1556
  specifiers,
1420
1557
  isTypeOnly,
1421
1558
  loc: node.loc ? {
1422
- start: { line: node.loc.start.line, column: node.loc.start.column },
1559
+ start: {
1560
+ line: node.loc.start.line,
1561
+ column: node.loc.start.column
1562
+ },
1423
1563
  end: { line: node.loc.end.line, column: node.loc.end.column }
1424
1564
  } : void 0
1425
1565
  });
@@ -1430,11 +1570,16 @@ var TypeScriptParser = class {
1430
1570
  extractExports(ast, imports) {
1431
1571
  const exports2 = [];
1432
1572
  const importedNames = new Set(
1433
- imports.flatMap((imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default"))
1573
+ imports.flatMap(
1574
+ (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
1575
+ )
1434
1576
  );
1435
1577
  for (const node of ast.body) {
1436
1578
  if (node.type === "ExportNamedDeclaration" && node.declaration) {
1437
- const extracted = this.extractFromDeclaration(node.declaration, importedNames);
1579
+ const extracted = this.extractFromDeclaration(
1580
+ node.declaration,
1581
+ importedNames
1582
+ );
1438
1583
  exports2.push(...extracted);
1439
1584
  } else if (node.type === "ExportDefaultDeclaration") {
1440
1585
  let name = "default";
@@ -1450,7 +1595,10 @@ var TypeScriptParser = class {
1450
1595
  name,
1451
1596
  type,
1452
1597
  loc: node.loc ? {
1453
- start: { line: node.loc.start.line, column: node.loc.start.column },
1598
+ start: {
1599
+ line: node.loc.start.line,
1600
+ column: node.loc.start.column
1601
+ },
1454
1602
  end: { line: node.loc.end.line, column: node.loc.end.column }
1455
1603
  } : void 0
1456
1604
  });
@@ -1472,7 +1620,10 @@ var TypeScriptParser = class {
1472
1620
  line: declaration.loc.start.line,
1473
1621
  column: declaration.loc.start.column
1474
1622
  },
1475
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1623
+ end: {
1624
+ line: declaration.loc.end.line,
1625
+ column: declaration.loc.end.column
1626
+ }
1476
1627
  } : void 0
1477
1628
  });
1478
1629
  } else if (declaration.type === "ClassDeclaration" && declaration.id) {
@@ -1484,7 +1635,10 @@ var TypeScriptParser = class {
1484
1635
  line: declaration.loc.start.line,
1485
1636
  column: declaration.loc.start.column
1486
1637
  },
1487
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1638
+ end: {
1639
+ line: declaration.loc.end.line,
1640
+ column: declaration.loc.end.column
1641
+ }
1488
1642
  } : void 0
1489
1643
  });
1490
1644
  } else if (declaration.type === "VariableDeclaration") {
@@ -1515,7 +1669,10 @@ var TypeScriptParser = class {
1515
1669
  line: declaration.loc.start.line,
1516
1670
  column: declaration.loc.start.column
1517
1671
  },
1518
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1672
+ end: {
1673
+ line: declaration.loc.end.line,
1674
+ column: declaration.loc.end.column
1675
+ }
1519
1676
  } : void 0
1520
1677
  });
1521
1678
  } else if (declaration.type === "TSInterfaceDeclaration") {
@@ -1527,7 +1684,10 @@ var TypeScriptParser = class {
1527
1684
  line: declaration.loc.start.line,
1528
1685
  column: declaration.loc.start.column
1529
1686
  },
1530
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1687
+ end: {
1688
+ line: declaration.loc.end.line,
1689
+ column: declaration.loc.end.column
1690
+ }
1531
1691
  } : void 0
1532
1692
  });
1533
1693
  }
@@ -1552,7 +1712,9 @@ var PythonParser = class {
1552
1712
  try {
1553
1713
  this.initialized = true;
1554
1714
  } catch (error) {
1555
- throw new Error(`Failed to initialize Python parser: ${error.message}`);
1715
+ throw new Error(
1716
+ `Failed to initialize Python parser: ${error.message}`
1717
+ );
1556
1718
  }
1557
1719
  }
1558
1720
  parse(code, filePath) {
@@ -1563,7 +1725,9 @@ var PythonParser = class {
1563
1725
  exports: exports2,
1564
1726
  imports,
1565
1727
  language: "python" /* Python */,
1566
- warnings: ["Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."]
1728
+ warnings: [
1729
+ "Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."
1730
+ ]
1567
1731
  };
1568
1732
  } catch (error) {
1569
1733
  throw new ParseError(
@@ -1658,7 +1822,7 @@ var PythonParser = class {
1658
1822
  }
1659
1823
  /**
1660
1824
  * Regex-based export extraction (temporary implementation)
1661
- *
1825
+ *
1662
1826
  * Python doesn't have explicit exports like JavaScript.
1663
1827
  * We extract:
1664
1828
  * - Functions defined at module level (def)
@@ -1826,7 +1990,13 @@ function getSupportedLanguages() {
1826
1990
 
1827
1991
  // src/future-proof-metrics.ts
1828
1992
  function calculateCognitiveLoad(params) {
1829
- const { linesOfCode, exportCount, importCount, uniqueConcepts, cyclomaticComplexity = 1 } = params;
1993
+ const {
1994
+ linesOfCode,
1995
+ exportCount,
1996
+ importCount,
1997
+ uniqueConcepts,
1998
+ cyclomaticComplexity = 1
1999
+ } = params;
1830
2000
  const sizeFactor = {
1831
2001
  name: "Size Complexity",
1832
2002
  score: Math.min(100, Math.max(0, (linesOfCode - 50) / 10)),
@@ -1852,7 +2022,12 @@ function calculateCognitiveLoad(params) {
1852
2022
  weight: 0.2,
1853
2023
  description: `${uniqueConcepts} unique concepts`
1854
2024
  };
1855
- const factors = [sizeFactor, interfaceFactor, dependencyFactor, conceptFactor];
2025
+ const factors = [
2026
+ sizeFactor,
2027
+ interfaceFactor,
2028
+ dependencyFactor,
2029
+ conceptFactor
2030
+ ];
1856
2031
  const score = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
1857
2032
  let rating;
1858
2033
  if (score < 20) rating = "trivial";
@@ -1875,7 +2050,10 @@ function calculateCognitiveLoad(params) {
1875
2050
  function calculateSemanticDistance(params) {
1876
2051
  const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
1877
2052
  const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
1878
- const importOverlap = sharedDependencies.length / Math.max(1, Math.min(params.file1Imports.length, params.file2Imports.length));
2053
+ const importOverlap = sharedDependencies.length / Math.max(
2054
+ 1,
2055
+ Math.min(params.file1Imports.length, params.file2Imports.length)
2056
+ );
1879
2057
  const importDistance = 1 - importOverlap;
1880
2058
  const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
1881
2059
  let relationship;
@@ -1883,7 +2061,9 @@ function calculateSemanticDistance(params) {
1883
2061
  else if (file1Domain === file2Domain) relationship = "same-domain";
1884
2062
  else if (sharedDependencies.length > 0) relationship = "cross-domain";
1885
2063
  else relationship = "unrelated";
1886
- const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter((s) => typeof s === "string" && s.length > 0);
2064
+ const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter(
2065
+ (s) => typeof s === "string" && s.length > 0
2066
+ );
1887
2067
  return {
1888
2068
  between: [file1, file2],
1889
2069
  distance: Math.round(distance * 100) / 100,
@@ -1898,7 +2078,11 @@ function calculatePatternEntropy(files) {
1898
2078
  domain: "unknown",
1899
2079
  entropy: 0,
1900
2080
  rating: "crystalline",
1901
- distribution: { locationCount: 0, dominantLocation: "", giniCoefficient: 0 },
2081
+ distribution: {
2082
+ locationCount: 0,
2083
+ dominantLocation: "",
2084
+ giniCoefficient: 0
2085
+ },
1902
2086
  recommendations: ["No files to analyze"]
1903
2087
  };
1904
2088
  }
@@ -1938,10 +2122,14 @@ function calculatePatternEntropy(files) {
1938
2122
  else rating = "chaotic";
1939
2123
  const recommendations = [];
1940
2124
  if (normalizedEntropy > 0.5) {
1941
- recommendations.push(`Consolidate ${files.length} files into fewer directories by domain`);
2125
+ recommendations.push(
2126
+ `Consolidate ${files.length} files into fewer directories by domain`
2127
+ );
1942
2128
  }
1943
2129
  if (dirGroups.size > 5) {
1944
- recommendations.push("Consider barrel exports to reduce directory navigation");
2130
+ recommendations.push(
2131
+ "Consider barrel exports to reduce directory navigation"
2132
+ );
1945
2133
  }
1946
2134
  if (gini > 0.5) {
1947
2135
  recommendations.push("Redistribute files more evenly across directories");
@@ -1966,7 +2154,11 @@ function calculateConceptCohesion(params) {
1966
2154
  return {
1967
2155
  score: 1,
1968
2156
  rating: "excellent",
1969
- analysis: { uniqueDomains: 0, domainConcentration: 0, exportPurposeClarity: 1 }
2157
+ analysis: {
2158
+ uniqueDomains: 0,
2159
+ domainConcentration: 0,
2160
+ exportPurposeClarity: 1
2161
+ }
1970
2162
  };
1971
2163
  }
1972
2164
  const allDomains = [];
@@ -2072,7 +2264,10 @@ function calculateAiSignalClarity(params) {
2072
2264
  recommendations: []
2073
2265
  };
2074
2266
  }
2075
- const overloadRatio = Math.min(1, overloadedSymbols / Math.max(1, totalSymbols));
2267
+ const overloadRatio = Math.min(
2268
+ 1,
2269
+ overloadedSymbols / Math.max(1, totalSymbols)
2270
+ );
2076
2271
  const overloadSignal = {
2077
2272
  name: "Symbol Overloading",
2078
2273
  count: overloadedSymbols,
@@ -2096,7 +2291,10 @@ function calculateAiSignalClarity(params) {
2096
2291
  // 20% weight
2097
2292
  description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
2098
2293
  };
2099
- const sideEffectRatio = Math.min(1, implicitSideEffects / Math.max(1, totalExports));
2294
+ const sideEffectRatio = Math.min(
2295
+ 1,
2296
+ implicitSideEffects / Math.max(1, totalExports)
2297
+ );
2100
2298
  const sideEffectSignal = {
2101
2299
  name: "Implicit Side Effects",
2102
2300
  count: implicitSideEffects,
@@ -2104,7 +2302,10 @@ function calculateAiSignalClarity(params) {
2104
2302
  // 15% weight
2105
2303
  description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
2106
2304
  };
2107
- const callbackRatio = Math.min(1, deepCallbacks / Math.max(1, totalSymbols * 0.1));
2305
+ const callbackRatio = Math.min(
2306
+ 1,
2307
+ deepCallbacks / Math.max(1, totalSymbols * 0.1)
2308
+ );
2108
2309
  const callbackSignal = {
2109
2310
  name: "Callback Nesting",
2110
2311
  count: deepCallbacks,
@@ -2112,7 +2313,10 @@ function calculateAiSignalClarity(params) {
2112
2313
  // 10% weight
2113
2314
  description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
2114
2315
  };
2115
- const ambiguousRatio = Math.min(1, ambiguousNames / Math.max(1, totalSymbols));
2316
+ const ambiguousRatio = Math.min(
2317
+ 1,
2318
+ ambiguousNames / Math.max(1, totalSymbols)
2319
+ );
2116
2320
  const ambiguousSignal = {
2117
2321
  name: "Ambiguous Names",
2118
2322
  count: ambiguousNames,
@@ -2120,7 +2324,10 @@ function calculateAiSignalClarity(params) {
2120
2324
  // 10% weight
2121
2325
  description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
2122
2326
  };
2123
- const undocRatio = Math.min(1, undocumentedExports / Math.max(1, totalExports));
2327
+ const undocRatio = Math.min(
2328
+ 1,
2329
+ undocumentedExports / Math.max(1, totalExports)
2330
+ );
2124
2331
  const undocSignal = {
2125
2332
  name: "Undocumented Exports",
2126
2333
  count: undocumentedExports,
@@ -2137,30 +2344,45 @@ function calculateAiSignalClarity(params) {
2137
2344
  ambiguousSignal,
2138
2345
  undocSignal
2139
2346
  ];
2140
- const score = Math.min(100, signals.reduce((sum, s) => sum + s.riskContribution, 0));
2347
+ const score = Math.min(
2348
+ 100,
2349
+ signals.reduce((sum, s) => sum + s.riskContribution, 0)
2350
+ );
2141
2351
  let rating;
2142
2352
  if (score < 10) rating = "minimal";
2143
2353
  else if (score < 25) rating = "low";
2144
2354
  else if (score < 50) rating = "moderate";
2145
2355
  else if (score < 75) rating = "high";
2146
2356
  else rating = "severe";
2147
- const topSignal = signals.reduce((a, b) => a.riskContribution > b.riskContribution ? a : b);
2357
+ const topSignal = signals.reduce(
2358
+ (a, b) => a.riskContribution > b.riskContribution ? a : b
2359
+ );
2148
2360
  const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant AI signal claritys detected";
2149
2361
  const recommendations = [];
2150
2362
  if (overloadSignal.riskContribution > 5) {
2151
- recommendations.push(`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`);
2363
+ recommendations.push(
2364
+ `Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
2365
+ );
2152
2366
  }
2153
2367
  if (magicSignal.riskContribution > 5) {
2154
- recommendations.push(`Extract ${magicLiterals} magic literals into named constants`);
2368
+ recommendations.push(
2369
+ `Extract ${magicLiterals} magic literals into named constants`
2370
+ );
2155
2371
  }
2156
2372
  if (trapSignal.riskContribution > 5) {
2157
- recommendations.push(`Replace ${booleanTraps} boolean traps with named options objects`);
2373
+ recommendations.push(
2374
+ `Replace ${booleanTraps} boolean traps with named options objects`
2375
+ );
2158
2376
  }
2159
2377
  if (undocSignal.riskContribution > 5) {
2160
- recommendations.push(`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`);
2378
+ recommendations.push(
2379
+ `Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
2380
+ );
2161
2381
  }
2162
2382
  if (sideEffectSignal.riskContribution > 5) {
2163
- recommendations.push("Mark functions with side effects explicitly in their names or docs");
2383
+ recommendations.push(
2384
+ "Mark functions with side effects explicitly in their names or docs"
2385
+ );
2164
2386
  }
2165
2387
  return {
2166
2388
  score: Math.round(score),
@@ -2185,7 +2407,10 @@ function calculateAgentGrounding(params) {
2185
2407
  domainVocabularySize
2186
2408
  } = params;
2187
2409
  const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
2188
- const structureClarityScore = Math.max(0, Math.round(100 - deepDirRatio * 80));
2410
+ const structureClarityScore = Math.max(
2411
+ 0,
2412
+ Math.round(100 - deepDirRatio * 80)
2413
+ );
2189
2414
  const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
2190
2415
  const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
2191
2416
  let entryPointScore = 60;
@@ -2197,7 +2422,10 @@ function calculateAgentGrounding(params) {
2197
2422
  const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
2198
2423
  const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
2199
2424
  const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
2200
- const domainConsistencyScore = Math.max(0, Math.round(100 - inconsistencyRatio * 80));
2425
+ const domainConsistencyScore = Math.max(
2426
+ 0,
2427
+ Math.round(100 - inconsistencyRatio * 80)
2428
+ );
2201
2429
  const score = Math.round(
2202
2430
  structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
2203
2431
  );
@@ -2209,21 +2437,33 @@ function calculateAgentGrounding(params) {
2209
2437
  else rating = "disorienting";
2210
2438
  const recommendations = [];
2211
2439
  if (structureClarityScore < 70) {
2212
- recommendations.push(`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`);
2440
+ recommendations.push(
2441
+ `Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
2442
+ );
2213
2443
  }
2214
2444
  if (selfDocumentationScore < 70) {
2215
- recommendations.push(`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`);
2445
+ recommendations.push(
2446
+ `Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
2447
+ );
2216
2448
  }
2217
2449
  if (!hasRootReadme) {
2218
- recommendations.push("Add a root README.md so agents understand the project context immediately");
2450
+ recommendations.push(
2451
+ "Add a root README.md so agents understand the project context immediately"
2452
+ );
2219
2453
  } else if (!readmeIsFresh) {
2220
- recommendations.push("Update README.md \u2014 stale entry-point documentation disorients agents");
2454
+ recommendations.push(
2455
+ "Update README.md \u2014 stale entry-point documentation disorients agents"
2456
+ );
2221
2457
  }
2222
2458
  if (apiClarityScore < 70) {
2223
- recommendations.push(`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`);
2459
+ recommendations.push(
2460
+ `Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
2461
+ );
2224
2462
  }
2225
2463
  if (domainConsistencyScore < 70) {
2226
- recommendations.push(`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`);
2464
+ recommendations.push(
2465
+ `Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
2466
+ );
2227
2467
  }
2228
2468
  return {
2229
2469
  score,
@@ -2256,7 +2496,9 @@ function calculateTestabilityIndex(params) {
2256
2496
  const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
2257
2497
  const purityScore = Math.round(purityRatio * 100);
2258
2498
  const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
2259
- const dependencyInjectionScore = Math.round(Math.min(100, injectionRatio * 100));
2499
+ const dependencyInjectionScore = Math.round(
2500
+ Math.min(100, injectionRatio * 100)
2501
+ );
2260
2502
  const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
2261
2503
  const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
2262
2504
  const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
@@ -2272,25 +2514,36 @@ function calculateTestabilityIndex(params) {
2272
2514
  else rating = "unverifiable";
2273
2515
  let aiChangeSafetyRating;
2274
2516
  if (rawCoverageRatio >= 0.5 && score >= 70) aiChangeSafetyRating = "safe";
2275
- else if (rawCoverageRatio >= 0.2 && score >= 50) aiChangeSafetyRating = "moderate-risk";
2517
+ else if (rawCoverageRatio >= 0.2 && score >= 50)
2518
+ aiChangeSafetyRating = "moderate-risk";
2276
2519
  else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
2277
2520
  else aiChangeSafetyRating = "blind-risk";
2278
2521
  const recommendations = [];
2279
2522
  if (!hasTestFramework) {
2280
- recommendations.push("Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests");
2523
+ recommendations.push(
2524
+ "Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
2525
+ );
2281
2526
  }
2282
2527
  if (rawCoverageRatio < 0.3) {
2283
2528
  const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
2284
- recommendations.push(`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`);
2529
+ recommendations.push(
2530
+ `Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
2531
+ );
2285
2532
  }
2286
2533
  if (purityScore < 50) {
2287
- recommendations.push("Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable");
2534
+ recommendations.push(
2535
+ "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
2536
+ );
2288
2537
  }
2289
2538
  if (dependencyInjectionScore < 50 && totalClasses > 0) {
2290
- recommendations.push("Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable");
2539
+ recommendations.push(
2540
+ "Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
2541
+ );
2291
2542
  }
2292
2543
  if (externalStateMutations > totalFunctions * 0.3) {
2293
- recommendations.push("Reduce direct state mutations \u2014 return values instead to improve observability");
2544
+ recommendations.push(
2545
+ "Reduce direct state mutations \u2014 return values instead to improve observability"
2546
+ );
2294
2547
  }
2295
2548
  return {
2296
2549
  score,
@@ -2307,7 +2560,12 @@ function calculateTestabilityIndex(params) {
2307
2560
  };
2308
2561
  }
2309
2562
  function calculateDocDrift(params) {
2310
- const { uncommentedExports, totalExports, outdatedComments, undocumentedComplexity } = params;
2563
+ const {
2564
+ uncommentedExports,
2565
+ totalExports,
2566
+ outdatedComments,
2567
+ undocumentedComplexity
2568
+ } = params;
2311
2569
  const uncommentedRatio = totalExports > 0 ? uncommentedExports / totalExports : 0;
2312
2570
  const outdatedScore = Math.min(100, outdatedComments * 15);
2313
2571
  const uncommentedScore = Math.min(100, uncommentedRatio * 100);
@@ -2324,13 +2582,19 @@ function calculateDocDrift(params) {
2324
2582
  else rating = "severe";
2325
2583
  const recommendations = [];
2326
2584
  if (outdatedComments > 0) {
2327
- recommendations.push(`Update or remove ${outdatedComments} outdated comments that contradict the code.`);
2585
+ recommendations.push(
2586
+ `Update or remove ${outdatedComments} outdated comments that contradict the code.`
2587
+ );
2328
2588
  }
2329
2589
  if (uncommentedRatio > 0.3) {
2330
- recommendations.push(`Add JSDoc to ${uncommentedExports} uncommented exports.`);
2590
+ recommendations.push(
2591
+ `Add JSDoc to ${uncommentedExports} uncommented exports.`
2592
+ );
2331
2593
  }
2332
2594
  if (undocumentedComplexity > 0) {
2333
- recommendations.push(`Explain the business logic for ${undocumentedComplexity} highly complex functions.`);
2595
+ recommendations.push(
2596
+ `Explain the business logic for ${undocumentedComplexity} highly complex functions.`
2597
+ );
2334
2598
  }
2335
2599
  return {
2336
2600
  score: finalScore,
@@ -2344,7 +2608,12 @@ function calculateDocDrift(params) {
2344
2608
  };
2345
2609
  }
2346
2610
  function calculateDependencyHealth(params) {
2347
- const { totalPackages, outdatedPackages, deprecatedPackages, trainingCutoffSkew } = params;
2611
+ const {
2612
+ totalPackages,
2613
+ outdatedPackages,
2614
+ deprecatedPackages,
2615
+ trainingCutoffSkew
2616
+ } = params;
2348
2617
  const outdatedRatio = totalPackages > 0 ? outdatedPackages / totalPackages : 0;
2349
2618
  const deprecatedRatio = totalPackages > 0 ? deprecatedPackages / totalPackages : 0;
2350
2619
  const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
@@ -2359,19 +2628,27 @@ function calculateDependencyHealth(params) {
2359
2628
  else if (score >= 30) rating = "poor";
2360
2629
  else rating = "hazardous";
2361
2630
  let aiKnowledgeConfidence;
2362
- if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0) aiKnowledgeConfidence = "high";
2363
- else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2) aiKnowledgeConfidence = "moderate";
2631
+ if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0)
2632
+ aiKnowledgeConfidence = "high";
2633
+ else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2)
2634
+ aiKnowledgeConfidence = "moderate";
2364
2635
  else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
2365
2636
  else aiKnowledgeConfidence = "blind";
2366
2637
  const recommendations = [];
2367
2638
  if (deprecatedPackages > 0) {
2368
- recommendations.push(`Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`);
2639
+ recommendations.push(
2640
+ `Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
2641
+ );
2369
2642
  }
2370
2643
  if (outdatedRatio > 0.2) {
2371
- recommendations.push(`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`);
2644
+ recommendations.push(
2645
+ `Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
2646
+ );
2372
2647
  }
2373
2648
  if (trainingCutoffSkew > 0.5) {
2374
- recommendations.push("High training cutoff skew detected. AI may hallucinate APIs that were introduced recently.");
2649
+ recommendations.push(
2650
+ "High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
2651
+ );
2375
2652
  }
2376
2653
  return {
2377
2654
  score,
@@ -2412,10 +2689,14 @@ function calculateChangeAmplification(params) {
2412
2689
  else if (score < 90) rating = "contained";
2413
2690
  const recommendations = [];
2414
2691
  if (score < 70 && hotspots.length > 0) {
2415
- recommendations.push(`Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`);
2692
+ recommendations.push(
2693
+ `Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`
2694
+ );
2416
2695
  }
2417
2696
  if (maxAmplification > 30) {
2418
- recommendations.push(`Break down key bottlenecks with amplification factor > 30.`);
2697
+ recommendations.push(
2698
+ `Break down key bottlenecks with amplification factor > 30.`
2699
+ );
2419
2700
  }
2420
2701
  return {
2421
2702
  score: Math.round(score),
@@ -2497,7 +2778,11 @@ function calculateExtendedFutureProofScore(params) {
2497
2778
  recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2498
2779
  }
2499
2780
  for (const rec of params.agentGrounding.recommendations) {
2500
- recommendations.push({ action: rec, estimatedImpact: 6, priority: "medium" });
2781
+ recommendations.push({
2782
+ action: rec,
2783
+ estimatedImpact: 6,
2784
+ priority: "medium"
2785
+ });
2501
2786
  }
2502
2787
  for (const rec of params.testability.recommendations) {
2503
2788
  const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
@@ -2508,12 +2793,20 @@ function calculateExtendedFutureProofScore(params) {
2508
2793
  }
2509
2794
  if (params.docDrift) {
2510
2795
  for (const rec of params.docDrift.recommendations) {
2511
- recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2796
+ recommendations.push({
2797
+ action: rec,
2798
+ estimatedImpact: 8,
2799
+ priority: "high"
2800
+ });
2512
2801
  }
2513
2802
  }
2514
2803
  if (params.dependencyHealth) {
2515
2804
  for (const rec of params.dependencyHealth.recommendations) {
2516
- recommendations.push({ action: rec, estimatedImpact: 7, priority: "medium" });
2805
+ recommendations.push({
2806
+ action: rec,
2807
+ estimatedImpact: 7,
2808
+ priority: "medium"
2809
+ });
2517
2810
  }
2518
2811
  }
2519
2812
  const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
@@ -2609,6 +2902,39 @@ function clearHistory(rootDir) {
2609
2902
  (0, import_fs4.writeFileSync)(historyPath, JSON.stringify([]));
2610
2903
  }
2611
2904
  }
2905
+
2906
+ // src/utils/history-git.ts
2907
+ var import_child_process = require("child_process");
2908
+ function getFileCommitTimestamps(file) {
2909
+ const lineStamps = {};
2910
+ try {
2911
+ const output = (0, import_child_process.execSync)(`git blame -t "${file}"`, {
2912
+ encoding: "utf-8",
2913
+ stdio: ["ignore", "pipe", "ignore"]
2914
+ });
2915
+ const lines = output.split("\n");
2916
+ for (const line of lines) {
2917
+ if (!line) continue;
2918
+ const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
2919
+ if (match) {
2920
+ const ts = parseInt(match[1], 10);
2921
+ const ln = parseInt(match[2], 10);
2922
+ lineStamps[ln] = ts;
2923
+ }
2924
+ }
2925
+ } catch {
2926
+ }
2927
+ return lineStamps;
2928
+ }
2929
+ function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
2930
+ let latest = 0;
2931
+ for (let i = startLine; i <= endLine; i++) {
2932
+ if (lineStamps[i] && lineStamps[i] > latest) {
2933
+ latest = lineStamps[i];
2934
+ }
2935
+ }
2936
+ return latest;
2937
+ }
2612
2938
  // Annotate the CommonJS export names for ESM import in node:
2613
2939
  0 && (module.exports = {
2614
2940
  CONTEXT_TIER_THRESHOLDS,
@@ -2624,6 +2950,7 @@ function clearHistory(rootDir) {
2624
2950
  SIZE_ADJUSTED_THRESHOLDS,
2625
2951
  TOOL_NAME_MAP,
2626
2952
  TypeScriptParser,
2953
+ VAGUE_FILE_NAMES,
2627
2954
  calculateAgentGrounding,
2628
2955
  calculateAiSignalClarity,
2629
2956
  calculateChangeAmplification,
@@ -2658,8 +2985,10 @@ function clearHistory(rootDir) {
2658
2985
  generateHTML,
2659
2986
  getDebtBreakdown,
2660
2987
  getElapsedTime,
2988
+ getFileCommitTimestamps,
2661
2989
  getFileExtension,
2662
2990
  getHistorySummary,
2991
+ getLineRangeLastModifiedCached,
2663
2992
  getModelPreset,
2664
2993
  getParser,
2665
2994
  getProjectSizeTier,
@@ -2685,5 +3014,6 @@ function clearHistory(rootDir) {
2685
3014
  readFileContent,
2686
3015
  resolveOutputPath,
2687
3016
  saveScoreEntry,
3017
+ scanEntries,
2688
3018
  scanFiles
2689
3019
  });