codesearch-mcp 0.0.17-alpha.1 → 0.1.0-alpha
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/CHANGELOG.md +48 -0
- package/README.md +95 -6
- package/dist/app.js +70 -10
- package/dist/src/commands/ConfigCommand.d.ts +1 -0
- package/dist/src/commands/ConfigCommand.js +84 -4
- package/dist/src/commands/IndexCommand.js +35 -7
- package/dist/src/commands/McpCommand.js +9 -4
- package/dist/src/commands/{SetupCommand.d.ts → WebCommand.d.ts} +3 -2
- package/dist/src/commands/WebCommand.js +56 -0
- package/dist/src/config-store.d.ts +85 -4
- package/dist/src/config-store.js +157 -32
- package/dist/src/config.d.ts +32 -8
- package/dist/src/config.js +93 -30
- package/dist/src/constants.d.ts +35 -0
- package/dist/src/constants.js +57 -4
- package/dist/src/container.d.ts +10 -6
- package/dist/src/container.js +10 -5
- package/dist/src/db/index.d.ts +1 -1
- package/dist/src/db/index.js +2 -1
- package/dist/src/db/schema.d.ts +111 -0
- package/dist/src/db/schema.js +15 -1
- package/dist/src/graph.d.ts +60 -0
- package/dist/src/graph.js +239 -120
- package/dist/src/help.js +20 -5
- package/dist/src/interfaces/IAstParser.d.ts +20 -3
- package/dist/src/interfaces/IChunker.d.ts +24 -0
- package/dist/src/interfaces/IChunker.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +27 -0
- package/dist/src/interfaces/IDatabase.js +2 -0
- package/dist/src/interfaces/IEmbedder.d.ts +15 -2
- package/dist/src/interfaces/ILogger.d.ts +10 -0
- package/dist/src/interfaces/ILogger.js +2 -0
- package/dist/src/interfaces/IMcpToolHandler.d.ts +22 -0
- package/dist/src/interfaces/IMcpToolHandler.js +2 -0
- package/dist/src/interfaces/IMonorepoResolver.d.ts +33 -0
- package/dist/src/interfaces/IMonorepoResolver.js +2 -0
- package/dist/src/interfaces/IProjectDiscovery.d.ts +28 -0
- package/dist/src/interfaces/IProjectDiscovery.js +2 -0
- package/dist/src/interfaces/ISearcher.d.ts +37 -0
- package/dist/src/interfaces/ISearcher.js +2 -0
- package/dist/src/interfaces/index.d.ts +8 -0
- package/dist/src/interfaces/index.js +2 -0
- package/dist/src/owner-path.d.ts +10 -0
- package/dist/src/owner-path.js +27 -0
- package/dist/src/project-selector.d.ts +1 -1
- package/dist/src/services/AstChunker.d.ts +31 -0
- package/dist/src/services/AstChunker.js +114 -0
- package/dist/src/services/AstParserService.d.ts +10 -6
- package/dist/src/services/AstParserService.js +60 -15
- package/dist/src/services/DatabaseService.d.ts +14 -1
- package/dist/src/services/DatabaseService.js +70 -25
- package/dist/src/services/EmbedderFactory.d.ts +9 -0
- package/dist/src/services/EmbedderFactory.js +29 -0
- package/dist/src/services/IndexerService.d.ts +22 -12
- package/dist/src/services/IndexerService.js +106 -46
- package/dist/src/services/InfinityEmbedder.d.ts +79 -0
- package/dist/src/services/InfinityEmbedder.js +199 -0
- package/dist/src/services/LoggerService.d.ts +2 -1
- package/dist/src/services/McpServer.d.ts +16 -7
- package/dist/src/services/McpServer.js +54 -367
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +62 -0
- package/dist/src/services/MonorepoAwareDiscoveryService.js +125 -0
- package/dist/src/services/NxMonorepoResolver.d.ts +45 -0
- package/dist/src/services/NxMonorepoResolver.js +308 -0
- package/dist/src/services/OllamaEmbedder.d.ts +19 -5
- package/dist/src/services/OllamaEmbedder.js +91 -36
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +25 -0
- package/dist/src/services/OpenAICompatibleEmbedder.js +75 -0
- package/dist/src/services/ProjectDiscoveryService.d.ts +8 -0
- package/dist/src/{env-loader.js → services/ProjectDiscoveryService.js} +16 -5
- package/dist/src/services/SearcherService.d.ts +46 -7
- package/dist/src/services/SearcherService.js +183 -53
- package/dist/src/services/VoyageEmbedder.d.ts +22 -0
- package/dist/src/services/VoyageEmbedder.js +72 -0
- package/dist/src/services/WebServerService.d.ts +77 -0
- package/dist/src/services/WebServerService.js +267 -0
- package/dist/src/services/http-retry.d.ts +19 -0
- package/dist/src/services/http-retry.js +54 -0
- package/dist/src/services/reranker.d.ts +15 -0
- package/dist/src/services/reranker.js +44 -0
- package/dist/src/setup.d.ts +6 -0
- package/dist/src/setup.js +249 -31
- package/dist/src/tools/FindSymbolTool.d.ts +8 -0
- package/dist/src/tools/FindSymbolTool.js +45 -0
- package/dist/src/tools/GetCallersTool.d.ts +8 -0
- package/dist/src/tools/GetCallersTool.js +42 -0
- package/dist/src/tools/GetDependenciesTool.d.ts +8 -0
- package/dist/src/tools/GetDependenciesTool.js +38 -0
- package/dist/src/tools/GetDependentsTool.d.ts +8 -0
- package/dist/src/tools/GetDependentsTool.js +57 -0
- package/dist/src/tools/ListFilesTool.d.ts +8 -0
- package/dist/src/tools/ListFilesTool.js +39 -0
- package/dist/src/tools/ListProjectsTool.d.ts +8 -0
- package/dist/src/tools/ListProjectsTool.js +44 -0
- package/dist/src/tools/ReadFileRangeTool.d.ts +8 -0
- package/dist/src/tools/ReadFileRangeTool.js +44 -0
- package/dist/src/tools/ReadFileTool.d.ts +8 -0
- package/dist/src/tools/ReadFileTool.js +42 -0
- package/dist/src/tools/SearchCodebaseTool.d.ts +13 -0
- package/dist/src/tools/SearchCodebaseTool.js +81 -0
- package/dist/src/tools/format-helpers.d.ts +8 -0
- package/dist/src/tools/format-helpers.js +58 -0
- package/dist/src/tools/index.d.ts +9 -0
- package/dist/src/tools/index.js +21 -0
- package/dist/src/web/dashboard.d.ts +9 -0
- package/dist/src/web/dashboard.js +342 -0
- package/docker/docker-compose.infinity-gpu.yml +19 -0
- package/docker/docker-compose.yml +25 -0
- package/docs/web-dashboard.md +187 -0
- package/package.json +25 -3
- package/dist/src/ast.d.ts +0 -2
- package/dist/src/ast.js +0 -110
- package/dist/src/commands/SetupCommand.js +0 -14
- package/dist/src/db/data-source.d.ts +0 -2
- package/dist/src/db/data-source.js +0 -13
- package/dist/src/db/entities/code-chunk.entity.d.ts +0 -9
- package/dist/src/db/entities/code-chunk.entity.js +0 -54
- package/dist/src/db/entities/code-dependency.entity.d.ts +0 -9
- package/dist/src/db/entities/code-dependency.entity.js +0 -54
- package/dist/src/db/entities/code-symbol.entity.d.ts +0 -10
- package/dist/src/db/entities/code-symbol.entity.js +0 -59
- package/dist/src/db/init.d.ts +0 -2
- package/dist/src/db/init.js +0 -62
- package/dist/src/env-loader.d.ts +0 -1
- package/dist/src/indexer.d.ts +0 -17
- package/dist/src/indexer.js +0 -242
- package/dist/src/mcp.d.ts +0 -1
- package/dist/src/mcp.js +0 -403
- package/dist/src/ollama.d.ts +0 -2
- package/dist/src/ollama.js +0 -6
- package/dist/src/searcher.d.ts +0 -74
- package/dist/src/searcher.js +0 -446
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -9
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListFilesTool = void 0;
|
|
4
|
+
const format_helpers_1 = require("./format-helpers");
|
|
5
|
+
class ListFilesTool {
|
|
6
|
+
searcher;
|
|
7
|
+
constructor(searcher) {
|
|
8
|
+
this.searcher = searcher;
|
|
9
|
+
}
|
|
10
|
+
definition() {
|
|
11
|
+
return {
|
|
12
|
+
name: 'list_files',
|
|
13
|
+
description: 'List files and directories in a project path as an indented tree. Use to understand project structure before reading files.',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
path: { type: 'string', description: 'Directory path relative to the project root (e.g. "src/components" or "." for root)' },
|
|
18
|
+
project: { type: 'string', description: 'Project name (from list_projects)' },
|
|
19
|
+
depth: { type: 'number', description: 'Tree depth: 1 = immediate children only, up to 5. Default: 2.' },
|
|
20
|
+
pattern: { type: 'string', description: 'Optional filename filter (substring or extension like ".ts"). Only matching files are shown.' },
|
|
21
|
+
},
|
|
22
|
+
required: ['path', 'project'],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async handle(args) {
|
|
27
|
+
const relPath = String(args.path ?? '.');
|
|
28
|
+
const project = String(args.project ?? '');
|
|
29
|
+
const rawDepth = Number(args.depth);
|
|
30
|
+
const depth = isNaN(rawDepth) || rawDepth < 1 ? 2 : Math.min(5, rawDepth);
|
|
31
|
+
const pattern = args.pattern ? String(args.pattern) : null;
|
|
32
|
+
const { tree, error } = this.searcher.listFiles(relPath, project, depth, pattern);
|
|
33
|
+
if (error) {
|
|
34
|
+
return (0, format_helpers_1.textResponse)(`list_files error: ${error}`);
|
|
35
|
+
}
|
|
36
|
+
return (0, format_helpers_1.textResponse)(`**${project}/${relPath}**\n\`\`\`\n${tree}\n\`\`\``);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.ListFilesTool = ListFilesTool;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IMcpToolHandler, ToolDefinition, ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
2
|
+
import type { ISearcher } from '../interfaces/ISearcher';
|
|
3
|
+
export declare class ListProjectsTool implements IMcpToolHandler {
|
|
4
|
+
private searcher;
|
|
5
|
+
constructor(searcher: ISearcher);
|
|
6
|
+
definition(): ToolDefinition;
|
|
7
|
+
handle(_args: Record<string, unknown>): Promise<ToolResponse>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListProjectsTool = void 0;
|
|
4
|
+
const format_helpers_1 = require("./format-helpers");
|
|
5
|
+
class ListProjectsTool {
|
|
6
|
+
searcher;
|
|
7
|
+
constructor(searcher) {
|
|
8
|
+
this.searcher = searcher;
|
|
9
|
+
}
|
|
10
|
+
definition() {
|
|
11
|
+
return {
|
|
12
|
+
name: 'list_projects',
|
|
13
|
+
description: 'List all indexed projects with their chunk counts and metadata (description, stack, notes, relations from .ragmeta.json).',
|
|
14
|
+
inputSchema: { type: 'object', properties: {} },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async handle(_args) {
|
|
18
|
+
const rows = await this.searcher.listProjects();
|
|
19
|
+
if (rows.length === 0) {
|
|
20
|
+
return (0, format_helpers_1.textResponse)('No indexed projects found.');
|
|
21
|
+
}
|
|
22
|
+
return (0, format_helpers_1.textResponse)(rows.map(r => {
|
|
23
|
+
const lines = [`${r.serviceName}: ${r.chunks} chunks`];
|
|
24
|
+
if (r.packageName)
|
|
25
|
+
lines.push(` package: ${r.packageName}`);
|
|
26
|
+
if (r.type)
|
|
27
|
+
lines.push(` type: ${r.type}`);
|
|
28
|
+
if (r.description)
|
|
29
|
+
lines.push(` description: ${r.description}`);
|
|
30
|
+
if (r.stack)
|
|
31
|
+
lines.push(` stack: ${r.stack}`);
|
|
32
|
+
if (r.notes)
|
|
33
|
+
lines.push(` notes: ${r.notes}`);
|
|
34
|
+
if (r.relations) {
|
|
35
|
+
for (const [key, val] of Object.entries(r.relations)) {
|
|
36
|
+
const list = Array.isArray(val) ? val.join(', ') : val;
|
|
37
|
+
lines.push(` ${key}: ${list}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return lines.join('\n');
|
|
41
|
+
}).join('\n\n'));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.ListProjectsTool = ListProjectsTool;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IMcpToolHandler, ToolDefinition, ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
2
|
+
import type { ISearcher } from '../interfaces/ISearcher';
|
|
3
|
+
export declare class ReadFileRangeTool implements IMcpToolHandler {
|
|
4
|
+
private searcher;
|
|
5
|
+
constructor(searcher: ISearcher);
|
|
6
|
+
definition(): ToolDefinition;
|
|
7
|
+
handle(args: Record<string, unknown>): Promise<ToolResponse>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReadFileRangeTool = void 0;
|
|
4
|
+
const format_helpers_1 = require("./format-helpers");
|
|
5
|
+
class ReadFileRangeTool {
|
|
6
|
+
searcher;
|
|
7
|
+
constructor(searcher) {
|
|
8
|
+
this.searcher = searcher;
|
|
9
|
+
}
|
|
10
|
+
definition() {
|
|
11
|
+
return {
|
|
12
|
+
name: 'read_file_range',
|
|
13
|
+
description: 'Read a specific line range from an indexed source file. Use this when a search result looks incomplete — read the surrounding lines to get full context.',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
file_path: { type: 'string', description: 'File path as returned by search_codebase' },
|
|
18
|
+
start_line: { type: 'number', description: 'First line to read (1-based). Use the **Line** number from search_codebase results. Default: 1.' },
|
|
19
|
+
end_line: { type: 'number', description: 'Last line to read (1-based, inclusive). Default: start_line + 99.' },
|
|
20
|
+
},
|
|
21
|
+
required: ['file_path'],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async handle(args) {
|
|
26
|
+
const filePath = String(args.file_path);
|
|
27
|
+
const startLine = Math.max(1, Number(args.start_line) || 1);
|
|
28
|
+
const endLine = Number(args.end_line) || startLine + 99;
|
|
29
|
+
const range = await this.searcher.readFileRange(filePath, startLine, endLine);
|
|
30
|
+
if (!range.ok) {
|
|
31
|
+
const message = range.reason === 'invalid_path' ? `Invalid file path (outside workspace): ${filePath}` :
|
|
32
|
+
range.reason === 'not_indexed' ? `File is not indexed: ${filePath}. Run indexing first.` :
|
|
33
|
+
`File was deleted from disk after indexing: ${filePath}. Re-run indexing to update.`;
|
|
34
|
+
return (0, format_helpers_1.textResponse)(message);
|
|
35
|
+
}
|
|
36
|
+
const numbered = range.lines.map((line, i) => `${startLine + i}: ${line}`).join('\n');
|
|
37
|
+
let text = `**File:** ${range.filePath}\n**Lines:** ${range.startLine}–${range.endLine}\n\`\`\`\n${numbered}\n\`\`\``;
|
|
38
|
+
if (range.hasMore) {
|
|
39
|
+
text += `\n\n[file has more lines — call read_file_range again with start_line: ${range.endLine + 1}]`;
|
|
40
|
+
}
|
|
41
|
+
return (0, format_helpers_1.textResponse)(text);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.ReadFileRangeTool = ReadFileRangeTool;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IMcpToolHandler, ToolDefinition, ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
2
|
+
import type { ISearcher } from '../interfaces/ISearcher';
|
|
3
|
+
export declare class ReadFileTool implements IMcpToolHandler {
|
|
4
|
+
private searcher;
|
|
5
|
+
constructor(searcher: ISearcher);
|
|
6
|
+
definition(): ToolDefinition;
|
|
7
|
+
handle(args: Record<string, unknown>): Promise<ToolResponse>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReadFileTool = void 0;
|
|
4
|
+
const format_helpers_1 = require("./format-helpers");
|
|
5
|
+
class ReadFileTool {
|
|
6
|
+
searcher;
|
|
7
|
+
constructor(searcher) {
|
|
8
|
+
this.searcher = searcher;
|
|
9
|
+
}
|
|
10
|
+
definition() {
|
|
11
|
+
return {
|
|
12
|
+
name: 'read_file',
|
|
13
|
+
description: 'Read a file by absolute path. Returns contents with line numbers. Truncates at max_lines with a hint to continue with read_file_range.',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
file_path: { type: 'string', description: 'Absolute path to the file. Note: search_codebase returns relative paths — construct the absolute path from your workspace root.' },
|
|
18
|
+
max_lines: { type: 'number', description: 'Maximum lines to return (default 300, max 1000)' },
|
|
19
|
+
},
|
|
20
|
+
required: ['file_path'],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async handle(args) {
|
|
25
|
+
const filePath = String(args.file_path ?? '');
|
|
26
|
+
const rawMax = Number(args.max_lines);
|
|
27
|
+
const maxLines = isNaN(rawMax) || rawMax < 1 ? 300 : Math.min(1000, rawMax);
|
|
28
|
+
const result = await this.searcher.readFileAbsolute(filePath, maxLines);
|
|
29
|
+
if (!result.ok) {
|
|
30
|
+
const message = result.reason === 'invalid_path' ? `Invalid path (outside workspace): ${filePath}` :
|
|
31
|
+
`File not found on disk: ${filePath}`;
|
|
32
|
+
return (0, format_helpers_1.textResponse)(message);
|
|
33
|
+
}
|
|
34
|
+
const numbered = result.lines.map((line, i) => `${i + 1}: ${line}`).join('\n');
|
|
35
|
+
let text = `**File:** ${result.filePath}\n**Lines:** 1–${result.endLine}\n\`\`\`\n${numbered}\n\`\`\``;
|
|
36
|
+
if (result.hasMore) {
|
|
37
|
+
text += `\n\n[truncated: file has more lines — use read_file_range with file_path: "${result.filePath}", start_line: ${result.endLine + 1} to continue]`;
|
|
38
|
+
}
|
|
39
|
+
return (0, format_helpers_1.textResponse)(text);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.ReadFileTool = ReadFileTool;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IMcpToolHandler, ToolDefinition, ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
2
|
+
import type { ISearcher } from '../interfaces/ISearcher';
|
|
3
|
+
import type { ILogger } from '../interfaces/ILogger';
|
|
4
|
+
export declare class SearchCodebaseTool implements IMcpToolHandler {
|
|
5
|
+
private searcher;
|
|
6
|
+
private defaultLimit;
|
|
7
|
+
private maxLimit;
|
|
8
|
+
private defaultThreshold;
|
|
9
|
+
private logger;
|
|
10
|
+
constructor(searcher: ISearcher, defaultLimit: number, maxLimit: number, defaultThreshold: number, logger: ILogger);
|
|
11
|
+
definition(): ToolDefinition;
|
|
12
|
+
handle(args: Record<string, unknown>): Promise<ToolResponse>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SearchCodebaseTool = void 0;
|
|
4
|
+
const format_helpers_1 = require("./format-helpers");
|
|
5
|
+
class SearchCodebaseTool {
|
|
6
|
+
searcher;
|
|
7
|
+
defaultLimit;
|
|
8
|
+
maxLimit;
|
|
9
|
+
defaultThreshold;
|
|
10
|
+
logger;
|
|
11
|
+
constructor(searcher, defaultLimit, maxLimit, defaultThreshold, logger) {
|
|
12
|
+
this.searcher = searcher;
|
|
13
|
+
this.defaultLimit = defaultLimit;
|
|
14
|
+
this.maxLimit = maxLimit;
|
|
15
|
+
this.defaultThreshold = defaultThreshold;
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
}
|
|
18
|
+
definition() {
|
|
19
|
+
return {
|
|
20
|
+
name: 'search_codebase',
|
|
21
|
+
description: 'Search local project codebase by semantic similarity. Each result shows File, Project, an optional Symbol breadcrumb (owning declaration, e.g. `IndexerService.ingestFile`), Line, the code, and Relevance.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
query: { type: 'string' },
|
|
26
|
+
limit: { type: 'number', description: `Max results (default ${this.defaultLimit}, max ${this.maxLimit})` },
|
|
27
|
+
project: { type: 'string', description: 'Filter by project name (optional). Use list_projects to see available names.' },
|
|
28
|
+
include_neighbors: { type: 'boolean', description: 'Also include the chunk before and after each result from the same file (Small-to-Big retrieval). Useful when results look truncated.' },
|
|
29
|
+
include_graph_context: { type: 'boolean', description: 'Append direct imports and dependents list to each result. Shows "Imports: X · Y" and "Used by: Z" without extra tool calls.' },
|
|
30
|
+
threshold: { type: 'number', description: `Max cosine distance (0–1). Lower = stricter. Default: ${this.defaultThreshold}. Use 0.40–0.45 for precise technical queries, 0.55–0.60 for broad exploration.` },
|
|
31
|
+
},
|
|
32
|
+
required: ['query'],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async handle(args) {
|
|
37
|
+
const t0 = Date.now();
|
|
38
|
+
const raw = String(args.query);
|
|
39
|
+
const limit = Math.min(Math.max(1, Number(args.limit) || this.defaultLimit), this.maxLimit);
|
|
40
|
+
const project = args.project ? String(args.project) : null;
|
|
41
|
+
const includeNeighbors = Boolean(args.include_neighbors);
|
|
42
|
+
const includeGraphContext = Boolean(args.include_graph_context);
|
|
43
|
+
const rawThreshold = args.threshold;
|
|
44
|
+
const parsedThreshold = Number(rawThreshold);
|
|
45
|
+
const threshold = rawThreshold != null && !isNaN(parsedThreshold)
|
|
46
|
+
? Math.min(1, Math.max(0, parsedThreshold))
|
|
47
|
+
: undefined;
|
|
48
|
+
const { query, translated } = await this.searcher.prepareQuery(raw);
|
|
49
|
+
const results = await this.searcher.searchChunks(query, limit, project, threshold);
|
|
50
|
+
this.logger.debug('SearchCodebaseTool: handle', {
|
|
51
|
+
query: raw.slice(0, 120), project, threshold: threshold ?? this.defaultThreshold,
|
|
52
|
+
limit, translated, count: results.length, ms: Date.now() - t0,
|
|
53
|
+
});
|
|
54
|
+
if (results.length === 0) {
|
|
55
|
+
return (0, format_helpers_1.textResponse)('No relevant results found.');
|
|
56
|
+
}
|
|
57
|
+
const header = translated ? `> Query translated: "${raw}" → "${query}"\n\n` : '';
|
|
58
|
+
let graphCtx = null;
|
|
59
|
+
if (includeGraphContext) {
|
|
60
|
+
graphCtx = await this.searcher.fetchGraphContext(results);
|
|
61
|
+
}
|
|
62
|
+
if (includeNeighbors) {
|
|
63
|
+
const withNeighbors = await this.searcher.fetchNeighbors(results);
|
|
64
|
+
const blocks = withNeighbors.map(({ result, prevChunk, nextChunk }) => {
|
|
65
|
+
const parts = [];
|
|
66
|
+
if (prevChunk)
|
|
67
|
+
parts.push((0, format_helpers_1.formatChunk)(prevChunk, 'prev chunk'));
|
|
68
|
+
parts.push(`${(0, format_helpers_1.formatChunk)(result)}\n**Relevance:** ${result.score}${(0, format_helpers_1.formatGraphContext)(graphCtx?.get(result.filePath))}`);
|
|
69
|
+
if (nextChunk)
|
|
70
|
+
parts.push((0, format_helpers_1.formatChunk)(nextChunk, 'next chunk'));
|
|
71
|
+
return parts.join('\n\n');
|
|
72
|
+
});
|
|
73
|
+
return (0, format_helpers_1.textResponse)(header + blocks.join('\n\n---\n\n'));
|
|
74
|
+
}
|
|
75
|
+
const text = results
|
|
76
|
+
.map(r => `${(0, format_helpers_1.formatChunk)(r)}\n**Relevance:** ${r.score}${(0, format_helpers_1.formatGraphContext)(graphCtx?.get(r.filePath))}`)
|
|
77
|
+
.join('\n\n---\n\n');
|
|
78
|
+
return (0, format_helpers_1.textResponse)(header + text);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.SearchCodebaseTool = SearchCodebaseTool;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChunkRow, DependencyRow, GraphContext } from '../services/SearcherService';
|
|
2
|
+
import type { ToolResponse } from '../interfaces/IMcpToolHandler';
|
|
3
|
+
export declare function textResponse(text: string): ToolResponse;
|
|
4
|
+
export declare function errorResponse(text: string): ToolResponse;
|
|
5
|
+
export declare function formatChunk(r: ChunkRow, label?: string): string;
|
|
6
|
+
export declare function formatGraphContext(ctx: GraphContext | undefined): string;
|
|
7
|
+
export declare function formatDependencies(filePath: string, rows: DependencyRow[], transitive: boolean): string;
|
|
8
|
+
export declare function formatDependents(filePath: string, rows: DependencyRow[], transitive: boolean): string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textResponse = textResponse;
|
|
4
|
+
exports.errorResponse = errorResponse;
|
|
5
|
+
exports.formatChunk = formatChunk;
|
|
6
|
+
exports.formatGraphContext = formatGraphContext;
|
|
7
|
+
exports.formatDependencies = formatDependencies;
|
|
8
|
+
exports.formatDependents = formatDependents;
|
|
9
|
+
function textResponse(text) {
|
|
10
|
+
return { content: [{ type: 'text', text }] };
|
|
11
|
+
}
|
|
12
|
+
function errorResponse(text) {
|
|
13
|
+
return { isError: true, content: [{ type: 'text', text }] };
|
|
14
|
+
}
|
|
15
|
+
function formatChunk(r, label) {
|
|
16
|
+
const symbolLine = r.ownerPath ? `**Symbol:** ${r.ownerPath}\n` : '';
|
|
17
|
+
return `${label ? `*(${label})*\n` : ''}**File:** ${r.filePath}\n**Project:** ${r.serviceName}\n${symbolLine}**Line:** ${r.startLine}\n\`\`\`\n${r.content}\n\`\`\``;
|
|
18
|
+
}
|
|
19
|
+
function formatGraphContext(ctx) {
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (ctx?.imports.length)
|
|
22
|
+
parts.push(`**Imports:** ${ctx.imports.join(' · ')}`);
|
|
23
|
+
if (ctx?.usedBy.length)
|
|
24
|
+
parts.push(`**Used by:** ${ctx.usedBy.join(' · ')}`);
|
|
25
|
+
return parts.length > 0 ? '\n' + parts.join('\n') : '';
|
|
26
|
+
}
|
|
27
|
+
function formatDependencies(filePath, rows, transitive) {
|
|
28
|
+
const direct = rows.filter(r => !transitive || (r.depth ?? 1) === 1);
|
|
29
|
+
const indirect = transitive ? rows.filter(r => (r.depth ?? 1) > 1) : [];
|
|
30
|
+
const lines = [`**Dependencies of** \`${filePath}\`\n`, '**Direct:**'];
|
|
31
|
+
for (const r of direct) {
|
|
32
|
+
const sym = r.toSymbol ? ` (${r.depType} \`${r.toSymbol}\`)` : ` (${r.depType})`;
|
|
33
|
+
lines.push(` → \`${r.toFile}\`${sym}`);
|
|
34
|
+
}
|
|
35
|
+
if (indirect.length > 0) {
|
|
36
|
+
lines.push('\n**Transitive:**');
|
|
37
|
+
for (const r of indirect) {
|
|
38
|
+
lines.push(` → \`${r.toFile}\` (depth ${r.depth}, via \`${r.via}\`)`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return lines.join('\n');
|
|
42
|
+
}
|
|
43
|
+
function formatDependents(filePath, rows, transitive) {
|
|
44
|
+
const direct = rows.filter(r => !transitive || (r.depth ?? 1) === 1);
|
|
45
|
+
const indirect = transitive ? rows.filter(r => (r.depth ?? 1) > 1) : [];
|
|
46
|
+
const lines = [`**Dependents of** \`${filePath}\`\n`, '**Direct:**'];
|
|
47
|
+
for (const r of direct) {
|
|
48
|
+
const sym = r.fromSymbol ? ` (in \`${r.fromSymbol}\`)` : '';
|
|
49
|
+
lines.push(` ← \`${r.fromFile}\`${sym}`);
|
|
50
|
+
}
|
|
51
|
+
if (indirect.length > 0) {
|
|
52
|
+
lines.push('\n**Transitive:**');
|
|
53
|
+
for (const r of indirect) {
|
|
54
|
+
lines.push(` ← \`${r.fromFile}\` (depth ${r.depth}, via \`${r.via}\`)`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ListProjectsTool } from './ListProjectsTool';
|
|
2
|
+
export { SearchCodebaseTool } from './SearchCodebaseTool';
|
|
3
|
+
export { ReadFileRangeTool } from './ReadFileRangeTool';
|
|
4
|
+
export { GetDependenciesTool } from './GetDependenciesTool';
|
|
5
|
+
export { GetDependentsTool } from './GetDependentsTool';
|
|
6
|
+
export { FindSymbolTool } from './FindSymbolTool';
|
|
7
|
+
export { GetCallersTool } from './GetCallersTool';
|
|
8
|
+
export { ListFilesTool } from './ListFilesTool';
|
|
9
|
+
export { ReadFileTool } from './ReadFileTool';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReadFileTool = exports.ListFilesTool = exports.GetCallersTool = exports.FindSymbolTool = exports.GetDependentsTool = exports.GetDependenciesTool = exports.ReadFileRangeTool = exports.SearchCodebaseTool = exports.ListProjectsTool = void 0;
|
|
4
|
+
var ListProjectsTool_1 = require("./ListProjectsTool");
|
|
5
|
+
Object.defineProperty(exports, "ListProjectsTool", { enumerable: true, get: function () { return ListProjectsTool_1.ListProjectsTool; } });
|
|
6
|
+
var SearchCodebaseTool_1 = require("./SearchCodebaseTool");
|
|
7
|
+
Object.defineProperty(exports, "SearchCodebaseTool", { enumerable: true, get: function () { return SearchCodebaseTool_1.SearchCodebaseTool; } });
|
|
8
|
+
var ReadFileRangeTool_1 = require("./ReadFileRangeTool");
|
|
9
|
+
Object.defineProperty(exports, "ReadFileRangeTool", { enumerable: true, get: function () { return ReadFileRangeTool_1.ReadFileRangeTool; } });
|
|
10
|
+
var GetDependenciesTool_1 = require("./GetDependenciesTool");
|
|
11
|
+
Object.defineProperty(exports, "GetDependenciesTool", { enumerable: true, get: function () { return GetDependenciesTool_1.GetDependenciesTool; } });
|
|
12
|
+
var GetDependentsTool_1 = require("./GetDependentsTool");
|
|
13
|
+
Object.defineProperty(exports, "GetDependentsTool", { enumerable: true, get: function () { return GetDependentsTool_1.GetDependentsTool; } });
|
|
14
|
+
var FindSymbolTool_1 = require("./FindSymbolTool");
|
|
15
|
+
Object.defineProperty(exports, "FindSymbolTool", { enumerable: true, get: function () { return FindSymbolTool_1.FindSymbolTool; } });
|
|
16
|
+
var GetCallersTool_1 = require("./GetCallersTool");
|
|
17
|
+
Object.defineProperty(exports, "GetCallersTool", { enumerable: true, get: function () { return GetCallersTool_1.GetCallersTool; } });
|
|
18
|
+
var ListFilesTool_1 = require("./ListFilesTool");
|
|
19
|
+
Object.defineProperty(exports, "ListFilesTool", { enumerable: true, get: function () { return ListFilesTool_1.ListFilesTool; } });
|
|
20
|
+
var ReadFileTool_1 = require("./ReadFileTool");
|
|
21
|
+
Object.defineProperty(exports, "ReadFileTool", { enumerable: true, get: function () { return ReadFileTool_1.ReadFileTool; } });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders the RAG dashboard as a single self-contained HTML page (inline CSS +
|
|
3
|
+
* JS), mirroring the approach in graph.ts. The page is schema-driven: it fetches
|
|
4
|
+
* /api/schema (from buildJsonSchema — the same source as Step 1's editor support)
|
|
5
|
+
* and /api/config, then renders a typed form. The Graph tab embeds the full
|
|
6
|
+
* standalone graph page (GET /graph — same buildHtml as project-graph.html) in
|
|
7
|
+
* an iframe rather than re-implementing the network here.
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderDashboardHtml(): string;
|