@aiready/context-analyzer 0.22.14 → 0.22.16

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.
Files changed (51) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/.turbo/turbo-format-check.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/.turbo/turbo-test.log +347 -25
  5. package/.turbo/turbo-type-check.log +1 -1
  6. package/dist/chunk-2LEHY2GV.mjs +1287 -0
  7. package/dist/chunk-P2ZQGQAO.mjs +1282 -0
  8. package/dist/chunk-QGI23DBA.mjs +1282 -0
  9. package/dist/chunk-QTB4KYCX.mjs +1260 -0
  10. package/dist/chunk-RQ5BQLT6.mjs +102 -0
  11. package/dist/chunk-VYFHSGV6.mjs +1283 -0
  12. package/dist/chunk-WLXLBWDU.mjs +96 -0
  13. package/dist/chunk-XDYPMFCH.mjs +1250 -0
  14. package/dist/cli-action-332WE54N.mjs +95 -0
  15. package/dist/cli-action-7QXG7LHS.mjs +95 -0
  16. package/dist/cli-action-BIX6TYXF.mjs +95 -0
  17. package/dist/cli-action-BUGVCH44.mjs +95 -0
  18. package/dist/cli-action-JKG3R6RV.mjs +93 -0
  19. package/dist/cli-action-RO24U52W.mjs +95 -0
  20. package/dist/cli-action-WAZ5KM6X.mjs +95 -0
  21. package/dist/cli-action-XDKINE2R.mjs +95 -0
  22. package/dist/cli-action-Y6VATXMV.mjs +95 -0
  23. package/dist/cli.js +79 -31
  24. package/dist/cli.mjs +1 -1
  25. package/dist/console-report-CVGRMWEU.mjs +74 -0
  26. package/dist/html-report-BYGKWC3K.mjs +73 -0
  27. package/dist/index.d.mts +4 -2
  28. package/dist/index.d.ts +4 -2
  29. package/dist/index.js +71 -25
  30. package/dist/index.mjs +3 -3
  31. package/dist/orchestrator-2KQNMO2L.mjs +10 -0
  32. package/dist/orchestrator-66ZVNOLR.mjs +10 -0
  33. package/dist/orchestrator-KM2OJPZD.mjs +10 -0
  34. package/dist/orchestrator-MKDZPRBA.mjs +10 -0
  35. package/dist/orchestrator-QSHWWBWS.mjs +10 -0
  36. package/dist/orchestrator-WFQPMNSD.mjs +10 -0
  37. package/dist/python-context-H2OLC5JN.mjs +162 -0
  38. package/dist/python-context-OBP7JD5P.mjs +162 -0
  39. package/package.json +8 -6
  40. package/src/__tests__/analyzer.test.ts +4 -3
  41. package/src/__tests__/issue-analyzer.test.ts +4 -2
  42. package/src/classify/file-classifiers.ts +14 -13
  43. package/src/cli-action.ts +6 -3
  44. package/src/graph-builder.ts +43 -8
  45. package/src/issue-analyzer.ts +19 -7
  46. package/src/orchestrator.ts +6 -4
  47. package/src/report/console-report.ts +2 -2
  48. package/src/report/html-report.ts +2 -2
  49. package/src/semantic/domain-inference.ts +1 -1
  50. package/src/types.ts +2 -0
  51. package/src/utils/dependency-graph-utils.ts +22 -13
@@ -0,0 +1,95 @@
1
+ import {
2
+ analyzeContext
3
+ } from "./chunk-VYFHSGV6.mjs";
4
+ import "./chunk-RQ5BQLT6.mjs";
5
+ import {
6
+ generateSummary
7
+ } from "./chunk-HD4Y3GYL.mjs";
8
+ import "./chunk-JSM7Q5CY.mjs";
9
+
10
+ // src/cli-action.ts
11
+ import {
12
+ loadMergedConfig,
13
+ handleJSONOutput,
14
+ handleCLIError,
15
+ getElapsedTime,
16
+ resolveOutputPath
17
+ } from "@aiready/core";
18
+ import chalk from "chalk";
19
+ import { writeFileSync } from "fs";
20
+ async function contextActionHandler(directory, options) {
21
+ console.log(chalk.blue("\u{1F50D} Analyzing context window costs...\n"));
22
+ const startTime = Date.now();
23
+ try {
24
+ const defaults = {
25
+ maxDepth: 10,
26
+ maxContextBudget: 1e4,
27
+ minCohesion: 0.6,
28
+ maxFragmentation: 0.5,
29
+ focus: "all",
30
+ includeNodeModules: false,
31
+ include: void 0,
32
+ exclude: void 0,
33
+ maxResults: 10
34
+ };
35
+ let finalOptions = await loadMergedConfig(directory, defaults, {
36
+ maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
37
+ maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
38
+ minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : void 0,
39
+ maxFragmentation: options.maxFragmentation ? parseFloat(options.maxFragmentation) : void 0,
40
+ focus: options.focus || void 0,
41
+ includeNodeModules: options.includeNodeModules,
42
+ include: options.include?.split(","),
43
+ exclude: options.exclude?.split(","),
44
+ maxResults: options.maxResults ? parseInt(options.maxResults) : void 0
45
+ });
46
+ if (options.interactive) {
47
+ const { runInteractiveSetup } = await import("./interactive-setup-JGFBFI3M.mjs");
48
+ finalOptions = await runInteractiveSetup(directory, finalOptions);
49
+ }
50
+ const results = await analyzeContext(
51
+ finalOptions
52
+ );
53
+ const summary = generateSummary(results, finalOptions);
54
+ const duration = getElapsedTime(startTime);
55
+ if (options.output === "json") {
56
+ handleJSONOutput(
57
+ {
58
+ summary: {
59
+ ...summary,
60
+ executionTime: duration,
61
+ config: {
62
+ scan: { tools: ["context"] },
63
+ tools: { context: finalOptions }
64
+ },
65
+ toolConfigs: { context: finalOptions }
66
+ },
67
+ context: { results }
68
+ },
69
+ options.outputFile
70
+ );
71
+ } else if (options.output === "html") {
72
+ const { generateHTMLReport } = await import("./html-report-BYGKWC3K.mjs");
73
+ const html = generateHTMLReport(summary, results);
74
+ const outputPath = resolveOutputPath(
75
+ directory,
76
+ options.outputFile,
77
+ "context-report.html"
78
+ );
79
+ writeFileSync(outputPath, html, "utf-8");
80
+ console.log(chalk.green(`
81
+ \u2705 HTML report saved to: ${outputPath}`));
82
+ } else {
83
+ const { displayConsoleReport } = await import("./console-report-CVGRMWEU.mjs");
84
+ displayConsoleReport(summary, results, finalOptions.maxResults);
85
+ console.log(chalk.dim(`
86
+ \u2728 Analysis completed in ${duration}ms
87
+ `));
88
+ }
89
+ } catch (error) {
90
+ handleCLIError(error, "context-analyzer");
91
+ }
92
+ }
93
+ export {
94
+ contextActionHandler
95
+ };
@@ -0,0 +1,95 @@
1
+ import {
2
+ analyzeContext
3
+ } from "./chunk-XDYPMFCH.mjs";
4
+ import "./chunk-64U3PNO3.mjs";
5
+ import {
6
+ generateSummary
7
+ } from "./chunk-HD4Y3GYL.mjs";
8
+ import "./chunk-JSM7Q5CY.mjs";
9
+
10
+ // src/cli-action.ts
11
+ import {
12
+ loadMergedConfig,
13
+ handleJSONOutput,
14
+ handleCLIError,
15
+ getElapsedTime,
16
+ resolveOutputPath
17
+ } from "@aiready/core";
18
+ import chalk from "chalk";
19
+ import { writeFileSync } from "fs";
20
+ async function contextActionHandler(directory, options) {
21
+ console.log(chalk.blue("\u{1F50D} Analyzing context window costs...\n"));
22
+ const startTime = Date.now();
23
+ try {
24
+ const defaults = {
25
+ maxDepth: 5,
26
+ maxContextBudget: 1e4,
27
+ minCohesion: 0.6,
28
+ maxFragmentation: 0.5,
29
+ focus: "all",
30
+ includeNodeModules: false,
31
+ include: void 0,
32
+ exclude: void 0,
33
+ maxResults: 10
34
+ };
35
+ let finalOptions = await loadMergedConfig(directory, defaults, {
36
+ maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
37
+ maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
38
+ minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : void 0,
39
+ maxFragmentation: options.maxFragmentation ? parseFloat(options.maxFragmentation) : void 0,
40
+ focus: options.focus || void 0,
41
+ includeNodeModules: options.includeNodeModules,
42
+ include: options.include?.split(","),
43
+ exclude: options.exclude?.split(","),
44
+ maxResults: options.maxResults ? parseInt(options.maxResults) : void 0
45
+ });
46
+ if (options.interactive) {
47
+ const { runInteractiveSetup } = await import("./interactive-setup-JGFBFI3M.mjs");
48
+ finalOptions = await runInteractiveSetup(directory, finalOptions);
49
+ }
50
+ const results = await analyzeContext(
51
+ finalOptions
52
+ );
53
+ const summary = generateSummary(results, finalOptions);
54
+ const duration = getElapsedTime(startTime);
55
+ if (options.output === "json") {
56
+ handleJSONOutput(
57
+ {
58
+ summary: {
59
+ ...summary,
60
+ executionTime: duration,
61
+ config: {
62
+ scan: { tools: ["context"] },
63
+ tools: { context: finalOptions }
64
+ },
65
+ toolConfigs: { context: finalOptions }
66
+ },
67
+ context: { results }
68
+ },
69
+ options.outputFile
70
+ );
71
+ } else if (options.output === "html") {
72
+ const { generateHTMLReport } = await import("./html-report-BYGKWC3K.mjs");
73
+ const html = generateHTMLReport(summary, results);
74
+ const outputPath = resolveOutputPath(
75
+ directory,
76
+ options.outputFile,
77
+ "context-report.html"
78
+ );
79
+ writeFileSync(outputPath, html, "utf-8");
80
+ console.log(chalk.green(`
81
+ \u2705 HTML report saved to: ${outputPath}`));
82
+ } else {
83
+ const { displayConsoleReport } = await import("./console-report-CVGRMWEU.mjs");
84
+ displayConsoleReport(summary, results, finalOptions.maxResults);
85
+ console.log(chalk.dim(`
86
+ \u2728 Analysis completed in ${duration}ms
87
+ `));
88
+ }
89
+ } catch (error) {
90
+ handleCLIError(error, "context-analyzer");
91
+ }
92
+ }
93
+ export {
94
+ contextActionHandler
95
+ };
package/dist/cli.js CHANGED
@@ -135,6 +135,7 @@ function analyzeIssues(params) {
135
135
  const {
136
136
  file,
137
137
  importDepth,
138
+ tokenCost,
138
139
  contextBudget,
139
140
  cohesionScore,
140
141
  fragmentationScore,
@@ -169,21 +170,29 @@ function analyzeIssues(params) {
169
170
  recommendations.push("Consider reducing dependency depth");
170
171
  potentialSavings += contextBudget * 0.15;
171
172
  }
172
- if (contextBudget > maxContextBudget * 1.5) {
173
- severity = import_core2.Severity.Critical;
173
+ const MAX_FILE_TOKENS = 1e4;
174
+ if (tokenCost > MAX_FILE_TOKENS) {
175
+ if (severity !== import_core2.Severity.Critical) severity = import_core2.Severity.Major;
174
176
  issues.push(
175
- `Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
177
+ `File is excessively large (${tokenCost.toLocaleString()} tokens)`
176
178
  );
177
179
  recommendations.push(
178
- "Split into smaller modules or reduce dependency tree"
180
+ "Split file into smaller, single-responsibility modules"
179
181
  );
182
+ }
183
+ if (contextBudget > maxContextBudget * 1.5) {
184
+ severity = import_core2.Severity.Critical;
185
+ issues.push(
186
+ `Total context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
187
+ );
188
+ recommendations.push("Reduce dependency tree width or reduce deep imports");
180
189
  potentialSavings += contextBudget * 0.4;
181
190
  } else if (contextBudget > maxContextBudget) {
182
191
  if (severity !== import_core2.Severity.Critical) severity = import_core2.Severity.Major;
183
192
  issues.push(
184
- `Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
193
+ `Total context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
185
194
  );
186
- recommendations.push("Reduce file size or dependencies");
195
+ recommendations.push("Optimize dependency graph and reduce deep imports");
187
196
  potentialSavings += contextBudget * 0.2;
188
197
  }
189
198
  if (cohesionScore < minCohesion * 0.5) {
@@ -268,19 +277,27 @@ function calculateImportDepthFromEdges(file, edges, visited = /* @__PURE__ */ ne
268
277
  return maxDepth;
269
278
  }
270
279
  function getTransitiveDependenciesFromEdges(file, edges, visited = /* @__PURE__ */ new Set()) {
271
- if (visited.has(file)) return [];
272
- const nextVisited = new Set(visited);
273
- nextVisited.add(file);
274
- const dependencies = edges.get(file);
275
- if (!dependencies || dependencies.size === 0) return [];
276
- const allDeps = [];
277
- for (const dep of dependencies) {
278
- allDeps.push(dep);
279
- allDeps.push(
280
- ...getTransitiveDependenciesFromEdges(dep, edges, nextVisited)
281
- );
280
+ const result = /* @__PURE__ */ new Map();
281
+ function dfs(current, depth) {
282
+ if (visited.has(current)) {
283
+ const existingDepth = result.get(current);
284
+ if (existingDepth !== void 0 && depth < existingDepth) {
285
+ result.set(current, depth);
286
+ }
287
+ return;
288
+ }
289
+ visited.add(current);
290
+ if (current !== file) {
291
+ result.set(current, depth);
292
+ }
293
+ const dependencies = edges.get(current);
294
+ if (!dependencies) return;
295
+ for (const dep of dependencies) {
296
+ dfs(dep, depth + 1);
297
+ }
282
298
  }
283
- return [...new Set(allDeps)];
299
+ dfs(file, 0);
300
+ return result;
284
301
  }
285
302
  function detectGraphCycles(edges) {
286
303
  const cycles = [];
@@ -605,6 +622,30 @@ var init_ast_utils = __esm({
605
622
  // src/graph-builder.ts
606
623
  function resolveImport(source, importingFile, allFiles) {
607
624
  if (!source.startsWith(".") && !source.startsWith("/")) {
625
+ const externalIgnores = [
626
+ "react",
627
+ "next",
628
+ "lucide-react",
629
+ "framer-motion",
630
+ "@aws-sdk",
631
+ "stripe",
632
+ "clsx",
633
+ "tailwind-merge",
634
+ "zod",
635
+ "commander",
636
+ "chalk",
637
+ "fs",
638
+ "path",
639
+ "util",
640
+ "child_process",
641
+ "os",
642
+ "crypto"
643
+ ];
644
+ if (externalIgnores.some(
645
+ (pkg) => source === pkg || source.startsWith(`${pkg}/`)
646
+ )) {
647
+ return null;
648
+ }
608
649
  if (source.startsWith("@aiready/")) {
609
650
  const pkgName = source.split("/")[1];
610
651
  const possiblePaths = [
@@ -683,8 +724,9 @@ async function buildDependencyGraph(files, options) {
683
724
  const allFilePaths = new Set(files.map((f) => f.file));
684
725
  for (const { file, content } of files) {
685
726
  const { imports: astImports } = await (0, import_core4.parseFileExports)(content, file);
686
- const resolvedImports = astImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
687
- const importSources = astImports.map((i) => i.source);
727
+ const runtimeImports = astImports.filter((i) => !i.isTypeOnly);
728
+ const resolvedImports = runtimeImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
729
+ const importSources = runtimeImports.map((i) => i.source);
688
730
  const exports2 = await extractExportsWithAST(
689
731
  content,
690
732
  file,
@@ -737,13 +779,14 @@ function calculateContextBudget(file, graph) {
737
779
  if (!node) return 0;
738
780
  let totalTokens = node.tokenCost;
739
781
  const deps = getTransitiveDependencies(file, graph);
740
- for (const dep of deps) {
782
+ for (const [dep, depth] of deps.entries()) {
741
783
  const depNode = graph.nodes.get(dep);
742
784
  if (depNode) {
743
- totalTokens += depNode.tokenCost;
785
+ const discountFactor = Math.pow(0.8, depth - 1);
786
+ totalTokens += depNode.tokenCost * discountFactor;
744
787
  }
745
788
  }
746
- return totalTokens;
789
+ return Math.round(totalTokens);
747
790
  }
748
791
  function detectCircularDependencies(graph) {
749
792
  return detectGraphCycles(graph.edges);
@@ -1508,6 +1551,7 @@ function mapNodeToResult(node, graph, clusters, allCircularDeps, options) {
1508
1551
  {
1509
1552
  file,
1510
1553
  importDepth,
1554
+ tokenCost,
1511
1555
  contextBudget,
1512
1556
  cohesionScore,
1513
1557
  fragmentationScore,
@@ -1531,8 +1575,8 @@ function mapNodeToResult(node, graph, clusters, allCircularDeps, options) {
1531
1575
  tokenCost,
1532
1576
  linesOfCode: node.linesOfCode,
1533
1577
  importDepth,
1534
- dependencyCount: transitiveDeps.length,
1535
- dependencyList: transitiveDeps,
1578
+ dependencyCount: transitiveDeps.size,
1579
+ dependencyList: Array.from(transitiveDeps.keys()),
1536
1580
  circularDeps,
1537
1581
  cohesionScore,
1538
1582
  domains: Array.from(
@@ -1589,6 +1633,8 @@ async function analyzeContext(options) {
1589
1633
  const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
1590
1634
  file: metric.file,
1591
1635
  importDepth: metric.importDepth,
1636
+ tokenCost: metric.contextBudget,
1637
+ // For Python, we use the reported budget as self-cost for now
1592
1638
  contextBudget: metric.contextBudget,
1593
1639
  cohesionScore: metric.cohesion,
1594
1640
  fragmentationScore: 0,
@@ -1914,8 +1960,8 @@ function generateHTMLReport(summary, results) {
1914
1960
  {
1915
1961
  title: "Context Analysis Report",
1916
1962
  packageName: "context-analyzer",
1917
- packageUrl: "https://github.com/caopengau/aiready-context-analyzer",
1918
- bugUrl: "https://github.com/caopengau/aiready-context-analyzer/issues",
1963
+ packageUrl: "https://github.com/getaiready/aiready-context-analyzer",
1964
+ bugUrl: "https://github.com/getaiready/aiready-context-analyzer/issues",
1919
1965
  emoji: "\u{1F9E0}"
1920
1966
  },
1921
1967
  stats,
@@ -1995,12 +2041,12 @@ function displayConsoleReport(summary, results, maxResults = 10) {
1995
2041
  console.log(import_chalk2.default.cyan(divider));
1996
2042
  console.log(
1997
2043
  import_chalk2.default.dim(
1998
- "\n\u2B50 Like aiready? Star us on GitHub: https://github.com/caopengau/aiready-context-analyzer"
2044
+ "\n\u2B50 Like aiready? Star us on GitHub: https://github.com/getaiready/aiready-context-analyzer"
1999
2045
  )
2000
2046
  );
2001
2047
  console.log(
2002
2048
  import_chalk2.default.dim(
2003
- "\u{1F41B} Found a bug? Report it: https://github.com/caopengau/aiready-context-analyzer/issues\n"
2049
+ "\u{1F41B} Found a bug? Report it: https://github.com/getaiready/aiready-context-analyzer/issues\n"
2004
2050
  )
2005
2051
  );
2006
2052
  }
@@ -2022,7 +2068,7 @@ async function contextActionHandler(directory, options) {
2022
2068
  const startTime = Date.now();
2023
2069
  try {
2024
2070
  const defaults = {
2025
- maxDepth: 5,
2071
+ maxDepth: 10,
2026
2072
  maxContextBudget: 1e4,
2027
2073
  minCohesion: 0.6,
2028
2074
  maxFragmentation: 0.5,
@@ -2047,7 +2093,9 @@ async function contextActionHandler(directory, options) {
2047
2093
  const { runInteractiveSetup: runInteractiveSetup2 } = await Promise.resolve().then(() => (init_interactive_setup(), interactive_setup_exports));
2048
2094
  finalOptions = await runInteractiveSetup2(directory, finalOptions);
2049
2095
  }
2050
- const results = await analyzeContext(finalOptions);
2096
+ const results = await analyzeContext(
2097
+ finalOptions
2098
+ );
2051
2099
  const summary = generateSummary(results, finalOptions);
2052
2100
  const duration = (0, import_core9.getElapsedTime)(startTime);
2053
2101
  if (options.output === "json") {
package/dist/cli.mjs CHANGED
@@ -37,7 +37,7 @@ function defineContextCommand(program2) {
37
37
  "--interactive",
38
38
  "Run interactive setup to suggest excludes and focus areas"
39
39
  ).action(async (directory, options) => {
40
- const { contextActionHandler } = await import("./cli-action-W7TESWAV.mjs");
40
+ const { contextActionHandler } = await import("./cli-action-7QXG7LHS.mjs");
41
41
  await contextActionHandler(directory, options);
42
42
  });
43
43
  }
@@ -0,0 +1,74 @@
1
+ // src/report/console-report.ts
2
+ import chalk from "chalk";
3
+ function displayConsoleReport(summary, results, maxResults = 10) {
4
+ const divider = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
5
+ const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
6
+ console.log(chalk.bold("\u{1F4CA} Context Analysis Summary:\n"));
7
+ console.log(` \u2022 Total Files: ${chalk.cyan(summary.totalFiles)}`);
8
+ console.log(
9
+ ` \u2022 Total Tokens: ${chalk.cyan(summary.totalTokens.toLocaleString())}`
10
+ );
11
+ console.log(
12
+ ` \u2022 Avg Budget: ${chalk.cyan(summary.avgContextBudget.toFixed(0))} tokens`
13
+ );
14
+ console.log(
15
+ ` \u2022 Potential Saving: ${chalk.green(summary.totalPotentialSavings.toLocaleString())} tokens`
16
+ );
17
+ console.log();
18
+ if (totalIssues > 0) {
19
+ console.log(chalk.bold("\u26A0\uFE0F Issues Detected:\n"));
20
+ console.log(` \u2022 ${chalk.red("\u{1F534} Critical:")} ${summary.criticalIssues}`);
21
+ console.log(` \u2022 ${chalk.yellow("\u{1F7E1} Major:")} ${summary.majorIssues}`);
22
+ console.log(` \u2022 ${chalk.blue("\u{1F535} Minor:")} ${summary.minorIssues}`);
23
+ console.log();
24
+ } else {
25
+ console.log(chalk.green("\u2705 No significant context issues detected!\n"));
26
+ }
27
+ if (summary.fragmentedModules.length > 0) {
28
+ console.log(chalk.bold("\u{1F9E9} Top Fragmented Modules:\n"));
29
+ summary.fragmentedModules.slice(0, maxResults).forEach((mod) => {
30
+ const scoreColor = mod.fragmentationScore > 0.7 ? chalk.red : mod.fragmentationScore > 0.4 ? chalk.yellow : chalk.green;
31
+ console.log(
32
+ ` ${scoreColor("\u25A0")} ${chalk.white(mod.domain)} ${chalk.dim(`(${mod.files.length} files, ${(mod.fragmentationScore * 100).toFixed(0)}% frag)`)}`
33
+ );
34
+ });
35
+ console.log();
36
+ }
37
+ if (summary.topExpensiveFiles.length > 0) {
38
+ console.log(chalk.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
39
+ summary.topExpensiveFiles.slice(0, maxResults).forEach((item) => {
40
+ const fileName = item.file.split("/").slice(-2).join("/");
41
+ const severityColor = item.severity === "critical" ? chalk.red : item.severity === "major" ? chalk.yellow : chalk.blue;
42
+ console.log(
43
+ ` ${severityColor("\u25CF")} ${chalk.white(fileName)} ${chalk.dim(`- ${item.contextBudget.toLocaleString()} tokens`)}`
44
+ );
45
+ });
46
+ console.log();
47
+ }
48
+ if (totalIssues > 0) {
49
+ console.log(chalk.bold("\u{1F4A1} Top Recommendations:\n"));
50
+ const topFiles = results.filter((r) => r.severity === "critical" || r.severity === "major").slice(0, 3);
51
+ topFiles.forEach((result, index) => {
52
+ const fileName = result.file.split("/").slice(-2).join("/");
53
+ console.log(chalk.cyan(` ${index + 1}. ${fileName}`));
54
+ result.recommendations.slice(0, 2).forEach((rec) => {
55
+ console.log(chalk.dim(` \u2022 ${rec}`));
56
+ });
57
+ });
58
+ console.log();
59
+ }
60
+ console.log(chalk.cyan(divider));
61
+ console.log(
62
+ chalk.dim(
63
+ "\n\u2B50 Like aiready? Star us on GitHub: https://github.com/getaiready/aiready-context-analyzer"
64
+ )
65
+ );
66
+ console.log(
67
+ chalk.dim(
68
+ "\u{1F41B} Found a bug? Report it: https://github.com/getaiready/aiready-context-analyzer/issues\n"
69
+ )
70
+ );
71
+ }
72
+ export {
73
+ displayConsoleReport
74
+ };
@@ -0,0 +1,73 @@
1
+ // src/report/html-report.ts
2
+ import {
3
+ generateIssueSummary,
4
+ generateTable,
5
+ generateStandardHtmlReport
6
+ } from "@aiready/core";
7
+ function generateHTMLReport(summary, results) {
8
+ const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
9
+ void results;
10
+ const stats = [
11
+ { value: summary.totalFiles, label: "Files Analyzed" },
12
+ { value: summary.totalTokens.toLocaleString(), label: "Total Tokens" },
13
+ { value: summary.avgContextBudget.toFixed(0), label: "Avg Context Budget" },
14
+ {
15
+ value: totalIssues,
16
+ label: "Total Issues",
17
+ color: totalIssues > 0 ? "#f39c12" : void 0
18
+ }
19
+ ];
20
+ const sections = [];
21
+ if (totalIssues > 0) {
22
+ sections.push({
23
+ title: "\u26A0\uFE0F Issues Summary",
24
+ content: generateIssueSummary(
25
+ summary.criticalIssues,
26
+ summary.majorIssues,
27
+ summary.minorIssues,
28
+ summary.totalPotentialSavings
29
+ )
30
+ });
31
+ }
32
+ if (summary.fragmentedModules.length > 0) {
33
+ sections.push({
34
+ title: "\u{1F9E9} Fragmented Modules",
35
+ content: generateTable({
36
+ headers: ["Domain", "Files", "Fragmentation", "Token Cost"],
37
+ rows: summary.fragmentedModules.map((m) => [
38
+ m.domain,
39
+ String(m.files.length),
40
+ `${(m.fragmentationScore * 100).toFixed(0)}%`,
41
+ m.totalTokens.toLocaleString()
42
+ ])
43
+ })
44
+ });
45
+ }
46
+ if (summary.topExpensiveFiles.length > 0) {
47
+ sections.push({
48
+ title: "\u{1F4B8} Most Expensive Files",
49
+ content: generateTable({
50
+ headers: ["File", "Context Budget", "Severity"],
51
+ rows: summary.topExpensiveFiles.map((f) => [
52
+ f.file,
53
+ `${f.contextBudget.toLocaleString()} tokens`,
54
+ `<span class="issue-${f.severity}">${f.severity.toUpperCase()}</span>`
55
+ ])
56
+ })
57
+ });
58
+ }
59
+ return generateStandardHtmlReport(
60
+ {
61
+ title: "Context Analysis Report",
62
+ packageName: "context-analyzer",
63
+ packageUrl: "https://github.com/getaiready/aiready-context-analyzer",
64
+ bugUrl: "https://github.com/getaiready/aiready-context-analyzer/issues",
65
+ emoji: "\u{1F9E0}"
66
+ },
67
+ stats,
68
+ sections
69
+ );
70
+ }
71
+ export {
72
+ generateHTMLReport
73
+ };
package/dist/index.d.mts CHANGED
@@ -23,6 +23,8 @@ interface ContextAnalyzerOptions extends ScanOptions {
23
23
  focus?: 'fragmentation' | 'cohesion' | 'depth' | 'all';
24
24
  /** Whether to include node_modules in the analysis (default: false) */
25
25
  includeNodeModules?: boolean;
26
+ /** Maximum number of results to display in console output (default: 10) */
27
+ maxResults?: number;
26
28
  }
27
29
  /**
28
30
  * The result of a context analysis for a single file or module.
@@ -286,13 +288,13 @@ declare function calculateImportDepth(file: string, graph: DependencyGraph, visi
286
288
  * @param visited - Optional set to track visited nodes.
287
289
  * @returns Array of all reachable file paths.
288
290
  */
289
- declare function getTransitiveDependencies(file: string, graph: DependencyGraph, visited?: Set<string>): string[];
291
+ declare function getTransitiveDependencies(file: string, graph: DependencyGraph, visited?: Set<string>): Map<string, number>;
290
292
  /**
291
293
  * Calculate total context budget (tokens needed to understand this file and its dependencies).
292
294
  *
293
295
  * @param file - File path to calculate budget for.
294
296
  * @param graph - The dependency graph.
295
- * @returns Total token count including recursive dependencies.
297
+ * @returns Total token count including recursive dependencies (discounted by depth).
296
298
  */
297
299
  declare function calculateContextBudget(file: string, graph: DependencyGraph): number;
298
300
  /**
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ interface ContextAnalyzerOptions extends ScanOptions {
23
23
  focus?: 'fragmentation' | 'cohesion' | 'depth' | 'all';
24
24
  /** Whether to include node_modules in the analysis (default: false) */
25
25
  includeNodeModules?: boolean;
26
+ /** Maximum number of results to display in console output (default: 10) */
27
+ maxResults?: number;
26
28
  }
27
29
  /**
28
30
  * The result of a context analysis for a single file or module.
@@ -286,13 +288,13 @@ declare function calculateImportDepth(file: string, graph: DependencyGraph, visi
286
288
  * @param visited - Optional set to track visited nodes.
287
289
  * @returns Array of all reachable file paths.
288
290
  */
289
- declare function getTransitiveDependencies(file: string, graph: DependencyGraph, visited?: Set<string>): string[];
291
+ declare function getTransitiveDependencies(file: string, graph: DependencyGraph, visited?: Set<string>): Map<string, number>;
290
292
  /**
291
293
  * Calculate total context budget (tokens needed to understand this file and its dependencies).
292
294
  *
293
295
  * @param file - File path to calculate budget for.
294
296
  * @param graph - The dependency graph.
295
- * @returns Total token count including recursive dependencies.
297
+ * @returns Total token count including recursive dependencies (discounted by depth).
296
298
  */
297
299
  declare function calculateContextBudget(file: string, graph: DependencyGraph): number;
298
300
  /**