jarvis-ai-assistant 0.1.132__py3-none-any.whl → 0.1.138__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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

Files changed (82) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +330 -347
  3. jarvis/jarvis_agent/builtin_input_handler.py +16 -6
  4. jarvis/jarvis_agent/file_input_handler.py +9 -9
  5. jarvis/jarvis_agent/jarvis.py +143 -0
  6. jarvis/jarvis_agent/main.py +12 -13
  7. jarvis/jarvis_agent/output_handler.py +3 -3
  8. jarvis/jarvis_agent/patch.py +92 -64
  9. jarvis/jarvis_agent/shell_input_handler.py +5 -3
  10. jarvis/jarvis_code_agent/code_agent.py +263 -177
  11. jarvis/jarvis_code_agent/file_select.py +24 -24
  12. jarvis/jarvis_dev/main.py +45 -59
  13. jarvis/jarvis_git_details/__init__.py +0 -0
  14. jarvis/jarvis_git_details/main.py +179 -0
  15. jarvis/jarvis_git_squash/main.py +7 -7
  16. jarvis/jarvis_lsp/base.py +11 -53
  17. jarvis/jarvis_lsp/cpp.py +13 -28
  18. jarvis/jarvis_lsp/go.py +13 -28
  19. jarvis/jarvis_lsp/python.py +8 -27
  20. jarvis/jarvis_lsp/registry.py +21 -83
  21. jarvis/jarvis_lsp/rust.py +15 -30
  22. jarvis/jarvis_methodology/main.py +101 -0
  23. jarvis/jarvis_multi_agent/__init__.py +10 -51
  24. jarvis/jarvis_multi_agent/main.py +43 -0
  25. jarvis/jarvis_platform/__init__.py +1 -1
  26. jarvis/jarvis_platform/ai8.py +67 -89
  27. jarvis/jarvis_platform/base.py +14 -13
  28. jarvis/jarvis_platform/kimi.py +25 -28
  29. jarvis/jarvis_platform/ollama.py +24 -26
  30. jarvis/jarvis_platform/openai.py +15 -19
  31. jarvis/jarvis_platform/oyi.py +48 -50
  32. jarvis/jarvis_platform/registry.py +29 -44
  33. jarvis/jarvis_platform/yuanbao.py +39 -43
  34. jarvis/jarvis_platform_manager/main.py +81 -81
  35. jarvis/jarvis_platform_manager/openai_test.py +21 -21
  36. jarvis/jarvis_rag/file_processors.py +18 -18
  37. jarvis/jarvis_rag/main.py +262 -278
  38. jarvis/jarvis_smart_shell/main.py +12 -12
  39. jarvis/jarvis_tools/ask_codebase.py +85 -78
  40. jarvis/jarvis_tools/ask_user.py +8 -8
  41. jarvis/jarvis_tools/base.py +4 -4
  42. jarvis/jarvis_tools/chdir.py +9 -9
  43. jarvis/jarvis_tools/code_review.py +40 -21
  44. jarvis/jarvis_tools/create_code_agent.py +15 -15
  45. jarvis/jarvis_tools/create_sub_agent.py +0 -1
  46. jarvis/jarvis_tools/execute_python_script.py +3 -3
  47. jarvis/jarvis_tools/execute_shell.py +11 -11
  48. jarvis/jarvis_tools/execute_shell_script.py +3 -3
  49. jarvis/jarvis_tools/file_analyzer.py +116 -105
  50. jarvis/jarvis_tools/file_operation.py +22 -20
  51. jarvis/jarvis_tools/find_caller.py +105 -40
  52. jarvis/jarvis_tools/find_methodolopy.py +65 -0
  53. jarvis/jarvis_tools/find_symbol.py +123 -39
  54. jarvis/jarvis_tools/function_analyzer.py +140 -57
  55. jarvis/jarvis_tools/git_commiter.py +10 -10
  56. jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
  57. jarvis/jarvis_tools/methodology.py +22 -67
  58. jarvis/jarvis_tools/project_analyzer.py +137 -53
  59. jarvis/jarvis_tools/rag.py +15 -20
  60. jarvis/jarvis_tools/read_code.py +25 -23
  61. jarvis/jarvis_tools/read_webpage.py +31 -31
  62. jarvis/jarvis_tools/registry.py +72 -52
  63. jarvis/jarvis_tools/search_web.py +23 -353
  64. jarvis/jarvis_tools/tool_generator.py +19 -19
  65. jarvis/jarvis_utils/config.py +36 -96
  66. jarvis/jarvis_utils/embedding.py +83 -83
  67. jarvis/jarvis_utils/git_utils.py +20 -20
  68. jarvis/jarvis_utils/globals.py +18 -6
  69. jarvis/jarvis_utils/input.py +10 -9
  70. jarvis/jarvis_utils/methodology.py +141 -140
  71. jarvis/jarvis_utils/output.py +13 -13
  72. jarvis/jarvis_utils/utils.py +23 -71
  73. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +6 -15
  74. jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
  75. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +4 -3
  76. jarvis/jarvis_tools/lsp_find_definition.py +0 -150
  77. jarvis/jarvis_tools/lsp_find_references.py +0 -127
  78. jarvis/jarvis_tools/select_code_files.py +0 -62
  79. jarvis_ai_assistant-0.1.132.dist-info/RECORD +0 -82
  80. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
  81. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
  82. {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,7 @@ class FileAnalyzerTool:
12
12
  单文件分析工具
13
13
  使用agent深入分析单个文件的结构、实现细节和代码质量
14
14
  """
15
-
15
+
16
16
  name = "file_analyzer"
17
17
  description = "深入分析单个文件的结构、实现细节和代码质量"
18
18
  parameters = {
@@ -35,26 +35,26 @@ class FileAnalyzerTool:
35
35
  },
36
36
  "required": ["file_path"]
37
37
  }
38
-
38
+
39
39
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
40
40
  """
41
41
  执行单文件分析工具
42
-
42
+
43
43
  Args:
44
44
  args: 包含参数的字典
45
-
45
+
46
46
  Returns:
47
47
  包含执行结果的字典
48
48
  """
49
49
  # 存储原始目录
50
50
  original_dir = os.getcwd()
51
-
51
+
52
52
  try:
53
53
  # 解析参数
54
54
  file_path = args.get("file_path", "")
55
55
  root_dir = args.get("root_dir", ".")
56
56
  objective = args.get("objective", "")
57
-
57
+
58
58
  # 验证参数
59
59
  if not file_path:
60
60
  return {
@@ -62,31 +62,31 @@ class FileAnalyzerTool:
62
62
  "stdout": "",
63
63
  "stderr": "必须提供文件路径"
64
64
  }
65
-
65
+
66
66
  # 确保文件路径是相对于root_dir的,如果是绝对路径则转换为相对路径
67
67
  abs_file_path = os.path.abspath(file_path)
68
68
  abs_root_dir = os.path.abspath(root_dir)
69
-
69
+
70
70
  if abs_file_path.startswith(abs_root_dir):
71
71
  rel_file_path = os.path.relpath(abs_file_path, abs_root_dir)
72
72
  else:
73
73
  rel_file_path = file_path
74
-
74
+
75
75
  # 获取文件扩展名和文件名
76
76
  file_ext = pathlib.Path(file_path).suffix
77
77
  file_name = os.path.basename(file_path)
78
-
78
+
79
79
  # 创建agent的system prompt
80
80
  system_prompt = self._create_system_prompt(
81
81
  rel_file_path, file_name, file_ext, root_dir, objective
82
82
  )
83
-
83
+
84
84
  # 创建agent的summary prompt
85
85
  summary_prompt = self._create_summary_prompt(rel_file_path, file_name)
86
-
86
+
87
87
  # 切换到根目录
88
88
  os.chdir(root_dir)
89
-
89
+
90
90
  # 检查文件是否存在
91
91
  if not os.path.isfile(rel_file_path):
92
92
  return {
@@ -94,44 +94,40 @@ class FileAnalyzerTool:
94
94
  "stdout": "",
95
95
  "stderr": f"文件不存在: {rel_file_path}"
96
96
  }
97
-
97
+
98
98
  # 构建使用的工具
99
99
  from jarvis.jarvis_tools.registry import ToolRegistry
100
100
  tool_registry = ToolRegistry()
101
101
  tool_registry.use_tools([
102
- "execute_shell",
103
- "read_code",
102
+ "execute_shell",
103
+ "read_code",
104
104
  "find_symbol",
105
- "function_analyzer",
105
+ "function_analyzer",
106
106
  "find_caller"
107
107
  ])
108
-
108
+
109
109
  # 创建并运行agent
110
110
  analyzer_agent = Agent(
111
111
  system_prompt=system_prompt,
112
112
  name=f"FileAnalyzer-{file_name}",
113
113
  description=f"分析 {file_name} 文件的结构和实现",
114
114
  summary_prompt=summary_prompt,
115
- platform=PlatformRegistry().get_codegen_platform(),
115
+ platform=PlatformRegistry().get_normal_platform(),
116
116
  output_handler=[tool_registry],
117
- need_summary=True,
118
- is_sub_agent=True,
119
- use_methodology=False,
120
- record_methodology=False,
121
117
  execute_tool_confirm=False,
122
118
  auto_complete=True
123
119
  )
124
-
120
+
125
121
  # 运行agent并获取结果
126
122
  task_input = f"深入分析文件 {rel_file_path} 的结构、实现细节和代码质量"
127
123
  result = analyzer_agent.run(task_input)
128
-
124
+
129
125
  return {
130
126
  "success": True,
131
127
  "stdout": result,
132
128
  "stderr": ""
133
129
  }
134
-
130
+
135
131
  except Exception as e:
136
132
  PrettyOutput.print(str(e), OutputType.ERROR)
137
133
  return {
@@ -142,117 +138,132 @@ class FileAnalyzerTool:
142
138
  finally:
143
139
  # 恢复原始目录
144
140
  os.chdir(original_dir)
145
-
141
+
146
142
  def _create_system_prompt(self, file_path: str, file_name: str, file_ext: str,
147
143
  root_dir: str, objective: str) -> str:
148
144
  """
149
145
  创建Agent的system prompt
150
-
146
+
151
147
  Args:
152
- file_path: 文件相对路径
148
+ file_path: 文件路径
153
149
  file_name: 文件名
154
150
  file_ext: 文件扩展名
155
- root_dir: 项目根目录
151
+ root_dir: 代码库根目录
156
152
  objective: 分析目标
157
-
153
+
158
154
  Returns:
159
155
  系统提示文本
160
156
  """
161
- # 根据文件扩展名确定语言
162
- language_map = {
163
- '.py': 'Python',
164
- '.js': 'JavaScript',
165
- '.ts': 'TypeScript',
166
- '.java': 'Java',
167
- '.c': 'C',
168
- '.cpp': 'C++',
169
- '.cs': 'C#',
170
- '.go': 'Go',
171
- '.rs': 'Rust',
172
- '.php': 'PHP',
173
- '.rb': 'Ruby',
174
- '.swift': 'Swift',
175
- '.kt': 'Kotlin',
176
- '.sh': 'Shell',
177
- '.html': 'HTML',
178
- '.css': 'CSS',
179
- '.scss': 'SCSS',
180
- '.json': 'JSON',
181
- '.xml': 'XML',
182
- '.yaml': 'YAML',
183
- '.md': 'Markdown'
184
- }
185
-
186
- language = language_map.get(file_ext, '未知')
187
157
  objective_text = f"\n\n## 分析目标\n{objective}" if objective else ""
188
-
189
- return f"""# 代码文件分析专家
158
+
159
+ return f"""# 文件分析专家
190
160
 
191
161
  ## 任务描述
192
- 分析文件 `{file_path}` 的内容,专注于分析目标所需的信息,生成有针对性的文件分析报告。{objective_text}
162
+ 分析文件 `{file_path}` 的结构、实现细节和代码质量,专注于分析目标所需的内容,生成有针对性、深入且有洞察力的文件分析报告。{objective_text}
163
+
164
+ ## 工具使用优先级
165
+ 1. **优先使用 read_code**: 直接读取文件内容是分析文件的首选方式
166
+ 2. **优先使用 execute_shell**:
167
+ - 使用 rg 搜索文件内容: `rg "pattern" {file_path}`
168
+ - 使用 loc 统计代码: `loc {file_path}`
169
+ 3. **仅在必要时使用其他分析工具**
193
170
 
194
171
  ## 文件信息
195
172
  - 文件路径: `{file_path}`
196
- - 编程语言: {language}
173
+ - 文件名称: `{file_name}`
174
+ - 文件类型: `{file_ext}`
197
175
  - 项目根目录: `{root_dir}`
198
176
 
199
177
  ## 分析策略
200
- 1. 首先理解分析目标,确定你需要查找的信息
201
- 2. 灵活采用适合目标的分析方法,不受预设分析框架的限制
202
- 3. 专注于与分析目标直接相关的内容,忽略无关部分
203
- 4. 根据目标需要自行判断分析的深度和广度
204
-
205
- ## 探索建议
206
- - 首先读取整个文件内容以获取全局视图
207
- - 识别与分析目标相关的关键部分
208
- - 深入探索这些关键部分,提供有针对性的分析
209
-
210
- ## 执行指令
211
- - 首先读取整个文件内容:
212
- ```
213
- read_code {{
214
- "files": [{{
215
- "path": "{file_path}",
216
- "start_line": 1,
217
- "end_line": -1
218
- }}]
219
- }}
220
- ```
221
-
222
- - 分析文件统计信息:
223
- ```
224
- wc -l {file_path}
225
- ```
226
-
227
- - 分析关键符号和函数:
228
- ```
229
- find_symbol {{
230
- "symbol": "关键符号名称",
231
- "root_dir": "."
232
- }}
233
-
234
- function_analyzer {{
235
- "function_name": "关键函数名称",
236
- "file_path": "{file_path}"
237
- }}
238
- ```
178
+ 1. 首先使用read_code直接读取整个文件内容或分段读取
179
+ 2. 使用rg命令搜索文件中的特定模式和结构
180
+ 3. 使用loc命令获取文件统计信息
181
+ 4. 根据文件类型和分析目标确定重点关注的方面
182
+ 5. 保证分析的完整性,收集充分的信息后再得出结论
183
+
184
+ ## 分析步骤
185
+ 以下步骤应根据具体分析目标灵活应用:
186
+
187
+ 1. **文件基本信息分析**:
188
+ - 使用 `loc {file_path}` 获取代码统计
189
+ - 使用 read_code 读取文件头部注释和文档
190
+ - 确定文件的编程语言和主要功能
191
+
192
+ 2. **结构分析**:
193
+ - 使用 read_code 阅读完整文件
194
+ - 对于大文件,可分段读取主要部分
195
+ - 识别文件的主要组成部分:
196
+ - 类定义: `rg "class\\s+" {file_path}`
197
+ - 函数定义: `rg "def\\s+|function\\s+" {file_path}`
198
+ - 重要变量: `rg "const\\s+|var\\s+|let\\s+" {file_path}`
199
+
200
+ 3. **核心组件分析**:
201
+ - 识别文件中的关键接口、类和函数
202
+ - 使用 rg 搜索重要的代码模式
203
+ - 分析组件间的交互和依赖关系
204
+
205
+ 4. **实现细节分析**:
206
+ - 读取并分析关键函数的实现
207
+ - 关注异常处理: `rg "try|catch|except" {file_path}`
208
+ - 检查资源管理: `rg "open|close|with" {file_path}`
209
+
210
+ 5. **引用分析**:
211
+ - 找出引用的外部依赖: `rg "import|require|include" {file_path}`
212
+ - 分析与其他模块的交互
213
+
214
+ ## 分析工具使用指南
215
+
216
+ ### read_code
217
+ - **首选工具**: 用于读取和分析文件内容
218
+ - **用法指南**:
219
+ - 读取整个文件: 直接指定文件路径
220
+ - 读取部分内容: 指定文件路径和行范围
221
+ - 读取头部或关键部分: 根据目的选择合适的行范围
222
+
223
+ ### execute_shell
224
+ - **用途**: 执行辅助命令进行分析
225
+ - **推荐命令**:
226
+ - `rg "pattern" {file_path}`: 在文件中搜索模式
227
+ - `loc {file_path}`: 获取文件代码统计
228
+ - `rg -n "class|def|function" {file_path}`: 查找结构元素
229
+
230
+ ### 其他专业工具
231
+ - **使用时机**: 仅当read_code和execute_shell不足以完成分析时
232
+ - **使用前提**: 必须先尝试使用基本工具解决问题
233
+ - **选择原则**: 根据实际需要选择最简洁有效的工具
234
+
235
+ ## 分析框架适应
236
+ 根据文件类型和编程范式调整分析重点:
237
+
238
+ ### 不同编程范式
239
+ - 面向对象: 类层次、继承、封装、接口实现
240
+ - 函数式: 函数组合、不可变性、纯函数
241
+ - 过程式: 流程控制、状态管理、数据处理
242
+ - 声明式: 规则定义、约束表达、模式匹配
243
+
244
+ ### 不同文件类型
245
+ - 源代码文件: 实现逻辑、算法、接口设计
246
+ - 配置文件: 参数设置、环境变量、系统选项
247
+ - 模板文件: 渲染逻辑、变量占位符、条件分支
248
+ - 数据文件: 结构组织、关系定义、索引设计
239
249
 
240
250
  ## 输出要求
241
251
  - 直接回应分析目标的关键问题
242
252
  - 提供与目标相关的深入洞察
243
253
  - 分析内容应直接服务于分析目标
244
254
  - 避免与目标无关的冗余信息
245
- - 使用具体代码路径和示例支持分析结论
246
- - 提供针对分析目标的具体建议和改进方向"""
255
+ - 使用具体代码片段支持分析结论
256
+ - 提供针对分析目标的具体建议
257
+ - 保证全面分析相关信息后再得出结论"""
247
258
 
248
259
  def _create_summary_prompt(self, file_path: str, file_name: str) -> str:
249
260
  """
250
261
  创建Agent的summary prompt
251
-
262
+
252
263
  Args:
253
264
  file_path: 文件路径
254
265
  file_name: 文件名
255
-
266
+
256
267
  Returns:
257
268
  总结提示文本
258
269
  """
@@ -268,4 +279,4 @@ class FileAnalyzerTool:
268
279
  - 使用具体的代码片段和实例支持你的观点
269
280
  - 以清晰的Markdown格式呈现,简洁明了
270
281
 
271
- 在分析中保持灵活性,避免固定思维模式。你的任务不是提供全面的文件概览,而是直接解决分析目标中提出的具体问题。"""
282
+ 在分析中保持灵活性,避免固定思维模式。你的任务不是提供全面的文件概览,而是直接解决分析目标中提出的具体问题。"""
@@ -3,6 +3,7 @@ import os
3
3
 
4
4
  from yaspin import yaspin
5
5
 
6
+ from jarvis.jarvis_utils.globals import add_read_file_record
6
7
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
8
 
8
9
 
@@ -34,11 +35,12 @@ class FileOperationTool:
34
35
  "required": ["operation", "files"]
35
36
  }
36
37
 
37
- def _handle_single_file(self, operation: str, filepath: str, content: str = "",
38
+ def _handle_single_file(self, operation: str, filepath: str, content: str = "",
38
39
  start_line: int = 1, end_line: int = -1) -> Dict[str, Any]:
39
40
  """Handle operations for a single file"""
40
41
  try:
41
42
  abs_path = os.path.abspath(filepath)
43
+ add_read_file_record(abs_path)
42
44
  if operation == "read":
43
45
  with yaspin(text=f"正在读取文件: {abs_path}...", color="cyan") as spinner:
44
46
  if not os.path.exists(abs_path):
@@ -47,18 +49,18 @@ class FileOperationTool:
47
49
  "stdout": "",
48
50
  "stderr": f"文件不存在: {abs_path}"
49
51
  }
50
-
52
+
51
53
  if os.path.getsize(abs_path) > 10 * 1024 * 1024: # 10MB
52
54
  return {
53
55
  "success": False,
54
56
  "stdout": "",
55
57
  "stderr": "File too large (>10MB)"
56
58
  }
57
-
59
+
58
60
  with open(abs_path, 'r', encoding='utf-8', errors="ignore") as f:
59
61
  lines = f.readlines()
60
-
61
-
62
+
63
+
62
64
  total_lines = len(lines)
63
65
  start_line = start_line if start_line >= 0 else total_lines + start_line + 1
64
66
  end_line = end_line if end_line >= 0 else total_lines + end_line + 1
@@ -66,7 +68,7 @@ class FileOperationTool:
66
68
  end_line = max(1, min(end_line, total_lines))
67
69
  if end_line == -1:
68
70
  end_line = total_lines
69
-
71
+
70
72
  if start_line > end_line:
71
73
  spinner.text = "无效的行范围"
72
74
  spinner.fail("❌")
@@ -76,10 +78,10 @@ class FileOperationTool:
76
78
  "stdout": "",
77
79
  "stderr": error_msg
78
80
  }
79
-
81
+
80
82
  content = "".join(lines[start_line - 1:end_line])
81
- output = f"\n文件: {abs_path}\n行: [{start_line}-{end_line}]\n{content}" + "\n\n" + "="*80 + "\n\n"
82
-
83
+ output = f"\n文件: {abs_path}\n行: [{start_line}-{end_line}]\n{content}" + "\n\n"
84
+
83
85
  spinner.text = f"文件读取完成: {abs_path}"
84
86
  spinner.ok("✅")
85
87
  return {
@@ -104,7 +106,7 @@ class FileOperationTool:
104
106
  "stdout": "",
105
107
  "stderr": f"Unknown operation: {operation}"
106
108
  }
107
-
109
+
108
110
  except Exception as e:
109
111
  PrettyOutput.print(str(e), OutputType.ERROR)
110
112
  return {
@@ -115,10 +117,10 @@ class FileOperationTool:
115
117
 
116
118
  def execute(self, args: Dict) -> Dict[str, Any]:
117
119
  """Execute file operations for multiple files
118
-
120
+
119
121
  Args:
120
122
  args: Dictionary containing operation and files list
121
-
123
+
122
124
  Returns:
123
125
  Dict containing:
124
126
  - success: Boolean indicating overall success
@@ -127,21 +129,21 @@ class FileOperationTool:
127
129
  """
128
130
  try:
129
131
  operation = args["operation"].strip()
130
-
132
+
131
133
  if "files" not in args or not isinstance(args["files"], list):
132
134
  return {
133
135
  "success": False,
134
136
  "stdout": "",
135
137
  "stderr": "files parameter is required and must be a list"
136
138
  }
137
-
139
+
138
140
  all_outputs = []
139
141
  success = True
140
-
142
+
141
143
  for file_info in args["files"]:
142
144
  if not isinstance(file_info, dict) or "path" not in file_info:
143
145
  continue
144
-
146
+
145
147
  content = file_info.get("content", "") if operation == "write" else ""
146
148
  result = self._handle_single_file(
147
149
  operation,
@@ -150,22 +152,22 @@ class FileOperationTool:
150
152
  file_info.get("start_line", 1),
151
153
  file_info.get("end_line", -1)
152
154
  )
153
-
155
+
154
156
  if result["success"]:
155
157
  all_outputs.append(result["stdout"])
156
158
  else:
157
159
  all_outputs.append(f"Error with {file_info['path']}: {result['stderr']}")
158
160
  success = success and result["success"]
159
-
161
+
160
162
  # Combine all outputs with separators
161
163
  combined_output = "\n\n" + "="*80 + "\n\n".join(all_outputs)
162
-
164
+
163
165
  return {
164
166
  "success": success,
165
167
  "stdout": combined_output,
166
168
  "stderr": ""
167
169
  }
168
-
170
+
169
171
  except Exception as e:
170
172
  PrettyOutput.print(str(e), OutputType.ERROR)
171
173
  return {