jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.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.
Files changed (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,6 @@ import sys
13
13
  import base64
14
14
  from typing import Iterable, List, Optional
15
15
  import wcwidth
16
-
17
16
  from colorama import Fore
18
17
  from colorama import Style as ColoramaStyle
19
18
  from fuzzywuzzy import process
@@ -35,11 +34,9 @@ from prompt_toolkit.layout.containers import Window
35
34
  from prompt_toolkit.layout.controls import FormattedTextControl
36
35
  from prompt_toolkit.layout.layout import Layout
37
36
  from prompt_toolkit.styles import Style as PromptStyle
38
-
39
37
  from jarvis.jarvis_utils.clipboard import copy_to_clipboard
40
38
  from jarvis.jarvis_utils.config import get_data_dir, get_replace_map
41
39
  from jarvis.jarvis_utils.globals import get_message_history
42
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
43
40
  from jarvis.jarvis_utils.tag import ot
44
41
 
45
42
  # Sentinel value to indicate that Ctrl+O was pressed
@@ -54,9 +51,8 @@ FZF_REQUEST_ALL_SENTINEL_PREFIX = "__FZF_REQUEST_ALL__::"
54
51
  # Persistent hint marker for multiline input (shown only once across runs)
55
52
  _MULTILINE_HINT_MARK_FILE = os.path.join(get_data_dir(), "multiline_enter_hint_shown")
56
53
 
57
-
58
54
  def _display_width(s: str) -> int:
59
- """Calculate printable width of a string in terminal columns (handles wide chars)."""
55
+ """计算字符串在终端中的可打印宽度(处理宽字符)"""
60
56
  try:
61
57
  w = 0
62
58
  for ch in s:
@@ -69,11 +65,10 @@ def _display_width(s: str) -> int:
69
65
  except Exception:
70
66
  return len(s)
71
67
 
72
-
73
68
  def _calc_prompt_rows(prev_text: str) -> int:
74
69
  """
75
- Estimate how many terminal rows the previous prompt occupied.
76
- Considers prompt prefix and soft-wrapping across terminal columns.
70
+ 估算上一个提示占用了多少终端行数。
71
+ 考虑提示前缀和跨终端列的软换行。
77
72
  """
78
73
  try:
79
74
  cols = os.get_terminal_size().columns
@@ -102,17 +97,15 @@ def _calc_prompt_rows(prev_text: str) -> int:
102
97
  total_rows += rows
103
98
  return max(1, total_rows)
104
99
 
105
-
106
100
  def _multiline_hint_already_shown() -> bool:
107
- """Check if the multiline Enter hint has been shown before (persisted)."""
101
+ """检查是否已显示过多行输入提示(持久化存储)"""
108
102
  try:
109
103
  return os.path.exists(_MULTILINE_HINT_MARK_FILE)
110
104
  except Exception:
111
105
  return False
112
106
 
113
-
114
107
  def _mark_multiline_hint_shown() -> None:
115
- """Persist that the multiline Enter hint has been shown."""
108
+ """持久化存储多行输入提示已显示的状态。"""
116
109
  try:
117
110
  os.makedirs(os.path.dirname(_MULTILINE_HINT_MARK_FILE), exist_ok=True)
118
111
  with open(_MULTILINE_HINT_MARK_FILE, "w", encoding="utf-8") as f:
@@ -121,7 +114,6 @@ def _mark_multiline_hint_shown() -> None:
121
114
  # Non-critical persistence failure; ignore to avoid breaking input flow
122
115
  pass
123
116
 
124
-
125
117
  def get_single_line_input(tip: str, default: str = "") -> str:
126
118
  """
127
119
  获取支持历史记录的单行输入。
@@ -133,7 +125,6 @@ def get_single_line_input(tip: str, default: str = "") -> str:
133
125
  prompt = FormattedText([("class:prompt", f"👤 > {tip}")])
134
126
  return session.prompt(prompt, default=default, style=style)
135
127
 
136
-
137
128
  def get_choice(tip: str, choices: List[str]) -> str:
138
129
  """
139
130
  提供一个可滚动的选择列表供用户选择。
@@ -229,7 +220,6 @@ def get_choice(tip: str, choices: List[str]) -> str:
229
220
  except (KeyboardInterrupt, EOFError):
230
221
  return ""
231
222
 
232
-
233
223
  class FileCompleter(Completer):
234
224
  """
235
225
  带有模糊匹配的文件路径自定义补全器。
@@ -375,7 +365,6 @@ def _get_current_agent_for_input():
375
365
  except Exception:
376
366
  pass
377
367
  return None
378
-
379
368
  def _is_non_interactive_for_current_agent() -> bool:
380
369
  try:
381
370
  from jarvis.jarvis_utils.config import is_non_interactive
@@ -386,7 +375,6 @@ def _is_non_interactive_for_current_agent() -> bool:
386
375
  return bool(is_non_interactive())
387
376
  except Exception:
388
377
  return False
389
-
390
378
  def _is_auto_complete_for_current_agent() -> bool:
391
379
  try:
392
380
  from jarvis.jarvis_utils.config import GLOBAL_CONFIG_DATA
@@ -402,7 +390,6 @@ def _is_auto_complete_for_current_agent() -> bool:
402
390
  return bool(GLOBAL_CONFIG_DATA.get("JARVIS_AUTO_COMPLETE", False))
403
391
  except Exception:
404
392
  return False
405
-
406
393
  def user_confirm(tip: str, default: bool = True) -> bool:
407
394
  """提示用户确认是/否问题(按当前Agent优先判断非交互)"""
408
395
  try:
@@ -414,16 +401,15 @@ def user_confirm(tip: str, default: bool = True) -> bool:
414
401
  except KeyboardInterrupt:
415
402
  return False
416
403
 
417
-
418
404
  def _show_history_and_copy():
419
405
  """
420
- Displays message history and handles copying to clipboard.
421
- This function uses standard I/O and is safe to call outside a prompt session.
406
+ 显示消息历史记录并处理复制到剪贴板。
407
+ 此函数使用标准I/O,可在提示会话之外安全调用。
422
408
  """
423
409
 
424
410
  history = get_message_history()
425
411
  if not history:
426
- PrettyOutput.print("没有可复制的消息", OutputType.INFO)
412
+ print("ℹ️ 没有可复制的消息")
427
413
  return
428
414
 
429
415
  # 为避免 PrettyOutput 在循环中为每行加框,先拼接后统一打印
@@ -435,8 +421,8 @@ def _show_history_and_copy():
435
421
  (cleaned_msg[:70] + "...") if len(cleaned_msg) > 70 else cleaned_msg
436
422
  )
437
423
  lines.append(f" {i + 1}: {display_msg.strip()}")
438
- lines.append("=" * 58 + "\n")
439
- PrettyOutput.print("\n".join(lines), OutputType.INFO)
424
+ lines.append("=" * 58 + "\n")
425
+ print("ℹ️ " + "\n".join(lines))
440
426
 
441
427
  while True:
442
428
  try:
@@ -445,11 +431,11 @@ def _show_history_and_copy():
445
431
 
446
432
  if not choice_str: # User pressed Enter
447
433
  if not history:
448
- PrettyOutput.print("没有历史记录可供选择。", OutputType.INFO)
434
+ print("ℹ️ 没有历史记录可供选择。")
449
435
  break
450
436
  choice = len(history) - 1
451
437
  elif choice_str.lower() == "c":
452
- PrettyOutput.print("已取消", OutputType.INFO)
438
+ print("ℹ️ 已取消")
453
439
  break
454
440
  else:
455
441
  choice = int(choice_str) - 1
@@ -457,19 +443,16 @@ def _show_history_and_copy():
457
443
  if 0 <= choice < len(history):
458
444
  selected_msg = history[choice]
459
445
  copy_to_clipboard(selected_msg)
460
- PrettyOutput.print(
461
- f"已复制消息: {selected_msg[:70]}...", OutputType.SUCCESS
462
- )
446
+ print(f"✅ 已复制消息: {selected_msg[:70]}...")
463
447
  break
464
448
  else:
465
- PrettyOutput.print("无效的序号,请重试。", OutputType.WARNING)
449
+ print("⚠️ 无效的序号,请重试。")
466
450
  except ValueError:
467
- PrettyOutput.print("无效的输入,请输入数字。", OutputType.WARNING)
451
+ print("⚠️ 无效的输入,请输入数字。")
468
452
  except (KeyboardInterrupt, EOFError):
469
- PrettyOutput.print("\n操作取消", OutputType.INFO)
453
+ print("\nℹ️ 操作取消")
470
454
  break
471
455
 
472
-
473
456
  def _get_multiline_input_internal(
474
457
  tip: str, preset: Optional[str] = None, preset_cursor: Optional[int] = None
475
458
  ) -> str:
@@ -489,10 +472,7 @@ def _get_multiline_input_internal(
489
472
  first_enter_hint_shown = True
490
473
 
491
474
  def _show_notice():
492
- PrettyOutput.print(
493
- "提示:当前支持多行输入。输入完成请使用 Ctrl+J 确认;Enter 仅用于换行。",
494
- OutputType.INFO,
495
- )
475
+ print("ℹ️ 提示:当前支持多行输入。输入完成请使用 Ctrl+J 确认;Enter 仅用于换行。")
496
476
  try:
497
477
  input("按回车继续...")
498
478
  except Exception:
@@ -692,7 +672,6 @@ def _get_multiline_input_internal(
692
672
  except (KeyboardInterrupt, EOFError):
693
673
  return ""
694
674
 
695
-
696
675
  def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
697
676
  """
698
677
  获取带有增强补全和确认功能的多行输入。
@@ -771,9 +750,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
771
750
  import subprocess
772
751
 
773
752
  if shutil.which("fzf") is None:
774
- PrettyOutput.print(
775
- "未检测到 fzf,无法打开文件选择器。", OutputType.WARNING
776
- )
753
+ print("⚠️ 未检测到 fzf,无法打开文件选择器。")
777
754
  else:
778
755
  files = []
779
756
  try:
@@ -802,7 +779,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
802
779
  break
803
780
 
804
781
  if not files:
805
- PrettyOutput.print("未找到可选择的文件。", OutputType.INFO)
782
+ print("ℹ️ 未找到可选择的文件。")
806
783
  else:
807
784
  try:
808
785
  specials = [
@@ -846,7 +823,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
846
823
  if sel:
847
824
  selected_path = sel
848
825
  except Exception as e:
849
- PrettyOutput.print(f"FZF 执行失败: {e}", OutputType.ERROR)
826
+ print(f"FZF 执行失败: {e}")
850
827
 
851
828
  # Compute new text based on selection (or keep original if none)
852
829
  if selected_path:
@@ -904,9 +881,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
904
881
  import subprocess
905
882
 
906
883
  if shutil.which("fzf") is None:
907
- PrettyOutput.print(
908
- "未检测到 fzf,无法打开文件选择器。", OutputType.WARNING
909
- )
884
+ print("⚠️ 未检测到 fzf,无法打开文件选择器。")
910
885
  else:
911
886
  files = []
912
887
  try:
@@ -927,7 +902,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
927
902
  files = []
928
903
 
929
904
  if not files:
930
- PrettyOutput.print("未找到可选择的文件。", OutputType.INFO)
905
+ print("ℹ️ 未找到可选择的文件。")
931
906
  else:
932
907
  try:
933
908
  specials = [
@@ -971,7 +946,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
971
946
  if sel:
972
947
  selected_path = sel
973
948
  except Exception as e:
974
- PrettyOutput.print(f"FZF 执行失败: {e}", OutputType.ERROR)
949
+ print(f"FZF 执行失败: {e}")
975
950
 
976
951
  # Compute new text based on selection (or keep original if none)
977
952
  if selected_path:
@@ -1025,5 +1000,5 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
1025
1000
  continue
1026
1001
  else:
1027
1002
  if not user_input and print_on_empty:
1028
- PrettyOutput.print("输入已取消", OutputType.INFO)
1003
+ print("ℹ️ 输入已取消")
1029
1004
  return user_input