@aiready/core 0.9.33 → 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/client.d.mts CHANGED
@@ -131,6 +131,8 @@ interface ScanOptions {
131
131
  include?: string[];
132
132
  exclude?: string[];
133
133
  maxDepth?: number;
134
+ onProgress?: (processed: number, total: number, message: string) => void;
135
+ includeTests?: boolean;
134
136
  }
135
137
  interface AIReadyConfig {
136
138
  scan?: {
package/dist/client.d.ts CHANGED
@@ -131,6 +131,8 @@ interface ScanOptions {
131
131
  include?: string[];
132
132
  exclude?: string[];
133
133
  maxDepth?: number;
134
+ onProgress?: (processed: number, total: number, message: string) => void;
135
+ includeTests?: boolean;
134
136
  }
135
137
  interface AIReadyConfig {
136
138
  scan?: {
package/dist/index.d.mts CHANGED
@@ -2,6 +2,7 @@ import { ScanOptions, AIReadyConfig, CostConfig, ModelContextTier, Comprehension
2
2
  export { AnalysisResult, BusinessReport, CONTEXT_TIER_THRESHOLDS, CommonASTNode, DEFAULT_TOOL_WEIGHTS, ExportInfo, GraphData, GraphEdge, GraphIssueSeverity, GraphMetadata, GraphNode, ImportInfo, Issue, IssueType, LANGUAGE_EXTENSIONS, LanguageConfig, Location, Metrics, ParseError, ParseStatistics, Report, SIZE_ADJUSTED_THRESHOLDS, ScoringConfig, ScoringResult, SourceLocation, SourceRange, TOOL_NAME_MAP, calculateOverallScore, formatScore, formatToolScore, generateHTML, getProjectSizeTier, getRating, getRatingDisplay, getRatingWithContext, getRecommendedThreshold, getToolWeight, normalizeToolName, parseWeightString } from './client.mjs';
3
3
 
4
4
  declare const DEFAULT_EXCLUDE: string[];
5
+ declare const VAGUE_FILE_NAMES: Set<string>;
5
6
  /**
6
7
  * Scan files in a directory using glob patterns
7
8
  *
@@ -12,6 +13,14 @@ declare const DEFAULT_EXCLUDE: string[];
12
13
  * @returns Array of absolute file paths matching the patterns
13
14
  */
14
15
  declare function scanFiles(options: ScanOptions): Promise<string[]>;
16
+ /**
17
+ * Scan for both files and directories, respecting ignore rules.
18
+ * Useful for tools that need to analyze directory structure.
19
+ */
20
+ declare function scanEntries(options: ScanOptions): Promise<{
21
+ files: string[];
22
+ dirs: string[];
23
+ }>;
15
24
  declare function readFileContent(filePath: string): Promise<string>;
16
25
  declare function getFileExtension(filePath: string): string;
17
26
  declare function isSourceFile(filePath: string): boolean;
@@ -826,4 +835,13 @@ declare function exportHistory(rootDir: string, format?: 'json' | 'csv'): string
826
835
  */
827
836
  declare function clearHistory(rootDir: string): void;
828
837
 
829
- export { AIReadyConfig, type ASTNode, AcceptancePrediction, type AgentGroundingScore, type AiSignalClarity, type AiSignalClaritySignal, type CLIOptions, type ChangeAmplificationScore, type CognitiveLoad, ComprehensionDifficulty, type ConceptCohesion, CostConfig, DEFAULT_COST_CONFIG, DEFAULT_EXCLUDE, type DebtBreakdown, type DependencyHealthScore, type DocDriftRisk, type ExportWithImports, type FileImport, type FileWithDomain, type KnowledgeConcentrationRisk, Language, LanguageParser, type LoadFactor, MODEL_PRICING_PRESETS, ModelContextTier, type ModelPricingPreset, NamingConvention, ParseResult, ParserFactory, type PatternEntropy, ProductivityImpact, PythonParser, type RemediationVelocity, ScanOptions, type ScoreHistoryEntry, type ScoreTrend, type SemanticDistance, type TechnicalDebtInterest, type TestabilityIndex, ToolScoringOutput, TypeScriptParser, calculateAgentGrounding, calculateAiSignalClarity, calculateChangeAmplification, calculateCognitiveLoad, calculateComprehensionDifficulty, calculateConceptCohesion, calculateDependencyHealth, calculateDocDrift, calculateExtendedFutureProofScore, calculateFutureProofScore, calculateImportSimilarity, calculateKnowledgeConcentration, calculateMonthlyCost, calculatePatternEntropy, calculateProductivityImpact, calculateRemediationVelocity, calculateScoreTrend, calculateSemanticDistance, calculateTechnicalDebtInterest, calculateTestabilityIndex, clearHistory, estimateTokens, exportHistory, extractFunctions, extractImports, formatAcceptanceRate, formatCost, formatHours, getDebtBreakdown, getElapsedTime, getFileExtension, getHistorySummary, getModelPreset, getParser, getSupportedLanguages, handleCLIError, handleJSONOutput, isFileSupported, isSourceFile, loadConfig, loadMergedConfig, loadScoreHistory, mergeConfigWithDefaults, parseCode, parseFileExports, predictAcceptanceRate, readFileContent, resolveOutputPath, saveScoreEntry, scanFiles };
838
+ /**
839
+ * Get git commit timestamps for each line in a file
840
+ */
841
+ declare function getFileCommitTimestamps(file: string): Record<number, number>;
842
+ /**
843
+ * Get the latest commit timestamp for a line range
844
+ */
845
+ declare function getLineRangeLastModifiedCached(lineStamps: Record<number, number>, startLine: number, endLine: number): number;
846
+
847
+ export { AIReadyConfig, type ASTNode, AcceptancePrediction, type AgentGroundingScore, type AiSignalClarity, type AiSignalClaritySignal, type CLIOptions, type ChangeAmplificationScore, type CognitiveLoad, ComprehensionDifficulty, type ConceptCohesion, CostConfig, DEFAULT_COST_CONFIG, DEFAULT_EXCLUDE, type DebtBreakdown, type DependencyHealthScore, type DocDriftRisk, type ExportWithImports, type FileImport, type FileWithDomain, type KnowledgeConcentrationRisk, Language, LanguageParser, type LoadFactor, MODEL_PRICING_PRESETS, ModelContextTier, type ModelPricingPreset, NamingConvention, ParseResult, ParserFactory, type PatternEntropy, ProductivityImpact, PythonParser, type RemediationVelocity, ScanOptions, type ScoreHistoryEntry, type ScoreTrend, type SemanticDistance, type TechnicalDebtInterest, type TestabilityIndex, ToolScoringOutput, TypeScriptParser, VAGUE_FILE_NAMES, calculateAgentGrounding, calculateAiSignalClarity, calculateChangeAmplification, calculateCognitiveLoad, calculateComprehensionDifficulty, calculateConceptCohesion, calculateDependencyHealth, calculateDocDrift, calculateExtendedFutureProofScore, calculateFutureProofScore, calculateImportSimilarity, calculateKnowledgeConcentration, calculateMonthlyCost, calculatePatternEntropy, calculateProductivityImpact, calculateRemediationVelocity, calculateScoreTrend, calculateSemanticDistance, calculateTechnicalDebtInterest, calculateTestabilityIndex, clearHistory, estimateTokens, exportHistory, extractFunctions, extractImports, formatAcceptanceRate, formatCost, formatHours, getDebtBreakdown, getElapsedTime, getFileCommitTimestamps, getFileExtension, getHistorySummary, getLineRangeLastModifiedCached, getModelPreset, getParser, getSupportedLanguages, handleCLIError, handleJSONOutput, isFileSupported, isSourceFile, loadConfig, loadMergedConfig, loadScoreHistory, mergeConfigWithDefaults, parseCode, parseFileExports, predictAcceptanceRate, readFileContent, resolveOutputPath, saveScoreEntry, scanEntries, scanFiles };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { ScanOptions, AIReadyConfig, CostConfig, ModelContextTier, Comprehension
2
2
  export { AnalysisResult, BusinessReport, CONTEXT_TIER_THRESHOLDS, CommonASTNode, DEFAULT_TOOL_WEIGHTS, ExportInfo, GraphData, GraphEdge, GraphIssueSeverity, GraphMetadata, GraphNode, ImportInfo, Issue, IssueType, LANGUAGE_EXTENSIONS, LanguageConfig, Location, Metrics, ParseError, ParseStatistics, Report, SIZE_ADJUSTED_THRESHOLDS, ScoringConfig, ScoringResult, SourceLocation, SourceRange, TOOL_NAME_MAP, calculateOverallScore, formatScore, formatToolScore, generateHTML, getProjectSizeTier, getRating, getRatingDisplay, getRatingWithContext, getRecommendedThreshold, getToolWeight, normalizeToolName, parseWeightString } from './client.js';
3
3
 
4
4
  declare const DEFAULT_EXCLUDE: string[];
5
+ declare const VAGUE_FILE_NAMES: Set<string>;
5
6
  /**
6
7
  * Scan files in a directory using glob patterns
7
8
  *
@@ -12,6 +13,14 @@ declare const DEFAULT_EXCLUDE: string[];
12
13
  * @returns Array of absolute file paths matching the patterns
13
14
  */
14
15
  declare function scanFiles(options: ScanOptions): Promise<string[]>;
16
+ /**
17
+ * Scan for both files and directories, respecting ignore rules.
18
+ * Useful for tools that need to analyze directory structure.
19
+ */
20
+ declare function scanEntries(options: ScanOptions): Promise<{
21
+ files: string[];
22
+ dirs: string[];
23
+ }>;
15
24
  declare function readFileContent(filePath: string): Promise<string>;
16
25
  declare function getFileExtension(filePath: string): string;
17
26
  declare function isSourceFile(filePath: string): boolean;
@@ -826,4 +835,13 @@ declare function exportHistory(rootDir: string, format?: 'json' | 'csv'): string
826
835
  */
827
836
  declare function clearHistory(rootDir: string): void;
828
837
 
829
- export { AIReadyConfig, type ASTNode, AcceptancePrediction, type AgentGroundingScore, type AiSignalClarity, type AiSignalClaritySignal, type CLIOptions, type ChangeAmplificationScore, type CognitiveLoad, ComprehensionDifficulty, type ConceptCohesion, CostConfig, DEFAULT_COST_CONFIG, DEFAULT_EXCLUDE, type DebtBreakdown, type DependencyHealthScore, type DocDriftRisk, type ExportWithImports, type FileImport, type FileWithDomain, type KnowledgeConcentrationRisk, Language, LanguageParser, type LoadFactor, MODEL_PRICING_PRESETS, ModelContextTier, type ModelPricingPreset, NamingConvention, ParseResult, ParserFactory, type PatternEntropy, ProductivityImpact, PythonParser, type RemediationVelocity, ScanOptions, type ScoreHistoryEntry, type ScoreTrend, type SemanticDistance, type TechnicalDebtInterest, type TestabilityIndex, ToolScoringOutput, TypeScriptParser, calculateAgentGrounding, calculateAiSignalClarity, calculateChangeAmplification, calculateCognitiveLoad, calculateComprehensionDifficulty, calculateConceptCohesion, calculateDependencyHealth, calculateDocDrift, calculateExtendedFutureProofScore, calculateFutureProofScore, calculateImportSimilarity, calculateKnowledgeConcentration, calculateMonthlyCost, calculatePatternEntropy, calculateProductivityImpact, calculateRemediationVelocity, calculateScoreTrend, calculateSemanticDistance, calculateTechnicalDebtInterest, calculateTestabilityIndex, clearHistory, estimateTokens, exportHistory, extractFunctions, extractImports, formatAcceptanceRate, formatCost, formatHours, getDebtBreakdown, getElapsedTime, getFileExtension, getHistorySummary, getModelPreset, getParser, getSupportedLanguages, handleCLIError, handleJSONOutput, isFileSupported, isSourceFile, loadConfig, loadMergedConfig, loadScoreHistory, mergeConfigWithDefaults, parseCode, parseFileExports, predictAcceptanceRate, readFileContent, resolveOutputPath, saveScoreEntry, scanFiles };
838
+ /**
839
+ * Get git commit timestamps for each line in a file
840
+ */
841
+ declare function getFileCommitTimestamps(file: string): Record<number, number>;
842
+ /**
843
+ * Get the latest commit timestamp for a line range
844
+ */
845
+ declare function getLineRangeLastModifiedCached(lineStamps: Record<number, number>, startLine: number, endLine: number): number;
846
+
847
+ export { AIReadyConfig, type ASTNode, AcceptancePrediction, type AgentGroundingScore, type AiSignalClarity, type AiSignalClaritySignal, type CLIOptions, type ChangeAmplificationScore, type CognitiveLoad, ComprehensionDifficulty, type ConceptCohesion, CostConfig, DEFAULT_COST_CONFIG, DEFAULT_EXCLUDE, type DebtBreakdown, type DependencyHealthScore, type DocDriftRisk, type ExportWithImports, type FileImport, type FileWithDomain, type KnowledgeConcentrationRisk, Language, LanguageParser, type LoadFactor, MODEL_PRICING_PRESETS, ModelContextTier, type ModelPricingPreset, NamingConvention, ParseResult, ParserFactory, type PatternEntropy, ProductivityImpact, PythonParser, type RemediationVelocity, ScanOptions, type ScoreHistoryEntry, type ScoreTrend, type SemanticDistance, type TechnicalDebtInterest, type TestabilityIndex, ToolScoringOutput, TypeScriptParser, VAGUE_FILE_NAMES, calculateAgentGrounding, calculateAiSignalClarity, calculateChangeAmplification, calculateCognitiveLoad, calculateComprehensionDifficulty, calculateConceptCohesion, calculateDependencyHealth, calculateDocDrift, calculateExtendedFutureProofScore, calculateFutureProofScore, calculateImportSimilarity, calculateKnowledgeConcentration, calculateMonthlyCost, calculatePatternEntropy, calculateProductivityImpact, calculateRemediationVelocity, calculateScoreTrend, calculateSemanticDistance, calculateTechnicalDebtInterest, calculateTestabilityIndex, clearHistory, estimateTokens, exportHistory, extractFunctions, extractImports, formatAcceptanceRate, formatCost, formatHours, getDebtBreakdown, getElapsedTime, getFileCommitTimestamps, getFileExtension, getHistorySummary, getLineRangeLastModifiedCached, getModelPreset, getParser, getSupportedLanguages, handleCLIError, handleJSONOutput, isFileSupported, isSourceFile, loadConfig, loadMergedConfig, loadScoreHistory, mergeConfigWithDefaults, parseCode, parseFileExports, predictAcceptanceRate, readFileContent, resolveOutputPath, saveScoreEntry, scanEntries, scanFiles };
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,20 +235,35 @@ async function scanFiles(options) {
207
235
  ignoreFromFile = [];
208
236
  }
209
237
  }
238
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
239
+ const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
210
240
  const finalExclude = [
211
- .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])
241
+ .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
212
242
  ];
213
243
  const files = await (0, import_glob.glob)(include, {
214
244
  cwd: rootDir,
215
245
  ignore: finalExclude,
216
246
  absolute: true
217
247
  });
218
- const gitignorePath = (0, import_path.join)(rootDir || ".", ".gitignore");
219
- 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) {
220
254
  try {
221
- const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
222
255
  const ig = (0, import_ignore.default)();
223
- 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
+ }
224
267
  const filtered = files.filter((f) => {
225
268
  let rel = (0, import_path.relative)(rootDir || ".", f).replace(/\\/g, "/");
226
269
  if (rel === "") rel = f;
@@ -233,6 +276,54 @@ async function scanFiles(options) {
233
276
  }
234
277
  return files;
235
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
+ }
236
327
  async function readFileContent(filePath) {
237
328
  return (0, import_promises.readFile)(filePath, "utf-8");
238
329
  }
@@ -457,9 +548,14 @@ async function loadConfig(rootDir) {
457
548
  return config;
458
549
  } catch (error) {
459
550
  const errorMessage = error instanceof Error ? error.message : String(error);
460
- throw new Error(
551
+ const e = new Error(
461
552
  `Failed to load config from ${configPath}: ${errorMessage}`
462
553
  );
554
+ try {
555
+ e.cause = error instanceof Error ? error : void 0;
556
+ } catch {
557
+ }
558
+ throw e;
463
559
  }
464
560
  }
465
561
  }
@@ -2806,6 +2902,39 @@ function clearHistory(rootDir) {
2806
2902
  (0, import_fs4.writeFileSync)(historyPath, JSON.stringify([]));
2807
2903
  }
2808
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
+ }
2809
2938
  // Annotate the CommonJS export names for ESM import in node:
2810
2939
  0 && (module.exports = {
2811
2940
  CONTEXT_TIER_THRESHOLDS,
@@ -2821,6 +2950,7 @@ function clearHistory(rootDir) {
2821
2950
  SIZE_ADJUSTED_THRESHOLDS,
2822
2951
  TOOL_NAME_MAP,
2823
2952
  TypeScriptParser,
2953
+ VAGUE_FILE_NAMES,
2824
2954
  calculateAgentGrounding,
2825
2955
  calculateAiSignalClarity,
2826
2956
  calculateChangeAmplification,
@@ -2855,8 +2985,10 @@ function clearHistory(rootDir) {
2855
2985
  generateHTML,
2856
2986
  getDebtBreakdown,
2857
2987
  getElapsedTime,
2988
+ getFileCommitTimestamps,
2858
2989
  getFileExtension,
2859
2990
  getHistorySummary,
2991
+ getLineRangeLastModifiedCached,
2860
2992
  getModelPreset,
2861
2993
  getParser,
2862
2994
  getProjectSizeTier,
@@ -2882,5 +3014,6 @@ function clearHistory(rootDir) {
2882
3014
  readFileContent,
2883
3015
  resolveOutputPath,
2884
3016
  saveScoreEntry,
3017
+ scanEntries,
2885
3018
  scanFiles
2886
3019
  });
package/dist/index.mjs CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  import { glob } from "glob";
25
25
  import { readFile } from "fs/promises";
26
26
  import { existsSync } from "fs";
27
- import { join, relative } from "path";
27
+ import { join, relative, dirname } from "path";
28
28
  import ignorePkg from "ignore";
29
29
  var DEFAULT_EXCLUDE = [
30
30
  // Dependencies
@@ -40,6 +40,8 @@ var DEFAULT_EXCLUDE = [
40
40
  "**/cdk.out/**",
41
41
  // Framework-specific build dirs
42
42
  "**/.next/**",
43
+ "**/.sst/**",
44
+ "**/.open-next/**",
43
45
  "**/.nuxt/**",
44
46
  "**/.vuepress/**",
45
47
  "**/.cache/**",
@@ -71,6 +73,28 @@ var DEFAULT_EXCLUDE = [
71
73
  "**/*.log",
72
74
  "**/.DS_Store"
73
75
  ];
76
+ var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
77
+ "utils",
78
+ "helpers",
79
+ "helper",
80
+ "misc",
81
+ "common",
82
+ "shared",
83
+ "tools",
84
+ "util",
85
+ "lib",
86
+ "libs",
87
+ "stuff",
88
+ "functions",
89
+ "methods",
90
+ "handlers",
91
+ "data",
92
+ "temp",
93
+ "tmp",
94
+ "test-utils",
95
+ "test-helpers",
96
+ "mocks"
97
+ ]);
74
98
  async function scanFiles(options) {
75
99
  const {
76
100
  rootDir,
@@ -88,20 +112,35 @@ async function scanFiles(options) {
88
112
  ignoreFromFile = [];
89
113
  }
90
114
  }
115
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
116
+ const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
91
117
  const finalExclude = [
92
- .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])
118
+ .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
93
119
  ];
94
120
  const files = await glob(include, {
95
121
  cwd: rootDir,
96
122
  ignore: finalExclude,
97
123
  absolute: true
98
124
  });
99
- const gitignorePath = join(rootDir || ".", ".gitignore");
100
- if (existsSync(gitignorePath)) {
125
+ const gitignoreFiles = await glob("**/.gitignore", {
126
+ cwd: rootDir,
127
+ ignore: finalExclude,
128
+ absolute: true
129
+ });
130
+ if (gitignoreFiles.length > 0) {
101
131
  try {
102
- const gitTxt = await readFile(gitignorePath, "utf-8");
103
132
  const ig = ignorePkg();
104
- ig.add(gitTxt);
133
+ for (const gitignorePath of gitignoreFiles) {
134
+ const gitTxt = await readFile(gitignorePath, "utf-8");
135
+ const gitignoreDir = dirname(gitignorePath);
136
+ const relativePrefix = relative(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
137
+ const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
138
+ if (relativePrefix === "." || relativePrefix === "") {
139
+ ig.add(patterns);
140
+ } else {
141
+ ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
142
+ }
143
+ }
105
144
  const filtered = files.filter((f) => {
106
145
  let rel = relative(rootDir || ".", f).replace(/\\/g, "/");
107
146
  if (rel === "") rel = f;
@@ -114,6 +153,54 @@ async function scanFiles(options) {
114
153
  }
115
154
  return files;
116
155
  }
156
+ async function scanEntries(options) {
157
+ const files = await scanFiles(options);
158
+ const { rootDir, include = ["**/*"], exclude, includeTests } = options;
159
+ const ignoreFilePath = join(rootDir || ".", ".aireadyignore");
160
+ let ignoreFromFile = [];
161
+ if (existsSync(ignoreFilePath)) {
162
+ try {
163
+ const txt = await readFile(ignoreFilePath, "utf-8");
164
+ ignoreFromFile = txt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#")).filter((l) => !l.startsWith("!"));
165
+ } catch (e) {
166
+ ignoreFromFile = [];
167
+ }
168
+ }
169
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
170
+ const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
171
+ const finalExclude = [.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])];
172
+ const dirs = await glob("**/", {
173
+ cwd: rootDir,
174
+ ignore: finalExclude,
175
+ absolute: true
176
+ });
177
+ const gitignoreFiles = await glob("**/.gitignore", {
178
+ cwd: rootDir,
179
+ ignore: finalExclude,
180
+ absolute: true
181
+ });
182
+ if (gitignoreFiles.length > 0) {
183
+ const ig = ignorePkg();
184
+ for (const gitignorePath of gitignoreFiles) {
185
+ const gitTxt = await readFile(gitignorePath, "utf-8");
186
+ const gitignoreDir = dirname(gitignorePath);
187
+ const relativePrefix = relative(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
188
+ const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
189
+ if (relativePrefix === "." || relativePrefix === "") {
190
+ ig.add(patterns);
191
+ } else {
192
+ ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
193
+ }
194
+ }
195
+ const filteredDirs = dirs.filter((d) => {
196
+ let rel = relative(rootDir || ".", d).replace(/\\/g, "/").replace(/\/$/, "");
197
+ if (rel === "") return true;
198
+ return !ig.ignores(rel);
199
+ });
200
+ return { files, dirs: filteredDirs };
201
+ }
202
+ return { files, dirs };
203
+ }
117
204
  async function readFileContent(filePath) {
118
205
  return readFile(filePath, "utf-8");
119
206
  }
@@ -306,7 +393,7 @@ function estimateTokens(text) {
306
393
 
307
394
  // src/utils/config.ts
308
395
  import { readFileSync, existsSync as existsSync2 } from "fs";
309
- import { join as join2, resolve, dirname } from "path";
396
+ import { join as join2, resolve, dirname as dirname2 } from "path";
310
397
  import { pathToFileURL } from "url";
311
398
  var CONFIG_FILES = [
312
399
  "aiready.json",
@@ -338,13 +425,18 @@ async function loadConfig(rootDir) {
338
425
  return config;
339
426
  } catch (error) {
340
427
  const errorMessage = error instanceof Error ? error.message : String(error);
341
- throw new Error(
428
+ const e = new Error(
342
429
  `Failed to load config from ${configPath}: ${errorMessage}`
343
430
  );
431
+ try {
432
+ e.cause = error instanceof Error ? error : void 0;
433
+ } catch {
434
+ }
435
+ throw e;
344
436
  }
345
437
  }
346
438
  }
347
- const parent = dirname(currentDir);
439
+ const parent = dirname2(currentDir);
348
440
  if (parent === currentDir) {
349
441
  break;
350
442
  }
@@ -377,7 +469,7 @@ function mergeConfigWithDefaults(userConfig, defaults) {
377
469
 
378
470
  // src/utils/cli-helpers.ts
379
471
  import { writeFileSync, mkdirSync, existsSync as existsSync3 } from "fs";
380
- import { join as join3, dirname as dirname2 } from "path";
472
+ import { join as join3, dirname as dirname3 } from "path";
381
473
  function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
382
474
  let outputPath;
383
475
  if (userPath) {
@@ -386,7 +478,7 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
386
478
  const aireadyDir = join3(workingDir, ".aiready");
387
479
  outputPath = join3(aireadyDir, defaultFilename);
388
480
  }
389
- const parentDir = dirname2(outputPath);
481
+ const parentDir = dirname3(outputPath);
390
482
  if (!existsSync3(parentDir)) {
391
483
  mkdirSync(parentDir, { recursive: true });
392
484
  }
@@ -404,7 +496,7 @@ async function loadMergedConfig(directory, defaults, cliOptions) {
404
496
  }
405
497
  function handleJSONOutput(data, outputFile, successMessage) {
406
498
  if (outputFile) {
407
- const dir = dirname2(outputFile);
499
+ const dir = dirname3(outputFile);
408
500
  if (!existsSync3(dir)) {
409
501
  mkdirSync(dir, { recursive: true });
410
502
  }
@@ -2258,7 +2350,7 @@ function calculateExtendedFutureProofScore(params) {
2258
2350
 
2259
2351
  // src/utils/history.ts
2260
2352
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2261
- import { join as join4, dirname as dirname3 } from "path";
2353
+ import { join as join4, dirname as dirname4 } from "path";
2262
2354
  function getHistoryPath(rootDir) {
2263
2355
  return join4(rootDir, ".aiready", "history.json");
2264
2356
  }
@@ -2277,7 +2369,7 @@ function loadScoreHistory(rootDir) {
2277
2369
  }
2278
2370
  function saveScoreEntry(rootDir, entry) {
2279
2371
  const historyPath = getHistoryPath(rootDir);
2280
- const historyDir = dirname3(historyPath);
2372
+ const historyDir = dirname4(historyPath);
2281
2373
  if (!existsSync4(historyDir)) {
2282
2374
  mkdirSync2(historyDir, { recursive: true });
2283
2375
  }
@@ -2329,6 +2421,39 @@ function clearHistory(rootDir) {
2329
2421
  writeFileSync2(historyPath, JSON.stringify([]));
2330
2422
  }
2331
2423
  }
2424
+
2425
+ // src/utils/history-git.ts
2426
+ import { execSync } from "child_process";
2427
+ function getFileCommitTimestamps(file) {
2428
+ const lineStamps = {};
2429
+ try {
2430
+ const output = execSync(`git blame -t "${file}"`, {
2431
+ encoding: "utf-8",
2432
+ stdio: ["ignore", "pipe", "ignore"]
2433
+ });
2434
+ const lines = output.split("\n");
2435
+ for (const line of lines) {
2436
+ if (!line) continue;
2437
+ const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
2438
+ if (match) {
2439
+ const ts = parseInt(match[1], 10);
2440
+ const ln = parseInt(match[2], 10);
2441
+ lineStamps[ln] = ts;
2442
+ }
2443
+ }
2444
+ } catch {
2445
+ }
2446
+ return lineStamps;
2447
+ }
2448
+ function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
2449
+ let latest = 0;
2450
+ for (let i = startLine; i <= endLine; i++) {
2451
+ if (lineStamps[i] && lineStamps[i] > latest) {
2452
+ latest = lineStamps[i];
2453
+ }
2454
+ }
2455
+ return latest;
2456
+ }
2332
2457
  export {
2333
2458
  CONTEXT_TIER_THRESHOLDS,
2334
2459
  DEFAULT_COST_CONFIG,
@@ -2343,6 +2468,7 @@ export {
2343
2468
  SIZE_ADJUSTED_THRESHOLDS,
2344
2469
  TOOL_NAME_MAP,
2345
2470
  TypeScriptParser,
2471
+ VAGUE_FILE_NAMES,
2346
2472
  calculateAgentGrounding,
2347
2473
  calculateAiSignalClarity,
2348
2474
  calculateChangeAmplification,
@@ -2377,8 +2503,10 @@ export {
2377
2503
  generateHTML,
2378
2504
  getDebtBreakdown,
2379
2505
  getElapsedTime,
2506
+ getFileCommitTimestamps,
2380
2507
  getFileExtension,
2381
2508
  getHistorySummary,
2509
+ getLineRangeLastModifiedCached,
2382
2510
  getModelPreset,
2383
2511
  getParser,
2384
2512
  getProjectSizeTier,
@@ -2404,5 +2532,6 @@ export {
2404
2532
  readFileContent,
2405
2533
  resolveOutputPath,
2406
2534
  saveScoreEntry,
2535
+ scanEntries,
2407
2536
  scanFiles
2408
2537
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.9.33",
3
+ "version": "0.9.35",
4
4
  "description": "Shared utilities for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",