jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__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 (162) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +1143 -245
  3. jarvis/jarvis_agent/agent_manager.py +97 -0
  4. jarvis/jarvis_agent/builtin_input_handler.py +12 -10
  5. jarvis/jarvis_agent/config_editor.py +57 -0
  6. jarvis/jarvis_agent/edit_file_handler.py +392 -99
  7. jarvis/jarvis_agent/event_bus.py +48 -0
  8. jarvis/jarvis_agent/events.py +157 -0
  9. jarvis/jarvis_agent/file_context_handler.py +79 -0
  10. jarvis/jarvis_agent/file_methodology_manager.py +117 -0
  11. jarvis/jarvis_agent/jarvis.py +1117 -147
  12. jarvis/jarvis_agent/main.py +78 -34
  13. jarvis/jarvis_agent/memory_manager.py +195 -0
  14. jarvis/jarvis_agent/methodology_share_manager.py +174 -0
  15. jarvis/jarvis_agent/prompt_manager.py +82 -0
  16. jarvis/jarvis_agent/prompts.py +46 -9
  17. jarvis/jarvis_agent/protocols.py +4 -1
  18. jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
  19. jarvis/jarvis_agent/run_loop.py +146 -0
  20. jarvis/jarvis_agent/session_manager.py +9 -9
  21. jarvis/jarvis_agent/share_manager.py +228 -0
  22. jarvis/jarvis_agent/shell_input_handler.py +23 -3
  23. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  24. jarvis/jarvis_agent/task_analyzer.py +212 -0
  25. jarvis/jarvis_agent/task_manager.py +154 -0
  26. jarvis/jarvis_agent/task_planner.py +496 -0
  27. jarvis/jarvis_agent/tool_executor.py +8 -4
  28. jarvis/jarvis_agent/tool_share_manager.py +139 -0
  29. jarvis/jarvis_agent/user_interaction.py +42 -0
  30. jarvis/jarvis_agent/utils.py +54 -0
  31. jarvis/jarvis_agent/web_bridge.py +189 -0
  32. jarvis/jarvis_agent/web_output_sink.py +53 -0
  33. jarvis/jarvis_agent/web_server.py +751 -0
  34. jarvis/jarvis_c2rust/__init__.py +26 -0
  35. jarvis/jarvis_c2rust/cli.py +613 -0
  36. jarvis/jarvis_c2rust/collector.py +258 -0
  37. jarvis/jarvis_c2rust/library_replacer.py +1122 -0
  38. jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
  39. jarvis/jarvis_c2rust/optimizer.py +960 -0
  40. jarvis/jarvis_c2rust/scanner.py +1681 -0
  41. jarvis/jarvis_c2rust/transpiler.py +2325 -0
  42. jarvis/jarvis_code_agent/build_validation_config.py +133 -0
  43. jarvis/jarvis_code_agent/code_agent.py +1605 -178
  44. jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
  45. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  46. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  47. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
  48. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
  61. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  62. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  63. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  64. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  65. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  66. jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
  68. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
  69. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
  70. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
  71. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
  72. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
  73. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
  74. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
  75. jarvis/jarvis_code_agent/lint.py +275 -13
  76. jarvis/jarvis_code_agent/utils.py +142 -0
  77. jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
  78. jarvis/jarvis_code_analysis/code_review.py +583 -548
  79. jarvis/jarvis_data/config_schema.json +339 -28
  80. jarvis/jarvis_git_squash/main.py +22 -13
  81. jarvis/jarvis_git_utils/git_commiter.py +171 -55
  82. jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
  83. jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
  84. jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
  85. jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
  86. jarvis/jarvis_methodology/main.py +48 -63
  87. jarvis/jarvis_multi_agent/__init__.py +302 -43
  88. jarvis/jarvis_multi_agent/main.py +70 -24
  89. jarvis/jarvis_platform/ai8.py +40 -23
  90. jarvis/jarvis_platform/base.py +210 -49
  91. jarvis/jarvis_platform/human.py +11 -1
  92. jarvis/jarvis_platform/kimi.py +82 -76
  93. jarvis/jarvis_platform/openai.py +73 -1
  94. jarvis/jarvis_platform/registry.py +8 -15
  95. jarvis/jarvis_platform/tongyi.py +115 -101
  96. jarvis/jarvis_platform/yuanbao.py +89 -63
  97. jarvis/jarvis_platform_manager/main.py +194 -132
  98. jarvis/jarvis_platform_manager/service.py +122 -86
  99. jarvis/jarvis_rag/cli.py +156 -53
  100. jarvis/jarvis_rag/embedding_manager.py +155 -12
  101. jarvis/jarvis_rag/llm_interface.py +10 -13
  102. jarvis/jarvis_rag/query_rewriter.py +63 -12
  103. jarvis/jarvis_rag/rag_pipeline.py +222 -40
  104. jarvis/jarvis_rag/reranker.py +26 -3
  105. jarvis/jarvis_rag/retriever.py +270 -14
  106. jarvis/jarvis_sec/__init__.py +3605 -0
  107. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  108. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  109. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  110. jarvis/jarvis_sec/cli.py +116 -0
  111. jarvis/jarvis_sec/report.py +257 -0
  112. jarvis/jarvis_sec/status.py +264 -0
  113. jarvis/jarvis_sec/types.py +20 -0
  114. jarvis/jarvis_sec/workflow.py +219 -0
  115. jarvis/jarvis_smart_shell/main.py +405 -137
  116. jarvis/jarvis_stats/__init__.py +13 -0
  117. jarvis/jarvis_stats/cli.py +387 -0
  118. jarvis/jarvis_stats/stats.py +711 -0
  119. jarvis/jarvis_stats/storage.py +612 -0
  120. jarvis/jarvis_stats/visualizer.py +282 -0
  121. jarvis/jarvis_tools/ask_user.py +1 -0
  122. jarvis/jarvis_tools/base.py +18 -2
  123. jarvis/jarvis_tools/clear_memory.py +239 -0
  124. jarvis/jarvis_tools/cli/main.py +220 -144
  125. jarvis/jarvis_tools/execute_script.py +52 -12
  126. jarvis/jarvis_tools/file_analyzer.py +17 -12
  127. jarvis/jarvis_tools/generate_new_tool.py +46 -24
  128. jarvis/jarvis_tools/read_code.py +277 -18
  129. jarvis/jarvis_tools/read_symbols.py +141 -0
  130. jarvis/jarvis_tools/read_webpage.py +86 -13
  131. jarvis/jarvis_tools/registry.py +294 -90
  132. jarvis/jarvis_tools/retrieve_memory.py +227 -0
  133. jarvis/jarvis_tools/save_memory.py +194 -0
  134. jarvis/jarvis_tools/search_web.py +62 -28
  135. jarvis/jarvis_tools/sub_agent.py +205 -0
  136. jarvis/jarvis_tools/sub_code_agent.py +217 -0
  137. jarvis/jarvis_tools/virtual_tty.py +330 -62
  138. jarvis/jarvis_utils/builtin_replace_map.py +4 -5
  139. jarvis/jarvis_utils/clipboard.py +90 -0
  140. jarvis/jarvis_utils/config.py +607 -50
  141. jarvis/jarvis_utils/embedding.py +3 -0
  142. jarvis/jarvis_utils/fzf.py +57 -0
  143. jarvis/jarvis_utils/git_utils.py +251 -29
  144. jarvis/jarvis_utils/globals.py +174 -17
  145. jarvis/jarvis_utils/http.py +58 -79
  146. jarvis/jarvis_utils/input.py +899 -153
  147. jarvis/jarvis_utils/methodology.py +210 -83
  148. jarvis/jarvis_utils/output.py +220 -137
  149. jarvis/jarvis_utils/utils.py +1906 -135
  150. jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
  151. jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
  152. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
  153. jarvis/jarvis_git_details/main.py +0 -265
  154. jarvis/jarvis_platform/oyi.py +0 -357
  155. jarvis/jarvis_tools/edit_file.py +0 -255
  156. jarvis/jarvis_tools/rewrite_file.py +0 -195
  157. jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
  158. jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
  159. /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
  160. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
  161. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
  162. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
@@ -1,178 +1,254 @@
1
- import sys
1
+ import json
2
+ from typing import Optional
3
+
4
+ import typer
5
+ from tabulate import tabulate
2
6
 
3
7
  from jarvis.jarvis_tools.registry import ToolRegistry
4
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
5
9
  from jarvis.jarvis_utils.utils import init_env
6
10
 
11
+ app = typer.Typer(help="Jarvis 工具系统命令行界面")
7
12
 
8
- def main() -> int:
9
- """
10
- 命令行工具入口,提供工具列表查看和工具调用功能
11
-
12
- 功能:
13
- 1. 列出所有可用工具 (list命令)
14
- 2. 调用指定工具 (call命令)
15
-
16
- 参数:
17
- 通过命令行参数传递,包括:
18
- - list: 列出工具
19
- --json: 以JSON格式输出
20
- --detailed: 显示详细信息
21
- - call: 调用工具
22
- tool_name: 工具名称
23
- --args: 工具参数(JSON格式)
24
- --args-file: 从文件加载工具参数
25
-
26
- 返回值:
27
- int: 0表示成功,非0表示错误
28
- """
29
- import argparse
30
- import json
31
-
32
- init_env("欢迎使用 Jarvis-Tools,您的工具系统已准备就绪!")
33
-
34
- parser = argparse.ArgumentParser(description="Jarvis 工具系统命令行界面")
35
- subparsers = parser.add_subparsers(dest="command", help="命令")
36
-
37
- # 列出工具子命令
38
- list_parser = subparsers.add_parser("list", help="列出所有可用工具")
39
- list_parser.add_argument("--json", action="store_true", help="以JSON格式输出")
40
- list_parser.add_argument("--detailed", action="store_true", help="显示详细信息")
41
-
42
- # 调用工具子命令
43
- call_parser = subparsers.add_parser("call", help="调用指定工具")
44
- call_parser.add_argument("tool_name", help="要调用的工具名称")
45
- call_parser.add_argument("--args", type=str, help="工具参数 (JSON格式)")
46
- call_parser.add_argument(
47
- "--args-file", type=str, help="从文件加载工具参数 (JSON格式)"
48
- )
49
-
50
- # 统计子命令
51
- stat_parser = subparsers.add_parser("stat", help="显示工具调用统计信息")
52
- stat_parser.add_argument("--json", action="store_true", help="以JSON格式输出")
53
13
 
54
- args = parser.parse_args()
55
14
 
56
- # 初始化工具注册表
15
+ @app.command("list")
16
+ def list_tools(
17
+ as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
18
+ detailed: bool = typer.Option(False, "--detailed", help="显示详细信息"),
19
+ ):
20
+ """列出所有可用工具"""
57
21
  registry = ToolRegistry()
22
+ tools = registry.get_all_tools()
58
23
 
59
- if args.command == "list":
60
- tools = registry.get_all_tools() # 从注册表获取所有工具信息
61
-
62
- if args.json:
63
- if args.detailed:
64
- print(
65
- json.dumps(tools, indent=2, ensure_ascii=False)
66
- ) # 输出完整JSON格式
67
- else:
68
- simple_tools = [
69
- {"name": t["name"], "description": t["description"]} for t in tools
70
- ] # 简化工具信息
71
- print(json.dumps(simple_tools, indent=2, ensure_ascii=False))
24
+ if as_json:
25
+ if detailed:
26
+ PrettyOutput.print(
27
+ json.dumps(tools, indent=2, ensure_ascii=False),
28
+ OutputType.CODE,
29
+ lang="json",
30
+ )
72
31
  else:
73
- PrettyOutput.section("可用工具列表", OutputType.SYSTEM) # 使用美化输出
74
- for tool in tools:
75
- print(f"\n✅ {tool['name']}")
76
- print(f" 描述: {tool['description']}")
77
- if args.detailed:
78
- print(f" 参数:")
79
- print(tool["parameters"]) # 显示详细参数信息
80
-
81
- elif args.command == "stat":
82
- from tabulate import tabulate
83
-
32
+ simple_tools = [
33
+ {"name": t["name"], "description": t["description"]} for t in tools
34
+ ]
35
+ PrettyOutput.print(
36
+ json.dumps(simple_tools, indent=2, ensure_ascii=False),
37
+ OutputType.CODE,
38
+ lang="json",
39
+ )
40
+ else:
41
+ PrettyOutput.section("可用工具列表", OutputType.SYSTEM)
42
+ # 为避免 PrettyOutput 对每行加框造成信息稀疏,先拼接字符串再统一打印
43
+ lines = []
44
+ import json as _json # local import to ensure available
45
+ for tool in tools:
46
+ lines.append(f"\n{tool['name']}")
47
+ lines.append(f" 描述: {tool['description']}")
48
+ if detailed:
49
+ lines.append(" 参数:")
50
+ # 使用 Markdown 代码块统一展示参数
51
+ lines.append("```json")
52
+ try:
53
+ lines.append(_json.dumps(tool["parameters"], ensure_ascii=False, indent=2))
54
+ except Exception:
55
+ lines.append(str(tool.get("parameters")))
56
+ lines.append("```")
57
+ PrettyOutput.print("\n".join(lines), OutputType.INFO, lang="markdown")
58
+
59
+
60
+ @app.command("stat")
61
+ def stat_tools(
62
+ as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
63
+ last_days: Optional[int] = typer.Option(
64
+ None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"
65
+ ),
66
+ format: str = typer.Option(
67
+ "table", "--format", help="显示格式: table, chart, summary"
68
+ ),
69
+ ):
70
+ """显示工具调用统计信息"""
71
+ from jarvis.jarvis_stats.stats import StatsManager
72
+
73
+ if format == "table":
74
+ registry = ToolRegistry()
84
75
  stats = registry._get_tool_stats()
85
76
  tools = registry.get_all_tools()
86
77
 
87
- # 构建统计表格数据
88
78
  table_data = []
89
79
  for tool in tools:
90
80
  name = tool["name"]
91
81
  count = stats.get(name, 0)
92
- table_data.append([name, count])
82
+ if count > 0: # 只显示有调用记录的工具
83
+ table_data.append([name, count])
93
84
 
94
- # 按调用次数降序排序
95
85
  table_data.sort(key=lambda x: x[1], reverse=True)
96
86
 
97
- if args.json:
98
- print(json.dumps(dict(table_data), indent=2))
99
- else:
100
- PrettyOutput.section("工具调用统计", OutputType.SYSTEM)
101
- print(
102
- tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid")
87
+ if as_json:
88
+ PrettyOutput.print(
89
+ json.dumps(dict(table_data), indent=2), OutputType.CODE, lang="json"
103
90
  )
104
-
105
- return 0
106
-
107
- elif args.command == "call":
108
- tool_name = args.tool_name
109
- tool_obj = registry.get_tool(tool_name)
110
-
111
- if not tool_obj:
112
- PrettyOutput.print(f"错误: 工具 '{tool_name}' 不存在", OutputType.ERROR)
113
- available_tools = ", ".join([t["name"] for t in registry.get_all_tools()])
114
- print(f"可用工具: {available_tools}")
115
- return 1
116
-
117
- # 获取参数: 支持从命令行直接传入或从文件加载
118
- tool_args = {}
119
- if args.args:
120
- try:
121
- tool_args = json.loads(args.args) # 解析JSON格式参数
122
- except json.JSONDecodeError:
123
- PrettyOutput.print("错误: 参数必须是有效的JSON格式", OutputType.ERROR)
124
- return 1
125
-
126
- elif args.args_file:
127
- try:
128
- with open(args.args_file, "r", encoding="utf-8") as f:
129
- tool_args = json.load(f) # 从文件加载JSON参数
130
- except (json.JSONDecodeError, FileNotFoundError) as e:
91
+ else:
92
+ time_desc = f"最近{last_days}天" if last_days else "所有历史"
93
+ PrettyOutput.section(f"工具调用统计 ({time_desc})", OutputType.SYSTEM)
94
+ if table_data:
95
+ PrettyOutput.print(
96
+ tabulate(
97
+ table_data, headers=["工具名称", "调用次数"], tablefmt="grid"
98
+ ),
99
+ OutputType.CODE,
100
+ lang="text",
101
+ )
131
102
  PrettyOutput.print(
132
- f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR
103
+ f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用",
104
+ OutputType.INFO,
133
105
  )
134
- return 1
106
+ else:
107
+ PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
108
+ else:
109
+ # 使用 stats 系统的高级功能
110
+ PrettyOutput.section("工具组统计", OutputType.SYSTEM)
111
+ # 显示所有标记为 tool 组的指标
112
+ metrics = StatsManager.list_metrics()
113
+ tool_metrics = []
114
+
115
+ for metric in metrics:
116
+ # 检查是否是工具组的指标
117
+ if last_days:
118
+ stats_data = StatsManager.get_stats(
119
+ metric_name=metric, last_days=last_days, tags={"group": "tool"}
120
+ )
121
+ else:
122
+ # 获取所有历史数据
123
+ from datetime import datetime
124
+
125
+ stats_data = StatsManager.get_stats(
126
+ metric_name=metric,
127
+ start_time=datetime(2000, 1, 1),
128
+ end_time=datetime.now(),
129
+ tags={"group": "tool"},
130
+ )
131
+ if stats_data and stats_data.get("records"):
132
+ tool_metrics.append(metric)
133
+
134
+ if tool_metrics:
135
+ for metric in tool_metrics:
136
+ if format == "chart":
137
+ if last_days:
138
+ StatsManager.plot(
139
+ metric, last_days=last_days, tags={"group": "tool"}
140
+ )
141
+ else:
142
+ from datetime import datetime
143
+
144
+ StatsManager.plot(
145
+ metric,
146
+ start_time=datetime(2000, 1, 1),
147
+ end_time=datetime.now(),
148
+ tags={"group": "tool"},
149
+ )
150
+ elif format == "summary":
151
+ if last_days:
152
+ StatsManager.show(
153
+ metric,
154
+ last_days=last_days,
155
+ format="summary",
156
+ tags={"group": "tool"},
157
+ )
158
+ else:
159
+ from datetime import datetime
160
+
161
+ StatsManager.show(
162
+ metric,
163
+ start_time=datetime(2000, 1, 1),
164
+ end_time=datetime.now(),
165
+ format="summary",
166
+ tags={"group": "tool"},
167
+ )
168
+ else:
169
+ PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
170
+
171
+
172
+ @app.command("call")
173
+ def call_tool(
174
+ tool_name: str = typer.Argument(..., help="要调用的工具名称"),
175
+ args: Optional[str] = typer.Option(None, "--args", help="工具参数 (JSON格式)"),
176
+ args_file: Optional[str] = typer.Option(
177
+ None, "--args-file", help="从文件加载工具参数 (JSON格式)"
178
+ ),
179
+ ):
180
+ """调用指定工具"""
181
+ registry = ToolRegistry()
182
+ tool_obj = registry.get_tool(tool_name)
183
+
184
+ if not tool_obj:
185
+ PrettyOutput.print(f"错误: 工具 '{tool_name}' 不存在", OutputType.ERROR)
186
+ available_tools = ", ".join([t["name"] for t in registry.get_all_tools()])
187
+ PrettyOutput.print(f"可用工具: {available_tools}", OutputType.INFO)
188
+ raise typer.Exit(code=1)
189
+
190
+ tool_args = {}
191
+ if args:
192
+ try:
193
+ tool_args = json.loads(args)
194
+ except json.JSONDecodeError:
195
+ PrettyOutput.print("错误: 参数必须是有效的JSON格式", OutputType.ERROR)
196
+ raise typer.Exit(code=1)
197
+ elif args_file:
198
+ try:
199
+ with open(args_file, "r", encoding="utf-8") as f:
200
+ tool_args = json.load(f)
201
+ except (json.JSONDecodeError, FileNotFoundError) as e:
202
+ PrettyOutput.print(f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR)
203
+ raise typer.Exit(code=1)
204
+
205
+ required_params = tool_obj.parameters.get("required", [])
206
+ missing_params = [p for p in required_params if p not in tool_args]
207
+
208
+ if missing_params:
209
+ # 先拼接提示与参数说明,再统一打印,避免循环中逐条打印
210
+ params = tool_obj.parameters.get("properties", {})
211
+ lines = [
212
+ f"错误: 缺少必需参数: {', '.join(missing_params)}",
213
+ "",
214
+ "参数说明:",
215
+ ]
216
+ for param_name in required_params:
217
+ param_info = params.get(param_name, {})
218
+ desc = param_info.get("description", "无描述")
219
+ lines.append(f" - {param_name}: {desc}")
220
+ PrettyOutput.print("\n".join(lines), OutputType.ERROR)
221
+ raise typer.Exit(code=1)
222
+
223
+ result = registry.execute_tool(tool_name, tool_args)
224
+
225
+ if result["success"]:
226
+ PrettyOutput.section(f"工具 {tool_name} 执行成功", OutputType.SUCCESS)
227
+ else:
228
+ PrettyOutput.section(f"工具 {tool_name} 执行失败", OutputType.ERROR)
135
229
 
136
- # 检查必需参数是否完整
137
- required_params = tool_obj.parameters.get("required", [])
138
- missing_params = [p for p in required_params if p not in tool_args]
230
+ if result.get("stdout"):
231
+ PrettyOutput.print("\n输出:", OutputType.INFO)
232
+ PrettyOutput.print(result["stdout"], OutputType.CODE, lang="text")
139
233
 
140
- if missing_params:
141
- PrettyOutput.print(
142
- f"错误: 缺少必需参数: {', '.join(missing_params)}", OutputType.ERROR
143
- )
144
- print("\n参数说明:")
145
- params = tool_obj.parameters.get("properties", {})
146
- for param_name in required_params:
147
- param_info = params.get(param_name, {})
148
- desc = param_info.get("description", "无描述")
149
- print(f" - {param_name}: {desc}")
150
- return 1
151
-
152
- # 执行工具并处理结果
153
- result = registry.execute_tool(tool_name, tool_args)
154
-
155
- # 显示执行结果
156
- if result["success"]:
157
- PrettyOutput.section(f"工具 {tool_name} 执行成功", OutputType.SUCCESS)
158
- else:
159
- PrettyOutput.section(f"工具 {tool_name} 执行失败", OutputType.ERROR)
234
+ if result.get("stderr"):
235
+ PrettyOutput.print("\n错误:", OutputType.ERROR)
236
+ PrettyOutput.print(result["stderr"], OutputType.ERROR, lang="text")
160
237
 
161
- if result.get("stdout"):
162
- print("\n输出:")
163
- print(result["stdout"])
238
+ if not result["success"]:
239
+ raise typer.Exit(code=1)
164
240
 
165
- if result.get("stderr"):
166
- PrettyOutput.print("\n错误:", OutputType.ERROR)
167
- print(result["stderr"])
168
241
 
169
- return 0 if result["success"] else 1
242
+ def cli():
243
+ """Typer application entry point"""
244
+ init_env("欢迎使用 Jarvis-Tools,您的工具系统已准备就绪!")
245
+ app()
170
246
 
171
- else:
172
- parser.print_help()
173
247
 
174
- return 0
248
+ def main():
249
+ """Main entry point for the script"""
250
+ cli()
175
251
 
176
252
 
177
253
  if __name__ == "__main__":
178
- sys.exit(main())
254
+ main()
@@ -2,7 +2,7 @@
2
2
  import os
3
3
  import tempfile
4
4
  from pathlib import Path
5
- from typing import Any, Dict
5
+ from typing import Any, Dict, List
6
6
 
7
7
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
8
 
@@ -19,6 +19,7 @@ class ScriptTool:
19
19
  + "注意:由于模型上下文长度限制,请避免在脚本中输出大量信息,应该使用rg过滤输出。"
20
20
  + "与virtual_tty不同,此工具会创建一个临时的脚本文件,并使用脚本命令执行脚本,不具备交互式操作的能力,"
21
21
  + "适用于需要执行脚本并获取结果的场景。不适合需要交互式操作的场景(如:ssh连接、sftp传输、gdb/dlv调试等)。"
22
+ + "在非交互模式(JARVIS_NON_INTERACTIVE=true)下:脚本执行时间限制为5分钟,超时将被终止并返回超时信息;用户无法与命令交互,请不要执行需要交互的命令。"
22
23
  )
23
24
  parameters = {
24
25
  "type": "object",
@@ -74,8 +75,7 @@ class ScriptTool:
74
75
  stream.feed(data)
75
76
 
76
77
  # 清理每行右侧空格,并过滤空行
77
- cleaned = []
78
- cleaned = []
78
+ cleaned: List[str] = []
79
79
  for y in range(screen.lines):
80
80
  line = screen.buffer[y]
81
81
  stripped = "".join(char.data for char in line.values()).rstrip()
@@ -116,8 +116,37 @@ class ScriptTool:
116
116
  f"script -q -c '{interpreter} {script_path}' {output_file}"
117
117
  )
118
118
 
119
- # Execute command and capture return code
120
- os.system(tee_command)
119
+ # Execute command with optional timeout in non-interactive mode
120
+ from jarvis.jarvis_utils.config import get_script_execution_timeout, is_non_interactive
121
+ import subprocess
122
+
123
+ timed_out = False
124
+ if is_non_interactive():
125
+ try:
126
+ proc = subprocess.Popen(tee_command, shell=True)
127
+ try:
128
+ proc.wait(timeout=get_script_execution_timeout())
129
+ except subprocess.TimeoutExpired:
130
+ timed_out = True
131
+ try:
132
+ proc.kill()
133
+ except Exception:
134
+ pass
135
+ except Exception as e:
136
+ PrettyOutput.print(str(e), OutputType.ERROR)
137
+ # Attempt to read any partial output if available
138
+ try:
139
+ output = self.get_display_output(output_file)
140
+ except Exception as ee:
141
+ output = f"读取输出文件失败: {str(ee)}"
142
+ return {
143
+ "success": False,
144
+ "stdout": output,
145
+ "stderr": f"执行脚本失败: {str(e)}",
146
+ }
147
+ else:
148
+ # Execute command and capture return code
149
+ os.system(tee_command)
121
150
 
122
151
  # Read and process output file
123
152
  try:
@@ -126,12 +155,19 @@ class ScriptTool:
126
155
  except Exception as e:
127
156
  output = f"读取输出文件失败: {str(e)}"
128
157
 
129
- # Return successful result
130
- return {
131
- "success": True,
132
- "stdout": output,
133
- "stderr": "",
134
- }
158
+ # Return result (handle timeout in non-interactive mode)
159
+ if is_non_interactive() and timed_out:
160
+ return {
161
+ "success": False,
162
+ "stdout": output,
163
+ "stderr": f"执行超时(超过{get_script_execution_timeout()}秒),进程已被终止(非交互模式)。",
164
+ }
165
+ else:
166
+ return {
167
+ "success": True,
168
+ "stdout": output,
169
+ "stderr": "",
170
+ }
135
171
 
136
172
  finally:
137
173
  # Clean up temporary files
@@ -173,4 +209,8 @@ class ScriptTool:
173
209
 
174
210
  if __name__ == "__main__":
175
211
  script_tool = ScriptTool()
176
- print(script_tool.get_display_output("/home/wangmaobin/code/Jarvis/a.txt"))
212
+ PrettyOutput.print(
213
+ script_tool.get_display_output("/path/to/a.txt"),
214
+ OutputType.CODE,
215
+ lang="text",
216
+ )
@@ -30,7 +30,7 @@ class FileAnalyzerTool:
30
30
 
31
31
  @staticmethod
32
32
  def check() -> bool:
33
- return PlatformRegistry().get_thinking_platform().support_upload_files()
33
+ return PlatformRegistry().get_normal_platform().support_upload_files()
34
34
 
35
35
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
36
36
  """执行文件分析操作
@@ -45,25 +45,31 @@ class FileAnalyzerTool:
45
45
  file_paths = args["file_paths"]
46
46
  prompt = args["prompt"]
47
47
 
48
- # 验证文件路径
48
+ # 验证文件路径(先收集不存在的文件,统一打印一次)
49
49
  valid_files = []
50
+ missing_files = []
50
51
  for file_path in file_paths:
51
52
  if os.path.exists(file_path):
52
53
  valid_files.append(file_path)
53
54
  else:
54
- PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
55
+ missing_files.append(file_path)
56
+ if missing_files:
57
+ PrettyOutput.print(
58
+ "以下文件不存在:\n" + "\n".join(f" - {p}" for p in missing_files),
59
+ OutputType.WARNING,
60
+ )
55
61
 
56
62
  if not valid_files:
57
63
  return {"success": False, "stdout": "", "stderr": "没有找到有效的文件"}
58
64
 
59
- # 创建thinking平台实例
60
- platform = PlatformRegistry().get_thinking_platform()
65
+ # 创建平台实例
66
+ platform = PlatformRegistry().get_normal_platform()
61
67
 
62
68
  if not platform:
63
69
  return {
64
70
  "success": False,
65
71
  "stdout": "",
66
- "stderr": "无法创建thinking平台实例",
72
+ "stderr": "无法创建平台实例",
67
73
  }
68
74
 
69
75
  # 设置系统消息
@@ -73,19 +79,19 @@ class FileAnalyzerTool:
73
79
  platform.set_system_prompt(system_message)
74
80
 
75
81
  # 上传文件
76
- print(f"📤 正在上传文件...")
82
+
77
83
  try:
78
84
  upload_result = platform.upload_files(valid_files)
79
85
  if not upload_result:
80
- print(f"文件上传失败")
86
+ PrettyOutput.print("文件上传失败", OutputType.ERROR)
81
87
  return {
82
88
  "success": False,
83
89
  "stdout": "",
84
90
  "stderr": "文件上传失败",
85
91
  }
86
- print(f"✅ 文件上传成功")
92
+
87
93
  except Exception as e:
88
- print(f"文件上传失败: {str(e)}")
94
+ PrettyOutput.print(f"文件上传失败: {str(e)}", OutputType.ERROR)
89
95
  return {
90
96
  "success": False,
91
97
  "stdout": "",
@@ -102,9 +108,8 @@ class FileAnalyzerTool:
102
108
  请提供详细的分析结果和理由。"""
103
109
 
104
110
  # 发送请求并获取分析结果
105
- print(f"🔍 正在分析文件...")
111
+
106
112
  analysis_result = platform.chat_until_success(analysis_request)
107
- print(f"✅ 分析完成")
108
113
 
109
114
  # 清理会话
110
115
  platform.delete_chat()