jarvis-ai-assistant 0.3.18__py3-none-any.whl → 0.3.20__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 (54) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +30 -12
  3. jarvis/jarvis_agent/config_editor.py +1 -1
  4. jarvis/jarvis_agent/edit_file_handler.py +8 -13
  5. jarvis/jarvis_agent/memory_manager.py +4 -4
  6. jarvis/jarvis_agent/shell_input_handler.py +17 -2
  7. jarvis/jarvis_agent/task_analyzer.py +4 -3
  8. jarvis/jarvis_agent/task_manager.py +6 -6
  9. jarvis/jarvis_agent/tool_executor.py +2 -2
  10. jarvis/jarvis_code_agent/code_agent.py +21 -29
  11. jarvis/jarvis_code_analysis/code_review.py +2 -4
  12. jarvis/jarvis_git_utils/git_commiter.py +17 -18
  13. jarvis/jarvis_methodology/main.py +12 -12
  14. jarvis/jarvis_platform/ai8.py +0 -4
  15. jarvis/jarvis_platform/base.py +16 -15
  16. jarvis/jarvis_platform/kimi.py +13 -13
  17. jarvis/jarvis_platform/tongyi.py +17 -15
  18. jarvis/jarvis_platform/yuanbao.py +11 -11
  19. jarvis/jarvis_platform_manager/service.py +2 -2
  20. jarvis/jarvis_rag/cli.py +36 -32
  21. jarvis/jarvis_rag/embedding_manager.py +11 -6
  22. jarvis/jarvis_rag/llm_interface.py +6 -5
  23. jarvis/jarvis_rag/rag_pipeline.py +9 -8
  24. jarvis/jarvis_rag/reranker.py +3 -2
  25. jarvis/jarvis_rag/retriever.py +18 -8
  26. jarvis/jarvis_smart_shell/main.py +307 -47
  27. jarvis/jarvis_stats/cli.py +2 -1
  28. jarvis/jarvis_stats/stats.py +45 -5
  29. jarvis/jarvis_stats/storage.py +220 -9
  30. jarvis/jarvis_tools/clear_memory.py +0 -11
  31. jarvis/jarvis_tools/cli/main.py +18 -17
  32. jarvis/jarvis_tools/edit_file.py +4 -4
  33. jarvis/jarvis_tools/execute_script.py +5 -1
  34. jarvis/jarvis_tools/file_analyzer.py +6 -6
  35. jarvis/jarvis_tools/generate_new_tool.py +6 -17
  36. jarvis/jarvis_tools/read_code.py +3 -6
  37. jarvis/jarvis_tools/read_webpage.py +4 -4
  38. jarvis/jarvis_tools/registry.py +8 -28
  39. jarvis/jarvis_tools/retrieve_memory.py +5 -16
  40. jarvis/jarvis_tools/rewrite_file.py +0 -4
  41. jarvis/jarvis_tools/save_memory.py +2 -10
  42. jarvis/jarvis_tools/search_web.py +5 -8
  43. jarvis/jarvis_tools/virtual_tty.py +22 -40
  44. jarvis/jarvis_utils/clipboard.py +2 -2
  45. jarvis/jarvis_utils/input.py +316 -30
  46. jarvis/jarvis_utils/methodology.py +3 -3
  47. jarvis/jarvis_utils/output.py +215 -135
  48. jarvis/jarvis_utils/utils.py +35 -58
  49. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/METADATA +1 -1
  50. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/RECORD +54 -54
  51. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/WHEEL +0 -0
  52. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/entry_points.txt +0 -0
  53. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/licenses/LICENSE +0 -0
  54. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/top_level.txt +0 -0
@@ -9,7 +9,10 @@
9
9
  - 用于输入控制的自定义键绑定
10
10
  """
11
11
  import os
12
+ import sys
13
+ import base64
12
14
  from typing import Iterable, List
15
+ import wcwidth
13
16
 
14
17
  from colorama import Fore
15
18
  from colorama import Style as ColoramaStyle
@@ -26,6 +29,8 @@ from prompt_toolkit.document import Document
26
29
  from prompt_toolkit.formatted_text import FormattedText
27
30
  from prompt_toolkit.history import FileHistory
28
31
  from prompt_toolkit.key_binding import KeyBindings
32
+ from prompt_toolkit.enums import DEFAULT_BUFFER
33
+ from prompt_toolkit.filters import has_focus
29
34
  from prompt_toolkit.layout.containers import Window
30
35
  from prompt_toolkit.layout.controls import FormattedTextControl
31
36
  from prompt_toolkit.layout.layout import Layout
@@ -39,10 +44,63 @@ from jarvis.jarvis_utils.tag import ot
39
44
 
40
45
  # Sentinel value to indicate that Ctrl+O was pressed
41
46
  CTRL_O_SENTINEL = "__CTRL_O_PRESSED__"
47
+ # Sentinel prefix to indicate that Ctrl+F (fzf) inserted content should prefill next prompt
48
+ FZF_INSERT_SENTINEL_PREFIX = "__FZF_INSERT__::"
49
+ # Sentinel to request running fzf outside the prompt and then prefill next prompt
50
+ FZF_REQUEST_SENTINEL_PREFIX = "__FZF_REQUEST__::"
42
51
 
43
52
  # Persistent hint marker for multiline input (shown only once across runs)
44
53
  _MULTILINE_HINT_MARK_FILE = os.path.join(get_data_dir(), "multiline_enter_hint_shown")
45
54
 
55
+ def _display_width(s: str) -> int:
56
+ """Calculate printable width of a string in terminal columns (handles wide chars)."""
57
+ try:
58
+ w = 0
59
+ for ch in s:
60
+ cw = wcwidth.wcwidth(ch)
61
+ if cw is None or cw < 0:
62
+ # Fallback for unknown width chars (e.g. emoji on some terminals)
63
+ cw = 1
64
+ w += cw
65
+ return w
66
+ except Exception:
67
+ return len(s)
68
+
69
+ def _calc_prompt_rows(prev_text: str) -> int:
70
+ """
71
+ Estimate how many terminal rows the previous prompt occupied.
72
+ Considers prompt prefix and soft-wrapping across terminal columns.
73
+ """
74
+ try:
75
+ cols = os.get_terminal_size().columns
76
+ except Exception:
77
+ cols = 80
78
+ prefix = "👤 > "
79
+ prefix_w = _display_width(prefix)
80
+
81
+ if prev_text is None:
82
+ return 1
83
+
84
+ lines = prev_text.splitlines()
85
+ if not lines:
86
+ lines = [""]
87
+ # If the text ends with a newline, there is a visible empty line at the end.
88
+ if prev_text.endswith("\n"):
89
+ lines.append("")
90
+ total_rows = 0
91
+ for i, line in enumerate(lines):
92
+ lw = _display_width(line)
93
+ if i == 0:
94
+ width = prefix_w + lw
95
+ else:
96
+ width = lw
97
+ rows = max(1, (width + cols - 1) // cols)
98
+ total_rows += rows
99
+ return max(1, total_rows)
100
+
101
+
102
+
103
+
46
104
 
47
105
  def _multiline_hint_already_shown() -> bool:
48
106
  """Check if the multiline Enter hint has been shown before (persisted)."""
@@ -68,8 +126,9 @@ def get_single_line_input(tip: str, default: str = "") -> str:
68
126
  获取支持历史记录的单行输入。
69
127
  """
70
128
  session: PromptSession = PromptSession(history=None)
71
- style = PromptStyle.from_dict({"prompt": "ansicyan"})
72
- return session.prompt(f"{tip}", default=default, style=style)
129
+ style = PromptStyle.from_dict({"prompt": "ansicyan", "bottom-toolbar": "fg:#888888"})
130
+ prompt = FormattedText([("class:prompt", f"👤 > {tip}")])
131
+ return session.prompt(prompt, default=default, style=style)
73
132
 
74
133
 
75
134
  def get_choice(tip: str, choices: List[str]) -> str:
@@ -289,14 +348,14 @@ def _show_history_and_copy():
289
348
  PrettyOutput.print("没有可复制的消息", OutputType.INFO)
290
349
  return
291
350
 
292
- print("\n" + "=" * 20 + " 消息历史记录 " + "=" * 20)
351
+ PrettyOutput.print("\n" + "=" * 20 + " 消息历史记录 " + "=" * 20, OutputType.INFO)
293
352
  for i, msg in enumerate(history):
294
353
  cleaned_msg = msg.replace("\n", r"\n")
295
354
  display_msg = (
296
355
  (cleaned_msg[:70] + "...") if len(cleaned_msg) > 70 else cleaned_msg
297
356
  )
298
- print(f" {i + 1}: {display_msg.strip()}")
299
- print("=" * 58 + "\n")
357
+ PrettyOutput.print(f" {i + 1}: {display_msg.strip()}", OutputType.INFO)
358
+ PrettyOutput.print("=" * 58 + "\n", OutputType.INFO)
300
359
 
301
360
  while True:
302
361
  try:
@@ -305,11 +364,11 @@ def _show_history_and_copy():
305
364
 
306
365
  if not choice_str: # User pressed Enter
307
366
  if not history:
308
- print("没有历史记录可供选择。")
367
+ PrettyOutput.print("没有历史记录可供选择。", OutputType.INFO)
309
368
  break
310
369
  choice = len(history) - 1
311
370
  elif choice_str.lower() == "c":
312
- print("已取消")
371
+ PrettyOutput.print("已取消", OutputType.INFO)
313
372
  break
314
373
  else:
315
374
  choice = int(choice_str) - 1
@@ -322,23 +381,23 @@ def _show_history_and_copy():
322
381
  )
323
382
  break
324
383
  else:
325
- print("无效的序号,请重试。")
384
+ PrettyOutput.print("无效的序号,请重试。", OutputType.WARNING)
326
385
  except ValueError:
327
- print("无效的输入,请输入数字。")
386
+ PrettyOutput.print("无效的输入,请输入数字。", OutputType.WARNING)
328
387
  except (KeyboardInterrupt, EOFError):
329
- print("\n操作取消")
388
+ PrettyOutput.print("\n操作取消", OutputType.INFO)
330
389
  break
331
390
 
332
391
 
333
- def _get_multiline_input_internal(tip: str) -> str:
392
+ def _get_multiline_input_internal(tip: str, preset: str | None = None, preset_cursor: int | None = None) -> str:
334
393
  """
335
394
  Internal function to get multiline input using prompt_toolkit.
336
395
  Returns a sentinel value if Ctrl+O is pressed.
337
396
  """
338
397
  bindings = KeyBindings()
339
398
 
340
- # Show a one-time hint on the first Enter press in this invocation
341
- first_enter_hint_shown = False
399
+ # Show a one-time hint on the first Enter press in this invocation (disabled; using inlay toolbar instead)
400
+ first_enter_hint_shown = True
342
401
 
343
402
  @bindings.add("enter")
344
403
  def _(event):
@@ -347,8 +406,9 @@ def _get_multiline_input_internal(tip: str) -> str:
347
406
  first_enter_hint_shown = True
348
407
 
349
408
  def _show_notice():
350
- print(
351
- f"{Fore.YELLOW}提示:当前支持多行输入。输入完成请使用 Ctrl+J 确认;Enter 仅用于换行。{ColoramaStyle.RESET_ALL}"
409
+ PrettyOutput.print(
410
+ "提示:当前支持多行输入。输入完成请使用 Ctrl+J 确认;Enter 仅用于换行。",
411
+ OutputType.INFO,
352
412
  )
353
413
  try:
354
414
  input("按回车继续...")
@@ -372,16 +432,112 @@ def _get_multiline_input_internal(tip: str) -> str:
372
432
  else:
373
433
  event.current_buffer.insert_text("\n")
374
434
 
375
- @bindings.add("c-j")
435
+ @bindings.add("c-j", filter=has_focus(DEFAULT_BUFFER))
376
436
  def _(event):
377
437
  event.current_buffer.validate_and_handle()
378
438
 
379
- @bindings.add("c-o")
439
+ @bindings.add("c-o", filter=has_focus(DEFAULT_BUFFER))
380
440
  def _(event):
381
441
  """Handle Ctrl+O by exiting the prompt and returning the sentinel value."""
382
442
  event.app.exit(result=CTRL_O_SENTINEL)
383
443
 
384
- style = PromptStyle.from_dict({"prompt": "ansicyan"})
444
+ @bindings.add("c-t", filter=has_focus(DEFAULT_BUFFER))
445
+ def _(event):
446
+ """Return a shell command like '!bash' for upper input_handler to execute."""
447
+ def _gen_shell_cmd() -> str: # type: ignore
448
+ try:
449
+ import os
450
+ import shutil
451
+
452
+ if os.name == "nt":
453
+ # Prefer PowerShell if available, otherwise fallback to cmd
454
+ for name in ("pwsh", "powershell", "cmd"):
455
+ if name == "cmd" or shutil.which(name):
456
+ return f"!{name}"
457
+ else:
458
+ shell_path = os.environ.get("SHELL", "")
459
+ if shell_path:
460
+ base = os.path.basename(shell_path)
461
+ if base:
462
+ return f"!{base}"
463
+ for name in ("fish", "zsh", "bash", "sh"):
464
+ if shutil.which(name):
465
+ return f"!{name}"
466
+ return "!bash"
467
+ except Exception:
468
+ return "!bash"
469
+
470
+ # Append a special marker to indicate no-confirm execution in shell_input_handler
471
+ event.app.exit(result=_gen_shell_cmd() + " # JARVIS-NOCONFIRM")
472
+
473
+
474
+ @bindings.add("@", filter=has_focus(DEFAULT_BUFFER), eager=True)
475
+ def _(event):
476
+ """
477
+ 使用 @ 触发 fzf(当 fzf 存在);否则仅插入 @ 以启用内置补全
478
+ 逻辑:
479
+ - 若检测到系统存在 fzf,则先插入 '@',随后请求外层运行 fzf 并在返回后进行替换/插入
480
+ - 若不存在 fzf 或发生异常,则直接插入 '@'
481
+ """
482
+ try:
483
+ import shutil
484
+ buf = event.current_buffer
485
+ if shutil.which("fzf") is None:
486
+ buf.insert_text("@")
487
+ return
488
+ # 先插入 '@',以便外层根据最后一个 '@' 进行片段替换
489
+ buf.insert_text("@")
490
+ doc = buf.document
491
+ text = doc.text
492
+ cursor = doc.cursor_position
493
+ payload = f"{cursor}:{base64.b64encode(text.encode('utf-8')).decode('ascii')}"
494
+ event.app.exit(result=FZF_REQUEST_SENTINEL_PREFIX + payload)
495
+ return
496
+ except Exception:
497
+ try:
498
+ event.current_buffer.insert_text("@")
499
+ except Exception:
500
+ pass
501
+
502
+ style = PromptStyle.from_dict(
503
+ {
504
+ "prompt": "ansibrightmagenta bold",
505
+ "bottom-toolbar": "bg:#4b145b #ffd6ff bold",
506
+ "bt.tip": "bold fg:#ff5f87",
507
+ "bt.sep": "fg:#ffb3de",
508
+ "bt.key": "bg:#d7005f #ffffff bold",
509
+ "bt.label": "fg:#ffd6ff",
510
+ }
511
+ )
512
+
513
+ def _bottom_toolbar():
514
+ return FormattedText(
515
+ [
516
+ ("class:bt.tip", f" {tip} "),
517
+ ("class:bt.sep", " • "),
518
+ ("class:bt.label", "快捷键: "),
519
+ ("class:bt.key", "@"),
520
+ ("class:bt.label", " 文件补全 "),
521
+ ("class:bt.sep", " • "),
522
+ ("class:bt.key", "Tab"),
523
+ ("class:bt.label", " 选择 "),
524
+ ("class:bt.sep", " • "),
525
+ ("class:bt.key", "Ctrl+J"),
526
+ ("class:bt.label", " 确认 "),
527
+ ("class:bt.sep", " • "),
528
+ ("class:bt.key", "Ctrl+O"),
529
+ ("class:bt.label", " 历史复制 "),
530
+ ("class:bt.sep", " • "),
531
+ ("class:bt.key", "@"),
532
+ ("class:bt.label", " FZF文件 "),
533
+ ("class:bt.sep", " • "),
534
+ ("class:bt.key", "Ctrl+T"),
535
+ ("class:bt.label", " 终端(!SHELL) "),
536
+ ("class:bt.sep", " • "),
537
+ ("class:bt.key", "Ctrl+C/D"),
538
+ ("class:bt.label", " 取消 "),
539
+ ]
540
+ )
385
541
 
386
542
  history_dir = get_data_dir()
387
543
  session: PromptSession = PromptSession(
@@ -394,33 +550,163 @@ def _get_multiline_input_internal(tip: str) -> str:
394
550
  mouse_support=False,
395
551
  )
396
552
 
397
- print(f"{Fore.GREEN}{tip}{ColoramaStyle.RESET_ALL}")
398
- prompt = FormattedText([("class:prompt", ">>> ")])
553
+ # Tip is shown in bottom toolbar; avoid extra print
554
+ prompt = FormattedText([("class:prompt", "👤 > ")])
555
+
556
+ def _pre_run():
557
+ try:
558
+ from prompt_toolkit.application.current import get_app as _ga
559
+ app = _ga()
560
+ buf = app.current_buffer
561
+ if preset is not None and preset_cursor is not None:
562
+ cp = max(0, min(len(buf.text), preset_cursor))
563
+ buf.cursor_position = cp
564
+ except Exception:
565
+ pass
399
566
 
400
567
  try:
401
- return session.prompt(prompt, style=style, pre_run=lambda: None).strip()
568
+ return session.prompt(
569
+ prompt,
570
+ style=style,
571
+ pre_run=_pre_run,
572
+ bottom_toolbar=_bottom_toolbar,
573
+ default=(preset or ""),
574
+ ).strip()
402
575
  except (KeyboardInterrupt, EOFError):
403
576
  return ""
404
577
 
405
578
 
406
- def get_multiline_input(tip: str) -> str:
579
+ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
407
580
  """
408
581
  获取带有增强补全和确认功能的多行输入。
409
582
  此函数处理控制流,允许在不破坏终端状态的情况下处理历史记录复制。
410
- """
411
- PrettyOutput.section(
412
- "用户输入 - 使用 @ 触发文件补全,Tab 选择补全项,Ctrl+J 确认,Ctrl+O 从历史记录中选择消息复制,按 Ctrl+C/D 取消输入",
413
- OutputType.USER,
414
- )
415
583
 
584
+ 参数:
585
+ tip: 提示文本,将显示在底部工具栏中
586
+ print_on_empty: 当输入为空字符串时,是否打印“输入已取消”提示。默认打印。
587
+ """
588
+ preset: str | None = None
589
+ preset_cursor: int | None = None
416
590
  while True:
417
- user_input = _get_multiline_input_internal(tip)
591
+ user_input = _get_multiline_input_internal(tip, preset=preset, preset_cursor=preset_cursor)
418
592
 
419
593
  if user_input == CTRL_O_SENTINEL:
420
594
  _show_history_and_copy()
421
595
  tip = "请继续输入(或按Ctrl+J确认):"
422
596
  continue
597
+ elif isinstance(user_input, str) and user_input.startswith(FZF_REQUEST_SENTINEL_PREFIX):
598
+ # Handle fzf request outside the prompt, then prefill new text.
599
+ try:
600
+ payload = user_input[len(FZF_REQUEST_SENTINEL_PREFIX) :]
601
+ sep_index = payload.find(":")
602
+ cursor = int(payload[:sep_index])
603
+ text = base64.b64decode(payload[sep_index + 1 :].encode("ascii")).decode("utf-8")
604
+ except Exception:
605
+ # Malformed payload; just continue without change.
606
+ preset = None
607
+ tip = "FZF 预填失败,继续输入:"
608
+ continue
609
+
610
+ # Run fzf to get a file selection synchronously (outside prompt)
611
+ selected_path = ""
612
+ try:
613
+ import shutil
614
+ import subprocess
615
+
616
+ if shutil.which("fzf") is None:
617
+ PrettyOutput.print("未检测到 fzf,无法打开文件选择器。", OutputType.WARNING)
618
+ else:
619
+ files: list[str] = []
620
+ try:
621
+ r = subprocess.run(
622
+ ["git", "ls-files"],
623
+ stdout=subprocess.PIPE,
624
+ stderr=subprocess.PIPE,
625
+ text=True,
626
+ )
627
+ if r.returncode == 0:
628
+ files = [line for line in r.stdout.splitlines() if line.strip()]
629
+ except Exception:
630
+ files = []
631
+
632
+ if not files:
633
+ import os as _os
634
+ for root, _, fnames in _os.walk(".", followlinks=False):
635
+ for name in fnames:
636
+ files.append(_os.path.relpath(_os.path.join(root, name), "."))
637
+ if len(files) > 10000:
638
+ break
639
+
640
+ if not files:
641
+ PrettyOutput.print("未找到可选择的文件。", OutputType.INFO)
642
+ else:
643
+ try:
644
+ specials = [ot("Summary"), ot("Clear"), ot("ToolUsage"), ot("ReloadConfig"), ot("SaveSession")]
645
+ except Exception:
646
+ specials = []
647
+ items = [s for s in specials if isinstance(s, str) and s.strip()] + files
648
+ proc = subprocess.run(
649
+ ["fzf", "--prompt", "Files> ", "--height", "40%", "--border"],
650
+ input="\n".join(items),
651
+ stdout=subprocess.PIPE,
652
+ stderr=subprocess.PIPE,
653
+ text=True,
654
+ )
655
+ sel = proc.stdout.strip()
656
+ if sel:
657
+ selected_path = sel
658
+ except Exception as e:
659
+ PrettyOutput.print(f"FZF 执行失败: {e}", OutputType.ERROR)
660
+
661
+ # Compute new text based on selection (or keep original if none)
662
+ if selected_path:
663
+ text_before = text[:cursor]
664
+ last_at = text_before.rfind("@")
665
+ if last_at != -1 and " " not in text_before[last_at + 1 :]:
666
+ # Replace @... segment
667
+ inserted = f"'{selected_path}'"
668
+ new_text = text[:last_at] + inserted + text[cursor:]
669
+ new_cursor = last_at + len(inserted)
670
+ else:
671
+ # Plain insert
672
+ inserted = f"'{selected_path}'"
673
+ new_text = text[:cursor] + inserted + text[cursor:]
674
+ new_cursor = cursor + len(inserted)
675
+ preset = new_text
676
+ preset_cursor = new_cursor
677
+ tip = "已插入文件,继续编辑或按Ctrl+J确认:"
678
+ else:
679
+ # No selection; keep original text and cursor
680
+ preset = text
681
+ preset_cursor = cursor
682
+ tip = "未选择文件或已取消,继续编辑:"
683
+ # 清除上一条输入行(多行安全),避免多清,保守仅按提示行估算
684
+ try:
685
+ rows_total = _calc_prompt_rows(text)
686
+ for _ in range(rows_total):
687
+ sys.stdout.write("\x1b[1A") # 光标上移一行
688
+ sys.stdout.write("\x1b[2K\r") # 清除整行
689
+ sys.stdout.flush()
690
+ except Exception:
691
+ pass
692
+ continue
693
+ elif isinstance(user_input, str) and user_input.startswith(FZF_INSERT_SENTINEL_PREFIX):
694
+ # 从哨兵载荷中提取新文本,作为下次进入提示的预填内容
695
+ preset = user_input[len(FZF_INSERT_SENTINEL_PREFIX) :]
696
+ preset_cursor = len(preset)
697
+
698
+ # 清除上一条输入行(多行安全),避免多清,保守仅按提示行估算
699
+ try:
700
+ rows_total = _calc_prompt_rows(preset)
701
+ for _ in range(rows_total):
702
+ sys.stdout.write("\x1b[1A")
703
+ sys.stdout.write("\x1b[2K\r")
704
+ sys.stdout.flush()
705
+ except Exception:
706
+ pass
707
+ tip = "已插入文件,继续编辑或按Ctrl+J确认:"
708
+ continue
423
709
  else:
424
- if not user_input:
425
- PrettyOutput.print("\n输入已取消", OutputType.INFO)
710
+ if not user_input and print_on_empty:
711
+ PrettyOutput.print("输入已取消", OutputType.INFO)
426
712
  return user_input
@@ -203,12 +203,12 @@ def load_methodology(
203
203
 
204
204
  try:
205
205
  # 加载所有方法论
206
- print(f"📁 加载方法论文件...")
206
+ PrettyOutput.print("📁 加载方法论文件...", OutputType.INFO)
207
207
  methodologies = _load_all_methodologies()
208
208
  if not methodologies:
209
- print(f"没有找到方法论文件")
209
+ PrettyOutput.print("没有找到方法论文件", OutputType.WARNING)
210
210
  return ""
211
- print(f"加载方法论文件完成 (共 {len(methodologies)} 个)")
211
+ PrettyOutput.print(f"加载方法论文件完成 (共 {len(methodologies)} 个)", OutputType.SUCCESS)
212
212
 
213
213
  if platform_name:
214
214
  platform = PlatformRegistry().create_platform(platform_name)