Skip to content

Commit 2479ef2

Browse files
Merge branch 'main' into feat-gitlab-mr-agent
2 parents 0944a80 + 14ca65d commit 2479ef2

18 files changed

Lines changed: 248 additions & 43 deletions

File tree

.github/workflows/trivy-vulnerability-triage.yml renamed to .github/workflows/vulnerability-triage.yml

Lines changed: 103 additions & 38 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- Added a `get_diff` MCP tool to return structured diffs between two git refs. [#1142](https://github.com/sourcebot-dev/sourcebot/pull/1142)
12+
13+
### Changed
14+
- Synced `vendor/zoekt` with upstream `sourcegraph/zoekt`. [#1140](https://github.com/sourcebot-dev/sourcebot/pull/1140)
15+
1016
## [4.16.12] - 2026-04-20
1117

1218
### Fixed

docs/docs/features/mcp-server.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,17 @@ Parameters:
388388
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
389389
| `limit` | no | Maximum number of files to return (default: 100). |
390390

391+
### `get_diff`
392+
393+
Returns a structured diff between two refs in a repository using a two-dot comparison.
394+
395+
Parameters:
396+
| Name | Required | Description |
397+
|:----------|:---------|:--------------------------------------------------------------------------------------------------------------|
398+
| `repo` | yes | The fully-qualified repository name. |
399+
| `base` | yes | The base git ref (branch, tag, or commit SHA) to diff from. |
400+
| `head` | yes | The head git ref (branch, tag, or commit SHA) to diff to. |
401+
391402
### `find_symbol_definitions`
392403

393404
Finds where a symbol (function, class, variable, etc.) is defined in a repository.

packages/backend/src/zoekt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const indexGitRepository = async (repo: Repo, settings: Settings, revisio
2323
`-branches "${revisions.join(',')}"`,
2424
`-tenant_id ${repo.orgId}`,
2525
`-repo_id ${repo.id}`,
26-
`-shard_prefix ${shardPrefix}`,
26+
`-shard_prefix_override ${shardPrefix}`,
2727
...largeFileGlobPatterns.map((pattern) => `-large_file "${pattern}"`),
2828
repoPath
2929
].join(' ');

packages/web/src/features/chat/agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ const createPrompt = ({
271271
The user has explicitly selected the following repositories for analysis:
272272
${repos.map(repo => `- ${repo}`).join('\n')}
273273
274-
When calling tools that accept a \`repo\` parameter (e.g. \`read_file\`, \`list_commits\`, \`list_tree\`, \`grep\`), use these repository names exactly as listed above, including the full host prefix (e.g. \`github.com/org/repo\`).
274+
When calling tools that accept a \`repo\` parameter (e.g. \`read_file\`, \`list_commits\`, \`list_tree\`, \`get_diff\`, \`grep\`), use these repository names exactly as listed above, including the full host prefix (e.g. \`github.com/org/repo\`).
275275
276276
When using \`grep\` to search across ALL selected repositories (e.g. "which repos have X?"), omit the \`repo\` parameter entirely — the tool will automatically search across all selected repositories in a single call. Do NOT call \`grep\` once per repository when a single broad search would suffice.
277277
</selected_repositories>

packages/web/src/features/chat/components/chatThread/detailsCard.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { FindSymbolDefinitionsToolComponent } from './tools/findSymbolDefinition
1919
import { FindSymbolReferencesToolComponent } from './tools/findSymbolReferencesToolComponent';
2020
import { GlobToolComponent } from './tools/globToolComponent';
2121
import { GrepToolComponent } from './tools/grepToolComponent';
22+
import { GetDiffToolComponent } from './tools/getDiffToolComponent';
2223
import { ListCommitsToolComponent } from './tools/listCommitsToolComponent';
2324
import { ListReposToolComponent } from './tools/listReposToolComponent';
2425
import { ListTreeToolComponent } from './tools/listTreeToolComponent';
@@ -289,6 +290,15 @@ export const StepPartRenderer = ({ part }: { part: SBChatMessagePart }) => {
289290
{(output) => <ListCommitsToolComponent {...output} />}
290291
</ToolOutputGuard>
291292
)
293+
case 'tool-get_diff':
294+
return (
295+
<ToolOutputGuard
296+
part={part}
297+
loadingText="Comparing revisions..."
298+
>
299+
{(output) => <GetDiffToolComponent {...output} />}
300+
</ToolOutputGuard>
301+
)
292302
case 'tool-list_tree':
293303
return (
294304
<ToolOutputGuard
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client';
2+
3+
import { Separator } from '@/components/ui/separator';
4+
import { GetDiffMetadata, ToolResult } from '@/features/tools';
5+
6+
export const GetDiffToolComponent = ({ metadata }: ToolResult<GetDiffMetadata>) => {
7+
const fileCount = metadata.files.length;
8+
9+
return (
10+
<div className="flex items-center gap-2 select-none cursor-default text-sm text-muted-foreground">
11+
<span className="flex-shrink-0">Compared</span>
12+
<span className="text-xs font-mono bg-muted px-1.5 py-0.5 rounded text-foreground">
13+
{metadata.base}
14+
</span>
15+
<span className="flex-shrink-0">to</span>
16+
<span className="text-xs font-mono bg-muted px-1.5 py-0.5 rounded text-foreground">
17+
{metadata.head}
18+
</span>
19+
<span className="flex-shrink-0">in</span>
20+
<span className="text-xs font-mono bg-muted px-1.5 py-0.5 rounded text-foreground truncate">
21+
{metadata.repo}
22+
</span>
23+
<span className="flex-1" />
24+
<span className="text-xs flex-shrink-0">
25+
{fileCount} changed {fileCount === 1 ? 'file' : 'files'}
26+
</span>
27+
<Separator orientation="vertical" className="h-3 flex-shrink-0" />
28+
</div>
29+
);
30+
};

packages/web/src/features/chat/tools.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
readFileDefinition,
44
listCommitsDefinition,
55
listReposDefinition,
6+
getDiffDefinition,
67
grepDefinition,
78
globDefinition,
89
findSymbolReferencesDefinition,
@@ -17,6 +18,7 @@ export const createTools = (context: ToolContext) => ({
1718
[readFileDefinition.name]: toVercelAITool(readFileDefinition, context),
1819
[listCommitsDefinition.name]: toVercelAITool(listCommitsDefinition, context),
1920
[listReposDefinition.name]: toVercelAITool(listReposDefinition, context),
21+
[getDiffDefinition.name]: toVercelAITool(getDiffDefinition, context),
2022
[grepDefinition.name]: toVercelAITool(grepDefinition, context),
2123
[globDefinition.name]: toVercelAITool(globDefinition, context),
2224
[findSymbolReferencesDefinition.name]: toVercelAITool(findSymbolReferencesDefinition, context),
@@ -27,6 +29,7 @@ export const createTools = (context: ToolContext) => ({
2729
export type ReadFileToolUIPart = ToolUIPart<{ read_file: SBChatMessageToolTypes['read_file'] }>;
2830
export type ListCommitsToolUIPart = ToolUIPart<{ list_commits: SBChatMessageToolTypes['list_commits'] }>;
2931
export type ListReposToolUIPart = ToolUIPart<{ list_repos: SBChatMessageToolTypes['list_repos'] }>;
32+
export type GetDiffToolUIPart = ToolUIPart<{ get_diff: SBChatMessageToolTypes['get_diff'] }>;
3033
export type GrepToolUIPart = ToolUIPart<{ grep: SBChatMessageToolTypes['grep'] }>;
3134
export type GlobToolUIPart = ToolUIPart<{ glob: SBChatMessageToolTypes['glob'] }>;
3235
export type FindSymbolReferencesToolUIPart = ToolUIPart<{ find_symbol_references: SBChatMessageToolTypes['find_symbol_references'] }>;

packages/web/src/features/mcp/server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { getConfiguredLanguageModelsInfo } from "../chat/utils.server";
1313
import {
1414
findSymbolDefinitionsDefinition,
1515
findSymbolReferencesDefinition,
16+
getDiffDefinition,
1617
listCommitsDefinition,
1718
listReposDefinition,
1819
listTreeDefinition,
@@ -40,6 +41,7 @@ export async function createMcpServer(): Promise<McpServer> {
4041

4142
registerMcpTool(server, grepDefinition, toolContext);
4243
registerMcpTool(server, globDefinition, toolContext);
44+
registerMcpTool(server, getDiffDefinition, toolContext);
4345
registerMcpTool(server, listCommitsDefinition, toolContext);
4446
registerMcpTool(server, listReposDefinition, toolContext);
4547
registerMcpTool(server, readFileDefinition, toolContext);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { getDiff, GetDiffResult } from '@/features/git';
2+
import { getDiffRequestSchema } from '@/features/git/schemas';
3+
import { isServiceError } from '@/lib/utils';
4+
import description from './getDiff.txt';
5+
import { logger } from './logger';
6+
import { ToolDefinition } from './types';
7+
8+
export type GetDiffMetadata = GetDiffResult & {
9+
repo: string;
10+
base: string;
11+
head: string;
12+
};
13+
14+
export const getDiffDefinition: ToolDefinition<'get_diff', typeof getDiffRequestSchema.shape, GetDiffMetadata> = {
15+
name: 'get_diff',
16+
title: 'Get diff',
17+
isReadOnly: true,
18+
isIdempotent: true,
19+
description,
20+
inputSchema: getDiffRequestSchema,
21+
execute: async ({ repo, base, head }, _context) => {
22+
logger.debug('get_diff', { repo, base, head });
23+
24+
const response = await getDiff({ repo, base, head });
25+
26+
if (isServiceError(response)) {
27+
throw new Error(response.message);
28+
}
29+
30+
return {
31+
output: JSON.stringify(response),
32+
metadata: {
33+
...response,
34+
repo,
35+
base,
36+
head,
37+
},
38+
};
39+
},
40+
};

0 commit comments

Comments
 (0)