jarvis-ai-assistant 0.2.7__tar.gz → 0.2.8__tar.gz

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 (122) hide show
  1. {jarvis_ai_assistant-0.2.7/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.2.8}/PKG-INFO +1 -1
  2. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/pyproject.toml +1 -1
  3. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/setup.py +1 -1
  4. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/__init__.py +1 -1
  5. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/jarvis.py +230 -2
  6. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_data/config_schema.json +5 -0
  7. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/registry.py +47 -20
  8. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/config.py +10 -0
  9. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/utils.py +27 -1
  10. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
  11. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/LICENSE +0 -0
  12. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/MANIFEST.in +0 -0
  13. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/README.md +0 -0
  14. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/setup.cfg +0 -0
  15. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/__init__.py +0 -0
  16. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/builtin_input_handler.py +0 -0
  17. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/edit_file_handler.py +0 -0
  18. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/main.py +0 -0
  19. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/output_handler.py +0 -0
  20. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/prompt_builder.py +0 -0
  21. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/prompts.py +0 -0
  22. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/protocols.py +0 -0
  23. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/session_manager.py +0 -0
  24. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/shell_input_handler.py +0 -0
  25. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_agent/tool_executor.py +0 -0
  26. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_agent/__init__.py +0 -0
  27. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_agent/code_agent.py +0 -0
  28. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_agent/lint.py +0 -0
  29. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/__init__.py +0 -0
  30. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/c_cpp.py +0 -0
  31. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/csharp.py +0 -0
  32. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/data_format.py +0 -0
  33. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/devops.py +0 -0
  34. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/docs.py +0 -0
  35. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/go.py +0 -0
  36. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/infrastructure.py +0 -0
  37. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/java.py +0 -0
  38. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/javascript.py +0 -0
  39. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/kotlin.py +0 -0
  40. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/loader.py +0 -0
  41. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/php.py +0 -0
  42. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/python.py +0 -0
  43. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/ruby.py +0 -0
  44. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/rust.py +0 -0
  45. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/shell.py +0 -0
  46. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/sql.py +0 -0
  47. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/swift.py +0 -0
  48. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/checklists/web.py +0 -0
  49. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_code_analysis/code_review.py +0 -0
  50. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_data/tiktoken/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +0 -0
  51. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_git_squash/__init__.py +0 -0
  52. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_git_squash/main.py +0 -0
  53. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_git_utils/git_commiter.py +0 -0
  54. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_mcp/__init__.py +0 -0
  55. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_mcp/sse_mcp_client.py +0 -0
  56. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_mcp/stdio_mcp_client.py +0 -0
  57. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_mcp/streamable_mcp_client.py +0 -0
  58. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_methodology/main.py +0 -0
  59. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_multi_agent/__init__.py +0 -0
  60. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_multi_agent/main.py +0 -0
  61. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/__init__.py +0 -0
  62. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/ai8.py +0 -0
  63. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/base.py +0 -0
  64. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/human.py +0 -0
  65. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/kimi.py +0 -0
  66. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/openai.py +0 -0
  67. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/registry.py +0 -0
  68. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/tongyi.py +0 -0
  69. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform/yuanbao.py +0 -0
  70. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform_manager/__init__.py +0 -0
  71. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform_manager/main.py +0 -0
  72. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_platform_manager/service.py +0 -0
  73. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/__init__.py +0 -0
  74. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/cache.py +0 -0
  75. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/cli.py +0 -0
  76. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/embedding_manager.py +0 -0
  77. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/llm_interface.py +0 -0
  78. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/query_rewriter.py +0 -0
  79. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/rag_pipeline.py +0 -0
  80. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/reranker.py +0 -0
  81. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_rag/retriever.py +0 -0
  82. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
  83. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_smart_shell/main.py +0 -0
  84. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_stats/__init__.py +0 -0
  85. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_stats/cli.py +0 -0
  86. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_stats/stats.py +0 -0
  87. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_stats/storage.py +0 -0
  88. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_stats/visualizer.py +0 -0
  89. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/__init__.py +0 -0
  90. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/ask_user.py +0 -0
  91. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/base.py +0 -0
  92. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/clear_memory.py +0 -0
  93. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/cli/__init__.py +0 -0
  94. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/cli/main.py +0 -0
  95. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/edit_file.py +0 -0
  96. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/execute_script.py +0 -0
  97. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/file_analyzer.py +0 -0
  98. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/generate_new_tool.py +0 -0
  99. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/methodology.py +0 -0
  100. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/read_code.py +0 -0
  101. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/read_webpage.py +0 -0
  102. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/retrieve_memory.py +0 -0
  103. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/rewrite_file.py +0 -0
  104. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/save_memory.py +0 -0
  105. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/search_web.py +0 -0
  106. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_tools/virtual_tty.py +0 -0
  107. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/__init__.py +0 -0
  108. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/builtin_replace_map.py +0 -0
  109. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/embedding.py +0 -0
  110. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/file_processors.py +0 -0
  111. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/git_utils.py +0 -0
  112. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/globals.py +0 -0
  113. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/http.py +0 -0
  114. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/input.py +0 -0
  115. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/methodology.py +0 -0
  116. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/output.py +0 -0
  117. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis/jarvis_utils/tag.py +0 -0
  118. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
  119. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  120. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  121. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
  122. {jarvis_ai_assistant-0.2.7 → jarvis_ai_assistant-0.2.8}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.2.7
3
+ Version: 0.2.8
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jarvis-ai-assistant"
7
- version = "0.2.7"
7
+ version = "0.2.8"
8
8
  description = "Jarvis: An AI assistant that uses tools to interact with the system"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "skyfire", email = "skyfireitdiy@hotmail.com" }]
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages # type: ignore
3
3
 
4
4
  setup(
5
5
  name="jarvis-ai-assistant",
6
- version="0.2.7",
6
+ version="0.2.8",
7
7
  author="skyfire",
8
8
  author_email="skyfireitdiy@hotmail.com",
9
9
  description="An AI assistant that uses various tools to interact with the system",
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.2.7"
4
+ __version__ = "0.2.8"
@@ -221,6 +221,188 @@ def _parse_selection(selection_str: str, max_value: int) -> List[int]:
221
221
  return sorted(list(selected))
222
222
 
223
223
 
224
+ def _handle_share_tool(config_file: Optional[str] = None) -> None:
225
+ """处理工具分享功能"""
226
+ from jarvis.jarvis_utils.config import (
227
+ get_central_tool_repo,
228
+ get_data_dir,
229
+ )
230
+ import glob
231
+ import shutil
232
+
233
+ # 获取中心工具仓库配置
234
+ central_repo = get_central_tool_repo()
235
+ if not central_repo:
236
+ PrettyOutput.print(
237
+ "错误:未配置中心工具仓库(JARVIS_CENTRAL_TOOL_REPO)",
238
+ OutputType.ERROR,
239
+ )
240
+ PrettyOutput.print("请在配置文件中设置中心工具仓库的Git地址", OutputType.INFO)
241
+ raise typer.Exit(code=1)
242
+
243
+ # 克隆或更新中心工具仓库
244
+ central_repo_path = os.path.join(get_data_dir(), "central_tool_repo")
245
+ if not os.path.exists(central_repo_path):
246
+ PrettyOutput.print(f"正在克隆中心工具仓库...", OutputType.INFO)
247
+ subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
248
+ else:
249
+ PrettyOutput.print(f"正在更新中心工具仓库...", OutputType.INFO)
250
+ # 检查是否是空仓库
251
+ try:
252
+ # 先尝试获取远程分支信息
253
+ result = subprocess.run(
254
+ ["git", "ls-remote", "--heads", "origin"],
255
+ cwd=central_repo_path,
256
+ capture_output=True,
257
+ text=True,
258
+ check=True,
259
+ )
260
+ # 如果有远程分支,执行pull
261
+ if result.stdout.strip():
262
+ subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
263
+ else:
264
+ PrettyOutput.print(
265
+ "中心工具仓库是空的,将初始化为新仓库", OutputType.INFO
266
+ )
267
+ except subprocess.CalledProcessError:
268
+ # 如果命令失败,可能是网络问题或其他错误
269
+ PrettyOutput.print("无法连接到远程仓库,将跳过更新", OutputType.WARNING)
270
+
271
+ # 获取中心仓库中已有的工具文件名
272
+ existing_tools = set()
273
+ for filepath in glob.glob(os.path.join(central_repo_path, "*.py")):
274
+ existing_tools.add(os.path.basename(filepath))
275
+
276
+ # 只从数据目录的tools目录获取工具
277
+ local_tools_dir = os.path.join(get_data_dir(), "tools")
278
+ if not os.path.exists(local_tools_dir):
279
+ PrettyOutput.print(
280
+ f"本地工具目录不存在: {local_tools_dir}",
281
+ OutputType.WARNING,
282
+ )
283
+ raise typer.Exit(code=0)
284
+
285
+ # 收集本地工具文件(排除已存在的)
286
+ tool_files = []
287
+ for filepath in glob.glob(os.path.join(local_tools_dir, "*.py")):
288
+ filename = os.path.basename(filepath)
289
+ # 跳过__init__.py和已存在的文件
290
+ if filename == "__init__.py" or filename in existing_tools:
291
+ continue
292
+
293
+ # 尝试获取工具名称(通过简单解析)
294
+ tool_name = filename[:-3] # 移除.py后缀
295
+ tool_files.append(
296
+ {
297
+ "path": filepath,
298
+ "filename": filename,
299
+ "tool_name": tool_name,
300
+ }
301
+ )
302
+
303
+ if not tool_files:
304
+ PrettyOutput.print(
305
+ "没有找到新的工具文件(所有工具可能已存在于中心仓库)",
306
+ OutputType.WARNING,
307
+ )
308
+ raise typer.Exit(code=0)
309
+
310
+ # 显示可选的工具
311
+ tool_list = ["\n可分享的工具(已排除中心仓库中已有的):"]
312
+ for i, tool in enumerate(tool_files, 1):
313
+ tool_list.append(f"[{i}] {tool['tool_name']} ({tool['filename']})")
314
+
315
+ # 一次性打印所有工具
316
+ PrettyOutput.print("\n".join(tool_list), OutputType.INFO)
317
+
318
+ # 让用户选择要分享的工具
319
+ while True:
320
+ try:
321
+ choice_str = prompt(
322
+ "\n请选择要分享的工具编号(支持格式: 1,2,3,4-9,20 或 all):"
323
+ ).strip()
324
+ if choice_str == "0":
325
+ raise typer.Exit(code=0)
326
+
327
+ selected_tools = []
328
+ if choice_str.lower() == "all":
329
+ selected_tools = tool_files
330
+ else:
331
+ selected_indices = _parse_selection(choice_str, len(tool_files))
332
+ if not selected_indices:
333
+ PrettyOutput.print("无效的选择", OutputType.WARNING)
334
+ continue
335
+ selected_tools = [tool_files[i - 1] for i in selected_indices]
336
+
337
+ # 确认操作
338
+ share_list = [
339
+ "\n将要分享以下工具到中心仓库(注意:文件将被移动而非复制):"
340
+ ]
341
+ for tool in selected_tools:
342
+ share_list.append(f"- {tool['tool_name']} ({tool['filename']})")
343
+ PrettyOutput.print("\n".join(share_list), OutputType.WARNING)
344
+
345
+ if not user_confirm("确认移动这些工具到中心仓库吗?(原文件将被删除)"):
346
+ continue
347
+
348
+ # 移动选中的工具到中心仓库
349
+ moved_list = []
350
+ for tool in selected_tools:
351
+ src_file = tool["path"]
352
+ dst_file = os.path.join(central_repo_path, tool["filename"])
353
+ shutil.move(src_file, dst_file) # 使用move而不是copy
354
+ moved_list.append(f"已移动: {tool['tool_name']}")
355
+
356
+ # 一次性显示所有移动结果
357
+ if moved_list:
358
+ PrettyOutput.print("\n".join(moved_list), OutputType.SUCCESS)
359
+
360
+ # 提交并推送更改
361
+ PrettyOutput.print("\n正在提交更改...", OutputType.INFO)
362
+ subprocess.run(["git", "add", "."], cwd=central_repo_path, check=True)
363
+
364
+ commit_msg = f"Add {len(selected_tools)} tool(s) from local collection"
365
+ subprocess.run(
366
+ ["git", "commit", "-m", commit_msg], cwd=central_repo_path, check=True
367
+ )
368
+
369
+ PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
370
+ # 检查是否需要设置上游分支(空仓库的情况)
371
+ try:
372
+ # 先尝试普通推送
373
+ subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
374
+ except subprocess.CalledProcessError:
375
+ # 如果失败,可能是空仓库,尝试设置上游分支
376
+ try:
377
+ subprocess.run(
378
+ ["git", "push", "-u", "origin", "main"],
379
+ cwd=central_repo_path,
380
+ check=True,
381
+ )
382
+ except subprocess.CalledProcessError:
383
+ # 如果main分支不存在,尝试master分支
384
+ subprocess.run(
385
+ ["git", "push", "-u", "origin", "master"],
386
+ cwd=central_repo_path,
387
+ check=True,
388
+ )
389
+
390
+ PrettyOutput.print("\n工具已成功分享到中心仓库!", OutputType.SUCCESS)
391
+ PrettyOutput.print(
392
+ f"原文件已从 {local_tools_dir} 移动到中心仓库", OutputType.INFO
393
+ )
394
+ break
395
+
396
+ except ValueError:
397
+ PrettyOutput.print("请输入有效的数字", OutputType.WARNING)
398
+ except subprocess.CalledProcessError as e:
399
+ PrettyOutput.print(f"Git操作失败: {str(e)}", OutputType.ERROR)
400
+ raise typer.Exit(code=1)
401
+ except Exception as e:
402
+ PrettyOutput.print(f"分享工具时出错: {str(e)}", OutputType.ERROR)
403
+ raise typer.Exit(code=1)
404
+
405
+
224
406
  def _handle_share_methodology(config_file: Optional[str] = None) -> None:
225
407
  """处理方法论分享功能"""
226
408
  from jarvis.jarvis_utils.config import (
@@ -249,7 +431,26 @@ def _handle_share_methodology(config_file: Optional[str] = None) -> None:
249
431
  subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
250
432
  else:
251
433
  PrettyOutput.print(f"正在更新中心方法论仓库...", OutputType.INFO)
252
- subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
434
+ # 检查是否是空仓库
435
+ try:
436
+ # 先尝试获取远程分支信息
437
+ result = subprocess.run(
438
+ ["git", "ls-remote", "--heads", "origin"],
439
+ cwd=central_repo_path,
440
+ capture_output=True,
441
+ text=True,
442
+ check=True,
443
+ )
444
+ # 如果有远程分支,执行pull
445
+ if result.stdout.strip():
446
+ subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
447
+ else:
448
+ PrettyOutput.print(
449
+ "中心方法论仓库是空的,将初始化为新仓库", OutputType.INFO
450
+ )
451
+ except subprocess.CalledProcessError:
452
+ # 如果命令失败,可能是网络问题或其他错误
453
+ PrettyOutput.print("无法连接到远程仓库,将跳过更新", OutputType.WARNING)
253
454
 
254
455
  # 获取中心仓库中已有的方法论
255
456
  existing_methodologies = {} # 改为字典,存储 problem_type -> content 的映射
@@ -385,7 +586,25 @@ def _handle_share_methodology(config_file: Optional[str] = None) -> None:
385
586
  )
386
587
 
387
588
  PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
388
- subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
589
+ # 检查是否需要设置上游分支(空仓库的情况)
590
+ try:
591
+ # 先尝试普通推送
592
+ subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
593
+ except subprocess.CalledProcessError:
594
+ # 如果失败,可能是空仓库,尝试设置上游分支
595
+ try:
596
+ subprocess.run(
597
+ ["git", "push", "-u", "origin", "main"],
598
+ cwd=central_repo_path,
599
+ check=True,
600
+ )
601
+ except subprocess.CalledProcessError:
602
+ # 如果main分支不存在,尝试master分支
603
+ subprocess.run(
604
+ ["git", "push", "-u", "origin", "master"],
605
+ cwd=central_repo_path,
606
+ check=True,
607
+ )
389
608
 
390
609
  PrettyOutput.print("\n方法论已成功分享到中心仓库!", OutputType.SUCCESS)
391
610
  break
@@ -426,6 +645,9 @@ def run_cli(
426
645
  share_methodology: bool = typer.Option(
427
646
  False, "--share-methodology", help="分享本地方法论到中心方法论仓库"
428
647
  ),
648
+ share_tool: bool = typer.Option(
649
+ False, "--share-tool", help="分享本地工具到中心工具仓库"
650
+ ),
429
651
  ) -> None:
430
652
  """Jarvis AI assistant command-line interface."""
431
653
  if ctx.invoked_subcommand is not None:
@@ -439,6 +661,12 @@ def run_cli(
439
661
  _handle_share_methodology(config_file)
440
662
  raise typer.Exit(code=0)
441
663
 
664
+ # 处理工具分享
665
+ if share_tool:
666
+ init_env("", config_file=config_file) # 初始化配置但不显示欢迎信息
667
+ _handle_share_tool(config_file)
668
+ raise typer.Exit(code=0)
669
+
442
670
  init_env(
443
671
  "欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file
444
672
  )
@@ -230,6 +230,11 @@
230
230
  "description": "中心方法论Git仓库地址,该仓库会自动添加到方法论加载路径中",
231
231
  "default": ""
232
232
  },
233
+ "JARVIS_CENTRAL_TOOL_REPO": {
234
+ "type": "string",
235
+ "description": "中心工具库Git仓库地址,该仓库会自动克隆到数据目录并加载其中的工具",
236
+ "default": ""
237
+ },
233
238
  "JARVIS_PRINT_PROMPT": {
234
239
  "type": "boolean",
235
240
  "description": "是否打印提示",
@@ -107,17 +107,13 @@ arguments:
107
107
 
108
108
 
109
109
  class OutputHandlerProtocol(Protocol):
110
- def name(self) -> str:
111
- ...
110
+ def name(self) -> str: ...
112
111
 
113
- def can_handle(self, response: str) -> bool:
114
- ...
112
+ def can_handle(self, response: str) -> bool: ...
115
113
 
116
- def prompt(self) -> str:
117
- ...
114
+ def prompt(self) -> str: ...
118
115
 
119
- def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
120
- ...
116
+ def handle(self, response: str, agent: Any) -> Tuple[bool, Any]: ...
121
117
 
122
118
 
123
119
  class ToolRegistry(OutputHandlerProtocol):
@@ -138,9 +134,7 @@ class ToolRegistry(OutputHandlerProtocol):
138
134
  try:
139
135
  tools_prompt += " <tool>\n"
140
136
  tools_prompt += f" <name>名称: {tool['name']}</name>\n"
141
- tools_prompt += (
142
- f" <description>描述: {tool['description']}</description>\n"
143
- )
137
+ tools_prompt += f" <description>描述: {tool['description']}</description>\n"
144
138
  tools_prompt += " <parameters>\n"
145
139
  tools_prompt += " <yaml>|\n"
146
140
 
@@ -202,15 +196,15 @@ class ToolRegistry(OutputHandlerProtocol):
202
196
  """从数据目录获取工具调用统计"""
203
197
  from jarvis.jarvis_stats.stats import StatsManager
204
198
  from datetime import datetime, timedelta
205
-
199
+
206
200
  # 获取所有工具的统计数据
207
201
  tool_stats = {}
208
202
  tools = self.get_all_tools()
209
-
203
+
210
204
  # 获取所有历史数据(从很早的时间开始)
211
205
  end_time = datetime.now()
212
206
  start_time = datetime(2000, 1, 1) # 使用一个足够早的时间
213
-
207
+
214
208
  for tool in tools:
215
209
  tool_name = tool["name"]
216
210
  # 获取该工具的统计数据
@@ -218,22 +212,22 @@ class ToolRegistry(OutputHandlerProtocol):
218
212
  metric_name=tool_name,
219
213
  start_time=start_time,
220
214
  end_time=end_time,
221
- tags={"group": "tool"}
215
+ tags={"group": "tool"},
222
216
  )
223
-
217
+
224
218
  # 计算总调用次数
225
219
  if stats_data and "records" in stats_data:
226
220
  total_count = sum(record["value"] for record in stats_data["records"])
227
221
  tool_stats[tool_name] = int(total_count)
228
222
  else:
229
223
  tool_stats[tool_name] = 0
230
-
224
+
231
225
  return tool_stats
232
226
 
233
227
  def _update_tool_stats(self, name: str) -> None:
234
228
  """更新工具调用统计"""
235
229
  from jarvis.jarvis_stats.stats import StatsManager
236
-
230
+
237
231
  StatsManager.increment(name, group="tool")
238
232
 
239
233
  def use_tools(self, name: List[str]) -> None:
@@ -292,7 +286,9 @@ class ToolRegistry(OutputHandlerProtocol):
292
286
  config = yaml.safe_load(open(file_path, "r", encoding="utf-8"))
293
287
  self.register_mcp_tool_by_config(config)
294
288
  except Exception as e:
295
- PrettyOutput.print(f"文件 {file_path} 加载失败: {str(e)}", OutputType.WARNING)
289
+ PrettyOutput.print(
290
+ f"文件 {file_path} 加载失败: {str(e)}", OutputType.WARNING
291
+ )
296
292
 
297
293
  def _load_builtin_tools(self) -> None:
298
294
  """从内置工具目录加载工具"""
@@ -308,8 +304,33 @@ class ToolRegistry(OutputHandlerProtocol):
308
304
 
309
305
  def _load_external_tools(self) -> None:
310
306
  """从jarvis_data/tools和配置的目录加载外部工具"""
307
+ from jarvis.jarvis_utils.config import get_central_tool_repo
308
+
311
309
  tool_dirs = [str(Path(get_data_dir()) / "tools")] + get_tool_load_dirs()
312
310
 
311
+ # 如果配置了中心工具仓库,将其添加到加载路径
312
+ central_repo = get_central_tool_repo()
313
+ if central_repo:
314
+ # 中心工具仓库存储在数据目录下的特定位置
315
+ central_repo_path = os.path.join(get_data_dir(), "central_tool_repo")
316
+ tool_dirs.append(central_repo_path)
317
+
318
+ # 确保中心工具仓库被克隆/更新
319
+ if not os.path.exists(central_repo_path):
320
+ try:
321
+ import subprocess
322
+
323
+ PrettyOutput.print(
324
+ f"正在克隆中心工具仓库: {central_repo}", OutputType.INFO
325
+ )
326
+ subprocess.run(
327
+ ["git", "clone", central_repo, central_repo_path], check=True
328
+ )
329
+ except Exception as e:
330
+ PrettyOutput.print(
331
+ f"克隆中心工具仓库失败: {str(e)}", OutputType.ERROR
332
+ )
333
+
313
334
  # --- 全局每日更新检查 ---
314
335
  daily_check_git_updates(tool_dirs, "tools")
315
336
 
@@ -662,6 +683,10 @@ class ToolRegistry(OutputHandlerProtocol):
662
683
  parameters: 工具参数定义
663
684
  func: 工具执行函数
664
685
  """
686
+ if name in self.tools:
687
+ PrettyOutput.print(
688
+ f"警告: 工具 '{name}' 已存在,将被覆盖", OutputType.WARNING
689
+ )
665
690
  self.tools[name] = Tool(name, description, parameters, func)
666
691
 
667
692
  def get_tool(self, name: str) -> Optional[Tool]:
@@ -735,7 +760,9 @@ class ToolRegistry(OutputHandlerProtocol):
735
760
  """
736
761
  if len(output.splitlines()) > 60:
737
762
  lines = output.splitlines()
738
- return "\n".join(lines[:30] + ["\n...内容太长,已截取前后30行...\n"] + lines[-30:])
763
+ return "\n".join(
764
+ lines[:30] + ["\n...内容太长,已截取前后30行...\n"] + lines[-30:]
765
+ )
739
766
  return output
740
767
 
741
768
  def handle_tool_calls(self, tool_call: Dict[str, Any], agent: Any) -> str:
@@ -319,6 +319,16 @@ def get_central_methodology_repo() -> str:
319
319
  return GLOBAL_CONFIG_DATA.get("JARVIS_CENTRAL_METHODOLOGY_REPO", "")
320
320
 
321
321
 
322
+ def get_central_tool_repo() -> str:
323
+ """
324
+ 获取中心工具Git仓库地址。
325
+
326
+ 返回:
327
+ str: 中心工具Git仓库地址,如果未配置则返回空字符串
328
+ """
329
+ return GLOBAL_CONFIG_DATA.get("JARVIS_CENTRAL_TOOL_REPO", "")
330
+
331
+
322
332
  def is_print_prompt() -> bool:
323
333
  """
324
334
  获取是否打印提示。
@@ -330,11 +330,18 @@ def _show_usage_stats() -> None:
330
330
  if total_changes > 0:
331
331
  parts.append(f"代码修改 {total_changes:,} 次")
332
332
  if total_lines_modified > 0:
333
- parts.append(f"代码行数 {total_lines_modified:,} 行")
333
+ parts.append(f"修改代码行数 {total_lines_modified:,} 行")
334
334
 
335
335
  if parts:
336
336
  summary_content.append(f"📈 总计: {', '.join(parts)}")
337
337
 
338
+ # 添加代码采纳率显示
339
+ adoption_metrics = categorized_stats["adoption"]["metrics"]
340
+ if "adoption_rate" in adoption_metrics:
341
+ summary_content.append(
342
+ f"✅ 代码采纳率: {adoption_metrics['adoption_rate']}"
343
+ )
344
+
338
345
  # 计算节省的时间
339
346
  time_saved_seconds = 0
340
347
  tool_stats = categorized_stats["tool"]["metrics"]
@@ -946,6 +953,25 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
946
953
  )
947
954
  before_hash = before_hash_result.stdout.strip()
948
955
 
956
+ # 检查是否是空仓库
957
+ ls_remote_result = subprocess.run(
958
+ ["git", "ls-remote", "--heads", "origin"],
959
+ cwd=repo_path,
960
+ capture_output=True,
961
+ text=True,
962
+ encoding="utf-8",
963
+ errors="replace",
964
+ check=True,
965
+ timeout=10,
966
+ )
967
+
968
+ if not ls_remote_result.stdout.strip():
969
+ PrettyOutput.print(
970
+ f"{repo_type}库 '{repo_path.name}' 的远程仓库是空的,跳过更新。",
971
+ OutputType.INFO,
972
+ )
973
+ return
974
+
949
975
  # 执行 git pull
950
976
  pull_result = subprocess.run(
951
977
  ["git", "pull"],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.2.7
3
+ Version: 0.2.8
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