jarvis-ai-assistant 0.2.4__py3-none-any.whl → 0.2.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +80 -16
- jarvis/jarvis_agent/edit_file_handler.py +5 -6
- jarvis/jarvis_code_agent/code_agent.py +17 -24
- jarvis/jarvis_data/config_schema.json +2 -19
- jarvis/jarvis_multi_agent/main.py +1 -0
- 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_tools/retrieve_memory.py +206 -0
- jarvis/jarvis_tools/save_memory.py +142 -0
- jarvis/jarvis_utils/config.py +6 -8
- jarvis/jarvis_utils/globals.py +120 -1
- jarvis/jarvis_utils/methodology.py +74 -67
- jarvis/jarvis_utils/utils.py +362 -121
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.6.dist-info}/METADATA +11 -2
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.6.dist-info}/RECORD +24 -22
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.6.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.6.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 (
|
@@ -24,6 +30,35 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
24
30
|
|
25
31
|
g_config_file = None
|
26
32
|
|
33
|
+
COMMAND_MAPPING = {
|
34
|
+
# jarvis主命令
|
35
|
+
"jvs": "jarvis",
|
36
|
+
# 代码代理
|
37
|
+
"jca": "jarvis-code-agent",
|
38
|
+
# 智能shell
|
39
|
+
"jss": "jarvis-smart-shell",
|
40
|
+
# 平台管理
|
41
|
+
"jpm": "jarvis-platform-manager",
|
42
|
+
# Git提交
|
43
|
+
"jgc": "jarvis-git-commit",
|
44
|
+
# 代码审查
|
45
|
+
"jcr": "jarvis-code-review",
|
46
|
+
# Git压缩
|
47
|
+
"jgs": "jarvis-git-squash",
|
48
|
+
# 多代理
|
49
|
+
"jma": "jarvis-multi-agent",
|
50
|
+
# 代理
|
51
|
+
"ja": "jarvis-agent",
|
52
|
+
# 工具
|
53
|
+
"jt": "jarvis-tool",
|
54
|
+
# 方法论
|
55
|
+
"jm": "jarvis-methodology",
|
56
|
+
# RAG
|
57
|
+
"jrg": "jarvis-rag",
|
58
|
+
# 统计
|
59
|
+
"jst": "jarvis-stats",
|
60
|
+
}
|
61
|
+
|
27
62
|
|
28
63
|
def _setup_signal_handler() -> None:
|
29
64
|
"""设置SIGINT信号处理函数"""
|
@@ -79,15 +114,20 @@ def _check_git_updates() -> bool:
|
|
79
114
|
|
80
115
|
def _show_usage_stats() -> None:
|
81
116
|
"""显示Jarvis使用统计信息"""
|
117
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
118
|
+
|
82
119
|
try:
|
83
|
-
from jarvis.jarvis_stats.stats import StatsManager
|
84
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
85
120
|
from datetime import datetime
|
86
121
|
|
87
|
-
|
122
|
+
from rich.console import Console, Group
|
123
|
+
from rich.panel import Panel
|
124
|
+
from rich.table import Table
|
125
|
+
from rich.text import Text
|
126
|
+
|
127
|
+
from jarvis.jarvis_stats.stats import StatsManager
|
88
128
|
|
89
129
|
# 获取所有可用的指标
|
90
|
-
all_metrics =
|
130
|
+
all_metrics = StatsManager.list_metrics()
|
91
131
|
|
92
132
|
# 根据指标名称和标签自动分类
|
93
133
|
categorized_stats: Dict[str, Dict[str, Any]] = {
|
@@ -96,12 +136,13 @@ def _show_usage_stats() -> None:
|
|
96
136
|
"lines": {"title": "📊 代码行数", "metrics": {}, "suffix": "行"},
|
97
137
|
"commit": {"title": "💾 提交统计", "metrics": {}, "suffix": "个"},
|
98
138
|
"command": {"title": "📱 命令使用", "metrics": {}, "suffix": "次"},
|
139
|
+
"adoption": {"title": "🎯 采纳情况", "metrics": {}, "suffix": ""},
|
99
140
|
}
|
100
141
|
|
101
142
|
# 遍历所有指标,获取统计数据
|
102
143
|
for metric in all_metrics:
|
103
144
|
# 获取该指标的所有数据
|
104
|
-
stats_data =
|
145
|
+
stats_data = StatsManager.get_stats(
|
105
146
|
metric_name=metric,
|
106
147
|
start_time=datetime(2000, 1, 1),
|
107
148
|
end_time=datetime.now(),
|
@@ -137,6 +178,50 @@ def _show_usage_stats() -> None:
|
|
137
178
|
elif group == "command":
|
138
179
|
categorized_stats["command"]["metrics"][metric] = int(total)
|
139
180
|
|
181
|
+
# 合并长短命令的历史统计数据
|
182
|
+
command_stats = categorized_stats["command"]["metrics"]
|
183
|
+
if command_stats:
|
184
|
+
merged_stats: Dict[str, int] = {}
|
185
|
+
for metric, count in command_stats.items():
|
186
|
+
long_command = COMMAND_MAPPING.get(metric, metric)
|
187
|
+
merged_stats[long_command] = merged_stats.get(long_command, 0) + count
|
188
|
+
categorized_stats["command"]["metrics"] = merged_stats
|
189
|
+
|
190
|
+
# 计算采纳率并添加到统计中
|
191
|
+
commit_stats = categorized_stats["commit"]["metrics"]
|
192
|
+
# 尝试多种可能的指标名称
|
193
|
+
generated_commits = commit_stats.get(
|
194
|
+
"commits_generated", commit_stats.get("commit_generated", 0)
|
195
|
+
)
|
196
|
+
accepted_commits = commit_stats.get(
|
197
|
+
"commits_accepted",
|
198
|
+
commit_stats.get("commit_accepted", commit_stats.get("commit_adopted", 0)),
|
199
|
+
)
|
200
|
+
rejected_commits = commit_stats.get(
|
201
|
+
"commits_rejected", commit_stats.get("commit_rejected", 0)
|
202
|
+
)
|
203
|
+
|
204
|
+
# 如果有 generated 和 accepted,则使用这两个计算采纳率
|
205
|
+
if generated_commits > 0 and accepted_commits > 0:
|
206
|
+
adoption_rate = (accepted_commits / generated_commits) * 100
|
207
|
+
categorized_stats["adoption"]["metrics"][
|
208
|
+
"adoption_rate"
|
209
|
+
] = f"{adoption_rate:.1f}%"
|
210
|
+
categorized_stats["adoption"]["metrics"][
|
211
|
+
"commits_status"
|
212
|
+
] = f"{accepted_commits}/{generated_commits}"
|
213
|
+
elif accepted_commits > 0 or rejected_commits > 0:
|
214
|
+
# 否则使用 accepted 和 rejected 计算
|
215
|
+
total_commits = accepted_commits + rejected_commits
|
216
|
+
if total_commits > 0:
|
217
|
+
adoption_rate = (accepted_commits / total_commits) * 100
|
218
|
+
categorized_stats["adoption"]["metrics"][
|
219
|
+
"adoption_rate"
|
220
|
+
] = f"{adoption_rate:.1f}%"
|
221
|
+
categorized_stats["adoption"]["metrics"][
|
222
|
+
"commits_status"
|
223
|
+
] = f"{accepted_commits}/{total_commits}"
|
224
|
+
|
140
225
|
# 构建输出
|
141
226
|
has_data = False
|
142
227
|
stats_output = []
|
@@ -148,18 +233,73 @@ def _show_usage_stats() -> None:
|
|
148
233
|
|
149
234
|
# 显示统计信息
|
150
235
|
if has_data:
|
151
|
-
#
|
152
|
-
|
153
|
-
|
236
|
+
# 1. 创建统计表格
|
237
|
+
from rich import box
|
238
|
+
|
239
|
+
table = Table(
|
240
|
+
show_header=True,
|
241
|
+
header_style="bold magenta",
|
242
|
+
title="📊 Jarvis 使用统计",
|
243
|
+
title_justify="center",
|
244
|
+
box=box.ROUNDED,
|
245
|
+
padding=(0, 1),
|
246
|
+
)
|
247
|
+
table.add_column("分类", style="cyan", no_wrap=True, width=12)
|
248
|
+
table.add_column("指标", style="white", width=20)
|
249
|
+
table.add_column("数量", style="green", justify="right", width=10)
|
250
|
+
table.add_column("分类", style="cyan", no_wrap=True, width=12)
|
251
|
+
table.add_column("指标", style="white", width=20)
|
252
|
+
table.add_column("数量", style="green", justify="right", width=10)
|
253
|
+
|
254
|
+
# 收集所有要显示的数据
|
255
|
+
all_rows = []
|
154
256
|
for title, stats, suffix in stats_output:
|
155
257
|
if stats:
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
):
|
160
|
-
# 美化指标名称
|
258
|
+
sorted_stats = sorted(
|
259
|
+
stats.items(), key=lambda item: item[1], reverse=True
|
260
|
+
)
|
261
|
+
for i, (metric, count) in enumerate(sorted_stats):
|
161
262
|
display_name = metric.replace("_", " ").title()
|
162
|
-
|
263
|
+
category_title = title if i == 0 else ""
|
264
|
+
# 处理不同类型的count值
|
265
|
+
if isinstance(count, (int, float)):
|
266
|
+
count_str = f"{count:,} {suffix}"
|
267
|
+
else:
|
268
|
+
# 对于字符串类型的count(如百分比或比率),直接使用
|
269
|
+
count_str = str(count)
|
270
|
+
all_rows.append((category_title, display_name, count_str))
|
271
|
+
|
272
|
+
# 以3行2列的方式添加数据
|
273
|
+
has_content = len(all_rows) > 0
|
274
|
+
# 计算需要多少行来显示所有数据
|
275
|
+
total_rows = len(all_rows)
|
276
|
+
rows_needed = (total_rows + 1) // 2 # 向上取整,因为是2列布局
|
277
|
+
|
278
|
+
for i in range(rows_needed):
|
279
|
+
left_idx = i
|
280
|
+
right_idx = i + rows_needed
|
281
|
+
|
282
|
+
if left_idx < len(all_rows):
|
283
|
+
left_row = all_rows[left_idx]
|
284
|
+
else:
|
285
|
+
left_row = ("", "", "")
|
286
|
+
|
287
|
+
if right_idx < len(all_rows):
|
288
|
+
right_row = all_rows[right_idx]
|
289
|
+
else:
|
290
|
+
right_row = ("", "", "")
|
291
|
+
|
292
|
+
table.add_row(
|
293
|
+
left_row[0],
|
294
|
+
left_row[1],
|
295
|
+
left_row[2],
|
296
|
+
right_row[0],
|
297
|
+
right_row[1],
|
298
|
+
right_row[2],
|
299
|
+
)
|
300
|
+
|
301
|
+
# 2. 创建总结面板
|
302
|
+
summary_content = []
|
163
303
|
|
164
304
|
# 总结统计
|
165
305
|
total_tools = sum(
|
@@ -175,80 +315,135 @@ def _show_usage_stats() -> None:
|
|
175
315
|
for metric, count in stats.items()
|
176
316
|
)
|
177
317
|
|
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()
|
318
|
+
# 统计代码行数
|
319
|
+
lines_stats = categorized_stats["lines"]["metrics"]
|
320
|
+
total_lines_added = lines_stats.get(
|
321
|
+
"code_lines_inserted", lines_stats.get("code_lines_added", 0)
|
201
322
|
)
|
202
|
-
|
323
|
+
total_lines_deleted = lines_stats.get("code_lines_deleted", 0)
|
324
|
+
total_lines_modified = total_lines_added + total_lines_deleted
|
203
325
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
if
|
209
|
-
|
210
|
-
|
211
|
-
|
326
|
+
if total_tools > 0 or total_changes > 0 or total_lines_modified > 0:
|
327
|
+
parts = []
|
328
|
+
if total_tools > 0:
|
329
|
+
parts.append(f"工具调用 {total_tools:,} 次")
|
330
|
+
if total_changes > 0:
|
331
|
+
parts.append(f"代码修改 {total_changes:,} 次")
|
332
|
+
if total_lines_modified > 0:
|
333
|
+
parts.append(f"代码行数 {total_lines_modified:,} 行")
|
212
334
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
335
|
+
if parts:
|
336
|
+
summary_content.append(f"📈 总计: {', '.join(parts)}")
|
337
|
+
|
338
|
+
# 计算节省的时间
|
339
|
+
time_saved_seconds = 0
|
340
|
+
tool_stats = categorized_stats["tool"]["metrics"]
|
341
|
+
code_agent_changes = categorized_stats["code"]["metrics"]
|
342
|
+
lines_stats = categorized_stats["lines"]["metrics"]
|
343
|
+
# commit_stats is already defined above
|
344
|
+
command_stats = categorized_stats["command"]["metrics"]
|
345
|
+
|
346
|
+
# 统一的工具使用时间估算(每次调用节省2分钟)
|
347
|
+
DEFAULT_TOOL_TIME_SAVINGS = 2 * 60 # 秒
|
348
|
+
|
349
|
+
# 计算所有工具的时间节省
|
350
|
+
for tool_name, count in tool_stats.items():
|
351
|
+
time_saved_seconds += count * DEFAULT_TOOL_TIME_SAVINGS
|
352
|
+
|
353
|
+
# 其他类型的时间计算
|
354
|
+
total_code_agent_calls = sum(code_agent_changes.values())
|
355
|
+
time_saved_seconds += total_code_agent_calls * 10 * 60
|
356
|
+
time_saved_seconds += lines_stats.get("code_lines_added", 0) * 0.8 * 60
|
357
|
+
time_saved_seconds += lines_stats.get("code_lines_deleted", 0) * 0.2 * 60
|
358
|
+
time_saved_seconds += sum(commit_stats.values()) * 10 * 60
|
359
|
+
time_saved_seconds += sum(command_stats.values()) * 1 * 60
|
360
|
+
|
361
|
+
time_str = ""
|
362
|
+
hours = 0
|
363
|
+
if time_saved_seconds > 0:
|
364
|
+
total_minutes = int(time_saved_seconds / 60)
|
365
|
+
seconds = int(time_saved_seconds % 60)
|
366
|
+
hours = total_minutes // 60
|
367
|
+
minutes = total_minutes % 60
|
368
|
+
# 只显示小时和分钟
|
369
|
+
if hours > 0:
|
232
370
|
time_str = f"{hours} 小时 {minutes} 分钟"
|
371
|
+
elif total_minutes > 0:
|
372
|
+
time_str = f"{minutes} 分钟 {seconds} 秒"
|
233
373
|
else:
|
234
|
-
time_str = f"{
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
374
|
+
time_str = f"{seconds} 秒"
|
375
|
+
|
376
|
+
if summary_content:
|
377
|
+
summary_content.append("") # Add a separator line
|
378
|
+
summary_content.append(f"⏱️ 节省时间: 约 {time_str}")
|
379
|
+
|
380
|
+
encouragement = ""
|
381
|
+
# 计算各级时间单位
|
382
|
+
total_work_days = hours // 8 # 总工作日数
|
383
|
+
work_years = total_work_days // 240 # 每年约240个工作日
|
384
|
+
remaining_days_after_years = total_work_days % 240
|
385
|
+
work_months = remaining_days_after_years // 20 # 每月约20个工作日
|
386
|
+
remaining_days_after_months = remaining_days_after_years % 20
|
387
|
+
work_days = remaining_days_after_months
|
388
|
+
remaining_hours = int(hours % 8) # 剩余不足一个工作日的小时数
|
389
|
+
|
390
|
+
# 构建时间描述
|
391
|
+
time_parts = []
|
392
|
+
if work_years > 0:
|
393
|
+
time_parts.append(f"{work_years} 年")
|
394
|
+
if work_months > 0:
|
395
|
+
time_parts.append(f"{work_months} 个月")
|
396
|
+
if work_days > 0:
|
397
|
+
time_parts.append(f"{work_days} 个工作日")
|
398
|
+
if remaining_hours > 0:
|
399
|
+
time_parts.append(f"{remaining_hours} 小时")
|
400
|
+
|
401
|
+
if time_parts:
|
402
|
+
time_description = "、".join(time_parts)
|
403
|
+
if work_years >= 1:
|
404
|
+
encouragement = (
|
405
|
+
f"🎉 相当于节省了 {time_description} 的工作时间!"
|
406
|
+
)
|
407
|
+
elif work_months >= 1:
|
408
|
+
encouragement = (
|
409
|
+
f"🚀 相当于节省了 {time_description} 的工作时间!"
|
410
|
+
)
|
411
|
+
elif work_days >= 1:
|
412
|
+
encouragement = (
|
413
|
+
f"💪 相当于节省了 {time_description} 的工作时间!"
|
414
|
+
)
|
415
|
+
else:
|
416
|
+
encouragement = (
|
417
|
+
f"✨ 相当于节省了 {time_description} 的工作时间!"
|
418
|
+
)
|
247
419
|
elif hours >= 1:
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
420
|
+
encouragement = f"⭐ 相当于节省了 {int(hours)} 小时的工作时间,积少成多,继续保持!"
|
421
|
+
if encouragement:
|
422
|
+
summary_content.append(encouragement)
|
423
|
+
|
424
|
+
# 3. 组合并打印
|
425
|
+
render_items: List[RenderableType] = []
|
426
|
+
if has_content:
|
427
|
+
# 居中显示表格
|
428
|
+
centered_table = Align.center(table)
|
429
|
+
render_items.append(centered_table)
|
430
|
+
|
431
|
+
if summary_content:
|
432
|
+
summary_panel = Panel(
|
433
|
+
Text("\n".join(summary_content), justify="left"),
|
434
|
+
title="✨ 总体表现 ✨",
|
435
|
+
title_align="center",
|
436
|
+
border_style="green",
|
437
|
+
expand=False,
|
438
|
+
)
|
439
|
+
# 居中显示面板
|
440
|
+
centered_panel = Align.center(summary_panel)
|
441
|
+
render_items.append(centered_panel)
|
442
|
+
|
443
|
+
if render_items:
|
444
|
+
console = Console()
|
445
|
+
render_group = Group(*render_items)
|
446
|
+
console.print(render_group)
|
252
447
|
except Exception as e:
|
253
448
|
# 输出错误信息以便调试
|
254
449
|
import traceback
|
@@ -310,7 +505,9 @@ def load_config():
|
|
310
505
|
if schema_path.exists():
|
311
506
|
try:
|
312
507
|
config_file_path.parent.mkdir(parents=True, exist_ok=True)
|
313
|
-
generate_default_config(
|
508
|
+
generate_default_config(
|
509
|
+
str(schema_path.absolute()), str(config_file_path)
|
510
|
+
)
|
314
511
|
PrettyOutput.print(
|
315
512
|
f"已生成默认配置文件: {config_file_path}", OutputType.INFO
|
316
513
|
)
|
@@ -427,7 +624,7 @@ def generate_default_config(schema_path: str, output_path: str) -> None:
|
|
427
624
|
|
428
625
|
default_config = _generate_from_schema(schema)
|
429
626
|
|
430
|
-
content = f"# yaml-language-server: $schema={
|
627
|
+
content = f"# yaml-language-server: $schema={schema_path}\n"
|
431
628
|
content += yaml.dump(default_config, allow_unicode=True, sort_keys=False)
|
432
629
|
|
433
630
|
with open(output_path, "w", encoding="utf-8") as f:
|
@@ -573,9 +770,14 @@ def count_cmd_usage() -> None:
|
|
573
770
|
cmd_path = sys.argv[0]
|
574
771
|
cmd_name = os.path.basename(cmd_path)
|
575
772
|
|
773
|
+
# 如果是短命令,映射到长命令
|
774
|
+
if cmd_name in COMMAND_MAPPING:
|
775
|
+
metric_name = COMMAND_MAPPING[cmd_name]
|
776
|
+
else:
|
777
|
+
metric_name = cmd_name
|
778
|
+
|
576
779
|
# 使用 StatsManager 记录命令使用统计
|
577
|
-
|
578
|
-
stats_manager.increment(cmd_name, group="command")
|
780
|
+
StatsManager.increment(metric_name, group="command")
|
579
781
|
|
580
782
|
|
581
783
|
def is_context_overflow(
|
@@ -603,7 +805,7 @@ def get_loc_stats() -> str:
|
|
603
805
|
|
604
806
|
|
605
807
|
def copy_to_clipboard(text: str) -> None:
|
606
|
-
"""
|
808
|
+
"""将文本复制到剪贴板,支持Windows、macOS和Linux
|
607
809
|
|
608
810
|
参数:
|
609
811
|
text: 要复制的文本
|
@@ -611,41 +813,80 @@ def copy_to_clipboard(text: str) -> None:
|
|
611
813
|
print("--- 剪贴板内容开始 ---")
|
612
814
|
print(text)
|
613
815
|
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
816
|
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
817
|
+
system = platform.system()
|
818
|
+
|
819
|
+
# Windows系统
|
820
|
+
if system == "Windows":
|
821
|
+
try:
|
822
|
+
# 使用Windows的clip命令
|
823
|
+
process = subprocess.Popen(
|
824
|
+
["clip"],
|
825
|
+
stdin=subprocess.PIPE,
|
826
|
+
stdout=subprocess.DEVNULL,
|
827
|
+
stderr=subprocess.DEVNULL,
|
828
|
+
shell=True,
|
829
|
+
)
|
830
|
+
if process.stdin:
|
831
|
+
process.stdin.write(text.encode("utf-8"))
|
832
|
+
process.stdin.close()
|
833
|
+
return
|
834
|
+
except Exception as e:
|
835
|
+
PrettyOutput.print(f"使用Windows clip命令时出错: {e}", OutputType.WARNING)
|
836
|
+
|
837
|
+
# macOS系统
|
838
|
+
elif system == "Darwin":
|
839
|
+
try:
|
840
|
+
process = subprocess.Popen(
|
841
|
+
["pbcopy"],
|
842
|
+
stdin=subprocess.PIPE,
|
843
|
+
stdout=subprocess.DEVNULL,
|
844
|
+
stderr=subprocess.DEVNULL,
|
845
|
+
)
|
846
|
+
if process.stdin:
|
847
|
+
process.stdin.write(text.encode("utf-8"))
|
848
|
+
process.stdin.close()
|
849
|
+
return
|
850
|
+
except Exception as e:
|
851
|
+
PrettyOutput.print(f"使用macOS pbcopy命令时出错: {e}", OutputType.WARNING)
|
852
|
+
|
853
|
+
# Linux系统
|
854
|
+
else:
|
855
|
+
# 尝试使用 xsel
|
856
|
+
try:
|
857
|
+
process = subprocess.Popen(
|
858
|
+
["xsel", "-b", "-i"],
|
859
|
+
stdin=subprocess.PIPE,
|
860
|
+
stdout=subprocess.DEVNULL,
|
861
|
+
stderr=subprocess.DEVNULL,
|
862
|
+
)
|
863
|
+
if process.stdin:
|
864
|
+
process.stdin.write(text.encode("utf-8"))
|
865
|
+
process.stdin.close()
|
866
|
+
return
|
867
|
+
except FileNotFoundError:
|
868
|
+
pass # xsel 未安装,继续尝试下一个
|
869
|
+
except Exception as e:
|
870
|
+
PrettyOutput.print(f"使用xsel时出错: {e}", OutputType.WARNING)
|
871
|
+
|
872
|
+
# 尝试使用 xclip
|
873
|
+
try:
|
874
|
+
process = subprocess.Popen(
|
875
|
+
["xclip", "-selection", "clipboard"],
|
876
|
+
stdin=subprocess.PIPE,
|
877
|
+
stdout=subprocess.DEVNULL,
|
878
|
+
stderr=subprocess.DEVNULL,
|
879
|
+
)
|
880
|
+
if process.stdin:
|
881
|
+
process.stdin.write(text.encode("utf-8"))
|
882
|
+
process.stdin.close()
|
883
|
+
return
|
884
|
+
except FileNotFoundError:
|
885
|
+
PrettyOutput.print(
|
886
|
+
"xsel 和 xclip 均未安装, 无法复制到剪贴板", OutputType.WARNING
|
887
|
+
)
|
888
|
+
except Exception as e:
|
889
|
+
PrettyOutput.print(f"使用xclip时出错: {e}", OutputType.WARNING)
|
649
890
|
|
650
891
|
|
651
892
|
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.6
|
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
|
#### 手动安装
|