@krotovm/gitlab-ai-review 1.0.40 → 1.0.41
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/README.md +24 -0
- package/dist/cli/ci-review.js +20 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
# AI Code Reviewer
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@krotovm/gitlab-ai-review)
|
|
6
|
+
[](https://www.npmjs.com/package/@krotovm/gitlab-ai-review)
|
|
7
|
+
[](https://github.com/KrotovM/gitlab-ai-mr-reviewer/blob/main/LICENSE)
|
|
8
|
+
[](https://gitlab.com/explore/catalog/KrotovM/gitlab-ai-review)
|
|
9
|
+
|
|
5
10
|
Gitlab AI Code Review is a CLI tool that leverages OpenAI models to automatically review code changes and post a Markdown review to GitLab merge requests from CI.
|
|
6
11
|
|
|
12
|
+

|
|
13
|
+
|
|
7
14
|
## Features
|
|
8
15
|
|
|
9
16
|
- Automatically reviews code changes in GitLab repositories
|
|
@@ -30,6 +37,15 @@ ai_review:
|
|
|
30
37
|
- npx -y @krotovm/gitlab-ai-review
|
|
31
38
|
```
|
|
32
39
|
|
|
40
|
+
Or include it as a [CI/CD component from the GitLab Catalog](https://gitlab.com/explore/catalog/KrotovM/gitlab-ai-review) (gitlab.com):
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
include:
|
|
44
|
+
- component: gitlab.com/KrotovM/gitlab-ai-review/review@1.0.2
|
|
45
|
+
inputs:
|
|
46
|
+
args: "--include-artifacts"
|
|
47
|
+
```
|
|
48
|
+
|
|
33
49
|
Save debug HTML as a CI artifact:
|
|
34
50
|
|
|
35
51
|
```yaml
|
|
@@ -48,6 +64,10 @@ ai_review:
|
|
|
48
64
|
- ai-review-report.html
|
|
49
65
|
```
|
|
50
66
|
|
|
67
|
+
The HTML artifact breaks the run down per pass — tokens, durations, triage decisions, per-file findings, and errors:
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
51
71
|
## Env variables
|
|
52
72
|
|
|
53
73
|
Set these in your project/group CI settings:
|
|
@@ -110,3 +130,7 @@ The reviewer uses a three-pass pipeline optimized for large merge requests:
|
|
|
110
130
|
3. **Consolidate** - Per-file findings are merged, deduplicated, ranked by severity, and trimmed to top N (default 5).
|
|
111
131
|
|
|
112
132
|
If the triage pass fails (API error, unparseable response), the pipeline falls back to the original single-pass approach automatically.
|
|
133
|
+
|
|
134
|
+
The pipeline in a CI job log — triage, parallel tool-assisted file reviews, consolidation, verification:
|
|
135
|
+
|
|
136
|
+

|
package/dist/cli/ci-review.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import OpenAI from "openai";
|
|
3
3
|
import { buildAnswer, buildConsolidatePrompt, buildFileReviewPrompt, buildPrompt, buildTriagePrompt, buildVerificationPrompt, DEFAULT_PROMPT_PROFILE, extractCompletionText, isEmptyReviewBody, NO_FINDINGS_SENTENCE, parseTriageResponseDetailed, parseTriageResponse, } from "../prompt/index.js";
|
|
4
4
|
import { fetchFileAtRef, searchRepository, } from "../gitlab/services.js";
|
|
5
|
-
import { truncateWithMarker } from "../prompt/utils.js";
|
|
5
|
+
import { parseReviewFindings, truncateWithMarker } from "../prompt/utils.js";
|
|
6
6
|
import { logToolUsageMinimal, MAX_FILE_TOOL_ROUNDS, MAX_TOOL_ROUNDS, MAX_VERIFICATION_TOOL_ROUNDS, TOOL_NAME_GET_FILE, TOOL_NAME_GREP, } from "./tooling.js";
|
|
7
7
|
/** Hard deadline per model call, retries included. Must exceed the gateway's own
|
|
8
8
|
* proxy timeout so the SDK's 5xx retries stay the first line of defense. */
|
|
@@ -849,6 +849,22 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
849
849
|
// every final-answer path gets it regardless of model formatting discipline.
|
|
850
850
|
const summaryText = triageResult.summary.trim().replace(/\s*\n\s*/g, " ");
|
|
851
851
|
const withHeader = (body) => `### 🤖 AI Code Review\n\n${summaryText === "" ? "" : `> ${summaryText}\n\n`}${body}`;
|
|
852
|
+
// Cap transparency: if consolidation likely trimmed the list (final count hit
|
|
853
|
+
// the cap while per-file review produced more candidates), say so in the note
|
|
854
|
+
// instead of silently dropping findings.
|
|
855
|
+
// ponytail: candidates deduped by file+title only; differently-worded dupes
|
|
856
|
+
// across files still inflate the count, which is why the note says "candidate".
|
|
857
|
+
const candidateCount = new Set(perFileFindings.flatMap((f) => parseReviewFindings(f.findings).map((x) => `${x.file}|${x.title.toLowerCase()}`))).size;
|
|
858
|
+
const withCapNote = (body) => {
|
|
859
|
+
const finalCount = parseReviewFindings(body).length;
|
|
860
|
+
if (finalCount < maxFindings || candidateCount <= finalCount)
|
|
861
|
+
return body;
|
|
862
|
+
const note = `\n\nℹ️ Showing top ${finalCount} of ${candidateCount} candidate findings (\`--max-findings=${maxFindings}\`). Run with \`--include-artifacts\` for the full list.`;
|
|
863
|
+
const disclaimerAt = body.lastIndexOf("\n\n---\n_");
|
|
864
|
+
return disclaimerAt === -1
|
|
865
|
+
? body + note
|
|
866
|
+
: body.slice(0, disclaimerAt) + note + body.slice(disclaimerAt);
|
|
867
|
+
};
|
|
852
868
|
logStep("Pass 3/4: consolidating findings");
|
|
853
869
|
const consolidateMessages = buildConsolidatePrompt({
|
|
854
870
|
perFileFindings,
|
|
@@ -875,7 +891,7 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
875
891
|
});
|
|
876
892
|
const consolidatedText = extractCompletionText(consolidateCompletion);
|
|
877
893
|
if (consolidatedText == null || consolidatedText.trim() === "") {
|
|
878
|
-
return withHeader(buildAnswer(consolidateCompletion));
|
|
894
|
+
return withHeader(withCapNote(buildAnswer(consolidateCompletion)));
|
|
879
895
|
}
|
|
880
896
|
logStep("Pass 4/4: verifying consolidated findings (repo tools)");
|
|
881
897
|
const verificationMessages = buildVerificationPrompt({
|
|
@@ -901,11 +917,11 @@ export async function reviewMergeRequestMultiPass(params) {
|
|
|
901
917
|
debugDumpFile,
|
|
902
918
|
debugRecordWriter,
|
|
903
919
|
});
|
|
904
|
-
return withHeader(buildAnswer(verificationCompletion));
|
|
920
|
+
return withHeader(withCapNote(buildAnswer(verificationCompletion)));
|
|
905
921
|
}
|
|
906
922
|
catch (error) {
|
|
907
923
|
logStep(`Verification failed: ${error?.message ?? error}. Returning consolidated findings.`);
|
|
908
|
-
return withHeader(buildAnswer(consolidateCompletion));
|
|
924
|
+
return withHeader(withCapNote(buildAnswer(consolidateCompletion)));
|
|
909
925
|
}
|
|
910
926
|
}
|
|
911
927
|
catch (error) {
|