Skip to main content

ararajuba_tools_coding/git/
commit.rs

1//! `git_commit` tool — commit staged changes.
2
3use ararajuba_core::tools::tool::{tool, ToolDef};
4use git2::Repository;
5use serde_json::json;
6
7/// Create the `git_commit` tool.
8///
9/// Commits currently staged changes using the supplied message.
10/// Reads author/email from git config.
11pub fn git_commit_tool() -> ToolDef {
12    tool("git_commit")
13        .description("Commit staged changes with a message.")
14        .input_schema(json!({
15            "type": "object",
16            "properties": {
17                "message": { "type": "string", "description": "Commit message" }
18            },
19            "required": ["message"]
20        }))
21        .execute(|input| async move {
22            let message = input["message"]
23                .as_str()
24                .ok_or_else(|| "missing required field: message".to_string())?;
25
26            let repo = Repository::discover(".")
27                .map_err(|e| format!("failed to open repository: {e}"))?;
28
29            let sig = repo
30                .signature()
31                .map_err(|e| format!("failed to get signature (set user.name/email): {e}"))?;
32
33            let mut index = repo
34                .index()
35                .map_err(|e| format!("failed to get index: {e}"))?;
36
37            let tree_id = index
38                .write_tree()
39                .map_err(|e| format!("failed to write tree: {e}"))?;
40
41            let tree = repo
42                .find_tree(tree_id)
43                .map_err(|e| format!("failed to find tree: {e}"))?;
44
45            // Get parent commit (if any — handles initial commit)
46            let parents: Vec<git2::Commit> = if let Ok(head) = repo.head() {
47                vec![head
48                    .peel_to_commit()
49                    .map_err(|e| format!("failed to peel HEAD to commit: {e}"))?]
50            } else {
51                vec![]
52            };
53            let parent_refs: Vec<&git2::Commit> = parents.iter().collect();
54
55            let oid = repo
56                .commit(Some("HEAD"), &sig, &sig, message, &tree, &parent_refs)
57                .map_err(|e| format!("failed to commit: {e}"))?;
58
59            let hash = oid.to_string();
60            let short = &hash[..7.min(hash.len())];
61
62            Ok(json!({
63                "hash": hash,
64                "short_hash": short
65            }))
66        })
67        .build()
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn tool_metadata() {
76        let t = git_commit_tool();
77        assert_eq!(t.name, "git_commit");
78        assert!(t.execute.is_some());
79    }
80}