open-agents-ai 0.12.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +497 -88
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1170,7 +1170,7 @@ var init_shell = __esm({
1170
1170
  const timeout = args["timeout"] ?? this.defaultTimeout;
1171
1171
  const stdinInput = args["stdin"];
1172
1172
  const start = performance.now();
1173
- return new Promise((resolve14) => {
1173
+ return new Promise((resolve15) => {
1174
1174
  const child = spawn("bash", ["-c", command], {
1175
1175
  cwd: this.workingDir,
1176
1176
  env: {
@@ -1223,7 +1223,7 @@ var init_shell = __esm({
1223
1223
  const combined = stdout + stderr;
1224
1224
  const looksInteractive = /\? .+[›>]|y\/n|yes\/no|\(Y\/n\)|\[y\/N\]/i.test(combined);
1225
1225
  const hint = looksInteractive ? " The command appears to be waiting for interactive input. Use non-interactive flags (e.g., --yes, --no-input) or provide input via the stdin parameter." : "";
1226
- resolve14({
1226
+ resolve15({
1227
1227
  success: false,
1228
1228
  output: stdout,
1229
1229
  error: `Command timed out after ${timeout}ms.${hint}`,
@@ -1232,7 +1232,7 @@ var init_shell = __esm({
1232
1232
  return;
1233
1233
  }
1234
1234
  const success = code === 0;
1235
- resolve14({
1235
+ resolve15({
1236
1236
  success,
1237
1237
  output: stdout + (stderr && success ? `
1238
1238
  STDERR:
@@ -1243,7 +1243,7 @@ ${stderr}` : ""),
1243
1243
  });
1244
1244
  child.on("error", (err) => {
1245
1245
  clearTimeout(timer);
1246
- resolve14({
1246
+ resolve15({
1247
1247
  success: false,
1248
1248
  output: stdout,
1249
1249
  error: err.message,
@@ -1747,24 +1747,50 @@ var init_web_search = __esm({
1747
1747
  // packages/execution/dist/tools/file-edit.js
1748
1748
  import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
1749
1749
  import { resolve as resolve5 } from "node:path";
1750
+ function countOccurrences(haystack, needle) {
1751
+ let count = 0;
1752
+ let pos = 0;
1753
+ while ((pos = haystack.indexOf(needle, pos)) !== -1) {
1754
+ count++;
1755
+ pos += needle.length;
1756
+ }
1757
+ return count;
1758
+ }
1759
+ function findMatchLines(haystack, needle) {
1760
+ const lines = [];
1761
+ let pos = 0;
1762
+ while ((pos = haystack.indexOf(needle, pos)) !== -1) {
1763
+ const lineNumber = haystack.slice(0, pos).split("\n").length;
1764
+ lines.push(lineNumber);
1765
+ pos += needle.length;
1766
+ }
1767
+ return lines;
1768
+ }
1769
+ function replaceAllOccurrences(haystack, needle, replacement) {
1770
+ return haystack.split(needle).join(replacement);
1771
+ }
1750
1772
  var FileEditTool;
1751
1773
  var init_file_edit = __esm({
1752
1774
  "packages/execution/dist/tools/file-edit.js"() {
1753
1775
  "use strict";
1754
1776
  FileEditTool = class {
1755
1777
  name = "file_edit";
1756
- description = "Make a precise edit to a file by replacing an exact string match. More efficient than rewriting the entire file.";
1778
+ description = "Make a precise edit to a file by replacing an exact string match. The old_string must be unique in the file unless replace_all is true. Use replace_all to rename variables or change repeated patterns throughout the file.";
1757
1779
  parameters = {
1758
1780
  type: "object",
1759
1781
  properties: {
1760
1782
  path: { type: "string", description: "Absolute or relative file path" },
1761
1783
  old_string: {
1762
1784
  type: "string",
1763
- description: "The exact string to search for and replace"
1785
+ description: "The exact string to search for and replace. Must be unique in the file (or use replace_all)."
1764
1786
  },
1765
1787
  new_string: {
1766
1788
  type: "string",
1767
1789
  description: "The replacement string"
1790
+ },
1791
+ replace_all: {
1792
+ type: "boolean",
1793
+ description: "Replace ALL occurrences instead of just the first. Use for variable renames, import path changes, etc. Default: false"
1768
1794
  }
1769
1795
  },
1770
1796
  required: ["path", "old_string", "new_string"]
@@ -1777,25 +1803,45 @@ var init_file_edit = __esm({
1777
1803
  const filePath = args["path"];
1778
1804
  const oldString = args["old_string"];
1779
1805
  const newString = args["new_string"];
1806
+ const replaceAll = args["replace_all"] === true;
1780
1807
  const start = performance.now();
1781
1808
  try {
1782
1809
  const fullPath = resolve5(this.workingDir, filePath);
1783
1810
  const content = await readFile2(fullPath, "utf-8");
1784
- const index = content.indexOf(oldString);
1785
- if (index === -1) {
1811
+ const occurrences = countOccurrences(content, oldString);
1812
+ if (occurrences === 0) {
1813
+ return {
1814
+ success: false,
1815
+ output: "",
1816
+ error: `old_string not found in ${filePath}. Read the file first to verify the exact content.`,
1817
+ durationMs: performance.now() - start
1818
+ };
1819
+ }
1820
+ if (!replaceAll && occurrences > 1) {
1821
+ const matchLines = findMatchLines(content, oldString);
1786
1822
  return {
1787
1823
  success: false,
1788
1824
  output: "",
1789
- error: `old_string not found in ${fullPath}`,
1825
+ error: `old_string is not unique \u2014 found ${occurrences} occurrences at lines ${matchLines.join(", ")}. Either include more surrounding context to make old_string unique, or set replace_all=true to replace all ${occurrences} occurrences.`,
1790
1826
  durationMs: performance.now() - start
1791
1827
  };
1792
1828
  }
1793
- const lineNumber = content.slice(0, index).split("\n").length;
1794
- const updated = content.slice(0, index) + newString + content.slice(index + oldString.length);
1829
+ let updated;
1830
+ let editedLines;
1831
+ if (replaceAll) {
1832
+ editedLines = findMatchLines(content, oldString);
1833
+ updated = replaceAllOccurrences(content, oldString, newString);
1834
+ } else {
1835
+ const index = content.indexOf(oldString);
1836
+ const lineNumber = content.slice(0, index).split("\n").length;
1837
+ editedLines = [lineNumber];
1838
+ updated = content.slice(0, index) + newString + content.slice(index + oldString.length);
1839
+ }
1795
1840
  await writeFile2(fullPath, updated, "utf-8");
1841
+ const linesInfo = editedLines.length === 1 ? `line ${editedLines[0]}` : `${editedLines.length} locations (lines ${editedLines.join(", ")})`;
1796
1842
  return {
1797
1843
  success: true,
1798
- output: `Edited ${fullPath} at line ${lineNumber}`,
1844
+ output: `Edited ${filePath} at ${linesInfo}`,
1799
1845
  durationMs: performance.now() - start
1800
1846
  };
1801
1847
  } catch (error) {
@@ -2437,15 +2483,24 @@ var init_aiwg_workflow = __esm({
2437
2483
  });
2438
2484
 
2439
2485
  // packages/execution/dist/tools/batch-edit.js
2440
- import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir3 } from "node:fs/promises";
2441
- import { resolve as resolve9, dirname as dirname2 } from "node:path";
2486
+ import { readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
2487
+ import { resolve as resolve9 } from "node:path";
2488
+ function countOccurrences2(haystack, needle) {
2489
+ let count = 0;
2490
+ let pos = 0;
2491
+ while ((pos = haystack.indexOf(needle, pos)) !== -1) {
2492
+ count++;
2493
+ pos += needle.length;
2494
+ }
2495
+ return count;
2496
+ }
2442
2497
  var BatchEditTool;
2443
2498
  var init_batch_edit = __esm({
2444
2499
  "packages/execution/dist/tools/batch-edit.js"() {
2445
2500
  "use strict";
2446
2501
  BatchEditTool = class {
2447
2502
  name = "batch_edit";
2448
- description = "Make multiple precise edits across one or more files in a single call. More efficient than calling file_edit repeatedly. Each edit replaces an exact string match. Edits are applied in order within each file.";
2503
+ description = "Make multiple precise edits across one or more files in a single call. More efficient than calling file_edit repeatedly. Each edit replaces an exact string match with uniqueness validation. Edits are applied in order within each file. Set replace_all on individual edits for bulk renames.";
2449
2504
  parameters = {
2450
2505
  type: "object",
2451
2506
  properties: {
@@ -2455,9 +2510,22 @@ var init_batch_edit = __esm({
2455
2510
  items: {
2456
2511
  type: "object",
2457
2512
  properties: {
2458
- path: { type: "string", description: "File path relative to project root" },
2459
- old_string: { type: "string", description: "Exact string to find and replace" },
2460
- new_string: { type: "string", description: "Replacement string" }
2513
+ path: {
2514
+ type: "string",
2515
+ description: "File path relative to project root"
2516
+ },
2517
+ old_string: {
2518
+ type: "string",
2519
+ description: "Exact string to find and replace (must be unique unless replace_all)"
2520
+ },
2521
+ new_string: {
2522
+ type: "string",
2523
+ description: "Replacement string"
2524
+ },
2525
+ replace_all: {
2526
+ type: "boolean",
2527
+ description: "Replace all occurrences (for renames). Default: false"
2528
+ }
2461
2529
  },
2462
2530
  required: ["path", "old_string", "new_string"]
2463
2531
  }
@@ -2485,7 +2553,12 @@ var init_batch_edit = __esm({
2485
2553
  const fullPath = resolve9(this.workingDir, edit.path);
2486
2554
  if (!byFile.has(fullPath))
2487
2555
  byFile.set(fullPath, []);
2488
- byFile.get(fullPath).push({ old_string: edit.old_string, new_string: edit.new_string });
2556
+ byFile.get(fullPath).push({
2557
+ old_string: edit.old_string,
2558
+ new_string: edit.new_string,
2559
+ replace_all: edit.replace_all,
2560
+ relPath: edit.path
2561
+ });
2489
2562
  }
2490
2563
  const results = [];
2491
2564
  let successCount = 0;
@@ -2494,15 +2567,26 @@ var init_batch_edit = __esm({
2494
2567
  try {
2495
2568
  let content = await readFile5(fullPath, "utf-8");
2496
2569
  for (const edit of fileEdits) {
2497
- const index = content.indexOf(edit.old_string);
2498
- if (index === -1) {
2499
- results.push(`SKIP: old_string not found in ${fullPath}`);
2570
+ const occurrences = countOccurrences2(content, edit.old_string);
2571
+ if (occurrences === 0) {
2572
+ results.push(`SKIP: old_string not found in ${edit.relPath}`);
2500
2573
  failCount++;
2501
2574
  continue;
2502
2575
  }
2503
- const lineNumber = content.slice(0, index).split("\n").length;
2504
- content = content.slice(0, index) + edit.new_string + content.slice(index + edit.old_string.length);
2505
- results.push(`EDIT: ${fullPath} line ${lineNumber}`);
2576
+ if (!edit.replace_all && occurrences > 1) {
2577
+ results.push(`AMBIGUOUS: old_string has ${occurrences} matches in ${edit.relPath} \u2014 add more context or use replace_all`);
2578
+ failCount++;
2579
+ continue;
2580
+ }
2581
+ if (edit.replace_all) {
2582
+ content = content.split(edit.old_string).join(edit.new_string);
2583
+ results.push(`EDIT: ${edit.relPath} (${occurrences} replacements)`);
2584
+ } else {
2585
+ const index = content.indexOf(edit.old_string);
2586
+ const lineNumber = content.slice(0, index).split("\n").length;
2587
+ content = content.slice(0, index) + edit.new_string + content.slice(index + edit.old_string.length);
2588
+ results.push(`EDIT: ${edit.relPath} line ${lineNumber}`);
2589
+ }
2506
2590
  successCount++;
2507
2591
  }
2508
2592
  await writeFile4(fullPath, content, "utf-8");
@@ -2524,6 +2608,178 @@ ${results.join("\n")}`,
2524
2608
  }
2525
2609
  });
2526
2610
 
2611
+ // packages/execution/dist/tools/file-patch.js
2612
+ import { readFile as readFile6, writeFile as writeFile5, copyFile } from "node:fs/promises";
2613
+ import { resolve as resolve10 } from "node:path";
2614
+ var FilePatchTool;
2615
+ var init_file_patch = __esm({
2616
+ "packages/execution/dist/tools/file-patch.js"() {
2617
+ "use strict";
2618
+ FilePatchTool = class {
2619
+ name = "file_patch";
2620
+ description = "Edit specific line ranges in a file. More precise than string matching for large files. Modes: 'replace' replaces lines start_line..end_line with new_content, 'insert_before' inserts before start_line, 'insert_after' inserts after start_line, 'delete' removes lines start_line..end_line. Use dry_run to preview changes.";
2621
+ parameters = {
2622
+ type: "object",
2623
+ properties: {
2624
+ path: {
2625
+ type: "string",
2626
+ description: "File path relative to project root"
2627
+ },
2628
+ start_line: {
2629
+ type: "number",
2630
+ description: "Starting line number (1-based, inclusive)"
2631
+ },
2632
+ end_line: {
2633
+ type: "number",
2634
+ description: "Ending line number (1-based, inclusive). Required for replace and delete modes. For single-line edits, set equal to start_line."
2635
+ },
2636
+ new_content: {
2637
+ type: "string",
2638
+ description: "Replacement content (for replace mode) or content to insert (for insert modes). Not needed for delete mode."
2639
+ },
2640
+ mode: {
2641
+ type: "string",
2642
+ enum: ["replace", "insert_before", "insert_after", "delete"],
2643
+ description: "Edit mode. Default: replace"
2644
+ },
2645
+ dry_run: {
2646
+ type: "boolean",
2647
+ description: "Preview the diff without writing. Returns what would change. Default: false"
2648
+ }
2649
+ },
2650
+ required: ["path", "start_line"]
2651
+ };
2652
+ workingDir;
2653
+ constructor(workingDir) {
2654
+ this.workingDir = workingDir;
2655
+ }
2656
+ async execute(args) {
2657
+ const filePath = args["path"];
2658
+ const startLine = args["start_line"];
2659
+ const endLine = args["end_line"] ?? startLine;
2660
+ const newContent = args["new_content"] ?? "";
2661
+ const mode = args["mode"] ?? "replace";
2662
+ const dryRun = args["dry_run"] === true;
2663
+ const start = performance.now();
2664
+ try {
2665
+ if (!Number.isInteger(startLine) || startLine < 1) {
2666
+ return {
2667
+ success: false,
2668
+ output: "",
2669
+ error: "start_line must be a positive integer (1-based)",
2670
+ durationMs: performance.now() - start
2671
+ };
2672
+ }
2673
+ if (!Number.isInteger(endLine) || endLine < startLine) {
2674
+ return {
2675
+ success: false,
2676
+ output: "",
2677
+ error: `end_line (${endLine}) must be >= start_line (${startLine})`,
2678
+ durationMs: performance.now() - start
2679
+ };
2680
+ }
2681
+ const fullPath = resolve10(this.workingDir, filePath);
2682
+ const content = await readFile6(fullPath, "utf-8");
2683
+ const lines = content.split("\n");
2684
+ const totalLines = lines.length;
2685
+ if (startLine > totalLines) {
2686
+ return {
2687
+ success: false,
2688
+ output: "",
2689
+ error: `start_line ${startLine} exceeds file length (${totalLines} lines)`,
2690
+ durationMs: performance.now() - start
2691
+ };
2692
+ }
2693
+ const effectiveEnd = Math.min(endLine, totalLines);
2694
+ const startIdx = startLine - 1;
2695
+ const endIdx = effectiveEnd;
2696
+ const newLines = newContent.length > 0 ? newContent.split("\n") : [];
2697
+ let resultLines;
2698
+ let description;
2699
+ switch (mode) {
2700
+ case "replace": {
2701
+ const removedLines = lines.slice(startIdx, endIdx);
2702
+ resultLines = [
2703
+ ...lines.slice(0, startIdx),
2704
+ ...newLines,
2705
+ ...lines.slice(endIdx)
2706
+ ];
2707
+ description = `Replaced lines ${startLine}-${effectiveEnd} (${removedLines.length} lines \u2192 ${newLines.length} lines)`;
2708
+ break;
2709
+ }
2710
+ case "insert_before": {
2711
+ resultLines = [
2712
+ ...lines.slice(0, startIdx),
2713
+ ...newLines,
2714
+ ...lines.slice(startIdx)
2715
+ ];
2716
+ description = `Inserted ${newLines.length} lines before line ${startLine}`;
2717
+ break;
2718
+ }
2719
+ case "insert_after": {
2720
+ resultLines = [
2721
+ ...lines.slice(0, startIdx + 1),
2722
+ ...newLines,
2723
+ ...lines.slice(startIdx + 1)
2724
+ ];
2725
+ description = `Inserted ${newLines.length} lines after line ${startLine}`;
2726
+ break;
2727
+ }
2728
+ case "delete": {
2729
+ const removedLines = lines.slice(startIdx, endIdx);
2730
+ resultLines = [...lines.slice(0, startIdx), ...lines.slice(endIdx)];
2731
+ description = `Deleted lines ${startLine}-${effectiveEnd} (${removedLines.length} lines removed)`;
2732
+ break;
2733
+ }
2734
+ default:
2735
+ return {
2736
+ success: false,
2737
+ output: "",
2738
+ error: `Unknown mode: ${mode}. Use replace, insert_before, insert_after, or delete.`,
2739
+ durationMs: performance.now() - start
2740
+ };
2741
+ }
2742
+ const oldSection = lines.slice(Math.max(0, startIdx - 2), Math.min(totalLines, endIdx + 2)).map((l, i) => `- ${String(Math.max(1, startLine - 2) + i).padStart(4)} | ${l}`).join("\n");
2743
+ const newSectionStart = Math.max(0, startIdx - 2);
2744
+ const newSectionEnd = Math.min(resultLines.length, startIdx + newLines.length + 2);
2745
+ const newSection = resultLines.slice(newSectionStart, newSectionEnd).map((l, i) => `+ ${String(newSectionStart + 1 + i).padStart(4)} | ${l}`).join("\n");
2746
+ const diff = `${description}
2747
+
2748
+ Before:
2749
+ ${oldSection}
2750
+
2751
+ After:
2752
+ ${newSection}`;
2753
+ if (dryRun) {
2754
+ return {
2755
+ success: true,
2756
+ output: `[DRY RUN] ${diff}
2757
+
2758
+ File NOT modified. Remove dry_run to apply.`,
2759
+ durationMs: performance.now() - start
2760
+ };
2761
+ }
2762
+ await writeFile5(fullPath, resultLines.join("\n"), "utf-8");
2763
+ return {
2764
+ success: true,
2765
+ output: `${filePath}: ${description} (${totalLines} \u2192 ${resultLines.length} total lines)
2766
+
2767
+ ${diff}`,
2768
+ durationMs: performance.now() - start
2769
+ };
2770
+ } catch (error) {
2771
+ return {
2772
+ success: false,
2773
+ output: "",
2774
+ error: error instanceof Error ? error.message : String(error),
2775
+ durationMs: performance.now() - start
2776
+ };
2777
+ }
2778
+ }
2779
+ };
2780
+ }
2781
+ });
2782
+
2527
2783
  // packages/execution/dist/tools/codebase-map.js
2528
2784
  import { readdirSync as readdirSync3, statSync as statSync3, readFileSync as readFileSync4, existsSync as existsSync4 } from "node:fs";
2529
2785
  import { join as join7, relative, extname } from "node:path";
@@ -3378,7 +3634,7 @@ Exit code: ${task.exitCode ?? "N/A"}`,
3378
3634
 
3379
3635
  // packages/execution/dist/tools/image.js
3380
3636
  import { existsSync as existsSync7, readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
3381
- import { resolve as resolve10, extname as extname2, basename } from "node:path";
3637
+ import { resolve as resolve11, extname as extname2, basename } from "node:path";
3382
3638
  import { execSync as execSync7 } from "node:child_process";
3383
3639
  import { tmpdir } from "node:os";
3384
3640
  import { join as join10 } from "node:path";
@@ -3487,7 +3743,7 @@ var init_image = __esm({
3487
3743
  if (!rawPath) {
3488
3744
  return { success: false, output: "", error: "path is required", durationMs: 0 };
3489
3745
  }
3490
- const fullPath = resolve10(this.workingDir, rawPath);
3746
+ const fullPath = resolve11(this.workingDir, rawPath);
3491
3747
  if (!existsSync7(fullPath)) {
3492
3748
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
3493
3749
  }
@@ -3556,7 +3812,7 @@ ${ocrText}`);
3556
3812
  }
3557
3813
  async execute(args) {
3558
3814
  const start = Date.now();
3559
- const outputPath = args["output_path"] ? resolve10(this.workingDir, String(args["output_path"])) : join10(tmpdir(), `oa-screenshot-${Date.now()}.png`);
3815
+ const outputPath = args["output_path"] ? resolve11(this.workingDir, String(args["output_path"])) : join10(tmpdir(), `oa-screenshot-${Date.now()}.png`);
3560
3816
  const delayMs = typeof args["delay_ms"] === "number" ? args["delay_ms"] : 0;
3561
3817
  const region = String(args["region"] ?? "full");
3562
3818
  if (delayMs > 0) {
@@ -3679,7 +3935,7 @@ ${ocrText}`);
3679
3935
  if (!rawPath) {
3680
3936
  return { success: false, output: "", error: "path is required", durationMs: 0 };
3681
3937
  }
3682
- const fullPath = resolve10(this.workingDir, rawPath);
3938
+ const fullPath = resolve11(this.workingDir, rawPath);
3683
3939
  if (!existsSync7(fullPath)) {
3684
3940
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
3685
3941
  }
@@ -3892,7 +4148,7 @@ var init_custom_tool = __esm({
3892
4148
  }
3893
4149
  /** Execute a single shell command and return output */
3894
4150
  runCommand(command) {
3895
- return new Promise((resolve14) => {
4151
+ return new Promise((resolve15) => {
3896
4152
  const child = spawn3("bash", ["-c", command], {
3897
4153
  cwd: this.workingDir,
3898
4154
  env: { ...process.env, CI: "true", NO_COLOR: "1" },
@@ -3917,11 +4173,11 @@ var init_custom_tool = __esm({
3917
4173
  child.kill("SIGTERM");
3918
4174
  } catch {
3919
4175
  }
3920
- resolve14({ success: false, output: stdout, error: "Command timed out after 60s" });
4176
+ resolve15({ success: false, output: stdout, error: "Command timed out after 60s" });
3921
4177
  }, 6e4);
3922
4178
  child.on("close", (code) => {
3923
4179
  clearTimeout(timer);
3924
- resolve14({
4180
+ resolve15({
3925
4181
  success: code === 0,
3926
4182
  output: stdout + (stderr && code === 0 ? `
3927
4183
  STDERR:
@@ -3931,7 +4187,7 @@ ${stderr}` : ""),
3931
4187
  });
3932
4188
  child.on("error", (err) => {
3933
4189
  clearTimeout(timer);
3934
- resolve14({ success: false, output: stdout, error: err.message });
4190
+ resolve15({ success: false, output: stdout, error: err.message });
3935
4191
  });
3936
4192
  });
3937
4193
  }
@@ -4327,6 +4583,7 @@ var init_dist2 = __esm({
4327
4583
  init_aiwg_health();
4328
4584
  init_aiwg_workflow();
4329
4585
  init_batch_edit();
4586
+ init_file_patch();
4330
4587
  init_codebase_map();
4331
4588
  init_diagnostic();
4332
4589
  init_git_info();
@@ -5448,8 +5705,8 @@ var init_code_retriever = __esm({
5448
5705
  });
5449
5706
  }
5450
5707
  async getFileContent(filePath, startLine, endLine) {
5451
- const { readFile: readFile9 } = await import("node:fs/promises");
5452
- const content = await readFile9(filePath, "utf-8");
5708
+ const { readFile: readFile10 } = await import("node:fs/promises");
5709
+ const content = await readFile10(filePath, "utf-8");
5453
5710
  if (startLine === void 0)
5454
5711
  return content;
5455
5712
  const lines = content.split("\n");
@@ -5464,7 +5721,7 @@ var init_code_retriever = __esm({
5464
5721
  // packages/retrieval/dist/lexicalSearch.js
5465
5722
  import { execFile as execFile4 } from "node:child_process";
5466
5723
  import { promisify as promisify4 } from "node:util";
5467
- import { readFile as readFile6, readdir, stat } from "node:fs/promises";
5724
+ import { readFile as readFile7, readdir, stat } from "node:fs/promises";
5468
5725
  import { join as join12, extname as extname3 } from "node:path";
5469
5726
  async function searchByPath(pathPattern, options) {
5470
5727
  const allFiles = await collectFiles(options.rootDir, options.includeGlobs ?? DEFAULT_INCLUDE_GLOBS, options.excludeGlobs ?? DEFAULT_EXCLUDE_GLOBS);
@@ -5570,7 +5827,7 @@ async function searchWithNodeFallback(pattern, kind, options) {
5570
5827
  if (results.length >= maxMatches)
5571
5828
  break;
5572
5829
  try {
5573
- const content = await readFile6(filePath, "utf-8");
5830
+ const content = await readFile7(filePath, "utf-8");
5574
5831
  const contentLines = content.split("\n");
5575
5832
  for (let i = 0; i < contentLines.length; i++) {
5576
5833
  if (results.length >= maxMatches)
@@ -5781,7 +6038,7 @@ var init_graphExpand = __esm({
5781
6038
  });
5782
6039
 
5783
6040
  // packages/retrieval/dist/snippetPacker.js
5784
- import { readFile as readFile7 } from "node:fs/promises";
6041
+ import { readFile as readFile8 } from "node:fs/promises";
5785
6042
  import { join as join13 } from "node:path";
5786
6043
  async function packSnippets(requests, opts = {}) {
5787
6044
  const maxTokens = opts.maxTokens ?? DEFAULT_MAX_TOKENS;
@@ -5811,7 +6068,7 @@ async function extractSnippet(req, repoRoot, contextLines = DEFAULT_CONTEXT_LINE
5811
6068
  const absPath = req.filePath.startsWith("/") ? req.filePath : join13(repoRoot, req.filePath);
5812
6069
  let content;
5813
6070
  try {
5814
- content = await readFile7(absPath, "utf-8");
6071
+ content = await readFile8(absPath, "utf-8");
5815
6072
  } catch {
5816
6073
  return null;
5817
6074
  }
@@ -6970,7 +7227,8 @@ var init_agenticRunner = __esm({
6970
7227
 
6971
7228
  - file_read: Read file contents (always read before editing). Supports path, offset, limit.
6972
7229
  - file_write: Create or overwrite a file with complete content
6973
- - file_edit: Make a precise string replacement in a file (preferred over rewriting). Uses old_string/new_string.
7230
+ - file_edit: Make a precise string replacement in a file (preferred over rewriting). Uses old_string/new_string. old_string must be unique unless replace_all=true. Use replace_all for variable renames.
7231
+ - file_patch: Edit specific line ranges in large files. Modes: replace (swap lines), insert_before, insert_after, delete. Use dry_run to preview. Best for large files (500+ lines) where string matching is fragile.
6974
7232
  - find_files: Find files by name pattern (glob). Searches recursively, excludes node_modules/.git.
6975
7233
  - grep_search: Search file contents with regex. Returns matching lines with paths and line numbers.
6976
7234
  - shell: Execute any shell command (tests, builds, git, npm, etc.). Supports stdin parameter for input. Commands run with CI=true for non-interactive mode.
@@ -7104,7 +7362,11 @@ If you notice you're performing the SAME multi-step sequence for the 3rd time or
7104
7362
 
7105
7363
  - Use grep_search to find specific code instead of reading many files
7106
7364
  - Use file_edit for targeted changes instead of full file rewrites
7107
- - When files are long, use file_read with offset/limit to read specific sections
7365
+ - Use file_edit with replace_all=true for variable/function renames across a file
7366
+ - If file_edit fails with "not unique", include more surrounding context in old_string
7367
+ - For large files (500+ lines): use file_read with offset/limit, then file_patch with line numbers
7368
+ - file_patch with dry_run=true lets you preview changes before applying them
7369
+ - batch_edit to apply multiple edits across files in one call (reduces turns)
7108
7370
  - Focus on error messages in shell output \u2014 skip verbose build logs
7109
7371
  - Don't read files you don't need to modify`;
7110
7372
  AgenticRunner = class {
@@ -7472,6 +7734,8 @@ ${marker}` : marker);
7472
7734
  // Context compaction
7473
7735
  // -------------------------------------------------------------------------
7474
7736
  compactMessages(messages) {
7737
+ if (messages.length < 3)
7738
+ return messages;
7475
7739
  const totalChars = messages.reduce((sum, m) => {
7476
7740
  if (typeof m.content === "string")
7477
7741
  return sum + m.content.length;
@@ -7486,8 +7750,22 @@ ${marker}` : marker);
7486
7750
  }
7487
7751
  const keepRecent = 12;
7488
7752
  const head = messages.slice(0, 2);
7489
- const recent = messages.slice(-keepRecent);
7490
- const middle = messages.slice(2, -keepRecent);
7753
+ if (messages.length <= 2 + keepRecent)
7754
+ return messages;
7755
+ let recentStart = messages.length - keepRecent;
7756
+ while (recentStart > 2) {
7757
+ const msg = messages[recentStart];
7758
+ if (msg && msg.role === "tool") {
7759
+ recentStart--;
7760
+ } else {
7761
+ break;
7762
+ }
7763
+ }
7764
+ if (recentStart > 2 && messages[recentStart]?.role === "tool") {
7765
+ recentStart = Math.max(2, recentStart - 1);
7766
+ }
7767
+ const recent = messages.slice(recentStart);
7768
+ const middle = messages.slice(2, recentStart);
7491
7769
  if (middle.length === 0)
7492
7770
  return messages;
7493
7771
  let previousSummary = "";
@@ -7514,7 +7792,10 @@ ${combinedSummary}
7514
7792
 
7515
7793
  [Continue from the recent context below. Do not repeat work already completed above.]`
7516
7794
  };
7517
- return [...head, compactionMsg, ...recent];
7795
+ const result = [...head, compactionMsg, ...recent];
7796
+ if (result.length < 2)
7797
+ return messages;
7798
+ return result;
7518
7799
  }
7519
7800
  /**
7520
7801
  * Progressive summarization: merge an older compacted summary with a newer one.
@@ -8490,6 +8771,7 @@ var init_render = __esm({
8490
8771
  "file_read",
8491
8772
  "file_write",
8492
8773
  "file_edit",
8774
+ "file_patch",
8493
8775
  "shell",
8494
8776
  "grep_search",
8495
8777
  "find_files",
@@ -8769,14 +9051,14 @@ async function handleUpdate() {
8769
9051
  try {
8770
9052
  const { createRequire: createRequire4 } = await import("node:module");
8771
9053
  const { fileURLToPath: fileURLToPath3 } = await import("node:url");
8772
- const { dirname: dirname5, join: join23 } = await import("node:path");
9054
+ const { dirname: dirname4, join: join24 } = await import("node:path");
8773
9055
  const { existsSync: existsSync15 } = await import("node:fs");
8774
9056
  const req = createRequire4(import.meta.url);
8775
- const thisDir = dirname5(fileURLToPath3(import.meta.url));
9057
+ const thisDir = dirname4(fileURLToPath3(import.meta.url));
8776
9058
  const candidates = [
8777
- join23(thisDir, "..", "package.json"),
8778
- join23(thisDir, "..", "..", "package.json"),
8779
- join23(thisDir, "..", "..", "..", "package.json")
9059
+ join24(thisDir, "..", "package.json"),
9060
+ join24(thisDir, "..", "..", "package.json"),
9061
+ join24(thisDir, "..", "..", "..", "package.json")
8780
9062
  ];
8781
9063
  for (const pkgPath of candidates) {
8782
9064
  if (existsSync15(pkgPath)) {
@@ -8941,8 +9223,8 @@ function modelSupportsToolCalling(modelName) {
8941
9223
  return false;
8942
9224
  }
8943
9225
  function ask(rl, question) {
8944
- return new Promise((resolve14) => {
8945
- rl.question(question, (answer) => resolve14(answer.trim()));
9226
+ return new Promise((resolve15) => {
9227
+ rl.question(question, (answer) => resolve15(answer.trim()));
8946
9228
  });
8947
9229
  }
8948
9230
  function pullModelWithAutoUpdate(tag) {
@@ -10688,6 +10970,8 @@ function describeToolCall(toolName, args) {
10688
10970
  return `Writing ${file}`;
10689
10971
  case "file_edit":
10690
10972
  return `Editing ${file}`;
10973
+ case "file_patch":
10974
+ return `Patching ${file}`;
10691
10975
  case "shell": {
10692
10976
  const cmd = String(args["command"] ?? "");
10693
10977
  if (/npm\s+test|vitest|jest|mocha/.test(cmd))
@@ -11038,7 +11322,7 @@ var init_voice = __esm({
11038
11322
  const cmd = this.getPlayCommand(path);
11039
11323
  if (!cmd)
11040
11324
  return;
11041
- return new Promise((resolve14) => {
11325
+ return new Promise((resolve15) => {
11042
11326
  const child = nodeSpawn(cmd[0], cmd.slice(1), {
11043
11327
  stdio: "ignore",
11044
11328
  detached: false
@@ -11047,12 +11331,12 @@ var init_voice = __esm({
11047
11331
  child.on("close", () => {
11048
11332
  if (this.currentPlayback === child)
11049
11333
  this.currentPlayback = null;
11050
- resolve14();
11334
+ resolve15();
11051
11335
  });
11052
11336
  child.on("error", () => {
11053
11337
  if (this.currentPlayback === child)
11054
11338
  this.currentPlayback = null;
11055
- resolve14();
11339
+ resolve15();
11056
11340
  });
11057
11341
  setTimeout(() => {
11058
11342
  if (this.currentPlayback === child) {
@@ -11062,7 +11346,7 @@ var init_voice = __esm({
11062
11346
  }
11063
11347
  this.currentPlayback = null;
11064
11348
  }
11065
- resolve14();
11349
+ resolve15();
11066
11350
  }, 15e3);
11067
11351
  });
11068
11352
  }
@@ -11492,10 +11776,125 @@ var init_stream_renderer = __esm({
11492
11776
  }
11493
11777
  });
11494
11778
 
11779
+ // packages/cli/dist/tui/edit-history.js
11780
+ import { appendFileSync, mkdirSync as mkdirSync7 } from "node:fs";
11781
+ import { join as join18 } from "node:path";
11782
+ function createEditHistoryLogger(repoRoot, sessionId) {
11783
+ const historyDir = join18(repoRoot, ".oa", "history");
11784
+ const logPath = join18(historyDir, "edits.jsonl");
11785
+ try {
11786
+ mkdirSync7(historyDir, { recursive: true });
11787
+ } catch {
11788
+ }
11789
+ function logToolCall(toolName, toolArgs, success) {
11790
+ if (!EDIT_TOOLS.has(toolName))
11791
+ return;
11792
+ const entry = {
11793
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
11794
+ tool: toolName,
11795
+ files: extractFilePaths(toolName, toolArgs),
11796
+ summary: buildSummary(toolName, toolArgs),
11797
+ success,
11798
+ session: sessionId,
11799
+ args: sanitizeArgs(toolName, toolArgs)
11800
+ };
11801
+ try {
11802
+ appendFileSync(logPath, JSON.stringify(entry) + "\n", "utf-8");
11803
+ } catch {
11804
+ }
11805
+ }
11806
+ return { logToolCall, logPath };
11807
+ }
11808
+ function extractFilePaths(tool, args) {
11809
+ if (tool === "batch_edit") {
11810
+ const edits = args["edits"];
11811
+ if (Array.isArray(edits)) {
11812
+ const paths = new Set(edits.map((e) => e.path).filter(Boolean));
11813
+ return [...paths];
11814
+ }
11815
+ return [];
11816
+ }
11817
+ const path = args["path"];
11818
+ return path ? [path] : [];
11819
+ }
11820
+ function buildSummary(tool, args) {
11821
+ switch (tool) {
11822
+ case "file_edit": {
11823
+ const old = args["old_string"] ?? "";
11824
+ const replaceAll = args["replace_all"] === true;
11825
+ const preview = old.length > 60 ? old.slice(0, 60) + "..." : old;
11826
+ return replaceAll ? `Replace all "${preview}"` : `Edit: "${preview}" \u2192 new`;
11827
+ }
11828
+ case "file_write": {
11829
+ const content = args["content"] ?? "";
11830
+ return `Write ${content.length} bytes`;
11831
+ }
11832
+ case "file_patch": {
11833
+ const mode = args["mode"] ?? "replace";
11834
+ const start = args["start_line"];
11835
+ const end = args["end_line"];
11836
+ const dryRun = args["dry_run"] === true;
11837
+ return `${dryRun ? "[dry-run] " : ""}${mode} lines ${start}${end ? `-${end}` : ""}`;
11838
+ }
11839
+ case "batch_edit": {
11840
+ const edits = args["edits"];
11841
+ return `Batch: ${Array.isArray(edits) ? edits.length : 0} edits`;
11842
+ }
11843
+ default:
11844
+ return tool;
11845
+ }
11846
+ }
11847
+ function sanitizeArgs(tool, args) {
11848
+ const sanitized = { ...args };
11849
+ if (tool === "file_write" && typeof sanitized["content"] === "string") {
11850
+ const content = sanitized["content"];
11851
+ sanitized["content"] = `[${content.length} bytes]`;
11852
+ }
11853
+ if (typeof sanitized["old_string"] === "string") {
11854
+ const s = sanitized["old_string"];
11855
+ sanitized["old_string"] = s.length > 120 ? s.slice(0, 120) + "..." : s;
11856
+ }
11857
+ if (typeof sanitized["new_string"] === "string") {
11858
+ const s = sanitized["new_string"];
11859
+ sanitized["new_string"] = s.length > 120 ? s.slice(0, 120) + "..." : s;
11860
+ }
11861
+ if (typeof sanitized["new_content"] === "string") {
11862
+ const s = sanitized["new_content"];
11863
+ sanitized["new_content"] = s.length > 120 ? s.slice(0, 120) + "..." : s;
11864
+ }
11865
+ if (tool === "batch_edit" && Array.isArray(sanitized["edits"])) {
11866
+ sanitized["edits"] = sanitized["edits"].map((e) => {
11867
+ const se = { ...e };
11868
+ if (typeof se["old_string"] === "string") {
11869
+ const s = se["old_string"];
11870
+ se["old_string"] = s.length > 80 ? s.slice(0, 80) + "..." : s;
11871
+ }
11872
+ if (typeof se["new_string"] === "string") {
11873
+ const s = se["new_string"];
11874
+ se["new_string"] = s.length > 80 ? s.slice(0, 80) + "..." : s;
11875
+ }
11876
+ return se;
11877
+ });
11878
+ }
11879
+ return sanitized;
11880
+ }
11881
+ var EDIT_TOOLS;
11882
+ var init_edit_history = __esm({
11883
+ "packages/cli/dist/tui/edit-history.js"() {
11884
+ "use strict";
11885
+ EDIT_TOOLS = /* @__PURE__ */ new Set([
11886
+ "file_edit",
11887
+ "file_write",
11888
+ "file_patch",
11889
+ "batch_edit"
11890
+ ]);
11891
+ }
11892
+ });
11893
+
11495
11894
  // packages/cli/dist/tui/interactive.js
11496
11895
  import * as readline2 from "node:readline";
11497
11896
  import { cwd } from "node:process";
11498
- import { resolve as resolve11, join as join18, dirname as dirname3 } from "node:path";
11897
+ import { resolve as resolve12, join as join19, dirname as dirname2 } from "node:path";
11499
11898
  import { createRequire as createRequire2 } from "node:module";
11500
11899
  import { fileURLToPath } from "node:url";
11501
11900
  import { readFileSync as readFileSync11 } from "node:fs";
@@ -11504,11 +11903,11 @@ import { extname as extname5 } from "node:path";
11504
11903
  function getVersion() {
11505
11904
  try {
11506
11905
  const require2 = createRequire2(import.meta.url);
11507
- const thisDir = dirname3(fileURLToPath(import.meta.url));
11906
+ const thisDir = dirname2(fileURLToPath(import.meta.url));
11508
11907
  const candidates = [
11509
- join18(thisDir, "..", "package.json"),
11510
- join18(thisDir, "..", "..", "package.json"),
11511
- join18(thisDir, "..", "..", "..", "package.json")
11908
+ join19(thisDir, "..", "package.json"),
11909
+ join19(thisDir, "..", "..", "package.json"),
11910
+ join19(thisDir, "..", "..", "..", "package.json")
11512
11911
  ];
11513
11912
  for (const pkgPath of candidates) {
11514
11913
  if (existsSync13(pkgPath)) {
@@ -11568,6 +11967,7 @@ function buildTools(repoRoot, config) {
11568
11967
  new AiwgWorkflowTool(repoRoot),
11569
11968
  // Advanced tools (mirroring Claude Code capabilities)
11570
11969
  new BatchEditTool(repoRoot),
11970
+ new FilePatchTool(repoRoot),
11571
11971
  new CodebaseMapTool(repoRoot),
11572
11972
  new DiagnosticTool(repoRoot),
11573
11973
  new GitInfoTool(repoRoot),
@@ -11682,6 +12082,9 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
11682
12082
  runner.registerTools(buildTools(repoRoot, config));
11683
12083
  const filesTouched = /* @__PURE__ */ new Set();
11684
12084
  const toolSequence = [];
12085
+ const editSessionId = `task-${Date.now()}`;
12086
+ const editHistory = createEditHistoryLogger(repoRoot, editSessionId);
12087
+ let lastToolCall = null;
11685
12088
  runner.onEvent((event) => {
11686
12089
  switch (event.type) {
11687
12090
  case "tool_call":
@@ -11691,10 +12094,11 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
11691
12094
  });
11692
12095
  if (event.toolArgs?.path && typeof event.toolArgs.path === "string") {
11693
12096
  const name = event.toolName ?? "";
11694
- if (name === "file_write" || name === "file_edit" || name === "batch_edit") {
12097
+ if (name === "file_write" || name === "file_edit" || name === "file_patch" || name === "batch_edit") {
11695
12098
  filesTouched.add(event.toolArgs.path);
11696
12099
  }
11697
12100
  }
12101
+ lastToolCall = { name: event.toolName ?? "unknown", args: event.toolArgs ?? {} };
11698
12102
  if (voice?.enabled) {
11699
12103
  const desc = describeToolCall(event.toolName ?? "unknown", event.toolArgs ?? {});
11700
12104
  renderVoiceText(desc);
@@ -11703,6 +12107,10 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
11703
12107
  renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {});
11704
12108
  break;
11705
12109
  case "tool_result":
12110
+ if (lastToolCall) {
12111
+ editHistory.logToolCall(lastToolCall.name, lastToolCall.args, event.success ?? false);
12112
+ lastToolCall = null;
12113
+ }
11706
12114
  renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? "");
11707
12115
  if (voice?.enabled && !(event.success ?? true)) {
11708
12116
  const desc = describeToolResult(event.toolName ?? "unknown", false);
@@ -11801,7 +12209,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
11801
12209
  return { runner, promise };
11802
12210
  }
11803
12211
  async function startInteractive(config, repoPath) {
11804
- const repoRoot = resolve11(repoPath ?? cwd());
12212
+ const repoRoot = resolve12(repoPath ?? cwd());
11805
12213
  const isResumed = !!process.env.__OA_RESUMED;
11806
12214
  if (isResumed) {
11807
12215
  delete process.env.__OA_RESUMED;
@@ -12015,11 +12423,11 @@ ${c2.dim("Goodbye!")}
12015
12423
  }
12016
12424
  }
12017
12425
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
12018
- const isImage = isImagePath(cleanPath) && existsSync13(resolve11(repoRoot, cleanPath));
12426
+ const isImage = isImagePath(cleanPath) && existsSync13(resolve12(repoRoot, cleanPath));
12019
12427
  if (activeTask) {
12020
12428
  if (isImage) {
12021
12429
  try {
12022
- const imgPath = resolve11(repoRoot, cleanPath);
12430
+ const imgPath = resolve12(repoRoot, cleanPath);
12023
12431
  const imgBuffer = readFileSync11(imgPath);
12024
12432
  const base64 = imgBuffer.toString("base64");
12025
12433
  const ext = extname5(cleanPath).toLowerCase();
@@ -12130,7 +12538,7 @@ ${c2.dim("(Use /quit to exit)")}
12130
12538
  });
12131
12539
  }
12132
12540
  async function runWithTUI(task, config, repoPath) {
12133
- const repoRoot = resolve11(repoPath ?? cwd());
12541
+ const repoRoot = resolve12(repoPath ?? cwd());
12134
12542
  const needsSetup = isFirstRun() || !await isModelAvailable(config);
12135
12543
  if (needsSetup && config.backendType === "ollama") {
12136
12544
  const setupModel = await runSetupWizard(config);
@@ -12177,6 +12585,7 @@ var init_interactive = __esm({
12177
12585
  init_carousel();
12178
12586
  init_voice();
12179
12587
  init_stream_renderer();
12588
+ init_edit_history();
12180
12589
  taskManager = new BackgroundTaskManager();
12181
12590
  }
12182
12591
  });
@@ -12210,9 +12619,9 @@ var init_run = __esm({
12210
12619
  // packages/indexer/dist/codebase-indexer.js
12211
12620
  import { glob } from "glob";
12212
12621
  import ignore from "ignore";
12213
- import { readFile as readFile8, stat as stat2 } from "node:fs/promises";
12622
+ import { readFile as readFile9, stat as stat2 } from "node:fs/promises";
12214
12623
  import { createHash } from "node:crypto";
12215
- import { join as join19, relative as relative3, extname as extname6, basename as basename4 } from "node:path";
12624
+ import { join as join20, relative as relative3, extname as extname6, basename as basename4 } from "node:path";
12216
12625
  var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
12217
12626
  var init_codebase_indexer = __esm({
12218
12627
  "packages/indexer/dist/codebase-indexer.js"() {
@@ -12256,7 +12665,7 @@ var init_codebase_indexer = __esm({
12256
12665
  const ig = ignore.default();
12257
12666
  if (this.config.respectGitignore) {
12258
12667
  try {
12259
- const gitignoreContent = await readFile8(join19(this.config.rootDir, ".gitignore"), "utf-8");
12668
+ const gitignoreContent = await readFile9(join20(this.config.rootDir, ".gitignore"), "utf-8");
12260
12669
  ig.add(gitignoreContent);
12261
12670
  } catch {
12262
12671
  }
@@ -12271,12 +12680,12 @@ var init_codebase_indexer = __esm({
12271
12680
  for (const relativePath of files) {
12272
12681
  if (ig.ignores(relativePath))
12273
12682
  continue;
12274
- const fullPath = join19(this.config.rootDir, relativePath);
12683
+ const fullPath = join20(this.config.rootDir, relativePath);
12275
12684
  try {
12276
12685
  const fileStat = await stat2(fullPath);
12277
12686
  if (fileStat.size > this.config.maxFileSize)
12278
12687
  continue;
12279
- const content = await readFile8(fullPath);
12688
+ const content = await readFile9(fullPath);
12280
12689
  const hash = createHash("sha256").update(content).digest("hex");
12281
12690
  const ext = extname6(relativePath);
12282
12691
  indexed.push({
@@ -12317,7 +12726,7 @@ var init_codebase_indexer = __esm({
12317
12726
  if (!child) {
12318
12727
  child = {
12319
12728
  name: part,
12320
- path: join19(current.path, part),
12729
+ path: join20(current.path, part),
12321
12730
  type: "directory",
12322
12731
  children: []
12323
12732
  };
@@ -12391,11 +12800,11 @@ var index_repo_exports = {};
12391
12800
  __export(index_repo_exports, {
12392
12801
  indexRepoCommand: () => indexRepoCommand
12393
12802
  });
12394
- import { resolve as resolve12 } from "node:path";
12803
+ import { resolve as resolve13 } from "node:path";
12395
12804
  import { existsSync as existsSync14, statSync as statSync6 } from "node:fs";
12396
12805
  import { cwd as cwd2 } from "node:process";
12397
12806
  async function indexRepoCommand(opts, _config) {
12398
- const repoRoot = resolve12(opts.repoPath ?? cwd2());
12807
+ const repoRoot = resolve13(opts.repoPath ?? cwd2());
12399
12808
  printHeader("Index Repository");
12400
12809
  printInfo(`Indexing: ${repoRoot}`);
12401
12810
  if (!existsSync14(repoRoot)) {
@@ -12644,7 +13053,7 @@ var config_exports = {};
12644
13053
  __export(config_exports, {
12645
13054
  configCommand: () => configCommand
12646
13055
  });
12647
- import { join as join20, resolve as resolve13 } from "node:path";
13056
+ import { join as join21, resolve as resolve14 } from "node:path";
12648
13057
  import { homedir as homedir8 } from "node:os";
12649
13058
  import { cwd as cwd3 } from "node:process";
12650
13059
  function coerceForSettings(key, value) {
@@ -12665,7 +13074,7 @@ async function configCommand(opts, config) {
12665
13074
  return handleShow(opts, config);
12666
13075
  }
12667
13076
  function handleShow(opts, config) {
12668
- const repoRoot = resolve13(opts.repoPath ?? cwd3());
13077
+ const repoRoot = resolve14(opts.repoPath ?? cwd3());
12669
13078
  printHeader("Configuration");
12670
13079
  printSection("Active Settings (merged)");
12671
13080
  printKeyValue("backendUrl", config.backendUrl, 2);
@@ -12697,7 +13106,7 @@ function handleShow(opts, config) {
12697
13106
  }
12698
13107
  }
12699
13108
  printSection("Config File");
12700
- printInfo(`~/.open-agents/config.json (${join20(homedir8(), ".open-agents", "config.json")})`);
13109
+ printInfo(`~/.open-agents/config.json (${join21(homedir8(), ".open-agents", "config.json")})`);
12701
13110
  printSection("Priority Chain");
12702
13111
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
12703
13112
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -12730,13 +13139,13 @@ function handleSet(opts, _config) {
12730
13139
  process.exit(1);
12731
13140
  }
12732
13141
  if (opts.local) {
12733
- const repoRoot = resolve13(opts.repoPath ?? cwd3());
13142
+ const repoRoot = resolve14(opts.repoPath ?? cwd3());
12734
13143
  try {
12735
13144
  initOaDirectory(repoRoot);
12736
13145
  const coerced = coerceForSettings(key, value);
12737
13146
  saveProjectSettings(repoRoot, { [key]: coerced });
12738
13147
  printSuccess(`Project override set: ${key} = ${value}`);
12739
- printInfo(`Saved to ${join20(repoRoot, ".oa", "settings.json")}`);
13148
+ printInfo(`Saved to ${join21(repoRoot, ".oa", "settings.json")}`);
12740
13149
  printInfo("This override applies only when running in this workspace.");
12741
13150
  } catch (err) {
12742
13151
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -12888,7 +13297,7 @@ async function serveVllm(opts, config) {
12888
13297
  await runVllmServer(args, opts.verbose ?? false);
12889
13298
  }
12890
13299
  async function runVllmServer(args, verbose) {
12891
- return new Promise((resolve14, reject) => {
13300
+ return new Promise((resolve15, reject) => {
12892
13301
  const child = spawn4("python", args, {
12893
13302
  stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
12894
13303
  env: { ...process.env }
@@ -12923,10 +13332,10 @@ async function runVllmServer(args, verbose) {
12923
13332
  child.once("exit", (code, signal) => {
12924
13333
  if (signal) {
12925
13334
  printInfo(`vLLM server stopped by signal ${signal}`);
12926
- resolve14();
13335
+ resolve15();
12927
13336
  } else if (code === 0) {
12928
13337
  printSuccess("vLLM server exited cleanly");
12929
- resolve14();
13338
+ resolve15();
12930
13339
  } else {
12931
13340
  printError(`vLLM server exited with code ${code}`);
12932
13341
  reject(new Error(`vLLM exited with code ${code}`));
@@ -12954,8 +13363,8 @@ __export(eval_exports, {
12954
13363
  evalCommand: () => evalCommand
12955
13364
  });
12956
13365
  import { tmpdir as tmpdir3 } from "node:os";
12957
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
12958
- import { join as join21 } from "node:path";
13366
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "node:fs";
13367
+ import { join as join22 } from "node:path";
12959
13368
  async function evalCommand(opts, config) {
12960
13369
  const suiteName = opts.suite ?? "basic";
12961
13370
  const suite = SUITES[suiteName];
@@ -13076,9 +13485,9 @@ async function evalCommand(opts, config) {
13076
13485
  process.exit(failed > 0 ? 1 : 0);
13077
13486
  }
13078
13487
  function createTempEvalRepo() {
13079
- const dir = join21(tmpdir3(), `open-agents-eval-${Date.now()}`);
13080
- mkdirSync7(dir, { recursive: true });
13081
- writeFileSync7(join21(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
13488
+ const dir = join22(tmpdir3(), `open-agents-eval-${Date.now()}`);
13489
+ mkdirSync8(dir, { recursive: true });
13490
+ writeFileSync7(join22(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
13082
13491
  return dir;
13083
13492
  }
13084
13493
  var BASIC_SUITE, FULL_SUITE, SUITES;
@@ -13138,7 +13547,7 @@ init_updater();
13138
13547
  import { parseArgs as nodeParseArgs2 } from "node:util";
13139
13548
  import { createRequire as createRequire3 } from "node:module";
13140
13549
  import { fileURLToPath as fileURLToPath2 } from "node:url";
13141
- import { dirname as dirname4, join as join22 } from "node:path";
13550
+ import { dirname as dirname3, join as join23 } from "node:path";
13142
13551
 
13143
13552
  // packages/cli/dist/cli.js
13144
13553
  import { createInterface } from "node:readline";
@@ -13245,7 +13654,7 @@ init_output();
13245
13654
  function getVersion2() {
13246
13655
  try {
13247
13656
  const require2 = createRequire3(import.meta.url);
13248
- const pkgPath = join22(dirname4(fileURLToPath2(import.meta.url)), "..", "package.json");
13657
+ const pkgPath = join23(dirname3(fileURLToPath2(import.meta.url)), "..", "package.json");
13249
13658
  const pkg = require2(pkgPath);
13250
13659
  return pkg.version;
13251
13660
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",