MCPcopy Create free account
hub / github.com/Ishabdullah/Codey-v2 / tool_patch_file

Function tool_patch_file

tools/patch_tools.py:10–86  ·  view source on GitHub ↗

Replace first occurrence of old_str with new_str in file. Snapshots before patching for /undo support.

(path: str, old_str: str, new_str: str)

Source from the content-addressed store, hash-verified

8from core.filehistory import snapshot
9
10def tool_patch_file(path: str, old_str: str, new_str: str) -> str:
11 """
12 Replace first occurrence of old_str with new_str in file.
13 Snapshots before patching for /undo support.
14 """
15 p = Path(path).expanduser()
16 if not p.exists():
17 # Try relative to cwd
18 import os
19 p = Path(os.getcwd()) / path
20 if not p.exists():
21 return f"[ERROR] File not found: {path}"
22
23 try:
24 content = p.read_text(encoding="utf-8", errors="replace")
25 except Exception as e:
26 return f"[ERROR] Could not read {path}: {e}"
27
28 count = content.count(old_str)
29 if count == 0:
30 # old_str not found — model's context may be stale. Return the current
31 # file content so the model can reconstruct the correct patch or fall back
32 # to write_file with the full intended content instead of retrying blindly.
33 lines = content.splitlines()
34 return (
35 f"[PATCH_FAILED] old_str not found in {path} ({len(lines)} lines).\n"
36 "The file may have changed since you last read it. "
37 "Use write_file with the complete intended content, or re-read the file "
38 "and issue a corrected patch.\n"
39 f"Current file content:\n{content}"
40 )
41
42 if count > 1:
43 lines = content.splitlines()
44 return (
45 f"[ERROR] String found {count} times in {path}. "
46 f"Provide more context in 'old_str' to make it unique.\n"
47 f"File has {len(lines)} lines."
48 )
49
50 # Show diff preview
51 new_content = content.replace(old_str, new_str, 1)
52
53 if AGENT_CONFIG.get("confirm_write"):
54 warning(f"About to patch: {path}")
55 # Show context around the change
56 old_lines = old_str.splitlines()
57 new_lines = new_str.splitlines()
58 print(f" Removing: {repr(old_str[:80])}")
59 print(f" Adding: {repr(new_str[:80])}")
60 if not ask_confirm("Apply patch?"):
61 return "[CANCELLED] Patch cancelled."
62
63 # Pre-patch syntax check for Python files: reject patches that break syntax
64 if p.suffix == '.py':
65 try:
66 from core.linter import check_syntax
67 syn_err = check_syntax(new_content, str(p))

Callers 5

agent.pyFile · 0.90
test_unique_patchMethod · 0.90
test_collisionMethod · 0.90
test_not_foundMethod · 0.90
test_undo_roundtripMethod · 0.90

Calls 8

warningFunction · 0.90
check_syntaxFunction · 0.90
snapshotFunction · 0.90
get_filesystemFunction · 0.90
existsMethod · 0.80
countMethod · 0.45
getMethod · 0.45
writeMethod · 0.45

Tested by 4

test_unique_patchMethod · 0.72
test_collisionMethod · 0.72
test_not_foundMethod · 0.72
test_undo_roundtripMethod · 0.72