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

Files changed (66) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +18 -20
  3. jarvis/jarvis_code_agent/code_agent.py +195 -45
  4. jarvis/jarvis_code_agent/file_select.py +6 -19
  5. jarvis/jarvis_code_agent/patch.py +189 -310
  6. jarvis/jarvis_codebase/main.py +6 -2
  7. jarvis/jarvis_dev/main.py +6 -4
  8. jarvis/jarvis_git_squash/__init__.py +0 -0
  9. jarvis/jarvis_git_squash/main.py +81 -0
  10. jarvis/jarvis_lsp/cpp.py +1 -1
  11. jarvis/jarvis_lsp/go.py +1 -1
  12. jarvis/jarvis_lsp/registry.py +2 -2
  13. jarvis/jarvis_lsp/rust.py +1 -1
  14. jarvis/jarvis_multi_agent/__init__.py +1 -1
  15. jarvis/jarvis_platform/ai8.py +2 -1
  16. jarvis/jarvis_platform/base.py +19 -24
  17. jarvis/jarvis_platform/kimi.py +2 -3
  18. jarvis/jarvis_platform/ollama.py +3 -1
  19. jarvis/jarvis_platform/openai.py +1 -1
  20. jarvis/jarvis_platform/oyi.py +2 -1
  21. jarvis/jarvis_platform/registry.py +2 -1
  22. jarvis/jarvis_platform_manager/main.py +4 -6
  23. jarvis/jarvis_platform_manager/openai_test.py +0 -1
  24. jarvis/jarvis_rag/main.py +5 -2
  25. jarvis/jarvis_smart_shell/main.py +9 -4
  26. jarvis/jarvis_tools/ask_codebase.py +12 -7
  27. jarvis/jarvis_tools/ask_user.py +3 -2
  28. jarvis/jarvis_tools/base.py +21 -7
  29. jarvis/jarvis_tools/chdir.py +0 -1
  30. jarvis/jarvis_tools/code_review.py +13 -14
  31. jarvis/jarvis_tools/create_code_agent.py +2 -2
  32. jarvis/jarvis_tools/create_sub_agent.py +2 -2
  33. jarvis/jarvis_tools/execute_shell.py +3 -1
  34. jarvis/jarvis_tools/execute_shell_script.py +4 -4
  35. jarvis/jarvis_tools/file_operation.py +3 -2
  36. jarvis/jarvis_tools/git_commiter.py +5 -2
  37. jarvis/jarvis_tools/lsp_find_definition.py +1 -1
  38. jarvis/jarvis_tools/lsp_find_references.py +1 -1
  39. jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -11
  40. jarvis/jarvis_tools/lsp_get_document_symbols.py +1 -1
  41. jarvis/jarvis_tools/lsp_prepare_rename.py +1 -1
  42. jarvis/jarvis_tools/lsp_validate_edit.py +1 -1
  43. jarvis/jarvis_tools/methodology.py +4 -1
  44. jarvis/jarvis_tools/rag.py +22 -15
  45. jarvis/jarvis_tools/read_code.py +4 -3
  46. jarvis/jarvis_tools/read_webpage.py +2 -1
  47. jarvis/jarvis_tools/registry.py +4 -1
  48. jarvis/jarvis_tools/{search.py → search_web.py} +5 -2
  49. jarvis/jarvis_tools/select_code_files.py +1 -1
  50. jarvis/jarvis_utils/__init__.py +19 -982
  51. jarvis/jarvis_utils/config.py +138 -0
  52. jarvis/jarvis_utils/embedding.py +201 -0
  53. jarvis/jarvis_utils/git_utils.py +120 -0
  54. jarvis/jarvis_utils/globals.py +82 -0
  55. jarvis/jarvis_utils/input.py +161 -0
  56. jarvis/jarvis_utils/methodology.py +128 -0
  57. jarvis/jarvis_utils/output.py +235 -0
  58. jarvis/jarvis_utils/utils.py +150 -0
  59. jarvis_ai_assistant-0.1.125.dist-info/METADATA +291 -0
  60. jarvis_ai_assistant-0.1.125.dist-info/RECORD +75 -0
  61. {jarvis_ai_assistant-0.1.124.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/entry_points.txt +1 -0
  62. jarvis_ai_assistant-0.1.124.dist-info/METADATA +0 -460
  63. jarvis_ai_assistant-0.1.124.dist-info/RECORD +0 -65
  64. {jarvis_ai_assistant-0.1.124.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/LICENSE +0 -0
  65. {jarvis_ai_assistant-0.1.124.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/WHEEL +0 -0
  66. {jarvis_ai_assistant-0.1.124.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,21 @@
1
1
  import re
2
- from typing import Dict, Any, List, Tuple
2
+ from typing import Dict, Any, Tuple
3
3
  import os
4
+
4
5
  from jarvis.jarvis_agent.output_handler import OutputHandler
6
+ from jarvis.jarvis_platform.registry import PlatformRegistry
5
7
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
6
- from jarvis.jarvis_tools.read_code import ReadCodeTool
7
- from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, user_confirm
8
-
8
+ from jarvis.jarvis_tools.file_operation import FileOperationTool
9
+ from jarvis.jarvis_tools.execute_shell_script import ShellScriptTool
10
+ from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
11
+ from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
12
+ from jarvis.jarvis_utils.input import get_multiline_input
13
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
14
+ from jarvis.jarvis_utils.utils import user_confirm
9
15
 
10
16
  class PatchOutputHandler(OutputHandler):
11
17
  def name(self) -> str:
12
18
  return "PATCH"
13
-
14
19
  def handle(self, response: str) -> Tuple[bool, Any]:
15
20
  return False, apply_patch(response)
16
21
 
@@ -21,94 +26,51 @@ class PatchOutputHandler(OutputHandler):
21
26
 
22
27
  def prompt(self) -> str:
23
28
  return """
24
- # 🛠️ Code Patch Specification
25
-
26
- You can output multiple patches, each patch is a <PATCH> block.
29
+ # 🛠️ Contextual Code Patch Specification
30
+ Use <PATCH> blocks to specify code changes:
27
31
  --------------------------------
28
- # [OPERATION] on [FILE]
29
- # Start Line: [START_LINE], End Line: [END_LINE] [include/exclude], I can verify the line number range is correct
30
- # Reason: [CLEAR EXPLANATION]
31
32
  <PATCH>
32
- [FILE] [RANGE]
33
- [CONTENT]
33
+ File: [file_path]
34
+ Reason: [change_reason]
35
+ [contextual_code_snippet]
34
36
  </PATCH>
35
37
  --------------------------------
36
-
37
- Explain:
38
- - [OPERATION]: The operation to be performed, including:
39
- - INSERT: Insert code before the specified line, [RANGE] should be [m,m)
40
- - REPLACE: Replace code in the specified range, [RANGE] should be [m,n] n>=m
41
- - DELETE: Delete code in the specified range, [RANGE] should be [m,n] n>=m
42
- - NEW_FILE: Create a new file, [RANGE] should be [1,1)
43
- - [FILE]: The path of the file to be modified
44
- - [RANGE]: The range of the lines to be modified, [m,n] includes both m and n, [m,n) includes m but excludes n
45
- - [CONTENT]: The content of the code to be modified, if the operation is delete, the [CONTENT] is empty
46
-
47
- Critical Rules:
48
- - NEVER include unchanged code in patch content
49
- - ONLY show lines that are being modified/added
50
- - Maintain original line breaks around modified sections
51
- - Preserve surrounding comments unless explicitly modifying them
52
- - Verify line number range is correct
53
- - Verify indentation is correct
38
+ Rules:
39
+ 1. Code snippets must include sufficient context (3 lines before/after)
40
+ 2. I can see full code, so only show modified code sections
41
+ 3. Preserve original indentation and formatting
42
+ 4. For new files, provide complete code
43
+ 5. When modifying existing files, retain surrounding unchanged code
44
+ Example:
45
+ <PATCH>
46
+ File: src/utils/math.py
47
+ Reason: Fix zero division handling
48
+ def safe_divide(a, b):
49
+ # Add parameter validation
50
+ if b == 0:
51
+ raise ValueError("Divisor cannot be zero")
52
+ return a / b
53
+ # existing code ...
54
+ def add(a, b):
55
+ return a + b
56
+ </PATCH>
54
57
  """
55
58
 
56
-
57
- def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
58
- """解析补丁格式"""
59
+ def _parse_patch(patch_str: str) -> Dict[str, str]:
60
+ """解析新的上下文补丁格式"""
59
61
  result = {}
60
- # 更新正则表达式以更好地处理文件路径和范围
61
- header_pattern = re.compile(
62
- r'^\s*"?([^\n\r\[]+)"?\s*\[(\d+)(?:,(\d+))?([\]\)])\s*$', # 匹配文件路径和行号
63
- re.ASCII
64
- )
65
62
  patches = re.findall(r'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
66
-
67
- for patch in patches:
68
- parts = patch.split('\n', 1)
69
- if len(parts) < 1:
70
- continue
71
- header_line = parts[0].strip()
72
- content = parts[1] if len(parts) > 1 else ''
73
-
74
- if content and not content.endswith('\n'):
75
- content += '\n'
76
-
77
- # 解析文件路径和行号
78
- header_match = header_pattern.match(header_line)
79
- if not header_match:
80
- PrettyOutput.print(f"无法解析补丁头: {header_line}", OutputType.WARNING)
81
- continue
82
-
83
- filepath = header_match.group(1).strip()
84
-
85
- try:
86
- start = int(header_match.group(2)) # 保持1-based行号
87
- end = int(header_match.group(3)) if header_match.group(3) else start
88
- range_type = header_match.group(4) # ] 或 ) 表示范围类型
89
- except (ValueError, IndexError) as e:
90
- PrettyOutput.print(f"解析行号失败: {str(e)}", OutputType.WARNING)
91
- continue
92
-
93
- # 根据范围类型调整结束行号
94
- if range_type == ')': # 对于 [m,n) 格式,不包括第n行
95
- end = end
96
- else: # 对于 [m,n] 格式,包括第n行
97
- end = end + 1
98
-
99
- if filepath not in result:
100
- result[filepath] = []
101
- result[filepath].append({
102
- 'filepath': filepath,
103
- 'start': start,
104
- 'end': end,
105
- 'content': content
106
- })
107
- for filepath in result.keys():
108
- result[filepath] = sorted(result[filepath], key=lambda x: x['start'], reverse=True)
63
+ if patches:
64
+ for patch in patches:
65
+ first_line = patch.splitlines()[0]
66
+ sm = re.match(r'^File:\s*(.+)$', first_line)
67
+ if not sm:
68
+ PrettyOutput.print("无效的补丁格式", OutputType.WARNING)
69
+ continue
70
+ filepath = sm.group(1).strip()
71
+ result[filepath] = patch
109
72
  return result
110
73
 
111
-
112
74
  def apply_patch(output_str: str) -> str:
113
75
  """Apply patches to files"""
114
76
  try:
@@ -116,262 +78,179 @@ def apply_patch(output_str: str) -> str:
116
78
  except Exception as e:
117
79
  PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
118
80
  return ""
119
-
120
- ret = ""
121
81
 
122
- for filepath, patch_list in patches.items():
123
- for i, patch in enumerate(patch_list):
124
- try:
125
- err = handle_code_operation(filepath, patch)
126
- if err:
127
- PrettyOutput.print(err, OutputType.WARNING)
128
- revert_change()
129
- return err
130
- PrettyOutput.print(f"成功为文件{filepath}应用补丁{i+1}/{len(patch_list)}", OutputType.SUCCESS)
131
- except Exception as e:
132
- PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
82
+ # 获取当前提交hash作为起始点
83
+ start_hash = get_latest_commit_hash()
133
84
 
134
- if has_uncommitted_changes():
135
- diff = get_diff()
136
- if handle_commit_workflow(diff):
137
- ret += "Successfully applied the patch\n"
138
- # Get modified line ranges
139
- modified_ranges = get_modified_line_ranges()
140
- modified_code = ReadCodeTool().execute({"files": [{"path": filepath, "start_line": start, "end_line": end} for filepath, (start, end) in modified_ranges.items()]})
141
- if modified_code["success"]:
142
- ret += "New code:\n"
143
- ret += modified_code["stdout"]
85
+ # 按文件逐个处理
86
+ for filepath, patch_content in patches.items():
87
+ try:
88
+ handle_code_operation(filepath, patch_content)
89
+ PrettyOutput.print(f"文件 {filepath} 处理完成", OutputType.SUCCESS)
90
+ except Exception as e:
91
+ revert_file(filepath) # 回滚单个文件
92
+ PrettyOutput.print(f"文件 {filepath} 处理失败: {str(e)}", OutputType.ERROR)
93
+
94
+ final_ret = ""
95
+ diff = get_diff()
96
+ if diff:
97
+ PrettyOutput.print(diff, OutputType.CODE, lang="diff")
98
+ if handle_commit_workflow():
99
+ # 获取提交信息
100
+ end_hash = get_latest_commit_hash()
101
+ commits = get_commits_between(start_hash, end_hash)
102
+
103
+ # 添加提交信息到final_ret
104
+ if commits:
105
+ final_ret += "✅ The patches have been applied\n"
106
+ final_ret += "Commit History:\n"
107
+ for commit_hash, commit_message in commits:
108
+ final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
109
+ else:
110
+ final_ret += "✅ The patches have been applied (no new commits)"
144
111
  else:
145
- ret += "User rejected the patch\nThis is your patch preview:\n"
146
- ret += diff
147
- user_input = get_multiline_input("你可以继续输入(输入空行重试,Ctrl+C退出): ")
148
- if user_input:
149
- ret += "\n" + user_input
112
+ final_ret += " I don't want to commit the code"
113
+ else:
114
+ final_ret += " There are no changes to commit"
115
+ # 用户确认最终结果
116
+ PrettyOutput.print(final_ret, OutputType.USER)
117
+ if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
118
+ return final_ret
119
+ return get_multiline_input("请输入自定义回复")
120
+ def revert_file(filepath: str):
121
+ """增强版git恢复,处理新文件"""
122
+ import subprocess
123
+ try:
124
+ # 检查文件是否在版本控制中
125
+ result = subprocess.run(
126
+ ['git', 'ls-files', '--error-unmatch', filepath],
127
+ stderr=subprocess.PIPE
128
+ )
129
+ if result.returncode == 0:
130
+ subprocess.run(['git', 'checkout', 'HEAD', '--', filepath], check=True)
150
131
  else:
151
- ret = ""
152
-
153
- return ret # Ensure a string is always returned
154
-
132
+ if os.path.exists(filepath):
133
+ os.remove(filepath)
134
+ subprocess.run(['git', 'clean', '-f', '--', filepath], check=True)
135
+ except subprocess.CalledProcessError as e:
136
+ PrettyOutput.print(f"恢复文件失败: {str(e)}", OutputType.ERROR)
137
+ # 修改后的恢复函数
138
+ def revert_change():
139
+ import subprocess
140
+ subprocess.run(['git', 'reset', '--hard', 'HEAD'], check=True)
141
+ subprocess.run(['git', 'clean', '-fd'], check=True)
142
+ # 修改后的获取差异函数
155
143
  def get_diff() -> str:
156
- """使用更安全的subprocess代替os.system"""
144
+ """使用git获取暂存区差异"""
157
145
  import subprocess
158
146
  try:
159
147
  subprocess.run(['git', 'add', '.'], check=True)
160
148
  result = subprocess.run(
161
- ['git', 'diff', 'HEAD'],
149
+ ['git', 'diff', '--cached'],
162
150
  capture_output=True,
163
151
  text=True,
164
152
  check=True
165
153
  )
166
- return result.stdout
167
- finally:
168
- subprocess.run(['git', 'reset', 'HEAD'], check=True)
169
-
170
- def revert_change():
171
- import subprocess
172
- subprocess.run(['git', 'reset', 'HEAD'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
173
- subprocess.run(['git', 'checkout', '--', '.'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
174
- subprocess.run(['git', 'clean', '-fd'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
175
-
176
- def handle_commit_workflow(diff:str)->bool:
154
+ ret = result.stdout
155
+ subprocess.run(['git', "reset", "--soft", "HEAD"], check=True)
156
+ return ret
157
+ except subprocess.CalledProcessError as e:
158
+ return f"获取差异失败: {str(e)}"
159
+ def handle_commit_workflow()->bool:
177
160
  """Handle the git commit workflow and return the commit details.
178
161
 
179
162
  Returns:
180
163
  tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
181
164
  """
182
- if not user_confirm("是否要提交代码?", default=True):
165
+ if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
183
166
  revert_change()
184
167
  return False
185
-
186
168
  git_commiter = GitCommitTool()
187
169
  commit_result = git_commiter.execute({})
188
170
  return commit_result["success"]
189
171
 
190
- def get_modified_line_ranges() -> Dict[str, Tuple[int, int]]:
191
- """Get modified line ranges from git diff for all changed files.
192
-
193
- Returns:
194
- Dictionary mapping file paths to tuple with (start_line, end_line) ranges
195
- for modified sections. Line numbers are 1-based.
196
- """
197
- # Get git diff for all files
198
- diff_output = os.popen("git show").read()
199
-
200
- # Parse the diff to get modified files and their line ranges
201
- result = {}
202
- current_file = None
203
-
204
- for line in diff_output.splitlines():
205
- # Match lines like "+++ b/path/to/file"
206
- file_match = re.match(r"^\+\+\+ b/(.*)", line)
207
- if file_match:
208
- current_file = file_match.group(1)
209
- continue
210
-
211
- # Match lines like "@@ -100,5 +100,7 @@" where the + part shows new lines
212
- range_match = re.match(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@", line)
213
- if range_match and current_file:
214
- start_line = int(range_match.group(1)) # Keep as 1-based
215
- line_count = int(range_match.group(2)) if range_match.group(2) else 1
216
- end_line = start_line + line_count - 1
217
- result[current_file] = (start_line, end_line)
218
-
219
- return result
220
172
  # New handler functions below ▼▼▼
221
-
222
- def handle_new_file(filepath: str, patch: Dict[str, Any]):
223
- """统一参数格式处理新文件"""
224
- os.makedirs(os.path.dirname(filepath), exist_ok=True)
225
- with open(filepath, 'w', encoding='utf-8') as f:
226
- f.write(patch['content'])
227
-
228
- def handle_code_operation(filepath: str, patch: Dict[str, Any]) -> str:
229
- """处理紧凑格式补丁"""
173
+ def handle_code_operation(filepath: str, patch_content: str) -> str:
174
+ """处理基于上下文的代码片段"""
230
175
  try:
231
- # 新建文件时强制覆盖
232
- os.makedirs(os.path.dirname(filepath) or '.', exist_ok=True)
233
176
  if not os.path.exists(filepath):
177
+ # 新建文件
178
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
234
179
  open(filepath, 'w', encoding='utf-8').close()
235
- with open(filepath, 'r+', encoding='utf-8') as f:
236
- lines = f.readlines()
237
-
238
- new_lines = validate_and_apply_changes(
239
- lines,
240
- patch['start'],
241
- patch['end'],
242
- patch['content']
243
- )
244
-
245
- f.seek(0)
246
- f.writelines(new_lines)
247
- f.truncate()
248
- PrettyOutput.print(f"成功更新 {filepath}", OutputType.SUCCESS)
249
- return ""
250
- except Exception as e:
251
- error_msg = f"Failed to handle code operation: {str(e)}"
252
- PrettyOutput.print(error_msg, OutputType.ERROR)
253
- return error_msg
254
- def validate_and_apply_changes(
255
- lines: List[str],
256
- start: int,
257
- end: int,
258
- content: str
259
- ) -> List[str]:
260
- new_content = content.splitlines(keepends=True)
261
-
262
- # 插入操作处理
263
- if start == end:
264
- if start < 1 or start > len(lines)+1:
265
- raise ValueError(f"无效插入位置: {start}")
266
- return lines[:start-1] + new_content + lines[start-1:]
267
-
268
- # 范围替换/删除操作
269
- if start > end:
270
- raise ValueError(f"起始行{start}不能大于结束行{end}")
271
-
272
- max_line = len(lines)
273
- # 自动修正行号范围
274
- start = max(1, min(start, max_line+1))
275
- end = max(start, min(end, max_line+1))
276
-
277
- # 执行替换
278
- return lines[:start-1] + new_content + lines[end-1:]
279
-
280
-
281
- def file_input_handler(user_input: str, agent: Any) -> str:
282
- """Handle file input with optional line ranges.
283
-
284
- Args:
285
- user_input: User input string containing file references
286
- agent: Agent instance (unused in current implementation)
180
+ old_file_content = FileOperationTool().execute({"operation": "read", "files": [{"path": filepath}]})
181
+ if not old_file_content["success"]:
182
+ return f"文件读取失败: {old_file_content['stderr']}"
287
183
 
288
- Returns:
289
- str: Prompt with file contents prepended if files are found
290
- """
291
- prompt = user_input
292
- files = []
293
-
294
- file_refs = re.findall(r"'([^']+)'", user_input)
295
- for ref in file_refs:
296
- # Handle file:start,end or file:start:end format
297
- if ':' in ref:
298
- file_path, line_range = ref.split(':', 1)
299
- # Initialize with default values
300
- start_line = 1 # 1-based
301
- end_line = -1
302
-
303
- # Process line range if specified
304
- if ',' in line_range or ':' in line_range:
305
- try:
306
- raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
307
-
308
- # Handle special values and Python-style negative indices
309
- try:
310
- with open(file_path, 'r', encoding='utf-8') as f:
311
- total_lines = len(f.readlines())
312
- except FileNotFoundError:
313
- PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
314
- continue
315
- # Process start line
316
- if raw_start == 0: # 0表示整个文件
317
- start_line = 1
318
- end_line = total_lines
319
- else:
320
- start_line = raw_start if raw_start > 0 else total_lines + raw_start + 1
321
-
322
- # Process end line
323
- if raw_end == 0: # 0表示整个文件(如果start也是0)
324
- end_line = total_lines
325
- else:
326
- end_line = raw_end if raw_end > 0 else total_lines + raw_end + 1
327
-
328
- # Auto-correct ranges
329
- start_line = max(1, min(start_line, total_lines))
330
- end_line = max(start_line, min(end_line, total_lines))
331
-
332
- # Final validation
333
- if start_line < 1 or end_line > total_lines or start_line > end_line:
334
- raise ValueError
184
+ prompt = f"""
185
+ You are a code reviewer, please review the following code and merge the code with the context.
186
+ Original Code:
187
+ {old_file_content["stdout"]}
188
+ Patch:
189
+ {patch_content}
190
+ """
191
+ prompt += f"""
192
+ Please merge the code with the context and return the fully merged code.
335
193
 
336
- except:
337
- continue
338
-
339
- # Add file if it exists
340
- if os.path.isfile(file_path):
341
- files.append({
342
- "path": file_path,
343
- "start_line": start_line,
344
- "end_line": end_line
345
- })
346
- else:
347
- # Handle simple file path
348
- if os.path.isfile(ref):
349
- files.append({
350
- "path": ref,
351
- "start_line": 1, # 1-based
352
- "end_line": -1
353
- })
354
-
355
- # Read and process files if any were found
356
- if files:
357
- result = ReadCodeTool().execute({"files": files})
358
- if result["success"]:
359
- return result["stdout"] + "\n" + prompt
360
-
361
- return prompt + """
362
- ==================================================================
363
- Patch Line Number Range Rules:
364
- - INSERT: [m,m)
365
- - REPLACE: [m,n] n>=m
366
- - DELETE: [m,n] n>=m
367
- - NEW_FILE: [1,1)
194
+ Requirements:
195
+ 1. Strictly preserve original code formatting and indentation
196
+ 2. Only include actual code content in <MERGED_CODE> block
197
+ 3. Absolutely NO markdown code blocks (```) or backticks
198
+ 4. Maintain exact line numbers from original code except for changes
368
199
 
369
- Critical Rules:
370
- - NEVER include unchanged code in patch content
371
- - ONLY show lines that are being modified/added
372
- - Maintain original line breaks around modified sections
373
- - Preserve surrounding comments unless explicitly modifying them
374
- - Verify line number range is correct
375
- - Verify indentation is correct
376
- ==================================================================
200
+ Output Format:
201
+ <MERGED_CODE>
202
+ [merged_code]
203
+ </MERGED_CODE>
377
204
  """
205
+ model = PlatformRegistry().get_codegen_platform()
206
+ model.set_suppress_output(False)
207
+ count = 5
208
+ start_line = -1
209
+ end_line = -1
210
+ response = []
211
+ while count > 0:
212
+ count -= 1
213
+ response.extend(model.chat_until_success(prompt).splitlines())
214
+ try:
215
+ start_line = response.index("<MERGED_CODE>") + 1
216
+ except:
217
+ pass
218
+ try:
219
+ end_line = response.index("</MERGED_CODE>")
220
+ except:
221
+ pass
222
+ if start_line == -1:
223
+ PrettyOutput.print(f"❌ 为文件 {filepath} 应用补丁失败", OutputType.WARNING)
224
+ return f"代码合并失败"
225
+ if end_line == -1:
226
+ last_line = response[-1]
227
+ prompt = f"""
228
+ continue with the last line:
229
+ {last_line}
230
+ """
231
+ response.pop() # 删除最后一行
232
+ continue
233
+ if end_line < start_line:
234
+ PrettyOutput.print(f"❌ 为文件 {filepath} 应用补丁失败", OutputType.WARNING)
235
+ return f"代码合并失败"
236
+ break
237
+ # 写入合并后的代码
238
+ with open(filepath, 'w', encoding='utf-8') as f:
239
+ f.write("\n".join(response[start_line:end_line]))
240
+ PrettyOutput.print(f"✅ 为文件 {filepath} 应用补丁成功", OutputType.SUCCESS)
241
+ return ""
242
+ except Exception as e:
243
+ return f"文件操作失败: {str(e)}"
244
+ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
245
+ lines = user_input.splitlines()
246
+ cmdline = [line for line in lines if line.startswith("!")]
247
+ if len(cmdline) == 0:
248
+ return user_input, False
249
+ else:
250
+ script = '\n'.join([c[1:] for c in cmdline])
251
+ PrettyOutput.print(script, OutputType.CODE, lang="bash")
252
+ if user_confirm(f"是否要执行以上shell脚本?", default=True):
253
+ ShellScriptTool().execute({"script_content": script})
254
+ return "", True
255
+ return user_input, False
256
+
@@ -7,14 +7,18 @@ from typing import List, Tuple, Optional, Dict
7
7
  from jarvis.jarvis_platform.registry import PlatformRegistry
8
8
  import concurrent.futures
9
9
  from concurrent.futures import ThreadPoolExecutor
10
- from jarvis.jarvis_utils import OutputType, PrettyOutput, find_git_root, get_context_token_count, get_embedding, get_file_md5, get_max_token_count, get_thread_count, load_embedding_model, user_confirm
11
- from jarvis.jarvis_utils import init_env
12
10
  import argparse
13
11
  import pickle
14
12
  import lzma # 添加 lzma 导入
15
13
  from tqdm import tqdm
16
14
  import re
17
15
 
16
+ from jarvis.jarvis_utils.config import get_max_token_count, get_thread_count
17
+ from jarvis.jarvis_utils.embedding import get_embedding, load_embedding_model, get_context_token_count
18
+ from jarvis.jarvis_utils.git_utils import find_git_root
19
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
20
+ from jarvis.jarvis_utils.utils import get_file_md5, init_env, user_confirm
21
+
18
22
  class CodeBase:
19
23
  def __init__(self, root_dir: str):
20
24
  init_env()
jarvis/jarvis_dev/main.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from jarvis.jarvis_platform.registry import PlatformRegistry
2
2
  from jarvis.jarvis_multi_agent import MultiAgent, AgentConfig
3
3
  from jarvis.jarvis_tools.registry import ToolRegistry
4
- from jarvis.jarvis_utils import get_multiline_input, init_env
4
+ from jarvis.jarvis_utils.input import get_multiline_input
5
+ from jarvis.jarvis_utils.utils import init_env
6
+
5
7
 
6
8
  # Define system prompts for each role
7
9
  PM_PROMPT = """
@@ -830,13 +832,13 @@ def create_dev_team() -> MultiAgent:
830
832
  """Create a development team with multiple agents."""
831
833
 
832
834
  PM_output_handler = ToolRegistry()
833
- PM_output_handler.use_tools(["ask_user", "file_operation", "search", "rag", "execute_shell"])
835
+ PM_output_handler.use_tools(["ask_user", "file_operation", "search_web", "rag", "execute_shell"])
834
836
 
835
837
  BA_output_handler = ToolRegistry()
836
- BA_output_handler.use_tools(["ask_user", "file_operation", "search", "rag", "execute_shell"])
838
+ BA_output_handler.use_tools(["ask_user", "file_operation", "search_web", "rag", "execute_shell"])
837
839
 
838
840
  SA_output_handler = ToolRegistry()
839
- SA_output_handler.use_tools(["read_code", "file_operation", "search", "rag", "ask_codebase", "lsp_get_document_symbols", "execute_shell"])
841
+ SA_output_handler.use_tools(["read_code", "file_operation", "search_web", "rag", "ask_codebase", "lsp_get_document_symbols", "execute_shell"])
840
842
 
841
843
  TL_output_handler = ToolRegistry()
842
844
  TL_output_handler.use_tools(["read_code", "file_operation", "ask_codebase", "lsp_get_diagnostics", "lsp_find_references", "lsp_find_definition", "execute_shell"])
File without changes