jarvis-ai-assistant 0.1.104__py3-none-any.whl → 0.1.106__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.

Files changed (62) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/agent.py +124 -67
  3. jarvis/jarvis_code_agent/code_agent.py +133 -22
  4. jarvis/jarvis_code_agent/patch.py +4 -7
  5. jarvis/jarvis_code_agent/relevant_files.py +163 -72
  6. jarvis/jarvis_codebase/main.py +36 -15
  7. jarvis/jarvis_lsp/base.py +143 -0
  8. jarvis/jarvis_lsp/cpp.py +134 -0
  9. jarvis/jarvis_lsp/go.py +140 -0
  10. jarvis/jarvis_lsp/python.py +135 -0
  11. jarvis/jarvis_lsp/registry.py +234 -0
  12. jarvis/jarvis_lsp/rust.py +142 -0
  13. jarvis/jarvis_platform/__init__.py +3 -0
  14. jarvis/{models → jarvis_platform}/ai8.py +1 -1
  15. jarvis/{models → jarvis_platform}/kimi.py +1 -1
  16. jarvis/{models → jarvis_platform}/ollama.py +1 -1
  17. jarvis/{models → jarvis_platform}/openai.py +1 -1
  18. jarvis/{models → jarvis_platform}/oyi.py +1 -1
  19. jarvis/{models → jarvis_platform}/registry.py +11 -11
  20. jarvis/{jarvis_platform → jarvis_platform_manager}/main.py +1 -1
  21. jarvis/jarvis_rag/main.py +6 -6
  22. jarvis/jarvis_smart_shell/main.py +3 -3
  23. jarvis/jarvis_tools/__init__.py +0 -0
  24. jarvis/{tools → jarvis_tools}/ask_user.py +1 -1
  25. jarvis/{tools → jarvis_tools}/code_review.py +34 -8
  26. jarvis/jarvis_tools/create_code_agent.py +115 -0
  27. jarvis/{tools → jarvis_tools}/create_sub_agent.py +1 -1
  28. jarvis/jarvis_tools/deep_thinking.py +160 -0
  29. jarvis/jarvis_tools/deep_thinking_agent.py +146 -0
  30. jarvis/{tools → jarvis_tools}/git_commiter.py +2 -2
  31. jarvis/jarvis_tools/lsp_find_definition.py +134 -0
  32. jarvis/jarvis_tools/lsp_find_references.py +111 -0
  33. jarvis/jarvis_tools/lsp_get_diagnostics.py +121 -0
  34. jarvis/jarvis_tools/lsp_get_document_symbols.py +87 -0
  35. jarvis/jarvis_tools/lsp_prepare_rename.py +130 -0
  36. jarvis/jarvis_tools/lsp_validate_edit.py +141 -0
  37. jarvis/{tools → jarvis_tools}/methodology.py +6 -1
  38. jarvis/{tools → jarvis_tools}/registry.py +6 -5
  39. jarvis/{tools → jarvis_tools}/search.py +2 -2
  40. jarvis/utils.py +68 -25
  41. {jarvis_ai_assistant-0.1.104.dist-info → jarvis_ai_assistant-0.1.106.dist-info}/METADATA +23 -16
  42. jarvis_ai_assistant-0.1.106.dist-info/RECORD +62 -0
  43. {jarvis_ai_assistant-0.1.104.dist-info → jarvis_ai_assistant-0.1.106.dist-info}/entry_points.txt +3 -4
  44. jarvis/models/__init__.py +0 -3
  45. jarvis/tools/create_code_test_agent.py +0 -115
  46. jarvis/tools/create_ctags_agent.py +0 -164
  47. jarvis/tools/find_in_codebase.py +0 -78
  48. jarvis_ai_assistant-0.1.104.dist-info/RECORD +0 -50
  49. /jarvis/{models → jarvis_platform}/base.py +0 -0
  50. /jarvis/{tools → jarvis_platform_manager}/__init__.py +0 -0
  51. /jarvis/{tools → jarvis_tools}/ask_codebase.py +0 -0
  52. /jarvis/{tools → jarvis_tools}/base.py +0 -0
  53. /jarvis/{tools → jarvis_tools}/chdir.py +0 -0
  54. /jarvis/{tools → jarvis_tools}/execute_shell.py +0 -0
  55. /jarvis/{tools → jarvis_tools}/file_operation.py +0 -0
  56. /jarvis/{tools → jarvis_tools}/rag.py +0 -0
  57. /jarvis/{tools → jarvis_tools}/read_code.py +0 -0
  58. /jarvis/{tools → jarvis_tools}/read_webpage.py +0 -0
  59. /jarvis/{tools → jarvis_tools}/select_code_files.py +0 -0
  60. {jarvis_ai_assistant-0.1.104.dist-info → jarvis_ai_assistant-0.1.106.dist-info}/LICENSE +0 -0
  61. {jarvis_ai_assistant-0.1.104.dist-info → jarvis_ai_assistant-0.1.106.dist-info}/WHEEL +0 -0
  62. {jarvis_ai_assistant-0.1.104.dist-info → jarvis_ai_assistant-0.1.106.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ from typing import List, Dict, Optional, Tuple, Any
5
+ import json
6
+ from jarvis.jarvis_lsp.base import BaseLSP
7
+ from jarvis.utils import PrettyOutput, OutputType
8
+
9
+ class GoLSP(BaseLSP):
10
+ """Go LSP implementation using gopls."""
11
+
12
+ language = "go"
13
+
14
+ @staticmethod
15
+ def check() -> bool:
16
+ """Check if gopls is installed."""
17
+ return shutil.which("gopls") is not None
18
+
19
+ def __init__(self):
20
+ self.workspace_path = ""
21
+ self.gopls_process = None
22
+ self.request_id = 0
23
+
24
+ def initialize(self, workspace_path: str) -> bool:
25
+ try:
26
+ self.workspace_path = workspace_path
27
+ # Start gopls process
28
+ self.gopls_process = subprocess.Popen(
29
+ ["gopls", "serve"],
30
+ stdin=subprocess.PIPE,
31
+ stdout=subprocess.PIPE,
32
+ stderr=subprocess.PIPE
33
+ )
34
+
35
+ # Send initialize request
36
+ self._send_request("initialize", {
37
+ "processId": os.getpid(),
38
+ "rootUri": f"file://{workspace_path}",
39
+ "capabilities": {
40
+ "textDocument": {
41
+ "hover": {"contentFormat": ["markdown", "plaintext"]},
42
+ "completion": {"completionItem": {"snippetSupport": True}},
43
+ "signatureHelp": {"signatureInformation": {"documentationFormat": ["markdown", "plaintext"]}},
44
+ }
45
+ }
46
+ })
47
+
48
+ return True
49
+ except Exception as e:
50
+ PrettyOutput.print(f"Go LSP initialization failed: {str(e)}", OutputType.ERROR)
51
+ return False
52
+
53
+ def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
54
+ """Send JSON-RPC request to gopls."""
55
+ if not self.gopls_process:
56
+ return None
57
+
58
+ try:
59
+ self.request_id += 1
60
+ request = {
61
+ "jsonrpc": "2.0",
62
+ "id": self.request_id,
63
+ "method": method,
64
+ "params": params
65
+ }
66
+
67
+ self.gopls_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
68
+ self.gopls_process.stdin.flush() # type: ignore
69
+
70
+ response = json.loads(self.gopls_process.stdout.readline().decode()) # type: ignore
71
+ return response.get("result")
72
+ except Exception:
73
+ return None
74
+
75
+ def find_references(self, file_path: str, position: Tuple[int, int]) -> List[Dict[str, Any]]:
76
+ result = self._send_request("textDocument/references", {
77
+ "textDocument": {"uri": f"file://{file_path}"},
78
+ "position": {"line": position[0], "character": position[1]},
79
+ "context": {"includeDeclaration": True}
80
+ })
81
+ return result or [] # type: ignore
82
+
83
+ def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
84
+ result = self._send_request("textDocument/definition", {
85
+ "textDocument": {"uri": f"file://{file_path}"},
86
+ "position": {"line": position[0], "character": position[1]}
87
+ })
88
+ return result[0] if result else None
89
+
90
+ def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
91
+ result = self._send_request("textDocument/documentSymbol", {
92
+ "textDocument": {"uri": f"file://{file_path}"}
93
+ })
94
+ return result or [] # type: ignore
95
+
96
+ def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
97
+ # Send didOpen notification to trigger diagnostics
98
+ self._send_request("textDocument/didOpen", {
99
+ "textDocument": {
100
+ "uri": f"file://{file_path}",
101
+ "languageId": "go",
102
+ "version": 1,
103
+ "text": open(file_path).read()
104
+ }
105
+ })
106
+
107
+ # Wait for diagnostic notification
108
+ try:
109
+ response = json.loads(self.gopls_process.stdout.readline().decode()) # type: ignore
110
+ if response.get("method") == "textDocument/publishDiagnostics":
111
+ return response.get("params", {}).get("diagnostics", [])
112
+ except Exception:
113
+ pass
114
+ return []
115
+
116
+ def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
117
+ result = self._send_request("textDocument/prepareRename", {
118
+ "textDocument": {"uri": f"file://{file_path}"},
119
+ "position": {"line": position[0], "character": position[1]}
120
+ })
121
+ return result
122
+
123
+ def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
124
+ # Send workspace/willRenameFiles request to check validity
125
+ result = self._send_request("workspace/willRenameFiles", {
126
+ "files": [{
127
+ "oldUri": f"file://{file_path}",
128
+ "newUri": f"file://{file_path}.tmp"
129
+ }]
130
+ })
131
+ return bool(result)
132
+
133
+ def shutdown(self):
134
+ if self.gopls_process:
135
+ try:
136
+ self._send_request("shutdown", {})
137
+ self.gopls_process.terminate()
138
+ self.gopls_process = None
139
+ except Exception:
140
+ pass
@@ -0,0 +1,135 @@
1
+ import os
2
+ from typing import List, Dict, Optional, Tuple, Any
3
+ import jedi
4
+ from jarvis.jarvis_lsp.base import BaseLSP
5
+ from jarvis.utils import PrettyOutput, OutputType
6
+
7
+ class PythonLSP(BaseLSP):
8
+ """Python LSP implementation using jedi."""
9
+
10
+ language = "python"
11
+
12
+ def __init__(self):
13
+ self.workspace_path = ""
14
+ self.script_cache = {}
15
+
16
+ def initialize(self, workspace_path: str) -> bool:
17
+ self.workspace_path = workspace_path
18
+ return True
19
+
20
+ def _get_script(self, file_path: str):
21
+ if file_path not in self.script_cache:
22
+ try:
23
+ with open(file_path, 'r') as f:
24
+ content = f.read()
25
+ self.script_cache[file_path] = jedi.Script(code=content, path=file_path)
26
+ except Exception:
27
+ return None
28
+ return self.script_cache[file_path]
29
+
30
+ def find_references(self, file_path: str, position: Tuple[int, int]) -> List[Dict[str, Any]]:
31
+ script = self._get_script(file_path)
32
+ if not script:
33
+ return []
34
+ try:
35
+ refs = script.get_references(line=position[0] + 1, column=position[1])
36
+ return [self._location_to_dict(ref) for ref in refs]
37
+ except Exception:
38
+ return []
39
+
40
+ def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
41
+ script = self._get_script(file_path)
42
+ if not script:
43
+ return None
44
+ try:
45
+ defs = script.goto(line=position[0] + 1, column=position[1])
46
+ return self._location_to_dict(defs[0]) if defs else None
47
+ except Exception:
48
+ return None
49
+
50
+ def _location_to_dict(self, location) -> Dict[str, Any]:
51
+ return {
52
+ "uri": location.module_path,
53
+ "range": {
54
+ "start": {"line": location.line - 1, "character": location.column},
55
+ "end": {"line": location.line - 1, "character": location.column + len(location.name)}
56
+ }
57
+ }
58
+
59
+ def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
60
+ script = self._get_script(file_path)
61
+ if not script:
62
+ return []
63
+ try:
64
+ names = script.get_names()
65
+ return [self._location_to_dict(name) for name in names]
66
+ except Exception:
67
+ return []
68
+
69
+ def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
70
+ script = self._get_script(file_path)
71
+ if not script:
72
+ return []
73
+ try:
74
+ errors = script.get_syntax_errors()
75
+ return [{
76
+ "range": {
77
+ "start": {"line": e.line - 1, "character": e.column},
78
+ "end": {"line": e.line - 1, "character": e.column + 1}
79
+ },
80
+ "severity": 1, # Error
81
+ "source": "jedi",
82
+ "message": str(e)
83
+ } for e in errors]
84
+ except Exception:
85
+ return []
86
+
87
+ def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
88
+ script = self._get_script(file_path)
89
+ if not script:
90
+ return None
91
+ try:
92
+ refs = script.get_references(line=position[0] + 1, column=position[1])
93
+ if refs:
94
+ ref = refs[0]
95
+ return {
96
+ "range": {
97
+ "start": {"line": ref.line - 1, "character": ref.column},
98
+ "end": {"line": ref.line - 1, "character": ref.column + len(ref.name)}
99
+ }
100
+ }
101
+ except Exception:
102
+ return None
103
+ return None
104
+
105
+ def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
106
+ try:
107
+ # Simple syntax check of the edited content
108
+ content = ""
109
+ with open(file_path, 'r') as f:
110
+ content = f.read()
111
+
112
+ # Apply edit
113
+ start = edit["range"]["start"]
114
+ end = edit["range"]["end"]
115
+ new_text = edit["newText"]
116
+
117
+ lines = content.splitlines(True)
118
+ before = "".join(lines[:start["line"]])
119
+ after = "".join(lines[end["line"] + 1:])
120
+ current_line = lines[start["line"]]
121
+
122
+ edited_line = (current_line[:start["character"]] +
123
+ new_text +
124
+ current_line[end["character"]:])
125
+
126
+ new_content = before + edited_line + after
127
+
128
+ # Check if new content is valid Python
129
+ jedi.Script(code=new_content)
130
+ return True
131
+ except Exception:
132
+ return False
133
+
134
+ def shutdown(self):
135
+ self.script_cache.clear()
@@ -0,0 +1,234 @@
1
+ import importlib
2
+ import inspect
3
+ import os
4
+ import re
5
+ import sys
6
+ from typing import Dict, Type, Optional, List
7
+ from jarvis.jarvis_lsp.base import BaseLSP
8
+ from jarvis.utils import PrettyOutput, OutputType
9
+
10
+ REQUIRED_METHODS = [
11
+ ('initialize', ['workspace_path']),
12
+ ('find_references', ['file_path', 'position']),
13
+ ('find_definition', ['file_path', 'position']),
14
+ ('get_document_symbols', ['file_path']),
15
+ ('get_diagnostics', ['file_path']),
16
+ ('prepare_rename', ['file_path', 'position']),
17
+ ('validate_edit', ['file_path', 'edit']),
18
+ ('shutdown', [])
19
+ ]
20
+
21
+ class LSPRegistry:
22
+ """LSP server registry"""
23
+
24
+ global_lsp_registry = None
25
+
26
+ @staticmethod
27
+ def get_lsp_dir() -> str:
28
+ """Get LSP implementation directory."""
29
+ user_lsp_dir = os.path.expanduser("~/.jarvis/lsp")
30
+ if not os.path.exists(user_lsp_dir):
31
+ try:
32
+ os.makedirs(user_lsp_dir)
33
+ with open(os.path.join(user_lsp_dir, "__init__.py"), "w") as f:
34
+ pass
35
+ except Exception as e:
36
+ PrettyOutput.print(f"Create LSP directory failed: {str(e)}", OutputType.ERROR)
37
+ return ""
38
+ return user_lsp_dir
39
+
40
+ @staticmethod
41
+ def check_lsp_implementation(lsp_class: Type[BaseLSP]) -> bool:
42
+ """Check if the LSP class implements all necessary methods."""
43
+ missing_methods = []
44
+
45
+ for method_name, params in REQUIRED_METHODS:
46
+ if not hasattr(lsp_class, method_name):
47
+ missing_methods.append(method_name)
48
+ continue
49
+
50
+ method = getattr(lsp_class, method_name)
51
+ if not callable(method):
52
+ missing_methods.append(method_name)
53
+ continue
54
+
55
+ sig = inspect.signature(method)
56
+ method_params = [p for p in sig.parameters if p != 'self']
57
+ if len(method_params) != len(params):
58
+ missing_methods.append(f"{method_name}(parameter mismatch)")
59
+
60
+ if missing_methods:
61
+ PrettyOutput.print(
62
+ f"LSP {lsp_class.__name__} is missing necessary methods: {', '.join(missing_methods)}",
63
+ OutputType.ERROR
64
+ )
65
+ return False
66
+
67
+ return True
68
+
69
+ @staticmethod
70
+ def load_lsp_from_dir(directory: str) -> Dict[str, Type[BaseLSP]]:
71
+ """Load LSP implementations from specified directory."""
72
+ lsp_servers = {}
73
+
74
+ if not os.path.exists(directory):
75
+ PrettyOutput.print(f"LSP directory does not exist: {directory}", OutputType.ERROR)
76
+ return lsp_servers
77
+
78
+ package_name = None
79
+ if directory == os.path.dirname(__file__):
80
+ package_name = "jarvis.jarvis_lsp"
81
+
82
+ if directory not in sys.path:
83
+ sys.path.append(directory)
84
+
85
+ for filename in os.listdir(directory):
86
+ if filename.endswith('.py') and not filename.startswith('__'):
87
+ module_name = filename[:-3]
88
+ try:
89
+ if package_name:
90
+ module = importlib.import_module(f"{package_name}.{module_name}")
91
+ else:
92
+ module = importlib.import_module(module_name)
93
+
94
+ for _, obj in inspect.getmembers(module):
95
+ if (inspect.isclass(obj) and
96
+ issubclass(obj, BaseLSP) and
97
+ obj != BaseLSP and
98
+ hasattr(obj, 'language')):
99
+ if not LSPRegistry.check_lsp_implementation(obj):
100
+ continue
101
+ if hasattr(obj, 'check'):
102
+ if not obj.check(): # type: ignore
103
+ continue
104
+ lsp_servers[obj.language] = obj
105
+ break
106
+ except Exception as e:
107
+ PrettyOutput.print(f"Load LSP {module_name} failed: {str(e)}", OutputType.ERROR)
108
+
109
+ return lsp_servers
110
+
111
+ @staticmethod
112
+ def get_global_lsp_registry():
113
+ """Get global LSP registry instance."""
114
+ if LSPRegistry.global_lsp_registry is None:
115
+ LSPRegistry.global_lsp_registry = LSPRegistry()
116
+ return LSPRegistry.global_lsp_registry
117
+
118
+ def __init__(self):
119
+ """Initialize LSP registry."""
120
+ self.lsp_servers: Dict[str, Type[BaseLSP]] = {}
121
+
122
+ # Load from user LSP directory
123
+ lsp_dir = LSPRegistry.get_lsp_dir()
124
+ if lsp_dir and os.path.exists(lsp_dir):
125
+ for language, lsp_class in LSPRegistry.load_lsp_from_dir(lsp_dir).items():
126
+ self.register_lsp(language, lsp_class)
127
+
128
+ # Load from built-in LSP directory
129
+ lsp_dir = os.path.dirname(__file__)
130
+ if lsp_dir and os.path.exists(lsp_dir):
131
+ for language, lsp_class in LSPRegistry.load_lsp_from_dir(lsp_dir).items():
132
+ self.register_lsp(language, lsp_class)
133
+
134
+ def register_lsp(self, language: str, lsp_class: Type[BaseLSP]):
135
+ """Register LSP implementation for a language."""
136
+ self.lsp_servers[language] = lsp_class
137
+
138
+ def create_lsp(self, language: str) -> Optional[BaseLSP]:
139
+ """Create LSP instance for specified language."""
140
+ if language not in self.lsp_servers:
141
+ PrettyOutput.print(f"LSP not found for language: {language}", OutputType.ERROR)
142
+ return None
143
+
144
+ try:
145
+ lsp = self.lsp_servers[language]()
146
+ return lsp
147
+ except Exception as e:
148
+ PrettyOutput.print(f"Create LSP failed: {str(e)}", OutputType.ERROR)
149
+ return None
150
+
151
+ def get_supported_languages(self) -> List[str]:
152
+ """Get list of supported languages."""
153
+ return list(self.lsp_servers.keys())
154
+
155
+ @staticmethod
156
+ def get_text_at_position(file_path: str, line: int, start_character: int) -> str:
157
+ """Get text at position."""
158
+ with open(file_path, 'r') as file:
159
+ lines = file.readlines()
160
+ symbol = re.search(r'\b\w+\b', lines[line][start_character:])
161
+ return symbol.group() if symbol else ""
162
+
163
+ @staticmethod
164
+ def get_line_at_position(file_path: str, line: int) -> str:
165
+ """Get line at position."""
166
+ with open(file_path, 'r') as file:
167
+ lines = file.readlines()
168
+ return lines[line]
169
+
170
+ def main():
171
+ """CLI entry point for LSP testing."""
172
+ import argparse
173
+
174
+ parser = argparse.ArgumentParser(description='LSP functionality testing')
175
+ parser.add_argument('--language', type=str, required=True, help='Programming language')
176
+ parser.add_argument('--file', type=str, required=True, help='File to analyze')
177
+ parser.add_argument('--action', choices=['symbols', 'diagnostics', 'references', 'definition'],
178
+ required=True, help='Action to perform')
179
+ parser.add_argument('--line', type=int, help='Line number (0-based) for references/definition')
180
+ parser.add_argument('--character', type=int, help='Character position for references/definition')
181
+
182
+ args = parser.parse_args()
183
+
184
+ # Initialize LSP
185
+ registry = LSPRegistry.get_global_lsp_registry()
186
+ lsp = registry.create_lsp(args.language)
187
+
188
+ if not lsp:
189
+ PrettyOutput.print(f"No LSP support for language: {args.language}", OutputType.ERROR)
190
+ return 1
191
+
192
+ if not lsp.initialize(os.path.dirname(os.path.abspath(args.file))):
193
+ PrettyOutput.print("LSP initialization failed", OutputType.ERROR)
194
+ return 1
195
+
196
+ try:
197
+ # Execute requested action
198
+ if args.action == 'symbols':
199
+ symbols = lsp.get_document_symbols(args.file)
200
+ for symbol in symbols:
201
+ print(f"Symbol {LSPRegistry.get_text_at_position(args.file, symbol['range']['start']['line'], symbol['range']['start']['character'])} at {symbol['range']['start']['line']}:{symbol['range']['start']['character']}: {symbol['uri']}")
202
+
203
+ elif args.action == 'diagnostics':
204
+ diagnostics = lsp.get_diagnostics(args.file)
205
+ for diag in diagnostics:
206
+ severity = ['Error', 'Warning', 'Info', 'Hint'][diag['severity'] - 1]
207
+ print(f"{severity} at {diag['range']['start']['line']}:{diag['range']['start']['character']}: {diag['message']}")
208
+
209
+ elif args.action in ('references', 'definition'):
210
+ if args.line is None or args.character is None:
211
+ PrettyOutput.print("Line and character position required for references/definition", OutputType.ERROR)
212
+ return 1
213
+
214
+ if args.action == 'references':
215
+ refs = lsp.find_references(args.file, (args.line, args.character))
216
+ for ref in refs:
217
+ print(f"Reference in {ref['uri']} at {ref['range']['start']['line']}:{ref['range']['start']['character']}\nLine: {LSPRegistry.get_line_at_position(ref['uri'], ref['range']['start']['line'])}")
218
+ else:
219
+ defn = lsp.find_definition(args.file, (args.line, args.character))
220
+ if defn:
221
+ print(f"Definition in {defn['uri']} at {defn['range']['start']['line']}:{defn['range']['start']['character']}\nLine: {LSPRegistry.get_line_at_position(defn['uri'], defn['range']['start']['line'])}")
222
+ else:
223
+ print("No definition found")
224
+
225
+ except Exception as e:
226
+ PrettyOutput.print(f"Error: {str(e)}", OutputType.ERROR)
227
+ return 1
228
+ finally:
229
+ lsp.shutdown()
230
+
231
+ return 0
232
+
233
+ if __name__ == "__main__":
234
+ exit(main())
@@ -0,0 +1,142 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ from typing import List, Dict, Optional, Tuple, Any
5
+ import json
6
+ from jarvis.jarvis_lsp.base import BaseLSP
7
+ from jarvis.utils import PrettyOutput, OutputType
8
+
9
+ class RustLSP(BaseLSP):
10
+ """Rust LSP implementation using rust-analyzer."""
11
+
12
+ language = "rust"
13
+
14
+ @staticmethod
15
+ def check() -> bool:
16
+ """Check if rust-analyzer is installed."""
17
+ return shutil.which("rust-analyzer") is not None
18
+
19
+ def __init__(self):
20
+ self.workspace_path = ""
21
+ self.analyzer_process = None
22
+ self.request_id = 0
23
+
24
+ def initialize(self, workspace_path: str) -> bool:
25
+ try:
26
+ self.workspace_path = workspace_path
27
+ # Start rust-analyzer process
28
+ self.analyzer_process = subprocess.Popen(
29
+ ["rust-analyzer"],
30
+ stdin=subprocess.PIPE,
31
+ stdout=subprocess.PIPE,
32
+ stderr=subprocess.PIPE
33
+ )
34
+
35
+ # Send initialize request
36
+ self._send_request("initialize", {
37
+ "processId": os.getpid(),
38
+ "rootUri": f"file://{workspace_path}",
39
+ "capabilities": {
40
+ "textDocument": {
41
+ "semanticTokens": {"full": True},
42
+ "hover": {"contentFormat": ["markdown"]},
43
+ "inlayHint": {"resolveSupport": {"properties": ["label.tooltip", "label.location", "label.command"]}},
44
+ "completion": {"completionItem": {"snippetSupport": True}}
45
+ }
46
+ },
47
+ "workspaceFolders": [{"uri": f"file://{workspace_path}", "name": "workspace"}]
48
+ })
49
+
50
+ return True
51
+ except Exception as e:
52
+ PrettyOutput.print(f"Rust LSP initialization failed: {str(e)}", OutputType.ERROR)
53
+ return False
54
+
55
+ def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
56
+ """Send JSON-RPC request to rust-analyzer."""
57
+ if not self.analyzer_process:
58
+ return None
59
+
60
+ try:
61
+ self.request_id += 1
62
+ request = {
63
+ "jsonrpc": "2.0",
64
+ "id": self.request_id,
65
+ "method": method,
66
+ "params": params
67
+ }
68
+
69
+ self.analyzer_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
70
+ self.analyzer_process.stdin.flush() # type: ignore
71
+
72
+ response = json.loads(self.analyzer_process.stdout.readline().decode()) # type: ignore
73
+ return response.get("result")
74
+ except Exception:
75
+ return None
76
+
77
+ def find_references(self, file_path: str, position: Tuple[int, int]) -> List[Dict[str, Any]]:
78
+ result = self._send_request("textDocument/references", {
79
+ "textDocument": {"uri": f"file://{file_path}"},
80
+ "position": {"line": position[0], "character": position[1]},
81
+ "context": {"includeDeclaration": True}
82
+ })
83
+ return result or [] # type: ignore
84
+
85
+ def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
86
+ result = self._send_request("textDocument/definition", {
87
+ "textDocument": {"uri": f"file://{file_path}"},
88
+ "position": {"line": position[0], "character": position[1]}
89
+ })
90
+ return result[0] if result else None
91
+
92
+ def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
93
+ result = self._send_request("textDocument/documentSymbol", {
94
+ "textDocument": {"uri": f"file://{file_path}"}
95
+ })
96
+ return result or [] # type: ignore
97
+
98
+ def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
99
+ # Send didOpen notification to trigger diagnostics
100
+ self._send_request("textDocument/didOpen", {
101
+ "textDocument": {
102
+ "uri": f"file://{file_path}",
103
+ "languageId": "rust",
104
+ "version": 1,
105
+ "text": open(file_path).read()
106
+ }
107
+ })
108
+
109
+ # Wait for diagnostic notification
110
+ try:
111
+ response = json.loads(self.analyzer_process.stdout.readline().decode()) # type: ignore
112
+ if response.get("method") == "textDocument/publishDiagnostics":
113
+ return response.get("params", {}).get("diagnostics", [])
114
+ except Exception:
115
+ pass
116
+ return []
117
+
118
+ def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
119
+ result = self._send_request("textDocument/prepareRename", {
120
+ "textDocument": {"uri": f"file://{file_path}"},
121
+ "position": {"line": position[0], "character": position[1]}
122
+ })
123
+ return result
124
+
125
+ def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
126
+ # Send workspace/willRenameFiles request to check validity
127
+ result = self._send_request("workspace/willRenameFiles", {
128
+ "files": [{
129
+ "oldUri": f"file://{file_path}",
130
+ "newUri": f"file://{file_path}.tmp"
131
+ }]
132
+ })
133
+ return bool(result)
134
+
135
+ def shutdown(self):
136
+ if self.analyzer_process:
137
+ try:
138
+ self._send_request("shutdown", {})
139
+ self.analyzer_process.terminate()
140
+ self.analyzer_process = None
141
+ except Exception:
142
+ pass
@@ -0,0 +1,3 @@
1
+ from .base import BasePlatform
2
+
3
+ __all__ = ['BasePlatform']
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from typing import Dict, List, Tuple
3
- from jarvis.models.base import BasePlatform
3
+ from jarvis.jarvis_platform.base import BasePlatform
4
4
  from jarvis.utils import PrettyOutput, OutputType
5
5
  import requests
6
6
  import json
@@ -4,7 +4,7 @@ import json
4
4
  import os
5
5
  import mimetypes
6
6
  import time
7
- from jarvis.models.base import BasePlatform
7
+ from jarvis.jarvis_platform.base import BasePlatform
8
8
  from jarvis.utils import PrettyOutput, OutputType
9
9
  from jarvis.utils import while_success
10
10
 
@@ -1,6 +1,6 @@
1
1
  import requests
2
2
  from typing import List, Dict, Tuple
3
- from jarvis.models.base import BasePlatform
3
+ from jarvis.jarvis_platform.base import BasePlatform
4
4
  from jarvis.utils import OutputType, PrettyOutput, get_single_line_input
5
5
  import os
6
6
  import json