From 760ebfcd409de75e0621329fbf7fb5b3ab9146ab Mon Sep 17 00:00:00 2001 From: Riz Ashraf Date: Sat, 10 Oct 2026 18:32:02 +0100 Subject: [PATCH] perf: auto-populate git context natively to avoid token waste - Added get_git_context() to utils.rs using git2 to natively fetch branch, commit, and repo_name without CLI overhead. - Wired native auto-population into LogCodeChangeHandler, LogErrorFixHandler, TechDebtHandler, TasksHandler, MilestonesHandler, AutoSessionCheckpointHandler, and HandoffMemosHandler. - Eliminates the need for the LLM to waste repetitive background turns running git rev-parse HEAD and git branch --show-current via terminal shell commands just to feed the MCP logging tools. --- server/src/handlers/meta.rs | 26 +++++++++++++++++--------- server/src/handlers/notes.rs | 10 +++++++--- server/src/handlers/tasks.rs | 6 +++--- server/src/handlers/utils.rs | 21 +++++++++++++++++++++ 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/server/src/handlers/meta.rs b/server/src/handlers/meta.rs index 244a389..ad2f7a3 100644 --- a/server/src/handlers/meta.rs +++ b/server/src/handlers/meta.rs @@ -34,8 +34,8 @@ impl McpTool for LogErrorFixHandler { signature: req.signature.clone(), solution: solution.clone(), timestamp: crate::handlers::utils::now_secs(), - git_commit: req.git_commit, - git_branch: req.git_branch, + git_commit: req.git_commit.or_else(|| crate::handlers::utils::get_git_context().1), + git_branch: req.git_branch.or_else(|| crate::handlers::utils::get_git_context().0), embedding, ..Default::default() }); @@ -203,17 +203,20 @@ impl McpTool for LogCodeChangeHandler { _ => crate::models::ChangeKind::Modified, }; + let (auto_branch, auto_commit, auto_repo) = crate::handlers::utils::get_git_context(); + let effective_repo = req.repo_name.clone().filter(|rn| !rn.trim().is_empty()).or(auto_repo.clone()); + let namespace = req .namespace .filter(|ns| !ns.trim().is_empty()) - .or_else(|| req.repo_name.clone().filter(|rn| !rn.trim().is_empty())) + .or_else(|| effective_repo.clone()) .unwrap_or_else(crate::models::default_namespace); let symbols = req.symbol_references.clone().unwrap_or_default(); let line_range = req.line_range.clone(); - let effective_rev = req.revision.clone().or_else(|| req.git_commit.clone()); - let effective_branch = req.branch.clone().or_else(|| req.git_branch.clone()); + let effective_rev = req.revision.clone().or_else(|| req.git_commit.clone()).or(auto_commit); + let effective_branch = req.branch.clone().or_else(|| req.git_branch.clone()).or(auto_branch); let detected_vcs = if let Some(vcs) = req.vcs_type.clone() { Some(vcs) } else if let Some(ref rev) = effective_rev { @@ -248,7 +251,7 @@ impl McpTool for LogCodeChangeHandler { description: description.clone(), git_commit: effective_rev.clone(), git_branch: effective_branch.clone(), - repo_name: req.repo_name, + repo_name: effective_repo, repo_url: req.repo_url, namespace: namespace.clone(), change_kind, @@ -629,10 +632,10 @@ impl McpTool for TechDebtHandler { ideal_solution: ideal, is_resolved: false, created_at: crate::handlers::utils::now_secs(), - git_commit: git_commit, - git_branch: git_branch, + git_commit: git_commit.or_else(|| crate::handlers::utils::get_git_context().1), + git_branch: git_branch.or_else(|| crate::handlers::utils::get_git_context().0), embedding, - repo_name: repo_name, + repo_name: repo_name.or_else(|| crate::handlers::utils::get_git_context().2), severity: severity, file_path: file_path, line_range: line_range, @@ -1823,6 +1826,8 @@ impl McpTool for AutoSessionCheckpointHandler { timestamp, active_tasks, unverified_hyp, recent_changes ); + let (auto_branch, auto_commit, auto_repo) = crate::handlers::utils::get_git_context(); + let memo = crate::models::HandoffMemo { id: memo_id.clone(), author, @@ -1830,6 +1835,9 @@ impl McpTool for AutoSessionCheckpointHandler { expires_at: None, namespace: req.namespace, timestamp, + git_branch: auto_branch, + vcs_revision: auto_commit, + repo_name: auto_repo, ..Default::default() }; diff --git a/server/src/handlers/notes.rs b/server/src/handlers/notes.rs index cb44e15..f3f5c44 100644 --- a/server/src/handlers/notes.rs +++ b/server/src/handlers/notes.rs @@ -32,6 +32,7 @@ impl McpTool for HandoffMemosHandler { let ns = req .namespace .unwrap_or_else(crate::models::default_namespace); + let (auto_branch, auto_commit, auto_repo) = crate::handlers::utils::get_git_context(); state.telemetry.handoff_memos.modify(|memos| { memos.push(crate::models::HandoffMemo { id: uuid::Uuid::new_v4().to_string(), @@ -40,6 +41,9 @@ impl McpTool for HandoffMemosHandler { namespace: ns, timestamp: crate::handlers::utils::now_secs(), expires_at: None, + git_branch: auto_branch, + vcs_revision: auto_commit, + repo_name: auto_repo, ..Default::default() }); if memos.len() > 200 { @@ -103,9 +107,9 @@ impl McpTool for AddSessionSummaryHandler { timestamp: crate::handlers::utils::now_secs(), expires_at: None, session_id: req.session_id, - repo_name: req.repo_name, - git_branch: req.git_branch, - git_commit: req.git_commit, + repo_name: req.repo_name.or_else(|| crate::handlers::utils::get_git_context().2), + git_branch: req.git_branch.or_else(|| crate::handlers::utils::get_git_context().0), + git_commit: req.git_commit.or_else(|| crate::handlers::utils::get_git_context().1), tasks_completed: req.tasks_completed.unwrap_or_default(), next_steps: req.next_steps.unwrap_or_default(), }); diff --git a/server/src/handlers/tasks.rs b/server/src/handlers/tasks.rs index 939916f..264b03c 100644 --- a/server/src/handlers/tasks.rs +++ b/server/src/handlers/tasks.rs @@ -37,13 +37,13 @@ impl McpTool for TasksHandler { description, created_at: now, updated_at: now, - git_branch, + git_branch: git_branch.or_else(|| crate::handlers::utils::get_git_context().0), parent_id, dependencies: deps, acceptance_criteria: vec![], expires_at: None, namespace: namespace.unwrap_or_else(|| "default".to_string()), - repo_name, + repo_name: repo_name.or_else(|| crate::handlers::utils::get_git_context().2), priority, assigned_agent, verification_command, @@ -430,7 +430,7 @@ impl McpTool for MilestonesHandler { description: description, task_ids: task_ids.unwrap_or_default(), deliverables: deliverables.unwrap_or_default(), - repo_name, + repo_name: repo_name.or_else(|| crate::handlers::utils::get_git_context().2), ..Default::default() }) }); diff --git a/server/src/handlers/utils.rs b/server/src/handlers/utils.rs index 44c425f..97bbc31 100644 --- a/server/src/handlers/utils.rs +++ b/server/src/handlers/utils.rs @@ -57,6 +57,27 @@ pub fn sanitize_path(raw_path: &str) -> String { raw_path.to_string() } +pub fn get_git_context() -> (Option, Option, Option) { + let mut branch = None; + let mut commit = None; + let mut repo_name = None; + + if let Ok(cwd) = std::env::current_dir() { + if let Ok(repo) = git2::Repository::discover(&cwd) { + if let Ok(head) = repo.head() { + branch = head.shorthand().map(|s| s.to_string()); + commit = head.target().map(|oid| oid.to_string()); + } + if let Some(path) = repo.workdir() { + if let Some(name) = path.file_name() { + repo_name = name.to_str().map(|s| s.to_string()); + } + } + } + } + (branch, commit, repo_name) +} + #[cfg(test)] mod tests { use super::*;