From ef26a6464d3b0f54124652f78a08acc84e35627c Mon Sep 17 00:00:00 2001 From: Jessica Mulein Date: Sat, 6 Jun 2026 09:55:29 -0700 Subject: [PATCH] fix(tools): steer ReadRange away from empty-file read loops Rebased onto v0.100.5 validation pipeline (#557). Route empty-file guidance through new_context_details so the model sees it. Hint EditText @000 or ContextManager instead of re-reading empty files. Co-authored-by: Cursor --- cecli/tools/read_range.py | 19 ++++++++++++++----- tests/tools/test_get_lines.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/cecli/tools/read_range.py b/cecli/tools/read_range.py index 07f1bb86c27..7df1cdbb5bc 100644 --- a/cecli/tools/read_range.py +++ b/cecli/tools/read_range.py @@ -194,11 +194,20 @@ def execute(cls, coder, show, **kwargs): num_lines = len(lines) if num_lines == 0: - # Handle empty file case - output_lines = [f"File {rel_path} is empty."] - if show_index > 0: - all_outputs.append("") - all_outputs.extend(output_lines) + new_context_details.append( + "\n".join( + [ + f"File {rel_path} is empty.", + ( + "Next: use EditText with start_line @000 and end_line @000 to" + " write content, or ContextManager to scaffold — do not call" + " ReadRange again on this empty file." + ), + ] + ) + ) + new_context_retrieved.append(rel_path) + cls._last_read_turn[abs_path] = coder.turn_count continue # 4. Determine line range start_line_idx = -1 diff --git a/tests/tools/test_get_lines.py b/tests/tools/test_get_lines.py index 8c7fd7705b3..1bbeb0d3b6c 100644 --- a/tests/tools/test_get_lines.py +++ b/tests/tools/test_get_lines.py @@ -142,3 +142,31 @@ def test_multiline_pattern_search(coder_with_file): assert "Retrieved context for 1 operation(s)" in result coder.io.tool_error.assert_not_called() + + +def test_empty_file_includes_edit_hint(tmp_path): + empty = tmp_path / "pubspec.yaml" + empty.write_text("") + coder = DummyCoder(tmp_path) + + from unittest.mock import patch + + with patch("cecli.helpers.conversation.ConversationService") as conv: + conv.get_files.return_value.clear_ranges = Mock() + conv.get_files.return_value.push_range = Mock() + conv.get_chunks.return_value.add_file_context_messages = Mock() + result = read_range.Tool.execute( + coder, + show=[ + { + "file_path": "pubspec.yaml", + "start_text": "@000", + "end_text": "@000", + } + ], + ) + + assert "pubspec.yaml is empty" in result + assert "EditText" in result + assert "readrange again" in result.lower() + coder.io.tool_error.assert_not_called()