jarvis-ai-assistant 0.1.96__py3-none-any.whl → 0.1.98__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 (41) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/agent.py +138 -144
  3. jarvis/jarvis_codebase/main.py +87 -54
  4. jarvis/jarvis_coder/git_utils.py +22 -25
  5. jarvis/jarvis_coder/main.py +166 -171
  6. jarvis/jarvis_coder/patch_handler.py +153 -453
  7. jarvis/jarvis_coder/plan_generator.py +76 -48
  8. jarvis/jarvis_platform/main.py +39 -39
  9. jarvis/jarvis_rag/main.py +182 -182
  10. jarvis/jarvis_smart_shell/main.py +34 -34
  11. jarvis/main.py +24 -24
  12. jarvis/models/ai8.py +22 -22
  13. jarvis/models/base.py +17 -13
  14. jarvis/models/kimi.py +31 -31
  15. jarvis/models/ollama.py +28 -28
  16. jarvis/models/openai.py +22 -24
  17. jarvis/models/oyi.py +25 -25
  18. jarvis/models/registry.py +33 -34
  19. jarvis/tools/ask_user.py +5 -5
  20. jarvis/tools/base.py +2 -2
  21. jarvis/tools/chdir.py +9 -9
  22. jarvis/tools/codebase_qa.py +4 -4
  23. jarvis/tools/coder.py +4 -4
  24. jarvis/tools/file_ops.py +1 -1
  25. jarvis/tools/generator.py +23 -23
  26. jarvis/tools/methodology.py +4 -4
  27. jarvis/tools/rag.py +4 -4
  28. jarvis/tools/registry.py +38 -38
  29. jarvis/tools/search.py +42 -42
  30. jarvis/tools/shell.py +13 -13
  31. jarvis/tools/sub_agent.py +16 -16
  32. jarvis/tools/thinker.py +41 -41
  33. jarvis/tools/webpage.py +17 -17
  34. jarvis/utils.py +59 -60
  35. {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.98.dist-info}/METADATA +1 -1
  36. jarvis_ai_assistant-0.1.98.dist-info/RECORD +47 -0
  37. jarvis_ai_assistant-0.1.96.dist-info/RECORD +0 -47
  38. {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.98.dist-info}/LICENSE +0 -0
  39. {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.98.dist-info}/WHEEL +0 -0
  40. {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.98.dist-info}/entry_points.txt +0 -0
  41. {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.98.dist-info}/top_level.txt +0 -0
@@ -20,14 +20,14 @@ index_lock = threading.Lock()
20
20
 
21
21
  class JarvisCoder:
22
22
  def __init__(self, root_dir: str, language: Optional[str] = "python"):
23
- """初始化代码修改工具"""
23
+ """Initialize code modification tool"""
24
24
  self.root_dir = root_dir
25
25
  self.language = language
26
26
  self._init_directories()
27
27
  self._init_codebase()
28
28
 
29
29
  def _init_directories(self):
30
- """初始化目录"""
30
+ """Initialize directories"""
31
31
  self.max_context_length = get_max_context_length()
32
32
 
33
33
  root_dir = find_git_root(self.root_dir)
@@ -36,9 +36,9 @@ class JarvisCoder:
36
36
 
37
37
  self.root_dir = root_dir
38
38
 
39
- PrettyOutput.print(f"Git根目录: {self.root_dir}", OutputType.INFO)
39
+ PrettyOutput.print(f"Git root directory: {self.root_dir}", OutputType.INFO)
40
40
 
41
- # 1. 判断代码库路径是否存在,如果不存在,创建
41
+ # 1. Check if the code repository path exists, if it does not exist, create it
42
42
  if not os.path.exists(self.root_dir):
43
43
  PrettyOutput.print(
44
44
  "Root directory does not exist, creating...", OutputType.INFO)
@@ -46,7 +46,7 @@ class JarvisCoder:
46
46
 
47
47
  os.chdir(self.root_dir)
48
48
 
49
- # 2. 创建 .jarvis-coder 目录
49
+ # 2. Create .jarvis-coder directory
50
50
  self.jarvis_dir = os.path.join(self.root_dir, ".jarvis-coder")
51
51
  if not os.path.exists(self.jarvis_dir):
52
52
  os.makedirs(self.jarvis_dir)
@@ -55,158 +55,157 @@ class JarvisCoder:
55
55
  if not os.path.exists(self.record_dir):
56
56
  os.makedirs(self.record_dir)
57
57
 
58
- # 3. 处理 .gitignore 文件
58
+ # 3. Process .gitignore file
59
59
  gitignore_path = os.path.join(self.root_dir, ".gitignore")
60
60
  gitignore_modified = False
61
61
  jarvis_ignore_pattern = ".jarvis-*"
62
62
 
63
- # 3.1 如果 .gitignore 不存在,创建它
63
+ # 3.1 If .gitignore does not exist, create it
64
64
  if not os.path.exists(gitignore_path):
65
- PrettyOutput.print("创建 .gitignore 文件", OutputType.INFO)
65
+ PrettyOutput.print("Create .gitignore file", OutputType.INFO)
66
66
  with open(gitignore_path, "w", encoding="utf-8") as f:
67
67
  f.write(f"{jarvis_ignore_pattern}\n")
68
68
  gitignore_modified = True
69
69
  else:
70
- # 3.2 检查是否已经包含 .jarvis-* 模式
70
+ # 3.2 Check if it already contains the .jarvis-* pattern
71
71
  with open(gitignore_path, "r", encoding="utf-8") as f:
72
72
  content = f.read()
73
73
 
74
- # 检查是否需要添加 .jarvis-* 模式
74
+ # 3.2 Check if it already contains the .jarvis-* pattern
75
75
  if jarvis_ignore_pattern not in content.split("\n"):
76
- PrettyOutput.print(" .jarvis-* 添加到 .gitignore", OutputType.INFO)
76
+ PrettyOutput.print("Add .jarvis-* to .gitignore", OutputType.INFO)
77
77
  with open(gitignore_path, "a", encoding="utf-8") as f:
78
- # 确保文件以换行符结尾
78
+ # Ensure the file ends with a newline
79
79
  if not content.endswith("\n"):
80
80
  f.write("\n")
81
81
  f.write(f"{jarvis_ignore_pattern}\n")
82
82
  gitignore_modified = True
83
83
 
84
- # 4. 判断代码库是否是git仓库,如果不是,初始化git仓库
84
+ # 4. Check if the code repository is a git repository, if not, initialize the git repository
85
85
  if not os.path.exists(os.path.join(self.root_dir, ".git")):
86
- PrettyOutput.print("初始化 Git 仓库", OutputType.INFO)
86
+ PrettyOutput.print("Initialize Git repository", OutputType.INFO)
87
87
  os.system("git init")
88
88
  os.system("git add .")
89
89
  os.system("git commit -m 'Initial commit'")
90
- # 5. 如果修改了 .gitignore,提交更改
90
+ # 5. If .gitignore is modified, commit the changes
91
91
  elif gitignore_modified:
92
- PrettyOutput.print("提交 .gitignore 更改", OutputType.INFO)
92
+ PrettyOutput.print("Commit .gitignore changes", OutputType.INFO)
93
93
  os.system("git add .gitignore")
94
94
  os.system("git commit -m 'chore: update .gitignore to exclude .jarvis-* files'")
95
- # 6. 查看代码库是否有未提交的文件,如果有,提交一次
95
+ # 6. Check if there are uncommitted files in the code repository, if there are, commit once
96
96
  elif self._has_uncommitted_files():
97
- PrettyOutput.print("提交未保存的更改", OutputType.INFO)
97
+ PrettyOutput.print("Commit uncommitted changes", OutputType.INFO)
98
98
  os.system("git add .")
99
99
  git_diff = os.popen("git diff --cached").read()
100
- commit_message = generate_commit_message(git_diff, "Pre-edit commit")
100
+ commit_message = generate_commit_message(git_diff)
101
101
  os.system(f"git commit -m '{commit_message}'")
102
102
 
103
103
  def _init_codebase(self):
104
- """初始化代码库"""
104
+ """Initialize codebase"""
105
105
  self._codebase = CodeBase(self.root_dir)
106
106
 
107
107
  def _has_uncommitted_files(self) -> bool:
108
- """判断代码库是否有未提交的文件"""
109
- # 获取未暂存的修改
108
+ """Check if there are uncommitted files in the code repository"""
109
+ # Get unstaged modifications
110
110
  unstaged = os.popen("git diff --name-only").read()
111
- # 获取已暂存但未提交的修改
111
+ # Get staged but uncommitted modifications
112
112
  staged = os.popen("git diff --cached --name-only").read()
113
- # 获取未跟踪的文件
113
+ # Get untracked files
114
114
  untracked = os.popen("git ls-files --others --exclude-standard").read()
115
115
 
116
116
  return bool(unstaged or staged or untracked)
117
117
 
118
118
  def _prepare_execution(self) -> None:
119
- """准备执行环境"""
119
+ """Prepare execution environment"""
120
120
  self._codebase.generate_codebase()
121
121
 
122
122
 
123
123
  def _load_related_files(self, feature: str) -> List[Dict]:
124
- """加载相关文件内容"""
124
+ """Load related file content"""
125
125
  ret = []
126
- # 确保索引数据库已生成
126
+ # Ensure the index database is generated
127
127
  if not self._codebase.is_index_generated():
128
- PrettyOutput.print("检测到索引数据库未生成,正在生成...", OutputType.WARNING)
128
+ PrettyOutput.print("Index database not generated, generating...", OutputType.WARNING)
129
129
  self._codebase.generate_codebase()
130
130
 
131
131
  related_files = self._codebase.search_similar(feature)
132
- for file, score, _ in related_files:
133
- PrettyOutput.print(f"相关文件: {file} 相关度: {score:.3f}", OutputType.SUCCESS)
134
- with open(file, "r", encoding="utf-8") as f:
135
- content = f.read()
132
+ for file, score in related_files:
133
+ PrettyOutput.print(f"Related file: {file} (score: {score:.3f})", OutputType.SUCCESS)
134
+ content = open(file, "r", encoding="utf-8").read()
136
135
  ret.append({"file_path": file, "file_content": content})
137
136
  return ret
138
137
 
139
138
  def _parse_file_selection(self, input_str: str, max_index: int) -> List[int]:
140
- """解析文件选择表达式
139
+ """Parse file selection expression
141
140
 
142
- 支持的格式:
143
- - 单个数字: "1"
144
- - 逗号分隔: "1,3,5"
145
- - 范围: "1-5"
146
- - 组合: "1,3-5,7"
141
+ Supported formats:
142
+ - Single number: "1"
143
+ - Comma-separated: "1,3,5"
144
+ - Range: "1-5"
145
+ - Combination: "1,3-5,7"
147
146
 
148
147
  Args:
149
- input_str: 用户输入的选择表达式
150
- max_index: 最大可选择的索引
148
+ input_str: User input selection expression
149
+ max_index: Maximum selectable index
151
150
 
152
151
  Returns:
153
- List[int]: 选中的索引列表(从0开始)
152
+ List[int]: Selected index list (starting from 0)
154
153
  """
155
154
  selected = set()
156
155
 
157
- # 移除所有空白字符
156
+ # Remove all whitespace characters
158
157
  input_str = "".join(input_str.split())
159
158
 
160
- # 处理逗号分隔的部分
159
+ # Process comma-separated parts
161
160
  for part in input_str.split(","):
162
161
  if not part:
163
162
  continue
164
163
 
165
- # 处理范围(例如:3-6
164
+ # Process range (e.g.: 3-6)
166
165
  if "-" in part:
167
166
  try:
168
167
  start, end = map(int, part.split("-"))
169
- # 转换为从0开始的索引
168
+ # Convert to index starting from 0
170
169
  start = max(0, start - 1)
171
170
  end = min(max_index, end - 1)
172
171
  if start <= end:
173
172
  selected.update(range(start, end + 1))
174
173
  except ValueError:
175
- PrettyOutput.print(f"忽略无效的范围表达式: {part}", OutputType.WARNING)
176
- # 处理单个数字
174
+ PrettyOutput.print(f"Ignore invalid range expression: {part}", OutputType.WARNING)
175
+ # Process single number
177
176
  else:
178
177
  try:
179
- index = int(part) - 1 # 转换为从0开始的索引
178
+ index = int(part) - 1 # Convert to index starting from 0
180
179
  if 0 <= index < max_index:
181
180
  selected.add(index)
182
181
  else:
183
- PrettyOutput.print(f"忽略超出范围的索引: {part}", OutputType.WARNING)
182
+ PrettyOutput.print(f"Ignore index out of range: {part}", OutputType.WARNING)
184
183
  except ValueError:
185
- PrettyOutput.print(f"忽略无效的数字: {part}", OutputType.WARNING)
184
+ PrettyOutput.print(f"Ignore invalid number: {part}", OutputType.WARNING)
186
185
 
187
186
  return sorted(list(selected))
188
187
 
189
188
  def _get_file_completer(self) -> Completer:
190
- """创建文件路径补全器"""
189
+ """Create file path completer"""
191
190
  class FileCompleter(Completer):
192
191
  def __init__(self, root_dir: str):
193
192
  self.root_dir = root_dir
194
193
 
195
194
  def get_completions(self, document, complete_event):
196
- # 获取当前输入的文本
195
+ # Get the text of the current input
197
196
  text = document.text_before_cursor
198
197
 
199
- # 如果输入为空,返回根目录下的所有文件
198
+ # If the input is empty, return all files in the root directory
200
199
  if not text:
201
200
  for path in self._list_files(""):
202
201
  yield Completion(path, start_position=0)
203
202
  return
204
203
 
205
- # 获取当前目录和部分文件名
204
+ # Get the current directory and partial file name
206
205
  current_dir = os.path.dirname(text)
207
206
  file_prefix = os.path.basename(text)
208
207
 
209
- # 列出匹配的文件
208
+ # List matching files
210
209
  search_dir = os.path.join(self.root_dir, current_dir) if current_dir else self.root_dir
211
210
  if os.path.isdir(search_dir):
212
211
  for path in self._list_files(current_dir):
@@ -214,7 +213,7 @@ class JarvisCoder:
214
213
  yield Completion(path, start_position=-len(text))
215
214
 
216
215
  def _list_files(self, current_dir: str) -> List[str]:
217
- """列出指定目录下的所有文件(递归)"""
216
+ """List all files in the specified directory (recursively)"""
218
217
  files = []
219
218
  search_dir = os.path.join(self.root_dir, current_dir)
220
219
 
@@ -222,7 +221,7 @@ class JarvisCoder:
222
221
  for filename in filenames:
223
222
  full_path = os.path.join(root, filename)
224
223
  rel_path = os.path.relpath(full_path, self.root_dir)
225
- # 忽略 .git 目录和其他隐藏文件
224
+ # Ignore .git directory and other hidden files
226
225
  if not any(part.startswith('.') for part in rel_path.split(os.sep)):
227
226
  files.append(rel_path)
228
227
 
@@ -231,13 +230,13 @@ class JarvisCoder:
231
230
  return FileCompleter(self.root_dir)
232
231
 
233
232
  def _fuzzy_match_files(self, pattern: str) -> List[str]:
234
- """模糊匹配文件路径
233
+ """Fuzzy match file path
235
234
 
236
235
  Args:
237
- pattern: 匹配模式
236
+ pattern: Matching pattern
238
237
 
239
238
  Returns:
240
- List[str]: 匹配的文件路径列表
239
+ List[str]: List of matching file paths
241
240
  """
242
241
  matches = []
243
242
 
@@ -259,38 +258,38 @@ class JarvisCoder:
259
258
  return sorted(matches)
260
259
 
261
260
  def _select_files(self, related_files: List[Dict]) -> List[Dict]:
262
- """让用户选择和补充相关文件"""
263
- PrettyOutput.section("相关文件", OutputType.INFO)
261
+ """Let the user select and supplement related files"""
262
+ PrettyOutput.section("Related files", OutputType.INFO)
264
263
 
265
- # 显示找到的文件
266
- selected_files = list(related_files) # 默认全选
264
+ # Display found files
265
+ selected_files = list(related_files) # Default select all
267
266
  for i, file in enumerate(related_files, 1):
268
267
  PrettyOutput.print(f"[{i}] {file['file_path']}", OutputType.INFO)
269
268
 
270
- # 询问用户是否需要调整
271
- user_input = input("\n是否需要调整文件列表?(y/n) [n]: ").strip().lower() or 'n'
269
+ # Ask the user if they need to adjust the file list
270
+ user_input = input("\nDo you need to adjust the file list? (y/n) [n]: ").strip().lower() or 'n'
272
271
  if user_input == 'y':
273
- # 让用户选择文件
274
- PrettyOutput.print("\n请输入要包含的文件编号(支持: 1,3-6 格式,直接回车保持当前选择):", OutputType.INFO)
272
+ # Let the user select files
273
+ PrettyOutput.print("\nPlease enter the file numbers to include (support: 1,3-6 format, press Enter to keep the current selection):", OutputType.INFO)
275
274
  numbers = input(">>> ").strip()
276
275
  if numbers:
277
276
  selected_indices = self._parse_file_selection(numbers, len(related_files))
278
277
  if selected_indices:
279
278
  selected_files = [related_files[i] for i in selected_indices]
280
279
  else:
281
- PrettyOutput.print("未选择任何有效文件,保持原有选择", OutputType.WARNING)
280
+ PrettyOutput.print("No valid files selected, keep the current selection", OutputType.WARNING)
282
281
 
283
- # 询问是否需要补充文件
284
- user_input = input("\n是否需要补充其他文件?(y/n) [n]: ").strip().lower() or 'n'
282
+ # Ask if they need to supplement files
283
+ user_input = input("\nDo you need to supplement other files? (y/n) [n]: ").strip().lower() or 'n'
285
284
  if user_input == 'y':
286
- # 创建文件补全会话
285
+ # Create file completion session
287
286
  session = PromptSession(
288
287
  completer=self._get_file_completer(),
289
288
  complete_while_typing=True
290
289
  )
291
290
 
292
291
  while True:
293
- PrettyOutput.print("\n请输入要补充的文件路径(支持Tab补全和*?通配符,输入空行结束):", OutputType.INFO)
292
+ PrettyOutput.print("\nPlease enter the file path to supplement (support Tab completion and *? wildcard, input empty line to end):", OutputType.INFO)
294
293
  try:
295
294
  file_path = session.prompt(">>> ").strip()
296
295
  except KeyboardInterrupt:
@@ -299,20 +298,20 @@ class JarvisCoder:
299
298
  if not file_path:
300
299
  break
301
300
 
302
- # 处理通配符匹配
301
+ # Process wildcard matching
303
302
  if '*' in file_path or '?' in file_path:
304
303
  matches = self._fuzzy_match_files(file_path)
305
304
  if not matches:
306
- PrettyOutput.print("未找到匹配的文件", OutputType.WARNING)
305
+ PrettyOutput.print("No matching files found", OutputType.WARNING)
307
306
  continue
308
307
 
309
- # 显示匹配的文件
310
- PrettyOutput.print("\n找到以下匹配的文件:", OutputType.INFO)
308
+ # Display matching files
309
+ PrettyOutput.print("\nFound the following matching files:", OutputType.INFO)
311
310
  for i, path in enumerate(matches, 1):
312
311
  PrettyOutput.print(f"[{i}] {path}", OutputType.INFO)
313
312
 
314
- # 让用户选择
315
- numbers = input("\n请选择要添加的文件编号(支持: 1,3-6 格式,直接回车全选): ").strip()
313
+ # Let the user select
314
+ numbers = input("\nPlease select the file numbers to add (support: 1,3-6 format, press Enter to select all): ").strip()
316
315
  if numbers:
317
316
  indices = self._parse_file_selection(numbers, len(matches))
318
317
  if not indices:
@@ -323,11 +322,11 @@ class JarvisCoder:
323
322
  else:
324
323
  paths_to_add = [file_path]
325
324
 
326
- # 添加选中的文件
325
+ # Add selected files
327
326
  for path in paths_to_add:
328
327
  full_path = os.path.join(self.root_dir, path)
329
328
  if not os.path.isfile(full_path):
330
- PrettyOutput.print(f"文件不存在: {path}", OutputType.ERROR)
329
+ PrettyOutput.print(f"File does not exist: {path}", OutputType.ERROR)
331
330
  continue
332
331
 
333
332
  try:
@@ -337,109 +336,105 @@ class JarvisCoder:
337
336
  "file_path": path,
338
337
  "file_content": content
339
338
  })
340
- PrettyOutput.print(f"已添加文件: {path}", OutputType.SUCCESS)
339
+ PrettyOutput.print(f"File added: {path}", OutputType.SUCCESS)
341
340
  except Exception as e:
342
- PrettyOutput.print(f"读取文件失败: {str(e)}", OutputType.ERROR)
341
+ PrettyOutput.print(f"Failed to read file: {str(e)}", OutputType.ERROR)
343
342
 
344
343
  return selected_files
345
344
 
346
345
  def _finalize_changes(self, feature: str) -> None:
347
- """完成修改并提交"""
348
- PrettyOutput.print("修改确认成功,提交修改", OutputType.INFO)
346
+ """Complete changes and commit"""
347
+ PrettyOutput.print("Modification confirmed, committing...", OutputType.INFO)
349
348
 
350
- # 只添加已经在 git 控制下的修改文件
349
+ # Add only modified files under git control
351
350
  os.system("git add -u")
352
351
 
353
- # 然后获取 git diff
352
+ # Then get git diff
354
353
  git_diff = os.popen("git diff --cached").read()
355
354
 
356
- # 自动生成commit信息,传入feature
357
- commit_message = generate_commit_message(git_diff, feature)
355
+ # Automatically generate commit information, pass in feature
356
+ commit_message = generate_commit_message(git_diff)
358
357
 
359
- # 显示并确认commit信息
360
- PrettyOutput.print(f"自动生成的commit信息: {commit_message}", OutputType.INFO)
361
- user_confirm = input("是否使用该commit信息?(y/n) [y]: ") or "y"
358
+ # Display and confirm commit information
359
+ PrettyOutput.print(f"Automatically generated commit information: {commit_message}", OutputType.INFO)
360
+ user_confirm = input("Use this commit information? (y/n) [y]: ") or "y"
362
361
 
363
362
  if user_confirm.lower() != "y":
364
- commit_message = input("请输入新的commit信息: ")
363
+ commit_message = input("Please enter a new commit information: ")
365
364
 
366
- # 不需要再次 git add,因为已经添加过了
365
+ # No need to git add again, it has already been added
367
366
  os.system(f"git commit -m '{commit_message}'")
368
367
  save_edit_record(self.record_dir, commit_message, git_diff)
369
368
 
370
369
  def _revert_changes(self) -> None:
371
- """回退所有修改"""
372
- PrettyOutput.print("修改已取消,回退更改", OutputType.INFO)
370
+ """Revert all changes"""
371
+ PrettyOutput.print("Modification cancelled, reverting changes", OutputType.INFO)
373
372
  os.system(f"git reset --hard")
374
373
  os.system(f"git clean -df")
375
374
 
376
375
  def get_key_code(self, files: List[Dict], feature: str):
377
- """提取文件中与需求相关的关键代码片段"""
376
+ """Extract relevant key code snippets from files"""
378
377
  for file_info in files:
379
- PrettyOutput.print(f"分析文件: {file_info['file_path']}", OutputType.INFO)
378
+ PrettyOutput.print(f"Analyzing file: {file_info['file_path']}", OutputType.INFO)
380
379
  model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
381
380
  model.set_suppress_output(True)
382
381
  file_path = file_info["file_path"]
383
382
  content = file_info["file_content"]
384
-
385
- # 生成分析提示
386
- system_message = f"""你是一个代码分析专家,可以从代码中提取出与需求相关的片段。
387
- 请按以下格式返回:
383
+
384
+ try:
385
+ prompt = f"""You are a code analysis expert who can extract relevant snippets from code.
386
+ Please return in the following format:
388
387
  <PART>
389
388
  content
390
389
  </PART>
391
390
 
392
- 可返回多个片段。如果文件内容与需求无关,则返回空。
393
- """
394
- model.set_system_message(system_message)
395
-
396
- try:
391
+ Multiple snippets can be returned. If the file content is not relevant to the requirement, return empty.
397
392
 
398
- prompt = f"""需求:{feature}
399
- 文件路径:{file_path}
400
- 代码内容:
393
+ Requirement: {feature}
394
+ File path: {file_path}
395
+ Code content:
401
396
  {content}
402
397
  """
403
398
 
404
399
  # 调用大模型进行分析
405
- response = while_success(lambda: model.chat(prompt))
400
+ response = model.chat_until_success(prompt)
406
401
 
407
402
  parts = re.findall(r'<PART>\n(.*?)\n</PART>', response, re.DOTALL)
408
403
  file_info["parts"] = parts
409
404
  except Exception as e:
410
- PrettyOutput.print(f"分析文件失败: {str(e)}", OutputType.ERROR)
405
+ PrettyOutput.print(f"Failed to analyze file: {str(e)}", OutputType.ERROR)
411
406
 
412
407
  def execute(self, feature: str) -> Dict[str, Any]:
413
- """执行代码修改"""
408
+ """Execute code modification"""
414
409
  try:
415
410
  self._prepare_execution()
416
411
 
417
- # 获取并选择相关文件
412
+ # Get and select related files
418
413
  initial_files = self._load_related_files(feature)
419
414
  selected_files = self._select_files(initial_files)
420
415
 
421
- # 是否是长上下文
416
+ # Whether it is a long context
422
417
  if is_long_context([file['file_path'] for file in selected_files]):
423
418
  self.get_key_code(selected_files, feature)
424
419
  else:
425
420
  for file in selected_files:
426
421
  file["parts"] = [file["file_content"]]
427
422
 
428
- # 获取修改方案
429
- modification_plan = PlanGenerator().generate_plan(feature, selected_files)
430
- if not modification_plan:
423
+ # Get modification plan
424
+ raw_plan, structed_plan = PlanGenerator().generate_plan(feature, selected_files)
425
+ if not raw_plan or not structed_plan:
431
426
  return {
432
427
  "success": False,
433
428
  "stdout": "",
434
- "stderr": "用户取消修改",
429
+ "stderr": "Failed to generate modification plan, please modify the requirement and try again",
435
430
  }
436
431
 
437
- # 执行修改
438
- if PatchHandler().handle_patch_application(selected_files, feature, modification_plan):
432
+ # Execute modification
433
+ if PatchHandler().handle_patch_application(feature ,raw_plan, structed_plan):
439
434
  self._finalize_changes(feature)
440
435
  return {
441
436
  "success": True,
442
- "stdout": "代码修改成功",
437
+ "stdout": "Code modification successful",
443
438
  "stderr": "",
444
439
  }
445
440
  else:
@@ -447,7 +442,7 @@ content
447
442
  return {
448
443
  "success": False,
449
444
  "stdout": "",
450
- "stderr": "代码修改失败,请修改需求后重试",
445
+ "stderr": "Code modification failed, please modify the requirement and try again",
451
446
  }
452
447
 
453
448
  except Exception as e:
@@ -455,54 +450,54 @@ content
455
450
  return {
456
451
  "success": False,
457
452
  "stdout": "",
458
- "stderr": f"执行失败: {str(e)},请修改需求后重试",
453
+ "stderr": f"Execution failed: {str(e)}, please modify the requirement and try again",
459
454
  "error": e
460
455
  }
461
456
 
462
457
  def main():
463
- """命令行入口"""
458
+ """Command line entry"""
464
459
  import argparse
465
460
 
466
461
  load_env_from_file()
467
462
 
468
- parser = argparse.ArgumentParser(description='代码修改工具')
469
- parser.add_argument('-d', '--dir', help='项目根目录', default=os.getcwd())
470
- parser.add_argument('-l', '--language', help='编程语言', default="python")
463
+ parser = argparse.ArgumentParser(description='Code modification tool')
464
+ parser.add_argument('-d', '--dir', help='Project root directory', default=os.getcwd())
465
+ parser.add_argument('-l', '--language', help='Programming language', default="python")
471
466
  args = parser.parse_args()
472
467
 
473
468
  tool = JarvisCoder(args.dir, args.language)
474
469
 
475
- # 循环处理需求
470
+ # Loop through requirements
476
471
  while True:
477
472
  try:
478
- # 获取需求,传入项目根目录
479
- feature = get_multiline_input("请输入开发需求 (输入空行退出):", tool.root_dir)
473
+ # Get requirements, pass in project root directory
474
+ feature = get_multiline_input("Please enter the development requirements (input empty line to exit):", tool.root_dir)
480
475
 
481
476
  if not feature or feature == "__interrupt__":
482
477
  break
483
478
 
484
- # 执行修改
479
+ # Execute modification
485
480
  result = tool.execute(feature)
486
481
 
487
- # 显示结果
482
+ # Display results
488
483
  if result["success"]:
489
484
  PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
490
485
  else:
491
486
  if result.get("stderr"):
492
487
  PrettyOutput.print(result["stderr"], OutputType.WARNING)
493
- if result.get("error"): # 使用 get() 方法避免 KeyError
488
+ if result.get("error"): # Use get() method to avoid KeyError
494
489
  error = result["error"]
495
- PrettyOutput.print(f"错误类型: {type(error).__name__}", OutputType.WARNING)
496
- PrettyOutput.print(f"错误信息: {str(error)}", OutputType.WARNING)
497
- # 提示用户可以继续输入
498
- PrettyOutput.print("\n您可以修改需求后重试", OutputType.INFO)
490
+ PrettyOutput.print(f"Error type: {type(error).__name__}", OutputType.WARNING)
491
+ PrettyOutput.print(f"Error information: {str(error)}", OutputType.WARNING)
492
+ # Prompt user to continue input
493
+ PrettyOutput.print("\nYou can modify the requirements and try again", OutputType.INFO)
499
494
 
500
495
  except KeyboardInterrupt:
501
- print("\n用户中断执行")
496
+ print("\nUser interrupted execution")
502
497
  break
503
498
  except Exception as e:
504
- PrettyOutput.print(f"执行出错: {str(e)}", OutputType.ERROR)
505
- PrettyOutput.print("\n您可以修改需求后重试", OutputType.INFO)
499
+ PrettyOutput.print(f"Execution failed: {str(e)}", OutputType.ERROR)
500
+ PrettyOutput.print("\nYou can modify the requirements and try again", OutputType.INFO)
506
501
  continue
507
502
 
508
503
  return 0
@@ -511,93 +506,93 @@ if __name__ == "__main__":
511
506
  exit(main())
512
507
 
513
508
  class FilePathCompleter(Completer):
514
- """文件路径自动完成器"""
509
+ """File path auto-completer"""
515
510
 
516
511
  def __init__(self, root_dir: str):
517
512
  self.root_dir = root_dir
518
513
  self._file_list = None
519
514
 
520
515
  def _get_files(self) -> List[str]:
521
- """获取git管理的文件列表"""
516
+ """Get the list of files managed by git"""
522
517
  if self._file_list is None:
523
518
  try:
524
- # 切换到项目根目录
519
+ # Switch to project root directory
525
520
  old_cwd = os.getcwd()
526
521
  os.chdir(self.root_dir)
527
522
 
528
- # 获取git管理的文件列表
523
+ # Get the list of files managed by git
529
524
  self._file_list = os.popen("git ls-files").read().splitlines()
530
525
 
531
- # 恢复工作目录
526
+ # Restore working directory
532
527
  os.chdir(old_cwd)
533
528
  except Exception as e:
534
- PrettyOutput.print(f"获取文件列表失败: {str(e)}", OutputType.WARNING)
529
+ PrettyOutput.print(f"Failed to get file list: {str(e)}", OutputType.WARNING)
535
530
  self._file_list = []
536
531
  return self._file_list
537
532
 
538
533
  def get_completions(self, document, complete_event):
539
- """获取补全建议"""
534
+ """Get completion suggestions"""
540
535
  text_before_cursor = document.text_before_cursor
541
536
 
542
- # 检查是否刚输入了@
537
+ # Check if @ was just entered
543
538
  if text_before_cursor.endswith('@'):
544
- # 显示所有文件
539
+ # Display all files
545
540
  for path in self._get_files():
546
541
  yield Completion(path, start_position=0)
547
542
  return
548
543
 
549
- # 检查之前是否有@,并获取@后的搜索词
544
+ # Check if there was an @ before, and get the search word after @
550
545
  at_pos = text_before_cursor.rfind('@')
551
546
  if at_pos == -1:
552
547
  return
553
548
 
554
549
  search = text_before_cursor[at_pos + 1:].lower().strip()
555
550
 
556
- # 提供匹配的文件建议
551
+ # Provide matching file suggestions
557
552
  for path in self._get_files():
558
553
  path_lower = path.lower()
559
- if (search in path_lower or # 直接包含
560
- search in os.path.basename(path_lower) or # 文件名包含
561
- any(fnmatch.fnmatch(path_lower, f'*{s}*') for s in search.split())): # 通配符匹配
562
- # 计算正确的start_position
554
+ if (search in path_lower or # Directly included
555
+ search in os.path.basename(path_lower) or # File name included
556
+ any(fnmatch.fnmatch(path_lower, f'*{s}*') for s in search.split())): # Wildcard matching
557
+ # Calculate the correct start_position
563
558
  yield Completion(path, start_position=-(len(search)))
564
559
 
565
560
  class SmartCompleter(Completer):
566
- """智能自动完成器,组合词语和文件路径补全"""
561
+ """Smart auto-completer, combine word and file path completion"""
567
562
 
568
563
  def __init__(self, word_completer: WordCompleter, file_completer: FilePathCompleter):
569
564
  self.word_completer = word_completer
570
565
  self.file_completer = file_completer
571
566
 
572
567
  def get_completions(self, document, complete_event):
573
- """获取补全建议"""
574
- # 如果当前行以@结尾,使用文件补全
568
+ """Get completion suggestions"""
569
+ # If the current line ends with @, use file completion
575
570
  if document.text_before_cursor.strip().endswith('@'):
576
571
  yield from self.file_completer.get_completions(document, complete_event)
577
572
  else:
578
- # 否则使用词语补全
573
+ # Otherwise, use word completion
579
574
  yield from self.word_completer.get_completions(document, complete_event)
580
575
 
581
576
  def get_multiline_input(prompt_text: str, root_dir: Optional[str] = ".") -> str:
582
- """获取多行输入,支持文件路径自动完成功能
577
+ """Get multi-line input, support file path auto-completion function
583
578
 
584
579
  Args:
585
- prompt_text: 提示文本
586
- root_dir: 项目根目录,用于文件补全
580
+ prompt_text: Prompt text
581
+ root_dir: Project root directory, for file completion
587
582
 
588
583
  Returns:
589
- str: 用户输入的文本
584
+ str: User input text
590
585
  """
591
- # 创建文件补全器
586
+ # Create file completion
592
587
  file_completer = FilePathCompleter(root_dir or os.getcwd())
593
588
 
594
- # 创建提示样式
589
+ # Create prompt style
595
590
  style = Style.from_dict({
596
591
  'prompt': 'ansicyan bold',
597
592
  'input': 'ansiwhite',
598
593
  })
599
594
 
600
- # 创建会话
595
+ # Create session
601
596
  session = PromptSession(
602
597
  completer=file_completer,
603
598
  style=style,
@@ -606,20 +601,20 @@ def get_multiline_input(prompt_text: str, root_dir: Optional[str] = ".") -> str:
606
601
  complete_while_typing=True
607
602
  )
608
603
 
609
- # 显示初始提示文本
604
+ # Display initial prompt text
610
605
  print(f"\n{prompt_text}")
611
606
 
612
- # 创建提示符
607
+ # Create prompt
613
608
  prompt = FormattedText([
614
609
  ('class:prompt', ">>> ")
615
610
  ])
616
611
 
617
- # 获取输入
612
+ # Get input
618
613
  lines = []
619
614
  try:
620
615
  while True:
621
616
  line = session.prompt(prompt).strip()
622
- if not line: # 空行表示输入结束
617
+ if not line: # Empty line means input end
623
618
  break
624
619
  lines.append(line)
625
620
  except KeyboardInterrupt: