janito 1.10.0__py3-none-any.whl → 1.11.1__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.
Files changed (57) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/conversation_api.py +178 -90
  3. janito/agent/conversation_ui.py +1 -1
  4. janito/agent/llm_conversation_history.py +12 -0
  5. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +19 -4
  6. janito/agent/tools/__init__.py +2 -0
  7. janito/agent/tools/create_directory.py +1 -1
  8. janito/agent/tools/create_file.py +1 -1
  9. janito/agent/tools/fetch_url.py +1 -1
  10. janito/agent/tools/find_files.py +26 -13
  11. janito/agent/tools/get_file_outline/core.py +1 -1
  12. janito/agent/tools/get_file_outline/python_outline.py +139 -95
  13. janito/agent/tools/get_lines.py +92 -63
  14. janito/agent/tools/move_file.py +58 -32
  15. janito/agent/tools/open_url.py +31 -0
  16. janito/agent/tools/python_command_runner.py +85 -86
  17. janito/agent/tools/python_file_runner.py +85 -86
  18. janito/agent/tools/python_stdin_runner.py +87 -88
  19. janito/agent/tools/remove_directory.py +1 -1
  20. janito/agent/tools/remove_file.py +1 -1
  21. janito/agent/tools/replace_file.py +2 -2
  22. janito/agent/tools/replace_text_in_file.py +193 -149
  23. janito/agent/tools/run_bash_command.py +1 -1
  24. janito/agent/tools/run_powershell_command.py +4 -0
  25. janito/agent/tools/search_text/__init__.py +1 -0
  26. janito/agent/tools/search_text/core.py +176 -0
  27. janito/agent/tools/search_text/match_lines.py +58 -0
  28. janito/agent/tools/search_text/pattern_utils.py +65 -0
  29. janito/agent/tools/search_text/traverse_directory.py +132 -0
  30. janito/agent/tools/validate_file_syntax/core.py +41 -30
  31. janito/agent/tools/validate_file_syntax/html_validator.py +21 -5
  32. janito/agent/tools/validate_file_syntax/markdown_validator.py +77 -34
  33. janito/agent/tools_utils/gitignore_utils.py +25 -2
  34. janito/agent/tools_utils/utils.py +7 -1
  35. janito/cli/config_commands.py +112 -109
  36. janito/shell/main.py +51 -8
  37. janito/shell/session/config.py +83 -75
  38. janito/shell/ui/interactive.py +97 -73
  39. janito/termweb/static/editor.css +32 -29
  40. janito/termweb/static/editor.css.bak +140 -22
  41. janito/termweb/static/editor.html +12 -7
  42. janito/termweb/static/editor.html.bak +16 -11
  43. janito/termweb/static/editor.js +94 -40
  44. janito/termweb/static/editor.js.bak +97 -65
  45. janito/termweb/static/index.html +1 -2
  46. janito/termweb/static/index.html.bak +1 -1
  47. janito/termweb/static/termweb.css +1 -22
  48. janito/termweb/static/termweb.css.bak +6 -4
  49. janito/termweb/static/termweb.js +0 -6
  50. janito/termweb/static/termweb.js.bak +1 -2
  51. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/METADATA +1 -1
  52. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/RECORD +56 -51
  53. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/WHEEL +1 -1
  54. janito/agent/tools/search_text.py +0 -254
  55. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/entry_points.txt +0 -0
  56. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/licenses/LICENSE +0 -0
  57. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import subprocess
2
+ import os
2
3
  import sys
3
4
  import tempfile
4
5
  import threading
@@ -49,102 +50,100 @@ class PythonCommandRunnerTool(ToolBase):
49
50
  bufsize=1,
50
51
  universal_newlines=True,
51
52
  encoding="utf-8",
52
- env={**dict(), **dict(PYTHONIOENCODING="utf-8")},
53
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
53
54
  )
54
- stdout_lines = 0
55
- stderr_lines = 0
56
-
57
- def stream_output(stream, file_obj, report_func, count_func):
58
- nonlocal stdout_lines, stderr_lines
59
- for line in stream:
60
- file_obj.write(line)
61
- file_obj.flush()
62
- report_func(line)
63
- if count_func == "stdout":
64
- stdout_lines += 1
65
- else:
66
- stderr_lines += 1
67
-
68
- stdout_thread = threading.Thread(
69
- target=stream_output,
70
- args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
55
+ stdout_lines, stderr_lines = self._stream_process_output(
56
+ process, stdout_file, stderr_file
71
57
  )
72
- stderr_thread = threading.Thread(
73
- target=stream_output,
74
- args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
75
- )
76
- stdout_thread.start()
77
- stderr_thread.start()
78
- try:
79
- return_code = process.wait(timeout=timeout)
80
- except subprocess.TimeoutExpired:
81
- process.kill()
82
- self.report_error(
83
- tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
84
- )
58
+ return_code = self._wait_for_process(process, timeout)
59
+ if return_code is None:
85
60
  return tr(
86
61
  "Code timed out after {timeout} seconds.", timeout=timeout
87
62
  )
88
- stdout_thread.join()
89
- stderr_thread.join()
90
63
  stdout_file.flush()
91
64
  stderr_file.flush()
92
-
93
65
  self.report_success(
94
66
  tr("\u2705 Return code {return_code}", return_code=return_code)
95
67
  )
96
- # Read back the content for summary if not too large
97
- with open(
98
- stdout_file.name, "r", encoding="utf-8", errors="replace"
99
- ) as out_f:
100
- stdout_content = out_f.read()
101
- with open(
102
- stderr_file.name, "r", encoding="utf-8", errors="replace"
103
- ) as err_f:
104
- stderr_content = err_f.read()
105
- max_lines = 100
106
- stdout_lines = stdout_content.count("\n")
107
- stderr_lines = stderr_content.count("\n")
108
-
109
- def head_tail(text, n=10):
110
- lines = text.splitlines()
111
- if len(lines) <= 2 * n:
112
- return "\n".join(lines)
113
- return "\n".join(
114
- lines[:n]
115
- + ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
116
- + lines[-n:]
117
- )
118
-
119
- if stdout_lines <= max_lines and stderr_lines <= max_lines:
120
- result = (
121
- f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
122
- )
123
- if stderr_content.strip():
124
- result += f"\n--- STDERR ---\n{stderr_content}"
125
- return result
126
- else:
127
- result = (
128
- f"stdout_file: {stdout_file.name} (lines: {stdout_lines})\n"
129
- )
130
- if stderr_lines > 0 and stderr_content.strip():
131
- result += (
132
- f"stderr_file: {stderr_file.name} (lines: {stderr_lines})\n"
133
- )
134
- result += f"returncode: {return_code}\n"
135
- result += (
136
- "--- STDOUT (head/tail) ---\n"
137
- + head_tail(stdout_content)
138
- + "\n"
139
- )
140
- if stderr_content.strip():
141
- result += (
142
- "--- STDERR (head/tail) ---\n"
143
- + head_tail(stderr_content)
144
- + "\n"
145
- )
146
- result += "Use the get_lines tool to inspect the contents of these files when needed."
147
- return result
68
+ return self._format_result(
69
+ stdout_file.name, stderr_file.name, return_code
70
+ )
148
71
  except Exception as e:
149
72
  self.report_error(tr("\u274c Error: {error}", error=e))
150
73
  return tr("Error running code: {error}", error=e)
74
+
75
+ def _stream_process_output(self, process, stdout_file, stderr_file):
76
+ stdout_lines = 0
77
+ stderr_lines = 0
78
+
79
+ def stream_output(stream, file_obj, report_func, count_func):
80
+ nonlocal stdout_lines, stderr_lines
81
+ for line in stream:
82
+ file_obj.write(line)
83
+ file_obj.flush()
84
+ report_func(line)
85
+ if count_func == "stdout":
86
+ stdout_lines += 1
87
+ else:
88
+ stderr_lines += 1
89
+
90
+ stdout_thread = threading.Thread(
91
+ target=stream_output,
92
+ args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
93
+ )
94
+ stderr_thread = threading.Thread(
95
+ target=stream_output,
96
+ args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
97
+ )
98
+ stdout_thread.start()
99
+ stderr_thread.start()
100
+ stdout_thread.join()
101
+ stderr_thread.join()
102
+ return stdout_lines, stderr_lines
103
+
104
+ def _wait_for_process(self, process, timeout):
105
+ try:
106
+ return process.wait(timeout=timeout)
107
+ except subprocess.TimeoutExpired:
108
+ process.kill()
109
+ self.report_error(
110
+ tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
111
+ )
112
+ return None
113
+
114
+ def _format_result(self, stdout_file_name, stderr_file_name, return_code):
115
+ with open(stdout_file_name, "r", encoding="utf-8", errors="replace") as out_f:
116
+ stdout_content = out_f.read()
117
+ with open(stderr_file_name, "r", encoding="utf-8", errors="replace") as err_f:
118
+ stderr_content = err_f.read()
119
+ max_lines = 100
120
+ stdout_lines = stdout_content.count("\n")
121
+ stderr_lines = stderr_content.count("\n")
122
+
123
+ def head_tail(text, n=10):
124
+ lines = text.splitlines()
125
+ if len(lines) <= 2 * n:
126
+ return "\n".join(lines)
127
+ return "\n".join(
128
+ lines[:n]
129
+ + ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
130
+ + lines[-n:]
131
+ )
132
+
133
+ if stdout_lines <= max_lines and stderr_lines <= max_lines:
134
+ result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
135
+ if stderr_content.strip():
136
+ result += f"\n--- STDERR ---\n{stderr_content}"
137
+ return result
138
+ else:
139
+ result = f"stdout_file: {stdout_file_name} (lines: {stdout_lines})\n"
140
+ if stderr_lines > 0 and stderr_content.strip():
141
+ result += f"stderr_file: {stderr_file_name} (lines: {stderr_lines})\n"
142
+ result += f"returncode: {return_code}\n"
143
+ result += "--- STDOUT (head/tail) ---\n" + head_tail(stdout_content) + "\n"
144
+ if stderr_content.strip():
145
+ result += (
146
+ "--- STDERR (head/tail) ---\n" + head_tail(stderr_content) + "\n"
147
+ )
148
+ result += "Use the get_lines tool to inspect the contents of these files when needed."
149
+ return result
@@ -1,4 +1,5 @@
1
1
  import subprocess
2
+ import os
2
3
  import sys
3
4
  import tempfile
4
5
  import threading
@@ -47,102 +48,100 @@ class PythonFileRunnerTool(ToolBase):
47
48
  bufsize=1,
48
49
  universal_newlines=True,
49
50
  encoding="utf-8",
50
- env={**dict(), **dict(PYTHONIOENCODING="utf-8")},
51
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
51
52
  )
52
- stdout_lines = 0
53
- stderr_lines = 0
54
-
55
- def stream_output(stream, file_obj, report_func, count_func):
56
- nonlocal stdout_lines, stderr_lines
57
- for line in stream:
58
- file_obj.write(line)
59
- file_obj.flush()
60
- report_func(line)
61
- if count_func == "stdout":
62
- stdout_lines += 1
63
- else:
64
- stderr_lines += 1
65
-
66
- stdout_thread = threading.Thread(
67
- target=stream_output,
68
- args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
53
+ stdout_lines, stderr_lines = self._stream_process_output(
54
+ process, stdout_file, stderr_file
69
55
  )
70
- stderr_thread = threading.Thread(
71
- target=stream_output,
72
- args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
73
- )
74
- stdout_thread.start()
75
- stderr_thread.start()
76
- try:
77
- return_code = process.wait(timeout=timeout)
78
- except subprocess.TimeoutExpired:
79
- process.kill()
80
- self.report_error(
81
- tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
82
- )
56
+ return_code = self._wait_for_process(process, timeout)
57
+ if return_code is None:
83
58
  return tr(
84
59
  "Code timed out after {timeout} seconds.", timeout=timeout
85
60
  )
86
- stdout_thread.join()
87
- stderr_thread.join()
88
61
  stdout_file.flush()
89
62
  stderr_file.flush()
90
-
91
63
  self.report_success(
92
64
  tr("\u2705 Return code {return_code}", return_code=return_code)
93
65
  )
94
- # Read back the content for summary if not too large
95
- with open(
96
- stdout_file.name, "r", encoding="utf-8", errors="replace"
97
- ) as out_f:
98
- stdout_content = out_f.read()
99
- with open(
100
- stderr_file.name, "r", encoding="utf-8", errors="replace"
101
- ) as err_f:
102
- stderr_content = err_f.read()
103
- max_lines = 100
104
- stdout_lines = stdout_content.count("\n")
105
- stderr_lines = stderr_content.count("\n")
106
-
107
- def head_tail(text, n=10):
108
- lines = text.splitlines()
109
- if len(lines) <= 2 * n:
110
- return "\n".join(lines)
111
- return "\n".join(
112
- lines[:n]
113
- + ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
114
- + lines[-n:]
115
- )
116
-
117
- if stdout_lines <= max_lines and stderr_lines <= max_lines:
118
- result = (
119
- f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
120
- )
121
- if stderr_content.strip():
122
- result += f"\n--- STDERR ---\n{stderr_content}"
123
- return result
124
- else:
125
- result = (
126
- f"stdout_file: {stdout_file.name} (lines: {stdout_lines})\n"
127
- )
128
- if stderr_lines > 0 and stderr_content.strip():
129
- result += (
130
- f"stderr_file: {stderr_file.name} (lines: {stderr_lines})\n"
131
- )
132
- result += f"returncode: {return_code}\n"
133
- result += (
134
- "--- STDOUT (head/tail) ---\n"
135
- + head_tail(stdout_content)
136
- + "\n"
137
- )
138
- if stderr_content.strip():
139
- result += (
140
- "--- STDERR (head/tail) ---\n"
141
- + head_tail(stderr_content)
142
- + "\n"
143
- )
144
- result += "Use the get_lines tool to inspect the contents of these files when needed."
145
- return result
66
+ return self._format_result(
67
+ stdout_file.name, stderr_file.name, return_code
68
+ )
146
69
  except Exception as e:
147
70
  self.report_error(tr("\u274c Error: {error}", error=e))
148
71
  return tr("Error running file: {error}", error=e)
72
+
73
+ def _stream_process_output(self, process, stdout_file, stderr_file):
74
+ stdout_lines = 0
75
+ stderr_lines = 0
76
+
77
+ def stream_output(stream, file_obj, report_func, count_func):
78
+ nonlocal stdout_lines, stderr_lines
79
+ for line in stream:
80
+ file_obj.write(line)
81
+ file_obj.flush()
82
+ report_func(line)
83
+ if count_func == "stdout":
84
+ stdout_lines += 1
85
+ else:
86
+ stderr_lines += 1
87
+
88
+ stdout_thread = threading.Thread(
89
+ target=stream_output,
90
+ args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
91
+ )
92
+ stderr_thread = threading.Thread(
93
+ target=stream_output,
94
+ args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
95
+ )
96
+ stdout_thread.start()
97
+ stderr_thread.start()
98
+ stdout_thread.join()
99
+ stderr_thread.join()
100
+ return stdout_lines, stderr_lines
101
+
102
+ def _wait_for_process(self, process, timeout):
103
+ try:
104
+ return process.wait(timeout=timeout)
105
+ except subprocess.TimeoutExpired:
106
+ process.kill()
107
+ self.report_error(
108
+ tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
109
+ )
110
+ return None
111
+
112
+ def _format_result(self, stdout_file_name, stderr_file_name, return_code):
113
+ with open(stdout_file_name, "r", encoding="utf-8", errors="replace") as out_f:
114
+ stdout_content = out_f.read()
115
+ with open(stderr_file_name, "r", encoding="utf-8", errors="replace") as err_f:
116
+ stderr_content = err_f.read()
117
+ max_lines = 100
118
+ stdout_lines = stdout_content.count("\n")
119
+ stderr_lines = stderr_content.count("\n")
120
+
121
+ def head_tail(text, n=10):
122
+ lines = text.splitlines()
123
+ if len(lines) <= 2 * n:
124
+ return "\n".join(lines)
125
+ return "\n".join(
126
+ lines[:n]
127
+ + ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
128
+ + lines[-n:]
129
+ )
130
+
131
+ if stdout_lines <= max_lines and stderr_lines <= max_lines:
132
+ result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
133
+ if stderr_content.strip():
134
+ result += f"\n--- STDERR ---\n{stderr_content}"
135
+ return result
136
+ else:
137
+ result = f"stdout_file: {stdout_file_name} (lines: {stdout_lines})\n"
138
+ if stderr_lines > 0 and stderr_content.strip():
139
+ result += f"stderr_file: {stderr_file_name} (lines: {stderr_lines})\n"
140
+ result += f"returncode: {return_code}\n"
141
+ result += "--- STDOUT (head/tail) ---\n" + head_tail(stdout_content) + "\n"
142
+ if stderr_content.strip():
143
+ result += (
144
+ "--- STDERR (head/tail) ---\n" + head_tail(stderr_content) + "\n"
145
+ )
146
+ result += "Use the get_lines tool to inspect the contents of these files when needed."
147
+ return result
@@ -1,4 +1,5 @@
1
1
  import subprocess
2
+ import os
2
3
  import sys
3
4
  import tempfile
4
5
  import threading
@@ -51,104 +52,102 @@ class PythonStdinRunnerTool(ToolBase):
51
52
  bufsize=1,
52
53
  universal_newlines=True,
53
54
  encoding="utf-8",
54
- env={**dict(), **dict(PYTHONIOENCODING="utf-8")},
55
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
55
56
  )
56
- stdout_lines = 0
57
- stderr_lines = 0
58
-
59
- def stream_output(stream, file_obj, report_func, count_func):
60
- nonlocal stdout_lines, stderr_lines
61
- for line in stream:
62
- file_obj.write(line)
63
- file_obj.flush()
64
- report_func(line)
65
- if count_func == "stdout":
66
- stdout_lines += 1
67
- else:
68
- stderr_lines += 1
69
-
70
- stdout_thread = threading.Thread(
71
- target=stream_output,
72
- args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
57
+ stdout_lines, stderr_lines = self._stream_process_output(
58
+ process, stdout_file, stderr_file, code
73
59
  )
74
- stderr_thread = threading.Thread(
75
- target=stream_output,
76
- args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
77
- )
78
- stdout_thread.start()
79
- stderr_thread.start()
80
- try:
81
- process.stdin.write(code)
82
- process.stdin.close()
83
- return_code = process.wait(timeout=timeout)
84
- except subprocess.TimeoutExpired:
85
- process.kill()
86
- self.report_error(
87
- tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
88
- )
60
+ return_code = self._wait_for_process(process, timeout)
61
+ if return_code is None:
89
62
  return tr(
90
63
  "Code timed out after {timeout} seconds.", timeout=timeout
91
64
  )
92
- stdout_thread.join()
93
- stderr_thread.join()
94
65
  stdout_file.flush()
95
66
  stderr_file.flush()
96
-
97
67
  self.report_success(
98
68
  tr("\u2705 Return code {return_code}", return_code=return_code)
99
69
  )
100
- # Read back the content for summary if not too large
101
- with open(
102
- stdout_file.name, "r", encoding="utf-8", errors="replace"
103
- ) as out_f:
104
- stdout_content = out_f.read()
105
- with open(
106
- stderr_file.name, "r", encoding="utf-8", errors="replace"
107
- ) as err_f:
108
- stderr_content = err_f.read()
109
- max_lines = 100
110
- stdout_lines = stdout_content.count("\n")
111
- stderr_lines = stderr_content.count("\n")
112
-
113
- def head_tail(text, n=10):
114
- lines = text.splitlines()
115
- if len(lines) <= 2 * n:
116
- return "\n".join(lines)
117
- return "\n".join(
118
- lines[:n]
119
- + ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
120
- + lines[-n:]
121
- )
122
-
123
- if stdout_lines <= max_lines and stderr_lines <= max_lines:
124
- result = (
125
- f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
126
- )
127
- if stderr_content.strip():
128
- result += f"\n--- STDERR ---\n{stderr_content}"
129
- return result
130
- else:
131
- result = (
132
- f"stdout_file: {stdout_file.name} (lines: {stdout_lines})\n"
133
- )
134
- if stderr_lines > 0 and stderr_content.strip():
135
- result += (
136
- f"stderr_file: {stderr_file.name} (lines: {stderr_lines})\n"
137
- )
138
- result += f"returncode: {return_code}\n"
139
- result += (
140
- "--- STDOUT (head/tail) ---\n"
141
- + head_tail(stdout_content)
142
- + "\n"
143
- )
144
- if stderr_content.strip():
145
- result += (
146
- "--- STDERR (head/tail) ---\n"
147
- + head_tail(stderr_content)
148
- + "\n"
149
- )
150
- result += "Use the get_lines tool to inspect the contents of these files when needed."
151
- return result
70
+ return self._format_result(
71
+ stdout_file.name, stderr_file.name, return_code
72
+ )
152
73
  except Exception as e:
153
74
  self.report_error(tr("\u274c Error: {error}", error=e))
154
75
  return tr("Error running code via stdin: {error}", error=e)
76
+
77
+ def _stream_process_output(self, process, stdout_file, stderr_file, code):
78
+ stdout_lines = 0
79
+ stderr_lines = 0
80
+
81
+ def stream_output(stream, file_obj, report_func, count_func):
82
+ nonlocal stdout_lines, stderr_lines
83
+ for line in stream:
84
+ file_obj.write(line)
85
+ file_obj.flush()
86
+ report_func(line)
87
+ if count_func == "stdout":
88
+ stdout_lines += 1
89
+ else:
90
+ stderr_lines += 1
91
+
92
+ stdout_thread = threading.Thread(
93
+ target=stream_output,
94
+ args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
95
+ )
96
+ stderr_thread = threading.Thread(
97
+ target=stream_output,
98
+ args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
99
+ )
100
+ stdout_thread.start()
101
+ stderr_thread.start()
102
+ process.stdin.write(code)
103
+ process.stdin.close()
104
+ stdout_thread.join()
105
+ stderr_thread.join()
106
+ return stdout_lines, stderr_lines
107
+
108
+ def _wait_for_process(self, process, timeout):
109
+ try:
110
+ return process.wait(timeout=timeout)
111
+ except subprocess.TimeoutExpired:
112
+ process.kill()
113
+ self.report_error(
114
+ tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
115
+ )
116
+ return None
117
+
118
+ def _format_result(self, stdout_file_name, stderr_file_name, return_code):
119
+ with open(stdout_file_name, "r", encoding="utf-8", errors="replace") as out_f:
120
+ stdout_content = out_f.read()
121
+ with open(stderr_file_name, "r", encoding="utf-8", errors="replace") as err_f:
122
+ stderr_content = err_f.read()
123
+ max_lines = 100
124
+ stdout_lines = stdout_content.count("\n")
125
+ stderr_lines = stderr_content.count("\n")
126
+
127
+ def head_tail(text, n=10):
128
+ lines = text.splitlines()
129
+ if len(lines) <= 2 * n:
130
+ return "\n".join(lines)
131
+ return "\n".join(
132
+ lines[:n]
133
+ + ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
134
+ + lines[-n:]
135
+ )
136
+
137
+ if stdout_lines <= max_lines and stderr_lines <= max_lines:
138
+ result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
139
+ if stderr_content.strip():
140
+ result += f"\n--- STDERR ---\n{stderr_content}"
141
+ return result
142
+ else:
143
+ result = f"stdout_file: {stdout_file_name} (lines: {stdout_lines})\n"
144
+ if stderr_lines > 0 and stderr_content.strip():
145
+ result += f"stderr_file: {stderr_file_name} (lines: {stderr_lines})\n"
146
+ result += f"returncode: {return_code}\n"
147
+ result += "--- STDOUT (head/tail) ---\n" + head_tail(stdout_content) + "\n"
148
+ if stderr_content.strip():
149
+ result += (
150
+ "--- STDERR (head/tail) ---\n" + head_tail(stderr_content) + "\n"
151
+ )
152
+ result += "Use the get_lines tool to inspect the contents of these files when needed."
153
+ return result
@@ -26,7 +26,7 @@ class RemoveDirectoryTool(ToolBase):
26
26
  disp_path = display_path(file_path)
27
27
  self.report_info(
28
28
  ActionType.WRITE,
29
- tr("🗃️ Removing directory '{disp_path}' ...", disp_path=disp_path),
29
+ tr("🗃️ Remove directory '{disp_path}' ...", disp_path=disp_path),
30
30
  )
31
31
  backup_zip = None
32
32
  try:
@@ -31,7 +31,7 @@ class RemoveFileTool(ToolBase):
31
31
  # Report initial info about what is going to be removed
32
32
  self.report_info(
33
33
  ActionType.WRITE,
34
- tr("🗑️ Removing file '{disp_path}' ...", disp_path=disp_path),
34
+ tr("🗑️ Remove file '{disp_path}' ...", disp_path=disp_path),
35
35
  )
36
36
  if not os.path.exists(path):
37
37
  self.report_error(tr("❌ File does not exist."))
@@ -39,7 +39,7 @@ class ReplaceFileTool(ToolBase):
39
39
  if not tracker.last_operation_is_full_read_or_replace(file_path):
40
40
  self.report_info(
41
41
  ActionType.WRITE,
42
- tr("📝 Replacing file '{disp_path}' ...", disp_path=disp_path),
42
+ tr("📝 Replace file '{disp_path}' ...", disp_path=disp_path),
43
43
  )
44
44
  self.report_warning(tr("ℹ️ Missing full view."))
45
45
  try:
@@ -54,7 +54,7 @@ class ReplaceFileTool(ToolBase):
54
54
  )
55
55
  self.report_info(
56
56
  ActionType.WRITE,
57
- tr("📝 Replacing file '{disp_path}' ...", disp_path=disp_path),
57
+ tr("📝 Replace file '{disp_path}' ...", disp_path=disp_path),
58
58
  )
59
59
  backup_path = file_path + ".bak"
60
60
  shutil.copy2(file_path, backup_path)