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.
Files changed (72) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +71 -38
  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 +77 -55
  7. jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +1 -2
  8. jarvis/jarvis_code_agent/code_agent.py +93 -88
  9. jarvis/jarvis_dev/main.py +335 -626
  10. jarvis/jarvis_git_squash/main.py +11 -32
  11. jarvis/jarvis_lsp/base.py +2 -26
  12. jarvis/jarvis_lsp/cpp.py +2 -14
  13. jarvis/jarvis_lsp/go.py +0 -13
  14. jarvis/jarvis_lsp/python.py +1 -30
  15. jarvis/jarvis_lsp/registry.py +10 -14
  16. jarvis/jarvis_lsp/rust.py +0 -12
  17. jarvis/jarvis_multi_agent/__init__.py +20 -29
  18. jarvis/jarvis_platform/ai8.py +7 -32
  19. jarvis/jarvis_platform/base.py +2 -7
  20. jarvis/jarvis_platform/kimi.py +3 -144
  21. jarvis/jarvis_platform/ollama.py +54 -68
  22. jarvis/jarvis_platform/openai.py +0 -4
  23. jarvis/jarvis_platform/oyi.py +0 -75
  24. jarvis/jarvis_platform/registry.py +1 -1
  25. jarvis/jarvis_platform/yuanbao.py +264 -0
  26. jarvis/jarvis_platform_manager/main.py +3 -3
  27. jarvis/jarvis_rag/file_processors.py +138 -0
  28. jarvis/jarvis_rag/main.py +1305 -425
  29. jarvis/jarvis_tools/ask_codebase.py +227 -41
  30. jarvis/jarvis_tools/code_review.py +229 -166
  31. jarvis/jarvis_tools/create_code_agent.py +76 -72
  32. jarvis/jarvis_tools/create_sub_agent.py +32 -15
  33. jarvis/jarvis_tools/execute_python_script.py +58 -0
  34. jarvis/jarvis_tools/execute_shell.py +15 -28
  35. jarvis/jarvis_tools/execute_shell_script.py +2 -2
  36. jarvis/jarvis_tools/file_analyzer.py +271 -0
  37. jarvis/jarvis_tools/file_operation.py +3 -3
  38. jarvis/jarvis_tools/find_caller.py +213 -0
  39. jarvis/jarvis_tools/find_symbol.py +211 -0
  40. jarvis/jarvis_tools/function_analyzer.py +248 -0
  41. jarvis/jarvis_tools/git_commiter.py +89 -70
  42. jarvis/jarvis_tools/lsp_find_definition.py +83 -67
  43. jarvis/jarvis_tools/lsp_find_references.py +62 -46
  44. jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
  45. jarvis/jarvis_tools/methodology.py +89 -48
  46. jarvis/jarvis_tools/project_analyzer.py +220 -0
  47. jarvis/jarvis_tools/read_code.py +24 -3
  48. jarvis/jarvis_tools/read_webpage.py +195 -81
  49. jarvis/jarvis_tools/registry.py +132 -11
  50. jarvis/jarvis_tools/search_web.py +73 -30
  51. jarvis/jarvis_tools/tool_generator.py +7 -9
  52. jarvis/jarvis_utils/__init__.py +1 -0
  53. jarvis/jarvis_utils/config.py +67 -3
  54. jarvis/jarvis_utils/embedding.py +344 -45
  55. jarvis/jarvis_utils/git_utils.py +18 -2
  56. jarvis/jarvis_utils/input.py +7 -4
  57. jarvis/jarvis_utils/methodology.py +379 -7
  58. jarvis/jarvis_utils/output.py +5 -3
  59. jarvis/jarvis_utils/utils.py +62 -10
  60. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/METADATA +3 -4
  61. jarvis_ai_assistant-0.1.132.dist-info/RECORD +82 -0
  62. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/entry_points.txt +2 -0
  63. jarvis/jarvis_c2rust/c2rust.yaml +0 -734
  64. jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
  65. jarvis/jarvis_codebase/__init__.py +0 -0
  66. jarvis/jarvis_codebase/main.py +0 -1011
  67. jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
  68. jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
  69. jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
  70. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/LICENSE +0 -0
  71. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/WHEEL +0 -0
  72. {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
- # 获取LSP实例
45
- registry = LSPRegistry.get_global_lsp_registry()
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
- # 初始化LSP
58
- if not lsp.initialize(os.path.abspath(os.getcwd())):
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 initialization failed",
65
+ "stderr": f"No LSP support for language: {language}",
62
66
  "stdout": ""
63
67
  }
64
68
 
65
- # 获取诊断信息
66
- diagnostics = lsp.get_diagnostics(file_path)
67
-
68
- # 如果没有诊断信息
69
- if not diagnostics:
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": "No issues found in the file",
129
+ "stdout": "\n".join(output),
73
130
  "stderr": ""
74
131
  }
75
132
 
76
- # 格式化输出
77
- output = ["Diagnostics:"]
78
- # 严重程度映射
79
- severity_map = {1: "Error", 2: "Warning", 3: "Info", 4: "Hint"}
80
-
81
- # 按严重程度和行号排序诊断信息
82
- sorted_diagnostics = sorted(
83
- diagnostics,
84
- key=lambda x: (x["severity"], x["range"]["start"]["line"])
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
- # 确保关闭LSP连接
128
- if lsp:
129
- lsp.shutdown()
144
+ # 恢复原始目录
145
+ os.chdir(original_dir)
@@ -1,5 +1,7 @@
1
1
  import os
2
- import yaml
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
- """Check if the methodology is enabled"""
40
+ """检查是否启用了方法论功能"""
39
41
  return is_use_methodology()
40
42
 
41
43
  def __init__(self):
42
- """Initialize the experience management tool"""
43
- self.methodology_file = os.path.expanduser("~/.jarvis/methodology")
44
- self._ensure_file_exists()
44
+ """初始化经验管理工具"""
45
+ self.methodology_dir = os.path.expanduser("~/.jarvis/methodologies")
46
+ self._ensure_dir_exists()
45
47
 
46
- def _ensure_file_exists(self):
47
- """Ensure the methodology file exists"""
48
- if not os.path.exists(self.methodology_file):
48
+ def _ensure_dir_exists(self):
49
+ """确保方法论目录存在"""
50
+ if not os.path.exists(self.methodology_dir):
49
51
  try:
50
- with open(self.methodology_file, 'w', encoding='utf-8') as f:
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"创建方法论文件失败:{str(e)}", OutputType.ERROR)
54
-
55
- def _load_methodologies(self) -> Dict:
56
- """Load all methodologies"""
57
- try:
58
- with open(self.methodology_file, 'r', encoding='utf-8') as f:
59
- return yaml.safe_load(f) or {}
60
- except Exception as e:
61
- PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
62
- return {}
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 _save_methodologies(self, methodologies: Dict):
65
- """Save all methodologies"""
66
- try:
67
- with open(self.methodology_file, 'w', encoding='utf-8') as f:
68
- yaml.safe_dump(methodologies, f, allow_unicode=True)
69
- except Exception as e:
70
- PrettyOutput.print(f"保存方法论失败: {str(e)}", OutputType.ERROR)
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
- """Execute the operation of managing methodologies
92
+ """执行管理方法论的操作
74
93
 
75
94
  Args:
76
- args: A dictionary containing the operation parameters
77
- - operation: The operation type (delete/update/add)
78
- - problem_type: The problem type
79
- - content: The methodology content (required for update/add)
95
+ args: 包含操作参数的字典
96
+ - operation: 操作类型 (delete/update/add)
97
+ - problem_type: 问题类型
98
+ - content: 方法论内容 (更新和添加时必填)
80
99
 
81
100
  Returns:
82
- Dict[str, Any]: A dictionary containing the execution result
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
- if problem_type in methodologies:
100
- del methodologies[problem_type]
101
- self._save_methodologies(methodologies)
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
- action = "Update" if problem_type in methodologies else "Add"
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
- """Get the methodology for a specific problem type
177
+ """获取特定问题类型的方法论
147
178
 
148
179
  Args:
149
- problem_type: The problem type
180
+ problem_type: 问题类型
150
181
 
151
182
  Returns:
152
- Optional[str]: The methodology content, or None if it does not exist
183
+ Optional[str]: 方法论内容,如果不存在则返回 None
153
184
  """
154
- methodologies = self._load_methodologies()
155
- return methodologies.get(problem_type)
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
+ 在分析中保持灵活性,避免固定思维模式。你的任务不是提供全面的项目概览,而是直接解决分析目标中提出的具体问题。"""
@@ -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} | {line}"
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 {