@aiready/core 0.23.1 → 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 +402 -221
- package/dist/index.mjs +404 -223
- 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,
|
|
@@ -862,6 +868,111 @@ function getSeverityColor(severity, chalk) {
|
|
|
862
868
|
return chalk.white;
|
|
863
869
|
}
|
|
864
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
|
+
}
|
|
865
976
|
|
|
866
977
|
// src/utils/ast-parser.ts
|
|
867
978
|
var import_typescript_estree2 = require("@typescript-eslint/typescript-estree");
|
|
@@ -989,8 +1100,10 @@ var TypeScriptParser = class {
|
|
|
989
1100
|
// camelCase for variables and functions
|
|
990
1101
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
991
1102
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
992
|
-
// PascalCase for classes
|
|
1103
|
+
// PascalCase for classes, types and interfaces
|
|
993
1104
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1105
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
1106
|
+
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
994
1107
|
// UPPER_CASE for constants
|
|
995
1108
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
996
1109
|
// Common exceptions (React hooks, etc.)
|
|
@@ -1289,6 +1402,101 @@ async function setupParser(language) {
|
|
|
1289
1402
|
}
|
|
1290
1403
|
}
|
|
1291
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
|
+
|
|
1292
1500
|
// src/parsers/python-parser.ts
|
|
1293
1501
|
var PythonParser = class {
|
|
1294
1502
|
constructor() {
|
|
@@ -1311,38 +1519,9 @@ var PythonParser = class {
|
|
|
1311
1519
|
return this.parser.parse(code);
|
|
1312
1520
|
}
|
|
1313
1521
|
analyzeMetadata(node, code) {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
};
|
|
1318
|
-
const body = node.childForFieldName("body");
|
|
1319
|
-
if (body && body.children.length > 0) {
|
|
1320
|
-
const firstStmt = body.children[0];
|
|
1321
|
-
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
1322
|
-
metadata.documentation = {
|
|
1323
|
-
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
1324
|
-
type: "docstring"
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
const walk = (n) => {
|
|
1329
|
-
if (n.type === "global_statement" || n.type === "nonlocal_statement") {
|
|
1330
|
-
metadata.isPure = false;
|
|
1331
|
-
metadata.hasSideEffects = true;
|
|
1332
|
-
}
|
|
1333
|
-
if (n.type === "call") {
|
|
1334
|
-
const functionNode = n.childForFieldName("function");
|
|
1335
|
-
if (functionNode && ["print", "input", "open"].includes(functionNode.text)) {
|
|
1336
|
-
metadata.isPure = false;
|
|
1337
|
-
metadata.hasSideEffects = true;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
for (const child of n.children) {
|
|
1341
|
-
walk(child);
|
|
1342
|
-
}
|
|
1343
|
-
};
|
|
1344
|
-
if (body) walk(body);
|
|
1345
|
-
return metadata;
|
|
1522
|
+
return analyzeNodeMetadata(node, code, {
|
|
1523
|
+
sideEffectSignatures: ["print(", "input(", "open("]
|
|
1524
|
+
});
|
|
1346
1525
|
}
|
|
1347
1526
|
parse(code, filePath) {
|
|
1348
1527
|
if (!this.initialized || !this.parser) {
|
|
@@ -1691,6 +1870,113 @@ var PythonParser = class {
|
|
|
1691
1870
|
}
|
|
1692
1871
|
};
|
|
1693
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
|
+
|
|
1694
1980
|
// src/parsers/java-parser.ts
|
|
1695
1981
|
var JavaParser = class {
|
|
1696
1982
|
constructor() {
|
|
@@ -1713,47 +1999,14 @@ var JavaParser = class {
|
|
|
1713
1999
|
return this.parser.parse(code);
|
|
1714
2000
|
}
|
|
1715
2001
|
analyzeMetadata(node, code) {
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
content: prev.text.replace(/[/*]/g, "").trim(),
|
|
1725
|
-
type: "xml-doc"
|
|
1726
|
-
// Using xml-doc as a catch-all for structured or we can add 'javadoc'
|
|
1727
|
-
};
|
|
1728
|
-
break;
|
|
1729
|
-
}
|
|
1730
|
-
prev = prev.previousSibling;
|
|
1731
|
-
}
|
|
1732
|
-
const walk = (n) => {
|
|
1733
|
-
if (n.type === "assignment_expression") {
|
|
1734
|
-
metadata.isPure = false;
|
|
1735
|
-
metadata.hasSideEffects = true;
|
|
1736
|
-
}
|
|
1737
|
-
if (n.type === "method_invocation") {
|
|
1738
|
-
const text = n.text;
|
|
1739
|
-
if (text.includes("System.out.print") || text.includes("System.err.print") || text.includes("Files.write")) {
|
|
1740
|
-
metadata.isPure = false;
|
|
1741
|
-
metadata.hasSideEffects = true;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
if (n.type === "throw_statement") {
|
|
1745
|
-
metadata.isPure = false;
|
|
1746
|
-
metadata.hasSideEffects = true;
|
|
1747
|
-
}
|
|
1748
|
-
for (const child of n.children) {
|
|
1749
|
-
walk(child);
|
|
1750
|
-
}
|
|
1751
|
-
};
|
|
1752
|
-
const body = node.children.find(
|
|
1753
|
-
(c) => c.type === "block" || c.type === "class_body"
|
|
1754
|
-
);
|
|
1755
|
-
if (body) walk(body);
|
|
1756
|
-
return metadata;
|
|
2002
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2003
|
+
sideEffectSignatures: [
|
|
2004
|
+
"System.out",
|
|
2005
|
+
"System.err",
|
|
2006
|
+
"Files.write",
|
|
2007
|
+
"Logging."
|
|
2008
|
+
]
|
|
2009
|
+
});
|
|
1757
2010
|
}
|
|
1758
2011
|
parse(code, filePath) {
|
|
1759
2012
|
if (!this.initialized || !this.parser) {
|
|
@@ -1853,7 +2106,7 @@ var JavaParser = class {
|
|
|
1853
2106
|
const imports = [];
|
|
1854
2107
|
for (const node of rootNode.children) {
|
|
1855
2108
|
if (node.type === "import_declaration") {
|
|
1856
|
-
|
|
2109
|
+
const sourceArr = [];
|
|
1857
2110
|
let isStatic = false;
|
|
1858
2111
|
let isWildcard = false;
|
|
1859
2112
|
for (const child of node.children) {
|
|
@@ -1951,14 +2204,7 @@ var JavaParser = class {
|
|
|
1951
2204
|
}
|
|
1952
2205
|
}
|
|
1953
2206
|
extractParameters(node) {
|
|
1954
|
-
|
|
1955
|
-
(c) => c.type === "formal_parameters"
|
|
1956
|
-
);
|
|
1957
|
-
if (!paramsNode) return [];
|
|
1958
|
-
return paramsNode.children.filter((c) => c.type === "formal_parameter").map((c) => {
|
|
1959
|
-
const idNode = c.children.find((child) => child.type === "identifier");
|
|
1960
|
-
return idNode ? idNode.text : "unknown";
|
|
1961
|
-
});
|
|
2207
|
+
return extractParameterNames(node);
|
|
1962
2208
|
}
|
|
1963
2209
|
getNamingConventions() {
|
|
1964
2210
|
return {
|
|
@@ -1996,47 +2242,9 @@ var CSharpParser = class {
|
|
|
1996
2242
|
return this.parser.parse(code);
|
|
1997
2243
|
}
|
|
1998
2244
|
analyzeMetadata(node, code) {
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
};
|
|
2003
|
-
let prev = node.previousSibling;
|
|
2004
|
-
while (prev && (prev.type === "comment" || prev.type === "triple_slash_comment")) {
|
|
2005
|
-
if (prev.text.trim().startsWith("///") || prev.type === "triple_slash_comment") {
|
|
2006
|
-
metadata.documentation = {
|
|
2007
|
-
content: prev.text.replace("///", "").trim(),
|
|
2008
|
-
type: "xml-doc"
|
|
2009
|
-
};
|
|
2010
|
-
break;
|
|
2011
|
-
}
|
|
2012
|
-
prev = prev.previousSibling;
|
|
2013
|
-
}
|
|
2014
|
-
const walk = (n) => {
|
|
2015
|
-
if (n.type === "assignment_expression") {
|
|
2016
|
-
metadata.isPure = false;
|
|
2017
|
-
metadata.hasSideEffects = true;
|
|
2018
|
-
}
|
|
2019
|
-
if (n.type === "invocation_expression") {
|
|
2020
|
-
const text = n.text;
|
|
2021
|
-
if (text.includes("Console.Write") || text.includes("File.Write") || text.includes("Log.")) {
|
|
2022
|
-
metadata.isPure = false;
|
|
2023
|
-
metadata.hasSideEffects = true;
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
if (n.type === "throw_statement") {
|
|
2027
|
-
metadata.isPure = false;
|
|
2028
|
-
metadata.hasSideEffects = true;
|
|
2029
|
-
}
|
|
2030
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
2031
|
-
const child = n.child(i);
|
|
2032
|
-
if (child) walk(child);
|
|
2033
|
-
}
|
|
2034
|
-
};
|
|
2035
|
-
const body = node.children.find(
|
|
2036
|
-
(c) => c.type === "block" || c.type === "declaration_list"
|
|
2037
|
-
);
|
|
2038
|
-
if (body) walk(body);
|
|
2039
|
-
return metadata;
|
|
2245
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2246
|
+
sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
|
|
2247
|
+
});
|
|
2040
2248
|
}
|
|
2041
2249
|
parse(code, filePath) {
|
|
2042
2250
|
if (!this.initialized || !this.parser) {
|
|
@@ -2241,19 +2449,7 @@ var CSharpParser = class {
|
|
|
2241
2449
|
return modifiers;
|
|
2242
2450
|
}
|
|
2243
2451
|
extractParameters(node) {
|
|
2244
|
-
|
|
2245
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2246
|
-
if (parameterList) {
|
|
2247
|
-
for (const param of parameterList.children) {
|
|
2248
|
-
if (param.type === "parameter") {
|
|
2249
|
-
const nameNode = param.childForFieldName("name") || param.children.find((c) => c.type === "identifier");
|
|
2250
|
-
if (nameNode) {
|
|
2251
|
-
params.push(nameNode.text);
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
return params;
|
|
2452
|
+
return extractParameterNames(node);
|
|
2257
2453
|
}
|
|
2258
2454
|
getNamingConventions() {
|
|
2259
2455
|
return {
|
|
@@ -2290,40 +2486,9 @@ var GoParser = class {
|
|
|
2290
2486
|
return this.parser.parse(code);
|
|
2291
2487
|
}
|
|
2292
2488
|
analyzeMetadata(node, code) {
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
};
|
|
2297
|
-
let prev = node.previousSibling;
|
|
2298
|
-
while (prev && prev.type === "comment") {
|
|
2299
|
-
metadata.documentation = {
|
|
2300
|
-
content: prev.text.replace(/\/\/|\/\*|\*\//g, "").trim(),
|
|
2301
|
-
type: "comment"
|
|
2302
|
-
};
|
|
2303
|
-
break;
|
|
2304
|
-
}
|
|
2305
|
-
const walk = (n) => {
|
|
2306
|
-
if (n.type === "send_statement" || n.type === "expression_statement" && n.text.includes("<-")) {
|
|
2307
|
-
metadata.isPure = false;
|
|
2308
|
-
metadata.hasSideEffects = true;
|
|
2309
|
-
}
|
|
2310
|
-
if (n.type === "assignment_statement" || n.type === "short_var_declaration") {
|
|
2311
|
-
}
|
|
2312
|
-
if (n.type === "call_expression") {
|
|
2313
|
-
const text = n.text;
|
|
2314
|
-
if (text.includes("fmt.Print") || text.includes("os.Exit") || text.includes("panic(") || text.includes("log.")) {
|
|
2315
|
-
metadata.isPure = false;
|
|
2316
|
-
metadata.hasSideEffects = true;
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
2320
|
-
const child = n.child(i);
|
|
2321
|
-
if (child) walk(child);
|
|
2322
|
-
}
|
|
2323
|
-
};
|
|
2324
|
-
const body = node.childForFieldName("body");
|
|
2325
|
-
if (body) walk(body);
|
|
2326
|
-
return metadata;
|
|
2489
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2490
|
+
sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
|
|
2491
|
+
});
|
|
2327
2492
|
}
|
|
2328
2493
|
parse(code, filePath) {
|
|
2329
2494
|
if (!this.initialized || !this.parser) {
|
|
@@ -2545,17 +2710,7 @@ var GoParser = class {
|
|
|
2545
2710
|
return exports2;
|
|
2546
2711
|
}
|
|
2547
2712
|
extractParameters(node) {
|
|
2548
|
-
|
|
2549
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2550
|
-
if (parameterList) {
|
|
2551
|
-
for (const param of parameterList.children) {
|
|
2552
|
-
if (param.type === "parameter_declaration") {
|
|
2553
|
-
const names = param.children.filter((c) => c.type === "identifier");
|
|
2554
|
-
names.forEach((n) => params.push(n.text));
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
return params;
|
|
2713
|
+
return extractParameterNames(node);
|
|
2559
2714
|
}
|
|
2560
2715
|
getNamingConventions() {
|
|
2561
2716
|
return {
|
|
@@ -2905,34 +3060,47 @@ var CONFIG_FILES = [
|
|
|
2905
3060
|
async function loadConfig(rootDir) {
|
|
2906
3061
|
let currentDir = (0, import_path3.resolve)(rootDir);
|
|
2907
3062
|
while (true) {
|
|
3063
|
+
const foundConfigs = [];
|
|
2908
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];
|
|
2909
3079
|
const configPath = (0, import_path3.join)(currentDir, configFile);
|
|
2910
|
-
|
|
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
|
+
);
|
|
2911
3099
|
try {
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
const fileUrl = (0, import_url.pathToFileURL)(configPath).href;
|
|
2915
|
-
const module2 = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2916
|
-
config = module2.default || module2;
|
|
2917
|
-
} else {
|
|
2918
|
-
const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
|
|
2919
|
-
config = JSON.parse(content);
|
|
2920
|
-
}
|
|
2921
|
-
if (typeof config !== "object" || config === null) {
|
|
2922
|
-
throw new Error("Config must be an object");
|
|
2923
|
-
}
|
|
2924
|
-
return config;
|
|
2925
|
-
} catch (error) {
|
|
2926
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2927
|
-
const e = new Error(
|
|
2928
|
-
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2929
|
-
);
|
|
2930
|
-
try {
|
|
2931
|
-
e.cause = error instanceof Error ? error : void 0;
|
|
2932
|
-
} catch {
|
|
2933
|
-
}
|
|
2934
|
-
throw e;
|
|
3100
|
+
e.cause = error instanceof Error ? error : void 0;
|
|
3101
|
+
} catch {
|
|
2935
3102
|
}
|
|
3103
|
+
throw e;
|
|
2936
3104
|
}
|
|
2937
3105
|
}
|
|
2938
3106
|
const parent = (0, import_path3.dirname)(currentDir);
|
|
@@ -3847,6 +4015,24 @@ function collectFutureProofRecommendations(params) {
|
|
|
3847
4015
|
}
|
|
3848
4016
|
return recommendations;
|
|
3849
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
|
+
}
|
|
3850
4036
|
|
|
3851
4037
|
// src/metrics/cognitive-load.ts
|
|
3852
4038
|
function calculateCognitiveLoad(params) {
|
|
@@ -4537,21 +4723,10 @@ function calculateFutureProofScore(params) {
|
|
|
4537
4723
|
description: params.conceptCohesion.rating
|
|
4538
4724
|
}
|
|
4539
4725
|
];
|
|
4540
|
-
const recommendations =
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
estimatedImpact: 5,
|
|
4545
|
-
priority: "medium"
|
|
4546
|
-
});
|
|
4547
|
-
}
|
|
4548
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
4549
|
-
recommendations.push({
|
|
4550
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
4551
|
-
estimatedImpact: 8,
|
|
4552
|
-
priority: "high"
|
|
4553
|
-
});
|
|
4554
|
-
}
|
|
4726
|
+
const recommendations = collectBaseFutureProofRecommendations({
|
|
4727
|
+
patternEntropy: params.patternEntropy,
|
|
4728
|
+
conceptCohesion: params.conceptCohesion
|
|
4729
|
+
});
|
|
4555
4730
|
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
4556
4731
|
return {
|
|
4557
4732
|
toolName: "future-proof",
|
|
@@ -4884,6 +5059,8 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4884
5059
|
TypeScriptParser,
|
|
4885
5060
|
UnifiedReportSchema,
|
|
4886
5061
|
VAGUE_FILE_NAMES,
|
|
5062
|
+
buildSimpleProviderScore,
|
|
5063
|
+
buildSpokeOutput,
|
|
4887
5064
|
calculateAgentGrounding,
|
|
4888
5065
|
calculateAiSignalClarity,
|
|
4889
5066
|
calculateBusinessROI,
|
|
@@ -4907,6 +5084,7 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4907
5084
|
calculateTestabilityIndex,
|
|
4908
5085
|
calculateTokenBudget,
|
|
4909
5086
|
clearHistory,
|
|
5087
|
+
createProvider,
|
|
4910
5088
|
emitAnnotation,
|
|
4911
5089
|
emitIssuesAsAnnotations,
|
|
4912
5090
|
emitProgress,
|
|
@@ -4915,6 +5093,8 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4915
5093
|
exportHistory,
|
|
4916
5094
|
extractFunctions,
|
|
4917
5095
|
extractImports,
|
|
5096
|
+
findLatestReport,
|
|
5097
|
+
findLatestScanReport,
|
|
4918
5098
|
formatAcceptanceRate,
|
|
4919
5099
|
formatCost,
|
|
4920
5100
|
formatHours,
|
|
@@ -4941,6 +5121,7 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4941
5121
|
getSupportedLanguages,
|
|
4942
5122
|
getToolWeight,
|
|
4943
5123
|
getWasmPath,
|
|
5124
|
+
groupIssuesByFile,
|
|
4944
5125
|
handleCLIError,
|
|
4945
5126
|
handleJSONOutput,
|
|
4946
5127
|
initTreeSitter,
|