jarvis-ai-assistant 0.1.111__py3-none-any.whl → 0.1.113__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 +72 -41
- 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 +8 -5
- 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 +69 -28
- jarvis_ai_assistant-0.1.113.dist-info/METADATA +460 -0
- jarvis_ai_assistant-0.1.113.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.113.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.113.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.113.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.113.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
from ast import Tuple
|
|
2
3
|
import time
|
|
3
4
|
from typing import Callable, Dict, List, Optional
|
|
4
5
|
|
|
@@ -33,12 +34,14 @@ class Agent:
|
|
|
33
34
|
system_prompt: str,
|
|
34
35
|
name: str = "Jarvis",
|
|
35
36
|
is_sub_agent: bool = False,
|
|
36
|
-
tool_registry: Optional[ToolRegistry] = None,
|
|
37
|
-
platform: Optional[BasePlatform] = None,
|
|
37
|
+
tool_registry: Optional[ToolRegistry|List[str]] = None,
|
|
38
|
+
platform: Optional[BasePlatform]|Optional[str] = None,
|
|
39
|
+
model_name: Optional[str] = None,
|
|
38
40
|
summary_prompt: Optional[str] = None,
|
|
39
41
|
auto_complete: Optional[bool] = None,
|
|
40
42
|
output_handler_before_tool: Optional[List[Callable]] = None,
|
|
41
43
|
output_handler_after_tool: Optional[List[Callable]] = None,
|
|
44
|
+
input_handler: Optional[List[Callable]] = None,
|
|
42
45
|
use_methodology: Optional[bool] = None,
|
|
43
46
|
record_methodology: Optional[bool] = None,
|
|
44
47
|
need_summary: Optional[bool] = None,
|
|
@@ -61,12 +64,35 @@ class Agent:
|
|
|
61
64
|
need_summary: Whether to generate summaries
|
|
62
65
|
max_context_length: Maximum context length
|
|
63
66
|
"""
|
|
64
|
-
PrettyOutput.print(f"
|
|
67
|
+
PrettyOutput.print(f"欢迎使用Jarvis,你的AI助手,正在初始化...", OutputType.SYSTEM)
|
|
68
|
+
|
|
69
|
+
# 初始化平台和模型
|
|
65
70
|
if platform is not None:
|
|
66
|
-
|
|
71
|
+
if isinstance(platform, str):
|
|
72
|
+
self.model = PlatformRegistry().create_platform(platform)
|
|
73
|
+
if self.model is None:
|
|
74
|
+
PrettyOutput.print(f"平台 {platform} 不存在,将使用普通模型", OutputType.WARNING)
|
|
75
|
+
self.model = PlatformRegistry().get_normal_platform()
|
|
76
|
+
else:
|
|
77
|
+
self.model = platform
|
|
67
78
|
else:
|
|
68
79
|
self.model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
69
|
-
|
|
80
|
+
|
|
81
|
+
if model_name is not None:
|
|
82
|
+
self.model.set_model_name(model_name)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# 初始化工具
|
|
86
|
+
if tool_registry is not None:
|
|
87
|
+
if isinstance(tool_registry, ToolRegistry):
|
|
88
|
+
self.tool_registry = tool_registry
|
|
89
|
+
elif isinstance(tool_registry, List):
|
|
90
|
+
self.tool_registry = ToolRegistry()
|
|
91
|
+
self.tool_registry.use_tools(tool_registry)
|
|
92
|
+
else:
|
|
93
|
+
self.tool_registry = ToolRegistry()
|
|
94
|
+
|
|
95
|
+
|
|
70
96
|
self.record_methodology = record_methodology if record_methodology is not None else is_record_methodology()
|
|
71
97
|
self.use_methodology = use_methodology if use_methodology is not None else is_use_methodology()
|
|
72
98
|
self.name = name
|
|
@@ -75,6 +101,7 @@ class Agent:
|
|
|
75
101
|
self.conversation_length = 0 # Use length counter instead
|
|
76
102
|
self.system_prompt = system_prompt
|
|
77
103
|
self.need_summary = need_summary if need_summary is not None else is_need_summary()
|
|
104
|
+
self.input_handler = input_handler if input_handler is not None else []
|
|
78
105
|
# Load configuration from environment variables
|
|
79
106
|
self.output_handler_before_tool = output_handler_before_tool if output_handler_before_tool else []
|
|
80
107
|
self.output_handler_after_tool = output_handler_after_tool if output_handler_after_tool else []
|
|
@@ -96,11 +123,11 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
96
123
|
|
|
97
124
|
self.auto_complete = auto_complete if auto_complete is not None else is_auto_complete()
|
|
98
125
|
|
|
99
|
-
PrettyOutput.section(f"Jarvis
|
|
126
|
+
PrettyOutput.section(f"Jarvis 初始化完成 - 使用 {self.model.name()} 模型", OutputType.SYSTEM)
|
|
100
127
|
|
|
101
128
|
tools = self.tool_registry.get_all_tools()
|
|
102
129
|
if tools:
|
|
103
|
-
PrettyOutput.section(f"
|
|
130
|
+
PrettyOutput.section(f"可用工具: {', '.join([tool['name'] for tool in tools])}", OutputType.SYSTEM)
|
|
104
131
|
|
|
105
132
|
|
|
106
133
|
tools_prompt = self.tool_registry.load_tools()
|
|
@@ -181,12 +208,16 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
181
208
|
Will retry with exponential backoff up to 30 seconds between retries
|
|
182
209
|
"""
|
|
183
210
|
sleep_time = 5
|
|
211
|
+
|
|
212
|
+
for handler in self.input_handler:
|
|
213
|
+
message = handler(message)
|
|
214
|
+
|
|
184
215
|
while True:
|
|
185
|
-
ret = self.model.chat_until_success(message)
|
|
216
|
+
ret = self.model.chat_until_success(message) # type: ignore
|
|
186
217
|
if ret:
|
|
187
218
|
return ret
|
|
188
219
|
else:
|
|
189
|
-
PrettyOutput.print(f"
|
|
220
|
+
PrettyOutput.print(f"模型调用失败,正在重试... 等待 {sleep_time}s", OutputType.INFO)
|
|
190
221
|
time.sleep(sleep_time)
|
|
191
222
|
sleep_time *= 2
|
|
192
223
|
if sleep_time > 30:
|
|
@@ -209,7 +240,7 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
209
240
|
"""
|
|
210
241
|
# Create a new model instance to summarize, avoid affecting the main conversation
|
|
211
242
|
|
|
212
|
-
PrettyOutput.print("
|
|
243
|
+
PrettyOutput.print("总结对话历史,准备生成摘要,开始新对话...", OutputType.PROGRESS)
|
|
213
244
|
|
|
214
245
|
prompt = """Please summarize the key information from the previous conversation, including:
|
|
215
246
|
1. Current task objective
|
|
@@ -222,7 +253,7 @@ Please describe in concise bullet points, highlighting important information. Do
|
|
|
222
253
|
"""
|
|
223
254
|
|
|
224
255
|
try:
|
|
225
|
-
summary = self.
|
|
256
|
+
summary = self._call_model(self.prompt + "\n" + prompt)
|
|
226
257
|
|
|
227
258
|
# 清空当前对话历史,但保留系统消息
|
|
228
259
|
self.conversation_length = 0 # Reset conversation length
|
|
@@ -237,7 +268,7 @@ Please continue the task based on the above information.
|
|
|
237
268
|
self.conversation_length = len(self.prompt) # 设置新的起始长度
|
|
238
269
|
|
|
239
270
|
except Exception as e:
|
|
240
|
-
PrettyOutput.print(f"
|
|
271
|
+
PrettyOutput.print(f"总结对话历史失败: {str(e)}", OutputType.ERROR)
|
|
241
272
|
|
|
242
273
|
def _complete_task(self) -> str:
|
|
243
274
|
"""Complete the current task and generate summary if needed.
|
|
@@ -249,7 +280,7 @@ Please continue the task based on the above information.
|
|
|
249
280
|
- For main agent: May generate methodology if enabled
|
|
250
281
|
- For sub-agent: May generate summary if enabled
|
|
251
282
|
"""
|
|
252
|
-
PrettyOutput.section("
|
|
283
|
+
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
253
284
|
|
|
254
285
|
if not self.is_sub_agent:
|
|
255
286
|
if self.record_methodology:
|
|
@@ -272,18 +303,18 @@ Please continue the task based on the above information.
|
|
|
272
303
|
if tool_calls:
|
|
273
304
|
self.tool_registry.handle_tool_calls(tool_calls)
|
|
274
305
|
except Exception as e:
|
|
275
|
-
PrettyOutput.print(f"
|
|
306
|
+
PrettyOutput.print(f"处理方法论生成失败: {str(e)}", OutputType.ERROR)
|
|
276
307
|
|
|
277
308
|
except Exception as e:
|
|
278
|
-
PrettyOutput.print(f"
|
|
309
|
+
PrettyOutput.print(f"生成方法论失败: {str(e)}", OutputType.ERROR)
|
|
279
310
|
|
|
280
|
-
return "
|
|
311
|
+
return "任务完成"
|
|
281
312
|
|
|
282
313
|
if self.need_summary:
|
|
283
314
|
self.prompt = self.summary_prompt
|
|
284
315
|
return self._call_model(self.prompt)
|
|
285
316
|
|
|
286
|
-
return "
|
|
317
|
+
return "任务完成"
|
|
287
318
|
|
|
288
319
|
|
|
289
320
|
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str:
|
|
@@ -306,12 +337,12 @@ Please continue the task based on the above information.
|
|
|
306
337
|
add_agent(self.name)
|
|
307
338
|
|
|
308
339
|
try:
|
|
309
|
-
PrettyOutput.section("
|
|
340
|
+
PrettyOutput.section("准备环境", OutputType.PLANNING)
|
|
310
341
|
if file_list:
|
|
311
|
-
self.model.upload_files(file_list)
|
|
342
|
+
self.model.upload_files(file_list) # type: ignore
|
|
312
343
|
|
|
313
344
|
# 显示任务开始
|
|
314
|
-
PrettyOutput.section(f"
|
|
345
|
+
PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
|
|
315
346
|
|
|
316
347
|
if self.first and self.use_methodology:
|
|
317
348
|
self.prompt = f"{user_input}\n\n{load_methodology(user_input)}"
|
|
@@ -322,7 +353,7 @@ Please continue the task based on the above information.
|
|
|
322
353
|
while True:
|
|
323
354
|
try:
|
|
324
355
|
# 显示思考状态
|
|
325
|
-
PrettyOutput.print("
|
|
356
|
+
PrettyOutput.print("正在分析任务...", OutputType.PROGRESS)
|
|
326
357
|
|
|
327
358
|
# 累加对话长度
|
|
328
359
|
self.conversation_length += get_context_token_count(self.prompt)
|
|
@@ -342,13 +373,13 @@ Please continue the task based on the above information.
|
|
|
342
373
|
try:
|
|
343
374
|
result = Agent._extract_tool_calls(current_response)
|
|
344
375
|
except Exception as e:
|
|
345
|
-
PrettyOutput.print(f"
|
|
376
|
+
PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
|
|
346
377
|
self.prompt += f"Tool call error: {str(e)}"
|
|
347
378
|
continue
|
|
348
379
|
|
|
349
380
|
if len(result) > 0:
|
|
350
|
-
if not self.execute_tool_confirm or user_confirm(f"
|
|
351
|
-
PrettyOutput.print("
|
|
381
|
+
if not self.execute_tool_confirm or user_confirm(f"执行工具调用: {result[0]['name']}?"):
|
|
382
|
+
PrettyOutput.print("正在执行工具调用...", OutputType.PROGRESS)
|
|
352
383
|
tool_result = self.tool_registry.handle_tool_calls(result)
|
|
353
384
|
self.prompt += tool_result
|
|
354
385
|
|
|
@@ -362,7 +393,7 @@ Please continue the task based on the above information.
|
|
|
362
393
|
return self._complete_task()
|
|
363
394
|
|
|
364
395
|
# 获取用户输入
|
|
365
|
-
user_input = get_multiline_input(f"{self.name}:
|
|
396
|
+
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行来结束当前任务:")
|
|
366
397
|
|
|
367
398
|
if user_input:
|
|
368
399
|
self.prompt = user_input
|
|
@@ -372,11 +403,11 @@ Please continue the task based on the above information.
|
|
|
372
403
|
return self._complete_task()
|
|
373
404
|
|
|
374
405
|
except Exception as e:
|
|
375
|
-
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
406
|
+
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
|
376
407
|
return f"Task failed: {str(e)}"
|
|
377
408
|
|
|
378
409
|
except Exception as e:
|
|
379
|
-
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
410
|
+
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
|
380
411
|
return f"Task failed: {str(e)}"
|
|
381
412
|
|
|
382
413
|
finally:
|
|
@@ -391,7 +422,7 @@ Please continue the task based on the above information.
|
|
|
391
422
|
3. Reset conversation length counter
|
|
392
423
|
"""
|
|
393
424
|
self.prompt = ""
|
|
394
|
-
self.model.reset()
|
|
425
|
+
self.model.reset() # type: ignore
|
|
395
426
|
self.conversation_length = 0 # Reset conversation length
|
|
396
427
|
|
|
397
428
|
|
|
@@ -414,9 +445,9 @@ def _load_tasks() -> dict:
|
|
|
414
445
|
if desc: # Ensure description is not empty
|
|
415
446
|
tasks[str(name)] = str(desc)
|
|
416
447
|
else:
|
|
417
|
-
PrettyOutput.print("
|
|
448
|
+
PrettyOutput.print("警告: ~/.jarvis/pre-command 文件应该包含一个字典,键为任务名称,值为任务描述", OutputType.ERROR)
|
|
418
449
|
except Exception as e:
|
|
419
|
-
PrettyOutput.print(f"
|
|
450
|
+
PrettyOutput.print(f"加载 ~/.jarvis/pre-command 文件失败: {str(e)}", OutputType.ERROR)
|
|
420
451
|
|
|
421
452
|
# Check .jarvis/pre-command in current directory
|
|
422
453
|
if os.path.exists(".jarvis/pre-command"):
|
|
@@ -430,9 +461,9 @@ def _load_tasks() -> dict:
|
|
|
430
461
|
if desc: # Ensure description is not empty
|
|
431
462
|
tasks[str(name)] = str(desc)
|
|
432
463
|
else:
|
|
433
|
-
PrettyOutput.print("
|
|
464
|
+
PrettyOutput.print("警告: .jarvis/pre-command 文件应该包含一个字典,键为任务名称,值为任务描述", OutputType.ERROR)
|
|
434
465
|
except Exception as e:
|
|
435
|
-
PrettyOutput.print(f"
|
|
466
|
+
PrettyOutput.print(f"加载 .jarvis/pre-command 文件失败: {str(e)}", OutputType.ERROR)
|
|
436
467
|
|
|
437
468
|
|
|
438
469
|
if is_use_methodology():
|
|
@@ -455,17 +486,17 @@ def _select_task(tasks: dict) -> str:
|
|
|
455
486
|
# Convert tasks to list for ordered display
|
|
456
487
|
task_names = list(tasks.keys())
|
|
457
488
|
|
|
458
|
-
task_list = ["
|
|
489
|
+
task_list = ["可用任务:"]
|
|
459
490
|
for i, name in enumerate(task_names, 1):
|
|
460
491
|
task_list.append(f"[{i}] {name}")
|
|
461
|
-
task_list.append("[0]
|
|
492
|
+
task_list.append("[0] 跳过预定义任务")
|
|
462
493
|
PrettyOutput.print("\n".join(task_list), OutputType.INFO)
|
|
463
494
|
|
|
464
495
|
|
|
465
496
|
while True:
|
|
466
497
|
try:
|
|
467
498
|
choice = prompt(
|
|
468
|
-
"\
|
|
499
|
+
"\n请选择一个任务编号(0 跳过预定义任务):",
|
|
469
500
|
).strip()
|
|
470
501
|
|
|
471
502
|
if not choice:
|
|
@@ -478,14 +509,14 @@ def _select_task(tasks: dict) -> str:
|
|
|
478
509
|
selected_name = task_names[choice - 1]
|
|
479
510
|
return tasks[selected_name] # Return the task description
|
|
480
511
|
else:
|
|
481
|
-
PrettyOutput.print("
|
|
512
|
+
PrettyOutput.print("无效的选择。请选择列表中的一个号码。", OutputType.WARNING)
|
|
482
513
|
|
|
483
514
|
except KeyboardInterrupt:
|
|
484
515
|
return "" # Return empty on Ctrl+C
|
|
485
516
|
except EOFError:
|
|
486
517
|
return "" # Return empty on Ctrl+D
|
|
487
518
|
except Exception as e:
|
|
488
|
-
PrettyOutput.print(f"
|
|
519
|
+
PrettyOutput.print(f"选择任务失败: {str(e)}", OutputType.ERROR)
|
|
489
520
|
continue
|
|
490
521
|
|
|
491
522
|
origin_agent_system_prompt = """You are Jarvis, an AI assistant with powerful problem-solving capabilities.
|
|
@@ -529,22 +560,22 @@ def main():
|
|
|
529
560
|
if tasks:
|
|
530
561
|
selected_task = _select_task(tasks)
|
|
531
562
|
if selected_task:
|
|
532
|
-
PrettyOutput.print(f"
|
|
563
|
+
PrettyOutput.print(f"执行任务: {selected_task}", OutputType.INFO)
|
|
533
564
|
agent.run(selected_task, args.files)
|
|
534
565
|
return 0
|
|
535
566
|
|
|
536
567
|
# 如果没有选择预定义任务,进入交互模式
|
|
537
568
|
while True:
|
|
538
569
|
try:
|
|
539
|
-
user_input = get_multiline_input("
|
|
570
|
+
user_input = get_multiline_input("请输入你的任务(输入空行退出):")
|
|
540
571
|
if not user_input:
|
|
541
572
|
break
|
|
542
573
|
agent.run(user_input, args.files)
|
|
543
574
|
except Exception as e:
|
|
544
|
-
PrettyOutput.print(f"
|
|
575
|
+
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
545
576
|
|
|
546
577
|
except Exception as e:
|
|
547
|
-
PrettyOutput.print(f"
|
|
578
|
+
PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
|
|
548
579
|
return 1
|
|
549
580
|
|
|
550
581
|
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")
|