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.
This commit is contained in:
Riz Ashraf committed 2026-10-10 18:32:02 +01:00
1 parent 11f375017a
commit 760ebfcd40
4 files changed
+48 -15

No files matched your search

+17 -9
View File
@@ -34,8 +34,8 @@ impl McpTool for LogErrorFixHandler {
signature: req.signature.clone(), signature: req.signature.clone(),
solution: solution.clone(), solution: solution.clone(),
timestamp: crate::handlers::utils::now_secs(), timestamp: crate::handlers::utils::now_secs(),
git_commit: req.git_commit, git_commit: req.git_commit.or_else(|| crate::handlers::utils::get_git_context().1),
git_branch: req.git_branch, git_branch: req.git_branch.or_else(|| crate::handlers::utils::get_git_context().0),
embedding, embedding,
..Default::default() ..Default::default()
}); });
@@ -203,17 +203,20 @@ impl McpTool for LogCodeChangeHandler {
_ => crate::models::ChangeKind::Modified, _ => 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 let namespace = req
.namespace .namespace
.filter(|ns| !ns.trim().is_empty()) .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); .unwrap_or_else(crate::models::default_namespace);
let symbols = req.symbol_references.clone().unwrap_or_default(); let symbols = req.symbol_references.clone().unwrap_or_default();
let line_range = req.line_range.clone(); let line_range = req.line_range.clone();
let effective_rev = req.revision.clone().or_else(|| req.git_commit.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()); 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() { let detected_vcs = if let Some(vcs) = req.vcs_type.clone() {
Some(vcs) Some(vcs)
} else if let Some(ref rev) = effective_rev { } else if let Some(ref rev) = effective_rev {
@@ -248,7 +251,7 @@ impl McpTool for LogCodeChangeHandler {
description: description.clone(), description: description.clone(),
git_commit: effective_rev.clone(), git_commit: effective_rev.clone(),
git_branch: effective_branch.clone(), git_branch: effective_branch.clone(),
repo_name: req.repo_name, repo_name: effective_repo,
repo_url: req.repo_url, repo_url: req.repo_url,
namespace: namespace.clone(), namespace: namespace.clone(),
change_kind, change_kind,
@@ -629,10 +632,10 @@ impl McpTool for TechDebtHandler {
ideal_solution: ideal, ideal_solution: ideal,
is_resolved: false, is_resolved: false,
created_at: crate::handlers::utils::now_secs(), created_at: crate::handlers::utils::now_secs(),
git_commit: git_commit, git_commit: git_commit.or_else(|| crate::handlers::utils::get_git_context().1),
git_branch: git_branch, git_branch: git_branch.or_else(|| crate::handlers::utils::get_git_context().0),
embedding, embedding,
repo_name: repo_name, repo_name: repo_name.or_else(|| crate::handlers::utils::get_git_context().2),
severity: severity, severity: severity,
file_path: file_path, file_path: file_path,
line_range: line_range, line_range: line_range,
@@ -1823,6 +1826,8 @@ impl McpTool for AutoSessionCheckpointHandler {
timestamp, active_tasks, unverified_hyp, recent_changes 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 { let memo = crate::models::HandoffMemo {
id: memo_id.clone(), id: memo_id.clone(),
author, author,
@@ -1830,6 +1835,9 @@ impl McpTool for AutoSessionCheckpointHandler {
expires_at: None, expires_at: None,
namespace: req.namespace, namespace: req.namespace,
timestamp, timestamp,
git_branch: auto_branch,
vcs_revision: auto_commit,
repo_name: auto_repo,
..Default::default() ..Default::default()
}; };
+7 -3
View File
@@ -32,6 +32,7 @@ impl McpTool for HandoffMemosHandler {
let ns = req let ns = req
.namespace .namespace
.unwrap_or_else(crate::models::default_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| { state.telemetry.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo { memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(), id: uuid::Uuid::new_v4().to_string(),
@@ -40,6 +41,9 @@ impl McpTool for HandoffMemosHandler {
namespace: ns, namespace: ns,
timestamp: crate::handlers::utils::now_secs(), timestamp: crate::handlers::utils::now_secs(),
expires_at: None, expires_at: None,
git_branch: auto_branch,
vcs_revision: auto_commit,
repo_name: auto_repo,
..Default::default() ..Default::default()
}); });
if memos.len() > 200 { if memos.len() > 200 {
@@ -103,9 +107,9 @@ impl McpTool for AddSessionSummaryHandler {
timestamp: crate::handlers::utils::now_secs(), timestamp: crate::handlers::utils::now_secs(),
expires_at: None, expires_at: None,
session_id: req.session_id, session_id: req.session_id,
repo_name: req.repo_name, repo_name: req.repo_name.or_else(|| crate::handlers::utils::get_git_context().2),
git_branch: req.git_branch, git_branch: req.git_branch.or_else(|| crate::handlers::utils::get_git_context().0),
git_commit: req.git_commit, git_commit: req.git_commit.or_else(|| crate::handlers::utils::get_git_context().1),
tasks_completed: req.tasks_completed.unwrap_or_default(), tasks_completed: req.tasks_completed.unwrap_or_default(),
next_steps: req.next_steps.unwrap_or_default(), next_steps: req.next_steps.unwrap_or_default(),
}); });
+3 -3
View File
@@ -37,13 +37,13 @@ impl McpTool for TasksHandler {
description, description,
created_at: now, created_at: now,
updated_at: now, updated_at: now,
git_branch, git_branch: git_branch.or_else(|| crate::handlers::utils::get_git_context().0),
parent_id, parent_id,
dependencies: deps, dependencies: deps,
acceptance_criteria: vec![], acceptance_criteria: vec![],
expires_at: None, expires_at: None,
namespace: namespace.unwrap_or_else(|| "default".to_string()), namespace: namespace.unwrap_or_else(|| "default".to_string()),
repo_name, repo_name: repo_name.or_else(|| crate::handlers::utils::get_git_context().2),
priority, priority,
assigned_agent, assigned_agent,
verification_command, verification_command,
@@ -430,7 +430,7 @@ impl McpTool for MilestonesHandler {
description: description, description: description,
task_ids: task_ids.unwrap_or_default(), task_ids: task_ids.unwrap_or_default(),
deliverables: deliverables.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() ..Default::default()
}) })
}); });
+21
View File
@@ -57,6 +57,27 @@ pub fn sanitize_path(raw_path: &str) -> String {
raw_path.to_string() raw_path.to_string()
} }
pub fn get_git_context() -> (Option<String>, Option<String>, Option<String>) {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;