@aiready/context-analyzer 0.20.0 → 0.21.1

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/cli.js CHANGED
@@ -43,8 +43,8 @@ async function analyzePythonContext(files, rootDir) {
43
43
  return results;
44
44
  }
45
45
  const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
46
- void import_path.relative;
47
- void import_path.join;
46
+ void import_path2.relative;
47
+ void import_path2.join;
48
48
  const dependencyGraph = await buildPythonDependencyGraph(
49
49
  pythonFiles,
50
50
  rootDir
@@ -122,7 +122,7 @@ async function buildPythonDependencyGraph(files, rootDir) {
122
122
  return graph;
123
123
  }
124
124
  function resolvePythonImport(fromFile, importPath, rootDir) {
125
- const dir = (0, import_path.dirname)(fromFile);
125
+ const dir = (0, import_path2.dirname)(fromFile);
126
126
  if (importPath.startsWith(".")) {
127
127
  const parts = importPath.split(".");
128
128
  let upCount = 0;
@@ -132,12 +132,12 @@ function resolvePythonImport(fromFile, importPath, rootDir) {
132
132
  }
133
133
  let targetDir = dir;
134
134
  for (let i = 0; i < upCount - 1; i++) {
135
- targetDir = (0, import_path.dirname)(targetDir);
135
+ targetDir = (0, import_path2.dirname)(targetDir);
136
136
  }
137
137
  const modulePath = parts.join("/");
138
138
  const possiblePaths = [
139
- (0, import_path.resolve)(targetDir, `${modulePath}.py`),
140
- (0, import_path.resolve)(targetDir, modulePath, "__init__.py")
139
+ (0, import_path2.resolve)(targetDir, `${modulePath}.py`),
140
+ (0, import_path2.resolve)(targetDir, modulePath, "__init__.py")
141
141
  ];
142
142
  for (const path of possiblePaths) {
143
143
  if (import_fs.default.existsSync(path)) {
@@ -147,8 +147,8 @@ function resolvePythonImport(fromFile, importPath, rootDir) {
147
147
  } else {
148
148
  const modulePath = importPath.replace(/\./g, "/");
149
149
  const possiblePaths = [
150
- (0, import_path.resolve)(rootDir, `${modulePath}.py`),
151
- (0, import_path.resolve)(rootDir, modulePath, "__init__.py")
150
+ (0, import_path2.resolve)(rootDir, `${modulePath}.py`),
151
+ (0, import_path2.resolve)(rootDir, modulePath, "__init__.py")
152
152
  ];
153
153
  for (const path of possiblePaths) {
154
154
  if (import_fs.default.existsSync(path)) {
@@ -231,12 +231,12 @@ function detectCircularDependencies2(file, dependencyGraph) {
231
231
  dfs(file, []);
232
232
  return [...new Set(circular)];
233
233
  }
234
- var import_core5, import_path, import_fs;
234
+ var import_core5, import_path2, import_fs;
235
235
  var init_python_context = __esm({
236
236
  "src/analyzers/python-context.ts"() {
237
237
  "use strict";
238
238
  import_core5 = require("@aiready/core");
239
- import_path = require("path");
239
+ import_path2 = require("path");
240
240
  import_fs = __toESM(require("fs"));
241
241
  }
242
242
  });
@@ -750,6 +750,26 @@ function generateSummary(results, options) {
750
750
 
751
751
  // src/graph-builder.ts
752
752
  var import_core4 = require("@aiready/core");
753
+ var import_path = require("path");
754
+ function resolveImport(source, importingFile, allFiles) {
755
+ if (!source.startsWith(".") && !source.startsWith("/")) {
756
+ if (allFiles.has(source)) return source;
757
+ return null;
758
+ }
759
+ const dir = (0, import_path.dirname)(importingFile);
760
+ const absolutePath = (0, import_path.normalize)((0, import_path.join)(dir, source));
761
+ if (allFiles.has(absolutePath)) return absolutePath;
762
+ const extensions = [".ts", ".tsx", ".js", ".jsx"];
763
+ for (const ext of extensions) {
764
+ const withExt = absolutePath + ext;
765
+ if (allFiles.has(withExt)) return withExt;
766
+ }
767
+ for (const ext of extensions) {
768
+ const indexFile = (0, import_path.normalize)((0, import_path.join)(absolutePath, `index${ext}`));
769
+ if (allFiles.has(indexFile)) return indexFile;
770
+ }
771
+ return null;
772
+ }
753
773
  function extractDomainKeywordsFromPaths(files) {
754
774
  const folderNames = /* @__PURE__ */ new Set();
755
775
  for (const { file } of files) {
@@ -800,8 +820,10 @@ function buildDependencyGraph(files, options) {
800
820
  const nodes = /* @__PURE__ */ new Map();
801
821
  const edges = /* @__PURE__ */ new Map();
802
822
  const autoDetectedKeywords = options?.domainKeywords ?? extractDomainKeywordsFromPaths(files);
823
+ const allFilePaths = new Set(files.map((f) => f.file));
803
824
  for (const { file, content } of files) {
804
825
  const { imports: astImports } = (0, import_core4.parseFileExports)(content, file);
826
+ const resolvedImports = astImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
805
827
  const importSources = astImports.map((i) => i.source);
806
828
  const exports2 = extractExportsWithAST(
807
829
  content,
@@ -818,7 +840,7 @@ function buildDependencyGraph(files, options) {
818
840
  tokenCost,
819
841
  linesOfCode
820
842
  });
821
- edges.set(file, new Set(importSources));
843
+ edges.set(file, new Set(resolvedImports));
822
844
  }
823
845
  const graph = { nodes, edges };
824
846
  const coUsageMatrix = buildCoUsageMatrix(graph);
@@ -1833,7 +1855,7 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
1833
1855
  // src/cli.ts
1834
1856
  var import_chalk = __toESM(require("chalk"));
1835
1857
  var import_fs2 = require("fs");
1836
- var import_path2 = require("path");
1858
+ var import_path3 = require("path");
1837
1859
  var import_core11 = require("@aiready/core");
1838
1860
  var import_prompts = __toESM(require("prompts"));
1839
1861
  var program = new import_commander.Command();
@@ -1918,7 +1940,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
1918
1940
  `context-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.html`,
1919
1941
  directory
1920
1942
  );
1921
- const dir = (0, import_path2.dirname)(outputPath);
1943
+ const dir = (0, import_path3.dirname)(outputPath);
1922
1944
  if (!(0, import_fs2.existsSync)(dir)) {
1923
1945
  (0, import_fs2.mkdirSync)(dir, { recursive: true });
1924
1946
  }
@@ -2340,7 +2362,7 @@ function generateHTMLReport(summary, results) {
2340
2362
  }
2341
2363
  async function runInteractiveSetup(directory, current) {
2342
2364
  console.log(import_chalk.default.yellow("\u{1F9ED} Interactive mode: let\u2019s tailor the analysis."));
2343
- const pkgPath = (0, import_path2.join)(directory, "package.json");
2365
+ const pkgPath = (0, import_path3.join)(directory, "package.json");
2344
2366
  let deps = {};
2345
2367
  if ((0, import_fs2.existsSync)(pkgPath)) {
2346
2368
  try {
@@ -2350,8 +2372,8 @@ async function runInteractiveSetup(directory, current) {
2350
2372
  void e;
2351
2373
  }
2352
2374
  }
2353
- const hasNextJs = (0, import_fs2.existsSync)((0, import_path2.join)(directory, ".next")) || !!deps["next"];
2354
- const hasCDK = (0, import_fs2.existsSync)((0, import_path2.join)(directory, "cdk.out")) || !!deps["aws-cdk-lib"] || Object.keys(deps).some((d) => d.startsWith("@aws-cdk/"));
2375
+ const hasNextJs = (0, import_fs2.existsSync)((0, import_path3.join)(directory, ".next")) || !!deps["next"];
2376
+ const hasCDK = (0, import_fs2.existsSync)((0, import_path3.join)(directory, "cdk.out")) || !!deps["aws-cdk-lib"] || Object.keys(deps).some((d) => d.startsWith("@aws-cdk/"));
2355
2377
  const recommendedExcludes = new Set(current.exclude || []);
2356
2378
  if (hasNextJs && !Array.from(recommendedExcludes).some((p) => p.includes(".next"))) {
2357
2379
  recommendedExcludes.add("**/.next/**");
package/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  analyzeContext,
4
4
  generateSummary
5
- } from "./chunk-CCBNKQYB.mjs";
5
+ } from "./chunk-PDN74MG3.mjs";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
package/dist/index.js CHANGED
@@ -43,8 +43,8 @@ async function analyzePythonContext(files, rootDir) {
43
43
  return results;
44
44
  }
45
45
  const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
46
- void import_path.relative;
47
- void import_path.join;
46
+ void import_path2.relative;
47
+ void import_path2.join;
48
48
  const dependencyGraph = await buildPythonDependencyGraph(
49
49
  pythonFiles,
50
50
  rootDir
@@ -122,7 +122,7 @@ async function buildPythonDependencyGraph(files, rootDir) {
122
122
  return graph;
123
123
  }
124
124
  function resolvePythonImport(fromFile, importPath, rootDir) {
125
- const dir = (0, import_path.dirname)(fromFile);
125
+ const dir = (0, import_path2.dirname)(fromFile);
126
126
  if (importPath.startsWith(".")) {
127
127
  const parts = importPath.split(".");
128
128
  let upCount = 0;
@@ -132,12 +132,12 @@ function resolvePythonImport(fromFile, importPath, rootDir) {
132
132
  }
133
133
  let targetDir = dir;
134
134
  for (let i = 0; i < upCount - 1; i++) {
135
- targetDir = (0, import_path.dirname)(targetDir);
135
+ targetDir = (0, import_path2.dirname)(targetDir);
136
136
  }
137
137
  const modulePath = parts.join("/");
138
138
  const possiblePaths = [
139
- (0, import_path.resolve)(targetDir, `${modulePath}.py`),
140
- (0, import_path.resolve)(targetDir, modulePath, "__init__.py")
139
+ (0, import_path2.resolve)(targetDir, `${modulePath}.py`),
140
+ (0, import_path2.resolve)(targetDir, modulePath, "__init__.py")
141
141
  ];
142
142
  for (const path of possiblePaths) {
143
143
  if (import_fs.default.existsSync(path)) {
@@ -147,8 +147,8 @@ function resolvePythonImport(fromFile, importPath, rootDir) {
147
147
  } else {
148
148
  const modulePath = importPath.replace(/\./g, "/");
149
149
  const possiblePaths = [
150
- (0, import_path.resolve)(rootDir, `${modulePath}.py`),
151
- (0, import_path.resolve)(rootDir, modulePath, "__init__.py")
150
+ (0, import_path2.resolve)(rootDir, `${modulePath}.py`),
151
+ (0, import_path2.resolve)(rootDir, modulePath, "__init__.py")
152
152
  ];
153
153
  for (const path of possiblePaths) {
154
154
  if (import_fs.default.existsSync(path)) {
@@ -231,12 +231,12 @@ function detectCircularDependencies2(file, dependencyGraph) {
231
231
  dfs(file, []);
232
232
  return [...new Set(circular)];
233
233
  }
234
- var import_core5, import_path, import_fs;
234
+ var import_core5, import_path2, import_fs;
235
235
  var init_python_context = __esm({
236
236
  "src/analyzers/python-context.ts"() {
237
237
  "use strict";
238
238
  import_core5 = require("@aiready/core");
239
- import_path = require("path");
239
+ import_path2 = require("path");
240
240
  import_fs = __toESM(require("fs"));
241
241
  }
242
242
  });
@@ -866,6 +866,26 @@ function generateSummary(results, options) {
866
866
 
867
867
  // src/graph-builder.ts
868
868
  var import_core4 = require("@aiready/core");
869
+ var import_path = require("path");
870
+ function resolveImport(source, importingFile, allFiles) {
871
+ if (!source.startsWith(".") && !source.startsWith("/")) {
872
+ if (allFiles.has(source)) return source;
873
+ return null;
874
+ }
875
+ const dir = (0, import_path.dirname)(importingFile);
876
+ const absolutePath = (0, import_path.normalize)((0, import_path.join)(dir, source));
877
+ if (allFiles.has(absolutePath)) return absolutePath;
878
+ const extensions = [".ts", ".tsx", ".js", ".jsx"];
879
+ for (const ext of extensions) {
880
+ const withExt = absolutePath + ext;
881
+ if (allFiles.has(withExt)) return withExt;
882
+ }
883
+ for (const ext of extensions) {
884
+ const indexFile = (0, import_path.normalize)((0, import_path.join)(absolutePath, `index${ext}`));
885
+ if (allFiles.has(indexFile)) return indexFile;
886
+ }
887
+ return null;
888
+ }
869
889
  function extractDomainKeywordsFromPaths(files) {
870
890
  const folderNames = /* @__PURE__ */ new Set();
871
891
  for (const { file } of files) {
@@ -916,8 +936,10 @@ function buildDependencyGraph(files, options) {
916
936
  const nodes = /* @__PURE__ */ new Map();
917
937
  const edges = /* @__PURE__ */ new Map();
918
938
  const autoDetectedKeywords = options?.domainKeywords ?? extractDomainKeywordsFromPaths(files);
939
+ const allFilePaths = new Set(files.map((f) => f.file));
919
940
  for (const { file, content } of files) {
920
941
  const { imports: astImports } = (0, import_core4.parseFileExports)(content, file);
942
+ const resolvedImports = astImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
921
943
  const importSources = astImports.map((i) => i.source);
922
944
  const exports2 = extractExportsWithAST(
923
945
  content,
@@ -934,7 +956,7 @@ function buildDependencyGraph(files, options) {
934
956
  tokenCost,
935
957
  linesOfCode
936
958
  });
937
- edges.set(file, new Set(importSources));
959
+ edges.set(file, new Set(resolvedImports));
938
960
  }
939
961
  const graph = { nodes, edges };
940
962
  const coUsageMatrix = buildCoUsageMatrix(graph);
package/dist/index.mjs CHANGED
@@ -44,7 +44,7 @@ import {
44
44
  isTypeDefinition,
45
45
  isUtilityModule,
46
46
  mapScoreToRating
47
- } from "./chunk-CCBNKQYB.mjs";
47
+ } from "./chunk-PDN74MG3.mjs";
48
48
  export {
49
49
  Classification,
50
50
  ContextAnalyzerProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/context-analyzer",
3
- "version": "0.20.0",
3
+ "version": "0.21.1",
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.22.0"
52
+ "@aiready/core": "0.23.1"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^24.0.0",
@@ -30,7 +30,7 @@ export const b = a + 1;
30
30
  const graph = buildDependencyGraph(files);
31
31
 
32
32
  expect(graph.nodes.size).toBe(2);
33
- expect(graph.edges.get('b.ts')?.has('./a')).toBe(true);
33
+ expect(graph.edges.get('b.ts')?.has('a.ts')).toBe(true);
34
34
  });
35
35
  });
36
36
 
@@ -72,6 +72,7 @@ describe('calculateImportDepth', () => {
72
72
  // Should not infinite loop
73
73
  const depth = calculateImportDepth('a.ts', graph);
74
74
  expect(depth).toBeGreaterThanOrEqual(0);
75
+ expect(graph.edges.get('a.ts')?.has('b.ts')).toBe(true);
75
76
  });
76
77
  });
77
78
 
@@ -6,12 +6,50 @@ import {
6
6
  inferDomainFromSemantics,
7
7
  } from './semantic-analysis';
8
8
  import { extractExportsWithAST } from './ast-utils';
9
+ import { join, dirname, normalize } from 'path';
9
10
 
10
11
  interface FileContent {
11
12
  file: string;
12
13
  content: string;
13
14
  }
14
15
 
16
+ /**
17
+ * Resolve an import source to its absolute path considering the importing file's location
18
+ */
19
+ function resolveImport(
20
+ source: string,
21
+ importingFile: string,
22
+ allFiles: Set<string>
23
+ ): string | null {
24
+ // If it's not a relative import, we treat it as an external dependency for now
25
+ // (unless it's an absolute path that exists in our set)
26
+ if (!source.startsWith('.') && !source.startsWith('/')) {
27
+ if (allFiles.has(source)) return source;
28
+ return null;
29
+ }
30
+
31
+ const dir = dirname(importingFile);
32
+ const absolutePath = normalize(join(dir, source));
33
+
34
+ // Try exact match
35
+ if (allFiles.has(absolutePath)) return absolutePath;
36
+
37
+ // Try common extensions
38
+ const extensions = ['.ts', '.tsx', '.js', '.jsx'];
39
+ for (const ext of extensions) {
40
+ const withExt = absolutePath + ext;
41
+ if (allFiles.has(withExt)) return withExt;
42
+ }
43
+
44
+ // Try directory index
45
+ for (const ext of extensions) {
46
+ const indexFile = normalize(join(absolutePath, `index${ext}`));
47
+ if (allFiles.has(indexFile)) return indexFile;
48
+ }
49
+
50
+ return null;
51
+ }
52
+
15
53
  /**
16
54
  * Auto-detect domain keywords from workspace folder structure
17
55
  */
@@ -88,12 +126,20 @@ export function buildDependencyGraph(
88
126
  const autoDetectedKeywords =
89
127
  options?.domainKeywords ?? extractDomainKeywordsFromPaths(files);
90
128
 
129
+ const allFilePaths = new Set(files.map((f) => f.file));
130
+
91
131
  for (const { file, content } of files) {
92
132
  // 1. Get high-fidelity AST-based imports & exports
93
133
  const { imports: astImports } = parseFileExports(content, file);
134
+
135
+ // 2. Resolve imports to absolute paths in the graph
136
+ const resolvedImports = astImports
137
+ .map((i) => resolveImport(i.source, file, allFilePaths))
138
+ .filter((path): path is string => path !== null);
139
+
94
140
  const importSources = astImports.map((i) => i.source);
95
141
 
96
- // 2. Wrap with platform-specific metadata (v0.11+)
142
+ // 3. Wrap with platform-specific metadata (v0.11+)
97
143
  const exports = extractExportsWithAST(
98
144
  content,
99
145
  file,
@@ -111,7 +157,7 @@ export function buildDependencyGraph(
111
157
  tokenCost,
112
158
  linesOfCode,
113
159
  });
114
- edges.set(file, new Set(importSources));
160
+ edges.set(file, new Set(resolvedImports));
115
161
  }
116
162
 
117
163
  const graph: DependencyGraph = { nodes, edges };