jarvis-ai-assistant 0.1.193__py3-none-any.whl → 0.1.195__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 (92) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +45 -41
  3. jarvis/jarvis_agent/builtin_input_handler.py +26 -4
  4. jarvis/jarvis_agent/jarvis.py +30 -19
  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 +81 -79
  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 +31 -29
  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 +292 -190
  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 +15 -5
  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 +71 -39
  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 +44 -18
  44. jarvis/jarvis_platform/human.py +15 -3
  45. jarvis/jarvis_platform/kimi.py +117 -81
  46. jarvis/jarvis_platform/openai.py +23 -28
  47. jarvis/jarvis_platform/registry.py +43 -29
  48. jarvis/jarvis_platform/tongyi.py +16 -10
  49. jarvis/jarvis_platform/yuanbao.py +197 -144
  50. jarvis/jarvis_platform_manager/main.py +4 -2
  51. jarvis/jarvis_smart_shell/main.py +35 -30
  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 +6 -11
  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 +35 -35
  66. jarvis/jarvis_tools/read_webpage.py +7 -16
  67. jarvis/jarvis_tools/registry.py +63 -30
  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 +9 -9
  73. jarvis/jarvis_utils/config.py +60 -37
  74. jarvis/jarvis_utils/embedding.py +24 -19
  75. jarvis/jarvis_utils/file_processors.py +16 -9
  76. jarvis/jarvis_utils/git_utils.py +157 -107
  77. jarvis/jarvis_utils/globals.py +1 -1
  78. jarvis/jarvis_utils/input.py +85 -52
  79. jarvis/jarvis_utils/jarvis_history.py +43 -0
  80. jarvis/jarvis_utils/methodology.py +31 -24
  81. jarvis/jarvis_utils/output.py +164 -80
  82. jarvis/jarvis_utils/tag.py +2 -1
  83. jarvis/jarvis_utils/utils.py +84 -52
  84. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/METADATA +362 -230
  85. jarvis_ai_assistant-0.1.195.dist-info/RECORD +98 -0
  86. jarvis/jarvis_agent/file_input_handler.py +0 -112
  87. jarvis/jarvis_event/__init__.py +0 -0
  88. jarvis_ai_assistant-0.1.193.dist-info/RECORD +0 -99
  89. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/WHEEL +0 -0
  90. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/entry_points.txt +0 -0
  91. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/licenses/LICENSE +0 -0
  92. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.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],
@@ -211,8 +287,12 @@ class PrettyOutput:
211
287
  console.print(content)
212
288
  else:
213
289
  console.print(header, content)
214
- if traceback:
215
- console.print_exception()
290
+ if traceback or output_type == OutputType.ERROR:
291
+ try:
292
+ console.print_exception()
293
+ except Exception as e:
294
+ console.print(f"Error: {e}")
295
+
216
296
  @staticmethod
217
297
  def section(title: str, output_type: OutputType = OutputType.INFO):
218
298
  """
@@ -223,38 +303,42 @@ class PrettyOutput:
223
303
  output_type: 输出类型(影响样式)
224
304
  """
225
305
  text = Text(title, style=output_type.value, justify="center")
226
- panel = Panel(
227
- text,
228
- border_style=output_type.value
229
- )
306
+ panel = Panel(text, border_style=output_type.value)
230
307
  if get_pretty_output():
231
308
  console.print(panel)
232
309
  else:
233
310
  console.print(text)
234
-
311
+
235
312
  @staticmethod
236
- def print_gradient_text(text: str, start_color: Tuple[int, int, int], end_color: Tuple[int, int, int]) -> None:
313
+ def print_gradient_text(
314
+ text: str, start_color: Tuple[int, int, int], end_color: Tuple[int, int, int]
315
+ ) -> None:
237
316
  """打印带有渐变色彩的文本。
238
-
317
+
239
318
  Args:
240
319
  text: 要打印的文本
241
320
  start_color: 起始RGB颜色元组 (r, g, b)
242
321
  end_color: 结束RGB颜色元组 (r, g, b)
243
322
  """
244
- lines = text.strip('\n').split('\n')
323
+ lines = text.strip("\n").split("\n")
245
324
  total_lines = len(lines)
246
325
  colored_lines = []
247
326
  for i, line in enumerate(lines):
248
327
  # 计算当前行的渐变颜色
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
-
328
+ r = int(
329
+ start_color[0] + (end_color[0] - start_color[0]) * i / (total_lines - 1)
330
+ )
331
+ g = int(
332
+ start_color[1] + (end_color[1] - start_color[1]) * i / (total_lines - 1)
333
+ )
334
+ b = int(
335
+ start_color[2] + (end_color[2] - start_color[2]) * i / (total_lines - 1)
336
+ )
337
+
253
338
  # 使用ANSI转义序列设置颜色
254
339
  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
340
+ colored_text = Text(
341
+ "\n".join(colored_lines), style=OutputType.TOOL.value, justify="center"
259
342
  )
260
- console.print(panel)
343
+ panel = Panel(colored_text, box=SIMPLE)
344
+ 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,15 @@ 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 (get_data_dir, get_max_big_content_size,
15
+ set_global_env_data)
15
16
  from jarvis.jarvis_utils.embedding import get_context_token_count
17
+ from jarvis.jarvis_utils.globals import (get_in_chat, get_interrupt,
18
+ set_interrupt)
16
19
  from jarvis.jarvis_utils.input import get_single_line_input
17
20
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
18
- from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrupt
19
21
 
22
+ g_config_file = None
20
23
 
21
24
 
22
25
  def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
@@ -35,7 +38,7 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
35
38
  """
36
39
  # 保存原始信号处理函数
37
40
  original_sigint = signal.getsignal(signal.SIGINT)
38
-
41
+
39
42
  def sigint_handler(signum, frame):
40
43
  if get_in_chat():
41
44
  set_interrupt(True)
@@ -44,7 +47,7 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
44
47
  else:
45
48
  if original_sigint and callable(original_sigint):
46
49
  original_sigint(signum, frame)
47
-
50
+
48
51
  signal.signal(signal.SIGINT, sigint_handler)
49
52
  count_cmd_usage()
50
53
 
@@ -63,22 +66,16 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
63
66
  if welcome_str:
64
67
  PrettyOutput.print_gradient_text(jarvis_ascii_art, (0, 120, 255), (0, 255, 200))
65
68
 
66
- config_file_path = Path(config_file) if config_file is not None else Path(os.path.expanduser("~/.jarvis/config.yaml"))
69
+ global g_config_file
70
+ g_config_file = config_file
67
71
 
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)
72
+ load_config()
75
73
 
76
74
  # 现在获取最终的数据目录(可能被配置文件修改)
77
75
  data_dir = Path(get_data_dir())
78
76
  script_dir = Path(os.path.dirname(os.path.dirname(__file__)))
79
77
  hf_archive = script_dir / "jarvis_data" / "huggingface.tar.gz"
80
78
 
81
-
82
79
  # 检查并解压huggingface模型
83
80
  hf_dir = data_dir / "huggingface" / "hub"
84
81
  if not hf_dir.exists() and hf_archive.exists():
@@ -88,22 +85,40 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
88
85
  tar.extractall(path=data_dir)
89
86
  PrettyOutput.print("HuggingFace模型解压完成", OutputType.SUCCESS)
90
87
  except Exception as e:
91
- PrettyOutput.print(f"解压HuggingFace模型失败: {e}", OutputType.ERROR)
88
+ PrettyOutput.print(f"解压HuggingFace模型失败: {e}", OutputType.ERROR)
92
89
 
93
90
  # 检查是否是git仓库并更新
94
91
  from jarvis.jarvis_utils.git_utils import check_and_update_git_repo
95
92
 
96
93
  check_and_update_git_repo(str(script_dir))
97
94
 
95
+
96
+ def load_config():
97
+ config_file = g_config_file
98
+ config_file_path = (
99
+ Path(config_file)
100
+ if config_file is not None
101
+ else Path(os.path.expanduser("~/.jarvis/config.yaml"))
102
+ )
103
+
104
+ # 加载配置文件
105
+ if not config_file_path.exists():
106
+ old_config_file = config_file_path.parent / "env"
107
+ if old_config_file.exists(): # 旧的配置文件存在
108
+ _read_old_config_file(old_config_file)
109
+ else:
110
+ _read_config_file(config_file_path.parent, config_file_path)
111
+
112
+
98
113
  def _read_config_file(jarvis_dir, config_file):
99
114
  """读取并解析YAML格式的配置文件
100
-
115
+
101
116
  功能:
102
117
  1. 读取配置文件内容
103
118
  2. 检查并添加schema声明(如果缺失)
104
119
  3. 将配置数据保存到全局变量
105
120
  4. 设置环境变量(如果配置中有ENV字段)
106
-
121
+
107
122
  参数:
108
123
  jarvis_dir: Jarvis数据目录路径
109
124
  config_file: 配置文件路径
@@ -112,31 +127,38 @@ def _read_config_file(jarvis_dir, config_file):
112
127
  content = f.read()
113
128
  config_data = yaml.safe_load(content) or {}
114
129
  if isinstance(config_data, dict):
115
- # 检查是否已有schema声明,没有则添加
130
+ # 检查是否已有schema声明,没有则添加
116
131
  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
- ))
132
+ schema_path = Path(
133
+ os.path.relpath(
134
+ Path(__file__).parent.parent
135
+ / "jarvis_data"
136
+ / "config_schema.json",
137
+ start=jarvis_dir,
138
+ )
139
+ )
121
140
  with open(config_file, "w", encoding="utf-8") as f:
122
141
  f.write(f"# yaml-language-server: $schema={schema_path}\n")
123
142
  f.write(content)
124
143
  # 保存到全局变量
125
144
  set_global_env_data(config_data)
126
- # 如果配置中有ENV键值对,则设置环境变量
145
+ # 如果配置中有ENV键值对,则设置环境变量
127
146
  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})
147
+ os.environ.update(
148
+ {str(k): str(v) for k, v in config_data["ENV"].items() if v is not None}
149
+ )
150
+
129
151
 
130
152
  def _read_old_config_file(config_file):
131
153
  """读取并解析旧格式的env配置文件
132
-
154
+
133
155
  功能:
134
156
  1. 解析键值对格式的旧配置文件
135
157
  2. 支持多行值的处理
136
158
  3. 自动去除值的引号和空格
137
159
  4. 将配置数据保存到全局变量
138
160
  5. 设置环境变量并显示迁移警告
139
-
161
+
140
162
  参数:
141
163
  config_file: 旧格式配置文件路径
142
164
  """
@@ -149,7 +171,7 @@ def _read_old_config_file(config_file):
149
171
  if not line or line.startswith(("#", ";")):
150
172
  continue
151
173
  if "=" in line and not line.startswith((" ", "\t")):
152
- # 处理之前收集的多行值
174
+ # 处理之前收集的多行值
153
175
  if current_key is not None:
154
176
  value = "\n".join(current_value).strip().strip("'").strip('"')
155
177
  # 将字符串"true"/"false"转换为bool类型
@@ -159,12 +181,12 @@ def _read_old_config_file(config_file):
159
181
  value = False
160
182
  config_data[current_key] = value
161
183
  current_value = []
162
- # 解析新的键值对
184
+ # 解析新的键值对
163
185
  key, value = line.split("=", 1)
164
186
  current_key = key.strip()
165
187
  current_value.append(value.strip())
166
188
  elif current_key is not None:
167
- # 多行值的后续行
189
+ # 多行值的后续行
168
190
  current_value.append(line.strip())
169
191
  # 处理最后一个键值对
170
192
  if current_key is not None:
@@ -175,11 +197,16 @@ def _read_old_config_file(config_file):
175
197
  elif value.lower() == "false":
176
198
  value = False
177
199
  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})
200
+ os.environ.update(
201
+ {str(k): str(v) for k, v in config_data.items() if v is not None}
202
+ )
179
203
  set_global_env_data(config_data)
180
- PrettyOutput.print(f"检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式", OutputType.WARNING)
204
+ PrettyOutput.print(
205
+ f"检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式",
206
+ OutputType.WARNING,
207
+ )
208
+
181
209
 
182
-
183
210
  def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
184
211
  """循环执行函数直到成功
185
212
 
@@ -194,19 +221,23 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
194
221
  try:
195
222
  return func()
196
223
  except Exception as e:
197
- PrettyOutput.print(f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.WARNING)
224
+ PrettyOutput.print(
225
+ f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.WARNING
226
+ )
198
227
  time.sleep(sleep_time)
199
228
  continue
229
+
230
+
200
231
  def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
201
232
  """循环执行函数直到返回True
202
-
233
+
203
234
  参数:
204
235
  func: 要执行的函数,必须返回布尔值
205
236
  sleep_time: 每次失败后的等待时间(秒)
206
-
237
+
207
238
  返回:
208
239
  函数最终返回的True值
209
-
240
+
210
241
  注意:
211
242
  与while_success不同,此函数只检查返回是否为True,
212
243
  不捕获异常,异常会直接抛出
@@ -218,7 +249,9 @@ def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
218
249
  PrettyOutput.print(f"执行失败, 等待 {sleep_time}s...", OutputType.WARNING)
219
250
  time.sleep(sleep_time)
220
251
  return ret
221
- def get_file_md5(filepath: str)->str:
252
+
253
+
254
+ def get_file_md5(filepath: str) -> str:
222
255
  """计算文件内容的MD5哈希值
223
256
 
224
257
  参数:
@@ -227,7 +260,9 @@ def get_file_md5(filepath: str)->str:
227
260
  返回:
228
261
  str: 文件内容的MD5哈希值
229
262
  """
230
- return hashlib.md5(open(filepath, "rb").read(100*1024*1024)).hexdigest()
263
+ return hashlib.md5(open(filepath, "rb").read(100 * 1024 * 1024)).hexdigest()
264
+
265
+
231
266
  def user_confirm(tip: str, default: bool = True) -> bool:
232
267
  """提示用户确认是/否问题
233
268
 
@@ -242,6 +277,7 @@ def user_confirm(tip: str, default: bool = True) -> bool:
242
277
  ret = get_single_line_input(f"{tip} {suffix}: ")
243
278
  return default if ret == "" else ret.lower() == "y"
244
279
 
280
+
245
281
  def get_file_line_count(filename: str) -> int:
246
282
  """计算文件中的行数
247
283
 
@@ -257,7 +293,6 @@ def get_file_line_count(filename: str) -> int:
257
293
  return 0
258
294
 
259
295
 
260
-
261
296
  def _get_cmd_stats() -> Dict[str, int]:
262
297
  """从数据目录获取命令调用统计"""
263
298
  stats_file = Path(get_data_dir()) / "cmd_stat.yaml"
@@ -266,11 +301,10 @@ def _get_cmd_stats() -> Dict[str, int]:
266
301
  with open(stats_file, "r", encoding="utf-8") as f:
267
302
  return yaml.safe_load(f) or {}
268
303
  except Exception as e:
269
- PrettyOutput.print(
270
- f"加载命令调用统计失败: {str(e)}", OutputType.WARNING
271
- )
304
+ PrettyOutput.print(f"加载命令调用统计失败: {str(e)}", OutputType.WARNING)
272
305
  return {}
273
306
 
307
+
274
308
  def _update_cmd_stats(cmd_name: str) -> None:
275
309
  """更新命令调用统计"""
276
310
  stats = _get_cmd_stats()
@@ -280,31 +314,29 @@ def _update_cmd_stats(cmd_name: str) -> None:
280
314
  with open(stats_file, "w", encoding="utf-8") as f:
281
315
  yaml.safe_dump(stats, f, allow_unicode=True)
282
316
  except Exception as e:
283
- PrettyOutput.print(
284
- f"保存命令调用统计失败: {str(e)}", OutputType.WARNING
285
- )
317
+ PrettyOutput.print(f"保存命令调用统计失败: {str(e)}", OutputType.WARNING)
318
+
286
319
 
287
320
  def count_cmd_usage() -> None:
288
321
  """统计当前命令的使用次数"""
289
322
  import sys
323
+
290
324
  _update_cmd_stats(sys.argv[0])
291
325
 
326
+
292
327
  def is_context_overflow(content: str) -> bool:
293
328
  """判断文件内容是否超出上下文限制"""
294
- return get_context_token_count(content) > get_max_big_content_size()
329
+ return get_context_token_count(content) > get_max_big_content_size()
330
+
295
331
 
296
332
  def get_loc_stats() -> str:
297
333
  """使用loc命令获取当前目录的代码统计信息
298
-
334
+
299
335
  返回:
300
336
  str: loc命令输出的原始字符串,失败时返回空字符串
301
337
  """
302
338
  try:
303
- result = subprocess.run(
304
- ['loc'],
305
- capture_output=True,
306
- text=True
307
- )
339
+ result = subprocess.run(["loc"], capture_output=True, text=True)
308
340
  return result.stdout if result.returncode == 0 else ""
309
341
  except FileNotFoundError:
310
- return ""
342
+ return ""