jarvis-ai-assistant 0.2.5__py3-none-any.whl → 0.2.7__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +78 -9
- jarvis/jarvis_agent/edit_file_handler.py +4 -4
- jarvis/jarvis_agent/jarvis.py +239 -7
- jarvis/jarvis_code_agent/code_agent.py +11 -11
- jarvis/jarvis_data/config_schema.json +5 -0
- jarvis/jarvis_multi_agent/main.py +1 -0
- jarvis/jarvis_stats/storage.py +2 -2
- jarvis/jarvis_tools/clear_memory.py +252 -0
- jarvis/jarvis_tools/retrieve_memory.py +212 -0
- jarvis/jarvis_tools/save_memory.py +203 -0
- jarvis/jarvis_utils/config.py +10 -0
- jarvis/jarvis_utils/globals.py +120 -1
- jarvis/jarvis_utils/input.py +5 -3
- jarvis/jarvis_utils/methodology.py +41 -6
- jarvis/jarvis_utils/output.py +1 -1
- jarvis/jarvis_utils/utils.py +59 -41
- {jarvis_ai_assistant-0.2.5.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.2.5.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/RECORD +23 -20
- {jarvis_ai_assistant-0.2.5.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.5.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.5.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.5.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -396,7 +396,7 @@ class Agent:
|
|
396
396
|
"""
|
397
397
|
return execute_tool_call(response, self)
|
398
398
|
|
399
|
-
def _complete_task(self) -> str:
|
399
|
+
def _complete_task(self, auto_completed: bool = False) -> str:
|
400
400
|
"""完成任务并生成总结(如果需要)
|
401
401
|
|
402
402
|
返回:
|
@@ -407,8 +407,26 @@ class Agent:
|
|
407
407
|
2. 对于子Agent: 可能会生成总结(如果启用)
|
408
408
|
3. 使用spinner显示生成状态
|
409
409
|
"""
|
410
|
+
satisfaction_feedback = ""
|
411
|
+
|
412
|
+
if not auto_completed and self.use_analysis:
|
413
|
+
if user_confirm("您对本次任务的完成是否满意?", True):
|
414
|
+
satisfaction_feedback = "\n\n用户对本次任务的完成表示满意。"
|
415
|
+
else:
|
416
|
+
feedback = self.multiline_inputer(
|
417
|
+
"请提供您的反馈意见(可留空直接回车):"
|
418
|
+
)
|
419
|
+
if feedback:
|
420
|
+
satisfaction_feedback = (
|
421
|
+
f"\n\n用户对本次任务的完成不满意,反馈意见如下:\n{feedback}"
|
422
|
+
)
|
423
|
+
else:
|
424
|
+
satisfaction_feedback = (
|
425
|
+
"\n\n用户对本次任务的完成不满意,未提供具体反馈意见。"
|
426
|
+
)
|
427
|
+
|
410
428
|
if self.use_analysis:
|
411
|
-
self._analysis_task()
|
429
|
+
self._analysis_task(satisfaction_feedback)
|
412
430
|
if self.need_summary:
|
413
431
|
print("📄 正在生成总结...")
|
414
432
|
self.session.prompt = self.summary_prompt
|
@@ -420,11 +438,13 @@ class Agent:
|
|
420
438
|
|
421
439
|
return "任务完成"
|
422
440
|
|
423
|
-
def _analysis_task(self):
|
441
|
+
def _analysis_task(self, satisfaction_feedback: str = ""):
|
424
442
|
print("🔍 正在分析任务...")
|
425
443
|
try:
|
426
444
|
# 让模型判断是否需要生成方法论
|
427
445
|
analysis_prompt = TASK_ANALYSIS_PROMPT
|
446
|
+
if satisfaction_feedback:
|
447
|
+
analysis_prompt += satisfaction_feedback
|
428
448
|
|
429
449
|
self.session.prompt = analysis_prompt
|
430
450
|
if not self.model:
|
@@ -452,6 +472,23 @@ class Agent:
|
|
452
472
|
else ""
|
453
473
|
)
|
454
474
|
|
475
|
+
# 检查工具列表并添加记忆工具相关提示
|
476
|
+
memory_prompts = ""
|
477
|
+
tool_registry = self.get_tool_registry()
|
478
|
+
if tool_registry:
|
479
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
480
|
+
|
481
|
+
# 如果有save_memory工具,添加相关提示
|
482
|
+
if "save_memory" in tool_names:
|
483
|
+
memory_prompts += "\n - 如果有关键信息需要记忆,请调用save_memory工具进行记忆:"
|
484
|
+
memory_prompts += "\n * project_long_term: 保存与当前项目相关的长期信息"
|
485
|
+
memory_prompts += "\n * global_long_term: 保存通用的信息、用户喜好、知识、方法等"
|
486
|
+
memory_prompts += "\n * short_term: 保存当前任务相关的临时信息"
|
487
|
+
|
488
|
+
# 如果有retrieve_memory工具,添加相关提示
|
489
|
+
if "retrieve_memory" in tool_names:
|
490
|
+
memory_prompts += "\n - 如果需要检索相关记忆信息,请调用retrieve_memory工具"
|
491
|
+
|
455
492
|
addon_prompt = f"""
|
456
493
|
<system_prompt>
|
457
494
|
请判断是否已经完成任务,如果已经完成:
|
@@ -461,7 +498,7 @@ class Agent:
|
|
461
498
|
- 仅包含一个操作
|
462
499
|
- 如果信息不明确,请请求用户补充
|
463
500
|
- 如果执行过程中连续失败5次,请使用ask_user询问用户操作
|
464
|
-
- 操作列表:{action_handlers}
|
501
|
+
- 操作列表:{action_handlers}{memory_prompts}
|
465
502
|
</system_prompt>
|
466
503
|
|
467
504
|
请继续。
|
@@ -547,7 +584,7 @@ class Agent:
|
|
547
584
|
continue
|
548
585
|
|
549
586
|
if self.auto_complete and ot("!!!COMPLETE!!!") in current_response:
|
550
|
-
return self._complete_task()
|
587
|
+
return self._complete_task(auto_completed=True)
|
551
588
|
|
552
589
|
# 获取用户输入
|
553
590
|
user_input = self.multiline_inputer(
|
@@ -560,7 +597,7 @@ class Agent:
|
|
560
597
|
continue
|
561
598
|
|
562
599
|
if not user_input:
|
563
|
-
return self._complete_task()
|
600
|
+
return self._complete_task(auto_completed=False)
|
564
601
|
|
565
602
|
except Exception as e:
|
566
603
|
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
@@ -571,6 +608,34 @@ class Agent:
|
|
571
608
|
return f"Task failed: {str(e)}"
|
572
609
|
|
573
610
|
def _first_run(self):
|
611
|
+
# 获取所有记忆标签并添加到提示中
|
612
|
+
from jarvis.jarvis_utils.globals import get_all_memory_tags
|
613
|
+
|
614
|
+
memory_tags = get_all_memory_tags()
|
615
|
+
memory_tags_prompt = ""
|
616
|
+
|
617
|
+
# 检查是否有save_memory工具
|
618
|
+
tool_registry = self.get_tool_registry()
|
619
|
+
has_save_memory = False
|
620
|
+
if tool_registry:
|
621
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
622
|
+
has_save_memory = "save_memory" in tool_names
|
623
|
+
|
624
|
+
# 如果有save_memory工具,添加记录关键信息的提示
|
625
|
+
if has_save_memory:
|
626
|
+
memory_tags_prompt = "\n\n💡 提示:在分析任务之前,建议使用 save_memory 工具将关键信息记录下来,便于后续检索和复用。"
|
627
|
+
|
628
|
+
if any(tags for tags in memory_tags.values()):
|
629
|
+
memory_tags_prompt += "\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
|
630
|
+
for memory_type, tags in memory_tags.items():
|
631
|
+
if tags:
|
632
|
+
type_name = {
|
633
|
+
"short_term": "短期记忆",
|
634
|
+
"project_long_term": "项目长期记忆",
|
635
|
+
"global_long_term": "全局长期记忆"
|
636
|
+
}.get(memory_type, memory_type)
|
637
|
+
memory_tags_prompt += f"\n- {type_name}: {', '.join(tags)}"
|
638
|
+
|
574
639
|
# 如果有上传文件,先上传文件
|
575
640
|
if self.model and self.model.support_upload_files():
|
576
641
|
if self.use_methodology:
|
@@ -583,12 +648,12 @@ class Agent:
|
|
583
648
|
msg = self.session.prompt
|
584
649
|
for handler in self.input_handler:
|
585
650
|
msg, _ = handler(msg, self)
|
586
|
-
self.session.prompt = f"{self.session.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
|
651
|
+
self.session.prompt = f"{self.session.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}{memory_tags_prompt}"
|
587
652
|
else:
|
588
653
|
if self.files:
|
589
|
-
self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。"
|
654
|
+
self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。{memory_tags_prompt}"
|
590
655
|
else:
|
591
|
-
self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
|
656
|
+
self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。{memory_tags_prompt}"
|
592
657
|
elif self.files:
|
593
658
|
if not self.model.upload_files(self.files):
|
594
659
|
PrettyOutput.print(
|
@@ -604,6 +669,10 @@ class Agent:
|
|
604
669
|
for handler in self.input_handler:
|
605
670
|
msg, _ = handler(msg, self)
|
606
671
|
self.session.prompt = f"{self.session.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
|
672
|
+
|
673
|
+
# 添加记忆标签提示
|
674
|
+
if memory_tags_prompt:
|
675
|
+
self.session.prompt = f"{self.session.prompt}{memory_tags_prompt}"
|
607
676
|
|
608
677
|
self.first = False
|
609
678
|
|
@@ -204,9 +204,9 @@ class EditFileHandler(OutputHandler):
|
|
204
204
|
found = False
|
205
205
|
|
206
206
|
if exact_search in modified_content:
|
207
|
-
#
|
207
|
+
# 直接执行替换(保留所有原始格式),只替换第一个匹配
|
208
208
|
modified_content = modified_content.replace(
|
209
|
-
exact_search, replace_text
|
209
|
+
exact_search, replace_text, 1
|
210
210
|
)
|
211
211
|
print(f"✅ 补丁 #{patch_count} 应用成功")
|
212
212
|
found = True
|
@@ -222,7 +222,7 @@ class EditFileHandler(OutputHandler):
|
|
222
222
|
stripped_replace = replace_text[1:-1]
|
223
223
|
if stripped_search in modified_content:
|
224
224
|
modified_content = modified_content.replace(
|
225
|
-
stripped_search, stripped_replace
|
225
|
+
stripped_search, stripped_replace, 1
|
226
226
|
)
|
227
227
|
print(f"✅ 补丁 #{patch_count} 应用成功 (自动去除首尾换行)")
|
228
228
|
found = True
|
@@ -251,7 +251,7 @@ class EditFileHandler(OutputHandler):
|
|
251
251
|
)
|
252
252
|
if indented_search in modified_content:
|
253
253
|
modified_content = modified_content.replace(
|
254
|
-
indented_search, indented_replace
|
254
|
+
indented_search, indented_replace, 1
|
255
255
|
)
|
256
256
|
print(
|
257
257
|
f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
|
jarvis/jarvis_agent/jarvis.py
CHANGED
@@ -4,7 +4,7 @@ import shutil
|
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Dict, Optional
|
7
|
+
from typing import Dict, Optional, List
|
8
8
|
|
9
9
|
import typer
|
10
10
|
import yaml # type: ignore
|
@@ -91,13 +91,19 @@ def _select_task(tasks: Dict[str, str]) -> str:
|
|
91
91
|
selected_task = tasks[task_names[choice - 1]]
|
92
92
|
PrettyOutput.print(f"将要执行任务:\n {selected_task}", OutputType.INFO)
|
93
93
|
# 询问是否需要补充信息
|
94
|
-
need_additional = user_confirm(
|
94
|
+
need_additional = user_confirm(
|
95
|
+
"需要为此任务添加补充信息吗?", default=False
|
96
|
+
)
|
95
97
|
if need_additional:
|
96
98
|
additional_input = get_multiline_input("请输入补充信息:")
|
97
99
|
if additional_input:
|
98
|
-
selected_task =
|
100
|
+
selected_task = (
|
101
|
+
f"{selected_task}\n\n补充信息:\n{additional_input}"
|
102
|
+
)
|
99
103
|
return selected_task
|
100
|
-
PrettyOutput.print(
|
104
|
+
PrettyOutput.print(
|
105
|
+
"无效的选择。请选择列表中的一个号码。", OutputType.WARNING
|
106
|
+
)
|
101
107
|
|
102
108
|
except (KeyboardInterrupt, EOFError):
|
103
109
|
return ""
|
@@ -183,6 +189,217 @@ def _get_and_run_task(agent: Agent, task_content: Optional[str] = None) -> None:
|
|
183
189
|
raise typer.Exit(code=0)
|
184
190
|
|
185
191
|
|
192
|
+
def _parse_selection(selection_str: str, max_value: int) -> List[int]:
|
193
|
+
"""解析用户输入的选择字符串,支持逗号分隔和范围选择
|
194
|
+
|
195
|
+
例如: "1,2,3,4-9,20" -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
|
196
|
+
"""
|
197
|
+
selected: set[int] = set()
|
198
|
+
parts = selection_str.split(",")
|
199
|
+
|
200
|
+
for part in parts:
|
201
|
+
part = part.strip()
|
202
|
+
if "-" in part:
|
203
|
+
# 处理范围选择
|
204
|
+
try:
|
205
|
+
start_str, end_str = part.split("-")
|
206
|
+
start_num = int(start_str.strip())
|
207
|
+
end_num = int(end_str.strip())
|
208
|
+
if 1 <= start_num <= max_value and 1 <= end_num <= max_value:
|
209
|
+
selected.update(range(start_num, end_num + 1))
|
210
|
+
except ValueError:
|
211
|
+
continue
|
212
|
+
else:
|
213
|
+
# 处理单个数字
|
214
|
+
try:
|
215
|
+
num = int(part)
|
216
|
+
if 1 <= num <= max_value:
|
217
|
+
selected.add(num)
|
218
|
+
except ValueError:
|
219
|
+
continue
|
220
|
+
|
221
|
+
return sorted(list(selected))
|
222
|
+
|
223
|
+
|
224
|
+
def _handle_share_methodology(config_file: Optional[str] = None) -> None:
|
225
|
+
"""处理方法论分享功能"""
|
226
|
+
from jarvis.jarvis_utils.config import (
|
227
|
+
get_central_methodology_repo,
|
228
|
+
get_methodology_dirs,
|
229
|
+
get_data_dir,
|
230
|
+
)
|
231
|
+
import glob
|
232
|
+
import json
|
233
|
+
import shutil
|
234
|
+
|
235
|
+
# 获取中心方法论仓库配置
|
236
|
+
central_repo = get_central_methodology_repo()
|
237
|
+
if not central_repo:
|
238
|
+
PrettyOutput.print(
|
239
|
+
"错误:未配置中心方法论仓库(JARVIS_CENTRAL_METHODOLOGY_REPO)",
|
240
|
+
OutputType.ERROR,
|
241
|
+
)
|
242
|
+
PrettyOutput.print("请在配置文件中设置中心方法论仓库的Git地址", OutputType.INFO)
|
243
|
+
raise typer.Exit(code=1)
|
244
|
+
|
245
|
+
# 克隆或更新中心方法论仓库
|
246
|
+
central_repo_path = os.path.join(get_data_dir(), "central_methodology_repo")
|
247
|
+
if not os.path.exists(central_repo_path):
|
248
|
+
PrettyOutput.print(f"正在克隆中心方法论仓库...", OutputType.INFO)
|
249
|
+
subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
|
250
|
+
else:
|
251
|
+
PrettyOutput.print(f"正在更新中心方法论仓库...", OutputType.INFO)
|
252
|
+
subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
|
253
|
+
|
254
|
+
# 获取中心仓库中已有的方法论
|
255
|
+
existing_methodologies = {} # 改为字典,存储 problem_type -> content 的映射
|
256
|
+
for filepath in glob.glob(os.path.join(central_repo_path, "*.json")):
|
257
|
+
try:
|
258
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
259
|
+
methodology = json.load(f)
|
260
|
+
problem_type = methodology.get("problem_type", "")
|
261
|
+
content = methodology.get("content", "")
|
262
|
+
if problem_type and content:
|
263
|
+
existing_methodologies[problem_type] = content
|
264
|
+
except Exception:
|
265
|
+
pass
|
266
|
+
|
267
|
+
# 获取所有方法论目录
|
268
|
+
from jarvis.jarvis_utils.methodology import _get_methodology_directory
|
269
|
+
|
270
|
+
methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
|
271
|
+
|
272
|
+
# 收集所有方法论文件(排除中心仓库目录和已存在的方法论)
|
273
|
+
all_methodologies = {}
|
274
|
+
methodology_files = []
|
275
|
+
seen_problem_types = set() # 用于去重
|
276
|
+
|
277
|
+
for directory in set(methodology_dirs):
|
278
|
+
# 跳过中心仓库目录
|
279
|
+
if os.path.abspath(directory) == os.path.abspath(central_repo_path):
|
280
|
+
continue
|
281
|
+
|
282
|
+
if not os.path.isdir(directory):
|
283
|
+
continue
|
284
|
+
|
285
|
+
for filepath in glob.glob(os.path.join(directory, "*.json")):
|
286
|
+
try:
|
287
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
288
|
+
methodology = json.load(f)
|
289
|
+
problem_type = methodology.get("problem_type", "")
|
290
|
+
content = methodology.get("content", "")
|
291
|
+
# 基于内容判断是否已存在于中心仓库
|
292
|
+
is_duplicate = False
|
293
|
+
if problem_type in existing_methodologies:
|
294
|
+
# 如果problem_type相同,比较内容
|
295
|
+
if (
|
296
|
+
content.strip()
|
297
|
+
== existing_methodologies[problem_type].strip()
|
298
|
+
):
|
299
|
+
is_duplicate = True
|
300
|
+
|
301
|
+
# 排除已存在于中心仓库的方法论(基于内容),以及本地重复的方法论
|
302
|
+
if (
|
303
|
+
problem_type
|
304
|
+
and content
|
305
|
+
and not is_duplicate
|
306
|
+
and problem_type not in seen_problem_types
|
307
|
+
):
|
308
|
+
methodology_files.append(
|
309
|
+
{
|
310
|
+
"path": filepath,
|
311
|
+
"problem_type": problem_type,
|
312
|
+
"directory": directory,
|
313
|
+
}
|
314
|
+
)
|
315
|
+
all_methodologies[problem_type] = methodology
|
316
|
+
seen_problem_types.add(problem_type)
|
317
|
+
except Exception:
|
318
|
+
pass
|
319
|
+
|
320
|
+
if not methodology_files:
|
321
|
+
PrettyOutput.print(
|
322
|
+
"没有找到新的方法论文件(所有方法论可能已存在于中心仓库)",
|
323
|
+
OutputType.WARNING,
|
324
|
+
)
|
325
|
+
raise typer.Exit(code=0)
|
326
|
+
|
327
|
+
# 显示可选的方法论
|
328
|
+
methodology_list = ["\n可分享的方法论(已排除中心仓库中已有的):"]
|
329
|
+
for i, meth in enumerate(methodology_files, 1):
|
330
|
+
dir_name = os.path.basename(meth["directory"])
|
331
|
+
methodology_list.append(f"[{i}] {meth['problem_type']} (来自: {dir_name})")
|
332
|
+
|
333
|
+
# 一次性打印所有方法论
|
334
|
+
PrettyOutput.print("\n".join(methodology_list), OutputType.INFO)
|
335
|
+
|
336
|
+
# 让用户选择要分享的方法论
|
337
|
+
while True:
|
338
|
+
try:
|
339
|
+
choice_str = prompt(
|
340
|
+
"\n请选择要分享的方法论编号(支持格式: 1,2,3,4-9,20 或 all):"
|
341
|
+
).strip()
|
342
|
+
if choice_str == "0":
|
343
|
+
raise typer.Exit(code=0)
|
344
|
+
|
345
|
+
selected_methodologies = []
|
346
|
+
if choice_str.lower() == "all":
|
347
|
+
selected_methodologies = methodology_files
|
348
|
+
else:
|
349
|
+
selected_indices = _parse_selection(choice_str, len(methodology_files))
|
350
|
+
if not selected_indices:
|
351
|
+
PrettyOutput.print("无效的选择", OutputType.WARNING)
|
352
|
+
continue
|
353
|
+
selected_methodologies = [
|
354
|
+
methodology_files[i - 1] for i in selected_indices
|
355
|
+
]
|
356
|
+
|
357
|
+
# 确认操作
|
358
|
+
share_list = ["\n将要分享以下方法论到中心仓库:"]
|
359
|
+
for meth in selected_methodologies:
|
360
|
+
share_list.append(f"- {meth['problem_type']}")
|
361
|
+
PrettyOutput.print("\n".join(share_list), OutputType.INFO)
|
362
|
+
|
363
|
+
if not user_confirm("确认分享这些方法论吗?"):
|
364
|
+
continue
|
365
|
+
|
366
|
+
# 复制选中的方法论到中心仓库
|
367
|
+
copied_list = []
|
368
|
+
for meth in selected_methodologies:
|
369
|
+
src_file = meth["path"]
|
370
|
+
dst_file = os.path.join(central_repo_path, os.path.basename(src_file))
|
371
|
+
shutil.copy2(src_file, dst_file)
|
372
|
+
copied_list.append(f"已复制: {meth['problem_type']}")
|
373
|
+
|
374
|
+
# 一次性显示所有复制结果
|
375
|
+
if copied_list:
|
376
|
+
PrettyOutput.print("\n".join(copied_list), OutputType.SUCCESS)
|
377
|
+
|
378
|
+
# 提交并推送更改
|
379
|
+
PrettyOutput.print("\n正在提交更改...", OutputType.INFO)
|
380
|
+
subprocess.run(["git", "add", "."], cwd=central_repo_path, check=True)
|
381
|
+
|
382
|
+
commit_msg = f"Add {len(selected_methodologies)} methodology(ies) from local collection"
|
383
|
+
subprocess.run(
|
384
|
+
["git", "commit", "-m", commit_msg], cwd=central_repo_path, check=True
|
385
|
+
)
|
386
|
+
|
387
|
+
PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
|
388
|
+
subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
|
389
|
+
|
390
|
+
PrettyOutput.print("\n方法论已成功分享到中心仓库!", OutputType.SUCCESS)
|
391
|
+
break
|
392
|
+
|
393
|
+
except ValueError:
|
394
|
+
PrettyOutput.print("请输入有效的数字", OutputType.WARNING)
|
395
|
+
except subprocess.CalledProcessError as e:
|
396
|
+
PrettyOutput.print(f"Git操作失败: {str(e)}", OutputType.ERROR)
|
397
|
+
raise typer.Exit(code=1)
|
398
|
+
except Exception as e:
|
399
|
+
PrettyOutput.print(f"分享方法论时出错: {str(e)}", OutputType.ERROR)
|
400
|
+
raise typer.Exit(code=1)
|
401
|
+
|
402
|
+
|
186
403
|
@app.callback(invoke_without_command=True)
|
187
404
|
def run_cli(
|
188
405
|
ctx: typer.Context,
|
@@ -191,17 +408,24 @@ def run_cli(
|
|
191
408
|
"--llm_type",
|
192
409
|
help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)",
|
193
410
|
),
|
194
|
-
task: Optional[str] = typer.Option(
|
411
|
+
task: Optional[str] = typer.Option(
|
412
|
+
None, "-t", "--task", help="从命令行直接输入任务内容"
|
413
|
+
),
|
195
414
|
model_group: Optional[str] = typer.Option(
|
196
415
|
None, "--llm_group", help="使用的模型组,覆盖配置文件中的设置"
|
197
416
|
),
|
198
|
-
config_file: Optional[str] = typer.Option(
|
417
|
+
config_file: Optional[str] = typer.Option(
|
418
|
+
None, "-f", "--config", help="自定义配置文件路径"
|
419
|
+
),
|
199
420
|
restore_session: bool = typer.Option(
|
200
421
|
False,
|
201
422
|
"--restore-session",
|
202
423
|
help="从 .jarvis/saved_session.json 恢复会话",
|
203
424
|
),
|
204
425
|
edit: bool = typer.Option(False, "-e", "--edit", help="编辑配置文件"),
|
426
|
+
share_methodology: bool = typer.Option(
|
427
|
+
False, "--share-methodology", help="分享本地方法论到中心方法论仓库"
|
428
|
+
),
|
205
429
|
) -> None:
|
206
430
|
"""Jarvis AI assistant command-line interface."""
|
207
431
|
if ctx.invoked_subcommand is not None:
|
@@ -209,7 +433,15 @@ def run_cli(
|
|
209
433
|
|
210
434
|
_handle_edit_mode(edit, config_file)
|
211
435
|
|
212
|
-
|
436
|
+
# 处理方法论分享
|
437
|
+
if share_methodology:
|
438
|
+
init_env("", config_file=config_file) # 初始化配置但不显示欢迎信息
|
439
|
+
_handle_share_methodology(config_file)
|
440
|
+
raise typer.Exit(code=0)
|
441
|
+
|
442
|
+
init_env(
|
443
|
+
"欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file
|
444
|
+
)
|
213
445
|
|
214
446
|
try:
|
215
447
|
agent = _initialize_agent(llm_type, model_group, restore_session)
|
@@ -65,6 +65,9 @@ class CodeAgent:
|
|
65
65
|
"ask_user",
|
66
66
|
"read_code",
|
67
67
|
"rewrite_file",
|
68
|
+
"save_memory",
|
69
|
+
"retrieve_memory",
|
70
|
+
"clear_memory",
|
68
71
|
]
|
69
72
|
)
|
70
73
|
code_system_prompt = """
|
@@ -269,7 +272,7 @@ class CodeAgent:
|
|
269
272
|
return
|
270
273
|
|
271
274
|
PrettyOutput.print(
|
272
|
-
"⚠️
|
275
|
+
"⚠️ 正在修改git换行符敏感设置,这会影响所有文件的换行符处理方式",
|
273
276
|
OutputType.WARNING,
|
274
277
|
)
|
275
278
|
print("将进行以下设置:")
|
@@ -277,18 +280,15 @@ class CodeAgent:
|
|
277
280
|
current = current_settings.get(key, "未设置")
|
278
281
|
print(f" {key}: {current} -> {value}")
|
279
282
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
+
# 直接执行设置,不需要用户确认
|
284
|
+
for key, value in target_settings.items():
|
285
|
+
subprocess.run(["git", "config", key, value], check=True)
|
283
286
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
+
# 对于Windows系统,提示用户可以创建.gitattributes文件
|
288
|
+
if sys.platform.startswith("win"):
|
289
|
+
self._handle_windows_line_endings()
|
287
290
|
|
288
|
-
|
289
|
-
else:
|
290
|
-
print("❌ 用户取消修改git换行符敏感设置")
|
291
|
-
sys.exit(0)
|
291
|
+
print("✅ git换行符敏感设置已更新")
|
292
292
|
|
293
293
|
def _handle_windows_line_endings(self) -> None:
|
294
294
|
"""在Windows系统上处理换行符问题,提供建议而非强制修改"""
|
@@ -225,6 +225,11 @@
|
|
225
225
|
},
|
226
226
|
"default": []
|
227
227
|
},
|
228
|
+
"JARVIS_CENTRAL_METHODOLOGY_REPO": {
|
229
|
+
"type": "string",
|
230
|
+
"description": "中心方法论Git仓库地址,该仓库会自动添加到方法论加载路径中",
|
231
|
+
"default": ""
|
232
|
+
},
|
228
233
|
"JARVIS_PRINT_PROMPT": {
|
229
234
|
"type": "boolean",
|
230
235
|
"description": "是否打印提示",
|
@@ -7,6 +7,7 @@ import yaml
|
|
7
7
|
from jarvis.jarvis_multi_agent import MultiAgent
|
8
8
|
from jarvis.jarvis_utils.input import get_multiline_input
|
9
9
|
from jarvis.jarvis_utils.utils import init_env
|
10
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
10
11
|
|
11
12
|
app = typer.Typer(help="多智能体系统启动器")
|
12
13
|
|
jarvis/jarvis_stats/storage.py
CHANGED
@@ -245,7 +245,7 @@ class StatsStorage:
|
|
245
245
|
metrics_from_meta = set(meta.get("metrics", {}).keys())
|
246
246
|
|
247
247
|
# 扫描所有数据文件获取实际存在的指标
|
248
|
-
metrics_from_data = set()
|
248
|
+
metrics_from_data: set[str] = set()
|
249
249
|
for data_file in self.data_dir.glob("stats_*.json"):
|
250
250
|
try:
|
251
251
|
data = self._load_json(data_file)
|
@@ -285,7 +285,7 @@ class StatsStorage:
|
|
285
285
|
return {}
|
286
286
|
|
287
287
|
# 聚合数据
|
288
|
-
aggregated = defaultdict(
|
288
|
+
aggregated: Dict[str, Dict[str, Any]] = defaultdict(
|
289
289
|
lambda: {
|
290
290
|
"count": 0,
|
291
291
|
"sum": 0,
|