jarvis-ai-assistant 0.2.3__py3-none-any.whl → 0.2.4__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/edit_file_handler.py +5 -0
- jarvis/jarvis_agent/jarvis.py +22 -25
- jarvis/jarvis_agent/main.py +6 -6
- jarvis/jarvis_code_agent/code_agent.py +279 -11
- jarvis/jarvis_code_analysis/code_review.py +21 -19
- jarvis/jarvis_data/config_schema.json +23 -10
- jarvis/jarvis_git_squash/main.py +3 -3
- jarvis/jarvis_git_utils/git_commiter.py +32 -11
- jarvis/jarvis_mcp/sse_mcp_client.py +4 -6
- jarvis/jarvis_mcp/streamable_mcp_client.py +5 -9
- jarvis/jarvis_rag/retriever.py +1 -1
- jarvis/jarvis_smart_shell/main.py +2 -2
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +337 -0
- jarvis/jarvis_stats/stats.py +433 -0
- jarvis/jarvis_stats/storage.py +329 -0
- jarvis/jarvis_stats/visualizer.py +443 -0
- jarvis/jarvis_tools/cli/main.py +84 -15
- jarvis/jarvis_tools/registry.py +35 -16
- jarvis/jarvis_tools/search_web.py +3 -3
- jarvis/jarvis_tools/virtual_tty.py +315 -26
- jarvis/jarvis_utils/config.py +6 -0
- jarvis/jarvis_utils/git_utils.py +8 -16
- jarvis/jarvis_utils/utils.py +210 -37
- {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/METADATA +19 -2
- {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/RECORD +31 -26
- {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/entry_points.txt +2 -0
- {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,30 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
import fcntl
|
3
2
|
import os
|
4
|
-
import
|
5
|
-
import select
|
6
|
-
import signal
|
3
|
+
import sys
|
7
4
|
import time
|
8
|
-
from typing import Any, Dict
|
5
|
+
from typing import Any, Dict, TYPE_CHECKING
|
6
|
+
|
7
|
+
# 为了类型检查,总是导入这些模块
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
import fcntl
|
10
|
+
import pty
|
11
|
+
import select
|
12
|
+
import signal
|
13
|
+
import subprocess
|
14
|
+
import threading
|
15
|
+
import queue
|
16
|
+
|
17
|
+
# 平台相关的导入
|
18
|
+
if sys.platform != "win32":
|
19
|
+
import fcntl
|
20
|
+
import pty
|
21
|
+
import select
|
22
|
+
import signal
|
23
|
+
else:
|
24
|
+
# Windows平台的导入
|
25
|
+
import subprocess
|
26
|
+
import threading
|
27
|
+
import queue
|
9
28
|
|
10
29
|
|
11
30
|
class VirtualTTYTool:
|
@@ -14,6 +33,7 @@ class VirtualTTYTool:
|
|
14
33
|
"控制虚拟终端执行各种操作,如启动终端、输入命令、获取输出等。"
|
15
34
|
+ "与execute_script不同,此工具会创建一个持久的虚拟终端会话,可以连续执行多个命令,并保持终端状态。"
|
16
35
|
+ "适用于需要交互式操作的场景,如运行需要用户输入的交互式程序(如:ssh连接、sftp传输、gdb/dlv调试等)。"
|
36
|
+
+ "注意:Windows平台功能有限,某些Unix特有功能可能不可用。"
|
17
37
|
)
|
18
38
|
parameters = {
|
19
39
|
"type": "object",
|
@@ -76,11 +96,21 @@ class VirtualTTYTool:
|
|
76
96
|
|
77
97
|
# 如果指定的tty_id不存在,为其创建一个新的tty_data
|
78
98
|
if tty_id not in agent.tty_sessions:
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
99
|
+
if sys.platform == "win32":
|
100
|
+
import queue as _queue # pylint: disable=import-outside-toplevel
|
101
|
+
|
102
|
+
agent.tty_sessions[tty_id] = {
|
103
|
+
"process": None,
|
104
|
+
"output_queue": _queue.Queue(),
|
105
|
+
"output_thread": None,
|
106
|
+
"shell": "cmd.exe",
|
107
|
+
}
|
108
|
+
else:
|
109
|
+
agent.tty_sessions[tty_id] = {
|
110
|
+
"master_fd": None,
|
111
|
+
"pid": None,
|
112
|
+
"shell": "/bin/bash",
|
113
|
+
}
|
84
114
|
|
85
115
|
action = args.get("action", "").strip().lower()
|
86
116
|
|
@@ -164,13 +194,25 @@ class VirtualTTYTool:
|
|
164
194
|
|
165
195
|
def _launch_tty(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
166
196
|
"""启动虚拟终端"""
|
197
|
+
if sys.platform == "win32":
|
198
|
+
return self._launch_tty_windows(agent, tty_id)
|
199
|
+
else:
|
200
|
+
return self._launch_tty_unix(agent, tty_id)
|
201
|
+
|
202
|
+
def _launch_tty_unix(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
203
|
+
"""Unix/Linux平台启动虚拟终端"""
|
167
204
|
try:
|
168
205
|
# 如果该ID的终端已经启动,先关闭它
|
169
206
|
if agent.tty_sessions[tty_id]["master_fd"] is not None:
|
170
207
|
self._close_tty(agent, tty_id)
|
171
208
|
|
209
|
+
# 在Unix平台上导入需要的模块
|
210
|
+
import pty as _pty # pylint: disable=import-outside-toplevel
|
211
|
+
import fcntl as _fcntl # pylint: disable=import-outside-toplevel
|
212
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
213
|
+
|
172
214
|
# 创建伪终端
|
173
|
-
pid, master_fd =
|
215
|
+
pid, master_fd = _pty.fork()
|
174
216
|
|
175
217
|
if pid == 0: # 子进程
|
176
218
|
# 执行shell
|
@@ -180,7 +222,7 @@ class VirtualTTYTool:
|
|
180
222
|
)
|
181
223
|
else: # 父进程
|
182
224
|
# 设置非阻塞模式
|
183
|
-
|
225
|
+
_fcntl.fcntl(master_fd, _fcntl.F_SETFL, os.O_NONBLOCK)
|
184
226
|
|
185
227
|
# 保存终端状态
|
186
228
|
agent.tty_sessions[tty_id]["master_fd"] = master_fd
|
@@ -191,7 +233,7 @@ class VirtualTTYTool:
|
|
191
233
|
start_time = time.time()
|
192
234
|
while time.time() - start_time < 2.0: # 最多等待2秒
|
193
235
|
try:
|
194
|
-
r, _, _ =
|
236
|
+
r, _, _ = _select.select([master_fd], [], [], 0.1)
|
195
237
|
if r:
|
196
238
|
data = os.read(master_fd, 1024)
|
197
239
|
if data:
|
@@ -211,6 +253,74 @@ class VirtualTTYTool:
|
|
211
253
|
"stderr": f"启动虚拟终端 [{tty_id}] 失败: {str(e)}",
|
212
254
|
}
|
213
255
|
|
256
|
+
def _launch_tty_windows(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
257
|
+
"""Windows平台启动虚拟终端"""
|
258
|
+
try:
|
259
|
+
# 如果该ID的终端已经启动,先关闭它
|
260
|
+
if agent.tty_sessions[tty_id]["process"] is not None:
|
261
|
+
self._close_tty(agent, tty_id)
|
262
|
+
|
263
|
+
# 在Windows平台上导入需要的模块
|
264
|
+
import subprocess as _subprocess # pylint: disable=import-outside-toplevel
|
265
|
+
import threading as _threading # pylint: disable=import-outside-toplevel
|
266
|
+
import queue as _queue # pylint: disable=import-outside-toplevel
|
267
|
+
|
268
|
+
# 创建子进程
|
269
|
+
process = _subprocess.Popen(
|
270
|
+
agent.tty_sessions[tty_id]["shell"],
|
271
|
+
stdin=_subprocess.PIPE,
|
272
|
+
stdout=_subprocess.PIPE,
|
273
|
+
stderr=_subprocess.STDOUT,
|
274
|
+
shell=True,
|
275
|
+
text=True,
|
276
|
+
bufsize=0,
|
277
|
+
encoding="utf-8",
|
278
|
+
errors="replace",
|
279
|
+
)
|
280
|
+
|
281
|
+
# 保存进程对象
|
282
|
+
agent.tty_sessions[tty_id]["process"] = process
|
283
|
+
|
284
|
+
# 创建输出读取线程
|
285
|
+
def read_output():
|
286
|
+
while True:
|
287
|
+
if process is None or process.poll() is not None:
|
288
|
+
break
|
289
|
+
try:
|
290
|
+
if process.stdout is None:
|
291
|
+
break
|
292
|
+
line = process.stdout.readline()
|
293
|
+
if line:
|
294
|
+
agent.tty_sessions[tty_id]["output_queue"].put(line)
|
295
|
+
except:
|
296
|
+
break
|
297
|
+
|
298
|
+
output_thread = _threading.Thread(target=read_output, daemon=True)
|
299
|
+
output_thread.start()
|
300
|
+
agent.tty_sessions[tty_id]["output_thread"] = output_thread
|
301
|
+
|
302
|
+
# 读取初始输出
|
303
|
+
output = ""
|
304
|
+
start_time = time.time()
|
305
|
+
while time.time() - start_time < 2.0: # 最多等待2秒
|
306
|
+
try:
|
307
|
+
line = agent.tty_sessions[tty_id]["output_queue"].get(timeout=0.1)
|
308
|
+
output += line
|
309
|
+
except _queue.Empty:
|
310
|
+
continue
|
311
|
+
|
312
|
+
if output:
|
313
|
+
print(f"📤 终端 [{tty_id}]: {output}")
|
314
|
+
|
315
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
316
|
+
|
317
|
+
except Exception as e:
|
318
|
+
return {
|
319
|
+
"success": False,
|
320
|
+
"stdout": "",
|
321
|
+
"stderr": f"启动虚拟终端 [{tty_id}] 失败: {str(e)}",
|
322
|
+
}
|
323
|
+
|
214
324
|
def _input_command(
|
215
325
|
self,
|
216
326
|
agent: Any,
|
@@ -225,6 +335,22 @@ class VirtualTTYTool:
|
|
225
335
|
command: 要输入的单行命令
|
226
336
|
add_enter: 是否在命令末尾添加回车符
|
227
337
|
"""
|
338
|
+
if sys.platform == "win32":
|
339
|
+
return self._input_command_windows(
|
340
|
+
agent, tty_id, command, timeout, add_enter
|
341
|
+
)
|
342
|
+
else:
|
343
|
+
return self._input_command_unix(agent, tty_id, command, timeout, add_enter)
|
344
|
+
|
345
|
+
def _input_command_unix(
|
346
|
+
self,
|
347
|
+
agent: Any,
|
348
|
+
tty_id: str,
|
349
|
+
command: str,
|
350
|
+
timeout: float,
|
351
|
+
add_enter: bool = True,
|
352
|
+
) -> Dict[str, Any]:
|
353
|
+
"""Unix/Linux平台输入命令"""
|
228
354
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
229
355
|
return {
|
230
356
|
"success": False,
|
@@ -251,7 +377,9 @@ class VirtualTTYTool:
|
|
251
377
|
while time.time() - start_time < timeout:
|
252
378
|
try:
|
253
379
|
# 使用select等待数据可读
|
254
|
-
|
380
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
381
|
+
|
382
|
+
r, _, _ = _select.select(
|
255
383
|
[agent.tty_sessions[tty_id]["master_fd"]], [], [], 0.1
|
256
384
|
)
|
257
385
|
if r:
|
@@ -270,10 +398,68 @@ class VirtualTTYTool:
|
|
270
398
|
"stderr": f"在终端 [{tty_id}] 执行命令失败: {str(e)}",
|
271
399
|
}
|
272
400
|
|
401
|
+
def _input_command_windows(
|
402
|
+
self,
|
403
|
+
agent: Any,
|
404
|
+
tty_id: str,
|
405
|
+
command: str,
|
406
|
+
timeout: float,
|
407
|
+
add_enter: bool = True,
|
408
|
+
) -> Dict[str, Any]:
|
409
|
+
"""Windows平台输入命令"""
|
410
|
+
if agent.tty_sessions[tty_id]["process"] is None:
|
411
|
+
return {
|
412
|
+
"success": False,
|
413
|
+
"stdout": "",
|
414
|
+
"stderr": f"虚拟终端 [{tty_id}] 未启动",
|
415
|
+
}
|
416
|
+
|
417
|
+
# 严格检查并拒绝多行输入
|
418
|
+
if "\n" in command:
|
419
|
+
return {"success": False, "stdout": "", "stderr": "错误:禁止多行输入"}
|
420
|
+
|
421
|
+
try:
|
422
|
+
# 根据add_enter参数决定是否添加回车符
|
423
|
+
if add_enter:
|
424
|
+
command = command + "\n"
|
425
|
+
|
426
|
+
# 发送命令
|
427
|
+
agent.tty_sessions[tty_id]["process"].stdin.write(command)
|
428
|
+
agent.tty_sessions[tty_id]["process"].stdin.flush()
|
429
|
+
|
430
|
+
# 等待输出
|
431
|
+
output = ""
|
432
|
+
start_time = time.time()
|
433
|
+
while time.time() - start_time < timeout:
|
434
|
+
try:
|
435
|
+
line = agent.tty_sessions[tty_id]["output_queue"].get(timeout=0.1)
|
436
|
+
output += line
|
437
|
+
except Exception: # queue.Empty
|
438
|
+
continue
|
439
|
+
|
440
|
+
print(f"📤 终端 [{tty_id}]: {output}")
|
441
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
442
|
+
|
443
|
+
except Exception as e:
|
444
|
+
return {
|
445
|
+
"success": False,
|
446
|
+
"stdout": "",
|
447
|
+
"stderr": f"在终端 [{tty_id}] 执行命令失败: {str(e)}",
|
448
|
+
}
|
449
|
+
|
273
450
|
def _get_output(
|
274
451
|
self, agent: Any, tty_id: str, timeout: float = 5.0
|
275
452
|
) -> Dict[str, Any]:
|
276
453
|
"""获取终端输出"""
|
454
|
+
if sys.platform == "win32":
|
455
|
+
return self._get_output_windows(agent, tty_id, timeout)
|
456
|
+
else:
|
457
|
+
return self._get_output_unix(agent, tty_id, timeout)
|
458
|
+
|
459
|
+
def _get_output_unix(
|
460
|
+
self, agent: Any, tty_id: str, timeout: float = 5.0
|
461
|
+
) -> Dict[str, Any]:
|
462
|
+
"""Unix/Linux平台获取输出"""
|
277
463
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
278
464
|
return {
|
279
465
|
"success": False,
|
@@ -287,7 +473,9 @@ class VirtualTTYTool:
|
|
287
473
|
|
288
474
|
while time.time() - start_time < timeout:
|
289
475
|
# 使用select等待数据可读
|
290
|
-
|
476
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
477
|
+
|
478
|
+
r, _, _ = _select.select(
|
291
479
|
[agent.tty_sessions[tty_id]["master_fd"]], [], [], 0.1
|
292
480
|
)
|
293
481
|
if r:
|
@@ -313,8 +501,47 @@ class VirtualTTYTool:
|
|
313
501
|
"stderr": f"获取终端 [{tty_id}] 输出失败: {str(e)}",
|
314
502
|
}
|
315
503
|
|
504
|
+
def _get_output_windows(
|
505
|
+
self, agent: Any, tty_id: str, timeout: float = 5.0
|
506
|
+
) -> Dict[str, Any]:
|
507
|
+
"""Windows平台获取输出"""
|
508
|
+
if agent.tty_sessions[tty_id]["process"] is None:
|
509
|
+
return {
|
510
|
+
"success": False,
|
511
|
+
"stdout": "",
|
512
|
+
"stderr": f"虚拟终端 [{tty_id}] 未启动",
|
513
|
+
}
|
514
|
+
|
515
|
+
try:
|
516
|
+
output = ""
|
517
|
+
start_time = time.time()
|
518
|
+
|
519
|
+
while time.time() - start_time < timeout:
|
520
|
+
try:
|
521
|
+
line = agent.tty_sessions[tty_id]["output_queue"].get(timeout=0.1)
|
522
|
+
output += line
|
523
|
+
except Exception: # queue.Empty
|
524
|
+
continue
|
525
|
+
|
526
|
+
print(f"📤 终端 [{tty_id}]: {output}")
|
527
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
528
|
+
|
529
|
+
except Exception as e:
|
530
|
+
return {
|
531
|
+
"success": False,
|
532
|
+
"stdout": "",
|
533
|
+
"stderr": f"获取终端 [{tty_id}] 输出失败: {str(e)}",
|
534
|
+
}
|
535
|
+
|
316
536
|
def _close_tty(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
317
537
|
"""关闭虚拟终端"""
|
538
|
+
if sys.platform == "win32":
|
539
|
+
return self._close_tty_windows(agent, tty_id)
|
540
|
+
else:
|
541
|
+
return self._close_tty_unix(agent, tty_id)
|
542
|
+
|
543
|
+
def _close_tty_unix(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
544
|
+
"""Unix/Linux平台关闭终端"""
|
318
545
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
319
546
|
return {
|
320
547
|
"success": True,
|
@@ -328,7 +555,9 @@ class VirtualTTYTool:
|
|
328
555
|
|
329
556
|
# 终止子进程
|
330
557
|
if agent.tty_sessions[tty_id]["pid"]:
|
331
|
-
|
558
|
+
import signal as _signal # pylint: disable=import-outside-toplevel
|
559
|
+
|
560
|
+
os.kill(agent.tty_sessions[tty_id]["pid"], _signal.SIGTERM)
|
332
561
|
|
333
562
|
# 重置终端数据
|
334
563
|
agent.tty_sessions[tty_id] = {
|
@@ -350,8 +579,53 @@ class VirtualTTYTool:
|
|
350
579
|
"stderr": f"关闭虚拟终端 [{tty_id}] 失败: {str(e)}",
|
351
580
|
}
|
352
581
|
|
582
|
+
def _close_tty_windows(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
583
|
+
"""Windows平台关闭终端"""
|
584
|
+
if agent.tty_sessions[tty_id]["process"] is None:
|
585
|
+
return {
|
586
|
+
"success": True,
|
587
|
+
"stdout": f"没有正在运行的虚拟终端 [{tty_id}]",
|
588
|
+
"stderr": "",
|
589
|
+
}
|
590
|
+
|
591
|
+
try:
|
592
|
+
# 终止进程
|
593
|
+
agent.tty_sessions[tty_id]["process"].terminate()
|
594
|
+
agent.tty_sessions[tty_id]["process"].wait()
|
595
|
+
|
596
|
+
# 重置终端数据
|
597
|
+
import queue as _queue # pylint: disable=import-outside-toplevel
|
598
|
+
|
599
|
+
agent.tty_sessions[tty_id] = {
|
600
|
+
"process": None,
|
601
|
+
"output_queue": _queue.Queue(),
|
602
|
+
"output_thread": None,
|
603
|
+
"shell": "cmd.exe",
|
604
|
+
}
|
605
|
+
|
606
|
+
return {
|
607
|
+
"success": True,
|
608
|
+
"stdout": f"虚拟终端 [{tty_id}] 已关闭",
|
609
|
+
"stderr": "",
|
610
|
+
}
|
611
|
+
|
612
|
+
except Exception as e:
|
613
|
+
return {
|
614
|
+
"success": False,
|
615
|
+
"stdout": "",
|
616
|
+
"stderr": f"关闭虚拟终端 [{tty_id}] 失败: {str(e)}",
|
617
|
+
}
|
618
|
+
|
353
619
|
def _get_screen(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
354
620
|
"""获取当前终端屏幕内容"""
|
621
|
+
if sys.platform == "win32":
|
622
|
+
# Windows平台暂不支持获取屏幕内容
|
623
|
+
return {
|
624
|
+
"success": False,
|
625
|
+
"stdout": "",
|
626
|
+
"stderr": "Windows平台暂不支持获取屏幕内容功能",
|
627
|
+
}
|
628
|
+
|
355
629
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
356
630
|
return {
|
357
631
|
"success": False,
|
@@ -371,7 +645,9 @@ class VirtualTTYTool:
|
|
371
645
|
start_time = time.time()
|
372
646
|
while time.time() - start_time < 2.0: # 最多等待2秒
|
373
647
|
try:
|
374
|
-
|
648
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
649
|
+
|
650
|
+
r, _, _ = _select.select(
|
375
651
|
[agent.tty_sessions[tty_id]["master_fd"]], [], [], 0.1
|
376
652
|
)
|
377
653
|
if r:
|
@@ -404,15 +680,28 @@ class VirtualTTYTool:
|
|
404
680
|
active_ttys = []
|
405
681
|
|
406
682
|
for tty_id, tty_data in agent.tty_sessions.items():
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
683
|
+
if sys.platform == "win32":
|
684
|
+
status = "活动" if tty_data["process"] is not None else "关闭"
|
685
|
+
active_ttys.append(
|
686
|
+
{
|
687
|
+
"id": tty_id,
|
688
|
+
"status": status,
|
689
|
+
"pid": tty_data["process"].pid
|
690
|
+
if tty_data["process"]
|
691
|
+
else None,
|
692
|
+
"shell": tty_data["shell"],
|
693
|
+
}
|
694
|
+
)
|
695
|
+
else:
|
696
|
+
status = "活动" if tty_data["master_fd"] is not None else "关闭"
|
697
|
+
active_ttys.append(
|
698
|
+
{
|
699
|
+
"id": tty_id,
|
700
|
+
"status": status,
|
701
|
+
"pid": tty_data["pid"] if tty_data["pid"] else None,
|
702
|
+
"shell": tty_data["shell"],
|
703
|
+
}
|
704
|
+
)
|
416
705
|
|
417
706
|
# 格式化输出
|
418
707
|
output = "虚拟终端列表:\n"
|
jarvis/jarvis_utils/config.py
CHANGED
@@ -262,6 +262,12 @@ def get_pretty_output() -> bool:
|
|
262
262
|
返回:
|
263
263
|
bool: 如果启用PrettyOutput则返回True,默认为True
|
264
264
|
"""
|
265
|
+
import platform
|
266
|
+
|
267
|
+
# Windows系统强制设置为False
|
268
|
+
if platform.system() == "Windows":
|
269
|
+
return False
|
270
|
+
|
265
271
|
return GLOBAL_CONFIG_DATA.get("JARVIS_PRETTY_OUTPUT", False) == True
|
266
272
|
|
267
273
|
|
jarvis/jarvis_utils/git_utils.py
CHANGED
@@ -214,9 +214,7 @@ def handle_commit_workflow() -> bool:
|
|
214
214
|
Returns:
|
215
215
|
bool: 提交是否成功
|
216
216
|
"""
|
217
|
-
if is_confirm_before_apply_patch() and not user_confirm(
|
218
|
-
"是否要提交代码?", default=True
|
219
|
-
):
|
217
|
+
if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
|
220
218
|
revert_change()
|
221
219
|
return False
|
222
220
|
|
@@ -429,9 +427,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
|
|
429
427
|
if not in_venv and (
|
430
428
|
"Permission denied" in error_msg or "not writeable" in error_msg
|
431
429
|
):
|
432
|
-
if user_confirm(
|
433
|
-
"检测到权限问题,是否尝试用户级安装(--user)?", True
|
434
|
-
):
|
430
|
+
if user_confirm("检测到权限问题,是否尝试用户级安装(--user)?", True):
|
435
431
|
user_result = subprocess.run(
|
436
432
|
install_cmd + ["--user"],
|
437
433
|
cwd=git_root,
|
@@ -446,9 +442,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
|
|
446
442
|
PrettyOutput.print(f"代码安装失败: {error_msg}", OutputType.ERROR)
|
447
443
|
return False
|
448
444
|
except Exception as e:
|
449
|
-
PrettyOutput.print(
|
450
|
-
f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR
|
451
|
-
)
|
445
|
+
PrettyOutput.print(f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR)
|
452
446
|
return False
|
453
447
|
# 更新检查日期文件
|
454
448
|
with open(last_check_file, "w") as f:
|
@@ -482,9 +476,7 @@ def get_diff_file_list() -> List[str]:
|
|
482
476
|
subprocess.run(["git", "reset"], check=True)
|
483
477
|
|
484
478
|
if result.returncode != 0:
|
485
|
-
PrettyOutput.print(
|
486
|
-
f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR
|
487
|
-
)
|
479
|
+
PrettyOutput.print(f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR)
|
488
480
|
return []
|
489
481
|
|
490
482
|
return [f for f in result.stdout.splitlines() if f]
|
@@ -533,8 +525,10 @@ def get_recent_commits_with_files() -> List[Dict[str, Any]]:
|
|
533
525
|
],
|
534
526
|
capture_output=True,
|
535
527
|
text=True,
|
528
|
+
encoding="utf-8",
|
529
|
+
errors="replace",
|
536
530
|
)
|
537
|
-
if result.returncode != 0:
|
531
|
+
if result.returncode != 0 or result.stdout is None:
|
538
532
|
return []
|
539
533
|
|
540
534
|
# 解析提交信息
|
@@ -632,9 +626,7 @@ def confirm_add_new_files() -> None:
|
|
632
626
|
need_confirm = True
|
633
627
|
|
634
628
|
if binary_files:
|
635
|
-
output_lines.append(
|
636
|
-
f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)"
|
637
|
-
)
|
629
|
+
output_lines.append(f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)")
|
638
630
|
output_lines.append("二进制文件列表:")
|
639
631
|
output_lines.extend(f" - {file}" for file in binary_files)
|
640
632
|
need_confirm = True
|