jarvis-ai-assistant 0.3.26__py3-none-any.whl → 0.3.28__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.
Files changed (62) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +303 -177
  3. jarvis/jarvis_agent/agent_manager.py +6 -0
  4. jarvis/jarvis_agent/config.py +92 -0
  5. jarvis/jarvis_agent/config_editor.py +1 -1
  6. jarvis/jarvis_agent/event_bus.py +48 -0
  7. jarvis/jarvis_agent/file_methodology_manager.py +1 -3
  8. jarvis/jarvis_agent/jarvis.py +77 -36
  9. jarvis/jarvis_agent/memory_manager.py +70 -3
  10. jarvis/jarvis_agent/prompt_manager.py +82 -0
  11. jarvis/jarvis_agent/run_loop.py +130 -0
  12. jarvis/jarvis_agent/shell_input_handler.py +1 -1
  13. jarvis/jarvis_agent/task_analyzer.py +89 -11
  14. jarvis/jarvis_agent/task_manager.py +26 -0
  15. jarvis/jarvis_agent/user_interaction.py +42 -0
  16. jarvis/jarvis_code_agent/code_agent.py +18 -3
  17. jarvis/jarvis_code_agent/lint.py +5 -5
  18. jarvis/jarvis_code_analysis/code_review.py +0 -1
  19. jarvis/jarvis_data/config_schema.json +7 -6
  20. jarvis/jarvis_git_squash/main.py +6 -1
  21. jarvis/jarvis_git_utils/git_commiter.py +51 -16
  22. jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
  23. jarvis/jarvis_memory_organizer/memory_organizer.py +2 -5
  24. jarvis/jarvis_methodology/main.py +0 -2
  25. jarvis/jarvis_multi_agent/__init__.py +3 -3
  26. jarvis/jarvis_platform/base.py +5 -6
  27. jarvis/jarvis_platform/registry.py +1 -1
  28. jarvis/jarvis_platform/yuanbao.py +0 -1
  29. jarvis/jarvis_platform_manager/main.py +28 -11
  30. jarvis/jarvis_platform_manager/service.py +1 -1
  31. jarvis/jarvis_rag/cli.py +1 -1
  32. jarvis/jarvis_rag/embedding_manager.py +0 -1
  33. jarvis/jarvis_rag/llm_interface.py +0 -3
  34. jarvis/jarvis_smart_shell/main.py +0 -1
  35. jarvis/jarvis_stats/cli.py +15 -35
  36. jarvis/jarvis_stats/stats.py +178 -51
  37. jarvis/jarvis_tools/clear_memory.py +1 -3
  38. jarvis/jarvis_tools/cli/main.py +0 -1
  39. jarvis/jarvis_tools/edit_file.py +0 -1
  40. jarvis/jarvis_tools/generate_new_tool.py +3 -5
  41. jarvis/jarvis_tools/registry.py +17 -3
  42. jarvis/jarvis_tools/retrieve_memory.py +2 -3
  43. jarvis/jarvis_tools/save_memory.py +3 -3
  44. jarvis/jarvis_tools/search_web.py +2 -2
  45. jarvis/jarvis_tools/sub_agent.py +114 -85
  46. jarvis/jarvis_tools/sub_code_agent.py +29 -7
  47. jarvis/jarvis_tools/virtual_tty.py +3 -14
  48. jarvis/jarvis_utils/builtin_replace_map.py +4 -4
  49. jarvis/jarvis_utils/config.py +44 -15
  50. jarvis/jarvis_utils/fzf.py +56 -0
  51. jarvis/jarvis_utils/git_utils.py +1 -1
  52. jarvis/jarvis_utils/globals.py +1 -2
  53. jarvis/jarvis_utils/input.py +0 -3
  54. jarvis/jarvis_utils/methodology.py +3 -5
  55. jarvis/jarvis_utils/output.py +1 -1
  56. jarvis/jarvis_utils/utils.py +117 -27
  57. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/METADATA +2 -3
  58. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/RECORD +62 -56
  59. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/WHEEL +0 -0
  60. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/entry_points.txt +0 -0
  61. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/licenses/LICENSE +0 -0
  62. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import json
3
- import random
4
3
  from pathlib import Path
5
4
  from typing import Any, Dict, List, Optional
6
5
 
@@ -170,7 +169,7 @@ class RetrieveMemoryTool:
170
169
  pass
171
170
 
172
171
  # 格式化为Markdown输出
173
- markdown_output = f"# 记忆检索结果\n\n"
172
+ markdown_output = "# 记忆检索结果\n\n"
174
173
  markdown_output += f"**检索到 {len(all_memories)} 条记忆**\n\n"
175
174
 
176
175
  if tags:
@@ -201,7 +200,7 @@ class RetrieveMemoryTool:
201
200
  if k not in ["id", "type", "tags", "created_at", "content"]
202
201
  }
203
202
  if metadata:
204
- markdown_output += f"**其他信息**:\n"
203
+ markdown_output += "**其他信息**:\n"
205
204
  for key, value in metadata.items():
206
205
  markdown_output += f"- {key}: {value}\n"
207
206
  markdown_output += "\n"
@@ -3,7 +3,7 @@ import json
3
3
  import time
4
4
  from datetime import datetime
5
5
  from pathlib import Path
6
- from typing import Any, Dict, List
6
+ from typing import Any, Dict
7
7
 
8
8
  from jarvis.jarvis_utils.config import get_data_dir
9
9
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
@@ -157,8 +157,8 @@ class SaveMemoryTool:
157
157
  success_count += 1
158
158
 
159
159
  # 打印单条记忆保存信息
160
- memory_type = memory_data["memory_type"]
161
- tags = memory_data.get("tags", [])
160
+ memory_data["memory_type"]
161
+ memory_data.get("tags", [])
162
162
 
163
163
  except Exception as e:
164
164
  failed_count += 1
@@ -54,7 +54,7 @@ class SearchWebTool:
54
54
  break
55
55
 
56
56
  url = r["href"]
57
- title = r.get("title", url)
57
+ r.get("title", url)
58
58
 
59
59
  try:
60
60
 
@@ -79,7 +79,7 @@ class SearchWebTool:
79
79
  "success": False,
80
80
  }
81
81
 
82
- url_list_str = "\n".join(f" - {u}" for u in visited_urls)
82
+ "\n".join(f" - {u}" for u in visited_urls)
83
83
 
84
84
  summary_prompt = f"请为查询“{query}”总结以下内容:\n\n{full_content}"
85
85
 
@@ -4,15 +4,17 @@ sub_agent 工具
4
4
  将子任务交给通用 Agent 执行,并返回执行结果。
5
5
 
6
6
  约定:
7
- - 仅接收一个参数:task
8
- - 不依赖父 Agent,所有配置使用系统默认与全局变量
7
+ - 必填参数:task, name, background, system_prompt, summary_prompt, use_tools
8
+ - 继承父 Agent 的部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供
9
9
  - 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
10
10
  """
11
- from typing import Any, Dict, Optional
11
+ from typing import Any, Dict
12
12
  import json
13
13
 
14
- from jarvis.jarvis_agent import Agent, origin_agent_system_prompt
14
+ from jarvis.jarvis_agent import Agent
15
15
  from jarvis.jarvis_utils.globals import delete_agent
16
+ from jarvis.jarvis_tools.registry import ToolRegistry
17
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
16
18
 
17
19
 
18
20
  class SubAgentTool:
@@ -25,7 +27,7 @@ class SubAgentTool:
25
27
 
26
28
  # 必须与文件名一致,供 ToolRegistry 自动注册
27
29
  name = "sub_agent"
28
- description = "将子任务交给通用 Agent 执行,并返回执行结果(使用系统默认配置,自动完成并生成总结)。"
30
+ description = "将子任务交给通用 Agent 执行,并返回执行结果(继承父Agent部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供,自动完成并生成总结)。"
29
31
  parameters = {
30
32
  "type": "object",
31
33
  "properties": {
@@ -33,12 +35,42 @@ class SubAgentTool:
33
35
  "type": "string",
34
36
  "description": "要执行的子任务内容(必填)",
35
37
  },
38
+ "name": {
39
+ "type": "string",
40
+ "description": "子Agent名称(必填)",
41
+ },
36
42
  "background": {
37
43
  "type": "string",
38
- "description": "任务背景与已知信息(可选,将与任务一并提供给子Agent)",
44
+ "description": "任务背景与已知信息(必填,将与任务一并提供给子Agent)",
45
+ },
46
+ "system_prompt": {
47
+ "type": "string",
48
+ "description": "覆盖子Agent的系统提示词(必填)",
49
+ },
50
+ "summary_prompt": {
51
+ "type": "string",
52
+ "description": "覆盖子Agent的总结提示词(必填)",
53
+ },
54
+ "use_tools": {
55
+ "type": "array",
56
+ "items": {"type": "string"},
57
+ "description": "限制子Agent可用的工具名称列表(必填)。兼容以逗号分隔的字符串输入。可用的工具列表:"
58
+ + "\n".join(
59
+ [
60
+ t["name"] + ": " + t["description"]
61
+ for t in ToolRegistry().get_all_tools()
62
+ ]
63
+ ),
39
64
  },
40
65
  },
41
- "required": ["task"],
66
+ "required": [
67
+ "task",
68
+ "name",
69
+ "background",
70
+ "system_prompt",
71
+ "summary_prompt",
72
+ "use_tools",
73
+ ],
42
74
  }
43
75
 
44
76
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
@@ -64,105 +96,102 @@ class SubAgentTool:
64
96
  f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
65
97
  )
66
98
 
67
- # 继承父Agent的运行参数(用于覆盖默认值);若无父Agent则使用默认/全局配置
68
- parent_agent = args.get("agent")
69
- # 如未注入父Agent,尝试从全局获取当前或任一已注册Agent
70
- if parent_agent is None:
71
- try:
72
- from jarvis.jarvis_utils import globals as G # 延迟导入避免循环
73
-
74
- curr = getattr(G, "current_agent_name", "")
75
- if curr:
76
- parent_agent = getattr(G, "global_agents", {}).get(curr)
77
- if parent_agent is None and getattr(G, "global_agents", {}):
78
- try:
79
- parent_agent = next(iter(G.global_agents.values()))
80
- except Exception:
81
- parent_agent = None
82
- except Exception:
83
- parent_agent = None
84
- # 默认/全局
85
- system_prompt = origin_agent_system_prompt
99
+ # 不继承父Agent,所有关键参数必须由调用方显式提供
86
100
  need_summary = True
87
101
  auto_complete = True
88
102
 
89
- # 可继承参数
90
- model_group = None
91
- summary_prompt = None
92
- execute_tool_confirm = None
93
- use_methodology = None
94
- use_analysis = None
95
- force_save_memory = None
103
+ # 读取并校验必填参数
104
+ system_prompt = str(args.get("system_prompt", "")).strip()
105
+ summary_prompt = str(args.get("summary_prompt", "")).strip()
106
+ agent_name = str(args.get("name", "")).strip()
107
+
108
+ # 解析可用工具列表(支持数组或以逗号分隔的字符串)
109
+ _use_tools = args.get("use_tools", None)
96
110
  use_tools: list[str] = []
111
+ if isinstance(_use_tools, list):
112
+ use_tools = [str(x).strip() for x in _use_tools if str(x).strip()]
113
+ elif isinstance(_use_tools, str):
114
+ use_tools = [s.strip() for s in _use_tools.split(",") if s.strip()]
115
+ else:
116
+ use_tools = []
117
+
118
+ errors = []
119
+ if not system_prompt:
120
+ errors.append("system_prompt 不能为空")
121
+ if not summary_prompt:
122
+ errors.append("summary_prompt 不能为空")
123
+ if not agent_name:
124
+ errors.append("name 不能为空")
125
+ if not use_tools:
126
+ errors.append("use_tools 不能为空")
127
+ if not background:
128
+ errors.append("background 不能为空")
129
+
130
+ if errors:
131
+ return {
132
+ "success": False,
133
+ "stdout": "",
134
+ "stderr": "; ".join(errors),
135
+ }
97
136
 
137
+ # 基于父Agent(如有)继承部分配置后创建子Agent
138
+ parent_agent = args.get("agent", None)
139
+ parent_model_group = None
140
+ parent_input_handler = None
141
+ parent_execute_tool_confirm = None
142
+ parent_multiline_inputer = None
98
143
  try:
99
144
  if parent_agent is not None:
100
- # 继承模型组
101
145
  if getattr(parent_agent, "model", None):
102
- model_group = getattr(parent_agent.model, "model_group", None)
103
- # 继承开关类参数
104
- summary_prompt = getattr(parent_agent, "summary_prompt", None)
105
- execute_tool_confirm = getattr(
106
- parent_agent, "execute_tool_confirm", None
107
- )
108
- use_methodology = getattr(parent_agent, "use_methodology", None)
109
- use_analysis = getattr(parent_agent, "use_analysis", None)
110
- force_save_memory = getattr(parent_agent, "force_save_memory", None)
111
- # 继承工具使用集(名称列表)
112
- parent_registry = parent_agent.get_tool_registry()
113
- if parent_registry:
114
- for t in parent_registry.get_all_tools():
115
- if isinstance(t, dict) and t.get("name"):
116
- use_tools.append(str(t["name"]))
146
+ parent_model_group = getattr(parent_agent.model, "model_group", None)
147
+ parent_input_handler = getattr(parent_agent, "input_handler", None)
148
+ parent_execute_tool_confirm = getattr(parent_agent, "execute_tool_confirm", None)
149
+ parent_multiline_inputer = getattr(parent_agent, "multiline_inputer", None)
117
150
  except Exception:
118
- # 忽略继承失败,退回默认配置
151
+ # 安全兜底:无法从父Agent获取配置则保持为None,使用系统默认
119
152
  pass
120
153
 
121
- # 为避免交互阻塞:提供自动确认与空输入处理器
122
- def _auto_confirm(tip: str, default: bool = True) -> bool:
123
- return default
124
-
125
- # 创建子Agent(其余配置使用默认/全局)
126
154
  agent = Agent(
127
155
  system_prompt=system_prompt,
128
- name="SubAgent",
156
+ name=agent_name,
129
157
  description="Temporary sub agent for executing a subtask",
130
-
131
- model_group=model_group, # 继承父Agent模型组(如可用)
132
- summary_prompt=summary_prompt, # 继承父Agent总结提示词(如可用)
158
+ model_group=parent_model_group,
159
+ summary_prompt=summary_prompt,
133
160
  auto_complete=auto_complete,
134
- output_handler=None, # 默认 ToolRegistry
135
- use_tools=None, # 初始不限定,稍后同步父Agent工具集
136
- input_handler=None, # 允许使用系统默认输入链
137
- execute_tool_confirm=execute_tool_confirm, # 继承父Agent(如可用)
161
+ output_handler=None,
162
+ use_tools=None,
163
+ input_handler=parent_input_handler,
164
+ execute_tool_confirm=parent_execute_tool_confirm,
138
165
  need_summary=need_summary,
139
- multiline_inputer=None, # 使用系统默认输入器(允许用户输入)
140
- use_methodology=use_methodology, # 继承父Agent(如可用)
141
- use_analysis=use_analysis, # 继承父Agent(如可用)
142
- force_save_memory=force_save_memory, # 继承父Agent(如可用)
166
+ multiline_inputer=parent_multiline_inputer,
167
+ use_methodology=None,
168
+ use_analysis=None,
169
+ force_save_memory=None,
143
170
  files=None,
144
- confirm_callback=_auto_confirm, # 自动确认
145
171
  )
146
172
 
147
- # 同步父Agent的模型名称与工具使用集(若可用)
173
+ # 设置可用工具列表
174
+ try:
175
+ agent.set_use_tools(use_tools)
176
+ except Exception:
177
+ pass
178
+
179
+ # 校验子Agent所用模型是否有效,必要时回退到平台可用模型
148
180
  try:
149
- if (
150
- parent_agent is not None
151
- and getattr(parent_agent, "model", None)
152
- and getattr(agent, "model", None)
153
- ):
154
- try:
155
- model_name = parent_agent.model.name() # type: ignore[attr-defined]
156
- if model_name:
157
- from typing import Any
158
- model_obj: Any = getattr(agent, "model", None)
159
- if model_obj is not None:
160
- model_obj.set_model_name(model_name)
161
- except Exception:
162
- pass
163
- if use_tools:
164
- agent.set_use_tools(use_tools)
181
+ platform = getattr(agent, "model", None)
182
+ if platform:
183
+ available_models = platform.get_model_list()
184
+ if available_models:
185
+ available_names = [m for m, _ in available_models]
186
+ current_model_name = platform.name()
187
+ if current_model_name not in available_names:
188
+ PrettyOutput.print(
189
+ f"检测到子Agent模型 {current_model_name} 不存在于平台 {platform.platform_name()} 的可用模型列表,将回退到 {available_names[0]}",
190
+ OutputType.WARNING,
191
+ )
192
+ platform.set_model_name(available_names[0])
165
193
  except Exception:
194
+ # 获取模型列表或设置模型失败时,保持原设置并继续,交由底层报错处理
166
195
  pass
167
196
 
168
197
  # 执行任务
@@ -8,12 +8,13 @@ sub_code_agent 工具
8
8
  - 不依赖父 Agent,所有配置使用系统默认与全局变量
9
9
  - 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
10
10
  """
11
- from typing import Any, Dict, Optional
11
+ from typing import Any, Dict
12
12
  import json
13
13
 
14
14
  from jarvis.jarvis_code_agent.code_agent import CodeAgent
15
15
  from jarvis.jarvis_utils.globals import delete_agent
16
- from jarvis.jarvis_code_agent import code_agent as code_agent_module
16
+ from jarvis.jarvis_utils.config import set_config, get_git_check_mode
17
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
17
18
 
18
19
 
19
20
  class SubCodeAgentTool:
@@ -59,11 +60,6 @@ class SubCodeAgentTool:
59
60
  "stderr": "task 不能为空",
60
61
  }
61
62
 
62
- # 在模块级别打补丁,仅自动确认交互(允许用户输入)
63
- def _auto_confirm(tip: str, default: bool = True) -> bool:
64
- return default
65
-
66
- code_agent_module.user_confirm = _auto_confirm
67
63
 
68
64
  # 读取背景信息并组合任务
69
65
  background: str = str(args.get("background", "")).strip()
@@ -131,7 +127,12 @@ class SubCodeAgentTool:
131
127
  except Exception:
132
128
  append_tools = None
133
129
 
130
+ # 在子Agent中放宽 Git 配置校验,避免因严格校验导致进程退出
131
+ # 使用配置项将校验模式临时切换为 warn,构造完成后恢复原值
132
+ old_mode = None
134
133
  try:
134
+ old_mode = get_git_check_mode()
135
+ set_config("JARVIS_GIT_CHECK_MODE", "warn")
135
136
  code_agent = CodeAgent(
136
137
  model_group=model_group,
137
138
  need_summary=True,
@@ -145,6 +146,12 @@ class SubCodeAgentTool:
145
146
  "stdout": "",
146
147
  "stderr": f"初始化 CodeAgent 失败(可能未配置 git 或当前非 git 仓库): {se}",
147
148
  }
149
+ finally:
150
+ if old_mode is not None:
151
+ try:
152
+ set_config("JARVIS_GIT_CHECK_MODE", old_mode)
153
+ except Exception:
154
+ pass
148
155
 
149
156
  # 子Agent需要自动完成
150
157
  try:
@@ -165,6 +172,21 @@ class SubCodeAgentTool:
165
172
  model_obj: Any = getattr(code_agent.agent, "model", None)
166
173
  if model_obj is not None:
167
174
  model_obj.set_model_name(parent_model_name)
175
+ # 模型有效性校验与回退,确保父Agent模型在子Agent平台上可用
176
+ try:
177
+ available_models = model_obj.get_model_list()
178
+ if available_models:
179
+ available_names = [m for m, _ in available_models]
180
+ current_model_name = model_obj.name()
181
+ if current_model_name not in available_names:
182
+ PrettyOutput.print(
183
+ f"检测到子CodeAgent模型 {current_model_name} 不存在于平台 {model_obj.platform_name()} 的可用模型列表,将回退到 {available_names[0]}",
184
+ OutputType.WARNING,
185
+ )
186
+ model_obj.set_model_name(available_names[0])
187
+ except Exception:
188
+ # 获取模型列表或设置模型失败时,保持原设置并继续,交由底层报错处理
189
+ pass
168
190
  except Exception:
169
191
  pass
170
192
  except Exception:
@@ -7,25 +7,14 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
7
 
8
8
  # 为了类型检查,总是导入这些模块
9
9
  if TYPE_CHECKING:
10
- import fcntl
11
- import pty
12
- import select
13
- import signal
14
- import subprocess
15
- import threading
16
- import queue
10
+ pass
17
11
 
18
12
  # 平台相关的导入
19
13
  if sys.platform != "win32":
20
- import fcntl
21
- import pty
22
- import select
23
- import signal
14
+ pass
24
15
  else:
25
16
  # Windows平台的导入
26
- import subprocess
27
- import threading
28
- import queue
17
+ pass
29
18
 
30
19
 
31
20
  class VirtualTTYTool:
@@ -31,25 +31,25 @@ arguments:
31
31
  },
32
32
  "FindRelatedFiles": {
33
33
  "append": False,
34
- "template": f"""
34
+ "template": """
35
35
  请使用工具在当前目录下查找与以下功能相关的文件:
36
36
  """,
37
37
  },
38
38
  "Dev": {
39
39
  "append": False,
40
- "template": f"""
40
+ "template": """
41
41
  请调用create_code_agent开发以下需求:
42
42
  """,
43
43
  },
44
44
  "Fix": {
45
45
  "append": False,
46
- "template": f"""
46
+ "template": """
47
47
  请修复以下问题:
48
48
  """,
49
49
  },
50
50
  "Check": {
51
51
  "append": True,
52
- "template": f"""
52
+ "template": """
53
53
  请使用静态检查工具检查当前代码,必须严格遵守工具调用格式。
54
54
 
55
55
  检查要求:
@@ -130,9 +130,14 @@ def _get_resolved_model_config(
130
130
  解析并合并模型配置,处理模型组。
131
131
 
132
132
  优先级顺序:
133
- 1. 单独的环境变量 (JARVIS_PLATFORM, JARVIS_MODEL, etc.)
134
- 2. JARVIS_LLM_GROUP 中定义的组配置
135
- 3. 代码中的默认值
133
+ - 当通过 model_group_override(例如命令行 -g/--llm-group)指定组时:
134
+ 1. JARVIS_LLM_GROUP 中定义的组配置
135
+ 2. 仅当组未提供对应键时,回退到顶层环境变量 (JARVIS_PLATFORM, JARVIS_MODEL, JARVIS_MAX_INPUT_TOKEN_COUNT)
136
+ 3. 代码中的默认值
137
+ - 当未显式指定组时(使用默认组或未设置):
138
+ 1. 顶层环境变量 (JARVIS_PLATFORM, JARVIS_MODEL, JARVIS_MAX_INPUT_TOKEN_COUNT)
139
+ 2. JARVIS_LLM_GROUP 中定义的组配置
140
+ 3. 代码中的默认值
136
141
 
137
142
  返回:
138
143
  Dict[str, Any]: 解析后的模型配置字典
@@ -155,14 +160,24 @@ def _get_resolved_model_config(
155
160
  # Start with group config
156
161
  resolved_config = group_config.copy()
157
162
 
158
- # Override with specific settings from GLOBAL_CONFIG_DATA
159
- for key in [
163
+ # 覆盖策略:
164
+ # - 若通过 CLI 传入了 model_group_override,则优先使用组内配置;
165
+ # 仅当组未提供对应键时,才回落到顶层 GLOBAL_CONFIG_DATA。
166
+ # - 若未传入 override(即使用默认组),保持原有行为:由顶层键覆盖组配置。
167
+ override_keys = [
160
168
  "JARVIS_PLATFORM",
161
169
  "JARVIS_MODEL",
162
170
  "JARVIS_MAX_INPUT_TOKEN_COUNT",
163
- ]:
171
+ ]
172
+ for key in override_keys:
164
173
  if key in GLOBAL_CONFIG_DATA:
165
- resolved_config[key] = GLOBAL_CONFIG_DATA[key]
174
+ if model_group_override is None:
175
+ # 未显式指定组:顶层覆盖组
176
+ resolved_config[key] = GLOBAL_CONFIG_DATA[key]
177
+ else:
178
+ # 显式指定组:仅在组未定义该键时回退到顶层
179
+ if key not in resolved_config:
180
+ resolved_config[key] = GLOBAL_CONFIG_DATA[key]
166
181
 
167
182
  return resolved_config
168
183
 
@@ -196,7 +211,7 @@ def _deprecated_platform_name_v1(model_group_override: Optional[str] = None) ->
196
211
  返回:
197
212
  str: 平台名称,默认为正常操作平台
198
213
  """
199
- config = _get_resolved_model_config(model_group_override)
214
+ _get_resolved_model_config(model_group_override)
200
215
  # Fallback to normal platform if thinking platform is not specified
201
216
  return get_normal_platform_name(model_group_override)
202
217
 
@@ -208,7 +223,7 @@ def _deprecated_model_name_v1(model_group_override: Optional[str] = None) -> str
208
223
  返回:
209
224
  str: 模型名称,默认为正常操作模型
210
225
  """
211
- config = _get_resolved_model_config(model_group_override)
226
+ _get_resolved_model_config(model_group_override)
212
227
  # Fallback to normal model if thinking model is not specified
213
228
  return get_normal_model_name(model_group_override)
214
229
 
@@ -220,7 +235,7 @@ def is_execute_tool_confirm() -> bool:
220
235
  返回:
221
236
  bool: 如果需要确认则返回True,默认为False
222
237
  """
223
- return GLOBAL_CONFIG_DATA.get("JARVIS_EXECUTE_TOOL_CONFIRM", False) == True
238
+ return GLOBAL_CONFIG_DATA.get("JARVIS_EXECUTE_TOOL_CONFIRM", False)
224
239
 
225
240
 
226
241
  def is_confirm_before_apply_patch() -> bool:
@@ -230,7 +245,7 @@ def is_confirm_before_apply_patch() -> bool:
230
245
  返回:
231
246
  bool: 如果需要确认则返回True,默认为False
232
247
  """
233
- return GLOBAL_CONFIG_DATA.get("JARVIS_CONFIRM_BEFORE_APPLY_PATCH", False) == True
248
+ return GLOBAL_CONFIG_DATA.get("JARVIS_CONFIRM_BEFORE_APPLY_PATCH", False)
234
249
 
235
250
 
236
251
  def get_data_dir() -> str:
@@ -270,7 +285,7 @@ def get_pretty_output() -> bool:
270
285
  if platform.system() == "Windows":
271
286
  return False
272
287
 
273
- return GLOBAL_CONFIG_DATA.get("JARVIS_PRETTY_OUTPUT", False) == True
288
+ return GLOBAL_CONFIG_DATA.get("JARVIS_PRETTY_OUTPUT", False)
274
289
 
275
290
 
276
291
  def is_use_methodology() -> bool:
@@ -280,7 +295,7 @@ def is_use_methodology() -> bool:
280
295
  返回:
281
296
  bool: 如果启用方法论则返回True,默认为True
282
297
  """
283
- return GLOBAL_CONFIG_DATA.get("JARVIS_USE_METHODOLOGY", True) == True
298
+ return GLOBAL_CONFIG_DATA.get("JARVIS_USE_METHODOLOGY", True)
284
299
 
285
300
 
286
301
  def is_use_analysis() -> bool:
@@ -290,7 +305,7 @@ def is_use_analysis() -> bool:
290
305
  返回:
291
306
  bool: 如果启用任务分析则返回True,默认为True
292
307
  """
293
- return GLOBAL_CONFIG_DATA.get("JARVIS_USE_ANALYSIS", True) == True
308
+ return GLOBAL_CONFIG_DATA.get("JARVIS_USE_ANALYSIS", True)
294
309
 
295
310
 
296
311
  def get_tool_load_dirs() -> List[str]:
@@ -390,7 +405,7 @@ def is_print_prompt() -> bool:
390
405
  返回:
391
406
  bool: 如果打印提示则返回True,默认为True
392
407
  """
393
- return GLOBAL_CONFIG_DATA.get("JARVIS_PRINT_PROMPT", False) == True
408
+ return GLOBAL_CONFIG_DATA.get("JARVIS_PRINT_PROMPT", False)
394
409
 
395
410
 
396
411
  def is_print_error_traceback() -> bool:
@@ -423,6 +438,20 @@ def is_enable_static_analysis() -> bool:
423
438
  return GLOBAL_CONFIG_DATA.get("JARVIS_ENABLE_STATIC_ANALYSIS", True) is True
424
439
 
425
440
 
441
+ def get_git_check_mode() -> str:
442
+ """
443
+ 获取Git校验模式。
444
+
445
+ 返回:
446
+ str: "strict" 或 "warn",默认为 "strict"
447
+ """
448
+ mode = GLOBAL_CONFIG_DATA.get("JARVIS_GIT_CHECK_MODE", "strict")
449
+ try:
450
+ return str(mode)
451
+ except Exception:
452
+ return "strict"
453
+
454
+
426
455
  def get_mcp_config() -> List[Dict[str, Any]]:
427
456
  """
428
457
  获取MCP配置列表。
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ """FZF selector utility."""
3
+ import shutil
4
+ import subprocess
5
+ from typing import List, Optional, Union
6
+
7
+ def fzf_select(
8
+ options: Union[List[str], List[dict]],
9
+ prompt: str = "SELECT> ",
10
+ key: Optional[str] = None,
11
+ ) -> Optional[str]:
12
+ """
13
+ Uses fzf to select an item from a list.
14
+
15
+ Args:
16
+ options: A list of strings or dicts to choose from.
17
+ prompt: The prompt to display in fzf.
18
+ key: If options is a list of dicts, this is the key to display.
19
+
20
+ Returns:
21
+ The selected item, or None if fzf is not available or the selection is cancelled.
22
+ """
23
+ if shutil.which("fzf") is None:
24
+ return None
25
+
26
+ if not options:
27
+ return None
28
+
29
+ if isinstance(options[0], dict):
30
+ if key is None:
31
+ raise ValueError("A key must be provided for a list of dicts.")
32
+ input_lines = [str(item.get(key, "")) for item in options]
33
+ else:
34
+ input_lines = [str(item) for item in options]
35
+
36
+ try:
37
+ process = subprocess.run(
38
+ [
39
+ "fzf",
40
+ "--prompt",
41
+ prompt,
42
+ "--height",
43
+ "40%",
44
+ "--border",
45
+ "--layout=reverse",
46
+ ],
47
+ input="\n".join(input_lines),
48
+ stdout=subprocess.PIPE,
49
+ stderr=subprocess.PIPE,
50
+ text=True,
51
+ check=True,
52
+ )
53
+ selected = process.stdout.strip()
54
+ return selected if selected else None
55
+ except (subprocess.CalledProcessError, FileNotFoundError):
56
+ return None
@@ -246,7 +246,7 @@ def handle_commit_workflow() -> bool:
246
246
  ["git", "commit", "-m", f"CheckPoint #{commit_count + 1}"], check=True
247
247
  )
248
248
  return True
249
- except subprocess.CalledProcessError as e:
249
+ except subprocess.CalledProcessError:
250
250
  return False
251
251
 
252
252
 
@@ -10,8 +10,7 @@
10
10
  import os
11
11
 
12
12
  # 全局变量:保存消息历史
13
- from typing import Any, Dict, Set, List, Optional
14
- from datetime import datetime
13
+ from typing import Any, Dict, List, Optional
15
14
 
16
15
  message_history: List[str] = []
17
16
  MAX_HISTORY_SIZE = 50