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
@@ -6,7 +6,6 @@ from datetime import datetime
6
6
 
7
7
  import typer
8
8
 
9
- from jarvis.jarvis_agent import OutputType, PrettyOutput
10
9
  from jarvis.jarvis_agent.agent_manager import AgentManager
11
10
  from jarvis.jarvis_agent.config_editor import ConfigEditor
12
11
  from jarvis.jarvis_agent.methodology_share_manager import MethodologyShareManager
@@ -170,7 +169,7 @@ def handle_interactive_config_option(
170
169
  config_data, ask_all=True
171
170
  )
172
171
  if not changed:
173
- PrettyOutput.print("没有需要更新的配置项,保持现有配置。", OutputType.INFO)
172
+ print("ℹ️ 没有需要更新的配置项,保持现有配置。")
174
173
  return True
175
174
 
176
175
  # 剔除与 schema 默认值一致的键,保持配置精简
@@ -209,10 +208,10 @@ def handle_interactive_config_option(
209
208
  wf.write(header)
210
209
  wf.write(yaml_str)
211
210
 
212
- PrettyOutput.print(f"配置已更新: {config_path}", OutputType.SUCCESS)
211
+ print(f"配置已更新: {config_path}")
213
212
  return True
214
213
  except Exception as e:
215
- PrettyOutput.print(f"交互式配置失败: {e}", OutputType.ERROR)
214
+ print(f"交互式配置失败: {e}")
216
215
  return True
217
216
 
218
217
 
@@ -224,7 +223,7 @@ def handle_backup_option(backup_dir_path: Optional[str]) -> bool:
224
223
  init_env("", config_file=None)
225
224
  data_dir = Path(get_data_dir())
226
225
  if not data_dir.is_dir():
227
- PrettyOutput.print(f"数据目录不存在: {data_dir}", OutputType.ERROR)
226
+ print(f"数据目录不存在: {data_dir}")
228
227
  return True
229
228
 
230
229
  backup_dir_str = backup_dir_path if backup_dir_path.strip() else "~/jarvis_backups"
@@ -238,9 +237,9 @@ def handle_backup_option(backup_dir_path: Optional[str]) -> bool:
238
237
  archive_path = shutil.make_archive(
239
238
  str(backup_file_base), "zip", root_dir=str(data_dir)
240
239
  )
241
- PrettyOutput.print(f"数据已成功备份到: {archive_path}", OutputType.SUCCESS)
240
+ print(f"数据已成功备份到: {archive_path}")
242
241
  except Exception as e:
243
- PrettyOutput.print(f"数据备份失败: {e}", OutputType.ERROR)
242
+ print(f"数据备份失败: {e}")
244
243
 
245
244
  return True
246
245
 
@@ -253,7 +252,7 @@ def handle_restore_option(restore_path: Optional[str], config_file: Optional[str
253
252
  restore_file = Path(os.path.expanduser(os.path.expandvars(restore_path)))
254
253
  # 兼容 ~ 与环境变量,避免用户输入未展开路径导致找不到文件
255
254
  if not restore_file.is_file():
256
- PrettyOutput.print(f"指定的恢复文件不存在: {restore_file}", OutputType.ERROR)
255
+ print(f"指定的恢复文件不存在: {restore_file}")
257
256
  return True
258
257
 
259
258
  # 在恢复数据时不要触发完整环境初始化,避免引导流程或网络请求
@@ -281,23 +280,21 @@ def handle_restore_option(restore_path: Optional[str], config_file: Optional[str
281
280
  if not user_confirm(
282
281
  f"数据目录 '{data_dir}' 已存在,恢复操作将覆盖它。是否继续?", default=False
283
282
  ):
284
- PrettyOutput.print("恢复操作已取消。", OutputType.INFO)
283
+ print("ℹ️ 恢复操作已取消。")
285
284
  return True
286
285
  try:
287
286
  shutil.rmtree(data_dir)
288
287
  except Exception as e:
289
- PrettyOutput.print(f"无法移除现有数据目录: {e}", OutputType.ERROR)
288
+ print(f"无法移除现有数据目录: {e}")
290
289
  return True
291
290
 
292
291
  try:
293
292
  data_dir.mkdir(parents=True)
294
293
  shutil.unpack_archive(str(restore_file), str(data_dir), "zip")
295
- PrettyOutput.print(
296
- f"数据已从 '{restore_path}' 成功恢复到 '{data_dir}'", OutputType.SUCCESS
297
- )
294
+ print(f"✅ 数据已从 '{restore_path}' 成功恢复到 '{data_dir}'")
298
295
 
299
296
  except Exception as e:
300
- PrettyOutput.print(f"数据恢复失败: {e}", OutputType.ERROR)
297
+ print(f"数据恢复失败: {e}")
301
298
 
302
299
  return True
303
300
 
@@ -333,9 +330,7 @@ def try_switch_to_jca_if_git_repo(
333
330
  if res.returncode == 0:
334
331
  git_root = res.stdout.strip()
335
332
  if git_root and os.path.isdir(git_root):
336
- PrettyOutput.print(
337
- f"检测到当前位于 Git 仓库: {git_root}", OutputType.INFO
338
- )
333
+ print(f"ℹ️ 检测到当前位于 Git 仓库: {git_root}")
339
334
  if user_confirm(
340
335
  "检测到Git仓库,是否切换到代码开发模式(jca)?", default=False
341
336
  ):
@@ -351,10 +346,7 @@ def try_switch_to_jca_if_git_repo(
351
346
  args += ["--restore-session"]
352
347
  if task:
353
348
  args += ["-r", task]
354
- PrettyOutput.print(
355
- "正在切换到 'jca'(jarvis-code-agent)以进入代码开发模式...",
356
- OutputType.INFO,
357
- )
349
+ print("ℹ️ 正在切换到 'jca'(jarvis-code-agent)以进入代码开发模式...")
358
350
  os.execvp(args[0], args)
359
351
  except Exception:
360
352
  # 静默忽略检测异常,不影响主流程
@@ -458,10 +450,7 @@ def handle_builtin_config_selector(
458
450
  # 可选调试输出:查看每类的搜索目录
459
451
  try:
460
452
  if os.environ.get("JARVIS_DEBUG_BUILTIN_SELECTOR") == "1":
461
- PrettyOutput.print(
462
- f"DEBUG: category={cat} search_dirs=" + ", ".join(str(p) for p in unique_dirs),
463
- OutputType.INFO,
464
- )
453
+ print(f"ℹ️ DEBUG: category={cat} search_dirs=" + ", ".join(str(p) for p in unique_dirs))
465
454
  except Exception:
466
455
  pass
467
456
 
@@ -538,7 +527,7 @@ def handle_builtin_config_selector(
538
527
  },
539
528
  )
540
529
 
541
- PrettyOutput.section("可用的内置配置", OutputType.SUCCESS)
530
+ print("可用的内置配置")
542
531
  # 使用 rich Table 呈现
543
532
  table = Table(show_header=True, header_style="bold magenta")
544
533
  table.add_column("No.", style="cyan", no_wrap=True)
@@ -643,9 +632,7 @@ def handle_builtin_config_selector(
643
632
  args += ["-g", str(model_group)]
644
633
 
645
634
  if args:
646
- PrettyOutput.print(
647
- f"正在启动: {' '.join(args)}", OutputType.INFO
648
- )
635
+ print(f"ℹ️ 正在启动: {' '.join(args)}")
649
636
  os.execvp(args[0], args)
650
637
  except Exception:
651
638
  # 任何异常都不影响默认流程
@@ -710,10 +697,14 @@ def run_cli(
710
697
  non_interactive: bool = typer.Option(
711
698
  False, "-n", "--non-interactive", help="启用非交互模式:用户无法与命令交互,脚本执行超时限制为5分钟"
712
699
  ),
713
- plan: Optional[bool] = typer.Option(None, "--plan/--no-plan", help="启用或禁用任务规划(不指定则从配置加载)"),
714
700
  web: bool = typer.Option(False, "--web", help="以 Web 模式启动,通过浏览器 WebSocket 交互"),
715
701
  web_host: str = typer.Option("127.0.0.1", "--web-host", help="Web 服务主机"),
716
702
  web_port: int = typer.Option(8765, "--web-port", help="Web 服务端口"),
703
+ web_launch_cmd: Optional[str] = typer.Option(
704
+ None,
705
+ "--web-launch-cmd",
706
+ help="交互式终端启动命令(字符串格式,用空格分隔,如: --web-launch-cmd 'jca --task \"xxx\"')",
707
+ ),
717
708
  stop: bool = typer.Option(False, "--stop", help="停止后台 Web 服务(需与 --web 一起使用)"),
718
709
  ) -> None:
719
710
  """Jarvis AI assistant command-line interface."""
@@ -748,10 +739,7 @@ def run_cli(
748
739
 
749
740
  # 非交互模式要求从命令行传入任务
750
741
  if non_interactive and not (task and str(task).strip()):
751
- PrettyOutput.print(
752
- "非交互模式已启用:必须使用 --task 传入任务内容,因多行输入不可用。",
753
- OutputType.ERROR,
754
- )
742
+ print("❌ 非交互模式已启用:必须使用 --task 传入任务内容,因多行输入不可用。")
755
743
  raise typer.Exit(code=2)
756
744
 
757
745
  # 处理数据备份
@@ -811,10 +799,10 @@ def run_cli(
811
799
  candidate_pid = int(ln.strip())
812
800
  try:
813
801
  os.kill(candidate_pid, signal.SIGTERM)
814
- PrettyOutput.print(f"已按端口停止后台 Web 服务 (PID {candidate_pid})。", OutputType.SUCCESS)
802
+ print(f"已按端口停止后台 Web 服务 (PID {candidate_pid})。")
815
803
  killed_any = True
816
804
  except Exception as e:
817
- PrettyOutput.print(f"按端口停止失败: {e}", OutputType.WARNING)
805
+ print(f"⚠️ 按端口停止失败: {e}")
818
806
  except Exception:
819
807
  continue
820
808
  except Exception:
@@ -833,10 +821,10 @@ def run_cli(
833
821
  candidate_pid = int(pid_str2)
834
822
  try:
835
823
  os.kill(candidate_pid, signal.SIGTERM)
836
- PrettyOutput.print(f"已按端口停止后台 Web 服务 (PID {candidate_pid})。", OutputType.SUCCESS)
824
+ print(f"已按端口停止后台 Web 服务 (PID {candidate_pid})。")
837
825
  killed_any = True
838
826
  except Exception as e:
839
- PrettyOutput.print(f"按端口停止失败: {e}", OutputType.WARNING)
827
+ print(f"⚠️ 按端口停止失败: {e}")
840
828
  break
841
829
  except Exception:
842
830
  continue
@@ -853,10 +841,10 @@ def run_cli(
853
841
  p = int(ptxt)
854
842
  try:
855
843
  os.kill(p, signal.SIGTERM)
856
- PrettyOutput.print(f"已停止后台 Web 服务 (PID {p})。", OutputType.SUCCESS)
844
+ print(f"已停止后台 Web 服务 (PID {p})。")
857
845
  killed_any = True
858
846
  except Exception as e:
859
- PrettyOutput.print(f"停止 PID {p} 失败: {e}", OutputType.WARNING)
847
+ print(f"⚠️ 停止 PID {p} 失败: {e}")
860
848
  except Exception:
861
849
  pass
862
850
  try:
@@ -866,7 +854,7 @@ def run_cli(
866
854
  except Exception:
867
855
  pass
868
856
  if not killed_any:
869
- PrettyOutput.print("未找到后台 Web 服务的 PID 文件,可能未启动或已停止。", OutputType.WARNING)
857
+ print("⚠️ 未找到后台 Web 服务的 PID 文件,可能未启动或已停止。")
870
858
  return
871
859
  # 优先使用 PID 文件中的 PID
872
860
  try:
@@ -878,10 +866,10 @@ def run_cli(
878
866
  if pid > 0:
879
867
  try:
880
868
  os.kill(pid, signal.SIGTERM)
881
- PrettyOutput.print(f"已向后台 Web 服务发送停止信号 (PID {pid})。", OutputType.SUCCESS)
869
+ print(f"已向后台 Web 服务发送停止信号 (PID {pid})。")
882
870
  killed = True
883
871
  except Exception as e:
884
- PrettyOutput.print(f"发送停止信号失败或进程不存在: {e}", OutputType.WARNING)
872
+ print(f"⚠️ 发送停止信号失败或进程不存在: {e}")
885
873
  if not killed:
886
874
  # 无 PID 文件或停止失败时,尝试按端口查找进程
887
875
  candidate_pid = 0
@@ -921,10 +909,10 @@ def run_cli(
921
909
  if candidate_pid:
922
910
  try:
923
911
  os.kill(candidate_pid, signal.SIGTERM)
924
- PrettyOutput.print(f"已按端口停止后台 Web 服务 (PID {candidate_pid})。", OutputType.SUCCESS)
912
+ print(f"已按端口停止后台 Web 服务 (PID {candidate_pid})。")
925
913
  killed = True
926
914
  except Exception as e:
927
- PrettyOutput.print(f"按端口停止失败: {e}", OutputType.WARNING)
915
+ print(f"⚠️ 按端口停止失败: {e}")
928
916
  # 清理可能存在的 PID 文件(两个位置)
929
917
  try:
930
918
  pidfile.unlink(missing_ok=True) # 家目录位置
@@ -936,7 +924,7 @@ def run_cli(
936
924
  except Exception:
937
925
  pass
938
926
  except Exception as e:
939
- PrettyOutput.print(f"停止后台 Web 服务失败: {e}", OutputType.ERROR)
927
+ print(f"停止后台 Web 服务失败: {e}")
940
928
  finally:
941
929
  return
942
930
  # 后台启动:父进程拉起子进程并记录 PID
@@ -970,6 +958,8 @@ def run_cli(
970
958
  args += ["-D"]
971
959
  if non_interactive:
972
960
  args += ["-n"]
961
+ if web_launch_cmd:
962
+ args += ["--web-launch-cmd", str(web_launch_cmd)]
973
963
  env = os.environ.copy()
974
964
  env["JARVIS_WEB_DAEMON"] = "1"
975
965
  # 启动子进程(后台运行)
@@ -990,24 +980,23 @@ def run_cli(
990
980
  pidfile.write_text(str(proc.pid), encoding="utf-8")
991
981
  except Exception:
992
982
  pass
993
- PrettyOutput.print(
994
- f"Web 服务已在后台启动 (PID {proc.pid}),地址: http://{web_host}:{web_port}",
995
- OutputType.SUCCESS,
996
- )
983
+ print(f"✅ Web 服务已在后台启动 (PID {proc.pid}),地址: http://{web_host}:{web_port}")
997
984
  except Exception as e:
998
- PrettyOutput.print(f"后台启动 Web 服务失败: {e}", OutputType.ERROR)
985
+ print(f"后台启动 Web 服务失败: {e}")
999
986
  raise typer.Exit(code=1)
1000
987
  return
1001
988
 
1002
989
  # 在初始化环境前检测Git仓库,并可选择自动切换到代码开发模式(jca)
1003
- if not non_interactive:
990
+ # 如果指定了 -T/--task 参数,跳过切换提示
991
+ if not non_interactive and not task:
1004
992
  try_switch_to_jca_if_git_repo(
1005
993
  model_group, tool_group, config_file, restore_session, task
1006
994
  )
1007
995
 
1008
996
  # 在进入默认通用代理前,列出内置配置供选择(agent/multi_agent/roles)
1009
997
  # 非交互模式下跳过内置角色/配置选择
1010
- if not non_interactive:
998
+ # 如果指定了 -T/--task 参数,跳过配置选择
999
+ if not non_interactive and not task:
1011
1000
  handle_builtin_config_selector(model_group, tool_group, config_file, task)
1012
1001
 
1013
1002
  # 初始化环境
@@ -1051,13 +1040,7 @@ def run_cli(
1051
1040
  non_interactive=non_interactive,
1052
1041
  **extra_kwargs,
1053
1042
  )
1054
- agent = agent_manager.initialize()
1055
- # CLI 开关:启用/禁用规划(不依赖 AgentManager 支持,直接设置 Agent 属性)
1056
- if plan is not None:
1057
- try:
1058
- agent.plan = bool(plan)
1059
- except Exception:
1060
- pass
1043
+ agent_manager.initialize()
1061
1044
 
1062
1045
  if web:
1063
1046
  try:
@@ -1068,7 +1051,7 @@ def run_cli(
1068
1051
  try:
1069
1052
  import os as _os
1070
1053
  _os.environ["COLUMNS"] = "200"
1071
- # 尝试固定全局 Console 的宽度(PrettyOutput 使用该 Console 实例)
1054
+ # 尝试固定全局 Console 的宽度
1072
1055
  try:
1073
1056
  from jarvis.jarvis_utils.globals import console as _console
1074
1057
  try:
@@ -1087,45 +1070,77 @@ def run_cli(
1087
1070
  enable_web_stdin_redirect()
1088
1071
  except Exception:
1089
1072
  pass
1090
- # 记录用于交互式终端(PTY)重启的 jvs 启动命令(移除 web 相关参数)
1091
- try:
1092
- import sys as _sys
1093
- import os as _os
1094
- import json as _json
1095
- _argv = list(_sys.argv)
1096
- # 去掉程序名(argv[0]),并过滤 --web 相关参数
1097
- filtered = []
1098
- i = 1
1099
- while i < len(_argv):
1100
- a = _argv[i]
1101
- if a == "--web" or a.startswith("--web="):
1102
- i += 1
1103
- continue
1104
- if a == "--web-host":
1105
- i += 2
1106
- continue
1107
- if a.startswith("--web-host="):
1108
- i += 1
1109
- continue
1110
- if a == "--web-port":
1111
- i += 2
1112
- continue
1113
- if a.startswith("--web-port="):
1073
+ # 构建用于交互式终端(PTY)重启的启动命令
1074
+ launch_cmd = None
1075
+ # 优先使用命令行参数指定的启动命令
1076
+ if web_launch_cmd and web_launch_cmd.strip():
1077
+ # 解析字符串命令(支持引号)
1078
+ try:
1079
+ import shlex
1080
+ launch_cmd = shlex.split(web_launch_cmd.strip())
1081
+ # 调试输出(可选,可以通过环境变量控制)
1082
+ if os.environ.get("JARVIS_DEBUG_WEB_LAUNCH_CMD") == "1":
1083
+ print(f"🔍 解析后的启动命令: {launch_cmd}")
1084
+ except Exception:
1085
+ # 如果解析失败,使用简单的空格分割
1086
+ launch_cmd = web_launch_cmd.strip().split()
1087
+ if os.environ.get("JARVIS_DEBUG_WEB_LAUNCH_CMD") == "1":
1088
+ print(f"🔍 使用简单分割的启动命令: {launch_cmd}")
1089
+ else:
1090
+ # 如果没有指定,则自动构建(移除 web 相关参数)
1091
+ try:
1092
+ import sys as _sys
1093
+ import os as _os
1094
+ _argv = list(_sys.argv)
1095
+ # 去掉程序名(argv[0]),并过滤 --web 相关参数
1096
+ filtered = []
1097
+ i = 1
1098
+ while i < len(_argv):
1099
+ a = _argv[i]
1100
+ if a == "--web" or a.startswith("--web="):
1101
+ i += 1
1102
+ continue
1103
+ if a == "--web-host":
1104
+ i += 2
1105
+ continue
1106
+ if a.startswith("--web-host="):
1107
+ i += 1
1108
+ continue
1109
+ if a == "--web-port":
1110
+ i += 2
1111
+ continue
1112
+ if a.startswith("--web-port="):
1113
+ i += 1
1114
+ continue
1115
+ if a == "--web-launch-cmd":
1116
+ # 跳过 --web-launch-cmd 及其值
1117
+ i += 2
1118
+ continue
1119
+ if a.startswith("--web-launch-cmd="):
1120
+ i += 1
1121
+ continue
1122
+ filtered.append(a)
1114
1123
  i += 1
1115
- continue
1116
- filtered.append(a)
1117
- i += 1
1118
- # 使用 jvs 命令作为可执行文件,保留其余业务参数
1119
- cmd = ["jvs"] + filtered
1120
- _os.environ["JARVIS_WEB_LAUNCH_JSON"] = _json.dumps(cmd, ensure_ascii=False)
1121
- except Exception:
1122
- pass
1123
- PrettyOutput.print("以 Web 模式启动,请在浏览器中打开提供的地址进行交互。", OutputType.INFO)
1124
- # 启动 Web 服务(阻塞调用)
1125
- start_web_server(agent_manager, host=web_host, port=web_port)
1124
+ # 使用 jvs 命令作为可执行文件,保留其余业务参数
1125
+ launch_cmd = ["jvs"] + filtered
1126
+ except Exception:
1127
+ pass
1128
+
1129
+ # 同时写入环境变量作为备选(向后兼容)
1130
+ if launch_cmd:
1131
+ try:
1132
+ import os as _os
1133
+ import json as _json
1134
+ _os.environ["JARVIS_WEB_LAUNCH_JSON"] = _json.dumps(launch_cmd, ensure_ascii=False)
1135
+ except Exception:
1136
+ pass
1137
+
1138
+ print("ℹ️ 以 Web 模式启动,请在浏览器中打开提供的地址进行交互。")
1139
+ # 启动 Web 服务(阻塞调用),传入启动命令
1140
+ start_web_server(agent_manager, host=web_host, port=web_port, launch_command=launch_cmd)
1126
1141
  return
1127
1142
  except Exception as e:
1128
- PrettyOutput.print(f"Web 模式启动失败: {e}", OutputType.ERROR)
1143
+ print(f"Web 模式启动失败: {e}")
1129
1144
  raise typer.Exit(code=1)
1130
1145
 
1131
1146
  # 默认 CLI 模式:运行任务(可能来自 --task 或交互输入)
@@ -1133,7 +1148,7 @@ def run_cli(
1133
1148
  except typer.Exit:
1134
1149
  raise
1135
1150
  except Exception as err: # pylint: disable=broad-except
1136
- PrettyOutput.print(f"初始化错误: {str(err)}", OutputType.ERROR)
1151
+ print(f"初始化错误: {str(err)}")
1137
1152
  raise typer.Exit(code=1)
1138
1153
 
1139
1154
 
@@ -0,0 +1,57 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Language extractors for file context handler.
3
+
4
+ This module automatically registers all language extractors.
5
+ """
6
+
7
+ # Import all language extractors to trigger registration
8
+ try:
9
+ from .python_extractor import register_python_extractor
10
+ register_python_extractor()
11
+ except (ImportError, Exception):
12
+ pass
13
+
14
+ try:
15
+ from .rust_extractor import register_rust_extractor
16
+ register_rust_extractor()
17
+ except (ImportError, Exception):
18
+ pass
19
+
20
+ try:
21
+ from .go_extractor import register_go_extractor
22
+ register_go_extractor()
23
+ except (ImportError, Exception):
24
+ pass
25
+
26
+ try:
27
+ from .c_extractor import register_c_extractor
28
+ register_c_extractor()
29
+ except (ImportError, Exception):
30
+ pass
31
+
32
+ try:
33
+ from .cpp_extractor import register_cpp_extractor
34
+ register_cpp_extractor()
35
+ except (ImportError, Exception):
36
+ pass
37
+
38
+ try:
39
+ from .javascript_extractor import register_javascript_extractor
40
+ register_javascript_extractor()
41
+ except (ImportError, Exception):
42
+ pass
43
+
44
+ try:
45
+ from .typescript_extractor import register_typescript_extractor
46
+ register_typescript_extractor()
47
+ except (ImportError, Exception):
48
+ pass
49
+
50
+ try:
51
+ from .java_extractor import register_java_extractor
52
+ register_java_extractor()
53
+ except (ImportError, Exception):
54
+ pass
55
+
56
+ __all__ = []
57
+
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ """C language symbol extractor."""
3
+
4
+ from typing import Optional, Any
5
+
6
+ from jarvis.jarvis_agent.file_context_handler import register_language_extractor
7
+
8
+
9
+ def create_c_extractor() -> Optional[Any]:
10
+ """Create C symbol extractor using tree-sitter."""
11
+ try:
12
+ from jarvis.jarvis_code_agent.code_analyzer.languages.c_cpp_language import CSymbolExtractor
13
+ return CSymbolExtractor()
14
+ except (ImportError, RuntimeError, Exception):
15
+ return None
16
+
17
+
18
+ def register_c_extractor() -> None:
19
+ """Register C extractor for .c and .h files."""
20
+ register_language_extractor(['.c', '.h'], create_c_extractor)
21
+
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ """C++ language symbol extractor."""
3
+
4
+ from typing import Optional, Any
5
+
6
+ from jarvis.jarvis_agent.file_context_handler import register_language_extractor
7
+
8
+
9
+ def create_cpp_extractor() -> Optional[Any]:
10
+ """Create C++ symbol extractor using tree-sitter."""
11
+ try:
12
+ from jarvis.jarvis_code_agent.code_analyzer.languages.c_cpp_language import CppSymbolExtractor
13
+ return CppSymbolExtractor()
14
+ except (ImportError, RuntimeError, Exception):
15
+ return None
16
+
17
+
18
+ def register_cpp_extractor() -> None:
19
+ """Register C++ extractor for .cpp, .cc, .cxx, .hpp, .hxx files."""
20
+ register_language_extractor(['.cpp', '.cc', '.cxx', '.hpp', '.hxx'], create_cpp_extractor)
21
+
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Go language symbol extractor."""
3
+
4
+ from typing import Optional, Any
5
+
6
+ from jarvis.jarvis_agent.file_context_handler import register_language_extractor
7
+
8
+
9
+ def create_go_extractor() -> Optional[Any]:
10
+ """Create Go symbol extractor using tree-sitter."""
11
+ try:
12
+ from jarvis.jarvis_code_agent.code_analyzer.languages.go_language import GoSymbolExtractor
13
+ return GoSymbolExtractor()
14
+ except (ImportError, RuntimeError, Exception):
15
+ return None
16
+
17
+
18
+ def register_go_extractor() -> None:
19
+ """Register Go extractor for .go files."""
20
+ register_language_extractor('.go', create_go_extractor)
21
+
@@ -0,0 +1,84 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Java language symbol extractor."""
3
+
4
+ from typing import Optional, Any
5
+
6
+ from jarvis.jarvis_agent.file_context_handler import register_language_extractor
7
+
8
+
9
+ def create_java_extractor() -> Optional[Any]:
10
+ """Create Java symbol extractor using tree-sitter."""
11
+ try:
12
+ from jarvis.jarvis_code_agent.code_analyzer.languages.java_language import JavaSymbolExtractor
13
+ return JavaSymbolExtractor()
14
+ except (ImportError, RuntimeError, Exception):
15
+ # 如果 code_analyzer 中没有 Java 支持,尝试直接使用 tree-sitter
16
+ try:
17
+ from tree_sitter import Language, Parser
18
+ import tree_sitter_java
19
+ from jarvis.jarvis_code_agent.code_analyzer.symbol_extractor import Symbol
20
+
21
+ JAVA_LANGUAGE = tree_sitter_java.language()
22
+ JAVA_SYMBOL_QUERY = """
23
+ (method_declaration
24
+ name: (identifier) @method.name)
25
+
26
+ (class_declaration
27
+ name: (identifier) @class.name)
28
+
29
+ (interface_declaration
30
+ name: (identifier) @interface.name)
31
+
32
+ (field_declaration
33
+ (variable_declarator
34
+ name: (identifier) @field.name))
35
+ """
36
+
37
+ class JavaSymbolExtractor:
38
+ def __init__(self):
39
+ # 如果传入的是 PyCapsule,需要转换为 Language 对象
40
+ if not isinstance(JAVA_LANGUAGE, Language):
41
+ self.language = Language(JAVA_LANGUAGE)
42
+ else:
43
+ self.language = JAVA_LANGUAGE
44
+ self.parser = Parser()
45
+ # 使用 language 属性而不是 set_language 方法
46
+ self.parser.language = self.language
47
+ self.symbol_query = JAVA_SYMBOL_QUERY
48
+
49
+ def extract_symbols(self, file_path: str, content: str) -> list:
50
+ try:
51
+ tree = self.parser.parse(bytes(content, "utf8"))
52
+ query = self.language.query(self.symbol_query)
53
+ captures = query.captures(tree.root_node)
54
+
55
+ symbols = []
56
+ for node, name in captures:
57
+ kind_map = {
58
+ "method.name": "method",
59
+ "class.name": "class",
60
+ "interface.name": "interface",
61
+ "field.name": "field",
62
+ }
63
+ symbol_kind = kind_map.get(name)
64
+ if symbol_kind:
65
+ symbols.append(Symbol(
66
+ name=node.text.decode('utf8'),
67
+ kind=symbol_kind,
68
+ file_path=file_path,
69
+ line_start=node.start_point[0] + 1,
70
+ line_end=node.end_point[0] + 1,
71
+ ))
72
+ return symbols
73
+ except Exception:
74
+ return []
75
+
76
+ return JavaSymbolExtractor()
77
+ except (ImportError, Exception):
78
+ return None
79
+
80
+
81
+ def register_java_extractor() -> None:
82
+ """Register Java extractor for .java files."""
83
+ register_language_extractor('.java', create_java_extractor)
84
+