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
|
@@ -8,10 +8,11 @@ from yaspin import yaspin
|
|
|
8
8
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
9
9
|
import sys
|
|
10
10
|
import argparse
|
|
11
|
+
import os
|
|
11
12
|
|
|
12
13
|
from jarvis.jarvis_utils.git_utils import find_git_root, has_uncommitted_changes
|
|
13
14
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
14
|
-
from jarvis.jarvis_utils.utils import init_env
|
|
15
|
+
from jarvis.jarvis_utils.utils import ct, ot, init_env
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class GitCommitTool:
|
|
@@ -24,6 +25,11 @@ class GitCommitTool:
|
|
|
24
25
|
"type": "string",
|
|
25
26
|
"description": "提交信息的语言",
|
|
26
27
|
"default": "Chinese"
|
|
28
|
+
},
|
|
29
|
+
"root_dir": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Git仓库的根目录路径(可选)",
|
|
32
|
+
"default": "."
|
|
27
33
|
}
|
|
28
34
|
},
|
|
29
35
|
"required": []
|
|
@@ -31,7 +37,7 @@ class GitCommitTool:
|
|
|
31
37
|
def _extract_commit_message(self, message):
|
|
32
38
|
"""Raw extraction preserving all characters"""
|
|
33
39
|
r = re.search(
|
|
34
|
-
r"(?i)
|
|
40
|
+
r"(?i)" + ot("COMMIT_MESSAGE") + r"\s*([\s\S]*?)\s*" + ct("COMMIT_MESSAGE"),
|
|
35
41
|
message
|
|
36
42
|
)
|
|
37
43
|
if r:
|
|
@@ -51,83 +57,95 @@ class GitCommitTool:
|
|
|
51
57
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
52
58
|
"""Execute automatic commit process with support for multi-line messages and special characters"""
|
|
53
59
|
try:
|
|
54
|
-
|
|
55
|
-
if not has_uncommitted_changes():
|
|
56
|
-
PrettyOutput.print("没有未提交的更改", OutputType.SUCCESS)
|
|
57
|
-
return {"success": True, "stdout": "No changes to commit", "stderr": ""}
|
|
60
|
+
root_dir = args.get("root_dir", ".")
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
stderr=subprocess.DEVNULL
|
|
66
|
-
).wait()
|
|
67
|
-
spinner.write("✅ 添加文件到提交")
|
|
68
|
-
|
|
69
|
-
# 获取差异
|
|
70
|
-
spinner.text = "正在获取代码差异..."
|
|
71
|
-
process = subprocess.Popen(
|
|
72
|
-
["git", "diff", "--cached", "--exit-code"],
|
|
73
|
-
stdout=subprocess.PIPE,
|
|
74
|
-
stderr=subprocess.PIPE
|
|
75
|
-
)
|
|
76
|
-
diff = process.communicate()[0].decode()
|
|
77
|
-
spinner.write("✅ 获取差异")
|
|
62
|
+
# Store current directory
|
|
63
|
+
original_dir = os.getcwd()
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
# Change to root_dir
|
|
67
|
+
os.chdir(root_dir)
|
|
78
68
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# 必需结构
|
|
84
|
-
必须使用以下格式:
|
|
85
|
-
<COMMIT_MESSAGE>
|
|
86
|
-
<类型>(<范围>): <主题>
|
|
87
|
-
使用祈使语气描述变更内容
|
|
88
|
-
</COMMIT_MESSAGE>
|
|
89
|
-
# 格式规则
|
|
90
|
-
1. 类型: fix, feat, docs, style, refactor, test, chore
|
|
91
|
-
2. 范围表示模块 (例如: auth, database)
|
|
92
|
-
3. 主题行 <= 72个字符,不以句号结尾
|
|
93
|
-
4. 正文使用现在时态解释每个变更的内容和原因
|
|
94
|
-
5. 不要遗漏任何变更
|
|
95
|
-
# 分析材料
|
|
96
|
-
{diff}
|
|
97
|
-
'''
|
|
98
|
-
platform = PlatformRegistry().get_codegen_platform()
|
|
99
|
-
commit_message = platform.chat_until_success(prompt)
|
|
100
|
-
commit_message = self._extract_commit_message(commit_message)
|
|
101
|
-
spinner.write("✅ 生成提交消息")
|
|
69
|
+
find_git_root()
|
|
70
|
+
if not has_uncommitted_changes():
|
|
71
|
+
PrettyOutput.print("没有未提交的更改", OutputType.SUCCESS)
|
|
72
|
+
return {"success": True, "stdout": "No changes to commit", "stderr": ""}
|
|
102
73
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
tmp_file.write(commit_message)
|
|
107
|
-
tmp_file.flush()
|
|
108
|
-
spinner.text = "正在执行提交..."
|
|
109
|
-
commit_cmd = ["git", "commit", "-F", tmp_file.name]
|
|
74
|
+
with yaspin(text="正在初始化提交流程...", color="cyan") as spinner:
|
|
75
|
+
# 添加文件
|
|
76
|
+
spinner.text = "正在添加文件到提交..."
|
|
110
77
|
subprocess.Popen(
|
|
111
|
-
|
|
78
|
+
["git", "add", "."],
|
|
112
79
|
stdout=subprocess.DEVNULL,
|
|
113
80
|
stderr=subprocess.DEVNULL
|
|
114
81
|
).wait()
|
|
115
|
-
spinner.write("✅
|
|
82
|
+
spinner.write("✅ 添加文件到提交")
|
|
83
|
+
|
|
84
|
+
# 获取差异
|
|
85
|
+
spinner.text = "正在获取代码差异..."
|
|
86
|
+
process = subprocess.Popen(
|
|
87
|
+
["git", "diff", "--cached", "--exit-code"],
|
|
88
|
+
stdout=subprocess.PIPE,
|
|
89
|
+
stderr=subprocess.PIPE
|
|
90
|
+
)
|
|
91
|
+
diff = process.communicate()[0].decode()
|
|
92
|
+
spinner.write("✅ 获取差异")
|
|
93
|
+
|
|
94
|
+
# 生成提交信息
|
|
95
|
+
spinner.text = "正在生成提交消息..."
|
|
96
|
+
prompt = f'''根据以下规则生成提交信息:
|
|
97
|
+
提交信息应使用{args.get('lang', '中文')}书写
|
|
98
|
+
# 必需结构
|
|
99
|
+
必须使用以下格式:
|
|
100
|
+
{ot("COMMIT_MESSAGE")}
|
|
101
|
+
<类型>(<范围>): <主题>
|
|
102
|
+
使用祈使语气描述变更内容
|
|
103
|
+
{ct("COMMIT_MESSAGE")}
|
|
104
|
+
# 格式规则
|
|
105
|
+
1. 类型: fix, feat, docs, style, refactor, test, chore
|
|
106
|
+
2. 范围表示模块 (例如: auth, database)
|
|
107
|
+
3. 主题行 <= 72个字符,不以句号结尾
|
|
108
|
+
4. 正文使用现在时态解释每个变更的内容和原因
|
|
109
|
+
5. 不要遗漏任何变更
|
|
110
|
+
# 分析材料
|
|
111
|
+
{diff}
|
|
112
|
+
'''
|
|
113
|
+
platform = PlatformRegistry().get_codegen_platform()
|
|
114
|
+
commit_message = platform.chat_until_success(prompt)
|
|
115
|
+
commit_message = self._extract_commit_message(commit_message)
|
|
116
|
+
spinner.write("✅ 生成提交消息")
|
|
117
|
+
|
|
118
|
+
# 执行提交
|
|
119
|
+
spinner.text = "正在准备提交..."
|
|
120
|
+
with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_file:
|
|
121
|
+
tmp_file.write(commit_message)
|
|
122
|
+
tmp_file.flush()
|
|
123
|
+
spinner.text = "正在执行提交..."
|
|
124
|
+
commit_cmd = ["git", "commit", "-F", tmp_file.name]
|
|
125
|
+
subprocess.Popen(
|
|
126
|
+
commit_cmd,
|
|
127
|
+
stdout=subprocess.DEVNULL,
|
|
128
|
+
stderr=subprocess.DEVNULL
|
|
129
|
+
).wait()
|
|
130
|
+
spinner.write("✅ 提交")
|
|
116
131
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
commit_hash = self._get_last_commit_hash()
|
|
133
|
+
spinner.text = "完成提交"
|
|
134
|
+
spinner.ok("✅")
|
|
120
135
|
|
|
121
|
-
|
|
136
|
+
PrettyOutput.print(f"提交哈希: {commit_hash}\n提交消息: {commit_message}", OutputType.SUCCESS)
|
|
122
137
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
return {
|
|
139
|
+
"success": True,
|
|
140
|
+
"stdout": yaml.safe_dump({
|
|
141
|
+
"commit_hash": commit_hash,
|
|
142
|
+
"commit_message": commit_message
|
|
143
|
+
}),
|
|
144
|
+
"stderr": ""
|
|
145
|
+
}
|
|
146
|
+
finally:
|
|
147
|
+
# Always restore original directory
|
|
148
|
+
os.chdir(original_dir)
|
|
131
149
|
|
|
132
150
|
except Exception as e:
|
|
133
151
|
return {
|
|
@@ -140,9 +158,10 @@ def main():
|
|
|
140
158
|
init_env()
|
|
141
159
|
parser = argparse.ArgumentParser(description='Git commit tool')
|
|
142
160
|
parser.add_argument('--lang', type=str, default='Chinese', help='Language for commit messages')
|
|
161
|
+
parser.add_argument('--root-dir', type=str, default='.', help='Root directory of the Git repository')
|
|
143
162
|
args = parser.parse_args()
|
|
144
163
|
tool = GitCommitTool()
|
|
145
|
-
tool.execute({"lang": args.lang if hasattr(args, 'lang') else 'Chinese'})
|
|
164
|
+
tool.execute({"lang": args.lang if hasattr(args, 'lang') else 'Chinese', "root_dir": args.root_dir})
|
|
146
165
|
|
|
147
166
|
if __name__ == "__main__":
|
|
148
167
|
sys.exit(main())
|
|
@@ -11,7 +11,12 @@ class LSPFindDefinitionTool:
|
|
|
11
11
|
"file_path": "包含符号的文件路径",
|
|
12
12
|
"line": "符号所在的行号(从0开始)",
|
|
13
13
|
"character": "符号在行中的字符位置",
|
|
14
|
-
"language": f"文件的编程语言({', '.join(LSPRegistry.get_global_lsp_registry().get_supported_languages())})"
|
|
14
|
+
"language": f"文件的编程语言({', '.join(LSPRegistry.get_global_lsp_registry().get_supported_languages())})",
|
|
15
|
+
"root_dir": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "LSP操作的根目录路径(可选)",
|
|
18
|
+
"default": "."
|
|
19
|
+
}
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
@staticmethod
|
|
@@ -26,6 +31,7 @@ class LSPFindDefinitionTool:
|
|
|
26
31
|
line = args.get("line", None)
|
|
27
32
|
character = args.get("character", None)
|
|
28
33
|
language = args.get("language", "")
|
|
34
|
+
root_dir = args.get("root_dir", ".")
|
|
29
35
|
|
|
30
36
|
# Validate inputs
|
|
31
37
|
if not all([file_path, line is not None, character is not None, language]):
|
|
@@ -52,83 +58,93 @@ class LSPFindDefinitionTool:
|
|
|
52
58
|
"stdout": ""
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
lsp = registry.create_lsp(language)
|
|
61
|
+
# Store current directory
|
|
62
|
+
original_dir = os.getcwd()
|
|
58
63
|
|
|
59
|
-
if not lsp:
|
|
60
|
-
return {
|
|
61
|
-
"success": False,
|
|
62
|
-
"stderr": f"No LSP support for language: {language}",
|
|
63
|
-
"stdout": ""
|
|
64
|
-
}
|
|
65
|
-
|
|
66
64
|
try:
|
|
67
|
-
#
|
|
68
|
-
|
|
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:
|
|
69
73
|
return {
|
|
70
74
|
"success": False,
|
|
71
|
-
"stderr": "LSP
|
|
75
|
+
"stderr": f"No LSP support for language: {language}",
|
|
72
76
|
"stdout": ""
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
83
132
|
|
|
84
|
-
# Find definition
|
|
85
|
-
defn = lsp.find_definition(file_path, (line, character))
|
|
86
|
-
|
|
87
|
-
if not defn:
|
|
88
133
|
return {
|
|
89
134
|
"success": True,
|
|
90
|
-
"stdout":
|
|
135
|
+
"stdout": "\n".join(output),
|
|
91
136
|
"stderr": ""
|
|
92
137
|
}
|
|
93
138
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
]
|
|
104
|
-
|
|
105
|
-
# Get a few lines of context around the definition
|
|
106
|
-
try:
|
|
107
|
-
with open(defn["uri"], 'r') as f:
|
|
108
|
-
lines = f.readlines()
|
|
109
|
-
start = max(0, def_line - 2)
|
|
110
|
-
end = min(len(lines), def_line + 3)
|
|
111
|
-
|
|
112
|
-
if start < def_line:
|
|
113
|
-
output.append("\nContext:")
|
|
114
|
-
for i in range(start, end):
|
|
115
|
-
prefix = ">" if i == def_line else " "
|
|
116
|
-
output.append(f"{prefix} {i+1:4d} | {lines[i].rstrip()}")
|
|
117
|
-
except Exception:
|
|
118
|
-
pass
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
"success": True,
|
|
122
|
-
"stdout": "\n".join(output),
|
|
123
|
-
"stderr": ""
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
except Exception as e:
|
|
127
|
-
return {
|
|
128
|
-
"success": False,
|
|
129
|
-
"stderr": f"Error finding definition: {str(e)}",
|
|
130
|
-
"stdout": ""
|
|
131
|
-
}
|
|
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()
|
|
132
148
|
finally:
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
# Always restore original directory
|
|
150
|
+
os.chdir(original_dir)
|
|
@@ -11,7 +11,12 @@ class LSPFindReferencesTool:
|
|
|
11
11
|
"file_path": "Path to the file containing the symbol",
|
|
12
12
|
"line": "Line number (0-based) of the symbol",
|
|
13
13
|
"character": "Character position in the line",
|
|
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
|
|
@@ -26,6 +31,7 @@ class LSPFindReferencesTool:
|
|
|
26
31
|
line = args.get("line", None)
|
|
27
32
|
character = args.get("character", None)
|
|
28
33
|
language = args.get("language", "")
|
|
34
|
+
root_dir = args.get("root_dir", ".")
|
|
29
35
|
|
|
30
36
|
# Validate inputs
|
|
31
37
|
if not all([file_path, line is not None, character is not None, language]):
|
|
@@ -52,60 +58,70 @@ class LSPFindReferencesTool:
|
|
|
52
58
|
"stdout": ""
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
lsp = registry.create_lsp(language)
|
|
61
|
+
# Store current directory
|
|
62
|
+
original_dir = os.getcwd()
|
|
58
63
|
|
|
59
|
-
if not lsp:
|
|
60
|
-
return {
|
|
61
|
-
"success": False,
|
|
62
|
-
"stderr": f"No LSP support for language: {language}",
|
|
63
|
-
"stdout": ""
|
|
64
|
-
}
|
|
65
|
-
|
|
66
64
|
try:
|
|
67
|
-
#
|
|
68
|
-
|
|
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:
|
|
69
73
|
return {
|
|
70
74
|
"success": False,
|
|
71
|
-
"stderr": "LSP
|
|
75
|
+
"stderr": f"No LSP support for language: {language}",
|
|
72
76
|
"stdout": ""
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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:
|
|
78
117
|
return {
|
|
79
118
|
"success": False,
|
|
80
|
-
"stderr": f"
|
|
119
|
+
"stderr": f"Error finding references: {str(e)}",
|
|
81
120
|
"stdout": ""
|
|
82
121
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# Format output
|
|
88
|
-
output = [f"References to '{symbol}':\n"]
|
|
89
|
-
for ref in refs:
|
|
90
|
-
ref_line = ref["range"]["start"]["line"]
|
|
91
|
-
ref_char = ref["range"]["start"]["character"]
|
|
92
|
-
context = LSPRegistry.get_line_at_position(ref["uri"], ref_line).strip()
|
|
93
|
-
output.append(f"File: {ref['uri']}")
|
|
94
|
-
output.append(f"Line {ref_line + 1}, Col {ref_char + 1}: {context}")
|
|
95
|
-
output.append("-" * 40)
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
"success": True,
|
|
99
|
-
"stdout": "\n".join(output) if len(refs) > 0 else f"No references found for '{symbol}'",
|
|
100
|
-
"stderr": ""
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
except Exception as e:
|
|
104
|
-
return {
|
|
105
|
-
"success": False,
|
|
106
|
-
"stderr": f"Error finding references: {str(e)}",
|
|
107
|
-
"stdout": ""
|
|
108
|
-
}
|
|
122
|
+
finally:
|
|
123
|
+
if lsp:
|
|
124
|
+
lsp.shutdown()
|
|
109
125
|
finally:
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
# Always restore original directory
|
|
127
|
+
os.chdir(original_dir)
|