@aiready/context-analyzer 0.3.7 → 0.4.0
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 +11 -11
- package/.turbo/turbo-test.log +3 -3
- package/README.md +15 -1
- package/dist/chunk-5N5DCJOV.mjs +583 -0
- package/dist/chunk-HDFXSEFW.mjs +605 -0
- package/dist/chunk-TPF75CNP.mjs +581 -0
- package/dist/cli.js +80 -2
- package/dist/cli.mjs +60 -4
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +70 -2
- package/dist/index.mjs +5 -3
- package/package.json +4 -2
- package/src/cli.ts +72 -2
- package/src/index.ts +102 -0
package/dist/cli.mjs
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
import {
|
|
3
3
|
analyzeContext,
|
|
4
4
|
generateSummary
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-HDFXSEFW.mjs";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
import chalk from "chalk";
|
|
10
|
-
import { writeFileSync } from "fs";
|
|
10
|
+
import { writeFileSync, existsSync, readFileSync } from "fs";
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
import { loadMergedConfig, handleJSONOutput, handleCLIError, getElapsedTime } from "@aiready/core";
|
|
13
|
+
import prompts from "prompts";
|
|
13
14
|
var program = new Command();
|
|
14
15
|
program.name("aiready-context").description("Analyze AI context window cost and code structure").version("0.1.0").addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings").argument("<directory>", "Directory to analyze").option("--max-depth <number>", "Maximum acceptable import depth").option(
|
|
15
16
|
"--max-context <number>",
|
|
@@ -24,7 +25,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
24
25
|
"-o, --output <format>",
|
|
25
26
|
"Output format: console, json, html",
|
|
26
27
|
"console"
|
|
27
|
-
).option("--output-file <path>", "Output file path (for json/html)").action(async (directory, options) => {
|
|
28
|
+
).option("--output-file <path>", "Output file path (for json/html)").option("--interactive", "Run interactive setup to suggest excludes and focus areas").action(async (directory, options) => {
|
|
28
29
|
console.log(chalk.blue("\u{1F50D} Analyzing context window costs...\n"));
|
|
29
30
|
const startTime = Date.now();
|
|
30
31
|
try {
|
|
@@ -39,7 +40,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
39
40
|
exclude: void 0,
|
|
40
41
|
maxResults: 10
|
|
41
42
|
};
|
|
42
|
-
|
|
43
|
+
let finalOptions = loadMergedConfig(directory, defaults, {
|
|
43
44
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
|
|
44
45
|
maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
|
|
45
46
|
minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : void 0,
|
|
@@ -50,6 +51,9 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
50
51
|
exclude: options.exclude?.split(","),
|
|
51
52
|
maxResults: options.maxResults ? parseInt(options.maxResults) : void 0
|
|
52
53
|
});
|
|
54
|
+
if (options.interactive) {
|
|
55
|
+
finalOptions = await runInteractiveSetup(directory, finalOptions);
|
|
56
|
+
}
|
|
53
57
|
const results = await analyzeContext(finalOptions);
|
|
54
58
|
const elapsedTime = getElapsedTime(startTime);
|
|
55
59
|
const summary = generateSummary(results);
|
|
@@ -384,3 +388,55 @@ function generateHTMLReport(summary, results) {
|
|
|
384
388
|
</body>
|
|
385
389
|
</html>`;
|
|
386
390
|
}
|
|
391
|
+
async function runInteractiveSetup(directory, current) {
|
|
392
|
+
console.log(chalk.yellow("\u{1F9ED} Interactive mode: let\u2019s tailor the analysis."));
|
|
393
|
+
const pkgPath = join(directory, "package.json");
|
|
394
|
+
let deps = {};
|
|
395
|
+
if (existsSync(pkgPath)) {
|
|
396
|
+
try {
|
|
397
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
398
|
+
deps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const hasNextJs = existsSync(join(directory, ".next")) || !!deps["next"];
|
|
403
|
+
const hasCDK = existsSync(join(directory, "cdk.out")) || !!deps["aws-cdk-lib"] || Object.keys(deps).some((d) => d.startsWith("@aws-cdk/"));
|
|
404
|
+
const recommendedExcludes = new Set(current.exclude || []);
|
|
405
|
+
if (hasNextJs && !Array.from(recommendedExcludes).some((p) => p.includes(".next"))) {
|
|
406
|
+
recommendedExcludes.add("**/.next/**");
|
|
407
|
+
}
|
|
408
|
+
if (hasCDK && !Array.from(recommendedExcludes).some((p) => p.includes("cdk.out"))) {
|
|
409
|
+
recommendedExcludes.add("**/cdk.out/**");
|
|
410
|
+
}
|
|
411
|
+
const { applyExcludes } = await prompts({
|
|
412
|
+
type: "toggle",
|
|
413
|
+
name: "applyExcludes",
|
|
414
|
+
message: `Detected ${hasNextJs ? "Next.js " : ""}${hasCDK ? "AWS CDK " : ""}frameworks. Apply recommended excludes?`,
|
|
415
|
+
initial: true,
|
|
416
|
+
active: "yes",
|
|
417
|
+
inactive: "no"
|
|
418
|
+
});
|
|
419
|
+
let nextOptions = { ...current };
|
|
420
|
+
if (applyExcludes) {
|
|
421
|
+
nextOptions.exclude = Array.from(recommendedExcludes);
|
|
422
|
+
}
|
|
423
|
+
const { focusArea } = await prompts({
|
|
424
|
+
type: "select",
|
|
425
|
+
name: "focusArea",
|
|
426
|
+
message: "Which areas to focus?",
|
|
427
|
+
choices: [
|
|
428
|
+
{ title: "Frontend (web app)", value: "frontend" },
|
|
429
|
+
{ title: "Backend (API/infra)", value: "backend" },
|
|
430
|
+
{ title: "Both", value: "both" }
|
|
431
|
+
],
|
|
432
|
+
initial: 2
|
|
433
|
+
});
|
|
434
|
+
if (focusArea === "frontend") {
|
|
435
|
+
nextOptions.include = ["**/*.{ts,tsx,js,jsx}"];
|
|
436
|
+
nextOptions.exclude = Array.from(/* @__PURE__ */ new Set([...nextOptions.exclude || [], "**/cdk.out/**", "**/infra/**", "**/server/**", "**/backend/**"]));
|
|
437
|
+
} else if (focusArea === "backend") {
|
|
438
|
+
nextOptions.include = ["**/api/**", "**/server/**", "**/backend/**", "**/infra/**", "**/*.{ts,js,py,java}"];
|
|
439
|
+
}
|
|
440
|
+
console.log(chalk.green("\u2713 Interactive configuration applied."));
|
|
441
|
+
return nextOptions;
|
|
442
|
+
}
|
package/dist/index.d.mts
CHANGED
|
@@ -67,6 +67,10 @@ interface ContextSummary {
|
|
|
67
67
|
}>;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Generate smart defaults for context analysis based on repository size
|
|
72
|
+
*/
|
|
73
|
+
declare function getSmartDefaults(directory: string, userOptions: Partial<ContextAnalyzerOptions>): Promise<ContextAnalyzerOptions>;
|
|
70
74
|
/**
|
|
71
75
|
* Analyze AI context window cost for a codebase
|
|
72
76
|
*/
|
|
@@ -76,4 +80,4 @@ declare function analyzeContext(options: ContextAnalyzerOptions): Promise<Contex
|
|
|
76
80
|
*/
|
|
77
81
|
declare function generateSummary(results: ContextAnalysisResult[]): ContextSummary;
|
|
78
82
|
|
|
79
|
-
export { type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type ModuleCluster, analyzeContext, generateSummary };
|
|
83
|
+
export { type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type ModuleCluster, analyzeContext, generateSummary, getSmartDefaults };
|
package/dist/index.d.ts
CHANGED
|
@@ -67,6 +67,10 @@ interface ContextSummary {
|
|
|
67
67
|
}>;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Generate smart defaults for context analysis based on repository size
|
|
72
|
+
*/
|
|
73
|
+
declare function getSmartDefaults(directory: string, userOptions: Partial<ContextAnalyzerOptions>): Promise<ContextAnalyzerOptions>;
|
|
70
74
|
/**
|
|
71
75
|
* Analyze AI context window cost for a codebase
|
|
72
76
|
*/
|
|
@@ -76,4 +80,4 @@ declare function analyzeContext(options: ContextAnalyzerOptions): Promise<Contex
|
|
|
76
80
|
*/
|
|
77
81
|
declare function generateSummary(results: ContextAnalysisResult[]): ContextSummary;
|
|
78
82
|
|
|
79
|
-
export { type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type ModuleCluster, analyzeContext, generateSummary };
|
|
83
|
+
export { type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type ModuleCluster, analyzeContext, generateSummary, getSmartDefaults };
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
analyzeContext: () => analyzeContext,
|
|
24
|
-
generateSummary: () => generateSummary
|
|
24
|
+
generateSummary: () => generateSummary,
|
|
25
|
+
getSmartDefaults: () => getSmartDefaults
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
27
28
|
var import_core2 = require("@aiready/core");
|
|
@@ -299,6 +300,50 @@ function generateConsolidationPlan(domain, files, targetFiles) {
|
|
|
299
300
|
}
|
|
300
301
|
|
|
301
302
|
// src/index.ts
|
|
303
|
+
async function getSmartDefaults(directory, userOptions) {
|
|
304
|
+
const files = await (0, import_core2.scanFiles)({
|
|
305
|
+
rootDir: directory,
|
|
306
|
+
include: userOptions.include,
|
|
307
|
+
exclude: userOptions.exclude
|
|
308
|
+
});
|
|
309
|
+
const estimatedBlocks = files.length;
|
|
310
|
+
let maxDepth;
|
|
311
|
+
let maxContextBudget;
|
|
312
|
+
let minCohesion;
|
|
313
|
+
let maxFragmentation;
|
|
314
|
+
if (estimatedBlocks < 100) {
|
|
315
|
+
maxDepth = 3;
|
|
316
|
+
maxContextBudget = 5e3;
|
|
317
|
+
minCohesion = 0.7;
|
|
318
|
+
maxFragmentation = 0.3;
|
|
319
|
+
} else if (estimatedBlocks < 500) {
|
|
320
|
+
maxDepth = 4;
|
|
321
|
+
maxContextBudget = 8e3;
|
|
322
|
+
minCohesion = 0.65;
|
|
323
|
+
maxFragmentation = 0.4;
|
|
324
|
+
} else if (estimatedBlocks < 2e3) {
|
|
325
|
+
maxDepth = 5;
|
|
326
|
+
maxContextBudget = 12e3;
|
|
327
|
+
minCohesion = 0.6;
|
|
328
|
+
maxFragmentation = 0.5;
|
|
329
|
+
} else {
|
|
330
|
+
maxDepth = 6;
|
|
331
|
+
maxContextBudget = 2e4;
|
|
332
|
+
minCohesion = 0.55;
|
|
333
|
+
maxFragmentation = 0.6;
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
maxDepth,
|
|
337
|
+
maxContextBudget,
|
|
338
|
+
minCohesion,
|
|
339
|
+
maxFragmentation,
|
|
340
|
+
focus: "all",
|
|
341
|
+
includeNodeModules: false,
|
|
342
|
+
rootDir: userOptions.rootDir || directory,
|
|
343
|
+
include: userOptions.include,
|
|
344
|
+
exclude: userOptions.exclude
|
|
345
|
+
};
|
|
346
|
+
}
|
|
302
347
|
async function analyzeContext(options) {
|
|
303
348
|
const {
|
|
304
349
|
maxDepth = 5,
|
|
@@ -554,10 +599,33 @@ function analyzeIssues(params) {
|
|
|
554
599
|
issues.push("No significant issues detected");
|
|
555
600
|
recommendations.push("File is well-structured for AI context usage");
|
|
556
601
|
}
|
|
602
|
+
if (isBuildArtifact(file)) {
|
|
603
|
+
issues.push("Detected build artifact (bundled/output file)");
|
|
604
|
+
recommendations.push("Exclude build outputs (e.g., cdk.out, dist, build, .next) from analysis");
|
|
605
|
+
severity = downgradeSeverity(severity);
|
|
606
|
+
potentialSavings = 0;
|
|
607
|
+
}
|
|
557
608
|
return { severity, issues, recommendations, potentialSavings: Math.floor(potentialSavings) };
|
|
558
609
|
}
|
|
610
|
+
function isBuildArtifact(filePath) {
|
|
611
|
+
const lower = filePath.toLowerCase();
|
|
612
|
+
return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/output/") || lower.includes("/cdk.out/") || lower.includes("/.next/") || /\/asset\.[^/]+\//.test(lower);
|
|
613
|
+
}
|
|
614
|
+
function downgradeSeverity(s) {
|
|
615
|
+
switch (s) {
|
|
616
|
+
case "critical":
|
|
617
|
+
return "minor";
|
|
618
|
+
case "major":
|
|
619
|
+
return "minor";
|
|
620
|
+
case "minor":
|
|
621
|
+
return "info";
|
|
622
|
+
default:
|
|
623
|
+
return "info";
|
|
624
|
+
}
|
|
625
|
+
}
|
|
559
626
|
// Annotate the CommonJS export names for ESM import in node:
|
|
560
627
|
0 && (module.exports = {
|
|
561
628
|
analyzeContext,
|
|
562
|
-
generateSummary
|
|
629
|
+
generateSummary,
|
|
630
|
+
getSmartDefaults
|
|
563
631
|
});
|
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.4.0",
|
|
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,10 +49,12 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"commander": "^12.1.0",
|
|
51
51
|
"chalk": "^5.3.0",
|
|
52
|
-
"
|
|
52
|
+
"prompts": "^2.4.2",
|
|
53
|
+
"@aiready/core": "0.5.0"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@types/node": "^22.10.2",
|
|
57
|
+
"@types/prompts": "^2.4.9",
|
|
56
58
|
"tsup": "^8.3.5",
|
|
57
59
|
"typescript": "^5.7.3",
|
|
58
60
|
"vitest": "^2.1.9",
|
package/src/cli.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { analyzeContext, generateSummary } from './index';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
-
import { writeFileSync } from 'fs';
|
|
6
|
+
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { loadMergedConfig, handleJSONOutput, handleCLIError, getElapsedTime } from '@aiready/core';
|
|
9
|
+
import prompts from 'prompts';
|
|
9
10
|
|
|
10
11
|
const program = new Command();
|
|
11
12
|
|
|
@@ -39,6 +40,7 @@ program
|
|
|
39
40
|
'console'
|
|
40
41
|
)
|
|
41
42
|
.option('--output-file <path>', 'Output file path (for json/html)')
|
|
43
|
+
.option('--interactive', 'Run interactive setup to suggest excludes and focus areas')
|
|
42
44
|
.action(async (directory, options) => {
|
|
43
45
|
console.log(chalk.blue('🔍 Analyzing context window costs...\n'));
|
|
44
46
|
|
|
@@ -59,7 +61,7 @@ program
|
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
// Load and merge config with CLI options
|
|
62
|
-
|
|
64
|
+
let finalOptions = loadMergedConfig(directory, defaults, {
|
|
63
65
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : undefined,
|
|
64
66
|
maxContextBudget: options.maxContext ? parseInt(options.maxContext) : undefined,
|
|
65
67
|
minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : undefined,
|
|
@@ -71,6 +73,11 @@ program
|
|
|
71
73
|
maxResults: options.maxResults ? parseInt(options.maxResults) : undefined,
|
|
72
74
|
}) as any;
|
|
73
75
|
|
|
76
|
+
// Optional: interactive setup to refine options for first-time users
|
|
77
|
+
if (options.interactive) {
|
|
78
|
+
finalOptions = await runInteractiveSetup(directory, finalOptions);
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
const results = await analyzeContext(finalOptions);
|
|
75
82
|
|
|
76
83
|
const elapsedTime = getElapsedTime(startTime);
|
|
@@ -455,3 +462,66 @@ function generateHTMLReport(
|
|
|
455
462
|
</body>
|
|
456
463
|
</html>`;
|
|
457
464
|
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Interactive setup: detect common frameworks and suggest excludes & focus areas
|
|
468
|
+
*/
|
|
469
|
+
async function runInteractiveSetup(directory: string, current: any): Promise<any> {
|
|
470
|
+
console.log(chalk.yellow('🧭 Interactive mode: let’s tailor the analysis.'));
|
|
471
|
+
|
|
472
|
+
const pkgPath = join(directory, 'package.json');
|
|
473
|
+
let deps: Record<string, string> = {};
|
|
474
|
+
if (existsSync(pkgPath)) {
|
|
475
|
+
try {
|
|
476
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
477
|
+
deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
478
|
+
} catch {}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const hasNextJs = existsSync(join(directory, '.next')) || !!deps['next'];
|
|
482
|
+
const hasCDK = existsSync(join(directory, 'cdk.out')) || !!deps['aws-cdk-lib'] || Object.keys(deps).some(d => d.startsWith('@aws-cdk/'));
|
|
483
|
+
|
|
484
|
+
const recommendedExcludes = new Set<string>(current.exclude || []);
|
|
485
|
+
if (hasNextJs && !Array.from(recommendedExcludes).some((p) => p.includes('.next'))) {
|
|
486
|
+
recommendedExcludes.add('**/.next/**');
|
|
487
|
+
}
|
|
488
|
+
if (hasCDK && !Array.from(recommendedExcludes).some((p) => p.includes('cdk.out'))) {
|
|
489
|
+
recommendedExcludes.add('**/cdk.out/**');
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const { applyExcludes } = await prompts({
|
|
493
|
+
type: 'toggle',
|
|
494
|
+
name: 'applyExcludes',
|
|
495
|
+
message: `Detected ${hasNextJs ? 'Next.js ' : ''}${hasCDK ? 'AWS CDK ' : ''}frameworks. Apply recommended excludes?`,
|
|
496
|
+
initial: true,
|
|
497
|
+
active: 'yes',
|
|
498
|
+
inactive: 'no',
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
let nextOptions = { ...current };
|
|
502
|
+
if (applyExcludes) {
|
|
503
|
+
nextOptions.exclude = Array.from(recommendedExcludes);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const { focusArea } = await prompts({
|
|
507
|
+
type: 'select',
|
|
508
|
+
name: 'focusArea',
|
|
509
|
+
message: 'Which areas to focus?',
|
|
510
|
+
choices: [
|
|
511
|
+
{ title: 'Frontend (web app)', value: 'frontend' },
|
|
512
|
+
{ title: 'Backend (API/infra)', value: 'backend' },
|
|
513
|
+
{ title: 'Both', value: 'both' },
|
|
514
|
+
],
|
|
515
|
+
initial: 2,
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
if (focusArea === 'frontend') {
|
|
519
|
+
nextOptions.include = ['**/*.{ts,tsx,js,jsx}'];
|
|
520
|
+
nextOptions.exclude = Array.from(new Set([...(nextOptions.exclude || []), '**/cdk.out/**', '**/infra/**', '**/server/**', '**/backend/**']));
|
|
521
|
+
} else if (focusArea === 'backend') {
|
|
522
|
+
nextOptions.include = ['**/api/**', '**/server/**', '**/backend/**', '**/infra/**', '**/*.{ts,js,py,java}'];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
console.log(chalk.green('✓ Interactive configuration applied.'));
|
|
526
|
+
return nextOptions;
|
|
527
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,67 @@ import type {
|
|
|
19
19
|
|
|
20
20
|
export type { ContextAnalyzerOptions, ContextAnalysisResult, ContextSummary, ModuleCluster };
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Generate smart defaults for context analysis based on repository size
|
|
24
|
+
*/
|
|
25
|
+
async function getSmartDefaults(
|
|
26
|
+
directory: string,
|
|
27
|
+
userOptions: Partial<ContextAnalyzerOptions>
|
|
28
|
+
): Promise<ContextAnalyzerOptions> {
|
|
29
|
+
// Estimate repository size by scanning files
|
|
30
|
+
const files = await scanFiles({
|
|
31
|
+
rootDir: directory,
|
|
32
|
+
include: userOptions.include,
|
|
33
|
+
exclude: userOptions.exclude,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const estimatedBlocks = files.length;
|
|
37
|
+
|
|
38
|
+
// Smart defaults based on repository size
|
|
39
|
+
let maxDepth: number;
|
|
40
|
+
let maxContextBudget: number;
|
|
41
|
+
let minCohesion: number;
|
|
42
|
+
let maxFragmentation: number;
|
|
43
|
+
|
|
44
|
+
if (estimatedBlocks < 100) {
|
|
45
|
+
// Small project
|
|
46
|
+
maxDepth = 3;
|
|
47
|
+
maxContextBudget = 5000;
|
|
48
|
+
minCohesion = 0.7;
|
|
49
|
+
maxFragmentation = 0.3;
|
|
50
|
+
} else if (estimatedBlocks < 500) {
|
|
51
|
+
// Medium project
|
|
52
|
+
maxDepth = 4;
|
|
53
|
+
maxContextBudget = 8000;
|
|
54
|
+
minCohesion = 0.65;
|
|
55
|
+
maxFragmentation = 0.4;
|
|
56
|
+
} else if (estimatedBlocks < 2000) {
|
|
57
|
+
// Large project
|
|
58
|
+
maxDepth = 5;
|
|
59
|
+
maxContextBudget = 12000;
|
|
60
|
+
minCohesion = 0.6;
|
|
61
|
+
maxFragmentation = 0.5;
|
|
62
|
+
} else {
|
|
63
|
+
// Enterprise project
|
|
64
|
+
maxDepth = 6;
|
|
65
|
+
maxContextBudget = 20000;
|
|
66
|
+
minCohesion = 0.55;
|
|
67
|
+
maxFragmentation = 0.6;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
maxDepth,
|
|
72
|
+
maxContextBudget,
|
|
73
|
+
minCohesion,
|
|
74
|
+
maxFragmentation,
|
|
75
|
+
focus: 'all',
|
|
76
|
+
includeNodeModules: false,
|
|
77
|
+
rootDir: userOptions.rootDir || directory,
|
|
78
|
+
include: userOptions.include,
|
|
79
|
+
exclude: userOptions.exclude,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
22
83
|
/**
|
|
23
84
|
* Analyze AI context window cost for a codebase
|
|
24
85
|
*/
|
|
@@ -392,5 +453,46 @@ function analyzeIssues(params: {
|
|
|
392
453
|
recommendations.push('File is well-structured for AI context usage');
|
|
393
454
|
}
|
|
394
455
|
|
|
456
|
+
// Detect build artifacts and downgrade severity to reduce noise
|
|
457
|
+
if (isBuildArtifact(file)) {
|
|
458
|
+
issues.push('Detected build artifact (bundled/output file)');
|
|
459
|
+
recommendations.push('Exclude build outputs (e.g., cdk.out, dist, build, .next) from analysis');
|
|
460
|
+
severity = downgradeSeverity(severity);
|
|
461
|
+
// Build artifacts do not represent actionable savings
|
|
462
|
+
potentialSavings = 0;
|
|
463
|
+
}
|
|
464
|
+
|
|
395
465
|
return { severity, issues, recommendations, potentialSavings: Math.floor(potentialSavings) };
|
|
396
466
|
}
|
|
467
|
+
|
|
468
|
+
export { getSmartDefaults };
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Heuristic: identify common build artifact paths
|
|
472
|
+
*/
|
|
473
|
+
function isBuildArtifact(filePath: string): boolean {
|
|
474
|
+
const lower = filePath.toLowerCase();
|
|
475
|
+
return (
|
|
476
|
+
lower.includes('/node_modules/') ||
|
|
477
|
+
lower.includes('/dist/') ||
|
|
478
|
+
lower.includes('/build/') ||
|
|
479
|
+
lower.includes('/out/') ||
|
|
480
|
+
lower.includes('/output/') ||
|
|
481
|
+
lower.includes('/cdk.out/') ||
|
|
482
|
+
lower.includes('/.next/') ||
|
|
483
|
+
/\/asset\.[^/]+\//.test(lower) // e.g., cdk.out/asset.*
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function downgradeSeverity(s: ContextAnalysisResult['severity']): ContextAnalysisResult['severity'] {
|
|
488
|
+
switch (s) {
|
|
489
|
+
case 'critical':
|
|
490
|
+
return 'minor';
|
|
491
|
+
case 'major':
|
|
492
|
+
return 'minor';
|
|
493
|
+
case 'minor':
|
|
494
|
+
return 'info';
|
|
495
|
+
default:
|
|
496
|
+
return 'info';
|
|
497
|
+
}
|
|
498
|
+
}
|