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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +13 -7
- jarvis/jarvis_agent/edit_file_handler.py +1 -2
- jarvis/jarvis_code_agent/code_agent.py +7 -13
- jarvis/jarvis_data/config_schema.json +2 -19
- jarvis/jarvis_stats/cli.py +72 -5
- jarvis/jarvis_stats/stats.py +175 -70
- jarvis/jarvis_stats/storage.py +53 -1
- jarvis/jarvis_stats/visualizer.py +63 -224
- jarvis/jarvis_tools/cli/main.py +7 -9
- jarvis/jarvis_tools/registry.py +2 -5
- jarvis/jarvis_utils/config.py +6 -8
- jarvis/jarvis_utils/methodology.py +74 -67
- jarvis/jarvis_utils/utils.py +342 -119
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/METADATA +11 -2
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/RECORD +20 -20
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/utils.py
CHANGED
@@ -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
|
-
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
if
|
209
|
-
|
210
|
-
|
211
|
-
|
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
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
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"{
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
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
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
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
|
-
"""
|
790
|
+
"""将文本复制到剪贴板,支持Windows、macOS和Linux
|
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
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
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.
|
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**:
|
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
|
#### 手动安装
|