jarvis-ai-assistant 0.1.192__py3-none-any.whl → 0.1.194__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 (91) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +69 -37
  3. jarvis/jarvis_agent/builtin_input_handler.py +26 -4
  4. jarvis/jarvis_agent/jarvis.py +38 -22
  5. jarvis/jarvis_agent/main.py +20 -12
  6. jarvis/jarvis_agent/output_handler.py +7 -7
  7. jarvis/jarvis_agent/shell_input_handler.py +14 -11
  8. jarvis/jarvis_code_agent/code_agent.py +93 -90
  9. jarvis/jarvis_code_agent/lint.py +92 -105
  10. jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
  11. jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
  12. jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
  13. jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
  14. jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
  15. jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
  16. jarvis/jarvis_code_analysis/checklists/go.py +1 -1
  17. jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
  18. jarvis/jarvis_code_analysis/checklists/java.py +1 -1
  19. jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
  20. jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
  21. jarvis/jarvis_code_analysis/checklists/loader.py +51 -35
  22. jarvis/jarvis_code_analysis/checklists/php.py +1 -1
  23. jarvis/jarvis_code_analysis/checklists/python.py +1 -1
  24. jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
  25. jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
  26. jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
  27. jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
  28. jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
  29. jarvis/jarvis_code_analysis/checklists/web.py +1 -1
  30. jarvis/jarvis_code_analysis/code_review.py +293 -192
  31. jarvis/jarvis_dev/main.py +73 -56
  32. jarvis/jarvis_git_details/main.py +29 -33
  33. jarvis/jarvis_git_squash/main.py +13 -11
  34. jarvis/jarvis_git_utils/git_commiter.py +12 -3
  35. jarvis/jarvis_mcp/__init__.py +8 -10
  36. jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
  37. jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
  38. jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
  39. jarvis/jarvis_methodology/main.py +81 -47
  40. jarvis/jarvis_multi_agent/__init__.py +24 -16
  41. jarvis/jarvis_multi_agent/main.py +10 -4
  42. jarvis/jarvis_platform/__init__.py +1 -1
  43. jarvis/jarvis_platform/base.py +49 -21
  44. jarvis/jarvis_platform/human.py +5 -3
  45. jarvis/jarvis_platform/kimi.py +96 -72
  46. jarvis/jarvis_platform/openai.py +23 -28
  47. jarvis/jarvis_platform/registry.py +50 -33
  48. jarvis/jarvis_platform/tongyi.py +16 -10
  49. jarvis/jarvis_platform/yuanbao.py +205 -147
  50. jarvis/jarvis_platform_manager/main.py +4 -2
  51. jarvis/jarvis_smart_shell/main.py +35 -29
  52. jarvis/jarvis_tools/ask_user.py +8 -16
  53. jarvis/jarvis_tools/base.py +3 -2
  54. jarvis/jarvis_tools/chdir.py +7 -19
  55. jarvis/jarvis_tools/cli/main.py +14 -10
  56. jarvis/jarvis_tools/code_plan.py +10 -31
  57. jarvis/jarvis_tools/create_code_agent.py +10 -13
  58. jarvis/jarvis_tools/create_sub_agent.py +10 -22
  59. jarvis/jarvis_tools/edit_file.py +98 -76
  60. jarvis/jarvis_tools/execute_script.py +46 -46
  61. jarvis/jarvis_tools/file_analyzer.py +22 -34
  62. jarvis/jarvis_tools/file_operation.py +69 -62
  63. jarvis/jarvis_tools/generate_new_tool.py +0 -2
  64. jarvis/jarvis_tools/methodology.py +19 -23
  65. jarvis/jarvis_tools/read_code.py +42 -33
  66. jarvis/jarvis_tools/read_webpage.py +7 -16
  67. jarvis/jarvis_tools/registry.py +65 -32
  68. jarvis/jarvis_tools/rewrite_file.py +26 -29
  69. jarvis/jarvis_tools/search_web.py +5 -8
  70. jarvis/jarvis_tools/virtual_tty.py +133 -122
  71. jarvis/jarvis_utils/__init__.py +0 -1
  72. jarvis/jarvis_utils/builtin_replace_map.py +96 -8
  73. jarvis/jarvis_utils/config.py +59 -32
  74. jarvis/jarvis_utils/embedding.py +17 -14
  75. jarvis/jarvis_utils/file_processors.py +16 -9
  76. jarvis/jarvis_utils/git_utils.py +140 -99
  77. jarvis/jarvis_utils/globals.py +1 -1
  78. jarvis/jarvis_utils/input.py +84 -52
  79. jarvis/jarvis_utils/methodology.py +28 -21
  80. jarvis/jarvis_utils/output.py +159 -78
  81. jarvis/jarvis_utils/tag.py +2 -1
  82. jarvis/jarvis_utils/utils.py +85 -51
  83. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/METADATA +337 -204
  84. jarvis_ai_assistant-0.1.194.dist-info/RECORD +97 -0
  85. jarvis/jarvis_agent/file_input_handler.py +0 -112
  86. jarvis/jarvis_event/__init__.py +0 -0
  87. jarvis_ai_assistant-0.1.192.dist-info/RECORD +0 -99
  88. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/WHEEL +0 -0
  89. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/entry_points.txt +0 -0
  90. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/licenses/LICENSE +0 -0
  91. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,7 @@ from colorama import Fore
12
12
  from colorama import Style as ColoramaStyle
13
13
  from fuzzywuzzy import process
14
14
  from prompt_toolkit import PromptSession
15
- from prompt_toolkit.completion import Completer, Completion, PathCompleter
15
+ from prompt_toolkit.completion import Completer, Completion, PathCompleter, CompleteEvent
16
16
  from prompt_toolkit.document import Document
17
17
  from prompt_toolkit.formatted_text import FormattedText
18
18
  from prompt_toolkit.key_binding import KeyBindings
@@ -34,10 +34,14 @@ def get_single_line_input(tip: str) -> str:
34
34
  str: 用户的输入
35
35
  """
36
36
  session = PromptSession(history=None)
37
- style = PromptStyle.from_dict({
38
- 'prompt': 'ansicyan',
39
- })
37
+ style = PromptStyle.from_dict(
38
+ {
39
+ "prompt": "ansicyan",
40
+ }
41
+ )
40
42
  return session.prompt(f"{tip}", style=style)
43
+
44
+
41
45
  class FileCompleter(Completer):
42
46
  """
43
47
  带有模糊匹配的文件路径自定义补全器。
@@ -47,13 +51,15 @@ class FileCompleter(Completer):
47
51
  max_suggestions: 显示的最大建议数量
48
52
  min_score: 建议的最小匹配分数
49
53
  """
54
+
50
55
  def __init__(self):
51
56
  """使用默认设置初始化文件补全器。"""
52
57
  self.path_completer = PathCompleter()
53
58
  self.max_suggestions = 10
54
59
  self.min_score = 10
55
60
  self.replace_map = get_replace_map()
56
- def get_completions(self, document: Document, complete_event) -> Completion: # type: ignore
61
+
62
+ def get_completions(self, document: Document, _: CompleteEvent) -> Completion: # type: ignore
57
63
  """
58
64
  生成带有模糊匹配的文件路径补全建议。
59
65
 
@@ -67,7 +73,7 @@ class FileCompleter(Completer):
67
73
  text = document.text_before_cursor
68
74
  cursor_pos = document.cursor_position
69
75
  # 查找文本中的所有@位置
70
- at_positions = [i for i, char in enumerate(text) if char == '@']
76
+ at_positions = [i for i, char in enumerate(text) if char == "@"]
71
77
  if not at_positions:
72
78
  return
73
79
  # 获取最后一个@位置
@@ -76,12 +82,10 @@ class FileCompleter(Completer):
76
82
  if cursor_pos <= current_at_pos:
77
83
  return
78
84
  # 检查@之后是否有空格
79
- text_after_at = text[current_at_pos + 1:cursor_pos]
80
- if ' ' in text_after_at:
85
+ text_after_at = text[current_at_pos + 1 : cursor_pos]
86
+ if " " in text_after_at:
81
87
  return
82
-
83
88
 
84
-
85
89
  # 获取当前@之后的文本
86
90
  file_path = text_after_at.strip()
87
91
  # 计算替换长度
@@ -89,38 +93,52 @@ class FileCompleter(Completer):
89
93
 
90
94
  # 获取所有可能的补全项
91
95
  all_completions = []
92
-
96
+
93
97
  # 1. 添加特殊标记
94
- all_completions.extend([
95
- (ot(tag), self._get_description(tag))
96
- for tag in self.replace_map.keys()
97
- ])
98
- all_completions.extend([
99
- (ot("Summary"), '总结'),
100
- (ot("Clear"), '清除历史'),
101
- ])
102
-
98
+ all_completions.extend(
99
+ [(ot(tag), self._get_description(tag)) for tag in self.replace_map.keys()]
100
+ )
101
+ all_completions.extend(
102
+ [
103
+ (ot("Summary"), "总结"),
104
+ (ot("Clear"), "清除历史"),
105
+ (ot("ToolUsage"), "工具使用说明"),
106
+ (ot("ReloadConfig"), "重新加载配置"),
107
+ ]
108
+ )
109
+
103
110
  # 2. 添加文件列表
104
111
  try:
105
112
  import subprocess
106
- result = subprocess.run(['git', 'ls-files'],
107
- stdout=subprocess.PIPE,
108
- stderr=subprocess.PIPE,
109
- text=True)
113
+
114
+ result = subprocess.run(
115
+ ["git", "ls-files"],
116
+ stdout=subprocess.PIPE,
117
+ stderr=subprocess.PIPE,
118
+ text=True,
119
+ )
110
120
  if result.returncode == 0:
111
- all_completions.extend([
112
- (path, "File")
113
- for path in result.stdout.splitlines()
114
- if path.strip()
115
- ])
121
+ all_completions.extend(
122
+ [
123
+ (path, "File")
124
+ for path in result.stdout.splitlines()
125
+ if path.strip()
126
+ ]
127
+ )
116
128
  except Exception:
117
129
  pass
118
-
130
+
119
131
  # 统一过滤和排序
120
132
  if file_path:
121
133
  # 使用模糊匹配过滤
122
- scored_items = process.extract(file_path, [item[0] for item in all_completions], limit=self.max_suggestions)
123
- scored_items = [(item[0], item[1]) for item in scored_items if item[1] > self.min_score]
134
+ scored_items = process.extract(
135
+ file_path,
136
+ [item[0] for item in all_completions],
137
+ limit=self.max_suggestions,
138
+ )
139
+ scored_items = [
140
+ (item[0], item[1]) for item in scored_items if item[1] > self.min_score
141
+ ]
124
142
  # 创建映射以便查找描述
125
143
  completion_map = {item[0]: item[1] for item in all_completions}
126
144
  # 生成补全项
@@ -132,23 +150,29 @@ class FileCompleter(Completer):
132
150
  text=f"'{text}'",
133
151
  start_position=-replace_length,
134
152
  display=display_text,
135
- display_meta=completion_map.get(text, "")
136
- ) # type: ignore
153
+ display_meta=completion_map.get(text, ""),
154
+ ) # type: ignore
137
155
  else:
138
156
  # 没有输入时返回前max_suggestions个建议
139
- for text, desc in all_completions[:self.max_suggestions]:
157
+ for text, desc in all_completions[: self.max_suggestions]:
140
158
  yield Completion(
141
159
  text=f"'{text}'",
142
160
  start_position=-replace_length,
143
161
  display=text,
144
- display_meta=desc
145
- ) # type: ignore
146
-
162
+ display_meta=desc,
163
+ ) # type: ignore
164
+
147
165
  def _get_description(self, tag: str) -> str:
148
166
  """获取标记的描述信息"""
149
167
  if tag in self.replace_map:
150
- return self.replace_map[tag].get("description", tag) + "(Append)" if "append" in self.replace_map[tag] and self.replace_map[tag]["append"] else "(Replace)"
168
+ return (
169
+ self.replace_map[tag].get("description", tag) + "(Append)"
170
+ if "append" in self.replace_map[tag] and self.replace_map[tag]["append"]
171
+ else "(Replace)"
172
+ )
151
173
  return tag
174
+
175
+
152
176
  def get_multiline_input(tip: str) -> str:
153
177
  """
154
178
  获取带有增强补全和确认功能的多行输入。
@@ -160,25 +184,35 @@ def get_multiline_input(tip: str) -> str:
160
184
  str: 用户的输入,如果取消则返回空字符串
161
185
  """
162
186
  # 显示输入说明
163
- PrettyOutput.section("用户输入 - 使用 @ 触发文件补全,Tab 选择补全项,Ctrl+J 提交,按 Ctrl+C 取消输入", OutputType.USER)
187
+ PrettyOutput.section(
188
+ "用户输入 - 使用 @ 触发文件补全,Tab 选择补全项,Ctrl+J 提交,按 Ctrl+C 取消输入",
189
+ OutputType.USER,
190
+ )
164
191
  print(f"{Fore.GREEN}{tip}{ColoramaStyle.RESET_ALL}")
165
192
  # 配置键绑定
166
193
  bindings = KeyBindings()
167
- @bindings.add('enter')
194
+
195
+ @bindings.add("enter")
168
196
  def _(event):
169
197
  """处理回车键以进行补全或换行。"""
170
198
  if event.current_buffer.complete_state:
171
- event.current_buffer.apply_completion(event.current_buffer.complete_state.current_completion)
199
+ event.current_buffer.apply_completion(
200
+ event.current_buffer.complete_state.current_completion
201
+ )
172
202
  else:
173
- event.current_buffer.insert_text('\n')
174
- @bindings.add('c-j')
203
+ event.current_buffer.insert_text("\n")
204
+
205
+ @bindings.add("c-j")
175
206
  def _(event):
176
207
  """处理Ctrl+J以提交输入。"""
177
208
  event.current_buffer.validate_and_handle()
209
+
178
210
  # 配置提示会话
179
- style = PromptStyle.from_dict({
180
- 'prompt': 'ansicyan',
181
- })
211
+ style = PromptStyle.from_dict(
212
+ {
213
+ "prompt": "ansicyan",
214
+ }
215
+ )
182
216
  try:
183
217
  import os
184
218
 
@@ -190,17 +224,15 @@ def get_multiline_input(tip: str) -> str:
190
224
  history_dir = get_data_dir()
191
225
  # 初始化带历史记录的会话
192
226
  session = PromptSession(
193
- history=FileHistory(os.path.join(history_dir, 'multiline_input_history')),
227
+ history=FileHistory(os.path.join(history_dir, "multiline_input_history")),
194
228
  completer=FileCompleter(),
195
229
  key_bindings=bindings,
196
230
  complete_while_typing=True,
197
231
  multiline=True,
198
232
  vi_mode=False,
199
- mouse_support=False
233
+ mouse_support=False,
200
234
  )
201
- prompt = FormattedText([
202
- ('class:prompt', '>>> ')
203
- ])
235
+ prompt = FormattedText([("class:prompt", ">>> ")])
204
236
  # 获取输入
205
237
  text = session.prompt(
206
238
  prompt,
@@ -34,6 +34,7 @@ def _get_methodology_directory() -> str:
34
34
  PrettyOutput.print(f"创建方法论目录失败: {str(e)}", OutputType.ERROR)
35
35
  return methodology_dir
36
36
 
37
+
37
38
  def _load_all_methodologies() -> Dict[str, str]:
38
39
  """
39
40
  加载所有方法论文件
@@ -48,6 +49,7 @@ def _load_all_methodologies() -> Dict[str, str]:
48
49
  return all_methodologies
49
50
 
50
51
  import glob
52
+
51
53
  for filepath in glob.glob(os.path.join(methodology_dir, "*.json")):
52
54
  try:
53
55
  with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
@@ -58,10 +60,13 @@ def _load_all_methodologies() -> Dict[str, str]:
58
60
  all_methodologies[problem_type] = content
59
61
  except Exception as e:
60
62
  filename = os.path.basename(filepath)
61
- PrettyOutput.print(f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING)
63
+ PrettyOutput.print(
64
+ f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING
65
+ )
62
66
 
63
67
  return all_methodologies
64
68
 
69
+
65
70
  def _create_methodology_temp_file(methodologies: Dict[str, str]) -> Optional[str]:
66
71
  """
67
72
  创建包含所有方法论的临时文件
@@ -74,33 +79,34 @@ def _create_methodology_temp_file(methodologies: Dict[str, str]) -> Optional[str
74
79
  """
75
80
  if not methodologies:
76
81
  return None
77
-
82
+
78
83
  try:
79
84
  # 创建临时文件
80
- fd, temp_path = tempfile.mkstemp(suffix='.md', prefix='methodologies_')
85
+ fd, temp_path = tempfile.mkstemp(suffix=".md", prefix="methodologies_")
81
86
  os.close(fd)
82
-
87
+
83
88
  # 写入方法论内容
84
- with open(temp_path, 'w', encoding='utf-8') as f:
89
+ with open(temp_path, "w", encoding="utf-8") as f:
85
90
  f.write("# 方法论集合\n\n")
86
91
  for problem_type, content in methodologies.items():
87
92
  f.write(f"## {problem_type}\n\n")
88
93
  f.write(f"{content}\n\n")
89
94
  f.write("---\n\n")
90
95
  f.flush()
91
-
96
+
92
97
  return temp_path
93
98
  except Exception as e:
94
99
  PrettyOutput.print(f"创建方法论临时文件失败: {str(e)}", OutputType.ERROR)
95
100
  return None
96
101
 
102
+
97
103
  def upload_methodology(platform: BasePlatform) -> bool:
98
104
  """
99
105
  上传方法论文件到指定平台
100
-
106
+
101
107
  参数:
102
108
  platform: 平台实例,需实现upload_files方法
103
-
109
+
104
110
  返回:
105
111
  bool: 上传是否成功
106
112
  """
@@ -108,19 +114,19 @@ def upload_methodology(platform: BasePlatform) -> bool:
108
114
  if not os.path.exists(methodology_dir):
109
115
  PrettyOutput.print("方法论文档不存在", OutputType.WARNING)
110
116
  return False
111
-
117
+
112
118
  methodologies = _load_all_methodologies()
113
119
  if not methodologies:
114
120
  PrettyOutput.print("没有可用的方法论文档", OutputType.WARNING)
115
121
  return False
116
-
122
+
117
123
  temp_file_path = _create_methodology_temp_file(methodologies)
118
124
  if not temp_file_path:
119
125
  return False
120
-
126
+
121
127
  try:
122
128
  return platform.upload_files([temp_file_path])
123
-
129
+
124
130
  finally:
125
131
  if temp_file_path and os.path.exists(temp_file_path):
126
132
  try:
@@ -141,8 +147,6 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
141
147
  """
142
148
  from yaspin import yaspin # type: ignore
143
149
 
144
- from jarvis.jarvis_tools.registry import ToolRegistry
145
-
146
150
  prompt = tool_registery.prompt() if tool_registery else ""
147
151
 
148
152
  # 获取方法论目录
@@ -178,7 +182,7 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
178
182
 
179
183
  full_content += f"以下是所有可用的工具内容:\n\n"
180
184
  full_content += prompt
181
-
185
+
182
186
  # 添加用户输入和输出要求
183
187
  full_content += f"""
184
188
  请根据以上方法论和可调用的工具内容,规划/总结出以下用户需求的执行步骤: {user_input}
@@ -199,7 +203,7 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
199
203
  # 检查内容是否过大
200
204
  is_large_content = is_context_overflow(full_content)
201
205
  temp_file_path = None
202
-
206
+
203
207
  try:
204
208
  if is_large_content:
205
209
  # 创建临时文件
@@ -214,10 +218,12 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
214
218
 
215
219
  # 尝试上传文件
216
220
  upload_success = platform.upload_files([temp_file_path])
217
-
221
+
218
222
  if upload_success:
219
223
  # 使用上传的文件生成摘要
220
- return platform.chat_until_success(base_prompt + f"""
224
+ return platform.chat_until_success(
225
+ base_prompt
226
+ + f"""
221
227
  请根据已上传的方法论和可调用的工具文件内容,规划/总结出以下用户需求的执行步骤: {user_input}
222
228
 
223
229
  请按以下格式回复:
@@ -231,12 +237,13 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
231
237
 
232
238
  如果没有匹配的方法论,请输出:没有历史方法论可参考
233
239
  除以上要求外,不要输出任何内容
234
- """)
240
+ """
241
+ )
235
242
  else:
236
243
  return "没有历史方法论可参考"
237
244
  # 如果内容不大或上传失败,直接使用chat_until_success
238
245
  return platform.chat_until_success(full_content)
239
-
246
+
240
247
  finally:
241
248
  # 清理临时文件
242
249
  if temp_file_path and os.path.exists(temp_file_path):
@@ -244,7 +251,7 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
244
251
  os.remove(temp_file_path)
245
252
  except Exception:
246
253
  pass
247
-
254
+
248
255
  except Exception as e:
249
256
  PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
250
257
  return ""