@@ -45,6 +45,35 @@ class Colors:
4545- MEDIUM: Code smells, potential bugs, missing error handling
4646- LOW: Minor improvements, suggestions, style inconsistencies"""
4747
48+ DOCUMENTATION_PROMPT = """Documentation review: you are a technical writer reviewing a git diff for documentation impact.
49+
50+ Output ONLY valid JSON - no markdown, no code blocks, no explanations.
51+
52+ Identify whether the diff changes user-facing behavior, setup steps, configuration,
53+ commands, flags, public APIs, or operational workflows that should be documented.
54+ Do not suggest documentation for purely internal refactors, test-only changes, or
55+ minor implementation details that users do not need to know.
56+
57+ Output format:
58+ {
59+ "needed": true,
60+ "summary": "one sentence describing why docs should change",
61+ "suggestions": [
62+ {
63+ "file": "README.md",
64+ "reason": "what changed in the diff",
65+ "suggestion": "specific documentation update to make"
66+ }
67+ ]
68+ }
69+
70+ If no documentation update is needed, return:
71+ {
72+ "needed": false,
73+ "summary": "No documentation updates needed.",
74+ "suggestions": []
75+ }"""
76+
4877def get_branch_diff ():
4978 """Get the diff of changed files in current branch vs main branch."""
5079 try :
@@ -130,6 +159,32 @@ def review_code(diff_content):
130159 print (f"{ Colors .CRITICAL } ✗ Error during AI review: { e } { Colors .RESET } " )
131160 return None
132161
162+ def suggest_documentation_updates (diff_content ):
163+ """Send code diff to OpenAI for documentation update suggestions."""
164+ openai = get_openai_client ()
165+ if not openai :
166+ return None
167+
168+ try :
169+ response = openai .chat .completions .create (
170+ model = "gpt-4o" ,
171+ messages = [
172+ {"role" : "system" , "content" : DOCUMENTATION_PROMPT },
173+ {"role" : "user" , "content" : f"Documentation review for this git diff:\n \n { diff_content } " }
174+ ],
175+ temperature = 0.2 ,
176+ response_format = {"type" : "json_object" }
177+ )
178+
179+ return json .loads (response .choices [0 ].message .content )
180+ except json .JSONDecodeError as e :
181+ print (f"{ Colors .CRITICAL } ✗ Failed to parse documentation response as JSON{ Colors .RESET } " )
182+ print (f"{ Colors .DIM } Error: { e } { Colors .RESET } " )
183+ return None
184+ except Exception as e :
185+ print (f"{ Colors .CRITICAL } ✗ Error during documentation review: { e } { Colors .RESET } " )
186+ return None
187+
133188def print_issues (issues , severity , color , icon ):
134189 """Print issues with consistent formatting."""
135190 if not issues :
@@ -213,6 +268,36 @@ def format_issues(issues, severity, emoji):
213268
214269 return comment
215270
271+ def format_documentation_comment (results ):
272+ """Format documentation suggestions as a GitLab markdown comment."""
273+ if not results or not results .get ('needed' ) or not results .get ('suggestions' ):
274+ return None
275+
276+ comment = "## Documentation Suggestions\n \n "
277+
278+ summary = results .get ('summary' , '' )
279+ if summary :
280+ comment += f"{ summary } \n \n "
281+
282+ for suggestion in results .get ('suggestions' , []):
283+ file_path = suggestion .get ('file' , 'Documentation' )
284+ reason = suggestion .get ('reason' , 'Documentation may need an update.' )
285+ update = suggestion .get ('suggestion' , 'Review the diff and update documentation as needed.' )
286+ comment += f"- **`{ file_path } `**: { update } \n "
287+ comment += f" - Reason: { reason } \n "
288+
289+ return comment
290+
291+ def display_documentation_results (results ):
292+ """Display documentation suggestions in the terminal."""
293+ comment = format_documentation_comment (results )
294+ if not comment :
295+ print (f"{ Colors .INFO } ℹ No documentation updates suggested{ Colors .RESET } " )
296+ return
297+
298+ print (f"\n { Colors .BOLD } DOCUMENTATION SUGGESTIONS{ Colors .RESET } " )
299+ print (comment )
300+
216301def get_merge_request_changes (project_id , mr_id , gitlab_token , api_url ):
217302 """Get the changes (diffs) from the merge request to find commit SHAs."""
218303 import requests
@@ -340,6 +425,11 @@ def run_review():
340425 sys .exit (0 )
341426 display_review_results (results )
342427
428+ print (f"{ Colors .INFO } 📝 Checking documentation impact...{ Colors .RESET } " )
429+ documentation_results = suggest_documentation_updates (diff_content )
430+ if documentation_results :
431+ display_documentation_results (documentation_results )
432+
343433def run_review_for_mr (project_id , mr_id , gitlab_token , api_url ):
344434 """Run AI code review and post inline comments to GitLab merge request."""
345435 print (f"{ Colors .INFO } 🤖 Running AI code review...{ Colors .RESET } " )
@@ -353,6 +443,8 @@ def run_review_for_mr(project_id, mr_id, gitlab_token, api_url):
353443 print (f"{ Colors .HIGH } ⚠ AI review skipped{ Colors .RESET } " )
354444 return
355445
446+ documentation_results = suggest_documentation_updates (diff_content )
447+
356448 # Get diff refs for inline comments
357449 diff_refs = get_diff_refs (project_id , mr_id , gitlab_token , api_url )
358450 if not diff_refs or not all (diff_refs .values ()):
@@ -386,5 +478,9 @@ def run_review_for_mr(project_id, mr_id, gitlab_token, api_url):
386478 else :
387479 print (f"{ Colors .INFO } ✓ All { total_posted } issues posted as inline comments{ Colors .RESET } " )
388480
481+ documentation_comment = format_documentation_comment (documentation_results )
482+ if documentation_comment :
483+ post_to_merge_request (documentation_comment , project_id , mr_id , gitlab_token , api_url )
484+
389485if __name__ == '__main__' :
390486 run_review ()
0 commit comments