(_ context.Context, execCtx ExecutionContext, input any)
| 225 | } |
| 226 | |
| 227 | func (t *EditFileTool) Execute(_ context.Context, execCtx ExecutionContext, input any) (string, error) { |
| 228 | in := deref[EditFileInput](input) |
| 229 | path := resolveToolPath(execCtx, in.FilePath) |
| 230 | data, err := os.ReadFile(path) |
| 231 | if err != nil { |
| 232 | return formatEditError(editReadFailed, fmt.Sprintf("Error reading file: %s", err)), nil |
| 233 | } |
| 234 | fileContent := decodeToolBytes(data) |
| 235 | lineEndings := detectLineEndings(fileContent) |
| 236 | if lineEndings == "\r\n" { |
| 237 | fileContent = strings.ReplaceAll(fileContent, "\r\n", "\n") |
| 238 | } |
| 239 | cleanedNew := stripTrailingWhitespace(in.NewString, path) |
| 240 | if cascading := checkCascadingEdit(execCtx, path, in.OldString); cascading != "" { |
| 241 | return cascading, nil |
| 242 | } |
| 243 | actualOld := findActualString(fileContent, in.OldString) |
| 244 | if actualOld == "" && in.OldString != "" { |
| 245 | return formatEditError(editStringNotFound, fmt.Sprintf("String to replace not found in file.\n Searched for: %s\n Hint: the file may have changed since you last read it.\n%s", truncateForMsg(in.OldString, 120), matchFailureSnippet(fileContent, in.OldString))), nil |
| 246 | } |
| 247 | if in.OldString == "" { |
| 248 | actualOld = "" |
| 249 | } |
| 250 | count := countOccurrences(fileContent, in.OldString) |
| 251 | if !in.ReplaceAll && count > 1 { |
| 252 | return formatEditError(editMultipleMatches, fmt.Sprintf("Found %d matches of the string to replace, but replace_all is false.\n To replace all occurrences, set replace_all=true.\n To replace only one, include more surrounding context to make the match unique.", count)), nil |
| 253 | } |
| 254 | actualNew := cleanedNew |
| 255 | if actualOld != in.OldString { |
| 256 | actualNew = preserveQuoteStyle(in.OldString, actualOld, cleanedNew) |
| 257 | } |
| 258 | if actualNew == "" && !strings.HasSuffix(in.OldString, "\n") && strings.Contains(fileContent, actualOld+"\n") { |
| 259 | actualOld += "\n" |
| 260 | } |
| 261 | newContent := strings.ReplaceAll(fileContent, actualOld, actualNew) |
| 262 | if !in.ReplaceAll && actualOld != "" { |
| 263 | newContent = strings.Replace(fileContent, actualOld, actualNew, 1) |
| 264 | } |
| 265 | recordAppliedEdit(execCtx, path, in.OldString, actualNew) |
| 266 | writeContent := newContent |
| 267 | if lineEndings == "\r\n" { |
| 268 | writeContent = strings.ReplaceAll(writeContent, "\n", "\r\n") |
| 269 | } |
| 270 | if err := os.WriteFile(path, []byte(writeContent), 0o644); err != nil { |
| 271 | return formatEditError(editWriteFailed, fmt.Sprintf("Error writing file: %s", err)), nil |
| 272 | } |
| 273 | updateReadState(execCtx, path, writeContent, detectLineEndings(writeContent), false) |
| 274 | replaced := count |
| 275 | if !in.ReplaceAll { |
| 276 | replaced = 1 |
| 277 | } |
| 278 | diff := generateUnifiedDiff(path, fileContent, writeContent, 3) |
| 279 | result := fmt.Sprintf("Edit applied successfully: %d occurrence(s) replaced in %s", replaced, in.FilePath) |
| 280 | if diff != "" { |
| 281 | result += "\n---DIFF---\n" + diff |
| 282 | } |
| 283 | return result, nil |
| 284 | } |
nothing calls this directly
no test coverage detected