jarvis-ai-assistant 0.1.207__py3-none-any.whl → 0.1.208__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 +71 -61
- jarvis/jarvis_agent/edit_file_handler.py +42 -46
- jarvis/jarvis_agent/jarvis.py +33 -39
- jarvis/jarvis_code_agent/code_agent.py +26 -27
- jarvis/jarvis_code_agent/lint.py +5 -5
- jarvis/jarvis_code_analysis/code_review.py +164 -175
- jarvis/jarvis_git_utils/git_commiter.py +147 -152
- jarvis/jarvis_methodology/main.py +70 -81
- jarvis/jarvis_platform/base.py +21 -17
- jarvis/jarvis_platform/kimi.py +39 -53
- jarvis/jarvis_platform/tongyi.py +108 -126
- jarvis/jarvis_platform/yuanbao.py +112 -120
- 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/edit_file.py +64 -55
- jarvis/jarvis_tools/file_analyzer.py +17 -25
- jarvis/jarvis_tools/read_code.py +80 -81
- jarvis/jarvis_utils/builtin_replace_map.py +1 -36
- jarvis/jarvis_utils/config.py +14 -4
- jarvis/jarvis_utils/git_utils.py +36 -35
- jarvis/jarvis_utils/methodology.py +12 -17
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.208.dist-info}/METADATA +1 -10
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.208.dist-info}/RECORD +29 -34
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.208.dist-info}/entry_points.txt +0 -1
- 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_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.208.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.208.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.208.dist-info}/top_level.txt +0 -0
jarvis/jarvis_tools/edit_file.py
CHANGED
@@ -19,8 +19,6 @@
|
|
19
19
|
"""
|
20
20
|
from typing import Any, Dict
|
21
21
|
|
22
|
-
from yaspin import yaspin
|
23
|
-
|
24
22
|
from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
|
25
23
|
|
26
24
|
|
@@ -62,12 +60,18 @@ class FileSearchReplaceTool:
|
|
62
60
|
"items": {
|
63
61
|
"type": "object",
|
64
62
|
"properties": {
|
65
|
-
"reason": {
|
63
|
+
"reason": {
|
64
|
+
"type": "string",
|
65
|
+
"description": "修改的原因",
|
66
|
+
},
|
66
67
|
"search": {
|
67
68
|
"type": "string",
|
68
69
|
"description": "需要查找的原始代码",
|
69
70
|
},
|
70
|
-
"replace": {
|
71
|
+
"replace": {
|
72
|
+
"type": "string",
|
73
|
+
"description": "替换后的新代码",
|
74
|
+
},
|
71
75
|
},
|
72
76
|
},
|
73
77
|
},
|
@@ -79,7 +83,6 @@ class FileSearchReplaceTool:
|
|
79
83
|
"required": ["files"],
|
80
84
|
}
|
81
85
|
|
82
|
-
|
83
86
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
84
87
|
"""执行文件编辑操作,支持快速编辑和AI辅助编辑两种模式。
|
85
88
|
|
@@ -152,46 +155,46 @@ class FileSearchReplaceTool:
|
|
152
155
|
if file_exists and agent:
|
153
156
|
files = agent.get_user_data("files")
|
154
157
|
if not files or file_path not in files:
|
155
|
-
file_results.append(
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
158
|
+
file_results.append(
|
159
|
+
{
|
160
|
+
"file": file_path,
|
161
|
+
"success": False,
|
162
|
+
"stdout": "",
|
163
|
+
"stderr": f"请先读取文件 {file_path} 的内容后再编辑",
|
164
|
+
}
|
165
|
+
)
|
161
166
|
continue
|
162
167
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
+
print(f"⚙️ 正在处理文件 {file_path}...")
|
169
|
+
# 首先尝试fast_edit模式
|
170
|
+
success, temp_content = EditFileHandler._fast_edit(
|
171
|
+
file_path, changes
|
172
|
+
)
|
173
|
+
if not success:
|
174
|
+
# 如果fast_edit失败,尝试slow_edit模式
|
175
|
+
success, temp_content = EditFileHandler._slow_edit(
|
176
|
+
file_path, changes, agent
|
177
|
+
)
|
168
178
|
if not success:
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
changes,
|
173
|
-
spinner,
|
174
|
-
agent
|
175
|
-
)
|
176
|
-
if not success:
|
177
|
-
spinner.text = f"文件 {file_path} 处理失败"
|
178
|
-
spinner.fail("❌")
|
179
|
-
file_results.append({
|
179
|
+
print(f"❌ 文件 {file_path} 处理失败")
|
180
|
+
file_results.append(
|
181
|
+
{
|
180
182
|
"file": file_path,
|
181
183
|
"success": False,
|
182
184
|
"stdout": "",
|
183
|
-
"stderr": temp_content
|
184
|
-
}
|
185
|
-
|
186
|
-
|
187
|
-
spinner.text = f"文件 {file_path} 内容生成完成"
|
188
|
-
spinner.ok("✅")
|
185
|
+
"stderr": temp_content,
|
186
|
+
}
|
187
|
+
)
|
188
|
+
continue
|
189
189
|
else:
|
190
|
-
|
191
|
-
|
190
|
+
print(f"✅ 文件 {file_path} 内容生成完成")
|
191
|
+
else:
|
192
|
+
print(f"✅ 文件 {file_path} 内容生成完成")
|
192
193
|
|
193
194
|
# 只有当所有替换操作都成功时,才写回文件
|
194
|
-
if success and (
|
195
|
+
if success and (
|
196
|
+
temp_content != original_content or not file_exists
|
197
|
+
):
|
195
198
|
# 确保目录存在
|
196
199
|
os.makedirs(
|
197
200
|
os.path.dirname(os.path.abspath(file_path)), exist_ok=True
|
@@ -208,24 +211,28 @@ class FileSearchReplaceTool:
|
|
208
211
|
PrettyOutput.print(stdout_message, OutputType.SUCCESS)
|
209
212
|
overall_success = True
|
210
213
|
|
211
|
-
file_results.append(
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
214
|
+
file_results.append(
|
215
|
+
{
|
216
|
+
"file": file_path,
|
217
|
+
"success": True,
|
218
|
+
"stdout": stdout_message,
|
219
|
+
"stderr": "",
|
220
|
+
}
|
221
|
+
)
|
217
222
|
|
218
223
|
except Exception as e:
|
219
224
|
stderr_message = f"处理文件 {file_path} 时出错: {str(e)}"
|
220
225
|
stderr_messages.append(stderr_message)
|
221
226
|
PrettyOutput.print(stderr_message, OutputType.WARNING)
|
222
227
|
file_success = False
|
223
|
-
file_results.append(
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
228
|
+
file_results.append(
|
229
|
+
{
|
230
|
+
"file": file_path,
|
231
|
+
"success": False,
|
232
|
+
"stdout": "",
|
233
|
+
"stderr": stderr_message,
|
234
|
+
}
|
235
|
+
)
|
229
236
|
|
230
237
|
except Exception as e:
|
231
238
|
error_msg = f"文件搜索替换操作失败: {str(e)}"
|
@@ -251,12 +258,14 @@ class FileSearchReplaceTool:
|
|
251
258
|
except:
|
252
259
|
stderr_messages.append(f"回滚文件失败: {file_path}")
|
253
260
|
|
254
|
-
file_results.append(
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
261
|
+
file_results.append(
|
262
|
+
{
|
263
|
+
"file": file_path,
|
264
|
+
"success": False,
|
265
|
+
"stdout": "",
|
266
|
+
"stderr": error_msg,
|
267
|
+
}
|
268
|
+
)
|
260
269
|
|
261
270
|
# 整合所有错误信息到stderr
|
262
271
|
all_stderr = []
|
@@ -267,5 +276,5 @@ class FileSearchReplaceTool:
|
|
267
276
|
return {
|
268
277
|
"success": overall_success,
|
269
278
|
"stdout": "\n".join(stdout_messages) if overall_success else "",
|
270
|
-
"stderr": "\n".join(all_stderr) if not overall_success else ""
|
279
|
+
"stderr": "\n".join(all_stderr) if not overall_success else "",
|
271
280
|
}
|
@@ -2,8 +2,6 @@
|
|
2
2
|
import os
|
3
3
|
from typing import Any, Dict
|
4
4
|
|
5
|
-
from yaspin import yaspin # type: ignore
|
6
|
-
from yaspin.spinners import Spinners # type: ignore
|
7
5
|
|
8
6
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
9
7
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
@@ -78,28 +76,24 @@ class FileAnalyzerTool:
|
|
78
76
|
platform.set_system_prompt(system_message)
|
79
77
|
|
80
78
|
# 上传文件
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
spinner.text = "文件上传失败"
|
87
|
-
spinner.fail("❌")
|
88
|
-
return {
|
89
|
-
"success": False,
|
90
|
-
"stdout": "",
|
91
|
-
"stderr": "文件上传失败",
|
92
|
-
}
|
93
|
-
spinner.text = "文件上传成功"
|
94
|
-
spinner.ok("✅")
|
95
|
-
except Exception as e:
|
96
|
-
spinner.text = "文件上传失败"
|
97
|
-
spinner.fail("❌")
|
79
|
+
print(f"📤 正在上传文件...")
|
80
|
+
try:
|
81
|
+
upload_result = platform.upload_files(valid_files)
|
82
|
+
if not upload_result:
|
83
|
+
print(f"❌ 文件上传失败")
|
98
84
|
return {
|
99
85
|
"success": False,
|
100
86
|
"stdout": "",
|
101
|
-
"stderr":
|
87
|
+
"stderr": "文件上传失败",
|
102
88
|
}
|
89
|
+
print(f"✅ 文件上传成功")
|
90
|
+
except Exception as e:
|
91
|
+
print(f"❌ 文件上传失败: {str(e)}")
|
92
|
+
return {
|
93
|
+
"success": False,
|
94
|
+
"stdout": "",
|
95
|
+
"stderr": f"文件上传失败: {str(e)}",
|
96
|
+
}
|
103
97
|
|
104
98
|
platform.set_suppress_output(False)
|
105
99
|
|
@@ -111,11 +105,9 @@ class FileAnalyzerTool:
|
|
111
105
|
请提供详细的分析结果和理由。"""
|
112
106
|
|
113
107
|
# 发送请求并获取分析结果
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
spinner.text = "分析完成"
|
118
|
-
spinner.ok("✅")
|
108
|
+
print(f"🔍 正在分析文件...")
|
109
|
+
analysis_result = platform.chat_until_success(analysis_request)
|
110
|
+
print(f"✅ 分析完成")
|
119
111
|
|
120
112
|
# 清理会话
|
121
113
|
platform.delete_chat()
|
jarvis/jarvis_tools/read_code.py
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
import os
|
3
3
|
from typing import Any, Dict
|
4
4
|
|
5
|
-
from yaspin import yaspin
|
6
|
-
|
7
5
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
8
6
|
|
9
7
|
|
@@ -46,89 +44,90 @@ class ReadCodeTool:
|
|
46
44
|
"""
|
47
45
|
try:
|
48
46
|
abs_path = os.path.abspath(filepath)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
total_lines = len(lines)
|
71
|
-
|
72
|
-
# 处理空文件情况
|
73
|
-
if total_lines == 0:
|
74
|
-
spinner.ok("✅")
|
75
|
-
return {
|
76
|
-
"success": True,
|
77
|
-
"stdout": f"\n🔍 文件: {abs_path}\n📄 文件为空 (0行)\n",
|
78
|
-
"stderr": "",
|
79
|
-
}
|
80
|
-
|
81
|
-
# 处理特殊值-1表示文件末尾
|
82
|
-
if end_line == -1:
|
83
|
-
end_line = total_lines
|
84
|
-
else:
|
85
|
-
end_line = (
|
86
|
-
max(1, min(end_line, total_lines))
|
87
|
-
if end_line >= 0
|
88
|
-
else total_lines + end_line + 1
|
89
|
-
)
|
90
|
-
|
91
|
-
start_line = (
|
92
|
-
max(1, min(start_line, total_lines))
|
93
|
-
if start_line >= 0
|
94
|
-
else total_lines + start_line + 1
|
95
|
-
)
|
47
|
+
print(f"📖 正在读取文件: {abs_path}...")
|
48
|
+
# 文件存在性检查
|
49
|
+
if not os.path.exists(abs_path):
|
50
|
+
return {
|
51
|
+
"success": False,
|
52
|
+
"stdout": "",
|
53
|
+
"stderr": f"文件不存在: {abs_path}",
|
54
|
+
}
|
55
|
+
|
56
|
+
# 文件大小限制检查(10MB)
|
57
|
+
if os.path.getsize(abs_path) > 10 * 1024 * 1024:
|
58
|
+
return {
|
59
|
+
"success": False,
|
60
|
+
"stdout": "",
|
61
|
+
"stderr": "文件过大 (>10MB)",
|
62
|
+
}
|
63
|
+
|
64
|
+
# 读取文件内容
|
65
|
+
with open(abs_path, "r", encoding="utf-8", errors="ignore") as f:
|
66
|
+
lines = f.readlines()
|
96
67
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
68
|
+
total_lines = len(lines)
|
69
|
+
|
70
|
+
# 处理空文件情况
|
71
|
+
if total_lines == 0:
|
72
|
+
print(f"✅ 文件读取完成: {abs_path}")
|
73
|
+
return {
|
74
|
+
"success": True,
|
75
|
+
"stdout": f"\n🔍 文件: {abs_path}\n📄 文件为空 (0行)\n",
|
76
|
+
"stderr": "",
|
77
|
+
}
|
78
|
+
|
79
|
+
# 处理特殊值-1表示文件末尾
|
80
|
+
if end_line == -1:
|
81
|
+
end_line = total_lines
|
82
|
+
else:
|
83
|
+
end_line = (
|
84
|
+
max(1, min(end_line, total_lines))
|
85
|
+
if end_line >= 0
|
86
|
+
else total_lines + end_line + 1
|
112
87
|
)
|
113
88
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
89
|
+
start_line = (
|
90
|
+
max(1, min(start_line, total_lines))
|
91
|
+
if start_line >= 0
|
92
|
+
else total_lines + start_line + 1
|
93
|
+
)
|
94
|
+
|
95
|
+
if start_line > end_line:
|
96
|
+
print(
|
97
|
+
f"❌ 无效的行范围 [{start_line}-{end_line}] (总行数: {total_lines})"
|
119
98
|
)
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
99
|
+
return {
|
100
|
+
"success": False,
|
101
|
+
"stdout": "",
|
102
|
+
"stderr": f"无效的行范围 [{start_line}-{end_line}] (总行数: {total_lines})",
|
103
|
+
}
|
104
|
+
|
105
|
+
# 添加行号并构建输出内容
|
106
|
+
selected_lines = lines[start_line - 1 : end_line]
|
107
|
+
numbered_content = "".join(
|
108
|
+
[
|
109
|
+
f"{i:4d}:{line}"
|
110
|
+
for i, line in enumerate(selected_lines, start=start_line)
|
111
|
+
]
|
112
|
+
)
|
113
|
+
|
114
|
+
# 构建输出格式
|
115
|
+
output = (
|
116
|
+
f"\n🔍 文件: {abs_path}\n"
|
117
|
+
f"📄 原始行号: {start_line}-{end_line} (共{total_lines}行) \n\n"
|
118
|
+
f"{numbered_content}\n\n"
|
119
|
+
)
|
120
|
+
print(f"✅ 文件读取完成: {abs_path}")
|
121
|
+
|
122
|
+
if agent:
|
123
|
+
files = agent.get_user_data("files")
|
124
|
+
if files:
|
125
|
+
files.append(abs_path)
|
126
|
+
else:
|
127
|
+
files = [abs_path]
|
128
|
+
agent.set_user_data("files", files)
|
129
|
+
|
130
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
132
131
|
|
133
132
|
except Exception as e:
|
134
133
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
@@ -29,42 +29,7 @@ arguments:
|
|
29
29
|
""",
|
30
30
|
"description": "网页搜索",
|
31
31
|
},
|
32
|
-
|
33
|
-
"append": True,
|
34
|
-
"template": f"""
|
35
|
-
请使用code_plan工具生成代码修改计划,必须严格遵守以下工具调用格式:
|
36
|
-
|
37
|
-
{ot("TOOL_CALL")}
|
38
|
-
want: 想要从执行结果中获取到的信息
|
39
|
-
name: code_plan
|
40
|
-
arguments:
|
41
|
-
requirement: "需要实现用户登录功能,包括用户名密码验证和JWT生成"
|
42
|
-
{ct("TOOL_CALL")}
|
43
|
-
|
44
|
-
请提供详细的需求描述和完整上下文信息:
|
45
|
-
|
46
|
-
必须包含的上下文信息:
|
47
|
-
1. 当前会话状态 - 当前正在处理的任务和进度
|
48
|
-
2. 用户历史请求 - 与本任务相关的历史请求
|
49
|
-
3. 系统状态 - 相关的系统配置和环境状态
|
50
|
-
4. 已确定的相关文件 - 已经分析出的需要修改的文件列表
|
51
|
-
|
52
|
-
示例:
|
53
|
-
1. 需要实现用户登录功能,包括用户名密码验证和JWT生成
|
54
|
-
2. 重构订单处理模块以提高性能
|
55
|
-
3. 优化数据库查询性能,减少响应时间
|
56
|
-
4. 添加支付网关集成功能
|
57
|
-
5. 修改用户权限管理系统
|
58
|
-
|
59
|
-
code_plan工具将:
|
60
|
-
1. 分析项目结构确定相关文件
|
61
|
-
2. 理解需求后制定详细修改步骤
|
62
|
-
3. 按功能模块分组修改内容
|
63
|
-
4. 评估修改影响范围
|
64
|
-
5. 生成可执行的开发计划
|
65
|
-
""",
|
66
|
-
"description": "生成代码修改计划",
|
67
|
-
},
|
32
|
+
|
68
33
|
"FindRelatedFiles": {
|
69
34
|
"append": False,
|
70
35
|
"template": f"""
|
jarvis/jarvis_utils/config.py
CHANGED
@@ -111,10 +111,20 @@ def get_shell_name() -> str:
|
|
111
111
|
获取系统shell名称。
|
112
112
|
|
113
113
|
返回:
|
114
|
-
str: Shell名称(例如bash, zsh),默认为bash
|
115
|
-
|
116
|
-
|
117
|
-
|
114
|
+
str: Shell名称(例如bash, zsh, fish),默认为bash
|
115
|
+
|
116
|
+
获取顺序:
|
117
|
+
1. 先从GLOBAL_CONFIG_DATA中获取JARVIS_SHELL配置
|
118
|
+
2. 再从GLOBAL_CONFIG_DATA中获取SHELL配置
|
119
|
+
3. 最后从环境变量SHELL获取
|
120
|
+
4. 如果都未配置,则默认返回bash
|
121
|
+
"""
|
122
|
+
shell_name = GLOBAL_CONFIG_DATA.get("JARVIS_SHELL")
|
123
|
+
if shell_name:
|
124
|
+
return shell_name.lower()
|
125
|
+
|
126
|
+
shell_path = GLOBAL_CONFIG_DATA.get("SHELL", os.getenv("SHELL", "/bin/bash"))
|
127
|
+
return os.path.basename(shell_path).lower()
|
118
128
|
|
119
129
|
|
120
130
|
def get_normal_platform_name() -> str:
|
jarvis/jarvis_utils/git_utils.py
CHANGED
@@ -12,9 +12,10 @@ Git工具模块
|
|
12
12
|
import os
|
13
13
|
import re
|
14
14
|
import subprocess
|
15
|
-
from typing import Any, Dict, List, Tuple
|
15
|
+
from typing import Any, Dict, List, Set, Tuple
|
16
16
|
|
17
|
-
from jarvis.jarvis_utils.config import get_auto_update,
|
17
|
+
from jarvis.jarvis_utils.config import (get_auto_update,
|
18
|
+
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
|
|
@@ -219,7 +220,6 @@ def handle_commit_workflow() -> bool:
|
|
219
220
|
import subprocess
|
220
221
|
|
221
222
|
try:
|
222
|
-
|
223
223
|
confirm_add_new_files()
|
224
224
|
|
225
225
|
if not has_uncommitted_changes():
|
@@ -411,7 +411,7 @@ def get_diff_file_list() -> List[str]:
|
|
411
411
|
"""
|
412
412
|
try:
|
413
413
|
confirm_add_new_files()
|
414
|
-
|
414
|
+
|
415
415
|
# 暂存新增文件
|
416
416
|
subprocess.run(["git", "add", "-N", "."], check=True)
|
417
417
|
|
@@ -488,14 +488,15 @@ def get_recent_commits_with_files() -> List[Dict[str, Any]]:
|
|
488
488
|
)
|
489
489
|
if files_result.returncode == 0:
|
490
490
|
file_lines = files_result.stdout.splitlines()
|
491
|
-
unique_files = set(filter(None, file_lines))
|
492
|
-
commit["files"] = list(unique_files)[:20] # 限制最多20个文件
|
491
|
+
unique_files: Set[str] = set(filter(None, file_lines))
|
492
|
+
commit["files"] = list(unique_files)[:20] # type: ignore[list-item] # 限制最多20个文件
|
493
493
|
|
494
494
|
return commits
|
495
495
|
|
496
496
|
except subprocess.CalledProcessError:
|
497
497
|
return []
|
498
498
|
|
499
|
+
|
499
500
|
def _get_new_files() -> List[str]:
|
500
501
|
"""获取新增文件列表"""
|
501
502
|
return subprocess.run(
|
@@ -505,8 +506,10 @@ def _get_new_files() -> List[str]:
|
|
505
506
|
check=True,
|
506
507
|
).stdout.splitlines()
|
507
508
|
|
509
|
+
|
508
510
|
def confirm_add_new_files() -> None:
|
509
511
|
"""确认新增文件、代码行数和二进制文件"""
|
512
|
+
|
510
513
|
def _get_added_lines() -> int:
|
511
514
|
"""获取新增代码行数"""
|
512
515
|
diff_stats = subprocess.run(
|
@@ -515,7 +518,7 @@ def confirm_add_new_files() -> None:
|
|
515
518
|
text=True,
|
516
519
|
check=True,
|
517
520
|
).stdout.splitlines()
|
518
|
-
|
521
|
+
|
519
522
|
added_lines = 0
|
520
523
|
for stat in diff_stats:
|
521
524
|
parts = stat.split()
|
@@ -531,55 +534,53 @@ def confirm_add_new_files() -> None:
|
|
531
534
|
binary_files = []
|
532
535
|
for file in files:
|
533
536
|
try:
|
534
|
-
with open(file,
|
535
|
-
if b
|
537
|
+
with open(file, "rb") as f:
|
538
|
+
if b"\x00" in f.read(1024):
|
536
539
|
binary_files.append(file)
|
537
540
|
except (IOError, PermissionError):
|
538
541
|
continue
|
539
542
|
return binary_files
|
540
543
|
|
541
|
-
def _check_conditions(
|
544
|
+
def _check_conditions(
|
545
|
+
new_files: List[str], added_lines: int, binary_files: List[str]
|
546
|
+
) -> bool:
|
542
547
|
"""检查各种条件并打印提示信息"""
|
543
548
|
need_confirm = False
|
544
|
-
|
549
|
+
output_lines = []
|
550
|
+
|
545
551
|
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)
|
552
|
+
output_lines.append(f"检测到{len(new_files)}个新增文件(选择N将重新检测)")
|
553
|
+
output_lines.append("新增文件列表:")
|
554
|
+
output_lines.extend(f" - {file}" for file in new_files)
|
553
555
|
need_confirm = True
|
554
|
-
|
556
|
+
|
555
557
|
if added_lines > 500:
|
556
|
-
|
557
|
-
f"检测到{added_lines}行新增代码(选择N将重新检测)",
|
558
|
-
OutputType.WARNING
|
559
|
-
)
|
558
|
+
output_lines.append(f"检测到{added_lines}行新增代码(选择N将重新检测)")
|
560
559
|
need_confirm = True
|
561
|
-
|
560
|
+
|
562
561
|
if binary_files:
|
562
|
+
output_lines.append(f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)")
|
563
|
+
output_lines.append("二进制文件列表:")
|
564
|
+
output_lines.extend(f" - {file}" for file in binary_files)
|
565
|
+
need_confirm = True
|
566
|
+
|
567
|
+
if output_lines:
|
563
568
|
PrettyOutput.print(
|
564
|
-
|
565
|
-
OutputType.WARNING
|
569
|
+
"\n".join(output_lines),
|
570
|
+
OutputType.WARNING if need_confirm else OutputType.INFO,
|
566
571
|
)
|
567
|
-
|
568
|
-
for file in binary_files:
|
569
|
-
PrettyOutput.print(f" - {file}", OutputType.INFO)
|
570
|
-
need_confirm = True
|
571
|
-
|
572
|
+
|
572
573
|
return need_confirm
|
573
574
|
|
574
575
|
while True:
|
575
576
|
new_files = _get_new_files()
|
576
577
|
added_lines = _get_added_lines()
|
577
578
|
binary_files = _get_binary_files(new_files)
|
578
|
-
|
579
|
+
|
579
580
|
if not _check_conditions(new_files, added_lines, binary_files):
|
580
581
|
break
|
581
|
-
|
582
|
+
|
582
583
|
if not user_confirm("是否要添加这些变更(如果不需要请修改.gitignore文件以忽略不需要的文件)?", False):
|
583
584
|
continue
|
584
|
-
|
585
|
-
break
|
585
|
+
|
586
|
+
break
|