@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.mjs
CHANGED
|
@@ -321,7 +321,8 @@ async function scanFiles(options) {
|
|
|
321
321
|
const files = await glob(include, {
|
|
322
322
|
cwd: rootDir,
|
|
323
323
|
ignore: finalExclude,
|
|
324
|
-
absolute: true
|
|
324
|
+
absolute: true,
|
|
325
|
+
nodir: true
|
|
325
326
|
});
|
|
326
327
|
const gitignoreFiles = await glob("**/.gitignore", {
|
|
327
328
|
cwd: rootDir,
|
|
@@ -438,8 +439,14 @@ function isSourceFile(filePath) {
|
|
|
438
439
|
}
|
|
439
440
|
|
|
440
441
|
// src/utils/cli-helpers.ts
|
|
441
|
-
import {
|
|
442
|
-
|
|
442
|
+
import {
|
|
443
|
+
writeFileSync,
|
|
444
|
+
mkdirSync,
|
|
445
|
+
existsSync as existsSync2,
|
|
446
|
+
readdirSync,
|
|
447
|
+
statSync
|
|
448
|
+
} from "fs";
|
|
449
|
+
import { join as join2, dirname as dirname2, resolve as resolvePath } from "path";
|
|
443
450
|
function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
|
|
444
451
|
let outputPath;
|
|
445
452
|
if (userPath) {
|
|
@@ -525,6 +532,111 @@ function getSeverityColor(severity, chalk) {
|
|
|
525
532
|
return chalk.white;
|
|
526
533
|
}
|
|
527
534
|
}
|
|
535
|
+
function findLatestReport(dirPath) {
|
|
536
|
+
const aireadyDir = resolvePath(dirPath, ".aiready");
|
|
537
|
+
if (!existsSync2(aireadyDir)) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
let files = readdirSync(aireadyDir).filter(
|
|
541
|
+
(f) => f.startsWith("aiready-report-") && f.endsWith(".json")
|
|
542
|
+
);
|
|
543
|
+
if (files.length === 0) {
|
|
544
|
+
files = readdirSync(aireadyDir).filter(
|
|
545
|
+
(f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
if (files.length === 0) {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
const sortedFiles = files.map((f) => ({
|
|
552
|
+
name: f,
|
|
553
|
+
path: resolvePath(aireadyDir, f),
|
|
554
|
+
mtime: statSync(resolvePath(aireadyDir, f)).mtime
|
|
555
|
+
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
556
|
+
return sortedFiles[0].path;
|
|
557
|
+
}
|
|
558
|
+
function findLatestScanReport(scanReportsDir, reportFilePrefix) {
|
|
559
|
+
try {
|
|
560
|
+
let reportFiles = [];
|
|
561
|
+
if (existsSync2(scanReportsDir)) {
|
|
562
|
+
const files = readdirSync(scanReportsDir);
|
|
563
|
+
if (files.length > 0) {
|
|
564
|
+
const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
|
|
565
|
+
reportFiles = files.filter((file) => prefixRegex.test(file));
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (reportFiles.length === 0) return null;
|
|
569
|
+
reportFiles.sort((a, b) => {
|
|
570
|
+
const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
|
|
571
|
+
const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
|
|
572
|
+
return idB - idA;
|
|
573
|
+
});
|
|
574
|
+
return join2(scanReportsDir, reportFiles[0]);
|
|
575
|
+
} catch (e) {
|
|
576
|
+
console.error("Error while finding latest scan report:", e);
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// src/utils/provider-utils.ts
|
|
582
|
+
function groupIssuesByFile(issues) {
|
|
583
|
+
const fileIssuesMap = /* @__PURE__ */ new Map();
|
|
584
|
+
for (const issue of issues) {
|
|
585
|
+
const file = issue.location?.file ?? "unknown";
|
|
586
|
+
if (!fileIssuesMap.has(file)) fileIssuesMap.set(file, []);
|
|
587
|
+
fileIssuesMap.get(file).push(issue);
|
|
588
|
+
}
|
|
589
|
+
return Array.from(fileIssuesMap.entries()).map(([fileName, issueList]) => ({
|
|
590
|
+
fileName,
|
|
591
|
+
issues: issueList,
|
|
592
|
+
metrics: {}
|
|
593
|
+
}));
|
|
594
|
+
}
|
|
595
|
+
function buildSimpleProviderScore(toolName, summary, rawData = {}) {
|
|
596
|
+
return {
|
|
597
|
+
toolName,
|
|
598
|
+
score: summary.score ?? 0,
|
|
599
|
+
rawMetrics: { ...summary, ...rawData },
|
|
600
|
+
factors: [],
|
|
601
|
+
recommendations: (summary.recommendations ?? []).map((action) => ({
|
|
602
|
+
action,
|
|
603
|
+
estimatedImpact: 5,
|
|
604
|
+
priority: "medium"
|
|
605
|
+
}))
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function buildSpokeOutput(toolName, version, summary, results, metadata = {}) {
|
|
609
|
+
return SpokeOutputSchema.parse({
|
|
610
|
+
results,
|
|
611
|
+
summary,
|
|
612
|
+
metadata: {
|
|
613
|
+
toolName,
|
|
614
|
+
version,
|
|
615
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
616
|
+
...metadata
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
function createProvider(config) {
|
|
621
|
+
return {
|
|
622
|
+
id: config.id,
|
|
623
|
+
alias: config.alias,
|
|
624
|
+
defaultWeight: config.defaultWeight,
|
|
625
|
+
async analyze(options) {
|
|
626
|
+
const report = await config.analyzeReport(options);
|
|
627
|
+
return buildSpokeOutput(
|
|
628
|
+
config.id,
|
|
629
|
+
config.version,
|
|
630
|
+
config.getSummary(report),
|
|
631
|
+
config.getResults(report),
|
|
632
|
+
config.getMetadata?.(report) ?? {}
|
|
633
|
+
);
|
|
634
|
+
},
|
|
635
|
+
score(output, options) {
|
|
636
|
+
return config.score(output, options);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
528
640
|
|
|
529
641
|
// src/utils/ast-parser.ts
|
|
530
642
|
import { parse as parse2 } from "@typescript-eslint/typescript-estree";
|
|
@@ -652,8 +764,10 @@ var TypeScriptParser = class {
|
|
|
652
764
|
// camelCase for variables and functions
|
|
653
765
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
654
766
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
655
|
-
// PascalCase for classes
|
|
767
|
+
// PascalCase for classes, types and interfaces
|
|
656
768
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
769
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
770
|
+
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
657
771
|
// UPPER_CASE for constants
|
|
658
772
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
659
773
|
// Common exceptions (React hooks, etc.)
|
|
@@ -952,6 +1066,101 @@ async function setupParser(language) {
|
|
|
952
1066
|
}
|
|
953
1067
|
}
|
|
954
1068
|
|
|
1069
|
+
// src/parsers/metadata-utils.ts
|
|
1070
|
+
function analyzeNodeMetadata(node, code, options) {
|
|
1071
|
+
const metadata = {
|
|
1072
|
+
isPure: true,
|
|
1073
|
+
hasSideEffects: false
|
|
1074
|
+
};
|
|
1075
|
+
try {
|
|
1076
|
+
let prev = node.previousSibling || null;
|
|
1077
|
+
while (prev && /comment/i.test(prev.type)) {
|
|
1078
|
+
const text = prev.text || "";
|
|
1079
|
+
if (text.trim().startsWith("/**") || text.trim().startsWith("/*")) {
|
|
1080
|
+
metadata.documentation = {
|
|
1081
|
+
content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
|
|
1082
|
+
type: "comment"
|
|
1083
|
+
};
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
if (text.trim().startsWith("///")) {
|
|
1087
|
+
metadata.documentation = {
|
|
1088
|
+
content: text.replace(/^\/\/\//, "").trim(),
|
|
1089
|
+
type: "xml-doc"
|
|
1090
|
+
};
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
if (text.trim().startsWith("//")) {
|
|
1094
|
+
metadata.documentation = {
|
|
1095
|
+
content: text.replace(/^\/\//, "").trim(),
|
|
1096
|
+
type: "comment"
|
|
1097
|
+
};
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
prev = prev.previousSibling;
|
|
1101
|
+
}
|
|
1102
|
+
if (node.type === "function_definition") {
|
|
1103
|
+
const body2 = node.childForFieldName ? node.childForFieldName("body") : node.children.find((c) => c.type === "block");
|
|
1104
|
+
if (body2 && body2.children.length > 0) {
|
|
1105
|
+
const firstStmt = body2.children[0];
|
|
1106
|
+
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
1107
|
+
metadata.documentation = {
|
|
1108
|
+
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
1109
|
+
type: "docstring"
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
} catch {
|
|
1115
|
+
}
|
|
1116
|
+
const defaultSignatures = [
|
|
1117
|
+
"console.",
|
|
1118
|
+
"fmt.",
|
|
1119
|
+
"panic(",
|
|
1120
|
+
"os.Exit",
|
|
1121
|
+
"log.",
|
|
1122
|
+
"Console.Write",
|
|
1123
|
+
"File.Write",
|
|
1124
|
+
"System.out",
|
|
1125
|
+
"System.err",
|
|
1126
|
+
"Files.write",
|
|
1127
|
+
"process.exit",
|
|
1128
|
+
"exit("
|
|
1129
|
+
];
|
|
1130
|
+
const signatures = Array.from(
|
|
1131
|
+
/* @__PURE__ */ new Set([...options?.sideEffectSignatures || [], ...defaultSignatures])
|
|
1132
|
+
);
|
|
1133
|
+
const walk = (n) => {
|
|
1134
|
+
try {
|
|
1135
|
+
const t = n.type || "";
|
|
1136
|
+
if (/assign|assignment|assignment_statement|assignment_expression|throw|throw_statement|send_statement|global_statement|nonlocal_statement/i.test(
|
|
1137
|
+
t
|
|
1138
|
+
)) {
|
|
1139
|
+
metadata.isPure = false;
|
|
1140
|
+
metadata.hasSideEffects = true;
|
|
1141
|
+
}
|
|
1142
|
+
const text = n.text || "";
|
|
1143
|
+
for (const s of signatures) {
|
|
1144
|
+
if (text.includes(s)) {
|
|
1145
|
+
metadata.isPure = false;
|
|
1146
|
+
metadata.hasSideEffects = true;
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
1151
|
+
const c = n.child(i);
|
|
1152
|
+
if (c) walk(c);
|
|
1153
|
+
}
|
|
1154
|
+
} catch {
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
const body = node.childForFieldName?.("body") || node.children.find(
|
|
1158
|
+
(c) => /body|block|class_body|declaration_list|function_body/.test(c.type)
|
|
1159
|
+
);
|
|
1160
|
+
if (body) walk(body);
|
|
1161
|
+
return metadata;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
955
1164
|
// src/parsers/python-parser.ts
|
|
956
1165
|
var PythonParser = class {
|
|
957
1166
|
constructor() {
|
|
@@ -974,38 +1183,9 @@ var PythonParser = class {
|
|
|
974
1183
|
return this.parser.parse(code);
|
|
975
1184
|
}
|
|
976
1185
|
analyzeMetadata(node, code) {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
};
|
|
981
|
-
const body = node.childForFieldName("body");
|
|
982
|
-
if (body && body.children.length > 0) {
|
|
983
|
-
const firstStmt = body.children[0];
|
|
984
|
-
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
985
|
-
metadata.documentation = {
|
|
986
|
-
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
987
|
-
type: "docstring"
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
const walk = (n) => {
|
|
992
|
-
if (n.type === "global_statement" || n.type === "nonlocal_statement") {
|
|
993
|
-
metadata.isPure = false;
|
|
994
|
-
metadata.hasSideEffects = true;
|
|
995
|
-
}
|
|
996
|
-
if (n.type === "call") {
|
|
997
|
-
const functionNode = n.childForFieldName("function");
|
|
998
|
-
if (functionNode && ["print", "input", "open"].includes(functionNode.text)) {
|
|
999
|
-
metadata.isPure = false;
|
|
1000
|
-
metadata.hasSideEffects = true;
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
for (const child of n.children) {
|
|
1004
|
-
walk(child);
|
|
1005
|
-
}
|
|
1006
|
-
};
|
|
1007
|
-
if (body) walk(body);
|
|
1008
|
-
return metadata;
|
|
1186
|
+
return analyzeNodeMetadata(node, code, {
|
|
1187
|
+
sideEffectSignatures: ["print(", "input(", "open("]
|
|
1188
|
+
});
|
|
1009
1189
|
}
|
|
1010
1190
|
parse(code, filePath) {
|
|
1011
1191
|
if (!this.initialized || !this.parser) {
|
|
@@ -1354,6 +1534,113 @@ var PythonParser = class {
|
|
|
1354
1534
|
}
|
|
1355
1535
|
};
|
|
1356
1536
|
|
|
1537
|
+
// src/parsers/shared-parser-utils.ts
|
|
1538
|
+
var SIDE_EFFECT_KEYWORDS = [
|
|
1539
|
+
"print(",
|
|
1540
|
+
"console.",
|
|
1541
|
+
"System.out",
|
|
1542
|
+
"System.err",
|
|
1543
|
+
"fmt.",
|
|
1544
|
+
"File.Write",
|
|
1545
|
+
"Files.write",
|
|
1546
|
+
"os.Exit",
|
|
1547
|
+
"panic(",
|
|
1548
|
+
"throw ",
|
|
1549
|
+
"Logging.",
|
|
1550
|
+
"log."
|
|
1551
|
+
];
|
|
1552
|
+
function analyzeGeneralMetadata(node, code, options = {}) {
|
|
1553
|
+
const metadata = {
|
|
1554
|
+
isPure: true,
|
|
1555
|
+
hasSideEffects: false
|
|
1556
|
+
};
|
|
1557
|
+
try {
|
|
1558
|
+
let prev = node.previousSibling || null;
|
|
1559
|
+
while (prev && /comment/i.test(prev.type)) {
|
|
1560
|
+
const text = prev.text || "";
|
|
1561
|
+
if (text.trim().startsWith("/**")) {
|
|
1562
|
+
metadata.documentation = {
|
|
1563
|
+
content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
|
|
1564
|
+
type: "jsdoc"
|
|
1565
|
+
};
|
|
1566
|
+
break;
|
|
1567
|
+
}
|
|
1568
|
+
if (text.trim().startsWith("///")) {
|
|
1569
|
+
metadata.documentation = {
|
|
1570
|
+
content: text.replace(/^\/\/\//, "").trim(),
|
|
1571
|
+
type: "xml-doc"
|
|
1572
|
+
};
|
|
1573
|
+
break;
|
|
1574
|
+
}
|
|
1575
|
+
if (text.trim().startsWith("//")) {
|
|
1576
|
+
metadata.documentation = {
|
|
1577
|
+
content: text.replace(/^\/\//, "").trim(),
|
|
1578
|
+
type: "comment"
|
|
1579
|
+
};
|
|
1580
|
+
break;
|
|
1581
|
+
}
|
|
1582
|
+
prev = prev.previousSibling;
|
|
1583
|
+
}
|
|
1584
|
+
} catch {
|
|
1585
|
+
}
|
|
1586
|
+
const signatures = [
|
|
1587
|
+
...SIDE_EFFECT_KEYWORDS,
|
|
1588
|
+
...options.sideEffectSignatures || []
|
|
1589
|
+
];
|
|
1590
|
+
const walk = (n) => {
|
|
1591
|
+
if (/assign|assignment|assignment_statement|assignment_expression/i.test(
|
|
1592
|
+
n.type
|
|
1593
|
+
)) {
|
|
1594
|
+
metadata.isPure = false;
|
|
1595
|
+
metadata.hasSideEffects = true;
|
|
1596
|
+
}
|
|
1597
|
+
const text = n.text;
|
|
1598
|
+
for (const sig of signatures) {
|
|
1599
|
+
if (text.includes(sig)) {
|
|
1600
|
+
metadata.isPure = false;
|
|
1601
|
+
metadata.hasSideEffects = true;
|
|
1602
|
+
break;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (!metadata.hasSideEffects) {
|
|
1606
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
1607
|
+
const child = n.child(i);
|
|
1608
|
+
if (child) walk(child);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
walk(node);
|
|
1613
|
+
return metadata;
|
|
1614
|
+
}
|
|
1615
|
+
function extractParameterNames(node) {
|
|
1616
|
+
const params = [];
|
|
1617
|
+
const candidates = [
|
|
1618
|
+
// common field name
|
|
1619
|
+
node.childForFieldName ? node.childForFieldName("parameters") : null,
|
|
1620
|
+
node.childForFieldName ? node.childForFieldName("parameter_list") : null,
|
|
1621
|
+
node.children.find((c) => c.type === "parameter_list") || null,
|
|
1622
|
+
node.children.find((c) => c.type === "parameters") || null,
|
|
1623
|
+
node.children.find((c) => c.type === "formal_parameters") || null,
|
|
1624
|
+
node.children.find((c) => c.type === "formal_parameter") || null
|
|
1625
|
+
];
|
|
1626
|
+
const list = candidates.find(Boolean);
|
|
1627
|
+
if (!list) return params;
|
|
1628
|
+
for (const child of list.children) {
|
|
1629
|
+
if (!child) continue;
|
|
1630
|
+
const id = child.childForFieldName?.("name") || child.children.find(
|
|
1631
|
+
(c) => [
|
|
1632
|
+
"identifier",
|
|
1633
|
+
"variable_name",
|
|
1634
|
+
"name",
|
|
1635
|
+
"parameter",
|
|
1636
|
+
"formal_parameter"
|
|
1637
|
+
].includes(c.type)
|
|
1638
|
+
) || (child.type === "identifier" ? child : void 0);
|
|
1639
|
+
if (id && typeof id.text === "string") params.push(id.text);
|
|
1640
|
+
}
|
|
1641
|
+
return params;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1357
1644
|
// src/parsers/java-parser.ts
|
|
1358
1645
|
var JavaParser = class {
|
|
1359
1646
|
constructor() {
|
|
@@ -1376,47 +1663,14 @@ var JavaParser = class {
|
|
|
1376
1663
|
return this.parser.parse(code);
|
|
1377
1664
|
}
|
|
1378
1665
|
analyzeMetadata(node, code) {
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
content: prev.text.replace(/[/*]/g, "").trim(),
|
|
1388
|
-
type: "xml-doc"
|
|
1389
|
-
// Using xml-doc as a catch-all for structured or we can add 'javadoc'
|
|
1390
|
-
};
|
|
1391
|
-
break;
|
|
1392
|
-
}
|
|
1393
|
-
prev = prev.previousSibling;
|
|
1394
|
-
}
|
|
1395
|
-
const walk = (n) => {
|
|
1396
|
-
if (n.type === "assignment_expression") {
|
|
1397
|
-
metadata.isPure = false;
|
|
1398
|
-
metadata.hasSideEffects = true;
|
|
1399
|
-
}
|
|
1400
|
-
if (n.type === "method_invocation") {
|
|
1401
|
-
const text = n.text;
|
|
1402
|
-
if (text.includes("System.out.print") || text.includes("System.err.print") || text.includes("Files.write")) {
|
|
1403
|
-
metadata.isPure = false;
|
|
1404
|
-
metadata.hasSideEffects = true;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
if (n.type === "throw_statement") {
|
|
1408
|
-
metadata.isPure = false;
|
|
1409
|
-
metadata.hasSideEffects = true;
|
|
1410
|
-
}
|
|
1411
|
-
for (const child of n.children) {
|
|
1412
|
-
walk(child);
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1415
|
-
const body = node.children.find(
|
|
1416
|
-
(c) => c.type === "block" || c.type === "class_body"
|
|
1417
|
-
);
|
|
1418
|
-
if (body) walk(body);
|
|
1419
|
-
return metadata;
|
|
1666
|
+
return analyzeGeneralMetadata(node, code, {
|
|
1667
|
+
sideEffectSignatures: [
|
|
1668
|
+
"System.out",
|
|
1669
|
+
"System.err",
|
|
1670
|
+
"Files.write",
|
|
1671
|
+
"Logging."
|
|
1672
|
+
]
|
|
1673
|
+
});
|
|
1420
1674
|
}
|
|
1421
1675
|
parse(code, filePath) {
|
|
1422
1676
|
if (!this.initialized || !this.parser) {
|
|
@@ -1516,7 +1770,7 @@ var JavaParser = class {
|
|
|
1516
1770
|
const imports = [];
|
|
1517
1771
|
for (const node of rootNode.children) {
|
|
1518
1772
|
if (node.type === "import_declaration") {
|
|
1519
|
-
|
|
1773
|
+
const sourceArr = [];
|
|
1520
1774
|
let isStatic = false;
|
|
1521
1775
|
let isWildcard = false;
|
|
1522
1776
|
for (const child of node.children) {
|
|
@@ -1614,14 +1868,7 @@ var JavaParser = class {
|
|
|
1614
1868
|
}
|
|
1615
1869
|
}
|
|
1616
1870
|
extractParameters(node) {
|
|
1617
|
-
|
|
1618
|
-
(c) => c.type === "formal_parameters"
|
|
1619
|
-
);
|
|
1620
|
-
if (!paramsNode) return [];
|
|
1621
|
-
return paramsNode.children.filter((c) => c.type === "formal_parameter").map((c) => {
|
|
1622
|
-
const idNode = c.children.find((child) => child.type === "identifier");
|
|
1623
|
-
return idNode ? idNode.text : "unknown";
|
|
1624
|
-
});
|
|
1871
|
+
return extractParameterNames(node);
|
|
1625
1872
|
}
|
|
1626
1873
|
getNamingConventions() {
|
|
1627
1874
|
return {
|
|
@@ -1659,47 +1906,9 @@ var CSharpParser = class {
|
|
|
1659
1906
|
return this.parser.parse(code);
|
|
1660
1907
|
}
|
|
1661
1908
|
analyzeMetadata(node, code) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
};
|
|
1666
|
-
let prev = node.previousSibling;
|
|
1667
|
-
while (prev && (prev.type === "comment" || prev.type === "triple_slash_comment")) {
|
|
1668
|
-
if (prev.text.trim().startsWith("///") || prev.type === "triple_slash_comment") {
|
|
1669
|
-
metadata.documentation = {
|
|
1670
|
-
content: prev.text.replace("///", "").trim(),
|
|
1671
|
-
type: "xml-doc"
|
|
1672
|
-
};
|
|
1673
|
-
break;
|
|
1674
|
-
}
|
|
1675
|
-
prev = prev.previousSibling;
|
|
1676
|
-
}
|
|
1677
|
-
const walk = (n) => {
|
|
1678
|
-
if (n.type === "assignment_expression") {
|
|
1679
|
-
metadata.isPure = false;
|
|
1680
|
-
metadata.hasSideEffects = true;
|
|
1681
|
-
}
|
|
1682
|
-
if (n.type === "invocation_expression") {
|
|
1683
|
-
const text = n.text;
|
|
1684
|
-
if (text.includes("Console.Write") || text.includes("File.Write") || text.includes("Log.")) {
|
|
1685
|
-
metadata.isPure = false;
|
|
1686
|
-
metadata.hasSideEffects = true;
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
if (n.type === "throw_statement") {
|
|
1690
|
-
metadata.isPure = false;
|
|
1691
|
-
metadata.hasSideEffects = true;
|
|
1692
|
-
}
|
|
1693
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
1694
|
-
const child = n.child(i);
|
|
1695
|
-
if (child) walk(child);
|
|
1696
|
-
}
|
|
1697
|
-
};
|
|
1698
|
-
const body = node.children.find(
|
|
1699
|
-
(c) => c.type === "block" || c.type === "declaration_list"
|
|
1700
|
-
);
|
|
1701
|
-
if (body) walk(body);
|
|
1702
|
-
return metadata;
|
|
1909
|
+
return analyzeGeneralMetadata(node, code, {
|
|
1910
|
+
sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
|
|
1911
|
+
});
|
|
1703
1912
|
}
|
|
1704
1913
|
parse(code, filePath) {
|
|
1705
1914
|
if (!this.initialized || !this.parser) {
|
|
@@ -1904,19 +2113,7 @@ var CSharpParser = class {
|
|
|
1904
2113
|
return modifiers;
|
|
1905
2114
|
}
|
|
1906
2115
|
extractParameters(node) {
|
|
1907
|
-
|
|
1908
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
1909
|
-
if (parameterList) {
|
|
1910
|
-
for (const param of parameterList.children) {
|
|
1911
|
-
if (param.type === "parameter") {
|
|
1912
|
-
const nameNode = param.childForFieldName("name") || param.children.find((c) => c.type === "identifier");
|
|
1913
|
-
if (nameNode) {
|
|
1914
|
-
params.push(nameNode.text);
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
return params;
|
|
2116
|
+
return extractParameterNames(node);
|
|
1920
2117
|
}
|
|
1921
2118
|
getNamingConventions() {
|
|
1922
2119
|
return {
|
|
@@ -1953,40 +2150,9 @@ var GoParser = class {
|
|
|
1953
2150
|
return this.parser.parse(code);
|
|
1954
2151
|
}
|
|
1955
2152
|
analyzeMetadata(node, code) {
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
};
|
|
1960
|
-
let prev = node.previousSibling;
|
|
1961
|
-
while (prev && prev.type === "comment") {
|
|
1962
|
-
metadata.documentation = {
|
|
1963
|
-
content: prev.text.replace(/\/\/|\/\*|\*\//g, "").trim(),
|
|
1964
|
-
type: "comment"
|
|
1965
|
-
};
|
|
1966
|
-
break;
|
|
1967
|
-
}
|
|
1968
|
-
const walk = (n) => {
|
|
1969
|
-
if (n.type === "send_statement" || n.type === "expression_statement" && n.text.includes("<-")) {
|
|
1970
|
-
metadata.isPure = false;
|
|
1971
|
-
metadata.hasSideEffects = true;
|
|
1972
|
-
}
|
|
1973
|
-
if (n.type === "assignment_statement" || n.type === "short_var_declaration") {
|
|
1974
|
-
}
|
|
1975
|
-
if (n.type === "call_expression") {
|
|
1976
|
-
const text = n.text;
|
|
1977
|
-
if (text.includes("fmt.Print") || text.includes("os.Exit") || text.includes("panic(") || text.includes("log.")) {
|
|
1978
|
-
metadata.isPure = false;
|
|
1979
|
-
metadata.hasSideEffects = true;
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
1983
|
-
const child = n.child(i);
|
|
1984
|
-
if (child) walk(child);
|
|
1985
|
-
}
|
|
1986
|
-
};
|
|
1987
|
-
const body = node.childForFieldName("body");
|
|
1988
|
-
if (body) walk(body);
|
|
1989
|
-
return metadata;
|
|
2153
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2154
|
+
sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
|
|
2155
|
+
});
|
|
1990
2156
|
}
|
|
1991
2157
|
parse(code, filePath) {
|
|
1992
2158
|
if (!this.initialized || !this.parser) {
|
|
@@ -2208,17 +2374,7 @@ var GoParser = class {
|
|
|
2208
2374
|
return exports;
|
|
2209
2375
|
}
|
|
2210
2376
|
extractParameters(node) {
|
|
2211
|
-
|
|
2212
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2213
|
-
if (parameterList) {
|
|
2214
|
-
for (const param of parameterList.children) {
|
|
2215
|
-
if (param.type === "parameter_declaration") {
|
|
2216
|
-
const names = param.children.filter((c) => c.type === "identifier");
|
|
2217
|
-
names.forEach((n) => params.push(n.text));
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
return params;
|
|
2377
|
+
return extractParameterNames(node);
|
|
2222
2378
|
}
|
|
2223
2379
|
getNamingConventions() {
|
|
2224
2380
|
return {
|
|
@@ -2568,34 +2724,47 @@ var CONFIG_FILES = [
|
|
|
2568
2724
|
async function loadConfig(rootDir) {
|
|
2569
2725
|
let currentDir = resolve(rootDir);
|
|
2570
2726
|
while (true) {
|
|
2727
|
+
const foundConfigs = [];
|
|
2571
2728
|
for (const configFile of CONFIG_FILES) {
|
|
2729
|
+
if (existsSync4(join4(currentDir, configFile))) {
|
|
2730
|
+
foundConfigs.push(configFile);
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
if (foundConfigs.length > 0) {
|
|
2734
|
+
if (foundConfigs.length > 1) {
|
|
2735
|
+
console.warn(
|
|
2736
|
+
`\u26A0\uFE0F Multiple configuration files found in ${currentDir}: ${foundConfigs.join(
|
|
2737
|
+
", "
|
|
2738
|
+
)}. Using ${foundConfigs[0]}.`
|
|
2739
|
+
);
|
|
2740
|
+
} else {
|
|
2741
|
+
}
|
|
2742
|
+
const configFile = foundConfigs[0];
|
|
2572
2743
|
const configPath = join4(currentDir, configFile);
|
|
2573
|
-
|
|
2744
|
+
try {
|
|
2745
|
+
let config;
|
|
2746
|
+
if (configFile.endsWith(".js")) {
|
|
2747
|
+
const fileUrl = pathToFileURL(configPath).href;
|
|
2748
|
+
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2749
|
+
config = module.default || module;
|
|
2750
|
+
} else {
|
|
2751
|
+
const content = readFileSync(configPath, "utf-8");
|
|
2752
|
+
config = JSON.parse(content);
|
|
2753
|
+
}
|
|
2754
|
+
if (typeof config !== "object" || config === null) {
|
|
2755
|
+
throw new Error("Config must be an object");
|
|
2756
|
+
}
|
|
2757
|
+
return config;
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2760
|
+
const e = new Error(
|
|
2761
|
+
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2762
|
+
);
|
|
2574
2763
|
try {
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
const fileUrl = pathToFileURL(configPath).href;
|
|
2578
|
-
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2579
|
-
config = module.default || module;
|
|
2580
|
-
} else {
|
|
2581
|
-
const content = readFileSync(configPath, "utf-8");
|
|
2582
|
-
config = JSON.parse(content);
|
|
2583
|
-
}
|
|
2584
|
-
if (typeof config !== "object" || config === null) {
|
|
2585
|
-
throw new Error("Config must be an object");
|
|
2586
|
-
}
|
|
2587
|
-
return config;
|
|
2588
|
-
} catch (error) {
|
|
2589
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2590
|
-
const e = new Error(
|
|
2591
|
-
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2592
|
-
);
|
|
2593
|
-
try {
|
|
2594
|
-
e.cause = error instanceof Error ? error : void 0;
|
|
2595
|
-
} catch {
|
|
2596
|
-
}
|
|
2597
|
-
throw e;
|
|
2764
|
+
e.cause = error instanceof Error ? error : void 0;
|
|
2765
|
+
} catch {
|
|
2598
2766
|
}
|
|
2767
|
+
throw e;
|
|
2599
2768
|
}
|
|
2600
2769
|
}
|
|
2601
2770
|
const parent = dirname4(currentDir);
|
|
@@ -3085,6 +3254,24 @@ function collectFutureProofRecommendations(params) {
|
|
|
3085
3254
|
}
|
|
3086
3255
|
return recommendations;
|
|
3087
3256
|
}
|
|
3257
|
+
function collectBaseFutureProofRecommendations(params) {
|
|
3258
|
+
const recommendations = [];
|
|
3259
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
3260
|
+
recommendations.push({
|
|
3261
|
+
action: rec,
|
|
3262
|
+
estimatedImpact: 5,
|
|
3263
|
+
priority: "medium"
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
3267
|
+
recommendations.push({
|
|
3268
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
3269
|
+
estimatedImpact: 8,
|
|
3270
|
+
priority: "high"
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
return recommendations;
|
|
3274
|
+
}
|
|
3088
3275
|
|
|
3089
3276
|
// src/metrics/cognitive-load.ts
|
|
3090
3277
|
function calculateCognitiveLoad(params) {
|
|
@@ -3775,21 +3962,10 @@ function calculateFutureProofScore(params) {
|
|
|
3775
3962
|
description: params.conceptCohesion.rating
|
|
3776
3963
|
}
|
|
3777
3964
|
];
|
|
3778
|
-
const recommendations =
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
estimatedImpact: 5,
|
|
3783
|
-
priority: "medium"
|
|
3784
|
-
});
|
|
3785
|
-
}
|
|
3786
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
3787
|
-
recommendations.push({
|
|
3788
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
3789
|
-
estimatedImpact: 8,
|
|
3790
|
-
priority: "high"
|
|
3791
|
-
});
|
|
3792
|
-
}
|
|
3965
|
+
const recommendations = collectBaseFutureProofRecommendations({
|
|
3966
|
+
patternEntropy: params.patternEntropy,
|
|
3967
|
+
conceptCohesion: params.conceptCohesion
|
|
3968
|
+
});
|
|
3793
3969
|
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
3794
3970
|
return {
|
|
3795
3971
|
toolName: "future-proof",
|
|
@@ -4121,6 +4297,8 @@ export {
|
|
|
4121
4297
|
TypeScriptParser,
|
|
4122
4298
|
UnifiedReportSchema,
|
|
4123
4299
|
VAGUE_FILE_NAMES,
|
|
4300
|
+
buildSimpleProviderScore,
|
|
4301
|
+
buildSpokeOutput,
|
|
4124
4302
|
calculateAgentGrounding,
|
|
4125
4303
|
calculateAiSignalClarity,
|
|
4126
4304
|
calculateBusinessROI,
|
|
@@ -4144,6 +4322,7 @@ export {
|
|
|
4144
4322
|
calculateTestabilityIndex,
|
|
4145
4323
|
calculateTokenBudget,
|
|
4146
4324
|
clearHistory,
|
|
4325
|
+
createProvider,
|
|
4147
4326
|
emitAnnotation,
|
|
4148
4327
|
emitIssuesAsAnnotations,
|
|
4149
4328
|
emitProgress,
|
|
@@ -4152,6 +4331,8 @@ export {
|
|
|
4152
4331
|
exportHistory,
|
|
4153
4332
|
extractFunctions,
|
|
4154
4333
|
extractImports,
|
|
4334
|
+
findLatestReport,
|
|
4335
|
+
findLatestScanReport,
|
|
4155
4336
|
formatAcceptanceRate,
|
|
4156
4337
|
formatCost,
|
|
4157
4338
|
formatHours,
|
|
@@ -4178,6 +4359,7 @@ export {
|
|
|
4178
4359
|
getSupportedLanguages,
|
|
4179
4360
|
getToolWeight,
|
|
4180
4361
|
getWasmPath,
|
|
4362
|
+
groupIssuesByFile,
|
|
4181
4363
|
handleCLIError,
|
|
4182
4364
|
handleJSONOutput,
|
|
4183
4365
|
initTreeSitter,
|