From d79ebf7d3644a33a91f786187e527d0e7c9460c2 Mon Sep 17 00:00:00 2001 From: ALI YESILKAYA Date: Fri, 5 Jun 2026 11:56:56 +0200 Subject: [PATCH] fix(hermes-auto-improve): squash merge + correction PR existante dans create_github_pr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GitHub repo configuré squash-only (allow_merge_commit=false, allow_rebase_merge=false) - create_github_pr() vérifie si une PR hermes/daily-work est déjà ouverte : si oui → met à jour titre+body (pas de PR dupliquée) si non → crée une nouvelle PR - Supprime le bug de silence sur "a pull request for branch X already exists" Co-Authored-By: Claude Sonnet 4.6 --- tools/hermes-auto-improve/auto-improve.py | 55 ++++++++++++++++----- tools/hermes-auto-improve/trigger-server.py | 28 ++++++++--- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/tools/hermes-auto-improve/auto-improve.py b/tools/hermes-auto-improve/auto-improve.py index 2cd6fc9..6b45a45 100644 --- a/tools/hermes-auto-improve/auto-improve.py +++ b/tools/hermes-auto-improve/auto-improve.py @@ -125,12 +125,29 @@ def rag_ingest() -> str: # ── Créer la PR GitHub ─────────────────────────────────────────── +def _get_open_hermes_pr(): + """Retourne le numéro et l'URL de la PR Hermes ouverte, ou None.""" + r = subprocess.run( + ["gh", "pr", "list", "--repo", "Alkatrazz24/Funk-lab", + "--head", BRANCH, "--state", "open", "--json", "number,url"], + capture_output=True, text=True, timeout=30, cwd=str(REPO_DIR) + ) + if r.returncode != 0: + return None + try: + prs = json.loads(r.stdout) + return prs[0] if prs else None + except (json.JSONDecodeError, IndexError): + return None + + def create_github_pr(summary: str) -> dict: today = datetime.now().strftime("%Y-%m-%d") diff = git(["diff", "--stat", "origin/main...HEAD"]) stats = diff.stdout.strip() or "Aucune modification depuis main" - body = f"""## Rapport quotidien Hermes — {today} + title = f"[Hermes] Rapport documentation — {today}" + body = f"""## Rapport quotidien Hermes — {today} Ce Pull Request est créé automatiquement par Hermes à 22h. @@ -144,26 +161,38 @@ Ce Pull Request est créé automatiquement par Hermes à 22h. ### Comment reviewer 1. Vérifier les rapports dans `admin/hermes/builtin/` -2. Merger si tout semble correct, ou fermer pour recommencer demain +2. Merger quand tout semble correct (squash merge configuré) > Généré par `hermes-doc-worker` · branche `{BRANCH}` """ + existing = _get_open_hermes_pr() + if existing: + # Met à jour le titre et le corps de la PR existante + subprocess.run( + ["gh", "pr", "edit", str(existing["number"]), + "--repo", "Alkatrazz24/Funk-lab", + "--title", title, + "--body", body], + capture_output=True, text=True, timeout=30, cwd=str(REPO_DIR) + ) + return {"html_url": existing["url"], + "number": existing["number"], + "title": title, + "updated": True} + r = subprocess.run( - [ - "gh", "pr", "create", - "--title", f"[Hermes] Rapport documentation — {today}", - "--body", body, - "--head", BRANCH, - "--base", "main", - "--repo", "Alkatrazz24/Funk-lab", - ], - capture_output=True, text=True, timeout=30, - cwd=str(REPO_DIR), + ["gh", "pr", "create", + "--title", title, + "--body", body, + "--head", BRANCH, + "--base", "main", + "--repo", "Alkatrazz24/Funk-lab"], + capture_output=True, text=True, timeout=30, cwd=str(REPO_DIR) ) if r.returncode == 0: pr_url = r.stdout.strip() - return {"html_url": pr_url, "title": f"[Hermes] Rapport documentation — {today}"} + return {"html_url": pr_url, "title": title} return {"error": r.stderr.strip() or "gh pr create a échoué"} diff --git a/tools/hermes-auto-improve/trigger-server.py b/tools/hermes-auto-improve/trigger-server.py index 493dd8d..5e7cb0a 100644 --- a/tools/hermes-auto-improve/trigger-server.py +++ b/tools/hermes-auto-improve/trigger-server.py @@ -31,6 +31,25 @@ _running = False _last_result = None +def _extract_last_json(text: str): + """Find the outermost JSON object at the end of mixed text+JSON output.""" + end = text.rfind("}") + if end < 0: + return None + depth = 0 + for i in range(end, -1, -1): + if text[i] == "}": + depth += 1 + elif text[i] == "{": + depth -= 1 + if depth == 0: + try: + return json.loads(text[i:end + 1]) + except json.JSONDecodeError: + return None + return None + + def run_script(extra_args: list = None): global _running, _last_result with _lock: @@ -50,13 +69,8 @@ def run_script(extra_args: list = None): if result.stderr: log.write(f"STDERR: {result.stderr}") - out = result.stdout - start = out.rfind("{") - end = out.rfind("}") + 1 - if start >= 0 and end > start: - _last_result = json.loads(out[start:end]) - else: - _last_result = {"error": "no_json", "stdout": out[-500:]} + out = result.stdout + _last_result = _extract_last_json(out) or {"error": "no_json", "stdout": out[-500:]} except subprocess.TimeoutExpired: _last_result = {"error": "timeout"}