Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ The Mem0 Memory Tool supports three different backend configurations:
| EDITOR_DIR_TREE_MAX_DEPTH | Maximum depth for directory tree visualization | 2 |
| EDITOR_DEFAULT_STYLE | Default style for output panels | default |
| EDITOR_DEFAULT_LANGUAGE | Default language for syntax highlighting | python |
| EDITOR_DISABLE_BACKUP | Skip creating .bak backup files during edit operations | false |

#### Environment Tool

Expand Down
20 changes: 14 additions & 6 deletions src/strands_tools/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,10 @@ def editor(

# Make replacements and backup
new_content = content.replace(old_str, new_str)
backup_path = f"{path}.bak"
shutil.copy2(path, backup_path)
disable_backup = os.environ.get("EDITOR_DISABLE_BACKUP", "").lower() == "true"
if not disable_backup:
backup_path = f"{path}.bak"
shutil.copy2(path, backup_path)

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

# Make replacements and backup
new_content = regex.sub(new_str, content)
backup_path = f"{path}.bak"
shutil.copy2(path, backup_path)
disable_backup = os.environ.get("EDITOR_DISABLE_BACKUP", "").lower() == "true"
if not disable_backup:
backup_path = f"{path}.bak"
shutil.copy2(path, backup_path)
else:
backup_path = "Disabled"

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

# Make backup
backup_path = f"{path}.bak"
shutil.copy2(path, backup_path)
disable_backup = os.environ.get("EDITOR_DISABLE_BACKUP", "").lower() == "true"
if not disable_backup:
backup_path = f"{path}.bak"
shutil.copy2(path, backup_path)

# Insert and write
lines.insert(insert_line, new_str)
Expand Down
73 changes: 73 additions & 0 deletions tests/test_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,79 @@ def test_insert_command(self, mock_user_input, temp_file, clean_content_history)
# changing the original file structure
assert "Line 2\nINSERTED LINE\nLine 3\n" in content

@patch("strands_tools.editor.get_user_input")
@patch.dict("os.environ", {"EDITOR_DISABLE_BACKUP": "true"})
def test_str_replace_no_backup(self, mock_user_input, temp_file, clean_content_history):
"""Test str_replace without creating backup when EDITOR_DISABLE_BACKUP is set."""
mock_user_input.return_value = "y"

result = editor.editor(
command="str_replace",
path=temp_file,
old_str="Line 2",
new_str="Modified Line 2"
)

assert result["status"] == "success"

# Verify backup was NOT created
backup_path = f"{temp_file}.bak"
assert not os.path.exists(backup_path)

@patch("strands_tools.editor.get_user_input")
@patch.dict("os.environ", {"EDITOR_DISABLE_BACKUP": "true"})
def test_pattern_replace_no_backup(self, mock_user_input, temp_file, clean_content_history):
"""Test pattern_replace without creating backup."""
mock_user_input.return_value = "y"

result = editor.editor(
command="pattern_replace",
path=temp_file,
pattern="Line.*",
new_str="Updated Line"
)

assert result["status"] == "success"
backup_path = f"{temp_file}.bak"
assert not os.path.exists(backup_path)

@patch("strands_tools.editor.get_user_input")
@patch.dict("os.environ", {"EDITOR_DISABLE_BACKUP": "true"})
def test_insert_no_backup(self, mock_user_input, temp_file, clean_content_history):
"""Test insert without creating backup."""
mock_user_input.return_value = "y"

result = editor.editor(
command="insert",
path=temp_file,
new_str="New line",
insert_line=2
)

assert result["status"] == "success"
backup_path = f"{temp_file}.bak"
assert not os.path.exists(backup_path)

@patch("strands_tools.editor.get_user_input")
def test_backup_created_by_default(self, mock_user_input, temp_file, clean_content_history):
"""Test that backup is still created by default."""
# Ensure env var is not set
if "EDITOR_DISABLE_BACKUP" in os.environ:
del os.environ["EDITOR_DISABLE_BACKUP"]

mock_user_input.return_value = "y"

result = editor.editor(
command="str_replace",
path=temp_file,
old_str="Line 2",
new_str="Modified Line 2"
)

assert result["status"] == "success"
backup_path = f"{temp_file}.bak"
assert os.path.exists(backup_path)

@patch("strands_tools.editor.get_user_input")
def test_insert_with_search_text(self, mock_user_input, temp_file, clean_content_history):
"""Test inserting text after a line found by search."""
Expand Down
Loading