@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.
- package/.turbo/turbo-build.log +18 -18
- package/.turbo/turbo-format-check.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +408 -30
- package/.turbo/turbo-type-check.log +1 -1
- package/dist/chunk-2LEHY2GV.mjs +1287 -0
- package/dist/chunk-P2ZQGQAO.mjs +1282 -0
- package/dist/chunk-QGI23DBA.mjs +1282 -0
- package/dist/chunk-QTB4KYCX.mjs +1260 -0
- package/dist/chunk-RQ5BQLT6.mjs +102 -0
- package/dist/chunk-VYFHSGV6.mjs +1283 -0
- package/dist/chunk-WLXLBWDU.mjs +96 -0
- package/dist/chunk-XDYPMFCH.mjs +1250 -0
- package/dist/cli-action-332WE54N.mjs +95 -0
- package/dist/cli-action-7QXG7LHS.mjs +95 -0
- package/dist/cli-action-BIX6TYXF.mjs +95 -0
- package/dist/cli-action-BUGVCH44.mjs +95 -0
- package/dist/cli-action-RO24U52W.mjs +95 -0
- package/dist/cli-action-WAZ5KM6X.mjs +95 -0
- package/dist/cli-action-XDKINE2R.mjs +95 -0
- package/dist/cli-action-Y6VATXMV.mjs +95 -0
- package/dist/cli.js +75 -27
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +71 -25
- package/dist/index.mjs +3 -3
- package/dist/orchestrator-2KQNMO2L.mjs +10 -0
- package/dist/orchestrator-66ZVNOLR.mjs +10 -0
- package/dist/orchestrator-KM2OJPZD.mjs +10 -0
- package/dist/orchestrator-MKDZPRBA.mjs +10 -0
- package/dist/orchestrator-QSHWWBWS.mjs +10 -0
- package/dist/orchestrator-WFQPMNSD.mjs +10 -0
- package/dist/python-context-H2OLC5JN.mjs +162 -0
- package/dist/python-context-OBP7JD5P.mjs +162 -0
- package/package.json +13 -10
- package/src/__tests__/analyzer.test.ts +4 -3
- package/src/__tests__/issue-analyzer.test.ts +4 -2
- package/src/classify/file-classifiers.ts +14 -13
- package/src/cli-action.ts +6 -3
- package/src/graph-builder.ts +43 -8
- package/src/issue-analyzer.ts +19 -7
- package/src/orchestrator.ts +6 -4
- package/src/semantic/domain-inference.ts +1 -1
- package/src/types.ts +2 -0
- package/src/utils/dependency-graph-utils.ts +22 -13
- 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
|
-
|
|
173
|
-
|
|
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
|
-
`
|
|
177
|
+
`File is excessively large (${tokenCost.toLocaleString()} tokens)`
|
|
176
178
|
);
|
|
177
179
|
recommendations.push(
|
|
178
|
-
"Split into smaller modules
|
|
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
|
-
`
|
|
193
|
+
`Total context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
|
|
185
194
|
);
|
|
186
|
-
recommendations.push("
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
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
|
|
687
|
-
const
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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(
|
|
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-
|
|
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
|
-
|
|
215
|
-
|
|
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
|
-
`
|
|
219
|
+
`File is excessively large (${tokenCost.toLocaleString()} tokens)`
|
|
218
220
|
);
|
|
219
221
|
recommendations.push(
|
|
220
|
-
"Split into smaller modules
|
|
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
|
-
`
|
|
235
|
+
`Total context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
|
|
227
236
|
);
|
|
228
|
-
recommendations.push("
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
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
|
|
729
|
-
const
|
|
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
|
-
|
|
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.
|
|
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-
|
|
16
|
-
import "./chunk-
|
|
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-
|
|
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);
|