ararajuba_tools_coding/git/
status.rs1use ararajuba_core::tools::tool::{tool, ToolDef};
4use git2::{Repository, StatusOptions};
5use serde_json::json;
6
7pub fn git_status_tool() -> ToolDef {
11 tool("git_status")
12 .description("Show git repository status: branch, staged, unstaged, and untracked files.")
13 .input_schema(json!({
14 "type": "object",
15 "properties": {
16 "path": { "type": "string", "description": "Repository path (default: current dir)" }
17 }
18 }))
19 .execute(|input| async move {
20 let path = input["path"].as_str().unwrap_or(".");
21
22 let repo = Repository::discover(path)
23 .map_err(|e| format!("failed to open repository: {e}"))?;
24
25 let branch = repo
27 .head()
28 .ok()
29 .and_then(|h| h.shorthand().map(String::from))
30 .unwrap_or_else(|| "HEAD (detached)".into());
31
32 let mut opts = StatusOptions::new();
33 opts.include_untracked(true)
34 .recurse_untracked_dirs(true);
35
36 let statuses = repo
37 .statuses(Some(&mut opts))
38 .map_err(|e| format!("failed to get status: {e}"))?;
39
40 let mut staged = Vec::new();
41 let mut unstaged = Vec::new();
42 let mut untracked = Vec::new();
43
44 for entry in statuses.iter() {
45 let path_str = entry.path().unwrap_or("").to_string();
46 let s = entry.status();
47
48 if s.is_index_new() {
49 staged.push(json!({"path": path_str, "status": "added"}));
50 } else if s.is_index_modified() {
51 staged.push(json!({"path": path_str, "status": "modified"}));
52 } else if s.is_index_deleted() {
53 staged.push(json!({"path": path_str, "status": "deleted"}));
54 }
55
56 if s.is_wt_modified() {
57 unstaged.push(json!({"path": path_str, "status": "modified"}));
58 } else if s.is_wt_deleted() {
59 unstaged.push(json!({"path": path_str, "status": "deleted"}));
60 }
61
62 if s.is_wt_new() {
63 untracked.push(json!(path_str));
64 }
65 }
66
67 Ok(json!({
68 "branch": branch,
69 "staged": staged,
70 "unstaged": unstaged,
71 "untracked": untracked
72 }))
73 })
74 .build()
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn tool_metadata() {
83 let t = git_status_tool();
84 assert_eq!(t.name, "git_status");
85 assert!(t.execute.is_some());
86 assert!(t.needs_approval.is_none());
87 }
88}