@krotovm/gitlab-ai-review 1.0.37 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/ci-review.js +9 -5
- package/dist/prompt/utils.js +30 -3
- package/package.json +1 -1
package/dist/cli/ci-review.js
CHANGED
|
@@ -827,6 +827,10 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
827
827
|
if (failedPaths.length === reviewFiles.length) {
|
|
828
828
|
throw new Error(`All ${reviewFiles.length} per-file reviews failed (e.g. ${failedPaths[0]}). See log above.`);
|
|
829
829
|
}
|
|
830
|
+
// Deterministic note header: title + triage summary. Prepended in code so
|
|
831
|
+
// every final-answer path gets it regardless of model formatting discipline.
|
|
832
|
+
const summaryText = triageResult.summary.trim().replace(/\s*\n\s*/g, " ");
|
|
833
|
+
const withHeader = (body) => `### 🤖 AI Code Review\n\n${summaryText === "" ? "" : `> ${summaryText}\n\n`}${body}`;
|
|
830
834
|
logStep("Pass 3/4: consolidating findings");
|
|
831
835
|
const consolidateMessages = buildConsolidatePrompt({
|
|
832
836
|
perFileFindings,
|
|
@@ -836,7 +840,7 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
836
840
|
});
|
|
837
841
|
if (consolidateMessages == null) {
|
|
838
842
|
const DISCLAIMER = "This comment was generated by AI review bot.";
|
|
839
|
-
return `${NO_FINDINGS_SENTENCE}\n\n---\n_${DISCLAIMER}_
|
|
843
|
+
return withHeader(`${NO_FINDINGS_SENTENCE}\n\n---\n_${DISCLAIMER}_`);
|
|
840
844
|
}
|
|
841
845
|
try {
|
|
842
846
|
const consolidateCompletion = await createCompletionWithDebug({
|
|
@@ -853,7 +857,7 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
853
857
|
});
|
|
854
858
|
const consolidatedText = extractCompletionText(consolidateCompletion);
|
|
855
859
|
if (consolidatedText == null || consolidatedText.trim() === "") {
|
|
856
|
-
return buildAnswer(consolidateCompletion);
|
|
860
|
+
return withHeader(buildAnswer(consolidateCompletion));
|
|
857
861
|
}
|
|
858
862
|
logStep("Pass 4/4: verifying consolidated findings (repo tools)");
|
|
859
863
|
const verificationMessages = buildVerificationPrompt({
|
|
@@ -879,11 +883,11 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
879
883
|
debugDumpFile,
|
|
880
884
|
debugRecordWriter,
|
|
881
885
|
});
|
|
882
|
-
return buildAnswer(verificationCompletion);
|
|
886
|
+
return withHeader(buildAnswer(verificationCompletion));
|
|
883
887
|
}
|
|
884
888
|
catch (error) {
|
|
885
889
|
logStep(`Verification failed: ${error?.message ?? error}. Returning consolidated findings.`);
|
|
886
|
-
return buildAnswer(consolidateCompletion);
|
|
890
|
+
return withHeader(buildAnswer(consolidateCompletion));
|
|
887
891
|
}
|
|
888
892
|
}
|
|
889
893
|
catch (error) {
|
|
@@ -893,6 +897,6 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
893
897
|
.filter((f) => !isEmptyReviewBody(f.findings))
|
|
894
898
|
.map((f) => f.findings)
|
|
895
899
|
.join("\n");
|
|
896
|
-
return `${raw || NO_FINDINGS_SENTENCE}\n\n---\n_${DISCLAIMER}_
|
|
900
|
+
return withHeader(`${raw || NO_FINDINGS_SENTENCE}\n\n---\n_${DISCLAIMER}_`);
|
|
897
901
|
}
|
|
898
902
|
}
|
package/dist/prompt/utils.js
CHANGED
|
@@ -70,11 +70,16 @@ export function normalizeReviewFindingsMarkdown(input) {
|
|
|
70
70
|
const lines = expanded.split("\n");
|
|
71
71
|
const findings = [];
|
|
72
72
|
const headerRe = /^\s*(?:[-*•]\s*)?\[(high|medium)\]\s+(.+?)\s*$/i;
|
|
73
|
+
// Already-rendered block (see renderer below) — parsing it too keeps this
|
|
74
|
+
// function idempotent when a model echoes pretty findings back verbatim.
|
|
75
|
+
const prettyHeaderRe = /^\s*(?:[-*•]\s*)?\*\*(?:🔴|🟠)?\s*(high|medium)\s*[—–-]\s*(.+?)\*\*\s*$/i;
|
|
76
|
+
const matchHeader = (line) => line.match(headerRe) ?? line.match(prettyHeaderRe);
|
|
73
77
|
const fileRe = /^\s*[-*]?\s*File:\s*(.+?)\s*$/i;
|
|
74
78
|
const lineRe = /^\s*[-*]?\s*Line:\s*(.+?)\s*$/i;
|
|
75
79
|
const whyRe = /^\s*[-*]?\s*Why:\s*(.+?)\s*$/i;
|
|
80
|
+
const prettyFileLineRe = /^\s*`([^`\n]+?):(~?[^`\n]*)`\s*$/;
|
|
76
81
|
for (let i = 0; i < lines.length; i += 1) {
|
|
77
|
-
const headerMatch = lines[i]
|
|
82
|
+
const headerMatch = matchHeader(lines[i]);
|
|
78
83
|
if (headerMatch == null)
|
|
79
84
|
continue;
|
|
80
85
|
const severity = headerMatch[1].toLowerCase();
|
|
@@ -84,9 +89,18 @@ export function normalizeReviewFindingsMarkdown(input) {
|
|
|
84
89
|
let why = null;
|
|
85
90
|
let j = i + 1;
|
|
86
91
|
while (j < lines.length) {
|
|
87
|
-
const nextHeader = lines[j]
|
|
92
|
+
const nextHeader = matchHeader(lines[j]);
|
|
88
93
|
if (nextHeader != null)
|
|
89
94
|
break;
|
|
95
|
+
if (file == null && line == null) {
|
|
96
|
+
const m = lines[j].match(prettyFileLineRe);
|
|
97
|
+
if (m != null) {
|
|
98
|
+
file = m[1].trim();
|
|
99
|
+
line = m[2].trim();
|
|
100
|
+
j += 1;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
90
104
|
if (file == null) {
|
|
91
105
|
const m = lines[j].match(fileRe);
|
|
92
106
|
if (m != null) {
|
|
@@ -111,6 +125,15 @@ export function normalizeReviewFindingsMarkdown(input) {
|
|
|
111
125
|
continue;
|
|
112
126
|
}
|
|
113
127
|
}
|
|
128
|
+
// Pretty blocks carry the why as an unlabeled line after `file:line`.
|
|
129
|
+
if (why == null && file != null && line != null) {
|
|
130
|
+
const text = lines[j].trim();
|
|
131
|
+
if (text !== "" && !/^(-{3,}|_)/.test(text)) {
|
|
132
|
+
why = text;
|
|
133
|
+
j += 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
114
137
|
j += 1;
|
|
115
138
|
}
|
|
116
139
|
if (file != null && line != null && why != null) {
|
|
@@ -120,7 +143,11 @@ export function normalizeReviewFindingsMarkdown(input) {
|
|
|
120
143
|
}
|
|
121
144
|
if (findings.length === 0)
|
|
122
145
|
return normalized;
|
|
146
|
+
const severityLabel = {
|
|
147
|
+
high: "🔴 High",
|
|
148
|
+
medium: "🟠 Medium",
|
|
149
|
+
};
|
|
123
150
|
return findings
|
|
124
|
-
.map((f) => `- [
|
|
151
|
+
.map((f) => `- **${severityLabel[f.severity]} — ${f.title}** \n \`${f.file}:${f.line}\` \n ${f.why}`)
|
|
125
152
|
.join("\n\n");
|
|
126
153
|
}
|