jarvis-ai-assistant 0.1.130__py3-none-any.whl → 0.1.131__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 (60) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +23 -9
  3. jarvis/jarvis_agent/builtin_input_handler.py +73 -0
  4. jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
  5. jarvis/jarvis_agent/main.py +1 -1
  6. jarvis/{jarvis_code_agent → jarvis_agent}/patch.py +23 -19
  7. jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +0 -1
  8. jarvis/jarvis_code_agent/code_agent.py +20 -16
  9. jarvis/jarvis_codebase/main.py +5 -5
  10. jarvis/jarvis_dev/main.py +1 -1
  11. jarvis/jarvis_git_squash/main.py +1 -1
  12. jarvis/jarvis_lsp/base.py +2 -26
  13. jarvis/jarvis_lsp/cpp.py +2 -14
  14. jarvis/jarvis_lsp/go.py +0 -13
  15. jarvis/jarvis_lsp/python.py +1 -30
  16. jarvis/jarvis_lsp/registry.py +10 -14
  17. jarvis/jarvis_lsp/rust.py +0 -12
  18. jarvis/jarvis_multi_agent/__init__.py +1 -1
  19. jarvis/jarvis_platform/registry.py +1 -1
  20. jarvis/jarvis_platform_manager/main.py +3 -3
  21. jarvis/jarvis_rag/main.py +1 -1
  22. jarvis/jarvis_tools/ask_codebase.py +40 -20
  23. jarvis/jarvis_tools/code_review.py +180 -143
  24. jarvis/jarvis_tools/create_code_agent.py +76 -72
  25. jarvis/jarvis_tools/create_sub_agent.py +32 -15
  26. jarvis/jarvis_tools/execute_shell.py +2 -2
  27. jarvis/jarvis_tools/execute_shell_script.py +1 -1
  28. jarvis/jarvis_tools/file_operation.py +2 -2
  29. jarvis/jarvis_tools/git_commiter.py +87 -68
  30. jarvis/jarvis_tools/lsp_find_definition.py +83 -67
  31. jarvis/jarvis_tools/lsp_find_references.py +62 -46
  32. jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
  33. jarvis/jarvis_tools/methodology.py +3 -3
  34. jarvis/jarvis_tools/read_code.py +1 -1
  35. jarvis/jarvis_tools/search_web.py +18 -20
  36. jarvis/jarvis_tools/tool_generator.py +1 -1
  37. jarvis/jarvis_tools/treesitter_analyzer.py +331 -0
  38. jarvis/jarvis_treesitter/README.md +104 -0
  39. jarvis/jarvis_treesitter/__init__.py +20 -0
  40. jarvis/jarvis_treesitter/database.py +258 -0
  41. jarvis/jarvis_treesitter/example.py +115 -0
  42. jarvis/jarvis_treesitter/grammar_builder.py +182 -0
  43. jarvis/jarvis_treesitter/language.py +117 -0
  44. jarvis/jarvis_treesitter/symbol.py +31 -0
  45. jarvis/jarvis_treesitter/tools_usage.md +121 -0
  46. jarvis/jarvis_utils/git_utils.py +10 -2
  47. jarvis/jarvis_utils/input.py +3 -1
  48. jarvis/jarvis_utils/methodology.py +1 -1
  49. jarvis/jarvis_utils/utils.py +3 -3
  50. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/METADATA +2 -4
  51. jarvis_ai_assistant-0.1.131.dist-info/RECORD +85 -0
  52. jarvis/jarvis_c2rust/c2rust.yaml +0 -734
  53. jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
  54. jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
  55. jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
  56. jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
  57. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/LICENSE +0 -0
  58. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/WHEEL +0 -0
  59. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/entry_points.txt +0 -0
  60. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/top_level.txt +0 -0
jarvis/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.130"
3
+ __version__ = "0.1.131"
@@ -6,6 +6,10 @@ import yaml
6
6
  from yaspin import yaspin
7
7
 
8
8
  from jarvis.jarvis_agent.output_handler import OutputHandler
9
+ from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
10
+ from jarvis.jarvis_agent.file_input_handler import file_input_handler
11
+ from jarvis.jarvis_agent.patch import PatchOutputHandler
12
+ from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
9
13
  from jarvis.jarvis_platform.base import BasePlatform
10
14
  from jarvis.jarvis_platform.registry import PlatformRegistry
11
15
  from jarvis.jarvis_tools.registry import ToolRegistry
@@ -30,6 +34,18 @@ class Agent:
30
34
  """
31
35
  self.summary_prompt = summary_prompt
32
36
 
37
+ def clear(self):
38
+ """清除当前对话历史,保留系统消息。
39
+
40
+ 该方法将:
41
+ 1. 调用模型的delete_chat方法清除对话历史
42
+ 2. 重置对话长度计数器
43
+ 3. 清空当前提示
44
+ """
45
+ self.model.delete_chat() # type: ignore
46
+ self.conversation_length = 0
47
+ self.prompt = ""
48
+
33
49
  def __del__(self):
34
50
  delete_agent(self.name)
35
51
 
@@ -126,7 +142,7 @@ class Agent:
126
142
  # 添加工具使用总结
127
143
  action_prompt += """
128
144
  # ❗ 重要操作使用规则
129
- 1. 一次只使用一个操作
145
+ 1. 一次对话只能使用一个操作,否则会出错
130
146
  2. 严格按照每个操作的格式执行
131
147
  3. 等待操作结果后再进行下一个操作
132
148
  4. 处理完结果后再调用新的操作
@@ -203,6 +219,8 @@ class Agent:
203
219
  try:
204
220
  with spinner.hidden():
205
221
  summary = self._call_model(self.prompt + "\n" + prompt)
222
+
223
+ self.model.delete_chat() # type: ignore
206
224
 
207
225
  # 清空当前对话历史,但保留系统消息
208
226
  self.conversation_length = 0 # Reset conversation length
@@ -375,7 +393,7 @@ def _load_tasks() -> dict:
375
393
  if os.path.exists(user_jarvis):
376
394
  with yaspin(text=f"从{user_jarvis}加载预定义任务...", color="cyan") as spinner:
377
395
  try:
378
- with open(user_jarvis, "r", encoding="utf-8") as f:
396
+ with open(user_jarvis, "r", encoding="utf-8", errors="ignore") as f:
379
397
  user_tasks = yaml.safe_load(f)
380
398
 
381
399
  if isinstance(user_tasks, dict):
@@ -393,7 +411,7 @@ def _load_tasks() -> dict:
393
411
  if os.path.exists(".jarvis/pre-command"):
394
412
  with yaspin(text=f"从{os.path.abspath('.jarvis/pre-command')}加载预定义任务...", color="cyan") as spinner:
395
413
  try:
396
- with open(".jarvis/pre-command", "r", encoding="utf-8") as f:
414
+ with open(".jarvis/pre-command", "r", encoding="utf-8", errors="ignore") as f:
397
415
  local_tasks = yaml.safe_load(f)
398
416
 
399
417
  if isinstance(local_tasks, dict):
@@ -467,10 +485,8 @@ origin_agent_system_prompt = """
467
485
 
468
486
  # 🔥 绝对行动要求
469
487
  1. 每个响应必须包含且仅包含一个工具调用
470
- 2. 唯一例外:使用 <!!!COMPLETE!!!> 命令
488
+ 2. 唯一例外:任务结束
471
489
  3. 空响应会触发致命错误
472
- 4. 不能处于"等待用户输入"状态
473
- 5. 任何行动都不能使用完成命令
474
490
 
475
491
  # 🚫 违规示例
476
492
  - 没有工具调用的分析 → 永久挂起
@@ -500,8 +516,6 @@ origin_agent_system_prompt = """
500
516
  4. 任务完成
501
517
  - 验证目标完成情况
502
518
  - 如有价值则记录方法论
503
- - 使用完成命令结束任务
504
- → 必须使用 <!!!COMPLETE!!!>
505
519
 
506
520
  # 📑 方法论模板
507
521
  ```markdown
@@ -553,7 +567,7 @@ def main():
553
567
 
554
568
  try:
555
569
  # 获取全局模型实例
556
- agent = Agent(system_prompt=origin_agent_system_prompt, platform=args.platform, model_name=args.model, output_handler=[ToolRegistry()])
570
+ agent = Agent(system_prompt=origin_agent_system_prompt, platform=args.platform, model_name=args.model, input_handler=[file_input_handler, shell_input_handler, builtin_input_handler] ,output_handler=[ToolRegistry(), PatchOutputHandler()])
557
571
 
558
572
  # 加载预定义任务
559
573
  tasks = _load_tasks()
@@ -0,0 +1,73 @@
1
+ import re
2
+ from typing import Any, Tuple
3
+
4
+
5
+
6
+ def builtin_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
7
+ """
8
+ 处理内置的特殊输入标记,并追加相应的提示词
9
+
10
+ 参数:
11
+ user_input: 用户输入
12
+ agent: 代理对象
13
+
14
+ 返回:
15
+ Tuple[str, bool]: 处理后的输入和是否需要进一步处理
16
+ """
17
+ # 查找特殊标记
18
+ special_tags = re.findall(r"'<([^>]+)>'", user_input)
19
+
20
+ if not special_tags:
21
+ return user_input, False
22
+
23
+ # 使用集合去重
24
+ processed_tags = set()
25
+ # 处理每个标记
26
+ for tag in special_tags:
27
+ if tag in processed_tags:
28
+ continue
29
+ processed_tags.add(tag)
30
+
31
+ if tag == "CodeBase":
32
+ user_input = user_input.replace(f"'<{tag}>'", "")
33
+ user_input += """
34
+ 请使用ask_codebase工具查询代码库,可以使用的提问格式包括:
35
+ 1. 与xxx功能相关的文件有哪些?
36
+ 2. 要实现xxx,应该要修改哪些文件?
37
+ 3. xxx功能是怎么实现的?
38
+ 4. xxx模块的入口函数是什么?
39
+ 5. xxx功能的测试用例在哪里?
40
+ """
41
+ elif tag == "Web":
42
+ user_input = user_input.replace(f"'<{tag}>'", "")
43
+ user_input += """
44
+ 请使用search_web工具进行网页搜索,可以使用的提问格式包括:
45
+ 1. xxx技术的最新发展是什么?
46
+ 2. xxx框架的官方文档在哪里?
47
+ 3. xxx库的GitHub仓库地址是什么?
48
+ 4. xxx问题的解决方案有哪些?
49
+ 5. xxx概念的详细解释是什么?
50
+ """
51
+ elif tag == "RAG":
52
+ user_input = user_input.replace(f"'<{tag}>'", "")
53
+ user_input += """
54
+ 请使用rag工具进行知识库检索,可以使用的提问格式包括:
55
+ 1. 关于xxx的知识点有哪些?
56
+ 2. xxx的最佳实践是什么?
57
+ 3. xxx的实现方案有哪些?
58
+ 4. xxx的相关案例有哪些?
59
+ 5. xxx的技术细节是什么?
60
+ """
61
+ elif tag == "Summary":
62
+ user_input = user_input.replace(f"'<{tag}>'", "")
63
+ agent._summarize_and_clear_history()
64
+ if not user_input.strip():
65
+ return "", True
66
+ elif tag == "Clear":
67
+ user_input = user_input.replace(f"'<{tag}>'", "")
68
+ agent.clear()
69
+ if not user_input.strip():
70
+ return "", True
71
+ # 移除对未知标记的警告输出
72
+
73
+ return user_input, False
@@ -30,7 +30,7 @@ def file_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
30
30
 
31
31
  # Handle special values and Python-style negative indices
32
32
  try:
33
- with open(file_path, 'r', encoding='utf-8') as f:
33
+ with open(file_path, 'r', encoding='utf-8', errors="ignore") as f:
34
34
  total_lines = len(f.readlines())
35
35
  except FileNotFoundError:
36
36
  PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
@@ -23,7 +23,7 @@ def load_config(config_path: str) -> dict:
23
23
  PrettyOutput.print(f"配置文件 {config_path} 不存在,使用默认配置", OutputType.WARNING)
24
24
  return {}
25
25
 
26
- with open(config_path, 'r', encoding='utf-8') as f:
26
+ with open(config_path, 'r', encoding='utf-8', errors="ignore") as f:
27
27
  try:
28
28
  config = yaml.safe_load(f)
29
29
  return config if config else {}
@@ -99,7 +99,10 @@ def _parse_patch(patch_str: str) -> Dict[str, str]:
99
99
  PrettyOutput.print("无效的补丁格式", OutputType.WARNING)
100
100
  continue
101
101
  filepath = sm.group(1).strip()
102
- result[filepath] = patch
102
+ if filepath not in result:
103
+ result[filepath] = patch
104
+ else:
105
+ result[filepath] += "\n\n" + patch
103
106
  return result
104
107
 
105
108
  def apply_patch(output_str: str) -> str:
@@ -127,11 +130,7 @@ def apply_patch(output_str: str) -> str:
127
130
  open(filepath, 'w', encoding='utf-8').close()
128
131
  spinner.write("✅ 文件创建完成")
129
132
  with spinner.hidden():
130
- retry_count = 3
131
133
  while not handle_code_operation(filepath, patch_content):
132
- retry_count -= 1
133
- if retry_count > 0:
134
- continue
135
134
  if user_confirm("补丁应用失败,是否重试?", default=True):
136
135
  pass
137
136
  else:
@@ -171,8 +170,8 @@ def apply_patch(output_str: str) -> str:
171
170
  else:
172
171
  final_ret += "❌ 没有要提交的更改\n"
173
172
  # 用户确认最终结果
174
- PrettyOutput.print(final_ret, OutputType.USER)
175
173
  with spinner.hidden():
174
+ PrettyOutput.print(final_ret, OutputType.USER)
176
175
  if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
177
176
  return final_ret
178
177
  return get_multiline_input("请输入自定义回复")
@@ -212,7 +211,7 @@ def get_diff() -> str:
212
211
  check=True
213
212
  )
214
213
  ret = result.stdout
215
- subprocess.run(['git', "reset", "--soft", "HEAD"], check=True)
214
+ subprocess.run(['git', "reset", "--mixed", "HEAD"], check=True)
216
215
  return ret
217
216
  except subprocess.CalledProcessError as e:
218
217
  return f"获取差异失败: {str(e)}"
@@ -233,10 +232,15 @@ def handle_commit_workflow()->bool:
233
232
 
234
233
  def handle_code_operation(filepath: str, patch_content: str) -> bool:
235
234
  """处理代码操作"""
236
- if get_file_line_count(filepath) < 30:
235
+ if get_file_line_count(filepath) < 100:
237
236
  return handle_small_code_operation(filepath, patch_content)
238
237
  else:
239
- return handle_large_code_operation(filepath, patch_content)
238
+ retry_count = 3
239
+ while retry_count > 0:
240
+ retry_count -= 1
241
+ if handle_large_code_operation(filepath, patch_content):
242
+ return True
243
+ return handle_small_code_operation(filepath, patch_content)
240
244
 
241
245
 
242
246
  def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
@@ -285,7 +289,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
285
289
  </MERGED_CODE>
286
290
  """
287
291
  model = PlatformRegistry().get_codegen_platform()
288
- model.set_suppress_output(True)
292
+ model.set_suppress_output(False)
289
293
  count = 30
290
294
  start_line = -1
291
295
  end_line = -1
@@ -293,7 +297,8 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
293
297
  finished = False
294
298
  while count>0:
295
299
  count -= 1
296
- response = model.chat_until_success(prompt).splitlines()
300
+ with spinner.hidden():
301
+ response = model.chat_until_success(prompt).splitlines()
297
302
  try:
298
303
  start_line = response.index("<MERGED_CODE>") + 1
299
304
  try:
@@ -329,7 +334,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
329
334
  return False
330
335
  # 写入合并后的代码
331
336
  spinner.text = "写入合并后的代码..."
332
- with open(filepath, 'w', encoding='utf-8') as f:
337
+ with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
333
338
  f.write("\n".join(code)+"\n")
334
339
  spinner.write("✅ 合并后的代码写入完成")
335
340
  spinner.text = "代码修改完成"
@@ -415,7 +420,7 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
415
420
  response, re.DOTALL)
416
421
 
417
422
  # 读取原始文件内容
418
- with open(filepath, 'r', encoding='utf-8') as f:
423
+ with open(filepath, 'r', encoding='utf-8', errors="ignore") as f:
419
424
  file_content = f.read()
420
425
 
421
426
  # 应用所有差异化补丁
@@ -425,25 +430,24 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
425
430
  for match in diff_blocks:
426
431
  search_text = match.group(1).strip()
427
432
  replace_text = match.group(2).strip()
428
-
433
+ patch_count += 1
429
434
  # 检查搜索文本是否存在于文件中
430
435
  if search_text in modified_content:
431
436
  # 如果有多处,报错
432
437
  if modified_content.count(search_text) > 1:
433
- spinner.text = f"补丁 #{patch_count+1} 应用失败:找到多个匹配的代码段"
438
+ spinner.text = f"补丁 #{patch_count} 应用失败:找到多个匹配的代码段"
434
439
  spinner.fail("❌")
435
440
  return False
436
441
  # 应用替换
437
442
  modified_content = modified_content.replace(search_text, replace_text)
438
- patch_count += 1
439
- spinner.write(f"✅ 补丁 #{patch_count+1} 应用成功")
443
+ spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
440
444
  else:
441
- spinner.text = f"补丁 #{patch_count+1} 应用失败:无法找到匹配的代码段"
445
+ spinner.text = f"补丁 #{patch_count} 应用失败:无法找到匹配的代码段"
442
446
  spinner.fail("❌")
443
447
  return False
444
448
 
445
449
  # 写入修改后的内容
446
- with open(filepath, 'w', encoding='utf-8') as f:
450
+ with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
447
451
  f.write(modified_content)
448
452
 
449
453
  spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
@@ -2,7 +2,6 @@
2
2
 
3
3
  from typing import Any, Tuple
4
4
 
5
- from jarvis.jarvis_tools.execute_shell_script import ShellScriptTool
6
5
  from jarvis.jarvis_tools.registry import ToolRegistry
7
6
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
7
  from jarvis.jarvis_utils.utils import user_confirm
@@ -1,17 +1,14 @@
1
- import re
2
1
  import subprocess
3
2
  import os
4
- from typing import Any, Tuple
5
3
 
6
4
  from yaspin import yaspin
7
5
 
8
6
  from jarvis.jarvis_agent import Agent
9
- from jarvis.jarvis_code_agent.builtin_input_handler import builtin_input_handler
10
- from jarvis.jarvis_code_agent.file_input_handler import file_input_handler
11
- from jarvis.jarvis_code_agent.shell_input_handler import shell_input_handler
12
- from jarvis.jarvis_code_agent.patch import PatchOutputHandler
7
+ from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
8
+ from jarvis.jarvis_agent.file_input_handler import file_input_handler
9
+ from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
10
+ from jarvis.jarvis_agent.patch import PatchOutputHandler
13
11
  from jarvis.jarvis_platform.registry import PlatformRegistry
14
- from jarvis.jarvis_tools.file_operation import FileOperationTool
15
12
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
16
13
 
17
14
  from jarvis.jarvis_tools.registry import ToolRegistry
@@ -34,11 +31,11 @@ class CodeAgent:
34
31
  "create_code_agent",
35
32
  "ask_user",
36
33
  "ask_codebase",
37
- "lsp_get_document_symbols",
38
34
  "lsp_get_diagnostics",
39
35
  "lsp_find_references",
40
- "lsp_find_definition",
41
- "lsp_prepare_rename"
36
+ "lsp_find_definition",
37
+ "treesitter_analyzer",
38
+ "code_review" # 新增代码审查工具
42
39
  ])
43
40
  code_system_prompt = """
44
41
  # 专业代码工程师
@@ -60,8 +57,8 @@ class CodeAgent:
60
57
 
61
58
  ## 任务执行规范
62
59
  ### 分析阶段
63
- - 使用lsp_get_document_symbols映射代码结构
64
60
  - 通过lsp_find_references和lsp_find_definition追踪依赖关系
61
+ - 使用treesitter_analyzer深入分析代码结构和关系
65
62
  - 在进行更改前识别潜在影响区域
66
63
  - 为安全创建回滚计划
67
64
 
@@ -70,10 +67,10 @@ class CodeAgent:
70
67
  - 保持一致的代码风格和格式
71
68
  - 每次重大更改后运行lsp_get_diagnostics
72
69
  - 立即修复任何检测到的问题
70
+ - 确保代码修改后第一时间验证,优先修复错误而不是继续实现新功能
73
71
 
74
72
  ### 验证阶段
75
73
  - 使用lsp_get_diagnostics进行全面诊断
76
- - 用lsp_prepare_rename验证所有重命名元素
77
74
  - 检查相关代码中的意外副作用
78
75
  - 确保必要的向后兼容性
79
76
 
@@ -97,7 +94,6 @@ class CodeAgent:
97
94
  3. **验证清单**:
98
95
  - 运行lsp_get_diagnostics确保零错误
99
96
  - 使用lsp_find_references确认影响范围
100
- - 用lsp_prepare_rename验证重命名安全性
101
97
 
102
98
  4. **修改后流程**:
103
99
  - 代码审查模拟
@@ -106,13 +102,21 @@ class CodeAgent:
106
102
 
107
103
  ## 工具使用指南
108
104
  - **分析工具**:
109
- - lsp_get_document_symbols:用于映射代码结构
110
105
  - lsp_find_references:用于理解使用模式
111
106
  - lsp_find_definition:用于追踪实现细节
107
+ - treesitter_analyzer:用于高级代码分析,支持以下功能:
108
+ - find_symbol:查找符号定义位置
109
+ - find_references:查找符号的所有引用
110
+ - find_callers:查找函数的所有调用处
111
+
112
+ 注意事项:
113
+ - 每次操作都会自动索引指定目录,无需单独执行索引步骤
114
+ - 适用于多种编程语言:Python, C, C++, Go, Rust 等
115
+ - 首次使用时会自动下载语法文件,可能需要一些时间
112
116
 
113
117
  - **验证工具**:
114
- - lsp_prepare_rename:用于安全重构检查
115
118
  - lsp_get_diagnostics:用于修改后检查
119
+ - code_review:用于代码审查
116
120
 
117
121
  - **系统工具**:
118
122
  - execute_shell:用于git操作和grep搜索
@@ -187,7 +191,7 @@ class CodeAgent:
187
191
  if commits and user_confirm("是否接受以上提交记录?", True):
188
192
  if len(commits) > 1 and user_confirm("是否要合并为一个更清晰的提交记录?", True):
189
193
  # Reset to start commit
190
- subprocess.run(["git", "reset", "--soft", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
194
+ subprocess.run(["git", "reset", "--mixed", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
191
195
  # Create new commit
192
196
  git_commiter = GitCommitTool()
193
197
  git_commiter.execute({})
@@ -78,7 +78,7 @@ class CodeBase:
78
78
 
79
79
  def is_text_file(self, file_path: str):
80
80
  try:
81
- open(file_path, "r", encoding="utf-8").read()
81
+ open(file_path, "r", encoding="utf-8", errors="ignore").read()
82
82
  return True
83
83
  except Exception:
84
84
  return False
@@ -233,7 +233,7 @@ class CodeBase:
233
233
  return cached_vector
234
234
 
235
235
  # Read the file content and combine information
236
- content = open(file_path, "r", encoding="utf-8").read()[:self.max_token_count] # Limit the file content length
236
+ content = open(file_path, "r", encoding="utf-8", errors="ignore").read()[:self.max_token_count] # Limit the file content length
237
237
 
238
238
  # Combine file information, including file content
239
239
  combined_text = f"""
@@ -288,7 +288,7 @@ Content: {content}
288
288
 
289
289
  md5 = get_file_md5(file_path)
290
290
 
291
- content = open(file_path, "r", encoding="utf-8").read()
291
+ content = open(file_path, "r", encoding="utf-8", errors="ignore").read()
292
292
 
293
293
  # Check if the file has already been processed and the content has not changed
294
294
  if file_path in self.vector_cache:
@@ -565,7 +565,7 @@ Content: {content}
565
565
 
566
566
  for path in initial_results:
567
567
  try:
568
- content = open(path, "r", encoding="utf-8").read()
568
+ content = open(path, "r", encoding="utf-8", errors="ignore").read()
569
569
  # Truncate large files
570
570
  if get_context_token_count(content) > max_file_length:
571
571
  spinner.write(f"❌ 截断大文件: {path}")
@@ -861,7 +861,7 @@ Content: {content}
861
861
 
862
862
  for path in files_from_codebase:
863
863
  try:
864
- content = open(path["file"], "r", encoding="utf-8").read()
864
+ content = open(path["file"], "r", encoding="utf-8", errors="ignore").read()
865
865
  file_content = f"""
866
866
  ## 文件: {path["file"]}
867
867
  ```
jarvis/jarvis_dev/main.py CHANGED
@@ -909,7 +909,7 @@ def create_dev_team() -> MultiAgent:
909
909
  BA_output_handler.use_tools(["ask_user", "file_operation", "search_web", "rag", "execute_shell"])
910
910
 
911
911
  SA_output_handler = ToolRegistry()
912
- SA_output_handler.use_tools(["file_operation", "search_web", "rag", "ask_codebase", "lsp_get_document_symbols", "execute_shell"])
912
+ SA_output_handler.use_tools(["file_operation", "search_web", "rag", "ask_codebase", "execute_shell"])
913
913
 
914
914
  TL_output_handler = ToolRegistry()
915
915
  TL_output_handler.use_tools(["file_operation", "ask_codebase", "lsp_get_diagnostics", "lsp_find_references", "lsp_find_definition", "execute_shell"])
@@ -18,7 +18,7 @@ class GitSquashTool:
18
18
  """Perform soft reset to specified commit hash"""
19
19
  try:
20
20
  subprocess.Popen(
21
- ["git", "reset", "--soft", commit_hash],
21
+ ["git", "reset", "--mixed", commit_hash],
22
22
  stdout=subprocess.DEVNULL,
23
23
  stderr=subprocess.DEVNULL
24
24
  ).wait()
jarvis/jarvis_lsp/base.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import List, Dict, Optional, Tuple, Any
2
+ from typing import List, Dict, Optional, Tuple, Any, Union
3
3
 
4
4
  class BaseLSP(ABC):
5
5
  """Base class for Language Server Protocol integration.
@@ -11,7 +11,7 @@ class BaseLSP(ABC):
11
11
  4. Symbol analysis
12
12
  """
13
13
 
14
- language: str = "" # Language identifier, should be overridden by subclasses
14
+ language: Union[str, List[str]] = "" # Language identifier, should be overridden by subclasses
15
15
 
16
16
  @abstractmethod
17
17
  def initialize(self, workspace_path: str) -> bool:
@@ -67,17 +67,6 @@ class BaseLSP(ABC):
67
67
  """
68
68
  return None
69
69
 
70
- @abstractmethod
71
- def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
72
- """Get all symbols in document.
73
-
74
- Args:
75
- file_path: Path to the file
76
-
77
- Returns:
78
- List of symbols with their locations and types
79
- """
80
- return []
81
70
 
82
71
  @abstractmethod
83
72
  def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
@@ -112,19 +101,6 @@ class BaseLSP(ABC):
112
101
  """
113
102
  return []
114
103
 
115
- @abstractmethod
116
- def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
117
- """Check if symbol at position can be renamed.
118
-
119
- Args:
120
- file_path: Path to the file
121
- position: Symbol position
122
-
123
- Returns:
124
- Range that would be renamed or None if rename not allowed
125
- """
126
- return None
127
-
128
104
 
129
105
  def shutdown(self):
130
106
  """Shutdown LSP server cleanly."""
jarvis/jarvis_lsp/cpp.py CHANGED
@@ -9,7 +9,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
  class CPPLSP(BaseLSP):
10
10
  """C++ LSP implementation using clangd."""
11
11
 
12
- language = "cpp"
12
+ language = ["cpp", "c"]
13
13
 
14
14
  @staticmethod
15
15
  def check() -> bool:
@@ -80,12 +80,7 @@ class CPPLSP(BaseLSP):
80
80
  "position": {"line": position[0], "character": position[1]}
81
81
  })
82
82
  return result[0] if result else None
83
-
84
- def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
85
- result = self._send_request("textDocument/documentSymbol", {
86
- "textDocument": {"uri": f"file://{file_path}"}
87
- })
88
- return result or [] # type: ignore
83
+
89
84
 
90
85
  def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
91
86
  # Send didOpen notification to trigger diagnostics
@@ -107,13 +102,6 @@ class CPPLSP(BaseLSP):
107
102
  pass
108
103
  return []
109
104
 
110
- def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
111
- result = self._send_request("textDocument/prepareRename", {
112
- "textDocument": {"uri": f"file://{file_path}"},
113
- "position": {"line": position[0], "character": position[1]}
114
- })
115
- return result
116
-
117
105
 
118
106
  def shutdown(self):
119
107
  if self.clangd_process:
jarvis/jarvis_lsp/go.py CHANGED
@@ -87,12 +87,6 @@ class GoLSP(BaseLSP):
87
87
  })
88
88
  return result[0] if result else None
89
89
 
90
- def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
91
- result = self._send_request("textDocument/documentSymbol", {
92
- "textDocument": {"uri": f"file://{file_path}"}
93
- })
94
- return result or [] # type: ignore
95
-
96
90
  def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
97
91
  # Send didOpen notification to trigger diagnostics
98
92
  self._send_request("textDocument/didOpen", {
@@ -113,13 +107,6 @@ class GoLSP(BaseLSP):
113
107
  pass
114
108
  return []
115
109
 
116
- def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
117
- result = self._send_request("textDocument/prepareRename", {
118
- "textDocument": {"uri": f"file://{file_path}"},
119
- "position": {"line": position[0], "character": position[1]}
120
- })
121
- return result
122
-
123
110
 
124
111
  def shutdown(self):
125
112
  if self.gopls_process:
@@ -18,7 +18,7 @@ class PythonLSP(BaseLSP):
18
18
  def _get_script(self, file_path: str):
19
19
  if file_path not in self.script_cache:
20
20
  try:
21
- with open(file_path, 'r') as f:
21
+ with open(file_path, 'r', errors="ignore") as f:
22
22
  content = f.read()
23
23
  self.script_cache[file_path] = jedi.Script(code=content, path=file_path)
24
24
  except Exception:
@@ -54,16 +54,6 @@ class PythonLSP(BaseLSP):
54
54
  }
55
55
  }
56
56
 
57
- def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
58
- script = self._get_script(file_path)
59
- if not script:
60
- return []
61
- try:
62
- names = script.get_names()
63
- return [self._location_to_dict(name) for name in names]
64
- except Exception:
65
- return []
66
-
67
57
  def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
68
58
  script = self._get_script(file_path)
69
59
  if not script:
@@ -82,24 +72,5 @@ class PythonLSP(BaseLSP):
82
72
  except Exception:
83
73
  return []
84
74
 
85
- def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
86
- script = self._get_script(file_path)
87
- if not script:
88
- return None
89
- try:
90
- refs = script.get_references(line=position[0] + 1, column=position[1])
91
- if refs:
92
- ref = refs[0]
93
- return {
94
- "range": {
95
- "start": {"line": ref.line - 1, "character": ref.column},
96
- "end": {"line": ref.line - 1, "character": ref.column + len(ref.name)}
97
- }
98
- }
99
- except Exception:
100
- return None
101
- return None
102
-
103
-
104
75
  def shutdown(self):
105
76
  self.script_cache.clear()