Spaces:
Runtime error
Runtime error
| """ | |
| GitHub MCP Integration — Autonomous Repository Operations | |
| ========================================================== | |
| Create repos, commit files, open PRs via GitHub REST API. | |
| """ | |
| import json, os, base64 | |
| from typing import Dict | |
| from pathlib import Path | |
| import httpx | |
| class GitHubMCP: | |
| def __init__(self): | |
| self.token = os.environ.get("GITHUB_TOKEN") or os.environ.get("HF_TOKEN") | |
| self.base_url = "https://api.github.com" | |
| self.headers = {"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28"} | |
| if self.token: | |
| self.headers["Authorization"] = f"Bearer {self.token}" | |
| async def create_and_push(self, repo_name: str, files: Dict[str, str], description: str = "", private: bool = False) -> Dict: | |
| result = {"success": False, "repo_url": None, "files_committed": [], "error": None} | |
| if not self.token: | |
| local_dir = Path("/app/wizard-vibe/generated") / repo_name | |
| local_dir.mkdir(parents=True, exist_ok=True) | |
| for path, content in files.items(): | |
| fp = local_dir / path; fp.parent.mkdir(parents=True, exist_ok=True); fp.write_text(content) | |
| result["files_committed"].append(str(fp)) | |
| result.update({"success": True, "local_path": str(local_dir), "note": "No GITHUB_TOKEN — saved locally"}) | |
| return result | |
| try: | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| user_resp = await client.get(f"{self.base_url}/user", headers=self.headers) | |
| owner = user_resp.json()["login"] | |
| create = await client.post(f"{self.base_url}/user/repos", headers=self.headers, json={"name": repo_name, "description": description or "Wizard-Vibe Studio", "private": private, "auto_init": True}) | |
| if create.status_code == 201: | |
| result["repo_url"] = create.json()["html_url"] | |
| elif create.status_code == 422: | |
| repos = (await client.get(f"{self.base_url}/user/repos", headers=self.headers)).json() | |
| for r in repos: | |
| if r["name"] == repo_name: | |
| result["repo_url"] = r["html_url"]; break | |
| result["success"] = True | |
| for path, content in files.items(): | |
| resp = await client.put( | |
| f"{self.base_url}/repos/{owner}/{repo_name}/contents/{path}", | |
| headers=self.headers, | |
| json={"message": f"✨ Wizard-Vibe: Add {path}", "content": base64.b64encode(content.encode()).decode()}, | |
| ) | |
| if resp.status_code in (201, 200): | |
| result["files_committed"].append(path) | |
| except Exception as e: | |
| result["error"] = str(e) | |
| return result | |
| async def push_file(self, repo_name: str, path: str, content: str) -> Dict: | |
| if not self.token: return {"success": False, "error": "No token"} | |
| try: | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| user_resp = await client.get(f"{self.base_url}/user", headers=self.headers) | |
| owner = user_resp.json()["login"] | |
| resp = await client.put( | |
| f"{self.base_url}/repos/{owner}/{repo_name}/contents/{path}", | |
| headers=self.headers, | |
| json={"message": f"✨ Wizard-Vibe: Add {path}", "content": base64.b64encode(content.encode()).decode()}, | |
| ) | |
| return {"success": resp.status_code in (201, 200)} | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| async def create_pr(self, repo_name: str, title: str, body: str = "", head: str = "wizard-vibe", base: str = "main") -> Dict: | |
| if not self.token: return {"success": False, "error": "No token"} | |
| try: | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| user_resp = await client.get(f"{self.base_url}/user", headers=self.headers) | |
| owner = user_resp.json()["login"] | |
| resp = await client.post( | |
| f"{self.base_url}/repos/{owner}/{repo_name}/pulls", | |
| headers=self.headers, | |
| json={"title": title, "body": body or "PR by Wizard-Vibe", "head": head, "base": base}, | |
| ) | |
| if resp.status_code == 201: | |
| d = resp.json() | |
| return {"success": True, "pr_url": d["html_url"], "pr_number": d["number"]} | |
| return {"success": False, "error": resp.text} | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |