@aiready/core 0.23.7 → 0.23.9
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/chunk-5SHLHMH7.mjs +760 -0
- package/dist/chunk-CGOS2J6T.mjs +807 -0
- package/dist/chunk-TJXR2CHZ.mjs +799 -0
- package/dist/client-BEoUYNLp.d.mts +1191 -0
- package/dist/client-BEoUYNLp.d.ts +1191 -0
- package/dist/client-CLulBnie.d.mts +1182 -0
- package/dist/client-CLulBnie.d.ts +1182 -0
- package/dist/client-CQwvp8ep.d.mts +1182 -0
- package/dist/client-CQwvp8ep.d.ts +1182 -0
- package/dist/client-PFPdeo-z.d.mts +1186 -0
- package/dist/client-PFPdeo-z.d.ts +1186 -0
- package/dist/client-wk2fgk1q.d.mts +1184 -0
- package/dist/client-wk2fgk1q.d.ts +1184 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +50 -4
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +61 -21
- package/dist/index.d.ts +61 -21
- package/dist/index.js +457 -361
- package/dist/index.mjs +389 -343
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AIReadyConfigSchema: () => AIReadyConfigSchema,
|
|
33
34
|
AnalysisResultSchema: () => AnalysisResultSchema,
|
|
34
35
|
AnalysisStatus: () => AnalysisStatus,
|
|
35
36
|
AnalysisStatusSchema: () => AnalysisStatusSchema,
|
|
@@ -155,6 +156,10 @@ __export(index_exports, {
|
|
|
155
156
|
loadMergedConfig: () => loadMergedConfig,
|
|
156
157
|
loadScoreHistory: () => loadScoreHistory,
|
|
157
158
|
mergeConfigWithDefaults: () => mergeConfigWithDefaults,
|
|
159
|
+
normalizeAnalysisResult: () => normalizeAnalysisResult,
|
|
160
|
+
normalizeIssue: () => normalizeIssue,
|
|
161
|
+
normalizeMetrics: () => normalizeMetrics,
|
|
162
|
+
normalizeSpokeOutput: () => normalizeSpokeOutput,
|
|
158
163
|
normalizeToolName: () => normalizeToolName,
|
|
159
164
|
parseFileExports: () => parseFileExports,
|
|
160
165
|
parseWeightString: () => parseWeightString,
|
|
@@ -181,21 +186,21 @@ var Severity = /* @__PURE__ */ ((Severity2) => {
|
|
|
181
186
|
return Severity2;
|
|
182
187
|
})(Severity || {});
|
|
183
188
|
var SeveritySchema = import_zod.z.nativeEnum(Severity);
|
|
184
|
-
var ToolName = /* @__PURE__ */ ((
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return
|
|
189
|
+
var ToolName = /* @__PURE__ */ ((ToolName3) => {
|
|
190
|
+
ToolName3["PatternDetect"] = "pattern-detect";
|
|
191
|
+
ToolName3["ContextAnalyzer"] = "context-analyzer";
|
|
192
|
+
ToolName3["NamingConsistency"] = "naming-consistency";
|
|
193
|
+
ToolName3["AiSignalClarity"] = "ai-signal-clarity";
|
|
194
|
+
ToolName3["AgentGrounding"] = "agent-grounding";
|
|
195
|
+
ToolName3["TestabilityIndex"] = "testability-index";
|
|
196
|
+
ToolName3["DocDrift"] = "doc-drift";
|
|
197
|
+
ToolName3["DependencyHealth"] = "dependency-health";
|
|
198
|
+
ToolName3["ChangeAmplification"] = "change-amplification";
|
|
199
|
+
ToolName3["CognitiveLoad"] = "cognitive-load";
|
|
200
|
+
ToolName3["PatternEntropy"] = "pattern-entropy";
|
|
201
|
+
ToolName3["ConceptCohesion"] = "concept-cohesion";
|
|
202
|
+
ToolName3["SemanticDistance"] = "semantic-distance";
|
|
203
|
+
return ToolName3;
|
|
199
204
|
})(ToolName || {});
|
|
200
205
|
var ToolNameSchema = import_zod.z.nativeEnum(ToolName);
|
|
201
206
|
var FRIENDLY_TOOL_NAMES = {
|
|
@@ -331,6 +336,52 @@ var UnifiedReportSchema = import_zod.z.object({
|
|
|
331
336
|
)
|
|
332
337
|
}).optional()
|
|
333
338
|
}).catchall(import_zod.z.any());
|
|
339
|
+
var AIReadyConfigSchema = import_zod.z.object({
|
|
340
|
+
/** Target score threshold (0-100) */
|
|
341
|
+
threshold: import_zod.z.number().optional(),
|
|
342
|
+
/** Files or directories to include in scan */
|
|
343
|
+
include: import_zod.z.array(import_zod.z.string()).optional(),
|
|
344
|
+
/** Files or directories to exclude from scan */
|
|
345
|
+
exclude: import_zod.z.array(import_zod.z.string()).optional(),
|
|
346
|
+
/** Scan-specific configuration */
|
|
347
|
+
scan: import_zod.z.object({
|
|
348
|
+
include: import_zod.z.array(import_zod.z.string()).optional(),
|
|
349
|
+
exclude: import_zod.z.array(import_zod.z.string()).optional(),
|
|
350
|
+
parallel: import_zod.z.boolean().optional(),
|
|
351
|
+
deep: import_zod.z.boolean().optional(),
|
|
352
|
+
tools: import_zod.z.array(import_zod.z.string()).optional()
|
|
353
|
+
}).optional(),
|
|
354
|
+
/** Output-specific configuration */
|
|
355
|
+
output: import_zod.z.object({
|
|
356
|
+
/** Output format (json, console, html) */
|
|
357
|
+
format: import_zod.z.enum(["json", "console", "html"]).optional(),
|
|
358
|
+
/** Output file path */
|
|
359
|
+
path: import_zod.z.string().optional(),
|
|
360
|
+
/** Output directory */
|
|
361
|
+
saveTo: import_zod.z.string().optional(),
|
|
362
|
+
/** Whether to show score breakdown in console */
|
|
363
|
+
showBreakdown: import_zod.z.boolean().optional(),
|
|
364
|
+
/** Baseline report to compare against */
|
|
365
|
+
compareBaseline: import_zod.z.string().optional()
|
|
366
|
+
}).optional(),
|
|
367
|
+
/** Tool-specific configuration overrides (Strictly ToolName -> Config) */
|
|
368
|
+
tools: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional(),
|
|
369
|
+
/** Scoring profile and weights */
|
|
370
|
+
scoring: import_zod.z.object({
|
|
371
|
+
/** Name of the scoring profile (e.g. "strict", "balanced") */
|
|
372
|
+
profile: import_zod.z.string().optional(),
|
|
373
|
+
/** Custom weights for tools and metrics */
|
|
374
|
+
weights: import_zod.z.record(import_zod.z.string(), import_zod.z.number()).optional()
|
|
375
|
+
}).optional(),
|
|
376
|
+
/** Visualizer settings (interactive graph) */
|
|
377
|
+
visualizer: import_zod.z.object({
|
|
378
|
+
groupingDirs: import_zod.z.array(import_zod.z.string()).optional(),
|
|
379
|
+
graph: import_zod.z.object({
|
|
380
|
+
maxNodes: import_zod.z.number().optional(),
|
|
381
|
+
maxEdges: import_zod.z.number().optional()
|
|
382
|
+
}).optional()
|
|
383
|
+
}).optional()
|
|
384
|
+
}).catchall(import_zod.z.any());
|
|
334
385
|
|
|
335
386
|
// src/types/business.ts
|
|
336
387
|
var import_zod2 = require("zod");
|
|
@@ -447,51 +498,100 @@ var ParseError = class extends Error {
|
|
|
447
498
|
}
|
|
448
499
|
};
|
|
449
500
|
|
|
501
|
+
// src/utils/normalization.ts
|
|
502
|
+
function normalizeIssue(raw) {
|
|
503
|
+
return {
|
|
504
|
+
type: raw.type ?? "pattern-inconsistency" /* PatternInconsistency */,
|
|
505
|
+
severity: raw.severity ?? raw.severityLevel ?? "info" /* Info */,
|
|
506
|
+
message: raw.message ?? "Unknown issue",
|
|
507
|
+
location: raw.location ?? {
|
|
508
|
+
file: raw.fileName ?? raw.file ?? raw.filePath ?? "unknown",
|
|
509
|
+
line: raw.line ?? 1,
|
|
510
|
+
column: raw.column
|
|
511
|
+
},
|
|
512
|
+
suggestion: raw.suggestion
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function normalizeMetrics(raw) {
|
|
516
|
+
return {
|
|
517
|
+
tokenCost: raw.tokenCost ?? 0,
|
|
518
|
+
complexityScore: raw.complexityScore ?? 0,
|
|
519
|
+
consistencyScore: raw.consistencyScore,
|
|
520
|
+
docFreshnessScore: raw.docFreshnessScore,
|
|
521
|
+
aiSignalClarityScore: raw.aiSignalClarityScore,
|
|
522
|
+
agentGroundingScore: raw.agentGroundingScore,
|
|
523
|
+
testabilityScore: raw.testabilityScore,
|
|
524
|
+
docDriftScore: raw.docDriftScore,
|
|
525
|
+
dependencyHealthScore: raw.dependencyHealthScore,
|
|
526
|
+
modelContextTier: raw.modelContextTier,
|
|
527
|
+
estimatedMonthlyCost: raw.estimatedMonthlyCost,
|
|
528
|
+
estimatedDeveloperHours: raw.estimatedDeveloperHours,
|
|
529
|
+
comprehensionDifficultyIndex: raw.comprehensionDifficultyIndex,
|
|
530
|
+
totalSymbols: raw.totalSymbols,
|
|
531
|
+
totalExports: raw.totalExports
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function normalizeAnalysisResult(raw) {
|
|
535
|
+
const fileName = raw.fileName ?? raw.file ?? raw.filePath ?? "unknown";
|
|
536
|
+
const rawIssues = Array.isArray(raw.issues) ? raw.issues : [];
|
|
537
|
+
return {
|
|
538
|
+
fileName,
|
|
539
|
+
issues: rawIssues.map((issue) => {
|
|
540
|
+
if (typeof issue === "string") {
|
|
541
|
+
return {
|
|
542
|
+
type: "pattern-inconsistency" /* PatternInconsistency */,
|
|
543
|
+
// Default fallback
|
|
544
|
+
severity: raw.severity ?? "info" /* Info */,
|
|
545
|
+
message: issue,
|
|
546
|
+
location: { file: fileName, line: 1 }
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
return normalizeIssue({
|
|
550
|
+
...issue,
|
|
551
|
+
fileName: issue.fileName ?? fileName,
|
|
552
|
+
severity: issue.severity ?? raw.severity
|
|
553
|
+
});
|
|
554
|
+
}),
|
|
555
|
+
metrics: normalizeMetrics(raw.metrics ?? {})
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function normalizeSpokeOutput(raw, toolName) {
|
|
559
|
+
const rawResults = Array.isArray(raw.results) ? raw.results : [];
|
|
560
|
+
return {
|
|
561
|
+
results: rawResults.map(normalizeAnalysisResult),
|
|
562
|
+
summary: raw.summary ?? {
|
|
563
|
+
totalFiles: rawResults.length,
|
|
564
|
+
totalIssues: 0,
|
|
565
|
+
criticalIssues: 0,
|
|
566
|
+
majorIssues: 0
|
|
567
|
+
},
|
|
568
|
+
metadata: {
|
|
569
|
+
toolName: raw.metadata?.toolName ?? toolName,
|
|
570
|
+
version: raw.metadata?.version,
|
|
571
|
+
timestamp: raw.metadata?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
572
|
+
config: raw.metadata?.config
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
450
577
|
// src/types/contract.ts
|
|
451
578
|
function validateSpokeOutput(toolName, output) {
|
|
452
|
-
const errors = [];
|
|
453
579
|
if (!output) {
|
|
454
580
|
return { valid: false, errors: ["Output is null or undefined"] };
|
|
455
581
|
}
|
|
456
|
-
if (!Array.isArray(output.results)) {
|
|
457
|
-
errors.push(`${toolName}: 'results' must be an array`);
|
|
458
|
-
} else {
|
|
459
|
-
output.results.forEach((res, idx) => {
|
|
460
|
-
const fileName = res.fileName || res.file || res.filePath;
|
|
461
|
-
if (!fileName)
|
|
462
|
-
errors.push(
|
|
463
|
-
`${toolName}: results[${idx}] missing 'fileName', 'file' or 'filePath'`
|
|
464
|
-
);
|
|
465
|
-
const issues = res.issues;
|
|
466
|
-
if (!Array.isArray(issues)) {
|
|
467
|
-
errors.push(`${toolName}: results[${idx}] 'issues' must be an array`);
|
|
468
|
-
} else if (issues.length > 0) {
|
|
469
|
-
issues.forEach((issue, iidx) => {
|
|
470
|
-
if (typeof issue === "string") return;
|
|
471
|
-
if (!issue.type && !res.file)
|
|
472
|
-
errors.push(
|
|
473
|
-
`${toolName}: results[${idx}].issues[${iidx}] missing 'type'`
|
|
474
|
-
);
|
|
475
|
-
if (!issue.severity && !res.severity)
|
|
476
|
-
errors.push(
|
|
477
|
-
`${toolName}: results[${idx}].issues[${iidx}] missing 'severity'`
|
|
478
|
-
);
|
|
479
|
-
const severity = issue.severity || res.severity;
|
|
480
|
-
if (severity && !["critical", "major", "minor", "info"].includes(severity)) {
|
|
481
|
-
errors.push(
|
|
482
|
-
`${toolName}: results[${idx}].issues[${iidx}] has invalid severity: ${severity}`
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
582
|
if (!output.summary) {
|
|
490
|
-
errors
|
|
583
|
+
return { valid: false, errors: [`${toolName}: missing 'summary'`] };
|
|
584
|
+
}
|
|
585
|
+
const normalized = normalizeSpokeOutput(output, toolName);
|
|
586
|
+
const result = SpokeOutputSchema.safeParse(normalized);
|
|
587
|
+
if (result.success) {
|
|
588
|
+
return { valid: true, errors: [] };
|
|
491
589
|
}
|
|
492
590
|
return {
|
|
493
|
-
valid:
|
|
494
|
-
errors
|
|
591
|
+
valid: false,
|
|
592
|
+
errors: result.error.issues.map(
|
|
593
|
+
(e) => `${toolName}: ${e.path.join(".")}: ${e.message}`
|
|
594
|
+
)
|
|
495
595
|
};
|
|
496
596
|
}
|
|
497
597
|
function validateWithSchema(schema, data) {
|
|
@@ -1115,148 +1215,67 @@ var TypeScriptParser = class {
|
|
|
1115
1215
|
this.extensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
1116
1216
|
}
|
|
1117
1217
|
async initialize() {
|
|
1118
|
-
return Promise.resolve();
|
|
1119
1218
|
}
|
|
1120
|
-
|
|
1121
|
-
return (
|
|
1122
|
-
loc: true,
|
|
1123
|
-
range: true,
|
|
1124
|
-
jsx: filePath.match(/\.[jt]sx$/i) !== null,
|
|
1125
|
-
filePath,
|
|
1126
|
-
sourceType: "module",
|
|
1127
|
-
ecmaVersion: "latest",
|
|
1128
|
-
comment: true
|
|
1129
|
-
});
|
|
1219
|
+
canHandle(filePath) {
|
|
1220
|
+
return this.extensions.some((ext) => filePath.endsWith(ext));
|
|
1130
1221
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
const precedingStartOffset = Math.max(0, start - 1e3);
|
|
1147
|
-
const absoluteStartOffset = precedingStartOffset + (lastMatch.index || 0);
|
|
1148
|
-
const absoluteEndOffset = precedingStartOffset + matchEndIndex;
|
|
1149
|
-
const codeBeforeStart = code.slice(0, absoluteStartOffset);
|
|
1150
|
-
const startLines = codeBeforeStart.split("\n");
|
|
1151
|
-
const startLine = startLines.length;
|
|
1152
|
-
const startColumn = startLines[startLines.length - 1].length;
|
|
1153
|
-
const codeBeforeEnd = code.slice(0, absoluteEndOffset);
|
|
1154
|
-
const endLines = codeBeforeEnd.split("\n");
|
|
1155
|
-
const endLine = endLines.length;
|
|
1156
|
-
const endColumn = endLines[endLines.length - 1].length;
|
|
1157
|
-
metadata.documentation = {
|
|
1158
|
-
content: lastMatch[1].replace(/^\s*\*+/gm, "").trim(),
|
|
1159
|
-
type: "jsdoc",
|
|
1160
|
-
loc: {
|
|
1161
|
-
start: { line: startLine, column: startColumn },
|
|
1162
|
-
end: { line: endLine, column: endColumn }
|
|
1163
|
-
}
|
|
1164
|
-
};
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
const walk = (n) => {
|
|
1168
|
-
if (!n) return;
|
|
1169
|
-
if (n.type === "AssignmentExpression") {
|
|
1170
|
-
metadata.isPure = false;
|
|
1171
|
-
metadata.hasSideEffects = true;
|
|
1172
|
-
}
|
|
1173
|
-
if (n.type === "UpdateExpression") {
|
|
1174
|
-
metadata.isPure = false;
|
|
1175
|
-
metadata.hasSideEffects = true;
|
|
1176
|
-
}
|
|
1177
|
-
if (n.type === "CallExpression" && n.callee.type === "MemberExpression" && n.callee.object.type === "Identifier") {
|
|
1178
|
-
const objName = n.callee.object.name;
|
|
1179
|
-
if (["console", "process", "fs", "window", "document"].includes(objName)) {
|
|
1180
|
-
metadata.isPure = false;
|
|
1181
|
-
metadata.hasSideEffects = true;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
if (n.type === "ThrowStatement") {
|
|
1185
|
-
metadata.isPure = false;
|
|
1186
|
-
metadata.hasSideEffects = true;
|
|
1187
|
-
}
|
|
1188
|
-
for (const key of Object.keys(n)) {
|
|
1189
|
-
if (key === "parent") continue;
|
|
1190
|
-
const child = n[key];
|
|
1191
|
-
if (child && typeof child === "object") {
|
|
1192
|
-
if (Array.isArray(child)) {
|
|
1193
|
-
child.forEach((c) => c?.type && walk(c));
|
|
1194
|
-
} else if (child.type) {
|
|
1195
|
-
walk(child);
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1200
|
-
let nodeToAnalyze = node;
|
|
1201
|
-
if (node.type === "ExportNamedDeclaration" && node.declaration) {
|
|
1202
|
-
nodeToAnalyze = node.declaration;
|
|
1203
|
-
} else if (node.type === "ExportDefaultDeclaration" && node.declaration) {
|
|
1204
|
-
if (node.declaration.type !== "TSInterfaceDeclaration" && node.declaration.type !== "TSTypeAliasDeclaration") {
|
|
1205
|
-
nodeToAnalyze = node.declaration;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
if (nodeToAnalyze.type === "FunctionDeclaration" || nodeToAnalyze.type === "FunctionExpression" || nodeToAnalyze.type === "ArrowFunctionExpression") {
|
|
1209
|
-
if (nodeToAnalyze.body) walk(nodeToAnalyze.body);
|
|
1210
|
-
} else if (nodeToAnalyze.type === "ClassDeclaration" || nodeToAnalyze.type === "ClassExpression") {
|
|
1211
|
-
walk(nodeToAnalyze.body);
|
|
1222
|
+
async getAST(code, filePath) {
|
|
1223
|
+
try {
|
|
1224
|
+
return (0, import_typescript_estree.parse)(code, {
|
|
1225
|
+
filePath,
|
|
1226
|
+
loc: true,
|
|
1227
|
+
range: true,
|
|
1228
|
+
tokens: true,
|
|
1229
|
+
comment: true,
|
|
1230
|
+
jsx: filePath.endsWith("x")
|
|
1231
|
+
});
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
throw new ParseError(error.message, filePath, {
|
|
1234
|
+
line: error.lineNumber || 1,
|
|
1235
|
+
column: error.column || 0
|
|
1236
|
+
});
|
|
1212
1237
|
}
|
|
1213
|
-
return metadata;
|
|
1214
1238
|
}
|
|
1215
1239
|
parse(code, filePath) {
|
|
1216
1240
|
try {
|
|
1217
|
-
const isJavaScript = filePath.match(/\.jsx?$/i);
|
|
1218
1241
|
const ast = (0, import_typescript_estree.parse)(code, {
|
|
1242
|
+
filePath,
|
|
1219
1243
|
loc: true,
|
|
1220
1244
|
range: true,
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
ecmaVersion: "latest",
|
|
1225
|
-
comment: true
|
|
1245
|
+
tokens: true,
|
|
1246
|
+
comment: true,
|
|
1247
|
+
jsx: filePath.endsWith("x")
|
|
1226
1248
|
});
|
|
1227
1249
|
const imports = this.extractImports(ast);
|
|
1228
|
-
const exports2 = this.extractExports(ast,
|
|
1250
|
+
const exports2 = this.extractExports(ast, code);
|
|
1229
1251
|
return {
|
|
1230
1252
|
exports: exports2,
|
|
1231
1253
|
imports,
|
|
1232
|
-
language:
|
|
1233
|
-
warnings: []
|
|
1254
|
+
language: this.language
|
|
1234
1255
|
};
|
|
1235
1256
|
} catch (error) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
);
|
|
1257
|
+
throw new ParseError(error.message, filePath, {
|
|
1258
|
+
line: error.lineNumber || 1,
|
|
1259
|
+
column: error.column || 0
|
|
1260
|
+
});
|
|
1241
1261
|
}
|
|
1242
1262
|
}
|
|
1243
1263
|
getNamingConventions() {
|
|
1244
1264
|
return {
|
|
1245
|
-
// camelCase for variables and functions
|
|
1246
1265
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
1247
1266
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
1248
|
-
// PascalCase for classes, types and interfaces
|
|
1249
1267
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1250
|
-
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1251
|
-
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1252
|
-
// UPPER_CASE for constants
|
|
1253
1268
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
1254
|
-
|
|
1255
|
-
|
|
1269
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1270
|
+
interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
|
|
1256
1271
|
};
|
|
1257
1272
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1273
|
+
analyzeMetadata(node, code) {
|
|
1274
|
+
if (!code) return {};
|
|
1275
|
+
return {
|
|
1276
|
+
isPure: this.isLikelyPure(node),
|
|
1277
|
+
hasSideEffects: !this.isLikelyPure(node)
|
|
1278
|
+
};
|
|
1260
1279
|
}
|
|
1261
1280
|
extractImports(ast) {
|
|
1262
1281
|
const imports = [];
|
|
@@ -1294,168 +1313,165 @@ var TypeScriptParser = class {
|
|
|
1294
1313
|
}
|
|
1295
1314
|
return imports;
|
|
1296
1315
|
}
|
|
1297
|
-
extractExports(ast,
|
|
1316
|
+
extractExports(ast, code) {
|
|
1298
1317
|
const exports2 = [];
|
|
1299
|
-
const importedNames = new Set(
|
|
1300
|
-
imports.flatMap(
|
|
1301
|
-
(imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
|
|
1302
|
-
)
|
|
1303
|
-
);
|
|
1304
1318
|
for (const node of ast.body) {
|
|
1305
|
-
if (node.type === "ExportNamedDeclaration"
|
|
1306
|
-
|
|
1307
|
-
node.declaration
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1319
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
1320
|
+
if (node.declaration) {
|
|
1321
|
+
const declaration = node.declaration;
|
|
1322
|
+
if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
|
|
1323
|
+
exports2.push(
|
|
1324
|
+
this.createExport(
|
|
1325
|
+
declaration.id.name,
|
|
1326
|
+
"function",
|
|
1327
|
+
node,
|
|
1328
|
+
// Pass the outer ExportNamedDeclaration
|
|
1329
|
+
code
|
|
1330
|
+
)
|
|
1331
|
+
);
|
|
1332
|
+
} else if (declaration.type === "ClassDeclaration" && declaration.id) {
|
|
1333
|
+
exports2.push(
|
|
1334
|
+
this.createExport(
|
|
1335
|
+
declaration.id.name,
|
|
1336
|
+
"class",
|
|
1337
|
+
node,
|
|
1338
|
+
// Pass the outer ExportNamedDeclaration
|
|
1339
|
+
code
|
|
1340
|
+
)
|
|
1341
|
+
);
|
|
1342
|
+
} else if (declaration.type === "TSTypeAliasDeclaration") {
|
|
1343
|
+
exports2.push(
|
|
1344
|
+
this.createExport(
|
|
1345
|
+
declaration.id.name,
|
|
1346
|
+
"type",
|
|
1347
|
+
node,
|
|
1348
|
+
// Pass the outer ExportNamedDeclaration
|
|
1349
|
+
code
|
|
1350
|
+
)
|
|
1351
|
+
);
|
|
1352
|
+
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
1353
|
+
exports2.push(
|
|
1354
|
+
this.createExport(
|
|
1355
|
+
declaration.id.name,
|
|
1356
|
+
"interface",
|
|
1357
|
+
node,
|
|
1358
|
+
// Pass the outer ExportNamedDeclaration
|
|
1359
|
+
code
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
} else if (declaration.type === "VariableDeclaration") {
|
|
1363
|
+
for (const decl of declaration.declarations) {
|
|
1364
|
+
if (decl.id.type === "Identifier") {
|
|
1365
|
+
exports2.push(
|
|
1366
|
+
this.createExport(
|
|
1367
|
+
decl.id.name,
|
|
1368
|
+
"const",
|
|
1369
|
+
node,
|
|
1370
|
+
// Pass the outer ExportNamedDeclaration
|
|
1371
|
+
code,
|
|
1372
|
+
decl.init
|
|
1373
|
+
)
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1324
1378
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
type,
|
|
1328
|
-
loc: node.loc ? {
|
|
1329
|
-
start: {
|
|
1330
|
-
line: node.loc.start.line,
|
|
1331
|
-
column: node.loc.start.column
|
|
1332
|
-
},
|
|
1333
|
-
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
1334
|
-
} : void 0,
|
|
1335
|
-
...metadata
|
|
1336
|
-
});
|
|
1379
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
1380
|
+
exports2.push(this.createExport("default", "default", node, code));
|
|
1337
1381
|
}
|
|
1338
1382
|
}
|
|
1339
1383
|
return exports2;
|
|
1340
1384
|
}
|
|
1341
|
-
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1385
|
+
createExport(name, type, node, code, initializer) {
|
|
1386
|
+
const documentation = this.extractDocumentation(node, code);
|
|
1387
|
+
let methodCount;
|
|
1388
|
+
let propertyCount;
|
|
1389
|
+
let parameters;
|
|
1390
|
+
let isPrimitive = false;
|
|
1391
|
+
if (initializer) {
|
|
1392
|
+
if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
|
|
1393
|
+
isPrimitive = true;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
1397
|
+
if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
|
|
1398
|
+
const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
|
|
1399
|
+
methodCount = body.filter(
|
|
1400
|
+
(m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
|
|
1401
|
+
).length;
|
|
1402
|
+
propertyCount = body.filter(
|
|
1403
|
+
(m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
|
|
1404
|
+
).length;
|
|
1405
|
+
if (structNode.type === "ClassDeclaration") {
|
|
1406
|
+
const constructor = body.find(
|
|
1407
|
+
(m) => m.type === "MethodDefinition" && m.kind === "constructor"
|
|
1408
|
+
);
|
|
1409
|
+
if (constructor && constructor.value && constructor.value.params) {
|
|
1410
|
+
parameters = constructor.value.params.map((p) => {
|
|
1411
|
+
if (p.type === "Identifier") return p.name;
|
|
1412
|
+
if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
|
|
1413
|
+
return p.parameter.name;
|
|
1414
|
+
}
|
|
1415
|
+
return void 0;
|
|
1416
|
+
}).filter(Boolean);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
|
|
1421
|
+
const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
|
|
1422
|
+
if (funcNode && funcNode.params) {
|
|
1423
|
+
parameters = funcNode.params.map((p) => {
|
|
1349
1424
|
if (p.type === "Identifier") return p.name;
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1374
|
-
);
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
column: declaration.loc.end.column
|
|
1395
|
-
}
|
|
1396
|
-
} : void 0,
|
|
1397
|
-
...metadata
|
|
1398
|
-
});
|
|
1399
|
-
} else if (declaration.type === "VariableDeclaration") {
|
|
1400
|
-
for (const declarator of declaration.declarations) {
|
|
1401
|
-
if (declarator.id.type === "Identifier") {
|
|
1402
|
-
exports2.push({
|
|
1403
|
-
name: declarator.id.name,
|
|
1404
|
-
type: "const",
|
|
1405
|
-
loc: declarator.loc ? {
|
|
1406
|
-
start: {
|
|
1407
|
-
line: declarator.loc.start.line,
|
|
1408
|
-
column: declarator.loc.start.column
|
|
1409
|
-
},
|
|
1410
|
-
end: {
|
|
1411
|
-
line: declarator.loc.end.line,
|
|
1412
|
-
column: declarator.loc.end.column
|
|
1413
|
-
}
|
|
1414
|
-
} : void 0,
|
|
1415
|
-
...metadata
|
|
1416
|
-
});
|
|
1425
|
+
return void 0;
|
|
1426
|
+
}).filter(Boolean);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return {
|
|
1430
|
+
name,
|
|
1431
|
+
type,
|
|
1432
|
+
isPrimitive,
|
|
1433
|
+
loc: node.loc ? {
|
|
1434
|
+
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
1435
|
+
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
1436
|
+
} : void 0,
|
|
1437
|
+
documentation,
|
|
1438
|
+
methodCount,
|
|
1439
|
+
propertyCount,
|
|
1440
|
+
parameters,
|
|
1441
|
+
isPure: this.isLikelyPure(node),
|
|
1442
|
+
hasSideEffects: !this.isLikelyPure(node)
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
extractDocumentation(node, code) {
|
|
1446
|
+
if (node.range) {
|
|
1447
|
+
const start = node.range[0];
|
|
1448
|
+
const precedingCode = code.substring(0, start);
|
|
1449
|
+
const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
|
|
1450
|
+
if (jsdocMatch) {
|
|
1451
|
+
return {
|
|
1452
|
+
content: jsdocMatch[1].trim(),
|
|
1453
|
+
type: "jsdoc"
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
return void 0;
|
|
1458
|
+
}
|
|
1459
|
+
isLikelyPure(node) {
|
|
1460
|
+
const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
1461
|
+
if (structNode.type === "VariableDeclaration" && structNode.kind === "const")
|
|
1462
|
+
return true;
|
|
1463
|
+
if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition" && structNode.value) {
|
|
1464
|
+
const body = structNode.type === "MethodDefinition" ? structNode.value.body : structNode.body;
|
|
1465
|
+
if (body && body.type === "BlockStatement") {
|
|
1466
|
+
const bodyContent = JSON.stringify(body);
|
|
1467
|
+
if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"type":"AssignmentExpression"')) {
|
|
1468
|
+
return false;
|
|
1417
1469
|
}
|
|
1470
|
+
return true;
|
|
1418
1471
|
}
|
|
1419
|
-
|
|
1420
|
-
exports2.push({
|
|
1421
|
-
name: declaration.id.name,
|
|
1422
|
-
type: "type",
|
|
1423
|
-
loc: declaration.loc ? {
|
|
1424
|
-
start: {
|
|
1425
|
-
line: declaration.loc.start.line,
|
|
1426
|
-
column: declaration.loc.start.column
|
|
1427
|
-
},
|
|
1428
|
-
end: {
|
|
1429
|
-
line: declaration.loc.end.line,
|
|
1430
|
-
column: declaration.loc.end.column
|
|
1431
|
-
}
|
|
1432
|
-
} : void 0,
|
|
1433
|
-
...metadata
|
|
1434
|
-
});
|
|
1435
|
-
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
1436
|
-
const body = declaration.body.body;
|
|
1437
|
-
const methods = body.filter((m) => m.type === "TSMethodSignature");
|
|
1438
|
-
const properties = body.filter((m) => m.type === "TSPropertySignature");
|
|
1439
|
-
exports2.push({
|
|
1440
|
-
name: declaration.id.name,
|
|
1441
|
-
type: "interface",
|
|
1442
|
-
methodCount: methods.length,
|
|
1443
|
-
propertyCount: properties.length || body.length,
|
|
1444
|
-
// Fallback to body.length
|
|
1445
|
-
loc: declaration.loc ? {
|
|
1446
|
-
start: {
|
|
1447
|
-
line: declaration.loc.start.line,
|
|
1448
|
-
column: declaration.loc.start.column
|
|
1449
|
-
},
|
|
1450
|
-
end: {
|
|
1451
|
-
line: declaration.loc.end.line,
|
|
1452
|
-
column: declaration.loc.end.column
|
|
1453
|
-
}
|
|
1454
|
-
} : void 0,
|
|
1455
|
-
...metadata
|
|
1456
|
-
});
|
|
1472
|
+
return true;
|
|
1457
1473
|
}
|
|
1458
|
-
return
|
|
1474
|
+
return false;
|
|
1459
1475
|
}
|
|
1460
1476
|
};
|
|
1461
1477
|
|
|
@@ -1745,7 +1761,7 @@ var PythonParser = class extends BaseLanguageParser {
|
|
|
1745
1761
|
* Extract import information using AST walk.
|
|
1746
1762
|
*
|
|
1747
1763
|
* @param rootNode - Root node of the Python AST.
|
|
1748
|
-
* @returns Array of discovered
|
|
1764
|
+
* @returns Array of discovered FileImport objects.
|
|
1749
1765
|
*/
|
|
1750
1766
|
extractImportsAST(rootNode) {
|
|
1751
1767
|
const imports = [];
|
|
@@ -2302,7 +2318,7 @@ var JavaParser = class extends BaseLanguageParser {
|
|
|
2302
2318
|
* Extract import information using AST walk.
|
|
2303
2319
|
*
|
|
2304
2320
|
* @param rootNode - Root node of the Java AST.
|
|
2305
|
-
* @returns Array of discovered
|
|
2321
|
+
* @returns Array of discovered FileImport objects.
|
|
2306
2322
|
*/
|
|
2307
2323
|
extractImportsAST(rootNode) {
|
|
2308
2324
|
const imports = [];
|
|
@@ -2534,7 +2550,7 @@ var CSharpParser = class extends BaseLanguageParser {
|
|
|
2534
2550
|
* Extract import information (usings) using AST walk.
|
|
2535
2551
|
*
|
|
2536
2552
|
* @param rootNode - Root node of the C# AST.
|
|
2537
|
-
* @returns Array of discovered
|
|
2553
|
+
* @returns Array of discovered FileImport objects.
|
|
2538
2554
|
*/
|
|
2539
2555
|
extractImportsAST(rootNode) {
|
|
2540
2556
|
const imports = [];
|
|
@@ -2787,7 +2803,7 @@ var GoParser = class extends BaseLanguageParser {
|
|
|
2787
2803
|
* Extract import information using AST walk.
|
|
2788
2804
|
*
|
|
2789
2805
|
* @param rootNode - Root node of the Go AST.
|
|
2790
|
-
* @returns Array of discovered
|
|
2806
|
+
* @returns Array of discovered FileImport objects.
|
|
2791
2807
|
*/
|
|
2792
2808
|
extractImportsAST(rootNode) {
|
|
2793
2809
|
const imports = [];
|
|
@@ -3275,10 +3291,25 @@ async function loadConfig(rootDir) {
|
|
|
3275
3291
|
const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
|
|
3276
3292
|
config = JSON.parse(content);
|
|
3277
3293
|
}
|
|
3278
|
-
|
|
3279
|
-
|
|
3294
|
+
const legacyKeys = ["toolConfigs"];
|
|
3295
|
+
const rootLevelTools = [
|
|
3296
|
+
"pattern-detect",
|
|
3297
|
+
"context-analyzer",
|
|
3298
|
+
"naming-consistency",
|
|
3299
|
+
"ai-signal-clarity"
|
|
3300
|
+
];
|
|
3301
|
+
const allKeys = Object.keys(config);
|
|
3302
|
+
const foundLegacy = allKeys.filter(
|
|
3303
|
+
(k) => legacyKeys.includes(k) || rootLevelTools.includes(k)
|
|
3304
|
+
);
|
|
3305
|
+
if (foundLegacy.length > 0) {
|
|
3306
|
+
console.warn(
|
|
3307
|
+
`\u26A0\uFE0F Legacy configuration keys found: ${foundLegacy.join(
|
|
3308
|
+
", "
|
|
3309
|
+
)}. These are deprecated and should be moved under the "tools" key.`
|
|
3310
|
+
);
|
|
3280
3311
|
}
|
|
3281
|
-
return config;
|
|
3312
|
+
return AIReadyConfigSchema.parse(config);
|
|
3282
3313
|
} catch (error) {
|
|
3283
3314
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3284
3315
|
const configError = new Error(
|
|
@@ -3306,12 +3337,10 @@ function mergeConfigWithDefaults(userConfig, defaults) {
|
|
|
3306
3337
|
if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
|
|
3307
3338
|
if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
|
|
3308
3339
|
}
|
|
3309
|
-
|
|
3310
|
-
if (toolOverrides) {
|
|
3340
|
+
if (userConfig.tools) {
|
|
3311
3341
|
if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
|
|
3312
|
-
for (const [toolName, toolConfig] of Object.entries(
|
|
3342
|
+
for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
|
|
3313
3343
|
if (typeof toolConfig === "object" && toolConfig !== null) {
|
|
3314
|
-
mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
|
|
3315
3344
|
mergedConfig.toolConfigs[toolName] = {
|
|
3316
3345
|
...mergedConfig.toolConfigs[toolName],
|
|
3317
3346
|
...toolConfig
|
|
@@ -3613,12 +3642,12 @@ function getRecommendedThreshold(fileCount, modelTier = "standard") {
|
|
|
3613
3642
|
return base + modelBonus;
|
|
3614
3643
|
}
|
|
3615
3644
|
function normalizeToolName(shortName) {
|
|
3616
|
-
return TOOL_NAME_MAP[shortName.toLowerCase()]
|
|
3645
|
+
return TOOL_NAME_MAP[shortName.toLowerCase()] ?? shortName;
|
|
3617
3646
|
}
|
|
3618
3647
|
function getToolWeight(toolName, toolConfig, cliOverride, profile = "default" /* Default */) {
|
|
3619
3648
|
if (cliOverride !== void 0) return cliOverride;
|
|
3620
3649
|
if (toolConfig?.scoreWeight !== void 0) return toolConfig.scoreWeight;
|
|
3621
|
-
const profileWeights = SCORING_PROFILES[profile]
|
|
3650
|
+
const profileWeights = SCORING_PROFILES[profile] ?? DEFAULT_TOOL_WEIGHTS;
|
|
3622
3651
|
return profileWeights[toolName] ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 5;
|
|
3623
3652
|
}
|
|
3624
3653
|
function parseWeightString(weightStr) {
|
|
@@ -3655,7 +3684,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
|
|
|
3655
3684
|
const toolsUsed = [];
|
|
3656
3685
|
const calculationWeights = {};
|
|
3657
3686
|
for (const [toolName, output] of toolOutputs.entries()) {
|
|
3658
|
-
const weight = weights.get(toolName)
|
|
3687
|
+
const weight = weights.get(toolName) ?? 5;
|
|
3659
3688
|
weightedSum += output.score * weight;
|
|
3660
3689
|
totalWeight += weight;
|
|
3661
3690
|
toolsUsed.push(toolName);
|
|
@@ -3666,7 +3695,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
|
|
|
3666
3695
|
const rating = getRating(overall);
|
|
3667
3696
|
const formulaParts = Array.from(toolOutputs.entries()).map(
|
|
3668
3697
|
([name, output]) => {
|
|
3669
|
-
const w = weights.get(name)
|
|
3698
|
+
const w = weights.get(name) ?? 5;
|
|
3670
3699
|
return `(${output.score} \xD7 ${w})`;
|
|
3671
3700
|
}
|
|
3672
3701
|
);
|
|
@@ -3759,6 +3788,13 @@ function formatToolScore(output) {
|
|
|
3759
3788
|
|
|
3760
3789
|
// src/business/pricing-models.ts
|
|
3761
3790
|
var MODEL_PRICING_PRESETS = {
|
|
3791
|
+
"gpt-5.4-mini": {
|
|
3792
|
+
name: "GPT-5.4 Mini",
|
|
3793
|
+
pricePer1KInputTokens: 1e-4,
|
|
3794
|
+
pricePer1KOutputTokens: 4e-4,
|
|
3795
|
+
contextTier: "extended",
|
|
3796
|
+
typicalQueriesPerDevPerDay: 200
|
|
3797
|
+
},
|
|
3762
3798
|
"gpt-5.3": {
|
|
3763
3799
|
name: "GPT-5.3",
|
|
3764
3800
|
pricePer1KInputTokens: 2e-3,
|
|
@@ -3780,20 +3816,6 @@ var MODEL_PRICING_PRESETS = {
|
|
|
3780
3816
|
contextTier: "frontier",
|
|
3781
3817
|
typicalQueriesPerDevPerDay: 120
|
|
3782
3818
|
},
|
|
3783
|
-
"gpt-4o": {
|
|
3784
|
-
name: "GPT-4o (legacy)",
|
|
3785
|
-
pricePer1KInputTokens: 5e-3,
|
|
3786
|
-
pricePer1KOutputTokens: 0.015,
|
|
3787
|
-
contextTier: "extended",
|
|
3788
|
-
typicalQueriesPerDevPerDay: 60
|
|
3789
|
-
},
|
|
3790
|
-
"claude-3-5-sonnet": {
|
|
3791
|
-
name: "Claude 3.5 Sonnet (legacy)",
|
|
3792
|
-
pricePer1KInputTokens: 3e-3,
|
|
3793
|
-
pricePer1KOutputTokens: 0.015,
|
|
3794
|
-
contextTier: "extended",
|
|
3795
|
-
typicalQueriesPerDevPerDay: 80
|
|
3796
|
-
},
|
|
3797
3819
|
"gemini-1-5-pro": {
|
|
3798
3820
|
name: "Gemini 1.5 Pro (legacy)",
|
|
3799
3821
|
pricePer1KInputTokens: 125e-5,
|
|
@@ -3817,7 +3839,7 @@ var MODEL_PRICING_PRESETS = {
|
|
|
3817
3839
|
}
|
|
3818
3840
|
};
|
|
3819
3841
|
function getModelPreset(modelId) {
|
|
3820
|
-
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["
|
|
3842
|
+
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-5.4-mini"];
|
|
3821
3843
|
}
|
|
3822
3844
|
|
|
3823
3845
|
// src/business/cost-metrics.ts
|
|
@@ -3838,7 +3860,7 @@ function calculateMonthlyCost(tokenWaste, config = {}) {
|
|
|
3838
3860
|
// Added baseline chattiness
|
|
3839
3861
|
}
|
|
3840
3862
|
});
|
|
3841
|
-
const preset = getModelPreset("
|
|
3863
|
+
const preset = getModelPreset("gpt-5.4-mini");
|
|
3842
3864
|
return estimateCostFromBudget(budget, preset, config);
|
|
3843
3865
|
}
|
|
3844
3866
|
function calculateTokenBudget(params) {
|
|
@@ -4668,6 +4690,40 @@ function calculateAgentGrounding(params) {
|
|
|
4668
4690
|
}
|
|
4669
4691
|
|
|
4670
4692
|
// src/metrics/testability-index.ts
|
|
4693
|
+
function isLikelyEntryPoint(filePath) {
|
|
4694
|
+
const basename = filePath.split("/").pop() || "";
|
|
4695
|
+
const lowerBasename = basename.toLowerCase();
|
|
4696
|
+
const entryPointPatterns = [
|
|
4697
|
+
"cli",
|
|
4698
|
+
"main",
|
|
4699
|
+
"bin",
|
|
4700
|
+
"index",
|
|
4701
|
+
// often used as entry point
|
|
4702
|
+
"run",
|
|
4703
|
+
"serve",
|
|
4704
|
+
"start",
|
|
4705
|
+
"boot",
|
|
4706
|
+
"init"
|
|
4707
|
+
];
|
|
4708
|
+
const nameWithoutExt = lowerBasename.replace(
|
|
4709
|
+
/\.(ts|js|tsx|jsx|mjs|cjs)$/,
|
|
4710
|
+
""
|
|
4711
|
+
);
|
|
4712
|
+
if (entryPointPatterns.some(
|
|
4713
|
+
(p) => nameWithoutExt === p || nameWithoutExt.endsWith(`-${p}`) || nameWithoutExt.startsWith(`${p}-`)
|
|
4714
|
+
)) {
|
|
4715
|
+
return true;
|
|
4716
|
+
}
|
|
4717
|
+
const cliDirPatterns = ["/bin/", "/cli/", "/cmd/", "/commands/"];
|
|
4718
|
+
if (cliDirPatterns.some((p) => filePath.includes(p))) {
|
|
4719
|
+
return true;
|
|
4720
|
+
}
|
|
4721
|
+
return false;
|
|
4722
|
+
}
|
|
4723
|
+
function calculateFilePurityScore(pureFunctions, totalFunctions) {
|
|
4724
|
+
if (totalFunctions === 0) return 100;
|
|
4725
|
+
return Math.round(pureFunctions / totalFunctions * 100);
|
|
4726
|
+
}
|
|
4671
4727
|
function calculateTestabilityIndex(params) {
|
|
4672
4728
|
const {
|
|
4673
4729
|
testFiles,
|
|
@@ -4679,17 +4735,18 @@ function calculateTestabilityIndex(params) {
|
|
|
4679
4735
|
bloatedInterfaces,
|
|
4680
4736
|
totalInterfaces,
|
|
4681
4737
|
externalStateMutations,
|
|
4682
|
-
hasTestFramework
|
|
4738
|
+
hasTestFramework,
|
|
4739
|
+
fileDetails
|
|
4683
4740
|
} = params;
|
|
4684
4741
|
const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
|
|
4685
4742
|
const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
|
|
4686
4743
|
const purityScore = Math.round(
|
|
4687
|
-
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.
|
|
4744
|
+
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.7) * 100
|
|
4688
4745
|
);
|
|
4689
4746
|
const dependencyInjectionScore = Math.round(
|
|
4690
4747
|
Math.min(
|
|
4691
4748
|
100,
|
|
4692
|
-
(totalClasses > 0 ? injectionPatterns / totalClasses : 0.
|
|
4749
|
+
(totalClasses > 0 ? injectionPatterns / totalClasses : 0.8) * 100
|
|
4693
4750
|
)
|
|
4694
4751
|
);
|
|
4695
4752
|
const interfaceFocusScore = Math.max(
|
|
@@ -4705,7 +4762,39 @@ function calculateTestabilityIndex(params) {
|
|
|
4705
4762
|
)
|
|
4706
4763
|
);
|
|
4707
4764
|
const frameworkWeight = hasTestFramework ? 1 : 0.8;
|
|
4708
|
-
|
|
4765
|
+
let fileMetrics;
|
|
4766
|
+
let libraryPureFunctions = pureFunctions;
|
|
4767
|
+
let libraryTotalFunctions = totalFunctions;
|
|
4768
|
+
let entryPointCount = 0;
|
|
4769
|
+
if (fileDetails && fileDetails.length > 0) {
|
|
4770
|
+
fileMetrics = fileDetails.map((file) => {
|
|
4771
|
+
const isEntryPoint = isLikelyEntryPoint(file.filePath);
|
|
4772
|
+
const purityScore2 = calculateFilePurityScore(
|
|
4773
|
+
file.pureFunctions,
|
|
4774
|
+
file.totalFunctions
|
|
4775
|
+
);
|
|
4776
|
+
if (isEntryPoint) {
|
|
4777
|
+
entryPointCount++;
|
|
4778
|
+
libraryPureFunctions -= file.pureFunctions;
|
|
4779
|
+
libraryTotalFunctions -= file.totalFunctions;
|
|
4780
|
+
}
|
|
4781
|
+
return {
|
|
4782
|
+
filePath: file.filePath,
|
|
4783
|
+
score: purityScore2,
|
|
4784
|
+
// Simplified - just purity for file-level
|
|
4785
|
+
purityScore: purityScore2,
|
|
4786
|
+
isEntryPoint
|
|
4787
|
+
};
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
4790
|
+
const libraryPurityScore = Math.max(
|
|
4791
|
+
0,
|
|
4792
|
+
Math.round(
|
|
4793
|
+
(libraryTotalFunctions > 0 ? libraryPureFunctions / libraryTotalFunctions : 0.7) * 100
|
|
4794
|
+
)
|
|
4795
|
+
);
|
|
4796
|
+
const effectivePurityScore = fileDetails && fileDetails.length > 0 ? libraryPurityScore : purityScore;
|
|
4797
|
+
const rawScore = (testCoverageRatio * 0.3 + effectivePurityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
|
|
4709
4798
|
const score = Math.max(0, Math.min(100, Math.round(rawScore)));
|
|
4710
4799
|
let rating;
|
|
4711
4800
|
if (score >= 85) rating = "excellent";
|
|
@@ -4730,9 +4819,9 @@ function calculateTestabilityIndex(params) {
|
|
|
4730
4819
|
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
4731
4820
|
);
|
|
4732
4821
|
}
|
|
4733
|
-
if (
|
|
4822
|
+
if (effectivePurityScore < 50)
|
|
4734
4823
|
recommendations.push(
|
|
4735
|
-
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
4824
|
+
entryPointCount > 0 ? `Extract pure functions from library code (${entryPointCount} entry point files excluded) \u2014 pure functions are trivially AI-testable` : "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
4736
4825
|
);
|
|
4737
4826
|
if (dependencyInjectionScore < 50 && totalClasses > 0)
|
|
4738
4827
|
recommendations.push(
|
|
@@ -4747,13 +4836,15 @@ function calculateTestabilityIndex(params) {
|
|
|
4747
4836
|
rating,
|
|
4748
4837
|
dimensions: {
|
|
4749
4838
|
testCoverageRatio,
|
|
4750
|
-
purityScore,
|
|
4839
|
+
purityScore: effectivePurityScore,
|
|
4751
4840
|
dependencyInjectionScore,
|
|
4752
4841
|
interfaceFocusScore,
|
|
4753
4842
|
observabilityScore
|
|
4754
4843
|
},
|
|
4755
4844
|
aiChangeSafetyRating,
|
|
4756
|
-
recommendations
|
|
4845
|
+
recommendations,
|
|
4846
|
+
fileMetrics,
|
|
4847
|
+
libraryScore: Math.max(0, fileMetrics ? libraryPurityScore : purityScore)
|
|
4757
4848
|
};
|
|
4758
4849
|
}
|
|
4759
4850
|
|
|
@@ -4770,9 +4861,9 @@ function calculateDocDrift(params) {
|
|
|
4770
4861
|
const outdatedRatio = totalExports > 0 ? outdatedComments / totalExports : 0;
|
|
4771
4862
|
const complexityRatio = totalExports > 0 ? undocumentedComplexity / totalExports : 0;
|
|
4772
4863
|
const driftRatio = totalExports > 0 ? actualDrift / totalExports : 0;
|
|
4773
|
-
const DRIFT_THRESHOLD = 0.
|
|
4774
|
-
const OUTDATED_THRESHOLD = 0.
|
|
4775
|
-
const COMPLEXITY_THRESHOLD = 0.
|
|
4864
|
+
const DRIFT_THRESHOLD = 0.35;
|
|
4865
|
+
const OUTDATED_THRESHOLD = 0.5;
|
|
4866
|
+
const COMPLEXITY_THRESHOLD = 0.3;
|
|
4776
4867
|
const UNCOMMENTED_THRESHOLD = 0.8;
|
|
4777
4868
|
const driftRisk = Math.min(100, driftRatio / DRIFT_THRESHOLD * 100);
|
|
4778
4869
|
const outdatedRisk = Math.min(
|
|
@@ -5238,6 +5329,7 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
5238
5329
|
}
|
|
5239
5330
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5240
5331
|
0 && (module.exports = {
|
|
5332
|
+
AIReadyConfigSchema,
|
|
5241
5333
|
AnalysisResultSchema,
|
|
5242
5334
|
AnalysisStatus,
|
|
5243
5335
|
AnalysisStatusSchema,
|
|
@@ -5363,6 +5455,10 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
5363
5455
|
loadMergedConfig,
|
|
5364
5456
|
loadScoreHistory,
|
|
5365
5457
|
mergeConfigWithDefaults,
|
|
5458
|
+
normalizeAnalysisResult,
|
|
5459
|
+
normalizeIssue,
|
|
5460
|
+
normalizeMetrics,
|
|
5461
|
+
normalizeSpokeOutput,
|
|
5366
5462
|
normalizeToolName,
|
|
5367
5463
|
parseFileExports,
|
|
5368
5464
|
parseWeightString,
|