1use std::{collections::HashMap, path::Path};
2
3use anyhow::Result;
4use async_trait::async_trait;
5use serde_json::Value;
6
7use crate::{agent::types::UndoAction, tools, tools::base::Tool};
8
9pub struct GithubRepoInfoTool;
10#[async_trait]
11impl Tool for GithubRepoInfoTool {
12 fn name(&self) -> &str {
13 "github_repo_info"
14 }
15 async fn execute(
16 &self,
17 args: &HashMap<String, Value>,
18 _undo: &mut Vec<UndoAction>,
19 _cwd: Option<&Path>,
20 ) -> Result<String> {
21 let repo = args
22 .get("repo")
23 .and_then(|v| v.as_str())
24 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
25 tools::github_ops::github_repo_info(repo).await
26 }
27}
28
29pub struct GithubRepoListIssuesTool;
30#[async_trait]
31impl Tool for GithubRepoListIssuesTool {
32 fn name(&self) -> &str {
33 "github_repo_list_issues"
34 }
35 async fn execute(
36 &self,
37 args: &HashMap<String, Value>,
38 _undo: &mut Vec<UndoAction>,
39 _cwd: Option<&Path>,
40 ) -> Result<String> {
41 let repo = args
42 .get("repo")
43 .and_then(|v| v.as_str())
44 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
45 let state = args.get("state").and_then(|v| v.as_str());
46 let limit = args
47 .get("limit")
48 .and_then(|v| v.as_u64())
49 .map(|v| v as usize);
50 tools::github_ops::github_repo_list_issues(repo, state, limit).await
51 }
52}
53
54pub struct GithubIssueCreateTool;
55#[async_trait]
56impl Tool for GithubIssueCreateTool {
57 fn name(&self) -> &str {
58 "github_issue_create"
59 }
60 async fn execute(
61 &self,
62 args: &HashMap<String, Value>,
63 _undo: &mut Vec<UndoAction>,
64 _cwd: Option<&Path>,
65 ) -> Result<String> {
66 let repo = args
67 .get("repo")
68 .and_then(|v| v.as_str())
69 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
70 let title = args
71 .get("title")
72 .and_then(|v| v.as_str())
73 .ok_or_else(|| anyhow::anyhow!("Missing 'title'"))?;
74 let body = args.get("body").and_then(|v| v.as_str());
75 let labels = args.get("labels").and_then(|v| v.as_str());
76 tools::github_ops::github_issue_create(repo, title, body, labels).await
77 }
78}
79
80pub struct GithubIssueUpdateTool;
81#[async_trait]
82impl Tool for GithubIssueUpdateTool {
83 fn name(&self) -> &str {
84 "github_issue_update"
85 }
86 async fn execute(
87 &self,
88 args: &HashMap<String, Value>,
89 _undo: &mut Vec<UndoAction>,
90 _cwd: Option<&Path>,
91 ) -> Result<String> {
92 let repo = args
93 .get("repo")
94 .and_then(|v| v.as_str())
95 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
96 let issue_number = args
97 .get("issue_number")
98 .and_then(|v| v.as_u64())
99 .ok_or_else(|| anyhow::anyhow!("Missing 'issue_number'"))?;
100 let title = args.get("title").and_then(|v| v.as_str());
101 let body = args.get("body").and_then(|v| v.as_str());
102 let state = args.get("state").and_then(|v| v.as_str());
103 tools::github_ops::github_issue_update(repo, issue_number, title, body, state).await
104 }
105}
106
107pub struct GithubPrListTool;
108#[async_trait]
109impl Tool for GithubPrListTool {
110 fn name(&self) -> &str {
111 "github_pr_list"
112 }
113 async fn execute(
114 &self,
115 args: &HashMap<String, Value>,
116 _undo: &mut Vec<UndoAction>,
117 _cwd: Option<&Path>,
118 ) -> Result<String> {
119 let repo = args
120 .get("repo")
121 .and_then(|v| v.as_str())
122 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
123 let state = args.get("state").and_then(|v| v.as_str());
124 let limit = args
125 .get("limit")
126 .and_then(|v| v.as_u64())
127 .map(|v| v as usize);
128 tools::github_ops::github_pr_list(repo, state, limit).await
129 }
130}
131
132pub struct GithubPrCreateTool;
133#[async_trait]
134impl Tool for GithubPrCreateTool {
135 fn name(&self) -> &str {
136 "github_pr_create"
137 }
138 async fn execute(
139 &self,
140 args: &HashMap<String, Value>,
141 _undo: &mut Vec<UndoAction>,
142 _cwd: Option<&Path>,
143 ) -> Result<String> {
144 let repo = args
145 .get("repo")
146 .and_then(|v| v.as_str())
147 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
148 let title = args
149 .get("title")
150 .and_then(|v| v.as_str())
151 .ok_or_else(|| anyhow::anyhow!("Missing 'title'"))?;
152 let head = args
153 .get("head")
154 .and_then(|v| v.as_str())
155 .ok_or_else(|| anyhow::anyhow!("Missing 'head'"))?;
156 let base = args
157 .get("base")
158 .and_then(|v| v.as_str())
159 .ok_or_else(|| anyhow::anyhow!("Missing 'base'"))?;
160 let body = args.get("body").and_then(|v| v.as_str());
161 let draft = args.get("draft").and_then(|v| v.as_bool()).unwrap_or(false);
162 tools::github_ops::github_pr_create(repo, title, head, base, body, draft).await
163 }
164}
165
166pub struct GithubPrInfoTool;
167#[async_trait]
168impl Tool for GithubPrInfoTool {
169 fn name(&self) -> &str {
170 "github_pr_info"
171 }
172 async fn execute(
173 &self,
174 args: &HashMap<String, Value>,
175 _undo: &mut Vec<UndoAction>,
176 _cwd: Option<&Path>,
177 ) -> Result<String> {
178 let repo = args
179 .get("repo")
180 .and_then(|v| v.as_str())
181 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
182 let pr_number = args
183 .get("pr_number")
184 .and_then(|v| v.as_u64())
185 .ok_or_else(|| anyhow::anyhow!("Missing 'pr_number'"))?;
186 tools::github_ops::github_pr_info(repo, pr_number).await
187 }
188}
189
190pub struct GithubPrMergeTool;
191#[async_trait]
192impl Tool for GithubPrMergeTool {
193 fn name(&self) -> &str {
194 "github_pr_merge"
195 }
196 async fn execute(
197 &self,
198 args: &HashMap<String, Value>,
199 _undo: &mut Vec<UndoAction>,
200 _cwd: Option<&Path>,
201 ) -> Result<String> {
202 let repo = args
203 .get("repo")
204 .and_then(|v| v.as_str())
205 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
206 let pr_number = args
207 .get("pr_number")
208 .and_then(|v| v.as_u64())
209 .ok_or_else(|| anyhow::anyhow!("Missing 'pr_number'"))?;
210 let method = args.get("method").and_then(|v| v.as_str());
211 tools::github_ops::github_pr_merge(repo, pr_number, method).await
212 }
213}
214
215pub struct GithubSearchCodeTool;
216#[async_trait]
217impl Tool for GithubSearchCodeTool {
218 fn name(&self) -> &str {
219 "github_search_code"
220 }
221 async fn execute(
222 &self,
223 args: &HashMap<String, Value>,
224 _undo: &mut Vec<UndoAction>,
225 _cwd: Option<&Path>,
226 ) -> Result<String> {
227 let query = args
228 .get("query")
229 .and_then(|v| v.as_str())
230 .ok_or_else(|| anyhow::anyhow!("Missing 'query'"))?;
231 let repo = args.get("repo").and_then(|v| v.as_str());
232 let limit = args
233 .get("limit")
234 .and_then(|v| v.as_u64())
235 .map(|v| v as usize);
236 tools::github_ops::github_search_code(query, repo, limit).await
237 }
238}
239
240pub struct GithubSearchReposTool;
241#[async_trait]
242impl Tool for GithubSearchReposTool {
243 fn name(&self) -> &str {
244 "github_search_repos"
245 }
246 async fn execute(
247 &self,
248 args: &HashMap<String, Value>,
249 _undo: &mut Vec<UndoAction>,
250 _cwd: Option<&Path>,
251 ) -> Result<String> {
252 let query = args
253 .get("query")
254 .and_then(|v| v.as_str())
255 .ok_or_else(|| anyhow::anyhow!("Missing 'query'"))?;
256 let limit = args
257 .get("limit")
258 .and_then(|v| v.as_u64())
259 .map(|v| v as usize);
260 tools::github_ops::github_search_repos(query, limit).await
261 }
262}
263
264pub struct GithubGetFileTool;
265#[async_trait]
266impl Tool for GithubGetFileTool {
267 fn name(&self) -> &str {
268 "github_get_file"
269 }
270 async fn execute(
271 &self,
272 args: &HashMap<String, Value>,
273 _undo: &mut Vec<UndoAction>,
274 _cwd: Option<&Path>,
275 ) -> Result<String> {
276 let repo = args
277 .get("repo")
278 .and_then(|v| v.as_str())
279 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
280 let path = args
281 .get("path")
282 .and_then(|v| v.as_str())
283 .ok_or_else(|| anyhow::anyhow!("Missing 'path'"))?;
284 let ref_ = args.get("ref").and_then(|v| v.as_str());
285 tools::github_ops::github_get_file(repo, path, ref_).await
286 }
287}
288
289pub struct GithubWorkflowListTool;
290#[async_trait]
291impl Tool for GithubWorkflowListTool {
292 fn name(&self) -> &str {
293 "github_workflow_list"
294 }
295 async fn execute(
296 &self,
297 args: &HashMap<String, Value>,
298 _undo: &mut Vec<UndoAction>,
299 _cwd: Option<&Path>,
300 ) -> Result<String> {
301 let repo = args
302 .get("repo")
303 .and_then(|v| v.as_str())
304 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
305 tools::github_ops::github_workflow_list(repo).await
306 }
307}
308
309pub struct GithubWorkflowRunsTool;
310#[async_trait]
311impl Tool for GithubWorkflowRunsTool {
312 fn name(&self) -> &str {
313 "github_workflow_runs"
314 }
315 async fn execute(
316 &self,
317 args: &HashMap<String, Value>,
318 _undo: &mut Vec<UndoAction>,
319 _cwd: Option<&Path>,
320 ) -> Result<String> {
321 let repo = args
322 .get("repo")
323 .and_then(|v| v.as_str())
324 .ok_or_else(|| anyhow::anyhow!("Missing 'repo'"))?;
325 let workflow_id = args.get("workflow_id").and_then(|v| v.as_str());
326 let limit = args
327 .get("limit")
328 .and_then(|v| v.as_u64())
329 .map(|v| v as usize);
330 tools::github_ops::github_workflow_runs(repo, workflow_id, limit).await
331 }
332}