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

jarvis/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.160"
3
+ __version__ = "0.1.162"
@@ -5,7 +5,6 @@ from typing import Any, Callable, List, Optional, Tuple, Union
5
5
  from yaspin import yaspin # type: ignore
6
6
 
7
7
  from jarvis.jarvis_agent.output_handler import OutputHandler
8
- from jarvis.jarvis_agent.patch import PatchOutputHandler
9
8
  from jarvis.jarvis_platform.base import BasePlatform
10
9
  from jarvis.jarvis_platform.registry import PlatformRegistry
11
10
  from jarvis.jarvis_utils.output import PrettyOutput, OutputType
@@ -180,7 +179,7 @@ class Agent:
180
179
  self.model.set_suppress_output(False)
181
180
 
182
181
  from jarvis.jarvis_tools.registry import ToolRegistry
183
- self.output_handler = output_handler if output_handler else [ToolRegistry(), PatchOutputHandler()]
182
+ self.output_handler = output_handler if output_handler else [ToolRegistry()]
184
183
  self.multiline_inputer = multiline_inputer if multiline_inputer else get_multiline_input
185
184
 
186
185
  self.prompt = ""
@@ -15,7 +15,6 @@ from jarvis.jarvis_agent import (
15
15
  Agent,
16
16
  origin_agent_system_prompt
17
17
  )
18
- from jarvis.jarvis_agent.patch import PatchOutputHandler
19
18
  from jarvis.jarvis_tools.registry import ToolRegistry
20
19
  from jarvis.jarvis_utils.utils import init_env
21
20
  from jarvis.jarvis_agent.file_input_handler import file_input_handler
@@ -94,7 +93,7 @@ def _select_task(tasks: Dict[str, str]) -> str:
94
93
  # 询问是否需要补充信息
95
94
  need_additional = user_confirm("需要为此任务添加补充信息吗?", default=False)
96
95
  if need_additional:
97
- additional_input = get_multiline_input("请输入补充信息(输入空行结束):")
96
+ additional_input = get_multiline_input("请输入补充信息:")
98
97
  if additional_input:
99
98
  selected_task = f"{selected_task}\n\n补充信息:\n{additional_input}"
100
99
  return selected_task
@@ -123,7 +122,7 @@ def main() -> None:
123
122
  platform=args.platform,
124
123
  model_name=args.model,
125
124
  input_handler=[file_input_handler, shell_input_handler, builtin_input_handler],
126
- output_handler=[ToolRegistry(), PatchOutputHandler()],
125
+ output_handler=[ToolRegistry()],
127
126
  need_summary=False
128
127
  )
129
128
 
@@ -7,7 +7,7 @@ import os
7
7
  import sys
8
8
  import subprocess
9
9
  import argparse
10
- from typing import Optional, List, Tuple
10
+ from typing import Any, Dict, Optional, List, Tuple
11
11
 
12
12
  # 忽略yaspin的类型检查
13
13
  from yaspin import yaspin # type: ignore
@@ -16,7 +16,6 @@ from jarvis.jarvis_agent import Agent
16
16
  from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
17
17
  from jarvis.jarvis_agent.file_input_handler import file_input_handler
18
18
  from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
19
- from jarvis.jarvis_agent.patch import PatchOutputHandler
20
19
  from jarvis.jarvis_platform.registry import PlatformRegistry
21
20
  from jarvis.jarvis_git_utils.git_commiter import GitCommitTool
22
21
  from jarvis.jarvis_tools.registry import ToolRegistry
@@ -51,9 +50,8 @@ class CodeAgent:
51
50
  "read_code",
52
51
  "methodology",
53
52
  "chdir",
54
- "create_code_agent",
55
53
  "find_methodology",
56
- "virtual_tty",
54
+ "edit_file",
57
55
  ])
58
56
  code_system_prompt = """
59
57
  <code_engineer_guide>
@@ -204,7 +202,13 @@ class CodeAgent:
204
202
  - loc比wc -l提供更多代码统计信息,应优先使用
205
203
  - 针对不同编程语言选择对应的代码质量检查工具
206
204
  - 不要留下未实现的代码
207
- - 对于非常复杂的需求,可以使用create_code_agent工具,但是要提供完整的上下文信息
205
+
206
+ ### 代码编辑规范
207
+ - 使用edit_file工具进行代码修改时,必须遵循最小补丁原则
208
+ - 只提供需要修改的代码部分,不要提供完整文件内容
209
+ - 保持原始代码的缩进、空行和格式风格
210
+ - 每个修改必须包含清晰的修改理由
211
+ - 新建文件时可以提供完整内容,修改现有文件时只提供差异部分
208
212
  </notes>
209
213
  </code_engineer_guide>
210
214
  """
@@ -220,7 +224,7 @@ class CodeAgent:
220
224
  system_prompt=code_system_prompt,
221
225
  name="CodeAgent",
222
226
  auto_complete=False,
223
- output_handler=[tool_registry, PatchOutputHandler()],
227
+ output_handler=[tool_registry],
224
228
  platform=platform_instance,
225
229
  input_handler=[
226
230
  shell_input_handler,
@@ -243,6 +247,83 @@ class CodeAgent:
243
247
  """
244
248
  return self.root_dir
245
249
 
250
+ def get_loc_stats(self) -> str:
251
+ """使用loc命令获取当前目录的代码统计信息
252
+
253
+ 返回:
254
+ str: loc命令输出的原始字符串,失败时返回空字符串
255
+ """
256
+ try:
257
+ result = subprocess.run(
258
+ ['loc'],
259
+ cwd=self.root_dir,
260
+ capture_output=True,
261
+ text=True
262
+ )
263
+ return result.stdout if result.returncode == 0 else ""
264
+ except FileNotFoundError:
265
+ return ""
266
+
267
+ def get_recent_commits_with_files(self) -> List[Dict[str, Any]]:
268
+ """获取最近5次提交的commit信息和文件清单
269
+
270
+ 返回:
271
+ List[Dict[str, Any]]: 包含commit信息和文件清单的字典列表,格式为:
272
+ [
273
+ {
274
+ 'hash': 提交hash,
275
+ 'message': 提交信息,
276
+ 'author': 作者,
277
+ 'date': 提交日期,
278
+ 'files': [修改的文件列表] (最多50个文件)
279
+ },
280
+ ...
281
+ ]
282
+ 失败时返回空列表
283
+ """
284
+ try:
285
+ # 获取最近5次提交的基本信息
286
+ result = subprocess.run(
287
+ ['git', 'log', '-5', '--pretty=format:%H%n%s%n%an%n%ad'],
288
+ cwd=self.root_dir,
289
+ capture_output=True,
290
+ text=True
291
+ )
292
+ if result.returncode != 0:
293
+ return []
294
+
295
+ # 解析提交信息
296
+ commits = []
297
+ lines = result.stdout.splitlines()
298
+ for i in range(0, len(lines), 4):
299
+ if i + 3 >= len(lines):
300
+ break
301
+ commit = {
302
+ 'hash': lines[i],
303
+ 'message': lines[i+1],
304
+ 'author': lines[i+2],
305
+ 'date': lines[i+3],
306
+ 'files': []
307
+ }
308
+ commits.append(commit)
309
+
310
+ # 获取每个提交的文件修改清单
311
+ for commit in commits:
312
+ files_result = subprocess.run(
313
+ ['git', 'show', '--name-only', '--pretty=format:', commit['hash']],
314
+ cwd=self.root_dir,
315
+ capture_output=True,
316
+ text=True
317
+ )
318
+ if files_result.returncode == 0:
319
+ files = list(set(filter(None, files_result.stdout.splitlines())))
320
+ commit['files'] = files[:50] # 限制最多50个文件
321
+
322
+ return commits
323
+
324
+ except subprocess.CalledProcessError:
325
+ return []
326
+
246
327
  def _init_env(self) -> None:
247
328
  """初始化环境"""
248
329
  with yaspin(text="正在初始化环境...", color="cyan") as spinner:
@@ -261,8 +342,29 @@ class CodeAgent:
261
342
  if has_uncommitted_changes():
262
343
  PrettyOutput.print("检测到未提交的修改,是否要提交?", OutputType.WARNING)
263
344
  if user_confirm("是否要提交?", True):
264
- git_commiter = GitCommitTool()
265
- git_commiter.execute({})
345
+ import subprocess
346
+ try:
347
+ # 获取当前分支的提交总数
348
+ commit_count = subprocess.run(
349
+ ['git', 'rev-list', '--count', 'HEAD'],
350
+ capture_output=True,
351
+ text=True
352
+ )
353
+ if commit_count.returncode != 0:
354
+ return
355
+
356
+ commit_count = int(commit_count.stdout.strip())
357
+
358
+ # 暂存所有修改
359
+ subprocess.run(['git', 'add', '.'], check=True)
360
+
361
+ # 提交变更
362
+ subprocess.run(
363
+ ['git', 'commit', '-m', f'CheckPoint #{commit_count + 1}'],
364
+ check=True
365
+ )
366
+ except subprocess.CalledProcessError as e:
367
+ PrettyOutput.print(f"提交失败: {str(e)}", OutputType.ERROR)
266
368
 
267
369
  def _show_commit_history(
268
370
  self,
@@ -301,17 +403,14 @@ class CodeAgent:
301
403
  ) -> None:
302
404
  """处理提交确认和可能的重置"""
303
405
  if commits and user_confirm("是否接受以上提交记录?", True):
304
- if len(commits) > 1 and user_confirm(
305
- "是否要合并为一个更清晰的提交记录?", True
306
- ):
307
- subprocess.run(
308
- ["git", "reset", "--mixed", str(start_commit)],
309
- stdout=subprocess.DEVNULL,
310
- stderr=subprocess.DEVNULL,
311
- check=True
312
- )
313
- git_commiter = GitCommitTool()
314
- git_commiter.execute({})
406
+ subprocess.run(
407
+ ["git", "reset", "--mixed", str(start_commit)],
408
+ stdout=subprocess.DEVNULL,
409
+ stderr=subprocess.DEVNULL,
410
+ check=True
411
+ )
412
+ git_commiter = GitCommitTool()
413
+ git_commiter.execute({})
315
414
  elif start_commit:
316
415
  os.system(f"git reset --hard {str(start_commit)}") # 确保转换为字符串
317
416
  PrettyOutput.print("已重置到初始提交", OutputType.INFO)
@@ -329,8 +428,24 @@ class CodeAgent:
329
428
  self._init_env()
330
429
  start_commit = get_latest_commit_hash()
331
430
 
431
+ # 获取项目统计信息并附加到用户输入
432
+ loc_stats = self.get_loc_stats()
433
+ commits_info = self.get_recent_commits_with_files()
434
+
435
+ project_info = []
436
+ if loc_stats:
437
+ project_info.append(f"代码统计:\n{loc_stats}")
438
+ if commits_info:
439
+ commits_str = "\n".join(
440
+ f"提交 {i+1}: {commit['hash'][:7]} - {commit['message']} ({len(commit['files'])}个文件)"
441
+ for i, commit in enumerate(commits_info)
442
+ )
443
+ project_info.append(f"最近提交:\n{commits_str}")
444
+
445
+ enhanced_input = f"{user_input}\n\n项目概况:\n" + "\n\n".join(project_info) if project_info else user_input
446
+
332
447
  try:
333
- self.agent.run(user_input)
448
+ self.agent.run(enhanced_input)
334
449
  except RuntimeError as e:
335
450
  PrettyOutput.print(f"执行失败: {str(e)}", OutputType.WARNING)
336
451
  return str(e)
jarvis/jarvis_dev/main.py CHANGED
@@ -3,7 +3,8 @@ from jarvis.jarvis_multi_agent import MultiAgent
3
3
  from jarvis.jarvis_tools.registry import ToolRegistry
4
4
  from jarvis.jarvis_utils.input import get_multiline_input
5
5
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
- from jarvis.jarvis_utils.utils import ct, ot, init_env
6
+ from jarvis.jarvis_utils.tag import ct, ot
7
+ from jarvis.jarvis_utils.utils import init_env
7
8
 
8
9
  # 定义每个角色的系统提示
9
10
  PM_PROMPT = f"""
@@ -994,7 +995,8 @@ def create_dev_team() -> MultiAgent:
994
995
  "search_web",
995
996
  "execute_script",
996
997
  "methodology",
997
- "ask_codebase"
998
+ "ask_codebase",
999
+ "edit_file",
998
1000
  ])
999
1001
 
1000
1002
  BA_output_handler = ToolRegistry()
@@ -1005,7 +1007,8 @@ def create_dev_team() -> MultiAgent:
1005
1007
  "execute_script",
1006
1008
  "read_webpage",
1007
1009
  "methodology",
1008
- "ask_codebase"
1010
+ "ask_codebase",
1011
+ "edit_file",
1009
1012
  ])
1010
1013
 
1011
1014
  SA_output_handler = ToolRegistry()
@@ -1015,7 +1018,8 @@ def create_dev_team() -> MultiAgent:
1015
1018
  "ask_codebase",
1016
1019
  "execute_script",
1017
1020
  "read_code",
1018
- "methodology"
1021
+ "methodology",
1022
+ "edit_file",
1019
1023
  ])
1020
1024
 
1021
1025
  TL_output_handler = ToolRegistry()
@@ -1025,6 +1029,7 @@ def create_dev_team() -> MultiAgent:
1025
1029
  "lsp_get_diagnostics",
1026
1030
  "execute_script",
1027
1031
  "methodology",
1032
+ "edit_file",
1028
1033
  ])
1029
1034
 
1030
1035
  DEV_output_handler = ToolRegistry()
@@ -1036,6 +1041,7 @@ def create_dev_team() -> MultiAgent:
1036
1041
  "read_code",
1037
1042
  "create_sub_agent",
1038
1043
  "methodology",
1044
+ "edit_file",
1039
1045
  ])
1040
1046
 
1041
1047
  QA_output_handler = ToolRegistry()
@@ -1048,6 +1054,7 @@ def create_dev_team() -> MultiAgent:
1048
1054
  "execute_script",
1049
1055
  "read_code",
1050
1056
  "methodology",
1057
+ "edit_file",
1051
1058
  ])
1052
1059
 
1053
1060
  # Update PM prompt with tool usage guidance
@@ -153,9 +153,10 @@ class GitCommitTool:
153
153
  except Exception as e:
154
154
  spinner.write(f"⚠️ 上传文件时出错: {str(e)}")
155
155
  upload_success = False
156
-
157
156
  # 根据上传状态准备完整的提示
158
157
  if upload_success:
158
+ # 尝试生成提交信息
159
+ spinner.text = "正在生成提交消息..."
159
160
  # 使用上传的文件
160
161
  prompt = base_prompt + f'''
161
162
  # 变更概述
@@ -164,6 +165,7 @@ class GitCommitTool:
164
165
 
165
166
  请详细分析已上传的代码差异文件,生成符合上述格式的提交信息。
166
167
  '''
168
+ commit_message = platform.chat_until_success(prompt)
167
169
  else:
168
170
  # 如果上传失败但内容较大,使用chat_big_content
169
171
  if is_large_content and hasattr(platform, 'chat_big_content'):
@@ -177,14 +179,14 @@ class GitCommitTool:
177
179
  '''
178
180
  commit_message = platform.chat_until_success(prompt)
179
181
 
180
- # 尝试生成提交信息
181
- spinner.text = "正在生成提交消息..."
182
182
  while True:
183
- if not upload_success and not is_large_content:
183
+ # 只在特定情况下重新获取commit_message
184
+ if not upload_success and not is_large_content and not commit_message:
184
185
  commit_message = platform.chat_until_success(prompt)
185
- commit_message = self._extract_commit_message(commit_message)
186
+ extracted_message = self._extract_commit_message(commit_message)
186
187
  # 如果成功提取,就跳出循环
187
- if commit_message:
188
+ if extracted_message:
189
+ commit_message = extracted_message
188
190
  break
189
191
  prompt = f"""格式错误,请按照以下格式重新生成提交信息:
190
192
  {ot("COMMIT_MESSAGE")}
@@ -193,6 +195,7 @@ class GitCommitTool:
193
195
  [可选] 详细描述变更内容和原因
194
196
  {ct("COMMIT_MESSAGE")}
195
197
  """
198
+ commit_message = platform.chat_until_success(prompt)
196
199
  spinner.write("✅ 生成提交消息")
197
200
 
198
201
  # 执行提交
@@ -1,7 +1,10 @@
1
+ from pydoc import describe
1
2
  import re
2
3
  from typing import Dict, Any, Tuple
3
4
  import os
4
5
 
6
+ from networkx import descendants
7
+
5
8
  from yaspin import yaspin # type: ignore
6
9
 
7
10
  from jarvis.jarvis_agent.output_handler import OutputHandler
@@ -18,35 +21,15 @@ from jarvis.jarvis_utils.utils import is_context_overflow, get_file_line_count,
18
21
  from jarvis.jarvis_utils.tag import ot, ct
19
22
 
20
23
 
21
- class PatchOutputHandler(OutputHandler):
22
- def name(self) -> str:
23
- return "PATCH"
24
-
25
- def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
26
- return False, apply_patch(response, agent)
27
-
28
- def can_handle(self, response: str) -> bool:
29
- if _has_patch_block(response):
30
- return True
31
- return False
24
+ class PatchOutputHandler:
32
25
 
33
- def prompt(self) -> str:
34
- return f"""
35
- # 代码补丁规范
26
+ name = "edit_file"
27
+ description = """代码编辑工具,用于编辑文件
28
+ # 代码编辑规范
36
29
 
37
30
  ## 重要提示
38
31
  我可以看到完整的代码,所以不需要生成完整的代码,只需要提供修改的代码片段即可。请尽量精简补丁内容,只包含必要的上下文和修改部分。特别注意:不要提供完整文件内容,只提供需要修改的部分!
39
32
 
40
- ## 补丁格式定义
41
- 使用{ot("PATCH")}块来精确指定代码更改:
42
- ```
43
- {ot("PATCH")}
44
- File: [文件路径]
45
- Reason: [修改原因]
46
- [代码修改说明,不用输出完整的代码,仅输出修改的片段即可]
47
- {ct("PATCH")}
48
- ```
49
-
50
33
  ## 核心原则
51
34
  1. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
52
35
  2. **最小补丁原则**:始终生成最小范围的补丁,只包含必要的上下文和实际修改
@@ -71,22 +54,6 @@ Reason: [修改原因]
71
54
  - 如果原代码行尾没有空格,补丁也不应添加行尾空格
72
55
  - 如果原代码使用特定的行尾注释风格,补丁也应保持该风格
73
56
 
74
- ## 补丁示例
75
- ```
76
- {ot("PATCH")}
77
- File: src/utils/math.py
78
- Reason: 修复除零错误,增加参数验证以提高函数健壮性
79
- def safe_divide(a, b):
80
- # 添加参数验证
81
- if b == 0:
82
- raise ValueError("除数不能为零")
83
- return a / b
84
- # 现有代码 ...
85
- def add(a, b):
86
- return a + b
87
- {ct("PATCH")}
88
- ```
89
-
90
57
  ## 最佳实践
91
58
  - 每个补丁专注于单一职责的修改
92
59
  - 避免包含过多无关代码
@@ -96,77 +63,47 @@ def add(a, b):
96
63
  - 绝不提供完整文件内容,除非是新建文件
97
64
  - 每个文件的修改是独立的,不能出现“参照xxx文件的修改”这样的描述
98
65
  - 不要出现未实现的代码,如:TODO
99
- """
100
-
101
- def _has_patch_block(patch_str: str) -> bool:
102
- """判断是否存在补丁块"""
103
- return re.search(ot("PATCH")+r'\n?(.*?)\n?' +
104
- ct("PATCH"), patch_str, re.DOTALL) is not None
105
-
106
-
107
- def _parse_patch(patch_str: str) -> Tuple[Dict[str, str], str]:
108
- """解析新的上下文补丁格式"""
109
- result = {}
110
- patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?' +
111
- ct("PATCH"), patch_str, re.DOTALL)
112
- if patches:
113
- for patch in patches:
114
- first_line = patch.splitlines()[0]
115
- sm = re.match(r'^File:\s*(.+)$', first_line)
116
- if not sm:
117
- return ({}, f"""无效的补丁格式,正确格式应该为:
118
- {ot("PATCH")}
119
- File: [文件路径]
120
- Reason: [修改原因]
121
- [代码修改说明,不用输出完整的代码,仅输出修改的片段即可]
122
- {ct("PATCH")}""")
123
- filepath = os.path.abspath(sm.group(1).strip())
124
- if filepath not in result:
125
- result[filepath] = patch
126
- else:
127
- result[filepath] += "\n\n" + patch
128
- return result, ""
66
+ - 如果要修改的内容较多,请分多次修改
67
+ """
68
+ parameters = {
69
+ "type": "object",
70
+ "properties": {
71
+ "file": {"type": "string", "description": "文件路径"},
72
+ "reason": {"type": "string", "description": "修改原因"},
73
+ "content": {"type": "string", "description": "补丁内容"}
74
+ }
75
+ }
129
76
 
130
77
 
131
- def apply_patch(output_str: str, agent: Any) -> str:
132
- """Apply patches to files"""
133
- with yaspin(text="正在应用补丁...", color="cyan") as spinner:
134
- try:
135
- patches, error_msg = _parse_patch(output_str)
136
- if error_msg:
137
- spinner.text = "补丁格式错误"
78
+
79
+ def execute(self, args: Dict) -> Dict[str, Any]:
80
+ """Apply patches to files"""
81
+
82
+ from jarvis.jarvis_agent import Agent
83
+ filepath = args["file"]
84
+ patch_content = args["content"]
85
+ agent: Agent = args["agent"]
86
+
87
+ with yaspin(text="正在应用补丁...", color="cyan") as spinner:
88
+
89
+ # 获取当前提交hash作为起始点
90
+ spinner.text = "开始获取当前提交hash..."
91
+ start_hash = get_latest_commit_hash()
92
+ spinner.write("✅ 当前提交hash获取完成")
93
+
94
+ not_read_file = not has_read_file(filepath) and os.path.exists(filepath) and os.path.getsize(filepath) > 0
95
+ if not_read_file:
96
+ spinner.text=f"以下文件未读取: {filepath},应用补丁存在风险,将先读取文件后再生成补丁"
138
97
  spinner.fail("❌")
139
- return error_msg
140
- except Exception as e:
141
- spinner.text = "解析补丁失败"
142
- spinner.fail("")
143
- return f"解析补丁失败: {str(e)}"
144
-
145
- # 获取当前提交hash作为起始点
146
- spinner.text = "开始获取当前提交hash..."
147
- start_hash = get_latest_commit_hash()
148
- spinner.write("✅ 当前提交hash获取完成")
149
-
150
- not_read_file = [
151
- f for f in patches.keys()
152
- if not has_read_file(f)
153
- and os.path.exists(f)
154
- and os.path.getsize(f) > 0
155
- ]
156
- if not_read_file:
157
- spinner.text=f"以下文件未读取: {not_read_file},应用补丁存在风险,将先读取文件后再生成补丁"
158
- spinner.fail("❌")
159
- return f"以下文件未读取: {not_read_file},应用补丁存在风险,请先读取文件后再生成补丁"
98
+ return {
99
+ "success": False,
100
+ "stdout": f"以下文件未读取: {filepath},应用补丁存在风险,请先读取文件后再生成补丁",
101
+ "stderr": ""
102
+ }
160
103
 
161
- # 检查是否有文件在Git仓库外
162
- in_git_repo = True
163
- for filepath in patches.keys():
164
- if not _is_file_in_git_repo(filepath):
165
- in_git_repo = False
166
- break
104
+ # 检查是否有文件在Git仓库外
105
+ in_git_repo = _is_file_in_git_repo(filepath)
167
106
 
168
- # 按文件逐个处理
169
- for filepath, patch_content in patches.items():
170
107
  try:
171
108
  spinner.text = f"正在处理文件: {filepath}"
172
109
  if not os.path.exists(filepath):
@@ -188,63 +125,74 @@ def apply_patch(output_str: str, agent: Any) -> str:
188
125
  revert_file(filepath) # 回滚单个文件
189
126
  spinner.write(f"✅ 文件 {filepath} 回滚完成")
190
127
 
191
- final_ret = ""
192
- if in_git_repo:
193
- diff = get_diff()
194
- if diff:
195
- PrettyOutput.print(diff, OutputType.CODE, lang="diff")
196
- with spinner.hidden():
197
- commited = handle_commit_workflow()
198
- if commited:
199
- # 获取提交信息
200
- end_hash = get_latest_commit_hash()
201
- commits = get_commits_between(start_hash, end_hash)
202
-
203
- # 添加提交信息到final_ret
204
- if commits:
205
- final_ret += "✅ 补丁已应用\n"
206
- final_ret += "# 提交信息:\n"
207
- for commit_hash, commit_message in commits:
208
- final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
209
-
210
- final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
211
-
212
- # 修改后的提示逻辑
213
- addon_prompt = "1. 请确认补丁是否已正确应用\n"
214
- addon_prompt += "2. 检查修改后的代码是否符合预期\n"
215
- addon_prompt += "3. 如果确认无误,请继续进行下一步修改\n"
216
- addon_prompt += "4. 如果发现问题,请立即开始修复\n"
217
- addon_prompt += f"如果用户的需求已经完成,请终止,不要输出新的 {ot('PATCH')},不要实现任何超出用户需求外的内容\n"
218
- addon_prompt += "如果有任何信息不清楚,调用工具获取信息\n"
219
- addon_prompt += "每次响应必须且只能包含一个操作\n"
220
-
221
- agent.set_addon_prompt(addon_prompt)
128
+ final_ret = ""
129
+ if in_git_repo:
130
+ diff = get_diff()
131
+ if diff:
132
+ PrettyOutput.print(diff, OutputType.CODE, lang="diff")
133
+ with spinner.hidden():
134
+ commited = handle_commit_workflow()
135
+ if commited:
136
+ # 获取提交信息
137
+ end_hash = get_latest_commit_hash()
138
+ commits = get_commits_between(start_hash, end_hash)
139
+
140
+ # 添加提交信息到final_ret
141
+ if commits:
142
+ final_ret += "✅ 补丁已应用\n"
143
+ final_ret += "# 提交信息:\n"
144
+ for commit_hash, commit_message in commits:
145
+ final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
146
+
147
+ final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
148
+
149
+ # 修改后的提示逻辑
150
+ addon_prompt = f"如果用户的需求未完成,请继续生成补丁,如果已经完成,请终止,不要输出新的PATCH,不要实现任何超出用户需求外的内容\n"
151
+ addon_prompt += "如果有任何信息不明确,调用工具获取信息\n"
152
+ addon_prompt += "每次响应必须且只能包含一个操作\n"
153
+
154
+ agent.set_addon_prompt(addon_prompt)
222
155
 
156
+ else:
157
+ final_ret += "✅ 补丁已应用(没有新的提交)"
223
158
  else:
224
- final_ret += " 补丁已应用(没有新的提交)"
159
+ final_ret += " 补丁应用被拒绝\n"
160
+ final_ret += f"# 补丁预览:\n```diff\n{diff}\n```"
225
161
  else:
226
- final_ret += "❌ 补丁应用被拒绝\n"
227
- final_ret += f"# 补丁预览:\n```diff\n{diff}\n```"
162
+ commited = False
163
+ final_ret += " 没有要提交的更改\n"
228
164
  else:
229
- commited = False
230
- final_ret += " 没有要提交的更改\n"
231
- else:
232
- # 对于Git仓库外的文件,直接返回成功
233
- final_ret += "✅ 补丁已应用(文件不在Git仓库中)"
234
- commited = True
235
- # 用户确认最终结果
236
- with spinner.hidden():
237
- if commited:
238
- return final_ret
239
- PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
240
- if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
241
- return final_ret
242
- custom_reply = get_multiline_input("请输入自定义回复")
243
- if not custom_reply.strip(): # 如果自定义回复为空,返回空字符串
244
- return ""
245
- agent.set_addon_prompt(custom_reply)
246
- return final_ret
247
-
165
+ # 对于Git仓库外的文件,直接返回成功
166
+ final_ret += " 补丁已应用(文件不在Git仓库中)"
167
+ commited = True
168
+ # 用户确认最终结果
169
+ with spinner.hidden():
170
+ if commited:
171
+ return {
172
+ "success": True,
173
+ "stdout": final_ret,
174
+ "stderr": ""
175
+ }
176
+ PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
177
+ if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
178
+ return {
179
+ "success": True,
180
+ "stdout": final_ret,
181
+ "stderr": ""
182
+ }
183
+ custom_reply = get_multiline_input("请输入自定义回复")
184
+ if not custom_reply.strip(): # 如果自定义回复为空,返回空字符串
185
+ return {
186
+ "success": True,
187
+ "stdout": final_ret,
188
+ "stderr": ""
189
+ }
190
+ agent.set_addon_prompt(custom_reply)
191
+ return {
192
+ "success": True,
193
+ "stdout": final_ret,
194
+ "stderr": ""
195
+ }
248
196
 
249
197
  def _is_file_in_git_repo(filepath: str) -> bool:
250
198
  """检查文件是否在当前Git仓库中"""
@@ -347,14 +295,36 @@ def handle_commit_workflow() -> bool:
347
295
  """Handle the git commit workflow and return the commit details.
348
296
 
349
297
  Returns:
350
- tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
298
+ bool: 提交是否成功
351
299
  """
352
300
  if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
353
301
  revert_change()
354
302
  return False
355
- git_commiter = GitCommitTool()
356
- commit_result = git_commiter.execute({})
357
- return commit_result["success"]
303
+
304
+ import subprocess
305
+ try:
306
+ # 获取当前分支的提交总数
307
+ commit_count = subprocess.run(
308
+ ['git', 'rev-list', '--count', 'HEAD'],
309
+ capture_output=True,
310
+ text=True
311
+ )
312
+ if commit_count.returncode != 0:
313
+ return False
314
+
315
+ commit_count = int(commit_count.stdout.strip())
316
+
317
+ # 暂存所有修改
318
+ subprocess.run(['git', 'add', '.'], check=True)
319
+
320
+ # 提交变更
321
+ subprocess.run(
322
+ ['git', 'commit', '-m', f'CheckPoint #{commit_count + 1}'],
323
+ check=True
324
+ )
325
+ return True
326
+ except subprocess.CalledProcessError as e:
327
+ return False
358
328
 
359
329
 
360
330
  def handle_code_operation(filepath: str, patch_content: str) -> bool:
@@ -377,7 +347,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
377
347
  model = PlatformRegistry().get_normal_platform()
378
348
  file_content = FileOperationTool().execute({"operation":"read", "files":[{"path":filepath}]})["stdout"]
379
349
 
380
- model.set_suppress_output(True)
350
+ model.set_suppress_output(False)
381
351
 
382
352
  prompt = f"""
383
353
  # 代码合并专家指南
@@ -419,7 +389,8 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
419
389
  finished = False
420
390
  while count > 0:
421
391
  count -= 1
422
- response = model.chat_until_success(prompt).splitlines()
392
+ with spinner.hidden():
393
+ response = model.chat_until_success(prompt).splitlines()
423
394
  try:
424
395
  start_line = response.index(ot("MERGED_CODE")) + 1
425
396
  try:
@@ -481,7 +452,7 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
481
452
  upload_success = True
482
453
 
483
454
 
484
- model.set_suppress_output(True)
455
+ model.set_suppress_output(False)
485
456
 
486
457
  main_prompt = f"""
487
458
  # 代码补丁生成专家指南
@@ -513,19 +484,19 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
513
484
 
514
485
  ## 输出模板
515
486
  {ot("DIFF")}
516
- >>>>>> SEARCH
487
+ {">" * 5} SEARCH
517
488
  [需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]
518
489
  {'='*5}
519
490
  [替换后的新代码]
520
- <<<<<< REPLACE
491
+ {"<" * 5} REPLACE
521
492
  {ct("DIFF")}
522
493
 
523
494
  {ot("DIFF")}
524
- >>>>>> SEARCH
495
+ {">" * 5} SEARCH
525
496
  [另一处需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]
526
497
  {'='*5}
527
498
  [另一处替换后的新代码]
528
- <<<<<< REPLACE
499
+ {"<" * 5} REPLACE
529
500
  {ct("DIFF")}
530
501
  """
531
502
 
@@ -536,12 +507,15 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
536
507
  # 原始代码
537
508
  {file_content}
538
509
  """
539
- response = model.chat_until_success(main_prompt + file_prompt)
510
+ with spinner.hidden():
511
+ response = model.chat_until_success(main_prompt + file_prompt)
540
512
  else:
541
513
  if upload_success:
542
- response = model.chat_until_success(main_prompt)
514
+ with spinner.hidden():
515
+ response = model.chat_until_success(main_prompt)
543
516
  else:
544
- response = model.chat_big_content(file_content, main_prompt)
517
+ with spinner.hidden():
518
+ response = model.chat_big_content(file_content, main_prompt)
545
519
 
546
520
  # 解析差异化补丁
547
521
  diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
@@ -563,7 +537,6 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
563
537
  if search_text in modified_content:
564
538
  # 如果有多处,报错
565
539
  if modified_content.count(search_text) > 1:
566
- prompt = f"补丁 #{patch_count} 应用失败:找到多个匹配的代码段"
567
540
  spinner.write(f"❌ 补丁 #{patch_count} 应用失败:找到多个匹配的代码段")
568
541
  success = False
569
542
  break
@@ -573,7 +546,6 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
573
546
  spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
574
547
  else:
575
548
  spinner.write(f"❌ 补丁 #{patch_count} 应用失败:无法找到匹配的代码段")
576
- prompt = f"补丁 #{patch_count} 应用失败:无法找到匹配的代码段"
577
549
  success = False
578
550
  break
579
551
  if not success:
@@ -82,29 +82,20 @@ class FileCompleter(Completer):
82
82
  # 计算替换长度
83
83
  replace_length = len(text_after_at) + 1
84
84
 
85
- # 从replace_map获取建议列表
86
- default_suggestions = [
87
- (ot(tag), desc) for tag, desc in [
88
- (tag, self._get_description(tag))
89
- for tag in self.replace_map.keys()
90
- ]
91
- ]
92
- # 添加特殊标记
93
- default_suggestions.extend([
85
+ # 获取所有可能的补全项
86
+ all_completions = []
87
+
88
+ # 1. 添加特殊标记
89
+ all_completions.extend([
90
+ (ot(tag), self._get_description(tag))
91
+ for tag in self.replace_map.keys()
92
+ ])
93
+ all_completions.extend([
94
94
  (ot("Summary"), '总结'),
95
95
  (ot("Clear"), '清除历史'),
96
96
  ])
97
- for name, desc in default_suggestions:
98
- yield Completion(
99
- text=f"'{name}'",
100
- start_position=-replace_length,
101
- display=name,
102
- display_meta=desc
103
- ) # type: ignore
104
-
105
-
106
- # 使用git ls-files获取所有可能的文件
107
- all_files = []
97
+
98
+ # 2. 添加文件列表
108
99
  try:
109
100
  import subprocess
110
101
  result = subprocess.run(['git', 'ls-files'],
@@ -112,28 +103,40 @@ class FileCompleter(Completer):
112
103
  stderr=subprocess.PIPE,
113
104
  text=True)
114
105
  if result.returncode == 0:
115
- all_files = [line.strip() for line in result.stdout.splitlines() if line.strip()]
106
+ all_completions.extend([
107
+ (path, "File")
108
+ for path in result.stdout.splitlines()
109
+ if path.strip()
110
+ ])
116
111
  except Exception:
117
112
  pass
118
- # 生成补全建议
119
- if not file_path:
120
- scored_files = [(path, 100) for path in all_files[:self.max_suggestions]]
121
- else:
122
- scored_files_data = process.extract(file_path, all_files, limit=self.max_suggestions)
123
- scored_files = [(m[0], m[1]) for m in scored_files_data]
124
- scored_files.sort(key=lambda x: x[1], reverse=True)
125
- scored_files = scored_files[:self.max_suggestions]
126
- # 生成补全项
127
- for path, score in scored_files:
128
- if not file_path or score > self.min_score:
129
- display_text = path
130
- if file_path and score < 100:
131
- display_text = f"{path} ({score}%)"
113
+
114
+ # 统一过滤和排序
115
+ if file_path:
116
+ # 使用模糊匹配过滤
117
+ scored_items = process.extract(file_path, [item[0] for item in all_completions], limit=self.max_suggestions)
118
+ scored_items = [(item[0], item[1]) for item in scored_items if item[1] > self.min_score]
119
+ # 创建映射以便查找描述
120
+ completion_map = {item[0]: item[1] for item in all_completions}
121
+ # 生成补全项
122
+ for text, score in scored_items:
123
+ display_text = text
124
+ if score < 100:
125
+ display_text = f"{text} ({score}%)"
132
126
  yield Completion(
133
- text=f"'{path}'",
127
+ text=f"'{text}'",
134
128
  start_position=-replace_length,
135
129
  display=display_text,
136
- display_meta="File"
130
+ display_meta=completion_map.get(text, "")
131
+ ) # type: ignore
132
+ else:
133
+ # 没有输入时返回前max_suggestions个建议
134
+ for text, desc in all_completions[:self.max_suggestions]:
135
+ yield Completion(
136
+ text=f"'{text}'",
137
+ start_position=-replace_length,
138
+ display=text,
139
+ display_meta=desc
137
140
  ) # type: ignore
138
141
 
139
142
  def _get_description(self, tag: str) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.160
3
+ Version: 0.1.162
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -1,14 +1,13 @@
1
- jarvis/__init__.py,sha256=2BC-X3PyDiJd9_Z7-M3Dz_vjF41hQuACD5kortAXRDM,50
2
- jarvis/jarvis_agent/__init__.py,sha256=tii6khwfG971MJiMfKSLwlKTjB3iEv8ITE3JqYSkfQA,24824
1
+ jarvis/__init__.py,sha256=f8wg5GALz7h8klEribpOjtvP8qwhjEN7ycs1Z0a32JE,50
2
+ jarvis/jarvis_agent/__init__.py,sha256=RCFx4Hz755PRYI01Y3VoUKXXJCj0pOGrEg4qP099o_g,24745
3
3
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=3rRA-7v_VUSFG1s7tTKhriq9vv0nsa3t69ReV0xH5gs,1505
4
4
  jarvis/jarvis_agent/file_input_handler.py,sha256=88VqJLe3oO9GtIRsqyx3KwZl10Apob2ddFMH3HQ2RMg,3413
5
- jarvis/jarvis_agent/jarvis.py,sha256=6Bp_Y-E3RN6bDmDs5lN3uALL_Dhq-p_aIuVNELG1vtw,5898
5
+ jarvis/jarvis_agent/jarvis.py,sha256=EQDK7CEfoJXg-KkZ7DGDi0lALauFR4tPRvW2O-CxJAQ,5795
6
6
  jarvis/jarvis_agent/main.py,sha256=Jlw_Tofh2C-sMVnkeOZBrwWJOWNH3IhsKDUn-WBlgU8,2602
7
7
  jarvis/jarvis_agent/output_handler.py,sha256=4limQ-Kf-YYvQjT5SMjJIyyvD1DVG8tINv1A_qbv4ho,405
8
- jarvis/jarvis_agent/patch.py,sha256=qeRZ6r1qpgHnWpX5uOX4dB3qRCPI1ibk4J50NmoJ74s,23531
9
8
  jarvis/jarvis_agent/shell_input_handler.py,sha256=9IoGQCe6FF4HA2V5S11q63AtnWDZFpNeRd3hcqCAlBw,1237
10
9
  jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- jarvis/jarvis_code_agent/code_agent.py,sha256=vi_g-LWdD5nn3RQsC1X8G2iNK8SrBADwFLUkflqtvKE,13723
10
+ jarvis/jarvis_code_agent/code_agent.py,sha256=yJbVIyJdEEzm2RpqpeBc0jGFQq9mGVWLBYj9Fco1vR8,18118
12
11
  jarvis/jarvis_code_analysis/code_review.py,sha256=gB9Xo0-FT6zciDzZb3jF6zxxWA_Aj8QU5008Tmu_Tr4,30192
13
12
  jarvis/jarvis_code_analysis/checklists/__init__.py,sha256=PCjlyxLa939613cAzS7pfEPgP57setO-1RvcdzzPivw,54
14
13
  jarvis/jarvis_code_analysis/checklists/c_cpp.py,sha256=8lfWmhImAxeTBdHPOgVXDjMllaq280Qki1ZOOSDBnvk,1293
@@ -30,12 +29,12 @@ jarvis/jarvis_code_analysis/checklists/shell.py,sha256=UBtGhi3d5sIhyUSGmDckYOXwp
30
29
  jarvis/jarvis_code_analysis/checklists/sql.py,sha256=-bGfYhaFJyHrbcJrUMbkMyPCNVbk8UljNqebqVJJKxM,2331
31
30
  jarvis/jarvis_code_analysis/checklists/swift.py,sha256=d-zPPbM_J1G8fgZ2M2-ASQbIxEocsdL1owL4Z2PCnOc,2542
32
31
  jarvis/jarvis_code_analysis/checklists/web.py,sha256=phdvLGqRHNijA0OyEwVtgHgz1Hi4ldtJJscOhEQvbSQ,3919
33
- jarvis/jarvis_dev/main.py,sha256=Ew4cm9o01OkSvHeF2Ky73T10gdp205pEyh2l5fWKT2M,42620
32
+ jarvis/jarvis_dev/main.py,sha256=diznR6kvh0X5kYnZ-lsDIcscgmXV-qatS79FbSWZ3iU,42784
34
33
  jarvis/jarvis_git_details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
34
  jarvis/jarvis_git_details/main.py,sha256=YowncVxYyJ3y2EvGrZhAJeR4yizXp6aB3dqvoYTepFY,6117
36
35
  jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
36
  jarvis/jarvis_git_squash/main.py,sha256=xBNkAl7_8_pQC-C6RcUImA1mEU4KTqhjtA57rG_mMJ8,2179
38
- jarvis/jarvis_git_utils/git_commiter.py,sha256=nOzM5Zio6CYxuo14YfZHrdr5rPS-yjM0_UbRhsXjPy4,11644
37
+ jarvis/jarvis_git_utils/git_commiter.py,sha256=T94R496xYx17sjh4P3RtLqVYZkjB3xz8so8Q1aaei64,11963
39
38
  jarvis/jarvis_lsp/base.py,sha256=f-76xgNijfQ4G3Q0t8IfOGtCu-q2TSQ7a_in6XwDb_8,2030
40
39
  jarvis/jarvis_lsp/cpp.py,sha256=ekci2M9_UtkCSEe9__72h26Gat93r9_knL2VmFr8X5M,3141
41
40
  jarvis/jarvis_lsp/go.py,sha256=sSypuQSP5X2YtrVMC8XCc5nXkgfG93SO7sC89lHzoR8,3458
@@ -66,6 +65,7 @@ jarvis/jarvis_tools/chdir.py,sha256=do_OdtabiH3lZcT_ynjSAX66XgH2gPl9mYiS7dMMDa8,
66
65
  jarvis/jarvis_tools/code_plan.py,sha256=jNa2rs4J3Fam8Q_RHE2_QvVch21TPp-Zfv-W6iQ3D_0,7729
67
66
  jarvis/jarvis_tools/create_code_agent.py,sha256=SRiQXZf57ViIDh6YSEmJkcoSKft0-y3iDfWF8f1bvZU,3387
68
67
  jarvis/jarvis_tools/create_sub_agent.py,sha256=wGiHukvi58wb1AKW5beP7R8VvApOn8TOeGmtXsmcETE,3001
68
+ jarvis/jarvis_tools/edit_file.py,sha256=z9ebYOUUWAkBHf0NVgv6vLZJ2vJzwasdAGWLH0UsuPE,22610
69
69
  jarvis/jarvis_tools/execute_script.py,sha256=AeuC3yZIg-nBq_LTIyqxu-lG_uLG63lvwO28A6dRDYA,5715
70
70
  jarvis/jarvis_tools/file_analyzer.py,sha256=EVl7WqGgZoaQXqEX8vLynpqZDE3aug1hVBJbycT7YiY,4844
71
71
  jarvis/jarvis_tools/file_operation.py,sha256=zbc1zX_aJ-9x1wpuUUbZOgg0aN8f5sd9ryaCQ-bi870,9124
@@ -84,14 +84,14 @@ jarvis/jarvis_utils/embedding.py,sha256=_Q-VurYHQZSsyISClTFjadDaNqNPBMqJe58lMM6b
84
84
  jarvis/jarvis_utils/file_processors.py,sha256=oNtVlz2JHcQ60NS6sgI-VsvYXOnsQgFUEVenznCXHC4,2952
85
85
  jarvis/jarvis_utils/git_utils.py,sha256=j_Jw6h7JD91XhMf0WD3MAH4URkLUBrrYCLnuLm1GeN4,5630
86
86
  jarvis/jarvis_utils/globals.py,sha256=Ed2d6diWXCgI74HVV_tI4qW7yXxLpNvQKN2yG0IH9hc,3388
87
- jarvis/jarvis_utils/input.py,sha256=zvL-JQSJvkmsZB7ApJsaVpk46BkpmJA3I_BnWSl_D-4,7299
87
+ jarvis/jarvis_utils/input.py,sha256=3mQO_Ys1DGQQWCrw_zD9hnTF4Xd6vyQUrer8IastzvQ,7408
88
88
  jarvis/jarvis_utils/methodology.py,sha256=a1QJLqZ-_RwhL6i7C6mixhT1BmOvZ0mKmh3PTOiNT9M,7744
89
89
  jarvis/jarvis_utils/output.py,sha256=BmWdB1bmizv0xfU4Z___9p_xQodorriIcEgADVq9fk0,8416
90
90
  jarvis/jarvis_utils/tag.py,sha256=YtXBYuZWy8j8YbeQX2qRrHRQl6Gp2Vt7W4p-2yjo0a4,405
91
91
  jarvis/jarvis_utils/utils.py,sha256=neokG_C9Djw6shwLcBxpQmRF5KFp9P6v52bMJMEFozg,4487
92
- jarvis_ai_assistant-0.1.160.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
93
- jarvis_ai_assistant-0.1.160.dist-info/METADATA,sha256=yjL-ZV1dnisJxbtkjiQH9fOeGGv0hI7SUGKIPWnz_h8,12669
94
- jarvis_ai_assistant-0.1.160.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
95
- jarvis_ai_assistant-0.1.160.dist-info/entry_points.txt,sha256=cKz_9SEpOvElTubKPMZMAdskD4GHz-NyKWRNssIVAWE,973
96
- jarvis_ai_assistant-0.1.160.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
97
- jarvis_ai_assistant-0.1.160.dist-info/RECORD,,
92
+ jarvis_ai_assistant-0.1.162.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
93
+ jarvis_ai_assistant-0.1.162.dist-info/METADATA,sha256=W8irlmW5HoenF5m40wSEGoL-jV8EYskUWbvChvq_v7M,12669
94
+ jarvis_ai_assistant-0.1.162.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
95
+ jarvis_ai_assistant-0.1.162.dist-info/entry_points.txt,sha256=cKz_9SEpOvElTubKPMZMAdskD4GHz-NyKWRNssIVAWE,973
96
+ jarvis_ai_assistant-0.1.162.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
97
+ jarvis_ai_assistant-0.1.162.dist-info/RECORD,,