jarvis-ai-assistant 0.1.132__py3-none-any.whl → 0.1.138__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 (82) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +330 -347
  3. jarvis/jarvis_agent/builtin_input_handler.py +16 -6
  4. jarvis/jarvis_agent/file_input_handler.py +9 -9
  5. jarvis/jarvis_agent/jarvis.py +143 -0
  6. jarvis/jarvis_agent/main.py +12 -13
  7. jarvis/jarvis_agent/output_handler.py +3 -3
  8. jarvis/jarvis_agent/patch.py +92 -64
  9. jarvis/jarvis_agent/shell_input_handler.py +5 -3
  10. jarvis/jarvis_code_agent/code_agent.py +263 -177
  11. jarvis/jarvis_code_agent/file_select.py +24 -24
  12. jarvis/jarvis_dev/main.py +45 -59
  13. jarvis/jarvis_git_details/__init__.py +0 -0
  14. jarvis/jarvis_git_details/main.py +179 -0
  15. jarvis/jarvis_git_squash/main.py +7 -7
  16. jarvis/jarvis_lsp/base.py +11 -53
  17. jarvis/jarvis_lsp/cpp.py +13 -28
  18. jarvis/jarvis_lsp/go.py +13 -28
  19. jarvis/jarvis_lsp/python.py +8 -27
  20. jarvis/jarvis_lsp/registry.py +21 -83
  21. jarvis/jarvis_lsp/rust.py +15 -30
  22. jarvis/jarvis_methodology/main.py +101 -0
  23. jarvis/jarvis_multi_agent/__init__.py +10 -51
  24. jarvis/jarvis_multi_agent/main.py +43 -0
  25. jarvis/jarvis_platform/__init__.py +1 -1
  26. jarvis/jarvis_platform/ai8.py +67 -89
  27. jarvis/jarvis_platform/base.py +14 -13
  28. jarvis/jarvis_platform/kimi.py +25 -28
  29. jarvis/jarvis_platform/ollama.py +24 -26
  30. jarvis/jarvis_platform/openai.py +15 -19
  31. jarvis/jarvis_platform/oyi.py +48 -50
  32. jarvis/jarvis_platform/registry.py +29 -44
  33. jarvis/jarvis_platform/yuanbao.py +39 -43
  34. jarvis/jarvis_platform_manager/main.py +81 -81
  35. jarvis/jarvis_platform_manager/openai_test.py +21 -21
  36. jarvis/jarvis_rag/file_processors.py +18 -18
  37. jarvis/jarvis_rag/main.py +262 -278
  38. jarvis/jarvis_smart_shell/main.py +12 -12
  39. jarvis/jarvis_tools/ask_codebase.py +85 -78
  40. jarvis/jarvis_tools/ask_user.py +8 -8
  41. jarvis/jarvis_tools/base.py +4 -4
  42. jarvis/jarvis_tools/chdir.py +9 -9
  43. jarvis/jarvis_tools/code_review.py +40 -21
  44. jarvis/jarvis_tools/create_code_agent.py +15 -15
  45. jarvis/jarvis_tools/create_sub_agent.py +0 -1
  46. jarvis/jarvis_tools/execute_python_script.py +3 -3
  47. jarvis/jarvis_tools/execute_shell.py +11 -11
  48. jarvis/jarvis_tools/execute_shell_script.py +3 -3
  49. jarvis/jarvis_tools/file_analyzer.py +116 -105
  50. jarvis/jarvis_tools/file_operation.py +22 -20
  51. jarvis/jarvis_tools/find_caller.py +105 -40
  52. jarvis/jarvis_tools/find_methodolopy.py +65 -0
  53. jarvis/jarvis_tools/find_symbol.py +123 -39
  54. jarvis/jarvis_tools/function_analyzer.py +140 -57
  55. jarvis/jarvis_tools/git_commiter.py +10 -10
  56. jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
  57. jarvis/jarvis_tools/methodology.py +22 -67
  58. jarvis/jarvis_tools/project_analyzer.py +137 -53
  59. jarvis/jarvis_tools/rag.py +15 -20
  60. jarvis/jarvis_tools/read_code.py +25 -23
  61. jarvis/jarvis_tools/read_webpage.py +31 -31
  62. jarvis/jarvis_tools/registry.py +72 -52
  63. jarvis/jarvis_tools/search_web.py +23 -353
  64. jarvis/jarvis_tools/tool_generator.py +19 -19
  65. jarvis/jarvis_utils/config.py +36 -96
  66. jarvis/jarvis_utils/embedding.py +83 -83
  67. jarvis/jarvis_utils/git_utils.py +20 -20
  68. jarvis/jarvis_utils/globals.py +18 -6
  69. jarvis/jarvis_utils/input.py +10 -9
  70. jarvis/jarvis_utils/methodology.py +141 -140
  71. jarvis/jarvis_utils/output.py +13 -13
  72. jarvis/jarvis_utils/utils.py +23 -71
  73. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +6 -15
  74. jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
  75. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +4 -3
  76. jarvis/jarvis_tools/lsp_find_definition.py +0 -150
  77. jarvis/jarvis_tools/lsp_find_references.py +0 -127
  78. jarvis/jarvis_tools/select_code_files.py +0 -62
  79. jarvis_ai_assistant-0.1.132.dist-info/RECORD +0 -82
  80. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
  81. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
  82. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
1
  from typing import Dict, Any
2
2
  import os
3
3
 
4
+ from pkg_resources import add_activation_listener
4
5
  from yaspin import yaspin
5
6
 
7
+ from jarvis.jarvis_utils.globals import add_read_file_record
6
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
9
 
8
10
  class ReadCodeTool:
@@ -30,17 +32,18 @@ class ReadCodeTool:
30
32
 
31
33
  def _handle_single_file(self, filepath: str, start_line: int = 1, end_line: int = -1) -> Dict[str, Any]:
32
34
  """处理单个文件的读取操作
33
-
35
+
34
36
  Args:
35
37
  filepath (str): 文件路径
36
38
  start_line (int): 起始行号,默认为1
37
39
  end_line (int): 结束行号,默认为-1表示文件末尾
38
-
40
+
39
41
  Returns:
40
42
  Dict[str, Any]: 包含成功状态、输出内容和错误信息的字典
41
43
  """
42
44
  try:
43
45
  abs_path = os.path.abspath(filepath)
46
+ add_read_file_record(abs_path)
44
47
  with yaspin(text=f"正在读取文件: {abs_path}...", color="cyan") as spinner:
45
48
  # 文件存在性检查
46
49
  if not os.path.exists(abs_path):
@@ -49,7 +52,7 @@ class ReadCodeTool:
49
52
  "stdout": "",
50
53
  "stderr": f"文件不存在: {abs_path}"
51
54
  }
52
-
55
+
53
56
  # 文件大小限制检查(10MB)
54
57
  if os.path.getsize(abs_path) > 10 * 1024 * 1024:
55
58
  return {
@@ -57,21 +60,21 @@ class ReadCodeTool:
57
60
  "stdout": "",
58
61
  "stderr": "文件过大 (>10MB)"
59
62
  }
60
-
63
+
61
64
  # 读取文件内容
62
65
  with open(abs_path, 'r', encoding='utf-8', errors="ignore") as f:
63
66
  lines = f.readlines()
64
-
67
+
65
68
  total_lines = len(lines)
66
-
69
+
67
70
  # 处理特殊值-1表示文件末尾
68
71
  if end_line == -1:
69
72
  end_line = total_lines
70
73
  else:
71
74
  end_line = max(1, min(end_line, total_lines)) if end_line >= 0 else total_lines + end_line + 1
72
-
75
+
73
76
  start_line = max(1, min(start_line, total_lines)) if start_line >= 0 else total_lines + start_line + 1
74
-
77
+
75
78
  if start_line > end_line:
76
79
  spinner.fail("❌")
77
80
  return {
@@ -79,20 +82,19 @@ class ReadCodeTool:
79
82
  "stdout": "",
80
83
  "stderr": f"无效的行范围 [{start_line}-{end_line}] (总行数: {total_lines})"
81
84
  }
82
-
85
+
83
86
  # 添加行号并构建输出内容
84
87
  selected_lines = lines[start_line-1:end_line]
85
88
  numbered_content = "".join(
86
- [f"{i:4d}:{line}"
89
+ [f"{i:4d}:{line}"
87
90
  for i, line in enumerate(selected_lines, start=start_line)]
88
91
  )
89
-
92
+
90
93
  # 构建输出格式
91
94
  output = (
92
95
  f"\n🔍 文件: {abs_path}\n"
93
- f"📄 原始行号: {start_line}-{end_line} (共{end_line - start_line + 1}行) | 显示行号: 1-{len(selected_lines)}\n\n"
94
- f"{numbered_content}\n"
95
- f"{'='*80}\n"
96
+ f"📄 原始行号: {start_line}-{end_line} (共{total_lines}行) \n\n"
97
+ f"{numbered_content}\n\n"
96
98
  )
97
99
  spinner.text = f"文件读取完成: {abs_path}"
98
100
  spinner.ok("✅")
@@ -104,7 +106,7 @@ class ReadCodeTool:
104
106
  "stdout": output,
105
107
  "stderr": ""
106
108
  }
107
-
109
+
108
110
  except Exception as e:
109
111
  PrettyOutput.print(str(e), OutputType.ERROR)
110
112
  return {
@@ -115,10 +117,10 @@ class ReadCodeTool:
115
117
 
116
118
  def execute(self, args: Dict) -> Dict[str, Any]:
117
119
  """执行代码读取操作
118
-
120
+
119
121
  Args:
120
122
  args (Dict): 包含文件列表的参数字典
121
-
123
+
122
124
  Returns:
123
125
  Dict[str, Any]: 包含成功状态、输出内容和错误信息的字典
124
126
  """
@@ -129,32 +131,32 @@ class ReadCodeTool:
129
131
  "stdout": "",
130
132
  "stderr": "参数中必须包含文件列表"
131
133
  }
132
-
134
+
133
135
  all_outputs = []
134
136
  overall_success = True
135
-
137
+
136
138
  for file_info in args["files"]:
137
139
  if not isinstance(file_info, dict) or "path" not in file_info:
138
140
  continue
139
-
141
+
140
142
  result = self._handle_single_file(
141
143
  file_info["path"].strip(),
142
144
  file_info.get("start_line", 1),
143
145
  file_info.get("end_line", -1)
144
146
  )
145
-
147
+
146
148
  if result["success"]:
147
149
  all_outputs.append(result["stdout"])
148
150
  else:
149
151
  all_outputs.append(f"❌ {file_info['path']}: {result['stderr']}")
150
152
  overall_success = False
151
-
153
+
152
154
  return {
153
155
  "success": overall_success,
154
156
  "stdout": "\n".join(all_outputs),
155
157
  "stderr": ""
156
158
  }
157
-
159
+
158
160
  except Exception as e:
159
161
  PrettyOutput.print(str(e), OutputType.ERROR)
160
162
  return {
@@ -24,38 +24,38 @@ class WebpageTool:
24
24
  """Read webpage content using Playwright to handle JavaScript-rendered pages"""
25
25
  try:
26
26
  url = args["url"].strip()
27
-
27
+
28
28
  with sync_playwright() as p:
29
29
  # Launch browser
30
30
  browser = p.chromium.launch(
31
31
  headless=True,
32
32
  args=['--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage']
33
33
  )
34
-
34
+
35
35
  # Create a new page with appropriate settings
36
36
  page = browser.new_page(
37
37
  user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
38
38
  viewport={'width': 1920, 'height': 1080}
39
39
  )
40
-
40
+
41
41
  # Set timeout to avoid long waits
42
42
  page.set_default_timeout(30000) # 30 seconds
43
-
43
+
44
44
  try:
45
45
  # Navigate to URL and wait for page to load
46
46
  response = page.goto(url, wait_until="domcontentloaded")
47
-
47
+
48
48
  # Additional wait for network to be idle (with a timeout)
49
49
  try:
50
50
  page.wait_for_load_state("networkidle", timeout=10000)
51
51
  except PlaywrightTimeoutError:
52
52
  # Continue even if network doesn't become completely idle
53
53
  pass
54
-
54
+
55
55
  # Make sure we got a valid response
56
56
  if not response or response.status >= 400:
57
57
  raise Exception(f"Failed to load page: HTTP {response.status if response else 'No response'}")
58
-
58
+
59
59
  # Get page title safely
60
60
  title = "No title"
61
61
  try:
@@ -68,26 +68,26 @@ class WebpageTool:
68
68
  title = title_element.text_content() or "No title"
69
69
  except Exception:
70
70
  pass
71
-
71
+
72
72
  # Get the HTML content after JavaScript execution
73
73
  html_content = page.content()
74
-
74
+
75
75
  except Exception as e:
76
76
  raise Exception(f"Error navigating to page: {str(e)}")
77
77
  finally:
78
78
  # Always close browser
79
79
  browser.close()
80
-
80
+
81
81
  # Parse with BeautifulSoup and convert to markdown
82
82
  markdown_content = self._html_to_markdown(html_content, url)
83
-
83
+
84
84
  # Build output in markdown format
85
85
  output = [
86
86
  f"# {title}",
87
87
  f"Url: {url}",
88
88
  markdown_content
89
89
  ]
90
-
90
+
91
91
  return {
92
92
  "success": True,
93
93
  "stdout": "\n".join(output),
@@ -101,7 +101,7 @@ class WebpageTool:
101
101
  "stdout": "",
102
102
  "stderr": f"Failed to parse webpage: {str(e)}"
103
103
  }
104
-
104
+
105
105
  def _create_soup_element(self, content):
106
106
  """Safely create a BeautifulSoup element, ensuring it's treated as markup"""
107
107
  if isinstance(content, str):
@@ -112,15 +112,15 @@ class WebpageTool:
112
112
  # Return an empty list if the div is None
113
113
  return []
114
114
  return content
115
-
115
+
116
116
  def _html_to_markdown(self, html_content: str, base_url: str) -> str:
117
117
  """Convert HTML to Markdown format preserving the content structure"""
118
118
  soup = BeautifulSoup(html_content, 'html.parser')
119
-
119
+
120
120
  # Remove unwanted elements
121
121
  for element in soup(['script', 'style', 'meta', 'noscript', 'head']):
122
122
  element.decompose()
123
-
123
+
124
124
  # Process headings
125
125
  for level in range(1, 7):
126
126
  for heading in soup.find_all(f'h{level}'):
@@ -128,14 +128,14 @@ class WebpageTool:
128
128
  heading_md = "\n\n" + "#" * level + " " + text + "\n\n"
129
129
  new_element = self._create_soup_element(heading_md)
130
130
  heading.replace_with(*new_element)
131
-
131
+
132
132
  # Process paragraphs
133
133
  for p in soup.find_all('p'):
134
134
  text = p.get_text().strip()
135
135
  if text:
136
136
  new_element = self._create_soup_element("\n\n" + text + "\n\n")
137
137
  p.replace_with(*new_element)
138
-
138
+
139
139
  # Process unordered lists
140
140
  for ul in soup.find_all('ul'):
141
141
  items = []
@@ -143,7 +143,7 @@ class WebpageTool:
143
143
  items.append("* " + li.get_text().strip())
144
144
  new_element = self._create_soup_element("\n\n" + "\n".join(items) + "\n\n")
145
145
  ul.replace_with(*new_element)
146
-
146
+
147
147
  # Process ordered lists
148
148
  for ol in soup.find_all('ol'):
149
149
  items = []
@@ -151,7 +151,7 @@ class WebpageTool:
151
151
  items.append(str(i) + ". " + li.get_text().strip())
152
152
  new_element = self._create_soup_element("\n\n" + "\n".join(items) + "\n\n")
153
153
  ol.replace_with(*new_element)
154
-
154
+
155
155
  # Process links (first pass)
156
156
  for a in soup.find_all('a', href=True):
157
157
  try:
@@ -166,7 +166,7 @@ class WebpageTool:
166
166
  a.replace_with(*new_element)
167
167
  except (KeyError, AttributeError):
168
168
  continue
169
-
169
+
170
170
  # Process images
171
171
  for img in soup.find_all('img', src=True):
172
172
  try:
@@ -180,37 +180,37 @@ class WebpageTool:
180
180
  img.replace_with(*new_element)
181
181
  except (KeyError, AttributeError, UnboundLocalError):
182
182
  continue
183
-
183
+
184
184
  # Process code blocks
185
185
  for pre in soup.find_all('pre'):
186
186
  code = pre.get_text().strip()
187
187
  pre_md = "\n\n```\n" + code + "\n```\n\n"
188
188
  new_element = self._create_soup_element(pre_md)
189
189
  pre.replace_with(*new_element)
190
-
190
+
191
191
  # Process inline code
192
192
  for code in soup.find_all('code'):
193
193
  text = code.get_text().strip()
194
194
  code_md = "`" + text + "`"
195
195
  new_element = self._create_soup_element(code_md)
196
196
  code.replace_with(*new_element)
197
-
197
+
198
198
  # Process line breaks
199
199
  for br in soup.find_all('br'):
200
200
  new_element = self._create_soup_element('\n')
201
201
  br.replace_with(*new_element)
202
-
202
+
203
203
  # Get the full text
204
204
  markdown_text = soup.get_text()
205
-
205
+
206
206
  # Clean up extra whitespace and line breaks
207
207
  markdown_text = re.sub(r'\n{3,}', '\n\n', markdown_text)
208
208
  markdown_text = re.sub(r'\s{2,}', ' ', markdown_text)
209
-
209
+
210
210
  # Process links again (for any that might have been missed)
211
211
  link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
212
212
  all_links = re.findall(link_pattern, markdown_text)
213
-
213
+
214
214
  # Add a section with all links at the end
215
215
  if all_links:
216
216
  link_section = ["", "## Links", ""]
@@ -220,7 +220,7 @@ class WebpageTool:
220
220
  if link_entry not in seen_links:
221
221
  link_section.append(link_entry)
222
222
  seen_links.add(link_entry)
223
-
223
+
224
224
  markdown_text += "\n\n" + "\n".join(link_section)
225
-
226
- return markdown_text.strip()
225
+
226
+ return markdown_text.strip()