@@ -409,8 +409,16 @@ export const robot = (app: Probot) => {
409409 const isFileCountWithinLimit = changedFiles . length <= MAX_FILE_COUNT ;
410410
411411 const ress = [ ] ;
412- let overallReview : { summary : string } | null = null ;
413- // 인라인 코멘트가 위치 불일치(예: 422)로 실패할 경우, 본문 코멘트로 대체하기 위해 누적하는 버퍼
412+ type SummaryResult = {
413+ changeType : string ;
414+ title : string ;
415+ summary : string ;
416+ walkthrough : Array < { file : string ; changes : string ; intent : string } > ;
417+ affectedAreas : string [ ] ;
418+ risks : Array < { level : string ; description : string } > ;
419+ testingNotes : string | null ;
420+ } ;
421+ let overallReview : SummaryResult | null = null ;
414422 let inlineFallback : string [ ] = [ ] ;
415423
416424 if ( isFileCountWithinLimit ) {
@@ -434,16 +442,12 @@ export const robot = (app: Probot) => {
434442 }
435443
436444 const prTitle = pull_request . title || "(no title)" ;
437- const summary = overallReview ?. summary
438- ? `\n## 변경 요약\n${ overallReview . summary } `
439- : "" ;
440445 const prMeta = [
441446 `## PR 제목\n${ prTitle } ` ,
442447 `\n## 커밋 메시지\n${ truncateForPrompt (
443448 aggregatedCommitMessages ,
444449 allocateBudget ( MAX_GLOBAL_CONTEXT_CHARS , 0.3 )
445450 ) } `,
446- summary ,
447451 ] . join ( "\n" ) ;
448452
449453 let prFilePatchByPath = new Map < string , string > ( ) ;
@@ -531,33 +535,41 @@ export const robot = (app: Probot) => {
531535 }
532536
533537 if ( ! res . lgtm || ( res . lgtm && REVIEW_ON_LGTM ) ) {
538+ const formatCommentBody = ( c : typeof res . comments [ 0 ] ) => {
539+ const severityEmoji = c . severity === "critical" ? "🔴" : "🟠" ;
540+ return `${ severityEmoji } **[${ c . category } ]** ${ c . body } ` ;
541+ } ;
542+
534543 if ( ! commentablePaths . has ( file . filename ) ) {
535544 log . info (
536545 `PR 파일에 patch가 없어 ${ file . filename } 에 대한 코멘트 생략`
537546 ) ;
538- const fallbackBody = res . comments . map ( ( c ) => c . body ) . join ( "\n" ) ;
547+ const fallbackBody = res . comments . map ( formatCommentBody ) . join ( "\n" ) ;
539548 inlineFallback . push ( `\n### ${ file . filename } \n${ fallbackBody } ` ) ;
540549 continue ;
541550 }
542551
543552 const prWidePatch = prFilePatchByPath . get ( file . filename ) || patch ;
544553 if ( ! prWidePatch || ! prWidePatch . includes ( "@@" ) ) {
545- const fallbackBody = res . comments . map ( ( c ) => c . body ) . join ( "\n" ) ;
554+ const fallbackBody = res . comments . map ( formatCommentBody ) . join ( "\n" ) ;
546555 inlineFallback . push ( `\n### ${ file . filename } \n${ fallbackBody } ` ) ;
547556 continue ;
548557 }
549558
550559 for ( const comment of res . comments ) {
551560 const position = comment . line ;
561+ const severityEmoji = comment . severity === "critical" ? "🔴" : "🟠" ;
562+ const formattedBody = `${ severityEmoji } **[${ comment . category } ]** ${ comment . body } ` ;
563+
552564 if ( ! Number . isSafeInteger ( position ) || position <= 0 ) {
553565 inlineFallback . push (
554- `\n### ${ file . filename } \n${ comment . body } `
566+ `\n### ${ file . filename } \n${ formattedBody } `
555567 ) ;
556568 continue ;
557569 }
558570 ress . push ( {
559571 path : file . filename ,
560- body : comment . body ,
572+ body : formattedBody ,
561573 position,
562574 } ) ;
563575 }
@@ -578,10 +590,63 @@ export const robot = (app: Probot) => {
578590 }
579591 }
580592
593+ const formatSummaryBody = ( review : SummaryResult ) : string => {
594+ const changeTypeEmoji : Record < string , string > = {
595+ feature : "✨" ,
596+ bugfix : "🐛" ,
597+ refactor : "♻️" ,
598+ docs : "📝" ,
599+ test : "🧪" ,
600+ chore : "🔧" ,
601+ } ;
602+ const riskEmoji : Record < string , string > = {
603+ high : "🔴" ,
604+ medium : "🟠" ,
605+ low : "🟡" ,
606+ } ;
607+
608+ const parts : string [ ] = [ ] ;
609+ parts . push ( `## ${ changeTypeEmoji [ review . changeType ] || "📋" } ${ review . title } ` ) ;
610+ parts . push ( "" ) ;
611+ parts . push ( review . summary ) ;
612+
613+ if ( review . walkthrough . length > 0 ) {
614+ parts . push ( "" ) ;
615+ parts . push ( "### 📂 변경 파일" ) ;
616+ parts . push ( "| 파일 | 변경 내용 | 의도 |" ) ;
617+ parts . push ( "|------|----------|------|" ) ;
618+ for ( const w of review . walkthrough ) {
619+ parts . push ( `| \`${ w . file } \` | ${ w . changes } | ${ w . intent } |` ) ;
620+ }
621+ }
622+
623+ if ( review . affectedAreas . length > 0 ) {
624+ parts . push ( "" ) ;
625+ parts . push ( `### 🎯 영향 범위` ) ;
626+ parts . push ( review . affectedAreas . map ( ( a ) => `- ${ a } ` ) . join ( "\n" ) ) ;
627+ }
628+
629+ if ( review . risks . length > 0 ) {
630+ parts . push ( "" ) ;
631+ parts . push ( "### ⚠️ 리스크" ) ;
632+ for ( const r of review . risks ) {
633+ parts . push ( `- ${ riskEmoji [ r . level ] || "⚪" } **${ r . level . toUpperCase ( ) } **: ${ r . description } ` ) ;
634+ }
635+ }
636+
637+ if ( review . testingNotes ) {
638+ parts . push ( "" ) ;
639+ parts . push ( "### 🧪 테스트 참고사항" ) ;
640+ parts . push ( review . testingNotes ) ;
641+ }
642+
643+ return parts . join ( "\n" ) ;
644+ } ;
645+
581646 try {
582647 let body = "LGTM 👍" ;
583648 if ( overallReview && overallReview . summary ) {
584- body = `<!-- chatgpt-summary:v1 -->\n${ overallReview . summary } ` ;
649+ body = `<!-- chatgpt-summary:v1 -->\n${ formatSummaryBody ( overallReview ) } ` ;
585650 } else if ( ress . length ) {
586651 body = "Code review by ChatGPT" ;
587652 }
@@ -615,7 +680,7 @@ export const robot = (app: Probot) => {
615680 try {
616681 let body = "LGTM 👍" ;
617682 if ( overallReview && overallReview . summary ) {
618- body = `<!-- chatgpt-summary:v1 -->\n${ overallReview . summary } ` ;
683+ body = `<!-- chatgpt-summary:v1 -->\n${ formatSummaryBody ( overallReview ) } ` ;
619684 }
620685
621686 if (
0 commit comments