ararajuba_tools_coding/git/
add.rs1use ararajuba_core::tools::tool::{tool, ToolDef};
4use git2::Repository;
5use serde_json::json;
6use std::path::Path;
7
8pub fn git_add_tool() -> ToolDef {
12 tool("git_add")
13 .description("Stage files for commit. Use [\".\"] to stage all changes.")
14 .input_schema(json!({
15 "type": "object",
16 "properties": {
17 "files": {
18 "type": "array",
19 "items": { "type": "string" },
20 "description": "Files to stage (relative to repo root)"
21 }
22 },
23 "required": ["files"]
24 }))
25 .execute(|input| async move {
26 let files = input["files"]
27 .as_array()
28 .ok_or_else(|| "missing required field: files".to_string())?;
29
30 let repo = Repository::discover(".")
31 .map_err(|e| format!("failed to open repository: {e}"))?;
32
33 let mut index = repo
34 .index()
35 .map_err(|e| format!("failed to get index: {e}"))?;
36
37 let mut staged = Vec::new();
38
39 for f in files {
40 let file_str = f
41 .as_str()
42 .ok_or_else(|| "file entry must be a string".to_string())?;
43
44 if file_str == "." {
45 index
46 .add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)
47 .map_err(|e| format!("failed to add all: {e}"))?;
48 staged.push(".".to_string());
49 } else {
50 index
51 .add_path(Path::new(file_str))
52 .map_err(|e| format!("failed to add {file_str}: {e}"))?;
53 staged.push(file_str.to_string());
54 }
55 }
56
57 index
58 .write()
59 .map_err(|e| format!("failed to write index: {e}"))?;
60
61 Ok(json!({ "staged": staged }))
62 })
63 .build()
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn tool_metadata() {
72 let t = git_add_tool();
73 assert_eq!(t.name, "git_add");
74 assert!(t.execute.is_some());
75 }
76}