MCPcopy Create free account
hub / github.com/OpenRaiser/NanoResearch / _apply_patch

Method _apply_patch

nanoresearch/agents/debug.py:330–396  ·  view source on GitHub ↗

Apply a single patch to a file. Returns True if successful.

(self, code_dir: Path, patch: dict)

Source from the content-addressed store, hash-verified

328}"""
329
330 def _apply_patch(self, code_dir: Path, patch: dict) -> bool:
331 """Apply a single patch to a file. Returns True if successful."""
332 filename = patch["file"]
333 old_text = patch["old"]
334 new_text = patch["new"]
335
336 filepath = code_dir / filename
337 try:
338 filepath.resolve().relative_to(code_dir.resolve())
339 except ValueError:
340 logger.warning(f"Patch target outside code_dir: {filepath}, skipping")
341 return False
342 if not filepath.exists():
343 if not old_text or old_text.strip() == "":
344 filepath.parent.mkdir(parents=True, exist_ok=True)
345 filepath.write_text(new_text)
346 logger.info(f"Created new file: {filepath}")
347 return True
348 logger.warning(f"Patch target not found: {filepath}, skipping")
349 return False
350
351 content = filepath.read_text(errors="replace")
352
353 # Strategy 1: Exact match
354 if old_text in content:
355 filepath.write_text(content.replace(old_text, new_text, 1))
356 return True
357
358 # Strategy 2: Strip trailing whitespace
359 def strip_trailing(text: str) -> str:
360 return "\n".join(line.rstrip() for line in text.split("\n"))
361
362 content_stripped = strip_trailing(content)
363 old_stripped = strip_trailing(old_text)
364 if old_stripped in content_stripped:
365 filepath.write_text(content_stripped.replace(old_stripped, strip_trailing(new_text), 1))
366 return True
367
368 # Strategy 3: Line-by-line fuzzy match
369 content_lines = content.split("\n")
370 old_lines = old_text.strip().split("\n")
371 if len(old_lines) >= 2:
372 first_line = old_lines[0].strip()
373 last_line = old_lines[-1].strip()
374 for i in range(len(content_lines)):
375 if first_line and first_line in content_lines[i].strip():
376 for j in range(i + len(old_lines) - 1, min(i + len(old_lines) + 5, len(content_lines))):
377 if last_line and last_line in content_lines[j].strip():
378 new_lines = new_text.rstrip().split("\n")
379 content_lines[i:j+1] = new_lines
380 filepath.write_text("\n".join(content_lines))
381 return True
382
383 # Strategy 4: Single line matching
384 if "\n" not in old_text.strip():
385 old_line = old_text.strip()
386 for i, line in enumerate(content_lines):
387 if old_line == line.strip():

Callers 1

runMethod · 0.95

Calls 2

write_textMethod · 0.80
read_textMethod · 0.80

Tested by

no test coverage detected