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
@@ -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)<COMMIT_MESSAGE>\s*([\s\S]*?)\s*</COMMIT_MESSAGE>",
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
- find_git_root()
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
- with yaspin(text="正在初始化提交流程...", color="cyan") as spinner:
60
- # 添加文件
61
- spinner.text = "正在添加文件到提交..."
62
- subprocess.Popen(
63
- ["git", "add", "."],
64
- stdout=subprocess.DEVNULL,
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
- spinner.text = "正在生成提交消息..."
81
- prompt = f'''根据以下规则生成提交信息:
82
- 提交信息应使用{args.get('lang', '中文')}书写
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
- spinner.text = "正在准备提交..."
105
- with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_file:
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
- commit_cmd,
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
- commit_hash = self._get_last_commit_hash()
118
- spinner.text = "完成提交"
119
- spinner.ok("✅")
132
+ commit_hash = self._get_last_commit_hash()
133
+ spinner.text = "完成提交"
134
+ spinner.ok("✅")
120
135
 
121
- PrettyOutput.print(f"提交哈希: {commit_hash}\n提交消息: {commit_message}", OutputType.SUCCESS)
136
+ PrettyOutput.print(f"提交哈希: {commit_hash}\n提交消息: {commit_message}", OutputType.SUCCESS)
122
137
 
123
- return {
124
- "success": True,
125
- "stdout": yaml.safe_dump({
126
- "commit_hash": commit_hash,
127
- "commit_message": commit_message
128
- }),
129
- "stderr": ""
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
- # Get LSP instance
56
- registry = LSPRegistry.get_global_lsp_registry()
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
- # Initialize LSP
68
- if not lsp.initialize(os.path.abspath(os.getcwd())):
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 initialization failed",
75
+ "stderr": f"No LSP support for language: {language}",
72
76
  "stdout": ""
73
77
  }
74
78
 
75
- # Get symbol at position
76
- symbol = LSPRegistry.get_text_at_position(file_path, line, character)
77
- if not symbol:
78
- return {
79
- "success": False,
80
- "stderr": f"No symbol found at position {line}:{character}",
81
- "stdout": ""
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": f"No definition found for '{symbol}'",
135
+ "stdout": "\n".join(output),
91
136
  "stderr": ""
92
137
  }
93
138
 
94
- # Format output
95
- def_line = defn["range"]["start"]["line"]
96
- def_char = defn["range"]["start"]["character"]
97
- context = LSPRegistry.get_line_at_position(defn["uri"], def_line).strip()
98
-
99
- output = [
100
- f"Definition of '{symbol}':",
101
- f"File: {defn['uri']}",
102
- f"Line {def_line + 1}, Col {def_char + 1}: {context}"
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
- if lsp:
134
- lsp.shutdown()
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
- # Get LSP instance
56
- registry = LSPRegistry.get_global_lsp_registry()
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
- # Initialize LSP
68
- if not lsp.initialize(os.path.abspath(os.getcwd())):
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 initialization failed",
75
+ "stderr": f"No LSP support for language: {language}",
72
76
  "stdout": ""
73
77
  }
74
78
 
75
- # Get symbol at position
76
- symbol = LSPRegistry.get_text_at_position(file_path, line, character)
77
- if not symbol:
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"No symbol found at position {line}:{character}",
119
+ "stderr": f"Error finding references: {str(e)}",
81
120
  "stdout": ""
82
121
  }
83
-
84
- # Find references
85
- refs = lsp.find_references(file_path, (line, character))
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
- if lsp:
111
- lsp.shutdown()
126
+ # Always restore original directory
127
+ os.chdir(original_dir)