@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.mjs
CHANGED
|
@@ -439,8 +439,14 @@ function isSourceFile(filePath) {
|
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
// src/utils/cli-helpers.ts
|
|
442
|
-
import {
|
|
443
|
-
|
|
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";
|
|
444
450
|
function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
|
|
445
451
|
let outputPath;
|
|
446
452
|
if (userPath) {
|
|
@@ -526,6 +532,111 @@ function getSeverityColor(severity, chalk) {
|
|
|
526
532
|
return chalk.white;
|
|
527
533
|
}
|
|
528
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
|
+
}
|
|
529
640
|
|
|
530
641
|
// src/utils/ast-parser.ts
|
|
531
642
|
import { parse as parse2 } from "@typescript-eslint/typescript-estree";
|
|
@@ -653,8 +764,10 @@ var TypeScriptParser = class {
|
|
|
653
764
|
// camelCase for variables and functions
|
|
654
765
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
655
766
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
656
|
-
// PascalCase for classes
|
|
767
|
+
// PascalCase for classes, types and interfaces
|
|
657
768
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
769
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
770
|
+
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
658
771
|
// UPPER_CASE for constants
|
|
659
772
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
660
773
|
// Common exceptions (React hooks, etc.)
|
|
@@ -953,6 +1066,101 @@ async function setupParser(language) {
|
|
|
953
1066
|
}
|
|
954
1067
|
}
|
|
955
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
|
+
|
|
956
1164
|
// src/parsers/python-parser.ts
|
|
957
1165
|
var PythonParser = class {
|
|
958
1166
|
constructor() {
|
|
@@ -975,38 +1183,9 @@ var PythonParser = class {
|
|
|
975
1183
|
return this.parser.parse(code);
|
|
976
1184
|
}
|
|
977
1185
|
analyzeMetadata(node, code) {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
};
|
|
982
|
-
const body = node.childForFieldName("body");
|
|
983
|
-
if (body && body.children.length > 0) {
|
|
984
|
-
const firstStmt = body.children[0];
|
|
985
|
-
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
986
|
-
metadata.documentation = {
|
|
987
|
-
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
988
|
-
type: "docstring"
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
const walk = (n) => {
|
|
993
|
-
if (n.type === "global_statement" || n.type === "nonlocal_statement") {
|
|
994
|
-
metadata.isPure = false;
|
|
995
|
-
metadata.hasSideEffects = true;
|
|
996
|
-
}
|
|
997
|
-
if (n.type === "call") {
|
|
998
|
-
const functionNode = n.childForFieldName("function");
|
|
999
|
-
if (functionNode && ["print", "input", "open"].includes(functionNode.text)) {
|
|
1000
|
-
metadata.isPure = false;
|
|
1001
|
-
metadata.hasSideEffects = true;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
for (const child of n.children) {
|
|
1005
|
-
walk(child);
|
|
1006
|
-
}
|
|
1007
|
-
};
|
|
1008
|
-
if (body) walk(body);
|
|
1009
|
-
return metadata;
|
|
1186
|
+
return analyzeNodeMetadata(node, code, {
|
|
1187
|
+
sideEffectSignatures: ["print(", "input(", "open("]
|
|
1188
|
+
});
|
|
1010
1189
|
}
|
|
1011
1190
|
parse(code, filePath) {
|
|
1012
1191
|
if (!this.initialized || !this.parser) {
|
|
@@ -1355,6 +1534,113 @@ var PythonParser = class {
|
|
|
1355
1534
|
}
|
|
1356
1535
|
};
|
|
1357
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
|
+
|
|
1358
1644
|
// src/parsers/java-parser.ts
|
|
1359
1645
|
var JavaParser = class {
|
|
1360
1646
|
constructor() {
|
|
@@ -1377,47 +1663,14 @@ var JavaParser = class {
|
|
|
1377
1663
|
return this.parser.parse(code);
|
|
1378
1664
|
}
|
|
1379
1665
|
analyzeMetadata(node, code) {
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
content: prev.text.replace(/[/*]/g, "").trim(),
|
|
1389
|
-
type: "xml-doc"
|
|
1390
|
-
// Using xml-doc as a catch-all for structured or we can add 'javadoc'
|
|
1391
|
-
};
|
|
1392
|
-
break;
|
|
1393
|
-
}
|
|
1394
|
-
prev = prev.previousSibling;
|
|
1395
|
-
}
|
|
1396
|
-
const walk = (n) => {
|
|
1397
|
-
if (n.type === "assignment_expression") {
|
|
1398
|
-
metadata.isPure = false;
|
|
1399
|
-
metadata.hasSideEffects = true;
|
|
1400
|
-
}
|
|
1401
|
-
if (n.type === "method_invocation") {
|
|
1402
|
-
const text = n.text;
|
|
1403
|
-
if (text.includes("System.out.print") || text.includes("System.err.print") || text.includes("Files.write")) {
|
|
1404
|
-
metadata.isPure = false;
|
|
1405
|
-
metadata.hasSideEffects = true;
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
if (n.type === "throw_statement") {
|
|
1409
|
-
metadata.isPure = false;
|
|
1410
|
-
metadata.hasSideEffects = true;
|
|
1411
|
-
}
|
|
1412
|
-
for (const child of n.children) {
|
|
1413
|
-
walk(child);
|
|
1414
|
-
}
|
|
1415
|
-
};
|
|
1416
|
-
const body = node.children.find(
|
|
1417
|
-
(c) => c.type === "block" || c.type === "class_body"
|
|
1418
|
-
);
|
|
1419
|
-
if (body) walk(body);
|
|
1420
|
-
return metadata;
|
|
1666
|
+
return analyzeGeneralMetadata(node, code, {
|
|
1667
|
+
sideEffectSignatures: [
|
|
1668
|
+
"System.out",
|
|
1669
|
+
"System.err",
|
|
1670
|
+
"Files.write",
|
|
1671
|
+
"Logging."
|
|
1672
|
+
]
|
|
1673
|
+
});
|
|
1421
1674
|
}
|
|
1422
1675
|
parse(code, filePath) {
|
|
1423
1676
|
if (!this.initialized || !this.parser) {
|
|
@@ -1517,7 +1770,7 @@ var JavaParser = class {
|
|
|
1517
1770
|
const imports = [];
|
|
1518
1771
|
for (const node of rootNode.children) {
|
|
1519
1772
|
if (node.type === "import_declaration") {
|
|
1520
|
-
|
|
1773
|
+
const sourceArr = [];
|
|
1521
1774
|
let isStatic = false;
|
|
1522
1775
|
let isWildcard = false;
|
|
1523
1776
|
for (const child of node.children) {
|
|
@@ -1615,14 +1868,7 @@ var JavaParser = class {
|
|
|
1615
1868
|
}
|
|
1616
1869
|
}
|
|
1617
1870
|
extractParameters(node) {
|
|
1618
|
-
|
|
1619
|
-
(c) => c.type === "formal_parameters"
|
|
1620
|
-
);
|
|
1621
|
-
if (!paramsNode) return [];
|
|
1622
|
-
return paramsNode.children.filter((c) => c.type === "formal_parameter").map((c) => {
|
|
1623
|
-
const idNode = c.children.find((child) => child.type === "identifier");
|
|
1624
|
-
return idNode ? idNode.text : "unknown";
|
|
1625
|
-
});
|
|
1871
|
+
return extractParameterNames(node);
|
|
1626
1872
|
}
|
|
1627
1873
|
getNamingConventions() {
|
|
1628
1874
|
return {
|
|
@@ -1660,47 +1906,9 @@ var CSharpParser = class {
|
|
|
1660
1906
|
return this.parser.parse(code);
|
|
1661
1907
|
}
|
|
1662
1908
|
analyzeMetadata(node, code) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
};
|
|
1667
|
-
let prev = node.previousSibling;
|
|
1668
|
-
while (prev && (prev.type === "comment" || prev.type === "triple_slash_comment")) {
|
|
1669
|
-
if (prev.text.trim().startsWith("///") || prev.type === "triple_slash_comment") {
|
|
1670
|
-
metadata.documentation = {
|
|
1671
|
-
content: prev.text.replace("///", "").trim(),
|
|
1672
|
-
type: "xml-doc"
|
|
1673
|
-
};
|
|
1674
|
-
break;
|
|
1675
|
-
}
|
|
1676
|
-
prev = prev.previousSibling;
|
|
1677
|
-
}
|
|
1678
|
-
const walk = (n) => {
|
|
1679
|
-
if (n.type === "assignment_expression") {
|
|
1680
|
-
metadata.isPure = false;
|
|
1681
|
-
metadata.hasSideEffects = true;
|
|
1682
|
-
}
|
|
1683
|
-
if (n.type === "invocation_expression") {
|
|
1684
|
-
const text = n.text;
|
|
1685
|
-
if (text.includes("Console.Write") || text.includes("File.Write") || text.includes("Log.")) {
|
|
1686
|
-
metadata.isPure = false;
|
|
1687
|
-
metadata.hasSideEffects = true;
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
if (n.type === "throw_statement") {
|
|
1691
|
-
metadata.isPure = false;
|
|
1692
|
-
metadata.hasSideEffects = true;
|
|
1693
|
-
}
|
|
1694
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
1695
|
-
const child = n.child(i);
|
|
1696
|
-
if (child) walk(child);
|
|
1697
|
-
}
|
|
1698
|
-
};
|
|
1699
|
-
const body = node.children.find(
|
|
1700
|
-
(c) => c.type === "block" || c.type === "declaration_list"
|
|
1701
|
-
);
|
|
1702
|
-
if (body) walk(body);
|
|
1703
|
-
return metadata;
|
|
1909
|
+
return analyzeGeneralMetadata(node, code, {
|
|
1910
|
+
sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
|
|
1911
|
+
});
|
|
1704
1912
|
}
|
|
1705
1913
|
parse(code, filePath) {
|
|
1706
1914
|
if (!this.initialized || !this.parser) {
|
|
@@ -1905,19 +2113,7 @@ var CSharpParser = class {
|
|
|
1905
2113
|
return modifiers;
|
|
1906
2114
|
}
|
|
1907
2115
|
extractParameters(node) {
|
|
1908
|
-
|
|
1909
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
1910
|
-
if (parameterList) {
|
|
1911
|
-
for (const param of parameterList.children) {
|
|
1912
|
-
if (param.type === "parameter") {
|
|
1913
|
-
const nameNode = param.childForFieldName("name") || param.children.find((c) => c.type === "identifier");
|
|
1914
|
-
if (nameNode) {
|
|
1915
|
-
params.push(nameNode.text);
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
return params;
|
|
2116
|
+
return extractParameterNames(node);
|
|
1921
2117
|
}
|
|
1922
2118
|
getNamingConventions() {
|
|
1923
2119
|
return {
|
|
@@ -1954,40 +2150,9 @@ var GoParser = class {
|
|
|
1954
2150
|
return this.parser.parse(code);
|
|
1955
2151
|
}
|
|
1956
2152
|
analyzeMetadata(node, code) {
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
};
|
|
1961
|
-
let prev = node.previousSibling;
|
|
1962
|
-
while (prev && prev.type === "comment") {
|
|
1963
|
-
metadata.documentation = {
|
|
1964
|
-
content: prev.text.replace(/\/\/|\/\*|\*\//g, "").trim(),
|
|
1965
|
-
type: "comment"
|
|
1966
|
-
};
|
|
1967
|
-
break;
|
|
1968
|
-
}
|
|
1969
|
-
const walk = (n) => {
|
|
1970
|
-
if (n.type === "send_statement" || n.type === "expression_statement" && n.text.includes("<-")) {
|
|
1971
|
-
metadata.isPure = false;
|
|
1972
|
-
metadata.hasSideEffects = true;
|
|
1973
|
-
}
|
|
1974
|
-
if (n.type === "assignment_statement" || n.type === "short_var_declaration") {
|
|
1975
|
-
}
|
|
1976
|
-
if (n.type === "call_expression") {
|
|
1977
|
-
const text = n.text;
|
|
1978
|
-
if (text.includes("fmt.Print") || text.includes("os.Exit") || text.includes("panic(") || text.includes("log.")) {
|
|
1979
|
-
metadata.isPure = false;
|
|
1980
|
-
metadata.hasSideEffects = true;
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
1984
|
-
const child = n.child(i);
|
|
1985
|
-
if (child) walk(child);
|
|
1986
|
-
}
|
|
1987
|
-
};
|
|
1988
|
-
const body = node.childForFieldName("body");
|
|
1989
|
-
if (body) walk(body);
|
|
1990
|
-
return metadata;
|
|
2153
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2154
|
+
sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
|
|
2155
|
+
});
|
|
1991
2156
|
}
|
|
1992
2157
|
parse(code, filePath) {
|
|
1993
2158
|
if (!this.initialized || !this.parser) {
|
|
@@ -2209,17 +2374,7 @@ var GoParser = class {
|
|
|
2209
2374
|
return exports;
|
|
2210
2375
|
}
|
|
2211
2376
|
extractParameters(node) {
|
|
2212
|
-
|
|
2213
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2214
|
-
if (parameterList) {
|
|
2215
|
-
for (const param of parameterList.children) {
|
|
2216
|
-
if (param.type === "parameter_declaration") {
|
|
2217
|
-
const names = param.children.filter((c) => c.type === "identifier");
|
|
2218
|
-
names.forEach((n) => params.push(n.text));
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
return params;
|
|
2377
|
+
return extractParameterNames(node);
|
|
2223
2378
|
}
|
|
2224
2379
|
getNamingConventions() {
|
|
2225
2380
|
return {
|
|
@@ -2569,34 +2724,47 @@ var CONFIG_FILES = [
|
|
|
2569
2724
|
async function loadConfig(rootDir) {
|
|
2570
2725
|
let currentDir = resolve(rootDir);
|
|
2571
2726
|
while (true) {
|
|
2727
|
+
const foundConfigs = [];
|
|
2572
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];
|
|
2573
2743
|
const configPath = join4(currentDir, configFile);
|
|
2574
|
-
|
|
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
|
+
);
|
|
2575
2763
|
try {
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
const fileUrl = pathToFileURL(configPath).href;
|
|
2579
|
-
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2580
|
-
config = module.default || module;
|
|
2581
|
-
} else {
|
|
2582
|
-
const content = readFileSync(configPath, "utf-8");
|
|
2583
|
-
config = JSON.parse(content);
|
|
2584
|
-
}
|
|
2585
|
-
if (typeof config !== "object" || config === null) {
|
|
2586
|
-
throw new Error("Config must be an object");
|
|
2587
|
-
}
|
|
2588
|
-
return config;
|
|
2589
|
-
} catch (error) {
|
|
2590
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2591
|
-
const e = new Error(
|
|
2592
|
-
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2593
|
-
);
|
|
2594
|
-
try {
|
|
2595
|
-
e.cause = error instanceof Error ? error : void 0;
|
|
2596
|
-
} catch {
|
|
2597
|
-
}
|
|
2598
|
-
throw e;
|
|
2764
|
+
e.cause = error instanceof Error ? error : void 0;
|
|
2765
|
+
} catch {
|
|
2599
2766
|
}
|
|
2767
|
+
throw e;
|
|
2600
2768
|
}
|
|
2601
2769
|
}
|
|
2602
2770
|
const parent = dirname4(currentDir);
|
|
@@ -3086,6 +3254,24 @@ function collectFutureProofRecommendations(params) {
|
|
|
3086
3254
|
}
|
|
3087
3255
|
return recommendations;
|
|
3088
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
|
+
}
|
|
3089
3275
|
|
|
3090
3276
|
// src/metrics/cognitive-load.ts
|
|
3091
3277
|
function calculateCognitiveLoad(params) {
|
|
@@ -3776,21 +3962,10 @@ function calculateFutureProofScore(params) {
|
|
|
3776
3962
|
description: params.conceptCohesion.rating
|
|
3777
3963
|
}
|
|
3778
3964
|
];
|
|
3779
|
-
const recommendations =
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
estimatedImpact: 5,
|
|
3784
|
-
priority: "medium"
|
|
3785
|
-
});
|
|
3786
|
-
}
|
|
3787
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
3788
|
-
recommendations.push({
|
|
3789
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
3790
|
-
estimatedImpact: 8,
|
|
3791
|
-
priority: "high"
|
|
3792
|
-
});
|
|
3793
|
-
}
|
|
3965
|
+
const recommendations = collectBaseFutureProofRecommendations({
|
|
3966
|
+
patternEntropy: params.patternEntropy,
|
|
3967
|
+
conceptCohesion: params.conceptCohesion
|
|
3968
|
+
});
|
|
3794
3969
|
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
3795
3970
|
return {
|
|
3796
3971
|
toolName: "future-proof",
|
|
@@ -4122,6 +4297,8 @@ export {
|
|
|
4122
4297
|
TypeScriptParser,
|
|
4123
4298
|
UnifiedReportSchema,
|
|
4124
4299
|
VAGUE_FILE_NAMES,
|
|
4300
|
+
buildSimpleProviderScore,
|
|
4301
|
+
buildSpokeOutput,
|
|
4125
4302
|
calculateAgentGrounding,
|
|
4126
4303
|
calculateAiSignalClarity,
|
|
4127
4304
|
calculateBusinessROI,
|
|
@@ -4145,6 +4322,7 @@ export {
|
|
|
4145
4322
|
calculateTestabilityIndex,
|
|
4146
4323
|
calculateTokenBudget,
|
|
4147
4324
|
clearHistory,
|
|
4325
|
+
createProvider,
|
|
4148
4326
|
emitAnnotation,
|
|
4149
4327
|
emitIssuesAsAnnotations,
|
|
4150
4328
|
emitProgress,
|
|
@@ -4153,6 +4331,8 @@ export {
|
|
|
4153
4331
|
exportHistory,
|
|
4154
4332
|
extractFunctions,
|
|
4155
4333
|
extractImports,
|
|
4334
|
+
findLatestReport,
|
|
4335
|
+
findLatestScanReport,
|
|
4156
4336
|
formatAcceptanceRate,
|
|
4157
4337
|
formatCost,
|
|
4158
4338
|
formatHours,
|
|
@@ -4179,6 +4359,7 @@ export {
|
|
|
4179
4359
|
getSupportedLanguages,
|
|
4180
4360
|
getToolWeight,
|
|
4181
4361
|
getWasmPath,
|
|
4362
|
+
groupIssuesByFile,
|
|
4182
4363
|
handleCLIError,
|
|
4183
4364
|
handleJSONOutput,
|
|
4184
4365
|
initTreeSitter,
|