feat: tools list_commits et get_commit (historique git)

This commit is contained in:
Bastien Gourdon
2026-07-21 18:54:45 +02:00
parent f18e9ad96f
commit e1dd9e5588
+111
View File
@@ -139,6 +139,34 @@ TOOLS = [
"required": ["repo", "path"],
},
},
{
"name": "list_commits",
"description": "Historique des commits d un depot (sha, auteur, date, message). path optionnel : limite l historique a un fichier ou dossier precis. Ne retourne pas les diffs.",
"inputSchema": {
"type": "object",
"properties": {
"repo": {"type": "string", "description": "proprietaire/depot"},
"path": {"type": "string", "description": "Optionnel - limite l historique a ce fichier ou dossier"},
"ref": {"type": "string", "description": "Branche, tag ou commit (defaut: branche par defaut)"},
"limit": {"type": "integer", "description": "Nombre de commits (defaut 20, max 50)"},
},
"required": ["repo"],
},
},
{
"name": "get_commit",
"description": "Detail d un commit : metadonnees, fichiers touches et statistiques. include_diff=true ajoute le diff complet (refuse au-dela de la limite de taille).",
"inputSchema": {
"type": "object",
"properties": {
"repo": {"type": "string", "description": "proprietaire/depot"},
"sha": {"type": "string", "description": "SHA du commit (complet ou abrege)"},
"include_diff": {"type": "boolean", "description": "Inclure le diff complet (defaut false)"},
},
"required": ["repo", "sha"],
},
},
]
# ---------------------------------------------------------------------------
@@ -241,12 +269,95 @@ def tool_get_metadata(repo, path, ref=None):
"download_url": data.get("download_url"),
}
MAX_DIFF_BYTES = 100000 # garde-fou diff : au-dela, on renvoie seulement les stats
def tool_list_commits(repo, path=None, ref=None, limit=20):
err = _valider_repo(repo) or _valider_path(path)
if err:
return err
try:
limit = int(limit)
except (TypeError, ValueError):
return {"error": "limit doit etre un entier"}
limit = max(1, min(limit, 50))
params = {"limit": limit, "stat": "false", "verification": "false", "files": "false"}
if path:
params["path"] = path.lstrip("/")
if ref:
params["sha"] = ref
data = forgejo_api("GET", "/api/v1/repos/%s/commits" % repo, params=params) or []
out = []
for c in data:
commit = c.get("commit") or {}
author = commit.get("author") or {}
message = (commit.get("message") or "").strip()
parts = message.split("\n", 1)
out.append({
"sha": (c.get("sha") or "")[:10],
"sha_full": c.get("sha"),
"date": author.get("date"),
"author": author.get("name"),
"subject": parts[0],
"body": parts[1].strip() if len(parts) > 1 else "",
})
return {"repo": repo, "path": path or "", "count": len(out), "commits": out}
def tool_get_commit(repo, sha, include_diff=False):
err = _valider_repo(repo)
if err:
return err
if not sha or "/" in sha or ".." in sha:
return {"error": "sha invalide"}
data = forgejo_api("GET", "/api/v1/repos/%s/git/commits/%s" % (repo, sha))
if not data:
return {"error": "Commit introuvable : %s" % sha}
commit = data.get("commit") or {}
author = commit.get("author") or {}
stats = data.get("stats") or {}
message = (commit.get("message") or "").strip()
parts = message.split("\n", 1)
result = {
"repo": repo,
"sha": data.get("sha"),
"date": author.get("date"),
"author": author.get("name"),
"subject": parts[0],
"body": parts[1].strip() if len(parts) > 1 else "",
"stats": {"additions": stats.get("additions"),
"deletions": stats.get("deletions"),
"total": stats.get("total")},
"files": [{"filename": f.get("filename"),
"status": f.get("status"),
"additions": f.get("additions"),
"deletions": f.get("deletions")}
for f in (data.get("files") or [])],
}
if include_diff:
url = "%s/api/v1/repos/%s/git/commits/%s.diff" % (FORGEJO_URL.rstrip("/"), repo, sha)
resp = requests.get(url, headers={"Authorization": "token %s" % FORGEJO_TOKEN}, timeout=30)
resp.raise_for_status()
raw = resp.content
if len(raw) > MAX_DIFF_BYTES:
result["diff_error"] = ("Diff trop volumineux (%d octets, limite %d). "
"Utiliser read_file sur les fichiers concernes."
% (len(raw), MAX_DIFF_BYTES))
else:
try:
result["diff"] = raw.decode("utf-8")
except UnicodeDecodeError:
result["diff_error"] = "Diff non decodable en UTF-8 (fichiers binaires)"
return result
DISPATCH = {
"list_repos": tool_list_repos,
"list_files": tool_list_files,
"read_file": tool_read_file,
"get_metadata": tool_get_metadata,
"list_commits": tool_list_commits,
"get_commit": tool_get_commit,
}
# ---------------------------------------------------------------------------