@corbat-tech/coco 2.14.1 → 2.15.0
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/cli/index.js +647 -59
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +631 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3972,6 +3972,18 @@ function trackSubprocess(proc) {
|
|
|
3972
3972
|
|
|
3973
3973
|
// src/quality/analyzers/coverage.ts
|
|
3974
3974
|
async function detectTestFramework(projectPath) {
|
|
3975
|
+
try {
|
|
3976
|
+
await access(join(projectPath, "pom.xml"), constants.R_OK);
|
|
3977
|
+
return "maven";
|
|
3978
|
+
} catch {
|
|
3979
|
+
}
|
|
3980
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
3981
|
+
try {
|
|
3982
|
+
await access(join(projectPath, f), constants.R_OK);
|
|
3983
|
+
return "gradle";
|
|
3984
|
+
} catch {
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3975
3987
|
try {
|
|
3976
3988
|
const pkgPath = join(projectPath, "package.json");
|
|
3977
3989
|
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
@@ -4033,6 +4045,55 @@ function parseCoverageSummary(report) {
|
|
|
4033
4045
|
}
|
|
4034
4046
|
};
|
|
4035
4047
|
}
|
|
4048
|
+
function parseJacocoCsv(csv) {
|
|
4049
|
+
const lines = csv.trim().split("\n").slice(1);
|
|
4050
|
+
let lineMissed = 0, lineCovered = 0;
|
|
4051
|
+
let branchMissed = 0, branchCovered = 0;
|
|
4052
|
+
let methodMissed = 0, methodCovered = 0;
|
|
4053
|
+
let instrMissed = 0, instrCovered = 0;
|
|
4054
|
+
for (const line of lines) {
|
|
4055
|
+
const cols = line.split(",");
|
|
4056
|
+
if (cols.length < 13) continue;
|
|
4057
|
+
instrMissed += parseInt(cols[3] ?? "0", 10);
|
|
4058
|
+
instrCovered += parseInt(cols[4] ?? "0", 10);
|
|
4059
|
+
branchMissed += parseInt(cols[5] ?? "0", 10);
|
|
4060
|
+
branchCovered += parseInt(cols[6] ?? "0", 10);
|
|
4061
|
+
lineMissed += parseInt(cols[7] ?? "0", 10);
|
|
4062
|
+
lineCovered += parseInt(cols[8] ?? "0", 10);
|
|
4063
|
+
methodMissed += parseInt(cols[11] ?? "0", 10);
|
|
4064
|
+
methodCovered += parseInt(cols[12] ?? "0", 10);
|
|
4065
|
+
}
|
|
4066
|
+
const pct = (covered, missed) => {
|
|
4067
|
+
const total = covered + missed;
|
|
4068
|
+
return total > 0 ? Math.round(covered / total * 1e3) / 10 : 0;
|
|
4069
|
+
};
|
|
4070
|
+
return {
|
|
4071
|
+
lines: {
|
|
4072
|
+
total: lineCovered + lineMissed,
|
|
4073
|
+
covered: lineCovered,
|
|
4074
|
+
skipped: 0,
|
|
4075
|
+
percentage: pct(lineCovered, lineMissed)
|
|
4076
|
+
},
|
|
4077
|
+
branches: {
|
|
4078
|
+
total: branchCovered + branchMissed,
|
|
4079
|
+
covered: branchCovered,
|
|
4080
|
+
skipped: 0,
|
|
4081
|
+
percentage: pct(branchCovered, branchMissed)
|
|
4082
|
+
},
|
|
4083
|
+
functions: {
|
|
4084
|
+
total: methodCovered + methodMissed,
|
|
4085
|
+
covered: methodCovered,
|
|
4086
|
+
skipped: 0,
|
|
4087
|
+
percentage: pct(methodCovered, methodMissed)
|
|
4088
|
+
},
|
|
4089
|
+
statements: {
|
|
4090
|
+
total: instrCovered + instrMissed,
|
|
4091
|
+
covered: instrCovered,
|
|
4092
|
+
skipped: 0,
|
|
4093
|
+
percentage: pct(instrCovered, instrMissed)
|
|
4094
|
+
}
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4036
4097
|
var CoverageAnalyzer = class {
|
|
4037
4098
|
constructor(projectPath) {
|
|
4038
4099
|
this.projectPath = projectPath;
|
|
@@ -4042,29 +4103,53 @@ var CoverageAnalyzer = class {
|
|
|
4042
4103
|
*/
|
|
4043
4104
|
async analyze() {
|
|
4044
4105
|
const framework = await detectTestFramework(this.projectPath);
|
|
4045
|
-
const coverageTool = await detectCoverageTool(this.projectPath);
|
|
4046
4106
|
if (!framework) {
|
|
4047
|
-
|
|
4107
|
+
return this.zeroCoverage();
|
|
4048
4108
|
}
|
|
4049
|
-
const existingCoverage = await this.readExistingCoverage();
|
|
4109
|
+
const existingCoverage = await this.readExistingCoverage(framework);
|
|
4050
4110
|
if (existingCoverage) {
|
|
4051
4111
|
return existingCoverage;
|
|
4052
4112
|
}
|
|
4113
|
+
if (framework === "maven" || framework === "gradle") {
|
|
4114
|
+
return this.zeroCoverage();
|
|
4115
|
+
}
|
|
4116
|
+
const coverageTool = await detectCoverageTool(this.projectPath);
|
|
4053
4117
|
return await this.runWithCoverage(framework, coverageTool);
|
|
4054
4118
|
}
|
|
4119
|
+
/** Return empty coverage metrics (graceful fallback) */
|
|
4120
|
+
zeroCoverage() {
|
|
4121
|
+
const zero = { total: 0, covered: 0, skipped: 0, percentage: 0 };
|
|
4122
|
+
return { lines: zero, branches: zero, functions: zero, statements: zero };
|
|
4123
|
+
}
|
|
4055
4124
|
/**
|
|
4056
|
-
* Read existing coverage report if available
|
|
4125
|
+
* Read existing coverage report if available.
|
|
4126
|
+
* Supports Node.js (c8/nyc JSON) and JVM (JaCoCo CSV) formats.
|
|
4057
4127
|
*/
|
|
4058
|
-
async readExistingCoverage() {
|
|
4128
|
+
async readExistingCoverage(framework) {
|
|
4129
|
+
if (framework === "maven" || framework === "gradle") {
|
|
4130
|
+
const jacocoPaths = framework === "maven" ? [
|
|
4131
|
+
join(this.projectPath, "target", "site", "jacoco", "jacoco.csv"),
|
|
4132
|
+
join(this.projectPath, "target", "site", "jacoco-ut", "jacoco.csv")
|
|
4133
|
+
] : [join(this.projectPath, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")];
|
|
4134
|
+
for (const csvPath of jacocoPaths) {
|
|
4135
|
+
try {
|
|
4136
|
+
await access(csvPath, constants.R_OK);
|
|
4137
|
+
const csv = await readFile(csvPath, "utf-8");
|
|
4138
|
+
return parseJacocoCsv(csv);
|
|
4139
|
+
} catch {
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
return null;
|
|
4143
|
+
}
|
|
4059
4144
|
const possiblePaths = [
|
|
4060
4145
|
join(this.projectPath, "coverage", "coverage-summary.json"),
|
|
4061
4146
|
join(this.projectPath, ".coverage", "coverage-summary.json"),
|
|
4062
4147
|
join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
|
|
4063
4148
|
];
|
|
4064
|
-
for (const
|
|
4149
|
+
for (const p5 of possiblePaths) {
|
|
4065
4150
|
try {
|
|
4066
|
-
await access(
|
|
4067
|
-
const content = await readFile(
|
|
4151
|
+
await access(p5, constants.R_OK);
|
|
4152
|
+
const content = await readFile(p5, "utf-8");
|
|
4068
4153
|
const report = JSON.parse(content);
|
|
4069
4154
|
return parseCoverageSummary(report);
|
|
4070
4155
|
} catch {
|
|
@@ -4724,7 +4809,7 @@ var BuildVerifier = class {
|
|
|
4724
4809
|
stderr: ""
|
|
4725
4810
|
};
|
|
4726
4811
|
}
|
|
4727
|
-
const SAFE_BUILD_PATTERN = /^(npm|pnpm|yarn|bun)\s+(run\s+)?[\w:.-]+$|^npx\s+tsc(\s+--[\w-]+)*$/;
|
|
4812
|
+
const SAFE_BUILD_PATTERN = /^(npm|pnpm|yarn|bun)\s+(run\s+)?[\w:.-]+$|^npx\s+tsc(\s+--[\w-]+)*$|^\.(\/|\\)(mvnw|gradlew)(\s+[\w:.-]+)*(\s+-[\w-]+)*$/;
|
|
4728
4813
|
if (!SAFE_BUILD_PATTERN.test(buildCommand.trim())) {
|
|
4729
4814
|
return {
|
|
4730
4815
|
success: false,
|
|
@@ -4829,9 +4914,20 @@ var BuildVerifier = class {
|
|
|
4829
4914
|
}
|
|
4830
4915
|
}
|
|
4831
4916
|
/**
|
|
4832
|
-
* Detect build command from
|
|
4917
|
+
* Detect build command from project build files.
|
|
4918
|
+
* Checks Maven, Gradle, and Node.js in that order.
|
|
4833
4919
|
*/
|
|
4834
4920
|
async detectBuildCommand() {
|
|
4921
|
+
if (await this.fileExists(path17.join(this.projectPath, "pom.xml"))) {
|
|
4922
|
+
const wrapper = path17.join(this.projectPath, "mvnw");
|
|
4923
|
+
return await this.fileExists(wrapper) ? "./mvnw compile -B -q" : "mvn compile -B -q";
|
|
4924
|
+
}
|
|
4925
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
4926
|
+
if (await this.fileExists(path17.join(this.projectPath, f))) {
|
|
4927
|
+
const wrapper = path17.join(this.projectPath, "gradlew");
|
|
4928
|
+
return await this.fileExists(wrapper) ? "./gradlew classes -q" : "gradle classes -q";
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4835
4931
|
try {
|
|
4836
4932
|
const packageJsonPath = path17.join(this.projectPath, "package.json");
|
|
4837
4933
|
const content = await fs16.readFile(packageJsonPath, "utf-8");
|
|
@@ -4842,10 +4938,9 @@ var BuildVerifier = class {
|
|
|
4842
4938
|
if (packageJson.devDependencies?.typescript || packageJson.dependencies?.typescript) {
|
|
4843
4939
|
return "npx tsc --noEmit";
|
|
4844
4940
|
}
|
|
4845
|
-
return null;
|
|
4846
4941
|
} catch {
|
|
4847
|
-
return null;
|
|
4848
4942
|
}
|
|
4943
|
+
return null;
|
|
4849
4944
|
}
|
|
4850
4945
|
/**
|
|
4851
4946
|
* Parse errors from build output
|
|
@@ -4919,6 +5014,16 @@ var BuildVerifier = class {
|
|
|
4919
5014
|
};
|
|
4920
5015
|
|
|
4921
5016
|
// src/quality/analyzers/correctness.ts
|
|
5017
|
+
async function resolveJvmExecutable(projectPath, tool) {
|
|
5018
|
+
const wrapper = tool === "maven" ? "mvnw" : "gradlew";
|
|
5019
|
+
const fallback = tool === "maven" ? "mvn" : "gradle";
|
|
5020
|
+
try {
|
|
5021
|
+
await access(join(projectPath, wrapper));
|
|
5022
|
+
return join(projectPath, wrapper);
|
|
5023
|
+
} catch {
|
|
5024
|
+
return fallback;
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
4922
5027
|
function parseVitestOutput(stdout) {
|
|
4923
5028
|
const testsMatch = stdout.match(
|
|
4924
5029
|
/Tests\s+(?:(\d+)\s+passed)?(?:\s*\|\s*(\d+)\s+failed)?(?:\s*\|\s*(\d+)\s+skipped)?/
|
|
@@ -4971,10 +5076,29 @@ function buildTestCommand(framework) {
|
|
|
4971
5076
|
return { command: "npx", args: ["jest", "--json"] };
|
|
4972
5077
|
case "mocha":
|
|
4973
5078
|
return { command: "npx", args: ["mocha", "--reporter=json"] };
|
|
5079
|
+
case "maven":
|
|
5080
|
+
return { command: "__maven__", args: ["test", "--no-transfer-progress", "-B"] };
|
|
5081
|
+
case "gradle":
|
|
5082
|
+
return { command: "__gradle__", args: ["test"] };
|
|
4974
5083
|
default:
|
|
4975
5084
|
return null;
|
|
4976
5085
|
}
|
|
4977
5086
|
}
|
|
5087
|
+
function parseMavenOutput(output) {
|
|
5088
|
+
let passed = 0, failed = 0, skipped = 0;
|
|
5089
|
+
const pattern = /Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)/gi;
|
|
5090
|
+
for (const match of output.matchAll(pattern)) {
|
|
5091
|
+
const total = parseInt(match[1] ?? "0", 10);
|
|
5092
|
+
const failures = parseInt(match[2] ?? "0", 10);
|
|
5093
|
+
const errors = parseInt(match[3] ?? "0", 10);
|
|
5094
|
+
const skip = parseInt(match[4] ?? "0", 10);
|
|
5095
|
+
const f = failures + errors;
|
|
5096
|
+
passed += total - f - skip;
|
|
5097
|
+
failed += f;
|
|
5098
|
+
skipped += skip;
|
|
5099
|
+
}
|
|
5100
|
+
return { passed, failed, skipped };
|
|
5101
|
+
}
|
|
4978
5102
|
var CorrectnessAnalyzer = class {
|
|
4979
5103
|
constructor(projectPath) {
|
|
4980
5104
|
this.projectPath = projectPath;
|
|
@@ -5026,6 +5150,11 @@ var CorrectnessAnalyzer = class {
|
|
|
5026
5150
|
if (!cmd) {
|
|
5027
5151
|
return { passed: 0, failed: 0, skipped: 0 };
|
|
5028
5152
|
}
|
|
5153
|
+
if (cmd.command === "__maven__") {
|
|
5154
|
+
cmd.command = await resolveJvmExecutable(this.projectPath, "maven");
|
|
5155
|
+
} else if (cmd.command === "__gradle__") {
|
|
5156
|
+
cmd.command = await resolveJvmExecutable(this.projectPath, "gradle");
|
|
5157
|
+
}
|
|
5029
5158
|
try {
|
|
5030
5159
|
const proc = execa(cmd.command, cmd.args, {
|
|
5031
5160
|
cwd: this.projectPath,
|
|
@@ -5037,15 +5166,15 @@ var CorrectnessAnalyzer = class {
|
|
|
5037
5166
|
});
|
|
5038
5167
|
trackSubprocess(proc);
|
|
5039
5168
|
const result = await proc;
|
|
5040
|
-
const output = result.stdout + "\n" + result.stderr;
|
|
5169
|
+
const output = (result.stdout ?? "") + "\n" + (result.stderr ?? "");
|
|
5041
5170
|
switch (framework) {
|
|
5042
5171
|
case "vitest":
|
|
5043
5172
|
return parseVitestOutput(output);
|
|
5044
5173
|
case "jest":
|
|
5045
|
-
return parseJestOutput(result.stdout);
|
|
5174
|
+
return parseJestOutput(result.stdout ?? "");
|
|
5046
5175
|
case "mocha": {
|
|
5047
5176
|
try {
|
|
5048
|
-
const json2 = JSON.parse(result.stdout);
|
|
5177
|
+
const json2 = JSON.parse(result.stdout ?? "");
|
|
5049
5178
|
return {
|
|
5050
5179
|
passed: json2.stats?.passes ?? 0,
|
|
5051
5180
|
failed: json2.stats?.failures ?? 0,
|
|
@@ -5055,6 +5184,9 @@ var CorrectnessAnalyzer = class {
|
|
|
5055
5184
|
return { passed: 0, failed: 0, skipped: 0 };
|
|
5056
5185
|
}
|
|
5057
5186
|
}
|
|
5187
|
+
case "maven":
|
|
5188
|
+
case "gradle":
|
|
5189
|
+
return parseMavenOutput(output);
|
|
5058
5190
|
default:
|
|
5059
5191
|
return { passed: 0, failed: 0, skipped: 0 };
|
|
5060
5192
|
}
|
|
@@ -7854,9 +7986,31 @@ var QualityEvaluator = class {
|
|
|
7854
7986
|
return suggestions;
|
|
7855
7987
|
}
|
|
7856
7988
|
/**
|
|
7857
|
-
* Find source files in project
|
|
7989
|
+
* Find source files in project, adapting to the detected language stack.
|
|
7858
7990
|
*/
|
|
7859
7991
|
async findSourceFiles() {
|
|
7992
|
+
const { access: access10 } = await import('fs/promises');
|
|
7993
|
+
const { join: join18 } = await import('path');
|
|
7994
|
+
let isJava = false;
|
|
7995
|
+
try {
|
|
7996
|
+
await access10(join18(this.projectPath, "pom.xml"));
|
|
7997
|
+
isJava = true;
|
|
7998
|
+
} catch {
|
|
7999
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
8000
|
+
try {
|
|
8001
|
+
await access10(join18(this.projectPath, f));
|
|
8002
|
+
isJava = true;
|
|
8003
|
+
break;
|
|
8004
|
+
} catch {
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
if (isJava) {
|
|
8009
|
+
return glob("src/main/java/**/*.java", {
|
|
8010
|
+
cwd: this.projectPath,
|
|
8011
|
+
absolute: true
|
|
8012
|
+
});
|
|
8013
|
+
}
|
|
7860
8014
|
return glob("**/*.{ts,js,tsx,jsx}", {
|
|
7861
8015
|
cwd: this.projectPath,
|
|
7862
8016
|
absolute: true,
|
|
@@ -17482,6 +17636,18 @@ var checkAgentCapabilityTool = defineTool({
|
|
|
17482
17636
|
});
|
|
17483
17637
|
var simpleAgentTools = [spawnSimpleAgentTool, checkAgentCapabilityTool];
|
|
17484
17638
|
async function detectTestFramework2(cwd) {
|
|
17639
|
+
try {
|
|
17640
|
+
await fs16__default.access(path17__default.join(cwd, "pom.xml"));
|
|
17641
|
+
return "maven";
|
|
17642
|
+
} catch {
|
|
17643
|
+
}
|
|
17644
|
+
for (const gradleFile of ["build.gradle", "build.gradle.kts"]) {
|
|
17645
|
+
try {
|
|
17646
|
+
await fs16__default.access(path17__default.join(cwd, gradleFile));
|
|
17647
|
+
return "gradle";
|
|
17648
|
+
} catch {
|
|
17649
|
+
}
|
|
17650
|
+
}
|
|
17485
17651
|
try {
|
|
17486
17652
|
const pkgPath = path17__default.join(cwd, "package.json");
|
|
17487
17653
|
const pkgContent = await fs16__default.readFile(pkgPath, "utf-8");
|
|
@@ -17499,36 +17665,79 @@ async function detectTestFramework2(cwd) {
|
|
|
17499
17665
|
return null;
|
|
17500
17666
|
}
|
|
17501
17667
|
}
|
|
17668
|
+
function toMavenTestFilter(pattern) {
|
|
17669
|
+
const base = path17__default.basename(pattern).replace(/\.java$/, "");
|
|
17670
|
+
return base;
|
|
17671
|
+
}
|
|
17672
|
+
function toGradleTestFilter(pattern) {
|
|
17673
|
+
const base = path17__default.basename(pattern).replace(/\.java$/, "");
|
|
17674
|
+
return `*${base}`;
|
|
17675
|
+
}
|
|
17676
|
+
async function mavenExecutable(cwd) {
|
|
17677
|
+
try {
|
|
17678
|
+
await fs16__default.access(path17__default.join(cwd, "mvnw"));
|
|
17679
|
+
return "./mvnw";
|
|
17680
|
+
} catch {
|
|
17681
|
+
return "mvn";
|
|
17682
|
+
}
|
|
17683
|
+
}
|
|
17684
|
+
async function gradleExecutable(cwd) {
|
|
17685
|
+
try {
|
|
17686
|
+
await fs16__default.access(path17__default.join(cwd, "gradlew"));
|
|
17687
|
+
return "./gradlew";
|
|
17688
|
+
} catch {
|
|
17689
|
+
return "gradle";
|
|
17690
|
+
}
|
|
17691
|
+
}
|
|
17502
17692
|
var runTestsTool = defineTool({
|
|
17503
17693
|
name: "run_tests",
|
|
17504
|
-
description: `Run tests in the project (auto-detects vitest, jest, or mocha).
|
|
17694
|
+
description: `Run tests in the project (auto-detects Maven/Gradle/JUnit, vitest, jest, or mocha).
|
|
17505
17695
|
|
|
17506
17696
|
Examples:
|
|
17507
17697
|
- Run all tests: {}
|
|
17508
17698
|
- With coverage: { "coverage": true }
|
|
17509
|
-
- Specific pattern: { "pattern": "src/**/*.test.ts" }
|
|
17510
|
-
- Specific
|
|
17699
|
+
- Specific pattern (JS): { "pattern": "src/**/*.test.ts" }
|
|
17700
|
+
- Specific test class (Java): { "pattern": "**/ItemRestControllerIT.java" }
|
|
17701
|
+
- Specific framework: { "framework": "maven" }
|
|
17702
|
+
- Maven module: { "framework": "maven", "args": ["-pl", "stock-core"] }`,
|
|
17511
17703
|
category: "test",
|
|
17512
17704
|
parameters: z.object({
|
|
17513
17705
|
cwd: z.string().optional().describe("Project directory"),
|
|
17514
|
-
pattern: z.string().optional().describe("Test file pattern"),
|
|
17706
|
+
pattern: z.string().optional().describe("Test file pattern or class glob"),
|
|
17515
17707
|
coverage: z.boolean().optional().default(false).describe("Collect coverage"),
|
|
17516
|
-
framework: z.string().optional().describe("Test framework (vitest, jest, mocha)"),
|
|
17517
|
-
watch: z.boolean().optional().default(false).describe("Watch mode")
|
|
17708
|
+
framework: z.string().optional().describe("Test framework (maven, gradle, vitest, jest, mocha)"),
|
|
17709
|
+
watch: z.boolean().optional().default(false).describe("Watch mode"),
|
|
17710
|
+
args: z.array(z.string()).optional().describe("Extra arguments (e.g. Maven -pl module)")
|
|
17518
17711
|
}),
|
|
17519
|
-
async execute({ cwd, pattern, coverage, framework, watch: watch2 }) {
|
|
17712
|
+
async execute({ cwd, pattern, coverage, framework, watch: watch2, args: extraArgs }) {
|
|
17520
17713
|
const projectDir = cwd ?? process.cwd();
|
|
17521
17714
|
const detectedFramework = framework ?? await detectTestFramework2(projectDir);
|
|
17522
17715
|
if (!detectedFramework) {
|
|
17523
|
-
throw new ToolError(
|
|
17524
|
-
|
|
17525
|
-
|
|
17716
|
+
throw new ToolError(
|
|
17717
|
+
"No test framework detected. For Java projects ensure pom.xml or build.gradle exists. For Node.js projects install vitest, jest, or mocha.",
|
|
17718
|
+
{ tool: "run_tests" }
|
|
17719
|
+
);
|
|
17526
17720
|
}
|
|
17527
17721
|
const startTime = performance.now();
|
|
17528
17722
|
try {
|
|
17529
17723
|
const args = [];
|
|
17530
17724
|
let command = "npx";
|
|
17531
17725
|
switch (detectedFramework) {
|
|
17726
|
+
case "maven": {
|
|
17727
|
+
command = await mavenExecutable(projectDir);
|
|
17728
|
+
args.push(coverage ? "verify" : "test");
|
|
17729
|
+
if (extraArgs && extraArgs.length > 0) args.push(...extraArgs);
|
|
17730
|
+
if (pattern) args.push(`-Dtest=${toMavenTestFilter(pattern)}`);
|
|
17731
|
+
break;
|
|
17732
|
+
}
|
|
17733
|
+
case "gradle": {
|
|
17734
|
+
command = await gradleExecutable(projectDir);
|
|
17735
|
+
args.push("test");
|
|
17736
|
+
if (extraArgs && extraArgs.length > 0) args.push(...extraArgs);
|
|
17737
|
+
if (pattern) args.push("--tests", toGradleTestFilter(pattern));
|
|
17738
|
+
if (coverage) args.push("jacocoTestReport");
|
|
17739
|
+
break;
|
|
17740
|
+
}
|
|
17532
17741
|
case "vitest":
|
|
17533
17742
|
args.push("vitest", "run");
|
|
17534
17743
|
if (coverage) args.push("--coverage");
|
|
@@ -17566,8 +17775,8 @@ Examples:
|
|
|
17566
17775
|
const duration = performance.now() - startTime;
|
|
17567
17776
|
return parseTestResults(
|
|
17568
17777
|
detectedFramework,
|
|
17569
|
-
result.stdout,
|
|
17570
|
-
result.stderr,
|
|
17778
|
+
result.stdout ?? "",
|
|
17779
|
+
result.stderr ?? "",
|
|
17571
17780
|
result.exitCode ?? 0,
|
|
17572
17781
|
duration
|
|
17573
17782
|
);
|
|
@@ -17581,18 +17790,37 @@ Examples:
|
|
|
17581
17790
|
}
|
|
17582
17791
|
});
|
|
17583
17792
|
function parseTestResults(framework, stdout, stderr, exitCode, duration) {
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
|
|
17587
|
-
|
|
17588
|
-
|
|
17793
|
+
if (framework === "vitest" || framework === "jest") {
|
|
17794
|
+
try {
|
|
17795
|
+
const jsonMatch = stdout.match(/\{[\s\S]*\}/);
|
|
17796
|
+
if (jsonMatch) {
|
|
17797
|
+
const json2 = JSON.parse(jsonMatch[0]);
|
|
17589
17798
|
return parseJestLikeResults(json2, duration);
|
|
17590
17799
|
}
|
|
17800
|
+
} catch {
|
|
17591
17801
|
}
|
|
17592
|
-
} catch {
|
|
17593
17802
|
}
|
|
17594
|
-
const
|
|
17595
|
-
|
|
17803
|
+
const mavenMatch = stdout.match(
|
|
17804
|
+
/Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)/i
|
|
17805
|
+
);
|
|
17806
|
+
if (mavenMatch) {
|
|
17807
|
+
const total = parseInt(mavenMatch[1] ?? "0", 10);
|
|
17808
|
+
const failures = parseInt(mavenMatch[2] ?? "0", 10);
|
|
17809
|
+
const errors = parseInt(mavenMatch[3] ?? "0", 10);
|
|
17810
|
+
const skipped2 = parseInt(mavenMatch[4] ?? "0", 10);
|
|
17811
|
+
const failed2 = failures + errors;
|
|
17812
|
+
return {
|
|
17813
|
+
passed: total - failed2 - skipped2,
|
|
17814
|
+
failed: failed2,
|
|
17815
|
+
skipped: skipped2,
|
|
17816
|
+
total,
|
|
17817
|
+
duration,
|
|
17818
|
+
success: exitCode === 0,
|
|
17819
|
+
failures: failed2 > 0 ? parseFailuresFromOutput(stderr || stdout) : []
|
|
17820
|
+
};
|
|
17821
|
+
}
|
|
17822
|
+
const passMatch = stdout.match(/(\d+)\s*(?:passed|passing|tests\s+run)/i);
|
|
17823
|
+
const failMatch = stdout.match(/(\d+)\s*(?:failed|failing|failures)/i);
|
|
17596
17824
|
const skipMatch = stdout.match(/(\d+)\s*(?:skipped|pending)/i);
|
|
17597
17825
|
const passed = passMatch ? parseInt(passMatch[1] ?? "0", 10) : 0;
|
|
17598
17826
|
const failed = failMatch ? parseInt(failMatch[1] ?? "0", 10) : 0;
|
|
@@ -17649,6 +17877,37 @@ function parseFailuresFromOutput(output) {
|
|
|
17649
17877
|
}
|
|
17650
17878
|
return failures;
|
|
17651
17879
|
}
|
|
17880
|
+
function parseJacocoCsvCoverage(csv) {
|
|
17881
|
+
const lines = csv.trim().split("\n").slice(1);
|
|
17882
|
+
if (lines.length === 0) return null;
|
|
17883
|
+
let lineMissed = 0, lineCovered = 0;
|
|
17884
|
+
let branchMissed = 0, branchCovered = 0;
|
|
17885
|
+
let methodMissed = 0, methodCovered = 0;
|
|
17886
|
+
let instrMissed = 0, instrCovered = 0;
|
|
17887
|
+
for (const line of lines) {
|
|
17888
|
+
const cols = line.split(",");
|
|
17889
|
+
if (cols.length < 13) continue;
|
|
17890
|
+
instrMissed += parseInt(cols[3] ?? "0", 10);
|
|
17891
|
+
instrCovered += parseInt(cols[4] ?? "0", 10);
|
|
17892
|
+
branchMissed += parseInt(cols[5] ?? "0", 10);
|
|
17893
|
+
branchCovered += parseInt(cols[6] ?? "0", 10);
|
|
17894
|
+
lineMissed += parseInt(cols[7] ?? "0", 10);
|
|
17895
|
+
lineCovered += parseInt(cols[8] ?? "0", 10);
|
|
17896
|
+
methodMissed += parseInt(cols[11] ?? "0", 10);
|
|
17897
|
+
methodCovered += parseInt(cols[12] ?? "0", 10);
|
|
17898
|
+
}
|
|
17899
|
+
if (lineCovered + lineMissed === 0) return null;
|
|
17900
|
+
const pct = (covered, missed) => {
|
|
17901
|
+
const total = covered + missed;
|
|
17902
|
+
return total > 0 ? Math.round(covered / total * 1e3) / 10 : 0;
|
|
17903
|
+
};
|
|
17904
|
+
return {
|
|
17905
|
+
lines: pct(lineCovered, lineMissed),
|
|
17906
|
+
branches: pct(branchCovered, branchMissed),
|
|
17907
|
+
functions: pct(methodCovered, methodMissed),
|
|
17908
|
+
statements: pct(instrCovered, instrMissed)
|
|
17909
|
+
};
|
|
17910
|
+
}
|
|
17652
17911
|
var getCoverageTool = defineTool({
|
|
17653
17912
|
name: "get_coverage",
|
|
17654
17913
|
description: `Get test coverage report (requires running tests with --coverage first).
|
|
@@ -17667,11 +17926,23 @@ Examples:
|
|
|
17667
17926
|
const coverageLocations = [
|
|
17668
17927
|
path17__default.join(projectDir, "coverage", "coverage-summary.json"),
|
|
17669
17928
|
path17__default.join(projectDir, "coverage", "coverage-final.json"),
|
|
17670
|
-
path17__default.join(projectDir, ".nyc_output", "coverage-summary.json")
|
|
17929
|
+
path17__default.join(projectDir, ".nyc_output", "coverage-summary.json"),
|
|
17930
|
+
// Maven JaCoCo
|
|
17931
|
+
path17__default.join(projectDir, "target", "site", "jacoco", "jacoco.csv"),
|
|
17932
|
+
path17__default.join(projectDir, "target", "site", "jacoco-ut", "jacoco.csv"),
|
|
17933
|
+
// Gradle JaCoCo
|
|
17934
|
+
path17__default.join(projectDir, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")
|
|
17671
17935
|
];
|
|
17672
17936
|
for (const location of coverageLocations) {
|
|
17673
17937
|
try {
|
|
17674
17938
|
const content = await fs16__default.readFile(location, "utf-8");
|
|
17939
|
+
if (location.endsWith(".csv")) {
|
|
17940
|
+
const result = parseJacocoCsvCoverage(content);
|
|
17941
|
+
if (result) {
|
|
17942
|
+
return { ...result, report: format === "detailed" ? content : void 0 };
|
|
17943
|
+
}
|
|
17944
|
+
continue;
|
|
17945
|
+
}
|
|
17675
17946
|
const coverage = JSON.parse(content);
|
|
17676
17947
|
if (coverage.total) {
|
|
17677
17948
|
return {
|
|
@@ -17685,9 +17956,10 @@ Examples:
|
|
|
17685
17956
|
} catch {
|
|
17686
17957
|
}
|
|
17687
17958
|
}
|
|
17688
|
-
throw new ToolError(
|
|
17689
|
-
|
|
17690
|
-
|
|
17959
|
+
throw new ToolError(
|
|
17960
|
+
"Coverage data not found. For Maven projects run 'mvn verify' with JaCoCo plugin. For Node.js run tests with --coverage.",
|
|
17961
|
+
{ tool: "get_coverage" }
|
|
17962
|
+
);
|
|
17691
17963
|
} catch (error) {
|
|
17692
17964
|
if (error instanceof ToolError) throw error;
|
|
17693
17965
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -17704,25 +17976,41 @@ var runTestFileTool = defineTool({
|
|
|
17704
17976
|
|
|
17705
17977
|
Examples:
|
|
17706
17978
|
- Single file: { "file": "src/utils.test.ts" }
|
|
17707
|
-
-
|
|
17979
|
+
- Java test: { "file": "**/ItemRestControllerIT.java" }
|
|
17980
|
+
- With framework: { "file": "test/app.spec.js", "framework": "jest" }
|
|
17981
|
+
- Maven module: { "file": "**/MyTest.java", "args": ["-pl", "my-module"] }`,
|
|
17708
17982
|
category: "test",
|
|
17709
17983
|
parameters: z.object({
|
|
17710
17984
|
cwd: z.string().optional().describe("Project directory"),
|
|
17711
|
-
file: z.string().describe("Test file path"),
|
|
17712
|
-
framework: z.string().optional().describe("Test framework")
|
|
17985
|
+
file: z.string().describe("Test file path or class glob"),
|
|
17986
|
+
framework: z.string().optional().describe("Test framework (maven, gradle, vitest, jest, mocha)"),
|
|
17987
|
+
args: z.array(z.string()).optional().describe("Extra arguments (e.g. Maven -pl module)")
|
|
17713
17988
|
}),
|
|
17714
|
-
async execute({ cwd, file, framework }) {
|
|
17989
|
+
async execute({ cwd, file, framework, args }) {
|
|
17715
17990
|
return runTestsTool.execute({
|
|
17716
17991
|
cwd,
|
|
17717
17992
|
pattern: file,
|
|
17718
17993
|
coverage: false,
|
|
17719
17994
|
framework,
|
|
17720
|
-
watch: false
|
|
17995
|
+
watch: false,
|
|
17996
|
+
args
|
|
17721
17997
|
});
|
|
17722
17998
|
}
|
|
17723
17999
|
});
|
|
17724
18000
|
var testTools = [runTestsTool, getCoverageTool, runTestFileTool];
|
|
17725
18001
|
async function detectLinter2(cwd) {
|
|
18002
|
+
try {
|
|
18003
|
+
await fs16__default.access(path17__default.join(cwd, "pom.xml"));
|
|
18004
|
+
return "maven-checkstyle";
|
|
18005
|
+
} catch {
|
|
18006
|
+
}
|
|
18007
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
18008
|
+
try {
|
|
18009
|
+
await fs16__default.access(path17__default.join(cwd, f));
|
|
18010
|
+
return "gradle-checkstyle";
|
|
18011
|
+
} catch {
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
17726
18014
|
try {
|
|
17727
18015
|
const pkgPath = path17__default.join(cwd, "package.json");
|
|
17728
18016
|
const pkgContent = await fs16__default.readFile(pkgPath, "utf-8");
|
|
@@ -17739,21 +18027,60 @@ async function detectLinter2(cwd) {
|
|
|
17739
18027
|
return null;
|
|
17740
18028
|
}
|
|
17741
18029
|
}
|
|
18030
|
+
async function mavenExec(cwd) {
|
|
18031
|
+
try {
|
|
18032
|
+
await fs16__default.access(path17__default.join(cwd, "mvnw"));
|
|
18033
|
+
return "./mvnw";
|
|
18034
|
+
} catch {
|
|
18035
|
+
return "mvn";
|
|
18036
|
+
}
|
|
18037
|
+
}
|
|
18038
|
+
async function gradleExec(cwd) {
|
|
18039
|
+
try {
|
|
18040
|
+
await fs16__default.access(path17__default.join(cwd, "gradlew"));
|
|
18041
|
+
return "./gradlew";
|
|
18042
|
+
} catch {
|
|
18043
|
+
return "gradle";
|
|
18044
|
+
}
|
|
18045
|
+
}
|
|
18046
|
+
function parseCheckstyleOutput(stdout, stderr) {
|
|
18047
|
+
const output = stdout + "\n" + stderr;
|
|
18048
|
+
const issues = [];
|
|
18049
|
+
let errors = 0;
|
|
18050
|
+
let warnings = 0;
|
|
18051
|
+
const lineRe = /\[(ERROR|WARN(?:ING)?)\]\s+(.+?):(?:\[(\d+)(?:,(\d+))?\])?\s*(?:\([^)]*\))?\s*(.+)/gi;
|
|
18052
|
+
for (const m of output.matchAll(lineRe)) {
|
|
18053
|
+
const sev = (m[1] ?? "").toUpperCase().startsWith("ERROR") ? "error" : "warning";
|
|
18054
|
+
if (sev === "error") errors++;
|
|
18055
|
+
else warnings++;
|
|
18056
|
+
issues.push({
|
|
18057
|
+
file: m[2]?.trim() ?? "",
|
|
18058
|
+
line: parseInt(m[3] ?? "0", 10),
|
|
18059
|
+
column: parseInt(m[4] ?? "0", 10),
|
|
18060
|
+
severity: sev,
|
|
18061
|
+
message: m[5]?.trim() ?? "",
|
|
18062
|
+
rule: ""
|
|
18063
|
+
});
|
|
18064
|
+
}
|
|
18065
|
+
const score = Math.max(0, 100 - errors * 5 - warnings * 2);
|
|
18066
|
+
return { errors, warnings, fixable: 0, issues, score };
|
|
18067
|
+
}
|
|
17742
18068
|
var runLinterTool = defineTool({
|
|
17743
18069
|
name: "run_linter",
|
|
17744
|
-
description: `Run linter on the codebase (auto-detects eslint, oxlint,
|
|
18070
|
+
description: `Run linter on the codebase (auto-detects eslint, oxlint, biome for Node.js; checkstyle for Maven/Gradle).
|
|
17745
18071
|
|
|
17746
18072
|
Examples:
|
|
17747
18073
|
- Lint all: {} \u2192 { "errors": 0, "warnings": 5, "score": 90 }
|
|
17748
|
-
- Auto-fix: { "fix": true }
|
|
18074
|
+
- Auto-fix (Node.js): { "fix": true }
|
|
17749
18075
|
- Specific files: { "files": ["src/app.ts", "src/utils.ts"] }
|
|
17750
|
-
- Force linter: { "linter": "eslint" }
|
|
18076
|
+
- Force linter: { "linter": "eslint" }
|
|
18077
|
+
- Java project (Maven): automatically runs checkstyle:check if plugin is configured`,
|
|
17751
18078
|
category: "quality",
|
|
17752
18079
|
parameters: z.object({
|
|
17753
18080
|
cwd: z.string().optional().describe("Project directory"),
|
|
17754
18081
|
files: z.array(z.string()).optional().describe("Specific files to lint"),
|
|
17755
|
-
fix: z.boolean().optional().default(false).describe("Auto-fix issues"),
|
|
17756
|
-
linter: z.string().optional().describe("Linter to use (eslint, oxlint, biome)")
|
|
18082
|
+
fix: z.boolean().optional().default(false).describe("Auto-fix issues (Node.js only)"),
|
|
18083
|
+
linter: z.string().optional().describe("Linter to use (eslint, oxlint, biome, maven-checkstyle, gradle-checkstyle)")
|
|
17757
18084
|
}),
|
|
17758
18085
|
async execute({ cwd, files, fix, linter }) {
|
|
17759
18086
|
const projectDir = cwd ?? process.cwd();
|
|
@@ -17766,13 +18093,23 @@ Examples:
|
|
|
17766
18093
|
issues: [],
|
|
17767
18094
|
score: null,
|
|
17768
18095
|
linter: "none",
|
|
17769
|
-
message: "No linter detected (looked for: eslint, oxlint, biome). Install one or use bash_exec to run a custom linter."
|
|
18096
|
+
message: "No linter detected (looked for: eslint, oxlint, biome for Node.js; checkstyle plugin for Maven/Gradle). Install one or use bash_exec to run a custom linter."
|
|
17770
18097
|
};
|
|
17771
18098
|
}
|
|
17772
18099
|
try {
|
|
17773
18100
|
const args = [];
|
|
17774
18101
|
let command = "npx";
|
|
17775
18102
|
switch (detectedLinter) {
|
|
18103
|
+
case "maven-checkstyle": {
|
|
18104
|
+
command = await mavenExec(projectDir);
|
|
18105
|
+
args.push("checkstyle:check", "--no-transfer-progress", "-q");
|
|
18106
|
+
break;
|
|
18107
|
+
}
|
|
18108
|
+
case "gradle-checkstyle": {
|
|
18109
|
+
command = await gradleExec(projectDir);
|
|
18110
|
+
args.push("checkstyleMain", "--quiet");
|
|
18111
|
+
break;
|
|
18112
|
+
}
|
|
17776
18113
|
case "oxlint":
|
|
17777
18114
|
args.push("oxlint");
|
|
17778
18115
|
if (files && files.length > 0) {
|
|
@@ -17813,7 +18150,25 @@ Examples:
|
|
|
17813
18150
|
reject: false,
|
|
17814
18151
|
timeout: 12e4
|
|
17815
18152
|
});
|
|
17816
|
-
|
|
18153
|
+
const combinedOutput = (result.stdout ?? "") + (result.stderr ?? "");
|
|
18154
|
+
if ((detectedLinter === "maven-checkstyle" || detectedLinter === "gradle-checkstyle") && /No plugin found|Task.*not found|checkstyle.*not configured/i.test(combinedOutput)) {
|
|
18155
|
+
return {
|
|
18156
|
+
errors: 0,
|
|
18157
|
+
warnings: 0,
|
|
18158
|
+
fixable: 0,
|
|
18159
|
+
issues: [],
|
|
18160
|
+
score: null,
|
|
18161
|
+
linter: "none",
|
|
18162
|
+
message: "Checkstyle plugin not configured in build file. Add maven-checkstyle-plugin (Maven) or checkstyle plugin (Gradle) to enable Java linting."
|
|
18163
|
+
};
|
|
18164
|
+
}
|
|
18165
|
+
if (detectedLinter === "maven-checkstyle" || detectedLinter === "gradle-checkstyle") {
|
|
18166
|
+
return {
|
|
18167
|
+
...parseCheckstyleOutput(result.stdout ?? "", result.stderr ?? ""),
|
|
18168
|
+
linter: detectedLinter
|
|
18169
|
+
};
|
|
18170
|
+
}
|
|
18171
|
+
return parseLintResults(detectedLinter, result.stdout ?? "", result.stderr ?? "");
|
|
17817
18172
|
} catch (error) {
|
|
17818
18173
|
throw new ToolError(
|
|
17819
18174
|
`Linting failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -17916,6 +18271,28 @@ Examples:
|
|
|
17916
18271
|
});
|
|
17917
18272
|
async function findSourceFiles(cwd) {
|
|
17918
18273
|
const { glob: glob17 } = await import('glob');
|
|
18274
|
+
let isJava = false;
|
|
18275
|
+
try {
|
|
18276
|
+
await fs16__default.access(path17__default.join(cwd, "pom.xml"));
|
|
18277
|
+
isJava = true;
|
|
18278
|
+
} catch {
|
|
18279
|
+
}
|
|
18280
|
+
if (!isJava) {
|
|
18281
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
18282
|
+
try {
|
|
18283
|
+
await fs16__default.access(path17__default.join(cwd, f));
|
|
18284
|
+
isJava = true;
|
|
18285
|
+
break;
|
|
18286
|
+
} catch {
|
|
18287
|
+
}
|
|
18288
|
+
}
|
|
18289
|
+
}
|
|
18290
|
+
if (isJava) {
|
|
18291
|
+
return glob17("src/main/java/**/*.java", {
|
|
18292
|
+
cwd,
|
|
18293
|
+
absolute: true
|
|
18294
|
+
});
|
|
18295
|
+
}
|
|
17919
18296
|
return glob17("src/**/*.{ts,js,tsx,jsx}", {
|
|
17920
18297
|
cwd,
|
|
17921
18298
|
absolute: true,
|
|
@@ -17933,6 +18310,8 @@ function analyzeFileComplexity(content, file) {
|
|
|
17933
18310
|
const line = lines[i] ?? "";
|
|
17934
18311
|
const funcMatch = line.match(
|
|
17935
18312
|
/(?:function|async function)\s+(\w+)|(\w+)\s*(?:=|:)\s*(?:async\s*)?\(?.*\)?\s*=>/
|
|
18313
|
+
) ?? line.match(
|
|
18314
|
+
/(?:public|private|protected|static|final|native|synchronized|abstract)\s+\S+\s+(\w+)\s*\(/
|
|
17936
18315
|
);
|
|
17937
18316
|
if (funcMatch && braceDepth === 0) {
|
|
17938
18317
|
if (currentFunction) {
|
|
@@ -18061,7 +18440,7 @@ Examples:
|
|
|
18061
18440
|
if (stats.isFile()) {
|
|
18062
18441
|
filesToSearch = [targetPath];
|
|
18063
18442
|
} else {
|
|
18064
|
-
const globPattern = include ?? "**/*.{ts,tsx,js,jsx,json,md,txt}";
|
|
18443
|
+
const globPattern = include ?? "**/*.{ts,tsx,js,jsx,java,py,go,rs,json,md,txt}";
|
|
18065
18444
|
const defaultExclude = ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/coverage/**"];
|
|
18066
18445
|
const excludePatterns = exclude ?? defaultExclude;
|
|
18067
18446
|
filesToSearch = await glob(globPattern, {
|
|
@@ -18791,7 +19170,205 @@ ${message}
|
|
|
18791
19170
|
}
|
|
18792
19171
|
}
|
|
18793
19172
|
});
|
|
18794
|
-
|
|
19173
|
+
async function resolveMaven(cwd) {
|
|
19174
|
+
try {
|
|
19175
|
+
await fs16__default.access(path17__default.join(cwd, "mvnw"));
|
|
19176
|
+
return "./mvnw";
|
|
19177
|
+
} catch {
|
|
19178
|
+
return "mvn";
|
|
19179
|
+
}
|
|
19180
|
+
}
|
|
19181
|
+
async function resolveGradle(cwd) {
|
|
19182
|
+
try {
|
|
19183
|
+
await fs16__default.access(path17__default.join(cwd, "gradlew"));
|
|
19184
|
+
return "./gradlew";
|
|
19185
|
+
} catch {
|
|
19186
|
+
return "gradle";
|
|
19187
|
+
}
|
|
19188
|
+
}
|
|
19189
|
+
var runMavenTool = defineTool({
|
|
19190
|
+
name: "run_maven",
|
|
19191
|
+
description: `Run a Maven goal (auto-detects ./mvnw wrapper).
|
|
19192
|
+
|
|
19193
|
+
Examples:
|
|
19194
|
+
- Compile: { "goal": "compile" }
|
|
19195
|
+
- Run tests: { "goal": "test" }
|
|
19196
|
+
- Package: { "goal": "package" }
|
|
19197
|
+
- Skip tests: { "goal": "package", "args": ["-DskipTests"] }
|
|
19198
|
+
- Specific module: { "goal": "test", "args": ["-pl", "stock-core"] }
|
|
19199
|
+
- Quiet mode: { "goal": "verify", "args": ["-q", "--no-transfer-progress"] }`,
|
|
19200
|
+
category: "build",
|
|
19201
|
+
parameters: z.object({
|
|
19202
|
+
goal: z.string().describe("Maven goal (compile, test, package, verify, clean, install, ...)"),
|
|
19203
|
+
cwd: z.string().optional().describe("Project directory"),
|
|
19204
|
+
args: z.array(z.string()).optional().describe("Additional Maven arguments"),
|
|
19205
|
+
env: z.record(z.string(), z.string()).optional().describe("Environment variables"),
|
|
19206
|
+
timeout: z.number().optional().describe("Timeout in milliseconds")
|
|
19207
|
+
}),
|
|
19208
|
+
async execute({ goal, cwd, args, env: env2, timeout }) {
|
|
19209
|
+
const projectDir = cwd ?? process.cwd();
|
|
19210
|
+
const startTime = performance.now();
|
|
19211
|
+
const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS4;
|
|
19212
|
+
const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
|
|
19213
|
+
const heartbeat = new CommandHeartbeat2({
|
|
19214
|
+
onUpdate: (stats) => {
|
|
19215
|
+
if (stats.elapsedSeconds > 10)
|
|
19216
|
+
process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
|
|
19217
|
+
},
|
|
19218
|
+
onWarn: (message) => process.stderr.write(`
|
|
19219
|
+
${message}
|
|
19220
|
+
`)
|
|
19221
|
+
});
|
|
19222
|
+
try {
|
|
19223
|
+
heartbeat.start();
|
|
19224
|
+
const command = await resolveMaven(projectDir);
|
|
19225
|
+
const cmdArgs = [goal, "--no-transfer-progress", "-B", ...args ?? []];
|
|
19226
|
+
const subprocess = execa(command, cmdArgs, {
|
|
19227
|
+
cwd: projectDir,
|
|
19228
|
+
timeout: timeoutMs,
|
|
19229
|
+
env: { ...process.env, ...env2 },
|
|
19230
|
+
reject: false,
|
|
19231
|
+
buffer: false,
|
|
19232
|
+
maxBuffer: MAX_OUTPUT_SIZE2
|
|
19233
|
+
});
|
|
19234
|
+
let stdoutBuffer = "";
|
|
19235
|
+
let stderrBuffer = "";
|
|
19236
|
+
subprocess.stdout?.on("data", (chunk) => {
|
|
19237
|
+
const text = chunk.toString();
|
|
19238
|
+
stdoutBuffer += text;
|
|
19239
|
+
process.stdout.write(text);
|
|
19240
|
+
heartbeat.activity();
|
|
19241
|
+
});
|
|
19242
|
+
subprocess.stderr?.on("data", (chunk) => {
|
|
19243
|
+
const text = chunk.toString();
|
|
19244
|
+
stderrBuffer += text;
|
|
19245
|
+
process.stderr.write(text);
|
|
19246
|
+
heartbeat.activity();
|
|
19247
|
+
});
|
|
19248
|
+
const result = await subprocess;
|
|
19249
|
+
const buildResult = {
|
|
19250
|
+
success: result.exitCode === 0,
|
|
19251
|
+
stdout: truncateOutput2(stdoutBuffer),
|
|
19252
|
+
stderr: truncateOutput2(stderrBuffer),
|
|
19253
|
+
exitCode: result.exitCode ?? 0,
|
|
19254
|
+
duration: performance.now() - startTime
|
|
19255
|
+
};
|
|
19256
|
+
if (!buildResult.success) {
|
|
19257
|
+
buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_maven");
|
|
19258
|
+
}
|
|
19259
|
+
return buildResult;
|
|
19260
|
+
} catch (error) {
|
|
19261
|
+
if (error.timedOut) {
|
|
19262
|
+
throw new TimeoutError(`Maven goal '${goal}' timed out after ${timeoutMs}ms`, {
|
|
19263
|
+
timeoutMs,
|
|
19264
|
+
operation: `mvn ${goal}`
|
|
19265
|
+
});
|
|
19266
|
+
}
|
|
19267
|
+
throw new ToolError(
|
|
19268
|
+
`Maven failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
19269
|
+
{ tool: "run_maven", cause: error instanceof Error ? error : void 0 }
|
|
19270
|
+
);
|
|
19271
|
+
} finally {
|
|
19272
|
+
heartbeat.stop();
|
|
19273
|
+
process.stderr.write("\r \r");
|
|
19274
|
+
}
|
|
19275
|
+
}
|
|
19276
|
+
});
|
|
19277
|
+
var runGradleTool = defineTool({
|
|
19278
|
+
name: "run_gradle",
|
|
19279
|
+
description: `Run a Gradle task (auto-detects ./gradlew wrapper).
|
|
19280
|
+
|
|
19281
|
+
Examples:
|
|
19282
|
+
- Build: { "task": "build" }
|
|
19283
|
+
- Run tests: { "task": "test" }
|
|
19284
|
+
- Assemble: { "task": "assemble" }
|
|
19285
|
+
- Skip tests: { "task": "build", "args": ["-x", "test"] }
|
|
19286
|
+
- Specific subproject: { "task": ":stock-core:test" }`,
|
|
19287
|
+
category: "build",
|
|
19288
|
+
parameters: z.object({
|
|
19289
|
+
task: z.string().describe("Gradle task (build, test, assemble, clean, check, ...)"),
|
|
19290
|
+
cwd: z.string().optional().describe("Project directory"),
|
|
19291
|
+
args: z.array(z.string()).optional().describe("Additional Gradle arguments"),
|
|
19292
|
+
env: z.record(z.string(), z.string()).optional().describe("Environment variables"),
|
|
19293
|
+
timeout: z.number().optional().describe("Timeout in milliseconds")
|
|
19294
|
+
}),
|
|
19295
|
+
async execute({ task, cwd, args, env: env2, timeout }) {
|
|
19296
|
+
const projectDir = cwd ?? process.cwd();
|
|
19297
|
+
const startTime = performance.now();
|
|
19298
|
+
const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS4;
|
|
19299
|
+
const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
|
|
19300
|
+
const heartbeat = new CommandHeartbeat2({
|
|
19301
|
+
onUpdate: (stats) => {
|
|
19302
|
+
if (stats.elapsedSeconds > 10)
|
|
19303
|
+
process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
|
|
19304
|
+
},
|
|
19305
|
+
onWarn: (message) => process.stderr.write(`
|
|
19306
|
+
${message}
|
|
19307
|
+
`)
|
|
19308
|
+
});
|
|
19309
|
+
try {
|
|
19310
|
+
heartbeat.start();
|
|
19311
|
+
const command = await resolveGradle(projectDir);
|
|
19312
|
+
const cmdArgs = [task, "--console=plain", ...args ?? []];
|
|
19313
|
+
const subprocess = execa(command, cmdArgs, {
|
|
19314
|
+
cwd: projectDir,
|
|
19315
|
+
timeout: timeoutMs,
|
|
19316
|
+
env: { ...process.env, ...env2 },
|
|
19317
|
+
reject: false,
|
|
19318
|
+
buffer: false,
|
|
19319
|
+
maxBuffer: MAX_OUTPUT_SIZE2
|
|
19320
|
+
});
|
|
19321
|
+
let stdoutBuffer = "";
|
|
19322
|
+
let stderrBuffer = "";
|
|
19323
|
+
subprocess.stdout?.on("data", (chunk) => {
|
|
19324
|
+
const text = chunk.toString();
|
|
19325
|
+
stdoutBuffer += text;
|
|
19326
|
+
process.stdout.write(text);
|
|
19327
|
+
heartbeat.activity();
|
|
19328
|
+
});
|
|
19329
|
+
subprocess.stderr?.on("data", (chunk) => {
|
|
19330
|
+
const text = chunk.toString();
|
|
19331
|
+
stderrBuffer += text;
|
|
19332
|
+
process.stderr.write(text);
|
|
19333
|
+
heartbeat.activity();
|
|
19334
|
+
});
|
|
19335
|
+
const result = await subprocess;
|
|
19336
|
+
const buildResult = {
|
|
19337
|
+
success: result.exitCode === 0,
|
|
19338
|
+
stdout: truncateOutput2(stdoutBuffer),
|
|
19339
|
+
stderr: truncateOutput2(stderrBuffer),
|
|
19340
|
+
exitCode: result.exitCode ?? 0,
|
|
19341
|
+
duration: performance.now() - startTime
|
|
19342
|
+
};
|
|
19343
|
+
if (!buildResult.success) {
|
|
19344
|
+
buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_gradle");
|
|
19345
|
+
}
|
|
19346
|
+
return buildResult;
|
|
19347
|
+
} catch (error) {
|
|
19348
|
+
if (error.timedOut) {
|
|
19349
|
+
throw new TimeoutError(`Gradle task '${task}' timed out after ${timeoutMs}ms`, {
|
|
19350
|
+
timeoutMs,
|
|
19351
|
+
operation: `gradle ${task}`
|
|
19352
|
+
});
|
|
19353
|
+
}
|
|
19354
|
+
throw new ToolError(
|
|
19355
|
+
`Gradle failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
19356
|
+
{ tool: "run_gradle", cause: error instanceof Error ? error : void 0 }
|
|
19357
|
+
);
|
|
19358
|
+
} finally {
|
|
19359
|
+
heartbeat.stop();
|
|
19360
|
+
process.stderr.write("\r \r");
|
|
19361
|
+
}
|
|
19362
|
+
}
|
|
19363
|
+
});
|
|
19364
|
+
var buildTools = [
|
|
19365
|
+
runScriptTool,
|
|
19366
|
+
installDepsTool,
|
|
19367
|
+
makeTool,
|
|
19368
|
+
tscTool,
|
|
19369
|
+
runMavenTool,
|
|
19370
|
+
runGradleTool
|
|
19371
|
+
];
|
|
18795
19372
|
|
|
18796
19373
|
// src/cli/repl/recommended-permissions.ts
|
|
18797
19374
|
init_paths();
|