@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/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-test.log +336 -22
- package/dist/chunk-PDN74MG3.mjs +1834 -0
- package/dist/cli.js +38 -16
- package/dist/cli.mjs +1 -1
- package/dist/index.js +33 -11
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +2 -1
- package/src/graph-builder.ts +48 -2
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
|
|
47
|
-
void
|
|
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,
|
|
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,
|
|
135
|
+
targetDir = (0, import_path2.dirname)(targetDir);
|
|
136
136
|
}
|
|
137
137
|
const modulePath = parts.join("/");
|
|
138
138
|
const possiblePaths = [
|
|
139
|
-
(0,
|
|
140
|
-
(0,
|
|
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,
|
|
151
|
-
(0,
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
2354
|
-
const hasCDK = (0, import_fs2.existsSync)((0,
|
|
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
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
|
|
47
|
-
void
|
|
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,
|
|
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,
|
|
135
|
+
targetDir = (0, import_path2.dirname)(targetDir);
|
|
136
136
|
}
|
|
137
137
|
const modulePath = parts.join("/");
|
|
138
138
|
const possiblePaths = [
|
|
139
|
-
(0,
|
|
140
|
-
(0,
|
|
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,
|
|
151
|
-
(0,
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/context-analyzer",
|
|
3
|
-
"version": "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.
|
|
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('
|
|
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
|
|
package/src/graph-builder.ts
CHANGED
|
@@ -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
|
-
//
|
|
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(
|
|
160
|
+
edges.set(file, new Set(resolvedImports));
|
|
115
161
|
}
|
|
116
162
|
|
|
117
163
|
const graph: DependencyGraph = { nodes, edges };
|