jarvis-ai-assistant 0.1.131__py3-none-any.whl → 0.1.134__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 +165 -285
- jarvis/jarvis_agent/jarvis.py +143 -0
- jarvis/jarvis_agent/main.py +0 -2
- jarvis/jarvis_agent/patch.py +70 -48
- jarvis/jarvis_agent/shell_input_handler.py +1 -1
- jarvis/jarvis_code_agent/code_agent.py +169 -117
- jarvis/jarvis_dev/main.py +327 -626
- jarvis/jarvis_git_squash/main.py +10 -31
- jarvis/jarvis_lsp/base.py +0 -42
- jarvis/jarvis_lsp/cpp.py +0 -15
- jarvis/jarvis_lsp/go.py +0 -15
- jarvis/jarvis_lsp/python.py +0 -19
- jarvis/jarvis_lsp/registry.py +0 -62
- jarvis/jarvis_lsp/rust.py +0 -15
- jarvis/jarvis_multi_agent/__init__.py +19 -69
- jarvis/jarvis_multi_agent/main.py +43 -0
- 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 +2 -16
- jarvis/jarvis_platform/yuanbao.py +264 -0
- jarvis/jarvis_rag/file_processors.py +138 -0
- jarvis/jarvis_rag/main.py +1305 -425
- jarvis/jarvis_tools/ask_codebase.py +216 -43
- jarvis/jarvis_tools/code_review.py +158 -113
- jarvis/jarvis_tools/create_sub_agent.py +0 -1
- jarvis/jarvis_tools/execute_python_script.py +58 -0
- jarvis/jarvis_tools/execute_shell.py +13 -26
- jarvis/jarvis_tools/execute_shell_script.py +1 -1
- jarvis/jarvis_tools/file_analyzer.py +282 -0
- jarvis/jarvis_tools/file_operation.py +1 -1
- jarvis/jarvis_tools/find_caller.py +278 -0
- jarvis/jarvis_tools/find_symbol.py +295 -0
- jarvis/jarvis_tools/function_analyzer.py +331 -0
- jarvis/jarvis_tools/git_commiter.py +5 -5
- jarvis/jarvis_tools/methodology.py +88 -53
- jarvis/jarvis_tools/project_analyzer.py +308 -0
- jarvis/jarvis_tools/rag.py +0 -5
- 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 +22 -307
- jarvis/jarvis_tools/tool_generator.py +8 -10
- jarvis/jarvis_utils/__init__.py +1 -0
- jarvis/jarvis_utils/config.py +80 -76
- jarvis/jarvis_utils/embedding.py +344 -45
- jarvis/jarvis_utils/git_utils.py +9 -1
- jarvis/jarvis_utils/input.py +7 -6
- jarvis/jarvis_utils/methodology.py +384 -15
- jarvis/jarvis_utils/output.py +5 -3
- jarvis/jarvis_utils/utils.py +60 -8
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/METADATA +8 -16
- jarvis_ai_assistant-0.1.134.dist-info/RECORD +82 -0
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/entry_points.txt +4 -3
- jarvis/jarvis_codebase/__init__.py +0 -0
- jarvis/jarvis_codebase/main.py +0 -1011
- jarvis/jarvis_tools/lsp_find_definition.py +0 -150
- jarvis/jarvis_tools/lsp_find_references.py +0 -127
- jarvis/jarvis_tools/treesitter_analyzer.py +0 -331
- jarvis/jarvis_treesitter/README.md +0 -104
- jarvis/jarvis_treesitter/__init__.py +0 -20
- jarvis/jarvis_treesitter/database.py +0 -258
- jarvis/jarvis_treesitter/example.py +0 -115
- jarvis/jarvis_treesitter/grammar_builder.py +0 -182
- jarvis/jarvis_treesitter/language.py +0 -117
- jarvis/jarvis_treesitter/symbol.py +0 -31
- jarvis/jarvis_treesitter/tools_usage.md +0 -121
- jarvis_ai_assistant-0.1.131.dist-info/RECORD +0 -85
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/top_level.txt +0 -0
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import Dict, Any
|
|
3
|
-
from jarvis.jarvis_lsp.registry import LSPRegistry
|
|
4
|
-
|
|
5
|
-
class LSPFindDefinitionTool:
|
|
6
|
-
"""使用LSP在代码中查找符号定义的工具"""
|
|
7
|
-
|
|
8
|
-
name = "lsp_find_definition"
|
|
9
|
-
description = "在代码中查找符号的定义"
|
|
10
|
-
parameters = {
|
|
11
|
-
"file_path": "包含符号的文件路径",
|
|
12
|
-
"line": "符号所在的行号(从0开始)",
|
|
13
|
-
"character": "符号在行中的字符位置",
|
|
14
|
-
"language": f"文件的编程语言({', '.join(LSPRegistry.get_global_lsp_registry().get_supported_languages())})",
|
|
15
|
-
"root_dir": {
|
|
16
|
-
"type": "string",
|
|
17
|
-
"description": "LSP操作的根目录路径(可选)",
|
|
18
|
-
"default": "."
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@staticmethod
|
|
23
|
-
def check() -> bool:
|
|
24
|
-
"""Check if any LSP server is available."""
|
|
25
|
-
registry = LSPRegistry.get_global_lsp_registry()
|
|
26
|
-
return len(registry.get_supported_languages()) > 0
|
|
27
|
-
|
|
28
|
-
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
29
|
-
"""Execute the tool."""
|
|
30
|
-
file_path = args.get("file_path", "")
|
|
31
|
-
line = args.get("line", None)
|
|
32
|
-
character = args.get("character", None)
|
|
33
|
-
language = args.get("language", "")
|
|
34
|
-
root_dir = args.get("root_dir", ".")
|
|
35
|
-
|
|
36
|
-
# Validate inputs
|
|
37
|
-
if not all([file_path, line is not None, character is not None, language]):
|
|
38
|
-
return {
|
|
39
|
-
"success": False,
|
|
40
|
-
"stderr": "All parameters (file_path, line, character, language) must be provided",
|
|
41
|
-
"stdout": ""
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
line = int(line)
|
|
46
|
-
character = int(character)
|
|
47
|
-
except ValueError:
|
|
48
|
-
return {
|
|
49
|
-
"success": False,
|
|
50
|
-
"stderr": "Line and character must be integers",
|
|
51
|
-
"stdout": ""
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if not os.path.exists(file_path):
|
|
55
|
-
return {
|
|
56
|
-
"success": False,
|
|
57
|
-
"stderr": f"File not found: {file_path}",
|
|
58
|
-
"stdout": ""
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Store current directory
|
|
62
|
-
original_dir = os.getcwd()
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
# Change to root_dir
|
|
66
|
-
os.chdir(root_dir)
|
|
67
|
-
|
|
68
|
-
# Get LSP instance
|
|
69
|
-
registry = LSPRegistry.get_global_lsp_registry()
|
|
70
|
-
lsp = registry.create_lsp(language)
|
|
71
|
-
|
|
72
|
-
if not lsp:
|
|
73
|
-
return {
|
|
74
|
-
"success": False,
|
|
75
|
-
"stderr": f"No LSP support for language: {language}",
|
|
76
|
-
"stdout": ""
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
try:
|
|
80
|
-
# Initialize LSP
|
|
81
|
-
if not lsp.initialize(os.path.abspath(os.getcwd())):
|
|
82
|
-
return {
|
|
83
|
-
"success": False,
|
|
84
|
-
"stderr": "LSP initialization failed",
|
|
85
|
-
"stdout": ""
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# Get symbol at position
|
|
89
|
-
symbol = LSPRegistry.get_text_at_position(file_path, line, character)
|
|
90
|
-
if not symbol:
|
|
91
|
-
return {
|
|
92
|
-
"success": False,
|
|
93
|
-
"stderr": f"No symbol found at position {line}:{character}",
|
|
94
|
-
"stdout": ""
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# Find definition
|
|
98
|
-
defn = lsp.find_definition(file_path, (line, character))
|
|
99
|
-
|
|
100
|
-
if not defn:
|
|
101
|
-
return {
|
|
102
|
-
"success": True,
|
|
103
|
-
"stdout": f"No definition found for '{symbol}'",
|
|
104
|
-
"stderr": ""
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
# Format output
|
|
108
|
-
def_line = defn["range"]["start"]["line"]
|
|
109
|
-
def_char = defn["range"]["start"]["character"]
|
|
110
|
-
context = LSPRegistry.get_line_at_position(defn["uri"], def_line).strip()
|
|
111
|
-
|
|
112
|
-
output = [
|
|
113
|
-
f"Definition of '{symbol}':",
|
|
114
|
-
f"File: {defn['uri']}",
|
|
115
|
-
f"Line {def_line + 1}, Col {def_char + 1}: {context}"
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
# Get a few lines of context around the definition
|
|
119
|
-
try:
|
|
120
|
-
with open(defn["uri"], 'r', errors="ignore") as f:
|
|
121
|
-
lines = f.readlines()
|
|
122
|
-
start = max(0, def_line - 2)
|
|
123
|
-
end = min(len(lines), def_line + 3)
|
|
124
|
-
|
|
125
|
-
if start < def_line:
|
|
126
|
-
output.append("\nContext:")
|
|
127
|
-
for i in range(start, end):
|
|
128
|
-
prefix = ">" if i == def_line else " "
|
|
129
|
-
output.append(f"{prefix} {i+1:4d} | {lines[i].rstrip()}")
|
|
130
|
-
except Exception:
|
|
131
|
-
pass
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
"success": True,
|
|
135
|
-
"stdout": "\n".join(output),
|
|
136
|
-
"stderr": ""
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
except Exception as e:
|
|
140
|
-
return {
|
|
141
|
-
"success": False,
|
|
142
|
-
"stderr": f"Error finding definition: {str(e)}",
|
|
143
|
-
"stdout": ""
|
|
144
|
-
}
|
|
145
|
-
finally:
|
|
146
|
-
if lsp:
|
|
147
|
-
lsp.shutdown()
|
|
148
|
-
finally:
|
|
149
|
-
# Always restore original directory
|
|
150
|
-
os.chdir(original_dir)
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import Dict, Any
|
|
3
|
-
from jarvis.jarvis_lsp.registry import LSPRegistry
|
|
4
|
-
|
|
5
|
-
class LSPFindReferencesTool:
|
|
6
|
-
"""Tool for finding references to symbols in code using LSP."""
|
|
7
|
-
|
|
8
|
-
name = "lsp_find_references"
|
|
9
|
-
description = "Find all references to a symbol in code"
|
|
10
|
-
parameters = {
|
|
11
|
-
"file_path": "Path to the file containing the symbol",
|
|
12
|
-
"line": "Line number (0-based) of the symbol",
|
|
13
|
-
"character": "Character position in the line",
|
|
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
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@staticmethod
|
|
23
|
-
def check() -> bool:
|
|
24
|
-
"""Check if any LSP server is available."""
|
|
25
|
-
registry = LSPRegistry.get_global_lsp_registry()
|
|
26
|
-
return len(registry.get_supported_languages()) > 0
|
|
27
|
-
|
|
28
|
-
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
29
|
-
"""Execute the tool."""
|
|
30
|
-
file_path = args.get("file_path", "")
|
|
31
|
-
line = args.get("line", None)
|
|
32
|
-
character = args.get("character", None)
|
|
33
|
-
language = args.get("language", "")
|
|
34
|
-
root_dir = args.get("root_dir", ".")
|
|
35
|
-
|
|
36
|
-
# Validate inputs
|
|
37
|
-
if not all([file_path, line is not None, character is not None, language]):
|
|
38
|
-
return {
|
|
39
|
-
"success": False,
|
|
40
|
-
"stderr": "All parameters (file_path, line, character, language) must be provided",
|
|
41
|
-
"stdout": ""
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
line = int(line)
|
|
46
|
-
character = int(character)
|
|
47
|
-
except ValueError:
|
|
48
|
-
return {
|
|
49
|
-
"success": False,
|
|
50
|
-
"stderr": "Line and character must be integers",
|
|
51
|
-
"stdout": ""
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if not os.path.exists(file_path):
|
|
55
|
-
return {
|
|
56
|
-
"success": False,
|
|
57
|
-
"stderr": f"File not found: {file_path}",
|
|
58
|
-
"stdout": ""
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Store current directory
|
|
62
|
-
original_dir = os.getcwd()
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
# Change to root_dir
|
|
66
|
-
os.chdir(root_dir)
|
|
67
|
-
|
|
68
|
-
# Get LSP instance
|
|
69
|
-
registry = LSPRegistry.get_global_lsp_registry()
|
|
70
|
-
lsp = registry.create_lsp(language)
|
|
71
|
-
|
|
72
|
-
if not lsp:
|
|
73
|
-
return {
|
|
74
|
-
"success": False,
|
|
75
|
-
"stderr": f"No LSP support for language: {language}",
|
|
76
|
-
"stdout": ""
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
try:
|
|
80
|
-
# Initialize LSP
|
|
81
|
-
if not lsp.initialize(os.path.abspath(os.getcwd())):
|
|
82
|
-
return {
|
|
83
|
-
"success": False,
|
|
84
|
-
"stderr": "LSP initialization failed",
|
|
85
|
-
"stdout": ""
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# Get symbol at position
|
|
89
|
-
symbol = LSPRegistry.get_text_at_position(file_path, line, character)
|
|
90
|
-
if not symbol:
|
|
91
|
-
return {
|
|
92
|
-
"success": False,
|
|
93
|
-
"stderr": f"No symbol found at position {line}:{character}",
|
|
94
|
-
"stdout": ""
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# Find references
|
|
98
|
-
refs = lsp.find_references(file_path, (line, character))
|
|
99
|
-
|
|
100
|
-
# Format output
|
|
101
|
-
output = [f"References to '{symbol}':\n"]
|
|
102
|
-
for ref in refs:
|
|
103
|
-
ref_line = ref["range"]["start"]["line"]
|
|
104
|
-
ref_char = ref["range"]["start"]["character"]
|
|
105
|
-
context = LSPRegistry.get_line_at_position(ref["uri"], ref_line).strip()
|
|
106
|
-
output.append(f"File: {ref['uri']}")
|
|
107
|
-
output.append(f"Line {ref_line + 1}, Col {ref_char + 1}: {context}")
|
|
108
|
-
output.append("-" * 40)
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
"success": True,
|
|
112
|
-
"stdout": "\n".join(output) if len(refs) > 0 else f"No references found for '{symbol}'",
|
|
113
|
-
"stderr": ""
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
except Exception as e:
|
|
117
|
-
return {
|
|
118
|
-
"success": False,
|
|
119
|
-
"stderr": f"Error finding references: {str(e)}",
|
|
120
|
-
"stdout": ""
|
|
121
|
-
}
|
|
122
|
-
finally:
|
|
123
|
-
if lsp:
|
|
124
|
-
lsp.shutdown()
|
|
125
|
-
finally:
|
|
126
|
-
# Always restore original directory
|
|
127
|
-
os.chdir(original_dir)
|
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any, List, Optional
|
|
2
|
-
import os
|
|
3
|
-
import logging
|
|
4
|
-
from yaspin import yaspin
|
|
5
|
-
|
|
6
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
|
-
from jarvis.jarvis_treesitter import (
|
|
8
|
-
CodeDatabase,
|
|
9
|
-
SymbolType,
|
|
10
|
-
setup_default_grammars,
|
|
11
|
-
DEFAULT_GRAMMAR_DIR
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
# 配置日志
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
class TreesitterAnalyzer:
|
|
18
|
-
"""Tree-sitter 代码分析工具,用于快速查找代码中的符号定义、引用和调用关系"""
|
|
19
|
-
|
|
20
|
-
name = "treesitter_analyzer"
|
|
21
|
-
description = "使用 Tree-sitter 分析代码,查找符号定义、引用和调用关系"
|
|
22
|
-
parameters = {
|
|
23
|
-
"type": "object",
|
|
24
|
-
"properties": {
|
|
25
|
-
"action": {
|
|
26
|
-
"type": "string",
|
|
27
|
-
"enum": ["find_symbol", "find_references", "find_callers"],
|
|
28
|
-
"description": "分析操作类型: find_symbol(查找符号), find_references(查找引用), find_callers(查找调用者)"
|
|
29
|
-
},
|
|
30
|
-
"symbol_name": {
|
|
31
|
-
"type": "string",
|
|
32
|
-
"description": "要查找的符号名称,如函数名、类名、变量名等"
|
|
33
|
-
},
|
|
34
|
-
"directory": {
|
|
35
|
-
"type": "string",
|
|
36
|
-
"description": "要索引的代码目录,默认为当前目录",
|
|
37
|
-
"default": "."
|
|
38
|
-
},
|
|
39
|
-
"extensions": {
|
|
40
|
-
"type": "array",
|
|
41
|
-
"items": {"type": "string"},
|
|
42
|
-
"description": "要索引的文件扩展名列表,如 [\".py\", \".c\"],不指定则索引所有支持的文件类型"
|
|
43
|
-
},
|
|
44
|
-
"max_results": {
|
|
45
|
-
"type": "integer",
|
|
46
|
-
"description": "最大返回结果数量",
|
|
47
|
-
"default": 20
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"required": ["action", "symbol_name", "directory"]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
def __init__(self):
|
|
54
|
-
"""初始化 Tree-sitter 分析器工具"""
|
|
55
|
-
# 确保语法文件目录存在
|
|
56
|
-
os.makedirs(DEFAULT_GRAMMAR_DIR, exist_ok=True)
|
|
57
|
-
|
|
58
|
-
# 创建代码数据库实例
|
|
59
|
-
self.db = None
|
|
60
|
-
|
|
61
|
-
def _get_database(self) -> CodeDatabase:
|
|
62
|
-
"""获取 Tree-sitter 代码数据库实例,如果不存在则创建"""
|
|
63
|
-
if self.db is None:
|
|
64
|
-
self.db = CodeDatabase()
|
|
65
|
-
return self.db
|
|
66
|
-
|
|
67
|
-
def _index_directory(self, directory: str, extensions: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
68
|
-
"""索引指定目录下的代码文件"""
|
|
69
|
-
try:
|
|
70
|
-
db = self._get_database()
|
|
71
|
-
indexed_files = []
|
|
72
|
-
skipped_files = []
|
|
73
|
-
|
|
74
|
-
with yaspin(text=f"正在索引目录: {directory}...", color="cyan") as spinner:
|
|
75
|
-
for root, _, files in os.walk(directory):
|
|
76
|
-
for file in files:
|
|
77
|
-
# 检查文件扩展名
|
|
78
|
-
if extensions and not any(file.endswith(ext) for ext in extensions):
|
|
79
|
-
continue
|
|
80
|
-
|
|
81
|
-
file_path = os.path.join(root, file)
|
|
82
|
-
try:
|
|
83
|
-
db.index_file(file_path)
|
|
84
|
-
indexed_files.append(file_path)
|
|
85
|
-
except Exception as e:
|
|
86
|
-
skipped_files.append((file_path, str(e)))
|
|
87
|
-
|
|
88
|
-
spinner.text = f"索引完成: {len(indexed_files)} 个文件"
|
|
89
|
-
spinner.ok("✅")
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
"success": True,
|
|
93
|
-
"indexed_files": indexed_files,
|
|
94
|
-
"skipped_files": skipped_files,
|
|
95
|
-
"index_summary": f"已成功索引 {len(indexed_files)} 个文件,跳过 {len(skipped_files)} 个文件"
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
except Exception as e:
|
|
99
|
-
logger.error(f"索引目录失败: {str(e)}")
|
|
100
|
-
return {
|
|
101
|
-
"success": False,
|
|
102
|
-
"stderr": f"索引目录失败: {str(e)}"
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
def _find_symbol(self, symbol_name: str, directory: str, max_results: int = 20) -> Dict[str, Any]:
|
|
106
|
-
"""查找代码中的符号定义"""
|
|
107
|
-
try:
|
|
108
|
-
db = self._get_database()
|
|
109
|
-
symbols = db.find_symbol(symbol_name)
|
|
110
|
-
|
|
111
|
-
if not symbols:
|
|
112
|
-
return {
|
|
113
|
-
"success": True,
|
|
114
|
-
"stdout": f"未找到名为 '{symbol_name}' 的符号",
|
|
115
|
-
"symbols": []
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
# 限制结果数量
|
|
119
|
-
symbols = symbols[:max_results]
|
|
120
|
-
|
|
121
|
-
# 构建结果
|
|
122
|
-
result_list = []
|
|
123
|
-
for symbol in symbols:
|
|
124
|
-
result_list.append({
|
|
125
|
-
"name": symbol.name,
|
|
126
|
-
"type": symbol.type.value,
|
|
127
|
-
"file": symbol.location.file_path,
|
|
128
|
-
"line": symbol.location.start_line,
|
|
129
|
-
"column": symbol.location.start_column
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
# 构建输出文本
|
|
133
|
-
output_text = f"找到 {len(symbols)} 个名为 '{symbol_name}' 的符号:\n\n"
|
|
134
|
-
for i, symbol in enumerate(symbols, 1):
|
|
135
|
-
output_text += (f"{i}. {symbol.type.value}: {symbol.name}\n"
|
|
136
|
-
f" 位置: {symbol.location.file_path}:{symbol.location.start_line}:{symbol.location.start_column}\n\n")
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
"success": True,
|
|
140
|
-
"stdout": output_text,
|
|
141
|
-
"symbols": result_list
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
except Exception as e:
|
|
145
|
-
logger.error(f"查找符号失败: {str(e)}")
|
|
146
|
-
return {
|
|
147
|
-
"success": False,
|
|
148
|
-
"stderr": f"查找符号失败: {str(e)}"
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
def _find_references(self, symbol_name: str, directory: str, max_results: int = 20) -> Dict[str, Any]:
|
|
152
|
-
"""查找代码中符号的引用"""
|
|
153
|
-
try:
|
|
154
|
-
db = self._get_database()
|
|
155
|
-
symbols = db.find_symbol(symbol_name)
|
|
156
|
-
|
|
157
|
-
if not symbols:
|
|
158
|
-
return {
|
|
159
|
-
"success": True,
|
|
160
|
-
"stdout": f"未找到名为 '{symbol_name}' 的符号",
|
|
161
|
-
"references": []
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
# 获取第一个匹配符号的所有引用
|
|
165
|
-
references = db.find_references(symbols[0])
|
|
166
|
-
|
|
167
|
-
# 限制结果数量
|
|
168
|
-
references = references[:max_results]
|
|
169
|
-
|
|
170
|
-
# 构建结果
|
|
171
|
-
result_list = []
|
|
172
|
-
for ref in references:
|
|
173
|
-
result_list.append({
|
|
174
|
-
"file": ref.location.file_path,
|
|
175
|
-
"line": ref.location.start_line,
|
|
176
|
-
"column": ref.location.start_column
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
# 构建输出文本
|
|
180
|
-
output_text = f"找到 {len(references)} 处对 '{symbol_name}' 的引用:\n\n"
|
|
181
|
-
for i, ref in enumerate(references, 1):
|
|
182
|
-
output_text += f"{i}. {ref.location.file_path}:{ref.location.start_line}:{ref.location.start_column}\n"
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
"success": True,
|
|
186
|
-
"stdout": output_text,
|
|
187
|
-
"symbol": {
|
|
188
|
-
"name": symbols[0].name,
|
|
189
|
-
"type": symbols[0].type.value,
|
|
190
|
-
"file": symbols[0].location.file_path,
|
|
191
|
-
"line": symbols[0].location.start_line,
|
|
192
|
-
"column": symbols[0].location.start_column
|
|
193
|
-
},
|
|
194
|
-
"references": result_list
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
except Exception as e:
|
|
198
|
-
logger.error(f"查找引用失败: {str(e)}")
|
|
199
|
-
return {
|
|
200
|
-
"success": False,
|
|
201
|
-
"stderr": f"查找引用失败: {str(e)}"
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
def _find_callers(self, symbol_name: str, directory: str, max_results: int = 20) -> Dict[str, Any]:
|
|
205
|
-
"""查找代码中调用指定函数的位置"""
|
|
206
|
-
try:
|
|
207
|
-
db = self._get_database()
|
|
208
|
-
symbols = db.find_symbol(symbol_name)
|
|
209
|
-
|
|
210
|
-
if not symbols:
|
|
211
|
-
return {
|
|
212
|
-
"success": True,
|
|
213
|
-
"stdout": f"未找到名为 '{symbol_name}' 的函数",
|
|
214
|
-
"callers": []
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
# 筛选出函数类型的符号
|
|
218
|
-
function_symbols = [s for s in symbols if s.type == SymbolType.FUNCTION]
|
|
219
|
-
if not function_symbols:
|
|
220
|
-
return {
|
|
221
|
-
"success": True,
|
|
222
|
-
"stdout": f"'{symbol_name}' 不是一个函数",
|
|
223
|
-
"callers": []
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
# 获取第一个函数符号的所有调用者
|
|
227
|
-
callers = db.find_callers(function_symbols[0])
|
|
228
|
-
|
|
229
|
-
# 限制结果数量
|
|
230
|
-
callers = callers[:max_results]
|
|
231
|
-
|
|
232
|
-
# 构建结果
|
|
233
|
-
result_list = []
|
|
234
|
-
for caller in callers:
|
|
235
|
-
result_list.append({
|
|
236
|
-
"file": caller.location.file_path,
|
|
237
|
-
"line": caller.location.start_line,
|
|
238
|
-
"column": caller.location.start_column
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
# 构建输出文本
|
|
242
|
-
output_text = f"找到 {len(callers)} 处对函数 '{symbol_name}' 的调用:\n\n"
|
|
243
|
-
for i, caller in enumerate(callers, 1):
|
|
244
|
-
output_text += f"{i}. {caller.location.file_path}:{caller.location.start_line}:{caller.location.start_column}\n"
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
"success": True,
|
|
248
|
-
"stdout": output_text,
|
|
249
|
-
"function": {
|
|
250
|
-
"name": function_symbols[0].name,
|
|
251
|
-
"file": function_symbols[0].location.file_path,
|
|
252
|
-
"line": function_symbols[0].location.start_line,
|
|
253
|
-
"column": function_symbols[0].location.start_column
|
|
254
|
-
},
|
|
255
|
-
"callers": result_list
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
except Exception as e:
|
|
259
|
-
logger.error(f"查找调用者失败: {str(e)}")
|
|
260
|
-
return {
|
|
261
|
-
"success": False,
|
|
262
|
-
"stderr": f"查找调用者失败: {str(e)}"
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
266
|
-
"""执行 Tree-sitter 代码分析
|
|
267
|
-
|
|
268
|
-
参数:
|
|
269
|
-
args: 包含操作参数的字典
|
|
270
|
-
|
|
271
|
-
返回:
|
|
272
|
-
Dict[str, Any]: 操作结果
|
|
273
|
-
"""
|
|
274
|
-
try:
|
|
275
|
-
action = args.get("action")
|
|
276
|
-
symbol_name = args.get("symbol_name")
|
|
277
|
-
directory = args.get("directory", ".")
|
|
278
|
-
extensions = args.get("extensions", None)
|
|
279
|
-
max_results = args.get("max_results", 20)
|
|
280
|
-
|
|
281
|
-
# 确保 symbol_name 参数存在
|
|
282
|
-
if not symbol_name:
|
|
283
|
-
return {
|
|
284
|
-
"success": False,
|
|
285
|
-
"stdout": "",
|
|
286
|
-
"stderr": "缺少必要参数: symbol_name"
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
# 确保语法目录存在
|
|
290
|
-
os.makedirs(DEFAULT_GRAMMAR_DIR, exist_ok=True)
|
|
291
|
-
|
|
292
|
-
# 先自动索引目录
|
|
293
|
-
with yaspin(text="正在索引目录...") as spinner:
|
|
294
|
-
index_result = self._index_directory(directory, extensions)
|
|
295
|
-
if not index_result.get("success", False):
|
|
296
|
-
spinner.fail("✗")
|
|
297
|
-
return index_result
|
|
298
|
-
spinner.ok("✓")
|
|
299
|
-
|
|
300
|
-
# 根据不同的操作执行相应的函数
|
|
301
|
-
result = None
|
|
302
|
-
if action == "find_symbol":
|
|
303
|
-
result = self._find_symbol(symbol_name, directory, max_results)
|
|
304
|
-
elif action == "find_references":
|
|
305
|
-
result = self._find_references(symbol_name, directory, max_results)
|
|
306
|
-
elif action == "find_callers":
|
|
307
|
-
result = self._find_callers(symbol_name, directory, max_results)
|
|
308
|
-
else:
|
|
309
|
-
return {
|
|
310
|
-
"success": False,
|
|
311
|
-
"stdout": "",
|
|
312
|
-
"stderr": f"不支持的操作: {action}"
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
# 将索引信息添加到结果中
|
|
316
|
-
if result:
|
|
317
|
-
if "stdout" in result:
|
|
318
|
-
result["stdout"] = f"{index_result.get('index_summary', '')}\n\n{result['stdout']}"
|
|
319
|
-
else:
|
|
320
|
-
result["stdout"] = index_result.get('index_summary', '')
|
|
321
|
-
|
|
322
|
-
return result
|
|
323
|
-
|
|
324
|
-
except Exception as e:
|
|
325
|
-
logger.error(f"Tree-sitter 分析失败: {str(e)}")
|
|
326
|
-
return {
|
|
327
|
-
"success": False,
|
|
328
|
-
"stdout": "",
|
|
329
|
-
"stderr": f"Tree-sitter 分析失败: {str(e)}"
|
|
330
|
-
}
|
|
331
|
-
|