Skip to content

Commit de3bd15

Browse files
author
Gavin Williams
committed
feat(web): GitLab MR Review Agent
Adds support for the AI Review Agent to review GitLab Merge Requests, mirroring the existing GitHub PR review functionality. Also fixes several bugs discovered during implementation and improves the shared review pipeline. --- ## New files ### `packages/web/src/features/agents/review-agent/nodes/gitlabMrParser.ts` Parses a GitLab MR webhook payload into the shared `sourcebot_pr_payload` format. Calls `MergeRequests.show()` and `MergeRequests.allDiffs()` in parallel — the API response is used for `title`, `description`, `sha`, and `diff_refs` (which can be absent in webhook payloads for `update` action events), while per-file diffs are parsed using the existing `parse-diff` library. ### `packages/web/src/features/agents/review-agent/nodes/gitlabPushMrReviews.ts` Posts review comments back to GitLab using `MergeRequestDiscussions.create()` with a position object carrying `base_sha`, `head_sha`, and `start_sha`. Falls back to `MergeRequestNotes.create()` (a general MR note) if the inline comment is rejected by the API (e.g. the line is not within the diff), ensuring reviews are always surfaced even when precise positioning fails. ### Test files (4 new files, 34 tests total) - `githubPrParser.test.ts` — diff parsing and metadata mapping for the GitHub parser - `githubPushPrReviews.test.ts` — single-line vs multi-line comment parameters, error resilience - `gitlabMrParser.test.ts` — API call arguments, metadata mapping, diff parsing, edge cases (empty diffs, nested groups, null description, API failures) - `gitlabPushMrReviews.test.ts` — inline comment posting, fallback behaviour, missing `diff_refs` guard, multi-file iteration --- ## Modified files ### `packages/web/src/features/agents/review-agent/types.ts` - Added `sourcebot_diff_refs` schema/type (`base_sha`, `head_sha`, `start_sha`) and an optional `diff_refs` field on `sourcebot_pr_payload` - Added `GitLabMergeRequestPayload` and `GitLabNotePayload` interfaces for webhook event typing ### `packages/web/src/features/agents/review-agent/app.ts` - Added `processGitLabMergeRequest()` function mirroring `processGitHubPullRequest()`: sets up logging, runs the GitLab parser, generates reviews via the shared LLM pipeline, and pushes results - Removed stale `OPENAI_API_KEY` guards (model availability is now enforced inside `invokeDiffReviewLlm`) ### `packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts` **Replaces the hardcoded OpenAI client** with the Vercel AI SDK's `generateText` and the shared `getAISDKLanguageModelAndOptions` / `getConfiguredLanguageModels` utilities from `chat/utils.server.ts`. The review agent now uses whichever language model is configured in `config.json`, supporting all providers (Anthropic, Bedrock, Azure, etc.). - `REVIEW_AGENT_MODEL` env var (matched against `displayName`) selects a specific model when multiple are configured; falls back to `models[0]` with a warning if the name is not found - Prompt is passed via the `system` parameter with a `"Review the code changes."` user turn, satisfying providers (e.g. Bedrock/Anthropic) that require conversations to begin with a user message ### `packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts` **Fixes "Not authenticated" error** when the review agent calls `getFileSource`. The original implementation used `withOptionalAuth`, which reads a session cookie — absent in webhook handlers. Now calls `getFileSourceForRepo` directly with `__unsafePrisma` and the single-tenant org, bypassing the session-based auth layer. The webhook handler has already authenticated the request via its own mechanism (GitHub App signature / GitLab token). ### `packages/web/src/features/git/getFileSourceApi.ts` - Extracted the core repo-lookup + git + language-detection logic into a new exported `getFileSourceForRepo({ path, repo, ref }, { org, prisma })` function - `getFileSource` now handles auth and audit logging then delegates to `getFileSourceForRepo` — all existing callers are unchanged ### `packages/web/src/app/api/(server)/webhook/route.ts` - Added GitLab webhook handling alongside the existing GitHub branch - Verifies `x-gitlab-token` against `GITLAB_REVIEW_AGENT_WEBHOOK_SECRET` - Handles `Merge Request Hook` events (auto-review on `open`, `update`, `reopen`) and `Note Hook` events (manual `/review` command on MR comments) - Initialises a `Gitlab` client at module load if `GITLAB_REVIEW_AGENT_TOKEN` is set ### `packages/web/src/app/(app)/agents/page.tsx` - Split the single "Review Agent" card into two separate cards: **GitHub Review Agent** and **GitLab Review Agent**, each showing its own configuration status - Removed `OPENAI_API_KEY` from the GitHub card's required env vars (no longer applicable) ### `packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx` & `index.tsx` - Added an **Agents** nav item (with `BotIcon`) between Repositories and Settings - Visible when the user is authenticated **and** at least one agent is configured (GitHub App triple or GitLab token pair), computed in the server component and passed down as `isAgentsVisible` ### `packages/shared/src/env.server.ts` Added four new environment variables: | Variable | Purpose | |---|---| | `GITLAB_REVIEW_AGENT_WEBHOOK_SECRET` | Verifies the `x-gitlab-token` header on incoming webhooks | | `GITLAB_REVIEW_AGENT_TOKEN` | Personal or project access token used for GitLab API calls | | `GITLAB_REVIEW_AGENT_HOST` | GitLab hostname (defaults to `gitlab.com`; set for self-hosted instances) | | `REVIEW_AGENT_MODEL` | `displayName` of the configured language model to use for reviews; falls back to the first model if unset or not matched | ### `packages/web/package.json` Added `@gitbeaker/rest` dependency (already used in `packages/backend`). --- ## Bug fixes | Bug | Fix | |---|---| | `"Not authenticated"` when fetching file content from the review agent | `fetchFileContent` now calls `getFileSourceForRepo` directly instead of `getFileSource` (which gates on session auth) | | `"diff_refs is missing"` when posting GitLab MR reviews | `gitlabMrParser` now fetches the full MR via `MergeRequests.show()` instead of relying on the webhook payload, which omits `diff_refs` on `update` events | | Bedrock/Anthropic rejection: `"A conversation must start with a user message"` | `invokeDiffReviewLlm` now passes the prompt via `system` + a `prompt` user turn instead of a `system`-role entry inside `messages` | | Review agent silently used `models[0]` with no way to specify a different model | New `REVIEW_AGENT_MODEL` env var selects by `displayName` |
1 parent 251f5b5 commit de3bd15

File tree

19 files changed

+1247
-58
lines changed

19 files changed

+1247
-58
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,11 @@ dist
160160

161161
# End of https://www.toptal.com/developers/gitignore/api/yarn,node
162162

163+
.claude
163164
.sourcebot
164165
/bin
165166
/config.json
166167
.DS_Store
167168
oss-licenses.json
168169
oss-license-summary.json
169-
license-audit-result.json
170+
license-audit-result.json

packages/shared/src/env.server.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ const options = {
195195
GITHUB_REVIEW_AGENT_APP_ID: z.string().optional(),
196196
GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET: z.string().optional(),
197197
GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH: z.string().optional(),
198+
// GitLab for review agent
199+
GITLAB_REVIEW_AGENT_WEBHOOK_SECRET: z.string().optional(),
200+
GITLAB_REVIEW_AGENT_TOKEN: z.string().optional(),
201+
GITLAB_REVIEW_AGENT_HOST: z.string().default('gitlab.com'),
202+
REVIEW_AGENT_MODEL: z.string().optional(),
198203
REVIEW_AGENT_API_KEY: z.string().optional(),
199204
REVIEW_AGENT_LOGGING_ENABLED: booleanSchema.default('true'),
200205
REVIEW_AGENT_AUTO_REVIEW_ENABLED: booleanSchema.default('false'),

packages/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"@codemirror/state": "^6.4.1",
5555
"@codemirror/view": "^6.33.0",
5656
"@floating-ui/react": "^0.27.2",
57+
"@gitbeaker/rest": "^40.5.1",
5758
"@grpc/grpc-js": "^1.14.1",
5859
"@grpc/proto-loader": "^0.8.0",
5960
"@hookform/resolvers": "^3.9.0",

packages/web/src/app/(app)/agents/page.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@ import { env } from "@sourcebot/shared";
55

66
const agents = [
77
{
8-
id: "review-agent",
9-
name: "Review Agent",
10-
description: "An AI code review agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase-wide context.",
11-
requiredEnvVars: ["GITHUB_REVIEW_AGENT_APP_ID", "GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET", "GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH", "OPENAI_API_KEY"],
8+
id: "github-review-agent",
9+
name: "GitHub Review Agent",
10+
description: "An AI code review agent that reviews your GitHub PRs. Uses the code indexed on Sourcebot to provide codebase-wide context.",
11+
requiredEnvVars: ["GITHUB_REVIEW_AGENT_APP_ID", "GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET", "GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH"],
12+
configureUrl: "https://docs.sourcebot.dev/docs/features/agents/review-agent"
13+
},
14+
{
15+
id: "gitlab-review-agent",
16+
name: "GitLab Review Agent",
17+
description: "An AI code review agent that reviews your GitLab MRs. Uses the code indexed on Sourcebot to provide codebase-wide context.",
18+
requiredEnvVars: ["GITLAB_REVIEW_AGENT_WEBHOOK_SECRET", "GITLAB_REVIEW_AGENT_TOKEN"],
1219
configureUrl: "https://docs.sourcebot.dev/docs/features/agents/review-agent"
1320
},
1421
];

packages/web/src/app/(app)/components/navigationMenu/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Separator } from "@/components/ui/separator";
77
import { ServiceErrorException } from "@/lib/serviceError";
88
import { isServiceError } from "@/lib/utils";
99
import { OrgRole, RepoIndexingJobStatus, RepoIndexingJobType } from "@sourcebot/db";
10+
import { env } from "@sourcebot/shared";
1011
import Link from "next/link";
1112
import { MeControlDropdownMenu } from "../meControlDropdownMenu";
1213
import WhatsNewIndicator from "../whatsNewIndicator";
@@ -103,6 +104,10 @@ export const NavigationMenu = async () => {
103104
) : false
104105
}
105106
isAuthenticated={isAuthenticated}
107+
isAgentsVisible={isAuthenticated && (
108+
!!(env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) ||
109+
!!(env.GITLAB_REVIEW_AGENT_WEBHOOK_SECRET && env.GITLAB_REVIEW_AGENT_TOKEN)
110+
)}
106111
/>
107112
</NavigationMenuBase>
108113
</div>

packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { NavigationMenuItem, NavigationMenuLink, NavigationMenuList, navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
44
import { Badge } from "@/components/ui/badge";
55
import { cn, getShortenedNumberDisplayString } from "@/lib/utils";
6-
import { SearchIcon, MessageCircleIcon, BookMarkedIcon, SettingsIcon } from "lucide-react";
6+
import { SearchIcon, MessageCircleIcon, BookMarkedIcon, SettingsIcon, BotIcon } from "lucide-react";
77
import { usePathname } from "next/navigation";
88
import { NotificationDot } from "../notificationDot";
99

@@ -12,13 +12,15 @@ interface NavigationItemsProps {
1212
isReposButtonNotificationDotVisible: boolean;
1313
isSettingsButtonNotificationDotVisible: boolean;
1414
isAuthenticated: boolean;
15+
isAgentsVisible: boolean;
1516
}
1617

1718
export const NavigationItems = ({
1819
numberOfRepos,
1920
isReposButtonNotificationDotVisible,
2021
isSettingsButtonNotificationDotVisible,
2122
isAuthenticated,
23+
isAgentsVisible,
2224
}: NavigationItemsProps) => {
2325
const pathname = usePathname();
2426

@@ -65,6 +67,18 @@ export const NavigationItems = ({
6567
</NavigationMenuLink>
6668
{isActive('/repos') && <ActiveIndicator />}
6769
</NavigationMenuItem>
70+
{isAgentsVisible && (
71+
<NavigationMenuItem className="relative">
72+
<NavigationMenuLink
73+
href="/agents"
74+
className={navigationMenuTriggerStyle()}
75+
>
76+
<BotIcon className="w-4 h-4 mr-1" />
77+
Agents
78+
</NavigationMenuLink>
79+
{isActive('/agents') && <ActiveIndicator />}
80+
</NavigationMenuItem>
81+
)}
6882
{isAuthenticated && (
6983
<NavigationMenuItem className="relative">
7084
<NavigationMenuLink

packages/web/src/app/api/(server)/webhook/route.ts

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
import { NextRequest } from "next/server";
44
import { App, Octokit } from "octokit";
55
import { WebhookEventDefinition} from "@octokit/webhooks/types";
6+
import { Gitlab } from "@gitbeaker/rest";
67
import { env } from "@sourcebot/shared";
7-
import { processGitHubPullRequest } from "@/features/agents/review-agent/app";
8+
import { processGitHubPullRequest, processGitLabMergeRequest } from "@/features/agents/review-agent/app";
89
import { throttling, type ThrottlingOptions } from "@octokit/plugin-throttling";
910
import fs from "fs";
10-
import { GitHubPullRequest } from "@/features/agents/review-agent/types";
11+
import { GitHubPullRequest, GitLabMergeRequestPayload, GitLabNotePayload } from "@/features/agents/review-agent/types";
1112
import { createLogger } from "@sourcebot/shared";
1213

13-
const logger = createLogger('github-webhook');
14+
const logger = createLogger('webhook');
1415

1516
const DEFAULT_GITHUB_API_BASE_URL = "https://api.github.com";
1617
type GitHubAppBaseOptions = Omit<ConstructorParameters<typeof App>[0], "Octokit"> & { throttle: ThrottlingOptions };
@@ -95,6 +96,40 @@ function isIssueCommentEvent(eventHeader: string, payload: unknown): payload is
9596
return eventHeader === "issue_comment" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && payload.action === "created";
9697
}
9798

99+
function isGitLabMergeRequestEvent(eventHeader: string, payload: unknown): payload is GitLabMergeRequestPayload {
100+
return (
101+
eventHeader === "Merge Request Hook" &&
102+
typeof payload === "object" &&
103+
payload !== null &&
104+
"object_attributes" in payload &&
105+
typeof (payload as GitLabMergeRequestPayload).object_attributes?.action === "string" &&
106+
["open", "update", "reopen"].includes((payload as GitLabMergeRequestPayload).object_attributes.action)
107+
);
108+
}
109+
110+
function isGitLabNoteEvent(eventHeader: string, payload: unknown): payload is GitLabNotePayload {
111+
return (
112+
eventHeader === "Note Hook" &&
113+
typeof payload === "object" &&
114+
payload !== null &&
115+
"object_attributes" in payload &&
116+
(payload as GitLabNotePayload).object_attributes?.noteable_type === "MergeRequest"
117+
);
118+
}
119+
120+
let gitlabClient: InstanceType<typeof Gitlab> | undefined;
121+
122+
if (env.GITLAB_REVIEW_AGENT_TOKEN) {
123+
try {
124+
gitlabClient = new Gitlab({
125+
host: `https://${env.GITLAB_REVIEW_AGENT_HOST}`,
126+
token: env.GITLAB_REVIEW_AGENT_TOKEN,
127+
});
128+
} catch (error) {
129+
logger.error(`Error initializing GitLab client: ${error}`);
130+
}
131+
}
132+
98133
export const POST = async (request: NextRequest) => {
99134
const body = await request.json();
100135
const headers = Object.fromEntries(Array.from(request.headers.entries(), ([key, value]) => [key.toLowerCase(), value]));
@@ -161,5 +196,62 @@ export const POST = async (request: NextRequest) => {
161196
}
162197
}
163198

199+
const gitlabEvent = headers['x-gitlab-event'];
200+
if (gitlabEvent) {
201+
logger.info('GitLab event received:', gitlabEvent);
202+
203+
const token = headers['x-gitlab-token'];
204+
if (!env.GITLAB_REVIEW_AGENT_WEBHOOK_SECRET || token !== env.GITLAB_REVIEW_AGENT_WEBHOOK_SECRET) {
205+
logger.warn('GitLab webhook token is invalid or GITLAB_REVIEW_AGENT_WEBHOOK_SECRET is not set');
206+
return Response.json({ status: 'ok' });
207+
}
208+
209+
if (!gitlabClient) {
210+
logger.warn('Received GitLab webhook event but GITLAB_REVIEW_AGENT_TOKEN is not set');
211+
return Response.json({ status: 'ok' });
212+
}
213+
214+
if (isGitLabMergeRequestEvent(gitlabEvent, body)) {
215+
if (env.REVIEW_AGENT_AUTO_REVIEW_ENABLED === "false") {
216+
logger.info('Review agent auto review (REVIEW_AGENT_AUTO_REVIEW_ENABLED) is disabled, skipping');
217+
return Response.json({ status: 'ok' });
218+
}
219+
220+
await processGitLabMergeRequest(
221+
gitlabClient,
222+
body.project.id,
223+
body,
224+
env.GITLAB_REVIEW_AGENT_HOST,
225+
);
226+
}
227+
228+
if (isGitLabNoteEvent(gitlabEvent, body)) {
229+
const noteBody = body.object_attributes?.note;
230+
if (noteBody === `/${env.REVIEW_AGENT_REVIEW_COMMAND}`) {
231+
logger.info('Review agent review command received on GitLab MR, processing');
232+
233+
const mrPayload: GitLabMergeRequestPayload = {
234+
object_kind: "merge_request",
235+
object_attributes: {
236+
iid: body.merge_request.iid,
237+
title: body.merge_request.title,
238+
description: body.merge_request.description,
239+
action: "update",
240+
last_commit: body.merge_request.last_commit,
241+
diff_refs: body.merge_request.diff_refs,
242+
},
243+
project: body.project,
244+
};
245+
246+
await processGitLabMergeRequest(
247+
gitlabClient,
248+
body.project.id,
249+
mrPayload,
250+
env.GITLAB_REVIEW_AGENT_HOST,
251+
);
252+
}
253+
}
254+
}
255+
164256
return Response.json({ status: 'ok' });
165257
}

packages/web/src/features/agents/review-agent/app.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { Octokit } from "octokit";
2+
import { Gitlab } from "@gitbeaker/rest";
23
import { generatePrReviews } from "@/features/agents/review-agent/nodes/generatePrReview";
34
import { githubPushPrReviews } from "@/features/agents/review-agent/nodes/githubPushPrReviews";
45
import { githubPrParser } from "@/features/agents/review-agent/nodes/githubPrParser";
6+
import { gitlabMrParser } from "@/features/agents/review-agent/nodes/gitlabMrParser";
7+
import { gitlabPushMrReviews } from "@/features/agents/review-agent/nodes/gitlabPushMrReviews";
8+
import { GitHubPullRequest, GitLabMergeRequestPayload } from "@/features/agents/review-agent/types";
59
import { env } from "@sourcebot/shared";
6-
import { GitHubPullRequest } from "@/features/agents/review-agent/types";
710
import path from "path";
811
import fs from "fs";
912
import { createLogger } from "@sourcebot/shared";
@@ -23,11 +26,6 @@ const logger = createLogger('review-agent');
2326
export async function processGitHubPullRequest(octokit: Octokit, pullRequest: GitHubPullRequest) {
2427
logger.info(`Received a pull request event for #${pullRequest.number}`);
2528

26-
if (!env.OPENAI_API_KEY) {
27-
logger.error("OPENAI_API_KEY is not set, skipping review agent");
28-
return;
29-
}
30-
3129
let reviewAgentLogPath: string | undefined;
3230
if (env.REVIEW_AGENT_LOGGING_ENABLED) {
3331
const reviewAgentLogDir = path.join(env.DATA_CACHE_DIR, "review-agent");
@@ -50,5 +48,38 @@ export async function processGitHubPullRequest(octokit: Octokit, pullRequest: Gi
5048

5149
const prPayload = await githubPrParser(octokit, pullRequest);
5250
const fileDiffReviews = await generatePrReviews(reviewAgentLogPath, prPayload, rules);
53-
await githubPushPrReviews(octokit, prPayload, fileDiffReviews);
51+
await githubPushPrReviews(octokit, prPayload, fileDiffReviews);
52+
}
53+
54+
export async function processGitLabMergeRequest(
55+
gitlabClient: InstanceType<typeof Gitlab>,
56+
projectId: number,
57+
mrPayload: GitLabMergeRequestPayload,
58+
hostDomain: string,
59+
) {
60+
logger.info(`Received a merge request event for !${mrPayload.object_attributes.iid}`);
61+
62+
let reviewAgentLogPath: string | undefined;
63+
if (env.REVIEW_AGENT_LOGGING_ENABLED) {
64+
const reviewAgentLogDir = path.join(env.DATA_CACHE_DIR, "review-agent");
65+
if (!fs.existsSync(reviewAgentLogDir)) {
66+
fs.mkdirSync(reviewAgentLogDir, { recursive: true });
67+
}
68+
69+
const timestamp = new Date().toLocaleString('en-US', {
70+
year: 'numeric',
71+
month: '2-digit',
72+
day: '2-digit',
73+
hour: '2-digit',
74+
minute: '2-digit',
75+
second: '2-digit',
76+
hour12: false
77+
}).replace(/(\d+)\/(\d+)\/(\d+), (\d+):(\d+):(\d+)/, '$3_$1_$2_$4_$5_$6');
78+
reviewAgentLogPath = path.join(reviewAgentLogDir, `review-agent-mr-${mrPayload.object_attributes.iid}-${timestamp}.log`);
79+
logger.info(`Review agent logging to ${reviewAgentLogPath}`);
80+
}
81+
82+
const prPayload = await gitlabMrParser(gitlabClient, mrPayload, hostDomain);
83+
const fileDiffReviews = await generatePrReviews(reviewAgentLogPath, prPayload, rules);
84+
await gitlabPushMrReviews(gitlabClient, projectId, prPayload, fileDiffReviews);
5485
}
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
import { sourcebot_context, sourcebot_pr_payload } from "@/features/agents/review-agent/types";
2-
import { fileSourceResponseSchema, getFileSource } from '@/features/git';
2+
import { fileSourceResponseSchema, getFileSourceForRepo } from '@/features/git';
3+
import { SINGLE_TENANT_ORG_ID } from "@/lib/constants";
34
import { isServiceError } from "@/lib/utils";
5+
import { __unsafePrisma } from "@/prisma";
46
import { createLogger } from "@sourcebot/shared";
57

68
const logger = createLogger('fetch-file-content');
79

810
export const fetchFileContent = async (pr_payload: sourcebot_pr_payload, filename: string): Promise<sourcebot_context> => {
911
logger.debug("Executing fetch_file_content");
1012

13+
const org = await __unsafePrisma.org.findUnique({
14+
where: { id: SINGLE_TENANT_ORG_ID },
15+
});
16+
if (!org) {
17+
throw new Error("Organization not found");
18+
}
19+
1120
const repoPath = pr_payload.hostDomain + "/" + pr_payload.owner + "/" + pr_payload.repo;
1221
const fileSourceRequest = {
1322
path: filename,
1423
repo: repoPath,
15-
}
24+
};
1625
logger.debug(JSON.stringify(fileSourceRequest, null, 2));
1726

18-
const response = await getFileSource(fileSourceRequest);
27+
const response = await getFileSourceForRepo(fileSourceRequest, { org, prisma: __unsafePrisma });
1928
if (isServiceError(response)) {
2029
throw new Error(`Failed to fetch file content for ${filename} from ${repoPath}: ${response.message}`);
2130
}
@@ -27,8 +36,8 @@ export const fetchFileContent = async (pr_payload: sourcebot_pr_payload, filenam
2736
type: "file_content",
2837
description: `The content of the file ${filename}`,
2938
context: fileContent,
30-
}
39+
};
3140

3241
logger.debug("Completed fetch_file_content");
3342
return fileContentContext;
34-
}
43+
};

0 commit comments

Comments
 (0)