@empiricalrun/test-gen 0.63.0 → 0.64.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/agent/browsing/run.d.ts +2 -1
- package/dist/agent/browsing/run.d.ts.map +1 -1
- package/dist/agent/browsing/run.js +25 -12
- package/dist/agent/browsing/utils.d.ts +1 -1
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +5 -4
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +2 -0
- package/dist/agent/chat/models.js +1 -1
- package/dist/agent/cua/computer.d.ts.map +1 -1
- package/dist/agent/cua/computer.js +45 -24
- package/dist/agent/cua/pw-codegen/element-from-point.d.ts +3 -1
- package/dist/agent/cua/pw-codegen/element-from-point.d.ts.map +1 -1
- package/dist/agent/cua/pw-codegen/element-from-point.js +73 -56
- package/dist/agent/cua/pw-codegen/types.d.ts +2 -1
- package/dist/agent/cua/pw-codegen/types.d.ts.map +1 -1
- package/dist/artifacts/utils.d.ts +21 -0
- package/dist/artifacts/utils.d.ts.map +1 -0
- package/dist/artifacts/utils.js +102 -0
- package/dist/bin/index.js +2 -1
- package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.js +2 -0
- package/dist/file/server.js +1 -1
- package/dist/test-build/index.js +1 -1
- package/dist/tool-call-service/index.d.ts +5 -2
- package/dist/tool-call-service/index.d.ts.map +1 -1
- package/dist/tool-call-service/index.js +11 -2
- package/dist/tool-call-service/utils.d.ts +1 -0
- package/dist/tool-call-service/utils.d.ts.map +1 -1
- package/dist/tool-call-service/utils.js +6 -2
- package/dist/tools/commit-and-create-pr.d.ts +1 -1
- package/dist/tools/commit-and-create-pr.d.ts.map +1 -1
- package/dist/tools/commit-and-create-pr.js +1 -1
- package/dist/tools/diagnosis-fetcher.d.ts +1 -1
- package/dist/tools/diagnosis-fetcher.d.ts.map +1 -1
- package/dist/tools/diagnosis-fetcher.js +1 -1
- package/dist/tools/download-build.d.ts +1 -1
- package/dist/tools/download-build.d.ts.map +1 -1
- package/dist/tools/download-build.js +1 -1
- package/dist/tools/environment-crud.d.ts +1 -1
- package/dist/tools/environment-crud.d.ts.map +1 -1
- package/dist/tools/environment-crud.js +1 -1
- package/dist/tools/grep/index.d.ts +1 -1
- package/dist/tools/grep/index.d.ts.map +1 -1
- package/dist/tools/grep/index.js +4 -3
- package/dist/tools/str_replace_editor.d.ts +5 -7
- package/dist/tools/str_replace_editor.d.ts.map +1 -1
- package/dist/tools/str_replace_editor.js +259 -247
- package/dist/tools/test-gen-browser.d.ts +1 -1
- package/dist/tools/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.js +45 -20
- package/dist/tools/test-run-fetcher/index.d.ts +1 -1
- package/dist/tools/test-run-fetcher/index.d.ts.map +1 -1
- package/dist/tools/test-run-fetcher/index.js +1 -1
- package/dist/tools/test-run.d.ts +1 -1
- package/dist/tools/test-run.d.ts.map +1 -1
- package/dist/tools/test-run.js +35 -12
- package/dist/tools/utils/index.d.ts +0 -13
- package/dist/tools/utils/index.d.ts.map +1 -1
- package/dist/tools/utils/index.js +0 -47
- package/dist/utils/exec.d.ts +4 -4
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +2 -4
- package/dist/utils/file-tree.d.ts.map +1 -1
- package/dist/utils/file-tree.js +2 -0
- package/package.json +4 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/tools/codegen-agent.d.ts +0 -3
- package/dist/tools/codegen-agent.d.ts.map +0 -1
- package/dist/tools/codegen-agent.js +0 -40
|
@@ -4,29 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.textEditorTools = void 0;
|
|
7
|
-
exports.cleanupBackupFiles = cleanupBackupFiles;
|
|
8
7
|
exports.strReplaceEditorExecutor = strReplaceEditorExecutor;
|
|
9
8
|
const fs_1 = __importDefault(require("fs"));
|
|
10
9
|
const path_1 = __importDefault(require("path"));
|
|
11
10
|
const zod_1 = require("zod");
|
|
12
11
|
const web_1 = require("../bin/utils/platform/web");
|
|
13
|
-
function createBackup(filePath) {
|
|
14
|
-
const backupPath = `${filePath}.bak`;
|
|
15
|
-
if (fs_1.default.existsSync(filePath)) {
|
|
16
|
-
fs_1.default.copyFileSync(filePath, backupPath);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function hasBackup(filePath) {
|
|
20
|
-
const backupPath = `${filePath}.bak`;
|
|
21
|
-
return fs_1.default.existsSync(backupPath);
|
|
22
|
-
}
|
|
23
|
-
function restoreBackup(filePath) {
|
|
24
|
-
const backupPath = `${filePath}.bak`;
|
|
25
|
-
if (fs_1.default.existsSync(backupPath)) {
|
|
26
|
-
fs_1.default.copyFileSync(backupPath, filePath);
|
|
27
|
-
fs_1.default.unlinkSync(backupPath);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
12
|
/**
|
|
31
13
|
* While running str_replace command, we've seen LLM can struggle to send unique old_str.
|
|
32
14
|
* This function tries to find unique contexts for each occurrence of old_str, so that the error
|
|
@@ -85,234 +67,249 @@ function getUniqueOccurences(contents, old_str) {
|
|
|
85
67
|
}
|
|
86
68
|
return uniqueContexts;
|
|
87
69
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
else if (file.endsWith(".bak")) {
|
|
117
|
-
try {
|
|
118
|
-
fs_1.default.unlinkSync(fullPath);
|
|
119
|
-
cleanedCount++;
|
|
120
|
-
}
|
|
121
|
-
catch (unlinkError) {
|
|
122
|
-
// Intentionally ignore errors during cleanup
|
|
123
|
-
}
|
|
124
|
-
}
|
|
70
|
+
function escapeRegExp(text) {
|
|
71
|
+
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
72
|
+
}
|
|
73
|
+
async function fileViewExecutor({ input, filePath, absoluteFilePath, }) {
|
|
74
|
+
if (!fs_1.default.existsSync(absoluteFilePath)) {
|
|
75
|
+
return {
|
|
76
|
+
result: `Error: File ${filePath} not found. Please provide relative file path to the Repository.`,
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// Handle directory view
|
|
81
|
+
if (fs_1.default.statSync(absoluteFilePath).isDirectory()) {
|
|
82
|
+
const files = fs_1.default.readdirSync(absoluteFilePath);
|
|
83
|
+
return {
|
|
84
|
+
result: files.join("\n"),
|
|
85
|
+
isError: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Check if file is binary, which is not supported
|
|
90
|
+
const { isBinary } = await import("istextorbinary");
|
|
91
|
+
const binary = isBinary(absoluteFilePath);
|
|
92
|
+
if (binary) {
|
|
93
|
+
return {
|
|
94
|
+
result: "Error: File is binary, which is not supported",
|
|
95
|
+
isError: true,
|
|
96
|
+
};
|
|
125
97
|
}
|
|
98
|
+
}
|
|
99
|
+
// Handle file view
|
|
100
|
+
const content = fs_1.default.readFileSync(absoluteFilePath, "utf8");
|
|
101
|
+
const lines = content.split("\n");
|
|
102
|
+
if (input.view_range) {
|
|
103
|
+
const [start, end] = input.view_range;
|
|
104
|
+
const endLine = end === -1 ? lines.length : end;
|
|
105
|
+
return {
|
|
106
|
+
result: lines
|
|
107
|
+
.slice(start - 1, endLine)
|
|
108
|
+
.map((line, idx) => `${start + idx}: ${line}`)
|
|
109
|
+
.join("\n"),
|
|
110
|
+
isError: false,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
result: lines.map((line, idx) => `${idx + 1}: ${line}`).join("\n"),
|
|
115
|
+
isError: false,
|
|
126
116
|
};
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
}
|
|
118
|
+
async function fileCreateExecutor({ input, filePath, absoluteFilePath, repoDir, }) {
|
|
119
|
+
if (input.file_text === undefined || input.file_text === null) {
|
|
120
|
+
return {
|
|
121
|
+
result: "Error: file_text is required for create command",
|
|
122
|
+
isError: true,
|
|
123
|
+
};
|
|
129
124
|
}
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
if (filePath.endsWith("test.ts")) {
|
|
126
|
+
return {
|
|
127
|
+
result: "Error: Creating test.ts files is not allowed. Did you mean spec.ts?",
|
|
128
|
+
isError: true,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (filePath.endsWith("spec.ts") && !filePath.startsWith("tests/")) {
|
|
132
|
+
return {
|
|
133
|
+
result: "Error: Creating spec.ts files is not allowed outside tests/ directory",
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const fileName = path_1.default.basename(filePath);
|
|
138
|
+
if (!fileName.includes(".")) {
|
|
139
|
+
return {
|
|
140
|
+
result: `Error: File name must include a file extension. This tool cannot create empty directories.
|
|
141
|
+
If you need to create a file which is inside a directory that does not exist, you can expect this tool to create
|
|
142
|
+
the required directories recursively for the new file.`,
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const parentDir = path_1.default.dirname(absoluteFilePath);
|
|
147
|
+
if (parentDir !== "." && !fs_1.default.existsSync(parentDir)) {
|
|
148
|
+
// Ensure parent directory exists
|
|
149
|
+
fs_1.default.mkdirSync(parentDir, { recursive: true });
|
|
150
|
+
}
|
|
151
|
+
fs_1.default.writeFileSync(absoluteFilePath, input.file_text);
|
|
152
|
+
let createTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
153
|
+
if (!createTypescriptResult.success) {
|
|
154
|
+
return {
|
|
155
|
+
result: `File ${filePath} has been created. However, type checks are failing with errors:\n\n${createTypescriptResult.errors.join("\n")}`,
|
|
156
|
+
isError: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
return {
|
|
161
|
+
result: `Successfully created file ${filePath}`,
|
|
162
|
+
isError: false,
|
|
163
|
+
};
|
|
132
164
|
}
|
|
133
|
-
return cleanedCount;
|
|
134
165
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
166
|
+
async function fileStrReplaceExecutor({ input, filePath, absoluteFilePath, repoDir, }) {
|
|
167
|
+
if (!fs_1.default.existsSync(absoluteFilePath)) {
|
|
168
|
+
return {
|
|
169
|
+
result: `Error: File ${filePath} not found. Please provide relative file path to the Repository.`,
|
|
170
|
+
isError: true,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (!input.old_str) {
|
|
174
|
+
return {
|
|
175
|
+
result: "Error: old_str is required for str_replace command",
|
|
176
|
+
isError: true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (input.new_str === undefined || input.new_str === null) {
|
|
180
|
+
// "" is valid as new_str, so we check for nullish -- not falsy
|
|
181
|
+
return {
|
|
182
|
+
result: "Error: new_str is required for str_replace command",
|
|
183
|
+
isError: true,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const content = fs_1.default.readFileSync(absoluteFilePath, "utf8");
|
|
187
|
+
// Normalize newlines in both the content and search string
|
|
188
|
+
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
189
|
+
const normalizedOldStr = input.old_str
|
|
190
|
+
.replace(/\\n/g, "\n")
|
|
191
|
+
.replace(/\r\n/g, "\n");
|
|
192
|
+
if (!normalizedContent.includes(normalizedOldStr)) {
|
|
193
|
+
return {
|
|
194
|
+
result: `old_str not found in file: ${filePath}`,
|
|
195
|
+
isError: true,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const escapedOldStr = escapeRegExp(normalizedOldStr);
|
|
200
|
+
const occurences = normalizedContent.match(new RegExp(escapedOldStr, "g"));
|
|
201
|
+
if (occurences && occurences.length > 1) {
|
|
202
|
+
const uniqueContexts = getUniqueOccurences(content, input.old_str);
|
|
203
|
+
if (uniqueContexts.length === 0) {
|
|
204
|
+
return {
|
|
205
|
+
result: `Error: old_str found ${occurences.length} times in file: ${filePath}, but no unique contexts could be identified. Try using a more specific string.`,
|
|
206
|
+
isError: true,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const uniqueContextsString = uniqueContexts
|
|
210
|
+
.map(({ uniqueContext, lineNumber }, idx) => `${idx + 1}. For occurence at line number ${lineNumber}, unique context is:\n\`\`\`\n${uniqueContext}\n\`\`\`\n`)
|
|
211
|
+
.join("\n");
|
|
212
|
+
return {
|
|
213
|
+
result: `Error: old_str found ${occurences.length} times in file: ${filePath}. Please use one of these unique contexts instead:\n\n${uniqueContextsString}`,
|
|
214
|
+
isError: true,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const newContent = normalizedContent.replace(normalizedOldStr, input.new_str);
|
|
218
|
+
fs_1.default.writeFileSync(absoluteFilePath, newContent);
|
|
219
|
+
let strReplaceTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
220
|
+
if (!strReplaceTypescriptResult.success) {
|
|
221
|
+
return {
|
|
222
|
+
result: `Edits to file ${filePath} have been applied. However, type checks are failing with errors:\n\n${strReplaceTypescriptResult.errors.join("\n")}`,
|
|
223
|
+
isError: true,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return {
|
|
228
|
+
result: `Edits to file ${filePath} have been applied. Type checks have also passed.`,
|
|
229
|
+
isError: false,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function fileInsertExecutor({ input, filePath, absoluteFilePath, repoDir, }) {
|
|
235
|
+
if (!fs_1.default.existsSync(absoluteFilePath)) {
|
|
236
|
+
return {
|
|
237
|
+
result: `Error: File ${filePath} not found. Please provide relative file path to the Repository.`,
|
|
238
|
+
isError: true,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (input.insert_line === undefined || !input.new_str) {
|
|
242
|
+
return {
|
|
243
|
+
result: "Error: insert_line and new_str are required for insert command",
|
|
244
|
+
isError: true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const content = fs_1.default.readFileSync(absoluteFilePath, "utf8");
|
|
248
|
+
const lines = content.split("\n");
|
|
249
|
+
if (input.insert_line < 1) {
|
|
250
|
+
return {
|
|
251
|
+
result: "Error: insert_line must be greater than or equal to 1 (line numbers are 1-indexed).",
|
|
252
|
+
isError: true,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (input.insert_line > lines.length + 1) {
|
|
256
|
+
return {
|
|
257
|
+
result: `Error: The file at ${filePath} has only ${lines.length} lines, so insert_line must be less than or equal to ${lines.length + 1}. At the maximum value of ${lines.length + 1}, you can insert at the end of the file.`,
|
|
258
|
+
isError: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
lines.splice(input.insert_line - 1, 0, input.new_str);
|
|
262
|
+
fs_1.default.writeFileSync(absoluteFilePath, lines.join("\n"));
|
|
263
|
+
let insertTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
264
|
+
if (!insertTypescriptResult.success) {
|
|
265
|
+
return {
|
|
266
|
+
result: `Insertion in file ${filePath} was applied. However, type checks are failing with errors:\n${insertTypescriptResult.errors.join("\n")}`,
|
|
267
|
+
isError: true,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
return {
|
|
272
|
+
result: `Insertion in file ${filePath} was applied. Type checks have also passed.`,
|
|
273
|
+
isError: false,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
137
276
|
}
|
|
138
277
|
/**
|
|
139
278
|
* Our implementation of Claude's built-in text editor tool
|
|
140
279
|
* https://docs.anthropic.com/en/docs/build-with-claude/tool-use/text-editor-tool
|
|
141
280
|
*/
|
|
142
|
-
async function strReplaceEditorExecutor(input, repoPath) {
|
|
281
|
+
async function strReplaceEditorExecutor({ input, repoPath, }) {
|
|
143
282
|
const repoDir = repoPath;
|
|
144
283
|
const { path: filePath } = input;
|
|
145
284
|
const absoluteFilePath = path_1.default.join(repoDir, filePath);
|
|
146
285
|
try {
|
|
147
|
-
let content;
|
|
148
|
-
let lines;
|
|
149
|
-
let newContent;
|
|
150
286
|
switch (input.command) {
|
|
151
287
|
case "view":
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
result: "Error: File not found",
|
|
155
|
-
isError: true,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
// Handle directory view
|
|
159
|
-
if (fs_1.default.statSync(absoluteFilePath).isDirectory()) {
|
|
160
|
-
const files = fs_1.default.readdirSync(absoluteFilePath);
|
|
161
|
-
return {
|
|
162
|
-
result: files.join("\n"),
|
|
163
|
-
isError: false,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
// Check if file is binary, which is not supported
|
|
168
|
-
const { isBinary } = await import("istextorbinary");
|
|
169
|
-
const binary = isBinary(absoluteFilePath);
|
|
170
|
-
if (binary) {
|
|
171
|
-
return {
|
|
172
|
-
result: "Error: File is binary, which is not supported",
|
|
173
|
-
isError: true,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Handle file view
|
|
178
|
-
content = fs_1.default.readFileSync(absoluteFilePath, "utf8");
|
|
179
|
-
lines = content.split("\n");
|
|
180
|
-
if (input.view_range) {
|
|
181
|
-
const [start, end] = input.view_range;
|
|
182
|
-
const endLine = end === -1 ? lines.length : end;
|
|
183
|
-
return {
|
|
184
|
-
result: lines
|
|
185
|
-
.slice(start - 1, endLine)
|
|
186
|
-
.map((line, idx) => `${start + idx}: ${line}`)
|
|
187
|
-
.join("\n"),
|
|
188
|
-
isError: false,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
return {
|
|
192
|
-
result: lines.map((line, idx) => `${idx + 1}: ${line}`).join("\n"),
|
|
193
|
-
isError: false,
|
|
194
|
-
};
|
|
288
|
+
return fileViewExecutor({ input, filePath, absoluteFilePath });
|
|
195
289
|
case "create":
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
if (filePath.endsWith("spec.ts") && !filePath.startsWith("tests/")) {
|
|
203
|
-
throw new Error("Creating spec.ts files is not allowed outside tests/ directory");
|
|
204
|
-
}
|
|
205
|
-
const parentDir = path_1.default.dirname(absoluteFilePath);
|
|
206
|
-
if (parentDir !== "." && !fs_1.default.existsSync(parentDir)) {
|
|
207
|
-
// Ensure parent directory exists
|
|
208
|
-
fs_1.default.mkdirSync(parentDir, { recursive: true });
|
|
209
|
-
}
|
|
210
|
-
fs_1.default.writeFileSync(absoluteFilePath, input.file_text);
|
|
211
|
-
let createTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
212
|
-
if (!createTypescriptResult.success) {
|
|
213
|
-
return {
|
|
214
|
-
result: `File ${filePath} has been created. However, type checks are failing with errors:\n${createTypescriptResult.errors.join("\n")}`,
|
|
215
|
-
isError: true,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
result: `Successfully created file ${filePath}`,
|
|
220
|
-
isError: false,
|
|
221
|
-
};
|
|
290
|
+
return fileCreateExecutor({
|
|
291
|
+
input,
|
|
292
|
+
filePath,
|
|
293
|
+
absoluteFilePath,
|
|
294
|
+
repoDir,
|
|
295
|
+
});
|
|
222
296
|
case "str_replace":
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
createBackup(absoluteFilePath);
|
|
231
|
-
content = fs_1.default.readFileSync(absoluteFilePath, "utf8");
|
|
232
|
-
// Normalize newlines in both the content and search string
|
|
233
|
-
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
234
|
-
const normalizedOldStr = input.old_str
|
|
235
|
-
.replace(/\\n/g, "\n")
|
|
236
|
-
.replace(/\r\n/g, "\n");
|
|
237
|
-
if (!normalizedContent.includes(normalizedOldStr)) {
|
|
238
|
-
return {
|
|
239
|
-
result: `old_str not found in file: ${filePath}`,
|
|
240
|
-
isError: true,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
const escapedOldStr = escapeRegExp(normalizedOldStr);
|
|
245
|
-
const occurences = normalizedContent.match(new RegExp(escapedOldStr, "g"));
|
|
246
|
-
if (occurences && occurences.length > 1) {
|
|
247
|
-
const uniqueContexts = getUniqueOccurences(content, input.old_str);
|
|
248
|
-
if (uniqueContexts.length === 0) {
|
|
249
|
-
return {
|
|
250
|
-
result: `Error: old_str found ${occurences.length} times in file: ${filePath}, but no unique contexts could be identified. Try using a more specific string.`,
|
|
251
|
-
isError: true,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
const uniqueContextsString = uniqueContexts
|
|
255
|
-
.map(({ uniqueContext, lineNumber }, idx) => `${idx + 1}. For occurence at line number ${lineNumber}, unique context is:\n\`\`\`\n${uniqueContext}\n\`\`\`\n`)
|
|
256
|
-
.join("\n");
|
|
257
|
-
return {
|
|
258
|
-
result: `Error: old_str found ${occurences.length} times in file: ${filePath}. Please use one of these unique contexts instead:\n\n${uniqueContextsString}`,
|
|
259
|
-
isError: true,
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
newContent = normalizedContent.replace(normalizedOldStr, input.new_str);
|
|
263
|
-
fs_1.default.writeFileSync(absoluteFilePath, newContent);
|
|
264
|
-
let strReplaceTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
265
|
-
if (!strReplaceTypescriptResult.success) {
|
|
266
|
-
return {
|
|
267
|
-
result: `Edits to file ${filePath} have been applied. However, type checks are failing with errors:\n${strReplaceTypescriptResult.errors.join("\n")}`,
|
|
268
|
-
isError: true,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
return {
|
|
273
|
-
result: `Edits to file ${filePath} have been applied. Type checks have also passed.`,
|
|
274
|
-
isError: false,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
297
|
+
return fileStrReplaceExecutor({
|
|
298
|
+
input,
|
|
299
|
+
filePath,
|
|
300
|
+
absoluteFilePath,
|
|
301
|
+
repoDir,
|
|
302
|
+
});
|
|
278
303
|
case "insert":
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (input.insert_line < 1) {
|
|
286
|
-
throw new Error("insert_line must be greater than or equal to 1 (line numbers are 1-indexed).");
|
|
287
|
-
}
|
|
288
|
-
if (input.insert_line > lines.length + 1) {
|
|
289
|
-
throw new Error(`The file at ${filePath} has only ${lines.length} lines, so insert_line must be less than or equal to ${lines.length + 1}. At the maximum value of ${lines.length + 1}, you can insert at the end of the file.`);
|
|
290
|
-
}
|
|
291
|
-
lines.splice(input.insert_line - 1, 0, input.new_str);
|
|
292
|
-
fs_1.default.writeFileSync(absoluteFilePath, lines.join("\n"));
|
|
293
|
-
let insertTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
294
|
-
if (!insertTypescriptResult.success) {
|
|
295
|
-
return {
|
|
296
|
-
result: `Insertion in file ${filePath} was applied. However, type checks are failing with errors:\n${insertTypescriptResult.errors.join("\n")}`,
|
|
297
|
-
isError: true,
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
return {
|
|
302
|
-
result: `Insertion in file ${filePath} was applied. Type checks have also passed.`,
|
|
303
|
-
isError: false,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
304
|
+
return fileInsertExecutor({
|
|
305
|
+
input,
|
|
306
|
+
filePath,
|
|
307
|
+
absoluteFilePath,
|
|
308
|
+
repoDir,
|
|
309
|
+
});
|
|
306
310
|
case "undo_edit":
|
|
307
|
-
if (hasBackup(absoluteFilePath)) {
|
|
308
|
-
restoreBackup(absoluteFilePath);
|
|
309
|
-
return {
|
|
310
|
-
result: `Successfully restored ${filePath} from backup`,
|
|
311
|
-
isError: false,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
311
|
return {
|
|
315
|
-
result: `
|
|
312
|
+
result: `undo_edit tool is not supported.`,
|
|
316
313
|
isError: true,
|
|
317
314
|
};
|
|
318
315
|
default:
|
|
@@ -343,28 +340,37 @@ File contents are returned with line numbers, starting from 1.
|
|
|
343
340
|
path: zod_1.z.string().describe("The path to the file or directory to view."),
|
|
344
341
|
}),
|
|
345
342
|
},
|
|
346
|
-
execute: async (input, repoPath) => {
|
|
343
|
+
execute: async ({ input, repoPath, }) => {
|
|
347
344
|
return strReplaceEditorExecutor({
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
345
|
+
input: {
|
|
346
|
+
command: "view",
|
|
347
|
+
path: input.path,
|
|
348
|
+
},
|
|
349
|
+
repoPath,
|
|
350
|
+
});
|
|
351
351
|
},
|
|
352
352
|
};
|
|
353
353
|
const fileCreateTool = {
|
|
354
354
|
schema: {
|
|
355
355
|
name: "fileCreateTool",
|
|
356
|
-
description:
|
|
356
|
+
description: `A tool to create a new file with given contents.
|
|
357
|
+
This tool will also create parent directories that do not exist.
|
|
358
|
+
For example, for path "tests/example/foo.spec.ts", the tool will create
|
|
359
|
+
directories "tests", "tests/example", and "tests/example/foo.spec.ts".`,
|
|
357
360
|
parameters: zod_1.z.object({
|
|
358
361
|
path: zod_1.z.string().describe("The path to the new file."),
|
|
359
362
|
file_text: zod_1.z.string().describe("The contents of the new file."),
|
|
360
363
|
}),
|
|
361
364
|
},
|
|
362
|
-
execute: async (input, repoPath) => {
|
|
365
|
+
execute: async ({ input, repoPath, }) => {
|
|
363
366
|
return strReplaceEditorExecutor({
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
367
|
+
input: {
|
|
368
|
+
command: "create",
|
|
369
|
+
path: input.path,
|
|
370
|
+
file_text: input.file_text,
|
|
371
|
+
},
|
|
372
|
+
repoPath,
|
|
373
|
+
});
|
|
368
374
|
},
|
|
369
375
|
};
|
|
370
376
|
const stringReplaceTool = {
|
|
@@ -378,13 +384,16 @@ in the file. If old_str is not unique, the tool will return an error.`,
|
|
|
378
384
|
new_str: zod_1.z.string().describe("The string to replace old_str with."),
|
|
379
385
|
}),
|
|
380
386
|
},
|
|
381
|
-
execute: async (input, repoPath) => {
|
|
387
|
+
execute: async ({ input, repoPath, }) => {
|
|
382
388
|
return strReplaceEditorExecutor({
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
389
|
+
input: {
|
|
390
|
+
command: "str_replace",
|
|
391
|
+
path: input.path,
|
|
392
|
+
old_str: input.old_str,
|
|
393
|
+
new_str: input.new_str,
|
|
394
|
+
},
|
|
395
|
+
repoPath,
|
|
396
|
+
});
|
|
388
397
|
},
|
|
389
398
|
};
|
|
390
399
|
const stringInsertTool = {
|
|
@@ -403,13 +412,16 @@ To insert a string at the end of the file, you should use insert_line = (total l
|
|
|
403
412
|
new_str: zod_1.z.string().describe("The string to insert."),
|
|
404
413
|
}),
|
|
405
414
|
},
|
|
406
|
-
execute: async (input, repoPath) => {
|
|
415
|
+
execute: async ({ input, repoPath, }) => {
|
|
407
416
|
return strReplaceEditorExecutor({
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
417
|
+
input: {
|
|
418
|
+
command: "insert",
|
|
419
|
+
path: input.path,
|
|
420
|
+
insert_line: input.insert_line,
|
|
421
|
+
new_str: input.new_str,
|
|
422
|
+
},
|
|
423
|
+
repoPath,
|
|
424
|
+
});
|
|
413
425
|
},
|
|
414
426
|
};
|
|
415
427
|
exports.textEditorTools = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAmGpE,eAAO,MAAM,4BAA4B,EAAE,IAwI1C,CAAC"}
|