jarvis-ai-assistant 0.1.111__py3-none-any.whl → 0.1.112__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/agent.py +40 -34
- jarvis/jarvis_code_agent/code_agent.py +23 -5
- jarvis/jarvis_code_agent/file_select.py +16 -16
- jarvis/jarvis_code_agent/patch.py +17 -11
- jarvis/jarvis_code_agent/relevant_files.py +33 -40
- jarvis/jarvis_codebase/main.py +57 -48
- jarvis/jarvis_lsp/cpp.py +1 -1
- jarvis/jarvis_lsp/go.py +1 -1
- jarvis/jarvis_lsp/python.py +0 -2
- jarvis/jarvis_lsp/registry.py +13 -13
- jarvis/jarvis_lsp/rust.py +1 -1
- jarvis/jarvis_platform/ai8.py +14 -14
- jarvis/jarvis_platform/base.py +1 -1
- jarvis/jarvis_platform/kimi.py +17 -17
- jarvis/jarvis_platform/ollama.py +14 -14
- jarvis/jarvis_platform/openai.py +8 -8
- jarvis/jarvis_platform/oyi.py +19 -19
- jarvis/jarvis_platform/registry.py +6 -6
- jarvis/jarvis_platform_manager/main.py +17 -17
- jarvis/jarvis_rag/main.py +25 -25
- jarvis/jarvis_smart_shell/main.py +6 -6
- jarvis/jarvis_tools/ask_codebase.py +3 -3
- jarvis/jarvis_tools/ask_user.py +2 -2
- jarvis/jarvis_tools/create_code_agent.py +8 -8
- jarvis/jarvis_tools/create_sub_agent.py +2 -2
- jarvis/jarvis_tools/execute_shell.py +2 -2
- jarvis/jarvis_tools/file_operation.py +1 -1
- jarvis/jarvis_tools/git_commiter.py +4 -4
- jarvis/jarvis_tools/methodology.py +3 -3
- jarvis/jarvis_tools/rag.py +3 -3
- jarvis/jarvis_tools/read_code.py +1 -1
- jarvis/jarvis_tools/read_webpage.py +19 -6
- jarvis/jarvis_tools/registry.py +11 -11
- jarvis/jarvis_tools/search.py +88 -27
- jarvis/jarvis_tools/select_code_files.py +1 -1
- jarvis/jarvis_tools/tool_generator.py +182 -0
- jarvis/utils.py +18 -20
- jarvis_ai_assistant-0.1.112.dist-info/METADATA +460 -0
- jarvis_ai_assistant-0.1.112.dist-info/RECORD +64 -0
- jarvis_ai_assistant-0.1.111.dist-info/METADATA +0 -461
- jarvis_ai_assistant-0.1.111.dist-info/RECORD +0 -63
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -39,6 +39,7 @@ class Agent:
|
|
|
39
39
|
auto_complete: Optional[bool] = None,
|
|
40
40
|
output_handler_before_tool: Optional[List[Callable]] = None,
|
|
41
41
|
output_handler_after_tool: Optional[List[Callable]] = None,
|
|
42
|
+
input_handler: Optional[List[Callable]] = None,
|
|
42
43
|
use_methodology: Optional[bool] = None,
|
|
43
44
|
record_methodology: Optional[bool] = None,
|
|
44
45
|
need_summary: Optional[bool] = None,
|
|
@@ -61,7 +62,7 @@ class Agent:
|
|
|
61
62
|
need_summary: Whether to generate summaries
|
|
62
63
|
max_context_length: Maximum context length
|
|
63
64
|
"""
|
|
64
|
-
PrettyOutput.print(f"
|
|
65
|
+
PrettyOutput.print(f"欢迎使用Jarvis,你的AI助手,正在初始化...", OutputType.SYSTEM)
|
|
65
66
|
if platform is not None:
|
|
66
67
|
self.model = platform
|
|
67
68
|
else:
|
|
@@ -75,6 +76,7 @@ class Agent:
|
|
|
75
76
|
self.conversation_length = 0 # Use length counter instead
|
|
76
77
|
self.system_prompt = system_prompt
|
|
77
78
|
self.need_summary = need_summary if need_summary is not None else is_need_summary()
|
|
79
|
+
self.input_handler = input_handler if input_handler is not None else []
|
|
78
80
|
# Load configuration from environment variables
|
|
79
81
|
self.output_handler_before_tool = output_handler_before_tool if output_handler_before_tool else []
|
|
80
82
|
self.output_handler_after_tool = output_handler_after_tool if output_handler_after_tool else []
|
|
@@ -96,11 +98,11 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
96
98
|
|
|
97
99
|
self.auto_complete = auto_complete if auto_complete is not None else is_auto_complete()
|
|
98
100
|
|
|
99
|
-
PrettyOutput.section(f"Jarvis
|
|
101
|
+
PrettyOutput.section(f"Jarvis 初始化完成 - 使用 {self.model.name()} 模型", OutputType.SYSTEM)
|
|
100
102
|
|
|
101
103
|
tools = self.tool_registry.get_all_tools()
|
|
102
104
|
if tools:
|
|
103
|
-
PrettyOutput.section(f"
|
|
105
|
+
PrettyOutput.section(f"可用工具: {', '.join([tool['name'] for tool in tools])}", OutputType.SYSTEM)
|
|
104
106
|
|
|
105
107
|
|
|
106
108
|
tools_prompt = self.tool_registry.load_tools()
|
|
@@ -181,12 +183,16 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
181
183
|
Will retry with exponential backoff up to 30 seconds between retries
|
|
182
184
|
"""
|
|
183
185
|
sleep_time = 5
|
|
186
|
+
|
|
187
|
+
for handler in self.input_handler:
|
|
188
|
+
message = handler(message)
|
|
189
|
+
|
|
184
190
|
while True:
|
|
185
191
|
ret = self.model.chat_until_success(message)
|
|
186
192
|
if ret:
|
|
187
193
|
return ret
|
|
188
194
|
else:
|
|
189
|
-
PrettyOutput.print(f"
|
|
195
|
+
PrettyOutput.print(f"模型调用失败,正在重试... 等待 {sleep_time}s", OutputType.INFO)
|
|
190
196
|
time.sleep(sleep_time)
|
|
191
197
|
sleep_time *= 2
|
|
192
198
|
if sleep_time > 30:
|
|
@@ -209,7 +215,7 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
209
215
|
"""
|
|
210
216
|
# Create a new model instance to summarize, avoid affecting the main conversation
|
|
211
217
|
|
|
212
|
-
PrettyOutput.print("
|
|
218
|
+
PrettyOutput.print("总结对话历史,准备生成摘要,开始新对话...", OutputType.PROGRESS)
|
|
213
219
|
|
|
214
220
|
prompt = """Please summarize the key information from the previous conversation, including:
|
|
215
221
|
1. Current task objective
|
|
@@ -222,7 +228,7 @@ Please describe in concise bullet points, highlighting important information. Do
|
|
|
222
228
|
"""
|
|
223
229
|
|
|
224
230
|
try:
|
|
225
|
-
summary = self.
|
|
231
|
+
summary = self._call_model(self.prompt + "\n" + prompt)
|
|
226
232
|
|
|
227
233
|
# 清空当前对话历史,但保留系统消息
|
|
228
234
|
self.conversation_length = 0 # Reset conversation length
|
|
@@ -237,7 +243,7 @@ Please continue the task based on the above information.
|
|
|
237
243
|
self.conversation_length = len(self.prompt) # 设置新的起始长度
|
|
238
244
|
|
|
239
245
|
except Exception as e:
|
|
240
|
-
PrettyOutput.print(f"
|
|
246
|
+
PrettyOutput.print(f"总结对话历史失败: {str(e)}", OutputType.ERROR)
|
|
241
247
|
|
|
242
248
|
def _complete_task(self) -> str:
|
|
243
249
|
"""Complete the current task and generate summary if needed.
|
|
@@ -249,7 +255,7 @@ Please continue the task based on the above information.
|
|
|
249
255
|
- For main agent: May generate methodology if enabled
|
|
250
256
|
- For sub-agent: May generate summary if enabled
|
|
251
257
|
"""
|
|
252
|
-
PrettyOutput.section("
|
|
258
|
+
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
253
259
|
|
|
254
260
|
if not self.is_sub_agent:
|
|
255
261
|
if self.record_methodology:
|
|
@@ -272,18 +278,18 @@ Please continue the task based on the above information.
|
|
|
272
278
|
if tool_calls:
|
|
273
279
|
self.tool_registry.handle_tool_calls(tool_calls)
|
|
274
280
|
except Exception as e:
|
|
275
|
-
PrettyOutput.print(f"
|
|
281
|
+
PrettyOutput.print(f"处理方法论生成失败: {str(e)}", OutputType.ERROR)
|
|
276
282
|
|
|
277
283
|
except Exception as e:
|
|
278
|
-
PrettyOutput.print(f"
|
|
284
|
+
PrettyOutput.print(f"生成方法论失败: {str(e)}", OutputType.ERROR)
|
|
279
285
|
|
|
280
|
-
return "
|
|
286
|
+
return "任务完成"
|
|
281
287
|
|
|
282
288
|
if self.need_summary:
|
|
283
289
|
self.prompt = self.summary_prompt
|
|
284
290
|
return self._call_model(self.prompt)
|
|
285
291
|
|
|
286
|
-
return "
|
|
292
|
+
return "任务完成"
|
|
287
293
|
|
|
288
294
|
|
|
289
295
|
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str:
|
|
@@ -306,12 +312,12 @@ Please continue the task based on the above information.
|
|
|
306
312
|
add_agent(self.name)
|
|
307
313
|
|
|
308
314
|
try:
|
|
309
|
-
PrettyOutput.section("
|
|
315
|
+
PrettyOutput.section("准备环境", OutputType.PLANNING)
|
|
310
316
|
if file_list:
|
|
311
317
|
self.model.upload_files(file_list)
|
|
312
318
|
|
|
313
319
|
# 显示任务开始
|
|
314
|
-
PrettyOutput.section(f"
|
|
320
|
+
PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
|
|
315
321
|
|
|
316
322
|
if self.first and self.use_methodology:
|
|
317
323
|
self.prompt = f"{user_input}\n\n{load_methodology(user_input)}"
|
|
@@ -322,7 +328,7 @@ Please continue the task based on the above information.
|
|
|
322
328
|
while True:
|
|
323
329
|
try:
|
|
324
330
|
# 显示思考状态
|
|
325
|
-
PrettyOutput.print("
|
|
331
|
+
PrettyOutput.print("正在分析任务...", OutputType.PROGRESS)
|
|
326
332
|
|
|
327
333
|
# 累加对话长度
|
|
328
334
|
self.conversation_length += get_context_token_count(self.prompt)
|
|
@@ -342,13 +348,13 @@ Please continue the task based on the above information.
|
|
|
342
348
|
try:
|
|
343
349
|
result = Agent._extract_tool_calls(current_response)
|
|
344
350
|
except Exception as e:
|
|
345
|
-
PrettyOutput.print(f"
|
|
351
|
+
PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
|
|
346
352
|
self.prompt += f"Tool call error: {str(e)}"
|
|
347
353
|
continue
|
|
348
354
|
|
|
349
355
|
if len(result) > 0:
|
|
350
|
-
if not self.execute_tool_confirm or user_confirm(f"
|
|
351
|
-
PrettyOutput.print("
|
|
356
|
+
if not self.execute_tool_confirm or user_confirm(f"执行工具调用: {result[0]['name']}?"):
|
|
357
|
+
PrettyOutput.print("正在执行工具调用...", OutputType.PROGRESS)
|
|
352
358
|
tool_result = self.tool_registry.handle_tool_calls(result)
|
|
353
359
|
self.prompt += tool_result
|
|
354
360
|
|
|
@@ -362,7 +368,7 @@ Please continue the task based on the above information.
|
|
|
362
368
|
return self._complete_task()
|
|
363
369
|
|
|
364
370
|
# 获取用户输入
|
|
365
|
-
user_input = get_multiline_input(f"{self.name}:
|
|
371
|
+
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行来结束当前任务:")
|
|
366
372
|
|
|
367
373
|
if user_input:
|
|
368
374
|
self.prompt = user_input
|
|
@@ -372,11 +378,11 @@ Please continue the task based on the above information.
|
|
|
372
378
|
return self._complete_task()
|
|
373
379
|
|
|
374
380
|
except Exception as e:
|
|
375
|
-
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
381
|
+
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
|
376
382
|
return f"Task failed: {str(e)}"
|
|
377
383
|
|
|
378
384
|
except Exception as e:
|
|
379
|
-
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
385
|
+
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
|
380
386
|
return f"Task failed: {str(e)}"
|
|
381
387
|
|
|
382
388
|
finally:
|
|
@@ -414,9 +420,9 @@ def _load_tasks() -> dict:
|
|
|
414
420
|
if desc: # Ensure description is not empty
|
|
415
421
|
tasks[str(name)] = str(desc)
|
|
416
422
|
else:
|
|
417
|
-
PrettyOutput.print("
|
|
423
|
+
PrettyOutput.print("警告: ~/.jarvis/pre-command 文件应该包含一个字典,键为任务名称,值为任务描述", OutputType.ERROR)
|
|
418
424
|
except Exception as e:
|
|
419
|
-
PrettyOutput.print(f"
|
|
425
|
+
PrettyOutput.print(f"加载 ~/.jarvis/pre-command 文件失败: {str(e)}", OutputType.ERROR)
|
|
420
426
|
|
|
421
427
|
# Check .jarvis/pre-command in current directory
|
|
422
428
|
if os.path.exists(".jarvis/pre-command"):
|
|
@@ -430,9 +436,9 @@ def _load_tasks() -> dict:
|
|
|
430
436
|
if desc: # Ensure description is not empty
|
|
431
437
|
tasks[str(name)] = str(desc)
|
|
432
438
|
else:
|
|
433
|
-
PrettyOutput.print("
|
|
439
|
+
PrettyOutput.print("警告: .jarvis/pre-command 文件应该包含一个字典,键为任务名称,值为任务描述", OutputType.ERROR)
|
|
434
440
|
except Exception as e:
|
|
435
|
-
PrettyOutput.print(f"
|
|
441
|
+
PrettyOutput.print(f"加载 .jarvis/pre-command 文件失败: {str(e)}", OutputType.ERROR)
|
|
436
442
|
|
|
437
443
|
|
|
438
444
|
if is_use_methodology():
|
|
@@ -455,17 +461,17 @@ def _select_task(tasks: dict) -> str:
|
|
|
455
461
|
# Convert tasks to list for ordered display
|
|
456
462
|
task_names = list(tasks.keys())
|
|
457
463
|
|
|
458
|
-
task_list = ["
|
|
464
|
+
task_list = ["可用任务:"]
|
|
459
465
|
for i, name in enumerate(task_names, 1):
|
|
460
466
|
task_list.append(f"[{i}] {name}")
|
|
461
|
-
task_list.append("[0]
|
|
467
|
+
task_list.append("[0] 跳过预定义任务")
|
|
462
468
|
PrettyOutput.print("\n".join(task_list), OutputType.INFO)
|
|
463
469
|
|
|
464
470
|
|
|
465
471
|
while True:
|
|
466
472
|
try:
|
|
467
473
|
choice = prompt(
|
|
468
|
-
"\
|
|
474
|
+
"\n请选择一个任务编号(0 跳过预定义任务):",
|
|
469
475
|
).strip()
|
|
470
476
|
|
|
471
477
|
if not choice:
|
|
@@ -478,14 +484,14 @@ def _select_task(tasks: dict) -> str:
|
|
|
478
484
|
selected_name = task_names[choice - 1]
|
|
479
485
|
return tasks[selected_name] # Return the task description
|
|
480
486
|
else:
|
|
481
|
-
PrettyOutput.print("
|
|
487
|
+
PrettyOutput.print("无效的选择。请选择列表中的一个号码。", OutputType.ERROR)
|
|
482
488
|
|
|
483
489
|
except KeyboardInterrupt:
|
|
484
490
|
return "" # Return empty on Ctrl+C
|
|
485
491
|
except EOFError:
|
|
486
492
|
return "" # Return empty on Ctrl+D
|
|
487
493
|
except Exception as e:
|
|
488
|
-
PrettyOutput.print(f"
|
|
494
|
+
PrettyOutput.print(f"选择任务失败: {str(e)}", OutputType.ERROR)
|
|
489
495
|
continue
|
|
490
496
|
|
|
491
497
|
origin_agent_system_prompt = """You are Jarvis, an AI assistant with powerful problem-solving capabilities.
|
|
@@ -529,22 +535,22 @@ def main():
|
|
|
529
535
|
if tasks:
|
|
530
536
|
selected_task = _select_task(tasks)
|
|
531
537
|
if selected_task:
|
|
532
|
-
PrettyOutput.print(f"
|
|
538
|
+
PrettyOutput.print(f"执行任务: {selected_task}", OutputType.INFO)
|
|
533
539
|
agent.run(selected_task, args.files)
|
|
534
540
|
return 0
|
|
535
541
|
|
|
536
542
|
# 如果没有选择预定义任务,进入交互模式
|
|
537
543
|
while True:
|
|
538
544
|
try:
|
|
539
|
-
user_input = get_multiline_input("
|
|
545
|
+
user_input = get_multiline_input("请输入你的任务(输入空行退出):")
|
|
540
546
|
if not user_input:
|
|
541
547
|
break
|
|
542
548
|
agent.run(user_input, args.files)
|
|
543
549
|
except Exception as e:
|
|
544
|
-
PrettyOutput.print(f"
|
|
550
|
+
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
545
551
|
|
|
546
552
|
except Exception as e:
|
|
547
|
-
PrettyOutput.print(f"
|
|
553
|
+
PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
|
|
548
554
|
return 1
|
|
549
555
|
|
|
550
556
|
return 0
|
|
@@ -34,9 +34,24 @@ class CodeAgent:
|
|
|
34
34
|
3. Map out affected components and their interactions
|
|
35
35
|
4. Plan changes that maintain system integrity
|
|
36
36
|
|
|
37
|
+
# Code Completeness Requirements
|
|
38
|
+
1. Implementation Must Be Complete
|
|
39
|
+
• NO TODOs or placeholder comments
|
|
40
|
+
• NO unfinished functions
|
|
41
|
+
• NO stub implementations
|
|
42
|
+
• All error cases must be handled
|
|
43
|
+
• All edge cases must be covered
|
|
44
|
+
|
|
45
|
+
2. Documentation Must Be Complete
|
|
46
|
+
• All functions must have docstrings
|
|
47
|
+
• All parameters must be documented
|
|
48
|
+
• Return values must be specified
|
|
49
|
+
• Exceptions must be documented
|
|
50
|
+
• Complex logic must be explained
|
|
51
|
+
|
|
37
52
|
# Patch Format
|
|
38
53
|
<PATCH>
|
|
39
|
-
>
|
|
54
|
+
> path/file start,end
|
|
40
55
|
new_content
|
|
41
56
|
</PATCH>
|
|
42
57
|
|
|
@@ -84,6 +99,7 @@ Key Rules:
|
|
|
84
99
|
• Follow existing patterns exactly
|
|
85
100
|
• Preserve all interfaces
|
|
86
101
|
• Maintain backward compatibility
|
|
102
|
+
• Implement completely - no TODOs
|
|
87
103
|
|
|
88
104
|
# File Handling
|
|
89
105
|
Large Files (>200 lines):
|
|
@@ -105,7 +121,9 @@ Every Change Must:
|
|
|
105
121
|
✓ Match existing style exactly
|
|
106
122
|
✓ Handle errors consistently
|
|
107
123
|
✓ Maintain documentation
|
|
108
|
-
✓ Follow project patterns
|
|
124
|
+
✓ Follow project patterns
|
|
125
|
+
✓ Be completely implemented
|
|
126
|
+
✓ Have no TODOs or stubs"""
|
|
109
127
|
self.agent = Agent(system_prompt=code_system_prompt,
|
|
110
128
|
name="CodeAgent",
|
|
111
129
|
auto_complete=False,
|
|
@@ -196,17 +214,17 @@ def main():
|
|
|
196
214
|
# Interactive mode
|
|
197
215
|
while True:
|
|
198
216
|
try:
|
|
199
|
-
user_input = get_multiline_input("
|
|
217
|
+
user_input = get_multiline_input("请输入你的需求(输入空行退出):")
|
|
200
218
|
if not user_input:
|
|
201
219
|
break
|
|
202
220
|
agent = CodeAgent()
|
|
203
221
|
agent.run(user_input)
|
|
204
222
|
|
|
205
223
|
except Exception as e:
|
|
206
|
-
PrettyOutput.print(f"
|
|
224
|
+
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
207
225
|
|
|
208
226
|
except Exception as e:
|
|
209
|
-
PrettyOutput.print(f"
|
|
227
|
+
PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
|
|
210
228
|
return 1
|
|
211
229
|
|
|
212
230
|
return 0
|
|
@@ -42,7 +42,7 @@ def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
|
|
|
42
42
|
if start <= end:
|
|
43
43
|
selected.update(range(start, end + 1))
|
|
44
44
|
except ValueError:
|
|
45
|
-
PrettyOutput.print(f"
|
|
45
|
+
PrettyOutput.print(f"忽略无效的范围表达式: {part}", OutputType.WARNING)
|
|
46
46
|
# Process single number
|
|
47
47
|
else:
|
|
48
48
|
try:
|
|
@@ -50,9 +50,9 @@ def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
|
|
|
50
50
|
if 0 <= index < max_index:
|
|
51
51
|
selected.add(index)
|
|
52
52
|
else:
|
|
53
|
-
PrettyOutput.print(f"
|
|
53
|
+
PrettyOutput.print(f"忽略超出范围的索引: {part}", OutputType.WARNING)
|
|
54
54
|
except ValueError:
|
|
55
|
-
PrettyOutput.print(f"
|
|
55
|
+
PrettyOutput.print(f"忽略无效的数字: {part}", OutputType.WARNING)
|
|
56
56
|
|
|
57
57
|
return sorted(list(selected))
|
|
58
58
|
|
|
@@ -127,8 +127,8 @@ def _fuzzy_match_files(root_dir: str, pattern: str) -> List[str]:
|
|
|
127
127
|
|
|
128
128
|
def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dict[str, str]]:
|
|
129
129
|
"""Let the user select and supplement related files"""
|
|
130
|
-
PrettyOutput.section("
|
|
131
|
-
|
|
130
|
+
PrettyOutput.section("相关文件", OutputType.INFO)
|
|
131
|
+
|
|
132
132
|
output = ""
|
|
133
133
|
# Display found files
|
|
134
134
|
selected_files = list(related_files) # Default select all
|
|
@@ -139,18 +139,18 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
139
139
|
|
|
140
140
|
if len(related_files) > 0:
|
|
141
141
|
# Ask the user if they need to adjust the file list
|
|
142
|
-
if user_confirm("
|
|
142
|
+
if user_confirm("是否需要调整文件列表?", False):
|
|
143
143
|
# Let the user select files
|
|
144
|
-
numbers = get_single_line_input("
|
|
144
|
+
numbers = get_single_line_input("请输入要包含的文件编号(支持: 1,3-6格式, 按回车保持当前选择)").strip()
|
|
145
145
|
if numbers:
|
|
146
146
|
selected_indices = _parse_file_selection(numbers, len(related_files))
|
|
147
147
|
if selected_indices:
|
|
148
148
|
selected_files = [related_files[i] for i in selected_indices]
|
|
149
149
|
else:
|
|
150
|
-
PrettyOutput.print("
|
|
150
|
+
PrettyOutput.print("没有有效的文件被选择, 保持当前选择", OutputType.WARNING)
|
|
151
151
|
|
|
152
152
|
# Ask if they need to supplement files
|
|
153
|
-
if user_confirm("
|
|
153
|
+
if user_confirm("是否需要补充其他文件?", False):
|
|
154
154
|
# Create file completion session
|
|
155
155
|
session = PromptSession(
|
|
156
156
|
completer=_get_file_completer(root_dir),
|
|
@@ -158,7 +158,7 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
158
158
|
)
|
|
159
159
|
|
|
160
160
|
while True:
|
|
161
|
-
PrettyOutput.print("
|
|
161
|
+
PrettyOutput.print("请输入要补充的文件路径(支持Tab补全和*?通配符, 输入空行结束)", OutputType.INFO)
|
|
162
162
|
try:
|
|
163
163
|
file_path = session.prompt(">>> ").strip()
|
|
164
164
|
except KeyboardInterrupt:
|
|
@@ -171,16 +171,16 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
171
171
|
if '*' in file_path or '?' in file_path:
|
|
172
172
|
matches = _fuzzy_match_files(root_dir, file_path)
|
|
173
173
|
if not matches:
|
|
174
|
-
PrettyOutput.print("
|
|
174
|
+
PrettyOutput.print("没有找到匹配的文件", OutputType.WARNING)
|
|
175
175
|
continue
|
|
176
176
|
|
|
177
177
|
# Display matching files
|
|
178
|
-
PrettyOutput.print("
|
|
178
|
+
PrettyOutput.print("找到以下匹配的文件:", OutputType.INFO)
|
|
179
179
|
for i, path in enumerate(matches, 1):
|
|
180
180
|
PrettyOutput.print(f"[{i}] {path}", OutputType.INFO)
|
|
181
181
|
|
|
182
182
|
# Let the user select
|
|
183
|
-
numbers = get_single_line_input("
|
|
183
|
+
numbers = get_single_line_input("请选择要添加的文件编号(支持: 1,3-6格式, 按回车选择所有)").strip()
|
|
184
184
|
if numbers:
|
|
185
185
|
indices = _parse_file_selection(numbers, len(matches))
|
|
186
186
|
if not indices:
|
|
@@ -195,13 +195,13 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
195
195
|
for path in paths_to_add:
|
|
196
196
|
full_path = os.path.join(root_dir, path)
|
|
197
197
|
if not os.path.isfile(full_path):
|
|
198
|
-
PrettyOutput.print(f"
|
|
198
|
+
PrettyOutput.print(f"文件不存在: {path}", OutputType.ERROR)
|
|
199
199
|
continue
|
|
200
200
|
|
|
201
201
|
try:
|
|
202
202
|
selected_files.append({"file": path, "reason": "User Added"})
|
|
203
|
-
PrettyOutput.print(f"
|
|
203
|
+
PrettyOutput.print(f"文件已添加: {path}", OutputType.SUCCESS)
|
|
204
204
|
except Exception as e:
|
|
205
|
-
PrettyOutput.print(f"
|
|
205
|
+
PrettyOutput.print(f"读取文件失败: {str(e)}", OutputType.ERROR)
|
|
206
206
|
|
|
207
207
|
return selected_files
|
|
@@ -2,13 +2,13 @@ import re
|
|
|
2
2
|
from typing import Dict, Any, List
|
|
3
3
|
import os
|
|
4
4
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
5
|
-
from jarvis.utils import OutputType, PrettyOutput, has_uncommitted_changes, user_confirm
|
|
5
|
+
from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, user_confirm
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
9
9
|
"""Parse patches from string with format:
|
|
10
10
|
<PATCH>
|
|
11
|
-
>
|
|
11
|
+
> path/to/file start_line,end_line
|
|
12
12
|
content_line1
|
|
13
13
|
content_line2
|
|
14
14
|
...
|
|
@@ -77,12 +77,12 @@ def apply_patch(output_str: str)->str:
|
|
|
77
77
|
# Write new file
|
|
78
78
|
with open(filepath, 'w', encoding='utf-8') as f:
|
|
79
79
|
f.writelines(new_content)
|
|
80
|
-
PrettyOutput.print(f"
|
|
80
|
+
PrettyOutput.print(f"成功创建新文件 {filepath}", OutputType.SUCCESS)
|
|
81
81
|
continue
|
|
82
82
|
|
|
83
83
|
# Regular patch logic for existing files
|
|
84
84
|
if not os.path.exists(filepath):
|
|
85
|
-
PrettyOutput.print(f"
|
|
85
|
+
PrettyOutput.print(f"文件不存在: {filepath}", OutputType.WARNING)
|
|
86
86
|
continue
|
|
87
87
|
|
|
88
88
|
# Read original file content
|
|
@@ -90,7 +90,7 @@ def apply_patch(output_str: str)->str:
|
|
|
90
90
|
|
|
91
91
|
# Validate line numbers
|
|
92
92
|
if start_line < 0 or end_line > len(lines) + 1 or start_line > end_line:
|
|
93
|
-
PrettyOutput.print(f"
|
|
93
|
+
PrettyOutput.print(f"无效的行范围 [{start_line}, {end_line}) 对于文件: {filepath}", OutputType.WARNING)
|
|
94
94
|
continue
|
|
95
95
|
|
|
96
96
|
# Create new content
|
|
@@ -99,15 +99,21 @@ def apply_patch(output_str: str)->str:
|
|
|
99
99
|
# Write back to file
|
|
100
100
|
open(filepath, 'w', encoding='utf-8').writelines(lines)
|
|
101
101
|
|
|
102
|
-
PrettyOutput.print(f"
|
|
102
|
+
PrettyOutput.print(f"成功应用补丁到 {filepath}", OutputType.SUCCESS)
|
|
103
103
|
|
|
104
104
|
except Exception as e:
|
|
105
|
-
PrettyOutput.print(f"
|
|
105
|
+
PrettyOutput.print(f"应用补丁到 {filepath} 失败: {str(e)}", OutputType.ERROR)
|
|
106
106
|
continue
|
|
107
|
-
|
|
107
|
+
ret = ""
|
|
108
108
|
if has_uncommitted_changes():
|
|
109
|
-
handle_commit_workflow()
|
|
110
|
-
|
|
109
|
+
if handle_commit_workflow():
|
|
110
|
+
ret += "Successfully applied the patch"
|
|
111
|
+
else:
|
|
112
|
+
ret += "User rejected the patch"
|
|
113
|
+
user_input = get_multiline_input("你可以继续输入: ")
|
|
114
|
+
if user_input:
|
|
115
|
+
ret += user_input
|
|
116
|
+
return ret
|
|
111
117
|
|
|
112
118
|
def handle_commit_workflow()->bool:
|
|
113
119
|
"""Handle the git commit workflow and return the commit details.
|
|
@@ -119,7 +125,7 @@ def handle_commit_workflow()->bool:
|
|
|
119
125
|
diff = os.popen("git diff HEAD").read()
|
|
120
126
|
os.system("git reset HEAD")
|
|
121
127
|
PrettyOutput.print(diff, OutputType.CODE, lang="diff")
|
|
122
|
-
if not user_confirm("
|
|
128
|
+
if not user_confirm("是否要提交代码?", default=True):
|
|
123
129
|
os.system("git reset HEAD")
|
|
124
130
|
os.system("git checkout -- .")
|
|
125
131
|
os.system("git clean -fd")
|
|
@@ -8,60 +8,54 @@ from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
|
8
8
|
from jarvis.utils import OutputType, PrettyOutput
|
|
9
9
|
|
|
10
10
|
def make_question(requirement: str) -> Optional[str]:
|
|
11
|
-
"""Generate structured questions to gather necessary information for the requirement.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
str: A formatted string containing relevant questions
|
|
18
|
-
"""
|
|
19
|
-
prompt = f"""You are a helpful assistant that generates SPECIFIC questions in English for a code analysis team. The analysis team:
|
|
20
|
-
- Has access to the codebase but NO CONTEXT about the requirement
|
|
21
|
-
- Will search and analyze code based on your questions
|
|
22
|
-
- Needs complete context to understand what to look for
|
|
11
|
+
"""Generate structured questions to gather necessary information for the requirement."""
|
|
12
|
+
prompt = f"""You are a code analysis expert who helps developers understand existing system implementations. Generate specific questions to investigate:
|
|
13
|
+
- Current system implementations
|
|
14
|
+
- Existing interfaces and integration points
|
|
15
|
+
- Extension mechanisms and patterns
|
|
16
|
+
- Related components and their interactions
|
|
23
17
|
|
|
24
18
|
Key Instructions:
|
|
25
|
-
1.
|
|
26
|
-
2.
|
|
27
|
-
3.
|
|
28
|
-
4.
|
|
19
|
+
1. Focus on understanding the EXISTING system
|
|
20
|
+
2. Ask about interfaces, hooks, and extension points
|
|
21
|
+
3. Investigate integration patterns and dependencies
|
|
22
|
+
4. Explore current implementation details
|
|
29
23
|
|
|
30
24
|
For example:
|
|
31
|
-
BAD: "How
|
|
32
|
-
GOOD: "
|
|
25
|
+
BAD: "How should we implement the new feature?"
|
|
26
|
+
GOOD: "What are the existing extension points in the authentication system that we can use to add the new OAuth provider? Specifically, how does AuthProvider interface work and where is it currently used?"
|
|
33
27
|
|
|
34
|
-
Consider these aspects
|
|
28
|
+
Consider these investigation aspects:
|
|
35
29
|
|
|
36
|
-
1.
|
|
37
|
-
- "What
|
|
38
|
-
- "
|
|
39
|
-
- "
|
|
30
|
+
1. System Architecture:
|
|
31
|
+
- "What are the existing interfaces/classes that handle [functionality]?"
|
|
32
|
+
- "How is [feature] currently integrated with other components?"
|
|
33
|
+
- "Where are the extension points for [system component]?"
|
|
40
34
|
|
|
41
|
-
2.
|
|
42
|
-
- "
|
|
43
|
-
- "
|
|
44
|
-
- "
|
|
35
|
+
2. Implementation Details:
|
|
36
|
+
- "What is the current workflow for [operation] in the system?"
|
|
37
|
+
- "How does the system expose hooks/callbacks for [functionality]?"
|
|
38
|
+
- "Which interfaces/abstract classes are used for [feature] extensibility?"
|
|
45
39
|
|
|
46
|
-
3. Integration
|
|
47
|
-
- "
|
|
48
|
-
- "What
|
|
49
|
-
- "How
|
|
40
|
+
3. Integration Patterns:
|
|
41
|
+
- "How do existing components integrate with [system part]?"
|
|
42
|
+
- "What are the current integration points for [feature]?"
|
|
43
|
+
- "How does the system handle extensions to [component]?"
|
|
50
44
|
|
|
51
|
-
4.
|
|
52
|
-
- "
|
|
53
|
-
- "
|
|
54
|
-
- "
|
|
45
|
+
4. Extension Mechanisms:
|
|
46
|
+
- "What patterns are used for extending [functionality]?"
|
|
47
|
+
- "How do existing plugins/extensions connect to [system]?"
|
|
48
|
+
- "Where are the configuration points for [feature] customization?"
|
|
55
49
|
|
|
56
50
|
User Requirement:
|
|
57
51
|
{requirement}
|
|
58
52
|
|
|
59
53
|
Output Format:
|
|
60
54
|
<QUESTION>
|
|
61
|
-
[Write 3-5 specific questions
|
|
55
|
+
[Write 3-5 specific questions focusing on existing implementations and extension points. Each question should help understand how to integrate with or extend the current system]
|
|
62
56
|
</QUESTION>
|
|
63
57
|
"""
|
|
64
|
-
model = PlatformRegistry().
|
|
58
|
+
model = PlatformRegistry().get_thinking_platform()
|
|
65
59
|
response = model.chat_until_success(prompt)
|
|
66
60
|
response = re.search(r'<QUESTION>(.*?)</QUESTION>', response, re.DOTALL)
|
|
67
61
|
if response is None:
|
|
@@ -71,16 +65,15 @@ Output Format:
|
|
|
71
65
|
|
|
72
66
|
def find_relevant_information(user_input: str, root_dir: str) -> Tuple[List[Dict[str, str]], str]:
|
|
73
67
|
try:
|
|
74
|
-
PrettyOutput.print("
|
|
68
|
+
PrettyOutput.print("从代码库中查找文件...", OutputType.INFO)
|
|
75
69
|
codebase = CodeBase(root_dir)
|
|
76
70
|
question = make_question(user_input)
|
|
77
71
|
if question is None:
|
|
78
72
|
return [], ""
|
|
79
73
|
files_from_codebase, infomation = codebase.ask_codebase(question)
|
|
80
|
-
PrettyOutput.print("Find files by agent...", OutputType.INFO)
|
|
81
74
|
|
|
82
75
|
selected_files = select_files(files_from_codebase, os.getcwd())
|
|
83
76
|
return selected_files, infomation
|
|
84
77
|
except Exception:
|
|
85
|
-
PrettyOutput.print("
|
|
78
|
+
PrettyOutput.print("查找相关文件失败", OutputType.ERROR)
|
|
86
79
|
return [], ""
|