jarvis-ai-assistant 0.2.3__py3-none-any.whl → 0.2.5__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 (33) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +13 -7
  3. jarvis/jarvis_agent/edit_file_handler.py +4 -0
  4. jarvis/jarvis_agent/jarvis.py +22 -25
  5. jarvis/jarvis_agent/main.py +6 -6
  6. jarvis/jarvis_code_agent/code_agent.py +273 -11
  7. jarvis/jarvis_code_analysis/code_review.py +21 -19
  8. jarvis/jarvis_data/config_schema.json +25 -29
  9. jarvis/jarvis_git_squash/main.py +3 -3
  10. jarvis/jarvis_git_utils/git_commiter.py +32 -11
  11. jarvis/jarvis_mcp/sse_mcp_client.py +4 -6
  12. jarvis/jarvis_mcp/streamable_mcp_client.py +5 -9
  13. jarvis/jarvis_rag/retriever.py +1 -1
  14. jarvis/jarvis_smart_shell/main.py +2 -2
  15. jarvis/jarvis_stats/__init__.py +13 -0
  16. jarvis/jarvis_stats/cli.py +404 -0
  17. jarvis/jarvis_stats/stats.py +538 -0
  18. jarvis/jarvis_stats/storage.py +381 -0
  19. jarvis/jarvis_stats/visualizer.py +282 -0
  20. jarvis/jarvis_tools/cli/main.py +82 -15
  21. jarvis/jarvis_tools/registry.py +32 -16
  22. jarvis/jarvis_tools/search_web.py +3 -3
  23. jarvis/jarvis_tools/virtual_tty.py +315 -26
  24. jarvis/jarvis_utils/config.py +12 -8
  25. jarvis/jarvis_utils/git_utils.py +8 -16
  26. jarvis/jarvis_utils/methodology.py +74 -67
  27. jarvis/jarvis_utils/utils.py +468 -72
  28. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/METADATA +29 -3
  29. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/RECORD +33 -28
  30. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/entry_points.txt +2 -0
  31. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/WHEEL +0 -0
  32. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/licenses/LICENSE +0 -0
  33. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/top_level.txt +0 -0
@@ -17,7 +17,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
17
17
  from jarvis.jarvis_utils.tag import ct, ot
18
18
  from jarvis.jarvis_utils.utils import init_env, is_context_overflow
19
19
 
20
- app = typer.Typer(help="Autonomous code review tool")
20
+ app = typer.Typer(help="自动代码审查工具")
21
21
 
22
22
 
23
23
  class CodeReviewTool:
@@ -300,7 +300,7 @@ class CodeReviewTool:
300
300
 
301
301
  # Execute git command and get diff output
302
302
  diff_output = subprocess.check_output(
303
- diff_cmd, shell=True, text=True
303
+ diff_cmd, shell=True, text=True, encoding="utf-8", errors="replace"
304
304
  )
305
305
  if not diff_output:
306
306
  return {
@@ -310,11 +310,13 @@ class CodeReviewTool:
310
310
  }
311
311
 
312
312
  # Extract changed files using git command
313
- files_cmd = f"git show --name-only --pretty=format: {commit_sha} | grep -v '^$'"
313
+ # Use git show with proper formatting to avoid needing grep
314
+ files_cmd = f"git show --name-only --pretty=format: {commit_sha}"
314
315
  try:
315
316
  files_output = subprocess.check_output(
316
317
  files_cmd, shell=True, text=True
317
318
  )
319
+ # Filter out empty lines without using grep
318
320
  file_paths = [
319
321
  f.strip() for f in files_output.split("\n") if f.strip()
320
322
  ]
@@ -337,7 +339,7 @@ class CodeReviewTool:
337
339
 
338
340
  # Execute git command and get diff output
339
341
  diff_output = subprocess.check_output(
340
- diff_cmd, shell=True, text=True
342
+ diff_cmd, shell=True, text=True, encoding="utf-8", errors="replace"
341
343
  )
342
344
  if not diff_output:
343
345
  return {
@@ -379,7 +381,7 @@ class CodeReviewTool:
379
381
 
380
382
  # Execute git command and get diff output
381
383
  diff_output = subprocess.check_output(
382
- diff_cmd, shell=True, text=True
384
+ diff_cmd, shell=True, text=True, encoding="utf-8", errors="replace"
383
385
  )
384
386
  if not diff_output:
385
387
  return {
@@ -578,7 +580,7 @@ class CodeReviewTool:
578
580
 
579
581
  tool_registry = ToolRegistry()
580
582
  tool_registry.dont_use_tools(["code_review"])
581
-
583
+
582
584
  # Use the provided agent's model_group or get it from globals
583
585
  calling_agent = agent or get_agent(current_agent_name)
584
586
  model_group = None
@@ -653,7 +655,7 @@ class CodeReviewTool:
653
655
  {ot("REPORT")}
654
656
  [在此处插入完整MARKDOWN格式的审查报告]
655
657
  {ct("REPORT")}""",
656
- output_handler=[tool_registry],
658
+ output_handler=[tool_registry], # type: ignore
657
659
  llm_type="thinking",
658
660
  auto_complete=False,
659
661
  )
@@ -769,10 +771,10 @@ def extract_code_report(result: str) -> str:
769
771
 
770
772
  @app.command("commit")
771
773
  def review_commit(
772
- commit: str = typer.Argument(..., help="Commit SHA to review"),
773
- root_dir: str = typer.Option(".", "--root-dir", help="Root directory of the codebase"),
774
+ commit: str = typer.Argument(..., help="要审查的提交SHA"),
775
+ root_dir: str = typer.Option(".", "--root-dir", help="代码库根目录路径"),
774
776
  ):
775
- """Review specific commit"""
777
+ """审查指定的提交"""
776
778
  tool = CodeReviewTool()
777
779
  tool_args = {"review_type": "commit", "commit_sha": commit, "root_dir": root_dir}
778
780
  result = tool.execute(tool_args)
@@ -786,9 +788,9 @@ def review_commit(
786
788
 
787
789
  @app.command("current")
788
790
  def review_current(
789
- root_dir: str = typer.Option(".", "--root-dir", help="Root directory of the codebase"),
791
+ root_dir: str = typer.Option(".", "--root-dir", help="代码库根目录路径"),
790
792
  ):
791
- """Review current changes"""
793
+ """审查当前的变更"""
792
794
  tool = CodeReviewTool()
793
795
  tool_args = {"review_type": "current", "root_dir": root_dir}
794
796
  result = tool.execute(tool_args)
@@ -802,11 +804,11 @@ def review_current(
802
804
 
803
805
  @app.command("range")
804
806
  def review_range(
805
- start_commit: str = typer.Argument(..., help="Start commit SHA"),
806
- end_commit: str = typer.Argument(..., help="End commit SHA"),
807
- root_dir: str = typer.Option(".", "--root-dir", help="Root directory of the codebase"),
807
+ start_commit: str = typer.Argument(..., help="起始提交SHA"),
808
+ end_commit: str = typer.Argument(..., help="结束提交SHA"),
809
+ root_dir: str = typer.Option(".", "--root-dir", help="代码库根目录路径"),
808
810
  ):
809
- """Review commit range"""
811
+ """审查提交范围"""
810
812
  tool = CodeReviewTool()
811
813
  tool_args = {
812
814
  "review_type": "range",
@@ -825,10 +827,10 @@ def review_range(
825
827
 
826
828
  @app.command("file")
827
829
  def review_file(
828
- file: str = typer.Argument(..., help="File path to review"),
829
- root_dir: str = typer.Option(".", "--root-dir", help="Root directory of the codebase"),
830
+ file: str = typer.Argument(..., help="要审查的文件路径"),
831
+ root_dir: str = typer.Option(".", "--root-dir", help="代码库根目录路径"),
830
832
  ):
831
- """Review specific file"""
833
+ """审查指定的文件"""
832
834
  tool = CodeReviewTool()
833
835
  tool_args = {"review_type": "file", "file_path": file, "root_dir": root_dir}
834
836
  result = tool.execute(tool_args)
@@ -111,14 +111,9 @@
111
111
  "description": "Git提交信息生成提示模板",
112
112
  "default": ""
113
113
  },
114
- "JARVIS_MAX_TOKEN_COUNT": {
115
- "type": "number",
116
- "description": "模型能处理的最大token数量",
117
- "default": 960000
118
- },
119
114
  "JARVIS_MAX_INPUT_TOKEN_COUNT": {
120
115
  "type": "number",
121
- "description": "模型能处理的最大输入token数量",
116
+ "description": "模型能处理的最大输入token数量。其他token限制基于此值计算:最大token数量=此值×100,最大大内容尺寸=此值×5",
122
117
  "default": 32000
123
118
  },
124
119
  "JARVIS_PLATFORM": {
@@ -139,7 +134,7 @@
139
134
  "JARVIS_THINKING_MODEL": {
140
135
  "type": "string",
141
136
  "description": "思考操作模型名称",
142
- "default": "deep_seek"
137
+ "default": "deep_seek_v3"
143
138
  },
144
139
  "JARVIS_LLM_GROUP": {
145
140
  "type": "string",
@@ -169,19 +164,11 @@
169
164
  },
170
165
  "JARVIS_THINKING_MODEL": {
171
166
  "type": "string",
172
- "default": "deep_seek"
173
- },
174
- "JARVIS_MAX_TOKEN_COUNT": {
175
- "type": "number",
176
- "default": 960000
167
+ "default": "deep_seek_v3"
177
168
  },
178
169
  "JARVIS_MAX_INPUT_TOKEN_COUNT": {
179
170
  "type": "number",
180
171
  "default": 32000
181
- },
182
- "JARVIS_MAX_BIG_CONTENT_SIZE": {
183
- "type": "number",
184
- "default": 160000
185
172
  }
186
173
  },
187
174
  "required": [
@@ -206,11 +193,7 @@
206
193
  "description": "Jarvis数据存储目录路径",
207
194
  "default": "~/.jarvis"
208
195
  },
209
- "JARVIS_MAX_BIG_CONTENT_SIZE": {
210
- "type": "number",
211
- "description": "最大大内容尺寸",
212
- "default": 160000
213
- },
196
+
214
197
  "JARVIS_PRETTY_OUTPUT": {
215
198
  "type": "boolean",
216
199
  "description": "是否启用美化输出",
@@ -234,6 +217,14 @@
234
217
  },
235
218
  "default": []
236
219
  },
220
+ "JARVIS_METHODOLOGY_DIRS": {
221
+ "type": "array",
222
+ "description": "方法论加载目录",
223
+ "items": {
224
+ "type": "string"
225
+ },
226
+ "default": []
227
+ },
237
228
  "JARVIS_PRINT_PROMPT": {
238
229
  "type": "boolean",
239
230
  "description": "是否打印提示",
@@ -260,11 +251,11 @@
260
251
  "properties": {
261
252
  "embedding_model": {
262
253
  "type": "string",
263
- "default": "BAAI/bge-base-zh-v1.5"
254
+ "default": "BAAI/bge-m3"
264
255
  },
265
256
  "rerank_model": {
266
257
  "type": "string",
267
- "default": "BAAI/bge-reranker-base"
258
+ "default": "BAAI/bge-reranker-v2-m3"
268
259
  },
269
260
  "use_bm25": {
270
261
  "type": "boolean",
@@ -284,13 +275,13 @@
284
275
  "properties": {
285
276
  "embedding_model": {
286
277
  "type": "string",
287
- "default": "BAAI/bge-base-zh-v1.5",
288
- "description": "用于RAG的嵌入模型的名称, 默认为 'BAAI/bge-base-zh-v1.5'"
278
+ "default": "BAAI/bge-m3",
279
+ "description": "用于RAG的嵌入模型的名称, 默认为 'BAAI/bge-m3'"
289
280
  },
290
281
  "rerank_model": {
291
282
  "type": "string",
292
- "default": "BAAI/bge-reranker-base",
293
- "description": "用于RAG的rerank模型的名称, 默认为 'BAAI/bge-reranker-base'"
283
+ "default": "BAAI/bge-reranker-v2-m3",
284
+ "description": "用于RAG的rerank模型的名称, 默认为 'BAAI/bge-reranker-v2-m3'"
294
285
  },
295
286
  "use_bm25": {
296
287
  "type": "boolean",
@@ -304,8 +295,8 @@
304
295
  }
305
296
  },
306
297
  "default": {
307
- "embedding_model": "BAAI/bge-base-zh-v1.5",
308
- "rerank_model": "BAAI/bge-reranker-base",
298
+ "embedding_model": "BAAI/bge-m3",
299
+ "rerank_model": "BAAI/bge-reranker-v2-m3",
309
300
  "use_bm25": true,
310
301
  "use_rerank": true
311
302
  }
@@ -361,6 +352,11 @@
361
352
  "OYI_API_KEY": {
362
353
  "type": "string",
363
354
  "description": "Oyi API Key"
355
+ },
356
+ "SHELL": {
357
+ "type": "string",
358
+ "description": "系统Shell路径,用于获取当前使用的shell类型",
359
+ "default": "/bin/bash"
364
360
  }
365
361
  },
366
362
  "additionalProperties": true
@@ -9,7 +9,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
  from jarvis.jarvis_utils.utils import init_env
10
10
  from jarvis.jarvis_utils.input import user_confirm
11
11
 
12
- app = typer.Typer(help="Git squash tool")
12
+ app = typer.Typer(help="Git压缩工具")
13
13
 
14
14
 
15
15
  class GitSquashTool:
@@ -53,8 +53,8 @@ class GitSquashTool:
53
53
 
54
54
  @app.command()
55
55
  def cli(
56
- commit_hash: str = typer.Argument(..., help="Base commit hash to squash from"),
57
- lang: str = typer.Option("Chinese", "--lang", help="Language for commit messages"),
56
+ commit_hash: str = typer.Argument(..., help="要压缩的基础提交哈希"),
57
+ lang: str = typer.Option("Chinese", "--lang", help="提交信息的语言"),
58
58
  ):
59
59
  init_env("欢迎使用 Jarvis-GitSquash,您的Git压缩助手已准备就绪!")
60
60
  tool = GitSquashTool()
@@ -21,7 +21,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
21
21
  from jarvis.jarvis_utils.tag import ct, ot
22
22
  from jarvis.jarvis_utils.utils import init_env, is_context_overflow
23
23
 
24
- app = typer.Typer(help="Git commit tool")
24
+ app = typer.Typer(help="Git提交工具")
25
25
 
26
26
 
27
27
  class GitCommitTool:
@@ -258,17 +258,38 @@ commit信息
258
258
 
259
259
  # 执行提交
260
260
  print("⚙️ 正在准备提交...")
261
- with tempfile.NamedTemporaryFile(mode="w", delete=True) as tmp_file:
261
+ # Windows 兼容性:使用 delete=False 避免权限错误
262
+ tmp_file = tempfile.NamedTemporaryFile(mode="w", delete=False)
263
+ tmp_file_path = tmp_file.name
264
+ try:
262
265
  tmp_file.write(commit_message)
263
- tmp_file.flush()
266
+ tmp_file.close() # Windows 需要先关闭文件才能被其他进程读取
267
+
264
268
  print("💾 正在执行提交...")
265
- commit_cmd = ["git", "commit", "-F", tmp_file.name]
266
- subprocess.Popen(
269
+ commit_cmd = ["git", "commit", "-F", tmp_file_path]
270
+ process = subprocess.Popen(
267
271
  commit_cmd,
268
- stdout=subprocess.DEVNULL,
269
- stderr=subprocess.DEVNULL,
270
- ).wait()
272
+ stdout=subprocess.PIPE,
273
+ stderr=subprocess.PIPE,
274
+ text=True,
275
+ )
276
+ stdout, stderr = process.communicate()
277
+
278
+ if process.returncode != 0:
279
+ # 如果提交失败,重置暂存区
280
+ subprocess.run(["git", "reset", "HEAD"], check=False)
281
+ error_msg = (
282
+ stderr.strip() if stderr else "Unknown git commit error"
283
+ )
284
+ raise Exception(f"Git commit failed: {error_msg}")
285
+
271
286
  print("✅ 提交")
287
+ finally:
288
+ # 手动删除临时文件
289
+ try:
290
+ os.unlink(tmp_file_path)
291
+ except Exception:
292
+ pass
272
293
 
273
294
  commit_hash = self._get_last_commit_hash()
274
295
  print("✅ 完成提交")
@@ -310,17 +331,17 @@ commit信息
310
331
  @app.command()
311
332
  def cli(
312
333
  root_dir: str = typer.Option(
313
- ".", "--root-dir", help="Root directory of the Git repository"
334
+ ".", "--root-dir", help="Git仓库的根目录路径"
314
335
  ),
315
336
  prefix: str = typer.Option(
316
337
  "",
317
338
  "--prefix",
318
- help="Prefix to prepend to commit message (separated by space)",
339
+ help="提交信息前缀(用空格分隔)",
319
340
  ),
320
341
  suffix: str = typer.Option(
321
342
  "",
322
343
  "--suffix",
323
- help="Suffix to append to commit message (separated by newline)",
344
+ help="提交信息后缀(用换行分隔)",
324
345
  ),
325
346
  ):
326
347
  init_env("欢迎使用 Jarvis-GitCommitTool,您的Git提交助手已准备就绪!")
@@ -50,9 +50,9 @@ class SSEMcpClient(McpClient):
50
50
  self.sse_thread: Optional[threading.Thread] = None
51
51
  self.messages_endpoint: Optional[str] = None
52
52
  self.session_id: Optional[str] = None
53
- self.pending_requests = {} # 存储等待响应的请求 {id: Event}
54
- self.request_results = {} # 存储请求结果 {id: result}
55
- self.notification_handlers = {}
53
+ self.pending_requests: Dict[str, threading.Event] = {} # 存储等待响应的请求 {id: Event}
54
+ self.request_results: Dict[str, Dict[str, Any]] = {} # 存储请求结果 {id: result}
55
+ self.notification_handlers: Dict[str, List[Callable]] = {}
56
56
  self.event_lock = threading.Lock()
57
57
  self.request_id_counter = 0
58
58
 
@@ -95,9 +95,7 @@ class SSEMcpClient(McpClient):
95
95
 
96
96
  # 验证服务器响应
97
97
  if "result" not in response:
98
- raise RuntimeError(
99
- f"初始化失败: {response.get('error', 'Unknown error')}"
100
- )
98
+ raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
101
99
 
102
100
  # 发送initialized通知
103
101
  self._send_notification("notifications/initialized", {})
@@ -45,9 +45,9 @@ class StreamableMcpClient(McpClient):
45
45
  self.session.headers.update(extra_headers)
46
46
 
47
47
  # 请求相关属性
48
- self.pending_requests = {} # 存储等待响应的请求 {id: Event}
49
- self.request_results = {} # 存储请求结果 {id: result}
50
- self.notification_handlers = {}
48
+ self.pending_requests: Dict[str, threading.Event] = {} # 存储等待响应的请求 {id: Event}
49
+ self.request_results: Dict[str, Dict[str, Any]] = {} # 存储请求结果 {id: result}
50
+ self.notification_handlers: Dict[str, List[Callable]] = {}
51
51
  self.event_lock = threading.Lock()
52
52
  self.request_id_counter = 0
53
53
 
@@ -70,9 +70,7 @@ class StreamableMcpClient(McpClient):
70
70
 
71
71
  # 验证服务器响应
72
72
  if "result" not in response:
73
- raise RuntimeError(
74
- f"初始化失败: {response.get('error', 'Unknown error')}"
75
- )
73
+ raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
76
74
 
77
75
  # 发送initialized通知
78
76
  self._send_notification("notifications/initialized", {})
@@ -143,9 +141,7 @@ class StreamableMcpClient(McpClient):
143
141
 
144
142
  # 发送请求到Streamable HTTP端点
145
143
  mcp_url = urljoin(self.base_url, "mcp")
146
- response = self.session.post(
147
- mcp_url, json=request, stream=True # 启用流式传输
148
- )
144
+ response = self.session.post(mcp_url, json=request, stream=True) # 启用流式传输
149
145
  response.raise_for_status()
150
146
 
151
147
  # 处理流式响应
@@ -144,7 +144,7 @@ class ChromaRetriever:
144
144
  ]
145
145
 
146
146
  # 按分数排序并取最高结果
147
- bm25_results_with_docs.sort(key=lambda x: x[2], reverse=True)
147
+ bm25_results_with_docs.sort(key=lambda x: x[2], reverse=True) # type: ignore
148
148
 
149
149
  for doc_text, metadata, _ in bm25_results_with_docs[: n_results * 2]:
150
150
  bm25_docs.append(Document(page_content=doc_text, metadata=metadata))
@@ -170,8 +170,8 @@ def process_request(request: str) -> Optional[str]:
170
170
  4. 多个命令用&&连接
171
171
 
172
172
  # 示例
173
- 输入: "查找Python文件"
174
- 输出: find . -name "*.py"
173
+ 输入: "显示当前目录内容"
174
+ 输出: ls -la
175
175
  """
176
176
  model.set_system_prompt(system_message)
177
177
 
@@ -0,0 +1,13 @@
1
+ """
2
+ Jarvis统计模块
3
+
4
+ 提供指标统计、数据持久化、可视化展示等功能
5
+ """
6
+
7
+ from jarvis.jarvis_stats.stats import StatsManager
8
+ from jarvis.jarvis_stats.storage import StatsStorage
9
+ from jarvis.jarvis_stats.visualizer import StatsVisualizer
10
+
11
+ __all__ = ["StatsManager", "StatsStorage", "StatsVisualizer"]
12
+
13
+ __version__ = "1.0.0"