| """git_log tool — read-only commit history queries.""" |
| from __future__ import annotations |
| import subprocess |
| from pathlib import Path |
|
|
| from .base import ToolResult, ToolSpec |
|
|
|
|
| def make_tool(repo_root: str | Path, max_commits: int = 50) -> ToolSpec: |
| root = Path(repo_root).resolve() |
|
|
| def run(path: str = "", limit: int = 20, since: str = "") -> ToolResult: |
| if not (root / ".git").exists(): |
| return ToolResult(ok=False, output="", error="not a git repository") |
| n = max(1, min(limit, max_commits)) |
| cmd = ["git", "log", f"-n{n}", "--pretty=format:%h|%an|%ai|%s"] |
| if since: |
| cmd += [f"--since={since}"] |
| if path: |
| cmd += ["--", path] |
| try: |
| proc = subprocess.run(cmd, cwd=str(root), capture_output=True, text=True, timeout=30) |
| except subprocess.TimeoutExpired: |
| return ToolResult(ok=False, output="", error="git log timeout") |
| if proc.returncode != 0: |
| return ToolResult(ok=False, output="", error=proc.stderr.strip()) |
| rows = [_format_row(line) for line in proc.stdout.splitlines() if line] |
| return ToolResult(ok=True, output="\n".join(rows), extra={"commits": len(rows)}) |
|
|
| return ToolSpec( |
| name="git_log", |
| description="Read commit history. Optionally filter by path or --since.", |
| parameters={ |
| "type": "object", |
| "properties": { |
| "path": {"type": "string", "default": ""}, |
| "limit": {"type": "integer", "default": 20}, |
| "since": {"type": "string", "default": "", "description": "git --since (e.g. '2 weeks ago')"}, |
| }, |
| }, |
| runner=run, |
| ) |
|
|
|
|
| def _format_row(line: str) -> str: |
| parts = line.split("|", 3) |
| if len(parts) != 4: |
| return line |
| sha, author, date, subject = parts |
| return f"{sha[:9]} {date[:10]} {author[:24]:<24} {subject}" |
|
|