Executor-Tyrant-Framework's picture
Dial in Codemine chassis for OpenRouter + HF Spaces deployment
adbf39e
# ---- Changelog ----
# [2026-03-29] Chisel/TQB — Block C: GitTool
# What: push_to_github, pull_from_github, create_shadow_branch extracted from RecursiveContextManager
# Why: PRD Block C — single-responsibility tool classes
# How: Git subprocess calls scoped to repo_path; policy_engine gating for shell execution
# [2026-03-29] Razor/TQB — Block A: Security Hardening
# What: Fixed PolicyEngine integration (check_tool_call returns tuple, not bool)
# Why: Block A/B interface alignment
# How: Use check_tool_call() dispatcher with proper (bool, reason) handling
# -------------------
import logging
import subprocess
import time
from pathlib import Path
logger = logging.getLogger("tools.git")
class GitTool:
"""Git operations scoped to repo_path."""
def __init__(self, repo_path: Path, policy_engine=None):
self.repo_path = repo_path
self.policy_engine = policy_engine
def _check_policy(self, tool_name: str, args: dict) -> tuple:
"""Check PolicyEngine if present. Returns (allowed, reason)."""
if not self.policy_engine:
return True, "No policy engine configured."
from policy_engine import check_tool_call
return check_tool_call(tool_name, args, self.repo_path)
def push_to_github(self, message: str) -> str:
allowed, reason = self._check_policy("push_to_github", {"message": message})
if not allowed:
return {"status": "error", "tool": "git", "error": reason, "type": "PermissionError"}
try:
cwd = str(self.repo_path)
subprocess.run(["git", "config", "user.email", "clawdbot@system.local"], check=False, cwd=cwd)
subprocess.run(["git", "config", "user.name", "Clawdbot"], check=False, cwd=cwd)
subprocess.run(["git", "add", "."], check=True, cwd=cwd)
subprocess.run(["git", "commit", "-m", message], check=True, cwd=cwd)
# Push if remote is configured
result = subprocess.run(
["git", "remote", "get-url", "origin"],
capture_output=True, text=True, cwd=cwd
)
if result.returncode == 0 and result.stdout.strip():
subprocess.run(["git", "push"], check=True, cwd=cwd, capture_output=True, text=True)
return f"Committed and pushed: {message}"
return "Committed locally (no remote configured for push)."
except subprocess.CalledProcessError as e:
logger.error("Git push failed: %s", e)
return {"status": "error", "tool": "git", "error": f"Git error: {e}", "type": "CalledProcessError"}
except OSError as e:
return {"status": "error", "tool": "git", "error": str(e), "type": type(e).__name__}
def pull_from_github(self, branch: str) -> str:
allowed, reason = self._check_policy("pull_from_github", {"branch": branch})
if not allowed:
return {"status": "error", "tool": "git", "error": reason, "type": "PermissionError"}
try:
subprocess.run(["git", "pull", "origin", branch], check=True, cwd=str(self.repo_path))
return f"Pulled {branch}"
except subprocess.CalledProcessError as e:
logger.error("Git pull failed: %s", e)
return {"status": "error", "tool": "git", "error": f"Git pull error: {e}", "type": "CalledProcessError"}
except OSError as e:
return {"status": "error", "tool": "git", "error": str(e), "type": type(e).__name__}
def create_shadow_branch(self) -> str:
allowed, reason = self._check_policy("create_shadow_branch", {})
if not allowed:
return {"status": "error", "tool": "git", "error": reason, "type": "PermissionError"}
ts = int(time.time())
try:
subprocess.run(["git", "checkout", "-b", f"shadow_{ts}"], check=True, cwd=str(self.repo_path))
return f"Created branch shadow_{ts}"
except subprocess.CalledProcessError as e:
logger.error("Shadow branch creation failed: %s", e)
return {"status": "error", "tool": "git", "error": f"Git error: {e}", "type": "CalledProcessError"}
except OSError as e:
return {"status": "error", "tool": "git", "error": str(e), "type": type(e).__name__}