wizard-vibe-studio / github_mcp.py
dryymatt's picture
✨ Wizard-Vibe: github_mcp.py
991d295 verified
"""
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)}