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.
- package/dist/index.js +497 -88
- 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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
1785
|
-
if (
|
|
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
|
|
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
|
-
|
|
1794
|
-
|
|
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 ${
|
|
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
|
|
2441
|
-
import { resolve as resolve9
|
|
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: {
|
|
2459
|
-
|
|
2460
|
-
|
|
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({
|
|
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
|
|
2498
|
-
if (
|
|
2499
|
-
results.push(`SKIP: old_string not found in ${
|
|
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
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
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
|
|
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 =
|
|
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"] ?
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
5452
|
-
const content = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
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
|
-
|
|
7490
|
-
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
9057
|
+
const thisDir = dirname4(fileURLToPath3(import.meta.url));
|
|
8776
9058
|
const candidates = [
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
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((
|
|
8945
|
-
rl.question(question, (answer) =>
|
|
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((
|
|
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
|
-
|
|
11334
|
+
resolve15();
|
|
11051
11335
|
});
|
|
11052
11336
|
child.on("error", () => {
|
|
11053
11337
|
if (this.currentPlayback === child)
|
|
11054
11338
|
this.currentPlayback = null;
|
|
11055
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
11906
|
+
const thisDir = dirname2(fileURLToPath(import.meta.url));
|
|
11508
11907
|
const candidates = [
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
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 =
|
|
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(
|
|
12426
|
+
const isImage = isImagePath(cleanPath) && existsSync13(resolve12(repoRoot, cleanPath));
|
|
12019
12427
|
if (activeTask) {
|
|
12020
12428
|
if (isImage) {
|
|
12021
12429
|
try {
|
|
12022
|
-
const imgPath =
|
|
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 =
|
|
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
|
|
12622
|
+
import { readFile as readFile9, stat as stat2 } from "node:fs/promises";
|
|
12214
12623
|
import { createHash } from "node:crypto";
|
|
12215
|
-
import { join as
|
|
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
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (${
|
|
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 =
|
|
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 ${
|
|
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((
|
|
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
|
-
|
|
13335
|
+
resolve15();
|
|
12927
13336
|
} else if (code === 0) {
|
|
12928
13337
|
printSuccess("vLLM server exited cleanly");
|
|
12929
|
-
|
|
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
|
|
12958
|
-
import { join as
|
|
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 =
|
|
13080
|
-
|
|
13081
|
-
writeFileSync7(
|
|
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
|
|
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 =
|
|
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