Skip to content

Commit 6fdb402

Browse files
feat(web): replace language param with path in symbol tools, make repo mandatory
- find_symbol_definitions and find_symbol_references now accept `path` (the file where the symbol was encountered) instead of `language` - Language is derived internally via detectLanguageFromFilename - `repo` is now required in both tool schemas - Also fixes ambiguous extension overrides in languageDetection.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e0d4ff9 commit 6fdb402

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { z } from "zod";
2+
import { isServiceError } from "@/lib/utils";
3+
import { findSearchBasedSymbolDefinitions } from "@/features/codeNav/api";
4+
import { addLineNumbers } from "@/features/chat/utils";
5+
import { ToolDefinition } from "./types";
6+
import { FindSymbolFile } from "./findSymbolReferences";
7+
import { logger } from "./logger";
8+
import description from "./findSymbolDefinitions.txt";
9+
import { detectLanguageFromFilename } from "@/lib/languageDetection";
10+
11+
const findSymbolDefinitionsShape = {
12+
symbol: z.string().describe("The symbol to find definitions of"),
13+
path: z.string().describe("The file path where the symbol was encountered."),
14+
repo: z.string().describe("The repository to scope the search to"),
15+
};
16+
17+
export type FindSymbolDefinitionsMetadata = {
18+
files: FindSymbolFile[];
19+
};
20+
21+
export const findSymbolDefinitionsDefinition: ToolDefinition<
22+
'find_symbol_definitions',
23+
typeof findSymbolDefinitionsShape,
24+
FindSymbolDefinitionsMetadata
25+
> = {
26+
name: 'find_symbol_definitions',
27+
title: 'Find symbol definitions',
28+
isReadOnly: true,
29+
isIdempotent: true,
30+
description,
31+
inputSchema: z.object(findSymbolDefinitionsShape),
32+
execute: async ({ symbol, path, repo }, _context) => {
33+
logger.debug('find_symbol_definitions', { symbol, path, repo });
34+
const revision = "HEAD";
35+
const language = detectLanguageFromFilename(path);
36+
37+
const response = await findSearchBasedSymbolDefinitions({
38+
symbolName: symbol,
39+
language,
40+
revisionName: revision,
41+
repoName: repo,
42+
});
43+
44+
if (isServiceError(response)) {
45+
throw new Error(response.message);
46+
}
47+
48+
const metadata: FindSymbolDefinitionsMetadata = {
49+
files: response.files.map((file) => ({
50+
fileName: file.fileName,
51+
repo: file.repository,
52+
language: file.language,
53+
matches: file.matches.map(({ lineContent, range }) => {
54+
return addLineNumbers(lineContent, range.start.lineNumber);
55+
}),
56+
revision,
57+
})),
58+
};
59+
60+
return {
61+
output: JSON.stringify(metadata),
62+
metadata,
63+
};
64+
},
65+
};
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { z } from "zod";
2+
import { isServiceError } from "@/lib/utils";
3+
import { findSearchBasedSymbolReferences } from "@/features/codeNav/api";
4+
import { addLineNumbers } from "@/features/chat/utils";
5+
import { ToolDefinition } from "./types";
6+
import { logger } from "./logger";
7+
import description from "./findSymbolReferences.txt";
8+
import { detectLanguageFromFilename } from "@/lib/languageDetection";
9+
10+
const findSymbolReferencesShape = {
11+
symbol: z.string().describe("The symbol to find references to"),
12+
path: z.string().describe("The file path where the symbol was encountered."),
13+
repo: z.string().describe("The repository to scope the search to"),
14+
};
15+
16+
export type FindSymbolFile = {
17+
fileName: string;
18+
repo: string;
19+
language: string;
20+
matches: string[];
21+
revision: string;
22+
};
23+
24+
export type FindSymbolReferencesMetadata = {
25+
files: FindSymbolFile[];
26+
};
27+
28+
export const findSymbolReferencesDefinition: ToolDefinition<
29+
'find_symbol_references',
30+
typeof findSymbolReferencesShape,
31+
FindSymbolReferencesMetadata
32+
> = {
33+
name: 'find_symbol_references',
34+
title: 'Find symbol references',
35+
isReadOnly: true,
36+
isIdempotent: true,
37+
description,
38+
inputSchema: z.object(findSymbolReferencesShape),
39+
execute: async ({ symbol, path, repo }, _context) => {
40+
logger.debug('find_symbol_references', { symbol, path, repo });
41+
const revision = "HEAD";
42+
const language = detectLanguageFromFilename(path);
43+
44+
const response = await findSearchBasedSymbolReferences({
45+
symbolName: symbol,
46+
language,
47+
revisionName: revision,
48+
repoName: repo,
49+
});
50+
51+
if (isServiceError(response)) {
52+
throw new Error(response.message);
53+
}
54+
55+
const metadata: FindSymbolReferencesMetadata = {
56+
files: response.files.map((file) => ({
57+
fileName: file.fileName,
58+
repo: file.repository,
59+
language: file.language,
60+
matches: file.matches.map(({ lineContent, range }) => {
61+
return addLineNumbers(lineContent, range.start.lineNumber);
62+
}),
63+
revision,
64+
})),
65+
};
66+
67+
return {
68+
output: JSON.stringify(metadata),
69+
metadata,
70+
};
71+
},
72+
};

packages/web/src/lib/languageDetection.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
import * as linguistLanguages from 'linguist-languages';
22
import path from 'path';
33

4+
// Override map for extensions that are ambiguous in linguist-languages.
5+
// These are extensions where linguist maps to multiple languages, but one
6+
// is overwhelmingly more common in practice.
7+
const ambiguousExtensionOverrides: Record<string, string> = {
8+
'.cs': 'C#', // Not Smalltalk
9+
'.fs': 'F#', // Not Forth, GLSL, or Filterscript
10+
'.html': 'HTML', // Not Ecmarkup
11+
'.json': 'JSON', // Not OASv2-json, OASv3-json
12+
'.md': 'Markdown', // Not GCC Machine Description
13+
'.rs': 'Rust', // Not RenderScript (deprecated)
14+
'.tsx': 'TSX', // Not XML
15+
'.ts': 'TypeScript', // Not XML
16+
'.txt': 'Text', // Not Adblock Filter List, Vim Help File
17+
'.yaml': 'YAML', // Not MiniYAML, OASv2-yaml, OASv3-yaml
18+
'.yml': 'YAML',
19+
};
20+
421
const extensionToLanguage = new Map<string, string>();
522

623
for (const [languageName, languageData] of Object.entries(linguistLanguages)) {
@@ -31,6 +48,12 @@ export const detectLanguageFromFilename = (filename: string): string => {
3148

3249
// Check for extension match
3350
const ext = path.extname(filename).toLowerCase();
51+
52+
// Check override map first for ambiguous extensions
53+
if (ext && ext in ambiguousExtensionOverrides) {
54+
return ambiguousExtensionOverrides[ext];
55+
}
56+
3457
if (ext && extensionToLanguage.has(ext)) {
3558
return extensionToLanguage.get(ext)!;
3659
}

0 commit comments

Comments
 (0)