@aiready/context-analyzer 0.22.15 → 0.22.17

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 (47) hide show
  1. package/.turbo/turbo-build.log +18 -18
  2. package/.turbo/turbo-format-check.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/.turbo/turbo-test.log +408 -30
  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-RO24U52W.mjs +95 -0
  19. package/dist/cli-action-WAZ5KM6X.mjs +95 -0
  20. package/dist/cli-action-XDKINE2R.mjs +95 -0
  21. package/dist/cli-action-Y6VATXMV.mjs +95 -0
  22. package/dist/cli.js +75 -27
  23. package/dist/cli.mjs +1 -1
  24. package/dist/index.d.mts +4 -2
  25. package/dist/index.d.ts +4 -2
  26. package/dist/index.js +71 -25
  27. package/dist/index.mjs +3 -3
  28. package/dist/orchestrator-2KQNMO2L.mjs +10 -0
  29. package/dist/orchestrator-66ZVNOLR.mjs +10 -0
  30. package/dist/orchestrator-KM2OJPZD.mjs +10 -0
  31. package/dist/orchestrator-MKDZPRBA.mjs +10 -0
  32. package/dist/orchestrator-QSHWWBWS.mjs +10 -0
  33. package/dist/orchestrator-WFQPMNSD.mjs +10 -0
  34. package/dist/python-context-H2OLC5JN.mjs +162 -0
  35. package/dist/python-context-OBP7JD5P.mjs +162 -0
  36. package/package.json +13 -10
  37. package/src/__tests__/analyzer.test.ts +4 -3
  38. package/src/__tests__/issue-analyzer.test.ts +4 -2
  39. package/src/classify/file-classifiers.ts +14 -13
  40. package/src/cli-action.ts +6 -3
  41. package/src/graph-builder.ts +43 -8
  42. package/src/issue-analyzer.ts +19 -7
  43. package/src/orchestrator.ts +6 -4
  44. package/src/semantic/domain-inference.ts +1 -1
  45. package/src/types.ts +2 -0
  46. package/src/utils/dependency-graph-utils.ts +22 -13
  47. package/tsconfig.json +2 -1
@@ -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,
@@ -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-JKG3R6RV.mjs");
40
+ const { contextActionHandler } = await import("./cli-action-7QXG7LHS.mjs");
41
41
  await contextActionHandler(directory, options);
42
42
  });
43
43
  }
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
  /**
package/dist/index.js CHANGED
@@ -177,6 +177,7 @@ function analyzeIssues(params) {
177
177
  const {
178
178
  file,
179
179
  importDepth,
180
+ tokenCost,
180
181
  contextBudget,
181
182
  cohesionScore,
182
183
  fragmentationScore,
@@ -211,21 +212,29 @@ function analyzeIssues(params) {
211
212
  recommendations.push("Consider reducing dependency depth");
212
213
  potentialSavings += contextBudget * 0.15;
213
214
  }
214
- if (contextBudget > maxContextBudget * 1.5) {
215
- severity = import_core3.Severity.Critical;
215
+ const MAX_FILE_TOKENS = 1e4;
216
+ if (tokenCost > MAX_FILE_TOKENS) {
217
+ if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
216
218
  issues.push(
217
- `Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
219
+ `File is excessively large (${tokenCost.toLocaleString()} tokens)`
218
220
  );
219
221
  recommendations.push(
220
- "Split into smaller modules or reduce dependency tree"
222
+ "Split file into smaller, single-responsibility modules"
221
223
  );
224
+ }
225
+ if (contextBudget > maxContextBudget * 1.5) {
226
+ severity = import_core3.Severity.Critical;
227
+ issues.push(
228
+ `Total context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
229
+ );
230
+ recommendations.push("Reduce dependency tree width or reduce deep imports");
222
231
  potentialSavings += contextBudget * 0.4;
223
232
  } else if (contextBudget > maxContextBudget) {
224
233
  if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
225
234
  issues.push(
226
- `Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
235
+ `Total context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
227
236
  );
228
- recommendations.push("Reduce file size or dependencies");
237
+ recommendations.push("Optimize dependency graph and reduce deep imports");
229
238
  potentialSavings += contextBudget * 0.2;
230
239
  }
231
240
  if (cohesionScore < minCohesion * 0.5) {
@@ -310,19 +319,27 @@ function calculateImportDepthFromEdges(file, edges, visited = /* @__PURE__ */ ne
310
319
  return maxDepth;
311
320
  }
312
321
  function getTransitiveDependenciesFromEdges(file, edges, visited = /* @__PURE__ */ new Set()) {
313
- if (visited.has(file)) return [];
314
- const nextVisited = new Set(visited);
315
- nextVisited.add(file);
316
- const dependencies = edges.get(file);
317
- if (!dependencies || dependencies.size === 0) return [];
318
- const allDeps = [];
319
- for (const dep of dependencies) {
320
- allDeps.push(dep);
321
- allDeps.push(
322
- ...getTransitiveDependenciesFromEdges(dep, edges, nextVisited)
323
- );
322
+ const result = /* @__PURE__ */ new Map();
323
+ function dfs(current, depth) {
324
+ if (visited.has(current)) {
325
+ const existingDepth = result.get(current);
326
+ if (existingDepth !== void 0 && depth < existingDepth) {
327
+ result.set(current, depth);
328
+ }
329
+ return;
330
+ }
331
+ visited.add(current);
332
+ if (current !== file) {
333
+ result.set(current, depth);
334
+ }
335
+ const dependencies = edges.get(current);
336
+ if (!dependencies) return;
337
+ for (const dep of dependencies) {
338
+ dfs(dep, depth + 1);
339
+ }
324
340
  }
325
- return [...new Set(allDeps)];
341
+ dfs(file, 0);
342
+ return result;
326
343
  }
327
344
  function detectGraphCycles(edges) {
328
345
  const cycles = [];
@@ -647,6 +664,30 @@ var init_ast_utils = __esm({
647
664
  // src/graph-builder.ts
648
665
  function resolveImport(source, importingFile, allFiles) {
649
666
  if (!source.startsWith(".") && !source.startsWith("/")) {
667
+ const externalIgnores = [
668
+ "react",
669
+ "next",
670
+ "lucide-react",
671
+ "framer-motion",
672
+ "@aws-sdk",
673
+ "stripe",
674
+ "clsx",
675
+ "tailwind-merge",
676
+ "zod",
677
+ "commander",
678
+ "chalk",
679
+ "fs",
680
+ "path",
681
+ "util",
682
+ "child_process",
683
+ "os",
684
+ "crypto"
685
+ ];
686
+ if (externalIgnores.some(
687
+ (pkg) => source === pkg || source.startsWith(`${pkg}/`)
688
+ )) {
689
+ return null;
690
+ }
650
691
  if (source.startsWith("@aiready/")) {
651
692
  const pkgName = source.split("/")[1];
652
693
  const possiblePaths = [
@@ -725,8 +766,9 @@ async function buildDependencyGraph(files, options) {
725
766
  const allFilePaths = new Set(files.map((f) => f.file));
726
767
  for (const { file, content } of files) {
727
768
  const { imports: astImports } = await (0, import_core5.parseFileExports)(content, file);
728
- const resolvedImports = astImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
729
- const importSources = astImports.map((i) => i.source);
769
+ const runtimeImports = astImports.filter((i) => !i.isTypeOnly);
770
+ const resolvedImports = runtimeImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
771
+ const importSources = runtimeImports.map((i) => i.source);
730
772
  const exports2 = await extractExportsWithAST(
731
773
  content,
732
774
  file,
@@ -779,13 +821,14 @@ function calculateContextBudget(file, graph) {
779
821
  if (!node) return 0;
780
822
  let totalTokens = node.tokenCost;
781
823
  const deps = getTransitiveDependencies(file, graph);
782
- for (const dep of deps) {
824
+ for (const [dep, depth] of deps.entries()) {
783
825
  const depNode = graph.nodes.get(dep);
784
826
  if (depNode) {
785
- totalTokens += depNode.tokenCost;
827
+ const discountFactor = Math.pow(0.8, depth - 1);
828
+ totalTokens += depNode.tokenCost * discountFactor;
786
829
  }
787
830
  }
788
- return totalTokens;
831
+ return Math.round(totalTokens);
789
832
  }
790
833
  function detectCircularDependencies(graph) {
791
834
  return detectGraphCycles(graph.edges);
@@ -1604,6 +1647,7 @@ function mapNodeToResult(node, graph, clusters, allCircularDeps, options) {
1604
1647
  {
1605
1648
  file,
1606
1649
  importDepth,
1650
+ tokenCost,
1607
1651
  contextBudget,
1608
1652
  cohesionScore,
1609
1653
  fragmentationScore,
@@ -1627,8 +1671,8 @@ function mapNodeToResult(node, graph, clusters, allCircularDeps, options) {
1627
1671
  tokenCost,
1628
1672
  linesOfCode: node.linesOfCode,
1629
1673
  importDepth,
1630
- dependencyCount: transitiveDeps.length,
1631
- dependencyList: transitiveDeps,
1674
+ dependencyCount: transitiveDeps.size,
1675
+ dependencyList: Array.from(transitiveDeps.keys()),
1632
1676
  circularDeps,
1633
1677
  cohesionScore,
1634
1678
  domains: Array.from(
@@ -1688,6 +1732,8 @@ async function analyzeContext(options) {
1688
1732
  const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
1689
1733
  file: metric.file,
1690
1734
  importDepth: metric.importDepth,
1735
+ tokenCost: metric.contextBudget,
1736
+ // For Python, we use the reported budget as self-cost for now
1691
1737
  contextBudget: metric.contextBudget,
1692
1738
  cohesionScore: metric.cohesion,
1693
1739
  fragmentationScore: 0,
package/dist/index.mjs CHANGED
@@ -12,8 +12,8 @@ import {
12
12
  getClassificationRecommendations,
13
13
  getGeneralRecommendations,
14
14
  getTransitiveDependencies
15
- } from "./chunk-ISWPFG2C.mjs";
16
- import "./chunk-64U3PNO3.mjs";
15
+ } from "./chunk-2LEHY2GV.mjs";
16
+ import "./chunk-RQ5BQLT6.mjs";
17
17
  import {
18
18
  generateSummary
19
19
  } from "./chunk-HD4Y3GYL.mjs";
@@ -230,7 +230,7 @@ var CONTEXT_ANALYZER_PROVIDER = {
230
230
  id: ToolName2.ContextAnalyzer,
231
231
  alias: ["context", "fragmentation", "budget"],
232
232
  async analyze(options) {
233
- const { analyzeContext: analyzeContext2 } = await import("./orchestrator-QNE2E4TE.mjs");
233
+ const { analyzeContext: analyzeContext2 } = await import("./orchestrator-66ZVNOLR.mjs");
234
234
  const { generateSummary: generateSummary2 } = await import("./summary-TZFB6ZFM.mjs");
235
235
  const results = await analyzeContext2(options);
236
236
  const summary = generateSummary2(results, options);
@@ -0,0 +1,10 @@
1
+ import {
2
+ analyzeContext,
3
+ calculateCohesion
4
+ } from "./chunk-QTB4KYCX.mjs";
5
+ import "./chunk-64U3PNO3.mjs";
6
+ import "./chunk-JSM7Q5CY.mjs";
7
+ export {
8
+ analyzeContext,
9
+ calculateCohesion
10
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ analyzeContext,
3
+ calculateCohesion
4
+ } from "./chunk-2LEHY2GV.mjs";
5
+ import "./chunk-RQ5BQLT6.mjs";
6
+ import "./chunk-JSM7Q5CY.mjs";
7
+ export {
8
+ analyzeContext,
9
+ calculateCohesion
10
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ analyzeContext,
3
+ calculateCohesion
4
+ } from "./chunk-P2ZQGQAO.mjs";
5
+ import "./chunk-64U3PNO3.mjs";
6
+ import "./chunk-JSM7Q5CY.mjs";
7
+ export {
8
+ analyzeContext,
9
+ calculateCohesion
10
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ analyzeContext,
3
+ calculateCohesion
4
+ } from "./chunk-QGI23DBA.mjs";
5
+ import "./chunk-WLXLBWDU.mjs";
6
+ import "./chunk-JSM7Q5CY.mjs";
7
+ export {
8
+ analyzeContext,
9
+ calculateCohesion
10
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ analyzeContext,
3
+ calculateCohesion
4
+ } from "./chunk-VYFHSGV6.mjs";
5
+ import "./chunk-RQ5BQLT6.mjs";
6
+ import "./chunk-JSM7Q5CY.mjs";
7
+ export {
8
+ analyzeContext,
9
+ calculateCohesion
10
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ analyzeContext,
3
+ calculateCohesion
4
+ } from "./chunk-XDYPMFCH.mjs";
5
+ import "./chunk-64U3PNO3.mjs";
6
+ import "./chunk-JSM7Q5CY.mjs";
7
+ export {
8
+ analyzeContext,
9
+ calculateCohesion
10
+ };