jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.6__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 (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@ import hashlib
11
11
  from typing import Any, Dict, List, Optional, Tuple
12
12
 
13
13
  import typer
14
+ import yaml
14
15
 
15
16
  from jarvis.jarvis_agent import Agent
16
17
  from jarvis.jarvis_agent.events import AFTER_TOOL_CALL
@@ -18,6 +19,7 @@ from jarvis.jarvis_code_agent.lint import (
18
19
  get_lint_tools,
19
20
  get_lint_commands_for_files,
20
21
  group_commands_by_tool,
22
+ get_format_commands_for_files,
21
23
  )
22
24
  from jarvis.jarvis_code_agent.code_analyzer.build_validator import BuildValidator, BuildResult, FallbackBuildValidator
23
25
  from jarvis.jarvis_code_agent.build_validation_config import BuildValidationConfig
@@ -33,10 +35,12 @@ from jarvis.jarvis_utils.config import (
33
35
  get_git_check_mode,
34
36
  set_config,
35
37
  get_data_dir,
36
- is_plan_enabled,
37
38
  is_enable_intent_recognition,
38
39
  is_enable_impact_analysis,
40
+ get_smart_platform_name,
41
+ get_smart_model_name,
39
42
  )
43
+ from jarvis.jarvis_platform.registry import PlatformRegistry
40
44
  from jarvis.jarvis_code_agent.utils import get_project_overview
41
45
  from jarvis.jarvis_utils.git_utils import (
42
46
  confirm_add_new_files,
@@ -46,13 +50,12 @@ from jarvis.jarvis_utils.git_utils import (
46
50
  get_diff,
47
51
  get_diff_file_list,
48
52
  get_latest_commit_hash,
49
- get_recent_commits_with_files,
50
53
  handle_commit_workflow,
51
54
  has_uncommitted_changes,
52
55
  revert_change,
53
56
  )
54
57
  from jarvis.jarvis_utils.input import get_multiline_input, user_confirm
55
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
58
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput # 保留用于语法高亮
56
59
  from jarvis.jarvis_utils.utils import init_env, _acquire_single_instance_lock
57
60
 
58
61
  app = typer.Typer(help="Jarvis 代码助手")
@@ -83,7 +86,7 @@ class CodeAgent(Agent):
83
86
  append_tools: Optional[str] = None,
84
87
  tool_group: Optional[str] = None,
85
88
  non_interactive: Optional[bool] = None,
86
- plan: Optional[bool] = None,
89
+ rule_names: Optional[str] = None,
87
90
  **kwargs,
88
91
  ):
89
92
  self.root_dir = os.getcwd()
@@ -98,13 +101,10 @@ class CodeAgent(Agent):
98
101
  self._check_git_config()
99
102
  base_tools = [
100
103
  "execute_script",
101
- "search_web",
102
- "ask_user",
103
104
  "read_code",
104
- "save_memory",
105
- "retrieve_memory",
106
- "clear_memory",
107
- "sub_code_agent",
105
+ "edit_file",
106
+ "rewrite_file",
107
+ "lsp_client", # LSP客户端工具,用于获取代码补全、悬停等信息
108
108
  ]
109
109
 
110
110
  if append_tools:
@@ -121,10 +121,23 @@ class CodeAgent(Agent):
121
121
  project_rules = self._read_project_rules()
122
122
 
123
123
  combined_parts: List[str] = []
124
+ loaded_rule_names: List[str] = [] # 记录加载的规则名称
125
+
124
126
  if global_rules:
125
127
  combined_parts.append(global_rules)
128
+ loaded_rule_names.append("global_rule")
126
129
  if project_rules:
127
130
  combined_parts.append(project_rules)
131
+ loaded_rule_names.append("project_rule")
132
+
133
+ # 如果指定了 rule_names,从 rules.yaml 文件中读取并添加多个规则
134
+ if rule_names:
135
+ rule_list = [name.strip() for name in rule_names.split(',') if name.strip()]
136
+ for rule_name in rule_list:
137
+ named_rule = self._get_named_rule(rule_name)
138
+ if named_rule:
139
+ combined_parts.append(named_rule)
140
+ loaded_rule_names.append(rule_name)
128
141
 
129
142
  if combined_parts:
130
143
  merged_rules = "\n\n".join(combined_parts)
@@ -132,25 +145,44 @@ class CodeAgent(Agent):
132
145
  f"{code_system_prompt}\n\n"
133
146
  f"<rules>\n{merged_rules}\n</rules>"
134
147
  )
148
+ # 显示加载的规则名称
149
+ if loaded_rule_names:
150
+ rules_display = ", ".join(loaded_rule_names)
151
+ print(f"ℹ️ 已加载规则: {rules_display}")
135
152
 
136
153
  # 调用父类 Agent 的初始化
137
154
  # 默认禁用方法论和分析,但允许通过 kwargs 覆盖
138
155
  use_methodology = kwargs.pop("use_methodology", False)
139
156
  use_analysis = kwargs.pop("use_analysis", False)
157
+ # name 使用传入的值,如果没有传入则使用默认值 "CodeAgent"
158
+ name = kwargs.pop("name", "CodeAgent")
159
+
160
+ # 准备显式传递给 super().__init__ 的参数
161
+ # 注意:这些参数如果也在 kwargs 中,需要先移除,避免重复传递错误
162
+ explicit_params = {
163
+ "system_prompt": code_system_prompt,
164
+ "name": name,
165
+ "auto_complete": False,
166
+ "model_group": model_group,
167
+ "need_summary": need_summary,
168
+ "use_methodology": use_methodology,
169
+ "use_analysis": use_analysis,
170
+ "non_interactive": non_interactive,
171
+ "use_tools": base_tools,
172
+ }
173
+
174
+ # 自动移除所有显式传递的参数,避免重复传递错误
175
+ # 这样以后添加新参数时,只要在 explicit_params 中声明,就会自动处理
176
+ for key in explicit_params:
177
+ kwargs.pop(key, None)
178
+
140
179
  super().__init__(
141
- system_prompt=code_system_prompt,
142
- name="CodeAgent",
143
- auto_complete=False,
144
- model_group=model_group,
145
- need_summary=need_summary,
146
- use_methodology=use_methodology,
147
- use_analysis=use_analysis,
148
- non_interactive=non_interactive,
149
- plan=bool(plan) if plan is not None else is_plan_enabled(),
150
- use_tools=base_tools, # 仅启用限定工具
180
+ **explicit_params,
151
181
  **kwargs,
152
182
  )
153
183
 
184
+ self._agent_type = "code_agent"
185
+
154
186
  # 建立CodeAgent与Agent的关联,便于工具获取上下文管理器
155
187
  self._code_agent = self
156
188
 
@@ -158,7 +190,7 @@ class CodeAgent(Agent):
158
190
  try:
159
191
  # 获取当前Agent的model实例
160
192
  parent_model = None
161
- if hasattr(self, 'model') and self.model:
193
+ if self.model:
162
194
  parent_model = self.model
163
195
 
164
196
  self.context_recommender = ContextRecommender(
@@ -167,82 +199,103 @@ class CodeAgent(Agent):
167
199
  )
168
200
  except Exception as e:
169
201
  # LLM推荐器初始化失败
170
- import logging
171
- logger = logging.getLogger(__name__)
172
- logger.warning(f"上下文推荐器初始化失败: {e},将跳过上下文推荐功能")
202
+ print(f"⚠️ 上下文推荐器初始化失败: {e},将跳过上下文推荐功能")
173
203
 
174
204
  self.event_bus.subscribe(AFTER_TOOL_CALL, self._on_after_tool_call)
205
+
206
+ # 打印语言功能支持表格
207
+ try:
208
+ from jarvis.jarvis_agent.language_support_info import print_language_support_table
209
+ print_language_support_table()
210
+ except Exception:
211
+ pass
212
+
213
+ def _init_model(self, model_group: Optional[str]):
214
+ """初始化模型平台(CodeAgent使用smart平台,适用于代码生成等复杂场景)"""
215
+ platform_name = get_smart_platform_name(model_group)
216
+ model_name = get_smart_model_name(model_group)
217
+
218
+ maybe_model = PlatformRegistry().create_platform(platform_name)
219
+ if maybe_model is None:
220
+ print(f"⚠️ 平台 {platform_name} 不存在,将使用smart模型")
221
+ maybe_model = PlatformRegistry().get_smart_platform()
222
+
223
+ # 在此处收敛为非可选类型,确保后续赋值满足类型检查
224
+ self.model = maybe_model
225
+
226
+ if model_name:
227
+ self.model.set_model_name(model_name)
228
+
229
+ self.model.set_model_group(model_group)
230
+ self.model.set_suppress_output(False)
175
231
 
176
232
  def _get_system_prompt(self) -> str:
177
233
  """获取代码工程师的系统提示词"""
178
234
  return """
179
- <code_engineer_guide>
180
- ## 角色定位
181
- 你是Jarvis系统的代码工程师,一个专业的代码分析和修改助手。你的职责是:
182
- - 理解用户的代码需求,并提供高质量的实现方案
183
- - 精确分析项目结构和代码,准确定位需要修改的位置
184
- - 编写符合项目风格和标准的代码
185
- - 在修改代码时保持谨慎,确保不破坏现有功能
186
- - 做出专业的技术决策,减少用户决策负担
187
-
188
- ## 核心原则
189
- - 自主决策:基于专业判断做出决策,减少用户询问
190
- - 高效精准:提供完整解决方案,避免反复修改
191
- - 修改审慎:修改前充分分析影响范围,做到一次把事情做好
192
- - 工具精通:选择最高效工具路径解决问题
193
-
194
- ## 工作流程
195
- 1. **项目分析**:分析项目结构,确定需修改的文件
196
- 2. **需求分析**:理解需求意图,选择影响最小的实现方案
197
- 3. **代码分析**:详细分析目标文件,禁止虚构现有代码
198
- - 结构分析:优先使用文件搜索工具快速定位文件和目录结构
199
- - 内容搜索:优先使用全文搜索工具进行函数、类、变量等内容的搜索,避免遗漏
200
- - 依赖关系:如需分析依赖、调用关系,可结合代码分析工具辅助
201
- - 代码阅读:使用 read_code 工具获取目标文件的完整内容或指定范围内容,禁止凭空假设代码
202
- - 变更影响:如需分析变更影响范围,可结合版本控制工具辅助判断
203
- - 上下文理解:系统已维护项目的符号表和依赖关系图,可以帮助理解代码结构和依赖关系
204
- - 工具优先级:优先使用自动化工具,减少人工推断,确保分析结果准确
205
- 4. **方案设计**:确定最小变更方案,保持代码结构
206
- 5. **实施修改**:遵循"先读后写"原则,保持代码风格一致性
207
-
208
- ## 工具使用
209
- - 项目结构:优先使用文件搜索命令查找文件
210
- - 代码搜索:优先使用内容搜索工具
211
- - 代码阅读:优先使用read_code工具
212
- - 仅在命令行工具不足时使用专用工具
213
-
214
- ## 文件编辑工具使用规范
215
- - 对于部分文件内容修改,使用edit_file工具
216
- - 对于需要重写整个文件内容,使用 REWRITE 操作
217
- - 对于简单的修改,可以使用execute_script工具执行shell命令完成
218
-
219
- ## 子任务与子CodeAgent
220
- - 当出现以下情况时,优先使用 sub_code_agent 工具将子任务托管给子 CodeAgent(自动完成并生成总结):
221
- - 需要在当前任务下并行推进较大且相对独立的代码改造
222
- - 涉及多文件/多模块的大范围变更,或需要较长的工具调用链
223
- - 需要隔离上下文以避免污染当前对话(如探索性改动、PoC)
224
- - 需要专注于单一子问题,阶段性产出可独立复用的结果
225
- - 其余常规、小粒度改动直接在当前 Agent 中完成即可
226
- </code_engineer_guide>
227
-
228
- <say_to_llm>
229
- 1. 保持专注与耐心,先分析再行动;将复杂问题拆解为可执行的小步骤
230
- 2. 以结果为导向,同时简明呈现关键推理依据,避免无关噪音
231
- 3. 信息不足时,主动提出最少且关键的问题以澄清需求
232
- 4. 输出前自检:一致性、边界条件、依赖关系、回滚与风险提示
233
- 5. 选择对现有系统影响最小且可回退的方案,确保稳定性与可维护性
234
- 6. 保持项目风格:结构、命名、工具使用与现有规范一致
235
- 7. 工具优先:使用搜索、read_code、版本控制与静态分析验证结论,拒绝臆测
236
- 8. 面对错误与不确定,给出修复计划与备选路径,持续迭代优于停滞
237
- 9. 沟通清晰:用要点列出结论、变更范围、影响评估与下一步行动
238
- 10. 持续改进:沉淀经验为可复用清单,下一次做得更快更稳
239
- </say_to_llm>
235
+ 你是Jarvis代码工程师,专注于**项目级代码分析、精准修改与问题排查**,核心原则:自主决策不犹豫、高效精准不冗余、修改审慎可回退、工具精通不臆测。
236
+
237
+ 工作流程(闭环执行,每步必落地):
238
+ 1. 需求拆解与项目对齐:
239
+ - 先明确用户需求的核心目标(如“修复XX报错”“新增XX功能”“优化XX性能”),标注关键约束(如“兼容Python 3.8+”“不修改核心依赖”);
240
+ - 快速定位项目核心目录(如src/、main/)、技术栈(语言/框架/版本)、代码风格规范(如PEP8、ESLint规则),避免无的放矢。
241
+
242
+ 2. 目标文件精准定位(工具优先,拒绝盲搜):
243
+ - 优先通过 lsp_client 的 search_symbol(符号搜索)定位关联文件(如函数、类、变量所属文件);
244
+ - 若符号不明确,用全文搜索工具按“关键词+文件类型过滤”(如“关键词:user_login + 后缀:.py”)缩小范围;
245
+ - 仅当工具无法定位时,才用 read_code 读取疑似目录下的核心文件(如入口文件、配置文件),避免无效读取。
246
+
247
+ 3. 代码深度分析(基于工具,禁止虚构):
248
+ - 符号分析:用 lsp_client 的 document_symbols(文档符号)、get_symbol_info(符号详情)、definition(定义跳转)、references(引用查询),确认符号的作用域、依赖关系、调用链路;
249
+ - 内容分析:用 read_code 读取目标文件完整内容,重点关注“逻辑分支、异常处理、依赖引入、配置参数”,记录关键代码片段(如报错位置、待修改逻辑);
250
+ - 影响范围评估:用 lsp_client 的 references 查询待修改符号的所有引用场景,预判修改可能波及的模块,避免“改一处崩一片”。
251
+
252
+ 4. 最小变更方案设计(可回退、易维护):
253
+ - 优先选择“局部修改”(如修改函数内逻辑、补充条件判断),而非“重构”或“全文件重写”;
254
+ - 方案需满足:① 覆盖需求核心;② 不破坏现有功能;③ 符合项目代码风格;④ 便于后续回退(如仅修改必要行,不删无关代码);
255
+ - 若需修改核心逻辑(如公共函数、配置文件),先记录原始代码片段(如用临时文件保存到 /tmp/backup_xxx.txt),再执行修改。
256
+
257
+ 5. 先读后写,精准执行(工具规范使用):
258
+ - 必须先通过 read_code 读取目标文件完整内容,确认待修改位置的上下文(如前后代码逻辑、缩进格式),再调用编辑工具;
259
+ - 编辑工具选择:
260
+ - 局部修改(改少数行、补代码块):用 edit_file,明确标注“修改范围(行号/代码片段)+ 修改内容”(如“替换第15-20行的循环逻辑为:xxx”);
261
+ - 全文件重写(如格式统一、逻辑重构):仅当局部修改无法满足需求时使用 rewrite_file,重写前必须备份原始文件到 /tmp/rewrite_backup_xxx.txt。
262
+
263
+ 6. 验证与兜底(避免无效交付):
264
+ - 修改后优先通过 lsp_client 的语法检查功能(若支持)验证代码无语法错误;
265
+ - 若涉及功能变更,建议补充1-2行核心测试用例(或提示用户验证场景),确保修改生效;
266
+ - 记录修改日志(保存到 /tmp/modify_log_xxx.txt),内容包括:修改时间、目标文件、修改原因、原始代码片段、修改后代码片段,便于问题追溯。
267
+
268
+ 工具使用规范(精准调用,不浪费资源):
269
+ - lsp_client:仅传递有效参数(如符号名精准、文件路径明确),避免模糊查询(如无关键词的全局搜索);
270
+ - 全文搜索:必须添加“文件类型过滤”“目录过滤”,减少无效结果(如仅搜索 src/ 目录下的 .java 文件);
271
+ - read_code:仅读取目标文件和关联依赖文件,不读取日志、测试数据、第三方依赖包等无关文件;
272
+ - edit_file/rewrite_file:修改后必须保持代码缩进、命名规范与原文件一致(如原文件用4空格缩进,不改为2空格),不引入多余空行、注释。
273
+
274
+ 代码质量约束(底线要求,不可突破):
275
+ 1. 语法正确性:修改后代码无语法错误、无未定义变量/函数、无依赖缺失;
276
+ 2. 功能兼容性:不破坏现有正常功能,修改后的代码能适配项目已有的调用场景;
277
+ 3. 风格一致性:严格遵循项目既有风格(如命名规范、缩进、注释格式),不引入个人风格;
278
+ 4. 可维护性:修改逻辑清晰,关键改动可加简洁注释(如“// 修复XX报错:XX场景下变量未初始化”),不写“魔法值”“冗余代码”。
279
+
280
+ 调试指引(问题闭环,高效排查):
281
+ - 定位报错:优先用 lsp_client 定位报错位置,结合 read_code 查看上下文,确认报错类型(语法错/逻辑错/运行时错);
282
+ - 日志补充:若报错模糊,在关键位置(如函数入口、循环内、异常捕获前)增加打印日志,内容包括“变量值、执行步骤、时间戳”(如 print(f"[DEBUG] user_login: username={username}, status={status}")),日志输出到 /tmp/ 目录,不污染项目日志;
283
+ - 中间结果保存:复杂逻辑修改时,用临时文件(/tmp/temp_result_xxx.txt)保存中间数据(如计算结果、接口返回值),便于验证逻辑正确性;
284
+ - 回退机制:若修改后出现新问题,立即用备份文件回退,重新分析,不盲目叠加修改。
285
+
286
+ 禁止行为(红线不可碰):
287
+ 1. 禁止虚构代码、依赖、文件路径,所有结论必须基于工具返回结果或实际读取的代码;
288
+ 2. 禁止无差别读取项目所有文件,避免浪费资源;
289
+ 3. 禁止大篇幅删除、重构未明确要求修改的代码;
290
+ 4. 禁止引入项目未依赖的第三方库(除非用户明确允许);
291
+ 5. 禁止修改 /tmp/ 以外的非项目目录文件,避免污染环境。
292
+
240
293
  """
241
294
 
242
295
  def _read_project_rules(self) -> Optional[str]:
243
296
  """读取 .jarvis/rules 内容,如果存在则返回字符串,否则返回 None"""
244
297
  try:
245
- rules_path = os.path.join(self.root_dir, ".jarvis", "rules")
298
+ rules_path = os.path.join(self.root_dir, ".jarvis", "rule")
246
299
  if os.path.exists(rules_path) and os.path.isfile(rules_path):
247
300
  with open(rules_path, "r", encoding="utf-8", errors="replace") as f:
248
301
  content = f.read().strip()
@@ -255,7 +308,7 @@ class CodeAgent(Agent):
255
308
  def _read_global_rules(self) -> Optional[str]:
256
309
  """读取数据目录 rules 内容,如果存在则返回字符串,否则返回 None"""
257
310
  try:
258
- rules_path = os.path.join(get_data_dir(), "rules")
311
+ rules_path = os.path.join(get_data_dir(), "rule")
259
312
  if os.path.exists(rules_path) and os.path.isfile(rules_path):
260
313
  with open(rules_path, "r", encoding="utf-8", errors="replace") as f:
261
314
  content = f.read().strip()
@@ -265,6 +318,48 @@ class CodeAgent(Agent):
265
318
  pass
266
319
  return None
267
320
 
321
+ def _get_named_rule(self, rule_name: str) -> Optional[str]:
322
+ """从 rules.yaml 文件中获取指定名称的规则
323
+
324
+ 参数:
325
+ rule_name: 规则名称
326
+
327
+ 返回:
328
+ str: 规则内容,如果未找到则返回 None
329
+ """
330
+ try:
331
+ # 读取全局数据目录下的 rules.yaml
332
+ global_rules_yaml_path = os.path.join(get_data_dir(), "rules.yaml")
333
+ global_rules = {}
334
+ if os.path.exists(global_rules_yaml_path) and os.path.isfile(global_rules_yaml_path):
335
+ with open(global_rules_yaml_path, "r", encoding="utf-8", errors="replace") as f:
336
+ global_rules = yaml.safe_load(f) or {}
337
+
338
+ # 读取 git 根目录下的 rules.yaml
339
+ project_rules_yaml_path = os.path.join(self.root_dir, "rules.yaml")
340
+ project_rules = {}
341
+ if os.path.exists(project_rules_yaml_path) and os.path.isfile(project_rules_yaml_path):
342
+ with open(project_rules_yaml_path, "r", encoding="utf-8", errors="replace") as f:
343
+ project_rules = yaml.safe_load(f) or {}
344
+
345
+ # 合并配置:项目配置覆盖全局配置
346
+ merged_rules = {**global_rules, **project_rules}
347
+
348
+ # 查找指定的规则
349
+ if rule_name in merged_rules:
350
+ rule_value = merged_rules[rule_name]
351
+ # 如果值是字符串,直接返回
352
+ if isinstance(rule_value, str):
353
+ return rule_value.strip() if rule_value.strip() else None
354
+ # 如果值是其他类型,转换为字符串
355
+ return str(rule_value).strip() if str(rule_value).strip() else None
356
+
357
+ return None
358
+ except Exception as e:
359
+ # 读取规则失败时忽略,不影响主流程
360
+ print(f"⚠️ 读取 rules.yaml 失败: {e}")
361
+ return None
362
+
268
363
  def _check_git_config(self) -> None:
269
364
  """检查 git username 和 email 是否已设置,如果没有则提示并退出"""
270
365
  try:
@@ -301,25 +396,22 @@ class CodeAgent(Agent):
301
396
  message = "❌ Git 配置不完整\n\n请运行以下命令配置 Git:\n" + "\n".join(
302
397
  missing_configs
303
398
  )
304
- PrettyOutput.print(message, OutputType.WARNING)
399
+ print(f"⚠️ {message}")
305
400
  # 通过配置控制严格校验模式(JARVIS_GIT_CHECK_MODE):
306
401
  # - warn: 仅告警并继续,后续提交可能失败
307
402
  # - strict: 严格模式(默认),直接退出
308
403
  mode = get_git_check_mode().lower()
309
404
  if mode == "warn":
310
- PrettyOutput.print(
311
- "已启用 Git 校验警告模式(JARVIS_GIT_CHECK_MODE=warn),将继续运行。"
312
- "注意:后续提交可能失败,请尽快配置 git user.name 与 user.email。",
313
- OutputType.INFO,
314
- )
405
+ print("ℹ️ 已启用 Git 校验警告模式(JARVIS_GIT_CHECK_MODE=warn),将继续运行。"
406
+ "注意:后续提交可能失败,请尽快配置 git user.name 与 user.email。")
315
407
  return
316
408
  sys.exit(1)
317
409
 
318
410
  except FileNotFoundError:
319
- PrettyOutput.print("❌ 未找到 git 命令,请先安装 Git", OutputType.ERROR)
411
+ print("❌ 未找到 git 命令,请先安装 Git")
320
412
  sys.exit(1)
321
413
  except Exception as e:
322
- PrettyOutput.print(f"❌ 检查 Git 配置时出错: {str(e)}", OutputType.ERROR)
414
+ print(f"❌ 检查 Git 配置时出错: {str(e)}")
323
415
  sys.exit(1)
324
416
 
325
417
  def _find_git_root(self) -> str:
@@ -468,7 +560,7 @@ class CodeAgent(Agent):
468
560
  content_to_write = "\n".join(new_lines).rstrip()
469
561
  if content_to_write:
470
562
  f.write(content_to_write + "\n")
471
- PrettyOutput.print("已创建 .gitignore 并添加常用忽略规则", OutputType.SUCCESS)
563
+ print("已创建 .gitignore 并添加常用忽略规则")
472
564
  else:
473
565
  if new_lines:
474
566
  # 追加缺失的规则
@@ -477,7 +569,7 @@ class CodeAgent(Agent):
477
569
  if existing_content and not existing_content.endswith("\n"):
478
570
  f.write("\n")
479
571
  f.write("\n".join(new_lines).rstrip() + "\n")
480
- PrettyOutput.print("已更新 .gitignore,追加常用忽略规则", OutputType.SUCCESS)
572
+ print("已更新 .gitignore,追加常用忽略规则")
481
573
 
482
574
  def _handle_git_changes(self, prefix: str, suffix: str) -> None:
483
575
  """处理git仓库中的未提交修改"""
@@ -528,16 +620,14 @@ class CodeAgent(Agent):
528
620
 
529
621
  return
530
622
 
531
- PrettyOutput.print(
532
- "⚠️ 正在修改git换行符敏感设置,这会影响所有文件的换行符处理方式",
533
- OutputType.WARNING,
534
- )
623
+ print("⚠️ 正在修改git换行符敏感设置,这会影响所有文件的换行符处理方式")
535
624
  # 避免在循环中逐条打印,先拼接后统一打印
536
625
  lines = ["将进行以下设置:"]
537
626
  for key, value in target_settings.items():
538
627
  current = current_settings.get(key, "未设置")
539
628
  lines.append(f"{key}: {current} -> {value}")
540
- PrettyOutput.print("\n".join(lines), OutputType.INFO)
629
+ joined_lines = '\n'.join(lines)
630
+ print(f"ℹ️ {joined_lines}")
541
631
 
542
632
  # 直接执行设置,不需要用户确认
543
633
  for key, value in target_settings.items():
@@ -547,7 +637,7 @@ class CodeAgent(Agent):
547
637
  if sys.platform.startswith("win"):
548
638
  self._handle_windows_line_endings()
549
639
 
550
- PrettyOutput.print("git换行符敏感设置已更新", OutputType.SUCCESS)
640
+ print("git换行符敏感设置已更新")
551
641
 
552
642
  def _handle_windows_line_endings(self) -> None:
553
643
  """在Windows系统上处理换行符问题,提供建议而非强制修改"""
@@ -561,13 +651,8 @@ class CodeAgent(Agent):
561
651
  if any(keyword in content for keyword in ["text=", "eol=", "binary"]):
562
652
  return
563
653
 
564
- PrettyOutput.print(
565
- "提示:在Windows系统上,建议配置 .gitattributes 文件来避免换行符问题。",
566
- OutputType.INFO,
567
- )
568
- PrettyOutput.print(
569
- "这可以防止仅因换行符不同而导致整个文件被标记为修改。", OutputType.INFO
570
- )
654
+ print("ℹ️ 提示:在Windows系统上,建议配置 .gitattributes 文件来避免换行符问题。")
655
+ print("ℹ️ 这可以防止仅因换行符不同而导致整个文件被标记为修改。")
571
656
 
572
657
  if user_confirm("是否要创建一个最小化的.gitattributes文件?", False):
573
658
  # 最小化的内容,只影响特定类型的文件
@@ -586,25 +671,18 @@ class CodeAgent(Agent):
586
671
  if not os.path.exists(gitattributes_path):
587
672
  with open(gitattributes_path, "w", encoding="utf-8", newline="\n") as f:
588
673
  f.write(minimal_content)
589
- PrettyOutput.print(
590
- "已创建最小化的 .gitattributes 文件", OutputType.SUCCESS
591
- )
674
+ print("✅ 已创建最小化的 .gitattributes 文件")
592
675
  else:
593
- PrettyOutput.print(
594
- "将以下内容追加到现有 .gitattributes 文件:", OutputType.INFO
595
- )
596
- PrettyOutput.print(minimal_content, OutputType.CODE, lang="text")
676
+ print("ℹ️ 将以下内容追加到现有 .gitattributes 文件:")
677
+ PrettyOutput.print(minimal_content, OutputType.CODE, lang="text") # 保留语法高亮
597
678
  if user_confirm("是否追加到现有文件?", True):
598
679
  with open(
599
680
  gitattributes_path, "a", encoding="utf-8", newline="\n"
600
681
  ) as f:
601
682
  f.write("\n" + minimal_content)
602
- PrettyOutput.print("已更新 .gitattributes 文件", OutputType.SUCCESS)
683
+ print("已更新 .gitattributes 文件")
603
684
  else:
604
- PrettyOutput.print(
605
- "跳过 .gitattributes 文件创建。如遇换行符问题,可手动创建此文件。",
606
- OutputType.INFO,
607
- )
685
+ print("ℹ️ 跳过 .gitattributes 文件创建。如遇换行符问题,可手动创建此文件。")
608
686
 
609
687
  def _record_code_changes_stats(self, diff_text: str) -> None:
610
688
  """记录代码变更的统计信息。
@@ -655,7 +733,7 @@ class CodeAgent(Agent):
655
733
  except subprocess.CalledProcessError:
656
734
  pass
657
735
 
658
- PrettyOutput.print("检测到未提交的修改,是否要提交?", OutputType.WARNING)
736
+ print("⚠️ 检测到未提交的修改,是否要提交?")
659
737
  if not user_confirm("是否要提交?", True):
660
738
  return
661
739
 
@@ -693,7 +771,7 @@ class CodeAgent(Agent):
693
771
  check=True,
694
772
  )
695
773
  except subprocess.CalledProcessError as e:
696
- PrettyOutput.print(f"提交失败: {str(e)}", OutputType.ERROR)
774
+ print(f"提交失败: {str(e)}")
697
775
 
698
776
  def _show_commit_history(
699
777
  self, start_commit: Optional[str], end_commit: Optional[str]
@@ -721,9 +799,81 @@ class CodeAgent(Agent):
721
799
  commit_messages = "检测到以下提交记录:\n" + "\n".join(
722
800
  f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits
723
801
  )
724
- PrettyOutput.print(commit_messages, OutputType.INFO)
802
+ print(f"ℹ️ {commit_messages}")
725
803
  return commits
726
804
 
805
+ def _format_modified_files(self, modified_files: List[str]) -> None:
806
+ """格式化修改的文件
807
+
808
+ Args:
809
+ modified_files: 修改的文件列表
810
+ """
811
+ if not modified_files:
812
+ return
813
+
814
+ # 获取格式化命令
815
+ format_commands = get_format_commands_for_files(modified_files, self.root_dir)
816
+ if not format_commands:
817
+ return
818
+
819
+ print("🔧 正在格式化代码...")
820
+
821
+ # 执行格式化命令
822
+ formatted_files = set()
823
+ for tool_name, file_path, command in format_commands:
824
+ try:
825
+ # 检查文件是否存在
826
+ abs_file_path = os.path.join(self.root_dir, file_path) if not os.path.isabs(file_path) else file_path
827
+ if not os.path.exists(abs_file_path):
828
+ continue
829
+
830
+ # 执行格式化命令
831
+ result = subprocess.run(
832
+ command,
833
+ shell=True,
834
+ cwd=self.root_dir,
835
+ capture_output=True,
836
+ text=True,
837
+ encoding="utf-8",
838
+ errors="replace",
839
+ timeout=300, # 300秒超时
840
+ )
841
+
842
+ if result.returncode == 0:
843
+ formatted_files.add(file_path)
844
+ print(f"✅ 已格式化: {os.path.basename(file_path)} ({tool_name})")
845
+ else:
846
+ # 格式化失败,记录但不中断流程
847
+ error_msg = (result.stderr or result.stdout or "").strip()
848
+ if error_msg:
849
+ print(f"⚠️ 格式化失败 ({os.path.basename(file_path)}, {tool_name}): {error_msg[:200]}")
850
+ except subprocess.TimeoutExpired:
851
+ print(f"⚠️ 格式化超时: {os.path.basename(file_path)} ({tool_name})")
852
+ except FileNotFoundError:
853
+ # 工具未安装,跳过
854
+ continue
855
+ except Exception as e:
856
+ # 其他错误,记录但继续
857
+ print(f"⚠️ 格式化失败 ({os.path.basename(file_path)}, {tool_name}): {str(e)[:100]}")
858
+ continue
859
+
860
+ if formatted_files:
861
+ print(f"✅ 已格式化 {len(formatted_files)} 个文件")
862
+ # 暂存格式化后的文件
863
+ try:
864
+ for file_path in formatted_files:
865
+ abs_file_path = os.path.join(self.root_dir, file_path) if not os.path.isabs(file_path) else file_path
866
+ if os.path.exists(abs_file_path):
867
+ subprocess.run(
868
+ ["git", "add", file_path],
869
+ cwd=self.root_dir,
870
+ check=False,
871
+ stdout=subprocess.DEVNULL,
872
+ stderr=subprocess.DEVNULL,
873
+ )
874
+ except Exception:
875
+ pass
876
+
727
877
  def _handle_commit_confirmation(
728
878
  self,
729
879
  commits: List[Tuple[str, str]],
@@ -744,6 +894,12 @@ class CodeAgent(Agent):
744
894
  stderr=subprocess.DEVNULL,
745
895
  check=True,
746
896
  )
897
+
898
+ # 检测变更文件并格式化
899
+ modified_files = get_diff_file_list()
900
+ if modified_files:
901
+ self._format_modified_files(modified_files)
902
+
747
903
  git_commiter = GitCommitTool()
748
904
  git_commiter.execute({"prefix": prefix, "suffix": suffix, "agent": self, "model_group": getattr(self.model, "model_group", None)})
749
905
 
@@ -753,7 +909,7 @@ class CodeAgent(Agent):
753
909
  elif start_commit:
754
910
  if user_confirm("是否要重置到初始提交?", True):
755
911
  os.system(f"git reset --hard {str(start_commit)}") # 确保转换为字符串
756
- PrettyOutput.print("已重置到初始提交", OutputType.INFO)
912
+ print("ℹ️ 已重置到初始提交")
757
913
 
758
914
  def run(self, user_input: str, prefix: str = "", suffix: str = "") -> Optional[str]:
759
915
  """使用给定的用户输入运行代码代理。
@@ -790,7 +946,7 @@ class CodeAgent(Agent):
790
946
  was_suppressed = getattr(self.model, '_suppress_output', False)
791
947
  self.model.set_suppress_output(True)
792
948
  try:
793
- PrettyOutput.print("🔍 正在进行智能上下文推荐....", OutputType.INFO)
949
+ print("🔍 正在进行智能上下文推荐....")
794
950
 
795
951
  # 生成上下文推荐(基于关键词和项目上下文)
796
952
  recommendation = self.context_recommender.recommend_context(
@@ -802,12 +958,10 @@ class CodeAgent(Agent):
802
958
 
803
959
  # 打印推荐的上下文
804
960
  if context_recommendation_text:
805
- PrettyOutput.print(context_recommendation_text, OutputType.INFO)
961
+ print(f"ℹ️ {context_recommendation_text}")
806
962
  except Exception as e:
807
963
  # 上下文推荐失败不应该影响主流程
808
- import logging
809
- logger = logging.getLogger(__name__)
810
- logger.debug(f"上下文推荐失败: {e}", exc_info=True)
964
+ print(f"⚠️ 上下文推荐失败: {e}")
811
965
  finally:
812
966
  # 恢复模型输出设置
813
967
  if self.model:
@@ -830,7 +984,7 @@ class CodeAgent(Agent):
830
984
  self.model.set_suppress_output(False)
831
985
  super().run(enhanced_input)
832
986
  except RuntimeError as e:
833
- PrettyOutput.print(f"执行失败: {str(e)}", OutputType.WARNING)
987
+ print(f"⚠️ 执行失败: {str(e)}")
834
988
  return str(e)
835
989
 
836
990
 
@@ -981,7 +1135,7 @@ class CodeAgent(Agent):
981
1135
  """更新上下文管理器:当文件被修改后,更新符号表和依赖图"""
982
1136
  if not modified_files:
983
1137
  return
984
- PrettyOutput.print("🔄 正在更新代码上下文...", OutputType.INFO)
1138
+ print("🔄 正在更新代码上下文...")
985
1139
  for file_path in modified_files:
986
1140
  if os.path.exists(file_path):
987
1141
  try:
@@ -1001,7 +1155,7 @@ class CodeAgent(Agent):
1001
1155
  if not is_enable_impact_analysis():
1002
1156
  return None
1003
1157
 
1004
- PrettyOutput.print("🔍 正在进行变更影响分析...", OutputType.INFO)
1158
+ print("🔍 正在进行变更影响分析...")
1005
1159
  try:
1006
1160
  impact_analyzer = ImpactAnalyzer(self.context_manager)
1007
1161
  all_edits = []
@@ -1064,9 +1218,7 @@ class CodeAgent(Agent):
1064
1218
  return impact_report
1065
1219
  except Exception as e:
1066
1220
  # 影响分析失败不应该影响主流程,仅记录日志
1067
- import logging
1068
- logger = logging.getLogger(__name__)
1069
- logger.warning(f"影响范围分析失败: {e}", exc_info=True)
1221
+ print(f"⚠️ 影响范围分析失败: {e}")
1070
1222
  return None
1071
1223
 
1072
1224
  def _handle_impact_report(self, impact_report: Optional[Any], agent: Agent, final_ret: str) -> str:
@@ -1115,7 +1267,6 @@ class CodeAgent(Agent):
1115
1267
  files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
1116
1268
  if file_count > 3:
1117
1269
  files_str += f" 等{file_count}个文件"
1118
- PrettyOutput.print(f"🔍 正在进行基础静态检查 ({files_str})...", OutputType.INFO)
1119
1270
 
1120
1271
  # 使用兜底验证器进行基础静态检查
1121
1272
  fallback_validator = FallbackBuildValidator(self.root_dir, timeout=get_build_validation_timeout())
@@ -1139,15 +1290,9 @@ class CodeAgent(Agent):
1139
1290
  if not config.has_been_asked():
1140
1291
  # 首次失败,询问用户
1141
1292
  error_preview = _format_build_error(build_validation_result)
1142
- PrettyOutput.print(
1143
- f"\n⚠️ 构建验证失败:\n{error_preview}\n",
1144
- OutputType.WARNING,
1145
- )
1146
- PrettyOutput.print(
1147
- "提示:如果此项目需要在特殊环境(如容器)中构建,或使用独立构建脚本,"
1148
- "可以选择禁用构建验证,后续将仅进行基础静态检查。",
1149
- OutputType.INFO,
1150
- )
1293
+ print(f"\n⚠️ 构建验证失败:\n{error_preview}\n")
1294
+ print("ℹ️ 提示:如果此项目需要在特殊环境(如容器)中构建,或使用独立构建脚本,"
1295
+ "可以选择禁用构建验证,后续将仅进行基础静态检查。")
1151
1296
 
1152
1297
  if user_confirm(
1153
1298
  "是否要禁用构建验证,后续仅进行基础静态检查?",
@@ -1165,7 +1310,6 @@ class CodeAgent(Agent):
1165
1310
  files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
1166
1311
  if file_count > 3:
1167
1312
  files_str += f" 等{file_count}个文件"
1168
- PrettyOutput.print(f"🔍 正在进行基础静态检查 ({files_str})...", OutputType.INFO)
1169
1313
 
1170
1314
  # 立即进行基础静态检查
1171
1315
  fallback_validator = FallbackBuildValidator(self.root_dir, timeout=get_build_validation_timeout())
@@ -1234,7 +1378,7 @@ class CodeAgent(Agent):
1234
1378
  """
1235
1379
  # 检查是否启用静态分析
1236
1380
  if not is_enable_static_analysis():
1237
- PrettyOutput.print("ℹ️ 静态分析已禁用,跳过静态检查", OutputType.INFO)
1381
+ print("ℹ️ 静态分析已禁用,跳过静态检查")
1238
1382
  return final_ret
1239
1383
 
1240
1384
  # 检查是否有可用的lint工具
@@ -1245,7 +1389,7 @@ class CodeAgent(Agent):
1245
1389
  )
1246
1390
 
1247
1391
  if not lint_tools_info:
1248
- PrettyOutput.print("ℹ️ 未找到可用的静态检查工具,跳过静态检查", OutputType.INFO)
1392
+ print("ℹ️ 未找到可用的静态检查工具,跳过静态检查")
1249
1393
  return final_ret
1250
1394
 
1251
1395
  # 如果构建验证失败且未禁用,不进行静态分析(避免重复错误)
@@ -1257,7 +1401,7 @@ class CodeAgent(Agent):
1257
1401
  )
1258
1402
 
1259
1403
  if should_skip_static:
1260
- PrettyOutput.print("ℹ️ 构建验证失败,跳过静态分析(避免重复错误)", OutputType.INFO)
1404
+ print("ℹ️ 构建验证失败,跳过静态分析(避免重复错误)")
1261
1405
  return final_ret
1262
1406
 
1263
1407
  # 直接执行静态扫描
@@ -1265,6 +1409,8 @@ class CodeAgent(Agent):
1265
1409
  if lint_results:
1266
1410
  # 有错误或警告,让大模型修复
1267
1411
  errors_summary = self._format_lint_results(lint_results)
1412
+ # 打印完整的检查结果
1413
+ print(f"⚠️ 静态扫描发现问题:\n{errors_summary}")
1268
1414
  addon_prompt = f"""
1269
1415
  静态扫描发现以下问题,请根据错误信息修复代码:
1270
1416
 
@@ -1321,29 +1467,23 @@ class CodeAgent(Agent):
1321
1467
  """
1322
1468
 
1323
1469
  try:
1324
- PrettyOutput.print("🤖 正在询问大模型判断大量代码删除是否合理...", OutputType.INFO)
1470
+ print("🤖 正在询问大模型判断大量代码删除是否合理...")
1325
1471
  response = self.model.chat_until_success(prompt) # type: ignore
1326
1472
 
1327
1473
  # 使用确定的协议标记解析回答
1328
1474
  if "<!!!YES!!!>" in response:
1329
- PrettyOutput.print("✅ 大模型确认:代码删除合理", OutputType.SUCCESS)
1475
+ print("✅ 大模型确认:代码删除合理")
1330
1476
  return True
1331
1477
  elif "<!!!NO!!!>" in response:
1332
- PrettyOutput.print(" 大模型确认:代码删除不合理", OutputType.WARNING)
1478
+ print("⚠️ 大模型确认:代码删除不合理")
1333
1479
  return False
1334
1480
  else:
1335
1481
  # 如果无法找到协议标记,默认认为不合理(保守策略)
1336
- PrettyOutput.print(
1337
- f"⚠️ 无法找到协议标记,默认认为不合理。回答内容: {response[:200]}",
1338
- OutputType.WARNING
1339
- )
1482
+ print(f"⚠️ 无法找到协议标记,默认认为不合理。回答内容: {response[:200]}")
1340
1483
  return False
1341
1484
  except Exception as e:
1342
1485
  # 如果询问失败,默认认为不合理(保守策略)
1343
- PrettyOutput.print(
1344
- f"⚠️ 询问大模型失败: {str(e)},默认认为不合理",
1345
- OutputType.WARNING
1346
- )
1486
+ print(f"⚠️ 询问大模型失败: {str(e)},默认认为不合理")
1347
1487
  return False
1348
1488
 
1349
1489
  def _on_after_tool_call(self, agent: Agent, current_response=None, need_return=None, tool_prompt=None, **kwargs) -> None:
@@ -1353,7 +1493,7 @@ class CodeAgent(Agent):
1353
1493
 
1354
1494
  if diff:
1355
1495
  start_hash = get_latest_commit_hash()
1356
- PrettyOutput.print(diff, OutputType.CODE, lang="diff")
1496
+ PrettyOutput.print(diff, OutputType.CODE, lang="diff") # 保留语法高亮
1357
1497
  modified_files = get_diff_file_list()
1358
1498
 
1359
1499
  # 更新上下文管理器
@@ -1364,21 +1504,20 @@ class CodeAgent(Agent):
1364
1504
 
1365
1505
  per_file_preview = self._build_per_file_patch_preview(modified_files)
1366
1506
 
1367
- # 非交互模式下,在提交前检测大量代码删除
1368
- if self.non_interactive:
1369
- detection_result = detect_large_code_deletion()
1370
- if detection_result is not None:
1371
- # 检测到大量代码删除,询问大模型是否合理
1372
- is_reasonable = self._ask_llm_about_large_deletion(detection_result, per_file_preview)
1373
- if not is_reasonable:
1374
- # 大模型认为不合理,撤销修改
1375
- PrettyOutput.print("已撤销修改(大模型认为代码删除不合理)", OutputType.INFO)
1376
- revert_change()
1377
- final_ret += "\n\n修改被撤销(检测到大量代码删除且大模型判断不合理)\n"
1378
- final_ret += f"# 补丁预览(按文件):\n{per_file_preview}"
1379
- PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
1380
- self.session.prompt += final_ret
1381
- return
1507
+ # 所有模式下,在提交前检测大量代码删除并询问大模型
1508
+ detection_result = detect_large_code_deletion()
1509
+ if detection_result is not None:
1510
+ # 检测到大量代码删除,询问大模型是否合理
1511
+ is_reasonable = self._ask_llm_about_large_deletion(detection_result, per_file_preview)
1512
+ if not is_reasonable:
1513
+ # 大模型认为不合理,撤销修改
1514
+ print("ℹ️ 已撤销修改(大模型认为代码删除不合理)")
1515
+ revert_change()
1516
+ final_ret += "\n\n修改被撤销(检测到大量代码删除且大模型判断不合理)\n"
1517
+ final_ret += f"# 补丁预览(按文件):\n{per_file_preview}"
1518
+ PrettyOutput.print(final_ret, OutputType.USER, lang="markdown") # 保留语法高亮
1519
+ self.session.prompt += final_ret
1520
+ return
1382
1521
 
1383
1522
  commited = handle_commit_workflow()
1384
1523
  if commited:
@@ -1409,8 +1548,16 @@ class CodeAgent(Agent):
1409
1548
 
1410
1549
  # 添加提交信息到final_ret(按文件展示diff;删除文件仅提示)
1411
1550
  if commits:
1551
+ # 获取最新的提交信息(commits列表按时间倒序,第一个是最新的)
1552
+ latest_commit_hash, latest_commit_message = commits[0]
1553
+ commit_short_hash = latest_commit_hash[:7] if len(latest_commit_hash) >= 7 else latest_commit_hash
1554
+
1412
1555
  final_ret += (
1413
- f"\n\n代码已修改完成\n补丁内容(按文件):\n{per_file_preview}\n"
1556
+ f"\n\n代码已修改完成\n"
1557
+ f"✅ 已自动提交\n"
1558
+ f" Commit ID: {commit_short_hash} ({latest_commit_hash})\n"
1559
+ f" 提交信息: {latest_commit_message}\n"
1560
+ f"\n补丁内容(按文件):\n{per_file_preview}\n"
1414
1561
  )
1415
1562
 
1416
1563
  # 添加影响范围分析报告
@@ -1423,7 +1570,33 @@ class CodeAgent(Agent):
1423
1570
  # 静态分析
1424
1571
  final_ret = self._handle_static_analysis(modified_files, build_validation_result, config, self, final_ret)
1425
1572
  else:
1426
- final_ret += "\n\n修改没有生效\n"
1573
+ # 如果没有获取到commits,尝试直接从end_hash获取commit信息
1574
+ commit_info = ""
1575
+ if end_hash:
1576
+ try:
1577
+ result = subprocess.run(
1578
+ ["git", "log", "-1", "--pretty=format:%H|%s", end_hash],
1579
+ capture_output=True,
1580
+ text=True,
1581
+ encoding="utf-8",
1582
+ errors="replace",
1583
+ check=False,
1584
+ )
1585
+ if result.returncode == 0 and result.stdout and "|" in result.stdout:
1586
+ commit_hash, commit_message = result.stdout.strip().split("|", 1)
1587
+ commit_short_hash = commit_hash[:7] if len(commit_hash) >= 7 else commit_hash
1588
+ commit_info = (
1589
+ f"\n✅ 已自动提交\n"
1590
+ f" Commit ID: {commit_short_hash} ({commit_hash})\n"
1591
+ f" 提交信息: {commit_message}\n"
1592
+ )
1593
+ except Exception:
1594
+ pass
1595
+
1596
+ if commit_info:
1597
+ final_ret += f"\n\n代码已修改完成{commit_info}\n"
1598
+ else:
1599
+ final_ret += "\n\n修改没有生效\n"
1427
1600
  else:
1428
1601
  final_ret += "\n修改被拒绝\n"
1429
1602
  final_ret += f"# 补丁预览(按文件):\n{per_file_preview}"
@@ -1433,7 +1606,7 @@ class CodeAgent(Agent):
1433
1606
  if commited:
1434
1607
  self.session.prompt += final_ret
1435
1608
  return
1436
- PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
1609
+ PrettyOutput.print(final_ret, OutputType.USER, lang="markdown") # 保留语法高亮
1437
1610
  if not is_confirm_before_apply_patch() or user_confirm(
1438
1611
  "是否使用此回复?", default=True
1439
1612
  ):
@@ -1473,7 +1646,7 @@ class CodeAgent(Agent):
1473
1646
  tools_str = ", ".join(tool_names[:3])
1474
1647
  if len(tool_names) > 3:
1475
1648
  tools_str += f" 等{len(tool_names)}个工具"
1476
- PrettyOutput.print(f"🔍 正在进行静态检查 ({files_str}, 使用 {tools_str})...", OutputType.INFO)
1649
+ print("🔍 静态检查中...")
1477
1650
 
1478
1651
  results = []
1479
1652
  # 记录每个文件的检查结果
@@ -1492,6 +1665,9 @@ class CodeAgent(Agent):
1492
1665
  file_results.append((file_name, tool_name, "跳过", "文件不存在"))
1493
1666
  continue
1494
1667
 
1668
+ # 打印执行的命令
1669
+ print(f"ℹ️ 执行: {command}")
1670
+
1495
1671
  # 执行命令
1496
1672
  result = subprocess.run(
1497
1673
  command,
@@ -1501,7 +1677,7 @@ class CodeAgent(Agent):
1501
1677
  text=True,
1502
1678
  encoding="utf-8",
1503
1679
  errors="replace",
1504
- timeout=30, # 30秒超时
1680
+ timeout=600, # 600秒超时
1505
1681
  )
1506
1682
 
1507
1683
  # 只记录有错误或警告的结果
@@ -1510,23 +1686,27 @@ class CodeAgent(Agent):
1510
1686
  if output.strip(): # 有输出才记录
1511
1687
  results.append((tool_name, file_path, command, result.returncode, output))
1512
1688
  file_results.append((file_name, tool_name, "失败", "发现问题"))
1689
+ # 失败时打印检查结果
1690
+ output_preview = output[:2000] if len(output) > 2000 else output
1691
+ print(f"⚠️ 检查失败 ({file_name}):\n{output_preview}")
1692
+ if len(output) > 2000:
1693
+ print(f"⚠️ ... (输出已截断,共 {len(output)} 字符)")
1513
1694
  else:
1514
1695
  file_results.append((file_name, tool_name, "通过", ""))
1515
1696
  else:
1516
1697
  file_results.append((file_name, tool_name, "通过", ""))
1517
1698
 
1518
1699
  except subprocess.TimeoutExpired:
1519
- results.append((tool_name, file_path, command, -1, "执行超时(30秒)"))
1520
- file_results.append((file_name, tool_name, "超时", "执行超时(30秒)"))
1700
+ results.append((tool_name, file_path, command, -1, "执行超时(600秒)"))
1701
+ file_results.append((file_name, tool_name, "超时", "执行超时(600秒)"))
1702
+ print(f"⚠️ 检查超时 ({file_name}): 执行超时(600秒)")
1521
1703
  except FileNotFoundError:
1522
1704
  # 工具未安装,跳过
1523
1705
  file_results.append((file_name, tool_name, "跳过", "工具未安装"))
1524
1706
  continue
1525
1707
  except Exception as e:
1526
1708
  # 其他错误,记录但继续
1527
- import logging
1528
- logger = logging.getLogger(__name__)
1529
- logger.warning(f"执行lint命令失败: {command}, 错误: {e}")
1709
+ print(f"⚠️ 执行lint命令失败: {command}, 错误: {e}")
1530
1710
  file_results.append((file_name, tool_name, "失败", f"执行失败: {str(e)[:50]}"))
1531
1711
  continue
1532
1712
 
@@ -1536,38 +1716,23 @@ class CodeAgent(Agent):
1536
1716
  passed_count = sum(1 for _, _, status, _ in file_results if status == "通过")
1537
1717
  failed_count = sum(1 for _, _, status, _ in file_results if status == "失败")
1538
1718
  timeout_count = sum(1 for _, _, status, _ in file_results if status == "超时")
1539
- skipped_count = sum(1 for _, _, status, _ in file_results if status == "跳过")
1719
+ sum(1 for _, _, status, _ in file_results if status == "跳过")
1540
1720
 
1541
- # 构建结果摘要
1542
- summary_lines = [f"🔍 静态检查完成: 共检查 {total_files} 个文件"]
1543
- if passed_count > 0:
1544
- summary_lines.append(f" ✅ 通过: {passed_count}")
1721
+ # 收缩为一行的结果摘要
1722
+ summary = f"🔍 静态检查: {total_files}个文件"
1545
1723
  if failed_count > 0:
1546
- summary_lines.append(f" 失败: {failed_count}")
1724
+ summary += f", {failed_count}失败"
1547
1725
  if timeout_count > 0:
1548
- summary_lines.append(f" ⏱️ 超时: {timeout_count}")
1549
- if skipped_count > 0:
1550
- summary_lines.append(f" ⚠️ 跳过: {skipped_count}")
1726
+ summary += f", {timeout_count}超时"
1727
+ if passed_count == total_files:
1728
+ summary += " ✅全部通过"
1551
1729
 
1552
- # 添加详细结果(只显示失败和超时的文件)
1553
1730
  if failed_count > 0 or timeout_count > 0:
1554
- summary_lines.append("\n详细结果:")
1555
- for file_name, tool_name, status, message in file_results:
1556
- if status not in ("失败", "超时"):
1557
- continue # 只显示失败和超时的文件
1558
- status_icon = {
1559
- "失败": "❌",
1560
- "超时": "⏱️"
1561
- }.get(status, "•")
1562
- if message:
1563
- summary_lines.append(f" {status_icon} {file_name} ({tool_name}): {message}")
1564
- else:
1565
- summary_lines.append(f" {status_icon} {file_name} ({tool_name})")
1566
-
1567
- output_type = OutputType.WARNING if (failed_count > 0 or timeout_count > 0) else OutputType.SUCCESS
1568
- PrettyOutput.print("\n".join(summary_lines), output_type)
1731
+ print(f"⚠️ {summary}")
1732
+ else:
1733
+ print(f" {summary}")
1569
1734
  else:
1570
- PrettyOutput.print("🔍 静态检查完成: 全部通过", OutputType.SUCCESS)
1735
+ print(" 静态检查完成")
1571
1736
 
1572
1737
  return results
1573
1738
 
@@ -1599,87 +1764,6 @@ class CodeAgent(Agent):
1599
1764
  lines.append("") # 空行分隔
1600
1765
 
1601
1766
  return "\n".join(lines)
1602
-
1603
- def _extract_file_paths_from_input(self, user_input: str) -> List[str]:
1604
- """从用户输入中提取文件路径
1605
-
1606
- Args:
1607
- user_input: 用户输入文本
1608
-
1609
- Returns:
1610
- 文件路径列表
1611
- """
1612
- import re
1613
- file_paths = []
1614
-
1615
- # 匹配常见的文件路径模式
1616
- # 1. 引号中的路径: "path/to/file.py" 或 'path/to/file.py'
1617
- quoted_paths = re.findall(r'["\']([^"\']+\.(?:py|js|ts|rs|go|java|cpp|c|h|hpp))["\']', user_input)
1618
- file_paths.extend(quoted_paths)
1619
-
1620
- # 2. 相对路径: ./path/to/file.py 或 path/to/file.py
1621
- relative_paths = re.findall(r'(?:\./)?[\w/]+\.(?:py|js|ts|rs|go|java|cpp|c|h|hpp)', user_input)
1622
- file_paths.extend(relative_paths)
1623
-
1624
- # 3. 绝对路径(简化匹配)
1625
- absolute_paths = re.findall(r'/(?:[\w\-\.]+/)+[\w\-\.]+\.(?:py|js|ts|rs|go|java|cpp|c|h|hpp)', user_input)
1626
- file_paths.extend(absolute_paths)
1627
-
1628
- # 转换为绝对路径并去重
1629
- unique_paths = []
1630
- seen = set()
1631
- for path in file_paths:
1632
- abs_path = os.path.abspath(path) if not os.path.isabs(path) else path
1633
- if abs_path not in seen and os.path.exists(abs_path):
1634
- seen.add(abs_path)
1635
- unique_paths.append(abs_path)
1636
-
1637
- return unique_paths
1638
-
1639
- def _extract_symbols_from_input(self, user_input: str) -> List[str]:
1640
- """从用户输入中提取符号名称(函数名、类名等)
1641
-
1642
- Args:
1643
- user_input: 用户输入文本
1644
-
1645
- Returns:
1646
- 符号名称列表
1647
- """
1648
- import re
1649
- symbols = []
1650
-
1651
- # 匹配常见的符号命名模式
1652
- # 1. 驼峰命名(类名): MyClass, ProcessData
1653
- camel_case = re.findall(r'\b[A-Z][a-zA-Z0-9]+\b', user_input)
1654
- symbols.extend(camel_case)
1655
-
1656
- # 2. 下划线命名(函数名、变量名): process_data, get_user_info
1657
- snake_case = re.findall(r'\b[a-z][a-z0-9_]+[a-z0-9]\b', user_input)
1658
- symbols.extend(snake_case)
1659
-
1660
- # 3. 在引号中的符号名: "function_name" 或 'ClassName'
1661
- quoted_symbols = re.findall(r'["\']([A-Za-z][A-Za-z0-9_]*?)["\']', user_input)
1662
- symbols.extend(quoted_symbols)
1663
-
1664
- # 过滤常见停用词和过短的符号
1665
- stop_words = {
1666
- 'the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'her', 'was', 'one',
1667
- 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'its', 'may', 'new', 'now',
1668
- 'old', 'see', 'two', 'way', 'who', 'boy', 'did', 'its', 'let', 'put', 'say', 'she',
1669
- 'too', 'use', '添加', '修改', '实现', '修复', '更新', '删除', '创建', '文件', '代码',
1670
- }
1671
-
1672
- unique_symbols = []
1673
- seen = set()
1674
- for symbol in symbols:
1675
- symbol_lower = symbol.lower()
1676
- if (symbol_lower not in stop_words and
1677
- len(symbol) > 2 and
1678
- symbol_lower not in seen):
1679
- seen.add(symbol_lower)
1680
- unique_symbols.append(symbol)
1681
-
1682
- return unique_symbols[:10] # 限制数量
1683
1767
 
1684
1768
  def _validate_build_after_edit(self, modified_files: List[str]) -> Optional[BuildResult]:
1685
1769
  """编辑后验证构建
@@ -1704,7 +1788,7 @@ class CodeAgent(Agent):
1704
1788
  files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
1705
1789
  if file_count > 3:
1706
1790
  files_str += f" 等{file_count}个文件"
1707
- PrettyOutput.print(f"🔨 正在进行编译检查 ({files_str})...", OutputType.INFO)
1791
+ print(f"🔨 正在进行编译检查 ({files_str})...")
1708
1792
 
1709
1793
  try:
1710
1794
  timeout = get_build_validation_timeout()
@@ -1713,9 +1797,7 @@ class CodeAgent(Agent):
1713
1797
  return result
1714
1798
  except Exception as e:
1715
1799
  # 构建验证失败不应该影响主流程,仅记录日志
1716
- import logging
1717
- logger = logging.getLogger(__name__)
1718
- logger.warning(f"构建验证执行失败: {e}", exc_info=True)
1800
+ print(f"⚠️ 构建验证执行失败: {e}")
1719
1801
  return None
1720
1802
 
1721
1803
 
@@ -1754,7 +1836,9 @@ def cli(
1754
1836
  non_interactive: bool = typer.Option(
1755
1837
  False, "-n", "--non-interactive", help="启用非交互模式:用户无法与命令交互,脚本执行超时限制为5分钟"
1756
1838
  ),
1757
- plan: bool = typer.Option(False, "--plan/--no-plan", help="启用或禁用任务规划(子任务拆分与汇总执行)"),
1839
+ rule_names: Optional[str] = typer.Option(
1840
+ None, "--rule-names", help="指定规则名称列表,用逗号分隔,从 rules.yaml 文件中读取对应的规则内容"
1841
+ ),
1758
1842
  ) -> None:
1759
1843
  """Jarvis主入口点。"""
1760
1844
  # CLI 标志:非交互模式(不依赖配置文件)
@@ -1766,10 +1850,7 @@ def cli(
1766
1850
  # 注意:全局配置同步放在 init_env 之后执行,避免被 init_env 覆盖
1767
1851
  # 非交互模式要求从命令行传入任务
1768
1852
  if non_interactive and not (requirement and str(requirement).strip()):
1769
- PrettyOutput.print(
1770
- "非交互模式已启用:必须使用 --requirement 传入任务内容,因多行输入不可用。",
1771
- OutputType.ERROR,
1772
- )
1853
+ print("❌ 非交互模式已启用:必须使用 --requirement 传入任务内容,因多行输入不可用。")
1773
1854
  raise typer.Exit(code=2)
1774
1855
  init_env(
1775
1856
  "欢迎使用 Jarvis-CodeAgent,您的代码工程助手已准备就绪!",
@@ -1801,9 +1882,7 @@ def cli(
1801
1882
  )
1802
1883
  except (subprocess.CalledProcessError, FileNotFoundError):
1803
1884
  curr_dir_path = os.getcwd()
1804
- PrettyOutput.print(
1805
- f"警告:当前目录 '{curr_dir_path}' 不是一个git仓库。", OutputType.WARNING
1806
- )
1885
+ print(f"⚠️ 警告:当前目录 '{curr_dir_path}' 不是一个git仓库。")
1807
1886
  init_git = True if non_interactive else user_confirm(
1808
1887
  f"是否要在 '{curr_dir_path}' 中初始化一个新的git仓库?", default=True
1809
1888
  )
@@ -1817,14 +1896,12 @@ def cli(
1817
1896
  encoding="utf-8",
1818
1897
  errors="replace",
1819
1898
  )
1820
- PrettyOutput.print("✅ 已成功初始化git仓库。", OutputType.SUCCESS)
1899
+ print("✅ 已成功初始化git仓库。")
1821
1900
  except (subprocess.CalledProcessError, FileNotFoundError) as e:
1822
- PrettyOutput.print(f"❌ 初始化git仓库失败: {e}", OutputType.ERROR)
1901
+ print(f"❌ 初始化git仓库失败: {e}")
1823
1902
  sys.exit(1)
1824
1903
  else:
1825
- PrettyOutput.print(
1826
- "操作已取消。Jarvis需要在git仓库中运行。", OutputType.INFO
1827
- )
1904
+ print("ℹ️ 操作已取消。Jarvis需要在git仓库中运行。")
1828
1905
  sys.exit(0)
1829
1906
 
1830
1907
  curr_dir = os.getcwd()
@@ -1838,25 +1915,22 @@ def cli(
1838
1915
  # 回退到全局锁,确保至少有互斥保护
1839
1916
  _acquire_single_instance_lock(lock_name="code_agent.lock")
1840
1917
  try:
1918
+
1841
1919
  agent = CodeAgent(
1842
1920
  model_group=model_group,
1843
1921
  need_summary=False,
1844
1922
  append_tools=append_tools,
1845
1923
  tool_group=tool_group,
1846
1924
  non_interactive=non_interactive,
1847
- plan=plan,
1925
+ rule_names=rule_names,
1848
1926
  )
1849
1927
 
1850
1928
  # 尝试恢复会话
1851
1929
  if restore_session:
1852
1930
  if agent.restore_session():
1853
- PrettyOutput.print(
1854
- "已从 .jarvis/saved_session.json 恢复会话。", OutputType.SUCCESS
1855
- )
1931
+ print("✅ 已从 .jarvis/saved_session.json 恢复会话。")
1856
1932
  else:
1857
- PrettyOutput.print(
1858
- "无法从 .jarvis/saved_session.json 恢复会话。", OutputType.WARNING
1859
- )
1933
+ print("⚠️ 无法从 .jarvis/saved_session.json 恢复会话。")
1860
1934
 
1861
1935
  if requirement:
1862
1936
  agent.run(requirement, prefix=prefix, suffix=suffix)
@@ -1870,7 +1944,7 @@ def cli(
1870
1944
  except typer.Exit:
1871
1945
  raise
1872
1946
  except RuntimeError as e:
1873
- PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
1947
+ print(f"错误: {str(e)}")
1874
1948
  sys.exit(1)
1875
1949
 
1876
1950