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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +23 -9
- jarvis/jarvis_agent/builtin_input_handler.py +73 -0
- jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
- jarvis/jarvis_agent/main.py +1 -1
- jarvis/{jarvis_code_agent → jarvis_agent}/patch.py +23 -19
- jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +0 -1
- jarvis/jarvis_code_agent/code_agent.py +20 -16
- jarvis/jarvis_codebase/main.py +5 -5
- jarvis/jarvis_dev/main.py +1 -1
- jarvis/jarvis_git_squash/main.py +1 -1
- jarvis/jarvis_lsp/base.py +2 -26
- jarvis/jarvis_lsp/cpp.py +2 -14
- jarvis/jarvis_lsp/go.py +0 -13
- jarvis/jarvis_lsp/python.py +1 -30
- jarvis/jarvis_lsp/registry.py +10 -14
- jarvis/jarvis_lsp/rust.py +0 -12
- jarvis/jarvis_multi_agent/__init__.py +1 -1
- jarvis/jarvis_platform/registry.py +1 -1
- jarvis/jarvis_platform_manager/main.py +3 -3
- jarvis/jarvis_rag/main.py +1 -1
- jarvis/jarvis_tools/ask_codebase.py +40 -20
- jarvis/jarvis_tools/code_review.py +180 -143
- jarvis/jarvis_tools/create_code_agent.py +76 -72
- jarvis/jarvis_tools/create_sub_agent.py +32 -15
- jarvis/jarvis_tools/execute_shell.py +2 -2
- jarvis/jarvis_tools/execute_shell_script.py +1 -1
- jarvis/jarvis_tools/file_operation.py +2 -2
- jarvis/jarvis_tools/git_commiter.py +87 -68
- jarvis/jarvis_tools/lsp_find_definition.py +83 -67
- jarvis/jarvis_tools/lsp_find_references.py +62 -46
- jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
- jarvis/jarvis_tools/methodology.py +3 -3
- jarvis/jarvis_tools/read_code.py +1 -1
- jarvis/jarvis_tools/search_web.py +18 -20
- jarvis/jarvis_tools/tool_generator.py +1 -1
- jarvis/jarvis_tools/treesitter_analyzer.py +331 -0
- jarvis/jarvis_treesitter/README.md +104 -0
- jarvis/jarvis_treesitter/__init__.py +20 -0
- jarvis/jarvis_treesitter/database.py +258 -0
- jarvis/jarvis_treesitter/example.py +115 -0
- jarvis/jarvis_treesitter/grammar_builder.py +182 -0
- jarvis/jarvis_treesitter/language.py +117 -0
- jarvis/jarvis_treesitter/symbol.py +31 -0
- jarvis/jarvis_treesitter/tools_usage.md +121 -0
- jarvis/jarvis_utils/git_utils.py +10 -2
- jarvis/jarvis_utils/input.py +3 -1
- jarvis/jarvis_utils/methodology.py +1 -1
- jarvis/jarvis_utils/utils.py +3 -3
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/METADATA +2 -4
- jarvis_ai_assistant-0.1.131.dist-info/RECORD +85 -0
- jarvis/jarvis_c2rust/c2rust.yaml +0 -734
- jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
- jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
- jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
- jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/entry_points.txt +0 -0
- {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
jarvis/jarvis_agent/__init__.py
CHANGED
|
@@ -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.
|
|
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)
|
jarvis/jarvis_agent/main.py
CHANGED
|
@@ -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
|
-
|
|
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", "--
|
|
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) <
|
|
235
|
+
if get_file_line_count(filepath) < 100:
|
|
237
236
|
return handle_small_code_operation(filepath, patch_content)
|
|
238
237
|
else:
|
|
239
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
439
|
-
spinner.write(f"✅ 补丁 #{patch_count+1} 应用成功")
|
|
443
|
+
spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
|
|
440
444
|
else:
|
|
441
|
-
spinner.text = f"补丁 #{patch_count
|
|
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.
|
|
10
|
-
from jarvis.
|
|
11
|
-
from jarvis.
|
|
12
|
-
from jarvis.
|
|
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
|
-
"
|
|
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", "--
|
|
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({})
|
jarvis/jarvis_codebase/main.py
CHANGED
|
@@ -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", "
|
|
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"])
|
jarvis/jarvis_git_squash/main.py
CHANGED
|
@@ -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", "--
|
|
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:
|
jarvis/jarvis_lsp/python.py
CHANGED
|
@@ -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()
|