feat: tools list_commits et get_commit (historique git)
This commit is contained in:
@@ -139,6 +139,34 @@ TOOLS = [
|
|||||||
"required": ["repo", "path"],
|
"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"),
|
"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 = {
|
DISPATCH = {
|
||||||
"list_repos": tool_list_repos,
|
"list_repos": tool_list_repos,
|
||||||
"list_files": tool_list_files,
|
"list_files": tool_list_files,
|
||||||
"read_file": tool_read_file,
|
"read_file": tool_read_file,
|
||||||
"get_metadata": tool_get_metadata,
|
"get_metadata": tool_get_metadata,
|
||||||
|
"list_commits": tool_list_commits,
|
||||||
|
"get_commit": tool_get_commit,
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user