Skip to content

Commit db33358

Browse files
authored
feat(editor): add EDITOR_DISABLE_BACKUP option to skip backup file creation (#259)
1 parent 36985b5 commit db33358

File tree

3 files changed

+88
-6
lines changed

3 files changed

+88
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ The Mem0 Memory Tool supports three different backend configurations:
10291029
| EDITOR_DIR_TREE_MAX_DEPTH | Maximum depth for directory tree visualization | 2 |
10301030
| EDITOR_DEFAULT_STYLE | Default style for output panels | default |
10311031
| EDITOR_DEFAULT_LANGUAGE | Default language for syntax highlighting | python |
1032+
| EDITOR_DISABLE_BACKUP | Skip creating .bak backup files during edit operations | false |
10321033

10331034
#### Environment Tool
10341035

src/strands_tools/editor.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,8 +536,10 @@ def editor(
536536

537537
# Make replacements and backup
538538
new_content = content.replace(old_str, new_str)
539-
backup_path = f"{path}.bak"
540-
shutil.copy2(path, backup_path)
539+
disable_backup = os.environ.get("EDITOR_DISABLE_BACKUP", "").lower() == "true"
540+
if not disable_backup:
541+
backup_path = f"{path}.bak"
542+
shutil.copy2(path, backup_path)
541543

542544
# Write new content and update cache
543545
with open(path, "w") as f:
@@ -606,8 +608,12 @@ def editor(
606608

607609
# Make replacements and backup
608610
new_content = regex.sub(new_str, content)
609-
backup_path = f"{path}.bak"
610-
shutil.copy2(path, backup_path)
611+
disable_backup = os.environ.get("EDITOR_DISABLE_BACKUP", "").lower() == "true"
612+
if not disable_backup:
613+
backup_path = f"{path}.bak"
614+
shutil.copy2(path, backup_path)
615+
else:
616+
backup_path = "Disabled"
611617

612618
# Write new content and update cache
613619
with open(path, "w") as f:
@@ -678,8 +684,10 @@ def editor(
678684
raise ValueError(f"insert_line {insert_line} is out of range")
679685

680686
# Make backup
681-
backup_path = f"{path}.bak"
682-
shutil.copy2(path, backup_path)
687+
disable_backup = os.environ.get("EDITOR_DISABLE_BACKUP", "").lower() == "true"
688+
if not disable_backup:
689+
backup_path = f"{path}.bak"
690+
shutil.copy2(path, backup_path)
683691

684692
# Insert and write
685693
lines.insert(insert_line, new_str)

tests/test_editor.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,79 @@ def test_insert_command(self, mock_user_input, temp_file, clean_content_history)
253253
# changing the original file structure
254254
assert "Line 2\nINSERTED LINE\nLine 3\n" in content
255255

256+
@patch("strands_tools.editor.get_user_input")
257+
@patch.dict("os.environ", {"EDITOR_DISABLE_BACKUP": "true"})
258+
def test_str_replace_no_backup(self, mock_user_input, temp_file, clean_content_history):
259+
"""Test str_replace without creating backup when EDITOR_DISABLE_BACKUP is set."""
260+
mock_user_input.return_value = "y"
261+
262+
result = editor.editor(
263+
command="str_replace",
264+
path=temp_file,
265+
old_str="Line 2",
266+
new_str="Modified Line 2"
267+
)
268+
269+
assert result["status"] == "success"
270+
271+
# Verify backup was NOT created
272+
backup_path = f"{temp_file}.bak"
273+
assert not os.path.exists(backup_path)
274+
275+
@patch("strands_tools.editor.get_user_input")
276+
@patch.dict("os.environ", {"EDITOR_DISABLE_BACKUP": "true"})
277+
def test_pattern_replace_no_backup(self, mock_user_input, temp_file, clean_content_history):
278+
"""Test pattern_replace without creating backup."""
279+
mock_user_input.return_value = "y"
280+
281+
result = editor.editor(
282+
command="pattern_replace",
283+
path=temp_file,
284+
pattern="Line.*",
285+
new_str="Updated Line"
286+
)
287+
288+
assert result["status"] == "success"
289+
backup_path = f"{temp_file}.bak"
290+
assert not os.path.exists(backup_path)
291+
292+
@patch("strands_tools.editor.get_user_input")
293+
@patch.dict("os.environ", {"EDITOR_DISABLE_BACKUP": "true"})
294+
def test_insert_no_backup(self, mock_user_input, temp_file, clean_content_history):
295+
"""Test insert without creating backup."""
296+
mock_user_input.return_value = "y"
297+
298+
result = editor.editor(
299+
command="insert",
300+
path=temp_file,
301+
new_str="New line",
302+
insert_line=2
303+
)
304+
305+
assert result["status"] == "success"
306+
backup_path = f"{temp_file}.bak"
307+
assert not os.path.exists(backup_path)
308+
309+
@patch("strands_tools.editor.get_user_input")
310+
def test_backup_created_by_default(self, mock_user_input, temp_file, clean_content_history):
311+
"""Test that backup is still created by default."""
312+
# Ensure env var is not set
313+
if "EDITOR_DISABLE_BACKUP" in os.environ:
314+
del os.environ["EDITOR_DISABLE_BACKUP"]
315+
316+
mock_user_input.return_value = "y"
317+
318+
result = editor.editor(
319+
command="str_replace",
320+
path=temp_file,
321+
old_str="Line 2",
322+
new_str="Modified Line 2"
323+
)
324+
325+
assert result["status"] == "success"
326+
backup_path = f"{temp_file}.bak"
327+
assert os.path.exists(backup_path)
328+
256329
@patch("strands_tools.editor.get_user_input")
257330
def test_insert_with_search_text(self, mock_user_input, temp_file, clean_content_history):
258331
"""Test inserting text after a line found by search."""

0 commit comments

Comments
 (0)