@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 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
- throw new Error("No test framework detected (vitest, jest, or mocha)");
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 path57 of possiblePaths) {
10631
+ for (const p45 of possiblePaths) {
10541
10632
  try {
10542
- await access(path57, constants.R_OK);
10543
- const content = await readFile(path57, "utf-8");
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 package.json
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, or biome).
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
- return parseLintResults(detectedLinter, result.stdout, result.stderr);
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 parsed = await parsePomXml(cwd);
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 framework: { "framework": "vitest" }`,
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("No test framework detected. Install vitest, jest, or mocha.", {
37927
- tool: "run_tests"
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
- try {
37988
- const jsonMatch = stdout.match(/\{[\s\S]*\}/);
37989
- if (jsonMatch) {
37990
- const json2 = JSON.parse(jsonMatch[0]);
37991
- if (framework === "vitest" || framework === "jest") {
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 passMatch = stdout.match(/(\d+)\s*(?:passed|passing)/i);
37998
- const failMatch = stdout.match(/(\d+)\s*(?:failed|failing)/i);
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("Coverage data not found. Run tests with --coverage first.", {
38092
- tool: "get_coverage"
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
- - With framework: { "file": "test/app.spec.js", "framework": "jest" }`,
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
- var buildTools = [runScriptTool, installDepsTool, makeTool, tscTool];
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) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)).join("\n");
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
- const estimatedOutputTokens = provider.countTokens(
47929
- responseContent + JSON.stringify(collectedToolCalls)
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);