jarvis-ai-assistant 0.1.130__py3-none-any.whl → 0.1.132__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +71 -38
- 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 +77 -55
- jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +1 -2
- jarvis/jarvis_code_agent/code_agent.py +93 -88
- jarvis/jarvis_dev/main.py +335 -626
- jarvis/jarvis_git_squash/main.py +11 -32
- 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 +20 -29
- jarvis/jarvis_platform/ai8.py +7 -32
- jarvis/jarvis_platform/base.py +2 -7
- jarvis/jarvis_platform/kimi.py +3 -144
- jarvis/jarvis_platform/ollama.py +54 -68
- jarvis/jarvis_platform/openai.py +0 -4
- jarvis/jarvis_platform/oyi.py +0 -75
- jarvis/jarvis_platform/registry.py +1 -1
- jarvis/jarvis_platform/yuanbao.py +264 -0
- jarvis/jarvis_platform_manager/main.py +3 -3
- jarvis/jarvis_rag/file_processors.py +138 -0
- jarvis/jarvis_rag/main.py +1305 -425
- jarvis/jarvis_tools/ask_codebase.py +227 -41
- jarvis/jarvis_tools/code_review.py +229 -166
- jarvis/jarvis_tools/create_code_agent.py +76 -72
- jarvis/jarvis_tools/create_sub_agent.py +32 -15
- jarvis/jarvis_tools/execute_python_script.py +58 -0
- jarvis/jarvis_tools/execute_shell.py +15 -28
- jarvis/jarvis_tools/execute_shell_script.py +2 -2
- jarvis/jarvis_tools/file_analyzer.py +271 -0
- jarvis/jarvis_tools/file_operation.py +3 -3
- jarvis/jarvis_tools/find_caller.py +213 -0
- jarvis/jarvis_tools/find_symbol.py +211 -0
- jarvis/jarvis_tools/function_analyzer.py +248 -0
- jarvis/jarvis_tools/git_commiter.py +89 -70
- 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 +89 -48
- jarvis/jarvis_tools/project_analyzer.py +220 -0
- jarvis/jarvis_tools/read_code.py +24 -3
- jarvis/jarvis_tools/read_webpage.py +195 -81
- jarvis/jarvis_tools/registry.py +132 -11
- jarvis/jarvis_tools/search_web.py +73 -30
- jarvis/jarvis_tools/tool_generator.py +7 -9
- jarvis/jarvis_utils/__init__.py +1 -0
- jarvis/jarvis_utils/config.py +67 -3
- jarvis/jarvis_utils/embedding.py +344 -45
- jarvis/jarvis_utils/git_utils.py +18 -2
- jarvis/jarvis_utils/input.py +7 -4
- jarvis/jarvis_utils/methodology.py +379 -7
- jarvis/jarvis_utils/output.py +5 -3
- jarvis/jarvis_utils/utils.py +62 -10
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/METADATA +3 -4
- jarvis_ai_assistant-0.1.132.dist-info/RECORD +82 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/entry_points.txt +2 -0
- jarvis/jarvis_c2rust/c2rust.yaml +0 -734
- jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
- jarvis/jarvis_codebase/__init__.py +0 -0
- jarvis/jarvis_codebase/main.py +0 -1011
- 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.132.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/top_level.txt +0 -0
|
@@ -11,7 +11,12 @@ class LSPGetDiagnosticsTool:
|
|
|
11
11
|
# 工具参数定义
|
|
12
12
|
parameters = {
|
|
13
13
|
"file_path": "Path to the file to analyze",
|
|
14
|
-
"language": f"Programming language of the file ({', '.join(LSPRegistry.get_global_lsp_registry().get_supported_languages())})"
|
|
14
|
+
"language": f"Programming language of the file ({', '.join(LSPRegistry.get_global_lsp_registry().get_supported_languages())})",
|
|
15
|
+
"root_dir": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Root directory for LSP operations (optional)",
|
|
18
|
+
"default": "."
|
|
19
|
+
}
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
@staticmethod
|
|
@@ -24,6 +29,7 @@ class LSPGetDiagnosticsTool:
|
|
|
24
29
|
"""执行工具的主要逻辑"""
|
|
25
30
|
file_path = args.get("file_path", "")
|
|
26
31
|
language = args.get("language", "")
|
|
32
|
+
root_dir = args.get("root_dir", ".")
|
|
27
33
|
|
|
28
34
|
# 验证输入参数
|
|
29
35
|
if not all([file_path, language]):
|
|
@@ -41,89 +47,99 @@ class LSPGetDiagnosticsTool:
|
|
|
41
47
|
"stdout": ""
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
lsp = registry.create_lsp(language)
|
|
50
|
+
# 存储当前目录
|
|
51
|
+
original_dir = os.getcwd()
|
|
47
52
|
|
|
48
|
-
# 检查语言是否支持
|
|
49
|
-
if not lsp:
|
|
50
|
-
return {
|
|
51
|
-
"success": False,
|
|
52
|
-
"stderr": f"No LSP support for language: {language}",
|
|
53
|
-
"stdout": ""
|
|
54
|
-
}
|
|
55
|
-
|
|
56
53
|
try:
|
|
57
|
-
#
|
|
58
|
-
|
|
54
|
+
# 切换到root_dir
|
|
55
|
+
os.chdir(root_dir)
|
|
56
|
+
|
|
57
|
+
# 获取LSP实例
|
|
58
|
+
registry = LSPRegistry.get_global_lsp_registry()
|
|
59
|
+
lsp = registry.create_lsp(language)
|
|
60
|
+
|
|
61
|
+
# 检查语言是否支持
|
|
62
|
+
if not lsp:
|
|
59
63
|
return {
|
|
60
64
|
"success": False,
|
|
61
|
-
"stderr": "LSP
|
|
65
|
+
"stderr": f"No LSP support for language: {language}",
|
|
62
66
|
"stdout": ""
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
try:
|
|
70
|
+
# 初始化LSP
|
|
71
|
+
if not lsp.initialize(os.path.abspath(os.getcwd())):
|
|
72
|
+
return {
|
|
73
|
+
"success": False,
|
|
74
|
+
"stderr": "LSP initialization failed",
|
|
75
|
+
"stdout": ""
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# 获取诊断信息
|
|
79
|
+
diagnostics = lsp.get_diagnostics(file_path)
|
|
80
|
+
|
|
81
|
+
# 如果没有诊断信息
|
|
82
|
+
if not diagnostics:
|
|
83
|
+
return {
|
|
84
|
+
"success": True,
|
|
85
|
+
"stdout": "No issues found in the file",
|
|
86
|
+
"stderr": ""
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# 格式化输出
|
|
90
|
+
output = ["Diagnostics:"]
|
|
91
|
+
# 严重程度映射
|
|
92
|
+
severity_map = {1: "Error", 2: "Warning", 3: "Info", 4: "Hint"}
|
|
93
|
+
|
|
94
|
+
# 按严重程度和行号排序诊断信息
|
|
95
|
+
sorted_diagnostics = sorted(
|
|
96
|
+
diagnostics,
|
|
97
|
+
key=lambda x: (x["severity"], x["range"]["start"]["line"])
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# 处理每个诊断信息
|
|
101
|
+
for diag in sorted_diagnostics:
|
|
102
|
+
severity = severity_map.get(diag["severity"], "Unknown")
|
|
103
|
+
start = diag["range"]["start"]
|
|
104
|
+
line = LSPRegistry.get_line_at_position(file_path, start["line"]).strip()
|
|
105
|
+
|
|
106
|
+
output.extend([
|
|
107
|
+
f"\n{severity} at line {start['line'] + 1}, column {start['character'] + 1}:",
|
|
108
|
+
f"Message: {diag['message']}",
|
|
109
|
+
f"Code: {line}",
|
|
110
|
+
"-" * 60
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
# 处理相关附加信息
|
|
114
|
+
if diag.get("relatedInformation"):
|
|
115
|
+
output.append("Related information:")
|
|
116
|
+
for info in diag["relatedInformation"]:
|
|
117
|
+
info_line = LSPRegistry.get_line_at_position(
|
|
118
|
+
info["location"]["uri"],
|
|
119
|
+
info["location"]["range"]["start"]["line"]
|
|
120
|
+
).strip()
|
|
121
|
+
output.extend([
|
|
122
|
+
f" - {info['message']}",
|
|
123
|
+
f" at {info['location']['uri']}:{info['location']['range']['start']['line'] + 1}",
|
|
124
|
+
f" {info_line}"
|
|
125
|
+
])
|
|
126
|
+
|
|
70
127
|
return {
|
|
71
128
|
"success": True,
|
|
72
|
-
"stdout": "
|
|
129
|
+
"stdout": "\n".join(output),
|
|
73
130
|
"stderr": ""
|
|
74
131
|
}
|
|
75
132
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# 处理每个诊断信息
|
|
88
|
-
for diag in sorted_diagnostics:
|
|
89
|
-
severity = severity_map.get(diag["severity"], "Unknown")
|
|
90
|
-
start = diag["range"]["start"]
|
|
91
|
-
line = LSPRegistry.get_line_at_position(file_path, start["line"]).strip()
|
|
92
|
-
|
|
93
|
-
output.extend([
|
|
94
|
-
f"\n{severity} at line {start['line'] + 1}, column {start['character'] + 1}:",
|
|
95
|
-
f"Message: {diag['message']}",
|
|
96
|
-
f"Code: {line}",
|
|
97
|
-
"-" * 60
|
|
98
|
-
])
|
|
99
|
-
|
|
100
|
-
# 处理相关附加信息
|
|
101
|
-
if diag.get("relatedInformation"):
|
|
102
|
-
output.append("Related information:")
|
|
103
|
-
for info in diag["relatedInformation"]:
|
|
104
|
-
info_line = LSPRegistry.get_line_at_position(
|
|
105
|
-
info["location"]["uri"],
|
|
106
|
-
info["location"]["range"]["start"]["line"]
|
|
107
|
-
).strip()
|
|
108
|
-
output.extend([
|
|
109
|
-
f" - {info['message']}",
|
|
110
|
-
f" at {info['location']['uri']}:{info['location']['range']['start']['line'] + 1}",
|
|
111
|
-
f" {info_line}"
|
|
112
|
-
])
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
"success": True,
|
|
116
|
-
"stdout": "\n".join(output),
|
|
117
|
-
"stderr": ""
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
except Exception as e:
|
|
121
|
-
return {
|
|
122
|
-
"success": False,
|
|
123
|
-
"stderr": f"Error getting diagnostics: {str(e)}",
|
|
124
|
-
"stdout": ""
|
|
125
|
-
}
|
|
133
|
+
except Exception as e:
|
|
134
|
+
return {
|
|
135
|
+
"success": False,
|
|
136
|
+
"stderr": f"Error getting diagnostics: {str(e)}",
|
|
137
|
+
"stdout": ""
|
|
138
|
+
}
|
|
139
|
+
finally:
|
|
140
|
+
# 确保关闭LSP连接
|
|
141
|
+
if lsp:
|
|
142
|
+
lsp.shutdown()
|
|
126
143
|
finally:
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
lsp.shutdown()
|
|
144
|
+
# 恢复原始目录
|
|
145
|
+
os.chdir(original_dir)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import
|
|
2
|
+
import json
|
|
3
|
+
import glob
|
|
4
|
+
import hashlib
|
|
3
5
|
from typing import Dict, Optional, Any
|
|
4
6
|
|
|
5
7
|
from jarvis.jarvis_utils.config import is_use_methodology
|
|
@@ -35,51 +37,68 @@ class MethodologyTool:
|
|
|
35
37
|
|
|
36
38
|
@staticmethod
|
|
37
39
|
def check()->bool:
|
|
38
|
-
"""
|
|
40
|
+
"""检查是否启用了方法论功能"""
|
|
39
41
|
return is_use_methodology()
|
|
40
42
|
|
|
41
43
|
def __init__(self):
|
|
42
|
-
"""
|
|
43
|
-
self.
|
|
44
|
-
self.
|
|
44
|
+
"""初始化经验管理工具"""
|
|
45
|
+
self.methodology_dir = os.path.expanduser("~/.jarvis/methodologies")
|
|
46
|
+
self._ensure_dir_exists()
|
|
45
47
|
|
|
46
|
-
def
|
|
47
|
-
"""
|
|
48
|
-
if not os.path.exists(self.
|
|
48
|
+
def _ensure_dir_exists(self):
|
|
49
|
+
"""确保方法论目录存在"""
|
|
50
|
+
if not os.path.exists(self.methodology_dir):
|
|
49
51
|
try:
|
|
50
|
-
|
|
51
|
-
yaml.safe_dump({}, f, allow_unicode=True)
|
|
52
|
+
os.makedirs(self.methodology_dir, exist_ok=True)
|
|
52
53
|
except Exception as e:
|
|
53
|
-
PrettyOutput.print(f"
|
|
54
|
-
|
|
55
|
-
def
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
PrettyOutput.print(f"创建方法论目录失败:{str(e)}", OutputType.ERROR)
|
|
55
|
+
|
|
56
|
+
def _get_methodology_file_path(self, problem_type: str) -> str:
|
|
57
|
+
"""
|
|
58
|
+
根据问题类型获取对应的方法论文件路径
|
|
59
|
+
|
|
60
|
+
参数:
|
|
61
|
+
problem_type: 问题类型
|
|
62
|
+
|
|
63
|
+
返回:
|
|
64
|
+
str: 方法论文件路径
|
|
65
|
+
"""
|
|
66
|
+
# 使用MD5哈希作为文件名,避免文件名中的特殊字符
|
|
67
|
+
safe_filename = hashlib.md5(problem_type.encode('utf-8')).hexdigest()
|
|
68
|
+
return os.path.join(self.methodology_dir, f"{safe_filename}.json")
|
|
63
69
|
|
|
64
|
-
def
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
def _load_methodologies(self) -> Dict[str, str]:
|
|
71
|
+
"""加载所有方法论"""
|
|
72
|
+
all_methodologies = {}
|
|
73
|
+
|
|
74
|
+
if not os.path.exists(self.methodology_dir):
|
|
75
|
+
return all_methodologies
|
|
76
|
+
|
|
77
|
+
for filepath in glob.glob(os.path.join(self.methodology_dir, "*.json")):
|
|
78
|
+
try:
|
|
79
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
80
|
+
methodology = json.load(f)
|
|
81
|
+
problem_type = methodology.get("problem_type", "")
|
|
82
|
+
content = methodology.get("content", "")
|
|
83
|
+
if problem_type and content:
|
|
84
|
+
all_methodologies[problem_type] = content
|
|
85
|
+
except Exception as e:
|
|
86
|
+
filename = os.path.basename(filepath)
|
|
87
|
+
PrettyOutput.print(f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING)
|
|
88
|
+
|
|
89
|
+
return all_methodologies
|
|
71
90
|
|
|
72
91
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
73
|
-
"""
|
|
92
|
+
"""执行管理方法论的操作
|
|
74
93
|
|
|
75
94
|
Args:
|
|
76
|
-
args:
|
|
77
|
-
- operation:
|
|
78
|
-
- problem_type:
|
|
79
|
-
- content:
|
|
95
|
+
args: 包含操作参数的字典
|
|
96
|
+
- operation: 操作类型 (delete/update/add)
|
|
97
|
+
- problem_type: 问题类型
|
|
98
|
+
- content: 方法论内容 (更新和添加时必填)
|
|
80
99
|
|
|
81
100
|
Returns:
|
|
82
|
-
Dict[str, Any]:
|
|
101
|
+
Dict[str, Any]: 包含执行结果的字典
|
|
83
102
|
"""
|
|
84
103
|
operation = args.get("operation", "").strip()
|
|
85
104
|
problem_type = args.get("problem_type", "").strip()
|
|
@@ -92,16 +111,18 @@ class MethodologyTool:
|
|
|
92
111
|
"stderr": "Missing required parameters: operation and problem_type"
|
|
93
112
|
}
|
|
94
113
|
|
|
95
|
-
methodologies = self._load_methodologies()
|
|
96
|
-
|
|
97
114
|
try:
|
|
98
115
|
if operation == "delete":
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
# 获取方法论文件路径
|
|
117
|
+
file_path = self._get_methodology_file_path(problem_type)
|
|
118
|
+
|
|
119
|
+
# 检查文件是否存在
|
|
120
|
+
if os.path.exists(file_path):
|
|
121
|
+
os.remove(file_path)
|
|
102
122
|
return {
|
|
103
123
|
"success": True,
|
|
104
|
-
"stdout": f"Deleted methodology for problem type '{problem_type}'"
|
|
124
|
+
"stdout": f"Deleted methodology for problem type '{problem_type}'",
|
|
125
|
+
"stderr": ""
|
|
105
126
|
}
|
|
106
127
|
else:
|
|
107
128
|
return {
|
|
@@ -117,11 +138,21 @@ class MethodologyTool:
|
|
|
117
138
|
"stdout": "",
|
|
118
139
|
"stderr": "Need to provide methodology content"
|
|
119
140
|
}
|
|
120
|
-
|
|
121
|
-
methodologies[problem_type] = content
|
|
122
|
-
self._save_methodologies(methodologies)
|
|
123
141
|
|
|
124
|
-
|
|
142
|
+
# 确保目录存在
|
|
143
|
+
self._ensure_dir_exists()
|
|
144
|
+
|
|
145
|
+
# 获取方法论文件路径
|
|
146
|
+
file_path = self._get_methodology_file_path(problem_type)
|
|
147
|
+
|
|
148
|
+
# 保存方法论到单独的文件
|
|
149
|
+
with open(file_path, "w", encoding="utf-8", errors="ignore") as f:
|
|
150
|
+
json.dump({
|
|
151
|
+
"problem_type": problem_type,
|
|
152
|
+
"content": content
|
|
153
|
+
}, f, ensure_ascii=False, indent=2)
|
|
154
|
+
|
|
155
|
+
action = "Updated" if os.path.exists(file_path) else "Added"
|
|
125
156
|
return {
|
|
126
157
|
"success": True,
|
|
127
158
|
"stdout": f"{action} methodology for problem type '{problem_type}'",
|
|
@@ -143,13 +174,23 @@ class MethodologyTool:
|
|
|
143
174
|
}
|
|
144
175
|
|
|
145
176
|
def get_methodology(self, problem_type: str) -> Optional[str]:
|
|
146
|
-
"""
|
|
177
|
+
"""获取特定问题类型的方法论
|
|
147
178
|
|
|
148
179
|
Args:
|
|
149
|
-
problem_type:
|
|
180
|
+
problem_type: 问题类型
|
|
150
181
|
|
|
151
182
|
Returns:
|
|
152
|
-
Optional[str]:
|
|
183
|
+
Optional[str]: 方法论内容,如果不存在则返回 None
|
|
153
184
|
"""
|
|
154
|
-
|
|
155
|
-
|
|
185
|
+
file_path = self._get_methodology_file_path(problem_type)
|
|
186
|
+
|
|
187
|
+
if not os.path.exists(file_path):
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
192
|
+
methodology = json.load(f)
|
|
193
|
+
return methodology.get("content")
|
|
194
|
+
except Exception as e:
|
|
195
|
+
PrettyOutput.print(f"读取方法论失败: {str(e)}", OutputType.ERROR)
|
|
196
|
+
return None
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from typing import Dict, Any, List
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from jarvis.jarvis_agent import Agent
|
|
5
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
6
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProjectAnalyzerTool:
|
|
10
|
+
"""
|
|
11
|
+
项目分析工具
|
|
12
|
+
使用agent分析项目结构、入口点、模块划分等信息
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
name = "project_analyzer"
|
|
16
|
+
description = "分析项目结构、入口点、模块划分等信息,提供项目概览"
|
|
17
|
+
parameters = {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"root_dir": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "项目根目录路径(可选)",
|
|
23
|
+
"default": "."
|
|
24
|
+
},
|
|
25
|
+
"focus_dirs": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"description": "要重点分析的目录列表(可选)",
|
|
31
|
+
"default": []
|
|
32
|
+
},
|
|
33
|
+
"exclude_dirs": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"description": "要排除的目录列表(可选)",
|
|
39
|
+
"default": []
|
|
40
|
+
},
|
|
41
|
+
"objective": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "描述本次项目分析的目标和用途,例如'理解项目架构以便进行重构'或'寻找性能瓶颈'",
|
|
44
|
+
"default": ""
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": []
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
执行项目分析工具
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
args: 包含参数的字典
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
包含执行结果的字典
|
|
59
|
+
"""
|
|
60
|
+
# 存储原始目录
|
|
61
|
+
original_dir = os.getcwd()
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# 解析参数
|
|
65
|
+
root_dir = args.get("root_dir", ".")
|
|
66
|
+
focus_dirs = args.get("focus_dirs", [])
|
|
67
|
+
exclude_dirs = args.get("exclude_dirs", [])
|
|
68
|
+
objective = args.get("objective", "")
|
|
69
|
+
|
|
70
|
+
# 创建agent的system prompt
|
|
71
|
+
system_prompt = self._create_system_prompt(
|
|
72
|
+
root_dir, focus_dirs, exclude_dirs, objective
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# 创建agent的summary prompt
|
|
76
|
+
summary_prompt = self._create_summary_prompt(root_dir, objective)
|
|
77
|
+
|
|
78
|
+
# 切换到根目录
|
|
79
|
+
os.chdir(root_dir)
|
|
80
|
+
|
|
81
|
+
# 构建使用的工具
|
|
82
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
83
|
+
tool_registry = ToolRegistry()
|
|
84
|
+
tool_registry.use_tools([
|
|
85
|
+
"execute_shell",
|
|
86
|
+
"read_code",
|
|
87
|
+
"find_symbol",
|
|
88
|
+
"function_analyzer",
|
|
89
|
+
"find_caller",
|
|
90
|
+
"file_analyzer",
|
|
91
|
+
"ask_codebase"
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
# 创建并运行agent
|
|
95
|
+
analyzer_agent = Agent(
|
|
96
|
+
system_prompt=system_prompt,
|
|
97
|
+
name=f"ProjectAnalyzer",
|
|
98
|
+
description=f"分析项目结构、模块划分和关键组件",
|
|
99
|
+
summary_prompt=summary_prompt,
|
|
100
|
+
platform=PlatformRegistry().get_codegen_platform(),
|
|
101
|
+
output_handler=[tool_registry],
|
|
102
|
+
need_summary=True,
|
|
103
|
+
is_sub_agent=True,
|
|
104
|
+
use_methodology=False,
|
|
105
|
+
record_methodology=False,
|
|
106
|
+
execute_tool_confirm=False,
|
|
107
|
+
auto_complete=True
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# 运行agent并获取结果
|
|
111
|
+
task_input = f"分析项目结构、入口点、模块划分等信息,提供项目概览"
|
|
112
|
+
result = analyzer_agent.run(task_input)
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
"success": True,
|
|
116
|
+
"stdout": result,
|
|
117
|
+
"stderr": ""
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
122
|
+
return {
|
|
123
|
+
"success": False,
|
|
124
|
+
"stdout": "",
|
|
125
|
+
"stderr": f"项目分析失败: {str(e)}"
|
|
126
|
+
}
|
|
127
|
+
finally:
|
|
128
|
+
# 恢复原始目录
|
|
129
|
+
os.chdir(original_dir)
|
|
130
|
+
|
|
131
|
+
def _create_system_prompt(self, root_dir: str, focus_dirs: List[str],
|
|
132
|
+
exclude_dirs: List[str], objective: str) -> str:
|
|
133
|
+
"""
|
|
134
|
+
创建Agent的system prompt
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
root_dir: 项目根目录
|
|
138
|
+
focus_dirs: 重点分析的目录列表
|
|
139
|
+
exclude_dirs: 排除的目录列表
|
|
140
|
+
objective: 分析目标
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
系统提示文本
|
|
144
|
+
"""
|
|
145
|
+
focus_dirs_str = ", ".join(focus_dirs) if focus_dirs else "整个项目"
|
|
146
|
+
exclude_dirs_str = ", ".join(exclude_dirs) if exclude_dirs else "无"
|
|
147
|
+
|
|
148
|
+
objective_text = f"\n\n## 分析目标\n{objective}" if objective else "\n\n## 分析目标\n全面了解项目结构、模块划分和关键组件"
|
|
149
|
+
|
|
150
|
+
return f"""# 项目架构分析专家
|
|
151
|
+
|
|
152
|
+
## 任务描述
|
|
153
|
+
对项目 `{root_dir}` 进行针对性分析,专注于分析目标所需的内容,生成有针对性、深入且有洞察力的项目分析报告。{objective_text}
|
|
154
|
+
|
|
155
|
+
## 分析范围
|
|
156
|
+
- 项目根目录: `{root_dir}`
|
|
157
|
+
- 重点分析: {focus_dirs_str}
|
|
158
|
+
- 排除目录: {exclude_dirs_str}
|
|
159
|
+
|
|
160
|
+
## 分析策略
|
|
161
|
+
1. 首先理解分析目标,确定你需要寻找什么信息
|
|
162
|
+
2. 灵活采用适合目标的分析方法,不受预设分析框架的限制
|
|
163
|
+
3. 有选择地探索项目,只关注与目标直接相关的部分
|
|
164
|
+
4. 根据目标需要自行判断分析的深度和广度
|
|
165
|
+
|
|
166
|
+
## 探索命令示例
|
|
167
|
+
```bash
|
|
168
|
+
# 获取项目文件结构
|
|
169
|
+
find . -type f -not -path "*/\\.*" | sort
|
|
170
|
+
|
|
171
|
+
# 查找可能的入口点
|
|
172
|
+
find . -name "main.*" -o -name "app.*" -o -name "index.*"
|
|
173
|
+
|
|
174
|
+
# 分析配置文件
|
|
175
|
+
find . -name "*.json" -o -name "*.yaml" -o -name "*.toml" -o -name "*.ini" -o -name "*.conf"
|
|
176
|
+
|
|
177
|
+
# 查找核心模块
|
|
178
|
+
find . -name "core.*" -o -name "*core*" -o -name "main.*" -o -name "api.*"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## 分析工具使用
|
|
182
|
+
- 使用`file_analyzer`分析关键文件结构和功能
|
|
183
|
+
- 使用`find_symbol`定位和分析重要符号和函数
|
|
184
|
+
- 使用`function_analyzer`深入理解复杂函数的实现
|
|
185
|
+
- 使用`find_caller`追踪函数调用关系和依赖
|
|
186
|
+
|
|
187
|
+
## 分析输出要求
|
|
188
|
+
- 直接回应分析目标的关键问题
|
|
189
|
+
- 提供与目标相关的深入洞察
|
|
190
|
+
- 分析内容应直接服务于分析目标
|
|
191
|
+
- 避免与目标无关的冗余信息
|
|
192
|
+
- 使用具体代码路径和示例支持分析结论
|
|
193
|
+
- 提供针对分析目标的具体建议和改进方向"""
|
|
194
|
+
|
|
195
|
+
def _create_summary_prompt(self, root_dir: str, objective: str) -> str:
|
|
196
|
+
"""
|
|
197
|
+
创建Agent的summary prompt
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
root_dir: 项目根目录
|
|
201
|
+
objective: 分析目标
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
总结提示文本
|
|
205
|
+
"""
|
|
206
|
+
objective_text = f"\n\n## 具体分析目标\n{objective}" if objective else ""
|
|
207
|
+
|
|
208
|
+
return f"""# 项目分析报告: `{root_dir}`{objective_text}
|
|
209
|
+
|
|
210
|
+
## 报告要求
|
|
211
|
+
生成一份完全以分析目标为导向的项目分析报告。不要遵循固定的报告模板,而是完全根据分析目标来组织内容:
|
|
212
|
+
|
|
213
|
+
- 专注回答分析目标提出的问题
|
|
214
|
+
- 只包含与分析目标直接相关的发现和洞察
|
|
215
|
+
- 完全跳过与分析目标无关的内容,无需做全面分析
|
|
216
|
+
- 分析深度应与目标的具体需求匹配
|
|
217
|
+
- 使用具体的代码路径和示例支持你的观点
|
|
218
|
+
- 以清晰的Markdown格式呈现,简洁明了
|
|
219
|
+
|
|
220
|
+
在分析中保持灵活性,避免固定思维模式。你的任务不是提供全面的项目概览,而是直接解决分析目标中提出的具体问题。"""
|
jarvis/jarvis_tools/read_code.py
CHANGED
|
@@ -7,7 +7,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
7
7
|
|
|
8
8
|
class ReadCodeTool:
|
|
9
9
|
name = "read_code"
|
|
10
|
-
description = "
|
|
10
|
+
description = "代码阅读与分析工具,用于读取源代码文件并添加行号,针对代码文件优化,提供更好的格式化输出和行号显示,适用于代码分析、审查和理解代码实现的场景"
|
|
11
11
|
parameters = {
|
|
12
12
|
"type": "object",
|
|
13
13
|
"properties": {
|
|
@@ -29,6 +29,16 @@ class ReadCodeTool:
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
def _handle_single_file(self, filepath: str, start_line: int = 1, end_line: int = -1) -> Dict[str, Any]:
|
|
32
|
+
"""处理单个文件的读取操作
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
filepath (str): 文件路径
|
|
36
|
+
start_line (int): 起始行号,默认为1
|
|
37
|
+
end_line (int): 结束行号,默认为-1表示文件末尾
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Dict[str, Any]: 包含成功状态、输出内容和错误信息的字典
|
|
41
|
+
"""
|
|
32
42
|
try:
|
|
33
43
|
abs_path = os.path.abspath(filepath)
|
|
34
44
|
with yaspin(text=f"正在读取文件: {abs_path}...", color="cyan") as spinner:
|
|
@@ -49,7 +59,7 @@ class ReadCodeTool:
|
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
# 读取文件内容
|
|
52
|
-
with open(abs_path, 'r', encoding='utf-8') as f:
|
|
62
|
+
with open(abs_path, 'r', encoding='utf-8', errors="ignore") as f:
|
|
53
63
|
lines = f.readlines()
|
|
54
64
|
|
|
55
65
|
total_lines = len(lines)
|
|
@@ -73,7 +83,7 @@ class ReadCodeTool:
|
|
|
73
83
|
# 添加行号并构建输出内容
|
|
74
84
|
selected_lines = lines[start_line-1:end_line]
|
|
75
85
|
numbered_content = "".join(
|
|
76
|
-
[f"{i:4d}
|
|
86
|
+
[f"{i:4d}:{line}"
|
|
77
87
|
for i, line in enumerate(selected_lines, start=start_line)]
|
|
78
88
|
)
|
|
79
89
|
|
|
@@ -86,6 +96,9 @@ class ReadCodeTool:
|
|
|
86
96
|
)
|
|
87
97
|
spinner.text = f"文件读取完成: {abs_path}"
|
|
88
98
|
spinner.ok("✅")
|
|
99
|
+
|
|
100
|
+
PrettyOutput.print(output, OutputType.SUCCESS)
|
|
101
|
+
|
|
89
102
|
return {
|
|
90
103
|
"success": True,
|
|
91
104
|
"stdout": output,
|
|
@@ -101,6 +114,14 @@ class ReadCodeTool:
|
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
117
|
+
"""执行代码读取操作
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
args (Dict): 包含文件列表的参数字典
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Dict[str, Any]: 包含成功状态、输出内容和错误信息的字典
|
|
124
|
+
"""
|
|
104
125
|
try:
|
|
105
126
|
if "files" not in args or not isinstance(args["files"], list):
|
|
106
127
|
return {
|