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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +63 -103
- jarvis/jarvis_agent/edit_file_handler.py +43 -47
- jarvis/jarvis_agent/jarvis.py +33 -39
- jarvis/jarvis_code_agent/code_agent.py +74 -30
- jarvis/jarvis_code_agent/lint.py +6 -6
- jarvis/jarvis_code_analysis/code_review.py +164 -175
- jarvis/jarvis_data/config_schema.json +0 -25
- jarvis/jarvis_git_utils/git_commiter.py +148 -153
- jarvis/jarvis_methodology/main.py +70 -81
- jarvis/jarvis_platform/base.py +21 -17
- jarvis/jarvis_platform/kimi.py +59 -64
- jarvis/jarvis_platform/tongyi.py +118 -131
- jarvis/jarvis_platform/yuanbao.py +117 -122
- jarvis/jarvis_platform_manager/main.py +102 -502
- jarvis/jarvis_platform_manager/service.py +432 -0
- jarvis/jarvis_smart_shell/main.py +99 -33
- jarvis/jarvis_tools/ask_user.py +0 -1
- jarvis/jarvis_tools/edit_file.py +64 -55
- jarvis/jarvis_tools/file_analyzer.py +17 -28
- jarvis/jarvis_tools/read_code.py +80 -81
- jarvis/jarvis_utils/builtin_replace_map.py +1 -36
- jarvis/jarvis_utils/config.py +13 -48
- jarvis/jarvis_utils/embedding.py +6 -51
- jarvis/jarvis_utils/git_utils.py +93 -43
- jarvis/jarvis_utils/http.py +104 -0
- jarvis/jarvis_utils/methodology.py +12 -17
- jarvis/jarvis_utils/utils.py +186 -63
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -19
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +34 -40
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -1
- jarvis/jarvis_data/huggingface.tar.gz +0 -0
- jarvis/jarvis_dev/main.py +0 -1247
- jarvis/jarvis_tools/chdir.py +0 -72
- jarvis/jarvis_tools/code_plan.py +0 -218
- jarvis/jarvis_tools/create_code_agent.py +0 -95
- jarvis/jarvis_tools/create_sub_agent.py +0 -82
- jarvis/jarvis_tools/file_operation.py +0 -238
- jarvis/jarvis_utils/jarvis_history.py +0 -98
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/git_utils.py
CHANGED
@@ -12,14 +12,15 @@ Git工具模块
|
|
12
12
|
import os
|
13
13
|
import re
|
14
14
|
import subprocess
|
15
|
-
|
15
|
+
import sys
|
16
|
+
from typing import Any, Dict, List, Set, Tuple
|
16
17
|
|
17
|
-
from jarvis.jarvis_utils.config import
|
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
|
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(
|
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 =
|
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
|
-
|
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(
|
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,
|
535
|
-
if b
|
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(
|
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
|
-
|
547
|
-
|
548
|
-
|
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
|
-
|
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
|
-
|
564
|
-
f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)"
|
565
|
-
OutputType.WARNING
|
606
|
+
output_lines.append(
|
607
|
+
f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)"
|
566
608
|
)
|
567
|
-
|
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(
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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])
|