@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
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
+ };
@@ -0,0 +1,162 @@
1
+ import {
2
+ calculateImportDepthFromEdges,
3
+ detectGraphCyclesFromFile
4
+ } from "./chunk-WLXLBWDU.mjs";
5
+
6
+ // src/analyzers/python-context.ts
7
+ import { getParser, estimateTokens } from "@aiready/core";
8
+ import { resolve, relative, dirname, join } from "path";
9
+ import fs from "fs";
10
+ async function analyzePythonContext(files, rootDir) {
11
+ const results = [];
12
+ const parser = await getParser("dummy.py");
13
+ if (!parser) {
14
+ console.warn("Python parser not available");
15
+ return results;
16
+ }
17
+ const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
18
+ void relative;
19
+ void join;
20
+ const dependencyGraph = await buildPythonDependencyGraph(
21
+ pythonFiles,
22
+ rootDir
23
+ );
24
+ for (const file of pythonFiles) {
25
+ try {
26
+ const code = await fs.promises.readFile(file, "utf-8");
27
+ const result = parser.parse(code, file);
28
+ const imports = result.imports.map((imp) => ({
29
+ source: imp.source,
30
+ specifiers: imp.specifiers,
31
+ isRelative: imp.source.startsWith("."),
32
+ resolvedPath: resolvePythonImport(file, imp.source, rootDir)
33
+ }));
34
+ const exports = result.exports.map((exp) => ({
35
+ name: exp.name,
36
+ type: exp.type
37
+ }));
38
+ const linesOfCode = code.split("\n").length;
39
+ const importDepth = calculateImportDepthFromEdges(
40
+ file,
41
+ dependencyGraph,
42
+ /* @__PURE__ */ new Set()
43
+ );
44
+ const contextBudget = estimateContextBudget(
45
+ code,
46
+ imports,
47
+ dependencyGraph
48
+ );
49
+ const cohesion = calculatePythonCohesion(exports, imports);
50
+ const circularDependencies = detectGraphCyclesFromFile(
51
+ file,
52
+ dependencyGraph
53
+ ).map((cycle) => cycle.join(" -> "));
54
+ results.push({
55
+ file,
56
+ importDepth,
57
+ contextBudget,
58
+ cohesion,
59
+ imports,
60
+ exports,
61
+ metrics: {
62
+ linesOfCode,
63
+ importCount: imports.length,
64
+ exportCount: exports.length,
65
+ circularDependencies
66
+ }
67
+ });
68
+ } catch (error) {
69
+ console.warn(`Failed to analyze ${file}:`, error);
70
+ }
71
+ }
72
+ return results;
73
+ }
74
+ async function buildPythonDependencyGraph(files, rootDir) {
75
+ const graph = /* @__PURE__ */ new Map();
76
+ const parser = await getParser("dummy.py");
77
+ if (!parser) return graph;
78
+ for (const file of files) {
79
+ try {
80
+ const code = await fs.promises.readFile(file, "utf-8");
81
+ const result = parser.parse(code, file);
82
+ const dependencies = /* @__PURE__ */ new Set();
83
+ for (const imp of result.imports) {
84
+ const resolved = resolvePythonImport(file, imp.source, rootDir);
85
+ if (resolved && files.includes(resolved)) {
86
+ dependencies.add(resolved);
87
+ }
88
+ }
89
+ graph.set(file, dependencies);
90
+ } catch (error) {
91
+ void error;
92
+ }
93
+ }
94
+ return graph;
95
+ }
96
+ function resolvePythonImport(fromFile, importPath, rootDir) {
97
+ const dir = dirname(fromFile);
98
+ if (importPath.startsWith(".")) {
99
+ const parts = importPath.split(".");
100
+ let upCount = 0;
101
+ while (parts[0] === "") {
102
+ upCount++;
103
+ parts.shift();
104
+ }
105
+ let targetDir = dir;
106
+ for (let i = 0; i < upCount - 1; i++) {
107
+ targetDir = dirname(targetDir);
108
+ }
109
+ const modulePath = parts.join("/");
110
+ const possiblePaths = [
111
+ resolve(targetDir, `${modulePath}.py`),
112
+ resolve(targetDir, modulePath, "__init__.py")
113
+ ];
114
+ for (const path of possiblePaths) {
115
+ if (fs.existsSync(path)) {
116
+ return path;
117
+ }
118
+ }
119
+ } else {
120
+ const modulePath = importPath.replace(/\./g, "/");
121
+ const possiblePaths = [
122
+ resolve(rootDir, `${modulePath}.py`),
123
+ resolve(rootDir, modulePath, "__init__.py")
124
+ ];
125
+ for (const path of possiblePaths) {
126
+ if (fs.existsSync(path)) {
127
+ return path;
128
+ }
129
+ }
130
+ }
131
+ return void 0;
132
+ }
133
+ function estimateContextBudget(code, imports, dependencyGraph) {
134
+ void dependencyGraph;
135
+ let budget = estimateTokens(code);
136
+ const avgTokensPerDep = 500;
137
+ budget += imports.length * avgTokensPerDep;
138
+ return budget;
139
+ }
140
+ function calculatePythonCohesion(exports, imports) {
141
+ if (exports.length === 0) return 1;
142
+ const exportCount = exports.length;
143
+ const importCount = imports.length;
144
+ let cohesion = 1;
145
+ if (exportCount > 10) {
146
+ cohesion *= 0.6;
147
+ } else if (exportCount > 5) {
148
+ cohesion *= 0.8;
149
+ }
150
+ if (exportCount > 0) {
151
+ const ratio = importCount / exportCount;
152
+ if (ratio > 2) {
153
+ cohesion *= 1.1;
154
+ } else if (ratio < 0.5) {
155
+ cohesion *= 0.9;
156
+ }
157
+ }
158
+ return Math.min(1, Math.max(0, cohesion));
159
+ }
160
+ export {
161
+ analyzePythonContext
162
+ };
@@ -0,0 +1,162 @@
1
+ import {
2
+ calculateImportDepthFromEdges,
3
+ detectGraphCyclesFromFile
4
+ } from "./chunk-RQ5BQLT6.mjs";
5
+
6
+ // src/analyzers/python-context.ts
7
+ import { getParser, estimateTokens } from "@aiready/core";
8
+ import { resolve, relative, dirname, join } from "path";
9
+ import fs from "fs";
10
+ async function analyzePythonContext(files, rootDir) {
11
+ const results = [];
12
+ const parser = await getParser("dummy.py");
13
+ if (!parser) {
14
+ console.warn("Python parser not available");
15
+ return results;
16
+ }
17
+ const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
18
+ void relative;
19
+ void join;
20
+ const dependencyGraph = await buildPythonDependencyGraph(
21
+ pythonFiles,
22
+ rootDir
23
+ );
24
+ for (const file of pythonFiles) {
25
+ try {
26
+ const code = await fs.promises.readFile(file, "utf-8");
27
+ const result = parser.parse(code, file);
28
+ const imports = result.imports.map((imp) => ({
29
+ source: imp.source,
30
+ specifiers: imp.specifiers,
31
+ isRelative: imp.source.startsWith("."),
32
+ resolvedPath: resolvePythonImport(file, imp.source, rootDir)
33
+ }));
34
+ const exports = result.exports.map((exp) => ({
35
+ name: exp.name,
36
+ type: exp.type
37
+ }));
38
+ const linesOfCode = code.split("\n").length;
39
+ const importDepth = calculateImportDepthFromEdges(
40
+ file,
41
+ dependencyGraph,
42
+ /* @__PURE__ */ new Set()
43
+ );
44
+ const contextBudget = estimateContextBudget(
45
+ code,
46
+ imports,
47
+ dependencyGraph
48
+ );
49
+ const cohesion = calculatePythonCohesion(exports, imports);
50
+ const circularDependencies = detectGraphCyclesFromFile(
51
+ file,
52
+ dependencyGraph
53
+ ).map((cycle) => cycle.join(" -> "));
54
+ results.push({
55
+ file,
56
+ importDepth,
57
+ contextBudget,
58
+ cohesion,
59
+ imports,
60
+ exports,
61
+ metrics: {
62
+ linesOfCode,
63
+ importCount: imports.length,
64
+ exportCount: exports.length,
65
+ circularDependencies
66
+ }
67
+ });
68
+ } catch (error) {
69
+ console.warn(`Failed to analyze ${file}:`, error);
70
+ }
71
+ }
72
+ return results;
73
+ }
74
+ async function buildPythonDependencyGraph(files, rootDir) {
75
+ const graph = /* @__PURE__ */ new Map();
76
+ const parser = await getParser("dummy.py");
77
+ if (!parser) return graph;
78
+ for (const file of files) {
79
+ try {
80
+ const code = await fs.promises.readFile(file, "utf-8");
81
+ const result = parser.parse(code, file);
82
+ const dependencies = /* @__PURE__ */ new Set();
83
+ for (const imp of result.imports) {
84
+ const resolved = resolvePythonImport(file, imp.source, rootDir);
85
+ if (resolved && files.includes(resolved)) {
86
+ dependencies.add(resolved);
87
+ }
88
+ }
89
+ graph.set(file, dependencies);
90
+ } catch (error) {
91
+ void error;
92
+ }
93
+ }
94
+ return graph;
95
+ }
96
+ function resolvePythonImport(fromFile, importPath, rootDir) {
97
+ const dir = dirname(fromFile);
98
+ if (importPath.startsWith(".")) {
99
+ const parts = importPath.split(".");
100
+ let upCount = 0;
101
+ while (parts[0] === "") {
102
+ upCount++;
103
+ parts.shift();
104
+ }
105
+ let targetDir = dir;
106
+ for (let i = 0; i < upCount - 1; i++) {
107
+ targetDir = dirname(targetDir);
108
+ }
109
+ const modulePath = parts.join("/");
110
+ const possiblePaths = [
111
+ resolve(targetDir, `${modulePath}.py`),
112
+ resolve(targetDir, modulePath, "__init__.py")
113
+ ];
114
+ for (const path of possiblePaths) {
115
+ if (fs.existsSync(path)) {
116
+ return path;
117
+ }
118
+ }
119
+ } else {
120
+ const modulePath = importPath.replace(/\./g, "/");
121
+ const possiblePaths = [
122
+ resolve(rootDir, `${modulePath}.py`),
123
+ resolve(rootDir, modulePath, "__init__.py")
124
+ ];
125
+ for (const path of possiblePaths) {
126
+ if (fs.existsSync(path)) {
127
+ return path;
128
+ }
129
+ }
130
+ }
131
+ return void 0;
132
+ }
133
+ function estimateContextBudget(code, imports, dependencyGraph) {
134
+ void dependencyGraph;
135
+ let budget = estimateTokens(code);
136
+ const avgTokensPerDep = 500;
137
+ budget += imports.length * avgTokensPerDep;
138
+ return budget;
139
+ }
140
+ function calculatePythonCohesion(exports, imports) {
141
+ if (exports.length === 0) return 1;
142
+ const exportCount = exports.length;
143
+ const importCount = imports.length;
144
+ let cohesion = 1;
145
+ if (exportCount > 10) {
146
+ cohesion *= 0.6;
147
+ } else if (exportCount > 5) {
148
+ cohesion *= 0.8;
149
+ }
150
+ if (exportCount > 0) {
151
+ const ratio = importCount / exportCount;
152
+ if (ratio > 2) {
153
+ cohesion *= 1.1;
154
+ } else if (ratio < 0.5) {
155
+ cohesion *= 0.9;
156
+ }
157
+ }
158
+ return Math.min(1, Math.max(0, cohesion));
159
+ }
160
+ export {
161
+ analyzePythonContext
162
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/context-analyzer",
3
- "version": "0.22.14",
3
+ "version": "0.22.16",
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",
@@ -39,17 +39,17 @@
39
39
  "license": "MIT",
40
40
  "repository": {
41
41
  "type": "git",
42
- "url": "https://github.com/caopengau/aiready-context-analyzer.git"
42
+ "url": "https://github.com/getaiready/aiready-context-analyzer.git"
43
43
  },
44
- "homepage": "https://github.com/caopengau/aiready-context-analyzer",
44
+ "homepage": "https://github.com/getaiready/aiready-context-analyzer",
45
45
  "bugs": {
46
- "url": "https://github.com/caopengau/aiready-context-analyzer/issues"
46
+ "url": "https://github.com/getaiready/aiready-context-analyzer/issues"
47
47
  },
48
48
  "dependencies": {
49
49
  "commander": "^14.0.0",
50
50
  "chalk": "^5.3.0",
51
51
  "prompts": "^2.4.2",
52
- "@aiready/core": "0.24.15"
52
+ "@aiready/core": "0.24.19"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^24.0.0",
@@ -70,6 +70,8 @@
70
70
  "clean": "rm -rf dist",
71
71
  "release": "pnpm build && pnpm publish --no-git-checks",
72
72
  "type-check": "tsc --noEmit",
73
- "format-check": "prettier --check . --ignore-path ../../.prettierignore"
73
+ "format-check": "prettier --check . --ignore-path ../../.prettierignore",
74
+ "format": "prettier --write . --ignore-path ../../.prettierignore",
75
+ "lint:fix": "eslint . --fix"
74
76
  }
75
77
  }
@@ -93,9 +93,10 @@ describe('getTransitiveDependencies', () => {
93
93
  const graph = await buildDependencyGraph(files);
94
94
  const deps = getTransitiveDependencies('c.ts', graph);
95
95
 
96
- expect(deps).toContain('b.ts');
97
- expect(deps).toContain('a.ts');
98
- expect(deps.length).toBe(2);
96
+ const depKeys = Array.from(deps.keys());
97
+ expect(depKeys).toContain('b.ts');
98
+ expect(depKeys).toContain('a.ts');
99
+ expect(deps.size).toBe(2);
99
100
  });
100
101
  });
101
102
 
@@ -4,9 +4,11 @@ import { Severity } from '@aiready/core';
4
4
 
5
5
  describe('analyzeIssues', () => {
6
6
  const baseParams = {
7
- file: 'src/test.ts',
7
+ file: 'test.ts',
8
8
  importDepth: 2,
9
- contextBudget: 10000,
9
+ tokenCost: 1000,
10
+ contextBudget: 5000,
11
+
10
12
  cohesionScore: 0.8,
11
13
  fragmentationScore: 0.3,
12
14
  maxDepth: 5,