@aiready/core 0.23.0 → 0.23.2
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/client.d.mts +4 -0
- package/dist/client.d.ts +4 -0
- package/dist/index.d.mts +62 -12
- package/dist/index.d.ts +62 -12
- package/dist/index.js +404 -222
- package/dist/index.mjs +406 -224
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -74,6 +74,8 @@ __export(index_exports, {
|
|
|
74
74
|
TypeScriptParser: () => TypeScriptParser,
|
|
75
75
|
UnifiedReportSchema: () => UnifiedReportSchema,
|
|
76
76
|
VAGUE_FILE_NAMES: () => VAGUE_FILE_NAMES,
|
|
77
|
+
buildSimpleProviderScore: () => buildSimpleProviderScore,
|
|
78
|
+
buildSpokeOutput: () => buildSpokeOutput,
|
|
77
79
|
calculateAgentGrounding: () => calculateAgentGrounding,
|
|
78
80
|
calculateAiSignalClarity: () => calculateAiSignalClarity,
|
|
79
81
|
calculateBusinessROI: () => calculateBusinessROI,
|
|
@@ -97,6 +99,7 @@ __export(index_exports, {
|
|
|
97
99
|
calculateTestabilityIndex: () => calculateTestabilityIndex,
|
|
98
100
|
calculateTokenBudget: () => calculateTokenBudget,
|
|
99
101
|
clearHistory: () => clearHistory,
|
|
102
|
+
createProvider: () => createProvider,
|
|
100
103
|
emitAnnotation: () => emitAnnotation,
|
|
101
104
|
emitIssuesAsAnnotations: () => emitIssuesAsAnnotations,
|
|
102
105
|
emitProgress: () => emitProgress,
|
|
@@ -105,6 +108,8 @@ __export(index_exports, {
|
|
|
105
108
|
exportHistory: () => exportHistory,
|
|
106
109
|
extractFunctions: () => extractFunctions,
|
|
107
110
|
extractImports: () => extractImports,
|
|
111
|
+
findLatestReport: () => findLatestReport,
|
|
112
|
+
findLatestScanReport: () => findLatestScanReport,
|
|
108
113
|
formatAcceptanceRate: () => formatAcceptanceRate,
|
|
109
114
|
formatCost: () => formatCost,
|
|
110
115
|
formatHours: () => formatHours,
|
|
@@ -131,6 +136,7 @@ __export(index_exports, {
|
|
|
131
136
|
getSupportedLanguages: () => getSupportedLanguages,
|
|
132
137
|
getToolWeight: () => getToolWeight,
|
|
133
138
|
getWasmPath: () => getWasmPath,
|
|
139
|
+
groupIssuesByFile: () => groupIssuesByFile,
|
|
134
140
|
handleCLIError: () => handleCLIError,
|
|
135
141
|
handleJSONOutput: () => handleJSONOutput,
|
|
136
142
|
initTreeSitter: () => initTreeSitter,
|
|
@@ -657,7 +663,8 @@ async function scanFiles(options) {
|
|
|
657
663
|
const files = await (0, import_glob.glob)(include, {
|
|
658
664
|
cwd: rootDir,
|
|
659
665
|
ignore: finalExclude,
|
|
660
|
-
absolute: true
|
|
666
|
+
absolute: true,
|
|
667
|
+
nodir: true
|
|
661
668
|
});
|
|
662
669
|
const gitignoreFiles = await (0, import_glob.glob)("**/.gitignore", {
|
|
663
670
|
cwd: rootDir,
|
|
@@ -861,6 +868,111 @@ function getSeverityColor(severity, chalk) {
|
|
|
861
868
|
return chalk.white;
|
|
862
869
|
}
|
|
863
870
|
}
|
|
871
|
+
function findLatestReport(dirPath) {
|
|
872
|
+
const aireadyDir = (0, import_path2.resolve)(dirPath, ".aiready");
|
|
873
|
+
if (!(0, import_fs2.existsSync)(aireadyDir)) {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
let files = (0, import_fs2.readdirSync)(aireadyDir).filter(
|
|
877
|
+
(f) => f.startsWith("aiready-report-") && f.endsWith(".json")
|
|
878
|
+
);
|
|
879
|
+
if (files.length === 0) {
|
|
880
|
+
files = (0, import_fs2.readdirSync)(aireadyDir).filter(
|
|
881
|
+
(f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
if (files.length === 0) {
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
const sortedFiles = files.map((f) => ({
|
|
888
|
+
name: f,
|
|
889
|
+
path: (0, import_path2.resolve)(aireadyDir, f),
|
|
890
|
+
mtime: (0, import_fs2.statSync)((0, import_path2.resolve)(aireadyDir, f)).mtime
|
|
891
|
+
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
892
|
+
return sortedFiles[0].path;
|
|
893
|
+
}
|
|
894
|
+
function findLatestScanReport(scanReportsDir, reportFilePrefix) {
|
|
895
|
+
try {
|
|
896
|
+
let reportFiles = [];
|
|
897
|
+
if ((0, import_fs2.existsSync)(scanReportsDir)) {
|
|
898
|
+
const files = (0, import_fs2.readdirSync)(scanReportsDir);
|
|
899
|
+
if (files.length > 0) {
|
|
900
|
+
const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
|
|
901
|
+
reportFiles = files.filter((file) => prefixRegex.test(file));
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (reportFiles.length === 0) return null;
|
|
905
|
+
reportFiles.sort((a, b) => {
|
|
906
|
+
const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
|
|
907
|
+
const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
|
|
908
|
+
return idB - idA;
|
|
909
|
+
});
|
|
910
|
+
return (0, import_path2.join)(scanReportsDir, reportFiles[0]);
|
|
911
|
+
} catch (e) {
|
|
912
|
+
console.error("Error while finding latest scan report:", e);
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/utils/provider-utils.ts
|
|
918
|
+
function groupIssuesByFile(issues) {
|
|
919
|
+
const fileIssuesMap = /* @__PURE__ */ new Map();
|
|
920
|
+
for (const issue of issues) {
|
|
921
|
+
const file = issue.location?.file ?? "unknown";
|
|
922
|
+
if (!fileIssuesMap.has(file)) fileIssuesMap.set(file, []);
|
|
923
|
+
fileIssuesMap.get(file).push(issue);
|
|
924
|
+
}
|
|
925
|
+
return Array.from(fileIssuesMap.entries()).map(([fileName, issueList]) => ({
|
|
926
|
+
fileName,
|
|
927
|
+
issues: issueList,
|
|
928
|
+
metrics: {}
|
|
929
|
+
}));
|
|
930
|
+
}
|
|
931
|
+
function buildSimpleProviderScore(toolName, summary, rawData = {}) {
|
|
932
|
+
return {
|
|
933
|
+
toolName,
|
|
934
|
+
score: summary.score ?? 0,
|
|
935
|
+
rawMetrics: { ...summary, ...rawData },
|
|
936
|
+
factors: [],
|
|
937
|
+
recommendations: (summary.recommendations ?? []).map((action) => ({
|
|
938
|
+
action,
|
|
939
|
+
estimatedImpact: 5,
|
|
940
|
+
priority: "medium"
|
|
941
|
+
}))
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function buildSpokeOutput(toolName, version, summary, results, metadata = {}) {
|
|
945
|
+
return SpokeOutputSchema.parse({
|
|
946
|
+
results,
|
|
947
|
+
summary,
|
|
948
|
+
metadata: {
|
|
949
|
+
toolName,
|
|
950
|
+
version,
|
|
951
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
952
|
+
...metadata
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
function createProvider(config) {
|
|
957
|
+
return {
|
|
958
|
+
id: config.id,
|
|
959
|
+
alias: config.alias,
|
|
960
|
+
defaultWeight: config.defaultWeight,
|
|
961
|
+
async analyze(options) {
|
|
962
|
+
const report = await config.analyzeReport(options);
|
|
963
|
+
return buildSpokeOutput(
|
|
964
|
+
config.id,
|
|
965
|
+
config.version,
|
|
966
|
+
config.getSummary(report),
|
|
967
|
+
config.getResults(report),
|
|
968
|
+
config.getMetadata?.(report) ?? {}
|
|
969
|
+
);
|
|
970
|
+
},
|
|
971
|
+
score(output, options) {
|
|
972
|
+
return config.score(output, options);
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
}
|
|
864
976
|
|
|
865
977
|
// src/utils/ast-parser.ts
|
|
866
978
|
var import_typescript_estree2 = require("@typescript-eslint/typescript-estree");
|
|
@@ -988,8 +1100,10 @@ var TypeScriptParser = class {
|
|
|
988
1100
|
// camelCase for variables and functions
|
|
989
1101
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
990
1102
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
991
|
-
// PascalCase for classes
|
|
1103
|
+
// PascalCase for classes, types and interfaces
|
|
992
1104
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1105
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1106
|
+
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
993
1107
|
// UPPER_CASE for constants
|
|
994
1108
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
995
1109
|
// Common exceptions (React hooks, etc.)
|
|
@@ -1288,6 +1402,101 @@ async function setupParser(language) {
|
|
|
1288
1402
|
}
|
|
1289
1403
|
}
|
|
1290
1404
|
|
|
1405
|
+
// src/parsers/metadata-utils.ts
|
|
1406
|
+
function analyzeNodeMetadata(node, code, options) {
|
|
1407
|
+
const metadata = {
|
|
1408
|
+
isPure: true,
|
|
1409
|
+
hasSideEffects: false
|
|
1410
|
+
};
|
|
1411
|
+
try {
|
|
1412
|
+
let prev = node.previousSibling || null;
|
|
1413
|
+
while (prev && /comment/i.test(prev.type)) {
|
|
1414
|
+
const text = prev.text || "";
|
|
1415
|
+
if (text.trim().startsWith("/**") || text.trim().startsWith("/*")) {
|
|
1416
|
+
metadata.documentation = {
|
|
1417
|
+
content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
|
|
1418
|
+
type: "comment"
|
|
1419
|
+
};
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
if (text.trim().startsWith("///")) {
|
|
1423
|
+
metadata.documentation = {
|
|
1424
|
+
content: text.replace(/^\/\/\//, "").trim(),
|
|
1425
|
+
type: "xml-doc"
|
|
1426
|
+
};
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
if (text.trim().startsWith("//")) {
|
|
1430
|
+
metadata.documentation = {
|
|
1431
|
+
content: text.replace(/^\/\//, "").trim(),
|
|
1432
|
+
type: "comment"
|
|
1433
|
+
};
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
prev = prev.previousSibling;
|
|
1437
|
+
}
|
|
1438
|
+
if (node.type === "function_definition") {
|
|
1439
|
+
const body2 = node.childForFieldName ? node.childForFieldName("body") : node.children.find((c) => c.type === "block");
|
|
1440
|
+
if (body2 && body2.children.length > 0) {
|
|
1441
|
+
const firstStmt = body2.children[0];
|
|
1442
|
+
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
1443
|
+
metadata.documentation = {
|
|
1444
|
+
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
1445
|
+
type: "docstring"
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
} catch {
|
|
1451
|
+
}
|
|
1452
|
+
const defaultSignatures = [
|
|
1453
|
+
"console.",
|
|
1454
|
+
"fmt.",
|
|
1455
|
+
"panic(",
|
|
1456
|
+
"os.Exit",
|
|
1457
|
+
"log.",
|
|
1458
|
+
"Console.Write",
|
|
1459
|
+
"File.Write",
|
|
1460
|
+
"System.out",
|
|
1461
|
+
"System.err",
|
|
1462
|
+
"Files.write",
|
|
1463
|
+
"process.exit",
|
|
1464
|
+
"exit("
|
|
1465
|
+
];
|
|
1466
|
+
const signatures = Array.from(
|
|
1467
|
+
/* @__PURE__ */ new Set([...options?.sideEffectSignatures || [], ...defaultSignatures])
|
|
1468
|
+
);
|
|
1469
|
+
const walk = (n) => {
|
|
1470
|
+
try {
|
|
1471
|
+
const t = n.type || "";
|
|
1472
|
+
if (/assign|assignment|assignment_statement|assignment_expression|throw|throw_statement|send_statement|global_statement|nonlocal_statement/i.test(
|
|
1473
|
+
t
|
|
1474
|
+
)) {
|
|
1475
|
+
metadata.isPure = false;
|
|
1476
|
+
metadata.hasSideEffects = true;
|
|
1477
|
+
}
|
|
1478
|
+
const text = n.text || "";
|
|
1479
|
+
for (const s of signatures) {
|
|
1480
|
+
if (text.includes(s)) {
|
|
1481
|
+
metadata.isPure = false;
|
|
1482
|
+
metadata.hasSideEffects = true;
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
1487
|
+
const c = n.child(i);
|
|
1488
|
+
if (c) walk(c);
|
|
1489
|
+
}
|
|
1490
|
+
} catch {
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
const body = node.childForFieldName?.("body") || node.children.find(
|
|
1494
|
+
(c) => /body|block|class_body|declaration_list|function_body/.test(c.type)
|
|
1495
|
+
);
|
|
1496
|
+
if (body) walk(body);
|
|
1497
|
+
return metadata;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1291
1500
|
// src/parsers/python-parser.ts
|
|
1292
1501
|
var PythonParser = class {
|
|
1293
1502
|
constructor() {
|
|
@@ -1310,38 +1519,9 @@ var PythonParser = class {
|
|
|
1310
1519
|
return this.parser.parse(code);
|
|
1311
1520
|
}
|
|
1312
1521
|
analyzeMetadata(node, code) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
};
|
|
1317
|
-
const body = node.childForFieldName("body");
|
|
1318
|
-
if (body && body.children.length > 0) {
|
|
1319
|
-
const firstStmt = body.children[0];
|
|
1320
|
-
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
1321
|
-
metadata.documentation = {
|
|
1322
|
-
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
1323
|
-
type: "docstring"
|
|
1324
|
-
};
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
const walk = (n) => {
|
|
1328
|
-
if (n.type === "global_statement" || n.type === "nonlocal_statement") {
|
|
1329
|
-
metadata.isPure = false;
|
|
1330
|
-
metadata.hasSideEffects = true;
|
|
1331
|
-
}
|
|
1332
|
-
if (n.type === "call") {
|
|
1333
|
-
const functionNode = n.childForFieldName("function");
|
|
1334
|
-
if (functionNode && ["print", "input", "open"].includes(functionNode.text)) {
|
|
1335
|
-
metadata.isPure = false;
|
|
1336
|
-
metadata.hasSideEffects = true;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
for (const child of n.children) {
|
|
1340
|
-
walk(child);
|
|
1341
|
-
}
|
|
1342
|
-
};
|
|
1343
|
-
if (body) walk(body);
|
|
1344
|
-
return metadata;
|
|
1522
|
+
return analyzeNodeMetadata(node, code, {
|
|
1523
|
+
sideEffectSignatures: ["print(", "input(", "open("]
|
|
1524
|
+
});
|
|
1345
1525
|
}
|
|
1346
1526
|
parse(code, filePath) {
|
|
1347
1527
|
if (!this.initialized || !this.parser) {
|
|
@@ -1690,6 +1870,113 @@ var PythonParser = class {
|
|
|
1690
1870
|
}
|
|
1691
1871
|
};
|
|
1692
1872
|
|
|
1873
|
+
// src/parsers/shared-parser-utils.ts
|
|
1874
|
+
var SIDE_EFFECT_KEYWORDS = [
|
|
1875
|
+
"print(",
|
|
1876
|
+
"console.",
|
|
1877
|
+
"System.out",
|
|
1878
|
+
"System.err",
|
|
1879
|
+
"fmt.",
|
|
1880
|
+
"File.Write",
|
|
1881
|
+
"Files.write",
|
|
1882
|
+
"os.Exit",
|
|
1883
|
+
"panic(",
|
|
1884
|
+
"throw ",
|
|
1885
|
+
"Logging.",
|
|
1886
|
+
"log."
|
|
1887
|
+
];
|
|
1888
|
+
function analyzeGeneralMetadata(node, code, options = {}) {
|
|
1889
|
+
const metadata = {
|
|
1890
|
+
isPure: true,
|
|
1891
|
+
hasSideEffects: false
|
|
1892
|
+
};
|
|
1893
|
+
try {
|
|
1894
|
+
let prev = node.previousSibling || null;
|
|
1895
|
+
while (prev && /comment/i.test(prev.type)) {
|
|
1896
|
+
const text = prev.text || "";
|
|
1897
|
+
if (text.trim().startsWith("/**")) {
|
|
1898
|
+
metadata.documentation = {
|
|
1899
|
+
content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
|
|
1900
|
+
type: "jsdoc"
|
|
1901
|
+
};
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
if (text.trim().startsWith("///")) {
|
|
1905
|
+
metadata.documentation = {
|
|
1906
|
+
content: text.replace(/^\/\/\//, "").trim(),
|
|
1907
|
+
type: "xml-doc"
|
|
1908
|
+
};
|
|
1909
|
+
break;
|
|
1910
|
+
}
|
|
1911
|
+
if (text.trim().startsWith("//")) {
|
|
1912
|
+
metadata.documentation = {
|
|
1913
|
+
content: text.replace(/^\/\//, "").trim(),
|
|
1914
|
+
type: "comment"
|
|
1915
|
+
};
|
|
1916
|
+
break;
|
|
1917
|
+
}
|
|
1918
|
+
prev = prev.previousSibling;
|
|
1919
|
+
}
|
|
1920
|
+
} catch {
|
|
1921
|
+
}
|
|
1922
|
+
const signatures = [
|
|
1923
|
+
...SIDE_EFFECT_KEYWORDS,
|
|
1924
|
+
...options.sideEffectSignatures || []
|
|
1925
|
+
];
|
|
1926
|
+
const walk = (n) => {
|
|
1927
|
+
if (/assign|assignment|assignment_statement|assignment_expression/i.test(
|
|
1928
|
+
n.type
|
|
1929
|
+
)) {
|
|
1930
|
+
metadata.isPure = false;
|
|
1931
|
+
metadata.hasSideEffects = true;
|
|
1932
|
+
}
|
|
1933
|
+
const text = n.text;
|
|
1934
|
+
for (const sig of signatures) {
|
|
1935
|
+
if (text.includes(sig)) {
|
|
1936
|
+
metadata.isPure = false;
|
|
1937
|
+
metadata.hasSideEffects = true;
|
|
1938
|
+
break;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
if (!metadata.hasSideEffects) {
|
|
1942
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
1943
|
+
const child = n.child(i);
|
|
1944
|
+
if (child) walk(child);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
walk(node);
|
|
1949
|
+
return metadata;
|
|
1950
|
+
}
|
|
1951
|
+
function extractParameterNames(node) {
|
|
1952
|
+
const params = [];
|
|
1953
|
+
const candidates = [
|
|
1954
|
+
// common field name
|
|
1955
|
+
node.childForFieldName ? node.childForFieldName("parameters") : null,
|
|
1956
|
+
node.childForFieldName ? node.childForFieldName("parameter_list") : null,
|
|
1957
|
+
node.children.find((c) => c.type === "parameter_list") || null,
|
|
1958
|
+
node.children.find((c) => c.type === "parameters") || null,
|
|
1959
|
+
node.children.find((c) => c.type === "formal_parameters") || null,
|
|
1960
|
+
node.children.find((c) => c.type === "formal_parameter") || null
|
|
1961
|
+
];
|
|
1962
|
+
const list = candidates.find(Boolean);
|
|
1963
|
+
if (!list) return params;
|
|
1964
|
+
for (const child of list.children) {
|
|
1965
|
+
if (!child) continue;
|
|
1966
|
+
const id = child.childForFieldName?.("name") || child.children.find(
|
|
1967
|
+
(c) => [
|
|
1968
|
+
"identifier",
|
|
1969
|
+
"variable_name",
|
|
1970
|
+
"name",
|
|
1971
|
+
"parameter",
|
|
1972
|
+
"formal_parameter"
|
|
1973
|
+
].includes(c.type)
|
|
1974
|
+
) || (child.type === "identifier" ? child : void 0);
|
|
1975
|
+
if (id && typeof id.text === "string") params.push(id.text);
|
|
1976
|
+
}
|
|
1977
|
+
return params;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1693
1980
|
// src/parsers/java-parser.ts
|
|
1694
1981
|
var JavaParser = class {
|
|
1695
1982
|
constructor() {
|
|
@@ -1712,47 +1999,14 @@ var JavaParser = class {
|
|
|
1712
1999
|
return this.parser.parse(code);
|
|
1713
2000
|
}
|
|
1714
2001
|
analyzeMetadata(node, code) {
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
content: prev.text.replace(/[/*]/g, "").trim(),
|
|
1724
|
-
type: "xml-doc"
|
|
1725
|
-
// Using xml-doc as a catch-all for structured or we can add 'javadoc'
|
|
1726
|
-
};
|
|
1727
|
-
break;
|
|
1728
|
-
}
|
|
1729
|
-
prev = prev.previousSibling;
|
|
1730
|
-
}
|
|
1731
|
-
const walk = (n) => {
|
|
1732
|
-
if (n.type === "assignment_expression") {
|
|
1733
|
-
metadata.isPure = false;
|
|
1734
|
-
metadata.hasSideEffects = true;
|
|
1735
|
-
}
|
|
1736
|
-
if (n.type === "method_invocation") {
|
|
1737
|
-
const text = n.text;
|
|
1738
|
-
if (text.includes("System.out.print") || text.includes("System.err.print") || text.includes("Files.write")) {
|
|
1739
|
-
metadata.isPure = false;
|
|
1740
|
-
metadata.hasSideEffects = true;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
if (n.type === "throw_statement") {
|
|
1744
|
-
metadata.isPure = false;
|
|
1745
|
-
metadata.hasSideEffects = true;
|
|
1746
|
-
}
|
|
1747
|
-
for (const child of n.children) {
|
|
1748
|
-
walk(child);
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
const body = node.children.find(
|
|
1752
|
-
(c) => c.type === "block" || c.type === "class_body"
|
|
1753
|
-
);
|
|
1754
|
-
if (body) walk(body);
|
|
1755
|
-
return metadata;
|
|
2002
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2003
|
+
sideEffectSignatures: [
|
|
2004
|
+
"System.out",
|
|
2005
|
+
"System.err",
|
|
2006
|
+
"Files.write",
|
|
2007
|
+
"Logging."
|
|
2008
|
+
]
|
|
2009
|
+
});
|
|
1756
2010
|
}
|
|
1757
2011
|
parse(code, filePath) {
|
|
1758
2012
|
if (!this.initialized || !this.parser) {
|
|
@@ -1852,7 +2106,7 @@ var JavaParser = class {
|
|
|
1852
2106
|
const imports = [];
|
|
1853
2107
|
for (const node of rootNode.children) {
|
|
1854
2108
|
if (node.type === "import_declaration") {
|
|
1855
|
-
|
|
2109
|
+
const sourceArr = [];
|
|
1856
2110
|
let isStatic = false;
|
|
1857
2111
|
let isWildcard = false;
|
|
1858
2112
|
for (const child of node.children) {
|
|
@@ -1950,14 +2204,7 @@ var JavaParser = class {
|
|
|
1950
2204
|
}
|
|
1951
2205
|
}
|
|
1952
2206
|
extractParameters(node) {
|
|
1953
|
-
|
|
1954
|
-
(c) => c.type === "formal_parameters"
|
|
1955
|
-
);
|
|
1956
|
-
if (!paramsNode) return [];
|
|
1957
|
-
return paramsNode.children.filter((c) => c.type === "formal_parameter").map((c) => {
|
|
1958
|
-
const idNode = c.children.find((child) => child.type === "identifier");
|
|
1959
|
-
return idNode ? idNode.text : "unknown";
|
|
1960
|
-
});
|
|
2207
|
+
return extractParameterNames(node);
|
|
1961
2208
|
}
|
|
1962
2209
|
getNamingConventions() {
|
|
1963
2210
|
return {
|
|
@@ -1995,47 +2242,9 @@ var CSharpParser = class {
|
|
|
1995
2242
|
return this.parser.parse(code);
|
|
1996
2243
|
}
|
|
1997
2244
|
analyzeMetadata(node, code) {
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
};
|
|
2002
|
-
let prev = node.previousSibling;
|
|
2003
|
-
while (prev && (prev.type === "comment" || prev.type === "triple_slash_comment")) {
|
|
2004
|
-
if (prev.text.trim().startsWith("///") || prev.type === "triple_slash_comment") {
|
|
2005
|
-
metadata.documentation = {
|
|
2006
|
-
content: prev.text.replace("///", "").trim(),
|
|
2007
|
-
type: "xml-doc"
|
|
2008
|
-
};
|
|
2009
|
-
break;
|
|
2010
|
-
}
|
|
2011
|
-
prev = prev.previousSibling;
|
|
2012
|
-
}
|
|
2013
|
-
const walk = (n) => {
|
|
2014
|
-
if (n.type === "assignment_expression") {
|
|
2015
|
-
metadata.isPure = false;
|
|
2016
|
-
metadata.hasSideEffects = true;
|
|
2017
|
-
}
|
|
2018
|
-
if (n.type === "invocation_expression") {
|
|
2019
|
-
const text = n.text;
|
|
2020
|
-
if (text.includes("Console.Write") || text.includes("File.Write") || text.includes("Log.")) {
|
|
2021
|
-
metadata.isPure = false;
|
|
2022
|
-
metadata.hasSideEffects = true;
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
if (n.type === "throw_statement") {
|
|
2026
|
-
metadata.isPure = false;
|
|
2027
|
-
metadata.hasSideEffects = true;
|
|
2028
|
-
}
|
|
2029
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
2030
|
-
const child = n.child(i);
|
|
2031
|
-
if (child) walk(child);
|
|
2032
|
-
}
|
|
2033
|
-
};
|
|
2034
|
-
const body = node.children.find(
|
|
2035
|
-
(c) => c.type === "block" || c.type === "declaration_list"
|
|
2036
|
-
);
|
|
2037
|
-
if (body) walk(body);
|
|
2038
|
-
return metadata;
|
|
2245
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2246
|
+
sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
|
|
2247
|
+
});
|
|
2039
2248
|
}
|
|
2040
2249
|
parse(code, filePath) {
|
|
2041
2250
|
if (!this.initialized || !this.parser) {
|
|
@@ -2240,19 +2449,7 @@ var CSharpParser = class {
|
|
|
2240
2449
|
return modifiers;
|
|
2241
2450
|
}
|
|
2242
2451
|
extractParameters(node) {
|
|
2243
|
-
|
|
2244
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2245
|
-
if (parameterList) {
|
|
2246
|
-
for (const param of parameterList.children) {
|
|
2247
|
-
if (param.type === "parameter") {
|
|
2248
|
-
const nameNode = param.childForFieldName("name") || param.children.find((c) => c.type === "identifier");
|
|
2249
|
-
if (nameNode) {
|
|
2250
|
-
params.push(nameNode.text);
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
return params;
|
|
2452
|
+
return extractParameterNames(node);
|
|
2256
2453
|
}
|
|
2257
2454
|
getNamingConventions() {
|
|
2258
2455
|
return {
|
|
@@ -2289,40 +2486,9 @@ var GoParser = class {
|
|
|
2289
2486
|
return this.parser.parse(code);
|
|
2290
2487
|
}
|
|
2291
2488
|
analyzeMetadata(node, code) {
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
};
|
|
2296
|
-
let prev = node.previousSibling;
|
|
2297
|
-
while (prev && prev.type === "comment") {
|
|
2298
|
-
metadata.documentation = {
|
|
2299
|
-
content: prev.text.replace(/\/\/|\/\*|\*\//g, "").trim(),
|
|
2300
|
-
type: "comment"
|
|
2301
|
-
};
|
|
2302
|
-
break;
|
|
2303
|
-
}
|
|
2304
|
-
const walk = (n) => {
|
|
2305
|
-
if (n.type === "send_statement" || n.type === "expression_statement" && n.text.includes("<-")) {
|
|
2306
|
-
metadata.isPure = false;
|
|
2307
|
-
metadata.hasSideEffects = true;
|
|
2308
|
-
}
|
|
2309
|
-
if (n.type === "assignment_statement" || n.type === "short_var_declaration") {
|
|
2310
|
-
}
|
|
2311
|
-
if (n.type === "call_expression") {
|
|
2312
|
-
const text = n.text;
|
|
2313
|
-
if (text.includes("fmt.Print") || text.includes("os.Exit") || text.includes("panic(") || text.includes("log.")) {
|
|
2314
|
-
metadata.isPure = false;
|
|
2315
|
-
metadata.hasSideEffects = true;
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
2319
|
-
const child = n.child(i);
|
|
2320
|
-
if (child) walk(child);
|
|
2321
|
-
}
|
|
2322
|
-
};
|
|
2323
|
-
const body = node.childForFieldName("body");
|
|
2324
|
-
if (body) walk(body);
|
|
2325
|
-
return metadata;
|
|
2489
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2490
|
+
sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
|
|
2491
|
+
});
|
|
2326
2492
|
}
|
|
2327
2493
|
parse(code, filePath) {
|
|
2328
2494
|
if (!this.initialized || !this.parser) {
|
|
@@ -2544,17 +2710,7 @@ var GoParser = class {
|
|
|
2544
2710
|
return exports2;
|
|
2545
2711
|
}
|
|
2546
2712
|
extractParameters(node) {
|
|
2547
|
-
|
|
2548
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2549
|
-
if (parameterList) {
|
|
2550
|
-
for (const param of parameterList.children) {
|
|
2551
|
-
if (param.type === "parameter_declaration") {
|
|
2552
|
-
const names = param.children.filter((c) => c.type === "identifier");
|
|
2553
|
-
names.forEach((n) => params.push(n.text));
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
return params;
|
|
2713
|
+
return extractParameterNames(node);
|
|
2558
2714
|
}
|
|
2559
2715
|
getNamingConventions() {
|
|
2560
2716
|
return {
|
|
@@ -2904,34 +3060,47 @@ var CONFIG_FILES = [
|
|
|
2904
3060
|
async function loadConfig(rootDir) {
|
|
2905
3061
|
let currentDir = (0, import_path3.resolve)(rootDir);
|
|
2906
3062
|
while (true) {
|
|
3063
|
+
const foundConfigs = [];
|
|
2907
3064
|
for (const configFile of CONFIG_FILES) {
|
|
3065
|
+
if ((0, import_fs3.existsSync)((0, import_path3.join)(currentDir, configFile))) {
|
|
3066
|
+
foundConfigs.push(configFile);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
if (foundConfigs.length > 0) {
|
|
3070
|
+
if (foundConfigs.length > 1) {
|
|
3071
|
+
console.warn(
|
|
3072
|
+
`\u26A0\uFE0F Multiple configuration files found in ${currentDir}: ${foundConfigs.join(
|
|
3073
|
+
", "
|
|
3074
|
+
)}. Using ${foundConfigs[0]}.`
|
|
3075
|
+
);
|
|
3076
|
+
} else {
|
|
3077
|
+
}
|
|
3078
|
+
const configFile = foundConfigs[0];
|
|
2908
3079
|
const configPath = (0, import_path3.join)(currentDir, configFile);
|
|
2909
|
-
|
|
3080
|
+
try {
|
|
3081
|
+
let config;
|
|
3082
|
+
if (configFile.endsWith(".js")) {
|
|
3083
|
+
const fileUrl = (0, import_url.pathToFileURL)(configPath).href;
|
|
3084
|
+
const module2 = await import(`${fileUrl}?t=${Date.now()}`);
|
|
3085
|
+
config = module2.default || module2;
|
|
3086
|
+
} else {
|
|
3087
|
+
const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
|
|
3088
|
+
config = JSON.parse(content);
|
|
3089
|
+
}
|
|
3090
|
+
if (typeof config !== "object" || config === null) {
|
|
3091
|
+
throw new Error("Config must be an object");
|
|
3092
|
+
}
|
|
3093
|
+
return config;
|
|
3094
|
+
} catch (error) {
|
|
3095
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3096
|
+
const e = new Error(
|
|
3097
|
+
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
3098
|
+
);
|
|
2910
3099
|
try {
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
const fileUrl = (0, import_url.pathToFileURL)(configPath).href;
|
|
2914
|
-
const module2 = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2915
|
-
config = module2.default || module2;
|
|
2916
|
-
} else {
|
|
2917
|
-
const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
|
|
2918
|
-
config = JSON.parse(content);
|
|
2919
|
-
}
|
|
2920
|
-
if (typeof config !== "object" || config === null) {
|
|
2921
|
-
throw new Error("Config must be an object");
|
|
2922
|
-
}
|
|
2923
|
-
return config;
|
|
2924
|
-
} catch (error) {
|
|
2925
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2926
|
-
const e = new Error(
|
|
2927
|
-
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2928
|
-
);
|
|
2929
|
-
try {
|
|
2930
|
-
e.cause = error instanceof Error ? error : void 0;
|
|
2931
|
-
} catch {
|
|
2932
|
-
}
|
|
2933
|
-
throw e;
|
|
3100
|
+
e.cause = error instanceof Error ? error : void 0;
|
|
3101
|
+
} catch {
|
|
2934
3102
|
}
|
|
3103
|
+
throw e;
|
|
2935
3104
|
}
|
|
2936
3105
|
}
|
|
2937
3106
|
const parent = (0, import_path3.dirname)(currentDir);
|
|
@@ -3846,6 +4015,24 @@ function collectFutureProofRecommendations(params) {
|
|
|
3846
4015
|
}
|
|
3847
4016
|
return recommendations;
|
|
3848
4017
|
}
|
|
4018
|
+
function collectBaseFutureProofRecommendations(params) {
|
|
4019
|
+
const recommendations = [];
|
|
4020
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
4021
|
+
recommendations.push({
|
|
4022
|
+
action: rec,
|
|
4023
|
+
estimatedImpact: 5,
|
|
4024
|
+
priority: "medium"
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4027
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
4028
|
+
recommendations.push({
|
|
4029
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
4030
|
+
estimatedImpact: 8,
|
|
4031
|
+
priority: "high"
|
|
4032
|
+
});
|
|
4033
|
+
}
|
|
4034
|
+
return recommendations;
|
|
4035
|
+
}
|
|
3849
4036
|
|
|
3850
4037
|
// src/metrics/cognitive-load.ts
|
|
3851
4038
|
function calculateCognitiveLoad(params) {
|
|
@@ -4536,21 +4723,10 @@ function calculateFutureProofScore(params) {
|
|
|
4536
4723
|
description: params.conceptCohesion.rating
|
|
4537
4724
|
}
|
|
4538
4725
|
];
|
|
4539
|
-
const recommendations =
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
estimatedImpact: 5,
|
|
4544
|
-
priority: "medium"
|
|
4545
|
-
});
|
|
4546
|
-
}
|
|
4547
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
4548
|
-
recommendations.push({
|
|
4549
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
4550
|
-
estimatedImpact: 8,
|
|
4551
|
-
priority: "high"
|
|
4552
|
-
});
|
|
4553
|
-
}
|
|
4726
|
+
const recommendations = collectBaseFutureProofRecommendations({
|
|
4727
|
+
patternEntropy: params.patternEntropy,
|
|
4728
|
+
conceptCohesion: params.conceptCohesion
|
|
4729
|
+
});
|
|
4554
4730
|
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
4555
4731
|
return {
|
|
4556
4732
|
toolName: "future-proof",
|
|
@@ -4883,6 +5059,8 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4883
5059
|
TypeScriptParser,
|
|
4884
5060
|
UnifiedReportSchema,
|
|
4885
5061
|
VAGUE_FILE_NAMES,
|
|
5062
|
+
buildSimpleProviderScore,
|
|
5063
|
+
buildSpokeOutput,
|
|
4886
5064
|
calculateAgentGrounding,
|
|
4887
5065
|
calculateAiSignalClarity,
|
|
4888
5066
|
calculateBusinessROI,
|
|
@@ -4906,6 +5084,7 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4906
5084
|
calculateTestabilityIndex,
|
|
4907
5085
|
calculateTokenBudget,
|
|
4908
5086
|
clearHistory,
|
|
5087
|
+
createProvider,
|
|
4909
5088
|
emitAnnotation,
|
|
4910
5089
|
emitIssuesAsAnnotations,
|
|
4911
5090
|
emitProgress,
|
|
@@ -4914,6 +5093,8 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4914
5093
|
exportHistory,
|
|
4915
5094
|
extractFunctions,
|
|
4916
5095
|
extractImports,
|
|
5096
|
+
findLatestReport,
|
|
5097
|
+
findLatestScanReport,
|
|
4917
5098
|
formatAcceptanceRate,
|
|
4918
5099
|
formatCost,
|
|
4919
5100
|
formatHours,
|
|
@@ -4940,6 +5121,7 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4940
5121
|
getSupportedLanguages,
|
|
4941
5122
|
getToolWeight,
|
|
4942
5123
|
getWasmPath,
|
|
5124
|
+
groupIssuesByFile,
|
|
4943
5125
|
handleCLIError,
|
|
4944
5126
|
handleJSONOutput,
|
|
4945
5127
|
initTreeSitter,
|