@corbat-tech/coco 2.14.0 → 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 +669 -63
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +643 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -8502,7 +8502,13 @@ function initializeContextManager(session, provider) {
|
|
|
8502
8502
|
function updateContextTokens(session, provider, toolRegistry) {
|
|
8503
8503
|
if (!session.contextManager) return;
|
|
8504
8504
|
let totalTokens = 0;
|
|
8505
|
-
{
|
|
8505
|
+
if (toolRegistry) {
|
|
8506
|
+
const effectiveMessages = getConversationContext(session, toolRegistry);
|
|
8507
|
+
for (const message of effectiveMessages) {
|
|
8508
|
+
const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
|
|
8509
|
+
totalTokens += provider.countTokens(content);
|
|
8510
|
+
}
|
|
8511
|
+
} else {
|
|
8506
8512
|
totalTokens += provider.countTokens(session.config.agent.systemPrompt);
|
|
8507
8513
|
for (const message of session.messages) {
|
|
8508
8514
|
const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
|
|
@@ -8515,7 +8521,7 @@ async function checkAndCompactContext(session, provider, signal, toolRegistry) {
|
|
|
8515
8521
|
if (!session.contextManager) {
|
|
8516
8522
|
initializeContextManager(session, provider);
|
|
8517
8523
|
}
|
|
8518
|
-
updateContextTokens(session, provider);
|
|
8524
|
+
updateContextTokens(session, provider, toolRegistry);
|
|
8519
8525
|
if (!session.contextManager.shouldCompact()) {
|
|
8520
8526
|
return null;
|
|
8521
8527
|
}
|
|
@@ -10444,6 +10450,18 @@ var init_subprocess_registry = __esm({
|
|
|
10444
10450
|
}
|
|
10445
10451
|
});
|
|
10446
10452
|
async function detectTestFramework(projectPath) {
|
|
10453
|
+
try {
|
|
10454
|
+
await access(join(projectPath, "pom.xml"), constants.R_OK);
|
|
10455
|
+
return "maven";
|
|
10456
|
+
} catch {
|
|
10457
|
+
}
|
|
10458
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
10459
|
+
try {
|
|
10460
|
+
await access(join(projectPath, f), constants.R_OK);
|
|
10461
|
+
return "gradle";
|
|
10462
|
+
} catch {
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10447
10465
|
try {
|
|
10448
10466
|
const pkgPath = join(projectPath, "package.json");
|
|
10449
10467
|
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
@@ -10505,6 +10523,55 @@ function parseCoverageSummary(report) {
|
|
|
10505
10523
|
}
|
|
10506
10524
|
};
|
|
10507
10525
|
}
|
|
10526
|
+
function parseJacocoCsv(csv) {
|
|
10527
|
+
const lines = csv.trim().split("\n").slice(1);
|
|
10528
|
+
let lineMissed = 0, lineCovered = 0;
|
|
10529
|
+
let branchMissed = 0, branchCovered = 0;
|
|
10530
|
+
let methodMissed = 0, methodCovered = 0;
|
|
10531
|
+
let instrMissed = 0, instrCovered = 0;
|
|
10532
|
+
for (const line of lines) {
|
|
10533
|
+
const cols = line.split(",");
|
|
10534
|
+
if (cols.length < 13) continue;
|
|
10535
|
+
instrMissed += parseInt(cols[3] ?? "0", 10);
|
|
10536
|
+
instrCovered += parseInt(cols[4] ?? "0", 10);
|
|
10537
|
+
branchMissed += parseInt(cols[5] ?? "0", 10);
|
|
10538
|
+
branchCovered += parseInt(cols[6] ?? "0", 10);
|
|
10539
|
+
lineMissed += parseInt(cols[7] ?? "0", 10);
|
|
10540
|
+
lineCovered += parseInt(cols[8] ?? "0", 10);
|
|
10541
|
+
methodMissed += parseInt(cols[11] ?? "0", 10);
|
|
10542
|
+
methodCovered += parseInt(cols[12] ?? "0", 10);
|
|
10543
|
+
}
|
|
10544
|
+
const pct = (covered, missed) => {
|
|
10545
|
+
const total = covered + missed;
|
|
10546
|
+
return total > 0 ? Math.round(covered / total * 1e3) / 10 : 0;
|
|
10547
|
+
};
|
|
10548
|
+
return {
|
|
10549
|
+
lines: {
|
|
10550
|
+
total: lineCovered + lineMissed,
|
|
10551
|
+
covered: lineCovered,
|
|
10552
|
+
skipped: 0,
|
|
10553
|
+
percentage: pct(lineCovered, lineMissed)
|
|
10554
|
+
},
|
|
10555
|
+
branches: {
|
|
10556
|
+
total: branchCovered + branchMissed,
|
|
10557
|
+
covered: branchCovered,
|
|
10558
|
+
skipped: 0,
|
|
10559
|
+
percentage: pct(branchCovered, branchMissed)
|
|
10560
|
+
},
|
|
10561
|
+
functions: {
|
|
10562
|
+
total: methodCovered + methodMissed,
|
|
10563
|
+
covered: methodCovered,
|
|
10564
|
+
skipped: 0,
|
|
10565
|
+
percentage: pct(methodCovered, methodMissed)
|
|
10566
|
+
},
|
|
10567
|
+
statements: {
|
|
10568
|
+
total: instrCovered + instrMissed,
|
|
10569
|
+
covered: instrCovered,
|
|
10570
|
+
skipped: 0,
|
|
10571
|
+
percentage: pct(instrCovered, instrMissed)
|
|
10572
|
+
}
|
|
10573
|
+
};
|
|
10574
|
+
}
|
|
10508
10575
|
var CoverageAnalyzer;
|
|
10509
10576
|
var init_coverage = __esm({
|
|
10510
10577
|
"src/quality/analyzers/coverage.ts"() {
|
|
@@ -10518,29 +10585,53 @@ var init_coverage = __esm({
|
|
|
10518
10585
|
*/
|
|
10519
10586
|
async analyze() {
|
|
10520
10587
|
const framework = await detectTestFramework(this.projectPath);
|
|
10521
|
-
const coverageTool = await detectCoverageTool(this.projectPath);
|
|
10522
10588
|
if (!framework) {
|
|
10523
|
-
|
|
10589
|
+
return this.zeroCoverage();
|
|
10524
10590
|
}
|
|
10525
|
-
const existingCoverage = await this.readExistingCoverage();
|
|
10591
|
+
const existingCoverage = await this.readExistingCoverage(framework);
|
|
10526
10592
|
if (existingCoverage) {
|
|
10527
10593
|
return existingCoverage;
|
|
10528
10594
|
}
|
|
10595
|
+
if (framework === "maven" || framework === "gradle") {
|
|
10596
|
+
return this.zeroCoverage();
|
|
10597
|
+
}
|
|
10598
|
+
const coverageTool = await detectCoverageTool(this.projectPath);
|
|
10529
10599
|
return await this.runWithCoverage(framework, coverageTool);
|
|
10530
10600
|
}
|
|
10601
|
+
/** Return empty coverage metrics (graceful fallback) */
|
|
10602
|
+
zeroCoverage() {
|
|
10603
|
+
const zero = { total: 0, covered: 0, skipped: 0, percentage: 0 };
|
|
10604
|
+
return { lines: zero, branches: zero, functions: zero, statements: zero };
|
|
10605
|
+
}
|
|
10531
10606
|
/**
|
|
10532
|
-
* Read existing coverage report if available
|
|
10607
|
+
* Read existing coverage report if available.
|
|
10608
|
+
* Supports Node.js (c8/nyc JSON) and JVM (JaCoCo CSV) formats.
|
|
10533
10609
|
*/
|
|
10534
|
-
async readExistingCoverage() {
|
|
10610
|
+
async readExistingCoverage(framework) {
|
|
10611
|
+
if (framework === "maven" || framework === "gradle") {
|
|
10612
|
+
const jacocoPaths = framework === "maven" ? [
|
|
10613
|
+
join(this.projectPath, "target", "site", "jacoco", "jacoco.csv"),
|
|
10614
|
+
join(this.projectPath, "target", "site", "jacoco-ut", "jacoco.csv")
|
|
10615
|
+
] : [join(this.projectPath, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")];
|
|
10616
|
+
for (const csvPath of jacocoPaths) {
|
|
10617
|
+
try {
|
|
10618
|
+
await access(csvPath, constants.R_OK);
|
|
10619
|
+
const csv = await readFile(csvPath, "utf-8");
|
|
10620
|
+
return parseJacocoCsv(csv);
|
|
10621
|
+
} catch {
|
|
10622
|
+
}
|
|
10623
|
+
}
|
|
10624
|
+
return null;
|
|
10625
|
+
}
|
|
10535
10626
|
const possiblePaths = [
|
|
10536
10627
|
join(this.projectPath, "coverage", "coverage-summary.json"),
|
|
10537
10628
|
join(this.projectPath, ".coverage", "coverage-summary.json"),
|
|
10538
10629
|
join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
|
|
10539
10630
|
];
|
|
10540
|
-
for (const
|
|
10631
|
+
for (const p45 of possiblePaths) {
|
|
10541
10632
|
try {
|
|
10542
|
-
await access(
|
|
10543
|
-
const content = await readFile(
|
|
10633
|
+
await access(p45, constants.R_OK);
|
|
10634
|
+
const content = await readFile(p45, "utf-8");
|
|
10544
10635
|
const report = JSON.parse(content);
|
|
10545
10636
|
return parseCoverageSummary(report);
|
|
10546
10637
|
} catch {
|
|
@@ -11215,7 +11306,7 @@ var init_build_verifier = __esm({
|
|
|
11215
11306
|
stderr: ""
|
|
11216
11307
|
};
|
|
11217
11308
|
}
|
|
11218
|
-
const SAFE_BUILD_PATTERN = /^(npm|pnpm|yarn|bun)\s+(run\s+)?[\w:.-]+$|^npx\s+tsc(\s+--[\w-]+)*$/;
|
|
11309
|
+
const SAFE_BUILD_PATTERN = /^(npm|pnpm|yarn|bun)\s+(run\s+)?[\w:.-]+$|^npx\s+tsc(\s+--[\w-]+)*$|^\.(\/|\\)(mvnw|gradlew)(\s+[\w:.-]+)*(\s+-[\w-]+)*$/;
|
|
11219
11310
|
if (!SAFE_BUILD_PATTERN.test(buildCommand2.trim())) {
|
|
11220
11311
|
return {
|
|
11221
11312
|
success: false,
|
|
@@ -11320,9 +11411,20 @@ var init_build_verifier = __esm({
|
|
|
11320
11411
|
}
|
|
11321
11412
|
}
|
|
11322
11413
|
/**
|
|
11323
|
-
* Detect build command from
|
|
11414
|
+
* Detect build command from project build files.
|
|
11415
|
+
* Checks Maven, Gradle, and Node.js in that order.
|
|
11324
11416
|
*/
|
|
11325
11417
|
async detectBuildCommand() {
|
|
11418
|
+
if (await this.fileExists(path36.join(this.projectPath, "pom.xml"))) {
|
|
11419
|
+
const wrapper = path36.join(this.projectPath, "mvnw");
|
|
11420
|
+
return await this.fileExists(wrapper) ? "./mvnw compile -B -q" : "mvn compile -B -q";
|
|
11421
|
+
}
|
|
11422
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
11423
|
+
if (await this.fileExists(path36.join(this.projectPath, f))) {
|
|
11424
|
+
const wrapper = path36.join(this.projectPath, "gradlew");
|
|
11425
|
+
return await this.fileExists(wrapper) ? "./gradlew classes -q" : "gradle classes -q";
|
|
11426
|
+
}
|
|
11427
|
+
}
|
|
11326
11428
|
try {
|
|
11327
11429
|
const packageJsonPath = path36.join(this.projectPath, "package.json");
|
|
11328
11430
|
const content = await fs34.readFile(packageJsonPath, "utf-8");
|
|
@@ -11333,10 +11435,9 @@ var init_build_verifier = __esm({
|
|
|
11333
11435
|
if (packageJson.devDependencies?.typescript || packageJson.dependencies?.typescript) {
|
|
11334
11436
|
return "npx tsc --noEmit";
|
|
11335
11437
|
}
|
|
11336
|
-
return null;
|
|
11337
11438
|
} catch {
|
|
11338
|
-
return null;
|
|
11339
11439
|
}
|
|
11440
|
+
return null;
|
|
11340
11441
|
}
|
|
11341
11442
|
/**
|
|
11342
11443
|
* Parse errors from build output
|
|
@@ -11410,6 +11511,16 @@ var init_build_verifier = __esm({
|
|
|
11410
11511
|
};
|
|
11411
11512
|
}
|
|
11412
11513
|
});
|
|
11514
|
+
async function resolveJvmExecutable(projectPath, tool) {
|
|
11515
|
+
const wrapper = tool === "maven" ? "mvnw" : "gradlew";
|
|
11516
|
+
const fallback = tool === "maven" ? "mvn" : "gradle";
|
|
11517
|
+
try {
|
|
11518
|
+
await access(join(projectPath, wrapper));
|
|
11519
|
+
return join(projectPath, wrapper);
|
|
11520
|
+
} catch {
|
|
11521
|
+
return fallback;
|
|
11522
|
+
}
|
|
11523
|
+
}
|
|
11413
11524
|
function parseVitestOutput(stdout) {
|
|
11414
11525
|
const testsMatch = stdout.match(
|
|
11415
11526
|
/Tests\s+(?:(\d+)\s+passed)?(?:\s*\|\s*(\d+)\s+failed)?(?:\s*\|\s*(\d+)\s+skipped)?/
|
|
@@ -11462,10 +11573,29 @@ function buildTestCommand(framework) {
|
|
|
11462
11573
|
return { command: "npx", args: ["jest", "--json"] };
|
|
11463
11574
|
case "mocha":
|
|
11464
11575
|
return { command: "npx", args: ["mocha", "--reporter=json"] };
|
|
11576
|
+
case "maven":
|
|
11577
|
+
return { command: "__maven__", args: ["test", "--no-transfer-progress", "-B"] };
|
|
11578
|
+
case "gradle":
|
|
11579
|
+
return { command: "__gradle__", args: ["test"] };
|
|
11465
11580
|
default:
|
|
11466
11581
|
return null;
|
|
11467
11582
|
}
|
|
11468
11583
|
}
|
|
11584
|
+
function parseMavenOutput(output) {
|
|
11585
|
+
let passed = 0, failed = 0, skipped = 0;
|
|
11586
|
+
const pattern = /Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)/gi;
|
|
11587
|
+
for (const match of output.matchAll(pattern)) {
|
|
11588
|
+
const total = parseInt(match[1] ?? "0", 10);
|
|
11589
|
+
const failures = parseInt(match[2] ?? "0", 10);
|
|
11590
|
+
const errors = parseInt(match[3] ?? "0", 10);
|
|
11591
|
+
const skip = parseInt(match[4] ?? "0", 10);
|
|
11592
|
+
const f = failures + errors;
|
|
11593
|
+
passed += total - f - skip;
|
|
11594
|
+
failed += f;
|
|
11595
|
+
skipped += skip;
|
|
11596
|
+
}
|
|
11597
|
+
return { passed, failed, skipped };
|
|
11598
|
+
}
|
|
11469
11599
|
var CorrectnessAnalyzer;
|
|
11470
11600
|
var init_correctness = __esm({
|
|
11471
11601
|
"src/quality/analyzers/correctness.ts"() {
|
|
@@ -11523,6 +11653,11 @@ var init_correctness = __esm({
|
|
|
11523
11653
|
if (!cmd) {
|
|
11524
11654
|
return { passed: 0, failed: 0, skipped: 0 };
|
|
11525
11655
|
}
|
|
11656
|
+
if (cmd.command === "__maven__") {
|
|
11657
|
+
cmd.command = await resolveJvmExecutable(this.projectPath, "maven");
|
|
11658
|
+
} else if (cmd.command === "__gradle__") {
|
|
11659
|
+
cmd.command = await resolveJvmExecutable(this.projectPath, "gradle");
|
|
11660
|
+
}
|
|
11526
11661
|
try {
|
|
11527
11662
|
const proc = execa(cmd.command, cmd.args, {
|
|
11528
11663
|
cwd: this.projectPath,
|
|
@@ -11534,15 +11669,15 @@ var init_correctness = __esm({
|
|
|
11534
11669
|
});
|
|
11535
11670
|
trackSubprocess(proc);
|
|
11536
11671
|
const result = await proc;
|
|
11537
|
-
const output = result.stdout + "\n" + result.stderr;
|
|
11672
|
+
const output = (result.stdout ?? "") + "\n" + (result.stderr ?? "");
|
|
11538
11673
|
switch (framework) {
|
|
11539
11674
|
case "vitest":
|
|
11540
11675
|
return parseVitestOutput(output);
|
|
11541
11676
|
case "jest":
|
|
11542
|
-
return parseJestOutput(result.stdout);
|
|
11677
|
+
return parseJestOutput(result.stdout ?? "");
|
|
11543
11678
|
case "mocha": {
|
|
11544
11679
|
try {
|
|
11545
|
-
const json2 = JSON.parse(result.stdout);
|
|
11680
|
+
const json2 = JSON.parse(result.stdout ?? "");
|
|
11546
11681
|
return {
|
|
11547
11682
|
passed: json2.stats?.passes ?? 0,
|
|
11548
11683
|
failed: json2.stats?.failures ?? 0,
|
|
@@ -11552,6 +11687,9 @@ var init_correctness = __esm({
|
|
|
11552
11687
|
return { passed: 0, failed: 0, skipped: 0 };
|
|
11553
11688
|
}
|
|
11554
11689
|
}
|
|
11690
|
+
case "maven":
|
|
11691
|
+
case "gradle":
|
|
11692
|
+
return parseMavenOutput(output);
|
|
11555
11693
|
default:
|
|
11556
11694
|
return { passed: 0, failed: 0, skipped: 0 };
|
|
11557
11695
|
}
|
|
@@ -14492,9 +14630,31 @@ var init_evaluator = __esm({
|
|
|
14492
14630
|
return suggestions;
|
|
14493
14631
|
}
|
|
14494
14632
|
/**
|
|
14495
|
-
* Find source files in project
|
|
14633
|
+
* Find source files in project, adapting to the detected language stack.
|
|
14496
14634
|
*/
|
|
14497
14635
|
async findSourceFiles() {
|
|
14636
|
+
const { access: access16 } = await import('fs/promises');
|
|
14637
|
+
const { join: join26 } = await import('path');
|
|
14638
|
+
let isJava = false;
|
|
14639
|
+
try {
|
|
14640
|
+
await access16(join26(this.projectPath, "pom.xml"));
|
|
14641
|
+
isJava = true;
|
|
14642
|
+
} catch {
|
|
14643
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
14644
|
+
try {
|
|
14645
|
+
await access16(join26(this.projectPath, f));
|
|
14646
|
+
isJava = true;
|
|
14647
|
+
break;
|
|
14648
|
+
} catch {
|
|
14649
|
+
}
|
|
14650
|
+
}
|
|
14651
|
+
}
|
|
14652
|
+
if (isJava) {
|
|
14653
|
+
return glob("src/main/java/**/*.java", {
|
|
14654
|
+
cwd: this.projectPath,
|
|
14655
|
+
absolute: true
|
|
14656
|
+
});
|
|
14657
|
+
}
|
|
14498
14658
|
return glob("**/*.{ts,js,tsx,jsx}", {
|
|
14499
14659
|
cwd: this.projectPath,
|
|
14500
14660
|
absolute: true,
|
|
@@ -14505,6 +14665,18 @@ var init_evaluator = __esm({
|
|
|
14505
14665
|
}
|
|
14506
14666
|
});
|
|
14507
14667
|
async function detectLinter2(cwd) {
|
|
14668
|
+
try {
|
|
14669
|
+
await fs34__default.access(path36__default.join(cwd, "pom.xml"));
|
|
14670
|
+
return "maven-checkstyle";
|
|
14671
|
+
} catch {
|
|
14672
|
+
}
|
|
14673
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
14674
|
+
try {
|
|
14675
|
+
await fs34__default.access(path36__default.join(cwd, f));
|
|
14676
|
+
return "gradle-checkstyle";
|
|
14677
|
+
} catch {
|
|
14678
|
+
}
|
|
14679
|
+
}
|
|
14508
14680
|
try {
|
|
14509
14681
|
const pkgPath = path36__default.join(cwd, "package.json");
|
|
14510
14682
|
const pkgContent = await fs34__default.readFile(pkgPath, "utf-8");
|
|
@@ -14521,6 +14693,44 @@ async function detectLinter2(cwd) {
|
|
|
14521
14693
|
return null;
|
|
14522
14694
|
}
|
|
14523
14695
|
}
|
|
14696
|
+
async function mavenExec(cwd) {
|
|
14697
|
+
try {
|
|
14698
|
+
await fs34__default.access(path36__default.join(cwd, "mvnw"));
|
|
14699
|
+
return "./mvnw";
|
|
14700
|
+
} catch {
|
|
14701
|
+
return "mvn";
|
|
14702
|
+
}
|
|
14703
|
+
}
|
|
14704
|
+
async function gradleExec(cwd) {
|
|
14705
|
+
try {
|
|
14706
|
+
await fs34__default.access(path36__default.join(cwd, "gradlew"));
|
|
14707
|
+
return "./gradlew";
|
|
14708
|
+
} catch {
|
|
14709
|
+
return "gradle";
|
|
14710
|
+
}
|
|
14711
|
+
}
|
|
14712
|
+
function parseCheckstyleOutput(stdout, stderr) {
|
|
14713
|
+
const output = stdout + "\n" + stderr;
|
|
14714
|
+
const issues = [];
|
|
14715
|
+
let errors = 0;
|
|
14716
|
+
let warnings = 0;
|
|
14717
|
+
const lineRe = /\[(ERROR|WARN(?:ING)?)\]\s+(.+?):(?:\[(\d+)(?:,(\d+))?\])?\s*(?:\([^)]*\))?\s*(.+)/gi;
|
|
14718
|
+
for (const m of output.matchAll(lineRe)) {
|
|
14719
|
+
const sev = (m[1] ?? "").toUpperCase().startsWith("ERROR") ? "error" : "warning";
|
|
14720
|
+
if (sev === "error") errors++;
|
|
14721
|
+
else warnings++;
|
|
14722
|
+
issues.push({
|
|
14723
|
+
file: m[2]?.trim() ?? "",
|
|
14724
|
+
line: parseInt(m[3] ?? "0", 10),
|
|
14725
|
+
column: parseInt(m[4] ?? "0", 10),
|
|
14726
|
+
severity: sev,
|
|
14727
|
+
message: m[5]?.trim() ?? "",
|
|
14728
|
+
rule: ""
|
|
14729
|
+
});
|
|
14730
|
+
}
|
|
14731
|
+
const score = Math.max(0, 100 - errors * 5 - warnings * 2);
|
|
14732
|
+
return { errors, warnings, fixable: 0, issues, score };
|
|
14733
|
+
}
|
|
14524
14734
|
function parseLintResults(_linter, stdout, _stderr) {
|
|
14525
14735
|
const issues = [];
|
|
14526
14736
|
let errors = 0;
|
|
@@ -14560,6 +14770,28 @@ function parseLintResults(_linter, stdout, _stderr) {
|
|
|
14560
14770
|
}
|
|
14561
14771
|
async function findSourceFiles(cwd) {
|
|
14562
14772
|
const { glob: glob17 } = await import('glob');
|
|
14773
|
+
let isJava = false;
|
|
14774
|
+
try {
|
|
14775
|
+
await fs34__default.access(path36__default.join(cwd, "pom.xml"));
|
|
14776
|
+
isJava = true;
|
|
14777
|
+
} catch {
|
|
14778
|
+
}
|
|
14779
|
+
if (!isJava) {
|
|
14780
|
+
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
14781
|
+
try {
|
|
14782
|
+
await fs34__default.access(path36__default.join(cwd, f));
|
|
14783
|
+
isJava = true;
|
|
14784
|
+
break;
|
|
14785
|
+
} catch {
|
|
14786
|
+
}
|
|
14787
|
+
}
|
|
14788
|
+
}
|
|
14789
|
+
if (isJava) {
|
|
14790
|
+
return glob17("src/main/java/**/*.java", {
|
|
14791
|
+
cwd,
|
|
14792
|
+
absolute: true
|
|
14793
|
+
});
|
|
14794
|
+
}
|
|
14563
14795
|
return glob17("src/**/*.{ts,js,tsx,jsx}", {
|
|
14564
14796
|
cwd,
|
|
14565
14797
|
absolute: true,
|
|
@@ -14577,6 +14809,8 @@ function analyzeFileComplexity(content, file) {
|
|
|
14577
14809
|
const line = lines[i] ?? "";
|
|
14578
14810
|
const funcMatch = line.match(
|
|
14579
14811
|
/(?:function|async function)\s+(\w+)|(\w+)\s*(?:=|:)\s*(?:async\s*)?\(?.*\)?\s*=>/
|
|
14812
|
+
) ?? line.match(
|
|
14813
|
+
/(?:public|private|protected|static|final|native|synchronized|abstract)\s+\S+\s+(\w+)\s*\(/
|
|
14580
14814
|
);
|
|
14581
14815
|
if (funcMatch && braceDepth === 0) {
|
|
14582
14816
|
if (currentFunction) {
|
|
@@ -14619,19 +14853,20 @@ var init_quality = __esm({
|
|
|
14619
14853
|
init_evaluator();
|
|
14620
14854
|
runLinterTool = defineTool({
|
|
14621
14855
|
name: "run_linter",
|
|
14622
|
-
description: `Run linter on the codebase (auto-detects eslint, oxlint,
|
|
14856
|
+
description: `Run linter on the codebase (auto-detects eslint, oxlint, biome for Node.js; checkstyle for Maven/Gradle).
|
|
14623
14857
|
|
|
14624
14858
|
Examples:
|
|
14625
14859
|
- Lint all: {} \u2192 { "errors": 0, "warnings": 5, "score": 90 }
|
|
14626
|
-
- Auto-fix: { "fix": true }
|
|
14860
|
+
- Auto-fix (Node.js): { "fix": true }
|
|
14627
14861
|
- Specific files: { "files": ["src/app.ts", "src/utils.ts"] }
|
|
14628
|
-
- Force linter: { "linter": "eslint" }
|
|
14862
|
+
- Force linter: { "linter": "eslint" }
|
|
14863
|
+
- Java project (Maven): automatically runs checkstyle:check if plugin is configured`,
|
|
14629
14864
|
category: "quality",
|
|
14630
14865
|
parameters: z.object({
|
|
14631
14866
|
cwd: z.string().optional().describe("Project directory"),
|
|
14632
14867
|
files: z.array(z.string()).optional().describe("Specific files to lint"),
|
|
14633
|
-
fix: z.boolean().optional().default(false).describe("Auto-fix issues"),
|
|
14634
|
-
linter: z.string().optional().describe("Linter to use (eslint, oxlint, biome)")
|
|
14868
|
+
fix: z.boolean().optional().default(false).describe("Auto-fix issues (Node.js only)"),
|
|
14869
|
+
linter: z.string().optional().describe("Linter to use (eslint, oxlint, biome, maven-checkstyle, gradle-checkstyle)")
|
|
14635
14870
|
}),
|
|
14636
14871
|
async execute({ cwd, files, fix, linter }) {
|
|
14637
14872
|
const projectDir = cwd ?? process.cwd();
|
|
@@ -14644,13 +14879,23 @@ Examples:
|
|
|
14644
14879
|
issues: [],
|
|
14645
14880
|
score: null,
|
|
14646
14881
|
linter: "none",
|
|
14647
|
-
message: "No linter detected (looked for: eslint, oxlint, biome). Install one or use bash_exec to run a custom linter."
|
|
14882
|
+
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."
|
|
14648
14883
|
};
|
|
14649
14884
|
}
|
|
14650
14885
|
try {
|
|
14651
14886
|
const args = [];
|
|
14652
14887
|
let command = "npx";
|
|
14653
14888
|
switch (detectedLinter) {
|
|
14889
|
+
case "maven-checkstyle": {
|
|
14890
|
+
command = await mavenExec(projectDir);
|
|
14891
|
+
args.push("checkstyle:check", "--no-transfer-progress", "-q");
|
|
14892
|
+
break;
|
|
14893
|
+
}
|
|
14894
|
+
case "gradle-checkstyle": {
|
|
14895
|
+
command = await gradleExec(projectDir);
|
|
14896
|
+
args.push("checkstyleMain", "--quiet");
|
|
14897
|
+
break;
|
|
14898
|
+
}
|
|
14654
14899
|
case "oxlint":
|
|
14655
14900
|
args.push("oxlint");
|
|
14656
14901
|
if (files && files.length > 0) {
|
|
@@ -14691,7 +14936,25 @@ Examples:
|
|
|
14691
14936
|
reject: false,
|
|
14692
14937
|
timeout: 12e4
|
|
14693
14938
|
});
|
|
14694
|
-
|
|
14939
|
+
const combinedOutput = (result.stdout ?? "") + (result.stderr ?? "");
|
|
14940
|
+
if ((detectedLinter === "maven-checkstyle" || detectedLinter === "gradle-checkstyle") && /No plugin found|Task.*not found|checkstyle.*not configured/i.test(combinedOutput)) {
|
|
14941
|
+
return {
|
|
14942
|
+
errors: 0,
|
|
14943
|
+
warnings: 0,
|
|
14944
|
+
fixable: 0,
|
|
14945
|
+
issues: [],
|
|
14946
|
+
score: null,
|
|
14947
|
+
linter: "none",
|
|
14948
|
+
message: "Checkstyle plugin not configured in build file. Add maven-checkstyle-plugin (Maven) or checkstyle plugin (Gradle) to enable Java linting."
|
|
14949
|
+
};
|
|
14950
|
+
}
|
|
14951
|
+
if (detectedLinter === "maven-checkstyle" || detectedLinter === "gradle-checkstyle") {
|
|
14952
|
+
return {
|
|
14953
|
+
...parseCheckstyleOutput(result.stdout ?? "", result.stderr ?? ""),
|
|
14954
|
+
linter: detectedLinter
|
|
14955
|
+
};
|
|
14956
|
+
}
|
|
14957
|
+
return parseLintResults(detectedLinter, result.stdout ?? "", result.stderr ?? "");
|
|
14695
14958
|
} catch (error) {
|
|
14696
14959
|
throw new ToolError(
|
|
14697
14960
|
`Linting failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -15165,7 +15428,18 @@ Examples:
|
|
|
15165
15428
|
required,
|
|
15166
15429
|
suggestions,
|
|
15167
15430
|
maturity,
|
|
15168
|
-
diff
|
|
15431
|
+
// Include full diff for skill access, but strip raw content from files
|
|
15432
|
+
// to prevent dumping thousands of lines into the LLM tool result.
|
|
15433
|
+
// Skills that need file content can access diff.files[].hunks,
|
|
15434
|
+
// but the serialised output stays lean (stats + file names only).
|
|
15435
|
+
diff: {
|
|
15436
|
+
...diff,
|
|
15437
|
+
files: diff.files.map((f) => ({
|
|
15438
|
+
...f,
|
|
15439
|
+
hunks: []
|
|
15440
|
+
// strip raw diff hunks — findings already extracted above
|
|
15441
|
+
}))
|
|
15442
|
+
}
|
|
15169
15443
|
};
|
|
15170
15444
|
if (diffWarnings.length > 0) {
|
|
15171
15445
|
result.warnings = diffWarnings;
|
|
@@ -20203,7 +20477,8 @@ async function detectProjectStack(cwd) {
|
|
|
20203
20477
|
testingFrameworks = parsed.testingFrameworks;
|
|
20204
20478
|
languages = parsed.languages;
|
|
20205
20479
|
} else if (stack === "java") {
|
|
20206
|
-
const
|
|
20480
|
+
const isGradle = await fileExists2(path36__default.join(cwd, "build.gradle")) || await fileExists2(path36__default.join(cwd, "build.gradle.kts"));
|
|
20481
|
+
const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
|
|
20207
20482
|
dependencies = parsed.dependencies;
|
|
20208
20483
|
frameworks = parsed.frameworks;
|
|
20209
20484
|
buildTools2 = parsed.buildTools;
|
|
@@ -37885,6 +38160,18 @@ init_registry4();
|
|
|
37885
38160
|
init_errors();
|
|
37886
38161
|
init_subprocess_registry();
|
|
37887
38162
|
async function detectTestFramework2(cwd) {
|
|
38163
|
+
try {
|
|
38164
|
+
await fs34__default.access(path36__default.join(cwd, "pom.xml"));
|
|
38165
|
+
return "maven";
|
|
38166
|
+
} catch {
|
|
38167
|
+
}
|
|
38168
|
+
for (const gradleFile of ["build.gradle", "build.gradle.kts"]) {
|
|
38169
|
+
try {
|
|
38170
|
+
await fs34__default.access(path36__default.join(cwd, gradleFile));
|
|
38171
|
+
return "gradle";
|
|
38172
|
+
} catch {
|
|
38173
|
+
}
|
|
38174
|
+
}
|
|
37888
38175
|
try {
|
|
37889
38176
|
const pkgPath = path36__default.join(cwd, "package.json");
|
|
37890
38177
|
const pkgContent = await fs34__default.readFile(pkgPath, "utf-8");
|
|
@@ -37902,36 +38189,79 @@ async function detectTestFramework2(cwd) {
|
|
|
37902
38189
|
return null;
|
|
37903
38190
|
}
|
|
37904
38191
|
}
|
|
38192
|
+
function toMavenTestFilter(pattern) {
|
|
38193
|
+
const base = path36__default.basename(pattern).replace(/\.java$/, "");
|
|
38194
|
+
return base;
|
|
38195
|
+
}
|
|
38196
|
+
function toGradleTestFilter(pattern) {
|
|
38197
|
+
const base = path36__default.basename(pattern).replace(/\.java$/, "");
|
|
38198
|
+
return `*${base}`;
|
|
38199
|
+
}
|
|
38200
|
+
async function mavenExecutable(cwd) {
|
|
38201
|
+
try {
|
|
38202
|
+
await fs34__default.access(path36__default.join(cwd, "mvnw"));
|
|
38203
|
+
return "./mvnw";
|
|
38204
|
+
} catch {
|
|
38205
|
+
return "mvn";
|
|
38206
|
+
}
|
|
38207
|
+
}
|
|
38208
|
+
async function gradleExecutable(cwd) {
|
|
38209
|
+
try {
|
|
38210
|
+
await fs34__default.access(path36__default.join(cwd, "gradlew"));
|
|
38211
|
+
return "./gradlew";
|
|
38212
|
+
} catch {
|
|
38213
|
+
return "gradle";
|
|
38214
|
+
}
|
|
38215
|
+
}
|
|
37905
38216
|
var runTestsTool = defineTool({
|
|
37906
38217
|
name: "run_tests",
|
|
37907
|
-
description: `Run tests in the project (auto-detects vitest, jest, or mocha).
|
|
38218
|
+
description: `Run tests in the project (auto-detects Maven/Gradle/JUnit, vitest, jest, or mocha).
|
|
37908
38219
|
|
|
37909
38220
|
Examples:
|
|
37910
38221
|
- Run all tests: {}
|
|
37911
38222
|
- With coverage: { "coverage": true }
|
|
37912
|
-
- Specific pattern: { "pattern": "src/**/*.test.ts" }
|
|
37913
|
-
- Specific
|
|
38223
|
+
- Specific pattern (JS): { "pattern": "src/**/*.test.ts" }
|
|
38224
|
+
- Specific test class (Java): { "pattern": "**/ItemRestControllerIT.java" }
|
|
38225
|
+
- Specific framework: { "framework": "maven" }
|
|
38226
|
+
- Maven module: { "framework": "maven", "args": ["-pl", "stock-core"] }`,
|
|
37914
38227
|
category: "test",
|
|
37915
38228
|
parameters: z.object({
|
|
37916
38229
|
cwd: z.string().optional().describe("Project directory"),
|
|
37917
|
-
pattern: z.string().optional().describe("Test file pattern"),
|
|
38230
|
+
pattern: z.string().optional().describe("Test file pattern or class glob"),
|
|
37918
38231
|
coverage: z.boolean().optional().default(false).describe("Collect coverage"),
|
|
37919
|
-
framework: z.string().optional().describe("Test framework (vitest, jest, mocha)"),
|
|
37920
|
-
watch: z.boolean().optional().default(false).describe("Watch mode")
|
|
38232
|
+
framework: z.string().optional().describe("Test framework (maven, gradle, vitest, jest, mocha)"),
|
|
38233
|
+
watch: z.boolean().optional().default(false).describe("Watch mode"),
|
|
38234
|
+
args: z.array(z.string()).optional().describe("Extra arguments (e.g. Maven -pl module)")
|
|
37921
38235
|
}),
|
|
37922
|
-
async execute({ cwd, pattern, coverage, framework, watch }) {
|
|
38236
|
+
async execute({ cwd, pattern, coverage, framework, watch, args: extraArgs }) {
|
|
37923
38237
|
const projectDir = cwd ?? process.cwd();
|
|
37924
38238
|
const detectedFramework = framework ?? await detectTestFramework2(projectDir);
|
|
37925
38239
|
if (!detectedFramework) {
|
|
37926
|
-
throw new ToolError(
|
|
37927
|
-
|
|
37928
|
-
|
|
38240
|
+
throw new ToolError(
|
|
38241
|
+
"No test framework detected. For Java projects ensure pom.xml or build.gradle exists. For Node.js projects install vitest, jest, or mocha.",
|
|
38242
|
+
{ tool: "run_tests" }
|
|
38243
|
+
);
|
|
37929
38244
|
}
|
|
37930
38245
|
const startTime = performance.now();
|
|
37931
38246
|
try {
|
|
37932
38247
|
const args = [];
|
|
37933
38248
|
let command = "npx";
|
|
37934
38249
|
switch (detectedFramework) {
|
|
38250
|
+
case "maven": {
|
|
38251
|
+
command = await mavenExecutable(projectDir);
|
|
38252
|
+
args.push(coverage ? "verify" : "test");
|
|
38253
|
+
if (extraArgs && extraArgs.length > 0) args.push(...extraArgs);
|
|
38254
|
+
if (pattern) args.push(`-Dtest=${toMavenTestFilter(pattern)}`);
|
|
38255
|
+
break;
|
|
38256
|
+
}
|
|
38257
|
+
case "gradle": {
|
|
38258
|
+
command = await gradleExecutable(projectDir);
|
|
38259
|
+
args.push("test");
|
|
38260
|
+
if (extraArgs && extraArgs.length > 0) args.push(...extraArgs);
|
|
38261
|
+
if (pattern) args.push("--tests", toGradleTestFilter(pattern));
|
|
38262
|
+
if (coverage) args.push("jacocoTestReport");
|
|
38263
|
+
break;
|
|
38264
|
+
}
|
|
37935
38265
|
case "vitest":
|
|
37936
38266
|
args.push("vitest", "run");
|
|
37937
38267
|
if (coverage) args.push("--coverage");
|
|
@@ -37969,8 +38299,8 @@ Examples:
|
|
|
37969
38299
|
const duration = performance.now() - startTime;
|
|
37970
38300
|
return parseTestResults(
|
|
37971
38301
|
detectedFramework,
|
|
37972
|
-
result.stdout,
|
|
37973
|
-
result.stderr,
|
|
38302
|
+
result.stdout ?? "",
|
|
38303
|
+
result.stderr ?? "",
|
|
37974
38304
|
result.exitCode ?? 0,
|
|
37975
38305
|
duration
|
|
37976
38306
|
);
|
|
@@ -37984,18 +38314,37 @@ Examples:
|
|
|
37984
38314
|
}
|
|
37985
38315
|
});
|
|
37986
38316
|
function parseTestResults(framework, stdout, stderr, exitCode, duration) {
|
|
37987
|
-
|
|
37988
|
-
|
|
37989
|
-
|
|
37990
|
-
|
|
37991
|
-
|
|
38317
|
+
if (framework === "vitest" || framework === "jest") {
|
|
38318
|
+
try {
|
|
38319
|
+
const jsonMatch = stdout.match(/\{[\s\S]*\}/);
|
|
38320
|
+
if (jsonMatch) {
|
|
38321
|
+
const json2 = JSON.parse(jsonMatch[0]);
|
|
37992
38322
|
return parseJestLikeResults(json2, duration);
|
|
37993
38323
|
}
|
|
38324
|
+
} catch {
|
|
37994
38325
|
}
|
|
37995
|
-
} catch {
|
|
37996
38326
|
}
|
|
37997
|
-
const
|
|
37998
|
-
|
|
38327
|
+
const mavenMatch = stdout.match(
|
|
38328
|
+
/Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)/i
|
|
38329
|
+
);
|
|
38330
|
+
if (mavenMatch) {
|
|
38331
|
+
const total = parseInt(mavenMatch[1] ?? "0", 10);
|
|
38332
|
+
const failures = parseInt(mavenMatch[2] ?? "0", 10);
|
|
38333
|
+
const errors = parseInt(mavenMatch[3] ?? "0", 10);
|
|
38334
|
+
const skipped2 = parseInt(mavenMatch[4] ?? "0", 10);
|
|
38335
|
+
const failed2 = failures + errors;
|
|
38336
|
+
return {
|
|
38337
|
+
passed: total - failed2 - skipped2,
|
|
38338
|
+
failed: failed2,
|
|
38339
|
+
skipped: skipped2,
|
|
38340
|
+
total,
|
|
38341
|
+
duration,
|
|
38342
|
+
success: exitCode === 0,
|
|
38343
|
+
failures: failed2 > 0 ? parseFailuresFromOutput(stderr || stdout) : []
|
|
38344
|
+
};
|
|
38345
|
+
}
|
|
38346
|
+
const passMatch = stdout.match(/(\d+)\s*(?:passed|passing|tests\s+run)/i);
|
|
38347
|
+
const failMatch = stdout.match(/(\d+)\s*(?:failed|failing|failures)/i);
|
|
37999
38348
|
const skipMatch = stdout.match(/(\d+)\s*(?:skipped|pending)/i);
|
|
38000
38349
|
const passed = passMatch ? parseInt(passMatch[1] ?? "0", 10) : 0;
|
|
38001
38350
|
const failed = failMatch ? parseInt(failMatch[1] ?? "0", 10) : 0;
|
|
@@ -38052,6 +38401,37 @@ function parseFailuresFromOutput(output) {
|
|
|
38052
38401
|
}
|
|
38053
38402
|
return failures;
|
|
38054
38403
|
}
|
|
38404
|
+
function parseJacocoCsvCoverage(csv) {
|
|
38405
|
+
const lines = csv.trim().split("\n").slice(1);
|
|
38406
|
+
if (lines.length === 0) return null;
|
|
38407
|
+
let lineMissed = 0, lineCovered = 0;
|
|
38408
|
+
let branchMissed = 0, branchCovered = 0;
|
|
38409
|
+
let methodMissed = 0, methodCovered = 0;
|
|
38410
|
+
let instrMissed = 0, instrCovered = 0;
|
|
38411
|
+
for (const line of lines) {
|
|
38412
|
+
const cols = line.split(",");
|
|
38413
|
+
if (cols.length < 13) continue;
|
|
38414
|
+
instrMissed += parseInt(cols[3] ?? "0", 10);
|
|
38415
|
+
instrCovered += parseInt(cols[4] ?? "0", 10);
|
|
38416
|
+
branchMissed += parseInt(cols[5] ?? "0", 10);
|
|
38417
|
+
branchCovered += parseInt(cols[6] ?? "0", 10);
|
|
38418
|
+
lineMissed += parseInt(cols[7] ?? "0", 10);
|
|
38419
|
+
lineCovered += parseInt(cols[8] ?? "0", 10);
|
|
38420
|
+
methodMissed += parseInt(cols[11] ?? "0", 10);
|
|
38421
|
+
methodCovered += parseInt(cols[12] ?? "0", 10);
|
|
38422
|
+
}
|
|
38423
|
+
if (lineCovered + lineMissed === 0) return null;
|
|
38424
|
+
const pct = (covered, missed) => {
|
|
38425
|
+
const total = covered + missed;
|
|
38426
|
+
return total > 0 ? Math.round(covered / total * 1e3) / 10 : 0;
|
|
38427
|
+
};
|
|
38428
|
+
return {
|
|
38429
|
+
lines: pct(lineCovered, lineMissed),
|
|
38430
|
+
branches: pct(branchCovered, branchMissed),
|
|
38431
|
+
functions: pct(methodCovered, methodMissed),
|
|
38432
|
+
statements: pct(instrCovered, instrMissed)
|
|
38433
|
+
};
|
|
38434
|
+
}
|
|
38055
38435
|
var getCoverageTool = defineTool({
|
|
38056
38436
|
name: "get_coverage",
|
|
38057
38437
|
description: `Get test coverage report (requires running tests with --coverage first).
|
|
@@ -38070,11 +38450,23 @@ Examples:
|
|
|
38070
38450
|
const coverageLocations = [
|
|
38071
38451
|
path36__default.join(projectDir, "coverage", "coverage-summary.json"),
|
|
38072
38452
|
path36__default.join(projectDir, "coverage", "coverage-final.json"),
|
|
38073
|
-
path36__default.join(projectDir, ".nyc_output", "coverage-summary.json")
|
|
38453
|
+
path36__default.join(projectDir, ".nyc_output", "coverage-summary.json"),
|
|
38454
|
+
// Maven JaCoCo
|
|
38455
|
+
path36__default.join(projectDir, "target", "site", "jacoco", "jacoco.csv"),
|
|
38456
|
+
path36__default.join(projectDir, "target", "site", "jacoco-ut", "jacoco.csv"),
|
|
38457
|
+
// Gradle JaCoCo
|
|
38458
|
+
path36__default.join(projectDir, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")
|
|
38074
38459
|
];
|
|
38075
38460
|
for (const location of coverageLocations) {
|
|
38076
38461
|
try {
|
|
38077
38462
|
const content = await fs34__default.readFile(location, "utf-8");
|
|
38463
|
+
if (location.endsWith(".csv")) {
|
|
38464
|
+
const result = parseJacocoCsvCoverage(content);
|
|
38465
|
+
if (result) {
|
|
38466
|
+
return { ...result, report: format === "detailed" ? content : void 0 };
|
|
38467
|
+
}
|
|
38468
|
+
continue;
|
|
38469
|
+
}
|
|
38078
38470
|
const coverage = JSON.parse(content);
|
|
38079
38471
|
if (coverage.total) {
|
|
38080
38472
|
return {
|
|
@@ -38088,9 +38480,10 @@ Examples:
|
|
|
38088
38480
|
} catch {
|
|
38089
38481
|
}
|
|
38090
38482
|
}
|
|
38091
|
-
throw new ToolError(
|
|
38092
|
-
|
|
38093
|
-
|
|
38483
|
+
throw new ToolError(
|
|
38484
|
+
"Coverage data not found. For Maven projects run 'mvn verify' with JaCoCo plugin. For Node.js run tests with --coverage.",
|
|
38485
|
+
{ tool: "get_coverage" }
|
|
38486
|
+
);
|
|
38094
38487
|
} catch (error) {
|
|
38095
38488
|
if (error instanceof ToolError) throw error;
|
|
38096
38489
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -38107,20 +38500,24 @@ var runTestFileTool = defineTool({
|
|
|
38107
38500
|
|
|
38108
38501
|
Examples:
|
|
38109
38502
|
- Single file: { "file": "src/utils.test.ts" }
|
|
38110
|
-
-
|
|
38503
|
+
- Java test: { "file": "**/ItemRestControllerIT.java" }
|
|
38504
|
+
- With framework: { "file": "test/app.spec.js", "framework": "jest" }
|
|
38505
|
+
- Maven module: { "file": "**/MyTest.java", "args": ["-pl", "my-module"] }`,
|
|
38111
38506
|
category: "test",
|
|
38112
38507
|
parameters: z.object({
|
|
38113
38508
|
cwd: z.string().optional().describe("Project directory"),
|
|
38114
|
-
file: z.string().describe("Test file path"),
|
|
38115
|
-
framework: z.string().optional().describe("Test framework")
|
|
38509
|
+
file: z.string().describe("Test file path or class glob"),
|
|
38510
|
+
framework: z.string().optional().describe("Test framework (maven, gradle, vitest, jest, mocha)"),
|
|
38511
|
+
args: z.array(z.string()).optional().describe("Extra arguments (e.g. Maven -pl module)")
|
|
38116
38512
|
}),
|
|
38117
|
-
async execute({ cwd, file, framework }) {
|
|
38513
|
+
async execute({ cwd, file, framework, args }) {
|
|
38118
38514
|
return runTestsTool.execute({
|
|
38119
38515
|
cwd,
|
|
38120
38516
|
pattern: file,
|
|
38121
38517
|
coverage: false,
|
|
38122
38518
|
framework,
|
|
38123
|
-
watch: false
|
|
38519
|
+
watch: false,
|
|
38520
|
+
args
|
|
38124
38521
|
});
|
|
38125
38522
|
}
|
|
38126
38523
|
});
|
|
@@ -40119,7 +40516,7 @@ Examples:
|
|
|
40119
40516
|
if (stats.isFile()) {
|
|
40120
40517
|
filesToSearch = [targetPath];
|
|
40121
40518
|
} else {
|
|
40122
|
-
const globPattern = include ?? "**/*.{ts,tsx,js,jsx,json,md,txt}";
|
|
40519
|
+
const globPattern = include ?? "**/*.{ts,tsx,js,jsx,java,py,go,rs,json,md,txt}";
|
|
40123
40520
|
const defaultExclude = ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/coverage/**"];
|
|
40124
40521
|
const excludePatterns = exclude ?? defaultExclude;
|
|
40125
40522
|
filesToSearch = await glob(globPattern, {
|
|
@@ -40858,7 +41255,205 @@ ${message}
|
|
|
40858
41255
|
}
|
|
40859
41256
|
}
|
|
40860
41257
|
});
|
|
40861
|
-
|
|
41258
|
+
async function resolveMaven(cwd) {
|
|
41259
|
+
try {
|
|
41260
|
+
await fs34__default.access(path36__default.join(cwd, "mvnw"));
|
|
41261
|
+
return "./mvnw";
|
|
41262
|
+
} catch {
|
|
41263
|
+
return "mvn";
|
|
41264
|
+
}
|
|
41265
|
+
}
|
|
41266
|
+
async function resolveGradle(cwd) {
|
|
41267
|
+
try {
|
|
41268
|
+
await fs34__default.access(path36__default.join(cwd, "gradlew"));
|
|
41269
|
+
return "./gradlew";
|
|
41270
|
+
} catch {
|
|
41271
|
+
return "gradle";
|
|
41272
|
+
}
|
|
41273
|
+
}
|
|
41274
|
+
var runMavenTool = defineTool({
|
|
41275
|
+
name: "run_maven",
|
|
41276
|
+
description: `Run a Maven goal (auto-detects ./mvnw wrapper).
|
|
41277
|
+
|
|
41278
|
+
Examples:
|
|
41279
|
+
- Compile: { "goal": "compile" }
|
|
41280
|
+
- Run tests: { "goal": "test" }
|
|
41281
|
+
- Package: { "goal": "package" }
|
|
41282
|
+
- Skip tests: { "goal": "package", "args": ["-DskipTests"] }
|
|
41283
|
+
- Specific module: { "goal": "test", "args": ["-pl", "stock-core"] }
|
|
41284
|
+
- Quiet mode: { "goal": "verify", "args": ["-q", "--no-transfer-progress"] }`,
|
|
41285
|
+
category: "build",
|
|
41286
|
+
parameters: z.object({
|
|
41287
|
+
goal: z.string().describe("Maven goal (compile, test, package, verify, clean, install, ...)"),
|
|
41288
|
+
cwd: z.string().optional().describe("Project directory"),
|
|
41289
|
+
args: z.array(z.string()).optional().describe("Additional Maven arguments"),
|
|
41290
|
+
env: z.record(z.string(), z.string()).optional().describe("Environment variables"),
|
|
41291
|
+
timeout: z.number().optional().describe("Timeout in milliseconds")
|
|
41292
|
+
}),
|
|
41293
|
+
async execute({ goal, cwd, args, env: env2, timeout }) {
|
|
41294
|
+
const projectDir = cwd ?? process.cwd();
|
|
41295
|
+
const startTime = performance.now();
|
|
41296
|
+
const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS4;
|
|
41297
|
+
const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
|
|
41298
|
+
const heartbeat = new CommandHeartbeat2({
|
|
41299
|
+
onUpdate: (stats) => {
|
|
41300
|
+
if (stats.elapsedSeconds > 10)
|
|
41301
|
+
process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
|
|
41302
|
+
},
|
|
41303
|
+
onWarn: (message) => process.stderr.write(`
|
|
41304
|
+
${message}
|
|
41305
|
+
`)
|
|
41306
|
+
});
|
|
41307
|
+
try {
|
|
41308
|
+
heartbeat.start();
|
|
41309
|
+
const command = await resolveMaven(projectDir);
|
|
41310
|
+
const cmdArgs = [goal, "--no-transfer-progress", "-B", ...args ?? []];
|
|
41311
|
+
const subprocess = execa(command, cmdArgs, {
|
|
41312
|
+
cwd: projectDir,
|
|
41313
|
+
timeout: timeoutMs,
|
|
41314
|
+
env: { ...process.env, ...env2 },
|
|
41315
|
+
reject: false,
|
|
41316
|
+
buffer: false,
|
|
41317
|
+
maxBuffer: MAX_OUTPUT_SIZE2
|
|
41318
|
+
});
|
|
41319
|
+
let stdoutBuffer = "";
|
|
41320
|
+
let stderrBuffer = "";
|
|
41321
|
+
subprocess.stdout?.on("data", (chunk) => {
|
|
41322
|
+
const text13 = chunk.toString();
|
|
41323
|
+
stdoutBuffer += text13;
|
|
41324
|
+
process.stdout.write(text13);
|
|
41325
|
+
heartbeat.activity();
|
|
41326
|
+
});
|
|
41327
|
+
subprocess.stderr?.on("data", (chunk) => {
|
|
41328
|
+
const text13 = chunk.toString();
|
|
41329
|
+
stderrBuffer += text13;
|
|
41330
|
+
process.stderr.write(text13);
|
|
41331
|
+
heartbeat.activity();
|
|
41332
|
+
});
|
|
41333
|
+
const result = await subprocess;
|
|
41334
|
+
const buildResult2 = {
|
|
41335
|
+
success: result.exitCode === 0,
|
|
41336
|
+
stdout: truncateOutput2(stdoutBuffer),
|
|
41337
|
+
stderr: truncateOutput2(stderrBuffer),
|
|
41338
|
+
exitCode: result.exitCode ?? 0,
|
|
41339
|
+
duration: performance.now() - startTime
|
|
41340
|
+
};
|
|
41341
|
+
if (!buildResult2.success) {
|
|
41342
|
+
buildResult2.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_maven");
|
|
41343
|
+
}
|
|
41344
|
+
return buildResult2;
|
|
41345
|
+
} catch (error) {
|
|
41346
|
+
if (error.timedOut) {
|
|
41347
|
+
throw new TimeoutError(`Maven goal '${goal}' timed out after ${timeoutMs}ms`, {
|
|
41348
|
+
timeoutMs,
|
|
41349
|
+
operation: `mvn ${goal}`
|
|
41350
|
+
});
|
|
41351
|
+
}
|
|
41352
|
+
throw new ToolError(
|
|
41353
|
+
`Maven failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
41354
|
+
{ tool: "run_maven", cause: error instanceof Error ? error : void 0 }
|
|
41355
|
+
);
|
|
41356
|
+
} finally {
|
|
41357
|
+
heartbeat.stop();
|
|
41358
|
+
process.stderr.write("\r \r");
|
|
41359
|
+
}
|
|
41360
|
+
}
|
|
41361
|
+
});
|
|
41362
|
+
var runGradleTool = defineTool({
|
|
41363
|
+
name: "run_gradle",
|
|
41364
|
+
description: `Run a Gradle task (auto-detects ./gradlew wrapper).
|
|
41365
|
+
|
|
41366
|
+
Examples:
|
|
41367
|
+
- Build: { "task": "build" }
|
|
41368
|
+
- Run tests: { "task": "test" }
|
|
41369
|
+
- Assemble: { "task": "assemble" }
|
|
41370
|
+
- Skip tests: { "task": "build", "args": ["-x", "test"] }
|
|
41371
|
+
- Specific subproject: { "task": ":stock-core:test" }`,
|
|
41372
|
+
category: "build",
|
|
41373
|
+
parameters: z.object({
|
|
41374
|
+
task: z.string().describe("Gradle task (build, test, assemble, clean, check, ...)"),
|
|
41375
|
+
cwd: z.string().optional().describe("Project directory"),
|
|
41376
|
+
args: z.array(z.string()).optional().describe("Additional Gradle arguments"),
|
|
41377
|
+
env: z.record(z.string(), z.string()).optional().describe("Environment variables"),
|
|
41378
|
+
timeout: z.number().optional().describe("Timeout in milliseconds")
|
|
41379
|
+
}),
|
|
41380
|
+
async execute({ task, cwd, args, env: env2, timeout }) {
|
|
41381
|
+
const projectDir = cwd ?? process.cwd();
|
|
41382
|
+
const startTime = performance.now();
|
|
41383
|
+
const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS4;
|
|
41384
|
+
const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
|
|
41385
|
+
const heartbeat = new CommandHeartbeat2({
|
|
41386
|
+
onUpdate: (stats) => {
|
|
41387
|
+
if (stats.elapsedSeconds > 10)
|
|
41388
|
+
process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
|
|
41389
|
+
},
|
|
41390
|
+
onWarn: (message) => process.stderr.write(`
|
|
41391
|
+
${message}
|
|
41392
|
+
`)
|
|
41393
|
+
});
|
|
41394
|
+
try {
|
|
41395
|
+
heartbeat.start();
|
|
41396
|
+
const command = await resolveGradle(projectDir);
|
|
41397
|
+
const cmdArgs = [task, "--console=plain", ...args ?? []];
|
|
41398
|
+
const subprocess = execa(command, cmdArgs, {
|
|
41399
|
+
cwd: projectDir,
|
|
41400
|
+
timeout: timeoutMs,
|
|
41401
|
+
env: { ...process.env, ...env2 },
|
|
41402
|
+
reject: false,
|
|
41403
|
+
buffer: false,
|
|
41404
|
+
maxBuffer: MAX_OUTPUT_SIZE2
|
|
41405
|
+
});
|
|
41406
|
+
let stdoutBuffer = "";
|
|
41407
|
+
let stderrBuffer = "";
|
|
41408
|
+
subprocess.stdout?.on("data", (chunk) => {
|
|
41409
|
+
const text13 = chunk.toString();
|
|
41410
|
+
stdoutBuffer += text13;
|
|
41411
|
+
process.stdout.write(text13);
|
|
41412
|
+
heartbeat.activity();
|
|
41413
|
+
});
|
|
41414
|
+
subprocess.stderr?.on("data", (chunk) => {
|
|
41415
|
+
const text13 = chunk.toString();
|
|
41416
|
+
stderrBuffer += text13;
|
|
41417
|
+
process.stderr.write(text13);
|
|
41418
|
+
heartbeat.activity();
|
|
41419
|
+
});
|
|
41420
|
+
const result = await subprocess;
|
|
41421
|
+
const buildResult2 = {
|
|
41422
|
+
success: result.exitCode === 0,
|
|
41423
|
+
stdout: truncateOutput2(stdoutBuffer),
|
|
41424
|
+
stderr: truncateOutput2(stderrBuffer),
|
|
41425
|
+
exitCode: result.exitCode ?? 0,
|
|
41426
|
+
duration: performance.now() - startTime
|
|
41427
|
+
};
|
|
41428
|
+
if (!buildResult2.success) {
|
|
41429
|
+
buildResult2.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_gradle");
|
|
41430
|
+
}
|
|
41431
|
+
return buildResult2;
|
|
41432
|
+
} catch (error) {
|
|
41433
|
+
if (error.timedOut) {
|
|
41434
|
+
throw new TimeoutError(`Gradle task '${task}' timed out after ${timeoutMs}ms`, {
|
|
41435
|
+
timeoutMs,
|
|
41436
|
+
operation: `gradle ${task}`
|
|
41437
|
+
});
|
|
41438
|
+
}
|
|
41439
|
+
throw new ToolError(
|
|
41440
|
+
`Gradle failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
41441
|
+
{ tool: "run_gradle", cause: error instanceof Error ? error : void 0 }
|
|
41442
|
+
);
|
|
41443
|
+
} finally {
|
|
41444
|
+
heartbeat.stop();
|
|
41445
|
+
process.stderr.write("\r \r");
|
|
41446
|
+
}
|
|
41447
|
+
}
|
|
41448
|
+
});
|
|
41449
|
+
var buildTools = [
|
|
41450
|
+
runScriptTool,
|
|
41451
|
+
installDepsTool,
|
|
41452
|
+
makeTool,
|
|
41453
|
+
tscTool,
|
|
41454
|
+
runMavenTool,
|
|
41455
|
+
runGradleTool
|
|
41456
|
+
];
|
|
40862
41457
|
|
|
40863
41458
|
// src/tools/permissions.ts
|
|
40864
41459
|
init_registry4();
|
|
@@ -47923,11 +48518,21 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
47923
48518
|
break;
|
|
47924
48519
|
}
|
|
47925
48520
|
}
|
|
47926
|
-
const inputText = messages.map((m) =>
|
|
48521
|
+
const inputText = messages.map((m) => {
|
|
48522
|
+
if (typeof m.content === "string") return m.content;
|
|
48523
|
+
try {
|
|
48524
|
+
return JSON.stringify(m.content);
|
|
48525
|
+
} catch {
|
|
48526
|
+
return "";
|
|
48527
|
+
}
|
|
48528
|
+
}).join("\n");
|
|
47927
48529
|
const estimatedInputTokens = provider.countTokens(inputText);
|
|
47928
|
-
|
|
47929
|
-
|
|
47930
|
-
|
|
48530
|
+
let serializedToolCalls = "";
|
|
48531
|
+
try {
|
|
48532
|
+
serializedToolCalls = JSON.stringify(collectedToolCalls);
|
|
48533
|
+
} catch {
|
|
48534
|
+
}
|
|
48535
|
+
const estimatedOutputTokens = provider.countTokens(responseContent + serializedToolCalls);
|
|
47931
48536
|
totalInputTokens += estimatedInputTokens;
|
|
47932
48537
|
totalOutputTokens += estimatedOutputTokens;
|
|
47933
48538
|
if (collectedToolCalls.length === 0) {
|
|
@@ -49419,7 +50024,8 @@ async function startRepl(options = {}) {
|
|
|
49419
50024
|
const compactionResult = await checkAndCompactContext(
|
|
49420
50025
|
session,
|
|
49421
50026
|
provider,
|
|
49422
|
-
compactAbort.signal
|
|
50027
|
+
compactAbort.signal,
|
|
50028
|
+
toolRegistry
|
|
49423
50029
|
);
|
|
49424
50030
|
if (compactionResult?.wasCompacted) {
|
|
49425
50031
|
usageForDisplay = getContextUsagePercent(session);
|