jarvis-ai-assistant 0.2.4__py3-none-any.whl → 0.2.5__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.
@@ -2,6 +2,7 @@
2
2
  import hashlib
3
3
  import json
4
4
  import os
5
+ import platform
5
6
  import signal
6
7
  import subprocess
7
8
  import sys
@@ -11,6 +12,11 @@ from typing import Any, Callable, Dict, List, Optional
11
12
  from datetime import datetime
12
13
 
13
14
  import yaml # type: ignore
15
+ from rich.align import Align
16
+ from rich.console import Group, RenderableType
17
+ from rich.panel import Panel
18
+ from rich.table import Table
19
+ from rich.text import Text
14
20
 
15
21
  from jarvis import __version__
16
22
  from jarvis.jarvis_utils.config import (
@@ -79,15 +85,20 @@ def _check_git_updates() -> bool:
79
85
 
80
86
  def _show_usage_stats() -> None:
81
87
  """显示Jarvis使用统计信息"""
88
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
89
+
82
90
  try:
83
- from jarvis.jarvis_stats.stats import StatsManager
84
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
85
91
  from datetime import datetime
86
92
 
87
- stats_manager = StatsManager()
93
+ from rich.console import Console, Group
94
+ from rich.panel import Panel
95
+ from rich.table import Table
96
+ from rich.text import Text
97
+
98
+ from jarvis.jarvis_stats.stats import StatsManager
88
99
 
89
100
  # 获取所有可用的指标
90
- all_metrics = stats_manager.list_metrics()
101
+ all_metrics = StatsManager.list_metrics()
91
102
 
92
103
  # 根据指标名称和标签自动分类
93
104
  categorized_stats: Dict[str, Dict[str, Any]] = {
@@ -96,12 +107,13 @@ def _show_usage_stats() -> None:
96
107
  "lines": {"title": "📊 代码行数", "metrics": {}, "suffix": "行"},
97
108
  "commit": {"title": "💾 提交统计", "metrics": {}, "suffix": "个"},
98
109
  "command": {"title": "📱 命令使用", "metrics": {}, "suffix": "次"},
110
+ "adoption": {"title": "🎯 采纳情况", "metrics": {}, "suffix": ""},
99
111
  }
100
112
 
101
113
  # 遍历所有指标,获取统计数据
102
114
  for metric in all_metrics:
103
115
  # 获取该指标的所有数据
104
- stats_data = stats_manager.get_stats(
116
+ stats_data = StatsManager.get_stats(
105
117
  metric_name=metric,
106
118
  start_time=datetime(2000, 1, 1),
107
119
  end_time=datetime.now(),
@@ -137,6 +149,41 @@ def _show_usage_stats() -> None:
137
149
  elif group == "command":
138
150
  categorized_stats["command"]["metrics"][metric] = int(total)
139
151
 
152
+ # 计算采纳率并添加到统计中
153
+ commit_stats = categorized_stats["commit"]["metrics"]
154
+ # 尝试多种可能的指标名称
155
+ generated_commits = commit_stats.get(
156
+ "commits_generated", commit_stats.get("commit_generated", 0)
157
+ )
158
+ accepted_commits = commit_stats.get(
159
+ "commits_accepted",
160
+ commit_stats.get("commit_accepted", commit_stats.get("commit_adopted", 0)),
161
+ )
162
+ rejected_commits = commit_stats.get(
163
+ "commits_rejected", commit_stats.get("commit_rejected", 0)
164
+ )
165
+
166
+ # 如果有 generated 和 accepted,则使用这两个计算采纳率
167
+ if generated_commits > 0 and accepted_commits > 0:
168
+ adoption_rate = (accepted_commits / generated_commits) * 100
169
+ categorized_stats["adoption"]["metrics"][
170
+ "adoption_rate"
171
+ ] = f"{adoption_rate:.1f}%"
172
+ categorized_stats["adoption"]["metrics"][
173
+ "commits_status"
174
+ ] = f"{accepted_commits}/{generated_commits}"
175
+ elif accepted_commits > 0 or rejected_commits > 0:
176
+ # 否则使用 accepted 和 rejected 计算
177
+ total_commits = accepted_commits + rejected_commits
178
+ if total_commits > 0:
179
+ adoption_rate = (accepted_commits / total_commits) * 100
180
+ categorized_stats["adoption"]["metrics"][
181
+ "adoption_rate"
182
+ ] = f"{adoption_rate:.1f}%"
183
+ categorized_stats["adoption"]["metrics"][
184
+ "commits_status"
185
+ ] = f"{accepted_commits}/{total_commits}"
186
+
140
187
  # 构建输出
141
188
  has_data = False
142
189
  stats_output = []
@@ -148,18 +195,73 @@ def _show_usage_stats() -> None:
148
195
 
149
196
  # 显示统计信息
150
197
  if has_data:
151
- # 构建统计信息字符串
152
- stats_lines = ["📊 Jarvis 使用统计"]
153
-
198
+ # 1. 创建统计表格
199
+ from rich import box
200
+
201
+ table = Table(
202
+ show_header=True,
203
+ header_style="bold magenta",
204
+ title="📊 Jarvis 使用统计",
205
+ title_justify="center",
206
+ box=box.ROUNDED,
207
+ padding=(0, 1),
208
+ )
209
+ table.add_column("分类", style="cyan", no_wrap=True, width=12)
210
+ table.add_column("指标", style="white", width=20)
211
+ table.add_column("数量", style="green", justify="right", width=10)
212
+ table.add_column("分类", style="cyan", no_wrap=True, width=12)
213
+ table.add_column("指标", style="white", width=20)
214
+ table.add_column("数量", style="green", justify="right", width=10)
215
+
216
+ # 收集所有要显示的数据
217
+ all_rows = []
154
218
  for title, stats, suffix in stats_output:
155
219
  if stats:
156
- stats_lines.append(f"\n{title}:")
157
- for metric, count in sorted(
158
- stats.items(), key=lambda x: x[1], reverse=True
159
- ):
160
- # 美化指标名称
220
+ sorted_stats = sorted(
221
+ stats.items(), key=lambda item: item[1], reverse=True
222
+ )
223
+ for i, (metric, count) in enumerate(sorted_stats):
161
224
  display_name = metric.replace("_", " ").title()
162
- stats_lines.append(f" • {display_name}: {count:,} {suffix}")
225
+ category_title = title if i == 0 else ""
226
+ # 处理不同类型的count值
227
+ if isinstance(count, (int, float)):
228
+ count_str = f"{count:,} {suffix}"
229
+ else:
230
+ # 对于字符串类型的count(如百分比或比率),直接使用
231
+ count_str = str(count)
232
+ all_rows.append((category_title, display_name, count_str))
233
+
234
+ # 以3行2列的方式添加数据
235
+ has_content = len(all_rows) > 0
236
+ # 计算需要多少行来显示所有数据
237
+ total_rows = len(all_rows)
238
+ rows_needed = (total_rows + 1) // 2 # 向上取整,因为是2列布局
239
+
240
+ for i in range(rows_needed):
241
+ left_idx = i
242
+ right_idx = i + rows_needed
243
+
244
+ if left_idx < len(all_rows):
245
+ left_row = all_rows[left_idx]
246
+ else:
247
+ left_row = ("", "", "")
248
+
249
+ if right_idx < len(all_rows):
250
+ right_row = all_rows[right_idx]
251
+ else:
252
+ right_row = ("", "", "")
253
+
254
+ table.add_row(
255
+ left_row[0],
256
+ left_row[1],
257
+ left_row[2],
258
+ right_row[0],
259
+ right_row[1],
260
+ right_row[2],
261
+ )
262
+
263
+ # 2. 创建总结面板
264
+ summary_content = []
163
265
 
164
266
  # 总结统计
165
267
  total_tools = sum(
@@ -175,80 +277,127 @@ def _show_usage_stats() -> None:
175
277
  for metric, count in stats.items()
176
278
  )
177
279
 
178
- if total_tools > 0 or total_changes > 0:
179
- stats_lines.append(
180
- f"\n📈 总计: 工具调用 {total_tools:,} 次, 代码修改 {total_changes:,} 次"
181
- )
182
-
183
- # 计算节省的时间
184
- # 基于经验估算:
185
- # - 每次工具调用平均节省5分钟(相比手动操作)
186
- # - 每行代码修改平均节省60秒(考虑思考、编写、测试时间)
187
- # - 每次提交平均节省15分钟(考虑整理、描述、检查时间)
188
- # - 每个命令调用平均节省5分钟(相比手动执行)
189
-
190
- time_saved_minutes = 0
191
-
192
- # 工具调用节省的时间
193
- time_saved_minutes += total_tools * 5
194
-
195
- # 代码行数节省的时间(每行修改节省60秒)
196
- total_lines = sum(
197
- count
198
- for title, stats, _ in stats_output
199
- if "代码行数" in title
200
- for metric, count in stats.items()
280
+ # 统计代码行数
281
+ lines_stats = categorized_stats["lines"]["metrics"]
282
+ total_lines_added = lines_stats.get(
283
+ "code_lines_inserted", lines_stats.get("code_lines_added", 0)
201
284
  )
202
- time_saved_minutes += total_lines * 1 # 60秒 = 1分钟
285
+ total_lines_deleted = lines_stats.get("code_lines_deleted", 0)
286
+ total_lines_modified = total_lines_added + total_lines_deleted
203
287
 
204
- # 提交节省的时间
205
- total_commits = sum(
206
- count
207
- for title, stats, _ in stats_output
208
- if "提交统计" in title
209
- for metric, count in stats.items()
210
- )
211
- time_saved_minutes += total_commits * 15
288
+ if total_tools > 0 or total_changes > 0 or total_lines_modified > 0:
289
+ parts = []
290
+ if total_tools > 0:
291
+ parts.append(f"工具调用 {total_tools:,} 次")
292
+ if total_changes > 0:
293
+ parts.append(f"代码修改 {total_changes:,} 次")
294
+ if total_lines_modified > 0:
295
+ parts.append(f"代码行数 {total_lines_modified:,} 行")
212
296
 
213
- # 命令调用节省的时间
214
- total_commands = sum(
215
- count
216
- for title, stats, _ in stats_output
217
- if "命令使用" in title
218
- for metric, count in stats.items()
219
- )
220
- time_saved_minutes += total_commands * 5
221
-
222
- # 转换为更友好的格式
223
- if time_saved_minutes > 0:
224
- hours = int(time_saved_minutes // 60)
225
- minutes = int(time_saved_minutes % 60)
226
-
227
- if hours > 24:
228
- days = hours // 24
229
- remaining_hours = hours % 24
230
- time_str = f"{days} 天 {remaining_hours} 小时 {minutes} 分钟"
231
- elif hours > 0:
297
+ if parts:
298
+ summary_content.append(f"📈 总计: {', '.join(parts)}")
299
+
300
+ # 计算节省的时间
301
+ time_saved_seconds = 0
302
+ tool_stats = categorized_stats["tool"]["metrics"]
303
+ code_agent_changes = categorized_stats["code"]["metrics"]
304
+ lines_stats = categorized_stats["lines"]["metrics"]
305
+ # commit_stats is already defined above
306
+ command_stats = categorized_stats["command"]["metrics"]
307
+
308
+ # 统一的工具使用时间估算(每次调用节省2分钟)
309
+ DEFAULT_TOOL_TIME_SAVINGS = 2 * 60 # 秒
310
+
311
+ # 计算所有工具的时间节省
312
+ for tool_name, count in tool_stats.items():
313
+ time_saved_seconds += count * DEFAULT_TOOL_TIME_SAVINGS
314
+
315
+ # 其他类型的时间计算
316
+ total_code_agent_calls = sum(code_agent_changes.values())
317
+ time_saved_seconds += total_code_agent_calls * 10 * 60
318
+ time_saved_seconds += lines_stats.get("code_lines_added", 0) * 0.8 * 60
319
+ time_saved_seconds += lines_stats.get("code_lines_deleted", 0) * 0.2 * 60
320
+ time_saved_seconds += sum(commit_stats.values()) * 10 * 60
321
+ time_saved_seconds += sum(command_stats.values()) * 1 * 60
322
+
323
+ time_str = ""
324
+ hours = 0
325
+ if time_saved_seconds > 0:
326
+ total_minutes = int(time_saved_seconds / 60)
327
+ seconds = int(time_saved_seconds % 60)
328
+ hours = total_minutes // 60
329
+ minutes = total_minutes % 60
330
+ # 只显示小时和分钟
331
+ if hours > 0:
232
332
  time_str = f"{hours} 小时 {minutes} 分钟"
333
+ elif total_minutes > 0:
334
+ time_str = f"{minutes} 分钟 {seconds} 秒"
233
335
  else:
234
- time_str = f"{minutes} 分钟"
235
-
236
- stats_lines.append(f"\n⏱️ 节省时间: 约 {time_str}")
237
-
238
- # 根据节省的时间给出鼓励信息
239
- if hours >= 100:
240
- stats_lines.append(
241
- "🎉 您已经通过 Jarvis 节省了超过100小时的开发时间!"
242
- )
243
- elif hours >= 40:
244
- stats_lines.append("🚀 相当于节省了一整周的工作时间!")
245
- elif hours >= 8:
246
- stats_lines.append("💪 相当于节省了一个工作日的时间!")
336
+ time_str = f"{seconds} "
337
+
338
+ if summary_content:
339
+ summary_content.append("") # Add a separator line
340
+ summary_content.append(f"⏱️ 节省时间: 约 {time_str}")
341
+
342
+ encouragement = ""
343
+ # 计算各级时间单位
344
+ total_work_days = hours // 8 # 总工作日数
345
+ work_years = total_work_days // 240 # 每年约240个工作日
346
+ remaining_days_after_years = total_work_days % 240
347
+ work_months = remaining_days_after_years // 20 # 每月约20个工作日
348
+ remaining_days_after_months = remaining_days_after_years % 20
349
+ work_days = remaining_days_after_months
350
+ remaining_hours = int(hours % 8) # 剩余不足一个工作日的小时数
351
+
352
+ # 构建时间描述
353
+ time_parts = []
354
+ if work_years > 0:
355
+ time_parts.append(f"{work_years} 年")
356
+ if work_months > 0:
357
+ time_parts.append(f"{work_months} 个月")
358
+ if work_days > 0:
359
+ time_parts.append(f"{work_days} 个工作日")
360
+ if remaining_hours > 0:
361
+ time_parts.append(f"{remaining_hours} 小时")
362
+
363
+ if time_parts:
364
+ time_description = "、".join(time_parts)
365
+ if work_years >= 1:
366
+ encouragement = f"🎉 相当于节省了 {time_description} 的工作时间!"
367
+ elif work_months >= 1:
368
+ encouragement = f"🚀 相当于节省了 {time_description} 的工作时间!"
369
+ elif work_days >= 1:
370
+ encouragement = f"💪 相当于节省了 {time_description} 的工作时间!"
371
+ else:
372
+ encouragement = f"✨ 相当于节省了 {time_description} 的工作时间!"
247
373
  elif hours >= 1:
248
- stats_lines.append(" 积少成多,继续保持!")
249
-
250
- # 一次性输出所有统计信息
251
- PrettyOutput.print("\n".join(stats_lines), OutputType.INFO)
374
+ encouragement = f" 相当于节省了 {int(hours)} 小时的工作时间,积少成多,继续保持!"
375
+ if encouragement:
376
+ summary_content.append(encouragement)
377
+
378
+ # 3. 组合并打印
379
+ render_items: List[RenderableType] = []
380
+ if has_content:
381
+ # 居中显示表格
382
+ centered_table = Align.center(table)
383
+ render_items.append(centered_table)
384
+
385
+ if summary_content:
386
+ summary_panel = Panel(
387
+ Text("\n".join(summary_content), justify="left"),
388
+ title="✨ 总体表现 ✨",
389
+ title_align="center",
390
+ border_style="green",
391
+ expand=False,
392
+ )
393
+ # 居中显示面板
394
+ centered_panel = Align.center(summary_panel)
395
+ render_items.append(centered_panel)
396
+
397
+ if render_items:
398
+ console = Console()
399
+ render_group = Group(*render_items)
400
+ console.print(render_group)
252
401
  except Exception as e:
253
402
  # 输出错误信息以便调试
254
403
  import traceback
@@ -569,13 +718,48 @@ def count_cmd_usage() -> None:
569
718
  import os
570
719
  from jarvis.jarvis_stats.stats import StatsManager
571
720
 
721
+ # 命令映射关系:将短命令映射到长命令
722
+ command_mapping = {
723
+ # jarvis主命令
724
+ "jvs": "jarvis",
725
+ # 代码代理
726
+ "jca": "jarvis-code-agent",
727
+ # 智能shell
728
+ "jss": "jarvis-smart-shell",
729
+ # 平台管理
730
+ "jpm": "jarvis-platform-manager",
731
+ # Git提交
732
+ "jgc": "jarvis-git-commit",
733
+ # 代码审查
734
+ "jcr": "jarvis-code-review",
735
+ # Git压缩
736
+ "jgs": "jarvis-git-squash",
737
+ # 多代理
738
+ "jma": "jarvis-multi-agent",
739
+ # 代理
740
+ "ja": "jarvis-agent",
741
+ # 工具
742
+ "jt": "jarvis-tool",
743
+ # 方法论
744
+ "jm": "jarvis-methodology",
745
+ # RAG
746
+ "jrg": "jarvis-rag",
747
+ # 统计
748
+ "jst": "jarvis-stats",
749
+ }
750
+
572
751
  # 从完整路径中提取命令名称
573
752
  cmd_path = sys.argv[0]
574
753
  cmd_name = os.path.basename(cmd_path)
754
+
755
+ # 如果是短命令,映射到长命令
756
+ if cmd_name in command_mapping:
757
+ metric_name = command_mapping[cmd_name]
758
+ else:
759
+ metric_name = cmd_name
575
760
 
576
761
  # 使用 StatsManager 记录命令使用统计
577
- stats_manager = StatsManager()
578
- stats_manager.increment(cmd_name, group="command")
762
+ StatsManager.increment(metric_name, group="command")
579
763
 
580
764
 
581
765
  def is_context_overflow(
@@ -603,7 +787,7 @@ def get_loc_stats() -> str:
603
787
 
604
788
 
605
789
  def copy_to_clipboard(text: str) -> None:
606
- """将文本复制到剪贴板,依次尝试xselxclip (非阻塞)
790
+ """将文本复制到剪贴板,支持Windows、macOSLinux
607
791
 
608
792
  参数:
609
793
  text: 要复制的文本
@@ -611,41 +795,80 @@ def copy_to_clipboard(text: str) -> None:
611
795
  print("--- 剪贴板内容开始 ---")
612
796
  print(text)
613
797
  print("--- 剪贴板内容结束 ---")
614
- # 尝试使用 xsel
615
- try:
616
- process = subprocess.Popen(
617
- ["xsel", "-b", "-i"],
618
- stdin=subprocess.PIPE,
619
- stdout=subprocess.DEVNULL,
620
- stderr=subprocess.DEVNULL,
621
- )
622
- if process.stdin:
623
- process.stdin.write(text.encode("utf-8"))
624
- process.stdin.close()
625
- return
626
- except FileNotFoundError:
627
- pass # xsel 未安装,继续尝试下一个
628
- except Exception as e:
629
- PrettyOutput.print(f"使用xsel时出错: {e}", OutputType.WARNING)
630
798
 
631
- # 尝试使用 xclip
632
- try:
633
- process = subprocess.Popen(
634
- ["xclip", "-selection", "clipboard"],
635
- stdin=subprocess.PIPE,
636
- stdout=subprocess.DEVNULL,
637
- stderr=subprocess.DEVNULL,
638
- )
639
- if process.stdin:
640
- process.stdin.write(text.encode("utf-8"))
641
- process.stdin.close()
642
- return
643
- except FileNotFoundError:
644
- PrettyOutput.print(
645
- "xsel 和 xclip 均未安装, 无法复制到剪贴板", OutputType.WARNING
646
- )
647
- except Exception as e:
648
- PrettyOutput.print(f"使用xclip时出错: {e}", OutputType.WARNING)
799
+ system = platform.system()
800
+
801
+ # Windows系统
802
+ if system == "Windows":
803
+ try:
804
+ # 使用Windows的clip命令
805
+ process = subprocess.Popen(
806
+ ["clip"],
807
+ stdin=subprocess.PIPE,
808
+ stdout=subprocess.DEVNULL,
809
+ stderr=subprocess.DEVNULL,
810
+ shell=True,
811
+ )
812
+ if process.stdin:
813
+ process.stdin.write(text.encode("utf-8"))
814
+ process.stdin.close()
815
+ return
816
+ except Exception as e:
817
+ PrettyOutput.print(f"使用Windows clip命令时出错: {e}", OutputType.WARNING)
818
+
819
+ # macOS系统
820
+ elif system == "Darwin":
821
+ try:
822
+ process = subprocess.Popen(
823
+ ["pbcopy"],
824
+ stdin=subprocess.PIPE,
825
+ stdout=subprocess.DEVNULL,
826
+ stderr=subprocess.DEVNULL,
827
+ )
828
+ if process.stdin:
829
+ process.stdin.write(text.encode("utf-8"))
830
+ process.stdin.close()
831
+ return
832
+ except Exception as e:
833
+ PrettyOutput.print(f"使用macOS pbcopy命令时出错: {e}", OutputType.WARNING)
834
+
835
+ # Linux系统
836
+ else:
837
+ # 尝试使用 xsel
838
+ try:
839
+ process = subprocess.Popen(
840
+ ["xsel", "-b", "-i"],
841
+ stdin=subprocess.PIPE,
842
+ stdout=subprocess.DEVNULL,
843
+ stderr=subprocess.DEVNULL,
844
+ )
845
+ if process.stdin:
846
+ process.stdin.write(text.encode("utf-8"))
847
+ process.stdin.close()
848
+ return
849
+ except FileNotFoundError:
850
+ pass # xsel 未安装,继续尝试下一个
851
+ except Exception as e:
852
+ PrettyOutput.print(f"使用xsel时出错: {e}", OutputType.WARNING)
853
+
854
+ # 尝试使用 xclip
855
+ try:
856
+ process = subprocess.Popen(
857
+ ["xclip", "-selection", "clipboard"],
858
+ stdin=subprocess.PIPE,
859
+ stdout=subprocess.DEVNULL,
860
+ stderr=subprocess.DEVNULL,
861
+ )
862
+ if process.stdin:
863
+ process.stdin.write(text.encode("utf-8"))
864
+ process.stdin.close()
865
+ return
866
+ except FileNotFoundError:
867
+ PrettyOutput.print(
868
+ "xsel 和 xclip 均未安装, 无法复制到剪贴板", OutputType.WARNING
869
+ )
870
+ except Exception as e:
871
+ PrettyOutput.print(f"使用xclip时出错: {e}", OutputType.WARNING)
649
872
 
650
873
 
651
874
  def _pull_git_repo(repo_path: Path, repo_type: str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -60,6 +60,7 @@ Requires-Dist: lxml==6.0.0
60
60
  Requires-Dist: markdownify>=1.1.0
61
61
  Requires-Dist: typer
62
62
  Requires-Dist: pathspec
63
+ Requires-Dist: plotext==5.2.8
63
64
  Provides-Extra: dev
64
65
  Requires-Dist: pytest; extra == "dev"
65
66
  Requires-Dist: black; extra == "dev"
@@ -180,15 +181,23 @@ Jarvis 正是为这种工作流而设计的工具。它通过无缝的命令行
180
181
 
181
182
  ### 系统要求
182
183
  - **Linux**: 完全支持。
183
- - **Windows**: 未经充分测试,建议在 [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) 中使用。
184
+ - **Windows**: 完全支持(需要 PowerShell)。
184
185
 
185
186
  ### 安装
186
187
 
187
188
  #### 一键安装 (推荐)
188
189
  只需一行命令即可完成所有安装和配置:
190
+
191
+ **Linux/macOS:**
189
192
  ```bash
190
193
  bash -c "$(curl -fsSL https://raw.githubusercontent.com/skyfireitdiy/Jarvis/main/scripts/install.sh)"
191
194
  ```
195
+
196
+ **Windows (PowerShell):**
197
+ ```powershell
198
+ iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/skyfireitdiy/Jarvis/main/scripts/install.ps1'))
199
+ ```
200
+
192
201
  > 该脚本会自动检测Python环境、克隆项目、安装依赖并设置好路径。
193
202
 
194
203
  #### 手动安装