jarvis-ai-assistant 0.1.207__py3-none-any.whl → 0.1.209__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 (42) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +63 -103
  3. jarvis/jarvis_agent/edit_file_handler.py +43 -47
  4. jarvis/jarvis_agent/jarvis.py +33 -39
  5. jarvis/jarvis_code_agent/code_agent.py +74 -30
  6. jarvis/jarvis_code_agent/lint.py +6 -6
  7. jarvis/jarvis_code_analysis/code_review.py +164 -175
  8. jarvis/jarvis_data/config_schema.json +0 -25
  9. jarvis/jarvis_git_utils/git_commiter.py +148 -153
  10. jarvis/jarvis_methodology/main.py +70 -81
  11. jarvis/jarvis_platform/base.py +21 -17
  12. jarvis/jarvis_platform/kimi.py +59 -64
  13. jarvis/jarvis_platform/tongyi.py +118 -131
  14. jarvis/jarvis_platform/yuanbao.py +117 -122
  15. jarvis/jarvis_platform_manager/main.py +102 -502
  16. jarvis/jarvis_platform_manager/service.py +432 -0
  17. jarvis/jarvis_smart_shell/main.py +99 -33
  18. jarvis/jarvis_tools/ask_user.py +0 -1
  19. jarvis/jarvis_tools/edit_file.py +64 -55
  20. jarvis/jarvis_tools/file_analyzer.py +17 -28
  21. jarvis/jarvis_tools/read_code.py +80 -81
  22. jarvis/jarvis_utils/builtin_replace_map.py +1 -36
  23. jarvis/jarvis_utils/config.py +13 -48
  24. jarvis/jarvis_utils/embedding.py +6 -51
  25. jarvis/jarvis_utils/git_utils.py +93 -43
  26. jarvis/jarvis_utils/http.py +104 -0
  27. jarvis/jarvis_utils/methodology.py +12 -17
  28. jarvis/jarvis_utils/utils.py +186 -63
  29. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -19
  30. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +34 -40
  31. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -1
  32. jarvis/jarvis_data/huggingface.tar.gz +0 -0
  33. jarvis/jarvis_dev/main.py +0 -1247
  34. jarvis/jarvis_tools/chdir.py +0 -72
  35. jarvis/jarvis_tools/code_plan.py +0 -218
  36. jarvis/jarvis_tools/create_code_agent.py +0 -95
  37. jarvis/jarvis_tools/create_sub_agent.py +0 -82
  38. jarvis/jarvis_tools/file_operation.py +0 -238
  39. jarvis/jarvis_utils/jarvis_history.py +0 -98
  40. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
  41. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
  42. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
@@ -12,14 +12,15 @@ Git工具模块
12
12
  import os
13
13
  import re
14
14
  import subprocess
15
- from typing import Any, Dict, List, Tuple
15
+ import sys
16
+ from typing import Any, Dict, List, Set, Tuple
16
17
 
17
- from jarvis.jarvis_utils.config import get_auto_update, is_confirm_before_apply_patch
18
+ from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
18
19
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
19
20
  from jarvis.jarvis_utils.utils import user_confirm
20
21
 
21
22
 
22
- def find_git_root(start_dir: str = ".") -> str:
23
+ def find_git_root_and_cd(start_dir: str = ".") -> str:
23
24
  """
24
25
  切换到给定路径的Git根目录,如果不是Git仓库则初始化。
25
26
 
@@ -212,14 +213,15 @@ def handle_commit_workflow() -> bool:
212
213
  Returns:
213
214
  bool: 提交是否成功
214
215
  """
215
- if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
216
+ if is_confirm_before_apply_patch() and not user_confirm(
217
+ "是否要提交代码?", default=True
218
+ ):
216
219
  revert_change()
217
220
  return False
218
221
 
219
222
  import subprocess
220
223
 
221
224
  try:
222
-
223
225
  confirm_add_new_files()
224
226
 
225
227
  if not has_uncommitted_changes():
@@ -337,13 +339,11 @@ def check_and_update_git_repo(repo_path: str) -> bool:
337
339
  bool: 是否执行了更新
338
340
  """
339
341
  curr_dir = os.path.abspath(os.getcwd())
340
- git_root = find_git_root(repo_path)
342
+ git_root = find_git_root_and_cd(repo_path)
341
343
  if git_root is None:
342
344
  return False
343
345
 
344
346
  try:
345
- if not get_auto_update():
346
- return False
347
347
  # 检查是否有未提交的修改
348
348
  if has_uncommitted_changes():
349
349
  return False
@@ -394,7 +394,49 @@ def check_and_update_git_repo(repo_path: str) -> bool:
394
394
  f"Jarvis已更新到tag {remote_tag_result.stdout.strip()}",
395
395
  OutputType.SUCCESS,
396
396
  )
397
- return True
397
+
398
+ # 执行pip安装更新代码
399
+ try:
400
+ PrettyOutput.print("正在安装更新后的代码...", OutputType.INFO)
401
+
402
+ # 检查是否在虚拟环境中
403
+ in_venv = hasattr(sys, 'real_prefix') or (
404
+ hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix
405
+ )
406
+
407
+ # 尝试普通安装
408
+ install_cmd = [sys.executable, "-m", "pip", "install", "-e", "."]
409
+ result = subprocess.run(
410
+ install_cmd,
411
+ cwd=git_root,
412
+ capture_output=True,
413
+ text=True
414
+ )
415
+
416
+ if result.returncode == 0:
417
+ PrettyOutput.print("代码更新安装成功", OutputType.SUCCESS)
418
+ return True
419
+
420
+ # 处理权限错误
421
+ error_msg = result.stderr.strip()
422
+ if not in_venv and ("Permission denied" in error_msg or "not writeable" in error_msg):
423
+ if user_confirm("检测到权限问题,是否尝试用户级安装(--user)?", True):
424
+ user_result = subprocess.run(
425
+ install_cmd + ["--user"],
426
+ cwd=git_root,
427
+ capture_output=True,
428
+ text=True
429
+ )
430
+ if user_result.returncode == 0:
431
+ PrettyOutput.print("用户级代码安装成功", OutputType.SUCCESS)
432
+ return True
433
+ error_msg = user_result.stderr.strip()
434
+
435
+ PrettyOutput.print(f"代码安装失败: {error_msg}", OutputType.ERROR)
436
+ return False
437
+ except Exception as e:
438
+ PrettyOutput.print(f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR)
439
+ return False
398
440
  return False
399
441
  except Exception as e:
400
442
  PrettyOutput.print(f"Git仓库更新检查失败: {e}", OutputType.WARNING)
@@ -411,7 +453,7 @@ def get_diff_file_list() -> List[str]:
411
453
  """
412
454
  try:
413
455
  confirm_add_new_files()
414
-
456
+
415
457
  # 暂存新增文件
416
458
  subprocess.run(["git", "add", "-N", "."], check=True)
417
459
 
@@ -424,7 +466,9 @@ def get_diff_file_list() -> List[str]:
424
466
  subprocess.run(["git", "reset"], check=True)
425
467
 
426
468
  if result.returncode != 0:
427
- PrettyOutput.print(f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR)
469
+ PrettyOutput.print(
470
+ f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR
471
+ )
428
472
  return []
429
473
 
430
474
  return [f for f in result.stdout.splitlines() if f]
@@ -488,14 +532,15 @@ def get_recent_commits_with_files() -> List[Dict[str, Any]]:
488
532
  )
489
533
  if files_result.returncode == 0:
490
534
  file_lines = files_result.stdout.splitlines()
491
- unique_files = set(filter(None, file_lines))
492
- commit["files"] = list(unique_files)[:20] # 限制最多20个文件
535
+ unique_files: Set[str] = set(filter(None, file_lines))
536
+ commit["files"] = list(unique_files)[:20] # type: ignore[list-item] # 限制最多20个文件
493
537
 
494
538
  return commits
495
539
 
496
540
  except subprocess.CalledProcessError:
497
541
  return []
498
542
 
543
+
499
544
  def _get_new_files() -> List[str]:
500
545
  """获取新增文件列表"""
501
546
  return subprocess.run(
@@ -505,8 +550,10 @@ def _get_new_files() -> List[str]:
505
550
  check=True,
506
551
  ).stdout.splitlines()
507
552
 
553
+
508
554
  def confirm_add_new_files() -> None:
509
555
  """确认新增文件、代码行数和二进制文件"""
556
+
510
557
  def _get_added_lines() -> int:
511
558
  """获取新增代码行数"""
512
559
  diff_stats = subprocess.run(
@@ -515,7 +562,7 @@ def confirm_add_new_files() -> None:
515
562
  text=True,
516
563
  check=True,
517
564
  ).stdout.splitlines()
518
-
565
+
519
566
  added_lines = 0
520
567
  for stat in diff_stats:
521
568
  parts = stat.split()
@@ -531,55 +578,58 @@ def confirm_add_new_files() -> None:
531
578
  binary_files = []
532
579
  for file in files:
533
580
  try:
534
- with open(file, 'rb') as f:
535
- if b'\x00' in f.read(1024):
581
+ with open(file, "rb") as f:
582
+ if b"\x00" in f.read(1024):
536
583
  binary_files.append(file)
537
584
  except (IOError, PermissionError):
538
585
  continue
539
586
  return binary_files
540
587
 
541
- def _check_conditions(new_files: List[str], added_lines: int, binary_files: List[str]) -> bool:
588
+ def _check_conditions(
589
+ new_files: List[str], added_lines: int, binary_files: List[str]
590
+ ) -> bool:
542
591
  """检查各种条件并打印提示信息"""
543
592
  need_confirm = False
544
-
593
+ output_lines = []
594
+
545
595
  if len(new_files) > 20:
546
- PrettyOutput.print(
547
- f"检测到{len(new_files)}个新增文件(选择N将重新检测)",
548
- OutputType.WARNING
549
- )
550
- PrettyOutput.print("新增文件列表:", OutputType.INFO)
551
- for file in new_files:
552
- PrettyOutput.print(f" - {file}", OutputType.INFO)
596
+ output_lines.append(f"检测到{len(new_files)}个新增文件(选择N将重新检测)")
597
+ output_lines.append("新增文件列表:")
598
+ output_lines.extend(f" - {file}" for file in new_files)
553
599
  need_confirm = True
554
-
600
+
555
601
  if added_lines > 500:
556
- PrettyOutput.print(
557
- f"检测到{added_lines}行新增代码(选择N将重新检测)",
558
- OutputType.WARNING
559
- )
602
+ output_lines.append(f"检测到{added_lines}行新增代码(选择N将重新检测)")
560
603
  need_confirm = True
561
-
604
+
562
605
  if binary_files:
563
- PrettyOutput.print(
564
- f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)",
565
- OutputType.WARNING
606
+ output_lines.append(
607
+ f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)"
566
608
  )
567
- PrettyOutput.print("二进制文件列表:", OutputType.INFO)
568
- for file in binary_files:
569
- PrettyOutput.print(f" - {file}", OutputType.INFO)
609
+ output_lines.append("二进制文件列表:")
610
+ output_lines.extend(f" - {file}" for file in binary_files)
570
611
  need_confirm = True
571
-
612
+
613
+ if output_lines:
614
+ PrettyOutput.print(
615
+ "\n".join(output_lines),
616
+ OutputType.WARNING if need_confirm else OutputType.INFO,
617
+ )
618
+
572
619
  return need_confirm
573
620
 
574
621
  while True:
575
622
  new_files = _get_new_files()
576
623
  added_lines = _get_added_lines()
577
624
  binary_files = _get_binary_files(new_files)
578
-
625
+
579
626
  if not _check_conditions(new_files, added_lines, binary_files):
580
627
  break
581
-
582
- if not user_confirm("是否要添加这些变更(如果不需要请修改.gitignore文件以忽略不需要的文件)?", False):
628
+
629
+ if not user_confirm(
630
+ "是否要添加这些变更(如果不需要请修改.gitignore文件以忽略不需要的文件)?",
631
+ False,
632
+ ):
583
633
  continue
584
-
585
- break
634
+
635
+ break
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import requests
4
+ from typing import Any, Dict, Optional, Union
5
+
6
+
7
+ def get_session() -> requests.Session:
8
+ """
9
+ 获取一个永不超时的 requests.Session 对象
10
+
11
+ 返回:
12
+ requests.Session 对象
13
+ """
14
+ session = requests.Session()
15
+
16
+ # 设置默认请求头以优化连接
17
+ session.headers.update(
18
+ {"Connection": "keep-alive"}
19
+ )
20
+
21
+ return session
22
+
23
+
24
+ # 增强版本的 HTTP 请求方法(带重试机制,解决连接中断问题)
25
+ def post(
26
+ url: str,
27
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
28
+ json: Optional[Dict[str, Any]] = None,
29
+ **kwargs,
30
+ ) -> requests.Response:
31
+ """
32
+ 发送增强版永不超时的 POST 请求,包含重试机制
33
+
34
+ 参数:
35
+ url: 请求的 URL
36
+ data: (可选) 请求体数据 (表单数据或原始数据)
37
+ json: (可选) JSON 数据,会自动设置 Content-Type
38
+ **kwargs: 其他传递给 requests.post 的参数
39
+
40
+ 返回:
41
+ requests.Response 对象
42
+
43
+ 注意:
44
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
45
+ """
46
+ session = get_session()
47
+ return session.post(url=url, data=data, json=json, **kwargs)
48
+
49
+
50
+ def get(url: str, **kwargs) -> requests.Response:
51
+ """
52
+ 发送增强版永不超时的 GET 请求,包含重试机制
53
+
54
+ 参数:
55
+ url: 请求的 URL
56
+ **kwargs: 其他传递给 requests.get 的参数
57
+
58
+ 返回:
59
+ requests.Response 对象
60
+
61
+ 注意:
62
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
63
+ """
64
+ session = get_session()
65
+ return session.get(url=url, **kwargs)
66
+
67
+
68
+ def put(
69
+ url: str, data: Optional[Union[Dict[str, Any], str, bytes]] = None, **kwargs
70
+ ) -> requests.Response:
71
+ """
72
+ 发送增强版永不超时的 PUT 请求,包含重试机制
73
+
74
+ 参数:
75
+ url: 请求的 URL
76
+ data: (可选) 请求体数据 (表单数据或原始数据)
77
+ **kwargs: 其他传递给 requests.put 的参数
78
+
79
+ 返回:
80
+ requests.Response 对象
81
+
82
+ 注意:
83
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
84
+ """
85
+ session = get_session()
86
+ return session.put(url=url, data=data, **kwargs)
87
+
88
+
89
+ def delete(url: str, **kwargs) -> requests.Response:
90
+ """
91
+ 发送增强版永不超时的 DELETE 请求,包含重试机制
92
+
93
+ 参数:
94
+ url: 请求的 URL
95
+ **kwargs: 其他传递给 requests.delete 的参数
96
+
97
+ 返回:
98
+ requests.Response 对象
99
+
100
+ 注意:
101
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
102
+ """
103
+ session = get_session()
104
+ return session.delete(url=url, **kwargs)
@@ -145,7 +145,6 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
145
145
  返回:
146
146
  str: 相关的方法论提示,如果未找到方法论则返回空字符串
147
147
  """
148
- from yaspin import yaspin # type: ignore
149
148
 
150
149
  prompt = tool_registery.prompt() if tool_registery else ""
151
150
 
@@ -156,14 +155,12 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
156
155
 
157
156
  try:
158
157
  # 加载所有方法论
159
- with yaspin(text="加载方法论文件...", color="yellow") as spinner:
160
- methodologies = _load_all_methodologies()
161
- if not methodologies:
162
- spinner.text = "没有找到方法论文件"
163
- spinner.fail("")
164
- return ""
165
- spinner.text = f"加载方法论文件完成 (共 {len(methodologies)} 个)"
166
- spinner.ok("✅")
158
+ print(f"📁 加载方法论文件...")
159
+ methodologies = _load_all_methodologies()
160
+ if not methodologies:
161
+ print(f"❌ 没有找到方法论文件")
162
+ return ""
163
+ print(f"✅ 加载方法论文件完成 (共 {len(methodologies)} 个)")
167
164
 
168
165
  # 获取当前平台
169
166
  platform = PlatformRegistry().get_normal_platform()
@@ -207,14 +204,12 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
207
204
  try:
208
205
  if is_large_content:
209
206
  # 创建临时文件
210
- with yaspin(text="创建方法论临时文件...", color="yellow") as spinner:
211
- temp_file_path = _create_methodology_temp_file(methodologies)
212
- if not temp_file_path:
213
- spinner.text = "创建方法论临时文件失败"
214
- spinner.fail("")
215
- return ""
216
- spinner.text = f"创建方法论临时文件完成: {temp_file_path}"
217
- spinner.ok("✅")
207
+ print(f"📝 创建方法论临时文件...")
208
+ temp_file_path = _create_methodology_temp_file(methodologies)
209
+ if not temp_file_path:
210
+ print(f"❌ 创建方法论临时文件失败")
211
+ return ""
212
+ print(f"✅ 创建方法论临时文件完成")
218
213
 
219
214
  # 尝试上传文件
220
215
  upload_success = platform.upload_files([temp_file_path])