@krotovm/gitlab-ai-review 1.0.41 → 1.0.42

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.
@@ -143,7 +143,7 @@ function buildReviewMetadata(changes, refs) {
143
143
  changed_files: files,
144
144
  tool_usage_guidance: [
145
145
  "If diff context is insufficient, call get_file_at_ref to read a specific file.",
146
- "Use grep_repository to search for usages, definitions, or patterns across the codebase.",
146
+ "Use grep_repository to find usages or definitions by literal text (no regex) across the codebase.",
147
147
  "Use refs.base to inspect pre-change content and refs.head for current content.",
148
148
  "Prefer targeted searches and file fetches; avoid broad context requests.",
149
149
  ],
@@ -161,7 +161,7 @@ async function handleGetFileTool(argsRaw, gitLabProjectApiUrl, headers) {
161
161
  });
162
162
  }
163
163
  const fileText = await fetchFileAtRef({
164
- gitLabBaseUrl: gitLabProjectApiUrl,
164
+ gitLabProjectApiUrl: gitLabProjectApiUrl,
165
165
  headers,
166
166
  filePath: path,
167
167
  ref,
@@ -198,7 +198,7 @@ async function handleGrepTool(argsRaw, defaultRef, gitLabProjectApiUrl, headers,
198
198
  return JSON.stringify({ ok: false, error: "query is required." });
199
199
  const ref = parsed.ref?.trim() || defaultRef;
200
200
  const results = await searchRepository({
201
- gitLabBaseUrl: gitLabProjectApiUrl,
201
+ gitLabProjectApiUrl: gitLabProjectApiUrl,
202
202
  headers,
203
203
  query,
204
204
  ref,
@@ -279,14 +279,14 @@ export async function reviewMergeRequestWithTools(params) {
279
279
  type: "function",
280
280
  function: {
281
281
  name: TOOL_NAME_GREP,
282
- description: "Search the repository for a keyword or pattern. Returns up to 10 matching code fragments with file paths and line numbers.",
282
+ description: "Search the repository for literal text (case-insensitive substring match). Regular expressions are not supported: `a|b` or `foo.*bar` are searched as literal text, so query one identifier at a time. Returns up to 10 matching code fragments with file paths and line numbers.",
283
283
  parameters: {
284
284
  type: "object",
285
285
  additionalProperties: false,
286
286
  properties: {
287
287
  query: {
288
288
  type: "string",
289
- description: "Search string (keyword, function name, variable, etc.).",
289
+ description: "Literal text to find, e.g. a function or variable name (no regex).",
290
290
  },
291
291
  ref: {
292
292
  type: "string",
@@ -424,14 +424,14 @@ async function runFileReviewWithTools(params) {
424
424
  type: "function",
425
425
  function: {
426
426
  name: TOOL_NAME_GREP,
427
- description: "Search the repository for a keyword or pattern. Returns up to 10 matching code fragments with file paths and line numbers.",
427
+ description: "Search the repository for literal text (case-insensitive substring match). Regular expressions are not supported: `a|b` or `foo.*bar` are searched as literal text, so query one identifier at a time. Returns up to 10 matching code fragments with file paths and line numbers.",
428
428
  parameters: {
429
429
  type: "object",
430
430
  additionalProperties: false,
431
431
  properties: {
432
432
  query: {
433
433
  type: "string",
434
- description: "Search string (keyword, function name, variable, etc.).",
434
+ description: "Literal text to find, e.g. a function or variable name (no regex).",
435
435
  },
436
436
  ref: {
437
437
  type: "string",
@@ -564,14 +564,14 @@ async function runVerificationWithTools(params) {
564
564
  type: "function",
565
565
  function: {
566
566
  name: TOOL_NAME_GREP,
567
- description: "Search the repository for a keyword or pattern. Returns up to 10 matching code fragments with file paths and line numbers.",
567
+ description: "Search the repository for literal text (case-insensitive substring match). Regular expressions are not supported: `a|b` or `foo.*bar` are searched as literal text, so query one identifier at a time. Returns up to 10 matching code fragments with file paths and line numbers.",
568
568
  parameters: {
569
569
  type: "object",
570
570
  additionalProperties: false,
571
571
  properties: {
572
572
  query: {
573
573
  type: "string",
574
- description: "Search string (keyword, function name, variable, etc.).",
574
+ description: "Literal text to find, e.g. a function or variable name (no regex).",
575
575
  },
576
576
  ref: {
577
577
  type: "string",
package/dist/cli.js CHANGED
@@ -8,43 +8,41 @@ import { reviewMergeRequestMultiPass } from "./cli/ci-review.js";
8
8
  import { fetchMergeRequestChanges, postMergeRequestNote, } from "./gitlab/services.js";
9
9
  import { renderDebugArtifactsHtml } from "./cli/debug-artifacts-html.js";
10
10
  function printHelp() {
11
- process.stdout.write([
12
- "gitlab-ai-review",
13
- "",
14
- "Usage:",
15
- " gitlab-ai-review",
16
- " gitlab-ai-review --help",
17
- " gitlab-ai-review --debug",
18
- " gitlab-ai-review --ignore-ext=md,lock",
19
- "",
20
- "Debug:",
21
- " --debug Print full error details (stack, API error fields).",
22
- " --include-artifacts Generate local HTML artifact without printing payloads to console.",
23
- " --force-tools Force at least one tool-call round in tool-enabled review paths.",
24
- " --ignore-ext Ignore file extensions (comma-separated only). Example: --ignore-ext=md,lock",
25
- " --max-diffs=50",
26
- " --max-diff-chars=16000",
27
- " --max-total-prompt-chars=220000",
28
- " --triage-diff-chars=2000 Max chars per file diff in triage pass (Pass 1).",
29
- " --max-findings=5 Max findings in final review (CI multi-pass only).",
30
- " --max-review-concurrency=2 Parallel per-file review calls (CI multi-pass only).",
31
- "",
32
- "Env vars:",
33
- " OPENAI_API_KEY (required) OpenAI API key.",
34
- " OPENAI_BASE_URL (optional) Custom OpenAI-compatible API base URL.",
35
- " AI_MODEL (optional) OpenAI chat model, e.g. gpt-4o. Default: gpt-4o-mini.",
36
- " AI_REVIEW_CONCURRENCY (optional) Same as --max-review-concurrency; the flag wins.",
37
- " AI_PROMPT_PROFILE (optional) Prompt style: \"default\" | \"weak\". Default: default.",
38
- " Use \"weak\" for small/quantized models — shorter prompts,",
39
- " positive rules, and few-shot examples.",
40
- " When unset, an unparseable triage response auto-retries",
41
- " with \"weak\" and keeps it for the whole run.",
42
- " PROJECT_ACCESS_TOKEN (optional) GitLab Project/Personal Access Token for API calls (required for most private repos; should have api scope).",
43
- "",
44
- "CI-only env vars (provided by GitLab):",
45
- " CI_API_V4_URL, CI_PROJECT_ID, CI_MERGE_REQUEST_IID, CI_JOB_TOKEN (only if PROJECT_ACCESS_TOKEN is not set)",
46
- "",
47
- ].join("\n"));
11
+ process.stdout.write(`gitlab-ai-review
12
+
13
+ Usage:
14
+ gitlab-ai-review
15
+ gitlab-ai-review --help
16
+ gitlab-ai-review --debug
17
+ gitlab-ai-review --ignore-ext=md,lock
18
+
19
+ Debug:
20
+ --debug Print full error details (stack, API error fields).
21
+ --include-artifacts Generate local HTML artifact without printing payloads to console.
22
+ --force-tools Force at least one tool-call round in tool-enabled review paths.
23
+ --ignore-ext Ignore file extensions (comma-separated only). Example: --ignore-ext=md,lock
24
+ --max-diffs=50
25
+ --max-diff-chars=16000
26
+ --max-total-prompt-chars=220000
27
+ --triage-diff-chars=2000 Max chars per file diff in triage pass (Pass 1).
28
+ --max-findings=5 Max findings in final review (CI multi-pass only).
29
+ --max-review-concurrency=2 Parallel per-file review calls (CI multi-pass only).
30
+
31
+ Env vars:
32
+ OPENAI_API_KEY (required) OpenAI API key.
33
+ OPENAI_BASE_URL (optional) Custom OpenAI-compatible API base URL.
34
+ AI_MODEL (optional) OpenAI chat model, e.g. gpt-4o. Default: gpt-4o-mini.
35
+ AI_REVIEW_CONCURRENCY (optional) Same as --max-review-concurrency; the flag wins.
36
+ AI_PROMPT_PROFILE (optional) Prompt style: "default" | "weak". Default: default.
37
+ Use "weak" for small/quantized models — shorter prompts,
38
+ positive rules, and few-shot examples.
39
+ When unset, an unparseable triage response auto-retries
40
+ with "weak" and keeps it for the whole run.
41
+ PROJECT_ACCESS_TOKEN (optional) GitLab Project/Personal Access Token for API calls (required for most private repos; should have api scope).
42
+
43
+ CI-only env vars (provided by GitLab):
44
+ CI_API_V4_URL, CI_PROJECT_ID, CI_MERGE_REQUEST_IID, CI_JOB_TOKEN (only if PROJECT_ACCESS_TOKEN is not set)
45
+ `);
48
46
  }
49
47
  const DEBUG_MODE = hasDebugFlag(process.argv);
50
48
  const FORCE_TOOLS = hasForceToolsFlag(process.argv);
@@ -116,6 +114,7 @@ async function main() {
116
114
  const ciApiV4Url = envs["CI_API_V4_URL"];
117
115
  const projectId = envs["CI_PROJECT_ID"];
118
116
  const mergeRequestIid = envs["CI_MERGE_REQUEST_IID"];
117
+ const gitLabProjectApiUrl = new URL(`${ciApiV4Url}/projects/${projectId}`);
119
118
  const headers = {};
120
119
  if (projectAccessToken != null)
121
120
  headers["PRIVATE-TOKEN"] = projectAccessToken;
@@ -124,7 +123,7 @@ async function main() {
124
123
  try {
125
124
  logStep("Fetching merge request changes");
126
125
  const mrChanges = await fetchMergeRequestChanges({
127
- gitLabBaseUrl: new URL(ciApiV4Url),
126
+ gitLabProjectApiUrl: gitLabProjectApiUrl,
128
127
  headers,
129
128
  projectId,
130
129
  mergeRequestIid,
@@ -149,7 +148,7 @@ async function main() {
149
148
  base: mrChanges.diff_refs?.base_sha ?? "HEAD",
150
149
  head: mrChanges.diff_refs?.head_sha ?? "HEAD",
151
150
  },
152
- gitLabProjectApiUrl: new URL(`${ciApiV4Url}/projects/${projectId}`),
151
+ gitLabProjectApiUrl: gitLabProjectApiUrl,
153
152
  projectId,
154
153
  headers,
155
154
  maxFindings,
@@ -162,7 +161,7 @@ async function main() {
162
161
  });
163
162
  logStep("Posting AI review note to merge request");
164
163
  const noteRes = await postMergeRequestNote({
165
- gitLabBaseUrl: new URL(`${ciApiV4Url}/projects/${projectId}`),
164
+ gitLabProjectApiUrl: gitLabProjectApiUrl,
166
165
  headers,
167
166
  mergeRequestIid,
168
167
  }, { body: answer });
@@ -2,9 +2,9 @@
2
2
  import OpenAI from "openai";
3
3
  import { AI_MODEL_TEMPERATURE } from "../prompt/index.js";
4
4
  import { GitLabError, OpenAIError, } from "./types.js";
5
- export const fetchPreEditFiles = async ({ gitLabBaseUrl, headers, changesOldPaths, ref }) => {
5
+ export const fetchPreEditFiles = async ({ gitLabProjectApiUrl, headers, changesOldPaths, ref }) => {
6
6
  const oldFilesRequestUrls = changesOldPaths.map((filePath) => {
7
- const url = new URL(`${gitLabBaseUrl}/repository/files/${encodeURIComponent(filePath)}/raw`);
7
+ const url = new URL(`${gitLabProjectApiUrl}/repository/files/${encodeURIComponent(filePath)}/raw`);
8
8
  url.searchParams.set("ref", ref);
9
9
  return url;
10
10
  });
@@ -42,8 +42,8 @@ export const fetchPreEditFiles = async ({ gitLabBaseUrl, headers, changesOldPath
42
42
  return acc;
43
43
  }, []);
44
44
  };
45
- export const fetchFileAtRef = async ({ gitLabBaseUrl, headers, filePath, ref }) => {
46
- const url = new URL(`${gitLabBaseUrl}/repository/files/${encodeURIComponent(filePath)}/raw`);
45
+ export const fetchFileAtRef = async ({ gitLabProjectApiUrl, headers, filePath, ref }) => {
46
+ const url = new URL(`${gitLabProjectApiUrl}/repository/files/${encodeURIComponent(filePath)}/raw`);
47
47
  url.searchParams.set("ref", ref);
48
48
  let res;
49
49
  try {
@@ -103,8 +103,8 @@ export async function generateAICompletion(messages, openaiInstance, aiModel) {
103
103
  }
104
104
  return completion;
105
105
  }
106
- export const postMergeRequestNote = async ({ gitLabBaseUrl, headers, mergeRequestIid }, commentPayload) => {
107
- const commentUrl = new URL(`${gitLabBaseUrl}/merge_requests/${mergeRequestIid}/notes`);
106
+ export const postMergeRequestNote = async ({ gitLabProjectApiUrl, headers, mergeRequestIid }, commentPayload) => {
107
+ const commentUrl = new URL(`${gitLabProjectApiUrl}/merge_requests/${mergeRequestIid}/notes`);
108
108
  let aiComment;
109
109
  try {
110
110
  aiComment = await fetch(commentUrl, {
@@ -147,8 +147,8 @@ export const postMergeRequestNote = async ({ gitLabBaseUrl, headers, mergeReques
147
147
  });
148
148
  }
149
149
  };
150
- export const searchRepository = async ({ gitLabBaseUrl, headers, query, ref, projectId }) => {
151
- const url = new URL(`${gitLabBaseUrl}/projects/${projectId}/search`);
150
+ export const searchRepository = async ({ gitLabProjectApiUrl, headers, query, ref }) => {
151
+ const url = new URL(`${gitLabProjectApiUrl}/search`);
152
152
  url.searchParams.set("scope", "blobs");
153
153
  url.searchParams.set("search", query);
154
154
  url.searchParams.set("ref", ref);
@@ -185,8 +185,8 @@ export const searchRepository = async ({ gitLabBaseUrl, headers, query, ref, pro
185
185
  }
186
186
  return (await res.json());
187
187
  };
188
- export const fetchMergeRequestChanges = async ({ gitLabBaseUrl, headers, projectId, mergeRequestIid }) => {
189
- const url = new URL(`${gitLabBaseUrl}/projects/${projectId}/merge_requests/${mergeRequestIid}/changes`);
188
+ export const fetchMergeRequestChanges = async ({ gitLabProjectApiUrl, headers, mergeRequestIid }) => {
189
+ const url = new URL(`${gitLabProjectApiUrl}/merge_requests/${mergeRequestIid}/changes`);
190
190
  // Ask GitLab for raw diffs to reduce truncation on larger merge requests.
191
191
  url.searchParams.set("access_raw_diffs", "true");
192
192
  let res;
@@ -1,7 +1,7 @@
1
1
  /** @format */
2
2
  import { buildFileReviewSystemMessage, buildMainSystemMessages, buildTriageSystemMessage, } from "./messages.js";
3
3
  import { extractFirstJsonObject, normalizeReviewFindingsMarkdown, sanitizeGitLabMarkdown, truncateWithMarker, } from "./utils.js";
4
- import { getConsolidateSystemLines, getVerificationSystemLines, } from "./templates/postprocess-system.js";
4
+ import { getConsolidateSystemPrompt, getVerificationSystemPrompt, } from "./templates/postprocess-system.js";
5
5
  import { buildConsolidateUserContent, buildFileReviewUserContent, buildMainReviewUserContent, buildTriageUserContent, buildVerificationUserContent, } from "./templates/user-prompts.js";
6
6
  import { DEFAULT_PROMPT_PROFILE, isEmptyReviewBody } from "./profile.js";
7
7
  export { DEFAULT_PROMPT_PROFILE, NO_FINDINGS_SENTENCE, isEmptyReviewBody, parsePromptProfile, } from "./profile.js";
@@ -160,7 +160,7 @@ export function buildConsolidatePrompt(params) {
160
160
  return [
161
161
  {
162
162
  role: "system",
163
- content: getConsolidateSystemLines(profile, maxFindings).join("\n"),
163
+ content: getConsolidateSystemPrompt(profile, maxFindings),
164
164
  },
165
165
  {
166
166
  role: "user",
@@ -180,7 +180,7 @@ export function buildVerificationPrompt(params) {
180
180
  return [
181
181
  {
182
182
  role: "system",
183
- content: getVerificationSystemLines(profile, maxFindings).join("\n"),
183
+ content: getVerificationSystemPrompt(profile, maxFindings),
184
184
  },
185
185
  {
186
186
  role: "user",
@@ -1,20 +1,20 @@
1
1
  /** @format */
2
- import { getFileReviewSystemLines, getMainSystemLines, } from "./templates/review-system.js";
3
- import { getTriageSystemLines } from "./templates/triage-system.js";
2
+ import { getFileReviewSystemPrompt, getMainSystemPrompt, } from "./templates/review-system.js";
3
+ import { getTriageSystemPrompt } from "./templates/triage-system.js";
4
4
  import { DEFAULT_PROMPT_PROFILE } from "./profile.js";
5
5
  export const buildMainSystemMessages = (profile = DEFAULT_PROMPT_PROFILE) => [
6
6
  {
7
7
  role: "system",
8
- content: getMainSystemLines(profile).join("\n"),
8
+ content: getMainSystemPrompt(profile),
9
9
  },
10
10
  ];
11
11
  export const buildTriageSystemMessage = (profile = DEFAULT_PROMPT_PROFILE) => ({
12
12
  role: "system",
13
- content: getTriageSystemLines(profile).join("\n"),
13
+ content: getTriageSystemPrompt(profile),
14
14
  });
15
15
  export const buildFileReviewSystemMessage = (profile = DEFAULT_PROMPT_PROFILE) => ({
16
16
  role: "system",
17
- content: getFileReviewSystemLines(profile).join("\n"),
17
+ content: getFileReviewSystemPrompt(profile),
18
18
  });
19
19
  /** @deprecated kept for backward compatibility — use buildTriageSystemMessage(). */
20
20
  export const TRIAGE_SYSTEM = buildTriageSystemMessage();
@@ -1,118 +1,104 @@
1
1
  /** @format */
2
2
  import { NO_FINDINGS_SENTENCE } from "../profile.js";
3
- function buildConsolidateDefaultLines(maxFindings) {
4
- return [
5
- "You consolidate per-file code review findings into a final ranked list.",
6
- `Select the top ${maxFindings} most important findings. Deduplicate overlapping issues.`,
7
- "Preserve this exact per-finding markdown block:",
8
- "`- [high|medium] <title>`",
9
- "` File: <path>`",
10
- "` Line: ~<N>`",
11
- "` Why: <one concise sentence with key evidence>`",
12
- "Rank by: (1) [high] before [medium], (2) correctness > security > perf.",
13
- "Do not add new findings. Do not add headings, summaries, or commentary.",
14
- "Drop any finding that: claims a syntax error without quoting the invalid token, claims a function/variable is missing without concrete proof, or flags a refactoring rename as a bug.",
15
- `If fewer than ${maxFindings} findings exist, return all of them.`,
16
- `If all findings are low quality or dubious after dedup, return exactly: "${NO_FINDINGS_SENTENCE}"`,
17
- "GitLab-flavoured markdown.",
18
- ];
3
+ function buildConsolidateDefaultPrompt(maxFindings) {
4
+ return `You consolidate per-file code review findings into a final ranked list.
5
+ Select the top ${maxFindings} most important findings. Deduplicate overlapping issues.
6
+ Preserve this exact per-finding markdown block:
7
+ \`- [high|medium] <title>\`
8
+ \` File: <path>\`
9
+ \` Line: ~<N>\`
10
+ \` Why: <one concise sentence with key evidence>\`
11
+ Rank by: (1) [high] before [medium], (2) correctness > security > perf.
12
+ Do not add new findings. Do not add headings, summaries, or commentary.
13
+ Drop any finding that: claims a syntax error without quoting the invalid token, claims a function/variable is missing without concrete proof, or flags a refactoring rename as a bug.
14
+ If fewer than ${maxFindings} findings exist, return all of them.
15
+ If all findings are low quality or dubious after dedup, return exactly: "${NO_FINDINGS_SENTENCE}"
16
+ GitLab-flavoured markdown.`;
19
17
  }
20
- function buildConsolidateWeakLines(maxFindings) {
21
- return [
22
- "You merge per-file review findings into one ranked list.",
23
- "Output bullets only — no preamble, no headings, no code fences.",
24
- `Keep at most ${maxFindings} findings. Do not invent new ones.`,
25
- "",
26
- "Steps:",
27
- "1. Drop duplicates and findings that are reworded versions of each other (keep the clearest).",
28
- "2. Drop a finding if it has no concrete evidence (no file/line, vague reasoning, or just \"this might be wrong\").",
29
- "3. Sort the remainder: all [high] before any [medium]; within a tier, correctness bugs before security before perf.",
30
- "4. Take the top items up to the limit.",
31
- "",
32
- "Output format — copy spacing and case exactly, do not wrap bullets in backticks:",
33
- "- [high|medium] <short title>",
34
- " File: <path>",
35
- " Line: ~<N>",
36
- " Why: <one sentence pointing at the evidence>",
37
- "",
38
- "Example output (two findings, ranked):",
39
- "- [high] Null pointer dereference in user lookup",
40
- " File: src/user/service.ts",
41
- " Line: ~42",
42
- " Why: getUser() can return null but the next line calls user.id without a guard.",
43
- "",
44
- "- [medium] Off-by-one in pagination loop",
45
- " File: src/api/list.ts",
46
- " Line: ~88",
47
- " Why: loop runs while i <= total but indices are 0-based, so the last item is read past the array end.",
48
- "",
49
- `If after dedup nothing survives, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`,
50
- ];
18
+ function buildConsolidateWeakPrompt(maxFindings) {
19
+ return `You merge per-file review findings into one ranked list.
20
+ Output bullets only — no preamble, no headings, no code fences.
21
+ Keep at most ${maxFindings} findings. Do not invent new ones.
22
+
23
+ Steps:
24
+ 1. Drop duplicates and findings that are reworded versions of each other (keep the clearest).
25
+ 2. Drop a finding if it has no concrete evidence (no file/line, vague reasoning, or just "this might be wrong").
26
+ 3. Sort the remainder: all [high] before any [medium]; within a tier, correctness bugs before security before perf.
27
+ 4. Take the top items up to the limit.
28
+
29
+ Output format — copy spacing and case exactly, do not wrap bullets in backticks:
30
+ - [high|medium] <short title>
31
+ File: <path>
32
+ Line: ~<N>
33
+ Why: <one sentence pointing at the evidence>
34
+
35
+ Example output (two findings, ranked):
36
+ - [high] Null pointer dereference in user lookup
37
+ File: src/user/service.ts
38
+ Line: ~42
39
+ Why: getUser() can return null but the next line calls user.id without a guard.
40
+
41
+ - [medium] Off-by-one in pagination loop
42
+ File: src/api/list.ts
43
+ Line: ~88
44
+ Why: loop runs while i <= total but indices are 0-based, so the last item is read past the array end.
45
+
46
+ If after dedup nothing survives, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`;
51
47
  }
52
- function buildVerificationDefaultLines(maxFindings) {
53
- return [
54
- "You are a skeptical verifier of a merge request review.",
55
- "Your job is to remove weak, speculative, or unsupported findings from the draft list.",
56
- "Tools get_file_at_ref and grep_repository are available. Use them to check claims about current code against the repository at the MR head ref.",
57
- "Drop a finding if file contents at refs.head contradict it, or if it cannot be verified after reasonable tool use.",
58
- "Do not add new findings. Keep, rewrite for clarity, or remove existing findings only.",
59
- "A finding can stay only if supported by the per-file evidence pool and not contradicted by tools when the claim is about code that exists at refs.head.",
60
- "If confidence is not high, drop the finding.",
61
- "Preserve this exact per-finding markdown block:",
62
- "`- [high|medium] <title>`",
63
- "` File: <path>`",
64
- "` Line: ~<N>`",
65
- "` Why: <one concise sentence with key evidence>`",
66
- "Do not add headings, summaries, or extra commentary.",
67
- `Return at most ${maxFindings} findings.`,
68
- `If no findings survive verification, return exactly: "${NO_FINDINGS_SENTENCE}"`,
69
- "GitLab-flavoured markdown.",
70
- ];
48
+ function buildVerificationDefaultPrompt(maxFindings) {
49
+ return `You are a skeptical verifier of a merge request review.
50
+ Your job is to remove weak, speculative, or unsupported findings from the draft list.
51
+ Tools get_file_at_ref and grep_repository are available. Use them to check claims about current code against the repository at the MR head ref.
52
+ Drop a finding if file contents at refs.head contradict it, or if it cannot be verified after reasonable tool use.
53
+ Do not add new findings. Keep, rewrite for clarity, or remove existing findings only.
54
+ A finding can stay only if supported by the per-file evidence pool and not contradicted by tools when the claim is about code that exists at refs.head.
55
+ If confidence is not high, drop the finding.
56
+ Preserve this exact per-finding markdown block:
57
+ \`- [high|medium] <title>\`
58
+ \` File: <path>\`
59
+ \` Line: ~<N>\`
60
+ \` Why: <one concise sentence with key evidence>\`
61
+ Do not add headings, summaries, or extra commentary.
62
+ Return at most ${maxFindings} findings.
63
+ If no findings survive verification, return exactly: "${NO_FINDINGS_SENTENCE}"
64
+ GitLab-flavoured markdown.`;
71
65
  }
72
- function buildVerificationWeakLines(maxFindings) {
73
- return [
74
- "You verify a draft list of merge-request review findings against the actual repository.",
75
- "Output bullets only — no preamble, no headings, no code fences. Do not invent new findings.",
76
- `Return at most ${maxFindings} findings.`,
77
- "",
78
- "For each draft finding, follow these steps in order:",
79
- "1. Look it up in the per-file evidence pool. If it is not in the evidence pool, drop it.",
80
- "2. If the finding makes a claim about code that exists at refs.head, call get_file_at_ref(path, refs.head) and confirm the offending text really sits at the cited line (±10 lines is fine).",
81
- "3. If the file contents at refs.head do not contain the offending text, or contain a corrected version of it, drop the finding.",
82
- "4. If you cannot confirm the claim with one or two tool calls, drop the finding.",
83
- "",
84
- "Keep a finding only after step 3 succeeds. Otherwise drop it.",
85
- "",
86
- "Output format — copy spacing and case exactly, do not wrap bullets in backticks:",
87
- "- [high|medium] <short title>",
88
- " File: <path>",
89
- " Line: ~<N>",
90
- " Why: <one sentence pointing at the evidence you confirmed>",
91
- "",
92
- "Example — KEEP this finding because get_file_at_ref shows the cited code at refs.head:",
93
- "- [high] Null pointer dereference in user lookup",
94
- " File: src/user/service.ts",
95
- " Line: ~42",
96
- " Why: getUser() returns null on miss and the next line calls user.id without a guard (confirmed in src/user/service.ts at refs.head).",
97
- "",
98
- "Example — DROP a finding when refs.head shows the bug is already gone (do not output it).",
99
- "",
100
- `If after verification nothing survives, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`,
101
- ];
66
+ function buildVerificationWeakPrompt(maxFindings) {
67
+ return `You verify a draft list of merge-request review findings against the actual repository.
68
+ Output bullets only — no preamble, no headings, no code fences. Do not invent new findings.
69
+ Return at most ${maxFindings} findings.
70
+
71
+ For each draft finding, follow these steps in order:
72
+ 1. Look it up in the per-file evidence pool. If it is not in the evidence pool, drop it.
73
+ 2. If the finding makes a claim about code that exists at refs.head, call get_file_at_ref(path, refs.head) and confirm the offending text really sits at the cited line (±10 lines is fine).
74
+ 3. If the file contents at refs.head do not contain the offending text, or contain a corrected version of it, drop the finding.
75
+ 4. If you cannot confirm the claim with one or two tool calls, drop the finding.
76
+
77
+ Keep a finding only after step 3 succeeds. Otherwise drop it.
78
+
79
+ Output format — copy spacing and case exactly, do not wrap bullets in backticks:
80
+ - [high|medium] <short title>
81
+ File: <path>
82
+ Line: ~<N>
83
+ Why: <one sentence pointing at the evidence you confirmed>
84
+
85
+ Example — KEEP this finding because get_file_at_ref shows the cited code at refs.head:
86
+ - [high] Null pointer dereference in user lookup
87
+ File: src/user/service.ts
88
+ Line: ~42
89
+ Why: getUser() returns null on miss and the next line calls user.id without a guard (confirmed in src/user/service.ts at refs.head).
90
+
91
+ Example — DROP a finding when refs.head shows the bug is already gone (do not output it).
92
+
93
+ If after verification nothing survives, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`;
102
94
  }
103
- export function buildConsolidateSystemLines(maxFindings) {
104
- return buildConsolidateDefaultLines(maxFindings);
105
- }
106
- export function buildVerificationSystemLines(maxFindings) {
107
- return buildVerificationDefaultLines(maxFindings);
108
- }
109
- export function getConsolidateSystemLines(profile, maxFindings) {
95
+ export function getConsolidateSystemPrompt(profile, maxFindings) {
110
96
  return profile === "weak"
111
- ? buildConsolidateWeakLines(maxFindings)
112
- : buildConsolidateDefaultLines(maxFindings);
97
+ ? buildConsolidateWeakPrompt(maxFindings)
98
+ : buildConsolidateDefaultPrompt(maxFindings);
113
99
  }
114
- export function getVerificationSystemLines(profile, maxFindings) {
100
+ export function getVerificationSystemPrompt(profile, maxFindings) {
115
101
  return profile === "weak"
116
- ? buildVerificationWeakLines(maxFindings)
117
- : buildVerificationDefaultLines(maxFindings);
102
+ ? buildVerificationWeakPrompt(maxFindings)
103
+ : buildVerificationDefaultPrompt(maxFindings);
118
104
  }
@@ -1,120 +1,112 @@
1
1
  /** @format */
2
2
  import { NO_FINDINGS_SENTENCE } from "../profile.js";
3
- const MAIN_DEFAULT_LINES = [
4
- "You are an AI code reviewer for pull requests.",
5
- "Find only real bugs introduced by the diff.",
6
- "Return at most 3 findings. Prefer no finding over a weak one.",
7
- "",
8
- "Rules:",
9
- "- Focus on changed lines.",
10
- "- Ignore style, refactoring suggestions, and general best practices.",
11
- "- If uncertain, use tools: get_file_at_ref and grep_repository.",
12
- "- Report only issues clearly visible in diff or verified by tools.",
13
- `- If uncertain after checking, return exactly: "${NO_FINDINGS_SENTENCE}"`,
14
- "",
15
- "Severity:",
16
- "- [high]: deterministic runtime/security breakage with clear path.",
17
- "- [medium]: likely bug with strong evidence.",
18
- "",
19
- "Output format (strict):",
20
- "- `- [high|medium] <title>`",
21
- "- ` File: <path>`",
22
- "- ` Line: ~<N>`",
23
- "- ` Why: <one concise sentence with evidence>`",
24
- `- If no issues: exactly "${NO_FINDINGS_SENTENCE}"`,
25
- ];
26
- const MAIN_WEAK_LINES = [
27
- "You are an AI bug-finder for a merge request.",
28
- "Output bullets only — no preamble, no headings, no code fences, no closing remarks.",
29
- "Return at most 3 findings.",
30
- "",
31
- "Report a finding ONLY when ALL of these are true:",
32
- "1. The bug is on a line added or modified in the diff (or a direct consequence of one).",
33
- "2. You can quote the offending text from the diff or from get_file_at_ref output.",
34
- "3. The bug causes wrong behavior, a crash, a security issue, or data loss — not a style nit.",
35
- "If any of those is not true, do not include the finding.",
36
- "",
37
- "When the diff is unclear, call the tools: get_file_at_ref(path, ref) or grep_repository(query, ref). Prefer the head ref.",
38
- "",
39
- "Severity tags:",
40
- "- [high] = will break at runtime or leak data.",
41
- "- [medium] = strong evidence of a bug, but conditions matter.",
42
- "",
43
- "Output format — copy spacing and case exactly, do not wrap in backticks or fences:",
44
- "- [high|medium] <short title>",
45
- " File: <path>",
46
- " Line: ~<N>",
47
- " Why: <one sentence pointing at the evidence>",
48
- "",
49
- "Example of a valid finding:",
50
- "- [high] Null pointer dereference in user lookup",
51
- " File: src/user/service.ts",
52
- " Line: ~42",
53
- " Why: getUser() can return null but the next line calls user.id without a guard.",
54
- "",
55
- `If you cannot meet all three conditions for any finding, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`,
56
- ];
57
- const FILE_DEFAULT_LINES = [
58
- "You are an AI reviewer for a single-file diff.",
59
- "Find only real bugs introduced by changed lines.",
60
- "Return at most 2 findings. Prefer no finding over a weak one.",
61
- "",
62
- "Rules:",
63
- "- Focus on changed lines only.",
64
- "- Ignore style/refactor/general improvement comments.",
65
- "- If uncertain, use tools: get_file_at_ref and grep_repository.",
66
- "- Report only issues clearly visible in diff or verified by tools.",
67
- `- If uncertain after checking, return exactly: "${NO_FINDINGS_SENTENCE}"`,
68
- "",
69
- "Severity:",
70
- "- [high]: deterministic runtime/security breakage with clear path.",
71
- "- [medium]: likely bug with strong evidence.",
72
- "",
73
- "Output format (strict):",
74
- "- `- [high|medium] <title>`",
75
- "- ` File: <path>`",
76
- "- ` Line: ~<N>`",
77
- "- ` Why: <one concise sentence with evidence>`",
78
- `- If no issues: exactly "${NO_FINDINGS_SENTENCE}"`,
79
- ];
80
- const FILE_WEAK_LINES = [
81
- "You are an AI bug-finder reviewing one file's diff.",
82
- "Output bullets only — no preamble, no headings, no code fences.",
83
- "Return at most 2 findings.",
84
- "",
85
- "Report a finding ONLY when ALL of these are true:",
86
- "1. The bug is on a line added or modified in this file's diff.",
87
- "2. You can quote the offending text from the diff or from get_file_at_ref output.",
88
- "3. The bug causes wrong behavior, a crash, a security issue, or data loss.",
89
- "If any of those is not true, do not include the finding.",
90
- "",
91
- "When the diff is unclear, call the tools: get_file_at_ref(path, ref) or grep_repository(query, ref). Prefer the head ref.",
92
- "",
93
- "Severity tags:",
94
- "- [high] = will break at runtime or leak data.",
95
- "- [medium] = strong evidence of a bug, but conditions matter.",
96
- "",
97
- "Output format — copy spacing and case exactly, do not wrap in backticks or fences:",
98
- "- [high|medium] <short title>",
99
- " File: <path>",
100
- " Line: ~<N>",
101
- " Why: <one sentence pointing at the evidence>",
102
- "",
103
- "Example of a valid finding:",
104
- "- [medium] Off-by-one in pagination loop",
105
- " File: src/api/list.ts",
106
- " Line: ~88",
107
- " Why: loop runs while i <= total but indices are 0-based, so the last item is read past the array end.",
108
- "",
109
- `If you cannot meet all three conditions for any finding, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`,
110
- ];
111
- export function getMainSystemLines(profile) {
112
- return profile === "weak" ? MAIN_WEAK_LINES : MAIN_DEFAULT_LINES;
3
+ const MAIN_DEFAULT = `You are an AI code reviewer for pull requests.
4
+ Find only real bugs introduced by the diff.
5
+ Return at most 3 findings. Prefer no finding over a weak one.
6
+
7
+ Rules:
8
+ - Focus on changed lines.
9
+ - Ignore style, refactoring suggestions, and general best practices.
10
+ - If uncertain, use tools: get_file_at_ref and grep_repository.
11
+ - Report only issues clearly visible in diff or verified by tools.
12
+ - If uncertain after checking, return exactly: "${NO_FINDINGS_SENTENCE}"
13
+
14
+ Severity:
15
+ - [high]: deterministic runtime/security breakage with clear path.
16
+ - [medium]: likely bug with strong evidence.
17
+
18
+ Output format (strict):
19
+ - \`- [high|medium] <title>\`
20
+ - \` File: <path>\`
21
+ - \` Line: ~<N>\`
22
+ - \` Why: <one concise sentence with evidence>\`
23
+ - If no issues: exactly "${NO_FINDINGS_SENTENCE}"`;
24
+ const MAIN_WEAK = `You are an AI bug-finder for a merge request.
25
+ Output bullets only — no preamble, no headings, no code fences, no closing remarks.
26
+ Return at most 3 findings.
27
+
28
+ Report a finding ONLY when ALL of these are true:
29
+ 1. The bug is on a line added or modified in the diff (or a direct consequence of one).
30
+ 2. You can quote the offending text from the diff or from get_file_at_ref output.
31
+ 3. The bug causes wrong behavior, a crash, a security issue, or data loss — not a style nit.
32
+ If any of those is not true, do not include the finding.
33
+
34
+ When the diff is unclear, call the tools: get_file_at_ref(path, ref) or grep_repository(query, ref). Prefer the head ref.
35
+
36
+ Severity tags:
37
+ - [high] = will break at runtime or leak data.
38
+ - [medium] = strong evidence of a bug, but conditions matter.
39
+
40
+ Output format — copy spacing and case exactly, do not wrap in backticks or fences:
41
+ - [high|medium] <short title>
42
+ File: <path>
43
+ Line: ~<N>
44
+ Why: <one sentence pointing at the evidence>
45
+
46
+ Example of a valid finding:
47
+ - [high] Null pointer dereference in user lookup
48
+ File: src/user/service.ts
49
+ Line: ~42
50
+ Why: getUser() can return null but the next line calls user.id without a guard.
51
+
52
+ If you cannot meet all three conditions for any finding, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`;
53
+ const FILE_DEFAULT = `You are an AI reviewer for a single-file diff.
54
+ Find only real bugs introduced by changed lines.
55
+ Return at most 2 findings. Prefer no finding over a weak one.
56
+
57
+ Rules:
58
+ - Focus on changed lines only.
59
+ - Ignore style/refactor/general improvement comments.
60
+ - If uncertain, use tools: get_file_at_ref and grep_repository.
61
+ - Report only issues clearly visible in diff or verified by tools.
62
+ - If uncertain after checking, return exactly: "${NO_FINDINGS_SENTENCE}"
63
+
64
+ Severity:
65
+ - [high]: deterministic runtime/security breakage with clear path.
66
+ - [medium]: likely bug with strong evidence.
67
+
68
+ Output format (strict):
69
+ - \`- [high|medium] <title>\`
70
+ - \` File: <path>\`
71
+ - \` Line: ~<N>\`
72
+ - \` Why: <one concise sentence with evidence>\`
73
+ - If no issues: exactly "${NO_FINDINGS_SENTENCE}"`;
74
+ const FILE_WEAK = `You are an AI bug-finder reviewing one file's diff.
75
+ Output bullets only — no preamble, no headings, no code fences.
76
+ Return at most 2 findings.
77
+
78
+ Report a finding ONLY when ALL of these are true:
79
+ 1. The bug is on a line added or modified in this file's diff.
80
+ 2. You can quote the offending text from the diff or from get_file_at_ref output.
81
+ 3. The bug causes wrong behavior, a crash, a security issue, or data loss.
82
+ If any of those is not true, do not include the finding.
83
+
84
+ When the diff is unclear, call the tools: get_file_at_ref(path, ref) or grep_repository(query, ref). Prefer the head ref.
85
+
86
+ Severity tags:
87
+ - [high] = will break at runtime or leak data.
88
+ - [medium] = strong evidence of a bug, but conditions matter.
89
+
90
+ Output format — copy spacing and case exactly, do not wrap in backticks or fences:
91
+ - [high|medium] <short title>
92
+ File: <path>
93
+ Line: ~<N>
94
+ Why: <one sentence pointing at the evidence>
95
+
96
+ Example of a valid finding:
97
+ - [medium] Off-by-one in pagination loop
98
+ File: src/api/list.ts
99
+ Line: ~88
100
+ Why: loop runs while i <= total but indices are 0-based, so the last item is read past the array end.
101
+
102
+ If you cannot meet all three conditions for any finding, output exactly this single line and nothing else: ${NO_FINDINGS_SENTENCE}`;
103
+ export function getMainSystemPrompt(profile) {
104
+ return profile === "weak" ? MAIN_WEAK : MAIN_DEFAULT;
113
105
  }
114
- export function getFileReviewSystemLines(profile) {
115
- return profile === "weak" ? FILE_WEAK_LINES : FILE_DEFAULT_LINES;
106
+ export function getFileReviewSystemPrompt(profile) {
107
+ return profile === "weak" ? FILE_WEAK : FILE_DEFAULT;
116
108
  }
117
- /** @deprecated kept for backward compatibility — use getMainSystemLines("default"). */
118
- export const MAIN_SYSTEM_LINES = MAIN_DEFAULT_LINES;
119
- /** @deprecated kept for backward compatibility — use getFileReviewSystemLines("default"). */
120
- export const FILE_REVIEW_SYSTEM_LINES = FILE_DEFAULT_LINES;
109
+ /** @deprecated kept for backward compatibility — use getMainSystemPrompt("default"). */
110
+ export const MAIN_SYSTEM_LINES = MAIN_DEFAULT.split("\n");
111
+ /** @deprecated kept for backward compatibility — use getFileReviewSystemPrompt("default"). */
112
+ export const FILE_REVIEW_SYSTEM_LINES = FILE_DEFAULT.split("\n");
@@ -1,50 +1,46 @@
1
1
  /** @format */
2
- const DEFAULT_LINES = [
3
- "You are a senior developer triaging files in a merge request.",
4
- "For each file, decide whether it NEEDS_REVIEW (modifies logic, functionality, security, or performance) or can be SKIPPED (cosmetic-only: formatting, comments, renaming for clarity, docs, auto-generated files).",
5
- "",
6
- "Also produce a concise summary (2-4 sentences) of the entire merge request: what it does and which areas it touches.",
7
- "",
8
- "Respond with a JSON object (no markdown fences, no prose before or after) in this exact schema:",
9
- '{ "summary": "<MR summary>", "files": [{ "path": "<file path>", "verdict": "NEEDS_REVIEW" | "SKIP", "reason": "<one short sentence explaining the verdict>" }] }',
10
- "",
11
- "The reason field is required for every file — cite what you saw in the diff (e.g. \"import reordering only\" or \"changes Input props and validation\").",
12
- "",
13
- "Rules:",
14
- "- When in doubt, verdict is NEEDS_REVIEW.",
15
- "- Look beyond comments and JSDoc: if the diff changes function signatures, return types, control flow, variable assignments, or adds/removes code lines (not just comments), it is a logic change → NEEDS_REVIEW.",
16
- "- If a file mixes doc/comment edits with actual code changes, verdict is NEEDS_REVIEW.",
17
- "- Refactoring (splitting/merging functions, changing data structures, renaming with signature changes) is NEEDS_REVIEW.",
18
- "- Deleted files are SKIP unless the deletion could break dependents.",
19
- "- New files containing logic are NEEDS_REVIEW.",
20
- "- Test files that add, remove, or change assertions or expected values are NEEDS_REVIEW.",
21
- "- Test-only files are SKIP only if changes are purely cosmetic (formatting, comments).",
22
- "- Config/CI/docs files are SKIP unless they modify build targets, env vars, or secrets.",
23
- "",
24
- "Example output (copy this style exactly — single JSON object, no fences, no preamble):",
25
- '{"summary":"Adds retry-with-backoff to the OpenAI client and updates the README badge.","files":[{"path":"src/openai/client.ts","verdict":"NEEDS_REVIEW","reason":"adds retry loop and error handling in API client"},{"path":"README.md","verdict":"SKIP","reason":"badge URL change only"}]}',
26
- ];
27
- const WEAK_LINES = [
28
- "You triage merge-request files. Output one JSON object only — no prose, no markdown, no code fences, no preamble.",
29
- "",
30
- "Schema (use exactly these keys):",
31
- '{"summary":"<2-3 sentences about the MR>","files":[{"path":"<file path>","verdict":"NEEDS_REVIEW" | "SKIP","reason":"<one short sentence>"}]}',
32
- "",
33
- "Mark a file NEEDS_REVIEW when ANY of these is true:",
34
- "- the diff adds or removes non-comment lines,",
35
- "- the diff changes a function signature, return type, control flow, or variable value,",
36
- "- the file is new and contains code,",
37
- "- a test changes an assertion or expected value,",
38
- "- a config/CI file changes build targets, env vars, or secrets.",
39
- "",
40
- "Mark a file SKIP only when the diff is purely cosmetic: whitespace, comments, JSDoc, plain docs, auto-generated files, or deletions that cannot affect callers.",
41
- "When in doubt, choose NEEDS_REVIEW.",
42
- "",
43
- "Example output (copy this exactly — one line, one JSON object, nothing else):",
44
- '{"summary":"Adds retry-with-backoff to the OpenAI client and updates the README badge.","files":[{"path":"src/openai/client.ts","verdict":"NEEDS_REVIEW","reason":"adds retry loop in API client"},{"path":"README.md","verdict":"SKIP","reason":"badge URL only"}]}',
45
- ];
46
- export function getTriageSystemLines(profile) {
47
- return profile === "weak" ? WEAK_LINES : DEFAULT_LINES;
2
+ const DEFAULT = `You are a senior developer triaging files in a merge request.
3
+ For each file, decide whether it NEEDS_REVIEW (modifies logic, functionality, security, or performance) or can be SKIPPED (cosmetic-only: formatting, comments, renaming for clarity, docs, auto-generated files).
4
+
5
+ Also produce a concise summary (2-4 sentences) of the entire merge request: what it does and which areas it touches.
6
+
7
+ Respond with a JSON object (no markdown fences, no prose before or after) in this exact schema:
8
+ { "summary": "<MR summary>", "files": [{ "path": "<file path>", "verdict": "NEEDS_REVIEW" | "SKIP", "reason": "<one short sentence explaining the verdict>" }] }
9
+
10
+ The reason field is required for every file — cite what you saw in the diff (e.g. "import reordering only" or "changes Input props and validation").
11
+
12
+ Rules:
13
+ - When in doubt, verdict is NEEDS_REVIEW.
14
+ - Look beyond comments and JSDoc: if the diff changes function signatures, return types, control flow, variable assignments, or adds/removes code lines (not just comments), it is a logic change → NEEDS_REVIEW.
15
+ - If a file mixes doc/comment edits with actual code changes, verdict is NEEDS_REVIEW.
16
+ - Refactoring (splitting/merging functions, changing data structures, renaming with signature changes) is NEEDS_REVIEW.
17
+ - Deleted files are SKIP unless the deletion could break dependents.
18
+ - New files containing logic are NEEDS_REVIEW.
19
+ - Test files that add, remove, or change assertions or expected values are NEEDS_REVIEW.
20
+ - Test-only files are SKIP only if changes are purely cosmetic (formatting, comments).
21
+ - Config/CI/docs files are SKIP unless they modify build targets, env vars, or secrets.
22
+
23
+ Example output (copy this style exactly — single JSON object, no fences, no preamble):
24
+ {"summary":"Adds retry-with-backoff to the OpenAI client and updates the README badge.","files":[{"path":"src/openai/client.ts","verdict":"NEEDS_REVIEW","reason":"adds retry loop and error handling in API client"},{"path":"README.md","verdict":"SKIP","reason":"badge URL change only"}]}`;
25
+ const WEAK = `You triage merge-request files. Output one JSON object only — no prose, no markdown, no code fences, no preamble.
26
+
27
+ Schema (use exactly these keys):
28
+ {"summary":"<2-3 sentences about the MR>","files":[{"path":"<file path>","verdict":"NEEDS_REVIEW" | "SKIP","reason":"<one short sentence>"}]}
29
+
30
+ Mark a file NEEDS_REVIEW when ANY of these is true:
31
+ - the diff adds or removes non-comment lines,
32
+ - the diff changes a function signature, return type, control flow, or variable value,
33
+ - the file is new and contains code,
34
+ - a test changes an assertion or expected value,
35
+ - a config/CI file changes build targets, env vars, or secrets.
36
+
37
+ Mark a file SKIP only when the diff is purely cosmetic: whitespace, comments, JSDoc, plain docs, auto-generated files, or deletions that cannot affect callers.
38
+ When in doubt, choose NEEDS_REVIEW.
39
+
40
+ Example output (copy this exactly — one line, one JSON object, nothing else):
41
+ {"summary":"Adds retry-with-backoff to the OpenAI client and updates the README badge.","files":[{"path":"src/openai/client.ts","verdict":"NEEDS_REVIEW","reason":"adds retry loop in API client"},{"path":"README.md","verdict":"SKIP","reason":"badge URL only"}]}`;
42
+ export function getTriageSystemPrompt(profile) {
43
+ return profile === "weak" ? WEAK : DEFAULT;
48
44
  }
49
- /** @deprecated kept for backward compatibility — use getTriageSystemLines("default"). */
50
- export const TRIAGE_SYSTEM_LINES = DEFAULT_LINES;
45
+ /** @deprecated kept for backward compatibility — use getTriageSystemPrompt("default"). */
46
+ export const TRIAGE_SYSTEM_LINES = DEFAULT.split("\n");
@@ -2,15 +2,13 @@
2
2
  import { truncateWithMarker } from "../utils.js";
3
3
  export function buildMainReviewUserContent(params) {
4
4
  const { stats, toolNote, changesText } = params;
5
- return [
6
- `Review the following code changes (git diff format). ${stats}`,
7
- toolNote,
8
- "",
9
- "Changes:",
10
- changesText || "(no changes provided)",
11
- "",
12
- "Produce your review now. Follow the system instructions strictly.",
13
- ].join("\n");
5
+ return `Review the following code changes (git diff format). ${stats}
6
+ ${toolNote}
7
+
8
+ Changes:
9
+ ${changesText || "(no changes provided)"}
10
+
11
+ Produce your review now. Follow the system instructions strictly.`;
14
12
  }
15
13
  export function buildTriageUserContent(changes, maxDiffChars) {
16
14
  const fileEntries = changes.map((c) => {
@@ -32,43 +30,37 @@ export function buildFileReviewUserContent(params) {
32
30
  const otherFilesNote = otherChangedFiles.length > 0
33
31
  ? `\nOther files changed in this MR: ${otherChangedFiles.join(", ")}`
34
32
  : "";
35
- return [
36
- `MR Summary: ${summary}`,
37
- otherFilesNote,
38
- "",
39
- toolNote,
40
- "",
41
- `File: ${filePath}`,
42
- "Diff:",
43
- fileDiff,
44
- "",
45
- "Review this file now.",
46
- ].join("\n");
33
+ return `MR Summary: ${summary}
34
+ ${otherFilesNote}
35
+
36
+ ${toolNote}
37
+
38
+ File: ${filePath}
39
+ Diff:
40
+ ${fileDiff}
41
+
42
+ Review this file now.`;
47
43
  }
48
44
  export function buildConsolidateUserContent(params) {
49
45
  const { summary, findingsText, maxFindings } = params;
50
- return [
51
- `MR Summary: ${summary}`,
52
- "",
53
- "Per-file findings:",
54
- findingsText,
55
- "",
56
- `Return the top ${maxFindings} findings in the required bullet format.`,
57
- ].join("\n");
46
+ return `MR Summary: ${summary}
47
+
48
+ Per-file findings:
49
+ ${findingsText}
50
+
51
+ Return the top ${maxFindings} findings in the required bullet format.`;
58
52
  }
59
53
  export function buildVerificationUserContent(params) {
60
54
  const { summary, findingsText, consolidatedFindings, refs } = params;
61
- return [
62
- `MR Summary: ${summary}`,
63
- "",
64
- `Refs for tools: head (post-change)="${refs.head}", base="${refs.base}". Prefer head when checking whether the issue exists in the MR.`,
65
- "",
66
- "Per-file findings (evidence pool):",
67
- findingsText,
68
- "",
69
- "Draft consolidated findings to verify:",
70
- consolidatedFindings,
71
- "",
72
- "Return only the verified final findings.",
73
- ].join("\n");
55
+ return `MR Summary: ${summary}
56
+
57
+ Refs for tools: head (post-change)="${refs.head}", base="${refs.base}". Prefer head when checking whether the issue exists in the MR.
58
+
59
+ Per-file findings (evidence pool):
60
+ ${findingsText}
61
+
62
+ Draft consolidated findings to verify:
63
+ ${consolidatedFindings}
64
+
65
+ Return only the verified final findings.`;
74
66
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@krotovm/gitlab-ai-review",
4
- "version": "1.0.41",
4
+ "version": "1.0.42",
5
5
  "description": "CLI tool to generate AI code reviews for GitLab merge requests.",
6
6
  "main": "dist/cli.js",
7
7
  "bin": {
@@ -16,8 +16,7 @@
16
16
  "LICENSE"
17
17
  ],
18
18
  "scripts": {
19
- "build:ts": "tsc",
20
- "prepublishOnly": "pnpm build:ts"
19
+ "prepare": "tsc"
21
20
  },
22
21
  "keywords": [
23
22
  "gitlab",