1+ #!/usr/bin/env python3
2+ """Grok PR reviewer (Hermes proxy) for Agent Office."""
3+ import json
4+ import os
5+ import sys
6+ import urllib .error
7+ import urllib .request
8+
9+ API = "https://api.github.com"
10+ MARKER = "<!-- grok-review -->"
11+ MODEL = os .environ .get ("GROK_MODEL" , "grok-4.3" )
12+ DIFF_LIMIT = 50_000
13+
14+ DEFAULT_PROMPT = """You are reviewing a pull request for Agent Office (VS Code extension + pixel-agent webview). Write ONE concise review comment in Markdown. Use `file:line` references. Do not nitpick formatting. If it is clean, say so plainly.
15+
16+ Check, in priority order:
17+
18+ 1. SECURITY — hook endpoint auth, unsafe file reads/writes, secret leakage, webview message-trust mistakes, path traversal in asset loading.
19+ 2. CORRECTNESS — terminal adoption, transcript parsing, layout/config persistence, server lifecycle, cross-window sync regressions.
20+ 3. EXTENSION UX — broken webview messaging, animation/state bugs, packaging/manifest issues, docs drift from actual behavior.
21+
22+ End with a single final line: `Verdict: <one sentence>`.
23+
24+ Here is the unified diff:
25+
26+ ```diff
27+ {diff}
28+ ```"""
29+
30+ PROMPT = os .environ .get ("GROK_REVIEW_PROMPT" , DEFAULT_PROMPT )
31+
32+
33+ def req (method , url , token , body = None , accept = "application/vnd.github+json" ):
34+ headers = {"Authorization" : f"Bearer { token } " , "Accept" : accept ,
35+ "User-Agent" : "grok-review" }
36+ data = None
37+ if body is not None :
38+ data = json .dumps (body ).encode ()
39+ headers ["Content-Type" ] = "application/json"
40+ r = urllib .request .Request (url , data = data , headers = headers , method = method )
41+ with urllib .request .urlopen (r , timeout = 120 ) as resp :
42+ return resp .status , resp .read ()
43+
44+
45+ def get_diff (repo , pr , token ):
46+ _ , raw = req ("GET" , f"{ API } /repos/{ repo } /pulls/{ pr } " , token ,
47+ accept = "application/vnd.github.v3.diff" )
48+ return raw .decode ("utf-8" , "replace" )
49+
50+
51+ def call_grok (url , diff ):
52+ body = {
53+ "model" : MODEL ,
54+ "max_tokens" : 1800 ,
55+ "stream" : False ,
56+ "messages" : [{"role" : "user" , "content" : PROMPT .format (diff = diff )}],
57+ }
58+ r = urllib .request .Request (
59+ url , data = json .dumps (body ).encode (),
60+ headers = {"Content-Type" : "application/json" ,
61+ "anthropic-version" : "2023-06-01" },
62+ method = "POST" )
63+ with urllib .request .urlopen (r , timeout = 180 ) as resp :
64+ payload = json .loads (resp .read ())
65+ parts = [b .get ("text" , "" ) for b in payload .get ("content" , [])
66+ if b .get ("type" ) == "text" ]
67+ return "" .join (parts ).strip ()
68+
69+
70+ def upsert_comment (repo , pr , token , text ):
71+ body_md = f"{ MARKER } \n ## 🔎 Grok review (Hermes · grok-4.3)\n \n { text } "
72+ _ , raw = req ("GET" , f"{ API } /repos/{ repo } /issues/{ pr } /comments?per_page=100" , token )
73+ for c in json .loads (raw ):
74+ if MARKER in (c .get ("body" ) or "" ):
75+ req ("PATCH" , f"{ API } /repos/{ repo } /issues/comments/{ c ['id' ]} " , token ,
76+ body = {"body" : body_md })
77+ return "updated"
78+ req ("POST" , f"{ API } /repos/{ repo } /issues/{ pr } /comments" , token ,
79+ body = {"body" : body_md })
80+ return "created"
81+
82+
83+ def main ():
84+ token = os .environ ["GITHUB_TOKEN" ]
85+ repo = os .environ ["REPO" ]
86+ pr = os .environ ["PR_NUMBER" ]
87+ url = os .environ ["GROK_URL" ]
88+
89+ diff = get_diff (repo , pr , token )
90+ if not diff .strip ():
91+ print ("empty diff; nothing to review" )
92+ return
93+ truncated = len (diff ) > DIFF_LIMIT
94+ if truncated :
95+ diff = diff [:DIFF_LIMIT ] + "\n \n [... diff truncated for length ...]"
96+
97+ try :
98+ review = call_grok (url , diff )
99+ except urllib .error .HTTPError as e :
100+ print (f"grok proxy error { e .code } : { e .read ()[:300 ]!r} " , file = sys .stderr )
101+ sys .exit (1 )
102+ if not review :
103+ print ("model returned no text" , file = sys .stderr )
104+ sys .exit (1 )
105+ if truncated :
106+ review += "\n \n _Note: the diff was truncated; review covers the first part only._"
107+
108+ action = upsert_comment (repo , pr , token , review )
109+ print (f"comment { action } " )
110+
111+
112+ if __name__ == "__main__" :
113+ main ()
0 commit comments