janito 1.12.1__py3-none-any.whl → 1.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
janito/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.11.0"
1
+ __version__ = "1.13.0"
@@ -93,7 +93,7 @@ class ConversationHandler:
93
93
 
94
94
  def _handle_no_tool_support(self, messages, max_tokens, spinner):
95
95
  print(
96
- "\u26a0\ufe0f Endpoint does not support tool use. Proceeding in vanilla mode (tools disabled)."
96
+ "⚠️ Endpoint does not support tool use. Proceeding in vanilla mode (tools disabled)."
97
97
  )
98
98
  runtime_config.set("vanilla_mode", True)
99
99
  resolved_max_tokens = 8000
@@ -45,7 +45,9 @@ class FindFilesTool(ToolBase):
45
45
  dir_output.add(os.path.join(root, d))
46
46
  return dir_output
47
47
 
48
- def run(self, paths: str, pattern: str, max_depth: int = None) -> str:
48
+ def run(
49
+ self, paths: str, pattern: str, max_depth: int = None, max_results: int = 0
50
+ ) -> str:
49
51
  if not pattern:
50
52
  self.report_warning(tr("ℹ️ Empty file pattern provided."))
51
53
  return tr("Warning: Empty file pattern provided. Operation skipped.")
@@ -61,13 +63,15 @@ class FindFilesTool(ToolBase):
61
63
  self.report_info(
62
64
  ActionType.READ,
63
65
  tr(
64
- "🔍 Search for files '{pattern}' in '{disp_path}'{depth_msg} ...",
66
+ "🔍 Search files '{pattern}' in '{disp_path}'{depth_msg} ...",
65
67
  pattern=pattern,
66
68
  disp_path=disp_path,
67
69
  depth_msg=depth_msg,
68
70
  ),
69
71
  )
70
72
  dir_output = set()
73
+ count_scanned = 0
74
+ limit_reached = False
71
75
  for root, dirs, files in walk_dir_with_gitignore(
72
76
  directory, max_depth=max_depth
73
77
  ):
@@ -79,11 +83,17 @@ class FindFilesTool(ToolBase):
79
83
  dir_output.update(
80
84
  self._match_dirs_without_slash(root, dirs, pat)
81
85
  )
86
+ if max_results > 0 and len(dir_output) >= max_results:
87
+ limit_reached = True
88
+ # Truncate to max_results
89
+ dir_output = set(list(dir_output)[:max_results])
90
+ break
82
91
  self.report_success(
83
92
  tr(
84
- " ✅ {count} {file_word}",
93
+ " ✅ {count} {file_word}{max_flag}",
85
94
  count=len(dir_output),
86
95
  file_word=pluralize("file", len(dir_output)),
96
+ max_flag=" (max)" if limit_reached else "",
87
97
  )
88
98
  )
89
99
  if directory.strip() == ".":
@@ -22,7 +22,7 @@ class GetLinesTool(ToolBase):
22
22
  - "---\nFile: /path/to/file.py | Lines: 1-10 (of 100)\n---\n<lines...>"
23
23
  - "---\nFile: /path/to/file.py | All lines (total: 100 (all))\n---\n<all lines...>"
24
24
  - "Error reading file: <error message>"
25
- - "\u2757 not found"
25
+ - " not found"
26
26
  """
27
27
 
28
28
  def run(self, file_path: str, from_line: int = None, to_line: int = None) -> str:
@@ -83,7 +83,7 @@ class GetLinesTool(ToolBase):
83
83
  if at_end:
84
84
  self.report_success(
85
85
  tr(
86
- " \u2705 {selected_len} {line_word} (end)",
86
+ " {selected_len} {line_word} (end)",
87
87
  selected_len=selected_len,
88
88
  line_word=pluralize("line", selected_len),
89
89
  )
@@ -91,7 +91,7 @@ class GetLinesTool(ToolBase):
91
91
  elif to_line < total_lines:
92
92
  self.report_success(
93
93
  tr(
94
- " \u2705 {selected_len} {line_word} ({remaining} to end)",
94
+ " {selected_len} {line_word} ({remaining} to end)",
95
95
  selected_len=selected_len,
96
96
  line_word=pluralize("line", selected_len),
97
97
  remaining=total_lines - to_line,
@@ -100,7 +100,7 @@ class GetLinesTool(ToolBase):
100
100
  else:
101
101
  self.report_success(
102
102
  tr(
103
- " \u2705 {selected_len} {line_word} (all)",
103
+ " {selected_len} {line_word} (all)",
104
104
  selected_len=selected_len,
105
105
  line_word=pluralize("line", selected_len),
106
106
  )
@@ -143,7 +143,7 @@ class GetLinesTool(ToolBase):
143
143
  def _handle_read_error(self, e):
144
144
  """Handle file read errors and report appropriately."""
145
145
  if isinstance(e, FileNotFoundError):
146
- self.report_error(tr("\u2757 not found"))
147
- return tr("\u2757 not found")
148
- self.report_error(tr(" \u274c Error: {error}", error=e))
146
+ self.report_error(tr(" not found"))
147
+ return tr(" not found")
148
+ self.report_error(tr(" Error: {error}", error=e))
149
149
  return tr("Error reading file: {error}", error=e)
@@ -7,6 +7,7 @@ from janito.agent.tool_base import ToolBase
7
7
  from janito.agent.tools_utils.action_type import ActionType
8
8
  from janito.agent.tool_registry import register_tool
9
9
  from janito.i18n import tr
10
+ from janito.agent.runtime_config import runtime_config
10
11
 
11
12
 
12
13
  @register_tool(name="python_command_runner")
@@ -22,26 +23,13 @@ class PythonCommandRunnerTool(ToolBase):
22
23
 
23
24
  def run(self, code: str, timeout: int = 60) -> str:
24
25
  if not code.strip():
25
- self.report_warning(tr("\u2139\ufe0f Empty code provided."))
26
+ self.report_warning(tr("539 Empty code provided."))
26
27
  return tr("Warning: Empty code provided. Operation skipped.")
27
28
  self.report_info(
28
- ActionType.EXECUTE, tr("🐍 Running: python -c ...\n{code}\n", code=code)
29
+ ActionType.EXECUTE, tr("40d Running: python -c ...\n{code}\n", code=code)
29
30
  )
30
31
  try:
31
- with (
32
- tempfile.NamedTemporaryFile(
33
- mode="w+",
34
- prefix="python_cmd_stdout_",
35
- delete=False,
36
- encoding="utf-8",
37
- ) as stdout_file,
38
- tempfile.NamedTemporaryFile(
39
- mode="w+",
40
- prefix="python_cmd_stderr_",
41
- delete=False,
42
- encoding="utf-8",
43
- ) as stderr_file,
44
- ):
32
+ if runtime_config.get("all_out"):
45
33
  process = subprocess.Popen(
46
34
  [sys.executable, "-c", code],
47
35
  stdout=subprocess.PIPE,
@@ -52,24 +40,88 @@ class PythonCommandRunnerTool(ToolBase):
52
40
  encoding="utf-8",
53
41
  env={**os.environ, "PYTHONIOENCODING": "utf-8"},
54
42
  )
55
- stdout_lines, stderr_lines = self._stream_process_output(
56
- process, stdout_file, stderr_file
43
+ stdout_accum = []
44
+ stderr_accum = []
45
+
46
+ def read_stream(stream, report_func, accum):
47
+ for line in stream:
48
+ accum.append(line)
49
+ report_func(line)
50
+
51
+ stdout_thread = threading.Thread(
52
+ target=read_stream,
53
+ args=(process.stdout, self.report_stdout, stdout_accum),
54
+ )
55
+ stderr_thread = threading.Thread(
56
+ target=read_stream,
57
+ args=(process.stderr, self.report_stderr, stderr_accum),
57
58
  )
58
- return_code = self._wait_for_process(process, timeout)
59
- if return_code is None:
59
+ stdout_thread.start()
60
+ stderr_thread.start()
61
+ try:
62
+ return_code = process.wait(timeout=timeout)
63
+ except subprocess.TimeoutExpired:
64
+ process.kill()
65
+ self.report_error(
66
+ tr("6d1 Timed out after {timeout} seconds.", timeout=timeout)
67
+ )
60
68
  return tr(
61
69
  "Code timed out after {timeout} seconds.", timeout=timeout
62
70
  )
63
- stdout_file.flush()
64
- stderr_file.flush()
71
+ stdout_thread.join()
72
+ stderr_thread.join()
65
73
  self.report_success(
66
- tr("\u2705 Return code {return_code}", return_code=return_code)
67
- )
68
- return self._format_result(
69
- stdout_file.name, stderr_file.name, return_code
74
+ tr("197 Return code {return_code}", return_code=return_code)
70
75
  )
76
+ stdout = "".join(stdout_accum)
77
+ stderr = "".join(stderr_accum)
78
+ result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout}"
79
+ if stderr and stderr.strip():
80
+ result += f"\n--- STDERR ---\n{stderr}"
81
+ return result
82
+ else:
83
+ with (
84
+ tempfile.NamedTemporaryFile(
85
+ mode="w+",
86
+ prefix="python_cmd_stdout_",
87
+ delete=False,
88
+ encoding="utf-8",
89
+ ) as stdout_file,
90
+ tempfile.NamedTemporaryFile(
91
+ mode="w+",
92
+ prefix="python_cmd_stderr_",
93
+ delete=False,
94
+ encoding="utf-8",
95
+ ) as stderr_file,
96
+ ):
97
+ process = subprocess.Popen(
98
+ [sys.executable, "-c", code],
99
+ stdout=subprocess.PIPE,
100
+ stderr=subprocess.PIPE,
101
+ text=True,
102
+ bufsize=1,
103
+ universal_newlines=True,
104
+ encoding="utf-8",
105
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
106
+ )
107
+ stdout_lines, stderr_lines = self._stream_process_output(
108
+ process, stdout_file, stderr_file
109
+ )
110
+ return_code = self._wait_for_process(process, timeout)
111
+ if return_code is None:
112
+ return tr(
113
+ "Code timed out after {timeout} seconds.", timeout=timeout
114
+ )
115
+ stdout_file.flush()
116
+ stderr_file.flush()
117
+ self.report_success(
118
+ tr("197 Return code {return_code}", return_code=return_code)
119
+ )
120
+ return self._format_result(
121
+ stdout_file.name, stderr_file.name, return_code
122
+ )
71
123
  except Exception as e:
72
- self.report_error(tr("\u274c Error: {error}", error=e))
124
+ self.report_error(tr("534 Error: {error}", error=e))
73
125
  return tr("Error running code: {error}", error=e)
74
126
 
75
127
  def _stream_process_output(self, process, stdout_file, stderr_file):
@@ -107,7 +159,7 @@ class PythonCommandRunnerTool(ToolBase):
107
159
  except subprocess.TimeoutExpired:
108
160
  process.kill()
109
161
  self.report_error(
110
- tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
162
+ tr("6d1 Timed out after {timeout} seconds.", timeout=timeout)
111
163
  )
112
164
  return None
113
165
 
@@ -7,6 +7,7 @@ from janito.agent.tool_base import ToolBase
7
7
  from janito.agent.tools_utils.action_type import ActionType
8
8
  from janito.agent.tool_registry import register_tool
9
9
  from janito.i18n import tr
10
+ from janito.agent.runtime_config import runtime_config
10
11
 
11
12
 
12
13
  @register_tool(name="python_file_runner")
@@ -23,23 +24,10 @@ class PythonFileRunnerTool(ToolBase):
23
24
  def run(self, file_path: str, timeout: int = 60) -> str:
24
25
  self.report_info(
25
26
  ActionType.EXECUTE,
26
- tr("🚀 Running: python {file_path}", file_path=file_path),
27
+ tr("680 Running: python {file_path}", file_path=file_path),
27
28
  )
28
29
  try:
29
- with (
30
- tempfile.NamedTemporaryFile(
31
- mode="w+",
32
- prefix="python_file_stdout_",
33
- delete=False,
34
- encoding="utf-8",
35
- ) as stdout_file,
36
- tempfile.NamedTemporaryFile(
37
- mode="w+",
38
- prefix="python_file_stderr_",
39
- delete=False,
40
- encoding="utf-8",
41
- ) as stderr_file,
42
- ):
30
+ if runtime_config.get("all_out"):
43
31
  process = subprocess.Popen(
44
32
  [sys.executable, file_path],
45
33
  stdout=subprocess.PIPE,
@@ -50,24 +38,88 @@ class PythonFileRunnerTool(ToolBase):
50
38
  encoding="utf-8",
51
39
  env={**os.environ, "PYTHONIOENCODING": "utf-8"},
52
40
  )
53
- stdout_lines, stderr_lines = self._stream_process_output(
54
- process, stdout_file, stderr_file
41
+ stdout_accum = []
42
+ stderr_accum = []
43
+
44
+ def read_stream(stream, report_func, accum):
45
+ for line in stream:
46
+ accum.append(line)
47
+ report_func(line)
48
+
49
+ stdout_thread = threading.Thread(
50
+ target=read_stream,
51
+ args=(process.stdout, self.report_stdout, stdout_accum),
52
+ )
53
+ stderr_thread = threading.Thread(
54
+ target=read_stream,
55
+ args=(process.stderr, self.report_stderr, stderr_accum),
55
56
  )
56
- return_code = self._wait_for_process(process, timeout)
57
- if return_code is None:
57
+ stdout_thread.start()
58
+ stderr_thread.start()
59
+ try:
60
+ return_code = process.wait(timeout=timeout)
61
+ except subprocess.TimeoutExpired:
62
+ process.kill()
63
+ self.report_error(
64
+ tr("6d1 Timed out after {timeout} seconds.", timeout=timeout)
65
+ )
58
66
  return tr(
59
67
  "Code timed out after {timeout} seconds.", timeout=timeout
60
68
  )
61
- stdout_file.flush()
62
- stderr_file.flush()
69
+ stdout_thread.join()
70
+ stderr_thread.join()
63
71
  self.report_success(
64
- tr("\u2705 Return code {return_code}", return_code=return_code)
65
- )
66
- return self._format_result(
67
- stdout_file.name, stderr_file.name, return_code
72
+ tr("197 Return code {return_code}", return_code=return_code)
68
73
  )
74
+ stdout = "".join(stdout_accum)
75
+ stderr = "".join(stderr_accum)
76
+ result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout}"
77
+ if stderr and stderr.strip():
78
+ result += f"\n--- STDERR ---\n{stderr}"
79
+ return result
80
+ else:
81
+ with (
82
+ tempfile.NamedTemporaryFile(
83
+ mode="w+",
84
+ prefix="python_file_stdout_",
85
+ delete=False,
86
+ encoding="utf-8",
87
+ ) as stdout_file,
88
+ tempfile.NamedTemporaryFile(
89
+ mode="w+",
90
+ prefix="python_file_stderr_",
91
+ delete=False,
92
+ encoding="utf-8",
93
+ ) as stderr_file,
94
+ ):
95
+ process = subprocess.Popen(
96
+ [sys.executable, file_path],
97
+ stdout=subprocess.PIPE,
98
+ stderr=subprocess.PIPE,
99
+ text=True,
100
+ bufsize=1,
101
+ universal_newlines=True,
102
+ encoding="utf-8",
103
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
104
+ )
105
+ stdout_lines, stderr_lines = self._stream_process_output(
106
+ process, stdout_file, stderr_file
107
+ )
108
+ return_code = self._wait_for_process(process, timeout)
109
+ if return_code is None:
110
+ return tr(
111
+ "Code timed out after {timeout} seconds.", timeout=timeout
112
+ )
113
+ stdout_file.flush()
114
+ stderr_file.flush()
115
+ self.report_success(
116
+ tr("197 Return code {return_code}", return_code=return_code)
117
+ )
118
+ return self._format_result(
119
+ stdout_file.name, stderr_file.name, return_code
120
+ )
69
121
  except Exception as e:
70
- self.report_error(tr("\u274c Error: {error}", error=e))
122
+ self.report_error(tr("534 Error: {error}", error=e))
71
123
  return tr("Error running file: {error}", error=e)
72
124
 
73
125
  def _stream_process_output(self, process, stdout_file, stderr_file):
@@ -105,7 +157,7 @@ class PythonFileRunnerTool(ToolBase):
105
157
  except subprocess.TimeoutExpired:
106
158
  process.kill()
107
159
  self.report_error(
108
- tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
160
+ tr("6d1 Timed out after {timeout} seconds.", timeout=timeout)
109
161
  )
110
162
  return None
111
163
 
@@ -7,6 +7,7 @@ from janito.agent.tool_base import ToolBase
7
7
  from janito.agent.tools_utils.action_type import ActionType
8
8
  from janito.agent.tool_registry import register_tool
9
9
  from janito.i18n import tr
10
+ from janito.agent.runtime_config import runtime_config
10
11
 
11
12
 
12
13
  @register_tool(name="python_stdin_runner")
@@ -22,27 +23,14 @@ class PythonStdinRunnerTool(ToolBase):
22
23
 
23
24
  def run(self, code: str, timeout: int = 60) -> str:
24
25
  if not code.strip():
25
- self.report_warning(tr("\u2139\ufe0f Empty code provided."))
26
+ self.report_warning(tr("ℹ️ Empty code provided."))
26
27
  return tr("Warning: Empty code provided. Operation skipped.")
27
28
  self.report_info(
28
29
  ActionType.EXECUTE,
29
- tr(" Running: python (stdin mode) ...\n{code}\n", code=code),
30
+ tr("5e1 Running: python (stdin mode) ...\n{code}\n", code=code),
30
31
  )
31
32
  try:
32
- with (
33
- tempfile.NamedTemporaryFile(
34
- mode="w+",
35
- prefix="python_stdin_stdout_",
36
- delete=False,
37
- encoding="utf-8",
38
- ) as stdout_file,
39
- tempfile.NamedTemporaryFile(
40
- mode="w+",
41
- prefix="python_stdin_stderr_",
42
- delete=False,
43
- encoding="utf-8",
44
- ) as stderr_file,
45
- ):
33
+ if runtime_config.get("all_out"):
46
34
  process = subprocess.Popen(
47
35
  [sys.executable],
48
36
  stdin=subprocess.PIPE,
@@ -54,24 +42,91 @@ class PythonStdinRunnerTool(ToolBase):
54
42
  encoding="utf-8",
55
43
  env={**os.environ, "PYTHONIOENCODING": "utf-8"},
56
44
  )
57
- stdout_lines, stderr_lines = self._stream_process_output(
58
- process, stdout_file, stderr_file, code
45
+ stdout_accum = []
46
+ stderr_accum = []
47
+
48
+ def read_stream(stream, report_func, accum):
49
+ for line in stream:
50
+ accum.append(line)
51
+ report_func(line)
52
+
53
+ stdout_thread = threading.Thread(
54
+ target=read_stream,
55
+ args=(process.stdout, self.report_stdout, stdout_accum),
56
+ )
57
+ stderr_thread = threading.Thread(
58
+ target=read_stream,
59
+ args=(process.stderr, self.report_stderr, stderr_accum),
59
60
  )
60
- return_code = self._wait_for_process(process, timeout)
61
- if return_code is None:
61
+ stdout_thread.start()
62
+ stderr_thread.start()
63
+ process.stdin.write(code)
64
+ process.stdin.close()
65
+ try:
66
+ return_code = process.wait(timeout=timeout)
67
+ except subprocess.TimeoutExpired:
68
+ process.kill()
69
+ self.report_error(
70
+ tr("6d1 Timed out after {timeout} seconds.", timeout=timeout)
71
+ )
62
72
  return tr(
63
73
  "Code timed out after {timeout} seconds.", timeout=timeout
64
74
  )
65
- stdout_file.flush()
66
- stderr_file.flush()
75
+ stdout_thread.join()
76
+ stderr_thread.join()
67
77
  self.report_success(
68
- tr("\u2705 Return code {return_code}", return_code=return_code)
69
- )
70
- return self._format_result(
71
- stdout_file.name, stderr_file.name, return_code
78
+ tr("197 Return code {return_code}", return_code=return_code)
72
79
  )
80
+ stdout = "".join(stdout_accum)
81
+ stderr = "".join(stderr_accum)
82
+ result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout}"
83
+ if stderr and stderr.strip():
84
+ result += f"\n--- STDERR ---\n{stderr}"
85
+ return result
86
+ else:
87
+ with (
88
+ tempfile.NamedTemporaryFile(
89
+ mode="w+",
90
+ prefix="python_stdin_stdout_",
91
+ delete=False,
92
+ encoding="utf-8",
93
+ ) as stdout_file,
94
+ tempfile.NamedTemporaryFile(
95
+ mode="w+",
96
+ prefix="python_stdin_stderr_",
97
+ delete=False,
98
+ encoding="utf-8",
99
+ ) as stderr_file,
100
+ ):
101
+ process = subprocess.Popen(
102
+ [sys.executable],
103
+ stdin=subprocess.PIPE,
104
+ stdout=subprocess.PIPE,
105
+ stderr=subprocess.PIPE,
106
+ text=True,
107
+ bufsize=1,
108
+ universal_newlines=True,
109
+ encoding="utf-8",
110
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
111
+ )
112
+ stdout_lines, stderr_lines = self._stream_process_output(
113
+ process, stdout_file, stderr_file, code
114
+ )
115
+ return_code = self._wait_for_process(process, timeout)
116
+ if return_code is None:
117
+ return tr(
118
+ "Code timed out after {timeout} seconds.", timeout=timeout
119
+ )
120
+ stdout_file.flush()
121
+ stderr_file.flush()
122
+ self.report_success(
123
+ tr("197 Return code {return_code}", return_code=return_code)
124
+ )
125
+ return self._format_result(
126
+ stdout_file.name, stderr_file.name, return_code
127
+ )
73
128
  except Exception as e:
74
- self.report_error(tr("\u274c Error: {error}", error=e))
129
+ self.report_error(tr("534 Error: {error}", error=e))
75
130
  return tr("Error running code via stdin: {error}", error=e)
76
131
 
77
132
  def _stream_process_output(self, process, stdout_file, stderr_file, code):
@@ -111,7 +166,7 @@ class PythonStdinRunnerTool(ToolBase):
111
166
  except subprocess.TimeoutExpired:
112
167
  process.kill()
113
168
  self.report_error(
114
- tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
169
+ tr("6d1 Timed out after {timeout} seconds.", timeout=timeout)
115
170
  )
116
171
  return None
117
172
 
@@ -5,6 +5,8 @@ from janito.i18n import tr
5
5
  import shutil
6
6
  import re
7
7
 
8
+ from janito.agent.tools.validate_file_syntax.core import validate_file_syntax
9
+
8
10
 
9
11
  @register_tool(name="replace_text_in_file")
10
12
  class ReplaceTextInFileTool(ToolBase):
@@ -82,11 +84,16 @@ class ReplaceTextInFileTool(ToolBase):
82
84
  line_delta_str,
83
85
  replace_all,
84
86
  )
85
- return self._format_final_msg(
87
+ final_msg = self._format_final_msg(
86
88
  file_path, warning, backup_path, match_info, details
87
89
  )
90
+ # Perform syntax validation and append result if file was changed
91
+ if file_changed:
92
+ validation_result = validate_file_syntax(file_path)
93
+ final_msg += f"\n{validation_result}"
94
+ return final_msg
88
95
  except Exception as e:
89
- self.report_error(tr(" \u274c Error"))
96
+ self.report_error(tr(" Error"))
90
97
  return tr("Error replacing text: {error}", error=e)
91
98
 
92
99
  def _read_file_content(self, file_path):
@@ -140,12 +147,12 @@ class ReplaceTextInFileTool(ToolBase):
140
147
  if replaced_count == 0:
141
148
  warning = tr(" [Warning: Search text not found in file]")
142
149
  if not file_changed:
143
- self.report_warning(tr(" \u2139\ufe0f No changes made. [not found]"))
150
+ self.report_warning(tr(" ℹ️ No changes made. [not found]"))
144
151
  concise_warning = tr(
145
152
  "No changes made. The search text was not found. Expand your search context with surrounding lines if needed."
146
153
  )
147
154
  if occurrences > 1 and replaced_count == 0:
148
- self.report_warning(tr(" \u2139\ufe0f No changes made. [not unique]"))
155
+ self.report_warning(tr(" ℹ️ No changes made. [not unique]"))
149
156
  concise_warning = tr(
150
157
  "No changes made. The search text is not unique. Expand your search context with surrounding lines to ensure uniqueness."
151
158
  )
@@ -155,11 +162,9 @@ class ReplaceTextInFileTool(ToolBase):
155
162
  """Report success with line numbers where replacements occurred."""
156
163
  if match_lines:
157
164
  lines_str = ", ".join(str(line_no) for line_no in match_lines)
158
- self.report_success(
159
- tr(" \u2705 replaced at {lines_str}", lines_str=lines_str)
160
- )
165
+ self.report_success(tr(" ✅ replaced at {lines_str}", lines_str=lines_str))
161
166
  else:
162
- self.report_success(tr(" \u2705 replaced (lines unknown)"))
167
+ self.report_success(tr(" replaced (lines unknown)"))
163
168
 
164
169
  def _get_line_delta_str(self, content, new_content):
165
170
  """Return a string describing the net line change after replacement."""