6262 _load_index ,
6363 _ledger_paths ,
6464)
65+ from aiir ._gitlab import (
66+ format_gitlab_summary ,
67+ format_gl_sast_report ,
68+ post_mr_comment ,
69+ )
6570
6671# ---------------------------------------------------------------------------
6772# Server metadata
@@ -319,6 +324,46 @@ def _sanitize_error(error: Exception) -> str:
319324 },
320325 },
321326 },
327+ {
328+ "name" : "aiir_gitlab_summary" ,
329+ "description" : (
330+ "Generate a GitLab-flavored Markdown summary of AIIR receipts. "
331+ "Returns a formatted table with AI authorship breakdown, per-commit "
332+ "details in a collapsible block, and optional SAST report data. "
333+ "Designed for GitLab Duo Chat, MR comments, and CI pipelines. "
334+ "Can optionally post the summary as a merge request comment when "
335+ "running inside GitLab CI. Reads from the .aiir/ ledger in cwd."
336+ ),
337+ "inputSchema" : {
338+ "type" : "object" ,
339+ "properties" : {
340+ "range" : {
341+ "type" : "string" ,
342+ "description" : (
343+ "Commit range to summarize (e.g., 'origin/main..HEAD'). "
344+ "If omitted, all receipts in the ledger are summarized."
345+ ),
346+ },
347+ "include_sast" : {
348+ "type" : "boolean" ,
349+ "description" : (
350+ "Include SAST report data (Security Dashboard format) "
351+ "alongside the Markdown summary."
352+ ),
353+ "default" : False ,
354+ },
355+ "post_to_mr" : {
356+ "type" : "boolean" ,
357+ "description" : (
358+ "Post the summary as a comment on the current merge request. "
359+ "Only works when running inside GitLab CI with "
360+ "CI_MERGE_REQUEST_IID set. Ignored outside CI."
361+ ),
362+ "default" : False ,
363+ },
364+ },
365+ },
366+ },
322367]
323368
324369# ---------------------------------------------------------------------------
@@ -500,6 +545,80 @@ def _handle_aiir_verify_release(args: Dict[str, Any]) -> Dict[str, Any]:
500545 return _text_result (report )
501546
502547
548+ def _handle_aiir_gitlab_summary (args : Dict [str , Any ]) -> Dict [str , Any ]:
549+ """Generate a GitLab-flavored Markdown summary of AIIR receipts.
550+
551+ Designed for GitLab Duo Chat and MR comment integration.
552+ Reads receipts from the ledger and produces a native GitLab Markdown
553+ summary with optional SAST report data and MR comment posting.
554+ """
555+ range_spec = args .get ("range" )
556+ include_sast = args .get ("include_sast" , False )
557+ post_to_mr = args .get ("post_to_mr" , False )
558+
559+ try :
560+ if range_spec :
561+ # Generate receipts for the specified range
562+ receipts = generate_receipts_for_range (range_spec , cwd = None )
563+ else :
564+ # Load from ledger
565+ ledger_path = Path .cwd () / ".aiir" / "receipts.jsonl"
566+ if not ledger_path .is_file ():
567+ return _text_result (
568+ "No AIIR ledger found in this repository.\n "
569+ "Run 'aiir --pretty' to generate your first receipt, "
570+ "then use this tool to create a GitLab summary."
571+ )
572+ receipts = []
573+ with open (ledger_path , encoding = "utf-8" ) as f :
574+ for line in f :
575+ line = line .strip ()
576+ if line :
577+ try :
578+ receipts .append (json .loads (line ))
579+ except json .JSONDecodeError :
580+ continue
581+
582+ if not receipts :
583+ return _text_result (
584+ "No receipts found. "
585+ "Run 'aiir --range <range>' to generate receipts first."
586+ )
587+
588+ # Format as GitLab-flavored Markdown
589+ summary = format_gitlab_summary (receipts )
590+
591+ # Optionally append SAST report data
592+ if include_sast :
593+ sast = format_gl_sast_report (receipts )
594+ summary += (
595+ "\n \n <details>\n <summary>🛡️ SAST Report Data</summary>\n \n "
596+ "```json\n "
597+ + json .dumps (sast , indent = 2 )
598+ + "\n ```\n \n </details>"
599+ )
600+
601+ # Optionally post to MR (best-effort, non-fatal)
602+ mr_posted = False
603+ if post_to_mr :
604+ import os as _os
605+
606+ if _os .environ .get ("CI_MERGE_REQUEST_IID" ):
607+ try :
608+ post_mr_comment (summary )
609+ mr_posted = True
610+ except Exception :
611+ pass # Best-effort — don't fail the tool call
612+
613+ if mr_posted :
614+ summary += "\n \n ✅ *Summary posted to merge request.*"
615+
616+ return _text_result (summary )
617+
618+ except Exception as e :
619+ return _error_result (_sanitize_error (e ))
620+
621+
503622# ---------------------------------------------------------------------------
504623# MCP response helpers
505624# ---------------------------------------------------------------------------
@@ -524,6 +643,7 @@ def _error_result(message: str) -> Dict[str, Any]:
524643 "aiir_explain" : _handle_aiir_explain ,
525644 "aiir_policy_check" : _handle_aiir_policy_check ,
526645 "aiir_verify_release" : _handle_aiir_verify_release ,
646+ "aiir_gitlab_summary" : _handle_aiir_gitlab_summary ,
527647}
528648
529649
0 commit comments