jarvis-ai-assistant 0.1.132__py3-none-any.whl → 0.1.138__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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

Files changed (82) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +330 -347
  3. jarvis/jarvis_agent/builtin_input_handler.py +16 -6
  4. jarvis/jarvis_agent/file_input_handler.py +9 -9
  5. jarvis/jarvis_agent/jarvis.py +143 -0
  6. jarvis/jarvis_agent/main.py +12 -13
  7. jarvis/jarvis_agent/output_handler.py +3 -3
  8. jarvis/jarvis_agent/patch.py +92 -64
  9. jarvis/jarvis_agent/shell_input_handler.py +5 -3
  10. jarvis/jarvis_code_agent/code_agent.py +263 -177
  11. jarvis/jarvis_code_agent/file_select.py +24 -24
  12. jarvis/jarvis_dev/main.py +45 -59
  13. jarvis/jarvis_git_details/__init__.py +0 -0
  14. jarvis/jarvis_git_details/main.py +179 -0
  15. jarvis/jarvis_git_squash/main.py +7 -7
  16. jarvis/jarvis_lsp/base.py +11 -53
  17. jarvis/jarvis_lsp/cpp.py +13 -28
  18. jarvis/jarvis_lsp/go.py +13 -28
  19. jarvis/jarvis_lsp/python.py +8 -27
  20. jarvis/jarvis_lsp/registry.py +21 -83
  21. jarvis/jarvis_lsp/rust.py +15 -30
  22. jarvis/jarvis_methodology/main.py +101 -0
  23. jarvis/jarvis_multi_agent/__init__.py +10 -51
  24. jarvis/jarvis_multi_agent/main.py +43 -0
  25. jarvis/jarvis_platform/__init__.py +1 -1
  26. jarvis/jarvis_platform/ai8.py +67 -89
  27. jarvis/jarvis_platform/base.py +14 -13
  28. jarvis/jarvis_platform/kimi.py +25 -28
  29. jarvis/jarvis_platform/ollama.py +24 -26
  30. jarvis/jarvis_platform/openai.py +15 -19
  31. jarvis/jarvis_platform/oyi.py +48 -50
  32. jarvis/jarvis_platform/registry.py +29 -44
  33. jarvis/jarvis_platform/yuanbao.py +39 -43
  34. jarvis/jarvis_platform_manager/main.py +81 -81
  35. jarvis/jarvis_platform_manager/openai_test.py +21 -21
  36. jarvis/jarvis_rag/file_processors.py +18 -18
  37. jarvis/jarvis_rag/main.py +262 -278
  38. jarvis/jarvis_smart_shell/main.py +12 -12
  39. jarvis/jarvis_tools/ask_codebase.py +85 -78
  40. jarvis/jarvis_tools/ask_user.py +8 -8
  41. jarvis/jarvis_tools/base.py +4 -4
  42. jarvis/jarvis_tools/chdir.py +9 -9
  43. jarvis/jarvis_tools/code_review.py +40 -21
  44. jarvis/jarvis_tools/create_code_agent.py +15 -15
  45. jarvis/jarvis_tools/create_sub_agent.py +0 -1
  46. jarvis/jarvis_tools/execute_python_script.py +3 -3
  47. jarvis/jarvis_tools/execute_shell.py +11 -11
  48. jarvis/jarvis_tools/execute_shell_script.py +3 -3
  49. jarvis/jarvis_tools/file_analyzer.py +116 -105
  50. jarvis/jarvis_tools/file_operation.py +22 -20
  51. jarvis/jarvis_tools/find_caller.py +105 -40
  52. jarvis/jarvis_tools/find_methodolopy.py +65 -0
  53. jarvis/jarvis_tools/find_symbol.py +123 -39
  54. jarvis/jarvis_tools/function_analyzer.py +140 -57
  55. jarvis/jarvis_tools/git_commiter.py +10 -10
  56. jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
  57. jarvis/jarvis_tools/methodology.py +22 -67
  58. jarvis/jarvis_tools/project_analyzer.py +137 -53
  59. jarvis/jarvis_tools/rag.py +15 -20
  60. jarvis/jarvis_tools/read_code.py +25 -23
  61. jarvis/jarvis_tools/read_webpage.py +31 -31
  62. jarvis/jarvis_tools/registry.py +72 -52
  63. jarvis/jarvis_tools/search_web.py +23 -353
  64. jarvis/jarvis_tools/tool_generator.py +19 -19
  65. jarvis/jarvis_utils/config.py +36 -96
  66. jarvis/jarvis_utils/embedding.py +83 -83
  67. jarvis/jarvis_utils/git_utils.py +20 -20
  68. jarvis/jarvis_utils/globals.py +18 -6
  69. jarvis/jarvis_utils/input.py +10 -9
  70. jarvis/jarvis_utils/methodology.py +141 -140
  71. jarvis/jarvis_utils/output.py +13 -13
  72. jarvis/jarvis_utils/utils.py +23 -71
  73. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +6 -15
  74. jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
  75. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +4 -3
  76. jarvis/jarvis_tools/lsp_find_definition.py +0 -150
  77. jarvis/jarvis_tools/lsp_find_references.py +0 -127
  78. jarvis/jarvis_tools/select_code_files.py +0 -62
  79. jarvis_ai_assistant-0.1.132.dist-info/RECORD +0 -82
  80. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
  81. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
  82. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
@@ -5,26 +5,30 @@ import os
5
5
  from yaspin import yaspin
6
6
 
7
7
  from jarvis.jarvis_agent.output_handler import OutputHandler
8
+ from jarvis.jarvis_platform.base import BasePlatform
8
9
  from jarvis.jarvis_platform.registry import PlatformRegistry
9
10
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
10
11
  from jarvis.jarvis_tools.file_operation import FileOperationTool
11
12
  from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
12
13
  from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
14
+ from jarvis.jarvis_utils.globals import add_read_file_record, has_read_file
13
15
  from jarvis.jarvis_utils.input import get_multiline_input
14
16
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
15
17
  from jarvis.jarvis_utils.utils import ct, ot, get_file_line_count, user_confirm
16
18
 
19
+
17
20
  class PatchOutputHandler(OutputHandler):
18
21
  def name(self) -> str:
19
22
  return "PATCH"
20
- def handle(self, response: str) -> Tuple[bool, Any]:
21
- return False, apply_patch(response)
22
-
23
+
24
+ def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
25
+ return False, apply_patch(response, agent)
26
+
23
27
  def can_handle(self, response: str) -> bool:
24
28
  if _parse_patch(response):
25
29
  return True
26
30
  return False
27
-
31
+
28
32
  def prompt(self) -> str:
29
33
  return f"""
30
34
  # 代码补丁规范
@@ -43,18 +47,17 @@ Reason: [修改原因]
43
47
  ```
44
48
 
45
49
  ## 核心原则
46
- 1. **上下文完整性**:代码片段必须包含足够的上下文(修改前后各3行)
47
- 2. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
48
- 3. **最小补丁原则**:始终生成最小范围的补丁,只包含必要的上下文和实际修改
49
- 4. **格式严格保持**:
50
+ 1. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
51
+ 2. **最小补丁原则**:始终生成最小范围的补丁,只包含必要的上下文和实际修改
52
+ 3. **格式严格保持**:
50
53
  - 严格保持原始代码的缩进方式(空格或制表符)
51
54
  - 保持原始代码的空行数量和位置
52
55
  - 保持原始代码的行尾空格处理方式
53
56
  - 不改变原始代码的换行风格
54
- 5. **新旧区分**:
57
+ 4. **新旧区分**:
55
58
  - 对于新文件:提供完整的代码内容
56
- - 对于现有文件:只提供修改部分及必要上下文,不要提供整个文件
57
- 6. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
59
+ - 对于现有文件:只提供修改部分,不要提供整个文件
60
+ 5. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
58
61
 
59
62
  ## 格式兼容性要求
60
63
  1. **缩进一致性**:
@@ -85,17 +88,21 @@ def add(a, b):
85
88
 
86
89
  ## 最佳实践
87
90
  - 每个补丁专注于单一职责的修改
88
- - 提供足够的上下文,但避免包含过多无关代码
91
+ - 避免包含过多无关代码
89
92
  - 确保修改理由清晰明确,便于理解变更目的
90
93
  - 保持代码风格一致性,遵循项目现有的编码规范
91
94
  - 在修改前仔细分析原代码的格式风格,确保补丁与之完全兼容
92
95
  - 绝不提供完整文件内容,除非是新建文件
96
+ - 每个文件的修改是独立的,不能出现“参照xxx文件的修改”这样的描述
97
+ - 不要出现未实现的代码,如:TODO
93
98
  """
94
99
 
100
+
95
101
  def _parse_patch(patch_str: str) -> Dict[str, str]:
96
102
  """解析新的上下文补丁格式"""
97
103
  result = {}
98
- patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?'+ct("PATCH"), patch_str, re.DOTALL)
104
+ patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?' +
105
+ ct("PATCH"), patch_str, re.DOTALL)
99
106
  if patches:
100
107
  for patch in patches:
101
108
  first_line = patch.splitlines()[0]
@@ -110,7 +117,8 @@ def _parse_patch(patch_str: str) -> Dict[str, str]:
110
117
  result[filepath] += "\n\n" + patch
111
118
  return result
112
119
 
113
- def apply_patch(output_str: str) -> str:
120
+
121
+ def apply_patch(output_str: str, agent: Any) -> str:
114
122
  """Apply patches to files"""
115
123
  with yaspin(text="正在应用补丁...", color="cyan") as spinner:
116
124
  try:
@@ -118,12 +126,17 @@ def apply_patch(output_str: str) -> str:
118
126
  except Exception as e:
119
127
  PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
120
128
  return ""
121
-
129
+
122
130
  # 获取当前提交hash作为起始点
123
- spinner.text= "开始获取当前提交hash..."
131
+ spinner.text = "开始获取当前提交hash..."
124
132
  start_hash = get_latest_commit_hash()
125
133
  spinner.write("✅ 当前提交hash获取完成")
126
-
134
+
135
+ not_read_file = [f for f in patches.keys() if not has_read_file(f)]
136
+ if not_read_file:
137
+ yaspin.write(f"❌ 以下文件未读取: {not_read_file},应用补丁存在风险,将先读取文件后再生成补丁", color="red")
138
+ return f"以下文件未读取: {not_read_file},应用补丁存在风险,请先读取文件后再生成补丁"
139
+
127
140
  # 按文件逐个处理
128
141
  for filepath, patch_content in patches.items():
129
142
  try:
@@ -134,6 +147,7 @@ def apply_patch(output_str: str) -> str:
134
147
  os.makedirs(os.path.dirname(filepath), exist_ok=True)
135
148
  open(filepath, 'w', encoding='utf-8').close()
136
149
  spinner.write("✅ 文件创建完成")
150
+ add_read_file_record(filepath)
137
151
  with spinner.hidden():
138
152
  while not handle_code_operation(filepath, patch_content):
139
153
  if user_confirm("补丁应用失败,是否重试?", default=True):
@@ -145,7 +159,7 @@ def apply_patch(output_str: str) -> str:
145
159
  spinner.text = f"文件 {filepath} 处理失败: {str(e)}, 回滚文件"
146
160
  revert_file(filepath) # 回滚单个文件
147
161
  spinner.write(f"✅ 文件 {filepath} 回滚完成")
148
-
162
+
149
163
  final_ret = ""
150
164
  diff = get_diff()
151
165
  if diff:
@@ -156,45 +170,50 @@ def apply_patch(output_str: str) -> str:
156
170
  # 获取提交信息
157
171
  end_hash = get_latest_commit_hash()
158
172
  commits = get_commits_between(start_hash, end_hash)
159
-
173
+
160
174
  # 添加提交信息到final_ret
161
175
  if commits:
162
176
  final_ret += "✅ 补丁已应用\n"
163
177
  final_ret += "# 提交信息:\n"
164
178
  for commit_hash, commit_message in commits:
165
179
  final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
166
-
180
+
167
181
  final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
168
-
182
+
169
183
  # 增加代码变更分析和错误提示
170
- final_ret += "\n\n# 代码变更分析:\n"
171
- final_ret += "1. 请仔细检查以上变更是否引入了潜在错误\n"
172
- final_ret += "2. 如果发现代码错误,请立即提出修复方案\n"
173
- final_ret += "3. 修复代码错误的优先级高于继续实现功能\n"
174
- final_ret += "4. 常见错误类型:语法错误、逻辑错误、命名错误、路径错误等\n"
175
- final_ret += "5. 确保修改后代码的一致性和完整性\n"
176
- final_ret += "\n\n"
177
- final_ret += "如果没有问题,请继续进行下一步修改,如果所有修改都已经完成,请终止"
178
-
184
+
185
+ addon_prompt = "1. 请调用静态检查工具(如有)检查以上变更是否引入了潜在错误\n"
186
+ addon_prompt += "2. 如果发现代码错误,请立即提出修复方案\n"
187
+ addon_prompt += "3. 如果错误并非是本次修改引入,要询问用户是否需要立即修复\n"
188
+ addon_prompt += "\n\n"
189
+ addon_prompt += "如果没有问题,请继续进行下一步修改\n"
190
+ addon_prompt += f"如果用户的需求已经完成,请终止,不要输出新的 {ot('PATCH')},不要实现任何超出用户需求外的内容\n"
191
+ addon_prompt += "如果有任何信息不清楚,调用工具获取信息\n"
192
+ addon_prompt += "每次响应必须且只能包含一个操作\n"
193
+
194
+ agent.set_addon_prompt(addon_prompt)
195
+
179
196
  else:
180
197
  final_ret += "✅ 补丁已应用(没有新的提交)"
181
198
  else:
182
199
  final_ret += "❌ 补丁应用被拒绝\n"
183
200
  final_ret += f"# 补丁预览:\n```diff\n{diff}\n```"
184
201
  else:
202
+ commited = False
185
203
  final_ret += "❌ 没有要提交的更改\n"
186
204
  # 用户确认最终结果
187
205
  with spinner.hidden():
206
+ if commited:
207
+ return final_ret
188
208
  PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
189
209
  if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
190
210
  return final_ret
191
211
  custom_reply = get_multiline_input("请输入自定义回复")
192
212
  if not custom_reply.strip(): # 如果自定义回复为空,返回空字符串
193
213
  return ""
194
- if not commited:
195
- return final_ret + "\n\n" + custom_reply
196
- else:
197
- return custom_reply
214
+ agent.set_addon_prompt(custom_reply)
215
+ return final_ret
216
+
198
217
 
199
218
  def revert_file(filepath: str):
200
219
  """增强版git恢复,处理新文件"""
@@ -206,7 +225,8 @@ def revert_file(filepath: str):
206
225
  stderr=subprocess.PIPE
207
226
  )
208
227
  if result.returncode == 0:
209
- subprocess.run(['git', 'checkout', 'HEAD', '--', filepath], check=True)
228
+ subprocess.run(['git', 'checkout', 'HEAD',
229
+ '--', filepath], check=True)
210
230
  else:
211
231
  if os.path.exists(filepath):
212
232
  os.remove(filepath)
@@ -214,11 +234,15 @@ def revert_file(filepath: str):
214
234
  except subprocess.CalledProcessError as e:
215
235
  PrettyOutput.print(f"恢复文件失败: {str(e)}", OutputType.ERROR)
216
236
  # 修改后的恢复函数
237
+
238
+
217
239
  def revert_change():
218
240
  import subprocess
219
241
  subprocess.run(['git', 'reset', '--hard', 'HEAD'], check=True)
220
242
  subprocess.run(['git', 'clean', '-fd'], check=True)
221
243
  # 修改后的获取差异函数
244
+
245
+
222
246
  def get_diff() -> str:
223
247
  """使用git获取暂存区差异"""
224
248
  import subprocess
@@ -236,9 +260,10 @@ def get_diff() -> str:
236
260
  except subprocess.CalledProcessError as e:
237
261
  return f"获取差异失败: {str(e)}"
238
262
 
239
- def handle_commit_workflow()->bool:
263
+
264
+ def handle_commit_workflow() -> bool:
240
265
  """Handle the git commit workflow and return the commit details.
241
-
266
+
242
267
  Returns:
243
268
  tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
244
269
  """
@@ -252,13 +277,13 @@ def handle_commit_workflow()->bool:
252
277
 
253
278
  def handle_code_operation(filepath: str, patch_content: str) -> bool:
254
279
  """处理代码操作"""
255
- if get_file_line_count(filepath) < 100:
280
+ if get_file_line_count(filepath) < 5:
256
281
  return handle_small_code_operation(filepath, patch_content)
257
282
  else:
258
283
  retry_count = 5
259
284
  while retry_count > 0:
260
285
  retry_count -= 1
261
- if handle_large_code_operation(filepath, patch_content):
286
+ if handle_large_code_operation(filepath, patch_content, PlatformRegistry().get_normal_platform() if retry_count > 2 else PlatformRegistry().get_thinking_platform()):
262
287
  return True
263
288
  return handle_small_code_operation(filepath, patch_content)
264
289
 
@@ -268,11 +293,12 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
268
293
  with yaspin(text=f"正在修改文件 {filepath}...", color="cyan") as spinner:
269
294
  try:
270
295
  with spinner.hidden():
271
- old_file_content = FileOperationTool().execute({"operation": "read", "files": [{"path": filepath}]})
296
+ old_file_content = FileOperationTool().execute(
297
+ {"operation": "read", "files": [{"path": filepath}]})
272
298
  if not old_file_content["success"]:
273
299
  spinner.write("❌ 文件读取失败")
274
300
  return False
275
-
301
+
276
302
  prompt = f"""
277
303
  # 代码合并专家指南
278
304
 
@@ -308,16 +334,17 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
308
334
  [合并后的完整代码,包括所有空行和缩进]
309
335
  {ct("MERGED_CODE")}
310
336
  """
311
- model = PlatformRegistry().get_codegen_platform()
312
- model.set_suppress_output(True)
337
+ model = PlatformRegistry().get_normal_platform()
338
+ model.set_suppress_output(False)
313
339
  count = 30
314
340
  start_line = -1
315
341
  end_line = -1
316
342
  code = []
317
343
  finished = False
318
- while count>0:
344
+ while count > 0:
319
345
  count -= 1
320
- response = model.chat_until_success(prompt).splitlines()
346
+ with spinner.hidden():
347
+ response = model.chat_until_success(prompt).splitlines()
321
348
  try:
322
349
  start_line = response.index(ot("MERGED_CODE")) + 1
323
350
  try:
@@ -328,7 +355,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
328
355
  except:
329
356
  pass
330
357
 
331
- try:
358
+ try:
332
359
  response.index(ot("!!!FINISHED!!!"))
333
360
  finished = True
334
361
  break
@@ -365,20 +392,20 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
365
392
  return False
366
393
 
367
394
 
368
- def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
395
+ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePlatform) -> bool:
369
396
  """处理大型代码文件的补丁操作,使用差异化补丁格式"""
370
397
  with yaspin(text=f"正在处理文件 {filepath}...", color="cyan") as spinner:
371
398
  try:
372
399
  # 读取原始文件内容
373
- old_file_content = FileOperationTool().execute({"operation": "read", "files": [{"path": filepath}]})
400
+ old_file_content = FileOperationTool().execute(
401
+ {"operation": "read", "files": [{"path": filepath}]})
374
402
  if not old_file_content["success"]:
375
403
  spinner.text = "文件读取失败"
376
404
  spinner.fail("❌")
377
405
  return False
378
-
379
- model = PlatformRegistry().get_codegen_platform()
380
- model.set_suppress_output(True)
381
-
406
+
407
+ model.set_suppress_output(False)
408
+
382
409
  prompt = f"""
383
410
  # 代码补丁生成专家指南
384
411
 
@@ -431,20 +458,21 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
431
458
  {ct("DIFF")}
432
459
  """
433
460
  # 获取补丁内容
434
- response = model.chat_until_success(prompt)
435
-
461
+ with spinner.hidden():
462
+ response = model.chat_until_success(prompt)
463
+
436
464
  # 解析差异化补丁
437
- diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
438
- response, re.DOTALL)
439
-
465
+ diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
466
+ response, re.DOTALL)
467
+
440
468
  # 读取原始文件内容
441
469
  with open(filepath, 'r', encoding='utf-8', errors="ignore") as f:
442
470
  file_content = f.read()
443
-
471
+
444
472
  # 应用所有差异化补丁
445
473
  modified_content = file_content
446
474
  patch_count = 0
447
-
475
+
448
476
  for match in diff_blocks:
449
477
  search_text = match.group(1).strip()
450
478
  replace_text = match.group(2).strip()
@@ -457,23 +485,23 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
457
485
  spinner.fail("❌")
458
486
  return False
459
487
  # 应用替换
460
- modified_content = modified_content.replace(search_text, replace_text)
488
+ modified_content = modified_content.replace(
489
+ search_text, replace_text)
461
490
  spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
462
491
  else:
463
492
  spinner.text = f"补丁 #{patch_count} 应用失败:无法找到匹配的代码段"
464
493
  spinner.fail("❌")
465
494
  return False
466
-
495
+
467
496
  # 写入修改后的内容
468
497
  with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
469
498
  f.write(modified_content)
470
-
499
+
471
500
  spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
472
501
  spinner.ok("✅")
473
502
  return True
474
-
503
+
475
504
  except Exception as e:
476
505
  spinner.text = f"文件修改失败: {str(e)}"
477
506
  spinner.fail("❌")
478
507
  return False
479
-
@@ -21,7 +21,9 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
21
21
  "arguments": {
22
22
  "script_content": script
23
23
  }
24
- })
25
- return f"{user_input}\n\n用户执行以下脚本:\n{script}\n\n执行结果:\n{output}", False
24
+ }, agent)
25
+ if user_confirm("是否将执行结果反馈给Agent?", default=True):
26
+ return f"{user_input}\n\n用户执行以下脚本:\n{script}\n\n执行结果:\n{output}", False
27
+ return "", True
26
28
  return user_input, False
27
-
29
+