@corbat-tech/coco 2.14.1 → 2.15.1

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
@@ -4705,12 +4705,12 @@ var init_copilot2 = __esm({
4705
4705
  init_openai();
4706
4706
  init_copilot();
4707
4707
  CONTEXT_WINDOWS4 = {
4708
- // Claude models
4709
- "claude-sonnet-4.6": 2e5,
4710
- "claude-opus-4.6": 2e5,
4711
- "claude-sonnet-4.5": 2e5,
4712
- "claude-opus-4.5": 2e5,
4713
- "claude-haiku-4.5": 2e5,
4708
+ // Claude models — Copilot API caps these at 168 000 (not 200 000 like Anthropic direct)
4709
+ "claude-sonnet-4.6": 168e3,
4710
+ "claude-opus-4.6": 168e3,
4711
+ "claude-sonnet-4.5": 168e3,
4712
+ "claude-opus-4.5": 168e3,
4713
+ "claude-haiku-4.5": 168e3,
4714
4714
  // OpenAI models — chat/completions
4715
4715
  "gpt-4.1": 1048576,
4716
4716
  // OpenAI models — /responses API (Codex/GPT-5+)
@@ -9401,7 +9401,7 @@ function humanizeError(message, toolName) {
9401
9401
  )) {
9402
9402
  return msg;
9403
9403
  }
9404
- if (/run git_init\b/.test(msg)) {
9404
+ if (/run git_init\b/i.test(msg)) {
9405
9405
  return msg;
9406
9406
  }
9407
9407
  if (/ECONNREFUSED/i.test(msg)) {
@@ -10450,6 +10450,18 @@ var init_subprocess_registry = __esm({
10450
10450
  }
10451
10451
  });
10452
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
+ }
10453
10465
  try {
10454
10466
  const pkgPath = join(projectPath, "package.json");
10455
10467
  const pkgContent = await readFile(pkgPath, "utf-8");
@@ -10511,6 +10523,55 @@ function parseCoverageSummary(report) {
10511
10523
  }
10512
10524
  };
10513
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
+ }
10514
10575
  var CoverageAnalyzer;
10515
10576
  var init_coverage = __esm({
10516
10577
  "src/quality/analyzers/coverage.ts"() {
@@ -10524,29 +10585,53 @@ var init_coverage = __esm({
10524
10585
  */
10525
10586
  async analyze() {
10526
10587
  const framework = await detectTestFramework(this.projectPath);
10527
- const coverageTool = await detectCoverageTool(this.projectPath);
10528
10588
  if (!framework) {
10529
- throw new Error("No test framework detected (vitest, jest, or mocha)");
10589
+ return this.zeroCoverage();
10530
10590
  }
10531
- const existingCoverage = await this.readExistingCoverage();
10591
+ const existingCoverage = await this.readExistingCoverage(framework);
10532
10592
  if (existingCoverage) {
10533
10593
  return existingCoverage;
10534
10594
  }
10595
+ if (framework === "maven" || framework === "gradle") {
10596
+ return this.zeroCoverage();
10597
+ }
10598
+ const coverageTool = await detectCoverageTool(this.projectPath);
10535
10599
  return await this.runWithCoverage(framework, coverageTool);
10536
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
+ }
10537
10606
  /**
10538
- * 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.
10539
10609
  */
10540
- 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
+ }
10541
10626
  const possiblePaths = [
10542
10627
  join(this.projectPath, "coverage", "coverage-summary.json"),
10543
10628
  join(this.projectPath, ".coverage", "coverage-summary.json"),
10544
10629
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
10545
10630
  ];
10546
- for (const path57 of possiblePaths) {
10631
+ for (const p45 of possiblePaths) {
10547
10632
  try {
10548
- await access(path57, constants.R_OK);
10549
- const content = await readFile(path57, "utf-8");
10633
+ await access(p45, constants.R_OK);
10634
+ const content = await readFile(p45, "utf-8");
10550
10635
  const report = JSON.parse(content);
10551
10636
  return parseCoverageSummary(report);
10552
10637
  } catch {
@@ -11221,7 +11306,7 @@ var init_build_verifier = __esm({
11221
11306
  stderr: ""
11222
11307
  };
11223
11308
  }
11224
- 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-]+)*$/;
11225
11310
  if (!SAFE_BUILD_PATTERN.test(buildCommand2.trim())) {
11226
11311
  return {
11227
11312
  success: false,
@@ -11326,9 +11411,20 @@ var init_build_verifier = __esm({
11326
11411
  }
11327
11412
  }
11328
11413
  /**
11329
- * Detect build command from package.json
11414
+ * Detect build command from project build files.
11415
+ * Checks Maven, Gradle, and Node.js in that order.
11330
11416
  */
11331
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
+ }
11332
11428
  try {
11333
11429
  const packageJsonPath = path36.join(this.projectPath, "package.json");
11334
11430
  const content = await fs34.readFile(packageJsonPath, "utf-8");
@@ -11339,10 +11435,9 @@ var init_build_verifier = __esm({
11339
11435
  if (packageJson.devDependencies?.typescript || packageJson.dependencies?.typescript) {
11340
11436
  return "npx tsc --noEmit";
11341
11437
  }
11342
- return null;
11343
11438
  } catch {
11344
- return null;
11345
11439
  }
11440
+ return null;
11346
11441
  }
11347
11442
  /**
11348
11443
  * Parse errors from build output
@@ -11416,6 +11511,16 @@ var init_build_verifier = __esm({
11416
11511
  };
11417
11512
  }
11418
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
+ }
11419
11524
  function parseVitestOutput(stdout) {
11420
11525
  const testsMatch = stdout.match(
11421
11526
  /Tests\s+(?:(\d+)\s+passed)?(?:\s*\|\s*(\d+)\s+failed)?(?:\s*\|\s*(\d+)\s+skipped)?/
@@ -11468,10 +11573,29 @@ function buildTestCommand(framework) {
11468
11573
  return { command: "npx", args: ["jest", "--json"] };
11469
11574
  case "mocha":
11470
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"] };
11471
11580
  default:
11472
11581
  return null;
11473
11582
  }
11474
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
+ }
11475
11599
  var CorrectnessAnalyzer;
11476
11600
  var init_correctness = __esm({
11477
11601
  "src/quality/analyzers/correctness.ts"() {
@@ -11529,6 +11653,11 @@ var init_correctness = __esm({
11529
11653
  if (!cmd) {
11530
11654
  return { passed: 0, failed: 0, skipped: 0 };
11531
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
+ }
11532
11661
  try {
11533
11662
  const proc = execa(cmd.command, cmd.args, {
11534
11663
  cwd: this.projectPath,
@@ -11540,15 +11669,15 @@ var init_correctness = __esm({
11540
11669
  });
11541
11670
  trackSubprocess(proc);
11542
11671
  const result = await proc;
11543
- const output = result.stdout + "\n" + result.stderr;
11672
+ const output = (result.stdout ?? "") + "\n" + (result.stderr ?? "");
11544
11673
  switch (framework) {
11545
11674
  case "vitest":
11546
11675
  return parseVitestOutput(output);
11547
11676
  case "jest":
11548
- return parseJestOutput(result.stdout);
11677
+ return parseJestOutput(result.stdout ?? "");
11549
11678
  case "mocha": {
11550
11679
  try {
11551
- const json2 = JSON.parse(result.stdout);
11680
+ const json2 = JSON.parse(result.stdout ?? "");
11552
11681
  return {
11553
11682
  passed: json2.stats?.passes ?? 0,
11554
11683
  failed: json2.stats?.failures ?? 0,
@@ -11558,6 +11687,9 @@ var init_correctness = __esm({
11558
11687
  return { passed: 0, failed: 0, skipped: 0 };
11559
11688
  }
11560
11689
  }
11690
+ case "maven":
11691
+ case "gradle":
11692
+ return parseMavenOutput(output);
11561
11693
  default:
11562
11694
  return { passed: 0, failed: 0, skipped: 0 };
11563
11695
  }
@@ -14498,9 +14630,31 @@ var init_evaluator = __esm({
14498
14630
  return suggestions;
14499
14631
  }
14500
14632
  /**
14501
- * Find source files in project
14633
+ * Find source files in project, adapting to the detected language stack.
14502
14634
  */
14503
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
+ }
14504
14658
  return glob("**/*.{ts,js,tsx,jsx}", {
14505
14659
  cwd: this.projectPath,
14506
14660
  absolute: true,
@@ -14511,6 +14665,18 @@ var init_evaluator = __esm({
14511
14665
  }
14512
14666
  });
14513
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
+ }
14514
14680
  try {
14515
14681
  const pkgPath = path36__default.join(cwd, "package.json");
14516
14682
  const pkgContent = await fs34__default.readFile(pkgPath, "utf-8");
@@ -14527,6 +14693,44 @@ async function detectLinter2(cwd) {
14527
14693
  return null;
14528
14694
  }
14529
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
+ }
14530
14734
  function parseLintResults(_linter, stdout, _stderr) {
14531
14735
  const issues = [];
14532
14736
  let errors = 0;
@@ -14566,6 +14770,28 @@ function parseLintResults(_linter, stdout, _stderr) {
14566
14770
  }
14567
14771
  async function findSourceFiles(cwd) {
14568
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
+ }
14569
14795
  return glob17("src/**/*.{ts,js,tsx,jsx}", {
14570
14796
  cwd,
14571
14797
  absolute: true,
@@ -14583,6 +14809,8 @@ function analyzeFileComplexity(content, file) {
14583
14809
  const line = lines[i] ?? "";
14584
14810
  const funcMatch = line.match(
14585
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*\(/
14586
14814
  );
14587
14815
  if (funcMatch && braceDepth === 0) {
14588
14816
  if (currentFunction) {
@@ -14625,19 +14853,20 @@ var init_quality = __esm({
14625
14853
  init_evaluator();
14626
14854
  runLinterTool = defineTool({
14627
14855
  name: "run_linter",
14628
- 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).
14629
14857
 
14630
14858
  Examples:
14631
14859
  - Lint all: {} \u2192 { "errors": 0, "warnings": 5, "score": 90 }
14632
- - Auto-fix: { "fix": true }
14860
+ - Auto-fix (Node.js): { "fix": true }
14633
14861
  - Specific files: { "files": ["src/app.ts", "src/utils.ts"] }
14634
- - Force linter: { "linter": "eslint" }`,
14862
+ - Force linter: { "linter": "eslint" }
14863
+ - Java project (Maven): automatically runs checkstyle:check if plugin is configured`,
14635
14864
  category: "quality",
14636
14865
  parameters: z.object({
14637
14866
  cwd: z.string().optional().describe("Project directory"),
14638
14867
  files: z.array(z.string()).optional().describe("Specific files to lint"),
14639
- fix: z.boolean().optional().default(false).describe("Auto-fix issues"),
14640
- 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)")
14641
14870
  }),
14642
14871
  async execute({ cwd, files, fix, linter }) {
14643
14872
  const projectDir = cwd ?? process.cwd();
@@ -14650,13 +14879,23 @@ Examples:
14650
14879
  issues: [],
14651
14880
  score: null,
14652
14881
  linter: "none",
14653
- 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."
14654
14883
  };
14655
14884
  }
14656
14885
  try {
14657
14886
  const args = [];
14658
14887
  let command = "npx";
14659
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
+ }
14660
14899
  case "oxlint":
14661
14900
  args.push("oxlint");
14662
14901
  if (files && files.length > 0) {
@@ -14697,7 +14936,25 @@ Examples:
14697
14936
  reject: false,
14698
14937
  timeout: 12e4
14699
14938
  });
14700
- 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 ?? "");
14701
14958
  } catch (error) {
14702
14959
  throw new ToolError(
14703
14960
  `Linting failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -20220,7 +20477,8 @@ async function detectProjectStack(cwd) {
20220
20477
  testingFrameworks = parsed.testingFrameworks;
20221
20478
  languages = parsed.languages;
20222
20479
  } else if (stack === "java") {
20223
- 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);
20224
20482
  dependencies = parsed.dependencies;
20225
20483
  frameworks = parsed.frameworks;
20226
20484
  buildTools2 = parsed.buildTools;
@@ -37902,6 +38160,18 @@ init_registry4();
37902
38160
  init_errors();
37903
38161
  init_subprocess_registry();
37904
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
+ }
37905
38175
  try {
37906
38176
  const pkgPath = path36__default.join(cwd, "package.json");
37907
38177
  const pkgContent = await fs34__default.readFile(pkgPath, "utf-8");
@@ -37919,36 +38189,79 @@ async function detectTestFramework2(cwd) {
37919
38189
  return null;
37920
38190
  }
37921
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
+ }
37922
38216
  var runTestsTool = defineTool({
37923
38217
  name: "run_tests",
37924
- 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).
37925
38219
 
37926
38220
  Examples:
37927
38221
  - Run all tests: {}
37928
38222
  - With coverage: { "coverage": true }
37929
- - Specific pattern: { "pattern": "src/**/*.test.ts" }
37930
- - 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"] }`,
37931
38227
  category: "test",
37932
38228
  parameters: z.object({
37933
38229
  cwd: z.string().optional().describe("Project directory"),
37934
- pattern: z.string().optional().describe("Test file pattern"),
38230
+ pattern: z.string().optional().describe("Test file pattern or class glob"),
37935
38231
  coverage: z.boolean().optional().default(false).describe("Collect coverage"),
37936
- framework: z.string().optional().describe("Test framework (vitest, jest, mocha)"),
37937
- 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)")
37938
38235
  }),
37939
- async execute({ cwd, pattern, coverage, framework, watch }) {
38236
+ async execute({ cwd, pattern, coverage, framework, watch, args: extraArgs }) {
37940
38237
  const projectDir = cwd ?? process.cwd();
37941
38238
  const detectedFramework = framework ?? await detectTestFramework2(projectDir);
37942
38239
  if (!detectedFramework) {
37943
- throw new ToolError("No test framework detected. Install vitest, jest, or mocha.", {
37944
- tool: "run_tests"
37945
- });
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
+ );
37946
38244
  }
37947
38245
  const startTime = performance.now();
37948
38246
  try {
37949
38247
  const args = [];
37950
38248
  let command = "npx";
37951
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
+ }
37952
38265
  case "vitest":
37953
38266
  args.push("vitest", "run");
37954
38267
  if (coverage) args.push("--coverage");
@@ -37986,8 +38299,8 @@ Examples:
37986
38299
  const duration = performance.now() - startTime;
37987
38300
  return parseTestResults(
37988
38301
  detectedFramework,
37989
- result.stdout,
37990
- result.stderr,
38302
+ result.stdout ?? "",
38303
+ result.stderr ?? "",
37991
38304
  result.exitCode ?? 0,
37992
38305
  duration
37993
38306
  );
@@ -38001,18 +38314,37 @@ Examples:
38001
38314
  }
38002
38315
  });
38003
38316
  function parseTestResults(framework, stdout, stderr, exitCode, duration) {
38004
- try {
38005
- const jsonMatch = stdout.match(/\{[\s\S]*\}/);
38006
- if (jsonMatch) {
38007
- const json2 = JSON.parse(jsonMatch[0]);
38008
- 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]);
38009
38322
  return parseJestLikeResults(json2, duration);
38010
38323
  }
38324
+ } catch {
38011
38325
  }
38012
- } catch {
38013
38326
  }
38014
- const passMatch = stdout.match(/(\d+)\s*(?:passed|passing)/i);
38015
- 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);
38016
38348
  const skipMatch = stdout.match(/(\d+)\s*(?:skipped|pending)/i);
38017
38349
  const passed = passMatch ? parseInt(passMatch[1] ?? "0", 10) : 0;
38018
38350
  const failed = failMatch ? parseInt(failMatch[1] ?? "0", 10) : 0;
@@ -38069,6 +38401,37 @@ function parseFailuresFromOutput(output) {
38069
38401
  }
38070
38402
  return failures;
38071
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
+ }
38072
38435
  var getCoverageTool = defineTool({
38073
38436
  name: "get_coverage",
38074
38437
  description: `Get test coverage report (requires running tests with --coverage first).
@@ -38087,11 +38450,23 @@ Examples:
38087
38450
  const coverageLocations = [
38088
38451
  path36__default.join(projectDir, "coverage", "coverage-summary.json"),
38089
38452
  path36__default.join(projectDir, "coverage", "coverage-final.json"),
38090
- 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")
38091
38459
  ];
38092
38460
  for (const location of coverageLocations) {
38093
38461
  try {
38094
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
+ }
38095
38470
  const coverage = JSON.parse(content);
38096
38471
  if (coverage.total) {
38097
38472
  return {
@@ -38105,9 +38480,10 @@ Examples:
38105
38480
  } catch {
38106
38481
  }
38107
38482
  }
38108
- throw new ToolError("Coverage data not found. Run tests with --coverage first.", {
38109
- tool: "get_coverage"
38110
- });
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
+ );
38111
38487
  } catch (error) {
38112
38488
  if (error instanceof ToolError) throw error;
38113
38489
  const msg = error instanceof Error ? error.message : String(error);
@@ -38124,20 +38500,24 @@ var runTestFileTool = defineTool({
38124
38500
 
38125
38501
  Examples:
38126
38502
  - Single file: { "file": "src/utils.test.ts" }
38127
- - 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"] }`,
38128
38506
  category: "test",
38129
38507
  parameters: z.object({
38130
38508
  cwd: z.string().optional().describe("Project directory"),
38131
- file: z.string().describe("Test file path"),
38132
- 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)")
38133
38512
  }),
38134
- async execute({ cwd, file, framework }) {
38513
+ async execute({ cwd, file, framework, args }) {
38135
38514
  return runTestsTool.execute({
38136
38515
  cwd,
38137
38516
  pattern: file,
38138
38517
  coverage: false,
38139
38518
  framework,
38140
- watch: false
38519
+ watch: false,
38520
+ args
38141
38521
  });
38142
38522
  }
38143
38523
  });
@@ -40136,7 +40516,7 @@ Examples:
40136
40516
  if (stats.isFile()) {
40137
40517
  filesToSearch = [targetPath];
40138
40518
  } else {
40139
- 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}";
40140
40520
  const defaultExclude = ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/coverage/**"];
40141
40521
  const excludePatterns = exclude ?? defaultExclude;
40142
40522
  filesToSearch = await glob(globPattern, {
@@ -40875,7 +41255,205 @@ ${message}
40875
41255
  }
40876
41256
  }
40877
41257
  });
40878
- 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
+ ];
40879
41457
 
40880
41458
  // src/tools/permissions.ts
40881
41459
  init_registry4();
@@ -47940,11 +48518,21 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
47940
48518
  break;
47941
48519
  }
47942
48520
  }
47943
- 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");
47944
48529
  const estimatedInputTokens = provider.countTokens(inputText);
47945
- const estimatedOutputTokens = provider.countTokens(
47946
- responseContent + JSON.stringify(collectedToolCalls)
47947
- );
48530
+ let serializedToolCalls = "";
48531
+ try {
48532
+ serializedToolCalls = JSON.stringify(collectedToolCalls);
48533
+ } catch {
48534
+ }
48535
+ const estimatedOutputTokens = provider.countTokens(responseContent + serializedToolCalls);
47948
48536
  totalInputTokens += estimatedInputTokens;
47949
48537
  totalOutputTokens += estimatedOutputTokens;
47950
48538
  if (collectedToolCalls.length === 0) {
@@ -49485,6 +50073,25 @@ async function startRepl(options = {}) {
49485
50073
  continue;
49486
50074
  }
49487
50075
  const errorMsg = error instanceof Error ? error.message : String(error);
50076
+ if (errorMsg.includes("prompt token count") && errorMsg.includes("exceeds the limit")) {
50077
+ renderError("Context window full \u2014 compacting conversation history...");
50078
+ try {
50079
+ const compactionResult = await checkAndCompactContext(
50080
+ session,
50081
+ provider,
50082
+ void 0,
50083
+ toolRegistry
50084
+ );
50085
+ if (compactionResult?.wasCompacted) {
50086
+ console.log(chalk2.green(" \u2713 Context compacted. Please retry your message."));
50087
+ } else {
50088
+ console.log(chalk2.yellow(" \u26A0 Could not compact context. Use /clear to start fresh."));
50089
+ }
50090
+ } catch {
50091
+ console.log(chalk2.yellow(" \u26A0 Context compaction failed. Use /clear to start fresh."));
50092
+ }
50093
+ continue;
50094
+ }
49488
50095
  if (errorMsg.includes("context length") || errorMsg.includes("tokens to keep")) {
49489
50096
  renderError(errorMsg);
49490
50097
  console.log();