jarvis-ai-assistant 0.1.192__py3-none-any.whl → 0.1.194__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 (91) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +69 -37
  3. jarvis/jarvis_agent/builtin_input_handler.py +26 -4
  4. jarvis/jarvis_agent/jarvis.py +38 -22
  5. jarvis/jarvis_agent/main.py +20 -12
  6. jarvis/jarvis_agent/output_handler.py +7 -7
  7. jarvis/jarvis_agent/shell_input_handler.py +14 -11
  8. jarvis/jarvis_code_agent/code_agent.py +93 -90
  9. jarvis/jarvis_code_agent/lint.py +92 -105
  10. jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
  11. jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
  12. jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
  13. jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
  14. jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
  15. jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
  16. jarvis/jarvis_code_analysis/checklists/go.py +1 -1
  17. jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
  18. jarvis/jarvis_code_analysis/checklists/java.py +1 -1
  19. jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
  20. jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
  21. jarvis/jarvis_code_analysis/checklists/loader.py +51 -35
  22. jarvis/jarvis_code_analysis/checklists/php.py +1 -1
  23. jarvis/jarvis_code_analysis/checklists/python.py +1 -1
  24. jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
  25. jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
  26. jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
  27. jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
  28. jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
  29. jarvis/jarvis_code_analysis/checklists/web.py +1 -1
  30. jarvis/jarvis_code_analysis/code_review.py +293 -192
  31. jarvis/jarvis_dev/main.py +73 -56
  32. jarvis/jarvis_git_details/main.py +29 -33
  33. jarvis/jarvis_git_squash/main.py +13 -11
  34. jarvis/jarvis_git_utils/git_commiter.py +12 -3
  35. jarvis/jarvis_mcp/__init__.py +8 -10
  36. jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
  37. jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
  38. jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
  39. jarvis/jarvis_methodology/main.py +81 -47
  40. jarvis/jarvis_multi_agent/__init__.py +24 -16
  41. jarvis/jarvis_multi_agent/main.py +10 -4
  42. jarvis/jarvis_platform/__init__.py +1 -1
  43. jarvis/jarvis_platform/base.py +49 -21
  44. jarvis/jarvis_platform/human.py +5 -3
  45. jarvis/jarvis_platform/kimi.py +96 -72
  46. jarvis/jarvis_platform/openai.py +23 -28
  47. jarvis/jarvis_platform/registry.py +50 -33
  48. jarvis/jarvis_platform/tongyi.py +16 -10
  49. jarvis/jarvis_platform/yuanbao.py +205 -147
  50. jarvis/jarvis_platform_manager/main.py +4 -2
  51. jarvis/jarvis_smart_shell/main.py +35 -29
  52. jarvis/jarvis_tools/ask_user.py +8 -16
  53. jarvis/jarvis_tools/base.py +3 -2
  54. jarvis/jarvis_tools/chdir.py +7 -19
  55. jarvis/jarvis_tools/cli/main.py +14 -10
  56. jarvis/jarvis_tools/code_plan.py +10 -31
  57. jarvis/jarvis_tools/create_code_agent.py +10 -13
  58. jarvis/jarvis_tools/create_sub_agent.py +10 -22
  59. jarvis/jarvis_tools/edit_file.py +98 -76
  60. jarvis/jarvis_tools/execute_script.py +46 -46
  61. jarvis/jarvis_tools/file_analyzer.py +22 -34
  62. jarvis/jarvis_tools/file_operation.py +69 -62
  63. jarvis/jarvis_tools/generate_new_tool.py +0 -2
  64. jarvis/jarvis_tools/methodology.py +19 -23
  65. jarvis/jarvis_tools/read_code.py +42 -33
  66. jarvis/jarvis_tools/read_webpage.py +7 -16
  67. jarvis/jarvis_tools/registry.py +65 -32
  68. jarvis/jarvis_tools/rewrite_file.py +26 -29
  69. jarvis/jarvis_tools/search_web.py +5 -8
  70. jarvis/jarvis_tools/virtual_tty.py +133 -122
  71. jarvis/jarvis_utils/__init__.py +0 -1
  72. jarvis/jarvis_utils/builtin_replace_map.py +96 -8
  73. jarvis/jarvis_utils/config.py +59 -32
  74. jarvis/jarvis_utils/embedding.py +17 -14
  75. jarvis/jarvis_utils/file_processors.py +16 -9
  76. jarvis/jarvis_utils/git_utils.py +140 -99
  77. jarvis/jarvis_utils/globals.py +1 -1
  78. jarvis/jarvis_utils/input.py +84 -52
  79. jarvis/jarvis_utils/methodology.py +28 -21
  80. jarvis/jarvis_utils/output.py +159 -78
  81. jarvis/jarvis_utils/tag.py +2 -1
  82. jarvis/jarvis_utils/utils.py +85 -51
  83. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/METADATA +337 -204
  84. jarvis_ai_assistant-0.1.194.dist-info/RECORD +97 -0
  85. jarvis/jarvis_agent/file_input_handler.py +0 -112
  86. jarvis/jarvis_event/__init__.py +0 -0
  87. jarvis_ai_assistant-0.1.192.dist-info/RECORD +0 -99
  88. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/WHEEL +0 -0
  89. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/entry_points.txt +0 -0
  90. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/licenses/LICENSE +0 -0
  91. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/top_level.txt +0 -0
@@ -42,6 +42,7 @@ class OutputType(Enum):
42
42
  USER: 用户输入
43
43
  TOOL: 工具调用
44
44
  """
45
+
45
46
  SYSTEM = "SYSTEM"
46
47
  CODE = "CODE"
47
48
  RESULT = "RESULT"
@@ -54,6 +55,8 @@ class OutputType(Enum):
54
55
  DEBUG = "DEBUG"
55
56
  USER = "USER"
56
57
  TOOL = "TOOL"
58
+
59
+
57
60
  class PrettyOutput:
58
61
  """
59
62
  使用rich库格式化和显示富文本输出的类。
@@ -64,6 +67,7 @@ class PrettyOutput:
64
67
  - 结构化内容的面板显示
65
68
  - 渐进显示的流式输出
66
69
  """
70
+
67
71
  # 不同输出类型的图标
68
72
  _ICONS = {
69
73
  OutputType.SYSTEM: "🤖",
@@ -81,39 +85,40 @@ class PrettyOutput:
81
85
  }
82
86
  # 语法高亮的语言映射
83
87
  _lang_map = {
84
- 'Python': 'python',
85
- 'JavaScript': 'javascript',
86
- 'TypeScript': 'typescript',
87
- 'Java': 'java',
88
- 'C++': 'cpp',
89
- 'C#': 'csharp',
90
- 'Ruby': 'ruby',
91
- 'PHP': 'php',
92
- 'Go': 'go',
93
- 'Rust': 'rust',
94
- 'Bash': 'bash',
95
- 'HTML': 'html',
96
- 'CSS': 'css',
97
- 'SQL': 'sql',
98
- 'R': 'r',
99
- 'Kotlin': 'kotlin',
100
- 'Swift': 'swift',
101
- 'Scala': 'scala',
102
- 'Perl': 'perl',
103
- 'Lua': 'lua',
104
- 'YAML': 'yaml',
105
- 'JSON': 'json',
106
- 'XML': 'xml',
107
- 'Markdown': 'markdown',
108
- 'Text': 'text',
109
- 'Shell': 'bash',
110
- 'Dockerfile': 'dockerfile',
111
- 'Makefile': 'makefile',
112
- 'INI': 'ini',
113
- 'TOML': 'toml',
88
+ "Python": "python",
89
+ "JavaScript": "javascript",
90
+ "TypeScript": "typescript",
91
+ "Java": "java",
92
+ "C++": "cpp",
93
+ "C#": "csharp",
94
+ "Ruby": "ruby",
95
+ "PHP": "php",
96
+ "Go": "go",
97
+ "Rust": "rust",
98
+ "Bash": "bash",
99
+ "HTML": "html",
100
+ "CSS": "css",
101
+ "SQL": "sql",
102
+ "R": "r",
103
+ "Kotlin": "kotlin",
104
+ "Swift": "swift",
105
+ "Scala": "scala",
106
+ "Perl": "perl",
107
+ "Lua": "lua",
108
+ "YAML": "yaml",
109
+ "JSON": "json",
110
+ "XML": "xml",
111
+ "Markdown": "markdown",
112
+ "Text": "text",
113
+ "Shell": "bash",
114
+ "Dockerfile": "dockerfile",
115
+ "Makefile": "makefile",
116
+ "INI": "ini",
117
+ "TOML": "toml",
114
118
  }
119
+
115
120
  @staticmethod
116
- def _detect_language(text: str, default_lang: str = 'markdown') -> str:
121
+ def _detect_language(text: str, default_lang: str = "markdown") -> str:
117
122
  """
118
123
  检测给定文本的编程语言。
119
124
 
@@ -130,6 +135,7 @@ class PrettyOutput:
130
135
  return PrettyOutput._lang_map.get(detected_lang, default_lang)
131
136
  except (ClassNotFound, Exception):
132
137
  return default_lang
138
+
133
139
  @staticmethod
134
140
  def _format(output_type: OutputType, timestamp: bool = True) -> str:
135
141
  """
@@ -145,13 +151,20 @@ class PrettyOutput:
145
151
  icon = PrettyOutput._ICONS.get(output_type, "")
146
152
  formatted = f"{icon} "
147
153
  if timestamp:
148
- formatted+=f"[{datetime.now().strftime('%H:%M:%S')}][{output_type.value}]"
154
+ formatted += f"[{datetime.now().strftime('%H:%M:%S')}][{output_type.value}]"
149
155
  agent_info = get_agent_list()
150
156
  if agent_info:
151
- formatted+=f"[{agent_info}]"
157
+ formatted += f"[{agent_info}]"
152
158
  return formatted
159
+
153
160
  @staticmethod
154
- def print(text: str, output_type: OutputType, timestamp: bool = True, lang: Optional[str] = None, traceback: bool = False):
161
+ def print(
162
+ text: str,
163
+ output_type: OutputType,
164
+ timestamp: bool = True,
165
+ lang: Optional[str] = None,
166
+ traceback: bool = False,
167
+ ):
155
168
  """
156
169
  使用样式和语法高亮打印格式化输出。
157
170
 
@@ -163,38 +176,101 @@ class PrettyOutput:
163
176
  traceback: 是否显示错误的回溯信息
164
177
  """
165
178
  styles = {
166
- OutputType.SYSTEM: dict( bgcolor="#1e2b3c"),
167
- OutputType.CODE: dict( bgcolor="#1c2b1c"),
168
- OutputType.RESULT: dict( bgcolor="#1c1c2b"),
169
- OutputType.ERROR: dict( bgcolor="#2b1c1c"),
170
- OutputType.INFO: dict( bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
171
- OutputType.PLANNING: dict( bgcolor="#2b1c2b"),
172
- OutputType.PROGRESS: dict( bgcolor="#1c1c1c"),
173
- OutputType.SUCCESS: dict( bgcolor="#1c2b1c"),
174
- OutputType.WARNING: dict( bgcolor="#2b2b1c"),
175
- OutputType.DEBUG: dict( bgcolor="#1c1c1c"),
176
- OutputType.USER: dict( bgcolor="#1c2b2b"),
177
- OutputType.TOOL: dict( bgcolor="#1c2b2b"),
179
+ OutputType.SYSTEM: dict(bgcolor="#1e2b3c"),
180
+ OutputType.CODE: dict(bgcolor="#1c2b1c"),
181
+ OutputType.RESULT: dict(bgcolor="#1c1c2b"),
182
+ OutputType.ERROR: dict(bgcolor="#2b1c1c"),
183
+ OutputType.INFO: dict(bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
184
+ OutputType.PLANNING: dict(bgcolor="#2b1c2b"),
185
+ OutputType.PROGRESS: dict(bgcolor="#1c1c1c"),
186
+ OutputType.SUCCESS: dict(bgcolor="#1c2b1c"),
187
+ OutputType.WARNING: dict(bgcolor="#2b2b1c"),
188
+ OutputType.DEBUG: dict(bgcolor="#1c1c1c"),
189
+ OutputType.USER: dict(bgcolor="#1c2b2b"),
190
+ OutputType.TOOL: dict(bgcolor="#1c2b2b"),
178
191
  }
179
192
 
180
193
  header_styles = {
181
- OutputType.SYSTEM: RichStyle(color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}),
182
- OutputType.CODE: RichStyle(color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}),
183
- OutputType.RESULT: RichStyle(color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": "✨"}),
184
- OutputType.ERROR: RichStyle(color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}),
185
- OutputType.INFO: RichStyle(color="gold1", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
186
- OutputType.PLANNING: RichStyle(color="purple", bold=True, frame=True, bgcolor="#2b1c2b", meta={"icon": "📋"}),
187
- OutputType.PROGRESS: RichStyle(color="white", encircle=True, frame=True, bgcolor="#1c1c1c", meta={"icon": "⏳"}),
188
- OutputType.SUCCESS: RichStyle(color="bright_green", bold=True, strike=False, bgcolor="#1c2b1c", meta={"icon": ""}),
189
- OutputType.WARNING: RichStyle(color="yellow", bold=True, blink2=True, bgcolor="#2b2b1c", meta={"icon": "⚠️"}),
190
- OutputType.DEBUG: RichStyle(color="grey58", dim=True, conceal=True, bgcolor="#1c1c1c", meta={"icon": "🔍"}),
191
- OutputType.USER: RichStyle(color="spring_green2", frame=True, bgcolor="#1c2b2b", meta={"icon": "👤"}),
192
- OutputType.TOOL: RichStyle(color="dark_sea_green4", bgcolor="#1c2b2b", frame=True, meta={"icon": "🔧"}),
194
+ OutputType.SYSTEM: RichStyle(
195
+ color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}
196
+ ),
197
+ OutputType.CODE: RichStyle(
198
+ color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}
199
+ ),
200
+ OutputType.RESULT: RichStyle(
201
+ color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": ""}
202
+ ),
203
+ OutputType.ERROR: RichStyle(
204
+ color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": ""}
205
+ ),
206
+ OutputType.INFO: RichStyle(
207
+ color="gold1", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
208
+ ),
209
+ OutputType.PLANNING: RichStyle(
210
+ color="purple",
211
+ bold=True,
212
+ frame=True,
213
+ bgcolor="#2b1c2b",
214
+ meta={"icon": "📋"},
215
+ ),
216
+ OutputType.PROGRESS: RichStyle(
217
+ color="white",
218
+ encircle=True,
219
+ frame=True,
220
+ bgcolor="#1c1c1c",
221
+ meta={"icon": "⏳"},
222
+ ),
223
+ OutputType.SUCCESS: RichStyle(
224
+ color="bright_green",
225
+ bold=True,
226
+ strike=False,
227
+ bgcolor="#1c2b1c",
228
+ meta={"icon": "✅"},
229
+ ),
230
+ OutputType.WARNING: RichStyle(
231
+ color="yellow",
232
+ bold=True,
233
+ blink2=True,
234
+ bgcolor="#2b2b1c",
235
+ meta={"icon": "⚠️"},
236
+ ),
237
+ OutputType.DEBUG: RichStyle(
238
+ color="grey58",
239
+ dim=True,
240
+ conceal=True,
241
+ bgcolor="#1c1c1c",
242
+ meta={"icon": "🔍"},
243
+ ),
244
+ OutputType.USER: RichStyle(
245
+ color="spring_green2",
246
+ frame=True,
247
+ bgcolor="#1c2b2b",
248
+ meta={"icon": "👤"},
249
+ ),
250
+ OutputType.TOOL: RichStyle(
251
+ color="dark_sea_green4",
252
+ bgcolor="#1c2b2b",
253
+ frame=True,
254
+ meta={"icon": "🔧"},
255
+ ),
193
256
  }
194
257
 
195
- lang = lang if lang is not None else PrettyOutput._detect_language(text, default_lang='markdown')
196
- header = Text(PrettyOutput._format(output_type, timestamp), style=header_styles[output_type])
197
- content = Syntax(text, lang, theme="monokai", word_wrap=True, background_color=styles[output_type]["bgcolor"])
258
+ lang = (
259
+ lang
260
+ if lang is not None
261
+ else PrettyOutput._detect_language(text, default_lang="markdown")
262
+ )
263
+ header = Text(
264
+ PrettyOutput._format(output_type, timestamp),
265
+ style=header_styles[output_type],
266
+ )
267
+ content = Syntax(
268
+ text,
269
+ lang,
270
+ theme="monokai",
271
+ word_wrap=True,
272
+ background_color=styles[output_type]["bgcolor"],
273
+ )
198
274
  panel = Panel(
199
275
  content,
200
276
  border_style=header_styles[output_type],
@@ -213,6 +289,7 @@ class PrettyOutput:
213
289
  console.print(header, content)
214
290
  if traceback:
215
291
  console.print_exception()
292
+
216
293
  @staticmethod
217
294
  def section(title: str, output_type: OutputType = OutputType.INFO):
218
295
  """
@@ -223,38 +300,42 @@ class PrettyOutput:
223
300
  output_type: 输出类型(影响样式)
224
301
  """
225
302
  text = Text(title, style=output_type.value, justify="center")
226
- panel = Panel(
227
- text,
228
- border_style=output_type.value
229
- )
303
+ panel = Panel(text, border_style=output_type.value)
230
304
  if get_pretty_output():
231
305
  console.print(panel)
232
306
  else:
233
307
  console.print(text)
234
-
308
+
235
309
  @staticmethod
236
- def print_gradient_text(text: str, start_color: Tuple[int, int, int], end_color: Tuple[int, int, int]) -> None:
310
+ def print_gradient_text(
311
+ text: str, start_color: Tuple[int, int, int], end_color: Tuple[int, int, int]
312
+ ) -> None:
237
313
  """打印带有渐变色彩的文本。
238
-
314
+
239
315
  Args:
240
316
  text: 要打印的文本
241
317
  start_color: 起始RGB颜色元组 (r, g, b)
242
318
  end_color: 结束RGB颜色元组 (r, g, b)
243
319
  """
244
- lines = text.strip('\n').split('\n')
320
+ lines = text.strip("\n").split("\n")
245
321
  total_lines = len(lines)
246
322
  colored_lines = []
247
323
  for i, line in enumerate(lines):
248
324
  # 计算当前行的渐变颜色
249
- r = int(start_color[0] + (end_color[0] - start_color[0]) * i / (total_lines - 1))
250
- g = int(start_color[1] + (end_color[1] - start_color[1]) * i / (total_lines - 1))
251
- b = int(start_color[2] + (end_color[2] - start_color[2]) * i / (total_lines - 1))
252
-
325
+ r = int(
326
+ start_color[0] + (end_color[0] - start_color[0]) * i / (total_lines - 1)
327
+ )
328
+ g = int(
329
+ start_color[1] + (end_color[1] - start_color[1]) * i / (total_lines - 1)
330
+ )
331
+ b = int(
332
+ start_color[2] + (end_color[2] - start_color[2]) * i / (total_lines - 1)
333
+ )
334
+
253
335
  # 使用ANSI转义序列设置颜色
254
336
  colored_lines.append(f"\033[38;2;{r};{g};{b}m{line}\033[0m")
255
- colored_text = Text('\n'.join(colored_lines), style=OutputType.TOOL.value, justify="center")
256
- panel = Panel(
257
- colored_text,
258
- box=SIMPLE
337
+ colored_text = Text(
338
+ "\n".join(colored_lines), style=OutputType.TOOL.value, justify="center"
259
339
  )
260
- console.print(panel)
340
+ panel = Panel(colored_text, box=SIMPLE)
341
+ console.print(panel)
@@ -10,6 +10,7 @@ def ot(tag_name: str) -> str:
10
10
  """
11
11
  return f"<{tag_name}>"
12
12
 
13
+
13
14
  def ct(tag_name: str) -> str:
14
15
  """生成HTML标签结束标记
15
16
 
@@ -19,4 +20,4 @@ def ct(tag_name: str) -> str:
19
20
  返回:
20
21
  格式化的结束标签字符串
21
22
  """
22
- return f"</{tag_name}>"
23
+ return f"</{tag_name}>"
@@ -11,12 +11,17 @@ from typing import Any, Callable, Dict, Optional
11
11
  import yaml
12
12
 
13
13
  from jarvis import __version__
14
- from jarvis.jarvis_utils.config import get_data_dir, get_max_big_content_size, set_global_env_data
14
+ from jarvis.jarvis_utils.config import (
15
+ get_data_dir,
16
+ get_max_big_content_size,
17
+ set_global_env_data,
18
+ )
15
19
  from jarvis.jarvis_utils.embedding import get_context_token_count
16
20
  from jarvis.jarvis_utils.input import get_single_line_input
17
21
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
18
22
  from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrupt
19
23
 
24
+ g_config_file = None
20
25
 
21
26
 
22
27
  def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
@@ -35,7 +40,7 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
35
40
  """
36
41
  # 保存原始信号处理函数
37
42
  original_sigint = signal.getsignal(signal.SIGINT)
38
-
43
+
39
44
  def sigint_handler(signum, frame):
40
45
  if get_in_chat():
41
46
  set_interrupt(True)
@@ -44,7 +49,7 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
44
49
  else:
45
50
  if original_sigint and callable(original_sigint):
46
51
  original_sigint(signum, frame)
47
-
52
+
48
53
  signal.signal(signal.SIGINT, sigint_handler)
49
54
  count_cmd_usage()
50
55
 
@@ -63,22 +68,16 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
63
68
  if welcome_str:
64
69
  PrettyOutput.print_gradient_text(jarvis_ascii_art, (0, 120, 255), (0, 255, 200))
65
70
 
66
- config_file_path = Path(config_file) if config_file is not None else Path(os.path.expanduser("~/.jarvis/config.yaml"))
71
+ global g_config_file
72
+ g_config_file = config_file
67
73
 
68
- # 加载配置文件
69
- if not config_file_path.exists():
70
- old_config_file = config_file_path.parent / "env"
71
- if old_config_file.exists():# 旧的配置文件存在
72
- _read_old_config_file(old_config_file)
73
- else:
74
- _read_config_file(config_file_path.parent, config_file_path)
74
+ load_config()
75
75
 
76
76
  # 现在获取最终的数据目录(可能被配置文件修改)
77
77
  data_dir = Path(get_data_dir())
78
78
  script_dir = Path(os.path.dirname(os.path.dirname(__file__)))
79
79
  hf_archive = script_dir / "jarvis_data" / "huggingface.tar.gz"
80
80
 
81
-
82
81
  # 检查并解压huggingface模型
83
82
  hf_dir = data_dir / "huggingface" / "hub"
84
83
  if not hf_dir.exists() and hf_archive.exists():
@@ -88,22 +87,40 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
88
87
  tar.extractall(path=data_dir)
89
88
  PrettyOutput.print("HuggingFace模型解压完成", OutputType.SUCCESS)
90
89
  except Exception as e:
91
- PrettyOutput.print(f"解压HuggingFace模型失败: {e}", OutputType.ERROR)
90
+ PrettyOutput.print(f"解压HuggingFace模型失败: {e}", OutputType.ERROR)
92
91
 
93
92
  # 检查是否是git仓库并更新
94
93
  from jarvis.jarvis_utils.git_utils import check_and_update_git_repo
95
94
 
96
95
  check_and_update_git_repo(str(script_dir))
97
96
 
97
+
98
+ def load_config():
99
+ config_file = g_config_file
100
+ config_file_path = (
101
+ Path(config_file)
102
+ if config_file is not None
103
+ else Path(os.path.expanduser("~/.jarvis/config.yaml"))
104
+ )
105
+
106
+ # 加载配置文件
107
+ if not config_file_path.exists():
108
+ old_config_file = config_file_path.parent / "env"
109
+ if old_config_file.exists(): # 旧的配置文件存在
110
+ _read_old_config_file(old_config_file)
111
+ else:
112
+ _read_config_file(config_file_path.parent, config_file_path)
113
+
114
+
98
115
  def _read_config_file(jarvis_dir, config_file):
99
116
  """读取并解析YAML格式的配置文件
100
-
117
+
101
118
  功能:
102
119
  1. 读取配置文件内容
103
120
  2. 检查并添加schema声明(如果缺失)
104
121
  3. 将配置数据保存到全局变量
105
122
  4. 设置环境变量(如果配置中有ENV字段)
106
-
123
+
107
124
  参数:
108
125
  jarvis_dir: Jarvis数据目录路径
109
126
  config_file: 配置文件路径
@@ -112,31 +129,38 @@ def _read_config_file(jarvis_dir, config_file):
112
129
  content = f.read()
113
130
  config_data = yaml.safe_load(content) or {}
114
131
  if isinstance(config_data, dict):
115
- # 检查是否已有schema声明,没有则添加
132
+ # 检查是否已有schema声明,没有则添加
116
133
  if "# yaml-language-server: $schema=" not in content:
117
- schema_path = Path(os.path.relpath(
118
- Path(__file__).parent.parent / "jarvis_data" / "config_schema.json",
119
- start=jarvis_dir
120
- ))
134
+ schema_path = Path(
135
+ os.path.relpath(
136
+ Path(__file__).parent.parent
137
+ / "jarvis_data"
138
+ / "config_schema.json",
139
+ start=jarvis_dir,
140
+ )
141
+ )
121
142
  with open(config_file, "w", encoding="utf-8") as f:
122
143
  f.write(f"# yaml-language-server: $schema={schema_path}\n")
123
144
  f.write(content)
124
145
  # 保存到全局变量
125
146
  set_global_env_data(config_data)
126
- # 如果配置中有ENV键值对,则设置环境变量
147
+ # 如果配置中有ENV键值对,则设置环境变量
127
148
  if "ENV" in config_data and isinstance(config_data["ENV"], dict):
128
- os.environ.update({str(k): str(v) for k, v in config_data["ENV"].items() if v is not None})
149
+ os.environ.update(
150
+ {str(k): str(v) for k, v in config_data["ENV"].items() if v is not None}
151
+ )
152
+
129
153
 
130
154
  def _read_old_config_file(config_file):
131
155
  """读取并解析旧格式的env配置文件
132
-
156
+
133
157
  功能:
134
158
  1. 解析键值对格式的旧配置文件
135
159
  2. 支持多行值的处理
136
160
  3. 自动去除值的引号和空格
137
161
  4. 将配置数据保存到全局变量
138
162
  5. 设置环境变量并显示迁移警告
139
-
163
+
140
164
  参数:
141
165
  config_file: 旧格式配置文件路径
142
166
  """
@@ -149,7 +173,7 @@ def _read_old_config_file(config_file):
149
173
  if not line or line.startswith(("#", ";")):
150
174
  continue
151
175
  if "=" in line and not line.startswith((" ", "\t")):
152
- # 处理之前收集的多行值
176
+ # 处理之前收集的多行值
153
177
  if current_key is not None:
154
178
  value = "\n".join(current_value).strip().strip("'").strip('"')
155
179
  # 将字符串"true"/"false"转换为bool类型
@@ -159,12 +183,12 @@ def _read_old_config_file(config_file):
159
183
  value = False
160
184
  config_data[current_key] = value
161
185
  current_value = []
162
- # 解析新的键值对
186
+ # 解析新的键值对
163
187
  key, value = line.split("=", 1)
164
188
  current_key = key.strip()
165
189
  current_value.append(value.strip())
166
190
  elif current_key is not None:
167
- # 多行值的后续行
191
+ # 多行值的后续行
168
192
  current_value.append(line.strip())
169
193
  # 处理最后一个键值对
170
194
  if current_key is not None:
@@ -175,11 +199,16 @@ def _read_old_config_file(config_file):
175
199
  elif value.lower() == "false":
176
200
  value = False
177
201
  config_data[current_key] = value
178
- os.environ.update({str(k): str(v) for k, v in config_data.items() if v is not None})
202
+ os.environ.update(
203
+ {str(k): str(v) for k, v in config_data.items() if v is not None}
204
+ )
179
205
  set_global_env_data(config_data)
180
- PrettyOutput.print(f"检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式", OutputType.WARNING)
206
+ PrettyOutput.print(
207
+ f"检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式",
208
+ OutputType.WARNING,
209
+ )
210
+
181
211
 
182
-
183
212
  def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
184
213
  """循环执行函数直到成功
185
214
 
@@ -194,19 +223,23 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
194
223
  try:
195
224
  return func()
196
225
  except Exception as e:
197
- PrettyOutput.print(f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.WARNING)
226
+ PrettyOutput.print(
227
+ f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.WARNING
228
+ )
198
229
  time.sleep(sleep_time)
199
230
  continue
231
+
232
+
200
233
  def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
201
234
  """循环执行函数直到返回True
202
-
235
+
203
236
  参数:
204
237
  func: 要执行的函数,必须返回布尔值
205
238
  sleep_time: 每次失败后的等待时间(秒)
206
-
239
+
207
240
  返回:
208
241
  函数最终返回的True值
209
-
242
+
210
243
  注意:
211
244
  与while_success不同,此函数只检查返回是否为True,
212
245
  不捕获异常,异常会直接抛出
@@ -218,7 +251,9 @@ def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
218
251
  PrettyOutput.print(f"执行失败, 等待 {sleep_time}s...", OutputType.WARNING)
219
252
  time.sleep(sleep_time)
220
253
  return ret
221
- def get_file_md5(filepath: str)->str:
254
+
255
+
256
+ def get_file_md5(filepath: str) -> str:
222
257
  """计算文件内容的MD5哈希值
223
258
 
224
259
  参数:
@@ -227,7 +262,9 @@ def get_file_md5(filepath: str)->str:
227
262
  返回:
228
263
  str: 文件内容的MD5哈希值
229
264
  """
230
- return hashlib.md5(open(filepath, "rb").read(100*1024*1024)).hexdigest()
265
+ return hashlib.md5(open(filepath, "rb").read(100 * 1024 * 1024)).hexdigest()
266
+
267
+
231
268
  def user_confirm(tip: str, default: bool = True) -> bool:
232
269
  """提示用户确认是/否问题
233
270
 
@@ -242,6 +279,7 @@ def user_confirm(tip: str, default: bool = True) -> bool:
242
279
  ret = get_single_line_input(f"{tip} {suffix}: ")
243
280
  return default if ret == "" else ret.lower() == "y"
244
281
 
282
+
245
283
  def get_file_line_count(filename: str) -> int:
246
284
  """计算文件中的行数
247
285
 
@@ -257,7 +295,6 @@ def get_file_line_count(filename: str) -> int:
257
295
  return 0
258
296
 
259
297
 
260
-
261
298
  def _get_cmd_stats() -> Dict[str, int]:
262
299
  """从数据目录获取命令调用统计"""
263
300
  stats_file = Path(get_data_dir()) / "cmd_stat.yaml"
@@ -266,11 +303,10 @@ def _get_cmd_stats() -> Dict[str, int]:
266
303
  with open(stats_file, "r", encoding="utf-8") as f:
267
304
  return yaml.safe_load(f) or {}
268
305
  except Exception as e:
269
- PrettyOutput.print(
270
- f"加载命令调用统计失败: {str(e)}", OutputType.WARNING
271
- )
306
+ PrettyOutput.print(f"加载命令调用统计失败: {str(e)}", OutputType.WARNING)
272
307
  return {}
273
308
 
309
+
274
310
  def _update_cmd_stats(cmd_name: str) -> None:
275
311
  """更新命令调用统计"""
276
312
  stats = _get_cmd_stats()
@@ -280,31 +316,29 @@ def _update_cmd_stats(cmd_name: str) -> None:
280
316
  with open(stats_file, "w", encoding="utf-8") as f:
281
317
  yaml.safe_dump(stats, f, allow_unicode=True)
282
318
  except Exception as e:
283
- PrettyOutput.print(
284
- f"保存命令调用统计失败: {str(e)}", OutputType.WARNING
285
- )
319
+ PrettyOutput.print(f"保存命令调用统计失败: {str(e)}", OutputType.WARNING)
320
+
286
321
 
287
322
  def count_cmd_usage() -> None:
288
323
  """统计当前命令的使用次数"""
289
324
  import sys
325
+
290
326
  _update_cmd_stats(sys.argv[0])
291
327
 
328
+
292
329
  def is_context_overflow(content: str) -> bool:
293
330
  """判断文件内容是否超出上下文限制"""
294
- return get_context_token_count(content) > get_max_big_content_size()
331
+ return get_context_token_count(content) > get_max_big_content_size()
332
+
295
333
 
296
334
  def get_loc_stats() -> str:
297
335
  """使用loc命令获取当前目录的代码统计信息
298
-
336
+
299
337
  返回:
300
338
  str: loc命令输出的原始字符串,失败时返回空字符串
301
339
  """
302
340
  try:
303
- result = subprocess.run(
304
- ['loc'],
305
- capture_output=True,
306
- text=True
307
- )
341
+ result = subprocess.run(["loc"], capture_output=True, text=True)
308
342
  return result.stdout if result.returncode == 0 else ""
309
343
  except FileNotFoundError:
310
- return ""
344
+ return ""