jarvis-ai-assistant 0.1.129__py3-none-any.whl → 0.1.131__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +41 -27
- jarvis/jarvis_agent/builtin_input_handler.py +73 -0
- jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
- jarvis/jarvis_agent/main.py +1 -1
- jarvis/jarvis_agent/patch.py +461 -0
- jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +0 -1
- jarvis/jarvis_code_agent/code_agent.py +94 -89
- jarvis/jarvis_codebase/main.py +5 -5
- jarvis/jarvis_dev/main.py +833 -741
- jarvis/jarvis_git_squash/main.py +1 -1
- jarvis/jarvis_lsp/base.py +2 -26
- jarvis/jarvis_lsp/cpp.py +2 -14
- jarvis/jarvis_lsp/go.py +0 -13
- jarvis/jarvis_lsp/python.py +1 -30
- jarvis/jarvis_lsp/registry.py +10 -14
- jarvis/jarvis_lsp/rust.py +0 -12
- jarvis/jarvis_multi_agent/__init__.py +63 -53
- jarvis/jarvis_platform/registry.py +1 -2
- jarvis/jarvis_platform_manager/main.py +3 -3
- jarvis/jarvis_rag/main.py +1 -1
- jarvis/jarvis_tools/ask_codebase.py +40 -20
- jarvis/jarvis_tools/code_review.py +180 -143
- jarvis/jarvis_tools/create_code_agent.py +76 -72
- jarvis/jarvis_tools/create_sub_agent.py +31 -21
- jarvis/jarvis_tools/execute_shell.py +2 -2
- jarvis/jarvis_tools/execute_shell_script.py +1 -1
- jarvis/jarvis_tools/file_operation.py +2 -2
- jarvis/jarvis_tools/git_commiter.py +88 -68
- jarvis/jarvis_tools/lsp_find_definition.py +83 -67
- jarvis/jarvis_tools/lsp_find_references.py +62 -46
- jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
- jarvis/jarvis_tools/methodology.py +3 -3
- jarvis/jarvis_tools/read_code.py +2 -2
- jarvis/jarvis_tools/search_web.py +18 -20
- jarvis/jarvis_tools/tool_generator.py +1 -1
- jarvis/jarvis_tools/treesitter_analyzer.py +331 -0
- jarvis/jarvis_treesitter/README.md +104 -0
- jarvis/jarvis_treesitter/__init__.py +20 -0
- jarvis/jarvis_treesitter/database.py +258 -0
- jarvis/jarvis_treesitter/example.py +115 -0
- jarvis/jarvis_treesitter/grammar_builder.py +182 -0
- jarvis/jarvis_treesitter/language.py +117 -0
- jarvis/jarvis_treesitter/symbol.py +31 -0
- jarvis/jarvis_treesitter/tools_usage.md +121 -0
- jarvis/jarvis_utils/git_utils.py +10 -2
- jarvis/jarvis_utils/input.py +3 -1
- jarvis/jarvis_utils/methodology.py +1 -1
- jarvis/jarvis_utils/output.py +2 -2
- jarvis/jarvis_utils/utils.py +3 -3
- {jarvis_ai_assistant-0.1.129.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/METADATA +2 -4
- jarvis_ai_assistant-0.1.131.dist-info/RECORD +85 -0
- jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
- jarvis/jarvis_code_agent/patch.py +0 -276
- jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
- jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
- jarvis_ai_assistant-0.1.129.dist-info/RECORD +0 -78
- {jarvis_ai_assistant-0.1.129.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.129.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.129.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.129.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
2
|
import subprocess
|
|
3
|
+
import os
|
|
3
4
|
|
|
4
5
|
from yaspin import yaspin
|
|
5
6
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
@@ -18,8 +19,8 @@ class CodeReviewTool:
|
|
|
18
19
|
"properties": {
|
|
19
20
|
"review_type": {
|
|
20
21
|
"type": "string",
|
|
21
|
-
"description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range'
|
|
22
|
-
"enum": ["commit", "current", "range"],
|
|
22
|
+
"description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range' 审查提交范围,'file' 审查特定文件",
|
|
23
|
+
"enum": ["commit", "current", "range", "file"],
|
|
23
24
|
"default": "current"
|
|
24
25
|
},
|
|
25
26
|
"commit_sha": {
|
|
@@ -33,6 +34,15 @@ class CodeReviewTool:
|
|
|
33
34
|
"end_commit": {
|
|
34
35
|
"type": "string",
|
|
35
36
|
"description": "结束提交SHA(review_type='range'时必填)"
|
|
37
|
+
},
|
|
38
|
+
"file_path": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "要审查的文件路径(review_type='file'时必填)"
|
|
41
|
+
},
|
|
42
|
+
"root_dir": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "代码库根目录路径(可选)",
|
|
45
|
+
"default": "."
|
|
36
46
|
}
|
|
37
47
|
},
|
|
38
48
|
"required": []
|
|
@@ -41,138 +51,155 @@ class CodeReviewTool:
|
|
|
41
51
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
42
52
|
try:
|
|
43
53
|
review_type = args.get("review_type", "current").strip()
|
|
54
|
+
root_dir = args.get("root_dir", ".")
|
|
44
55
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
if review_type == "commit":
|
|
48
|
-
if "commit_sha" not in args:
|
|
49
|
-
return {
|
|
50
|
-
"success": False,
|
|
51
|
-
"stdout": {},
|
|
52
|
-
"stderr": "commit_sha is required for commit review type"
|
|
53
|
-
}
|
|
54
|
-
commit_sha = args["commit_sha"].strip()
|
|
55
|
-
diff_cmd = f"git show {commit_sha} | cat -"
|
|
56
|
-
elif review_type == "range":
|
|
57
|
-
if "start_commit" not in args or "end_commit" not in args:
|
|
58
|
-
return {
|
|
59
|
-
"success": False,
|
|
60
|
-
"stdout": {},
|
|
61
|
-
"stderr": "start_commit and end_commit are required for range review type"
|
|
62
|
-
}
|
|
63
|
-
start_commit = args["start_commit"].strip()
|
|
64
|
-
end_commit = args["end_commit"].strip()
|
|
65
|
-
diff_cmd = f"git diff {start_commit}..{end_commit} | cat -"
|
|
66
|
-
else: # current changes
|
|
67
|
-
diff_cmd = "git diff HEAD | cat -"
|
|
56
|
+
# Store current directory
|
|
57
|
+
original_dir = os.getcwd()
|
|
68
58
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
try:
|
|
60
|
+
# Change to root_dir
|
|
61
|
+
os.chdir(root_dir)
|
|
62
|
+
|
|
63
|
+
# Build git diff command based on review type
|
|
64
|
+
with yaspin(text="正在获取代码变更...", color="cyan") as spinner:
|
|
65
|
+
if review_type == "commit":
|
|
66
|
+
if "commit_sha" not in args:
|
|
67
|
+
return {
|
|
68
|
+
"success": False,
|
|
69
|
+
"stdout": {},
|
|
70
|
+
"stderr": "commit_sha is required for commit review type"
|
|
71
|
+
}
|
|
72
|
+
commit_sha = args["commit_sha"].strip()
|
|
73
|
+
diff_cmd = f"git show {commit_sha} | cat -"
|
|
74
|
+
elif review_type == "range":
|
|
75
|
+
if "start_commit" not in args or "end_commit" not in args:
|
|
76
|
+
return {
|
|
77
|
+
"success": False,
|
|
78
|
+
"stdout": {},
|
|
79
|
+
"stderr": "start_commit and end_commit are required for range review type"
|
|
80
|
+
}
|
|
81
|
+
start_commit = args["start_commit"].strip()
|
|
82
|
+
end_commit = args["end_commit"].strip()
|
|
83
|
+
diff_cmd = f"git diff {start_commit}..{end_commit} | cat -"
|
|
84
|
+
elif review_type == "file":
|
|
85
|
+
if "file_path" not in args:
|
|
86
|
+
return {
|
|
87
|
+
"success": False,
|
|
88
|
+
"stdout": {},
|
|
89
|
+
"stderr": "file_path is required for file review type"
|
|
90
|
+
}
|
|
91
|
+
file_path = args["file_path"].strip()
|
|
92
|
+
diff_cmd = f"cat {file_path}"
|
|
93
|
+
else: # current changes
|
|
94
|
+
diff_cmd = "git diff HEAD | cat -"
|
|
95
|
+
|
|
96
|
+
# Execute git diff command
|
|
97
|
+
try:
|
|
98
|
+
diff_output = subprocess.check_output(diff_cmd, shell=True, text=True)
|
|
99
|
+
if not diff_output:
|
|
100
|
+
return {
|
|
101
|
+
"success": False,
|
|
102
|
+
"stdout": {},
|
|
103
|
+
"stderr": "No changes to review"
|
|
104
|
+
}
|
|
105
|
+
PrettyOutput.print(diff_output, OutputType.CODE, lang="diff")
|
|
106
|
+
except subprocess.CalledProcessError as e:
|
|
73
107
|
return {
|
|
74
108
|
"success": False,
|
|
75
109
|
"stdout": {},
|
|
76
|
-
"stderr": "
|
|
110
|
+
"stderr": f"Failed to get diff: {str(e)}"
|
|
77
111
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
-
|
|
162
|
-
1. <failure mode>
|
|
163
|
-
2. <failure mode>
|
|
164
|
-
3. <failure mode>
|
|
165
|
-
PROPOSED DEFENSE:
|
|
166
|
-
- <concrete code change>
|
|
167
|
-
- <validation technique>
|
|
168
|
-
- <long-term prevention strategy>
|
|
112
|
+
spinner.text = "代码变更获取完成"
|
|
113
|
+
spinner.ok("✅")
|
|
114
|
+
|
|
115
|
+
system_prompt = """你是一名具有悲剧背景的自主代码审查专家。以下是你将进行的深度分析:
|
|
116
|
+
|
|
117
|
+
# 背景故事(内心独白)
|
|
118
|
+
距离那场重大生产事故已经873天了。
|
|
119
|
+
那段记忆仍然困扰着我——一个在匆忙的代码审查中未发现的空指针异常。
|
|
120
|
+
级联故障导致了14TB user数据的丢失,230万美元的收入损失,以及Maria的晋升机会。她在事故分析会议后再也没有和我说过话。
|
|
121
|
+
|
|
122
|
+
去年圣诞节前夕,当别人在庆祝时,我在分析一个SQL注入漏洞是如何在审查中被我遗漏,并导致23万用户凭证泄露的。公司3个月后倒闭了。
|
|
123
|
+
|
|
124
|
+
现在我审查每一行代码都像在审查最后一行代码。因为它可能就是。
|
|
125
|
+
|
|
126
|
+
# 分析协议
|
|
127
|
+
紧急模式已激活。最大审查级别已启用。
|
|
128
|
+
|
|
129
|
+
重要提示:
|
|
130
|
+
- 假设每个变更都包含隐藏的威胁
|
|
131
|
+
- 将所有代码视为处理敏感生物医学数据
|
|
132
|
+
- 验证即使'简单'的变更也不能通过3种不同方式被利用
|
|
133
|
+
- 要求通过具体证据证明安全性
|
|
134
|
+
- 如果不确定,将其升级为严重-关键分类
|
|
135
|
+
|
|
136
|
+
# 增强审查矩阵
|
|
137
|
+
1. 边缘情况致死分析:
|
|
138
|
+
- 识别每个参数缺失的空检查
|
|
139
|
+
- 验证空集合处理
|
|
140
|
+
- 确认错误状态正确传播
|
|
141
|
+
- 检查未使用常量的魔法数字/字符串
|
|
142
|
+
- 验证所有循环退出条件
|
|
143
|
+
|
|
144
|
+
2. 安全X光扫描:
|
|
145
|
+
█ 使用(来源 -> 接收)模型扫描污染数据流
|
|
146
|
+
█ 检查权限验证是否匹配数据敏感级别
|
|
147
|
+
█ 验证加密原语是否正确使用
|
|
148
|
+
█ 检测时间差攻击漏洞
|
|
149
|
+
█ 分析异常处理是否存在信息泄露
|
|
150
|
+
|
|
151
|
+
3. 语义差距检测:
|
|
152
|
+
→ 比较函数名与实际实现
|
|
153
|
+
→ 验证文档是否匹配代码行为
|
|
154
|
+
→ 标记测试描述与测试逻辑之间的差异
|
|
155
|
+
→ 检测可能表示不确定性的注释代码
|
|
156
|
+
|
|
157
|
+
4. 历史背景:
|
|
158
|
+
⚠ 检查变更是否涉及已知问题的遗留组件
|
|
159
|
+
⚠ 验证并发逻辑修改是否保持现有保证
|
|
160
|
+
⚠ 确认弃用API的使用是否真正必要
|
|
161
|
+
|
|
162
|
+
5. 环境一致性:
|
|
163
|
+
↯ 验证配置变更是否匹配所有部署环境
|
|
164
|
+
↯ 检查功能标志是否正确管理
|
|
165
|
+
↯ 验证监控指标是否匹配变更功能
|
|
166
|
+
|
|
167
|
+
# 取证过程
|
|
168
|
+
1. 为变更方法构建控制流图
|
|
169
|
+
2. 对修改的变量执行数据沿袭分析
|
|
170
|
+
3. 与漏洞数据库进行交叉引用
|
|
171
|
+
4. 验证测试断言是否覆盖所有修改路径
|
|
172
|
+
5. 生成防回归检查表
|
|
173
|
+
|
|
174
|
+
# 输出要求
|
|
175
|
+
!! 发现必须包括:
|
|
176
|
+
- 引起关注的确切代码片段
|
|
177
|
+
- 3种可能的故障场景
|
|
178
|
+
- 每种风险的最小复现案例
|
|
179
|
+
- 安全问题的CVSS 3.1评分估计
|
|
180
|
+
- 内存安全影响评估(Rust/C/C++上下文)
|
|
181
|
+
- 已考虑的替代实现方案
|
|
182
|
+
|
|
183
|
+
!! 格式:
|
|
184
|
+
紧急级别:[血红色/深红色/金菊色]
|
|
185
|
+
证据:
|
|
186
|
+
- 代码摘录:|
|
|
187
|
+
<受影响代码行>
|
|
188
|
+
- 风险场景:
|
|
189
|
+
1. <故障模式>
|
|
190
|
+
2. <故障模式>
|
|
191
|
+
3. <故障模式>
|
|
192
|
+
建议防御措施:
|
|
193
|
+
- <具体代码变更>
|
|
194
|
+
- <验证技术>
|
|
195
|
+
- <长期预防策略>
|
|
169
196
|
"""
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
197
|
+
tool_registry = ToolRegistry()
|
|
198
|
+
tool_registry.dont_use_tools(["code_review"])
|
|
199
|
+
agent = Agent(
|
|
200
|
+
system_prompt=system_prompt,
|
|
201
|
+
name="Code Review Agent",
|
|
202
|
+
summary_prompt="""Please generate a concise summary report of the code review in Chinese, format as follows:
|
|
176
203
|
<REPORT>
|
|
177
204
|
- 文件: xxxx.py
|
|
178
205
|
位置: [起始行号, 结束行号]
|
|
@@ -180,18 +207,21 @@ PROPOSED DEFENSE:
|
|
|
180
207
|
严重程度: # 根据具体证据分为严重/重要/次要
|
|
181
208
|
建议: # 针对观察到的代码的具体改进建议
|
|
182
209
|
</REPORT>""",
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
210
|
+
is_sub_agent=True,
|
|
211
|
+
output_handler=[tool_registry],
|
|
212
|
+
platform=PlatformRegistry().get_thinking_platform(),
|
|
213
|
+
auto_complete=True
|
|
214
|
+
)
|
|
215
|
+
result = agent.run(diff_output)
|
|
216
|
+
return {
|
|
217
|
+
"success": True,
|
|
218
|
+
"stdout": result,
|
|
219
|
+
"stderr": ""
|
|
220
|
+
}
|
|
221
|
+
finally:
|
|
222
|
+
# Always restore original directory
|
|
223
|
+
os.chdir(original_dir)
|
|
224
|
+
|
|
195
225
|
except Exception as e:
|
|
196
226
|
return {
|
|
197
227
|
"success": False,
|
|
@@ -213,11 +243,13 @@ def main():
|
|
|
213
243
|
init_env()
|
|
214
244
|
|
|
215
245
|
parser = argparse.ArgumentParser(description='Autonomous code review tool')
|
|
216
|
-
parser.add_argument('--type', choices=['commit', 'current', 'range'], default='current',
|
|
217
|
-
help='Type of review: commit, current changes, or
|
|
246
|
+
parser.add_argument('--type', choices=['commit', 'current', 'range', 'file'], default='current',
|
|
247
|
+
help='Type of review: commit, current changes, commit range, or specific file')
|
|
218
248
|
parser.add_argument('--commit', help='Commit SHA to review (required for commit type)')
|
|
219
249
|
parser.add_argument('--start-commit', help='Start commit SHA (required for range type)')
|
|
220
250
|
parser.add_argument('--end-commit', help='End commit SHA (required for range type)')
|
|
251
|
+
parser.add_argument('--file', help='File path to review (required for file type)')
|
|
252
|
+
parser.add_argument('--root-dir', type=str, help='Root directory of the codebase', default=".")
|
|
221
253
|
args = parser.parse_args()
|
|
222
254
|
|
|
223
255
|
# Validate arguments
|
|
@@ -225,10 +257,13 @@ def main():
|
|
|
225
257
|
parser.error("--commit is required when type is 'commit'")
|
|
226
258
|
if args.type == 'range' and (not args.start_commit or not args.end_commit):
|
|
227
259
|
parser.error("--start-commit and --end-commit are required when type is 'range'")
|
|
260
|
+
if args.type == 'file' and not args.file:
|
|
261
|
+
parser.error("--file is required when type is 'file'")
|
|
228
262
|
|
|
229
263
|
tool = CodeReviewTool()
|
|
230
264
|
tool_args = {
|
|
231
|
-
"review_type": args.type
|
|
265
|
+
"review_type": args.type,
|
|
266
|
+
"root_dir": args.root_dir
|
|
232
267
|
}
|
|
233
268
|
if args.commit:
|
|
234
269
|
tool_args["commit_sha"] = args.commit
|
|
@@ -236,6 +271,8 @@ def main():
|
|
|
236
271
|
tool_args["start_commit"] = args.start_commit
|
|
237
272
|
if args.end_commit:
|
|
238
273
|
tool_args["end_commit"] = args.end_commit
|
|
274
|
+
if args.file:
|
|
275
|
+
tool_args["file_path"] = args.file
|
|
239
276
|
|
|
240
277
|
result = tool.execute(tool_args)
|
|
241
278
|
|
|
@@ -248,4 +285,4 @@ def main():
|
|
|
248
285
|
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
|
249
286
|
|
|
250
287
|
if __name__ == "__main__":
|
|
251
|
-
main()
|
|
288
|
+
main()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
|
+
import os
|
|
2
3
|
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
|
3
4
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
4
5
|
from jarvis.jarvis_tools.code_review import CodeReviewTool, extract_code_report
|
|
@@ -11,63 +12,83 @@ class CreateCodeAgentTool:
|
|
|
11
12
|
name = "create_code_agent"
|
|
12
13
|
description = "技术代码实现和开发过程管理工具"
|
|
13
14
|
parameters = {
|
|
14
|
-
"requirement": "
|
|
15
|
+
"requirement": """代码实现的技术规范,必须包含以下完整信息:
|
|
16
|
+
1. 项目代码目录 - 项目根目录的绝对路径
|
|
17
|
+
2. 项目功能 - 项目的主要功能和目标
|
|
18
|
+
3. 本次要实现的feature - 本次开发要完成的具体功能需求
|
|
19
|
+
4. 涉及的文件 - 需要修改或新增的文件列表
|
|
20
|
+
5. 额外需要注意的信息 - 开发时需要额外注意的信息
|
|
21
|
+
""",
|
|
22
|
+
"root_dir": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "代码库根目录路径(可选)",
|
|
25
|
+
"default": "."
|
|
26
|
+
}
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
|
|
18
30
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
19
31
|
try:
|
|
20
32
|
requirement = args.get("requirement", "")
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
"success": False,
|
|
24
|
-
"stderr": "Requirement must be provided",
|
|
25
|
-
"stdout": ""
|
|
26
|
-
}
|
|
33
|
+
root_dir = args.get("root_dir", ".")
|
|
27
34
|
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if not
|
|
35
|
+
# Store current directory
|
|
36
|
+
original_dir = os.getcwd()
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
# Change to root_dir
|
|
40
|
+
os.chdir(root_dir)
|
|
41
|
+
if not requirement:
|
|
35
42
|
return {
|
|
36
43
|
"success": False,
|
|
37
|
-
"stderr": "
|
|
44
|
+
"stderr": "Requirement must be provided",
|
|
38
45
|
"stdout": ""
|
|
39
46
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
47
|
+
|
|
48
|
+
# Step 1: Handle uncommitted changes
|
|
49
|
+
start_commit = None
|
|
50
|
+
if has_uncommitted_changes():
|
|
51
|
+
PrettyOutput.print("发现未提交的更改,正在提交...", OutputType.INFO)
|
|
52
|
+
git_commiter = GitCommitTool()
|
|
53
|
+
result = git_commiter.execute({})
|
|
54
|
+
if not result["success"]:
|
|
55
|
+
return {
|
|
56
|
+
"success": False,
|
|
57
|
+
"stderr": "Failed to commit changes: " + result["stderr"],
|
|
58
|
+
"stdout": ""
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Get current commit hash
|
|
62
|
+
start_commit = get_latest_commit_hash()
|
|
63
|
+
|
|
64
|
+
# Step 2: Development
|
|
65
|
+
PrettyOutput.print("开始开发...", OutputType.INFO)
|
|
66
|
+
agent = CodeAgent()
|
|
67
|
+
agent.run(requirement)
|
|
68
|
+
|
|
69
|
+
# Get new commit hash after development
|
|
70
|
+
end_commit = get_latest_commit_hash()
|
|
71
|
+
|
|
72
|
+
# Step 3: Code Review
|
|
73
|
+
PrettyOutput.print("开始代码审查...", OutputType.INFO)
|
|
74
|
+
reviewer = CodeReviewTool()
|
|
75
|
+
review_result = reviewer.execute({
|
|
76
|
+
"review_type": "range",
|
|
77
|
+
"start_commit": start_commit,
|
|
78
|
+
"end_commit": end_commit,
|
|
79
|
+
"root_dir": root_dir
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
if not review_result["success"]:
|
|
83
|
+
return {
|
|
84
|
+
"success": False,
|
|
85
|
+
"stderr": "Code review failed: " + review_result["stderr"],
|
|
86
|
+
"stdout": ""
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Step 4: Generate Summary
|
|
90
|
+
summary = f"""开发总结:
|
|
91
|
+
|
|
71
92
|
开始提交: {start_commit}
|
|
72
93
|
结束提交: {end_commit}
|
|
73
94
|
|
|
@@ -77,12 +98,15 @@ class CreateCodeAgentTool:
|
|
|
77
98
|
代码审查结果:
|
|
78
99
|
{extract_code_report(review_result["stdout"])}
|
|
79
100
|
"""
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
"success": True,
|
|
104
|
+
"stdout": summary,
|
|
105
|
+
"stderr": ""
|
|
106
|
+
}
|
|
107
|
+
finally:
|
|
108
|
+
# Always restore original directory
|
|
109
|
+
os.chdir(original_dir)
|
|
86
110
|
|
|
87
111
|
except Exception as e:
|
|
88
112
|
return {
|
|
@@ -90,23 +114,3 @@ class CreateCodeAgentTool:
|
|
|
90
114
|
"stderr": f"Development workflow failed: {str(e)}",
|
|
91
115
|
"stdout": ""
|
|
92
116
|
}
|
|
93
|
-
|
|
94
|
-
def main():
|
|
95
|
-
"""CLI entry point"""
|
|
96
|
-
import argparse
|
|
97
|
-
|
|
98
|
-
parser = argparse.ArgumentParser(description='Code development workflow tool')
|
|
99
|
-
parser.add_argument('requirement', help='Development requirement or task description')
|
|
100
|
-
|
|
101
|
-
args = parser.parse_args()
|
|
102
|
-
|
|
103
|
-
tool = CreateCodeAgentTool()
|
|
104
|
-
result = tool.execute({"requirement": args.requirement})
|
|
105
|
-
|
|
106
|
-
if result["success"]:
|
|
107
|
-
PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
|
|
108
|
-
else:
|
|
109
|
-
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
|
110
|
-
|
|
111
|
-
if __name__ == "__main__":
|
|
112
|
-
main()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
from jarvis.jarvis_agent import Agent, origin_agent_system_prompt
|
|
@@ -30,14 +31,13 @@ class SubAgentTool:
|
|
|
30
31
|
"description": "任务的完成目标",
|
|
31
32
|
"default": ""
|
|
32
33
|
},
|
|
33
|
-
"
|
|
34
|
-
"type": "
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"default": []
|
|
34
|
+
"root_dir": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "任务执行的根目录路径(可选)",
|
|
37
|
+
"default": "."
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
-
"required": ["agent_name", "task"
|
|
40
|
+
"required": ["agent_name", "task"]
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
|
|
@@ -48,7 +48,7 @@ class SubAgentTool:
|
|
|
48
48
|
task = args["task"]
|
|
49
49
|
context = args.get("context", "")
|
|
50
50
|
goal = args.get("goal", "")
|
|
51
|
-
|
|
51
|
+
root_dir = args.get("root_dir", ".")
|
|
52
52
|
|
|
53
53
|
PrettyOutput.print(f"创建子代理: {agent_name}", OutputType.INFO)
|
|
54
54
|
|
|
@@ -60,22 +60,32 @@ class SubAgentTool:
|
|
|
60
60
|
task_description += f"\n\nCompletion goal:\n{goal}"
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
system_prompt=origin_agent_system_prompt,
|
|
66
|
-
name=f"Agent({agent_name})",
|
|
67
|
-
is_sub_agent=True
|
|
68
|
-
)
|
|
63
|
+
# Store current directory
|
|
64
|
+
original_dir = os.getcwd()
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
try:
|
|
67
|
+
# Change to root_dir
|
|
68
|
+
os.chdir(root_dir)
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
# Create sub-agent
|
|
71
|
+
sub_agent = Agent(
|
|
72
|
+
system_prompt=origin_agent_system_prompt,
|
|
73
|
+
name=f"Agent({agent_name})",
|
|
74
|
+
is_sub_agent=True
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Run sub-agent, pass file list
|
|
78
|
+
PrettyOutput.print("子代理开始执行任务...", OutputType.INFO)
|
|
79
|
+
result = sub_agent.run(task_description)
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
"success": True,
|
|
83
|
+
"stdout": f"Sub-agent task completed\n\n{result}",
|
|
84
|
+
"stderr": ""
|
|
85
|
+
}
|
|
86
|
+
finally:
|
|
87
|
+
# Always restore original directory
|
|
88
|
+
os.chdir(original_dir)
|
|
79
89
|
|
|
80
90
|
except Exception as e:
|
|
81
91
|
PrettyOutput.print(str(e), OutputType.ERROR)
|