jarvis-ai-assistant 0.1.132__py3-none-any.whl → 0.1.138__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 +1 -1
- jarvis/jarvis_agent/__init__.py +330 -347
- jarvis/jarvis_agent/builtin_input_handler.py +16 -6
- jarvis/jarvis_agent/file_input_handler.py +9 -9
- jarvis/jarvis_agent/jarvis.py +143 -0
- jarvis/jarvis_agent/main.py +12 -13
- jarvis/jarvis_agent/output_handler.py +3 -3
- jarvis/jarvis_agent/patch.py +92 -64
- jarvis/jarvis_agent/shell_input_handler.py +5 -3
- jarvis/jarvis_code_agent/code_agent.py +263 -177
- jarvis/jarvis_code_agent/file_select.py +24 -24
- jarvis/jarvis_dev/main.py +45 -59
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +179 -0
- jarvis/jarvis_git_squash/main.py +7 -7
- jarvis/jarvis_lsp/base.py +11 -53
- jarvis/jarvis_lsp/cpp.py +13 -28
- jarvis/jarvis_lsp/go.py +13 -28
- jarvis/jarvis_lsp/python.py +8 -27
- jarvis/jarvis_lsp/registry.py +21 -83
- jarvis/jarvis_lsp/rust.py +15 -30
- jarvis/jarvis_methodology/main.py +101 -0
- jarvis/jarvis_multi_agent/__init__.py +10 -51
- jarvis/jarvis_multi_agent/main.py +43 -0
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/ai8.py +67 -89
- jarvis/jarvis_platform/base.py +14 -13
- jarvis/jarvis_platform/kimi.py +25 -28
- jarvis/jarvis_platform/ollama.py +24 -26
- jarvis/jarvis_platform/openai.py +15 -19
- jarvis/jarvis_platform/oyi.py +48 -50
- jarvis/jarvis_platform/registry.py +29 -44
- jarvis/jarvis_platform/yuanbao.py +39 -43
- jarvis/jarvis_platform_manager/main.py +81 -81
- jarvis/jarvis_platform_manager/openai_test.py +21 -21
- jarvis/jarvis_rag/file_processors.py +18 -18
- jarvis/jarvis_rag/main.py +262 -278
- jarvis/jarvis_smart_shell/main.py +12 -12
- jarvis/jarvis_tools/ask_codebase.py +85 -78
- jarvis/jarvis_tools/ask_user.py +8 -8
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +9 -9
- jarvis/jarvis_tools/code_review.py +40 -21
- jarvis/jarvis_tools/create_code_agent.py +15 -15
- jarvis/jarvis_tools/create_sub_agent.py +0 -1
- jarvis/jarvis_tools/execute_python_script.py +3 -3
- jarvis/jarvis_tools/execute_shell.py +11 -11
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_analyzer.py +116 -105
- jarvis/jarvis_tools/file_operation.py +22 -20
- jarvis/jarvis_tools/find_caller.py +105 -40
- jarvis/jarvis_tools/find_methodolopy.py +65 -0
- jarvis/jarvis_tools/find_symbol.py +123 -39
- jarvis/jarvis_tools/function_analyzer.py +140 -57
- jarvis/jarvis_tools/git_commiter.py +10 -10
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
- jarvis/jarvis_tools/methodology.py +22 -67
- jarvis/jarvis_tools/project_analyzer.py +137 -53
- jarvis/jarvis_tools/rag.py +15 -20
- jarvis/jarvis_tools/read_code.py +25 -23
- jarvis/jarvis_tools/read_webpage.py +31 -31
- jarvis/jarvis_tools/registry.py +72 -52
- jarvis/jarvis_tools/search_web.py +23 -353
- jarvis/jarvis_tools/tool_generator.py +19 -19
- jarvis/jarvis_utils/config.py +36 -96
- jarvis/jarvis_utils/embedding.py +83 -83
- jarvis/jarvis_utils/git_utils.py +20 -20
- jarvis/jarvis_utils/globals.py +18 -6
- jarvis/jarvis_utils/input.py +10 -9
- jarvis/jarvis_utils/methodology.py +141 -140
- jarvis/jarvis_utils/output.py +13 -13
- jarvis/jarvis_utils/utils.py +23 -71
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +6 -15
- jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +4 -3
- jarvis/jarvis_tools/lsp_find_definition.py +0 -150
- jarvis/jarvis_tools/lsp_find_references.py +0 -127
- jarvis/jarvis_tools/select_code_files.py +0 -62
- jarvis_ai_assistant-0.1.132.dist-info/RECORD +0 -82
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
jarvis/jarvis_agent/patch.py
CHANGED
|
@@ -5,26 +5,30 @@ import os
|
|
|
5
5
|
from yaspin import yaspin
|
|
6
6
|
|
|
7
7
|
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
8
|
+
from jarvis.jarvis_platform.base import BasePlatform
|
|
8
9
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
9
10
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
10
11
|
from jarvis.jarvis_tools.file_operation import FileOperationTool
|
|
11
12
|
from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
|
|
12
13
|
from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
|
|
14
|
+
from jarvis.jarvis_utils.globals import add_read_file_record, has_read_file
|
|
13
15
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
14
16
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
15
17
|
from jarvis.jarvis_utils.utils import ct, ot, get_file_line_count, user_confirm
|
|
16
18
|
|
|
19
|
+
|
|
17
20
|
class PatchOutputHandler(OutputHandler):
|
|
18
21
|
def name(self) -> str:
|
|
19
22
|
return "PATCH"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
|
|
24
|
+
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
|
25
|
+
return False, apply_patch(response, agent)
|
|
26
|
+
|
|
23
27
|
def can_handle(self, response: str) -> bool:
|
|
24
28
|
if _parse_patch(response):
|
|
25
29
|
return True
|
|
26
30
|
return False
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
def prompt(self) -> str:
|
|
29
33
|
return f"""
|
|
30
34
|
# 代码补丁规范
|
|
@@ -43,18 +47,17 @@ Reason: [修改原因]
|
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
## 核心原则
|
|
46
|
-
1.
|
|
47
|
-
2.
|
|
48
|
-
3.
|
|
49
|
-
4. **格式严格保持**:
|
|
50
|
+
1. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
|
|
51
|
+
2. **最小补丁原则**:始终生成最小范围的补丁,只包含必要的上下文和实际修改
|
|
52
|
+
3. **格式严格保持**:
|
|
50
53
|
- 严格保持原始代码的缩进方式(空格或制表符)
|
|
51
54
|
- 保持原始代码的空行数量和位置
|
|
52
55
|
- 保持原始代码的行尾空格处理方式
|
|
53
56
|
- 不改变原始代码的换行风格
|
|
54
|
-
|
|
57
|
+
4. **新旧区分**:
|
|
55
58
|
- 对于新文件:提供完整的代码内容
|
|
56
|
-
-
|
|
57
|
-
|
|
59
|
+
- 对于现有文件:只提供修改部分,不要提供整个文件
|
|
60
|
+
5. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
|
|
58
61
|
|
|
59
62
|
## 格式兼容性要求
|
|
60
63
|
1. **缩进一致性**:
|
|
@@ -85,17 +88,21 @@ def add(a, b):
|
|
|
85
88
|
|
|
86
89
|
## 最佳实践
|
|
87
90
|
- 每个补丁专注于单一职责的修改
|
|
88
|
-
-
|
|
91
|
+
- 避免包含过多无关代码
|
|
89
92
|
- 确保修改理由清晰明确,便于理解变更目的
|
|
90
93
|
- 保持代码风格一致性,遵循项目现有的编码规范
|
|
91
94
|
- 在修改前仔细分析原代码的格式风格,确保补丁与之完全兼容
|
|
92
95
|
- 绝不提供完整文件内容,除非是新建文件
|
|
96
|
+
- 每个文件的修改是独立的,不能出现“参照xxx文件的修改”这样的描述
|
|
97
|
+
- 不要出现未实现的代码,如:TODO
|
|
93
98
|
"""
|
|
94
99
|
|
|
100
|
+
|
|
95
101
|
def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
96
102
|
"""解析新的上下文补丁格式"""
|
|
97
103
|
result = {}
|
|
98
|
-
patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?'+
|
|
104
|
+
patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?' +
|
|
105
|
+
ct("PATCH"), patch_str, re.DOTALL)
|
|
99
106
|
if patches:
|
|
100
107
|
for patch in patches:
|
|
101
108
|
first_line = patch.splitlines()[0]
|
|
@@ -110,7 +117,8 @@ def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
|
110
117
|
result[filepath] += "\n\n" + patch
|
|
111
118
|
return result
|
|
112
119
|
|
|
113
|
-
|
|
120
|
+
|
|
121
|
+
def apply_patch(output_str: str, agent: Any) -> str:
|
|
114
122
|
"""Apply patches to files"""
|
|
115
123
|
with yaspin(text="正在应用补丁...", color="cyan") as spinner:
|
|
116
124
|
try:
|
|
@@ -118,12 +126,17 @@ def apply_patch(output_str: str) -> str:
|
|
|
118
126
|
except Exception as e:
|
|
119
127
|
PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
|
|
120
128
|
return ""
|
|
121
|
-
|
|
129
|
+
|
|
122
130
|
# 获取当前提交hash作为起始点
|
|
123
|
-
spinner.text= "开始获取当前提交hash..."
|
|
131
|
+
spinner.text = "开始获取当前提交hash..."
|
|
124
132
|
start_hash = get_latest_commit_hash()
|
|
125
133
|
spinner.write("✅ 当前提交hash获取完成")
|
|
126
|
-
|
|
134
|
+
|
|
135
|
+
not_read_file = [f for f in patches.keys() if not has_read_file(f)]
|
|
136
|
+
if not_read_file:
|
|
137
|
+
yaspin.write(f"❌ 以下文件未读取: {not_read_file},应用补丁存在风险,将先读取文件后再生成补丁", color="red")
|
|
138
|
+
return f"以下文件未读取: {not_read_file},应用补丁存在风险,请先读取文件后再生成补丁"
|
|
139
|
+
|
|
127
140
|
# 按文件逐个处理
|
|
128
141
|
for filepath, patch_content in patches.items():
|
|
129
142
|
try:
|
|
@@ -134,6 +147,7 @@ def apply_patch(output_str: str) -> str:
|
|
|
134
147
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
135
148
|
open(filepath, 'w', encoding='utf-8').close()
|
|
136
149
|
spinner.write("✅ 文件创建完成")
|
|
150
|
+
add_read_file_record(filepath)
|
|
137
151
|
with spinner.hidden():
|
|
138
152
|
while not handle_code_operation(filepath, patch_content):
|
|
139
153
|
if user_confirm("补丁应用失败,是否重试?", default=True):
|
|
@@ -145,7 +159,7 @@ def apply_patch(output_str: str) -> str:
|
|
|
145
159
|
spinner.text = f"文件 {filepath} 处理失败: {str(e)}, 回滚文件"
|
|
146
160
|
revert_file(filepath) # 回滚单个文件
|
|
147
161
|
spinner.write(f"✅ 文件 {filepath} 回滚完成")
|
|
148
|
-
|
|
162
|
+
|
|
149
163
|
final_ret = ""
|
|
150
164
|
diff = get_diff()
|
|
151
165
|
if diff:
|
|
@@ -156,45 +170,50 @@ def apply_patch(output_str: str) -> str:
|
|
|
156
170
|
# 获取提交信息
|
|
157
171
|
end_hash = get_latest_commit_hash()
|
|
158
172
|
commits = get_commits_between(start_hash, end_hash)
|
|
159
|
-
|
|
173
|
+
|
|
160
174
|
# 添加提交信息到final_ret
|
|
161
175
|
if commits:
|
|
162
176
|
final_ret += "✅ 补丁已应用\n"
|
|
163
177
|
final_ret += "# 提交信息:\n"
|
|
164
178
|
for commit_hash, commit_message in commits:
|
|
165
179
|
final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
|
|
166
|
-
|
|
180
|
+
|
|
167
181
|
final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
|
|
168
|
-
|
|
182
|
+
|
|
169
183
|
# 增加代码变更分析和错误提示
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
|
|
185
|
+
addon_prompt = "1. 请调用静态检查工具(如有)检查以上变更是否引入了潜在错误\n"
|
|
186
|
+
addon_prompt += "2. 如果发现代码错误,请立即提出修复方案\n"
|
|
187
|
+
addon_prompt += "3. 如果错误并非是本次修改引入,要询问用户是否需要立即修复\n"
|
|
188
|
+
addon_prompt += "\n\n"
|
|
189
|
+
addon_prompt += "如果没有问题,请继续进行下一步修改\n"
|
|
190
|
+
addon_prompt += f"如果用户的需求已经完成,请终止,不要输出新的 {ot('PATCH')},不要实现任何超出用户需求外的内容\n"
|
|
191
|
+
addon_prompt += "如果有任何信息不清楚,调用工具获取信息\n"
|
|
192
|
+
addon_prompt += "每次响应必须且只能包含一个操作\n"
|
|
193
|
+
|
|
194
|
+
agent.set_addon_prompt(addon_prompt)
|
|
195
|
+
|
|
179
196
|
else:
|
|
180
197
|
final_ret += "✅ 补丁已应用(没有新的提交)"
|
|
181
198
|
else:
|
|
182
199
|
final_ret += "❌ 补丁应用被拒绝\n"
|
|
183
200
|
final_ret += f"# 补丁预览:\n```diff\n{diff}\n```"
|
|
184
201
|
else:
|
|
202
|
+
commited = False
|
|
185
203
|
final_ret += "❌ 没有要提交的更改\n"
|
|
186
204
|
# 用户确认最终结果
|
|
187
205
|
with spinner.hidden():
|
|
206
|
+
if commited:
|
|
207
|
+
return final_ret
|
|
188
208
|
PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
|
|
189
209
|
if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
|
|
190
210
|
return final_ret
|
|
191
211
|
custom_reply = get_multiline_input("请输入自定义回复")
|
|
192
212
|
if not custom_reply.strip(): # 如果自定义回复为空,返回空字符串
|
|
193
213
|
return ""
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return custom_reply
|
|
214
|
+
agent.set_addon_prompt(custom_reply)
|
|
215
|
+
return final_ret
|
|
216
|
+
|
|
198
217
|
|
|
199
218
|
def revert_file(filepath: str):
|
|
200
219
|
"""增强版git恢复,处理新文件"""
|
|
@@ -206,7 +225,8 @@ def revert_file(filepath: str):
|
|
|
206
225
|
stderr=subprocess.PIPE
|
|
207
226
|
)
|
|
208
227
|
if result.returncode == 0:
|
|
209
|
-
subprocess.run(['git', 'checkout', 'HEAD',
|
|
228
|
+
subprocess.run(['git', 'checkout', 'HEAD',
|
|
229
|
+
'--', filepath], check=True)
|
|
210
230
|
else:
|
|
211
231
|
if os.path.exists(filepath):
|
|
212
232
|
os.remove(filepath)
|
|
@@ -214,11 +234,15 @@ def revert_file(filepath: str):
|
|
|
214
234
|
except subprocess.CalledProcessError as e:
|
|
215
235
|
PrettyOutput.print(f"恢复文件失败: {str(e)}", OutputType.ERROR)
|
|
216
236
|
# 修改后的恢复函数
|
|
237
|
+
|
|
238
|
+
|
|
217
239
|
def revert_change():
|
|
218
240
|
import subprocess
|
|
219
241
|
subprocess.run(['git', 'reset', '--hard', 'HEAD'], check=True)
|
|
220
242
|
subprocess.run(['git', 'clean', '-fd'], check=True)
|
|
221
243
|
# 修改后的获取差异函数
|
|
244
|
+
|
|
245
|
+
|
|
222
246
|
def get_diff() -> str:
|
|
223
247
|
"""使用git获取暂存区差异"""
|
|
224
248
|
import subprocess
|
|
@@ -236,9 +260,10 @@ def get_diff() -> str:
|
|
|
236
260
|
except subprocess.CalledProcessError as e:
|
|
237
261
|
return f"获取差异失败: {str(e)}"
|
|
238
262
|
|
|
239
|
-
|
|
263
|
+
|
|
264
|
+
def handle_commit_workflow() -> bool:
|
|
240
265
|
"""Handle the git commit workflow and return the commit details.
|
|
241
|
-
|
|
266
|
+
|
|
242
267
|
Returns:
|
|
243
268
|
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
244
269
|
"""
|
|
@@ -252,13 +277,13 @@ def handle_commit_workflow()->bool:
|
|
|
252
277
|
|
|
253
278
|
def handle_code_operation(filepath: str, patch_content: str) -> bool:
|
|
254
279
|
"""处理代码操作"""
|
|
255
|
-
if get_file_line_count(filepath) <
|
|
280
|
+
if get_file_line_count(filepath) < 5:
|
|
256
281
|
return handle_small_code_operation(filepath, patch_content)
|
|
257
282
|
else:
|
|
258
283
|
retry_count = 5
|
|
259
284
|
while retry_count > 0:
|
|
260
285
|
retry_count -= 1
|
|
261
|
-
if handle_large_code_operation(filepath, patch_content):
|
|
286
|
+
if handle_large_code_operation(filepath, patch_content, PlatformRegistry().get_normal_platform() if retry_count > 2 else PlatformRegistry().get_thinking_platform()):
|
|
262
287
|
return True
|
|
263
288
|
return handle_small_code_operation(filepath, patch_content)
|
|
264
289
|
|
|
@@ -268,11 +293,12 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
268
293
|
with yaspin(text=f"正在修改文件 {filepath}...", color="cyan") as spinner:
|
|
269
294
|
try:
|
|
270
295
|
with spinner.hidden():
|
|
271
|
-
old_file_content = FileOperationTool().execute(
|
|
296
|
+
old_file_content = FileOperationTool().execute(
|
|
297
|
+
{"operation": "read", "files": [{"path": filepath}]})
|
|
272
298
|
if not old_file_content["success"]:
|
|
273
299
|
spinner.write("❌ 文件读取失败")
|
|
274
300
|
return False
|
|
275
|
-
|
|
301
|
+
|
|
276
302
|
prompt = f"""
|
|
277
303
|
# 代码合并专家指南
|
|
278
304
|
|
|
@@ -308,16 +334,17 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
308
334
|
[合并后的完整代码,包括所有空行和缩进]
|
|
309
335
|
{ct("MERGED_CODE")}
|
|
310
336
|
"""
|
|
311
|
-
model = PlatformRegistry().
|
|
312
|
-
model.set_suppress_output(
|
|
337
|
+
model = PlatformRegistry().get_normal_platform()
|
|
338
|
+
model.set_suppress_output(False)
|
|
313
339
|
count = 30
|
|
314
340
|
start_line = -1
|
|
315
341
|
end_line = -1
|
|
316
342
|
code = []
|
|
317
343
|
finished = False
|
|
318
|
-
while count>0:
|
|
344
|
+
while count > 0:
|
|
319
345
|
count -= 1
|
|
320
|
-
|
|
346
|
+
with spinner.hidden():
|
|
347
|
+
response = model.chat_until_success(prompt).splitlines()
|
|
321
348
|
try:
|
|
322
349
|
start_line = response.index(ot("MERGED_CODE")) + 1
|
|
323
350
|
try:
|
|
@@ -328,7 +355,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
328
355
|
except:
|
|
329
356
|
pass
|
|
330
357
|
|
|
331
|
-
try:
|
|
358
|
+
try:
|
|
332
359
|
response.index(ot("!!!FINISHED!!!"))
|
|
333
360
|
finished = True
|
|
334
361
|
break
|
|
@@ -365,20 +392,20 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
365
392
|
return False
|
|
366
393
|
|
|
367
394
|
|
|
368
|
-
def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
|
|
395
|
+
def handle_large_code_operation(filepath: str, patch_content: str, model: BasePlatform) -> bool:
|
|
369
396
|
"""处理大型代码文件的补丁操作,使用差异化补丁格式"""
|
|
370
397
|
with yaspin(text=f"正在处理文件 {filepath}...", color="cyan") as spinner:
|
|
371
398
|
try:
|
|
372
399
|
# 读取原始文件内容
|
|
373
|
-
old_file_content = FileOperationTool().execute(
|
|
400
|
+
old_file_content = FileOperationTool().execute(
|
|
401
|
+
{"operation": "read", "files": [{"path": filepath}]})
|
|
374
402
|
if not old_file_content["success"]:
|
|
375
403
|
spinner.text = "文件读取失败"
|
|
376
404
|
spinner.fail("❌")
|
|
377
405
|
return False
|
|
378
|
-
|
|
379
|
-
model
|
|
380
|
-
|
|
381
|
-
|
|
406
|
+
|
|
407
|
+
model.set_suppress_output(False)
|
|
408
|
+
|
|
382
409
|
prompt = f"""
|
|
383
410
|
# 代码补丁生成专家指南
|
|
384
411
|
|
|
@@ -431,20 +458,21 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
431
458
|
{ct("DIFF")}
|
|
432
459
|
"""
|
|
433
460
|
# 获取补丁内容
|
|
434
|
-
|
|
435
|
-
|
|
461
|
+
with spinner.hidden():
|
|
462
|
+
response = model.chat_until_success(prompt)
|
|
463
|
+
|
|
436
464
|
# 解析差异化补丁
|
|
437
|
-
diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
|
|
438
|
-
|
|
439
|
-
|
|
465
|
+
diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
|
|
466
|
+
response, re.DOTALL)
|
|
467
|
+
|
|
440
468
|
# 读取原始文件内容
|
|
441
469
|
with open(filepath, 'r', encoding='utf-8', errors="ignore") as f:
|
|
442
470
|
file_content = f.read()
|
|
443
|
-
|
|
471
|
+
|
|
444
472
|
# 应用所有差异化补丁
|
|
445
473
|
modified_content = file_content
|
|
446
474
|
patch_count = 0
|
|
447
|
-
|
|
475
|
+
|
|
448
476
|
for match in diff_blocks:
|
|
449
477
|
search_text = match.group(1).strip()
|
|
450
478
|
replace_text = match.group(2).strip()
|
|
@@ -457,23 +485,23 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
457
485
|
spinner.fail("❌")
|
|
458
486
|
return False
|
|
459
487
|
# 应用替换
|
|
460
|
-
modified_content = modified_content.replace(
|
|
488
|
+
modified_content = modified_content.replace(
|
|
489
|
+
search_text, replace_text)
|
|
461
490
|
spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
|
|
462
491
|
else:
|
|
463
492
|
spinner.text = f"补丁 #{patch_count} 应用失败:无法找到匹配的代码段"
|
|
464
493
|
spinner.fail("❌")
|
|
465
494
|
return False
|
|
466
|
-
|
|
495
|
+
|
|
467
496
|
# 写入修改后的内容
|
|
468
497
|
with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
|
|
469
498
|
f.write(modified_content)
|
|
470
|
-
|
|
499
|
+
|
|
471
500
|
spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
|
|
472
501
|
spinner.ok("✅")
|
|
473
502
|
return True
|
|
474
|
-
|
|
503
|
+
|
|
475
504
|
except Exception as e:
|
|
476
505
|
spinner.text = f"文件修改失败: {str(e)}"
|
|
477
506
|
spinner.fail("❌")
|
|
478
507
|
return False
|
|
479
|
-
|
|
@@ -21,7 +21,9 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
|
21
21
|
"arguments": {
|
|
22
22
|
"script_content": script
|
|
23
23
|
}
|
|
24
|
-
})
|
|
25
|
-
|
|
24
|
+
}, agent)
|
|
25
|
+
if user_confirm("是否将执行结果反馈给Agent?", default=True):
|
|
26
|
+
return f"{user_input}\n\n用户执行以下脚本:\n{script}\n\n执行结果:\n{output}", False
|
|
27
|
+
return "", True
|
|
26
28
|
return user_input, False
|
|
27
|
-
|
|
29
|
+
|