feat: tools list_commits et get_commit (historique git)
This commit is contained in:
@@ -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,
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user