@aiready/context-analyzer 0.21.8 → 0.21.10

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.d.ts CHANGED
@@ -184,43 +184,66 @@ declare function calculateCohesion(exports: ExportInfo[], filePath?: string, opt
184
184
  declare function analyzeContext(options: ContextAnalyzerOptions): Promise<ContextAnalysisResult[]>;
185
185
 
186
186
  /**
187
- * Auto-detect domain keywords from workspace folder structure
187
+ * Auto-detect domain keywords from workspace folder structure.
188
+ *
189
+ * @param files - Array of file contents to analyze for folder patterns.
190
+ * @returns Array of singularized domain keywords.
188
191
  */
189
192
  declare function extractDomainKeywordsFromPaths(files: FileContent[]): string[];
190
193
  /**
191
- * Build a dependency graph from file contents
194
+ * Build a dependency graph from file contents, resolving imports and extracting metadata.
195
+ *
196
+ * @param files - Array of file contents to process.
197
+ * @param options - Optional configuration for domain detection.
198
+ * @returns Complete dependency graph with nodes, edges, and semantic matrices.
192
199
  */
193
200
  declare function buildDependencyGraph(files: FileContent[], options?: {
194
201
  domainKeywords?: string[];
195
202
  }): DependencyGraph;
196
203
  /**
197
- * Calculate the maximum depth of import tree for a file
204
+ * Calculate the maximum depth of the import tree for a specific file.
205
+ *
206
+ * @param file - File path to start depth calculation from.
207
+ * @param graph - The dependency graph.
208
+ * @param visited - Optional set to track visited nodes during traversal.
209
+ * @param depth - Current recursion depth.
210
+ * @returns Maximum depth of the import chain.
198
211
  */
199
212
  declare function calculateImportDepth(file: string, graph: DependencyGraph, visited?: Set<string>, depth?: number): number;
200
213
  /**
201
- * Get all transitive dependencies for a file
214
+ * Retrieve all transitive dependencies for a specific file.
215
+ *
216
+ * @param file - File path to analyze.
217
+ * @param graph - The dependency graph.
218
+ * @param visited - Optional set to track visited nodes.
219
+ * @returns Array of all reachable file paths.
202
220
  */
203
221
  declare function getTransitiveDependencies(file: string, graph: DependencyGraph, visited?: Set<string>): string[];
204
222
  /**
205
- * Calculate total context budget (tokens needed to understand this file)
223
+ * Calculate total context budget (tokens needed to understand this file and its dependencies).
224
+ *
225
+ * @param file - File path to calculate budget for.
226
+ * @param graph - The dependency graph.
227
+ * @returns Total token count including recursive dependencies.
206
228
  */
207
229
  declare function calculateContextBudget(file: string, graph: DependencyGraph): number;
208
230
  /**
209
- * Detect circular dependencies
231
+ * Detect circular dependencies (cycles) within the dependency graph.
232
+ *
233
+ * @param graph - The dependency graph to scan.
234
+ * @returns Array of dependency cycles (each cycle is an array of file paths).
210
235
  */
211
236
  declare function detectCircularDependencies(graph: DependencyGraph): string[][];
212
237
 
213
- /**
214
- * Calculate cohesion score (how related are exports in a file)
215
- */
216
238
  /**
217
239
  * Calculates a cohesion score (0-1) for a module based on its exports,
218
240
  * shared imports, and internal structure. High cohesion indicates
219
241
  * a well-focused module that is easy for AI models to reason about.
220
242
  *
221
- * @param exports - Exported symbols and their metadata
222
- * @param imports - Imported symbols and their sources
223
- * @returns Cohesion score between 0 and 1
243
+ * @param exports - Exported symbols and their metadata.
244
+ * @param filePath - Optional file path for context.
245
+ * @param options - Optional configuration for weights and co-usage context.
246
+ * @returns Cohesion score between 0 and 1.
224
247
  */
225
248
  declare function calculateEnhancedCohesion(exports: ExportInfo[], filePath?: string, options?: {
226
249
  coUsageMatrix?: Map<string, Map<string, number>>;
@@ -232,10 +255,19 @@ declare function calculateEnhancedCohesion(exports: ExportInfo[], filePath?: str
232
255
  }): number;
233
256
  /**
234
257
  * Calculate structural cohesion for a file based on co-usage patterns.
258
+ *
259
+ * @param file - The file path to analyze.
260
+ * @param coUsageMatrix - Matrix of files frequently imported together.
261
+ * @returns Cohesion score between 0 and 1 based on co-usage distribution.
235
262
  */
236
263
  declare function calculateStructuralCohesionFromCoUsage(file: string, coUsageMatrix?: Map<string, Map<string, number>>): number;
237
264
  /**
238
265
  * Calculate fragmentation score (how scattered is a domain)
266
+ *
267
+ * @param files - List of files belonging to the domain.
268
+ * @param domain - The domain identifier.
269
+ * @param options - Optional calculation parameters (log scale, thresholds).
270
+ * @returns Fragmentation score from 0 (perfect) to 1 (highly scattered).
239
271
  */
240
272
  declare function calculateFragmentation(files: string[], domain: string, options?: {
241
273
  useLogScale?: boolean;
@@ -244,11 +276,17 @@ declare function calculateFragmentation(files: string[], domain: string, options
244
276
  dependencyCount?: number;
245
277
  }): number;
246
278
  /**
247
- * Calculate path entropy for a set of files
279
+ * Calculate path entropy for a set of files to measure directory distribution.
280
+ *
281
+ * @param files - Array of file paths.
282
+ * @returns Entropy score representing the spread across directories.
248
283
  */
249
284
  declare function calculatePathEntropy(files: string[]): number;
250
285
  /**
251
- * Calculate directory-distance metric based on common ancestor depth
286
+ * Calculate directory-distance metric based on common ancestor depth.
287
+ *
288
+ * @param files - Array of file paths to compare.
289
+ * @returns Normalized distance metric (0-1).
252
290
  */
253
291
  declare function calculateDirectoryDistance(files: string[]): number;
254
292
 
@@ -308,10 +346,19 @@ declare function detectModuleClusters(graph: DependencyGraph, options?: {
308
346
 
309
347
  /**
310
348
  * Get classification-specific recommendations
349
+ *
350
+ * @param classification - The identified type of file (e.g., 'barrel-export', 'utility-module').
351
+ * @param file - File path or identifier.
352
+ * @param issues - Initial list of issues to supplement.
353
+ * @returns Array of tailored recommendations.
311
354
  */
312
355
  declare function getClassificationRecommendations(classification: FileClassification, file: string, issues: string[]): string[];
313
356
  /**
314
- * Generate general context recommendations
357
+ * Generate general context recommendations based on cross-tool metrics and thresholds.
358
+ *
359
+ * @param metrics - Object containing context budget, depth, circular dependencies, cohesion, and fragmentation.
360
+ * @param thresholds - Configurable limits for each metric.
361
+ * @returns Object with recommendations array, issues array, and overall severity level.
315
362
  */
316
363
  declare function getGeneralRecommendations(metrics: {
317
364
  contextBudget: number;
@@ -331,9 +378,23 @@ declare function getGeneralRecommendations(metrics: {
331
378
  };
332
379
 
333
380
  /**
334
- * Calculate AI Readiness Score for context efficiency (0-100)
381
+ * Calculate AI Readiness Score for context efficiency (0-100).
382
+ *
383
+ * Evaluates how efficiently an AI model can process the project's code context
384
+ * based on token budgets, import depth, and file fragmentation.
385
+ *
386
+ * @param summary - Consolidated context summary from the scan.
387
+ * @param costConfig - Optional configuration for business value calculations.
388
+ * @returns Standardized scoring output for the context analyzer tool.
389
+ * @lastUpdated 2026-03-18
335
390
  */
336
391
  declare function calculateContextScore(summary: ContextSummary, costConfig?: Partial<CostConfig>): ToolScoringOutput;
392
+ /**
393
+ * Maps a numerical score to a human-readable rating slug.
394
+ *
395
+ * @param score - The 0-100 readiness score.
396
+ * @returns A formatted rating string (e.g., "excellent", "at risk").
397
+ */
337
398
  declare function mapScoreToRating(score: number): string;
338
399
 
339
400
  /**
@@ -347,46 +408,96 @@ declare function getSmartDefaults(directory: string, userOptions: Partial<Contex
347
408
 
348
409
  /**
349
410
  * Generate summary of context analysis results
411
+ *
412
+ * @param results - Array of individual file analysis results.
413
+ * @param options - Optional scan configuration for context extraction.
414
+ * @returns A consolidated summary of the entire context scan.
350
415
  */
351
416
  declare function generateSummary(results: ContextAnalysisResult[], options?: any): ContextSummary;
352
417
 
353
418
  /**
354
- * Build co-usage matrix: track which files are imported together
355
- * @param graph - The dependency graph to analyze
356
- * @returns Map of file to co-usage counts
419
+ * Build co-usage matrix: track which files are imported together frequently.
420
+ *
421
+ * @param graph - The dependency graph to analyze.
422
+ * @returns Map of file path to nested map of related files and their co-occurrence counts.
357
423
  */
358
424
  declare function buildCoUsageMatrix(graph: DependencyGraph): Map<string, Map<string, number>>;
359
425
  /**
360
- * Extract type dependencies from AST exports
361
- * @param graph - The dependency graph to analyze
362
- * @returns Map of type references to files that use them
426
+ * Extract type dependencies from AST exports to build a type-based relationship graph.
427
+ *
428
+ * @param graph - The dependency graph to analyze.
429
+ * @returns Map of type reference names to sets of files that consume or export them.
363
430
  */
364
431
  declare function buildTypeGraph(graph: DependencyGraph): Map<string, Set<string>>;
365
432
  /**
366
- * Find semantic clusters using co-usage patterns
367
- * @param coUsageMatrix - The co-usage matrix from buildCoUsageMatrix
368
- * @param minCoUsage - Minimum co-usage count to consider (default: 3)
369
- * @returns Map of cluster representative files to their cluster members
433
+ * Find semantic clusters using frequently occurring co-usage patterns.
434
+ *
435
+ * @param coUsageMatrix - The co-usage matrix from buildCoUsageMatrix.
436
+ * @param minCoUsage - Minimum co-usage count to consider a strong relationship (default: 3).
437
+ * @returns Map of cluster representative files to their associated cluster members.
370
438
  */
371
439
  declare function findSemanticClusters(coUsageMatrix: Map<string, Map<string, number>>, minCoUsage?: number): Map<string, string[]>;
372
440
  /**
373
- * Infer domain from semantic analysis (co-usage + types)
441
+ * Infer domain from semantic analysis (co-usage + types) to identify logical modules.
442
+ *
443
+ * @param file - The file path to infer domain for.
444
+ * @param exportName - The specific export identifier.
445
+ * @param graph - The full dependency graph.
446
+ * @param coUsageMatrix - Matrix of files frequently imported together.
447
+ * @param typeGraph - Map of type references to files.
448
+ * @param exportTypeRefs - Optional list of types referenced by the export.
449
+ * @returns Array of potential domain assignments with confidence scores.
374
450
  */
375
451
  declare function inferDomainFromSemantics(file: string, exportName: string, graph: DependencyGraph, coUsageMatrix: Map<string, Map<string, number>>, typeGraph: Map<string, Set<string>>, exportTypeRefs?: string[]): DomainAssignment[];
452
+ /**
453
+ * Calculate confidence score for a domain assignment based on signals.
454
+ *
455
+ * @param signals - The set of semantic signals detected for a domain.
456
+ * @returns Numerical confidence score (0-1).
457
+ */
376
458
  declare function calculateDomainConfidence(signals: DomainSignals): number;
377
459
  /**
378
460
  * Regex-based export extraction (legacy/fallback)
461
+ *
462
+ * @param content - Source code content.
463
+ * @param filePath - Optional file path for domain context.
464
+ * @param domainOptions - Optional overrides for domain keywords.
465
+ * @param fileImports - Optional list of actual imports for semantic context.
466
+ * @returns Array of extracted export information.
379
467
  */
380
468
  declare function extractExports(content: string, filePath?: string, domainOptions?: {
381
469
  domainKeywords?: string[];
382
470
  }, fileImports?: string[]): ExportInfo[];
383
471
  /**
384
472
  * Infer domain from name, path, or imports
473
+ *
474
+ * @param name - The identifier name to analyze.
475
+ * @param filePath - Optional file path for structure context.
476
+ * @param domainOptions - Optional overrides for domain keywords.
477
+ * @param fileImports - Optional list of imports for domain context.
478
+ * @returns The inferred domain name (string).
385
479
  */
386
480
  declare function inferDomain(name: string, filePath?: string, domainOptions?: {
387
481
  domainKeywords?: string[];
388
482
  }, fileImports?: string[]): string;
483
+ /**
484
+ * Retrieve co-usage data for a specific file.
485
+ *
486
+ * @param file - The file path to look up.
487
+ * @param coUsageMatrix - The global co-usage matrix.
488
+ * @returns Formatted co-usage data object.
489
+ */
389
490
  declare function getCoUsageData(file: string, coUsageMatrix: Map<string, Map<string, number>>): CoUsageData;
491
+ /**
492
+ * Identify candidates for module consolidation based on high co-usage or shared types.
493
+ *
494
+ * @param graph - The dependency graph.
495
+ * @param coUsageMatrix - Matrix of frequently paired files.
496
+ * @param typeGraph - Map of shared type references.
497
+ * @param minCoUsage - Minimum co-usage count threshold.
498
+ * @param minSharedTypes - Minimum shared types threshold.
499
+ * @returns Array of consolidation candidates sorted by strength.
500
+ */
390
501
  declare function findConsolidationCandidates(graph: DependencyGraph, coUsageMatrix: Map<string, Map<string, number>>, typeGraph: Map<string, Set<string>>, minCoUsage?: number, minSharedTypes?: number): any[];
391
502
 
392
503
  /**
package/dist/index.js CHANGED
@@ -1718,6 +1718,25 @@ function generateSummary(results, options = {}) {
1718
1718
 
1719
1719
  // src/scoring.ts
1720
1720
  var import_core8 = require("@aiready/core");
1721
+ var BUDGET_EXCELLENT_THRESHOLD = 8e3;
1722
+ var BUDGET_PENALTY_RATE = 200;
1723
+ var DEPTH_EXCELLENT_THRESHOLD = 8;
1724
+ var DEPTH_PENALTY_WEIGHT = 5;
1725
+ var FRAGMENTATION_EXCELLENT_THRESHOLD = 0.5;
1726
+ var FRAGMENTATION_PENALTY_WEIGHT = 100;
1727
+ var MAX_CRITICAL_PENALTY = 20;
1728
+ var CRITICAL_ISSUE_WEIGHT = 3;
1729
+ var MAX_MAJOR_PENALTY = 15;
1730
+ var MAJOR_ISSUE_WEIGHT = 1;
1731
+ var EXTREME_FILE_THRESHOLD = 15e3;
1732
+ var MAX_EXTREME_PENALTY = 20;
1733
+ var EXTREME_PENALTY_DIVISOR = 500;
1734
+ var FRAGMENTATION_BONUS_THRESHOLD = 0.2;
1735
+ var ORGANIZATION_BONUS = 5;
1736
+ var MAX_TOTAL_PENALTY = 30;
1737
+ var WEIGHT_BUDGET = 0.35;
1738
+ var WEIGHT_DEPTH = 0.25;
1739
+ var WEIGHT_FRAGMENTATION = 0.25;
1721
1740
  function calculateContextScore(summary, costConfig) {
1722
1741
  const {
1723
1742
  avgContextBudget,
@@ -1729,33 +1748,53 @@ function calculateContextScore(summary, costConfig) {
1729
1748
  majorIssues,
1730
1749
  totalFiles
1731
1750
  } = summary;
1732
- const budgetScore = avgContextBudget < 8e3 ? 100 : Math.max(0, 100 - (avgContextBudget - 8e3) / 200);
1733
- const depthScore = avgImportDepth < 8 ? 100 : Math.max(0, 100 - (avgImportDepth - 8) * 5);
1734
- const fragmentationScore = avgFragmentation < 0.5 ? 100 : Math.max(0, 100 - (avgFragmentation - 0.5) * 100);
1735
- const criticalPenalty = Math.min(20, criticalIssues * 3);
1736
- const majorPenalty = Math.min(15, majorIssues * 1);
1737
- const maxBudgetPenalty = maxContextBudget > 15e3 ? Math.min(20, (maxContextBudget - 15e3) / 500) : 0;
1751
+ const budgetScore = avgContextBudget < BUDGET_EXCELLENT_THRESHOLD ? 100 : Math.max(
1752
+ 0,
1753
+ 100 - (avgContextBudget - BUDGET_EXCELLENT_THRESHOLD) / BUDGET_PENALTY_RATE
1754
+ );
1755
+ const depthScore = avgImportDepth < DEPTH_EXCELLENT_THRESHOLD ? 100 : Math.max(
1756
+ 0,
1757
+ 100 - (avgImportDepth - DEPTH_EXCELLENT_THRESHOLD) * DEPTH_PENALTY_WEIGHT
1758
+ );
1759
+ const fragmentationScore = avgFragmentation < FRAGMENTATION_EXCELLENT_THRESHOLD ? 100 : Math.max(
1760
+ 0,
1761
+ 100 - (avgFragmentation - FRAGMENTATION_EXCELLENT_THRESHOLD) * FRAGMENTATION_PENALTY_WEIGHT
1762
+ );
1763
+ const criticalPenalty = Math.min(
1764
+ MAX_CRITICAL_PENALTY,
1765
+ criticalIssues * CRITICAL_ISSUE_WEIGHT
1766
+ );
1767
+ const majorPenalty = Math.min(
1768
+ MAX_MAJOR_PENALTY,
1769
+ majorIssues * MAJOR_ISSUE_WEIGHT
1770
+ );
1771
+ const maxBudgetPenalty = maxContextBudget > EXTREME_FILE_THRESHOLD ? Math.min(
1772
+ MAX_EXTREME_PENALTY,
1773
+ (maxContextBudget - EXTREME_FILE_THRESHOLD) / EXTREME_PENALTY_DIVISOR
1774
+ ) : 0;
1738
1775
  let bonus = 0;
1739
- if (criticalIssues === 0 && majorIssues === 0 && avgFragmentation < 0.2) {
1740
- bonus = 5;
1776
+ if (criticalIssues === 0 && majorIssues === 0 && avgFragmentation < FRAGMENTATION_BONUS_THRESHOLD) {
1777
+ bonus = ORGANIZATION_BONUS;
1741
1778
  }
1742
- const rawScore = budgetScore * 0.35 + depthScore * 0.25 + fragmentationScore * 0.25 + bonus;
1743
- const finalScore = rawScore - Math.min(30, criticalPenalty + majorPenalty) - maxBudgetPenalty;
1779
+ const rawScore = budgetScore * WEIGHT_BUDGET + depthScore * WEIGHT_DEPTH + fragmentationScore * WEIGHT_FRAGMENTATION + bonus;
1780
+ const finalScore = rawScore - Math.min(MAX_TOTAL_PENALTY, criticalPenalty + majorPenalty) - maxBudgetPenalty;
1744
1781
  const score = Math.max(0, Math.min(100, Math.round(finalScore)));
1745
1782
  const factors = [
1746
1783
  {
1747
1784
  name: "Context Budget",
1748
- impact: Math.round(budgetScore * 0.35 - 35),
1749
- description: `Avg ${Math.round(avgContextBudget)} tokens per file ${avgContextBudget < 8e3 ? "(excellent)" : avgContextBudget < 12e3 ? "(acceptable)" : "(high)"}`
1785
+ impact: Math.round(budgetScore * WEIGHT_BUDGET - WEIGHT_BUDGET * 100),
1786
+ description: `Avg ${Math.round(avgContextBudget)} tokens per file ${avgContextBudget < BUDGET_EXCELLENT_THRESHOLD ? "(excellent)" : avgContextBudget < 12e3 ? "(acceptable)" : "(high)"}`
1750
1787
  },
1751
1788
  {
1752
1789
  name: "Import Depth",
1753
- impact: Math.round(depthScore * 0.25 - 25),
1754
- description: `Avg ${avgImportDepth.toFixed(1)} levels ${avgImportDepth < 8 ? "(excellent)" : avgImportDepth < 12 ? "(acceptable)" : "(deep)"}`
1790
+ impact: Math.round(depthScore * WEIGHT_DEPTH - WEIGHT_DEPTH * 100),
1791
+ description: `Avg ${avgImportDepth.toFixed(1)} levels ${avgImportDepth < DEPTH_EXCELLENT_THRESHOLD ? "(excellent)" : avgImportDepth < 12 ? "(acceptable)" : "(deep)"}`
1755
1792
  },
1756
1793
  {
1757
1794
  name: "Fragmentation",
1758
- impact: Math.round(fragmentationScore * 0.25 - 25),
1795
+ impact: Math.round(
1796
+ fragmentationScore * WEIGHT_FRAGMENTATION - WEIGHT_FRAGMENTATION * 100
1797
+ ),
1759
1798
  description: `${(avgFragmentation * 100).toFixed(0)}% fragmentation ${avgFragmentation < 0.3 ? "(well-organized)" : avgFragmentation < 0.5 ? "(moderate)" : "(high)"}`
1760
1799
  }
1761
1800
  ];
package/dist/index.mjs CHANGED
@@ -53,6 +53,25 @@ import {
53
53
  ToolName,
54
54
  getRatingSlug
55
55
  } from "@aiready/core";
56
+ var BUDGET_EXCELLENT_THRESHOLD = 8e3;
57
+ var BUDGET_PENALTY_RATE = 200;
58
+ var DEPTH_EXCELLENT_THRESHOLD = 8;
59
+ var DEPTH_PENALTY_WEIGHT = 5;
60
+ var FRAGMENTATION_EXCELLENT_THRESHOLD = 0.5;
61
+ var FRAGMENTATION_PENALTY_WEIGHT = 100;
62
+ var MAX_CRITICAL_PENALTY = 20;
63
+ var CRITICAL_ISSUE_WEIGHT = 3;
64
+ var MAX_MAJOR_PENALTY = 15;
65
+ var MAJOR_ISSUE_WEIGHT = 1;
66
+ var EXTREME_FILE_THRESHOLD = 15e3;
67
+ var MAX_EXTREME_PENALTY = 20;
68
+ var EXTREME_PENALTY_DIVISOR = 500;
69
+ var FRAGMENTATION_BONUS_THRESHOLD = 0.2;
70
+ var ORGANIZATION_BONUS = 5;
71
+ var MAX_TOTAL_PENALTY = 30;
72
+ var WEIGHT_BUDGET = 0.35;
73
+ var WEIGHT_DEPTH = 0.25;
74
+ var WEIGHT_FRAGMENTATION = 0.25;
56
75
  function calculateContextScore(summary, costConfig) {
57
76
  const {
58
77
  avgContextBudget,
@@ -64,33 +83,53 @@ function calculateContextScore(summary, costConfig) {
64
83
  majorIssues,
65
84
  totalFiles
66
85
  } = summary;
67
- const budgetScore = avgContextBudget < 8e3 ? 100 : Math.max(0, 100 - (avgContextBudget - 8e3) / 200);
68
- const depthScore = avgImportDepth < 8 ? 100 : Math.max(0, 100 - (avgImportDepth - 8) * 5);
69
- const fragmentationScore = avgFragmentation < 0.5 ? 100 : Math.max(0, 100 - (avgFragmentation - 0.5) * 100);
70
- const criticalPenalty = Math.min(20, criticalIssues * 3);
71
- const majorPenalty = Math.min(15, majorIssues * 1);
72
- const maxBudgetPenalty = maxContextBudget > 15e3 ? Math.min(20, (maxContextBudget - 15e3) / 500) : 0;
86
+ const budgetScore = avgContextBudget < BUDGET_EXCELLENT_THRESHOLD ? 100 : Math.max(
87
+ 0,
88
+ 100 - (avgContextBudget - BUDGET_EXCELLENT_THRESHOLD) / BUDGET_PENALTY_RATE
89
+ );
90
+ const depthScore = avgImportDepth < DEPTH_EXCELLENT_THRESHOLD ? 100 : Math.max(
91
+ 0,
92
+ 100 - (avgImportDepth - DEPTH_EXCELLENT_THRESHOLD) * DEPTH_PENALTY_WEIGHT
93
+ );
94
+ const fragmentationScore = avgFragmentation < FRAGMENTATION_EXCELLENT_THRESHOLD ? 100 : Math.max(
95
+ 0,
96
+ 100 - (avgFragmentation - FRAGMENTATION_EXCELLENT_THRESHOLD) * FRAGMENTATION_PENALTY_WEIGHT
97
+ );
98
+ const criticalPenalty = Math.min(
99
+ MAX_CRITICAL_PENALTY,
100
+ criticalIssues * CRITICAL_ISSUE_WEIGHT
101
+ );
102
+ const majorPenalty = Math.min(
103
+ MAX_MAJOR_PENALTY,
104
+ majorIssues * MAJOR_ISSUE_WEIGHT
105
+ );
106
+ const maxBudgetPenalty = maxContextBudget > EXTREME_FILE_THRESHOLD ? Math.min(
107
+ MAX_EXTREME_PENALTY,
108
+ (maxContextBudget - EXTREME_FILE_THRESHOLD) / EXTREME_PENALTY_DIVISOR
109
+ ) : 0;
73
110
  let bonus = 0;
74
- if (criticalIssues === 0 && majorIssues === 0 && avgFragmentation < 0.2) {
75
- bonus = 5;
111
+ if (criticalIssues === 0 && majorIssues === 0 && avgFragmentation < FRAGMENTATION_BONUS_THRESHOLD) {
112
+ bonus = ORGANIZATION_BONUS;
76
113
  }
77
- const rawScore = budgetScore * 0.35 + depthScore * 0.25 + fragmentationScore * 0.25 + bonus;
78
- const finalScore = rawScore - Math.min(30, criticalPenalty + majorPenalty) - maxBudgetPenalty;
114
+ const rawScore = budgetScore * WEIGHT_BUDGET + depthScore * WEIGHT_DEPTH + fragmentationScore * WEIGHT_FRAGMENTATION + bonus;
115
+ const finalScore = rawScore - Math.min(MAX_TOTAL_PENALTY, criticalPenalty + majorPenalty) - maxBudgetPenalty;
79
116
  const score = Math.max(0, Math.min(100, Math.round(finalScore)));
80
117
  const factors = [
81
118
  {
82
119
  name: "Context Budget",
83
- impact: Math.round(budgetScore * 0.35 - 35),
84
- description: `Avg ${Math.round(avgContextBudget)} tokens per file ${avgContextBudget < 8e3 ? "(excellent)" : avgContextBudget < 12e3 ? "(acceptable)" : "(high)"}`
120
+ impact: Math.round(budgetScore * WEIGHT_BUDGET - WEIGHT_BUDGET * 100),
121
+ description: `Avg ${Math.round(avgContextBudget)} tokens per file ${avgContextBudget < BUDGET_EXCELLENT_THRESHOLD ? "(excellent)" : avgContextBudget < 12e3 ? "(acceptable)" : "(high)"}`
85
122
  },
86
123
  {
87
124
  name: "Import Depth",
88
- impact: Math.round(depthScore * 0.25 - 25),
89
- description: `Avg ${avgImportDepth.toFixed(1)} levels ${avgImportDepth < 8 ? "(excellent)" : avgImportDepth < 12 ? "(acceptable)" : "(deep)"}`
125
+ impact: Math.round(depthScore * WEIGHT_DEPTH - WEIGHT_DEPTH * 100),
126
+ description: `Avg ${avgImportDepth.toFixed(1)} levels ${avgImportDepth < DEPTH_EXCELLENT_THRESHOLD ? "(excellent)" : avgImportDepth < 12 ? "(acceptable)" : "(deep)"}`
90
127
  },
91
128
  {
92
129
  name: "Fragmentation",
93
- impact: Math.round(fragmentationScore * 0.25 - 25),
130
+ impact: Math.round(
131
+ fragmentationScore * WEIGHT_FRAGMENTATION - WEIGHT_FRAGMENTATION * 100
132
+ ),
94
133
  description: `${(avgFragmentation * 100).toFixed(0)}% fragmentation ${avgFragmentation < 0.3 ? "(well-organized)" : avgFragmentation < 0.5 ? "(moderate)" : "(high)"}`
95
134
  }
96
135
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/context-analyzer",
3
- "version": "0.21.8",
3
+ "version": "0.21.10",
4
4
  "description": "AI context window cost analysis - detect fragmented code, deep import chains, and expensive context budgets",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -49,7 +49,7 @@
49
49
  "commander": "^14.0.0",
50
50
  "chalk": "^5.3.0",
51
51
  "prompts": "^2.4.2",
52
- "@aiready/core": "0.23.4"
52
+ "@aiready/core": "0.23.7"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^24.0.0",
package/src/ast-utils.ts CHANGED
@@ -3,7 +3,14 @@ import type { ExportInfo } from './types';
3
3
  import { inferDomain, extractExports } from './semantic-analysis';
4
4
 
5
5
  /**
6
- * Extract exports using high-fidelity AST parsing across 5+ languages
6
+ * Extract exports using high-fidelity AST parsing across 5+ languages.
7
+ *
8
+ * @param content - File contents to parse.
9
+ * @param filePath - Path to the file for language detection and context.
10
+ * @param domainOptions - Optional configuration for domain detection.
11
+ * @param fileImports - Optional array of strings for resolving imports.
12
+ * @returns Array of high-fidelity export metadata.
13
+ * @lastUpdated 2026-03-18
7
14
  */
8
15
  export function extractExportsWithAST(
9
16
  content: string,
@@ -40,7 +47,10 @@ export function extractExportsWithAST(
40
47
  }
41
48
 
42
49
  /**
43
- * Check if a file is a test, mock, or fixture file
50
+ * Check if a file is a test, mock, or fixture file.
51
+ *
52
+ * @param filePath - The path to the file to check.
53
+ * @returns True if the file matches test/mock patterns.
44
54
  */
45
55
  export function isTestFile(filePath: string): boolean {
46
56
  const lower = filePath.toLowerCase();
@@ -0,0 +1,61 @@
1
+ import { Command } from 'commander';
2
+ import { contextActionHandler } from './cli-action';
3
+
4
+ /**
5
+ * Define the context analysis command structure.
6
+ * Separating this from the execution logic helps reduce context overhead.
7
+ *
8
+ * @param program - Commander program instance to attach the command to.
9
+ */
10
+ export function defineContextCommand(program: Command): void {
11
+ program
12
+ .name('aiready-context')
13
+ .description('Analyze AI context window cost and code structure')
14
+ .version('0.1.0')
15
+ .addHelpText(
16
+ 'after',
17
+ '\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings'
18
+ )
19
+ .argument('<directory>', 'Directory to analyze')
20
+ .option('--max-depth <number>', 'Maximum acceptable import depth')
21
+ .option(
22
+ '--max-context <number>',
23
+ 'Maximum acceptable context budget (tokens)'
24
+ )
25
+ .option(
26
+ '--min-cohesion <number>',
27
+ 'Minimum acceptable cohesion score (0-1)'
28
+ )
29
+ .option(
30
+ '--max-fragmentation <number>',
31
+ 'Maximum acceptable fragmentation (0-1)'
32
+ )
33
+ .option(
34
+ '--focus <type>',
35
+ 'Analysis focus: fragmentation, cohesion, depth, all'
36
+ )
37
+ .option('--include-node-modules', 'Include node_modules in analysis')
38
+ .option(
39
+ '--include <patterns>',
40
+ 'File patterns to include (comma-separated)'
41
+ )
42
+ .option(
43
+ '--exclude <patterns>',
44
+ 'File patterns to exclude (comma-separated)'
45
+ )
46
+ .option(
47
+ '--max-results <number>',
48
+ 'Maximum number of results to show in console output'
49
+ )
50
+ .option(
51
+ '-o, --output <format>',
52
+ 'Output format: console, json, html',
53
+ 'console'
54
+ )
55
+ .option('--output-file <path>', 'Output file path (for json/html)')
56
+ .option(
57
+ '--interactive',
58
+ 'Run interactive setup to suggest excludes and focus areas'
59
+ )
60
+ .action(contextActionHandler);
61
+ }
package/src/cli.ts CHANGED
@@ -1,50 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import { contextActionHandler } from './cli-action';
4
+ import { defineContextCommand } from './cli-definition';
5
5
 
6
6
  const program = new Command();
7
-
8
- program
9
- .name('aiready-context')
10
- .description('Analyze AI context window cost and code structure')
11
- .version('0.1.0')
12
- .addHelpText(
13
- 'after',
14
- '\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings'
15
- )
16
- .argument('<directory>', 'Directory to analyze')
17
- .option('--max-depth <number>', 'Maximum acceptable import depth')
18
- .option(
19
- '--max-context <number>',
20
- 'Maximum acceptable context budget (tokens)'
21
- )
22
- .option('--min-cohesion <number>', 'Minimum acceptable cohesion score (0-1)')
23
- .option(
24
- '--max-fragmentation <number>',
25
- 'Maximum acceptable fragmentation (0-1)'
26
- )
27
- .option(
28
- '--focus <type>',
29
- 'Analysis focus: fragmentation, cohesion, depth, all'
30
- )
31
- .option('--include-node-modules', 'Include node_modules in analysis')
32
- .option('--include <patterns>', 'File patterns to include (comma-separated)')
33
- .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
34
- .option(
35
- '--max-results <number>',
36
- 'Maximum number of results to show in console output'
37
- )
38
- .option(
39
- '-o, --output <format>',
40
- 'Output format: console, json, html',
41
- 'console'
42
- )
43
- .option('--output-file <path>', 'Output file path (for json/html)')
44
- .option(
45
- '--interactive',
46
- 'Run interactive setup to suggest excludes and focus areas'
47
- )
48
- .action(contextActionHandler);
49
-
7
+ defineContextCommand(program);
50
8
  program.parse(process.argv);