(tool_input: dict[str, Any], context: ToolContext)
| 215 | # -- Main call ----------------------------------------------------------------- |
| 216 | |
| 217 | def _edit_call(tool_input: dict[str, Any], context: ToolContext) -> ToolResult: |
| 218 | file_path = tool_input["file_path"] |
| 219 | old_string = tool_input["old_string"] |
| 220 | new_string = tool_input["new_string"] |
| 221 | replace_all = bool(tool_input.get("replace_all", False)) |
| 222 | |
| 223 | if not isinstance(file_path, str) or not file_path: |
| 224 | raise ToolInputError("file_path must be a non-empty string") |
| 225 | if not isinstance(old_string, str): |
| 226 | raise ToolInputError("old_string must be a string") |
| 227 | if not isinstance(new_string, str): |
| 228 | raise ToolInputError("new_string must be a string") |
| 229 | if old_string == new_string: |
| 230 | raise ToolInputError("old_string and new_string must differ") |
| 231 | |
| 232 | path = context.ensure_allowed_path(file_path) |
| 233 | |
| 234 | # Reject .ipynb files |
| 235 | if path.suffix.lower() == ".ipynb": |
| 236 | raise ToolInputError("Cannot edit .ipynb files with Edit tool. Use the NotebookEdit tool instead.") |
| 237 | |
| 238 | # File creation (empty old_string) |
| 239 | if old_string == "": |
| 240 | if path.exists(): |
| 241 | raise ToolInputError("old_string is empty but file already exists -- use non-empty old_string to edit") |
| 242 | path.parent.mkdir(parents=True, exist_ok=True) |
| 243 | path.write_text(new_string, encoding="utf-8") |
| 244 | context.mark_file_read(path) |
| 245 | return ToolResult( |
| 246 | name="Edit", |
| 247 | output={ |
| 248 | "type": "create", |
| 249 | "filePath": str(path), |
| 250 | "content": new_string, |
| 251 | "structuredPatch": [], |
| 252 | }, |
| 253 | ) |
| 254 | |
| 255 | # File existence and type checks |
| 256 | if not path.exists(): |
| 257 | hint = _find_similar_file(file_path, context.cwd) |
| 258 | msg = f"file does not exist: {path}" |
| 259 | if hint: |
| 260 | msg += f'. Did you mean "{hint}"?' |
| 261 | raise ToolInputError(msg) |
| 262 | if not path.is_file(): |
| 263 | raise ToolInputError(f"path is not a file: {path}") |
| 264 | |
| 265 | # File size guard |
| 266 | try: |
| 267 | size = path.stat().st_size |
| 268 | except OSError: |
| 269 | size = 0 |
| 270 | if size > _MAX_FILE_SIZE: |
| 271 | raise ToolInputError(f"file is too large ({size} bytes, max {_MAX_FILE_SIZE})") |
| 272 | |
| 273 | # Staleness check |
| 274 | if not context.was_file_read_and_unchanged(path): |
nothing calls this directly
no test coverage detected